PHP中强制引用的实现
为什么要使用引用?目的很简单,减少创建对象的副本,从而可以减少内存消耗。
但是,在PHP中,一些情况下却无法使用引用。典型的情况有:
1、通过__callStatic静态调用一个类的方法时。
为什么要这么做呢?这是因为,用此方法,可以减少大量的new。方便快速开发。
因为,在PHP中,我们new一个类以后,并没有也并不需要手工释放。
所以,我们经常通过__callStatic,在这个函数中实现新对象的创建。
比如:在Laravel项目的Repsitory中,一般我们是这么做的。
public function __callStatic($method,$parameters){ $instance = app(get_called_class()); return $instance->$method(...$parameters); }
但这就出现了问题,参数不能引用传参。
2、通过__call调用。与上述__callStatic是一样的。
3、通过call_user_func和call_user_func_array这两个函数调用。
4、把对象当函数使用,这就是说,函数是通过__invoke进行调用。
虽然,函数的参数不能用引用传参,并且只发生在这5个地点。但这也是相当致命的。因为这些,不我不得不在代码中创建变量的副本。从而浪费内存。
不仅如此,全世界的PHP程序员都在试图寻找这个问题的解决方案。但是,一直没有好的方法。
目前,网上提供的方法有:
在哪到参数后,这时,参数还是引用,所以,一般都是在这里做文章:
第一种方案:
把参数放到数组中,在数组内引用。
比如函数:
function test(&$foo){ return $foo; }
调用时:
call_user_func_array("test",[&$arg]);
但这个方法有些问题,一个参数好办,多个参数,第二个要引用则会如何?
没有办法。除非合并起来引用。
第二种方案:
因为,对象object参数永远是引用的,所以,直接把变量放到 stdclass中。
这时,函数中也不要定义引用了。
function test($foo){ return $foo->some; }
调用时:
$obj = new stdclass(); $obj->some = $foo; call_user_func_array("test",$obj);
这样有两个问题:第一,凭空多写两行代码。第二,定义方与调用方要约定好,参数放在stdclass的哪一个属性中。否则,就会找不到。
我们现在提供的方案则是一个相对比较好一些的方案。这一方案是使用PHP的另一语言特性:闭包。
代码如下:
if(!function_exists('byRef')){ /** * @desc 让参数强制引用的函数。通过闭包创建一个引用通道。 * @param $data * @return Closure */ function byRef(&$data){ //传入引用参数 return function &()use(&$data){ //定义闭包函数以引用返回,同时接收引用参数 return $data; }; } }
将以上代码,保存到helpers.php的文件中。
首先要加载此函数。当然,你可以使用include,require等语句。
但我相信,这年头,你连composer都没用上,对此需求不大。
那么,composer项目中,怎么配自动加载呢?
如果是composer项目,那么,在项目根目录中的composer.json文件中的"autoload"中添加:
"files": [ "app/Helpers/helpers.php" ],
注:上述路径则指明,你是将文件保存在app/Helpers/目录中的。
添加以后,这个全局函数就可以使用了。
接下来,我们说一下使用示例:
例如:我们要调用的源码是:
$ret = SomeRepository::SomeMethod($pass_data,$columns,$where,$order) ;
其中,我们想让 $data 为引用传参,只要这样:
$ret = SomeRepository::SomeMethod(byRef($pass_data),$columns,$where,$order) ;
同时,在类的 SomeMethod方法中要增加几行代码
public function SomeMethod(\Closure $data,$columns,$where,$order){ //以下这个代码不是必须的。因为,我们会直接用闭包方式获取数据。 if(!$data instanceof Closure){ //检测闭包的有效性。 Throw InvalidArgumentException("Data is not valid Closure."); } //只要这一行代码就够了。如果出错,证明调用方未使用强制引用函数。 $src_data = & $data(); //通过引用从闭包中获取数据 //下面,是你的代码。你直接修改$src_data,不用return,你会发现: //$pass_data在同步跟着你的代码改变。 }
以下代码是一个可以测试的示例:
function byRef(&$data){ return function &()use(&$data){ return $data; }; } function &test($try){ $try_data = &$try(); return $try_data; } $data = '123456'; $data_after = &test(byRef($data)); $data_after = '345678'; echo($data); //结果输出:345678
可以看出,通过闭包进行强制传参,代码是最简单,并且,使用也是最方便的。
上一篇: linux命令大全chm下载