HHeLiBeXの日記 正道編

日々の記憶の記録とメモ‥

fopenに存在しないファイル名を指定したときの挙動

PHP 5.4.16からPHP 8.1.7にバージョンアップをしようとしたときのこと。

<input type="file" name="file" ~に値が指定されていないときに

fopen($_FILES['file']['tmp_name'], 'r');

したとき、例外が発生するようになってしまったので調査。同じPHP 5.x系ということで、php56を使い、php56とphp81での比較とする。

そもそものコードは以下のような感じ。

Main1.php

<?php

$filename = $argv[1];

$fp = fopen($filename, "r");
if (!$fp) {
    print "{$filename}: Cannot open file.\n";
    exit(1);
}

print "{$filename}: OK\n";

// ・・・

fclose($fp);

php56での実行結果。

$ php56 Main1.php Main1.php
Main1.php: OK
$ php56 Main1.php a.txt
PHP Warning:  fopen(a.txt): failed to open stream: No such file or directory in /home/hhelibex/blog/2022-0705-01/Main1.php on line 5
a.txt: Cannot open file.
$ php56 Main1.php ""
PHP Warning:  fopen(): Filename cannot be empty in /home/hhelibex/blog/2022-0705-01/Main1.php on line 5
: Cannot open file.
$ 

php81での実行結果。

$ php81 Main1.php Main1.php
Main1.php: OK
$ php81 Main1.php a.txt
PHP Warning:  fopen(a.txt): Failed to open stream: No such file or directory in /home/hhelibex/blog/2022-0705-01/Main1.php on line 5
a.txt: Cannot open file.
$ php81 Main1.php ""
PHP Fatal error:  Uncaught ValueError: Path cannot be empty in /home/hhelibex/blog/2022-0705-01/Main1.php:5
Stack trace:
#0 /home/hhelibex/blog/2022-0705-01/Main1.php(5): fopen()
#1 {main}
  thrown in /home/hhelibex/blog/2022-0705-01/Main1.php on line 5
$ 

ファイル名が空でなく存在しないファイルの場合の挙動はWarningのまま変わらないが、ファイル名が空文字列の場合に、PHP 8.1ではValueErrorが発生するようになってしまった。

対症療法をするなら、以下のような感じだろうか。

Main2.php

<?php

$filename = $argv[1];

if (empty($filename)) {
    print "Filename cannot be empty.\n";
    exit(1);
}
$fp = fopen($filename, "r");
if (!$fp) {
    print "{$filename}: Cannot open file.\n";
    exit(1);
}

print "{$filename}: OK\n";

// ・・・

fclose($fp);

しかし、ファイルが存在しない場合のWarningが気持ち悪い。

これは、C言語でプログラムを書いていた頃の癖でfopen()の実行結果しかチェックしないのが問題なのだろう。

ということで、PHPでの最適解は以下になるのではないか。

Main3.php

<?php

$filename = $argv[1];

if (!file_exists($filename)
|| !($fp = fopen($filename, "r"))) {
    print "{$filename}: Cannot open file.\n";
    exit(1);
}

print "{$filename}: OK\n";

// ・・・

fclose($fp);

php81での実行結果。

$ php81 Main3.php Main3.php
Main3.php: OK
$ php81 Main3.php a.txt
a.txt: Cannot open file.
$ php81 Main3.php ""
: Cannot open file.
$ 

厳密には、file_exists()とfopen()の間にファイルが存在しなくなった場合のエラーを考えなければならないが、それはイレギュラーケースとしてログに記録されてもいいのではないだろうか。