2016年6月

PHP通过fsock实现异步HTTP请求

参考文章:

PHP实现异步调用方法研究
使用fscok实现异步调用PHP
how-to-make-async-requests-in-php

function asyncRequest($url, $params = array(), $type = 'GET')
{
    $query = http_build_query($params);

    $parts = parse_url($url);

    $fp = fsockopen(
        $parts['host'],
        isset($parts['port']) ? $parts['port'] : 80,
        $errno,
        $errstr,
        30
    );

    $location = $parts['path'] . (empty($parts['query']) ? '' : "?{$parts['query']}");
    $location .= ($type == 'GET' && !empty($query)) ? (empty($parts['query']) ? '?' : '&').$query : '';

    $out = "{$type} {$location} HTTP/1.1\r\n";
    $out .= "Host: {$parts['host']}\r\n";
    $out .= "Connection: Close\r\n\r\n";
    $out .= $type == 'POST' ? "Content-Type: application/x-www-form-urlencoded\r\n" : '';
    $out .= 'Content-Length: '.strlen($query)."\r\n";
    $out .= ($type == 'POST' && isset($query)) ? $query : '';

    fwrite($fp, $out);
    fclose($fp);
}

注意点:

服务端 PHP 要设置 ignore_user_abortture,Apache 需要在 Apache.conf 中设置对应的 php_value ignore_user_abort,nginx 需要设置 fastcgi_ignore_client_aborton

PHP编码规范内部分享大纲

序言

为什么要有规范

在谷歌,我可以查看任何的代码,进入所有谷歌的代码库,我有权查看它们。事实上,这种权限是很少人能拥有的。但是,让我感到惊讶的却是,如此多的编码规范—缩进,命名,文件结构,注释风格—这一切让我出乎意料的轻松的阅读任意一段代码,并轻易的看懂它们。这让我震惊—因为我以为这些规范是微不足道的东西。它们不可能有这么大的作用—但它们却起到了这么大的作用。当你发现只通过看程序的基本语法结构就能读懂一段代码,这种时间上的节省不能不让人震撼! —— Mark CC.《Google为何要执行严格的代码编写规范》

现在项目规模和团队规模不断壮大,如果不执行规范,只会让整个项目的理解和沟通更加复杂。本指南的意图是为了减少不同开发者在浏览代码时减少认知的差异。为此列举一组如何格式化PHP代码的共用规则。

现有的一些规范

PSR规范

PEAR 编码准则

Symfony 编码准则

PHP-FIG

FIG组织 (Framework Interop Group) 在制定跟PHP相关规范 (PHP Standard Recommendation),简称PSR。按照其官网的说法,这个组织的目的并不是告诉你你应该怎么做,只是一些主流的框架之间相互协商和约定。但是目前PSR已经成了PHP社区主流的代码规范。

在PHP-FIG出现之前,PHP的社区可以说是一盘散沙,各种风格和标准同时存在,熟悉不同项目的代码需要额外的成本。PHP-FIG 的意义对普通开发者来说,好处很多,如果你熟悉一个遵守标准的框架,你学习另一个框架也会快很多,代码也容易读懂,你要开源一个遵守标准的库,别人也容易使用和掌握;对框架团队来说,竞争就更激烈了,因为编码风格上的优势现在大家都没了(以前这真算一个优势),现在只能拼框架的设计、效率、扩展性、可用的类库,等,但这对开发者来说还是个好处。

其实 PHP-FIG 的这些标准,和设计模式的性质是差不多的,都是些最佳实践,通过反复实践沉淀下来的东西会更加稳定一些,固化下来有利于大家的沟通和交流。

现有的PSR规范如下:

PSR-0 Autoloading Standard (Deprecated)

PSR-1 Basic Coding Standard

PSR-2 Coding Style Guide

PSR-3 Logger Interface

PSR-4 Autoloader

PSR-5 PHPDoc (Draft)

PSR-6 Cache Interface

PSR-7 HTTP Message Interface

PSR-8 Hug (April Fool)

PSR-9 Security Reporting Process (Draft)

PSR-10 Security Disclosure Publication (Draft)

PSR-11 Container Interoperability (Draft)

PSR-12 Extended Coding Style Guide (Draft)

1. 概览


