PHP 7 新特性

变量类型:

类型提示
PHP 版本函数的参数和返回值增加了类型限定. 为什么 PHP 要加入类型, 实际上此项特性是为了 PHP 7.1 版本的 JIT 特性做准备, 增加类型后 PHP JIT 可以准确判断类型, 生成最佳的机器码
function test(int $a, string $b, array $c) : int 
{
    // do something
}

PHP 5.0 首次提出函数参数(只针对对象类型)的类型提示(Type Hint),之后 PHP 5.1 进一步扩展到可针对数组类型。

<?php
class Person
{
    public $name;
    public $id;
    function __construct($name, $id) {
        $this->name = $name;
        $this->id = $id;
    }
}

$person = new Person("Tom", 101);

function printPerson(Person $person) {  // 对象类型参数提示
    echo "Name: ", $person->name, ", ID: ", $person->id, ".";
}

printPerson($person);

$skills = ["PHP 7", "C++", "Java", "Golang", "Python"];  // PHP 5.4 起可以使用数组定义短语法

function printSkills(array $skills) {   // 数组类型参数提示
    foreach ($skills as $skill) {
        echo $skill, "\n";
    }
}

printSkills($skills);

PHP 7 增加了对标量参数类型的支持:

  • int
  • float
  • string
  • bool
function printRecord(string $name, int $id, float $salary, bool $sex) {
    echo $sex ? "$name, $id, $salary, male." : "$name, $id, $salary, female.";
}

printRecord("Tom", 101, 5650.00, TRUE);     // Tom, 101, 5650, male.
printRecord("Suzy", 101, 5650.00, FALSE);   // Suzy, 101, 5650, female.

PHP 7 的函数/方法返回值类型提示:PHP 7还支持了函数/方法返回值类型提示

function getRecord(string $name, int $id, float $salary, bool $sex) : string { // 返回值定义类型为字符串
    return $sex ? "$name, $id, $salary, male." : "$name, $id, $salary, female.";
}

getRecord("Tom", 101, 5650.00, TRUE);

// return  "Tom, 101, 5650, male.";

return的返回类型提示,跟 PHPDoc 的 @return 注解是完全不同的两个方面,@return 只是“好言规劝”或向IDE“友好反馈”返回类型应该是什么,而对于实际的返回类型不具约束力。return的返回类型提示则具有对返回类型的运行时强制约束力

类型提示涉及父类继承或接口实现时
interface iFoo {}
class Foo implements iFoo {}
class Bar extends Foo {}

function coo(iFoo $foo) : iFoo {
    return $foo;
}

coo(new Foo());
coo(new Bar());

function gaa(Foo $foo) : iFoo {
    return $foo;
}

gaa(new Foo());
gaa(new Bar());

function zii(Bar $bar) : Foo {
    return $bar;
}

zii(new Foo());  // TypeError: Argument 1 passed to zii() must be an instance of Bar, instance of Foo given on line 1
zii(new Bar());
严格类型约束
function xii(array $a, string $s) : int {
    print_r($a);
    echo $s, "\n";
    return "101";
}

xii([1, 2, 3, 4, 5, 6, 7, 8], 101);
xii(101, 102);  // TypeError: Argument 1 passed to xii() must be of the type array, integer given on line 1

对于标量类型提示,我们的参数也罢、返回值也罢,其类型跟类型提示不一致也不影响程序运行(注:对象及数组类型具备约束力,注意区别)。这可能不是我们想要的。解决办法就是在 PHP 脚本文件的第一条语句的位置放上:declare(strict_types=1);。这是个文件级别的指令,同时不影响其他包含文件——主要是考虑向后兼容及不影响各类扩展、内建代码。

PHP7 新增的生成器特性:

生成器委托(Generator Delegation)
<?php
declare(strict_types=1);

$seh_seh_liām = function () {
    $generator = function () {
        yield from range(1, 3);

        foreach (range(4, 6) as $i) {
            yield $i;
        }
    };

    foreach ($generator() as $value) {
        echo "每天念 PHP 是最好的编程语言 6 遍...第 $value 遍...", PHP_EOL;
    }
};

$seh_seh_liām();
生成器返回表达式(Generator Return Expression)

生成器返回表达式(Generator Return Expression)为生成器函数提供了增强内力,在 PHP 7 之前是无法在生成器函数内返回值的。

<?php
$traverser = (function () {
  yield "foo";
  yield "bar";
  return "value";
})();

$traverser->getReturn();

foreach ($traverser as $value) {
    echo "{$value}", PHP_EOL;
}

$traverser->getReturn();  // "value"
生成器与Coroutine
<?php
declare(strict_types=1);

