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

Bugfix: Enum default values require a use statement for correct serialization #955

Merged
merged 1 commit into from Feb 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/static-analysis.yml
Expand Up @@ -14,4 +14,4 @@ jobs:
name: "Static Analysis"
uses: "doctrine/.github/.github/workflows/static-analysis.yml@1.4.0"
with:
php-version: "7.4"
php-version: "8.1"
2 changes: 1 addition & 1 deletion composer.json
Expand Up @@ -22,7 +22,7 @@
"doctrine/persistence": "^2.0"
},
"require-dev": {
"phpstan/phpstan": "^1.2.0",
"phpstan/phpstan": "^1.4.1",
"phpstan/phpstan-phpunit": "^1",
"phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0",
"doctrine/coding-standard": "^9.0",
Expand Down
43 changes: 42 additions & 1 deletion lib/Doctrine/Common/Proxy/ProxyGenerator.php
Expand Up @@ -2,6 +2,7 @@

namespace Doctrine\Common\Proxy;

use BackedEnum;
use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
use Doctrine\Common\Proxy\Exception\UnexpectedValueException;
use Doctrine\Common\Util\ClassUtils;
Expand All @@ -19,6 +20,7 @@
use function array_key_exists;
use function array_map;
use function array_slice;
use function array_unique;
use function assert;
use function call_user_func;
use function chmod;
Expand All @@ -27,6 +29,7 @@
use function explode;
use function file;
use function file_put_contents;
use function get_class;
use function implode;
use function in_array;
use function interface_exists;
Expand All @@ -53,6 +56,7 @@
use function var_export;

use const DIRECTORY_SEPARATOR;
use const PHP_VERSION_ID;

/**
* This factory is used to generate proxy classes.
Expand Down Expand Up @@ -98,7 +102,7 @@ class ProxyGenerator
protected $proxyClassTemplate = '<?php

namespace <namespace>;

<enumUseStatements>
/**
* DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR
*/
Expand Down Expand Up @@ -381,6 +385,43 @@ private function generateNamespace(ClassMetadata $class)
return strrev($parts[1]);
}

/**
* Enums must have a use statement when used as public property defaults.
*/
public function generateEnumUseStatements(ClassMetadata $class): string
{
if (PHP_VERSION_ID < 80100) {
return "\n";
}

$defaultProperties = $class->getReflectionClass()->getDefaultProperties();
$lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
$enumClasses = [];

foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
$name = $property->getName();

if (! in_array($name, $lazyLoadedPublicProperties, true)) {
continue;
}

if (array_key_exists($name, $defaultProperties) && $defaultProperties[$name] instanceof BackedEnum) {
$enumClassNameParts = explode('\\', get_class($defaultProperties[$name]));
$enumClasses[] = $enumClassNameParts[0];
}
}

return implode(
"\n",
array_map(
static function ($className) {
return 'use ' . $className . ';';
beberlei marked this conversation as resolved.
Show resolved Hide resolved
},
array_unique($enumClasses)
)
) . "\n";
}

/**
* Generates the original class name.
*
Expand Down
7 changes: 6 additions & 1 deletion phpstan.neon.dist
@@ -1,5 +1,5 @@
parameters:
phpVersion: 70100
beberlei marked this conversation as resolved.
Show resolved Hide resolved
phpVersion: 80100
level: 3
paths:
- lib
Expand All @@ -19,6 +19,7 @@ parameters:
- tests/Doctrine/Tests/Common/Proxy/ProxyLogicTypedPropertiesTest.php
- tests/Doctrine/Tests/Common/Proxy/SerializedClass.php
- tests/Doctrine/Tests/Common/Proxy/VariadicTypeHintClass.php
- tests/Doctrine/Tests/Common/Proxy/Php71NullableDefaultedNonOptionalHintClass.php
- tests/Doctrine/Tests/Common/Proxy/generated
ignoreErrors:
- '#Access to an undefined property Doctrine\\Common\\Proxy\\Proxy::\$publicField#'
Expand Down Expand Up @@ -49,6 +50,10 @@ parameters:
message: '#^Access to an undefined property Doctrine\\Tests\\Common\\Proxy\\MagicGetByRefClass\:\:\$nonExisting\.$#'
path: 'tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php'

-
message: "#^Class Doctrine\\\\Tests\\\\Common\\\\Proxy\\\\MagicIssetClassWithInteger not found\\.$#"
count: 1
path: tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
14 changes: 14 additions & 0 deletions tests/Doctrine/Tests/Common/Proxy/Php81EnumPublicPropertyType.php
@@ -0,0 +1,14 @@
<?php

namespace Doctrine\Tests\Common\Proxy;

enum YesOrNo : int {
case YES = 1;
case NO = 0;
}

class Php81EnumPublicPropertyType
{
public int $id;
public YesOrNo $isEnum = YesOrNo::YES;
}
27 changes: 27 additions & 0 deletions tests/Doctrine/Tests/Common/Proxy/ProxyGeneratorTest.php
Expand Up @@ -525,6 +525,33 @@ public function testPhp81NeverType()
);
}

/**
* @requires PHP >= 8.1.0
*/
public function testEnumDefaultInPublicProperty() : void
{
$className = Php81EnumPublicPropertyType::class;

if ( ! class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\Php81EnumPublicPropertyType', false)) {
$metadata = $this->createClassMetadata($className, ['id']);

$metadata->method('hasField')->will($this->returnValue(true));

$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy');
$this->generateAndRequire($proxyGenerator, $metadata);
}

$this->assertStringContainsString(
'use Doctrine;',
file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyPhp81EnumPublicPropertyType.php')
);

$object = new \Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\Php81EnumPublicPropertyType();
$object = unserialize(serialize($object));

$this->assertSame($object->isEnum, \Doctrine\Tests\Common\Proxy\YesOrNo::YES);
}

/**
* @param string $className
* @param mixed[] $ids
Expand Down