该文档基于PSR-12的结构,将PSR-1、PSR-2以及PSR-12进行了总结。

  • 源文件必须只使用 <?php<?= 这两种标签。

  • 源文件中php代码的编码格式必须只使用不带字节顺序标记(BOM)UTF-8

  • 一个源文件建议只用来做声明(类(class)函数(function)常量(constant)等)或者只用来做一些引起副作用的操作(例如:输出信息,修改.ini配置等),但不建议同时做这两件事。

  • 命名空间(namespace)类(class) 必须遵守[PSR-0][]标准。

  • 类名(class name) 必须使用帕斯卡式(PascalCase)写法 (后文将直接用PascalCase表示)。

  • 类(class)中的常量必须只由大写字母和下划线(_)组成。

  • 方法名(method name) 必须使用驼峰式(cameCase)写法(后文将直接用camelCase表示)。

  • 代码必须使用4个空格来进行缩进,而不是用制表符。

  • 一行代码的长度不建议有硬限制;软限制必须为120个字符,建议每行代码80个字符或者更少。

  • 命名空间(namespace)的声明下面必须有一行空行,并且在导入(use)的声明下面也必须有一行空行。

  • 类(class)的左花括号必须放到其声明下面自成一行,右花括号则必须放到类主体下面自成一行。

  • 方法(method)的左花括号必须放到其声明下面自成一行,右花括号则必须放到方法主体的下一行。

  • 所有的属性(property)方法(method) 必须有可见性声明;抽象(abstract)终结(final)声明必须在可见性声明之前;而静态(static)声明必须在可见性声明之后。

  • 在控制结构关键字的后面必须有一个空格;而方法(method)函数(function)的关键字的后面不可有空格。

  • 控制结构的左花括号必须跟其放在同一行,右花括号必须放在该控制结构代码主体的下一行。

  • 控制结构的左括号之后不可有空格,右括号之前也不可有空格。

实例

<?php
declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
use Vendor\Package\Namespace\ClassD as D;

use function Vendor\Package\{functionA, functionB, functionC};
use const Vendor\Package\{ConstantA, ConstantB, ConstantC};

class Foo extends Bar implements FooInterface
{
    public function sampleFunction(int $a, int $b = null): array
    {
        if ($a === $b) {
            bar();
        } elseif ($a > $b) {
            $foo->bar($arg1);
        } else {
            BazClass::bar($arg2, $arg3);
        }
    }

    final public static function bar()
    {
        // method body
    }
}


```

### 2. 通则

----------

### 2.1 基础代码规范

代码`必须`遵守 [PSR-1][] 和 [PSR-2][] 中的所有规则。

#### 2.2 源文件

所有的PHP源文件`必须`使用Unix LF(换行)作为行结束符。

> File > Settings > Editor > Coding Style 设置换行符为LF。

所有PHP源文件`必须`以一个空行结束。

纯PHP代码源文件的关闭标签`?>` `必须`省略。

##### 2.2.1 PHP标签

PHP代码`必须`只使用`长标签(<?php ?>)`或者`短输出式标签(<?= ?>)`;而`不可`使用其他标签。

##### 2.2.2 字符编码

PHP代码的编码格式`必须`只使用不带`字节顺序标记(BOM)`的`UTF-8`。

> File > Settings > Editor > File Encodings 设置IDE Encoding为UTF-8。

##### 2.2.3 副作用

一个源文件`建议`只用来做声明(`类(class)`,`函数(function)`,`常量(constant)`等)或者只用来做一些引起副作用的操作(例如:输出信息,修改`.ini`配置等),但`不建议`同时做这两件事。

短语`副作用(side effects)`的意思是 *在包含文件时* 所执行的逻辑与所声明的`类(class)`,`函数(function)`,`常量(constant)`等没有直接的关系。

`副作用(side effects)`包含但不局限于:产生输出,显式地使用`require`或`include`,连接外部服务,修改ini配置,触发错误或异常,修改全局或者静态变量,读取或修改文件等等

下面是一个既包含声明又有副作用的示例文件;即应避免的例子:

```php
<?php
// 副作用:修改了ini配置
ini_set('error_reporting', E_ALL);

// 副作用:载入了文件
include "file.php";

// 副作用:产生了输出
echo "<html>\n";

// 声明
function foo()
{
    // 函数体
}
```

下面是一个仅包含声明的示例文件;即应提倡的例子:

```php
<?php
// 声明
function foo()
{
    // 函数体
}