class Coroutine
{
    public static function create(callable $callback) : Generator
    {
        return (function () use ($callback) {
            try {
                yield $callback;
            } catch (Exception $e) {
                echo "OH.. an error, but don't care and continue...", PHP_EOL;
            }
       })();
    }

    public static function run(array $cos)
    {
        $cnt = count($cos);
        while ($cnt > 0) {
            $loc = random_int(0, $cnt-1);  // 用 random 模拟调度策略。
            $cos[$loc]->current()();
            array_splice($cos, $loc, 1);
            $cnt--;
        }
    }
}

$co = new Coroutine();

$cos = [];
for ($i = 1; $i <= 10; $i++) {
    $cos[] = $co::create(function () use ($i) { echo "Co.{$i}.", PHP_EOL; });
}
$co::run($cos);

$cos = [];
for ($i = 1; $i <= 20; $i++) {
    $cos[] = $co::create(function () use ($i) { echo "Co.{$i}.", PHP_EOL; });
}
$co::run($cos);

新的写法:

空合并操作符(Null Coalesce Operator)
$name = $name ?? "NoName";  // 如果$name有值就取其值,否则设$name成"NoName"
飞船操作符(Spaceship Operator)

形式:(expr) <=> (expr)

左边运算对象小,则返回-1;左、右两边运算对象相等,则返回0;左边运算对象大,则返回1。

$name = ["Simen", "Suzy", "Cook", "Stella"];
usort($name, function ($left, $right) {
    return $left <=> $right;
});
print_r($name);
常量数组(Constant Array)

PHP 7 之前只允许类/接口中使用常量数组,现在 PHP 7 也支持非类/接口的普通常量数组了。

define("USER", [
  "name"  => "Simen",
  "sex"   => "Male",
  "age"   => "38",
  "skill" => ["PHP", "MySQL", "C"]
]);
// USER["skill"][2] = "C/C++";  // PHP Fatal error:  Cannot use temporary expression in write context in...
统一了变量语法
$goo = [
    "bar" => [
        "baz" => 100,
        "cug" => 900
    ]
];

$foo = "goo";

$$foo["bar"]["baz"];  // 实际为:($$foo)['bar']['baz']; PHP 5 中为:${$foo['bar']['baz']};
                      // PHP 7 中一个笼统的判定规则是,由左向右结合。
Throwable 接口

这是 PHP 7 引进的一个值得期待的新特性,将极大增强 PHP 错误处理能力。PHP 5 的 try … catch … finally 无法处理传统错误,如果需要,你通常会考虑用 set_error_handler() 来 Hack 一下。但是仍有很多错误类型是 set_error_handler() 捕捉不到的。PHP 7引入 Throwable 接口,错误及异常都实现了 Throwable,无法直接实现 Throwable,但可以扩展 \Exception 和 \Error 类。可以用 Throwable 捕捉异常跟错误。\Exception 是所有PHP及用户异常的基类;\Error 是所有内部PHP错误的基类。

$name = "Tony";
try {
    $name = $name->method();
} catch (\Error $e) {
    echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
}

try {
    $name = $name->method();
} catch (\Throwable $e) {
    echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
}

try {
    intdiv(5, 0);
} catch (\DivisionByZeroError $e) {
    echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
}
use 组合声明

use 组合声明可以减少 use 的输入冗余。

use PHPGoodTaste\Utils\{
    Util,
    Form,
    Form\Validation,
    Form\Binding
};
一次捕捉多种类型的异常 / 错误

PHP 7.1 新添加了捕获多种异常/错误类型的语法——通过竖杠“|”来实现。

try {
      throw new LengthException("LengthException");
    //   throw new DivisionByZeroError("DivisionByZeroError");
    //   throw new Exception("Exception");
} catch (\DivisionByZeroError | \LengthException $e) {
    echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
} catch (\Exception $e) {
    echo "出错消息 --- ", $e->getMessage(), PHP_EOL;
} finally {
    // ...
}
可见性修饰符的变化

PHP 7.1 之前的类常量是不允许添加可见性修饰符的,此时类常量可见性相当于 public。PHP 7.1 为类常量添加了可见性修饰符支持特性。总的来说,可见性修饰符使用范围如下所示:

函数/方法:public、private、protected、abstract、final

  • 类:abstract、final
  • 属性/变量:public、private、protected
  • 类常量:public、private、protected
class YourClass 
{
    const THE_OLD_STYLE_CONST = "One";

    public const THE_PUBLIC_CONST = "Two";
    private const THE_PRIVATE_CONST = "Three";
    protected const THE_PROTECTED_CONST = "Four";
}
iterable 伪类型

