数组 函数
在线手册:中文  英文

array_walk_recursive

(PHP 5)

array_walk_recursive对数组中的每个成员递归地应用用户函数

说明

bool array_walk_recursive ( array &$input , callable $funcname [, mixed $userdata = NULL ] )

将用户自定义函数 funcname 应用到 array 数组中的每个单元。本函数会递归到更深层的数组中去。

参数

input

输入的数组。

funcname

典型情况下 funcname 接受两个参数。input 参数的值作为第一个,键名作为第二个。

Note:

如果 funcname 需要直接作用于数组中的值,则给 funcname 的第一个参数指定为引用。这样任何对这些单元的改变也将会改变原始数组本身。

userdata

如果提供了可选参数 userdata,将被作为第三个参数传递给 callback funcname

返回值

成功时返回 TRUE, 或者在失败时返回 FALSE

范例

Example #1 array_walk_recursive() example

<?php
$sweet 
= array('a' => 'apple''b' => 'banana');
$fruits = array('sweet' => $sweet'sour' => 'lemon');

function 
test_print($item$key)
{
    echo 
"$key holds $item\n";
}

array_walk_recursive($fruits'test_print');
?>

以上例程会输出:

a holds apple
b holds banana
sour holds lemon

注意上例中的键 'sweet' 并没有显示出来。任何其值为 array 的键都不会被传递到回调函数中去。

参见


数组 函数
在线手册:中文  英文

用户评论:

none at of dot your dot biz (2013-06-03 10:50:25)

Since this is only mentioned in the footnote of the output of one of the examples, I feel it should be spelled out:
* THIS FUNCTION ONLY VISITS LEAF NODES *
That is to say that if you have a tree of arrays with subarrays of subarrays, only the plain values at the leaves of the tree will be visited by the callback function. The callback function isn't ever called for a nodes in the tree that subnodes (i.e., a subarray). This has the effect as to make this function unusable for most practical situations.

chris at willowsconsulting dot ie (2012-10-23 10:11:42)

To convert all values of an array in UTF8, do this:

<?php

function convert_before_json(&$item, &$key)
{
   
$item=utf8_encode($item);
}

array_walk_recursive($your_array,"convert_before_json");

?>

qfox at ya dot ru (2012-03-25 02:11:58)

array_flatten example powered by array_walk_recursive
function array_flatten($nested, $preserve_keys = false) {
$flat = array();
$collector = $preserve_keys ? function ($v, $k) use (&$flat) {
$flat[$k] = $v;
} : function ($v) use (&$flat) {
$flat[] = $v;
};
array_walk_recursive($paths, $nested);
return $flat;
}

cyranix at cyranix dot com (2011-10-28 19:45:29)

I needed to add or modify values in an array with unknown structure. I was hoping to use array_walk_recursive for the task, but because I was also adding new nodes I came up with an alternate solution.

<?php

    
/**
     * Sets key/value pairs at any depth on an array.
     * @param $data an array of key/value pairs to be added/modified
     * @param $array the array to operate on
     */
    
function setNodes($data, &$array)
    {
        
$separator '.'// set this to any string that won't occur in your keys
        
foreach ($data as $name => $value) {
            if (
strpos($name$separator) === false) {
                
// If the array doesn't contain a special separator character, just set the key/value pair. 
                // If $value is an array, you will of course set nested key/value pairs just fine.
                
$array[$name] = $value;
            } else {
                
// In this case we're trying to target a specific nested node without overwriting any other siblings/ancestors. 
                // The node or its ancestors may not exist yet.
                
$keys explode($separator$name);
                
// Set the root of the tree.
                
$opt_tree =& $array;
                
// Start traversing the tree using the specified keys.
                
while ($key array_shift($keys)) {
                    
// If there are more keys after the current one...
                    
if ($keys) {
                        if (!isset(
$opt_tree[$key]) || !is_array($opt_tree[$key])) {
                            
// Create this node if it doesn't already exist.
                            
$opt_tree[$key] = array();
                        }
                        
// Redefine the "root" of the tree to this node (assign by reference) then process the next key.
                        
$opt_tree =& $opt_tree[$key];
                    } else {
                        
// This is the last key to check, so assign the value.
                        
$opt_tree[$key] = $value;
                    }
                }
            }
        }
    }