// 条件式声明不算做是副作用
if (! function_exists('bar')) {
    function bar()
    {
        // 函数体
    }
}
```

#### 2.3 行

行长度`不可`有硬限制。

行长度的软限制`必须`是120个字符;对于软限制,代码风格检查器`必须`警告但`不可`报错。

一行代码的长度`不建议`超过80个字符;较长的行`建议`拆分成多个不超过80个字符的子行。

在非空行后面`不可`有空格。

空行`可以`用来增强可读性和区分相关代码块。

一行`不可`多于一个语句。

#### 2.4 缩进

代码`必须`使用4个空格,且`不可`使用制表符来作为缩进。

> 注意:代码中只使用空格,且不和制表符混合使用,将会对避免代码差异,补丁,历史和注解中的一些问题有帮助。空格的使用还可以使通过调整细微的缩进来改进行间对齐变得更加的简单。


#### 2.5 关键字和 True/False/Null

PHP类型(`int`, `object`, `float`, `mixed`,
`bool`, `numeric`, `string` 和 `resource`)、常量(`true`, `false`和`null`)和关键字([keywords][])  `必须`使用小写字母。

### 3. `声明(Declare)`,`命名空间(Namespace)`和`导入(Use)`声明

如果存在`declare`语句,那么该语句后`必须`有一行空行,如`declare(ticks=);`。

declare语句`必须`紧跟在PHP开始标签的下一行,在declare语句前`不可`有空行。

一行`不可`有多个declare语句。

当开始标签`<?php`在文件的第一行时, 该行`不可`有其他语句。

`命名空间(namespace)`的声明后面`必须`有一行空行。

`命名空间(namespace)``必须`写在`declare`语句之后。

所有的`导入(use)`声明`必须`放在`命名空间(namespace)`声明的下面。

一句声明中,`必须`只有一个`导入(use)`关键字。

Use statements MUST be in blocks, grouped by varying entity (classes [inc. interfaces and traits],
functions or constants). To elaborate, this means that any and all classes are in a block
together; any and all functions are in a block together; and any and all constants must
be grouped together. Within each block there MUST be no blank lines. If a block has
multiple lines there MUST be a blank line before the first line and a blank line after
the last line.

Classes, functions or constants grouped together into a single line must be listed
alphabetically.

The groups MUST be ordered such that classes (together with interfaces and traits) are first,
followed by functions and then constants.

Example of the above notices about namespace, strict types and use declarations:

```php
<?php
declare(strict_types=1);

namespace Vendor\Package;

use Vendor\Package\{ClassA as A, ClassB, ClassC as C};
use Vendor\Package\Namespace\ClassD as D;
use Vendor\Package\AnotherNamespace\ClassE as E;

use function Vendor\Package\{functionA, functionB, functionC};
use const Vendor\Package\{ConstantA, ConstantB, ConstantC};

class FooBar
{
    // ... additional PHP code ...
}

```

Compound namespaces with a depth of two or more MUST not be used. Therefore the
following is the maximum compounding depth allowed:
```php
<?php

use Vendor\Package\Namespace\{
    SubnamespaceOne\ClassA,
    SubnamespaceOne\ClassB,
    SubnamespaceTwo\ClassY,
    ClassZ,
};
```

When wishing to declare strict types in files containing markup outside PHP
opening and closing tags MUST, on the first line, include an opening php tag,
the strict types declaration and closing tag.

For example:
```php
<?php declare(strict_types=1); ?>
<html>
<body>
    <?php
        // ... additional PHP code ...
    ?>
</body>
</html>

Declare statements MUST contain no spaces and MUST look like declare(strict_types=1);.

Block declare statements are allowed and MUST be formatted as below. Note position of
braces and spacing:

declare(ticks=1) {
    //some code
}

4. 类(class)属性(property)方法(method)

术语“类”指所有的类(class)接口(interface)特性(trait)

类的右花括号后同一行内不可有任何注释或语句。

当实例化一个类时,即使没有任何初始化参数,也必须写上括号。

new Foo();

4.1 扩展(extend)实现(implement)

一个类的扩展(extend)实现(implement)关键词必须类名(class name)在同一行。

类(class)的左花括号必须放在下面自成一行,且其之前或之后不可有空行;右花括号必须放在类(class)主体的后面自成一行,且其之前不可有空行。

