Created
July 30, 2026 00:17
-
-
Save inventor96/2fb3d7bd4e7bf9ac9703f611d48e5f3e to your computer and use it in GitHub Desktop.
Auto-load Mako cron commands for use with crunz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Crunz Configuration Settings | |
| # Required: set Tasks source dir | |
| source: app/console/crunz | |
| # Optional: set logging to Mako's logging dir | |
| log_errors: true | |
| errors_log_file: /var/www/html/app/storage/logs/crunz-errors.log | |
| log_output: true | |
| output_log_file: /var/www/html/app/storage/logs/crunz-output.log |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| * app/console/commands/crons/Example.php | |
| */ | |
| namespace app\console\commands\crons; | |
| use app\console\crunz\ScheduleInterface; | |
| use Crunz\Event; | |
| use mako\reactor\attributes\CommandDescription; | |
| use mako\reactor\attributes\CommandName; | |
| use mako\reactor\Command; | |
| #[CommandName('cron:example')] | |
| #[CommandDescription('Wastes the user\'s time for 5 seconds.')] | |
| class Example extends Command implements ScheduleInterface | |
| { | |
| public function schedule(Event $event): void | |
| { | |
| $event->everyMinute(); | |
| } | |
| public function execute(): void | |
| { | |
| sleep(5); // Simulate a long-running task | |
| $this->write('Hello there! This command wasted your time for 5 seconds.'); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| * app/console/crunz/MakoCronTasks.php | |
| */ | |
| /** | |
| * Mako cron tasks auto-loader. | |
| * | |
| * This is a Tasks file for phpcrunz/crunz. Task definitions and scheduling are | |
| * delegated to the Mako commands in the app/console/commands/crons/ directory. | |
| * Each command class must implement the app\console\crunz\ScheduleInterface | |
| * interface, which defines a schedule() method that receives a Crunz\Event | |
| * object. The schedule() method is responsible for defining the schedule for | |
| * the command using the Crunz\Event API. | |
| * | |
| * Scheduling crons in this way allows for running cron jobs within a Mako | |
| * application instance, thereby allowing access to the Mako framework's | |
| * features and configuration. | |
| */ | |
| use Crunz\Schedule; | |
| use app\console\crunz\ScheduleInterface; | |
| use mako\cli\Environment; | |
| use mako\cli\input\arguments\ArgvParser; | |
| use mako\cli\input\Input; | |
| use mako\cli\input\reader\ReaderInterface; | |
| use mako\cli\output\Output; | |
| use mako\cli\output\writer\WriterInterface; | |
| use mako\reactor\attributes\CommandDescription; | |
| use mako\reactor\attributes\CommandName; | |
| $runTs = date('Y-m-d H:i:s'); | |
| $schedule = new Schedule(); | |
| $matchingClasses = []; | |
| $path = realpath(__DIR__ . '/../commands/crons'); | |
| $errorFile = (__DIR__ . '/../../storage/logs') . '/crunz-errors.log'; | |
| $targetInterface = ScheduleInterface::class; | |
| // ensure the interface itself is loaded first | |
| if (!interface_exists($targetInterface)) { | |
| file_put_contents($errorFile, "[{$runTs}] Interface {$targetInterface} not found. Ensure it is loaded.\n", FILE_APPEND); | |
| return $schedule; | |
| } | |
| // fake the input | |
| $argvParser = new ArgvParser([]); | |
| $reader = new class() implements ReaderInterface { | |
| public function read(): string { return ''; } | |
| public function readCharacter(): string { return ''; } | |
| public function readBytes(int $length): string { return ''; } | |
| }; | |
| $input = new Input($reader, $argvParser); | |
| $input->makeNonInteractive(); | |
| // fake the writer | |
| $writer = new class() implements WriterInterface { | |
| public string $output = ''; | |
| public function setStream($stream): void {} | |
| public function isDirect(): bool { return false; } | |
| public function write(string $string): void { $this->output .= $string; } | |
| }; | |
| // fake the environment | |
| $env = new class() extends Environment { | |
| protected ?bool $hasStty = false; | |
| protected ?bool $hasAnsiSupport = false; | |
| protected ?bool $noColor = true; | |
| protected function getDimensionsForUnixLike(): ?array { | |
| return ['width' => self::DEFAULT_WIDTH, 'height' => self::DEFAULT_HEIGHT]; | |
| } | |
| }; | |
| // create the output | |
| $output = new Output($writer, $writer, $env); | |
| // determine PHP executable path -- IMPORTANT: this probably requires updating to match your environment | |
| $phpExecutable = isset($_SERVER['OS']) && $_SERVER['OS'] === 'Windows_NT' | |
| ? str_replace('-cgi', '', PHP_BINARY) // Windows | |
| : '/usr/local/bin/php'; // Docker | |
| // get the Mako env | |
| $makoEnv = getenv('MAKO_ENV'); | |
| $envOption = empty($makoEnv) ? '' : "--env={$makoEnv}"; | |
| // find all PHP files in the crons directory | |
| $iterator = new RecursiveIteratorIterator( | |
| new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS) | |
| ); | |
| $phpFiles = new RegexIterator($iterator, '/\.php$/'); | |
| // process each file | |
| foreach ($phpFiles as $phpFile) { | |
| // construct FQCN | |
| $relativePath = str_replace($path, '', dirname($phpFile->getRealPath())); | |
| $namespace = 'app\\console\\commands\\crons' . str_replace(DIRECTORY_SEPARATOR, '\\', trim($relativePath, '\\/')); | |
| $className = $phpFile->getBasename('.php'); | |
| // skip if filename looks like it doesn't match class name | |
| if ($className === '') { | |
| file_put_contents($errorFile, "[{$runTs}] Invalid class name for file {$phpFile->getRealPath()}.\n", FILE_APPEND); | |
| continue; | |
| } | |
| $fqcn = $namespace ? "{$namespace}\\{$className}" : $className; | |
| // double-check if class exists | |
| if (!class_exists($fqcn)) { | |
| file_put_contents($errorFile, "[{$runTs}] Class {$fqcn} not found in file {$phpFile->getRealPath()}.\n", FILE_APPEND); | |
| continue; | |
| } | |
| // check if it implements the interface | |
| $reflection = new ReflectionClass($fqcn); | |
| if ($reflection->implementsInterface($targetInterface)) { | |
| // get command name | |
| $commandNameAttrs = $reflection->getAttributes(CommandName::class); | |
| $commandName = ''; | |
| if (isset($commandNameAttrs[0])) { | |
| $commandName = $commandNameAttrs[0]->newInstance()->getName(); | |
| } | |
| // can't use the command if it doesn't have a name | |
| if ($commandName === '') { | |
| file_put_contents($errorFile, "[{$runTs}] Command name not found for class {$fqcn}. Ensure it has a #[CommandName('name')] attribute.\n", FILE_APPEND); | |
| continue; | |
| } | |
| // get command description | |
| $commandDescriptionAttrs = $reflection->getAttributes(CommandDescription::class); | |
| $commandDescription = isset($commandDescriptionAttrs[0]) ? ($commandDescriptionAttrs[0]->newInstance()->getDescription() ?: $commandName) : $commandName; | |
| // instantiate and call schedule method | |
| $commandInstance = $reflection->newInstance($input, $output); | |
| $event = $schedule->run("{$phpExecutable} app/reactor {$commandName} {$envOption}"); | |
| $commandInstance->schedule($event); | |
| $event->description($commandDescription); | |
| } | |
| } | |
| return $schedule; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /* | |
| * app/console/crunz/ScheduleInterface.php | |
| */ | |
| namespace app\console\crunz; | |
| use Crunz\Event; | |
| interface ScheduleInterface | |
| { | |
| /** | |
| * Sets the schedule for the cron job. PLEASE NOTE: This method is called | |
| * in the context of the Crunz scheduler, NOT the Mako application context. | |
| * This means that you cannot use any Mako services or helpers in this | |
| * method. | |
| * | |
| * @param Event $event | |
| * @return void | |
| */ | |
| public function schedule(Event $event): void; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment