亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > PHP > 正文

PHP autoload與spl_autoload自動加載機制的深入理解

2020-03-22 18:24:55
字體:
來源:轉載
供稿:網友
PHP autoload機制詳解
(1) autoload機制概述
在使用PHP的OO模式開發系統時,通常大家習慣上將每個類的實現都存放在一個單獨的文件里,這樣會很容易實現對類進行復用,同時將來維護時也很便利。這 也是OO設計的基本思想之一。在PHP5之前,如果需要使用一個類,只需要直接使用include/require將其包含進來即可。下面是一個實際的例 子:
復制代碼 代碼如下:
/* Person.html' target='_blank'>class.php */
?php
class Person {
var $name, $age;
function __construct ($name, $age)
{
$this- name = $name;
$this- age = $age;
}
}
?
/* no_autoload.php */
?php
require_once (”Person.class.php”);
$person = new Person(”Altair”, 6);
var_dump ($person);
?

在這個例子中,no-autoload.php文件需要使用Person類,它使用了require_once將其包含,然后就可以直接使用Person類來實例化一個對象。

但 隨著項目規模的不斷擴大,使用這種方式會帶來一些隱含的問題:如果一個PHP文件需要使用很多其它類,那么就需要很多的require/include語 句,這樣有可能會造成遺漏或者包含進不必要的類文件。如果大量的文件都需要使用其它的類,那么要保證每個文件都包含正確的類文件肯定是一個噩夢。

PHP5為這個問題提供了一個解決方案,這就是類的自動裝載(autoload)機制。autoload機制可以使得PHP程序有可能在使用類時才自動包含類文件,而不是一開始就將所有的類文件include進來,這種機制也稱為lazy loading。
下面是使用autoload機制加載Person類的例子:
復制代碼 代碼如下:
/* autoload.php */
?php
function __autoload($classname) {
require_once ($classname . “class.php”);
}
$person = new Person(”Altair”, 6);
var_dump ($person);
?

通常PHP5在使用一個類時,如果發現這個類沒有加載,就會自動運行__autoload()函數,在這個函數中我們可以加載需要使用的類。在我們這個簡 單的例子中,我們直接將類名加上擴展名" title="擴展名" 擴展名”.class.php”構成了類文件名,然后使用require_once將其加載。從這個例子中,我們可以看出 autoload至少要做三件事情,第一件事是根據類名確定類文件名,第二件事是確定類文件所在的磁盤路徑(在我們的例子是最簡單的情況,類與調用它們的 PHP程序文件在同一個文件夾下),第三件事是將類從磁盤文件中加載到系統中。第三步最簡單,只需要使用include/require即可。要實現第一 步,第二步的功能,必須在開發時約定類名與磁盤文件的映射方法,只有這樣我們才能根據類名找到它對應的磁盤文件。

因此,當有大量的類文件要包含的時候,我們只要確定相應的規則,然后在__autoload()函數中,將類名與實際的磁盤文件對應起來,就可以實現 lazy loading的效果。從這里我們也可以看出__autoload()函數的實現中最重要的是類名與實際的磁盤文件映射規則的實現。

但現在問題來了,如果在一個系統的實現中,如果需要使用很多其它的類庫,這些類庫可能是由不同的開發人員編寫的,其類名與實際的磁盤文件的映射規則不盡相 同。這時如果要實現類庫文件的自動加載,就必須在__autoload()函數中將所有的映射規則全部實現,這樣的話__autoload()函數有可能 會非常復雜,甚至無法實現。最后可能會導致__autoload()函數十分臃腫,這時即便能夠實現,也會給將來的維護和系統效率帶來很大的負面影響。在 這種情況下,難道就沒有更簡單清晰的解決辦法了吧?答案當然是:NO! 在看進一步的解決方法之前,我們先來看一下PHP中的autoload機制是如何實現的。

(2) PHP的autoload機制的實現
我 們知道,PHP文件的執行分為兩個獨立的過程,第一步是將PHP文件編譯成普通稱之為OPCODE的字節碼序列(實際上是編譯成一個叫做 zend_op_array的字節數組),第二步是由一個虛擬機來執行這些OPCODE。PHP的所有行為都是由這些OPCODE來實現的。因此,為了研 究PHP中autoload的實現機制,我們將autoload.php文件編譯成opcode,然后根據這些OPCODE來研究PHP在這過程中都做了 些什么:
/* autoload.php 編譯后的OPCODE列表,是使用作者開發的OPDUMP工具
*/
復制代碼 代碼如下:
?php
// require_once (”Person.php”);

