Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5fe5737

Browse files
committed
[Serializer] Add a NormalizerGenerator
1 parent e0bdc0c commit 5fe5737

File tree

3 files changed

+273
-23
lines changed

3 files changed

+273
-23
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Dumper;
13+
14+
use Symfony\Component\Serializer\NormalizerInterface;
15+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
16+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactoryInterface;
17+
18+
/**
19+
* @author Guilhem Niot <[email protected]>
20+
* @author Amrouche Hamza <[email protected]>
21+
* @experimental
22+
*/
23+
final class NormalizerDumper
24+
{
25+
private $classMetadataFactory;
26+
27+
public function __construct(ClassMetadataFactoryInterface $classMetadataFactory)
28+
{
29+
$this->classMetadataFactory = $classMetadataFactory;
30+
}
31+
32+
public function dump(string $class, array $context = array())
33+
{
34+
$reflectionClass = new \ReflectionClass($class);
35+
if (!isset($context['class'])) {
36+
$context['class'] = $reflectionClass->getShortName().'Normalizer';
37+
}
38+
39+
$namespaceLine = isset($context['namespace']) ? "\nnamespace {$context['namespace']};\n" : '';
40+
41+
return <<<EOL
42+
<?php
43+
$namespaceLine
44+
use Symfony\Component\Serializer\Exception\CircularReferenceException;
45+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
46+
use Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait;
47+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
48+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
49+
50+
/**
51+
* This class is generated.
52+
* Please do not update it manually.
53+
*/
54+
class {$context['class']} implements NormalizerInterface, NormalizerAwareInterface
55+
{
56+
use NormalizerAwareTrait;
57+
58+
{$this->generateNormalizeMethod($reflectionClass)}
59+
60+
{$this->generateSupportsNormalizationMethod($reflectionClass)}
61+
}
62+
EOL;
63+
}
64+
65+
/**
66+
* Generates the {@see NormalizerInterface::normalize} method.
67+
*/
68+
private function generateNormalizeMethod(\ReflectionClass $reflectionClass): string
69+
{
70+
return <<<EOL
71+
public function normalize(\$object, \$format = null, array \$context = array())
72+
{
73+
{$this->generateNormalizeMethodInner($reflectionClass)}
74+
}
75+
EOL;
76+
}
77+
78+
private function generateNormalizeMethodInner(\ReflectionClass $reflectionClass): string
79+
{
80+
$code = <<<EOL
81+
82+
\$objectHash = spl_object_hash(\$object);
83+
if (isset(\$context[ObjectNormalizer::CIRCULAR_REFERENCE_LIMIT][\$objectHash])) {
84+
return null;
85+
} else {
86+
\$context[ObjectNormalizer::CIRCULAR_REFERENCE_LIMIT][\$objectHash] = 1;
87+
}
88+
89+
\$groups = isset(\$context[ObjectNormalizer::GROUPS]) && is_array(\$context[ObjectNormalizer::GROUPS]) ? \$context[ObjectNormalizer::GROUPS] : null;
90+
91+
\$output = array();
92+
EOL;
93+
94+
$attributesMetadata = $this->classMetadataFactory->getMetadataFor($reflectionClass->name)->getAttributesMetadata();
95+
$maxDepthCode = '';
96+
foreach ($attributesMetadata as $attributeMetadata) {
97+
if (null === $maxDepth = $attributeMetadata->getMaxDepth()) {
98+
continue;
99+
}
100+
101+
$key = sprintf(ObjectNormalizer::DEPTH_KEY_PATTERN, $reflectionClass->name, $attributeMetadata->name);
102+
$maxDepthCode .= <<<EOL
103+
isset(\$context['{$key}']) ? ++\$context['{$key}'] : \$context['{$key}'] = 1;
104+
EOL;
105+
}
106+
107+
if ($maxDepthCode) {
108+
$code .= <<<EOL
109+
110+
if (isset(\$context[ObjectNormalizer::ENABLE_MAX_DEPTH])) {{$maxDepthCode}
111+
}
112+
113+
EOL;
114+
}
115+
116+
foreach ($attributesMetadata as $attributeMetadata) {
117+
$code .= <<<EOL
118+
119+
if ((null === \$groups
120+
EOL;
121+
122+
if ($attributeMetadata->groups) {
123+
$code .= sprintf(" || array_intersect(\$groups, array('%s'))", implode("', '", $attributeMetadata->groups));
124+
}
125+
$code .= ')';
126+
127+
$code .= " && (!isset(\$context['attributes']) || isset(\$context['attributes']['{$attributeMetadata->name}']) || (is_array(\$context['attributes']) && in_array('{$attributeMetadata->name}', \$context['attributes'], true)))";
128+
129+
if (null !== $maxDepth = $attributeMetadata->getMaxDepth()) {
130+
$key = sprintf(ObjectNormalizer::DEPTH_KEY_PATTERN, $reflectionClass->name, $attributeMetadata->name);
131+
$code .= " && (!isset(\$context['{$key}']) || {$maxDepth} >= \$context['{$key}'])";
132+
}
133+
134+
$code .= ') {';
135+
136+
$value = $this->generateGetAttributeValueExpression($attributeMetadata->name, $reflectionClass);
137+
$code .= <<<EOL
138+
139+
\$value = {$value};
140+
if (is_scalar(\$value)) {
141+
\$output['{$attributeMetadata->name}'] = \$value;
142+
} else {
143+
\$subContext = \$context;
144+
if (isset(\$context['attributes']['{$attributeMetadata->name}'])) {
145+
\$subContext['attributes'] = \$context['attributes']['{$attributeMetadata->name}'];
146+
} else {
147+
unset(\$subContext['attributes']);
148+
}
149+
150+
\$output['{$attributeMetadata->name}'] = \$this->normalizer->normalize(\$value, \$format, \$subContext);
151+
}
152+
}
153+
EOL;
154+
}
155+
156+
$code .= <<<EOL
157+
158+
159+
return \$output;
160+
EOL;
161+
162+
return $code;
163+
}
164+
165+
private function generateGetAttributeValueExpression(string $property, \ReflectionClass $reflectionClass): string
166+
{
167+
$camelProp = $this->camelize($property);
168+
169+
foreach ($methods = array("get$camelProp", lcfirst($camelProp), "is$camelProp", "has$camelProp)") as $method) {
170+
if ($reflectionClass->hasMethod($method) && $reflectionClass->getMethod($method)) {
171+
return sprintf('$object->%s()', $method);
172+
}
173+
}
174+
175+
if ($reflectionClass->hasProperty($property) && $reflectionClass->getProperty($property)->isPublic()) {
176+
return sprintf('$object->%s', $property);
177+
}
178+
179+
if ($reflectionClass->hasMethod('__get') && $reflectionClass->getMethod('__get')) {
180+
return sprintf('$object->__get(\'%s\')', $property);
181+
}
182+
183+
throw new \DomainException(sprintf('Neither the property "%s" nor one of the methods "%s()", "__get()" exist and have public access in class "%s".', $property, implode('()", "', $methods), $reflectionClass->name));
184+
}
185+
186+
private function generateSupportsNormalizationMethod(\ReflectionClass $reflectionClass): string
187+
{
188+
$instanceof = '\\'.$reflectionClass->name;
189+
190+
return <<<EOL
191+
public function supportsNormalization(\$data, \$format = null, array \$context = array())
192+
{
193+
return \$data instanceof {$instanceof};
194+
}
195+
EOL;
196+
}
197+
198+
private function camelize(string $string): string
199+
{
200+
return str_replace(' ', '', ucwords(str_replace('_', ' ', $string)));
201+
}
202+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
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\Tests\Dumper;
13+
14+
use Doctrine\Common\Annotations\AnnotationReader;
15+
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
16+
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
17+
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
18+
use Symfony\Component\Serializer\Tests\Normalizer\ObjectNormalizerTest;
19+
use Symfony\Component\Serializer\Dumper\NormalizerDumper;
20+
21+
class NormalizerDumperTest extends ObjectNormalizerTest
22+
{
23+
protected function getNormalizerFor(string $class): NormalizerInterface
24+
{
25+
$normalizerName = 'Test'.md5($class).'Normalizer';
26+
27+
if (!class_exists($normalizerName)) {
28+
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
29+
$dumper = new NormalizerDumper($classMetadataFactory);
30+
31+
eval('?>'.$dumper->dump($class, array('class' => $normalizerName)));
32+
}
33+
34+
$normalizer = new $normalizerName();
35+
$normalizer->setNormalizer($this->serializer);
36+
37+
return $normalizer;
38+
}
39+
}

0 commit comments

Comments
 (0)