Skip to content

Commit

Permalink
Generate recursive code up to the defined max depth value (#30)
Browse files Browse the repository at this point in the history
With this addition, it is now possible to generate recursive code up to a specified maximum depth that can be provided via the `MaxDepth` annotation/attribute from JMS
  • Loading branch information
Spea authored Dec 13, 2022
1 parent 047b565 commit 2e0314d
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 1 deletion.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

# 2.1.0 (unreleased)

* Add support for generating recursive code up to a specified maximum depth
that can be defined via the `@MaxDepth` annotation/attribute from JMS

# 2.0.6

* Allow installation with liip/metadata-parser 0.5
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"php": "^7.2 || ^8.0",
"ext-json": "*",
"doctrine/annotations": "^1.10.2",
"liip/metadata-parser": "^0.2 || ^0.3 || ^0.4 || ^0.5",
"liip/metadata-parser": "^0.6",
"pnz/json-exception": "^1.0",
"symfony/filesystem": "^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^4.4 || ^5.0 || ^6.0",
Expand Down
4 changes: 4 additions & 0 deletions src/DeserializerGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ private function generateCodeForProperty(
if ($propertyMetadata->isReadOnly()) {
return '';
}

if (Recursion::hasMaxDepthReached($propertyMetadata, $stack)) {
return '';
}

if ($propertyMetadata->getAccessor()->hasSetterMethod()) {
$tempVariable = ModelPath::tempVariable([(string) $modelPath, $propertyMetadata->getName()]);
Expand Down
35 changes: 35 additions & 0 deletions src/Recursion.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

namespace Liip\Serializer;

use Liip\MetadataParser\Metadata\PropertyMetadata;
use Liip\MetadataParser\Metadata\PropertyTypeArray;
use Liip\MetadataParser\Metadata\PropertyTypeClass;

abstract class Recursion
{
public static function check(string $className, array $stack, string $modelPath): bool
Expand All @@ -14,4 +18,35 @@ public static function check(string $className, array $stack, string $modelPath)

return false;
}


public static function hasMaxDepthReached(PropertyMetadata $propertyMetadata, array $stack): bool
{
if (null === $propertyMetadata->getMaxDepth()) {
return false;
}

$className = self::getClassNameFromProperty($propertyMetadata);
if (null === $className) {
return false;
}

$classStackCount = $stack[$className] ?? 0;

return $classStackCount > $propertyMetadata->getMaxDepth();
}

private static function getClassNameFromProperty(PropertyMetadata $propertyMetadata): ?string
{
$type = $propertyMetadata->getType();
if ($type instanceof PropertyTypeArray) {
$type = $type->getLeafType();
}

if (!($type instanceof PropertyTypeClass)) {
return null;
}

return $type->getClassName();
}
}
4 changes: 4 additions & 0 deletions src/SerializerGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,10 @@ private function generateCodeForField(
string $modelPath,
array $stack
): string {
if (Recursion::hasMaxDepthReached($propertyMetadata, $stack)) {
return '';
}

$modelPropertyPath = $modelPath.'->'.$propertyMetadata->getName();
$fieldPath = $arrayPath.'["'.$propertyMetadata->getSerializedName().'"]';

Expand Down
19 changes: 19 additions & 0 deletions tests/Fixtures/RecursionModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace Tests\Liip\Serializer\Fixtures;

use JMS\Serializer\Annotation as Serializer;

class RecursionModel
{
/**
* @Serializer\Type("string")
*/
public $property;

/**
* @Serializer\MaxDepth(2)
* @Serializer\Type("Tests\Liip\Serializer\Fixtures\RecursionModel")
*/
public $recursion;
}
32 changes: 32 additions & 0 deletions tests/Unit/DeserializerGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Tests\Liip\Serializer\Fixtures\NonEmptyConstructor;
use Tests\Liip\Serializer\Fixtures\PostDeserialize;
use Tests\Liip\Serializer\Fixtures\PrivateProperty;
use Tests\Liip\Serializer\Fixtures\RecursionModel;
use Tests\Liip\Serializer\Fixtures\VirtualProperties;

/**
Expand Down Expand Up @@ -96,6 +97,37 @@ public function testLists(): void
}
}

public function testRecursion(): void
{
$functionName = 'deserialize_Tests_Liip_Serializer_Fixtures_RecursionModel';
self::generateDeserializer(self::$metadataBuilder, RecursionModel::class, $functionName);

$input = [
'property'=> 'property',
'recursion' => [
'property' => 'recursive property',
'recursion' => [
'property' => 'recursive recursive property',
'recursion' => [
'property' => 'recursive recursive recursive property',
],
],
],
];

/** @var RecursionModel $model */
$model = $functionName($input);
static::assertInstanceOf(RecursionModel::class, $model);
static::assertSame('property', $model->property);
static::assertInstanceOf(RecursionModel::class, $model->recursion);

static::assertSame('recursive property', $model->recursion->property);
static::assertInstanceOf(RecursionModel::class, $model->recursion->recursion);

static::assertSame('recursive recursive property', $model->recursion->recursion->property);
static::assertNull($model->recursion->recursion->recursion);
}

/**
* JSON has no type information and if no .0 is used, integers and floats can be confused.
*/
Expand Down
29 changes: 29 additions & 0 deletions tests/Unit/SerializerGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Tests\Liip\Serializer\Fixtures\Nested;
use Tests\Liip\Serializer\Fixtures\PostDeserialize;
use Tests\Liip\Serializer\Fixtures\PrivateProperty;
use Tests\Liip\Serializer\Fixtures\RecursionModel;
use Tests\Liip\Serializer\Fixtures\Versions;
use Tests\Liip\Serializer\Fixtures\VirtualProperties;

Expand Down Expand Up @@ -118,6 +119,34 @@ public function testArrays(): void
static::assertSame($expected, $data);
}

public function testRecursions(): void
{
$functionName = 'serialize_Tests_Liip_Serializer_Fixtures_RecursionModel';
self::generateSerializers(self::$metadataBuilder, RecursionModel::class, [$functionName], ['']);

$model = new RecursionModel();
$model->property = 'property';
$model->recursion = new RecursionModel();
$model->recursion->property = 'recursive property';
$model->recursion->recursion = new RecursionModel();
$model->recursion->recursion->property = 'recursive recursive property';
$model->recursion->recursion->recursion = new RecursionModel();
$model->recursion->recursion->recursion->property = 'recursive recursive recursive property';

$expected = [
'property'=> 'property',
'recursion' => [
'property' => 'recursive property',
'recursion' => [
'property' => 'recursive recursive property',
],
],
];

$data = $functionName($model);
static::assertSame($expected, $data);
}

public function testEmptyModel(): void
{
$functionName = 'serialize_Tests_Liip_Serializer_Fixtures_Model';
Expand Down

0 comments on commit 2e0314d

Please sign in to comment.