摘要:如何構(gòu)建一個(gè)自己的框架為什么我們要去構(gòu)建一個(gè)自己的框架可能絕大多數(shù)的人都會說市面上已經(jīng)那么多的框架了,還造什么輪子。
如何構(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");
[file: public/index.php]
自加載模塊使用spl_autoload_register函數(shù)注冊自加載函數(shù)到__autoload隊(duì)列中,配合使用命名空間,當(dāng)使用一個(gè)類的時(shí)候可以自動載入(require)類文件。注冊完成自加載邏輯后,我們就可以使用use和配合命名空間申明對某個(gè)類文件的依賴。
[file: framework/Load.php]
錯(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 (FrameworkApp $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) "AppDemoLogicsGatewayEntrance" ];
這樣這個(gè)gateway就可以工作了。接著說說這個(gè)UserDefinedCase類,UserDefinedCase會在框架加載到路由機(jī)制之前被執(zhí)行,這樣我們就可以靈活的實(shí)現(xiàn)一些自定義的處理了。這個(gè)gateway只是個(gè)演示,你完全可以天馬行空的組織你的邏輯~
視圖View去哪了?由于選擇了完全的前后端分離和SPA(單頁應(yīng)用), 所以傳統(tǒng)的視圖層也因此去掉了,詳細(xì)的介紹看下面。
[file: app/*]
使用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ā)布分支忽略該文件]
[file: frontend/*]
數(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ù)說是最優(yōu)雅的),那我們這里言簡意賅就叫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; }
[file: framework/orm/*]
服務(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");
[file: framework/Container]
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");
[file: framework/nosql/*]
接口文檔生成和接口模擬模塊通常我們寫完一個(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
[file: docs/*]
單元測試模塊基于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") ); }
phpunit斷言文檔語法參考
[file: tests/*]
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~。
[file: ./git-hooks/*]
輔助腳本cli腳本
以命令行的方式運(yùn)行框架,具體見使用說明。
build腳本
打包PHP項(xiàng)目腳本,打包整個(gè)項(xiàng)目到runtime/build目錄,例如:
runtime/build/App.20170505085503.phar[file: ./build]
如何使用?執(zhí)行:
composer create-project tigerb/easy-php easy --prefer-dist && cd easy網(wǎng)站服務(wù)模式:
快速開始一個(gè)demo:
cd bin && php cli --rundemo如下:
客戶端腳本模式:
php cli --method=-- = ... 例如, php cli --method=demo.index.get --username=easy-php 獲取幫助:
使用命令 php cli 或者 php cli --help
Swoole模式:
cd public && php server.php獲取幫助:
使用命令 php cli 或者 php cli --help
docker環(huán)境本框架提供docker開發(fā)環(huán)境,一行命令幾分鐘構(gòu)建你的開發(fā)環(huán)境,詳細(xì)請點(diǎn)擊easy-env查看。
性能-fpmab -c 100 -n 10000 "http://easy-php.local/Demo/Index/hello"Document Path: / Document Length: 53 bytes Concurrency Level: 100 Time taken for tests: 3.259 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 1970000 bytes HTML transferred: 530000 bytes Requests per second: 3068.87 [#/sec] (mean) Time per request: 32.585 [ms] (mean) Time per request: 0.326 [ms] (mean, across all concurrent requests) Transfer rate: 590.40 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 0 0.3 0 4 Processing: 6 32 4.0 31 68 Waiting: 6 32 4.0 31 68 Total: 8 32 4.0 31 68 Percentage of the requests served within a certain time (ms) 50% 31 66% 32 75% 33 80% 34 90% 39 95% 41 98% 43 99% 46 100% 68 (longest request)性能-Swooleab -c 100 -n 10000 "http://easy-php.local/Demo/Index/hello"Concurrency Level: 100 Time taken for tests: 1.319 seconds Complete requests: 10000 Failed requests: 0 Total transferred: 1870000 bytes HTML transferred: 160000 bytes Requests per second: 7580.84 [#/sec] (mean) Time per request: 13.191 [ms] (mean) Time per request: 0.132 [ms] (mean, across all concurrent requests) Transfer rate: 1384.39 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 5 10.6 3 172 Processing: 1 9 13.4 7 177 Waiting: 0 7 11.7 6 173 Total: 3 13 16.9 11 179 Percentage of the requests served within a certain time (ms) 50% 11 66% 12 75% 13 80% 14 90% 15 95% 17 98% 28 99% 39 100% 179 (longest request)問題和貢獻(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)化框架加載流程
變更Helper助手類的成員方法為框架函數(shù),簡化使用提高生產(chǎn)效率
提供更友善的開發(fā)api幫助
模塊支持?jǐn)?shù)據(jù)庫nosql自定義配置
支持mysql主從配置
ORM提供更多鏈?zhǔn)讲僮鱝pi
框架log行為進(jìn)行級別分類
想辦法解決上線部署是配置文件問題
性能測試和優(yōu)化
...
Easy PHP:一個(gè)極速輕量級的PHP全??蚣?/pre>掃面下方二維碼關(guān)注我的技術(shù)公眾號,及時(shí)為大家推送我的原創(chuàng)技術(shù)分享
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/22920.html
摘要:的機(jī)器學(xué)習(xí)庫的機(jī)器學(xué)習(xí)庫,包括算法交叉驗(yàn)證神經(jīng)網(wǎng)絡(luò)等內(nèi)容。在即將到來的大會上,她將和大家分享在機(jī)器學(xué)習(xí)領(lǐng)域的全新可能。入門總結(jié)入門相關(guān),如安裝配置基本使用等。 基于 Swoole 開發(fā) PHP 擴(kuò)展 Swoole-1.9.7 增加了一個(gè)新特性,可以基于 Swoole 使用 C++ 語言開發(fā)擴(kuò)展模塊,在擴(kuò)展模塊中可以注冊 PHP 內(nèi)置函數(shù)和類。現(xiàn)在可以基于 Swoole 來編寫 PHP ...
摘要:如何才能成為一名專業(yè)的開發(fā)者資深開發(fā)者在其博客上分享了一些心得。要想成為一個(gè)專業(yè)的程序員,首先要成為一個(gè)中級程序員。永遠(yuǎn)不要低估陪伴的力量結(jié)論當(dāng)你專注于實(shí)踐上面所提到各種方法的時(shí)候,你就在成為專業(yè)開發(fā)者的路上。 如何才能成為一名專業(yè)的PHP開發(fā)者?資深Web開發(fā)者Bruno Skvorc在其博客上分享了一些心得。 showImg(http://segmentfault.com/img...
摘要:為了成為一個(gè)專家,他必須先成為中級者。它非常適合于急于求成或者沒有太多技術(shù)的人,但掌握絕對無法使你成為一個(gè)專業(yè)的開發(fā)者它使用意大利面條式的編碼,教你的是不合適的設(shè)計(jì)原則。 這一篇文章是Becoming a PHP Professional系列 4 篇博文中的第 1 篇。 當(dāng)瀏覽各類與PHP相關(guān)的博客時(shí),比如Quora上的問題,谷歌群組,簡訊和雜志,我經(jīng)常注意到技能的等級分化。問題都類...
摘要:原文發(fā)表在我的個(gè)人網(wǎng)站利用完善自己的框架三緩存本教程示例代碼見回顧上兩篇文章中我們完成了視圖加載類和郵件發(fā)送類的設(shè)計(jì),完成了兩個(gè)可插拔組件。相比使用文件作為緩存,擁有更高的性能更好地可維護(hù)性和更強(qiáng)大的操作。 原文發(fā)表在我的個(gè)人網(wǎng)站:利用 Composer 完善自己的 PHP 框架(三)——Redis 緩存 本教程示例代碼見 https://github.com/johnlui/My...
閱讀 2984·2021-10-19 11:46
閱讀 979·2021-08-03 14:03
閱讀 2934·2021-06-11 18:08
閱讀 2905·2019-08-29 13:52
閱讀 2744·2019-08-29 12:49
閱讀 480·2019-08-26 13:56
閱讀 924·2019-08-26 13:41
閱讀 849·2019-08-26 13:35