![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/mautic.corals.io/vendor/doctrine/orm/src/Tools/Pagination/ |
<?php declare(strict_types=1); namespace Doctrine\ORM\Tools\Pagination; use ArrayIterator; use Countable; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Internal\SQLResultCasing; use Doctrine\ORM\NoResultException; use Doctrine\ORM\Query; use Doctrine\ORM\Query\Parameter; use Doctrine\ORM\Query\Parser; use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\QueryBuilder; use IteratorAggregate; use ReturnTypeWillChange; use Traversable; use function array_key_exists; use function array_map; use function array_sum; use function assert; use function is_string; /** * The paginator can handle various complex scenarios with DQL. * * @template-covariant T * @implements IteratorAggregate<array-key, T> */ class Paginator implements Countable, IteratorAggregate { use SQLResultCasing; public const HINT_ENABLE_DISTINCT = 'paginator.distinct.enable'; /** @var Query */ private $query; /** @var bool */ private $fetchJoinCollection; /** @var bool|null */ private $useOutputWalkers; /** @var int|null */ private $count; /** * @param Query|QueryBuilder $query A Doctrine ORM query or query builder. * @param bool $fetchJoinCollection Whether the query joins a collection (true by default). */ public function __construct($query, $fetchJoinCollection = true) { if ($query instanceof QueryBuilder) { $query = $query->getQuery(); } $this->query = $query; $this->fetchJoinCollection = (bool) $fetchJoinCollection; } /** * Returns the query. * * @return Query */ public function getQuery() { return $this->query; } /** * Returns whether the query joins a collection. * * @return bool Whether the query joins a collection. */ public function getFetchJoinCollection() { return $this->fetchJoinCollection; } /** * Returns whether the paginator will use an output walker. * * @return bool|null */ public function getUseOutputWalkers() { return $this->useOutputWalkers; } /** * Sets whether the paginator will use an output walker. * * @param bool|null $useOutputWalkers * * @return $this * @psalm-return static<T> */ public function setUseOutputWalkers($useOutputWalkers) { $this->useOutputWalkers = $useOutputWalkers; return $this; } /** * {@inheritDoc} * * @return int */ #[ReturnTypeWillChange] public function count() { if ($this->count === null) { try { $this->count = (int) array_sum(array_map('current', $this->getCountQuery()->getScalarResult())); } catch (NoResultException $e) { $this->count = 0; } } return $this->count; } /** * {@inheritDoc} * * @return Traversable * @psalm-return Traversable<array-key, T> */ #[ReturnTypeWillChange] public function getIterator() { $offset = $this->query->getFirstResult(); $length = $this->query->getMaxResults(); if ($this->fetchJoinCollection && $length !== null) { $subQuery = $this->cloneQuery($this->query); if ($this->useOutputWalker($subQuery)) { $subQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, LimitSubqueryOutputWalker::class); } else { $this->appendTreeWalker($subQuery, LimitSubqueryWalker::class); $this->unbindUnusedQueryParams($subQuery); } $subQuery->setFirstResult($offset)->setMaxResults($length); $foundIdRows = $subQuery->getScalarResult(); // don't do this for an empty id array if ($foundIdRows === []) { return new ArrayIterator([]); } $whereInQuery = $this->cloneQuery($this->query); $ids = array_map('current', $foundIdRows); $this->appendTreeWalker($whereInQuery, WhereInWalker::class); $whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_HAS_IDS, true); $whereInQuery->setFirstResult(0)->setMaxResults(null); $whereInQuery->setCacheable($this->query->isCacheable()); $databaseIds = $this->convertWhereInIdentifiersToDatabaseValues($ids); $whereInQuery->setParameter(WhereInWalker::PAGINATOR_ID_ALIAS, $databaseIds); $result = $whereInQuery->getResult($this->query->getHydrationMode()); } else { $result = $this->cloneQuery($this->query) ->setMaxResults($length) ->setFirstResult($offset) ->setCacheable($this->query->isCacheable()) ->getResult($this->query->getHydrationMode()); } return new ArrayIterator($result); } private function cloneQuery(Query $query): Query { $cloneQuery = clone $query; $cloneQuery->setParameters(clone $query->getParameters()); $cloneQuery->setCacheable(false); foreach ($query->getHints() as $name => $value) { $cloneQuery->setHint($name, $value); } return $cloneQuery; } /** * Determines whether to use an output walker for the query. */ private function useOutputWalker(Query $query): bool { if ($this->useOutputWalkers === null) { return (bool) $query->getHint(Query::HINT_CUSTOM_OUTPUT_WALKER) === false; } return $this->useOutputWalkers; } /** * Appends a custom tree walker to the tree walkers hint. * * @psalm-param class-string $walkerClass */ private function appendTreeWalker(Query $query, string $walkerClass): void { $hints = $query->getHint(Query::HINT_CUSTOM_TREE_WALKERS); if ($hints === false) { $hints = []; } $hints[] = $walkerClass; $query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, $hints); } /** * Returns Query prepared to count. */ private function getCountQuery(): Query { $countQuery = $this->cloneQuery($this->query); if (! $countQuery->hasHint(CountWalker::HINT_DISTINCT)) { $countQuery->setHint(CountWalker::HINT_DISTINCT, true); } if ($this->useOutputWalker($countQuery)) { $platform = $countQuery->getEntityManager()->getConnection()->getDatabasePlatform(); // law of demeter win $rsm = new ResultSetMapping(); $rsm->addScalarResult($this->getSQLResultCasing($platform, 'dctrn_count'), 'count'); $countQuery->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, CountOutputWalker::class); $countQuery->setResultSetMapping($rsm); } else { $this->appendTreeWalker($countQuery, CountWalker::class); $this->unbindUnusedQueryParams($countQuery); } $countQuery->setFirstResult(0)->setMaxResults(null); return $countQuery; } private function unbindUnusedQueryParams(Query $query): void { $parser = new Parser($query); $parameterMappings = $parser->parse()->getParameterMappings(); /** @var Collection|Parameter[] $parameters */ $parameters = $query->getParameters(); foreach ($parameters as $key => $parameter) { $parameterName = $parameter->getName(); if (! (isset($parameterMappings[$parameterName]) || array_key_exists($parameterName, $parameterMappings))) { unset($parameters[$key]); } } $query->setParameters($parameters); } /** * @param mixed[] $identifiers * * @return mixed[] */ private function convertWhereInIdentifiersToDatabaseValues(array $identifiers): array { $query = $this->cloneQuery($this->query); $query->setHint(Query::HINT_CUSTOM_OUTPUT_WALKER, RootTypeWalker::class); $connection = $this->query->getEntityManager()->getConnection(); $type = $query->getSQL(); assert(is_string($type)); return array_map(static function ($id) use ($connection, $type) { return $connection->convertToDatabaseValue($id, $type); }, $identifiers); } }