simplexml doesn't read CDATA - php

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;

Related

Delete XML node with SimpleXML, PHP

I'm trying to delete XML node with PHP (SimpleXML).
This is my XML:
<?xml version="1.0"?>
<items>
<a>
<name>A1</name>
<b>
<title>Item1</title>
<url>item1</url>
</b>
<b>
<title>Item2</title>
<url>item2</url>
</b>
<b>
<title>Item3</title>
<url>item3</url>
</b>
</a>
<a>
<name>A2</name>
<b>
<title>Item1</title>
<url>item1</url>
</b>
</a>
</items>
and this is my PHP code:
<?php
$xml = simplexml_load_file($_GET["xml"]);
$sxe = new SimpleXMLElement($xml->asXML());
$ID = $_GET["ID"];
$i = -1;
$num = $_GET["num"];
foreach ($sxe->children() as $var) {
if ($var == $ID) {
foreach ($var->children() as $data) {
if ($data == "link") {
$i++;
if ($i == $num) {
if ( ! empty($sxe)) {
unset($sxe[0]);
}
}
}
}
}
}
$sxe->asXML($_GET["xml"]);
?>
This code looks for with data of $ID (for example, $ID="A1"). The node it looks to delete is a node (with its and ), which is the #$num node.
Example: if $ID="A1" and $num=1,
it needs to delete the node with the title "Item2" and url "item2".
What am I doing wrong?
Thanks!
Use xpath to find a node. With your example it will be
//a[name="A1"]/b[2]
and use DomDocument method removeChild to change xml
$sxe = simplexml_load_string($xml);
$node = $sxe->xpath('//a[name="'. $ID .'"]/b['. $num .']');
$dom=dom_import_simplexml($node[0]);
$dom->parentNode->removeChild($dom);
echo $sxe->asXML();

How to update an xml file on filesystem conditionally - 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");

Get specific tag with XMLReader in PHP

