php start.php reload 并非万能热更新,仅对 onMessage/onRequest 中 require/include 的未被 autoloader 加载的 PHP 文件生效;改 app/controller/ 类、config/bootstrap.php 或静态定义均无效,因 Composer 缓存和进程内存状态不重置。

Webman 的 php start.php reload 为什么改了代码没反应
它不是“改完就生效”的热更新,而是向 Worker 进程发 SIGUSR1 信号,触发进程逐个退出重启——但只对「运行时动态 require/include」的文件有效。Composer 自动加载的类(比如 app/controller/ 下的控制器)、在 onWorkerStart 里一次性载入的逻辑层(如 app/logic/OrderSync.php),reload 后完全不会重新解析。
常见误判场景:
- 改了
app/controller/UserController.php,刷新页面没变化 → 因为 Composer autoloader 已缓存类定义,reload 不会清 autoload cache - 改了
modules/admin/src/service/ReportService.php,var_dump(get_class_methods())还是旧方法 → 插件类走的是 PSR-4 autoload 通道,不是 runtime require - 热更新后登录态错乱,a 用户看到 b 用户的缓存 → 静态属性或单例未重置,新 Worker 进程复用旧内存状态
哪些代码能真正被 reload 生效
只有满足两个条件的 PHP 文件才会在 reload 后重新执行:
- 在
onMessage、onRequest或类似回调中,用require或include显式引入(不是require_once) - 该文件未被 Composer autoloader 提前注册(即不在
autoload.psr-4或autoload.classmap中声明)
典型可用路径:
-
app/handler/下的 WebSocket 消息处理器(若用require加载业务逻辑) -
app/middleware/中手动include的临时中间件文件(不通过容器自动解析) - 路由闭包内直接
require的脚本(如Route::get('/debug', function () { require 'debug/logic.php'; });)
注意:require_once 会导致第二次加载失败;opcache_reset() 单独调用也无法绕过 autoloader 缓存。
插件和模块如何实现安全热加载
Webman mod 的热加载依赖两套并行机制:Composer autoload 负责类加载,PluginManager 运行时负责生命周期管理。二者不同步,就会出现“文件存在却 class not found”或“修改无反应”。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
正确做法:
- 确保插件目录在
config/plugin.php中声明为['path' => 'modules/*'],且命名空间与composer.json的autoload.psr-4严格匹配 - 避免在插件
boot()方法中注册单例或静态初始化 —— 它只在首次启动执行,reload 后不会重跑 - 若需动态重注册服务,应在
onWorkerStart回调中做判断:检查isset($GLOBALS['plugin_booted']),未设则调用Plugin::getInstance()->boot()并标记 - 插件内所有
require_once的辅助文件(如helpers/validator.php)必须改为require,否则修改后不会重新载入
定时任务怎么做到免重启热更新
Webman 默认的 Crontab 是进程启动时硬编码注册的,改配置就得重启。要实现真正的热更新,得把任务调度从“代码声明”转为“运行时拉取+动态注册”。
关键动作:
- 把任务配置存数据库(字段至少含
command、cron_expr、enabled、last_run_at) - 写一个独立的
TaskManager类,在onWorkerStart中启动协程,每 30 秒查一次 DB,对比本地缓存的任务列表,新增/禁用/调整频率的任务都通过new Crontab(...)动态注册或unset销毁 - 任务闭包内避免闭包捕获外部变量(如
$this或全局静态对象),改用Di::get()每次获取新实例,防止状态残留 - 配合
fileboy监听config/task.php(若用配置文件替代 DB),监听到变更后触发TaskManager::refresh()
真正难的不是“怎么加新任务”,而是“怎么安全停掉正在执行的旧任务”。别直接 unset,先标记为 pending stop,等当前执行完成再清理 —— 否则可能中断事务或丢数据。










