Passed
Pull Request — master (#7448)
by Ilya
14:31
created

Query::setQueryCacheLifetime()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
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\Cache\Cache;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Doctrine\ORM\Cache. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Doctrine\DBAL\LockMode;
10
use Doctrine\ORM\Internal\Hydration\IterableResult;
11
use Doctrine\ORM\Mapping\ClassMetadata;
12
use Doctrine\ORM\Query\AST\DeleteStatement;
13
use Doctrine\ORM\Query\AST\SelectStatement;
14
use Doctrine\ORM\Query\AST\UpdateStatement;
15
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
16
use Doctrine\ORM\Query\Parameter;
17
use Doctrine\ORM\Query\ParameterTypeInferer;
18
use Doctrine\ORM\Query\Parser;
19
use Doctrine\ORM\Query\ParserResult;
20
use Doctrine\ORM\Query\QueryException;
21
use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
22
use function array_keys;
23
use function array_values;
24
use function count;
25
use function in_array;
26
use function ksort;
27
use function md5;
28
use function reset;
29
use function serialize;
30
use function sha1;
31
use function stripos;
32
33
/**
34
 * A Query object represents a DQL query.
35
 */
36
final class Query extends AbstractQuery
37
{
38
    /**
39
     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
40
     */
41
    public const STATE_CLEAN = 1;
42
43
    /**
44
     * A query object is in state DIRTY when it has DQL parts that have not yet been
45
     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
46
     * is called.
47
     */
48
    public const STATE_DIRTY = 2;
49
50
    /* Query HINTS */
51
52
    /**
53
     * The refresh hint turns any query into a refresh query with the result that
54
     * any local changes in entities are overridden with the fetched values.
55
     */
56
    public const HINT_REFRESH = 'doctrine.refresh';
57
58
    public const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
59
60
    public const HINT_CACHE_EVICT = 'doctrine.cache.evict';
61
62
    /**
63
     * Internal hint: is set to the proxy entity that is currently triggered for loading
64
     */
65
    public const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
66
67
    /**
68
     * The forcePartialLoad query hint forces a particular query to return
69
     * partial objects.
70
     *
71
     * @todo Rename: HINT_OPTIMIZE
72
     */
73
    public const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
74
75
    /**
76
     * The includeMetaColumns query hint causes meta columns like foreign keys and
77
     * discriminator columns to be selected and returned as part of the query result.
78
     *
79
     * This hint does only apply to non-object queries.
80
     */
81
    public const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
82
83
    /**
84
     * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
85
     * are iterated and executed after the DQL has been parsed into an AST.
86
     */
87
    public const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
88
89
    /**
90
     * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
91
     * and is used for generating the target SQL from any DQL AST tree.
92
     */
93
    public const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
94
95
    //const HINT_READ_ONLY = 'doctrine.readOnly';
96
97
    public const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
98
99
    public const HINT_LOCK_MODE = 'doctrine.lockMode';
100
101
    /**
102
     * The current state of this query.
103
     *
104
     * @var int
105
     */
106
    private $state = self::STATE_CLEAN;
107
108
    /**
109
     * A snapshot of the parameter types the query was parsed with.
110
     *
111
     * @var mixed[]
112
     */
113
    private $parsedTypes = [];
114
115
    /**
116
     * Cached DQL query.
117
     *
118
     * @var string
119
     */
120
    private $dql;
121
122
    /**
123
     * The parser result that holds DQL => SQL information.
124
     *
125
     * @var ParserResult
126
     */
127
    private $parserResult;
128
129
    /**
130
     * The first result to return (the "offset").
131
     *
132
     * @var int
133
     */
134
    private $firstResult;
135
136
    /**
137
     * The maximum number of results to return (the "limit").
138
     *
139
     * @var int|null
140
     */
141
    private $maxResults;
142
143
    /**
144
     * The cache driver used for caching queries.
145
     *
146
     * @var Cache|null
147
     */
148
    private $queryCache;
149
150
    /**
151
     * Whether or not expire the query cache.
152
     *
153
     * @var bool
154
     */
155
    private $expireQueryCache = false;
156
157
    /**
158
     * The query cache lifetime.
159
     *
160
     * @var int
161
     */
162
    private $queryCacheTTL;
163
164
    /**
165
     * Whether to use a query cache, if available. Defaults to TRUE.
166
     *
167
     * @var bool
168
     */
169
    private $useQueryCache = true;
170
171
    /**
172
     * Gets the SQL query/queries that correspond to this DQL query.
173
     *
174
     * @return mixed The built sql query or an array of all sql queries.
175
     *
176
     * @override
177
     */
178 342
    public function getSQL()
179
    {
180 342
        return $this->parse()->getSqlExecutor()->getSqlStatements();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->parse()->g...r()->getSqlStatements() returns the type string[] which is incompatible with the return type mandated by Doctrine\ORM\AbstractQuery::getSQL() of string.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
181
    }
182
183
    /**
184
     * Returns the corresponding AST for this DQL query.
185
     *
186
     * @return SelectStatement|UpdateStatement|DeleteStatement
187
     */
188 2
    public function getAST()
189
    {
190 2
        $parser = new Parser($this);
191
192 2
        return $parser->getAST();
193
    }
194
195
    /**
196
     * {@inheritdoc}
197
     */
198 451
    protected function getResultSetMapping()
199
    {
200
        // parse query or load from cache
201 451
        if ($this->resultSetMapping === null) {
202 38
            $this->resultSetMapping = $this->parse()->getResultSetMapping();
203
        }
204
205 448
        return $this->resultSetMapping;
206
    }
207
208
    /**
209
     * Parses the DQL query, if necessary, and stores the parser result.
210
     *
211
     * Note: Populates $this->parserResult as a side-effect.
212
     *
213
     * @return ParserResult
214
     */
215 774
    private function parse()
216
    {
217 774
        $types = [];
218
219 774
        foreach ($this->parameters as $parameter) {
220
            /** @var Query\Parameter $parameter */
221 177
            $types[$parameter->getName()] = $parameter->getType();
222
        }
223
224
        // Return previous parser result if the query and the filter collection are both clean
225 774
        if ($this->state === self::STATE_CLEAN && $this->parsedTypes === $types && $this->em->isFiltersStateClean()) {
226 39
            return $this->parserResult;
227
        }
228
229 774
        $this->state       = self::STATE_CLEAN;
230 774
        $this->parsedTypes = $types;
231
232
        // Check query cache.
233 774
        $queryCache = $this->getQueryCacheDriver();
234 774
        if (! ($this->useQueryCache && $queryCache)) {
235 181
            $parser = new Parser($this);
236
237 181
            $this->parserResult = $parser->parse();
238
239 177
            return $this->parserResult;
240
        }
241
242 593
        $hash   = $this->getQueryCacheId();
243 593
        $cached = $this->expireQueryCache ? false : $queryCache->fetch($hash);
244
245 593
        if ($cached instanceof ParserResult) {
246
            // Cache hit.
247 120
            $this->parserResult = $cached;
248
249 120
            return $this->parserResult;
250
        }
251
252
        // Cache miss.
253 541
        $parser = new Parser($this);
254
255 541
        $this->parserResult = $parser->parse();
256
257 525
        $queryCache->save($hash, $this->parserResult, $this->queryCacheTTL);
258
259 525
        return $this->parserResult;
260
    }
261
262
    /**
263
     * {@inheritdoc}
264
     */
265 464
    protected function doExecute()
266
    {
267 464
        $executor = $this->parse()->getSqlExecutor();
268
269 459
        if ($this->queryCacheProfile) {
270 8
            $executor->setQueryCacheProfile($this->queryCacheProfile);
271
        } else {
272 453
            $executor->removeQueryCacheProfile();
273
        }
274
275 459
        if ($this->resultSetMapping === null) {
276 417
            $this->resultSetMapping = $this->parserResult->getResultSetMapping();
277
        }
278
279
        // Prepare parameters
280 459
        $paramMappings = $this->parserResult->getParameterMappings();
281 459
        $paramCount    = count($this->parameters);
282 459
        $mappingCount  = count($paramMappings);
283
284 459
        if ($paramCount > $mappingCount) {
285 2
            throw QueryException::tooManyParameters($mappingCount, $paramCount);
286
        }
287
288 458
        if ($paramCount < $mappingCount) {
289 1
            throw QueryException::tooFewParameters($mappingCount, $paramCount);
290
        }
291
292
        // evict all cache for the entity region
293 457
        if ($this->hasCache && isset($this->hints[self::HINT_CACHE_EVICT]) && $this->hints[self::HINT_CACHE_EVICT]) {
294 2
            $this->evictEntityCacheRegion();
295
        }
296
297 457
        [$sqlParams, $types] = $this->processParameterMappings($paramMappings);
298
299 456
        $this->evictResultSetCache(
300 456
            $executor,
301 456
            $sqlParams,
302 456
            $types,
303 456
            $this->em->getConnection()->getParams()
304
        );
305
306 456
        return $executor->execute($this->em->getConnection(), $sqlParams, $types);
307
    }
308
309
    /**
310
     * @param mixed[] $sqlParams
311
     * @param mixed[] $types
312
     * @param mixed[] $connectionParams
313
     */
314 456
    private function evictResultSetCache(
315
        AbstractSqlExecutor $executor,
316
        array $sqlParams,
317
        array $types,
318
        array $connectionParams
319
    ) {
320 456
        if ($this->queryCacheProfile === null || ! $this->getExpireResultCache()) {
321 456
            return;
322
        }
323
324 2
        $cacheDriver = $this->queryCacheProfile->getResultCacheDriver();
325 2
        $statements  = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array
326
327 2
        foreach ($statements as $statement) {
328 2
            $cacheKeys = $this->queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams);
329
330 2
            $cacheDriver->delete(reset($cacheKeys));
331
        }
332 2
    }
333
334
    /**
335
     * Evict entity cache region
336
     */
337 2
    private function evictEntityCacheRegion()
338
    {
339 2
        $AST = $this->getAST();
340
341 2
        if ($AST instanceof SelectStatement) {
342
            throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
343
        }
344
345 2
        $className = $AST instanceof DeleteStatement
346 1
            ? $AST->deleteClause->abstractSchemaName
347 2
            : $AST->updateClause->abstractSchemaName;
348
349 2
        $this->em->getCache()->evictEntityRegion($className);
350 2
    }
351
352
    /**
353
     * Processes query parameter mappings.
354
     *
355
     * @param mixed[] $paramMappings
356
     *
357
     * @return mixed[][]
358
     *
359
     * @throws Query\QueryException
360
     */
361 457
    private function processParameterMappings($paramMappings)
362
    {
363 457
        $sqlParams = [];
364 457
        $types     = [];
365
366 457
        foreach ($this->parameters as $parameter) {
367 164
            $key   = $parameter->getName();
368 164
            $value = $parameter->getValue();
369 164
            $rsm   = $this->getResultSetMapping();
370
371 164
            if (! isset($paramMappings[$key])) {
372 1
                throw QueryException::unknownParameter($key);
373
            }
374
375 163
            if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
376
                $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
0 ignored issues
show
Bug introduced by
The method getMetadataValue() does not exist on Doctrine\ORM\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

376
                /** @scrutinizer ignore-call */ 
377
                $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);

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...
377
            }
378
379 163
            if (isset($rsm->discriminatorParameters[$key]) && $value instanceof ClassMetadata) {
380 3
                $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->em));
