From fed945432777db104a8e2d8361565cc6635aeac9 Mon Sep 17 00:00:00 2001 From: Jean-Christian Denis Date: Sat, 13 May 2023 00:57:32 +0200 Subject: [PATCH] extract and fix lock methods --- src/Lock.php | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/Utils.php | 56 ++++++------------- 2 files changed, 165 insertions(+), 39 deletions(-) create mode 100644 src/Lock.php diff --git a/src/Lock.php b/src/Lock.php new file mode 100644 index 0000000..4ac07a3 --- /dev/null +++ b/src/Lock.php @@ -0,0 +1,148 @@ + + */ + protected static $lock_stack = []; + + /** + * Locked files status stack. + * + * @var array + */ + protected static $lock_disposable = []; + + /** + * Last lock attempt error + * + * @var string + */ + protected static $lock_error = ''; + + /** + * Lock file. + * + * @param string $file The file path + * @param bool $disposable File only use to lock + * + * @return null|string Clean file path on success, empty string on error, null if already locked + */ + public static function lock(string $file, bool $disposable = false): ?string + { + # Real path + $file = Path::real($file, false); + if (false === $file) { + self::$lock_error = __("Can't get file path"); + + return ''; + } + + # not a dir + if (is_dir($file)) { + self::$lock_error = __("Can't lock a directory"); + + return ''; + } + + # already marked as locked + if (isset(self::$lock_stack[$file]) || $disposable && file_exists($file)) { + return null; + } + + # Need flock function + if (!function_exists('flock')) { + self::$lock_error = __("Can't call php function named flock"); + + return ''; + } + + # Make dir + if (!is_dir(dirname($file))) { + Files::makeDir(dirname($file), true); + } + + # Open new file + if (!file_exists($file)) { + $resource = @fopen($file, 'w'); + if ($resource === false) { + self::$lock_error = __("Can't create file"); + + return ''; + } + fwrite($resource, '1', strlen('1')); + //fclose($resource); + } else { + # Open existsing file + $resource = @fopen($file, 'r+'); + if ($resource === false) { + self::$lock_error = __("Can't open file"); + + return ''; + } + } + + # Lock file + if (!flock($resource, LOCK_EX | LOCK_NB)) { + self::$lock_error = __("Can't lock file"); + + return ''; + } + + self::$lock_stack[$file] = $resource; + self::$lock_disposable[$file] = $disposable; + + return $file; + } + + /** + * Unlock file. + * + * @param string $file The file to unlock + */ + public static function unlock(string $file): void + { + if (isset(self::$lock_stack[$file])) { + fclose(self::$lock_stack[$file]); + if (!empty(self::$lock_disposable[$file]) && file_exists($file)) { + @unlink($file); + } + unset( + self::$lock_stack[$file], + self::$lock_disposable[$file] + ); + } + } + + /** + * Get last error from lock method. + * + * @return string The last lock error + */ + public static function getlastLockError(): string + { + return self::$lock_error; + } +} diff --git a/src/Utils.php b/src/Utils.php index c7f95a2..c3f6083 100644 --- a/src/Utils.php +++ b/src/Utils.php @@ -38,7 +38,7 @@ use Exception; */ class Utils { - /** @var null|resource Lock update process */ + /** @var null|string $lock File lock for update */ private static $lock = null; /** @@ -401,56 +401,34 @@ class Utils } /** - * Lock a file to see if an update is ongoing + * Lock a file to see if an update is ongoing. + * + * @return bool True if file is locked */ public static function lockUpdate(): bool { try { - // Need flock function - if (!function_exists('flock')) { - throw new Exception("Can't call php function named flock"); - } - // Cache writable ? + # Cache writable ? if (!is_writable(DC_TPL_CACHE)) { throw new Exception("Can't write in cache fodler"); } - // Set file path - $f_md5 = md5((string) dcCore::app()->blog?->id); - $cached_file = sprintf( + # Set file path + $f_md5 = md5((string) dcCore::app()->blog?->id); + $file = sprintf( '%s/%s/%s/%s/%s.txt', DC_TPL_CACHE, - 'periodical', + My::id(), substr($f_md5, 0, 2), substr($f_md5, 2, 2), $f_md5 ); - // Real path - $cached_file = Path::real($cached_file, false); - if (is_bool($cached_file)) { - throw new Exception("Can't write in cache fodler"); + + $file = Lock::lock($file); + if (is_null($file) || empty($file)) { + return false; } - // Make dir - if (!is_dir(dirname($cached_file))) { - Files::makeDir(dirname($cached_file), true); - } - // Make file - if (!file_exists($cached_file)) { - !$fp = @fopen($cached_file, 'w'); - if ($fp === false) { - throw new Exception("Can't create file"); - } - fwrite($fp, '1', strlen('1')); - fclose($fp); - } - // Open file - if (!($fp = @fopen($cached_file, 'r+'))) { - throw new Exception("Can't open file"); - } - // Lock file - if (!flock($fp, LOCK_EX)) { - throw new Exception("Can't lock file"); - } - self::$lock = $fp; + + self::$lock = $file; return true; } catch (Exception $e) { @@ -459,12 +437,12 @@ class Utils } /** - * Unlock update process + * Unlock file of update process. */ public static function unlockUpdate(): void { if (!is_null(self::$lock)) { - @fclose(self::$lock); + Lock::unlock(self::$lock); self::$lock = null; } }