从入口文件开始说起
框架概述
简单地说框架就是封装好各种便利功能的工具,
同时还会“自动化”的处理复杂的事情,
通俗的说就是让程序员开开心心码代码的结构。
我们现在要撸的这套框架,应该满足以下需求:
- 可以缩短开发时间
- 支持后期扩展
不是仅仅封装一下类和方法就叫框架了,
它整体的结构应该让程序员使用起来非常舒服!
环境
PHP 版本:7.2.31
Swoole 版本:4.5.2
PHP Redis 版本:5.1.1
这里我用的 redis 是 PHP 的扩展,
如果你不安装 redis 扩展也可以用 composer 引入。
框架起名
第一步是给框架起个好名字,
我把这个框架叫做“火兔引擎”,
创建目录:/firerabbit-engine
。
这个目录名称和位置你可以随意设置,
之后用 nginx 指定就行了,最简单的就是放到 www 目录下。
虚拟域名
平时我们可能都是用 127.0.0.1
,
但是本地的项目多了的话,就不能用单一的地址了。
我们可以设置一个虚拟域名,即修改本地的主机解析记录。
windows 系统和 mac os 都是修改 hosts 文件,
以 mac os 为例:
1 | sudo vim /etc/hosts |
在最底下插入一行:
1 | 127.0.0.1 firerabbit-engine.ht |
firerabbit-engine.ht
是你设置的虚拟域名,
可以任意设置,但是最好不要是跟真正域名冲突的,
比如你设置了 baidu.com
,
那你访问百度就会变成解析到自己本机了,
这个 .ht
后缀也是我虚构的。
测试的时候就可以在浏览器输入 firerabbit-engine.ht
访问博客地址。
Hello World!
现在项目是空的,一个文件也没有,
首先在项目目录下新建一个 http_server.php
。
然后查看 Swoole 官方文档:Http 服务器
直接把示例代码抠下来复制到 http_server.php
:
1 | $http = new Swoole\Http\Server('0.0.0.0', 9527); |
注意我这里把默认端口改成 9527 了。
之后可以用 127.0.0.1:9527
进行访问。
如果你跟我一样是用 docker 的话,
记得要设置端口映射:-p 9527:9527
,不然宿主机是访问不到的。
接着在项目的目录下,在控制台输出命令启动程序:php http_server.php
你会看到光标卡住了,这说明程序已经运行了。
swoole 本身就自带了 http 服务器的功能,因此不需要借助 nginx
1 | cd /www/firerabbit-engine/ |
做完这一步就可以用 IP 地址+端口号的方式访问了,
打开浏览器,输入地址:127.0.0.1:9527
可以看到:
1 | Hello Swoole. #7090 |
后面的是随机数字所以每次刷新都不一样。
控制台也打印出了请求详情,
这样,第一步就成功了。
Nginx 转发
虽然 swoole 自带了 http 服务器,
但是 swoole 处理 css、js 等静态文件却没有什么好的方法,
而 nginx 的强项就是处理静态文件,一拍即合,各展所长!
所以这里我们结合 nginx 处理静态文件,
静态文件 nginx 处理,动态文件 swoole 处理;
同时还可以利用上面的虚拟域名来访问网站。
nginx 增加一个配置文件 firerabbit.conf
:
1 | server { |
因为我是用 docker 搭建的 nginx 和 php 环境,
容器之间不能直接通过 IP 访问,而是要用容器名。
如果你不是用 docker 环境,php-fpm72 要改成 127.0.0.1
:
1 | proxy_pass http://127.0.0.1:9527; |
然后执行命令:nginx -s reload
来平滑重启 nginx。
接着在浏览器输入上面设置的虚拟域名:http://firerabbit-engine.ht/
可以看到同样的 hello world 页面。
通过 nginx 的转发,
所有静态文件如图片、css 文件等都会被转发到请求 public 这个目录,
不会发送到 swoole 那边,只有找不到文件才会转发给 swoole。
捋一捋程序的处理流程:
在浏览器输入域名的时候,用户的请求先经过 nginx 的正则判断,
如果是 jpg 之类的结尾,就去 public 这个目录下面找,
如果找不到文件了,再转发给 http://php-fpm72:9527
。
这样 nginx 的配置也弄好了。
项目结构
基本结构
一个典型的框架,包含以下几个特征:
- 单一入口
- MVC 结构(模型、视图、控制器分离)
- 自动加载(composer)
单一入口这个很好理解,
swoole 默认即单一入口,
也就是全部请求都转发给 http_server.php
文件处理。
MVC 就是分离代码,让每个类的功能更加单一,
简单的说就是 PHP 写接口,前端人员写前端页面,
但是我们并不会真正的完全分离,因为博客是要做 SEO 的,
如果全部都用接口,搜索引擎就不会收录了。
我们的 MVC 结构会用模板引擎来实现代码解耦。
自动加载是我们这个框架的核心部分,
因为我们会依赖其他组件,同时我们自己的类也需要加上命名空间。
现代 PHP composer 自动加载几乎是必备的。
执行流程
捋一捋框架的整体运行流程:
swoole 收到 nginx 转发来的请求,
通过“解析器”将请求 uri 解析成对应的控制器和方法,
(解析器就是路由功能,解析器是一个类文件,自己要写的)
以及获取各种参数、cookie 等等,
将这些参数传递给 controller,
controller 负责处理业务逻辑。
虽然 MVC 框架已经很流行了,
但是我们这里不推荐在 controller 处理逻辑,
在这里写业务,后面这个文件就会变成几千行,不方便维护。
我们再增加一个 Service 层,将业务逻辑的代码移到 Service 去处理。
这样整个框架的流程大致可以捋顺了,如下图:
swoole 收到 nginx 转发的请求,
通过解析器(一个类文件),
将请求的 uri 解析成对应的路由和参数,
实例化路由的类并且调用对应的方法,并将参数传递给类的实例化对象,
类的实例化对象(controller)再调用 service 来处理逻辑。
(controller 的作用 与 nginx 类似,也是分发请求,但是它还有一个返回响应的功能)
controller 得到参数和 cookie 等,交给 service 处理,
service 再调用 model 或者其他的类库,返回处理结果,将值返回给 controller,
最后一步,controoler 收到返回的值,再返回对应类型的响应。
(响应的种类有很多,例如 json、html 等等,如:content-type: text/html; charset=UTF-8
)
原本 controller 是处理逻辑代码的地方,
在这里我们把它变成分发请求了,
这是防止以后 controller 变得臃肿,
但实际上,
逻辑代码转移到 service 会让 service 变得臃肿……
(目前没有更好的方法了)
控制器的处理流程可以看图:
我个人比较喜欢这种方式,
最终我们写的控制器会是这样的:
1 |
|
控制器的代码会变得十分简洁。
控制器还有一个作用就是验证数据,
比如用户提交的表单,
如果输入的邮箱格式不正确就直接返回错误的响应,
数据验证就全部在 controller 处理了,而不是传到 service。
我把控制器的功能限制为:① 验证数据 ② 转发给 service 处理 ③ 返回结果
而 service 则处理数据库操作之类的业务逻辑。
controller 和 service 的功能变得十分单一,从而降低耦合性。
问题就是,控制器干净了,service 却脏了……
service 不仅要处理数据库,还要更新缓存之类的,想想就可怕。
后面我们可以考虑把数据库处理的逻辑转移到 model,
这样可以减少 service 臃肿度。
第一阶段目标
现在思路已经很明确了,
但是很多细节部分我们还没有设计,
比如日志系统、缓存系统、数据库系统、配置参数文件、中间件……
下期统一进行规划,本篇文章就到这了。