天天看點

提高 PHP 代碼品質的 36 種方法

常常會看到:

1

<code>require_once</code><code>(</code><code>'../../lib/some_class.php'</code><code>);</code>

該方法有很多缺點:

它首先查找指定的php包含路徑, 然後查找目前目錄.

是以會檢查過多路徑.

如果該腳本被另一目錄的腳本包含, 它的基本目錄變成了另一腳本所在的目錄.

另一問題, 當定時任務運作該腳本, 它的上級目錄可能就不是工作目錄了.

是以最佳選擇是使用絕對路徑:

2

3

4

<code>define(</code><code>'ROOT'</code> <code>,</code><code>'/var/www/project/'</code><code>);</code>

<code>require_once</code><code>(ROOT .</code><code>'../../lib/some_class.php'</code><code>);</code>

<code>//rest of the code</code>

我們定義了一個絕對路徑, 值被寫死了. 我們還可以改進它. 路徑 /var/www/project 也可能會改變, 那麼我們每次都要改變它嗎? 不是的, 我們可以使用__FILE__常量, 如:

5

6

7

<code>//suppose your script is /var/www/project/index.php</code>

<code>//Then __FILE__ will always have that full path.</code>

<code>define(</code><code>'ROOT'</code> <code>,</code><code>pathinfo</code><code>(</code><code>__FILE__</code><code>, PATHINFO_DIRNAME));</code>

現在, 無論你移到哪個目錄, 如移到一個外網的伺服器上, 代碼無須更改便可正确運作.

可以在腳本頭部引入多個檔案, 像類庫, 工具檔案和助手函數等, 如:

<code>require_once</code><code>(</code><code>'lib/Database.php'</code><code>);</code>

<code>require_once</code><code>(</code><code>'lib/Mail.php'</code><code>);</code>

<code>require_once</code><code>(</code><code>'helpers/utitlity_functions.php'</code><code>);</code>

這種用法相當原始. 應該更靈活點. 應編寫個助手函數包含檔案. 例如:

8

9

<code>function</code> <code>load_class(</code><code>$class_name</code><code>)</code>

<code>{</code>

<code>    </code><code>//path to the class file</code>

<code>    </code><code>$path</code> <code>= ROOT .</code><code>'/lib/'</code> <code>.</code><code>$class_name</code> <code>.</code><code>'.php'</code><code>);</code>

<code>    </code><code>require_once</code><code>(</code><code>$path</code> <code>);</code>

<code>}</code>

<code>load_class(</code><code>'Database'</code><code>);</code>

<code>load_class(</code><code>'Mail'</code><code>);</code>

有什麼不一樣嗎? 該代碼更具可讀性.

將來你可以按需擴充該函數, 如:

10

<code>    </code><code>if</code><code>(</code><code>file_exists</code><code>(</code><code>$path</code><code>))</code>

<code>    </code><code>{</code>

<code>        </code><code>require_once</code><code>(</code><code>$path</code> <code>);</code>

<code>    </code><code>}</code>

還可做得更多:

為同樣檔案查找多個目錄

能很容易的改變放置類檔案的目錄, 無須在代碼各處一一修改

可使用類似的函數加載檔案, 如html内容.

在開發環境中, 我們列印資料庫查詢語句, 轉存有問題的變量值, 而一旦問題解決, 我們注釋或删除它們. 然而更好的做法是保留調試代碼.

在開發環境中, 你可以:

11

12

13

<code>define(</code><code>'ENVIRONMENT'</code> <code>,</code><code>'development'</code><code>);</code>

<code>if</code><code>(!</code><code>$db</code><code>-&gt;query(</code><code>$query</code> <code>)</code>

<code>    </code><code>if</code><code>(ENVIRONMENT ==</code><code>'development'</code><code>)</code>

<code>        </code><code>echo</code> <code>"$query failed"</code><code>;</code>

<code>    </code><code>else</code>

<code>        </code><code>echo</code> <code>"Database error. Please contact administrator"</code><code>;</code>

在伺服器中, 你可以:

<code>define(</code><code>'ENVIRONMENT'</code> <code>,</code><code>'production'</code><code>);</code>

system, exec, passthru, shell_exec 這4個函數可用于執行系統指令. 每個的行為都有細微差别. 問題在于, 當在共享主機中, 某些函數可能被選擇性的禁用. 大多數新手趨于每次首先檢查哪個函數可用, 然而再使用它.

更好的方案是封成函數一個可跨平台的函數.

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

<code>/**</code>

<code>    </code><code>Method to execute a command in the terminal</code>

<code>    </code><code>Uses :</code>

<code>    </code><code>1. system</code>

<code>    </code><code>2. passthru</code>

<code>    </code><code>3. exec</code>

<code>    </code><code>4. shell_exec</code>

<code>*/</code>

<code>function</code> <code>terminal(</code><code>$command</code><code>)</code>

<code>    </code><code>//system</code>

<code>    </code><code>if</code><code>(function_exists(</code><code>'system'</code><code>))</code>

<code>        </code><code>ob_start();</code>

<code>        </code><code>system(</code><code>$command</code> <code>,</code><code>$return_var</code><code>);</code>

<code>        </code><code>$output</code> <code>= ob_get_contents();</code>

<code>        </code><code>ob_end_clean();</code>

<code>    </code><code>//passthru</code>

<code>    </code><code>else</code> <code>if</code><code>(function_exists(</code><code>'passthru'</code><code>))</code>

<code>        </code><code>passthru</code><code>(</code><code>$command</code> <code>,</code><code>$return_var</code><code>);</code>

<code>    </code><code>//exec</code>

<code>    </code><code>else</code> <code>if</code><code>(function_exists(</code><code>'exec'</code><code>))</code>

<code>        </code><code>exec</code><code>(</code><code>$command</code> <code>,</code><code>$output</code> <code>,</code><code>$return_var</code><code>);</code>

<code>        </code><code>$output</code> <code>= implode(</code><code>"n"</code> <code>,</code><code>$output</code><code>);</code>

<code>    </code><code>//shell_exec</code>

<code>    </code><code>else</code> <code>if</code><code>(function_exists(</code><code>'shell_exec'</code><code>))</code>

<code>        </code><code>$output</code> <code>= shell_exec(</code><code>$command</code><code>) ;</code>

<code>        </code><code>$output</code> <code>=</code><code>'Command execution not possible on this system'</code><code>;</code>

<code>        </code><code>$return_var</code> <code>= 1;</code>

<code>    </code><code>return</code> <code>array</code><code>(</code><code>'output'</code> <code>=&gt;</code><code>$output</code> <code>,</code><code>'status'</code> <code>=&gt;</code><code>$return_var</code><code>);</code>

<code>terminal(</code><code>'ls'</code><code>);</code>

上面的函數將運作shell指令, 隻要有一個系統函數可用, 這保持了代碼的一緻性.

<code>function</code> <code>add_to_cart(</code><code>$item_id</code> <code>,</code><code>$qty</code><code>)</code>

<code>    </code><code>$_SESSION</code><code>[</code><code>'cart'</code><code>][</code><code>'item_id'</code><code>] =</code><code>$qty</code><code>;</code>

<code>add_to_cart(</code><code>'IPHONE3'</code> <code>, 2 );</code>

使用上面的函數添加單個項目. 而當添加項清單的時候,你要建立另一個函數嗎? 不用, 隻要稍加留意不同類型的參數, 就會更靈活. 如:

<code>    </code><code>if</code><code>(!</code><code>is_array</code><code>(</code><code>$item_id</code><code>))</code>

<code>        </code><code>$_SESSION</code><code>[</code><code>'cart'</code><code>][</code><code>'item_id'</code><code>] =</code><code>$qty</code><code>;</code>

<code>        </code><code>foreach</code><code>(</code><code>$item_id</code> <code>as</code> <code>$i_id</code> <code>=&gt;</code><code>$qty</code><code>)</code>

<code>        </code><code>{</code>

<code>            </code><code>$_SESSION</code><code>[</code><code>'cart'</code><code>][</code><code>'i_id'</code><code>] =</code><code>$qty</code><code>;</code>

<code>        </code><code>}</code>

<code>add_to_cart(</code><code>array</code><code>(</code><code>'IPHONE3'</code> <code>=&gt; 2 ,</code><code>'IPAD'</code> <code>=&gt; 5) );</code>

現在, 同個函數可以處理不同類型的輸入參數了. 可以參照上面的例子重構你的多處代碼, 使其更智能.

我很想知道為什麼這麼多關于php建議的部落格文章都沒提到這點.

<code>&lt;?php</code>

<code>echo</code> <code>"Hello"</code><code>;</code>

<code>//Now dont close this tag</code>

這將節約你很多時間. 我們舉個例子:

一個 super_class.php 檔案

<code>//super extra character after the closing tag</code>

index.php

<code>require_once</code><code>(</code><code>'super_class.php'</code><code>);</code>

<code>//echo an p_w_picpath or pdf , or set the cookies or session data</code>

這樣, 你將會得到一個 Headers already send error. 為什麼? 因為 &amp;ldquo;super extra character&amp;rdquo; 已經被輸出了. 現在你得開始調試啦. 這會花費大量時間尋找 super extra 的位置.

是以, 養成省略關閉符的習慣:

<code>&lt;!--?php</code>

<code>class</code> <code>super_class</code>

<code>    </code><code>function</code> <code>super_function()</code>

<code>        </code><code>//super code</code>

<code>//No closing tag&lt;/pre--&gt;</code>

這會更好.

這稱為輸出緩沖, 假如說你已在不同的函數輸出内容:

<code>function</code> <code>print_header()</code>

<code>    </code><code>echo</code> <code>"Site Log and Login links"</code><code>;</code>

<code>function</code> <code>print_footer()</code>

<code>    </code><code>echo</code> <code>"Site was made by me"</code><code>;</code>

<code>print_header();</code>

<code>for</code><code>(</code><code>$i</code> <code>= 0 ;</code><code>$i</code> <code>';</code>

<code>print_footer();</code>

替代方案, 在某地方集中收集輸出. 你可以存儲在函數的局部變量中, 也可以使用ob_start和ob_end_clean. 如下:

<code>    </code><code>$o</code> <code>=</code><code>"Site Log and Login links"</code><code>;</code>

<code>    </code><code>return</code> <code>$o</code><code>;</code>

<code>    </code><code>$o</code> <code>=</code><code>"Site was made by me"</code><code>;</code>

<code>echo</code> <code>print_header();</code>

<code>echo</code> <code>print_footer();</code>

為什麼需要輸出緩沖:

&gt;&gt;可以在發送給浏覽器前更改輸出. 如 str_replaces 函數或可能是 preg_replaces 或添加些監控/調試的html内容.

&gt;&gt;輸出給浏覽器的同時又做php的處理很糟糕. 你應該看到過有些站點的側邊欄或中間出現錯誤資訊. 知道為什麼會發生嗎? 因為處理和輸出混合了.

輸出一些xml.

<code>$xml = '';</code>

<code>$xml = "0</code>

<code>";</code>

<code>//Send xml data</code>

<code>echo $xml;</code>

工作得不錯. 但需要一些改進.

<code>header("content-type: text/xml");</code>

注意header行. 該行告知浏覽器發送的是xml類型的内容. 是以浏覽器能正确的處理. 很多的javascript庫也依賴頭資訊.

類似的有 javascript , css, jpg p_w_picpath, png p_w_picpath:

JavaScript

<code>header("content-type: application/x-javascript");</code>

<code>echo "var a = 10";</code>

CSS

<code>header(</code><code>"content-type: text/css"</code><code>);</code>

<code>echo</code><code>"#div id { background:#000; }"</code><code>;</code>

曾經遇到過在mysql表中設定了unicode/utf-8編碼,  phpadmin也能正确顯示, 但當你擷取内容并在頁面輸出的時候,會出現亂碼. 這裡的問題出在mysql連接配接的字元編碼.

<code>//Attempt to connect to database</code>

<code>$c</code> <code>= mysqli_connect(</code><code>$this</code><code>-&gt;host ,</code><code>$this</code><code>-&gt;username,</code><code>$this</code><code>-&gt;password);</code>

<code>//Check connection validity</code>

<code>if</code> <code>(!</code><code>$c</code><code>) </code>

<code>    </code><code>die</code> <code>(</code><code>"Could not connect to the database host: "</code><code>. mysqli_connect_error());</code>

<code>//Set the character set of the connection</code>

<code>if</code><code>(!mysqli_set_charset (</code><code>$c</code> <code>,</code><code>'UTF8'</code> <code>))</code>

<code>    </code><code>die</code><code>(</code><code>'mysqli_set_charset() failed'</code><code>);</code>

一旦連接配接資料庫, 最好設定連接配接的 characterset. 你的應用如果要支援多語言, 這麼做是必須的.

php5.4前, 字元的預設編碼是ISO-8859-1, 不能直接輸出如&amp;Agrave; &amp;acirc;等.

<code>$value</code> <code>= htmlentities(</code><code>$this</code><code>-&gt;value , ENT_QUOTES , CHARSET);</code>

php5.4以後, 預設編碼為UTF-8, 這將解決很多問題. 但如果你的應用是多語言的, 仍然要留意編碼問題,.

考慮過使用 ob_gzhandler 嗎? 不要那樣做. 毫無意義. php隻應用來編寫應用. 不應操心伺服器和浏覽器的資料傳輸優化問題.

使用apache的mod_gzip/mod_deflate 子產品壓縮内容.

時常會用php輸出動态javascript内容:

<code>$p_w_picpaths = array(</code>

<code> </code><code>'myself.png'</code> <code>,</code><code>'friends.png'</code> <code>,</code><code>'colleagues.png'</code>

<code>);</code>

<code>$js_code =</code><code>''</code><code>;</code>

<code>foreach($p_w_picpaths as $p_w_picpath)</code>

<code>$js_code .=</code><code>"'$p_w_picpath' ,"</code><code>;</code>

<code>$js_code =</code><code>'var p_w_picpaths = ['</code> <code>. $js_code .</code><code>']; '</code><code>;</code>

<code>echo $js_code;</code>

<code>//Output is var p_w_picpaths = ['myself.png' ,'friends.png' ,'colleagues.png' ,];</code>

更聰明的做法, 使用 json_encode:

<code>$p_w_picpaths</code> <code>=</code><code>array</code><code>(</code>

<code>$js_code</code> <code>=</code><code>'var p_w_picpaths = '</code> <code>. json_encode(</code><code>$p_w_picpaths</code><code>);</code>

<code>echo</code> <code>$js_code</code><code>;</code>

<code>//Output is : var p_w_picpaths = ["myself.png","friends.png","colleagues.png"]</code>

優雅乎?

寫或儲存檔案前, 確定目錄是可寫的, 假如不可寫, 輸出錯誤資訊. 這會節約你很多調試時間. linux系統中, 需要處理權限, 目錄權限不當會導緻很多很多的問題, 檔案也有可能無法讀取等等.

確定你的應用足夠智能, 輸出某些重要資訊.

<code>$contents = "All the content";</code>

<code>$file_path = "/var/www/project/content.txt";</code>

<code>file_put_contents($file_path , $contents);</code>

這大體上正确. 但有些間接的問題. file_put_contents 可能會由于幾個原因失敗:

&gt;&gt;父目錄不存在

&gt;&gt;目錄存在, 但不可寫

&gt;&gt;檔案被寫鎖住?

是以寫檔案前做明确的檢查更好.

<code>$dir = '/var/www/project';</code>

<code>$file_path = $dir . "/content.txt";</code>

<code>if(is_writable($dir))</code>

<code>    </code><code>file_put_contents($file_path , $contents);</code>

<code>else</code>

<code>    </code><code>die("Directory $dir is not writable, or does not exist. Please check");</code>

這麼做後, 你會得到一個檔案在何處寫及為什麼失敗的明确資訊.

在linux環境中, 權限問題可能會浪費你很多時間. 從今往後, 無論何時, 當你建立一些檔案後, 確定使用chmod設定正确權限. 否則的話, 可能檔案先是由&amp;rdquo;php&amp;rdquo;使用者建立, 但你用其它的使用者登入工作, 系統將會拒絕通路或打開檔案, 你不得不奮力擷取root權限,  更改檔案的權限等等.

<code>// Read and write for owner, read for everybody else</code>

<code>chmod</code><code>(</code><code>"/somedir/somefile"</code><code>, 0644);</code>

<code>// Everything for owner, read and execute for others</code>

<code>chmod</code><code>(</code><code>"/somedir/somefile"</code><code>, 0755);</code>

<code>if</code><code>(</code><code>$_POST</code><code>[</code><code>'submit'</code><code>] ==</code><code>'Save'</code><code>)</code>

<code>    </code><code>//Save the things</code>

上面大多數情況正确, 除了應用是多語言的. &amp;lsquo;Save&amp;rsquo; 可能代表其它含義. 你怎麼區分它們呢. 是以, 不要依賴于submit按鈕的值.

<code>if</code><code>(</code><code>$_SERVER</code><code>[</code><code>'REQUEST_METHOD'</code><code>] ==</code><code>'POST'</code> <code>and</code> <code>isset(</code><code>$_POST</code><code>[</code><code>'submit'</code><code>]) )</code>

現在你從submit按鈕值中解脫出來了.

<code>//Delay for some time</code>

<code>function</code> <code>delay()</code>

<code>    </code><code>$sync_delay</code> <code>= get_option(</code><code>'sync_delay'</code><code>);</code>

<code>    </code><code>echo</code> <code>"Delaying for $sync_delay seconds..."</code><code>;</code>

<code>    </code><code>sleep(</code><code>$sync_delay</code><code>);</code>

<code>    </code><code>echo</code> <code>"Done "</code><code>;</code>

用靜态變量取代:

<code>    </code><code>static</code> <code>$sync_delay</code> <code>= null;</code>

<code>    </code><code>if</code><code>(</code><code>$sync_delay</code> <code>== null)</code>

某些簡單例子:

<code>$_SESSION</code><code>[</code><code>'username'</code><code>] =</code><code>$username</code><code>;</code>

<code>$username</code> <code>=</code><code>$_SESSION</code><code>[</code><code>'username'</code><code>];</code>

這會導緻某些問題. 如果在同個域名中運作了多個應用, session 變量可能會沖突. 兩個不同的應用可能使用同一個session key. 例如, 一個前端門戶, 和一個背景管理系統使用同一域名.

從現在開始, 使用應用相關的key和一個包裝函數:

<code>define(</code><code>'APP_ID'</code> <code>,</code><code>'abc_corp_ecommerce'</code><code>);</code>

<code>//Function to get a session variable</code>

<code>function</code> <code>session_get(</code><code>$key</code><code>)</code>

<code>    </code><code>$k</code> <code>= APP_ID .</code><code>'.'</code> <code>.</code><code>$key</code><code>;</code>

<code>    </code><code>if</code><code>(isset(</code><code>$_SESSION</code><code>[</code><code>$k</code><code>]))</code>

<code>        </code><code>return</code> <code>$_SESSION</code><code>[</code><code>$k</code><code>];</code>

<code>    </code><code>return</code> <code>false;</code>

<code>//Function set the session variable</code>

<code>function</code> <code>session_set(</code><code>$key</code> <code>,</code><code>$value</code><code>)</code>

<code>    </code><code>$_SESSION</code><code>[</code><code>$k</code><code>] =</code><code>$value</code><code>;</code>

<code>    </code><code>return</code> <code>true;</code>

假如你在某檔案中定義了很多工具函數:

<code>function</code> <code>utility_a()</code>

<code>    </code><code>//This function does a utility thing like string processing</code>

<code>function</code> <code>utility_b()</code>

<code>    </code><code>//This function does nother utility thing like database processing</code>

<code>function</code> <code>utility_c()</code>

<code>    </code><code>//This function is ...</code>

這些函數的使用分散到應用各處. 你可能想將他們封裝到某個類中:

<code>class</code> <code>Utility</code>

<code>    </code><code>public</code> <code>static</code> <code>function</code> <code>utility_a()</code>

<code>    </code><code>public</code> <code>static</code> <code>function</code> <code>utility_b()</code>

<code>    </code><code>public</code> <code>static</code> <code>function</code> <code>utility_c()</code>

<code>//and call them as</code>

<code>$a</code> <code>= Utility::utility_a();</code>

<code>$b</code> <code>= Utility::utility_b();</code>

顯而易見的好處是, 如果php内建有同名的函數, 這樣可以避免沖突.

另一種看法是, 你可以在同個應用中為同個類維護多個版本, 而不導緻沖突. 這是封裝的基本好處, 無它.

&gt;&gt;使用echo取代print

&gt;&gt;使用str_replace取代preg_replace, 除非你絕對需要

&gt;&gt;不要使用 short tag

&gt;&gt;簡單字元串用單引号取代雙引号

&gt;&gt;head重定向後記得使用exit

&gt;&gt;不要在循環中調用函數

&gt;&gt;isset比strlen快

&gt;&gt;始中如一的格式化代碼

&gt;&gt;不要删除循環或者if-else的括号

不要這樣寫代碼:

<code>if</code><code>(</code><code>$a</code> <code>== true)</code><code>$a_count</code><code>++;</code>

這絕對WASTE.

寫成:

<code>if</code><code>(</code><code>$a</code> <code>== true)</code>

<code>    </code><code>$a_count</code><code>++;</code>

不要嘗試省略一些文法來縮短代碼. 而是讓你的邏輯簡短.

&gt;&gt;使用有高亮文法顯示的文本編輯器. 高亮文法能讓你減少錯誤.

比如說你想 trim 數組中的所有元素. 新手可能會:

<code>foreach</code><code>(</code><code>$arr</code> <code>as</code> <code>$c</code> <code>=&gt;</code><code>$v</code><code>)</code>

<code>    </code><code>$arr</code><code>[</code><code>$c</code><code>] = trim(</code><code>$v</code><code>);</code>

但使用 array_map 更簡單:

<code>$arr</code> <code>=</code><code>array_map</code><code>(</code><code>'trim'</code> <code>,</code><code>$arr</code><code>);</code>

這會為$arr數組的每個元素都申請調用trim. 另一個類似的函數是 array_walk. 請查閱文檔學習更多技巧.

你肯定曾使用過正規表達式驗證 email , ip位址等. 是的,每個人都這麼使用. 現在, 我們想做不同的嘗試, 稱為filter.

php的filter擴充提供了簡單的方式驗證和檢查輸入.

<code>$amount</code> <code>=</code><code>intval</code><code>(</code><code>$_GET</code><code>[</code><code>'amount'</code><code>] );</code>

<code>$rate</code> <code>= (int)</code><code>$_GET</code><code>[</code><code>'rate'</code><code>];</code>

這是個好習慣.

如果你使用php開發大型的應用, php承擔了很多運算量, 速度會是一個很重要的名額. 使用profile幫助優化代碼. 可使用

xdebug和webgrid.

對于大的數組和字元串, 必須小心處理. 常見錯誤是發生數組拷貝導緻記憶體溢出,抛出Fatal Error of Memory size 資訊:

<code>$db_records_in_array_format</code><code>;</code><code>//This is a big array holding 1000 rows from a table each having 20 columns , every row is atleast 100 bytes , so total 1000 * 20 * 100 = 2MB</code>

<code>$cc</code> <code>=</code><code>$db_records_in_array_format</code><code>;</code><code>//2MB more</code>

<code>some_function(</code><code>$cc</code><code>);</code><code>//Another 2MB ?</code>

當導入或導出csv檔案時, 常常會這麼做.

不要認為上面的代碼會經常因記憶體限制導緻腳本崩潰. 對于小的變量是沒問題的, 但處理大數組的時候就必須避免.

確定通過引用傳遞, 或存儲在類變量中:

<code>$a</code> <code>= get_large_array();</code>

<code>pass_to_function(&amp;</code><code>$a</code><code>);</code>

這麼做後, 向函數傳遞變量引用(而不是拷貝數組). 檢視文檔.

<code>class</code> <code>A</code>

<code>    </code><code>function</code> <code>first()</code>

<code>        </code><code>$this</code><code>-&gt;a = get_large_array();</code>

<code>        </code><code>$this</code><code>-&gt;pass_to_function();</code>

<code>    </code><code>function</code> <code>pass_to_function()</code>

<code>        </code><code>//process $this-&gt;a</code>

盡快的 unset 它們, 讓記憶體得以釋放,減輕腳本負擔.

確定你的腳本由始至終都使用單一的資料庫連接配接. 在開始處正确的打開連接配接, 使用它直到結束, 最後關閉它. 不要像下面這樣在函數中打開連接配接:

<code>function</code> <code>add_to_cart()</code>

<code>    </code><code>$db</code> <code>=</code><code>new</code> <code>Database();</code>

<code>    </code><code>$db</code><code>-&gt;query(</code><code>"INSERT INTO cart ....."</code><code>);</code>

<code>function</code> <code>empty_cart()</code>

<code>    </code><code>$db</code><code>-&gt;query(</code><code>"DELETE FROM cart ....."</code><code>);</code>

使用多個連接配接是個糟糕的, 它們會拖慢應用, 因為建立連接配接需要時間和占用記憶體.

特定情況使用單例模式, 如資料庫連接配接.

不厭其煩的寫了太多如下的語句:

<code>$query</code> <code>=</code><code>"INSERT INTO users(name , email , address , phone) VALUES('$name' , '$email' , '$address' , '$phone')"</code><code>;</code>

<code>$db</code><code>-&gt;query(</code><code>$query</code><code>);</code><code>//call to mysqli_query()</code>

這不是個建壯的方案. 它有些缺點:

&gt;&gt;每次都手動轉義值

&gt;&gt;驗證查詢是否正确

&gt;&gt;查詢的錯誤會花很長時間識别(除非每次都用if-else檢查)

&gt;&gt;很難維護複雜的查詢

是以使用函數封裝:

<code>function</code> <code>insert_record(</code><code>$table_name</code> <code>,</code><code>$data</code><code>)</code>

<code>    </code><code>foreach</code><code>(</code><code>$data</code> <code>as</code> <code>$key</code> <code>=&gt;</code><code>$value</code><code>)</code>

<code>    </code><code>//mysqli_real_escape_string</code>

<code>        </code><code>$data</code><code>[</code><code>$key</code><code>] =</code><code>$db</code><code>-&gt;mres(</code><code>$value</code><code>);</code>

<code>    </code><code>$fields</code> <code>= implode(</code><code>','</code> <code>,</code><code>array_keys</code><code>(</code><code>$data</code><code>));</code>

<code>    </code><code>$values</code> <code>=</code><code>"'"</code> <code>. implode(</code><code>"','"</code> <code>,</code><code>array_values</code><code>(</code><code>$data</code><code>)) .</code><code>"'"</code><code>;</code>

<code>    </code><code>//Final query</code>

<code>    </code><code>$query</code> <code>=</code><code>"INSERT INTO {$table}($fields) VALUES($values)"</code><code>;</code>

<code>    </code><code>return</code> <code>$db</code><code>-&gt;query(</code><code>$query</code><code>);</code>

<code>$data</code> <code>=</code><code>array</code><code>(</code><code>'name'</code> <code>=&gt;</code><code>$name</code> <code>,</code><code>'email'</code> <code>=&gt;</code><code>$email</code>  <code>,</code><code>'address'</code> <code>=&gt;</code><code>$address</code> <code>,</code><code>'phone'</code> <code>=&gt;</code><code>$phone</code><code>);</code>

<code>insert_record(</code><code>'users'</code> <code>,</code><code>$data</code><code>);</code>

看到了嗎? 這樣會更易讀和擴充. record_data 函數小心的處理了轉義.

最大的優點是資料被預處理為一個數組, 任何文法錯誤都會被捕獲.

該函數應該定義在某個database類中, 你可以像 $db-&gt;insert_record這樣調用.

檢視本文, 看看怎樣讓你處理資料庫更容易.

類似的也可以編寫update,select,delete方法. 試試吧.

如果所有的内容都是從資料庫擷取的, 它們應該被緩存. 一旦生成了, 就將它們儲存在臨時檔案中. 下次請求該頁面時, 可直接從緩存中取, 不用再查資料庫.

好處:

&gt;&gt;節約php處理頁面的時間, 執行更快

&gt;&gt;更少的資料庫查詢意味着更少的mysql連接配接開銷

基于檔案的session政策會有很多限制. 使用基于檔案的session不能擴充到叢集中, 因為session儲存在單個伺服器中. 但資料庫可被多個伺服器通路, 這樣就可以解決問題.

在資料庫中儲存session資料, 還有更多好處:

&gt;&gt;處理username重複登入問題. 同個username不能在兩個地方同時登入.

&gt;&gt;能更準備的查詢線上使用者狀态.

&gt;&gt;使用 defines/constants

&gt;&gt;使用函數擷取值

&gt;&gt;使用類并通過$this通路

沒聽說過? 請看下面:

<code>&lt;</code><code>img</code> <code>alt</code><code>=</code><code>""</code> <code>src</code><code>=</code><code>"happy.jpg"</code> <code>/&gt;</code>

base 标簽非常有用. 假設你的應用分成幾個子目錄, 它們都要包括相同的導航菜單.

www.domain.com/store/home.php

www.domain.com/store/products/ipad.php

在首頁中, 可以寫:

<code>&lt;</code><code>a</code> <code>href</code><code>=</code><code>"home.php"</code><code>&gt;Home&lt;/</code><code>a</code><code>&gt;</code>

<code>&lt;</code><code>a</code> <code>href</code><code>=</code><code>"products/ipad.php"</code><code>&gt;Ipad&lt;/</code><code>a</code><code>&gt;</code>

但在你的ipad.php不得不寫成:

<code>&lt;</code><code>span</code> <code>style</code><code>=</code><code>"color:#333333;font-family:''Helvetica, Arial, sans-serif'';"</code><code>&gt;&lt;</code><code>a</code> <code>href</code><code>=</code><code>"../home.php"</code><code>&gt;Home&lt;/</code><code>a</code><code>&gt;</code>

<code>&lt;</code><code>a</code> <code>href</code><code>=</code><code>"ipad.php"</code><code>&gt;Ipad&lt;/</code><code>a</code><code>&gt;&lt;/</code><code>span</code><code>&gt;</code>

因為目錄不一樣. 有這麼多不同版本的導航菜單要維護, 很糟糕啊. 

是以, 請使用base标簽.

<code>&lt;</code><code>span</code> <code>style</code><code>=</code><code>"color:#333333;font-family:''Helvetica, Arial, sans-serif'';"</code><code>&gt;&lt;</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>base</code> <code>href</code><code>=</code><code>"http://www.domain.com/store/"</code><code>&gt;</code>

<code>&lt;/</code><code>head</code><code>&gt;</code>

<code>&lt;</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>body</code><code>&gt;</code>

<code>&lt;/</code><code>html</code><code>&gt;&lt;/</code><code>span</code><code>&gt;</code>

現在, 這段代碼放在應用的各個目錄檔案中行為都一緻. 

關閉不相的錯誤報告. E_FATAL 錯誤是很重要的. 

<code>ini_set</code><code>(</code><code>'display_errors'</code><code>, 1);</code>

<code>error_reporting</code><code>(~E_WARNING &amp; ~E_NOTICE &amp; ~E_STRICT);</code>

integer在32位和64位體系結構中長度是不同的. 是以某些函數如 strtotime 的行為會不同.

在64位的機器中, 你會看到如下的輸出.

<code>$ php -a</code>

<code>Interactive shell</code>

<code>php &gt;</code><code>echo</code> <code>strtotime</code><code>(</code><code>"0000-00-00 00:00:00"</code><code>);</code>

<code>-62170005200</code>

<code>php &gt;</code><code>echo</code> <code>strtotime</code><code>(</code><code>'1000-01-30'</code><code>);</code>

<code>-30607739600</code>

<code>php &gt;</code><code>echo</code> <code>strtotime</code><code>(</code><code>'2100-01-30'</code><code>);</code>

<code>4104930600</code>

但在32位機器中, 它們將是bool(false).

如果你想限制最小時間, 可以使用下面的腳本:

<code>&lt;span style=</code><code>"color:#333333;font-family:''Helvetica, Arial, sans-serif'';"</code><code>&gt;set_time_limit(30);</code>

<code>//Rest of the code&lt;/span&gt;</code>

高枕無憂嗎?  注意任何外部的執行, 如系統調用,socket操作, 資料庫操作等, 就不在set_time_limits的控制之下.

 是以, 就算資料庫花費了很多時間查詢, 腳本也不會停止執行. 視情況而定.

一些例子:

&gt;&gt;mPDF &amp;mdash; 能通過html生成pdf文檔

&gt;&gt;PHPExcel &amp;mdash; 讀寫excel

&gt;&gt;PhpMailer &amp;mdash; 輕松處理發送包含附近的郵件

&gt;&gt;pChart &amp;mdash; 使用php生成報表

使用開源庫完成複雜任務, 如生成pdf, ms-excel檔案, 報表等.

是時候使用像 codeigniter 這樣的MVC架構了. MVC架構并不強迫你寫面向對象的代碼. 它們僅將php代碼與html分離.

&gt;&gt;明确區分php和html代碼. 在團隊協作中有好處, 設計師和程式員可以同時工作.

&gt;&gt;面向對象設計的函數能讓你更容易維護

&gt;&gt;内建函數完成了很多工作, 你不需要重複編寫

&gt;&gt;開發大的應用是必須的

&gt;&gt;很多建議, 技巧和hack已被架構實作了

phpbench 提供了些php基本操作的基準測試結果, 它展示了一些徽小的文法變化是怎樣導緻巨大差異的.

檢視php站點的評論, 有問題到IRC提問, 時常閱讀開源代碼, 使用Linux開發.