Drupal investigation

GetSetMethodNormalizer.php 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.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\Serializer\Normalizer;
  11. use Symfony\Component\Serializer\Exception\CircularReferenceException;
  12. use Symfony\Component\Serializer\Exception\LogicException;
  13. use Symfony\Component\Serializer\Exception\RuntimeException;
  14. /**
  15. * Converts between objects with getter and setter methods and arrays.
  16. *
  17. * The normalization process looks at all public methods and calls the ones
  18. * which have a name starting with get and take no parameters. The result is a
  19. * map from property names (method name stripped of the get prefix and converted
  20. * to lower case) to property values. Property values are normalized through the
  21. * serializer.
  22. *
  23. * The denormalization first looks at the constructor of the given class to see
  24. * if any of the parameters have the same name as one of the properties. The
  25. * constructor is then called with all parameters or an exception is thrown if
  26. * any required parameters were not present as properties. Then the denormalizer
  27. * walks through the given map of property names to property values to see if a
  28. * setter method exists for any of the properties. If a setter exists it is
  29. * called with the property value. No automatic denormalization of the value
  30. * takes place.
  31. *
  32. * @author Nils Adermann <naderman@naderman.de>
  33. * @author Kévin Dunglas <dunglas@gmail.com>
  34. */
  35. class GetSetMethodNormalizer extends AbstractNormalizer
  36. {
  37. /**
  38. * {@inheritdoc}
  39. *
  40. * @throws LogicException
  41. * @throws CircularReferenceException
  42. */
  43. public function normalize($object, $format = null, array $context = array())
  44. {
  45. if ($this->isCircularReference($object, $context)) {
  46. return $this->handleCircularReference($object);
  47. }
  48. $reflectionObject = new \ReflectionObject($object);
  49. $reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
  50. $allowedAttributes = $this->getAllowedAttributes($object, $context, true);
  51. $attributes = array();
  52. foreach ($reflectionMethods as $method) {
  53. if ($this->isGetMethod($method)) {
  54. $attributeName = lcfirst(substr($method->name, 0 === strpos($method->name, 'is') ? 2 : 3));
  55. if (in_array($attributeName, $this->ignoredAttributes)) {
  56. continue;
  57. }
  58. if (false !== $allowedAttributes && !in_array($attributeName, $allowedAttributes)) {
  59. continue;
  60. }
  61. $attributeValue = $method->invoke($object);
  62. if (isset($this->callbacks[$attributeName])) {
  63. $attributeValue = call_user_func($this->callbacks[$attributeName], $attributeValue);
  64. }
  65. if (null !== $attributeValue && !is_scalar($attributeValue)) {
  66. if (!$this->serializer instanceof NormalizerInterface) {
  67. throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $attributeName));
  68. }
  69. $attributeValue = $this->serializer->normalize($attributeValue, $format, $context);
  70. }
  71. if ($this->nameConverter) {
  72. $attributeName = $this->nameConverter->normalize($attributeName);
  73. }
  74. $attributes[$attributeName] = $attributeValue;
  75. }
  76. }
  77. return $attributes;
  78. }
  79. /**
  80. * {@inheritdoc}
  81. *
  82. * @throws RuntimeException
  83. */
  84. public function denormalize($data, $class, $format = null, array $context = array())
  85. {
  86. $allowedAttributes = $this->getAllowedAttributes($class, $context, true);
  87. $normalizedData = $this->prepareForDenormalization($data);
  88. $reflectionClass = new \ReflectionClass($class);
  89. $object = $this->instantiateObject($normalizedData, $class, $context, $reflectionClass, $allowedAttributes);
  90. $classMethods = get_class_methods($object);
  91. foreach ($normalizedData as $attribute => $value) {
  92. if ($this->nameConverter) {
  93. $attribute = $this->nameConverter->denormalize($attribute);
  94. }
  95. $allowed = $allowedAttributes === false || in_array($attribute, $allowedAttributes);
  96. $ignored = in_array($attribute, $this->ignoredAttributes);
  97. if ($allowed && !$ignored) {
  98. $setter = 'set'.ucfirst($attribute);
  99. if (in_array($setter, $classMethods) && !$reflectionClass->getMethod($setter)->isStatic()) {
  100. $object->$setter($value);
  101. }
  102. }
  103. }
  104. return $object;
  105. }
  106. /**
  107. * {@inheritdoc}
  108. */
  109. public function supportsNormalization($data, $format = null)
  110. {
  111. return is_object($data) && !$data instanceof \Traversable && $this->supports(get_class($data));
  112. }
  113. /**
  114. * {@inheritdoc}
  115. */
  116. public function supportsDenormalization($data, $type, $format = null)
  117. {
  118. return class_exists($type) && $this->supports($type);
  119. }
  120. /**
  121. * Checks if the given class has any get{Property} method.
  122. *
  123. * @param string $class
  124. *
  125. * @return bool
  126. */
  127. private function supports($class)
  128. {
  129. $class = new \ReflectionClass($class);
  130. $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
  131. foreach ($methods as $method) {
  132. if ($this->isGetMethod($method)) {
  133. return true;
  134. }
  135. }
  136. return false;
  137. }
  138. /**
  139. * Checks if a method's name is get.* or is.*, and can be called without parameters.
  140. *
  141. * @param \ReflectionMethod $method the method to check
  142. *
  143. * @return bool whether the method is a getter or boolean getter
  144. */
  145. private function isGetMethod(\ReflectionMethod $method)
  146. {
  147. $methodLength = strlen($method->name);
  148. return
  149. !$method->isStatic() &&
  150. (
  151. ((0 === strpos($method->name, 'get') && 3 < $methodLength) ||
  152. (0 === strpos($method->name, 'is') && 2 < $methodLength)) &&
  153. 0 === $method->getNumberOfRequiredParameters()
  154. )
  155. ;
  156. }
  157. }