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