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

Cron Manager Bundle Laravel Package

phakpoom/cron-manager-bundle

A PHP bundle for managing cron jobs: register scheduled tasks, review their timing, and keep cron-driven work organized from one place. Useful when you want a lightweight, centralized way to declare and inspect recurring jobs instead of scattering crontab entries across servers.

View on GitHub
Deep Wiki
Context7

Getting Started

Reality check first: this is a Symfony2-era bundle (BCCCronManagerBundle), not a Laravel package. There is no service provider, no facade, no config/cron-manager.php. It will not install into a Laravel app — its composer.json constrains Symfony 2.x components and its classes extend Symfony\Component\HttpKernel\Bundle\Bundle. So "getting started" branches into two real paths:

  1. You maintain a legacy Symfony2/2.8 app that needs a crontab admin UI. Then this bundle is a candidate.
  2. You are in Laravel (the common case). Then you are not installing it — you are reading it as a reference implementation and rebuilding ~200 lines in Laravel, or using Laravel's scheduler instead.

Where to look first (in the repo):

  • composer.json — confirm the Symfony/PHP constraints before anything else. Treat the 2024-04-04 release date with suspicion; it is almost certainly a tag on an untouched 2013-era codebase, not active maintenance.
  • DependencyInjection/ — shows the config tree and the service definitions (the CronManager-style service that shells out to crontab).
  • Resources/config/routing.* — the exact route prefix for the UI.
  • Controller/ — the read/write/list flow for crontab entries.
  • Form/ and any validator — the cron-expression validation path, which is the only piece genuinely reusable in Laravel.

First use case (legacy Symfony):

// app/AppKernel.php
new Phakpoom\BCCCronManagerBundle\BCCCronManagerBundle(),

Then import the bundle routing under an admin-only prefix, add it to security.yml behind ROLE_ADMIN + IP allowlist, and point it at a dedicated crontab file rather than the live user crontab while you evaluate it. Verify with crontab -l before and after your first save.

First use case (Laravel): don't reach for this at all if the jobs are application jobs — app/Console/Kernel.php (schedule()) + one system cron entry (* * * * * php artisan schedule:run) already covers it. Only pursue this pattern if you must manage system-level crontab lines owned by a deployment user from an admin panel.

Implementation Patterns

1. Port the concept, not the code, into Laravel. The bundle's real value is the shape of the abstraction: a CronEntry value object (expression, command, comment, enabled), a repository that reads/writes the crontab, and a form with expression validation. In Laravel:

// app/Services/CrontabManager.php
use Symfony\Component\Process\Process; // already ships with Laravel

public function all(): array
{
    $p = new Process(['crontab', '-l']);
    $p->run();
    // exit code 1 + "no crontab" is a normal empty state, not an error
    return $this->parse($p->isSuccessful() ? $p->getOutput() : '');
}

public function replace(array $entries): void
{
    $tmp = tempnam(sys_get_temp_dir(), 'crontab');
    file_put_contents($tmp, $this->render($entries));
    (new Process(['crontab', $tmp]))->mustRun(); // atomic-ish swap
    unlink($tmp);
}

Validate expressions with dragonmantank/cron-expression — it is already a transitive dependency of Laravel, so there is zero new surface area:

use Cron\CronExpression;
CronExpression::isValidExpression($line); // cheap gate before saving

2. Keep the web UI write path narrow and audited. The bundle's pattern of "form POST replaces the whole crontab" is the thing to copy carefully: render the entire file each time, never append blindly. Append-only writes are how you get duplicate jobs after a double-submit — a classic failure mode in these managers. Pair every write with a stored snapshot (previous file contents in a crontab_snapshots table) so a bad save is one-click recoverable.

3. Run it as the right user. Crontab is per-user. If your web request runs as www-data and your deployed jobs belong to deploy, the UI will show an empty crontab and silently write jobs that never see your app's environment. Integration tip: either (a) run the manager under the same user via a queue worker/supervisor, or (b) shell out explicitly as the owner with sudo -u deploy crontab -l and a tightly scoped sudoers rule (deploy ALL=(deploy) NOPASSWD: /usr/bin/crontab). Never sudo around the user boundary with a web-editable command field.

4. Treat the expression as untrusted input both ways. Round-trip discipline: parse the stored line into structured fields for the form, then re-render canonically. Users type */5 * * * 1-5 and expect the edit form to show the same string, not a normalized 0,5,10,.... Store the raw user string alongside the parsed parts.

5. Layer on observability. The bundle gives you CRUD and nothing else. A day-to-day-useful manager needs: last-run status (parse syslog or write a wrapper that touches a status file per job), a "run now" button that executes the command through Process with a timeout rather than triggering cron, and a diff view before saving. The bundle's extension points are the usual Symfony ones — override Resources/views templates in app/Resources/BCCCronManagerBundle/views/, and decorate the manager service in services.yml to add logging — but plan on most features being yours.

Gotchas and Tips

It will not run on modern Symfony, let alone Laravel. PHP 8.3 will hit deprecated dynamic properties, each()-era helpers, and old form APIs. Do not budget for "just composer require it" — budget for a fork or a rewrite. Given 0 stars and 0 dependents, assume no upstream fixes will arrive.

This is a remote-code-execution-adjacent feature by design. A web form that writes crontab lines is an arbitrary-command-execution console with a login page in front of it. Non-negotiables: admin-only auth, CSRF, IP allowlist, audit log of who changed what, and a hard block on shell metacharacters in the command field if you restrict to app commands (artisan, php). If the bundle ships without CSRF on its form (very possible for a Symfony2-era bundle), that alone disqualifies it from internet-facing use.

Cron expression edge cases that bite every implementation:

  • 5 fields vs 6 (seconds) — pick one and enforce it; mixing them silently schedules wrong.
  • @reboot, @daily, @hourly macros are valid crontab lines but not valid CronExpression input; your validator must special-case them.
  • Day-of-week accepts both 0 and 7 for Sunday.
  • Ranges with steps (10-50/5) and lists of ranges are legal; naive regex validators reject them.

The % in command lines is a crontab landmine. Unescaped % becomes a newline in the command and splits off stdin in crontab syntax. Any command containing % (URLs, date formats) must be written as \%. A manager that round-trips user input without handling this corrupts jobs.

No transactions, so guard the read-modify-write. Two admins saving simultaneously will lose an entry. Wrap the read → edit → write in flock on a lock file, and re-read inside the lock. Also back up before every write; crontab -r mistakes are unrecoverable otherwise.

Environment is not your shell's environment. Cron gets a minimal PATH and no APP_ENV, no .env, no rbenv/nvm shims. Every generated line should carry explicit PATH= and absolute binary paths, and the manager UI should default to the absolute PHP binary rather than bare php. This is the single most common "the job exists but never ran" support ticket.

Check the config tree, don't guess it. The bundle's DI extension defines the actual option names (crontab binary path, whether to use the system crontab, temp file location). Read DependencyInjection/Configuration.php in the repo before writing config.yml; guessing keys on an undocumented 0-star bundle wastes the most time.

Debugging recipe: reproduce outside the UI first — crontab -l > /tmp/before, do the save through the UI, crontab -l > /tmp/after, diff. If the diff looks right but jobs don't fire, check grep CRON /var/log/syslog and the user's mail spool (/var/mail/<user>) where cron stderr lands. Set a MAILTO= header in the generated file so output doesn't disappear.

Escape hatch: if you need this capability in Laravel, budget a day, not a week — model + service + validation + Filament/Nova resource is small, testable, and yours. Adopting an abandoned Symfony2 bundle into a Laravel stack costs more than the rewrite and leaves you owning unfamiliar code anyway.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource