Php 如何通过键名/路径访问和操作多维数组?

Php 如何通过键名/路径访问和操作多维数组?,php,arrays,multidimensional-array,Php,Arrays,Multidimensional Array,我必须在PHP中实现一个setter,它允许我指定数组(目标)的键或子键,并将名称作为点分隔键值传递 给定以下代码: $arr = array('a' => 1, 'b' => array( 'y' => 2, 'x' => array('z' => 5, 'w' => 'abc') ), 'c' => nul

我必须在PHP中实现一个setter,它允许我指定数组(目标)的键或子键,并将名称作为点分隔键值传递

给定以下代码:

$arr = array('a' => 1,
             'b' => array(
                 'y' => 2,
                 'x' => array('z' => 5, 'w' => 'abc')
             ),
             'c' => null);

$key = 'b.x.z';
$path = explode('.', $key);
$key
的值中,我希望达到
$arr['b']['x']['z']
的值5

现在,给定一个变量值
$key
和一个不同的
$arr
值(深度不同)

如何设置
$key
引用的元素的值? 对于getter
get()
我编写了以下代码:

public static function get($name, $default = null)
{
    $setting_path = explode('.', $name);
    $val = $this->settings;

    foreach ($setting_path as $key) {
        if(array_key_exists($key, $val)) {
            $val = $val[$key];
        } else {
            $val = $default;
            break;
        }
    }
    return $val;
}
编写setter更加困难,因为我成功地找到了正确的元素(从
$key
),但我无法在原始数组中设置值,而且我不知道如何一次指定所有键


我应该使用某种回溯吗?或者我可以避免吗?

我为您提供的解决方案不是用纯PHP,而是用具体的方法:

类似地,如果需要设置嵌套值,可以使用方法


我为您提供的解决方案不是纯PHP,而是使用具体的方法:

类似地,如果需要设置嵌套值,可以使用方法


假设
$path
通过
分解(或添加到函数)已经是一个数组,则可以使用引用。您需要在无效的
$path
等情况下添加一些错误检查(想想
isset
):

吸气剂 设定者/创造者 此组合将在现有数组中设置一个值,如果传递一个尚未定义的数组,则创建该数组。确保定义要通过引用传递的
$array
和$array

function set($path, &$array=array(), $value=null) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($path, $arr);
//or
set($path, $arr, 'some value');
function set(&$array, $path, $value) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($arr, $path, 'some value');
扰乱 这将
取消设置路径中的最后一个键:

function unsetter($path, &$array) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        if(!is_array($temp[$key])) {
            unset($temp[$key]);
        } else {
            $temp =& $temp[$key];
        }
    }
}
unsetter($path, $arr);
*最初的答案有一些有限的功能,我将保留这些功能,以防它们对某人有用:

二传手

确保定义要通过引用传递的
$array
和$array

function set($path, &$array=array(), $value=null) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($path, $arr);
//or
set($path, $arr, 'some value');
function set(&$array, $path, $value) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($arr, $path, 'some value');
或者如果您想返回更新的数组(因为我很无聊):

创建者

如果您不想创建数组并选择设置值:

function create($path, $value=null) {
    //$path = explode('.', $path); //if needed
    foreach(array_reverse($path) as $key) {
        $value = array($key => $value);
    }
    return $value;
}    

$arr = create($path);    
//or
$arr = create($path, 'some value');
为了好玩

在给定字符串的情况下,构造并计算类似于
$array['b']['x']['z']
的内容
b.x.z

function get($array, $path) {
    //$path = explode('.', $path); //if needed
    $path = "['" . implode("']['", $path) . "']";
    eval("\$result = \$array{$path};");

    return $result;
}
设置类似于
$array['b']['x']['z']='some value'

function set(&$array, $path, $value) {
    //$path = explode('.', $path); //if needed
    $path = "['" . implode("']['", $path) . "']";
    eval("\$array{$path} = $value;");
}
取消设置类似于
$array['b']['x']['z']

function unsetter(&$array, $path) {
    //$path = explode('.', $path); //if needed
    $path = "['" . implode("']['", $path) . "']";
    eval("unset(\$array{$path});");
}

假设
$path
通过
分解(或添加到函数)已经是一个数组,则可以使用引用。您需要在无效的
$path
等情况下添加一些错误检查(想想
isset
):

吸气剂 设定者/创造者 此组合将在现有数组中设置一个值,如果传递一个尚未定义的数组,则创建该数组。确保定义要通过引用传递的
$array
和$array

function set($path, &$array=array(), $value=null) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($path, $arr);
//or
set($path, $arr, 'some value');
function set(&$array, $path, $value) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($arr, $path, 'some value');
扰乱 这将
取消设置路径中的最后一个键:

function unsetter($path, &$array) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        if(!is_array($temp[$key])) {
            unset($temp[$key]);
        } else {
            $temp =& $temp[$key];
        }
    }
}
unsetter($path, $arr);
*最初的答案有一些有限的功能,我将保留这些功能,以防它们对某人有用:

二传手

确保定义要通过引用传递的
$array
和$array

function set($path, &$array=array(), $value=null) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($path, $arr);
//or
set($path, $arr, 'some value');
function set(&$array, $path, $value) {
    //$path = explode('.', $path); //if needed
    $temp =& $array;

    foreach($path as $key) {
        $temp =& $temp[$key];
    }
    $temp = $value;
}

set($arr, $path, 'some value');
或者如果您想返回更新的数组(因为我很无聊):

创建者

如果您不想创建数组并选择设置值:

function create($path, $value=null) {
    //$path = explode('.', $path); //if needed
    foreach(array_reverse($path) as $key) {
        $value = array($key => $value);
    }
    return $value;
}    

$arr = create($path);    
//or
$arr = create($path, 'some value');
为了好玩

在给定字符串的情况下,构造并计算类似于
$array['b']['x']['z']
的内容
b.x.z

function get($array, $path) {
    //$path = explode('.', $path); //if needed
    $path = "['" . implode("']['", $path) . "']";
    eval("\$result = \$array{$path};");

    return $result;
}
设置类似于
$array['b']['x']['z']='some value'

function set(&$array, $path, $value) {
    //$path = explode('.', $path); //if needed
    $path = "['" . implode("']['", $path) . "']";
    eval("\$array{$path} = $value;");
}
取消设置类似于
$array['b']['x']['z']

function unsetter(&$array, $path) {
    //$path = explode('.', $path); //if needed
    $path = "['" . implode("']['", $path) . "']";
    eval("unset(\$array{$path});");
}

我有一个我经常使用的工具,我将与大家分享。区别在于它使用数组访问符号(例如
b[x][z]
)而不是点符号(例如
b.x.z
)。有了文档和代码,它就相当不言自明了

<?php
class Utils {
    /**
     * Gets the value from input based on path.
     * Handles objects, arrays and scalars. Nesting can be mixed.
     * E.g.: $input->a->b->c = 'val' or $input['a']['b']['c'] = 'val' will
     * return "val" with path "a[b][c]".
     * @see Utils::arrayParsePath
     * @param mixed $input
     * @param string $path
     * @param mixed $default Optional default value to return on failure (null)
     * @return NULL|mixed NULL on failure, or the value on success (which may also be NULL)
     */
    public static function getValueByPath($input,$path,$default=null) {
        if ( !(isset($input) && (static::isIterable($input) || is_scalar($input))) ) {
            return $default; // null already or we can't deal with this, return early
        }
        $pathArray = static::arrayParsePath($path);
        $last = &$input;
        foreach ( $pathArray as $key ) {
            if ( is_object($last) && property_exists($last,$key) ) {
                $last = &$last->$key;
            } else if ( (is_scalar($last) || is_array($last)) && isset($last[$key]) ) {
                $last = &$last[$key];
            } else {
                return $default;
            }
        }
        return $last;
    }

    /**
     * Parses an array path like a[b][c] into a lookup array like array('a','b','c')
     * @param string $path
     * @return array
     */
    public static function arrayParsePath($path) {
        preg_match_all('/\\[([^[]*)]/',$path,$matches);
        if ( isset($matches[1]) ) {
            $matches = $matches[1];
        } else {
            $matches = array();
        }
        preg_match('/^([^[]+)/',$path,$name);
        if ( isset($name[1]) ) {
            array_unshift($matches,$name[1]);
        } else {
            $matches = array();
        }
        return $matches;
    }

    /**
     * Check if a value/object/something is iterable/traversable, 
     * e.g. can it be run through a foreach? 
     * Tests for a scalar array (is_array), an instance of Traversable, and 
     * and instance of stdClass
     * @param mixed $value
     * @return boolean
     */
    public static function isIterable($value) {
        return is_array($value) || $value instanceof Traversable || $value instanceof stdClass;
    }
}

$arr = array('a' => 1,
             'b' => array(
                 'y' => 2,
                 'x' => array('z' => 5, 'w' => 'abc')
             ),
             'c' => null);

$key = 'b[x][z]';

var_dump(Utils::getValueByPath($arr,$key)); // int 5

?>

我有一个经常使用的实用程序,我将与大家分享。区别在于它使用数组访问符号(例如
b[x][z]
)而不是点符号(例如
b.x.z
)。有了文档和代码,它就相当不言自明了

<?php
class Utils {
    /**
     * Gets the value from input based on path.
     * Handles objects, arrays and scalars. Nesting can be mixed.
     * E.g.: $input->a->b->c = 'val' or $input['a']['b']['c'] = 'val' will
     * return "val" with path "a[b][c]".
     * @see Utils::arrayParsePath
     * @param mixed $input
     * @param string $path
     * @param mixed $default Optional default value to return on failure (null)
     * @return NULL|mixed NULL on failure, or the value on success (which may also be NULL)
     */
    public static function getValueByPath($input,$path,$default=null) {
        if ( !(isset($input) && (static::isIterable($input) || is_scalar($input))) ) {
            return $default; // null already or we can't deal with this, return early
        }
        $pathArray = static::arrayParsePath($path);
        $last = &$input;
        foreach ( $pathArray as $key ) {
            if ( is_object($last) && property_exists($last,$key) ) {
                $last = &$last->$key;
            } else if ( (is_scalar($last) || is_array($last)) && isset($last[$key]) ) {
                $last = &$last[$key];
            } else {
                return $default;
            }
        }
        return $last;
    }

