Source for file Query2XML.php
Documentation is available at Query2XML.php
* This file contains the class XML_Query2XML and all exception classes.
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @copyright 2006 Lukas Feiler
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @version CVS: $Id: Query2XML.php 309897 2011-04-02 17:36:42Z lukasfeiler $
* @link http://pear.php.net/package/XML_Query2XML
* PEAR_Exception is used as the parent for XML_Query2XML_Exception.
require_once 'PEAR/Exception.php';
* Create XML data from SQL queries.
* XML_Query2XML heavily uses exceptions and therefore requires PHP5.
* Additionally one of the following database abstraction layers is
* required: PDO (compiled-in by default since PHP 5.1), PEAR DB,
* The two most important public methods this class provides are:
* <b>{@link XML_Query2XML::getFlatXML()}</b>
* Transforms your SQL query into flat XML data.
* <b>{@link XML_Query2XML::getXML()}</b>
* Very powerful and flexible method that can produce whatever XML data you want. It
* was specifically written to also handle LEFT JOINS.
* They both return an instance of the class DOMDocument provided by PHP5's
* A typical usage of XML_Query2XML looks like this:
* require_once 'XML/Query2XML.php';
* $query2xml = XML_Query2XML::factory(MDB2::factory($dsn));
* $dom = $query2xml->getXML($sql, $options);
* header('Content-Type: application/xml');
* Please read the <b>{@tutorial XML_Query2XML.pkg tutorial}</b> for
* detailed usage examples and more documentation.
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @copyright 2006 Lukas Feiler
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @version Release: 1.7.2
* @link http://pear.php.net/package/XML_Query2XML
* @var XML_Query2XML_Driver A subclass of XML_Query2XML_Driver.
* An instance of PEAR Log
* @var mixed An object that has a method with the signature log(String $msg);
* Whether debug logging is to be performed
* Whether profiling is to be performed
private $_profiling =
false;
* @var array A multi dimensional associative array
* @see _debugStartQuery()
* @see _stopDBProfiling()
private $_profile =
array();
* An associative array of global options.
* @var array An associative array
private $_globalOptions =
array(
'hidden_container_prefix' =>
'__'
* An associative array that will contain an element for each prefix.
* The prefix is used as the element key. Each array element consists
* of an indexed array containing a file path and a class name.
* @var array An associative multidimensional array.
* @see unregisterPrefix()
* @see unregisterAllPrefixes()
* @see _buildCommandChain()
private $_prefixes =
array(
'XML/Query2XML/Data/Condition/NonEmpty.php',
'XML_Query2XML_Data_Condition_NonEmpty'
'XML/Query2XML/Data/Processor/Unserialize.php',
'XML_Query2XML_Data_Processor_Unserialize'
'XML/Query2XML/Data/Processor/CDATA.php',
'XML_Query2XML_Data_Processor_CDATA'
'XML/Query2XML/Data/Processor/Base64.php',
'XML_Query2XML_Data_Processor_Base64'
'XML/Query2XML/Data/Source/Static.php',
'XML_Query2XML_Data_Source_Static'
'XML/Query2XML/Data/Source/PHPCallback.php',
'XML_Query2XML_Data_Source_PHPCallback'
'XML/Query2XML/Data/Source/XPath.php',
'XML_Query2XML_Data_Source_XPath'
* @param mixed $backend A subclass of XML_Query2XML_Driver or
* an instance of PEAR DB, PEAR MDB2, ADOdb,
* PDO, Net_LDAP2 or Net_LDAP.
private function __construct($backend)
$this->_driver =
$backend;
* As first argument pass an instance of PDO, PEAR DB, PEAR MDB2, ADOdb,
* Net_LDAP or an instance of any class that extends XML_Query2XML_Driver:
* require_once 'XML/Query2XML.php';
* $query2xml = XML_Query2XML::factory(
* new PDO('mysql://root@localhost/Query2XML_Tests')
* require_once 'XML/Query2XML.php';
* $query2xml = XML_Query2XML::factory(
* DB::connect('mysql://root@localhost/Query2XML_Tests')
* require_once 'XML/Query2XML.php';
* require_once 'MDB2.php';
* $query2xml = XML_Query2XML::factory(
* MDB2::factory('mysql://root@localhost/Query2XML_Tests')
* require_once 'XML/Query2XML.php';
* require_once 'adodb/adodb.inc.php';
* $adodb = ADONewConnection('mysql');
* $adodb->Connect('localhost', 'root', '', 'Query2XML_Tests');
* $query2xml = XML_Query2XML::factory($adodb);
* @param mixed $backend An instance of PEAR DB, PEAR MDB2, ADOdb, PDO,
* Net_LDAP or a subclass of XML_Query2XML_Driver.
* @return XML_Query2XML A new instance of XML_Query2XML
* @throws XML_Query2XML_DriverException If $backend already is a PEAR_Error.
* @throws XML_Query2XML_ConfigException If $backend is not an instance of a
* child class of DB_common, MDB2_Driver_Common, ADOConnection
* PDO, Net_LDAP or XML_Query2XML_Driver.
public static function factory($backend)
* Register a prefix that can be used in all value specifications.
* @param string $prefix The prefix name. This must be a single chracter.
* @param string $className The name of the Data Class. This class has
* to extend XML_Query2XML_Data.
* @param string $filePath The path to the file that contains the Command
* class. This argument is optional.
* @throws XML_Query2XML_ConfigException Thrown if $prefix is not a string
* or has a length other than 1.
'Prefix name has to be a single character'
$this->_prefixes[$prefix] =
array(
* @param string $prefix The prefix name.
unset
($this->_prefixes[$prefix]);
* Unregister all prefixes.
$this->_prefixes =
array();
* Currently the following global options are available:
* hidden_container_prefix: The prefix to use for container elements that are
* to be removed before the DOMDocument before it is returned by
* {@link XML_Query2XML::getXML()}. This has to be a non-empty string.
* The default value is '__'.
* @param string $option The name of the option
* @param mixed $value The option value
* @throws XML_Query2XML_ConfigException If the configuration option
* does not exist or if the value is invalid for that option
case 'hidden_container_prefix':
// unit test: setGlobalOption/hidden_container_prefix.phpt
$this->_globalOptions[$option] =
$value;
* unit test: setGlobalOption/
* configException_hidden_container_prefix_wrongTypeObject.phpt
* configException_hidden_container_prefix_wrongTypeEmptyStr.phpt
'The value for the hidden_container_prefix option '
.
'has to be a non-empty string'
// unit tests: setGlobalOption/configException_noSuchOption.phpt
'No such global option: ' .
$option
* Returns the current value for a global option.
* See {@link XML_Query2XML::setGlobalOption()} for a list
* @param string $option The name of the option
* @return mixed The option's value
* @throws XML_Query2XML_ConfigException If the option does not exist
if (!isset
($this->_globalOptions[$option])) {
// unit test: getGlobalOption/configException_noSuchOption.phpt
'No such global option: ' .
$option
// unit test: getGlobalOption/hidden_container_prefix.phpt
return $this->_globalOptions[$option];
* Enable the logging of debug messages.
* This will include all queries sent to the database.
* require_once 'Log.php';
* require_once 'XML/Query2XML.php';
* $query2xml = XML_Query2XML::factory(MDB2::connect($dsn));
* $debugLogger = Log::factory('file', 'out.log', 'XML_Query2XML');
* $query2xml->enableDebugLog($debugLogger);
* Please see {@link http://pear.php.net/package/Log} for details on PEAR Log.
* @param mixed $log Most likely an instance of PEAR Log but any object
* that provides a method named 'log' is accepted.
// unit test: enableDebugLog/enableDebugLog.phpt
$this->_debugLogger =
$log;
* Disable the logging of debug messages
// unit test: disableDebugLog/disableDebugLog.phpt
// unit tests: startProfile/startProfile.phpt
$this->_profiling =
true;
// unit test: stopProfile/stopProfile.phpt
$this->_profiling =
false;
if (isset
($this->_profile['start']) &&
$this->_profile['stop'] ==
0) {
$this->_profile['duration'] =
$this->_profile['stop'] -
$this->_profile['start'];
* Returns all raw profiling data.
* In 99.9% of all cases you will want to use getProfile().
// unit test: getRawProfile/getRawProfile.phpt
* Returns the profile as a single multi line string.
* @return string The profiling data.
// unit test: getProfile/getProfile.phpt
if (count($this->_profile) ===
0) {
$ret =
'COUNT AVG_DURATION DURATION_SUM SQL' .
"\n";
foreach ($this->_profile['queries'] as $sql =>
$value) {
$runTimes =
& $this->_profile['queries'][$sql]['runTimes'];
foreach ($runTimes as $runTime) {
$durationSum +=
($runTime['stop'] -
$runTime['start']);
if ($durationCount ==
0) {
// so that division does not fail
$durationAverage =
$durationSum /
$durationCount;
$ret .=
str_pad($this->_profile['queries'][$sql]['count'], 5)
.
substr($durationAverage, 0, 12).
' '
.
substr($durationSum, 0, 12).
' '
$ret .=
'TOTAL_DURATION: ' .
$this->_profile['duration'] .
"\n";
$ret .=
'DB_DURATION: ' .
$this->_profile['dbDuration'] .
"\n";
* Calls {@link XML_Query2XML::stopProfiling()} and then clears the profiling
* data by resetting a private property.
// unit test: clearProfile/clearProfile.phpt
$this->_profile =
array();
* Transforms the data retrieved by a single SQL query into flat XML data.
* This method will return a new instance of DOMDocument. The column names
* will be used as element names.
* require_once 'XML/Query2XML.php';
* $query2xml = XML_Query2XML::factory(MDB2::connect($dsn));
* $dom = $query2xml->getFlatXML(
* 'SELECT * FROM artist',
* @param string $sql The query string.
* @param string $rootTagName The name of the root tag; this argument is optional
* @param string $rowTagName The name of the tag used for each row; this
* argument is optional (default: 'row').
* @return DOMDocument A new instance of DOMDocument.
* @throws XML_Query2XML_Exception This is the base class for the exception
* types XML_Query2XML_DBException and
* XML_Query2XML_XMLException. By catching
* XML_Query2XML_Exception you can catch all
* exceptions this method will ever throw.
* @throws XML_Query2XML_DBException If a database error occurrs.
* @throws XML_Query2XML_XMLException If an XML error occurrs - most likely
* $rootTagName or $rowTagName is not a valid
public function getFlatXML($sql, $rootTagName =
'root', $rowTagName =
'row')
* unit tests: getFlatXML/*.phpt
$dom =
self::_createDOMDocument();
$rootTag =
self::_addNewDOMChild($dom, $rootTagName, 'getFlatXML');
$records =
$this->_getAllRecords(array('query' =>
$sql), 'getFlatXML', $sql);
foreach ($records as $record) {
$rowTag =
self::_addNewDOMChild($rootTag, $rowTagName, 'getFlatXML');
foreach ($record as $field =>
$value) {
self::_utf8encode($value)
* Transforms your SQL data retrieved by one or more queries into complex and
* highly configurable XML data.
* This method will return a new instance of DOMDocument.
* Please see the <b>{@tutorial XML_Query2XML.pkg tutorial}</b> for details.
* @param mixed $sql A string an array or the boolean value false.
* @param array $options Options for the creation of the XML data stored in an
* associative, potentially mutli-dimensional array
* (please see the tutorial).
* @return DOMDocument The XML data as a new instance of DOMDocument.
* @throws XML_Query2XML_Exception This is the base class for the exception types
* XML_Query2XML_DBException, XML_Query2XML_XMLException
* and XML_Query2XML_ConfigException. By catching
* XML_Query2XML_Exception you can catch all exceptions
* this method will ever throw.
* @throws XML_Query2XML_DBException If a database error occurrs.
* @throws XML_Query2XML_XMLException If an XML error occurrs - most likely
* an invalid XML element name.
* @throws XML_Query2XML_ConfigException If some configuration options passed
* as second argument are invalid or missing.
public function getXML($sql, $options)
* unit tests: getXML/*.phpt
// the default root tag name is 'root'
if (isset
($options['rootTag'])) {
$rootTagName =
$options['rootTag'];
$dom =
self::_createDOMDocument();
$rootTag =
self::_addNewDOMChild($dom, $rootTagName, '[rootTag]');
if ($options['sql'] ===
false) {
$this->_preprocessOptions($options);
/* Used to store the information which element has been created
* for which ID column value.
$records =
array(array()); // one empty record
$records =
$this->_applySqlOptionsToRecord(
foreach ($records as $key =>
$record) {
$tag =
$this->_getNestedXMLRecord($records[$key], $options, $dom, $tree);
/* _getNestedXMLRecord() returns false if an element already existed for
* the current ID column value.
$rootTag->appendChild($tag);
$this->_stopDBProfiling();
* Perform pre-processing on $options.
* This is a recursive method; it will call itself for every complex element
* specification and every complex attribute specification found.
* @param array &$options An associative array
* @param string $context Indecates whether an element or an attribute is
* @throws XML_Query2XML_ConfigException If a mandatory option is missing
* or any option is defined incorrectly.
private function _preprocessOptions(&$options, $context =
'elements')
if (!isset
($options['--q2x--path'])) {
// things to do only at the root level
$options['--q2x--path'] =
'';
if (!isset
($options['rowTag'])) {
$options['rowTag'] =
'row';
if (!isset
($options['idColumn'])) {
* unit test: _preprocessOptions/
* throwConfigException_idcolumnOptionMissing.phpt
'The configuration option "idColumn" '
.
'is missing on the root level.'
foreach (array('encoder', 'mapper') as $option) {
if (isset
($options[$option])) {
strpos($options[$option], '::') !==
false
$options[$option] =
explode('::', $options[$option]);
$options[$option] !==
false
!($option ==
'encoder' &&
$options[$option] ===
null)
!($option ==
'mapper' &&
$options[$option] ==
false)
* Only check whether $options['encoder'] is callable if it's not
* - false (don't use an encoder)
* - null (use self::_utf8encode()).
* unit test: _preprocessOptions/
* throwConfigException_encoderNotCallableStaticMethod1.phpt
* throwConfigException_encoderNotCallableStaticMethod2.phpt
* throwConfigException_encoderNotCallableNonstaticMethod.phpt
* throwConfigException_encoderNotCallableFunction.phpt
* Only check whether $options['mapper'] is callable if
* - $options['mapper'] != false
* unit tests: _preprocessOptions/
* throwConfigException_mapperNotCallableStaticMethod1.phpt
* throwConfigException_mapperNotCallableStaticMethod2.phpt
* throwConfigException_mapperNotCallableNonstaticMethod.phpt
* throwConfigException_mapperNotCallableFunction.phpt
$options['--q2x--path'] .
'[' .
$option .
']: The '
.
'method/function "' .
$callableName .
'" is not callable.'
$options[$option] =
null;
if ($context ==
'elements') {
foreach (array('elements', 'attributes') as $option) {
if (isset
($options[$option])) {
* unit test: _preprocessOptions/
* throwConfigException_attributesOptionWrongType.phpt
* throwConfigException_elementsOptionWrongType.phpt
$options['--q2x--path'] .
'[' .
$option .
']: '
.
'array expected, ' .
gettype($options[$option])
foreach ($options[$option] as $key =>
$columnStr) {
$configPath =
$options['--q2x--path'] .
'[' .
$option
$options[$option][$key] =
$this->_buildCommandChain($columnStr, $configPath);
* unit test: _preprocessOptions/
* throwConfigException_prefix_noArrayKey.phpt
$configPath .
': the element name has to be '
.
'specified as the array key when prefixes '
.
'are used within the value specification'
$options[$option][$key]['--q2x--path'] =
$configPath;
// encoder option used by elements as well as attributes
$options[$option][$key]['encoder'] =
if ($option ==
'elements') {
// these options are only used by elements
!isset
($options[$option][$key]['rootTag']) ||
$options[$option][$key]['rootTag'] ==
''
* If rootTag is not set or an empty string:
* create a hidden root tag
$options[$option][$key]['rootTag'] =
'hidden_container_prefix'
if (!isset
($options[$option][$key]['rowTag'])) {
$options[$option][$key]['rowTag'] =
$key;
foreach (array('mapper', 'idColumn') as $option2) {
$options[$option][$key][$option2] =
$this->_preprocessOptions(
} elseif (self::_isCallback($columnStr)) {
* unit test: _preprocessOptions/
* throwConfigException_callbackInterface_
$configPath .
': the element name has to be '
.
'specified as the array key when the value '
.
'is specified using an instance of '
.
'XML_Query2XML_Callback.'
* $columnStr is neither a string, an array or an
* instance of XML_Query2XML_Callback.
* throwConfigException_attributeSpecWrongType.phpt
* throwConfigException_callbackInterface_
* complexAttributeSpec.phpt
* simpleAttributeSpec.phpt
$configPath .
': array, string or instance of'
.
' XML_Query2XML_Callback expected, '
} // end of foreach (array('elements', 'attributes'))
// $context == 'attributes'
if (!isset
($options['value'])) {
* the option "value" is mandatory
* unit test: _preprocessOptions/
* throwConfigException_valueOptionMissing.phpt
$options['--q2x--path'] .
'[value]: Mandatory option "value" '
.
'missing from the complex attribute specification.'
$opt =
array('value', 'condition', 'dynamicRowTag', 'idColumn');
foreach ($opt as $option) {
if (isset
($options[$option])) {
$options[$option] =
$this->_buildCommandChain(
$options['--q2x--path'] .
'[value]'
!self::_isCallback($options[$option]) &&
!($option ==
'idColumn' &&
$options[$option] ===
false)
* throwConfigException_callbackInterface_
* complexElementSpec.phpt
$options['--q2x--path'] .
'[' .
$option .
']: string or'
.
' instance of XML_Query2XML_Callback expected, '
if (isset
($options['query'])) {
$options['sql'] =
$options['query'];
if (isset
($options['sql'])) {
// we will pre-process $options['sql_options'] first
if (isset
($options['query_options'])) {
$options['sql_options'] =
$options['query_options'];
if (!isset
($options['sql_options'])) {
$options['sql_options'] =
array();
'cached', 'single_record', 'merge', 'merge_master', 'merge_selective'
foreach ($sql_options as $option) {
if (!isset
($options['sql_options'][$option])) {
$options['sql_options'][$option] =
false;
if (isset
($options['sql_options']['uncached'])) {
$options['sql_options']['cached'] =
!$options['sql_options']['uncached'];
if ($options['sql_options']['cached']) {
$options['sql'] =
array('query' =>
$options['sql']);
if (isset
($options['sql']['driver'])) {
$driver =
$options['sql']['driver'];
$driver =
$this->_driver;
!($driver instanceof
XML_Query2XML_Driver_Caching)
include_once 'XML/Query2XML/Driver/Caching.php';
$options['sql']['driver'] =
new XML_Query2XML_Driver_Caching(
$options['sql_options']['merge_selective'] !==
false &&
!is_array($options['sql_options']['merge_selective'])
* unit test: _preprocessOptions/
* throwConfigException_mergeselectiveOptionWrongType.phpt
$options['--q2x--path'] .
'[sql_options][merge_selective]: '
.
gettype($options['sql_options']['merge_selective']) .
' given.'
// end of pre-processing of $options['sql_options']
isset
($options['sql']['driver']) &&
$query =
$options['sql']['driver']->preprocessQuery(
$options['--q2x--path'] .
'[sql]'
$options['--q2x--path'] .
'[sql]'
$options['--q2x--query_statement'] =
$query;
isset
($options['sql']['driver']) &&
* unit test: _preprocessOptions
* throwConfigException_sqlOptionWrongType.phpt
$options['--q2x--path'] .
'[sql][driver]: '
.
'instance of XML_Query2XML_Driver expected, '
.
gettype($options['sql']['driver']) .
' given.'
if (isset
($options['sql']['data'])) {
if (is_array($options['sql']['data'])) {
foreach ($options['sql']['data'] as $key =>
$data) {
$options['sql']['data'][$key] =
$this->_buildCommandChain(
$options['sql']['data'][$key],
.
'[sql][data][' .
$key .
']'
} elseif (!self::_isCallback($data)) {
* unit tests: _preprocessOptions/
* throwConfigException_callbackInterface_data.phpt
$options['--q2x--path'] .
'[sql][data][' .
$key
.
' instance of XML_Query2XML_Callback expected,'
.
' ' .
gettype($options['sql']['data'][$key])
* unit test: _preprocessOptions/
* throwConfigException_dataOptionWrongType.phpt
$options['--q2x--path'] .
'[sql][data]: array expected, '
.
gettype($options['sql']['data']) .
' given.'
} // end of if (isset($options['sql'])
* Private recursive method that creates the nested XML elements from a record.
* getXML calls this method for every row in the initial result set.
* The $tree argument deserves some more explanation. All DOMNodes are stored
* in $tree the way they appear in the XML document. The same hirachy needs to be
* built so that we can know if a DOMNode that corresponds to a column ID of 2 is
* already a child node of a certain XML element. Let's have a look at an example
* would be represended in the $tree array as something like this:
* [tag] => DOMElement Object
* [tag] => DOMElement Object
* [tag] => DOMElement Object
* [tag] => DOMElement Object
* The numbers in the square brackets are column ID values.
* @param array $record An associative array representing a record;
* column names must be used as keys.
* @param array &$options An array containing the options for this nested
* element; this will be a subset of the array
* originally passed to getXML().
* @param DOMDocument $dom An instance of DOMDocument.
* @param array &$tree An associative multi-dimensional array, that is
* used to store the information which tag has
* already been created for a certain ID column
* 'elements' => Array( ... )
* @return mixed The XML element's representation as a new instance of
* DOMNode or the boolean value false (meaning no
* @throws XML_Query2XML_DBException Bubbles up through this method if thrown by
* - _processComplexElementSpecification()
* @throws XML_Query2XML_XMLException Bubbles up through this method if thrown by
* - _appendTextChildNode()
* - _processComplexElementSpecification()
* @throws XML_Query2XML_ConfigException Thrown if
* - $options['idColumn'] is not set
* - $options['elements'] is set but not an array
* - $options['attributes'] is set but not an array
* Bubbles up through this method if thrown by
* - _applyColumnStringToRecord()
* - _processComplexElementSpecification()
* @throws XML_Query2XML_Exception Bubbles up through this method if thrown by
* - _applyColumnStringToRecord()
private function _getNestedXMLRecord($record, &$options, $dom, &$tree)
// the default row tag name is 'row'
if (isset
($options['dynamicRowTag'])) {
$rowTagName =
$this->_applyColumnStringToRecord(
$options['dynamicRowTag'],
$options['--q2x--path'] .
'[dynamicRowTag]'
$rowTagName =
$options['rowTag'];
if ($options['idColumn'] ===
false) {
static $uniqueIdCounter =
0;
$id = ++
$uniqueIdCounter;
$id =
$this->_applyColumnStringToRecord(
$options['--q2x--path'] .
'[idColumn]'
* unit test: _getNestedXMLRecord/
* throwConfigException_idcolumnOptionWrongTypeArray.phpt
* throwConfigException_idcolumnOptionWrongTypeObject.phpt
$options['--q2x--path'] .
'[idColumn]: Must evaluate to a '
.
'value that is not an object or an array.'
/* Is there already an identical tag (identity being determined by the
* value of the ID-column)?
if (isset
($options['elements'])) {
foreach ($options['elements'] as $tagName =>
$column) {
$this->_processComplexElementSpecification(
$options['elements'][$tagName],
* We return false because $tree[$id]['tag'] is already
* a child of the parent element.
if (isset
($options['value'])) {
$parsedValue =
$this->_applyColumnStringToRecord(
$options['--q2x--path'] .
'[value]'
if (!$this->_evaluateCondition($parsedValue, $options['value'])) {
// this element is to be skipped
if (isset
($options['condition'])) {
$continue =
$this->_applyColumnStringToRecord(
$options['--q2x--path'] .
'[condition]'
// this element is to be skipped
$tree[$id]['tag'] =
self::_createDOMElement(
$options['--q2x--path'] .
'[rowTag/dynamicRowTag]'
$tag =
$tree[$id]['tag'];
if (isset
($options['attributes'])) {
if (!isset
($options['processed'])) {
$options['attributes'] =
self::_expandShortcuts(
$options['--q2x--path'] .
'[attributes]'
foreach ($options['attributes'] as $attributeName =>
$column) {
// complex attribute specification
$this->_processComplexAttributeSpecification(
$attributeName, $record, $column, $tree[$id]['tag']
// simple attribute specifications
$attributeValue =
$this->_applyColumnStringToRecord(
.
'[attributes][' .
$attributeName .
']'
if ($this->_evaluateCondition($attributeValue, $column)) {
.
'[attributes][' .
$attributeName .
']'
if (isset
($options['value'])) {
if ($parsedValue instanceof
DOMNode ||
is_array($parsedValue)) {
* The value returned from _applyColumnStringToRecord() and
* stored in $parsedValue is an instance of DOMNode or an
* array of DOMNode instances. _addDOMChildren() will handle
$options['--q2x--path'] .
'[value]',
if ($parsedValue !==
false &&
!is_null($parsedValue)) {
self::_appendTextChildNode(
$options['--q2x--path'] .
'[value]'
if (isset
($options['elements'])) {
if (!isset
($options['processed'])) {
$options['elements'] =
self::_expandShortcuts(
$options['--q2x--path'] .
'[elements]'
foreach ($options['elements'] as $tagName =>
$column) {
// complex element specification
$this->_processComplexElementSpecification(
$options['elements'][$tagName],
// simple element specification
$tagValue =
$this->_applyColumnStringToRecord(
$options['--q2x--path'] .
'[elements][' .
$tagName .
']'
if ($this->_evaluateCondition($tagValue, $column)) {
$tagValue instanceof
DOMNode ||
* The value returned from
* _applyColumnStringToRecord() and stored in
* $tagValue is an instance of DOMNode or an array
* of DOMNode instances. self::_addDOMChildren()
.
'[elements][' .
$tagName .
']'
.
'[elements][' .
$tagName .
']',
.
'[elements][' .
$tagName .
']',
// some things only need to be done once
$options['processed'] =
true;
* We return $tree[$id]['tag'] because it needs to be added to it's
* parent; this is to be handled by the method that called
return $tree[$id]['tag'];
* Private method that will expand asterisk characters in an array
* of simple element specifications.
* This method gets called to handle arrays specified using the 'elements'
* or the 'attributes' option. An element specification that contains an
* asterisk will be duplicated for each column present in $record.
* Please see the {@tutorial XML_Query2XML.pkg tutorial} for details.
* @param Array &$elements An array of simple element specifications.
* @param Array &$record An associative array that represents a single
* @param mixed $mapper A valid argument for call_user_func(), a full method
* method name (e.g. "MyMapperClass::map") or a value
* that == false for no special mapping at all.
* @param string $configPath The config path; used for exception messages.
* @return Array The extended array.
* @throws XML_Query2XML_ConfigException If only the column part but not the
* explicitly defined tagName part contains an asterisk.
* @throws XML_Query2XML_Exception Will bubble up if it is thrown by
* _mapSQLIdentifierToXMLName(). This should never
* happen as _getNestedXMLRecord() already checks if
* @throws XML_Query2XML_XMLException Will bubble up if it is thrown by
* _mapSQLIdentifierToXMLName() which will happen if the
* $mapper function called, throws any exception.
private function _expandShortcuts(&$elements, &$record, $mapper, $configPath)
foreach ($elements as $tagName =>
$column) {
// expand all occurences of '*' to all column names
foreach ($record as $columnName =>
$value) {
$column instanceof
XML_Query2XML_Data
$newColumn =
clone $column;
$callback =
$newColumn->getFirstPreProcessor();
$callback->replaceAsterisks($columnName);
$newTagName =
self::_mapSQLIdentifierToXMLName(
$configPath .
'[' .
$tagName .
']'
if (!isset
($newElements[$newTagName])) {
// only if the tagName hasn't already been used
$newElements[$newTagName] =
$newColumn;
* Complex element specifications will always be dealt with here.
* We don't want any mapping or handling of the asterisk shortcut
* to be done for complex element specifications.
// do the mapping but not for complex element specifications
$tagName =
self::_mapSQLIdentifierToXMLName(
$configPath .
'[' .
$tagName .
']'
* explicit specification without an asterisk;
* this always overrules an expanded asterisk
unset
($newElements[$tagName]);
$newElements[$tagName] =
$column;
* Maps an SQL identifier to an XML name using the supplied $mapper.
* @param string $sqlIdentifier The SQL identifier as a string.
* @param mixed $mapper A valid argument for call_user_func(), a full
* method method name (e.g. "MyMapperClass::map")
* or a value that == false for no special mapping
* @param string $configPath The config path; used for exception messages.
* @return string The mapped XML name.
* @throws XML_Query2XML_Exception If $mapper is not callable. This should never
* happen as _getNestedXMLRecord() already checks
* if $mapper is callable.
* @throws XML_Query2XML_XMLException If the $mapper function called, throws any
private function _mapSQLIdentifierToXMLName($sqlIdentifier, $mapper, $configPath)
$xmlName =
$sqlIdentifier;
* This will also catch XML_Query2XML_ISO9075Mapper_Exception
* if $mapper was "XML_Query2XML_ISO9075Mapper::map".
* _mapSQLIdentifierToXMLName/throwXMLException.phpt
$configPath .
': Could not map "' .
$sqlIdentifier
.
'" to an XML name using the mapper '
.
$callableName .
': ' .
$e->getMessage()
* This should never happen as _preprocessOptions() already
* checks if $mapper is callable. Therefore no unit tests
* can be provided for this exception.
$configPath .
': The mapper "' .
$callableName
* Private method that processes a complex element specification
* for {@link XML_Query2XML::_getNestedXMLRecord()}.
* @param array &$record The current record.
* @param array &$options The current options.
* @param array &$tree Associative multi-dimensional array, that is used to
* store which tags have already been created
* @param string $tagName The element's name.
* @throws XML_Query2XML_XMLException This exception will bubble up
* if it is thrown by _getNestedXMLRecord(),
* _applySqlOptionsToRecord() or _addDOMChildren().
* @throws XML_Query2XML_DBException This exception will bubble up
* if it is thrown by _applySqlOptionsToRecord()
* or _getNestedXMLRecord().
* @throws XML_Query2XML_ConfigException This exception will bubble up
* if it is thrown by _applySqlOptionsToRecord()
* or _getNestedXMLRecord().
* @throws XML_Query2XML_Exception This exception will bubble up if it
* is thrown by _getNestedXMLRecord().
private function _processComplexElementSpecification(&$record, &$options, &$tree,
if (!isset
($tree['elements'])) {
$tree['elements'] =
array();
if (!isset
($tree['elements'][$tagName])) {
$tree['elements'][$tagName] =
array();
$tree['elements'][$tagName]['rootTag'] =
self::_addNewDOMChild(
$options['--q2x--path'] .
'[rootTag]'
$records =
& $this->_applySqlOptionsToRecord($options, $record);
for ($i =
0; $i <
count($records); $i++
) {
$tree['elements'][$tagName]['rootTag'],
$this->_getNestedXMLRecord(
$tree['elements'][$tagName]
* Private method that processes a complex attribute specification
* for {@link XML_Query2XML::_getNestedXMLRecord()}.
* A complex attribute specification consists of an associative array
* with the keys 'value' (mandatory), 'condition', 'sql' and 'sql_options'.
* @param string $attributeName The name of the attribute as it was specified
* using the array key of the complex attribute
* @param array &$record The current record.
* @param array &$options The complex attribute specification itself.
* @param DOMNode $tag The DOMNode to which the attribute is to be
* @throws XML_Query2XML_XMLException This exception will bubble up
* if it is thrown by _setDOMAttribute(),
* _applyColumnStringToRecord(),
* _applySqlOptionsToRecord() or _executeEncoder().
* @throws XML_Query2XML_DBException This exception will bubble up
* if it is thrown by _applySqlOptionsToRecord().
* @throws XML_Query2XML_ConfigException This exception will bubble up
* if it is thrown by _applySqlOptionsToRecord() or
* _applyColumnStringToRecord(). It will also be thrown
* by this method if $options['value'] is not set.
private function _processComplexAttributeSpecification($attributeName, &$record,
if (isset
($options['condition'])) {
$continue =
$this->_applyColumnStringToRecord(
$options['--q2x--path'] .
'[condition]'
// this element is to be skipped
// only fetching a single record makes sense for a single attribute
$options['sql_options']['single_record'] =
true;
$records =
$this->_applySqlOptionsToRecord($options, $record);
if (count($records) ==
0) {
* $options['sql'] was set but the query did not return any records.
* Therefore this attribute is to be skipped.
$attributeRecord =
$records[0];
$attributeValue =
$this->_applyColumnStringToRecord(
$options['--q2x--path'] .
'[value]'
if ($this->_evaluateCondition($attributeValue, $options['value'])) {
self::_executeEncoder($attributeValue, $options),
$options['--q2x--path'] .
'[value]'
* Private method to apply the givenen sql option to a record.
* This method handles the sql options 'single_record',
* 'merge', 'merge_master' and 'merge_selective'. Please see the
* {@tutorial XML_Query2XML.pkg tutorial} for details.
* @param array &$options An associative multidimensional array of options.
* @param array &$record The current record as an associative array.
* @return array An indexed array of records that are themselves
* represented as associative arrays.
* @throws XML_Query2XML_ConfigException This exception is thrown if
* - a column specified in merge_selective does not exist
* - it bubbles up from _applyColumnStringToRecord()
* @throws XML_Query2XML_DBException This exception will bubble up
* if it is thrown by _getAllRecords().
* @throws XML_Query2XML_XMLException It will bubble up if it is thrown
* by _applyColumnStringToRecord().
private function _applySqlOptionsToRecord(&$options, &$record)
if (!isset
($options['sql'])) {
$single_record =
$options['sql_options']['single_record'];
$merge =
$options['sql_options']['merge'];
$merge_master =
$options['sql_options']['merge_master'];
$merge_selective =
$options['sql_options']['merge_selective'];
if (isset
($sql['data'])) {
foreach ($sql['data'] as $key =>
$columnStr) {
$sql['data'][$key] =
$this->_applyColumnStringToRecord(
$options['--q2x--path'] .
'[sql][data][' .
$key .
']'
$sqlConfigPath =
$options['--q2x--path'] .
'[sql]';
$records =
& $this->_getAllRecords(
$options['--q2x--query_statement']
if ($single_record && isset
($records[0])) {
$records =
array($records[0]);
// current records are master
for ($ii =
0; $ii <
count($merge_selective); $ii++
) {
for ($i =
0; $i <
count($records); $i++
) {
/* Selected field does not exist in the parent record
* (passed as argumnet $record)
* unit test: _applySqlOptionsToRecord/
* throwConfigException_mergeMasterTrue.phpt
$options['--q2x--path'] .
'[sql_options]'
.
'[merge_selective]['.
$ii .
']: The column "'
.
$merge_selective[$ii] .
'" '
.
'was not found in the result set.'
// we are the master, so only if it does not yet exist
$records[$i][$merge_selective[$ii]] =
$record[$merge_selective[$ii]];
// parent record is master
for ($ii =
0; $ii <
count($merge_selective); $ii++
) {
for ($i =
0; $i <
count($records); $i++
) {
/* Selected field does not exist in the parent record
* (passed as argumnet $record)
* unit test: _applySqlOptionsToRecord/
* throwConfigException_mergeMasterFalse.phpt
$options['--q2x--path'] .
'[sql_options]'
.
'[merge_selective]['.
$ii .
']: The column "'
.
$merge_selective[$ii] .
'" '
.
'was not found in the result set.'
$records[$i][$merge_selective[$ii]] =
$record[$merge_selective[$ii]];
for ($i =
0; $i <
count($records); $i++
) {
for ($i =
0; $i <
count($records); $i++
) {
* Private method to apply a column string to a record.
* Please see the tutorial for details on the different column strings.
* @param string $columnStr A valid column name or an instance of a class
* implementing XML_Query2XML_Callback.
* @param array &$record The record as an associative array.
* @param string $configPath The config path; used for exception messages.
* @return mixed A value that can be cast to a string or an instance of DOMNode.
* @throws XML_Query2XML_ConfigException Thrown if $columnStr is not
* a string or an instance of XML_Query2XML_Callback or if
* $record[$columnStr] does not exist (and $columnStr has
* @throws XML_Query2XML_XMLException Thrown if the '&' prefix was used
* but the data was not unserializeable, i.e. not valid XML data.
private function _applyColumnStringToRecord($columnStr, &$record, $configPath)
if (self::_isCallback($columnStr)) {
$value =
$columnStr->execute($record);
$value =
$record[$columnStr];
* _applyColumnStringToRecord/throwConfigException_element1.phpt
* _applyColumnStringToRecord/throwConfigException_element2.phpt
* _applyColumnStringToRecord/throwConfigException_idcolumn.phpt
$configPath .
': The column "' .
$columnStr
.
'" was not found in the result set.'
// should never be reached
$configPath .
': string or instance of XML_Query2XML_Callback'
.
' expected, ' .
gettype($columnStr) .
' given.'
* Returns whether $value is to be included in the output.
* If $spec is a string an is prefixed by a question mark this method will
* return false if $value is null or is a string with a length of zero. In
* any other case, this method will return the true.
* @param string $value The value.
* @param mixed $spec The value specification. This can be a string
* or an instance of XML_Query2XML_Callback.
* @return boolean Whether $value is to be included in the output.
private function _evaluateCondition($value, $spec)
$spec->evaluateCondition($value);
* Private method to fetch all records from a result set.
* @param mixed $sql The SQL query as a string or an array.
* @param string $configPath The config path; used for exception messages.
* @param string $queryStatement The query as a string; it will be used for
* @return array An array of records. Each record itself will be an
private function &_getAllRecords($sql, $configPath, $queryStatement)
// $queryStatement will be used for profiling
if ($this->_profiling ||
$this->_debug) {
$loggingQuery =
$queryStatement;
$loggingQuery .=
'; DATA:' .
implode(',', $sql['data']);
$this->_debugStartQuery($loggingQuery, $queryStatement);
if (is_array($sql) && isset
($sql['driver'])) {
$driver =
$sql['driver'];
$driver =
$this->_driver;
$records =
$driver->getAllRecords($sql, $configPath);
$this->_debugStopQuery($queryStatement);
* Initializes a query's profile (only used if profiling is turned on).
* @param mixed &$sql The SQL query as a string or an array.
private function _initQueryProfile(&$sql)
if (!isset
($this->_profile['queries'][$sql])) {
$this->_profile['queries'][$sql] =
array(
* Starts the debugging and profiling of the query passed as argument.
* @param string $loggingQuery The query statement as it will be logged.
* @param string $profilingQuery The query statement as it will be used for
private function _debugStartQuery($loggingQuery, $profilingQuery)
$this->_debug('QUERY: ' .
$loggingQuery);
$this->_initQueryProfile($profilingQuery);
++
$this->_profile['queries'][$profilingQuery]['count'];
$this->_profile['queries'][$profilingQuery]['runTimes'][] =
array(
* Ends the debugging and profiling of the query passed as argument.
* @param string $profilingQuery The query statement as it will be used for
private function _debugStopQuery($profilingQuery)
$this->_initQueryProfile($profilingQuery);
$this->_profile['queries'][$profilingQuery]['runTimes']
$this->_profile['queries'][$profilingQuery]['runTimes'][$lastIndex]['stop'] =
* Stops the DB profiling.
* This will set $this->_profile['dbDuration'].
private function _stopDBProfiling()
if ($this->_profiling && isset
($this->_profile['start'])) {
$this->_profile['dbDuration'] =
$this->_profile['dbStop'] -
$this->_profile['start'];
* Private method used to log debug messages.
* This method will do no logging if $this->_debug is set to false.
* @param string $msg The message to log.
private function _debug($msg)
$this->_debugLogger->log($msg);
* Returns whether $object is an instance of XML_Query2XML_Callback.
* @param mixed $object The variable to check.
private static function _isCallback($object)
* Parse specifications that use the prifixes ?, &, =, ^, :, or #.
* This method will produce a number of chained Data Class objects all of
* which be an instance of the abstract class XML_Query2XML_Data.
* @param string $columnStr The original specification.
* @param string $configPath The config path; used for exception messages.
* @return mixed An instance of XML_Query2XML_Callback or a column
* @throws XML_Query2XML_ConfigException Bubbles up through this method if
* thrown by any of the command class
private function _buildCommandChain($columnStr, $configPath)
if (ltrim($columnStr, $prefixList) ==
$columnStr) {
for ($i =
0; $i <
strlen($columnStr); $i++
) {
$prefix =
substr($columnStr, $i, 1);
if (isset
($this->_prefixes[$prefix])) {
$columnSubStr =
substr($columnStr, $i +
1);
$filePath =
$this->_prefixes[$prefix][0];
$className =
$this->_prefixes[$prefix][1];
if ($columnSubStr ===
false) {
$configPath .
': Prefix class ' .
$className .
' does ' .
'not extend XML_Query2XML_Data.'
'XML_Query2XML_Data_Source',
array($className, 'create'),
array($columnSubStr, $configPath)
// data processing prefix
array($className, 'create'),
if (ltrim($columnSubStr, $prefixList) ==
$columnSubStr) {
// no more prefixes: ColumnValue is the default data source
include_once 'XML/Query2XML/Data/Source/ColumnValue.php';
$callback->setPreProcessor(
new XML_Query2XML_Data_Source_ColumnValue(
$firstCallback =
$callback;
$configPath .
': conditional prefixes always have to '
$firstCallback->getFirstPreProcessor()->setPreProcessor(
$firstCallback->getFirstPreProcessor()
// there can only be one data source
* Creates a new instance of DOMDocument.
* '1.0' is passed as first argument and 'UTF-8' as second to the
* DOMDocument constructor.
* @return DOMDocument The new instance.
private static function _createDOMDocument()
return new DOMDocument('1.0', 'UTF-8');
* Create and then add a new child element.
* @param DOMNode $element The parent DOMNode the new DOM element should be
* @param string $name The tag name of the new element.
* @param string $configPath The config path; used for exception messages.
* @param string $value The value of a child text node. This argument is
* optional. The default is the boolean value false,
* which means that no child text node will be
* @return DOMNode The newly created DOMNode instance that was appended
* @throws XML_Query2XML_XMLException This exception will bubble up if it is
* thrown by _createDOMElement().
private static function _addNewDOMChild(DOMNode $element, $name, $configPath,
if ($element instanceof
DOMDocument) {
$dom =
$element->ownerDocument;
$child =
self::_createDOMElement($dom, $name, $configPath, $value);
$element->appendChild($child);
* Helper method to create a new instance of DOMNode
* @param DOMDocument $dom An instance of DOMDocument. It's
* createElement() method is used to create the
* @param string $name The tag name of the new element.
* @param string $configPath The config path; used for exception messages.
* @param string $value The value of a child text node. This argument
* is optional. The default is the boolean value
* false, which means that no child text node will
* @return DOMNode An instance of DOMNode.
* @throws XML_Query2XML_XMLException If $name is an invalid XML identifier.
* Also it will bubble up if it is thrown by
* _appendTextChildNode().
private static function _createDOMElement(DOMDocument $dom, $name, $configPath,
$element =
$dom->createElement($name);
} catch
(DOMException $e) {
* _createDOMElement/throwXMLException_elementInvalid1.phpt
* _createDOMElement/throwXMLException_elementInvalid2.phpt
* _createDOMElement/throwXMLException_roottagOptionInvalid1.phpt
* _createDOMElement/throwXMLException_roottagOptionInvalid2.phpt
* _createDOMElement/throwXMLException_rowtagOptionInvalid.phpt
$configPath .
': "' .
$name .
'" is an invalid XML element name: '
self::_appendTextChildNode($element, $value, $configPath);
* Append a new child text node to $element.
* $value must already be UTF8-encoded; this is to be handled
* by self::_executeEncoder() and $options['encoder'].
* This method will not create and append a child text node
* if $value === false || is_null($value).
* @param DOMNode $element An instance of DOMNode
* @param string $value The value of the text node.
* @param string $configPath The config path; used for exception messages.
* @throws XML_Query2XML_XMLException Any lower-level DOMException will
* wrapped and re-thrown as a XML_Query2XML_XMLException. This
* will happen if $value cannot be UTF8-encoded for some reason.
* It will also be thrown if $value is an object or an array
* (and can therefore not be converted into a string).
private static function _appendTextChildNode(DOMNode $element,
if ($value ===
false ||
is_null($value)) {
* Objects and arrays cannot be cast
* to a string without an error.
* _appendTextChildNode/throwXMLException.phpt
$configPath .
': A value of the type ' .
gettype($value)
.
' cannot be used for a text node.'
$dom =
$element->ownerDocument;
$element->appendChild($dom->createTextNode($value));
} catch
(DOMException $e) {
// this should never happen as $value is UTF-8 encoded
$configPath .
': "' .
$value .
'" is not a vaild text node: '
* Set the attribute $name with a value of $value for $element.
* $value must already be UTF8-encoded; this is to be handled
* by self::_executeEncoder() and $options['encoder'].
* @param DOMNode $element An instance of DOMNode
* @param string $name The name of the attribute to set.
* @param string $value The value of the attribute to set.
* @param string $configPath The config path; used for exception messages.
* @throws XML_Query2XML_XMLException Any lower-level DOMException will be
* wrapped and re-thrown as a XML_Query2XML_XMLException. This
* will happen if $name is not a valid attribute name. It will
* also be thrown if $value is an object or an array (and can
* therefore not be converted into a string).
private static function _setDOMAttribute(DOMNode $element,
* Objects and arrays cannot be cast
* to a string without an error.
* _setDOMAttribute/throwXMLException.phpt
$configPath .
': A value of the type ' .
gettype($value)
.
' cannot be used for an attribute value.'
$element->setAttribute($name, $value);
} catch
(DOMException $e) {
// no unit test available for this one
$configPath .
': "' .
$name .
'" is an invalid XML attribute name: '
* Adds one or more child nodes to an existing DOMNode instance.
* @param DOMNode $base An instance of DOMNode.
* @param mixed $children An array of DOMNode instances or
* just a single DOMNode instance.
* Boolean values of false are always ignored.
* @param string $configPath The config path; used for exception messages.
* @param boolean $import Whether DOMDocument::importNode() should be called
* for $children. This is necessary if the instance(s)
* passed as $children was/were created using a
* different DOMDocument instance. This argument is
* optional. The default is false.
* @throws XML_Query2XML_XMLException If one of the specified children
* is not one of the following: an instance of DOMNode,
* the boolean value false, or an array containing
private static function _addDOMChildren(DOMNode $base,
if ($children ===
false) {
} elseif ($children instanceof
DOMNode) {
// $children is a single complex child
$children =
$base->ownerDocument->importNode($children, true);
$base->appendChild($children);
for ($i =
0; $i <
count($children); $i++
) {
if ($children[$i] ===
false) {
} elseif ($children[$i] instanceof
DOMNode) {
$children[$i] =
$base->ownerDocument->importNode(
$base->appendChild($children[$i]);
* _addDOMChildren/throwXMLException_arrayWithObject.phpt
* _addDOMChildren/throwXMLException_arrayWithString.phpt
* _addDOMChildren/throwXMLException_arrayWithInt.phpt
* _addDOMChildren/throwXMLException_arrayWithBool.phpt
* _addDOMChildren/throwXMLException_arrayWithDouble.phpt
$configPath .
': DOMNode, false or an array of the two '
.
'expected, but ' .
gettype($children[$i]) .
' given '
.
'(hint: check your callback).'
* This should never happen because _addDOMChildren() is only called
* for arrays and instances of DOMNode.
$configPath .
': DOMNode, false or an array of the two '
.
'expected, but ' .
gettype($children) .
' given '
.
'(hint: check your callback).'
* Remove all container elements created by XML_Query2XML to ensure that all
* elements are correctly ordered.
* This is a recursive method. This method calls
* {@link XML_Query2XML::_replaceParentWithChildren()}. For the concept of
* container elements please see the {@tutorial XML_Query2XML.pkg tutorial}.
* @param DOMNode $element An instance of DOMNode.
* @param string $hiddenContainerPrefix The containers that will be removed
* all start with this string.
private static function _removeContainers($element, $hiddenContainerPrefix)
$xpath =
new DOMXPath($element);
$containers =
$xpath->query(
'//*[starts-with(name(),\'' .
$hiddenContainerPrefix .
'\')]'
foreach ($containers as $container) {
if (!is_null($container->parentNode)) {
self::_replaceParentWithChildren($container);
* Replace a certain node with its child nodes.
* @param DOMNode $parent An instance of DOMNode.
private static function _replaceParentWithChildren(DOMNode $parent)
$child =
$parent->firstChild;
$nextChild =
$child->nextSibling;
$parent->removeChild($child);
$parent->parentNode->insertBefore($child, $parent);
$parent->parentNode->removeChild($parent);
* Calls an encoder for XML node and attribute values
* $options['encoder'] can be one of the following:
* - null: self::_utf8encode() will be used
* - false: no encoding will be performed
* - callback: a string or an array as defined by the
* callback pseudo-type; please see
* http://www.php.net/manual/en/
* language.pseudo-types.php#language.types.callback
* @param string $str The string to encode
* @param array $options An associative array with $options['encoder'] set.
* @throws XML_Query2XML_XMLException If the $options['encoder'] is a callback
* function that threw an exception.
private static function _executeEncoder($str, $options)
if (!is_string($str) ||
$options['encoder'] ===
false) {
if ($options['encoder'] ===
null) {
return self::_utf8encode($str);
* _executeEncoder/throwXMLException.phpt
$options['--q2x--path'] .
'[encoder]: Could not encode '
.
'"' .
$str .
'": ' .
$e->getMessage()
* UTF-8 encode $str using mb_conver_encoding or if that is not
* @param string $str The string to encode
* @return String The UTF-8 encoded version of $str
private static function _utf8encode($str)
$str =
mb_convert_encoding($str, 'UTF-8');
* Parent class for ALL exceptions thrown by this package.
* By catching XML_Query2XML_Exception you will catch all exceptions
* thrown by XML_Query2XML.
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @link http://pear.php.net/package/XML_Query2XML
* @param string $message The error message.
* @param Exception $exception The Exception that caused this exception
* to be thrown. This argument is optional.
public function __construct($message, $exception =
null)
parent::__construct($message, $exception);
* Exception for driver errors
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @link http://pear.php.net/package/XML_Query2XML
* @since Release 1.6.0RC1
* @param string $message The error message.
* Exception for database errors
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @link http://pear.php.net/package/XML_Query2XML
* @param string $message The error message.
* Exception for XML errors
* In most cases this exception will be thrown if a DOMException occurs.
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @link http://pear.php.net/package/XML_Query2XML
* @param string $message The error message.
* @param DOMException $exception The DOMException that caused this exception
* to be thrown. This argument is optional.
public function __construct($message, DOMException $exception =
null)
* Exception that handles configuration errors.
* This exception handels errors in the $options array passed to
* XML_Query2XML::getXML() and wrong arguments passed to the constructor via
* XML_Query2XML::factory().
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @link http://pear.php.net/package/XML_Query2XML
* @see XML_Query2XML::getXML()
* @param string $message A detailed error message.
* $driver = XML_Query2XML_Driver::factory($backend);
* @author Lukas Feiler <lukas.feiler@lukasfeiler.com>
* @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1
* @version Release: 1.7.2
* @link http://pear.php.net/package/XML_Query2XML
* @since Release 1.5.0RC1
* This method, when implemented executes the query passed as the
* first argument and returns all records from the result set.
* The format of the first argument depends on the driver being used.
* @param mixed $sql The SQL query as a string or an array.
* @param string $configPath The config path; used for exception messages.
* @return array An array of records. Each record itself will be an
* @throws XML_Query2XML_DriverException If some driver related error occures.
* Pre-processes a query specification and returns a string representation
* The returned string will be used for logging purposes. It
* does not need to be valid SQL.
* If $query is a string, it will be changed to array('query' => $query).
* @param mixed &$query A string or an array containing the element 'query'.
* @param string $configPath The config path; used for exception messages.
* @return string The query statement as a string.
* @throws XML_Query2XML_ConfigException If $query is an array but does not
* contain the element 'query'.
$query =
array('query' =>
$query);
if (!isset
($query['query'])) {
* unit test: _preprocessOptions/
* throwConfigException_queryOptionMissing.phpt
$configPath .
': The configuration option'
} else { //neither a string nor an array
* unit test: _preprocessOptions/
* throwConfigException_sqlOptionWrongType.phpt
$configPath .
': array or string expected, '
* @param mixed $backend An instance of MDB2_Driver_Common, PDO, DB_common,
* ADOConnection, Net_LDAP2 or Net_LDAP.
* @return XML_Query2XML_Driver An instance of a driver class that
* extends XML_Query2XML_Driver.
* @throws XML_Query2XML_DriverException If $backend already is a PEAR_Error.
* @throws XML_Query2XML_ConfigException If $backend is not an instance of a
* child class of MDB2_Driver_Common, PDO, DB_common,
* ADOConnection, Net_LDAP2 or Net_LDAP.
public static function factory($backend)
$backend instanceof
MDB2_Driver_Common
include_once 'XML/Query2XML/Driver/MDB2.php';
include_once 'XML/Query2XML/Driver/PDO.php';
} elseif (class_exists('DB_common') &&
$backend instanceof
DB_common) {
include_once 'XML/Query2XML/Driver/DB.php';
$backend instanceof
ADOConnection
include_once 'XML/Query2XML/Driver/ADOdb.php';
} elseif (class_exists('Net_LDAP') &&
$backend instanceof
Net_LDAP) {
include_once 'XML/Query2XML/Driver/LDAP.php';
} elseif (class_exists('Net_LDAP2') &&
$backend instanceof
Net_LDAP2) {
include_once 'XML/Query2XML/Driver/LDAP2.php';
} elseif (class_exists('PEAR_Error') &&
$backend instanceof
PEAR_Error) {
//unit tests: NoDBLayer/factory/throwDBException.phpt
'Driver error: ' .
$backend->toString()
//unit test: NoDBLayer/factory/throwConfigException.phpt
'Argument passed to the XML_Query2XML constructor is not an '
.
'instance of DB_common, MDB2_Driver_Common, ADOConnection'
.
', PDO, Net_LDAP or Net_LDAP2.'
Documentation generated on Sun, 03 Apr 2011 13:13:11 +0200 by phpDocumentor 1.4.1