PHP Migrating 8.3 to 8.4

简介 PHP 8.4 引入了许多新特性,如属性钩子(Property Hooks)、非对称可见性(Asymmetric Visibility)以及更新的 DOM API 等。本文将介绍主要变化和如何进行迁移。 参考资源 在线测试环境:https://onlinephp.io/ 官方发布说明:https://www.php.net/releases/8.4/en.php 迁移指南:https://www.php.net/manual/en/migration84.php 更新详情:https://php.watch/versions/8.4 PHP 8.4 新特性 属性钩子与非对称可见性 // 属性钩子(Property Hooks)与非对称可见性(Asymmetric Visibility) class User { private bool $isModified = false; public function __construct(public private(set) string $first, private string $last) { } public string $fullName { get => $this->first . " " . $this->last; set { [$this->first, $this->last] = explode(' ', $value, 2); $this->isModified = true; } } } $user = new User("Ming", "Li"); echo $user->fullName; // 输出: Ming Li $user->fullName = "Hong Xiao"; echo $user->fullName; // 输出: Hong Xiao // 非对称可见性示例 echo $user->first; // 输出: Hong $user->first = "Hua"; // 错误: Fatal error: Uncaught Error: Cannot modify private(set) property User::$first 属性钩子允许我们定义属性的获取和设置行为,而非对称可见性允许我们为属性设置不同的读写权限。在上例中,$first 属性可以公开读取但只能私有写入。 可空类型解包 PHP 8.4 引入了可空类型解包操作符 ??=,可以在一行代码中解包可空值: ...

February 1, 2025 · 3 min · 499 words · Me

PHP Migrating 8.2 to 8.3

实验环境:https://onlinephp.io/ https://www.php.net/releases/8.3/en.php https://www.php.net/manual/en/migration83.php https://php.watch/versions/8.3 PHP 8.3 contains many new features, such as explicit typing of class constants, deep-cloning of readonly properties and additions to the randomness functionality. New Features 8.3 // Typed class constants // Include class, trait, interface, enum trait TestTrait { final protected const string TEST = 'test'; } interface TestInterface { public const string TEST = 'test'; } enum TestEnum: string { public const string TEST = 'test'; } // Similar to return types, class constant types can also be "narrowed", or kept the same in a sub-class or an implementation. This follows LSP. class ParentClass { public const string|int VALUE = 'MyValue'; } class ChildClass extends ParentClass { public const string VALUE = 'MyValue'; } json_validate('[1, 2, 3]'); // true json_validate('{1, 2, 3]'); // false // Dynamic class constant and Enum member fetch support class Foo { const PHP = 'PHP 8.3'; } $searchableConstant = 'PHP'; echo constant(Foo::class . "::{$searchableConstant}"); echo Foo::{$searchableConstant}; // instead of `constant()` // Random extension // public function getFloat( // float $min, float $max, // \Random\IntervalBoundary $boundary = \Random\IntervalBoundary::ClosedOpen // ): float // // enum IntervalBoundary { // case ClosedOpen; // case ClosedClosed; // case OpenClosed; // case OpenOpen; // } $rng = new Random\Randomizer(); echo $rng->getFloat(0, 10, \Random\IntervalBoundary::OpenOpen); // 6.7810757668383 // Generate a random number in the range 0 <= and < 1: $rng = new Random\Randomizer(); echo $rng->nextFloat(); // 0.21185336351144 // Returns a random number sequence of a requested length ($length parameter), only containing a bytes from the requested series of bytes ($string parameter). $rng = new Random\Randomizer(); $chars = 'AEIOU'; echo $rng->getBytesFromString($chars, 1); // "E" echo $rng->getBytesFromString($chars, 5); // "AIIEO" // #[\Override] attribute // PHP enforces that the targeted class method must be overriding or implementing a parent/interface method. class ParentClass { protected function demo() {} } class ChildClass extends ParentClass { #[\Override] protected function demo() {} // ✅ 明确覆盖 #[\Override] protected function demmo() {} // ❌ Fatal error (拼写错误) } New Functions 8.3 // Pad a multibyte string to a certain length with another multibyte string echo mb_str_pad('中国', 6, '爱', STR_PAD_RIGHT) . "\n"; echo mb_str_pad('中国', 6, '爱', STR_PAD_LEFT) . "\n"; echo mb_str_pad('中国人', 6, '爱', STR_PAD_BOTH) . "\n"; // 中国爱爱爱爱 // 爱爱爱爱中国 // 爱中国人爱爱 Backward Incompatible Changes 8.3 // Assigning a negative index n to an empty array will now make sure that the next index is n + 1 instead of 0. $arr = []; $arr[-5] = 'a'; $arr[] = 'b'; var_dump($arr); // PHP8.3 → [-5 => 'a', -4 => 'b'] // PHP <8.3 → [-5 => 'a', 0 => 'b'] – EOF – ...

