岩本隆史の日記帳(アーカイブ)

はてなダイアリーのサービス終了をうけて移行したものです。更新はしません。

「Perlメモ」の排他制御ロジックをPHPに移植してみた

PHPにおける排他制御のベストプラクティスが分からなかったので、定評のありそうな「Perlメモ」の「排他制御(ファイルロック)をする」を移植してみました。誤りがあるかもしれませんので、お気づきの方はご指摘いただけると助かります。

<?php
function my_flock($options = array())
{
    $lfh = array_merge(array('dir'      => './lockdir/',
                             'basename' => 'lockfile',
                             'timeout'  => 60,
                             'trytime'  => 10), $options);
    $lfh['path'] = $lfh['dir'] . $lfh['basename'];

    for ($i = 0; $i < $lfh['trytime']; $i++, sleep(1)) {
        if (rename($lfh['path'], $lfh['current'] = $lfh['path'] . time())) {
            return $lfh;
        }
    }
    $filelist = scandir($lfh['dir']);
    foreach ($filelist as $file) {
        if (preg_match('/^' . $lfh['basename'] . '(\d+)/', $file, $matches)) {
            if (time() - $matches[1] > $lfh['timeout']
              and rename($lfh['dir'] . $matches[0],
                         $lfh['current'] = $lfh['path'] . time())
            ) {
                return $lfh;
            }
            break;
        }
    }
    return false;
}

function my_funlock($lfh) {
    rename($lfh['current'], $lfh['path']);
}

# ロックする(タイムアウトあり)
$lfh = my_flock() or die('Busy!');

# アンロックする
my_funlock($lfh);

排他制御の目的ですが、WebサービスAPIを呼び出す際のインターバル管理に使うつもりです。たとえば、Amazon Associates Web ServiceのAPIには、1秒1コールまでという制限があります。これを遵守するためには、最終呼び出し時刻を保持しつつ、それをアトミックに更新する仕組みが必要になるでしょう。

ちなみに、拙作のリリースチェッカーにもそのような仕組みはあるのですが、Perlメモのロジックに比べると中途半端な感じなので、改善したいと思っているわけです。