Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ parameters:
path: src/wp-includes/canonical.php
count: 1

# Both the `edit_post_{$field}` filter in sanitize_post_field() and the `edit_post_{$post->post_type}` action
# normalize to the hook name "edit_post_{...}", so PHPStan compares every call of the action against the
# filter's two documented parameters. The two are different hooks with different signatures.
-
message: '#^do_action\(\) for hook "edit_post_\{\.\.\.\}" provides 3 arguments, but the hook is documented with 2 parameters\.$#'
identifier: wordpress.hookParamCountMismatch
paths:
- src/wp-includes/class-wp-customize-manager.php
- src/wp-includes/comment.php
- src/wp-includes/post.php

# In core, @internal is advisory for extenders: it marks a symbol that plugins should not rely on. Core itself is
# one codebase and its own uses of these symbols are intended. PHPStan scopes @internal by root namespace, and
# since core symbols live in the global namespace, it reports every use outside the declaring class or file.
Expand Down
10 changes: 5 additions & 5 deletions src/wp-includes/class-wp-customize-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -3109,19 +3109,19 @@ public function trash_changeset_post( $post ) {
wp_transition_post_status( $new_status, $previous_status, $post );

/** This action is documented in wp-includes/post.php */
do_action( "edit_post_{$post->post_type}", $post->ID, $post );
do_action( "edit_post_{$post->post_type}", $post->ID, $post, array() );

/** This action is documented in wp-includes/post.php */
do_action( 'edit_post', $post->ID, $post );
do_action( 'edit_post', $post->ID, $post, array() );

/** This action is documented in wp-includes/post.php */
do_action( "save_post_{$post->post_type}", $post->ID, $post, true );
do_action( "save_post_{$post->post_type}", $post->ID, $post, true, array() );

/** This action is documented in wp-includes/post.php */
do_action( 'save_post', $post->ID, $post, true );
do_action( 'save_post', $post->ID, $post, true, array() );

/** This action is documented in wp-includes/post.php */
do_action( 'wp_insert_post', $post->ID, $post, true );
do_action( 'wp_insert_post', $post->ID, $post, true, array() );

wp_after_insert_post( get_post( $post_id ), true, $post );

Expand Down
4 changes: 2 additions & 2 deletions src/wp-includes/comment.php
Original file line number Diff line number Diff line change
Expand Up @@ -3276,10 +3276,10 @@ function wp_update_comment_count_now( $post_id ) {
do_action( 'wp_update_comment_count', $post_id, $new, $old );

/** This action is documented in wp-includes/post.php */
do_action( "edit_post_{$post->post_type}", $post_id, $post );
do_action( "edit_post_{$post->post_type}", $post_id, $post, array() );

/** This action is documented in wp-includes/post.php */
do_action( 'edit_post', $post_id, $post );
do_action( 'edit_post', $post_id, $post, array() );

return true;
}
Expand Down
128 changes: 83 additions & 45 deletions src/wp-includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -5116,12 +5116,15 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true )
* Filters whether or not to add a `__trashed` suffix to the name of trashed posts that match the name of the updated post.
*
* @since 5.4.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param bool $add_trashed_suffix Whether to attempt to add the suffix.
* @param string $post_name The name of the post being updated.
* @param int $post_id Post ID.
* @param bool $add_trashed_suffix Whether to attempt to add the suffix.
* @param string $post_name The name of the post being updated.
* @param int $post_id Post ID.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
*/
$add_trashed_suffix = apply_filters( 'add_trashed_suffix_to_trashed_posts', true, $post_name, $post_id );
$add_trashed_suffix = apply_filters( 'add_trashed_suffix_to_trashed_posts', true, $post_name, $post_id, $unsanitized_postarr );

if ( $add_trashed_suffix ) {
wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_id );
Expand Down Expand Up @@ -5216,11 +5219,14 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true )
* Fires immediately before an existing post is updated in the database.
*
* @since 2.5.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param array $data Array of unslashed post data.
* @param int $post_id Post ID.
* @param array $data Array of unslashed post data.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
*/
do_action( 'pre_post_update', $post_id, $data );
do_action( 'pre_post_update', $post_id, $data, $unsanitized_postarr );

