Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Sdk Laravel Package

cron-monitor/php-sdk

A PHP SDK for integrating applications with Cron Monitor. Report cron job heartbeats, execution status, and failures so you can track scheduled tasks and catch missed or broken jobs. Simple to add to Laravel or any PHP project.

View on GitHub
Deep Wiki
Context7

Getting Started

Prereqs (check before anything else):

php -r 'echo PHP_VERSION, " curl=", extension_loaded("curl") ? "yes" : "no", PHP_EOL;'
composer show --direct --name-only

PHP < 8.2 is a hard stop. curl=no means you must bind a PSR-18 client or enable ext-curl. Presence of laravel/framework or symfony/framework-bundle tells you which integration path applies; neither means plain PHP.

Install and register:

composer require cron-monitor/php-sdk
  • Laravel — CronMonitor\Bridge\Laravel\CronMonitorServiceProvider is auto-discovered. Verify with php artisan list cron-monitor (you should see cron-monitor:sync). If the app lists the package under extra.laravel.dont-discover, register manually in bootstrap/providers.php (L11) or config/app.php (L10).
  • Symfony — Flex adds CronMonitor\Bridge\Symfony\CronMonitorBundle to config/bundles.php. Verify with php bin/console debug:config cron_monitor.
  • Plain PHP — nothing further; CronMonitor\Client\CronMonitorClient and vendor/bin/cron-monitor are ready.

Get an account and token without touching a dashboard:

vendor/bin/cron-monitor signup [email protected] --accept-terms >> .env

Only pass --accept-terms once the person has actually accepted the terms. The token lands in the (git-ignored) env file and never appears on screen or in shell history.

Enable exactly one job first. Enumerate your schedule (php artisan schedule:list, php bin/console debug:scheduler, or your crontab/systemd timers), pick a low-stakes nightly job, and create a monitor for it. Chain the monitor in Laravel:

// Laravel 11: routes/console.php — Laravel 10: app/Console/Kernel.php
Schedule::command('reports:nightly')
    ->dailyAt('02:00')
    ->monitor(config('cron-monitor.monitors')['reports:nightly'] ?? '');

Then confirm the heartbeat arrives on the cronheart.com side, force a failure, and confirm you get alerted. Do not expand coverage until "missing heartbeat" alerting is proven — the client fails open, so a misconfiguration is invisible from inside the app.

Where to look first: config/cron-monitor.php after php artisan vendor:publish --tag=cron-monitor-config, and skills/add-cronheart/SKILL.md in the package — it is an unusually rigorous step-by-step recipe with per-step verification commands, and it is more reliable than the README.

Implementation Patterns

1. Config-map attribution (the production-safe default). Publish the config and read UUIDs through config() at the call site, so they survive php artisan config:cache:

// config/cron-monitor.php
'monitors' => [
    'reports:nightly'      => env('CRON_MONITOR_REPORTS_NIGHTLY_UUID', ''),
    'billing:charge-batch' => env('CRON_MONITOR_BILLING_BATCH_UUID', ''),
],

An empty UUID is the documented "not monitored in this environment" signal — use it to keep local and staging silent without branching code.

2. Environment-gated rollout. Because an empty UUID is a no-op, you can merge ->monitor(...) on every job in a tier and light them up per environment purely through env vars. This decouples code review from rollout.

3. Queue jobs outside the scheduler. Laravel's MonitorQueueJob middleware covers jobs dispatched by the scheduler's ->runInBackground() or by consumers. Attach it to the job, not the schedule, so retries and re-dispatches are each recorded as their own run.

4. Symfony console commands. Pick one form per command — the YAML commands: map wins when both are present, which is a silent trap:

#[AsCommand(name: 'app:reports:nightly')]
#[Monitor(env: 'CRON_MONITOR_REPORTS_NIGHTLY_UUID')]
final class GenerateNightlyReportCommand extends Command

The bundle's console event subscriber sends start on begin, success on exit 0, fail otherwise, with the throwable's class, message, and file:line in the fail body.

5. Symfony Scheduler / Messenger. Map the message class in messages: and put CronMonitor\Bridge\Symfony\Messenger\MonitorPingMiddleware on the bus that actually handles it — the bundle registers the service but Messenger only runs middleware listed in config/packages/messenger.yaml.

