Make WordPress Core

Changeset 55169


Ignore:
Timestamp:
01/31/2023 04:54:22 PM (4 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.