<?php
declare (strict_types=1);
namespace MailPoetVendor\Doctrine\ORM;
if (!defined('ABSPATH')) exit;
use Countable;
use MailPoetVendor\Doctrine\Common\Cache\Psr6\CacheAdapter;
use MailPoetVendor\Doctrine\Common\Cache\Psr6\DoctrineProvider;
use MailPoetVendor\Doctrine\Common\Collections\ArrayCollection;
use MailPoetVendor\Doctrine\Common\Collections\Collection;
use MailPoetVendor\Doctrine\DBAL\Cache\QueryCacheProfile;
use MailPoetVendor\Doctrine\DBAL\Result;
use MailPoetVendor\Doctrine\Deprecations\Deprecation;
use MailPoetVendor\Doctrine\ORM\Cache\Exception\InvalidResultCacheDriver;
use MailPoetVendor\Doctrine\ORM\Cache\Logging\CacheLogger;
use MailPoetVendor\Doctrine\ORM\Cache\QueryCacheKey;
use MailPoetVendor\Doctrine\ORM\Cache\TimestampCacheKey;
use MailPoetVendor\Doctrine\ORM\Internal\Hydration\IterableResult;
use MailPoetVendor\Doctrine\ORM\Mapping\MappingException as ORMMappingException;
use MailPoetVendor\Doctrine\ORM\Query\Parameter;
use MailPoetVendor\Doctrine\ORM\Query\QueryException;
use MailPoetVendor\Doctrine\ORM\Query\ResultSetMapping;
use MailPoetVendor\Doctrine\Persistence\Mapping\MappingException;
use LogicException;
use MailPoetVendor\Psr\Cache\CacheItemPoolInterface;
use Traversable;
use function array_map;
use function array_shift;
use function assert;
use function count;
use function is_array;
use function is_numeric;
use function is_object;
use function is_scalar;
use function iterator_count;
use function iterator_to_array;
use function ksort;
use function method_exists;
use function reset;
use function serialize;
use function sha1;
abstract class AbstractQuery
{
 public const HYDRATE_OBJECT = 1;
 public const HYDRATE_ARRAY = 2;
 public const HYDRATE_SCALAR = 3;
 public const HYDRATE_SINGLE_SCALAR = 4;
 public const HYDRATE_SIMPLEOBJECT = 5;
 public const HYDRATE_SCALAR_COLUMN = 6;
 protected $parameters;
 protected $_resultSetMapping;
 protected $_em;
 protected $_hints = [];
 protected $_hydrationMode = self::HYDRATE_OBJECT;
 protected $_queryCacheProfile;
 protected $_expireResultCache = \false;
 protected $_hydrationCacheProfile;
 protected $cacheable = \false;
 protected $hasCache = \false;
 protected $cacheRegion;
 protected $cacheMode;
 protected $cacheLogger;
 protected $lifetime = 0;
 public function __construct(EntityManagerInterface $em)
 {
 $this->_em = $em;
 $this->parameters = new ArrayCollection();
 $this->_hints = $em->getConfiguration()->getDefaultQueryHints();
 $this->hasCache = $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
 if ($this->hasCache) {
 $this->cacheLogger = $em->getConfiguration()->getSecondLevelCacheConfiguration()->getCacheLogger();
 }
 }
 public function setCacheable($cacheable)
 {
 $this->cacheable = (bool) $cacheable;
 return $this;
 }
 public function isCacheable()
 {
 return $this->cacheable;
 }
 public function setCacheRegion($cacheRegion)
 {
 $this->cacheRegion = (string) $cacheRegion;
 return $this;
 }
 public function getCacheRegion()
 {
 return $this->cacheRegion;
 }
 protected function isCacheEnabled()
 {
 return $this->cacheable && $this->hasCache;
 }
 public function getLifetime()
 {
 return $this->lifetime;
 }
 public function setLifetime($lifetime)
 {
 $this->lifetime = (int) $lifetime;
 return $this;
 }
 public function getCacheMode()
 {
 return $this->cacheMode;
 }
 public function setCacheMode($cacheMode)
 {
 $this->cacheMode = (int) $cacheMode;
 return $this;
 }
 public abstract function getSQL();
 public function getEntityManager()
 {
 return $this->_em;
 }
 public function free()
 {
 $this->parameters = new ArrayCollection();
 $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints();
 }
 public function getParameters()
 {
 return $this->parameters;
 }
 public function getParameter($key)
 {
 $key = Query\Parameter::normalizeName($key);
 $filteredParameters = $this->parameters->filter(static function (Query\Parameter $parameter) use($key) : bool {
 $parameterName = $parameter->getName();
 return $key === $parameterName;
 });
 return !$filteredParameters->isEmpty() ? $filteredParameters->first() : null;
 }
 public function setParameters($parameters)
 {
 // BC compatibility with 2.3-
 if (is_array($parameters)) {
 $parameterCollection = new ArrayCollection();
 foreach ($parameters as $key => $value) {
 $parameterCollection->add(new Parameter($key, $value));
 }
 $parameters = $parameterCollection;
 }
 $this->parameters = $parameters;
 return $this;
 }
 public function setParameter($key, $value, $type = null)
 {
 $existingParameter = $this->getParameter($key);
 if ($existingParameter !== null) {
 $existingParameter->setValue($value, $type);
 return $this;
 }
 $this->parameters->add(new Parameter($key, $value, $type));
 return $this;
 }
 public function processParameterValue($value)
 {
 if (is_scalar($value)) {
 return $value;
 }
 if ($value instanceof Collection) {
 $value = iterator_to_array($value);
 }
 if (is_array($value)) {
 $value = $this->processArrayParameterValue($value);
 return $value;
 }
 if ($value instanceof Mapping\ClassMetadata) {
 return $value->name;
 }
 if (!is_object($value)) {
 return $value;
 }
 try {
 $value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value);
 if ($value === null) {
 throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
 }
 } catch (MappingException|ORMMappingException $e) {
 $value = $this->potentiallyProcessIterable($value);
 }
 return $value;
 }
 private function potentiallyProcessIterable($value)
 {
 if ($value instanceof Traversable) {
 $value = iterator_to_array($value);
 $value = $this->processArrayParameterValue($value);
 }
 return $value;
 }
 private function processArrayParameterValue(array $value) : array
 {
 foreach ($value as $key => $paramValue) {
 $paramValue = $this->processParameterValue($paramValue);
 $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
 }
 return $value;
 }
 public function setResultSetMapping(Query\ResultSetMapping $rsm)
 {
 $this->translateNamespaces($rsm);
 $this->_resultSetMapping = $rsm;
 return $this;
 }
 protected function getResultSetMapping()
 {
 return $this->_resultSetMapping;
 }
 private function translateNamespaces(Query\ResultSetMapping $rsm) : void
 {
 $translate = function ($alias) : string {
 return $this->_em->getClassMetadata($alias)->getName();
 };
 $rsm->aliasMap = array_map($translate, $rsm->aliasMap);
 $rsm->declaringClasses = array_map($translate, $rsm->declaringClasses);
 }
 public function setHydrationCacheProfile(?QueryCacheProfile $profile = null)
 {
 if ($profile === null) {
 $this->_hydrationCacheProfile = null;
 return $this;
 }
 // DBAL 2
 if (!method_exists(QueryCacheProfile::class, 'setResultCache')) {
 if (!$profile->getResultCacheDriver()) {
 $defaultHydrationCacheImpl = $this->_em->getConfiguration()->getHydrationCache();
 if ($defaultHydrationCacheImpl) {
 $profile = $profile->setResultCacheDriver(DoctrineProvider::wrap($defaultHydrationCacheImpl));
 }
 }
 } elseif (!$profile->getResultCache()) {
 $defaultHydrationCacheImpl = $this->_em->getConfiguration()->getHydrationCache();
 if ($defaultHydrationCacheImpl) {
 $profile = $profile->setResultCache($defaultHydrationCacheImpl);
 }
 }
 $this->_hydrationCacheProfile = $profile;
 return $this;
 }
 public function getHydrationCacheProfile()
 {
 return $this->_hydrationCacheProfile;
 }
 public function setResultCacheProfile(?QueryCacheProfile $profile = null)
 {
 if ($profile === null) {
 $this->_queryCacheProfile = null;
 return $this;
 }
 // DBAL 2
 if (!method_exists(QueryCacheProfile::class, 'setResultCache')) {
 if (!$profile->getResultCacheDriver()) {
 $defaultResultCacheDriver = $this->_em->getConfiguration()->getResultCache();
 if ($defaultResultCacheDriver) {
 $profile = $profile->setResultCacheDriver(DoctrineProvider::wrap($defaultResultCacheDriver));
 }
 }
 } elseif (!$profile->getResultCache()) {
 $defaultResultCache = $this->_em->getConfiguration()->getResultCache();
 if ($defaultResultCache) {
 $profile = $profile->setResultCache($defaultResultCache);
 }
 }
 $this->_queryCacheProfile = $profile;
 return $this;
 }
 public function setResultCacheDriver($resultCacheDriver = null)
 {
 if ($resultCacheDriver !== null && !$resultCacheDriver instanceof \MailPoetVendor\Doctrine\Common\Cache\Cache) {
 throw InvalidResultCacheDriver::create();
 }
 return $this->setResultCache($resultCacheDriver ? CacheAdapter::wrap($resultCacheDriver) : null);
 }
 public function setResultCache(?CacheItemPoolInterface $resultCache = null)
 {
 if ($resultCache === null) {
 if ($this->_queryCacheProfile) {
 $this->_queryCacheProfile = new QueryCacheProfile($this->_queryCacheProfile->getLifetime(), $this->_queryCacheProfile->getCacheKey());
 }
 return $this;
 }
 // DBAL 2
 if (!method_exists(QueryCacheProfile::class, 'setResultCache')) {
 $resultCacheDriver = DoctrineProvider::wrap($resultCache);
 $this->_queryCacheProfile = $this->_queryCacheProfile ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver) : new QueryCacheProfile(0, null, $resultCacheDriver);
 return $this;
 }
 $this->_queryCacheProfile = $this->_queryCacheProfile ? $this->_queryCacheProfile->setResultCache($resultCache) : new QueryCacheProfile(0, null, $resultCache);
 return $this;
 }
 public function getResultCacheDriver()
 {
 if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
 return $this->_queryCacheProfile->getResultCacheDriver();
 }
 return $this->_em->getConfiguration()->getResultCacheImpl();
 }
 public function useResultCache($useCache, $lifetime = null, $resultCacheId = null)
 {
 return $useCache ? $this->enableResultCache($lifetime, $resultCacheId) : $this->disableResultCache();
 }
 public function enableResultCache(?int $lifetime = null, ?string $resultCacheId = null) : self
 {
 $this->setResultCacheLifetime($lifetime);
 $this->setResultCacheId($resultCacheId);
 return $this;
 }
 public function disableResultCache() : self
 {
 $this->_queryCacheProfile = null;
 return $this;
 }
 public function setResultCacheLifetime($lifetime)
 {
 $lifetime = (int) $lifetime;
 if ($this->_queryCacheProfile) {
 $this->_queryCacheProfile = $this->_queryCacheProfile->setLifetime($lifetime);
 return $this;
 }
 $this->_queryCacheProfile = new QueryCacheProfile($lifetime);
 $cache = $this->_em->getConfiguration()->getResultCache();
 if (!$cache) {
 return $this;
 }
 // Compatibility for DBAL 2
 if (!method_exists($this->_queryCacheProfile, 'setResultCache')) {
 $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCacheDriver(DoctrineProvider::wrap($cache));
 return $this;
 }
 $this->_queryCacheProfile = $this->_queryCacheProfile->setResultCache($cache);
 return $this;
 }
 public function getResultCacheLifetime()
 {
 return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
 }
 public function expireResultCache($expire = \true)
 {
 $this->_expireResultCache = $expire;
 return $this;
 }
 public function getExpireResultCache()
 {
 return $this->_expireResultCache;
 }
 public function getQueryCacheProfile()
 {
 return $this->_queryCacheProfile;
 }
 public function setFetchMode($class, $assocName, $fetchMode)
 {
 if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
 $fetchMode = Mapping\ClassMetadata::FETCH_LAZY;
 }
 $this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
 return $this;
 }
 public function setHydrationMode($hydrationMode)
 {
 $this->_hydrationMode = $hydrationMode;
 return $this;
 }
 public function getHydrationMode()
 {
 return $this->_hydrationMode;
 }
 public function getResult($hydrationMode = self::HYDRATE_OBJECT)
 {
 return $this->execute(null, $hydrationMode);
 }
 public function getArrayResult()
 {
 return $this->execute(null, self::HYDRATE_ARRAY);
 }
 public function getSingleColumnResult()
 {
 return $this->execute(null, self::HYDRATE_SCALAR_COLUMN);
 }
 public function getScalarResult()
 {
 return $this->execute(null, self::HYDRATE_SCALAR);
 }
 public function getOneOrNullResult($hydrationMode = null)
 {
 try {
 $result = $this->execute(null, $hydrationMode);
 } catch (NoResultException $e) {
 return null;
 }
 if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && !$result) {
 return null;
 }
 if (!is_array($result)) {
 return $result;
 }
 if (count($result) > 1) {
 throw new NonUniqueResultException();
 }
 return array_shift($result);
 }
 public function getSingleResult($hydrationMode = null)
 {
 $result = $this->execute(null, $hydrationMode);
 if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && !$result) {
 throw new NoResultException();
 }
 if (!is_array($result)) {
 return $result;
 }
 if (count($result) > 1) {
 throw new NonUniqueResultException();
 }
 return array_shift($result);
 }
 public function getSingleScalarResult()
 {
 return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
 }
 public function setHint($name, $value)
 {
 $this->_hints[$name] = $value;
 return $this;
 }
 public function getHint($name)
 {
 return $this->_hints[$name] ?? \false;
 }
 public function hasHint($name)
 {
 return isset($this->_hints[$name]);
 }
 public function getHints()
 {
 return $this->_hints;
 }
 public function iterate($parameters = null, $hydrationMode = null)
 {
 Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/issues/8463', 'Method %s() is deprecated and will be removed in Doctrine ORM 3.0. Use toIterable() instead.', __METHOD__);
 if ($hydrationMode !== null) {
 $this->setHydrationMode($hydrationMode);
 }
 if (!empty($parameters)) {
 $this->setParameters($parameters);
 }
 $rsm = $this->getResultSetMapping();
 if ($rsm === null) {
 throw new LogicException('Uninitialized result set mapping.');
 }
 $stmt = $this->_doExecute();
 return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints);
 }
 public function toIterable(iterable $parameters = [], $hydrationMode = null) : iterable
 {
 if ($hydrationMode !== null) {
 $this->setHydrationMode($hydrationMode);
 }
 if ($this->isCountable($parameters) && count($parameters) !== 0 || $parameters instanceof Traversable && iterator_count($parameters) !== 0) {
 $this->setParameters($parameters);
 }
 $rsm = $this->getResultSetMapping();
 if ($rsm === null) {
 throw new LogicException('Uninitialized result set mapping.');
 }
 if ($rsm->isMixed && count($rsm->scalarMappings) > 0) {
 throw QueryException::iterateWithMixedResultNotAllowed();
 }
 $stmt = $this->_doExecute();
 return $this->_em->newHydrator($this->_hydrationMode)->toIterable($stmt, $rsm, $this->_hints);
 }
 public function execute($parameters = null, $hydrationMode = null)
 {
 if ($this->cacheable && $this->isCacheEnabled()) {
 return $this->executeUsingQueryCache($parameters, $hydrationMode);
 }
 return $this->executeIgnoreQueryCache($parameters, $hydrationMode);
 }
 private function executeIgnoreQueryCache($parameters = null, $hydrationMode = null)
 {
 if ($hydrationMode !== null) {
 $this->setHydrationMode($hydrationMode);
 }
 if (!empty($parameters)) {
 $this->setParameters($parameters);
 }
 $setCacheEntry = static function ($data) : void {
 };
 if ($this->_hydrationCacheProfile !== null) {
 [$cacheKey, $realCacheKey] = $this->getHydrationCacheId();
 $cache = $this->getHydrationCache();
 $cacheItem = $cache->getItem($cacheKey);
 $result = $cacheItem->isHit() ? $cacheItem->get() : [];
 if (isset($result[$realCacheKey])) {
 return $result[$realCacheKey];
 }
 if (!$result) {
 $result = [];
 }
 $setCacheEntry = static function ($data) use($cache, $result, $cacheItem, $realCacheKey) : void {
 $cache->save($cacheItem->set($result + [$realCacheKey => $data]));
 };
 }
 $stmt = $this->_doExecute();
 if (is_numeric($stmt)) {
 $setCacheEntry($stmt);
 return $stmt;
 }
 $rsm = $this->getResultSetMapping();
 if ($rsm === null) {
 throw new LogicException('Uninitialized result set mapping.');
 }
 $data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt, $rsm, $this->_hints);
 $setCacheEntry($data);
 return $data;
 }
 private function getHydrationCache() : CacheItemPoolInterface
 {
 assert($this->_hydrationCacheProfile !== null);
 // Support for DBAL 2
 if (!method_exists($this->_hydrationCacheProfile, 'getResultCache')) {
 $cacheDriver = $this->_hydrationCacheProfile->getResultCacheDriver();
 assert($cacheDriver !== null);
 return CacheAdapter::wrap($cacheDriver);
 }
 $cache = $this->_hydrationCacheProfile->getResultCache();
 assert($cache !== null);
 return $cache;
 }
 private function executeUsingQueryCache($parameters = null, $hydrationMode = null)
 {
 $rsm = $this->getResultSetMapping();
 if ($rsm === null) {
 throw new LogicException('Uninitialized result set mapping.');
 }
 $queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion);
 $queryKey = new QueryCacheKey($this->getHash(), $this->lifetime, $this->cacheMode ?: Cache::MODE_NORMAL, $this->getTimestampKey());
 $result = $queryCache->get($queryKey, $rsm, $this->_hints);
 if ($result !== null) {
 if ($this->cacheLogger) {
 $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
 }
 return $result;
 }
 $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode);
 $cached = $queryCache->put($queryKey, $rsm, $result, $this->_hints);
 if ($this->cacheLogger) {
 $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);
 if ($cached) {
 $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
 }
 }
 return $result;
 }
 private function getTimestampKey() : ?TimestampCacheKey
 {
 assert($this->_resultSetMapping !== null);
 $entityName = reset($this->_resultSetMapping->aliasMap);
 if (empty($entityName)) {
 return null;
 }
 $metadata = $this->_em->getClassMetadata($entityName);
 return new Cache\TimestampCacheKey($metadata->rootEntityName);
 }
 protected function getHydrationCacheId()
 {
 $parameters = [];
 foreach ($this->getParameters() as $parameter) {
 $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
 }
 $sql = $this->getSQL();
 $queryCacheProfile = $this->getHydrationCacheProfile();
 $hints = $this->getHints();
 $hints['hydrationMode'] = $this->getHydrationMode();
 ksort($hints);
 assert($queryCacheProfile !== null);
 return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints);
 }
 public function setResultCacheId($id)
 {
 if (!$this->_queryCacheProfile) {
 return $this->setResultCacheProfile(new QueryCacheProfile(0, $id));
 }
 $this->_queryCacheProfile = $this->_queryCacheProfile->setCacheKey($id);
 return $this;
 }
 public function getResultCacheId()
 {
 return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
 }
 protected abstract function _doExecute();
 public function __clone()
 {
 $this->parameters = new ArrayCollection();
 $this->_hints = [];
 $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints();
 }
 protected function getHash()
 {
 $query = $this->getSQL();
 $hints = $this->getHints();
 $params = array_map(function (Parameter $parameter) {
 $value = $parameter->getValue();
 // Small optimization
 // Does not invoke processParameterValue for scalar value
 if (is_scalar($value)) {
 return $value;
 }
 return $this->processParameterValue($value);
 }, $this->parameters->getValues());
 ksort($hints);
 return sha1($query . '-' . serialize($params) . '-' . serialize($hints));
 }
 private function isCountable(iterable $subject) : bool
 {
 return $subject instanceof Countable || is_array($subject);
 }
}
