Skip to content

Commit 295082d

Browse files
authored
Merge pull request #955 from beberlei/EnumDefaultSupport
Bugfix: Enum default values require a use statement for correct serialization
2 parents 91d90e9 + 094a7f0 commit 295082d

File tree

6 files changed

+91
-4
lines changed

6 files changed

+91
-4
lines changed

.github/workflows/static-analysis.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ jobs:
1414
name: "Static Analysis"
1515
uses: "doctrine/.github/.github/workflows/[email protected]"
1616
with:
17-
php-version: "7.4"
17+
php-version: "8.1"

composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
"doctrine/persistence": "^2.0"
2323
},
2424
"require-dev": {
25-
"phpstan/phpstan": "^1.2.0",
25+
"phpstan/phpstan": "^1.4.1",
2626
"phpstan/phpstan-phpunit": "^1",
2727
"phpunit/phpunit": "^7.5.20 || ^8.5 || ^9.0",
2828
"doctrine/coding-standard": "^9.0",

lib/Doctrine/Common/Proxy/ProxyGenerator.php

+42-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Doctrine\Common\Proxy;
44

5+
use BackedEnum;
56
use Doctrine\Common\Proxy\Exception\InvalidArgumentException;
67
use Doctrine\Common\Proxy\Exception\UnexpectedValueException;
78
use Doctrine\Common\Util\ClassUtils;
@@ -19,6 +20,7 @@
1920
use function array_key_exists;
2021
use function array_map;
2122
use function array_slice;
23+
use function array_unique;
2224
use function assert;
2325
use function call_user_func;
2426
use function chmod;
@@ -27,6 +29,7 @@
2729
use function explode;
2830
use function file;
2931
use function file_put_contents;
32+
use function get_class;
3033
use function implode;
3134
use function in_array;
3235
use function interface_exists;
@@ -53,6 +56,7 @@
5356
use function var_export;
5457

5558
use const DIRECTORY_SEPARATOR;
59+
use const PHP_VERSION_ID;
5660

5761
/**
5862
* This factory is used to generate proxy classes.
@@ -98,7 +102,7 @@ class ProxyGenerator
98102
protected $proxyClassTemplate = '<?php
99103
100104
namespace <namespace>;
101-
105+
<enumUseStatements>
102106
/**
103107
* DO NOT EDIT THIS FILE - IT WAS CREATED BY DOCTRINE\'S PROXY GENERATOR
104108
*/
@@ -381,6 +385,43 @@ private function generateNamespace(ClassMetadata $class)
381385
return strrev($parts[1]);
382386
}
383387

388+
/**
389+
* Enums must have a use statement when used as public property defaults.
390+
*/
391+
public function generateEnumUseStatements(ClassMetadata $class): string
392+
{
393+
if (PHP_VERSION_ID < 80100) {
394+
return "\n";
395+
}
396+
397+
$defaultProperties = $class->getReflectionClass()->getDefaultProperties();
398+
$lazyLoadedPublicProperties = $this->getLazyLoadedPublicPropertiesNames($class);
399+
$enumClasses = [];
400+
401+
foreach ($class->getReflectionClass()->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
402+
$name = $property->getName();
403+
404+
if (! in_array($name, $lazyLoadedPublicProperties, true)) {
405+
continue;
406+
}
407+
408+
if (array_key_exists($name, $defaultProperties) && $defaultProperties[$name] instanceof BackedEnum) {
409+
$enumClassNameParts = explode('\\', get_class($defaultProperties[$name]));
410+
$enumClasses[] = $enumClassNameParts[0];
411+
}
412+
}
413+
414+
return implode(
415+
"\n",
416+
array_map(
417+
static function ($className) {
418+
return 'use ' . $className . ';';
419+
},
420+
array_unique($enumClasses)
421+
)
422+
) . "\n";
423+
}
424+
384425
/**
385426
* Generates the original class name.
386427
*

phpstan.neon.dist

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
phpVersion: 70100
2+
phpVersion: 80100
33
level: 3
44
paths:
55
- lib
@@ -19,6 +19,7 @@ parameters:
1919
- tests/Doctrine/Tests/Common/Proxy/ProxyLogicTypedPropertiesTest.php
2020
- tests/Doctrine/Tests/Common/Proxy/SerializedClass.php
2121
- tests/Doctrine/Tests/Common/Proxy/VariadicTypeHintClass.php
22+
- tests/Doctrine/Tests/Common/Proxy/Php71NullableDefaultedNonOptionalHintClass.php
2223
- tests/Doctrine/Tests/Common/Proxy/generated
2324
ignoreErrors:
2425
- '#Access to an undefined property Doctrine\\Common\\Proxy\\Proxy::\$publicField#'
@@ -49,6 +50,10 @@ parameters:
4950
message: '#^Access to an undefined property Doctrine\\Tests\\Common\\Proxy\\MagicGetByRefClass\:\:\$nonExisting\.$#'
5051
path: 'tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php'
5152

53+
-
54+
message: "#^Class Doctrine\\\\Tests\\\\Common\\\\Proxy\\\\MagicIssetClassWithInteger not found\\.$#"
55+
count: 1
56+
path: tests/Doctrine/Tests/Common/Proxy/ProxyMagicMethodsTest.php
5257
includes:
5358
- vendor/phpstan/phpstan-phpunit/extension.neon
5459
- vendor/phpstan/phpstan-phpunit/rules.neon
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
namespace Doctrine\Tests\Common\Proxy;
4+
5+
enum YesOrNo : int {
6+
case YES = 1;
7+
case NO = 0;
8+
}
9+
10+
class Php81EnumPublicPropertyType
11+
{
12+
public int $id;
13+
public YesOrNo $isEnum = YesOrNo::YES;
14+
}

tests/Doctrine/Tests/Common/Proxy/ProxyGeneratorTest.php

+27
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,33 @@ public function testPhp81NeverType()
525525
);
526526
}
527527

528+
/**
529+
* @requires PHP >= 8.1.0
530+
*/
531+
public function testEnumDefaultInPublicProperty() : void
532+
{
533+
$className = Php81EnumPublicPropertyType::class;
534+
535+
if ( ! class_exists('Doctrine\Tests\Common\ProxyProxy\__CG__\Php81EnumPublicPropertyType', false)) {
536+
$metadata = $this->createClassMetadata($className, ['id']);
537+
538+
$metadata->method('hasField')->will($this->returnValue(true));
539+
540+
$proxyGenerator = new ProxyGenerator(__DIR__ . '/generated', __NAMESPACE__ . 'Proxy');
541+
$this->generateAndRequire($proxyGenerator, $metadata);
542+
}
543+
544+
$this->assertStringContainsString(
545+
'use Doctrine;',
546+
file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyPhp81EnumPublicPropertyType.php')
547+
);
548+
549+
$object = new \Doctrine\Tests\Common\ProxyProxy\__CG__\Doctrine\Tests\Common\Proxy\Php81EnumPublicPropertyType();
550+
$object = unserialize(serialize($object));
551+
552+
$this->assertSame($object->isEnum, \Doctrine\Tests\Common\Proxy\YesOrNo::YES);
553+
}
554+
528555
/**
529556
* @param string $className
530557
* @param mixed[] $ids

0 commit comments

Comments
 (0)