Skip to content

Commit 068b578

Browse files
committed
Code Quality: Improve WP_Post type coverage.
Add a `Data_Array` array-shape type describing every key returned by `WP_Post::to_array()`, and tighten each `WP_Post` property to the narrowest type the schema and code guarantee: * `ID` and `post_parent` become `non-negative-int`. * `comment_count` becomes `numeric-string`, and `post_author` becomes `numeric-string|''` (a user ID, or an empty string for a default post that has not yet been assigned an author). * The always-populated `post_status`, `comment_status`, `ping_status`, and `post_type` slugs become `non-empty-string`, left open rather than enumerated so custom statuses and types remain valid. * The magic `ancestors`, `post_category`, and `tags_input` accessors become precise lists (`list<non-negative-int>` and `list<non-empty-string>`). The shape stays open because `WP_Post` permits dynamic properties. Fields that can legitimately be empty, including the datetime fields (which `get_default_post_to_edit()` may leave empty), stay `string`, and `menu_order` stays `int` since it can be negative. Correct `WP_Post::$filter`, which was previously typed without `null` or the `'sample'` context: an unsanitized post has no `filter` (checked in core via `isset()` and `empty()`), and `get_sample_permalink()` has assigned `'sample'` since [8526]. Widen `sanitize_post_field()`'s `$context` to accept `'sample'` to match, which it already treats as a `'display'` context. Also type `get_post_ancestors()` as returning `list<non-negative-int>`, read the post in `trackback_url_list()` via `to_array()` while guarding against a missing post, and cast `post_author` to a string in `WP_Customize_Nav_Menu_Item_Setting` and `inject_ignored_hooked_blocks_metadata_attributes()`, which each populate an object before passing it to `new WP_Post()`. Developed in WordPress#12491. Follow-up to r62648, r62694. See #64898, #64896. git-svn-id: https://develop.svn.wordpress.org/trunk@62717 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 0c5aa8e commit 068b578

5 files changed

Lines changed: 73 additions & 22 deletions

File tree

