從0開始構(gòu)建一個(gè)屬于你自己的PHP框架
從0開始構(gòu)建一個(gè)屬于你自己的PHP框架
如何構(gòu)建一個(gè)自己的PHP框架
為什么我們要去構(gòu)建一個(gè)自己的PHP框架?可能絕大多數(shù)的人都會說“市面上已經(jīng)那么多的框架了,還造什么輪子?”。我的觀點(diǎn)“造輪子不是目的,造輪子的過程中汲取到知識才是目的”。
那怎樣才能構(gòu)建一個(gè)自己的PHP框架呢?大致流程如下:
- 入口文件 ----> 注冊自加載函數(shù)
 - ----> 注冊錯(cuò)誤(和異常)處理函數(shù)
 - ----> 加載配置文件
 - ----> 請求
 - ----> 路由
 - ---->(控制器 <----> 數(shù)據(jù)模型)
 - ----> 響應(yīng)
 - ----> json
 - ----> 視圖渲染數(shù)據(jù)
 
除此之外我們還需要單元測試、nosql支持、接口文檔支持、一些輔助腳本等。最終我的框架目錄如下:
框架目錄一覽
- app [PHP應(yīng)用目錄]
 - ├── demo [模塊目錄]
 - │ ├── controllers [控制器目錄]
 - │ │ └── Index.php [默認(rèn)控制器文件,輸出json數(shù)據(jù)]
 - │ ├── logics [邏輯層,主要寫業(yè)務(wù)邏輯的地方]
 - │ │ ├── exceptions [異常目錄]
 - │ │ ├── gateway [一個(gè)邏輯層實(shí)現(xiàn)的gateway演示]
 - │ │ ├── tools [工具類目錄]
 - │ │ └── UserDefinedCase.php [注冊框架加載到路由前的處理用例]
 - │ └── models [數(shù)據(jù)模型目錄]
 - │ └── TestTable.php [演示模型文件,定義一一對應(yīng)的數(shù)據(jù)模型]
 - ├── config [配置目錄]
 - │ ├── demo [模塊配置目錄]
 - │ │ ├── config.php [模塊自定義配置]
 - │ │ └── route.php [模塊自定義路由]
 - │ ├── common.php [公共配置]
 - │ ├── database.php [數(shù)據(jù)庫配置]
 - │ └── nosql.php [nosql配置]
 - docs [接口文檔目錄]
 - ├── apib [Api Blueprint]
 - │ └── demo.apib [接口文檔示例文件]
 - ├── swagger [swagger]
 - framework [Easy PHP核心框架目錄]
 - ├── exceptions [異常目錄]
 - │ ├── CoreHttpException.php[核心http異常]
 - ├── handles [框架運(yùn)行時(shí)掛載處理機(jī)制類目錄]
 - │ ├── Handle.php [處理機(jī)制接口]
 - │ ├── ErrorHandle.php [錯(cuò)誤處理機(jī)制類]
 - │ ├── ExceptionHandle.php [未捕獲異常處理機(jī)制類]
 - │ ├── ConfigHandle.php [配置文件處理機(jī)制類]
 - │ ├── NosqlHandle.php [nosql處理機(jī)制類]
 - │ ├── LogHandle.php [log機(jī)制類]
 - │ ├── UserDefinedHandle.php[用戶自定義處理機(jī)制類]
 - │ └── RouterHandle.php [路由處理機(jī)制類]
 - ├── orm [對象關(guān)系模型]
 - │ ├── Interpreter.php [sql解析器]
 - │ ├── DB.php [數(shù)據(jù)庫操作類]
 - │ ├── Model.php [數(shù)據(jù)模型基類]
 - │ └── db [數(shù)據(jù)庫類目錄]
 - │ └── Mysql.php [mysql實(shí)體類]
 - ├── nosql [nosql類目錄]
 - │ ├── Memcahed.php [Memcahed類文件]
 - │ ├── MongoDB.php [MongoDB類文件]
 - │ └── Redis.php [Redis類文件]
 - ├── App.php [框架類]
 - ├── Container.php [服務(wù)容器]
 - ├── Helper.php [框架助手類]
 - ├── Load.php [自加載類]
 - ├── Request.php [請求類]
 - ├── Response.php [響應(yīng)類]
 - ├── run.php [框架應(yīng)用啟用腳本]
 - frontend [前端源碼和資源目錄]
 - ├── src [資源目錄]
 - │ ├── components [vue組件目錄]
 - │ ├── views [vue視圖目錄]
 - │ ├── images [圖片]
 - │ ├── ...
 - ├── app.js [根js]
 - ├── app.vue [根組件]
 - ├── index.template.html [前端入口文件模板]
 - ├── store.js [vuex store文件]
 - public [公共資源目錄,暴露到萬維網(wǎng)]
 - ├── dist [前端build之后的資源目錄,build生成的目錄,不是發(fā)布分支忽略該目錄]
 - │ └── ...
 - ├── index.html [前端入口文件,build生成的文件,不是發(fā)布分支忽略該文件]
 - ├── index.php [后端入口文件]
 - runtime [臨時(shí)目錄]
 - ├── logs [日志目錄]
 - ├── build [php打包生成phar文件目錄]
 - tests [單元測試目錄]
 - ├── demo [模塊名稱]
 - │ └── DemoTest.php [測試演示]
 - ├── TestCase.php [測試用例]
 - vendor [composer目錄]
 - .git-hooks [git鉤子目錄]
 - ├── pre-commit [git pre-commit預(yù)commit鉤子示例文件]
 - ├── commit-msg [git commit-msg示例文件]
 - .babelrc [babel配置文件]
 - .env [環(huán)境變量文件]
 - .gitignore [git忽略文件配置]
 - build [php打包腳本]
 - cli [框架cli模式運(yùn)行腳本]
 - LICENSE [lincese文件]
 - logo.png [框架logo圖片]
 - composer.json [composer配置文件]
 - composer.lock [composer lock文件]
 - package.json [前端依賴配置文件]
 - phpunit.xml [phpunit配置文件]
 - README-CN.md [中文版readme文件]
 - README.md [readme文件]
 - webpack.config.js [webpack配置文件]
 - yarn.lock [yarn lock文件]
 
