可以線上執行檢視效果哦!    

    導言:ThinkPHP框架是國內知名度很高應用很廣泛的php框架,我們從一些簡單的開發示例中來深入瞭解一下這個框架給我們帶來的開發便捷性,以及遊刃有餘的擴充套件設計。同時也從原始碼分析的角度看看框架的一些不足,儘量做全面客觀的評價。這裡假設大家已經使用過ThinkPHP框架,基本使用方法請參考官方文件。

一、框架分層及url路由

    框架的安裝非常簡單,下載後放入web伺服器的目錄即可,但是建議大家不要用預設的入口檔案位置,而是放入單獨的目錄,便於保護程式碼和資料。例如我的入口檔案和web伺服器配置目錄在web目錄(外層框架裡的index.php沒有刪除但是沒有使用):

同大多數MVC框架一樣,我們只需要按框架的目錄結構,擴充套件自己的ControllerView,一些頁面就開發完成了。ThinkPHP提供ModuleControllerAction三層結構來組織自己的url3.1版本叫分組、Actionmethod3.2更加國際範),目錄結構如下:

這裡強烈建議大家:

1、業務單獨分層,不用放在ControllerModel裡,例如我這裡通過擴充套件函式庫

Application/Common/Common/function.php強制定義業務層名稱為Service:
function service($name)
{
    return D($name, 'Service');
}

好處是複用性好,假如將來要開發wap頁面,寫了不同的Controller,就可以複用service,假如以後的資料儲存變了,比如把資料庫從mysql遷移到mongodb之類,那修改Model就可以,service還是不需要任何修改。

2、基礎模組和業務模組分開,不要相互引用。基礎模組(例如使用者基本資訊)只提供資料介面沒有ControllerView

三層目錄已經可以應對一般的web應用,更加複雜的web應用我們可以定義不同的入口檔案載入不同的Application來解決。更更復雜的應用?門戶和超大規模網站麼,那就不是一個php框架能解決所有問題的了,需要自己的中介軟體和定製框架。

ThinkPHP的支援4url訪問模式,分別是:

普通模式,傳統url模式,所有引數分開,例如

路由引數:m引數表示模組,c表示控制器,a表示訪問方法

相容模式

路由引數通過s引數組裝,當然資料引數也可以不必放在s引數裡

pathinfo模式

這種模式把入口檔案和真實指令碼放在一起,含義明確,也便於SEO

rewrite模式

這種模式通過web伺服器的rewrite配置隱藏入口檔案,顯得更加友好

其中pathinforewrite模式需要web伺服器支援。ThinkPHP有個配置需要設定為哪種模式,其實是用在U方法裡生成url連結的時候用到的,訪問的時候只要web伺服器支援用哪種方式都可以。

也建議ThinkPHP其實不需要配置,而是記住使用者訪問的方式,只要第一個訪問用的是哪種模式,以後生成的url都用這種方式生成,因為使用者都已經訪問到了就不存在支不支援的問題了。

如果正常的url不能達到我們的要求,還可以通過配置路由進一步優化url,例如我們想把url配置的更加簡單

我們只需要在模組配置檔案中新增如下的路由配置即可,如果用正則表示式則可以更加簡化

'URL_ROUTE_RULES'   =>   array(
              'login/:para' => 'Ucai/User/index',
              'login' => 'Ucai/User/index',
       ),
 

到這裡我們可以看到,ThinkPHP框架支援的層次結構和url配置非常豐富,能滿足各種不同的需求。當然我們建議大家不要濫用路由配置,適當少量的配置能帶來更好的seo效果,但是大量的配置會給專案的維護和修改帶來困難。

二、ThinkPHP擴充套件

ThinkPHP本身含有豐富的元件和驅動,我們以資料庫驅動擴充套件和行為擴充套件為例來了解一下ThinkPHP的擴充套件設計。

1、資料庫驅動擴充套件

    雖然ThinkPHP提供了眾多的資料庫驅動,但是也並不能滿足所有的需求。例如我們的資料很可能不是通過直接訪問資料庫去實現,而是通過一些中介軟體(例如C程式)進行轉發,從而獲得更好的效能,這時就需要擴充套件資料庫驅動來支援。

    擴充套件非常簡單,在DB/Driver目錄下新建自己的驅動,例如Custom.php,然後實現requestexecute方法擴充套件就算完成了,然後再配置檔案裡配置DB_TYPE=’custom’,就可以使用了。這裡的request表示查詢,execute表示更改資料,所有其他操作都會在Model裡進行解析,包裝成sql語句呼叫這兩個方法執行。

例如我所實現的最簡單的query方式,通過shell命令呼叫sqlite執行sql語句:

public function query($str) {
        $cmd = sprintf('sqlite3 %s "%s"', $this->config['params']['dbfile'], $str);
        exec($cmd, $arr);
}

當然這個只是示例,ThinkPHP本身就支援sqlite3,通過pdo的方式去連線就可以。實際的應用環境可能是通過連線4層協議訪問中間層埠獲取資料。

