Skip to content

Commit 9c4c171

Browse files
committed
Skip writing readonly properties if the value did not change
1 parent 20d0298 commit 9c4c171

File tree

4 files changed

+81
-9
lines changed

4 files changed

+81
-9
lines changed

lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php

+23-8
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ class ClassMetadataInfo implements ClassMetadata
705705
/**
706706
* The ReflectionProperty instances of the mapped class.
707707
*
708-
* @var ReflectionProperty[]|null[]
708+
* @var array<string, ReflectionProperty|null>
709709
*/
710710
public $reflFields = [];
711711

@@ -993,7 +993,8 @@ public function wakeupReflection($reflService)
993993

994994
foreach ($this->embeddedClasses as $property => $embeddedClass) {
995995
if (isset($embeddedClass['declaredField'])) {
996-
$childProperty = $reflService->getAccessibleProperty(
996+
$childProperty = $this->getAccessibleProperty(
997+
$reflService,
997998
$this->embeddedClasses[$embeddedClass['declaredField']]['class'],
998999
$embeddedClass['originalField']
9991000
);
@@ -1007,7 +1008,8 @@ public function wakeupReflection($reflService)
10071008
continue;
10081009
}
10091010

1010-
$fieldRefl = $reflService->getAccessibleProperty(
1011+
$fieldRefl = $this->getAccessibleProperty(
1012+
$reflService,
10111013
$embeddedClass['declared'] ?? $this->name,
10121014
$property
10131015
);
@@ -1020,15 +1022,15 @@ public function wakeupReflection($reflService)
10201022
if (isset($mapping['declaredField']) && isset($parentReflFields[$mapping['declaredField']])) {
10211023
$this->reflFields[$field] = new ReflectionEmbeddedProperty(
10221024
$parentReflFields[$mapping['declaredField']],
1023-
$reflService->getAccessibleProperty($mapping['originalClass'], $mapping['originalField']),
1025+
$this->getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField']),
10241026
$mapping['originalClass']
10251027
);
10261028
continue;
10271029
}
10281030

10291031
$this->reflFields[$field] = isset($mapping['declared'])
1030-
? $reflService->getAccessibleProperty($mapping['declared'], $field)
1031-
: $reflService->getAccessibleProperty($this->name, $field);
1032+
? $this->getAccessibleProperty($reflService, $mapping['declared'], $field)
1033+
: $this->getAccessibleProperty($reflService, $this->name, $field);
10321034

10331035
if (isset($mapping['enumType']) && $this->reflFields[$field] !== null) {
10341036
$this->reflFields[$field] = new ReflectionEnumProperty(
@@ -1040,8 +1042,8 @@ public function wakeupReflection($reflService)
10401042

10411043
foreach ($this->associationMappings as $field => $mapping) {
10421044
$this->reflFields[$field] = isset($mapping['declared'])
1043-
? $reflService->getAccessibleProperty($mapping['declared'], $field)
1044-
: $reflService->getAccessibleProperty($this->name, $field);
1045+
? $this->getAccessibleProperty($reflService, $mapping['declared'], $field)
1046+
: $this->getAccessibleProperty($reflService, $this->name, $field);
10451047
}
10461048
}
10471049

@@ -3779,4 +3781,17 @@ private function assertMappingOrderBy(array $mapping): void
37793781
throw new InvalidArgumentException("'orderBy' is expected to be an array, not " . gettype($mapping['orderBy']));
37803782
}
37813783
}
3784+
3785+
/**
3786+
* @psalm-param class-string $class
3787+
*/
3788+
private function getAccessibleProperty(ReflectionService $reflService, string $class, string $field): ?ReflectionProperty
3789+
{
3790+
$reflectionProperty = $reflService->getAccessibleProperty($class, $field);
3791+
if ($reflectionProperty !== null && PHP_VERSION_ID >= 80100 && $reflectionProperty->isReadOnly()) {
3792+
$reflectionProperty = new ReflectionReadonlyProperty($reflectionProperty);
3793+
}
3794+
3795+
return $reflectionProperty;
3796+
}
37823797
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\ORM\Mapping;
6+
7+
use InvalidArgumentException;
8+
use LogicException;
9+
use ReflectionProperty;
10+
11+
use function assert;
12+
use function func_get_args;
13+
use function func_num_args;
14+
use function is_object;
15+
use function sprintf;
16+
17+
/**
18+
* @internal
19+
*/
20+
final class ReflectionReadonlyProperty extends ReflectionProperty
21+
{
22+
public function __construct(
23+
private ReflectionProperty $wrappedProperty
24+
) {
25+
if (! $wrappedProperty->isReadOnly()) {
26+
throw new InvalidArgumentException('Given property is not readonly.');
27+
}
28+
29+
parent::__construct($wrappedProperty->class, $wrappedProperty->name);
30+
}
31+
32+
public function getValue(?object $object = null): mixed
33+
{
34+
return $this->wrappedProperty->getValue(...func_get_args());
35+
}
36+
37+
public function setValue(mixed $objectOrValue, mixed $value = null): void
38+
{
39+
if (func_num_args() < 2 || $objectOrValue === null || ! $this->isInitialized($objectOrValue)) {
40+
$this->wrappedProperty->setValue(...func_get_args());
41+
42+
return;
43+
}
44+
45+
assert(is_object($objectOrValue));
46+
47+
if (parent::getValue($objectOrValue) !== $value) {
48+
throw new LogicException(sprintf('Attempting to change readonly property %s::$%s.', $this->class, $this->name));
49+
}
50+
}
51+
}

psalm-baseline.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@
820820
<code>$parentReflFields[$embeddedClass['declaredField']]</code>
821821
<code>$parentReflFields[$mapping['declaredField']]</code>
822822
<code>$queryMapping['resultClass']</code>
823-
<code>$reflService-&gt;getAccessibleProperty($mapping['originalClass'], $mapping['originalField'])</code>
823+
<code>$this-&gt;getAccessibleProperty($reflService, $mapping['originalClass'], $mapping['originalField'])</code>
824824
</PossiblyNullArgument>
825825
<PossiblyNullPropertyFetch occurrences="2">
826826
<code>$embeddable-&gt;reflClass-&gt;name</code>

psalm.xml

+6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@
7575
<referencedClass name="Doctrine\DBAL\Platforms\PostgreSQLPlatform" />
7676
</errorLevel>
7777
</InvalidClass>
78+
<MethodSignatureMismatch>
79+
<errorLevel type="suppress">
80+
<!-- Psalm is wrong about ReflectionProperty signatures. -->
81+
<file name="lib/Doctrine/ORM/Mapping/ReflectionReadonlyProperty.php"/>
82+
</errorLevel>
83+
</MethodSignatureMismatch>
7884
<MissingDependency>
7985
<errorLevel type="suppress">
8086
<!-- DBAL 3.2 forward compatibility -->

0 commit comments

Comments
 (0)