系列笔记:
Modern PHP 笔记(一):语言特性
Modern PHP 笔记(二):良好实践
Modern PHP 笔记(三):部署测试和调优
现在正式加入世界上最好的语言大家庭了。jimbochen大佬给推荐了 Josh Lockhart 写的,安道翻译的《Modern PHP》. 准备记录下一些学习笔记。刚刚入坑,如果理解的不对,欢迎指出。
这本书刚看,觉得内容很扎实。对PHP的整个生态有了更深的理解。强烈推荐读一下。顺便吐槽下,简书的markdown不支持toc,这个蛮影响体验。
第一章:新时代的PHP
PHP正在重生,得益于命名空间、性状、闭包和内置的操作码缓存等有用的特性
现状
跳出了框架的束缚,依赖composer
管理插件,混搭合适的组件。
历史
- 创始人:拉斯姆斯·勒多夫
- PHP tools:Personal Home Page Tools
- 1998年,PHP3: PHP: Hypertext Preprocessor
- PHP引擎: Zend Engine、HipHop Virtual Machine
- 基于PHP之上的语言:Hack
第二章:特性
命名空间
目的:避免命名冲突
使用命名空间
// 声明命名空间
namespace Oreilly\ModernPHP;
// 厂商命名空间最重要,必须具有全局唯一性
// 导入类和别名
use Symfony\Component\HttpFoundation\Response as Res;
// 导入函数
use func Namespace\functionName;
// 导入常量
use constant Namespamce\CONST_NAME;
全局命名空间
<?php
namespace My\App;
class Foo
{
public function doSomething()
{
$exception = new Exception();
}
}
Exception
类没有命名空间,在全局命名空间中,此时调用\My\App\Foo::doSomething()
会
报错。
正确写法为:new \Exception()
使用接口
接口是两个PHP对象之间的契约,其目的不是让一个对象依赖另一个对象的身份,而是依赖另一个
对象的能力。我们不关心第三方代码如何实现接口,只关心是否实现了指定接口。
// 操作的入参Documentale不是类,是接口
class DocumentStore
{
public function addDocument(Documentable $document)
{
...
$document->getId();
$document->getContent();
}
}
// 接口定义
interface Documentable
{
public function getId();
public function getContent();
}
// 类1实现了接口
class HtmlDocument implements Documentable
{
public function getId()
{
// ...
}
public function getContent()
{
// ...
}
}
// 类2实现了接口
class StreamDocument implements Documentable
{
public function getId()
{
// ...
}
public function getContent()
{
// ...
}
}
性状trait
性状有两个作用:表明类可以做什么(像接口),提供模块化实现(像类)
为什么使用性状
继承模型不好用时,比如想让两个无关的PHP类具有类似的行为?(e.g.零售店类和汽车类都想使用
地理编码转换成经纬度功能时)
- 方案一:继承共同父类。——不好。让两个无关类继承自同一祖先。
- 方案二:创建地理转换接口。——好一点。但是两个类都需要实现相同的代码,不符合DRY原则
- 方案三:使用性状,直接在两个类中混入性状——最好
创建性状
<?php
trait MyTrait {
// 这里写性状实现
}
使用性状
class MyClass
{
use MyTrait;
// 类其他实现
}
PHP会直接把性状代码粘贴在类中,因此要注意兼容问题。
生成器generator
生成器是简单的迭代器。不同的是,不要求类实现Iterator接口,每次只计算下一个值,不会在内存
中预先计算。提升性能。
创建生成器函数
是的,生成器是使用了
yield
关键字的函数。只产出值,不返回值。
返回一个属于
Generator
类的对象,可以用foreach()
迭代。-
生成器每产生一个值内部状态就会停顿,然后恢复,停顿...
function myGenerator() { yield 'value1'; yield 'value2'; }
使用生成器
常用于需要节省内存时,比如生成一个大范围的数、处理很多行的csv
文件等。
foreach (myGenerator() as $yieldValue) {
echo $yieldValue, PHP_EOL;
}
闭包
- 闭包是创建时封闭周围状态的函数。即便闭包所在环境不存在了,闭包中封装的状态依旧存在。
- 匿名函数时没有名称的函数,非常适合作为函数或方法的回调。
PHP中将闭包和匿名函数视为相同概念。
创建闭包
与函数语法相同,但实际上闭包是Closure
类的实例,不是函数。
附加状态
必须调用闭包对象的bindTo()
方法或者使用use
关键字,将状态附加到PHP闭包上。
$que = function ($query)use ($id) {
// $id是外面变量
}
bindTo()
方法将闭包实例的内部状态绑定到其他对象上,则闭包可以访问绑定闭包的对象中受保护方法和私有成员变量。
e.g.,PHP框架经常使用bindTo()
方法把路由URL映射到匿名函数。因此可以在匿
名函数中使用$this
引用应用对象。
Zend OPcache 字节码缓存
PHP是解释型语言,PHP解释器执行PHP脚本时会解析代码,将其编译成 Zend 操作码,然后执行
字节码。每次HTTP请求都会不断解析、编译、执行。缓存字节码可以节省时间,提高性能。
Zend OPcache 默认不启用,需要手动开启并配置。
内置HTTP服务器
从PHP 5.4 开始
启动内置服务器
// 根目录
php -S localhost:4000
自定义配置
php -S localhost:8000 -c app/config/php.ini
路由器脚本
内置的服务器不支持.htaccess
文件,因此很难使用前端控制器。
前端控制器是一个PHP文件,用于转发所有的HTTP请求(通过.htaccess文件或者重写规则实现)。
前端控制器的职责是,分发请求,以及调度适当的PHP代码。
内置服务器使用路由脚本弥补这个功能。处理HTTP请求前,先执行路由器脚本,如果为false,则
返回请求的静态资源URI,否则返回脚本的执行结果。
php -S localhost:8000 router.php
判断使用哪种服务器
if (php_sapi_name() === 'cli-server'){
// PHP 内置的服务器
} else {
// 其他 Web 服务器
}
缺点
不能用在生产环境:
- 一次只能处理一个请求
- 只支持少量媒体类型
- 通过路由器脚本支持少量URL重写规则,更高级不行
更多笔记还在读书中O(∩_∩)O哈哈~
最后,照例记录下一段很喜欢的话共勉。
过一个平凡无趣的人生实在太容易了,你可以不读书,不冒险,不运动,不写作,不外出,不折腾……但是,人生最后悔的事情就是:我本可以。 ——陈素封