欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

PHP中强制引用的实现

程序员文章站 2022-03-31 12:27:19
...

 

  为什么要使用引用?目的很简单,减少创建对象的副本,从而可以减少内存消耗。

但是,在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

 可以看出,通过闭包进行强制传参,代码是最简单,并且,使用也是最方便的。

相关标签: 闭包 引用