I have a problem rendering a sitemap with laravel.
Generated xml seems ok but when i try to call the url from chrome or firefox i got an error
error on line 2 at column 6: XML declaration allowed only at the start of the document
In fact line 1 of the document is empty and xml declaration starts on line 2
Here is my code :
return Response::view('sitemap.index', ['agences' => $agences])->header('Content-Type', 'application/xml');
i tried that syntax too :
$xml = View::make('sitemap.index', ['agences' => $agences]);
return Response::make($xml, 200)->header('Content-Type', 'application/xml');
That way i could do
dd($xml->render());
and realize the string returned has no empty first line.
So i'm guessing Response::make is the one to blame but i really have no idea where to look from there
Ok I'm gonna post my own answer cause that was tricky and it costs me a day, the good thing is my knowledge of laravel has slightly increased.
So i had my xml sitemap beginning with an empty line, and that created an error on browser.
Xml was first generated using a blade template.
As it didn't work i decided to use RoumenDamianoff/laravel-sitemap
But i had the same problem. So finally i decided to generate Xml myself again using SimpleXmlElement and it changes nothing.
At that point i begun to dig in Laravel internal's to see where that empty line could come from in the request lifecycle.
Basically my sitemap is very simple :
$urlset = new SimpleXMLElement('<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" /><!--?xml version="1.0" encoding="UTF-8"?-->');
datas = MyModel::All();
foreach($datas as $index=>$data){
// generate sitemap
}
$dom = new DomDocument();
$dom->loadXML($urlset->asXML());
$dom->formatOutput = true;
//output xml
$xml = $dom->saveXML();
$response = Response::make($xml, 200, ['Content-Type' => 'application/xml']);
Just to test i decided to change the model i was requesting, and then my xml generated without that first empty line.
So i decided to investigate the model itself and find the error. The model file just had an empty line before php opening tag.
Deleting that empty line has solved my problem ....
Related
I am trying to do a simple append to an existing XML document using SimpleXML in PHP. I have an HTML form that calls a PHP script that tries to append data to an already existing XML document in the same directory.
I have tried the basic implementation of this, but I keep getting the follow error:
Warning: SimpleXMLElement::addChild(): Cannot add child. Parent is
not a permanent member of the XML tree in
/the/directory/to/the/site/generate.php on line
9
Fatal error: Uncaught Error: Call to a member function addChild() on
null...
Here is the already existing XML file that is being used:
<?xml version="1.0" encoding="UTF-8"?>
<tasks>
<users/>
<taskList>
<tasks id="1234">
<activities>
<activity/>
</activities>
</task>
</taskList>
</tasks>
Here is the PHP code:
<?php
$file = 'tasks.xml';
$xml = simplexml_load_file($file);
$activities = $xml->activities;
$activity = $activities->addChild('activity');
$activity->setAttribute('id', '45678');
$activity->addChild('assigned', 'Jon');
$activity->addChild('priority', 'low');
$xml->asXML($file);
I am hard coding the values in just to get the append to work, but eventually these values will be from a submitted html form.
Any ideas as to why this is failing?
There are a couple of problems with your code. Firstly your XML is invalid, your close tag should be </tasks>.
When you try and get the <activities> tag in
$activities = $xml->activities;
this is trying to find the tag just off the root of the document, you could use the full path
$activities = $xml->taskList->tasks->activities;
or use (I have in this code) XPath to find it, this also allows you to pick out (if neccessary) which <tasks> tag you use depending of id.
$activities = $xml->xpath("//activities")[0];
$activity = $activities->addChild('activity');
$activity->addAttribute('id', '45678');
$activity->addChild('assigned', 'Jon');
$activity->addChild('priority', 'low');
You also use setAttribute() which doesn't exist - as the code shows it is addAttribute().
I try to validate this document in PHP using DOMdocument's schemaValidate:
<?xml version="1.0" encoding="UTF-8"?> <works xmlns="http://pbn.nauka.gov.pl/-/ns/bibliography" pbn-unit-id="1388"><article><title>Mukowiscydoza</title></article></works>
by using $domDocument->schemaValidate('pbn-report.xsd')
Link to XSD:
https://pbn.nauka.gov.pl/help/images/files/pbn-report.xsd.zip
... and I always get an error
Error 1871: Element 'article': This element is not expected. Expected
is one of ( {http://pbn.nauka.gov.pl/-/ns/bibliography}article,
{http://pbn.nauka.gov.pl/-/ns/bibliography}book,
{http://pbn.nauka.gov.pl/-/ns/bibliography}chapter ). on line 0
For me it is incomprehensible. Why do I get an error when I pointed out the default namespace?
Solved.
It turns out that when you create a DOMDocument, when you add an Element every time you need to give Namespace. When generating a document (saveXML) will not make any difference, but if you run schemaValidate, the validator checks DOMDocument object, and not the generated XML.
In other words this code:
$domDocument = new DOMDocument('1.0', "UTF-8");
$domWorks = $domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'works');
$domWorksId = $domDocument->createAttribute('pbn-unit-id');
$domWorksId->value = PBNID;
$domWorks->appendChild($domWorksId);
$domDocument->appendChild($domWorks);
$domArticle = $domDocument->createElement('article');
$domArticle->appendChild($domDocument->createElement('title','Mukowiscydoza'));
$domWorks->appendChild($domArticle);
echo htmlentities($domDocument->saveXML());
generates the same XML as this code
$domDocument = new DOMDocument('1.0', "UTF-8");
$domWorks = $domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'works');
$domWorksId = $domDocument->createAttribute('pbn-unit-id');
$domWorksId->value = PBNID;
$domWorks->appendChild($domWorksId);
$domDocument->appendChild($domWorks);
$domArticle = $domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'article');
$domArticle->appendChild($domDocument->createElementNS("http://pbn.nauka.gov.pl/-/ns/bibliography",'title','Mukowiscydoza'));
$domWorks->appendChild($domArticle);
echo htmlentities($domDocument->saveXML());
But if you check schema
$domDocument->schemaValidate('pbn-report.xsd');
, the first code will return an error.
Strange ...
Strange ...
Well not really. As long as the document is in memory, the information about the namespace(s) with the elements is preserved.
In that case the two different methods / parameter here really make a difference even if you don't see a difference in the generated XML (afterwards):
// null namespace
$domArticle = $domDocument->createElement('article');
// vs. concrete namespace
$domArticle = $domDocument->createElementNS(
'http://pbn.nauka.gov.pl/-/ns/bibliography', 'article'
);
You then serialize the document (what you describe as "generates the same XML") as XML and you then load that XML back into memory. Then the elements with no namespace aren't within the null namespace any longer because they inherit their namespace from their parent element.
So you must differ between the document and it's elements in memory (DOM) and in the serialized form (string, file).
You can have similar effects when you do XSLT transformations. So if you experience something strange, it's worth to consider that the document in memory is not representing what you first think even it creates similar - or even exact same - looking XML ;)
Try to put the xmlns inside the article element , then try again.
xmlns="http://pbn.nauka.gov.pl/-/ns/bibliography"
I need to post XML data from a textarea input to PHP so I can parse it and output a table.
I've tried a few methods and none seem to be working.
Currently I have this:
jQuery('#btnRegistrarXML').live('click', function(){
var xml;
if (jQuery('#txtRegXML').val() == ""){
AddMsg('You must paste XML from the excel export into the textarea.', 'error');
} else {
jQuery.post('registrar-xml-to-table.php', {xml:escape(jQuery('#txtRegXML').val())}, function(data){
jQuery('#regXMLasTable').empty();
jQuery('#regXMLasTable').append(data);
});
}
displayMsgs();
});
The PHP is:
$xmlraw = urldecode($_POST['xml']);
$dom = new DOMDocument;
$dom->preserveWhiteSpace = FALSE;
$dom->loadXML($xmlraw);
$dom->formatOutput = TRUE;
$xmlstr = $dom->saveXml();
$xml = simplexml_load_string($xmlstr);
echo "<p>xmlraw:</p>";
echo $xmlraw;
echo "<p>xml:</p>";
echo $xml;
foreach ($xml->document as $doc) {
echo '<p class="alert alert-error">'.$doc->title.'</p>';
}
The first echo $xmlraw is working - it outputs the XML string all on one line - the post is sending the data through properly.
The second echo $xml doesn't output anything and the foreach doesn't output anything either - something is not working in the PHP
I've also tried loading $xmlraw directly into simplexml_load_string($xmlraw) but it doesn't work - I'm assuming because it's not well formed?
The XML I'm using to test is:
<?xml version="1.0"?>
<document>
<title>
Foobar
</title>
</document>
You can paste the XML into the textarea on this ( http://tsdexter.com/webservice-testing/programs-list.php ) page and then if you inspect element underneath xmlraw: you can see that the raw xml string was echoed - however, underneath xml: there is nothing and also the foreach doesn't output anything either.
Here's a jsFiddle so you can see the HTML/JS - it doesn't actually do anything though because it can't ajax to the PHP page - so I included the PHP version above to test. http://jsfiddle.net/tsdexter/EDqQB/
Any ideas?
A few questions, which may or may not constitute an answer, but needed more space than a comment.
Why are you running urldecode on the $_POST variable? PHP should be doing that for you, unless you are double-escaping it somewhere.
Why are you loading into DOM and then SimpleXML? I know you said you tried without, but they use the same XML parser underneath, so there is no way this will help you rather than introducing more confusion.
What does var_dump(simplexml_load_string($rawxml)) give you? My guess would be FALSE, meaning an error. In which case, check you have warnings turned on (error_reporting(E_ALL); ini_set('display_errors', '1');) and detailed XML error handling turned off (libxml_use_internal_errors(false);).
In case it does give you a SimpleXMLElement object, don't spend too long looking at the var_dump output. Try one of these debugging functions instead.
I'm using CURL and a proxy to grab some xml files, occasionally only part of the XML document comes through and fails when I try to load/use the xml (simplexml_load_string).
I thought something like..
if(curl_errno($ch))
{
$error = curl_error($ch);
// handle error
}
would catch this sorta error via CURL errno..
CURLE_PARTIAL_FILE (18)
A file transfer was shorter or larger
than expected. This happens when the
server first reports an expected
transfer size, and then delivers data
that doesn't match the previously
given size.
However, this does not work, I think it might be due to using a proxy. Anything else I can check? My only thought now is to do a preg_match for the last bit of the XML document, but that seems less than ideal as I'm getting multiple types of XML documents and I'd have to write checks for each type.
I have experienced the same problem with a proxy and I fell short of solving the issue using cURL's error handlers. If you have access to both scripts (the one requesting and the one delivering the XML) have the requesting one deliver a unique code which it expects at the end of the XML:
// Request
http://localhost/getxml.php?id=4&uniq=1337
And append a comment at the end of the output:
<?xml encoding="..." ..?>
...
<!--1337-->
Well, having an error already tells you that the XML file you got is invalid. What you need to do is catch that error and handle it.
One quick fix is this:
$xml = #simplexml_load_string($xmlString);
if($xml === false){ /* The XML was not valid. */ }
One log fix is this one:
libxml_use_internal_errors(true);
libxml_clear_errors();
$xml = simplexml_load_string($xmlString);
if( ($err = libxml_get_last_error()) !== false ){ /* We got ourselves an XML error. */ }
I am trying to generate a dynamic xml document in CakePHP to output to the browser.
Here is my controller code:
Configure::write ('debug', 0);
$this->layout = null;
header('Content-type: text/xml');
echo "<?xml version=\"1.0\"?>";
View is something like this:
<abc>
something
</abc>
The output is probably as expected:
<?xml version="1.0"?><abc>something</abc>
The only problem is that there is a space before <?xml giving me an error:
XML Parsing Error: XML or text declaration not at start of entity
Line Number 1, Column 2:
<?xml version="1.0"?><abc> something </abc>
-^
I know this problem in PHP, when you have php-start and end tags it leaves a space and creates problems, so, I tried to move the line echo "<?xml ver... to controller from the view to avoid that but it didn't help.
Thanks in advance.
-happyhardik
Yes, the problem should be an space after the php end tag somewhere.
As the php end tag is not mandatory, remove any end tag in all your models (if there're any), the controller you're asking about, from app_controller.php and app_model.php and from your view helpers... It should be somewhere but it is not easy to find
EDIT: In fact it could be also an space before the php begin tag, look into those files and check that the begin tag is at the absolute beginning of the file
EDIT AGAIN: There are people that have created some scripts for doing that automatically for you, take a look to:
http://ragrawal.wordpress.com/2007/11/07/script-for-removing-blank-spaces-before-and-after-php-tags/
Actually, I find that it is most often a space AFTER the closing ?> tag in the layout file.
Also you should know that if you use the RequestHandler component and Router::parseExtensions( 'xml' ) in your routes.php you will automatically get the XmlHelper for use in your xml views.
The XmlHelper has a few neat functions in it. Check it out.
<?php
echo( $xml->header( ));
// outputs <?xml version="1.0" encoding="UTF-8" ?>
?>
The links for RequestHandler Component and the XmlHelper
http://book.cakephp.org/view/174/Request-Handling
http://book.cakephp.org/view/380/XML
Even though this does not answer the question directly. I thought it would be worth mentioning how easy it is to create dynamic XML views automatically using the CakePHP JSON and XML views helper, just in case people don't want to be doing it manually as seem to be the case above.
Step one: Add Router::parseExtensions(); to your routes.php file
Step two: Ensure the RequestHandler component is included in the relevant countroller by adding public $components = array('RequestHandler');
Step three: Now we only have to load some data and then display the data as XML or JSON automatically. Add something like the below:
public function xml_view () {
$this->set('data_array', $this->Model->find('all'));
$this->set('_serialize', array('data_array'));
}
That's literally all we need to do to generate an XML or JSON respone for the xml_view action. Not even necessary to set up a view file. When your request is .../controller/xml_view.xml then CakePHP will return an XML document, and when .json is the extension, a JSON response will be generate. So easy I can't believe it!