381
            }
382
383 163
            $value = $this->processParameterValue($value);
384 163
            $type  = $parameter->getValue() === $value
385 152
                ? $parameter->getType()
386 163
                : ParameterTypeInferer::inferType($value);
387
388 163
            foreach ($paramMappings[$key] as $position) {
389 163
                $types[$position] = $type;
390
            }
391
392 163
            $sqlPositions      = $paramMappings[$key];
393 163
            $sqlPositionsCount = count($sqlPositions);
394
395
            // optimized multi value sql positions away for now,
396
            // they are not allowed in DQL anyways.
397 163
            $value      = [$value];
398 163
            $countValue = count($value);
399
400 163
            for ($i = 0, $l = $sqlPositionsCount; $i < $l; $i++) {
401 163
                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
402
            }
403
        }
404
405 456
        if (count($sqlParams) !== count($types)) {
406
            throw QueryException::parameterTypeMismatch();
407
        }
408
409 456
        if ($sqlParams) {
410 163
            ksort($sqlParams);
411 163
            $sqlParams = array_values($sqlParams);
412
413 163
            ksort($types);
414 163
            $types = array_values($types);
415
        }
416
417 456
        return [$sqlParams, $types];
418
    }
419
420
    /**
421
     * Defines a cache driver to be used for caching queries.
422
     *
423
     * @param Cache|null $queryCache Cache driver.
424
     *
425
     * @return Query This query instance.
426
     */
