言語別 YAML用ライブラリ徹底解説

第4回 PHP編

この記事を読むのに必要な時間:およそ 5.5 分

php_yaml

php_yamlはlibyamlのPHP用ラッパーです。libyamlを使っていますので,Syckと違い,YAML1.1の仕様に沿っています。PHPでYAMLパーサを使いたいなら,現在いちばんお勧めです。

なお余談ですが,作者であるrsky氏は,PHPの言語仕様を強化する拡張モジュールQIQを開発するなど,今注目のプログラマです。

php_yamlについて

ダウンロードhttp://www.opendogs.org/
pub/php_yaml-0.3.0.tgz
バージョン0.3.0
ドキュメントなし
作者Ryusuke Sekiyam(rsky)

インストール

コンパイルするにはlibyamlをあらかじめインストールする必要があります。以下の手順でlibyamlをインストールしてくださいリスト6⁠。

リスト6 libyamlのインストール

$ wget http://pyyaml.org/download/libyaml/yaml-0.0.1.tar.gz
$ tar xzf yaml-0.0.1.tar.gz
$ cd yaml-0.0.1/
$ ./configure
$ make
$ sudo make install

続いて,php_yamlを次の手順でインストールしますリスト7⁠。コンパイルする前に,こちらのパッチ注1を適用してください。

リスト7 php_yamlのインストール

$ wget http://www.opendogs.org/pub/php_yaml-0.3.0.tgz
$ tar xzf php_yaml-0.3.0.tgz
$ cd php_yaml-0.3.0/
$ wget http://www.kuwata-lab.com/materials/php_yaml-0.3.0.patch
$ patch -p1 < php_yaml-0.3.0.patch
$ phpize
$ ./configure --with-yaml
$ make
$ sudo make install
注1
このパッチは,値がない場合にNULLではなく空文字となる不具合と,日時がパースできない不具合を修正し,また'y'と'n'を真偽値として認識させないように挙動を変更します。

使い方

php_yaml 0.3.0 では以下の関数が用意されています注2⁠。

yaml_parse($str [, $index [, $count [, $callbacks]]])
YAML文字列を読み込む
yaml_parse_file($str [, $index [, $count [, $callbacks]]])
YAMLファイルを読み込む
yaml_parse_url($str [, $index [, $count [, $callbacks]]])
URLからYAMLドキュメントを読み込む
注2
ソースコードを見ると,データをYAMLドキュメントに変換するための関数(yaml_emit()とyaml_emit_file())もありますが,実装途中であり完成はしていないようです。

php_yamlの基本的な使い方は,リスト8のとおりです。

リスト8 PHPにおけるphp_yamlの使い方(ex-phpyaml1.php)

<?php
// 拡張モジュールyaml.soを読み込む
if (! extension_loaded('yaml')) {
    dl('yaml.so') or die('cannot load yaml.so.');
}

// YAMLドキュメントを読み込む
$str = file_get_contents("example.yaml");
$ydoc = yaml_parse($str);
// または $ydoc = yaml_parse_file("example.yaml");
var_dump($ydoc);
?>

ストリームを読み込むときは,何番目のドキュメントを読み込むかをyaml_parse()またはyaml_parse_file()の第2引数で指定できます。また-1を指定すると,すべてのドキュメントを読み込み,配列に格納して返します。第3引数を指定すると,ストリームに含まれるドキュメント数が設定されますリスト9⁠。

リスト9 ストリームを読み込む(ex-phpyaml2.php)

<?php
if (! extension_loaded('yaml')) {
    dl('yaml.so') or die('cannot load yaml.so.');
}

// YAMLストリームを読み込む
$str = file_get_contents("example.yaml");
$ydocs = yaml_parse($str, -1, $count);
// または $ydocs = yaml_parse_file("example.yaml", -1, $count);
var_dump($ydocs);   // YAMLドキュメントの配列
var_dump($count);   // ドキュメント数
?>

