Failed Conditions
Push — 2.7 ( c036c0...266f0d )
by Jonathan
57:23 queued 50:07
created

lib/Doctrine/ORM/Query.php (1 issue)

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM;
21
22
use Doctrine\DBAL\LockMode;
23
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
24
use Doctrine\ORM\Query\Parser;
25
use Doctrine\ORM\Query\ParserResult;
26
use Doctrine\ORM\Query\QueryException;
27
use Doctrine\ORM\Mapping\ClassMetadata;
28
use Doctrine\ORM\Query\ParameterTypeInferer;
29
use Doctrine\Common\Collections\ArrayCollection;
30
use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
31
32
/**
33
 * A Query object represents a DQL query.
34
 *
35
 * @since   1.0
36
 * @author  Guilherme Blanco <[email protected]>
37
 * @author  Konsta Vesterinen <[email protected]>
38
 * @author  Roman Borschel <[email protected]>
39
 */
40
final class Query extends AbstractQuery
41
{
42
    /**
43
     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
44
     */
45
    const STATE_CLEAN  = 1;
46
47
    /**
48
     * A query object is in state DIRTY when it has DQL parts that have not yet been
49
     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
50
     * is called.
51
     */
52
    const STATE_DIRTY = 2;
53
54
    /* Query HINTS */
55
56
    /**
57
     * The refresh hint turns any query into a refresh query with the result that
58
     * any local changes in entities are overridden with the fetched values.
59
     *
60
     * @var string
61
     */
62
    const HINT_REFRESH = 'doctrine.refresh';
63
64
    /**
65
     * @var string
66
     */
67
    const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
68
69
    /**
70
     * @var string
71
     */
72
    const HINT_CACHE_EVICT = 'doctrine.cache.evict';
73
74
    /**
75
     * Internal hint: is set to the proxy entity that is currently triggered for loading
76
     *
77
     * @var string
78
     */
79
    const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
80
81
    /**
82
     * The forcePartialLoad query hint forces a particular query to return
83
     * partial objects.
84
     *
85
     * @var string
86
     * @todo Rename: HINT_OPTIMIZE
87
     */
88
    const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
89
90
    /**
91
     * The includeMetaColumns query hint causes meta columns like foreign keys and
92
     * discriminator columns to be selected and returned as part of the query result.
93
     *
94
     * This hint does only apply to non-object queries.
95
     *
96
     * @var string
97
     */
98
    const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
99
100
    /**
101
     * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
102
     * are iterated and executed after the DQL has been parsed into an AST.
103
     *
104
     * @var string
105
     */
106
    const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
107
108
    /**
109
     * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
110
     * and is used for generating the target SQL from any DQL AST tree.
111
     *
112
     * @var string
113
     */
114
    const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
115
116
    //const HINT_READ_ONLY = 'doctrine.readOnly';
117
118
    /**
119
     * @var string
120
     */
121
    const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
122
123
    /**
124
     * @var string
125
     */
126
    const HINT_LOCK_MODE = 'doctrine.lockMode';
127
128
    /**
129
     * The current state of this query.
130
     *
131
     * @var integer
132
     */
133
    private $_state = self::STATE_CLEAN;
134
135
    /**
136
     * A snapshot of the parameter types the query was parsed with.
137
     *
138
     * @var array
139
     */
140
    private $_parsedTypes = [];
141
142
    /**
143
     * Cached DQL query.
144
     *
145
     * @var string
146
     */
147
    private $_dql = null;
148
149
    /**
150
     * The parser result that holds DQL => SQL information.
151
     *
152
     * @var \Doctrine\ORM\Query\ParserResult
153
     */
154
    private $_parserResult;
155
156
    /**
157
     * The first result to return (the "offset").
158
     *
159
     * @var integer
160
     */
161
    private $_firstResult = null;
162
163
    /**
164
     * The maximum number of results to return (the "limit").
165
     *
166
     * @var integer|null
167
     */
168
    private $_maxResults = null;
169
170
    /**
171
     * The cache driver used for caching queries.
172
     *
173
     * @var \Doctrine\Common\Cache\Cache|null
174
     */
175
    private $_queryCache;
176
177
    /**
178
     * Whether or not expire the query cache.
179
     *
180
     * @var boolean
181
     */
182
    private $_expireQueryCache = false;
183
184
    /**
185
     * The query cache lifetime.
186
     *
187
     * @var int
188
     */
189
    private $_queryCacheTTL;
190
191
    /**
192
     * Whether to use a query cache, if available. Defaults to TRUE.
193
     *
194
     * @var boolean
195
     */
196
    private $_useQueryCache = true;
197
198
    /**
199
     * Gets the SQL query/queries that correspond to this DQL query.
200
     *
201
     * @return mixed The built sql query or an array of all sql queries.
202
     *
203
     * @override
204
     */
205 340
    public function getSQL()
206
    {
207 340
        return $this->_parse()->getSqlExecutor()->getSqlStatements();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_parse()->...r()->getSqlStatements() returns the type array 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...
208
    }
209
210
    /**
211
     * Returns the corresponding AST for this DQL query.
212
     *
213
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
214
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
215
     *         \Doctrine\ORM\Query\AST\DeleteStatement
216
     */
217 2
    public function getAST()
218
    {
219 2
        $parser = new Parser($this);
220
221 2
        return $parser->getAST();
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227 462
    protected function getResultSetMapping()
228
    {
229
        // parse query or load from cache
230 462
        if ($this->_resultSetMapping === null) {
231 38
            $this->_resultSetMapping = $this->_parse()->getResultSetMapping();
232
        }
233
234 459
        return $this->_resultSetMapping;
235
    }
236
237
    /**
238
     * Parses the DQL query, if necessary, and stores the parser result.
239
     *
240
     * Note: Populates $this->_parserResult as a side-effect.
241
     *
242
     * @return \Doctrine\ORM\Query\ParserResult
243
     */
244 785
    private function _parse()
245
    {
246 785
        $types = [];
247
248 785
        foreach ($this->parameters as $parameter) {
249
            /** @var Query\Parameter $parameter */
250 182
            $types[$parameter->getName()] = $parameter->getType();
251
        }
252
253
        // Return previous parser result if the query and the filter collection are both clean
254 785
        if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) {
255 39
            return $this->_parserResult;
256
        }
257
258 785
        $this->_state = self::STATE_CLEAN;
259 785
        $this->_parsedTypes = $types;
260
261
        // Check query cache.
262 785
        if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
263 172
            $parser = new Parser($this);
264
265 172
            $this->_parserResult = $parser->parse();
266
267 168
            return $this->_parserResult;
268
        }
269
270 613
        $hash   = $this->_getQueryCacheId();
271 613
        $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
272
273 613
        if ($cached instanceof ParserResult) {
274
            // Cache hit.
275 124
            $this->_parserResult = $cached;
276
277 124
            return $this->_parserResult;
278
        }
279
280
        // Cache miss.
281 560
        $parser = new Parser($this);
282
283 560
        $this->_parserResult = $parser->parse();
284
285 542
        $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
286
287 542
        return $this->_parserResult;
288
    }
