These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | /* |
||
3 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||
4 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||
5 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||
6 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||
7 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||
8 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||
9 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||
10 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||
11 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||
12 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||
13 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||
14 | * |
||
15 | * This software consists of voluntary contributions made by many individuals |
||
16 | * and is licensed under the MIT license. For more information, see |
||
17 | * <http://www.doctrine-project.org>. |
||
18 | */ |
||
19 | |||
20 | namespace Doctrine\ODM\MongoDB\Hydrator; |
||
21 | |||
22 | use Doctrine\Common\EventManager; |
||
23 | use Doctrine\ODM\MongoDB\DocumentManager; |
||
24 | use Doctrine\ODM\MongoDB\Event\LifecycleEventArgs; |
||
25 | use Doctrine\ODM\MongoDB\Event\PreLoadEventArgs; |
||
26 | use Doctrine\ODM\MongoDB\Events; |
||
27 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
||
28 | use Doctrine\ODM\MongoDB\Proxy\Proxy; |
||
29 | use Doctrine\ODM\MongoDB\Types\Type; |
||
30 | use Doctrine\ODM\MongoDB\UnitOfWork; |
||
31 | use Doctrine\ODM\MongoDB\Configuration; |
||
32 | |||
33 | /** |
||
34 | * The HydratorFactory class is responsible for instantiating a correct hydrator |
||
35 | * type based on document's ClassMetadata |
||
36 | * |
||
37 | * @since 1.0 |
||
38 | */ |
||
39 | class HydratorFactory |
||
40 | { |
||
41 | /** |
||
42 | * The DocumentManager this factory is bound to. |
||
43 | * |
||
44 | * @var \Doctrine\ODM\MongoDB\DocumentManager |
||
45 | */ |
||
46 | private $dm; |
||
47 | |||
48 | /** |
||
49 | * The UnitOfWork used to coordinate object-level transactions. |
||
50 | * |
||
51 | * @var \Doctrine\ODM\MongoDB\UnitOfWork |
||
52 | */ |
||
53 | private $unitOfWork; |
||
54 | |||
55 | /** |
||
56 | * The EventManager associated with this Hydrator |
||
57 | * |
||
58 | * @var \Doctrine\Common\EventManager |
||
59 | */ |
||
60 | private $evm; |
||
61 | |||
62 | /** |
||
63 | * Which algorithm to use to automatically (re)generate hydrator classes. |
||
64 | * |
||
65 | * @var integer |
||
66 | */ |
||
67 | private $autoGenerate; |
||
68 | |||
69 | /** |
||
70 | * The namespace that contains all hydrator classes. |
||
71 | * |
||
72 | * @var string |
||
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 = array(); |
||
89 | |||
90 | /** |
||
91 | * @param DocumentManager $dm |
||
92 | * @param EventManager $evm |
||
93 | * @param string $hydratorDir |
||
94 | * @param string $hydratorNs |
||
95 | * @param integer $autoGenerate |
||
96 | * @throws HydratorException |
||
97 | */ |
||
98 | 1094 | public function __construct(DocumentManager $dm, EventManager $evm, $hydratorDir, $hydratorNs, $autoGenerate) |
|
99 | { |
||
100 | 1094 | if ( ! $hydratorDir) { |
|
101 | throw HydratorException::hydratorDirectoryRequired(); |
||
102 | } |
||
103 | 1094 | if ( ! $hydratorNs) { |
|
104 | throw HydratorException::hydratorNamespaceRequired(); |
||
105 | } |
||
106 | 1094 | $this->dm = $dm; |
|
107 | 1094 | $this->evm = $evm; |
|
108 | 1094 | $this->hydratorDir = $hydratorDir; |
|
109 | 1094 | $this->hydratorNamespace = $hydratorNs; |
|
110 | 1094 | $this->autoGenerate = $autoGenerate; |
|
111 | 1094 | } |
|
112 | |||
113 | /** |
||
114 | * Sets the UnitOfWork instance. |
||
115 | * |
||
116 | * @param UnitOfWork $uow |
||
117 | */ |
||
118 | 1094 | public function setUnitOfWork(UnitOfWork $uow) |
|
119 | { |
||
120 | 1094 | $this->unitOfWork = $uow; |
|
121 | 1094 | } |
|
122 | |||
123 | /** |
||
124 | * Gets the hydrator object for the given document class. |
||
125 | * |
||
126 | * @param string $className |
||
127 | * @return \Doctrine\ODM\MongoDB\Hydrator\HydratorInterface $hydrator |
||
128 | */ |
||
129 | 407 | public function getHydratorFor($className) |
|
130 | { |
||
131 | 407 | if (isset($this->hydrators[$className])) { |
|
132 | 210 | return $this->hydrators[$className]; |
|
133 | } |
||
134 | 407 | $hydratorClassName = str_replace('\\', '', $className) . 'Hydrator'; |
|
135 | 407 | $fqn = $this->hydratorNamespace . '\\' . $hydratorClassName; |
|
136 | 407 | $class = $this->dm->getClassMetadata($className); |
|
137 | |||
138 | 407 | View Code Duplication | if ( ! class_exists($fqn, false)) { |
139 | 180 | $fileName = $this->hydratorDir . DIRECTORY_SEPARATOR . $hydratorClassName . '.php'; |
|
140 | 180 | switch ($this->autoGenerate) { |
|
141 | 180 | case Configuration::AUTOGENERATE_NEVER: |
|
142 | require $fileName; |
||
143 | break; |
||
144 | |||
145 | 180 | case Configuration::AUTOGENERATE_ALWAYS: |
|
146 | 180 | $this->generateHydratorClass($class, $hydratorClassName, $fileName); |
|
147 | 180 | require $fileName; |
|
148 | 180 | break; |
|
149 | |||
150 | case Configuration::AUTOGENERATE_FILE_NOT_EXISTS: |
||
151 | if (!file_exists($fileName)) { |
||
152 | $this->generateHydratorClass($class, $hydratorClassName, $fileName); |
||
153 | } |
||
154 | require $fileName; |
||
155 | break; |
||
156 | |||
157 | case Configuration::AUTOGENERATE_EVAL: |
||
158 | $this->generateHydratorClass($class, $hydratorClassName, false); |
||
159 | break; |
||
160 | } |
||
161 | } |
||
162 | 407 | $this->hydrators[$className] = new $fqn($this->dm, $this->unitOfWork, $class); |
|
163 | 407 | return $this->hydrators[$className]; |
|
164 | } |
||
165 | |||
166 | /** |
||
167 | * Generates hydrator classes for all given classes. |
||
168 | * |
||
169 | * @param array $classes The classes (ClassMetadata instances) for which to generate hydrators. |
||
170 | * @param string $toDir The target directory of the hydrator classes. If not specified, the |
||
171 | * directory configured on the Configuration of the DocumentManager used |
||
172 | * by this factory is used. |
||
173 | */ |
||
174 | public function generateHydratorClasses(array $classes, $toDir = null) |
||
175 | { |
||
176 | $hydratorDir = $toDir ?: $this->hydratorDir; |
||
177 | $hydratorDir = rtrim($hydratorDir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR; |
||
178 | foreach ($classes as $class) { |
||
179 | $hydratorClassName = str_replace('\\', '', $class->name) . 'Hydrator'; |
||
180 | $hydratorFileName = $hydratorDir . $hydratorClassName . '.php'; |
||
181 | $this->generateHydratorClass($class, $hydratorClassName, $hydratorFileName); |
||
182 | } |
||
183 | } |
||
184 | |||
185 | /** |
||
186 | * @param ClassMetadata $class |
||
187 | * @param string $hydratorClassName |
||
188 | * @param string $fileName |
||
189 | */ |
||
190 | 180 | private function generateHydratorClass(ClassMetadata $class, $hydratorClassName, $fileName) |
|
191 | { |
||
192 | 180 | $code = ''; |
|
193 | |||
194 | 180 | foreach ($class->fieldMappings as $fieldName => $mapping) { |
|
195 | 180 | if (isset($mapping['alsoLoadFields'])) { |
|
196 | 5 | foreach ($mapping['alsoLoadFields'] as $name) { |
|
197 | 5 | $code .= sprintf(<<<EOF |
|
198 | |||
199 | 5 | /** @AlsoLoad("$name") */ |
|
200 | 5 | if (!array_key_exists('%1\$s', \$data) && array_key_exists('$name', \$data)) { |
|
201 | 5 | \$data['%1\$s'] = \$data['$name']; |
|
202 | } |
||
203 | |||
204 | EOF |
||
205 | , |
||
206 | 5 | $mapping['name'] |
|
207 | ); |
||
208 | } |
||
209 | } |
||
210 | |||
211 | 180 | if ($mapping['type'] === 'date') { |
|
212 | 9 | $code .= sprintf(<<<EOF |
|
213 | |||
214 | /** @Field(type="date") */ |
||
215 | if (isset(\$data['%1\$s'])) { |
||
216 | \$value = \$data['%1\$s']; |
||
217 | %3\$s |
||
218 | \$this->class->reflFields['%2\$s']->setValue(\$document, clone \$return); |
||
219 | \$hydratedData['%2\$s'] = \$return; |
||
220 | } |
||
221 | |||
222 | EOF |
||
223 | , |
||
224 | 9 | $mapping['name'], |
|
225 | 9 | $mapping['fieldName'], |
|
226 | 9 | Type::getType($mapping['type'])->closureToPHP() |
|
227 | ); |
||
228 | |||
229 | |||
230 | 180 | } elseif ( ! isset($mapping['association'])) { |
|
231 | 180 | $code .= sprintf(<<<EOF |
|
232 | |||
233 | 180 | /** @Field(type="{$mapping['type']}") */ |
|
234 | if (isset(\$data['%1\$s']) || (! empty(\$this->class->fieldMappings['%2\$s']['nullable']) && array_key_exists('%1\$s', \$data))) { |
||
235 | \$value = \$data['%1\$s']; |
||
236 | if (\$value !== null) { |
||
237 | \$typeIdentifier = \$this->class->fieldMappings['%2\$s']['type']; |
||
238 | %3\$s |
||
239 | } else { |
||
240 | \$return = null; |
||
241 | } |
||
242 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
243 | \$hydratedData['%2\$s'] = \$return; |
||
244 | } |
||
245 | |||
246 | EOF |
||
247 | , |
||
248 | 180 | $mapping['name'], |
|
249 | 180 | $mapping['fieldName'], |
|
250 | 180 | Type::getType($mapping['type'])->closureToPHP() |
|
251 | ); |
||
252 | 122 | View Code Duplication | } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) { |
253 | 50 | $code .= sprintf(<<<EOF |
|
254 | |||
255 | /** @ReferenceOne */ |
||
256 | if (isset(\$data['%1\$s'])) { |
||
257 | \$reference = \$data['%1\$s']; |
||
258 | if (isset(\$this->class->fieldMappings['%2\$s']['storeAs']) && \$this->class->fieldMappings['%2\$s']['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID) { |
||
259 | \$className = \$this->class->fieldMappings['%2\$s']['targetDocument']; |
||
260 | \$mongoId = \$reference; |
||
261 | } else { |
||
262 | \$className = \$this->unitOfWork->getClassNameForAssociation(\$this->class->fieldMappings['%2\$s'], \$reference); |
||
263 | \$mongoId = \$reference['\$id']; |
||
264 | } |
||
265 | \$targetMetadata = \$this->dm->getClassMetadata(\$className); |
||
266 | \$id = \$targetMetadata->getPHPIdentifierValue(\$mongoId); |
||
267 | \$return = \$this->dm->getReference(\$className, \$id); |
||
268 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
269 | \$hydratedData['%2\$s'] = \$return; |
||
270 | } |
||
271 | |||
272 | EOF |
||
273 | , |
||
274 | 50 | $mapping['name'], |
|
275 | 50 | $mapping['fieldName'] |
|
276 | ); |
||
277 | 104 | } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isInverseSide']) { |
|
278 | 6 | if (isset($mapping['repositoryMethod']) && $mapping['repositoryMethod']) { |
|
279 | 1 | $code .= sprintf(<<<EOF |
|
280 | |||
281 | \$className = \$this->class->fieldMappings['%2\$s']['targetDocument']; |
||
282 | \$return = \$this->dm->getRepository(\$className)->%3\$s(\$document); |
||
283 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
284 | \$hydratedData['%2\$s'] = \$return; |
||
285 | |||
286 | EOF |
||
287 | , |
||
288 | 1 | $mapping['name'], |
|
289 | 1 | $mapping['fieldName'], |
|
290 | 1 | $mapping['repositoryMethod'] |
|
291 | ); |
||
292 | } else { |
||
293 | 6 | $code .= sprintf(<<<EOF |
|
294 | |||
295 | \$mapping = \$this->class->fieldMappings['%2\$s']; |
||
296 | \$className = \$mapping['targetDocument']; |
||
297 | \$targetClass = \$this->dm->getClassMetadata(\$mapping['targetDocument']); |
||
298 | \$mappedByMapping = \$targetClass->fieldMappings[\$mapping['mappedBy']]; |
||
299 | \$mappedByFieldName = isset(\$mappedByMapping['storeAs']) && \$mappedByMapping['storeAs'] === ClassMetadataInfo::REFERENCE_STORE_AS_ID ? \$mapping['mappedBy'] : \$mapping['mappedBy'].'.\$id'; |
||
300 | \$criteria = array_merge( |
||
301 | array(\$mappedByFieldName => \$data['_id']), |
||
302 | isset(\$this->class->fieldMappings['%2\$s']['criteria']) ? \$this->class->fieldMappings['%2\$s']['criteria'] : array() |
||
303 | ); |
||
304 | \$sort = isset(\$this->class->fieldMappings['%2\$s']['sort']) ? \$this->class->fieldMappings['%2\$s']['sort'] : array(); |
||
305 | \$return = \$this->unitOfWork->getDocumentPersister(\$className)->load(\$criteria, null, array(), 0, \$sort); |
||
306 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
307 | \$hydratedData['%2\$s'] = \$return; |
||
308 | |||
309 | EOF |
||
310 | , |
||
311 | 6 | $mapping['name'], |
|
312 | 6 | $mapping['fieldName'] |
|
313 | ); |
||
314 | } |
||
315 | 103 | View Code Duplication | } elseif ($mapping['association'] === ClassMetadata::REFERENCE_MANY || $mapping['association'] === ClassMetadata::EMBED_MANY) { |
316 | 82 | $code .= sprintf(<<<EOF |
|
317 | |||
318 | /** @Many */ |
||
319 | \$mongoData = isset(\$data['%1\$s']) ? \$data['%1\$s'] : null; |
||
320 | \$return = \$this->dm->getConfiguration()->getPersistentCollectionFactory()->create(\$this->dm, \$this->class->fieldMappings['%2\$s']); |
||
321 | \$return->setHints(\$hints); |
||
322 | \$return->setOwner(\$document, \$this->class->fieldMappings['%2\$s']); |
||
323 | \$return->setInitialized(false); |
||
324 | if (\$mongoData) { |
||
325 | \$return->setMongoData(\$mongoData); |
||
326 | } |
||
327 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
328 | \$hydratedData['%2\$s'] = \$return; |
||
329 | |||
330 | EOF |
||
331 | , |
||
332 | 82 | $mapping['name'], |
|
333 | 82 | $mapping['fieldName'] |
|
334 | ); |
||
335 | 45 | } elseif ($mapping['association'] === ClassMetadata::EMBED_ONE) { |
|
336 | 45 | $code .= sprintf(<<<EOF |
|
337 | |||
338 | /** @EmbedOne */ |
||
339 | if (isset(\$data['%1\$s'])) { |
||
340 | \$embeddedDocument = \$data['%1\$s']; |
||
341 | \$className = \$this->unitOfWork->getClassNameForAssociation(\$this->class->fieldMappings['%2\$s'], \$embeddedDocument); |
||
342 | \$embeddedMetadata = \$this->dm->getClassMetadata(\$className); |
||
343 | \$return = \$embeddedMetadata->newInstance(); |
||
344 | |||
345 | \$this->unitOfWork->setParentAssociation(\$return, \$this->class->fieldMappings['%2\$s'], \$document, '%1\$s'); |
||
346 | |||
347 | \$embeddedData = \$this->dm->getHydratorFactory()->hydrate(\$return, \$embeddedDocument, \$hints); |
||
348 | \$embeddedId = \$embeddedMetadata->identifier && isset(\$embeddedData[\$embeddedMetadata->identifier]) ? \$embeddedData[\$embeddedMetadata->identifier] : null; |
||
349 | |||
350 | if (empty(\$hints[Query::HINT_READ_ONLY])) { |
||
351 | \$this->unitOfWork->registerManaged(\$return, \$embeddedId, \$embeddedData); |
||
352 | } |
||
353 | |||
354 | \$this->class->reflFields['%2\$s']->setValue(\$document, \$return); |
||
355 | \$hydratedData['%2\$s'] = \$return; |
||
356 | } |
||
357 | |||
358 | EOF |
||
359 | , |
||
360 | 45 | $mapping['name'], |
|
361 | 45 | $mapping['fieldName'] |
|
362 | ); |
||
363 | } |
||
364 | } |
||
365 | |||
366 | 180 | $namespace = $this->hydratorNamespace; |
|
367 | 180 | $code = sprintf(<<<EOF |
|
368 | <?php |
||
369 | |||
370 | 180 | namespace $namespace; |
|
371 | |||
372 | use Doctrine\ODM\MongoDB\DocumentManager; |
||
373 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; |
||
374 | use Doctrine\ODM\MongoDB\Hydrator\HydratorInterface; |
||
375 | use Doctrine\ODM\MongoDB\Query\Query; |
||
376 | use Doctrine\ODM\MongoDB\UnitOfWork; |
||
377 | use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo; |
||
378 | |||
379 | /** |
||
380 | * THIS CLASS WAS GENERATED BY THE DOCTRINE ODM. DO NOT EDIT THIS FILE. |
||
381 | */ |
||
382 | 180 | class $hydratorClassName implements HydratorInterface |
|
383 | { |
||
384 | private \$dm; |
||
385 | private \$unitOfWork; |
||
386 | private \$class; |
||
387 | |||
388 | public function __construct(DocumentManager \$dm, UnitOfWork \$uow, ClassMetadata \$class) |
||
389 | { |
||
390 | \$this->dm = \$dm; |
||
391 | \$this->unitOfWork = \$uow; |
||
392 | \$this->class = \$class; |
||
393 | } |
||
394 | |||
395 | public function hydrate(\$document, \$data, array \$hints = array()) |
||
396 | { |
||
397 | \$hydratedData = array(); |
||
398 | %s return \$hydratedData; |
||
399 | } |
||
400 | } |
||
401 | EOF |
||
402 | , |
||
403 | 180 | $code |
|
404 | ); |
||
405 | |||
406 | 180 | if ($fileName === false) { |
|
407 | if ( ! class_exists($namespace . '\\' . $hydratorClassName)) { |
||
408 | eval(substr($code, 5)); |
||
409 | } |
||
410 | View Code Duplication | } else { |
|
411 | 180 | $parentDirectory = dirname($fileName); |
|
412 | |||
413 | 180 | if ( ! is_dir($parentDirectory) && (false === @mkdir($parentDirectory, 0775, true))) { |
|
414 | throw HydratorException::hydratorDirectoryNotWritable(); |
||
415 | } |
||
416 | |||
417 | 180 | if ( ! is_writable($parentDirectory)) { |
|
418 | throw HydratorException::hydratorDirectoryNotWritable(); |
||
419 | } |
||
420 | |||
421 | 180 | $tmpFileName = $fileName . '.' . uniqid('', true); |
|
422 | 180 | file_put_contents($tmpFileName, $code); |
|
423 | 180 | rename($tmpFileName, $fileName); |
|
424 | 180 | chmod($fileName, 0664); |
|
425 | } |
||
426 | 180 | } |
|
427 | |||
428 | /** |
||
429 | * Hydrate array of MongoDB document data into the given document object. |
||
430 | * |
||
431 | * @param object $document The document object to hydrate the data into. |
||
432 | * @param array $data The array of document data. |
||
433 | * @param array $hints Any hints to account for during reconstitution/lookup of the document. |
||
434 | * @return array $values The array of hydrated values. |
||
435 | */ |
||
436 | 407 | public function hydrate($document, $data, array $hints = array()) |
|
437 | { |
||
438 | 407 | $metadata = $this->dm->getClassMetadata(get_class($document)); |
|
439 | // Invoke preLoad lifecycle events and listeners |
||
440 | 407 | if ( ! empty($metadata->lifecycleCallbacks[Events::preLoad])) { |
|
441 | 14 | $args = array(new PreLoadEventArgs($document, $this->dm, $data)); |
|
442 | 14 | $metadata->invokeLifecycleCallbacks(Events::preLoad, $document, $args); |
|
443 | } |
||
444 | 407 | View Code Duplication | if ($this->evm->hasListeners(Events::preLoad)) { |
445 | 3 | $this->evm->dispatchEvent(Events::preLoad, new PreLoadEventArgs($document, $this->dm, $data)); |
|
446 | } |
||
447 | |||
448 | // alsoLoadMethods may transform the document before hydration |
||
449 | 407 | if ( ! empty($metadata->alsoLoadMethods)) { |
|
450 | 12 | foreach ($metadata->alsoLoadMethods as $method => $fieldNames) { |
|
451 | 12 | foreach ($fieldNames as $fieldName) { |
|
452 | // Invoke the method only once for the first field we find |
||
453 | 12 | if (array_key_exists($fieldName, $data)) { |
|
454 | 8 | $document->$method($data[$fieldName]); |
|
455 | 12 | continue 2; |
|
456 | } |
||
457 | } |
||
458 | } |
||
459 | } |
||
460 | |||
461 | 407 | $data = $this->getHydratorFor($metadata->name)->hydrate($document, $data, $hints); |
|
462 | 407 | if ($document instanceof Proxy) { |
|
463 | 69 | $document->__isInitialized__ = true; |
|
0 ignored issues
–
show
|
|||
464 | 69 | $document->__setInitializer(null); |
|
465 | 69 | $document->__setCloner(null); |
|
466 | // lazy properties may be left uninitialized |
||
467 | 69 | $properties = $document->__getLazyProperties(); |
|
468 | 69 | foreach ($properties as $propertyName => $property) { |
|
469 | 27 | if ( ! isset($document->$propertyName)) { |
|
470 | 10 | $document->$propertyName = $properties[$propertyName]; |
|
471 | } |
||
472 | } |
||
473 | } |
||
474 | |||
475 | // Invoke the postLoad lifecycle callbacks and listeners |
||
476 | 407 | View Code Duplication | if ( ! empty($metadata->lifecycleCallbacks[Events::postLoad])) { |
477 | 13 | $metadata->invokeLifecycleCallbacks(Events::postLoad, $document, array(new LifecycleEventArgs($document, $this->dm))); |
|
478 | } |
||
479 | 407 | View Code Duplication | if ($this->evm->hasListeners(Events::postLoad)) { |
480 | 4 | $this->evm->dispatchEvent(Events::postLoad, new LifecycleEventArgs($document, $this->dm)); |
|
481 | } |
||
482 | |||
483 | 407 | return $data; |
|
484 | } |
||
485 | } |
||
486 |
If you access a property on an interface, you most likely code against a concrete implementation of the interface.
Available Fixes
Adding an additional type check:
Changing the type hint: