构建自己的 PHP 框架
从最原始的 echo 出发 , 衍生出一套框架的过程
整体结构图
画了一个粗糙的结构图, 后面的演进过程中, Router 和 Event Dispatcher 其实纳入到 Http Kernel 里面了 , 虚线里的是主要的 HTTP 请求处理流程, 周边是可选扩展
演进过程
完整的演进过程在这个 framework repo 里, 每一个演进是一个 commit, 方便 diff 出代码差异
0.0.3 - 演进的方向是 OOP 框架,所以请求和响应自然地需要 morph 成对象, http-foundation 库就提供了 Request 类和 Response 类两个实现, 并且做了许多额外的工作, 如验证响应的 HTTP 规范, 代码经过严格审查, 修复了各种安全漏洞, 并且还有更多功能等待探索(后面会有一部分)
0.0.5 - 使用单一入口的理由, 把请求分发逻辑移到服务层, 提高灵活性
0.0.8 - 使用 symfony/routing 的理由, URL 美化, 匹配规则更灵活, 并且官方提供了一个路由缓存的实现, 是一个装饰器模式, 直接包裹原实现即可
0.0.9 - 使用 http-kernel 的理由, 延迟解析控制器, 自动注入控制器参数
0.1.0 - 一个基本的请求处理框架形成了, 将请求处理逻辑移到 Framework 类的理由, 关注分离原则, 保持对外暴露的 front.php 干净, 只负责请求框架的实例化和 handle()方法调用
0.1.1 - 引入 phpunit, 并将注入的参数改为接口类型, 提高可测试性 , test doubles
0.1.2 - 引入 event dispatcher , 可选, 提高扩展性
0.1.3 - 引入 DI 容器, 好处可以分为 DI 和容器两部分讲
DI (依赖注入) :
- 两个类不再强耦合, 一个类有变化不需要修改另一个类的代码
- 提高可测试性, 在单元测试时, 不关心的部分可以mock 替代掉
容器:
- 统一定义实例化过程的静态描述, 避免最外层到处都是 new 的 copy / paste (如需要定义不同的 front.php 时)
- 从可测试性和易于修改两个角度, 和 DI 一致
- 实现单例模式
- 其他没探索到的功能 (如自动反射注入)
0.1.4 - Framework 实现 HttpKernelInterface, HttpCache包裹原 Framework, 实现缓存功能 (这里的 HttpCache 要好好看看, 都是 HTTP 缓存规范的实现,如 etag,cache-control)
0.1.5 - 引入官方 HttpKernel, 利用埋好的扩展点,处理字符串响应,响应格式检查,全局异常处理, 自定义错误处理返回内容实现
0.1.6 - 引入 DI 容器, FrontController 变为瘦模型, 所有类的实例化由容器负责, 预定义好实例化的静态描述
结尾
从创建一个 MVP (最小可行产品) 开始, 慢慢演进的过程可以了解到这样设计的原因, 面向对象技能也因此有所提高