Make WordPress Core

Changeset 55169


Ignore:
Timestamp:
01/31/2023 04:54:22 PM (2 years ago)
Author:
spacedmonkey
Message:

Query: Use WP_Query in get_page_by_path.

Replace raw database queries in get_page_by_path with a call to WP_Query class. This has a number of benefits, including improved caching and priming of post caches. To maintain backwards compatibility, this function calls WP_Query to gets all matching posts of all post statuses.

Props spacedmonkey, peterwilsoncc, costdev,
Fixes #56689.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/post.php

    r55080 r55169  
    56615661 * @since 2.1.0
    56625662 *
    5663  * @global wpdb $wpdb WordPress database abstraction object.
    5664  *
    56655663 * @param string       $page_path Page path.
    56665664 * @param string       $output    Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which
     
    56715669 */
    56725670function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
    5673     global $wpdb;
    5674 
    5675     $last_changed = wp_cache_get_last_changed( 'posts' );
    5676 
    5677     $hash      = md5( $page_path . serialize( $post_type ) );
    5678     $cache_key = "get_page_by_path:$hash:$last_changed";
    5679     $cached    = wp_cache_get( $cache_key, 'posts' );
    5680     if ( false !== $cached ) {
    5681         // Special case: '0' is a bad `$page_path`.
    5682         if ( '0' === $cached || 0 === $cached ) {
    5683             return;
    5684         } else {
    5685             return get_post( $cached, $output );
    5686         }
    5687     }
    5688 
    5689     $page_path     = rawurlencode( urldecode( $page_path ) );
    5690     $page_path     = str_replace( '%2F', '/', $page_path );
    5691     $page_path     = str_replace( '%20', ' ', $page_path );
    5692     $parts         = explode( '/', trim( $page_path, '/' ) );
    5693     $parts         = array_map( 'sanitize_title_for_query', $parts );
    5694     $escaped_parts = esc_sql( $parts );
    5695 
    5696     $in_string = "'" . implode( "','", $escaped_parts ) . "'";
     5671    $page_path = rawurlencode( urldecode( $page_path ) );
     5672    $page_path = str_replace( '%2F', '/', $page_path );
     5673    $page_path = str_replace( '%20', ' ', $page_path );
     5674    $parts     = explode( '/', trim( $page_path, '/' ) );
     5675    $parts     = array_map( 'sanitize_title_for_query', $parts );
    56975676
    56985677    if ( is_array( $post_type ) ) {
     
    57025681    }
    57035682
    5704     $post_types          = esc_sql( $post_types );
    5705     $post_type_in_string = "'" . implode( "','", $post_types ) . "'";
    5706     $sql                 = "
    5707         SELECT ID, post_name, post_parent, post_type
    5708         FROM $wpdb->posts
    5709         WHERE post_name IN ($in_string)
    5710         AND post_type IN ($post_type_in_string)
    5711     ";
    5712 
    5713     $pages = $wpdb->get_results( $sql, OBJECT_K );
     5683    $args = array(
     5684        'post_name__in'          => $parts,
     5685        'post_type'              => $post_types,
     5686        'post_status'            => 'all',
     5687        'posts_per_page'         => -1,
     5688        'update_post_term_cache' => false,
     5689        'update_post_meta_cache' => false,
     5690        'no_found_rows'          => true,
     5691        'orderby'                => 'none',
     5692    );
     5693
     5694    $query = new WP_Query( $args );
     5695    $posts = $query->get_posts();
     5696    $pages = array();
     5697
     5698    foreach ( $posts as $post ) {
     5699        $pages[ $post->ID ] = $post;
     5700    }
    57145701
    57155702    $revparts = array_reverse( $parts );
    57165703
    57175704    $foundid = 0;
    5718     foreach ( (array) $pages as $page ) {
     5705    foreach ( $pages as $page ) {
    57195706        if ( $page->post_name == $revparts[0] ) {
    57205707            $count = 0;
     
    57425729        }
    57435730    }
    5744 
    5745     // We cache misses as well as hits.
    5746     wp_cache_set( $cache_key, $foundid, 'posts' );
    57475731
    57485732    if ( $foundid ) {
  • trunk/tests/phpunit/tests/post/getPageByPath.php

    r51331 r55169  
    104104    }
    105105
     106    /**
     107     * @ticket 56689
     108     *
     109     * @covers ::get_page_by_path
     110     */
     111    public function test_should_match_nested_page_query_count() {
     112        $p1 = self::factory()->post->create(
     113            array(
     114                'post_type' => 'page',
     115                'post_name' => 'foo',
     116            )
     117        );
     118
     119        $p2 = self::factory()->post->create(
     120            array(
     121                'post_type'   => 'page',
     122                'post_name'   => 'bar',
     123                'post_parent' => $p1,
     124            )
     125        );
     126
     127        $p3 = self::factory()->post->create(
     128            array(
     129                'post_type'   => 'page',
     130                'post_name'   => 'baz',
     131                'post_parent' => $p2,
     132            )
     133        );
     134
     135        $queries_before = get_num_queries();
     136        $found          = get_page_by_path( 'foo/bar/baz' );
     137        $queries_after  = get_num_queries();
     138        $cached_post    = wp_cache_get( $p1, 'posts' );
     139
     140        $this->assertSame( 1, $queries_after - $queries_before, 'Only one query should run' );
     141        $this->assertSame( $p3, $found->ID, 'Check to see if the result is correct' );
     142        $this->assertIsObject( $cached_post, 'The cached post is not an object' );
     143    }
     144
     145    /**
     146     * @ticket 56689
     147     *
     148     * @covers ::get_page_by_path
     149     */
     150    public function test_should_match_nested_page_query_count_status() {
     151        $p1 = self::factory()->post->create(
     152            array(
     153                'post_type'   => 'page',
     154                'post_name'   => 'foo',
     155                'post_status' => 'draft',
     156            )
     157        );
     158
     159        $p2 = self::factory()->post->create(
     160            array(
     161                'post_type'   => 'page',
     162                'post_name'   => 'bar',
     163                'post_parent' => $p1,
     164            )
     165        );
     166
     167        $p3 = self::factory()->post->create(
     168            array(
     169                'post_type'   => 'page',
     170                'post_name'   => 'baz',
     171                'post_parent' => $p2,
     172            )
     173        );
     174
     175        $queries_before = get_num_queries();
     176        $found          = get_page_by_path( 'foo/bar/baz' );
     177        $queries_after  = get_num_queries();
     178        $cached_post    = wp_cache_get( $p1, 'posts' );
     179
     180        $this->assertSame( 1, $queries_after - $queries_before, 'Only one query should run' );
     181        $this->assertSame( $p3, $found->ID, 'Check to see if the result is correct' );
     182        $this->assertIsObject( $cached_post, 'The cached post is not an object' );
     183    }
     184
     185    /**
     186     * @ticket 56689
     187     *
     188     * @covers ::get_page_by_path
     189     */
     190    public function test_should_return_null_for_invalid_path() {
     191        $queries_before = get_num_queries();
     192        $get_1          = get_page_by_path( 'should/return/null/for/an/invalid/path' );
     193        $get_2          = get_page_by_path( 'should/return/null/for/an/invalid/path' );
     194        $queries_after  = get_num_queries();
     195
     196        $this->assertNull( $get_1, 'Invalid path should return null.' );
     197        $this->assertSame( 1, $queries_after - $queries_before, 'Only one query should run.' );
     198        $this->assertSame( $get_1, $get_2, 'The cached result should be the same as the uncached result.' );
     199    }
     200
    106201    public function test_should_not_make_partial_match() {
    107202        $p1 = self::factory()->post->create(
Note: See TracChangeset for help on using the changeset viewer.