2、Behavior行為擴充套件

    Behavior行為設計是ThinkPHP框架的核心,通過行為配置和擴充套件,為系統的伸縮性和定製性提供了最大的支援。假如我們要加入登入驗證的功能,按照常規我們會設計自己的父類Controller,然後所有其他的Controller都從這裡繼承。但有了Behavior會變得更加簡單和靈活,我們只需要在tags.php(沒有的話在配置目錄新建)新增一個Behavior就可以了:

return array(
       'action_begin' => array('Ucai\Behavior\AuthBehavior'),
       'view_begin' => array('Ucai\Behavior\OutputBehavior'),
);

程式在執行到action_begin流程時就會呼叫這個Behavior,我們可以根據狀態進行跳轉或終止執行。

namespace Ucai\Behavior;
class AuthBehavior {
     // 行為擴充套件的執行入口必須是run
     public function run(&$return) {
          //不需要驗證的action設定為true
         if (!$return['AUTH_PUBLIC']) {
              if (service('User')->checkLogin())
              {
                     $return = true;
              }
              else
              {
                     header('Content-Type: text/html; charset=utf-8');
                     redirect(U('User/index', array('url' => $_SERVER['HTTP_REFERER'])), 5, '需要登入,5秒後跳轉。。。');
              }
         }
     }
}

對於不需要登入的頁面我們可以在Controller裡新增配置,所有不配置的都會要求登入驗證。

public $config = array('AUTH_PUBLIC' => true);

    這裡大家對繼承和Behavior實現登入驗證做一個對比,可能覺得區別不大。但是在一個複雜的專案裡,這種功能會非常多,如果每個功能都放到父類裡,就會非常龐大,並且部分子類可能又不需要,這時候用Behavior去定製流程就會顯得遊刃有餘。

    在上面的配置中我們還發現了一個配置OutputBehavior更能說明問題,大家有沒有猜到,這個Behavior我是用來在view裡輸出一些共有變數,例如jscss的域名和路徑等。在沒有Behavior之前,大家是不是需要一個公共方法,然後每個頁面都去呼叫一次,或者改寫View的類程式碼?有了Behavior就顯得方便許多。

namespace Ucai\Behavior;
class OutputBehavior {
     public function run(&$return) {
          $view = \Think\Think::instance('Think\View');
        $view->assign('STATIC_URL', 'http://p3.ucai.cn/static');
     }
}

擴充套件總結:通過Behavior擴充套件和資料庫驅動擴充套件大家可以看到,ThinkPHP提供了很靈活的擴充套件和增強機制,能滿足眾多需求。其他儲存、快取、日誌、模板引擎等如果需要也能很方便的擴充套件。

三、原始碼分析與不足

首先我們來分析一下框架執行的大致流程:

index.php(入口、除錯模式、應用路徑)

--> ThinkPHP.php(定義路徑與訪問模式)

--> Think\Think(類載入器、異常處理、讀取共有配置)

--> Think\App(請求url排程解析、執行排程解析結果)

      --> exec 執行使用者定義的ControllerAction方法

--> Think\Dispatcher(根據url模式解析MCA和引數,載入模組配置)

--> Think\Controller(呼叫檢視、包裝和重定向)

可以看到,框架的內部流程其實比較簡單,還有2個很重要的類:

Think\Hook: 監聽AppActionView的各個階段,執行Behavior

Think\Behavior: 可配置(配置檔案)可增刪(程式碼)

在分析原始碼的過程中,我們也看到了一些不足:

1、巨集定義過多,難於維護和修改

建議:只在個別檔案定義極少數幾個巨集,其餘用類常量包裝

2、面向過程程式碼過多,封裝不清晰

建議:用面向物件思想包裝

例如:url的解析和包裝,現在是在Dispatcher裡生成__APP__巨集,然後在U方法裡讀取巨集並生成最終url。其實完全可以定義一個類來包裝例如UrlHelper,而類的二個方法parsegenerate分別負責解析和生成url,這樣程式碼結構會清晰很多。

3、有的函式和類程式碼封裝過多,複用和改進不方便

建議:用組合來封裝獨立功能內容

例如:Model的校驗功能,完全可以獨立成類,也可以用於非Model物件呼叫。而現在的校驗介面是Model的保護性方法,只能在Modelcreate函式呼叫,外面必須通過create方法才能校驗。

4、程式碼規範和風格問題

希望程式碼風格能更加規範和標準,例如DB類作為模板方法的父類,應該用抽象方法或丟擲異常形式定義所有Model需用到的方法。事實上有些方法子類是不需要的,而Db類卻沒有實現。

四、總結

    ThinkPHP作為國內熱門的php框架,確實給我們的開發帶來了便利。框架開發者對web流程理解的很透徹,對php的函式應用爐火純青。框架定義了靈活的配置和擴充套件適應各種需求,提供了豐富的元件和模組來加速開發。最後說一點,ThinkPHP的文件和社群支援非常完善,這也是框架流行不可缺少的重要一環。我們也希望ThinkPHP以後能更加完善自身的結構,打造成最優秀的php框架。