ACC SHELL
<?php
/**
* @file xmlutils.php
* XML helper utilities.
*
* If you need to output elements, never do simple string replacement
* and then <xsl:value-of disable-output-escaping='yes' ... /> !
* It's evil and you will definitely at least once forget to properly
* escape it. Used these routines instead and then <xsl:copy-of ... />.
*
* Note that code in this file is real WTF, but I was not able to
* create anything simpler without loosing flexibility.
* Tell me if you have some good idea how to make this code
* shorter or more readable. Otherwise you can try to send this
* code to http://www.thedailywtf.com :-)
*/
/*
Easy PHP Framework
Copyright (c) 2005 Michal Molhanec
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but
is not required.
2. Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any
source distribution.
*/
/**
* Instances of this class serves for creating
* XML elements. To be exact, you can create
* any <a href='http://www.php.net/manual/en/ref.dom.php'>DOMNode</a>
* descendant in this class descendants.
*/
class ElementCreator {
/// Element's name
protected $name;
/// Element's attributes. Keys are used as attribute names,
/// values as their values.
protected $attrs = array();
/**
* @param[in] $name Elements name.
* @param[in] $attrs Array of attributes or NULL. Keys are used as
* attribute names, values as their values.
*/
function __construct($name, $attrs = NULL) {
$this->name = $name;
if ($attrs):
$this->attrs = $attrs;
endif;
}
/**
* Call this to get new XML element. By default it creates a
* <a href='http://www.php.net/manual/en/ref.dom.php'>DOMElement</a>
* but descendants can return arbitrary <a href='http://www.php.net/manual/en/ref.dom.php'>DOMNode</a>
* descendant.
* @param[in] $doc <a href='http://www.php.net/manual/en/ref.dom.php'>DOMDocument</a> instance.
* @param[in] $content String with element's content or NULL.
* @return <a href='http://www.php.net/manual/en/ref.dom.php'>DOMElement</a> instance or
* other <a href='http://www.php.net/manual/en/ref.dom.php'>DOMNode</a>
* descendant in descendants of this class.
*/
function create_element($doc, $content = NULL) {
$elem = $doc->createElement($this->name);
foreach ($this->attrs as $name => $value):
$elem->setAttribute($name, $value);
endforeach;
if ($content):
$contentNode = $doc->createTextNode($content);
$elem->appendChild($contentNode);
endif;
return $elem;
}
}
/**
* ElementCreator descendant which allows you to set regex for
* extracting part of passed content text to use for creating the
* element. I guess that nobody have understood previous sentence.
*/
class ExtractingElementCreator extends ElementCreator {
/// Regular expression used for extracting
protected $regex;
/**
* @see ElementCreator::__construct()
* @param[in] $regex Regular expression used for extracting.
*/
function __construct($regex, $name, $attrs = NULL) {
$this->regex = $regex;
parent::__construct($name, $attrs);
}
/**
* Creates the XML element.
* @see ElementCreator::create_element()
* @param[in] $content String content of the element. It's passed
* to the <a href='http://www.php.net/manual/en/function.preg-match.php'>preg_match</a>
* function and first captured parenthesized subpattern
* is used as a real content of the element.
* @throw Exception If there is no match.
*/
function create_element($doc, $content) {
if (preg_match($this->regex, $content, $matches)):
return parent::create_element($doc, $matches[1]);
endif;
throw new Exception(
sprintf(
"ExtractingElementCreator::create_element() error: Nothing matched! Regex: >%s< String: >%s<",
$this->regex, $content
)
);
}
}
/**
* Helper function for the xml_convert_any2elem() function.
* It splits the string into an array of <a href='http://www.php.net/manual/en/ref.dom.php'>DOMText</a>
* and <a href='http://www.php.net/manual/en/ref.dom.php'>DOMElement</a>
* (or other <a href='http://www.php.net/manual/en/ref.dom.php'>DOMNode</a>
* descendant) instances.
* @param[in] $doc <a href='http://www.php.net/manual/en/ref.dom.php'>DOMDocument</a> instance.
* @param[in] $text String which is going to be converted.
* @param[in] $str Separator.
* @param[in] $elemcreator Instance of ElementCreator class.
* @return Array of DOMNodes, there is always at least one DOMText instance.
*/
function str_splitter($doc, $text, $str, $elemcreator) {
$text_parts = explode($str, $text);
$first = TRUE;
$result = array();
foreach ($text_parts as $text_part):
if ($first):
$first = FALSE;
else:
$result[] = $elemcreator->create_element($doc);
endif;
if(strlen($text_part) > 0):
$result[] = $doc->createTextNode($text_part);
endif;
endforeach;
return $result;
}
/**
* Helper function for the xml_convert_any2elem() function.
* It splits the string into an array of <a href='http://www.php.net/manual/en/ref.dom.php'>DOMText</a>
* and <a href='http://www.php.net/manual/en/ref.dom.php'>DOMElement</a>
* (or other <a href='http://www.php.net/manual/en/ref.dom.php'>DOMNode</a>
* descendant) instances.
* @param[in] $doc <a href='http://www.php.net/manual/en/ref.dom.php'>DOMDocument</a> instance.
* @param[in] $text String which is going to be converted.
* @param[in] $regex Regular expression used for splitting the string.
* Note that if you need to use rounding brackets
* inside of the regex you should mark them
* <a href='http://www.php.net/manual/en/reference.pcre.pattern.syntax.php'>non-capturing (?:)</a>
* otherwise strange things will happen.
* @param[in] $elemcreator Instance of ElementCreator class.
* @return Array of DOMNodes, there is always at least one DOMText instance.
*/
function regex_splitter($doc, $text, $regex, $elemcreator) {
$text_parts = preg_split($regex, $text, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$result = array();
foreach ($text_parts as $text_part):
if (preg_match($regex, $text_part)):
$result[] = $elemcreator->create_element($doc, $text_part);
else:
$result[] = $doc->createTextNode($text_part);
endif;
endforeach;
return $result;
}
/**
* Converts string to an array of <a href='http://www.php.net/manual/en/ref.dom.php'>DOMNode</a>
* descendants.
* @param[in] $doc <a href='http://www.php.net/manual/en/ref.dom.php'>DOMDocument</a> instance.
* @param[in] $text String which is going to be converted. It can be
* also array of <a href='http://www.php.net/manual/en/ref.dom.php'>DOMNode</a>
* instances (so you can call this function on a value
* returned by another call to this function).
* @param[in] $str Anything (e.g. plain string or regex) which will be
* used for splitting the text. This value will be passed
* to the splitter function.
* @param[in] $elemcreator Instance of ElementCreator class or string
* which will be wrapped into a ElementCreator
* instance (the string will be used as an elements
* name).
* @param[in] $splitter Function which will be used to split the string.
* See str_splitter() and regex_splitter().
* @param[in] $recurse If is set to true than also text nodes which are
* children of existing elements are processed.
* Defaults to false.
* @return Array of DOMNodes, there is always at least one DOMText instance.
*/
function xml_convert_any2elem($doc, $text, $str, $elemcreator, $splitter, $recurse = FALSE) {
if (is_string($elemcreator)):
$elemcreator = new ElementCreator($elemcreator);
endif;
if (is_array($text)):
$result = array();
foreach ($text as $part):
$result = array_merge($result, xml_convert_any2elem($doc, $part, $str, $elemcreator, $splitter, $recurse));
endforeach;
return $result;
endif;
if ($text instanceof DomText):
return xml_convert_any2elem($doc, $text->wholeText, $str, $elemcreator, $splitter, $recurse);
endif;
if ($text instanceof DomElement):
// recursion means that we are converting also content of already
// created elements
// we will convert childnodes, result of their conversion put into
// an array and then replace childnodes with it
if ($recurse):
$result = array();
foreach ($text->childNodes as $childNode):
// text nodes convert
if ($childNode instanceof DomText):
$result = array_merge($result, xml_convert_any2elem($doc, $childNode, $str, $elemcreator, $splitter, $recurse));
// element nodes recurse
elseif ($childNode instanceof DomElement):
xml_convert_any2elem($doc, $childNode, $str, $elemcreator, $splitter, $recurse);
$result[] = $childNode;
// other nodes (e.g. attributes) copy
else:
assert($childNode instanceof DomNode);
$result[] = $childNode;
endif;
endforeach;
// now remove existing childnodes ...
$node_list = clone $text->childNodes;
foreach ($node_list as $childNode):
$text->removeChild($childNode);
endforeach;
// ... and add new ones
foreach ($result as $newChild):
assert($newChild instanceof DomNode);
$text->appendChild($newChild);
endforeach;
endif;
// we have to return ourselves for a case that DomElement
// is in the array. see the "if (is_array($text))" part to know why
// is this needed
return array($text);
endif;
assert(is_string($text));
if (strlen($text) == 0):
return array($doc->createTextNode(''));
endif;
return $splitter($doc, $text, $str, $elemcreator);
}
/**
* Shortcut for calling xml_convert_any2elem() with str_splitter().
*/
function xml_convert_str2elem($doc, $text, $str, $elemcreator, $recurse = FALSE) {
return xml_convert_any2elem($doc, $text, $str, $elemcreator, 'str_splitter', $recurse);
}
/**
* Shortcut for calling xml_convert_any2elem() with regex_splitter().
*/
function xml_convert_regex2elem($doc, $text, $regex, $elemcreator, $recurse = FALSE) {
return xml_convert_any2elem($doc, $text, $regex, $elemcreator, 'regex_splitter', $recurse);
}
?>
ACC SHELL 2018