<?php
// {{{ Header
/*
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| SIERRA : PHP Application Framework http://code.google.com/p/sierra-php |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Copyright 2005 Jason Read |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.|
| See the License for the specific language governing permissions and |
| limitations under the License. |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
*/
// }}}
// {{{ Constants
/**
* SRA_XmlParser class debug flag
*
* @type String
* @access public
*/
define('SRA_XMLPARSER_DEBUG', FALSE);
// }}}
// {{{ Includes
// }}}
// {{{ SRA_XmlParser
/**
* This class is used to parse xml files, maintain xml data structures,
* cache xml data structures, and write data to xml files. It also
* performs some xml file validation including checking for existence of
* the required header. All xml tags are case insensitive. This parser
* uses only lower-case xml tags.
*
* @author Jason Read <hide@address.com>
* @package sierra.util
*/
class SRA_XmlParser {
// {{{ Properties
/**
* used to store any errors generated by this class constructor
* @type Object
* @access public
*/
var $err = NULL;
/**
* Static array of SRA_XmlParser objects used as cache by the getXmlParser
* static method.
*
* STATIC: Maintained in getXmlParser method
*
* @type SRA_XmlParser[]
* @access private
var $_cachedXmlParser;
*/
/**
* The name of the cached xml array file.
* @type String
* @access private
*/
var $_cacheFileName;
/**
* Attribute used to store the data for the xml file. This will be an
* associative data structure (key=element).
* @type Object
* @access private
*/
var $_data;
/**
* Whether or not the root tag was included in the associative array
* data structure when the file was parsed.
* @type boolean
* @access private
*/
var $_includeRootTag;
/**
* The value of the root tag.
* @type String
* @access private
*/
var $_rootTag;
/**
* Used by the _process* methods to maintain the relevant parse related
* data. This attribute will be an associative array with the
* following sub-elements:
*
* val
* currTag
* levels
* prevTag
* multipleData
* xml
*
* @type Object
* @access private
*/
var $_xmlParseData = array();
/**
* The name of the file from which the xml data was extracted.
* @type String
* @access private
*/
var $_xmlParserFile;
// }}}
// {{{ SRA_XmlParser()
/**
* Class constructor. This method loads the data from the xml file
* specified into the _data attribute as an associative multi-level
* array. This parser uses all lower-case xml tags even if the tags
* are otherwise written in the source xml file. This method is
* private and should not be directly accessed. Instead, new SRA_XmlParser
* objects should be retrieved through the SRA_XmlParser::getXmlParser
* static method.
*
* @param xmlParserFile : String - The name of the file to parse.
* This name must include the .xml file extension. alternatively, this can
* be an xml string
* @param includeRootTag : boolean - Whether or not the root tag
* should be included in the associative array this method generates
* (all data will then be accessible through
* $objectReference["root_tag"]["..."]).
* @param createFile : boolean - Whether or not the file should be
* created if it does not exist. By default this parameter is false.
* @param rootTag : String - The root tag to use if the createFile
* parameter is true.
* @access private
* @return
* @author Jason Read <hide@address.com>
*/
function SRA_XmlParser($xmlParserFile, $includeRootTag=FALSE, $createFile=FALSE, $rootTag=FALSE)
{
// Check if file exists
if (!file_exists($xmlParserFile) && !strstr($xmlParserFile, 'http') && !strstr($xmlParserFile, 'ftp') && !strpos($xmlParserFile, '>'))
{
// SRA_File does not exist and should not be created
if (!$createFile)
{
$msg = "SRA_XmlParser::SRA_XmlParser: Failed - SRA_File '${xmlParserFile}' does not exist and will not be created.";
$this->err = SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
return;
}
// Root tag was not specified, file cannot be created
else if (!$rootTag)
{
$msg = "SRA_XmlParser::SRA_XmlParser: Failed - SRA_File '${xmlParserFile}' cannot be created, rootTag parameter was not specified.";
$this->err = SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
return;
}
// SRA_File cannot be written
if (SRA_Error::isError(SRA_File::write($xmlParserFile, $rbuffer)))
{
$msg = "SRA_XmlParser::SRA_XmlParser: Failed - SRA_File '${xmlParserFile}' could not be written to.";
$this->err = SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
return;
}
$msg = "SRA_XmlParser::SRA_XmlParser: SRA_File '${xmlParserFile}' does not exist and was created.";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
}
$this->_xmlParserFile = $xmlParserFile;
$this->_includeRootTag = $includeRootTag;
$this->_rootTag = $rootTag;
// Check cache for array
if (is_file($this->_xmlParserFile)) {
$this->_cacheFileName = SRA_TMP_DIR . '/' . str_replace('/', '.', $xmlParserFile) . $includeRootTag . '.php';
if (file_exists($this->_cacheFileName))
{
// Verify that cache file is up to date
if (SRA_File::compareMTimes($this->_cacheFileName, $xmlParserFile) != -1)
{
include($this->_cacheFileName);
if (!isset($xml) || !is_array($xml))
{
unlink($this->_cacheFileName);
$msg = 'SRA_XmlParser::SRA_XmlParser: Failed - Cache file "' . $this->_cacheFileName .
'" does not contain valid data. Deleting, and continuing.';
SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
else
{
$xmlKeys = array_keys($xml);
$this->_rootTag = $xmlKeys[0];
if ($this->_includeRootTag)
{
$this->_data =& $xml;
}
else
{
$this->_data =& $xml[$this->_rootTag];
}
$msg = "SRA_XmlParser::SRA_XmlParser: Accessing xml array from cache ($this->_cacheFileName) for file: ${xmlParserFile}";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
return;
}
}
else
{
unlink($this->_cacheFileName);
$msg = 'SRA_XmlParser::SRA_XmlParser: Cache file "' . $this->_cacheFileName . '" has expired. Deleting and continuing.';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
}
}
}
// Parse file
if (SRA_Error::isError($this->_process()))
{
$msg = "SRA_XmlParser::SRA_XmlParser: Failed - SRA_File '${xmlParserFile}' could not be parsed. _process method returned an error";
$this->err = SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
return;
}
else
{
unset($this->_xmlParseData);
}
}
// }}}
// {{{ arrayToXML()
/**
* Static method used to convert an associative array back to xml format.
*
* @param array. Object. The associative array to convert. values in this array
* should be properly encoded already
* @param margin. int. Used to inset each level of tags.
* @param parentKey. String. The key used by the parent of the array.
* @access public
* @return String
* @author Jason Read <hide@address.com>
*/
function & arrayToXML($array, $margin = 0, $parentKey = false)
{
$keys = array_keys($array);
$xmlBuffer = '';
$spaceBuffer = '';
for ($i=0; $i<$margin; $i++)
{
$spaceBuffer .= ' ';
}
foreach ($keys as $key)
{
$attrKey = $key;
$data = $array[$key];
// 2..* values
if (is_array($array[$key]))
{
$skeys = array_keys($array[$key]);
if ($skeys[0] == '0' && !isset($array[$key][$skeys[0]]['attributes']))
{
foreach ($skeys as $skey)
{
// render open
if ($skey == '0' && is_array($array[$key][$skey])) {
$xmlBuffer .= "${spaceBuffer}<${key}>\n";
}
$msg = "SRA_XmlParser::arrayToXML: Key '${key}' contains 2..* sub elements. Adding '${skey}'";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
$temp = !is_array($array[$key][$skey]) ? array($key => $array[$key][$skey]) : $array[$key][$skey];
$xmlBuffer .= SRA_XmlParser::arrayToXML($temp, !is_array($array[$key][$skey]) ? $margin : $margin + 1, $key);
// render close
if ($skey == $skeys[count($skeys) - 1] && is_array($array[$key][$skey])) {
$xmlBuffer .= "${spaceBuffer}</${key}>\n";
}
}
continue;
}
else if (is_array($array[$key][$skeys[0]]) && array_key_exists('attributes', $array[$key][$skeys[0]]) &&
is_array($array[$key][$skeys[0]]['attributes']) && array_key_exists('key', $array[$key][$skeys[0]]['attributes']))
{
foreach ($skeys as $skey)
{
$msg = "SRA_XmlParser::arrayToXML: Key '${key}' contains 1..* sub elements with keys. Adding '${skey}'";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
$temp = array($key => $array[$key][$skey]);
$xmlBuffer .= SRA_XmlParser::arrayToXML($temp, $margin, $key);
}
continue;
}
}
// Add attributes
if (is_array($array[$key]) && array_key_exists('attributes', $array[$key]))
{
$data = isset($array[$key]['xml_value']) ? $array[$key]['xml_value'] : $data;
$msg = "SRA_XmlParser::arrayToXML: Key '${key}' contains attributes.";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
$akeys = array_keys($array[$key]['attributes']);
$ukey = false;
foreach ($akeys as $akey)
{
if ($akey == '_akey' || ($akey == 'key' && in_array('_akey', $akeys))) { continue; }
$xmlBuffer .= ' ' . $akey . '="' . htmlspecialchars($array[$key]['attributes'][$akey], ENT_COMPAT) . '"';
}
if (count($array[$key]) == 1)
{
$msg = "SRA_XmlParser::arrayToXML: Key '${key}' is an empty tag.";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
$xmlBuffer = $spaceBuffer . '<' . $attrKey . $xmlBuffer . "/>\n";
continue;
}
}
$xmlBuffer = $spaceBuffer . '<' . $attrKey . $xmlBuffer . '>';
// Add data
if (!is_array($data))
{
$msg = "SRA_XmlParser::arrayToXML: Key '${key}' is a data tag.";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
$xmlBuffer .= $data . '</' . $attrKey . ">\n";
}
// Add sub-elements
else
{
$ukeys = array_keys($data);
$xmlBuffer .= "\n";
foreach ($ukeys as $ukey)
{
if ($ukey != 'attributes')
{
$msg = "SRA_XmlParser::arrayToXML: Key '${key}' contains distinct sub elements. Adding '${ukey}'";
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
$temp = array($ukey => $data[$ukey]);
$xmlBuffer .= SRA_XmlParser::arrayToXML($temp, $margin + 1, $attrKey);
}
}
$xmlBuffer .= $spaceBuffer . '</' . $attrKey . ">\n";
}
}
return $xmlBuffer;
}
// }}}
// {{{ deleteCache()
/**
* Used to delete a cached xml file
*
* @param xmlParserFile : String - path to the xml file
* @param includeRootTag : boolean - Whether or not the root tag
* should be included
* @access public
* @return boolean
* @author Jason Read <hide@address.com>
*/
function deleteCache($xmlParserFile, $includeRootTag=FALSE)
{
// Check cache for array
$cacheFileName = SRA_Controller::getSysTmpDir() . '/' . str_replace('/', '.', $xmlParserFile) . $includeRootTag . '.php';
if (file_exists($cacheFileName))
{
unlink($cacheFileName);
}
}
// }}}
// {{{ getCacheFileName()
/**
* Returns the value of the _cacheFileName attribute.
*
* @access public
* @return String
* @author Jason Read <hide@address.com>
*/
function getCacheFileName()
{
if (isset($this->_cacheFileName))
{
return($this->_cacheFileName);
}
}
// }}}
// {{{ getData()
/**
* Returns a reference to the _data attribute or to the keys data
* specified by the sub parameter. If the requested element is a data
* element (contains no sub-elements) with attributes, then only the
* data will be returned.
*
* @param keys : String[] - An array of strings representing a sub
* element that should be returned from the _data attribute. If the
* sub-element does not exist, this method will return an OPERATIONAL
* level error. For example, if the _data structure was of the format:
*
* array( "root" => array("level1" => array("level2" => "text")))
*
* and the level2 element was needed, this parameter would be of the
* following format:
*
* keys = array("root", "level1", "level2")
*
* The order of this parameter is important. Using the example above,
* the following parameter would generate an SRA_Error:
*
* keys = array("root", "level2", "level1")
*
* because there is not "level2" node within the "root" node.
*
* @param importFile : boolean - Whether or not the return value
* should be the contents of a file if the data references
* a file. SRA_File data elements are designated by setting the
* 'is_file' attribute to 1. The default value for this
* parameter is true.
* @access public
* @return Object
* @author Jason Read <hide@address.com>
*/
function & getData($keys=false, $importFile=true)
{
if (isset($this->_data))
{
// Convert single elements to an array
if ($keys && !is_array($keys))
{
$keys = array($keys);
}
// Return all data
if (!$keys)
{
return $this->_data;
}
// Return sub-data
else
{
$xml =& $this->_data;
foreach ($keys as $key)
{
// SRA_Error
if (!is_scalar($key) || !is_array($xml))
{
$msg = 'SRA_XmlParser::getData: Failed - Invalid data types for xml file: "' .
$this->_xmlParserFile . '" - key: ' . gettype($key) . ' xml: ' . gettype($xml);
return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
// Sub data does not exist
if (!array_key_exists($key, $xml))
{
$msg = "SRA_XmlParser::getData: Failed - Sub-key '${key}' does not exist (Sub-keys - " . implode('::', $keys) . ') in xml file: "' .
$this->_xmlParserFile . '"';
return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_OPERATIONAL, SRA_XMLPARSER_DEBUG);
}
else
{
$temp =& $xml;
$xml =& $temp[$key];
}
}
$msg = 'SRA_XmlParser::getData: Sub-keys - "' . implode('::', $keys) . '" found in xml file: "' . $this->_xmlParserFile . '"';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
// Import file if specified
if ($importFile && array_key_exists('attributes', $xml) && array_key_exists('is_file', $xml['attributes']) &&
$xml['attributes']['is_file'] == 1)
{
if (file_exists($xml['xml_value']))
{
$msg = 'SRA_XmlParser::getData: Returning contents of file: "' . $xml['xml_value'] . '"';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
return SRA_File::toString($xml['xml_value']);
}
else
{
$msg = 'SRA_XmlParser::getData: SRA_Error - Sub-keys - ' . implode('::', $keys) . ') in xml file: "' .
$this->_xmlParserFile . '" specify is_file=1 for file that does not exist. SRA_File: "' .
$xml['xml_value'] . '", returning element data instead';
SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
}
// Only return data if data element with attributes
if (array_key_exists('xml_value', $xml) && (!isset($xml['attributes']) || !count($xml['attributes'])))
{
$msg = 'SRA_XmlParser::getData: Data element found with attributes. Returning data only.';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
return $xml['xml_value'];
}
else
{
return $xml;
}
}
}
}
// }}}
// {{{ setData
/**
* sets the xml data
* @param mixed $data the data to set
* @access public
* @return void
*/
function setData(& $data) {
$this->_data =& $data;
}
// }}}
// {{{ getDataAttributes()
/**
* Similiar to the getData method except that this method returns the
* attributes associated with the data. It returns an empty array if
* none exist.
*
* @param keys : String[] - An array of strings representing a sub
* element that should be returned from the _data attribute.
* See SRA_XmlParser::getData api for more information.
* @access public
* @return Object
* @author Jason Read <hide@address.com>
*/
function & getDataAttributes($keys=false)
{
if (isset($this->_data))
{
// Convert single elements to an array
if ($keys && !is_array($keys))
{
$keys = array($keys);
}
// Return all data
if (!$keys)
{
return $this->_data;
}
// Return sub-data
else
{
$xml =& $this->_data;
foreach ($keys as $key)
{
// Sub data does not exist
if (!array_key_exists($key, $xml))
{
$msg = "SRA_XmlParser::getDataAttributes: Failed - Sub-key '${key}' does not exist (Sub-keys - " . implode('::', $keys) . ") in xml file: '" .
$this->_xmlParserFile . "'";
return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_OPERATIONAL, SRA_XMLPARSER_DEBUG);
}
else
{
$temp =& $xml;
$xml =& $temp[$key];
}
}
$msg = 'SRA_XmlParser::getDataAttributes: Sub-keys - "' . implode('::', $keys) . '" found in xml file: "' . $this->_xmlParserFile . '"';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
// Only return data if data element with attributes
if (array_key_exists('attributes', $xml))
{
$msg = 'SRA_XmlParser::getDataAttributes: Attributes found. Returning';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
return $xml['attributes'];
}
else
{
$msg = 'SRA_XmlParser::getDataAttributes: Attributes NOT found. Returning empty array.';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
return array();
}
}
}
}
// }}}
// {{{ getRootTag()
/**
* Returns the value of the _rootTag attribute.
*
* @access public
* @return String
* @author Jason Read <hide@address.com>
*/
function getRootTag()
{
if (isset($this->_rootTag))
{
return($this->_rootTag);
}
}
// }}}
// {{{ getXmlParser()
/**
* Static method used to instantiate a new SRA_XmlParser object, or return
* an existing one. This method maintains a cached array of SRA_XmlParser
* objects. If one is requested that has already been instantiated, a
* reference to the existing object is returned. Otherwise, an new
* object is instantiated, added to the cache, and returned as a
* reference.
*
* @param xmlParserFile : String - The name of the file to parse. See
* constructor api for more info. alternative, this can be an xml string
* @param includeRootTag : boolean - Whether or not to include the
* root tag in the data structure. See constructor api for more info.
* @param createFile : boolean - Whether or not the file should be
* created if it does not exist. See constructor api for more info.
* @param rootTag : String - The value of the root tag (only applies
* if creating a new xml file). See constructor api for more info.
* @param boolean $noCache whether or not to return a cached parser if
* available
* @access public
* @return SRA_XmlParser
* @author Jason Read <hide@address.com>
*/
function & getXmlParser($xmlParserFile, $includeRootTag=FALSE, $createFile=FALSE, $rootTag=NULL, $noCache=FALSE)
{
static $_cachedXmlParser = array();
if (!is_file($xmlParserFile) || !array_key_exists($xmlParserFile . $includeRootTag, $_cachedXmlParser) || $noCache) {
if (SRA_XMLPARSER_DEBUG) {
$msg = 'SRA_XmlParser::getXmlParser: Instantiating new SRA_XmlParser object for file: "' . $xmlParserFile . '"';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
}
$parser = new SRA_XmlParser($xmlParserFile, $includeRootTag, $createFile, $rootTag);
if (is_file($xmlParserFile)) {
$_cachedXmlParser[$xmlParserFile . $includeRootTag] =& $parser;
}
if (!SRA_XmlParser::isValid($parser)) {
$msg = 'SRA_XmlParser::getXmlParser: Failed - Could not instantiate new SRA_XmlParser object for file: "' . $xmlParserFile . '"';
return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
else if (SRA_XMLPARSER_DEBUG) {
$msg = 'SRA_XmlParser::getXmlParser: SRA_XmlParser object instantiated successfully for file: "' . $xmlParserFile . '"';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
}
return $parser;
}
else {
$msg = 'SRA_XmlParser::getXmlParser: Returning cached SRA_XmlParser object for file: "' . $xmlParserFile . '"';
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
}
return $_cachedXmlParser[$xmlParserFile . $includeRootTag];
}
// }}}
// {{{ getXmlParserFile()
/**
* Returns the value of the _xmlParserFile attribute.
*
* @access public
* @return String
* @author Jason Read <hide@address.com>
*/
function getXmlParserFile()
{
if (isset($this->_xmlParserFile))
{
return($this->_xmlParserFile);
}
}
// }}}
// {{{ isCached
/**
* used to check if an xml file is cached
* @param string $xmlParserFile path to the xml file
* @param boolean $includeRootTag whether or not the root tag should be
* included
* @access public
* @return boolean
*/
function isCached($xmlParserFile, $includeRootTag=FALSE) {
// Check cache for array
$cacheFileName = SRA_Controller::getSysTmpDir() . '/' . str_replace('/', '.', $xmlParserFile) . $includeRootTag . '.php';
if (file_exists($cacheFileName)) {
return SRA_File::compareMTimes($cacheFileName, $xmlParserFile) != -1 ? TRUE : FALSE;
}
}
// }}}
// {{{ isDirty()
/**
* Returns true if the data associated with this SRA_XmlParser is dirty,
* false otherwise.
*
* @access public
* @return boolean
* @author Jason Read <hide@address.com>
*/
function isDirty()
{
if (SRA_XmlParser::isValid($xmlParser = new SRA_XmlParser($this->_xmlParserFile, $this->_includeRootTag, false, false)))
{
return ($this->_data != $xmlParser->getData());
}
// SRA_XmlParser object copy could not be instantiated, generate SRA_Error and return false
else
{
$msg = 'SRA_XmlParser::isDirty: Failed - Could not instantiate SRA_XmlParser object copy';
SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
return false;
}
// }}}
// {{{ isIncludeRootTag()
/**
* Returns the value of the _includeRootTag attribute.
*
* @access public
* @return boolean
* @author Jason Read <hide@address.com>
*/
function isIncludeRootTag()
{
if (isset($this->_includeRootTag))
{
return($this->_includeRootTag);
}
}
// }}}
// {{{ isValid()
/**
* Static method used to validate a SRA_XmlParser object.
*
* @param object. Object. The object to validate.
* @access public
* @return boolean
* @author Jason Read <hide@address.com>
*/
function isValid(& $object)
{
return (is_object($object) && (!isset($object->err) || !SRA_Error::isError($object->err)) && strtolower(get_class($object)) == 'sra_xmlparser');
}
// }}}
// {{{ write
/**
* This method writes the current SRA_XmlParser data to the associated xml and
* cache files overwriting any existing data in those file.
* @param string $xmlFile an optional parameter specifying another xml file to
* write to. If this parameter is not specified, the $xmlFile specified in the
* constructor will be written to
* @param string $header an optional header to include when writing to the
* xml file
* @access public
* @return void
*/
function write($xmlFile=NULL, $header=NULL) {
$xmlFile = $xmlFile ? $xmlFile : $this->_xmlParserFile;
$xml = !$this->_includeRootTag ? array($this->_rootTag => $this->_data) : $this->_data;
SRA_Util::printDebug('SRA_XmlParser::write: Writing XML structure - ', SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
SRA_Util::printDebug($xml, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
if (SRA_Error::isError($rbuffer =& SRA_XmlParser::arrayToXML($xml))) {
$msg = 'SRA_XmlParser::write: Failed - Could not get xml buffer (SRA_XmlParser::arrayToXML failed) for file: "' . $xmlParserFile . '"';
return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
else {
$msg ='SRA_XmlParser::write: xml buffer created, writing xml back to file: ' . $this->_xmlParserFile;
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
if ($header) $rbuffer = $header . $rbuffer;
if (SRA_Error::isError(SRA_File::write($xmlFile, $rbuffer))) {
$msg = 'SRA_XmlParser::write: Failed - Could not write xml buffer to file: "' . $xmlParserFile . '"';
return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
else {
$this->_cache();
}
}
}
// }}}
// {{{ _cache()
/**
* Writes the existing SRA_XmlParser data to a cache file.
*
* @access public
* @return void
* @author Jason Read <hide@address.com>
*/
function _cache() {
if ($this->_cacheFileName) {
// Cache array to file for future reference
if (SRA_XMLPARSER_DEBUG) {
$msg ='SRA_XmlParser::_cache: Writing xml array to cache for file: ' . $this->_xmlParserFile;
SRA_Util::printDebug($msg, SRA_XMLPARSER_DEBUG, __FILE__, __LINE__);
}
$xml = !$this->_includeRootTag ? array($this->_rootTag => & $this->_data) : $xml = & $this->_data;
if (is_array($xml)) {
SRA_File::arrayToFile($this->_cacheFileName, 'xml', $xml);
exec('chmod 666 ' . $this->_cacheFileName);
}
else {
$msg = 'SRA_XmlParser::_cache: Failed - _data attribute is not an array: "' . gettype($this->_data) . '"';
return SRA_Error::logError($msg, __FILE__, __LINE__, SRA_ERROR_PROBLEM, SRA_XMLPARSER_DEBUG);
}
}
}
// }}}
// {{{ _checkForSingleLevel()
/**
* checks for and promotes single level elements in $data
*
* @access public
* @return void
*/
function _checkForSingleLevel(& $data) {
if (is_array($data) && count($data) == 1 && ($keys = array_keys($data)) && isset($data[$keys[0]]) && isset($data[$keys[0]]['attributes']) && isset($data[$keys[0]]['attributes']['_akey'])) {
$ukey = $keys[0];
$keys = array_keys($data[$ukey]);
foreach($keys as $key) {
$data[$key] = $data[$ukey][$key];
}
unset($data[$ukey]);
}
if (is_array($data)) {
$keys = array_keys($data);
foreach($keys as $key) {
$this->_checkForSingleLevel($data[$key]);
}
}
}
// }}}
// {{{ _getKey()
/**
* This method is used to provide the functionality for retrieving an
* xml special attribute 'key' which defines the index key to use in
* the resultant associative array.
*
* @param tag : String - The tag to get the data for. If data does
* not exist for this tag, the value of this tag will be returned.
* @access public
* @return void
* @author Jason Read <hide@address.com>
*/
function _getKey($tag)
{
$key = '';
$limit = count($this->_xmlParseData['keys']);
for ($i=$this->_xmlParseData['key']; $i<$limit; $i++)
{
// Data found
if ($this->_xmlParseData['vals'][$i]['type'] == 'complete' &&
strtolower($this->_xmlParseData['vals'][$i]['tag']) == strtolower($tag) &&
isset($this->_xmlParseData['vals'][$i]['value']))
{
$key = $this->_xmlParseData['vals'][$i]['value'];
break;
}
// End found
if ($this->_xmlParseData['vals'][$i]['type'] == 'close' &&
$this->_xmlParseData['vals'][$i]['tag'] == $this->_xmlParseData['val']['tag'])
{
break;
}
}
// If data not found, return value of tag
if ($key == '')
{
$key = $tag;
}
$key = preg_replace_callback("'php::(.*?)::php'si", 'codeToString', $key);
return $key;
}
// }}}
// {{{ _process()
/**
* Method used to begin parsing of an xml file. Also manages xml file
* cache.
*
* @access private
* @return void
* @author Jason Read <hide@address.com>
*/
function _process()
{
$p = xml_parser_create();
xml_parse_into_struct($p, (is_file($this->_xmlParserFile) ? SRA_File::toString($this->_xmlParserFile) : $this->_xmlParserFile), $this->_xmlParseData['vals'], $index);
xml_parser_free($p);
// add dynamic keys
$keyptr = array(0);
$vkeys = array_keys($this->_xmlParseData['vals']);
foreach ($vkeys as $vkey) {
if ($this->_xmlParseData['vals'][$vkey]['type'] == 'open') {
array_push($keyptr, 0);
}
if (!isset($this->_xmlParseData['vals'][$vkey]['attributes']['KEY']) && ($this->_xmlParseData['vals'][$vkey]['type'] == 'complete' || $this->_xmlParseData['vals'][$vkey]['type'] == 'open')) {
if (!isset($this->_xmlParseData['vals'][$vkey]['attributes'])) { $this->_xmlParseData['vals'][$vkey]['attributes'] = array(); }
$this->_xmlParseData['vals'][$vkey]['attributes']['KEY'] = $this->_xmlParseData['vals'][$vkey]['type'] == 'complete' ? $keyptr[count($keyptr) - 1]++ : $keyptr[count($keyptr) - 2]++;
$this->_xmlParseData['vals'][$vkey]['attributes']['_AKEY'] = '1';
}
if ($this->_xmlParseData['vals'][$vkey]['type'] == 'close') {
array_pop($keyptr);
}
}
$this->_data = array();
$this->_xmlParseData['levels'] = array();
$this->_xmlParseData['multipleData'] = array();
$this->_xmlParseData['prevTag'] = '';
$this->_xmlParseData['currTag'] = '';
$this->_xmlParseData['topTag'] = false;
$this->_xmlParseData['keys'] = array_keys($this->_xmlParseData['vals']);
foreach ($this->_xmlParseData['keys'] as $this->_xmlParseData['key'])
{
// Set data
$this->_xmlParseData['val'] =& $this->_xmlParseData['vals'][$this->_xmlParseData['key']];
// Open tag
if ($this->_xmlParseData['val']['type'] == 'open')
{
if (!$this->_processOpen())
{
continue;
}
}
// Close tag
else if ($this->_xmlParseData['val']['type'] == 'close')
{
if (!$this->_processClose())
{
continue;
}
}
// Data tag
else if ($this->_xmlParseData['val']['type'] == 'complete' && isset($this->_xmlParseData['val']['value']))
{
$loc =& $this->_data;
foreach ($this->_xmlParseData['levels'] as $level)
{
$temp =& $loc[str_replace(':arr#', '', $level)];
$loc =& $temp;
}
$tag = strtolower($this->_xmlParseData['val']['tag']);
// Check for embedded php code
if (strstr($this->_xmlParseData['val']['value'], 'php::'))
{
$this->_xmlParseData['val']['value'] = preg_replace_callback("'php::(.*?)::php'si", 'codeToString',
$this->_xmlParseData['val']['value']);
}
// Check for attributes or attribute key
$addAttributes = false;
unset($dataKey);
if (array_key_exists('attributes', $this->_xmlParseData['val']))
{
if (array_key_exists('KEY', $this->_xmlParseData['val']['attributes']))
{
$dataKey = $this->_getKey($this->_xmlParseData['val']['attributes']['KEY']);
}
if (count($this->_xmlParseData['val']['attributes']) >= 1)
{
$keys = array_keys($this->_xmlParseData['val']['attributes']);
foreach ($keys as $key)
{
// Check for embedded php code
if (strstr($this->_xmlParseData['val']['attributes'][$key], 'php::'))
{
$this->_xmlParseData['val']['attributes'][$key] = preg_replace_callback("'php::(.*?)::php'si", 'codeToString',
$this->_xmlParseData['val']['attributes'][$key]);
}
$this->_xmlParseData['val']['attributes'][strtolower($key)] = $this->_xmlParseData['val']['attributes'][$key];
unset($this->_xmlParseData['val']['attributes'][$key]);
}
$addAttributes = true;
}
}
// Add to array
if ((is_array($loc) && array_key_exists($tag, $loc)) || isset($dataKey))
{
if (isset($loc[$tag]) && !is_array($loc[$tag]) && !isset($dataKey))
{
$loc[$tag] = array($loc[$tag]);
}
if (isset($dataKey))
{
if (!$addAttributes)
{
$loc[$tag][$dataKey] = $this->_xmlParseData['val']['value'];
}
else
{
$loc[$tag][$dataKey] = array('attributes' => $this->_xmlParseData['val']['attributes'],
'xml_value' => $this->_xmlParseData['val']['value']);
}
}
else
{
if (!$addAttributes)
{
$loc[$tag][] = $this->_xmlParseData['val']['value'];
}
else
{
$loc[$tag][] = array('attributes' => $this->_xmlParseData['val']['attributes'],
'xml_value' => $this->_xmlParseData['val']['value']);
}
}
}
// Add new element
else
{
if (!$addAttributes)
{
$loc[$tag] = $this->_xmlParseData['val']['value'];
}
else
{
$loc[$tag] = array('attributes' => $this->_xmlParseData['val']['attributes'],
'xml_value' => $this->_xmlParseData['val']['value']);
}
}
}
// Tag without data
else if ($this->_xmlParseData['val']['type'] == 'complete')
{
$this->_processOpen();
$this->_processClose();
}
}
// move root sub-elements up a level
if ($this->_includeRootTag) {
$keys = array_keys($this->_data);
if (isset($this->_data[$keys[0]][0])) {
$this->_data = array($keys[0] => $this->_data[$keys[0]][0]);
}
}
// move single elements up 1 level
$keys = array_keys($this->_data);
foreach($keys as $key) {
// not needed
//$this->_checkForSingleLevel($this->_data[$key]);
}
// Write to cache
$this->_cache();
}
// }}}
// {{{ _processClose()
/**
* Method used to process an xml close tag.
*
* @access private
* @return void
* @author Jason Read <hide@address.com>
*/
function _processClose()
{
// don't include top tag
if ($this->_xmlParseData['topTag'] && !$this->_includeRootTag && $this->_xmlParseData['val']['tag'] == $this->_xmlParseData['topTag'])
{
return false;
}
if (isset($this->_xmlParseData['currTag']) && array_key_exists($this->_xmlParseData['currTag'], $this->_xmlParseData['multipleData']) &&
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple'])
{
$tkeys = array_reverse(array_keys($this->_xmlParseData['multipleData']));
foreach ($tkeys as $tkey)
{
if ($this->_xmlParseData['multipleData'][$tkey]['multiple'] && !$this->_xmlParseData['multipleData'][$tkey]['popped'])
{
array_pop($this->_xmlParseData['levels']);
$this->_xmlParseData['multipleData'][$tkey]['popped'] = true;
break;
}
else if (!$this->_xmlParseData['multipleData'][$tkey]['multiple'])
{
break;
}
}
}
// Pop any additional key level attributes
$this->_xmlParseData['prevTag'] = array_pop($this->_xmlParseData['levels']);
if (strpos($this->_xmlParseData['prevTag'], 'arr#'))
{
$this->_xmlParseData['prevTag'] = array_pop($this->_xmlParseData['levels']);
}
return true;
}
// }}}
// {{{ _processOpen()
/**
* Method used to process an xml open tag.
*
* @access private
* @return void
* @author Jason Read <hide@address.com>
*/
function _processOpen()
{
// Set root tag
if (!$this->_xmlParseData['topTag'])
{
$this->_rootTag = strtolower($this->_xmlParseData['val']['tag']);
}
// don't include top tag
if (!$this->_xmlParseData['topTag'] && !$this->_includeRootTag)
{
$this->_xmlParseData['topTag'] = $this->_xmlParseData['val']['tag'];
return false;
}
$this->_xmlParseData['currTag'] = $this->_xmlParseData['val']['tag'];
$this->_xmlParseData['currTag'] = strtolower($this->_xmlParseData['val']['tag']);
$this->_xmlParseData['levels'][] = $this->_xmlParseData['currTag'];
// Add attributes array
$addAttributes = false;
unset($dataKey);
if (array_key_exists('attributes', $this->_xmlParseData['val']))
{
if (array_key_exists('KEY', $this->_xmlParseData['val']['attributes']))
{
$dataKey = $this->_getKey($this->_xmlParseData['val']['attributes']['KEY']);
}
if (count($this->_xmlParseData['val']['attributes']) >= 1)
{
$keys = array_keys($this->_xmlParseData['val']['attributes']);
foreach ($keys as $key)
{
// Check for embedded php code
if (strstr($this->_xmlParseData['val']['attributes'][$key], 'php::'))
{
$this->_xmlParseData['val']['attributes'][$key] = preg_replace_callback("'php::(.*?)::php'si", 'codeToString',
$this->_xmlParseData['val']['attributes'][$key]);
}
$this->_xmlParseData['val']['attributes'][strtolower($key)] = $this->_xmlParseData['val']['attributes'][$key];
unset($this->_xmlParseData['val']['attributes'][$key]);
}
$addAttributes = true;
}
}
// Multiple items w/ same name. Convert to array
$dataKeyPushed = false;
if ($this->_xmlParseData['prevTag'] === $this->_xmlParseData['currTag'])
{
if ((!array_key_exists($this->_xmlParseData['currTag'], $this->_xmlParseData['multipleData']) ||
!$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple']) && !isset($dataKey))
{
$loc =& $this->_data;
foreach ($this->_xmlParseData['levels'] as $level)
{
$temp =& $loc[$level];
$loc =& $temp;
}
$loc = array($loc);
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple'] = true;
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple_count'] = 0;
}
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['popped'] = false;
if (!isset($dataKey))
{
$this->_xmlParseData['levels'][] = ':arr#' . ++$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple_count'];
}
else
{
$dataKeyPushed = true;
$this->_xmlParseData['levels'][] = ':arr#' . $dataKey;
}
}
else
{
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple'] = false;
}
if (isset($dataKey))
{
if (!isset($loc))
{
$loc = false;
}
$loc = array($dataKey => $loc);
if (!$dataKeyPushed)
{
$this->_xmlParseData['levels'][] = ':arr#' . $dataKey;
}
$this->_xmlParseData['prevTag'] = $this->_xmlParseData['currTag'];
$this->_xmlParseData['currTag'] = $dataKey;
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple'] = true;
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['multiple_count'] = 1;
$this->_xmlParseData['multipleData'][$this->_xmlParseData['currTag']]['popped'] = false;
}
if ($addAttributes)
{
$loc =& $this->_data;
foreach ($this->_xmlParseData['levels'] as $level)
{
$temp =& $loc[str_replace(':arr#', '', $level)];
$loc =& $temp;
}
$loc['attributes'] = $this->_xmlParseData['val']['attributes'];
}
return true;
}
// }}}
}
// }}}
/* Keep this comment at the end of the file.
Local variables:
mode:c++
minor-mode:font-lock
indent-tabs-mode:nil
c-basic-offset:4
End:
*/
?>