PHP使用数组作为名称设置魔术方法

时间:2011-09-21 12:58:00

标签: php setter

我正在创建一个类,我将用它来存储和加载一些设置。在课堂内,所有设置都存储在一个数组中。设置可以嵌套,因此设置数组是一个多维数组。我想使用魔术方法__get和__set存储和加载设置,因此设置可以充当类成员。但是,由于我使用的是嵌套方法,因此在尝试访问嵌套设置时无法使__set方法起作用。

班级是这样的:

class settings
{
    private $_settings = array();

    //some functions to fill the array

    public function __set($name, $value)
    {
        echo 'inside the __set method';
        //do some stuff
    }
}

使用此类的代码:

$foo = new settings();
//do some stuff with the class, so the internal settings array is as followed:
//array(
//    somename => somevalue
//    bar => array (
//               baz = someothervalue
//               qux = 42
//                 )
//     )
$foo->somename = something; //this works, __set method is called correctly
$foo->bar['baz'] = somethingelse; //Doesn't work, __set method isn't called at all

如何让最后一行工作?

4 个答案:

答案 0 :(得分:6)

使用此方法访问数组时,实际上会通过__get。为了在返回的数组上设置参数,需要将其作为引用返回:&__get($name)

除非,您的意思是您希望作为数组返回的每个项目的行为方式与父对象相同,在这种情况下,您应该查看Zend Framework的Zend_Config对象源这样做的好方法。 (它以子数组作为参数返回自身的新实例。)

答案 1 :(得分:4)

答案 2 :(得分:2)

部分$ foo-> bar实际上是在调用__get,这个函数应该(在你的情况下)返回一个数组。

在__get中返回正确的数组将成为您的解决方案。

答案 3 :(得分:0)

如前所述,这是因为存储在$foo->bar中的数组正在被修改而不是类成员。在'数组'上调用__set行为的唯一方法是创建一个实现ArrayAccess接口和offsetSet方法的类,但是这会破坏保持设置的目的同一个对象。

一个相当简洁和常见的解决方法是使用点分隔路径:

class Settings {

  protected $__settings = array();

  // Saves a lot of code duplication in get/set methods.
  protected function get_or_set($key, $value = null) {
    $ref =& $this->__settings;
    $parts = explode('.', $key);

    // Find the last array section
    while(count($parts) > 1) {
      $part = array_shift($parts);
      if(!isset($ref[$part]))
        $ref[$part] = array();
      $ref =& $ref[$part];
    }

    // Perform the appropriate action.
    $part = array_shift($parts);
    if($value)
      $ref[$part] = $value;
    return $ref[$part];
  }

  public function get($key) { return $this->get_or_set($key); }

  public function set($key, $value) { return $this->get_or_set($key, $value); }

  public function dump() { print_r($this->__settings); }
}

$foo = new Settings();
$foo->set('somename', 'something');
$foo->set('bar.baz', 'somethingelse');
$foo->dump();
/*Array
  (
    [somename] => something
    [bar] => Array
      (
        [baz] => somethingelse
      )
  )*/

这也使您更清楚地了解操作实例变量,以及允许任意键而不必担心与实例变量冲突。通过简单地将关键比较添加到get/set,例如

,可以实现对特定密钥的进一步处理
public function set(/* ... */) {
  /* ... */
  if(strpos($key, 'display.theme') == 0)
    /* update the theme */
  /* ... */
}