vendor/doctrine/orm/lib/Doctrine/ORM/AbstractQuery.php line 791

Open in your IDE?
  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. namespace Doctrine\ORM;
  20. use Countable;
  21. use Doctrine\Common\Collections\ArrayCollection;
  22. use Doctrine\Common\Collections\Collection;
  23. use Doctrine\DBAL\Cache\QueryCacheProfile;
  24. use Doctrine\DBAL\Driver\Statement;
  25. use Doctrine\ORM\Cache\Logging\CacheLogger;
  26. use Doctrine\ORM\Cache\QueryCacheKey;
  27. use Doctrine\ORM\Cache\TimestampCacheKey;
  28. use Doctrine\ORM\Internal\Hydration\IterableResult;
  29. use Doctrine\ORM\Mapping\MappingException as ORMMappingException;
  30. use Doctrine\ORM\Query\Parameter;
  31. use Doctrine\ORM\Query\QueryException;
  32. use Doctrine\ORM\Query\ResultSetMapping;
  33. use Doctrine\Persistence\Mapping\MappingException;
  34. use Traversable;
  35. use function array_map;
  36. use function array_shift;
  37. use function count;
  38. use function is_array;
  39. use function is_numeric;
  40. use function is_object;
  41. use function is_scalar;
  42. use function iterator_count;
  43. use function iterator_to_array;
  44. use function ksort;
  45. use function reset;
  46. use function serialize;
  47. use function sha1;
  48. use function trigger_error;
  49. use const E_USER_DEPRECATED;
  50. /**
  51.  * Base contract for ORM queries. Base class for Query and NativeQuery.
  52.  *
  53.  * @link    www.doctrine-project.org
  54.  */
  55. abstract class AbstractQuery
  56. {
  57.     /* Hydration mode constants */
  58.     /**
  59.      * Hydrates an object graph. This is the default behavior.
  60.      */
  61.     public const HYDRATE_OBJECT 1;
  62.     /**
  63.      * Hydrates an array graph.
  64.      */
  65.     public const HYDRATE_ARRAY 2;
  66.     /**
  67.      * Hydrates a flat, rectangular result set with scalar values.
  68.      */
  69.     public const HYDRATE_SCALAR 3;
  70.     /**
  71.      * Hydrates a single scalar value.
  72.      */
  73.     public const HYDRATE_SINGLE_SCALAR 4;
  74.     /**
  75.      * Very simple object hydrator (optimized for performance).
  76.      */
  77.     public const HYDRATE_SIMPLEOBJECT 5;
  78.     /**
  79.      * The parameter map of this query.
  80.      *
  81.      * @var ArrayCollection|Parameter[]
  82.      * @psalm-var ArrayCollection<int, Parameter>
  83.      */
  84.     protected $parameters;
  85.     /**
  86.      * The user-specified ResultSetMapping to use.
  87.      *
  88.      * @var ResultSetMapping
  89.      */
  90.     protected $_resultSetMapping;
  91.     /**
  92.      * The entity manager used by this query object.
  93.      *
  94.      * @var EntityManagerInterface
  95.      */
  96.     protected $_em;
  97.     /**
  98.      * The map of query hints.
  99.      *
  100.      * @psalm-var array<string, mixed>
  101.      */
  102.     protected $_hints = [];
  103.     /**
  104.      * The hydration mode.
  105.      *
  106.      * @var string|int
  107.      */
  108.     protected $_hydrationMode self::HYDRATE_OBJECT;
  109.     /** @var QueryCacheProfile */
  110.     protected $_queryCacheProfile;
  111.     /**
  112.      * Whether or not expire the result cache.
  113.      *
  114.      * @var bool
  115.      */
  116.     protected $_expireResultCache false;
  117.     /** @var QueryCacheProfile */
  118.     protected $_hydrationCacheProfile;
  119.     /**
  120.      * Whether to use second level cache, if available.
  121.      *
  122.      * @var bool
  123.      */
  124.     protected $cacheable false;
  125.     /** @var bool */
  126.     protected $hasCache false;
  127.     /**
  128.      * Second level cache region name.
  129.      *
  130.      * @var string|null
  131.      */
  132.     protected $cacheRegion;
  133.     /**
  134.      * Second level query cache mode.
  135.      *
  136.      * @var int|null
  137.      */
  138.     protected $cacheMode;
  139.     /** @var CacheLogger|null */
  140.     protected $cacheLogger;
  141.     /** @var int */
  142.     protected $lifetime 0;
  143.     /**
  144.      * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
  145.      */
  146.     public function __construct(EntityManagerInterface $em)
  147.     {
  148.         $this->_em        $em;
  149.         $this->parameters = new ArrayCollection();
  150.         $this->_hints     $em->getConfiguration()->getDefaultQueryHints();
  151.         $this->hasCache   $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
  152.         if ($this->hasCache) {
  153.             $this->cacheLogger $em->getConfiguration()
  154.                 ->getSecondLevelCacheConfiguration()
  155.                 ->getCacheLogger();
  156.         }
  157.     }
  158.     /**
  159.      * Enable/disable second level query (result) caching for this query.
  160.      *
  161.      * @param bool $cacheable
  162.      *
  163.      * @return static This query instance.
  164.      */
  165.     public function setCacheable($cacheable)
  166.     {
  167.         $this->cacheable = (bool) $cacheable;
  168.         return $this;
  169.     }
  170.     /**
  171.      * @return bool TRUE if the query results are enable for second level cache, FALSE otherwise.
  172.      */
  173.     public function isCacheable()
  174.     {
  175.         return $this->cacheable;
  176.     }
  177.     /**
  178.      * @param string $cacheRegion
  179.      *
  180.      * @return static This query instance.
  181.      */
  182.     public function setCacheRegion($cacheRegion)
  183.     {
  184.         $this->cacheRegion = (string) $cacheRegion;
  185.         return $this;
  186.     }
  187.     /**
  188.      * Obtain the name of the second level query cache region in which query results will be stored
  189.      *
  190.      * @return string|null The cache region name; NULL indicates the default region.
  191.      */
  192.     public function getCacheRegion()
  193.     {
  194.         return $this->cacheRegion;
  195.     }
  196.     /**
  197.      * @return bool TRUE if the query cache and second level cache are enabled, FALSE otherwise.
  198.      */
  199.     protected function isCacheEnabled()
  200.     {
  201.         return $this->cacheable && $this->hasCache;
  202.     }
  203.     /**
  204.      * @return int
  205.      */
  206.     public function getLifetime()
  207.     {
  208.         return $this->lifetime;
  209.     }
  210.     /**
  211.      * Sets the life-time for this query into second level cache.
  212.      *
  213.      * @param int $lifetime
  214.      *
  215.      * @return static This query instance.
  216.      */
  217.     public function setLifetime($lifetime)
  218.     {
  219.         $this->lifetime = (int) $lifetime;
  220.         return $this;
  221.     }
  222.     /**
  223.      * @return int
  224.      */
  225.     public function getCacheMode()
  226.     {
  227.         return $this->cacheMode;
  228.     }
  229.     /**
  230.      * @param int $cacheMode
  231.      *
  232.      * @return static This query instance.
  233.      */
  234.     public function setCacheMode($cacheMode)
  235.     {
  236.         $this->cacheMode = (int) $cacheMode;
  237.         return $this;
  238.     }
  239.     /**
  240.      * Gets the SQL query that corresponds to this query object.
  241.      * The returned SQL syntax depends on the connection driver that is used
  242.      * by this query object at the time of this method call.
  243.      *
  244.      * @return string SQL query
  245.      */
  246.     abstract public function getSQL();
  247.     /**
  248.      * Retrieves the associated EntityManager of this Query instance.
  249.      *
  250.      * @return EntityManager
  251.      */
  252.     public function getEntityManager()
  253.     {
  254.         return $this->_em;
  255.     }
  256.     /**
  257.      * Frees the resources used by the query object.
  258.      *
  259.      * Resets Parameters, Parameter Types and Query Hints.
  260.      *
  261.      * @return void
  262.      */
  263.     public function free()
  264.     {
  265.         $this->parameters = new ArrayCollection();
  266.         $this->_hints $this->_em->getConfiguration()->getDefaultQueryHints();
  267.     }
  268.     /**
  269.      * Get all defined parameters.
  270.      *
  271.      * @return ArrayCollection The defined query parameters.
  272.      */
  273.     public function getParameters()
  274.     {
  275.         return $this->parameters;
  276.     }
  277.     /**
  278.      * Gets a query parameter.
  279.      *
  280.      * @param mixed $key The key (index or name) of the bound parameter.
  281.      *
  282.      * @return Query\Parameter|null The value of the bound parameter, or NULL if not available.
  283.      */
  284.     public function getParameter($key)
  285.     {
  286.         $key Query\Parameter::normalizeName($key);
  287.         $filteredParameters $this->parameters->filter(
  288.             static function (Query\Parameter $parameter) use ($key): bool {
  289.                 $parameterName $parameter->getName();
  290.                 return $key === $parameterName;
  291.             }
  292.         );
  293.         return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null;
  294.     }
  295.     /**
  296.      * Sets a collection of query parameters.
  297.      *
  298.      * @param ArrayCollection|mixed[] $parameters
  299.      *
  300.      * @return static This query instance.
  301.      *
  302.      * @psalm-param ArrayCollection<int, Parameter>|mixed[] $parameters
  303.      */
  304.     public function setParameters($parameters)
  305.     {
  306.         // BC compatibility with 2.3-
  307.         if (is_array($parameters)) {
  308.             /** @psalm-var ArrayCollection<int, Parameter> $parameterCollection */
  309.             $parameterCollection = new ArrayCollection();
  310.             foreach ($parameters as $key => $value) {
  311.                 $parameterCollection->add(new Parameter($key$value));
  312.             }
  313.             $parameters $parameterCollection;
  314.         }
  315.         $this->parameters $parameters;
  316.         return $this;
  317.     }
  318.     /**
  319.      * Sets a query parameter.
  320.      *
  321.      * @param string|int  $key   The parameter position or name.
  322.      * @param mixed       $value The parameter value.
  323.      * @param string|null $type  The parameter type. If specified, the given value will be run through
  324.      *                           the type conversion of this type. This is usually not needed for
  325.      *                           strings and numeric types.
  326.      *
  327.      * @return static This query instance.
  328.      */
  329.     public function setParameter($key$value$type null)
  330.     {
  331.         $existingParameter $this->getParameter($key);
  332.         if ($existingParameter !== null) {
  333.             $existingParameter->setValue($value$type);
  334.             return $this;
  335.         }
  336.         $this->parameters->add(new Parameter($key$value$type));
  337.         return $this;
  338.     }
  339.     /**
  340.      * Processes an individual parameter value.
  341.      *
  342.      * @param mixed $value
  343.      *
  344.      * @return mixed[]|string|int|float|bool
  345.      *
  346.      * @throws ORMInvalidArgumentException
  347.      *
  348.      * @psalm-return array|scalar
  349.      */
  350.     public function processParameterValue($value)
  351.     {
  352.         if (is_scalar($value)) {
  353.             return $value;
  354.         }
  355.         if ($value instanceof Collection) {
  356.             $value iterator_to_array($value);
  357.         }
  358.         if (is_array($value)) {
  359.             $value $this->processArrayParameterValue($value);
  360.             return $value;
  361.         }
  362.         if ($value instanceof Mapping\ClassMetadata) {
  363.             return $value->name;
  364.         }
  365.         if (! is_object($value)) {
  366.             return $value;
  367.         }
  368.         try {
  369.             $value $this->_em->getUnitOfWork()->getSingleIdentifierValue($value);
  370.             if ($value === null) {
  371.                 throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
  372.             }
  373.         } catch (MappingException ORMMappingException $e) {
  374.             /* Silence any mapping exceptions. These can occur if the object in
  375.                question is not a mapped entity, in which case we just don't do
  376.                any preparation on the value.
  377.                Depending on MappingDriver, either MappingException or
  378.                ORMMappingException is thrown. */
  379.             $value $this->potentiallyProcessIterable($value);
  380.         }
  381.         return $value;
  382.     }
  383.     /**
  384.      * If no mapping is detected, trying to resolve the value as a Traversable
  385.      *
  386.      * @param mixed $value
  387.      *
  388.      * @return mixed
  389.      */
  390.     private function potentiallyProcessIterable($value)
  391.     {
  392.         if ($value instanceof Traversable) {
  393.             $value iterator_to_array($value);
  394.             $value $this->processArrayParameterValue($value);
  395.         }
  396.         return $value;
  397.     }
  398.     /**
  399.      * Process a parameter value which was previously identified as an array
  400.      *
  401.      * @param mixed[] $value
  402.      *
  403.      * @return mixed[]
  404.      */
  405.     private function processArrayParameterValue(array $value): array
  406.     {
  407.         foreach ($value as $key => $paramValue) {
  408.             $paramValue  $this->processParameterValue($paramValue);
  409.             $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
  410.         }
  411.         return $value;
  412.     }
  413.     /**
  414.      * Sets the ResultSetMapping that should be used for hydration.
  415.      *
  416.      * @return static This query instance.
  417.      */
  418.     public function setResultSetMapping(Query\ResultSetMapping $rsm)
  419.     {
  420.         $this->translateNamespaces($rsm);
  421.         $this->_resultSetMapping $rsm;
  422.         return $this;
  423.     }
  424.     /**
  425.      * Gets the ResultSetMapping used for hydration.
  426.      *
  427.      * @return ResultSetMapping
  428.      */
  429.     protected function getResultSetMapping()
  430.     {
  431.         return $this->_resultSetMapping;
  432.     }
  433.     /**
  434.      * Allows to translate entity namespaces to full qualified names.
  435.      *
  436.      * @return void
  437.      */
  438.     private function translateNamespaces(Query\ResultSetMapping $rsm)
  439.     {
  440.         $translate = function ($alias): string {
  441.             return $this->_em->getClassMetadata($alias)->getName();
  442.         };
  443.         $rsm->aliasMap         array_map($translate$rsm->aliasMap);
  444.         $rsm->declaringClasses array_map($translate$rsm->declaringClasses);
  445.     }
  446.     /**
  447.      * Set a cache profile for hydration caching.
  448.      *
  449.      * If no result cache driver is set in the QueryCacheProfile, the default
  450.      * result cache driver is used from the configuration.
  451.      *
  452.      * Important: Hydration caching does NOT register entities in the
  453.      * UnitOfWork when retrieved from the cache. Never use result cached
  454.      * entities for requests that also flush the EntityManager. If you want
  455.      * some form of caching with UnitOfWork registration you should use
  456.      * {@see AbstractQuery::setResultCacheProfile()}.
  457.      *
  458.      * @return static This query instance.
  459.      *
  460.      * @example
  461.      * $lifetime = 100;
  462.      * $resultKey = "abc";
  463.      * $query->setHydrationCacheProfile(new QueryCacheProfile());
  464.      * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
  465.      */
  466.     public function setHydrationCacheProfile(?QueryCacheProfile $profile null)
  467.     {
  468.         if ($profile !== null && ! $profile->getResultCacheDriver()) {
  469.             $resultCacheDriver $this->_em->getConfiguration()->getHydrationCacheImpl();
  470.             $profile           $profile->setResultCacheDriver($resultCacheDriver);
  471.         }
  472.         $this->_hydrationCacheProfile $profile;
  473.         return $this;
  474.     }
  475.     /**
  476.      * @return QueryCacheProfile
  477.      */
  478.     public function getHydrationCacheProfile()
  479.     {
  480.         return $this->_hydrationCacheProfile;
  481.     }
  482.     /**
  483.      * Set a cache profile for the result cache.
  484.      *
  485.      * If no result cache driver is set in the QueryCacheProfile, the default
  486.      * result cache driver is used from the configuration.
  487.      *
  488.      * @return static This query instance.
  489.      */
  490.     public function setResultCacheProfile(?QueryCacheProfile $profile null)
  491.     {
  492.         if ($profile !== null && ! $profile->getResultCacheDriver()) {
  493.             $resultCacheDriver $this->_em->getConfiguration()->getResultCacheImpl();
  494.             $profile           $profile->setResultCacheDriver($resultCacheDriver);
  495.         }
  496.         $this->_queryCacheProfile $profile;
  497.         return $this;
  498.     }
  499.     /**
  500.      * Defines a cache driver to be used for caching result sets and implicitly enables caching.
  501.      *
  502.      * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver
  503.      *
  504.      * @return static This query instance.
  505.      *
  506.      * @throws ORMException
  507.      */
  508.     public function setResultCacheDriver($resultCacheDriver null)
  509.     {
  510.         if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
  511.             throw ORMException::invalidResultCacheDriver();
  512.         }
  513.         $this->_queryCacheProfile $this->_queryCacheProfile
  514.             $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
  515.             : new QueryCacheProfile(0null$resultCacheDriver);
  516.         return $this;
  517.     }
  518.     /**
  519.      * Returns the cache driver used for caching result sets.
  520.      *
  521.      * @deprecated
  522.      *
  523.      * @return \Doctrine\Common\Cache\Cache Cache driver
  524.      */
  525.     public function getResultCacheDriver()
  526.     {
  527.         if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
  528.             return $this->_queryCacheProfile->getResultCacheDriver();
  529.         }
  530.         return $this->_em->getConfiguration()->getResultCacheImpl();
  531.     }
  532.     /**
  533.      * Set whether or not to cache the results of this query and if so, for
  534.      * how long and which ID to use for the cache entry.
  535.      *
  536.      * @deprecated 2.7 Use {@see enableResultCache} and {@see disableResultCache} instead.
  537.      *
  538.      * @param bool   $useCache
  539.      * @param int    $lifetime
  540.      * @param string $resultCacheId
  541.      *
  542.      * @return static This query instance.
  543.      */
  544.     public function useResultCache($useCache$lifetime null$resultCacheId null)
  545.     {
  546.         return $useCache
  547.             $this->enableResultCache($lifetime$resultCacheId)
  548.             : $this->disableResultCache();
  549.     }
  550.     /**
  551.      * Enables caching of the results of this query, for given or default amount of seconds
  552.      * and optionally specifies which ID to use for the cache entry.
  553.      *
  554.      * @param int|null    $lifetime      How long the cache entry is valid, in seconds.
  555.      * @param string|null $resultCacheId ID to use for the cache entry.
  556.      *
  557.      * @return static This query instance.
  558.      */
  559.     public function enableResultCache(?int $lifetime null, ?string $resultCacheId null): self
  560.     {
  561.         $this->setResultCacheLifetime($lifetime);
  562.         $this->setResultCacheId($resultCacheId);
  563.         return $this;
  564.     }
  565.     /**
  566.      * Disables caching of the results of this query.
  567.      *
  568.      * @return static This query instance.
  569.      */
  570.     public function disableResultCache(): self
  571.     {
  572.         $this->_queryCacheProfile null;
  573.         return $this;
  574.     }
  575.     /**
  576.      * Defines how long the result cache will be active before expire.
  577.      *
  578.      * @param int|null $lifetime How long the cache entry is valid.
  579.      *
  580.      * @return static This query instance.
  581.      */
  582.     public function setResultCacheLifetime($lifetime)
  583.     {
  584.         $lifetime $lifetime !== null ? (int) $lifetime 0;
  585.         $this->_queryCacheProfile $this->_queryCacheProfile
  586.             $this->_queryCacheProfile->setLifetime($lifetime)
  587.             : new QueryCacheProfile($lifetimenull$this->_em->getConfiguration()->getResultCacheImpl());
  588.         return $this;
  589.     }
  590.     /**
  591.      * Retrieves the lifetime of resultset cache.
  592.      *
  593.      * @deprecated
  594.      *
  595.      * @return int
  596.      */
  597.     public function getResultCacheLifetime()
  598.     {
  599.         return $this->_queryCacheProfile $this->_queryCacheProfile->getLifetime() : 0;
  600.     }
  601.     /**
  602.      * Defines if the result cache is active or not.
  603.      *
  604.      * @param bool $expire Whether or not to force resultset cache expiration.
  605.      *
  606.      * @return static This query instance.
  607.      */
  608.     public function expireResultCache($expire true)
  609.     {
  610.         $this->_expireResultCache $expire;
  611.         return $this;
  612.     }
  613.     /**
  614.      * Retrieves if the resultset cache is active or not.
  615.      *
  616.      * @return bool
  617.      */
  618.     public function getExpireResultCache()
  619.     {
  620.         return $this->_expireResultCache;
  621.     }
  622.     /**
  623.      * @return QueryCacheProfile
  624.      */
  625.     public function getQueryCacheProfile()
  626.     {
  627.         return $this->_queryCacheProfile;
  628.     }
  629.     /**
  630.      * Change the default fetch mode of an association for this query.
  631.      *
  632.      * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
  633.      *
  634.      * @param string $class
  635.      * @param string $assocName
  636.      * @param int    $fetchMode
  637.      *
  638.      * @return static This query instance.
  639.      */
  640.     public function setFetchMode($class$assocName$fetchMode)
  641.     {
  642.         if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
  643.             $fetchMode Mapping\ClassMetadata::FETCH_LAZY;
  644.         }
  645.         $this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
  646.         return $this;
  647.     }
  648.     /**
  649.      * Defines the processing mode to be used during hydration / result set transformation.
  650.      *
  651.      * @param string|int $hydrationMode Doctrine processing mode to be used during hydration process.
  652.      *                                  One of the Query::HYDRATE_* constants.
  653.      *
  654.      * @return static This query instance.
  655.      */
  656.     public function setHydrationMode($hydrationMode)
  657.     {
  658.         $this->_hydrationMode $hydrationMode;
  659.         return $this;
  660.     }
  661.     /**
  662.      * Gets the hydration mode currently used by the query.
  663.      *
  664.      * @return string|int
  665.      */
  666.     public function getHydrationMode()
  667.     {
  668.         return $this->_hydrationMode;
  669.     }
  670.     /**
  671.      * Gets the list of results for the query.
  672.      *
  673.      * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
  674.      *
  675.      * @param string|int $hydrationMode
  676.      *
  677.      * @return mixed
  678.      */
  679.     public function getResult($hydrationMode self::HYDRATE_OBJECT)
  680.     {
  681.         return $this->execute(null$hydrationMode);
  682.     }
  683.     /**
  684.      * Gets the array of results for the query.
  685.      *
  686.      * Alias for execute(null, HYDRATE_ARRAY).
  687.      *
  688.      * @return mixed[]
  689.      */
  690.     public function getArrayResult()
  691.     {
  692.         return $this->execute(nullself::HYDRATE_ARRAY);
  693.     }
  694.     /**
  695.      * Gets the scalar results for the query.
  696.      *
  697.      * Alias for execute(null, HYDRATE_SCALAR).
  698.      *
  699.      * @return mixed[]
  700.      */
  701.     public function getScalarResult()
  702.     {
  703.         return $this->execute(nullself::HYDRATE_SCALAR);
  704.     }
  705.     /**
  706.      * Get exactly one result or null.
  707.      *
  708.      * @param string|int $hydrationMode
  709.      *
  710.      * @return mixed
  711.      *
  712.      * @throws NonUniqueResultException
  713.      */
  714.     public function getOneOrNullResult($hydrationMode null)
  715.     {
  716.         try {
  717.             $result $this->execute(null$hydrationMode);
  718.         } catch (NoResultException $e) {
  719.             return null;
  720.         }
  721.         if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
  722.             return null;
  723.         }
  724.         if (! is_array($result)) {
  725.             return $result;
  726.         }
  727.         if (count($result) > 1) {
  728.             throw new NonUniqueResultException();
  729.         }
  730.         return array_shift($result);
  731.     }
  732.     /**
  733.      * Gets the single result of the query.
  734.      *
  735.      * Enforces the presence as well as the uniqueness of the result.
  736.      *
  737.      * If the result is not unique, a NonUniqueResultException is thrown.
  738.      * If there is no result, a NoResultException is thrown.
  739.      *
  740.      * @param string|int $hydrationMode
  741.      *
  742.      * @return mixed
  743.      *
  744.      * @throws NonUniqueResultException If the query result is not unique.
  745.      * @throws NoResultException        If the query returned no result and hydration mode is not HYDRATE_SINGLE_SCALAR.
  746.      */
  747.     public function getSingleResult($hydrationMode null)
  748.     {
  749.         $result $this->execute(null$hydrationMode);
  750.         if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
  751.             throw new NoResultException();
  752.         }
  753.         if (! is_array($result)) {
  754.             return $result;
  755.         }
  756.         if (count($result) > 1) {
  757.             throw new NonUniqueResultException();
  758.         }
  759.         return array_shift($result);
  760.     }
  761.     /**
  762.      * Gets the single scalar result of the query.
  763.      *
  764.      * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
  765.      *
  766.      * @return mixed The scalar result.
  767.      *
  768.      * @throws NoResultException        If the query returned no result.
  769.      * @throws NonUniqueResultException If the query result is not unique.
  770.      */
  771.     public function getSingleScalarResult()
  772.     {
  773.         return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
  774.     }
  775.     /**
  776.      * Sets a query hint. If the hint name is not recognized, it is silently ignored.
  777.      *
  778.      * @param string $name  The name of the hint.
  779.      * @param mixed  $value The value of the hint.
  780.      *
  781.      * @return static This query instance.
  782.      */
  783.     public function setHint($name$value)
  784.     {
  785.         $this->_hints[$name] = $value;
  786.         return $this;
  787.     }
  788.     /**
  789.      * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
  790.      *
  791.      * @param string $name The name of the hint.
  792.      *
  793.      * @return mixed The value of the hint or FALSE, if the hint name is not recognized.
  794.      */
  795.     public function getHint($name)
  796.     {
  797.         return $this->_hints[$name] ?? false;
  798.     }
  799.     /**
  800.      * Check if the query has a hint
  801.      *
  802.      * @param string $name The name of the hint
  803.      *
  804.      * @return bool False if the query does not have any hint
  805.      */
  806.     public function hasHint($name)
  807.     {
  808.         return isset($this->_hints[$name]);
  809.     }
  810.     /**
  811.      * Return the key value map of query hints that are currently set.
  812.      *
  813.      * @return array<string,mixed>
  814.      */
  815.     public function getHints()
  816.     {
  817.         return $this->_hints;
  818.     }
  819.     /**
  820.      * Executes the query and returns an IterableResult that can be used to incrementally
  821.      * iterate over the result.
  822.      *
  823.      * @deprecated
  824.      *
  825.      * @param ArrayCollection|mixed[]|null $parameters    The query parameters.
  826.      * @param string|int|null              $hydrationMode The hydration mode to use.
  827.      *
  828.      * @return IterableResult
  829.      */
  830.     public function iterate($parameters null$hydrationMode null)
  831.     {
  832.         @trigger_error(
  833.             'Method ' __METHOD__ '() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.',
  834.             E_USER_DEPRECATED
  835.         );
  836.         if ($hydrationMode !== null) {
  837.             $this->setHydrationMode($hydrationMode);
  838.         }
  839.         if (! empty($parameters)) {
  840.             $this->setParameters($parameters);
  841.         }
  842.         $rsm  $this->getResultSetMapping();
  843.         $stmt $this->_doExecute();
  844.         return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt$rsm$this->_hints);
  845.     }
  846.     /**
  847.      * Executes the query and returns an iterable that can be used to incrementally
  848.      * iterate over the result.
  849.      *
  850.      * @param ArrayCollection|mixed[] $parameters    The query parameters.
  851.      * @param string|int|null         $hydrationMode The hydration mode to use.
  852.      *
  853.      * @return iterable<mixed>
  854.      */
  855.     public function toIterable(iterable $parameters = [], $hydrationMode null): iterable
  856.     {
  857.         if ($hydrationMode !== null) {
  858.             $this->setHydrationMode($hydrationMode);
  859.         }
  860.         if (
  861.             ($this->isCountable($parameters) && count($parameters) !== 0)
  862.             || ($parameters instanceof Traversable && iterator_count($parameters) !== 0)
  863.         ) {
  864.             $this->setParameters($parameters);
  865.         }
  866.         $rsm $this->getResultSetMapping();
  867.         if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
  868.             throw QueryException::iterateWithMixedResultNotAllowed();
  869.         }
  870.         $stmt $this->_doExecute();
  871.         return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt$rsm$this->_hints);
  872.     }
  873.     /**
  874.      * Executes the query.
  875.      *
  876.      * @param ArrayCollection|mixed[]|null $parameters    Query parameters.
  877.      * @param string|int|null              $hydrationMode Processing mode to be used during the hydration process.
  878.      *
  879.      * @return mixed
  880.      */
  881.     public function execute($parameters null$hydrationMode null)
  882.     {
  883.         if ($this->cacheable && $this->isCacheEnabled()) {
  884.             return $this->executeUsingQueryCache($parameters$hydrationMode);
  885.         }
  886.         return $this->executeIgnoreQueryCache($parameters$hydrationMode);
  887.     }
  888.     /**
  889.      * Execute query ignoring second level cache.
  890.      *
  891.      * @param ArrayCollection|mixed[]|null $parameters
  892.      * @param string|int|null              $hydrationMode
  893.      *
  894.      * @return mixed
  895.      */
  896.     private function executeIgnoreQueryCache($parameters null$hydrationMode null)
  897.     {
  898.         if ($hydrationMode !== null) {
  899.             $this->setHydrationMode($hydrationMode);
  900.         }
  901.         if (! empty($parameters)) {
  902.             $this->setParameters($parameters);
  903.         }
  904.         $setCacheEntry = static function (): void {
  905.         };
  906.         if ($this->_hydrationCacheProfile !== null) {
  907.             [$cacheKey$realCacheKey] = $this->getHydrationCacheId();
  908.             $queryCacheProfile $this->getHydrationCacheProfile();
  909.             $cache             $queryCacheProfile->getResultCacheDriver();
  910.             $result            $cache->fetch($cacheKey);
  911.             if (isset($result[$realCacheKey])) {
  912.                 return $result[$realCacheKey];
  913.             }
  914.             if (! $result) {
  915.                 $result = [];
  916.             }
  917.             $setCacheEntry = static function ($data) use ($cache$result$cacheKey$realCacheKey$queryCacheProfile): void {
  918.                 $result[$realCacheKey] = $data;
  919.                 $cache->save($cacheKey$result$queryCacheProfile->getLifetime());
  920.             };
  921.         }
  922.         $stmt $this->_doExecute();
  923.         if (is_numeric($stmt)) {
  924.             $setCacheEntry($stmt);
  925.             return $stmt;
  926.         }
  927.         $rsm  $this->getResultSetMapping();
  928.         $data $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt$rsm$this->_hints);
  929.         $setCacheEntry($data);
  930.         return $data;
  931.     }
  932.     /**
  933.      * Load from second level cache or executes the query and put into cache.
  934.      *
  935.      * @param ArrayCollection|mixed[]|null $parameters
  936.      * @param string|int|null              $hydrationMode
  937.      *
  938.      * @return mixed
  939.      */
  940.     private function executeUsingQueryCache($parameters null$hydrationMode null)
  941.     {
  942.         $rsm        $this->getResultSetMapping();
  943.         $queryCache $this->_em->getCache()->getQueryCache($this->cacheRegion);
  944.         $queryKey   = new QueryCacheKey(
  945.             $this->getHash(),
  946.             $this->lifetime,
  947.             $this->cacheMode ?: Cache::MODE_NORMAL,
  948.             $this->getTimestampKey()
  949.         );
  950.         $result $queryCache->get($queryKey$rsm$this->_hints);
  951.         if ($result !== null) {
  952.             if ($this->cacheLogger) {
  953.                 $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
  954.             }
  955.             return $result;
  956.         }
  957.         $result $this->executeIgnoreQueryCache($parameters$hydrationMode);
  958.         $cached $queryCache->put($queryKey$rsm$result$this->_hints);
  959.         if ($this->cacheLogger) {
  960.             $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);
  961.             if ($cached) {
  962.                 $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
  963.             }
  964.         }
  965.         return $result;
  966.     }
  967.     /**
  968.      * @return TimestampCacheKey|null
  969.      */
  970.     private function getTimestampKey()
  971.     {
  972.         $entityName reset($this->_resultSetMapping->aliasMap);
  973.         if (empty($entityName)) {
  974.             return null;
  975.         }
  976.         $metadata $this->_em->getClassMetadata($entityName);
  977.         return new Cache\TimestampCacheKey($metadata->rootEntityName);
  978.     }
  979.     /**
  980.      * Get the result cache id to use to store the result set cache entry.
  981.      * Will return the configured id if it exists otherwise a hash will be
  982.      * automatically generated for you.
  983.      *
  984.      * @return array<string, string> ($key, $hash)
  985.      */
  986.     protected function getHydrationCacheId()
  987.     {
  988.         $parameters = [];
  989.         foreach ($this->getParameters() as $parameter) {
  990.             $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
  991.         }
  992.         $sql                    $this->getSQL();
  993.         $queryCacheProfile      $this->getHydrationCacheProfile();
  994.         $hints                  $this->getHints();
  995.         $hints['hydrationMode'] = $this->getHydrationMode();
  996.         ksort($hints);
  997.         return $queryCacheProfile->generateCacheKeys($sql$parameters$hints);
  998.     }
  999.     /**
  1000.      * Set the result cache id to use to store the result set cache entry.
  1001.      * If this is not explicitly set by the developer then a hash is automatically
  1002.      * generated for you.
  1003.      *
  1004.      * @param string $id
  1005.      *
  1006.      * @return static This query instance.
  1007.      */
  1008.     public function setResultCacheId($id)
  1009.     {
  1010.         $this->_queryCacheProfile $this->_queryCacheProfile
  1011.             $this->_queryCacheProfile->setCacheKey($id)
  1012.             : new QueryCacheProfile(0$id$this->_em->getConfiguration()->getResultCacheImpl());
  1013.         return $this;
  1014.     }
  1015.     /**
  1016.      * Get the result cache id to use to store the result set cache entry if set.
  1017.      *
  1018.      * @deprecated
  1019.      *
  1020.      * @return string
  1021.      */
  1022.     public function getResultCacheId()
  1023.     {
  1024.         return $this->_queryCacheProfile $this->_queryCacheProfile->getCacheKey() : null;
  1025.     }
  1026.     /**
  1027.      * Executes the query and returns a the resulting Statement object.
  1028.      *
  1029.      * @return Statement The executed database statement that holds the results.
  1030.      */
  1031.     abstract protected function _doExecute();
  1032.     /**
  1033.      * Cleanup Query resource when clone is called.
  1034.      *
  1035.      * @return void
  1036.      */
  1037.     public function __clone()
  1038.     {
  1039.         $this->parameters = new ArrayCollection();
  1040.         $this->_hints = [];
  1041.         $this->_hints $this->_em->getConfiguration()->getDefaultQueryHints();
  1042.     }
  1043.     /**
  1044.      * Generates a string of currently query to use for the cache second level cache.
  1045.      *
  1046.      * @return string
  1047.      */
  1048.     protected function getHash()
  1049.     {
  1050.         $query  $this->getSQL();
  1051.         $hints  $this->getHints();
  1052.         $params array_map(function (Parameter $parameter) {
  1053.             $value $parameter->getValue();
  1054.             // Small optimization
  1055.             // Does not invoke processParameterValue for scalar value
  1056.             if (is_scalar($value)) {
  1057.                 return $value;
  1058.             }
  1059.             return $this->processParameterValue($value);
  1060.         }, $this->parameters->getValues());
  1061.         ksort($hints);
  1062.         return sha1($query '-' serialize($params) . '-' serialize($hints));
  1063.     }
  1064.     /** @param iterable<mixed> $subject */
  1065.     private function isCountable(iterable $subject): bool
  1066.     {
  1067.         return $subject instanceof Countable || is_array($subject);
  1068.     }
  1069. }