if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) {
if ( $wp_error ) {
Expand Down Expand Up @@ -5410,33 +5416,42 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true )
* Fires once an existing attachment has been updated.
*
* @since 2.0.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Attachment ID.
* @param int $post_id Attachment ID.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed attachment post data
* as originally passed to wp_insert_post().
*/
do_action( 'edit_attachment', $post_id );
do_action( 'edit_attachment', $post_id, $unsanitized_postarr );

$post_after = get_post( $post_id );

/**
* Fires once an existing attachment has been updated.
*
* @since 4.4.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param WP_Post $post_after Post object following the update.
* @param WP_Post $post_before Post object before the update.
* @param int $post_id Post ID.
* @param WP_Post $post_after Post object following the update.
* @param WP_Post $post_before Post object before the update.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed attachment post data
* as originally passed to wp_insert_post().
*/
do_action( 'attachment_updated', $post_id, $post_after, $post_before );
do_action( 'attachment_updated', $post_id, $post_after, $post_before, $unsanitized_postarr );
} else {

/**
* Fires once an attachment has been added.
*
* @since 2.0.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Attachment ID.
* @param int $post_id Attachment ID.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed attachment post data
* as originally passed to wp_insert_post().
*/
do_action( 'add_attachment', $post_id );
do_action( 'add_attachment', $post_id, $unsanitized_postarr );
}

return $post_id;
Expand All @@ -5455,34 +5470,45 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true )
* - `edit_post_page`
*
* @since 5.1.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
* Empty array when the hook fires outside of wp_insert_post().
*/
do_action( "edit_post_{$post->post_type}", $post_id, $post );
do_action( "edit_post_{$post->post_type}", $post_id, $post, $unsanitized_postarr );

/**
* Fires once an existing post has been updated.
*
* @since 1.2.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
* Empty array when the hook fires outside of wp_insert_post().
*/
do_action( 'edit_post', $post_id, $post );
do_action( 'edit_post', $post_id, $post, $unsanitized_postarr );

$post_after = get_post( $post_id );

/**
* Fires once an existing post has been updated.
*
* @since 3.0.0
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param WP_Post $post_after Post object following the update.
* @param WP_Post $post_before Post object before the update.
* @param int $post_id Post ID.
* @param WP_Post $post_after Post object following the update.
* @param WP_Post $post_before Post object before the update.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
*/
do_action( 'post_updated', $post_id, $post_after, $post_before );
do_action( 'post_updated', $post_id, $post_after, $post_before, $unsanitized_postarr );
}

/**
Expand All @@ -5497,34 +5523,46 @@ function wp_insert_post( $postarr, $wp_error = false, $fire_after_hooks = true )
* - `save_post_page`
*
* @since 3.7.0
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
* Empty array when the hook fires outside of wp_insert_post().
*/
do_action( "save_post_{$post->post_type}", $post_id, $post, $update );
do_action( "save_post_{$post->post_type}", $post_id, $post, $update, $unsanitized_postarr );

/**
* Fires once a post has been saved.
*
* @since 1.5.0
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
* Empty array when the hook fires outside of wp_insert_post().
*/
do_action( 'save_post', $post_id, $post, $update );
do_action( 'save_post', $post_id, $post, $update, $unsanitized_postarr );

/**
* Fires once a post has been saved.
*
* @since 2.0.0
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
* @since 7.3.0 The `$unsanitized_postarr` parameter was added.
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
* @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as
* originally passed to wp_insert_post().
* Empty array when the hook fires outside of wp_insert_post().
*/
do_action( 'wp_insert_post', $post_id, $post, $update );
do_action( 'wp_insert_post', $post_id, $post, $update, $unsanitized_postarr );

