PHP沉思錄-第四篇-Zend Framework-左輕侯-《程序員》2007年12月號

本文發表於《程序員》雜誌2007年第12期,略有刪節。    
  PHP沉思錄之四:Zend Framework 
  左輕侯 
  2007.11.11 
   
   從理論上來講,PHP是一種通用的動態語言,它能夠替代Perl實現通用的腳本,甚至能夠建立客戶端GUI程序(經過GTK+)。可是,在實際應用中,PHP在絕大多數狀況下都被用來開發Web應用。即便在Java和.NET這樣有軟件業界巨頭支持的重量級競爭對手面前,出身草莽的PHP也絕不遜色,尤爲是在應用最爲普遍的輕量級web開發領域,PHP一直緊緊佔據着領先的位置。在這一領域參與競爭的其餘語言,例如Python和Perl,雖然各具特點,可是仍然沒法撼動PHP的地位。PHP是當之無愧的「Web開發第一語言」,並且彷佛沒有什麼能對它構成挑戰。隨着web應用在軟件界的地位愈來愈重要,PHP也逐漸從腳本小子手中的玩具,轉變成重要的工業語言,並得到了IBM這樣的巨人的支持。 
   可是,就在近兩三年,一種新的Web開發解決方案的迅速崛起,震動了整個業界,讓PHP開始感到王座不穩。這個解決方案,固然就是Ruby on Rails。 
   本文無心對PHP和Ruby這兩種語言進行全面的比較(雖然這的確是一個很是有意思的話題)。不管讀者對Ruby和ROR持什麼見解,有一點倒是不爭的事實:Ruby做爲一種新的語言,雖然極具特點,可是並無獲得業界的廣泛接受;只有在ROR誕生之後,以其驚人的生產力征服了無數開發者,Ruby這才一飛沖天。也就是說,Ruby至於在很大程度上是依靠ROR這個框架,才能起迅速崛起。 
   對於感到嚴重威脅的PHP社區,若是要對Ruby進行有效的反擊,這彷佛是一條可行的道路:基於PHP語言,實現一個新的web開發框架,可以達到甚至超過ROR那樣的生產力。這既是對Ruby的反擊,其實也是PHP自身發展的必然結果。由於PHP在其發展過程當中,早已經出現了數十個各類各樣的框架,只是尚沒有一個能具備ROR那樣的生產力和影響力。 
   可是,實現這樣一個框架,也面臨着很多的問題: 
   
   PHP語言自己可否完成這一任務?PHP本來只是用於快速解決web應用的簡單腳本,雖然通過不斷的發展,已經具備了許多高級的語言特徵,可是是否可以實現象ROR那樣精巧和強大的框架,可以達到ROR那樣的生產力? 
   PHP社羣可否接受這樣一個框架?PHP的使用者,大多數是腳本小子出身,習慣於快速、敏捷、直觀的開發方式,對於重量級的框架,未必可以廣泛認同。 
   
   不管這些問題的答案如何,重要的是,有人這麼作了。Zend Framework就是這一思路的結果。 
   如上所述,Zend Framework就是這樣一個徹底基於PHP語言的、向ROR學習的、針對web應用開發的框架。與其它同類的框架相比,Zend Framework有兩個特色讓它顯得不同凡響: 
   
   Zend Framework由Zend公司開發,所以它是一個「官方的」框架。衆所周知,Zend公司是PHP編譯器的維護者,也是Zend Optimizer、Zend Studio等一系列PHP相關產品的擁有者。因爲這一關係,Zend Framework雖然沒有被內置到PHP發行包中,但也算得上PHP的官方解決方案了。(順便說句題外話,Zend公司最近發佈了Zend Studio for Eclipse的beta版本,有興趣的讀者不妨嘗試一下。) 
   Zend Framework跟ROR實在太象了。和ROR同樣,Zend Framework使用了一樣的Front Controller模式,一樣的Model/Controller/View模型,一樣的ORM思路,甚至連命名規則都十分類似。固然了,Zend Framework跟ROR的不一樣之處仍是不少的,至於底層的實現細節,確定區別更大。可是,無論是有意仍是無心(好吧,我認可「無心」絕對不可能作成這樣),Zend Framework的實現思路和ROR是很是類似的。 
   
  這是一個典型的Zend Framework應用的目錄: 
   
  /root 
  /application 
  /controllers 
  /models 
  /views 
  /filters 
  /helpers 
  /scripts 
  /library 
  /public 
  /images 
  /scripts 
  /styles 
   
   其中,library目錄下存放的是Zend Framework全部的庫文件,若是還有其餘第三方的庫文件,也能夠存放在這裏。Public目錄是惟一一個能夠經過web方式直接訪問的目錄,但它並不存放php文件,只存放CSS、JavaScript腳本、圖片等靜態文件。Application目錄中存放着實現應用邏輯的全部PHP文件,它又擁有三個子目錄:controllers、models、views。顧名思義,它們分別用於存放控制器/模型/視圖的相應PHP文件。 
   Zend Framework實現了Front Controller模式,這意味着,全部的http請求都會被轉發到同一個入口,而後再路由到相應的控制器。這個入口就是root根目錄下的index.php文件。Zend Framework經過URL Rewrite技術來實現這一點。在root根目錄下,存在一個.htaccess文件,內容以下: 
   
  RewriteEngine on 
  RewriteRule .* index.php 
  php_flag magic_quotes_gpc off 
  php_flag register_globals off 
   
   熟悉apache的URL Rewrite技術的讀者,很容易理解它的意義:將全部的請求轉發到index.php。固然,對於圖片之類的靜態文件的請求不須要被轉發,這能夠經過在public目錄下放置另外一個.htaccess來覆蓋上級目錄的Rewrite規則。 
   (習慣在IIS下運行PHP的讀者可能會問,Zend Framework是否能夠在IIS下運行呢?因爲IIS不支持基於. htaccess的URL Rewrite,所以Zend Framework沒法簡單地在IIS下運行。可是,因爲IIS支持基於HttpModule的URL Rewrite,所以理論上通過某些修改,是有可能讓Zend Framework在IIS下運行的。有興趣的讀者能夠自行嘗試。) 
   Index.php的功能是將http請求轉發到相關的控制器,另外,它還須要完成一些初始化時期的配置工做。它的內容以下: 
   
  <?php 
  error_reporting(E_ALL|E_STRICT); 
  date_default_timezone_set('Europe/London'); 
  set_include_path('.' . PATH_SEPARATOR . './library' 
  . PATH_SEPARATOR . './application/models/' 
  . PATH_SEPARATOR . get_include_path()); 
  include "Zend/Loader.php"; 
  Zend_Loader::loadClass('Zend_Controller_Front'); 
  // setup controller 
  $frontController = Zend_Controller_Front::getInstance(); 
  $frontController->throwExceptions(true); 
  $frontController->setControllerDirectory('./application/controllers'); 
  // run! 
  $frontController->dispatch(); 
  ?> 
   
   開始的幾行,分別用於配置報錯等級、設置時區、設置包含文件路徑,以及載入相關文件。Zend_Loader::loadClass是一個靜態方法,用於裝載指定的類。在載入Zend_Controller_Front之後,程序創建了這個類的實例,通知它拋出全部的異常,而且設置了控制器所在的目錄。最後,調用dispatch()方法,將http請求轉發到相應的控制器。 
  在application/controllers目錄下,存放着全部的控制器類。那麼路由器是根據什麼規則尋找相應的控制器呢?很簡單,它按照必定的命名規範來進行匹配。下面是一個控制器文件的內容: 
   
  root/application/controllers/IndexController.php 
   
  <?php 
  class IndexController extends Zend_Controller_Action { 
  function indexAction() { 
  echo "<p>in IndexController::indexAction()</p>"; 
  } 
  function addAction() { 
  echo "<p>in IndexController::addAction()</p>"; 
  } 
  function editAction() { 
  echo "<p>in IndexController::editAction()</p>"; 
  } 
  function deleteAction() { 
  echo "<p>in IndexController::deleteAction()</p>"; 
  } 
  } 
   
   全部的控制器類都派生自Zend_Controller_Action。控制器名字必須大寫字母開頭,其餘字母必須都爲小寫,最後加上Controller結尾,而且存放在同名的php文件中。本例中的控制器是IndexController。(須要指出的是,「其餘字母必須都爲小寫」是一條很詭異的規則,這意味着CurrentUserController這樣的名字是不合法的,沒法被正確地路由。若是你這樣作了,會是個很難發現的bug。) 
   控制器擁有一個或多個行爲(action),體如今代碼上,行爲是控制器類擁有的public方法,以小寫字母開頭,以Action結尾。在本例中,indexAction、deleteAction、addAction和editAction是IndexController的四個行爲。 
   在瀏覽器中,能夠經過「控制器/行爲」的URL,來訪問相應的行爲。如上例,URL和行爲的對應關係是: 
   
  URL Action 
  http://localhost/root/ 
   IndexController::indexAction() 
  http://localhost/root/index 
   IndexController::indexAction() 
  http://localhost/root/index/add IndexController::addAction() 
  http://localhost/root/index/edit IndexController::editAction() 
  http://localhost/root/index/delete IndexController::deleteAction() 
   
   能夠看到,indexAction是一個控制器的缺省行爲:若是沒有指定行爲,路由器將把請求轉發到indexAction方法。一樣,IndexController是缺省的控制器:若是沒有指定控制器,路由器將把請求轉發到IndexController控制器。 
   在上例中,行爲方法直接輸出一行代碼做爲響應。在規範的應用中,與顯示相關的代碼應該存放在視圖中。一個規範的控制器行爲方法內容以下: 
   
  function indexAction() { 
  $view = new Zend_View(); 
  $view->setScriptPath('./application/views/scripts’); 
  $view->title = "Hello world"; 
  echo $view->render(); 
  } 
   
   首先,建立了一個視圖類(Zend_View)的實例,而後設置其路徑,並對它的title屬性賦值,最後調用render()方法,渲染相應的模板文件,將結果返回給瀏覽器。 
   一樣的,視圖根據特定的命名規範來匹配相應的模板文件。對於IndexController::indexAction()方法,視圖會查找以下模板文件:root/application/views/scripts/index/index.phtml。後綴名爲.phtml的模板文件事實上就是一個標準的PHP文件。(熟悉.rhtml後綴名的讀者有何感受?)在本例中,index.phtml的內容以下: 
   
  <html> 
  <head> 
  <title><?php echo $this->escape($this->title); ?></title> 
  </head> 
  <body> 
  <h1><?php echo $this->escape($this->title); ?></h1> 
  </body> 
  </html> 
   
   最後,咱們終於又一次看到了親切的「Hello world」。 
  Zend Framework使用Zend_Db_Table來實現相似於ActiveRecord的ORM功能。和任何ORM同樣,首先須要爲它配置數據庫的相關信息(服務器,用戶名,密碼,數據庫名,等等)。雖然這並不複雜,但本文不擬討論這些細節問題。(Zend Framework在它的Roadmap中計劃支持YAML!)一旦配置完成後,能夠在/application/models子目錄下建立和數據庫表相對應的模型類。
  舉例來講,在數據庫中存在表user,結構以下: 
   
  CREATE TABLE user ( 
  id int(11) NOT NULL auto_increment, 
  name varchar(100) NOT NULL, 
  password varchar(100) NOT NULL, 
  PRIMARY KEY (id) 
  ) 
   
   相應地,能夠在models目錄下建立一個模型: 
   
  root/application/models/User.php 
   
  <?php 
  class User extends Zend_Db_Table 
  { 
  protected $_name = 'user'; 
  } 
   
   全部數據庫相關的代碼,都已經被封裝在抽象類Zend_Db_Table中。有ROR經驗的讀者,能夠和ROR中的模型對比一下: 
   
  Class User < ActiveRecord::Base 
  End 
   
   Zend Framework的模型僅僅多出了一行,就是把數據庫表名賦予$_name屬性。 
   下面來看看怎麼使用這個User模型類。新的indexAction方法以下: 
   
  function indexAction() { 
  … 
  $user = new User(); 
  $view->user = $user->fetchAll(); 
  echo $view->render(); 
  } 
   
   User類的fetchAll()返回一個數組,包含該表中全部的內容。這個數組被存放在$view的user屬性。接下來,看看新的index.phtml: 
   
  … 
  <table> 
  <tr> 
  <th>Id</th> 
  <th>Name</th> 
  </tr> 
  <?php foreach($this->user as $user) : ?> 
  <tr> 
  <td><?php echo $this->escape($user->id);?></td> 
  <td><?php echo $this->escape($user->name);?></td> 
  </tr> 
  <?php endforeach; ?> 
  </table> 
  … 
   
   這段代碼遍歷了$this->user,並將它的屬性依次打印出來。 
   除了基本的MVC模型之外,Zend Framework還提供了一系列高級功能,下面是一個不全面的列表: 
   
   Zend_Acl(access control list):實現了很是靈活的權限控制機制,經過對特定的角色和資源進行規則定義,能夠方便地實現各類複雜的權限控制規則,而且權限能夠按照角色的樹型結構實現繼承。 
   Zend_Cache:提供了一種通用的數據緩存方式,能夠將任何數據緩存到文件系統、數據庫、內存,可以與Memcached和SQLite這樣的第三方軟件很好地集成。 
   Zend_Log:提供了一種通用的log解決方案,支持多個log後端,支持log信息格式化,支持信息過濾。 
   Zend_Pdf:徹底用PHP語言實現的PDF文件操做引擎。 
   Zend_Feed:封裝了對RSS和ATOM的操做。 
   Zend_Gdata:封裝了對Google Data API的操做。 
   Zend_Json:JSON是AJAX中必不可少的數據格式。Zend_Json封裝了數據在PHP和JSON格式之間的轉換操做。 
   Zend_Rest:REST是愈來愈流行的Web Service協議,有逐漸凌駕傳統的SOAP協議之勢。Zend_Rest封裝了對REST的操做,能夠方便地實現REST的客戶端或服務端應用。 
   Zend_Search_Lucene:封裝了對著名的全文檢索引擎Lucene的操做。 
   
  Zend Framework在7月份發佈1.0正式版,如今最新的版本爲1.0.2。能夠說,Zend Framework仍是一個很是新的事物。可是在它漫長的測試期間,已經有很多人在嘗試,甚至用於正式的項目,由於它的種種功能實在太讓人饞涎欲滴了。 
  回到本文開頭的兩個問題,Zend Framework的前途將會如何呢?雖然做者深知揣測將來的風險,可是仍是很願意嘗試回答這兩個問題:第一,Zend Framework的出現,已經證實了PHP 5有能力實現大型的、複雜的框架,而因爲PHP的編譯器掌握在Zend公司手中,可以保證PHP語言會持續發展,以知足進一步的需求。第二,PHP原本就已經在逐步進入企業開發的領域,出現一個重量級的Framework是必然的事情。也許PHP將來的開發主力,將由通過短時間培訓的、偏向快速解決問題的初級程序員,變爲經驗豐富的、熟悉各類複雜的IT平臺、側重應用的安全性和健壯性的資深工程師。畢竟,只要通過不懈的努力,腳本小子也可能有成爲大師的一天,對嗎? 
  至於Zend Framework和ROR之間的競爭,如今沒有人可以給出答案。也許到目前爲止,Zend Framework還沒法達到ROR 100%的生產力,可是它擁有更廣大的用戶基礎,更成熟的語言,更短的學習曲線,相似C和Java的語法也更讓人容易掌握。ROR中的DRY、「慣例重於配置」等原則,在Zend Framework中也得了很好的秉承和發揚。不管它是否可以有效地狙擊ROR,毫無疑問的是,一旦Zend Framework被PHP社羣普遍接受,PHP的生產力將會有極大的提升。 php

相關文章
相關標籤/搜索