高質(zhì)量PHP代碼的50個(gè)實(shí)用技巧必備(下)
接著上篇《高質(zhì)量PHP代碼的50個(gè)實(shí)用技巧必備(上)》繼續(xù)研究。
26. 避免直接寫SQL, 抽象之
不厭其煩的寫了太多如下的語句:
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';">$query = "INSERT INTO users(name , email , address , phone) VALUES('$name' , '$email' , '$address' , '$phone')"; $db->query($query); //call to mysqli_query()</span>
這不是個(gè)建壯的方案. 它有些缺點(diǎn):
- >>每次都手動轉(zhuǎn)義值
- >>驗(yàn)證查詢是否正確
- >>查詢的錯(cuò)誤會花很長時(shí)間識別(除非每次都用if-else檢查)
- >>很難維護(hù)復(fù)雜的查詢
因此使用函數(shù)封裝:
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';">function insert_record($table_name , $data) { foreach($data as $key => $value) { //mysqli_real_escape_string $data[$key] = $db->mres($value); } $fields = implode(',' , array_keys($data)); $values = "'" . implode("','" , array_values($data)) . "'"; //Final query $query = "INSERT INTO {$table}($fields) VALUES($values)"; return $db->query($query); } $data = array('name' => $name , 'email' => $email , 'address' => $address , 'phone' => $phone); insert_record('users' , $data);</span>
看到了嗎? 這樣會更易讀和擴(kuò)展. record_data 函數(shù)小心的處理了轉(zhuǎn)義。最大的優(yōu)點(diǎn)是數(shù)據(jù)被預(yù)處理為一個(gè)數(shù)組, 任何語法錯(cuò)誤都會被捕獲。該函數(shù)應(yīng)該定義在某個(gè)database類中, 你可以像 $db->insert_record這樣調(diào)用。查看本文, 看看怎樣讓你處理數(shù)據(jù)庫更容易。類似的也可以編寫update,select,delete方法. 試試吧.
27. 將數(shù)據(jù)庫生成的內(nèi)容緩存到靜態(tài)文件中
如果所有的內(nèi)容都是從數(shù)據(jù)庫獲取的, 它們應(yīng)該被緩存. 一旦生成了, 就將它們保存在臨時(shí)文件中. 下次請求該頁面時(shí), 可直接從緩存中取, 不用再查數(shù)據(jù)庫.
好處:
>>節(jié)約php處理頁面的時(shí)間, 執(zhí)行更快
>>更少的數(shù)據(jù)庫查詢意味著更少的mysql連接開銷
28. 在數(shù)據(jù)庫中保存session
基于文件的session策略會有很多限制. 使用基于文件的session不能擴(kuò)展到集群中, 因?yàn)閟ession保存在單個(gè)服務(wù)器中. 但數(shù)據(jù)庫可被多個(gè)服務(wù)器訪問, 這樣就可以解決問題.
在數(shù)據(jù)庫中保存session數(shù)據(jù), 還有更多好處:
>>處理username重復(fù)登錄問題. 同個(gè)username不能在兩個(gè)地方同時(shí)登錄.
>>能更準(zhǔn)備的查詢在線用戶狀態(tài).
29. 避免使用全局變量
- >>使用 defines/constants
- >>使用函數(shù)獲取值
- >>使用類并通過$this訪問
30. 在head中使用base標(biāo)簽
沒聽說過? 請看下面:
<head> <base > </head> <body> <img src="happy.jpg" /> </body> </html>
base 標(biāo)簽非常有用. 假設(shè)你的應(yīng)用分成幾個(gè)子目錄, 它們都要包括相同的導(dǎo)航菜單.
- www.domain.com/store/home.php
- www.domain.com/store/products/ipad.php
在首頁中, 可以寫:
<a href="home.php">Home</a> <a href="products/ipad.php">Ipad</a>
但在你的ipad.php不得不寫成:
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';"><a href="../home.php">Home</a> <a href="ipad.php">Ipad</a></span>
因?yàn)槟夸洸灰粯? 有這么多不同版本的導(dǎo)航菜單要維護(hù), 很糟糕啊。因此, 請使用base標(biāo)簽.
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';"><head> <base > </head> <body> <a href="home.php">Home</a> <a href="products/ipad.php">Ipad</a> </body> </html></span>
現(xiàn)在, 這段代碼放在應(yīng)用的各個(gè)目錄文件中行為都一致.
31. 永遠(yuǎn)不要將 error_reporting 設(shè)為 0
關(guān)閉不相的錯(cuò)誤報(bào)告. E_FATAL 錯(cuò)誤是很重要的.
<span style="color:#333333;font-family:'Helvetica, Arial, sans-serif';">ini_set('display_errors', 1); error_reporting(~E_WARNING & ~E_NOTICE & ~E_STRICT);</span>
32. 注意平臺體系結(jié)構(gòu)
integer在32位和64位體系結(jié)構(gòu)中長度是不同的. 因此某些函數(shù)如 strtotime 的行為會不同.
在64位的機(jī)器中, 你會看到如下的輸出.
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';">$ php -a Interactive shell php > echo strtotime("0000-00-00 00:00:00"); -62170005200 php > echo strtotime('1000-01-30'); -30607739600 php > echo strtotime('2100-01-30'); 4104930600</span>
但在32位機(jī)器中, 它們將是bool(false). 查看這里, 了解更多.
33. 不要過分依賴 set_time_limit
如果你想限制最小時(shí)間, 可以使用下面的腳本:
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';">set_time_limit(30); //Rest of the code</span>
高枕無憂嗎? 注意任何外部的執(zhí)行, 如系統(tǒng)調(diào)用,socket操作, 數(shù)據(jù)庫操作等, 就不在set_time_limits的控制之下.
因此, 就算數(shù)據(jù)庫花費(fèi)了很多時(shí)間查詢, 腳本也不會停止執(zhí)行. 視情況而定.
34. 使用擴(kuò)展庫
一些例子:
- >>mPDF — 能通過html生成pdf文檔
- >>PHPExcel — 讀寫excel
- >>PhpMailer — 輕松處理發(fā)送包含附近的郵件
- >>pChart — 使用php生成報(bào)表
使用開源庫完成復(fù)雜任務(wù), 如生成pdf, ms-excel文件, 報(bào)表等.
35. 使用MVC框架
是時(shí)候使用像 codeigniter 這樣的MVC框架了. MVC框架并不強(qiáng)迫你寫面向?qū)ο蟮拇a. 它們僅將php代碼與html分離.
- >>明確區(qū)分php和html代碼. 在團(tuán)隊(duì)協(xié)作中有好處, 設(shè)計(jì)師和程序員可以同時(shí)工作.
- >>面向?qū)ο笤O(shè)計(jì)的函數(shù)能讓你更容易維護(hù)
- >>內(nèi)建函數(shù)完成了很多工作, 你不需要重復(fù)編寫
- >>開發(fā)大的應(yīng)用是必須的
- >>很多建議, 技巧和hack已被框架實(shí)現(xiàn)了
36. 時(shí)常看看 phpbench
phpbench 提供了些php基本操作的基準(zhǔn)測試結(jié)果, 它展示了一些徽小的語法變化是怎樣導(dǎo)致巨大差異的.
查看php站點(diǎn)的評論, 有問題到IRC提問, 時(shí)常閱讀開源代碼, 使用Linux開發(fā).
37. 如何正確的創(chuàng)建一個(gè)網(wǎng)站的Index頁面
創(chuàng)建每一個(gè)網(wǎng)站時(shí),建立網(wǎng)站的index頁面是首要做的事情之一。如果你是一個(gè)PHP新手,在編寫index頁面時(shí)典型的做法是只對index頁面所需的內(nèi)容進(jìn)行編程,其它鏈接創(chuàng)建另一個(gè)頁面。不過,如果想學(xué)習(xí)一種更高效的方式來實(shí)現(xiàn)PHP編程,可以采用“index.php?page=home”模式,許多網(wǎng)站都在采用這種模式。
38. 使用Request Global Array抓取數(shù)據(jù)
實(shí)際上我們沒有任何理由使用$_GET和$_POST數(shù)組來抓取數(shù)值。$_REQUEST這個(gè)全局?jǐn)?shù)組能夠讓你獲取一個(gè)get或form請求。因此,多數(shù)情況下解析數(shù)據(jù)的更高效代碼大體如下:
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 0;
39. 利用var_dump進(jìn)行PHP代碼調(diào)試
如果你在尋找php調(diào)試技術(shù),我必須說var_dump應(yīng)該是你要找的目標(biāo)。在顯示php信息方面這個(gè)命令可以滿足你的所有需要。而調(diào)試代碼的多數(shù)情況與得到PHP中的數(shù)值有關(guān)。
40. PHP處理代碼邏輯,Smarty處理展現(xiàn)層
Smarty是一個(gè)使用PHP寫出來的模板PHP模板引擎,是目前業(yè)界最著名的PHP模板引擎之一。它分離了邏輯代碼和外在的內(nèi)容,提供了一種易于管理和使用的方法,用來將原本與HTML代碼混雜在一起PHP代碼邏輯分離。簡單的講,目的就是要使PHP程序員同前端人員分離,使程序員改變程序的邏輯內(nèi)容不會影響到前端人員的頁面設(shè)計(jì),前端人員重新修改頁面不會影響到程序的程序邏輯,這在多人合作的項(xiàng)目中顯的尤為重要。
41. 的確需要使用全局?jǐn)?shù)值時(shí),創(chuàng)建一個(gè)Config文件
動輒創(chuàng)建全局?jǐn)?shù)值是一種糟糕的做法,不過有時(shí)候?qū)嶋H情況的確又需要這么做。對于數(shù)據(jù)庫表或數(shù)據(jù)庫連接信息使用全局?jǐn)?shù)值是一個(gè)不錯(cuò)的想法,但不要在你的PHP代碼中頻繁使用全局?jǐn)?shù)值。另外,更好的一種做法是把你的全局變量存放在一個(gè)config.php文件中。
42. 如果未定義,禁止訪問!
如果你正確的創(chuàng)建了頁面,那么任何其他人沒有理由訪問index.php或home.php之外的index.php頁面。一旦index.php被訪問后,你可以通過獲得變量的方式來打開需要的頁面。你的index頁面應(yīng)該包含類似的以下代碼:
define('yourPage',1);
然后,其它頁面應(yīng)該包含:
if (!defined('yourPage')) die('Access Denied');
這么做的目的是防止直接訪問你的其它php頁面。這樣,任何試圖不通過index.php訪問其它網(wǎng)頁的人,將得到“訪問被拒絕”的消息。
43. 創(chuàng)建一個(gè)數(shù)據(jù)庫類
如果你正在進(jìn)行數(shù)據(jù)庫編程(在PHP中非常常見的任務(wù)),一個(gè)不錯(cuò)的想法是創(chuàng)建一個(gè)數(shù)據(jù)庫類來處理任何數(shù)據(jù)庫管理功能。示例代碼如下:
public function dbExec($query) { $result = $this->db->exec($query); if (PEAR::isError($result)) errorRedirect($result->getMessage(), true); else return $result; }
這個(gè)函數(shù)僅接收一個(gè)查詢語句并對其執(zhí)行。它還處理可能出現(xiàn)的任何錯(cuò)誤。你還可以在這兒包含審核代碼,不過我更喜歡使用一個(gè)類似的審核函數(shù):
// checks if arguments given are integer values not less than 0 - has multiple arguments function sanitizeInput() { $numargs = func_num_args(); $arg_list = func_get_args(); for ($i = 0; $i < $numargs; $i++) { if (!is_numeric($arg_list[$i]) || $arg_list[$i] < 0) errorRedirect("Unexpected variable value", true); } }
44. 一個(gè)php文件處理輸入,一個(gè)class.php文件處理具體功能
不讓代碼變得混亂的一個(gè)重要方法是:獲取用戶輸入后,將其重定向到其它函數(shù)來進(jìn)行處理。原理非常簡單,php文件獲得我們需要的任何輸入,然后將其執(zhí)行重定向到類文件中的一個(gè)函數(shù)。舉例來講,假設(shè)有一個(gè)類似“index.php?page=profile&action=display”的URL。由profile.php來檢索該網(wǎng)址并得到操作是“display”。然后使用一個(gè)簡單的switch函數(shù),我們來執(zhí)行真正的顯示函數(shù):
require_once PROJECTROOT.'libs/messages.class.php'; $message = new Message(); switch ($action) { case 'display': $message->display(); break; ...
如上所示,我使用了一個(gè)消息類,然后開始進(jìn)行switch檢查。$message只是被類中的調(diào)用函數(shù)使用的一個(gè)對象。
45. 了解你的SQL語句,并總是對其審查(Sanitize)
正如我以前所提到的,任何php網(wǎng)站中最重要的部分有99%的可能是數(shù)據(jù)庫。因此,你需要非常熟悉如何正確的使用sql。學(xué)會關(guān)聯(lián)表和更多高級技術(shù)。下面我將展示一個(gè)使用MySQL的函數(shù)示例,并使用本文第7條函數(shù)進(jìn)行審查。
private function getSentMessages($id) { $this->util->sanitizeInput($id); $pm_table = $GLOBALS['config']['privateMsg']; $users = $GLOBALS['config']['users']; $sql = "SELECT PM.*, USR.username as name_sender FROM $pm_table PM, $users USR WHERE id_sender = '$id' AND sender_purge = FALSE AND USR.id = PM.id_receiver AND is_read = TRUE ORDER BY date_sent DESC"; $result = $this->dbQueryAll($sql); return $result; }
首先,我們對用戶輸入進(jìn)行檢查(通過一個(gè)GET變量傳遞消息id),然后我們執(zhí)行我們的SQL命令。注意這兒SQL的用法。你需要了解如何使用別名和關(guān)聯(lián)表。
46. 當(dāng)你只需要一個(gè)對象時(shí),使用單例模式
在PHP中相當(dāng)常見的一種情形時(shí),我們只需要創(chuàng)建一個(gè)對象一次,然后在我們的整個(gè)程序中使用它。一個(gè)很好的例子就是smarty變量,一旦被初始化后就可以在任何地方使用。這種情形的一個(gè)很好實(shí)現(xiàn)方案就是單例模式。示例代碼如下:
function smartyObject() { if ($GLOBALS['config']['SmartyObj'] == 0) { $smarty = new SmartyGame(); $GLOBALS['config']['SmartyObj'] = $smarty; } else $smarty = $GLOBALS['config']['SmartyObj']; return $smarty; }
注意,我們擁有一個(gè)全局smarty變量(該示例中它在config.php中被初始化),如果它的值為0,我們將創(chuàng)建一個(gè)新smarty對象。否則,它意味著該對象已經(jīng)被創(chuàng)建,我們只需要返回它。
47. 關(guān)于PHP重定向
方法一:header("Location:index.php");
方法二:echo"<script>window.location=\"$PHP_SELF\";</script>";
方法三:echo"<METAHTTP-EQUIV=\"Refresh\"CONTENT=\"0;URL=index.php\">";
48. 獲取訪問者瀏覽器
functionbrowse_infor() { $browser="";$browserver=""; $Browsers=array("Lynx","MOSAIC","AOL","Opera","JAVA","MacWeb","WebExplorer","OmniWeb"); $Agent=$GLOBALS["HTTP_USER_AGENT"]; for($i=0;$i<=7;$i++) { if(strpos($Agent,$Browsers[$i])) { $browser=$Browsers[$i]; $browserver=""; } } if(ereg("Mozilla",$Agent)&&!ereg("MSIE",$Agent)) { $temp=explode("(",$Agent);$Part=$temp[0]; $temp=explode("/",$Part);$browserver=$temp[1]; $temp=explode("",$browserver);$browserver=$temp[0]; $browserver=preg_replace("/([\d\.]+)/","\1",$browserver); $browserver="$browserver"; $browser="NetscapeNavigator"; } if(ereg("Mozilla",$Agent)&&ereg("Opera",$Agent)) { $temp=explode("(",$Agent);$Part=$temp[1]; $temp=explode(")",$Part);$browserver=$temp[1]; $temp=explode("",$browserver);$browserver=$temp[2]; $browserver=preg_replace("/([\d\.]+)/","\1",$browserver); $browserver="$browserver"; $browser="Opera"; } if(ereg("Mozilla",$Agent)&&ereg("MSIE",$Agent)) { $temp=explode("(",$Agent);$Part=$temp[1]; $temp=explode(";",$Part);$Part=$temp[1]; $temp=explode("",$Part);$browserver=$temp[2]; $browserver=preg_replace("/([\d\.]+)/","\1",$browserver); $browserver="$browserver"; $browser="InternetExplorer"; } if($browser!="") { $browseinfo="$browser$browserver"; } else { $browseinfo="Unknown"; } return$browseinfo; } //調(diào)用方法$browser=browseinfo();直接返回結(jié)果
49.獲取訪問者操作系統(tǒng)
functionosinfo(){ $os=""; $Agent=$GLOBALS["HTTP_USER_AGENT"]; if(eregi('win',$Agent)&&strpos($Agent,'95')){ $os="Windows95"; } elseif(eregi('win9x',$Agent)&&strpos($Agent,'4.90')){ $os="WindowsME"; } elseif(eregi('win',$Agent)&&ereg('98',$Agent)){ $os="Windows98"; } elseif(eregi('win',$Agent)&&eregi('nt5\.0',$Agent)){ $os="Windows2000"; } elseif(eregi('win',$Agent)&&eregi('nt',$Agent)){ $os="WindowsNT"; } elseif(eregi('win',$Agent)&&eregi('nt5\.1',$Agent)){ $os="WindowsXP"; } elseif(eregi('win',$Agent)&&ereg('32',$Agent)){ $os="Windows32"; } elseif(eregi('linux',$Agent)){ $os="Linux"; } elseif(eregi('unix',$Agent)){ $os="Unix"; } elseif(eregi('sun',$Agent)&&eregi('os',$Agent)){ $os="SunOS"; } elseif(eregi('ibm',$Agent)&&eregi('os',$Agent)){ $os="IBMOS/2"; } elseif(eregi('Mac',$Agent)&&eregi('PC',$Agent)){ $os="Macintosh"; } elseif(eregi('PowerPC',$Agent)){ $os="PowerPC"; } elseif(eregi('AIX',$Agent)){ $os="AIX"; } elseif(eregi('HPUX',$Agent)){ $os="HPUX"; } elseif(eregi('NetBSD',$Agent)){ $os="NetBSD"; } elseif(eregi('BSD',$Agent)){ $os="BSD"; } elseif(ereg('OSF1',$Agent)){ $os="OSF1"; } elseif(ereg('IRIX',$Agent)){ $os="IRIX"; } elseif(eregi('FreeBSD',$Agent)){ $os="FreeBSD"; } if($os=='')$os="Unknown"; return$os; } //調(diào)用方法$os=os_infor();
50. 文件格式類
$mime_types=array( 'gif'=>'image/gif', 'jpg'=>'image/jpeg', 'jpeg'=>'image/jpeg', 'jpe'=>'image/jpeg', 'bmp'=>'image/bmp', 'png'=>'image/png', 'tif'=>'image/tiff', 'tiff'=>'image/tiff', 'pict'=>'image/x-pict', 'pic'=>'image/x-pict', 'pct'=>'image/x-pict', 'tif'=>'image/tiff', 'tiff'=>'image/tiff', 'psd'=>'image/x-photoshop', 'swf'=>'application/x-shockwave-flash', 'js'=>'application/x-javascript', 'pdf'=>'application/pdf', 'ps'=>'application/postscript', 'eps'=>'application/postscript', 'ai'=>'application/postscript', 'wmf'=>'application/x-msmetafile', 'css'=>'text/css', 'htm'=>'text/html', 'html'=>'text/html', 'txt'=>'text/plain', 'xml'=>'text/xml', 'wml'=>'text/wml', 'wbmp'=>'image/vnd.wap.wbmp', 'mid'=>'audio/midi', 'wav'=>'audio/wav', 'mp3'=>'audio/mpeg', 'mp2'=>'audio/mpeg', 'avi'=>'video/x-msvideo', 'mpeg'=>'video/mpeg', 'mpg'=>'video/mpeg', 'qt'=>'video/quicktime', 'mov'=>'video/quicktime', 'lha'=>'application/x-lha', 'lzh'=>'application/x-lha', 'z'=>'application/x-compress', 'gtar'=>'application/x-gtar', 'gz'=>'application/x-gzip', 'gzip'=>'application/x-gzip', 'tgz'=>'application/x-gzip', 'tar'=>'application/x-tar', 'bz2'=>'application/bzip2', 'zip'=>'application/zip', 'arj'=>'application/x-arj', 'rar'=>'application/x-rar-compressed', 'hqx'=>'application/mac-binhex40', 'sit'=>'application/x-stuffit', 'bin'=>'application/x-macbinary', 'uu'=>'text/x-uuencode', 'uue'=>'text/x-uuencode', 'latex'=>'application/x-latex', 'ltx'=>'application/x-latex', 'tcl'=>'application/x-tcl', 'pgp'=>'application/pgp', 'asc'=>'application/pgp', 'exe'=>'application/x-msdownload', 'doc'=>'application/msword', 'rtf'=>'application/rtf', 'xls'=>'application/vnd.ms-excel', 'ppt'=>'application/vnd.ms-powerpoint', 'mdb'=>'application/x-msaccess', 'wri'=>'application/x-mswrite', ); 5、php生成excel文檔 <? header("Content-type:application/vnd.ms-excel"); header("Content-Disposition:filename=test.xls"); echo"test1\t"; echo"test2\t\n"; echo"test1\t"; echo"test2\t\n"; echo"test1\t"; echo"test2\t\n"; echo"test1\t"; echo"test2\t\n"; echo"test1\t"; echo"test2\t\n"; echo"test1\t"; echo"test2\t\n"; ?> //改動相應(yīng)文件頭就可以輸出.doc.xls等文件格式了
以上就是本文的全部內(nèi)容,大家結(jié)合前一篇進(jìn)行深入學(xué)習(xí),一定會有所收獲。
相關(guān)文章
在php和MySql中計(jì)算時(shí)間差的方法詳解
這篇文章主要介紹了在php和MySql中計(jì)算時(shí)間差的方法詳解的相關(guān)資料,需要的朋友可以參考下2015-03-03php中使用ExcelFileParser處理excel獲得數(shù)據(jù)(可作批量導(dǎo)入到數(shù)據(jù)庫使用)
使用ExcelFileParser處理excel獲得數(shù)據(jù) 可以用作批量導(dǎo)入到數(shù)據(jù)庫使用,需要獲取excel數(shù)據(jù)的朋友可以參考下。2010-08-08php.ini save_handler 修改不生效的解決辦法
這篇文章主要介紹了php.ini save_handler 修改不生效的解決辦法,需要的朋友可以參考下2014-07-07為Plesk PHP7啟用Oracle OCI8擴(kuò)展方法總結(jié)
在本篇文章里小編給大家總結(jié)了關(guān)于為Plesk PHP7啟用Oracle OCI8擴(kuò)展方法和相關(guān)代碼,需要的朋友們學(xué)習(xí)下。2019-03-03