289
290
    /**
291
     * {@inheritdoc}
292
     */
293 477
    protected function _doExecute()
294
    {
295 477
        $executor = $this->_parse()->getSqlExecutor();
296
297 470
        if ($this->_queryCacheProfile) {
298 8
            $executor->setQueryCacheProfile($this->_queryCacheProfile);
299
        } else {
300 464
            $executor->removeQueryCacheProfile();
301
        }
302
303 470
        if ($this->_resultSetMapping === null) {
304 428
            $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
305
        }
306
307
        // Prepare parameters
308 470
        $paramMappings = $this->_parserResult->getParameterMappings();
309 470
        $paramCount = count($this->parameters);
310 470
        $mappingCount = count($paramMappings);
311
312 470
        if ($paramCount > $mappingCount) {
313 2
            throw QueryException::tooManyParameters($mappingCount, $paramCount);
314
        }
315
316 469
        if ($paramCount < $mappingCount) {
317 1
            throw QueryException::tooFewParameters($mappingCount, $paramCount);
318
        }
319
320
        // evict all cache for the entity region
321 468
        if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
322 2
            $this->evictEntityCacheRegion();
323
        }
324
325 468
        list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
326
327 467
        $this->evictResultSetCache(
328 467
            $executor,
329 467
            $sqlParams,
330 467
            $types,
331 467
            $this->_em->getConnection()->getParams()
332
        );
