/lib/internal/Magento/Framework/Search/Request/Cleaner.php

https://gitlab.com/crazybutterfly815/magento2 · PHP · 245 lines · 162 code · 15 blank · 68 comment · 29 complexity · 4a8d6d0e7ef5e861e8faa6dfbbd252ce MD5 · raw file

  1. <?php
  2. /**
  3. * Copyright © 2016 Magento. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Search\Request;
  7. use Magento\Framework\Exception\StateException;
  8. use Magento\Framework\Search\Request\Aggregation\StatusInterface as AggregationStatus;
  9. use Magento\Framework\Phrase;
  10. class Cleaner
  11. {
  12. /**
  13. * @var array
  14. */
  15. private $requestData;
  16. /**
  17. * @var array
  18. */
  19. private $mappedQueries;
  20. /**
  21. * @var array
  22. */
  23. private $mappedFilters;
  24. /**
  25. * @var AggregationStatus
  26. */
  27. private $aggregationStatus;
  28. /**
  29. * Cleaner constructor
  30. *
  31. * @param AggregationStatus $aggregationStatus
  32. */
  33. public function __construct(AggregationStatus $aggregationStatus)
  34. {
  35. $this->aggregationStatus = $aggregationStatus;
  36. }
  37. /**
  38. * Clean not binder queries and filters
  39. *
  40. * @param array $requestData
  41. * @return array
  42. */
  43. public function clean(array $requestData)
  44. {
  45. $this->clear();
  46. $this->requestData = $requestData;
  47. $this->cleanQuery($requestData['query']);
  48. $this->cleanAggregations();
  49. $requestData = $this->requestData;
  50. $this->clear();
  51. if (empty($requestData['queries']) && empty($requestData['filters'])) {
  52. throw new EmptyRequestDataException(new Phrase('Request query and filters are not set'));
  53. }
  54. return $requestData;
  55. }
  56. /**
  57. * Clear don't bind queries
  58. *
  59. * @param string $queryName
  60. * @return void
  61. * @throws StateException
  62. * @throws \Exception
  63. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  64. * @SuppressWarnings(PHPMD.UnusedLocalVariable)
  65. */
  66. private function cleanQuery($queryName)
  67. {
  68. if (!isset($this->requestData['queries'][$queryName])) {
  69. throw new \Exception('Query ' . $queryName . ' does not exist');
  70. } elseif (in_array($queryName, $this->mappedQueries)) {
  71. throw new StateException(
  72. new Phrase('Cycle found. Query %1 already used in request hierarchy', [$queryName])
  73. );
  74. }
  75. $this->mappedQueries[] = $queryName;
  76. $query = $this->requestData['queries'][$queryName];
  77. switch ($query['type']) {
  78. case QueryInterface::TYPE_BOOL:
  79. $queryReference = $this->processQueryReference($query['queryReference']);
  80. if (empty($queryReference)) {
  81. unset($this->requestData['queries'][$queryName]);
  82. } else {
  83. $this->requestData['queries'][$queryName]['queryReference'] = array_values($queryReference);
  84. }
  85. break;
  86. case QueryInterface::TYPE_MATCH:
  87. if (!array_key_exists('is_bind', $query)) {
  88. unset($this->requestData['queries'][$queryName]);
  89. }
  90. break;
  91. case QueryInterface::TYPE_FILTER:
  92. if (isset($query['queryReference'][0])) {
  93. $fQueryName = $query['queryReference'][0]['ref'];
  94. $this->cleanQuery($fQueryName);
  95. if (!isset($this->requestData['queries'][$fQueryName])) {
  96. unset($this->requestData['queries'][$queryName]);
  97. }
  98. } elseif (isset($query['filterReference'][0])) {
  99. $filterName = $query['filterReference'][0]['ref'];
  100. $this->cleanFilter($filterName);
  101. if (!isset($this->requestData['filters'][$filterName])) {
  102. unset($this->requestData['queries'][$queryName]);
  103. }
  104. } else {
  105. throw new \Exception('Reference is not provided');
  106. }
  107. break;
  108. default:
  109. throw new \InvalidArgumentException('Invalid query type');
  110. }
  111. }
  112. /**
  113. * Clean aggregations if we don't need to process them
  114. *
  115. * @return void
  116. */
  117. private function cleanAggregations()
  118. {
  119. if (!$this->aggregationStatus->isEnabled()) {
  120. $this->requestData['aggregations'] = [];
  121. } else {
  122. if (array_key_exists('aggregations', $this->requestData) && is_array($this->requestData['aggregations'])) {
  123. foreach ($this->requestData['aggregations'] as $aggregationName => $aggregationValue) {
  124. switch ($aggregationValue['type']) {
  125. case 'dynamicBucket':
  126. if (is_string($aggregationValue['method'])
  127. && preg_match('/^\$(.+)\$$/si', $aggregationValue['method'])
  128. ) {
  129. unset($this->requestData['aggregations'][$aggregationName]);
  130. }
  131. }
  132. }
  133. }
  134. }
  135. }
  136. /**
  137. * Clear don't bind filters
  138. *
  139. * @param string $filterName
  140. * @return void
  141. * @throws StateException
  142. * @throws \Exception
  143. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  144. */
  145. private function cleanFilter($filterName)
  146. {
  147. if (!isset($this->requestData['filters'][$filterName])) {
  148. throw new \Exception('Filter ' . $filterName . ' does not exist');
  149. } elseif (in_array($filterName, $this->mappedFilters)) {
  150. throw new StateException(
  151. new Phrase('Cycle found. Filter %1 already used in request hierarchy', [$filterName])
  152. );
  153. }
  154. $this->mappedFilters[] = $filterName;
  155. $filter = $this->requestData['filters'][$filterName];
  156. switch ($filter['type']) {
  157. case FilterInterface::TYPE_WILDCARD:
  158. case FilterInterface::TYPE_TERM:
  159. if (!array_key_exists('is_bind', $filter)) {
  160. unset($this->requestData['filters'][$filterName]);
  161. }
  162. break;
  163. case FilterInterface::TYPE_RANGE:
  164. $keys = ['from', 'to'];
  165. foreach ($keys as $key) {
  166. if (isset($filter[$key]) && preg_match('/^\$(.+)\$$/si', $filter[$key])) {
  167. unset($this->requestData['filters'][$filterName][$key]);
  168. }
  169. }
  170. $filterKeys = array_keys($this->requestData['filters'][$filterName]);
  171. if (count(array_diff($keys, $filterKeys)) == count($keys)) {
  172. unset($this->requestData['filters'][$filterName]);
  173. }
  174. break;
  175. case FilterInterface::TYPE_BOOL:
  176. $filterReference = $this->processFilterReference($filter['filterReference']);
  177. if (empty($filterReference)) {
  178. unset($this->requestData['filters'][$filterName]);
  179. } else {
  180. $this->requestData['filters'][$filterName]['filterReference'] = array_values($filterReference);
  181. }
  182. break;
  183. default:
  184. throw new \InvalidArgumentException('Invalid filter type');
  185. }
  186. }
  187. /**
  188. * Aggregate Queries by clause
  189. *
  190. * @param array $queryReference
  191. * @return array
  192. */
  193. private function processQueryReference($queryReference)
  194. {
  195. foreach ($queryReference as $key => $value) {
  196. $this->cleanQuery($value['ref']);
  197. if (!isset($this->requestData['queries'][$value['ref']])) {
  198. unset($queryReference[$key]);
  199. }
  200. }
  201. return $queryReference;
  202. }
  203. /**
  204. * Aggregate Filters by clause
  205. *
  206. * @param array $filterReference
  207. * @return array
  208. */
  209. private function processFilterReference($filterReference)
  210. {
  211. foreach ($filterReference as $key => $value) {
  212. $this->cleanFilter($value['ref']);
  213. if (!isset($this->requestData['filters'][$value['ref']])) {
  214. unset($filterReference[$key]);
  215. }
  216. }
  217. return $filterReference;
  218. }
  219. /**
  220. * Clear variables to default status
  221. *
  222. * @return void
  223. */
  224. private function clear()
  225. {
  226. $this->mappedQueries = [];
  227. $this->mappedFilters = [];
  228. $this->requestData = [];
  229. }
  230. }