Skip to content

Scheduler Sample Application

Build an app that runs recurring maintenance jobs on fixed cadences. You put #[Scheduled] on zero-argument methods of a #[Service] bean and enable scheduling on the application class. The framework runs each tick in a dedicated worker process, so a slow job never blocks web requests.

Use scheduled tasks for work that runs on a fixed cadence — cleanup, reports, cache warming. Use a daemon thread instead when the work must run continuously.

You need PHP 8.5 or later with the swoole and pcntl extensions. No external services are needed.

The sample uses this layout:

scheduler/
├── bin/
│ └── application.php # Application entry point
├── config/
│ └── application.yml # Scheduling pool config
├── src/
│ ├── SchedulerSampleApplication.php # Main application class
│ └── schedule/
│ └── MaintenanceScheduler.php # Scheduled job methods
└── composer.json # Dependencies

Require the framework package:

Terminal window
composer require suvera/winter-boot

Switch between the source files. Each tab shows the exact file from the sample.

The single entry point. #[EnableScheduling] activates the #[Scheduled] ticks in the scanned namespace.

<?php
namespace dev\example;
use dev\winterframework\stereotype\task\EnableScheduling;
use dev\winterframework\stereotype\WinterBootApplication;
#[WinterBootApplication(
configDirectory: [__DIR__ . "/../config"],
scanNamespaces: [
['dev\\example', __DIR__ . '']
]
)]
#[EnableScheduling]
class SchedulerSampleApplication {
public static function main(): void {
$winterApp = new \dev\winterframework\core\app\WinterWebSwooleApplication();
$winterApp->run(self::class);
}
}

No module is needed — scheduling is a core framework feature. The full sample application.yml sets the server, app identity, and the scheduling worker pool:

server:
port: 8080
address: 0.0.0.0
context-path: /
winter:
application:
name: Scheduler Sample Application
id: scheduler-sample-app
version: 1.0.0
task:
scheduling:
poolSize: 1
queueCapacity: 50

See Configuration for every application.yml key.

Start the app and trigger the jobs with short delays to verify them quickly.

1. Start the application:

Terminal window
composer install
php bin/application.php

2. Seed a stale temp file and watch the hourly tick:

Terminal window
mkdir -p /tmp/scheduler-tmp
touch -d "2 hours ago" /tmp/scheduler-tmp/stale.tmp

About a minute after boot (initialDelay: 60), the app log shows the cleanup result:

Hourly cleanup deleted 1 temp files

3. Check the daily summary log:

Terminal window
cat /tmp/scheduler.log

About two minutes after boot (initialDelay: 120), the summary line appears:

[2026-09-10 12:02:00] Daily summary: tmp dir holds 0 files

The short initial delays let you verify both ticks without waiting an hour or a day — the steady-state cadence stays hourly and daily via fixedDelay.

  • Read Scheduling for fixedRate, placeholder-driven intervals, and pool tuning.
  • Read the Daemon example when your work must run continuously instead of on a cadence.