PHP 8.1 核心特性

简介:

  自 PHP 8.0 发布以来,该语言经历了一系列重要的功能迭代与性能改进。对于开发者而言,系统理解这些新特性不仅是技术更新的需要,更是优化代码设计、降低维护成本的有效途径。本分类将按版本梳理 PHP 8.x 的新特性。

8.1官方手册参考指南(含其他特性):官方链接

一、枚举

  PHP8.1 正式引入枚举(Enumeration,简称 Enum)特性,解决了传统开发中用「类常量」「数组」管理固定值集合时的类型不安全「取值范围无法约束」「语义不清晰」等问题。

  核心概念:枚举是一种特殊的自定义数据类型,用于定义一组命名且不可变的常量集合,强制变量只能取集合内的某个值。PHP 枚举分为两种核心类型(纯枚举、带值枚举),且所有枚举本质上是「final 类」,无法继承、无法实例化(单例设计)。

纯枚举示例:

php 复制代码
<?php
declare(strict_types=1);

/**
 * 纯枚举类 
 * 无关联值,仅表示「命名常量」
 * 标识状态(如开关)
 */
enum EnumExample{
    case Active; // 激活
    case Inactive; // 未激活
    case Banned; // 封禁
}
$status = EnumExample::Active;
var_dump($status->name); // string(6) "Active"

带值枚举示例:

php 复制代码
<?php
declare(strict_types=1);

/**
 * 带值枚举 
 * 关联标量值(仅 int/string),且值类型统一
 * 需要状态 + 对应值(如编码)
 */
enum HttpEnumExample : int {
    // 通常用于 GET 和 POST请求成功, 服务器已成功处理同时响应体中包含所请求的数据
    case OK = 200;
    // 临时重定向​​, 常用于临时活动页或登录后跳转
    case FOUND = 302;
    // 请求参数错误
    case BAD_REQUEST = 400;
    // ​​未认证​​。请求要求用户的身份验证
    case UNAUTHORIZED = 401;
    // 服务器内部错误
    case INTERNAL_SERVER_ERROR = 500;
}

$http = HttpEnumExample::OK;
var_dump($http->name); // string(2) "OK"
var_dump($http->value); // int(200) 注:纯枚举无此属性

枚举的核心特性:

  1. 内置默认方法

    方法 适用类型 作用
    cases(): array 所有枚举 返回枚举所有案例的数组(按定义顺序)
    from(mixed $value) 带值枚举 根据值查找枚举案例,值不存在则抛出 ValueError 异常
    tryFrom(mixed $value) 带值枚举 根据值查找枚举案例,值不存在则返回 null(推荐替代 from

    示例:

    php 复制代码
    echo '<pre>';
    $httpArray = HttpEnumExample::cases();
    foreach($httpArray as $http){
        var_dump($http->name.'->'.$http->value); // string(7) "OK->200" ...
    }
    
    // from():值存在返回案例,不存在抛异常
    $isOk = HttpEnumExample::from(200);
    
    // $isFound = TestHttpEnum::from(303); //抛出异常
    var_dump($isOk); // 枚举案例 enum(HttpEnumExample::OK)
    
    // tryFrom():值不存在返回null(更安全) 
    $isOk = HttpEnumExample::tryFrom(200);
    $isFound = HttpEnumExample::tryFrom(303); 
    var_dump($isOk); // 枚举案例 enum(HttpEnumExample::OK)
    var_dump($isFound); // NULL
  2. 自定义方法与构造函数,枚举支持定义普通方法「静态方法」。甚至私有构造函数(禁止外部实例化),但构造函数必须是 private(枚举默认单例)注:PHP8.4版本已经禁用

    示例:

    php 复制代码
    <?php
    declare(strict_types=1);
    
    enum HttpEnumExample : int {
        case OK = 200;
        case FOUND = 302;
        case BAD_REQUEST = 400;
        case UNAUTHORIZED = 401;
        case INTERNAL_SERVER_ERROR = 500;
    
        /**
         * 自定义方法:获取状态的中文描述
         * @return string
         */
        public function getLabel() : string{
            return match($this){
                self::OK => "请求成功",
                self::FOUND => "临时重定向​",
                self::BAD_REQUEST => "请求参数错误",
                self::UNAUTHORIZED => "​未认证",
                self::INTERNAL_SERVER_ERROR => "服务器内部错误"
            };
    
        }
    
        /**
         * 静态方法:批量获取「值-标签」映射
         * @return array
         */
        public static function getLabelMap(): array{
            $map = [];
            foreach(self::cases() as $case){
                    $map[$case->value] = $case->getLabel();
            }
            return $map;
        }
    }
    
    $status = HttpEnumExample::OK;
    echo '<pre>';
    var_dump($status->getLabel()); // 请求成功
    var_dump($status::getLabelMap()); // 数组 [200 => "请求成功",...]
  3. 类型提示与类型安全。枚举可作为参数「返回值」「属性」的类型提示,强制传入 / 返回的值必须是枚举案例,杜绝非法值。

  4. 枚举的比较,枚举案例是单例对象,推荐使用「严格比较(===)」,松散比较(==)也可但不推荐

  5. 反射(ReflectionEnum),PHP 提供 ReflectionEnum 类(继承自 ReflectionClass),用于动态获取枚举信息

  6. 序列化与反序列化,枚举支持 serialize()/unserialize(),且反序列化后仍指向原单例对象,保证一致性

补充:注意事项与最佳实践

核心限制:

  枚举是 final 类,无法继承;

  枚举不能实例化(new OrderStatus() 抛 Error);

  带值枚举的值必须唯一(重复值抛 Fatal Error);

  枚举不能定义属性(仅能通过 name/value 访问固定值)

最佳实践:

  优先使用 tryFrom() 而非 from():避免未捕获的 ValueError,提升代码健壮性;

  枚举方法保持单一职责:仅处理与枚举本身相关的逻辑(如标签转换),不写复杂业务逻辑;

  结合接口统一契约:多个枚举需实现相同逻辑时,通过接口约束;

  用 match 替代 switch:穷尽检查确保覆盖所有案例,避免遗漏;

  带值枚举优先用 int 类型:便于扩展(如后续加状态时,int 比 string 更灵活)

二、只读属性

  PHP8.1 引入的只读属性是对对象属性不可变性的语法级 增强。只读属性是通过 readonly 关键字修饰的实例属性,仅能在对象初始化阶段(构造函数内)赋值一次,赋值后任何修改操作都会触发 Error 异常,从语法层面强制保障属性的不可变性。

语法规则:

  1. readonly 关键字必须位于访问修饰符(public/protected/private)之后、类型声明之前;

  2. PHP8.1 中,只读属性必须显式声明类型(不能省略类型,也不能用 mixed,PHP8.2 开始支持 mixed);

  3. 只读属性仅支持实例属性,不能修饰静态属性(static);

  4. 支持所有合法的类型声明(标量、对象、联合类型、Nullable 类型等)

  注意规避「浅只读」坑点。若属性类型为「标量(int/string/bool/float)」:完全不可修改;若属性类型为「数组 / 对象」:属性本身的引用不可改,但数组元素 / 对象属性可自由修改。

示例:

php 复制代码
<?php

declare(strict_types=1);

class Example
{
    public readonly ?string $msg;

    public function __construct(
        ?string $msg = '信息',
        public readonly string $otherMsg = '另一个信息'
    ) {
        $this->msg = $msg === '信息' ? '第一次改变信息' : $msg;
        // 使用属性构造器提升,其属性赋值在进入构造函数函数体之前就已经赋值完成了。
        // $this->otherMsg = '第一次改变另一个信息'; // 这行代码会抛出错误,因为 otherMsg 已经被赋值了,并且是只读的。

    }

    public function getChangeMsg(): string
    {
        $this->msg = '尝试第二次改变信息';
        return $this->msg;
    }
}

$exampleClass = new Example();
echo '<pre>';
try {
    var_dump($exampleClass->msg); // 输出: string(9) "信息"
    var_dump($exampleClass->otherMsg); // 输出: string(12) "另一个信息"
    var_dump($exampleClass->getChangeMsg()); // 抛出异常
} catch (Error $e) {
    echo  $e->getMessage(); // Cannot modify readonly property Example::$msg
}

最佳实践:

  按需使用:仅对「赋值后无需修改」的属性使用 readonly,避免滥用(如业务属性需要动态修改的,不要用);

  结合构造函数参数提升:简化代码,提升可读性(推荐写法);

  初始化校验:在构造函数内对只读属性做合法性校验(如金额非负、邮箱格式正确);

三、一阶可调用语法(First-class)

  一阶可调用语法是 PHP 8.1 推出的语法糖级核心特性,官方定义:Callable 表达式语法,用于极简、安全、优雅地创建可调用对象(Callable),彻底解决了传统 PHP 中回调写法丑陋、无类型检查、IDE 不识别、易写错的痛点。

  语法:用 函数/方法名(...) 的语法,直接将任意可调用元素转换为标准闭包(Closure),... 是固定语法标记(非可变参数、非参数展开)

示例:

php 复制代码
<?php

declare(strict_types=1);

class Example
{
    public function getMsg(string $msg = '')
    {
        return $msg ? $msg : '执行了';
    }

    public static function getStaticMsg(string $msg = '')
    {
        return $msg ? $msg : '执行了';
    }
}

/** 传统写法 */
echo '<pre>';
// 字符串函数名
$func = 'strlen';
var_dump($func('把"strlen"这个函数当做一个变量来传递,$func("hello") 相当于调用 strlen("hello")')); // 99
// 数组静态方法 
$staticMethod = [Example::class, 'getStaticMsg'];
var_dump($staticMethod()); // 执行了
// 数组对象方法
$test = new Example();
$objectMethod = [$test, 'getMsg'];
var_dump($objectMethod()); // 执行了

/** 一级可调用语法  */
$func1 = strlen(...);
var_dump($func1('普通函数:类型安全、IDE 识别')); // 40

$staticMethod1 = Example::getStaticMsg(...);
var_dump($staticMethod1('静态方法:语法简洁、无数组冗余')); // 静态方法:语法简洁、无数组冗余

$objectMethod1 = $test->getMsg(...);
var_dump($objectMethod1('对象方法:语义直观、自动绑定作用域')); // 对象方法:语义直观、自动绑定作用域

// 闭包 / 匿名函数
$add = fn($a, $b) => $a + $b;
$callable = $add(...);
var_dump($callable(1, 2)); // 3

$arr = ['apple', 'banana', 'cherry'];
var_dump(array_map('strlen', $arr)); // 旧写法 数据[0=>5, 1=>6, 2=>6]
var_dump(array_map(strlen(...), $arr)); // 新写法 数据[0=>5, 1=>6, 2=>6]

  注意:不支持通过此语法(例如 new Foo(...))创建对象,因为不会视 new Foo() 语法为调用;一级可调用语法不能与 nullsafe 运算符结合使用

四、新的初始化器

  PHP 8.1 核心的语法升级之一,允许在「编译期可解析的上下文」中,直接使用 new 类名(参数) 作为默认值 / 初始值,但需要满足"常量表达式"的要求,禁止运行时变量、函数、动态值。

这一特性主要涵盖四个场景:

  1. 函数/方法参数默认值(支持箭头函数)

    示例:

    php 复制代码
    <?php
    declare(strict_types=1);
    
    class Example
    {
        public function __construct(
            public DateTime $createTime = new DateTime()
        ){}
    }
    
    $ExampleClass = new Example();
    var_dump($ExampleClass->createTime->format('Y-m-d H:i:s')); // 输出当前时间
  2. 静态变量初始化器

    php 复制代码
    <?php
    
    declare(strict_types=1);
    
    function getDateTimeStr()
    {
        static $date = new DateTime(); // 低版本会触发error
        return $date->format('Y-m-d H:i:s');
    }
    
    var_dump(getDateTimeStr()); // 当前时间
  3. 属性参数(支持嵌套属性)

    php 复制代码
    <?php
    
    declare(strict_types=1);
    
    // 普通类
    class Role {
        public function __construct(public string $name) {}
    }
    
    // 注解(Attribute) 类
    #[Attribute]
    class Auth { 
        public function __construct(public Role $role = new Role("默认管理员")) {}
    }
    
    // 用法1:不传参 → 自动使用 new 初始化的默认对象
    #[Auth]
    class Admin {}
    
    // 用法2:手动传 new 对象 → 注解参数直接传实例
    #[Auth(new Role("普通用户"))]
    class User {}
    
    echo "<pre>";
    // 获取Admin的注解
    $adminAttr = new ReflectionClass(Admin::class)->getAttributes(Auth::class)[0]->newInstance();
    echo "Admin 权限:" . $adminAttr->role->name; // Admin 权限:默认管理员
    
    // 获取User的注解
    $userAttr = new ReflectionClass(User::class)->getAttributes(Auth::class)[0]->newInstance();
    echo "User 权限:" . $userAttr->role->name; // User 权限:普通用户
  4. 全局常量(类常量存在限制)

    php 复制代码
    <?php
    
    declare(strict_types=1);
    
    class Example {
        // ❌ 非法:类常量不支持 new
        // public const OBJ = new stdClass();  // Fatal error
    
        // ❌ 非法:类属性(静态)不支持 new
        // public  $obj = new stdClass();  // Fatal error
    
        public function __construct(public string $name = 'admin') {}
    }
    
    // ✅ 合法:全局常量支持 new
    const DEFAULT_ROLE = new Example();
    print_r(DEFAULT_ROLE->name);

    补充(明确禁止的语法):

    php 复制代码
    <?php
    
    declare(strict_types=1);
    
    class Example {
        // ❌ 类属性、类常量的明确排除 均不合法
        public static $classOne = new stdClass();
        public $classTwo = new stdClass();
        public const CLASS_THREE = new stdClass(); 
    }
    
    // ❌ 禁止:动态类名
    function funOne($a = new (CLASS_NAME_CONSTANT)()){}
    // ❌ 禁止:匿名类
    function funTwo($a = new class {}){}
    // ❌ 禁止:参数解包
    function funThree($a = new A(...[])){}
    // ❌ 禁止:非常量表达式作为参数
    function funFour($a = new B($someVariable)){}

五、纯交集类型

  PHP 8.1 引入了 纯交集类型(Pure Intersection Types),允许开发者使用 & 符号将多个类/接口类型 组合起来,表示一个值必须同时是所有这些类型的实例。"纯"的含义:目前 PHP 的交集类型只能包含类类型(类名、接口名),不能包含标量类型(如 intstring)。这与联合类型(可包含标量)形成对比。

  可用于:参数类型、返回类型、属性类型、instanceof 操作符右侧

示例:

php 复制代码
<?php

declare(strict_types=1);

function process(Iterator & Countable $value): int {
    // $value 必须同时实现 Iterator 和 Countable
    foreach ($value as $item) { /* ... */ }
    return count($value);
}

$data = ['apple', 'banana', 'cherry'];
// ArrayIterator 既实现 Iterator,也实现 Countable
$iterator = new ArrayIterator($data);
var_dump(process($iterator)); // 输出: int(3)

  补充:交集类型可以与联合类型嵌套,但不能直接在同一个类型声明中混用 &|(除非使用括号)。其中,& 的优先级高于 |,但直接书写 A&B|C 会被解释为 (A&B)|C,然而由于语法限制,这样的写法会被拒绝,必须显式使用括号。

php 复制代码
<?php

declare(strict_types=1);

// ✅ 合法:表示 $value 要么是 (A 且 B),要么是 C
function fun1((A & B) | C $value): void {}
// ❌ 错误!会解析为 (A&B)|C 吗?实际上会报错,因为优先级不明确
function fun2( A & B | C $value): void {}

六、Never 返回类型

  在 PHP 8.1 之前,函数可以声明返回类型为 void,表示该函数不返回任何值(即没有 return 语句,或者 return; 不带值)。但有些函数不仅不返回值,它们根本不会正常返回------要么抛出异常,要么调用 exit/die 终止脚本执行。PHP 8.1 引入了 never 类型来明确标记这种"永不返回"的函数,让开发者和静态分析工具能够更准确地理解程序的控制流。

  范围:never 作为返回类型声明,用于函数或方法定义中,也可以用于闭包和箭头函数。

  规则:never 类型只能在返回类型位置使用;声明为 never 的函数或方法绝对不能正常返回(即不能有 return; 或 return $value; 语句,除非该语句在异常或 exit 之后且不可达);如果函数声明为 never 却正常返回,PHP 会抛出一个编译时错误(或运行时 TypeError)。

示例:

php 复制代码
<?php

declare(strict_types=1);

function redirect(string $url): never {
    header("Location: $url");
    exit();
}

function alwaysThrows(): never {
    throw new RuntimeException("Something went wrong");
}

$handler = function(): never {
    throw new Exception("Abort");
};

$arrow = fn(): never => throw new Exception("Abort");

七、Final 类常量

  PHP 8.1 之前,类常量可以被任何继承它的子类重新定义,即使是在 final 类中定义的常量也是如此。这种行为虽然灵活,但也带来了问题:父类常量的语义可能被子类无意或有意地改变,破坏了封装性。PHP 8.1 引入了 final 修饰符用于类常量,声明后该常量 不能被子类重写。

  语法:final 可以与其他可见性修饰符(publicprotectedprivate)一起使用。它的位置在可见性修饰符之前/后,常量名之前。

示例:

php 复制代码
<?php

declare(strict_types=1);

class A {
    // 两种均可用,官方示例为:final public
    final public const X = 1;
    public final const Y = 2;

    public const Z = 3;
}

class B extends A {
    public const Z = 4; // 会重写
 
    public const X = 2; // 不能被重写,抛出 Fatal error: Cannot override final constant A::X
}

八、对字符串键控数组的数组解包支持

  在 PHP 8.1 之前,数组解包(...)仅支持数字索引数组,遇到字符串键名时会直接抛出错误。PHP 8.1 通过 RFC 投票正式打破了这个限制,采用 array_merge 的语义实现了对字符串键数组的解包支持。

  1. 采用 array_merge 语义,后者覆盖前者。

  这是理解该特性的关键。当使用 ... 展开数组时,其行为与 array_merge 函数保持一致:对于字符串键,如果键名重复,后面出现的值会覆盖前面的值。

示例:

php 复制代码
<?php

declare(strict_types=1);

$array1 = ["a" => 1];
$array2 = ["a" => 2, "b" => 3];

$result = ["a" => 0, ...$array1, ...$array2];
var_dump($result); // 输出:Array ( [a] => 2 [b] => 3 )
  1. 数字键的例外:保持"追加"语义。

  对于数字索引(或数字字符串键),PHP 8.1 依然保留了原有的"重编号(Renumbering)"行为,不会保留原有的数字键名,而是像列表一样追加。

示例:

php 复制代码
<?php

declare(strict_types=1);

$arr1 = [1 => "one", 2 => "two"];
$arr2 = ["3" => "three"]; // 字符串数字键,会被转换为整数 3

$result = [...$arr1, ...$arr2];
var_dump($result); // 输出:Array ( [0] => one [1] => two [2] => three )
  1. 与 + 运算符的核心区别:前者"后覆盖",后者"前保留"。

  数组解包 (...) => 行为逻辑:后者覆盖前者 (类似 array_merge);数字键:重新编号,不保留原键;关联键:后面覆盖前面。

  + 运算符 => 行为逻辑:前者保留,后者忽略 (类似"First Wins");数字键:保留原数字键,但仅当键不存在时才追加;关联键:前面优先,后面被丢弃。

示例:

php 复制代码
<?php

declare(strict_types=1);

$arr1 = ["a" => 1, 1 => "one"];
$arr2 = ["a" => 2, 2 => "two"];
// 使用 ... 解包 字符串键覆盖,数字键重排
$unpack = [...$arr1, ...$arr2];
print_r($unpack); // 输出:Array ( [a] => 2 [0] => one [1] => two )

// 使用 + 运算符 字符串键保留第一个,数字键保留原样且不冲突的追加
$plus = $arr1 + $arr2;
print_r($plus); // 输出:Array ( [a] => 1 [1] => one [2] => two )
  1. 为什么是 array_merge 而非"直接丢弃"?

  在开发该特性时,社区曾讨论过另一种方案:直接丢弃字符串键,将其视为普通值重排(类似 [...$arr1] 仅提取值)。但最终选择 array_merge 语义,主要有两个原因:

    直觉一致性:解包操作符 ... 在字面量中可以被理解为把数组内容"写"进去。例如 ["a" => 1, ...["a" => 2]],就像手动写入了两次 "a" 键,后写的值(2)理应生效。

    与参数解包对齐:在 PHP 8.0 中,参数解包(func(...$args))已经支持了字符串键(映射为命名参数)。为了保持语法统一,数组解包也不应简单丢弃这些键。

九、纤程(Fiber)

  PHP 8.1 正式引入了纤程(Fiber) 特性,这是 PHP 在语言层面首次原生支持协作式多任务(cooperative multitasking)的轻量级并发机制。在此之前,PHP 开发者主要依靠生成器(Generator)或第三方扩展(如 Swoole、ReactPHP)来实现异步非阻塞编程。纤程的加入,使得 PHP 能够以更优雅、更灵活的方式处理 I/O 密集型任务,并为构建高并发应用提供了新的可能性。

  定义:纤程是一种协程(coroutine)的实现,它允许在单线程内实现多个执行流的切换。与线程不同,纤程的切换完全由开发者显式控制(协作式),而非操作系统抢占式调度。每个纤程拥有自己的调用栈,可以在任意位置挂起(suspend),并将控制权交还给主调用者,稍后再从挂起点恢复(resume) 继续执行。

部分应用场景示例:

  1、并发调用第三方 API :同时发起 3 个请求,挂起等待,所有响应返回后自动恢复,总耗时 = 最慢的那个接口的耗时

  2、并行数据库查询:避免 "查完 A 表再查 B 表" 的串行等待,数据库查询时间从 T1+T2+T3 缩短为 max(T1,T2,T3)

  3、非阻塞缓存操作:缓存操作是典型的网络 IO,纤程能让等待缓存响应的时间不浪费。例如:同时从 Redis/Memcached 获取多个 key 的值(如get('user:1') + get('product:100')

  4、文件上传与处理:文件 IO 是慢操作,纤程能避免阻塞进程,同时处理多个文件任务。

  5、后台任务队列:相比传统的 "多进程消费队列",纤程能在一个进程内同时处理成百上千个任务,内存占用极低。

  6、定时任务调度:避免 "一个定时任务阻塞其他任务",无需为每个任务开一个进程 / 线程。

  7、日志异步写入:日志写入是高频 IO 操作,纤程能让主业务逻辑不受日志写入速度影响

  8、WebSocket 实时通信:比多进程,纤程能在一个进程内维护数万 WebSocket 连接,内存占用仅为多进程的几十分之一。

  9、批量数据转换:据处理中往往夹杂着大量文件 / 数据库 IO,纤程能让 CPU 和 IO 设备同时忙碌

示例:

php 复制代码
<?php

declare(strict_types=1);

/**
 * 定义 Task 类,用于封装纤程任务及其当前结果
 */
class Task {
    public function __construct(
        public Fiber $fiber,   // 存储纤程对象
        public mixed $result = null  // 存储每次恢复时传入/返回的值
    ) {}
}

/**
 * 定义调度器类,管理多个任务的轮询执行
 */
class Scheduler {
    // 存储所有 Task 对象的数组
    private array $tasks = [];

    /**
     * 添加一个可调用任务到调度器
     * @param callable $fn 要执行的任务函数
     * @param mixed ...$args 传递给任务函数的参数
     */
    public function add(callable $fn, mixed ...$args): void {
        // 创建一个新的纤程,其回调是一个闭包,用于调用 $fn 并传递参数
        $fiber = new Fiber(function () use ($fn, $args) {
            // 执行用户任务并返回结果
            return $fn(...$args);
        });
        // 将任务封装为 Task 对象,存入任务数组
        $this->tasks[] = new Task($fiber);
    }

    /**
     * 运行调度器,轮询执行所有任务直到完成
     */
    public function run(): void {
        // 当还有未完成的任务时持续循环
        while (!empty($this->tasks)) {
            // 遍历当前所有任务
            foreach ($this->tasks as $i => $task) {
                // 获取当前任务关联的纤程对象
                $fiber = $task->fiber;

                // 情况1:纤程已启动且尚未终止(即处于挂起状态) -> 恢复执行
                if ($fiber->isStarted() && !$fiber->isTerminated()) {
                    // 调用 resume() 恢复纤程,并将上次挂起时保存的 result 作为参数传入
                    // resume() 返回纤程下一次挂起时传出的值(或 null 如果纤程结束)
                    $task->result = $fiber->resume($task->result);
                }
                // 情况2:纤程尚未启动 -> 首次启动
                elseif (!$fiber->isStarted()) {
                    // 调用 start() 启动纤程,执行到第一个挂起点
                    // start() 返回第一个 Fiber::suspend() 传出的值
                    $task->result = $fiber->start();
                }

                // 如果纤程已经终止(执行完毕或抛出异常),则输出返回值并从任务列表中移除
                if ($fiber->isTerminated()) {
                    // 获取纤程的返回值(即任务函数的 return 值)
                    echo "Task finished with return: ", $fiber->getReturn(), "\n";
                    // 从任务数组中删除当前任务
                    unset($this->tasks[$i]);
                }
            }
            // 一轮遍历结束后,继续 while 循环,处理可能剩余的任务
        }
    }
}

// 使用示例:创建调度器
$scheduler = new Scheduler();
echo '<pre>';
// 添加任务 A:先输出 step1,挂起,然后输出 step2,返回字符串
$scheduler->add(function () {
    echo "Task A: step 1\n";
    Fiber::suspend();          // 挂起,控制权交回调度器
    echo "Task A: step 2\n";
    return "A done";
});

// 添加任务 B:结构与任务 A 相同
$scheduler->add(function () {
    echo "Task B: step 1\n";
    Fiber::suspend();
    echo "Task B: step 2\n";
    return "B done";
});

// 启动调度器,开始轮询执行
$scheduler->run();
// 执行结果:
// Task A: step 1
// Task B: step 1
// Task A: step 2
// Task finished with return: A done
// Task B: step 2
// Task finished with return: B done
相关推荐
两个人的幸福11 天前
Windows 桌面应用自研 PHP 队列(下):完整代码与六大工程化优化
php
BingoGo13 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
JaguarJack13 天前
PHP 泛型之殇 泛型 RFC 提案被拒绝
后端·php
用户30745969820714 天前
PHP 扩展——从入门到理解
php
鹏仔先生14 天前
拷贝漫画APP下载页PHP程序,后台带免费AI写作
php
云水一下15 天前
从零开始学 PHP 系列(一):PHP 的前世今生与开发环境搭建
开发语言·php
xingpanvip15 天前
星盘接口开发文档:本命盘接口指南
android·开发语言·css·php·lua
酉鬼女又兒15 天前
零基础入门计算机网络运输层:端到端通信核心作用、端口号分类规则、复用分用工作机制及UDP与TCP协议全方位对比详解
网络·网络协议·tcp/ip·计算机网络·考研·udp·php
dog25015 天前
不要再继续优化 TCP
网络协议·tcp/ip·php
Channing Lewis15 天前
PHP 解析 Excel 的那些坑:一次“行号错位”引发的数据丢失
开发语言·php·excel