在不更改数千行代码的情况下创建静态数组


Creating a static array without changing thousands of lines of code

我们有一个名为 $saved 的公共数组,其中包含在方法之间共享所需的大量数据(下面的示例(...

class Common {
    public $saved = array();
    public function setUser($data) {
        $this->saved['user_data'] = $data;
    }
    public function getUserID() {
        return $this->saved['user_data']['id'];
    }
}

实际上有数千行代码像这样工作。

问题在于,在某些方法中正在创建扩展Common类的新实例,因此当它们访问$saved时,它不会保存相同的数据。

解决方案是使$saved成为静态变量,但是我无法更改对$this->saved的所有引用,因此我想尝试保持代码相同,但使其充当静态。

这是我尝试使$this->saved调用静态...

class PropertyTest {
    private $data = array();
    public function __set($name, $value) {
        $this->data[$name] = $value;
    }
    public function __get($name) {
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name];
        }
        return null;
    }
    public function __isset($name) {
        return isset($this->data[$name]);
    }
    public function __unset($name) {
        unset($this->data[$name]);
    }
}
class Common {
    public $saved;
    private static $_instance;
    public function __construct() {
        $this->saved = self::getInstance();
    }
    public static function getInstance() {
        if (self::$_instance === null) {
            self::$_instance = new PropertyTest();
            self::$_instance->foo = array();
        }
        return self::$_instance->foo;
    }
}

这在设置变量时不太有效,它似乎不会保持静态(下面的测试用例(......

class Template extends Common {
    public function __construct() {
        parent::__construct();
        $this->saved['user_data'] = array('name' => 'bob');
        $user = new User();
    }
}
class User extends Common {
    public function __construct() {
        parent::__construct();
        $this->saved['user_data']['name'] .= " rocks!";
        $this->saved['user_data']['id'] = array(400, 10, 20);
    }
}
$tpl = new Template();
print_r($tpl->saved['user_data']);

$this->saved初始化时User为空,并且似乎不是同一个变量,最后的print_r只显示 name => bob 的数组。

有什么想法吗?

首先,我不得不说,IMO,将实例的属性用作类的属性并不是那么好($saved没有声明为static但它的值与所有实例共享(。

这是 http://codepad.org/8hj1MOCT 的工作版本,这是注释的代码。基本上,诀窍在于同时使用 ArrayAccess 接口和单例模式。

class Accumulator implements ArrayAccess {
    private $container = array();
    private static $instance = null;
    private function __construct() {
    }
    public function getInstance() {
        if( self::$instance === null ) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    public function offsetSet($offset, $value) {
        if (is_null($offset)) {
            $this->container[] = $value;
        } else {
            $this->container[$offset] = $value;
        }
    }
    public function offsetExists($offset) {
        return isset($this->container[$offset]);
    }
    public function offsetUnset($offset) {
        unset($this->container[$offset]);
    }
    public function offsetGet($offset) {
        return isset($this->container[$offset]) ? $this->container[$offset] : null;
    }
}

class Common {
    public $saved = null;

    public function __construct() {
        // initialize the "saved" object's property with the singleton
        // that variable can be used with the array syntax thanks to the ArrayAccess interface
        // so you won't have to modify your actual code
        // but also, since it's an object, this local "$this->saved" is a reference to the singleton object
        // so any change made to "$this->saved" is in reality made into the Accumulator::$instance variable
        $this->saved = Accumulator::getInstance();
    }
    public function setUser($data) {
        $this->saved['user_data'] = $data;
    }
    public function getUser() {
        return $this->saved['user_data'];
    }
}

class Template extends Common {
    // you can redeclare the variable or not. Since the property is inherited, IMO you should not redeclare it, but it works in both cases
    // public $saved = null;
    public function __construct() {
        // maybe we can move this initialization in a method in the parent class and call that method here
        $this->saved = Accumulator::getInstance();
    }
}
我认为

这个实现有很多问题很可能会回来咬你。但是,在当前的实现中,您每次都会构建一个新实例(尽管通过静态调用(。

相反,请使用 getInstance(( 作为您的单例钩子,并将您的__construct设为私有,因为您只能从 Common 类的上下文中访问它。

这样:

class Common {
    public $saved;
    private static $_instance;
    private function __construct() {
    }
    public static function getInstance() {
        if (self::$_instance === null) {
            self::$_instance = new self();
            ... any other modifications you want to make ....
        }
        return self::$_instance;
    }
}

并且永远不要运行 parent::_construct((,而是始终使用 getInstance(( 方法。

您可能还想放弃扩展此单例类的想法。这确实是一个糟糕的反模式,从长远来看可能会给您带来许多问题。相反,只需维护一个其他类可以读取/写入的Common类。由于它是单例,因此您无需担心注射。

我似乎已经解决了这个问题,通过$this->saved引用静态变量,它可以工作......

class Common {
    private static $savedData = array();
    public $saved;
    public function __construct() {
        $this->saved =& self::$savedData;
    }
}