function __autoload ($classname) {
0 NOP
0 RECV 1
if (!class_exists($classname)) {
1 SEND_VAR !0
2 DO_FCALL ‘class_exists' [extval:1]
3 BOOL_NOT $0 = RES[~1]
4 JMPZ ~1, - 8
require_once ($classname. “.class.php”);
5 CONCAT !0, ‘.class.php' = RES[~2]
6 INCLUDE_OR_EVAL ~2, REQUIRE_ONCE
}
7 JMP - 8
}
8 RETURN null

$p = new Person('Fred', 35);
1 FETCH_CLASS ‘Person' = RES[:0]
2 NEW :0 = RES[$1]
3 SEND_VAL ‘Fred'
4 SEND_VAL 35
5 DO_FCALL_BY_NAME [extval:2]
6 ASSIGN !0, $1

var_dump ($p);
7 SEND_VAR !0
8 DO_FCALL ‘var_dump' [extval:1]
?

在 autoload.php的第10行代碼中我們需要為類Person實例化一個對象。因此autoload機制一定會在該行編譯后的opcode中有所體 現。從上面的第10行代碼生成的OPCODE中我們知道,在實例化對象Person時,首先要執行FETCH_CLASS指令。我們就從PHP對 FETCH_CLASS指令的處理過程開始我們的探索之旅。
通過查閱PHP的源代碼(我使用的是PHP 5.3alpha2版本)可以發現如下的調用序列:
ZEND_VM_HANDLER(109, ZEND_FETCH_CLASS, ) (zend_vm_def.h 1864行)
= zend_fetch_class (zend_execute_API.c 1434行)
= zend_lookup_class_ex (zend_execute_API.c 964行)
= zend_call_function(&fcall_info, &fcall_cache) (zend_execute_API.c 1040行)
在最后一步的調用之前,我們先看一下調用時的關鍵參數:
/* 設置autoload_function變量值為”__autoload” */
fcall_info.function_name = &autoload_function; // Ooops, 終于發現”__autoload”了

fcall_cache.function_handler = EG(autoload_func); // autoload_func !
zend_call_function 是Zend Engine中最重要的函數之一,其主要功能是執行用戶在PHP程序中自定義的函數或者PHP本身的庫函數。zend_call_function有兩個 重要的指針形參數fcall_info, fcall_cache,它們分別指向兩個重要的結構,一個是zend_fcall_info, 另一個是zend_fcall_info_cache。zend_call_function主要工作流程如下:如果 fcall_cache.function_handler指針為NULL,則嘗試查找函數名為fcall_info.function_name的函 數,如果存在的話,則執行之;如果fcall_cache.function_handler不為NULL,則直接執行 fcall_cache.function_handler指向的函數。

現在我們清楚了,PHP在實例化一個 對象時(實際上在實現接口,使用類常數或類中的靜態變量,調用類中的靜態方法時都會如此),首先會在系統中查找該類(或接口)是否存在,如果不存在的話就 嘗試使用autoload機制來加載該類。而autoload機制的主要執行過程為:
(1) 檢查執行器全局變量函數指針autoload_func是否為NULL。
(2) 如果autoload_func==NULL, 則查找系統中是否定義有__autoload()函數,如果沒有,則報告錯誤并退出。
(3) 如果定義了__autoload()函數,則執行__autoload()嘗試加載類,并返回加載結果。
(4) 如果autoload_func不為NULL,則直接執行autoload_func指針指向的函數用來加載類。注意此時并不檢查__autoload()函數是否定義。
真 相終于大白,PHP提供了兩種方法來實現自動裝載機制,一種我們前面已經提到過,是使用用戶定義的__autoload()函數,這通常在PHP源程序中 來實現;另外一種就是設計一個函數,將autoload_func指針指向它,這通常使用C語言在PHP擴展中實現。如果既實現了 __autoload()函數,又實現了autoload_func(將autoload_func指向某一PHP函數),那么只執行 autoload_func函數。

(3) SPL autoload機制的實現
SPL 是Standard PHP Library(標準PHP庫)的縮寫。它是PHP5引入的一個擴展庫,其主要功能包括autoload機制的實現及包括各種Iterator接口或類。 SPL autoload機制的實現是通過將函數指針autoload_func指向自己實現的具有自動裝載功能的函數來實現的。SPL有兩個不同的函數 spl_autoload, spl_autoload_call,通過將autoload_func指向這兩個不同的函數地址來實現不同的自動加載機制。

spl_autoload 是SPL實現的默認的自動加載函數,它的功能比較簡單。它可以接收兩個參數,第一個參數是$class_name,表示類名,第二個參 數$file_extensions是可選的,表示類文件的擴展名" title="擴展名" 擴展名,可以在$file_extensions中指定多個擴展名" title="擴展名" 擴展名,護展名之間用分號隔開即 可;如果不指定的話,它將使用默認的擴展名" title="擴展名" 擴展名.inc或.php。spl_autoload首先將$class_name變為小寫,然后在所有的 include path中搜索$class_name.inc或$class_name.php文件(如果不指定$file_extensions參數的話),如果找 到,就加載該類文件。你可以手動使用spl_autoload(”Person”, “.class.php”)來加載Person類。實際上,它跟require/include差不多,不同的它可以指定多個擴展名" title="擴展名" 擴展名。

