PHP 新特性

1、类型声明

1.1、标量类型声明

标量类型声明可以使用下列类型参数:

类型 中文 版本
class 类名 PHP5.0
interface 接口 PHP5.0
array 数组 PHP5.1
callable 回调 PHP5.4
string 字符串 PHP7.0
int 整数 PHP7.0
float 浮点数 PHP7.0
bool 布尔值 PHP7.0

在非严格模式下,bool 和 int 参数类型的可有容错性。也就是说,bool 类型可以传字符串类型(string)、数字型(整型[int]、浮点型[float]),返回结果都是布尔值(true|false)。int 类型可以传浮点型(float),返回结果为整型(int)。
在严格模式下(strict_types=1),传入其他类型会报 Fatal error: 错误,看下面的小例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
declare(strict_types=1);
class Persion
{
public function age(int $age)
{
return $age;
}

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

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

$persion = new Persion();

echo $persion->name('lsc');
echo "\n";
echo $persion->age(454.3);
echo "\n";
var_dump($persion->isAlive('lsc'));
echo "\n";

非严格模式下调用,正确输出结果:

lsc
454
bool(false)

严格模式下调用,结果会报错:

lsc

Fatal error: Uncaught TypeError: Argument 1 passed to Persion::age() must be of the type integer, float given, called in /Users/liushuaicai/Desktop/PHP/test.php on line 25 and defined in /Users/liushuaicai/Desktop/PHP/test.php:5
Stack trace:
#0 /Users/liushuaicai/Desktop/PHP/test.php(25): Persion->age(454.3)
#1 {main}
thrown in /Users/liushuaicai/Desktop/PHP/test.php on line 5

1.2、返回值类型声明

PHP7 增加返回类型声明,对返回的数据类型进行限制。可用的类型与参数声明中可用的类型相同。例如:

1
2
3
4
5
6
7
8
function arraySum(array ...$arrays): array
{
return array_map(function(array $array) {
return array_sum($array);
}, $arrays);
}

print_r(arraySum([1,2,3], [4,5,6], [7,8,9]));

对数组求和,返回数组类型,输出结果:

Array
(
    [0] => 6
    [1] => 15
    [2] => 24
)

2、Closure的call方法

Closure 类在平常的工作中也没有用过,在这里也学习一下,先来看一下新方法 call()。Closure::call() 现在有着更好的性能,简短干练的暂时绑定一个方法到对象上闭包并调用它。来看一个例子,来对比一下 bindTo 和 call 方法。

1
2
3
4
5
6
7
8
9
10
11
class A {private $x = 1;}

// PHP7 之前版本代码
$getXCB = function() {return $this->x;};
$getX = $getXCB->bindTo(new A, 'A'); // 中间层闭包
echo 'PHP7 之前版本代码:'.$getX();
echo "\n";

// PHP7+ 版本代码
$getX = function() {return $this->x;};
echo 'PHP7+ 版本代码:'.$getX->call(new A);

输出结果:

PHP7 之前版本代码:1
PHP7+ 版本代码:1

3、匿名类

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
interface Logger
{
public function log(string $msg);
}

class Application
{
private $logger;

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


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

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

var_dump($app->getLogger());

打印结果是 Logger 对象:

object(class@anonymous)#3 (0) {
}

这种方法看起来挺方便的,不过在工作中还真没这么用过,一时半会也想不起在什么具体场景下使用。

4、太空船操作符

太空船操作符(<=>)在比较变量时非常有用,变量可以是数值(字符串型、整型、浮点型)、数组、对象。可以用于书写清晰易读的用于 usort、uasort、uksort 的回调函数,具体使用规则如下:

  • 当符号两边相等时返回0
  • 当符号右边大于左边时返回-1
  • 当符号左边大于右边时返回1
    例如下面的例子:
    1
    2
    3
    4
    5
    6
    7
    8
    $int1 = 1;
    $int2 = 2;
    $int3 = 1;
    echo '$int1 <=> $int3 = '. ($int1 <=> $int3);
    echo "\n";
    echo '$int1 <=> $int2 = '. ($int1 <=> $int2);
    echo "\n";
    echo '$int2 <=> $int3 = '. ($int2 <=> $int3);

结果为:

$int1 <=> $int3 = 0
$int1 <=> $int2 = -1
$int2 <=> $int3 = 1

这个操作符在进行数组排序时是非常有用的,看个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?php
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, 67, 56, 34, 98, 45];
$spaceArray = [1, 67, 56, 34, 98, 45];

