我编写了一个相当简单的延迟加载代理类,我已经在http://blog.simonholywell.com/post/2072272471/logging-global-php-objects-lazy-loading-proxy
过去记录过了现在当我将另一个项目转换为使用它时,我已经通过代理一个方法来绊倒,该方法的一个参数通过引用传递给它。当这通过我的代理类的__call方法时,我得到:
致命错误:方法LazyLoader :: __ call()不能通过/home/simon/file/name.php中的引用获取参数
任何关于如何解决或解决这个问题的聪明想法。最好不要重构需要通过引用传递的代码。
延迟加载代理类看起来像这样,但my blog post中的描述更好地解释了目的:
<?php
/**
* @author Simon Holywell <treffynnon@php.net>
*/
class LazyLoadingProxy {
/**
* Where the instance of the actual class is stored.
* @var $instance object
*/
private $instance = null;
/**
* The name of the class to load
* @var $class_name string
*/
private $class_name = null;
/**
* The path to the class to load
* @var $class_path string
*/
private $class_path = null;
/**
* Set the name of the class this LazyLoader should proxy
* at the time of instantiation
* @param $class_name string
*/
public function __construct($class_name, $class_path = null) {
$this->setClassName($class_name);
$this->setClassPath($class_path);
}
public function setClassName($class_name) {
if(null !== $class_name) {
$this->class_name = $class_name;
}
}
public function getClassName() {
return $this->class_name;
}
public function setClassPath($class_path) {
if(null !== $class_path) {
$this->class_path = $class_path;
}
}
public function getClassPath() {
return $this->class_path;
}
/**
* Get the instance of the class this LazyLoader is proxying.
* If the instance does not already exist then it is initialised.
* @return object An instance of the class this LazyLoader is proxying
*/
public function getInstance() {
if(null === $this->instance) {
$this->instance = $this->initInstance();
}
return $this->instance;
}
/**
* Load an instance of the class that is being proxied.
* @return object An instance of the class this LazyLoader is proxying
*/
private function initInstance() {
Logger::log('Loaded: ' . $class_name);
require_once($this->class_path);
$class_name = $this->class_name;
return new $class_name();
}
/**
* Magic Method to call functions on the class that is being proxied.
* @return mixed Whatever the requested method would normally return
*/
public function __call($name, &$arguments) {
$instance = $this->getInstance();
Logger::log('Called: ' . $this->class_name . '->' . $name . '(' . print_r($arguments, true) . ');');
return call_user_func_array(
array($instance, $name),
$arguments
);
}
/**
* These are the standard PHP Magic Methods to access
* the class properties of the class that is being proxied.
*/
public function __get($name) {
Logger::log('Getting property: ' . $this->class_name . '->' . $name);
return $this->getInstance()->$name;
}
public function __set($name, $value) {
Logger::log('Setting property: ' . $this->class_name . '->' . $name);
$this->getInstance()->$name = $value;
}
public function __isset($name) {
Logger::log('Checking isset for property: ' . $this->class_name . '->' . $name);
return isset($this->getInstance()->$name);
}
public function __unset($name) {
Logger::log('Unsetting property: ' . $this->class_name . '->' . $name);
unset($this->getInstance()->$name);
}
}
非常感谢任何帮助。
答案 0 :(得分:4)
简短的回答是不通过引用传递。在99.9%的情况下,您不需要它。而在其他0.1%的情况下,你可以解决缺乏参考的问题。请记住,objects are passed by object-reference anyway因此您不需要为它们使用变量引用。
现在,就解决方法而言,我个人将其硬编码到适配器中。因此,扩展您的特定类的代理,并包含该特定方法的包装器。然后实例化新的扩展类而不是该类的核心代理。它脏了吗?绝对。但是,这是你唯一的解决方法,不重构原始类,不通过引用接受参数,或重构调用者以防止通过引用传递(无论如何都弃用)。
答案 1 :(得分:1)
这是一个技巧,通过__call魔术方法通过引用传递变量: 我现在不会有多长时间或巫婆php版本。 我在php 5.3.2上测试了
Fitst你不能在__call函数定义中通过$ reference传递$ arguments变量。因为php会出错。
首先: 这是一个代码什么不做,你想要的引用参数,但几乎是好的;)
class A { public $x = array(); }
class Teszt{
private function addElement( &$list ){
$list->x[] = 'new element';
return count($list->x);
}
public function __call($name,$arguments){
if (!preg_match('#^add([0-9]+)Element$#', $name, $matches) || $matches[1]<1){
trigger_error ("Function is not exists teszt::$name", E_USER_ERROR);
}
$ret = null;
for($i=0;$i<$matches[1];$i++){
$ret = call_user_func_array(array($this,'addElement'), $arguments);
}
return $ret;
}
}
$a = new A();
$a->x = array('old element','old element');
$t = new Teszt();
$cnt = $t->add5Element($a);
var_dump($a);
var_dump($cnt);
输出:
PHP Warning: Parameter 1 to Teszt::addElement() expected to be a reference, value given in /home/gkovacs/callMagicWithReference.php on line 19
PHP Stack trace:
PHP 1. {main}() /home/gkovacs/callMagicWithReference.php:0
PHP 2. Teszt->add2Element() /home/gkovacs/callMagicWithReference.php:27
PHP 3. Teszt->__call() /home/gkovacs/callMagicWithReference.php:0
PHP 4. call_user_func_array() /home/gkovacs/callMagicWithReference.php:19
PHP Warning: Parameter 1 to Teszt::addElement() expected to be a reference, value given in /home/gkovacs/callMagicWithReference.php on line 19
PHP Stack trace:
PHP 1. {main}() /home/gkovacs/callMagicWithReference.php:0
PHP 2. Teszt->add2Element() /home/gkovacs/callMagicWithReference.php:27
PHP 3. Teszt->__call() /home/gkovacs/callMagicWithReference.php:0
PHP 4. call_user_func_array() /home/gkovacs/callMagicWithReference.php:19
object(A)#1 (1) {
["x"]=>
array(2) {
[0]=>
string(11) "old element"
[1]=>
string(11) "old element"
}
}
NULL
哦,哦(((
在一个小小的'HACK'之后:
class A { public $x = array(); }
class Teszt{
private function addElement( &$list ){
$list->x[] = 'new element';
return count($list->x);
}
public function __call($name,$arguments){
if (!preg_match('#^add([0-9]+)Element$#', $name, $matches) || $matches[1]<1){
trigger_error ("Function is not exists teszt::$name", E_USER_ERROR);
}
$ret = null;
//Look at my hand, because i will cheat
foreach($arguments as $k=>&$v){ }
//end of cheat
for($i=0;$i<$matches[1];$i++){
$ret = call_user_func_array(array($this,'addElement'), $arguments);
}
return $ret;
}
}
$a = new A();
$a->x = array('old element','old element');
$t = new Teszt();
$cnt = $t->add5Element($a);
var_dump($a);
var_dump($cnt);
输出:
object(A)#1 (1) {
["x"]=>
array(4) {
[0]=>
string(11) "old element"
[1]=>
string(11) "old element"
[2]=>
string(11) "new element"
[3]=>
string(11) "new element"
}
}
int(4)
我们想要的。这仅适用于对象,但不适用于array-s。 有一种方法可以使用array-s来调用time-pass-by-reference ...... 像这样:(和corse你也可以用这种方式对象)
$cnt = $t->add5Element(&$a);
在这种情况下,php生成通知......
如果可能的话,重新定义函数的最简单方法。 在我的代码中,:private functionaddElement($ list),不要在参数列表中定义为引用。当您在上一条消息中阅读时,它将起作用,因为对象通过引用自动传递 但有时,由于实现了接口或其他原因,您无法重新定义函数...
答案 2 :(得分:0)
它可以。饥肠辘辘。
<?php
class MyClass(){
public function __call($name, $params)
{
var_dump($params);
$params[0][0] = '222';
}
}
$obj = new MyClass();
$info = 3;
var_dump($info);
$obj->setter([&$info]);
var_dump($info);