How to add a child in xml without overwrite using php dom? - php

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";
}

Related

Php:Proble when i convert in xml

I want my arElemt(gurl and gname) put in . Example and problem2 = when i write g:url or g:name = Error... php7.2* and now example now now i have this construction
-rss
---title
---link
---description
---gurl
---gname
i want now i have this construction
-rss
---title
---link
---description
---gurl
---gname
---gurl
---gname
---gurl
---gname i want
-rss
---title
---link
---description
---item
-----gurl
-----gname
---item
-----gurl
-----gname
---item
-----gurl
-----gname
---item
-----gurl
-----gname
header("Content-type: text/xml; charset=utf-8");
$dom = new DOMDocument('1.0','utf-8');
$root = $dom->createElement('rss');
$dom->appendChild($root);
$title = $dom->createElement('title', 'test');
$root->appendChild($title );
$link = $dom->createElement('link', 'test');
$root->appendChild($link );
$description = $dom->createElement('description', 'test');
$root->appendChild($description );
$root = $item->createElement('item');
while($arElement = $rsElements->GetNext())
{
$url = $dom->createElement("gurl", $surl.$arElement[DETAIL_PAGE_URL]);
$item->appendChild($url );
$name = $dom->createElement("gname", $arElement[NAME]);
$root->appendChild($name );
}
echo $dom->saveXML();
$dom->save($file_name); // save as file
Here is a big difference between gurl and g:url. gurl is not an valid RSS tag afaik. g:url is an url element inside a defined namespace.
The g from g:url is a namespace prefix. It references a namespace definition. Look for a xmlns:g attribute in examples or for the namespace URI in the documentation of the format. The g is an alias for the value of that attribute. A parser resolves that to the URI internally. All the following nodes can be read as {urn:example:namespace}url.
<g:url xmlns:g="urn:example:namespace"/>
<g2:url xmlns:g2="urn:example:namespace"/>
<url xmlns="urn:example:namespace"/>
RSS itself is just wellformed XML, it uses no namespace. But it can contain other XML formats that use namespaces (MediaRSS, ...).
To create an element with a namespace use the method DOMDocument::createElementNS(). This will automatically add the namespace definition if needed. However if do not use the namespace for the document element it will be added multiple times. You can set the namespace definition as an attribute of the reserved XMLNS namespace.
$data = ['one', 'two'];
// the namespace for namespace definitions
const XMLNS_XMLNS = 'http://www.w3.org/2000/xmlns/';
// namespace referenced by prefix g?
const XMLNS_G = 'urn:example:namespace';
$document = new DOMDocument('1.0','utf-8');
$rss = $document->appendChild(
$document->createElement('rss')
);
// add the namespace definition to the document element
$rss->setAttributeNS(XMLNS_XMLNS, 'xmlns:g', XMLNS_G);
// create + append element node, set its text content
$rss->appendChild(
$document->createElement('title')
)->textContent = 'test';
foreach ($data as $value) {
$item = $rss->appendChild(
$document->createElement('item')
);
// create and append an element with the namespace
$item->appendChild(
$document->createElementNS(XMLNS_G, 'g:url')
)->textContent = 'http://example.com/page?'.$value;
}
$document->formatOutput = TRUE;
echo $document->saveXML();
Output:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:g="urn:example:namespace">
<title>test</title>
<item>
<g:url>http://example.com/page?one</g:url>
</item>
<item>
<g:url>http://example.com/page?two</g:url>
</item>
</rss>
Hint 1: DOMNode::appendChild() returns the appended node. It is possible to nest the create call.
Hint 2: DOMNode::$textContent allows to read/write the text content of a node and escapes properly.
const XMLNS_XMLNS = 'http://www.w3.org/2000/xmlns/';
// namespace referenced by prefix g?
const XMLNS_G = 'urn:example:namespace';
$document = new DOMDocument('1.0','utf-8');
$rss = $document->appendChild(
$document->createElement('rss')
);
// add the namespace definition to the document element
$rss->setAttributeNS(XMLNS_XMLNS, 'xmlns:g', XMLNS_G);
// create + append element node, set its text content
$rss->appendChild(
$document->createElement('title')
)->textContent = 'test';
while($arElement = $rsElements->GetNext())
{
$item = $rss->appendChild(
$document->createElement('item')
);
$item->appendChild(
$document->createElementNS(XMLNS_G, 'g:url')
)->textContent = $surl.$arElement["DETAIL_PAGE_URL"];
$item->appendChild(
$document->createElementNS(XMLNS_G, 'g:name')
)->textContent = $arElement["NAME"];
}

how to append node to parent node in xml file using php

