こんにちは、高木です。

PHPでC言語の前処理をする話題が続きます。今回は、PHPで前処理を行った結果のファイルに、BOM(byte order mark)を自動的に付加する方法について解説します。

まずはBOMについてのおさらいからです。BOMはUnicodeのファイルのバイトオーダーを判別するために、文字として使わないU+FEFFをファイルの先頭に格納するマーカーです。

UTF-16であれば、先頭の2バイトが0xfe, 0xffであればビッグエンディアン、0xff, 0xfeであればリトルエンディアンであることがわかります。UTF-32であれば、最初の4バイトが0x00, 0x00, 0xfe, 0xffであればビッグエンディアン、0xff, 0xfe, 0x00, 0x00であればリトルエンディアンになります。

これまで解説してきた中ではあまり強調しませんでしたが、前処理を行った結果は、原則としてUTF-8で出力することを想定しています。UTF-8であればバイトオーダーを判別する方法は本来必要ないのですが、他のエンコーディングと区別するために、0xef, 0xbb, 0xbfの3バイトをファイルの先頭に格納することがあります。これも便宜上BOMと呼ばれています。

PHPで前処理した結果はC言語のソースファイルです。現在使われている主要な3つの処理系(Visual C++, GCC, Clang)で、とくにオプションを指定することなくコンパイルできるようにするには、BOM付きのUTF-8にする必要があります。GCCやClangはBOMはあってもなくてもかまいませんが、Visual C++はBOMがなければUTF-8と認識してくれません(Visual C++ 2019からはコンパイルオプションを付けることで認識できるようです)。以前はBOMを付けるとGCCが認識してくれなくて困ったものです。現在はBOMを付けておけば3つの処理系のすべてに対応することができます。

では、前処理した結果にBOMを付ける方法ですが、もっとも安直なのは、前処理前のソースファイルにBOMを付けておくことです。ただ、複数のソースファイルを合成して前処理することもありますし、ソースファイルの先頭にコメントなどを自動挿入することもあるでしょうから、この方法はおすすめしません。元のソースファイルはBOM無しのUTF-8にしておくべきでしょう。

前処理した結果は標準出力に書き込まれますので、これをキャプチャして、BOMを付加した上で標準出力またはファイルに書き込むのがよいと考えます。以下に具体的なサンプルコードを挙げます。

ここで登場するimport関数は「PHPによる前処理でソースコードを取り込む」の回で作成した関数です。import関数は取り込んだソースファイルを前処理して標準出力に書き込みます。

ob_start関数というのは、標準出力をバッファリングするためのPHPの関数です。ob_start関数の呼び出しからob_end_clean関数の呼び出しまでの間に標準出力に書き込まれた内容はメモリ上にバッファリングされます。このバッファリングされた内容はob_get_contents関数で文字列として取り出すことができます。

あとは、取り出した文字列の先頭にBOMを付加してから、file_puts_contents関数でファイルに書き出しています。標準出力に書き出すのであればechoを使えばいいでしょう。

ところで、エディタによってはBOMが付加されていると不都合が生じる場合があるようです。そのような環境では、BOMを付加するのはやめた方がいいでしょう。UTF-8以外、たとえばシフトJISで出力する場合も、不具合の原因になるのでBOMの付加はやめてください。