How to access and manipulate multi-dimensional array by key names / path?

前端 未结 10 2213
北恋
北恋 2020-11-21 07:35

I\'ve to implement a setter in PHP, that allows me to specify the key, or sub key, of an array (the target), passing the name as a dot-separated-keys value.

10条回答
  •  爱一瞬间的悲伤
    2020-11-21 08:11

    I have a utility I regularly use that I'll share. The difference being it uses array access notation (e.g. b[x][z]) instead of dot notation (e.g. b.x.z). With the documentation and code it is fairly self-explanatory.

    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
    
    ?>
    

提交回复
热议问题