Skip to content

Commit

Permalink
Merge branch '6.4' into 7.0
Browse files Browse the repository at this point in the history
* 6.4:
  [Cache] Fix generating proxies when ext-redis v6 is installed
  [Serializer] Fix deserializing nested arrays of objects with mixed keys
  • Loading branch information
nicolas-grekas committed Jul 27, 2023
2 parents 0351be8 + 510e51f commit dc1fab3
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 25 deletions.
4 changes: 2 additions & 2 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ install:
- cd ext
- appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_apcu-5.1.22-8.2-ts-vs16-x86.zip
- 7z x php_apcu-5.1.22-8.2-ts-vs16-x86.zip -y >nul
- appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_redis-5.3.7-8.2-ts-vs16-x86.zip
- 7z x php_redis-5.3.7-8.2-ts-vs16-x86.zip -y >nul
- appveyor DownloadFile https://github.com/symfony/binary-utils/releases/download/v0.1/php_redis-6.0.0-dev-8.2-ts-vs16-x86.zip
- 7z x php_redis-6.0.0-dev-8.2-ts-vs16-x86.zip -y >nul
- cd ..
- copy /Y php.ini-development php.ini-min
- echo memory_limit=-1 >> php.ini-min
Expand Down
23 changes: 14 additions & 9 deletions src/Symfony/Component/Cache/Tests/Traits/RedisProxiesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
class RedisProxiesTest extends TestCase
{
/**
* @requires extension redis
* @requires extension redis < 6
*
* @testWith ["Redis"]
* ["RedisCluster"]
Expand Down Expand Up @@ -85,19 +85,24 @@ public function testRelayProxy()
*/
public function testRedis6Proxy($class, $stub)
{
$this->markTestIncomplete('To be re-enabled when phpredis v6 becomes stable');

$stub = file_get_contents("https://raw.githubusercontent.com/phpredis/phpredis/develop/{$stub}.stub.php");
$stub = preg_replace('/^class /m', 'return; \0', $stub);
$stub = preg_replace('/^return; class ([a-zA-Z]++)/m', 'interface \1StubInterface', $stub, 1);
$stub = preg_replace('/^ public const .*/m', '', $stub);
eval(substr($stub, 5));
if (version_compare(phpversion('redis'), '6.0.0', '<')) {
$this->markTestIncomplete('To be re-enabled when phpredis v6 becomes stable');

$stub = file_get_contents("https://raw.githubusercontent.com/phpredis/phpredis/develop/{$stub}.stub.php");
$stub = preg_replace('/^class /m', 'return; \0', $stub);
$stub = preg_replace('/^return; class ([a-zA-Z]++)/m', 'interface \1StubInterface', $stub, 1);
$stub = preg_replace('/^ public const .*/m', '', $stub);
eval(substr($stub, 5));
$r = new \ReflectionClass($class.'StubInterface');
} else {
$r = new \ReflectionClass($class);
}

$proxy = file_get_contents(\dirname(__DIR__, 2)."/Traits/{$class}6Proxy.php");
$proxy = substr($proxy, 0, 4 + strpos($proxy, '[];'));
$methods = [];

foreach ((new \ReflectionClass($class.'StubInterface'))->getMethods() as $method) {
foreach ($r->getMethods() as $method) {
if ('reset' === $method->name || method_exists(LazyProxyTrait::class, $method->name)) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ private function validateAndDenormalize(array $types, string $currentClass, stri
$class = $collectionValueType->getClassName().'[]';

if (\count($collectionKeyType = $type->getCollectionKeyTypes()) > 0) {
[$context['key_type']] = $collectionKeyType;
$context['key_type'] = \count($collectionKeyType) > 1 ? $collectionKeyType : $collectionKeyType[0];
}

$context['value_type'] = $collectionValueType;
Expand Down
24 changes: 20 additions & 4 deletions src/Symfony/Component/Serializer/Normalizer/ArrayDenormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,15 @@ public function denormalize(mixed $data, string $type, string $format = null, ar

$type = substr($type, 0, -2);

$builtinType = isset($context['key_type']) ? $context['key_type']->getBuiltinType() : null;
$builtinTypes = array_map(static function (Type $keyType) {
return $keyType->getBuiltinType();
}, \is_array($keyType = $context['key_type'] ?? []) ? $keyType : [$keyType]);

foreach ($data as $key => $value) {
$subContext = $context;
$subContext['deserialization_path'] = ($context['deserialization_path'] ?? false) ? sprintf('%s[%s]', $context['deserialization_path'], $key) : "[$key]";

if (null !== $builtinType && !('is_'.$builtinType)($key)) {
throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, $builtinType, get_debug_type($key)), $key, [$builtinType], $subContext['deserialization_path'] ?? null, true);
}
$this->validateKeyType($builtinTypes, $key, $subContext['deserialization_path']);

$data[$key] = $this->denormalizer->denormalize($value, $type, $format, $subContext);
}
Expand All @@ -78,4 +79,19 @@ public function supportsDenormalization(mixed $data, string $type, string $forma
return str_ends_with($type, '[]')
&& $this->denormalizer->supportsDenormalization($data, substr($type, 0, -2), $format, $context);
}

private function validateKeyType(array $builtinTypes, mixed $key, string $path): void
{
if (!$builtinTypes) {
return;
}

foreach ($builtinTypes as $builtinType) {
if (('is_'.$builtinType)($key)) {
return;
}
}

throw NotNormalizableValueException::createForUnexpectedDataType(sprintf('The type of the key "%s" must be "%s" ("%s" given).', $key, implode('", "', $builtinTypes), get_debug_type($key)), $key, $builtinTypes, $path, true);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ public static function provider()
*/
public function testPropertyPhpDoc($class)
{
// GIVEN
$json = <<<EOF
{
"animals": [
Expand All @@ -47,13 +46,62 @@ public function testPropertyPhpDoc($class)
new ObjectNormalizer(null, null, null, new PhpDocExtractor()),
new ArrayDenormalizer(),
], ['json' => new JsonEncoder()]);
// WHEN
/** @var Zoo $zoo */

/** @var Zoo|ZooImmutable $zoo */
$zoo = $serializer->deserialize($json, $class, 'json');
// THEN

self::assertCount(1, $zoo->getAnimals());
self::assertInstanceOf(Animal::class, $zoo->getAnimals()[0]);
}

public function testPropertyPhpDocWithKeyTypes()
{
$json = <<<EOF
{
"animalsInt": [
{"name": "Bug"}
],
"animalsString": {
"animal1": {"name": "Bug"}
},
"animalsUnion": {
"animal2": {"name": "Bug"},
"2": {"name": "Dog"}
},
"animalsGenerics": {
"animal3": {"name": "Bug"},
"3": {"name": "Dog"}
}
}
EOF;
$serializer = new Serializer([
new ObjectNormalizer(null, null, null, new PhpDocExtractor()),
new ArrayDenormalizer(),
], ['json' => new JsonEncoder()]);

/** @var ZooWithKeyTypes $zoo */
$zoo = $serializer->deserialize($json, ZooWithKeyTypes::class, 'json');

self::assertCount(1, $zoo->animalsInt);
self::assertArrayHasKey(0, $zoo->animalsInt);
self::assertInstanceOf(Animal::class, $zoo->animalsInt[0]);

self::assertCount(1, $zoo->animalsString);
self::assertArrayHasKey('animal1', $zoo->animalsString);
self::assertInstanceOf(Animal::class, $zoo->animalsString['animal1']);

self::assertCount(2, $zoo->animalsUnion);
self::assertArrayHasKey('animal2', $zoo->animalsUnion);
self::assertInstanceOf(Animal::class, $zoo->animalsUnion['animal2']);
self::assertArrayHasKey(2, $zoo->animalsUnion);
self::assertInstanceOf(Animal::class, $zoo->animalsUnion[2]);

self::assertCount(2, $zoo->animalsGenerics);
self::assertArrayHasKey('animal3', $zoo->animalsGenerics);
self::assertInstanceOf(Animal::class, $zoo->animalsGenerics['animal3']);
self::assertArrayHasKey(3, $zoo->animalsGenerics);
self::assertInstanceOf(Animal::class, $zoo->animalsGenerics[3]);
}
}

class Zoo
Expand Down Expand Up @@ -100,16 +148,23 @@ public function getAnimals(): array
}
}

class ZooWithKeyTypes
{
/** @var array<int, Animal> */
public $animalsInt = [];
/** @var array<string, Animal> */
public $animalsString = [];
/** @var array<int|string, Animal> */
public $animalsUnion = [];
/** @var \stdClass<Animal> */
public $animalsGenerics = [];
}

class Animal
{
/** @var string */
private $name;

public function __construct()
{
echo '';
}

public function getName(): ?string
{
return $this->name;
Expand Down

0 comments on commit dc1fab3

Please sign in to comment.