項目開發過程中,經常遇到了一些PHP處理程序性能底下的情況,程序運行在centos+nginx環境,雖然這個有很多的原因如:服務器本身配置,運行環境nginx服務,php-fpm配置等等,更多有一點仍然是PHPer沒有對程序進行更好的優化。
第一類、變量類型:
1)主要是使用未定義變量,而直接使用變量,做程序開發結束的時候,程序員都喜歡關閉debug,讓一切Notice和Warning都影藏起來,但實質上php即使關閉debug也會記錄日志,將所有的Notice和Warning信息全部寫入日志文件中,無非是一件浪費性能的事,很多phper都有這個小習慣,包括我也一樣。
2)另外在程序中應盡量注意變量的使用,如字符串最好使用單引號,而不是雙引號,畢竟雙引號還得PHP解析為單引號在執行。
3)說到這里數組中的鍵值索引一樣也需要用單引號,不要不寫引號這樣的程序執行效率極低。
第二類、函數類型:
1)SESSION變量,大家都知道調用SESSION這個全局變量,往往大家在將值放入SESSION之后,直接就往下接著寫自己的代碼了,卻忘記了SESSION變量會在頁面執行完畢之后才會將值放入SESSION中供其他頁面使用,這樣如果這個頁面執行時間過長,其他頁面也調用這個SESSION就會等待這個寫入SESSION的頁面執行完畢之后,再往后執行其他程序,但是我們的程序要的不是這樣的效果,因為我們只需要將值寫入SESSION執行之后,就已經放在SESSION中了,而不是等待頁面執行完畢,從PHP官網也能看到這個SESSION變量確實有這個問題,因此我們需要在寫入SESSION之后關閉寫入SESSION操作,加上session_write_close()函數則可以減少等待頁面加載完畢的這些無用時間。
2)file_get_contents()函數,這個函數意思是獲取遠程URL的頁面內容,但雖然這樣寫著,很多人都不會注意這樣一個問題”超時”,如果獲取不到頁面內容,程序將一直卡在這里,很多人會聯系到設置頁面超時或者在php.ini文件中去設置max_execution_time最大執行超時時間,但如果使用的是php-fpm(也就是php-cgi)將對這個參數視為無效,而需要在php-fpm配置文件中設置最大執行超時時間,最終這樣也無法起到作用,需要解決這個問題,仍然需要我們在至調用該函數的時候加上超時時間,這樣才能從根本上解決問題。
第三類、引用文件類型:
引用文件很多時間我們都習慣性的使用include ,但是這中間也隱含著一些使用技巧,如果經常使用框架開發程序的人就很熟悉,項目項目下面會有”include”字樣類型的文件夾,程序在查找包含文件的順序先是在當前工作目錄根路徑下include字樣文件夾中下查找,然后再是當前該文件所在目錄相對的include字樣文件夾中查找。也就是這個include使用不當,程序將會按照這個方式在根路徑下面依次查找,這樣同樣是一件很費解的事,所以大家應盡量將使用的文件寫在最容易查找的位置,這樣才有利于程序的執行效率。此外還有像echo輸出多個字符串或變量的函數,使用”.”連接效率要比”,”連接執行效率低。
更詳細具體的總結如下:
1、用單引號代替雙引號來包含字符串,這樣做會更快一些。因為PHP會在雙引號包圍的字符串中搜尋變量, 單引號則不會,注意:只有echo能這么做,它是一種可以把多個字符串當作參數的”函數”(譯注:PHP手冊中說echo是語言結構,不是真正的函數,故 把函數加上了雙引號)。
2、如果能將類的方法定義成static,就盡量定義成static,它的速度會提升將近4倍。
3、$row[‘id’] 的速度是$row[id]的7倍。
4、echo 比 print 快,并且使用echo的多重參數(譯注:指用逗號而不是句點)代替字符串連接,比如echo $str1,$str2。
5、在執行for循環之前確定最大循環數,不要每循環一次都計算最大值,最好運用foreach代替。
6、注銷那些不用的變量尤其是大數組,以便釋放內存。
7、盡量避免使用__get,__set,__autoload。
8、require_once()代價昂貴。
9、include文件時盡量使用絕對路徑,因為它避免了PHP去include_path里查找文件的速度,解析操作系統路徑所需的時間會更少。
10、如果你想知道腳本開始執行(譯注:即服務器端收到客戶端請求)的時刻,使用$_SERVER[‘REQUEST_TIME’]要好于time()。
11、函數代替正則表達式完成相同功能。
12、str_replace函數比preg_replace函數快,但strtr函數的效率是str_replace函數的四倍。
13、如果一個字符串替換函數,可接受數組或字符作為參數,并且參數長度不太長,那么可以考慮額外寫一段替換代碼,使得每次傳遞參數是一個字符,而不是只寫一行代碼接受數組作為查詢和替換的參數。
14、使用選擇分支語句(譯注:即switch case)好于使用多個if,else if語句。
15、用@屏蔽錯誤消息的做法非常低效,極其低效。
16、打開apache的mod_deflate模塊,可以提高網頁的瀏覽速度。
17、數據庫連接當使用完畢時應關掉,不要用長連接。
18、錯誤消息代價昂貴。
19、在方法中遞增局部變量,速度是最快的。幾乎與在函數中調用局部變量的速度相當。
20、遞增一個全局變量要比遞增一個局部變量慢2倍。
21、遞增一個對象屬性(如:$this->prop++)要比遞增一個局部變量慢3倍。
22、遞增一個未預定義的局部變量要比遞增一個預定義的局部變量慢9至10倍。
23、僅定義一個局部變量而沒在函數中調用它,同樣會減慢速度(其程度相當于遞增一個局部變量)。PHP大概會檢查看是否存在全局變量。
24、方法調用看來與類中定義的方法的數量無關,因為我(在測試方法之前和之后都)添加了10個方法,但性能上沒有變化。
25、派生類中的方法運行起來要快于在基類中定義的同樣的方法。
26、調用帶有一個參數的空函數,其花費的時間相當于執行7至8次的局部變量遞增操作。類似的方法調用所花費的時間接近于15次的局部變量遞增操作。
27、Apache解析一個PHP腳本的時間要比解析一個靜態HTML頁面慢2至10倍。盡量多用靜態HTML頁面,少用腳本。
28、除非腳本可以緩存,否則每次調用時都會重新編譯一次。引入一套PHP緩存機制通常可以提升25%至100%的性能,以免除編譯開銷。
29、盡量做緩存,可使用memcached。memcached是一款高性能的內存對象緩存系統,可用來加速動態Web應用程序,減輕數據庫負載。對運算碼 (OP code)的緩存很有用,使得腳本不必為每個請求做重新編譯。
30、當操作字符串并需要檢驗其長度是否滿足某種要求時,你想當然地會使用strlen()函數。此函數執行起來相當快,因為它不做任何計算, 只返回在zval 結構(C的內置數據結構,用于存儲PHP變量)中存儲的已知字符串長度。但是,由于strlen()是函數,多多少少會有些慢,因為函數調用會經過諸多步 驟,如字母小寫化(譯注:指函數名小寫化,PHP不區分函數名大小寫)、哈希查找,會跟隨被調用的函數一起執行。在某些情況下,你可以使用isset() 技巧加速執行你的代碼。
(舉例如下)
if (strlen($foo) < 5) { echo “Foo is too short”$$ }
(與下面的技巧做比較)
if (!isset($foo{5})) { echo “Foo is too short”$$ }
調用isset()恰巧比strlen()快,因為與后者不同的是,isset()作為一種語言結構,意味著它的執行不需要函數查找和字母小寫化。也就是說,實際上在檢驗字符串長度的頂層代碼中你沒有花太多開銷。
31、當執行變量$i的遞增或遞減時,$i++會比++$i慢一些。這種差異是PHP特有的,并不適用于其他語言,所以請不要修改你的C或 Java代碼并指望它們能立即變快,沒用的。++$i更快是因為它只需要3條指令(opcodes),$i++則需要4條指令。后置遞增實際上會產生一個 臨時變量,這個臨時變量隨后被遞增。而前置遞增直接在原值上遞增。這是最優化處理的一種,正如Zend的PHP優化器所作的那樣。牢記這個優化處理不失為 一個好主意,因為并不是所有的指令優化器都會做同樣的優化處理,并且存在大量沒有裝配指令優化器的互聯網服務提供商(ISPs)和服務器。
32、并不是事必面向對象(OOP),面向對象往往開銷很大,每個方法和對象調用都會消耗很多內存。
33、并非要用類實現所有的數據結構,數組也很有用。
34、不要把方法細分得過多,仔細想想你真正打算重用的是哪些代碼?
35、當你需要時,你總能把代碼分解成方法。
36、盡量采用大量的PHP內置函數。
37、如果在代碼中存在大量耗時的函數,你可以考慮用C擴展的方式實現它們。
38、評估檢驗(profile)你的代碼。檢驗器會告訴你,代碼的哪些部分消耗了多少時間。Xdebug調試器包含了檢驗程序,評估檢驗總體上可以顯示出代碼的瓶頸。
39、mod_zip可作為Apache模塊,用來即時壓縮你的數據,并可讓數據傳輸量降低80%。
40、在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情況下,盡量用 file_get_contents,因為他的效率高得多!但是要注意file_get_contents在打開一個URL文件時候的PHP版本問題。
41、盡量的少進行文件操作,雖然PHP的文件操作效率也不低的。
42、優化Select SQL語句,在可能的情況下盡量少的進行Insert、Update操作(在update上,我被惡批過)。
43、盡可能的使用PHP內部函數(但是我卻為了找個PHP里面不存在的函數,浪費了本可以寫出一個自定義函數的時間,經驗問題啊!)。
44、循環內部不要聲明變量,尤其是大變量:對象(這好像不只是PHP里面要注意的問題吧?)。
45、多維數組盡量不要循環嵌套賦值。
46、在可以用PHP內部字符串操作函數的情況下,不要用正則表達式。
47、foreach效率更高,盡量用foreach代替while和for循環。
48、用單引號替代雙引號引用字符串。
49、用i+=1代替i=i+1。符合c/c++的習慣,效率高。
50、對global變量,應該用完就unset()掉。
上述都是一些最基本的程序優化問題。希望在以后少犯這樣的低級錯誤。
點擊了解更多去學習:非常使用的代碼優化,怎么才能寫好代碼
、多閱讀手冊和源代碼
沒什么比閱讀手冊更值得強調的事了–僅僅通過閱讀手冊你就可以學習到很多東西,特別是很多有關于字符串和數組的函數。就在這些函數里面包括許多有用 的功能,如果你仔細閱讀手冊,你會經常發現在以往的項目開發過程中,很多時候你在“重復發明輪子”,而實際上你只需要一個核心函數就可以完成相應的功能。 手冊是你的朋友。另外,現在有很多使用PHP開發的開源程序。為什么不去學習和借鑒呢?下載一份開源的PHP應用程序的源代碼,仔細閱讀它吧。也許越大的 項目越值得去閱讀,雖然它們也許有更復雜的結構和系統,但也有更詳細的解釋文檔。
2、編寫模塊化代碼
良好的PHP代碼應該是模塊化的代碼。PHP的面向對象的編程功能是一些特別強大的工具,可以把你的應用程序分解成函數或方法。你應該盡可能多的從 你的應用程序的服務器端分開前端的HTML/CSS/JavaScript代碼,你也可以在任何PHP框架上遵循MVC(模型-視圖-控制器)模式。
3、代碼編寫規范
良好的PHP代碼應該有一套完整的代碼編寫規范。通過對變量和函數的命名,統一的方法訪問數據庫和對錯誤的處理,以及同樣的代碼縮進方式等來達到編程規范,這樣可以使你的代碼更具可讀性。
4、編寫可移植代碼
良好的PHP代碼應該是可移植的。你可以使用php的現有功能,如魔術引號和短標簽。試著了解你的需求,然后通過適應PHP特性來編寫代碼讓代碼獨立、可移植。
5、編寫安全代碼
良好的PHP代碼應該是安全的。PHP5提供了出色的性能和靈活性。但是安全問題完全在于開發人員。對于一個專業的PHP開發人員來說,深入理解重 大安全漏洞是至關重要的,如:跨站點腳本(XSS)、跨站請求偽造(CSRF)、代碼注入漏洞、字符編碼漏洞。通過使用PHP的特殊功能和函數, 如:mysql_real_escape_string等等,你可以編寫出安全的代碼。
6、代碼注釋
代碼注釋是代碼的重要組成部分。通過代碼注釋可以知道該變量或函數是做什么的,這將在今后的代碼維護中十分有用。
7、使用單引號代替雙引號
字符串始終使用單引號代替雙引號,以避免PHP搜索字符串內的變量導致的性能下降。用單引號代替雙引號來包含字符串,這樣做會更快一些。因為PHP會在雙引號包圍的字符串中搜尋變量,單引號則不會
8、轉義字符串輸出
使用ENT_QUOTES作參數傳遞給htmlspecialchars函數,以確保單引號(‘)也轉換成HTML實體,這是一個好習慣。
9、使用逗號分隔字符串輸出
通過echo語句輸出使用逗號(,)分隔的字符串,要比使用字符串連接操作符(.)的性能更好。
10、輸出前檢查傳來的值
輸出前檢查傳過來的值$_GET[‘query’]。使用isset或empty函數,可以用來檢查變量是否為null值
. 如果一個方法可靜態化,就對它做靜態聲明,速度可以提高至4倍。
2. echo比print快,而且使用echo的多重參數代替字符串連接。
3. 在執行for循環之前確定最大的循環數,不要每循環一次都計算一下最大值。 for($i=0;$i<count($array);$i++){},不要每次循環都計算count中值。
4. 注銷那些不用的變量,尤其是大數組,以便釋放內存。
5. 盡量避免使用__get __set __autoload。
6. require_once() 代價昂貴。
7. 在包含文件時使用完全路徑,解析操作系統路徑所需的時間會更少。
8. 如果想要知道腳本開始執行的時刻,$_SERVER['REQUEST_TIME']要好于time()
9. 函數代替正則表達式完成相同功能。
10.strtr函數的效率比str_replace的效率要高4倍。
11.使用選擇分支語句switch case 要好于使用多個if,elseif
12.使用@屏蔽錯誤信息的做法非常低效。
13.數據庫連接使用完成的時候,要及時關閉。
14.$val['id']比$val[id]效率高很多。
15.遞增一個局部變量要比遞增一個局部變量慢很多,遞增一個對象屬性($this->prop++)要比遞增一個局部變量慢很多。
16.僅定義一個局部變量二沒在函數中調用它,同樣會減慢速度。 方法調用與類中定義的方法的數量無關。
17.派生類中的方法運行起來要快于在基類中定義的同樣的方法。
18.用單引號的效率會比雙引號快一些,因為PHP會在雙引號包圍的字符串中搜尋變量。
19.Apache解析一個PHP腳本的時間比解析一個HTML的頁面慢2倍至10倍,盡可能多的用靜態HTML腳本。
20. 如果一個方法可靜態化,就對它做靜態聲明。速率可提升至4倍。因此,在編寫代碼的過程中盡可能的把類及方法寫成靜態的,如果涉及構造方法初始數據時編寫一個inistand()方法。如
class Base
{
static function Foo ( $class=__CLASS__ )
{
call_user_func(array($class,'Bar'));
}
}
class Derived extends Base
{
static function Foo ( $class=__CLASS__ )
{
parent::Foo($class);
}
static function Bar ()
{
echo "ZRPHERO\r\n";
}
}
Derived::Foo(); // This time it works.
Derived::Bar();
21. echo 比 print 快。
22. 使用echo的多重參數(譯注:指用逗號而不是句點)代替字符串連接。
23. 在執行for循環之前確定最大循環數,不要每循環一次都計算最大值。
24. 注銷那些不用的變量尤其是大數組,以便釋放內存。
25.盡量避免使用__get,__set,__autoload。
26.檢查是否能用strncasecmp,strpbrk,stripos函數代替正則表達式完成相同功能。
27.require_once()代價昂貴。
28.在包含文件時使用完整路徑,解析操作系統路徑所需的時間會更少。
29.如果你想知道腳本開始執行的時刻,使用$_SERVER[‘REQUEST_TIME’]要好于time()。
30.遞增一個對象屬性(如:$this->prop++)要比遞增一個局部變量慢3倍。
31.str_replace函數比preg_replace函數快,但strtr函數的效率是str_replace函數的四倍。
32.如果一個字符串替換函數,可接受數組或字符作為參數,并且參數長度不太長,那么可以考慮額外寫一段替換代碼,使得每次傳遞參數是一個字符,而不是只寫一行代碼接受數組作為查詢和替換的參數。
33.用@屏蔽錯誤消息的做法非常低效。
34.數據庫連接當使用完畢時應關掉。
35.$row[‘id’]的效率是$row[id]的7倍。
36.錯誤消息代價昂貴。
37.盡量不要在for循環中使用函數,比如for ($x=0; $x < count($array); $x)每循環一次都會調用count()函數。
38.在方法中遞增局部變量,速度是最快的。幾乎與在函數中調用局部變量的速度相當。
39.遞增一個全局變量要比遞增一個局部變量慢2倍。
40.在config里盡量不要加入邏輯,如if …else 等這樣的操作。
41.遞增一個未預定義的局部變量要比遞增一個預定義的局部變量慢9至10倍。
42.在一個函數里如果只聲明一個全局變量而沒在函數中調用它,同樣會減慢速度(其程度相當于遞增一個局部變量)。PHP很有可能會檢查看是否存在全局變量。
43.方法調用看來與類中定義的方法的數量無關,因為我(在測試方法之前和之后都)添加了10個方法,但性能上沒有變化。
44.派生類中的方法運行起來要快于在基類中定義的同樣的方法。
45.調用帶有一個參數的空函數,其花費的時間相當于執行7至8次的局部變量遞增操作。類似的方法調用所花費的時間接近于15次的局部變量遞增操作。
46.用單引號代替雙引號來包含字符串,這樣做會更快一些。因為PHP會在雙引號包圍的字符串中搜尋變量,單引號則不會。當然,只有當你不需要在字符串中包含變量時才可以這么做。
47. 輸出多個字符串時,用逗號代替句點來分隔字符串,速度更快。注意:只有echo能這么做,它是一種可以把多個字符串當作參數的“函數”。
48. Apache解析一個PHP腳本的時間要比解析一個靜態HTML頁面慢2至10倍。盡量多用靜態HTML頁面,少用腳本。
49. 當操作字符串并需要檢驗其長度是否滿足某種要求時,你想當然地會使用strlen()函數。此函數執行起來相當快,因為它不做任何計算,只返回在zval 結構(C的內置數據結構,用于存儲PHP變量)中存儲的已知字符串長度。但是,由于strlen()是函數,多多少少會有些慢,因為函數調用會經過諸多步驟,如字母小寫化(譯注:指函數名小寫化,PHP不區分函數名大小寫)、哈希查找,會跟隨被調用的函數一起執行。在某些情況下,你可以使用isset() 技巧加速執行你的代碼。
舉例如下
if (strlen($foo) < 5) { echo “Foo is too short”; }
vs.(與下面的技巧做比較)
if (!isset($foo{5})) { echo “Foo is too short”; }
調用isset()恰巧比strlen()快,因為與后者不同的是,isset()作為一種語言結構,意味著它的執行不需要函數查找和字母小寫化。也就是說,實際上在檢驗字符串長度的頂層代碼中你沒有花太多開銷.運用isset() 也能解決掉我們的很多notice操作。也可以帶來性能上的提高。
50. 當執行變量$i的遞增或遞減時,$i++會比++$i慢一些。這種差異是PHP特有的,++$i更快是因為它只需要3條指令(opcodes),$i++則需要4條指令。后置遞增實際上會產生一個臨時變量,這個臨時變量隨后被遞增。而前置遞增直接在原值上遞增。這是最優化處理的一種,正如Zend的PHP優化器所作的那樣。牢記這個優化處理不失為一個好主意,因為并不是所有的指令優化器都會做同樣的優化處理,并且存在大量沒有裝配指令優化器的互聯網服務提供商(ISPs)和服務器。
51. 并不是事必面向對象(OOP),面向對象往往開銷很大,每個方法和對象調用都會消耗很多內存。
52. 并非要用類實現所有的數據結構,數組也很有用。
53. 不要把方法細分得過多,仔細想想你真正打算重用的是哪些代碼?
54. 當你需要時,你總能把代碼分解成方法。
55.盡量采用大量的PHP內置函數,別自己寫,因為在你調用自己寫的函數時會編譯才能執行,而內置函數直接調用。
56.如果在代碼中存在大量耗時的函數,你可以考慮用C擴展的方式實現它們,后繼會與孟光聊這方面的事情。
57.評估檢驗你的代碼。檢驗器會告訴你,代碼的哪些部分消耗了多少時間。Xdebug調試器包含了檢驗程序,評估檢驗總體上可以顯示出代碼的瓶頸。
58. include和require不管帶不帶once,他都會先執行并把執行后聽代碼返回給調用該語句的地方,因此減少不必要php代碼載入。
59.從MC取數據時,盡量進行批量處理,因為我們現在的大部份MC都是以緩存池有多個端口并利用環行HASH,他們會把KEY全部發到我們對應的池也里,先找最近的找到返回,如沒有再到下一個依次。所以我們不管是多個key還是一個都會走這樣的處理,所以在對于同一緩存池里的數據最好就是批量處理。
60.盡量把程序中的NOTICE處理掉,因為他會在沒有找到的時候會去include_path里,當前載入目錄,include的文件中找。這樣一來多了會影響我們的性能。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。