Modern PHP

首先推荐这本书,不厚,沉下心来半天能看完。前半部分才是介绍的新特性,后半部分讲的都是测试、部署的工具和流程。没有编码技巧,只是介绍了 PHP 最新的现代化的工程开发、测试、部署应该是什么样。其实这也是最重要的,PHP 需要一种优雅、有效的方式来规范化工程的开发。

这本书基于 5.6 的,毕竟那个时候 PHP7 没有发布,但是读者应该以现在的眼光和视角来阅读这本书,毕竟技术一直在向前发展,从作者整理到出书甚至得加上翻译再出版,这个时间已经很长了。

从另外一个角度来看,互联网才是获取知识的最快途径,并且,英文很重要。

本人在做笔记整理的时候,会加入 PHP7 (截止目前 7.1) 的一些特性和一些书上没介绍的,但本人接触比较多的特性,会在后面注明:

语言新特性

命名空间 (5.3)

这是现代 PHP 组件生态系统的基础。

官方文档

命名空间好比操作系统中的目录,两个同名的文件可以共存在不同的目录下。同理两个同名的 PHP 类可以在不同的 PHP 命名空间下共存,就这么简单。

你的代码很可能和其他开发者的代码使用相同的类名、接口名、函数名或常量名,如果不使用命名空间,名称会起冲突,导致 PHP 执行出错。而使用命名空间,把代码放在唯一的厂商命名空间中的话,你的代码和其他开发者的代码可以使用相同的名称命名类、接口、函数和常量。

有些代码可能没有命名空间,这些代码就存在于全局命名空间中,在命名空间中引用全局命名空间代码的时候,要在前面加上 \,比如你在自己的命名空间中调用 PHP 原生的 Exception 类。

后期静态绑定 (补 5.3)

<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        self::who();
    }
}
class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}
B::test();
//输出:A
<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // 后期静态绑定从这里开始
    }
}
class B extends A {
    public static function who() {
        echo __CLASS__;
    }
}
B::test();
//输出:B

“后期绑定” 的意思是说,static:: 不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为 “静态绑定”,因为它可以用于(但不限于)静态方法的调用。

使用接口

接口并不是特性,而是面向对象中一个很重要的思想。

接口是两个 PHP 对象间的契约,其目的不是为了让一个对象依赖另一个对象的身份,而是依赖于另一个对象的能力。

使用性状 (5.4)

性状表明了这个 trait 可以做什么,又提供了一种模块话的实现,既像类又像接口。然而其实两者都不是,性状是部分类的实现 (包含常量、属性、方法),可以混入到一个或多个 PHP 类中。

官方文档

你想下,手机和汽车都用定位功能,他们两者没有什么关联,继承同一个父类显然不合适,但是他们如何复用定位有关的功能呢?现在我们项目中是通过封装一个 service 来实现复用的。

在 Laravel 里面可以看到很多 trait 使用场景,典型的就是 Auth 认证服务。

class LoginController extends Controller
{
    use AuthenticatesUsers;
  	....
}
trait AuthenticatesUsers
{
  ....
}

Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。

生成器 (5.5)

生成器是只能向前进行的迭代器,这意味着不能使用生成器在数据集中执行后退、快进、查找操作,只能让生成器计算并生产下一个值。

<?php
  function makeRange($length)
{
  for($i = 0;$i < $length;$i++){
    yield $i;
  }  
}
foreach(makeRange(10000000) as $i){
  echo $i.PHP_EOL;
}

每当产生一个数组元素,就通过 yield 关键字返回成一个,并且函数执行暂停,当返回的迭代器的 next 方法被调用的时候,会恢复刚才函数的执行,从上一次被 yield 暂停的位置开始继续执行,到下一次遇到 yield 的时候,再次返回。foreach 会自动调用 next 方法。

还有一个更高级的用法,见鸟哥博客

在 PHP7 中,当生成器迭代完成后,可以获取该生成器函数的返回值 (如果有 return 操作的话)。通过 Generator::getReturn() 得到。

闭包 | 匿名函数 (5.3)

PHP 中的闭包很有意思,它可以引用外部变量,并且可能保存其状态,直到使用的时候。

最常用的闭包在 array_map 这个函数中。

