Chris@0: Chris@0: * Chris@0: * For the full copyright and license information, please view the LICENSE Chris@0: * file that was distributed with this source code. Chris@0: */ Chris@0: Chris@0: namespace Symfony\Component\Serializer\Normalizer; Chris@0: Chris@0: /** Chris@0: * Converts between objects with getter and setter methods and arrays. Chris@0: * Chris@0: * The normalization process looks at all public methods and calls the ones Chris@0: * which have a name starting with get and take no parameters. The result is a Chris@0: * map from property names (method name stripped of the get prefix and converted Chris@0: * to lower case) to property values. Property values are normalized through the Chris@0: * serializer. Chris@0: * Chris@0: * The denormalization first looks at the constructor of the given class to see Chris@0: * if any of the parameters have the same name as one of the properties. The Chris@0: * constructor is then called with all parameters or an exception is thrown if Chris@0: * any required parameters were not present as properties. Then the denormalizer Chris@0: * walks through the given map of property names to property values to see if a Chris@0: * setter method exists for any of the properties. If a setter exists it is Chris@0: * called with the property value. No automatic denormalization of the value Chris@0: * takes place. Chris@0: * Chris@0: * @author Nils Adermann Chris@0: * @author Kévin Dunglas Chris@0: */ Chris@0: class GetSetMethodNormalizer extends AbstractObjectNormalizer Chris@0: { Chris@0: private static $setterAccessibleCache = array(); Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function supportsNormalization($data, $format = null) Chris@0: { Chris@0: return parent::supportsNormalization($data, $format) && $this->supports(get_class($data)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function supportsDenormalization($data, $type, $format = null) Chris@0: { Chris@0: return parent::supportsDenormalization($data, $type, $format) && $this->supports($type); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if the given class has any get{Property} method. Chris@0: * Chris@0: * @param string $class Chris@0: * Chris@0: * @return bool Chris@0: */ Chris@0: private function supports($class) Chris@0: { Chris@0: $class = new \ReflectionClass($class); Chris@0: $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC); Chris@0: foreach ($methods as $method) { Chris@0: if ($this->isGetMethod($method)) { Chris@0: return true; Chris@0: } Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Checks if a method's name is get.* or is.*, and can be called without parameters. Chris@0: * Chris@0: * @param \ReflectionMethod $method the method to check Chris@0: * Chris@0: * @return bool whether the method is a getter or boolean getter Chris@0: */ Chris@0: private function isGetMethod(\ReflectionMethod $method) Chris@0: { Chris@0: $methodLength = strlen($method->name); Chris@0: Chris@0: return Chris@0: !$method->isStatic() && Chris@0: ( Chris@0: ((0 === strpos($method->name, 'get') && 3 < $methodLength) || Chris@0: (0 === strpos($method->name, 'is') && 2 < $methodLength)) && Chris@0: 0 === $method->getNumberOfRequiredParameters() Chris@0: ) Chris@0: ; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function extractAttributes($object, $format = null, array $context = array()) Chris@0: { Chris@0: $reflectionObject = new \ReflectionObject($object); Chris@0: $reflectionMethods = $reflectionObject->getMethods(\ReflectionMethod::IS_PUBLIC); Chris@0: Chris@0: $attributes = array(); Chris@0: foreach ($reflectionMethods as $method) { Chris@0: if (!$this->isGetMethod($method)) { Chris@0: continue; Chris@0: } Chris@0: Chris@0: $attributeName = lcfirst(substr($method->name, 0 === strpos($method->name, 'is') ? 2 : 3)); Chris@0: Chris@0: if ($this->isAllowedAttribute($object, $attributeName)) { Chris@0: $attributes[] = $attributeName; Chris@0: } Chris@0: } Chris@0: Chris@0: return $attributes; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function getAttributeValue($object, $attribute, $format = null, array $context = array()) Chris@0: { Chris@0: $ucfirsted = ucfirst($attribute); Chris@0: Chris@0: $getter = 'get'.$ucfirsted; Chris@0: if (is_callable(array($object, $getter))) { Chris@0: return $object->$getter(); Chris@0: } Chris@0: Chris@0: $isser = 'is'.$ucfirsted; Chris@0: if (is_callable(array($object, $isser))) { Chris@0: return $object->$isser(); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array()) Chris@0: { Chris@0: $setter = 'set'.ucfirst($attribute); Chris@0: $key = get_class($object).':'.$setter; Chris@0: Chris@0: if (!isset(self::$setterAccessibleCache[$key])) { Chris@0: self::$setterAccessibleCache[$key] = is_callable(array($object, $setter)) && !(new \ReflectionMethod($object, $setter))->isStatic(); Chris@0: } Chris@0: Chris@0: if (self::$setterAccessibleCache[$key]) { Chris@0: $object->$setter($value); Chris@0: } Chris@0: } Chris@0: }