BOMの付いているファイルを探す

UTF-8でプログラムを書いていると、Windowsのメモ帳などが勝手に付けてくれるBOM(Byte Order Mark)が邪魔をしてうまく動かないということが時々起こります。
PHPでは特にこの問題が表面化しやすく、リダイレクトなど、HTTPヘッダを出力しようとすると、すでに本文出力してるぞってエラーが出てくるわけです。

そういうときは何らかの方法でBOMを除去するわけですが、そもそもBOMの入ったファイルを見つけなければ話にならないわけです。
そこで、BOM付きファイルを探し出すのをPHPで作ってみました。

<?php

// 初期ディレクトリ
$initial_directory = __DIR__;

header('Content-type: text/plain');
s($initial_directory.'/');

function s($target_dir)
{
    chdir($target_dir);
    $files = glob('*');
    if (!is_array($files) || count($files) == 0) {
        return;
    }
    foreach ($files as $file) {
        $path = $target_dir.$file;
        if (is_dir($path)) {
            s($path.'/');
        } elseif (filesize($path) >= 3) {
            $contents = file_get_contents($path);
            if (hasbom($contents)) {
                // 出力
                echo "$path\n";
            }
        }
    }
}

function hasbom($contents)
{
    return preg_match('/^efbbbf/', bin2hex($contents[0].$contents[1].$contents[2])) === 1;
}

特定のディレクトリ以下のすべてのファイルを走査して、BOMコードで始まるファイルを列挙していきます。
globのパターンを変更してPHPファイルだけを対象にするというのもよいでしょう。

なんでもかんでもBOM除去していいってわけでもないので、とりあえず表示だけしています。
あとは出てきたファイルを、必要に応じてテキストエディタなどでBOMなしにして保存しなおせばよいわけです。

BOM除去していいテキストファイルしかないとわかっているときは、ファイルを列挙せずにすぐBOM除去して書き戻したほうが便利でしょう。
その場合は、出力部分を以下のコードに入れ替えます。

file_put_contents($path, substr($contents, 3));

今回のプログラムは、PHP5.6と7.1で動作確認済みです。