I want to copy the content from the PHP file into XML:
$channel = "15";
$start = "20180124021100 +0200";
$stop = "20180124031500 +0200";
$title = "news";
$desc = "show number 50";
I need to do later loop and add more channels, so I do not want only to edit the XML,
I want to open the XML file and add the PHP content to the page in a specific location:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<channel id="1">
<display-name lang="he">1</display-name>
</channel>
// I want the content to stick to the end
<programme start="$start" stop="$stop" channel="$number">
<title lang="he">$title</title>
<desc lang="he">$desc</desc>
</programme>
// And that the next loop will be the same, will paste the content to the end as well:
<programme start="$start" stop="$stop" channel="$number">
<title lang="he">$title</title>
<desc lang="he">$desc</desc>
</programme>
Rather than using strings to build the XML you ought to look at one of the existing classes to do all the heavy lifting - they are after all designed for this purpose. Looking at the xml in the question it does not appear valid XML as there is no root node for the content. The following ought to help get started.
<?php
$channel = "15";
$start = "20180124021100 +0200";
$stop = "20180124031500 +0200";
$title = "news";
$desc = "show number 50";
/* file path to saved xml document */
$xmlfile= __DIR__ . '/xmlfile.xml';
/* whether to display XML in browser or simply save */
$displayxml=true;
/* create DOMDocument and set args */
$dom=new DOMDocument('1.0','utf-8');
$dom->standalone=true;
$dom->formatOutput=true;
$dom->recover=true;
$dom->strictErrorChecking=true;
/* a ROOT node for the document */
$rootnode='programmes';
/* if the file already exists, open it */
if( realpath( $xmlfile ) ){
$dom->load( $xmlfile );
$root=$dom->getElementsByTagName( $rootnode )->item(0);
} else {
/* File does not exist, create root elements & content */
$root=$dom->createElement( $rootnode );
$dom->appendChild( $root );
$ochan=$dom->createElement('channel');
$ochan->setAttribute('id',1);
$root->appendChild( $ochan );
$odisp=$dom->createElement('display-name',1);
$odisp->setAttribute('lang','he');
$ochan->appendChild( $odisp );
}
/* process variables for new record/programme */
if( isset( $channel, $start, $stop, $title, $desc ) ){
$oprog=$dom->createElement('programme');
$root->appendChild( $oprog );
$oprog->setAttribute('start',$start);
$oprog->setAttribute('end',$stop);
$oprog->setAttribute('channel',$channel);
$otitle=$dom->createElement('title',$title);
$oprog->appendChild($otitle);
$otitle->setAttribute('lang','he');
$odescr=$dom->createElement('desc',$desc);
$oprog->appendChild($odescr);
$odescr->setAttribute('lang','he');
}
/* save the file */
$dom->save( $xmlfile );
/* if you want to see the generated xml in the browser */
if( $displayxml ){
header('Content-Type: application/xml');
echo $dom->saveXML();
}
?>
SimpleXML for PHP is fairly simple to use, either for reading or writing XML. In this question How to convert array to SimpleXML you can find a number of suggestions on using SimpleXML in PHP. Documentation can be found here: https://secure.php.net/manual/en/book.simplexml.php
Related
I have wrote this code in PHP to compile an XML file with parameters that are in URL.
But when the XML file is already created instead of adding the new data at the bottom of file inside the root element, overwrite it and delete all old data.
Where is the problem?
I have seen some examples online but I can't figure out how fix it.
I need to verify if file already exist and then add the element?
Or I need to read it and then add again the old elements and new?
I don't know very well dom so I can't figure out
<?php
$FOL = $_GET["FOL"];
$NUM = $_GET["NUM"];
$DAT = $_GET["DAT"];
$ZON = $_GET["ZON"];
$TIP = $_GET["TIP"];
$COM = $_GET["COM"];
$dom = new DOMDocument();
$dom->encoding = 'utf-8';
$dom->xmlVersion = '1.0';
$dom->formatOutput = true;
$xml_file_name = "$NUM.xml";
$xmlString = file_get_contents($xml_file_name);
$dom->loadXML($xmlString);
$loaded_xml = $dom->getElementsByTagName('Territorio');
$territorio_node = $dom->createElement('Territorio');
$child_node_NOM = $dom->createElement('NOM', "$NOM");
$territorio_node->appendChild($child_node_NOM);
$child_node_NUM = $dom->createElement('NUM', "$NUM");
$territorio_node->appendChild($child_node_NUM);
$child_node_DAT = $dom->createElement('DAT', "$DAT");
$territorio_node->appendChild($child_node_DAT);
$child_node_ZON = $dom->createElement('ZON', "$ZON");
$territorio_node->appendChild($child_node_ZON);
$dom->appendChild($territorio_node);
$child_node_TIP = $dom->createElement('TIP', "$TIP");
$territorio_node->appendChild($child_node_TIP);
$child_node_COM = $dom->createElement('COM', "$COM");
$territorio_node->appendChild($child_node_COM);
$dom->appendChild($territorio_node);
$dom->save($FOL.'/'.$xml_file_name);
echo "$xml_file_name creato correttamente";
?>
as per the comment: You should check that the root node of the XML file exists before calling createElement to generate a new one. To do that you can call getElementsByClassName and test whether the first entry is empty
ie: empty( $dom->getElementsByTagName('Territorio')[0] ) sort of thing...
If the root node exists we use that, otherwise add a new root to the document and continue
// check that the querystring has all the required parameters
if( isset(
$_GET['FOL'],
$_GET['NUM'],
$_GET['DAT'],
$_GET['ZON'],
$_GET['TIP'],
$_GET['COM']
)){
// the filename is generated from one of the querystring parameters
// - here the directory used is the same as the script running
$file=sprintf( '%s/%s.xml', __DIR__, $_GET['NUM'] );
// create an empty file if it does not exist
if( !file_exists( $file )){
file_put_contents( $file, '' );
}
clearstatcache();
// create the DOMDocument and set various options
libxml_use_internal_errors( true );
$dom=new DOMDocument('1.0','utf-8');
$dom->strictErrorChecking=false;
$dom->preserveWhiteSpace=true;
$dom->formatOutput=true;
$dom->recover=true;
// load the XML file directly rather than loading a string read from the original file.
$dom->load( $file );
// The ROOT node of the document ... does it exist? if not, create it and add to the DOM.
$root=$dom->getElementsByTagName('Territorio')[0];
if( empty( $root ) ){
$root=$dom->createElement('Territorio');
$dom->appendChild( $root );
}
// I added this part so that you can distinguish easily new elements
$record=$dom->createElement('record');
$root->appendChild( $record );
// add all the querystring parameters within the new record.
$record->appendChild( $dom->createElement('NUM', $_GET["NUM"] ) );
$record->appendChild( $dom->createElement('DAT', $_GET["DAT"] ) );
$record->appendChild( $dom->createElement('ZON', $_GET["ZON"] ) );
$record->appendChild( $dom->createElement('TIP', $_GET["TIP"] ) );
$record->appendChild( $dom->createElement('COM', $_GET["COM"] ) );
$record->appendChild( $dom->createElement('FOL', $_GET["FOL"] ) );
$dom->save( $file );
}
An example of the XML generated:
<?xml version="1.0" encoding="utf-8"?>
<Territorio>
<record>
<NUM>wibble</NUM>
<DAT>25/10/2022</DAT>
<ZON>europe</ZON>
<TIP>total</TIP>
<COM>94</COM>
<FOL>234</FOL>
</record>
<record>
<NUM>wibble</NUM>
<DAT>26/10/2022</DAT>
<ZON>europe</ZON>
<TIP>total</TIP>
<COM>96</COM>
<FOL>238</FOL>
</record>
</Territorio>
In the original code the file is saved to a location defined by another parameter in the querystring ( only just noticed that afterwards ) so rather than
$file=sprintf( '%s/%s.xml', __DIR__, $_GET['NUM'] );
you would likely want to do:
$file=sprintf( '%s/%s.xml', $_GET['FOL'], $_GET['NUM'] );
The root node of an XML document is called document element and here is an property for it. So you can just check if it is undefined. However an document can have only a single document element, so will need to modify the structure of your XML - for example add a "Territori" document element.
Do not use the second argument of the createElement() method or the $nodeValue property. Their escaping is broken - try adding a value with an &. Use $textContent or add a text node.
In modern DOM you can even just append() a string.
$NUM = "NUM";
$DAT = "DAT";
$ZON = "ZON";
$document = new DOMDocument('1.0', 'UTF-8');
// let the parser ignore existing indents
$document->preserveWhiteSpace = false;
$document->loadXML(getXMLString());
// fetch or create document element
$territori = $document->documentElement
?? $document->appendChild($document->createElement('Territori'));
// create/append an item element
$territori
->appendChild(
$territorio = $document->createElement('Territorio')
);
// create/append an element and set its text content
$territorio
->appendChild($document->createElement('NUM'))
->textContent = $NUM;
// create/append an element with a text child node
$territorio
->appendChild($document->createElement('DAT'))
->appendChild($document->createTextNode($DAT));
// create/append an element and a string (DOM Level 3)
$territorio
->appendChild($document->createElement('ZON'))
->append((string)$ZON);
// enable output formatting
$document->formatOutput = true;
echo $document->saveXML();
function getXMLString() {
return <<<'XML'
<?xml version="1.0"?>
<Territori>
<Territorio>
<NUM>NUM</NUM>
<DAT>DAT</DAT>
<ZON>DAT</ZON>
</Territorio>
</Territori>
XML;
}
For a more flexible approach to fetch nodes use Xpath expressions. Here is an example that checks if an Territorio with a specific NUM value exists:
$document = new DOMDocument('1.0', 'UTF-8');
$document->loadXML(getXMLString());
$xpath = new DOMXpath($document);
if ($xpath->evaluate('count(//Territorio[NUM="NUM"]) > 0')) {
echo "Node exists";
}
I'm trying to update and add node to a xml file. Currently I can create and overwrite the file with my new node, however what I need to do is do to is add the new node to the existing file (.xml) and I am exhausted. I am new to php (I've tried all every code on this site and this is my current code can't be added here ... please Help
$doc = new DOMDocument;
// Load the XML
///$doc->loadXML("<root/>");
//---- ///$xml = new Document;
///$xml ->loadXML($xml);
//$xml = simplexml_load_file("pole.xml");
$title = $_POST["title"];
$xml = <<<XML <item> <title>$title</title> </item> XML;
$xml = new Document;
$xml ->loadXML($xml);
$xml ->appendXML($xml);
$xml = new SimpleXMLElement($xml);
echo $xml->saveXML('pole.xml');
I can offer no advice for using SimpleXML but as the above does attempt at using DOMDocument perhaps the following simple example will be of use.
$filename='pole.xml';
# Stage 1
# -------
// Create an instance of DOMDocument and then
// generate whatever XML you need using DOMDocument
// and save.
libxml_use_internal_errors( true );
$dom=new DOMDocument('1.0','utf-8');
$dom->formatOutput=true;
$dom->preserveWhiteSpace=true;
$root=$dom->createElement('Root');
$dom->appendChild( $root );
$item=$dom->createElement('Item');
$title=$dom->createElement('title','Hello World');
$item->appendChild( $title );
$root->appendChild( $item );
$dom->save( $filename );
$dom=null;
This yields the following XML:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item>
<title>Hello World</title>
</Item>
</Root>
To then modify the XML file you have created or downloaded etc:
# Stage 2
# -------
// A new instance of DOMDocument is NOT strictly necessary here
// if you are continuing to work with the generated XML but for the purposes
// of this example assume stage 1 and stage 2 are done in isolation.
// Find the ROOT node of the document and then add some more data...
// This simply adds two new simple nodes that have various attributes
// but could be considerably more complex in structure.
$dom=new DOMDocument;
$dom->formatOutput=true;
$dom->preserveWhiteSpace=false;
$dom->load( $filename );
# Find the Root node... !!important!!
$root=$dom->getElementsByTagName('Root')->item(0);
# add a new node
$item=$dom->createElement('Banana','Adored by monkeys');
$attributes=array(
'Origin' => 'Central America',
'Type' => 'Berry',
'Genus' => 'Musa'
);
foreach( $attributes as $attr => $value ){
$attr=$dom->createAttribute( $attr );
$attr->value=$value;
$item->appendChild( $attr );
}
#ensure that you add the new node to the dom
$root->appendChild( $item );
#new node
$item=$dom->createElement('Monkey','Enemies of Bananas');
$attributes=array(
'Phylum' => 'Chordata',
'Class' => 'Mammalia',
'Order' => 'Primates'
);
foreach( $attributes as $attr => $value ){
$attr=$dom->createAttribute( $attr );
$attr->value=$value;
$item->appendChild( $attr );
}
$root->appendChild( $item );
$dom->save( $filename );
$dom=null;
This modifies the XML file and yields the following:
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Item>
<title>Hello World</title>
</Item>
<Banana Origin="Central America" Type="Berry" Genus="Musa">Adored by monkeys</Banana>
<Monkey Phylum="Chordata" Class="Mammalia" Order="Primates">Enemies of Bananas</Monkey>
</Root>
The XML file contains a 1000 product entries. Some of them for the country Portugal.
I would like to get only the products where the country is Portugal and write that information into a new XML file onto my server.
How would I do that in PHP?
The XML content structure:
<products>
<product ID="38450">
<name>Aparthotel Alfagar</name>
<price currency="EUR">239.00</price>
<URL>https://website.com/</URL>
<images>
<image>https://website.com/1.jpg</image>
<image>https://website.com/2.jpg</image>
<image>https://website.com/3.jpg</image>
</images>
<description>
<![CDATA[<p>some text</p>]]>
</description>
<categories/>
<properties>
<property name="country">
<value>Portugal</value>
</property>
<property name="lowestPrice">
<value>239.00</value>
</property>
<property name="lowestPriceDate">
<value>13-01-2020</value>
</property>
</properties>
<variations/>
</product>
<!-- more product entries -->
My approuch started out as this:
<?php
// Define source
$source_url = 'https://website.net/?encoding=utf-8&type=xml&id=';
// Define target
$file_url = '/home/website/public_html/media/';
$file_ext = '.xml';
// Load data
$array = simplexml_load_file($source_url.'654321');
// Filter data
$results_portugal = '';
foreach($array->product->properties->property->value['Portugal'] as $results) {
}
// Create datafiles
copy ($results_portugal,$file_url.'portugal'.$file_ext);
Obiously I got stuck pretty soon. Can anyone help me out please? Many thanks in advance!
You can fetch a part of an XML in SimpleXML or DOM using XPath expressions:
$products = new SimpleXMLElement($xml);
var_dump(
count(
$products->xpath('//product[properties/property[#name = "country"]/value = "Portugal"]')
)
);
var_dump(
count(
$products->xpath('//product[properties/property[#name = "country"]/value = "Spain"]')
)
);
However here is no "nice" way to copy nodes in SimpleXML. DOM allows that:
// create source document and load XML
$source = new DOMDocument();
$source->loadXML($xml);
$xpath = new DOMXpath($source);
// create target document and append root node
$target = new DOMDocument();
$target->appendChild($target->createElement('products'));
$expression = '//product[properties/property[#name = "country"]/value = "Portugal"]';
// iterate filtered nodes
foreach ($xpath->evaluate($expression) as $product) {
// import node into target document and append
$target->documentElement->appendChild(
$target->importNode($product, TRUE)
);
}
echo $target->saveXML();
For really large XMLs you need to use XMLReader/XMLWriter. They allow you to load only a part of the XML file into memory. Originally here is no easy way to copy nodes but I added this to FluentDOM.
// Create the target writer and add the root element
$writer = new \FluentDOM\XMLWriter();
$writer->openUri('php://stdout');
$writer->setIndent(2);
$writer->startDocument();
$writer->startElement('products');
// load the source into a reader
$reader = new \FluentDOM\XMLReader();
$reader->open('data://text/plain;base64,'.base64_encode($xml));
// iterate the product elements - the iterator expands them into a DOM node
foreach (new FluentDOM\XMLReader\SiblingIterator($reader, 'product') as $product) {
/** #var \FluentDOM\DOM\Element $product */
// validate country property
if ($product('properties/property[#name = "country"]/value = "Portugal"')) {
// write expanded node to the output
$writer->collapse($product);
}
}
$writer->endElement();
$writer->endDocument();
I have an xml document which I transform, view in the browser and save to a directory.
I would like to add a linked stylesheet to the saved version of the file. I've tried str_replace as below (might be incorrect usage) and also using xsl processing instruction.
xsl processing instruction worked to a degree, if you view source in the browser, you would see the stylesheet link, however it would not save this information to the saved file!!
All I want to do is take the raw xml file, transform it with the stylesheet, save it to a directory and append the xsl stylesheet to the header of the new file, so when the newly saved xml file is opened in the browser, the stylesheet is applied automatically. Hope this makes sense!!
My code is below.
//write to the file
$id = $_POST['id'];//xml id
$path = 'xml/';//send to xml directory
$filename = $path. $id . ".xml";
$header = str_replace('<?xml version="1.0" encoding="UTF-8"?>', '<?xml version="1.0" encoding="UTF-8" ?><?xml-stylesheet type="text/xsl" href="../foo.xsl"?>');
$article->asXML($filename);//saving the original xml as new file with posted id
$xml = new DOMDocument;
$xml->load($filename);
$xsl = new DOMDocument;
$xsl->load('insert.xsl');
$proc = new XSLTProcessor;
$proc->importStyleSheet($xsl);
echo $proc->transformToXML($xml);
Thanks in advance!
You could use the <xsl:processing-instruction/> tag in the XSLT-file to add the stylesheet link.
<xsl:processing-instruction name="xml-stylesheet">
type="text/xsl" href="../foo.xsl"
</xsl:processing-instruction>
This would produce:
<?xml-stylesheet type="text/xsl" href="../foo.xsl"?>
Alternativly, you could do it with a DOMDocument.
$newXml = $proc->transformToXML($xml);
// Re-create the DOMDocument with the new XML
$xml = new DOMDocument;
$xml->loadXML($newXml);
// Find insertion-point
$insertBefore = $xml->firstChild;
foreach($xml->childNodes as $node)
{
if ($node->nodeType == XML_ELEMENT_NODE)
{
$inertBefore = $node;
break;
}
}
// Create and insert the processing instruction
$pi = $xml->createProcessingInstruction('xml-stylesheet', 'type="text/xsl" href="../foo.xsl"');
$xml->insertBefore($pi, $insertBefore);
echo $xml->saveXML();
I am using XPath to query an xml document and then copy found nodes to a separate results xml file, however the problem I am having is that the XML declaration at the top gets added each time a new node is added to the results file.
Here is my php code:
$allEventsForVenue = $xPath->query(
"/Ticket/EventsPoints/Event[contains(PerformanceName,'$searchParam')]"
);
foreach ($allEventsForVenue as $event) {
$dstDom = new DOMDocument('1.0', 'utf-8');
$dstDom->appendChild($dstDom->createElement('EventsPoints'));
$dstDom->documentElement->appendChild($dstDom->importNode($event, true));
//echo $dstDom->saveXml();
$dstDom->formatOutput = true;
$strxml .= $dstDom->saveXML();
$handle = fopen("/var/www/html/xml/searchresults$searchID.xml", "w");
fwrite($handle, $strxml);
fclose($handle);
}
Invalid XML File (two xml declarations in the one file):
->> <?xml version="1.0" encoding="utf-8"?>
<EventsPoints>
<Event ID="17">
<PerformanceName>Bressie</PerformanceName>
<PresaleTime/>
<PriceRange>17 - 17</PriceRange>
<VenueID ID="19"/>
</Event>
</EventsPoints>
->> <?xml version="1.0" encoding="utf-8"?>
<EventsPoints>
<Event ID="180">
<PerformanceName>U2</PerformanceName>
<PriceRange>20 - 20</PriceRange>
<VenueID ID="198"/>
</Event>
</EventsPoints>
You are creating new DOMDocument instance in loop. Also you are writing in each iteration. This is making new XML document on each iteration and its get appended in your xml file. What you need to do is, loop the XML generation part. Not the whole thing.
If you loop only the appendChild parts, your problem will be solved.
$dstDom = new DOMDocument('1.0', 'utf-8');
$dstDom->formatOutput = true;
foreach ($allEventsForVenue as $event) {
$dstDom->appendChild($dstDom->createElement('EventsPoints'));
$dstDom->documentElement->appendChild($dstDom->importNode($event, true));
}
file_put_contents("/var/www/html/xml/searchresults$searchID.xml", $dstDom->saveXML());
DomDocument lets you output a specific node without the xml declaration.
Just include a node in saveXML, like saveXML($node), with node being an XMLNode type object.
With this method I think you can bypass the whole "create sub-xmldocument/import node" thing, by outputting directly the desired node in a string.
use something like this
enter <?php
// Continued from example XML above.
/* Search for <a><b><c> */
$result = $xml->xpath('/a/b/c');
while(list( , $node) = each($result)) {
echo $node->asXML();
}
?>