Skip to content

Commit af4ecba

Browse files
committed
Document possibly-null member variables
Many of the variables in AbstractHydrator are not initialized in the constructor, and should be documented as possibly null because of that. Introducing accessors that perform null checks allows to to have to do these null checks when using the accessors. Making the member variables private would be a backwards-compatibility break and could be considered for the next major version. This makes Psalm's and PHPStan's baselines smaller, and should make implementing new hydrators easier.
1 parent 14da92c commit af4ecba

File tree

8 files changed

+89
-110
lines changed

8 files changed

+89
-110
lines changed

lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker;
1818
use Doctrine\ORM\UnitOfWork;
1919
use Generator;
20+
use LogicException;
2021
use ReflectionClass;
2122
use TypeError;
2223

@@ -37,7 +38,7 @@ abstract class AbstractHydrator
3738
/**
3839
* The ResultSetMapping.
3940
*
40-
* @var ResultSetMapping
41+
* @var ResultSetMapping|null
4142
*/
4243
protected $_rsm;
4344

@@ -65,7 +66,7 @@ abstract class AbstractHydrator
6566
/**
6667
* Local ClassMetadata cache to avoid going to the EntityManager all the time.
6768
*
68-
* @var array<string, ClassMetadata>
69+
* @var array<string, ClassMetadata<object>>
6970
*/
7071
protected $_metadataCache = [];
7172

@@ -79,7 +80,7 @@ abstract class AbstractHydrator
7980
/**
8081
* The statement that provides the data to hydrate.
8182
*
82-
* @var Result
83+
* @var Result|null
8384
*/
8485
protected $_stmt;
8586

@@ -88,7 +89,7 @@ abstract class AbstractHydrator
8889
*
8990
* @var array<string, mixed>
9091
*/
91-
protected $_hints;
92+
protected $_hints = [];
9293

