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