August 1, 2024 · 2 min · 426 words · Me

PHP Migrating 8.1 to 8.2

实验环境:https://onlinephp.io/ https://www.php.net/releases/8.2/en.php https://www.php.net/manual/en/migration82.php https://php.watch/versions/8.2 https://github.com/symfony/polyfill/tree/1.x/src/Php82 PHP 8.2 brings type-system improvements for expressive and fine-grained type-safety, readonly classes, sensitive parameter redaction support, new random extension, and many new features along with several changes to streamline and modernize PHP language. New Features 8.2 // Readonly Classes // Complete class properties become immutable when declared readonly readonly class User { public function __construct(public string $username, public string $email) { } } // All properties automatically become readonly $user = new User('john_doe', 'john@example.com'); $user->email = 'new@example.com'; // Fatal error: Uncaught Error: Cannot modify readonly property User::$email // Combine union and intersection types using Disjunctive Normal Form // 析取范式,允许通过 |(联合)和 &(交叉)运算符组合类型 function process((Countable&Iterator)|null $item) { return $item; } // Use null, true, false as independent types function alwaysFalse(): false {} function alwaysNull(): null {} function alwaysTrue(): true {} // Define constants directly in traits trait NetworkTrait { public const TIMEOUT = 60; } // Hide sensitive data in stack traces function hashPassword(#[\SensitiveParameter] string $password) { debug_print_backtrace(); } hashPassword('secret123'); // Before #0 /home/user/scripts/code.php(7): hashPassword('secret123') // PHP8.2 #0 /home/user/scripts/code.php(7): hashPassword(Object(SensitiveParameterValue)) // Consolidated RNG functions with OOP interface $randomizer = new \Random\Randomizer(); echo $randomizer->getInt(1, 100); // Random integer New Functions 8.2 // Parses any data size recognized by PHP INI values ini_parse_quantity('256M'); // 268435456 Deprecated Features 8.2 // ${var} String Interpolation Deprecated $name = 'Li'; echo "Hello ${name}"; // should be `{$name}` // Deprecated: Using ${var} in strings is deprecated, use {$var} instead $var = 'name'; echo "Hello ${$var}"; // should be `{$$var}` // Deprecated: Using ${expr} (variable variables) in strings is deprecated, use {${expr}} – EOF – ...

July 1, 2024 · 2 min · 260 words · Me

PHP Migrating 8.0 to 8.1