i want to replace value of commented tags in xml file..how can i replace it using php ..given is file.xml
<SyncOpportunity_Input>
<SendConfirmationEmail/>
<UpdateToken/>
<CallingSystem>WS-TEST</CallingSystem>
<KeepLocking/>
<Error_spcCode/>
<EmailAddrOverwrite/>
<Opportunity>
//<IntegrationId>12</IntegrationId>
//<LoanWriterId>13</LoanWriterId>
//<Description>NA</Description>
<Applicant>
<integratedid1>28</integratedid1>
<FirstName>pri</FirstName>
<LastName>kj</LastName>
</Applicant>
<Property>
<integratedid>2</integratedid>
<Title>Mr</Title>
<DateOfBirth>1999-11-11</DateOfBirth>
</Property>
<Ownership>
<integratedid1>28</integratedid1>
<OwnershipPercentage>100</OwnershipPercentage>
</Ownership>
</Opportunity>
</SyncOpportunity_Input>
i dont want to create node each time.. i just want to replace the value of child node..as i am appending other tags dynamically using php.
here is my function.php file where I have appended other tags,where i have get tags and then created child nodes and the value of each tags is coming from our php form.As well each time we require to delete previous data.
$xml = new DOMDocument('1.0', 'utf-8');
$xml->formatOutput = true;
$xml->preserveWhiteSpace = false;
$nodesToDelete=array();
$pnodesToDelete=array();
//echo $fname;
$xml->load('file.xml');
$nodesToDelete=array();
$IntegrationId = rand(1,19);
$IntegrationId1 = rand(10,30);
$IntegrationId2 = rand(20,40);
$element = $xml->getElementsByTagName('Applicant')->item(0);
$pelement = $xml->getElementsByTagName('Property')->item(0);
$FirstName = $element->getElementsByTagName('FirstName')->item(0);
$LastName = $element->getElementsByTagName('LastName')->item(0);
//echo $timestamp;
//$oelement = $xml->getElementsByTagName('Opportunity');
$oelement = $xml->getElementsByTagName('Ownership')->item(0);;
$ownItem= $xml->createElement('Ownership');
$ownItem->appendChild($xml->createElement('integratedid1',$IntegrationId1 ));
$ownItem->appendChild($xml->createElement('OwnershipPercentage',100));
//$oItem->appendChild($xml->createElement('integratedid2',$IntegrationId2));
$newItem= $xml->createElement('Applicant');
$newItem->appendChild($xml->createElement('integratedid1',$IntegrationId1 ));
$newItem->appendChild($xml->createElement('FirstName', $_POST['fld_2260636']));
$newItem->appendChild($xml->createElement('LastName', $_POST['fld_4322743']));
$pnewItem= $xml->createElement('Property');
$pnewItem->appendChild($xml->createElement('integratedid',$IntegrationId ));
$pnewItem->appendChild($xml->createElement('Title', $_POST['fld_4755553']));
$pnewItem->appendChild($xml->createElement('DateOfBirth', $_POST['fld_1151367']));
//echo $newItem;
$xml->getElementsByTagName('Opportunity')->item(0)->appendChild($newItem);
$xml->getElementsByTagName('Opportunity')->item(0)->appendChild($pnewItem);
$xml->getElementsByTagName('Opportunity')->item(0)->appendChild($ownItem);
$nodesToDelete[]=$element;
$pnodesToDelete[]=$pelement;
$onodesToDelete[]=$oelement;

how do you add an attibute to root element of XML DOMDocument

i created a XML document using DOMDocument and simpleXML. I need to add an attibute to the root element.
Below is how I created the document and the element. You will note that although the document is initally created by DOMDocument, the child/user nodes are created with simple XML.
$dom = new DOMDocument('1.0','UTF-8');
/*** create the root element ***/
$root = $dom->appendChild($dom->createElement( "Feed" ));
/*** create the simple xml element ***/
$sxe = simplexml_import_dom( $dom );
/*** add a user node ***/
$firstChild = $sxe->addchild("FirstChild");
I tried adding the attibutes to the root like this:
$root = $dom->appendData($dom->createAttribute("extractDate",
"$now"));
but this does not work.
Using DOMDocument, you can set the attribute using the DOMElement::setAttribute method:
$dom = new DOMDocument('1.0','UTF-8');
$root = $dom->createElement("Feed");
$attr = $root->setAttribute("extractDate", "$now"); // <-- here
$dom->appendChild($root);
For SimpleXML, you'll need to use the SimpleXMLElement::addAttribute method:
$sxe = simplexml_import_dom($dom);
$sxe->addAttribute("extractDate", "$now"); // <-- here

domDocument's formatOutput property writes inline [duplicate]

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)));
}

How to add XSL line in XML created by PHP DOMDocument()?

In a nutshell: creating a xml document with PHP DOMDocument(). Need to add a line to the document.
Successfully creating an XML document with:
$doc = new DOMDocument();
$doc->formatOutput = true;
$r = $doc->createElement( "products" );
$doc->appendChild( $r );
while ( $row = mysql_fetch_array($res) )
{
$b = $doc->createElement( "product" );
//adding child elements.....
}
echo $doc->saveXML();
$doc->save("write.xml")
However, at the top of the xml document I need to link to an xls.
<?xml-stylesheet type="text/xsl" href="my.xsl"?>
How do I add that line in the PHP, to the dynamically generated xml?
Thanks.
If you check the manual for DOMDocument, there is actually an example to do exactly what you need in the createProcessingInstruction method, # http://www.php.net/manual/en/domdocument.createprocessinginstruction.php

Categories