?>

Sample usage: 

<?php

$x 
= array();
setNodes(array('foo' => 'bar''baz' => array('quux' => 42'hup' => 101)), $x);
print_r($x); // $x has the same structure as the first argument
setNodes(array('jif.snee' => 'hello world''baz.quux.wek' => 5), $x);
print_r($x); // added $x['jif']['snee'] and modified $x['baz']['quux'] to be array('wek' => 5)

?>

ghoffman at salientdigital dot com (2011-10-13 22:29:15)

If you are wanting to change the values of an existing multi-dimensional array, as it says above in the note, you need to specify the first argument as a reference. All that means is, be sure to precede the $item variable with an ampersand (&) as in the good_example below. 

Unfortunately the PHP example given doesn't do this. It actually took me a while to figure out why my function wasn't changing the original array, even though I was passing by reference. 

Here's the tip: Don't return any value from the function! Just change the value of $item that you passed in by reference. This is rather counter-intuitive since the vast majority of functions return a value.

<?php
// array_walk_recursive fails to change your array unless you pass by reference.
// Don't return values from your filter function, even though it's quite logical at a glance!
function bad_example($item,$key){
   if(
$key=='test'){
       return 
'PHP Rocks';  // Don't do it
   
}else{
      return 
$item;  // Don't do this either
   
}
}

// array_walk_recursive pass-by-reference example
function good_example(&$item,$key){
   if(
$key=='test'){
        
$item='PHP Rocks'// Do This!
   
}
}

$arr = array('a'=>'1','b'=>'2','test'=>'Replace This');

array_walk_recursive($arr,'bad_example');
var_dump($arr);
/**
 * no errors, but prints...
 * array('a'=>'1','b'=>'2','test'=>'Replace This');
 */

array_walk_recursive($arr,'good_example');
var_dump($arr);
/**
 * prints...
 * array('a'=>'1','b'=>'2','test'=>'PHP Rocks');
 */

?>

Returning a value from your function does work if you pass by reference and modify $item before you return, but you will eat up memory very, very fast if you try it, even on an example as small as the one here.

One other silly thing you might try first is something like this:

<?php
// Resist the urge to do this, it doesn't work.
$filtered array_walk_recursive($unfiltered,'filter_function');
?>

Of course, $filtered is just TRUE afterwards, not the filtered results you were wanting. Oh, it ran your function recursively alright, but changed all the values in the local function scope only and returns a boolean as the documentation states.

rob at yurkowski dot net (2010-10-26 11:16:04)

If you don't really particularly care about the keys of an array, you can capture all values quite simply:

<?php

$sample 
= array('dog' => 'woof''cat' => array('angry' => 'hiss''happy' => 'purr'), 'aardvark' => 'kssksskss');
$output = array();

// Push all $val onto $output.
array_walk_recursive($samplecreate_function('$val, $key, $obj''array_push($obj, $val);'), &output);

// Printing
echo nl2br(print_r($outputtrue));

/* 
* Array
* (
*  [0] => woof
*  [1] => hiss
*  [2] => purr
*  [3] => kssksskss
* )
*/
?>

