接著上篇《高質量PHP代碼的50個實用技巧必備(上)》繼續研究。
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>
這不是個建壯的方案. 它有些缺點:
因此使用函數封裝:
<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>
看到了嗎? 這樣會更易讀和擴展. record_data 函數小心的處理了轉義。最大的優點是數據被預處理為一個數組, 任何語法錯誤都會被捕獲。該函數應該定義在某個database類中, 你可以像 $db->insert_record這樣調用。查看本文, 看看怎樣讓你處理數據庫更容易。類似的也可以編寫update,select,delete方法. 試試吧.
27. 將數據庫生成的內容緩存到靜態文件中
如果所有的內容都是從數據庫獲取的, 它們應該被緩存. 一旦生成了, 就將它們保存在臨時文件中. 下次請求該頁面時, 可直接從緩存中取, 不用再查數據庫.
好處:
>>節約php處理頁面的時間, 執行更快
>>更少的數據庫查詢意味著更少的mysql連接開銷
28. 在數據庫中保存session
基於文件的session策略會有很多限制. 使用基於文件的session不能擴展到集群中, 因為session保存在單個服務器中. 但數據庫可被多個服務器訪問, 這樣就可以解決問題.
在數據庫中保存session數據, 還有更多好處:
>>處理username重復登錄問題. 同個username不能在兩個地方同時登錄.
>>能更准備的查詢在線用戶狀態.
29. 避免使用全局變量
30. 在head中使用base標簽
沒聽說過? 請看下面:
<head> <base href="http://www.domain.com/store/"> </head> <body> <img src="happy.jpg" /> </body> </html>
base 標簽非常有用. 假設你的應用分成幾個子目錄, 它們都要包括相同的導航菜單.
在首頁中, 可以寫:
<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>
因為目錄不一樣. 有這麼多不同版本的導航菜單要維護, 很糟糕啊。因此, 請使用base標簽.
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';"><head> <base href="http://www.domain.com/store/"> </head> <body> <a href="home.php">Home</a> <a href="products/ipad.php">Ipad</a> </body> </html></span>
現在, 這段代碼放在應用的各個目錄文件中行為都一致.
31. 永遠不要將 error_reporting 設為 0
關閉不相的錯誤報告. E_FATAL 錯誤是很重要的.
<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. 注意平台體系結構
integer在32位和64位體系結構中長度是不同的. 因此某些函數如 strtotime 的行為會不同.
在64位的機器中, 你會看到如下的輸出.
<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位機器中, 它們將是bool(false). 查看這裡, 了解更多.
33. 不要過分依賴 set_time_limit
如果你想限制最小時間, 可以使用下面的腳本:
<span style="color:#333333;font-family:''Helvetica, Arial, sans-serif'';">set_time_limit(30); //Rest of the code</span>
高枕無憂嗎? 注意任何外部的執行, 如系統調用,socket操作, 數據庫操作等, 就不在set_time_limits的控制之下.
因此, 就算數據庫花費了很多時間查詢, 腳本也不會停止執行. 視情況而定.
34. 使用擴展庫
一些例子:
使用開源庫完成復雜任務, 如生成pdf, ms-excel文件, 報表等.
35. 使用MVC框架
是時候使用像 codeigniter 這樣的MVC框架了. MVC框架並不強迫你寫面向對象的代碼. 它們僅將php代碼與html分離.
36. 時常看看 phpbench
phpbench 提供了些php基本操作的基准測試結果, 它展示了一些徽小的語法變化是怎樣導致巨大差異的.
查看php站點的評論, 有問題到IRC提問, 時常閱讀開源代碼, 使用Linux開發.
37. 如何正確的創建一個網站的Index頁面
創建每一個網站時,建立網站的index頁面是首要做的事情之一。如果你是一個PHP新手,在編寫index頁面時典型的做法是只對index頁面所需的內容進行編程,其它鏈接創建另一個頁面。不過,如果想學習一種更高效的方式來實現PHP編程,可以采用“index.php?page=home”模式,許多網站都在采用這種模式。
38. 使用Request Global Array抓取數據
實際上我們沒有任何理由使用$_GET和$_POST數組來抓取數值。$_REQUEST這個全局數組能夠讓你獲取一個get或form請求。因此,多數情況下解析數據的更高效代碼大體如下:
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : 0;
39. 利用var_dump進行PHP代碼調試
如果你在尋找php調試技術,我必須說var_dump應該是你要找的目標。在顯示php信息方面這個命令可以滿足你的所有需要。而調試代碼的多數情況與得到PHP中的數值有關。
40. PHP處理代碼邏輯,Smarty處理展現層
Smarty是一個使用PHP寫出來的模板PHP模板引擎,是目前業界最著名的PHP模板引擎之一。它分離了邏輯代碼和外在的內容,提供了一種易於管理和使用的方法,用來將原本與HTML代碼混雜在一起PHP代碼邏輯分離。簡單的講,目的就是要使PHP程序員同前端人員分離,使程序員改變程序的邏輯內容不會影響到前端人員的頁面設計,前端人員重新修改頁面不會影響到程序的程序邏輯,這在多人合作的項目中顯的尤為重要。
41. 的確需要使用全局數值時,創建一個Config文件
動辄創建全局數值是一種糟糕的做法,不過有時候實際情況的確又需要這麼做。對於數據庫表或數據庫連接信息使用全局數值是一個不錯的想法,但不要在你的PHP代碼中頻繁使用全局數值。另外,更好的一種做法是把你的全局變量存放在一個config.php文件中。
42. 如果未定義,禁止訪問!
如果你正確的創建了頁面,那麼任何其他人沒有理由訪問index.php或home.php之外的index.php頁面。一旦index.php被訪問後,你可以通過獲得變量的方式來打開需要的頁面。你的index頁面應該包含類似的以下代碼:
define('yourPage',1);
然後,其它頁面應該包含:
if (!defined('yourPage')) die('Access Denied');
這麼做的目的是防止直接訪問你的其它php頁面。這樣,任何試圖不通過index.php訪問其它網頁的人,將得到“訪問被拒絕”的消息。
43. 創建一個數據庫類
如果你正在進行數據庫編程(在PHP中非常常見的任務),一個不錯的想法是創建一個數據庫類來處理任何數據庫管理功能。示例代碼如下:
public function dbExec($query) { $result = $this->db->exec($query); if (PEAR::isError($result)) errorRedirect($result->getMessage(), true); else return $result; }
這個函數僅接收一個查詢語句並對其執行。它還處理可能出現的任何錯誤。你還可以在這兒包含審核代碼,不過我更喜歡使用一個類似的審核函數:
// 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. 一個php文件處理輸入,一個class.php文件處理具體功能
不讓代碼變得混亂的一個重要方法是:獲取用戶輸入後,將其重定向到其它函數來進行處理。原理非常簡單,php文件獲得我們需要的任何輸入,然後將其執行重定向到類文件中的一個函數。舉例來講,假設有一個類似“index.php?page=profile&action=display”的URL。由profile.php來檢索該網址並得到操作是“display”。然後使用一個簡單的switch函數,我們來執行真正的顯示函數:
require_once PROJECTROOT.'libs/messages.class.php'; $message = new Message(); switch ($action) { case 'display': $message->display(); break; ...
如上所示,我使用了一個消息類,然後開始進行switch檢查。$message只是被類中的調用函數使用的一個對象。
45. 了解你的SQL語句,並總是對其審查(Sanitize)
正如我以前所提到的,任何php網站中最重要的部分有99%的可能是數據庫。因此,你需要非常熟悉如何正確的使用sql。學會關聯表和更多高級技術。下面我將展示一個使用MySQL的函數示例,並使用本文第7條函數進行審查。
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; }
首先,我們對用戶輸入進行檢查(通過一個GET變量傳遞消息id),然後我們執行我們的SQL命令。注意這兒SQL的用法。你需要了解如何使用別名和關聯表。
46. 當你只需要一個對象時,使用單例模式
在PHP中相當常見的一種情形時,我們只需要創建一個對象一次,然後在我們的整個程序中使用它。一個很好的例子就是smarty變量,一旦被初始化後就可以在任何地方使用。這種情形的一個很好實現方案就是單例模式。示例代碼如下:
function smartyObject() { if ($GLOBALS['config']['SmartyObj'] == 0) { $smarty = new SmartyGame(); $GLOBALS['config']['SmartyObj'] = $smarty; } else $smarty = $GLOBALS['config']['SmartyObj']; return $smarty; }
注意,我們擁有一個全局smarty變量(該示例中它在config.php中被初始化),如果它的值為0,我們將創建一個新smarty對象。否則,它意味著該對象已經被創建,我們只需要返回它。
47. 關於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; } //調用方法$browser=browseinfo();直接返回結果
49.獲取訪問者操作系統
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; } //調用方法$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"; ?> //改動相應文件頭就可以輸出.doc.xls等文件格式了
以上就是本文的全部內容,大家結合前一篇進行深入學習,一定會有所收獲。