PHP 7.1 引入了 iterable 伪类型。iterable 类型适用于数组、生成器以及实现了 Traversable 的对象,它是 PHP 中保留类名。

$fn = function (iterable $it) : iterable {
    $result = [];
    foreach ($it as $value) {
        $result[] = $value + 1000;
    }
    return $result;
};

$fn([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
可空类型(Nullable Type)

PHP 7.1 引入了可空类型。看看新兴的 Kotlin 编程语言的一个噱头特性就是可空类型。PHP 越来越像“强类型语言了”。对于同一类型的强制要求,可以设置其是否可空。

$fn = function (?int $in) {     //  ? 的形式表明函数参数或者返回值的类型要么为指定类型
    return $in ?? "NULL";
};

$fn(null);
$fn(5);
$fn();  // TypeError: Too few arguments to function {closure}(), 0 passed in ...

此方法也可用于接口函数的定义:

interface Fooable {
    function foo(?Fooable $f);
}

但有一个需要注意的地方:如果函数本身定义了参数类型并且没有默认值,即使是可空的,也不能省略,否则会触发错误

function foo_nullable(?Bar $bar) {}

foo_nullable(new Bar); // 可行
foo_nullable(null); // 可行
foo_nullable(); // 不可行
Void 返回类型

PHP 7.1 引入了 Void 返回类型。

function first(): void {
    // ...
}

function second(): void {
    // ...
    return;
}
list 的方括号简写

我们知道在 PHP5.4 之前只能通过 array() 来定义数组,5.4之后添加了 [] 的简化写法

// 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 来实现:

$array = ['a', 'b', 'c'];

list($a, $b, $c) = $array;  // list() 仅能用于数字索引的数组,并假定数字索引从 0 开始。

最新的变化:

// 通过 [] 的简写
[$a, $b, $c] = $array;

// list 指定 key
["a" => $a, "b" => $b, "c" => $c] = $array;

PHP7.1 实现了这个特性。但是要注意的是:出现在左值中的 [] 并不是数组的简写,是 list() 的简写。
但是并不仅仅如此,新的 list() 的实现并不仅仅可以出现在左值中,也能在 foreach 循环中使用:

foreach ($points as ["x" => $x, "y" => $y]) {
    var_dump($x, $y);
}

list() 和 [] 不能相互嵌套使用

// 不合法
list([$a, $b], [$c, $d]) = [[1, 2], [3, 4]];

// 不合法
[list($a, $b), list($c, $d)] = [[1, 2], [3, 4]];

// 合法
[[$a, $b], [$c, $d]] = [[1, 2], [3, 4]];
允许在 list 中指定 key
$array = ["a" => 1, "b" => 2, "c" => 3];
["a" => $a, "b" => $b, "c" => $c] = $array;

以上代码相当于:

$a = $array['a'];
$b = $array['b'];
$c = $array['c'];

和以往的区别在于以往的 list() 的实现相当于 key 只能是 0, 1, 2, 3 的数字形式并且不能调整顺序。执行以下语句:

list($a, $b) = [1 => '1', 2 => '2'];

会得到 PHP error: Undefined offset: 0... 的错误。

而新的实现则可以通过以下方式来调整赋值:

list(1 => $a, 2 => $b) = [1 => '1', 2 => '2'];

不同于数组的是,list 并不支持混合形式的 key,以下写法会触发解析错误

// Parse error: syntax error, ...
list($unkeyed, "key" => $keyed) = $array;

更复杂的情况,list 也支持复合形式的解析:

$points = [
    ["x" => 1, "y" => 2],
    ["x" => 2, "y" => 1]
];

list(list("x" => $x1, "y" => $y1), list("x" => $x2, "y" => $y2)) = $points;

$points = [
    "first" => [1, 2],
    "second" => [2, 1]
];

list("first" => list($x1, $y1), "second" => list($x2, $y2)) = $points;

以及循环中使用:

$points = [
    ["x" => 1, "y" => 2],
    ["x" => 2, "y" => 1]
];

foreach ($points as list("x" => $x, "y" => $y)) {
    echo "Point at ($x, $y)", PHP_EOL;
}

多条件 catch

在以往的 try … catch 语句中,每个 catch 只能设定一个条件判断:

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

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

try {
    // Some code...
} catch (ExceptionType1 | ExceptionType2 $e) {
    // 对于 ExceptionType1 和 ExceptionType2 的处理
} catch (\Exception $e) {
    // ...
}
    原文作者:斐波那契额
    原文地址: https://www.jianshu.com/p/c0277f98ea0b
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