框架模塊說明:
入口文件
定義一個(gè)統(tǒng)一的入口文件,對外提供統(tǒng)一的訪問文件。對外隱藏了內(nèi)部的復(fù)雜性,類似企業(yè)服務(wù)總線的思想。
- // 載入框架運(yùn)行文件
 - require('../framework/run.php');
 
自加載模塊
使用spl_autoload_register函數(shù)注冊自加載函數(shù)到__autoload隊(duì)列中,配合使用命名空間,當(dāng)使用一個(gè)類的時(shí)候可以自動載入(require)類文件。注冊完成自加載邏輯后,我們就可以使用use和配合命名空間申明對某個(gè)類文件的依賴。
錯(cuò)誤和異常模塊
腳本運(yùn)行期間:
- 錯(cuò)誤:
 
通過函數(shù)set_error_handler注冊用戶自定義錯(cuò)誤處理方法,但是set_error_handler不能處理以下級別錯(cuò)誤,E_ERROR、 E_PARSE、 E_CORE_ERROR、 E_CORE_WARNING、 E_COMPILE_ERROR、 E_COMPILE_WARNING,和在 調(diào)用 set_error_handler() 函數(shù)所在文件中產(chǎn)生的大多數(shù) E_STRICT。所以我們需要使用register_shutdown_function配合error_get_last獲取腳本終止執(zhí)行的***錯(cuò)誤,目的是對于不同錯(cuò)誤級別和致命錯(cuò)誤進(jìn)行自定義處理,例如返回友好的提示的錯(cuò)誤信息。
[ file: framework/hanles/ErrorHandle.php ]
異常:
通過函數(shù)set_exception_handler注冊未捕獲異常處理方法,目的捕獲未捕獲的異常,例如返回友好的提示和異常信息。
[ file: framework/hanles/ExceptionHandle.php ]
配置文件模塊
加載框架自定義和用戶自定義的配置文件。
[ file: framework/hanles/ConfigHandle.php ]
輸入和輸出
- 定義請求對象:包含所有的請求信息
 - 定義響應(yīng)對象:申明響應(yīng)相關(guān)信息
 
