經過測試,require_once是一個效能低下的文法結構,當然,這個效能低下是相對於require而言的,本文闡述我們項目目前使用的require方式,通過實驗代碼證明其高效性,同時,描述我們在使用過程中遇到的問題,避免他人在同一個石頭上絆倒。
- require: 引入一個檔案,運行時編譯引入.
- require_once: 功能等同於require,只是當這個檔案被引用過後,不再編譯引入。
上面就是兩者的區別。可以看出,兩者的不同僅在於require_once有一個判斷是否已經引用過的機制。通過網路搜尋,可以看到很多關於require_once效能比require低很多的資料,這裡就不再做這個實驗。
我們項目中的做法是: 在每個檔案起始位置定義一個全域變數,require的時候,使用isset($xxxxxx) or require 'xxxxx.php';
這種做法有什麼不足呢?
全域變數以$xxx方式定義的時候,如果該檔案在函數內被require,該變數會被解析為函數的局部變數,而不是全域的,因此,函數內部的isset($xxx) or require 'xxx.php'這個文法結構會失效,帶來的結果當然是意料不到的,比如,類的重定義,方法的重定義等等。
前車之鑒,所以,全域變數的定義,請使用$GLOBALS['xxx'],require的時候,使用isset($GLOBALS['xxx']) or require 'xxx.php';,使用GLOBALS會比直接定義稍慢,但總比錯是要好很多的。
由於我們之前的全域變數是直接定義的,今天在和同事討論的過程中,想到另外一種寫法:
定義的位置仍然使用$xxx方式直接定義,require的方法中進行修改(檔案頭部定義的全域變數和檔案名稱是有關聯的)。
function ud_require($xxx) {global $$xxx; isset($$xxx) or require $xxx . '.php';}
這種方式使用了動態變數,經過和直接的GLOBALS方式比較,有兩個顯著缺點:
- 效能,由於動態變數的引入,比GLOBALS方式慢2倍左右。
- 無法解決間接引用問題,因為我們無法預知被間接引用的檔案名稱,也就無法用global去聲明那些被間接引用的檔案中定義的標記性全域變數了。
好了,下面是我對GLOBALS方式的require和require_once的測試:
require_requireonce.php
/n"; $start = microtime(true); while($j ++ < 1000000) test1('require_requireonce_require.php'); $end = microtime(true); echo "使用方法的isset or require方式: " . ($end - $start) . "
/n"; $start = microtime(true); while($k ++ < 1000000) test2(); $end = microtime(true); echo "require_once方式: " . ($end - $start) . "
/n"; ?>
require_requireonce_require.php (用於測試require的被引入檔案)
require_requireonce_requireonce.php (用於測試require_once的被引入檔案)
下面是測試的結果(單位: 秒):
- 不使用方法的isset or require方式: 0.22953701019287
- 使用方法的isset or require方式: 0.23866105079651
- require_once方式: 2.3119640350342
可以看出,不套一個方法的require速度是比使用方法的略快的,兩者速度都是require_once的10倍左右。
那麼,效能損耗究竟在哪裡呢?
上面require_requireone.php檔案中的test1方法中,我注釋了一句pathinfo($filename),因為,我本來意圖是使用檔案名稱不帶尾碼作為標記性的全域變數名的,但是,當我使用pathinfo之後,我發現這種方式的效能消耗和require_once基本一致了。因此,我在那裡單獨的加了一個pathinfo的調用,又做了測試,果然是pathinfo在搗鬼。所以,後面我就修改為了現在的版本,直接使用檔案名稱作為變數名,如果你害怕檔案名稱重複,那不妨加上路徑名...
猜測: 加上pathinfo之後,require和require_once的效能消耗基本一致,那我們是否可以猜測PHP內部對require_once的處理是基於它的呢?據說PHP5.3中對require_once做了顯著的最佳化,但是,我測試過程中使用的是PHP5.3.5版本,仍然能夠看到和require明顯的差距,難道只是比之前版本較大最佳化?這個倒還沒有測試....
嘗試把test1方法做了如下修改:isset($GLOBALS[substr($filename, 0, strlen($filename) - 4)]) or require $filename;
使用手動的字串截取,當然,截取是要耗時的,不過比pathinfo的版本是要好一點的。這次的測試結果是:
- 不使用方法的isset or require方式: 0.21035599708557
- 使用方法的isset or require方式: 0.92985796928406
- require_once方式: 2.3799331188202
對於require_once修改為isset or require方式,需要注意以下幾方面:
- 每個檔案頭部定義唯一的一個標記性變數,使用$GLOBALS['XXX'] = 1;的方式定義,並且,建議變數名是檔案名稱或帶路徑的檔案名稱(如果單獨的檔案名稱會重複)
- 定義一個自訂require方法:
function ud_require_once($filename) {isset($GLOBALS[$filename]) or require $filename;}
http://www.bkjia.com/PHPjc/752440.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/752440.htmlTechArticle經過測試,require_once是一個效能低下的文法結構,當然,這個效能低下是相對於require而言的,本文闡述我們項目目前使用的require方式,通...