Windows版PHPのDirectoryIterator->isFile()にバグ?

Windows版PHPのDirectoryIterator->isFile()にバグ?

- Tatsuya Shirai の投稿
返信数: 6

日本語WindowsXP SP3上での検証結果です.

PHP5.2.8では問題ありません.
PHP5.3.5で発生します.PHP5.3.5はApacheと組み合わせて使用していますが,PHP本家にはApacheと組み合わせるためのVC6版のPHP5.3がありませんでしたので,moodle.orgのWindows版パッケージ(XAMPP)のPHPバイナリを利用しました.

現象は比較的シンプルかつ深刻で,日本語Windows上の日本語ファイル名にキャラクタコード5Cが含まれると,DirectoryIterator->isFile()が,ファイルであるにも関わらずfalseを返します.

この問題に気付いたのは,Moodle2.1.2(fs_moodle化の途中)でMoodle1.9.10のサイトをマイグレーションによりMoodle2形式に変換している最中のエラーでした.mod_scormの変換中に,

Exception - DirectoryIterator::__construct(C:\xampplite\moodledata20/56/moddata/scorm/15/ITセキュリティ評価・認証に関するe-Learning ―開発者(管理者)編―.hpc/,C:\xampplite\moodledata20/56/moddata/scorm/15/ITセキュリティ評価・認証に関するe-Learning ―開発者(管理者)編―.hpc/) [directoryiterator.--construct]: �w�肳�ꂽ�t�@�C�������‚����܂����B (code: 2)

このようなエラーが発生しました.問題の個所のコードを調べたところ,moodle/mod/scorm/db/upgradelib.php中のfunction scorm_migrate_moddata_subdir()で発生しています.DirectoryIteratorで取得したアイテムを一つ一つ調べ,ファイルならば新しいファイルシステムにコピーし,アイテムがフォルダの場合は再帰呼び出しでscorm_migrate_moddata_subdir()を呼ぶ.ここでアイテムがファイルであるにも関わらず $item->isFile()がfalseとなるためフォルダであると勘違いされ,ファイル名をフォルダ名として再帰呼び出しするものの呼び出された先ではそれをフォルダとして認識できずにエラーになります.

具体的には,「ITセキュリティ評価・認証に関するe-Learning ―開発者(管理者)編―.hpc」というファイルで,この中の「―」がCP932(シフトJIS準拠)で 81 5C です.

Moodle2からはファイルシステムの仕様に依存しない新しいファイルシステムに変わったため,新規にインストールしたMoodle2ではこの問題は”ほぼ”発生しないと思います.しかし,Moodle1.9, 1.8の環境からMoodle2にアップグレードする際に,どうしても2バイト文字コード(CP932)のファイルへアクセスしなくてはならないでしょう.今回はSCORMのファイルにこの5Cを含むファイル名が運悪く存在しましたが,フォーラム投稿に添付したファイルやコースファイルなどに日本語文字を使用している場合,運悪くそれが5Cを含む文字を使っていると同じ問題が発生します.

なお,標準のMoodle1.9, 1.8を日本語Windows(Server含む)上で使用する場合,ファイル名に日本語が使用できないので,Moodle2にアップグレードするのにPHP5.3.5を使用しても問題は無いでしょう.非常に残念なのですが,日本語Windows上で日本語ファイル名を利用可能としたfs_moodleにおいてのみ発生する問題です.頭が痛いです.

もしmod_scormにおいてのみ発生する問題であればこの個所のみを改良すれば済むのですが…

一番好ましいのは,PHP5.3がこの5C問題のエンバグを解消してくれることです.どなたかこの問題について詳しい方,あるいは他にも問題になっているページなどをご存じではないでしょうか.

#PHPのソースを解析しようとして挫折しました…

Tatsuya Shirai への返信

Re: Windows版PHPのDirectoryIterator->isFile()にバグ?

- Tatsuya Shirai の投稿

関連する情報がありました.2010年7月の情報ですね.

http://old.nabble.com/-PHP-users-35287--Windows7-%E3%81%A7%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8C%E8%AA%AD%E3%81%BF%E5%87%BA%E3%81%9B%E3%81%AA%E3%81%84-td29136372.html

さらに追加

http://notebook99.blog87.fc2.com/blog-entry-71.html

PHP5.3.8のVC6版を探したら,もしかして改善されているのかも,と期待したのですが,PHP5.3系統でシフトJISのファイル名を扱うことは困難である雰囲気ですね…

Tatsuya Shirai への返信

Re: Windows版PHPのDirectoryIterator->isFile()にバグ?

- Tatsuya Shirai の投稿

(注意)以下は,日本語Windows(Server)上でfs_moodleを使って構築したMoodle1.8, 1.9のサイトをMoodle2にアップグレードする環境に限定した話です.Linux上のMoodleや日本語Windows上で日本語ファイル名を一切使用していない場合や,日本語版以外のWindowsをサーバとして用いている場合は関係ありません.

 今のところ思い付く対策は,Moodle1.9, 1.8からMoodle2.0.5などのMoodle2.0系統でマイグレーションを行い,その後,Moodle2.1系統へアップグレードを行うという手段です.Moodle2.1系統はPHP5.3.2を要求しますが,Moodle2.0系統はいまのところPHP5.2.8でOKですので.

 思わぬ伏兵に,fs_moodleのMoodle2対応は後退を余儀なくされた感.

Tatsuya Shirai への返信

Re: Windows版PHPのDirectoryIterator->isFile()にバグ?

