如今的 PHP 框架不少,固然不止 PHP ,即便是其餘編程語言也有不少框架,這篇文章講 PHP 框架構建是由於我對 PHP 的生態最爲熟悉,但這個方法一樣也適用於其餘編程語言框架的構建。php
框架是爲了提高咱們的應用開發效率,市面上有不少開源免費的框架給咱們使用,咱們盡能夠拿來用,爲何還要本身構建一個本身的框架呢?緣由就在於市面上的開源框架,是給大部分人用的,給通用項目用的,做爲框架的開發者是不知道本身的框架使用者的具體業務的,因此開源框架必定是知足大部分人的需求,並且力求可以爲開發者提供全部可能用到的功能。前端
可是對於一個商業項目或者是一個你本身要作的項目也許只能用到框架的不多一部分功能,或者是框架給你提供的東西並非最符合你本身的需求的,你使用了框架的一部分功能,另外一部分根本沒用,這樣使用框架首先是性能上的損失,一些你根本用不到的功能卻要下降你應用的性能顯然不合適的。再就是也許框架提供的功能不是你想要的,或者這個功能這個框架提供的並非符合你需求的,又或者要使用這部分功能必須按照框架開發者制定的規範來使用,這個規範並符合你的開發哲學。mysql
各類現代編程語言都有本身的包管理工具,PHP 就是 composer ,利用它咱們就能夠構建屬於本身的框架了,並能很好的組織咱們的框架。laravel
咱們該怎麼開始構建咱們本身的框架呢?從零開始嗎?這個問題沒有標準答案,若是你要作的項目要求很嚴格,從底層開始就要保證項目架構的最穩定可控,那麼建議你從零開始。若是要求不是很是嚴格那麼咱們就從那些開發一個應用最基本須要的功能開始,這樣的功能誰提供呢?PHP 有不少微框架,這些框架提供開發一個應用最基礎的功能,咱們能夠從這裏開始。git
首先咱們經過 composer init
初始化一個項目:程序員
{
"name": "dongm2ez/m2ez-framework",
"description": "a dongm2ez's framework",
"keywords": ["framework", "m2ez-framework"],
"license": "MIT",
"authors": [
{
"name": "dongm2ez",
"email": "dongm2ez@163.com"
}
],
"require": {}
}複製代碼
這就是我獲得的一個 composer.json
的描述文件,如今咱們就從這裏開始。個人目標是構建一個最符合我開發習慣的框架,讓個人開發效率最高。github
我選擇 Slim 框架做爲個人框架基礎框架,這是一個微框架,我喜歡它,它足夠簡單,提供了 web 開發 和 API 開發最基礎的功能,並且還有一個緣由開發這個框架的做者寫了一本名爲《Modern PHP》的書,這本書顛覆了我對 PHP 這個語言的認知,開始喜歡並樂於使用它。web
在引入這個框架以前我還要對 PHP 版本作個限制,從我使用 PHP 從 5.2 開始到如今,PHP 已經發展到 PHP 7.2 了,可是我不想再去使用低版本的 PHP,一個是 PHP 低版本立刻將失去官方的支付,另外一個是一些 PHP 的新特性我不能使用,並且低版本的性能也是很差的。因此我要將個人框架限制在 PHP 7.0 以上,同時我但願個人框架對中文有更好的支持。sql
那麼我將更新個人框架 composer.json
文件:shell
{
"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"
}
}複製代碼
這樣我就得到了一個最基礎的個人框架版本,可是我還沒完成,由於咱們沒有定義個人框架目錄結構。我以爲 laravel 框架的目錄劃分是挺讓我喜歡的,但我又不徹底喜歡 laravel 的目錄結構,我須要對它進行改造。
├── app
│ ├── Helpers.php
│ ├── Http
│ │ └── Controllers
│ └── Models
├── composer.json
└── tests複製代碼
我這樣設置個人目錄結構,並更新個人 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/"
}
}
}複製代碼
這樣的框架能夠訪問嗎,顯然是不行的,咱們還要加一些東西讓咱們的框架真正能夠跑起來,而後在來迭代它。
├── app
│ ├── Helpers.php
│ ├── Http
│ │ └── Controllers
│ └── Models
├── bootstrap
│ ├── app.php
│ └── autoload.php
├── composer.json
├── config
├── public
│ └── index.php
├── routers
└── tests複製代碼
咱們將目錄結構改形成這樣,並編寫一些啓動框架的代碼到相應的文件
// public/index.php
<?php
require __DIR__.'/../bootstrap/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$app->run();
// bootstrap/app.php
<?php
$app = new \Slim\App;
return $app;
// bootstrap/autoload.php
<?php
define('M2EZ_START', microtime(true));
require __DIR__.'/../vendor/autoload.php';複製代碼
而後運行 composer install
安裝框架的依賴包,安裝完成後咱們的目錄中就會多出一個 vendor
的目錄和 composer.lock
的文件,此時運行 php -S 0.0.0.0:8080 -t public public/index.php
利用 PHP 自帶的 web 服務器進行測試,爲了這個命令更簡單使用,咱們能夠將這個命令加到 composer.json
的 script
中。
此時訪問 127.0.0.1:8080
或 localhost:8080
就能夠看到以下的頁面:
這說明框架正確啓動了,那麼咱們怎麼肯定框架工做正常呢,這裏有個簡單方法:
<?php
use Slim\Http\Request;
use Slim\Http\Response;
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();複製代碼
對 public/index.php
的代碼進行修改,此時訪問 http://localhost:8080/hello/dongm2ez
,那麼咱們就會看到:
測試是成功了,可是咱們不能把路由和邏輯都寫到 index.php
文件裏,所以咱們須要代碼更好的組織。要讓咱們的目錄規劃發揮正在的做用。
爲了單獨管理路由,我將路由單獨寫在 routers
文件夾中,在文件夾中咱們新建兩個 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();複製代碼
變成這樣,這樣我就能夠單獨管理 API 和 WEB 項目的路由了,若是有其餘路由就也能夠 require 更多路由。
<?php
$app->get('/', '\App\Http\Controllers\WelcomeController:index');複製代碼
而咱們的控制器長什麼樣呢,是這個樣子的:
<?php
namespace App\Http\Controllers;
use Slim\Http\Request;
use Slim\Http\Response;
class WelcomeController extends Controller {
public function index(Request $request, Response $response) {
$response->getBody()->write("Hello, world");
return $response;
}
}複製代碼
咱們知道在現代化的框架中,容器會讓咱們很方便,咱們的基礎框架 Slim 提供一個容器的實現,固然你也可使用其餘的第三方的,那麼這顯然是咱們想要的結果,不是隻能使用框架提供的,咱們能夠隨時換掉框架的功能,換成咱們想要的一樣功能組件。
使用也很簡單,在 app.php
文件中初始化 Slim 框架時將容器實例傳遞給它就能夠了。
<?php
$container = new \Slim\Container;
$app = new \Slim\App($container);
return $app;複製代碼
還記得上面那個控制器繼承的基類控制器嗎,那也是我本身寫的,裏面能夠作一些全部控制器都有可能用的的操做封裝。好比我爲了更方便的使用容器,我在基類裏初始化了一個容器實例。
<?php
namespace App\Http\Controllers;
use Interop\Container\ContainerInterface;
abstract class Controller {
protected $ci;
public function __construct(ContainerInterface $ci) {
$this->ci = $ci;
}
}複製代碼
如今我已經有了 路由功能,有了控制器功能,還有請求響應的操做,那麼做爲一個完整的框架那麼必須有訪問數據庫的方法。
我很喜歡 Laravel 提供的數據庫 ORM 組件,那麼我就決定使用它了,執行 composer require illuminate/database "~5.5"
,我選擇了最新的 Laravel 長期支持版 ORM 。
咱們須要此時在 config
文件夾中新添加一個文件 databases.php
文件:
<?php
return [
'settings' => [
'db' => [
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'database',
'username' => 'user',
'password' => 'password',
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
]
],
];複製代碼
而後修改 public/index.php
:
<?php
require __DIR__ . '/../bootstrap/autoload.php';
$config = require __DIR__ . '/../config/databases.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
require __DIR__ . '/../routers/web.php';
require __DIR__ . '/../routers/api.php';
$app->run();複製代碼
修改 bootstrap/app.php
:
<?php
$container = new \Slim\Container($config);
$app = new \Slim\App($container);
// Service factory for the ORM
$container['db'] = function ($container) {
$capsule = new \Illuminate\Database\Capsule\Manager;
$capsule->addConnection($container['settings']['db']);
$capsule->setAsGlobal();
$capsule->bootEloquent();
return $capsule;
};
return $app;複製代碼
而後咱們就能夠在控制器中使用 ORM 功能了。
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\Database\DatabaseManager;
use Illuminate\Support\Facades\DB;
use Interop\Container\ContainerInterface;
use Slim\Http\Request;
use Slim\Http\Response;
class WelcomeController extends Controller {
public function index(Request $request, Response $response) {
/** @var DatabaseManager $db */
$db = $this->ci->get('db');
$user = $db->table("user")->first();
var_dump($user);
$response->getBody()->write("Hello, world");
return $response;
}
}複製代碼
那麼此時這個框架已是一個能夠開發 API 功能的框架了,若是要開發 Web 站我可能還須要加入渲染模板組件,不管是 twig
、Smarty
、Haml
仍是 Blade
,全都看你的喜愛了。固然我以爲我作到這裏就能夠了,夠我用了,由於對於前端我更喜歡用 React
或者是 Vue
去實現它。
須要更簡便的操做 session
、cookie
那麼咱們也能夠添加相應的組件,各類已經有的框架了都提供這樣的組件,看看你更喜歡哪個了,如今你的框架你作主,你想添加什麼就能夠添加什麼組件,通過這樣的定製的框架必定是最符合你開發需求的。
我這裏只是對已有的組件進行了配置組裝,一旦哪天你發現全部的開源組件都知足不了你的需求的時候,由於你對你的框架了解,你能夠本身造個輪子給本身的框架用,若是你寫的好那麼你也會創造出一個極好用的框架,如今最流行的 PHP 框架,你能夠看看它的 composer.json
文件,它就是在前人的基礎上進行開發維護的,已經有的功能他拿來直接用,以爲別人作的不完善的地方本身造一個輪子給你們用。
並且我這裏也沒有用到太多的設計模式,你還能夠改造你的框架,利用PHP的魔術方法,反射,SPL 等等讓你的框架更好,更容易擴展,更容易配置。
框架很神祕嗎?看過這篇文章我相信你不會這樣以爲了。
造一個框架很難嗎,是的很難,由於從 0 到 1 任何事都難,可是咱們如今還須要從 0 到 1 嗎,基本不須要了!站在巨人身上作事更容易,並且要記住,任何事只有行動起來你就會發現嘗試比躊躇不前更好,從小處開始,作小事,有一天這個小事就變成了大事。不積跬步無以致千里。
Laravel 爲何流行,由於做者本是一名 .net 開發者,在使用 CI 框架時萌生了想法要作一個更簡潔、靈活的框架,他的思想真的很先進嗎,不必定的,其餘開發語言早就有了 Laravel 中的功能,它只是在 PHP 中實現了它們。
以上例子其實告訴咱們,不要給本身貼標籤,人生不設限,你不是 PHP 程序員,你就是開發者,任何開發相關的東西咱們都該去了解和掌握,標籤只能別人給你貼,不要本身給本身貼。
最後,這份代碼我已經上傳到 GitHub ,若是你有興趣能夠 fork 並完善它,開發配置一個本身的框架,以你的需求爲出發,選擇你最喜歡的技術和組件。