/src/Symfony/Component/Validator/GraphWalker.php

https://github.com/sebio/symfony · PHP · 169 lines · 114 code · 25 blank · 30 comment · 15 complexity · 44a5d32f691da61b5559ebea3d81da37 MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Validator;
  11. use Symfony\Component\Validator\ConstraintValidatorFactoryInterface;
  12. use Symfony\Component\Validator\Constraint;
  13. use Symfony\Component\Validator\Constraints\All;
  14. use Symfony\Component\Validator\Constraints\Valid;
  15. use Symfony\Component\Validator\Exception\UnexpectedTypeException;
  16. use Symfony\Component\Validator\Mapping\ClassMetadataFactoryInterface;
  17. use Symfony\Component\Validator\Mapping\ClassMetadata;
  18. use Symfony\Component\Validator\Mapping\MemberMetadata;
  19. /**
  20. * Responsible for walking over and initializing validation on different
  21. * types of items.
  22. *
  23. * @author Fabien Potencier <fabien.potencier@symfony-project.com>
  24. * @author Bernhard Schussek <bernhard.schussek@symfony-project.com>
  25. */
  26. class GraphWalker
  27. {
  28. protected $context;
  29. protected $validatorFactory;
  30. protected $metadataFactory;
  31. protected $validatedObjects = array();
  32. public function __construct($root, ClassMetadataFactoryInterface $metadataFactory, ConstraintValidatorFactoryInterface $factory)
  33. {
  34. $this->context = new ExecutionContext($root, $this, $metadataFactory);
  35. $this->validatorFactory = $factory;
  36. $this->metadataFactory = $metadataFactory;
  37. }
  38. /**
  39. * @return ConstraintViolationList
  40. */
  41. public function getViolations()
  42. {
  43. return $this->context->getViolations();
  44. }
  45. /**
  46. * Initialize validation on the given object using the given metadata
  47. * instance and validation group.
  48. *
  49. * @param ClassMetadata $metadata
  50. * @param object $object The object to validate
  51. * @param string $group The validator group to use for validation
  52. * @param string $propertyPath
  53. */
  54. public function walkObject(ClassMetadata $metadata, $object, $group, $propertyPath)
  55. {
  56. $this->context->setCurrentClass($metadata->getClassName());
  57. if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
  58. $groups = $metadata->getGroupSequence();
  59. foreach ($groups as $group) {
  60. $this->walkObjectForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);
  61. if (count($this->getViolations()) > 0) {
  62. break;
  63. }
  64. }
  65. } else {
  66. $this->walkObjectForGroup($metadata, $object, $group, $propertyPath);
  67. }
  68. }
  69. protected function walkObjectForGroup(ClassMetadata $metadata, $object, $group, $propertyPath, $propagatedGroup = null)
  70. {
  71. $hash = spl_object_hash($object);
  72. // Exit, if the object is already validated for the current group
  73. if (isset($this->validatedObjects[$hash])) {
  74. if (isset($this->validatedObjects[$hash][$group])) {
  75. return;
  76. }
  77. } else {
  78. $this->validatedObjects[$hash] = array();
  79. }
  80. // Remember validating this object before starting and possibly
  81. // traversing the object graph
  82. $this->validatedObjects[$hash][$group] = true;
  83. foreach ($metadata->findConstraints($group) as $constraint) {
  84. $this->walkConstraint($constraint, $object, $group, $propertyPath);
  85. }
  86. if (null !== $object) {
  87. foreach ($metadata->getConstrainedProperties() as $property) {
  88. $localPropertyPath = empty($propertyPath) ? $property : $propertyPath.'.'.$property;
  89. $this->walkProperty($metadata, $property, $object, $group, $localPropertyPath, $propagatedGroup);
  90. }
  91. }
  92. }
  93. public function walkProperty(ClassMetadata $metadata, $property, $object, $group, $propertyPath, $propagatedGroup = null)
  94. {
  95. foreach ($metadata->getMemberMetadatas($property) as $member) {
  96. $this->walkMember($member, $member->getValue($object), $group, $propertyPath, $propagatedGroup);
  97. }
  98. }
  99. public function walkPropertyValue(ClassMetadata $metadata, $property, $value, $group, $propertyPath)
  100. {
  101. foreach ($metadata->getMemberMetadatas($property) as $member) {
  102. $this->walkMember($member, $value, $group, $propertyPath);
  103. }
  104. }
  105. protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null)
  106. {
  107. $this->context->setCurrentProperty($metadata->getPropertyName());
  108. foreach ($metadata->findConstraints($group) as $constraint) {
  109. $this->walkConstraint($constraint, $value, $group, $propertyPath);
  110. }
  111. if ($metadata->isCascaded()) {
  112. $this->walkReference($value, $propagatedGroup ?: $group, $propertyPath);
  113. }
  114. }
  115. protected function walkReference($value, $group, $propertyPath)
  116. {
  117. if (null !== $value) {
  118. if (is_array($value)) {
  119. foreach ($value as $key => $element) {
  120. $this->walkReference($element, $group, $propertyPath.'['.$key.']');
  121. }
  122. } else if (!is_object($value)) {
  123. throw new UnexpectedTypeException($value, 'object or array');
  124. } else {
  125. $metadata = $this->metadataFactory->getClassMetadata(get_class($value));
  126. $this->walkObject($metadata, $value, $group, $propertyPath);
  127. }
  128. }
  129. }
  130. public function walkConstraint(Constraint $constraint, $value, $group, $propertyPath)
  131. {
  132. $validator = $this->validatorFactory->getInstance($constraint);
  133. $this->context->setPropertyPath($propertyPath);
  134. $this->context->setGroup($group);
  135. $validator->initialize($this->context);
  136. if (!$validator->isValid($value, $constraint)) {
  137. $this->context->addViolation(
  138. $validator->getMessageTemplate(),
  139. $validator->getMessageParameters(),
  140. $value
  141. );
  142. }
  143. }
  144. }