Failed Conditions
Pull Request — develop (#6935)
by Michael
167:08 queued 149:28
created

AbstractQuery::setResultCacheProfile()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.3332

Importance

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

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

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