```php
get('/hello/{name}', function ($name) use ($app) { return 'Hello '.$app->escape($name); }); ``` When you have a return type declaration present there MUST be one space after the colon with followed by the type declaration. The colon and declaration MUST be on the same line as the argument list closing parentheses with no spaces between the two characters. The declaration keyword (e.g. string) MUST be lowercase. ```php bar($arg1); Foo::bar($arg2, $arg3); ``` 参数列表`可以`被拆分成多个缩进了一次的子行。如果拆分成子行,列表中的第一项`必须`放在下一行,并且每一行`必须`只能有一个参数。 ```php bar( $longArgument, $longerArgument, $muchLongerArgument ); ``` ### 5. 控制结构 下面是对于控制结构代码风格的概括: - 控制结构的关键词之后`必须`有一个空格。 - 控制结构的左括号之后`不可`有空格。 - 控制结构的右括号之前`不可`有空格。 - 控制结构的右括号和左花括号之间`必须`有一个空格。 - 控制结构的代码主体`必须`进行一次缩进。 - 控制结构的右花括号`必须`主体的下一行。 每个控制结构的代码主体`必须`被括在花括号里。这样可是使代码看上去更加标准化,并且加入新代码的时候还可以因此而减少引入错误的可能性。 #### 5.1 `if`,`elseif`,`else` 下面是一个`if`条件控制结构的示例,注意其中括号,空格和花括号的位置。同时注意`else`和`elseif`要和前一个条件控制结构的右花括号在同一行。 ```php $value) { // foreach body } ``` #### 5.6 `try`, `catch`, `finally` 下面是一个`try catch`异常处理控制结构的示例,注意其中括号,空格和花括号的位置。 ```php $b) { $variable = $foo ? 'foo' : 'bar'; } ``` ### 7. 闭包 声明闭包时所用的`function`关键字之后`必须`要有一个空格,而`use`关键字的前后都要有一个空格。 闭包的左花括号`必须`跟其在同一行,而右花括号`必须`在闭包主体的下一行。 闭包的参数列表和变量列表的左括号后面`不可`有空格,右括号的前面也`不可`有空格。 闭包的参数列表和变量列表中逗号前面`不可`有空格,而逗号后面则`必须`有空格。 闭包的参数列表中带默认值的参数`必须`放在参数列表的结尾部分。 下面是一个闭包的示例。注意括号,空格和花括号的位置。 ```php bar( $arg1, function ($arg2) use ($var1) { // body }, $arg3 ); ``` ### 8. 匿名类 Anonymous Classes MUST follow the same guidelines and principles as closures in the above section. ```php Settings > Tools > External Tools ![QQ截图20151226115156.jpg](https://ooo.0o0.ooo/2015/12/25/567e0f1881501.jpg) `--level=psr2 --verbose fix "$FileDir$/$FileName$"` `$ProjectFileDir$` File > Settings > Keymap 设置快捷键 ### 其他文档推荐 [PHP之道](http://www.phptherightway.com/) [PHP设计模式](https://github.com/domnikl/DesignPatternsPHP) [前端开发规范手册](http://zhibimo.com/read/Ashu/front-end-style-guide/) [《SQL编程风格》](http://pan.baidu.com/share/link?shareid=1023640994&uk=3861181332) ### php-cs-fixer的一些fixer含义 * **encoding** [PSR-1] 源文件中php代码的编码格式`必须`只使用不带`字节顺序标记(BOM)`的`UTF-8`。 * **short_tag** [PSR-1] PHP代码`必须`只使用`长标签()或者短输出式标签();而不可`使用其他标签。

  • braces [PSR-2]

类(class)的左花括号必须放到其声明下面自成一行,右花括号则必须放到类主体下面自成一行。

方法(method)的左花括号必须放到其声明下面自成一行,右花括号则必须放到方法主体的下一行。

控制结构的左花括号必须跟其放在同一行,右花括号必须放在该控制结构代码主体的下一行。控制结构的左括号之后不可有空格,右括号之前也不可有空格。

  • class_definition [PSR-2]

在定义类(class)特性(trait)接口(interface)时,关键词左右必须有一个空格。

  • elseif [PSR-2]

推荐elseif来替代else if,以保持所有的条件控制关键字看起来像是一个单词。

  • eof_ending [PSR-2]

所有PHP源文件必须以一个空行结束。

  • function_call_space [PSR-2]