实验环境:https://onlinephp.io/ https://www.php.net/releases/8.1/en.php https://www.php.net/manual/en/migration81.php https://php.watch/versions/8.1 https://github.com/symfony/polyfill/tree/1.x/src/Php81 PHP 8.1 contains many new features, including enums, readonly properties, first-class callable syntax, fibers, intersection types, performance improvements and more. New Features 8.1 // Add native support for enumerations using enum keyword // https://www.php.net/manual/en/language.enumerations.examples.php enum Status: string { case Draft = 'draft'; case Published = 'published'; case Archived = 'archived'; } class BlogPost { public function __construct( public Status $status = Status::Draft ) {} } // Readonly properties // Properties can be marked as immutable after initialization class User { public readonly string $uuid; public function __construct(string $uuid) { $this->uuid = $uuid; } } // Fibers, new concurrency primitive for managing green threads // https://www.php.net/manual/en/language.fibers.php $fiber = new Fiber(function(): void { echo "Starting fiber\n"; Fiber::suspend(); echo "Resuming fiber\n"; }); $fiber->start(); echo "...\n"; $fiber->resume(); // Starting fiber // ... // Resuming fiber // Intersection Types, combine multiple type constraints // 交叉类型 function countItems(Iterator&Countable $items): int { return count($items); } // Never Return Type // Indicates function never returns function redirect(string $url): never { header("Location: $url"); exit(); } // First-Class Callable Syntax // Replaces Closure::fromCallable() with cleaner syntax $strlen = strlen(...); echo $strlen('hello'); // 5 // same as $fn = Closure::fromCallable('strlen'); echo $fn('hello'); // 5 $words = ['apple', 'banana']; var_dump(array_map(strlen(...), $words)); // [5, 6] class Calculator { public function add(int $a, int $b): int { return $a + $b; } public function __invoke(int $x) { return $x * 2; } } $calc = new Calculator(); $addCallable = $calc->add(...); echo $addCallable(3, 5); // 8 $invocable = $calc(...); echo $invocable(10); // 20 // New Array Features // String key unpacking $arr1 = [1, 'a' => 'b']; var_dump([...$arr1, 'c' => 'd']); // [1,'a' => 'b', 'c' => 'd'] // 参数解包后的命名参数 function foo($a, $b, $c) { var_dump($a, $b, $c); } foo(...[1,2], c: 3); // File System Improvements $file = fopen('data.txt', 'w'); fwrite($file, 'content'); fsync($file); // Force write to disk fclose($file); // Objects in default parameters // 现在可以使用 new ClassName() 表达式作为参数、静态变量、全局常量初始值设定项和属性参数的默认值 // 现在也可以将对象传递给 define() class Logger { public function __construct( private Formatter $fmt = new DefaultFormatter() ) {} } // 八进制 var_dump(0o14); // int(12) New Functions 8.1 // 如果数组的键由从 0 到 count($array)-1 的连续数字组成,则数组被认为是 list array_is_list([]); // true array_is_list(['apple', 2, 3]); // true array_is_list([0 => 'apple', 'orange']); // true // The array does not start at 0 array_is_list([1 => 'apple', 'orange']); // false Backward Incompatible Changes 8.1 // Usage of static Variables in Inherited Methods ¶ class A { public static function counter() { static $i = 0; return ++$i; } } class B extends A {} var_dump(A::counter()); // int(1) var_dump(A::counter()); // int(2) var_dump(B::counter()); // PHP 8.0 int(1) // PHP 8.1+ int(3) Deprecated 8.1 // Implicit incompatible float to int conversions $arr = []; $arr[3.14] = 'value'; // Deprecated(键值3.14转为3) $arr[(int)3.14] = 'value'; // 显式转换避免警告 – EOF – ...

December 21, 2022 · 3 min · 454 words · Me

PHP Migrating 7.4 to 8.0

实验环境:https://onlinephp.io/ https://www.php.net/releases/8.0/en.php https://www.php.net/manual/en/migration80.php https://php.watch/versions/8.0 https://github.com/symfony/polyfill/tree/1.x/src/Php80 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 – ...

December 14, 2022 · 5 min · 942 words · Me

PHP Migrating to 7.2 7.3 7.4

