用 PHP 開發(fā)健壯的代碼(二):有效果地使用變量
發(fā)表時間:2023-08-14 來源:明輝站整理相關(guān)軟件相關(guān)文章人氣:
[摘要]“用 PHP 開發(fā)健壯的代碼”是關(guān)于解決大中型應(yīng)用程序中的實際問題的系列文章。在本文中,PHP 老手 Amol Hatwar 討論了如何有效地使用變量。他還演示了如何通過使用 PHP 中可變的變量名...
“用 PHP 開發(fā)健壯的代碼”是關(guān)于解決大中型應(yīng)用程序中的實際問題的系列文章。在本文中,PHP 老手 Amol Hatwar 討論了如何有效地使用變量。他還演示了如何通過使用 PHP 中可變的變量名來構(gòu)造配置文件解析器,以便簡化腳本配置。
在我的前一篇文章中,我研究了在規(guī)劃、設(shè)計甚至編寫代碼期間必須考慮的一些因素。在本文中,您將真正接觸到實際代碼,并可以看到實際運行中的一些東西。如果您還沒有看過前一篇文章,那么最好現(xiàn)在就看一看。
正確處理變量
變量與函數(shù)是任何計算機語言必不可少的要素。有了變量,您可以將數(shù)據(jù)抽象化;有了函數(shù),您可以將幾行代碼抽象化。正如 Bruce Eckel 在他的書籍《C++ 編程思想》中所說的那樣,所有編程語言都提供抽象。匯編語言是對底層機器的小抽象。隨后的許多所謂的命令式語言(如 Fortran、BASIC 和 C)是對匯編語言的抽象。
編程語言提供的抽象的種類和質(zhì)量直接關(guān)系到您所能解決的問題的復雜程度。理解 PHP 如何處理變量和函數(shù),將有助于您有效地使用它們。
名稱里有什么?
就象我在前一篇文章中提到的那樣,命名約定和編碼約定是重要的。無論您使用什么命名約定,請記住要在項目中嚴格遵守它。如果您使用應(yīng)用得最廣泛的命名約定,那么您的代碼將被更多的人所接受。
對變量進行命名時,在包括腳本時要特別注意不要覆蓋正在使用的變量。在大型應(yīng)用程序中,當增加新的功能時,這是常見的錯誤根源。防止這一問題的最佳辦法就是使用前綴。把變量所在模塊的名稱縮寫作為前綴來使用。例如,如果一個處理投票的模塊中有一個保存用戶標識的變量,那么您可以將該變量命名為 $poll_userID 或 $pollUserID。
理解 PHP 變量
PHP 是解釋型語言。這有許多好處,很快您將學習利用其中的一些。第一個很明顯的好處是:它使您省掉了設(shè)計-編碼-編譯-測試周期 — 您在編輯器中編寫的任何代碼都立即可使用。然而,最重要的好處是您不用擔心變量的類型以及如何在內(nèi)存中管理這些變量。所有分配給腳本的內(nèi)存在執(zhí)行完腳本后都由 PHP 自動收回。此外,可以對變量執(zhí)行許多操作而不必知道變量的類型。清單 1 中的代碼在 PHP 中工作十分正常,但在 C 和 Java 語言中會拋出一大堆錯誤消息:
清單 1. 帶變量的樣本 PHP 代碼
<?php
$myStr = 789696; // An integer.
$myVar = 2; // Another integer.
$myStr = "This is my favorite band: "; // Strings are more fun.
$myStr = $myStr . "U" . $myVar; // Doing this is OK, too.
echo "$myVar\n";
?>
安裝完 PHP 后,如要運行運行代碼,可首先將該代碼保存為一個 .php 文件,再將該文件放置在 Web 服務(wù)器上,然后將瀏覽器指向該文件。更好的辦法是安裝 PHP 的 CGI 版本。然后,通過在 shell 或命令提示符下輸入以下命令,并用包含您的腳本的文件名替代 script-name,這樣就可以運行該腳本了。
path-to-php/php script-name
該代碼能夠正常工作,因為 PHP 是類型寬松的語言。用通俗易懂的英語,可以不考慮變量類型,可以把字符串賦值給整數(shù),以及毫不費力地用較大的字符串替代較小的字符串。這在象 C 這樣的語言中是不可能的事情。在內(nèi)部,PHP 將變量所擁有的數(shù)據(jù)與類型分開存儲。類型存儲在單獨的表中。每當出現(xiàn)包含不同類型的表達式時,PHP 自動確定程序員想要做什么,接著更改表中的類型,然后自動對表達式求值。
介紹一個常見的小問題
不用擔心類型固然很好,但有時那也會使您陷入真正的麻煩。怎么回事呢?這里有一個實際的示例:我常常必須把在基于 Windows 的 PC 上創(chuàng)建的內(nèi)容移到 Linux 系統(tǒng),以便能在 Web 上使用它們; Windows 的文件系統(tǒng)在處理文件名時是不區(qū)分大小寫的。文件名 DefParser.php 和 defparser.php 指向 Windows 上的同一文件。在 Linux 操作系統(tǒng)上,它們指向不同的文件。您可能提倡文件名要么全用大寫,要么全用小寫,但最好的做法應(yīng)該是使大小寫保持不變。
解決這個小問題
假設(shè)您想要一個函數(shù),它能在不考慮大小寫的情況下檢查給定文件是否存在于某個目錄中。首先,將這個任務(wù)分解成一些簡單的步驟。分解代碼可能聽起來有些可笑,但它確實有助于您在編寫代碼時將主要精力放在這段代碼上。另外,在紙上重寫步驟始終比重寫代碼容易得多:
獲取源目錄中的所有文件名
過濾掉 . 和 .. 目錄
檢查目標文件是否存在于該目錄中
如果文件存在,則獲取具有正確大小寫的文件名
如果名稱不匹配,則返回 false
要讀取目錄的內(nèi)容,需要使用 readdir() 函數(shù)?梢栽 PHP 手冊(請參閱參考資料)中獲取有關(guān)該函數(shù)的更多細節(jié)。至于現(xiàn)在,只要知道:readdir() 在每次調(diào)用時會逐個返回給定目錄中所有文件的名稱。在列出了所有的文件名后,它返回 false。您將使用一個循環(huán),該循環(huán)在 readdir() 返回 false 時終止。
但這樣就夠了嗎?請記住,PHP 是類型寬松的語言,這意味著會將整型值 0 與 false 視為相同(甚至 C 也把 0 和布爾值 false 視為等價)。問題不是該代碼是否能正常工作;想象一下,如果文件的名稱是 0 會如何!該腳本會過早終止。可以使用以下腳本(清單 2)來確定 0 與布爾值 false 的等價性:
清單 2. 確定 0 與布爾值 false 是否等價的腳本
<?php
$file_name = 0;
if (0 == $file_name ) {
echo "The code is in trouble ...\n"; // This text prints on the screen.
}
else {
echo "Phew ... The code is safe"; // This text never prints.
}
?>
那么您可以做什么呢?您知道 PHP 會在內(nèi)部存儲類型,而如果能夠訪問這些類型的話,問題就解決了。布爾值 false 和整型值 0 明顯是不同的。
PHP 有一個 gettype() 函數(shù),但讓我們在這里選擇更簡單的方法。您可以使用 === 運算符(是的,有三個等號)。不同之處在于該運算符同時比較數(shù)據(jù)的值和類型。如果您對此覺得有些疑惑,PHP 還有 !== 運算符。只有 PHP 4 中才有這些新型運算符和 gettype() 函數(shù)。清單 3 顯示了解決該問題的完整代碼:
清單 3. 完整代碼
<?php
/* This is the function where the action takes place */
function chk_file_name( $name, $path="." ) {
$fileList = get_file_list($path);
foreach ($fileList as $file) {
if (eregi($name, $file)) {
return $file;
}
}
return false;
}
/* Return the list of files in a given directory in an array.
Uses the current directory as default. */
function get_file_list($dirName=".") {
$list = array();
$handle = opendir($dirName);
while (false !== ($file = readdir($handle))) {
/* Omit the '.' and the '..' directories. */
if ((".."== $file) ("."== $file)) continue;
array_push($list, $file);
}
closedir($handle);
return $list;
}
?>
觀察中得到的經(jīng)驗
我不打算對清單 3 中各個函數(shù)的功能加以說明,相反,我鼓勵您查閱 PHP 手冊(請參閱參考資料)。當您使用不熟悉的函數(shù)時,假設(shè)的參數(shù)與返回值的類型會是另一個錯誤根源。我沒有對 PHP 中的內(nèi)置函數(shù)加以說明,而是打算說明一些不太一目了然的事情。
當終止條件中涉及不同的變量類型時,通過使用 === 和 !== 運算符進行強類型檢查是很重要的。
由各部分組成的代碼
我本來可以將整個腳本編寫為一個函數(shù),但這里我卻把代碼分割成兩個函數(shù)。還記得前一篇文章中的“分而治之”規(guī)則嗎?我這么做正是因為每個函數(shù)所起的作用不同。如果您用其它腳本獲取某個目錄的內(nèi)容,那么現(xiàn)在就可以使用方便的實現(xiàn)。我希望您考慮一些事情:想象一下將整個腳本作為一個函數(shù)來實現(xiàn),然后想象調(diào)試、測試和重用代碼所需的工作。
正確使用循環(huán)
現(xiàn)在看看 foreach 循環(huán),想想為什么不用 for 循環(huán)?使用 for 循環(huán)要求您知道數(shù)組中項的數(shù)目 — 需要一個額外的步驟。此外,在處理 PHP 數(shù)組時,有可能超出數(shù)組邊界。也就是說,在數(shù)組只有 10 個元素時,試圖訪問它的第 15 個元素。PHP 的確會給出一個小警告,但據(jù)我所知,在一些情況下,當反復運行某個腳本時,CPU 活動率會突然上升到 100% 而服務(wù)器性能則連續(xù)下降。我建議您盡可能地避免使用 for 循環(huán)。
斷言性的 if
最后,我希望您研究一下那個在 get_file_list() 函數(shù)中用于忽略 . 和 .. 目錄的較大的 if 條件。顯然,我可以采用傳統(tǒng)的方法,根據(jù)常數(shù)來檢查變量。但在我自己的許多編碼昏招中,我經(jīng)常會遺漏等號并且在以后找不到哪里出了問題。當然,PHP 不會報錯,因為它認為我想進行賦值而不是比較。當您根據(jù)變量來比較常數(shù)并且又遺漏了一個等號時,PHP 會拋出錯誤消息。
可變的變量名
現(xiàn)在來討論一些奇妙的事情。作為新手的開發(fā)人員認為,使用可變變量來完成任務(wù)是一種令人費解的方法,所以常常回避它。實際上,很容易理解和使用可變變量。它們已經(jīng)不止一次地幫我擺脫困境,而且它們是一種重要的語言元素。事實上,在有些情況下,使用可變變量在所難免。很快我將研究一種此類現(xiàn)實情況,但首先讓我們看看可變變量到底是什么。讓我們先嘗試一下清單 4 中的代碼:
清單 4. 具有可變變量的代碼
<?php
$myStr = "I";
$$myStr = "am";
$$$myStr = "great.";
// These are new variables.
echo "$myStr ";
echo "$I ";
echo "$am\n";
// Now for the moment of truth ...";
$am = "exaggerating.";
// Does it work the other way?
echo "$myStr ";
echo "${$myStr} ";
echo "${${$myStr}}\n ";
?>
首先,清單 4 中的代碼聲明了名為 $myStr 的變量,并將字符串 I 賦給它。接下來的語句定義了另一個變量。但這次,變量的名稱是 $myStr 中的數(shù)據(jù)。$$myStr 是一種告訴 PHP 產(chǎn)生另一個變量的方法,其意思是“我想要一個變量,可以在變量 $myStr 中找到這個變量的名稱”。當然,為做到這一點,必須定義 $myStr。所以,現(xiàn)在您有一個名為 I 的變量,并用字符串 am 給它賦值。接下來的語句做了同樣的事情,并將字符串 great 賦給名為 am 的變量。而 am 只是變量 I 中的數(shù)據(jù)。
那澄清了一些事,但 echo 語句中那些奇怪的花括號是怎么回事呢?那就是您打印可變變量的方法。如果您省略花括號,那么 PHP 在打印時會將美元符號($)附加到變量的內(nèi)容上。這些花括號告訴 PHP 首先對它們里面的變量求值。
試著這樣考慮:變量可以做什么?簡單地說,它們抽象或表示數(shù)據(jù),這些數(shù)據(jù)可以變化,而它們的名稱保持不變?勺冏兞孔龅氖峭瑯拥氖虑;它們抽象數(shù)據(jù)。但在本例中,它們包含的數(shù)據(jù)實際上是另一個變量的名稱。
我在清單 4 中所給的例子是為了讓您熟悉可變變量?勺冏兞棵膶嶋H功能來自這樣的事實:您可以在運行時動態(tài)地生成可變的變量名。您將在構(gòu)造一個配置文件解析器時用到這一特性。
配置文件解析器
按照我的經(jīng)驗,在配置應(yīng)用程序以使其運行期間,用 PHP 編輯配置文件時,用戶常有所抱怨。配置只不過是全局變量的一個列表,應(yīng)用程序中的其它腳本依靠它來查找文件、URL 和其它控制應(yīng)用程序行為的設(shè)置。一個遺漏的美元符號、分號或一段未封閉的注釋常常會破壞整個代碼。有什么能幫助用戶呢?
假設(shè)您給用戶一個文件,讓用戶使用由等號分開的簡單的名稱-值對來編輯該文件。配置文件類似于清單 5。以 # 開頭的行的文本被作為注釋處理。
清單 5. 樣本配置文件
# This is sample a configuration file.
admin_fname = Amol
admin_lname = Hatwar
admin_email = amolhatwar@consultant.com
admin_login = admin
admin_pass = secretstring
# File Ends
意思清楚嗎?是的,的確如此……既然可以用 PHP 解析文件,為什么讓用戶編輯配置文件呢?事實上,這是人們非常期望的。請記住,您的應(yīng)用程序必須在對用戶隱藏所有復雜性的同時,仍然讓他知道他能控制該應(yīng)用程序。
您可以編寫負責解析工作的函數(shù),這樣您可以在任何地方使用它而不用做任何修改。讓我們將該任務(wù)分為一些更簡單的步驟:
逐行地讀取文件
丟棄一行中 # 號字符后的所有內(nèi)容
以等號為界,將一行分為兩個字符串,并丟棄等號
除去字符串中的額外空格
相應(yīng)聲明變量
要編寫最后一步,只有使用可變變量才行。清單 6 顯示了代碼:
清單 6. 解析函數(shù)
<?php
/* conf_parser.php */
/* Give the filename with path info whenever possible. */
function conf_parse($file_name) {
// @ in front makes the function quiet. Error messages are not printed.
$fp = @fopen($file_name, "r") or die("Cannot open $file_name");
while ($conf_line = @fgets($fp, 1024)) {
$line = ereg_replace("#.*$", "", $line); // Do stripping after hashes.
if ($line == "") continue; // Drop blank lines resulting from the previous step.
list($name, $value) = explode ('=', $line); // Drop '=' and split.
$name = trim($name); // Strip spaces.
$$name= trim($value); // Define the said variable.
}
fclose($fp) or die("Can't close file $file_name");
}
?>
用正則表達式除去 # 號。盡管這里的表達式很簡單,但要知道復雜的正則表達式會消耗大量的 CPU 時間。此外,為每頁反復地解析配置文件不是一個好的決策。更好的選擇是:使用變量或定義語句將已解析的輸出存儲為 PHP 腳本。我傾向于使用 define() 函數(shù)進行定義,因為一旦設(shè)置了值就不能在運行時更改它?梢栽趨⒖假Y料中找到一個能夠根據(jù)您的需要加以修改的實現(xiàn)。
結(jié)束語
既然知道了如何有效地使用變量,那么您可以著手編寫一些較大的程序了。在本系列的下一篇文章中,我將研究函數(shù)和 API 設(shè)計。在下次見面以前,希望您編程愉快!
參考資料
下載 def_parser.zip 文件,其中包含配置文件解析器的實現(xiàn),您可以將該解析器寫的文件包括在您的腳本中。
請訪問 PHPBuilder.com 和 Developer Shed,以獲取更多有關(guān)在 PHP 中使用可變變量名稱的示例。
請閱讀了解 Free Energy,這是一個對 Web 應(yīng)用程序進行編碼的簡單方法。
下載或在線查閱 PHP 手冊。
參加“Writing efficient PHP”教程,學習如何編寫有效的 PHP 代碼(developerWorks,2002 年 7 月)。
請閱讀本系列文章中的第一篇“奠定基礎(chǔ)”,了解有關(guān)設(shè)計和規(guī)劃 Web 應(yīng)用程序的要點(developerWorks,2002 年 8 月)。
關(guān)于作者
Amol Hatwar 從能記事起就開始接觸計算機。作為 GNU/Linux 的絕對擁護者,他為過去在 Microsoft 平臺上編程感到內(nèi)疚。他現(xiàn)在作為獨立顧問幫助眾多公司遷移到 GNU/Linux。作為開發(fā) Web 應(yīng)用程序領(lǐng)域的專家,他把所剩無幾的空余時間花在研究沒人聽說過的技術(shù)上。他現(xiàn)在的興趣包括開放源碼軟件、Web 服務(wù)、對等計算以及高可用性群集。您可以通過 amolhatwar@consultant.com 與 Amol 聯(lián)系。