How to update an xml file on filesystem conditionally - PHP - php

I have the xml file called fonts.xml located on my filesystem.
Goal:
I want to update the attribute <status> where name is "Aclonica" but I don't know how to do it conditionally.
XML:
<fonts>
<font>
<name>Aclonica</name>
<category>Aclonica</category>
<variants>100,bold</variants>
<status>active</status>
</font>
<font>
<name>Azeebe</name>
<category>Sans-serif</category>
<variants>100,bold,italic</variants>
<status>active</status>
</font>
</fonts>

You need to use DOMDocument class. This way you select data using normal if condition:
solution:
$a = $_POST['font']; // here value 'Aclonica' is assigned to $a
$dom = new DOMDocument();
$dom->load('c:/xampp/htdocs/cms/public/fonts/font.xml');
foreach ($dom->documentElement->childNodes as $node) {
// print_r($node); // >> uncomment for debug purposes
if($node->nodeType == 1) {
$name = $node->getElementsByTagName('name')->Item(0);
if($name->nodeValue == $a) { // >> IMPORTANT: here is the condition you need
$OldJobId = $node->getElementsByTagName('status')->Item(0);
if($OldJobId->nodeValue == 'active') {
$newelement = $dom->createElement('status','inactive');
$OldJobId->parentNode->replaceChild($newelement, $OldJobId);
}else{
$newelement = $dom->createElement('status','active');
$OldJobId->parentNode->replaceChild($newelement, $OldJobId);
}
}
}
}
$dom->save("c:/xampp/htdocs/cms/public/fonts/font.xml");

Related

XMLReader not reading cdata

I have a problem. I wrote this code but I can't read <![CDATA[Epsilon Yayınları]]>. Items with cdata, when I get them it's empty. Is there an alternative solution?
XML:
<urunler>
<urun>
<stok_kod>9789753314930</stok_kod>
<urun_ad><![CDATA[Kırmızı Erik]]></urun_ad>
<Barkod>9789753314930</Barkod>
<marka><![CDATA[Epsilon Yayınları]]></marka>
<Kdv>8,00</Kdv>
<satis_fiyat>9,5000</satis_fiyat>
<kat_yolu><![CDATA[Edebiyat>Hikaye]]></kat_yolu>
<resim>http://basaridagitim.com/images/product/9789753314930.jpg</resim>
<Yazar>Tülay Ferah</Yazar>
<Bakiye>2,00000000</Bakiye>
<detay><![CDATA[]]></detay>
</urun>
</urunler>
$xml = new XMLReader;
$xml->open(DIR_DOWNLOAD . 'xml/'.$xml_info['xml_file_name']);
$doc = new DOMDocument;
$product_data = array();
$i=0;
while ($xml->read() && $xml->name !== 'urun');
while ($xml->name === 'urun') { $i++;
$node = simplexml_import_dom($doc->importNode($xml->expand(), true));
var_dump($node->urun_ad); die();
Dump print:
object(SimpleXMLElement)#143 (1) {
[0]=>
object(SimpleXMLElement)#145 (0) {
}
}
It just comes down to how your printing out the value. If you change the var_dump to either of the following, you will get what your after...
//var_dump($node->urun_ad)
echo $node->urun_ad.PHP_EOL;
echo $node->urun_ad->asXML().PHP_EOL;
outputs...
Kırmızı Erik
<urun_ad><![CDATA[Kırmızı Erik]]></urun_ad>
One thing to note is that if you want to use the value in another method, you may have to cast it to a string (echo does this automatically). So the first one would be (for example)...
$urun_ad = (string)$node->urun_ad;

How to get a list of all html elements in PHP?