- Tatsuya Shirai の投稿

以下のように投稿したところ,

http://old.nabble.com/-PHP-users-35287--Windows7-%E3%81%A7%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%8C%E8%AA%AD%E3%81%BF%E5%87%BA%E3%81%9B%E3%81%AA%E3%81%84-to29136372.html#a32674027 

> 内部エンコーディングにはShift_JISなどのISO-8859-1非互換の文字エンコーディング
> は使えません。例えば、EUC-JP、UTF-8に設定すると直るはずです。

という返信があったので色々と試したのですが,この次元でどうこうできる問題では無いように感じられます.php.iniのmbstring系のパラメータの理解が甘かったので再度勉強.やはりあまり期待できないようです.

http://hain.jp/index.php/tech-j/2007/02/13/p125



 

Tatsuya Shirai への返信

Re: Windows版PHPのDirectoryIterator->isFile()にバグ?

- Tatsuya Shirai の投稿

現象を容易に再現させようと以下のコードを試した段階で誤動作…本当に調べたいのは,このファイルがファイルとして認識できるか(DirectoryIterator->isFile())だったのに.

<?php

    require_once('config.php');

    $filepath = mb_convert_encoding($CFG->dirroot.'/テスト5C_ソ.txt', 'SJIS-WIN');
    touch($filepath);

?>

どうなるかと言うと,"テスト5C_ソ.txt"('ソ'が5Cを含む)というファイルを作成したかっただけです.それが何故か,"テスト5C_ソ.txt"ファイルに加えて"テスト5C_ソテスト5C_ソ.txt"というファイルまで同時に生成されます.DirectoryIteratorどころの問題では無いですね,日本語Windows + PHP5.3は.なお,このPHP5.3.5はmoodle.orgで配布されているXAMPP版のものです.

もし同じ環境でこのサンプルを実行して上記異常現象が発生しない場合はご連絡下さい.もしかしたら私の環境がおかしいのでしょう.

当然ですが,プログラム中の"テスト5C_ソ.txt"を"テスト5C.txt"に変更すれば,"テスト5C.txt"という一つのファイルが生成されるだけ(=正常な動作)です.

添付 5C_Problem.jpg
Tatsuya Shirai への返信

Re: Windows版PHPのDirectoryIterator->isFile()にバグ?

- Tatsuya Shirai の投稿

<?php
    $filename = 'テスト5C_ソ.txt';
    $filepath = mb_convert_encoding($filename, 'SJIS-WIN', 'UTF-8');
?>

このコードをWebMatrix(Microsoftが提供する無償のWeb開発環境)上のPHP5.3.5 For WebMatrix(実体はjaist.dl.sourceforge.net/project/phpinstallermsi/zip/php-5.3.8-nts-Win32-VC9-x86.zip )で実行したところ,サーバ兼クライアントである日本語WindowsXP SP3/x64上に正しくファイルが作成されました.

Apache + PHP5.3(VC6?)だとダメで,IIS + PHP5.3(VC9)だと大丈夫という話?

次に考える選択肢は,Apache friend版Apache + PHP5.3(VC9).これが通ればWindows(Server含む)でMoodle2.2を動かすことができる可能性がある.

Tatsuya Shirai への返信

Re: Windows版PHPのDirectoryIterator->isFile()にバグ?

- Tatsuya Shirai の投稿

fs_moodleで日本語版Windows(Server含む)上に構築されたMoodle1.8, 1.9をMoodle2.0, 2.1にアップグレードする際の問題点について,こちらに少しまとめました.

  1. Moodle2.0にPHP5.2.8でアップグレードした後にMoodle2.1にPHP5.3.5でアップグレードする
  2. これを機にLinuxに変更する
  3. WindowsからLinuxにサイトをコピーしてMoodle2.1にアップグレードした後にWindowsに書き戻す(どうしてもWindowsで公開しないといけない場合)

の3つの選択肢があると考えています.さて,ではファイルシステムが英数半角文字だけで取り扱われるMoodle2系統にアップグレードされれば日本語Windows+PHP5.3系統でMoodle2.1以降を使用し続けることができるのか? 実は少し不安があります.

「Webブラウザからアップロードされたファイル」

 通常の手順でファイルがアップロードされた場合はmove_uploaded_file()関数を用いてテンポラリ領域から所定のエリアにコピーします.Moodle2で,この手順がどのように実装されているのかは未確認ですが,とりあえずどこかに”オリジナルのファイル名のまま”保存し,それから新しいファイルシステムに保管し直しているのだとしたら,この手順の中で処理に失敗する恐れがあります.

「zip書庫の解凍」

 Moodle1系統ではpclzipというPHPのライブラリを用いてアップロードされたzipファイルを解凍するか,外部コマンド(unzip.exeなど)を用いてファイルを解凍します.前者も不安ですが,後者の場合はほぼ間違いなく,一時的にWindowsのファイルシステム上に日本語ファイル名のファイルが展開され,それから新しいファイルシステムに保管することになるでしょう.この手順の中で処理に失敗する恐れがあります.

 他にも何らかのモジュールで一時ファイルを作成する際に新しいファイルシステムを用いずにOSのファイルシステムをそのまま利用してしまうコードがあるかも知れません.
 やはりWindows版PHP5.3系統で5C問題を改善して貰うための働きかけが必要です.どなたかお助けいただけないでしょうか?

#もしPHP5.3.5より後のバージョン(現在はPHP5.3.8)では改善されている,といった情報もありますと助かります.