Skip to content

Commit 8b5e565

Browse files
authored
Merge pull request #991 from deguif/fix-nullable-union-type-identifier-lazy-load
Fix nullable union type identifier lazy load
2 parents 1422e30 + c187105 commit 8b5e565

13 files changed

+560
-4
lines changed

.github/workflows/continuous-integration.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,4 @@ jobs:
1717
name: "PHPUnit"
1818
uses: "doctrine/.github/.github/workflows/[email protected]"
1919
with:
20-
php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0", "8.1"]'
20+
php-versions: '["7.1", "7.2", "7.3", "7.4", "8.0", "8.1", "8.2"]'

phpstan.neon.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
parameters:
2-
phpVersion: 80100
2+
phpVersion: 80200
33
level: 3
44
paths:
55
- src

src/Proxy/ProxyGenerator.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,13 @@ class ProxyGenerator
7474
* Used to match very simple id methods that don't need
7575
* to be decorated since the identifier is known.
7676
*/
77-
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';
77+
public const PATTERN_MATCH_ID_METHOD = <<<'EOT'
78+
((?(DEFINE)
79+
(?<type>\\?[a-z_\x7f-\xff][\w\x7f-\xff]*(?:\\[a-z_\x7f-\xff][\w\x7f-\xff]*)*)
80+
(?<intersection_type>(?&type)\s*&\s*(?&type))
81+
(?<union_type>(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type))(?:\s*\|\s*(?:(?:\(\s*(?&intersection_type)\s*\))|(?&type)))+)
82+
)(?:public\s+)?(?:function\s+%s\s*\(\)\s*)\s*(?::\s*(?:(?&union_type)|(?&intersection_type)|(?:\??(?&type)))\s*)?{\s*return\s*\$this->%s;\s*})i
83+
EOT;
7884

7985
/**
8086
* The namespace that contains all proxy classes.
@@ -1218,6 +1224,10 @@ private function formatType(
12181224
if ($type instanceof ReflectionUnionType) {
12191225
return implode('|', array_map(
12201226
function (ReflectionType $unionedType) use ($method, $parameter) {
1227+
if ($unionedType instanceof ReflectionIntersectionType) {
1228+
return '(' . $this->formatType($unionedType, $method, $parameter) . ')';
1229+
}
1230+
12211231
return $this->formatType($unionedType, $method, $parameter);
12221232
},
12231233
$type->getTypes()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Doctrine\Tests\Common\Proxy;
4+
5+
class LazyLoadableObjectWithPHP81IntersectionType
6+
{
7+
private \stdClass&\Stringable $identifierFieldIntersectionType;
8+
9+
public function getIdentifierFieldIntersectionType(): \stdClass&\Stringable
10+
{
11+
return $this->identifierFieldIntersectionType;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
namespace Doctrine\Tests\Common\Proxy;
4+
5+
use BadMethodCallException;
6+
use Doctrine\Persistence\Mapping\ClassMetadata;
7+
use ReflectionClass;
8+
use function array_keys;
9+
10+
class LazyLoadableObjectWithPHP81IntersectionTypeClassMetadata implements ClassMetadata
11+
{
12+
/** @var ReflectionClass */
13+
protected $reflectionClass;
14+
15+
/** @var array<string,bool> */
16+
protected $identifier = [
17+
'identifierFieldIntersectionType' => true,
18+
];
19+
20+
/** @var array<string,bool> */
21+
protected $fields = [
22+
'identifierFieldIntersectionType' => true,
23+
];
24+
25+
/**
26+
* {@inheritDoc}
27+
*/
28+
public function getName()
29+
{
30+
return $this->getReflectionClass()->getName();
31+
}
32+
33+
/**
34+
* {@inheritDoc}
35+
*/
36+
public function getIdentifier()
37+
{
38+
return array_keys($this->identifier);
39+
}
40+
41+
/**
42+
* {@inheritDoc}
43+
*/
44+
public function getReflectionClass()
45+
{
46+
if ($this->reflectionClass === null) {
47+
$this->reflectionClass = new ReflectionClass(__NAMESPACE__ . '\LazyLoadableObjectWithPHP81IntersectionType');
48+
}
49+
50+
return $this->reflectionClass;
51+
}
52+
53+
/**
54+
* {@inheritDoc}
55+
*/
56+
public function isIdentifier($fieldName)
57+
{
58+
return isset($this->identifier[$fieldName]);
59+
}
60+
61+
/**
62+
* {@inheritDoc}
63+
*/
64+
public function hasField($fieldName)
65+
{
66+
return isset($this->fields[$fieldName]);
67+
}
68+
69+
/**
70+
* {@inheritDoc}
71+
*/
72+
public function hasAssociation($fieldName)
73+
{
74+
return false;
75+
}
76+
77+
/**
78+
* {@inheritDoc}
79+
*/
80+
public function isSingleValuedAssociation($fieldName)
81+
{
82+
throw new BadMethodCallException('not implemented');
83+
}
84+
85+
/**
86+
* {@inheritDoc}
87+
*/
88+
public function isCollectionValuedAssociation($fieldName)
89+
{
90+
throw new BadMethodCallException('not implemented');
91+
}
92+
93+
/**
94+
* {@inheritDoc}
95+
*/
96+
public function getFieldNames()
97+
{
98+
return array_keys($this->fields);
99+
}
100+
101+
/**
102+
* {@inheritDoc}
103+
*/
104+
public function getIdentifierFieldNames()
105+
{
106+
return $this->getIdentifier();
107+
}
108+
109+
/**
110+
* {@inheritDoc}
111+
*/
112+
public function getAssociationNames()
113+
{
114+
return [];
115+
}
116+
117+
/**
118+
* {@inheritDoc}
119+
*/
120+
public function getTypeOfField($fieldName)
121+
{
122+
return 'string';
123+
}
124+
125+
/**
126+
* {@inheritDoc}
127+
*/
128+
public function getAssociationTargetClass($assocName)
129+
{
130+
throw new BadMethodCallException('not implemented');
131+
}
132+
133+
/**
134+
* {@inheritDoc}
135+
*/
136+
public function isAssociationInverseSide($assocName)
137+
{
138+
throw new BadMethodCallException('not implemented');
139+
}
140+
141+
/**
142+
* {@inheritDoc}
143+
*/
144+
public function getAssociationMappedByTargetField($assocName)
145+
{
146+
throw new BadMethodCallException('not implemented');
147+
}
148+
149+
/**
150+
* {@inheritDoc}
151+
*/
152+
public function getIdentifierValues($object)
153+
{
154+
throw new BadMethodCallException('not implemented');
155+
}
156+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Doctrine\Tests\Common\Proxy;
4+
5+
class LazyLoadableObjectWithPHP82UnionAndIntersectionType
6+
{
7+
private (\stdClass&\Stringable)|null $identifierFieldUnionAndIntersectionType = null;
8+
9+
public function getIdentifierFieldUnionAndIntersectionType(): (\stdClass&\Stringable)|null
10+
{
11+
return $this->identifierFieldUnionAndIntersectionType;
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
<?php
2+
3+
namespace Doctrine\Tests\Common\Proxy;
4+
5+
use BadMethodCallException;
6+
use Doctrine\Persistence\Mapping\ClassMetadata;
7+
use ReflectionClass;
8+
use function array_keys;
9+
10+
class LazyLoadableObjectWithPHP82UnionAndIntersectionTypeClassMetadata implements ClassMetadata
11+
{
12+
/** @var ReflectionClass */
13+
protected $reflectionClass;
14+
15+
/** @var array<string,bool> */
16+
protected $identifier = [
17+
'identifierFieldUnionAndIntersectionType' => true,
18+
];
19+
20+
/** @var array<string,bool> */
21+
protected $fields = [
22+
'identifierFieldUnionAndIntersectionType' => true,
23+
];
24+
25+
/**
26+
* {@inheritDoc}
27+
*/
28+
public function getName()
29+
{
30+
return $this->getReflectionClass()->getName();
31+
}
32+
33+
/**
34+
* {@inheritDoc}
35+
*/
36+
public function getIdentifier()
37+
{
38+
return array_keys($this->identifier);
39+
}
40+
41+
/**
42+
* {@inheritDoc}
43+
*/
44+
public function getReflectionClass()
45+
{
46+
if ($this->reflectionClass === null) {
47+
$this->reflectionClass = new ReflectionClass(__NAMESPACE__ . '\LazyLoadableObjectWithPHP82UnionAndIntersectionType');
48+
}
49+
50+
return $this->reflectionClass;
51+
}
52+
53+
/**
54+
* {@inheritDoc}
55+
*/
56+
public function isIdentifier($fieldName)
57+
{
58+
return isset($this->identifier[$fieldName]);
59+
}
60+
61+
/**
62+
* {@inheritDoc}
63+
*/
64+
public function hasField($fieldName)
65+
{
66+
return isset($this->fields[$fieldName]);
67+
}
68+
69+
/**
70+
* {@inheritDoc}
71+
*/
72+
public function hasAssociation($fieldName)
73+
{
74+
return false;
75+
}
76+
77+
/**
78+
* {@inheritDoc}
79+
*/
80+
public function isSingleValuedAssociation($fieldName)
81+
{
82+
throw new BadMethodCallException('not implemented');
83+
}
84+
85+
/**
86+
* {@inheritDoc}
87+
*/
88+
public function isCollectionValuedAssociation($fieldName)
89+
{
90+
throw new BadMethodCallException('not implemented');
91+
}
92+
93+
/**
94+
* {@inheritDoc}
95+
*/
96+
public function getFieldNames()
97+
{
98+
return array_keys($this->fields);
99+
}
100+
101+
/**
102+
* {@inheritDoc}
103+
*/
104+
public function getIdentifierFieldNames()
105+
{
106+
return $this->getIdentifier();
107+
}
108+
109+
/**
110+
* {@inheritDoc}
111+
*/
112+
public function getAssociationNames()
113+
{
114+
return [];
115+
}
116+
117+
/**
118+
* {@inheritDoc}
119+
*/
120+
public function getTypeOfField($fieldName)
121+
{
122+
return 'string';
123+
}
124+
125+
/**
126+
* {@inheritDoc}
127+
*/
128+
public function getAssociationTargetClass($assocName)
129+
{
130+
throw new BadMethodCallException('not implemented');
131+
}
132+
133+
/**
134+
* {@inheritDoc}
135+
*/
136+
public function isAssociationInverseSide($assocName)
137+
{
138+
throw new BadMethodCallException('not implemented');
139+
}
140+
141+
/**
142+
* {@inheritDoc}
143+
*/
144+
public function getAssociationMappedByTargetField($assocName)
145+
{
146+
throw new BadMethodCallException('not implemented');
147+
}
148+
149+
/**
150+
* {@inheritDoc}
151+
*/
152+
public function getIdentifierValues($object)
153+
{
154+
throw new BadMethodCallException('not implemented');
155+
}
156+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace Doctrine\Tests\Common\Proxy;
4+
5+
class LazyLoadableObjectWithPHP8UnionType
6+
{
7+
private int|string|null $identifierFieldUnionType = null;
8+
9+
public function getIdentifierFieldUnionType(): int|string|null
10+
{
11+
return $this->identifierFieldUnionType;
12+
}
13+
}

0 commit comments

Comments
 (0)