框架中所有的異常輸出和控制器輸出都是json格式,因?yàn)槲艺J(rèn)為在前后端完全分離的今天,這是很友善的,目前我們不需要再去考慮別的東西。
[ file: framework/Request.php ]
[ file: framework/Response.php ]
路由模塊
通過用戶訪問的url信息,通過路由規(guī)則執(zhí)行目標(biāo)控制器類的的成員方法。我在這里把路由大致分成了四類:
傳統(tǒng)路由
- domain/index.php?module=Demo&contoller=Index&action=test&username=test
 
pathinfo路由
- domain/demo/index/modelExample
 
用戶自定義路由
- // 定義在config/moduleName/route.php文件中,這個(gè)的this指向RouterHandle實(shí)例
 - $this->get('v1/user/info', function (Framework\App $app) {
 - return 'Hello Get Router';
 - });
 
微單體路由
我在這里詳細(xì)說下這里所謂的微單體路由,面向SOA和微服務(wù)架構(gòu)大行其道的今天,有很多的團(tuán)隊(duì)都在向服務(wù)化邁進(jìn),但是服務(wù)化過程中很多問題的復(fù)雜度都是指數(shù)級的增長,例如分布式的事務(wù),服務(wù)部署,跨服務(wù)問題追蹤等等。這導(dǎo)致對于小的團(tuán)隊(duì)從單體架構(gòu)走向服務(wù)架構(gòu)難免困難重重,所以有人提出來了微單體架構(gòu),按照我的理解就是在一個(gè)單體架構(gòu)的SOA過程,我們把微服務(wù)中的的各個(gè)服務(wù)還是以模塊的方式放在同一個(gè)單體中,比如:
- app
 - ├── UserService [用戶服務(wù)模塊]
 - ├── ContentService [內(nèi)容服務(wù)模塊]
 - ├── OrderService [訂單服務(wù)模塊]
 - ├── CartService [購物車服務(wù)模塊]
 - ├── PayService [支付服務(wù)模塊]
 - ├── GoodsService [商品服務(wù)模塊]
 - └── CustomService [客服服務(wù)模塊]
 
如上,我們簡單的在一個(gè)單體里構(gòu)建了各個(gè)服務(wù)模塊,但是這些模塊怎么通信呢?如下:
- App::$app->get('demo/index/hello', [
 - 'user' => 'TIGERB'
 - ]);
 
通過上面的方式我們就可以松耦合的方式進(jìn)行單體下各個(gè)模塊的通信和依賴了。與此同時(shí),業(yè)務(wù)的發(fā)展是難以預(yù)估的,未來當(dāng)我們向SOA的架構(gòu)遷移時(shí),很簡單,我們只需要把以往的模塊獨(dú)立成各個(gè)項(xiàng)目,然后把App實(shí)例get方法的實(shí)現(xiàn)轉(zhuǎn)變?yōu)镽PC或者REST的策略即可,我們可以通過配置文件去調(diào)整對應(yīng)的策略或者把自己的,第三方的實(shí)現(xiàn)注冊進(jìn)去即可。
[ file: framework/hanles/RouterHandle.php ]
傳統(tǒng)的MVC模式提倡為MCL模式
傳統(tǒng)的MVC模式包含model-view-controller層,絕大多時(shí)候我們會把業(yè)務(wù)邏輯寫到controller層或model層,但是慢慢的我們會發(fā)現(xiàn)代碼難以閱讀、維護(hù)、擴(kuò)展,所以我在這里強(qiáng)制增加了一個(gè)logics層。至于,邏輯層里怎么寫代碼怎么,完全由你自己定義,你可以在里面實(shí)現(xiàn)一個(gè)工具類,你也可以在里面再新建子文件夾并在里面構(gòu)建你的業(yè)務(wù)邏輯代碼,你甚至可以實(shí)現(xiàn)一個(gè)基于責(zé)任連模式的網(wǎng)關(guān)(我會提供具體的示例)。這樣看來,我們的最終結(jié)構(gòu)是這樣的:
- M: models, 職責(zé)只涉及數(shù)據(jù)模型相關(guān)操作
 - C: controllers, 職責(zé)對外暴露資源,前后端分離架構(gòu)下controllers其實(shí)就相當(dāng)于json格式的視圖
 - L: logics, 職責(zé)靈活實(shí)現(xiàn)所有業(yè)務(wù)邏輯的地方
 
