Index: branches/5.1.x/core/units/helpers/xml_helper5.php
===================================================================
diff -u
--- branches/5.1.x/core/units/helpers/xml_helper5.php (revision 0)
+++ branches/5.1.x/core/units/helpers/xml_helper5.php (revision 14001)
@@ -0,0 +1,71 @@
+var = $array;
+ }
+ }
+
+ public function rewind() {
+ reset($this->var);
+ }
+
+ public function current() {
+ $var = current($this->var);
+ return $var;
+ }
+
+ public function key() {
+ $var = key($this->var);
+ return $var;
+ }
+
+ public function next() {
+ $var = next($this->var);
+ return $var;
+ }
+
+ public function valid() {
+ $var = $this->current() !== false;
+ return $var;
+ }
+
+}
+
+class kXMLNode5 extends kXMLNode implements IteratorAggregate {
+
+ public function getIterator() {
+ return new XMLIterator($this->Children);
+ }
+
+ public function __destruct()
+ {
+// echo number_format(memory_get_usage()). ' <-- Entered destructor for '.$this->Name.'
';
+ unset($this->Attributes);
+ if (is_array($this->Children)) {
+ foreach ($this->Children as $key => $child)
+ {
+ if ($this->Children[$key] instanceof kXMLNode5 ) {
+ $this->Children[$key]->__destruct();
+ }
+ unset($this->Children[$key]);
+ }
+ }
+ unset($this->Children);
+// echo number_format(memory_get_usage()). ' <-- Destructed '.$this->Name.' Children
';
+ unset($this->Name);
+ unset($this->Data);
+ unset($this->firstChild);
+ unset($this->lastChild);
+ unset($this->Parent);
+ unset($this->Position);
+ unset($this->CRC);
+ unset($this);
+ }
+}
+
Index: branches/5.1.x/core/units/helpers/xml_helper.php
===================================================================
diff -u -r13086 -r14001
--- branches/5.1.x/core/units/helpers/xml_helper.php (.../xml_helper.php) (revision 13086)
+++ branches/5.1.x/core/units/helpers/xml_helper.php (.../xml_helper.php) (revision 14001)
@@ -1,6 +1,6 @@
XMLNodeClassName = 'kXMLNode5';
+ k4_include_once( 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
*/
- function &Parse($xml = null, $mode = XML_NO_TEXT_NODES)
+ function &Parse($xml = null, $mode = null, $no_case_folding = false)
{
- $this->Mode = $mode;
+ $xml = trim($xml);
+ $this->Mode = !isset($mode) ? 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)) {
- $this->RootElement = new kXMLNode('ERROR', array('code'=>xml_get_error_code($xml_parser),'message'=>xml_error_string(xml_get_error_code($xml_parser))));
- trigger_error(sprintf('XML error: %s at line %d',
+
+ 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)), E_USER_WARNING);
+ 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;
+ unset($this->RootElement);
+ unset($this->CurrentElement);
return $root_copy;
}
- function ConvertHTMLEntities($s){
+ function ConvertHTMLEntities($s)
+ {
//build first an assoc. array with the entities we want to match
$table1 = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES);
@@ -69,16 +113,20 @@
//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
- $this->CurrentElement =& new kXMLNode($Elem, $Attrs); // 2. =& ensures, that new object won't be assigned to $parent as well (don't remove)
+ $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);
}
@@ -87,7 +135,8 @@
function characterData($Parser, $Line)
{
if ($this->Mode == XML_WITH_TEXT_NODES) {
- $text_node = new kXMLNode('_TEXT_');
+ $class_name = $this->XMLNodeClassName;
+ $text_node = new $class_name('_TEXT_');
$text_node->AppendData($Line);
$this->CurrentElement->AddChild( $text_node );
}
@@ -103,6 +152,7 @@
$this->CurrentElement->Children = array();
}*/
}
+
if ($this->CurrentElement->Parent != null) {
$this->CurrentElement =& $this->CurrentElement->Parent;
}
@@ -113,24 +163,51 @@
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;
+ }
}
class kXMLNode {
+
/**
- * Name of this node
+ * Casefolded name of this node
*
* @var string
*/
var $Name = null;
/**
- * Attributes of this node
+ * 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 childnodes
*
* @var Array
@@ -145,7 +222,7 @@
var $Data = null;
/**
- * First child of this node
+ * Reference to first child
*
* @var kXMLNode
*/
@@ -182,12 +259,33 @@
function kXMLNode($name, $attrs = array())
{
$this->Name = strtoupper($name);
+ $this->OriginalName = $name;
+ $this->OriginalAttributes = $attrs;
+
foreach ($attrs as $attr => $value) {
- $this->Attributes[strtoupper($attr)] = $value;
+ $this->Attributes[ strtoupper($attr) ] = $value;
}
+
$this->CRC = crc32($this->Name.join(array_keys($this->Attributes)).join(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;
@@ -243,6 +341,11 @@
return $this->Children[$cur]->GetChild($left);
}
+ function &GetFirstChild()
+ {
+ return $this->firstChild;
+ }
+
/**
* Returns node value by given path
*
@@ -294,6 +397,10 @@
return $child;
}
}
+ if (isset($child) && is_object($child)) {
+ $child->_destruct();
+ }
+ unset($child);
$false = false;
return $false;
}
@@ -308,10 +415,12 @@
function FindChildValue($name, $attr=null)
{
$child =& $this->FindChild($name);
+
if ($child !== false) {
if (isset($attr)) {
return $child->Attributes[strtoupper($attr)];
}
+
return $child->Data;
}
}
@@ -368,28 +477,84 @@
function GetXML($content_only = false)
{
$xml = '';
+ $single = (!$this->Data && count($this->Children) == 0);
+
if (!$content_only) {
- $xml = '<'.$this->Name;
- if (count($this->Attributes)) {
+ $xml = '<'.$this->OriginalName;
+
+ if (count($this->OriginalAttributes)) {
$xml .= ' ';
$att_contents = array();
- foreach ($this->Attributes as $name => $value) {
- $att_contents[] = $name.'="'.$value.'"';
+ foreach ($this->OriginalAttributes as $name => $value) {
+ $att_contents[] = $name.'="'.htmlspecialchars($value).'"';
}
$xml .= implode(' ', $att_contents);
}
- $xml .= '>';
- }
- $xml .= $this->Data;
- foreach ($this->Children as $node) {
- /* @var $node kXMLNode */
- $xml .= $node->GetXML($node->Name == '_TEXT_' ? true : false);
+ $xml .= $single ? '/>' : '>';
}
- if (!$content_only) {
- $xml .= ''.$this->Name.'>';
+ if (!$single) {
+ if ($content_only) {
+ $xml .= $this->Data;
+ }
+ else {
+ $xml .= preg_match('/&|', $this->Data) ? 'Data.']]>' : $this->Data;
+ }
+
+ foreach ($this->Children as $node) {
+ /* @var $node kXMLNode */
+
+ $xml .= $node->GetXML($node->Name == '_TEXT_' ? true : false);
+ }
+
+ if (!$content_only) {
+ $xml .= ''.$this->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;
+ }
}
\ No newline at end of file