Search code examples
phpxmlcurldomsimplexml

LibXMLError XML declaration allowed only at the start of the document


So I'm using curl to call a php page to receive an XML response. simplexml_load_string isn't liking my xml for some reason. Using libxml_get_errors() I was able to get the title error message.

Here's code:

$service_url = 'blahblahblah';
$curl = curl_init($service_url);
$curl_post_data = array(
    "action" => "LISTWORKOUT",
    "accesstoken" => $accesstoken,
    "workoutid" => 2,
);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_POST, true);
curl_setopt($curl, CURLOPT_POSTFIELDS, $curl_post_data);
$curl_response = curl_exec($curl);
curl_close($curl);


libxml_use_internal_errors(true);
$xml=simplexml_load_string($curl_response); //or simplexml_load_file

foreach( libxml_get_errors() as $error ) {

    print_r($error);

}

echo $curl_response;

Heres the page its hitting:

    $workoutid = mysql_real_escape_string($_POST['workoutid']);
        // Create the Element and Append the Child
        $result = mysql_query("SELECT * FROM `lifts` WHERE `workoutid` = $workoutid OR `sharedid` = $workoutid ",$sqlAPI);

        $xml = new DOMDocument("1.0");
        $xml->formatOutput=true;
        $workout=$xml->createElement("workout");
        $xml->appendChild($workout);

        while($row = mysql_fetch_array($result))
        {
            $lift=$xml->createElement("lift");
            $workout->appendChild($lift);

            $name=$xml->createElement("name",$row['name']);
            $lift->appendChild($name);

            $weight=$xml->createElement("weight", $row['weight']);
            $lift->appendChild($weight);


            $sets=$xml->createElement("sets",$row['sets']);
            $lift->appendChild($sets);

            $reps=$xml->createElement("reps", $row['reps']);
            $lift->appendChild($reps);


        }

        echo"<xmp>" . $xml->saveXML() . "</xmp>";

And for good measure here is the output:

LibXMLError Object ( [level] => 3 [code] => 64 [column] => 11 [message] => 

XML declaration allowed only at the start of the document [file] => [line] => 1 )
<?xml version="1.0"?>
<workout>
  <lift>
    <name>Squats</name>
    <weight>45</weight>
    <sets>5</sets>
    <reps>5</reps>
  </lift>
  <lift>
    <name>Overhead Press</name>
    <weight>45</weight>
    <sets>5</sets>
    <reps>5</reps>
  </lift>
  <lift>
    <name>Deadlift</name>
    <weight>45</weight>
    <sets>1</sets>
    <reps>5</reps>
  </lift>
</workout>

Note the echo displays the XML with no issues.

I think it has something to do with these quotation marks maybe? It might be a stretch. I already went ahead and tried str_replace them but still no bueno. enter image description here


Solution

  • From the image and code in the question is seems like what you have in your document is this:

    <xmp>
      <?xml version="1.0"?>
      <workout>
    

    That is, the root element of the document you’re feeding to simplexml_load_string is <xmp>, not <workout>. So LibXML starts parsing that document from the <xmp> element, then hits the <?xml version="1.0"?> and says, Hey that’s an XML declaration—that shouldn’t be here.

    To prevent the XML declaration from getting added, you can replace your $xml->saveXML() with:

    foreach ($xml->childNodes as $node) {
       echo $xml->saveXML($node);
    }
    

    Or see some of the other answers for the questions PHP DomDocument output without <?xml version="1.0" encoding="UTF-8"?> and remove xml version tag when a xml is created in php.

    The idea is just that instead of outputting the whole thing as a document, your code instead iterates through all nodes of the document starting with the root and outputs them one-by-one in order.

    So because you’re outputting individual nodes that way instead of the whole document as a document, that skips the unwanted XML declaration at the beginning for the document.

    An alternative that may also work is, make saveXML(…) only emit the document element (root):

    $xml->saveXML($xml->documentElement)
    

    If it works, it’ll emit your workout element and all its descendants, skipping the XML declaration.