PluginProbe
Filter Everything — WordPress & WooCommerce Filters / 1.2.4
Filter Everything — WordPress & WooCommerce Filters v1.2.4
1.9.6 1.9.5 1.9.4 1.9.3 1.9.2.2 1.9.2.1 trunk 1.2.1 1.2.3 1.2.4 1.2.5 1.3.0 1.3.1 1.3.2 1.4.1 1.4.4 1.4.5 1.4.8 1.4.9 1.5.0 1.5.1 1.6.0 1.6.1 1.6.2 1.6.3 All 51 releases
filter-everything / src / RequestParser.php

RequestParser.php in Filter Everything — WordPress & WooCommerce Filters 1.2.4, at src/RequestParser.php

421 lines 13.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FilterEverything\Filter;
4
5 if ( ! defined('WPINC') ) {
6 wp_die();
7 }
8
9 class RequestParser
10 {
11 private $request;
12
13 private $queryVars = [];
14
15 private $separator;
16
17 public function __construct( $request )
18 {
19 $this->setRequest( $request );
20 $this->separator = FLRT_PREFIX_SEPARATOR;
21 }
22
23 private function initQueryVars(){
24 // Setup default $queryVars
25 $this->queryVars = array(
26 'queried_values' => [],
27 'segments_order' => [],
28 'wpc_logic_separators' => [],
29 'non_filter_segments' => [],
30 'error' => '',
31 );
32 }
33
34 public function getQueryVars(){
35 $this->initQueryVars();
36 $this->parseRequest();
37 $this->validateQueryVars();
38 return $this->queryVars;
39 }
40
41 private function isSlugInRequest( $slug ){
42 return ( $this->isSlugInQuery( $slug ) || $this->isSlugInPath( $slug ) );
43 }
44
45 public function detectFilterRequest(){
46 $em = Container::instance()->getEntityManager();
47 foreach ( $em->getGlobalConfiguredSlugs() as $entity_slug ){
48 if( $this->isSlugInRequest( $entity_slug ) ){
49 return true;
50 }
51 }
52 return false;
53 }
54
55 private function isSlugInPath( $entity_slug ){
56 if( mb_strpos( '/' . $this->request, '/' . $entity_slug . $this->separator ) !== false ){
57 return true;
58 }
59 return false;
60 }
61
62 private function isSlugInQuery( $entity_slug ){
63 if(
64 ( $this->queryStringParam( 'max_' . $entity_slug ) !== false )
65 ||
66 ( $this->queryStringParam( 'min_' . $entity_slug ) !== false )
67 ||
68 ( $this->queryStringParam( $entity_slug ) !== false )
69 ){
70 return true;
71 }
72 return false;
73 }
74
75 /**
76 * Result always must be checked for !== false because 0 may be returned
77 * @param $key [max_{slug}|min_{slug}|{slug}]
78 * @return false|mixed
79 */
80 public function queryStringParam( $key )
81 {
82 $container = Container::instance();
83 $get = $container->getTheGet();
84 $post = $container->getThePost();
85
86 if( isset( $post['flrt_ajax_link'] ) ){
87 $parts = parse_url( $post['flrt_ajax_link'] );
88
89 if( isset( $parts['query'] ) ){
90 parse_str( $parts['query'], $output );
91 if( isset( $output[$key] ) ){
92 return $output[$key];
93 }
94 }
95
96 }
97
98 if( isset( $get[$key] ) ){
99 return $get[$key];
100 }
101
102 return false;
103 }
104
105 private function extractValuesFromQuery( $slug ){
106 $em = Container::instance()->getEntityManager();
107 $filter = $em->getFilterBySlug( $slug, array( 'entity' ) );
108
109 $values = [];
110 // Matches numbers and decimal separator
111 $regexp = '/^([\-]?\d+(?:[\.\,]\d{1,})?)$/';
112
113 if( ( $this->queryStringParam( 'max_' . $slug ) !== false ) && $filter['entity'] === 'post_meta_num' ){
114 preg_match($regexp, $this->queryStringParam( 'max_' . $slug ), $output);
115 $values['max'] = isset( $output[1] ) ? $output[1] : false;
116 }
117
118 if( ( $this->queryStringParam( 'min_' . $slug ) !== false ) && $filter['entity'] === 'post_meta_num' ){
119 preg_match($regexp, $this->queryStringParam( 'min_' . $slug ), $output);
120 $values['min'] = isset( $output[1] ) ? $output[1] : false;
121 }
122
123 if( ( $this->queryStringParam( $slug ) !== false ) ){
124 if( $filter['entity'] !== 'post_meta_num' ){
125 $params = $this->queryStringParam( $slug );
126 $values = $this->extractQueriedValuesFromQueryString( $params, $slug );
127 }else{
128 $values[] = $this->queryStringParam( $slug );
129 }
130
131 }
132
133 unset($em);
134
135 return $values;
136 }
137
138 private function set_404( $message = '' ){
139 $this->queryVars['error'] = '404';
140 if( $message && FLRT_PLUGIN_DEBUG ){
141 echo esc_html( $message );
142 }
143 }
144
145 public function getRequest(){
146 return $this->request;
147 }
148
149 public function setRequest( $request ){
150 $this->request = urldecode( strtolower( trim( $request, '/' ) ) );
151 }
152
153 /**
154 * @return array
155 */
156 private function getPathSegments(){
157 if( $this->request ){
158 return explode('/', $this->request );
159 }
160 return [];
161 }
162
163 public function cleanUpRequestPathFromFilterSegments( $request_path ){
164
165 foreach( $this->getPathSegments() as $segment ){
166 if( $this->checkSlugInSegmentForCleaningNativePath( $segment ) ){
167 /**
168 *@improvement Maybe remove query_args also
169 */
170 $request_path = str_replace('/' . $segment, '', $request_path );
171 }
172 }
173
174 return $request_path;
175 }
176
177 public function parseRequest(){
178 /**
179 * @bug this method fires twice.
180 */
181 $pathSegments = $this->getPathSegments();
182 $em = Container::instance()->getEntityManager();
183 $fse = Container::instance()->getFilterService();
184 // Path values
185 foreach( $pathSegments as $segment ){
186
187 if( $slug = $this->getSlugFromSegment( $segment ) ){
188 $segmentParams = $this->cutParamsFromSegment( $segment, $slug );
189 // List of entity, e_name, slug should be unique for all filters
190 $filter_entity = $em->getFilterBySlug( $slug, array('entity', 'e_name', 'slug', 'in_path') );
191
192 $filter_entity['values'] = $this->extractQueriedValuesFromSegment( $segmentParams, $slug );
193 $filter_entity['founded_in_path'] = 'yes';
194 $this->queryVars['queried_values'][$slug] = $filter_entity;
195
196 $order_element = $fse->getEntityFullName( $filter_entity['entity'], $filter_entity['e_name'] );
197
198 $this->queryVars['segments_order'][] = $order_element;
199 } else {
200 $this->queryVars['non_filter_segments'][] = $segment;
201 }
202 }
203
204 // Query string values
205 foreach( $em->getConfiguredQuerySlugs() as $slug ){
206
207 if( $this->isSlugInQuery( $slug ) ){
208 $filter_entity = $em->getFilterBySlug( $slug, array('entity', 'e_name', 'slug', 'in_path') );
209 $filter_entity['values'] = $this->extractValuesFromQuery( $slug );
210 $filter_entity['founded_in_path'] = 'no';
211 $this->queryVars['queried_values'][$slug] = $filter_entity;
212 }
213 }
214
215 unset($em, $fse);
216 }
217
218 private function checkSlugInSegmentForCleaningNativePath( $segment ){
219 $em = Container::instance()->getEntityManager();
220 foreach( $em->getConfiguredPathSlugs() as $key => $slug ){
221 if( mb_strpos( $segment, $slug . $this->separator ) === 0 ){
222 return $slug;
223 }
224 }
225 return false;
226 }
227
228 private function getSlugFromSegment( $segment ){
229 $em = Container::instance()->getEntityManager();
230 foreach( $em->getConfiguredPathSlugs() as $key => $slug ){
231 if( mb_strpos( $segment, $slug . $this->separator ) === 0 ){
232 return $slug;
233 }
234 }
235 return false;
236 }
237
238 private function cutParamsFromSegment( $segment, $slug ){
239 return mb_substr( $segment, mb_strlen( $slug . $this->separator ) );
240 }
241
242 private function checkValuesOrder( $segmentParams, $sep ){
243 $fse = Container::instance()->getFilterService();
244 $terms = explode( $sep, $segmentParams );
245 $terms = $fse->sortTerms($terms);
246 $sortedParams = implode( $sep, $terms );
247
248 if( $segmentParams !== $sortedParams ){
249 return false;
250 }
251
252 return true;
253 }
254
255 /**
256 * @param string $segmentParams specially formatted sting like two#or#or-or-three#and
257 * @param array $filters filters arrays with logic value
258 */
259 private function extractLogicSeparator( $segmentParams, $filters ){
260 $fse = Container::instance()->getFilterService();
261 foreach( $filters as $filter ){
262 $logicSeparator = $fse->getLogicSeparator( $filter ); // -or- | -and-
263 if( mb_strpos( $segmentParams, $logicSeparator ) !== false ){
264 $this->queryVars['wpc_logic_separators'][$filter['slug']] = $filter['logic'];
265 return $logicSeparator;
266 }
267 }
268
269 return false;
270 }
271
272 private function extractQueriedValuesFromQueryString( $filterParams, $slug ){
273 // $filterParams = accessories;tshirts
274 $em = Container::instance()->getEntityManager();
275 $allEntityTerms = $em->getEntityAllTermsSlugs( $slug );
276 $queriedValues = $em->safeExplodeFilterValues( $filterParams, $slug, FLRT_QUERY_TERMS_SEPARATOR );
277 $queriedValues = $em->safeImplodeFilterValues( $queriedValues, FLRT_QUERY_TERMS_SEPARATOR );
278
279 foreach ( $queriedValues as $k => $value ) {
280 if ( ! in_array($value, $allEntityTerms) ) {
281 unset( $queriedValues[$k] );
282 $this->set_404( 'Term does not exist - ' . $value );
283 }
284 }
285
286 // Check duplicates
287 if( flrt_array_contains_duplicate( $queriedValues ) ){
288 $this->set_404('Param duplicates');
289 $queriedValues = array_unique($queriedValues);
290 }
291
292 unset( $em );
293
294 return $queriedValues;
295 }
296
297 /**
298 * @param string $segmentParams
299 * @param string $slug
300 * @return false|array
301 */
302 private function extractQueriedValuesFromSegment( $segmentParams, $slug ){
303
304 $em = Container::instance()->getEntityManager();
305 $allEntityTerms = $em->getEntityAllTermsSlugs( $slug );
306 $filters = $em->getAllFiltersBySlug( $slug, array('logic', 'slug') );
307
308 $segmentParams = $em->safeExplodeFilterValues( $segmentParams, $slug, $this->separator, false );
309 $logicSeparator = $this->extractLogicSeparator( $segmentParams, $filters );
310
311 // $segmentParams = two#or#or-or-three#and
312 // $valueSeparator = '-or-'
313 if( $logicSeparator ) {
314
315 $queriedValues = explode( $logicSeparator, $segmentParams );
316
317 if ( ! $this->checkValuesOrder($segmentParams, $logicSeparator) ) {
318 /**
319 * @feature maybe redirect to URL with correct order of values
320 */
321 $this->set_404('Invalid params order');
322 }
323 }else{
324 $queriedValues[0] = $segmentParams;
325 }
326
327 $queriedValues = $em->safeImplodeFilterValues( $queriedValues, $this->separator );
328
329 foreach ( $queriedValues as $k => $value ) {
330 if ( ! in_array( $value, $allEntityTerms) ) {
331 unset( $queriedValues[$k] );
332 $this->set_404( 'Term does not exist - ' . $value );
333 }
334 }
335
336 // Check duplicates
337 if( flrt_array_contains_duplicate( $queriedValues ) ){
338 $this->set_404('Param duplicates');
339 $queriedValues = array_unique($queriedValues);
340 }
341
342 unset($em);
343
344 return $queriedValues;
345 }
346
347 private function validateSegmentsOrder( $template = [] ){
348 $fse = Container::instance()->getFilterService();
349
350 if( ! $template ){
351 $template = $fse->getFiltersOrder();
352 }
353
354 $to_compare = $this->queryVars['segments_order'];
355
356 if( flrt_array_contains_duplicate( $to_compare ) ){
357 return false;
358 }
359
360 if( ! is_array( $template ) || ! is_array( $to_compare ) ){
361 return false;
362 }
363
364 $new_template = array_intersect( $template, $to_compare );
365
366 if( empty( $new_template ) ){
367 return false;
368 }
369
370 $new_template = array_values( $new_template );
371 $already_compared = [];
372 $i = 0;
373
374 foreach ( $to_compare as $index => $value ) {
375 if( in_array( $value, $already_compared ) ){
376 $existing_index = array_search( $value, $already_compared );
377 $existing_index++;
378 if( isset( $already_compared[$existing_index] ) ){
379 return false;
380 }
381 }
382
383 if( $value === $new_template[$i] ){
384 $i++;
385 $already_compared[] = $value;
386 }
387 }
388
389 unset( $fse );
390
391 return ( $new_template === $already_compared );
392
393 }
394
395 private function validateQueryVars(){
396 // Check for segments duplicates
397 $maybe_duplicates = [];
398 $fse = Container::instance()->getFilterService();
399
400 if( ! empty( $this->queryVars['queried_values'] ) ){
401 foreach( $this->queryVars['queried_values'] as $filter ){
402 $maybe_duplicates[] = $fse->getEntityFullName( $filter['entity'], $filter['e_name'] );
403 }
404
405 if( flrt_array_contains_duplicate( $maybe_duplicates ) ){
406 $this->set_404( 'Segment duplicates' );
407 }
408 }
409
410 // Check segments order
411 if( ! empty( $this->queryVars['segments_order'] ) ) {
412 if (!$this->validateSegmentsOrder()) {
413 $this->set_404('Invalid segments order');
414 }
415 }
416
417 unset( $fse );
418 // Check something other
419 // If max < than min maybe should be an error
420 }
421 }