函数(function)的关键字的后面不可有空格。

  • function_declaration [PSR-2]

声明闭包时所用的function关键字之后必须要有一个空格,而use关键字的前后都要有一个空格。

  • indentation [PSR-2]

代码必须使用4个空格,且不可使用制表符来作为缩进。

注意:代码中只使用空格,且不和制表符混合使用,将会对避免代码差异,补丁,历史和注解中的一些问题有帮助。空格的使用还可以使通过调整细微的缩进来改进行间对齐变得更加的简单。

  • line_after_namespace [PSR-2]

命名空间(namespace)的声明后面必须有一行空行。

  • linefeed [PSR-2]

所有的PHP源文件必须使用Unix LF(换行)作为行结束符。

  • lowercase_constants [PSR-2]

PHP常量true, falsenull 必须使用小写字母。

  • lowercase_keywords [PSR-2]

PHP关键字(keywords)必须使用小写字母。

  • method_argument_space [PSR-2]

方法(method)的参数列表中,逗号之前不可有空格,而逗号之后则必须要有一个空格。

  • multiple_use [PSR-2]

一句声明中,必须只有一个导入(use)关键字。

  • parenthesis [PSR-2]

左括号之后不可有空格,右括号之前也不可有空格。

  • php_closing_tag [PSR-2]

纯PHP代码源文件的关闭标签?> 必须省略。

  • single_line_after_imports [PSR-2]

所有的导入(use)声明必须放在命名空间(namespace)声明的下面。

导入(use)声明代码块后面必须有一行空行。

  • switch_case_space [PSR-2]

case和值之间必须只有一个空格。

  • trailing_spaces [PSR-2]

在非空行后面不可有空格。

  • visibility [PSR-2]

所有的属性(property)方法(method)必须声明其可见性。

属性名(property name)方法名(method name) 不推荐用单个下划线作为前缀来表明其保护(protected)私有(private)的可见性。

当用到抽象(abstract)终结(final)来做类声明时,它们必须放在可见性声明的前面。

而当用到静态(static)来做类声明时,则必须放在可见性声明的后面。

PHP新特性内部分享大纲

PHP 5.4

数组短标签

$arr = array(1, 2, 3);

$arr = [1, 2, 3];

函数返回值数组访问解析

function get_arr() {
    return [
        'a' => 1,
        'b' => 2,
    ];
}

echo get_arr()['a'];

在实例化时访问类成员

$query = new Query();
$news = $query->from('news')->all();

$news = (new Query())->from('news')->all();

traits

场景:soft delete等

trait SoftDeleteTrait
{
    public function delete()
    {
        $this->status = Status::DELETED;

        return $this->save();
    }
}

class News extends \yii\db\ActiveRecord
{
    use SoftDeleteTrait;
}

$news = News::findOne($condition);
$news->delete();

内置的http服务器

php -S localhost:8000

实例:https://github.com/laravel/valet

默认字符集改为 UTF-8

http://php.net/manual/en/function.htmlspecialchars.php

PHP 5.5

http://php.net/manual/en/migration55.new-features.php

foreach支持list

在元素顺序固定时使用比较方便。

$news = [
    [
        'title' => 'title1',
        'content' => 'content1',
        'url' => 'url1',
    ],
    [
        'title' => 'title2',
        'content' => 'content2',
        'url' => 'url2',
    ],
];

foreach ($news as $r) {
    echo $r['title'].$r['content'].$r['url'];
}

foreach ($news as list($title, $content, $url)) {
    echo $title.$content.$url;
}

empty支持任意表达式

function get_result() {
    return false;
}

$var = get_result();
var_dump(empty($var));

var_dump(empty(get_result()));

数组和字符串直接取值

echo [1, 2, 3][0];
echo 'string'[0];

通过 ::class 获取类名

echo News::class;

//yii2中的兼容5.4的实现
class Object implements Configurable
{
    public static function className()
    {
        return get_called_class();
    }
}

News::className();

生成器

生成器和迭代器有点类似,但是与标准的PHP迭代器不同,PHP生成器不要求类实现Iterator接口,生成器会根据需求每次计算并产出需要迭代的值,从而减轻了类的开销和负担。这在处理较大的特殊文件、缓存数据等时会有比较明显的优势。

function generatorRange($length) {
    for ($i=0; $i<$length; $i++) {
        yield $i;
    }
}

