Failed Conditions
Push — master ( 82c91a...ddb3cd )
by Guilherme
15:32 queued 12:12
created

lib/Doctrine/ORM/AbstractQuery.php (4 issues)

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\ORM;
6
7
use Doctrine\Common\Collections\ArrayCollection;
8
use Doctrine\Common\Collections\Collection;
9
use Doctrine\DBAL\Cache\QueryCacheProfile;
10
use Doctrine\ORM\Cache\QueryCacheKey;
11
use Doctrine\ORM\Query\Parameter;
12
use Doctrine\ORM\Utility\StaticClassNameConverter;
13
14
/**
15
 * Base contract for ORM queries. Base class for Query and NativeQuery.
16
 */
17
abstract class AbstractQuery
18
{
19
    /* Hydration mode constants */
20
21
    /**
22
     * Hydrates an object graph. This is the default behavior.
23
     */
24
    public const HYDRATE_OBJECT = 1;
25
26
    /**
27
     * Hydrates an array graph.
28
     */
29
    public const HYDRATE_ARRAY = 2;
30
31
    /**
32
     * Hydrates a flat, rectangular result set with scalar values.
33
     */
34
    public const HYDRATE_SCALAR = 3;
35
36
    /**
37
     * Hydrates a single scalar value.
38
     */
39
    public const HYDRATE_SINGLE_SCALAR = 4;
40
41
    /**
42
     * Very simple object hydrator (optimized for performance).
43
     */
44
    public const HYDRATE_SIMPLEOBJECT = 5;
45
46
    /**
47
     * The parameter map of this query.
48
     *
49
     * @var \Doctrine\Common\Collections\ArrayCollection
50
     */
51
    protected $parameters;
52
53
    /**
54
     * The user-specified ResultSetMapping to use.
55
     *
56
     * @var \Doctrine\ORM\Query\ResultSetMapping
57
     */
58
    protected $resultSetMapping;
59
60
    /**
61
     * The entity manager used by this query object.
62
     *
63
     * @var EntityManagerInterface
64
     */
65
    protected $em;
66
67
    /**
68
     * The map of query hints.
69
     *
70
     * @var mixed[]
71
     */
72
    protected $hints = [];
73
74
    /**
75
     * The hydration mode.
76
     *
77
     * @var int
78
     */
79
    protected $hydrationMode = self::HYDRATE_OBJECT;
80
81
    /**
82
     * @var \Doctrine\DBAL\Cache\QueryCacheProfile
83
     */
84
    protected $queryCacheProfile;
85
86
    /**
87
     * Whether or not expire the result cache.
88
     *
89
     * @var bool
90
     */
91
    protected $expireResultCache = false;
92
93
    /**
94
     * @var \Doctrine\DBAL\Cache\QueryCacheProfile
95
     */
96
    protected $hydrationCacheProfile;
97
98
    /**
99
     * Whether to use second level cache, if available.
100
     *
101
     * @var bool
102
     */
103
    protected $cacheable = false;
104
105
    /**
106
     * @var bool
107
     */
108
    protected $hasCache = false;
109
110
    /**
111
     * Second level cache region name.
112
     *
113
     * @var string|null
114
     */
115
    protected $cacheRegion;
116
117
    /**
118
     * Second level query cache mode.
119
     *
120
     * @var int|null
121
     */
122
    protected $cacheMode;
123
124
    /**
125
     * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null
126
     */
127
    protected $cacheLogger;
128
129
    /**
130
     * @var int
131
     */
132
    protected $lifetime = 0;
133
134
    /**
135
     * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
136
     */
137 973
    public function __construct(EntityManagerInterface $em)
138
    {
139 973
        $this->em         = $em;
140 973
        $this->parameters = new ArrayCollection();
141 973
        $this->hints      = $em->getConfiguration()->getDefaultQueryHints();
142 973
        $this->hasCache   = $this->em->getConfiguration()->isSecondLevelCacheEnabled();
143
144 973
        if ($this->hasCache) {
145 32
            $this->cacheLogger = $em->getConfiguration()
146 32
                ->getSecondLevelCacheConfiguration()
147 32
                ->getCacheLogger();
148
        }
149 973
    }
150
151
    /**
152
     * Enable/disable second level query (result) caching for this query.
153
     *
154
     * @param bool $cacheable
155
     *
156
     * @return static This query instance.
157
     */
158 133
    public function setCacheable($cacheable)
159
    {
160 133
        $this->cacheable = (bool) $cacheable;
161
162 133
        return $this;
163
    }
164
165
    /**
166
     * @return bool TRUE if the query results are enable for second level cache, FALSE otherwise.
167
     */
168 90
    public function isCacheable()
169
    {
170 90
        return $this->cacheable;
171
    }
172
173
    /**
174
     * @param string $cacheRegion
175
     *
176
     * @return static This query instance.
177
     */
178 2
    public function setCacheRegion($cacheRegion)
179
    {
180 2
        $this->cacheRegion = (string) $cacheRegion;
181
182 2
        return $this;
183
    }
184
185
    /**
186
     * Obtain the name of the second level query cache region in which query results will be stored
187
     *
188
     * @return string|null The cache region name; NULL indicates the default region.
189
     */
190 1
    public function getCacheRegion()
191
    {
192 1
        return $this->cacheRegion;
193
    }
194
195
    /**
196
     * @return bool TRUE if the query cache and second level cache are enabled, FALSE otherwise.
197
     */
198 29
    protected function isCacheEnabled()
199
    {
200 29
        return $this->cacheable && $this->hasCache;
201
    }
202
203
    /**
204
     * @return int
205
     */
206 1
    public function getLifetime()
207
    {
208 1
        return $this->lifetime;
209
    }
210
211
    /**
212
     * Sets the life-time for this query into second level cache.
213
     *
214
     * @param int $lifetime
215
     *
216
     * @return \Doctrine\ORM\AbstractQuery This query instance.
217
     */
218 2
    public function setLifetime($lifetime)
219
    {
220 2
        $this->lifetime = (integer) $lifetime;
221
222 2
        return $this;
223
    }
224
225
    /**
226
     * @return int
227
     */
228 1
    public function getCacheMode()
229
    {
230 1
        return $this->cacheMode;
231
    }
232
233
    /**
234
     * @param int $cacheMode
235
     *
236
     * @return \Doctrine\ORM\AbstractQuery This query instance.
237
     */
238 4
    public function setCacheMode($cacheMode)
239
    {
240 4
        $this->cacheMode = (integer) $cacheMode;
241
242 4
        return $this;
243
    }
244
245
    /**
246
     * Gets the SQL query that corresponds to this query object.
247
     * The returned SQL syntax depends on the connection driver that is used
248
     * by this query object at the time of this method call.
249
     *
250
     * @return string SQL query
251
     */
252
    abstract public function getSQL();
253
254
    /**
255
     * Retrieves the associated EntityManager of this Query instance.
256
     *
257
     * @return \Doctrine\ORM\EntityManagerInterface
258
     */
259 888
    public function getEntityManager()
260
    {
261 888
        return $this->em;
262
    }
263
264
    /**
265
     * Frees the resources used by the query object.
266
     *
267
     * Resets Parameters, Parameter Types and Query Hints.
268
     */
269 219
    public function free()
270
    {
271 219
        $this->parameters = new ArrayCollection();
272
273 219
        $this->hints = $this->em->getConfiguration()->getDefaultQueryHints();
274 219
    }
275
276
    /**
277
     * Get all defined parameters.
278
     *
279
     * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters.
280
     */
281 142
    public function getParameters()
282
    {
283 142
        return $this->parameters;
284
    }
285
286
    /**
287
     * Gets a query parameter.
288
     *
289
     * @param mixed $key The key (index or name) of the bound parameter.
290
     *
291
     * @return Query\Parameter|null The value of the bound parameter, or NULL if not available.
292
     */
293 267
    public function getParameter($key)
294
    {
295 267
        $filteredParameters = $this->parameters->filter(
296 267
            function (Query\Parameter $parameter) use ($key) : bool {
297 147
                $parameterName = $parameter->getName();
298
299 147
                return $key === $parameterName || (string) $key === (string) $parameterName;
300 267
            }
301
        );
302
303 267
        return $filteredParameters->isEmpty() ? null : $filteredParameters->first();
304
    }
305
306
    /**
307
     * Sets a collection of query parameters.
308
     *
309
     * @param \Doctrine\Common\Collections\ArrayCollection|array|Parameter[]|mixed[] $parameters
310
     *
311
     * @return static This query instance.
312
     */
313 188
    public function setParameters($parameters)
314
    {
315
        // BC compatibility with 2.3-
316 188
        if (is_array($parameters)) {
317 1
            $parameterCollection = new ArrayCollection();
318
319 1
            foreach ($parameters as $key => $value) {
320 1
                $parameterCollection->add(new Parameter($key, $value));
321
            }
322
323 1
            $parameters = $parameterCollection;
324
        }
325
326 188
        $this->parameters = $parameters;
327
328 188
        return $this;
329
    }
330
331
    /**
332
     * Sets a query parameter.
333
     *
334
     * @param string|int  $key   The parameter position or name.
335
     * @param mixed       $value The parameter value.
336
     * @param string|null $type  The parameter type. If specified, the given value will be run through
337
     *                           the type conversion of this type. This is usually not needed for
338
     *                           strings and numeric types.
339
     *
340
     * @return static This query instance.
341
     */
342 179
    public function setParameter($key, $value, $type = null)
343
    {
344 179
        $existingParameter = $this->getParameter($key);
345
346 179
        if ($existingParameter !== null) {
347 5
            $existingParameter->setValue($value, $type);
348
349 5
            return $this;
350
        }
351
352 177
        $this->parameters->add(new Parameter($key, $value, $type));
353
354 177
        return $this;
355
    }
356
357
    /**
358
     * Processes an individual parameter value.
359
     *
360
     * @param mixed $value
361
     *
362
     * @return string|mixed[]
363
     *
364
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
365
     */
366 174
    public function processParameterValue($value)
367
    {
368 174
        if (is_scalar($value)) {
369 162
            return $value;
370
        }
371
372 89
        if ($value instanceof Mapping\ClassMetadata) {
373 1
            return $value->discriminatorValue ?: $value->getClassName();
374
        }
375
376 88
        if ($value instanceof Collection) {
377 1
            $value = $value->toArray();
378
        }
379
380 88
        if (is_array($value)) {
381 73
            foreach ($value as $key => $paramValue) {
382 73
                $paramValue  = $this->processParameterValue($paramValue);
383 73
                $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
384
            }
385
386 73
            return $value;
387
        }
388
389 18
        if (is_object($value) && $this->em->getMetadataFactory()->hasMetadataFor(StaticClassNameConverter::getClass($value))) {
390 13
            $value = $this->em->getUnitOfWork()->getSingleIdentifierValue($value);
391
392 13
            if ($value === null) {
393
                throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
394
            }
395
        }
396
397 18
        return $value;
398
    }
399
400
    /**
401
     * Sets the ResultSetMapping that should be used for hydration.
402
     *
403
     *
404
     * @return static This query instance.
405
     */
406 31
    public function setResultSetMapping(Query\ResultSetMapping $rsm)
407
    {
408 31
        $this->resultSetMapping = $rsm;
409
410 31
        return $this;
411
    }
412
413
    /**
414
     * Gets the ResultSetMapping used for hydration.
415
     *
416
     * @return \Doctrine\ORM\Query\ResultSetMapping
417
     */
418 23
    protected function getResultSetMapping()
419
    {
420 23
        return $this->resultSetMapping;
421
    }
422
423
    /**
424
     * Set a cache profile for hydration caching.
425
     *
426
     * If no result cache driver is set in the QueryCacheProfile, the default
427
     * result cache driver is used from the configuration.
428
     *
429
     * Important: Hydration caching does NOT register entities in the
430
     * UnitOfWork when retrieved from the cache. Never use result cached
431
     * entities for requests that also flush the EntityManager. If you want
432
     * some form of caching with UnitOfWork registration you should use
433
     * {@see AbstractQuery::setResultCacheProfile()}.
434
     *
435
     * @example
436
     * $lifetime = 100;
437
     * $resultKey = "abc";
438
     * $query->setHydrationCacheProfile(new QueryCacheProfile());
439
     * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
440
     *
441
     *
442
     * @return static This query instance.
443
     */
444 3
    public function setHydrationCacheProfile(?QueryCacheProfile $profile = null)
445
    {
446 3
        if ($profile !== null && ! $profile->getResultCacheDriver()) {
447
            $resultCacheDriver = $this->em->getConfiguration()->getHydrationCacheImpl();
448
            $profile           = $profile->setResultCacheDriver($resultCacheDriver);
449
        }
450
451 3
        $this->hydrationCacheProfile = $profile;
452
453 3
        return $this;
454
    }
455
456
    /**
457
     * @return \Doctrine\DBAL\Cache\QueryCacheProfile
458
     */
459 3
    public function getHydrationCacheProfile()
460
    {
461 3
        return $this->hydrationCacheProfile;
462
    }
463
464
    /**
465
     * Set a cache profile for the result cache.
466
     *
467
     * If no result cache driver is set in the QueryCacheProfile, the default
468
     * result cache driver is used from the configuration.
469
     *
470
     *
471
     * @return static This query instance.
472
     */
473 1
    public function setResultCacheProfile(?QueryCacheProfile $profile = null)
474
    {
475 1
        if ($profile !== null && ! $profile->getResultCacheDriver()) {
476
            $resultCacheDriver = $this->em->getConfiguration()->getResultCacheImpl();
477
            $profile           = $profile->setResultCacheDriver($resultCacheDriver);
478
        }
479
480 1
        $this->queryCacheProfile = $profile;
481
482 1
        return $this;
483
    }
484
485
    /**
486
     * Defines a cache driver to be used for caching result sets and implicitly enables caching.
487
     *
488
     * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver
489
     *
490
     * @return static This query instance.
491
     *
492
     * @throws ORMException
493
     */
494 8
    public function setResultCacheDriver($resultCacheDriver = null)
495
    {
496 8
        if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
497
            throw ORMException::invalidResultCacheDriver();
498
        }
499
500 8
        $this->queryCacheProfile = $this->queryCacheProfile
501 1
            ? $this->queryCacheProfile->setResultCacheDriver($resultCacheDriver)
0 ignored issues
show
It seems like $resultCacheDriver can also be of type null; however, parameter $cache of Doctrine\DBAL\Cache\Quer...:setResultCacheDriver() does only seem to accept Doctrine\Common\Cache\Cache, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

501
            ? $this->queryCacheProfile->setResultCacheDriver(/** @scrutinizer ignore-type */ $resultCacheDriver)
Loading history...
502 7
            : new QueryCacheProfile(0, null, $resultCacheDriver);
503
504 8
        return $this;
505
    }
506
507
    /**
508
     * Returns the cache driver used for caching result sets.
509
     *
510
     * @deprecated
511
     *
512
     * @return \Doctrine\Common\Cache\Cache Cache driver
513
     */
514 3
    public function getResultCacheDriver()
515
    {
516 3
        if ($this->queryCacheProfile && $this->queryCacheProfile->getResultCacheDriver()) {
517 3
            return $this->queryCacheProfile->getResultCacheDriver();
518
        }
519
520
        return $this->em->getConfiguration()->getResultCacheImpl();
521
    }
522
523
    /**
524
     * Set whether or not to cache the results of this query and if so, for
525
     * how long and which ID to use for the cache entry.
526
     *
527
     * @param bool   $bool
528
     * @param int    $lifetime
529
     * @param string $resultCacheId
530
     *
531
     * @return static This query instance.
532
     */
533 9
    public function useResultCache($bool, $lifetime = null, $resultCacheId = null)
534
    {
535 9
        if ($bool) {
536 9
            $this->setResultCacheLifetime($lifetime);
537 9
            $this->setResultCacheId($resultCacheId);
538
539 9
            return $this;
540
        }
541
542 1
        $this->queryCacheProfile = null;
543
544 1
        return $this;
545
    }
546
547
    /**
548
     * Defines how long the result cache will be active before expire.
549
     *
550
     * @param int $lifetime How long the cache entry is valid.
551
     *
552
     * @return static This query instance.
553
     */
554 10
    public function setResultCacheLifetime($lifetime)
555
    {
556 10
        $lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
0 ignored issues
show
The condition $lifetime !== null can never be false.
Loading history...
557
558 10
        $this->queryCacheProfile = $this->queryCacheProfile
559 4
            ? $this->queryCacheProfile->setLifetime($lifetime)
560 6
            : new QueryCacheProfile($lifetime, null, $this->em->getConfiguration()->getResultCacheImpl());
561
562 10
        return $this;
563
    }
564
565
    /**
566
     * Retrieves the lifetime of resultset cache.
567
     *
568
     * @deprecated
569
     *
570
     * @return int
571
     */
572
    public function getResultCacheLifetime()
573
    {
574
        return $this->queryCacheProfile ? $this->queryCacheProfile->getLifetime() : 0;
575
    }
576
577
    /**
578
     * Defines if the result cache is active or not.
579
     *
580
     * @param bool $expire Whether or not to force resultset cache expiration.
581
     *
582
     * @return static This query instance.
583
     */
584 4
    public function expireResultCache($expire = true)
585
    {
586 4
        $this->expireResultCache = $expire;
587
588 4
        return $this;
589
    }
590
591
    /**
592
     * Retrieves if the resultset cache is active or not.
593
     *
594
     * @return bool
595
     */
