Skip to content

Commit

Permalink
Merge pull request #991 from deguif/fix-nullable-union-type-identifie…
Browse files Browse the repository at this point in the history
…r-lazy-load

Fix nullable union type identifier lazy load
  • Loading branch information
malarzm committed Oct 9, 2022
2 parents 1422e30 + c187105 commit 8b5e565
Show file tree
Hide file tree
Showing 13 changed files with 560 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@ jobs:
name: "PHPUnit"
uses: "doctrine/.github/.github/workflows/continuous-integration.yml@1.1.1"
with:
php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0", "8.1"]'
php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2"]'
2 changes: 1 addition & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
phpVersion: 80100
phpVersion: 80200
level: 3
paths:
- src
Expand Down
12 changes: 11 additions & 1 deletion src/Proxy/ProxyGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,13 @@ class ProxyGenerator
* Used to match very simple id methods that don't need
* to be decorated since the identifier is known.
*/
public const PATTERN_MATCH_ID_METHOD = '((public\s+)?(function\s+%s\s*\(\)\s*)\s*(?::\s*\??\s*\\\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*\s*)?{\s*return\s*\$this->%s;\s*})i';
public const PATTERN_MATCH_ID_METHOD = <<<'EOT'
((?(DEFINE)
(?<type>\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*)
(?<intersection_type>(?&type)\s*&\s*(?&type))
(?<union_type>(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type))(?:\s*\|\s*(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type)))+)
)(?:public\s+)?(?:function\s+%s\s*\(\)\s*)\s*(?::\s*(?:(?&union_type)|(?&intersection_type)|(?:\??(?&type)))\s*)?{\s*return\s*\$this->%s;\s*})i
EOT;

/**
* The namespace that contains all proxy classes.
Expand Down Expand Up @@ -1218,6 +1224,10 @@ private function formatType(
if ($type instanceof ReflectionUnionType) {
return implode('|', array_map(
function (ReflectionType $unionedType) use ($method, $parameter) {
if ($unionedType instanceof ReflectionIntersectionType) {
return '(' . $this->formatType($unionedType, $method, $parameter) . ')';
}

return $this->formatType($unionedType, $method, $parameter);
},
$type->getTypes()
Expand Down
13 changes: 13 additions & 0 deletions tests/Common/Proxy/LazyLoadableObjectWithPHP81IntersectionType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Doctrine\Tests\Common\Proxy;

class LazyLoadableObjectWithPHP81IntersectionType
{
private \stdClass&\Stringable $identifierFieldIntersectionType;

public function getIdentifierFieldIntersectionType(): \stdClass&\Stringable
{
return $this->identifierFieldIntersectionType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

namespace Doctrine\Tests\Common\Proxy;

use BadMethodCallException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use ReflectionClass;
use function array_keys;

class LazyLoadableObjectWithPHP81IntersectionTypeClassMetadata implements ClassMetadata
{
/** @var ReflectionClass */
protected $reflectionClass;

/** @var array<string,bool> */
protected $identifier = [
'identifierFieldIntersectionType' => true,
];

/** @var array<string,bool> */
protected $fields = [
'identifierFieldIntersectionType' => true,
];

/**
* {@inheritDoc}
*/
public function getName()
{
return $this->getReflectionClass()->getName();
}

/**
* {@inheritDoc}
*/
public function getIdentifier()
{
return array_keys($this->identifier);
}

/**
* {@inheritDoc}
*/
public function getReflectionClass()
{
if ($this->reflectionClass === null) {
$this->reflectionClass = new ReflectionClass(__NAMESPACE__ . '\LazyLoadableObjectWithPHP81IntersectionType');
}

return $this->reflectionClass;
}

/**
* {@inheritDoc}
*/
public function isIdentifier($fieldName)
{
return isset($this->identifier[$fieldName]);
}

/**
* {@inheritDoc}
*/
public function hasField($fieldName)
{
return isset($this->fields[$fieldName]);
}

/**
* {@inheritDoc}
*/
public function hasAssociation($fieldName)
{
return false;
}

/**
* {@inheritDoc}
*/
public function isSingleValuedAssociation($fieldName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function isCollectionValuedAssociation($fieldName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function getFieldNames()
{
return array_keys($this->fields);
}

/**
* {@inheritDoc}
*/
public function getIdentifierFieldNames()
{
return $this->getIdentifier();
}

/**
* {@inheritDoc}
*/
public function getAssociationNames()
{
return [];
}

/**
* {@inheritDoc}
*/
public function getTypeOfField($fieldName)
{
return 'string';
}

/**
* {@inheritDoc}
*/
public function getAssociationTargetClass($assocName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function isAssociationInverseSide($assocName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function getAssociationMappedByTargetField($assocName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function getIdentifierValues($object)
{
throw new BadMethodCallException('not implemented');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Doctrine\Tests\Common\Proxy;

class LazyLoadableObjectWithPHP82UnionAndIntersectionType
{
private (\stdClass&\Stringable)|null $identifierFieldUnionAndIntersectionType = null;

public function getIdentifierFieldUnionAndIntersectionType(): (\stdClass&\Stringable)|null
{
return $this->identifierFieldUnionAndIntersectionType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
<?php

namespace Doctrine\Tests\Common\Proxy;

use BadMethodCallException;
use Doctrine\Persistence\Mapping\ClassMetadata;
use ReflectionClass;
use function array_keys;

class LazyLoadableObjectWithPHP82UnionAndIntersectionTypeClassMetadata implements ClassMetadata
{
/** @var ReflectionClass */
protected $reflectionClass;

/** @var array<string,bool> */
protected $identifier = [
'identifierFieldUnionAndIntersectionType' => true,
];

/** @var array<string,bool> */
protected $fields = [
'identifierFieldUnionAndIntersectionType' => true,
];

/**
* {@inheritDoc}
*/
public function getName()
{
return $this->getReflectionClass()->getName();
}

/**
* {@inheritDoc}
*/
public function getIdentifier()
{
return array_keys($this->identifier);
}

/**
* {@inheritDoc}
*/
public function getReflectionClass()
{
if ($this->reflectionClass === null) {
$this->reflectionClass = new ReflectionClass(__NAMESPACE__ . '\LazyLoadableObjectWithPHP82UnionAndIntersectionType');
}

return $this->reflectionClass;
}

/**
* {@inheritDoc}
*/
public function isIdentifier($fieldName)
{
return isset($this->identifier[$fieldName]);
}

/**
* {@inheritDoc}
*/
public function hasField($fieldName)
{
return isset($this->fields[$fieldName]);
}

/**
* {@inheritDoc}
*/
public function hasAssociation($fieldName)
{
return false;
}

/**
* {@inheritDoc}
*/
public function isSingleValuedAssociation($fieldName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function isCollectionValuedAssociation($fieldName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function getFieldNames()
{
return array_keys($this->fields);
}

/**
* {@inheritDoc}
*/
public function getIdentifierFieldNames()
{
return $this->getIdentifier();
}

/**
* {@inheritDoc}
*/
public function getAssociationNames()
{
return [];
}

/**
* {@inheritDoc}
*/
public function getTypeOfField($fieldName)
{
return 'string';
}

/**
* {@inheritDoc}
*/
public function getAssociationTargetClass($assocName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function isAssociationInverseSide($assocName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function getAssociationMappedByTargetField($assocName)
{
throw new BadMethodCallException('not implemented');
}

/**
* {@inheritDoc}
*/
public function getIdentifierValues($object)
{
throw new BadMethodCallException('not implemented');
}
}
13 changes: 13 additions & 0 deletions tests/Common/Proxy/LazyLoadableObjectWithPHP8UnionType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Doctrine\Tests\Common\Proxy;

class LazyLoadableObjectWithPHP8UnionType
{
private int|string|null $identifierFieldUnionType = null;

public function getIdentifierFieldUnionType(): int|string|null
{
return $this->identifierFieldUnionType;
}
}

0 comments on commit 8b5e565

Please sign in to comment.