PHP版本
PHP8.0.x版本特性
随着2020年11月26日开发者峰会的结束,php开发团队也宣布 PHP 8 正式发布。PHP8作为PHP语言的一个主版本更新,带来了相当多的新功能和优化项包括命名参数、联合类型、注解、构造器属性提升、match 表达式、nullsafe 运算符、JIT,并改进了类型系统、错误处理、语法一致性等,其中大部分内容都与安全和性能有关。 PHP作为一门已经存在了26年的编程语言,可以说是比较长寿的语言了。但是作为一门长寿的语言,他有些与时代脱节了。在运行效率上,不如C语言java语言这样的编译型语言。在安全性上因为使用的人数比较多,所以被发现漏洞的几率也就越多。同时因为语法宽松,发现的漏洞也就更多了,最近版本的PHP7就曾爆出过重大漏洞,如果对安全有较高要求的话,PHP将不再是首选的语言。鉴于这些原因,PHP8的优化方向主要是性能和安全。 # 新特性 #### 1. 命名参数 PHP 8.0.0 开始引入了命名参数作为现有位置参数的扩展。命名参数允许根据参数名而不是参数位置向函数传参。这使得参数的含义自成体系,参数与顺序无关,并允许任意跳过默认值。 命名参数通过在参数名前加上冒号来传递。允许使用保留关键字作为参数名。参数名必须是一个标识符,不允许动态指定。 ```php <?php myFunction(paramName: $value); array_foobar(array: $value); // 不支持。 function_name($variableStoringParamName: $value); // 使用顺序传递参数: array_fill(0, 100, 50); // 使用命名参数: array_fill(start_index: 0, count: 100, value: 50); //指定参数的传递顺序并不重要 array_fill(value: 50, count: 100, start_index: 0); //传递多个相同参数将会导致抛出 Error foo(param: 1, param: 2); ``` #### 2. 注解(Attributes) 注解功能提供了代码中的声明部分都可以添加结构化、机器可读的元数据的能力, 注解的目标可以是类、方法、函数、参数、属性、类常量。 通过 反射 API 可在运行时获取注解所定义的元数据。 因此注解可以成为直接嵌入代码的配置式语言。 通过注解的使用,在应用中实现功能、使用功能可以相互解耦。 某种程度上讲,它可以和接口(interface)与其实现(implementation)相比较。 但接口与实现是代码相关的,注解则与声明额外信息和配置相关。 接口可以通过类来实现,而注解也可以声明到方法、函数、参数、属性、类常量中。 因此它们比接口更灵活。 - 在PHP7中,注解的写法为: ```php class PostsController{ /** * @Route("/api/posts/{id}", methods={"GET"}) */ public function get($id) { /* ... */ } } ``` - 在PHP8中,注解写法优化为: ```php class PostsController{ #[Route("/api/posts/{id}", methods: ["GET"])] public function get($id) { /* ... */ } } ``` #### 3. 构造器属性提升(Constructor Property Promotion) PHP 8.0.0 起,构造器的参数也可以相应提升为类的属性。 构造器的参数赋值给类属性的行为很普遍,否则无法操作。 而构造器提升的功能则为这种场景提供了便利。 当构造器参数带访问控制(visibility modifier)时,PHP 会同时把它当作对象属性和构造器参数, 并赋值到属性。 构造器可以是空的,或者包含其他语句。 参数值赋值到相应属性后执行正文中额外的代码语句。 并非所有参数都需要提升。可以混合提升或不提升参数作为属性,也不需要按顺序。 提升后的参数不影响构造器内代码调用。 放在构造器提升参数里的属性会同时复制为属性和参数。 - 在PHP7中,构造器的写法为: ```php class Point { public float $x; public float $y; public float $z; public function __construct( float $x = 0.0, float $y = 0.0, float $z = 0.0 ) { $this->x = $x; $this->y = $y; $this->z = $z; } } ``` - 在PHP8中,构造器的写法优化为: ```php class Point { public function __construct( public float $x = 0.0, public float $y = 0.0, public float $z = 0.0, ) {} } ``` #### 4. 联合类型 >警告:在一个联合类型中不能同时用两个 literal 类型 false 和 true。而是使用 bool 替代。 >警告:在 PHP 8.2.0 之前,由于 false 和 null 不能作为独立的类型使用,因此不允许仅由这些类型组成联合类型。这还包括以下类型:false、false|null, 和 ?false。 - 在PHP7中,联合类型的写法为: ```php class Number { private $number; public function __construct($number) { $this->number = $number; } } new Number('NaN'); // Ok ``` - 在PHP8中,联合类型的写法优化为: ```php class Number { public function __construct( private int|float $number ) {} } new Number('NaN'); // TypeError ``` #### 5. Match 表达式 match 表达式基于值的一致性进行分支计算。 match表达式和 switch 语句类似, 都有一个表达式主体,可以和多个可选项进行比较。但是有以下关键区别: - match 比较分支值,使用了严格比较 (===), 而 switch 语句使用了松散比较。 - match 表达式会返回一个值。 - match 的分支不会像 switch 语句一样, 落空时执行下个 case。 - match 表达式必须彻底列举所有情况。 ```php <?php $food = 'cake'; $return_value = match ($food) { 'apple' => 'This food is an apple', 'bar' => 'This food is a bar', 'cake' => 'This food is a cake', }; var_dump($return_value);//string(19) "This food is a cake" ``` match 表达式和 switch 语句类似, 逐个检测匹配分支。一开始不会执行代码。 只有在所有之前的条件不匹配主体表达式时,才会执行剩下的条件表达式。 只会执行返回的表达式所对应的匹配条件表达式。 举例: ```php <?php $result = match ($x) { foo() => ..., $this->bar() => ..., // 如果 foo() === $x,不会执行 $this->bar() $this->baz => beep(), // 只有 $x === $this->baz 时才会执行 beep() // 等等 }; ``` match 表达式分支可以通过逗号分隔,包含多个表达式。 这是一个逻辑 OR,当多个分支表达式右侧相同时,就可以用这种缩写。 ```php <?php $result = match ($x) { // 匹配分支: $a, $b, $c => 5, // 等同于以下三个分支: $a => 5, $b => 5, $c => 5, }; ``` default 模式是个特殊的条件。 当之前的条件都不匹配时,会匹配到该模式。例子: ```php <?php $expressionResult = match ($condition) { 1, 2 => foo(), 3, 4 => bar(), default => baz(), }; ``` match 表达式必须详尽列出所有情况。 如果主体表达式不能被任意分支条件处理, 会抛出 UnhandledMatchError。 ```php <?php $condition = 5; try { match ($condition) { 1, 2 => foo(), 3, 4 => bar(), }; } catch (\UnhandledMatchError $e) { var_dump($e); } ``` 可以使用 match 表达式将 true 作为主项表达式来处理非一致性条件的情况 ```php <?php $age = 23; $result = match (true) { $age >= 65 => 'senior', $age >= 25 => 'adult', $age >= 18 => 'young adult', default => 'kid', }; $text = 'Bienvenue chez nous'; $result = match (true) { str_contains($text, 'Welcome') || str_contains($text, 'Hello') => 'en', str_contains($text, 'Bienvenue') || str_contains($text, 'Bonjour') => 'fr', // ... }; ``` #### 6. Nullsafe 运算符 自 PHP 8.0.0 起,类属性和方法可以通过 "nullsafe" 操作符访问: ?->。 除了一处不同,nullsafe 操作符和以上原来的属性、方法访问是一致的: 对象引用解析(dereference)为 null 时不抛出异常,而是返回 null。 并且如果是链式调用中的一部分,剩余链条会直接跳过。 此操作的结果,类似于在每次访问前使用 is_null() 函数判断方法和属性是否存在,但更加简洁。 ```php <?php // 自 PHP 8.0.0 起可用 $result = $repository?->getUser(5)?->name; // 上边那行代码等价于以下代码 if (is_null($repository)) { $result = null; } else { $user = $repository->getUser(5); if (is_null($user)) { $result = null; } else { $result = $user->name; } } ``` 仅当 null 被认为是属性或方法返回的有效和预期的可能值时,才推荐使用 nullsafe 操作符。如果业务中需要明确指示错误,抛出异常会是更好的处理方式。 #### 7. 只要类型兼容,任意数量的函数参数都可以用一个可变参数替换。 ```php <?php class A { public function method(int $many, string $parameters, $here) {} } class B extends A { public function method(...$everything) {} } ``` #### 8. static ("后期静态绑定"中) 可以作为返回类型: ```php <?php class Test { public function create(): static { return new static(); } } ``` #### 9. 现在可以通过 $object::class 获取类名,返回的结果和 get_class($object) 一致。 #### 10. new、instanceof 可用于任何表达式, 用法为 new (expression)(...$args) 和 $obj instanceof (expression)。 #### 11. 添加对一些变量语法一致性的修复,例如现在能够编写 Foo::BAR::$baz。 #### 12. 添加 Stringable interface, 当一个类定义 __toString() 方法后会自动实现该接口。 #### 13. Trait 可以定义私有抽象方法(abstract private method)。 类必须实现 trait 定义的该方法。 #### 14. 现在允许 catch (Exception) 一个 exception 而无需捕获到变量中。 #### 15. 可作为表达式使用 throw。 使得可以编写以下用法: ```php <?php $fn = fn() => throw new Exception('Exception in arrow function'); $user = $session->user ?? throw new Exception('Must have user'); ``` #### 16. 参数列表中的末尾逗号为可选。 ```php <?php function functionWithLongSignature( Type1 $parameter1, Type2 $parameter2, // <-- 这个逗号也被允许了 ) { } ``` #### 17. 父类中声明的私有方法不在对子类中的方法执行任何继承规则(final private 构造函数除外)。下列示例说明删除了那些限制: ```php <?php class ParentClass { private function method1() {} private function method2() {} private static function method3() {} // 抛出警告,因为“final”不再有效: private final function method4() {} } class ChildClass extends ParentClass { // 现在允许以下所有内容,即使修饰符与父类中的私有方法不同。 public abstract function method1() {} public static function method2() {} public function method3() {} public function method4() {} } ``` # 不向后兼容的变更 - 字符串与数字的比较 :数字与非数字形式的字符串之间的非严格比较现在将首先将数字转为字符串,然后比较这两个字符串。 数字与数字形式的字符串之间的比较仍然像之前那样进行。 请注意,这意味着 0 == "not-a-number" 现在将被认为是 false 。 - match 现在是一个保留字。 - 断言(Assertion)失败现在默认抛出异常。如果想要改回之前的行为,可以在 INI 设置中设置 assert.exception=0 。 - mixed 现在是保留字,所以不能用于类,接口或者 trait,也禁止在命名空间中使用。 - 与类名相同的方法名将不再被当做构造方法。应该使用__construct() 来取代它。 - 不再允许通过静态调用的方式去调用非静态方法。因此is_callable()在检查一个类名与非静态方法 时将返回失败(应当检查一个类的实例)。 - (real) 和 (unset) 转换已被移除。 - 移除 track_errors 执行。这意味着不能再用 php_errormsg。可以改用 error_get_last() 函数。 - 移除定义不区分大小写的常量功能。define() 的第三个参数可能不再为 true。 - 移除使用 __autoload() 函数指定自动加载器的功能。应该改用 spl_autoload_register()。 - errcontext 参数将不再传递给使用 set_error_handler() 设置的自定义错误处理程序。 - 移除 create_function()。应该改用匿名函数。 - 移除 each()。应该改用 foreach 或者 ArrayIterator。 - 移除在方法中使用 Closure::fromCallable() 或 ReflectionMethod::getClosure() 创建的匿名函数中解绑 this 的能力。 - The ability to unbind this from proper closures that contain uses of this has also been removed. - 移除对对象使用 array_key_exists() 的能力。应该改用 isset() 或 property_exists()。 - array_key_exists() 中 key 参数类型的行为已经和 isset() 和正常数组访问一致。所有的 key 类型现在使用通用的强制转换,数组/对象 key 会抛出 TypeError。 - 任意一个数组,将数字 n 作为第一个数字 key,下一个隐式键将会是 n+1。即使 n 为负数也是如此。 - error_reporting 默认级别现在是 E_ALL。之前排除 E_NOTICE 和 E_DEPRECATED。 - 现在默认启用 display_startup_errors。 - 在没有父级的类中使用 parent 将会导致 fatal compile-time 错误。 - @ 操作将不再屏蔽 fatal 错误(E_ERROR、E_CORE_ERROR、E_COMPILE_ERROR、E_USER_ERROR、E_RECOVERABLE_ERROR、E_PARSE)。当使用 @ 时,接受 error_reporting 为 0 的错误处理程序,应该调整为使用位掩码检查 - \#[ 不再解释为注释的开头,因为此语法现在用于注解。 - 由于不兼容的方法签名(违反 LSP)导致的继承错误现在将始终生成致命错误。以前在某些情况下会生成警告。 - 在运行时默认值解析为 null 的参数,将不在默默将参数类型标记为可为 null。必须改用指定可为 null 类型或者默认值为 null。 # PHP 8.0 废弃的功能 - 如果带有默认值的参数后面跟着一个必要的参数,那么默认值就会无效。这在 PHP 8.0.0 中已被废弃,通常可以通过删除默认值,不影响现有功能: ```php <?php function test($a = [], $b) {} // 之前 function test($a, $b) {} // 之后 ?> ``` 这条规则的一个例外是 Type $param = null 形式的参数,其中 null 的默认值使得类型隐式为空。这种用法仍然是允许的,但仍建议使用显式可空类型。 ```php <?php function test(A $a = null, $b) {} // 旧写法,仍可用 function test(?A $a, $b) {} // 推荐写法 ?> ``` - 参数 exclude_disabled 不能设置为 false 来调用 get_defined_functions(),该参数已被废弃,不再起作用。 get_defined_functions() 绝不会再包含禁用的函数。
顶部
收展
底部
[TOC]
目录
关于VC和线性安全的选择
PHP5.6版本
PHP6版本去哪儿了?
PHP7.0.x版本特性
PHP7.1.x版本特性
PHP7.2.x版本特性
PHP7.3.x版本特性
PHP7.4.x版本特性
PHP8.0.x版本特性
PHP8.1.x版本特性
相关推荐
PHP基础
PHP函数
PHP设计模式
PHP算法