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
8 changes: 8 additions & 0 deletions src/wp-includes/formatting.php
Original file line number Diff line number Diff line change
Expand Up @@ -5256,6 +5256,7 @@ function sanitize_option( $option, $value ) {
* This is similar to `array_walk_recursive()` but acts upon objects too.
*
* @since 4.4.0
* @since 7.2.0 Added support for ArrayObject entries.
*
* @param mixed $value The array, object, or scalar.
* @param callable $callback The function to map onto $value.
Expand All @@ -5271,6 +5272,13 @@ function map_deep( $value, $callback ) {
$value[ $index ] = map_deep( $item, $callback );
}
} elseif ( is_object( $value ) ) {
// get_object_vars() does not include ArrayObject entries on PHP 7.4 and later.
if ( $value instanceof ArrayObject && ! ( $value->getFlags() & ArrayObject::STD_PROP_LIST ) ) {
foreach ( $value->getArrayCopy() as $index => $item ) {
$value[ $index ] = map_deep( $item, $callback );
}
}

$object_vars = get_object_vars( $value );
foreach ( $object_vars as $property_name => $property_value ) {
$value->$property_name = map_deep( $property_value, $callback );
Expand Down
114 changes: 114 additions & 0 deletions tests/phpunit/tests/formatting/mapDeep.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,120 @@ public function test_map_deep_should_map_array_elements_passed_by_reference() {
);
}

/**
* @ticket 53160
* @dataProvider data_array_object_flags
*
* @param int $flags ArrayObject flags.
* @param string $expected_value Expected value of the stored entry.
*/
public function test_map_deep_should_map_array_object_entries_and_public_properties( $flags, $expected_value ) {
$value = new class( array( 'entry' => 'x' ), $flags ) extends ArrayObject {
public $property = 'a';
};

$result = map_deep( $value, array( $this, 'append_baba' ) );

$this->assertSame( $value, $result, 'The original object should be returned.' );
$this->assertSame( $flags, $result->getFlags(), 'The flags should not change.' );
$this->assertSame( 'ababa', $result->property, 'Public properties should still be mapped.' );
$this->assertSame( array( 'entry' => $expected_value ), $result->getArrayCopy() );
}

/**
* Data provider for ArrayObject flag combinations.
*
* @return array[]
*/
public static function data_array_object_flags() {
return array(
'default' => array( 0, 'xbaba' ),
'array entries as properties' => array( ArrayObject::ARRAY_AS_PROPS, 'xbaba' ),
'public properties only' => array( ArrayObject::STD_PROP_LIST, 'x' ),
'both flags' => array( ArrayObject::STD_PROP_LIST | ArrayObject::ARRAY_AS_PROPS, 'x' ),
);
}

/**
* @ticket 53160
*/
public function test_map_deep_should_map_nested_array_objects() {
$inner = new ArrayObject( array( 'x' ) );
$outer = new ArrayObject(
array(
'array' => array( 'a' ),
'object' => (object) array( 'inner' => $inner ),
)
);

$result = map_deep( array( 'outer' => $outer ), array( $this, 'append_baba' ) );

$this->assertSame( $outer, $result['outer'] );
$this->assertSame( array( 'ababa' ), $outer['array'] );
$this->assertSame( $inner, $outer['object']->inner );
$this->assertSame( array( 'xbaba' ), $inner->getArrayCopy() );
}

/**
* Object-backed storage is deprecated as of PHP 8.5.
*
* @ticket 53160
* @requires PHP < 8.5

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this works?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes, I retested this with PHPUnit 9.6.38 on PHP 8.3.36 and 8.5.12, on both single site and multisite. All 22 tests pass on 8.3. On 8.5, only this fixture skips, with PHP < 8.5 is required.

Removing the annotation makes the fixture fail on PHP 8.5 because new ArrayObject( $storage ) triggers the object-backed storage deprecation.

*/
public function test_map_deep_should_map_array_object_backed_by_an_object() {
$storage = (object) array( 'entry' => 'x' );
$value = new ArrayObject( $storage );

$this->assertSame( $value, map_deep( $value, array( $this, 'append_baba' ) ) );
$this->assertSame( 'xbaba', $storage->entry );
}

/**
* @ticket 53160
*/
public function test_map_deep_should_not_call_callback_for_empty_array_object() {
$value = new ArrayObject();

$result = map_deep(
$value,
function () {
$this->fail( 'The callback should not be called for an empty ArrayObject.' );
}
);

$this->assertSame( $value, $result );
$this->assertSame( array(), $result->getArrayCopy() );
}

/**
* @ticket 53160
* @dataProvider data_array_object_callers
*
* @param callable $callback Function using map_deep().
* @param string $input Input value.
* @param string $expected Expected mapped value.
*/
public function test_map_deep_callers_should_map_array_object_entries( $callback, $input, $expected ) {
$value = new ArrayObject( array( 'entry' => $input ) );

$this->assertSame( $value, $callback( $value ) );
$this->assertSame( array( 'entry' => $expected ), $value->getArrayCopy() );
}

/**
* Data provider for functions using map_deep().
*
* @return array[]
*/
public static function data_array_object_callers() {
return array(
'strip slashes' => array( 'stripslashes_deep', "a\\'b", "a'b" ),
'encode URL' => array( 'urlencode_deep', 'a b', 'a+b' ),
'encode raw URL' => array( 'rawurlencode_deep', 'a b', 'a%20b' ),
'decode URL' => array( 'urldecode_deep', 'a+b', 'a b' ),
);
}

public function append_baba( $value ) {
return $value . 'baba';
}
Expand Down
Loading