Ticket #17093: apply_query_filters.diff

File apply_query_filters.diff, 13.1 KB (added by kevinB, 2 years ago)

new plugin API for query filtering :add_query_filter() and apply_query_filters()

Line 
1Index: wp-includes/plugin.php
2===================================================================
3--- wp-includes/plugin.php      (revision 17624)
4+++ wp-includes/plugin.php      (working copy)
5@@ -72,6 +72,31 @@
6 }
7 
8 /**
9+ * Hooks a function or method to a specific filter action, specifying a required context for application.
10+ *
11+ * @package WordPress
12+ * @subpackage Plugin
13+ * @global array $wp_filter Stores all of the filters added in the form of
14+ *     wp_filter['tag']['array of priorities']['array of functions serialized']['array of ['array (functions, accepted_args)']']
15+ * @global array $merged_filters Tracks the tags that need to be merged for later. If the hook is added, it doesn't need to run through that process.
16+ *
17+ * @param string $tag The name of the filter to hook the $function_to_add to.
18+ * @param callback $function_to_add The name of the function to be called when the filter is applied.
19+ * @param int $priority optional. Used to specify the order in which the functions associated with a particular action are executed (default: 10). Lower numbers correspond with earlier execution, and functions with the same priority are executed in the order in which they were added to the action.
20+ * @param int $accepted_args optional. The number of arguments the function accept (default 1).
21+ * @param string $context optional. Context to require for filter application (default '').  Nullstring means filter is applied for all query contexts.
22+ * @return boolean true
23+ */
24+function add_query_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1, $context = '') {
25+       global $wp_filter, $merged_filters;
26+
27+       $idx = _wp_filter_build_unique_id($tag, $function_to_add, $priority);
28+       $wp_filter[$tag][$priority][$idx] = array('function' => $function_to_add, 'accepted_args' => $accepted_args, 'context' => $context);
29+       unset( $merged_filters[ $tag ] );
30+       return true;
31+}
32+
33+/**
34  * Check if any filter has been registered for a hook.
35  *
36  * @package WordPress
37@@ -173,7 +198,72 @@
38        return $value;
39 }
40 
41+
42 /**
43+ * Call the functions added to a query filter hook, applying return value validation and context check.
44+ *
45+ * @package WordPress
46+ * @subpackage Plugin
47+ * @global array $wp_filter Stores all of the filters
48+ * @global array $merged_filters Merges the filter hooks using this function.
49+ * @global array $wp_current_filter stores the list of current filters with the current one last
50+ *
51+ * @param string $tag The name of the filter hook.
52+ * @param mixed $value The value on which the filters hooked to <tt>$tag</tt> are applied on.
53+ * @param object $query_obj Query object which triggered filter application.
54+ * @return mixed The filtered value after all hooked functions are applied to it.
55+ */
56+function apply_query_filters($tag, $value, $query_obj) {
57+       global $wp_filter, $merged_filters, $wp_current_filter;
58+
59+       $wp_current_filter[] = $tag;
60+
61+       // Do 'all' actions first
62+       if ( isset($wp_filter['all']) ) {
63+               _wp_call_all_hook( func_get_args() );
64+       }
65+
66+       if ( !isset($wp_filter[$tag]) ) {
67+               array_pop($wp_current_filter);
68+               return $value;
69+       }
70+
71+       // Sort
72+       if ( !isset( $merged_filters[ $tag ] ) ) {
73+               ksort($wp_filter[$tag]);
74+               $merged_filters[ $tag ] = true;
75+       }
76+
77+       reset( $wp_filter[ $tag ] );
78+
79+       $context = $query_obj->query_vars['query_context'];
80+
81+       do {
82+               foreach( (array) current($wp_filter[$tag]) as $the_ ) {
83+                       // Each valid filter hooked to this tag is applied if it was not added for a specific context or if the current context matches
84+                       if ( !is_null($the_['function']) && ( empty($the_['context']) || ( $context == $the_['context'] ) ) ) {
85+                               if ( (int) $the_['accepted_args'] > 1 )
86+                                       $_value = call_user_func($the_['function'], $value, $query_obj);
87+                               else
88+                                       $_value = call_user_func($the_['function'], $value);
89+
90+                               if ( is_null($_value) ) {
91+                                       if ( WP_DEBUG ) {
92+                                               trigger_error( sprintf( __('Query filter returned null value for %s hook.'), $tag ), E_USER_WARNING );
93+                                       }
94+                               } else
95+                                       $value = $_value;
96+                       }
97+               }
98+
99+       } while ( next($wp_filter[$tag]) !== false );
100+
101+       array_pop( $wp_current_filter );
102+
103+       return $value;
104+}
105+
106+/**
107  * Execute functions hooked on a specific filter hook, specifying arguments in an array.
108  *
109  * @see apply_filters() This function is identical, but the arguments passed to the
110Index: wp-includes/query.php
111===================================================================
112--- wp-includes/query.php       (revision 17624)
113+++ wp-includes/query.php       (working copy)
114@@ -1369,6 +1369,7 @@
115                        , 's'
116                        , 'sentence'
117                        , 'fields'
118+                       , 'query_context'
119                );
120 
121                foreach ( $keys as $key ) {
122@@ -2167,7 +2168,7 @@
123                }
124 
125                // Allow plugins to contextually add/remove/modify the search section of the database query
126-               $search = apply_filters_ref_array('posts_search', array( $search, &$this ) );
127+               $search = apply_query_filters('posts_search', $search, $this);
128 
129                // Taxonomies
130                if ( !$this->is_singular ) {
131@@ -2483,8 +2484,8 @@
132                // Apply filters on where and join prior to paging so that any
133                // manipulations to them are reflected in the paging by day queries.
134                if ( !$q['suppress_filters'] ) {
135-                       $where = apply_filters_ref_array('posts_where', array( $where, &$this ) );
136-                       $join = apply_filters_ref_array('posts_join', array( $join, &$this ) );
137+                       $where = apply_query_filters('posts_where', $where, $this);
138+                       $join = apply_query_filters('posts_join', $join, $this);
139                }
140 
141                // Paging
142@@ -2517,11 +2518,11 @@
143                        }
144 
145                        if ( !$q['suppress_filters'] ) {
146-                               $cjoin = apply_filters_ref_array('comment_feed_join', array( $cjoin, &$this ) );
147-                               $cwhere = apply_filters_ref_array('comment_feed_where', array( $cwhere, &$this ) );
148-                               $cgroupby = apply_filters_ref_array('comment_feed_groupby', array( $cgroupby, &$this ) );
149-                               $corderby = apply_filters_ref_array('comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
150-                               $climits = apply_filters_ref_array('comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
151+                               $cjoin = apply_query_filters('comment_feed_join', $cjoin, $this);
152+                               $cwhere = apply_query_filters('comment_feed_where', $cwhere, $this);
153+                               $cgroupby = apply_query_filters('comment_feed_groupby', $cgroupby, $this);
154+                               $corderby = apply_query_filters('comment_feed_orderby', 'comment_date_gmt DESC', $this);
155+                               $climits = apply_query_filters('comment_feed_limits', 'LIMIT ' . get_option('posts_per_rss'), $this);
156                        }
157                        $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
158                        $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
159@@ -2549,16 +2550,16 @@
160                // Apply post-paging filters on where and join.  Only plugins that
161                // manipulate paging queries should use these hooks.
162                if ( !$q['suppress_filters'] ) {
163-                       $where          = apply_filters_ref_array( 'posts_where_paged', array( $where, &$this ) );
164-                       $groupby        = apply_filters_ref_array( 'posts_groupby',             array( $groupby, &$this ) );
165-                       $join           = apply_filters_ref_array( 'posts_join_paged',  array( $join, &$this ) );
166-                       $orderby        = apply_filters_ref_array( 'posts_orderby',             array( $orderby, &$this ) );
167-                       $distinct       = apply_filters_ref_array( 'posts_distinct',    array( $distinct, &$this ) );
168-                       $limits         = apply_filters_ref_array( 'post_limits',               array( $limits, &$this ) );
169-                       $fields         = apply_filters_ref_array( 'posts_fields',              array( $fields, &$this ) );
170+                       $where          = apply_query_filters( 'posts_where_paged',      $where, $this );
171+                       $groupby        = apply_query_filters( 'posts_groupby',          $groupby, $this );
172+                       $join           = apply_query_filters( 'posts_join_paged',       $join, $this );
173+                       $orderby        = apply_query_filters( 'posts_orderby',          $orderby, $this );
174+                       $distinct       = apply_query_filters( 'posts_distinct',         $distinct, $this );
175+                       $limits         = apply_query_filters( 'post_limits',            $limits, $this );
176+                       $fields         = apply_query_filters( 'posts_fields',           $fields, $this );
177 
178                        // Filter all clauses at once, for convenience
179-                       $clauses = (array) apply_filters_ref_array( 'posts_clauses', array( compact( $pieces ), &$this ) );
180+                       $clauses = (array) apply_query_filters( 'posts_clauses', compact( $pieces ), $this );
181                        foreach ( $pieces as $piece )
182                                $$piece = isset( $clauses[ $piece ] ) ? $clauses[ $piece ] : '';
183                }
184@@ -2568,16 +2569,16 @@
185 
186                // Filter again for the benefit of caching plugins.  Regular plugins should use the hooks above.
187                if ( !$q['suppress_filters'] ) {
188-                       $where          = apply_filters_ref_array( 'posts_where_request',               array( $where, &$this ) );
189-                       $groupby        = apply_filters_ref_array( 'posts_groupby_request',             array( $groupby, &$this ) );
190-                       $join           = apply_filters_ref_array( 'posts_join_request',                array( $join, &$this ) );
191-                       $orderby        = apply_filters_ref_array( 'posts_orderby_request',             array( $orderby, &$this ) );
192-                       $distinct       = apply_filters_ref_array( 'posts_distinct_request',    array( $distinct, &$this ) );
193-                       $fields         = apply_filters_ref_array( 'posts_fields_request',              array( $fields, &$this ) );
194-                       $limits         = apply_filters_ref_array( 'post_limits_request',               array( $limits, &$this ) );
195+                       $where          = apply_query_filters( 'posts_where_request',           $where, $this );
196+                       $groupby        = apply_query_filters( 'posts_groupby_request',         $groupby, $this );
197+                       $join           = apply_query_filters( 'posts_join_request',            $join, $this );
198+                       $orderby        = apply_query_filters( 'posts_orderby_request',         $orderby, $this );
199+                       $distinct       = apply_query_filters( 'posts_distinct_request',        $distinct, $this );
200+                       $fields         = apply_query_filters( 'posts_fields_request',          $fields, $this );
201+                       $limits         = apply_query_filters( 'post_limits_request',           $limits, $this );
202 
203                        // Filter all clauses at once, for convenience
204-                       $clauses = (array) apply_filters_ref_array( 'posts_clauses_request', array( compact( $pieces ), &$this ) );
205+                       $clauses = (array) apply_query_filters( 'posts_clauses_request', compact( $pieces ), $this );
206                        foreach ( $pieces as $piece )
207                                $$piece = isset( $clauses[ $piece ] ) ? $clauses[ $piece ] : '';
208                }
209@@ -2593,7 +2594,7 @@
210 
211                $this->request = " SELECT $found_rows $distinct $fields FROM $wpdb->posts $join WHERE 1=1 $where $groupby $orderby $limits";
212                if ( !$q['suppress_filters'] )
213-                       $this->request = apply_filters_ref_array('posts_request', array( $this->request, &$this ) );
214+                       $this->request = apply_query_filters('posts_request', $this->request, $this);
215 
216                if ( 'ids' == $q['fields'] ) {
217                        $this->posts = $wpdb->get_col($this->request);
218@@ -2615,25 +2616,25 @@
219 
220                // Raw results filter.  Prior to status checks.
221                if ( !$q['suppress_filters'] )
222-                       $this->posts = apply_filters_ref_array('posts_results', array( $this->posts, &$this ) );
223+                       $this->posts = apply_query_filters('posts_results', $this->posts, $this);
224 
225                if ( !empty($this->posts) && $this->is_comment_feed && $this->is_singular ) {
226-                       $cjoin = apply_filters_ref_array('comment_feed_join', array( '', &$this ) );
227-                       $cwhere = apply_filters_ref_array('comment_feed_where', array( "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'", &$this ) );
228-                       $cgroupby = apply_filters_ref_array('comment_feed_groupby', array( '', &$this ) );
229+                       $cjoin = apply_query_filters('comment_feed_join', '', $this);
230+                       $cwhere = apply_query_filters('comment_feed_where', "WHERE comment_post_ID = '{$this->posts[0]->ID}' AND comment_approved = '1'", $this);
231+                       $cgroupby = apply_query_filters('comment_feed_groupby', '', $this);
232                        $cgroupby = ( ! empty( $cgroupby ) ) ? 'GROUP BY ' . $cgroupby : '';
233-                       $corderby = apply_filters_ref_array('comment_feed_orderby', array( 'comment_date_gmt DESC', &$this ) );
234+                       $corderby = apply_query_filters('comment_feed_orderby', 'comment_date_gmt DESC', $this);
235                        $corderby = ( ! empty( $corderby ) ) ? 'ORDER BY ' . $corderby : '';
236-                       $climits = apply_filters_ref_array('comment_feed_limits', array( 'LIMIT ' . get_option('posts_per_rss'), &$this ) );
237+                       $climits = apply_query_filters('comment_feed_limits', 'LIMIT ' . get_option('posts_per_rss'), $this);
238                        $comments_request = "SELECT $wpdb->comments.* FROM $wpdb->comments $cjoin $cwhere $cgroupby $corderby $climits";
239                        $this->comments = $wpdb->get_results($comments_request);
240                        $this->comment_count = count($this->comments);
241                }
242 
243                if ( !$q['no_found_rows'] && !empty($limits) ) {
244-                       $found_posts_query = apply_filters_ref_array( 'found_posts_query', array( 'SELECT FOUND_ROWS()', &$this ) );
245+                       $found_posts_query = apply_query_filters( 'found_posts_query', 'SELECT FOUND_ROWS()', $this);
246                        $this->found_posts = $wpdb->get_var( $found_posts_query );
247-                       $this->found_posts = apply_filters_ref_array( 'found_posts', array( $this->found_posts, &$this ) );
248+                       $this->found_posts = apply_query_filters( 'found_posts', $this->found_posts, $this);
249                        $this->max_num_pages = ceil($this->found_posts / $q['posts_per_page']);
250                }
251 
252@@ -2666,7 +2667,7 @@
253                        }
254 
255                        if ( $this->is_preview && current_user_can( $edit_cap, $this->posts[0]->ID ) )
256-                               $this->posts[0] = apply_filters_ref_array('the_preview', array( $this->posts[0], &$this ));
257+                               $this->posts[0] = apply_query_filters('the_preview', $this->posts[0], $this);
258                }
259 
260                // Put sticky posts at the top of the posts array
261@@ -2720,7 +2721,7 @@
262                }
263 
264                if ( !$q['suppress_filters'] )
265-                       $this->posts = apply_filters_ref_array('the_posts', array( $this->posts, &$this ) );
266+                       $this->posts = apply_query_filters('the_posts', $this->posts, $this);
267 
268                $this->post_count = count($this->posts);
269