function iteratorRange($length) {
    $dataSet = [];
    for ($i=0; $i<$length; $i++) {
        $dataSet[] = $i;
    }
    return $dataSet;
}

foreach (generatorRange(10000000) as $i) {
    if ($i % 1000000 == 0) {
        echo $i.'<br>';
    }
}

集成 Zend Opcache

PHP 5.6

使用表达式定义常量

在之前的 PHP 版本中, 必须使用静态值来定义常量,声明属性以及指定函数参数默认值。 现在你可以使用包括数值、字符串字面量以及其他常量在内的数值表达式来 定义常量、声明属性以及设置函数参数默认值。

const ONE = 1;
const TWO = ONE * 2;

class C {
    const THREE = TWO + 1;
    const ONE_THIRD = ONE / self::THREE;
    const SENTENCE = 'The value of THREE is '.self::THREE;

    public function f($a = ONE + self::THREE) {
        return $a;
    }
}

echo (new C)->f().'<br>';
echo C::SENTENCE;

常量支持数组

可以通过 const 关键字来定义类型为 array 的常量。

const ABC = ['a', 'b', 'c'];

... 运算符

参数合并

function f($req, $opt = null, ...$params) {
    // $params 是一个包含了剩余参数的数组
    printf('$req: %d; $opt: %d; number of params: %d'."<br>",
           $req, $opt, count($params));
}

f(1);
f(1, 2);
f(1, 2, 3);
f(1, 2, 3, 4);
f(1, 2, 3, 4, 5);

参数展开

function add($a, $b, $c) {
    return $a + $b + $c;
}

$operators = [2, 3];
echo add(1, ...$operators);

** 运算符

用于幂运算

$a = 2;
$a **= 3;
printf("a = %d", $a);

use 可用于函数和常量

函数和常量可以不必封装在类中就可以支持命名空间。

namespace Name\Space {
    const SORT_ASC = 1;
    const SORT_DESC = -1;
    function phpversion() { return 'custom'; }
}

namespace {
    echo SORT_ASC.' '.SORT_DESC.'<br>';
    echo phpversion().'<br>';

    use const Name\Space\SORT_ASC;
    use const Name\Space\SORT_DESC;
    use function Name\Space\phpversion;

    echo SORT_ASC.' '.SORT_DESC.'<br>';
    echo phpversion().'<br>';
}

大文件上传

现在可以支持大于 2GB 的文件上传。

php://input 是可重用的了

可以多次打开并读取php://input, 数据流支持seek 操作,同时,这个特性使得在处理 POST 的数据的时候, 可以明显降低对于内存的需求量。

PHP 7

PHP 7 最令人激动的就是性能上的提升了,下面是来自鸟哥惠新宸的一些测试图片:

5.6 vs 7.0

benchmark

wordpress qps

?? 运算符

日常使用中存在大量同时使用三元表达式和 isset()的情况而新增的语法糖。

$result = isset($var) ? $var : 'none';

$result = $var ?? 'none';

<=> 比较操作符

组合比较符用于比较两个表达式。当$a小于、等于或大于$b时它分别返回-1、0或1。比较的原则是沿用 PHP 的常规比较规则进行的

$arr = [
    ['name' => 'one', 'age' => 23],
    ['name' => 'two', 'age' => 12],
    ['name' => 'three', 'age' => 55],
];

usort($arr, function($a, $b) {
    return $a['age'] <=> $b['age'];
});

var_dump($arr);

Unicode codepoint 转译语法

这接受一个以16进制形式的 Unicode codepoint,并打印出一个双引号或 heredoc 包围的 UTF-8 编码格式的字符串。 可以接受任何有效的 codepoint,并且开头的 0 是可以省略的。

//emoji微笑
echo "\u{1F603}";

标量类型声明

标量类型声明有两种模式: 强制 (默认) 和 严格模式。 现在可以使用下列类型参数(无论用强制模式还是严格模式): 字符串(string), 整数 (int), 浮点数 (float), 以及布尔值 (bool)。它们扩充了PHP5中引入的其他类型:类名,接口,数组和 回调类型。

declare(strict_types=0);

function sumOfInts(int ...$ints)
{
    return array_sum($ints);
}

var_dump(sumOfInts(2, '3', 4.1));

标量类型声明在目前的阶段对性能是有负面的影响的,但类型提示会对类型推断起到帮助作用, PHP 7.1 中已经加入了"类似"JIT的技术(type specifical opcode handler), 到时候类型声明则可以间接地提高性能。