[EDIT BY danbrown AT php DOT net: In a note added by 'FaustoFilho' on 17-MAY-2011, the following information was appended to this note.

[If you intend to use this] "function to extract the last key value of an array, don't forget to insert a currency sign ($) signal before '$output' at 7th line.

This value must be a variable, and if you forgot to assign this signal, your code won't work, displaying an error like this:

Parse error: syntax error, unexpected ')', expecting T_PAAMAYIM_NEKUDOTAYIM in /path/to/script.php on line 7."]

bradbeattie at gmail dot com (2010-08-27 11:18:13)

The description says "If funcname needs to be working with the actual values of the array, specify the first parameter of funcname as a reference." This isn't necessarily helpful as the function you're calling might be built in (e.g. trim or strip_tags). One option would be to create a version of these like so.

<?php
    
function trim_by_reference(&$string) {
        
$string trim($string);
    }
?>

The downside to this approach is that you need to create a wrapper function for each function you might want to call. Instead, we can use PHP 5.3's inline function syntax to create a new version of array_walk_recursive.

<?php
    
/**
     * This function acts exactly like array_walk_recursive, except that it pretends that the function
     * its calling replaces the value with its result.
     * 
     * @param $array The first value of the array will be passed into $function as the primary argument
     * @param $function The function to be called on each element in the array, recursively
     * @param $parameters An optional array of the additional parameters to be appeneded to the function
     * 
     * Example usage to alter $array to get the second, third and fourth character from each value
     *     array_walk_recursive_referential($array, "substr", array("1","3"));
     */
    
function array_walk_recursive_referential(&$array$function$parameters = array()) {
        
$reference_function = function(&$value$key$userdata) {
            
$parameters array_merge(array($value), $userdata[1]);
            
$value call_user_func_array($userdata[0], $parameters);
        };
        
array_walk_recursive($array$reference_function, array($function$parameters));
    }
?>

The advantage here is that we only explicitly define one wrapper function instead of potentially dozens.

devel at starosta dot org (2008-10-12 17:41:05)

Nice system to make DB tree using function array_walk_recursive:

<?php
function _CategoriesTree(&$item$key)
{
global 
$objDB;

  if (
$key=="childs")
  {
    
$res $objDB->getAssoc('SELECT category_id, name,category_parent_id, category_id AS childs  FROM categories WHERE category_parent_id = '.$item.' ORDER BY orderid');
    if(!empty(
$res))
    {
      
// walk inside subcategories
      
$item $res;
    }
    else
    {
      
// equvalent => no childs
      
$item = -1;
    }
  }
}

$categories $objDB->getAssoc('SELECT category_id, name,category_parent_id, category_id AS childs  FROM categories WHERE category_parent_id = 0 ORDER BY orderid');  // fetchPairs -> value is the index
array_walk_recursive($categories'treeFunc',$objDB);

// test
print_r($categories);
?>

martin dot velek at gmail dot com (2008-09-28 14:01:18)

If you will know that a tree in DB is not huge you can use array_walk_recursive to create tree structure e.g. in array.

let's assume following table:
NAME        PARENT
a                  null
b                  null
c                  null
a1               a
a2               a
b1               b
b11             b1
b111           b11

<?php
require_once 'Zend/Db/Adapter/Pdo/Sqlite.php';

function 
treeFunc(&$item$key$dbConn
{
    
$res $dbConn->fetchPairs('SELECT name FROM tree where parent = ?',$key);
    if(!empty(
$res))
         
$item $res;
}

$arr $dbConn->fetchPairs('SELECT name FROM tree where parent is null');  // fetchPairs -> value is the index

$valid true;
while(
$valid)
{
       
$oldCount  count($arr,COUNT_RECURSIVE);
       
array_walk_recursive($arr'treeFunc',$dbConn);
       
$newCount count($arr,COUNT_RECURSIVE);
       if (
$oldCount == $newCount)  // if there is no change, exit...
                   
$valid false;     
}
print_r($arr);
?>

--------------------------------------------------
Array
(
    [a] => Array
        (
            [a1] => 
            [a2] => 
        )

    [b] => Array
        (
            [b1] => Array
                (
                    [b11] => Array
                        (
                            [b111] => 
                        )

                )

        )

    [c] => 
)

amoffat at amoffat dot com (2008-06-04 17:15:40)

<?
function my_array_map() {
    $args = func_get_args();
    $arr = array_shift($args);
    
    foreach ($args as $fn) {
        $nfn = create_function('&$v, $k, $fn', '$v = $fn($v);');
        array_walk_recursive($arr, $nfn, $fn);
    }
    return $arr;
}
?>

takes an array as the first argument, and functions as the other arguments.  it applies those functions recursively to the array

JW (2008-03-14 17:59:09)

This function has a serious bug, which is still not fixed as of the PHP 5.2.5 release. After you call it, it can accidentally modify your original array. Save yourself hours of frustration by reading on.

The bug is here: http://bugs.php.net/bug.php?id=42850, and it looks like it will be fixed for 5.3.

If the array that you walk contains other array elements, they will be turned into references. This will happen even if the callback function doesn't take its first argument by reference, and doesn't do anything to the values.

For example, try this:
<?php
$data 
= array ('key1' => 'val1''key2' => array('key3' => 'val3'));
function 
foo($item$key){}
var_dump($data);
?>

The original array has no references. Now try this:
<?php
array_walk_recursive
($data,'foo');
var_dump($data);
?>

Now key2 is a reference, not just an array. So if you do this:
<?php
function test($item){$item['key2'] = array();}
test($data);
var_dump($data);
?>

you will see that test modifies $data, even though it shouldn't.

One workaround is to immediately make a deep copy of the array after calling array_walk_recursive, like this:
<?php
function array_duplicate($input) {
  if (!
is_array($input)) return $input;
  
$output = array();
  foreach (
$input as $key => $value) {
    
$output[$key] = array_duplicate($value);
  }
  return 
$output;
}
array_walk_recursive($data,'foo');
$data array_duplicate($data);
var_dump($data);
?>

After doing that, the references are gone.

wileur at gmail dot com (2007-03-06 05:59:48)

A simple way to use callback functions that are defined in a class is to simply pass the class instance and function names as values in an array:
<?php
class testClass {
    function 
callbackFunction($value$key) {
        echo 
"$key$value<br />\n";
    }
    function 
printArray($foo) {
        
array_walk_recursive($foo, array($this'callbackFunction'));
    }
}
?>

adam dott pub att adamswick dott com (2006-11-09 18:08:07)

Thanks to all of you in the prior posts.
I don't like the fact that the current version of array_walk_recursive() doesn't track all array keys. This version will do that, as well as tracking array depth on multi-dimensional arrays.
- - - - - - -
Class Array_walk_recursive3 {
private $depth = -1;
private $userdata, $funcname;
public $status;
public $input;

public function __construct($input, $funcname, $userdata = "") {
$this->input = $input;
$this->funcname = $funcname;
$this->userdata = $userdata;
$this->status = $this->array_walk_recursive($this->input);
}

private function test_print(&$value, &$key)
{
echo str_repeat(" ", $this->depth)."$key holds ";
if (!is_array($value)) {
echo $value;
if (trim($value) == "banana") {
$value = "cherry";
$key = "c";
}
}
echo "\n";
}

private function array_walk_recursive(&$input) {
$funcname = array(&$this, $this->funcname);
if (!is_callable($funcname)) {
return false;
}

if (!is_array($input)) {
return false;
}
$this->depth++;

foreach (array_keys($input) AS $keyIdx => $key) {
$saved_value = $input[$key];
$saved_key = $key;
call_user_func_array($funcname, array(&$input[$saved_key], &$key));

if ($input[$saved_key] !== $saved_value || $saved_key !== $key) {
$saved_value = $input[$saved_key];
unset($input[$saved_key]);
$input[$key] = $saved_value;
}
if (is_array($input[$key])) {
if (!$this->array_walk_recursive($input[$key], $funcname)) return false;
$this->depth--;
}
}
return true;
}
}
$sweet = array('a' => 'apple', 'b' => 'banana');
$fruits = array('sweet' => $sweet, 'sour' => 'lemon');
$fruitObj = new Array_walk_recursive3($fruits, 'test_print');
echo $fruitObj->status;
$newFruitArr = $fruitObj->input;
print_r($fruits);
print_r($newFruitArr);

ik at paulkaspers dot nl (2006-10-15 13:46:46)

To egingell at sisna dot com:
There is a small bug in your function, the following line should be changed:
From: if ($value != $saved_value || $saved_key != $key) {
Into: if ($value !== $saved_value || $saved_key !== $key) {
It's a nice function, because I was searching for something to change the keys of a multiple dimension array.

gieterke at gmail dot com (2006-03-29 04:59:59)

I think there are a lot of people who want/have to work object oriented.
this is an Object oriented version

the code is written in PHP5
<?php
    
//auto overload classes, so you don't have to put in all the includes
    
function __autoload($class_name) {
        require_once 
$class_name '.php';
    }

    echo 
"TESTING: arraywalker<br>";
    
$anObject = new ClassName();

    
//array_walk_recursive($anObject->getFruits(), 'test_print'); => doesn't work
    
array_walk_recursive($anObject->getFruits(), array(&$anObject'test_print'));

    echo
"<br><br>";
        
//if you just want to give the array and not hte function name that handles the array
    
$anObject->arrWalker($anObject->getFruits());
?>

<?php
//Class description---------------------------------------------
class ClassName {
    
//Class variables
    
private $sweet = array();
    private 
$fruits = array();

    
//Constructor
    
public function __construct(){
        
$this->sweet = array('a' => 'apple''b' => 'banana');
        
$this->fruits = array('sweet' => $this->sweet'sour' => 'lemon');
    }

    
//public methods
    
public function getFruits(){
        return 
$this->fruits;
    }

    public function 
test_print($item$key){
        echo 
"$key holds $item\n<br>";
    }

    public function 
arrWalker($arr){
        
array_walk_recursive($arr, array(&$this'test_print'));

    }
}
?>

the code above gives following input:
TESTING: arraywalker
a holds apple
b holds banana
sour holds lemon

a holds apple
b holds banana
sour holds lemon

egingell at sisna dot com (2006-03-18 01:53:31)

Slightly modified version of array_walk_recursive function by "omega13a at sbcglobal dot net"

$function also modifies the keys in addition to the values.

Usefull if you want to trim() the keys and values of an array.

The $key in $funcname would also have to be passed by referrence.

Named 'array_walk_recursive2' since it is not the same as the PHP5 version, you can rename it to 'array_walk_recursive' if you are running PHP4 and have no reason to upgrade.

<?
function funcname(&$value, &$key, $userdata = "") {
    // Do stuff to $value and $key
}

function array_walk_recursive2(&$input, $funcname, $userdata = "") {
    if (!is_callable($funcname)) {
        return false;
    }

    if (!is_array($input)) {
        return false;
    }

    foreach ($input AS $key => $value) {
        if (is_array($input[$key])) {
            array_walk_recursive2($input[$key], $funcname, $userdata);
        } else {
            $saved_value = $value;
            $saved_key = $key;
            if (!empty($userdata)) {
                $funcname($value, $key, $userdata);
            } else {
                $funcname($value, $key);
            }

            if ($value != $saved_value || $saved_key != $key) {
                unset($input[$saved_key]);
                $input[$key] = $value;
            }
        }
    }
    return true;
}
?>

gabrielu at hotmail dot com (2006-02-21 13:13:18)

I decided to add to the previous PHP 4 compatible version of array_walk_recursive() so that it would work within a class and as a standalone function.  Both instances are handled by the following function which I modified from omega13a at sbcglobal dot net.

The following example is for usage within a class.  To use as a standalone function take it out of the class and rename it.  (Example: array_walk_recursive_2)

<?php
class A_Class {

function 
array_walk_recursive(&$input$funcname$userdata '') {
  if(!
function_exists('array_walk_recursive')) {
    if(!
is_callable($funcname))
      return 
false;

    if(!
is_array($input))
      return 
false;

    foreach(
$input as $key=>$value) {
      if(
is_array($input[$key])) {
        if(isset(
$this)) {
          eval(
'$this->' __FUNCTION__ '($input[$key], $funcname, $userdata);');
        } else {
          if(@
get_class($this))
            eval(
get_class() . '::' __FUNCTION__ '($input[$key], $funcname, $userdata);');
          else
            eval(
__FUNCTION__ '($input[$key], $funcname, $userdata);');
        }
      } else {
        
$saved_value $value;

        if(
is_array($funcname)) {
          
$f '';
          for(
$a=0$a<count($funcname); $a++)
            if(
is_object($funcname[$a])) {
              
$f .= get_class($funcname[$a]);
            } else {
              if(
$a 0)
                
$f .= '::';
              
$f .= $funcname[$a];
            }
          
$f .= '($value, $key' . (!empty($userdata) ? ', $userdata' '') . ');';
          eval(
$f);
        } else {
          if(!empty(
$userdata))
            
$funcname($value$key$userdata);
          else
            
$funcname($value$key);
        }

        if(
$value != $saved_value)
          
$input[$key] = $value;
      }
    }
    return 
true;
  } else {
    
array_walk_recursive($input$funcname$userdata);
  }
}

function 
kv_addslashes(&$v$k) {
  
$v addslashes($v);
}
}
?>

Usage:
<?php
$arr 
= array(
  
'a' => '"Hello World"',
  
'b' => "'Hello World'",
  
'c' => "Hello 'Worl\"d",
  
'd' => array(
    
'A' => 'H"e"l"l"o" "W"o"r"l"d'
    
)
  );

$class = new A_Class();
$class->array_walk_recursive($arr, array(&$class'kv_addslashes'));
print_r($arr);
?>

omega13a at sbcglobal dot net (2005-12-21 16:21:17)

This is a peice of code I wrote that appears to create this function for PHP 4.

<?php
if (!function_exists('array_walk_recursive'))
{
    function 
array_walk_recursive(&$input$funcname$userdata "")
    {
        if (!
is_callable($funcname))
        {
            return 
false;
        }
        
        if (!
is_array($input))
        {
            return 
false;
        }
        
        foreach (
$input AS $key => $value)
        {
            if (
is_array($input[$key]))
            {
                
array_walk_recursive($input[$key], $funcname$userdata);
            }
            else
            {
                
$saved_value $value;
                if (!empty(
$userdata))
                {
                    
$funcname($value$key$userdata);
                }
                else
                {
                    
$funcname($value$key);
                }
                
                if (
$value != $saved_value)
                {
                    
$input[$key] = $value;
                }
            }
        }
        return 
true;
    }
}
?>

Please note it is a conditionaly set function and will have to be put before any call to it.

If there is anything wrong with it, please email me.

cory at lavacube dot com (2005-12-06 14:09:58)

A simple way to implement array_walk_recursive() in PHP 4 is to use to do the following...

<?php

// first, lets define our function
    
function test_array_walkr( &$item$key )
    {
        
// do what you want to do here - in this example we will 
        // check to see if $item is an array. If it is, we will 
        // check to see if the key '.hide' exists. If this exists, 
        // we will set the entire array to NULL;
        
if( is_array($item) && array_key_exists('.hide'$item) )
        {
            
$item NULL;
        }

        
// this is the code that causes the recursive effect
        // we do this after, to allow for any changes to $item
        // to be included in the next recursive call...
        
if( is_array($item) )
        {
            
array_walk($item__FUNCTION__);
        }
    }

// next, let's define our array:
$test = array(
                
'one'    => array('one->one''one->two''one->three'),
                
'two'    => array('.hide'=>true'two->one''two->two''two->three'),
                
'three'    => array('three->one''three->two''three->three')
            );

// lets run the test. ;-)
    
array_walk($test'test_array_walkr');

// ... and get the results
    
print_r($test);

?>

This example will yeild:
Array
(
    [one] => Array
        (
            [0] => one->one
            [1] => one->two
            [2] => one->three
        )
    [two] => 
    [three] => Array
        (
            [0] => three->one
            [1] => three->two
            [2] => three->three
        )
)

Hope this helps someone. :-)

aidan at php dot net (2005-02-21 23:10:03)

This functionality is now implemented in the PEAR package PHP_Compat.
More information about using this function without upgrading your version of PHP can be found on the below link:
http://pear.php.net/package/PHP_Compat

hannes (at) tismer.com (2004-12-14 11:08:54)

I wondered about an array_mergedown function working with PHP4, using an array_walk_recursive-like function which just merges any number of arrays of any dimension to a one dimension array containing every key=>value part of all arrays:

<?php
function array_mergedown() {
    global 
$outarray;
    
$outarray = array();
    function 
array_walk_recphp4(&$val,$key) {
        global 
$outarray;
        if (
is_array($val)) array_walk($val,'array_walk_recphp4');
        else {
            
$outarray[$key] = $val;
        }
    }
    
$params func_get_args();
    foreach (
$params as $subarr) {
        
array_walk_recphp4($subarr'');
    }
    return 
$outarray;
}
?>

For testing:

<?php
$arr1
[]["foo1"] = "bar1";
$arr2["foo2"] = "bar2";
$arr2[12] = "bar3";
$arr2[10]["foo4"] = "bar4";
$arr2[]["foo4"][0]["foo5"] = "bar5";
$arr3 "nono";

print_r(array_mergedown($arr1$arr2$arr3));
?>

returns:
Array ( [foo1] => bar1 [foo2] => bar2 [12] => bar3 [foo4] => bar4 [foo5] => bar5 )

I hope this helped someone :)

易百教程