logics邏輯層
邏輯層實(shí)現(xiàn)網(wǎng)關(guān)示例:
我們在logics層目錄下增加了一個(gè)gateway目錄,然后我們就可以靈活的在這個(gè)目錄下編寫邏輯了。gateway的結(jié)構(gòu)如下:
- gateway [Logics層目錄下gateway邏輯目錄]
 - ├── Check.php [接口]
 - ├── CheckAppkey.php [檢驗(yàn)app key]
 - ├── CheckArguments.php [校驗(yàn)必傳參數(shù)]
 - ├── CheckAuthority.php [校驗(yàn)訪問權(quán)限]
 - ├── CheckFrequent.php [校驗(yàn)訪問頻率]
 - ├── CheckRouter.php [網(wǎng)關(guān)路由]
 - ├── CheckSign.php [校驗(yàn)簽名]
 - └── Entrance.php [網(wǎng)關(guān)入口文件]
 
網(wǎng)關(guān)入口類主要負(fù)責(zé)網(wǎng)關(guān)的初始化,代碼如下:
- // 初始化一個(gè):必傳參數(shù)校驗(yàn)的check
 - $checkArguments = new CheckArguments();
 - // 初始化一個(gè):app key check
 - $checkAppkey = new CheckAppkey();
 - // 初始化一個(gè):訪問頻次校驗(yàn)的check
 - $checkFrequent = new CheckFrequent();
 - // 初始化一個(gè):簽名校驗(yàn)的check
 - $checkSign = new CheckSign();
 - // 初始化一個(gè):訪問權(quán)限校驗(yàn)的check
 - $checkAuthority = new CheckAuthority();
 - // 初始化一個(gè):網(wǎng)關(guān)路由規(guī)則
 - $checkRouter = new CheckRouter();
 - // 構(gòu)成對象鏈
 - $checkArguments->setNext($checkAppkey)
 - ->setNext($checkFrequent)
 - ->setNext($checkSign)
 - ->setNext($checkAuthority)
 - ->setNext($checkRouter);
 - // 啟動網(wǎng)關(guān)
 - $checkArguments->start(
 - APP::$container->getSingle('request')
 - );
 
實(shí)現(xiàn)完成這個(gè)gateway之后,我們?nèi)绾卧诳蚣苤腥ナ褂媚?在logic層目錄中我提供了一個(gè)user-defined的實(shí)體類,我們把gateway的入口類注冊到UserDefinedCase這個(gè)類中,示例如下:
- /**
 - * 注冊用戶自定義執(zhí)行的類
 - *
 - * @var array
 - */
 - private $map = [
 - // 演示 加載自定義網(wǎng)關(guān)
 - 'App\Demo\Logics\Gateway\Entrance'
 - ];
 
這樣這個(gè)gateway就可以工作了。接著說說這個(gè)UserDefinedCase類,UserDefinedCase會在框架加載到路由機(jī)制之前被執(zhí)行,這樣我們就可以靈活的實(shí)現(xiàn)一些自定義的處理了。這個(gè)gateway只是個(gè)演示,你完全可以天馬行空的組織你的邏輯~
視圖View去哪了?由于選擇了完全的前后端分離和SPA(單頁應(yīng)用), 所以傳統(tǒng)的視圖層也因此去掉了,詳細(xì)的介紹看下面。
使用Vue作為視圖
源碼目錄
完全的前后端分離,數(shù)據(jù)雙向綁定,模塊化等等的大勢所趨。這里我把我自己開源的vue前端項(xiàng)目結(jié)構(gòu) easy-vue 移植到了這個(gè)項(xiàng)目里,作為視圖層。我們把前端的源碼文件都放在frontend目錄里,詳細(xì)如下,你也可以自己定義:
- frontend [前端源碼和資源目錄,這里存放我們整個(gè)前端的源碼文件]
 - ├── src [資源目錄]
 - │ ├── components [編寫我們的前端組件]
 - │ ├── views [組裝我們的視圖]
 - │ ├── images [圖片]
 - │ ├── ...
 - ├── app.js [根js]
 - ├── app.vue [根組件]
 - ├── index.template.html [前端入口文件模板]
 - └── store.js [狀態(tài)管理,這里只是個(gè)演示,你可以很靈活的編寫文件和目錄]
 
