《Create Your own PHP Framework》笔记
前言
大力推荐该教程:《Create Your own PHP Framework》
Symfony的学习蛮累的,官方文档虽然很丰富,但是组织方式像参考书而不是指南,一些不错的指导性文档常常是是看组件文档时提到了才偶然发现的,这方面感觉就跟看Laravel和Webpack的官方文档有差距。同时Google中找Symfony的问题也不像Laravel容易找到答案,经常是自己看完整个官方文档结合源码才解决,进度赶的时候真是折磨人。总的来讲,虽然非常非常强大,但是在掌握上,确实不像Laravel那么方便学习。如果从Linux的设计哲学上来讲,我认为Laravel是策略,Symfony是机制。策略的目标是在易用的前提下,提供足够的灵活性;而机制相反,在保证灵活性的情况下,足够易用,比较难学是自然的。策略需要依赖于机制之上,所以Laravel依赖Symfony。
之前在学Laravel时,看了《如何Composer一步一步构建自己的PHP框架》这个系列,对于Laravel的学习大有裨益。于是在学Symfony时,也是希望有个类似的教程,结果在Symfony官方文档中偶然找到了《Create Your own PHP Framework》,学完后再看Symfony确实清晰了很多。
这里简单做下每一节的笔记,主要记了一些设计思想的点,比较零散,看完原文再来看估计会有所共鸣。
笔记
introduction
When creating a framework, following the MVC pattern is not the right goal. The main goal should be the Separation of Concerns.”
看到这句话时,我想起之前跟别人谈如何一步步学习Laravel时说:“路由是框架的基石,而在这之上,通过构建MVC的每一层就完成了基本框架;然后再搭配一些现代必备特性比如命令行、测试;以及一些常用服务:队列、安全认证等。就能理解Laravel。”
一下子就被打脸了,Symfony提出了构建框架的主要目标是关注点分离。从理念层面上是对的,MVC不是唯一的解决,不过太过抽象,MVC只是一种具体的关注点分离的方法,对于普通开发者会比较容易掌握。实际上,如果我只想着关注点分离,也还不知道如何下手。
为什么要自己写一个框架?
- 研究Symfony, 这是我的主要目的;
- 根据自己特殊需求做一个自己的框架;
- 纯粹出于探索的乐趣;
- 重构旧代码以便符合现代的最佳实践;
- 证明你自己。。。
The HttpFoundation Componen
即便是最简单的事情,使用框架也好于不使用。再简单的代码都面临以下问题:
- 对参数的判断
- 安全问题,比如XSS攻击;
- 方便单元测试;
再简单的问题如果要满足上面的条件,写出的代码都比使用框架还累。
如果你认为安全性与可测试性不足以说服你停止写旧代码,赶紧采用新框架的话 ,那么你可以停止读本书并继续你以前的工作方式了。(深深地感受到作者的高冷)
框架存在的目的是让你更快地写出更好的代码,而不是让你有所牺牲,如果有什么牺牲的话,我想应该是学习成本的增加吧。
以后就算不使用框架,也应该使用HttpFoundation
组件的Request
和Response
处理请求与响应。
原文:The HttpFoundation Component
The Front Controller
用于分配路由的控制器称为前端控制器(Front Controller
),它根据$request->getPathInfo()
调用不同目标代码。这个框架到此最大的问题在于路由于于简单,所以下一节应该是解决路由问题。
The Routing Component
上面简单的路由并不太能满足我们的要求,比如我们想实现路由的通配符匹配就比较麻烦。
因此,使用第三方的路由库是必要的。symfony/routing
就很方便。这个路由很好,对象却有点多,刚看时还真是不太好理解。
Routing
组件的基本对象:
RouteCollection
路由集合Route
单个路由RequestContext
请求上下文,通过fromRequest
方法与Request
绑定。(这种分离有利于测试)UrlMatcher
将RouteCollection
与RequestContext
绑定
然后通过
$attributes = $matcher->match($request->getPathInfo());
获取当前的路由信息,下面这些实例表明每个路由都会有_route
这个属性,同时如果定义了通配属性,也会变成对应的变量。
print_r($matcher->match('/bye'));
/* Gives:
array (
'_route' => 'bye',
);
*/
print_r($matcher->match('/hello/Fabien'));
/* Gives:
array (
'name' => 'Fabien',
'_route' => 'hello',
);
*/
print_r($matcher->match('/hello'));
/* Gives:
array (
'name' => 'World',
'_route' => 'hello',
);
*/
另外,当match不到时,会抛出如下异常:Routing\Exception\ResourceNotFoundException
,
使用Routing有个额外的好处,就是可以从根据路由生成路径:
echo $generator->generate(
'hello',
array('name' => 'Fabien'),
UrlGeneratorInterface::ABSOLUTE_URL
);
// outputs something like http://example.com/somewhere/hello/Fabien
路由的问题解决了,但是到现在还没控制器,这个后面应该要解决了。
Templating
直接渲染模板是有问题的,当业务逻辑稍微复杂一点就无法在模板中完成。因此需要将逻辑与渲染模板分开。
这一节为什么不是直接谈控制器
呢,我想跟第一节作者提到的关注点分离
的概念有关,目前为止,框架的问题在于逻辑在模板中做很困难,所以当前事情是要把模板与逻辑抽离出来,本节模板逻辑分离是目的,控制器
只是惯例做法。
按照Symfony
的惯例。通过给Route
的属性
,增加_controller
这个键值,它指明路由对应的方法,框架将直接调用_controller
完成各种不同的工作。
这里有个注意点,路由的属性都被保存到$request->attributes
中,该属性用保存跟HTTP
没有直接相关的信息。
增加了_controller
属性之后,再将路由信息
剥离到单独一个文件src/app.php
,现在模板与业务逻辑区分开了。
The HtppKernel Component:The Controller Resolver]
上一节为止,所有的操作都是过程化的。我们希望将_controller
指向一个类的方法,比如LeapYearController
的indexAction
。改造起来也很简单。将路由的_controller
改为[new LeapYearController(), ‘indexAction’]
即可。
然而这也带来了另外一个缺点,不论路由有没有用到,在它们添加的时候,控制器都被初始化,这对性能是个很大的影响。因此我们希望只有用到的路由才初始化。这个问题可以使用http-kernel
模块解决。
http-kernel
提供了非常丰富的功能,不过我们现在只关心HttpKernel\Controller\ControllerResolver
和HttpKernel\Controller\ArgumentResolver
。
前者可以用来路由中确定出要调用的方法;后者用来确定要传递给方法的参数;参数解析器使用了反射机制,以便实现依赖注入
和将路由的attributes
的同名参数传递进去。调用路由方法与传参,自己做还是要费一定功夫的,所以使用这两个解析器都是必须的。
原文:The HtppKernel Component:The Controller Resolver
The Separation Of Concerns
我们的目标是构建一个框架,前面的代码虽然可满足要求,但是缺少封装,没有放到命名空间,这个在规模扩大时并不方便。同时每建一个新站都需要复制整个front.php
。对它们做封装可提高可用性和可测试性。
本节引入了命名空间
,创建Simplex\Framework
的类和控制器
以及增加psr-4
的自动加载。
本节的分离关注的意义其实是从工程层面体现的:通过对前面实现的功能做一次代码整理,揭示现代WEB PHP框架
的基本目录组织方法。
Unit Testing
这一节,对于Framework
这个的类测试了404
, 500
和正常响应
,该类的测试覆盖率为100%
。这一节对于后续学习单元测试是很有启发性的:
- 如何配置单元测试文件
phpunit.xml.dist
- 如何创建
Mock Object
,以避免要依赖真实环境; - 如何尽可能的覆盖测试,通过
404
,500
,正常响应
的示例说明; - 如何生成覆盖率报告:
$phpunit --coverage-text # 命令行输出
$phpunit --coverage-html=cov/ # 输出HTML文档
这一节的启发在于:在写代码时,传参应该尽量设计成接口才方便Mock
;而错误以throw
的方式抛出;这样子会方便测试。另外如果你能从单元测试的角度去考虑框架,就会发现很多框架中觉得可能多余的设计并不是多余的。比如Laravel
的Facade
。
原文:Unit Testing
event dispatcher
整个框架虽然是完备的,但称不上是一个好框架。所有的好框架都有很强的可扩展性。那么什么是可扩展性呢,作者给了一个蛮不错的定义:
Being extensible means that the developer should be able to easily hook into the framework life cycle to modify the way the request is handled.
实际上,event dispatcher
这个名字不好理解,我是直接把它当成Laravel
的middleware
来看待。
The HttpKernel Component: HttpKernelInterface
HttpKernelInterface
是HttpKernel
组件最重要的一个方法。许多组件都依赖于该接口,比如HttpCache
。所以自己设计框架的时候,应该实现该接口,以便更好地利用现有组件。(这一节跟下面一节总结起来呢就是一句话:自己实现的框架核心会有很多问题,还是使用HttpKernel
这个组件好)
原文:The HttpKernel Component: HttpKernelInterface
The HttpKernel Component: The HttpKernel Class
HttpKernel
是HttpKernelInterface
的默认实现。相比于自己实现,它提供了更完备的处理机制,比如我们自己的框架只处理了404
和500
的错误,但还有其他的错误没处理;另外,它提供了event dispatcher
的各种默认机制,允许灵活地控制异常时、控制器进入前后、渲染视图时的显示;最后,在安全方面和规模增长后的表现也在各个实际的网站中表现得十分优异。
原文:The HttpKernel Component: The HttpKernel Class
The DependencyInjection Comonent
front.php
的代码基本上在每个应用中都是重复的,可以考虑将其移到Framework
的构造函数
中,但是你会发现:没法添加新的listener
, 没办法模拟接口做单元测试等等。在实际场景中,我们需要区分开发环境与生产环境;或者想要添加越来越多的dispatcher
;改变response
的输出字符集等,由于相关的类都只在front.php
中出现,所以这些改动都要在front.php
中增加代码完成,最终显然会导致front.php
越来越大。而当我们搞一个新的应用时又需要将front.php
拷贝过去,万一要改时就显得更不方便。有没有一个好的方法,能够保持依然当前框架的灵活性,但是又要可定制,可以单元测试,同时又没有重复代码吗?依赖注入(DI)就是解决这个问题的好方法。symfony/dependency-injection
就是一个棒的DI
组件,另外一个轻量级Pimple
也是广受好评。
通过依赖注入,不同的服务都变成了可配置的。框架本身也通过容器初始化,初始化时的参数也都是容器,可根据需要传递不同的实现。而disptacher
也是个容器,配置的时候可以根据实际情况在初始化阶段添加尽可能多的listener
。最终,front.php
的代码就变成获取framework
的容器即可,其他的事情则在container.php
配置。当程序变复杂时,将listener
单独独立出来,将配置单独独立出来,都是很简单的事情。基本上可以说,依赖注入是现代框架的标配了。
原文:The DependencyInjection Comonent
END
《Create Your own PHP Framework》笔记的更多相关文章
- HTML+CSS笔记 CSS笔记集合
HTML+CSS笔记 表格,超链接,图片,表单 涉及内容:表格,超链接,图片,表单 HTML+CSS笔记 CSS入门 涉及内容:简介,优势,语法说明,代码注释,CSS样式位置,不同样式优先级,选择器, ...
- CSS笔记--选择器
CSS笔记--选择器 mate的使用 <meta charset="UTF-8"> <title>Document</title> <me ...
- HTML+CSS笔记 CSS中级 一些小技巧
水平居中 行内元素的水平居中 </a></li> <li><a href="#">2</a></li> &l ...
- HTML+CSS笔记 CSS中级 颜色&长度值
颜色值 在网页中的颜色设置是非常重要,有字体颜色(color).背景颜色(background-color).边框颜色(border)等,设置颜色的方法也有很多种: 1.英文命令颜色 语法: p{co ...
- HTML+CSS笔记 CSS中级 缩写入门
盒子模型代码简写 回忆盒模型时外边距(margin).内边距(padding)和边框(border)设置上下左右四个方向的边距是按照顺时针方向设置的:上右下左. 语法: margin:10px 15p ...
- HTML+CSS笔记 CSS进阶再续
CSS的布局模型 清楚了CSS 盒模型的基本概念. 盒模型类型, 我们就可以深入探讨网页布局的基本模型了.布局模型与盒模型一样都是 CSS 最基本. 最核心的概念. 但布局模型是建立在盒模型基础之上, ...
- HTML+CSS笔记 CSS进阶续集
元素分类 在CSS中,html中的标签元素大体被分为三种不同的类型:块状元素.内联元素(又叫行内元素)和内联块状元素. 常用的块状元素有: <div>.<p>.<h1&g ...
- HTML+CSS笔记 CSS进阶
文字排版 字体 我们可以使用css样式为网页中的文字设置字体.字号.颜色等样式属性. 语法: body{font-family:"宋体";} 这里注意不要设置不常用的字体,因为如果 ...
- HTML+CSS笔记 CSS入门续集
继承 CSS的某些样式是具有继承性的,那么什么是继承呢?继承是一种规则,它允许样式不仅应用于某个特定html标签元素,而且应用于其后代(标签). 语法: p{color:red;} <p> ...
- HTML+CSS笔记 CSS入门
简介: </span>年的圣诞节期间,吉多·范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的<span>脚本解释程序</span>,作为ABC语言的一种继承. & ...
随机推荐
- Spring Cloud教程合集
Spring Cloud系列终于搞完啦! 这一系列是笔者的学习笔记,原书之前也给小伙伴们推荐过 <Spring Cloud微服务实战> 原书采用了较老的Brixton版,笔者在学习的过程中 ...
- JAVA8新特性(一)
default拓展方法 java8为接口声明添加非抽象方法的实现,也成为拓展方法. public interface Formula { void doSomething(); default voi ...
- javascript设计模式——迭代器模式
前面的话 迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示.迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也 ...
- 小白的Python之路 day1 用户输入
1 2 3 4 5 6 7 8 #!/usr/bin/env python #_*_coding:utf-8_*_ username =input("username:") p ...
- Python--Pycharm backup_ver1.py 控制台一直Backup FAILED
1.windows不自带zip,需自行安装,http://gnuwin32.sourceforge.net/packages/zip.htm 2.安装后,要配置环境变量:PATH 3.简明Python ...
- 探索版 webstorm快捷方式
ctrl + alt + s 打开配置面板 Settings 国内的资料比较少,大概很多人已经放弃了原生快捷方式,不过我打算通关原生快捷方式. 在配置面板中 IDE S ...
- Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效)
Spring的Bean内部方法调用无法使用AOP切面(CacheAble注解失效) 前言 今天在使用Spring cache的Cacheable注解的过程中遇见了一个Cacheable注解失效的问题, ...
- kafak集群安装-转
前言 最近在利用Spark streaming和Kafka构建一个实时的数据分析系统,对图书阅读数据进行分析,做实时推荐.Spark Streaming 模块是对于 Spark Core 的一个扩展, ...
- Spring3.0官网文档学习笔记(二)
1.3 使用场景 典型的成熟的spring web应用 spring使用第三方框架作为中间层 远程使用场景 EJB包装 1.3.1 依赖管理.命名规则(包) spring-*.jar *号代表 ...
- 【hdu5419】Victor and Toys
求求求 搞搞搞 搞法例如以下:考虑每一个数w[i]w[i]对答案的贡献,呃. . .首先答案一定是 ∑[...](m3) \sum [...]\over {m\choose 3}的形式,仅仅须要搞分子 ...