实验环境:https://onlinephp.io/ PHP7.1 to PHP7.2 https://www.php.net/manual/en/migration72.php https://php.watch/versions/7.2 Argon2 password hashing support, class constant visibility, object type, and many more. New Features 7.2 // 新的 object 类型 // 可用于逆变(contravariant)参数输入和协变(covariant)返回任何对象类型 // https://www.php.net/manual/zh/language.oop5.variance.php // 协变使子类比父类方法能返回更具体的类型;逆变使子类比父类方法参数类型能接受更模糊的类型 function test(object $obj): object { return new SplQueue(); } test(new StdClass()); // 抽象类可以重写被继承的抽象类的抽象方法 abstract class A { abstract function test(string $s); } abstract class B extends A { // overridden - 仍然保持参数的逆变和返回的逆变 abstract function test($s): int; } // 重写方法和接口实现的参数类型可以省略 // 仍然是符合LSP,这种参数类型是逆变 interface A { public function test(array $input); } class B implements A { // type omitted for $input public function test($input){ return $input; } } var_dump((new B())->test(1)); // PHP72 int(1) // // PHP71 Fatal error: Declaration of B::test($input) must be compatible with A::test(array $input) Backward incompatible changes 7.2 // 防止 number_format() 返回负零 var_dump(number_format(-0.01)); // PHP72 string(1) "0" // // PHP71 string(2) "-0" // 转换对象和数组中的数字键 $arr = [0 => 1]; $obj = (object) $arr; var_dump($obj); var_dump($obj->{'0'}, // 新写法 $obj->{0} // 新写法 ); // PHP72 object(stdClass)#1 (1) { // ["0"]=> // int(1) // } // int(1) // int(1) // // PHP71 object(stdClass)#1 (1) { // [0]=> // int(1) // } // Notice: Undefined property: stdClass::$0 $obj = new class { public function __construct() { $this->{1} = "value"; } }; $arr = (array) $obj; var_dump($arr); var_dump($arr["1"]); // 整数 或者 字符串整数 含义相同 var_dump($arr[1]); // PHP71 var_dump($arr[0]); // PHP72 array(1) { // [1]=> // string(2) "my" // } // // PHP71 无法取整型字符串 key // array(1) { // ["1"]=> // string(2) "my" // } // Notice: Undefined offset: 1 // Notice: Undefined offset: 1 // Notice: Undefined offset: 0 $a = [1.3 => "v1", 1.4 => "v2"]; var_dump($a); // ALL array key float 会被转换为 int // array(1) { // [1]=> // string(2) "v2" // } // get_class() 函数不再接受 null var_dump(get_class(null)); // PHP72 // Warning: get_class() expects parameter 1 to be object // bool(false) // // PHP71 // Warning: get_class() called without object from outside a class // bool(false) // // PHP80 // Fatal error: Uncaught TypeError: get_class(): Argument #1 ($object) must be of type object, null given // 计算非可数类型(non-countable)时发出警告 var_dump( count(null), // NULL is not countable count(1), // integers are not countable count('abc'), // strings are not countable count(new stdclass) // objects not implementing the Countable interface are not countable ); // PHP72 // Warning: count(): Parameter must be an array or an object that implements Countable // // PHP71 // 无 Warning // // PHP80 // Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array // 调用未定义的常量,现在会抛出一个 E_WARNING 错误(之前版本中为 E_NOTICE)) // PHP8 将不会转化成他们自身的字符串,同时抛出 Error 异常 var_dump(MY_CONST); // PHP72 // Warning: Use of undefined constant MY_CONST - assumed 'MY_CONST' // string(8) "MY_CONST" // // PHP71 // Notice: Use of undefined constant MY_CONST // string(8) "MY_CONST" // // PHP80 // Fatal error: Uncaught Error: Undefined constant "MY_CONST" // bcmod 任意精度数字取模,添加新增参数 scale var_dump(bcmod("4", "3.5")); // PHP72 // string(1) "0" // // PHP71 // string(1) "1" var_dump(bcmod("4", "3.5", 1)); // PHP72 // string(3) "0.5" // // PHP71 // Warning: bcmod() expects exactly 2 parameters, 3 given // json_decode associative 允许为 null // 当为 true 时,JSON 对象将返回关联 array;当为 false 时,JSON 对象将返回 object。 // 当为 null 时,JSON 对象将返回关联 array 或 object,这取决于是否在 flags 中设置 JSON_OBJECT_AS_ARRAY // https://www.php.net/manual/zh/function.json-decode.php $json = '{"a":1,"b":2}'; var_dump(json_decode($json, null, 512, JSON_OBJECT_AS_ARRAY)); // PHP72 // array(2) { // ["a"]=> // int(1) // ["b"]=> // int(2) // } // // PHP71 // object(stdClass)#1 (2) { // ["a"]=> // int(1) // ["b"]=> // int(2) // } PHP7.2 to PHP7.3 https://www.php.net/manual/en/migration72.php https://php.watch/versions/7.3 Heredoc/nowdoc syntax improvements and a bunch of legacy code deprecations. ...

December 12, 2022 · 7 min · 1281 words · Me

PHP Migrating to 7.0 7.1

实验环境:https://onlinephp.io/ Polyfill https://github.com/symfony/polyfill/tree/main/src PHP7.0 to PHP7.1 https://www.php.net/manual/en/migration71.php 7.1 New Features // Nullable types 可为空类型 function test(?string $name): ?string // Void 函数 function swap(&$left, &$right) : void // 获取一个 void 方法的返回值会得到 null,并且不会产生任何警告 // 对称数组解构 $data = [ [1, 'Tom'], [2, 'Fred'], ]; [$id, $name] = $data[0]; // 赋值部分 [, $name1] = $data[1]; foreach ($data as [$id, $name]) { // logic here with $id and $name } function swap(&$a, &$b): void { [$a, $b] = [$b, $a]; } // list 支持键名 $data = [ ["id" => 1, "name" => 'Tom'], ["id" => 2, "name" => 'Fred'], ]; foreach ($data as ["name" => $name, "id" => $id]) { // logic here with $id and $name } // 类常量支持可见性 class ConstDemo { public const PUBLIC_CONST_A = 1; } // iterable 伪类(与 callable 类似) interface Traversable extends iterable { // 这个接口没有任何方法,它的作用仅仅是作为所有可遍历类的基本接口 // Traversable as part of either Iterator or IteratorAggregate,不能直接实现 } function iterator(iterable $iter) { foreach ($iter as $val) { // ... } } // 多异常捕获处理,可以通过管道字符(|)来实现多个异常的捕获 try { // some code } catch (FirstException | SecondException $e) { // handle first and second exceptions } // 支持为负的字符串偏移量,一个负数的偏移量会被理解为一个从字符串结尾开始的偏移量 // 所有支持偏移量的字符串操作函数,都支持接受负数作为偏移量 // 中文操作要小心 var_dump("abcdef"[-2]); // e var_dump(strpos("aabbcc", "b")); // 2 var_dump(strpos("aabbcc", "b", -3)); // 3 // 新增 Closure::fromCallable(),将 callable 转为一个 Closure 对象 // public static Closure::fromCallable(callable $callback): Closure 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" 7.1 New Functions // 否为可迭代 is_iterable() 7.1 Backward Incompatible Changes // 当传递参数过少时将抛出错误 function test($param){} test(); /* PHP71 Fatal error: Uncaught ArgumentCountError: Too few arguments to function test() PHP70 Warning: Missing argument 1 for test() */ // 禁止动态调用作用域自检函数 // $func() or array_map('extract', ...) // function array_map($callback, array $array, array ...$arrays): array (function () { $func = 'func_num_args'; $func(); })(); /* PHP71 Warning: Cannot call func_num_args() dynamically */ // 以下名称不能用于命名 class, interface, trait // - void // - iterable // 数字字符串转换 遵循科学记数法 var_dump(intval('1e5')); var_dump(is_numeric('1e5')); var_dump(is_numeric('e')); // PHP71 PHP70 // int(100000) int(1) // bool(true) bool(true) // bool(false) bool(false) // call_user_func() 不再支持对传址的函数的调用 error_reporting(E_ALL); function increment(&$var) { $var++; } $a = 0; call_user_func('increment', $a); // ...expected to be a reference, value given call_user_func_array('increment', [&$a]); // 1 $increment = 'increment'; $increment($a); // 2 // 字符串不再支持空索引运算符 $str = ""; $str[] = "abc"; var_dump($str); // PHP71 Fatal error: Uncaught Error: [] operator not supported for strings // PHP70 array(1) { // [0]=> // string(3) "abc" // } $a = ""; $a[0] = "hello world"; var_dump($a); // PHP71 string(1) "h" // PHP70 array(1) { // [0]=> // string(11) "hello world" // } // 通过空字符串上的字符串索引访问赋值 $str = ''; $str[10] = 'foo'; var_dump($str); // PHP71 string(11) " f" // PHP70 array(1) { // [10]=> // string(3) "foo" // } // 当通过引用赋值引用它们自动创建这些元素时,数组中元素的顺序已更改。 $array = []; $array['a'] = &$array['b']; $array['b'] = 1; var_dump($array); // PHP71 array(2) { // ["b"]=> // &int(1) // ["a"]=> // &int(1) // } // PHP70 array(2) { // ["a"]=> // &int(1) // ["b"]=> // &int(1) // } // 词法绑定变量不能重用名称,以下都将 fatal error $f = function () use ($_SERVER) {}; // any superglobals $f = function () use ($this) {}; $f = function ($param) use ($param) {}; // 禁止 "return;" 对于已经在编译时键入的返回值 function test(int $i): array { $i += 1; return; } // PHP Compile Error A function with return type must return a value var_dump('1b' + 'something'); // PHP71 // Notice: A non well formed numeric value encountered // Warning: A non-numeric value encountered // int(1) // PHP70 int(1) PHP5.6 to PHP7.0 https://www.php.net/manual/en/migration70.php Backward incompatible changes // 错误和异常处理相关的变更 // set_exception_handler() 不再保证收到的一定是 Exception 对象 function handler(Exception $e) { ... } set_exception_handler('handler'); // 兼容 PHP 5 和 7 function handler($e) { ... } // 仅支持 PHP 7 function handler(Throwable $e) { ... } // 当内部构造器失败的时候,总是抛出异常 // 间接调用的表达式的新旧解析顺序 // 现在严格遵循从左到右的顺序来解析 // 表达式 PHP 5 的解析方式 PHP 7 的解析方式 $$foo['bar']['baz'] ${$foo['bar']['baz']} ($$foo)['bar']['baz'] $foo->$bar['baz'] $foo->{$bar['baz']} ($foo->$bar)['baz'] // foreach 通过值遍历时,操作的值为数组的副本 $array = [0]; foreach ($array as $val) { var_dump($val); $array[1] = 1; } // int(0) // foreach通过引用遍历时,有更好的迭代特性 $array = [0]; foreach ($array as &$val) { var_dump($val); $array[1] = 1; } // PHP 7.0 // int(0) // int(1) // PHP 5.6 // int(0) // call_user_method() and call_user_method_array() 被移除。 // 应该使用 call_user_func() 和 call_user_func_array() // 在函数中检视参数值会返回 当前 的值 function foo($x) { $x++; var_dump(func_get_arg(0)); } foo(1); // int(2) PHP 7.0 // int(1) PHP 5.6 7.0 New Features // 标量类型声明 // 返回值类型声明 function arraysSum(array ...$arrays): array { return array_map(function(array $array): int { return array_sum($array); }, $arrays); } // null 合并运算符,对 isset() 的简化 $username = $_GET['user'] ?? 'nobody'; // 太空船操作符(组合比较符) echo 1 <=> 1; // 0 echo 1 <=> 2; // -1 echo 2 <=> 1; // 1 // 通过 define() 定义 常量数组 define('ANIMALS', [ 'dog', 'cat', 'bird' ]); // 匿名类 用来替代一些“用后即焚”的完整类定义 $app = new Application; $app->setLogger(new class implements Logger { public function log(string $msg) { echo $msg; } }); // Unicode codepoint 转译语法 // 这接受一个以 16 进制形式的 Unicode codepoint echo "\u{9999}"; // 香 // Closure::call() 新方法,简化绑定一个方法到对象上闭包并调用它 // PHP 7 之前版本的代码 $getXCB = function() {return $this->x;}; // function bindTo($newThis, $newScope = 'static') { } // $newScope 将闭包关联到的类作用域 $getX = $getXCB->bindTo(new A, A::class); // 中间层闭包 echo $getX(); // PHP 7+ 及更高版本的代码 $getX = function() {return $this->x;}; echo $getX->call(new A); // 生成器委托 yield from 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 // 柯里化 function add($a) { return function($b) use ($a) { return $a + $b; }; } $result = add(10)(15); var_dump($result); // int 25 Deprecated features in PHP 7.0.x // 构造函数(方法名和类名一样)将被弃用,并在将来移除 class Foo { function foo() { echo 'I am the constructor'; } } // PHP 7.0: Deprecated: Methods with the same name as their class will not be constructors in a future version of PHP // PHP 5.6: OK Other Changes // 放宽了保留词限制,以前不能用 'new'、'private' 和 'for' Project::new('Project Name')->private()->for('purpose here')->with('username here'); // class 关键词不能用于常量名,否则会和类名解析语法冲突(ClassName::class) New Global Constants PHP_INT_MIN – EOF – ...

November 28, 2022 · 5 min · 975 words · Me

Laradock 学习笔记

Laradock 简介 Laradock 是一个基于 Docker 的完整 PHP 开发环境,专为 PHP 开发者(特别是 Laravel 开发者)设计。它提供了一套预配置的 Docker 容器,包含开发中常用的服务,如 Nginx、MySQL、Redis、PHP-FPM 等。 主要优势 开箱即用:预配置的开发环境,无需繁琐设置 一致性:确保开发、测试和生产环境的一致性 灵活性:支持多种 PHP 版本和扩展 模块化:可以根据需要启用或禁用特定服务 跨平台:在 Windows、macOS 和 Linux 上表现一致 基本架构 Laradock 采用模块化设计,每个服务都运行在独立的容器中,通过 Docker 网络连接: Workspace:包含开发工具的容器 PHP-FPM:PHP 解释器 Nginx/Apache:Web 服务器 MySQL/PostgreSQL/MongoDB:数据库 Redis/Memcached:缓存 其他辅助服务(邮件服务器、消息队列等) Dockerfile DEBIAN_FRONTEND ENV DEBIAN_FRONTEND=noninteractive DEBIAN_FRONTEND 环境变量用于告知操作系统应该从哪儿获得用户输入。 设置为 “noninteractive” 可以实现自动安装而无需用户交互,对于 CI/CD 流程和 Docker 构建特别有用。这在运行 apt-get 命令时尤为重要,因为它会自动选择默认选项并以最快速度完成构建。 最佳实践是在 RUN 命令中设置该变量,而不是使用 ENV 进行全局设置,因为全局设置会影响容器运行时的交互体验: RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y ... 基于角色的用户配置 # 创建非 root 用户防止文件在宿主机上创建时具有 root 权限 ARG PUID=1000 ENV PUID ${PUID} ARG PGID=1000 ENV PGID ${PGID} RUN set -xe; \ groupadd -g ${PGID} laradock && \ useradd -l -u ${PUID} -g laradock -m laradock -G docker_env && \ usermod -p "*" laradock -s /bin/bash 这段配置创建了一个非 root 用户 laradock,解决了容器和宿主机之间文件权限问题: ...

