Introduce map_deep(), a utility function that recursively maps a callable function to every item in an array or object. Works like array_walk_recursive() but works with objects too.

Updates `rawurlencode_deep()`, `urlencode_deep()`, and `stripslashes_deep()` to use `map_deep()`. Introduces `urldecode_deep()` for completeness.

Props wpmuguru, nbachiyski, boonebgorges, MikeHansenMe, chriscct7, realloc, johnbillion
Fixes #22300


git-svn-id: https://develop.svn.wordpress.org/trunk@35252 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
John Blackbourn 2015-10-17 23:25:21 +00:00
parent f8e79c5bbc
commit d5b31e6ba2
3 changed files with 202 additions and 24 deletions

View File

@ -2009,10 +2009,7 @@ function addslashes_gpc($gpc) {
}
/**
* Navigates through an array and removes slashes from the values.
*
* If an array is passed, the array_map() function causes a callback to pass the
* value back to the function. The slashes from this value will removed.
* Navigates through an array, object, or scalar, and removes slashes from the values.
*
* @since 2.0.0
*
@ -2020,43 +2017,55 @@ function addslashes_gpc($gpc) {
* @return mixed Stripped value.
*/
function stripslashes_deep( $value ) {
if ( is_array($value) ) {
$value = array_map('stripslashes_deep', $value);
} elseif ( is_object($value) ) {
$vars = get_object_vars( $value );
foreach ($vars as $key=>$data) {
$value->{$key} = stripslashes_deep( $data );
}
} elseif ( is_string( $value ) ) {
$value = stripslashes($value);
}
return $value;
return map_deep( $value, 'stripslashes_from_strings_only' );
}
/**
* Navigates through an array and encodes the values to be used in a URL.
* Callback function for `stripslashes_deep()` which strips slashes from strings.
*
* @since 4.4.0
*
* @param mixed $value The array or string to be stripped.
* @return mixed $value The stripped value.
*/
function stripslashes_from_strings_only( $value ) {
return is_string( $value ) ? stripslashes( $value ) : $value;
}
/**
* Navigates through an array, object, or scalar, and encodes the values to be used in a URL.
*
* @since 2.2.0
*
* @param array|string $value The array or string to be encoded.
* @return array|string $value The encoded array (or string from the callback).
* @param mixed $value The array or string to be encoded.
* @return mixed $value The encoded value.
*/
function urlencode_deep( $value ) {
return is_array( $value ) ? array_map( 'urlencode_deep', $value ) : urlencode( $value );
return map_deep( $value, 'urlencode' );
}
/**
* Navigates through an array and raw encodes the values to be used in a URL.
* Navigates through an array, object, or scalar, and raw-encodes the values to be used in a URL.
*
* @since 3.4.0
*
* @param array|string $value The array or string to be encoded.
* @return array|string $value The encoded array (or string from the callback).
* @param mixed $value The array or string to be encoded.
* @return mixed $value The encoded value.
*/
function rawurlencode_deep( $value ) {
return is_array( $value ) ? array_map( 'rawurlencode_deep', $value ) : rawurlencode( $value );
return map_deep( $value, 'rawurlencode' );
}
/**
* Navigates through an array, object, or scalar, and decodes URL-encoded values
*
* @since 4.4.0
*
* @param mixed $value The array or string to be decoded.
* @return mixed $value The decoded value.
*/
function urldecode_deep( $value ) {
return map_deep( $value, 'urldecode' );
}
/**
@ -3862,6 +3871,28 @@ function sanitize_option( $option, $value ) {
return apply_filters( "sanitize_option_{$option}", $value, $option, $original_value );
}
/**
* Maps a function to all non-iterable elements of an array or an object.
*
* This is similar to `array_walk_recursive()` but acts upon objects too.
*
* @since 4.4.0
*
* @param mixed $value The array, object, or scalar.
* @param callable $function The function to map onto $value.
* @return The value with the callback applied to all non-arrays and non-objects inside it.
*/
function map_deep( $value, $callback ) {
if ( is_array( $value ) || is_object( $value ) ) {
foreach ( $value as &$item ) {
$item = map_deep( $item, $callback );
}
return $value;
} else {
return call_user_func( $callback, $value );
}
}
/**
* Parses a string into variables to be stored in an array.
*

View File

@ -0,0 +1,101 @@
<?php
/**
* @group formatting
* @ticket 22300
*/
class Tests_Formatting_MapDeep extends WP_UnitTestCase {
public function test_map_deep_with_any_function_over_empty_array_should_return_empty_array() {
$this->assertEquals( array(), map_deep( array(), array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_map_each_element_of_array_one_level_deep() {
$this->assertEquals( array(
'ababa',
'xbaba',
), map_deep( array(
'a',
'x',
), array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_map_each_element_of_array_two_levels_deep() {
$this->assertEquals( array(
'ababa',
array(
'xbaba',
),
), map_deep( array(
'a',
array(
'x',
),
), array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_map_each_object_element_of_an_array() {
$this->assertEquals( array(
'var0' => 'ababa',
'var1' => (object) array(
'xbaba',
),
), map_deep( array(
'var0' => 'a',
'var1' => (object) array(
'x',
),
), array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_apply_the_function_to_a_string() {
$this->assertEquals( 'xbaba', map_deep( 'x', array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_apply_the_function_to_an_integer() {
$this->assertEquals( '5baba' , map_deep( 5, array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_map_each_property_of_an_object() {
$this->assertEquals( (object) array(
'var0' => 'ababa',
'var1' => 'xbaba',
), map_deep( (object) array(
'var0' => 'a',
'var1' => 'x',
), array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_map_each_array_property_of_an_object() {
$this->assertEquals( (object) array(
'var0' => 'ababa',
'var1' => array(
'xbaba',
),
), map_deep( (object) array(
'var0' => 'a',
'var1' => array(
'x',
),
), array( $this, 'append_baba' ) ) );
}
public function test_map_deep_should_map_each_object_property_of_an_object() {
$this->assertEquals( (object) array(
'var0' => 'ababa',
'var1' => (object) array(
'xbaba',
),
), map_deep( (object) array(
'var0' => 'a',
'var1' => (object) array(
'x',
),
), array( $this, 'append_baba' ) ) );
}
public function append_baba( $value ) {
return $value . 'baba';
}
}

View File

@ -0,0 +1,46 @@
<?php
/**
* @group formatting
* @ticket 22300
*/
class Tests_Formatting_UrlencodeDeep extends WP_UnitTestCase {
/**
* Data Provider
*/
public function data_test_values() {
return array(
array( 'qwerty123456', 'qwerty123456' ),
array( '|!"£$%&/()=?', '%7C%21%22%C2%A3%24%25%26%2F%28%29%3D%3F' ),
array( '^é*ç°§;:_-.,', '%5E%C3%A9%2A%C3%A7%C2%B0%C2%A7%3B%3A_-.%2C' ),
array( 'abc123 @#[]€', 'abc123+%40%23%5B%5D%E2%82%AC' ),
array( 'abc123 @#[]€', urlencode( 'abc123 @#[]€' ) ),
);
}
/**
* Validate the urlencode_deep function pair by pair
*
* @dataProvider data_test_values
*
* @param string $actual
* @param string $expected
*/
public function test_urlencode_deep_should_encode_individual_value( $actual, $expected ) {
$this->assertEquals( $expected, urlencode_deep( $actual ) );
}
/**
* Test the whole array as input
*/
public function test_urlencode_deep_should_encode_all_values_in_array() {
$data = $this->data_test_values();
$actual = wp_list_pluck( $data, 0 );
$expected = wp_list_pluck( $data, 1 );
$this->assertEquals( $expected, urlencode_deep( $actual ) );
}
}