src/wp-includes/block-template-utils.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1739,9 +1739,15 @@ function inject_ignored_hooked_blocks_metadata_attributes( $changes, $deprecated
17391739
// Required for the WP_Block_Template. Update the post object with the current time.
17401740
$post->post_modified = current_time( 'mysql' );
17411741

1742-
// If the post_author is empty, set it to the current user.
1742+
/*
1743+
* If the post_author is empty, set it to the current user. If it arrived as
1744+
* an int (e.g. from a REST controller), normalize it to a string, since the
1745+
* resulting object is passed to new WP_Post().
1746+
*/
17431747
if ( empty( $post->post_author ) ) {
1744-
$post->post_author = get_current_user_id();
1748+
$post->post_author = (string) get_current_user_id();
1749+
} elseif ( is_int( $post->post_author ) ) {
1750+
$post->post_author = (string) $post->post_author;
17451751
}
17461752

17471753
if ( 'wp_template_part' === $post->post_type && ! isset( $terms['wp_template_part_area'] ) ) {

src/wp-includes/class-wp-post.php

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,41 @@
1414
*
1515
* @property string $page_template
1616
*
17-
* @property-read int[] $ancestors
18-
* @property-read int[] $post_category
19-
* @property-read string[] $tags_input
17+
* @property-read list<non-negative-int> $ancestors
18+
* @property-read list<non-negative-int> $post_category
19+
* @property-read list<non-empty-string> $tags_input
20+
*
21+
* @phpstan-type Data_Array array{
22+
* ID: non-negative-int,
23+
* post_author: numeric-string|'',
24+
* post_date: string,
25+
* post_date_gmt: string,
26+
* post_content: string,
27+
* post_title: string,
28+
* post_excerpt: string,
29+
* post_status: non-empty-string,
30+
* comment_status: non-empty-string,
31+
* ping_status: non-empty-string,
32+
* post_password: string,
33+
* post_name: string,
34+
* to_ping: string,
35+
* pinged: string,
36+
* post_modified: string,
37+
* post_modified_gmt: string,
38+
* post_content_filtered: string,
39+
* post_parent: non-negative-int,
40+
* guid: string,
41+
* menu_order: int,
42+
* post_type: non-empty-string,
43+
* post_mime_type: string,
44+
* comment_count: numeric-string,
45+
* filter: 'raw'|'edit'|'db'|'display'|'attribute'|'js'|'sample'|null,
46+
* ancestors: list<non-negative-int>,
47+
* page_template: string,
48+
* post_category: list<non-negative-int>,
49+
* tags_input: list<non-empty-string>,
50+
* ...
51+
* }
2052
*/
2153
#[AllowDynamicProperties]
2254
final class WP_Post {
@@ -26,17 +58,19 @@ final class WP_Post {
2658
*
2759
* @since 3.5.0
2860
* @var int
61+
* @phpstan-var non-negative-int
2962
*/
3063
public $ID;
3164

3265
/**
3366
* ID of post author.
3467
*
35-
* A numeric string, for compatibility reasons.
68+
* A numeric string, for compatibility reasons. May be an empty string for a
69+
* default post that has not yet been assigned an author.
3670
*
3771
* @since 3.5.0
3872
* @var string
39-
* @phpstan-var numeric-string
73+
* @phpstan-var numeric-string|''
4074
*/
4175
public $post_author = '0';
4276

@@ -85,6 +119,7 @@ final class WP_Post {
85119
*
86120
* @since 3.5.0
87121
* @var string
122+
* @phpstan-var non-empty-string
88123
*/
89124
public $post_status = 'publish';
90125

@@ -93,6 +128,7 @@ final class WP_Post {
93128
*
94129
* @since 3.5.0
95130
* @var string
131+
* @phpstan-var non-empty-string
96132
*/
97133
public $comment_status = 'open';
98134

@@ -101,6 +137,7 @@ final class WP_Post {
101137
*
102138
* @since 3.5.0
103139
* @var string
140+
* @phpstan-var non-empty-string
104141
*/
105142
public $ping_status = 'open';
106143

@@ -165,6 +202,7 @@ final class WP_Post {
165202
*
166203
* @since 3.5.0
167204
* @var int
205+
* @phpstan-var non-negative-int
168206
*/
169207
public $post_parent = 0;
170208

@@ -189,6 +227,7 @@ final class WP_Post {
189227
*
190228
* @since 3.5.0
191229
* @var string
230+
* @phpstan-var non-empty-string
192231
*/
193232
public $post_type = 'post';
194233

@@ -216,9 +255,11 @@ final class WP_Post {
216255
*
217256
* Does not correspond to a DB field.
218257
*
258+
* The 'sample' value is set exclusively by {@see get_sample_permalink()} and is read during permalink previewing.
259+
*
219260
* @since 3.5.0
220-
* @var string
221-
* @phpstan-var 'raw'|'edit'|'db'|'display'|'attribute'|'js'
261+
* @var string|null
262+
* @phpstan-var 'raw'|'edit'|'db'|'display'|'attribute'|'js'|'sample'|null
222263
*/
223264
public $filter;
224265

@@ -389,10 +430,9 @@ public function filter( $filter ) {
389430
*
390431
* @return array<string, mixed> Object as array.
391432
*
392-
* @phpstan-return non-empty-array<string, mixed>
433+
* @phpstan-return Data_Array
393434
*/
394435
public function to_array() {
395-
/** @var non-empty-array<string, mixed> $post */
396436
$post = get_object_vars( $this );
397437

398438
foreach ( array( 'ancestors', 'page_template', 'post_category', 'tags_input' ) as $key ) {
@@ -401,6 +441,7 @@ public function to_array() {
401441
}
402442
}
403443

444+
/** @var Data_Array $post */
404445
return $post;
405446
}
406447
}

src/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -625,7 +625,7 @@ public function value_as_wp_post_nav_menu_item() {
625625
$post = new WP_Post( (object) $item );
626626

627627
if ( empty( $post->post_author ) ) {
628-
$post->post_author = get_current_user_id();
628+
$post->post_author = (string) get_current_user_id();
629629
}
630630

631631
if ( ! isset( $post->type_label ) ) {

src/wp-includes/post.php

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,7 @@ function get_post( $post = null, $output = OBJECT, $filter = 'raw' ) {
11851185
*
11861186
* @param int|WP_Post $post Post ID or post object.
11871187
* @return int[] Array of ancestor IDs or empty array if there are none.
1188+
* @phpstan-return list<non-negative-int>
11881189
*/
11891190
function get_post_ancestors( $post ) {
11901191
$post = get_post( $post );
@@ -3001,19 +3002,20 @@ function sanitize_post( $post, $context = 'display' ) {
30013002
*
30023003
* Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and
30033004
* 'js'. The 'display' context is used by default. 'attribute' and 'js' contexts
3004-
* are treated like 'display' when calling filters.
3005+
* are treated like 'display' when calling filters. The 'sample' value is used
3006+
* for permalink previewing.
30053007
*
30063008
* @since 2.3.0
30073009
* @since 4.4.0 Like `sanitize_post()`, `$context` defaults to 'display'.
30083010
*
30093011
* @param string $field The Post Object field name.
30103012
* @param mixed $value The Post Object value.
30113013
* @param int $post_id Post ID.
3012-
* @param string $context Optional. How to sanitize the field. Possible values are 'raw', 'edit',
3013-
* 'db', 'display', 'attribute' and 'js'. Default 'display'.
3014+
* @param string $context Optional. How to sanitize the field. Possible values are 'raw', 'edit', 'db', 'display',
3015+
* 'attribute' and 'js'. The 'sample' value is used for permalink previewing. Default 'display'.
30143016
* @return mixed Sanitized value.
30153017
*
3016-
* @phpstan-param 'raw'|'edit'|'db'|'display'|'attribute'|'js' $context
3018+
* @phpstan-param 'raw'|'edit'|'db'|'display'|'attribute'|'js'|'sample' $context
30173019
* @phpstan-return (
30183020
* $field is 'ID'|'post_parent'|'menu_order' ? int : (
30193021
* $field is 'ancestors' ? non-negative-int[] : string
@@ -3286,7 +3288,8 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) {
32863288
* @param int $post_id Post ID.
32873289
* @param string $context Context for how to sanitize the field.
32883290
* Accepts 'raw', 'edit', 'db', 'display',
3289-
* 'attribute', or 'js'. Default 'display'.
3291+
* 'attribute', or 'js'. The 'sample' value is
3292+
* used for permalink previewing. Default 'display'.
32903293
*/
32913294
$value = apply_filters( "{$field}", $value, $post_id, $context );
32923295
} else {
@@ -3313,7 +3316,8 @@ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) {
33133316
* @param int $post_id Post ID
33143317
* @param string $context Context for how to sanitize the field.
33153318
* Accepts 'raw', 'edit', 'db', 'display',
3316-
* 'attribute', or 'js'. Default 'display'.
3319+
* 'attribute', or 'js'. The 'sample' value is
3320+
* used for permalink previewing. Default 'display'.
33173321
*/
33183322
$value = apply_filters( "post_{$field}", $value, $post_id, $context );
33193323
}
@@ -6149,11 +6153,11 @@ function get_to_ping( $post ) {
61496153
function trackback_url_list( $tb_list, $post_id ) {
61506154
if ( ! empty( $tb_list ) ) {
61516155
// Get post data.
6152-
$postdata = get_post( $post_id, ARRAY_A );
6153-
6154-
if ( ! $postdata ) {
6156+
$post = get_post( $post_id );
6157+
if ( ! $post ) {
61556158
return;
61566159
}
6160+
$postdata = $post->to_array();
61576161

61586162
// Form an excerpt.
61596163
$excerpt = strip_tags( $postdata['post_excerpt'] ? $postdata['post_excerpt'] : $postdata['post_content'] );

tests/phpunit/tests/customize/nav-menu-item-setting.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -950,7 +950,7 @@ public function test_value_as_wp_post_nav_menu_item() {
950950
$this->assertSame( $post_value['title'], $nav_menu_item->post_title );
951951
$this->assertSame( 123, $nav_menu_item->ID );
952952
$this->assertSame( 123, $nav_menu_item->db_id );
953-
$this->assertSame( wp_get_current_user()->ID, $nav_menu_item->post_author );
953+
$this->assertSame( (string) wp_get_current_user()->ID, $nav_menu_item->post_author );
954954
$this->assertObjectHasProperty( 'type_label', $nav_menu_item );
955955
$expected = apply_filters( 'nav_menu_attr_title', wp_unslash( apply_filters( 'excerpt_save_pre', wp_slash( $post_value['attr_title'] ) ) ) );
956956
$this->assertSame( $expected, $nav_menu_item->attr_title );

0 commit comments

Comments
 (0)