Skip to content

Commit 7ad9e7d

Browse files
committed
Posts, Post types: Cache get_page_by_title().
Convert `get_page_by_title()` to a `WP_Query` wrapper to take advantage of the object caching built in to the latter. Add dedicated unit tests for the function `get_page_by_title()`. Props spacedmonkey, boonebgorges, igmoweb, pcfreak30, pbearne. Fixes #36905. git-svn-id: https://develop.svn.wordpress.org/trunk@54234 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 45da79e commit 7ad9e7d

File tree

2 files changed

+252
-33
lines changed

2 files changed

+252
-33
lines changed

src/wp-includes/post.php

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5756,8 +5756,6 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
57565756
* @since 2.1.0
57575757
* @since 3.0.0 The `$post_type` parameter was added.
57585758
*
5759-
* @global wpdb $wpdb WordPress database abstraction object.
5760-
*
57615759
* @param string $page_title Page title.
57625760
* @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which
57635761
* correspond to a WP_Post object, an associative array, or a numeric array,
@@ -5766,40 +5764,25 @@ function get_page_by_path( $page_path, $output = OBJECT, $post_type = 'page' ) {
57665764
* @return WP_Post|array|null WP_Post (or array) on success, or null on failure.
57675765
*/
57685766
function get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) {
5769-
global $wpdb;
5770-
5771-
if ( is_array( $post_type ) ) {
5772-
$post_type = esc_sql( $post_type );
5773-
$post_type_in_string = "'" . implode( "','", $post_type ) . "'";
5774-
$sql = $wpdb->prepare(
5775-
"
5776-
SELECT ID
5777-
FROM $wpdb->posts
5778-
WHERE post_title = %s
5779-
AND post_type IN ($post_type_in_string)
5780-
",
5781-
$page_title
5782-
);
5783-
} else {
5784-
$sql = $wpdb->prepare(
5785-
"
5786-
SELECT ID
5787-
FROM $wpdb->posts
5788-
WHERE post_title = %s
5789-
AND post_type = %s
5790-
",
5791-
$page_title,
5792-
$post_type
5793-
);
5794-
}
5795-
5796-
$page = $wpdb->get_var( $sql );
5767+
$args = array(
5768+
'post_title' => $page_title,
5769+
'post_type' => $post_type,
5770+
'post_status' => get_post_stati(),
5771+
'posts_per_page' => 1,
5772+
'update_post_term_cache' => false,
5773+
'update_post_meta_cache' => false,
5774+
'no_found_rows' => true,
5775+
'orderby' => 'ID',
5776+
'order' => 'ASC',
5777+
);
5778+
$query = new WP_Query( $args );
5779+
$pages = $query->get_posts();
57975780

5798-
if ( $page ) {
5799-
return get_post( $page, $output );
5781+
if ( empty( $pages ) ) {
5782+
return null;
58005783
}
58015784

5802-
return null;
5785+
return get_post( $pages[0], $output );
58035786
}
58045787

58055788
/**
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
<?php
2+
3+
/**
4+
* @group post
5+
* @covers ::get_page_by_title
6+
*/
7+
class Tests_Post_GetPageByTitle extends WP_UnitTestCase {
8+
/**
9+
* @ticket 36905
10+
*/
11+
public function test_get_page_by_title_priority() {
12+
$attachment = self::factory()->post->create_and_get(
13+
array(
14+
'post_title' => 'some-other-page',
15+
'post_type' => 'attachment',
16+
)
17+
);
18+
$page = self::factory()->post->create_and_get(
19+
array(
20+
'post_title' => 'some-page',
21+
'post_type' => 'page',
22+
)
23+
);
24+
25+
$this->assertEquals( $page, get_page_by_title( 'some-page' ), 'should return a post of the requested type before returning an attachment.' );
26+
27+
$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." );
28+
}
29+
30+
/**
31+
* @ticket 36905
32+
*/
33+
public function test_should_match_top_level_page() {
34+
$page = self::factory()->post->create(
35+
array(
36+
'post_type' => 'page',
37+
'post_title' => 'foo',
38+
)
39+
);
40+
41+
$found = get_page_by_title( 'foo' );
42+
43+
$this->assertSame( $page, $found->ID );
44+
}
45+
46+
/**
47+
* @ticket 36905
48+
*/
49+
public function test_inherit() {
50+
$page = self::factory()->post->create(
51+
array(
52+
'post_type' => 'page',
53+
'post_title' => 'foo',
54+
'post_status' => 'inherit',
55+
)
56+
);
57+
58+
$found = get_page_by_title( 'foo' );
59+
60+
$this->assertSame( $page, $found->ID );
61+
}
62+
63+
/**
64+
* @ticket 36905
65+
*/
66+
public function test_should_obey_post_type() {
67+
register_post_type( 'wptests_pt' );
68+
69+
$page = self::factory()->post->create(
70+
array(
71+
'post_type' => 'wptests_pt',
72+
'post_title' => 'foo',
73+
)
74+
);
75+
76+
$found = get_page_by_title( 'foo' );
77+
$this->assertNull( $found, 'Should return null, as post type does not match' );
78+
79+
$found = get_page_by_title( 'foo', OBJECT, 'wptests_pt' );
80+
$this->assertSame( $page, $found->ID, 'Should return find post, as post type does do match' );
81+
}
82+
83+
84+
/**
85+
* @ticket 36905
86+
*/
87+
public function test_should_hit_cache() {
88+
$page = self::factory()->post->create(
89+
array(
90+
'post_type' => 'page',
91+
'post_title' => 'foo',
92+
)
93+
);
94+
95+
// Prime cache.
96+
$found = get_page_by_title( 'foo' );
97+
$this->assertSame( $page, $found->ID, 'Should return find page.' );
98+
99+
$num_queries = get_num_queries();
100+
101+
$found = get_page_by_title( 'foo' );
102+
$this->assertSame( $page, $found->ID, 'Should return find page on second run.' );
103+
$this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' );
104+
}
105+
106+
/**
107+
* @ticket 36905
108+
*/
109+
public function test_bad_title_should_be_cached() {
110+
// Prime cache.
111+
$found = get_page_by_title( 'foo' );
112+
$this->assertNull( $found, 'Should return not find a page.' );
113+
114+
$num_queries = get_num_queries();
115+
116+
$found = get_page_by_title( 'foo' );
117+
$this->assertNull( $found, 'Should return not find a page on second run.' );
118+
$this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' );
119+
}
120+
121+
/**
122+
* @ticket 36905
123+
*/
124+
public function test_bad_title_served_from_cache_should_not_fall_back_on_current_post() {
125+
global $post;
126+
127+
// Fake the global.
128+
$post = self::factory()->post->create_and_get();
129+
130+
// Prime cache.
131+
$found = get_page_by_title( 'foo' );
132+
$this->assertNull( $found, 'Should return not find a page.' );
133+
134+
$num_queries = get_num_queries();
135+
136+
$found = get_page_by_title( 'foo' );
137+
$this->assertNull( $found, 'Should return not find a page on second run.' );
138+
$this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' );
139+
}
140+
141+
/**
142+
* @ticket 36905
143+
*/
144+
public function test_cache_should_not_match_post_in_different_post_type_with_same_title() {
145+
register_post_type( 'wptests_pt' );
146+
147+
$p1 = self::factory()->post->create(
148+
array(
149+
'post_type' => 'page',
150+
'post_title' => 'foo',
151+
)
152+
);
153+
154+
$p2 = self::factory()->post->create(
155+
array(
156+
'post_type' => 'wptests_pt',
157+
'post_title' => 'foo',
158+
)
159+
);
160+
161+
// Prime cache for the page.
162+
$found = get_page_by_title( 'foo' );
163+
$this->assertSame( $p1, $found->ID, 'Should find a page.' );
164+
165+
$num_queries = get_num_queries();
166+
167+
$found = get_page_by_title( 'foo', OBJECT, 'wptests_pt' );
168+
$this->assertSame( $p2, $found->ID, 'Should find a post with post type wptests_pt.' );
169+
$num_queries ++;
170+
$this->assertSame( $num_queries, get_num_queries(), 'Should result in another database query.' );
171+
}
172+
173+
/**
174+
* @ticket 36905
175+
*/
176+
public function test_cache_should_be_invalidated_when_post_title_is_edited() {
177+
$page = self::factory()->post->create(
178+
array(
179+
'post_type' => 'page',
180+
'post_title' => 'foo',
181+
)
182+
);
183+
184+
// Prime cache.
185+
$found = get_page_by_title( 'foo' );
186+
$this->assertSame( $page, $found->ID, 'Should find a page.' );
187+
188+
wp_update_post(
189+
array(
190+
'ID' => $page,
191+
'post_title' => 'bar',
192+
)
193+
);
194+
195+
$num_queries = get_num_queries();
196+
197+
$found = get_page_by_title( 'bar' );
198+
$this->assertSame( $page, $found->ID, 'Should find a page with the new title.' );
199+
$num_queries++;
200+
$this->assertSame( $num_queries, get_num_queries(), 'Should result in another database query.' );
201+
}
202+
203+
/**
204+
* @ticket 36905
205+
*/
206+
public function test_output_param_should_be_obeyed_for_cached_value() {
207+
$page = self::factory()->post->create(
208+
array(
209+
'post_type' => 'page',
210+
'post_title' => 'foo',
211+
)
212+
);
213+
214+
// Prime cache.
215+
$found = get_page_by_title( 'foo' );
216+
217+
$num_queries = get_num_queries();
218+
$this->assertSame( $page, $found->ID, 'Should find a page.' );
219+
220+
$object = get_page_by_title( 'foo', OBJECT );
221+
$this->assertIsObject( $object, 'Should be an object.' );
222+
$this->assertSame( $page, $object->ID, 'Should match post id.' );
223+
$this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' );
224+
225+
$array_n = get_page_by_title( 'foo', ARRAY_N );
226+
$num_queries++; // Add one database query for loading of post metadata.
227+
$this->assertIsArray( $array_n, 'Should be numbric array.' );
228+
$this->assertSame( $page, $array_n[0], 'Should match post id.' );
229+
$this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' );
230+
231+
$array_a = get_page_by_title( 'foo', ARRAY_A );
232+
$this->assertIsArray( $array_a, 'Should be associative array.' );
233+
$this->assertSame( $page, $array_a['ID'], 'Should match post id.' );
234+
$this->assertSame( $num_queries, get_num_queries(), 'Should not result in another database query.' );
235+
}
236+
}

0 commit comments

Comments
 (0)