PHP 7 新特性

作者: 斐波那契额 | 来源:发表于2018-02-24 09:49 被阅读73次

    变量类型:

    类型提示
    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) {
        // ...
    }
    

    相关文章

      网友评论

        本文标题:PHP 7 新特性

        本文链接:https://www.haomeiwen.com/subject/xkfqxftx.html