PHP手册:https://www.php.net/manual/zh/appendices.php
这里只介绍常用或相对重要的特性。
标量类型声明
现在可以使用下列类型参数:字符串(string), 整数 (int), 浮点数 (float), 以及布尔值 (bool)。
扩充了PHP5中引入的其他类型:类名(object),接口(interface),数组(array)和回调类型(callable)。
可使用多变长参数列表:
function sumOfInts(int ...$ints)
{
return array_sum($ints);
}
sumOfInts(2, '3', 4.1); // 9
返回值类型声明
对函数返回类型声明的支持,可用的类型与参数声明中可用的类型相同。
function foo(int $bar): int
{
return $bar;
}
null合并运算符(??)
$username = $_GET['user'] ?? 'nobody';
// 等价于
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';
// 可以连续使用: 返回第一个有效值(变量存在且不为NULL)
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';
太空船操作符(组合比较符)
表达式 a <=> b
,当a小于、等于或大于b时它分别返回-1、0或1。
通过 define() 定义常量数组
define('ANIMALS', ['dog', 'cat', 'bird']);
匿名类
支持通过 new class
来实例化一个匿名类,这可以用来替代一些“用后即焚”的完整类。
interface Logger {
public function log(string $msg);
}
$t = new Class implements Logger {
function log(string $msg) {
echo $msg;
}
};
var_dump($t);
$t->log('hello');
以上例程会输出:
object(class@anonymous)#1 (0) {
}
hello
Unicode codepoint 转译语法
双引号或heredoc
语法现在接受一个以16进制的 Unicode codepoint,并且开头的 0 是可以省略的。
echo "\u{aa}";
echo "\u{0000aa}";
echo "\u{9999}";
以上例程会输出:
ª
ª
香
Closure::call()
Closure::call() 现在有着更好的性能,暂时绑定一个方法到对象上闭包并调用它。
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);
为 unserialize() 提供过滤
这个特性旨在提供更安全的方式解包不可靠的数据。它通过白名单的方式来防止潜在的代码注入。
// 将所有的对象都转换为 __PHP_Incomplete_Class 对象
$data = unserialize($foo, ["allowed_classes" => false]);
// 将除 MyClass 和 MyClass2 之外的所有对象都转换为 __PHP_Incomplete_Class 对象
$data = unserialize($foo, ["allowed_classes" => ["MyClass", "MyClass2"]);
// 默认情况下所有的类都是可接受的,等同于省略第二个参数
$data = unserialize($foo, ["allowed_classes" => true]);
增强断言
断言是向后兼用并增强之前的 assert() 的方法。它使得在生产环境中启用断言为零成本,并且提供当断言失败时抛出特定异常的能力。
assert() 现在是一个语言结构,它允许第一个参数是一个表达式,而不仅仅是一个待计算的 string 或一个待测试的 boolean。
ini_set('assert.exception', 1);
class CustomError extends AssertionError {}
assert(false, new CustomError('Some error message'));
使用 use 一次导入多个类、函数、常量
use some\namespace\{ClassA, ClassB, ClassC as C};
use function some\namespace\{fn_a, fn_b, fn_c};
use const some\namespace\{ConstA, ConstB, ConstC};
生成器可以返回表达式
在生成器函数中通过使用 return
返回一个表达式 (但是不允许返回引用值),通过调用 Generator::getReturn()
获取生成器的返回值, 但是只能在生成器完成产生工作后调用一次。
$gen = (function() {
yield 1;
yield 2;
return 3;
})();
foreach ($gen as $val) {
echo $val, PHP_EOL;
}
echo $gen->getReturn(), PHP_EOL;
1
2
3
生成器委派
使用 yield from
可以把一个生成器委派给其他的生成器、Taversable 对象或者 array。
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
整数除法函数 intdiv()
var_dump( intdiv(10, 3) ); // int(3)
新增 random_types() 和 random_int()
echo bin2hex( random_types(4) ), PHP_EOL; // 85867f06
echo random_int(0, 100), PHP_EOL; // 56
可为空(Nullable)类型
参数以及返回值的类型现在可以通过在类型前加上一个问号使之允许为NULL
。 这表示传入的参数或者函数返回的结果要么是给定的类型,要么是 NULL
。
function foo(?string $bar): ?string
{
return $bar;
}
var_dump( foo('php') ); // string(3) "php"
var_dump( foo(null) ); // NULL
// var_dump( foo() ); // 语法错误
其实还可以使用默认参数的方式实现可用参数这种效果:
function foo(string $bar = null): ?string
{
return $bar;
}
var_dump( foo('php') ); // string(3) "php"
var_dump( foo(null) ); // NULL
var_dump( foo() ); // NULL
但是返回nullable
还是得用?
void 返回类型
返回值声明为 void 类型的函数,要么干脆省去 return
语句,要么使用一个空的 return;
语句,NULL 也不是一个合法的返回值。
但是试图去获取一个 void 方法的返回值会得到 NULL ,并且不会产生任何警告。这么做的原因是不想影响更高层次的方法
function foo(): void
{
// return null; // Fatal error
return;
}
var_dump(foo());
数组解析新语法
短数组语法[]
现在作为 list()
语法的一个备选项,可以用于将数组的值赋给一些变量(在foreach中也可使用)。
$data = [
[1, 'Tom'],
[2, 'Fred'],
];
list($id1, $name1) = $data[0];
// 等价于
[$id2, $name2] = $data[1];
foreach ($data as list($id, $name)) {
//
}
// 等价于
foreach ($data as [$id, $name]) {
//
}
list()支持键名
list()和它的新的[]语法支持指定键名。这意味着它可以将任意类型的数组都赋值给一些变量。
$data = [
["id" => 1, "name" => 'Tom'],
["id" => 2, "name" => 'Fred'],
];
list("id" => $id1, "name" => $name1) = $data[0];
// 或者用 []
["id" => $id1, "name" => $name1] = $data[0];
foreach ($data as list("id" => $id, "name" => $name)) {
//
}
// 或者用 []
foreach ($data as ["id" => $id, "name" => $name]) {
//
}
类常量可见性
现在支持设置类常量的可见性:
class ConstDemo
{
const PUBLIC_CONST_A = 1; // 默认为 public
public const PUBLIC_CONST_B = 2;
protected const PROTECTED_CONST = 3;
private const PRIVATE_CONST = 4;
}
iterable 伪类
引入了一个新的iterable
伪类 (与callable
类似)。 这可以被用在参数或者返回值类型中,它表示可用接受数组或者实现了Traversable
接口的对象。
至于子类,当用作参数时,子类可以具体化父类的iterable
类型为 array
或一个实现了Traversable
的对象。
对于返回值,子类可以拓宽父类的 array
或对象返回值类型到iterable
。
function iterator(iterable $iter)
{
foreach ($iter as $val) {
// some code
}
}
iterator([1, 2, 3]);
多异常捕获处理
一个catch
语句块现在可以通过管道字符|
来实现多个异常的捕获。
try {
// some code
} catch (FirstException | SecondException $e) {
// handle first and second exceptions
}
负的字符串偏移值
所有支持偏移量的字符串操作函数现在都支持接受负数作为偏移量,包括通过[]
或{}
下标访问字符。
在这种情况下,一个负数的偏移量会被理解为一个从字符串结尾开始的偏移量,-1表示最后一个字符。
var_dump( "abcdef"[-2] ); // string (1) "e"
var_dump( substr("aabbcc", -3) ); // string (1) "bcc"
通过 Closure::fromCallable() 将 callable 转为闭包
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"
新的object类型声明
新的对象类型 object
(伪类, 作用类似于 callable), 引进了可用于逆变(contravariant)参数输入和协变(covariant)返回任何对象类型。
function test(object $obj) : object
{
return new SplQueue();
}
test(new StdClass());
允许重写抽象方法
当一个抽象类继承于另外一个抽象类的时候,继承后的抽象类可以重写被继承的抽象类的抽象方法。
abstract class A
{
abstract function test(string $s);
}
abstract class B extends A
{
// 重写 test() 方法,可修改参数列表和返回类型
abstract function test($s) : int;
}
use导入支持尾部逗号
use Foo\Bar\{
Foo,
Bar,
Baz,
};
更灵活的 Heredoc 和 Nowdoc 语法
Heredoc/Nowdoc 语法变的更灵活。现在支持闭合标记符的缩进,并且不再强制闭合标记符的换行。
闭合标记的缩进决定了 Heredoc/Nowdoc 中每个新行的空格的数量。
// 4 个缩进空格
echo <<<END
a
b
c
END;
a
b
c
数组析构支持引用赋值
数组解构现在支持使用语法 [&$a, [$b, &$c]] = $array
的引用赋值。list()也支持同样的语法。
instanceof操作符接受常量
instanceof现在允许常量作为第一个操作数,在这种情况下,结果始终为false。
CompileError异常类代替一些编译错误
添加了一个新的CompileError
异常类,ParseError
继承了该异常。现在,少量编译错误将抛出 CompileError,而不是fatal error。
目前,这只会影响 Token_GET_All()
在Token_parse模式下可能引发的编译错误,但将来可能会转换更多的错误。
类的属性中现在支持添加指定的类型
class User {
public int $id;
public string $name;
}
$user = new User();
// $user->id = 'abc'; // error
$user->name = 123; // ok, 严格模式下报错
箭头函数
$factor = 10;
$nums = array_map(fn($n) => $n * $factor, [1, 2, 3, 4]);
// $nums = [10, 20, 30, 40];
// 和之前使用function定义匿名函数的方式相比,这里使用外部变量 $factor 不需要使用 use 了
有限返回类型协变与参数类型逆变
class A {}
class B extends A {}
class Producer {
public function method(): A {}
}
class ChildProducer extends Producer {
public function method(): B {}
}
简单来说就是子类可以修改返回类型(类型更具体化)了,以下情况可用:
空合并运算符赋值
$array['key'] ??= computeDefault();
// 等同于以下旧写法
if (!isset($array['key'])) {
$array['key'] = computeDefault();
}
可用于设定变量默认值,比较之前更简洁。
数组展开操作(符)
$parts = ['apple', 'pear'];
$fruits = ['banana', 'orange', ...$parts, 'watermelon'];
// ['banana', 'orange', 'apple', 'pear', 'watermelon'];
在函数传参时也可以使用:
$a = [1, 2];
$b = [3, 4];
array_push($a, ...$b);
// $b = [1, 2, 3, 4];
数值文字分隔符
6.674_083e-11; // float
299_792_458; // decimal
0xCAFE_F00D; // hexadecimal
0b0101_1111; // binary
数字文字可以在数字之间包含下划线。
strip_tags() 可以传入标签列表
strip_tags() 函数现在可以传入标签列表strip_tags($str, ['a', 'p'])
,代替原来的方式: strip_tags($str, '<a><p>')
array_merge() 和 array_merge_recursive()
这两个函数现在可以在没有任何参数的情况下调用,在这种情况下,它们将返回空数组。这与扩展运算符结合使用是有用的,
$arrays = [ [1], [2] ];
array_merge(...$arrays); // [1, 2]
FFI扩展
这是一个新扩展,它提供了一种简单的方式来调用本机函数、访问本机变量以及创建/访问C库中定义的数据结构。
此扩展目前是实验性的。
本文标签: PHP
暂无评论,赶紧发表一下你的看法吧。