徳丸浩のtumblr
PHP5.3.2以降ではfcloseで自動的にアンロックされない

PHPの本家サイトでflockの説明を読んでいたら、以下の変更履歴に気がつきました。

5.3.2 ファイルのリソースハンドルを閉じたときにロックを自動的に解放する機能が削除されました。 ロックの解放は、常に手動で行わなければなりません。

http://php.net/manual/ja/function.flock.php

ところがネットの解説を見ると、ロック開放はflock($fp, LOCK_UN); ではなく、fcloseでやれとしている解説が結構あります。

(4)fcloseの前にflock解除するな

 fcloseの前にflock(ファイルポインタ, LOCK_UN) する人は実に多いのですが、これははっきりと間違いだと断言します

@ITのPHPの記事が突っ込みどころ満載 - 暴言満載

LOCK_UNは普通は使われない。ロック開放はfclose()関数でやるのが鉄則。

http://doremi.s206.xrea.com/php/tips/flock.html

最初に引用した解説では、理論的な根拠を説明していてその内容は正しいのですが、その対処として明示的なflock解除をするなと書いているわけです。

しかし、PHP5.3.2以降では、正式なマニュアルに「ロックの解放は、常に手動で行わなければなりません」と書かれているのですね。

twitterで教えていただいたところでは、これはPHPのバグ修正として行われた変更のようです。

ライブラリの中で「勝手に」LOCK_UNすると、おかしなことの原因になるということですね。

@rskyさんがPHPのソース差分を調べてくださいました。

該当部分のみを引用しますと、以下のように、明確にアンロック処理が削除されています。

image

一方、明示的にアンロックしていない場合、どういう時におかしくなるかですが、これは@haruyamaさんに教えていただきました。

参照している先には、rubyのサンプルが載っていますが、PHPで書き直してみました。

// a.php
<?php
  $fp = fopen('a.txt', 'r+'); // ファイルをopen
  flock($fp, LOCK_EX);       // ロックする

  system('(php b.php > log.txt) >/dev/null &'); // バックグラウンドでb.phpを実行
  // fflush($fp);           // (B)
  // flock($fp, LOCK_UN);   // (A)
  fclose($fp);              // ファイルを閉じる
  echo "done a.php\n";
// b.php
<?php echo "start b.php\n"; $fp = fopen('a.txt', 'r+'); flock($fp, LOCK_EX); // ロックする fclose($fp); echo "done b.php\n";

実行してみると、PHP5.3.1ではa.phpのロックがfclose時にアンロックされてb.phpが動き出しますが、PHP5.3.2ではロックが残り、b.phpはロックされたままになります。PHP5.3.2でも、上記(A)とコメントアウトしたflock($fp, LOCK_UN)で明示的にアンロックするとb.phpも動くようになります。

ということで、マニュアル通り明示的なアンロックをすれば、すべてのバージョンのPHPで正しく動くようになります。

ところで、元々どうして「明示的にアンロックするな」という話が出てきたかというと、上記のサンプルには出て来ませんがロックした状態でファイルに書き込みをしたい場合、アンロックしてからfcloseするまでの間に、バッファに残っていたデータが *ロックされない状態で* ファイルに書き込まれる可能性があるからです。したがって、アンロックの前に、明示的にfflushを呼び出してバッファの内容をはき出すようにすべきです。ソース上では(B)のコメントアウトした部分を活かせば大丈夫です。

ということで、ファイルのロックに関しては、

  • 明示的にflock($fp, LOCK_UN); でアンロックする
  • アンロックする前に fflush($fp); でバッファの内容をはき出す

ことが必要です。

  1. kani-develockeghemからリブログしました
  2. peketaminkiri2からリブログしました
  3. act2012blatm09tdからリブログしました
  4. sharkppockeghemからリブログしました
  5. elfockeghemからリブログしました
  6. dara-jockeghemからリブログしました
  7. zouma3ockeghemからリブログしました
  8. sky-windockeghemからリブログしました
  9. samaiockeghemからリブログしました
  10. atm09tdockeghemからリブログしました
  11. cworld2kockeghemからリブログしました
  12. ockeghemの投稿です
Google