Access array using dynamic path

2020-02-06 08:47发布

问题:

I have an issue in accessing the array in php.

$path  = "['a']['b']['c']";
$value = $array.$path;

In the above piece of code I have an multidimensional array named $array.

$path is a dynamic value which I would get from database.

Now I want to retrieve the value from $array using $path but I am not able to.

$value = $array.$path

returns me

Array['a']['b']['c']

rather than the value.

I hope I have explained my question properly.

回答1:

You have two options. First (evil) if to use eval() function - i.e. interpret your string as code.

Second is to parse your path. That will be:

//$path = "['a']['b']['c']";
preg_match_all("/\['(.*?)'\]/", $path, $rgMatches);
$rgResult = $array;
foreach($rgMatches[1] as $sPath)
{
   $rgResult=$rgResult[$sPath];
}


回答2:

The Kohana framework "Arr" class (API) has a method (Arr::path) that does something similar to what you are requesting. It simply takes an array and a path (with a . as delimiter) and returns the value if found. You could modify this method to suit your needs.

public static function path($array, $path, $default = NULL, $delimiter = NULL)
{
    if ( ! Arr::is_array($array))
    {
        // This is not an array!
        return $default;
    }

    if (is_array($path))
    {
        // The path has already been separated into keys
        $keys = $path;
    }
    else
    {
        if (array_key_exists($path, $array))
        {
            // No need to do extra processing
            return $array[$path];
        }

        if ($delimiter === NULL)
        {
            // Use the default delimiter
            $delimiter = Arr::$delimiter;
        }

        // Remove starting delimiters and spaces
        $path = ltrim($path, "{$delimiter} ");

        // Remove ending delimiters, spaces, and wildcards
        $path = rtrim($path, "{$delimiter} *");

        // Split the keys by delimiter
        $keys = explode($delimiter, $path);
    }

    do
    {
        $key = array_shift($keys);

        if (ctype_digit($key))
        {
            // Make the key an integer
            $key = (int) $key;
        }

        if (isset($array[$key]))
        {
            if ($keys)
            {
                if (Arr::is_array($array[$key]))
                {
                    // Dig down into the next part of the path
                    $array = $array[$key];
                }
                else
                {
                    // Unable to dig deeper
                    break;
                }
            }
            else
            {
                // Found the path requested
                return $array[$key];
            }
        }
        elseif ($key === '*')
        {
            // Handle wildcards

            $values = array();
            foreach ($array as $arr)
            {
                if ($value = Arr::path($arr, implode('.', $keys)))
                {
                    $values[] = $value;
                }
            }

            if ($values)
            {
                // Found the values requested
                return $values;
            }
            else
            {
                // Unable to dig deeper
                break;
            }
        }
        else
        {
            // Unable to dig deeper
            break;
        }
    }
    while ($keys);

    // Unable to find the value requested
    return $default;
}


回答3:

I was hoping to find an elegant solution to nested array access without throwing undefined index errors, and this post hits high on google. I'm late to the party, but I wanted to weigh in for future visitors.

A simple isset($array['a']['b']['c'] can safely check nested values, but you need to know the elements to access ahead of time. I like the dot notation for accessing multidimensional arrays, so I wrote a class of my own. It does require PHP 5.6.

This class parses a string path written in dot-notation and safely accesses the nested values of the array or array-like object (implements ArrayAccess). It will return the value or NULL if not set.

use ArrayAccess;

class SafeArrayGetter implements \JsonSerializable {

/**
 * @var array
 */
private $data;

/**
 * SafeArrayGetter constructor.
 *
 * @param array $data
 */
public function __construct( array $data )
{
    $this->data = $data;
}

/**
 * @param array $target
 * @param array ...$indices
 *
 * @return array|mixed|null
 */
protected function safeGet( array $target, ...$indices )
{
    $movingTarget = $target;

    foreach ( $indices as $index )
    {
        $isArray = is_array( $movingTarget ) || $movingTarget instanceof ArrayAccess;
        if ( ! $isArray || ! isset( $movingTarget[ $index ] ) ) return NULL;

        $movingTarget = $movingTarget[ $index ];
    }

    return $movingTarget;
}

/**
 * @param array ...$keys
 *
 * @return array|mixed|null
 */
public function getKeys( ...$keys )
{
    return static::safeGet( $this->data, ...$keys );
}

/**
 * <p>Access nested array index values by providing a dot notation access string.</p>
 * <p>Example: $safeArrayGetter->get('customer.paymentInfo.ccToken') ==
 * $array['customer']['paymentInfo']['ccToken']</p>
 *
 * @param $accessString
 *
 * @return array|mixed|null
 */
public function get( $accessString )
{
    $keys = $this->parseDotNotation( $accessString );

    return $this->getKeys( ...$keys );
}

/**
 * @param $string
 *
 * @return array
 */
protected function parseDotNotation( $string )
{
    return explode( '.', strval( $string ) );
}

/**
 * @return array
 */
public function toArray()
{
    return $this->data;
}

/**
 * @param int $options
 * @param int $depth
 *
 * @return string
 */
public function toJson( $options = 0, $depth = 512 )
{
    return json_encode( $this, $options, $depth );
}

/**
 * @param array $data
 *
 * @return static
 */
public static function newFromArray( array $data )
{
    return new static( $data );
}

/**
 * @param \stdClass $data
 *
 * @return static
 */
public static function newFromObject( \stdClass $data )
{
    return new static( json_decode( json_encode( $data ), TRUE ) );
}

/**
 * Specify data which should be serialized to JSON
 * @link http://php.net/manual/en/jsonserializable.jsonserialize.php
 * @return array data which can be serialized by <b>json_encode</b>,
 * which is a value of any type other than a resource.
 * @since 5.4.0
 */
function jsonSerialize()
{
    return $this->toArray();
}
}