From 19d5bd0787c9daf1d8a67bf5c0a3e997abf1f89a Mon Sep 17 00:00:00 2001 From: Peter Wilson Date: Thu, 10 Nov 2022 00:44:10 +0000 Subject: [PATCH] Posts, Post Types: Revert `get_page_by_title()`'s use of `WP_Query`. Revert to legacy database query in `get_pages_by_title()`. Due to the lack of `orderby` clause in the previous database query, it is not possible to gain consistent results by converting the function to a `WP_Query` wrapper. Reverts [54271, 54242, 54234]. Props Bjorn2404, 10upsimon, dilipbheda, mukesh27, spacedmonkey, TimothyBlynJacobs, rjasdfiii, stentibbing, pbiron, pento. Fixes #57039, #56991. See #57041. git-svn-id: https://develop.svn.wordpress.org/trunk@54782 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/post.php | 49 ++- tests/phpunit/tests/post/getPageByTitle.php | 340 -------------------- 2 files changed, 33 insertions(+), 356 deletions(-) delete mode 100644 tests/phpunit/tests/post/getPageByTitle.php diff --git a/src/wp-includes/post.php b/src/wp-includes/post.php index 6a7c637831..381ee526b4 100644 --- a/src/wp-includes/post.php +++ b/src/wp-includes/post.php @@ -5766,6 +5766,8 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { * @since 2.1.0 * @since 3.0.0 The `$post_type` parameter was added. * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param string $page_title Page title. * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which * correspond to a WP_Post object, an associative array, or a numeric array, @@ -5774,25 +5776,40 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) { * @return WP_Post|array|null WP_Post (or array) on success, or null on failure. */ function get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) { - $args = array( - 'title' => $page_title, - 'post_type' => $post_type, - 'post_status' => get_post_stati(), - 'posts_per_page' => 1, - 'update_post_term_cache' => false, - 'update_post_meta_cache' => false, - 'no_found_rows' => true, - 'orderby' => 'post_date ID', - 'order' => 'ASC', - ); - $query = new WP_Query( $args ); - $pages = $query->posts; + global $wpdb; - if ( empty( $pages ) ) { - return null; + if ( is_array( $post_type ) ) { + $post_type = esc_sql( $post_type ); + $post_type_in_string = "'" . implode( "','", $post_type ) . "'"; + $sql = $wpdb->prepare( + " + SELECT ID + FROM $wpdb->posts + WHERE post_title = %s + AND post_type IN ($post_type_in_string) + ", + $page_title + ); + } else { + $sql = $wpdb->prepare( + " + SELECT ID + FROM $wpdb->posts + WHERE post_title = %s + AND post_type = %s + ", + $page_title, + $post_type + ); } - return get_post( $pages[0], $output ); + $page = $wpdb->get_var( $sql ); + + if ( $page ) { + return get_post( $page, $output ); + } + + return null; } /** diff --git a/tests/phpunit/tests/post/getPageByTitle.php b/tests/phpunit/tests/post/getPageByTitle.php deleted file mode 100644 index 1c94987d5f..0000000000 --- a/tests/phpunit/tests/post/getPageByTitle.php +++ /dev/null @@ -1,340 +0,0 @@ -post->create_many( - 2, - array( - 'post_type' => 'page', - ) - ); - - // Fill the database with some attachments. - $factory->post->create_many( - 2, - array( - 'post_type' => 'attachment', - ) - ); - - // Fill the database with some test post types. - register_post_type( 'wptests_pt' ); - $factory->post->create_many( - 2, - array( - 'post_type' => 'wptests_pt', - ) - ); - } - - /** - * @ticket 36905 - */ - public function test_get_page_by_title_priority() { - $attachment = self::factory()->post->create_and_get( - array( - 'post_title' => 'some-other-page', - 'post_type' => 'attachment', - ) - ); - $page = self::factory()->post->create_and_get( - array( - 'post_title' => 'some-page', - 'post_type' => 'page', - ) - ); - - $this->assertEquals( $page, get_page_by_title( 'some-page' ), 'should return a post of the requested type before returning an attachment.' ); - - $this->assertEquals( $attachment, get_page_by_title( 'some-other-page', OBJECT, 'attachment' ), "will still select an attachment when a post of the requested type doesn't exist." ); - } - - /** - * @ticket 36905 - */ - public function test_should_match_top_level_page() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $page, $found->ID ); - } - - /** - * @ticket 36905 - * @ticket 56609 - */ - public function test_should_be_case_insensitive_match() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'Foo', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $page, $found->ID ); - } - - /** - * Test the oldest published post is matched first. - * - * Per the docs: in case of more than one post having the same title, - * it will check the oldest publication date, not the smallest ID. - * - * @ticket 36905 - * @ticket 56609 - */ - public function test_should_match_oldest_published_date_when_titles_match() { - self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - $old_page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - 'post_date' => '1984-01-11 05:00:00', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $old_page, $found->ID ); - } - - /** - * @ticket 36905 - */ - public function test_inherit() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - 'post_status' => 'inherit', - ) - ); - - $found = get_page_by_title( 'foo' ); - - $this->assertSame( $page, $found->ID ); - } - - /** - * @ticket 36905 - */ - public function test_should_obey_post_type() { - register_post_type( 'wptests_pt' ); - - $page = self::factory()->post->create( - array( - 'post_type' => 'wptests_pt', - 'post_title' => 'foo', - ) - ); - - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return null, as post type does not match' ); - - $found = get_page_by_title( 'foo', OBJECT, 'wptests_pt' ); - $this->assertSame( $page, $found->ID, 'Should return find post, as post type does do match' ); - } - - - /** - * @ticket 36905 - */ - public function test_should_hit_cache() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertSame( $page, $found->ID, 'Should return find page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo' ); - $this->assertSame( $page, $found->ID, 'Should return find page on second run.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_bad_title_should_be_cached() { - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page on second run.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_bad_title_served_from_cache_should_not_fall_back_on_current_post() { - global $post; - - // Fake the global. - $post = self::factory()->post->create_and_get(); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo' ); - $this->assertNull( $found, 'Should return not find a page on second run.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_cache_should_not_match_post_in_different_post_type_with_same_title() { - register_post_type( 'wptests_pt' ); - - $p1 = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - $p2 = self::factory()->post->create( - array( - 'post_type' => 'wptests_pt', - 'post_title' => 'foo', - ) - ); - - // Prime cache for the page. - $found = get_page_by_title( 'foo' ); - $this->assertSame( $p1, $found->ID, 'Should find a page.' ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'foo', OBJECT, 'wptests_pt' ); - $this->assertSame( $p2, $found->ID, 'Should find a post with post type wptests_pt.' ); - ++$num_queries; - $this->assertSame( $num_queries, get_num_queries(), 'Should result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_cache_should_be_invalidated_when_post_title_is_edited() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - $this->assertSame( $page, $found->ID, 'Should find a page.' ); - - wp_update_post( - array( - 'ID' => $page, - 'post_title' => 'bar', - ) - ); - - $num_queries = get_num_queries(); - - $found = get_page_by_title( 'bar' ); - $this->assertSame( $page, $found->ID, 'Should find a page with the new title.' ); - ++$num_queries; - $this->assertSame( $num_queries, get_num_queries(), 'Should result in another database query.' ); - } - - /** - * @ticket 36905 - */ - public function test_output_param_should_be_obeyed_for_cached_value() { - $page = self::factory()->post->create( - array( - 'post_type' => 'page', - 'post_title' => 'foo', - ) - ); - - // Prime cache. - $found = get_page_by_title( 'foo' ); - - $num_queries = get_num_queries(); - $this->assertSame( $page, $found->ID, 'Should find a page.' ); - - $object = get_page_by_title( 'foo', OBJECT ); - $this->assertIsObject( $object, 'Should be an object.' ); - $this->assertSame( $page, $object->ID, 'Should match post id.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - - $array_n = get_page_by_title( 'foo', ARRAY_N ); - ++$num_queries; // Add one database query for loading of post metadata. - $this->assertIsArray( $array_n, 'Should be numbric array.' ); - $this->assertSame( $page, $array_n[0], 'Should match post id.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - - $array_a = get_page_by_title( 'foo', ARRAY_A ); - $this->assertIsArray( $array_a, 'Should be associative array.' ); - $this->assertSame( $page, $array_a['ID'], 'Should match post id.' ); - $this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' ); - } - - /** - * Ensure get_page_by_title() only runs the query once. - * - * @ticket 56721 - * @covers ::get_page_by_title - */ - public function test_should_not_run_query_more_than_once() { - $page = self::factory()->post->create_and_get( - array( - 'post_title' => 'some-page', - 'post_type' => 'page', - ) - ); - - // Use the `pre_get_posts` hook to ensure the query is only run once. - $ma = new MockAction(); - add_action( 'pre_get_posts', array( $ma, 'action' ) ); - - get_page_by_title( 'some-page' ); - $this->assertSame( 1, $ma->get_call_count(), 'Query does not run exactly once.' ); - } -}