Failed Conditions
Push — master ( 2ade86...13f838 )
by Jonathan
18s
created

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

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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 336
    public function getSQL()
206
    {
207 336
        return $this->_parse()->getSqlExecutor()->getSqlStatements();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->_parse()->...()->getSqlStatements(); (array) is incompatible with the return type declared by the abstract method Doctrine\ORM\AbstractQuery::getSQL of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

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 440
    protected function getResultSetMapping()
228
    {
229
        // parse query or load from cache
230 440
        if ($this->_resultSetMapping === null) {
231 38
            $this->_resultSetMapping = $this->_parse()->getResultSetMapping();
232
        }
233
234 437
        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 761
    private function _parse()
245
    {
246 761
        $types = [];
247
248 761
        foreach ($this->parameters as $parameter) {
249
            /** @var Query\Parameter $parameter */
250 174
            $types[$parameter->getName()] = $parameter->getType();
251
        }
252
253
        // Return previous parser result if the query and the filter collection are both clean
254 761
        if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) {
255 39
            return $this->_parserResult;
256
        }
257
258 761
        $this->_state = self::STATE_CLEAN;
259 761
        $this->_parsedTypes = $types;
260
261
        // Check query cache.
262 761
        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 589
        $hash   = $this->_getQueryCacheId();
271 589
        $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
272
273 589
        if ($cached instanceof ParserResult) {
274
            // Cache hit.
275 121
            $this->_parserResult = $cached;
276
277 121
            return $this->_parserResult;
278
        }
279
280
        // Cache miss.
281 538
        $parser = new Parser($this);
282
283 538
        $this->_parserResult = $parser->parse();
284
285 517
        $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
286
287 517
        return $this->_parserResult;
288
    }
289
290
    /**
291
     * {@inheritdoc}
292
     */
293 457
    protected function _doExecute()
294
    {
295 457
        $executor = $this->_parse()->getSqlExecutor();
296
297 448
        if ($this->_queryCacheProfile) {
298 8
            $executor->setQueryCacheProfile($this->_queryCacheProfile);
299
        } else {
300 442
            $executor->removeQueryCacheProfile();
301
        }
302
303 448
        if ($this->_resultSetMapping === null) {
304 406
            $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
305
        }
306
307
        // Prepare parameters
308 448
        $paramMappings = $this->_parserResult->getParameterMappings();
309 448
        $paramCount = count($this->parameters);
310 448
        $mappingCount = count($paramMappings);
311
312 448
        if ($paramCount > $mappingCount) {
313 1
            throw QueryException::tooManyParameters($mappingCount, $paramCount);
314
        }
315
316 447
        if ($paramCount < $mappingCount) {
317 1
            throw QueryException::tooFewParameters($mappingCount, $paramCount);
318
        }
319
320
        // evict all cache for the entity region
321 446
        if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
322 2
            $this->evictEntityCacheRegion();
323
        }
324
325 446
        list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
326
327 445
        $this->evictResultSetCache(
328 445
            $executor,
329 445
            $sqlParams,
330 445
            $types,
331 445
            $this->_em->getConnection()->getParams()
332
        );
333
334 445
        return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
335
    }
336
337 445
    private function evictResultSetCache(
338
        AbstractSqlExecutor $executor,
339
        array $sqlParams,
340
        array $types,
341
        array $connectionParams
342
    ) {
343 445
        if (null === $this->_queryCacheProfile || ! $this->getExpireResultCache()) {
344 445
            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 446
    private function processParameterMappings($paramMappings)
385
    {
386 446
        $sqlParams = [];
387 446
        $types     = [];
388
389 446
        foreach ($this->parameters as $parameter) {
390 162
            $key    = $parameter->getName();
391 162
            $value  = $parameter->getValue();
392 162
            $rsm    = $this->getResultSetMapping();
393
394 162
            if ( ! isset($paramMappings[$key])) {
395 1
                throw QueryException::unknownParameter($key);
396
            }
397
398 161
            if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
399
                $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
400
            }
401
402 161
            if (isset($rsm->discriminatorParameters[$key]) && $value instanceof ClassMetadata) {
403 3
                $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em));
404
            }
405
406 161
            $value = $this->processParameterValue($value);
407 161
            $type  = ($parameter->getValue() === $value)
408 149
                ? $parameter->getType()
409 161
                : ParameterTypeInferer::inferType($value);
410
411 161
            foreach ($paramMappings[$key] as $position) {
412 161
                $types[$position] = $type;
413
            }
414
415 161
            $sqlPositions = $paramMappings[$key];
416
417
            // optimized multi value sql positions away for now,
418
            // they are not allowed in DQL anyways.
419 161
            $value = [$value];
420 161
            $countValue = count($value);
421
422 161
            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
423 161
                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
424
            }
425
        }
426
427 445
        if (count($sqlParams) != count($types)) {
428
            throw QueryException::parameterTypeMismatch();
429
        }
430
431 445
        if ($sqlParams) {
432 161
            ksort($sqlParams);
433 161
            $sqlParams = array_values($sqlParams);
434
435 161
            ksort($types);
436 161
            $types = array_values($types);
437
        }
438
439 445
        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 589
    public function getQueryCacheDriver()
477
    {
478 589
        if ($this->_queryCache) {
479 9
            return $this->_queryCache;
480
        }
481
482 580
        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 211
    public function free()
541
    {
542 211
        parent::free();
543
544 211
        $this->_dql = null;
545 211
        $this->_state = self::STATE_CLEAN;
546 211
    }
547
548
    /**
549
     * Sets a DQL query string.
550
     *
551
     * @param string $dqlQuery DQL Query.
552
     *
553
     * @return \Doctrine\ORM\AbstractQuery
554
     */
555 944
    public function setDQL($dqlQuery)
556
    {
557 944
        if ($dqlQuery !== null) {
558 944
            $this->_dql = $dqlQuery;
559 944
            $this->_state = self::STATE_DIRTY;
560
        }
561
562 944
        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 896
    public function getDQL()
571
    {
572 896
        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 220
    public function setFirstResult($firstResult)
610
    {
611 220
        $this->_firstResult = $firstResult;
612 220
        $this->_state       = self::STATE_DIRTY;
613
614 220
        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 646
    public function getFirstResult()
624
    {
625 646
        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 229
    public function setMaxResults($maxResults)
636
    {
637 229
        $this->_maxResults = $maxResults;
638 229
        $this->_state      = self::STATE_DIRTY;
639
640 229
        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 646
    public function getMaxResults()
650
    {
651 646
        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 integer                    $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 461
    public function setHint($name, $value)
674
    {
675 461
        $this->_state = self::STATE_DIRTY;
676
677 461
        return parent::setHint($name, $value);
678
    }
679
680
    /**
681
     * {@inheritdoc}
682
     */
683 354
    public function setHydrationMode($hydrationMode)
684
    {
685 354
        $this->_state = self::STATE_DIRTY;
686
687 354
        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 589
    protected function _getQueryCacheId()
736
    {
737 589
        ksort($this->_hints);
738
739 589
        $platform = $this->getEntityManager()
740 589
            ->getConnection()
741 589
            ->getDatabasePlatform()
742 589
            ->getName();
743
744 589
        return md5(
745 589
            $this->getDQL() . serialize($this->_hints) .
746 589
            '&platform=' . $platform .
747 589
            ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
748 589
            '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
749 589
            '&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 136
    public function __clone()
767
    {
768 136
        parent::__clone();
769
770 136
        $this->_state = self::STATE_DIRTY;
771 136
    }
772
}
773