読者です 読者をやめる 読者になる 読者になる

脳みそスワップアウト

揮発性なもので。おもにPHPのこととか。

SimpleXMLのバグ #66084 の影響

php bug

とあるAPIから返されたXMLをparseしたところ、環境により結果が違っていた。

どうも、Bug #66084 の修正が影響しているらしい。
この修正は php-5.6.11, php-5.5.27, php-5.4.28 で取り込まれている。

現象

レスポンスXML
foo がスペース、その後なぜか改行がついている行儀の悪いXML

<?xml version="1.0" encoding="UTF-8"?><root><foo> </foo>
</root>

再現phpコード

$strXml =<<<'EOF'
<?xml version="1.0" encoding="UTF-8"?><root><foo> </foo>
</root>
EOF;

$objXml = new SimpleXMLElement($strXml);
var_dump($objXml);

php-5.5.6 の結果

class SimpleXMLElement#1 (1) {
  public $foo =>
  class SimpleXMLElement#2 (0) {
  }
}

php-5.6.11 の結果

class SimpleXMLElement#1 (1) {
  public $foo =>
  class SimpleXMLElement#2 (1) {
    public ${0} =>
    string(1) " "
  }
}

今回の例だと、foo がホワイトスペースで構成されていて、
foo の直後にホワイトスペースが存在する場合に
foo が空だと判断されていまっていたようで、
新しいバージョンでは正常にスペースが取得できている。

正しい方向への修正なのだけど、今回みたいな行儀の悪いXMLをparseしている場合は動作に影響がある。

値自体は、(string)$objXml->foo でピンポイントになら取得できる。
全体の構造のparseがおかしいので、foreach にかけたりしているとまずい。

こんなのとか

static public function xml2Array(\SimpleXMLElement $objXml) {
    $f = function($elem, $out=[]) use (&$f) {
        if(empty($elem)) {
            return null;
        }
        foreach((array)$elem as $key=>$node) {
            if(is_object($node) || is_array($node)) {
                $out[$key] = $f($node);
            } else {
                $out[$key] = $node;
            }
        }
        return $out;
    };
    $arrXml = $f($objXml);
    return $arrXml;
}