427 6
    public function setQueryCacheDriver($queryCache)
428
    {
429 6
        $this->queryCache = $queryCache;
430
431 6
        return $this;
432
    }
433
434
    /**
435
     * Defines whether the query should make use of a query cache, if available.
436
     *
437
     * @param bool $bool
438
     *
439
     * @return Query This query instance.
440
     */
441 184
    public function useQueryCache($bool)
442
    {
443 184
        $this->useQueryCache = $bool;
444
445 184
        return $this;
446
    }
447
448
    /**
449
     * Returns the cache driver used for query caching.
450
     *
451
     * @return Cache|null The cache driver used for query caching or NULL, if
452
     *                                           this Query does not use query caching.
453
     */
454 774
    public function getQueryCacheDriver()
455
    {
456 774
        if ($this->queryCache) {
457 9
            return $this->queryCache;
458
        }
459
460 765
        return $this->em->getConfiguration()->getQueryCacheImpl();
461
    }
462
463
    /**
464
     * Defines how long the query cache will be active before expire.
465
     *
466
     * @param int $timeToLive How long the cache entry is valid.
467
     *
468
     * @return Query This query instance.
469
     */
470 1
    public function setQueryCacheLifetime($timeToLive)
471
    {
472 1
        if ($timeToLive !== null) {
0 ignored issues
show
introduced by
The condition $timeToLive !== null is always true.
Loading history...
473 1
            $timeToLive = (int) $timeToLive;
474
        }
475
476 1
        $this->queryCacheTTL = $timeToLive;
477
478 1
        return $this;
479
    }