怎 樣讓spl_autoload自動起作用呢,也就是將autoload_func指向spl_autoload?答案是使用 spl_autoload_register函數。在PHP腳本中第一次調用spl_autoload_register()時不使用任何參數,就可以將 autoload_func指向spl_autoload。

通過上面的說明我們知道,spl_autoload的功能比較簡單,而且它是在SPL擴展中實現的,我們無法擴充它的功能。如果想實現自己的更靈活的自動加載機制怎么辦呢?這時,spl_autoload_call函數閃亮登場了。
我 們先看一下spl_autoload_call的實現有何奇妙之處。在SPL模塊內部,有一個全局變量autoload_functions,它本質上是 一個HashTable,不過我們可以將其簡單的看作一個鏈表,鏈表中的每一個元素都是一個函數指針,指向一個具有自動加載類功能的函數。 spl_autoload_call本身的實現很簡單,只是簡單的按順序執行這個鏈表中每個函數,在每個函數執行完成后都判斷一次需要的類是否已經加載, 如果加載成功就直接返回,不再繼續執行鏈表中的其它函數。如果這個鏈表中所有的函數都執行完成后類還沒有加載,spl_autoload_call就直接 退出,并不向用戶報告錯誤。因此,使用了autoload機制,并不能保證類就一定能正確的自動加載,關鍵還是要看你的自動加載函數如何實現。

那 么自動加載函數鏈表autoload_functions是誰來維護呢?就是前面提到的spl_autoload_register函數。它可以將用戶定 義的自動加載函數注冊到這個鏈表中,并將autoload_func函數指針指向spl_autoload_call函數(注意有一種情況例外,具體是哪 種情況留給大家思考)。我們也可以通過spl_autoload_unregister函數將已經注冊的函數從autoload_functions鏈表 中刪除。

上節說過,當autoload_func指針非空時,就不會自動執行__autoload()函數 了,現在autoload_func已經指向了spl_autoload_call,如果我們還想讓__autoload()函數起作用應該怎么辦呢?當 然還是使用spl_autoload_register(__autoload)調用將它注冊到autoload_functions鏈表中。

現在回到第一節最后的問題,我們有了解決方案:根據每個類庫不同的命名機制實現各自的自動加載函數,然后使用spl_autoload_register分別將其注冊到SPL自動加載函數隊列中就可了。這樣我們就不用維護一個非常復雜的__autoload函數了。

(4) autoload效率問題及對策
使 用autoload機制時,很多人的第一反應就是使用autoload會降低系統效率,甚至有人干脆提議為了效率不要使用autoload。在我們了解了 autoload實現的原理后,我們知道autoload機制本身并不是影響系統效率的原因,甚至它還有可能提高系統效率,因為它不會將不需要的類加載到 系統中。

那么為什么很多人都有一個使用autoload會降低系統效率的印象呢?實際上,影響autoload機制效率本身恰恰是用戶設計的自動加載函數。如果它 不能高效的將類名與實際的磁盤文件(注意,這里指實際的磁盤文件,而不僅僅是文件名)對應起來,系統將不得不做大量的文件是否存在(需要在每個 include path中包含的路徑中去尋找)的判斷,而判斷文件是否存在需要做磁盤I/O操作,眾所周知磁盤I/O操作的效率很低,因此這才是使得autoload機 制效率降低的罪魁禍首!

因此,我們在系統設計時,需要定義一套清晰的將類名與實際磁盤文件映射的機制。這個規則越簡單越明確,autoload機制的效率就越高。

結論:autoload機制并不是天然的效率低下,只有濫用autoload,設計不好的自動裝載函數才會導致其效率的降低。
PHP教程

鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产精品国内视频| 久久免费精品视频| 亚洲福利在线视频| 精品国偷自产在线| 欧美日本高清视频| 精品一区电影国产| 久久久久国产精品www| 欧美大肥婆大肥bbbbb| 俺去了亚洲欧美日韩| 国产成人91久久精品| 国产精品久久久久久超碰| 2020国产精品视频| www.99久久热国产日韩欧美.com| 久久精品国产欧美亚洲人人爽| 精品国产乱码久久久久久天美| 精品偷拍一区二区三区在线看| 日韩h在线观看| 啊v视频在线一区二区三区| 久久成人18免费网站| 亚洲欧洲国产伦综合| 亚洲乱亚洲乱妇无码| 国产精品第一页在线| 国产欧美日韩中文字幕在线| 欧美性少妇18aaaa视频| 亚洲在线免费看| xvideos国产精品| 日本精品一区二区三区在线播放视频| 中文.日本.精品| 日本精品久久中文字幕佐佐木| 亚洲护士老师的毛茸茸最新章节| 亚洲精品视频久久| 久久综合五月天| 国产日韩欧美日韩| 日韩中文第一页| 成人免费自拍视频| 91av在线免费观看视频| 国产亚洲精品久久久久久| 亚洲精品成人免费| 久久婷婷国产麻豆91天堂| 日韩视频―中文字幕| 国产91精品黑色丝袜高跟鞋| 亚洲免费av网址| 欧美亚洲另类在线| 综合久久五月天| 欧美视频专区一二在线观看| 91久久国产综合久久91精品网站| 国产精品劲爆视频| 国产成人亚洲综合91精品| 国产亚洲精品久久久久久| 亚洲自拍偷拍福利| 国产极品jizzhd欧美| 欧美区在线播放| 日韩在线观看你懂的| 成人免费激情视频| 91夜夜未满十八勿入爽爽影院| 成人av色在线观看| 亚洲欧美激情一区| 欧美性猛交xxxx| 精品在线欧美视频| 亚洲最大的av网站| 日韩视频免费在线| 91亚洲精华国产精华| 51视频国产精品一区二区| 国产精品久久久久久久久久久久| 日韩精品中文字幕在线播放| 69久久夜色精品国产69| 国产精品香蕉在线观看| 欧美日韩不卡合集视频| 精品国产乱码久久久久久婷婷| 色青青草原桃花久久综合| 91亚洲国产成人精品性色| 日韩成人av在线播放| 国产主播喷水一区二区| 日韩av日韩在线观看| 亚洲性69xxxbbb| 久久久成人精品视频| 国产99久久精品一区二区永久免费| 久久精品一区中文字幕| 国产综合在线观看视频| 欧美精品精品精品精品免费| 欧美国产中文字幕| 亚洲国产精品一区二区三区| 97av在线视频免费播放| 中国人与牲禽动交精品| 国产一区二区三区中文| 国产精品爽黄69天堂a| 日韩av电影免费观看高清| 国产精品入口尤物| 日韩精品视频免费专区在线播放| 亚洲欧美日本伦理| 欧美黄色片在线观看| 日韩在线欧美在线国产在线| 少妇av一区二区三区| 91精品成人久久| 中文字幕最新精品| 亚洲一区精品电影| 国产精品91久久久久久| 亚洲激情视频网| 欧美视频在线观看免费网址| 亚洲欧美第一页| 欧美中文在线字幕| 2018日韩中文字幕| 国产精品久久久久久久久久久新郎| 亚洲国产精品va在看黑人| 日韩精品高清视频| 日韩一区二区在线视频| 欧美国产视频日韩| 午夜精品蜜臀一区二区三区免费| 国内精品久久久久久久久| 久久久久久久一区二区| 成人午夜在线影院| 午夜精品久久久久久99热| 亚洲欧美国产va在线影院| 深夜福利国产精品| 在线观看日韩www视频免费| 成人高清视频观看www| 国产婷婷色综合av蜜臀av| 国产在线观看不卡| 色婷婷综合久久久久中文字幕1| 久久久久久久网站| 国产一区二区三区中文| 69久久夜色精品国产69乱青草| 久久全国免费视频| 日韩中文字幕免费视频| 色偷偷88888欧美精品久久久| 亚洲电影第1页| 亚洲国产成人精品一区二区| 国产欧美va欧美va香蕉在线| 欧美激情一区二区三区高清视频| 久久综合亚洲社区| 日韩欧美在线看| 91爱爱小视频k| 国产女人精品视频| 午夜剧场成人观在线视频免费观看| 久久亚洲一区二区三区四区五区高| 欧美成人免费视频| 国产精品观看在线亚洲人成网| 中文字幕精品一区二区精品| 国产精品自产拍在线观看| 欲色天天网综合久久| 亚洲综合一区二区不卡| 亚洲欧美国产精品久久久久久久| 精品久久久久久国产91| 国产精品亚洲аv天堂网| 国产精品国语对白| 亚洲精品网站在线播放gif| 九色成人免费视频| 亚洲精品视频网上网址在线观看| 日本sm极度另类视频| 亚洲免费伊人电影在线观看av| 国产成人精品日本亚洲专区61| 97视频com| 亚洲日本中文字幕免费在线不卡| 中文在线资源观看视频网站免费不卡| 国内精久久久久久久久久人| 人体精品一二三区| 乱亲女秽乱长久久久| 欧美日本亚洲视频| 色999日韩欧美国产| 伊人伊人伊人久久| 九九热在线精品视频| 国产成人综合av| 久久影视电视剧凤归四时歌| 国产亚洲精品综合一区91|