2011-02-24 68 views
1

問題PHP:在運行時修改具有未知結構的數組;什麼是最優雅的解決方案?

我有一個函數,它在嵌套陣列,其中該陣列的結構和築巢是在運行時未知。所有已知的是一些目標字段名稱和一些葉子的期望值。

質詢

1)我希望能修改這個未知的結構,仍然有代碼可讀性和同胞程序員容易理解。什麼(如果有的話)解決方案將允許我在PHP中做這樣的事情?

// Pseudo-code for things I would like to be able to do 
// this is kinda like the same thing as XPATH, but for native PHP array 

// find *every* fname with value of "Brad" and change it to "Brian" 
$mydata->find_all('*:fname')->where_value_eq('Brad')->set_equal_to('Brian'); 

// find *the first* fave_color and set it to "Green" 
$mydata->find('*:fave_color')->get(0)->set_equal_to('Green'); 

2)如果有什麼在那裏,讓我做到這一點,是有什麼,什麼,至少能接近我希望能在這裏完成的精神?

樣本數組

$mydata = array(
    'people' => array(
     array('fname'=>'Alice'), 
     array('fname'=>'Brad'), 
     array('fname'=>'Chris'), 
    ), 
    'animals' => array(
     array('fname'=>'Dino'), 
     array('fname'=>'Lassie'), 
     array('fname'=>'Brad'), 
    ), 
    'settings' => array(
     'user_prefs'=>array(
     'localhost'=>array(
      'fave_color'=>'blue', 
     ), 
    ), 
    ), 
    'places' => array(
     array('state'=>'New york', 
      'cities'=>array(
       'name'=>'Albany', 
       'name'=>'Buffalo', 
       'name'=>'Corning', 
      ), 
      'state'=>'California', 
      'cities'=>array(
       'name'=>'Anaheim', 
       'name'=>'Bakersfield', 
       'name'=>'Carlsbad', 
      ),    
    ), 
    ), 
); 
+0

嗨!這個問題沒有回答嗎? – 2011-03-03 04:20:50

回答

1

雖然我認爲,你應該有明確的操作堅持在我以前的答案,無聊和陰謀讓我的好;)

它可能有孔(和顯然缺乏文檔),但如果你堅持這個路線,它應該讓你開始:

class Finder { 
    protected $data; 

    public function __construct(&$data) { 
     if (!is_array($data)) { 
      throw new InvalidArgumentException; 
     } 

     $this->data = &$data; 
    } 

    public function all() { 
     return $this->find(); 
    } 

    public function find($expression = null) { 
     if (!isset($expression)) { 
      return new Results($this->data); 
     } 

     $results = array(); 
     $this->_find(explode(':', $expression), $this->data, $results); 
     return new Results($results); 
    } 

    protected function _find($parts, &$data, &$results) { 
     if (!$parts) { 
      return; 
     } 

     $currentParts = $parts; 
     $search = array_shift($currentParts); 
     if ($wildcard = $search == '*') { 
      $search = array_shift($currentParts); 
     } 

     foreach ($data as $key => &$value) { 
      if ($key === $search) { 
       if ($currentParts) { 
        $this->_find($currentParts, $value, $results); 
       } else { 
        $results[] = &$value; 
       } 
      } else if ($wildcard && is_array($value)) { 
       $this->_find($parts, $value, $results); 
      } 
     } 
    } 
} 

class Results { 
    protected $data; 

    public function __construct(&$data) { 
     $this->data = $data; 
    } 

    public function get($index, $limit = 1) { 
     $this->data = array_slice($this->data, $index, $limit); 
     return $this; 
    } 

    public function set_equal_to($value) { 
     foreach ($this->data as &$datum) { 
      $datum = $value; 
     } 
    } 

    public function __call($method, $args) { 
     if (!preg_match('/^where_?(key|value)_?(eq|contains)$/i', $method, $m)) { 
      throw new BadFunctionCallException; 
     } 

     if (!isset($args[0])) { 
      throw new InvalidArgumentException; 
     } 

     $operand = $args[0]; 
     $isKey = strtolower($m[1]) == 'key'; 
     $method = array('Results', '_compare' . (strtolower($m[2]) == 'eq' ? 'EqualTo' : 'Contains')); 

     $ret = array(); 
     foreach ($this->data as $key => &$datum) { 
      if (call_user_func($method, $isKey ? $key : $datum, $operand)) { 
       $ret[] = &$datum; 
      } 
     } 

     $this->data = $ret; 
     return $this; 
    } 

    protected function _compareEqualTo($value, $test) { 
     return $value == $test; 
    } 

    protected function _compareContains($value, $test) { 
     return strpos($value, $test) !== false; 
    } 
} 

$finder = new Finder($mydata); 
$finder->find('*:fname')->where_value_eq('Brad')->set_equal_to('Brian'); 
$finder->find('*:fave_color')->get(0)->set_equal_to('Green'); 
$finder->find('places:*:cities:*:name')->where_value_contains('ba')->set_equal_to('Stackoton'); 

print_r($mydata); 
1

有一定對此沒有本地的解決方案和語法是比較奇怪的。如果你想要的代碼爲「爲可讀的,同胞的程序員很容易理解」請堅持,我們已經習慣了與工作方法;)

foreach ($mydata as $type => &$data) { 
    foreach ($data as &$member) { 
     if (isset($member['fname']) && $member['fname'] == 'Brad') { 
      $member['fname'] = 'Brian'; 
     } 
    } 
} 

這是無可否認的更詳細,但有少得多混亂的機會。

+0

語法奇怪,因爲它的僞代碼,其主要目的是讓我的問題得到理解。就你所給出的例子而言,這不適用於組成未知的遞歸結構;或者可以澄清它以說明它是如何做到的? – dreftymac 2011-02-24 21:56:52

+0

@dreftymac你說得對,我太匆忙了,儘管做遞歸/堆疊是很微不足道的。 – 2011-02-24 22:58:51

相關問題