Skip to content

Instantly share code, notes, and snippets.

@avtobys
Last active June 24, 2024 04:49
Show Gist options
  • Save avtobys/fe0b42470509002efa25b329b41d592d to your computer and use it in GitHub Desktop.
Save avtobys/fe0b42470509002efa25b329b41d592d to your computer and use it in GitHub Desktop.
class Locker
<?php
namespace App\Utils;
/**
* Class Locker
*
* This class is used to lock files for a given amount of time.
*
* @package App\Utils
*/
class Locker
{
/**
* The path to the directory where lock files will be stored.
*/
const LOCK_PATH = __DIR__ . '/../../tmp/lock';
/**
* The prefix of the lock file.
*/
const LOCK_PREFIX = 'lock';
/**
* The maximum time a lock should be held in seconds.
*/
const CLEAN_TIME = 3600;
/**
* Variable used to store the path to the lock file.
*/
private static $path;
/**
* Used to determine if the current process has locked the file.
*/
private static $lock = false;
/**
* Locks the file with the given name for a specified period of time.
*
* @param int $id The name of the file to be locked.
* @param int $max_time The maximum time the lock should be held in seconds.
* @return bool True if the lock was successfully obtained, false otherwise.
*/
public static function lock($id, $max_time = self::CLEAN_TIME)
{
// Set the path of the lock file
self::setPath($id);
// If the lock file exists and the lock has expired, remove the file
if (file_exists(self::$path) && self::cleaner()) {
return false;
}
// Create a SplFileObject and lock the file
$file = new \SplFileObject(self::$path, 'a+b');
if ($file->flock(LOCK_EX)) {
$file->rewind();
// Read the time stored in the file
$time = $file->fgets();
// If the file is empty, write the current time plus the maximum lock time to the file and return true
if (!$time) {
$file->ftruncate(0);
$file->fwrite(time() + $max_time);
touch(self::$path, time() + $max_time);
$file->flock(LOCK_UN);
self::$lock = true;
return true; // Lock obtained
}
// Unlock the file and return false
$file->flock(LOCK_UN);
}
return false; // Lock not obtained
}
/**
* Sets the path of the lock file.
*
* @param int $id The identifier used to create the lock file's filename.
* @throws \Error If the lock directory cannot be created.
*/
private static function setPath($id)
{
// Check if the lock directory exists, and if not, create it
if (!file_exists(self::LOCK_PATH)) {
mkdir(self::LOCK_PATH, 0755, true);
}
// If the lock directory still does not exist, throw an error
if (!file_exists(self::LOCK_PATH)) {
throw new \Error('Path does not create');
}
// Set the path of the lock file
self::$path = self::LOCK_PATH . '/' . self::LOCK_PREFIX . '-' . $id . '.lock';
// If the lock file exists, clear its last access time
file_exists(self::$path) && clearstatcache(true, self::$path);
}
/**
* Removes the lock file.
*
* @param int $id The name of the lock file.
* @param bool $force True if the lock should be removed, even if it was not created by the current process.
* @return bool True if the lock file was successfully removed, false otherwise.
*/
public static function unlock($id, $force = false)
{
// Set the path of the lock file
self::setPath($id);
// Use SplFileObject to handle the file
$lockFile = new \SplFileObject(self::$path, 'c');
// Try to acquire an exclusive lock
if ($lockFile->flock(LOCK_EX)) {
// Check if the lock file exists and if it was created by the current process or if force is true
if (file_exists(self::$path) && (self::$lock || $force)) {
// Remove the lock file
@unlink(self::$path);
$lockFile->flock(LOCK_UN); // Release the lock
return true; // Lock file successfully removed
}
$lockFile->flock(LOCK_UN); // Release the lock
}
return false; // Lock file not removed
}
/**
* Deletes any lock files that are older than the current time.
*
* @return bool True if the current lock file still exists, false otherwise.
*/
private static function cleaner()
{
// Loop through all lock files in the lock directory.
foreach (glob(self::LOCK_PATH . '/' . self::LOCK_PREFIX . '-*.lock') as $path) {
// Check if the file is older than the current time and if the time stored in the file is older than the current time.
if (filemtime($path) < time() && intval(file_get_contents($path)) < time()) {
// Use SplFileObject to handle the file
$lockFile = new \SplFileObject($path, 'c');
// Try to acquire an exclusive lock
if ($lockFile->flock(LOCK_EX)) {
// Delete the file.
@unlink($path);
$lockFile->flock(LOCK_UN); // Release the lock
}
}
}
// Return true if the current lock file still exists, false otherwise.
return file_exists(self::$path);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment