Skip to content

Commit

Permalink
[Serializer] Save missing arguments class in MissingConstructorArgume…
Browse files Browse the repository at this point in the history
…ntsException
  • Loading branch information
HypeMC committed Jan 20, 2023
1 parent 5bf96bd commit 73490be
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 6 deletions.
5 changes: 5 additions & 0 deletions UPGRADE-6.3.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,8 @@ Validator
---------

* Implementing the `ConstraintViolationInterface` without implementing the `getConstraint()` method is deprecated

Serializer
----------

* Deprecate passing an array of missing arguments as the fourth argument of the `Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException` constructor, pass the missing arguments class name instead and the missing arguments name as the fifth argument.
1 change: 1 addition & 0 deletions src/Symfony/Component/Serializer/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ CHANGELOG
---

* Add `XmlEncoder::SAVE_OPTIONS` context option
* Save missing arguments class in `MissingConstructorArgumentsException`

6.2
---
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,55 @@
class MissingConstructorArgumentsException extends RuntimeException
{
/**
* @deprecated since Symfony 6.3
*
* @var string[]
*/
private $missingArguments;

public function __construct(string $message, int $code = 0, \Throwable $previous = null, array $missingArguments = [])
private ?string $class;
private ?string $missingArgument;

/**
* @param class-string|null $class
*/
public function __construct(string $message, int $code = 0, \Throwable $previous = null, /* string */ $class = null, string $missingArgument = null)
{
$this->missingArguments = $missingArguments;
if (\is_array($class)) {
trigger_deprecation('symfony/serializer', '6.3', 'Passing an array of missing arguments as the fourth argument of the "%s" constructor is deprecated, pass the missing arguments class name instead and the missing arguments name as the fifth argument.', __CLASS__);

$this->missingArguments = $class;

$class = null;
$missingArgument = $this->missingArguments[0] ?? null;
}

if (null !== $class && !\is_string($class)) {
throw new \TypeError(sprintf('Argument 4 passed to "%s()" must be a string or null, "%s" given.', __METHOD__, get_debug_type($class)));
}

$this->class = $class;
$this->missingArgument = $missingArgument;

$this->missingArguments ??= null !== $this->missingArgument ? [$this->missingArgument] : [];

parent::__construct($message, $code, $previous);
}

public function getClass(): ?string
{
return $this->class;
}

public function getMissingArgument(): ?string
{
return $this->missingArgument;
}

/**
* @return string[]
*
* @deprecated since Symfony 6.3, use {@see getMissingArgument()} instead
*/
public function getMissingConstructorArguments(): array
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ protected function instantiateObject(array &$data, string $class, array &$contex
$params[] = null;
} else {
if (!isset($context['not_normalizable_value_exceptions'])) {
throw new MissingConstructorArgumentsException(sprintf('Cannot create an instance of "%s" from serialized data because its constructor requires parameter "%s" to be present.', $class, $constructorParameter->name), 0, null, [$constructorParameter->name]);
throw new MissingConstructorArgumentsException(sprintf('Cannot create an instance of "%s" from serialized data because its constructor requires parameter "%s" to be present.', $class, $constructorParameter->name), 0, null, $class, $constructorParameter->name);
}

$exception = NotNormalizableValueException::createForUnexpectedDataType(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Serializer\Tests\Exception;

use PHPUnit\Framework\TestCase;
use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait;
use Symfony\Component\Serializer\Exception\MissingConstructorArgumentsException;

/**
* @group legacy
*/
class MissingConstructorArgumentsExceptionTest extends TestCase
{
use ExpectDeprecationTrait;

/**
* @dataProvider provideValidArguments
*/
public function testValidArguments(
array|string|null $class,
?string $missingArgument,
?string $expectedClass,
?string $expectedMissingArgument,
array $expectedMissingConstructorArguments,
) {
if (\is_array($class)) {
$this->expectDeprecation(sprintf('Since symfony/serializer 6.3: Passing an array of missing arguments as the fourth argument of the "%s" constructor is deprecated, pass the missing arguments class name instead and the missing arguments name as the fifth argument.', MissingConstructorArgumentsException::class));
}

$exception = new MissingConstructorArgumentsException('', 0, null, $class, $missingArgument);

self::assertSame($expectedClass, $exception->getClass());
self::assertSame($expectedMissingArgument, $exception->getMissingArgument());
self::assertSame($expectedMissingConstructorArguments, $exception->getMissingConstructorArguments());
}

public static function provideValidArguments(): iterable
{
yield 'Without class and missingArgument' => [null, null, null, null, []];
yield 'With class and missingArgument' => ['Foo', 'bar', 'Foo', 'bar', ['bar']];
yield 'With missingArguments' => [['foo', 'bar'], null, null, 'foo', ['foo', 'bar']];
}

public function testInvalidFourthArgument()
{
$this->expectException(\TypeError::class);
$this->expectExceptionMessage(sprintf('Argument 4 passed to "%s::__construct()" must be a string or null, "bool" given.', MissingConstructorArgumentsException::class));

new MissingConstructorArgumentsException('', 0, null, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,13 @@ public function testConstructorWithMissingData()

$normalizer = $this->getDenormalizerForConstructArguments();

$this->expectException(MissingConstructorArgumentsException::class);
$this->expectExceptionMessage('Cannot create an instance of "'.ConstructorArgumentsObject::class.'" from serialized data because its constructor requires parameter "bar" to be present.');
$normalizer->denormalize($data, ConstructorArgumentsObject::class);
try {
$normalizer->denormalize($data, ConstructorArgumentsObject::class);
self::fail(sprintf('Failed asserting that exception of type "%s" is thrown.', MissingConstructorArgumentsException::class));
} catch (MissingConstructorArgumentsException $e) {
self::assertSame(sprintf('Cannot create an instance of "%s" from serialized data because its constructor requires parameter "bar" to be present.', ConstructorArgumentsObject::class), $e->getMessage());
self::assertSame(ConstructorArgumentsObject::class, $e->getClass());
self::assertSame('bar', $e->getMissingArgument());
}
}
}

0 comments on commit 73490be

Please sign in to comment.