# 介绍

> 小虾米（QQ:509129）

针对 PHP 7.0.x 版本，PHP 7.1.x 版本的新特性做了详细的介绍与解读.部分做了相关中文译文

## 查看官方中文文档： <a href="#cha-kan-wen-dang" id="cha-kan-wen-dang"></a>

[PHP 最新官方中文文档](https://secure.php.net/manual/zh/)


# PHP 7 安装

## PHP 7 安装

### [CentOS](https://www.centos.org/) 或 [RHEL](https://www.redhat.com/en/technologies/linux-platforms/enterprise-linux) 推荐使用 Yum 安装

* PHP 5.6 版本: [PHP 5.6 on CentOS/RHEL 7.2 and 6.8 via Yum](https://webtatic.com/packages/php56/)
* PHP 7.0 版本:[PHP 7.0 on CentOS/RHEL 6.8 and 7.3 via Yum](https://webtatic.com/packages/php70/)
* PHP 7.1 版本: [PHP 7.1 on CentOS/RHEL 6.8 and 7.3 via Yum](https://webtatic.com/packages/php71/)

> 注意:5.6 7.0 7.1的源其实是一个

### [Ubuntu](http://www.ubuntu.org.cn/) 推荐使用apt -get 安装

> php7.0不支持Ubuntu 12.04版本及以下版本
>
> 注意:5.6 7.0 7.1的源其实是一个
>
> <https://launchpad.net/~ondrej/+archive/ubuntu/php-qa>

```
$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:ondrej/php
$ sudo add-apt-repository ppa:ondrej/php-qa
$ sudo apt-get update

# 安装php5.6
$ sudo apt-get install -y php5.6

# 安装php7.0
$sudo apt-get install -y php7.0

# 安装php7.1
$sudo apt-get install -y php7.1
```

#### 资料

* [How to Install PHP 5.6 or PHP 7.1 on Ubuntu 16.04, 14.04 or 12.04 using PPA](https://tecadmin.net/install-php5-on-ubuntu/#)
* [install-php-7-on-ubuntu](http://tecadmin.net/install-php-7-on-ubuntu/)
* [使用PPA在Ubuntu上安装php 5.4\~php 5.6 , php7](http://www.cnblogs.com/toughlife/p/5479325.html)
* [How to Install and Configure PHP 7.0 or PHP 7.1 on Ubuntu 16.04](https://www.vultr.com/docs/how-to-install-and-configure-php-70-or-php-71-on-ubuntu-16-04)

## PHP 各版本切换

### Ubuntu

假设您使用了以上方式在Ubuntu环境下安装了

* php5.6
* php7.0
* php7.1

> 以下例子只做了php5.6 与php7.0之前的切换, php7.1之间的切换雷同

```
从 php5.6 切换到 php7.0 :

# Apache:
$ sudo a2dismod php5.6 ; sudo a2enmod php7.0 ; sudo service apache2 restart

# CLI:
sudo update-alternatives --set php /usr/bin/php7.0
sudo update-alternatives --set phpize /usr/bin/phpize7.0
sudo update-alternatives --set php-config /usr/bin/php-config7.0

﻿从 php7.0 切换到 php5.6 :

# Apache:
$ sudo a2dismod php7.0 ; sudo a2enmod php5.6 ; sudo service apache2 restart

# CLI:
sudo update-alternatives --set php /usr/bin/php5.6
sudo update-alternatives --set phpize /usr/bin/phpize5.6
sudo update-alternatives --set php-config /usr/bin/php-config5.6

﻿从 php7.0 切换到 php7.1 :

# Apache:
sudo a2dismod php7.0 ; sudo a2enmod php7.1; sudo service apache2 restart

# CLI:
sudo update-alternatives --set php /usr/bin/php7.1
sudo update-alternatives --set phpize /usr/bin/phpize7.1
sudo update-alternatives --set php-config /usr/bin/php-config7.1
```


# PHP 7.0.x 新特性


# OOP 特性


# 类型声明

## 简介：

在PHP7之前，我们在函数和类之间传递参数时不必声明变量类型，返回数据时也不必声明变量类型。任何数据类型都可以被传递、返回。这样会给PHP带来一个很大的问题：**PHP不清楚传递的是什么类型的变量，函数、方法接收到的变量也不知道是什么类型。为了解决这个问题，PHP7中引入了类型声明，目前明确的有两类变量可以声明类型：形参和返回值。**

> 类型声明在OOP与PHP程序中属于同一个特性，因为它既可以用在程序的函数中，也可以用在对象的方法中。

## 形参类型声明：

PHP7支持的形参类型声明的类型有：字符串型(string), 整型 (int), 浮点类型 (float), 以及布尔类型 (bool)。

```php
class Person
{
    public function age(int $age)
    {
        return $age;
    }

    public function name(string $name)
    {
        return $name;
    }

    public function isAlive(bool $alive)
    {
        return $alive;
    }
}


$person = new Person();

echo $person->name('Revin');
echo $person->age(27);
echo $person->isAlive(TRUE);
```

上面的代码，创建一个 Person 类，里面有三个方法，每个方法接受不同类型的形参且有着类型声明。如果执行上面的代码，能够正确运行并通过类型检测。

Age 支持浮点数型，例如 `27.5` 如果传递一个浮点数作为age方法的形参，也是正常运行的。代码如下：

```php
echo $person->age(30.5);
```

**默认请看下，形参类型声明不是完全限制的**，这就意味着我们可以传递一个浮点给期望得到整型数的方法, 也同样可以传递一个整形给期望得到整形术的方法。

```php
//echo $person->name(11111111111);
echo $person->name('Revin');
echo $person->age(27.5);
echo $person->isAlive(TRUE);
```

这样也就失去了指定声明形参类型,不过可以做一些限制，代码如下：

> 开启严格模式. 注意必须放在程序首行,否则会报`Fatal error: strict_types declaration must be the very first statement in the script in ...`

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

此时我们若再传递一个浮点数给age方法的话，会得到一个 **Fatal error: Uncaught TypeError，**&#x8FD9;个Fatal 错误 的意思是 `Person::age` 只能接受一个整型数而非浮点型数。

> 注意：爱需要字符串形参的情况下，如果传入的不是字符串形参的话，也会出现以上类似的Fatal 错误。例如以下报错代码：
>
> ```php
> echo $person->isAlive('true');
> ```

## 返回类型声明：

**PHP 7 的另一个重要特性就是支持返回类型的声明，无论实在函数还是对象的方法中。**

这有点类似形参类型声明，对刚才的 Person 类进行修改，代码如下：

```php
declare(strict_types = 1);

class Person
{
    public function age(float $age) : float
    {
        return $age;
    }

    public function name(string $name) : string
    {
        return $name;
    }

    public function isAlive(bool $alive) : string
    {
        return ($alive) ? 'Yes' : 'No';
    }
}

$person = new Person();

echo $person->name('revin');
echo $person->age(27.5);
echo $person->isAlive(TRUE);
```

上面的代码 返回类型声明使用了 data-type 语法，对于形参类型声明与返回类型声明。

### 举个返回类型是对象的栗子：

修改上面的代码，加Address类， Person 类中加入 getAddress 方法，并且返回类型是Address对象。

```php
declare(strict_types = 1);

class Address
{
    public function getAddress()
    {
        return ['street' => 'shanghai', 'country' => 'China'];
    }
}

class Person
{
    public function age(float $age) : float
    {
        return $age;
    }

    public function name(string $name) : string
    {
        return $name;
    }

    public function isAlive(bool $alive) : string
    {
        return ($alive) ? 'Yes' : 'No';
    }

    public function getAddress() : Address
    {
        return new Address();
    }
}

$person = new Person();

var_dump($person->getAddress());
```

上面的代码执行没有问题，调用`Persion` 类中的 getAddress 方法执行完毕后，返回一个Address类型的数据。

若上面`Persion` 类中的getAddress 方法修改如下

```php
    public function getAddress() : Address
    {
        return ['street' => 'shanghai', 'country' => 'China'];
    }
```

再次实行会Fatal 错误，这是因为`Persion` 类中的 getAddress 方法返回了一个数组，而不是方法声明的Address类型的返回值。

```
Fatal error: Uncaught TypeError: Return value of Person::getAddress() must be an instance of Address, array returned in ..
```

## 为什么要使用类型声明？

它可以让函数、方法的形参与返回值有所预期，避免出现不必要的数据传递，从而造成错误。PHP 7 这个特性使代码更清晰且可读性更强，能够清楚的知道怎么样的数据类型将会被传递与返回。

## 补充知识: 三个点语法是什么?

当你看php7 官方的相关文档时m会看到`...` 的东西,如下示例:

```php
<?php
// Coercive mode
function sumOfInts(int ...$ints)
{
    return array_sum($ints);
}

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

其实这是PHP5.6 新特性-PHP 可变参数,详情官方文档:<http://php.net/manual/zh/functions.arguments.php#functions.variable-arg-list> , 把传进来的值，转成数组.

## 资料：

[PHP 7 标量类型声明 RFC](http://blog.jobbole.com/91735/)


# 命名空间与use关键词批量声明

## 目录：

* 测试数据
* 基础的写法(复杂)
* 可读性高的写法（简明许多）
* php7 的写法（更加清晰）
  * 非混合模式的use声明
  * 混合模式的use声明
  * 复合模式的use声明

## **测试数据**

> 注意：测试目录及其命名没有按照标准规范定义目录，定义文件名及命名空间，只为展示php新特征而临时数据。
>
> 注意：加载类的方式有两种：⑴通过类似include等显示要加载类。⑵通过 \_\_autoload 函数来加载所有的类文件，这里只为测试所以使用了第一种方式

以下libs目录文件中定义了类、函数、常量，均在Includes\Code`e命名空间下。`

```
libs目录
libs/mysql.php
libs/mysqli.php
libs/pdo.php
libs/file.php
libs/constants.php
libs/functions.php
index.php
```

* **mysql.php**

```php
<?php
namespace Includes\Code;

class Mysql
{
    public function get() : string
    {
        return get_class();
    }
}
```

* **mysqli.php**

```php
<?php
namespace Includes\Code;

class Mysqli
{
    public function get() : string
    {
        return get_class();
    }
}
```

* **pdo.php**

```php
<?php
namespace Includes\Code;

class Pdo
{
    public function get() : string
    {
        return get_class();
    }
}
```

* **file.php**

```php
<?php
namespace Includes\Code;

class File
{
    public function get() : string
    {
        return get_class();
    }
}
```

* **constants.php**

```php
<?php
namespace Includes\Code;

const DB_TYPE   = 'mysql';
const DB_HOST   = 'localhost';
const DB_NAME   = 'test';
const DB_USER   = 'root';
const DB_PWD    = '123456';
const DB_PORT   = '3306';
const DB_PREFIX = '';
```

* **functions.php**

```php
<?php
namespace Includes\Code;

function getSql() : string
{
    return 'Its sql';
}

function saveSql(string $sql) : string
{
    return $sql.' saved!';
}
```

## 基础的写法(复杂)：

* &#x20;**index.php**

```php
<?php
include 'libs/mysql.php';
include 'libs/mysqli.php';
include 'libs/pdo.php';
include 'libs/file.php';
include 'libs/constants.php';
include 'libs/functions.php';

$mysql  = new \Includes\Code\Mysql();
$mysqli = new \Includes\Code\Mysqli();
$pdo    = new \Includes\Code\Pdo();
$file   = new \Includes\Code\File();

echo \Includes\Code\getSql() . '<br>';
echo \Includes\Code\saveSql('select * from abc') . '<br>';
echo \Includes\Code\DB_USER . '<br>';
echo \Includes\Code\DB_PWD . '<br>';
```

## 可读性高的写法（简明许多）：

> 注意:PHP 5.6.0发布 use关键字可导入函数与常量

```php
<?php
include 'libs/mysql.php';
include 'libs/mysqli.php';
include 'libs/pdo.php';
include 'libs/file.php';
include 'libs/constants.php';
include 'libs/functions.php';

use Includes\Code\Mysql;
use Includes\Code\Mysqli;
use Includes\Code\Pdo;
use Includes\Code\File;
use function Includes\Code\saveSql;
use function Includes\Code\getSql;
use const Includes\Code\DB_USER;
use const Includes\Code\DB_PWD;

$mysql  = new Mysql();
$mysqli = new Mysqli();
$pdo    = new File();
$file   = new File();

echo getSql() . '<br>';
echo saveSql('select * from abc') . '<br>';

echo DB_USER . '<br>';
echo DB_PWD . '<br>';
```

在这段代码中，我通过命名空间中的PHP声明来显示引入很多的类、函数、常量。这种方法依然需要很多行复杂的代码才能表明我们希望用到的类、函数、常量等，这导致在文件中需要些很多的use声明，显得繁琐。

**为了解决这个问题，PHP7引入了批量的use声明，下面列举三种use声明的模式。**

## **php7 的写法：**

* 非混合模式的use声明
* 混合模式的use声明
* 复合模式的use声明

### 非混合模式的use声明

假设命名空间里多种类型的资源，例如类、函数、常量等，使用费混合模式的use声明，可以按照类型将它们归类后逐个用use声明。代码如下

> 这里只修改了use代码，其他的不变

```php
use Includes\Code\{ Mysql, Mysqli, Pdo, File };
use function Includes\Code\{ getSql, saveSql };
use const Includes\Code\{ DB_USER, DB_PWD };
```

### 混合模式的use声明

在这种声明方式中，将同一个命名空间下的内容合并在一起，使用一次use关键字完成全部声明，代码如下

```php
use Includes\Code\{ 
    Mysql,
    Mysqli,
    Pdo,
    File,
    function getSql,
    function saveSql,
    const DB_USER,
    const DB_PWD
};
```

### **复合模式的use声明**

举例：有一个Mysql类位于Includes\Code\Cache命名空间下，有一个File类位于Includes\Code\Index命名空间下，还有两个位于Includes\Code\Data命名空间下，那么此时，若需要use声明，代码如下

```php
use Includes\Code\{ 
    Cache\Mysql,
    Index\File,
    Data\Mysqli,
    Data\Pdo
};
```

这样的声明看上去更清晰，不必写太多的命名空间信息。


# 匿名类

## 目录：

* 基本语法
* 匿名类的继承
  * 匿名类继承普通类
  * 匿名类继承接口
* 匿名嵌套在一个类中

## 基本语法：

匿名类的声明与使用是同时进行的，它具备其他类所具备的所有功能，差别在与匿名类没有类名。匿名类的一次性小任务代码流程对性能提升帮助很大，你不必将整个类都写完再使用它。

> 虽然我们看到的匿名类是没有命名的，但在PHP内部，会在内存的引用地址表中为其分配一个全局唯一的名称。例如全局的一个匿名类的名称为 class\@0x5e4f2d5s132

匿名类的语法与命名类的语法相似，仅仅是没有设置类名，语法如下：

```php
new class(argument) { defintion };
```

通俗易懂的例子：

```php
<?php
$name = new class {
    public function __construct()
    {
        echo 'hello world';
    }
};
```

结果仅显示一行 `hello world`

参数可以直接设置在匿名类中当做构造函数的参数，如下代码：

```php
<?php
$name = new class('hello world') {
    public function __construct(string $name)
    {
        echo $name;
    }
};
```

## 匿名类的继承：

* 匿名类继承普通类
* 匿名类继承接口

### 匿名类继承普通类

匿名类在继承方面与命名类相同，一样可以继承父类及父类的方法，如下代码：

```php
<?php
//构造函数的参数。

class User
{
    protected $number;

    public function __construct()
    {
        echo "I am parent class constructor";
    }

    public function getNumber() : float
    {
        return $this->num;
    }
}

//匿名类继承Info类

$number = new class(5) extends User
{
    public function __construct(float $num)
    {
        parent::__construct();
        $this->num = $num;
    }
};

echo $number->getNumber();
```

### 输出结果：

```
I am parent class constructor5
```

> ### 通过使用匿名类继承了父类User。同时，父类的public，protected，private属性在匿名类中依然有效。

### 匿名类继承接口

匿名类同样可以继承接口，方式与继承普通命名类相同。

```php
<?php
interface Info
{
    public function __construct(string $name, string $address);
    public function getName() : string;
    public function getAddress() : string;
}

// 修改后的User类
class User
{
    protected $number;

    protected $name;

    protected $address;

    public function __construct()
    {
        echo "I am parent class constructor";
    }

    public function getNumber() : float
    {
        return $this->num;
    }
}

    $info = new class('revin', 'China') extends User implements Info
    {
        public function __construct(string $name, string $address)
        {
            $this->name    = $name;
            $this->address = $address;
        }

        public function getName() : string
        {
            return $this->name;
        }

        public function getAddress() : string
        {
            return $this->address;
        }
    };

echo $info->getName() . ' ' . $info->getAddress();
```

输出结果：

```
revin China
```

## 匿名嵌套在一个类中

匿名类可以嵌套在一个类中使用。

```php
class Math
{
    public $first_number = 10;

    public $second_number = 20;


    protected function add() : float
    {
        return $this->first_number + $this->second_number;
    }

    public function multiply_sum()
    {
        return new class() extends Math
        {

            public function multiply($third_number) : float
            {
                return $this->add() * $third_number;
            }
        };
    }
}

$math = new Math();

echo $math->multiply_sum()->multiply(2);
```

输出结果

```
60
```

实现过程：

1. Math类中有一个multiply\_sum方法，这个方法会返回一个匿名类
2. 该匿名类继承于Math类，包含一个multiply方法
3. 当使用echo输出内容时，步骤如下
4. 首先调用$math->multiply\_sum()生成一个由匿名类创建的对象
5. 接着执行->multiply(2)，因为这个对象会调用匿名类的multiply方法并传递参数2

> 上面的代码中，Match类可以被外部类调用，匿名类可以被内部调用。
>
> 需要注意：内部类不需要也不推荐调用外部类，本例子这是展示证明内部类可以继承外部类的方式来调用外部类中呗声明为protected的方法。

补充:上面的列子针对匿名内部类调用外部类的栗子不明显,虽然不被推荐这么写,但是为了防止上面的栗子蒙,看一下例子.

```php
<?php

class MathTest
{
    public $first_number = 20;

    public $second_number = 30;


    protected function add() : float
    {
        return $this->first_number + $this->second_number;
    }
}

class Math
{
    public $first_number = 10;

    public $second_number = 20;


    protected function add() : float
    {
        return $this->first_number + $this->second_number;
    }

    public function multiply_sum()
    {
        return new class() extends MathTest
        {

            public function multiply($third_number) : float
            {
                return $this->add() * $third_number;
            }
        };
    }
}

$math = new Math();

echo $math->multiply_sum()->multiply(2);
```


# Throwable 接口

## 目录：

* 简介
* Throwable 层次结构
* Error 对象

## 简介：

PHP7提供了一种全局的接口，使得所有的类都可以基于此使用throw关键字。在PHP中，异常与错误经常会遇到。**在PHP之前，异常可以被捕获，但是错误是不能被捕获的。从PHP7开始，任何完整程序或一部分程序中的Fatal错误都可以被捕获。**

> 为了更好的捕获诸多错误（大多数的 Fatal 错误），PHP7 提供了 throwable 接口，异常与错误都继承于这个接口。
>
> 注意：wom自己写的PHP类是不能直接继承throwable 接口的，如果希望继承throwable 接口，需要继承某个异常类。

PHP 7 改变了大多数错误的报告方式。不同于传统（PHP 5）的错误报告机制，现在大多数错误被作为 **Error** 异常抛出。

## Throwable层次结构：

* Throwable
  * Error
    * ArithmeticError
    * DivisionByZeroError
    * AssertionError
    * ParseError
    * TypeError
  * Exception
    * ...

## Error 对象：

现在大多数的Fatal错误情况会抛出一个Error实例，类似于异常捕获，Error实例可以被try/catch捕获，如下代码：

```php
<?php
function iHaveError($object)
{
    return $object->iDontExist();
}

iHaveError(null);

echo "I am still running";
```

以上代码会产生一个Fatal 的错误，程序也停止运行，并且最后一行的echo语句不会被执行。

现在加入try/catch 后，代码如下：

```php
<?php
function iHaveError($object)
{
    return $object->iDontExist();
}

try {
    iHaveError(null);
} catch(Error $e) {
    echo $e->getMessage();
}

echo "<br>I am still running";
```

再次执行以上代码，catch中的内容将会执行。并且代码会继续执行，最后的一行echo也会被输出。结果如下:

```
Call to a member function iDontExist() on null
I am still running
```

## DivisionByZeroError 对象(Error的子实例)

大多数情况下，Error实例会在大部分 Fatal 错误的情况下被抛出，但是对于一些错误情况，只有Error的子实例（见Error层次图）会被抛出,当然使用 Error 对象也是可以的。

如下代码：

```php
<?php
try {
    $a = 20;
    $division = $a % 0;
} catch(DivisionByZeroError $e) { // catch(Error $e) {
    echo $e->getMessage();
}
```

在PHP7之前，上面的代码会触发一个warning级别的错误，如今在PHP7中，执行上面的代码会抛出一个可以被捕获的DivisionByZeroError异常.如下:

```
Modulo by zero
```

补充:**除以零的变化**

```php
<?php
var_dump(3/0);
var_dump(0/0);
var_dump(0%0);
?>
```

Output of the above example in PHP 5:

```
Warning: Division by zero in %s on line %d
bool(false)

Warning: Division by zero in %s on line %d
bool(false)

Warning: Division by zero in %s on line %d
bool(false)
```

Output of the above example in PHP 7:

```
Warning: Division by zero in %s on line %d
float(INF)

Warning: Division by zero in %s on line %d
float(NAN)

PHP Fatal error:  Uncaught DivisionByZeroError: Modulo by zero in %s line %d
```

## ParseError 对象(Error的子实例)

> [eval函数](http://php.net/manual/zh/function.eval.php) — 把字符串作为PHP代码执行

当使用eval函数进行对字符串进行解析时,发生 Fatal 错误,如下

```
Parse error: syntax error, unexpected end of file in ....
```

如今在PHP7中，执行下面的代码会抛出一个可以被捕获的ParseError异常.如下:

```php
<?php
try {
    $command = 20;
    $b = eval($command);
} catch (ParseError $e) {
    echo $e->getMessage();
}
```

捕获到的异常如下:

```
syntax error, unexpected end of file
```

## 资料：

[PHP 7 错误处理](http://php.net/manual/zh/language.errors.php7.php)


# 新增操作符


# 太空飞船操作符（<=>）

## 目录：

* 简介
* 整型比较
* 字符串比较
* 数组比较
* 资料
* usort 场景示例

## 简介：

太空飞船操作符在比较变量时非常有用，这里说的变量包括标量类型（字符串型、整型、浮点型等）、，数组、对象。这个操作符相当于把三个比较符（`== 、 < 、 >`）融合成一个。

比如说使用场景可以用于 usort 、uasort 、uksort 的回调函数。具体使用规则如下：

* 当符号两边相等时返回 0
* 当符号右边大于符号左边时返回 -1
* 当符号左边大于符号右边时返回 1

## 整型比较：

例子：

```php
<?php
$intA = 1;
$intB = 2;
$intC = 1;

echo '整型比较:<br>';

echo $intA <=> $intC; // 返回 0
echo '<br>';

echo $intA <=> $intB; // 返回 -1
echo '<br>';

echo $intB <=> $intC; // 返回 1
```

执行结果：

```
整型比较:
0
-1
1
```

## 字符串比较：

例子：

```php
<?php
echo '字符串比较:<br>';

echo 'PHP 7' <=> 'PHP 7'; //返回 0
echo '<br>';

echo 'a' <=> 'b'; // 返回 -1
echo '<br>';

echo 'z' <=> 'x'; // 返回 1
```

执行结果：

```
字符串比较:
0
-1
1
```

## 数组比较：

```php
<?php
echo '数组比较:<br>';
echo [1,2,3] <=> [1,2,3]; // 0
echo "<br>";
echo [1,2,3] <=> [3,2,1]; // -1
echo "<br>";
echo [3,2,1] <=> [1,2,3]; // 1
```

执行结果：

```
数组比较:
0
-1
1
```

## 对象比较:

```php
<?php
// Objects
$a = (object) ["a" => "b"]; 
$b = (object) ["a" => "b"]; 
echo $a <=> $b; // 0
$a = (object) ["a" => "b"]; 
$b = (object) ["a" => "c"]; 
echo $a <=> $b; // -1
$a = (object) ["a" => "c"]; 
$b = (object) ["a" => "b"]; 
echo $a <=> $b; // 1
$a = (object) ["a" => "b"]; 
$b = (object) ["b" => "b"]; 
echo $a <=> $b; // 0
```

## usort 场景示例：

```php
<?php
// 老的 if 方式比较
function normal_sort($a, $b) : int
{
    if($a == $b) {
        return 0;
    } elseif ($a < $b) {
        return -1;
    } else {
        return 1;
    }
}

// 新的太空飞船操作符比较
function space_sort($a, $b) : int
{
    return $a <=> $b;
}

$normalArray = [1,34,56,67,98,45];

usort($normalArray, 'normal_sort');

foreach($normalArray as $k => $v) {
    echo $k.' => '.$v.'<br>';
}

//SpaceShip example
$sarray = [1,34,56,67,98,45];

usort($sarray, 'space_sort');

foreach($sarray as $key => $value) {
    echo $key.' => '.$value.'<br>';
}
```

以上代码if条件判断比较的方式，采用太空操作符一行搞定！输出结果:

```php
0 => 1
1 => 34
2 => 45
3 => 56
4 => 67
5 => 98
0 => 1
1 => 34
2 => 45
3 => 56
4 => 67
5 => 98
```

## 资料：

[php官方wiki 太空飞船操作符示例](https://wiki.php.net/rfc/combined-comparison-operator)


# null 合并运算符（??）

三元运算符经常用到，三元运算符只需一行代码就可以实现`if-else`的功能。如：

```php
$post = ($_POST['title']) ? $_POST['title'] : NULL;
```

以上代码理想状态是对的，但是当`$_POST、$_POST['title']`不存在，或者为null时，PHP就会抛出 `Notice: Undefined index: title in ...` 错误。为了解决和这个问题，一般这样写

```php
$post = isset($_POST['title']) ? $_POST['title'] : NULL;
```

这样写解决了报错的问题，但是重复书写了代码。PHP7中合并运算符，在第一个操作数存在时，可以直接返回，不然则返回第二个操作数，代码如下：

```php
$post = $_POST['title'] ?? NULL;
```

合并运算符检查`$_POST['title']，`是否存在如果存在则返回`$_POST['title']`，否则返回NULL

**合并运算符的另一个的好处是可以连续使用**，代码如下：

```php
$title = $_POST['title'] ?? $_GET['title'] ?? 'No POST or GET';
```

上面的代码执行时会先检查第一个操作符是否存在，若存在则直接返回，若不存在则检查第二个操作数。此时第二个合并操作符开始生效，它会检查第二个操作数是否存在，若存在则返回，若不存在则返回右边的值。

如果用老的代码为：

```php
if (isset($_POST['title'])) {
    $title = $_POST['title'];
} elseif (isset($_GET['title'])) {
    $title = $_GET['title'];
} else {
    $title = 'No POST or GET';
}
```


# 统一变量语法

我们有可能会遇到这种情况：方法、变量、类名等会被保存在某个变量里，例如如下代码：

```php
$objects['class']->name
```

`$objects['class']`先会被解析，之后name属性被解析，由左到右。

新的情况，代码如下：

```php
$first = ['name'] => 'revin'];
$revin = 'xiaoxiami';

echo $$first['name'];
```

**PHP5.X版本中**，代码会正常运行，并且输出`xiaoxiami`。解析的过程不是按照如第一个例子那样从左到右解析的原则，在PHP7中，会产生一个Notice级别的错误。为了避免解析混淆，PHP7 引入了统一变量语法。

```php
echo ${$first['name']};
```

再举一个栗子：

```php
class Info
{
    public $title = 'PHP 7';
    public $description = 'PHP 7 descriptions';

    public function geTitle() : string
    {
        return $this->title;
    }

    public function getDescription() : string
    {
        return $this->description;
    }
}

$methods = ['title' => 'geTitle', 'description' => 'getDescription'];
$object = new Info();

echo 'Info ' . $object->$methods['title']() . ' description : ' . $object->$methods['description']();
```

PHP5.X版本中，代码会正常运行，但是在PHP7中最后一行会先解析`$object->$methods`,之后才会尝试解析`['title']`等，这样并不是我们想要达到的解析预期。所以想要在PHP7中成功运行，则最后echo输出行修改为：

```php
echo 'Info ' . $object->{$methods['title']}() . ' description : ' . $object->{$methods['description']}();
```

## 总结:

**PHP7 中 对变量、属性和方法的间接调用现在将严格遵循从左到右的顺序来解析，而不是之前的混杂着几个特殊案例的情况**。 下面这张表说明了这个解析顺序的变化。

### 间接调用的表达式的新旧解析顺序

| **表达式**               | **PHP 5 的解析方式**         | **PHP 7 的解析方式**         |
| --------------------- | ----------------------- | ----------------------- |
| `$$foo['bar']['baz']` | `${$foo['bar']['baz']}` | `($$foo)['bar']['baz']` |
| `$foo->$bar['baz']`   | `$foo->{$bar['baz']}`   | `($foo->$bar)['baz']`   |
| `$foo->$bar['baz']()` | `$foo->{$bar['baz']}()` | `($foo->$bar)['baz']()` |
| `Foo::$bar['baz']()`  | `Foo::{$bar['baz']}()`  | `(Foo::$bar)['baz']()`  |

使用了旧的从右到左的解析顺序的代码必须被重写，明确的使用圆括号来表明顺序（参见上表）。 这样使得代码既保持了与PHP 7.x的前向兼容性，又保持了与PHP 5.x的后向兼容性。


# 其他特性和变更


# 常量数组

从PHP5.6开始，常量数组可以使用const关键词来声明，代码如下：

```php
const ANIMALS = ['dog', 'cat', 'bird'];
```

与普通数组做一个对比,代码如下:

```php
const ANIMALS = ['aa' => 'dog', 'cat', 'bird'];
echo ANIMALS[0] . '<br>';
echo ANIMALS['aa'] . '<br>';

$amimals =  ['aa' => 'dog', 'cat', 'bird'];
echo $amimals[0] . '<br>';
echo $amimals['aa'] . '<br>';
```

输出结果:

```
cat
dog
cat
dog
```

在PHP7中常量数组也可以通过define函数来初始化。代码如下：

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

echo ANIMALS[1]; // 输出 "cat"
```

补充

```php
define('ANIMALS', [
    'aa' => 'dog',
    'cat',
    'bird'
]);

echo ANIMALS[1]; // 输出 "bird"
echo ANIMALS['aa']; // 输出 "dog"
```


# Switch 中的多个default默认值

在PHP7之前的版本中，Switch可以有多个default默认值。代码如下：

```php
<?php
$a = 10;
$b = 0;

switch(true)
{
    default:
        $b += 1;
        break;
    default:
        $b += 2;
}

echo 'b is' . $b;
```

但是从PHP7开始，这样写会产生 Fatal 级别错误，错误内容如下：

```
Fatal error: Switch statements may only contain one default clause in ...
```


# Session\_start 函数中的参数数组

在PHP7之前，当我们要使用session时，必须先调用`session_start()`函数。这个函数并不能参数传递。所有的关于session相关配置都在php.ini中进行设置。从PHP7开始，可以在调用函数时传递参数选项数组。这些设置选项将覆盖php.ini 中的session配置。

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

上面代码设置[session.cache\_limiter](http://php.net/manual/zh/session.configuration.php#ini.session.cache-limiter)为*private*，并且在读取完毕会话数据之后马上关闭会话存储文件。实参部分传递的选项数组将优先于php.ini中的session配置。


# Unserialize 函数引入过滤器

通常我们使用 serialize 和 unserialize 两个方法分别对对象进行序列化和反序列化。然而 unserialize并不安全，因为它没有任何过滤项，可以反序列化任何对象。PHP7中unserialize函数引入了过滤器，这个特性旨在提供更安全的方式解包不可靠的数据。它通过白名单的方式来防止潜在的代码注入。默认情况下运行反序列化所有类型的对象。使用代码示例如下：

```php
<?php
$foo = new stdClass();
$foo->name = 'revin';
$foo = serialize($foo);

// 将所有的对象都转换为 __PHP_Incomplete_Class 对象
$data = unserialize($foo, ["allowed_classes" => false]);
echo $data->name;  // 空
//var_dump($data);

// 将除 MyClass 和 MyClass2 之外的所有对象都转换为 __PHP_Incomplete_Class 对象
$data = unserialize($foo, ["allowed_classes" => ["MyClass", "MyClass2", "stdClass"]]);
//var_dump($data);
echo $data->name; //输出 "revin"

// 默认情况下所有的类都是可接受的，等同于省略第二个参数
$data = unserialize($foo, ["allowed_classes" => true]);
//var_dump($data);
echo $data->name; //输出 "revin"
```


# 整数除法函数 intdiv()

新加的函数[intdiv()](http://php.net/manual/zh/function.intdiv.php)用来进行 整数的除法运算。

```php
var_dump(intdiv(10, 3));
```

输出结果：

```
int(3)
```


# 补充\*其他特性和变更

## 目录:

* Unicode codepoint 转译语法
* Closure::call()
* IntlChar
* 预期
* 生成器可以返回表达式
* Generator delegation
* preg\_replace\_callback\_array()
* CSPRNG Functions
* 可以使用list()函数来展开实现了ArrayAccess接口的对象
* 允许在克隆表达式上访问对象成员
* 可以使用关键词作为方法名（链式操作）

## Unicode codepoint 转译语法

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

```php
echo "\u{aa}";
echo "\u{0000aa}";
echo "\u{9999}";
```

以上例程会输出：

```
ª
ª (same as before but with optional leading 0's)
香
```

## **Closure::call()**

**Closure::call()**&#x73B0;在有着更好的性能，简短干练的暂时绑定一个方法到对象上闭包并调用它。

```php
<?php
class A {private $x = 1;}

// PHP 7 之前版本的代码
$getXCB = function() {return $this->x;};
$getX = $getXCB->bindTo(new A, 'A'); // 中间层闭包
echo $getX();

// PHP 7+ 及更高版本的代码
$getX = function() {return $this->x;};
echo $getX->call(new A);
```

以上例程会输出：

```
1
1
```

## IntlChar

新增加的[IntlChar](http://php.net/manual/zh/class.intlchar.php)类旨在暴露出更多的 ICU 功能。这个类自身定义了许多静态方法用于操作多字符集的 unicode 字符。

```php
<?php

printf('%x', IntlChar::CODEPOINT_MAX);
echo IntlChar::charName('@');
var_dump(IntlChar::ispunct('!'));
```

以上例程会输出：

```
10ffff
COMMERCIAL AT
bool(true)
```

若要使用此类，请先安装[Intl](http://php.net/manual/zh/book.intl.php)扩展

## 预期

[预期](http://php.net/manual/zh/function.assert.php#function.assert.expectations)是向后兼用并增强之前的[assert()](http://php.net/manual/zh/function.assert.php)的方法。 它使得在生产环境中启用断言为零成本，并且提供当断言失败时抛出特定异常的能力。

老版本的API出于兼容目的将继续被维护，[assert()](http://php.net/manual/zh/function.assert.php)现在是一个语言结构，它允许第一个参数是一个表达式，而不仅仅是一个待计算的[string](http://php.net/manual/zh/language.types.string.php)或一个待测试的[boolean](http://php.net/manual/zh/language.types.boolean.php)。

```php
<?php
ini_set('assert.exception', 1);

class CustomError extends AssertionError {}

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

以上例程会输出：

```
Fatal error: Uncaught CustomError: Some error message
```

关于这个特性的完整说明，包括如何在开发和生产环境中配置它，可以在[assert()](http://php.net/manual/zh/function.assert.php)的[expectations section](http://php.net/manual/zh/function.assert.php#function.assert.expectations)章节找到。

## 生成器可以返回表达式

此特性基于 PHP 5.5 版本中引入的生成器特性构建的。 它允许在生成器函数中通过使用*return*语法来返回一个表达式 （但是不允许返回引用值）， 可以通过调用*Generator::getReturn()*&#x65B9;法来获取生成器的返回值， 但是这个方法只能在生成器完成产生工作以后调用一次。

```php
<?php

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

    return 3;
})();

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

echo $gen->getReturn(), PHP_EOL;
```

以上例程会输出：

```
1
2
3
```

在生成器中能够返回最终的值是一个非常便利的特性， 因为它使得调用生成器的客户端代码可以直接得到生成器（或者其他协同计算）的返回值， 相对于之前版本中客户端代码必须先检查生成器是否产生了最终的值然后再进行响应处理 来得方便多了。

## Generator delegation

现在，只需在最外层生成其中使用 [*yield from*](http://php.net/manual/zh/language.generators.syntax.php#control-structures.yield.from)， 就可以把一个生成器自动委派给其他的生成器，**Traversable** 对象或者 [array](http://php.net/manual/zh/language.types.array.php) 。

```php
<?php

function gen()
{
    yield 1;
    yield 2;

    yield from gen2();
}

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

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

?>
```

以上例程会输出：

```
1
2
3
4
```

## preg\_replace\_callback\_array()

在 PHP 7 之前，当使用 [preg\_replace\_callback()](http://php.net/manual/zh/function.preg-replace-callback.php)

函数的时候， 由于针对每个正则表达式都要执行回调函数，可能导致过多的分支代码。 而使用新加的 [preg\_replace\_callback\_array()](http://php.net/manual/zh/function.preg-replace-callback-array.php)函数， 可以使得代码更加简洁。

现在，可以使用一个关联数组来对每个正则表达式注册回调函数， 正则表达式本身作为关联数组的键， 而对应的回调函数就是关联数组的值。

## CSPRNG Functions

新加入两个跨平台的函数：[random\_bytes()](http://php.net/manual/zh/function.random-bytes.php)和[random\_int()](http://php.net/manual/zh/function.random-int.php)用来产生高安全级别的随机字符串和随机整数。

## 可以使用 list() 函数来展开实现了**ArrayAccess**接口的对象

在之前版本中，[list()](http://php.net/manual/zh/function.list.php)函数不能保证 正确的展开实现了**ArrayAccess**接口的对象， 现在这个问题已经被修复。

## 允许在克隆表达式上访问对象成员

```php
允许在克隆表达式上访问对象成员，例如： (clone $foo)->bar()。
```

## **可以使用关键词作为方法名**

详见：<http://php.net/manual/zh/migration70.other-changes.php>

放宽保留字限制,场景为链式操作时．

全局保留的单词作为属性、常量和方法名称在类、接口和特性中被允许。当引入新的关键字并避免对api的命名限制时，这会减少BC的表面。

这在创建具有连贯接口的内部DSLs时特别有用:

```php
<?php
// 'new', 'private', and 'for' were previously unusable
Project::new('Project Name')->private()->for('purpose here')->with('username here');
?>
```

唯一的限制是类关键字仍然不能用作常量名称，否则它将与类名解析语法(ClassName:class)相冲突。


# 补充\*新函数

参考: [New functions ](http://php.net/manual/zh/migration70.new-functions.php)

## 新函数

### [Closure](http://php.net/manual/zh/class.closure.php)

* **Closure::call()**

### [CSPRNG](http://php.net/manual/zh/book.csprng.php)

* [random\_bytes()](http://php.net/manual/zh/function.random-bytes.php)
* [random\_int()](http://php.net/manual/zh/function.random-int.php)

### [Error Handling and Logging](http://php.net/manual/zh/book.errorfunc.php)

* [error\_clear\_last()](http://php.net/manual/zh/function.error-clear-last.php)

### [Generator](http://php.net/manual/zh/class.generator.php)

* **Generator::getReturn()**

### [GNU Multiple Precision](http://php.net/manual/zh/book.gmp.php)

* [gmp\_random\_seed()](http://php.net/manual/zh/function.gmp-random-seed.php)

### [Math](http://php.net/manual/zh/book.math.php)

* [intdiv()](http://php.net/manual/zh/function.intdiv.php) - 整除运算

### [PCRE](http://php.net/manual/zh/book.pcre.php)

* [preg\_replace\_callback\_array()](http://php.net/manual/zh/function.preg-replace-callback-array.php)

### [PHP Options/Info](http://php.net/manual/zh/book.info.php)

* [gc\_mem\_caches()](http://php.net/manual/zh/function.gc-mem-caches.php)
* [get\_resources()](http://php.net/manual/zh/function.get-resources.php)

### [POSIX](http://php.net/manual/zh/book.posix.php)

* [posix\_setrlimit()](http://php.net/manual/zh/function.posix-setrlimit.php)

### [Reflection](http://php.net/manual/zh/book.reflection.php)

* [ReflectionParameter::getType()](http://php.net/manual/zh/reflectionparameter.gettype.php)
* [ReflectionParameter::hasType()](http://php.net/manual/zh/reflectionparameter.hastype.php)
* [ReflectionFunctionAbstract::getReturnType()](http://php.net/manual/zh/reflectionfunctionabstract.getreturntype.php)
* [ReflectionFunctionAbstract::hasReturnType()](http://php.net/manual/zh/reflectionfunctionabstract.hasreturntype.php)

### [Zip](http://php.net/manual/zh/book.zip.php) - 此扩展可以让你透明地读写ZIP压缩文档以及它们里面的文件。

* [ZipArchive::setCompressionIndex()](http://php.net/manual/zh/ziparchive.setcompressionindex.php)
* [ZipArchive::setCompressionName()](http://php.net/manual/zh/ziparchive.setcompressionname.php)

### [Zlib Compression](http://php.net/manual/zh/book.zlib.php)

* [inflate\_add()](http://php.net/manual/zh/function.inflate-add.php)
* [deflate\_add()](http://php.net/manual/zh/function.deflate-add.php)
* [inflate\_init()](http://php.net/manual/zh/function.inflate-init.php)
* [deflate\_init()](http://php.net/manual/zh/function.deflate-init.php)


# 补充\*新的全局常量

> 参考:[New Global Constants](http://php.net/manual/zh/migration70.constants.php)

## 新的全局常量

### [Core Predefined Constants](http://php.net/manual/zh/reserved.constants.php)

* `PHP_INT_MIN`

### [GD](http://php.net/manual/zh/book.image.php)

* `IMG_WEBP`

  (as of PHP 7.0.10)

### [JSON](http://php.net/manual/zh/book.json.php)

* `JSON_ERROR_INVALID_PROPERTY_NAME`
* `JSON_ERROR_UTF16`

### [LibXML](http://php.net/manual/zh/book.libxml.php)

* `LIBXML_BIGLINES`

### [PCRE](http://php.net/manual/zh/book.pcre.php)

* `PREG_JIT_STACKLIMIT_ERROR`

### [POSIX](http://php.net/manual/zh/book.posix.php)

* `POSIX_RLIMIT_AS`
* `POSIX_RLIMIT_CORE`
* `POSIX_RLIMIT_CPU`
* `POSIX_RLIMIT_DATA`
* `POSIX_RLIMIT_FSIZE`
* `POSIX_RLIMIT_LOCKS`
* `POSIX_RLIMIT_MEMLOCK`
* `POSIX_RLIMIT_MSGQUEUE`
* `POSIX_RLIMIT_NICE`
* `POSIX_RLIMIT_NOFILE`
* `POSIX_RLIMIT_NPROC`
* `POSIX_RLIMIT_RSS`
* `POSIX_RLIMIT_RTPRIO`
* `POSIX_RLIMIT_RTTIME`
* `POSIX_RLIMIT_SIGPENDING`
* `POSIX_RLIMIT_STACK`
* `POSIX_RLIMIT_INFINITY`

### [Zlib](http://php.net/manual/zh/book.zlib.php)

* `ZLIB_ENCODING_RAW`
* `ZLIB_ENCODING_DEFLATE`
* `ZLIB_ENCODING_GZIP`
* `ZLIB_FILTERED`
* `ZLIB_HUFFMAN_ONLY`
* `ZLIB_FIXED`
* `ZLIB_RLE`
* `ZLIB_DEFAULT_STRATEGY`
* `ZLIB_BLOCK`
* `ZLIB_FINISH`
* `ZLIB_FULL_FLUSH`
* `ZLIB_NO_FLUSH`
* `ZLIB_PARTIAL_FLUSH`
* `ZLIB_SYNC_FLUSH`


# 补充\*变更的函数

> 参考: [变更的函数](http://php.net/manual/zh/migration70.changed-functions.php)

## 变更的函数

### PHP 核心

* [debug\_zval\_dump()](http://php.net/manual/zh/function.debug-zval-dump.php)
* 现在打印 "int" 替代 "long", 打印 "float" 替代 "double"
* [dirname()](http://php.net/manual/zh/function.dirname.php) 增加了可选的第二个参数,`depth`, 获取当前目录向上`depth`级父目录的名称。
* [getrusage()](http://php.net/manual/zh/function.getrusage.php) 现在支持 Windows.
* [mktime()](http://php.net/manual/zh/function.mktime.php) and [gmmktime()](http://php.net/manual/zh/function.gmmktime.php) 函数不再接受`is_dst`参数。
* [preg\_replace()](http://php.net/manual/zh/function.preg-replace.php) 函数不再支持 "\e" ( `PREG_REPLACE_EVAL`). 应当使用 [preg\_replace\_callback()](http://php.net/manual/zh/function.preg-replace-callback.php) 替代。
* [setlocale()](http://php.net/manual/zh/function.setlocale.php) 函数不再接受 `category`传入字符串。 应当使用`LC_*`常量。
* [exec()](http://php.net/manual/zh/function.exec.php), [system()](http://php.net/manual/zh/function.system.php) and [passthru()](http://php.net/manual/zh/function.passthru.php) 函数对 NULL 增加了保护.
* [shmop\_open()](http://php.net/manual/zh/function.shmop-open.php) 现在返回一个资源而非一个int， 这个资源可以传给[shmop\_size()](http://php.net/manual/zh/function.shmop-size.php),[shmop\_write()](http://php.net/manual/zh/function.shmop-write.php),[shmop\_read()](http://php.net/manual/zh/function.shmop-read.php),[shmop\_close()](http://php.net/manual/zh/function.shmop-close.php)

  和[shmop\_delete()](http://php.net/manual/zh/function.shmop-delete.php)
* [substr()](http://php.net/manual/zh/function.substr.php) 现在当 start 的值与 string 的长度相同时将返回一个空字符串。
* 为了避免内存泄露，[xml\_set\_object()](http://php.net/manual/zh/function.xml-set-object.php) 现在在执行结束时需要手动清除 $parse。


# 补充\*摒弃一些老式的写法

> 参考:[Deprecated features in PHP 7.0.x](http://php.net/manual/zh/migration70.deprecated.php)

## 摒弃一些老式的写法

### 目录:

* 摒弃老式构造函数的写法
* 摒弃静态调用非静态方法
* 摒弃password\_hash() salt 的选项写法
* 摒弃 capture\_session\_meta SSL上下文选项写法
* LDAP的用法 - 摒弃使用 [ldap\_sort()](http://php.net/manual/zh/function.ldap-sort.php) 函数(被废弃)

### 摒弃老式构造函数的写法

从php4开始，构造函数的便可以通过命名保持与类名一致的方式来声明自己是构造函数。这种方式一直被沿用到php5.6。但是在PHP7中，不推荐,官方已经说会在未来删除。

```php
<?php
class Info
{
    public function info()
    {
        echo "I am just a normal class method";
    }
}

$info = new Info();
$info->info();
```

使用\_\_construct方法

```php
<?php
class Info
{

    public function __construct()
    {
        echo "I am default constructor";
    }
}

$info = new Info();
$info->info();
```

### 摒弃静态调用非静态方法

静态调用的方法不声明的静态是过时的，或许未来会删除。

```php
<?php
class foo {
    function bar() {
        echo 'I am not static!';
    }
}

foo::bar();
?>
```

以上例程会输出：

```
Deprecated: Non-static method foo::bar() should not be called statically in - on line 8
I am not static!
```

### 摒弃password\_hash() salt 的选项写法

password\_hash()函数的salt选项已经被弃用，以防止开发人员生成他们自己(通常不安全的) salt。当开发人员不提供salt 时，该函数本身生成一个加密安全的salt，因此不应该需要定制的salt 生成。

### 摒弃 capture\_session\_meta SSL上下文选项写法

capture\_session\_meta SSL上下文选项已经被弃用。现在，通过stream\_get\_meta\_data()函数可以使用SSL元数据。

### LDAP的用法 - 摒弃使用 [ldap\_sort()](http://php.net/manual/zh/function.ldap-sort.php) 函数(被废弃)

下面的函数已经被废弃：

* [ldap\_sort()](http://php.net/manual/zh/function.ldap-sort.php)


# 补充\*不向后兼容的变更

## 目录:

* E\_STRICT 警告级别变更
* 关于list()处理方式的变更
  * 常用的方式
  * list() 不再以反向的顺序来进行赋值
  * 空的list()赋值支持已经被移除
  * list() string 不再能解开
* 当引用分配时自动创建元素的数组排序已更改
* *global*只接受简单变量
* 非**Traversable**对象的遍历(本文无座介绍,详见[链接](http://php.net/manual/zh/migration70.incompatible.php) , 搜索"非**Traversable**对象的遍历")
* 函数参数附近的括号不再影响行为
* foreach的变化
  * foreach不再改变内部数组指针
  * foreach通过值遍历时，操作的值为数组的副本
  * foreach通过引用遍历时，有更好的迭代特性
* string处理上的调整
  * 十六进制字符串不再被认为是数字
  * *\u{*&#x53EF;能引起错误
* 被移除的函数（Removed functions）
  * [call\_user\_method()](https://www.gitbook.com/book/xiaoxiami/php-7/edit#) 和 [call\_user\_method\_array()](https://www.gitbook.com/book/xiaoxiami/php-7/edit#)
  * 所有的 ereg\* 函数
  * [mcrypt](https://www.gitbook.com/book/xiaoxiami/php-7/edit#) 别名
  * 所有 ext/mysql 函数
  * [intl](https://www.gitbook.com/book/xiaoxiami/php-7/edit#) 别名
  * set\_magic\_quotes\_runtime()
  * set\_socket\_blocking()
  * dl() in PHP-FPM
  * GD Type1 functions
* 被移除掉的 INI 配置指令
  * 被移除的功能
* 其他不向后兼容的变更
  * new 操作符创建的对象不能以引用方式赋值给变量
  * 无效的类、接口以及 trait 命名
  * 移除了 ASP 和 script PHP 标签
  * 从不匹配的上下文发起调用
  * yield 变更为右联接运算符
  * 函数定义不可以包含多个同名参数
  * Switch 语句不可以包含多个 default 块
  * 在函数中检视参数值会返回当前的值
  * $HTTP\_RAW\_POST\_DATA被移除
  * INI 文件中#注释格式被移除
  * JSON 扩展已经被 JSOND 取代
  * 在数值溢出的时候，内部函数将会失败
  * 自定义会话处理器的返回值修复
  * 相等的元素在排序时的顺序问题

## E\_STRICT 警告级别变更

原有的`E_STRICT`警告都被迁移到其他级别。`E_STRICT`常量会被保留，所以调用*error\_reporting(E\_ALL|E\_STRICT)*&#x4E0D;会引发错误.

举例(以静态方式调用实例方法):

```php
<?php
error_reporting(E_ALL|E_STRICT);
class mycls{

  function func()
  {
  echo "none static";
  }

}

mycls::func();
```

php7 中则会印发新的`E_DEPRECATED 级别的错误`

```
eprecated: Non-static method mycls::func() should not be called statically in xxxx
```

| **场景**                  | **新的级别/行为**    |
| ----------------------- | -------------- |
| 将资源类型的变量用作键来进行索引        | `E_NOTICE`     |
| 抽象静态方法                  | 不再警告，会引发错误     |
| 重复定义构造器函数               | 不再警告，会引发错误     |
| 在继承的时候，方法签名不匹配          | `E_WARNING`    |
| 在两个 trait 中包含相同的（兼容的）属性 | 不再警告，会引发错误     |
| 以非静态调用的方式访问静态属性         | `E_NOTICE`     |
| 变量应该以引用的方式赋值            | `E_NOTICE`     |
| 变量应该以引用的方式传递（到函数参数中）    | `E_NOTICE`     |
| 以静态方式调用实例方法             | `E_DEPRECATED` |

## 关于list()处理方式的变更

详见官方: [关于list()处理方式的变更 ](http://php.net/manual/zh/migration70.incompatible.php)

### 常用的方式:

```php
<?php
$str = "China|Chinese";

list($country, $language) = explode('|', $str);

echo 'country:' . $country;
echo 'language:' . $language;
```

### list() 不再以反向的顺序来进行赋值.

官方说明:

PHP7 现在会按照变量定义的顺序来给他们进行赋值，而非反过来的顺序。 通常来说，这只会影响list() 与数组的\[]操作符一起使用的案例，如下所示：

```php
<?php
list($a[], $a[], $a[]) = [1, 2, 3];
var_dump($a);
?>
```

Output of the above example in PHP 5:

```
array(3) {
  [0]=>
  int(3)
  [1]=>
  int(2)
  [2]=>
  int(1)
}
```

Output of the above example in PHP 7:

```
array(3) {
  [0]=>
  int(1)
  [1]=>
  int(2)
  [2]=>
  int(3)
}
```

总之，我们推荐不要依赖[list()](http://php.net/manual/zh/function.list.php)的赋值顺序，因为这是一个在未来也许会变更的实现细节。

### 空的list () 赋值支持已经被移除

[list()](http://php.net/manual/zh/function.list.php)结构在PHP 7 现在不再能是空的。如下的例子不再被允许：

```php
<?php
list() = $a;
list(,,) = $a;
list($x, list(), $y) = $a;
?>
```

### **list() string 不再能解开**

如下代码,在php 5.x 当中顺利运行,并输出相应的 a b 值 ,但是在php7 中不会输出 如下:

```php
<?php
$str = 'ab';
list($a, $b) = $str;
echo $a;
echo $b;
```

list() 不再能解开字符串（string）变量。 你可以使用str\_split()来代替它,如下代码:

> str\_split() 函数把字符串分割到数组中。 第二个可选参数支持规定每个数组元素的长度。默认是 1。

```php
<?php
$str = 'ab';
list($a, $b) = str_split($str);
echo $a;
echo $b;
```

## 当引用分配时自动创建元素的数组排序已更改

数组中的元素的顺序已更改，这些元素在引用引用赋值时自动创建。例如:

```php
<?php
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>
```

Output of the above example in PHP 5:

```
array(2) {
  ["b"]=>
  &int(1)
  ["a"]=>
  &int(1)
}
```

Output of the above example in PHP 7:

```
array(2) {
  ["a"]=>
  &int(1)
  ["b"]=>
  &int(1)
}
```

## *global*只接受简单变量

可变变量不再能够与 global 关键字一起使用。 如果在PHP7 中需要的话可以使用圆括号来模拟之前的行为。

```php
<?php
function f() {
    // Valid in PHP 5 only.
    global $$foo->bar;

    // Valid in PHP 5 and 7.
    global ${$foo->bar};
}
?>
```

作为一个通用的准则，[*global*](http://php.net/manual/zh/language.variables.scope.php#language.variables.scope.global)一个除了裸的变量以外的任何东西都是不被推荐的。

## 函数参数附近的括号不再影响行为

在PHP 7中(官方标注为php5,经过测试php5中无报错,报错在php7中,故作修改)，当函数参数通过引用传递时，围绕函数参数使用冗余括号可以保持严格的标准警告。警告将永远发出。

```php
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);

function getArray() {
    return [1, 2, 3];
}

function squareArray(array &$a) {
    foreach ($a as &$v) {
        $v **= 2;
    }
}

// Generates a warning in PHP 7.
squareArray((getArray()));
```

以上栗子会报错:

```
Notice: Only variables should be passed by reference in xxxxx
```

## foreach的变化

foreach发生了细微的变化，控制结构， 主要围绕阵列的内部数组指针和迭代处理的修改。

### foreach不再改变内部数组指针

在PHP7之前，当数组通过foreach迭代时，数组指针会移动。现在开始，不再如此，见下面代码:

```php
<?php
$array = [0, 1, 2];
foreach ($array as &$val) {
    var_dump(current($array));
}
?>
```

Output of the above example in PHP 5:

```
int(1)
int(2)
bool(false)
```

Output of the above example in PHP 7:

```
int(0)
int(0)
int(0)
```

### foreach通过值遍历时，操作的值为数组的副本

当默认使用通过值遍历数组时，foreach实际操作的是数组的迭代副本，而非数组本身。这就意味着，foreach 中的操作不会修改原数组的值。

### foreach通过引用遍历时，有更好的迭代特性

当使用引用遍历数组时，现在foreach在迭代中能更好的跟踪变化。例如，在迭代中添加一个迭代值到数组中，参考下面的代码：

```php
<?php
$array = [0];
foreach ($array as &$val) {
    var_dump($val);
    $array[1] = 1;
}
?>
```

Output of the above example in PHP 5:

```
int(0)
```

Output of the above example in PHP 7:

```
int(0)
int(1)
```

## string处理上的调整

### 十六进制字符串不再被认为是数字

含十六进制字符串不再被认为是数字(**也就是十六进制的字符串不会转成十进制数字进行比较,而是做一个字符串**)。例如：

```php
<?php
var_dump("0x123" == "291");
var_dump(is_numeric("0x123"));
var_dump("0xe" + "0x1");
var_dump(substr("foo", "0x1"));
?>
```

Output of the above example in PHP 5:

```
bool(true)
bool(true)
int(15)
string(2) "oo"
```

Output of the above example in PHP 7:

```
bool(false)
bool(false)
int(0)

Notice: A non well formed numeric value encountered in /tmp/test.php on line 5
string(3) "foo"
```

解决办法:

filter\_var() 函数可以用于检查一个 string 是否含有十六进制数字,并将其转换为integer:

```php
<?php
$str = "0xffff";
$int = filter_var($str, FILTER_VALIDATE_INT, FILTER_FLAG_ALLOW_HEX);
if (false === $int) {
    throw new Exception("Invalid integer!");
}
var_dump($int); // int(65535)
?>
```

### *\u{*&#x53EF;能引起错误

由于新的[Unicode codepoint escape syntax](http://php.net/manual/zh/migration70.new-features.php#migration70.new-features.unicode-codepoint-escape-syntax) 语法， 紧连着无效序列并包含`\u{`的字串可能引起致命错误。 为了避免这一报错，应该避免反斜杠开头。

## 被移除的函数（Removed functions）

### [call\_user\_method()](http://php.net/manual/zh/function.call-user-method.php) 和 [call\_user\_method\_array()](http://php.net/manual/zh/function.call-user-method-array.php)

这两个函数从PHP 4.1.0开始被废弃，应该使用[call\_user\_func()](http://php.net/manual/zh/function.call-user-func.php)和[call\_user\_func\_array()](http://php.net/manual/zh/function.call-user-func-array.php)。 你也可以考虑使用[变量函数](http://php.net/manual/zh/functions.variable-functions.php)或者[*...*](http://php.net/manual/zh/functions.arguments.php#functions.variable-arg-list.new)操作符。

### 所有的 ereg\* 函数

所有[ereg](http://php.net/manual/zh/book.regex.php)系列函数被删掉了。[PCRE](http://php.net/manual/zh/book.pcre.php)作为推荐的替代品。

### [mcrypt](http://php.net/manual/zh/book.mcrypt.php)别名

已废弃的[mcrypt\_generic\_end()](http://php.net/manual/zh/function.mcrypt-generic-end.php)函数已被移除，请使用[mcrypt\_generic\_deinit()](http://php.net/manual/zh/function.mcrypt-generic-deinit.php)代替。

此外，已废弃的[mcrypt\_ecb()](http://php.net/manual/zh/function.mcrypt-ecb.php),[mcrypt\_cbc()](http://php.net/manual/zh/function.mcrypt-cbc.php),[mcrypt\_cfb()](http://php.net/manual/zh/function.mcrypt-cfb.php)和[mcrypt\_ofb()](http://php.net/manual/zh/function.mcrypt-ofb.php)函数已被移除，请配合恰当的`MCRYPT_MODE_*`常量来使用[mcrypt\_decrypt()](http://php.net/manual/zh/function.mcrypt-decrypt.php)进行代替。

### 所有 ext/mysql 函数

所有[ext/mysql](http://php.net/manual/zh/book.mysql.php)函数已被删掉了。 如何选择不同的 MySQL API，详情请见[选择 MySQL API](http://php.net/manual/zh/mysqlinfo.api.choosing.php)。

### 所有 ext/mssql 函数

所有[ext/mssql](http://php.net/manual/zh/book.mssql.php)函数已被删掉了。 替代品的列表，参见[MSSQL 介绍](http://php.net/manual/zh/intro.mssql.php)。

### [intl](http://php.net/manual/zh/book.intl.php)别名

已废弃的[datefmt\_set\_timezone\_id()](http://php.net/manual/zh/intldateformatter.settimezoneid.php)和[IntlDateFormatter::setTimeZoneID()](http://php.net/manual/zh/intldateformatter.settimezoneid.php)函数已被移除，请使用[datefmt\_set\_timezone()](http://php.net/manual/zh/intldateformatter.settimezone.php)与[IntlDateFormatter::setTimeZone()](http://php.net/manual/zh/intldateformatter.settimezone.php)代替。

### [set\_magic\_quotes\_runtime()](http://php.net/manual/zh/function.set-magic-quotes-runtime.php)

[set\_magic\_quotes\_runtime()](http://php.net/manual/zh/function.set-magic-quotes-runtime.php), 和它的别名[magic\_quotes\_runtime()](http://php.net/manual/zh/function.magic-quotes-runtime.php)已被移除. 它们在PHP 5.3.0中已经被废弃,并且 在[in PHP 5.4.0](http://php.net/manual/zh/migration54.incompatible.php)也由于魔术引号的废弃而失去功能。

### [set\_socket\_blocking()](http://php.net/manual/zh/function.set-socket-blocking.php)

已废弃的[set\_socket\_blocking()](http://php.net/manual/zh/function.set-socket-blocking.php)函数已被移除，请使用[stream\_set\_blocking()](http://php.net/manual/zh/function.stream-set-blocking.php)代替。

### [dl()](http://php.net/manual/zh/function.dl.php) in PHP-FPM

[dl()](http://php.net/manual/zh/function.dl.php)在 PHP-FPM 不再可用，在 CLI 和 embed SAPIs 中仍可用。

### [GD](http://php.net/manual/zh/book.image.php) Type1 functions

PostScript Type1字体的支持已经从GD扩展删除，导致以下功能的去除：

* [imagepsbbox()](http://php.net/manual/zh/function.imagepsbbox.php)
* [imagepsencodefont()](http://php.net/manual/zh/function.imagepsencodefont.php)
* [imagepsextendfont()](http://php.net/manual/zh/function.imagepsextendfont.php)
* [imagepsfreefont()](http://php.net/manual/zh/function.imagepsfreefont.php)
* [imagepsloadfont()](http://php.net/manual/zh/function.imagepsloadfont.php)
* [imagepsslantfont()](http://php.net/manual/zh/function.imagepsslantfont.php)
* [imagepstext()](http://php.net/manual/zh/function.imagepstext.php)

推荐使用 TrueType 字体和相关的函数作为替代。

## 被移除掉的 INI 配置指令

### 被移除的功能

以下 INI 配置指令已经被移除，同时移除的还有其对应的功能

* [`always_populate_raw_post_data`](http://php.net/manual/zh/ini.core.php#ini.always-populate-raw-post-data)
* [`asp_tags`](http://php.net/manual/zh/ini.core.php#ini.asp-tags)

`xsl.security_prefs`

`xsl.security_prefs`指令被移除 在预处理的时候，取而代之的方法[XsltProcessor::setSecurityPrefs()](http://php.net/manual/zh/xsltprocessor.setsecurityprefs.php)应该被调用到

## 其他不向后兼容的变更

### new 操作符创建的对象不能以引用方式赋值给变量

[*new*](http://php.net/manual/zh/language.oop5.basic.php#language.oop5.basic.new)语句创建的对象不能 以引用的方式赋值给变量。

```php
<?php
class C {}
$c =& new C;
?>
```

Output of the above example in PHP 5:

```php
Deprecated: Assigning the return value of new by reference is deprecated in /tmp/test.php on line 3
```

Output of the above example in PHP 7:

```
Parse error: syntax error, unexpected 'new' (T_NEW) in /tmp/test.php on line 3
```

### 无效的类、接口以及 trait 命名

不能以下列名字来命名类、接口以及 trait：

* [bool](http://php.net/manual/zh/language.types.boolean.php)
* [int](http://php.net/manual/zh/language.types.integer.php)
* [float](http://php.net/manual/zh/language.types.float.php)
* [string](http://php.net/manual/zh/language.types.string.php)
* `NULL`
* `TRUE`
* `FALSE`

此外，也不要使用下列的名字来命名类、接口以及 trait。虽然在 PHP 7.0 中， 这并不会引发错误， 但是这些名字是保留给将来使用的。

* [resource](http://php.net/manual/zh/language.types.resource.php)
* [object](http://php.net/manual/zh/language.types.object.php)
* [mixed](http://php.net/manual/zh/language.pseudo-types.php#language.types.mixed)
* numeric

### 移除了 ASP 和 script PHP 标签

使用类似 ASP 的标签，以及 script 标签来区分 PHP 代码的方式被移除。 受到影响的标签有：

**被移除的 ASP 和 script 标签**

| 开标签                       | 闭标签         |
| ------------------------- | ----------- |
| `<%`                      | `%>`        |
| `<%=`                     | `%>`        |
| `<script language="php">` | `</script>` |

### 从不匹配的上下文发起调用

在不匹配的上下文中以静态方式调用非静态方法，[在 PHP 5.6 中已经废弃](http://php.net/manual/zh/migration56.deprecated.php#migration56.deprecated.incompatible-context)， 但是在 PHP 7.0 中， 会导致被调用方法中未定&#x4E49;*$this*变量，以及此行为已经废弃的警告。

```php
<?php
class A {
    public function test() { var_dump($this); }
}

// 注意：并没有从类 A 继承
class B {
    public function callNonStaticMethodOfA() { A::test(); }
}

(new B)->callNonStaticMethodOfA();
?>
```

Output of the above example in PHP 5.6:

```
Deprecated: Non-static method A::test() should not be called statically, assuming $this from incompatible context in /tmp/test.php on line 8
object(B)#1 (0) {
}
```

Output of the above example in PHP 7:

```
Deprecated: Non-static method A::test() should not be called statically in /tmp/test.php on line 8

Notice: Undefined variable: this in /tmp/test.php on line 3
NULL
```

### [yield](http://php.net/manual/zh/language.generators.syntax.php#control-structures.yield) 变更为右联接运算符

在使用 yield 关键字的时候，不再需要括号， 并且它变更为右联接操作符，其运算符优先级介于 print 和 => 之间。 这可能导致现有代码的行为发生改变：

```php
<?php
echo yield -1;
// 在之前版本中会被解释为：
echo (yield) - 1;
// 现在，它将被解释为：
echo yield (-1);

yield $foo or die;
// 在之前版本中会被解释为：
yield ($foo or die);
// 现在，它将被解释为：
(yield $foo) or die;
?>
```

可以通过使用括号来消除歧义。

### 函数定义不可以包含多个同名参数

在函数定义中，不可以包含两个或多个同名的参数。 例如，下面代码中的函数定义会触发`E_COMPILE_ERROR`错误：

```php
<?php
function foo($a, $b, $unused, $unused) {
    //
}
?>
```

### Switch 语句不可以包含多个 default 块

在 switch 语句中，两个或者多个 default 块的代码已经不再被支持。 例如，下面代码中的 switch 语句会触发`E_COMPILE_ERROR`错误：

```php
<?php
switch (1) {
    default:
    break;
    default:
    break;
}
?>
```

### 在函数中检视参数值会返回*当前*的值

当在函数代码中使用 [func\_get\_arg()](http://php.net/manual/zh/function.func-get-arg.php) 或 [func\_get\_args()](http://php.net/manual/zh/function.func-get-args.php) 函数来检视参数值， 或者使用 [debug\_backtrace()](http://php.net/manual/zh/function.debug-backtrace.php) 函数查看回溯跟踪， 以及在异常回溯中所报告的参数值是指参数当前的值（有可能是已经被函数内的代码改变过的值）， 而不再是参数被传入函数时候的原始值了。

```php
<?php
function foo($x) {
    $x++;
    var_dump(func_get_arg(0));
}
foo(1);?>
```

Output of the above example in PHP 5:

```
1
```

Output of the above example in PHP 7:

```
2
```

### [$HTTP\_RAW\_POST\_DATA](http://php.net/manual/zh/reserved.variables.httprawpostdata.php)被移除

不再提供 [$HTTP\_RAW\_POST\_DATA](http://php.net/manual/zh/reserved.variables.httprawpostdata.php) 变量。 请使用 [*php://input*](http://php.net/manual/zh/wrappers.php.php#wrappers.php.input) 作为替代。

### INI 文件&#x4E2D;*#*&#x6CE8;释格式被移除

在 INI 文件中，不再支持&#x4EE5;*#*&#x5F00;始的注释行， 请使&#x7528;*;*（分号）来表示注释。 此变更适用于php.ini以及用[parse\_ini\_file()](http://php.net/manual/zh/function.parse-ini-file.php)和[parse\_ini\_string()](http://php.net/manual/zh/function.parse-ini-string.php)函数来处理的文件。

### JSON 扩展已经被 JSOND 取代

JSON 扩展已经被 JSOND 扩展取代。 对于数值的处理，有以下两点需要注意的： 第一，数值不能以点号（.）结束 （例如，数值*34.*&#x5FC5;须写作*34.0*或*34*）。 第二，如果使用科学计数法表示数值，*e*前面必须不是点号（.） （例如，*3.e3*必须写作*3.0e3*或*3e3*）。 另外，空字符串不再被视作有效的 JSON 字符串。

### 在数值溢出的时候，内部函数将会失败

将浮点数转换为整数的时候，如果浮点数值太大，导致无法以整数表达的情况下， 在之前的版本中，内部函数会直接将整数截断，并不会引发错误。 在 PHP 7.0 中，如果发生这种情况，会引发 E\_WARNING 错误，并且返回`NULL`。

### 自定义会话处理器的返回值修复

在自定义会话处理器中，如果函数的返回值不是`FALSE`，也不&#x662F;*-1*， 会引发致命错误。现在，如果这些函数的返回值不是布尔值，也不&#x662F;*-1*或者*0*，函数调用结果将被视为失败，并且引发 E\_WARNING 错误。

### 相等的元素在排序时的顺序问题

由于内部排序算法进行了提升， 可能会导致对比时被视为相等的多个元素之间的顺序不稳定。

> 在对比时被视为相等的多个元素之间的排序顺序是不可信赖的，即使是相等的两个元素， 他们的位置也可能被排序算法所改变。


# 补充\*在SAPI模块的变化

> 参考:[Changes in SAPI Modules](http://php.net/manual/zh/migration70.sapi-changes.php#migration70.sapi-changes)

## 在SAPI模块的变化

### [FPM](http://php.net/manual/zh/book.fpm.php)

#### 不合格的监听端口 现在IPv4和IPv6¶监听

在PHP 5中，只有一个端口号的监听指令会监听所有接口，但只监听IPv4。PHP 7现在将接受通过IPv4和IPv6进行的请求。

这不会影响包含特定IP地址的指令;这些指令将继续只监听那个地址和协议。


# 补充\*PHP7底层性能优化

> 请参考视频资料：[PHP7 底层性能优化(一)](http://www.imooc.com/video/8532) [PHP7 底层性能优化(二)](http://www.imooc.com/video/8533)

## 目录：

* zval使用栈内存
* zend\_string存储hash值，array查询不在需要重复计算hash
* zend\_parse\_parameters改为宏实现，性能提升5%
* 新增加了4种OPCODE, call\_user\_function,is\_int/string/array,strlen,defined 4个函数变为PHP OpCode指令，速度更快
* 其他更多性能优化：
* 其他：PHP7.0-final版本不会携带JIT特征

## **zval使用栈内存**

在Zend引擎和扩展中，经常要创建一个PHP变量，底层就是一个zval指针。之前的版本都是通过MAKE\_STD\_ZVAL动态从堆上分配一个zval内存。而PHP7可以直接使用栈内存。

32字节-》16字节

这样做的好处：大量节省了内存分配和内存管理的工作，性能就会得到很大的提升。

#### PHP **5**

```c
zval *val; MAKE_STD_ZVAL(val);
```

#### PHP7

```c
zval val;
```

## **zend\_string存储hash值，array查询不在需要重复计算hash**

PHP7为字符串单独创建了新类型叫zend\_string，除了char \*指针和长度之外，增加了一个hash字段，用于保存字符串的hash值。数组键值查找不需要反复计算hash值。

```c
struct _zend_string {
    zend_refcounted gc;
    zend_ulong      h;
    size_t          len;
    char            val[1]
};
```

为了优化数组的键值查找性能，h即为存储hash值的字段。

**hashtable桶内直接存数据，减少了内存申请次数，提升了Cache命中率和内存访问速度**

PHP 7 新的**hashtable实现**：

![](/files/-LfnTJowlOH2wk--hfom)\
老：之前的php底层HashTable的实现:<http://www.cnblogs.com/mingaixin/p/4318805.html>

## ![](/files/-LfnTJoyW-dEPsSiPtXA)**zend\_parse\_parameters改为宏实现，性能提升5%**

zend\_parse\_parameters：是从php变量到C扩展函数之间交换一些参数，还有交换返回值，这时候要用zend\_parse\_parameters函数来实现。

### **新增加了4种OPCODE, call\_user\_function,is\_int/string/array,strlen,defined 4个函数变为PHP OpCode指令，速度更快**

### **其他更多性能优化：**

如基础类型为int，float，bool等改为直接进行值拷贝；排序算法改进，PCRE with JIT（正则表达式直接编译成机器码）, execute\_data和opline使用全局寄存器，使用gdb4.8的PGO功能（运行一段时间，它会导出一份运行时的数据统计）

## 其他：PHP7.0-final版本不会携带JIT特征

> 请参考视频资料： PHP7 与 JIT

JIT是just in time的缩写，表示运行时将指令转为二进制机器码。

**为什么PHP7中没有引入JIT ?** 原因：JIT对于实际项目，如WordPress没有太大的性能提升。

**但是：**&#x5BF9;于计算密集型程序，JIT可以将PHP的OpCode直接转换为机器码，大幅提升性能。\
PHP开发组已重启JIT开发计划，我看了一下官方，截止到PHP7.1版本没有带有JIT特征。


# PHP 7.1.x 新特性


# 新特性


# 可为空（Nullable）类型

**官方解释：**&#x7C7B;型现在允许为空，当启用这个特性时，传入的参数或者函数返回的结果要么是给定的类型，要么是[null](https://php.net/manual/zh/language.types.null.php)。可以通过在类型前面加上一个问号来使之成为可为空的。

**大话解释：**&#x5B98;方的中文翻译是有歧义的，经过测试正确的解释应该是：类型在7.0的时候,不能传入null值，也不能什么都不传，两种情况都会报错，7.1中加上一个问号问号来使之可以传入为null的类型，但是什么值都不传的情况仍然会报错

> 同时适用于函数和对象的方法中

## 形参类型声明：

### 函数中，如下代码：

```php
function test(string $name)
{
    var_dump($name);
}

test('tpunt');
test(null); //此行会引发的错误
test();　//此行会引发的错误
```

### 对象的方法中，如下代码：

```php
class Tests
{
    function test(string $name)
    {
        var_dump($name);
    }

}

$test = new Tests();
$test->test('tpunt');
$test->test(null); //由此会行引发的错误
$test->test();　//由此会行引发的错误
```

PHP7.0 中会报错．如下：

```
Fatal error: Uncaught TypeError: Argument 1 passed to test() must be of the type string, null given, called in .....
TypeError: Argument 1 passed to test() must be of the type string, null given, called in ....
```

**在PHP7.1当中只需要在类型前面加** `?` **即可解决null 报错，什么值都不传的情况仍然会报错．**

```
?string $name
```

结果

```
test();　//此行仍然会引发的错误
－－－－－－－－－－－－－－－－－－－－－－－－－－
Fatal error: Uncaught TypeError: Argument 1 passed to test() must be of the type string, null given, called in .....
TypeError: Argument 1 passed to test() must be of the type string, null given, called in ....
```

进一步结果可以解决`test()`这种情况的报错，但是如果以上函数的参数定义为 ?Bar $bar = null 的形式，则第三种写法也是可行的。因为 = null实际上相当于 ? 的超集，对于可空类型的参数，可以设定 null 为默认值。

```php
function test(?string $name = null)
{
    var_dump($name);
}

test('tpunt');
test(null); // ok
test(); // ok
```

以上代码即可解决．同样适用于对象的方法中的形参类型声明．

## 返回类型声明：

```php
function answer1(): ?int  {
    return null; //ok
}
var_dump(answer1());

function answer2(): ?int  {
    return 42; // ok
}
var_dump(answer2());

function answer3(): ?int  {
    return ''; //由此会行引发的错误 
}
var_dump(answer3());
```

对象候总类方法的返回类型声明就不再举例了．


# 对称阵列解构

**官方解释：**&#x77ED;数组语法（*\[]*）现在可以用于将数组的值赋给一些变量（包括在*foreach*中）。 这种方式使从数组中提取值变得更为容易。

以下是官方示例：**但是经过我测试（PHP Version 7.1.5），是一个错误的例子**

```php
<?php
$data = [
    ['id' => 1, 'name' => 'Tom'],
    ['id' => 2, 'name' => 'Fred'],
];

while (['id' => $id, 'name' => $name] = $data) {
    // logic here with $id and $name
}
```

我们知道在 PHP5.4 之前只能通过 `array()` 来定义数组，5.4之后添加了 `[]` 的简化写法（省略了5个字符还是很实在的）。

```php
<?php
// 5.4 之前
$array = array(1, 2, 3);
$array = array("a" => 1, "b" => 2, "c" => 3);

// 5.4 及之后
$array = [1, 2, 3];
$array = ["a" => 1, "b" => 2, "c" => 3];
```

引申到另外一个问题上，如果我们要把数组的值赋值给不同的变量，可以通过 `list` 来实现：

```php
<?php
list($a, $b, $c) = $array;

list($a,$b)=array(10,20);
echo $a,'~',$b,'<br />';
//返回10~20

list($a,$b,,$c)=array(2=>10,3=>20,4=>30,1=>40);
echo $a,'~',$b,'~',$c,'<br />';
//返回notice~40~20
//执行到$a的时候返回给我一个notice：说数组没有0键
```

为什么会返回这个notice\~40\~20呢？ [查看解释](http://www.cnblogs.com/ggbd-lie/p/3269192.html)

PHP7.1 实现了一下特性。但是要注意的是：出现在左值中的 `[]` 并不是数组的简写，是 `list()` 的简写。

但是并不仅仅如此，新的 `list()` 的实现并不仅仅可以出现在左值中，也能在 `foreach` 循环中使用：

```php
$data = [
    ['id' => 1, 'name' => 'Tom'],
    ['id' => 2, 'name' => 'Fred'],
];


foreach ($data as ['id' => $id, 'name' => $name]) {
    echo $id . PHP_EOL;
    echo $name. PHP_EOL;
}
```

结果：

```
id:1 name:Tom id:2 name:Fred
```

不过因为实现的问题，`list()`和`[]`不能相互嵌套使用：


# Void 函数

在PHP 7 中引入的其他返回值类型的基础上，一个新的返回值类型void被引入。 返回值声明为 void 类型的方法要么干脆省去 return 语句，要么使用一个空的 return 语句。 对于 void 函数来说，null 不是一个合法的返回值。

```php
<?php
function swap(&$left, &$right) : void
{
    if ($left === $right) {
        return;
    }

    $tmp = $left;
    $left = $right;
    $right = $tmp;
}

$a = 1;
$b = 2;
var_dump(swap($a, $b), $a, $b);
```

以上代码

* 当`$a = 1;　$b = 2;` 并不会进入到if语句中，所以会继续执行，最后也没有返回到任何的值．

```
null
int(2)
int(1)
```

* 当`$a = 1;　$b = １;`则会进入到if语句中，return返回．

```
null
int(1)
int(1)
```

由于返回类型为void，则两种情况１．无写返回值 ２．直接return 方式返回，获取返回结果均会得到一个null值．

试图去获取一个 void 方法的返回值会得到 null ，并且不会产生任何警告。这么做的原因是不想影响更高层次的方法。

> 注意，不但适用于函数中，也同样适用于对象的方法中

如下示例：

```php
<?php
error_reporting(E_ALL);
ini_set('display_errors', true);


class Tests
{
    function swap(&$left, &$right) : void
    {
        if ($left === $right) {
            return;
        }

        $tmp = $left;
        $left = $right;
        $right = $tmp;
    }
}

$test = new Tests();
$a = 1;
$b = 2;
var_dump($test->swap($a, $b), $a, $b);
```

结果：

```
null
int(2)
int(1)
```

## 补充整理总结：

PHP7.0 添加了指定函数返回类型的特性，但是返回类型却不能指定为 `void`，7.1 的这个特性算是一个补充，但是让人大跌眼镜的是一下会报错：

```php
<?php
function should_return_nothing(): void {
    return 1; // Fatal error: A void function must not return a value
}
```

以下两种情况都可以通过验证：

```php
<?php
function lacks_return(): void {
    // valid
}

function returns_nothing(): void {
    return; // valid
}
```

定义返回类型为 `void` 的函数不能有返回值，即使返回 `null` 也不行：

```php
<?php
function returns_one(): void {
    return 1; // Fatal error: A void function must not return a value
}

function returns_null(): void {
    return null; // Fatal error: A void function must not return a value
}
```

此外 `void` 也只适用于返回类型，并不能用于参数类型声明，或者会触发错误：

```php
<?php
function foobar(void $foo) { // Fatal error: void cannot be used as a parameter type
}
```

类函数中对于返回类型的声明也不能被子类覆盖，否则会触发错误：

```php
<?php
class Foo
{
    public function bar(): void {
    }
}

class Foobar extends Foo
{
    public function bar(): array { // Fatal error: Declaration of Foobar::bar() must be compatible with Foo::bar(): void
    }
}
```


# 类常量访问权限控制

现在起支持设置类常量的可见性。可使用private、protected、public权限控制．

```php
class ConstDemo
{
    // 常量默认为 public
    const PUBLIC_CONST_A = 1;

    // 可以自定义常量的可见范围
    public const PUBLIC_CONST_B = 2;
    protected const PROTECTED_CONST = 3;
    private const PRIVATE_CONST = 4;

    // 多个常量同时声明只能有一个属性
    private const FOO = 1, BAR = 2;
}

class ConstDemo1 extends ConstDemo
{
    public function __construct() {
        echo self::PUBLIC_CONST_A;
        echo self::PUBLIC_CONST_B;
        echo self::PROTECTED_CONST;
        echo self::PRIVATE_CONST;
    }
}

 new ConstDemo1();
```

结果：

```php
123
```

由此可见用法和之前的对象属性和对象方法的控制一样．

此外，接口（interface）中的常量只能是 `public`属性：

```php
<?php
interface ICache {
    public const PUBLIC = 0;
    const IMPLICIT_PUBLIC = 1;
}
```


# 多异常捕获处理

一个catch语句块现在可以通过管道字符(|)来实现多个异常的捕获。 这对于需要同时处理来自不同类的不同异常时很有用。

```php
<?php
try {
    // some code
} catch (FirstException | SecondException $e) {
    // handle first and second exceptions
}
```

## 解说：

在以往的 `try ... catch` 语句中，每个 `catch` 只能设定一个条件判断：

```php
<?php
try {
    // Some code...
} catch (ExceptionType1 $e) {
    // 处理 ExceptionType1
} catch (ExceptionType2 $e) {
    // 处理 ExceptionType2
} catch (\Exception $e) {
    // ...
}
```

新的实现中可以在一个 `catch` 中设置多个条件，相当于或的形式判断：

```php
<?php
try {
    // Some code...
} catch (ExceptionType1 | ExceptionType2 $e) {
    // 对于 ExceptionType1 和 ExceptionType2 的处理
} catch (\Exception $e) {
    // ...
}
```

对于异常的处理简化了一些。


# list()现在支持键名

现在[list()](https://php.net/manual/zh/function.list.php)支持在它内部去指定键名。这意味着它可以将任意类型的数组 都赋值给一些变量（与短数组语法类似）

**此例子我在PHP Version 7.1.5测试中始终报错，具体的功能和对称阵列解构的新特征一样**

```php
<?php
$data = [
    ['id' => 1, 'name' => 'Tom'],
    ['id' => 2, 'name' => 'Fred'],
];

while (list('id' => $id, 'name' => $name) = $data) {
    // logic here with $id and $name
}
```


# 支持为负的字符串偏移量

现在所有支持偏移量的字符串操作函数 都支持接受负数作为偏移量，包括通过\[]或{}操作字符串下标。在这种情况下，一个负数的偏移量会被理解为一个从字符串结尾开始的偏移量。

> 注意：偏移量从０开始

```php
var_dump("abcdef"[2]);  //c
var_dump("abcdef"[-2]);  //e
var_dump(strpos("aabbcc", "b", -3)); // 3
```

负字符串和数组偏移现在也支持字符串中简单的变量分析语法。

```php
<?php
$string = 'bar';
echo "The last character of '$string' is '$string[-1]'.\n";
?>
```

以上例程会输出：

```
The last character of 'bar' is 'r'.
```


# 补充\*其他特性与变更

## iterable伪类

现在引入了一个新的被称为iterable的伪类 (与[callable](https://php.net/manual/zh/language.types.callable.php)类似)。 这可以被用在参数或者返回值类型中，它代表接受数组或者实现了

**Traversable** 接口的对象。 至于子类，当用作参数时，子类可以收紧父类的iterable类型到[array](https://php.net/manual/zh/language.types.array.php)或一个实现了**Traversable**的对象。对

于返回值，子类可以拓宽父类的[array](https://php.net/manual/zh/language.types.array.php)或对象返回值类型到iterable。

```php
<?php
function iterator(iterable $iter)
{
    foreach ($iter as $val) {
        //
    }
}
```

## ext/openssl 支持 AEAD

通过给 [openssl\_encrypt()](https://php.net/manual/zh/function.openssl-encrypt.php) 和 [openssl\_decrypt()](https://php.net/manual/zh/function.openssl-decrypt.php) 添加额外参数，现在支持了AEAD (模式 GCM and CCM)。

## 通过**Closure::fromCallable()**&#x5C06;callables转为闭包

Closure新增了一个静态方法，用于将callable快速地 转为一个Closure 对象。

```php
<?php
class Test
{
    public function exposeFunction()
    {
        return Closure::fromCallable([$this, 'privateFunction']);
    }

    private function privateFunction($param)
    {
        var_dump($param);
    }
}

$privFunc = (new Test)->exposeFunction();
$privFunc('some value');
```

以上例程会输出：

```
string(10) "some value"
```

## 异步信号处理

一个新的名为**pcntl\_async\_signals()**&#x7684;方法现在被引入， 用于启用无需 ticks （这会带来很多额外的开销）的异步信号处理。

```php
<?php
pcntl_async_signals(true); // turn on async signals

pcntl_signal(SIGHUP,  function($sig) {
    echo "SIGHUP\n";
});

posix_kill(posix_getpid(), SIGHUP);
```

以上例程会输出：

```
SIGHUP
```

## HTTP/2 server push support in ext/curl

对服务器推送的支持现在已经被加入到 CURL 扩展中（ 需要版本 7.46 或更高）。这个可以通过[curl\_multi\_setopt()](https://php.net/manual/zh/function.curl-multi-setopt.php)函数与新的常量`CURLMOPT_PUSHFUNCTION`来进行调节。常量`CURL_PUST_OK`和`CURL_PUSH_DENY`也已经被添加进来，以便服务器推送的回调函数来表明自己会同意或拒绝处理。


# 补充\*新的函数

> 参考：[新的函数](https://secure.php.net/manual/zh/migration71.new-functions.php)

## 新的函数

### PHP Core

* **sapi\_windows\_cp\_get()**
* **sapi\_windows\_cp\_set()**
* **sapi\_windows\_cp\_conv()**
* **sapi\_windows\_cp\_is\_utf8()**

### [Closure](https://php.net/manual/zh/class.closure.php)

* **Closure::fromCallable()**

### [CURL](https://php.net/manual/zh/book.curl.php)

* **curl\_multi\_errno()**
* **curl\_share\_errno()**
* **curl\_share\_strerror()**

### [Session](https://php.net/manual/zh/book.session.php)

* [session\_create\_id()](https://php.net/manual/zh/function.session-create-id.php)
* [session\_gc()](https://php.net/manual/zh/function.session-gc.php)

### [SPL](https://php.net/manual/zh/book.spl.php)

* [is\_iterable()](https://php.net/manual/zh/function.is-iterable.php)

### [PCNTL](https://php.net/manual/zh/book.pcntl.php)

* **pcntl\_async\_signals()**
* [pcntl\_signal\_get\_handler()](https://php.net/manual/zh/function.pcntl-signal-get-handler.php)


# 补充\*新增的全局常量

> 参考：[新增的全局常量](https://secure.php.net/manual/zh/migration71.constants.php)

## 新增的全局常量

### [PHP 核心中预定义的常量](https://php.net/manual/zh/reserved.constants.php)

* `PHP_FD_SETSIZE`

### [CURL](https://php.net/manual/zh/book.curl.php)

* `CURLMOPT_PUSHFUNCTION`
* `CURL_PUSH_OK`
* `CURL_PUSH_DENY`

### [Data Filtering](https://php.net/manual/zh/book.filter.php)

* `FILTER_FLAG_EMAIL_UNICODE`

### [Image Processing and GD](https://php.net/manual/zh/book.image.php)

* `IMAGETYPE_WEBP`

### [JSON](https://php.net/manual/zh/book.json.php)

* `JSON_UNESCAPED_LINE_TERMINATORS`

### [LDAP](https://php.net/manual/zh/book.ldap.php)

* `LDAP_OPT_X_SASL_NOCANON`
* `LDAP_OPT_X_SASL_USERNAME`
* `LDAP_OPT_X_TLS_CACERTDIR`
* `LDAP_OPT_X_TLS_CACERTFILE`
* `LDAP_OPT_X_TLS_CERTFILE`
* `LDAP_OPT_X_TLS_CIPHER_SUITE`
* `LDAP_OPT_X_TLS_KEYFILE`
* `LDAP_OPT_X_TLS_RANDOM_FILE`
* `LDAP_OPT_X_TLS_CRLCHECK`
* `LDAP_OPT_X_TLS_CRL_NONE`
* `LDAP_OPT_X_TLS_CRL_PEER`
* `LDAP_OPT_X_TLS_CRL_ALL`
* `LDAP_OPT_X_TLS_DHFILE`
* `LDAP_OPT_X_TLS_CRLFILE`
* `LDAP_OPT_X_TLS_PROTOCOL_MIN`
* `LDAP_OPT_X_TLS_PROTOCOL_SSL2`
* `LDAP_OPT_X_TLS_PROTOCOL_SSL3`
* `LDAP_OPT_X_TLS_PROTOCOL_TLS1_0`
* `LDAP_OPT_X_TLS_PROTOCOL_TLS1_1`
* `LDAP_OPT_X_TLS_PROTOCOL_TLS1_2`
* `LDAP_OPT_X_TLS_PACKAGE`
* `LDAP_OPT_X_KEEPALIVE_IDLE`
* `LDAP_OPT_X_KEEPALIVE_PROBES`
* `LDAP_OPT_X_KEEPALIVE_INTERVAL`

### [PostgreSQL](https://php.net/manual/zh/book.pgsql.php)

* `PGSQL_NOTICE_LAST`
* `PGSQL_NOTICE_ALL`
* `PGSQL_NOTICE_CLEAR`

### [SPL](https://php.net/manual/zh/book.spl.php)

* `MT_RAND_PHP`


# 补充:不向后兼容的变更

> 参考：[不向后兼容的变更](https://secure.php.net/manual/zh/migration71.incompatible.php)

## 不向后兼容的变更

### 当传递参数过少时将抛出错误

在过去如果我们调用一个用户定义的函数时，提供的参数不足，那么将会产生一个警告(warning)。 现在，这个警告被提升为一个错误异常(Error exception)。这个变更仅对用户定义的函数生效， 并不包含内置函数。例如：

```php
<?php
function test($param){}
test();
```

Output of the above example in PHP 5.5:

```
Uncaught Error: Too few arguments to function test(), 0 passed in %s on line %d and exactly 1 expected in %s:%d
```

### 禁止动态调用范围内功能

对某些函数的动态调用被禁止(以`$func()`或`array_map('extract', ...)`等形式)。这些函数可以检查或修改另一个范围，并呈现出模糊和不可靠的行为。其职能如下:

* [assert()](https://php.net/manual/zh/function.assert.php) - 用一个字符串作为第一个参数
* [compact()](https://php.net/manual/zh/function.compact.php)
* [extract()](https://php.net/manual/zh/function.extract.php)
* [func\_get\_args()](https://php.net/manual/zh/function.func-get-args.php)
* [func\_get\_arg()](https://php.net/manual/zh/function.func-get-arg.php)
* [func\_num\_args()](https://php.net/manual/zh/function.func-num-args.php)
* [get\_defined\_vars()](https://php.net/manual/zh/function.get-defined-vars.php)
* [mb\_parse\_str()](https://php.net/manual/zh/function.mb-parse-str.php) - 有一个参数
* [parse\_str()](https://php.net/manual/zh/function.parse-str.php) - 有一个参数

```php
<?php
(function () {
    'func_num_args'();
})();
```

以上例程会输出：

```
Warning: Cannot call func_num_args() dynamically in %s on line %d
```

### 无效的类、接口和特征名称

以下名称不能用于名称类、接口或特征:

* void
* iterable &#x20;

### 数值串转换现在尊重科学符号

数值操作和数值转换现在要尊重科学符号。这还包括(int)cast操作，以及以下函数:intval()(在这里的基础是10)、settype()、decbin()、decbin()和dechex()。

### 修复mt\_rand()算法

mt\_rand()现在默认使用Mersenne Twister算法的固定版本。如果依赖于mt\_srand()的确定性输出，则MT\_RAND\_PHP有能力将旧的(不正确的)实现通过另一个可选的第二个参数来保存mt\_srand()。

### rand()别名为mt\_rand()和srand()别名为mt\_srand()

rand()和srand()现在分别对mt\_rand()和mt\_srand()进行了别名。这意味着下列函数的输出有更改:rand()、shuffle()、str\_shuffle()和array\_rand()。

### 不允许在标识符中删除ASCII删除控制字符

ASCII删除控制字符(0x7F)不能再用在没有引用的标识符中。

### error\_log用syslog值进行更改

如果将error\_log ini设置设置为syslog，则将PHP错误级别映射到syslog错误级别。这将在错误日志中提供更细的差异，与前面的方法相反，所有的错误都只在通知级别上记录。

在不完整的对象上不再调用析构方法

析构方法在一个不完整的对象（例如在构造方法中抛出一个异常）上将不再会被调用。

### call\_user\_func()不再支持对传址的函数的调用

[call\_user\_func()](https://php.net/manual/zh/function.call-user-func.php)现在在调用一个以引用作为参数的函数时将始终失败。

### 字符串不再支持空索引操作符 The empty index operator is not supported for strings anymore

对字符串使用一个空索引操作符（例&#x5982;*$str\[] = $x*）将会抛出一个致命错误， 而不是静默地将其转为一个数组。

### ini配置项移除

下列ini配置项已经被移除：

* `session.entropy_file`
* `session.entropy_length`
* `session.hash_function`
* `session.hash_bits_per_character`&#x20;

### 在引用赋值过程中自动创建元素的数组顺序发生了变化

数组中元素的顺序已经发生了变化，当这些元素通过引用被引用的赋值自动创建时。例如:

```php
<?php
$array = [];
$array["a"] =& $array["b"];
$array["b"] = 1;
var_dump($array);
?>
```

Output of the above example in PHP 7.0:

array(2) {

```
["a"]=
>
&
int(1)
  ["b"]=
>
&
int(1)
}
```

Output of the above example in PHP 7.1:

```php
array(2) {
  ["b"]=>
  &int(1)
  ["a"]=>
  &int(1)
}
```

### 等元素的排序 Sort order of equal elements

内部排序算法得到了改进，其结果可能是不同的元素顺序，比之前的相等。

> 不要依赖于元素的顺序，因为元素的顺序是相等的;它可能随时变化。

### Error message for E\_RECOVERABLE errors

The error message for E\_RECOVERABLE errors has been changed from "Catchable fatal error" to "Recoverable fatal error".

### unserialize() 的`$options`参数

unserialize()的`$options`参数的allowed\_classes元素现在被严格输入，即如果给定数组或布尔值之外的其他任何东西，unserialize()将返回FALSE并发出E\_WARNING。

### DateTime构造函数包含微秒

[DateTime](https://php.net/manual/zh/class.datetime.php)和[DateTimeImmutable](https://php.net/manual/zh/class.datetimeimmutable.php)不变现在正确地融合了从当前时间构建的微秒，无论是显式的还是相对的字符串。“下个月的第一天”)。这意味着对两个新创建的实例进行简单的比较，现在更有可能返回**FALSE**而不是**TRUE**:

```php
<?php
new DateTime() == new DateTime();
?>
```

### 错误异常的致命错误转换

详见：[官方](https://secure.php.net/manual/zh/migration71.incompatible.php) ,搜索：Fatal errors to**Error**exceptions conversions

### 词汇绑定的变量不能重用名称

通过使用构造绑定到闭包的变量不能使用与任何超全局变量相同的名称，$ this或任何参数。例如，所有这些函数定义都会导致一个致命错误:

```php
<?php
$f = function () use ($_SERVER) {};
$f = function () use ($this) {};
$f = function ($param) use ($param) {};
```

### JSON编码和解码

在编码双精度时，serialize\_precision ini设置现在控制序列化精度。

现在解码一个空的键会导致一个空的属性名，而不是一个属性名。

```php
<?php
var_dump(json_decode(json_encode(['' => 1])));
```

以上例程的输出类似于：

```
object(stdClass)#1 (1) {
  [""]=>
  int(1)
}
```

当向[json\_encode()](https://php.net/manual/zh/function.json-encode.php)提供JSON\_UNESCAPED\_UNICODE标志时，现在转义了U + 2028和U + 2029的序列。

### 对mb\_ereg()和mb\_eregi()参数语义的更改

如果没有匹配，则将把第三个参数设置为mb\_ereg()和mb\_eregi()函数(regs)，现在将被设置为空数组。Formely，参数不会被修改。

### 停止支持sslv2流

sslv2流现在已经在OpenSSL中被删除了。


# 补充\*废弃的特性

> 参考：[从PHP 7.0.x 移植到 PHP 7.1.x](https://php.net/manual/zh/migration71.php)

## 废弃的特性

### ext/mcrypt

mcrypt 扩展已经过时了大约10年，并且用起来很复杂。因此它被废弃并且被 OpenSSL 所取代。 从PHP 7.2起它将被从核心代码中移除并且移到PECL中。

### [mb\_ereg\_replace()](https://php.net/manual/zh/function.mb-ereg-replace.php)和[mb\_eregi\_replace()](https://php.net/manual/zh/function.mb-eregi-replace.php)的Eval选项

对于[mb\_ereg\_replace()](https://php.net/manual/zh/function.mb-ereg-replace.php)和[mb\_eregi\_replace()](https://php.net/manual/zh/function.mb-eregi-replace.php)的*e*模式修饰符现在已被废弃。


# 补充\*变更的函数

> 参考：[Changed functions](https://secure.php.net/manual/zh/migration71.changed-functions.php)

## 变更的函数

### PHP Core

* [getopt()](https://php.net/manual/zh/function.getopt.php) 有一个可选的第三个参数，在参数向量列表中显示下一个元素的索引。这是通过一个by ref参数完成的。
* [getenv()](https://php.net/manual/zh/function.getenv.php) 不再需要它的参数。如果忽略了参数，那么当前的环境变量将作为关联数组返回。
* [get\_headers()](https://php.net/manual/zh/function.get-headers.php) 现在有了一个附加的参数来启用自定义流上下文。\
  [long2ip()](https://php.net/manual/zh/function.long2ip.php) 现在还可以接受整数作为参数。
* [output\_reset\_rewrite\_vars()](https://php.net/manual/zh/function.output-reset-rewrite-vars.php) 不再重新设置会话URL重写变量。
* [parse\_url()](https://php.net/manual/zh/function.parse-url.php) 现在的限制更多，支持RFC3986。
* [unpack()](https://php.net/manual/zh/function.unpack.php) 现在接受一个可选的第三个参数来指定开始解包的偏移量。

### File System

* [file\_get\_contents()](https://php.net/manual/zh/function.file-get-contents.php) now accepts a negative seek offset if the stream is seekable.
* [tempnam()](https://php.net/manual/zh/function.tempnam.php) 现在，在返回系统的临时目录时发出通知。

### JSON

* [json\_encode()](https://php.net/manual/zh/function.json-encode.php) 现在接受一个新选项，`JSON_UNESCAPED_LINE_TERMINATORS`

  ,禁用 U+2028 和 U+2029 字符串的转义 当`JSON_UNESCAPED_UNICODE　被提供`.

### Multibyte String

* [mb\_ereg()](https://php.net/manual/zh/function.mb-ereg.php)　现在拒绝非法字节序列。
* [mb\_ereg\_replace()](https://php.net/manual/zh/function.mb-ereg-replace.php)　现在拒绝非法字节序列。

### PDO

* [PDO::lastInsertId()](https://php.net/manual/zh/pdo.lastinsertid.php)　对于PostgreSQL，当nextval没有调用当前会话(postgres连接)时，将触发一个错误。

### PostgreSQL

* [pg\_last\_notice()](https://php.net/manual/zh/function.pg-last-notice.php)　现在接受一个可选参数来指定操作。这可以通过以下新常量之一来完成: PGSQL\_NOTICE\_LAST, PGSQL\_NOTICE\_ALL, or PGSQL\_NOTICE\_CLEAR.

  .
* [pg\_fetch\_all()](https://php.net/manual/zh/function.pg-fetch-all.php)　现在接受一个可选的第二个参数来指定结果类型(类似于[pg\_fetch\_array()](https://php.net/manual/zh/function.pg-fetch-array.php)的第三个参数)。
* [pg\_select()](https://php.net/manual/zh/function.pg-select.php)　现在接受一个可选的第四个参数来指定结果类型(类似于[pg\_fetch\_array()](https://php.net/manual/zh/function.pg-fetch-array.php)的第三个参数)。


# 补充\*其他的变更

> 参考：[Other changes](https://secure.php.net/manual/zh/migration71.other-changes.php)

## 其他的变更

### 使用无效字符串进行算术的注意和警告

引入了新的E\_WARNING和E\_NOTICE错误时无效的字符串强制使用运营商预计数字(+ - \* / \* \* % < < > > | & ^)或其等价物。当字符串以数字值开始时，会发出一个E\_NOTICE，但是它包含后面的非数值字符，当字符串不包含数值时，会发出一个E\_WARNING。

```
<?php
'1b' + 'something';
```

以上例程会输出：

```
Notice: A non well formed numeric value encountered in %s on line %d
```

### 警告octal转义序列溢出

以前，3 octet的八进制字符串转义序列将会悄无声息地溢出。现在，它们仍然会溢出，但E\_WARNING将被发出。

```php
<?php
var_dump("\500");
```

以上例程会输出：

```
Warning: Octal escape sequence overflow \500 is greater than \377 in %s on line %d
string(1) "@"
```

矛盾解决`$this`

尽管`$this`被认为是PHP中的一个特殊变量，但是它缺少适当的检查来确保它不用作变量名或重新分配。现在已经纠正了这一点，以确保$ This不能是用户定义的变量，重新分配给不同的值，或者是全球化的。

### 没有哈希的会话ID生成

会话id将不再被哈希生成。有了这一变化，就会导致以下4个ini设置的删除:

* `session.entropy_file`
* `session.entropy_length`
* `session.hash_function`
* `session.hash_bits_per_character`&#x20;

加上以下两个ini设置:

* `session.sid_length`- 定义会话ID的长度，默认为向后兼容的32个字符
* `session.sid_bits_per_character`- 定义每个字符存储的比特数(即增加可以在会话ID中使用的字符的范围)，默认为4以支持向后兼容.

### 更改INI文件处理

精度(precision)

如果值设置为- 1，则使用dtoa模式0。默认值仍然是14。

serialize\_precision

如果值设置为- 1，则使用dtoa模式0。值- 1现在默认使用。

gd.jpeg\_ignore\_warning

这个php的默认值。ini设置已经更改为1，因此默认的libjpeg警告将被忽略。

opcache.enable\_cli

这个php的默认值。在PHP 7.1.2中，ini设置已经更改为1(启用)。

### 只使用CSPRNG的会话ID生成

会话id现在只能用CSPRNG生成。

### 当允许NULL时，更多的信息类型错误消息

**TypeError**exceptions for arg\_info type checks will now provide more informative error messages. If the parameter type or return type accepts`NULL`(by either having a default value of`NULL`or being a nullable type), then the error message will now mention this with a message of "must be ... or null" or "must ... or be null."


# PHP7 开发工具


# Eclipse 安装与配置

## 目录

* Eclipse 安装
* Eclipse 配置
  * 1.tab 变为4个空格:
  * 2.Eclipse配色方案插件
  * 3.停止Eclipse里面**DLTKindexing**进程

## Eclipse 安装

[Eclipse 官方下载地址](https://www.eclipse.org/downloads/)

[Eclipse for PHP Developers](http://www.eclipse.org/downloads/packages/eclipse-php-developers/neon3)

由于最版本一般对java的版本有最新的要求,所以可以选择考虑早期版本的[Eclipse](https://www.gitbook.com/book/xiaoxiami/php-7/edit#)列表

注意:php7的开发的话下载最新版本的[Eclipse for PHP Developers](https://www.gitbook.com/book/xiaoxiami/php-7/edit#)即可,会集成php7及以上开发环境,

> 这里我使用的版本: Neon.3 Release (4.6.3)

![](/files/-LfnTFmVl6elImcWxzOT)

需要java 1.8版本

```
$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
```

## Eclipse 配置

### 1.tab 变为4个空格:

1.点击 window->preference-,依次选择 General->Editors->Text Editors,选中右侧的 insert space for tabs;如下图所示，保存，第一步完成；

![](/files/-LfnTGrtlBxMr5vyalxP)

2.点击 window->preference-,依次选择PHP->code style ->formatter,点击右侧的editor，选则左侧 tab policy的值为spaces only,确定，应用保存即可，如下图所示：

![](/files/-LfnTGrv5QSzrNEK6FAG)

点击Edit 进行修改成Space:![](/files/-LfnTGrxdXwMtA_FGCVE)

![](/files/-LfnTGrzupS--cL8uaj8)![](/files/-LfnTGs0rjlBobFUlUwF)

### 2.Eclipse配色方案插件

插件主页：<http://eclipsecolorthemes.org/>

**安装步骤:**

1. 打开: `Help -> Eclipse Marketplace`
2. 查找 **Eclipse Color Theme**，会找到这个插件。
3. 进行安装

**配置:**

打开`Window->Preferences->General->Apperance`,会出现Color Theme,就可选择主题了![](/files/-LfnTGs2bWiERNfrbhwi)

### 3.停止Eclipse里面**DLTKindexing**进程

后台一直在运行一个**DLTK indexing** 的进程，导致Eclipse反应缓慢,直到强制关闭,停止该进程对一般开发者没有多大影响。

解决方案:删除workspace下的以下目录，然后重启eclipse，万事大吉！


# PHP 标准规范及开发技巧


# PHP 标准规范 - PSR

PSR 是 PHP Standard Recommendations 的简写，由[PHP FIG](https://github.com/php-fig)组织制定的 PHP 规范，是 PHP 开发的实践标准。\
![](/files/-LfnTGpiZ08AMoVIgMtZ)

详细请查看[中文译文](https://psr.phphub.org/)

## 资料

[标准注释](http://manual.phpdoc.org/HTMLframesConverter/default/)

[PHP 规范 注释](https://blog.csdn.net/qq_29099209/article/details/80923170)

[PHP标准注释](https://www.cnblogs.com/liangzia/p/6223129.html)


# PHP 开发技巧


# 面向对象编程的基本原则

1. **单一职责原则**：一个类，只需要做好一件事情．
2. **开放封闭原则**：一个类，应该是对扩展是开放的，但是对修改是封闭的；不应该使用修改增加功能，而是通过扩展来增加功能.
3. **依赖倒置原则**：一个类，不应该强依赖另外一个类．每个类对于另外一个类都是可替换的．
   1. 比如有A和B两个类，当A类依赖B类时，A类不能在其中直接调用B类，而是应该使用依赖注入的方式，通过注入将B类对象注入给A类，这样B类对于A类来说就是可以替换的.当新类C类实现了与B类实现了一致的接口类，这样就可以在B类和C类之间切换．
4. **配置化原则**：尽可能地使用配置，而不是硬编码．
5. **面向接口编程原则**：只需要关心接口，不需要关心实现．


# PHP７调试与性能分析


# 调试 - Xdebug安装配置

## Xdebug 安装

* **Ubuntu**

如果您是按照 php7 安装章节中的unbuntu安装方法安装的话,可以直接使用下面的命令

```
sudo apt-get install php-xdebug
```

> 如果是源代码安装,请参考[ Installing Xdebug for PHP7](https://php-built.com/2016/01/20/installing-xdebug-for-php7/)

查看是否安装成功:

```php
<?php
phpinfo();
?>
```

## ![](/files/-LfnTGIAs8ZYud6yGxct)

## Xdebug 基本配置:

```
$ vi /etc/php/7.0/mods-available/xdebug.ini
----------------------------------------
zend_extension=xdebug.so
xdebug.remote_enable=On
xdebug.remote_host="localhost"
xdebug.remote_port=9000
xdebug.remote_handler="dbgp"
```


# 使用Eclipse调试

## 章节

* Eclipse 安装
* Xdebug 安装
* Eclipse 和 Xebug的配置
* The easiest Xdebug 浏览器插件安装
* 配置host的网站,如何配置Xdebug?

## Eclipse安装

详见开发工具章节里的Eclipse 安装与配置

> 教程所使用的Eclipse版本: Eclipse for PHP Developers Version: Neon.3 Release (4.6.3)

![](/files/-LfnTFmVl6elImcWxzOT)

## Xdebug 安装

详见Xdebug章节

## Eclipse 和 Xebug的配置

1\).进行php相关配置: (`Window->Preferences->PHP`）![](/files/-LfnTFmYducbOnMxpoGq)![](/files/-LfnTFm_6-If80g8khgj)

2）配置PHP运行程序（`PHP->PHP Executables->Add`）

> 注意我使用的此版本已经配置好,如果您的Eclipse版本不存在,则Add

![](/files/-LfnTFmcWeUGLBlH9Stc)

填入名字，可随意，建议填写本机安装的PHP版本号，填写php.exe和php.ini的路径，将PHP debugger选为XDebug（Zend Debugger原先是由Zend公司维护的，但现在貌似Zend公司已经将其集成到了自己旗下的一款产品Zend Server里，不再独立维护，因此只支持到PHP5.2.6，不适用于现在的PHP7.0版本了），我的配置见下图：

![](/files/-LfnTFmelZkr2gv7mW1f)

![](/files/-LfnTFmgaQRPKWLsBHlW)

3）配置运行环境: ( `PHP->PHP Executables->Execution Environment->php7.0`），勾选左侧你刚刚配置的PHP运行程序（这里可能要在上一步完成之后先OK一下把配置窗口关掉，再重新打开）。

![](/files/-LfnTFmiMtTFwikDdI1C)

4）选择php版本: ( `PHP->Interpreter`）

![](/files/-LfnTFmk7I4Y6GAIiMWk)

5）配置服务器

![](/files/-LfnTFmmnPljFs6BwxPh)

6）配置xdebug

![](/files/-LfnTFmoIqL4LYwEPQX7)![](/files/-LfnTFmqGDFUhrsbx2ik)![](/files/-LfnTFmsRvNHBSKB8zrr)

配置完成

## The easiest Xdebug 浏览器插件安装

可以去火狐扩展当中搜索安装

![](/files/-LfnTFmu0Aiw-r0Q8qas)

配置

![](/files/-LfnTFmwSKq_wdcA5fKc)

现在就可以完全安装完成,点亮右上角那个小虫图标插件即可进行调试.

## 配置host的网站,如何配置Xdebug?

当您配置自定义解析的本地的网站

```
cat /etc/hosts
-----------
127.0.0.1 test.local
```

点击Ecliplse debug的小虫图标,点击Debug configurations ![](/files/-LfnTFmyVCfuci6Hco-D)添加一个新的PHP Web Application 下面是我的配置

```
/test/public/index.php  为项目的入口文件
```

![](/files/-LfnTFn-E2Kq26HH_T8F)

配置完成后即可在你的网站中打断点, 打开firfox,浏览<http://test.local,点亮右上角的小虫插件,进行调试>

## 资料

[Eclipse集成PDT+XDebug调试PHP脚本](http://pjdong1990.iteye.com/blog/1610305)

[Eclipse 的单步调试](http://www.cnblogs.com/mq0036/p/3780538.html)

[eclipse快捷键调试总结](http://www.cnblogs.com/yxnchinahlj/archive/2012/02/22/2363542.html)

[Eclipse 在Debug调试中用到的快捷键](http://www.cnblogs.com/David-Young/p/4451375.html)


# 性能分析 - Xhprof

[官方网址](http://www.xhprof.com/)

XHProf以php扩展的方式存在

ubuntu快速安装:

```
sudo apt-get install  php5-xhprof
sudo apt-get remove  php5-xhprof
```

目前暂时不支持php7

## 资料

[facebook工具xhprof的安装与使用-分析php执行性能](http://www.cnblogs.com/wangtao_20/p/3320497.html)


# 性能分析 - Vld

## Vld

## 简介

vld是PECL（PHP 扩展和应用仓库）的一个PHP扩展，现在最新版本是 0.14.0（2016-12-18），它的作用是：显示转储PHP脚本（opcode）的内部表示（来自PECL的vld简介）。简单来说，可以查看PHP程序的opcode。

vld(Vulcan Logic Dumper)是一个在Zend引擎中，以挂钩的方式实现的用于输出PHP脚本生成的中间代码（执行单元）的扩展。 它可以在一定程序上查看Zend引擎内部的一些实现原理，是我们学习PHP源码的必备良器。它的作者是Derick Rethans, 除了VLD扩展，我们常用的XDebug扩展的也有该牛人的身影。

pecl地址：<https://pecl.php.net/package/vld>

## 安装

### 方式1：源代码安装：[参考地址](http://www.cnblogs.com/miao-zp/p/6374311.html)

### 方式2：pecl 方式安装

版本：

```
Ubuntu 14.04.3 LTS \n \l
PHP 7.1.5-1
```

安装过程：

最新版本可以去pecl查看，运行一下命令：

```
$ sudo pecl install channel://pecl.php.net/vld-0.14.0
```

安装完成后显示以下结果：

```
Build process completed successfully
Installing '/usr/lib/php/20160303/vld.so'
install ok: channel://pecl.php.net/vld-0.14.0
configuration option "php_ini" is not set to php.ini location
You should add "extension=vld.so" to php.ini
```

添加将扩展加入到php.ini中

```
$ cd /etc/php/7.1/mods-available
$ touch vld.ini     #将划线以下的部分添加到文件中
-------------------
; configuration for php VLD module 
; ; priority=20 
extension=/usr/lib/php/20160303/vld.so
```

做软链接

```
$ cd /etc/php/7.1/cli/conf.d/
$ sudo  ln -s  ../../mods-available/vld.ini 20-vld.ini
$ cd /etc/php/7.1/apache2/conf.d/
$ sudo  ln -s  ../../mods-available/vld.ini 20-vld.ini
```

重启查看phpinfo信息即可

![](/files/-LfnTEJV0R2SMMosBdu7)

## 使用

新建两个php文件,输出一个用 . ，一个用 , 连起来

* 1.php

```php
<?php echo "Hello"." "."world!" ?>
```

* 2.php

```php
<?php echo "Hello"," ","world!" ?>
```

分别开启两个终端，在cli命令行下执行

```
$ php -dvld.active=1 1.php
```

结果：

```
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  (null)
number of ops:  5
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   1     0  E >   EXT_STMT                                                 
         1        ECHO                                                     'Hello+world%21'
         2        EXT_STMT                                                 
         3        ECHO                                                     '++'
         4      > RETURN                                                   1

branch: #  0; line:     1-    1; sop:     0; eop:     4; out1:  -2
path #1: 0, 
Hello world!
```

```
$ php -dvld.active=1 2.php
```

## 结果：

```
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/2.php
function name:  (null)
number of ops:  9
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   1     0  E >   EXT_STMT                                                 
         1        ECHO                                                     'Hello'
         2        EXT_STMT                                                 
         3        ECHO                                                     '+'
         4        EXT_STMT                                                 
         5        ECHO                                                     'world%21'
         6        EXT_STMT                                                 
         7        ECHO                                                     '++'
         8      > RETURN                                                   1

branch: #  0; line:     1-    1; sop:     0; eop:     8; out1:  -2
path #1: 0, 
Hello world!
```

## 参数

```
-dvld.active 是否在执行PHP时激活VLD挂钩，默认为0，表示禁用。可以使用-dvld.active=1启用。
-dvld.skip_prepend 是否跳过php.ini配置文件中auto_prepend_file指定的文件， 默认为0，即不跳过包含的文件，显示这些包含的文件中的代码所生成的中间代码。此参数生效有一个前提条件：-dvld.execute=0
-dvld.skip_append 是否跳过php.ini配置文件中auto_append_file指定的文件， 默认为0，即不跳过包含的文件，显示这些包含的文件中的代码所生成的中间代码。此参数生效有一个前提条件：-dvld.execute=0
-dvld.execute 是否执行这段PHP脚本，默认值为1，表示执行。可以使用-dvld.execute=0，表示只显示中间代码，不执行生成的中间代码。
-dvld.format 是否以自定义的格式显示，默认为0，表示否。可以使用-dvld.format=1，表示以自己定义的格式显示。这里自定义的格式输出是以-dvld.col_sep指定的参数间隔
-dvld.col_sep 在-dvld.format参数启用时此函数才会有效，默认为 “\t”。
-dvld.verbosity 是否显示更详细的信息，默认为1，其值可以为0,1,2,3 其实比0小的也可以，只是效果和0一样，比如0.1之类，但是负数除外，负数和效果和3的效果一样 比3大的值也是可以的，只是效果和3一样。
-dvld.save_dir 指定文件输出的路径，默认路径为/tmp。
-dvld.save_paths 控制是否输出文件，默认为0，表示不输出文件
-dvld.dump_paths 控制输出的内容，现在只有0和1两种情况，默认为1,输出内容
```

## 资料

[PHP性能之语言性能优化：vld——查看代码opcode的神器](http://www.cnblogs.com/miao-zp/p/6374311.html)

[VLD扩展使用指南](http://www.phppan.com/2011/05/vld-extension/)


# 附录\*Ubuntu环境php开发配置

> 注意:Ubuntu安装完成后,没有网络,则需要安装驱动.

## 查看Ubuntu版本号:

方法1:

```
$ cat /etc/issue
Ubuntu 14.04.3 LTS \n \l
```

方法2(详细打印输出):

```
$ sudo lsb_release -a
[sudo] password for revin: 
No LSB modules are available.
Distributor ID:    Ubuntu
Description:    Ubuntu 14.04.3 LTS
Release:    14.04
Codename:    trusty
```

## 更新Ubuntu

```
$ sudo apt-get upgrade
$ sudo apt-get update
```

## 安装git和vim:

```
$ sudo apt-get install git
$ sudo apt-get install vim
```

## 安装java 1.8 :

```
$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:webupd8team/java
$ sudo apt-get update
$ sudo apt-get install oracle-java8-installer
```

## 安装elasticsearch

* 安装

```
$ wget https://download.elastic.co/elasticsearch/elasticsearch/elasticsearch-1.5.2.deb
$ sudo dpkg -i elasticsearch-1.5.2.deb
$ sudo vi /etc/elasticsearch/elasticsearch.yml
```

* 配置

[配置文件详解](http://blog.csdn.net/an74520/article/details/8219814)

```
$ sudo vi /etc/elasticsearch/elasticsearch.yml

# 注意34行,集群的名字，局域网不能重复,开发单机不用管
# cluster.name: elasticsearch-56

# 查看一下启用的配置:

$ grep '^[a-z]'  /etc/elasticsearch/elasticsearch.yml 

# 启动:

$ sudo service elasticsearch start

# 查看9200端口是否启用

$ sudo netstat -nlpt |grep 9200

# 插件安装

$  sudo /usr/share/elasticsearch/bin/plugin -i mobz/elasticsearch-head

#安装插件访问地址:http://localhost:9200/_plugin/head/
```

## 安装Rabbitmq

```
$ sudo apt-get install rabbitmq-server
$ cd /usr/lib/rabbitmq/bin/
$ ll
#开启ui插件
$ ./rabbitmq-plugins enable rabbitmq-management
$ cd ~
$ /usr/lib/rabbitmq/lib/rabbitmq_server-3.2.4/sbin/rabbitmq-plugins list
$ sudo /usr/lib/rabbitmq/lib/rabbitmq_server-3.2.4/sbin/rabbitmq-plugins enable rabbitmq_management
sudo service rabbitmq-server restart
```

## 创建快捷开发目录别名命令:

```
$ cd ~
$ vi .bashrc 
-----------------
alias zf='sudo -u www-data /usr/bin/php /var/www/public/index.php'
alias workc='cd /home/revin/work/code/'

$ source ~/.bashrc
```

## 配置logrotate:

[linux下logrotate 配置和理解](http://blog.csdn.net/cjwid/article/details/1690101)

比如apache,mysql等等日志进行设置

```
$ cd /etc/logrotate.d/
---------------------------------------
/var/www/data/log/* {
        size 100M
        missingok
        rotate 7
        compress
        create 664 www-data www-data
}
```


# 附录\*使用php开发扩展


# 附录\*浏览器插件

## firfox:

* [HttpRequester](https://addons.mozilla.org/zh-CN/firefox/addon/httprequester/?src=search)
* [JSON-formatter](https://addons.mozilla.org/zh-CN/firefox/addon/json-formatter/?src=search)
* [LastPass](https://addons.mozilla.org/zh-CN/firefox/addon/lastpass-password-manager/?src=search)
* [Proxy Authentication](https://addons.mozilla.org/zh-CN/firefox/addon/proxy-authentication/?src=search)
* [The easiest Xdebug](https://addons.mozilla.org/zh-CN/firefox/addon/the-easiest-xdebug/?src=search)

### chrome:


# 附录\*第三方类库

## 第三方类库

* Carbon - PHP中很人性化的时间日期处理插件, [github地址](https://github.com/briannesbitt/Carbon).　[官方地址](http://carbon.nesbot.com/)
* Monolog - PHP的一个功能强大的日志类库．[github地址](https://github.com/Seldaek/monolog) [官方教程地址](https://seldaek.github.io/monolog/)

  资料：[\[PHP 类库\] Monolog - Logging for PHP 5.3+](http://www.tuicool.com/articles/eiIbYjJ)
* swagger-ui - 是一个API在线文档生成和测试的利器 [github地址](https://github.com/xiaoxiami/php-7/tree/05918238c62d4bb23cfc363bf3b996f1907a3d00/swagger-ui/README.md) [官方地址](http://swagger.io/swagger-ui/)
* swagger-php [github地址](https://github.com/zircote/swagger-php)

资料：[Swagger-PHP 自定义生成API](http://blog.csdn.net/wenanshi/article/details/52169630) [Swagger PHP使用指南](http://www.cnblogs.com/derrck/p/5234961.html) [swagger ui教程，API文档生成神器](http://blog.didibird.com/2015/06/23/swagger-ui-tutorials-api-documentation-generation-artifact/)

* auraphp - 强大的应用程序强大的工具。（牛逼）

## PHP 扩展

* &#x20;**php-bcmath** : 将二个高精确度数字相乘。需要安装 php-bcmath扩展

  例如：`echo bcmul('1.34747474747', '35', 3); // 47.161`&#x20;

## 资料

[php composer 流行排行榜](https://packagist.org/explore/popular)

[那些最好的轮子 - PHP篇 php类库](http://www.oschina.net/question/1244136_135347?sort=time)

[guzzle中文文档](http://guzzle-cn.readthedocs.io/)

[phpunit中文文档](https://phpunit.de/manual/6.0/zh_cn/index.html)


# 附录\*小问题整理

## spl\_autoload\_register 函数

[spl\_autoload\_register官方手册](http://www.php.net/manual/zh/function.spl-autoload-register.php)

[为什么要用spl\_autoload\_register来取代\_\_autoload()](https://segmentfault.com/q/1010000000625354)

[spl\_autoload\_register与autoload的区别详解](http://www.jb51.net/article/37746.htm)

## array\_splice函数

[PHP之在数组任意位置插入元素](http://blog.sina.com.cn/s/blog_664c9f650101dwo7.html)


# 附录\*资料\*工具

[《PHP扩展开发及内核应用》](http://www.cunmou.com/phpbook/preface.md) - [github地址](https://github.com/walu/phpbook)

[ PHP源码分析](http://www.phppan.com/php-source-analytics/)

[PHP7内核剖析](https://github.com/pangudashu/php7-internal)

工具

[vim配置文件和插件](https://www.gitbook.com/book/xiaoxiami/php/edit#)


# 附录\*Composer

[composer中文网](http://docs.phpcomposer.com/)

> 注意：Composer 不是一个包管理器。是的，它涉及 "packages" 和 "libraries"，但它在每个项目的基础上进行管理，在你项目的某个目录中（例如`vendor`）进行安装。默认情况下它不会在全局安装任何东西。因此，这仅仅是一个依赖管理。

## 安装

* 局部安装（当前项目安装）：

```
curl -sS https://getcomposer.org/installer | php
```

> 使用命令：`php composer.phar install`

* 全局安装：

```
curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer
```

> 使用命令：`composer install`

## 配置中国镜像

**查看当前的镜像地址：**

```
$ composer config -g repo.packagist
-----------------------------
{"type":"composer","url":"https?:\/\/packagist.org","allow_ssl_downgrade":true} #当前为国外的源
```

启动本镜像服务,一下两种配置

* 系统全局配置
* 单个项目配置

详见：[Packagist 镜像使用方法](https://pkg.phpcomposer.com/)

## 使用

[命令行指令介绍](http://docs.phpcomposer.com/03-cli.html)

* composer初始化

```
$ composer init
# Package name : cms/test
# Description : test composer
# Author : revin
# Minimum Stability : 忽略,直接确定
# Package Type ： project
剩下全部确定
```

composer.json

```javascript
{
    "name": "cms/test",
    "description": "test composer",
    "type": "project",
    "authors": [
        {
            "name": "revin",
            "email": "revin.bian@vidaxl.com"
        }
    ],
    "require": {}
}
```

* 搜索依赖包

```
$ composer search monolog
------------------------------------------
monolog/monolog Sends your logs to files, sockets, inboxes, databases and various web services
kdyby/monolog Integration of Monolog into Nette Framework
adeira/monolog 
stackify/monolog Stackify logs and errors for Monolog
thinframe/monolog Monolog Application
flowpack/monolog Monolog integration for Flow
cakephp/monolog CakePHP Monolog Plugin
mrtnzlml/monolog 
mero/yii2-monolog The Monolog integration for the Yii framework.
amberovsky/zf2-monolog Monolog integration to Zend Framework 2
symfony/monolog-bundle Symfony MonologBundle
symfony/monolog-bridge Symfony Monolog Bridge
theorchard/monolog-cascade Monolog extension to configure multiple loggers in the blink of an eye and access them from anywhere
kamisama/monolog-init Very basic and light Dependency Injector Container for Monolog
logentries/logentries-monolog-handler A handler for Monolog that sends messages to Logentries.com.
```

则monolog/monolog 则为库名称

* 查看库的详细信息

```
$ composer show monolog/monolog
------------------------------------------
name     : monolog/monolog
descrip. : Sends your logs to files, sockets, inboxes, databases and various web services
keywords : log, logging, psr-3
versions : dev-master, 2.0.x-dev, 1.x-dev, 1.22.1, 1.22.0, 1.21.0, 1.20.0, 1.19.0, 1.18.2, 1.18.1, 1.18.0, 1.17.2, 1.17.1, 1.17.0, 1.16.0, 1.15.0, 1.14.0, 1.13.1, 1.13.0, 1.12.0, 1.11.0, 1.10.0, 1.9.1, 1.9.0, 1.8.0, 1.7.0, 1.6.0, 1.5.0, 1.4.1, 1.4.0, 1.3.1, 1.3.0, 1.2.1, 1.2.0, 1.1.0, 1.0.2, 1.0.1, 1.0.0, 1.0.0-RC1
type     : library
license  : MIT License (MIT) (OSI approved) https://spdx.org/licenses/MIT.html#licenseText
source   : [git] https://github.com/Seldaek/monolog.git f8248dba5db8f4e60ec6a505bf43146c6c0466f9
dist     : [zip] https://api.github.com/repos/Seldaek/monolog/zipball/f8248dba5db8f4e60ec6a505bf43146c6c0466f9 f8248dba5db8f4e60ec6a505bf43146c6c0466f9
names    : monolog/monolog, psr/log-implementation

autoload
psr-4
Monolog\ => src/Monolog

requires
php ^7.0
psr/log ^1.0.1

requires (dev)
doctrine/couchdb ~1.0@dev
ruflin/elastica >=0.90 <3.0
php-console/php-console ^3.1.3
php-amqplib/php-amqplib ~2.4
sentry/sentry ^0.13
graylog2/gelf-php ^1.4.2
jakub-onderka/php-parallel-lint ^0.9
predis/predis ^1.1
phpspec/prophecy ^1.6.1
aws/aws-sdk-php ^2.4.9 || ^3.0
phpunit/phpunit ^5.7
swiftmailer/swiftmailer ^5.3|^6.0

suggests
aws/aws-sdk-php Allow sending log messages to AWS services like DynamoDB
doctrine/couchdb Allow sending log messages to a CouchDB server
ext-amqp Allow sending log messages to an AMQP server (1.0+ required)
ext-mongodb Allow sending log messages to a MongoDB server (via driver)
graylog2/gelf-php Allow sending log messages to a GrayLog2 server
mongodb/mongodb Allow sending log messages to a MongoDB server (via library)
php-amqplib/php-amqplib Allow sending log messages to an AMQP server using php-amqplib
php-console/php-console Allow sending log messages to Google Chrome
rollbar/rollbar Allow sending log messages to Rollbar
ruflin/elastica Allow sending log messages to an Elastic Search server
sentry/sentry Allow sending log messages to a Sentry server

provides
psr/log-implementation 1.0.0
```

* 安装：

**声明方式安装：**

1. 修改`composer.json`文件：

```javascript
{
    "name": "cms/test",
    "description": "test composer",
    "type": "project",
    "authors": [
        {
            "name": "revin",
            "email": "revin.bian@vidaxl.com"
        }
    ],
        "require": {
            "monolog/monolog": "1.21.*"
        }
}
```

1. 执行命令`$ composer install`

**require 命令安装**

例子：`composer require symfony/http-foundation`

此时的composer.json

```javascript
{
    "name": "cms/test",
    "description": "test composer",
    "type": "project",
    "authors": [
        {
            "name": "revin",
            "email": "revin.bian@vidaxl.com"
        }
    ],
        "require": {
            "monolog/monolog": "1.21.*",
        "symfony/http-foundation": "^3.3"
        }
}
```

并没有更新之前安装的版本

* 删除包

当删除了`"monolog/monolog": "1.21.*"` 包。则只需要删除此行。执行`composer update 命令。`

> 执行`composer update`时，不会影响到其他的包版本。


# 附录\*前端

## 模板：AdminLTE

* [adminLTE 教程](http://11140372.blog.51cto.com/11130372/1907120)


# 附录\*进程

[php中如何实现多进程](https://www.cnblogs.com/Renyi-Fan/p/10909584.html)

[PHP操作多进程](https://course.blog.csdn.net/article/details/105193559)


# 附录\*PHP的ticks机制

PHP提供declare关键字和ticks关键字来声明ticks机制。如：declare(ticks = N); 这表示：在当前scope内，每执行N句internal statements（opcodes），就会中断当前的业务语句，去执行通过register\_tick\_function注册的函数（如果存在的话），然后再继续之前的代码。需要注意的是这里的N是指的PHP的一些OPCODE，而OPCODE与我们见到的PHP语句却不是一一对应的。

实例1：

```php
    $name = "phppan";
    echo $name;
    class Tipi {
        public function test() {
            echo "test";
        }
    }
    function f_tipi() {
    }
```

如上代码包括了我们常见的几种语句，赋值，输出，定义类，定义函数。通常我们用VLD查看PHP生成的中间代码，上面的代码通过**php -dvld.active=1 t.php**我们会看到 ECHO、ASSIGN、NOP等中间代码，如下所示：

```
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  (null)
number of ops:  7
compiled vars:  !0 = $name
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT                                                 
         1        ASSIGN                                                   !0, 'phppan'
   3     2        EXT_STMT                                                 
         3        ECHO                                                     !0
   4     4        EXT_STMT                                                 
   9     5        EXT_STMT                                                 
  10     6      > RETURN                                                   1

branch: #  0; line:     2-   10; sop:     0; eop:     6; out1:  -2
path #1: 0, 
Function f_tipi:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  f_tipi
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   9     0  E >   EXT_NOP                                                  
  10     1        EXT_STMT                                                 
         2      > RETURN                                                   null

branch: #  0; line:     9-   10; sop:     0; eop:     2; out1:  -2
path #1: 0, 
End of function f_tipi

Class Tipi:
Function test:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  test
number of ops:  5
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   5     0  E >   EXT_NOP                                                  
   6     1        EXT_STMT                                                 
         2        ECHO                                                     'test'
   7     3        EXT_STMT                                                 
         4      > RETURN                                                   null

branch: #  0; line:     5-    7; sop:     0; eop:     4; out1:  -2
path #1: 0, 
End of function test

End of class Tipi.

phppan
```

现在我们在示例1的代码上添加上ticks机制。如PHP代码示例2：

```php
    declare(ticks=1);
    $name = "phppan";
    echo $name;
    class Tipi {
        public function test() {
            echo "test";
        }
    }
    function f_tipi() {
    }
```

示例2与示例1相比也就是多了第一条语句： declare(ticks=1); 如果我们此时再次通过VLD查看中间代码，会发现在每个中间代码的后面都多了一句中间代码：**TICKS**。

```
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  (null)
number of ops:  13
compiled vars:  !0 = $name
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   2     0  E >   EXT_STMT                                                 
         1        TICKS                                                    
   3     2        EXT_STMT                                                 
         3        ASSIGN                                                   !0, 'phppan'
         4        TICKS                                                    
   4     5        EXT_STMT                                                 
         6        ECHO                                                     !0
         7        TICKS                                                    
   5     8        EXT_STMT                                                 
         9        TICKS                                                    
  10    10        EXT_STMT                                                 
  11    11        TICKS                                                    
        12      > RETURN                                                   1

branch: #  0; line:     2-   11; sop:     0; eop:    12; out1:  -2
path #1: 0, 
Function f_tipi:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  f_tipi
number of ops:  3
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
  10     0  E >   EXT_NOP                                                  
  11     1        EXT_STMT                                                 
         2      > RETURN                                                   null

branch: #  0; line:    10-   11; sop:     0; eop:     2; out1:  -2
path #1: 0, 
End of function f_tipi

Class Tipi:
Function test:
Finding entry points
Branch analysis from position: 0
Jump found. (Code = 62) Position 1 = -2
filename:       /home/revin/work/code/test/1.php
function name:  test
number of ops:  6
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   6     0  E >   EXT_NOP                                                  
   7     1        EXT_STMT                                                 
         2        ECHO                                                     'test'
         3        TICKS                                                    
   8     4        EXT_STMT                                                 
         5      > RETURN                                                   null

branch: #  0; line:     6-    8; sop:     0; eop:     5; out1:  -2
path #1: 0, 
End of function test

End of class Tipi.

phppan
```

是否因为ticks=1的原因而在每个中间代码的后面添加了TICKS？将declare(ticks=1);换成declare(ticks=100);，再次VLD，结果没有变化。从以上的结果可以看出，PHP内核在语法分析过程中实现了ticks机制。

## 声明ticks机制过程

声明的过程就是调用declare(ticks = N); 在语法分析时根据declare关键字和参数中的ticks关键字来声明ticks机制。通过zend\_compile.c文件中的zend\_do\_declare\_begin、declare\_statement、zend\_do\_declare\_end三个函数来编译声明ticks机制。在declare\_statement函数中我们可以看到：declare除了可以声明ticks之外，还可以声明encoding，这在代码里面就是一个if else的判断。

ticks机制的声明仅在编译过程有用，它为后面的声明控制语句服务。其编译过程中的全局变量为：CG(declarables)。这是一个结构体，它仅有一个成员：ticks。当然后面应该还会有更多的成员出现。

## 声明控制语句

示例1和示例2已经充分说明在每条语句的语法分析时，会根据是否声明了ticks机制来添加TICKS中间代码，其实现在于每条语句在语法解析时都会添加一条函数调用：zend\_do\_ticks。从zend\_language\_parser.y文件中可以看出：zend\_do\_ticks函数添加在类定义语句，函数定义语句和常规语句的后面。 zend\_compile.c文件中的zend\_do\_ticks函数会根据前面提到的 CG(declarables).ticks 来判断是否生成 ZEND\_TICKS 中间代码（在VLD中看到的中间代码都是没有ZEND开头）。

除了声明ticks机制，还有执行。执行过程中关键的变量是在声明时的ticks=N。其实这里的N可以换个角度去理解：ticks指定的数字是指执行了多少次TICKS语句。在TICKS中间代码的执行函数ZEND\_TICKS\_SPEC\_CONST\_HANDLER中，会统计执行当前函数的次数，存储变量为EG(ticks\_count)。当达到当初声明的界限，就会调用一次所有通过register\_tick\_function注册的函数，并计数清零。

与当初自己设想的实现相比，PHP内核对ticks机制的实现满足了功能单一原则和松耦合原则。将ticks机制作为一个中间代码添加到整个中间代码的执行体系中，包括状态的转移，函数的切换这些都是直接使用原有的机制。

## ticks机制的应用场景

手册上说：Ticks 很适合用来做调试，以及实现简单的多任务，后台 I/O 和很多其它任务。

在调试过程中，对于定位一段特定代码中速度慢的语句比较有用，我们可以每执行两条低级语句就记录一次时间。虽然这个过程也可以用其它方法完成，但用 tick 更方便也更容易实现。

PCNTL也使用ticks机制来作为信号处理机制（signal handle callback mechanism），可以最小程度地降低处理异步事件时的负载。这里的关键在于PCNTL扩展的模块初始化函数（PHP\_MINIT\_FUNCTION(pcntl)）。在此模块做模块初始化时，它会调用： php\_add\_tick\_function(pcntl\_signal\_dispatch);将pcntl的分发执行函数添加到ticks机制的调用函数中去，从而当ticks触发时就会调用PCNTL扩展函数中指定的所有方法。

## 资料

[PHP的ticks机制](http://www.codesky.net/article/201306/181874.html)

[php手册里，pcntl\_signal函数解释，很多例子前面declare(ticks=1)这句话的意义](http://blog.chinaunix.net/uid-26363964-id-3938401.html)

[你知道PHP信号处理的正确打开方式吗？](http://www.jianshu.com/p/c3fb535ecd8b)

[PHP官方的pcntl\_signal性能极差](http://rango.swoole.com/archives/364)


# 附录\* 通过composer发布自己的包

前提：

已经学会了composer的基础用法，知道composer.json的作用，知道install和update命令的作用。\
你会使用git，并在github上有一个账号。

基本流程：

1.在github上创建自己的项目，例如：helloworld

2.将项目通过git克隆到本地，创建composer.json

3.commit并push到github上

4.到<https://packagist.org/> 上点击右上角"submit package"，需要登录，点击"login with github"使用github账号登录即可，初次登录会让你登记邮箱，完了再次点击"submit package"。

5.填写项目地址"Repository URL"，这个url就是你github上helloworld项目的url。

6.点击"check"按钮，系统自动检测你的项目中composer.json是否合格，并给出原因。如果没有错误的话，请点击提交。

7.包创建成功，可以根据提示继续配置github自动同步功能，这样每次push后，packagist对应包的版本号也会更新。

8.修改包并更新，修改后git push，然后到使用该包的项目中执行composer --dev --prefer-source update \[包名] ，加--prefer-source意思是从github上检出最新版本。


# 附录\*字符编码问题

[php\_字符编码浅谈\_积累中。。。](https://www.cnblogs.com/gaoshicai/archive/2012/06/14/2548976.html)

[php 编码转换 乱码解决](https://blog.csdn.net/u013372487/article/details/52528535/)


# 附录\*注释

官方文档地址:<http://manual.phpdoc.org/HTMLframesConverter/default/>

[关于PHPDocument 代码注释规范的总结](https://www.jb51.net/article/39063.htm)