    /**
     * Parses an array path like a[b][c] into a lookup array like array('a','b','c')
     * @param string $path
     * @return array
     */
    public static function arrayParsePath($path) {
        preg_match_all('/\\[([^[]*)]/',$path,$matches);
        if ( isset($matches[1]) ) {
            $matches = $matches[1];
        } else {
            $matches = array();
        }
        preg_match('/^([^[]+)/',$path,$name);
        if ( isset($name[1]) ) {
            array_unshift($matches,$name[1]);
        } else {
            $matches = array();
        }
        return $matches;
    }

    /**
     * Check if a value/object/something is iterable/traversable, 
     * e.g. can it be run through a foreach? 
     * Tests for a scalar array (is_array), an instance of Traversable, and 
     * and instance of stdClass
     * @param mixed $value
     * @return boolean
     */
    public static function isIterable($value) {
        return is_array($value) || $value instanceof Traversable || $value instanceof stdClass;
    }
}

$arr = array('a' => 1,
             'b' => array(
                 'y' => 2,
                 'x' => array('z' => 5, 'w' => 'abc')
             ),
             'c' => null);

$key = 'b[x][z]';

var_dump(Utils::getValueByPath($arr,$key)); // int 5

?>
作为一个“getter”,我在过去使用过:

$array = array('data' => array('one' => 'first', 'two' => 'second'));

$key = 'data.one';

function find($key, $array) {
    $parts = explode('.', $key);
    foreach ($parts as $part) {
        $array = $array[$part];
    }
    return $array;
}

$result = find($key, $array);
var_dump($result);
作为一名“获得者”,我过去曾使用过:

$array = array('data' => array('one' => 'first', 'two' => 'second'));

$key = 'data.one';

function find($key, $array) {
    $parts = explode('.', $key);
    foreach ($parts as $part) {
        $array = $array[$part];
    }
    return $array;
}

$result = find($key, $array);
var_dump($result);

如果数组的键是唯一的,则可以使用以下方法在几行代码中解决此问题:


如果数组的键是唯一的,则可以使用以下方法在几行代码中解决此问题:


此函数的作用与接受的答案相同,另外还通过引用添加第三个参数,如果存在该键,则该参数设置为true/false

function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
  $ref = &$array;
  foreach ($parents as $parent) {
    if (is_array($ref) && array_key_exists($parent, $ref)) {
      $ref = &$ref[$parent];
    }
    else {
      $key_exists = FALSE;
      $null = NULL;
      return $null;
    }
  }
  $key_exists = TRUE;
  return $ref;
}

此函数的作用与接受的答案相同,另外还通过引用添加第三个参数,如果存在该键,则该参数设置为true/false

function drupal_array_get_nested_value(array &$array, array $parents, &$key_exists = NULL) {
  $ref = &$array;
  foreach ($parents as $parent) {
    if (is_array($ref) && array_key_exists($parent, $ref)) {
      $ref = &$ref[$parent];
    }
    else {
      $key_exists = FALSE;
      $null = NULL;
      return $null;
    }
  }
  $key_exists = TRUE;
  return $ref;
}

我有一个非常简单而肮脏的解决方案(非常肮脏!如果密钥的值不可信,请不要使用!)。它可能比在数组中循环更有效

function array_get($key, $array) {
    return eval('return $array["' . str_replace('.', '"]["', $key) . '"];');
}

function array_set($key, &$array, $value=null) {
    eval('$array["' . str_replace('.', '"]["', $key) . '"] = $value;');
}

这两个函数都对一段代码进行了分析,其中键作为PHP代码转换为数组的一个元素。它返回或设置对应键处的数组值。

我有一个非常简单而肮脏的解决方案(非常肮脏!如果键的值不可信,请不要使用!)。它可能比在数组中循环更有效

function array_get($key, $array) {
    return eval('return $array["' . str_replace('.', '"]["', $key) . '"];');
}

function array_set($key, &$array, $value=null) {
    eval('$array["' . str_replace('.', '"]["', $key) . '"] = $value;');
}

这两个函数都对一段代码进行了分析,其中键作为PHP代码转换为数组的一个元素。它在相应的键处返回或设置数组值。

这里是一个访问和操作MD数组的简单代码。但是没有证券

塞特:

eval('$vars = &$array["' . implode('"]["', explode('.', strtolower($dot_seperator_path))) . '"];');
$vars = $new_value;
吸气剂:

eval('$vars = $array["' . implode('"]["', explode('.', strtolower($dot_seperator_path))) . '"];');
return $vars;

这里有一个访问和操作MD数组的简单代码。但是没有证券

塞特:

eval('$vars = &$array["' . implode('"]["', explode('.', strtolower($dot_seperator_path))) . '"];');
$vars = $new_value;
吸气剂:

eval('$vars = $array["' . implode('"]["', explode('.', strtolower($dot_seperator_path))) . '"];');
return $vars;

另一个解决方案是使用普通的
array\u reduce
方法来实现
getter
<