333
334 467
        return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
335
    }
336
337 467
    private function evictResultSetCache(
338
        AbstractSqlExecutor $executor,
339
        array $sqlParams,
340
        array $types,
341
        array $connectionParams
342
    ) {
343 467
        if (null === $this->_queryCacheProfile || ! $this->getExpireResultCache()) {
344 467
            return;
345
        }
346
347 2
        $cacheDriver = $this->_queryCacheProfile->getResultCacheDriver();
348 2
        $statements  = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array
349
350 2
        foreach ($statements as $statement) {
351 2
            $cacheKeys = $this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams);
352
353 2
            $cacheDriver->delete(reset($cacheKeys));
354
        }
355 2
    }
356
357
    /**
358
     * Evict entity cache region
359
     */
360 2
    private function evictEntityCacheRegion()
361
    {
362 2
        $AST = $this->getAST();
363
364 2
        if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) {
365
            throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
366
        }
367
368 2
        $className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement)
369 1
            ? $AST->deleteClause->abstractSchemaName
370 2
            : $AST->updateClause->abstractSchemaName;
371
372 2
        $this->_em->getCache()->evictEntityRegion($className);
373 2
    }
374
375
    /**
376
     * Processes query parameter mappings.
377
     *
378
     * @param array $paramMappings
379
     *
380
     * @return array
381
     *
382
     * @throws Query\QueryException
383
     */
384 468
    private function processParameterMappings($paramMappings)
385
    {
386 468
        $sqlParams = [];
387 468
        $types     = [];
388
389 468
        foreach ($this->parameters as $parameter) {
390 169
            $key    = $parameter->getName();
391 169
            $value  = $parameter->getValue();
392 169
            $rsm    = $this->getResultSetMapping();
393
394 169
            if ( ! isset($paramMappings[$key])) {
395 1
                throw QueryException::unknownParameter($key);
396
            }
397
398 168
            if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
399
                $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
400
            }
401
402 168
            if (isset($rsm->discriminatorParameters[$key]) && $value instanceof ClassMetadata) {
403 3
                $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em));
404
            }
405
406 168
            $value = $this->processParameterValue($value);
407 168
            $type  = ($parameter->getValue() === $value)
408 156
                ? $parameter->getType()
409 168
                : ParameterTypeInferer::inferType($value);
410
411 168
            foreach ($paramMappings[$key] as $position) {
412 168
                $types[$position] = $type;
413
            }
414
415 168
            $sqlPositions = $paramMappings[$key];
416
417
            // optimized multi value sql positions away for now,
418
            // they are not allowed in DQL anyways.
419 168
            $value = [$value];
420 168
            $countValue = count($value);
421
422 168
            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
423 168
                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
424
            }
425
        }
426
427 467
        if (count($sqlParams) != count($types)) {
428
            throw QueryException::parameterTypeMismatch();
429
        }
430
431 467
        if ($sqlParams) {
432 168
            ksort($sqlParams);
433 168
            $sqlParams = array_values($sqlParams);
434
435 168
            ksort($types);
436 168
            $types = array_values($types);
437
        }
438
439 467
        return [$sqlParams, $types];
440
    }
441
442
    /**
443
     * Defines a cache driver to be used for caching queries.
444
     *
445
     * @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver.
446
     *
447
     * @return Query This query instance.
448
     */
449 6
    public function setQueryCacheDriver($queryCache)
450
    {
451 6
        $this->_queryCache = $queryCache;
452
453 6
        return $this;
454
    }
455
456
    /**
457
     * Defines whether the query should make use of a query cache, if available.
458
     *
459
     * @param boolean $bool
460
     *
461
     * @return Query This query instance.
462
     */
463 175
    public function useQueryCache($bool)