480
481
    /**
482
     * Retrieves the lifetime of resultset cache.
483
     *
484
     * @return int
485
     */
486
    public function getQueryCacheLifetime()
487
    {
488
        return $this->queryCacheTTL;
489
    }
490
491
    /**
492
     * Defines if the query cache is active or not.
493
     *
494
     * @param bool $expire Whether or not to force query cache expiration.
495
     *
496
     * @return Query This query instance.
497
     */
498 7
    public function expireQueryCache($expire = true)
499
    {
500 7
        $this->expireQueryCache = $expire;
501
502 7
        return $this;
503
    }
504
505
    /**
506
     * Retrieves if the query cache is active or not.
507
     *
508
     * @return bool
509
     */
510
    public function getExpireQueryCache()
511
    {
512
        return $this->expireQueryCache;
513
    }
514
515 220
    public function free()
516
    {
517 220
        parent::free();
518
519 220
        $this->dql   = null;
520 220
        $this->state = self::STATE_CLEAN;
521 220
    }
522
523
    /**
524
     * Sets a DQL query string.
525
     *
526
     * @param string $dqlQuery DQL Query.
527
     *
528
     * @return AbstractQuery
529
     */
530 957
    public function setDQL($dqlQuery)
531
    {
532 957
        if ($dqlQuery !== null) {
0 ignored issues
show
introduced by
The condition $dqlQuery !== null is always true.
Loading history...
533 957
            $this->dql   = $dqlQuery;
534 957
            $this->state = self::STATE_DIRTY;
535
        }
536
537 957
        return $this;
538
    }
539
540
    /**
541
     * Returns the DQL query that is represented by this query object.
542
     *
543
     * @return string DQL query.
544
     */
545 904
    public function getDQL()
546
    {
547 904
        return $this->dql;
548
    }
549
550
    /**
551
     * Returns the state of this query object
552
     * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
553
     * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
554
     *
555
     * @see AbstractQuery::STATE_CLEAN
556
     * @see AbstractQuery::STATE_DIRTY
557
     *
558
     * @return int The query state.
559
     */
560
    public function getState()
561
    {
562
        return $this->state;
563
    }
564
565
    /**
566
     * Method to check if an arbitrary piece of DQL exists
567
     *
568
     * @param string $dql Arbitrary piece of DQL to check for.
569
     *
570
     * @return bool
571
     */
572
    public function contains($dql)
573
    {
574
        return stripos($this->getDQL(), $dql) !== false;
575
    }
576
577
    /**
578
     * Sets the position of the first result to retrieve (the "offset").
579
     *
580
     * @param int $firstResult The first result to return.
581
     *
582
     * @return Query This query object.
583
     */
584 221
    public function setFirstResult($firstResult)
585
    {
586 221
        $this->firstResult = $firstResult;
587 221
        $this->state       = self::STATE_DIRTY;
588
589 221
        return $this;
590
    }
591
592
    /**
593
     * Gets the position of the first result the query object was set to retrieve (the "offset").
594
     * Returns NULL if {@link setFirstResult} was not applied to this query.
595
     *
596
     * @return int The position of the first result.
597
     */
598 663
    public function getFirstResult()
599
    {
600 663
        return $this->firstResult;
601
    }
602
603
    /**
604
     * Sets the maximum number of results to retrieve (the "limit").
605
     *
606
     * @param int|null $maxResults
607
     *
608
     * @return Query This query object.
609
     */
610 245
    public function setMaxResults($maxResults)
611
    {
612 245
        $this->maxResults = $maxResults;
613 245
        $this->state      = self::STATE_DIRTY;
614
615 245
        return $this;
616
    }
617
618
    /**
619
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
620
     * Returns NULL if {@link setMaxResults} was not applied to this query.
621
     *
622
     * @return int|null Maximum number of results.
623
     */
624 663
    public function getMaxResults()
625
    {
626 663
        return $this->maxResults;
627
    }
628
629
    /**
630
     * Executes the query and returns an IterableResult that can be used to incrementally
631
     * iterated over the result.
632
     *
633
     * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters    The query parameters.
634
     * @param int                                            $hydrationMode The hydration mode to use.
635
     *
636
     * @return IterableResult
637
     */
638 10
    public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT)
639
    {
640 10
        $this->setHint(self::HINT_INTERNAL_ITERATION, true);
641
642 10
        return parent::iterate($parameters, $hydrationMode);
643
    }
644
645
    /**
646
     * {@inheritdoc}
647
     */
648 466
    public function setHint($name, $value)
649
    {
650 466
        $this->state = self::STATE_DIRTY;
651
652 466
        return parent::setHint($name, $value);
653
    }
654
655
    /**
656
     * {@inheritdoc}
657
     */
658 362
    public function setHydrationMode($hydrationMode)
659
    {
660 362
        $this->state = self::STATE_DIRTY;
661
662 362
        return parent::setHydrationMode($hydrationMode);
663
    }
664
665
    /**
666
     * Set the lock mode for this Query.
667
     *
668
     * @see \Doctrine\DBAL\LockMode
669
     *
670
     * @param int $lockMode
671
     *
672
     * @return Query
673
     *
674
     * @throws TransactionRequiredException
675
     */
676
    public function setLockMode($lockMode)
677
    {
678
        if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) {
679
            if (! $this->em->getConnection()->isTransactionActive()) {
680
                throw TransactionRequiredException::transactionRequired();
681
            }
682
        }
683
684
        $this->setHint(self::HINT_LOCK_MODE, $lockMode);
685
686
        return $this;
687
    }
688
689
    /**
690
     * Get the current lock mode for this query.
691
     *
692
     * @return int|null The current lock mode of this query or NULL if no specific lock mode is set.
693
     */
694
    public function getLockMode()
695
    {
696
        $lockMode = $this->getHint(self::HINT_LOCK_MODE);
697
698
        if ($lockMode === false) {
699
            return null;
700
        }
701
702
        return $lockMode;
703
    }
704
705
    /**
706
     * Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
707
     *
708
     * @return string
709
     */
710 593
    protected function getQueryCacheId()
711
    {
712 593
        ksort($this->hints);
713
714 593
        $platform = $this->getEntityManager()
715 593
            ->getConnection()
716 593
            ->getDatabasePlatform()
717 593
            ->getName();
718
719 593
        return md5(
720 593
            $this->getDQL() . serialize($this->hints) .
721 593
            '&platform=' . $platform .
722 593
            ($this->em->hasFilters() ? $this->em->getFilters()->getHash() : '') .
723 593
            '&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults .
724 593
            '&hydrationMode=' . $this->hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT'
725
        );
726
    }
727
728
    /**
729
     * {@inheritdoc}
730
     */
731 28
    protected function getHash()
732
    {
733 28
        return sha1(parent::getHash() . '-' . $this->firstResult . '-' . $this->maxResults);
734
    }
735
736
    /**
737
     * Cleanup Query resource when clone is called.
738
     */
739 142
    public function __clone()
740
    {
741 142
        parent::__clone();
742
743 142
        $this->state = self::STATE_DIRTY;
744 142
    }
745
}
746