/

PHP Migrating 7.4 to 8.0

PHP 8.0 contains many new features and optimizations including named arguments, union types, attributes, constructor property promotion, match expression, nullsafe operator, JIT, and improvements in the type system, error handling, and consistency.

New Features 8.0

// Named Parameters 命名参数
// 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 [50, 50, 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"
// }
// 构造器属性提升 Constructor Property Promotion
// 当构造器参数带访问控制(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 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 都会导致错误
public function parse_value(string|int|float): string|null {}
// match 表达式
// https://www.php.net/manual/en/control-structures.match.php
// match 表达式必须是详尽,则会抛出 UnhandledMatchError。
echo match (8.0) {
'8.0' => "Oh no!",
8.0 => "This matches",
};
// This matches
// Nullsafe Operator
$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;
}
}
// Argument Unpacking
echo str_replace(...[
'replace' => 'Iron Man',
'search' => 'Inevitable',
'subject' => 'I am Inevitable',
]);
// Named Parameters with Variadic Parameters
function test($a, $b, ...$args) {
var_dump($args);
}
test(28, 15, june: 6, september: 15, january: "01");
// array(3) {
// ["june"]=> int(6)
// ["september"]=> int(15)
// ["january"]=> string(2) "01"
// }
// JIT Just-In-Time Compilation
// php -i | grep -i opcache
// php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=256M -d opcache.jit=tracing -i | grep -i jit

// opcache.enable=1
// opcache.enable_cli=1
// opcache.jit_buffer_size=256M

<?php
function fibonacci($n) {
if ($n < 2) return $n;
return fibonacci($n - 1) + fibonacci($n - 2);
}

$start = microtime(true);

fibonacci(42);

$end = microtime(true);
echo "Time taken: " . ($end - $start) . " seconds\n";

// php -d opcache.enable_cli=1 -d opcache.jit_buffer_size=256M -d opcache.jit=tracing test.php
// 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
$value = $config['path'] ?? throw new InvalidArgumentException('path required');
$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
// New string functions
str_contains('Foobar', 'Foo'); // true
str_starts_with('PHP 8', 'PHP'); // true
str_ends_with('Version8', '8'); // true

Backward Incompatible Changes 8.0

// 字符串与数字比较
// 0 == "not-a-number" is false
// 将数字转换为字符串,并使用字符串比较
var_dump(0 == "foo");
var_dump(42 == "42foo");
var_dump(0 == "");
// PHP80 PHP74
// bool(false) true
// bool(false) true
// bool(false) true

var_dump("" < 0);
var_dump("" < -1);
var_dump("a" < 0);
var_dump("b" < -1);
var_dump("ab" > 0);
// PHP80 PHP74
// bool(true) false
// bool(true) false
// bool(false) false
// bool(false) false
// bool(true) false
// 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 PHP74
// bool(false) true
// bool(true) true
// __autoload() function has been removed
// 删除了对 object 使用 array_key_exists() 的能力。isset() or property_exists() may be used instead
// isset($arr['key']) → false(当值为 null)- 只能在 null 安全时检查,无法区分 null 与 不存在
// array_key_exists('key', $arr) → true(即使值为 null)- 可以区分存在但值为 null 的情况

– EOF –