Skip to content

Commit

Permalink
Bugfix: Enum default values require a use statement for correct seria…
Browse files Browse the repository at this point in the history
…lization.
  • Loading branch information
beberlei committed Feb 1, 2022
1 parent 91d90e9 commit 094a7f0
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 4 deletions.
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 . ';';
},
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
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

0 comments on commit 094a7f0

Please sign in to comment.