Scheduled Tasks
Plugins koennen wiederkehrende Aufgaben registrieren, die der TaskRunner ueber die CLI abarbeitet — Sitemap-Generierung, Session-Cleanup, Expiry-Checks, Re-Indexing etc. Diese Seite zeigt die Registrierung in bootstrap.php und das Script-Pattern.
Registrierung via $this->scheduledTasks
Im install() des Plugins:
public function install()
{
// ...
$this->scheduledTasks = [
'{
"name": "Session Cleanup",
"script": "session_cleanup.php",
"interval": 300,
"description": "Removes inactive sessions older than 2 hours"
}',
'{
"name": "Generate Sitemap",
"script": "generate_sitemap.php",
"interval": 86400,
"description": "Generates XML sitemap with hreflang from database"
}'
];
}Jeder Eintrag ist ein JSON-String mit vier Feldern:
| Feld | Pflicht | Zweck |
|---|---|---|
name | ja | UI-Label im Task Manager |
script | ja | Dateiname relativ zu {plugin}/scheduledTasks/ |
interval | ja | Intervall in Sekunden (300 = 5 Min, 3600 = 1 h, 86400 = 24 h). Alias interval_seconds wird auch akzeptiert |
description | nein | Text im Task Manager (Tooltip/Info) |
Echtes Beispiel aus scheduledTasks-Plugin (_public/extensions/core/backend/scheduledTasks/bootstrap.php):
$this->scheduledTasks = [
'{"name": "Session Cleanup", "script": "session_cleanup.php", "interval": 300, "description": "Removes inactive sessions older than 2 hours"}',
'{"name": "Generate Sitemap", "script": "generate_sitemap.php", "interval": 86400, "description": "Generates XML sitemap with hreflang from database"}',
'{"name": "Publish Scheduled Pages", "script": "publish_scheduled_pages.php", "interval": 300, "description": "Publishes pages with scheduled publish times that have passed"}'
];Beim installPlugin() ruft der Core installScheduledTasks($pluginID) auf, was pro Eintrag eine Zeile in scheduled_tasks anlegt — next_run wird initial auf NOW() gesetzt, der Task laeuft also beim naechsten Runner-Durchlauf sofort.
Script-Ordner
Die registrierten Scripts liegen im Plugin-Unterordner scheduledTasks/:
_public/extensions/core/backend/{plugin}/
├── bootstrap.php
└── scheduledTasks/
├── session_cleanup.php
├── generate_sitemap.php
└── publish_scheduled_pages.phpScript-Pattern
Ein Scheduled-Task-Script ist eine normale PHP-Datei. Der TaskRunner inkludiert sie via require — das heisst: das Script laeuft im Kontext einer lebendigen DB-Connection, query(), fetch_assoc() etc. stehen bereit:
<?php
// _public/extensions/core/backend/{plugin}/scheduledTasks/cleanup_old_logs.php
// TaskRunner hat DB-Connection schon aufgebaut — keine zusaetzliche Init noetig
$cutoff = date('Y-m-d H:i:s', strtotime('-30 days'));
$cutoffEsc = real_escape_string($cutoff);
$q = query("DELETE FROM my_plugin_logs WHERE created_at < '$cutoffEsc'");
// Fuers Log in der task-Manager-UI sichtbar machen:
echo "Deleted " . mysqli_affected_rows($GLOBALS['connection']) . " old log entries\n";Keine Ausgabe ans Web — das Script laeuft im CLI-Modus. Echo-Output landet im CLI-Log (bzw. im Task Manager, wenn der Runner die Ausgabe cached).
Tasks kurz halten
Der Cron-Aufruf scheduled-tasks --time-limit=540 hat ein Zeitlimit von 9 Minuten (Default des Cronjobs alle 10 Min). Laufen mehrere Tasks gleichzeitig, teilen sie sich das Budget. Fuer lange Arbeiten (Sitemap fuer 10k-Seiten, Bulk-Reindexing): in mehrere kurze Tasks aufteilen oder auf die Job-Queue (QueueWorker) auslagern.
CLI-Runner
Der TaskRunner wird ueber console/bin aufgerufen:
# Alle faelligen Tasks ausfuehren, max. 540s Zeitlimit
php console/bin scheduled-tasks --time-limit=540
# Einzelnen Task sofort starten (unabhaengig von next_run)
php console/bin scheduled-tasks --run=42
# Alle Tasks auflisten
php console/bin list-tasksEmpfohlener Cron-Setup (crontab -e):
# Alle 10 Minuten: scheduled tasks
*/10 * * * * php /pfad/console/bin scheduled-tasks --time-limit=540
# Jede Minute: Job-Queue
* * * * * php /pfad/console/bin process-queue --time-limit=55
# Alle 30 Sekunden: Webhooks
* * * * * php /pfad/console/bin process-webhooks --time-limit=25
* * * * * sleep 30 && php /pfad/console/bin process-webhooks --time-limit=25Wie der TaskRunner Tasks ausfuehrt
Der TaskRunner::runDue()-Flow:
1. SELECT alle Tasks WHERE next_run <= NOW() AND active = 1 AND is_running = 0
2. Fuer jeden Task:
- SET is_running = 1 (Stale-Check + Parallel-Schutz)
- require script
- SET last_run = NOW(), next_run = NOW() + interval_seconds, is_running = 0
3. Wenn Zeit vor Ende der Queue abgelaufen: Rest beim naechsten Runis_running-Flag: Verhindert parallele Ausfuehrung desselben Tasks. Bei Crash bleibt das Flag haengen — dann im Task Manager manuell zuruecksetzen oder UPDATE scheduled_tasks SET is_running = 0 WHERE id = X.
Task Manager UI
Unter /admin/task-manager gibt es drei Tabs:
- Status — APCu-Info, Last-Run pro Task, naechster Run-Zeitpunkt
- Queue — Job-Queue (nicht Scheduled, sondern API-Call-Queue via
QueueWorker) - Scheduled Tasks — Liste aller Tasks mit "Jetzt ausfuehren"-Button und Toggle fuer
active
Die Sidebar zeigt eine Status-Lampe — gruen: Runner laeuft regelmaessig, rot: letzter Run liegt laenger zurueck als erwartet.
Job-Queue vs. Scheduled-Task
Fuer einmalige Jobs (nicht wiederkehrend) gibt es zusaetzlich die Job-Queue:
require_once $_SERVER['DOCUMENT_ROOT'] . '/_core/system/cron/QueueWorker.php';
QueueWorker::createJob('api/pages', 'GET', ['view' => 'test'], 10, 3);
// Endpoint, HTTP-Methode, Params, Priority (hoeher = wichtiger), maxAttemptsDer QueueWorker (via process-queue CLI) macht einen internen cURL-Call gegen die eigene Domain und speichert Response/Status in task_queue. Nuetzlich fuer Fire-and-Forget-Aufgaben aus API-Endpoints, die nicht blockieren sollen.
Details in CLAUDE.md-Sektion "Cron/Task-System" und im Skill php-backend-check.
Datenbank-Tabelle
CREATE TABLE IF NOT EXISTS scheduled_tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
plugin_id INT NOT NULL DEFAULT 0, -- FK → plugins.id
name VARCHAR(255) NOT NULL,
script VARCHAR(255) NOT NULL, -- relativ zu plugin/scheduledTasks/
interval_seconds INT NOT NULL DEFAULT 3600,
description TEXT DEFAULT NULL,
last_run DATETIME DEFAULT NULL,
next_run DATETIME DEFAULT NULL,
is_running TINYINT(1) NOT NULL DEFAULT 0,
active TINYINT(1) NOT NULL DEFAULT 1,
language_short VARCHAR(5) NOT NULL DEFAULT '', -- derzeit ungenutzt, bleibt leer
base_id INT NOT NULL DEFAULT 0, -- derzeit ungenutzt, bleibt 0
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY plugin_id (plugin_id),
KEY active_next_run (active, next_run)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Schema-Migration: _migrations/core/005_create_task_system.sql. language_short und base_id existieren fuer konsistente Core-Tabellen-Konvention, werden vom TaskRunner aber nicht ausgewertet — Scheduled Tasks sind systemweit, nicht sprach-spezifisch.
Haeufige Fehler
Script nicht unter scheduledTasks/ abgelegt
Der TaskRunner sucht unter _public/extensions/core/backend/{plugin}/scheduledTasks/{script}. Liegt die Datei im falschen Ordner (z. B. script/ oder Root des Plugins), wirft der Runner einen File not found-Fehler und markiert den Task als failed.
interval vergessen → Default 3600s
Fehlt das interval-Feld im JSON, nimmt der Installer 3600 (1 Stunde) als Default. Das ist oft nicht was gewollt ist. Immer explizit setzen.
JSON-Syntax-Fehler → Task wird uebersprungen
installScheduledTasks() nutzt json_decode(); bei null-Return (Syntax-Fehler) wird der Eintrag stumm uebersprungen — keine Fehlermeldung, kein Log. Vor dem Commit das JSON mit einem Linter pruefen oder temporaer im Plugin error_log(print_r($task, true)) nach dem json_decode einbauen.
is_running bleibt nach Crash bei 1
Bei PHP-Fatal-Error oder Timeout wird is_running = 0 nicht zurueckgesetzt. Der Task wird beim naechsten Runner-Lauf als "bereits laufend" erkannt und uebersprungen. Manueller Fix: UPDATE scheduled_tasks SET is_running = 0 WHERE id = X.
update() aktualisiert Scheduled Tasks nicht
Aenderungen an $this->scheduledTasks in einer spaeteren Plugin-Version greifen nur bei frischer Installation. Fuer bestehende Installationen: Migration schreiben, die den scheduled_tasks-Eintrag updatet oder neu einfuegt.
Siehe auch
- Plugin-Anatomie —
$this->scheduledTasks-Property,scheduledTasks/-Ordner - Migrations —
_migrations/core/005_create_task_system.sql - Webhook-Events —
process-webhooks-Worker mit demselben CLI-Dispatcher - Task Manager UI:
/admin/task-manager