PHP XML validation fails on valid XML - php

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.

Related

Using DOMDocument Validate() on XML Causes Infinite Load

I have an extremely simple implementation that pulls in a test bit of XML and attempts to validate it using DOMDocument. In testing, it's able to get through the LoadHTML() call fine, but as soon as I try and run validate(), the browser hangs forever and doesn't load. Here's the code:
$content = '<?xml version="1.0" encoding="utf-8"?><mainElement></mainElement>';
$dom = new DOMDocument;
$dom->LoadHTML($content);
if (!$dom->validate()) {
echo 'fail';
} else {
echo 'success!';
}
It seems that if you want to validate content loaded with loadHTML, you need DOCTYPE declaration (without it, you get an infinitive loop). For example, following code works and prints fail
$content = "
<!DOCTYPE html>
<html>
<body>
Content of the document......
</body>
</html>
";
$dom = new DOMDocument();
$dom->loadHTML($content);
if (!$dom->validate()) {
echo 'fail';
} else {
echo 'success!';
}
For XML it's more tolerant (it works even you didn't declare dtd but it returns false). In your case, you might use loadXML method and your code will print fail.
Tested with php 7.0.13.

PHP xpath won't return data

im trying to filter out some informations from an XML file using PHP xpath, somehow the response seems always to be empty. Does anyone have an idea what could cause this?
Xml example:
<account>
<a>Information1</a>
<b>Information2</b>
<c>Informatio3</c>
</account>
My actual code:
public static function user_cache($username) {
$xml = #file_get_contents('xmlfile');
$xml = #simplexml_load_string($xml);
if ( $xml ) {
$results = array(
"userid" => $xml->xpath('a')
);
}
else {
$results['message'] = 'Unable to get data.';
}
return $results;
}
{
"userid": []
}
This is more of a XPATH question. The correct syntax would be
$xml->xpath('//a')
You can find more information about XPATH's syntax here: http://www.w3schools.com/xsl/xpath_syntax.asp
On the other hand, xpath method returns an array of SimpleXMLElement, so take that into account.
http://php.net/manual/en/simplexmlelement.xpath.php

XML signature differences between HEREDOC and DOMDocument

An API client I have developed works with XML messages and the messages are signed according to the XML Signature Syntax and Processing specification. After a long struggle, I finally got the signatures working.
At this moment I am building the XML with HEREDOC (simply php strings) and with a cleanup, I'd like to create the XML with DOMDocument directly. However, this causes the message to be invalidated by the server.
This is the current setup (server accepts this message when signed):
$xml = <<<EOT
<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
<createDateTimestamp>$timestamp</createDateTimestamp>
<Merchant>
<merchantID>$merchantId</merchantID>
<subID>$subId</subID>
</Merchant>
</DirectoryReq>
EOT;
$document = new DOMDocument();
$document->loadXML($xml);
This is the OO approach (server rejects this message when signed):
$document = new DOMDocument('1.0', 'UTF-8');
$request = $document->createElement('DirectoryReq');
$xmlns = $document->createAttribute('xmlns');
$xmlns->value = 'http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1';
$version = $document->createAttribute('version');
$version->value = '3.3.1';
$request->appendChild($xmlns);
$request->appendChild($version);
$merchant = $document->createElement('Merchant');
$merchant->appendChild($document->createElement('merchantID', $merchantId));
$merchant->appendChild($document->createElement('subID', $subId));
$request->appendChild($document->createElement('createDateTimestamp', $timestamp));
$request->appendChild($merchant);
$document->appendChild($request);
What can cause the difference as such the XML signature is invalidated by the server? The code to sign the message is exactly the same. The server is simply reporting "Invalid electronic signature".
If required I can show more code.
EDIT, more output and comparison of XML generated
To give some more information, this is the output of the first (HEREDOC) xml, generated via $document->saveXml():
<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
<createDateTimestamp>2013-08-10T19:41:20.000Z</createDateTimestamp>
<Merchant>
<merchantID>0020XXXXXX</merchantID>
<subID>0</subID>
</Merchant>
</DirectoryReq>
This is the output ($document->saveXML()) for the second (direct DOMDocument generation) method:
<?xml version="1.0" encoding="UTF-8"?>
<DirectoryReq xmlns="http://www.idealdesk.com/ideal/messages/mer-acq/3.3.1" version="3.3.1">
<createDateTimestamp>2013-08-10T19:41:20.000Z</createDateTimestamp>
<Merchant>
<merchantID>0020XXXXXX</merchantID>
<subID>0</subID>
</Merchant>
</DirectoryReq>
In php, var_dump() gives the exact same string length. If I compare both strings (=== obviously) they are the same. Comparing both objects, then they are not the same.
Signing example
Signing occurs with the library xmlseclibs with this code (NB. both types are signed the same way!):
public function sign(DOMDocument $document, $fingerprint, $keyfile, $passphrase = null)
{
$dsig = new XMLSecurityDSig();
$dsig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
$dsig->addReference($document, XMLSecurityDSig::SHA256,
array('http://www.w3.org/2000/09/xmldsig#enveloped-signature'),
array('force_uri' => true)
);
$key = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'private'));
if ($passphrase !== null) {
$key->passphrase = $passphrase;
}
$key->loadKey($keyfile, true);
$dsig->sign($key);
$dsig->addKeyInfoAndName($fingerprint);
$dsig->appendSignature($document->documentElement);
}
If I dump the XML after it's signed, the <DigestValue> and <SignatureValue> values are different. So the server is correct the signature is invalid, but I cannot come up with a clue why method A works and B not.
You are overwriting $merchant when you create the Merchant element, so just rename the variable
$merchantElement = $document->createElement('Merchant');
I have now solved it by exporting and importing the XML again. It's quite ugly, but allows me to flexibly handle the DOMNodes.
protected function repairDOMDocument(DOMDocument $document)
{
$xml = $document->saveXML();
$document = new DOMDocument;
$document->loadXML($xml);
return $document;
}
If there is a suggestion how to stop doing this, I am pleased to hear so.

