diff --git a/src/Symfony/Component/JsonStreamer/CHANGELOG.md b/src/Symfony/Component/JsonStreamer/CHANGELOG.md index 87f1e74c951da..fdc1439a90748 100644 --- a/src/Symfony/Component/JsonStreamer/CHANGELOG.md +++ b/src/Symfony/Component/JsonStreamer/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Remove `nikic/php-parser` dependency + * Add `_current_object` to the context passed to value transformers during write operations 7.3 --- diff --git a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_value_transformer.php b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_value_transformer.php index 54994fbfd1c18..e2a92696652e7 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_value_transformer.php +++ b/src/Symfony/Component/JsonStreamer/Tests/Fixtures/stream_writer/object_with_value_transformer.php @@ -6,13 +6,13 @@ return static function (mixed $data, \Psr\Container\ContainerInterface $valueTransformers, array $options): \Traversable { try { yield '{"id":'; - yield \json_encode($valueTransformers->get('Symfony\Component\JsonStreamer\Tests\Fixtures\ValueTransformer\DoubleIntAndCastToStringValueTransformer')->transform($data->id, $options), \JSON_THROW_ON_ERROR, 511); + yield \json_encode($valueTransformers->get('Symfony\Component\JsonStreamer\Tests\Fixtures\ValueTransformer\DoubleIntAndCastToStringValueTransformer')->transform($data->id, $options + ['_current_object' => $data]), \JSON_THROW_ON_ERROR, 511); yield ',"active":'; - yield \json_encode($valueTransformers->get('Symfony\Component\JsonStreamer\Tests\Fixtures\ValueTransformer\BooleanToStringValueTransformer')->transform($data->active, $options), \JSON_THROW_ON_ERROR, 511); + yield \json_encode($valueTransformers->get('Symfony\Component\JsonStreamer\Tests\Fixtures\ValueTransformer\BooleanToStringValueTransformer')->transform($data->active, $options + ['_current_object' => $data]), \JSON_THROW_ON_ERROR, 511); yield ',"name":'; yield \json_encode(strtolower($data->name), \JSON_THROW_ON_ERROR, 511); yield ',"range":'; - yield \json_encode(Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::concatRange($data->range, $options), \JSON_THROW_ON_ERROR, 511); + yield \json_encode(Symfony\Component\JsonStreamer\Tests\Fixtures\Model\DummyWithValueTransformerAttributes::concatRange($data->range, $options + ['_current_object' => $data]), \JSON_THROW_ON_ERROR, 511); yield '}'; } catch (\JsonException $e) { throw new \Symfony\Component\JsonStreamer\Exception\NotEncodableValueException($e->getMessage(), 0, $e); diff --git a/src/Symfony/Component/JsonStreamer/Tests/JsonStreamWriterTest.php b/src/Symfony/Component/JsonStreamer/Tests/JsonStreamWriterTest.php index 14cc50881d0d1..5235801fb2f3d 100644 --- a/src/Symfony/Component/JsonStreamer/Tests/JsonStreamWriterTest.php +++ b/src/Symfony/Component/JsonStreamer/Tests/JsonStreamWriterTest.php @@ -157,6 +157,42 @@ public function testWriteObjectWithValueTransformer() ); } + public function testValueTransformerHasAccessToCurrentObject() + { + $dummy = new DummyWithValueTransformerAttributes(); + $dummy->id = 10; + $dummy->active = true; + + $this->assertWritten( + '{"id":"20","active":"true","name":"dummy","range":"10..20"}', + $dummy, + Type::object(DummyWithValueTransformerAttributes::class), + options: ['scale' => 1], + valueTransformers: [ + BooleanToStringValueTransformer::class => new class($this) implements ValueTransformerInterface { + public function __construct( + private JsonStreamWriterTest $test, + ) { + } + + public function transform(mixed $value, array $options = []): mixed + { + $this->test->assertArrayHasKey('_current_object', $options); + $this->test->assertInstanceof(DummyWithValueTransformerAttributes::class, $options['_current_object']); + + return (new BooleanToStringValueTransformer())->transform($value, $options); + } + + public static function getStreamValueType(): Type + { + return BooleanToStringValueTransformer::getStreamValueType(); + } + }, + DoubleIntAndCastToStringValueTransformer::class => new DoubleIntAndCastToStringValueTransformer(), + ], + ); + } + public function testWriteObjectWithPhpDoc() { $dummy = new DummyWithPhpDoc(); diff --git a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php index 4035d84770fbf..ce6737239c58c 100644 --- a/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php +++ b/src/Symfony/Component/JsonStreamer/Write/StreamWriterGenerator.php @@ -138,7 +138,7 @@ private function createDataModel(Type $type, string $accessor, array $options = foreach ($propertyMetadata->getNativeToStreamValueTransformer() as $valueTransformer) { if (\is_string($valueTransformer)) { $valueTransformerServiceAccessor = "\$valueTransformers->get('$valueTransformer')"; - $propertyAccessor = "{$valueTransformerServiceAccessor}->transform($propertyAccessor, \$options)"; + $propertyAccessor = "{$valueTransformerServiceAccessor}->transform($propertyAccessor, \$options + ['_current_object' => $accessor])"; continue; } @@ -152,7 +152,7 @@ private function createDataModel(Type $type, string $accessor, array $options = $functionName = !$functionReflection->getClosureCalledClass() ? $functionReflection->getName() : \sprintf('%s::%s', $functionReflection->getClosureCalledClass()->getName(), $functionReflection->getName()); - $arguments = $functionReflection->isUserDefined() ? "$propertyAccessor, \$options" : $propertyAccessor; + $arguments = $functionReflection->isUserDefined() ? "$propertyAccessor, \$options + ['_current_object' => $accessor]" : $propertyAccessor; $propertyAccessor = "$functionName($arguments)"; }