According to the documentation for DOMDocument::getElementsByTagName, I can call the function with "*" argument, and get a list of all HTML elements from some HTML code.
However, with the following code:
<?php
$dom = new DOMDocument();
$dom->loadHTML("<html><body><div>hello</div><div>bye</div></body></html>");
$nodes = $dom->getElementsByTagName("*");
foreach ($nodes as $node) {
$new_text= new DOMText($node->textContent."MODIFIED");
$node->removeChild($node->firstChild);
$node->appendChild($new_text);
}
$content = $dom->saveHTML();
echo $content;
?>
I get a list of only one element, and the result of execution of the code above is:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>hellobyeMODIFIED</html>
while I would expect something like this:
<html><body><div>helloMODIFIED</div><div>byeMODIFIED</div></body></html>
Shouldn't DOMDocument::getElementsByTagName method return a list of as many HTML elements as available in the HTML code?
Note: I need to create DOMText instances explicitly, because I need this to work in PHP 5.4. DOMNode::textContent is accessible for writing only from PHP 5.6
The DOMDocument::getElementsByTagName method actually returns all the tags, if the first argument is '*'. But your code replaces <body> tag (including all child nodes) with a text node at the first iteration.
Iterate the nodes, and modify only the nodes with nodeType property equal to XML_TEXT_NODE:
$nodes = $dom->getElementsByTagName('*');
foreach ($nodes as $node) {
for ($child = $node->firstChild; $child; $child = $child->nextSibling) {
if (! ($child->nodeType === XML_TEXT_NODE && trim($child->textContent))) {
continue;
}
// The textContent is writable since PHP 5.6.1
if (PHP_VERSION_ID >= 50601) {
$child->textContent .= 'MODIFIED';
continue;
}
// For older versions, create DOMText explicitly
$text = new DOMText($child->textContent . 'MODIFIED');
try {
if ($child->parentNode->replaceChild($text, $child))
$child = $text;
} catch (Exception $e) {
trigger_error("Failed to modify text '$child->textContent': "
. $e->getMessage(), E_USER_WARNING);
}
}
}
echo $dom->saveHTML();
Note, for PHP versions 5.6.1 and newer, you don't need to create DOMText instances explicitly, since the DOMNode::textContent property is accessible for read and write. So you can simply modify the text by assigning a string value to this property. Only make sure that the node has no child nodes other than XML_TEXT_NODE.
The code above checks if trim($child->textContent) is not empty, because the document may contain extra space characters (including newline), e.g.:
<div><!-- newline/spaces -->
<span>text</span><!-- newline/spaces -->
</div><!-- newline/spaces -->
This function 'DOMDocument::getElementsByTagName' returns a new instance of class DOMNodeList containing all the elements.
And it works fine:
<?php
$dom = new DOMDocument();
$dom->loadHTML("<html><body><div>hello</div><div>bye</div></body></html>");
$nodes = $dom->getElementsByTagName("*");
foreach ($nodes as $node) {
echo $node->tagName."<br />";
}
?>
it output all tags of your document.
Probably you need smth like:
<?php
$dom = new DOMDocument();
$dom->loadHTML("<html><body><div>hello</div><div>bye</div></body></html>");
$nodes = $dom->getElementsByTagName("*");
foreach ($nodes as $node) {
if ($node->tagName=='div'){
$node->nodeValue .= "new content";
}
}
$content = $dom->saveHTML();
echo htmlspecialchars($content);
?>
Try this:-
foreach($dom->getElementsByTagName('*') as $element ){
}

How to merge two xml arrays in a third array in php

I have two xml arrays, and i want to merge these arrays in a third array... the first xml struxture is
$current = '<forms id="frm16648">
<group ref="" id="tarascioheader" mode="block">
<label>
<![CDATA[Group (tarascioheader)]]>
</label> structure u
<select ref="" id="petorresp">
<label>
<![CDATA[Select (petorresp)]]>
</label>
</select>
and the 2nd array is
$old = '<forms id="frm16648">
<group ref="" id="tarascioheader" mode="block">
<label>
<![CDATA[abc]]>
</label>
</group>
</forms>':
</group>
</forms>';
from these xmls, i want to copy all the matching tags in the new array....
I am trying to do this by a recursive function which is....
function merge_xmls($current, $old)
{
$cxml = str_get_html($current);
$oxml = str_get_html($old);
do
{
$tt = $cxml->first_child();
if(!empty($tt) && !is_null($cxml->first_child()))
{
$x = $cxml->first_child();
$this->merge_xmls($x, $cxml, $oxml);
}
if(empty($tt))
{
$cid = $cxml->id;
$oid = $oxml -> find('#'.$cid);
if(!is_null($oid))
{
$cxml -> innerHTML = $oxml -> innerHTML;
}
}
$cxml = $cxml->next_sibling();
}
while(!empty($cxml) && !is_null($cxml));
}
From the pseudo code you've posted it looks like you want to copy over the children of one xml element to another. As I use a different parser, I to it a little differently, but the same:
Find all elements to copy into.
Find the element to copy from based on the one found to copy into.
Remove all children of the element to copy into.
Copy all children from into
I do it here with DOMDocument as it's a good fit for dedicated operations like such:
$doc = new DOMDocument();
$copyTo = $doc->createDocumentFragment();
$copyTo->appendXML($current);
$copyFrom = new DOMDocument();
$copyFrom->loadXML($old);
$xpath = new DOMXPath($copyFrom);
foreach (new DOMElementFilter($copyTo->childNodes, 'forms') as $form) {
$id = $form->getAttribute('id');
$expression = sprintf('(//*[#id=%s])[1]', xpath_string($id));
$copy = $xpath->query($expression)->item(0);
if (!$copy) {
throw new UnexpectedValueException("No element with ID to copy from \"$id\"");
}
dom_replace_children($copy, $form);
}
Output is as:
echo $doc->saveXML($doc->importNode($copyTo, TRUE));
and gives:
<forms id="frm16648">
<group ref="" id="tarascioheader" mode="block">
<label>
<![CDATA[abc]]>
</label>
</group>
</forms>
The helping routines here are:
function dom_remove_children(DOMElement $node)
{
while ($node->firstChild) {
$node->removeChild($node->firstChild);
}
}
function dom_replace_children(DOMElement $from, DOMElement $into)
{
dom_remove_children($into);
$doc = $into->ownerDocument;
foreach ($from->childNodes as $child) {
$into->appendChild($doc->importNode($child, TRUE));
}
}
Also DOMElementFilter class (via PHP DOM: How to get child elements by tag name in an elegant manner?) and there's the xpath_string() function (also as shown on Stackoverflow).
Hope this helps, the example works with your data for me this way: https://eval.in/59886

