From 413207be6d71836d43acf0b6382509c07ac245e8 Mon Sep 17 00:00:00 2001 From: Andrew Nacin Date: Thu, 20 Mar 2014 02:59:15 +0000 Subject: [PATCH] Fix various issues with WP_Adjacent_Post: * Performance / number of queries. * Incorrect results caused by sticky posts. * Back compat for filters, which had used "WHERE" while WP_Query does not; and fixing table references. props ethitter. fixes #26937. Built from https://develop.svn.wordpress.org/trunk@27635 git-svn-id: http://core.svn.wordpress.org/trunk@27478 1a063a9b-81f0-0310-95a4-ce76da25c4cd --- wp-includes/link-template.php | 126 +++++++++++++++++++++++++++------- 1 file changed, 103 insertions(+), 23 deletions(-) diff --git a/wp-includes/link-template.php b/wp-includes/link-template.php index a838c2a187..d58c951810 100644 --- a/wp-includes/link-template.php +++ b/wp-includes/link-template.php @@ -1243,7 +1243,7 @@ class WP_Adjacent_Post { } /** - * Allow direct access to adjacent post from the class instance itself + * Allow direct access to adjacent post from the class instance itself. * * @param string $property Property to get. * @return mixed String when adjacent post is found and post property exists. Null when no adjacent post is found. @@ -1273,7 +1273,7 @@ class WP_Adjacent_Post { * @return mixed Post object on success. False if no adjacent post exists. Null on failure. */ protected function get_post( $args ) { - $this->current_post = get_post( $args['post'] ); + $this->current_post = get_post( $args['post'] ); $this->excluded_terms = array_map( 'intval', $args['excluded_terms'] ); $this->adjacent = $args['previous'] ? 'previous' : 'next'; $this->taxonomy = $args['taxonomy']; @@ -1291,14 +1291,20 @@ class WP_Adjacent_Post { // Build our arguments for WP_Query. $query_args = array( - 'posts_per_page' => 1, - 'post_status' => 'publish', - 'post_type' => 'post', - 'orderby' => 'date', - 'order' => 'previous' === $this->adjacent ? 'DESC' : 'ASC', - 'no_found_rows' => true, - 'cache_results' => true, - 'date_query' => array(), + 'posts_per_page' => 1, + 'post_status' => 'publish', + 'post_type' => 'post', + 'orderby' => 'date', + 'order' => 'previous' === $this->adjacent ? 'DESC' : 'ASC', + 'ignore_sticky_posts' => true, + 'date_query' => array(), + + // Performance considerations: + 'no_found_rows' => true, + 'cache_results' => true, + 'update_post_term_cache' => false, + 'update_post_meta_cache' => false, + 'split_the_query' => wp_using_ext_object_cache(), ); $tax_query = array(); @@ -1388,14 +1394,37 @@ class WP_Adjacent_Post { */ remove_filter( 'posts_clauses', array( $this, 'filter' ) ); - /* - * The `join` and `where` filters are identical in their parameters, - * so we can use the same approach for both. + /** + * If no legacy filter callbacks are registered, proceed no further. */ - foreach ( array( 'join', 'where' ) as $clause ) { - if ( has_filter( 'get_' . $this->adjacent . '_post_' . $clause ) ) { - $clauses[ $clause ] = $this->filter_join_and_where( $clauses[ $clause ], $clause ); - } + if ( ! has_filter( 'get_' . $this->adjacent . '_post_join' ) && ! has_filter( 'get_' . $this->adjacent . '_post_where' ) && ! has_filter( 'get_' . $this->adjacent . '_post_sort' ) ) { + return $clauses; + } + + /** + * Posts table must be aliased as `p` for backwards compatibility with query previously generated by `get_adjacent_post()`. + */ + $clauses = array_map( array( $this, 'alias_posts_table' ), $clauses ); + + /** + * Apply the legacy `join` filter. + */ + if ( has_filter( 'get_' . $this->adjacent . '_post_join' ) ) { + $clauses['join'] = $this->filter_join( $clauses['join'] ); + } + + /** + * Posts table must be aliased as `p` for backwards compatibility with query previously generated by `get_adjacent_post()`. + * No filter on the table name exists, so we have to leverage the next applied filter, that for the `join` clause. + * We wait to apply this until after the legacy filter is applied so that the legacy filter doesn't remove the alias. + */ + $clauses['join'] = 'AS p ' . $clauses['join']; + + /** + * Apply the legacy `where` filter. + */ + if ( has_filter( 'get_' . $this->adjacent . '_post_where' ) ) { + $clauses['where'] = $this->filter_where( $clauses['where'] ); } /* @@ -1411,18 +1440,69 @@ class WP_Adjacent_Post { } /** - * Apply the deprecated `join` or `where` clause filter to the clauses built by WP_Query. + * Alias posts table as `p` to match query previously built by `get_adjacent_post()`. * - * @param string $value - * @param string $clause + * @global $wpdb + * @param string Clause to alias * @return string */ - protected function filter_join_and_where( $value, $clause ) { + protected function alias_posts_table( $clause ) { + global $wpdb; + + return str_replace( $wpdb->posts, 'p', $clause ); + } + + /** + * Apply the deprecated `join` clause filter to the clause built by WP_Query. + * + * @param string $join + * @return string + */ + protected function filter_join( $join ) { /** - * @todo Minimal hook docs * @deprecated 3.9.0 */ - return apply_filters( 'get_' . $this->adjacent . '_post_' . $clause, $value, $this->in_same_term, $this->excluded_terms ); + return apply_filters( 'get_' . $this->adjacent . '_post_join', $join, $this->in_same_term, $this->excluded_terms ); + } + + /** + * Apply the deprecated `where` clause filter to the clause built by WP_Query. + * + * @param string $join + * @return string + */ + protected function filter_where( $where ) { + $where = trim( $where ); + + // The legacy filter passed the entire clause, including the `WHERE`, while WP_Query's filter does not. + // We prepend the `WHERE` for the benefit of legacy callbacks that look for it. + if ( 0 !== stripos( $where, 'where' ) ) { + $where = 'WHERE 1=1 ' . $where; + } + + /** + * @deprecated 3.9.0 + */ + $where = apply_filters( 'get_' . $this->adjacent . '_post_where', $where, $this->in_same_term, $this->excluded_terms ); + + $where = trim( $where ); + + // The legacy filter passed the entire clause, including the `WHERE`, while WP_Query's filter does not. + // Removing the `WHERE` is necessary as we've added it above, and the legacy filter could include it in the returned string. + if ( 0 === stripos( $where, 'where 1=1' ) ) { + $where = substr( $where, 9 ); + } elseif ( 0 === stripos( $where, 'where' ) ) { + $where = substr( $where, 5 ); + } + + $where = trim( $where ); + + // WP_Query expects that the string returned begins with `AND`, as it is prepended with "1=1" when the clauses are joined + if ( 0 !== stripos( $where, 'and' ) ) { + $where = 'AND ' . $where; + } + + return $where; } /**