/

PHP Migrating to 7.4 8.0

PHP7.3 to PHP7.4

https://www.php.net/manual/en/migration74.php

New Features 7.4

// Typed properties
class User {
public int $id; // 会强制要求 $user->id 只能为 int 类型,访问前必须进行处理化,?int 也要进行初始化
}
// 箭头函数 Arrow functions
$factor = 10;
$nums = array_map(fn($n) => $n * $factor, [1, 2, 3, 4]);
// 有限的 Limited 返回类型协变和参数类型逆变
class A {}
class B extends A {}
class Producer {
public function method(): A {}
}
class ChildProducer extends Producer {
public function method(): B {}
}
// PHP74
// ok
//
// PHP73
// Fatal error: Declaration of ChildProducer::method(): B must be compatible with Producer::method(): A
// 空合并赋值运算符
$array['key1'] ??= 1;
// is roughly equivalent to
if (!isset($array['key2'])) {
$array['key2'] = 2;
}
var_dump($array);
// array(2) {
// ["key1"]=>
// int(1)
// ["key2"]=>
// int(2)
// }
// Unpacking inside arrays
// 可以平替 array_merge
$parts = ['apple', 'pear'];
$fruits = ['orange', ...$parts, 'watermelon'];
var_dump($fruits);
// array(4) {
// [0]=>
// string(6) "orange"
// [1]=>
// string(5) "apple"
// [2]=>
// string(4) "pear"
// [3]=>
// string(10) "watermelon"
// }
// 数字文字分隔符
6.674_083e-11; // float
299_792_458; // decimal
0xCAFE_F00D; // hexadecimal
0b0101_1111; // binary

var_dump((int)"1_123");
// int(1)
// WeakReference 类
// https://www.php.net/manual/zh/class.weakreference.php
$obj = new stdClass;
$weakref = WeakReference::create($obj);
var_dump($weakref->get());
unset($obj);
var_dump($weakref->get());
// object(stdClass)#1 (0) {
// }
// NULL

Backward Incompatible Changes 7.4

// 以数组形式访问非数组,将会抛出 notic
// null, bool, int, float or resource
$i = 12;
$i["a"];
// PHP74
// Notice: Trying to access array offset on value of type int
//
// PHP80
// Warning: Trying to access array offset on value of type int
//
// PHP73 ok

Deprecated Features 7.4

// 嵌套的三元运算必须明确地使用括号来指示运算的顺序
var_dump(1 ? 2 : 3 ? 4 : 5);
// PHP74
// Deprecated: Unparenthesized `a ? b : c ? d : e` is deprecated. Use either `(a ? b : c) ? d : e` or `a ? b : (c ? d : e)`
// int(4)

PHP7.4 to PHP8.0

https://www.php.net/manual/en/migration80.php

New Features 8.0

// 命名参数
// https://www.php.net/manual/zh/functions.arguments.php#functions.named-arguments
// array_fill(int $start_index, int $count, mixed $value): array
var_dump(array_fill(value: 50, count: 3, start_index: 0));
// PHP80
// array(3) {
// [0]=>
// int(50)
// [1]=>
// int(50)
// [2]=>
// int(50)
// }
// 注解(Attributes)
// https://www.php.net/manual/zh/language.attributes.php
// 定义注解 -> 使用注解 -> (通过反射)提取注释
// 注意以后用词:Attributes 注解,Properties 属性
#[\Attribute]
class JsonSerialize {
public function __construct(public ?string $fieldName = null) {
var_dump("JsonSerialize:$fieldName");
}
}
class VersionedObject {
#[JsonSerialize('json-foobar')]
protected string $myValue = '';
}
$object = new VersionedObject();
$reflection = new ReflectionObject($object);
foreach ($reflection->getProperties() as $reflectionProp) {
foreach ($reflectionProp->getAttributes(JsonSerialize::class) as $jsonSerializeAttr) {
var_dump($jsonSerializeAttr->getName());
var_dump($jsonSerializeAttr->getArguments());
var_dump($jsonSerializeAttr->getTarget());
var_dump($jsonSerializeAttr->isRepeated());
var_dump($jsonSerializeAttr->newInstance());
}
}
// string(13) "JsonSerialize"
//
// array(1) {
// [0]=>
// string(11) "json-foobar"
// }
//
// int(8) Attribute::TARGET_PROPERTY
//
// bool(false)
//
// string(25) "JsonSerialize:json-foobar"
// object(JsonSerialize)#5 (1) {
// ["fieldName"]=>
// string(11) "json-foobar"
// }
// 构造器属性提升
// 当构造器参数带访问控制(visibility modifier)时,PHP 会同时把它当作对象属性和构造器参数,并赋值到属性
class Point {
public function __construct(protected int $x, protected int $y = 0) {
}
}
var_dump(new Point(2,4));
// object(Point)#1 (2) {
// ["x":protected]=>
// int(2)
// ["y":protected]=>
// int(4)
// }
class Point {
public function __construct(int $x, int $y = 0) {
$this->x = $x;
$this->y = $y;
}
}
var_dump(new Point(2,4));
// object(Point)#1 (2) {
// ["x"]=>
// int(2)
// ["y"]=>
// int(4)
// }

class Point2 {
protected int $x;
protected int $y;
public function __construct(protected int $x, protected int $y = 0) {}
}
// 此类中已经定义了具有相同名称的字段
// Fatal error: Cannot redeclare Point::$x
// Union types 联合类型
// ?T is same T|null
//
// - 类型只能出现一次 int|string|INT 否则报错
// - 使用 mixed 会报错
// - bool 与 false or true 不能混用
// - object 与 class 不能混用
// - iterable 与 array Traversable 不能混用
// - 使用 self、parent 或 static 都会导致错误
// match 表达式
// https://www.php.net/manual/en/control-structures.match.php
// match 表达式必须是详尽,则会抛出 UnhandledMatchError。
$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"
// Nullsafe 方法和属性
// As of PHP 8.0.0, this line:
$result = $repository?->getUser(5)?->name;
// Is equivalent to the following code block:
if (is_null($repository)) {
$result = null;
} else {
$user = $repository->getUser(5);
if (is_null($user)) {
$result = null;
} else {
$result = $user->name;
}
}
// The WeakMap class has been added, accepts objects as keys, similar SplObjectStorage
// https://www.php.net/manual/en/class.weakmap.php
// https://www.php.net/manual/en/class.splobjectstorage.php
// final class WeakMap implements ArrayAccess, Countable, IteratorAggregate
$wm = new WeakMap();
$o = new StdClass;
class A {
public function __destruct() {
echo "Dead!\n";
}
}
$wm[$o] = new A;
var_dump(count($wm));
// int(1)
unset($o);
// Dead!
// 销毁
var_dump(count($wm));
// int(0)


// 对比 SplObjectStorage
$wm = new SplObjectStorage();
$o = new StdClass;
class A {
public function __destruct() {
echo "Dead!\n";
}
}
$wm[$o] = new A;
var_dump(count($wm));
// int(1)
unset($o);
// 未销毁
var_dump(count($wm));
// int(1)
// 只要类型兼容,现在可以将任意数量的函数参数替换为可变参数 variadic argument
class A {
public function method(int $many, string $parameters, $here) {}
}
class B extends A {
public function method(...$everything) {}
}
// OK
class C extends A {
public function method(int ...$everything) {}
}
// Fatal error: Declaration of C::method(int ...$everything)
// static 后期静态绑定,现在可以用作返回类型
class Test {
public function create(): static {
return new static();
}
}
// get_class($obj) === $obj::class
class Test {}
$obj = new Test;
var_dump($obj::class);
var_dump(get_class($obj));
// new instanceof 可以与任意表达式一起使用
// new (expression)(...$args)
// $obj instanceof (expression)
class A{};
class B{};
new (1>2 ? "A" : "B")();
// 新增 Stringable 接口。如果 class 定义了 __toString(),则自动实现了此接口
// Traits can define abstract private methods
trait T {
abstract private function a();
}
// throw 可以作为一个表达式 as an expression
$fn = fn() => throw new Exception('Exception in arrow function');
// 参数列表中现在允许使用可选的尾随逗号
function functionWithLongSignature(
Type1 $parameter1,
Type2 $parameter2, // <-- This comma is now allowed.
) {}
// 允许 catch (Exception) 无需存储到变量
try {}
catch (Exception) {}
// 在父类上声明的私有方法不再对子类的方法强制执行任何继承规则(私有构造函数除外)
class ParentClass {
private function method1() {}
private function method2() {}
private static function method3() {}
}
abstract class ChildClass extends ParentClass {
public abstract function method1();
public static function method2() {}
public function method3() {}
}
// OK

Backward Incompatible Changes 8.0

// 字符串与数字比较
// 0 == "not-a-number" is false
// 将数字转换为字符串,并使用字符串比较
var_dump(0 == "foo");
var_dump(42 == "42foo");
var_dump(0 == "");
// PHP80
// bool(false)
// bool(false)
// bool(false)
//
// PHP74
// bool(true)
// bool(true)
// bool(true)
var_dump("" < 0);
var_dump("" < -1);
var_dump("a" < 0);
var_dump("b" < -1);
var_dump("ab" > 0);
// PHP80
// bool(true)
// bool(true)
// bool(false)
// bool(false)
// bool(true)
//
// PHP74
// bool(false)
// bool(false)
// bool(false)
// bool(false)
// bool(false)
// match is 保留关键字
// is_callable() 在检查具有 classname 的 non-static 方法时将 false(必须检查对象实例)
class Test {
public function method1() {}
}
var_dump(is_callable([Test::class, 'method1']));
var_dump(is_callable([new Test, 'method1']));
// PHP80
// bool(false)
// bool(true)
//
// PHP74
// bool(true)
// bool(true)
// __autoload() function has been removed
// 删除了对 object 使用 array_key_exists() 的能力。isset() or property_exists() may be used instead

– EOF –