9394
/**
9495
* Initializes a new instance of a class derived from <tt>AbstractHydrator</tt>.
@@ -108,7 +109,7 @@ public function __construct(EntityManagerInterface $em)
108109
* @deprecated
109110
*
110111
* @param Result|ResultStatement $stmt
111-
* @param object $resultSetMapping
112+
* @param ResultSetMapping $resultSetMapping
112113
* @psalm-param array<string, mixed> $hints
113114
*
114115
* @return IterableResult
@@ -180,7 +181,7 @@ public function toIterable($stmt, ResultSetMapping $resultSetMapping, array $hin
180181
$this->prepare();
181182

182183
while (true) {
183-
$row = $this->_stmt->fetchAssociative();
184+
$row = $this->statement()->fetchAssociative();
184185

185186
if ($row === false) {
186187
$this->cleanup();
@@ -202,6 +203,24 @@ public function toIterable($stmt, ResultSetMapping $resultSetMapping, array $hin
202203
}
203204
}
204205

206+
final protected function statement(): Result
207+
{
208+
if ($this->_stmt === null) {
209+
throw new LogicException('Uninitialized _stmt property');
210+
}
211+
212+
return $this->_stmt;
213+
}
214+
215+
final protected function resultSetMapping(): ResultSetMapping
216+
{
217+
if ($this->_rsm === null) {
218+
throw new LogicException('Uninitialized _rsm property');
219+
}
220+
221+
return $this->_rsm;
222+
}
223+
205224
/**
206225
* Hydrates all rows returned by the passed statement instance at once.
207226
*
@@ -259,7 +278,7 @@ public function hydrateAll($stmt, $resultSetMapping, array $hints = [])
259278
*/
260279
public function hydrateRow()
261280
{
262-
$row = $this->_stmt->fetchAssociative();
281+
$row = $this->statement()->fetchAssociative();
263282

264283
if ($row === false) {
265284
$this->cleanup();
@@ -304,7 +323,7 @@ protected function prepare()
304323
*/
305324
protected function cleanup()
306325
{
307-
$this->_stmt->free();
326+
$this->statement()->free();
308327

309328
$this->_stmt = null;
310329
$this->_rsm = null;

lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ class ArrayHydrator extends AbstractHydrator
4141
*/
4242
protected function prepare()
4343
{
44-
$this->_isSimpleQuery = count($this->_rsm->aliasMap) <= 1;
44+
$this->_isSimpleQuery = count($this->resultSetMapping()->aliasMap) <= 1;
4545

46-
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
46+
foreach ($this->resultSetMapping()->aliasMap as $dqlAlias => $className) {
4747
$this->_identifierMap[$dqlAlias] = [];
4848
$this->_resultPointers[$dqlAlias] = [];
4949
$this->_idTemplate[$dqlAlias] = '';
@@ -57,7 +57,7 @@ protected function hydrateAllData()
5757
{
5858
$result = [];
5959

60-
while ($data = $this->_stmt->fetchAssociative()) {
60+
while ($data = $this->statement()->fetchAssociative()) {
6161
$this->hydrateRowData($data, $result);
6262
}
6363

@@ -78,10 +78,10 @@ protected function hydrateRowData(array $row, array &$result)
7878
foreach ($rowData['data'] as $dqlAlias => $data) {
7979
$index = false;
8080

81-
if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
81+
if (isset($this->resultSetMapping()->parentAliasMap[$dqlAlias])) {
8282
// It's a joined result
8383

84-
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
84+
$parent = $this->resultSetMapping()->parentAliasMap[$dqlAlias];
8585
$path = $parent . '.' . $dqlAlias;
8686

8787
// missing parent data, skipping as RIGHT JOIN hydration is not supported.
@@ -91,7 +91,7 @@ protected function hydrateRowData(array $row, array &$result)
9191

9292
// Get a reference to the right element in the result tree.
9393
// This element will get the associated element attached.
94-
if ($this->_rsm->isMixed && isset($this->_rootAliases[$parent])) {
94+
if ($this->resultSetMapping()->isMixed && isset($this->_rootAliases[$parent])) {
9595
$first = reset($this->_resultPointers);
9696
// TODO: Exception if $key === null ?
9797
$baseElement =& $this->_resultPointers[$parent][key($first)];
@@ -103,8 +103,8 @@ protected function hydrateRowData(array $row, array &$result)
103103
continue;
104104
}
105105

106-
$relationAlias = $this->_rsm->relationMap[$dqlAlias];
107-
$parentClass = $this->_metadataCache[$this->_rsm->aliasMap[$parent]];
106+
$relationAlias = $this->resultSetMapping()->relationMap[$dqlAlias];
107+
$parentClass = $this->_metadataCache[$this->resultSetMapping()->aliasMap[$parent]];
108108
$relation = $parentClass->associationMappings[$relationAlias];
109109

110110
// Check the type of the relation (many or single-valued)
@@ -123,8 +123,8 @@ protected function hydrateRowData(array $row, array &$result)
123123
if (! $indexExists || ! $indexIsValid) {
124124
$element = $data;
125125

126-
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
127-
$baseElement[$relationAlias][$row[$this->_rsm->indexByMap[$dqlAlias]]] = $element;
126+
if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {
127+
$baseElement[$relationAlias][$row[$this->resultSetMapping()->indexByMap[$dqlAlias]]] = $element;
128128
} else {
129129
$baseElement[$relationAlias][] = $element;
130130
}
@@ -156,11 +156,11 @@ protected function hydrateRowData(array $row, array &$result)
156156
// It's a root result element
157157

158158
$this->_rootAliases[$dqlAlias] = true; // Mark as root
159-
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
159+
$entityKey = $this->resultSetMapping()->entityMappings[$dqlAlias] ?: 0;
160160

161161
// if this row has a NULL value for the root result id then make it a null result.
162162
if (! isset($nonemptyComponents[$dqlAlias])) {
163-
$result[] = $this->_rsm->isMixed
163+
$result[] = $this->resultSetMapping()->isMixed
164164
? [$entityKey => null]
165165
: null;
166166

@@ -172,12 +172,12 @@ protected function hydrateRowData(array $row, array &$result)
172172

173173
// Check for an existing element
174174
if ($this->_isSimpleQuery || ! isset($this->_identifierMap[$dqlAlias][$id[$dqlAlias]])) {
175-
$element = $this->_rsm->isMixed
175+
$element = $this->resultSetMapping()->isMixed
176176
? [$entityKey => $data]
177177
: $data;
178178

179-
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
180-
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
179+
if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {
180+
$resultKey = $row[$this->resultSetMapping()->indexByMap[$dqlAlias]];
181181
$result[$resultKey] = $element;
182182
} else {
183183
$resultKey = $this->_resultCounter;
@@ -204,8 +204,8 @@ protected function hydrateRowData(array $row, array &$result)
204204
if (isset($rowData['scalars'])) {
205205
if (! isset($resultKey)) {
206206
// this only ever happens when no object is fetched (scalar result only)
207-
$resultKey = isset($this->_rsm->indexByMap['scalars'])
208-
? $row[$this->_rsm->indexByMap['scalars']]
207+
$resultKey = isset($this->resultSetMapping()->indexByMap['scalars'])
208+
? $row[$this->resultSetMapping()->indexByMap['scalars']]
209209
: $this->_resultCounter - 1;
210210
}
211211

lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,25 @@ protected function prepare()
5656
$this->_hints[UnitOfWork::HINT_DEFEREAGERLOAD] = true;
5757
}
5858

59-
foreach ($this->_rsm->aliasMap as $dqlAlias => $className) {
59+
foreach ($this->resultSetMapping()->aliasMap as $dqlAlias => $className) {
6060
$this->identifierMap[$dqlAlias] = [];
6161
$this->idTemplate[$dqlAlias] = '';
6262

6363
// Remember which associations are "fetch joined", so that we know where to inject
6464
// collection stubs or proxies and where not.
65-
if (! isset($this->_rsm->relationMap[$dqlAlias])) {
65+
if (! isset($this->resultSetMapping()->relationMap[$dqlAlias])) {
6666
continue;
6767
}
6868

69-
$parent = $this->_rsm->parentAliasMap[$dqlAlias];
69+
$parent = $this->resultSetMapping()->parentAliasMap[$dqlAlias];
7070

71-
if (! isset($this->_rsm->aliasMap[$parent])) {
71+
if (! isset($this->resultSetMapping()->aliasMap[$parent])) {
7272
throw HydrationException::parentObjectOfRelationNotFound($dqlAlias, $parent);
7373
}
7474

75-
$sourceClassName = $this->_rsm->aliasMap[$parent];
75+
$sourceClassName = $this->resultSetMapping()->aliasMap[$parent];
7676
$sourceClass = $this->getClassMetadata($sourceClassName);
77-
$assoc = $sourceClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]];
77+
$assoc = $sourceClass->associationMappings[$this->resultSetMapping()->relationMap[$dqlAlias]];
7878

7979
$this->_hints['fetched'][$parent][$assoc['fieldName']] = true;
8080

@@ -139,7 +139,7 @@ protected function hydrateAllData()
139139
{
140140
$result = [];
141141

142-
while ($row = $this->_stmt->fetchAssociative()) {
142+
while ($row = $this->statement()->fetchAssociative()) {
143143
$this->hydrateRowData($row, $result);
144144
}
145145

@@ -215,16 +215,16 @@ private function initRelatedCollection(
215215
*/
216216
private function getEntity(array $data, string $dqlAlias)
217217
{
218-
$className = $this->_rsm->aliasMap[$dqlAlias];
218+
$className = $this->resultSetMapping()->aliasMap[$dqlAlias];
219219

220-
if (isset($this->_rsm->discriminatorColumns[$dqlAlias])) {
221-
$fieldName = $this->_rsm->discriminatorColumns[$dqlAlias];
220+
if (isset($this->resultSetMapping()->discriminatorColumns[$dqlAlias])) {
221+
$fieldName = $this->resultSetMapping()->discriminatorColumns[$dqlAlias];
222222

223-
if (! isset($this->_rsm->metaMappings[$fieldName])) {
223+
if (! isset($this->resultSetMapping()->metaMappings[$fieldName])) {
224224
throw HydrationException::missingDiscriminatorMetaMappingColumn($className, $fieldName, $dqlAlias);
225225
}
226226

227-
$discrColumn = $this->_rsm->metaMappings[$fieldName];
227+
$discrColumn = $this->resultSetMapping()->metaMappings[$fieldName];
228228

229229
if (! isset($data[$discrColumn])) {
230230
throw HydrationException::missingDiscriminatorColumn($className, $discrColumn, $dqlAlias);
@@ -318,12 +318,12 @@ protected function hydrateRowData(array $row, array &$result)
318318

319319
// Hydrate the data chunks
320320
foreach ($rowData['data'] as $dqlAlias => $data) {
321-
$entityName = $this->_rsm->aliasMap[$dqlAlias];
321+
$entityName = $this->resultSetMapping()->aliasMap[$dqlAlias];
322322

323-
if (isset($this->_rsm->parentAliasMap[$dqlAlias])) {
323+
if (isset($this->resultSetMapping()->parentAliasMap[$dqlAlias])) {
324324
// It's a joined result
325325

326-
$parentAlias = $this->_rsm->parentAliasMap[$dqlAlias];
326+
$parentAlias = $this->resultSetMapping()->parentAliasMap[$dqlAlias];
327327
// we need the $path to save into the identifier map which entities were already
328328
// seen for this parent-child relationship
329329
$path = $parentAlias . '.' . $dqlAlias;
@@ -334,13 +334,13 @@ protected function hydrateRowData(array $row, array &$result)
334334
continue;
335335
}
336336

337-
$parentClass = $this->_metadataCache[$this->_rsm->aliasMap[$parentAlias]];
338-
$relationField = $this->_rsm->relationMap[$dqlAlias];
337+
$parentClass = $this->_metadataCache[$this->resultSetMapping()->aliasMap[$parentAlias]];
338+
$relationField = $this->resultSetMapping()->relationMap[$dqlAlias];
339339
$relation = $parentClass->associationMappings[$relationField];
340340
$reflField = $parentClass->reflFields[$relationField];
341341

342342
// Get a reference to the parent object to which the joined element belongs.
343-
if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) {
343+
if ($this->resultSetMapping()->isMixed && isset($this->rootAliases[$parentAlias])) {
344344
$objectClass = $this->resultPointers[$parentAlias];
345345
$parentObject = $objectClass[key($objectClass)];
346346
} elseif (isset($this->resultPointers[$parentAlias])) {
@@ -389,8 +389,8 @@ protected function hydrateRowData(array $row, array &$result)
389389
} else {
390390
$element = $this->getEntity($data, $dqlAlias);
391391

392-
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
393-
$indexValue = $row[$this->_rsm->indexByMap[$dqlAlias]];
392+
if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {
393+
$indexValue = $row[$this->resultSetMapping()->indexByMap[$dqlAlias]];
394394
$reflFieldValue->hydrateSet($indexValue, $element);
395395
$this->identifierMap[$path][$id[$parentAlias]][$id[$dqlAlias]] = $indexValue;
396396
} else {
@@ -458,11 +458,11 @@ protected function hydrateRowData(array $row, array &$result)
458458
} else {
459459
// PATH C: Its a root result element
460460
$this->rootAliases[$dqlAlias] = true; // Mark as root alias
461-
$entityKey = $this->_rsm->entityMappings[$dqlAlias] ?: 0;
461+
$entityKey = $this->resultSetMapping()->entityMappings[$dqlAlias] ?: 0;
462462

463463
// if this row has a NULL value for the root result id then make it a null result.
464464
if (! isset($nonemptyComponents[$dqlAlias])) {
465-
if ($this->_rsm->isMixed) {
465+
if ($this->resultSetMapping()->isMixed) {
466466
$result[] = [$entityKey => null];
467467
} else {
468468
$result[] = null;
@@ -477,12 +477,12 @@ protected function hydrateRowData(array $row, array &$result)
477477
if (! isset($this->identifierMap[$dqlAlias][$id[$dqlAlias]])) {
478478
$element = $this->getEntity($data, $dqlAlias);
479479

480-
if ($this->_rsm->isMixed) {
480+
if ($this->resultSetMapping()->isMixed) {
481481
$element = [$entityKey => $element];
482482
}
483483

484-
if (isset($this->_rsm->indexByMap[$dqlAlias])) {
485-
$resultKey = $row[$this->_rsm->indexByMap[$dqlAlias]];
484+
if (isset($this->resultSetMapping()->indexByMap[$dqlAlias])) {
485+
$resultKey = $row[$this->resultSetMapping()->indexByMap[$dqlAlias]];
486486

487487
if (isset($this->_hints['collection'])) {
488488
$this->_hints['collection']->hydrateSet($resultKey, $element);
@@ -524,8 +524,8 @@ protected function hydrateRowData(array $row, array &$result)
524524
// Append scalar values to mixed result sets
525525
if (isset($rowData['scalars'])) {
526526
if (! isset($resultKey)) {
527-
$resultKey = isset($this->_rsm->indexByMap['scalars'])
528-
? $row[$this->_rsm->indexByMap['scalars']]
527+
$resultKey = isset($this->resultSetMapping()->indexByMap['scalars'])
528+
? $row[$this->resultSetMapping()->indexByMap['scalars']]
529529
: $this->resultCounter - 1;
530530
}
531531

lib/Doctrine/ORM/Internal/Hydration/ScalarHydrator.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ protected function hydrateAllData()
1818
{
1919
$result = [];
2020

21-
while ($data = $this->_stmt->fetchAssociative()) {
21+
while ($data = $this->statement()->fetchAssociative()) {
2222
$this->hydrateRowData($data, $result);
2323
}
2424

0 commit comments

Comments
 (0)