Completed
Pull Request — master (#6055)
by Martin
09:59
created

Query::setHint()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
crap 1
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\Parser;
24
use Doctrine\ORM\Query\ParserResult;
25
use Doctrine\ORM\Query\QueryException;
26
use Doctrine\ORM\Mapping\ClassMetadata;
27
use Doctrine\ORM\Query\ParameterTypeInferer;
28
use Doctrine\Common\Collections\ArrayCollection;
29
30
/**
31
 * A Query object represents a DQL query.
32
 *
33
 * @since   1.0
34
 * @author  Guilherme Blanco <[email protected]>
35
 * @author  Konsta Vesterinen <[email protected]>
36
 * @author  Roman Borschel <[email protected]>
37
 */
38
final class Query extends AbstractQuery
39
{
40
    /**
41
     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
42
     */
43
    const STATE_CLEAN  = 1;
44
45
    /**
46
     * A query object is in state DIRTY when it has DQL parts that have not yet been
47
     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
48
     * is called.
49
     */
50
    const STATE_DIRTY = 2;
51
52
    /* Query HINTS */
53
54
    /**
55
     * The refresh hint turns any query into a refresh query with the result that
56
     * any local changes in entities are overridden with the fetched values.
57
     *
58
     * @var string
59
     */
60
    const HINT_REFRESH = 'doctrine.refresh';
61
62
    /**
63
     * @var string
64
     */
65
    const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
66
67
    /**
68
     * @var string
69
     */
70
    const HINT_CACHE_EVICT = 'doctrine.cache.evict';
71
72
    /**
73
     * Internal hint: is set to the proxy entity that is currently triggered for loading
74
     *
75
     * @var string
76
     */
77
    const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
78
79
    /**
80
     * The forcePartialLoad query hint forces a particular query to return
81
     * partial objects.
82
     *
83
     * @var string
84
     * @todo Rename: HINT_OPTIMIZE
85
     */
86
    const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
87
88
    /**
89
     * The includeMetaColumns query hint causes meta columns like foreign keys and
90
     * discriminator columns to be selected and returned as part of the query result.
91
     *
92
     * This hint does only apply to non-object queries.
93
     *
94
     * @var string
95
     */
96
    const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
97
98
    /**
99
     * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
100
     * are iterated and executed after the DQL has been parsed into an AST.
101
     *
102
     * @var string
103
     */
104
    const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
105
106
    /**
107
     * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
108
     * and is used for generating the target SQL from any DQL AST tree.
109
     *
110
     * @var string
111
     */
112
    const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
113
114
    //const HINT_READ_ONLY = 'doctrine.readOnly';
115
116
    /**
117
     * @var string
118
     */
119
    const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
120
121
    /**
122
     * @var string
123
     */
124
    const HINT_LOCK_MODE = 'doctrine.lockMode';
125
126
    /**
127
     * The current state of this query.
128
     *
129
     * @var integer
130
     */
131
    private $_state = self::STATE_CLEAN;
132
133
    /**
134
     * A snapshot of the parameter types the query was parsed with.
135
     *
136
     * @var array
137
     */
138
    private $_parsedTypes = array();
139
140
    /**
141
     * Cached DQL query.
142
     *
143
     * @var string
144
     */
145
    private $_dql = null;
146
147
    /**
148
     * The parser result that holds DQL => SQL information.
149
     *
150
     * @var \Doctrine\ORM\Query\ParserResult
151
     */
152
    private $_parserResult;
153
154
    /**
155
     * The first result to return (the "offset").
156
     *
157
     * @var integer
158
     */
159
    private $_firstResult = null;
160
161
    /**
162
     * The maximum number of results to return (the "limit").
163
     *
164
     * @var integer
165
     */
166
    private $_maxResults = null;
167
168
    /**
169
     * The cache driver used for caching queries.
170
     *
171
     * @var \Doctrine\Common\Cache\Cache|null
172
     */
173
    private $_queryCache;
174
175
    /**
176
     * Whether or not expire the query cache.
177
     *
178
     * @var boolean
179
     */
180
    private $_expireQueryCache = false;
181
182
    /**
183
     * The query cache lifetime.
184
     *
185
     * @var int
186
     */
187
    private $_queryCacheTTL;
188
189
    /**
190
     * Whether to use a query cache, if available. Defaults to TRUE.
191
     *
192
     * @var boolean
193
     */
194
    private $_useQueryCache = true;
195
196
    /**
197
     * Gets the SQL query/queries that correspond to this DQL query.
198
     *
199
     * @return mixed The built sql query or an array of all sql queries.
200
     *
201
     * @override
202
     */
203 323
    public function getSQL()
204
    {
205 323
        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...
206
    }
207
208
    /**
209
     * Returns the corresponding AST for this DQL query.
210
     *
211
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
212
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
213
     *         \Doctrine\ORM\Query\AST\DeleteStatement
214
     */
215
    public function getAST()
216
    {
217
        $parser = new Parser($this);
218
219
        return $parser->getAST();
220
    }
221
222
    /**
223
     * {@inheritdoc}
224
     */
225 354
    protected function getResultSetMapping()
226
    {
227
        // parse query or load from cache
228 354
        if ($this->_resultSetMapping === null) {
229 31
            $this->_resultSetMapping = $this->_parse()->getResultSetMapping();
230
        }
231
232 350
        return $this->_resultSetMapping;
233
    }
234
235
    /**
236
     * Parses the DQL query, if necessary, and stores the parser result.
237
     *
238
     * Note: Populates $this->_parserResult as a side-effect.
239
     *
240
     * @return \Doctrine\ORM\Query\ParserResult
241
     */
242 678
    private function _parse()
243
    {
244 678
        $types = array();
245
246 678
        foreach ($this->parameters as $parameter) {
247
            /** @var Query\Parameter $parameter */
248 69
            $types[$parameter->getName()] = $parameter->getType();
249
        }
250
251
        // Return previous parser result if the query and the filter collection are both clean
252 678
        if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) {
253 28
            return $this->_parserResult;
254
        }
255
256 678
        $this->_state = self::STATE_CLEAN;
257 678
        $this->_parsedTypes = $types;
258
259
        // Check query cache.
260 678
        if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
261 172
            $parser = new Parser($this);
262
263 172
            $this->_parserResult = $parser->parse();
264
265 168
            return $this->_parserResult;
266
        }
267
268 506
        $hash   = $this->_getQueryCacheId();
269 506
        $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
0 ignored issues
show
Bug introduced by
The variable $queryCache does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
270
271 506
        if ($cached instanceof ParserResult) {
272
            // Cache hit.
273 65
            $this->_parserResult = $cached;
274
275 65
            return $this->_parserResult;
276
        }
277
278
        // Cache miss.
279 464
        $parser = new Parser($this);
280
281 464
        $this->_parserResult = $parser->parse();
282
283 443
        $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
284
285 443
        return $this->_parserResult;
286
    }
287
288
    /**
289
     * {@inheritdoc}
290
     */
291 377
    protected function _doExecute()
292
    {
293 377
        $executor = $this->_parse()->getSqlExecutor();
294
295 366
        if ($this->_queryCacheProfile) {
296 6
            $executor->setQueryCacheProfile($this->_queryCacheProfile);
297
        } else {
298 361
            $executor->removeQueryCacheProfile();
299
        }
300
301 366
        if ($this->_resultSetMapping === null) {
302 328
            $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
303
        }
304
305
        // Prepare parameters
306 366
        $paramMappings = $this->_parserResult->getParameterMappings();
307 366
        $paramCount = count($this->parameters);
308 366
        $mappingCount = count($paramMappings);
309
310 366
        if ($paramCount > $mappingCount) {
311 1
            throw QueryException::tooManyParameters($mappingCount, $paramCount);
312 365
        } elseif ($paramCount < $mappingCount) {
313 1
            throw QueryException::tooFewParameters($mappingCount, $paramCount);
314
        }
315
316
        // evict all cache for the entity region
317 364
        if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
318
            $this->evictEntityCacheRegion();
319
        }
320
321 364
        list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
322
323 354
        return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
324
    }
325
326
    /**
327
     * Evict entity cache region
328
     */
329
    private function evictEntityCacheRegion()
330
    {
331
        $AST = $this->getAST();
332
333
        if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) {
334
            throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
335
        }
336
337
        $className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement)
338
            ? $AST->deleteClause->abstractSchemaName
339
            : $AST->updateClause->abstractSchemaName;
340
341
        $this->_em->getCache()->evictEntityRegion($className);
342
    }
343
344
    /**
345
     * Processes query parameter mappings.
346
     *
347
     * @param array $paramMappings
348
     *
349
     * @return array
350
     *
351
     * @throws Query\QueryException
352
     */
353 364
    private function processParameterMappings($paramMappings)
354
    {
355 364
        $sqlParams = array();
356 364
        $types     = array();
357
358 364
        foreach ($this->parameters as $parameter) {
359 58
            $key    = $parameter->getName();
360 58
            $value  = $parameter->getValue();
361 58
            $rsm    = $this->getResultSetMapping();
362
363 58
            if ( ! isset($paramMappings[$key])) {
364 1
                throw QueryException::unknownParameter($key);
365
            }
366
367 57
            if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
368 2
                $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
369
            }
370
371 57
            $value = $this->processParameterValue($value);
372 49
            $type  = ($parameter->getValue() === $value)
373 47
                ? $parameter->getType()
374 49
                : ParameterTypeInferer::inferType($value);
375
376 49
            foreach ($paramMappings[$key] as $position) {
377 49
                $types[$position] = $type;
378
            }
379
380 49
            $sqlPositions = $paramMappings[$key];
381
382
            // optimized multi value sql positions away for now,
383
            // they are not allowed in DQL anyways.
384 49
            $value = array($value);
385 49
            $countValue = count($value);
386
387 49
            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
388 49
                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
389
            }
390
        }
391
392 354
        if (count($sqlParams) != count($types)) {
393
            throw QueryException::parameterTypeMismatch();
394
        }
395
396 354
        if ($sqlParams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sqlParams of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

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