/** * Executes a data source. Invalid XML is escaped (CDATA) but still * cached. Prevents persistent cached XML from breaking pages. * * @param Datasource $datasource * The current data source object * @param int $file_age * Cache file age (in seconds), passed by reference */ private function __executeDatasource($datasource, &$param_pool = array()) { $result = $datasource->grab($param_pool); $xml = is_object($result) ? $result->generate(true, 1) : $result; // Parse DS XML to check for errors. If contains malformed XML such as // an unescaped database error, the error is escaped in CDATA $doc = new DOMDocument('1.0', 'utf-8'); libxml_use_internal_errors(true); $doc->loadXML($xml); $errors = libxml_get_errors(); libxml_clear_errors(); libxml_use_internal_errors(false); // No error, just return the result if (empty($errors)) { return $result; } // There's an error, so $doc will be empty // Use regex to get the root node // If something's wrong, just push back the broken XML if (!preg_match('/<([^ \\/>]+)/', $xml, $matches)) { return $result; } $ret = new XMLElement($matches[1]); // Set the invalid flag $ret->setAttribute("xml-invalid", "true"); $errornode = new XMLElement("errors"); // Store the errors foreach ($errors as $error) { $item = new XMLElement("error", trim($error->message)); $item->setAttribute('line', $error->line); $item->setAttribute('column', $error->column); $errornode->appendChild($item); } $ret->appendChild($errornode); // Return the XML $ret->appendChild(new XMLElement('broken-xml', "<![CDATA[" . $xml . "]]>")); return $ret; }