PHP CLI Scripts, Cron Jobs, and Automation

PHP is useful beyond web requests. CLI scripts can import data, process queues, run maintenance, synchronize APIs, and perform repeatable operational tasks.

A Safe CLI Entry Point

#!/usr/bin/env php
<?php
declare(strict_types=1);

require dirname(__DIR__) . '/vendor/autoload.php';

if (PHP_SAPI !== 'cli') {
    fwrite(STDERR, "This command must run from the CLI.\n");
    exit(1);
}

$options = getopt('', ['file:', 'dry-run']);
$filename = $options['file'] ?? null;
$dryRun = array_key_exists('dry-run', $options);

if (!is_string($filename) || !is_readable($filename)) {
    fwrite(STDERR, "Usage: import.php --file=/path/data.csv [--dry-run]\n");
    exit(2);
}

fwrite(STDOUT, $dryRun ? "Dry-run mode enabled.\n" : "Import starting.\n");

Exit Codes Matter

CodeMeaning
0Successful completion
1General runtime failure
2Usage or validation error

Schedulers and monitoring systems use exit codes to determine whether a job succeeded.

Prevent Overlapping Runs

<?php
declare(strict_types=1);

$lock = fopen(sys_get_temp_dir() . '/customer-sync.lock', 'c');

if ($lock === false || !flock($lock, LOCK_EX | LOCK_NB)) {
    fwrite(STDERR, "Another sync is already running.\n");
    exit(1);
}

try {
    // Run the synchronization.
} finally {
    flock($lock, LOCK_UN);
    fclose($lock);
}

Design for Retries

  • Make operations idempotent whenever possible.
  • Checkpoint progress for large imports.
  • Use unique external IDs to prevent duplicates.
  • Set timeouts on network calls.
  • Retry transient failures with backoff and a limit.
  • Send permanent failures to a review queue.

Cron Example

*/5 * * * * /usr/bin/php /var/www/app/bin/process-queue.php \
  >> /var/log/app/process-queue.log 2>&1

Use absolute paths and a predictable environment. Cron often has a smaller PATH and different environment variables than an interactive shell.

Long-Running Workers

Release database transactions promptly, reconnect after failures, cap memory growth, and restart workers after a reasonable number of jobs. Deployments should signal or replace old workers so they do not continue running outdated code.