原文地址:传送门

PHP Closure类是用于代表匿名函数的类,匿名函数(在PHP 5.3中被引入)会产生这个类型的对象,Closure类摘要如下:

Closure {  
    __construct ( void )  
    public static Closure bind (Closure $closure , object $newthis [, mixed $newscope = 'static' ])  
    public Closure bindTo (object $newthis [, mixed $newscope = 'static' ])  
}

方法说明:

Closure :: __ construct – 用于禁止实例化的构造函数
Closure :: bind – 复制一个闭包,绑定指定的$ this对象和类作用域。
Closure:: bindTo – 复制当前闭包对象,绑定指定的$这个对象和类作用域。

除了此处列出的方法,还有一个__invoke方法。这是为了与其他实现了__invoke()魔术方法的对象保持一致性,但调用闭包对象的过程与它无关。

下面将介绍Closure :: bindClosure :: bindTo

关闭::绑定是封闭:: bindTo的静态版本,其说明如下:

public static Closure bind(Closure $ closure ,object $ newthis [,mixed $ newscope = 'static' ])

闭包表示需要绑定的闭包对象

  • newthis表示需要绑定到闭包对象的对象,或者NULL创建绑定的闭包
  • newscope表示想绑定给闭包的类作用域,可以传入类名或类的示例,默认值是’static’,表示不改变。

该方法成功时返回一个新的闭包对象,失败时返回FALSE。

例子说明:

/**  
 * 复制一个闭包,绑定指定的$this对象和类作用域。  
 *  
 * @author 疯狂老司机  
 */  
class Animal {  
    private static $cat = "cat";  
    private $dog = "dog";  
    public $pig = "pig";  
}  
  
/*  
 * 获取Animal类静态私有成员属性 
 */  
$cat = static function() {  
    return Animal::$cat;  
};  
  
/*  
 * 获取Animal实例私有成员属性 
 */  
$dog = function() {  
    return $this->dog;  
};  
  
/*  
 * 获取Animal实例公有成员属性 
 */  
$pig = function() {  
    return $this->pig;  
};  
  
$bindCat = Closure::bind($cat, null, new Animal());// 给闭包绑定了Animal实例的作用域,但未给闭包绑定$this对象  
$bindDog = Closure::bind($dog, new Animal(), 'Animal');// 给闭包绑定了Animal类的作用域,同时将Animal实例对象作为$this对象绑定给闭包  
$bindPig = Closure::bind($pig, new Animal());// 将Animal实例对象作为$this对象绑定给闭包,保留闭包原有作用域  
echo $bindCat(),'<br>';// 根据绑定规则,允许闭包通过作用域限定操作符获取Animal类静态私有成员属性  
echo $bindDog(),'<br>';// 根据绑定规则,允许闭包通过绑定的$this对象(Animal实例对象)获取Animal实例私有成员属性  
echo $bindPig(),'<br>';// 根据绑定规则,允许闭包通过绑定的$this对象获取Animal实例公有成员属性  
?>

输出:

cat
dog
pig

Closure::bindTo — 复制当前闭包对象,绑定指定的$this对象和类作用域,其说明如下:

public Closure Closure::bindTo (object $newthis [, mixed $newscope = 'static' ])

newthis表示绑定给闭包对象的一个对象,或者NULL来取消绑定。
newscope表示关联到闭包对象的类作用域,可以传入类名或类的示例,默认值是 ‘static’, 表示不改变。

该方法创建并返回一个闭包对象,它与当前对象绑定了同样变量,但可以绑定不同的对象,也可以绑定新的类作用域。绑定的对象决定了返回的闭包对象中的$this的取值,类作用域决定返回的闭包对象能够调用哪些方法,也就是说,此时$this可以调用的方法,与newscope类作用域相同。

例子1:

<?php  
function __autoload($class) {  
    require_once "$class.php";  
}  
  
$template = new Template;  
$template->render(new Article, 'tpl.php');  
?>

Template.php 模板类

<?php  
/**  
 * 模板类,用于渲染输出  
 *  
 * @author 疯狂老司机  
 */  