simplexml doesn't read CDATA

I want to grab data from a xml file from a remote location which contains CDATA information in all nodes as listed below.
I use the following PHP function to grab such information but it doesn't work and seems not to be able to catch CDATA tags from xml file.
the question is whether my piece of code is correct or not ? and if it's wrong can you suggest any php code to get requested information?
<Items>
<Item ID="1">
<Name>Mountain</Name>
<Properties>
<Property Code="feature"><![CDATA[<ul><li>sample text</li></ul>]]></Property>
<Property Code="SystemRequirements"><![CDATA[Windows XP/Windows Vista]]></Property>
<Property Code="Description" Type="plain"><![CDATA[sample text2]]></Property>
</Properties>
</Item>
<Items>
and this is my php code :
<?
function xmlParse($file, $wrapperName, $callback, $limit = NULL) {
$xml = new XMLReader();
if (!$xml->open($file)) {
die("Failed to open input file.");
}
$n = 0;
$x = 0;
while ($xml->read()) {
if ($xml->nodeType == XMLReader::ELEMENT && $xml->name == $wrapperName) {
while ($xml->read() && $xml->name != $wrapperName) {
if ($xml->nodeType == XMLReader::ELEMENT) {
//$subarray[]=$xml->expand();
$doc = new DOMDocument('1.0', 'UTF-8');
$simplexml = simplexml_import_dom($doc->importNode($xml->expand(), true));
$subarray[]=$simplexml;
}
}
if ($limit == NULL || $x < $limit) {
if ($callback($subarray)) {
$x++;
}
unset($subarray);
}
$n++;
}
}
$xml->close();
}
echo '<pre>';
function func1($s) {
print_r($s);
}
xmlParse('myfile.xml', 'Item', 'func1', 100);
When I print this object by print_r($s); I can't see CDATA in result !.
do you have any idea in order to retrieve CDATA context ?
Treat it like a string
$file = "1.xml";
$xml = simplexml_load_file($file);
foreach($xml->Item->Properties->children() as $properties) {
printf("%s", $properties);
}
Output
<ul><li>sample text</li></ul>
Windows XP/Windows Vista
sample text2
There is allways way to use DOMDocument to open xml files, for example:
$xmlFile = new DOMDocument();
$xmlFile->load(myfile.xml);
echo $xmlFile->getElementsByTagName('Property')->item(0)->nodeValue;

How to delete xml Dom document in php

I'd search for this problem and find some questions but they didn't mention to my error...
I'm trying to remove a child of my DOM document and when I type the $x->removeChild($key); function, nothing happend...
$xmlreq = new DOMDocument;
$xmlreq->loadXML($xmlStr);
$x = $xmlreq->getElementsByTagName('*');
foreach($x as $key)
{
if (substr($key->nodeValue,0,3)=="{{{" and substr($key->nodeValue,-3)=="}}}")
{
$field = explode("|",substr($key->nodeValue,3,strlen($key->nodeValue)-6));
if((int)$field[3]==0)
{
if(trim($_POST[$field[2]])=="")
{
$x->removeChild($key);
}else{
$key->nodeValue = trim($_POST[$field[2]]);
}
}elseif((int)$field[3]==1)
{
if(trim($_POST[$field[2]])=="")
{
$errors.="";
}else{
$key->nodeValue = trim($_POST[$field[2]]);
}
}else{
}
}
}
header("content-type: application/xml");
print $xmlreq->saveXml();
and this is my xml:
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<contact:check xmlns:contact="http://epp.nic.ir/ns/contact-1.0">
<contact:id>ghhg-ghgh</contact:id>
<contact:id>45</contact:id>
<contact:id>45</contact:id>
<contact:id>45</contact:id>
<contact:authInfo>
<contact:pw>1561651321321</contact:pw>
</contact:authInfo>
</contact:check>
</check>
<clTRID>TEST-12345</clTRID>
</command>
</epp>
and I want to delete one of <contact:id>45</contact:id>
Your loop does nothing since the outer conditional is looking for a node where nodeValue starts with {{{ and ends with }}}:
foreach($x as $key)
{
if (substr($key->nodeValue,0,3)=="{{{" and substr($key->nodeValue,-3)=="}}}")
Additionally, there's no removeChild() method in DOMNodeList. You probably want to fetch the node's parent first and call its removeChild() method instead.
A possible alternative:
$x = $xmlreq->getElementsByTagName('*');
$remove = TRUE;
foreach($x as $key)
{
if( $key->nodeName=='contact:id' && $key->nodeValue=='45' ){
if($remove){
$key->parentNode->removeChild($key);
$remove = FALSE;
}
}
}

Categories