<?php
  $result = array_map(function($value){
    return $value + 1;
  },[1,2,3]);
  print_r($result);
  //[2,3,4];

或者直接创建一个闭包。关于实现,涉及到_invoke() 方法,可以看官方文档

<?php
  $closure = function($blogName){
  return 'Welcome to '.$blogName;
}
echo $closure('chengxiaobai');
//Welcome to chengxiaobai

附加一个外部状态到闭包内。

<?php
  function hello($blogName){
    return function($userName) use ($blogName){
      return sprintf('Hello %s,welcome to %s',$userName,$blogName);
    }
  }
//附加 chengxiaobai 到闭包内
$helloString = hello('chengxiaobai');
//正常调用闭包
echo $helloString('Tom');
//Hello Tom,welcome to chengxiaobai;

//下面也是相同的效果
$blogName = 'chengxiaobai';
$helloString = function($userName) use ($blogName){
      return sprintf('Hello %s,welcome to %s',$userName,$blogName);
    };
echo $helloString('Tom');
//Hello Tom,welcome to chengxiaobai;

还有一个很有意思的 bindTo 方法,laravel 的路由闭包就用了这个方法,具体可以去看看 laravel。

可变长函数参数 (补 5.6)

<?php
function sum(...$numbers) {
    $acc = 0;
    foreach ($numbers as $n) {
        $acc += $n;
    }
    return $acc;
}
echo sum(1, 2, 3, 4);
//输出 10

社区 (PHP-FIG) 规范 (PSR)

官方最新文档看这里

中文翻译文档在这里,已经落后了,仅供参考吧

PHP-FIG 的目的是实现组件的互操作性。这个互操作性是指通过接口、自动加载机制和标准的风格,让组件互相合作。

PSR 是 PHP Standards Recommendation 的简称,中文就是 PHP 推荐标准。每一个规范都是用于解决大多数 PHP 框架经常用会遇到的某个具体问题。

正是大家都遵循于此规范,才让大家现在如此轻松的使用第三方组件,减少很多重复造轮子的工作,也让整个社区的编码质量得到了提升,最典型的就是 Symfony。

序号 描述 备注
1 Basic Coding Standard 编码基础规范
2 Coding Style Guide 编码风格规范
3 Logger Interface 日志接口规范
4 Autoloading Standard 自动加载规范
6 Caching Interface 缓存接口规范
7 HTTP Message Interface HTTP 消息接口规范
13 Hypermedia Links 超媒体链接规范
16 Simple Cache 轻量缓存接口规范

本来有个 PSR-0,也是自动加载规范,但是已经被 PSR-4 替代了,但是现在部分框架依然使用了该规范,比如鸟哥的 Yaf 框架。

组件

在组件概念没有流行前,要解决问题,大都选择某个全栈框架去开发应用解决问题。大家会花大量时间去学习某个框架的封闭生态系统,使用这个框架提供的工具,很难集成第三方库或者自定义库,因为这些库没有使用相同的接口。但是现在,这样的日子一去不复返了,现在不必拘泥于框架筑起的围墙中了。

组件是什么?组件就是打包好的代码,用于解决某一个具体的问题。比如 Guzzle,Monolog。

一般选择一个合适的组件可以从几个方面来选择:

  • 作用单一,能很好的解决一个问题,术业有专攻,并不需要一个万金油。
  • 精炼
  • 适配性,能很好的结合现有代码,并且与其他组件间能很好的适配。尽量不选有侵入性的组件,万一改了你的默认时区呢?
  • 最好有测试用例,能很好的介绍如何使用该组件,也能提高测试覆盖度。
  • 文档完善。应该有良好的文档说明,方便大家理解和使用。
  • 社区活跃度。

Packagist 你能找到很多组件,这是一个专门收集组件的地方。当然你得使用神器 Composer,PEAR 现在也还能用,但向 Composer 靠拢吧。

组件和框架的选择。用正确的工具做正确的事情即可。组件和框架的区别好像专科医生和医院的区别。大多数现代框架也是构建在小型的 PHP 组件之上的一系列约定。

当然,团队协作最好还是使用框架,并不是说框架提供了多丰富的功能,而是为了一种规范和约定。