PHP 性能優(yōu)化在中大型項(xiàng)目當(dāng)中是非常重要的一件事情。
PHP 性能優(yōu)化分為代碼級(jí)和配置級(jí)兩個(gè)部分异赫。
一、PHP 代碼級(jí)優(yōu)化建議
1)用單引號(hào)代替雙引號(hào)
因?yàn)?PHP 會(huì)在雙引號(hào)包含的字符串中搜索變量市袖,單引號(hào)不會(huì)洋腮。在單引號(hào)當(dāng)中,PHP 不會(huì)自動(dòng)搜索變量讼育、轉(zhuǎn)義字符等帐姻。因此效率上快很多。特別是在一些大內(nèi)容的字符串的情景下奶段,單引號(hào)性能更優(yōu)異饥瓷。
2)
$row['id']
的速度是$row[id]
的 7 倍。
在一些舊版不嚴(yán)格的 PHP 當(dāng)中痹籍,$row['id']
寫成 $row[id]
也是能正確解析的呢铆。新版這樣會(huì)提示 notice 錯(cuò)誤。
3)使用 for 對(duì)數(shù)組循環(huán)之前確定數(shù)組元素的長(zhǎng)度
雖然像 count蹲缠、strlen 這樣的操作時(shí)間復(fù)雜度是O(1)棺克,不會(huì)有太大的性能消耗。當(dāng)然避免每次都循環(huán)計(jì)算是一個(gè)良好的習(xí)慣线定。
4)unset 那些不用的變量及大數(shù)組娜谊。
大小項(xiàng)目當(dāng)中性能可能并不明顯。但是在大項(xiàng)目且并發(fā)量高的時(shí)候斤讥,變量占用的內(nèi)存如果不及時(shí)注銷回收纱皆,會(huì)導(dǎo)致內(nèi)存負(fù)載很高。
5)盡量使用偽函數(shù)(語言結(jié)構(gòu) )
常用的語言結(jié)構(gòu)有 echo周偎、isset抹剩、unset 等。它們比函數(shù)性能要高很多蓉坎。常見的 isset 替換掉 array_key_exists澳眷。如何判斷一個(gè)方法是語言結(jié)構(gòu)還是普通方法?
方式一:
<?php
var_dump(function_exists('echo'));
方式二:
// 在 PHP 命令行操作
$ php --rf echo
6)類靜態(tài)方法比普通類成員方法性能好蛉艾。
這個(gè)性能差異在舊版表現(xiàn)明顯钳踊,在 PHP 5.6+ 之后,這個(gè)差異并不是很明顯了勿侯。當(dāng)然拓瞪,靜態(tài)方法在調(diào)用的時(shí)候更易操作。
7)盡量避免使用魔術(shù)方法助琐。
通過對(duì)魔術(shù)方法測(cè)試魔術(shù)方法比普通的方法性能在壓力很高的環(huán)境下相差成倍呈現(xiàn)祭埂。
8)加載文件時(shí)盡量使用絕對(duì)路徑。
PHP 加載相對(duì)路徑會(huì)從可能的庫位置進(jìn)行嘗試加載∏穑可能的庫位置由 php.ini 配置文件當(dāng)中的 include_path
配置決定舌界。這里的路徑配置越多,那么相對(duì)路徑的加載就會(huì)越慢泰演。
9)函數(shù)代替正則表達(dá)式完成相同的功能呻拌。
與之相關(guān)的功能有:strtok、strstr睦焕、strpos藐握、str_replace、substr垃喊、explode猾普、implode。
10)盡量避免使用 @ 屏蔽錯(cuò)誤消息的做法缔御。
我們應(yīng)該使用使用 error_reporting(0) 關(guān)閉錯(cuò)誤抬闷,使用使用 error_log 把錯(cuò)誤寫入日志文件妇蛀。
11)盡量避免程序拋出 notice 錯(cuò)誤消息耕突。
錯(cuò)誤消息對(duì)性能影響很大。即使通過 error_reporting(0) 關(guān)閉了也不行评架。所以眷茁,我們要盡量避免錯(cuò)誤的產(chǎn)生。
12)能用 PHP 內(nèi)置方法解決就不要自己用代碼實(shí)現(xiàn)
內(nèi)置方法比自定義方法速度快纵诞。在自己實(shí)現(xiàn)之前上祈,請(qǐng)檢查 PHP 內(nèi)置方法已經(jīng)提供。
13)用盡量少的代碼實(shí)現(xiàn)功能浙芙。
很明顯登刺,代碼越長(zhǎng)不僅不易維護(hù),而且 PHP 詞法解析器與語法語法解析器會(huì)用更多的時(shí)間去翻譯嗡呼,挺累的纸俭。
14)自增/自減局部變量比全局變量快。
本身從變量所處內(nèi)存位置亦可知局部變量比全局變量要快南窗。
15)派生類中的方法運(yùn)行起來要快于在基類中定義的同樣方法揍很。
從 PHP 原理來說,首先從當(dāng)前類查找該方法万伤,找不到再找父類窒悔。假如此時(shí)當(dāng)前類是一個(gè)多級(jí)的子孫類。那么敌买,就會(huì)導(dǎo)致向上查找简珠。從而性能會(huì)下降。
16)并非要用類實(shí)現(xiàn)所有的數(shù)據(jù)結(jié)構(gòu)虹钮,數(shù)組也很有用聋庵。
類比對(duì)象會(huì)消耗更多的內(nèi)存荐操。很多人在 PHP 開發(fā)過程中會(huì)過度的像 Java 這種語言一樣面向?qū)ο蟆0岩恍?fù)雜參數(shù)的傳遞封裝成一個(gè)對(duì)象珍策。其實(shí)大多數(shù)情況下沒有必要托启。直接傳數(shù)據(jù)就很好。性能表現(xiàn)也非常好攘宙。
17)盡量少的進(jìn)行文件操作屯耸。
在任何語言下,對(duì)文件的操作都會(huì)對(duì)性能產(chǎn)生影響蹭劈。操作的文件越多性能越差疗绣。畢竟,最終性能都會(huì)反應(yīng)在系統(tǒng) IO 上铺韧。
18)盡量使用 file_get_contents/file_put_contents 代替 fopen/fget 等相關(guān)的文件讀寫函數(shù)多矮。除非需要對(duì)文件更精細(xì)的操作。
19)盡量少的對(duì)數(shù)據(jù)庫進(jìn)行 INSERT/UPDATE/DELETE 操作哈打。
眾所周知塔逃,INSERT/UPDATE/DELETE 對(duì)數(shù)據(jù)庫造成 IO 壓力。操作越頻繁料仗,壓力越大湾盗。通常在一個(gè)項(xiàng)目中 20% 是寫入操作,80% 是讀操作立轧。這就是所謂的二八原則格粪。
20)對(duì)函數(shù)/方法內(nèi) global 的變量,應(yīng)該用完就 unset 掉氛改。
global 的變量帐萎,unset 的時(shí)候只會(huì)在當(dāng)前函數(shù)或方法內(nèi)部被清除掉。并不會(huì)把全局的變量給清除胜卤。
21)能用緩存的地方盡量使用緩存疆导。
眾所周知,緩存可以幫助我們擋掉一部分調(diào)用數(shù)據(jù)庫以及邏輯運(yùn)算部分的工作瑰艘。所以是鬼,性能肯定是快快的。
22)"用 i+=1代替 i=i+1紫新。符合 c/c++ 的習(xí)慣均蜜,效率還高 "。
23)多維數(shù)組盡量不要循環(huán)嵌套賦值芒率。
翻譯過來就是不要在循環(huán)過程中動(dòng)態(tài)在數(shù)據(jù)內(nèi)部創(chuàng)建一個(gè)元素囤耳。然后,再次循環(huán)在的時(shí)候在這個(gè)元素內(nèi)部再動(dòng)態(tài)創(chuàng)建一個(gè)元素。
24)如不是特殊需要充择,參數(shù)傳遞都建議使用傳值而不是傳引用德玫。當(dāng)然,如果參數(shù)是很大的數(shù)組且需要修改時(shí)可以考慮引用傳遞椎麦。
二宰僧、PHP 配置級(jí)優(yōu)化建議
- 禁用高風(fēng)險(xiǎn) PHP 函數(shù)
disable_functions = 該選項(xiàng)可以設(shè)置哪些 PHP 函數(shù)是禁止使用的。PHP 中有一些函數(shù)的風(fēng)險(xiǎn)性還是相當(dāng)大的观挎,可以直接執(zhí)行一些系統(tǒng)級(jí)腳本命令琴儿。如果允許這些函數(shù)執(zhí)行,當(dāng) PHP 程序出現(xiàn)漏洞時(shí)嘁捷,損失是非常嚴(yán)重的造成!以下我們給出推薦的禁用函數(shù)設(shè)置:
disable_functions = phpinfo,passthru,exec,system,popen,chroot,escapeshellcmd,escapeshellarg,shell_exec,proc_open,proc_get_status
需注意:如果您的服務(wù)器中含有一些系統(tǒng)狀態(tài)檢測(cè)的 PHP 程序,則不要禁用shell_exec,proc_open,proc_get_status 等函數(shù)雄嚣。
- 優(yōu)化 PHP 腳本執(zhí)行時(shí)間
max_execution_time = 30
該選項(xiàng)設(shè)定 PHP 程序的最大執(zhí)行時(shí)間晒屎,如果一個(gè) PHP 腳本被請(qǐng)求,且該 PHP 腳本在 max_execution_time 時(shí)間內(nèi)沒能執(zhí)行完畢缓升,則 PHP 不再繼續(xù)執(zhí)行鼓鲁,直接給客戶端返回超時(shí)錯(cuò)誤。沒有特殊需要該選項(xiàng)可保持默認(rèn)設(shè)置 30秒仔沿。如果您的 PHP 腳本確實(shí)需要長(zhǎng)執(zhí)行時(shí)間則可以適當(dāng)增大該時(shí)間設(shè)置坐桩。
- 優(yōu)化 PHP 腳本處理內(nèi)存占用
memory_limit = 8M
該選項(xiàng)指定 PHP 腳本處理所能占用的最大內(nèi)存,默認(rèn)為 8MB封锉,如果您的服務(wù)器內(nèi)存為 1GB 以上,則該選項(xiàng)可以設(shè)置為 12MB 以獲得更快的 PHP 腳本處理效率膘螟。
如果此值設(shè)置過小成福,那么一些大型的耗時(shí)的框架,比如像 Laravel/wordpress 就會(huì)導(dǎo)致報(bào) "Fatal Error: Allowed memory size of xxxxxx bytes exhausted " 的錯(cuò)誤荆残。
- PHP上傳文件大小限制:
upload_max_filesize = 2M
該選項(xiàng)設(shè)定PHP所能允許最大上傳文件大小奴艾,默認(rèn)為 2MB。根據(jù)實(shí)際應(yīng)用需求内斯,可以適當(dāng)增大該設(shè)置蕴潦。一定要謹(jǐn)防此值設(shè)置過大,導(dǎo)致遭遇大文件上傳把服務(wù)器資源吃盡導(dǎo)致服務(wù)器癱瘓俘闯。
比如潭苞,我們?cè)O(shè)置此值允許最大上傳 1GB。一個(gè)普通用戶上傳一個(gè) 1GB 的文件需要 30 分鐘真朗。那么假如一個(gè)惡意用戶模擬 1000 個(gè)正常用戶上傳文件此疹。那么此時(shí)服務(wù)器同一時(shí)刻會(huì)面臨 1000 上傳與服務(wù)器對(duì)接。造成服務(wù)器 30 分鐘內(nèi)不能響應(yīng)其他正常請(qǐng)求。導(dǎo)致服務(wù)器陷入癱瘓之境蝗碎。
- Session存儲(chǔ)介質(zhì)
session.save_path
在小應(yīng)用當(dāng)中湖笨,通常 session 都存儲(chǔ)到服務(wù)器文件當(dāng)中。就是由此選項(xiàng)決定蹦骑。如果你用的是一些 PHP 框架慈省。那么,可能此值會(huì)在框架當(dāng)中被修改眠菇。
一般大型應(yīng)用會(huì)將 session 寫入到類似如 Redis 這樣的緩存當(dāng)中辫呻。以此降低 session 寫入/刪除時(shí)對(duì)服務(wù)器性能造成 IO 壓力。