6. Plain PHP scripts. Wrap the invocation rather than editing inside the script:

*/15 * * * * /usr/bin/php /app/bin/report.php \
  && vendor/bin/cron-monitor success "$CRON_MONITOR_REPORT_UUID" \
  || vendor/bin/cron-monitor fail "$CRON_MONITOR_REPORT_UUID"

Or, if you want start/finish granularity, use CronMonitorClient::create() with start(), then success()/fail() in a finally.

7. Reconciliation as a deploy step. cron-monitor:sync --dry-run shows the diff between your config map and the account's monitors; --apply creates/updates them. Run --dry-run in PR review or a scheduled job, and only promote --apply to the pipeline after you have confirmed idempotency in staging. This is the mechanism that keeps config and account from drifting apart.

8. Naming convention as a first-class rule. Monitors reconcile by name. Fix a convention (e.g. the artifact command name verbatim: billing:charge-batch) before the first PR, and make "dashboard rename in the same change as the job rename" a code-review checklist item.

9. Tiering for rollout. Rank by revenue impact — billing runs, dunning, payout batches, renewal notices, nightly ETL — and expand one PR per tier. Each job either gets a UUID or is deliberately, explicitly left unmonitored. That inventory is itself useful: it surfaces orphaned crons and duplicate jobs.

Gotchas and Tips

Fail-open is a feature and a hazard. The client never throws, never retries beyond one attempt, and hard-times-out at 5s — a monitoring outage can never fail a business job. But it also means a wrong UUID, a blocked egress rule, or a config:cache mishap produces no application error at all, just a silent absence of heartbeats. This is only safe if dead-man's-switch alerting is configured and tested on the SaaS side. Verify both a forced failure and a skipped run reach on-call before you call the integration done.

Never put CRON_MONITOR_API_KEY= (empty) in Laravel's .env. The empty string is transmitted as an empty bearer token. Omit the line entirely when there is no key. Symfony has the mirror-image hazard: use %env(default::CRON_MONITOR_API_KEY)% so an empty value resolves to null instead of an empty bearer.

Do not read env() at the call site, and do not use the attribute form in production Laravel. Attributes read process env directly and break under config:cache, which stops Laravel loading .env. The config-map form read through config() is the only supported production path.

Never pass a UUID or token as a command argument or flag. It lands in shell history, CI logs, and ps output. Export in the environment and reference the variable. In committed files write only xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx and cmk_xxxxxxxxxxxxxxxx placeholders. Remember the asymmetry: a monitor UUID is the write credential for one monitor; the cmk_… token is account-wide — pings never need it, so scope it to cron-monitor:sync and the management client only.

Leave timeout_seconds: 5.0 and retries: 1 alone. Do not wrap pings in your own retry loop, and do not put assertions or try/catch re-throws around them. The whole value proposition is that a ping cannot lengthen or fail the job.

Renames orphan monitors. A renamed job silently creates a second monitor while the original goes stale — and stale monitors alert. Pair every rename with a dashboard rename, and periodically audit cron-monitor:sync --dry-run output for monitors that no longer correspond to anything in code.

Keep allow_insecure_endpoint: false in production. It only permits http:// targets and is refused outright when combined with api_key. Use endpoint for staging or a self-hosted instance, and turn the flag on nowhere but local experiments.

Sub-minute and high-frequency jobs are a poor fit. Ping volume and per-monitor cost dominate the value; heartbeat monitoring answers "did it start, did it finish, did it fail" and nothing about latency or throughput. It complements Horizon/Sentry, it does not replace them — running two heartbeat tools is worse than one.

Debugging "my pings aren't showing up": in order — (1) is the UUID actually non-empty in the deployed config (config:cache and missing env are the usual culprits); (2) does the process have egress to endpoint; (3) is endpoint pointed at staging while you are watching production; (4) is api_key set to an empty string; (5) check the monitor's own heartbeat log on cronheart.com. The app will never tell you — that is by design.

Extension points and exit. The client is thin, MIT, and dependency-light: you can fork it or replace a ping with a direct authenticated POST to your own endpoint in about an hour. Pin the version in composer.json and keep monitors reproducible from the config map alone, so that if the SaaS is discontinued or repriced you can recreate every monitor elsewhere from your own repository.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo