Failed Conditions
Push — master ( 901929...f6cc12 )
by Jonathan
11:18
created

AbstractQuery::getHydrationCacheProfile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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

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