KeySet.php 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. <?php
  2. /*
  3. * Copyright (c) Alexandre Gomes Gaigalas <alganet@gmail.com>
  4. * SPDX-License-Identifier: MIT
  5. */
  6. declare(strict_types=1);
  7. namespace Respect\Validation\Rules;
  8. use Respect\Validation\Exceptions\ComponentException;
  9. use Respect\Validation\NonNegatable;
  10. use Respect\Validation\Validatable;
  11. use function array_key_exists;
  12. use function array_map;
  13. use function count;
  14. use function current;
  15. use function is_array;
  16. /**
  17. * Validates a keys in a defined structure.
  18. *
  19. * @author Emmerson Siqueira <emmersonsiqueira@gmail.com>
  20. * @author Henrique Moody <henriquemoody@gmail.com>
  21. */
  22. final class KeySet extends AbstractWrapper implements NonNegatable
  23. {
  24. /**
  25. * @var mixed[]
  26. */
  27. private $keys;
  28. /**
  29. * @var mixed[]
  30. */
  31. private $extraKeys = [];
  32. /**
  33. * @var Key[]
  34. */
  35. private $keyRules;
  36. /**
  37. * Initializes the rule.
  38. *
  39. * phpcs:ignore SlevomatCodingStandard.TypeHints.ParameterTypeHint.UselessAnnotation
  40. * @param Validatable ...$validatables
  41. */
  42. public function __construct(Validatable ...$validatables)
  43. {
  44. $this->keyRules = array_map([$this, 'getKeyRule'], $validatables);
  45. $this->keys = array_map([$this, 'getKeyReference'], $this->keyRules);
  46. parent::__construct(new AllOf(...$this->keyRules));
  47. }
  48. /**
  49. * {@inheritDoc}
  50. */
  51. public function assert($input): void
  52. {
  53. if (!$this->hasValidStructure($input)) {
  54. throw $this->reportError($input);
  55. }
  56. parent::assert($input);
  57. }
  58. /**
  59. * {@inheritDoc}
  60. */
  61. public function check($input): void
  62. {
  63. if (!$this->hasValidStructure($input)) {
  64. throw $this->reportError($input);
  65. }
  66. parent::check($input);
  67. }
  68. /**
  69. * {@inheritDoc}
  70. */
  71. public function validate($input): bool
  72. {
  73. if (!$this->hasValidStructure($input)) {
  74. return false;
  75. }
  76. return parent::validate($input);
  77. }
  78. /**
  79. * @throws ComponentException
  80. */
  81. private function getKeyRule(Validatable $validatable): Key
  82. {
  83. if ($validatable instanceof Key) {
  84. return $validatable;
  85. }
  86. if (!$validatable instanceof AllOf || count($validatable->getRules()) !== 1) {
  87. throw new ComponentException('KeySet rule accepts only Key rules');
  88. }
  89. return $this->getKeyRule(current($validatable->getRules()));
  90. }
  91. /**
  92. * @return mixed
  93. */
  94. private function getKeyReference(Key $rule)
  95. {
  96. return $rule->getReference();
  97. }
  98. /**
  99. * @param mixed $input
  100. */
  101. private function hasValidStructure($input): bool
  102. {
  103. if (!is_array($input)) {
  104. return false;
  105. }
  106. foreach ($this->keyRules as $keyRule) {
  107. if (!array_key_exists($keyRule->getReference(), $input) && $keyRule->isMandatory()) {
  108. return false;
  109. }
  110. unset($input[$keyRule->getReference()]);
  111. }
  112. foreach ($input as $extraKey => &$ignoreValue) {
  113. $this->extraKeys[] = $extraKey;
  114. }
  115. return count($input) == 0;
  116. }
  117. }