少し説明すると、Symfony2 のセットアップがあります。拡張した抽象コマンド クラスを使用しています。これらのバッチのいずれかを 1 回だけ実行できるようにします。私の目標は、私が開いて群がるロックファイルを作成して、phpスクリプトが可能な限り停止したときにロックが自動的に解放されるようにすることです。
これを実現するために、Lock という名前のクラスを作成しました。このクラスは SplFileObject を拡張し、基本的に *.lock をどこかに (通常は /var/lock/*) 作成するためのラッパーです。現在、このロックの検出に問題があります。fopenとflockを使用して機能するセットアップがありました。何らかの理由で、それはもう検出されません。
私は基本的に私がやりたいことをするためにOOP構造を作成しました:
- ロックファイルの名前を決定する (フォルダーを使用)
- ロック オブジェクトを作成する
- ディレクトリを作成し、存在しない場合はファイルをロックします
- SplFileObject::__construct() を呼び出す
- ファイルをロックする
これをハンドルでもsplファイルオブジェクトでも動作させることができません。スクリプトを実行して 15 秒間スリープさせ、別のコンソールで同じスクリプトを実行すると、スクリプトがファイルをロックできたという結果が得られ、flock は true を返します。同じスクリプトで同じロック ファイルに 2 つの Lock オブジェクトを作成すると、最初のロックでは true になり、2 番目のロックでは false になります。つまり、2 回目のロックの取得に失敗したことを意味します。スクリプトは機能しているようです。
ただし、両方のスクリプトで 2 つのロックを使用してスクリプトを 2 回実行すると、両方のスクリプトで True と false が返されます...つまり、スクリプト全体でファイルが適切にロックされていないようです:/
私が間違っていることを教えてくれる人はいますか?ファイル名を確認しましたが、スクリプトを実行した両方の時間で同じです。777、755、733 などの複数の権限を試しましたが、違いはありません。
私がそれを呼び出す方法(クラスの一部):
abstract class AbstractTripolisCommand extends ContainerAwareCommand
{
[...]
/**
* Locks the current file based on environments
*
* @param string $application_env
* @param string $symfony_env
*/
private function lockCommand($application_env, $symfony_env)
{
$lock_name = "tripolis/$application_env/$symfony_env/" . crc32(get_class($this));
$lock = new Lock($lock_name, 'w+', $this->getContainer()->get('filesystem'));
var_dump($lock->lock());
$lock2 = new Lock($lock_name, 'w+', $this->getContainer()->get('filesystem'));
var_dump($lock2->lock());
// results when ran 2 times at the same time
// bool(true)
// bool(false)
// when I run this script twice I expect the second run at the same time
// bool(false)
// bool(false)
if(!$lock->lock()) {
throw new Tripolis\Exception('Unable to obtain lock, script is already running');
}
}
[...]
}
Lock.php
namespace Something\Component\File;
use Symfony\Component\Filesystem\Filesystem;
/**
* Creates a new SplFileObject with access to lock and release locks
* Upon creation it will create the lock file if not exists
*
* The lock works by keeping a stream open to the lock file instead
* of creating/deleting the lock file. This way the lock is always
* released when the script ends or crashes.
*
* create a file named /var/lock/something/something.lock
* <example>
* $lock = new Lock('something');
* $lock->lock();
* $lock->release();
* </example>
*
* create a file named /var/lock/something/my-lock-file.lock
* <example>
* $lock = new Lock('something/my-lock-file');
* $lock->lock();
* </example>
*
* NOTE: locks that are not released are released
* automatically when the php script ends
*
* @author Iltar van der Berg <ivanderberg@something.nl>
*/
class Lock extends \SplFileObject implements Lockable
{
/**
* @param string $file_name
* @param string $open_mode
* @param Filesystem $filesystem
* @param string $lock_directory
*/
public function __construct($file_name, $open_mode = 'r+', Filesystem $filesystem = null, $lock_directory = '/var/lock')
{
$filesystem = $filesystem ?: new Filesystem();
$file = self::touchLockFile($file_name, $lock_directory, $filesystem);
parent::__construct($file, $open_mode);
}
/**
* Returns true if the lock is placed, false if unable to
*
* @return boolean
*/
public function lock()
{
return $this->flock(LOCK_EX | LOCK_NB);
}
/**
* Returns true if the lock is released
*
* @return bool
*/
public function release()
{
return $this->flock(LOCK_UN);
}
/**
* Attempts to create a lock file for a given filename and directory
* it will return a string if the file is touched
*
* @param string $file_name
* @param string $lock_directory
* @param Filesystem $filesystem
* @return string
*/
private static function touchLockFile($file_name, $lock_directory, Filesystem $filesystem)
{
$lock_file_path = explode('/', $file_name);
$lock_file = array_pop($lock_file_path);
$path = "$lock_directory/" . (empty($lock_file_path)
? $lock_file
: implode('/', $lock_file_path));
$lock_file = "$path/$lock_file.lock";
if(!$filesystem->exists($path) || !is_dir($path)) {
$filesystem->mkdir($path);
}
return $lock_file;
}
}
?>