I have the following XML-structure in my XML file (it's not the whole XML-file, only a part of it):
<?xml version="1.0" encoding="utf-8"?>
<extensions>
<extension extensionkey="fp_product_features">
<downloadcounter>355</downloadcounter>
<version version="0.1.0">
<title>Product features</title>
<description/>
<downloadcounter>24</downloadcounter>
<state>beta</state>
<reviewstate>0</reviewstate>
<category>plugin</category>
<lastuploaddate>1142878270</lastuploaddate>
<uploadcomment> added related features</uploadcomment>
</version>
</extension>
</extensions>
The file is too big for SimpleXML, so I'm using XMLReader. I have a switch that checks for the XML-tags and their content:
while ($xmlReader->read()) {
if ($xmlReader->nodeType == XMLReader::ELEMENT) {
switch ($xmlReader->name) {
case "title" :
$xmlReader->read();
$foo = $xmlReader->value;
//Do stuff with the value
break;
case "description":
$xmlReader->read();
$bar = $xmlReader->value;
//Do stuff with the value
break;
case "downloadcounter" :
$xmlReader->read();
$foobar = $xmlReader->value;
//Do stuff with the value
break;
case "state" :
$xmlReader->read();
$barfoo = $xmlReader->value;
//Do stuff with the value
break;
//Repeat for other tags
}
}
}
The problem here is that there are two <downloadcounter> tags. The one beneath <extension> and the one beneath <version>. I need the one beneath <version>, but the code in my switch is giving me the one beneath <extension>. All the other cases are giving me the right information.
I have thought about some solutions. Maybe there is a way where I can specify that XMLReader only reads the tag after <description>? I've been using the $xmlReader->read() function multiple times in one case, but that didn't help.
I'm very new to this, so maybe it is not the right the way to do it, but if anyone can point me in the right direction, it would be much appreciated.
Thanks in advance!
Ok, some notes on this...
The file is too big for SimpleXML, so I'm using XMLReader.
That would mean that loading the XML file with SimpleXML reaches PHP's memory_limit, right?
Alternatives would be to stream or chunk read the XML file and process the parts.
$xml_chunk = (.... read file chunked ...)
$xml = simplexml_load_string($xml_chunk);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
But working with XMLReader is fine!
Maybe there is a way where I can specify that XMLReader only reads the
tag after ?
Yes, there is. Like "i alarmed alien" pointed out: if you work with DomDocument, you can use an Xpath query to reach the exact (node|item|element) you want.
$dom = new DomDocument();
$dom->load("tooBig.xml");
$xp = new DomXPath($dom);
$result = $xp->query("/extensions/extension/version/downloadcounter");
print $result->item(0)->nodeValue ."\n";
For more examples see the PHP manual: http://php.net/manual/de/domxpath.query.php
If you want to stick to XMLReader:
The XMLReader extension is an XML Pull parser. The reader is going forward on the document stream, stopping on each node on the way. This explains why you get the first from beneath the tag, but not the one beneath .
This makes iterations hard, because lookahead and stuff is not really possible without re-reading.
DEMO http://ideone.com/Oykfyh
<?php
$xml = <<<'XML'
<?xml version="1.0" encoding="utf-8"?>
<extensions>
<extension extensionkey="fp_product_features">
<downloadcounter>355</downloadcounter>
<version version="0.1.0">
<title>Product features</title>
<description/>
<downloadcounter>24</downloadcounter>
<state>beta</state>
<reviewstate>0</reviewstate>
<category>plugin</category>
<lastuploaddate>1142878270</lastuploaddate>
<uploadcomment> added related features</uploadcomment>
</version>
</extension>
</extensions>
XML;
$reader = new XMLReader();
$reader->open('data:/text/plain,'.urlencode($xml));
$result = [];
$element = null;
while ($reader->read()) {
if($reader->nodeType === XMLReader::ELEMENT)
{
$element = $reader->name;
if($element === 'extensions') {
$result['extensions'] = array();
}
if($element === 'extension') {
$result['extensions']['extension'] = array();
}
if($element === 'downloadcounter') {
if(!is_array($result['extensions']['extension']['version'])) {
$result['extensions']['extension']['downloadcounter'] = '';
} /*else {
$result['extensions']['extension']['version']['downloadcounter'] = '';
}*/
}
if($element === 'version') {
$result['extensions']['extension']['version'] = array();
while ($reader->read()) {
if($reader->nodeType === XMLReader::ELEMENT)
{
$element = $reader->name;
$result['extensions']['extension']['version'][$element] = '';
}
if($reader->nodeType === XMLReader::TEXT)
{
$value = $reader->value;
$result['extensions']['extension']['version'][$element] = $value;
}
}
}
}
if($reader->nodeType === XMLReader::TEXT)
{
$value = $reader->value;
if($element === 'downloadcounter') {
if(!is_array($result['extensions']['extension']['version'])) {
$result['extensions']['extension']['downloadcounter'] = $value;
}
if(is_array($result['extensions']['extension']['version'])) {
$result['extensions']['extension']['version']['downloadcounter'] = $value;
}
}
}
}
$reader->close();
echo var_export($result, true);
Result:
array (
'extensions' =>
array (
'extension' =>
array (
'downloadcounter' => '355',
'version' =>
array (
'title' => 'Product features',
'description' => '',
'downloadcounter' => '24',
'state' => 'beta',
'reviewstate' => '0',
'category' => 'plugin',
'lastuploaddate' => '1142878270',
'uploadcomment' => ' added related features',
),
),
),
)
This transform your XML into an array (with nested arrays).
It's not really perfect, because of unnecessary iterations.
Feel free to hack away...
Additionally:
- Parsing Huge XML Files in PHP
- https://github.com/prewk/XmlStreamer

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

PHP DOM remove child

I am trying to remove the parent node of <wcccanumber> from my xml, if it's content matches a certain criterion, but it keeps just removing the one node <wcccanumber>. How do I remove the whole parent node?
Heres my code:
$xml = new SimpleXMLElement('<xml/>');
if (file_exists("xml/units/E01.xml")) {
$xml = simplexml_load_file("xml/units/E01.xml");
echo "File exists";
echo "</br>";
$wcccanumber = "121202482";
foreach ($xml->call->wcccanumber as $call) {
if ($call == $wcccanumber) {
$dom = dom_import_simplexml($call);
$dom->parentNode->removeChild($dom);
$fp = fopen("xml/units/E01.xml","wb");
fwrite($fp,$xml->asXML());
fclose($fp);
}
}
}
Here is the xml:
<xml>
<call>
<wcccanumber>121202482</wcccanumber>
<currentcall>FALL</currentcall>
<county>W</county>
<id>82</id>
<location>234 E MAIN ST</location>
<callcreated>12:26:09</callcreated>
<station>HBM</station>
<units>E01</units>
<calltype>M</calltype>
<lat>45.5225067888299</lat>
<lng>-122.987112718574</lng>
<inputtime>12/18/2012 12:27:01 pm</inputtime>
</call>
</xml>
Iterate through call and compare $call->wcccanumber with $wcccanumber. Convert $call to dom and remove it (parentNode->removeChild).
foreach ($xml->call as $call) {
if ($call->wcccanumber == $wcccanumber) {
$dom = dom_import_simplexml($call);
$dom->parentNode->removeChild($dom);
$fp = fopen("xml/units/E01.xml","wb");
fwrite($fp,$xml->asXML());
fclose($fp);
}
}
If there are multiple deletions it makes sense to save only once after all deletions have been done.
$deletionCount = 0;
foreach ($xml->call as $call) {
if ($call->wcccanumber != $wcccanumber) {
continue;
}
$dom = dom_import_simplexml($call);
$dom->parentNode->removeChild($dom);
$deletionCount++;
}
if ($deletionCount) {
file_put_contents("xml/units/E01.xml", $xml->asXML());
}

Categories