PluginProbe
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI / 6.0.2
AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI v6.0.2
6.0.2 6.0.1 6.0.0 5.8.6 5.8.5 5.8.4 5.8.3 5.8.2 5.8.1 5.8.0 5.7.9.2 5.7.9.1 5.7.8 5.7.9 5.7.6 5.7.7 5.7.5 5.7.4 5.7.3 5.7.2 5.7.1 trunk 5.6.0 5.6.1 5.6.2 All 33 releases
automatorwp / libraries / ct / includes / class-ct-query.php

class-ct-query.php in AutomatorWP – No-Code Workflow Automation, Integration & Webhooks Plugin, now with AI 6.0.2, at libraries/ct/includes/class-ct-query.php

1,105 lines 41.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Query class
4 *
5 * Based on WP_Query class
6 *
7 * @author GamiPress <contact@gamipress.com>, Ruben Garcia <rubengcdev@gamil.com>
8 *
9 * @since 1.0.0
10 */
11 // Exit if accessed directly
12 defined( 'ABSPATH' ) || exit;
13
14 if ( ! class_exists( 'CT_Query' ) ) :
15
16 class CT_Query {
17
18 /**
19 * Query vars set by the user
20 *
21 * @since 1.0.0
22 * @access public
23 * @var array
24 */
25 public $query;
26
27 /**
28 * Query vars, after parsing
29 *
30 * @since 1.0.0
31 * @access public
32 * @var array
33 */
34 public $query_vars = array();
35
36 /**
37 * Holds the data for a single object that is queried.
38 *
39 * Holds the contents of a post, page, category, attachment.
40 *
41 * @since 1.0.0
42 * @access public
43 * @var object|array
44 */
45 public $queried_object;
46
47 /**
48 * The ID of the queried object.
49 *
50 * @since 1.0.0
51 * @access public
52 * @var int
53 */
54 public $queried_object_id;
55
56 /**
57 * Get post database query.
58 *
59 * @since 1.0.0
60 * @access public
61 * @var string
62 */
63 public $request;
64
65 /**
66 * List of posts.
67 *
68 * @since 1.0.0
69 * @access public
70 * @var array
71 */
72 public $results;
73
74 /**
75 * The amount of posts for the current query.
76 *
77 * @since 1.0.0
78 * @access public
79 * @var int
80 */
81 public $result_count = 0;
82
83 /**
84 * Index of the current item in the loop.
85 *
86 * @since 1.0.0
87 * @access public
88 * @var int
89 */
90 public $current_result = -1;
91
92 /**
93 * Whether the loop has started and the caller is in the loop.
94 *
95 * @since 1.0.0
96 * @access public
97 * @var bool
98 */
99 public $in_the_loop = false;
100
101 /**
102 * The current post.
103 *
104 * @since 1.0.0
105 * @access public
106 * @var WP_Post
107 */
108 public $result;
109
110 /**
111 * The amount of found posts for the current query.
112 *
113 * If limit clause was not used, equals $post_count.
114 *
115 * @since 1.0.0
116 * @access public
117 * @var int
118 */
119 public $found_results = 0;
120
121 /**
122 * The amount of pages.
123 *
124 * @since 1.0.0
125 * @access public
126 * @var int
127 */
128 public $max_num_pages = 0;
129
130 /**
131 * stopwords used when parsing search terms
132 *
133 * @since 1.0.0
134 * @access public
135 * @var array
136 */
137 public $stopwords = array();
138
139
140 public function __construct( $query = '' ) {
141
142 $this->init();
143
144 $this->query = $this->query_vars = wp_parse_args( $query );
145
146 }
147
148 public function init() {
149
150 unset($this->results);
151 unset($this->query);
152 $this->query_vars = array();
153 unset($this->queried_object);
154 unset($this->queried_object_id);
155 $this->result_count = 0;
156 $this->current_result = -1;
157 $this->in_the_loop = false;
158 unset( $this->request );
159 unset( $this->result );
160 $this->found_results = 0;
161 $this->max_num_pages = 0;
162 $this->stopwords = array();
163
164 }
165
166 public function set( $query_var, $value ) {
167 $this->query_vars[$query_var] = $value;
168 }
169
170 public function get_results() {
171
172 global $wpdb, $ct_table;
173
174 $this->parse_query();
175
176 /**
177 * Fires after the query variable object is created, but before the actual query is run.
178 *
179 * Note: If using conditional tags, use the method versions within the passed instance
180 * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
181 * like is_main_query() test against the global $wp_query instance, not the passed one.
182 *
183 * @since 1.0.0
184 *
185 * @param CT_Query &$this The CT_Query instance (passed by reference).
186 */
187 do_action_ref_array( 'ct_pre_get_results', array( &$this ) );
188
189 // Shorthand.
190 $q = &$this->query_vars;
191
192 // Fill again in case pre_get_posts unset some vars.
193 $q = $this->fill_query_vars($q);
194
195 // Suppress filters
196 if ( ! isset($q['suppress_filters']) )
197 $q['suppress_filters'] = false;
198
199 if ( empty( $q['items_per_page'] ) ) {
200 $q['items_per_page'] = get_option( 'items_per_page', 20 );
201 }
202
203 // Pagination
204 if ( ! isset( $q['nopaging'] ) ) {
205 if ( $q['items_per_page'] == -1 ) {
206 $q['nopaging'] = true;
207 } else {
208 $q['nopaging'] = false;
209 }
210 }
211
212 // Items per page
213 $q['items_per_page'] = (int) $q['items_per_page'];
214
215 if ( $q['items_per_page'] < -1 )
216 $q['items_per_page'] = abs($q['items_per_page']);
217 elseif ( $q['items_per_page'] == 0 )
218 $q['items_per_page'] = 1;
219
220 // page
221 if ( isset($q['page']) ) {
222 $q['page'] = trim($q['page'], '/');
223 $q['page'] = absint($q['page']);
224 }
225
226 // If true, forcibly turns off SQL_CALC_FOUND_ROWS even when limits are present.
227 if ( isset($q['no_found_rows']) ) {
228 $q['no_found_rows'] = (bool) $q['no_found_rows'];
229 } else {
230 $q['no_found_rows'] = false;
231 }
232
233 switch ( $q['fields'] ) {
234 case 'ids':
235 $fields = "{$ct_table->db->table_name}.{$ct_table->db->primary_key}";
236 break;
237 default:
238 $fields = "{$ct_table->db->table_name}.*";
239 }
240
241 // First let's clear some variables
242 $distinct = '';
243 $where = '';
244 $limits = '';
245 $join = '';
246 $search = '';
247 $groupby = '';
248 $orderby = '';
249 $page = 1;
250
251 // If a search pattern is specified, load the posts that match.
252 if ( strlen( $q['s'] ) ) {
253 $search = $this->parse_search( $q );
254 }
255
256 if ( ! $q['suppress_filters'] ) {
257 /**
258 * Filters the search SQL that is used in the WHERE clause of WP_Query.
259 *
260 * @since 1.0.0
261 *
262 * @param string $search Search SQL for WHERE clause.
263 * @param WP_Query $this The current WP_Query object.
264 */
265 $search = apply_filters_ref_array( 'ct_query_search', array( $search, &$this ) );
266 }
267
268 $where .= $search;
269
270 // Rand order by
271 $rand = ( isset( $q['orderby'] ) && 'rand' === $q['orderby'] );
272 if ( ! isset( $q['order'] ) ) {
273 $q['order'] = $rand ? '' : 'DESC';
274 } else {
275 $q['order'] = $rand ? '' : $this->parse_order( $q['order'] );
276 }
277
278 // Order by.
279 if ( empty( $q['orderby'] ) ) {
280 /*
281 * Boolean false or empty array blanks out ORDER BY,
282 * while leaving the value unset or otherwise empty sets the default.
283 */
284 if ( isset( $q['orderby'] ) && ( is_array( $q['orderby'] ) || false === $q['orderby'] ) ) {
285 $orderby = '';
286 } else {
287 // Default order by primary key
288 $orderby = "{$ct_table->db->table_name}.{$ct_table->db->primary_key} " . $q['order'];
289 }
290 } elseif ( 'none' == $q['orderby'] ) {
291 $orderby = '';
292 } else {
293 $orderby_array = array();
294 if ( is_array( $q['orderby'] ) ) {
295 foreach ( $q['orderby'] as $_orderby => $order ) {
296 $orderby = wp_slash( urldecode( $_orderby ) );
297 $parsed = $this->parse_orderby( $orderby );
298
299 // Only allow certain values for safety.
300 if ( ! $parsed ) {
301 continue;
302 }
303
304 $orderby_array[] = $parsed . ' ' . $this->parse_order( $order );
305 }
306 $orderby = implode( ', ', $orderby_array );
307
308 } else {
309 $q['orderby'] = wp_slash( urldecode( $q['orderby'] ) );
310
311 foreach ( explode( ' ', $q['orderby'] ) as $i => $orderby ) {
312
313 $parsed = $this->parse_orderby( $orderby );
314
315 // Only allow certain values for safety.
316 if ( ! $parsed ) {
317 continue;
318 }
319
320 $orderby_array[] = $parsed;
321 }
322
323 $orderby = implode( ' ' . $q['order'] . ', ', $orderby_array );
324
325 if ( empty( $orderby ) ) {
326 $orderby = "{$ct_table->db->table_name}.{$ct_table->db->primary_key} " . $q['order'];
327 } elseif ( ! empty( $q['order'] ) ) {
328 $orderby .= " {$q['order']}";
329 }
330 }
331 }
332
333 /*
334 * Apply filters on where and join prior to paging so that any
335 * manipulations to them are reflected in the paging by day queries.
336 */
337 if ( !$q['suppress_filters'] ) {
338 /**
339 * Filters the WHERE clause of the query.
340 *
341 * @since 1.0.0
342 *
343 * @param string $where The WHERE clause of the query.
344 * @param WP_Query &$this The WP_Query instance (passed by reference).
345 */
346 $where = apply_filters_ref_array( 'ct_query_where', array( $where, &$this ) );
347
348 /**
349 * Filters the JOIN clause of the query.
350 *
351 * @since 1.0.0
352 *
353 * @param string $where The JOIN clause of the query.
354 * @param WP_Query &$this The WP_Query instance (passed by reference).
355 */
356 $join = apply_filters_ref_array( 'ct_query_join', array( $join, &$this ) );
357 }
358
359 // Paging
360 if ( empty($q['nopaging']) ) {
361 $page = absint($q['paged']);
362 if ( !$page )
363 $page = 1;
364
365 // If 'offset' is provided, it takes precedence over 'paged'.
366 if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
367 $q['offset'] = absint( $q['offset'] );
368 $pgstrt = $q['offset'] . ', ';
369 } else {
370 $pgstrt = absint( ( $page - 1 ) * $q['items_per_page'] ) . ', ';
371 }
372 $limits = 'LIMIT ' . $pgstrt . $q['items_per_page'];
373 }
374
375 $pieces = array( 'where', 'groupby', 'join', 'orderby', 'distinct', 'fields', 'limits' );
376
377 /*
378 * Apply post-paging filters on where and join. Only plugins that
379 * manipulate paging queries should use these hooks.
380 */
381 if ( !$q['suppress_filters'] ) {
382 /**
383 * Filters the WHERE clause of the query.
384 *
385 * Specifically for manipulating paging queries.
386 *
387 * @since 1.0.0
388 *
389 * @param string $where The WHERE clause of the query.
390 * @param WP_Query &$this The WP_Query instance (passed by reference).
391 */
392 $where = apply_filters_ref_array( 'ct_query_where_paged', array( $where, &$this ) );
393
394 /**
395 * Filters the GROUP BY clause of the query.
396 *
397 * @since 1.0.0
398 *
399 * @param string $groupby The GROUP BY clause of the query.
400 * @param WP_Query &$this The WP_Query instance (passed by reference).
401 */
402 $groupby = apply_filters_ref_array( 'ct_query_groupby', array( $groupby, &$this ) );
403
404 /**
405 * Filters the JOIN clause of the query.
406 *
407 * Specifically for manipulating paging queries.
408 *
409 * @since 1.0.0
410 *
411 * @param string $join The JOIN clause of the query.
412 * @param WP_Query &$this The WP_Query instance (passed by reference).
413 */
414 $join = apply_filters_ref_array( 'ct_query_join_paged', array( $join, &$this ) );
415
416 /**
417 * Filters the ORDER BY clause of the query.
418 *
419 * @since 1.0.0
420 *
421 * @param string $orderby The ORDER BY clause of the query.
422 * @param WP_Query &$this The WP_Query instance (passed by reference).
423 */
424 $orderby = apply_filters_ref_array( 'ct_query_orderby', array( $orderby, &$this ) );
425
426 /**
427 * Filters the DISTINCT clause of the query.
428 *
429 * @since 1.0.0
430 *
431 * @param string $distinct The DISTINCT clause of the query.
432 * @param WP_Query &$this The WP_Query instance (passed by reference).
433 */
434 $distinct = apply_filters_ref_array( 'ct_query_distinct', array( $distinct, &$this ) );
435
436 /**
437 * Filters the LIMIT clause of the query.
438 *
439 * @since 1.0.0
440 *
441 * @param string $limits The LIMIT clause of the query.
442 * @param WP_Query &$this The WP_Query instance (passed by reference).
443 */
444 $limits = apply_filters_ref_array( 'ct_querylimits', array( $limits, &$this ) );
445
446 /**
447 * Filters the SELECT clause of the query.
448 *
449 * @since 1.0.0
450 *
451 * @param string $fields The SELECT clause of the query.
452 * @param WP_Query &$this The WP_Query instance (passed by reference).
453 */
454 $fields = apply_filters_ref_array( 'ct_query_fields', array( $fields, &$this ) );
455
456 /**
457 * Filters all query clauses at once, for convenience.
458 *
459 * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
460 * fields (SELECT), and LIMITS clauses.
461 *
462 * @since 1.0.0
463 *
464 * @param array $clauses The list of clauses for the query.
465 * @param WP_Query &$this The WP_Query instance (passed by reference).
466 */
467 $clauses = (array) apply_filters_ref_array( 'ct_query_clauses', array( compact( $pieces ), &$this ) );
468
469 $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
470 $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
471 $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
472 $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
473 $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
474 $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
475 $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
476 }
477
478 /**
479 * Fires to announce the query's current selection parameters.
480 *
481 * For use by caching plugins.
482 *
483 * @since 1.0.0
484 *
485 * @param string $selection The assembled selection query.
486 */
487 do_action( 'ct_query_selection', $where . $groupby . $orderby . $limits . $join );
488
489 /*
490 * Filters again for the benefit of caching plugins.
491 * Regular plugins should use the hooks above.
492 */
493 if ( !$q['suppress_filters'] ) {
494 /**
495 * Filters the WHERE clause of the query.
496 *
497 * For use by caching plugins.
498 *
499 * @since 1.0.0
500 *
501 * @param string $where The WHERE clause of the query.
502 * @param WP_Query &$this The WP_Query instance (passed by reference).
503 */
504 $where = apply_filters_ref_array( 'ct_query_where_request', array( $where, &$this ) );
505
506 /**
507 * Filters the GROUP BY clause of the query.
508 *
509 * For use by caching plugins.
510 *
511 * @since 1.0.0
512 *
513 * @param string $groupby The GROUP BY clause of the query.
514 * @param WP_Query &$this The WP_Query instance (passed by reference).
515 */
516 $groupby = apply_filters_ref_array( 'ct_query_groupby_request', array( $groupby, &$this ) );
517
518 /**
519 * Filters the JOIN clause of the query.
520 *
521 * For use by caching plugins.
522 *
523 * @since 1.0.0
524 *
525 * @param string $join The JOIN clause of the query.
526 * @param WP_Query &$this The WP_Query instance (passed by reference).
527 */
528 $join = apply_filters_ref_array( 'ct_query_join_request', array( $join, &$this ) );
529
530 /**
531 * Filters the ORDER BY clause of the query.
532 *
533 * For use by caching plugins.
534 *
535 * @since 1.0.0
536 *
537 * @param string $orderby The ORDER BY clause of the query.
538 * @param WP_Query &$this The WP_Query instance (passed by reference).
539 */
540 $orderby = apply_filters_ref_array( 'ct_query_orderby_request', array( $orderby, &$this ) );
541
542 /**
543 * Filters the DISTINCT clause of the query.
544 *
545 * For use by caching plugins.
546 *
547 * @since 1.0.0
548 *
549 * @param string $distinct The DISTINCT clause of the query.
550 * @param WP_Query &$this The WP_Query instance (passed by reference).
551 */
552 $distinct = apply_filters_ref_array( 'ct_query_distinct_request', array( $distinct, &$this ) );
553
554 /**
555 * Filters the SELECT clause of the query.
556 *
557 * For use by caching plugins.
558 *
559 * @since 1.0.0
560 *
561 * @param string $fields The SELECT clause of the query.
562 * @param WP_Query &$this The WP_Query instance (passed by reference).
563 */
564 $fields = apply_filters_ref_array( 'ct_query_fields_request', array( $fields, &$this ) );
565
566 /**
567 * Filters the LIMIT clause of the query.
568 *
569 * For use by caching plugins.
570 *
571 * @since 1.0.0
572 *
573 * @param string $limits The LIMIT clause of the query.
574 * @param WP_Query &$this The WP_Query instance (passed by reference).
575 */
576 $limits = apply_filters_ref_array( 'ct_query_limits_request', array( $limits, &$this ) );
577
578 /**
579 * Filters all query clauses at once, for convenience.
580 *
581 * For use by caching plugins.
582 *
583 * Covers the WHERE, GROUP BY, JOIN, ORDER BY, DISTINCT,
584 * fields (SELECT), and LIMITS clauses.
585 *
586 * @since 1.0.0
587 *
588 * @param array $pieces The pieces of the query.
589 * @param WP_Query &$this The WP_Query instance (passed by reference).
590 */
591 $clauses = (array) apply_filters_ref_array( 'ct_query_clauses_request', array( compact( $pieces ), &$this ) );
592
593 $where = isset( $clauses[ 'where' ] ) ? $clauses[ 'where' ] : '';
594 $groupby = isset( $clauses[ 'groupby' ] ) ? $clauses[ 'groupby' ] : '';
595 $join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
596 $orderby = isset( $clauses[ 'orderby' ] ) ? $clauses[ 'orderby' ] : '';
597 $distinct = isset( $clauses[ 'distinct' ] ) ? $clauses[ 'distinct' ] : '';
598 $fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
599 $limits = isset( $clauses[ 'limits' ] ) ? $clauses[ 'limits' ] : '';
600 }
601
602 if ( ! empty($groupby) )
603 $groupby = 'GROUP BY ' . $groupby;
604 if ( !empty( $orderby ) )
605 $orderby = 'ORDER BY ' . $orderby;
606
607 $found_rows = '';
608 if ( !$q['no_found_rows'] && !empty($limits) )
609 $found_rows = 'SQL_CALC_FOUND_ROWS';
610
611 $this->request = $old_request = "SELECT $found_rows $distinct $fields FROM {$ct_table->db->table_name} $join WHERE 1=1 $where $groupby $orderby $limits";
612
613 if ( !$q['suppress_filters'] ) {
614 /**
615 * Filters the completed SQL query before sending.
616 *
617 * @since 1.0.0
618 *
619 * @param string $request The complete SQL query.
620 * @param WP_Query &$this The WP_Query instance (passed by reference).
621 */
622 $this->request = apply_filters_ref_array( 'ct_query_request', array( $this->request, &$this ) );
623 }
624
625 /**
626 * Filters the posts array before the query takes place.
627 *
628 * Return a non-null value to bypass WordPress's default post queries.
629 *
630 * Filtering functions that require pagination information are encouraged to set
631 * the `found_posts` and `max_num_pages` properties of the WP_Query object,
632 * passed to the filter by reference. If WP_Query does not perform a database
633 * query, it will not have enough information to generate these values itself.
634 *
635 * @since 1.0.0
636 *
637 * @param array|null $posts Return an array of post data to short-circuit WP's query,
638 * or null to allow WP to run its normal queries.
639 * @param WP_Query $this The WP_Query instance, passed by reference.
640 */
641 $this->results = apply_filters_ref_array( 'ct_results_pre_query', array( null, &$this ) );
642
643 if ( 'ids' == $q['fields'] ) {
644 if ( null === $this->results ) {
645 $this->results = $wpdb->get_col( $this->request );
646 }
647
648 $this->results = array_map( 'intval', $this->results );
649 $this->result_count = count( $this->results );
650 $this->set_found_results( $q, $limits );
651
652 return $this->results;
653 }
654
655 if ( null === $this->results ) {
656 $split_the_query = ( $old_request == $this->request && "{$ct_table->db->table_name}.*" == $fields && !empty( $limits ) && $q['items_per_page'] < 500 );
657
658 /**
659 * Filters whether to split the query.
660 *
661 * Splitting the query will cause it to fetch just the IDs of the found posts
662 * (and then individually fetch each post by ID), rather than fetching every
663 * complete row at once. One massive result vs. many small results.
664 *
665 * @since 1.0.0
666 *
667 * @param bool $split_the_query Whether or not to split the query.
668 * @param WP_Query $this The WP_Query instance.
669 */
670 $split_the_query = apply_filters( 'split_the_query', $split_the_query, $this );
671
672 if ( $split_the_query ) {
673 // First get the IDs and then fill in the objects
674
675 $this->request = "SELECT $found_rows $distinct {$ct_table->db->table_name}.{$ct_table->db->primary_key} FROM {$ct_table->db->table_name} $join WHERE 1=1 $where $groupby $orderby $limits";
676
677 /**
678 * Filters the Post IDs SQL request before sending.
679 *
680 * @since 1.0.0
681 *
682 * @param string $request The post ID request.
683 * @param WP_Query $this The WP_Query instance.
684 */
685 $this->request = apply_filters( 'ct_query_request_ids', $this->request, $this );
686
687 $ids = $wpdb->get_col( $this->request );
688
689 if ( $ids ) {
690 $this->results = $ids;
691 $this->set_found_results( $q, $limits );
692 // TODO: Add caching utility
693 //_prime_post_caches( $ids, $q['update_post_term_cache'], $q['update_post_meta_cache'] );
694 } else {
695 $this->results = array();
696 }
697 } else {
698 $this->results = $wpdb->get_results( $this->request );
699 $this->set_found_results( $q, $limits );
700 }
701 }
702
703 // Convert to objects.
704 if ( $this->results ) {
705 $this->results = array_map( 'ct_get_object', $this->results );
706 }
707
708 if ( ! $q['suppress_filters'] ) {
709 /**
710 * Filters the raw post results array, prior to status checks.
711 *
712 * @since 1.0.0
713 *
714 * @param array $posts The post results array.
715 * @param WP_Query &$this The WP_Query instance (passed by reference).
716 */
717 $this->results = apply_filters_ref_array( 'ct_query_results', array( $this->results, &$this ) );
718 }
719
720 if ( ! $q['suppress_filters'] ) {
721 /**
722 * Filters the array of retrieved posts after they've been fetched and
723 * internally processed.
724 *
725 * @since 1.0.0
726 *
727 * @param array $posts The array of retrieved posts.
728 * @param WP_Query &$this The WP_Query instance (passed by reference).
729 */
730 $this->results = apply_filters_ref_array( 'ct_the_results', array( $this->results, &$this ) );
731 }
732
733 // Ensure that any posts added/modified via one of the filters above are
734 // of the type WP_Post and are filtered.
735 if ( $this->results ) {
736 $this->result_count = count( $this->results );
737
738 $this->results = array_map( 'ct_get_object', $this->results );
739
740 //if ( $q['cache_results'] )
741 //update_post_caches($this->posts, $post_type, $q['update_post_term_cache'], $q['update_post_meta_cache']);
742
743 //$this->post = reset( $this->posts );
744 } else {
745 $this->result_count = 0;
746 $this->results = array();
747 }
748
749 return $this->results;
750
751 }
752
753 public function parse_query( $query = '' ) {
754
755 if ( ! empty( $query ) ) {
756 $this->init();
757 $this->query = $this->query_vars = wp_parse_args( $query );
758 } elseif ( ! isset( $this->query ) ) {
759 $this->query = $this->query_vars;
760 }
761
762 $this->query_vars = $this->fill_query_vars($this->query_vars);
763
764 $qv = &$this->query_vars;
765
766 $qv['paged'] = absint($qv['paged']);
767
768 // Fairly insane upper bound for search string lengths.
769 if ( ! is_scalar( $qv['s'] ) || ( ! empty( $qv['s'] ) && strlen( $qv['s'] ) > 1600 ) ) {
770 $qv['s'] = '';
771 }
772
773 /**
774 * Fires after the main query vars have been parsed.
775 *
776 * @since 1.0.0
777 *
778 * @param WP_Query &$this The WP_Query instance (passed by reference).
779 */
780 do_action_ref_array( 'ct_parse_query', array( &$this ) );
781
782 }
783
784 /**
785 * Fills in the query variables, which do not exist within the parameter.
786 *
787 * @since 1.0.0
788 * @access public
789 *
790 * @param array $array Defined query variables.
791 * @return array Complete query variables with undefined ones filled in empty.
792 */
793 public function fill_query_vars($array) {
794 $keys = array(
795 'paged'
796 , 's'
797 , 'sentence'
798 , 'fields'
799 );
800
801 foreach ( $keys as $key ) {
802 if ( ! isset($array[$key]) )
803 $array[$key] = '';
804 }
805
806 return $array;
807 }
808
809 /**
810 * Generate SQL for the WHERE clause based on passed search terms.
811 *
812 * @since 1.0.0
813 *
814 * @param array $q Query variables.
815 * @return string WHERE clause.
816 */
817 protected function parse_search( &$q ) {
818 global $wpdb, $ct_table;
819
820 $search = '';
821
822 $search_fields = apply_filters( "ct_query_{$ct_table->name}_search_fields", array() );
823
824 if( empty( $search_fields ) ) {
825 return $search;
826 }
827
828 // added slashes screw with quote grouping when done early, so done later
829 $q['s'] = stripslashes( $q['s'] );
830
831 if ( empty( $_GET['s'] ) )
832 $q['s'] = urldecode( $q['s'] );
833 // there are no line breaks in <input /> fields
834 $q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] );
835 $q['search_terms_count'] = 1;
836 if ( ! empty( $q['sentence'] ) ) {
837 $q['search_terms'] = array( $q['s'] );
838 } else {
839 if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) {
840 $q['search_terms_count'] = count( $matches[0] );
841 $q['search_terms'] = $this->parse_search_terms( $matches[0] );
842 // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence
843 if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 )
844 $q['search_terms'] = array( $q['s'] );
845 } else {
846 $q['search_terms'] = array( $q['s'] );
847 }
848 }
849
850 $n = ! empty( $q['exact'] ) ? '' : '%';
851 $searchand = '';
852 $q['search_orderby_title'] = array();
853
854 /**
855 * Filters the prefix that indicates that a search term should be excluded from results.
856 *
857 * @since 1.0.0
858 *
859 * @param string $exclusion_prefix The prefix. Default '-'. Returning
860 * an empty value disables exclusions.
861 */
862 $exclusion_prefix = apply_filters( 'ct_query_search_exclusion_prefix', '-' );
863
864 foreach ( $q['search_terms'] as $term ) {
865 // If there is an $exclusion_prefix, terms prefixed with it should be excluded.
866 $exclude = $exclusion_prefix && ( $exclusion_prefix === substr( $term, 0, 1 ) );
867 if ( $exclude ) {
868 $like_op = 'NOT LIKE';
869 $andor_op = 'AND';
870 $term = substr( $term, 1 );
871 } else {
872 $like_op = 'LIKE';
873 $andor_op = 'OR';
874 }
875
876 if ( $n && ! $exclude ) {
877 $like = '%' . $wpdb->esc_like( $term ) . '%';
878 //$q['search_orderby_title'][] = $wpdb->prepare( "{$wpdb->posts}.post_title LIKE %s", $like );
879 }
880
881 $like = $n . $wpdb->esc_like( $term ) . $n;
882
883 $search_fields_where = array();
884 $search_fields_args = array();
885
886 foreach( $search_fields as $search_field ) {
887 $search_fields_where[] = "{$ct_table->db->table_name}.$search_field $like_op %s";
888 $search_fields_args[] = $like;
889 }
890
891 $search_where = "(" . implode( ") $andor_op (", $search_fields_where ) . ")";
892
893 $search .= $wpdb->prepare( "{$searchand}($search_where)", $search_fields_args );
894
895 //$search .= $wpdb->prepare( "{$searchand}(({$wpdb->posts}.post_title $like_op %s) $andor_op ({$wpdb->posts}.post_excerpt $like_op %s) $andor_op ({$wpdb->posts}.post_content $like_op %s))", $like, $like, $like );
896
897 $searchand = ' AND ';
898 }
899
900 if ( ! empty( $search ) ) {
901 $search = " AND ({$search}) ";
902 }
903
904 return $search;
905 }
906
907 /**
908 * Check if the terms are suitable for searching.
909 *
910 * Uses an array of stopwords (terms) that are excluded from the separate
911 * term matching when searching for posts. The list of English stopwords is
912 * the approximate search engines list, and is translatable.
913 *
914 * @since 1.0.0
915 *
916 * @param array $terms Terms to check.
917 * @return array Terms that are not stopwords.
918 */
919 protected function parse_search_terms( $terms ) {
920 $strtolower = function_exists( 'mb_strtolower' ) ? 'mb_strtolower' : 'strtolower';
921 $checked = array();
922
923 $stopwords = $this->get_search_stopwords();
924
925 foreach ( $terms as $term ) {
926 // keep before/after spaces when term is for exact match
927 if ( preg_match( '/^".+"$/', $term ) )
928 $term = trim( $term, "\"'" );
929 else
930 $term = trim( $term, "\"' " );
931
932 // Avoid single A-Z and single dashes.
933 if ( ! $term || ( 1 === strlen( $term ) && preg_match( '/^[a-z\-]$/i', $term ) ) )
934 continue;
935
936 if ( in_array( call_user_func( $strtolower, $term ), $stopwords, true ) )
937 continue;
938
939 $checked[] = $term;
940 }
941
942 return $checked;
943 }
944
945 /**
946 * Retrieve stopwords used when parsing search terms.
947 *
948 * @since 1.0.0
949 *
950 * @return array Stopwords.
951 */
952 protected function get_search_stopwords() {
953 if ( isset( $this->stopwords ) )
954 return $this->stopwords;
955
956 /* translators: This is a comma-separated list of very common words that should be excluded from a search,
957 * like a, an, and the. These are usually called "stopwords". You should not simply translate these individual
958 * words into your language. Instead, look for and provide commonly accepted stopwords in your language.
959 */
960 $words = explode( ',', _x(
961 'about,an,are,as,at,be,by,com,for,from,how,in,is,it,of,on,or,that,the,this,to,was,what,when,where,who,will,with,www',
962 'Comma-separated list of search stopwords in your language'
963 ) );
964
965 $stopwords = array();
966 foreach ( $words as $word ) {
967 $word = trim( $word, "\r\n\t " );
968 if ( $word )
969 $stopwords[] = $word;
970 }
971
972 /**
973 * Filters stopwords used when parsing search terms.
974 *
975 * @since 1.0.0
976 *
977 * @param array $stopwords Stopwords.
978 */
979 $this->stopwords = apply_filters( 'ct_search_stopwords', $stopwords );
980 return $this->stopwords;
981 }
982
983 /**
984 * Converts the given orderby alias (if allowed) to a properly-prefixed value.
985 *
986 * @since 4.0.0
987 *
988 * @global wpdb $wpdb WordPress database abstraction object.
989 *
990 * @param string $orderby Alias for the field to order by.
991 * @return string|false Table-prefixed value to used in the ORDER clause. False otherwise.
992 */
993 protected function parse_orderby( $orderby ) {
994 global $wpdb, $ct_table;
995
996 $allowed_keys = array(
997 'rand' => 'RAND()',
998 );
999
1000
1001 foreach ($ct_table->db->schema->fields as $key => $value) {
1002 $allowed_keys[$key] = "{$ct_table->db->table_name}.{$key}";
1003 }
1004
1005 $allowed_keys = apply_filters( "ct_query_{$ct_table->name}_orderby_allowed_keys", $allowed_keys, $orderby );
1006
1007
1008 // If RAND() contains a seed value, sanitize and add to allowed keys.
1009 if ( preg_match( '/RAND\(([0-9]+)\)/i', $orderby, $matches ) ) {
1010 $orderby = 'rand_with_seed';
1011 $allowed_keys['rand_with_seed'] = sprintf( 'RAND(%s)', (int) $matches[1] );
1012 }
1013
1014 if ( ! isset( $allowed_keys[$orderby] ) )
1015 return '';
1016
1017 $orderby_clause = $allowed_keys[$orderby];
1018
1019 return apply_filters( "ct_query_{$ct_table->name}_parse_orderby", $orderby_clause, $orderby );
1020
1021 }
1022
1023 /**
1024 * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
1025 *
1026 * @since 1.0.0
1027 * @access protected
1028 *
1029 * @param string $order The 'order' query variable.
1030 * @return string The sanitized 'order' query variable.
1031 */
1032 protected function parse_order( $order ) {
1033 if ( ! is_string( $order ) || empty( $order ) ) {
1034 return 'DESC';
1035 }
1036
1037 if ( 'ASC' === strtoupper( $order ) ) {
1038 return 'ASC';
1039 } else {
1040 return 'DESC';
1041 }
1042 }
1043
1044 /**
1045 * Set up the amount of found posts and the number of pages (if limit clause was used)
1046 * for the current query.
1047 *
1048 * @since 1.0.0
1049 * @access private
1050 *
1051 * @param array $q Query variables.
1052 * @param string $limits LIMIT clauses of the query.
1053 */
1054 private function set_found_results( $q, $limits ) {
1055 global $wpdb;
1056 // Bail if posts is an empty array. Continue if posts is an empty string,
1057 // null, or false to accommodate caching plugins that fill posts later.
1058 if ( $q['no_found_rows'] || ( is_array( $this->results ) && ! $this->results ) )
1059 return;
1060
1061 if ( ! empty( $limits ) ) {
1062 /**
1063 * Filters the query to run for retrieving the found posts.
1064 *
1065 * @since 1.0.0
1066 *
1067 * @param string $found_posts The query to run to find the found posts.
1068 * @param WP_Query &$this The WP_Query instance (passed by reference).
1069 */
1070 $this->found_results = $wpdb->get_var( apply_filters_ref_array( 'ct_found_results_query', array( 'SELECT FOUND_ROWS()', &$this ) ) );
1071 } else {
1072 $this->found_results = count( $this->results );
1073 }
1074
1075 /**
1076 * Filters the number of found posts for the query.
1077 *
1078 * @since 1.0.0
1079 *
1080 * @param int $found_posts The number of posts found.
1081 * @param WP_Query &$this The WP_Query instance (passed by reference).
1082 */
1083 $this->found_results = apply_filters_ref_array( 'ct_found_results', array( $this->found_results, &$this ) );
1084
1085 if ( ! empty( $limits ) )
1086 $this->max_num_pages = ceil( $this->found_results / $q['items_per_page'] );
1087 }
1088
1089 /**
1090 * Sets up the query by parsing query string.
1091 *
1092 * @since 1.0.0
1093 *
1094 * @param string|array $query URL query string or array of query arguments.
1095 * @return array List of results.
1096 */
1097 public function query( $query ) {
1098 $this->init();
1099 $this->query = $this->query_vars = wp_parse_args( $query );
1100 return $this->get_results();
1101 }
1102
1103 }
1104
1105 endif;