返回值类型声明

支持函数function、方法method或者匿名函数closure的返回值类型声明。返回值支持的类型有:string、int、float、bool、array、callable、self(仅成员方法)、parent(仅成员方法)、Closure、类名、接口名。

function sum5($a, $b) {
    return $a + $b;
}

function sum7($a, $b): float {
    return $a + $b;
}

var_dump(sum5(1, 2), sum7(1, 2), sum5(1.0, 2));
class News
{
    public static function findNewsYouNeed()
    {
        return null;
    }
}

function getNews(): array {
    $news = News::findNewsYouNeed();
    return $news;
}

foreach ($news as $r) {}

unserialize() 过滤

这个特性旨在提供更安全的方式解包不可靠的数据。它通过白名单的方式来防止潜在的代码注入。

// converts all objects into __PHP_Incomplete_Class object
$data = unserialize($foo, ["allowed_classes" => false]);

// converts all objects into __PHP_Incomplete_Class object except those of MyClass and MyClass2
$data = unserialize($foo, ["allowed_classes" => ["MyClass", "MyClass2"]);

// default behaviour (same as omitting the second argument) that accepts all classes
$data = unserialize($foo, ["allowed_classes" => true]);

常量数组支持 define() 定义

PHP5.6开始支持const定义数组常量,PHP7开始支持define()

<?php
define('ANIMALS', [
    'dog',
    'cat',
    'bird'
]);

echo ANIMALS[1];

匿名类

现在支持通过 new class 来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类定义。

<?php
interface Logger {
    public function log(string $msg);
}

class Application {
    private $logger;

    public function getLogger(): Logger {
         return $this->logger;
    }

    public function setLogger(Logger $logger) {
         $this->logger = $logger;
    }
}

$app = new Application;
$app->setLogger(new class implements Logger {
    public function log(string $msg) {
        echo $msg;
    }
});

var_dump($app->getLogger());

Closure::call()

Closure::call() 可以方便地即时绑定一个闭包到对象上并调用它,而且它的性能更好。

class A {private $x = 1;}

// Pre PHP 7 code
$getXCB = function() {return $this->x;};
$getX = $getXCB->bindTo(new A, A::class); // intermediate closure
echo $getX();

// PHP 7+ code
$getX = function() {return $this->x;};
echo $getX->call(new A);

IntlChar

新增加的 IntlChar 类旨在暴露出更多的 ICU 功能。这个类自身定义了许多静态方法用于操作多字符集的 unicode 字符。

printf('%x', IntlChar::CODEPOINT_MAX);
echo '<br>'.IntlChar::charName('@').'<br>';
var_dump(IntlChar::ispunct('!'));

若要使用此类,需先安装 Intl 扩展。

预期

预期是向后兼用并增强之前的 assert() 的方法。 它使得在生产环境中启用断言为零成本,并且提供当断言失败时抛出特定异常的能力。

老版本的API出于兼容目的将继续被维护,assert() 现在是一个语言结构,它允许第一个参数是一个表达式,而不仅仅是一个待计算的 string 或一个待测试的 boolean

ini_set('assert.exception', 1);

class CustomError extends AssertionError {}

assert(false, new CustomError('Some error message'));

Group use declarations

从同一 namespace 导入的类、函数和常量现在可以通过单个 use 语句 一次性导入了。

// Pre PHP 7 code
use some\namespace\ClassA;
use some\namespace\ClassB;
use some\namespace\ClassC as C;

use function some\namespace\fn_a;
use function some\namespace\fn_b;
use function some\namespace\fn_c;

use const some\namespace\ConstA;
use const some\namespace\ConstB;
use const some\namespace\ConstC;

// PHP 7+ code
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};

生成器支持 return

这个特性是基于 PHP 5.5 版本中引入的生成器的,它使得生成器内部可以使用return语句返回一个最终的表达式(不允许返回引用)。该返回值在生成器产生完所有的值之后可以通过 getReturn() 方法获取到。

$gen = (function() {
    yield 1;
    yield 2;

    return 3;
})();

foreach ($gen as $val) {
    echo $val, PHP_EOL;
}

echo $gen->getReturn(), PHP_EOL;

生成器委托 Generator delegation