if ( $fire_after_hooks ) {
wp_after_insert_post( $post, $update, $post_before );
Expand Down Expand Up @@ -5682,19 +5720,19 @@ function wp_publish_post( $post ) {
wp_transition_post_status( 'publish', $old_status, $post );

/** This action is documented in wp-includes/post.php */
do_action( "edit_post_{$post->post_type}", $post->ID, $post );
do_action( "edit_post_{$post->post_type}", $post->ID, $post, array() );

/** This action is documented in wp-includes/post.php */
do_action( 'edit_post', $post->ID, $post );
do_action( 'edit_post', $post->ID, $post, array() );

/** This action is documented in wp-includes/post.php */
do_action( "save_post_{$post->post_type}", $post->ID, $post, true );
do_action( "save_post_{$post->post_type}", $post->ID, $post, true, array() );

/** This action is documented in wp-includes/post.php */
do_action( 'save_post', $post->ID, $post, true );
do_action( 'save_post', $post->ID, $post, true, array() );

/** This action is documented in wp-includes/post.php */
do_action( 'wp_insert_post', $post->ID, $post, true );
do_action( 'wp_insert_post', $post->ID, $post, true, array() );

wp_after_insert_post( $post, true, $post_before );
}
Expand Down
20 changes: 20 additions & 0 deletions tests/phpunit/tests/comment/wpUpdateCommentCountNow.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,24 @@ public function test_only_approved_regular_comments_are_counted() {
public function _return_100() {
return 100;
}

/**
* @ticket 60038
*/
public function test_edit_post_receives_empty_unsanitized_postarr() {
$post_id = self::factory()->post->create();
$captured = null;
add_action(
'edit_post',
static function ( $post_id, $post, $unsanitized_postarr = null ) use ( &$captured ) {
$captured = $unsanitized_postarr;
},
10,
3
);

wp_update_comment_count_now( $post_id );

$this->assertSame( array(), $captured );
}
}
29 changes: 29 additions & 0 deletions tests/phpunit/tests/customize/manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -3693,6 +3693,35 @@ public function test_sanitize_external_header_video_trim() {
$this->assertSame( $video_url, $sanitized );
}
}

/**
* @ticket 60038
*/
public function test_trash_changeset_post_passes_empty_unsanitized_postarr() {
$args = array(
'post_type' => 'customize_changeset',
'post_content' => wp_json_encode( array( 'blogname' => array( 'value' => 'Test' ) ) ),
'post_name' => wp_generate_uuid4(),
'post_status' => 'draft',
);

$post_id = wp_insert_post( $args );
$manager = $this->create_test_manager( $args['post_name'] );

$captured = null;
add_action(
'save_post',
static function ( $post_id, $post, $update, $unsanitized_postarr = null ) use ( &$captured ) {
$captured = $unsanitized_postarr;
},
10,
4
);

$manager->trash_changeset_post( $post_id );

$this->assertSame( array(), $captured );
}
}

require_once ABSPATH . WPINC . '/class-wp-customize-setting.php';
Expand Down
39 changes: 39 additions & 0 deletions tests/phpunit/tests/post/attachments.php
Original file line number Diff line number Diff line change
Expand Up @@ -552,4 +552,43 @@ public function test_wp_mime_type_icon_video_with_preferred_ext() {
$this->assertStringContainsString( 'images/media/video.png', $icon1, 'Mime type icon should be correctly returned with ".png" argument.' );
$this->assertStringContainsString( 'images/media/video.png', $icon2, 'Mime type icon should be correctly returned with "png" argument.' );
}

/**
* @ticket 60038
*/
public function test_attachment_hooks_receive_unsanitized_postarr() {
$captured = array();
$callback = static function () use ( &$captured ) {
$args = func_get_args();
$captured[ current_action() ] = end( $args );
};

add_action( 'add_attachment', $callback, 10, 2 );
add_action( 'edit_attachment', $callback, 10, 2 );
add_action( 'attachment_updated', $callback, 10, 4 );

$attachment_id = wp_insert_attachment(
array(
'post_title' => 'Attachment',
'post_mime_type' => 'image/jpeg',
'my_custom_arg' => 'abc',
)
);

$this->assertIsArray( $captured['add_attachment'] );
$this->assertSame( 'abc', $captured['add_attachment']['my_custom_arg'] );

wp_update_post(
array(
'ID' => $attachment_id,
'post_title' => 'Updated attachment',
'my_custom_arg' => 'abc',
)
);

$this->assertIsArray( $captured['edit_attachment'] );
$this->assertSame( 'abc', $captured['edit_attachment']['my_custom_arg'] );
$this->assertIsArray( $captured['attachment_updated'] );
$this->assertSame( 'abc', $captured['attachment_updated']['my_custom_arg'] );
}
}
Loading
Loading