596 8
    public function getExpireResultCache()
597
    {
598 8
        return $this->expireResultCache;
599
    }
600
601
    /**
602
     * @return QueryCacheProfile
603
     */
604 1
    public function getQueryCacheProfile()
605
    {
606 1
        return $this->queryCacheProfile;
607
    }
608
609
    /**
610
     * Change the default fetch mode of an association for this query.
611
     *
612
     * $fetchMode can be one of FetchMode::EAGER, FetchMode::LAZY or FetchMode::EXTRA_LAZY
613
     *
614
     * @param string $class
615
     * @param string $assocName
616
     * @param int    $fetchMode
617
     *
618
     * @return static This query instance.
619
     */
620 3
    public function setFetchMode($class, $assocName, $fetchMode)
621
    {
622 3
        if ($fetchMode !== Mapping\FetchMode::EAGER) {
0 ignored issues
show
The condition $fetchMode !== Doctrine\...apping\FetchMode::EAGER can never be false.
Loading history...
623
            $fetchMode = Mapping\FetchMode::LAZY;
624
        }
625
626 3
        $this->hints['fetchMode'][$class][$assocName] = $fetchMode;
627
628 3
        return $this;
629
    }
630
631
    /**
632
     * Defines the processing mode to be used during hydration / result set transformation.
633
     *
634
     * @param int $hydrationMode Doctrine processing mode to be used during hydration process.
635
     *                           One of the Query::HYDRATE_* constants.
636
     *
637
     * @return static This query instance.
638
     */
639 380
    public function setHydrationMode($hydrationMode)
640
    {
641 380
        $this->hydrationMode = $hydrationMode;
642
643 380
        return $this;
644
    }
645
646
    /**
647
     * Gets the hydration mode currently used by the query.
648
     *
649
     * @return int
650
     */
651 652
    public function getHydrationMode()
652
    {
653 652
        return $this->hydrationMode;
654
    }
655
656
    /**
657
     * Gets the list of results for the query.
658
     *
659
     * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
660
     *
661
     * @param int $hydrationMode
662
     *
663
     * @return mixed
664
     */
665 303
    public function getResult($hydrationMode = self::HYDRATE_OBJECT)
666
    {
667 303
        return $this->execute(null, $hydrationMode);
668
    }
669
670
    /**
671
     * Gets the array of results for the query.
672
     *
673
     * Alias for execute(null, HYDRATE_ARRAY).
674
     *
675
     * @return mixed[]
676
     */
677 25
    public function getArrayResult()
678
    {
679 25
        return $this->execute(null, self::HYDRATE_ARRAY);
680
    }
681
682
    /**
683
     * Gets the scalar results for the query.
684
     *
685
     * Alias for execute(null, HYDRATE_SCALAR).
686
     *
687
     * @return mixed[]
688
     */
689 87
    public function getScalarResult()
690
    {
691 87
        return $this->execute(null, self::HYDRATE_SCALAR);
692
    }
693
694
    /**
695
     * Get exactly one result or null.
696
     *
697
     * @param int $hydrationMode
698
     *
699
     * @return mixed
700
     *
701
     * @throws NonUniqueResultException
702
     */
703 14
    public function getOneOrNullResult($hydrationMode = null)
704
    {
705
        try {
706 14
            $result = $this->execute(null, $hydrationMode);
707 1
        } catch (NoResultException $e) {
708 1
            return null;
709
        }
710
711 13
        if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
712 1
            return null;
713
        }
714
715 12
        if (! is_array($result)) {
716 1
            return $result;
717
        }
718
719 12
        if (count($result) > 1) {
720 1
            throw new NonUniqueResultException;
721
        }
722
723 11
        return array_shift($result);
724
    }
725
726
    /**
727
     * Gets the single result of the query.
728
     *
729
     * Enforces the presence as well as the uniqueness of the result.
730
     *
731
     * If the result is not unique, a NonUniqueResultException is thrown.
732
     * If there is no result, a NoResultException is thrown.
733
     *
734
     * @param int $hydrationMode
735
     *
736
     * @return mixed
737
     *
738
     * @throws NonUniqueResultException If the query result is not unique.
739
     * @throws NoResultException        If the query returned no result
740
     */
741 99
    public function getSingleResult($hydrationMode = null)
742
    {
743 99
        $result = $this->execute(null, $hydrationMode);
744
745 93
        if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
746 2
            throw new NoResultException;
747
        }
748
749 92
        if (! is_array($result)) {
750 9
            return $result;
751
        }
752
753 84
        if (count($result) > 1) {
754 1
            throw new NonUniqueResultException;
755
        }
756
757 83
        return array_shift($result);
758
    }
759
760
    /**
761
     * Gets the single scalar result of the query.
762
     *
763
     * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
764
     *
765
     * @return mixed The scalar result, or NULL if the query returned no result.
766
     *
767
     * @throws NonUniqueResultException If the query result is not unique.
768
     * @throws NoResultException        If the query returned no result
769
     */
770 11
    public function getSingleScalarResult()
771
    {
772 11
        return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
773
    }
774
775
    /**
776
     * Sets a query hint. If the hint name is not recognized, it is silently ignored.
777
     *
778
     * @param string $name  The name of the hint.
779
     * @param mixed  $value The value of the hint.
780
     *
781
     * @return static This query instance.
782
     */
783 464
    public function setHint($name, $value)
784
    {
785 464
        $this->hints[$name] = $value;
786
787 464
        return $this;
788
    }
789
790
    /**
791
     * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
792
     *
793
     * @param string $name The name of the hint.
794
     *
795
     * @return mixed The value of the hint or FALSE, if the hint name is not recognized.
796
     */
797 795
    public function getHint($name)
798
    {
799 795
        return $this->hints[$name] ?? false;
800
    }
801
802
    /**
803
     * Check if the query has a hint
804
     *
805
     * @param string $name The name of the hint
806
     *
807
     * @return bool False if the query does not have any hint
808
     */
809 17
    public function hasHint($name)
810
    {
811 17
        return isset($this->hints[$name]);
812
    }
813
814
    /**
815
     * Return the key value map of query hints that are currently set.
816
     *
817
     * @return mixed[]
818
     */
819 136
    public function getHints()
820
    {
821 136
        return $this->hints;
822
    }
823
824
    /**
825
     * Executes the query and returns an IterableResult that can be used to incrementally
826
     * iterate over the result.
827
     *
828
     * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters    The query parameters.
829
     * @param int|null                                       $hydrationMode The hydration mode to use.
830
     *
831
     * @return \Doctrine\ORM\Internal\Hydration\IterableResult
832
     */
833 10
    public function iterate($parameters = null, $hydrationMode = null)
834
    {
835 10
        if ($hydrationMode !== null) {
836 10
            $this->setHydrationMode($hydrationMode);
837
        }
838
839 10
        if (! empty($parameters)) {
840 1
            $this->setParameters($parameters);
841
        }
842
843 10
        $rsm  = $this->getResultSetMapping();
844 7
        $stmt = $this->doExecute();
845
846 7
        return $this->em->newHydrator($this->hydrationMode)->iterate($stmt, $rsm, $this->hints);
847
    }
848
849
    /**
850
     * Executes the query.
851
     *
852
     * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters    Query parameters.
853
     * @param int|null                                       $hydrationMode Processing mode to be used during the hydration process.
854
     *
855
     * @return mixed
856
     */
857 471
    public function execute($parameters = null, $hydrationMode = null)
858
    {
859 471
        if ($this->cacheable && $this->isCacheEnabled()) {
860 29
            return $this->executeUsingQueryCache($parameters, $hydrationMode);
861
        }
862
863 444
        return $this->executeIgnoreQueryCache($parameters, $hydrationMode);
864
    }
865
866
    /**
867
     * Execute query ignoring second level cache.
868
     *
869
     * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters
870
     * @param int|null                                       $hydrationMode
871
     *
872
     * @return mixed
873
     */
874 471
    private function executeIgnoreQueryCache($parameters = null, $hydrationMode = null)
875
    {
876 471
        if ($hydrationMode !== null) {
877 370
            $this->setHydrationMode($hydrationMode);
878
        }
879
880 471
        if (! empty($parameters)) {
881
            $this->setParameters($parameters);
882
        }
883
884 471
        $setCacheEntry = function () {
885 471
        };
886
887 471
        if ($this->hydrationCacheProfile !== null) {
888 2
            list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
889
890 2
            $queryCacheProfile = $this->getHydrationCacheProfile();
891 2
            $cache             = $queryCacheProfile->getResultCacheDriver();
892 2
            $result            = $cache->fetch($cacheKey);
893
894 2
            if (isset($result[$realCacheKey])) {
895 2
                return $result[$realCacheKey];
896
            }
897
898 2
            if (! $result) {
899 2
                $result = [];
900
            }
901
902 2
            $setCacheEntry = function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
903 2
                $result[$realCacheKey] = $data;
904
905 2
                $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
906 2
            };
907
        }
908
909 471
        $stmt = $this->doExecute();
910
911 461
        if (is_numeric($stmt)) {
912 27
            $setCacheEntry($stmt);
913
914 27
            return $stmt;
915
        }
916
917 446
        $rsm  = $this->getResultSetMapping();
918 446
        $data = $this->em->newHydrator($this->hydrationMode)->hydrateAll($stmt, $rsm, $this->hints);
919
920 442
        $setCacheEntry($data);
921
922 442
        return $data;
923
    }
924
925
    /**
926
     * Load from second level cache or executes the query and put into cache.
927
     *
928
     * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters
929
     * @param int|null                                       $hydrationMode
930
     *
931
     * @return mixed
932
     */
933 29
    private function executeUsingQueryCache($parameters = null, $hydrationMode = null)
934
    {
935 29
        $rsm        = $this->getResultSetMapping();
936 29
        $queryCache = $this->em->getCache()->getQueryCache($this->cacheRegion);
937 29
        $queryKey   = new QueryCacheKey(
938 29
            $this->getHash(),
939 29
            $this->lifetime,
940 29
            $this->cacheMode ?: Cache::MODE_NORMAL,
941 29
            $this->getTimestampKey()
942
        );
943
944 29
        $result = $queryCache->get($queryKey, $rsm, $this->hints);
945
946 29
        if ($result !== null) {
947 16
            if ($this->cacheLogger) {
948 16
                $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
949
            }
950
951 16
            return $result;
952
        }
953
954 29
        $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode);
955 29
        $cached = $queryCache->put($queryKey, $rsm, $result, $this->hints);
