導言:ThinkPHP框架是國內知名度很高應用很廣泛的php框架,我們從一些簡單的開發示例中來深入了解一下這個框架給我們帶來的開發便捷性,以及游刃有余的擴展設計。同時也從源碼分析的角度看看框架的一些不足,盡量做全面客觀的評價。這里假設大家已經使用過ThinkPHP框架,基本使用方法請參考官方文檔。
一、框架分層及url路由
框架的安裝非常簡單,下載后放入web服務器的目錄即可,但是建議大家不要用默認的入口文件位置,而是放入單獨的目錄,便于保護代碼和數據。例如我的入口文件和web服務器配置目錄在web目錄(外層框架里的index.php沒有刪除但是沒有使用):
[1.png]
同大多數MVC框架一樣,我們只需要按框架的目錄結構,擴展自己的Controller和View,一些頁面就開發完成了。ThinkPHP提供Module、Controller、Action三層結構來組織自己的url(3.1版本叫分組、Action和method,3.2更加國際范),目錄結構如下:
[2.png]
這里強烈建議大家:
1、業務單獨分層,不用放在Controller和Model里,例如我這里通過擴展函數庫Application/Common/Common/function.php強制定義業務層名稱為Service:
1
2
3
4
functionservice($name)
{
return D($name, 'Service');
}
好處是復用性好,假如將來要開發wap頁面,寫了不同的Controller,就可以復用service,假如以后的數據存儲變了,比如把數據庫從mysql遷移到mongodb之類,那修改Model就可以,service還是不需要任何修改。
2、基礎模塊和業務模塊分開,不要相互引用。基礎模塊(例如用戶基本信息)只提供數據接口沒有Controller和View。
三層目錄已經可以應對一般的web應用,更加復雜的web應用我們可以定義不同的入口文件加載不同的Application來解決。更更復雜的應用?門戶和超大規模網站么,那就不是一個php框架能解決所有問題的了,需要自己的中間件和定制框架。
ThinkPHP的支持4種url訪問模式,分別是:
1、 普通模式,傳統url模式,所有參數分開,例如
http://localhost/tp/index.php?m=Ucai&c=User&a=index¶=xxx
路由參數:m參數表示模塊,c表示控制器,a表示訪問方法
2、 兼容模式
http://localhost/tp/index.php?s=/Ucai/User/index/para/xxx
路由參數通過s參數組裝,當然數據參數也可以不必放在s參數里;
3、pathinfo模式
http://localhost/tp/index.php/Ucai/User/index/para/xxx
這種模式把入口文件和真實腳本放在一起,含義明確,也便于SEO;
4、rewrite模式
http://localhost/tp/Ucai/User/index/para/xxx
這種模式通過web服務器的rewrite配置隱藏入口文件,顯得更加友好;其中pathinfo和rewrite模式需要web服務器支持。ThinkPHP有個配置需要設置為哪種模式,其實是用在U方法里生成url鏈接的時候用到的,訪問的時候只web服務器支持用哪種方式都可以。也建議ThinkPHP其實不需要配置,而是記住用戶訪問的方式,只要第一個訪問用的是哪種模式,以后生成的url都用這種方式生成,因為用戶都已經訪問到了就不存在支不支持的問題了。如果正常的url不能達到我們的要求,還可以通過配置路由進一步優化url,例如我們想把url配置的更加簡單http://localhost/tp/Ucai/login/xxx
我們只需要在模塊配置文件中添加如下的路由配置即可,如果用正則表達式則可以更加簡化
1
2
3
4
'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,然后實現request和execute方法擴展就算完成了,然后再配置文件里配置DB_TYPE=’custom’,就可以使用了。這里的request表示查詢,execute表示更改數據,所有其他操作都會在Model里進行解析,包裝成sql語句調用這兩個方法執行。
例如我所實現的最簡單的query方式,通過shell命令調用sqlite執行sql語句:
1
2
3
4
publicfunction 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就可以了:
1
2
3
4
return array(
'action_begin' => array('UcaiBehaviorAuthBehavior'),
'view_begin' => array('UcaiBehaviorOutputBehavior'),
);
程序在執行到action_begin流程時就會調用這個Behavior,我們可以根據狀態進行跳轉或終止執行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
namespace UcaiBehavior;
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里添加配置,所有不配置的都會要求登錄驗證。
1
public $config = array('AUTH_PUBLIC' => true);
這里大家對繼承和Behavior實現登錄驗證做一個對比,可能覺得區別不大。但是在一個復雜的項目里,這種功能會非常多,如果每個功能都放到父類里,就會非常龐大,并且部分子類可能又不需要,這時候用Behavior去定制流程就會顯得游刃有余。
在上面的配置中我們還發現了一個配置OutputBehavior更能說明問題,大家有沒有猜到,這個Behavior我是用來在view里輸出一些共有變量,例如jscss的域名和路徑等。在沒有Behavior之前,大家是不是需要一個公共方法,然后每個頁面都去調用一次,或者改寫View的類代碼?有了Behavior就顯得方便許多。
1
2
3
4
5
6
7
namespace UcaiBehavior;
class OutputBehavior {
public function run(&$return) {
$view = ThinkThink::instance('ThinkView');
$view->assign('STATIC_URL', 'http://p3.ucai.cn/static');
}
}
擴展總結:通過Behavior擴展和數據庫驅動擴展大家可以看到,ThinkPHP提供了很靈活的擴展和增強機制,能滿足眾多需求。其他存儲、緩存、日志、模板引擎等如果需要也能很方便的擴展。
三、源碼分析與不足
首先我們來分析一下框架執行的大致流程:
index.php(入口、調試模式、應用路徑)
-->ThinkPHP.php(定義路徑與訪問模式)
-->ThinkThink(類加載器、異常處理、讀取共有配置)
-->ThinkApp(請求url調度解析、執行調度解析結果)
--> exec 執行用戶定義的Controller的Action方法
-->ThinkDispatcher(根據url模式解析M、C、A和參數,加載模塊配置)
-->ThinkController(調用視圖、包裝和重定向)
可以看到,框架的內部流程其實比較簡單,還有2個很重要的類:
ThinkHook: 監聽App、Action、View的各個階段,執行Behavior
ThinkBehavior: 可配置(配置文件)可增刪(代碼)
在分析源代碼的過程中,我們也看到了一些不足:
1、宏定義過多,難于維護和修改
建議:只在個別文件定義極少數幾個宏,其余用類常量包裝
2、面向過程代碼過多,封裝不清晰
建議:用面向對象思想包裝
例如:url的解析和包裝,現在是在Dispatcher里生成/index.php?app=blog宏,然后在U方法里讀取宏并生成最終url。其實完全可以定義一個類來包裝例如UrlHelper,而類的二個方法parse和generate分別負責解析和生成url,這樣代碼結構會清晰很多。
3、有的函數和類代碼封裝過多,復用和改進不方便
建議:用組合來封裝獨立功能內容
例如:Model的校驗功能,完全可以獨立成類,也可以用于非Model對象調用。而現在的校驗接口是Model的保護性方法,只能在Model的create函數調用,外面必須通過create方法才能校驗。
4、代碼規范和風格問題
希望代碼風格能更加規范和標準,例如DB類作為模板方法的父類,應該用抽象方法或拋出異常形式定義所有Model需用到的方法。事實上有些方法子類是不需要的,而Db類卻沒有實現。
四、總結
ThinkPHP作為國內熱門的php框架,確實給我們的開發帶來了便利。框架開發者對web流程理解的很透徹,對php的函數應用爐火純青。框架定義了靈活的配置和擴展適應各種需求,提供了豐富的組件和模塊來加速開發。最后說一點,ThinkPHP的文檔和社區支持非常完善,這也是框架流行不可缺少的重要一環。我們也希望ThinkPHP以后能更加完善自身的結構,打造成最優秀的php框架。
- PHP技術文章
- PHP中session和cookie的區別
- php設計模式(一):簡介及創建型模式
- php設計模式結構型模式
- Php設計模式(三):行為型模式
- 十款最出色的 PHP 安全開發庫中文詳細介紹
- 12個提問頻率最高的PHP面試題
- PHP 語言需要避免的 10 大誤區
- PHP 死鎖問題分析
- 致PHP路上的“年輕人”
- PHP網站常見安全漏洞,及相應防范措施總結
- 各開源框架使用與設計總結(一)
- 數據庫的本質、概念及其應用實踐(二)
- PHP導出MySQL數據到Excel文件(fputcsv)
- PHP中14種排序算法評測
- 深入理解PHP原理之--echo的實現
- PHP性能分析相關的函數
- PHP 性能分析10則
- 10 位頂級 PHP 大師的開發原則
- 30條爆笑的程序員梗 PHP是最好的語言
- PHP底層的運行機制與原理
- PHP 性能分析與實驗——性能的宏觀分析
- PHP7 性能翻倍關鍵大揭露
- 鳥哥:寫在PHP7發布之際一些話
- PHP與MySQL通訊那點事
- Php session內部執行流程的再次剖析
- 關于 PHP 中的 Class 的幾點個人看法
- PHP Socket 編程過程詳解
- PHP過往及現在及變革
- PHP吉祥物大象的由來
- PHP生成靜態頁面的方法
- 吊炸天的 PHP 7 ,你值得擁有!
- PHP開發中文件操作疑難問答
- MongoDB PHP Driver的連接處理解析
- PHP 雜談《重構-改善既有代碼的設計》之二 對象
- 在php中判斷一個請求是ajax請求還是普通請求的方法
- 使用HAProxy、PHP、Redis和MySQL支撐10億請求每周架構細節
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重構-改善既有代碼的設計
- PHP場景中getshell防御思路分享
- 移動互聯時代,你看看除了PHP你還會些什么
- 安卓系統上搭建本地php服務器環境
- PHP中常見的緩存技術!
- PHP里10個鮮為人知但卻非常有用的函數
- 成為一名PHP專家其實并不難
- PHP 命令行?是的,您可以!
- PHP開發提高效率技巧
- PHP八大安全函數解析
- PHP實現四種基本排序算法
- PHP開發中的中文編碼問題
- php.get.post
- php發送get、post請求的6種方法簡明總結
- 中高級PHP開發者應該掌握哪些技術?
- 前端開發
- web前端知識體系大全
- 前端工程與性能優化(下)
- 前端工程與性能優化(上)
- 2016 年技術發展方向
- Web應用檢查清單
- 如何成為一名優秀的web前端工程師
- 前端組件化開發實踐
- 移動端H5頁面高清多屏適配方案
- 2015前端框架何去何從
- 從前端看“百度遷徙”的技術實現(一)
- 從前端看“百度遷徙”的技術實現(二)
- 前端路上的旅行
- 大公司里怎樣開發和部署前端代碼?
- 5個經典的前端面試問題
- 前端工程師新手必讀
- 手機淘寶前端的圖片相關工作流程梳理
- 一個自動化的前端項目實現(附源碼)
- 前端代碼異常日志收集與監控
- 15年雙11手淘前端技術總結 - H5性能最佳實踐
- 深入理解javascript原型和閉包系列
- 一切都是對象
- 函數和對象的關系
- prototype原型
- 隱式原型
- instanceof
- 繼承
- 原型的靈活性
- 簡述【執行上下文】上
- 簡述【執行上下文】下
- this
- 執行上下文棧
- 簡介【作用域】
- 【作用域】和【上下文環境】
- 從【自由變量】到【作用域鏈】
- 閉包
- 完結
- 補充:上下文環境和作用域的關系
- Linux私房菜