PageRenderTime 34ms CodeModel.GetById 9ms RepoModel.GetById 1ms app.codeStats 0ms

/wp-content/plugins/mailpoet/vendor-prefixed/doctrine/orm/lib/Doctrine/ORM/Query.php

https://gitlab.com/remyvianne/krowkaramel
PHP | 366 lines | 356 code | 0 blank | 10 comment | 37 complexity | 7e50b113820401d54a463c28fe00484f MD5 | raw file
  1. <?php
  2. declare (strict_types=1);
  3. namespace MailPoetVendor\Doctrine\ORM;
  4. if (!defined('ABSPATH')) exit;
  5. use MailPoetVendor\Doctrine\Common\Cache\Cache;
  6. use MailPoetVendor\Doctrine\Common\Cache\Psr6\CacheAdapter;
  7. use MailPoetVendor\Doctrine\Common\Cache\Psr6\DoctrineProvider;
  8. use MailPoetVendor\Doctrine\Common\Collections\ArrayCollection;
  9. use MailPoetVendor\Doctrine\DBAL\Cache\QueryCacheProfile;
  10. use MailPoetVendor\Doctrine\DBAL\LockMode;
  11. use MailPoetVendor\Doctrine\DBAL\Types\Type;
  12. use MailPoetVendor\Doctrine\Deprecations\Deprecation;
  13. use MailPoetVendor\Doctrine\ORM\Internal\Hydration\IterableResult;
  14. use MailPoetVendor\Doctrine\ORM\Mapping\ClassMetadata;
  15. use MailPoetVendor\Doctrine\ORM\Query\AST\DeleteStatement;
  16. use MailPoetVendor\Doctrine\ORM\Query\AST\SelectStatement;
  17. use MailPoetVendor\Doctrine\ORM\Query\AST\UpdateStatement;
  18. use MailPoetVendor\Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
  19. use MailPoetVendor\Doctrine\ORM\Query\Parameter;
  20. use MailPoetVendor\Doctrine\ORM\Query\ParameterTypeInferer;
  21. use MailPoetVendor\Doctrine\ORM\Query\Parser;
  22. use MailPoetVendor\Doctrine\ORM\Query\ParserResult;
  23. use MailPoetVendor\Doctrine\ORM\Query\QueryException;
  24. use MailPoetVendor\Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
  25. use MailPoetVendor\Psr\Cache\CacheItemPoolInterface;
  26. use function array_keys;
  27. use function array_values;
  28. use function assert;
  29. use function count;
  30. use function get_debug_type;
  31. use function in_array;
  32. use function ksort;
  33. use function md5;
  34. use function method_exists;
  35. use function reset;
  36. use function serialize;
  37. use function sha1;
  38. use function stripos;
  39. final class Query extends AbstractQuery
  40. {
  41. public const STATE_CLEAN = 1;
  42. public const STATE_DIRTY = 2;
  43. public const HINT_REFRESH = 'doctrine.refresh';
  44. public const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
  45. public const HINT_CACHE_EVICT = 'doctrine.cache.evict';
  46. public const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
  47. public const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
  48. public const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
  49. public const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
  50. public const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
  51. public const HINT_READ_ONLY = 'doctrine.readOnly';
  52. public const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
  53. public const HINT_LOCK_MODE = 'doctrine.lockMode';
  54. private $_state = self::STATE_DIRTY;
  55. private $parsedTypes = [];
  56. private $dql = null;
  57. private $parserResult;
  58. private $firstResult = null;
  59. private $maxResults = null;
  60. private $queryCache;
  61. private $expireQueryCache = \false;
  62. private $queryCacheTTL;
  63. private $useQueryCache = \true;
  64. public function getSQL()
  65. {
  66. return $this->parse()->getSqlExecutor()->getSqlStatements();
  67. }
  68. public function getAST()
  69. {
  70. $parser = new Parser($this);
  71. return $parser->getAST();
  72. }
  73. protected function getResultSetMapping()
  74. {
  75. // parse query or load from cache
  76. if ($this->_resultSetMapping === null) {
  77. $this->_resultSetMapping = $this->parse()->getResultSetMapping();
  78. }
  79. return $this->_resultSetMapping;
  80. }
  81. private function parse() : ParserResult
  82. {
  83. $types = [];
  84. foreach ($this->parameters as $parameter) {
  85. $types[$parameter->getName()] = $parameter->getType();
  86. }
  87. // Return previous parser result if the query and the filter collection are both clean
  88. if ($this->_state === self::STATE_CLEAN && $this->parsedTypes === $types && $this->_em->isFiltersStateClean()) {
  89. return $this->parserResult;
  90. }
  91. $this->_state = self::STATE_CLEAN;
  92. $this->parsedTypes = $types;
  93. $queryCache = $this->queryCache ?? $this->_em->getConfiguration()->getQueryCache();
  94. // Check query cache.
  95. if (!($this->useQueryCache && $queryCache)) {
  96. $parser = new Parser($this);
  97. $this->parserResult = $parser->parse();
  98. return $this->parserResult;
  99. }
  100. $cacheItem = $queryCache->getItem($this->getQueryCacheId());
  101. if (!$this->expireQueryCache && $cacheItem->isHit()) {
  102. $cached = $cacheItem->get();
  103. if ($cached instanceof ParserResult) {
  104. // Cache hit.
  105. $this->parserResult = $cached;
  106. return $this->parserResult;
  107. }
  108. }
  109. // Cache miss.
  110. $parser = new Parser($this);
  111. $this->parserResult = $parser->parse();
  112. $queryCache->save($cacheItem->set($this->parserResult)->expiresAfter($this->queryCacheTTL));
  113. return $this->parserResult;
  114. }
  115. protected function _doExecute()
  116. {
  117. $executor = $this->parse()->getSqlExecutor();
  118. if ($this->_queryCacheProfile) {
  119. $executor->setQueryCacheProfile($this->_queryCacheProfile);
  120. } else {
  121. $executor->removeQueryCacheProfile();
  122. }
  123. if ($this->_resultSetMapping === null) {
  124. $this->_resultSetMapping = $this->parserResult->getResultSetMapping();
  125. }
  126. // Prepare parameters
  127. $paramMappings = $this->parserResult->getParameterMappings();
  128. $paramCount = count($this->parameters);
  129. $mappingCount = count($paramMappings);
  130. if ($paramCount > $mappingCount) {
  131. throw QueryException::tooManyParameters($mappingCount, $paramCount);
  132. }
  133. if ($paramCount < $mappingCount) {
  134. throw QueryException::tooFewParameters($mappingCount, $paramCount);
  135. }
  136. // evict all cache for the entity region
  137. if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
  138. $this->evictEntityCacheRegion();
  139. }
  140. [$sqlParams, $types] = $this->processParameterMappings($paramMappings);
  141. $this->evictResultSetCache($executor, $sqlParams, $types, $this->_em->getConnection()->getParams());
  142. return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
  143. }
  144. private function evictResultSetCache(AbstractSqlExecutor $executor, array $sqlParams, array $types, array $connectionParams) : void
  145. {
  146. if ($this->_queryCacheProfile === null || !$this->getExpireResultCache()) {
  147. return;
  148. }
  149. $cache = method_exists(QueryCacheProfile::class, 'getResultCache') ? $this->_queryCacheProfile->getResultCache() : $this->_queryCacheProfile->getResultCacheDriver();
  150. assert($cache !== null);
  151. $statements = (array) $executor->getSqlStatements();
  152. // Type casted since it can either be a string or an array
  153. foreach ($statements as $statement) {
  154. $cacheKeys = $this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams);
  155. $cache instanceof CacheItemPoolInterface ? $cache->deleteItem(reset($cacheKeys)) : $cache->delete(reset($cacheKeys));
  156. }
  157. }
  158. private function evictEntityCacheRegion() : void
  159. {
  160. $AST = $this->getAST();
  161. if ($AST instanceof SelectStatement) {
  162. throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
  163. }
  164. $className = $AST instanceof DeleteStatement ? $AST->deleteClause->abstractSchemaName : $AST->updateClause->abstractSchemaName;
  165. $this->_em->getCache()->evictEntityRegion($className);
  166. }
  167. private function processParameterMappings(array $paramMappings) : array
  168. {
  169. $sqlParams = [];
  170. $types = [];
  171. foreach ($this->parameters as $parameter) {
  172. $key = $parameter->getName();
  173. if (!isset($paramMappings[$key])) {
  174. throw QueryException::unknownParameter($key);
  175. }
  176. [$value, $type] = $this->resolveParameterValue($parameter);
  177. foreach ($paramMappings[$key] as $position) {
  178. $types[$position] = $type;
  179. }
  180. $sqlPositions = $paramMappings[$key];
  181. // optimized multi value sql positions away for now,
  182. // they are not allowed in DQL anyways.
  183. $value = [$value];
  184. $countValue = count($value);
  185. for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
  186. $sqlParams[$sqlPositions[$i]] = $value[$i % $countValue];
  187. }
  188. }
  189. if (count($sqlParams) !== count($types)) {
  190. throw QueryException::parameterTypeMismatch();
  191. }
  192. if ($sqlParams) {
  193. ksort($sqlParams);
  194. $sqlParams = array_values($sqlParams);
  195. ksort($types);
  196. $types = array_values($types);
  197. }
  198. return [$sqlParams, $types];
  199. }
  200. private function resolveParameterValue(Parameter $parameter) : array
  201. {
  202. if ($parameter->typeWasSpecified()) {
  203. return [$parameter->getValue(), $parameter->getType()];
  204. }
  205. $key = $parameter->getName();
  206. $originalValue = $parameter->getValue();
  207. $value = $originalValue;
  208. $rsm = $this->getResultSetMapping();
  209. assert($rsm !== null);
  210. if ($value instanceof ClassMetadata && isset($rsm->metadataParameterMapping[$key])) {
  211. $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
  212. }
  213. if ($value instanceof ClassMetadata && isset($rsm->discriminatorParameters[$key])) {
  214. $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em));
  215. }
  216. $processedValue = $this->processParameterValue($value);
  217. return [$processedValue, $originalValue === $processedValue ? $parameter->getType() : ParameterTypeInferer::inferType($processedValue)];
  218. }
  219. public function setQueryCacheDriver($queryCache) : self
  220. {
  221. Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/9004', '%s is deprecated and will be removed in Doctrine 3.0. Use setQueryCache() instead.', __METHOD__);
  222. $this->queryCache = $queryCache ? CacheAdapter::wrap($queryCache) : null;
  223. return $this;
  224. }
  225. public function setQueryCache(?CacheItemPoolInterface $queryCache) : self
  226. {
  227. $this->queryCache = $queryCache;
  228. return $this;
  229. }
  230. public function useQueryCache($bool) : self
  231. {
  232. $this->useQueryCache = $bool;
  233. return $this;
  234. }
  235. public function getQueryCacheDriver() : ?Cache
  236. {
  237. Deprecation::trigger('doctrine/orm', 'https://github.com/doctrine/orm/pull/9004', '%s is deprecated and will be removed in Doctrine 3.0 without replacement.', __METHOD__);
  238. $queryCache = $this->queryCache ?? $this->_em->getConfiguration()->getQueryCache();
  239. return $queryCache ? DoctrineProvider::wrap($queryCache) : null;
  240. }
  241. public function setQueryCacheLifetime($timeToLive) : self
  242. {
  243. if ($timeToLive !== null) {
  244. $timeToLive = (int) $timeToLive;
  245. }
  246. $this->queryCacheTTL = $timeToLive;
  247. return $this;
  248. }
  249. public function getQueryCacheLifetime() : ?int
  250. {
  251. return $this->queryCacheTTL;
  252. }
  253. public function expireQueryCache($expire = \true) : self
  254. {
  255. $this->expireQueryCache = $expire;
  256. return $this;
  257. }
  258. public function getExpireQueryCache() : bool
  259. {
  260. return $this->expireQueryCache;
  261. }
  262. public function free() : void
  263. {
  264. parent::free();
  265. $this->dql = null;
  266. $this->_state = self::STATE_CLEAN;
  267. }
  268. public function setDQL($dqlQuery) : self
  269. {
  270. if ($dqlQuery !== null) {
  271. $this->dql = $dqlQuery;
  272. $this->_state = self::STATE_DIRTY;
  273. }
  274. return $this;
  275. }
  276. public function getDQL() : ?string
  277. {
  278. return $this->dql;
  279. }
  280. public function getState() : int
  281. {
  282. return $this->_state;
  283. }
  284. public function contains($dql) : bool
  285. {
  286. return stripos($this->getDQL(), $dql) !== \false;
  287. }
  288. public function setFirstResult($firstResult) : self
  289. {
  290. if ($firstResult !== null) {
  291. $firstResult = (int) $firstResult;
  292. }
  293. $this->firstResult = $firstResult;
  294. $this->_state = self::STATE_DIRTY;
  295. return $this;
  296. }
  297. public function getFirstResult() : ?int
  298. {
  299. return $this->firstResult;
  300. }
  301. public function setMaxResults($maxResults) : self
  302. {
  303. if ($maxResults !== null) {
  304. $maxResults = (int) $maxResults;
  305. }
  306. $this->maxResults = $maxResults;
  307. $this->_state = self::STATE_DIRTY;
  308. return $this;
  309. }
  310. public function getMaxResults() : ?int
  311. {
  312. return $this->maxResults;
  313. }
  314. public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT) : IterableResult
  315. {
  316. $this->setHint(self::HINT_INTERNAL_ITERATION, \true);
  317. return parent::iterate($parameters, $hydrationMode);
  318. }
  319. public function toIterable(iterable $parameters = [], $hydrationMode = self::HYDRATE_OBJECT) : iterable
  320. {
  321. $this->setHint(self::HINT_INTERNAL_ITERATION, \true);
  322. return parent::toIterable($parameters, $hydrationMode);
  323. }
  324. public function setHint($name, $value) : self
  325. {
  326. $this->_state = self::STATE_DIRTY;
  327. return parent::setHint($name, $value);
  328. }
  329. public function setHydrationMode($hydrationMode) : self
  330. {
  331. $this->_state = self::STATE_DIRTY;
  332. return parent::setHydrationMode($hydrationMode);
  333. }
  334. public function setLockMode($lockMode) : self
  335. {
  336. if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], \true)) {
  337. if (!$this->_em->getConnection()->isTransactionActive()) {
  338. throw TransactionRequiredException::transactionRequired();
  339. }
  340. }
  341. $this->setHint(self::HINT_LOCK_MODE, $lockMode);
  342. return $this;
  343. }
  344. public function getLockMode() : ?int
  345. {
  346. $lockMode = $this->getHint(self::HINT_LOCK_MODE);
  347. if ($lockMode === \false) {
  348. return null;
  349. }
  350. return $lockMode;
  351. }
  352. protected function getQueryCacheId() : string
  353. {
  354. ksort($this->_hints);
  355. return md5($this->getDQL() . serialize($this->_hints) . '&platform=' . get_debug_type($this->getEntityManager()->getConnection()->getDatabasePlatform()) . ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') . '&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults . '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT');
  356. }
  357. protected function getHash() : string
  358. {
  359. return sha1(parent::getHash() . '-' . $this->firstResult . '-' . $this->maxResults);
  360. }
  361. public function __clone()
  362. {
  363. parent::__clone();
  364. $this->_state = self::STATE_DIRTY;
  365. }
  366. }