-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Closure 类
一 匿名类
1.1 概要
PHP 7 起支持匿名类
语法
<?php
$closure = new class {
};1.2 特性
- ① 可以继承(extends)其它类,实现(implements)接口,或使用 Trait
- ② 可以传递参数到匿名类的构造器
- ③ 普通类(Class)嵌套匿名类,匿名类无法直接访问
- protected 级别可见属性或方法,可以让匿名类继承普通类访问
- private 属性,可以通过匿名类的构造函数参数传递进匿名类
- ④ 声明同一个匿名类,所创建的对象都是这个匿名类实例
<?php
interface Wing
{
public function fly();
}
class Car
{
private $hasGasoline = true;
protected $wheels = 3;
protected function canDrive()
{
if ($this->hasGasoline) {
echo "Can drive.<br/>";
}
}
public function getWheels()
{
return $this->wheels;
}
public function mountWing()
{
return new class($this->hasGasoline) extends Car implements Wing{
public $hasGasolineClone = false;
public function __construct($hasGasoline)
{
$this->hasGasolineClone = $hasGasoline;
}
public function fly()
{
if ($this->hasGasolineClone) {
echo "I have wing and gasoline, i can fly." . PHP_EOL;
}
if ($this->wheels == 3) {
echo "Car just have 3 wheels need one more.". PHP_EOL;
++$this->wheels;
}
if ($this->wheels == 4) {
echo "Car have 4 wheels.". PHP_EOL;
}
}
};
}
}
function d($data)
{
echo "-- START --" . PHP_EOL;
var_dump($data);
echo "-- END --" . PHP_EOL;
}
$car = new Car();
d($car->getWheels());
$car->mountWing()->fly();
d($car->getWheels());二 Closure 类
名词解释:
- callback: 回调函数(PHP 4 引入)
- anonymous function: 匿名函数(PHP 5.3 引入)
- closure: 闭包(PHP 5.3 引入)
- callable: 可回调
在 PHP 4 最早稱 callback,PHP 5.3 引入 closure 與 anonymous function ,PHP 5.4 則新增 callable type hint。
所以在 PHP 中,callback、closure、anonymous function,與 callable,事實上指的是同一件事情,但因為底層用的都是 Closure 物件,通常統稱為 closure。
-- from 如何使用 Closure
2.1 匿名函数(anonymous function,或称闭包(closure))
2.1.1 概念
允许创建临时的没有指定函数名的函数
2.1.2 匿名函数用法
- 用作回调函数参数的值
- 用作变量的值
2.1.3 匿名函数原理
匿名函数是通过 closure 类实现的。
即在定义匿名函数时,php 内部会将匿名函数自动转换成为 closure 类的对象实例
2.1.4 匿名函数语法与特性
- 定义
<?php
function($message)
{
var_dump($message);
// 注意结束分号 ;
};- 继承父作用域变量
使用 use 语言结构传递(继承)父作用域变量(PHP 7.1 起,不能传递 superglobals, $this 或和匿名函数参数重名的变量)。
① 不使用 use
<?php
$greeting = "hello";
$greet = function(){
printf("%s world", $greeting);
};
$greet();
// PHP Notice: Undefined variable: greeting in /home/cg/root/main.php on line 5② 使用 use 传递(继承)父作用域变量
<?php
$greeting = "hello";
$greet = function() use ($greeting) {
printf("%s world", $greeting);
};
$greet();
// print: hello world③ 匿名函数获取父作用域变量的值,在定义变量时即确定,而非调用时确定
<?php
$greeting = "hello";
$greet = function() use ($greeting) {
printf("%s world", $greeting);
};
$greeting = "hi";// 赋新值
$greet();
//print: hello world④ 引用传递父作用域变量
<?php
$greeting = "hello";
$greet = function() use (&$greeting) {
printf("%s ", $greeting);
};
$greet();//print: hello
$greeting = "world";
$greet();//print: world- 使用 $this 伪引用
PHP 5.4 以后,在类中定义匿名函数,将自动将 $this 类的伪引用绑定到匿名函数,可在匿名函数内使用 $this。
<?php
class Test
{
public function testing()
{
return function() {
var_dump($this);
};
}
}
$object = new Test;
$function = $object->testing();
$function();
//object(Test)#1 (0) {
//}
} 闭包父作用域: 定义该闭包的函数,而非调用它的函数;所以,使用父作用域变量,需在定义匿名函数之前声明
- 静态匿名函数(static anonymous function)
定义
<?php
static function()
{
};静态匿名函数用户与普通匿名函数一致,区别在于类中定义静态匿名函数时,类不会将 $this 绑定到静态匿名函数
<?php
class Test
{
public function testing()
{
return static function() {
var_dump($this);
};
}
}
$object = new Test;
$function = $object->testing();
$function();
//PHP Fatal error: Uncaught Error: Using $this when not in object context in /home/cg/root/main.php:7
} 2.2 Closure 类
在 2.1.3 匿名函数原理一节中说过,所有的匿名函数在声明时会自动转换成 Closure 类的实例。
简言之,匿名函数就是Closure 类
<<?php
$anonymous = function() {
};
var_dump($anonymous instanceof Closure);
//print: bool(true)2.2.1 Closure 类方法
- __construct(): 用于禁止实例化一个 closure 类对象
- bind(Closure $closure, object $obj [, mixed $scope = "static"]): 绑定指定的 $obj 对象和和作用域( $score )到静态闭包( $closure )
- bindTo(object $obj [, mixed $scope = "static"]): 绑定指定的 $obj 对象和和作用域( $score )到非静态闭包( $closure )
- call(object
$obj [, mixed $ ... ]): 临时绑定 $obj 对象到闭包($closure),同时并以给定的参数执行闭包调用 - fromCallable(callable $callable)
2.2.2 Closure 类方法详解
bind 方法
bind 方法和 bindTo 方法,功能作用完全一样,唯一区别就是,bind 是 bindTo 方法的静态版本
功能: 是让 object $obj 对象能够在匿名函数作用域内使用
先上示例
<?php
class A {
private static $sfoo = 1;
private $ifoo = 2;
}
$cl1 = static function() {
return A::$sfoo;
};
$cl2 = function() {
return $this->ifoo;
};
$bcl1 = Closure::bind($cl1, null, 'A');
$bcl2 = Closure::bind($cl2, new A(), 'A');
echo $bcl1(), "\n";
echo $bcl2(), "\n";
?>示例详解:
① 示例组成: 实例中一共由三部分组成,类 A 声明、静态匿名函数 $cl1 和 非静态匿名函数 $cl2 声明、执行 Closure:: bind 绑定
② 类 A 用于声明静态属性 $sfoo 和非静态属性 $ifoo
<?php
class A {
private static $sfoo = 1;
private $ifoo = 2;
}③ 静态匿名函数 $cl1 函数体返回 A::$sfoo 的结果;匿名函数 $cl2 返回 $this->ifoo的结果
<?php
$cl1 = static function() {
return A::$sfoo;
};
$cl2 = function() {
return $this->ifoo;
};③ 执行 Closure:: bind 绑定
<?php
$bcl1 = Closure::bind($cl1, null, 'A');
$bcl2 = Closure::bind($cl2, new A(), 'A');- 通过
$bcl1 = Closure::bind($cl1, null, 'A');我们发现 - a. 第一个参数 $cl1 为 ② 中定义的静态匿名函数,而 $cl1 中使用了
A::$sfoo; - b. 即
A::$sfoo;对应了第三个参数 'A',表示 Closure::bind 操作,将类 A 的作用域绑定到了匿名行数 $cl1 - c. 而由于 $cl1 使用了类 A的静态方法,因而第二个参数为 null,表示无需实例化类 A 且仅能在 $cl1 函数中使用类 A 的静态方法及属性
- d. 提出相关代码示例
<?php
class A {
private static $sfoo = 1;
private $ifoo = 2;
}
$cl1 = static function() {
return A::$sfoo;
};
$bcl1 = Closure::bind($cl1, null, 'A');
echo $bcl1(), "\n";//print: 1
//---------------
// 更改第三个参数 'A' 为 'B',会提示报错 Class 'B' not found,可见 b 点观点正确
// 表示 Closure::bind 操作,将类 A 的作用域绑定到了匿名行数 $cl1
//$bcl2 = Closure::bind($cl1, null, 'B');
//echo $bcl2(), "\n";//Exception: PHP Warning: Class 'B' not found in /home/cg/root/main.php on line 14
//---------------
// 文档 http://php.net/manual/en/class.closure.php
// 有关 $newscrope = 'static' 的说明 The class scope to which associate the closure is to be associated, or 'static' to keep the current one
// 这个意思是说使用当前作用域的类(即 A)
// 但是执行时会报错 Uncaught Error: Cannot access private property A::$sfoo...
// 修改可见性为 public 且仅为public,在 'static' 作用域下才能执行,结果为 1
//$bcl3 = Closure::bind($cl1, null, 'static');
//echo $bcl3(), "\n";
?>- 通过
$bcl2 = Closure::bind($cl2, new A(), 'A');我们了解到 - a. 第一个参数由静态匿名函数 $cl1 变成了 非静态匿名函数 $cl2
- b. 由此,第二个参数由 null 变成了 new A();这是因为在非静态匿名函数 $cl2 内部使用了 $this,因此需要实例化类 A
<?php
class A {
private static $sfoo = 1;
private $ifoo = 2;
}
$cl2 = function() {
return $this->ifoo;
};
$bcl2 = Closure::bind($cl2, new A(), 'A');
echo $bcl2(), "\n";//print: 2
//$bcl3 = Closure::bind($cl2, new A(), 'static');
//echo $bcl3(), "\n";//当 A $ifoo 属性控制访问更改为 public 输出 2
?>bindTo 方法
<?php
// 实例功能同 $bcl1 = Closure::bind($cl1, null, 'A');
class A {
public static $sfoo = 1;
public $ifoo = 2;
}
$cl1 = static function () {
return A::$sfoo;
};
$bcl1 = $cl1->bindTo(null, A::class);
echo $bcl1();<?php
// 实例功能同 $bcl2 = Closure::bind($cl2, new A(), 'A');
class A {
public static $sfoo = 1;
public $ifoo = 2;
}
$cl1 = function () {
return $this->ifoo;
};
$bcl1 = $cl1->bindTo(new A(), A::class);
echo $bcl1();三 参考资料
匿名函数
Closure 类
Closure::bind
todo 深入探討 bindTo()
todo 如何使用 Closure?
https://stackoverflow.com/questions/39884308/closurebindto-how-its-work
深入理解PHP之匿名函数