Php中方法作用域,PHP 函数(下):匿名函数和作用域
PHP 函數(shù)(下):匿名函數(shù)和作用域
由 學(xué)院君 創(chuàng)建于9個(gè)月前, 最后更新于 7個(gè)月前
版本號(hào) #2
2282 views
1 likes
1 collects
匿名函數(shù)
從 PHP 5.3 開(kāi)始,引入了對(duì)匿名函數(shù)的支持,所謂匿名函數(shù)就是在函數(shù)定義中沒(méi)有顯式聲明函數(shù)名,在 PHP 中,匿名函數(shù)也被稱(chēng)作閉包函數(shù)(Closure)。
編寫(xiě)匿名函數(shù)
我們?cè)?php_learning/function 目錄下創(chuàng)建 closure.php 來(lái)存放本篇教程編寫(xiě)的代碼。 以上篇教程演示的自定義函數(shù) add 為例,如果通過(guò)匿名函數(shù)進(jìn)行定義,就是這樣的:
上面第一個(gè)紅色方框里面是匿名函數(shù)的定義部分,可以看到在 function 之后沒(méi)有聲明函數(shù)名,而是將整個(gè)函數(shù)賦值給了 $add 變量(不要漏掉賦值語(yǔ)句最后的分號(hào)),這樣,$add 就變成了函數(shù)類(lèi)型,也因此,函數(shù)在 PHP 中也可以看作是一等公民(first class),可以賦值給變量進(jìn)行調(diào)用,此時(shí),如果我們?cè)噲D通過(guò) var_dump($add) 打印 $add,結(jié)果如下:
可以看到它的類(lèi)型是用于代表匿名函數(shù)的 Closure 類(lèi),并且該匿名函數(shù)支持兩個(gè)必填參數(shù) $a 和 $b。
回到 closure.php,在上述截圖的第二個(gè)紅色方框區(qū)域是匿名函數(shù)的調(diào)用部分,我們可以直接將 $add 作為一個(gè)函數(shù)名進(jìn)行調(diào)用,打印結(jié)果是:
1 + 2 = 3
此外,還可以通過(guò) PHP 內(nèi)置的 call_user_func 函數(shù)調(diào)用該函數(shù),第一個(gè)參數(shù)是函數(shù)名,后面的參數(shù)是函數(shù)參數(shù)(非匿名函數(shù)亦可通過(guò) call_user_func 函數(shù)調(diào)用):
$sum = call_user_func($add, $a, $b);
返回結(jié)果和上面的 $add($a, $b) 完全一致。
可變數(shù)量的參數(shù)列表
如果感興趣的話,看 call_user_func 函數(shù)的聲明:
function call_user_func ($function, ...$parameter)
可以看到代表參數(shù)列表的 $parameter 前面有一個(gè) ... 前綴,其作用是標(biāo)識(shí)該參數(shù)是一個(gè)可變數(shù)量的參數(shù)列表,也就是支持傳入任意多個(gè)參數(shù),從 0~N 個(gè)不等,比如我們這里傳入的就是 $a 和 $b 兩個(gè)參數(shù),如果待調(diào)用函數(shù) $function 不需要傳遞參數(shù),則 $parameter 部分留空,如果只需要傳入一個(gè)參數(shù),則傳入一個(gè)參數(shù),依此類(lèi)推。
默認(rèn)參數(shù)
說(shuō)到這里,我們還可以為函數(shù)設(shè)置默認(rèn)參數(shù),即為指定參數(shù)設(shè)置默認(rèn)值,需要注意的是默認(rèn)參數(shù)需要放到參數(shù)列表最后:
$add = function (int $a, int $b = 2): int {
return $a + $b;
};
這個(gè)時(shí)候,調(diào)用 $add 函數(shù)就可以不傳入第二個(gè)參數(shù)了,該參數(shù)會(huì)使用默認(rèn)參數(shù)值:
$n1 = 1;
$n2 = 2;
$sum = $add($n1);
echo "$n1 + $n2 = $sum" . PHP_EOL;
當(dāng)然,你可以可以傳入第二個(gè)參數(shù)覆蓋默認(rèn)值:
$n1 = 1;
$n2 = 3;
$sum = $add($n1, $n2);
echo "$n1 + $n2 = $sum" . PHP_EOL;
這樣打印的結(jié)果就變成了:
1 + 3 = 4
可變函數(shù)
最后,由于 $add 是一個(gè)函數(shù)類(lèi)型變量,并且 PHP 是動(dòng)態(tài)類(lèi)型語(yǔ)言,所以我們還可以像操作基本類(lèi)型變量那樣將其他函數(shù)類(lèi)型值賦值給 $add,這些函數(shù)類(lèi)型值包括匿名函數(shù)和非匿名函數(shù),比如我們新增一個(gè)兩數(shù)相乘函數(shù) multi,然后在運(yùn)行時(shí)將其賦值給 $add:
注意第二個(gè)紅色方框,我們?cè)谶\(yùn)行時(shí)將 multi 函數(shù)賦值給 $add,再調(diào)用 $add($n1, $n2) 則等同于調(diào)用 multi($n1, $n2),當(dāng)然如果通過(guò)匿名函數(shù)定義 multi 也是可以的,對(duì)應(yīng)的實(shí)現(xiàn)代碼如下:
/**
* 通過(guò)匿名函數(shù)定義兩數(shù)相加函數(shù) add
* @param int $a
* @param int $b
* @return int
*/
$add = function (int $a, int $b = 2): int {
return $a + $b;
};
/**
* 兩數(shù)相乘函數(shù) multi
* @param int $a
* @param int $b
* @return int
*/
$multi = function (int $a, int $b): int {
return $a * $b;
};
// 調(diào)用匿名函數(shù)
$n1 = 1;
$n2 = 3;
$sum = $add($n1, $n2);
echo "$n1 + $n2 = $sum" . PHP_EOL;
// 將 multi 賦值給 $add
$add = $multi;
$product = $add($n1, $n2);
echo "$n1 x $n2 = $product" . PHP_EOL;
打印結(jié)果都是一樣的:
這種在運(yùn)行時(shí)動(dòng)態(tài)設(shè)置函數(shù)類(lèi)型值給變量的功能,在 PHP 中稱(chēng)之為可變函數(shù)。
作用域
繼承父作用域變量
匿名函數(shù)(或者叫閉包函數(shù))的一個(gè)強(qiáng)大功能是支持在函數(shù)體中直接引用上下文變量(繼承父作用域的變量),比如在上述代碼中,我們可以這樣編寫(xiě)匿名函數(shù)實(shí)現(xiàn)代碼:
$n1 = 1;
$n2 = 3;
// 計(jì)算兩數(shù)相加
$add = function () use ($n1, $n2) {
return $n1 + $n2;
};
// 計(jì)算兩數(shù)相乘
$multi = function () use ($n1, $n2){
return $n1 * $n2;
};
// 調(diào)用匿名函數(shù)
$sum = $add();
echo "$n1 + $n2 = $sum" . PHP_EOL;
$product = $multi();
echo "$n1 x $n2 = $product" . PHP_EOL;
只需要通過(guò) use 關(guān)鍵字傳遞當(dāng)前上下文中的變量,它們就可以在閉包函數(shù)體中直接使用,而不需要通過(guò)參數(shù)形式傳入,這樣一來(lái),其他引用該文件的代碼就可以間接引用當(dāng)前父作用域下的變量,如果是在類(lèi)方法中定義的匿名函數(shù),則可以直接引用相應(yīng)類(lèi)實(shí)例的屬性,關(guān)于這一塊,學(xué)院君會(huì)在后續(xù)面向?qū)ο缶幊讨性敿?xì)介紹。
通過(guò) global 聲明全局變量
如果不是通過(guò)匿名函數(shù)的話,只能基于 global 關(guān)鍵字通過(guò)全局變量引用函數(shù)體外部定義的變量:
// 計(jì)算兩數(shù)相減
function sub() {
global $n1, $n2;
return $n1 - $n2;
}
global vs. 匿名函數(shù)
從父作用域中繼承變量與使用全局變量是不同的,全局變量存在于一個(gè)全局的范圍,無(wú)論當(dāng)前在執(zhí)行的是哪個(gè)函數(shù),而閉包的父作用域是定義該閉包的函數(shù),不一定是調(diào)用它的函數(shù)。
我們編寫(xiě)一段示例代碼來(lái)詳細(xì)解釋:
function add1($n1, $n2) {
return function () use ($n1, $n2) {
return $n1 + $n2;
};
}
function add2() {
return function () {
global $n1, $n2, $n3;
return $n1 + $n2 + $n3;
};
}
$n1 = 1;
$n2 = 3;
$n3 = 4;
$add = add1($n1, $n2);
$sum = $add();
echo "$n1 + $n2 = $sum" . PHP_EOL;
$add = add2();
$sum = $add();
echo "$n1 + $n2 + $n3 = $sum" . PHP_EOL;
在上述代碼中,add1 中定義的閉包函數(shù)通過(guò) use 引用了父作用域下的 $n1 和 $n2 變量,對(duì)于該閉包函數(shù)來(lái)說(shuō),其作用域是 add1 函數(shù),而非調(diào)用它的位置,所以如果我們?cè)噲D在 add1 中定義的閉包函數(shù)中通過(guò) use 引用 $n3 會(huì)報(bào)錯(cuò)。
而 add2 中定義的閉包函數(shù)通過(guò) global 關(guān)鍵字以全局變量的方式引用 $n1、$n2 和 $n3,全局變量存在于全局范圍,與調(diào)用位置無(wú)關(guān),所以可以成功引用。
上述代碼的執(zhí)行結(jié)果是:
global 的安全隱患
但實(shí)際編碼中,盡量避免使用 global 關(guān)鍵字,因?yàn)橐坏┞暶髁巳肿兞?#xff0c;就可以在任何位置獲取到這些全局變量,非常容易導(dǎo)致系統(tǒng)被攻擊,比如我們新增一個(gè)函數(shù) test,在這個(gè)函數(shù)內(nèi)部就可以試圖通過(guò) $GLOBALS 獲取對(duì)應(yīng)全局變量:
function test() {
printf("n1 = %d, n2 = %d, n3 = %d\n", $GLOBALS['n1'], $GLOBALS['n2'], $GLOBALS['n3']);
}
匿名函數(shù)則有效規(guī)避了這種安全隱患。此外,匿名函數(shù)的另一個(gè)典型應(yīng)用場(chǎng)景就是兜底處理(fallback),關(guān)于這一點(diǎn),學(xué)院君將在作業(yè)項(xiàng)目中演示。
總結(jié)
以上是生活随笔為你收集整理的Php中方法作用域,PHP 函数(下):匿名函数和作用域的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: java发布后功能不能用,急项目发布后j
- 下一篇: php 大图找小图,点击小图弹出大图,点