如今的 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 并完善它,开发配置一个本身的框架,以你的需求为出发,选择你最喜欢的技术和组件。