用封裝類來合理的設計PHP項目--談PHP項目中類的封裝
最後更新:2017-02-28
來源:互聯網
上載者:User
封裝|設計|項目 編碼對於合格的PHP程式員來說並不是什麼難事(也許只是花費時間長短的問題),因此系統分析和設計這一階段就顯得尤為重要。不過本文並不打算討論和需求分析、擷取商業邏輯相關的話題,而是針對系統設計方面進行探討。
面臨難題
編碼對於合格的PHP程式員來說並不是什麼難事(也許只是花費時間長短的問題),因此系統分析和設計這一階段就顯得尤為重要。對於一個擔任PHP項目的系統分析員來說,面臨著兩個難題:
PHP語言本身的限制。
這一點在複雜系統的物件導向設計中尤其顯著。PHP的物件導向特性在現有版本中雖然得到了改善,但是還不甚健全,根本不足以擔任物件導向設計的實現語言;即使眼光長遠一些,在即將釋出的以Zend Engine 2.0支援的全新PHP中,物件導向特性也不會像現在流行的Java或者C++那樣(關於這方面的內容可以參見我在developerWorks中國網站發表的另一篇文章)。但是如果採用完全面向過程(準確說是面向Web頁面)的方式,可以想見整個系統的設計會非常複雜,而由此帶來的編碼複雜和維護困難更加難以應付。
現有資料的嚴重缺乏。
這是眾所周知的現象即針對Web項目的系統設計資料不足;而在這些有限資料中,關於PHP的設計資料又非常匱乏。如果本公司或本人也沒有相關的技術積累,系統分析員只能在黑暗中摸索方法(更壞的兩種情況,一是照搬其他項目比如Java或者C++的設計,二是認為項目簡單而不負責任的草草了事)。
認識面對的系統
既然如此,採用何種方法妥善處理PHP系統的分析和設計?最初的構想應該需要分清項目承擔任務的類型:
涉及大量客戶本身或者客戶所在行業的商業邏輯的項目,包括辦公系統、訂單系統以及其他商業系統。
簡單網站項目,包括一些需要承擔高訪問量或要求快速響應的項目比如品牌網站或者即時網頁以及其他一些網站。
綜合性網站項目。通常包含多個相對獨立的子系統比如新聞子系統、論罈子系統、產品陳列子系統等等。
PHP的設計初衷在於解決後兩種項目的迫切需求,語言本身對於這些項目進行了良好的改造。而眾多的PHP開發人員對這些項目也具有或多或少的經驗,相關書籍中的範例也大都圍繞於此。相對說來第一種系統所有的資料不多,各種出版物對其內容也很少提及。因此在本文中將題所述對第一種類型的項目進行詳細講述(有關MVC模式和類封裝),同時附帶提及第二種項目(有關駭客代碼)以及第三種項目的設計方法。當然,並不是被歸類的這些項目就只能採用本文描述的方式,系統分析員需要權衡各方面因素加以選擇。
方案一:涉及大量商業邏輯項目
如何分離使用者介面和後台操作?如何避免將商業邏輯混淆於一般的流程式控制制中?作為一個嚴謹的商用項目,就需要考慮很多類似的問題。對於由PHP擔當的這類項目,貫徹Model-View-Controller(MVC)模式的設計是一個非常好的方法。
理論描述
在這裡我不想多加解釋MVC模式本身--簡單的從字面上以及應用上說,通過將系統的設計分為Model模型/邏輯、View視圖/介面、Controller控制/流程三個邏輯部分達到良好的項目效果,以此便利各部分開發人員的工作並降低日後的維護成本。(如果您熟悉JSP開發的Model 2模式,可以發現它也是MVC模式的很好體現。)就現實的項目開發而言,現存的很大問題包括網頁設計人員和程式開發人員的工作交錯和衝突以及商業邏輯嵌入頁面造成不可重用也很難維護等等。引入MVC模式一方面可以為系統的總體設計指出明確的方向,對於Team Dev的分工也是良好的指導。
既然依照MVC模式要求對系統的總體結構在邏輯上分成三部分,那麼團隊的開發人員中也存在著針對各個部分的開發人員。
開發人員角色 相關係統邏輯 職責
網頁設計人員 View視圖/介面 設計所有使用者介面的網頁模板。
控制流程程開發人員 Controller控制/流程 編寫系統流程中的所有PHP頁面。
商業邏輯開發人員 Model模型/邏輯 開發系統設計中規定的各個類(其中的方法)。
由以上的表格可以看出,傳統的網頁設計和程式開發的人員分工被打破而取而代之的是根據系統邏輯劃定的職責。對於網頁設計人員,職責並沒有改變,準確說由於這樣的劃分避免了以往與程式設計人員的糾紛,他們完成的只是網頁模板,因此只需關注於純粹的網頁代碼(主要是HTML,也許會有其他用戶端的代碼比如WML之類)而根本不需要被伺服器端的<? … ?>幹擾。程式設計人員則被分為兩部分:其中比較容易把握的是商業邏輯開發人員,他們的任務是根據系統分析員給定的模組(準確說是類方法)完成之,在他們手中的PHP更像一般的程式設計語言(比如Java)而與Web沒有什麼關係;一時較難接受的是控制流程程開發人員,他們的任務是在實現系統設計時制定的系統流程的同時,根據用戶端的輸入調用商業邏輯(相應的類方法)以及輸出更新的介面(對設計網頁模板進行處理),在他們手中PHP可以充分發揮Web程式設計語言的優勢。
程式碼群組織相關的話題
這樣的觀念有些抽象,沒有執行個體的示範很難接受。在舉例之前先介紹一下我對這類工程的推薦代碼結構:
一級目錄 二級目錄 三級目錄 備忘
/project_name 項目原始碼根目錄
/Templates 網頁模板目錄(View)
/admin 管理主控台目錄/admin下的網頁模板
/Include 商業邏輯目錄(Model)
/Temp 臨時代碼目錄(可選),可供開發人員進行一些實驗代碼的測試
/images 圖片目錄,網頁模板採用
/css 樣式單目錄,網頁模板採用
/scripts 用戶端代碼目錄,網頁模板採用
/admin 管理主控台目錄(可選),包含所有後台管理的功能代碼
/other_dir 對應與原始碼根目錄下的/other_dir,包含管理該類的功能代碼
/other_dir 其他與相應功能相關的目錄,比如與使用者相關的/member目錄或者與從產品相關的/product目錄等等
/config.inc.php 全域組態變數,定義系統中的全域變數
/security.inc.php 安全性原則控制(可選)
/error.php 錯誤控制返回頁面(可選),也可以採用靜態頁面如/error.html或者其他頁面名稱
看完之後您是不是被喚起了一點使用Java進行Web開發的記憶?比如WEB-INF目錄下的classes目錄和lib目錄以及web.xml都是開發中的規則--雖然支援PHP的Web伺服器不可能像Java應用伺服器那樣自動載入這些目錄下的檔案,但是規定一個合適的程式碼群組織模式還是非常有利於開發的便利和後續的維護的。(我開始考慮PHP項目的代碼結構就是由Tomcat的開發手冊中獲得了啟發。)
一個使用者登入的例子
根據以上的代碼目錄,前文所說的MVC模式的實現可以得到更簡單的解釋。以最常見的使用者登入功能為例,設想/project_name目錄下有一個/member目錄包含有關於使用者的一切功能,其中包含了login.php頁面接受使用者登入使用的使用者名稱稱和密碼,index.php頁面是登入完成之後的使用者首頁,而/project_name目錄下的error.php是登入失敗後的錯誤顯示頁面。
使用者通過系統的其他部分請求進入使用者首頁即/member/index.php頁面,此時該頁面判斷使用者情況:已登入使用者則直接顯示本頁內容(可以採用檢查session等方法);未登入使用者則需要登入(重新導向到/member/login.php);出現了未知錯誤(重新導向到/error.php)。同時採取相應的反應。
假如使用者被引導至登入頁面即/member/login.php頁面,該頁面接受使用者的登入資訊(使用者名稱稱和密碼),並判斷是否正確登入:正確登入則再次重新導向到使用者首頁/member/index.php;登入錯誤則重新導向到/error.php。
假如使用者被引導至錯誤顯示頁面/error.php頁面(無論是從以上哪個頁面前來),都會顯示錯誤資訊。
流程圖示如下:
根據以上的文字描述和圖示,再結合MVC模式的實現,可以非常輕鬆的寫出這幾個頁面的架構代碼:
先看簡單的頁面/error.php:
然後是/member/login.php:
最後是/member/index.php:
(注意:以上代碼只是片斷,而且沒有考慮項目全域,只起示範作用)
關於Controller
首先可以明確的是,以上的三個頁面代碼就是前文所說的Controller控制/流程代碼。很明顯,他們的不包含特定的操作,也沒有一行網頁代碼,有的只是與前面流程圖一致的流程式控制制代碼(放眼望去,這些頁面的共同特點是充滿了引用網頁模板並輸出、取得對象並執行其某個方法或者重新導向)。
再選擇其中的一個頁面/member/login.php詳細的解釋。整個頁面通過判斷是否提交表單分為兩個部分:顯示登入表單供使用者填寫和處理登入資訊。作為前者直接引用一個處於網頁模板目錄/Templates下對應該頁面的member_login.dwt並在解析後輸出;作為後者先取得一個Member對象(該對象出於商業邏輯目錄/Include下的Member.inc.php中),然後獲得登入判斷的結果後進行重新導向。在這個控制頁面的代碼中,member_login.dwt作為View視圖/介面出現,類Member作為Model模型/邏輯出現,而頁面代碼本身就Controller控制/流程。下面就是加入標示的/member/login.php架構代碼:
(關於模板類以及在MVC模式中的應用,可以參考本站另一篇文章《在PHP中選擇合適的模板》)
關於Model
既然談到了Model,下面就是另一個重要的話題:類封裝在PHP項目中的應用。
請注意用詞"類封裝"--這和"物件導向"或者其他什麼"採用對象設計"的方法有著本質的不同。"類封裝"只是講述了將商業邏輯採用類方法的方式封裝成各個不同的類,因而這裡的"類"並不是因此採用了物件導向設計出現的"類"--準確的說,這裡的"類"其實是對一系列相關功能模組進行合并的結果。
為什麼不直接採用物件導向的方式而是採用這種看起來不倫不類的辦法去設計系統呢?PHP不是具有物件導向特性嗎?不錯,PHP具有這樣的特性,但是非常不完全(可以參考本站另一篇文章《從Zend Engine 2.0的設計藍圖(草稿)看PHP的將來》)。舉例來說,PHP是沒有介面這一概念和實現方法的,同時也就沒有什麼多重繼承、方法重載之類的典型物件導向特徵。如果非要採用物件導向的設計方法,也許在概要設計階段可以非常輕鬆,但是詳細設計階段就會比較苦悶,而如果還有幸堅持到編碼階段簡直就是苦不堪言了。另一方面,如果不在系統中引入類的概念,而是採用函數來實現模組功能,那麼可以想象在一個採用這樣"純粹"的中大型系統中會有多少的函數,由此帶來的麻煩非常明顯。
還是回到PHP語言本身。雖然PHP提供不了什麼實際的物件導向支援,但是還是提供了對類以及其中的屬性和方法的定義。那麼自然而然可以想到的是採用類的方法封裝相關函數模組,既可以借鑒一些對象設計的優點,又可以避免完全採用函數模組的一些缺點。
(一些採用函數模組的系統會採用這樣一種方式:將相關的函數編寫在相同的檔案中,這樣在引用時可以引入單獨的檔案。比如Member.func.php這個檔案中包含了所有與使用者相關的操作,在處理使用者登入時可以先require這個檔案,然後調用諸如member_login()這樣的函數。但是這樣的方式僅僅解決了系統中眾多函數的程式碼群組織問題,沒有解決名字衝突的問題。下面的舉例中就會看到。)
比如上文的使用者登入執行個體中,如果採用函數模組的方法,代碼也許是這樣:
而採用類封裝的方法,可能就是這樣:
也許您會覺得代碼並沒有什麼區別(甚至看起來採用函數模組的代碼由於不需要取得新的對象而顯得更簡潔一些),而真正的不同是發生在include的檔案裡面。採用函數模組的方法將相關的函數集合在一個檔案中加以組織(有些系統還不能做到這一點,那麼就會造成異常混亂的局面),而採用類封裝的方法在每一個檔案中聲明一個和檔案名稱相同的類(比如在Member.inc.php聲明一個Member的類,這一點和Java的規定相似);而在使用時,都需要先進行include(如果採用函數模組又沒有進行很好的組織,也許有些人就會很"簡便"的將所有函數include進每一個頁面--PHP可不是Java那樣編譯執行,光是解析這些函數就會花費一段時間),但是關鍵就在於採用類封裝的方法可以清楚的指明調用的位置--某個類(Member)的某個方法(login):從避免名字衝突的角度來說這一點是非常成功的;而對於代碼檢查和維護而言,方便程度更是不言而喻。設想一個頁面需要完成若干功能,因而需要include數個檔案:採用函數模組的方法不能夠輕易的從函數調用中找到函數本身所在的檔案(如果函數名稱或者include檔案名稱沒有什麼統一規則,那麼這個工作就非常艱巨了),而採用類封裝的辦法可以根據類名稱和類檔案名稱準確定位類方法代碼的位置。(也許您會認為這樣一個小小的好處不足掛齒,但是經曆一個維護工程之後也許就不會再有什麼異議。)
以上是採用類封裝方法的原因,決定採用這種方法設計系統只是第一步;完成整個系統的設計還有很多可以借鑒的經驗。
部分設計可以借鑒物件導向的思路。雖然PHP中沒有介面和抽象類別的定義,繼承機制也非常不完全,但至少具備了基本的類定義和簡單的繼承關係。類似"公司-僱員"、"賣家-商品-買家"這類顯而易見的關係可以很容易在系統中通過類和類別關係定義。既然PHP可以做到這一點,就按照實際的邏輯關係去定義即可。
經常會在系統中出現的另一個情況是關於個體和列表的關係--這樣說也許難以理解,想象一個BBS系統中的貼文清單和每個文章之間,就是這樣的關係。根據設計經驗,這樣的關係大量存在於PHP或者其他Web系統中。對於這類別關係,我個人建議可以採用以下Item和Item_List的類封裝方式:
Item類定義:Item.inc.php的代碼
Item_List類定義:Item_List.inc.php的代碼
由於PHP對於類的成員變數和方法並沒有文法上的訪問限制(均為公開),因此會帶來對象使用方面的某些混亂。基於此,建議在Team Dev的代碼規範中加以規定,從代碼應用的層級上控制這一情況:
首先,可以通過對成員變數和方法的注釋來說明其屬性,由此使用該對象的其他開發人員可以瞭解自己的使用方法是否觸犯了規定的訪問限制。(如果採用phpdoc等自動文檔產生的工具,開發人員甚至可以在不翻閱類源碼的情況下通過瀏覽類文檔正確使用它。)
其次,對於成員變數訪問限制的考慮,可以將一些主要的、經常需要被訪問或更改的變數(在注釋中)聲明為公開。這樣的作法可以省卻大量get()和set()方法的代碼--雖然在其他的物件導向語言中這一點被認為非常醜陋,但是記住PHP不是Java,只要這樣的用法合理,就應該大膽使用。
從上面的範例程式碼中您也許已經注意到了注釋的比重--雖然大家都瞭解注釋的重要性,但是仍然有必要提出。這個樣本中採用了Javadoc的樣式,利用現有工具也可以很容易的直接產生文檔(當然您和您的Team Dev也可以定義自己的合適注釋樣式和文檔產生工具)。對於系統分析員來說,您在設計階段完成之後交付給您的開發夥伴的代碼部分很可能就是這些注釋占絕大部分的架構代碼;你們之間交流的工具除了那些沒完沒了的圖表之外就是這些程式員最熟悉的代碼和注釋了。
在PHP系統中進行類的設計雖然不像構建物件導向系統那樣需要各種合理的模式介入(也沒有這樣的"本錢"為之),但還是需要一番思量的。邏輯上的合理性和操作上的可行性都是檢驗的標準。
(說到類設計,又想到了適合PHP開發的IDE問題。據我所知比較專業一些有Zend出品的Zend IDE;另外還有作為JBuilder的Open Tools出現的藉助JBuilder的PHP開發工具;不過最常用的還是PHPEd或者UltraEdit之類的編輯器。如果現有的編輯器可以非常聰明的支援PHP的類設計和代碼實現就非常理想了。)
關於View
最後說到的是View方面,雖然這部分內容與網頁設計人員聯絡比較緊密,不過PHP項目(以及其他Web項目)的系統分析員也必須關注這一話題。可以看出MVC模式的應用使得網頁開發人員和程式設計人員的各自工作成果不會像以前那樣互相影響,自然可以提高各自的工作效率(相互關係也許會比以前更加融洽一些)。但是對於系統分析員來說,將使用者介面分離為各個獨立的網頁模板需要進行許多分析工作。
首先是確定整個系統的流程,這一點在系統設計的初期就應該做到。而對於View視圖/介面和Controller控制/流程來說,所有需要的頁面都是圍繞此流程產生。不過通常此時能夠在流程圖上看到的也許只是相關的參數在各個頁面之間傳遞,卻不能瞭解各個頁面展示的內容--這就是下一步分析使用者介面需要進行的工作。
在分析使用者介面的工作中,第一步可以確定各個頁面核心、對於完成流程必不可少的使用者介面元素(表單和表單域、連結等);第二步是確定頁面中需要出現的導航內容;最後還需要依據流程複核。還是以上文的使用者登入狀態例。對於/member/login.php這個關鍵的頁面,第一步可以確定的是在使用者提交之前應該顯示一個表單,表單包含兩個文字框供使用者輸入使用者名稱稱和密碼;而提交之後根據流程在本頁面中不需要有使用者介面,取而代之的是利用Controller控制/流程這一邏輯層進行重新導向。而第二步需要制定該頁面中(準確說是在顯示登入表單時)需要提供的導航連結,在這裡可以加上到系統的首頁或者其他非註冊使用者頁的起點的連結(方便使用者臨時決定取消登入)以及一個登出現有使用者的連結(針對已登入使用者)。之後進行複核,此時也許會發現這一設計似乎沒有考慮到在登入前更好的區別是管理員登入還是普通使用者登入,那麼就可以在表單中增加一個隱藏欄位表示選擇登入的使用者是準備以管理員還是普通使用者的身份進行登入。
確定完使用者介面的元素,並不意味著可以將這些分析結果交付網頁設計人員進行製作了;還有最關鍵的一步沒有實施--為分析完成的各個頁面制定模板所需的變數名稱。對於以上的使用者登入執行個體,如果系統有識別曾經登入使用者的功能(依據之前訪問時在用戶端設定的相關cookie值)並且把這個使用者名稱稱顯示在登入表單的使用者名稱稱一欄,此時就需要在member_login.dwt設計中說明該表單域將被賦值為一個模板變數(比如{USERNAME})。這一步驟完成之後就可以交付網頁設計人員進行製作了。
需要指出的是,在編碼階段很可能局部的一些系統設計需要進行修改,這其中也許就包括對網頁模板的修改,需要仔細處理。
對於程式碼群組織的補充說明
還有幾個檔案和目錄沒有在上文提及:
config.inc.php -- 如果您熟悉phpMyAdmin或者其他phpWizard.net釋出的項目,就應該非常清楚這個檔案的作用:定義本專案範圍內的全域變數(在每個頁面中被include)。我個人認為這是一個非常良好的設計,因此也提倡在項目中應用。另外,為了保證與項目中其他的變數衝突,建議在該檔案中定義一個多重數組,而各種全域變數都以該數組的某一個值出現。這樣方便團隊中的其他開發人員只需要避免一個變數名的使用,而不是避免所有config.inc.php中出現的變數名。使用這個檔案的另一個好處是由於將關鍵的變數(比如與伺服器環境相關的變數)集中定義,可以方便的安裝和移植整個項目。
security.inc.php -- 顧名思義這個檔案控制並實施整個系統的安全性原則。關於安全問題,可以想到的是兩種控制方案:在每個控制流程程頁面頂端針對本頁面加以控制以及採用一個控制檔案整個控制並被加入每個控制流程程頁面。我個人提倡採用後一種方式,原因也很簡單:定義簡單而且維護方便。雖然相比每個頁面單獨定義,也許會損失一點點效率(一些不需要安全控制的頁面也需要include該檔案並進行判別),但是獲得的是對系統安全的整體控制以及代碼維護的便利(損失一個if…else…的判別換取這樣的結果還是很值得的)。
/Temp -- 很明顯存在於這個目錄下的都是一個臨時檔案,並且這個目錄其實並不會出現在項目正式發行的版本中。如果開發時對一些函數的使用不甚明了或者實驗一段沒有相關經驗的代碼,都可以在此目錄下建立檔案;因為該目錄就位於項目代碼之中,可以非常便利的取得項目啟動並執行上下文環境,大大降低了實驗代碼的成本。
/admin -- 通常對系統的後台管理內容應該放置在一個獨立的目錄中,我個人比較喜歡admin這個簡寫詞(當然也有一些情況系統分析員認為不應該設定一個容易猜測的管理目錄名稱以增加一重對系統安全的保護)。
/css和/scripts -- 都是與網頁設計也就是View視圖/介面有關的檔案存放處,分別是樣式單和用戶端指令碼。這樣做的好處在任何一本講述網站規劃的書籍中都會有所提及。
方案二:簡單網站項目
系統效能是這類項目追求的首要目標,而與此同時系統的維護和擴充幾乎可以不用多加考慮。(也許這句話聽起來有些絕對,但是根據客戶的需求和項目的性質判斷,盡最大可能以最短時間滿足客戶的需求並使得系統高效運轉就是項目成功的最好檢驗標準。)因此,也許這類項目就是PHP駭客的天堂(曾經我也是一個過分追求PHP使用效率的人)。由於這類項目的特殊性,這裡討論的範圍不僅僅局限與系統設計而是從組建項目小組開始直到交付項目的過程。
首先需要關注的是參與項目的人選(雖然也許這是專案經理的職責,但是最熟悉PHP項目特點的系統分析員應該參與)。在PHP開發人員方面,至少應該選擇對PHP各種函數較為熟悉的開發人員(這類項目不適合作為現實項目以培訓參與的開發新人),如果公司中還有能夠在源碼層級理解PHP的人員就更加理想(不過通常對於一般的PHP開發公司是不可能的)。而在網頁設計人員方面,最好可以選擇一些略通用戶端(比如JavaScript)以及伺服器端(最好是PHP)指令碼的人員;因為這類項目的一大特點即是單個網頁代碼量較大且夾雜網頁代碼(通常是HTML)、用戶端指令碼(比如JavaScript)和伺服器端指令碼(比如PHP),加入瞭解各種指令碼語言的網頁設計人員的目的不是為了增加團隊的PHP開發力量,而是避免在修改網頁時影響程式設計人員的工作。
其次就是面向過程,準確說是面向頁面的系統設計。相對第一類項目,客戶的需求在該類項目中表現得非常清晰,而且一般長期進行Web開發的公司對於這類網站項目也應該有一定的設計經驗積累。設計中需要圍繞整個系統的流程,包括每個頁面的輸入參數和輸出內容(包括網頁中出現的除導航連結之外的功能性連結),以求完全滿足客戶的需求;另一關鍵在於確定系統安全性原則,在這類項目中主要是使用者等級的確定和頁面的存取權限,並給出實現的方式。不過還需要指出的是,這類項目中由編碼階段返回設計階段的情況並不少見,對於局部設計(比如頁面傳入參數或者輸出連結)的更改應該加以及時控制。
最後是針對代碼和資料庫的最佳化。在這類項目中需要適當鼓勵開發人員的駭客態度。推薦的辦法是系統分析員給出每個頁面的虛擬碼(架構代碼),而局部的實現則由各個程式開發人員和網頁設計人員進行。
對於PHP代碼方面,通常可以從如下幾方面考慮:
演算法的選擇和功能實現的方式:模組層級別的最佳化,可以由幾名開發人員共同討論解決;
函數的使用:代碼層級的最佳化,需要開發人員對各類函數有清楚的認識,至少養成多多參考函數手冊的習慣;
資料庫的查詢和更改即SQL語句的使用:如果公司中有相關資料庫系統的管理員,可以就一些最佳化問題徵詢他們的建議;
其他應該避免的問題:比如拷貝代碼、等不良代碼情況。
而根據我的經驗,通常會在這類項目中撰寫的駭客代碼如下:
迴圈語句的使用特別是在尋找時的應用:此時注意while和for的區別(想必大家在大學課堂中都做過這類的程式),這也是良好的編程習慣;
SQL語句的最佳化:首先是盡量避免多餘的資料庫互動,這是提高效率非常重要的一點;其次是不要害怕長達幾行的語句而寧願使用所謂簡單的語句;再次是認真考慮查詢語句返回的欄位,減少不必要的資料。
表單提交值的擷取,比如複選框和文本域。精巧的表單網域名稱稱設計可以減少一定的代碼量,而處理提交值時也需要注意處理的方式。
駭客代碼在這類項目中值得鼓勵,不過最好在每段代碼旁附上儘可能詳細的注釋。
由於該類項目的特殊性,完成項目的關鍵不僅僅在於系統設計階段,因此給出項目開始、系統設計、編碼以及測試、交付這一過程的簡單描述:
挑選合適人員組成項目小組,可以考慮銷售人員和客戶代表的加入。
系統分析員可以簡單的從客戶的需求以及以往項目經驗的結合中總結出系統所需的每個網頁並對其功能作出描述,同時確定初步的安全性原則。這一步驟中可以加入銷售人員和客戶代表的加入。(此時網頁設計人員正在準備提供給客戶的一系列網站形象頁面。)
詳細設計中需要為每個頁面確定位置和名稱,更加關鍵的是確定輸入參數和輸出內容以及不同層級使用者對於網頁的確切存取權限。同時進行資料庫設計。該階段完成後至少應該提供系統的流程圖(包括存取權限標識)以及資料庫設計資料。
網頁設計人員和程式開發人員拿到相關資料各自進行工作。對於前者,根據客戶認可的一套形象設計每個頁面;對於後者,開始進行"興奮的"(因為此時要求的是高效簡介的代碼--駭客代碼)編碼工作。此階段工作中遇到的困難均需要反饋到系統分析員處,可能返回以上的第3步甚至第2步進行設計修改。
程式編寫和網頁設計結束後需要有一段整合的時間,也是程式開發人員對代碼進行自我測試的階段。同時在這一階段可以進行的是代碼(包括網頁代碼和程式碼)和資料庫的最佳化工作。此階段結束後應該可以提供一個完整的系統。
真正的測試階段通常都比較倉促,這方面的技術和經驗公司也應該有一定積累(如果有條件希望採用一些軟體工具進行穩定性和抗壓能力的測試)。最後是提供一個可Web訪問的地址供客戶測試。此階段完成後可以提供正式交付客戶的系統。
方案三:綜合性網站項目
已經有一些大型網站使用PHP作為主要的開發語言。對於這類項目,單純從PHP技術方面值得提出的話題不多,簡而言之還是根據網站各部分的實際應用情況(訪問強度、操作行為等)選擇以上提出的兩種項目設計方法或者綜合使用。除此之外,根據我個人的經驗,項目團隊的組織和協調工作以及項目各期完成後的維護工作等等是較之單純的技術更加關鍵的因素。
對於這類項目,可以提出的建議是,適當採納一些開源軟體對於快速、優質的完成項目很有好處。項目的某些部分可以直接引入開源軟體項目的設計甚至是代碼,不過前提是系統設計人員對這些引入的項目需要非常瞭解,同時需要做好這些孤立的開源項目和整個項目之間的接合(比如安全性原則的考慮和全域變數的引用等)。
舉例來說,根據客戶要求某個綜合網站需要以下的功能:
複雜的新聞發布;
需要不多管理功能的線上論壇;
簡單的產品陳列;
需要使用者管理。
(很明顯這是一個企業網站的雛形。)
其中的1、2項很明顯可以借用一些成熟的開源軟體項目,而3項由於客戶需求簡單自主開發比較符合成本。由此看來4項則是整個系統中最重要的部分--需要做好與1、2項使用的開源軟體項目的使用者管理整合工作(3項由於自主開發的原因整合工作非常簡單)。(某些技術積累較好的公司甚至對於以上提及的整合部分都有簡單的解決方案,那麼這樣一個網站項目的完成所需成本非常微小。)
幾個特殊的功能點
另外還有一些通常項目中都會出現但是必須妥善處理的功能點:
1. 資料列表分頁。
關於這個功能,互連網上的中文和英文資料都有許多。具體的技術和實施細節不需要多說,這裡只需要指出的是:
A. 建議封裝成某一個工具類的方法或者其他可複用的形式--這樣的好處不言自明,任何人都不希望系統中只要存在資料列表分頁的時候都會出現一堆幾乎相同的代碼。
B. 如果針對一些效率要求較高的項目(例如上文提到的"簡單網站項目"類型),應該直接使用PHP內建的針對特定資料庫系統的操作函數以及與該資料庫系統相關的結果集截取技術(SQL語句),比如MySQL中的'LIMIT start, offset'之類;其他一些需要系統設計工整合理的項目(例如上文提到的"設計大量商業邏輯項目"),如果採用了通用的資料庫介面,出於相容多種資料庫系統的考慮,可以採用此介面完成結果集的篩選,以損失的效率換取系統更好的可維護性和可擴充性。也就是說,對於採用特定資料庫操作函數還是第三方通用資料庫介面來實現資料列表分頁,需要考慮系統的效能和擴充兩方面因素。
2. 錯誤控制。
這一點在上文之中也有提及。除了建立屬於工具類的錯誤類之外,最好可以建立專門的錯誤顯示頁面。該頁面既可以是靜態HTML頁面(表達一些對使用者的歉意和出錯之後的處理指導)或者動態PHP頁面(可以包含具體的出錯原因和地點以及其他更詳細的資訊,前提是在系統安全性原則允許提供這些資訊)。而錯誤類的任務就是接受正常的程式中拋出的錯誤,進行必要處理之後將資訊一起重新導向在錯誤顯示頁面上。
同時,建立出錯頁面對於開發階段也有一定好處,可以彌補現有PHP缺少類似try{…} catch{…} 塊的違例控制的缺點,將調試中的錯誤或者輸出通過錯誤類拋出並顯示出來。
3.上傳與下載。
對於PHP來說,上傳的實現並不會像其他流行的Web開發語言那樣需要第三方程式的支援,內建的機制可以非常簡單的處理。不過這裡提及的是一些複雜的上傳功能實現。考察以下一個處理附加檔案的流程:
該功能使得使用者在撰寫新的訊息時可以附加其他檔案,而且在訊息沒有提交之前可以隨意的對已經附加的檔案進行刪除或者繼續增加。這種需求會體現在許多辦公相關的系統中,作為有經驗的系統分析員應該在系統設計階段制定完成針對該類功能的實施計劃。比如在圖中所示的流程中,其實是通過一個或者多個表單的互相提交完成(具體設計不再贅述,提供相關的PHP檔案參考;另外的一個直觀的例子就是多數免費郵件系統的添加附件功能,如果有興趣可以考察一下)。
至於下載,將檔案置於伺服器Web可訪問目錄下、提供訪問者真實檔案路徑是最簡單的解決辦法;不過一些系統中對檔案的下載基於某些安全性原則需要進行身份方面的判別方可予以下載,這樣的方式就會帶來隱患。通常採用的方式也許是將檔案放置在Web可訪問目錄以外的伺服器檔案系統中或者儲存進資料庫系統--都需要一個簡單的程式取得檔案內容並直接返回給發出請求的使用者(這其中涉及到一些HTTP輸出頭的問題請注意,提供一個PHP檔案代替具體敘述)。在系統設計時針對不同的需求可以採用相應的辦法。
4. 客戶會話session的保持
PHP的現有版本已經內建了對session的支援,通常項目中都使用這樣的方式;一些特殊需要的項目(比如分布系統)也許會採用複雜一些的處理方式。
在用戶端,通常採用的是設定cookie以識別特定的客戶,另一個可以應付不支援cookie的用戶端的方法是採用URL重寫加入足夠標示特定客戶的字串。從這方面來說,採用PHP內建的session支援最為理想,因為它可以自動的進行用戶端的FALLBACK:如果用戶端支援cookie,那麼就順其自然;如果cookie不被支援,就採用URL重寫方式--一切都不需要開發人員幹預。如果採用其他session處理方式,或者自己編寫適應需要的session庫,需要注意的就是怎樣處理用戶端儲存資料的問題--cookie還是URL重寫還是兩者兼顧。
在伺服器端,簡單說來只需要針對以字串標示的每個特定客戶儲存相關的資料即可--可以採用的方式多種多樣,通常的方式是檔案和資料庫。PHP內建的session支援中,預設的支援方式是在系統的臨時目錄或者制定的目錄下為每個客戶建立一個檔案儲存體其資料;當然也可以修改設定使其支援資料庫的方式或者其他方式。如果自己編寫session庫,根據系統的需要選擇一種合適的儲存方式即可。值得指出的是,對於分布系統,如何共用伺服器端的session資訊是需要極大關注的。
另外,在設定session變數的時候,PHP內建的session庫支援對象作為變數值(實際上所有的變數值,不論是一般的變數還是數組或是對象,都在經過序列化之後被儲存),也就是說,以下代碼是可用的:
這一點對於上文提到的一些商用系統是有益的:首先,可以使用關於使用者的對象作為一個session變數值儲存一套資訊,而不是割裂的多個session變數;其次,如果具有類似購物車的功能,可以以非常符合整個系統設計的方式(即前文所述商用系統的設計方式,強調類封裝)將該購物車對象放入session中。
5. ……其他和特定項目有關的功能點……
如果能在系統設計階段就預見並解決這些功能點固然很好,即使有少量未發現的功能點遺留到了編碼階段也並不可怕--通常這樣的遺漏並不會影響整個系統的架構,只是需要返回設計階段加入相應的內容和文檔即可。畢竟對於相關項目經驗不太豐富的系統分析員來說,做到設計階段對這類功能點瞭然於胸是不太現實的。
關於其他
對於PHP的爭論從前很多,不過自從Java在Web方面的優勢越來越進入人們的視野之後,這樣的爭論倒偃旗息鼓了--看來大家都達成了共識--PHP對於嚴謹的商用系統還是無能為力。不過基於此就一味否定PHP在商用系統中的應用也不大客觀,畢竟PHP還具有低成本的優勢(這裡的成本包括開發成本、使用成本和維護成本)。本文的目的除了講述一些PHP系統設計的方法之外,也希望吸引一些開發人員或者企業採用PHP構建合適的商用系統。
另外,本文僅僅是我自己的一些經驗,如果您看到這裡時候已經有了自己的一些想法,我非常樂意與您分享--能夠推動如PHP這樣無商業支援的開源軟體的發展,畢竟是一件非常令人興奮的事情。(從這方面來說,我甚至想撰寫關於PHP開發的文檔資料和示範項目,就如同Sun Microsystems為J2EE發布的Blueprint和Java Pet Store--可惜暫時受到時間、精力以及個人能力的限制--也許春節假期是一個好時機:)
參考資料
本文中提及的文章和代碼
代碼:使用者登入執行個體(包括控制頁面index.php,login.php;網頁模板member_index.dwt,member_login.dwt;邏輯類Member.inc.php)相關附件;
代碼:類封裝執行個體(Item類定義Item.inc.php;Item_List類定義Item_List.inc.php)相關附件;
代碼:上傳執行個體(控制頁面add.php,attach.php)相關附件;
代碼:下載執行個體(控制頁面download.php)相關附件;
代碼:執行個體程式碼群組織中的兩個通用檔案(config.inc.php,security.inc.php)相關附件;
文章:關於PHP的未來;
文章:模板在PHP中的使用。
其他參考資料
PHP官方網站--http://www.php.net
包含軟體和文檔以及使用方式等(本文成文時的最近動向是PHP 4.1.0釋出,在某些方面有較大改進)。
為PHP供應商業支援的Zend公司--http://www.zend.com
包含PHP相關的工具和文字資料(可以尋找到一些與本文主題相關的話題)。
著名的開放源碼專案網站SoureForge--http://www.sourceforge.net
開放源碼項目的聚集地,並提供基於Web的各種便利工具(可以尋找到成千上萬PHP撰寫的項目)。