XBRL for PHP の公開
※以前別の場所で書いた文章を備忘的に書き記しておきます。
【投稿年月日】2011-01-24 【ジャンル】PHP/MySQL
先日「XBRL for PHP」というスクリプトをオープンソースという形で公開しました。xmlのお勉強(PHPで解析)の成果です。XBRL for PHP
sourceforge.jp/projects/xbrl-php/
XBRL for PHP サンプルサイト
mzstyle.s147.xrea.com/xbrlphp/
そもそもXBRLを解析している最中(XBRLのお勉強3 (PHPで解析))、肝心のXMLに関する自らの無理解さに愕然としたことをきっかけです。これではいけないと、XMLの名前空間や要素を簡単に解析可能なPHPのクラスを作成することにした訳です。
PHP5にはSimpleXMLという便利な関数がありますが、そのままでは、名前空間や要素を解析することができません。どうするかと simplexml_load_file などでXMLファイルを読み込んで配列に格納した後、 getDocNamespaces や getNamespaces で抽出した名前空間ごとに解析する必要があります。
具体的なソースは以下の通り。「XBRL for PHP」の「/xbrl/xml/XML.php」のXMLクラスから必要な部分を抜粋したものです。
<?php
/*
* A class for Xml Perse. PHP 5 >
*
* @author Jun Yamane <jun1969x[at]gmail.com>
* @license BSD www.opensource.org/licenses/bsd-license.html
* @version ver 0.12 (2011-01-21)
* @link sourceforge.jp/projects/xbrl-php/
*
*/
class XML {
public $ATTR = "_attributes:";
public $NS = "_namespace:";
public $VAL = "_value:";
/**********************************************************
Perse Xml.
*********************************************************/
public function perseXml($file="") {
libxml_use_internal_errors(true);
$doc = @simplexml_load_file($file, null, LIBXML_COMPACT|LIBXML_NOCDATA|LIBXML_NOBLANKS|LIBXML_NOENT);
if(!is_object($doc)) {
$err["status"] = "Failed loading XML.";
foreach(libxml_get_errors() as $error) {
$err["error"][] = $error->message;
}
return $err;
}
$ns = $data[$this->NS] = $doc->getDocNamespaces();
if(!count($ns)) {
$body = $this->_perseXmlLoop($doc, "", 1);
}else {
$ns = $this->_nameSpace($ns, 1);
foreach($ns as $key=>$val) {
$obj_attr = ($val) ? $doc->attributes($val) : $doc->attributes();
if($obj_attr) {
$data[$this->ATTR.$key] = $this->_perseAttributes($obj_attr);
}
}
$root = $doc->getName();
$body[$root] = $data;
foreach($ns as $key=>$val) {
if($obj_data = $this->_perseXmlLoop($doc, $val)) {
$body[$root][$key] = $obj_data;
}
}
}
return $body;
}
/**********************************************************
Name Space.
*********************************************************/
private function _nameSpace($array="") {
if(!array_key_exists("", $array) || !in_array("", $array)) {
$array[""] = "";
}
return $array;
}
/**********************************************************
Perse Xml Attributes.
*********************************************************/
private function _perseAttributes($attributes="") {
foreach($attributes as $attr) {
$data[(string)$attr->getName()] = (string)$attr;
}
return $data;
}
/**********************************************************
Perse Xml Loop.
*********************************************************/
private function _perseXmlLoop($doc="", $vals="", $not="") {
if(!is_object($doc)) {
return;
}
$ns = $this->_nameSpace($doc->getNamespaces(true));
$vals_ = (!$vals) ? $doc->children() : $doc->children($vals);
foreach($vals_ as $obj_key=>$obj_val) {
if($not===1) {
if($obj_attr = $doc->attributes()) {
$body[$obj_key][$this->ATTR] = $this->_perseAttributes($obj_attr);
}
}
$data = "";
foreach($ns as $key=>$val) {
if($obj_loop = $this->_perseXmlLoop($obj_val, $val)) {
$data[$key] = $obj_loop;
}
}
if(count($ns)===1) {
$data = $data[""];
}
foreach($ns as $key=>$val) {
$obj_attr = ($val) ? $obj_val->attributes($val) : $obj_val->attributes();
if($obj_attr) {
$data[$this->ATTR.$key] = $this->_perseAttributes($obj_attr);
}
}
if((string)$obj_val && !is_array($obj_val)) {
$data[$this->VAL] = (string)$obj_val;
}
$body[$obj_key][] = $data;
}
return $body;
}
}
?>
100行ほどのシンプルなクラスですが、あらゆるXML文書の解析に対応するはずです(やや自信なし)。/*
* A class for Xml Perse. PHP 5 >
*
* @author Jun Yamane <jun1969x[at]gmail.com>
* @license BSD www.opensource.org/licenses/bsd-license.html
* @version ver 0.12 (2011-01-21)
* @link sourceforge.jp/projects/xbrl-php/
*
*/
class XML {
public $ATTR = "_attributes:";
public $NS = "_namespace:";
public $VAL = "_value:";
/**********************************************************
Perse Xml.
*********************************************************/
public function perseXml($file="") {
libxml_use_internal_errors(true);
$doc = @simplexml_load_file($file, null, LIBXML_COMPACT|LIBXML_NOCDATA|LIBXML_NOBLANKS|LIBXML_NOENT);
if(!is_object($doc)) {
$err["status"] = "Failed loading XML.";
foreach(libxml_get_errors() as $error) {
$err["error"][] = $error->message;
}
return $err;
}
$ns = $data[$this->NS] = $doc->getDocNamespaces();
if(!count($ns)) {
$body = $this->_perseXmlLoop($doc, "", 1);
}else {
$ns = $this->_nameSpace($ns, 1);
foreach($ns as $key=>$val) {
$obj_attr = ($val) ? $doc->attributes($val) : $doc->attributes();
if($obj_attr) {
$data[$this->ATTR.$key] = $this->_perseAttributes($obj_attr);
}
}
$root = $doc->getName();
$body[$root] = $data;
foreach($ns as $key=>$val) {
if($obj_data = $this->_perseXmlLoop($doc, $val)) {
$body[$root][$key] = $obj_data;
}
}
}
return $body;
}
/**********************************************************
Name Space.
*********************************************************/
private function _nameSpace($array="") {
if(!array_key_exists("", $array) || !in_array("", $array)) {
$array[""] = "";
}
return $array;
}
/**********************************************************
Perse Xml Attributes.
*********************************************************/
private function _perseAttributes($attributes="") {
foreach($attributes as $attr) {
$data[(string)$attr->getName()] = (string)$attr;
}
return $data;
}
/**********************************************************
Perse Xml Loop.
*********************************************************/
private function _perseXmlLoop($doc="", $vals="", $not="") {
if(!is_object($doc)) {
return;
}
$ns = $this->_nameSpace($doc->getNamespaces(true));
$vals_ = (!$vals) ? $doc->children() : $doc->children($vals);
foreach($vals_ as $obj_key=>$obj_val) {
if($not===1) {
if($obj_attr = $doc->attributes()) {
$body[$obj_key][$this->ATTR] = $this->_perseAttributes($obj_attr);
}
}
$data = "";
foreach($ns as $key=>$val) {
if($obj_loop = $this->_perseXmlLoop($obj_val, $val)) {
$data[$key] = $obj_loop;
}
}
if(count($ns)===1) {
$data = $data[""];
}
foreach($ns as $key=>$val) {
$obj_attr = ($val) ? $obj_val->attributes($val) : $obj_val->attributes();
if($obj_attr) {
$data[$this->ATTR.$key] = $this->_perseAttributes($obj_attr);
}
}
if((string)$obj_val && !is_array($obj_val)) {
$data[$this->VAL] = (string)$obj_val;
}
$body[$obj_key][] = $data;
}
return $body;
}
}
?>
なお、上記XMLクラスに関するサンプルサイトを別途用意しましたので、ご利用にあたってはソースや事例を実際に確認することをお勧めします。
XML for PHP
mzstyle.s147.xrea.com/xmlphp/
EDIUNET | PHP/MySQL | 独り言 | 提供サービス | JavaScript