52dc4f4a52dcc424512ff1fe99aa828c4a3360dc
[yaffs-website] / serializer / Normalizer / PropertyNormalizer.php
1 <?php
2
3 /*
4  * This file is part of the Symfony package.
5  *
6  * (c) Fabien Potencier <fabien@symfony.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Symfony\Component\Serializer\Normalizer;
13
14 /**
15  * Converts between objects and arrays by mapping properties.
16  *
17  * The normalization process looks for all the object's properties (public and private).
18  * The result is a map from property names to property values. Property values
19  * are normalized through the 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  * property with the corresponding name exists. If found, the property gets the value.
27  *
28  * @author Matthieu Napoli <matthieu@mnapoli.fr>
29  * @author Kévin Dunglas <dunglas@gmail.com>
30  */
31 class PropertyNormalizer extends AbstractObjectNormalizer
32 {
33     private $cache = array();
34
35     /**
36      * {@inheritdoc}
37      */
38     public function supportsNormalization($data, $format = null)
39     {
40         return parent::supportsNormalization($data, $format) && (isset($this->cache[$type = \get_class($data)]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type));
41     }
42
43     /**
44      * {@inheritdoc}
45      */
46     public function supportsDenormalization($data, $type, $format = null)
47     {
48         return parent::supportsDenormalization($data, $type, $format) && (isset($this->cache[$type]) ? $this->cache[$type] : $this->cache[$type] = $this->supports($type));
49     }
50
51     /**
52      * Checks if the given class has any non-static property.
53      *
54      * @param string $class
55      *
56      * @return bool
57      */
58     private function supports($class)
59     {
60         $class = new \ReflectionClass($class);
61
62         // We look for at least one non-static property
63         do {
64             foreach ($class->getProperties() as $property) {
65                 if (!$property->isStatic()) {
66                     return true;
67                 }
68             }
69         } while ($class = $class->getParentClass());
70
71         return false;
72     }
73
74     /**
75      * {@inheritdoc}
76      */
77     protected function isAllowedAttribute($classOrObject, $attribute, $format = null, array $context = array())
78     {
79         if (!parent::isAllowedAttribute($classOrObject, $attribute, $format, $context)) {
80             return false;
81         }
82
83         try {
84             $reflectionProperty = $this->getReflectionProperty($classOrObject, $attribute);
85             if ($reflectionProperty->isStatic()) {
86                 return false;
87             }
88         } catch (\ReflectionException $reflectionException) {
89             return false;
90         }
91
92         return true;
93     }
94
95     /**
96      * {@inheritdoc}
97      */
98     protected function extractAttributes($object, $format = null, array $context = array())
99     {
100         $reflectionObject = new \ReflectionObject($object);
101         $attributes = array();
102
103         do {
104             foreach ($reflectionObject->getProperties() as $property) {
105                 if (!$this->isAllowedAttribute($reflectionObject->getName(), $property->name)) {
106                     continue;
107                 }
108
109                 $attributes[] = $property->name;
110             }
111         } while ($reflectionObject = $reflectionObject->getParentClass());
112
113         return $attributes;
114     }
115
116     /**
117      * {@inheritdoc}
118      */
119     protected function getAttributeValue($object, $attribute, $format = null, array $context = array())
120     {
121         try {
122             $reflectionProperty = $this->getReflectionProperty($object, $attribute);
123         } catch (\ReflectionException $reflectionException) {
124             return;
125         }
126
127         // Override visibility
128         if (!$reflectionProperty->isPublic()) {
129             $reflectionProperty->setAccessible(true);
130         }
131
132         return $reflectionProperty->getValue($object);
133     }
134
135     /**
136      * {@inheritdoc}
137      */
138     protected function setAttributeValue($object, $attribute, $value, $format = null, array $context = array())
139     {
140         try {
141             $reflectionProperty = $this->getReflectionProperty($object, $attribute);
142         } catch (\ReflectionException $reflectionException) {
143             return;
144         }
145
146         if ($reflectionProperty->isStatic()) {
147             return;
148         }
149
150         // Override visibility
151         if (!$reflectionProperty->isPublic()) {
152             $reflectionProperty->setAccessible(true);
153         }
154
155         $reflectionProperty->setValue($object, $value);
156     }
157
158     /**
159      * @param string|object $classOrObject
160      * @param string        $attribute
161      *
162      * @return \ReflectionProperty
163      *
164      * @throws \ReflectionException
165      */
166     private function getReflectionProperty($classOrObject, $attribute)
167     {
168         $reflectionClass = new \ReflectionClass($classOrObject);
169         while (true) {
170             try {
171                 return $reflectionClass->getProperty($attribute);
172             } catch (\ReflectionException $e) {
173                 if (!$reflectionClass = $reflectionClass->getParentClass()) {
174                     throw $e;
175                 }
176             }
177         }
178     }
179 }