<?php
/**
* Filename: Xml.php
* The description of the file:
* =========================================
* Copy right 2017
* 基于事件机制的XML文件处理
* =========================================
* Author: pengzhi
* Version: 1.0.0
* Encoding:UTF-8
* Date: 2017/3/30 17:33
**/
class Xml
{
protected $resParser = null;
protected $document = array();
protected $stack = array();
protected $parent = null;
protected $openTag = '';
public function __construct()
{
$this->init();
}
public function __destruct()
{
// TODO: Implement __destruct() method.
if (is_resource($this->resParser)) {
xml_parser_free($this->resParser);//释放资源
}
}
public function getError()
{
return xml_error_string(xml_get_error_code($this->resParser));
}
/**
* @param $data
* @return array|null
*/
public function parse($data)
{
$this->document = array();
$this->stack = array();
$this->parent = &$this->document;
return xml_parse($this->resParser, $data, true) ? $this->document : null;
}
public function serialize($data)
{
$arrResult = $this->parse($data);
echo json_encode($arrResult).PHP_EOL;
//ob_start();
$str = '<?xml version="1.0" encoding="utf-8"?>'."\n<root>\n";
//内容输出 深度优先搜索
$str .= $this->dfs($arrResult, 1);
//echo '\n</root>';
$str .= "</root>";
//$str = ob_get_contents();
//ob_end_clean();
return $str;
}
/**
* @param $parser
* @param $tag
* @param $attributes
*/
protected function open(&$parser, $tag, $attributes)
{
Bd_Log::trace(json_encode($attributes).":attr|{$tag}:tag");
$this->openTag = $tag;
//tag 处理
if (isset($this->parent[$tag])) {
//数组元素
if (is_array($this->parent[$tag]) && !$this->isAssoc($this->parent[$tag])) {
// 数组元素 第2 3 ...
$len = count($this->parent[$tag]);
//属性解析
if (!empty($attributes)) {
if (!isset($this->parent["{$tag} attr"])) {
$this->parent["{$tag} attr"] = array();
}
$this->parent["{$tag} attr"][$len] = $attributes;
}
$this->parent = &$this->parent[$tag][$len];
} else {
//数组元素
$arr = array(&$this->parent[$tag]);
$this->parent[$tag] = &$arr;
//属性解析
if (isset($this->parent["{$tag} attr"])) {
$this->parent["{$tag} attr"] = array($this->parent["{$tag} attr"]);
}
if (!empty($attributes)) {
//注意 木有 0 项
if (!isset($this->parent["{$tag} attr"])) {
$this->parent["{$tag} attr"] = array();
}
$this->parent["{$tag} attr"][1] = $attributes;
}
$this->parent = &$this->parent[$tag][1];
}
} else {
//普通元素
if (!empty($attributes)) {
//属性解析
$this->parent["{$tag} attr"] = $attributes;
}
$this->parent = &$this->parent[$tag];
}
//属性处理
$this->stack[] = &$this->parent;
}
/**
* @param $parser
* @param $data
*/
protected function data(&$parser, $data)
{
Bd_Log::trace("{$data}:data");
$this->data = $data;
}
/**
* @param $parser
* @param $tag
*/
protected function close(&$parser, $tag)
{
Bd_Log::trace("{$tag}:tag");
if ($tag == $this->openTag) {
$this->parent = $this->data;
$this->openTag= '';
}
array_pop($this->stack);
if (!empty($this->stack)) {
$this->parent = &$this->stack[count($this->stack) - 1];
}
}
/**
* @return null|resource
*/
protected function init()
{
$this->resParser = xml_parser_create();//PHP5 开始自动检查encoding
xml_parser_set_option($this->resParser, XML_OPTION_CASE_FOLDING, false);
xml_set_object($this->resParser, $this);
xml_set_element_handler($this->resParser, 'open','close');
xml_set_character_data_handler($this->resParser, 'data');
return $this->resParser;
}
/**
* @param array $arr
* @return bool
*/
private function isAssoc(array $arr)
{
$keys = array_keys($arr);
return $keys !== array_keys($keys);
}
/**
* @param $item
* @param int $level
* @return string
*/
private function dfs($item, $level = 1)
{
Bd_Log::trace(json_encode($item)."|level:{$level}|dfs");
$str = '';
if (!is_array($item)) {
$str .= str_repeat('\t', $level).$item."\n";
} else {
foreach ($item as $key => $val) {
if (strpos($key, 'attr') > 0) {
continue;//属性元素 跳过
}
if (is_array($val) && !$this->isAssoc($val)) {
//数组元素
foreach ($val as $idx => $subval) {
$str .= str_repeat('\t', $level)."<{$key}";
//元素属性 todo
if (isset($item["{$key} attr"][$idx])) {
foreach ($item["{$key} attr"][$idx] as $k => $v) {
$str .= " {$k}='{$v}'";
}
}
$str .=">\n";
//元素内容
$str .= $this->dfs($subval, $level+1);
$str .= str_repeat('\t', $level)."</{$key}>\n";
}
} elseif(is_array($val)){
$str .= str_repeat('\t', $level)."<{$key}";
//元素属性 todo
if (isset($item["{$key} attr"])) {
foreach ($item["{$key} attr"] as $k => $v) {
$str .= " {$k}='{$v}'";
}
}
$str .= ">\n";
$str .= $this->dfs($val, $level+1);
$str .= str_repeat('\t', $level)."</{$key}>\n";
} else {
$str .= $this->dfs($val, $level+1);
}//if elseif else
}//foreach
}//else
return $str;
}
}
$simple = "<infos><para><note attr1='11'>note11</note><note attr2='12'>note12</note></para><para a='12'><note>note21</note></para></infos>";
$xml = Xml();
//echo json_encode($xml->parse($simple)).PHP_EOL;
echo $xml->serialize($simple).PHP_EOL;
echo $xml->getError().PHP_EOL;
exit;