SimpleXMLElement() - XPath() & SOAP returning nothing

After hours of searching, finding similar threads and still not being able to get it to work I've resorted to posting my specific problem. I'm getting a SOAP encoded XML response from a server that i want to use SimpleXMLElement() on, but i'm having a real hard time establishing a base path to work from.
I've tried two different methods:
xpath():
public function XMLParseUserData($xml)
{
$ActivityData = new SimpleXMLElement($xml);
$ActivityData->registerXPathNamespace("ns", "http://webservices.website.net/");
$basePath = $ActivityData->xpath('//ns:GetUserActivityDataResult/ActArray');
foreach ($basePath->ACT as $userActivity)
{
$this->uGUID = $userActivity->UserGUID;
echo $this->uGUID."<br />";
}
}
children():
public function XMLParseUserData($xml)
{
$ActivityData = new SimpleXMLElement($xml);
$basePath = $ActivityData->children('soap',true)->Body->GetUserActivityDataResponse->GetUserActivityDataResult->ActArray->ACT;
foreach ($basePath as $userActivity)
{
$this->uGUID = $userActivity->UserGUID;
echo $this->uGUID."<br />";
}
}
The XML response:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<GetUserActivityDataResponse xmlns="http://webservices.website.net/">
<GetUserActivityDataResult>
<ResponseCode>SUCCESS</ResponseCode>
<FailedUserCount>0</FailedUserCount>
<ActCount>1</ActCount>
<ActArray>
<ACT>
<UserGUID>0dc299ba-XXXX-XXXX-XXXX-7ca097d51eb6</UserGUID>
<ActDataCount>15</ActDataCount>
<ActData>
<ACT_DATA>
<Start>2012-03-05T08:40:00</Start>
<End>2012-03-05T09:00:00</End>
<SourceCount>1</SourceCount>
<SourceData>
<ACT_SRC_DATA>
<Source>ACTIPED</Source>
<TypeCount>3</TypeCount>
<TypeData>
<ACT_TYPE_DATA>
<Type>WALK</Type>
<S>40</S>
<C>2</C>
<D>20</D>
<T>16</T>
</ACT_TYPE_DATA>
<ACT_TYPE_DATA>
<Type>RUN</Type>
<S>20</S>
<C>2</C>
<D>20</D>
<T>10</T>
</ACT_TYPE_DATA>
<ACT_TYPE_DATA>
<Type>OTHER</Type>
<S>0</S>
<C>0</C>
<D>0</D>
<T>28</T>
</ACT_TYPE_DATA>
</TypeData>
<MetricCount>0</MetricCount>
</ACT_SRC_DATA>
</SourceData>
</ACT_DATA>
</ActData>
</ACT>
</ActArray>
<AsOfServerTimeGMT>2012-03-06T16:41:41.513</AsOfServerTimeGMT>
</GetUserActivityDataResult>
</GetUserActivityDataResponse>
</soap:Body>
</soap:Envelope>
Neither method works and both leave me with the same error:
Warning: Invalid argument supplied for foreach() in /c08/domains/dev.mysite.com/html/class/XMLParse.class.php on line 29
It says:
<GetUserActivityDataResponse xmlns="http://webservices.website.net/">
and you are trying to do
$basePath = $soapEnvelope
->children('soap', true)
->Body
->GetUserActivityDataResponse
…
which means you try to get <soap:GetUserActivityDataResponse> which obviously doesnt exist. You have to do (demo)
$basePath = $soapEnvelope
->children('soap', true)
->Body
->children('http://webservices.website.net/')
->GetUserActivityDataResponse
->GetUserActivityDataResult
->ActArray
->ACT;
Actually, you could just do ->children() to jump back to the default namespace, but I find providing the namespace explicitly somewhat clearer. Your choice.
Your XPath fails because you didn't specify the namespace for ActArray. Also, when xpath() is successful, it returns an array of SimpleXmlElements. You tried array->ACT, which doesn't work because an array is not an object. The first ActArray is in $basePath[0]. So you have to adjust the code to
$basePath = $soapEnvelope->xpath('//ns:GetUserActivityDataResult/ns:ActArray');
foreach ($basePath[0]->ACT as $userActivity) {
…
To get the ACT elements directly, change the XPath to
//ns:GetUserActivityDataResult/ns:ActArray/ns:ACT

PHP XML Validation Throws Fatal Exceptions When Passed Bad XML

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

Categories