なお日付および日時のスカラー値は,デフォルトではパースされずに文字列となります。日付や日時をパースするには,ini_set() を使ってyaml.decode.timestampを1または2に設定しますリスト10⁠。

  • init_set('yaml.decode_timestamp', 1) なら,1970年1月1日 00:00:00からの通算秒を返します
  • init_set('yaml.decode_timestamp', 2)なら,関数date_create()を使ってDateTimeオブジェクトに変換されます

date_create()はPHP 5.1から利用可能なので,関数が存在することを確認してから使ってください。

リスト10  日付や日時のパース(ex-phpyaml3.php)

<?php
if (! extension_loaded('yaml')) {
    dl('yaml.so') or die('cannot load yaml.so.');
}

/// 日付と日時をパースするよう設定する
if (function_exists('date_create')) {
  ini_set('yaml.decode_timestamp', 2);  // DateTimeオブジェクトを使う
} else {
  ini_set('yaml.decode_timestamp', 1);  // 秒数(整数値)を使う
}

$input = <<<END
- 2008-01-01
- 2008-01-01 12:34:56
- 2008-01-01 12:34:56 +9
END;
$ydoc = yaml_parse($input);
var_export($ydoc);
/// 実行結果(yaml.decode_timestampが1のとき):
/// array (
///   0 => 1199113200,
///   1 => 1199158496,
///   2 => 1199158496,
/// )
?>

日本語の扱い

筆者が試した限りでは,EUC-JPやShift_JISではエラーになりましたが,UTF-8であれば問題なく扱うことができました。

タグを変更する

クラスに対応するタグを登録するには,インスタンスオブジェクトを生成するような関数を定義し,それをコールバック関数としてyaml_parse()またはyaml_parse_file()の第4引数に指定しますリスト11⁠。

ただし,タグは「!tagname」ではなく「!<tagname>」と書かないといけないようです。

リスト11 クラスに対応するタグを指定する(ex-phpyaml4.php)

<?php
if (! extension_loaded('yaml')) {
    dl('yaml.so') or die('cannot load yaml.so.');
}

/// クラス定義
class Color {
  var $r, $g, $b;
  function __construct($r, $g, $b) {
    $this->r = $r;
    $this->g = $g;
    $this->b = $b;
  }
  /// オブジェクトを生成するstaticメソッド
  static function create($arr) {
    return new Color($arr['r'], $arr['g'], $arr['b']);
  }
}

/// YAMLドキュメント
$input = <<<END
- !<color>
  r: 0
  g: 127
  b: 255
END;

/// タグ名と,オブジェクトを生成するコールバック関数名を
/// 指定してパースする
$callbacks = array(
  'color'=>'Color::create',
);
$ydoc = yaml_parse($input, 0, $count, $callbacks);
var_export($ydoc);

/// 実行結果:
/// array (
///   0 =>
///   Color::__set_state(array(
///      'r' => 0,
///      'g' => 127,
///      'b' => 255,
///   )),
/// )
?>

不具合

筆者が試した範囲では,以下のような不具合がありました。

  • マッピングのマージ(<<)に未対応
  • マッピングのデフォルト値(=)に未対応
  • 値がないときにNULLにならず空文字列になる(パッチで修正済み)
  • 「2008-01-01 12:34:56」のパターンの日時がパースできず文字列となる(パッチで修正済み)
  • 'y'や'n'が文字列ではなく真偽値と見なされる注3パッチで修正済み)
注3
YAMLの仕様では,'y'はtrue,'n'はfalseと見なされるはずですが,RubyのSyckやPythonのPyYAMLなど,他の主なライブラリでは'y'も'n'も真偽値ではなく文字列になります。
そのため,データ交換のことを考えて,php_yamlでも同じ挙動をするように変更しています。

その他

php_yamlはlibyamlを使っているだけあって,YAML 1.1の仕様に忠実です。PHPでYAMLパーサが必要なら,現時点でいちばんお勧めです。将来的にはyaml_emitter()が実装されることに期待しましょう。

著者プロフィール

桑田誠(くわたまこと)

プログラマー。Javaに対するLL,XMLに対するYAMLなど,複雑なことをシンプルにするような技術に興味をもつ。

URLhttp://www.kuwata-lab.com/