现在我们可以直接在构造生成器时通过 yield from 自动将另一个生成器、对象或数组作为自己的成员,而不需要在生成器外部去写冗余代码。

function gen()
{
    yield 1;
    yield 2;

    yield from gen2();
}

function gen2()
{
    yield 3;
    yield 4;
}

foreach (gen() as $val)
{
    echo $val, PHP_EOL;
}

使用 intdiv() 计算整数除法

用于计算整数除法。

function div($a, $b) {
    var_dump(intval($a / $b), intdiv($a, $b));
    echo '<br>';
}

div(3, 2);
div(-3, 2);
div(3, -2);
div(-3, -2);
div(PHP_INT_MAX, PHP_INT_MAX);
div(PHP_INT_MIN, PHP_INT_MIN);
div(PHP_INT_MIN, -1);
div(1, 0);

Session配置项

session_start()
现在可以接收一个包含配置的数组以覆盖写在 php.ini 中的 session 配置项。

session_start([
    'cache_limiter' => 'private',
    'read_and_close' => true,
]);

preg_replace_callback_array()

mixed preg_replace_callback ( mixed $pattern , callable $callback , mixed $subject [, int $limit = -1 [, int &$count ]] )

mixed preg_replace_callback_array ( array $patterns_and_callbacks , mixed $subject [, int $limit = -1 [, int &$count ]] )

PHP7之前使用preg_replace_callback时只能使用一个特定的回调函数来替换匹配到的表达式,这使得有可能需要在回调函数里写分支来实现特定的替换逻辑,现在有了preg_replace_callback_array就可以让每个不同的表达式对应不同的回调函数。

$subject = 'Aaaaaa Bbb';

echo preg_replace_callback_array(
    [
        '~[a]+~i' => function ($match) {
            return str_repeat('1', strlen($match[0]));
        },
        '~[b]+~i' => function ($match) {
            return str_repeat('2', strlen($match[0]));
        }
    ],
    $subject
);

更可靠的随机函数

新增两个CSPRNG函数:random_intrandom_bytes

https://github.com/paragonie/random_compatPHP 5.x也可以使用random_intrandom_bytes

以上函数的随机性不同的取决于环境:

  • 在window上,CryptGenRandom()总是被使用。
  • 在其他平台,arc4random_buf()如果可用会被使用(在BSD系列或者具有libbsd的系统上成立)
  • 以上都不成立的话,一个linux系统调用getrandom(2)会被使用
  • 如果还不行,/dev/urandom 会被作为最后一个可使用的工具
  • 如果以上都不行,系统会抛出错误
$times = 600000; 
$csprng = 0; 
$rand = 0;

for ($i = 0; $i < $times; $i++){ 
    $csprng += roll() === 6 ? 1 : 0;
    $rand += roll(false) === 6 ? 1 : 0;
} 

function roll($csprng = true){ 
    return $csprng ? random_int(1, 6) : rand(1, 6); 
}

var_dump($csprng, $rand); 

关键词在特定的场景中也可以使用

class Test 
{
    public static function forEach($param) 
    {
        var_dump($param);
    }

    public static function function($param) 
    {
        static::forEach($param);
    }
}

Test::function([1,2,3]);

附录

PHP7 一些不向后兼容的变更

PHP 5.x在版本更新中不向后兼容的变更大多数都是历史比较久远的特性了,现在的代码里尤其是在使用框架的情况下基本见不到,PHP 7的改动比较大,但影响到的部分一般也并不常用,很多优先级的改动其实只要实际编码中注意多使用 (){},原本的代码基本就可以无痛迁移至 PHP 7

PHP7 Compatibility Checker

composer global require sstalle/php7cc

php7cc --help

php7cc /path/to/my/directory/

参考资料

风雪之隅

http://www.php7.site/

现代 PHP 新特性系列文章

记一则被骗经历

下班走在路上,因为急着去吃点东西所以走的很快,突然被一对中年夫妇叫住,说要去嘉定但是身上没钱了,希望能借点钱。

我第一反映就是这两人是骗子,想快点走,但他们缠着我而且说的很诚恳,说了些明天十点前必定会还给我,走的累了身体又不舒服等等,还用手机给我打了电话,留了联系方式。

结果我就给了他们两百块钱,事后仔细一看他的电话是171开头的虚拟号,打过去关机,想来必然是骗子了,好在只是两百块钱,不过之后想起来还是觉得自己好傻。