build步驟
- yarn install
 - DOMAIN=http://你的域名 npm run dev
 
編譯后
build成功之后會生成dist目錄和入口文件index.html在public目錄中。非發(fā)布分支.gitignore文件會忽略這些文件,發(fā)布分支去除忽略即可。
- public [公共資源目錄,暴露到萬維網(wǎng)]
 - ├── dist [前端build之后的資源目錄,build生成的目錄,不是發(fā)布分支忽略該目錄]
 - │ └── ...
 - ├── index.html [前端入口文件,build生成的文件,不是發(fā)布分支忽略該文件]
 
數(shù)據(jù)庫對象關(guān)系映射
數(shù)據(jù)庫對象關(guān)系映射ORM(Object Relation Map)是什么?按照我目前的理解:顧名思義是建立對象和抽象事物的關(guān)聯(lián)關(guān)系,在數(shù)據(jù)庫建模中model實(shí)體類其實(shí)就是具體的表,對表的操作其實(shí)就是對model實(shí)例的操作??赡芙^大多數(shù)的人都要問“為什么要這樣做,直接sql語句操作不好嗎?搞得這么麻煩!”,我的答案:直接sql語句當(dāng)然可以,一切都是靈活的,但是從一個(gè)項(xiàng)目的 可復(fù)用,可維護(hù), 可擴(kuò)展 出發(fā),采用ORM思想處理數(shù)據(jù)操作是理所當(dāng)然的,想想如果若干一段時(shí)間你看見代碼里大段的難以閱讀且無從復(fù)用的sql語句,你是什么樣的心情。
市面上對于ORM的具體實(shí)現(xiàn)有thinkphp系列框架的Active Record,yii系列框架的Active Record,laravel系列框架的Eloquent(據(jù)說是***雅的),那我們這里言簡意賅就叫ORM了。接著為ORM建模,首先是ORM客戶端實(shí)體DB:通過配置文件初始化不同的db策略,并封裝了操作數(shù)據(jù)庫的所有行為,最終我們通過DB實(shí)體就可以直接操作數(shù)據(jù)庫了,這里的db策略目前我只實(shí)現(xiàn)了mysql(負(fù)責(zé)建立連接和db的底層操作)。接著我們把DB實(shí)體的sql解析功能獨(dú)立成一個(gè)可復(fù)用的sql解析器的trait,具體作用:把對象的鏈?zhǔn)讲僮鹘馕龀删唧w的sql語句。***,建立我們的模型基類model,model直接繼承DB即可。***的結(jié)構(gòu)如下:
- ├── orm [對象關(guān)系模型]
 - │ ├── Interpreter.php [sql解析器]
 - │ ├── DB.php [數(shù)據(jù)庫操作類]
 - │ ├── Model.php [數(shù)據(jù)模型基類]
 - │ └── db [數(shù)據(jù)庫類目錄]
 - │ └── Mysql.php [mysql實(shí)體類]
 
DB類使用示例
- /**
 - * DB操作示例
 - *
 - * findAll
 - *
 - * @return void
 - */
 - public function dbFindAllDemo()
 - {
 - $where = [
 - 'id' => ['>=', 2],
 - ];
 - $instance = DB::table('user');
 - $res = $instance->where($where)
 - ->orderBy('id asc')
 - ->limit(5)
 - ->findAll(['id','create_at']);
 - $sql = $instance->sql;
 - return $res;
 - }
 
