Skip to content
← Back to Functions
Code

Cron Lock Manager

Prevents overlapping cron/maintenance jobs with expiring locks.

Function signature

ogAcquireCronLock(lock_file, ttl_seconds = 900, owner = '')

Categories

  • Maintenance and Cron

Parameters

lock_fileApproved filesystem path for the cron lock file.ttl_secondsMaximum lock lifetime in seconds before a stale lock can be reviewed.ownerLog-safe owner label for the process or request attempting the lock.

Return value

Public-safe status string returned by the function for explicit controller branching or logging.

  • success
  • message
  • data

Compatibility

Existing function name, slug, path, and call order preserved; advertised metadata corrected to the actual source behavior.

Minimum PHP version: 7.4

Security notes

Use caller-owned allowlists and context-specific escaping; validate admin actions, export fields, privacy plans, cache keys, templates, settings, routes, and ecommerce policies before production use.

Code

<?php

/*
 * Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
 * Free for personal and internal use. Paid project use requires visible credit
 * to Jeffery L. Paris. Corporate use requires a paid license fee unless a
 * separate written license states otherwise.
 */

/**
 * Prevents overlapping cron/maintenance jobs with expiring locks.
 *
 * Primary use case: Imports, cleanup, emails, sitemap generation.
 * Typical inputs: job key, ttl, storage backend.
 * Typical output: lock result.
 *
 * Implementation note: Release locks safely and expire stale locks.
 *
 * @param string $lock_file Approved lock-file path.
 * @param int $ttl_seconds Lock lifetime in seconds.
 * @param string $owner Optional owner label.
 * @return array Lock acquisition result.
 */
function ogAcquireCronLock($lock_file, $ttl_seconds = 900, $owner = '') {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array()
	);

	$lock_file = trim((string)$lock_file);
	$ttl_seconds = (int)$ttl_seconds;
	$owner = trim((string)$owner);

	if (empty($lock_file)) {
		$result['message'] = 'Lock file is required.';
		return $result;
	}
	if ($ttl_seconds < 60) {
		$ttl_seconds = 60;
	}

	$directory = dirname($lock_file);
	if (!is_dir($directory) || !is_writable($directory)) {
		$result['message'] = 'Lock directory is not writable.';
		return $result;
	}

	$existing = array();
	if (is_file($lock_file)) {
		$raw = file_get_contents($lock_file);
		$decoded = json_decode((string)$raw, true);
		if (is_array($decoded)) {
			$existing = $decoded;
		}
	}

	$now = time();
	if (!empty($existing['expires_at']) && (int)$existing['expires_at'] > $now) {
		$result['message'] = 'Cron lock is already active.';
		$result['data'] = array('acquired' => false, 'expires_at' => (int)$existing['expires_at']);
		return $result;
	}

	$lock_data = array(
		'owner' => $owner,
		'pid' => getmypid(),
		'created_at' => $now,
		'expires_at' => $now + $ttl_seconds
	);

	$written = file_put_contents($lock_file, json_encode($lock_data), LOCK_EX);
	if ($written === false) {
		$result['message'] = 'Unable to write cron lock.';
		return $result;
	}

	$result['success'] = true;
	$result['message'] = 'Cron lock acquired.';
	$result['data'] = array('acquired' => true, 'lock_file' => $lock_file, 'expires_at' => $lock_data['expires_at']);
	return $result;
}