These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Doctrine\ODM\MongoDB\Hydrator; |
||
6 | |||
7 | use Doctrine\Common\EventManager; |
||
8 | use Doctrine\ODM\MongoDB\Configuration; |
||
9 | use Doctrine\ODM\MongoDB\DocumentManager; |
||
10 | use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; |
||
11 | use Doctrine\ODM\MongoDB\Event\PreLoadEventArgs; |
||
12 | use Doctrine\ODM\MongoDB\Events; |
||
13 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
||
14 | use Doctrine\ODM\MongoDB\Types\Type; |
||
15 | use Doctrine\ODM\MongoDB\UnitOfWork; |
||
16 | use ProxyManager\Proxy\GhostObjectInterface; |
||
17 | use const DIRECTORY_SEPARATOR; |
||
18 | use function array_key_exists; |
||
19 | use function chmod; |
||
20 | use function class_exists; |
||
21 | use function dirname; |
||
22 | use function file_exists; |
||
23 | use function file_put_contents; |
||
24 | use function get_class; |
||
25 | use function is_dir; |
||
26 | use function is_writable; |
||
27 | use function mkdir; |
||
28 | use function rename; |
||
29 | use function rtrim; |
||
30 | use function sprintf; |
||
31 | use function str_replace; |
||
32 | use function substr; |
||
33 | use function uniqid; |
||
34 | |||
35 | /** |
||
36 | * The HydratorFactory class is responsible for instantiating a correct hydrator |
||
37 | * type based on document's ClassMetadata |
||
38 | */ |
||
39 | class HydratorFactory |
||
40 | { |
||
41 | /** |
||
42 | * The DocumentManager this factory is bound to. |
||
43 | * |
||
44 | * @var DocumentManager |
||
45 | */ |
||
46 | private $dm; |
||
47 | |||
48 | /** |
||
49 | * The UnitOfWork used to coordinate object-level transactions. |
||
50 | * |
||
51 | * @var UnitOfWork |
||
52 | */ |
||
53 | private $unitOfWork; |
||
54 | |||
55 | /** |
||
56 | * The EventManager associated with this Hydrator |
||
57 | * |
||
58 | * @var EventManager |
||
59 | */ |
||
60 | private $evm; |
||
61 | |||
62 | /** |
||
63 | * Which algorithm to use to automatically (re)generate hydrator classes. |
||
64 | * |
||
65 | * @var int |
||
66 | */ |
||
67 | private $autoGenerate; |
||
68 | |||
69 | /** |
||
70 | * The namespace that contains all hydrator classes. |
||
71 | * |
||
72 | * @var string|null |
||
73 | */ |
||
74 | private $hydratorNamespace; |
||
75 | |||
76 | /** |
||
77 | * The directory that contains all hydrator classes. |
||
78 | * |
||
79 | * @var string |
||
80 | */ |
||
81 | private $hydratorDir; |
||
82 | |||
83 | /** |
||
84 | * Array of instantiated document hydrators. |
||
85 | * |
||
86 | * @var array |
||
87 | */ |
||
88 | private $hydrators = []; |
||
89 | |||
90 | /** |
||
91 | * @throws HydratorException |
||
92 | */ |
||
93 | 1651 | public function __construct(DocumentManager $dm, EventManager $evm, ?string $hydratorDir, ?string $hydratorNs, int $autoGenerate) |
|
94 | { |
||
95 | 1651 | if (! $hydratorDir) { |
|
96 | throw HydratorException::hydratorDirectoryRequired(); |
||
97 | } |
||
98 | 1651 | if (! $hydratorNs) { |
|
99 | throw HydratorException::hydratorNamespaceRequired(); |
||
100 | } |
||
101 | 1651 | $this->dm = $dm; |
|
102 | 1651 | $this->evm = $evm; |
|
103 | 1651 | $this->hydratorDir = $hydratorDir; |
|
104 | 1651 | $this->hydratorNamespace = $hydratorNs; |
|
105 | 1651 | $this->autoGenerate = $autoGenerate; |
|
106 | 1651 | } |
|
107 | |||
108 | /** |
||
109 | * Sets the UnitOfWork instance. |
||
110 | */ |
||
111 | 1651 | public function setUnitOfWork(UnitOfWork $uow) : void |
|
112 | { |
||
113 | 1651 | $this->unitOfWork = $uow; |
|
114 | 1651 | } |
|
115 | |||
116 | /** |
||
117 | * Gets the hydrator object for the given document class. |
||
118 | */ |
||
119 | 385 | public function getHydratorFor(string $className) : HydratorInterface |
|
120 | { |
||
121 | 385 | if (isset($this->hydrators[$className])) { |
|
122 | 213 | return $this->hydrators[$className]; |
|
123 | } |
||
124 | 385 | $hydratorClassName = str_replace('\\', '', $className) . 'Hydrator'; |
|
125 | 385 | $fqn = $this->hydratorNamespace . '\\' . $hydratorClassName; |
|
126 | 385 | $class = $this->dm->getClassMetadata($className); |
|
127 | |||
128 | 385 | if (! class_exists($fqn, false)) { |
|
129 | 177 | $fileName = $this->hydratorDir . DIRECTORY_SEPARATOR . $hydratorClassName . '.php'; |
|
130 | 177 | switch ($this->autoGenerate) { |
|
131 | case Configuration::AUTOGENERATE_NEVER: |
||
132 | require $fileName; |
||
133 | break; |
||
134 | |||
135 | case Configuration::AUTOGENERATE_ALWAYS: |
||
136 | 177 | $this->generateHydratorClass($class, $hydratorClassName, $fileName); |
|
0 ignored issues
–
show
|
|||
137 | 177 | require $fileName; |
|
138 | 177 | break; |
|
139 | |||
140 | case Configuration::AUTOGENERATE_FILE_NOT_EXISTS: |
||
141 | if (! file_exists($fileName)) { |
||
142 | $this->generateHydratorClass($class, $hydratorClassName, $fileName); |
||
0 ignored issues
–
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.
Loading history...
|
|||
143 | } |
||
144 | require $fileName; |
||
145 | break; |
||
146 | |||
147 | case Configuration::AUTOGENERATE_EVAL: |
||
148 | $this->generateHydratorClass($class, $hydratorClassName, null); |
||
0 ignored issues
–
show
$class of type object<Doctrine\Common\P...\Mapping\ClassMetadata> is not a sub-type of object<Doctrine\ODM\Mong...\Mapping\ClassMetadata> . It seems like you assume a concrete implementation of the interface Doctrine\Common\Persistence\Mapping\ClassMetadata to be always present.
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass. Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.
Loading history...
|
|||
149 | break; |
||
150 | } |
||
151 | } |
||
152 | 385 | $this->hydrators[$className] = new $fqn($this->dm, $this->unitOfWork, $class); |
|
153 | 385 | return $this->hydrators[$className]; |
|
154 | } |
||
155 | |||
156 | /** |
||
157 | * Generates hydrator classes for all given classes. |
||
158 | * |
||
159 | * @param array $classes The classes (ClassMetadata instances) for which to generate hydrators. |
||
160 | * @param string $toDir The target directory of the hydrator classes. If not specified, the |
||
161 | * directory configured on the Configuration of the DocumentManager used |
||
162 | * by this factory is used. |
||
163 | */ |
||
164 | public function generateHydratorClasses(array $classes, ?string $toDir = null) : void |
||
165 | { |
||
166 | $hydratorDir = $toDir ?: $this->hydratorDir; |
||
167 | $hydratorDir = rtrim($hydratorDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
||
168 | foreach ($classes as $class) { |
||
169 | $hydratorClassName = str_replace('\\', '', $class->name) . 'Hydrator'; |
||
170 | $hydratorFileName = $hydratorDir . $hydratorClassName . '.php'; |
||
171 | $this->generateHydratorClass($class, $hydratorClassName, $hydratorFileName); |
||
172 | } |
||
173 | } |
||
174 | |||
175 | 177 | private function generateHydratorClass(ClassMetadata $class, string $hydratorClassName, ?string $fileName) : void |
|
176 | { |
||
177 | 177 | $code = ''; |
|
178 | |||
179 | 177 | foreach ($class->fieldMappings as $fieldName => $mapping) { |
|
180 | 177 | if (isset($mapping['alsoLoadFields'])) { |
|
181 | 5 | foreach ($mapping['alsoLoadFields'] as $name) { |
|
182 | 5 | $code .= sprintf( |
|
183 | <<<EOF |
||
184 | |||
185 | 5 | /** @AlsoLoad("$name") */ |
|
186 | 5 | if (!array_key_exists('%1\$s', \$data) && array_key_exists('$name', \$data)) { |
|
187 | 5 | \$data['%1\$s'] = \$data['$name']; |
|
188 | } |
||
189 | |||
190 | EOF |
||
191 | , |
||
192 | 5 | $mapping['name'] |
|
193 | ); |
||
194 | } |
||
195 | } |
||
196 | |||
197 | 177 | if ($mapping['type'] === 'date') { |
|
198 | 9 | $code .= sprintf( |
|
199 | <<<EOF |
||
200 | |||
201 | /** @Field(type="date") */ |
||
202 | if (isset(\$data['%1\$s'])) { |
||
203 | \$value = \$data['%1\$s']; |
||
204 | %3\$s |
||
205 | \$this->class->reflFields['%2\$s']->setValue(\$document, clone \$return); |
||
206 | \$hydratedData['%2\$s'] = \$return; |
||
207 | } |
||
208 | |||
209 | EOF |
||
210 | , |
||
211 | 9 | $mapping['name'], |
|
212 | 9 | $mapping['fieldName'], |
|
213 | 9 | Type::getType($mapping['type'])->closureToPHP() |
|
214 | ); |
||
215 | 177 | } elseif (! isset($mapping['association'])) { |
|
216 | 176 | $code .= sprintf( |
|
217 | <<<EOF |
||
218 | |||
219 | 176 | /** @Field(type="{$mapping['type']}") */ |
|
220 | if (isset(\$data['%1\$s']) || (! empty(\$this->class->fieldMappings['%2\$s']['nullable']) && array_key_exists('%1\$s', \$data))) { |
||
221 | \$value = \$data['%1\$s']; |
||
222 | if (\$value !== null) { |
||
223 | \$typeIdentifier = \$this->class->fieldMappings['%2\$s']['type']; |
||
224 | %3\$s |
||
225 | } else { |
||
226 | \$return = null; |
||
227 | } |
||
228 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
229 | \$hydratedData['%2\$s'] = \$return; |
||
230 | } |
||
231 | |||
232 | EOF |
||
233 | , |
||
234 | 176 | $mapping['name'], |
|
235 | 176 | $mapping['fieldName'], |
|
236 | 176 | Type::getType($mapping['type'])->closureToPHP() |
|
237 | ); |
||
238 | 118 | } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { |
|
239 | 50 | $code .= sprintf( |
|
240 | <<<EOF |
||
241 | |||
242 | /** @ReferenceOne */ |
||
243 | if (isset(\$data['%1\$s'])) { |
||
244 | \$reference = \$data['%1\$s']; |
||
245 | \$className = \$this->unitOfWork->getClassNameForAssociation(\$this->class->fieldMappings['%2\$s'], \$reference); |
||
246 | \$identifier = ClassMetadata::getReferenceId(\$reference, \$this->class->fieldMappings['%2\$s']['storeAs']); |
||
247 | \$targetMetadata = \$this->dm->getClassMetadata(\$className); |
||
248 | \$id = \$targetMetadata->getPHPIdentifierValue(\$identifier); |
||
249 | \$return = \$this->dm->getReference(\$className, \$id); |
||
250 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
251 | \$hydratedData['%2\$s'] = \$return; |
||
252 | } |
||
253 | |||
254 | EOF |
||
255 | , |
||
256 | 50 | $mapping['name'], |
|
257 | 50 | $mapping['fieldName'] |
|
258 | ); |
||
259 | 100 | } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isInverseSide']) { |
|
260 | 6 | if (isset($mapping['repositoryMethod']) && $mapping['repositoryMethod']) { |
|
261 | 1 | $code .= sprintf( |
|
262 | <<<EOF |
||
263 | |||
264 | \$className = \$this->class->fieldMappings['%2\$s']['targetDocument']; |
||
265 | \$return = \$this->dm->getRepository(\$className)->%3\$s(\$document); |
||
266 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
267 | \$hydratedData['%2\$s'] = \$return; |
||
268 | |||
269 | EOF |
||
270 | , |
||
271 | 1 | $mapping['name'], |
|
272 | 1 | $mapping['fieldName'], |
|
273 | 1 | $mapping['repositoryMethod'] |
|
274 | ); |
||
275 | } else { |
||
276 | 6 | $code .= sprintf( |
|
277 | <<<EOF |
||
278 | |||
279 | \$mapping = \$this->class->fieldMappings['%2\$s']; |
||
280 | \$className = \$mapping['targetDocument']; |
||
281 | \$targetClass = \$this->dm->getClassMetadata(\$mapping['targetDocument']); |
||
282 | \$mappedByMapping = \$targetClass->fieldMappings[\$mapping['mappedBy']]; |
||
283 | \$mappedByFieldName = ClassMetadata::getReferenceFieldName(\$mappedByMapping['storeAs'], \$mapping['mappedBy']); |
||
284 | \$criteria = array_merge( |
||
285 | array(\$mappedByFieldName => \$data['_id']), |
||
286 | isset(\$this->class->fieldMappings['%2\$s']['criteria']) ? \$this->class->fieldMappings['%2\$s']['criteria'] : array() |
||
287 | ); |
||
288 | \$sort = isset(\$this->class->fieldMappings['%2\$s']['sort']) ? \$this->class->fieldMappings['%2\$s']['sort'] : array(); |
||
289 | \$return = \$this->unitOfWork->getDocumentPersister(\$className)->load(\$criteria, null, array(), 0, \$sort); |
||
290 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
291 | \$hydratedData['%2\$s'] = \$return; |
||
292 | |||
293 | EOF |
||
294 | , |
||
295 | 6 | $mapping['name'], |
|
296 | 6 | $mapping['fieldName'] |
|
297 | ); |
||
298 | } |
||
299 | 99 | } elseif ($mapping['association'] === ClassMetadata::REFERENCE_MANY || $mapping['association'] === ClassMetadata::EMBED_MANY) { |
|
300 | 79 | $code .= sprintf( |
|
301 | <<<EOF |
||
302 | |||
303 | /** @Many */ |
||
304 | \$mongoData = isset(\$data['%1\$s']) ? \$data['%1\$s'] : null; |
||
305 | \$return = \$this->dm->getConfiguration()->getPersistentCollectionFactory()->create(\$this->dm, \$this->class->fieldMappings['%2\$s']); |
||
306 | \$return->setHints(\$hints); |
||
307 | \$return->setOwner(\$document, \$this->class->fieldMappings['%2\$s']); |
||
308 | \$return->setInitialized(false); |
||
309 | if (\$mongoData) { |
||
310 | \$return->setMongoData(\$mongoData); |
||
311 | } |
||
312 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
313 | \$hydratedData['%2\$s'] = \$return; |
||
314 | |||
315 | EOF |
||
316 | , |
||
317 | 79 | $mapping['name'], |
|
318 | 79 | $mapping['fieldName'] |
|
319 | ); |
||
320 | 44 | } elseif ($mapping['association'] === ClassMetadata::EMBED_ONE) { |
|
321 | 44 | $code .= sprintf( |
|
322 | <<<EOF |
||
323 | |||
324 | /** @EmbedOne */ |
||
325 | if (isset(\$data['%1\$s'])) { |
||
326 | \$embeddedDocument = \$data['%1\$s']; |
||
327 | \$className = \$this->unitOfWork->getClassNameForAssociation(\$this->class->fieldMappings['%2\$s'], \$embeddedDocument); |
||
328 | \$embeddedMetadata = \$this->dm->getClassMetadata(\$className); |
||
329 | \$return = \$embeddedMetadata->newInstance(); |
||
330 | |||
331 | \$this->unitOfWork->setParentAssociation(\$return, \$this->class->fieldMappings['%2\$s'], \$document, '%1\$s'); |
||
332 | |||
333 | \$embeddedData = \$this->dm->getHydratorFactory()->hydrate(\$return, \$embeddedDocument, \$hints); |
||
334 | \$embeddedId = \$embeddedMetadata->identifier && isset(\$embeddedData[\$embeddedMetadata->identifier]) ? \$embeddedData[\$embeddedMetadata->identifier] : null; |
||
335 | |||
336 | if (empty(\$hints[Query::HINT_READ_ONLY])) { |
||
337 | \$this->unitOfWork->registerManaged(\$return, \$embeddedId, \$embeddedData); |
||
338 | } |
||
339 | |||
340 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
341 | \$hydratedData['%2\$s'] = \$return; |
||
342 | } |
||
343 | |||
344 | EOF |
||
345 | , |
||
346 | 44 | $mapping['name'], |
|
347 | 44 | $mapping['fieldName'] |
|
348 | ); |
||
349 | } |
||
350 | } |
||
351 | |||
352 | 177 | $namespace = $this->hydratorNamespace; |
|
353 | 177 | $code = sprintf( |
|
354 | <<<EOF |
||
355 | <?php |
||
356 | |||
357 | 177 | namespace $namespace; |
|
358 | |||
359 | use Doctrine\ODM\MongoDB\DocumentManager; |
||
360 | use Doctrine\ODM\MongoDB\Hydrator\HydratorInterface; |
||
361 | use Doctrine\ODM\MongoDB\Query\Query; |
||
362 | use Doctrine\ODM\MongoDB\UnitOfWork; |
||
363 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
||
364 | |||
365 | /** |
||
366 | * THIS CLASS WAS GENERATED BY THE DOCTRINE ODM. DO NOT EDIT THIS FILE. |
||
367 | */ |
||
368 | 177 | class $hydratorClassName implements HydratorInterface |
|
369 | { |
||
370 | private \$dm; |
||
371 | private \$unitOfWork; |
||
372 | private \$class; |
||
373 | |||
374 | public function __construct(DocumentManager \$dm, UnitOfWork \$uow, ClassMetadata \$class) |
||
375 | { |
||
376 | \$this->dm = \$dm; |
||
377 | \$this->unitOfWork = \$uow; |
||
378 | \$this->class = \$class; |
||
379 | } |
||
380 | |||
381 | public function hydrate(object \$document, array \$data, array \$hints = array()): array |
||
382 | { |
||
383 | \$hydratedData = array(); |
||
384 | %s return \$hydratedData; |
||
385 | } |
||
386 | } |
||
387 | EOF |
||
388 | , |
||
389 | 177 | $code |
|
390 | ); |
||
391 | |||
392 | 177 | if ($fileName === null) { |
|
393 | if (! class_exists($namespace . '\\' . $hydratorClassName)) { |
||
394 | eval(substr($code, 5)); |
||
395 | } |
||
396 | |||
397 | return; |
||
398 | } |
||
399 | |||
400 | 177 | $parentDirectory = dirname($fileName); |
|
401 | |||
402 | 177 | if (! is_dir($parentDirectory) && (@mkdir($parentDirectory, 0775, true) === false)) { |
|
403 | throw HydratorException::hydratorDirectoryNotWritable(); |
||
404 | } |
||
405 | |||
406 | 177 | if (! is_writable($parentDirectory)) { |
|
407 | throw HydratorException::hydratorDirectoryNotWritable(); |
||
408 | } |
||
409 | |||
410 | 177 | $tmpFileName = $fileName . '.' . uniqid('', true); |
|
411 | 177 | file_put_contents($tmpFileName, $code); |
|
412 | 177 | rename($tmpFileName, $fileName); |
|
413 | 177 | chmod($fileName, 0664); |
|
414 | 177 | } |
|
415 | |||
416 | /** |
||
417 | * Hydrate array of MongoDB document data into the given document object. |
||
418 | * |
||
419 | * @param array $hints Any hints to account for during reconstitution/lookup of the document. |
||
420 | */ |
||
421 | 385 | public function hydrate(object $document, array $data, array $hints = []) : array |
|
422 | { |
||
423 | 385 | $metadata = $this->dm->getClassMetadata(get_class($document)); |
|
424 | // Invoke preLoad lifecycle events and listeners |
||
425 | 385 | if (! empty($metadata->lifecycleCallbacks[Events::preLoad])) { |
|
0 ignored issues
–
show
Accessing
lifecycleCallbacks on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
426 | 14 | $args = [new PreLoadEventArgs($document, $this->dm, $data)]; |
|
427 | 14 | $metadata->invokeLifecycleCallbacks(Events::preLoad, $document, $args); |
|
428 | } |
||
429 | 385 | if ($this->evm->hasListeners(Events::preLoad)) { |
|
430 | 3 | $this->evm->dispatchEvent(Events::preLoad, new PreLoadEventArgs($document, $this->dm, $data)); |
|
431 | } |
||
432 | |||
433 | // alsoLoadMethods may transform the document before hydration |
||
434 | 385 | if (! empty($metadata->alsoLoadMethods)) { |
|
0 ignored issues
–
show
Accessing
alsoLoadMethods on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
435 | 12 | foreach ($metadata->alsoLoadMethods as $method => $fieldNames) { |
|
0 ignored issues
–
show
Accessing
alsoLoadMethods on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
436 | 12 | foreach ($fieldNames as $fieldName) { |
|
437 | // Invoke the method only once for the first field we find |
||
438 | 12 | if (array_key_exists($fieldName, $data)) { |
|
439 | 8 | $document->$method($data[$fieldName]); |
|
440 | 8 | continue 2; |
|
441 | } |
||
442 | } |
||
443 | } |
||
444 | } |
||
445 | |||
446 | 385 | if ($document instanceof GhostObjectInterface) { |
|
447 | 73 | $document->setProxyInitializer(null); |
|
448 | } |
||
449 | |||
450 | 385 | $data = $this->getHydratorFor($metadata->name)->hydrate($document, $data, $hints); |
|
0 ignored issues
–
show
Accessing
name on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
451 | |||
452 | // Invoke the postLoad lifecycle callbacks and listeners |
||
453 | 385 | if (! empty($metadata->lifecycleCallbacks[Events::postLoad])) { |
|
0 ignored issues
–
show
Accessing
lifecycleCallbacks on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
If you access a property on an interface, you most likely code against a concrete implementation of the interface. Available Fixes
Loading history...
|
|||
454 | 13 | $metadata->invokeLifecycleCallbacks(Events::postLoad, $document, [new LifecycleEventArgs($document, $this->dm)]); |
|
455 | } |
||
456 | 385 | if ($this->evm->hasListeners(Events::postLoad)) { |
|
457 | 4 | $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($document, $this->dm)); |
|
458 | } |
||
459 | |||
460 | 385 | return $data; |
|
461 | } |
||
462 | } |
||
463 |
This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.
Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.