Model類使用示例
- // controller 代碼
 - /**
 - * model example
 - *
 - * @return mixed
 - */
 - public function modelExample()
 - {
 - try {
 - DB::beginTransaction();
 - $testTableModel = new TestTable();
 - // find one data
 - $testTableModel->modelFindOneDemo();
 - // find all data
 - $testTableModel->modelFindAllDemo();
 - // save data
 - $testTableModel->modelSaveDemo();
 - // delete data
 - $testTableModel->modelDeleteDemo();
 - // update data
 - $testTableModel->modelUpdateDemo([
 - 'nickname' => 'easy-php'
 - ]);
 - // count data
 - $testTableModel->modelCountDemo();
 - DB::commit();
 - return 'success';
 - } catch (Exception $e) {
 - DB::rollBack();
 - return 'fail';
 - }
 - }
 - //TestTable model
 - /**
 - * Model操作示例
 - *
 - * findAll
 - *
 - * @return void
 - */
 - public function modelFindAllDemo()
 - {
 - $where = [
 - 'id' => ['>=', 2],
 - ];
 - $res = $this->where($where)
 - ->orderBy('id asc')
 - ->limit(5)
 - ->findAll(['id','create_at']);
 - $sql = $this->sql;
 - return $res;
 - }
 
服務(wù)容器模塊
什么是服務(wù)容器?
服務(wù)容器聽起來很浮,按我的理解簡單來說就是提供一個(gè)第三方的實(shí)體,我們把業(yè)務(wù)邏輯需要使用的類或?qū)嵗⑷氲竭@個(gè)第三方實(shí)體類中,當(dāng)需要獲取類的實(shí)例時(shí)我們直接通過這個(gè)第三方實(shí)體類獲取。
服務(wù)容器的意義?
用設(shè)計(jì)模式來講:其實(shí)不管設(shè)計(jì)模式還是實(shí)際編程的經(jīng)驗(yàn)中,我們都是強(qiáng)調(diào)“高內(nèi)聚,松耦合”,我們做到高內(nèi)聚的結(jié)果就是每個(gè)實(shí)體的作用都是極度專一,所以就產(chǎn)生了各個(gè)作用不同的實(shí)體類。在組織一個(gè)邏輯功能時(shí),這些細(xì)化的實(shí)體之間就會不同程度的產(chǎn)生依賴關(guān)系,對于這些依賴我們通常的做法如下:
- class Demo
 - {
 - public function __construct()
 - {
 - // 類demo直接依賴RelyClassName
 - $instance = new RelyClassName();
 - }
 - }
 
這樣的寫法沒有什么邏輯上的問題,但是不符合設(shè)計(jì)模式的“最少知道原則”,因?yàn)橹g產(chǎn)生了直接依賴,整個(gè)代碼結(jié)構(gòu)不夠靈活是緊耦合的。所以我們就提供了一個(gè)第三方的實(shí)體,把直接依賴轉(zhuǎn)變?yōu)橐蕾囉诘谌剑覀儷@取依賴的實(shí)例直接通過第三方去完成以達(dá)到松耦合的目的,這里這個(gè)第三方充當(dāng)?shù)慕巧皖愃葡到y(tǒng)架構(gòu)中的“中間件”,都是協(xié)調(diào)依賴關(guān)系和去耦合的角色。***,這里的第三方就是所謂的服務(wù)容器。
在實(shí)現(xiàn)了一個(gè)服務(wù)容器之后,我把Request,Config等實(shí)例都以單例的方式注入到了服務(wù)容器中,當(dāng)我們需要使用的時(shí)候從容器中獲取即可,十分方便。使用如下:
- // 注入單例
 - App::$container->setSingle('別名,方便獲取', '對象/閉包/類名');
 - // 例,注入Request實(shí)例
 - App::$container->setSingle('request', function () {
 - // 匿名函數(shù)懶加載
 - return new Request();
 - });
 - // 獲取Request對象
 - App::$container->getSingle('request');
 
Nosql模塊
提供對nosql的支持,提供全局單例對象,借助我們的服務(wù)容器我們在框架啟動的時(shí)候,通過配置文件的配置把需要的nosql實(shí)例注入到服務(wù)容器中。目前我們支持redis/memcahed/mongodb。
如何使用?如下,
- // 獲取redis對象
 - App::$container->getSingle('redis');
 - // 獲取memcahed對象
 - App::$container->getSingle('memcahed');
 - // 獲取mongodb對象
 - App::$container->getSingle('mongodb');
 
