PHP中的函數(shù)相關(guān)處理方法學(xué)習(xí)在很早之前,面向過程的時(shí)代,函數(shù)方法是這些面向過程語言中的一等公民。在進(jìn)步到面向?qū)ο笾?,函?shù)依然有著舉足輕重的地位,它在類中成為了方法,但本質(zhì)上,方法就是類的內(nèi)部的一個(gè)函數(shù)。一般地,我們會(huì)將類外部定義的 function 稱為函數(shù),而將類的內(nèi)部定義的 function 稱為方法。我們的 PHP 也是從面向過程語言發(fā)展成為面向?qū)ο笳Z言的一門編程語言,所以函數(shù)方法的支持也是非常全面的。今天我們學(xué)習(xí)的內(nèi)容,是和 PHP 的函數(shù)操作有關(guān)的一些函數(shù)方法,它們?yōu)槲覀儾僮骱瘮?shù)方法提供了許多方便有用的功能。 創(chuàng)建匿名函數(shù)首先是創(chuàng)建一個(gè)匿名函數(shù)的方法,當(dāng)然,之前的許多文章中我們也經(jīng)常使用匿名函數(shù)來演示代碼,不過今天介紹的這個(gè)與之前的匿名函數(shù)創(chuàng)建的方式略有不同。
create_function() 方法就是用于創(chuàng)建一個(gè)函數(shù),它的第一個(gè)參數(shù)是創(chuàng)建的函數(shù)的參數(shù),第二個(gè)參數(shù)是函數(shù)體內(nèi)部的內(nèi)容。從代碼中可以看出,這種形式創(chuàng)建函數(shù)非常地不直觀,所以現(xiàn)在這個(gè) create_function() 方法已經(jīng)被標(biāo)記為過時(shí)的,并在 PHP8 中不推薦使用了。更好地方式當(dāng)然是直接地寫我們的匿名函數(shù)就可以啦。
函數(shù)參數(shù)操作函數(shù)的參數(shù)操作也是非常常見的應(yīng)用。
func_get_args() 用于獲取全部的參數(shù),func_num_args() 用于獲取參數(shù)的數(shù)量,func_get_arg() 用于獲取指定位置的參數(shù)。PHP 和 Java 等靜態(tài)語言的不同,除了變量類型之外,函數(shù)參數(shù)的不固定性也是其特點(diǎn)之一。這一點(diǎn)即有好處,也有壞處。比如說好處在于我們可以靈活地定義使用方法的參數(shù),就像測(cè)試代碼中的這個(gè) test() 一樣。我們?cè)诙x函數(shù)體的時(shí)候,只寫了兩個(gè)參數(shù),但是我們是可以給它傳遞更多的參數(shù)的。多出來的參數(shù)就必須使用 func_get_arg() 來獲取參數(shù)的值了,不能也沒辦法使用別的方式獲取這個(gè)參數(shù)的內(nèi)容。而壞處就是靈活過頭了就會(huì)缺少契約的限制,從而導(dǎo)致在面向?qū)ο笾械姆椒ǘ鄳B(tài)這一特性在 PHP 中無法簡單的實(shí)現(xiàn),因?yàn)榉椒ǘ鄳B(tài)的一個(gè)重要應(yīng)用就是方法的重載,對(duì)于 Java 等語言來說,改變參數(shù)就可以實(shí)現(xiàn)方法的多種狀態(tài)的重載,而 PHP 中是沒有這種能力的。關(guān)于這方面的內(nèi)容,我們?cè)谥暗奈恼?PHP中的“重載”是個(gè)啥?https://mp.weixin.qq.com/s/oYFp4PEqQu0Wx5Uo-Xnhew 中也講過如何用 PHP 來實(shí)現(xiàn)方法重載多態(tài)的效果,大家可以參考一下。 判斷函數(shù)是否存在判斷一個(gè)函數(shù)是否已經(jīng)定義,或者說在當(dāng)前的運(yùn)行環(huán)境中是否存在,是很多業(yè)務(wù)和框架開發(fā)中非常常用的功能。
function_exists() 函數(shù)就是用于這個(gè)判斷的,相信不用過多的解釋了,和 class_exists() 這些都是類似的。不僅能夠判斷我們自定義的函數(shù)方法是否存在,也可以判斷一些系統(tǒng)及擴(kuò)展相關(guān)的函數(shù)是否存在。比如在很多 CMS 開源系統(tǒng)的初始化檢測(cè)中,就一定會(huì)用到這類函數(shù)來檢測(cè)當(dāng)前運(yùn)行環(huán)境是否符合系統(tǒng)的需求。 獲得全部已定義的函數(shù)通過一個(gè)函數(shù),就可以獲得整個(gè)系統(tǒng)環(huán)境中全部可以使用的方法名稱,感覺怎么樣,是不是很爽?
從輸出的結(jié)果來看,系統(tǒng)或者擴(kuò)展自帶的函數(shù),會(huì)放在 internal 下面,我們當(dāng)前的系統(tǒng)環(huán)境中有 1543 個(gè)函數(shù)。當(dāng)然,這個(gè)數(shù)量是根據(jù)我們不同的 PHP 版本以及安裝的不同的擴(kuò)展會(huì)有差別的。而我們自己定義的函數(shù)則會(huì)在 user 下面展示出來。 匿名回調(diào)方式調(diào)用函數(shù)接下來就到了我們的重頭戲部分,使用匿名回調(diào)的方式來調(diào)用一個(gè)函數(shù)。先看看怎么用,最后我們?cè)僬f說具體的應(yīng)用場(chǎng)景。
沒錯(cuò),如果上面的說明沒看明白的話,看到這個(gè) call_user_func() 函數(shù)或許不少人就明白了。這個(gè)函數(shù)也是很多面試中喜歡出現(xiàn)的一個(gè)函數(shù),因?yàn)樗鋵?shí)是一個(gè)很神奇的函數(shù)。在測(cè)試代碼中,我們可以看到,使用 call_user_func() 方法可以調(diào)用執(zhí)行一個(gè)函數(shù),感覺就和我們正常調(diào)用這個(gè)函數(shù)沒什么區(qū)別呀?別急,我們最后再說這個(gè)問題。 繼續(xù)來看看它的使用。
調(diào)用類中的方法,或者直接使用匿名函數(shù)都是可以的。如果是類中的方法的話,第一個(gè)參數(shù)需要是一個(gè)數(shù)組,這個(gè)數(shù)組中的第一個(gè)元素是類的名稱,第二個(gè)元素是類中的方法名稱。call_user_func() 中第二個(gè)以及后面的參數(shù)是可以有 N 多個(gè)的,它們代表的是傳遞給回調(diào)函數(shù)參數(shù)的參數(shù)值。 當(dāng)然,我們還有更方便的一個(gè)函數(shù)可以傳遞參數(shù)值。
或許更多的人會(huì)對(duì)這個(gè) call_user_func_array() 更熟悉。而在面試中,也會(huì)有面試官問 call_user_func() 和 call_user_func_array() 的區(qū)別,這時(shí)就不要犯迷糊了,它們的區(qū)別就是對(duì)于參數(shù)的傳遞方式的不同,call_user_func_array() 的參數(shù)直接是以一個(gè)數(shù)組傳遞給回調(diào)函數(shù)的。
在回調(diào)函數(shù)的操作中,傳遞的參數(shù)值也可以是引用形式的,這一點(diǎn)其實(shí)和普通的函數(shù)調(diào)用是沒有什么區(qū)別的。 調(diào)用靜態(tài)函數(shù)除了上面調(diào)用普通的函數(shù)以及方法外,PHP 中還提供了調(diào)用靜態(tài)函數(shù)方法當(dāng)做回調(diào)函數(shù)的功能。
從功能上來看 forward_static_call() 和前面介紹的 call_user_func() 其實(shí)沒什么太大的區(qū)別。而從使用角度看,forward_static_call() 必須是在類的方法中使用的,它可以調(diào)用類外部的函數(shù)。注意,這個(gè) forward_static_call() 方法完全不能在類的外部使用的,直接就會(huì)報(bào)錯(cuò)。 既然說到了靜態(tài)方法,那么靜態(tài)方法的一些特別情況也是我們需要關(guān)注的,比如后期靜態(tài)綁定的一些問題。
幸好 forward_static_call() 對(duì)于后期靜態(tài)綁定的支持是沒有問題的。在上面的測(cè)試代碼中,注意到我們輸出的 static::name ,使用 forward_static_call() 和 call_user_func() 打印出來的結(jié)果是不同的哦。關(guān)于這方面的內(nèi)容,可以參考之前文章 后期靜態(tài)綁定在PHP中的使用https://mp.weixin.qq.com/s/N0rlafUCBFf3kZlRy5btYA 中的講解。 同樣地 forward_static_call() 也有一個(gè) forward_static_call_array() 方法與之相對(duì)應(yīng),也是傳遞參數(shù)的方式不同。
講完了函數(shù)的使用,接下來我們就好好講一講這些函數(shù)的真實(shí)作用。對(duì)于一個(gè)編程語言來說,過早地初始化暫時(shí)還不需要的內(nèi)容是對(duì)資源的極大浪費(fèi)。而在早期來說,我們?cè)诰幾g啟動(dòng)運(yùn)行一個(gè)應(yīng)用之后,所有的變量、方法、類對(duì)象都會(huì)加載到內(nèi)容中,致使應(yīng)用占用的空間非常龐大。而回調(diào)這種能力的出現(xiàn),大大緩解的這個(gè)問題。為什么這么說呢?回調(diào)就像是事件一樣,觸發(fā)才執(zhí)行,也就是說,我們可以在框架中定義好許多類、函數(shù)、方法,但是只有真正在執(zhí)行到對(duì)應(yīng)的功能時(shí),才實(shí)例化并調(diào)用它們。這個(gè)可以參考 Laravel 中的服務(wù)容器以及路由的實(shí)現(xiàn)。大家可以在 vendor/laravel/framework/src/Illuminate/Routing/Controller.php 看到 callAction() 方法中正是使用了 call_user_func_array() 來實(shí)現(xiàn)路由中控制器的加載的。其實(shí)你在現(xiàn)在比較流行的任意一個(gè)框架的 vendor 目錄中搜索 call_user_func 都可以看到非常多地使用這一系列功能函數(shù)的地方。 綜上所述,call_user_func() 相關(guān)的這幾個(gè)函數(shù),在框架應(yīng)用中非常廣泛,也是我們向更高層次邁進(jìn)所應(yīng)該深入學(xué)習(xí)的內(nèi)容。 在 PHP 中止時(shí)運(yùn)行一個(gè)回調(diào)函數(shù)最后,我們?cè)賮砜匆粋€(gè)非常簡單的函數(shù),也是我們?cè)谟涗浵到y(tǒng)運(yùn)行信息時(shí)非常常用的一個(gè)函數(shù)。
register_shutdown_function() 函數(shù)就是注冊(cè)一個(gè)在 PHP 結(jié)束運(yùn)行時(shí)所執(zhí)行的回調(diào)函數(shù)。不管是整個(gè)腳本執(zhí)行完成還是調(diào)用了 exit 或者 die 結(jié)束運(yùn)行之后,這個(gè)函數(shù)中定義的回調(diào)函數(shù)都會(huì)被執(zhí)行。 總結(jié)今天學(xué)習(xí)的內(nèi)容最核心的地方就在于 call_user_func() 相關(guān)函數(shù)的應(yīng)用,大家掌握好了嗎?其它的比較有用的包括 function_exists() 和 func_get_arg() 這類的函數(shù)也是非常常見的,也是大家要好好了解掌握的內(nèi)容。另外還有兩個(gè)函數(shù) register_tick_function() 以及 unregister_tick_function() ,這兩個(gè)函數(shù)的使用場(chǎng)景非常少,也不多見,大家可以參考之前的文章 PHP沒有定時(shí)器?https://mp.weixin.qq.com/s/NIYwhVLRl0drIcRvIoWvJA 中的相關(guān)講解說明。 測(cè)試代碼: https://github.com/zhangyue0503/dev-blog/blob/master/php/2021/04/source/7.PHP%E4%B8%AD%E7%9A%84%E5%87%BD%E6%95%B0%E7%9B%B8%E5%85%B3%E5%A4%84%E7%90%86%E6%96%B9%E6%B3%95%E5%AD%A6%E4%B9%A0.php 參考文檔: https://www./manual/zh/book.funchand.php |
|