為什么要構(gòu)建自己的 PHP 框架?
現(xiàn)在的 PHP 框架很多,當(dāng)然不止 PHP ,即使是其他編程語(yǔ)言也有很多框架,這篇文章講 PHP 框架構(gòu)建是因?yàn)槲覍?duì) PHP 的生態(tài)最為熟悉,但這個(gè)方法同樣也適用于其他編程語(yǔ)言框架的構(gòu)建。
框架是為了提升我們的應(yīng)用開發(fā)效率,市面上有很多開源免費(fèi)的框架給我們使用,我們盡可以拿來(lái)用,為什么還要自己構(gòu)建一個(gè)自己的框架呢?原因就在于市面上的開源框架,是給大部分人用的,給通用項(xiàng)目用的,作為框架的開發(fā)者是不知道自己的框架使用者的具體業(yè)務(wù)的,所以開源框架一定是滿足大部分人的需求,而且力求能夠?yàn)殚_發(fā)者提供所有可能用到的功能。
但是對(duì)于一個(gè)商業(yè)項(xiàng)目或者是一個(gè)你自己要做的項(xiàng)目也許只能用到框架的很少一部分功能,或者是框架給你提供的東西并不是最符合你自己的需求的,你使用了框架的一部分功能,另一部分根本沒用,這樣使用框架首先是性能上的損失,一些你根本用不到的功能卻要降低你應(yīng)用的性能顯然不合適的。再就是也許框架提供的功能不是你想要的,或者這個(gè)功能這個(gè)框架提供的并不是符合你需求的,又或者要使用這部分功能必須按照框架開發(fā)者制定的規(guī)范來(lái)使用,這個(gè)規(guī)范并符合你的開發(fā)哲學(xué)。
從哪開始?
各種現(xiàn)代編程語(yǔ)言都有自己的包管理工具,PHP 就是 composer ,利用它我們就可以構(gòu)建屬于自己的框架了,并能很好的組織我們的框架。
怎么開始?
我們?cè)撛趺撮_始構(gòu)建我們自己的框架呢?從零開始嗎?這個(gè)問(wèn)題沒有標(biāo)準(zhǔn)答案,如果你要做的項(xiàng)目要求很嚴(yán)格,從底層開始就要保證項(xiàng)目架構(gòu)的最穩(wěn)定可控,那么建議你從零開始。如果要求不是非常嚴(yán)格那么我們就從那些開發(fā)一個(gè)應(yīng)用最基本需要的功能開始,這樣的功能誰(shuí)提供呢?PHP 有很多微框架,這些框架提供開發(fā)一個(gè)應(yīng)用最基礎(chǔ)的功能,我們可以從這里開始。
首先我們通過(guò) composer init 初始化一個(gè)項(xiàng)目:
{ "name": "dongm2ez/m2ez-framework", "description": "a dongm2ez's framework", "keywords": ["framework", "m2ez-framework"], "license": "MIT", "authors": [ { "name": "dongm2ez", "email": "dongm2ez@163.com" } ], "require": {} }
復(fù)制代碼這就是我得到的一個(gè) composer.json 的描述文件,現(xiàn)在我們就從這里開始。我的目標(biāo)是構(gòu)建一個(gè)最符合我開發(fā)習(xí)慣的框架,讓我的開發(fā)效率最高。
我選擇 Slim 框架作為我的框架基礎(chǔ)框架,這是一個(gè)微框架,我喜歡它,它足夠簡(jiǎn)單,提供了 web 開發(fā) 和 API 開發(fā)最基礎(chǔ)的功能,而且還有一個(gè)原因開發(fā)這個(gè)框架的作者寫了一本名為《Modern PHP》的書,這本書顛覆了我對(duì) PHP 這個(gè)語(yǔ)言的認(rèn)知,開始喜歡并樂(lè)于使用它。
在引入這個(gè)框架之前我還要對(duì) PHP 版本做個(gè)限制,從我使用 PHP 從 5.2 開始到現(xiàn)在,PHP 已經(jīng)發(fā)展到 PHP 7.2 了,但是我不想再去使用低版本的 PHP,一個(gè)是 PHP 低版本馬上將失去官方的支付,另一個(gè)是一些 PHP 的新特性我不能使用,而且低版本的性能也是不好的。所以我要將我的框架限制在 PHP 7.0 以上,同時(shí)我希望我的框架對(duì)中文有更好的支持。
那么我將更新我的框架 composer.json 文件:
{ "name": "dongm2ez/m2ez-framework", "description": "a dongm2ez's framework", "keywords": ["framework", "m2ez-framework"], "license": "MIT", "authors": [ { "name": "dongm2ez", "email": "dongm2ez@163.com" } ], "require": { "php": ">=7.0.0", "ext-mbstring": "*", "slim/slim": "^3.0" } }
復(fù)制代碼這樣我就獲得了一個(gè)最基礎(chǔ)的我的框架版本,但是我還沒完成,因?yàn)槲覀儧]有定義我的框架目錄結(jié)構(gòu)。我覺得 laravel 框架的目錄劃分是挺讓我喜歡的,但我又不完全喜歡 laravel 的目錄結(jié)構(gòu),我需要對(duì)它進(jìn)行改造。
├── app
│ ├── Helpers.php
│ ├── Http
│ │ └── Controllers
│ └── Models
├── composer.json
└── tests復(fù)制代碼我這樣設(shè)置我的目錄結(jié)構(gòu),并更新我的 composer.json:
{ "name": "dongm2ez/m2ez-framework", "description": "a dongm2ez's framework", "keywords": ["framework", "m2ez-framework"], "license": "MIT", "type": "project", "authors": [ { "name": "dongm2ez", "email": "dongm2ez@163.com" } ], "require": { "php": ">=7.0.0", "slim/slim": "^3.0" }, "require-dev": { "phpunit/phpunit": "~6.0" }, "autoload": { "classmap": [ "app/Models" ], "psr-4": { "App\": "app/" }, "files": [ "app/Helpers.php" ] }, "autoload-dev": { "psr-4": { "Tests\": "tests/" } } }
復(fù)制代碼這樣的框架可以訪問(wèn)嗎,顯然是不行的,我們還要加一些東西讓我們的框架真正可以跑起來(lái),然后在來(lái)迭代它。
├── app
│ ├── Helpers.php
│ ├── Http
│ │ └── Controllers
│ └── Models
├── bootstrap
│ ├── app.php
│ └── autoload.php
├── composer.json
├── config
├── public
│ └── index.php
├── routers
└── tests復(fù)制代碼我們將目錄結(jié)構(gòu)改造成這樣,并編寫一些啟動(dòng)框架的代碼到相應(yīng)的文件
// public/index.php
<?php require __DIR__.'/../bootstrap/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $app->run(); // bootstrap/app.php <?php $app = new SlimApp; return $app; // bootstrap/autoload.php <?php define('M2EZ_START', microtime(true)); require __DIR__.'/../vendor/autoload.php';
復(fù)制代碼然后運(yùn)行 composer install 安裝框架的依賴包,安裝完成后我們的目錄中就會(huì)多出一個(gè) vendor 的目錄和 composer.lock 的文件,此時(shí)運(yùn)行 php -S 0.0.0.0:8080 -t public public/index.php 利用 PHP 自帶的 web 服務(wù)器進(jìn)行測(cè)試,為了這個(gè)命令更簡(jiǎn)單使用,我們可以將這個(gè)命令加到 composer.json 的 script 中。
此時(shí)訪問(wèn) 127.0.0.1:8080 或 localhost:8080 就可以看到如下的頁(yè)面:
file
這說(shuō)明框架正確啟動(dòng)了,那么我們?cè)趺创_定框架工作正常呢,這里有個(gè)簡(jiǎn)單方法:
<?php use SlimHttpRequest; use SlimHttpResponse; require __DIR__.'/../bootstrap/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $app->get('/hello/{name}', function (Request $request, Response $response) { $name = $request->getAttribute('name'); $response->getBody()->write("Hello, $name"); return $response; }); $app->run();
復(fù)制代碼對(duì) public/index.php 的代碼進(jìn)行修改,此時(shí)訪問(wèn) http://localhost:8080/hello/dongm2ez,那么我們就會(huì)看到:
file
測(cè)試是成功了,但是我們不能把路由和邏輯都寫到 index.php 文件里,因此我們需要代碼更好的組織。要讓我們的目錄規(guī)劃發(fā)揮正在的作用。
為了單獨(dú)管理路由,我將路由單獨(dú)寫在 routers 文件夾中,在文件夾中我們新建兩個(gè) PHP 腳本文件,然后在 public/index.php 中加入兩行代碼:
<?php require __DIR__ . '/../bootstrap/autoload.php'; $app = require_once __DIR__ . '/../bootstrap/app.php'; require __DIR__ . '/../routers/web.php'; require __DIR__ . '/../routers/api.php'; $app->run();
復(fù)制代碼變成這樣,這樣我就可以單獨(dú)管理 API 和 WEB 項(xiàng)目的路由了,如果有其他路由就也可以 require