XMLNodeClassName = 'kXMLNode5'; kUtil::includeOnce( dirname(__FILE__) . DIRECTORY_SEPARATOR . 'xml_helper5.php' ); } } /** * Parses XML data specified and returns root node * * @param string $xml * @param int $mode * @param bool $no_case_folding * @return kXMLNode * @access public */ public function &Parse($xml = null, $mode = self::XML_NO_TEXT_NODES, $no_case_folding = false) { $xml = trim($xml); $this->Mode = !isset($mode) ? self::XML_NO_TEXT_NODES : $mode; $this->Clear(); // in case if Parse method is called more then one time $xml_parser = xml_parser_create(); if ( $no_case_folding ) { xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0); } xml_set_element_handler($xml_parser, Array (&$this, 'startElement'), Array (&$this, 'endElement')); xml_set_character_data_handler($xml_parser, Array (&$this, 'characterData')); if ( !xml_parse($xml_parser, $xml, 1) ) { $class_name = $this->XMLNodeClassName; $byte = xml_get_current_byte_index($xml_parser); $extract = '...' . mb_substr($xml, $byte - 50, 50) . ' !!![' . mb_substr($xml, $byte, 1) . ']!!! ' . mb_substr($xml, $byte + 1, 50) . '...'; $message = sprintf( 'XML error number %s: %s at line %d col %d, byte %d, extract: %s', xml_get_error_code($xml_parser), xml_error_string( xml_get_error_code($xml_parser) ), xml_get_current_line_number($xml_parser), xml_get_current_column_number($xml_parser), xml_get_current_byte_index($xml_parser), $extract ); $this->RootElement =& new $class_name('ERROR', array ('code' => xml_get_error_code($xml_parser), 'message' => $message)); trigger_error($message, E_USER_WARNING); } xml_parser_free($xml_parser); $root_copy = $this->RootElement; /* @var $root_copy kXMLNode */ unset($this->RootElement); unset($this->CurrentElement); return $root_copy; } function ConvertHTMLEntities($s) { //build first an assoc. array with the entities we want to match $table1 = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES); $patterns = array(); $replacements = array(); //now build another assoc. array with the entities we want to replace (numeric entities) foreach ($table1 as $k=>$v){ $patterns[] = "/$v/"; // $c = htmlentities($k,ENT_QUOTES,"UTF-8"); $replacements[] = "&#".ord($k).";"; } //now perform a replacement using preg_replace //each matched value in array 1 will be replaced with the corresponding value in array 2 $s = preg_replace($patterns,$replacements,$s); return $s; } function startElement(&$Parser, &$Elem, $Attrs) { $parent =& $this->CurrentElement; // 1. $parent is now reference to $this->CurrentElement $class_name = $this->XMLNodeClassName; $this->CurrentElement =& new $class_name($Elem, $Attrs); // 2. =& ensures, that new object won't be assigned to $parent as well (don't remove) if (!isset($this->RootElement) || is_null($this->RootElement)) { $this->RootElement =& $this->CurrentElement; } if (!is_null($parent)) { $parent->AddChild($this->CurrentElement); } } function characterData($Parser, $Line) { if ($this->Mode == self::XML_WITH_TEXT_NODES) { $class_name = $this->XMLNodeClassName; $text_node = new $class_name('_TEXT_'); /* @var $text_node kXMLNode */ $text_node->AppendData($Line); $this->CurrentElement->AddChild( $text_node ); } else { $this->CurrentElement->AppendData($Line); } } function endElement($Parser, $Elem) { if ($this->Mode == self::XML_WITH_TEXT_NODES) { /*if (count($this->CurrentElement->Children) == 1 && $this->CurrentElement->firstChild->Name == '_TEXT_') { $this->CurrentElement->Children = array(); }*/ } if ($this->CurrentElement->Parent != null) { $this->CurrentElement =& $this->CurrentElement->Parent; } } function Clear() { unset($this->RootElement); unset($this->CurrentElement); } function &CreateNode($name, $value=null, $attributes=array()) { $class_name = $this->XMLNodeClassName; $node = new $class_name($name, $attributes); /* @var $node kXMLNode */ if ($value) { $node->SetData($value); } return $node; } /** * Checks, that there is no error during XML document parsing * * @param kXMLNode $root_node * @param string $root_node_name * @return bool * @access public */ public function isError(&$root_node, $root_node_name) { if ( !is_object($root_node) || !preg_match('/^kxmlnode/i', get_class($root_node)) || ($root_node->Name == 'ERROR') || ($root_node->Name != $root_node_name) ) { return true; } return false; } } class kXMLNode { /** * Casefolded name of this node * * @var string */ var $Name = null; /** * Original name of this node * * @var string */ var $OriginalName = null; /** * Casefolded attributes of this node * * @var Array */ var $Attributes = array(); /** * Original attributes of this node * * @var Array */ var $OriginalAttributes = array(); /** * List of node child nodes * * @var Array * @access public */ public $Children = Array (); /** * Node content (usually text) * * @var string */ var $Data = null; /** * Reference to first child * * @var kXMLNode */ var $firstChild = null; /** * Last child of this node * * @var kXMLNode */ var $lastChild = null; /** * Parent node * * @var kXMLNode */ var $Parent = null; /** * Node position relative to other nodes of it's parent * * @var int */ var $Position = 0; /** * Node identifier * * @var int */ var $CRC = null; function __construct($name, $attributes = Array()) { $this->Name = strtoupper($name); $this->OriginalName = $name; $this->OriginalAttributes = $attributes; foreach ($attributes as $attr => $value) { $this->Attributes[strtoupper($attr)] = $value; } $this->CRC = crc32($this->Name . implode('', array_keys($this->Attributes)) . implode('', array_values($this->Attributes))); } /** * Returns attribute value, first checking it casesensitively, then caseinsensitively * If attribute is not set returns default value (if passed), or false otherwise * * @param string $name * @param mixed $default * @return string */ function GetAttribute($name, $default=false) { if (isset($this->OriginalAttributes[$name])) { return $this->OriginalAttributes[$name]; } return isset($this->Attributes[strtoupper($name)]) ? $this->Attributes[strtoupper($name)] : $default; } function SetParent(&$elem) { $this->Parent =& $elem; } /** * Adds new child to current node * * @param kXMLNode $a_child */ function AddChild(&$a_child) { $node_count = count($this->Children); $a_child->Position = $node_count; if ($node_count == 0) { $this->firstChild =& $a_child; $this->lastChild =& $a_child; } else { $this->lastChild =& $a_child; } $this->Children[] =& $a_child; $a_child->SetParent($this); } /** * Appends data to current node * * @param string $data */ function AppendData($data) { $this->Data .= $data; } /** * Returns child node by given path * * @param string $path * @return kXMLNode */ function &GetChild($path) { $entries = explode('/', strtoupper($path)); $cur = array_shift($entries); if ($cur == $this->Name) $cur = array_shift($entries); if (!$cur) return $this; if (!isset($this->Children[$cur])) return false; $left = implode('/', $entries); if (!$left) return $this->Children[$cur]; return $this->Children[$cur]->GetChild($left); } function &GetFirstChild() { return $this->firstChild; } /** * Returns node value by given path * * @param string $path * @return string */ function GetChildValue($path) { $child =& $this->GetChild($path); return $child !== false ? $child->Data : ''; } /** * Returns child node by given position among it siblings * * @param int $position * @return kXMLNode */ function &GetChildByPosition($position) { if ($position < count($this->Children) ) { return $this->Children[$position]; } else { $false = false; return $false; } } /** * Recursively searches for child with given name under current node * * @param string $name * @return kXMLNode */ function &FindChild($name) { $name = strtoupper($name); if ( $this->Name == $name ) { return $this; } /*if ( isset($this->Children[$name]) ) { return $this->Children[$name]; } $children = array_keys($this->Children);*/ foreach ($this->Children as $elem) { /* @var $elem kXMLNode */ $child =& $elem->FindChild($name); if ( $child !== false ) { return $child; } } if ( isset($child) && is_object($child) ) { $child->_destruct(); } unset($child); $false = false; return $false; } /** * Returns value of given child or value of it's attribute * * @param string $name * @param string $attr * @return string * @access public */ public function FindChildValue($name, $attr = null) { $child =& $this->FindChild($name); if ( $child !== false ) { if ( isset($attr) ) { return $child->Attributes[ strtoupper($attr) ]; } return $child->Data; } return ''; } /** * Returns next node to this, false in case of end list * * @return kXMLNode */ function &PrevSibling() { if (!is_null($this->Parent) && $this->Position > 0) { $pos = $this->Position - 1; do { $ret =& $this->Parent->GetChildByPosition($pos--); } while ($ret->Name == '_TEXT_' && $pos >= 0); if ($ret->Name == '_TEXT_') $ret = false; return $ret; } else { $false = false; return $false; } } /** * Returns next node to this, false in case of end list * * @return kXMLNode */ function &NextSibling() { if (!is_null($this->Parent)) { $pos = $this->Position + 1; do { $ret =& $this->Parent->GetChildByPosition($pos++); } while ($pos < count($this->Parent->Children) && ($ret->Name == '_TEXT_')); if (is_object($ret) && ($ret->Name == '_TEXT_')) { $ret = false; } return $ret; } else { $false = false; return $false; } } /** * Reconstructs XML of the node and subnodes * * $param bool $content_only */ function GetXML($content_only = false) { $xml = ''; $single = (!$this->Data && count($this->Children) == 0); if (!$content_only) { $xml = '<'.$this->OriginalName; if (count($this->OriginalAttributes)) { $xml .= ' '; $att_contents = array(); foreach ($this->OriginalAttributes as $name => $value) { $att_contents[] = $name.'="'.htmlspecialchars($value).'"'; } $xml .= implode(' ', $att_contents); } $xml .= $single ? '/>' : '>'; } if (!$single) { if ($content_only) { $xml .= $this->Data; } else { $xml .= preg_match('/&|Data) ? 'Data.']]>' : $this->Data; } foreach ($this->Children as $node) { /* @var $node kXMLNode */ $xml .= $node->GetXML($node->Name == '_TEXT_' ? true : false); } if (!$content_only) { $xml .= 'OriginalName.'>'; } } return $xml; } function RemoveChild($name) { $child =& $this->FindChild($name); $parent =& $child->Parent; $pos = $child->Position; array_splice($parent->Children, $pos, 1); for ($i=$pos; $i < count($parent->Children); $i++) { $parent->Children[$i]->Position = $i; } $parent->firstChild =& $parent->Children[0]; $parent->lastChild =& $parent->Children[count($parent->Children)-1]; } function ReplaceChild($name, &$replacement) { $child =& $this->FindChild($name); $parent =& $child->Parent; $pos = $child->Position; array_splice($parent->Children, $pos, 1, array($replacement)); $replacement->Parent =& $parent; $replacement->Position = $pos; $parent->firstChild =& $parent->Children[0]; $parent->lastChild =& $parent->Children[count($parent->Children)-1]; } function SetName($name) { $this->Name = strtoupper($name); $this->OriginalName = $name; } function SetData($data) { $this->Data = $data; } function SetAttribute($name, $value) { $this->Attributes[strtoupper($name)] = $value; $this->OriginalAttributes[$name] = $value; } }