概要
PHPでYAMLファイルを取り扱う方法について2つの方法を取り上げて解説したいと思います。
PHPでは純正のyamlライブラリが標準では無いため、別途用意する必要があります。
- yaml拡張モジュール
yaml拡張モジュールとはlibyamlを用いた拡張モジュールになります。
cで記述されているため高速に動作することが見込まれます。
- Spyc
また完全にPHPのみを用いて記述されたyamlライブラリも存在し、そのうちの一つにSpycというも有名なのがあるのでこれも取り扱います。公式ページ
特徴としては拡張モジュールはインストール作業が必要なのに対してSpycに関してはPHPのみで記述されているライブラリなので、複雑なインストール作業は不要です。
1ファイルで構成されているのでリンク先からファイルをダウンロードし、Spyc.phpをrequireするだけで使用できる状態になります。非常にお手軽です。
準備する
それぞれの実装が使用できるように準備しましょう。
- yaml拡張モジュール
拡張モジュールはlibyamlおよびlibyaml-develを事前にインストールした状態でpeclコマンドを用いてインストールすることができます。
rootになるまたはsudoコマンド経由で下記のようなコマンドを実行すれば完了です。
[root@localhost ~]# yum install libyaml ... [root@localhost ~]# yum install libyaml-devel ... [root@localhost ~]# pecl install YAML ...
- Spyc
対してSpycは先にも述べましたがソースコードをダウンロードしてきて使用したい箇所でrequireするだけです。
最近ではgithubでソースコードを管理されているようなので下記のようにcloneしてくれば十分だと思います。
git clone https://github.com/mustangostang/spyc/
検証してみる
先にSpycで動かしてみましょう
使用箇所でrequireします。するとグローバルでspyc_load_fileという関数が使用可能になります。
これは引数としてファイルパスを与えることで、ファイルを読み込んでyaml形式でパースを行います。
下記のような感じでOKです。
<?php require_once (dirname(__FILE__) . '/Spyc.php'); $obj = spyc_load_file('hoge.yaml'); var_dump($obj);
hoge.yamlは下記のように記述しておきましょう。
- foo - bar
すると出力結果は下記のようになります。
array(2) { [0]=> string(3) "foo" [1]=> string(3) "bar" }
動きましたね〜。
対してyaml拡張モジュールではphp.iniにyaml.soライブラリをロードするように追記します。
するとyaml_parse_fileという関数がグローバルで使用可能になります。
こちらも同様に引数としてファイルパスを与えることで、ファイルを読み込んでパースを行います。
$obj = yaml_parse_file('hoge.yaml'); var_dump($obj);
こちらも同様に、動きましたね。
ただこの2つにはかなり挙動の違いがあるような箇所もあります。
例えば入力ファイルの中身を下記のように編集したとしよう
foo : bar bar
この内容をパースすると
Spycでは下記のようにパースされる。
array(2) { 'foo' => string(3) "bar" [0] => string(3) "bar" }
たいしてyaml拡張モジュールではワーニングが表示され返り値としてはfalseがかえってきます。
パースに失敗しているのです。
PHP Warning: yaml_parse_file(): scanning error encountered during parsing: could not find expected ':' (line 3, column 1), context while scanning a simple key (line 2, column 1) in /tmp/hoge.php on line 2 bool(false)
はたしてこれらは一体どちらが正しいのか。
異なるフォーマットが与えられた時にちゃんとしてくれないのは困る。
これに関していえるのはbarの扱いをどのように解釈しているのか、である。
yaml1.1の仕様書を参考にしてみる。
パラパラっと見た感じ、スカラ値のみで構成されるような場合は定義されていない。
したがって結果として拡張モジュールでパースエラーが発生しているほうが安全であると言える。
Spycではこれを意図的しているのかしていないのかは分からないが、純粋に配列の頭に付け加える(index:0)ことでパースを試みる。
また別な例として下記の例もある。
yamlではいくつかのメタ文字を定義していて
例えば~はNULLを意味するしyはtrueを、nはfalseを意味する。などがあります。
これらは型情報も一緒に解釈されるべきであります。
実際に下記のようなyamlデータを用意して食わせてみましょう。
null: ~ true: true false: n string: '12345'
Spycでの出力結果は下記のようになります。
array(4) { 'null' => NULL 'true' => bool(true) 'false' => bool(false) 'string' => string(5) "12345" }
yaml拡張モジュールでは下記のようになります。
array(3) { [""]=> bool(false) [1]=> bool(true) ["string"]=> string(5) "12345" }
驚いたことにyaml拡張モジュールでの出力結果は、はっきり言って構成を壊してしまっているといってもいいです。
yaml界では一般的なのだろうか。
対してSpycではメタ文字が使用されたとしてもキーとして使用されているうちは一意にstringとして解釈されるようです。
拡張モジュールの方ではキーで使用される際もメタ文字として解釈され、結果わけのわからないことになっちゃってます。
実際に用途として拡張モジュールのように解釈されてしまうと困ることのほうが困るような気がします。
この辺りは逆にSpycのほうが直感に沿った解釈をしてくれているのかなと思います。
好みのチョイスであるところもあるかもしれませんが、今回私はSpycの方をチョイスして実装の方を進めることにしました。
今のところそこまで悪いところはなさそうです。
(それに余談ですがインフラ都合もあって簡単にミドルモジュールをいじれるような状況でもないので)
とりあえず、一点だけSpycで直して欲しいところがあったりして、spyc_load_fileファイルは読み込むyamlファイルパスを指定することでファイルを読み込めるのですが、引数に存在しないファイルを指定した際にもなんと普通に動いちゃいます
たとえば下記のようなプログラムを実行すると
<?php require_once (dirname(__FILE__) . '/Spyc.php'); // aaaaは存在しないファイル $hoge = spyc_load_file('aaaa'); var_dump($hoge);
下記のように出力されます。
array(1) { [0]=> string(4) "aaaa" }
これまずくない??
個人的には返り値がfalseになるとか例外投げるとかいろいろ設計はあると思うんですが。
明らかに設計ミス?なにか意図があるのかと理解に苦しみます。
と、、まあ今のところそんなこともあるのですが、また追加で問題などあれば追ってレポートしたいと思います。
Synfony に Yaml Component なんてものがあるらしい。知らなんだ。
https://symfony.com/doc/current/components/yaml.html