GetSetMethodNormalizer.php 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. <?php
  2. namespace Symfony\Component\Serializer\Normalizer;
  3. use Symfony\Component\Serializer\SerializerInterface;
  4. /*
  5. * This file is part of the Symfony framework.
  6. *
  7. * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. /**
  13. * Converts between objects with getter and setter methods and arrays.
  14. *
  15. * The normalization process looks at all public methods and calls the ones
  16. * which have a name starting with get and take no parameters. The result is a
  17. * map from property names (method name stripped of the get prefix and converted
  18. * to lower case) to property values. Property values are normalized through the
  19. * serializer.
  20. *
  21. * The denormalization first looks at the constructor of the given class to see
  22. * if any of the parameters have the same name as one of the properties. The
  23. * constructor is then called with all parameters or an exception is thrown if
  24. * any required parameters were not present as properties. Then the denormalizer
  25. * walks through the given map of property names to property values to see if a
  26. * setter method exists for any of the properties. If a setter exists it is
  27. * called with the property value. No automatic denormalization of the value
  28. * takes place.
  29. *
  30. * @author Nils Adermann <naderman@naderman.de>
  31. */
  32. class GetSetMethodNormalizer extends AbstractNormalizer implements NormalizerInterface
  33. {
  34. /**
  35. * {@inheritdoc}
  36. */
  37. public function normalize($object, $format, $properties = null)
  38. {
  39. $propertyMap = (null === $properties) ? null : array_flip(array_map('strtolower', $properties));
  40. $reflectionObject = new \ReflectionObject($object);
  41. $reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC);
  42. $attributes = array();
  43. foreach ($reflectionMethods as $method) {
  44. if ($this->isGetMethod($method)) {
  45. $attributeName = strtolower(substr($method->getName(), 3));
  46. if (null === $propertyMap || isset($propertyMap[$attributeName])) {
  47. $attributeValue = $method->invoke($object);
  48. if (!is_scalar($attributeValue)) {
  49. $attributeValue = $this->serializer->normalize($attributeValue, $format);
  50. }
  51. $attributes[$attributeName] = $attributeValue;
  52. }
  53. }
  54. }
  55. return $attributes;
  56. }
  57. /**
  58. * {@inheritdoc}
  59. */
  60. public function denormalize($data, $class, $format = null)
  61. {
  62. $reflectionClass = new \ReflectionClass($class);
  63. $constructor = $reflectionClass->getConstructor();
  64. if ($constructor) {
  65. $constructorParameters = $constructor->getParameters();
  66. $attributeNames = array_keys($data);
  67. $params = array();
  68. foreach ($constructorParameters as $constructorParameter) {
  69. $paramName = strtolower($constructorParameter->getName());
  70. if (isset($data[$paramName])) {
  71. $params[] = $data[$paramName];
  72. // don't run set for a parameter passed to the constructor
  73. unset($data[$paramName]);
  74. } else if (!$constructorParameter->isOptional()) {
  75. throw new \RuntimeException(
  76. 'Cannot create an instance of ' . $class .
  77. ' from serialized data because its constructor requires ' .
  78. 'parameter "' . $constructorParameter->getName() .
  79. '" to be present.');
  80. }
  81. }
  82. $object = $reflectionClass->newInstanceArgs($params);
  83. } else {
  84. $object = new $class;
  85. }
  86. foreach ($data as $attribute => $value) {
  87. $setter = 'set' . $attribute;
  88. if (method_exists($object, $setter)) {
  89. $object->$setter($value);
  90. }
  91. }
  92. return $object;
  93. }
  94. /**
  95. * Checks if the given class has any get{Property} method.
  96. *
  97. * @param ReflectionClass $class A ReflectionClass instance of the class
  98. * to serialize into or from.
  99. * @param string $format The format being (de-)serialized from or into.
  100. * @return Boolean Whether the class has any getters.
  101. */
  102. public function supports(\ReflectionClass $class, $format = null)
  103. {
  104. $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
  105. foreach ($methods as $method) {
  106. if ($this->isGetMethod($method)) {
  107. return true;
  108. }
  109. }
  110. return false;
  111. }
  112. /**
  113. * Checks if a method's name is get.* and can be called without parameters.
  114. *
  115. * @param ReflectionMethod $method the method to check
  116. * @return Boolean whether the method is a getter.
  117. */
  118. protected function isGetMethod(\ReflectionMethod $method)
  119. {
  120. return (
  121. 0 === strpos($method->getName(), 'get') &&
  122. 3 < strlen($method->getName()) &&
  123. 0 === $method->getNumberOfRequiredParameters()
  124. );
  125. }
  126. }