PHP闭包(匿名函数)

在PHP的编程中,我们经常会遇到或者使用闭包,也叫做匿名函数。这个特性从PHP5.3开始支持,匿名函数可用于动态创建函数,并保存到一个变量中,以及其他巧妙的用法,下面我们来了解一下闭包。

最常见的匿名函数

<?php
$func = function(){
    echo('Hello world!!');
};//这里必须要有;结尾
$func();
?>

可以看到,将函数保存为变量func,通过变量名()来调用函数。以上代码等同于:

<?php
 function func(){
    echo('Hello world!!');
};//这里必须要有;结尾
func();
?>

看上去没什么特别的,那么我们继续研究

<?php
function operate($operator){
    if($operator == "-"){
        return function($a,$b){
            return $a-$b;
        };
    }else{
        return function($a,$b){
            return $a+$b;
        };
    }
}
$subtraction = operate("-");
echo $subtraction(4,3);//输出1
$addition = operate("+");
echo $addition(1,2);//输出3
?>

以上代码通过给函数operate传入不同的参数,从而返回不同的匿名函数,用变量来保存返回的匿名函数,再来调用。其巧妙之处在于动态的返回不同的函数。

进阶匿名函数

<?php
class Di{
    private $_factory;
    public function set($id,$value){
        $this->_factory[$id] = $value;
    }

    public function get($id){
        $value = $this->_factory[$id];
        return $value();//此处调用传入的匿名方法生成对象
    }
}
class User{
    private $_username;
    function __construct($username="") {
        $this->_username = $username;
    }
    function getUserName(){
        return $this->_username;
    }
}
//从这里开始看
$di = new Di();
$di->set("zhangsan",function(){
    return new User('张三');
});
$di->set("lisi",function(){
    return new User("李四");
});
echo $di->get("zhangsan")->getUserName();
echo $di->get("lisi")->getUserName();
?>

代码中的Di容器用来保存返回对象实例的方法,使用di容器用set()注册了两个这样的方法,通过get()方法获取方法。
我们看到$di->set()的时候,使用了匿名函数,我们预先注册了zhangsan和lisi两个服务,这两个服务都是User类的实例,在$di->set的时候实际上并没有实例化,而是在$di->get()的时候才执行了匿名函数并将对象返回,这就实现了按需实例化,不用则不实例化,提高效率(这点很重要)。

闭包特性

闭包的特性:
①.封闭性:外界无法访问闭包内部的数据,如果在闭包内声明变量,外界是无法访问的,除非闭包主动向外界提供访问接口;
②.持久性:一般的函数,调用完毕之后,系统自动注销函数,而对于闭包来说,在外部函数被调用之后,闭包结构依然保存在
系统中,闭包中的数据依然存在,从而实现对数据的持久使用。

优点:
① 减少全局变量。
② 减少传递函数的参数量
③ 封装
缺点:
使用闭包会占有内存资源,过多的使用闭包会导致内存溢出等。
经常写js的同学应该知道js的闭包特性,简单的说就是在一个函数里可以定义子函数,父函数中的变量在子函数中可以直接使用,那么PHP的闭包有什么区别。

<?php
function func1($a,$b){
    return function() use ($a,$b){
        echo $a,$b;
    };
}
$a = func1("a",'dd');
$a();//输出add
?>

将外层变量传进内层匿名函数时,使用use (变量)的方式。

闭包的常见功能

减少foreach的循环的代码

<?php
// 一个基本的购物车,包括一些已经添加的商品和每种商品的数量。
// 其中有一个方法用来计算购物车中所有商品的总价格。该方法使用了一个closure作为回调函数。
class Cart
{
    const PRICE_BUTTER  = 1.00;//3个产品的价格
    const PRICE_MILK    = 3.00;
    const PRICE_EGGS    = 6.95;

    protected   $products =array();

    public function add($product,$quantity)
    {
        $this->products[$product] = $quantity;
    }

    public function getQuantity($product)
    {
        return isset($this->products[$product]) ? $this->products[$product] :
            FALSE;
    }

    public function getTotal($tax)
    {
        $total = 0.00;

        $callback =
            function ($quantity,$product)use ($tax, &$total)//传地址进来
            {
                $pricePerItem = constant(__CLASS__ ."::PRICE_" .
                    strtoupper($product));//获取头部定义的3个常量
                $total += ($pricePerItem *$quantity) * ($tax + 1.0);
            };

        array_walk($this->products,$callback);//array_walk()对数组中的每个元素应用用户自定义函数,一般为函数赋值2个参数,分别为数组的键值。
        return round($total, 2);//round() 函数对浮点数进行四舍五入。
    }
}

$my_cart =new Cart;

// 往购物车里添加条目
$my_cart->add('butter', 1);//产品名及个数
$my_cart->add('milk', 3);
$my_cart->add('eggs', 6);

// 打出出总价格,其中有 5% 的销售税.
print $my_cart->getTotal(0.05) . "\n";
// The result is 54.29
?>

可以看到,通过array_walk函数对匿名函数的调用实现了循环计算价格,如果不用匿名函数的话不好用array_walk,因为只为被调用函数赋值2个参数,而计算价格所需的参数较多。

减少函数的参数

<?php
function html ($code ,$id="",$class=""){

if ($id !=="")$id =" id = \"$id\"" ;

$class = ($class !=="")?" class =\"$class\"":">";

$open ="<$code$id$class";

$close ="</$code>";

return function ($inner ="")use ($open,$close){

echo "$open$inner$close";
};

}
 html("java","id","class")("hahahh")
?>

结果为:
<java id = "id" class ="class"hahahh</java>
如果是使用平时的方法,我们会把inner放到html函数参数中,说实话我不认为这有什么意义,等于将参数拆开赋值而已,并将一个函数拆成2个。或者这样:

 $code=html("java","id","class");
$code("hahah");

看个人需求吧。

代替递归函数

<?php
$fib =function($n)use(&$fib) {//必须用&,变量没有定义
    if($n == 0 || $n == 1) return 1;
    return $fib($n - 1) + $fib($n - 2);
};

echo $fib(5) . "\n";// 2
function tigui($n){

    if($n == 0 || $n == 1) return 1;
    return tigui($n - 1) + tigui($n - 2);

}
echo tigui(5);

关于延迟绑定

如果你需要延迟绑定use里面的变量,你就需要使用引用(&),否则在定义的时候就会做一份拷贝放到use中

<?php
$result = 0;
$two =function()use ($result)
{ var_dump($result); };

$three =function()use (&$result)
{ var_dump($result); };

$result++;


$two(); // outputs int(0): $result was copied
$three(); // outputs int(1)

two方法拷贝一份为0的数据,所以结果为0
运行完result++之后,result为1,再调用three方法绑定,结果为1

点赞

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注