// 对数组进行升序排序
usort($normalArray, 'normal_sort');
print_r($normalArray);

usort($spaceArray, 'space_sort');
print_r($spaceArray);

对两个相同的数组,分别用了两个方法进行了排序,结果是一样的,不过 space_sort 方法的代码更为简洁。

5、null 合并运算符

我们经常使用三元运算符来实现 if-else 的功能,例如:

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

如果 $_POST$_POST['title'] 不存在时,就会抛出 Notice: Undefined index: 的错误。为了解决这个问题,一般采用 islet() 函数,例如:

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

这样就可以解决报错,不过很麻烦,PHP7 推荐使用合并运算符(??),在第一个操作存在时可直接被返回,不然返回第二个操作,具体如下:

1
$post = $_POST['title'] ?? NULL;

它会检查 $_POST['title'] 是否存在,如果存在返回 $_POST['title'],不存在返回 NULL。
合并运算符还有另外一个好处是可以连续使用,相当于多个判读 if - elseif - else:

1
$post = $_POST['title'] ?? $_GET['title'] ?? NULL;

如果 POST 不存在,返回 GET,GET 不存在,返回 NULL。相当于下面的代码:

1
2
3
4
5
6
if (isset($_POST['title']))
$title = $_POST['title'];
elseif (isset($_GET['title']))
$title = $_GET['title'];
else
$title = NULL;

通过上面的例子可以看到,合并运算符代码简短、方便、容易理解。

6、统一语法变量

有时候会遇到这样的情况:方法、变量、类名等会被保存在某个变量里,例如下面的例子:

1
$object['class']->name;

在上面的代码中, $object[‘class’] 会先被解析,之后的 name 属性会再被解析,通常由左到右解析。看一下下面的情况:

1
2
3
4
5
error_reporting(E_ALL);
$first = ['name' => 'second'];
$second = 'Howdy';

echo $$first['name'];

在 PHP5.x 中,这样写没问题,这段代码会被执行,并且会被输出 Howdy。不过这样很容易造成混淆,解析的顺序发生了改变(不是从左到右的顺序)。
为了避免这种不一致性,PHP7 引入了统一变量的语法,用 {} 包括来区分解析的前后,例如:

1
echo ${$first['name']};

如果不使用这种写法,上面的代码不会有任何的输出,并且会有 Notice 级别的错误:

Notice: Array to string conversion in /Users/liushuaicai/Desktop/PHP/test.php on line 34

Notice: Undefined variable: Array in /Users/liushuaicai/Desktop/PHP/test.php on line 34 

7、其它新特性和变更

7.1、常量数组

从 PHP5.6 开始,常量数组可以使用 const 关键字来声明,方法如下:

1
const STORES = ['en', 'fr', 'ar'];

在 PHP7 中,常量数组可以通过 define 函数来初始化,例如:

1
define('STORES', ['en', 'fr', 'ar']);

7.2、Switch 中的多个 default 默认值

在 PHP7 之前,多个 default 默认值在 switch 语句中是被允许的,例如:

1
2
3
4
5
6
7
8
switch(true)
{
default:
echo 'I am first one';
break;
default:
echo 'I am second one';
}

但从 PHP7 开始,这样的代码就会产生 Fatal 级别的错误,错误内容如下:

Fatal error: Switch statements may only contain one default clause in /Users/liushuaicai/Desktop/PHP/test.php on line 35

7.3、session_start 函数中的选项数组

在 PHP7 之前,使用 session 时,必须先调用 session_start() 函数。这个函数并没有传递参数,所有关于 session 的相关配置都在 php.ini 文件中。
从 PHP7 开始,可以在调用 session_start() 函数时,可以把 session 相关配置以数组形式传入函数,这些设置信息将覆盖 php.ini 中的 session 配置,例如:

1
2
3
4
session_start([
'cookie_lifetime' => 3600,
'read_and_close' => true
]);

7.4、unserialize 函数引入过滤器

通常我们使用 serialize 和 unserialize 两个方法分别对对象进行「序列化」和「反序列化」操作。然而,unserialize() 函数并不安全,因为他没有任何过滤项,并且可以反序列化任何类型的对象。因此,PHP7 在该函数中引入了过滤器,默认情况下允许反序列化所有类型的对象。使用方法如下:

1
2
3
4
$result = unserialize(
$object,
['allowed_classes' => ['ClassName1', 'ClassName2', 'ClassName3']]
);