Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Serializer] Preserve array keys while denormalize variadic parameters #49618

Conversation

melya
Copy link
Contributor

@melya melya commented Mar 6, 2023

Q A
Branch? 5.4
Bug fix? yes
New feature? no
Deprecations? no
Tickets Fix #49616
License MIT
Doc PR Let me know if needed
PHP 8.1.15

Hi Symfony folks!

I've struggled with denormalization issue.
It appears when serialize an object with variadic parameter from associative array.
Currently, after denormalization array keys get reset and it is not compatible with previous version of object.

Example:

<?php

declare(strict_types=1);

use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
use Symfony\Component\Serializer\Serializer;

require_once __DIR__ . "/vendor/autoload.php";


final class Dummy {
    public function __construct(public readonly string $i)
    {
    }
}

final class DummyWithVariadic {
    public $dummies;
    public function __construct(Dummy ...$dummies)
    {
        $this->dummies = $dummies;
    }
}

$serializer = new Serializer([$normalizer = new PropertyNormalizer()], [new JsonEncoder()]);
$normalizer->setSerializer($serializer);

$dummies = ["d1" => new Dummy("d1 value"), "d2" => new Dummy("d2 value")];
$object  = new DummyWithVariadic(...$dummies);
var_dump($object);
/* Output:
class DummyWithVariadic#310 (1) {
  public $dummies =>
  array(2) {
    'd1' =>
    class Dummy#308 (1) {
      public readonly string $i =>
      string(8) "d1 value"
    }
    'd2' =>
    class Dummy#309 (1) {
      public readonly string $i =>
      string(8) "d2 value"
    }
  }
}
*/

$json = $serializer->serialize($object, "json");
echo $json . PHP_EOL;
/* Output:
{"dummies":{"d1":{"i":"d1 value"},"d2":{"i":"d2 value"}}}
*/

$deserialized = $serializer->deserialize($json, DummyWithVariadic::class, "json");
var_dump($deserialized);
/* Output:
class DummyWithVariadic#315 (1) {
  public $dummies =>
  array(2) {
    [0] =>
    class Dummy#319 (1) {
      public readonly string $i =>
      string(8) "d1 value"
    }
    [1] =>
    class Dummy#320 (1) {
      public readonly string $i =>
      string(8) "d2 value"
    }
  }
}
*/

$jsonFromDeserialized = $serializer->serialize($deserialized, "json");
echo $jsonFromDeserialized . PHP_EOL;
/* Output:
{"dummies":[{"i":"d1 value"},{"i":"d2 value"}]}
*/

var_dump($json === $jsonFromDeserialized); // And now the deserialization result without respected array keys :(
/* Output:
bool(false)
*/

Let me know if we can add this fix for earlier versions 🙌

Bug fixes must be submitted against the lowest maintained branch where they apply
(lowest branches are regularly merged to upper ones so they get the fixes too).

I hope it's not , feedback is appreciated!

Never break backward compatibility (see https://symfony.com/bc)

@melya
Copy link
Contributor Author

melya commented Mar 6, 2023

Also, next scenario could be taken into consideration as well. 🤔
*Numeric array-keys unpacked into normally indexed array

$dummies = ["345" => new Dummy("d1 value"), "321" => new Dummy("d2 value")];
$object  = new DummyWithVariadic(...$dummies);
var_dump($object);
/* Output:
class DummyWithVariadic#310 (1) {
  public $dummies =>
  array(2) {
    0 =>
    class Dummy#308 (1) {
      public readonly string $i =>
      string(8) "d1 value"
    }
    1 =>
    class Dummy#309 (1) {
      public readonly string $i =>
      string(8) "d2 value"
    }
  }
}
*/
$dummies = ["345" => new Dummy("d1 value"), "d1" => new Dummy("d2 value")];
$object  = new DummyWithVariadic(...$dummies);
var_dump($object);
/* Output:
class DummyWithVariadic#310 (1) {
  public $dummies =>
  array(2) {
    0 =>
    class Dummy#308 (1) {
      public readonly string $i =>
      string(8) "d1 value"
    }
    'd1' =>
    class Dummy#309 (1) {
      public readonly string $i =>
      string(8) "d2 value"
    }
  }
}
*/

UPDATE: Most likely, symfony can't do nothing about this scenario as this is how PHP unpacks array 🤷‍♂️

@nicolas-grekas nicolas-grekas modified the milestones: 6.2, 5.4 Mar 31, 2023
@nicolas-grekas nicolas-grekas changed the base branch from 6.2 to 5.4 March 31, 2023 08:37
@nicolas-grekas nicolas-grekas force-pushed the serializer/preserve-array-keys-while-denormalize branch from 1eb4c26 to c1f844a Compare March 31, 2023 08:37
@nicolas-grekas
Copy link
Member

Thank you @melya.

@nicolas-grekas nicolas-grekas merged commit dc6c016 into symfony:5.4 Mar 31, 2023
8 of 11 checks passed
This was referenced Mar 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants