crontinel / php
Framework-agnostic PHP core for Crontinel — data objects, monitor contracts, and alert management.
Requires
- php: ^8.2|^8.3|^8.4
- dragonmantank/cron-expression: ^3.3
- psr/log: ^3.0
- psr/simple-cache: ^3.0
Requires (Dev)
- laravel/pint: ^1.0
- pestphp/pest: ^3.0
- symfony/cache: ^7.0
This package is auto-updated.
Last update: 2026-04-26 00:03:07 UTC
README
Framework-agnostic PHP core for Crontinel. Provides monitor contracts, typed data objects, alert management with deduplication, and cron expression helpers.
If you use Laravel, install crontinel/laravel instead. It pulls in this package automatically and adds service providers, dashboard, Artisan commands, and event listeners.
Install crontinel/php directly when you are:
- Building an adapter for Symfony, Slim, or another framework
- Integrating Crontinel into a vanilla PHP application
- Writing a custom monitor that does not depend on Laravel
Requirements
- PHP 8.2, 8.3, or 8.4
- Composer
Compatibility
- Laravel 10, 11, and 12 — install
crontinel/laravelinstead, which wraps this package and adds Laravel-specific integration. - Horizon — this package reads the same Redis keys that Horizon uses. When you install
crontinel/laravel, theHorizonMonitorworks out of the box with no additional configuration. - Other frameworks — Symfony, Slim, and any PHP 8.2+ application can use the contracts and data objects directly.
Installation
composer require crontinel/php
What this package provides
Contracts
MonitorInterface-- implementisHealthy(): boolto create a custom monitorAlertChannelInterface-- implementsend(AlertEvent $event): voidto deliver alerts via any transport (Slack, email, webhook, etc.)
Data objects
Immutable, readonly value objects used across the stack:
AlertEvent-- key, title, message, level (critical/warning/info/resolved), fired timestampCronStatus-- command, expression, status, last run metadata, next due timeHorizonStatus-- Horizon supervisor stateQueueStatus-- queue depth, failed count, oldest job age
Alert management
AlertManager handles fire/resolve lifecycle with built-in deduplication. It accepts any PSR-16 cache and any AlertChannelInterface implementation:
- Deduplicates repeated alerts for the same key (default: 5 min TTL)
- Auto-sends "resolved" notifications when an issue clears
- Logs failures via PSR-3 logger (falls back to
NullLogger)
Cron expression helpers
CronExpressionHelper wraps dragonmantank/cron-expression with convenience methods:
nextDue(string $expression)-- next scheduled run asDateTimeImmutablepreviousDue(string $expression)-- last scheduled runisLate(DateTimeInterface $lastRunAt, string $expression, int $graceSeconds = 120)-- whether a job has missed its window
Built-in monitors
Ready-to-use monitor implementations (all implement MonitorInterface):
CronMonitor,HorizonMonitor,QueueMonitor
Usage example
Implement MonitorInterface to build a custom health check:
use Crontinel\Contracts\MonitorInterface; final class DiskSpaceMonitor implements MonitorInterface { public function __construct( private readonly string $path = '/', private readonly float $minFreePercent = 10.0, ) {} public function isHealthy(): bool { $free = disk_free_space($this->path); $total = disk_total_space($this->path); return ($free / $total) * 100 >= $this->minFreePercent; } }
Wire up alert delivery by implementing AlertChannelInterface:
use Crontinel\Contracts\AlertChannelInterface; use Crontinel\Data\AlertEvent; final class WebhookChannel implements AlertChannelInterface { public function __construct( private readonly string $url, ) {} public function send(AlertEvent $event): void { file_get_contents($this->url, false, stream_context_create([ 'http' => [ 'method' => 'POST', 'header' => 'Content-Type: application/json', 'content' => json_encode([ 'key' => $event->key, 'title' => $event->title, 'message' => $event->message, 'level' => $event->level, 'resolved' => $event->resolved, ]), ], ])); } }
Then use AlertManager to fire and resolve alerts:
use Crontinel\Alert\AlertManager; // Requires any PSR-16 cache implementation $alertManager = new AlertManager( cache: $cache, channel: new WebhookChannel('https://example.com/webhook'), ); $monitor = new DiskSpaceMonitor('/data'); if (! $monitor->isHealthy()) { $alertManager->fire('disk.data', 'Low disk space', 'Disk /data is below 10% free', 'critical'); } else { $alertManager->resolve('disk.data'); }
Using CronExpressionHelper
use Crontinel\Cron\CronExpressionHelper; $helper = new CronExpressionHelper(); // When is the next run of "every minute"? $next = $helper->nextDue('* * * * *'); echo $next->format('Y-m-d H:i:s'); // Is a job that's been silent for 10 minutes late for a 5-minute schedule? $lastRun = new \DateTimeImmutable('2026-04-11 10:50:00'); $isLate = $helper->isLate($lastRun, '*/5 * * * *', graceSeconds: 60); // Returns true if current time exceeds $lastRun + 5min + 60s grace
Using built-in monitors with AlertManager
use Crontinel\Alert\AlertManager; use Crontinel\Monitors\QueueMonitor; $alertManager = new AlertManager( cache: $cache, channel: new WebhookChannel('https://example.com/alerts'), ); $queueMonitor = new QueueMonitor( redisConnection: 'default', maxDepth: 100, maxFailed: 10, ); if (! $queueMonitor->isHealthy()) { $status = $queueMonitor->getStatus(); // returns QueueStatus object $alertManager->fire( 'queue.depth', 'Queue depth exceeded', "Queue has {$status->depth} jobs (max: 100)", 'warning', ); }
Ecosystem
| Package | Description |
|---|---|
| crontinel/php (this repo) | Framework-agnostic PHP core |
| crontinel/laravel | Laravel package with dashboard, Artisan commands, and event listeners |
| crontinel/mcp-server | MCP server for AI assistants (Claude, Cursor) |
| docs.crontinel.com | Full documentation |
| app.crontinel.com | Hosted SaaS dashboard |
License
MIT. See LICENSE.
Built by Harun R Rayhan · crontinel.com