464
    {
465 175
        $this->_useQueryCache = $bool;
466
467 175
        return $this;
468
    }
469
470
    /**
471
     * Returns the cache driver used for query caching.
472
     *
473
     * @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if
474
     *                                           this Query does not use query caching.
475
     */
476 613
    public function getQueryCacheDriver()
477
    {
478 613
        if ($this->_queryCache) {
479 9
            return $this->_queryCache;
480
        }
481
482 604
        return $this->_em->getConfiguration()->getQueryCacheImpl();
483
    }
484
485
    /**
486
     * Defines how long the query cache will be active before expire.
487
     *
488
     * @param integer $timeToLive How long the cache entry is valid.
489
     *
490
     * @return Query This query instance.
491
     */
492 1
    public function setQueryCacheLifetime($timeToLive)
493
    {
494 1
        if ($timeToLive !== null) {
495 1
            $timeToLive = (int) $timeToLive;
496
        }
497
498 1
        $this->_queryCacheTTL = $timeToLive;
499
500 1
        return $this;
501
    }
502
503
    /**
504
     * Retrieves the lifetime of resultset cache.
505
     *
506
     * @return int
507
     */
508
    public function getQueryCacheLifetime()
509
    {
510
        return $this->_queryCacheTTL;
511
    }
512
513
    /**
514
     * Defines if the query cache is active or not.
515
     *
516
     * @param boolean $expire Whether or not to force query cache expiration.
517
     *
518
     * @return Query This query instance.
519
     */
520 7
    public function expireQueryCache($expire = true)
521
    {
522 7
        $this->_expireQueryCache = $expire;
523
524 7
        return $this;
525
    }
526
527
    /**
528
     * Retrieves if the query cache is active or not.
529
     *
530
     * @return bool
531
     */
532
    public function getExpireQueryCache()
533
    {
534
        return $this->_expireQueryCache;
535
    }
536
537
    /**
538
     * @override
539
     */
540 212
    public function free()
541
    {
542 212
        parent::free();
543
544 212
        $this->_dql = null;
545 212
        $this->_state = self::STATE_CLEAN;
546 212
    }
547
548
    /**
549
     * Sets a DQL query string.
550
     *
551
     * @param string $dqlQuery DQL Query.
552
     *
553
     * @return \Doctrine\ORM\AbstractQuery
554
     */
555 978
    public function setDQL($dqlQuery)
556
    {
557 978
        if ($dqlQuery !== null) {
558 978
            $this->_dql = $dqlQuery;
559 978
            $this->_state = self::STATE_DIRTY;
560
        }
561
562 978
        return $this;
563
    }
564
565
    /**
566
     * Returns the DQL query that is represented by this query object.
567
     *
568
     * @return string DQL query.
569
     */
570 923
    public function getDQL()
571
    {
572 923
        return $this->_dql;
573
    }
574
575
    /**
576
     * Returns the state of this query object
577
     * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
578
     * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
579
     *
580
     * @see AbstractQuery::STATE_CLEAN
581
     * @see AbstractQuery::STATE_DIRTY
582
     *
583
     * @return integer The query state.
584
     */
585
    public function getState()
586
    {
587
        return $this->_state;
588
    }
589
590
    /**
591
     * Method to check if an arbitrary piece of DQL exists
592
     *
593
     * @param string $dql Arbitrary piece of DQL to check for.
594
     *
595
     * @return boolean
596
     */
597
    public function contains($dql)
598
    {
599
        return stripos($this->getDQL(), $dql) !== false;
600
    }
601
602
    /**
603
     * Sets the position of the first result to retrieve (the "offset").
604
     *
605
     * @param integer $firstResult The first result to return.
606
     *
607
     * @return Query This query object.
608
     */
609 223
    public function setFirstResult($firstResult)
610
    {
611 223
        $this->_firstResult = $firstResult;
612 223
        $this->_state       = self::STATE_DIRTY;
613
614 223
        return $this;
615
    }
616
617
    /**
618
     * Gets the position of the first result the query object was set to retrieve (the "offset").
619
     * Returns NULL if {@link setFirstResult} was not applied to this query.
620
     *
621
     * @return integer The position of the first result.
622
     */
623 670
    public function getFirstResult()
624
    {
625 670
        return $this->_firstResult;
626
    }
627
628
    /**
629
     * Sets the maximum number of results to retrieve (the "limit").
630
     *
631
     * @param integer|null $maxResults
632
     *
633
     * @return Query This query object.
634
     */
635 247
    public function setMaxResults($maxResults)
636
    {
637 247
        $this->_maxResults = $maxResults;
638 247
        $this->_state      = self::STATE_DIRTY;
639
640 247
        return $this;
641
    }
642
643
    /**
644
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
645
     * Returns NULL if {@link setMaxResults} was not applied to this query.
646
     *
647
     * @return integer|null Maximum number of results.
648
     */
649 670
    public function getMaxResults()
650
    {
651 670
        return $this->_maxResults;
652
    }
653
654
    /**
655
     * Executes the query and returns an IterableResult that can be used to incrementally
656
     * iterated over the result.
657
     *
658
     * @param ArrayCollection|array|null $parameters    The query parameters.
659
     * @param string|int                 $hydrationMode The hydration mode to use.
660
     *
661
     * @return \Doctrine\ORM\Internal\Hydration\IterableResult
662
     */
663 10
    public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT)
664
    {
665 10
        $this->setHint(self::HINT_INTERNAL_ITERATION, true);
666
667 10
        return parent::iterate($parameters, $hydrationMode);
668
    }
669
670
    /**
671
     * {@inheritdoc}
672
     */
673 468
    public function setHint($name, $value)
674
    {
675 468
        $this->_state = self::STATE_DIRTY;
676
677 468
        return parent::setHint($name, $value);
678
    }
679
680
    /**
681
     * {@inheritdoc}
682
     */
683 368
    public function setHydrationMode($hydrationMode)
684
    {
685 368
        $this->_state = self::STATE_DIRTY;
686
687 368
        return parent::setHydrationMode($hydrationMode);
688
    }
689
690
    /**
691
     * Set the lock mode for this Query.
692
     *
693
     * @see \Doctrine\DBAL\LockMode
694
     *
695
     * @param int $lockMode
696
     *
697
     * @return Query
698
     *
699
     * @throws TransactionRequiredException
700
     */
701
    public function setLockMode($lockMode)
702
    {
703
        if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) {
704
            if ( ! $this->_em->getConnection()->isTransactionActive()) {
705
                throw TransactionRequiredException::transactionRequired();
706
            }
707
        }
708
709
        $this->setHint(self::HINT_LOCK_MODE, $lockMode);
710
711
        return $this;
712
    }
713
714
    /**
715
     * Get the current lock mode for this query.
716
     *
717
     * @return int|null The current lock mode of this query or NULL if no specific lock mode is set.
718
     */
719
    public function getLockMode()
720
    {
721
        $lockMode = $this->getHint(self::HINT_LOCK_MODE);
722
723
        if (false === $lockMode) {
724
            return null;
725
        }
726
727
        return $lockMode;
728
    }
729
730
    /**
731
     * Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
732
     *
733
     * @return string
734
     */
735 613
    protected function _getQueryCacheId()
736
    {
737 613
        ksort($this->_hints);
738
739 613
        $platform = $this->getEntityManager()
740 613
            ->getConnection()
741 613
            ->getDatabasePlatform()
742 613
            ->getName();
743
744 613
        return md5(
745 613
            $this->getDQL() . serialize($this->_hints) .
746 613
            '&platform=' . $platform .
747 613
            ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
748 613
            '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
749 613
            '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->_parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT'
750
        );
751
    }
752
753
     /**
754
     * {@inheritdoc}
755
     */
756 28
    protected function getHash()
757
    {
758 28
        return sha1(parent::getHash(). '-'. $this->_firstResult . '-' . $this->_maxResults);
759
    }
760
761
    /**
762
     * Cleanup Query resource when clone is called.
763
     *
764
     * @return void
765
     */
766 142
    public function __clone()
767
    {
768 142
        parent::__clone();
769
770 142
        $this->_state = self::STATE_DIRTY;
771 142
    }
772
}
773