接口文檔生成和接口模擬模塊
通常我們寫完一個(gè)接口后,接口文檔是一個(gè)問題,我們這里使用Api Blueprint協(xié)議完成對接口文檔的書寫和mock(可用),同時(shí)我們配合使用Swagger通過接口文檔實(shí)現(xiàn)對接口的實(shí)時(shí)訪問(目前未實(shí)現(xiàn))。
Api Blueprint接口描述協(xié)議選取的工具是snowboard,具體使用說明如下:
接口文檔生成說明
- cd docs/apib
 - ./snowboard html -i demo.apib -o demo.html -s
 - open the website, http://localhost:8088/
 
接口mock使用說明
- cd docs/apib
 - ./snowboard mock -i demo.apib
 - open the website, http://localhost:8087/demo/index/hello
 
單元測試模塊
基于phpunit的單元測試,寫單元測試是個(gè)好的習(xí)慣。
如何使用?
tests目錄下編寫測試文件,具體參考tests/demo目錄下的DemoTest文件,然后運(yùn)行:
- vendor/bin/phpunit
 
測試斷言示例:
- /**
 - * 演示測試
 - */
 - public function testDemo()
 - {
 - $this->assertEquals(
 - 'Hello Easy PHP',
 - // 執(zhí)行demo模塊index控制器hello操作,斷言結(jié)果是不是等于'Hello Easy PHP'
 - App::$app->get('demo/index/hello')
 - );
 - }
 
Git鉤子配置
目的規(guī)范化我們的項(xiàng)目代碼和commit記錄。
- 代碼規(guī)范:配合使用php_codesniffer,在代碼提交前對代碼的編碼格式進(jìn)行強(qiáng)制驗(yàn)證。
 - commit-msg規(guī)范:采用ruanyifeng的commit msg規(guī)范,對commit msg進(jìn)行格式驗(yàn)證,增強(qiáng)git log可讀性和便于后期查錯(cuò)和統(tǒng)計(jì)log等, 這里使用了 Treri 的commit-msg腳本,Thx~。
 
輔助腳本
cli腳本
以命令行的方式運(yùn)行框架,具體見使用說明。
build腳本
打包PHP項(xiàng)目腳本,打包整個(gè)項(xiàng)目到runtime/build目錄,例如:
- runtime/build/App.20170505085503.phar
 - <?php
 - // 入口文件引入包文件即可
 - require('runtime/build/App.20170505085503.phar')
 
如何使用?
執(zhí)行:
- composer install
 - chmod -R 777 runtime
 
網(wǎng)站服務(wù)模式:
- 步驟 1: yarn install
 - 步驟 2: DOMAIN=http://localhost:666 npm run demo
 - 步驟 3: cd public
 - 步驟 4: php -S localhost:666
 
訪問網(wǎng)站:http://localhost:666/index.html
訪問接口:http://localhost:666/Demo/Index/hello
demo如下:
客戶端腳本模式:
- php cli --method= --= ...
 - 例如, php cli --method=demo.index.get --username=easy-php
 
獲取幫助:
使用命令 php cli 或者 php cli --help
問題和貢獻(xiàn)
不足的地方還有很多,如果大家發(fā)現(xiàn)了什么問題,可以給我提 issue 或者PR。
或者你覺著在這個(gè)框架實(shí)現(xiàn)的細(xì)節(jié)你想了解的,一樣可以給我提 issue ,后面我會總結(jié)成相應(yīng)的文章分享給大家。
如何貢獻(xiàn)?
- cp ./.git-hooks/* ./git/hooks
 
然后正常發(fā)起PR即可, 所有的commit我都會進(jìn)行代碼格式(psr)驗(yàn)證和commit-msg驗(yàn)證,如果發(fā)生錯(cuò)誤,請按照提示糾正即可。
項(xiàng)目地址: https://github.com/TIGERB/easy-php
TODO
- 懶加載優(yōu)化框架加載流程
 - 性能測試和優(yōu)化
 - 變更Helper助手類的成員方法為框架函數(shù),簡化使用提高生產(chǎn)效率
 - 提供更友善的開發(fā)api幫助
 - 模塊支持?jǐn)?shù)據(jù)庫nosql自定義配置
 - 支持mysql主從配置
 - ORM提供更多鏈?zhǔn)讲僮鱝pi
 - 框架log行為進(jìn)行級別分類
 - 想辦法解決上線部署是配置文件問題
 - 基于phar文件和git webhook自動化打包部署
 - ...
 
















 
 
 










 
 
 
 