I currently use the following PHP code to generate an XML file from form inputs on a HTML page (I'm using $_POST):
$doc = new DOMDocument('1.0');
$doc->formatOutput = true;
$doc->preserveWhiteSpace = true;
$doc->loadXML($xml->asXML(), LIBXML_NOBLANKS);
$doc->save('../application/'.$filefname.$filesname.'_'.date("Y-m-d").'.xml');
However I would like to generate two XML files, each with different information. Do I need to do something like have two variables? eg. $xml1 and $xml2, $doc1 and $doc2 like so?
$doc1 = new DOMDocument('1.0');
$doc1->formatOutput = true;
$doc1->preserveWhiteSpace = true;
$doc1->loadXML($xml1->asXML(), LIBXML_NOBLANKS);
$doc1->save('../application/'.$filefname.$filesname.'_'.date("Y-m-d").'.xml');
$doc2 = new DOMDocument('1.0');
$doc2->formatOutput = true;
$doc2->preserveWhiteSpace = true;
$doc2->loadXML($xml2->asXML(), LIBXML_NOBLANKS);
$doc2->save('../application/'.$filefname.$filesname.'_'.date("Y-m-d").'.xml');
Eg. Two email addresses and two names are entered into the online form, I want each person's details in a seperate file.
Customer 1's name and email in cust1.xml and Customer 2's name and email in cust2.xml
You can reuse the $doc variable. As soon as you assign it a new DOMDocument the $doc variable points to the new instance.
// $doc points to instance #1 of DOMDocument
$doc = new DOMDocument('1.0');
...
// $doc points to instance #2 of DOMDocument
$doc = new DOMDocument('1.0');
The same applies to all reference types like objects.
Instead of duplicating your code you should create a function
function createDocument($xml) {
$doc = new DOMDocument('1.0');
$doc->formatOutput = true;
$doc->preserveWhiteSpace = true;
$doc->loadXML($xml->asXML(), LIBXML_NOBLANKS);
$doc->save('../application/'.$filefname.$filesname.'_'.date("Y-m-d").'.xml');
}
You should always avoid code duplication. See DRY principle.
Related
I try to access the values of a table on a web page with a php expression DOMXPath::query. When I navigate with my web browser in this page I can see this table but when I execute my query this table isn't visible and don't seem accessible.
This table have an id, but when I specify it on my query an other one is returned. I want to read the table with the id 'totals', but I only have that one with the id 'per_game'. When I inspect page's code, a lot of elements seem to be in comments.
Here is my script:
<?php
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->strictErrorChecking = false;
$doc->recover = true;
$doc->loadHTMLFile('https://www.basketball-reference.com/players/j/jokicni01.html');
$xpath = new DOMXPath($doc);
$table = $xpath->query("//div[#id='totals']")->item(0);
$elem = $doc->saveXML($table);
echo $elem;
?>
How can i read elements in the table with the id 'totals' ?
The full path is /html/body/div[#id="wrap"]/div[#id="content"]/div[#id="all_totals"]/div[#class="table_outer_container"]/div[#id="div_totals"]/table[#id="totals"]
You can cut your query in two parts : first, retrieve the comment in the correct div, then create a new document with this content to retrieve the element you want :
$doc = new DOMDocument;
$doc->preserveWhiteSpace = false;
$doc->strictErrorChecking = false;
$doc->recover = true;
#$doc->loadHTMLFile('https://www.basketball-reference.com/players/j/jokicni01.html');
$xpath = new DOMXPath($doc);
// retrieve the comment section in 'all_totals' div
$all_totals_element = $xpath->query('/html/body/div[#id="wrap"]/div[#id="content"]/div[#id="all_totals"]/comment()')->item(0);
$all_totals_table = $doc->saveXML($all_totals_element);
// strip comment tags to keep the content inside
$all_totals_table = substr($all_totals_table, strpos($all_totals_table, '<!--') + strlen('<!--'));
$all_totals_table = substr($all_totals_table, 0, strpos($all_totals_table, '-->'));
// create a new Document with the content of the comment
$tableDoc = new DOMDocument ;
$tableDoc->loadHTML($all_totals_table);
$xpath = new DOMXPath($tableDoc);
// second part of the query
$totals = $xpath->query('/div[#class="table_outer_container"]/div[#id="div_totals"]/table[#id="totals"]')->item(0);
echo $tableDoc->saveXML($totals) ;
Here are the codes:
$doc = new DomDocument('1.0');
// create root node
$root = $doc->createElement('root');
$root = $doc->appendChild($root);
$signed_values = array('a' => 'eee', 'b' => 'sd', 'c' => 'df');
// process one row at a time
foreach ($signed_values as $key => $val) {
// add node for each row
$occ = $doc->createElement('error');
$occ = $root->appendChild($occ);
// add a child node for each field
foreach ($signed_values as $fieldname => $fieldvalue) {
$child = $doc->createElement($fieldname);
$child = $occ->appendChild($child);
$value = $doc->createTextNode($fieldvalue);
$value = $child->appendChild($value);
}
}
// get completed xml document
$xml_string = $doc->saveXML() ;
echo $xml_string;
If I print it in the browser I don't get nice XML structure like
<xml> \n tab <child> etc.
I just get
<xml><child>ee</child></xml>
And I want to be utf-8
How is this all possible to do?
You can try to do this:
...
// get completed xml document
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$xml_string = $doc->saveXML();
echo $xml_string;
You can make set these parameter right after you've created the DOMDocument as well:
$doc = new DomDocument('1.0');
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
That's probably more concise. Output in both cases is (Demo):
<?xml version="1.0"?>
<root>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
<error>
<a>eee</a>
<b>sd</b>
<c>df</c>
</error>
</root>
I'm not aware how to change the indentation character(s) with DOMDocument. You could post-process the XML with a line-by-line regular-expression based replacing (e.g. with preg_replace):
$xml_string = preg_replace('/(?:^|\G) /um', "\t", $xml_string);
Alternatively, there is the tidy extension with tidy_repair_string which can pretty print XML data as well. It's possible to specify indentation levels with it, however tidy will never output tabs.
tidy_repair_string($xml_string, ['input-xml'=> 1, 'indent' => 1, 'wrap' => 0]);
With a SimpleXml object, you can simply
$domxml = new DOMDocument('1.0');
$domxml->preserveWhiteSpace = false;
$domxml->formatOutput = true;
/* #var $xml SimpleXMLElement */
$domxml->loadXML($xml->asXML());
$domxml->save($newfile);
$xml is your simplexml object
So then you simpleXml can be saved as a new file specified by $newfile
<?php
$xml = $argv[1];
$dom = new DOMDocument();
// Initial block (must before load xml string)
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
// End initial block
$dom->loadXML($xml);
$out = $dom->saveXML();
print_R($out);
Tried all the answers but none worked. Maybe it's because I'm appending and removing childs before saving the XML.
After a lot of googling found this comment in the php documentation. I only had to reload the resulting XML to make it work.
$outXML = $xml->saveXML();
$xml = new DOMDocument();
$xml->preserveWhiteSpace = false;
$xml->formatOutput = true;
$xml->loadXML($outXML);
$outXML = $xml->saveXML();
// ##### IN SUMMARY #####
$xmlFilepath = 'test.xml';
echoFormattedXML($xmlFilepath);
/*
* echo xml in source format
*/
function echoFormattedXML($xmlFilepath) {
header('Content-Type: text/xml'); // to show source, not execute the xml
echo formatXML($xmlFilepath); // format the xml to make it readable
} // echoFormattedXML
/*
* format xml so it can be easily read but will use more disk space
*/
function formatXML($xmlFilepath) {
$loadxml = simplexml_load_file($xmlFilepath);
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$dom->loadXML($loadxml->asXML());
$formatxml = new SimpleXMLElement($dom->saveXML());
//$formatxml->saveXML("testF.xml"); // save as file
return $formatxml->saveXML();
} // formatXML
Two different issues here:
Set the formatOutput and preserveWhiteSpace attributes to TRUE to generate formatted XML:
$doc->formatOutput = TRUE;
$doc->preserveWhiteSpace = TRUE;
Many web browsers (namely Internet Explorer and Firefox) format XML when they display it. Use either the View Source feature or a regular text editor to inspect the output.
See also xmlEncoding and encoding.
This is a slight variation of the above theme but I'm putting here in case others hit this and cannot make sense of it ...as I did.
When using saveXML(), preserveWhiteSpace in the target DOMdocument does not apply to imported nodes (as at PHP 5.6).
Consider the following code:
$dom = new DOMDocument(); //create a document
$dom->preserveWhiteSpace = false; //disable whitespace preservation
$dom->formatOutput = true; //pretty print output
$documentElement = $dom->createElement("Entry"); //create a node
$dom->appendChild ($documentElement); //append it
$message = new DOMDocument(); //create another document
$message->loadXML($messageXMLtext); //populate the new document from XML text
$node=$dom->importNode($message->documentElement,true); //import the new document content to a new node in the original document
$documentElement->appendChild($node); //append the new node to the document Element
$dom->saveXML($dom->documentElement); //print the original document
In this context, the $dom->saveXML(); statement will NOT pretty print the content imported from $message, but content originally in $dom will be pretty printed.
In order to achieve pretty printing for the entire $dom document, the line:
$message->preserveWhiteSpace = false;
must be included after the $message = new DOMDocument(); line - ie. the document/s from which the nodes are imported must also have preserveWhiteSpace = false.
based on the answer by #heavenevil
This function pretty prints using the browser
function prettyPrintXmlToBrowser(SimpleXMLElement $xml)
{
$domXml = new DOMDocument('1.0');
$domXml->preserveWhiteSpace = false;
$domXml->formatOutput = true;
$domXml->loadXML($xml->asXML());
$xmlString = $domXml->saveXML();
echo nl2br(str_replace(' ', ' ', htmlspecialchars($xmlString)));
}
I made a PHP script that updates an existing XML file by adding new nodes. The problem is that the new nodes are not formatted. They are written in a single line. Here is my code :
$file = fopen('data.csv','r');
$xml = new DOMDocument('1.0', 'utf-8');
$xml->formatOutput = true;
$doc = new DOMDocument();
$doc->loadXML(file_get_contents('data.xml'));
$xpath = new DOMXPath($doc);
$root = $xpath->query('/my/node');
$root = $root->item(0);
$root = $xml->importNode($root,true);
// all the tags created in this loop are not formatted, and written in a single line
while($line=fgetcsv($file,1000,';')){
$tag = $xml->createElement('cart');
$tag->setAttribute('attr1',$line[0]);
$tag->setAttribute('attr2',$line[1]);
$root->appendChild($tag);
}
$xml->appendChild($root);
$xml->save('updated.xml');
How can I solve this?
Try adding preserveWhiteSpace = FALSE; to DOMDocument object where is file stored.
$xml = new DOMDocument('1.0', 'utf-8');
$xml->formatOutput = true;
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->loadXML(file_get_contents('data.xml'));
$doc->formatOutput = true;
...
PHP.net - DOMDocument::preserveWhiteSpace
Currently, im having this for appending data to my items file:
$xmldoc = new DOMDocument();
$xmldoc->load('ex.xml');
$item= $xmldoc->createElement('item');
$item->setAttribute('id', '100');
$item->setAttribute('category', 'Fitness');
$item->setAttribute('name', 'Basketball');
$item->setAttribute('url', 'http://google.com');
$item->setAttribute('description', 'This is a description');
$item->setAttribute('price', '899');
$xmldoc->getElementsByTagName('items')->item(0)->appendChild($item);
$xmldoc->save('ex.xml');
Now before appending this, I would like to check for an existing element "item" that has the same attribute id value.
And if it does it should update that element with these new data.
Currently it just appends and doesnt check anything.
$xmldoc = new DOMDocument();
$xmldoc->load('ex.xml');
$xpath = new DOMXPath($xmldoc);
$query = $xpath->query('/mainXML/items/item[#id = "100"]');
$create_new_node = false;
if($query->length == 0)
{
$item = $xmldoc->createElement('item');
$create_new_node = true;
}
else
{
$item = $query->item(0);
}
$item->setAttribute('id', '100');
$item->setAttribute('category', 'Fitness');
$item->setAttribute('name', 'Basketball');
$item->setAttribute('url', 'http://google.com');
$item->setAttribute('description', 'This is a description');
$item->setAttribute('price', '899');
if($create_new_node)
{
$xmldoc->getElementsByTagName('items')->item(0)->appendChild($item);
}
$xmldoc->save('ex.xml');
I haven't used this functionality but looks like a good match for DOMDocument: Get Element By ID
If you get a matching element, edit it, and if not, post away.
If you have a DTD for this xml file that specifies that the "id" attribute is an ID type (i.e. its value is unique in a document and uniquely identifies its element), then you can use DOMDocument::getElementById().
Most likely, however, you do not have a DTD. In this case, you should just use XPath:
$xmldoc = new DOMDocument();
$xmldoc->load('ex.xml');
$xpath = new DOMXPath($xmldoc);
$results = $xpath->query('//items/item[#id=100][0]');
if (!$results->length) {
$item= $xmldoc->createElement('item');
$item->setAttribute('id', '100');
$item->setAttribute('category', 'Fitness');
$item->setAttribute('name', 'Basketball');
$item->setAttribute('url', 'http://google.com');
$item->setAttribute('description', 'This is a description');
$item->setAttribute('price', '899');
$xmldoc->getElementsByTagName('items')->item(0)->appendChild($item);
$xmldoc->save('ex.xml');
}
You should also consider using SimpleXML for this task. The way this xml is structured and manipulated would probably be better-suited to SimpleXML.
I am looking for something equivalent to this:
$e= xmlwriter_open_uri("test.xml");
....
print htmlentities(xmlwriter_output_memory($e));
now this print allows to display whats in the xml list into a table.
But my with my simple xml (combined with $dom for formatting) i have no idea how to display this. Although this generates the proper output i wish into the xml how do i display the xml below? Something similar to a print or?
The purpose is to display the values of the xml into a table.
$dom = new DOMDocument('1.0');
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
$xml = new SimpleXMLElement('<test></test>');
$one= $xml->addChild('enemy', 'yes');
$two= $xml->addChild('friend', 'maybe');
$dom->loadXML($xml->asXML());
$dom->save('test.xml');
Regards
You don't need to stringify (technical term!) the SimpleXMLElement to load it into a DOMDocument, in fact that's a terrible idea (though, you're forgiven).
$xml = new SimpleXMLElement('<test></test>');
$one= $xml->addChild('enemy', 'yes');
$two= $xml->addChild('friend', 'maybe');
// Get the DOMDocument associated with this XML
$dom = dom_import_simplexml($xml)->ownerDocument;
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;
echo $dom->saveXML(); // or echo htmlentities($dom->saveXML()) if you really must
More info about retrieving a DOMElement (and its DOMDocument) from a SimpleXMLElement can be found in the docs for dom_import_simplexml().