一个简易的PHP框架

此篇文章是想总结下工作中一直在用的一个简单的PHP框架,真的是超级简单,解析步骤如下:

  1. 访问:https://abc.com/user/info
  2. nginx将请求指向index.php
  3. 解析出对应的控制器User和执行方法info
  4. 通过autoload机制实例化控制器,执行对应的方法
  5. 输出结果

项目目录结构如下:

1
2
3
4
5
6
7
8
9
framework/
├── public/
│ └── index.php ← 入口文件
├── src/ ← 项目源码
│ └── controller/ ← 控制器目录
│ └── App.php ← 核心解析类
├── vendor/ ← Composer安装的第三方库
│ └── autoload.php
└── composer.json

要点

nginx

因为框架使用了单一入口,任意请求都会指向index.php文件,所以只需要这么配置:

1
2
3
location / {
try_files $uri $uri/ /index.php?$query_string;
}

autoload

框架中的自动加载使用的是PSR-4的规范,大致原理就是通过文件系统目录结构与PHP的命名空间一一映射起来,即命名空间指向文件所在目录,具体PSR规范可以看这篇文章。所以框架的composer.json文件中我们这样定义:

1
2
3
4
5
"autoload": {
"psr-4": {
"app\\": "src/"
}
}

app开头的命名空间指向src目录。

入口

既然是入口,那尽量不要做过多限制,让入口变得很臃肿、复杂,只要引入必要的依赖和配置就行,其他事情就交给别人去做就好了,具体如下:

1
2
3
4
5
6
7
8
9
header("Content-type: text/html; charset=utf-8");
date_default_timezone_set("Asia/Shanghai");

require "../vendor/autoload.php";

define('ROOT', __DIR__ . '/../src');

$app = new \app\App() ;
$app->run() ;

index.php内容就很简单,没啥好说的,主要解析操作和控制器的分发交给了\app\App处理。

控制器

这个简单的框架是没有单独的路由配置文件的,熟悉laravel的都知道,写业务逻辑之前需要先在routes.php文件中定义路由,而这个框架直接是通过请求路径对应到指定的控制器及方法,比如请求/user/info,控制器就是User,接收方法就是Info,看下怎么解析的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// App.php

public function parse() {

$uri = $this->param('REQUEST_URI', 'server');
$p = strpos($uri, '?');
if ($p > 0) {
$uri = substr($uri, 0, $p);
}

@list($_, $controller, $action) = @explode('/', $uri);
if (empty($controller)) {
$controller = 'index';
}
if (empty($action)) {
$action = 'default';
}

if ($controller && $action) {
$c = '\app\controller\\' . ucfirst($controller);
$a = 'Action' . ucfirst($action);
if (is_callable(array($c, $a))) {
$this->controllerName = $c;
$this->actionName = $a;

return true;
}
}
}

public function run() {
$this->parse();

if ($this->controllerName && $this->actionName) {

$c = $this->controllerName;
$a = $this->actionName;

$ins = new $c();
$ins->app = $this;

do {
$res = $ins->before() ;
if ($res === false) break;

$res = $ins->$a();
if ($res === false) break;

$ins->after();

} while(false);
}
}

上面就是大致的解析流程,解析完成后会实例化对应的并调用方法,我们看下控制器是怎么写的:

1
2
3
4
5
6
7
8
9
10
namespace app\controller;

class User extends Base
{
public function ActionInfo()
{
$this->setResponse('aa', 123);
$this->setResponse('bb', 'hello');
}
}

这里,可以看到控制器和方法有点像两级目录,控制器属于一级,对应的方法属于二级目录,简单清晰,但是不能处理复杂路由。

数据库

公司内部使用的是自研的一套数据模型,就是封装的一个抽象数据层,包括了redismysql等,如果要使用Eloquent等其他比较流行的ORM,这里可以配合composer autoload使用。

缺点

没有统一的路由配置文件,每个路由需要人工记住有哪些控制器和方法。

一个路由只能针对一个请求使用,拿上面的/user/info来说,getpost请求都会发送到同一个处理方法上,这就变得很耦合,而且也不符合不同请求方法本身的目的,但可以建新的路由来解决,可也会增加路由数量造成维护的负担。

缺少一套模版引擎。

总结

相比于市面上成熟的框架,这个框架可谓相当的简陋,可以说是非常原始了,没有任何高大上的技术,比如依赖注入、服务容器、服务提供者等。我认为框架就是一套约定好的代码编写风格以及大量工具类的集合,学会使用框架就是按照这套风格写控制器,什么地方放配置文件,掌握工具类的使用,毕竟大部分情况都是处理CURD和编写业务逻辑,运用框架会大幅度提高开发效率。所以在选择框架时没必要在意使用了什么技术,反而更应该在意框架的效率/性能以及周围的生态。当让这也不妨碍我们自己造轮子 ^^


欢迎阅读本篇文章,如有兴趣可以关注博主公众号哦: