PHP XML Validation Throws Fatal Exceptions When Passed Bad XML - php

I've a quick function to load up an XML string, and validate it against a schema. When its given well formed XML, it behaves perfectly.
However when I muck up the xml syntax itself, php throws a fatal error and kills the script. I am checking the loadXML function return value, and I want a simple true/false. If the xml is dirty, loadXML() will fail and I can simply return validation failure. I've tried setting an empty error handler, but it still kills the script.
Any ideas? Do I need to downgrade errors or something?
Included code for reference (PHP):
function __maskerrors(){};
function ValidateImageXML($xml_string)
{
/* Parse XML data string into DOM object */
$xdoc = new DomDocument;
/* Calculate schema location */
$schema = dirname(realpath(__FILE__));
$schema.= "/image-xml-schema.xsd";
/* Mask any errors */
set_error_handler('__maskerrors');
/* Parse xml string, check for success */
if($xdoc->loadXML($xml_string))
{
/* Validate xml against schema */
if($xdoc->schemaValidate($schema))
{
/* Valid XML structure & valid against schema, return true */
return true;
}
else
{
/* Valid XML structure, but invalid against schema. Return false */
return false;
}
}
else
{
/* Invalid XML structure, return false */
return false;
}
/* Stop masking errors */
restore_error_handler();
}

Try with
libxml_use_internal_errors(true);
$xdoc->loadXml($yourXml);
libxml_clear_errors();
return $xdoc->schemaValidate($schema)
This will disable libxml errors and allow user to fetch error information as needed (or clear them)
See http://.php.net/manual/en/book.libxml.php

Related

Saving SimpleXMLElement parameter to global variable

I'm currently using the Botman framework to make my bot read a XML file.
Currently, my bot is able to grab data from an XML file and output it.
I'm having issue saving the XML file back into a global variable (so I can reuse later on in the code). Here is the current error message I get when trying to do this:
"message": "Serialization of 'SimpleXMLElement' is not allowed",
"exception": "Exception",
"file": "C:\\Users\\Jack\\finalyearproject\\gfyp\\gfyp\\vendor\\opis\\closure\\src\\SerializableClosure.php
I'm having issues here:
public function nodeTest($xmlFile, $answer)
{
$this->XMLFile = $xmlFile;
...
}
Here is the class code before the function:
class StartConversation extends Conversation
{
public $XMLFile;
...
public function askForDatabase()
{
$question = Question::create('Test test test?')
->fallback('Unable to create a new database')
->callbackId('create_database')
->addButtons([
Button::create('Suicide')->value('suic'),
Button::create('Self-harm')->value('sh'),
]);
$this->ask($question, function (Answer $answer) {
$xmlResult = $this->testXMLGrabFunction($answer);
if ($answer->getValue() == 'suic') {
$this->nodeTest($xmlResult, $answer);
}
if ($answer->getValue() == 'sh') {
$this->nodeTest($xmlResult, $answer);
}
});
}
}
Here is the class where I get the XML file originally:
class testClass
{
function getXMLCategory($categoryName)
{
$xml = simplexml_load_file('ST-working-age-23-3-20.xml');
if($categoryName == 'suic')
{
$xml = $xml->node[0];
return $xml;
} elseif($categoryName == 'sh') {
$xml = $xml->node[1];
return $xml;
} else {
return null;
}
}
}
Any suggestions would be great - thanks
The error message is telling you that somewhere in the code is trying to serialize the object, that is turn it into a string representation. This is probably in the framework you're using, and what you are thinking of as a "global variable" is actually stored between requests in some form of session, e.g. in a file on disk.
Because of the way SimpleXML is implemented, it doesn't allow this operation. The simplest workaround is to instead store the XML by calling ->asXML(), and then re-parse it when you need it with simplexml_load_string().
You'll want to do that round trip as rarely as possible, so it will be worth understanding better about how the "global variables" are actually handled by the framework so you can try to do it once on each request.

PHP XML validation fails on valid XML

I use the following function to validate XML coming from a Web API before trying to parse it:
function isValidXML($xml) {
$doc = #simplexml_load_string($xml);
if ($doc) {
return true;
} else {
return false;
}
}
For some reason, it fails on the following XML. While it's a bit light in content, it looks valid to me.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><connection-response-list xmlns="http://www.ca.com/spectrum/restful/schema/response" />
Why would this fail? I tried another method of validate the XML that used DOMDocument and libxml_get_errors(), but it was actually more fickle.
EDIT: I should mention that I'm using PHP 5.3.8.
I think your interpretation is just wrong here – var_dump($doc) should give you
object(SimpleXMLElement)#1 (0) {
}
– but since it is an “empty” SimpleXMLElement, if($doc) considers it to be false-y due to PHP’s loose type comparison rules.
You should be using
if ($doc !== false)
here – a type-safe comparison.
(Had simplexml_load_string actually failed, it would have returned false – but it didn’t, see var_dump output I have shown above, that was tested with exactly the XML string you’ve given.)
SimpleXML wants some kind of "root" element. A self-closing tag at the root won't cut it.
See the following code when a root element is added:
<?php
function isValidXML($xml)
{
$doc = #simplexml_load_string($xml);
if ($doc) {
return true;
} else {
return false;
}
}
var_dump(isValidXML('<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root><connection-response-list xmlns="http://www.ca.com/spectrum/restful/schema/response" /></root>'));
// returns true
print_r(isValidXML('<?xml version="1.0" encoding="UTF-8" standalone="yes"?><root><connection-response-list xmlns="http://www.ca.com/spectrum/restful/schema/response" /></root>'));
// returns 1
?>
Hope that helps.

Check if an xml is loaded with simplexml_load_string

I am querying an xml file with php like this :
public function trackOrderAction()
{
$request = Mage::getResourceModel( 'order/request' );
$request->setOrder($this->getRequest()->getParam('increment_id'));
$response = $request->submit(true);
$xml = simplexml_load_string($response);
$items = count($xml->OrderItems->OrderItem);
}
The xml is not ready immediately so if people try to use the function before it is ready there is an error because it is trying to get the property of a non-object. My question is what is the proper way to check the xml response to see if there is anything and stop the function if there is not?
I tried something simple like
if (empty($xml)){
die();
} else {
$items = count($xml->OrderItems->OrderItem);
}
But this does not help. Any ideas on how to check to see if the xml loaded?
From http://us.php.net/manual/en/function.simplexml-load-string.php
Returns an object of class SimpleXMLElement with properties containing
the data held within the xml document. On errors, it will return
FALSE.
public function trackOrderAction()
{
$request = Mage::getResourceModel( 'order/request' );
$request->setOrder($this->getRequest()->getParam('increment_id'));
$response = $request->submit(true);
$xml = simplexml_load_string($response);
if ( !$xml ) {
return false;
}
$items = count($xml->OrderItems->OrderItem);
}
It will return false if there was an error. So fail right away if simplexml_load_string fails and return false. Otherwise continue on with the rest of the function. Never die() in a function.

How to get validation errors in an array?

I am developing an REST API using Codeigniter.
I need to have all error messages from Form validation in array format so that
I can easily respond in either JSON or XML.
Right now Codeigniter is delivering error messages with <p> as delimiters (see below)
but that is not good for a REST based API.
<p>There was an error with this item</p>
How can I get errors in an array?
Thankful for all input!
The form validation library stores errors in an array and loops through them to generate the error string. They are stored in a private variable called $_error_array. You could extend the library with a simple method that returns the error array.
class MY_Form_validation extends CI_Form_validation
{
function get_error_array()
{
return $this->_error_array;
}
}
I assume you are familiar with extending core CI libraries, but this extension of the form validation library will give you a method to return the errors as an array with the name attribute's value as the key and the message as the value. I tested it out on one of my projects and it worked fine.
You can transform it easily:
/**
* #param $errors string
* #return array
*/
function transformErrorsToArray ($errors) {
$errors = explode('</p>', $errors);
foreach ($errors as $index => $error) {
$error = str_replace('<p>', '', $error);
$error = trim($error);
// ... more cleaning up if necessary
$errors[$index] = $error
}
return $errors;
}

PHP Always run function

I am trying to get some errors returned in JSON format. So, I made a class level var:
public $errors = Array();
So, lower down in the script, different functions might return an error, and add their error to the $errors array. But, I have to use return; in some places to stop the script after an error occurs.
So, when I do that, how can I still run my last error function that will return all the gathered errors? How can I get around the issue of having to stop the script, but still wanting to return the errors for why I needed to stop the script?!
Really bare bones skeleton:
$errors = array();
function add_error($message, $die = false) {
global $errors;
$errors[] = $message;
if ($die) {
die(implode("\n", $errors));
}
}
If you are using PHP5+ your class can have a destructor method:
public function __destruct() {
die(var_dump($this->errors));
}
You can register a shutdown function.
Add the errors to the current $_SESSION
Add the latest errors to any kind of cache, XML or some storage
If the code 'stops':
// code occurs error
die(print_r($errors));
You can use a trick involving do{}.
do {
if(something) {
// add error
}
if(something_else) {
// add error
break;
}
if(something) {
// add error
}
}while(0);
// check/print errors
Notice break, you can use it to break out of the do scope at any time, after which you have the final error returning logic.
Or you could just what's inside do{} inside a function, and use return instead of break, which would be even better. Or yes, even better, a class with a destructor.

Categories