956
957 26
        if ($this->cacheLogger) {
958 26
            $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);
959
960 26
            if ($cached) {
961 26
                $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
962
            }
963
        }
964
965 26
        return $result;
966
    }
967
968
    /**
969
     * @return \Doctrine\ORM\Cache\TimestampCacheKey|null
970
     */
971 29
    private function getTimestampKey()
972
    {
973 29
        $entityName = reset($this->resultSetMapping->aliasMap);
974
975 29
        if (empty($entityName)) {
976 2
            return null;
977
        }
978
979 27
        $metadata = $this->em->getClassMetadata($entityName);
980
981 27
        return new Cache\TimestampCacheKey($metadata->getRootClassName());
0 ignored issues
show
The method getRootClassName() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

981
        return new Cache\TimestampCacheKey($metadata->/** @scrutinizer ignore-call */ getRootClassName());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
982
    }
983
984
    /**
985
     * Get the result cache id to use to store the result set cache entry.
986
     * Will return the configured id if it exists otherwise a hash will be
987
     * automatically generated for you.
988
     *
989
     * @return string[] ($key, $hash)
990
     */
991 2
    protected function getHydrationCacheId()
992
    {
993 2
        $parameters = [];
994
995 2
        foreach ($this->getParameters() as $parameter) {
996 1
            $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
997
        }
998
999 2
        $sql                    = $this->getSQL();
1000 2
        $queryCacheProfile      = $this->getHydrationCacheProfile();
1001 2
        $hints                  = $this->getHints();
1002 2
        $hints['hydrationMode'] = $this->getHydrationMode();
1003
1004 2
        ksort($hints);
1005
1006 2
        return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints);
1007
    }
1008
1009
    /**
1010
     * Set the result cache id to use to store the result set cache entry.
1011
     * If this is not explicitly set by the developer then a hash is automatically
1012
     * generated for you.
1013
     *
1014
     * @param string $id
1015
     *
1016
     * @return static This query instance.
1017
     */
1018 12
    public function setResultCacheId($id)
1019
    {
1020 12
        $this->queryCacheProfile = $this->queryCacheProfile
1021 12
            ? $this->queryCacheProfile->setCacheKey($id)
1022
            : new QueryCacheProfile(0, $id, $this->em->getConfiguration()->getResultCacheImpl());
1023
1024 12
        return $this;
1025
    }
1026
1027
    /**
1028
     * Get the result cache id to use to store the result set cache entry if set.
1029
     *
1030
     * @deprecated
1031
     *
1032
     * @return string
1033
     */
1034
    public function getResultCacheId()
1035
    {
1036
        return $this->queryCacheProfile ? $this->queryCacheProfile->getCacheKey() : null;
1037
    }
1038
1039
    /**
1040
     * Executes the query and returns a the resulting Statement object.
1041
     *
1042
     * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
1043
     */
1044
    abstract protected function doExecute();
1045
1046
    /**
1047
     * Cleanup Query resource when clone is called.
1048
     */
1049 139
    public function __clone()
1050
    {
1051 139
        $this->parameters = new ArrayCollection();
1052 139
        $this->hints      = $this->em->getConfiguration()->getDefaultQueryHints();
1053 139
    }
1054
1055
    /**
1056
     * Generates a string of currently query to use for the cache second level cache.
1057
     *
1058
     * @return string
1059
     */
1060 29
    protected function getHash()
1061
    {
1062 29
        $query  = $this->getSQL();
1063 29
        $hints  = $this->getHints();
1064 29
        $params = array_map(function (Parameter $parameter) {
1065 5
            $value = $parameter->getValue();
1066
1067
            // Small optimization
1068
            // Does not invoke processParameterValue for scalar values
1069 5
            if (is_scalar($value)) {
1070 4
                return $value;
1071
            }
1072
1073 1
            return $this->processParameterValue($value);
1074 29
        }, $this->parameters->getValues());
1075
1076 29
        ksort($hints);
1077
1078 29
        return sha1($query . '-' . serialize($params) . '-' . serialize($hints));
1079
    }
1080
}
1081