class Template{  
    /** 
     * 渲染方法 
     * 
     * @access public  
     * @param obj 信息类 
     * @param string 模板文件名 
     */  
    public function render($context, $tpl){  
        $closure = function($tpl){  
            ob_start();  
            include $tpl;  
            return ob_end_flush();  
        };  
        $closure = $closure->bindTo($context, $context);  
        $closure($tpl);  
    }  
  
}

Article.php 信息类

<?php  
/**  
 * 文章信息类  
 *  
 * @author 疯狂老司机  
 */  
class Article{  
    private $title = "这是文章标题";  
    private $content = "这是文章内容";  
}

tpl.php 模板文件

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">  
    <head>  
        <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">  
    </head>  
    <body>  
        <h1><?php echo $this->title;?></h1>  
        <p><?php echo $this->content;?></p>  
    </body>  
</html>

运行时确保以上文件位于同级目录。

输出:

这是文章标题
这是文章内容

例子2:

<?php  
/**  
 * 给类动态添加新方法 
 *  
 * @author 疯狂老司机 
 */  
trait DynamicTrait {  
  
    /** 
     * 自动调用类中存在的方法 
     */  
    public function __call($name, $args) {  
        if(is_callable($this->$name)){  
            return call_user_func($this->$name, $args);  
        }else{  
            throw new \RuntimeException("Method {$name} does not exist");  
        }  
    }  
    /** 
     * 添加方法 
     */  
    public function __set($name, $value) {  
        $this->$name = is_callable($value)?   
            $value->bindTo($this, $this):   
            $value;  
    }  
}  
  
/**  
 * 只带属性不带方法动物类 
 *  
 * @author 疯狂老司机 
 */  
class Animal {  
    use DynamicTrait;  
    private $dog = 'dog';  
}  
  
$animal = new Animal;  
  
// 往动物类实例中添加一个方法获取实例的私有属性$dog  
$animal->getdog = function() {  
    return $this->dog;  
};  
  
echo $animal->getdog();  
  
?>

输出:

dog

例子3:

<?php  
/**  
 * 一个基本的购物车,包括一些已经添加的商品和每种商品的数量 
 *  
 * @author 疯狂老司机 
 */  
class Cart {  
    // 定义商品价格  
    const PRICE_BUTTER  = 1.00;  
    const PRICE_MILK    = 3.33;  
    const PRICE_EGGS    = 8.88;  
   
    protected   $products = array();  
  
    /** 
     * 添加商品和数量 
     * 
     * @access public  
     * @param string 商品名称 
     * @param string 商品数量 
     */  
    public function add($item, $quantity) {  
        $this->products[$item] = $quantity;  
    }  
  
    /** 
     * 获取单项商品数量 
     * 
     * @access public  
     * @param string 商品名称 
     */  
    public function getQuantity($item) {  
        return isset($this->products[$item]) ? $this->products[$item] : FALSE;  
    }  
  
    /** 
     * 获取总价 
     * 
     * @access public  
     * @param string 税率 
     */  
    public function getTotal($tax) {  
        $total = 0.00;  
  
        $callback = function ($quantity, $item) use ($tax, &$total) {  
            $pricePerItem = constant(__CLASS__ . "::PRICE_" . strtoupper($item));  
            $total += ($pricePerItem * $quantity) * ($tax + 1.0);  
        };  
           
        array_walk($this->products, $callback);  
        return round($total, 2);;  
    }  
}  
  
$my_cart = new Cart;  
  
// 往购物车里添加商品及对应数量  
$my_cart->add('butter', 10);  
$my_cart->add('milk', 3);  
$my_cart->add('eggs', 12);  
  
// 打出出总价格,其中有 5% 的销售税.  
echo $my_cart->getTotal(0.05);  
  
?>

输出:

132.88

补充说明:闭包可以使用USE关键连接外部变量。

总结:合理使用闭包能使代码更加简洁和精炼。

Last modification:January 21, 2020
如果觉得我的文章对你有用,请随意赞赏