Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ composer.lock
.phpunit.result.cache
bob
.phpunit.cache
.vscode/
5 changes: 0 additions & 5 deletions .vscode/settings.json

This file was deleted.

6 changes: 5 additions & 1 deletion src/Configuration/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ public function getName(): string
* @param Loader $config
* @return void
*/
abstract public function create(Loader $config): void;
public function create(Loader $config): void
{
// By default, we do nothing here, but you can override this method in your configuration class
// to set up your server or package as needed.
}

/**
* Start the configured package
Expand Down
14 changes: 14 additions & 0 deletions src/Configuration/Loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Bow\Configuration\EnvConfiguration;
use Bow\Application\Exception\ApplicationException;
use Bow\Container\CompassConfiguration;
use Bow\Scheduler\Scheduler;

class Loader implements ArrayAccess
{
Expand Down Expand Up @@ -369,6 +370,19 @@ public function events(): array
];
}

/**
* Define scheduled tasks
*
* Override this method in your Kernel to define scheduled tasks.
*
* @param Scheduler $schedule
* @return void
*/
public function schedules(Scheduler $schedule): void
{
//
}

/**
* @inheritDoc
*/
Expand Down
8 changes: 7 additions & 1 deletion src/Console/Command.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Bow\Console\Command\SeederCommand;
use Bow\Console\Command\ServerCommand;
use Bow\Console\Command\WorkerCommand;
use Bow\Console\Command\SchedulerCommand;
use Bow\Console\Command\MigrationCommand;
use Bow\Console\Command\Generator\GenerateKeyCommand;
use Bow\Console\Command\Generator\GenerateCacheCommand;
Expand Down Expand Up @@ -64,6 +65,11 @@ class Command extends AbstractCommand
"run:server" => ServerCommand::class,
"run:worker" => WorkerCommand::class,
"flush:worker" => WorkerCommand::class,
"schedule:run" => SchedulerCommand::class,
"schedule:work" => SchedulerCommand::class,
"schedule:list" => SchedulerCommand::class,
"schedule:next" => SchedulerCommand::class,
"schedule:test" => SchedulerCommand::class,
"generate:key" => GenerateKeyCommand::class,
"generate:resource" => GenerateRouterResourceCommand::class,
"generate:session-table" => GenerateSessionCommand::class,
Expand Down Expand Up @@ -99,7 +105,7 @@ public function call(string $command, string $action, ...$rest): mixed
$this->throwFailsCommand("The command $command not found !");
}

if (!preg_match('/^(migration|seed)/', $command)) {
if (!preg_match('/^(migration|seed|schedule)/', $command)) {
$method = "run";
} else {
$method = $action;
Expand Down
252 changes: 252 additions & 0 deletions src/Console/Command/SchedulerCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
<?php

declare(strict_types=1);

namespace Bow\Console\Command;

use DateTime;
use Bow\Console\AbstractCommand;
use Bow\Console\Color;
use Bow\Scheduler\Scheduler;
use Bow\Configuration\Loader;

class SchedulerCommand extends AbstractCommand
{
/**
* Run the scheduler once (execute all due events)
*
* @return void
*/
public function run(): void
{
$scheduler = $this->getScheduler();

echo Color::green("Running scheduler...\n");

$results = $scheduler->run();

if (empty($results)) {
echo Color::yellow("No scheduled events are due.\n");
return;
}

foreach ($results as $result) {
$this->displayResult($result);
}

echo Color::green("\nScheduler run completed.\n");
}

/**
* Start the scheduler daemon (continuous loop)
*
* @return void
*/
public function work(): void
{
$scheduler = $this->getScheduler();

echo Color::green("Starting scheduler daemon...\n");
echo Color::yellow("Press Ctrl+C to stop.\n\n");

// Set up custom logger for console output
$scheduler->setLogger(function (string $message) {
echo $message . "\n";
});

$scheduler->start();
}

/**
* List all registered scheduled events
*
* @return void
*/
public function list(): void
{
$scheduler = $this->getScheduler();
$events = $scheduler->getEvents();

if (empty($events)) {
echo Color::yellow("No scheduled events registered.\n");
return;
}

echo Color::green("Registered Scheduled Events:\n");
echo str_repeat('-', 100) . "\n";

printf("%-45s | %-10s | %-15s | %s\n", "Description", "Type", "Expression", "Next Due");
echo str_repeat('-', 100) . "\n";

$now = new DateTime();

foreach ($events as $event) {
$description = $event->getDescription();
$type = $event->getType();
$expression = $event->getCronExpression();
$isDue = $event->isDue($now);

// Truncate long descriptions
if (strlen($description) > 43) {
$description = substr($description, 0, 40) . '...';
}

$dueStatus = $isDue ? Color::green("DUE NOW") : Color::yellow("waiting");

printf(
"%-45s | %-10s | %-15s | %s\n",
$description,
$type,
$expression,
$dueStatus
);
}

echo str_repeat('-', 100) . "\n";
echo Color::green("Total: " . count($events) . " event(s)\n");
}

/**
* Show the next run time for all events
*
* @return void
*/
public function next(): void
{
$scheduler = $this->getScheduler();
$events = $scheduler->getEvents();

if (empty($events)) {
echo Color::yellow("No scheduled events registered.\n");
return;
}

echo Color::green("Next Run Times:\n");
echo str_repeat('-', 80) . "\n";

$now = new DateTime();

foreach ($events as $event) {
$description = $event->getDescription();
$isDue = $event->isDue($now);

$status = $isDue
? Color::green("DUE NOW")
: Color::yellow("waiting");

echo sprintf(
"[%-8s] %-50s %s (%s)\n",
$event->getType(),
$description,
$status,
$event->getCronExpression()
);
}

echo str_repeat('-', 80) . "\n";
}

/**
* Test run a specific event by its index
*
* @param int $index The 0-based index of the event to run
* @return void
*/
public function test(int $index = 0): void
{
$scheduler = $this->getScheduler();
$events = $scheduler->getEvents();

if (empty($events)) {
echo Color::yellow("No scheduled events registered.\n");
return;
}

if ($index < 0 || $index >= count($events)) {
echo Color::red("Invalid event index: {$index}\n");
echo Color::yellow("Use 'php bow schedule:list' to see available events (0-indexed).\n");
return;
}

$event = $events[$index];
$description = $event->getDescription();

echo Color::green("Running event: {$description}\n");

try {
$startTime = microtime(true);
$event->run();
$endTime = microtime(true);

$duration = round(($endTime - $startTime) * 1000, 2);
echo Color::green("Event completed successfully in {$duration}ms\n");

$output = $event->getOutput();
if ($output) {
echo Color::yellow("Output:\n{$output}\n");
}
} catch (\Throwable $e) {
echo Color::red("Event failed: " . $e->getMessage() . "\n");
echo Color::yellow("Stack trace:\n" . $e->getTraceAsString() . "\n");
}
}

/**
* Get the scheduler instance
*
* @return Scheduler
*/
private function getScheduler(): Scheduler
{
$scheduler = Scheduler::getInstance();

$this->loadSchedulerFile($scheduler);

return $scheduler;
}

/**
* Load the scheduler from kernel
*
* @param Scheduler $scheduler
* @return void
*/
private function loadSchedulerFile(Scheduler $scheduler): void
{
$kernel = Loader::getInstance();

$kernel->schedules($scheduler);
}

/**
* Display an event result
*
* @param array $result
* @return void
*/
private function displayResult(array $result): void
{
$status = match ($result['status']) {
'success' => Color::green('[SUCCESS]'),
'failed' => Color::red('[FAILED]'),
'skipped' => Color::yellow('[SKIPPED]'),
default => Color::yellow('[UNKNOWN]'),
};

echo sprintf(
"%s [%s] %s\n",
$status,
$result['type'],
$result['description']
);

if ($result['error']) {
echo Color::red(" Error: {$result['error']}\n");
}

if ($result['started_at'] && $result['finished_at']) {
$duration = $result['finished_at']->getTimestamp() - $result['started_at']->getTimestamp();
echo Color::yellow(" Duration: {$duration}s\n");
}
}
}
Loading
Loading