Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
21 changes: 20 additions & 1 deletion src/wp-includes/post.php
Original file line number Diff line number Diff line change
Expand Up @@ -2262,6 +2262,11 @@ function _add_post_type_submenus() {
* A third, optional parameter can also be passed along with a feature to provide
* additional information about supporting that feature.
*
* When calling this function multiple times for the same post type and feature
* with associative array arguments, the arguments will be merged rather than
* overwritten. This allows multiple calls to add different sub-properties to
* the same feature.
Comment on lines +2265 to +2268
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* When calling this function multiple times for the same post type and feature
* with associative array arguments, the arguments will be merged rather than
* overwritten. This allows multiple calls to add different sub-properties to
* the same feature.
* When calling this function multiple times for the same post type and feature
* with array arguments, the arguments will be merged rather than overwritten.
* This allows multiple calls to add different sub-properties to the same
* feature.

*
* Example usage:
*
* add_post_type_support( 'my_post_type', 'comments' );
Expand All @@ -2275,6 +2280,8 @@ function _add_post_type_submenus() {
* @since 3.0.0
* @since 5.3.0 Formalized the existing and already documented `...$args` parameter
* by adding it to the function signature.
* @since 6.9.0 Multiple calls to add support for the same feature with associative
* array arguments now merge the arguments instead of overwriting them.
Comment on lines +2283 to +2284
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* @since 6.9.0 Multiple calls to add support for the same feature with associative
* array arguments now merge the arguments instead of overwriting them.
* @since 6.9.0 Multiple calls to add support for the same feature with array
* arguments now merge the arguments instead of overwriting them.

*
* @global array $_wp_post_type_features
*
Expand All @@ -2289,7 +2296,19 @@ function add_post_type_support( $post_type, $feature, ...$args ) {
$features = (array) $feature;
foreach ( $features as $feature ) {
if ( $args ) {
$_wp_post_type_features[ $post_type ][ $feature ] = $args;
// Check if feature already exists with args and if both are associative arrays that should be merged.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Check if feature already exists with args and if both are associative arrays that should be merged.
// Check if feature already exists with args and if both are arrays that should be merged.

if ( isset( $_wp_post_type_features[ $post_type ][ $feature ][0] )
&& is_array( $_wp_post_type_features[ $post_type ][ $feature ][0] )
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per below, should this check to make sure it is actually an associative array? I think it should add this check: ! array_is_list( $_wp_post_type_features[ $post_type ][ $feature ][0] )

&& isset( $args[0] )
&& is_array( $args[0] ) ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The is_array( $args[0] ) call here isn't checking whether the arg is an associative array. Shouldn't it also check if ! isset( $args[0][0] )?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or rather: ! array_is_list( $args[0] )

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additionally, what if someone tries to merge an empty array? Should that be allowed? So perhaps the new check here should be: ! array_is_list( $args[0] ) || empty( $args[0] )

// Merge the associative arrays to preserve existing properties.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Merge the associative arrays to preserve existing properties.
// Merge the arrays to preserve existing properties.

$_wp_post_type_features[ $post_type ][ $feature ][0] = array_merge(
$_wp_post_type_features[ $post_type ][ $feature ][0],
$args[0]
);
} else {
$_wp_post_type_features[ $post_type ][ $feature ] = $args;
}
} else {
$_wp_post_type_features[ $post_type ][ $feature ] = true;
}
Expand Down
225 changes: 225 additions & 0 deletions tests/phpunit/tests/post/types.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,231 @@ public function test_post_type_supports() {
$this->assertFalse( post_type_supports( 'notaposttype', 'notafeature' ) );
}

/**
* Tests that add_post_type_support() merges array arguments on subsequent calls.
*
* @ticket 64156
*/
public function test_add_post_type_support_merges_array_arguments() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Also add tests for theme features merging non-associative arrays.

register_post_type( 'foo' );

// First call with array arguments.
add_post_type_support(
'foo',
'editor',
array(
'default-mode' => 'template-locked',
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertIsArray( $support['editor'] );
$this->assertArrayHasKey( 0, $support['editor'] );
$this->assertIsArray( $support['editor'][0] );
$this->assertSame( 'template-locked', $support['editor'][0]['default-mode'] );

// Second call with different array arguments should merge.
add_post_type_support(
'foo',
'editor',
array(
'block-comments' => true,
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertIsArray( $support['editor'] );
$this->assertArrayHasKey( 0, $support['editor'] );
$this->assertIsArray( $support['editor'][0] );
$this->assertSame( 'template-locked', $support['editor'][0]['default-mode'] );
$this->assertTrue( $support['editor'][0]['block-comments'] );

// Third call with yet another property should merge with both previous.
add_post_type_support(
'foo',
'editor',
array(
'another-option' => 'test-value',
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertSame( 'template-locked', $support['editor'][0]['default-mode'] );
$this->assertTrue( $support['editor'][0]['block-comments'] );
$this->assertSame( 'test-value', $support['editor'][0]['another-option'] );

_unregister_post_type( 'foo' );
}

/**
* Tests that add_post_type_support() overwrites values when called with the same key.
*
* @ticket 64156
*/
public function test_add_post_type_support_overwrites_same_key() {
register_post_type( 'foo' );

add_post_type_support(
'foo',
'editor',
array(
'default-mode' => 'template-locked',
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertSame( 'template-locked', $support['editor'][0]['default-mode'] );

// Calling with same key but different value should overwrite.
add_post_type_support(
'foo',
'editor',
array(
'default-mode' => 'unlocked',
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertSame( 'unlocked', $support['editor'][0]['default-mode'] );

_unregister_post_type( 'foo' );
}

/**
* Tests backwards compatibility: calling add_post_type_support() without args still works.
*
* @ticket 64156
*/
public function test_add_post_type_support_without_args() {
register_post_type( 'foo' );

add_post_type_support( 'foo', 'custom-fields' );

$this->assertTrue( post_type_supports( 'foo', 'custom-fields' ) );
$support = get_all_post_type_supports( 'foo' );
$this->assertTrue( $support['custom-fields'] );

_unregister_post_type( 'foo' );
}

/**
* Tests backwards compatibility: calling add_post_type_support() with args after
* setting it to true should overwrite with args.
*
* @ticket 64156
*/
public function test_add_post_type_support_with_args_after_true() {
register_post_type( 'foo' );

// First call without args sets to true.
add_post_type_support( 'foo', 'editor' );

$support = get_all_post_type_supports( 'foo' );
$this->assertTrue( $support['editor'] );

// Second call with args should overwrite true with args.
add_post_type_support(
'foo',
'editor',
array(
'default-mode' => 'template-locked',
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertIsArray( $support['editor'] );
$this->assertSame( 'template-locked', $support['editor'][0]['default-mode'] );

_unregister_post_type( 'foo' );
}

/**
* Tests backwards compatibility: calling add_post_type_support() without args after
* setting it with args should overwrite with true.
*
* @ticket 64156
*/
public function test_add_post_type_support_without_args_after_array() {
register_post_type( 'foo' );

// First call with args.
add_post_type_support(
'foo',
'editor',
array(
'default-mode' => 'template-locked',
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertIsArray( $support['editor'] );
$this->assertSame( 'template-locked', $support['editor'][0]['default-mode'] );

// Second call without args should overwrite args with true.
add_post_type_support( 'foo', 'editor' );

$support = get_all_post_type_supports( 'foo' );
$this->assertTrue( $support['editor'] );

_unregister_post_type( 'foo' );
}

/**
* Tests that add_post_type_support() can add multiple features at once.
*
* @ticket 64156
*/
public function test_add_post_type_support_multiple_features() {
register_post_type( 'foo' );

add_post_type_support( 'foo', array( 'title', 'editor', 'thumbnail' ) );

$this->assertTrue( post_type_supports( 'foo', 'title' ) );
$this->assertTrue( post_type_supports( 'foo', 'editor' ) );
$this->assertTrue( post_type_supports( 'foo', 'thumbnail' ) );

_unregister_post_type( 'foo' );
}

/**
* Tests that add_post_type_support() works with nested array arguments.
*
* @ticket 64156
*/
public function test_add_post_type_support_with_nested_arrays() {
register_post_type( 'foo' );

add_post_type_support(
'foo',
'editor',
array(
'allowed_blocks' => array( 'core/paragraph', 'core/heading' ),
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertIsArray( $support['editor'][0]['allowed_blocks'] );
$this->assertContains( 'core/paragraph', $support['editor'][0]['allowed_blocks'] );
$this->assertContains( 'core/heading', $support['editor'][0]['allowed_blocks'] );

// Adding another nested array should merge.
add_post_type_support(
'foo',
'editor',
array(
'disallowed_blocks' => array( 'core/code' ),
)
);

$support = get_all_post_type_supports( 'foo' );
$this->assertIsArray( $support['editor'][0]['allowed_blocks'] );
$this->assertIsArray( $support['editor'][0]['disallowed_blocks'] );
$this->assertContains( 'core/paragraph', $support['editor'][0]['allowed_blocks'] );
$this->assertContains( 'core/code', $support['editor'][0]['disallowed_blocks'] );

_unregister_post_type( 'foo' );
}

/**
* @ticket 21586
* @ticket 41172
Expand Down
Loading