2013-02-05 19 views
12

Sto cercando di scrivere una classe che sarà in grado di accedere array multidimensionali con la notazione del punto:accesso un array multidimensionale con la notazione del punto

$config->get('bar.baz.foo'); 

Invece di:

$config['bar']['baz']['foo'] 

Questa è la classe :

<?php 

class DotNotation 
{ 
    /** 
    * @var array 
    */ 
    protected $values = array(); 

    public function __construct(array $values) 
    { 
     $this->values = $values; 
    } 

    /** 
    * @param string $path 
    * @param string $default 
    * @return mixed 
    */ 
    public function get($path, $default = null) 
    { 
     $array = $this->values; 

     if (!empty($path)) { 
      $keys = explode('.', $path); 
      foreach ($keys as $key) { 
       if (isset($array[$key])) { 
        $array = $array[$key]; 
       } else { 
        return $default; 
       } 
      } 
     } 

     return $array; 
    } 

    /** 
    * @param string $path 
    * @param mixed $value 
    */ 
    public function set($path, $value) 
    { 
     $link = & $this->values; 

     if (!empty($path)) { 
      $keys = explode('.', $path); 
      foreach ($keys as $key) { 

       if (!isset($link[$key])) { 
        $link[$key] = array(); 
       } 

       if (!is_array($link[$key])) { 
        throw new \RuntimeException("Can not set value for `$key` in `$path` path because it is not array."); 
       } 

       $link = & $link[$key]; 
      } 
     } 

     $link = $value; 
    } 

    /** 
    * @param $path 
    * @param array $values 
    */ 
    public function add($path, array $values) 
    { 
     $get = (array)$this->get($path); 
     $this->set($path, $this->arrayMergeRecursiveDistinct($get, $values)); 
    } 

    /** 
    * @param mixed $parameters 
    */ 
    public function merge($parameters) 
    { 
     $this->add(null, (array)$parameters); 
    } 

    /** 
    * @param string $path 
    * @return bool 
    */ 
    public function have($path) 
    { 
     $keys = explode('.', $path); 
     $array = $this->values; 
     foreach ($keys as $key) { 
      if (isset($array[$key])) { 
       $array = $array[$key]; 
      } else { 
       return false; 
      } 
     } 

     return true; 
    } 

    /** 
    * array_merge_recursive does indeed merge arrays, but it converts values with duplicate 
    * keys to arrays rather than overwriting the value in the first array with the duplicate 
    * value in the second array, as array_merge does. I.e., with array_merge_recursive, 
    * this happens (documented behavior): 
    * 
    * array_merge_recursive(array('key' => 'org value'), array('key' => 'new value')); 
    *  => array('key' => array('org value', 'new value')); 
    * 
    * arrayMergeRecursiveDistinct does not change the datatypes of the values in the arrays. 
    * Matching keys' values in the second array overwrite those in the first array, as is the 
    * case with array_merge, i.e.: 
    * 
    * arrayMergeRecursiveDistinct(array('key' => 'org value'), array('key' => 'new value')); 
    *  => array('key' => array('new value')); 
    * 
    * Parameters are passed by reference, though only for performance reasons. They're not 
    * altered by this function. 
    * 
    * If key is integer, it will be merged like array_merge do: 
    * arrayMergeRecursiveDistinct(array(0 => 'org value'), array(0 => 'new value')); 
    *  => array(0 => 'org value', 1 => 'new value'); 
    * 
    * @param array $array1 
    * @param array $array2 
    * @return array 
    * @author Daniel <daniel (at) danielsmedegaardbuus (dot) dk> 
    * @author Gabriel Sobrinho <gabriel (dot) sobrinho (at) gmail (dot) com> 
    * @author Anton Medvedev <anton (at) elfet (dot) ru> 
    */ 
    protected function arrayMergeRecursiveDistinct(array &$array1, array &$array2) 
    { 
     $merged = $array1; 

     foreach ($array2 as $key => &$value) { 
      if (is_array($value) && isset ($merged[$key]) && is_array($merged[$key])) { 
       if (is_int($key)) { 
        $merged[] = $this->arrayMergeRecursiveDistinct($merged[$key], $value); 
       } else { 
        $merged[$key] = $this->arrayMergeRecursiveDistinct($merged[$key], $value); 
       } 
      } else { 
       if (is_int($key)) { 
        $merged[] = $value; 
       } else { 
        $merged[$key] = $value; 
       } 
      } 
     } 

     return $merged; 
    } 
} 

Come posso migliorare questa classe non utilizzare explode?

+0

semplicemente '$ config = json_decode (json_encode ($ config));' permetterebbe di utilizzare: 'echo $ config-> bar-> baz-> foo; ';) – Yoshi

risposta

12

È possibile scrivere utilizzando strtok() invece di explode():

$a = array('foo' => array('bar' => array('baz' => 1))); 

function resolve(array $a, $path, $default = null) 
{ 
    $current = $a; 
    $p = strtok($path, '.'); 

    while ($p !== false) { 
    if (!isset($current[$p])) { 
     return $default; 
    } 
    $current = $current[$p]; 
    $p = strtok('.'); 
    } 

    return $current; 
} 

var_dump(resolve($a, 'foo.bar')); 

// array('baz' => 1) 
Problemi correlati