fab2s/filelock

一个用于正确处理flock()文件锁定的流畅助手

1.0.1 2021-06-16 09:55 UTC

This package is auto-updated.

Last update: 2024-09-16 16:59:19 UTC


README

Build Status Total Downloads Monthly Downloads Latest Stable Version Scrutinizer Code Quality PRs Welcome License

一个基于flock()的流畅助手,用于正确处理文件锁定。

FileLock提供两种锁定策略和多种选项。与flock()类似,FileLock可以等待直到获取到独占锁(阻塞),或者立即失败(非阻塞),但它也可以尝试配置的时间来获取非阻塞的独占锁,并在每次尝试之间等待配置的时间。FileLock可以锁定文件(自我锁定)或创建一个file.lock文件并对其进行锁定(外部锁定)

安装

可以使用composer安装FileLock

composer require "fab2s/filelock"

FileLock也被包含在OpinHelper中,它打包了多个低于“瑞士军刀”级别的助手,覆盖了PHP编程中一些最令人烦恼的方面,如UTF8字符串操作、高精度数学或正确锁定文件

如果您需要使用低于7.1的PHP,您仍然可以使用OpinHelper 0.x

先决条件

FileLock没有特定的依赖关系

外部锁定

这种锁定策略不会锁定输入filePath本身,而是创建一个新的文件$lockFilePath = "$inputFilePath.lock";并尝试代替它锁定。这种方法在高度并发的使用中更受欢迎,其中许多进程将同时尝试写入相同的文件,例如文件缓存。因为这允许我们首先尝试以写入模式打开.lock文件,并在最终尝试flock()之前失败回读取模式

通过使用单独的文件进行锁定,我们确保等待锁定的每个写入(这应该在阻塞模式下完成)在文件最有可能被密集读取时不会持有缓存文件的任何句柄

当写入失败时,我们再次将写入句柄降低到单个句柄,仅在需要创建外部.lock文件时。总的来说,这意味着在预热后,每个等待写入同一文件的进程都将持有对.lock文件的读取句柄,而单个写入句柄最多用于实际写入缓存文件。因为写入句柄的打开成本很高,大约是读取句柄的十倍,这样做可能会产生一些差异

外部锁定在您不想实际对文件进行flock()(可能已经被锁定或由其他程序/进程使用),或者只是需要某种排他性时也很有用,因为在这种情况下将为您创建锁文件,并且每个PHP进程都将能够检查其存在

请注意,FileLock永远不会删除外部和空的.lock文件,并且其存在并不一定意味着锁是活动的

如果$lockFilePath = "$inputFilePath.lock";版本的.lock文件(即输入FilePath的目录)不可写,它将替换为sys_get_temp_dir(),文件名前带有哈希的basedir($inputFilePath)前缀

$filePath = "/some/dir/some.file.ext";
$lock = new FileLock($filePath, Filelock::LOCK_EXTERNAL); // will create /some/dir/some/file.ext.lock or /tmp/sha1(/some/dir/some)_file.ext.lock

自我锁定

这种锁定策略会锁定输入filePath本身。它比外部锁定策略提供更多的保证,因为文件将为任何进程锁定,而不仅仅是通过FileLock检查锁的进程,并且在写入会话不是即时时更受欢迎

$filePath = "/some/dir/some.file.ext";
$lock = new FileLock($filePath, Filelock::LOCK_SELF); // will directtly flock() /some/dir/some/file.ext

在某些特定情况下,使用双锁(外部和自我)并通过两个 FileLock 实例来使用双锁是有意义的。

实际上

在外部和自我锁定中,一旦你有一个实例,你可以

  • 获取一个阻塞锁

    $lock->doLock(true);
    // we either own the lock or php timed out
  • 尝试获取一个非阻塞锁

    if ($lock->doLock()) {
        // we got the lock
    }
  • 尝试多次获取非阻塞锁,失败后才放弃

    $isLocked = $lock->setLockTry(5) // default is 3
        ->setLockWait(0.01) // default is 0.1 second
        ->obtainLock(); // will try 5 times and wait 0.01 second in between
    if ($isLocked) { // could call $lock->isLocked()
        // we got the lock
    }

然后,你可以获取底层的句柄

$lockHandle = $lock->getHandle();

这主要在自我锁定时有用,因为你可能需要句柄来实际写入某些内容。

释放锁

在所有情况下,锁要么在实例销毁时释放,要么手动释放

$lock->unLock(); // doing so also fclose() underlying handle

重要的是要注意,当你获取自我锁时,你需要保持 $lock 实例处于活动状态,直到你完成对文件的操纵。因为 FileLock 在销毁时将释放其锁和句柄。这可能会在某个函数中获取锁而没有将生成的实例存储在其作用域之外时发生。

打开工厂

FileLock 随附一个方便的工厂,用于简化独占和自我锁定文件的打开。

    /**
     * @param string     $file
     * @param string     $mode fopen() mode
     * @param int|null   $maxTries 0|null for single non blocking attempt
     *                             1 for a single blocking attempt
     *                             1-N Number of non blocking attempts
     * @param float|null $lockWait Time to wait between attempts in second
     *
     * @return null|static
     */
    public static function open($file, $mode, $maxTries = null, $lockWait = null)

用法与 fopen() 相似,但它在成功时返回一个 FileLock 实例(打开 + 锁定),而不是资源,失败时返回 null。

$filePath = "/some/dir/some.file.ext";
$mode = 'wb'; // any fopen() mode
$fileLock = Filelock::open($filePath, $mode); // returns null or FileLock instance

if ($fileLock) {
	// we got it opened and locked
	$handle = $fileLock->getHandle();
}

要求

FileLock 已在 php 7.1、7.2、7.3、7.4 和 8.0 上进行了测试。

贡献

欢迎贡献,请不要犹豫,提出问题和提交拉取请求。

许可证

FileLock 是开源软件,许可协议为 MIT 许可证