8000 [JsonEncoder] Fix max depth handling and json errors · symfony/symfony@e8e62bc · GitHub
[go: up one dir, main page]

Skip to content

Commit e8e62bc

Browse files
committed
[JsonEncoder] Fix max depth handling and json errors
1 parent 5876c48 commit e8e62bc

39 files changed

+619
-255
lines changed

src/Symfony/Component/JsonEncoder/DataModel/Encode/BackedEnumNode.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ public function __construct(
3131
) {
3232
}
3333

34+
public function withAccessor(DataAccessorInterface $accessor): self
35+
{
36+
return new self($accessor, $this->type);
37+
}
38+
39+
public function getIdentifier(): string
40+
{
41+
return (string) $this->getType();
42+
}
43+
3444
public function getAccessor(): DataAccessorInterface
3545
{
3646
return $this->accessor;

src/Symfony/Component/JsonEncoder/DataModel/Encode/CollectionNode.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ public function __construct(
3030
) {
3131
}
3232

33+
public function withAccessor(DataAccessorInterface $accessor): self
34+
{
35+
return new self($accessor, $this->type, $this->item);
36+
}
37+
38+
public function getIdentifier(): string
39+
{
40+
return (string) $this->getType();
41+
}
42+
3343
public function getAccessor(): DataAccessorInterface
3444
{
3545
return $this->accessor;

src/Symfony/Component/JsonEncoder/DataModel/Encode/CompositeNode.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,16 @@ public function __construct(
6060
$this->nodes = $nodes;
6161
}
6262

63+
public function withAccessor(DataAccessorInterface $accessor): self
64+
{
65+
return new self($accessor, array_map(static fn (DataModelNodeInterface $n): DataModelNodeInterface => $n->withAccessor($accessor), $this->nodes));
66+
}
67+
68+
public function getIdentifier(): string
69+
{
70+
return (string) $this->getType();
71+
}
72+
6373
public function getAccessor(): DataAccessorInterface
6474
{
6575
return $this->accessor;

src/Symfony/Component/JsonEncoder/DataModel/Encode/DataModelNodeInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
*/
2424
interface DataModelNodeInterface
2525
{
26+
public function getIdentifier(): string;
27+
2628
public function getType(): Type;
2729

2830
public function getAccessor(): DataAccessorInterface;
31+
32+
public function withAccessor(DataAccessorInterface $accessor): self;
2933
}

src/Symfony/Component/JsonEncoder/DataModel/Encode/ExceptionNode.php

Lines changed: 0 additions & 49 deletions
This file was deleted.

src/Symfony/Component/JsonEncoder/DataModel/Encode/ObjectNode.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Component\JsonEncoder\DataModel\Encode;
1313

1414
use Symfony\Component\JsonEncoder\DataModel\DataAccessorInterface;
15+
use Symfony\Component\JsonEncoder\DataModel\FunctionDataAccessor;
16+
use Symfony\Component\JsonEncoder\DataModel\PropertyDataAccessor;
1517
use Symfony\Component\TypeInfo\Type\ObjectType;
1618

1719
/**
@@ -30,9 +32,36 @@ public function __construct(
3032
private DataAccessorInterface $accessor,
3133
private ObjectType $type,
3234
private array $properties,
35+
private bool $ghost = false,
3336
) {
3437
}
3538

39+
public static function createGhost(DataAccessorInterface $accessor, ObjectType $type): self
40+
{
41+
return new self($accessor, $type, [], true);
42+
}
43+
44+
public function withAccessor(DataAccessorInterface $accessor): self
45+
{
46+
$properties = [];
47+
foreach ($this->properties as $key => $property) {
48+
$propertyAccessor = $property->getAccessor();
49+
50+
if ($propertyAccessor instanceof PropertyDataAccessor || $propertyAccessor instanceof FunctionDataAccessor && $propertyAccessor->getObjectAccessor()) {
51+
$propertyAccessor = $propertyAccessor->withObjectAccessor($accessor);
52+
}
53+
54+
$properties[$key] = $property->withAccessor($propertyAccessor);
55+
}
56+
57+
return new self($accessor, $this->type, $properties, $this->isGhost());
58+
}
59+
60+
public function getIdentifier(): string
61+
{
62+
return (string) $this->getType();
63+
}
64+
3665
public function getAccessor(): DataAccessorInterface
3766
{
3867
return $this->accessor;
@@ -50,4 +79,9 @@ public function getProperties(): array
5079
{
5180
return $this->properties;
5281
}
82+
83+
public function isGhost(): bool
84+
{
85+
return $this->ghost;
86+
}
5387
}

src/Symfony/Component/JsonEncoder/DataModel/Encode/ScalarNode.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ public function __construct(
3131
) {
3232
}
3333

34+
public function withAccessor(DataAccessorInterface $accessor): self
35+
{
36+
return new self($accessor, $this->type);
37+
}
38+
39+
public function getIdentifier(): string
40+
{
41+
return (string) $this->getType();
42+
}
43+
3444
public function getAccessor(): DataAccessorInterface
3545
{
3646
return $this->accessor;

src/Symfony/Component/JsonEncoder/DataModel/FunctionDataAccessor.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ public function __construct(
3333
) {
3434
}
3535

36+
public function getObjectAccessor(): ?DataAccessorInterface
37+
{
38+
return $this->objectAccessor;
39+
}
40+
41+
public function withObjectAccessor(?DataAccessorInterface $accessor): self
42+
{
43+
return new self($this->functionName, $this->arguments, $accessor);
44+
}
45+
3646
public function toPhpExpr(): Expr
3747
{
3848
$builder = new BuilderFactory();

src/Symfony/Component/JsonEncoder/DataModel/PropertyDataAccessor.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ public function __construct(
2929
) {
3030
}
3131

32+
public function getObjectAccessor(): DataAccessorInterface
33+
{
34+
return $this->objectAccessor;
35+
}
36+
37+
public function withObjectAccessor(DataAccessorInterface $accessor): self
38+
{
39+
return new self($accessor, $this->propertyName);
40+
}
41+
3242
public function toPhpExpr(): Expr
3343
{
3444
return (new BuilderFactory())->propertyFetch($this->objectAccessor->toPhpExpr(), $this->propertyName);

src/Symfony/Component/JsonEncoder/Encode/EncoderGenerator.php

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,12 @@
2121
use Symfony\Component\JsonEncoder\DataModel\Encode\CollectionNode;
2222
use Symfony\Component\JsonEncoder\DataModel\Encode\CompositeNode;
2323
use Symfony\Component\JsonEncoder\DataModel\Encode\DataModelNodeInterface;
24-
use Symfony\Component\JsonEncoder\DataModel\Encode\ExceptionNode;
2524
use Symfony\Component\JsonEncoder\DataModel\Encode\ObjectNode;
2625
use Symfony\Component\JsonEncoder\DataModel\Encode\ScalarNode;
2726
use Symfony\Component\JsonEncoder\DataModel\FunctionDataAccessor;
2827
use Symfony\Component\JsonEncoder\DataModel\PropertyDataAccessor;
2928
use Symfony\Component\JsonEncoder\DataModel\ScalarDataAccessor;
3029
use Symfony\Component\JsonEncoder\DataModel\VariableDataAccessor;
31-
use Symfony\Component\JsonEncoder\Exception\MaxDepthException;
3230
use Symfony\Component\JsonEncoder\Exception\RuntimeException;
3331
use Symfony\Component\JsonEncoder\Exception\UnsupportedException;
3432
use Symfony\Component\JsonEncoder\Mapping\PropertyMetadataLoaderInterface;
@@ -49,8 +47,6 @@
4947
*/
5048
final class EncoderGenerator
5149
{
52-
private const MAX_DEPTH = 512;
53-
5450
private ?PhpAstBuilder $phpAstBuilder = null;
5551
private ?PhpOptimizer $phpOptimizer = null;
5652
private ?PrettyPrinter $phpPrinter = null;
@@ -114,12 +110,6 @@ private function getPath(Type $type): string
114110
*/
115111
private function createDataModel(Type $type, DataAccessorInterface $accessor, array $options = [], array $context = []): DataModelNodeInterface
116112
{
117-
$context['depth'] ??= 0;
118-
119-
if ($context['depth'] > self::MAX_DEPTH) {
120-
return new ExceptionNode(MaxDepthException::class);
121-
}
122-
123113
$context['original_type'] ??= $type;
124114

125115
if ($type instanceof UnionType) {
@@ -135,9 +125,14 @@ private function createDataModel(Type $type, DataAccessorInterface $accessor, ar
135125
}
136126

137127
if ($type instanceof ObjectType && !$type instanceof EnumType) {
138-
++$context['depth'];
139-
128+
$typeString = (string) $type;
140129
$className = $type->getClassName();
130+
131+
if ($context['generated_classes'][$typeString] ??= false) {
132+
return ObjectNode::createGhost($accessor, $type);
133+
}
134+
135+
$context['generated_classes'][$typeString] = true;
141136
$propertiesMetadata = $this->propertyMetadataLoader->load($className, $options, ['original_type' => $type] + $context);
142137

143138
try {
@@ -180,8 +175,6 @@ private function createDataModel(Type $type, DataAccessorInterface $accessor, ar
180175
}
181176

182177
if ($type instanceof CollectionType) {
183-
++$context['depth'];
184-
185178
return new CollectionNode(
186179
$accessor,
187180
$type,

0 commit comments

Comments
 (0)
0