November 9, 2022 · 6 min · 1220 words · Me, LLM

PHP 安装 gRPC

ENV cat /etc/redhat-release CentOS Linux release 7.2 (Final) uname -a Linux xxx-xxx 3.10.107-1-tlinux2_kvm_guest-0052 #1 SMP Wed Jan 15 18:42:19 CST 2020 x86_64 x86_64 x86_64 GNU/Linux PECL pecl install grpc 如果遇到: Connection to `ssl://pecl.php.net:443′ failed: 参考: PHP Swoft 框架环境配置 | ZYF.IM Build 通用手动安装 PECL 扩展 For this to work, you’ll need to have root access to your server, and you’ll most probably need developer tools installed as well. https://pecl.php.net/ # 创建临时目录 mkdir /tmp/download/ && cd /tmp/download/ # 下载 wget http://pecl.php.net/get/[extension].tgz # 解压 tar -xvf [extension].tgz cd [extension] # 配置 phpize # 查看参数 ./configure --help # 设置参考 以实际情况为准 ./configure --enable-[extension] # 编译 make && make test # 安装 make install # 查看 ini 路径 php --ini # 开启 vi [php-ini].ini extension=[extension].so gcc is currently not installed yum install gcc ...already installed and latest version... gcc ...gcc is currently not installed. yum reinstall gcc # 重新安装后解决 GrpcLb::TokenAndClientStatsAttribute::ToString() const 原因:gcc 版本过低。 ...

October 13, 2021 · 1 min · 175 words · Me

PHP __invoke 使用

why they are magic? Because they are magically called by PHP when specific actions happen. The __invoke() method is called when a script tries to call an object as a function. <?php class CallableClass { public function __invoke($x) { var_dump($x); } } $obj = new CallableClass; $obj(5); var_dump(is_callable($obj)); int(5) bool(true) 使用明显的操作方法初始化 例如,当我们有一个提供者时,就会发生这种情况。 aws-sdk-php/src/Endpoint/PatternEndpointProvider.php public function __invoke(array $args = []) { $service = isset($args['service']) ? $args['service'] : ''; $region = isset($args['region']) ? $args['region'] : ''; $keys = ["{$region}/{$service}", "{$region}/*", "*/{$service}", "*/*"]; foreach ($keys as $key) { if (isset($this->patterns[$key])) { return $this->expand( $this->patterns[$key], isset($args['scheme']) ? $args['scheme'] : 'https', $service, $region ); } } return null; } 它使用 invoke 使用一些参数提供端点。我们如何使用这个类? ...

July 20, 2021 · 2 min · 305 words · Me