{"record":{"id":"5773024d6ee79cb7","repo":"walkor/workerman","slug":"timer-can-only-be-used-in-workerman-running-enviro","errorCode":null,"errorMessage":"Timer can only be used in workerman running environment","messagePattern":"Timer can only be used in workerman running environment","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"src/Timer.php","lineNumber":157,"sourceCode":"     * @return int\n     */\n    public static function add(float $timeInterval, callable $func, ?array $args = [], bool $persistent = true): int\n    {\n        if ($timeInterval < 0) {\n            throw new RuntimeException('$timeInterval can not less than 0');\n        }\n\n        if ($args === null) {\n            $args = [];\n        }\n\n        if (self::$event) {\n            return $persistent ? self::$event->repeat($timeInterval, $func, $args) : self::$event->delay($timeInterval, $func, $args);\n        }\n\n        // If not workerman runtime just return.\n        if (!Worker::getAllWorkers()) {\n            throw new RuntimeException('Timer can only be used in workerman running environment');\n        }\n\n        if (empty(self::$tasks)) {\n            pcntl_alarm(1);\n        }\n\n        $runTime = (int)floor(time() + $timeInterval);\n        if (!isset(self::$tasks[$runTime])) {\n            self::$tasks[$runTime] = [];\n        }\n\n        self::$timerId = self::$timerId == PHP_INT_MAX ? 1 : ++self::$timerId;\n        self::$status[self::$timerId] = true;\n        self::$tasks[$runTime][self::$timerId] = [$func, (array)$args, $persistent, $timeInterval];\n\n        return self::$timerId;\n    }\n","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/walkor/workerman/blob/1391112a61d23020e11e7b89f17050f6cfaea431/src/Timer.php#L139-L175","documentation":"Timer::add() needs either an installed event loop (set via Timer::init by a running Worker) or Worker instances registered in the process. When both are absent - i.e. the code is executing outside a Workerman runtime - it cannot schedule anything and throws instead of pretending to work.","triggerScenarios":"Calling Timer::add() in a plain CLI script or unit test that never starts workers; calling it in bootstrap code before Worker::runAll()/Worker::run() has initialized the event loop; a shared library file used both by Workerman workers and standalone cron scripts.","commonSituations":"Running part of the application via 'php bin/job.php' for debugging; PHPUnit bootstraps that touch code paths using Timer; composer post-install scripts reusing app code; calling Timer::add at file top-level instead of inside a worker callback.","solutions":["Move Timer::add() calls into worker lifecycle callbacks, typically onWorkerStart, where the event loop exists","If you need a timer outside Workerman, use a different mechanism (pcntl_alarm, usleep loop, sched/cron) guarded by an environment check","For tests, initialize the loop explicitly (Timer::init(new Workerman\\Events\\Select())) or mock the Timer calls"],"exampleFix":"// before\nTimer::add(1, fn() => echo 'tick'); // top of script, no worker running -> throws\n$worker = new Worker('http://0.0.0.0:8080');\nWorker::runAll();\n\n// after\n$worker = new Worker('http://0.0.0.0:8080');\n$worker->onWorkerStart = function () {\n    Timer::add(1, fn() => echo 'tick'); // loop is ready here\n};\nWorker::runAll();","handlingStrategy":"validation","validationCode":"if (!Timer::initialized() && !Worker::getAllWorkers()) { // guard before scheduling\n    error_log('Timer skipped: not running under Workerman');\n    return;\n}\nTimer::add(1, $cb);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only call Timer::add from worker callbacks (onWorkerStart/onConnect/onMessage), never from top-level bootstrap","Give shared libraries an explicit runtime flag so the same file can run under Workerman and standalone cron"],"tags":["php","workerman","timer","bootstrap","runtime-context"],"backgroundTag":"wrong-runtime-context","analyzedSha":"1391112a61d23020e11e7b89f17050f6cfaea431","analyzedAt":"2026-08-21T02:05:46.744Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}