匿名函數(shù)(Anonymous functions),也叫閉包函數(shù)(closures
),允許
臨時(shí)創(chuàng)建一個(gè)沒有指定名稱的函數(shù)。最經(jīng)常用作回調(diào)函數(shù) callable參數(shù)的值。當(dāng)然,也有其它應(yīng)用的情況。
匿名函數(shù)目前是通過 Closure 類來實(shí)現(xiàn)的。
示例 #1 匿名函數(shù)示例
<?php
echo preg_replace_callback('~-([a-z])~', function ($match) {
return strtoupper($match[1]);
}, 'hello-world');
// 輸出 helloWorld
?>
閉包函數(shù)也可以作為變量的值來使用。PHP 會(huì)自動(dòng)把此種表達(dá)式轉(zhuǎn)換成內(nèi)置類 Closure 的對(duì)象實(shí)例。把一個(gè) closure 對(duì)象賦值給一個(gè)變量的方式與普通變量賦值的語法是一樣的,最后也要加上分號(hào):
示例 #2 匿名函數(shù)變量賦值示例
<?php
$greet = function($name)
{
printf("Hello %s\r\n", $name);
};
$greet('World');
$greet('PHP');
?>
閉包可以從父作用域中繼承變量。
任何此類變量都應(yīng)該用 use
語言結(jié)構(gòu)傳遞進(jìn)去。
PHP 7.1 起,不能傳入此類變量: superglobals、 $this 或者和參數(shù)重名。
返回類型聲明必須放在 use
子句的 后面 。
示例 #3 從父作用域繼承變量
<?php
$message = 'hello';
// 沒有 "use"
$example = function () {
var_dump($message);
};
$example();
// 繼承 $message
$example = function () use ($message) {
var_dump($message);
};
$example();
// 當(dāng)函數(shù)被定義而不是被調(diào)用的時(shí)候繼承變量的值
$message = 'world';
$example();
// 重置 message
$message = 'hello';
// 通過引用繼承
$example = function () use (&$message) {
var_dump($message);
};
$example();
// 父級(jí)作用域改變的值反映在函數(shù)調(diào)用中
$message = 'world';
$example();
// 閉包函數(shù)也可以接受常規(guī)參數(shù)
$example = function ($arg) use ($message) {
var_dump($arg . ' ' . $message);
};
$example("hello");
// 返回類型在 use 子句的后面
$example = function () use ($message): string {
return "hello $message";
};
var_dump($example());
?>
以上例程的輸出類似于:
Notice: Undefined variable: message in /example.php on line 6 NULL string(5) "hello" string(5) "hello" string(5) "hello" string(5) "world" string(11) "hello world" string(11) "hello world"
從 PHP 8.0.0 開始,作用域繼承的變量列表可能包含一個(gè)尾部的逗號(hào),這個(gè)逗號(hào)將被忽略。
這些變量都必須在函數(shù)或類的頭部聲明。 從父作用域中繼承變量與使用全局變量是不同的。全局變量存在于一個(gè)全局的范圍,無論當(dāng)前在執(zhí)行的是哪個(gè)函數(shù)。而 閉包的父作用域是定義該閉包的函數(shù)(不一定是調(diào)用它的函數(shù))。示例如下:
示例 #4 Closures 和作用域
<?php
// 一個(gè)基本的購(gòu)物車,包括一些已經(jīng)添加的商品和每種商品的數(shù)量。
// 其中有一個(gè)方法用來計(jì)算購(gòu)物車中所有商品的總價(jià)格,該方法使
// 用了一個(gè) closure 作為回調(diào)函數(shù)。
class Cart
{
const PRICE_BUTTER = 1.00;
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));
$total += ($pricePerItem * $quantity) * ($tax + 1.0);
};
array_walk($this->products, $callback);
return round($total, 2);
}
}
$my_cart = new Cart;
// 往購(gòu)物車?yán)锾砑訔l目
$my_cart->add('butter', 1);
$my_cart->add('milk', 3);
$my_cart->add('eggs', 6);
// 打出出總價(jià)格,其中有 5% 的銷售稅.
print $my_cart->getTotal(0.05) . "\n";
// 最后結(jié)果是 54.29
?>
示例 #5 自動(dòng)綁定 $this
<?php
class Test
{
public function testing()
{
return function() {
var_dump($this);
};
}
}
$object = new Test;
$function = $object->testing();
$function();
?>
以上例程會(huì)輸出:
object(Test)#1 (0) { }
當(dāng)在類的上下文中聲明時(shí),當(dāng)前的類會(huì)自動(dòng)與之綁定,使得
$this
在函數(shù)的作用域中可用。如果不需要當(dāng)前類的自動(dòng)綁定,可以使用
靜態(tài)匿名函數(shù) 替代。
匿名函數(shù)允許被定義為靜態(tài)化。這樣可以防止當(dāng)前類自動(dòng)綁定到它們身上,對(duì)象在運(yùn)行時(shí)也可能不會(huì)被綁定到它們上面。
示例 #6 試圖在靜態(tài)匿名函數(shù)中使用 $this
<?php
class Foo
{
function __construct()
{
$func = static function() {
var_dump($this);
};
$func();
}
};
new Foo();
?>
以上例程會(huì)輸出:
Notice: Undefined variable: this in %s on line %d NULL
示例 #7 試圖將對(duì)象綁定到靜態(tài)匿名函數(shù)
<?php
$func = static function() {
// function body
};
$func = $func->bindTo(new StdClass);
$func();
?>
以上例程會(huì)輸出:
Warning: Cannot bind an instance to a static closure in %s on line %d
注意: 可以在閉包中使用 func_num_args(),func_get_arg() 和 func_get_args()。