Get last node in XML using DOMDocument PhP - php

I've made a class that reads value and node name of value and combines it into array to use it as a simple and quick access to config in XML. But this solution works only if ill give bottom node, XML looks like this.
<?xml version="1.0" encoding="ISO-8859-2"?>
<settings>
<const>
<inscript>true</inscript>
<title>Template</title>
</const>
<meta>
</meta>
<db>
<user>user</user>
<pass>pass</pass>
<host>host</host>
<name>name</name>
</db>
<path>
<style>
<css>/Template/view/www/style/</css>
<img>/Template/view/www/style/img</img>
</style>
</path>
</settings>
Now I want to get for example whole db node and return it as an array where node name wold be the key and node value, value. but im stuck at this. Heres what i made so far.
class config {
private static $xml = "lib/config/settings.xml";
private static $xmlRoot = "settings";
public static function loadConfig($value) {
$domDocument = new DOMDocument();
$domDocument->load(self::$xml);
$settings = $domDocument->getElementsByTagName(self::$xmlRoot);
try {
foreach ($settings as $setting) {
$configValue = $setting->getElementsByTagName($value)->item(0)->nodeValue;
$configNode = $setting->getElementsByTagName($value)->item(0)->nodeName;
$test = $setting->getElementsByTagName("path")->item(0)->childNodes->item(2)->nodeName;
var_dump($test);
}
$configValue = explode(' ', trim(preg_replace( '/\s+/', ' ', $configValue)));
$configNode = explode(' ', trim(preg_replace( '/\s+/', ' ', $configNode)));
$configArray = array_combine($configNode, $configValue);
return $configArray;
}
catch(Exception $e) {
echo '<h1>Błąd - '.$e->getMessage().'</h1>';
}
}
}

You could use XPath like this:
$xp = new DOMXPath($domDocument);
$config = array();
foreach ($xp->query('./db/*') as $node) {
$config[$node->nodeName] = $node->textContent;
}
return $config;

Related

inserting an object into an array of objects php

I am trying to write a php script which will insert an object into an array of objects which is originally in XML format. I need to insert the object at a specified index and then be able to re-write the xml file from which the data was pulled with the updated object. Here is the structure of my XML
<?xml version="1.0" encoding="UTF-8"?>
<Bars>
<Bar>
<BarName>Kam's</BarName>
<bar_id>0</bar_id>
<Bartenders>
<Bartender>
<fname>Max</fname>
<lname>Vest</lname>
<imageURL>http://uofi-bars.com/bartenderImages/maxvest.jpg</imageURL>
<shift>2</shift>
</Bartender>
</Bartenders>
<Events>
<Event>
<EventName>Kams event</EventName>
<date>08/10/1989</date>
</Event>
</Events>
<Specials>
<Special>Kam's Special 1</Special>
<Special>Kam's Special 2</Special>
</Specials>
</Bar>
So in other words, if I have a bartender who works at a bar with an id of bar_id = 0, I need to be able to insert that bartender into the array of bartenders for that particular bar.
I use the following php code to create the arrays from XML:
function objectsIntoArray($arrObjData, $arrSkipIndices = array())
{
$arrData = array();
// if input is object, convert into array
if (is_object($arrObjData)) {
$arrObjData = get_object_vars($arrObjData);
}
if (is_array($arrObjData)) {
foreach ($arrObjData as $index => $value) {
if (is_object($value) || is_array($value)) {
$value = objectsIntoArray($value, $arrSkipIndices); // recursive call
}
if (in_array($index, $arrSkipIndices)) {
continue;
}
$arrData[$index] = $value;
}
}
return $arrData;
}
$xmlUrl = "Bars.xml"; // XML
$xmlStr = file_get_contents($xmlUrl);
$xmlObj = simplexml_load_string($xmlStr);
$arrXml = objectsIntoArray($xmlObj);
print_r($arrXml);
I guess I just don't know how to refer to this array of objects within an array of objects... Any help would be greatly appreciated!
Thanks!
if you just replace your code:
$xmlUrl = "Bars.xml"; // XML
$xmlStr = file_get_contents($xmlUrl);
$xmlObj = simplexml_load_string($xmlStr);
$arrXml = objectsIntoArray($xmlObj);
print_r($arrXml);
with this:
$xmlUrl = "Bars.xml"; // XML
$xmlStr = file_get_contents($xmlUrl);
$xml = new SimpleXMLElement($xmlStr);
$bartenders = $xml->xpath('//Bartenders');
$new_bartender = $bartenders[0]->addChild('Bartender');
$new_bartender->fname = 'test1';
$new_bartender->lname = 'test2';
$new_bartender->imgURL = 'http://test.com';
$new_bartender->shift = '0';
print_r($bartenders);
this should do the trick, just replace the values with appropriate values :) i hope this helps!!

How do I change XML tag names with PHP?

I have an XML file that looks something like this:
<product>
<modelNumber>Data</modelNumber>
<salePrice>Data</salePrice>
</product>
<product>
<modelNumber>Data</modelNumber>
<salePrice>Data</salePrice>
</product>
Is there a simple way to change the tag names , to something else such as model, price.
Essentially, I have a bunch of XML files containing similar data, but in different formats, so I'm looking for a simple way to parse the XML file, change certain tag names, and write a new XML file with the changed tag names.
There are two issues with Kris and dfsq code:
Only first child node will be copied - solved with temporary copy of $childNodes)
Children will get xmlns tag - solved by replacing node at the beginning - so it's connected to the document
A corrected renaming function is:
function renameTag( DOMElement $oldTag, $newTagName ) {
$document = $oldTag->ownerDocument;
$newTag = $document->createElement($newTagName);
$oldTag->parentNode->replaceChild($newTag, $oldTag);
foreach ($oldTag->attributes as $attribute) {
$newTag->setAttribute($attribute->name, $attribute->value);
}
foreach (iterator_to_array($oldTag->childNodes) as $child) {
$newTag->appendChild($oldTag->removeChild($child));
}
return $newTag;
}
Next function will do the trick:
/**
* #param $xml string Your XML
* #param $old string Name of the old tag
* #param $new string Name of the new tag
* #return string New XML
*/
function renameTags($xml, $old, $new)
{
$dom = new DOMDocument();
$dom->loadXML($xml);
$nodes = $dom->getElementsByTagName($old);
$toRemove = array();
foreach ($nodes as $node)
{
$newNode = $dom->createElement($new);
foreach ($node->attributes as $attribute)
{
$newNode->setAttribute($attribute->name, $attribute->value);
}
foreach ($node->childNodes as $child)
{
$newNode->appendChild($node->removeChild($child));
}
$node->parentNode->appendChild($newNode);
$toRemove[] = $node;
}
foreach ($toRemove as $node)
{
$node->parentNode->removeChild($node);
}
return $dom->saveXML();
}
// Load XML from file data.xml
$xml = file_get_contents('data.xml');
$xml = renameTags($xml, 'modelNumber', 'number');
$xml = renameTags($xml, 'salePrice', 'price');
echo '<pre>'; print_r(htmlspecialchars($xml)); echo '</pre>';
There is some sample code that works in my question over here, but there is no direct way of changing a tag name through DOMDocument/DOMElement, you can however copy elements with a new tagname as shown.
basically you have to:
function renameTag(DOMElement $oldTag, $newTagName)
{
$document = $oldTag->ownerDocument;
$newTag = $document->createElement($newTagName);
foreach($oldTag->attributes as $attribute)
{
$newTag->setAttribute($attribute->name, $attribute->value);
}
foreach($oldTag->childNodes as $child)
{
$newTag->appendChild($oldTag->removeChild($child));
}
$oldTag->parentNode->replaceChild($newTag, $oldTag);
return $newTag;
}

Need to show child data on parent id

i'm struggling with Xpath, i have an xml list and i need to get the child data based on the parent id ...
My xml file :
<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
the code i use to get for example the project 1 data (does not work):
$xmlDoc = new DOMDocument();
$xmlDoc->load(data.xml);
$xpath = new DOMXPath($xmlDoc);
$projectId = '1';
$query = '//projecten/project[#id='.$projectId.']';
$details = $xpath->query($query);
foreach( $details as $detail )
{
echo $detail->titel;
echo $detail->siteurl;
echo $detail->screenshot;
echo $detail->omschrijving;
}
But this does not show anything, if someone can point me out ... thanks
In addition to the solution already given you can also use:
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$projectNode->getElementsByTagName('titel')->item(0)->nodeValue,
$projectNode->getElementsByTagName('siteurl')->item(0)->nodeValue,
$projectNode->getElementsByTagName('screenshot')->item(0)->nodeValue,
$projectNode->getElementsByTagName('omschrijving')->item(0)->nodeValue;
}
or fetch the DOMText node values directly with Xpath
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$xpath->evaluate('string(titel)', $projectNode),
$xpath->evaluate('string(siteurl)', $projectNode),
$xpath->evaluate('string(screenshot)', $projectNode),
$xpath->evaluate('string(omschrijving)', $projectNode);
}
or import the node to SimpleXml
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
$detail = simplexml_import_dom($projectNode);
echo
$detail->titel,
$detail->siteurl,
$detail->screenshot,
$detail->omschrijving;
}
or even concatenate all the values directly in the XPath:
$xpath = new DOMXPath($dom);
echo $xpath->evaluate(
sprintf(
'concat(
/projecten/project[#id = %1$d]/titel,
/projecten/project[#id = %1$d]/siteurl,
/projecten/project[#id = %1$d]/screenshot,
/projecten/project[#id = %1$d]/omschrijving
', $id
)
);
Accessing the child nodes as you do:
echo $detail->title;
Is not valid, if you use DOM* functions. This would probably work if you were using SimpleXML.
For DOM* try this:
$dom = new DOMDocument;
$dom->loadXml('<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
');
$id = 2;
$xpath = new DOMXPath($dom);
foreach ($xpath->query(sprintf('/projecten/project[#id="%s"]', $id)) as $projectNode) {
// repeat this for every needed node
$titleNode = $xpath->query('titel', $projectNode)->item(0);
if ($titleNode instanceof DOMElement) {
echo $titleNode->nodeValue;
}
// or us a loop for all child nodes
foreach ($projectNode->childNodes as $childNode) {
echo $childNode->nodeValue;
}
}

PHP SimpleXML: Feed modification

I want to modify an RSS feed. I want to remove X items from feed and then return the new feed as XML.
<?php
class RSS {
private $simpleXML;
public function __construct($address) {
$xml = file_get_contents($address);
$this->simpleXML = simplexml_load_string($xml);
}
private function getRssInformation() {
// Here I want to get the Rss Head information ...
}
// Here I get X items ...
private function getItems($itemsNumber, $UTF8) {
$xml = null;
$items = $this->simpleXML->xpath('/rss/channel/item');
if(count($items) > $itemsNumber) {
$items = array_slice($items, 0, $itemsNumber, true);
}
foreach($items as $item) {
$xml .= $item->asXML() . "\n";
}
return $xml;
}
public function getFeed($itemsNumber = 5, $UTF8 = true) {
// Here i will join rss information with X items ...
echo $this->getItems($itemsNumber, $UTF8);
}
}
?>
Is it possible with XPath?
Thank you.
Another way to do it (but could be cleaner) :
private function getItems($itemsNumber, $UTF8) {
$xml = '<?xml version="1.0" ?>
<rss version="2.0">
<channel>';
$i = 0;
if (count($this->simpleXML->rss->channel->item)>0){
foreach ($this->simpleXML->rss->channel->item as $item) {
$xml .= str_replace('<?xml version="1.0" ?>', '', $item->asXML());
$i++;
if($i==$itemsNumber) break;
}
}
$xml .=' </channel></rss> ';
return $xml;
}
But I think asXML(); add the XML declaration :
<?xml version="1.0" ?>
I edited my code. It's dirty but it should work. Any better idea?

PHP parsing XML file with and without namespaces

I need to get a XML File into a Database. Thats not the problem. Cant read it, parse it and create some Objects to map to the DB. Problem is, that sometimes the XML File can contain namespaces and sometimes not. Furtermore sometimes there is no namespace defined at all.
So what i first got was something like this:
<?xml version="1.0" encoding="UTF-8"?>
<struct xmlns:b="http://www.w3schools.com/test/">
<objects>
<object>
<node_1>value1</node_1>
<node_2>value2</node_2>
<node_3 iso_land="AFG"/>
<coords lat="12.00" long="13.00"/>
</object>
</objects>
</struct>
And the parsing:
$obj = new stdClass();
$nodes = array('node_1', 'node_2');
$t = $xml->xpath('/objects/object');
foreach($nodes AS $node) {
if($t[0]->$node) {
$obj->$node = (string) $t[0]->$node;
}
}
Thats fine as long as there are no namespaces. Here comes the XML File with namespaces:
<?xml version="1.0" encoding="UTF-8"?>
<b:struct xmlns:b="http://www.w3schools.com/test/">
<b:objects>
<b:object>
<b:node_1>value1</b:node_1>
<b:node_2>value2</b:node_2>
<b:node_3 iso_land="AFG"/>
<b:coords lat="12.00" long="13.00"/>
</b:object>
</b:objects>
</b:struct>
I now came up with something like this:
$xml = simplexml_load_file("test.xml");
$namespaces = $xml->getNamespaces(TRUE);
$ns = count($namespaces) ? 'a:' : '';
$xml->registerXPathNamespace("a", "http://www.w3schools.com/test/");
$nodes = array('node_1', 'node_2');
$obj = new stdClass();
foreach($nodes AS $node) {
$t = $xml->xpath('/'.$ns.'objects/'.$ns.'object/'.$ns.$node);
if($t[0]) {
$obj->$node = (string) $t[0];
}
}
$t = $xml->xpath('/'.$ns.'objects/'.$ns.'object/'.$ns.'node_3');
if($t[0]) {
$obj->iso_land = (string) $t[0]->attributes()->iso_land;
}
$t = $xml->xpath('/'.$ns.'objects/'.$ns.'object/'.$ns.'coords');
if($t[0]) {
$obj->lat = (string) $t[0]->attributes()->lat;
$obj->long = (string) $t[0]->attributes()->long;
}
That works with namespaces and without. But i feel that there must be a better way. Before that i could do something like this:
$t = $xml->xpath('/'.$ns.'objects/'.$ns.'object');
foreach($nodes AS $node) {
if($t[0]->$node) {
$obj->$node = (string) $t[0]->$node;
}
}
But that just wont work with namespaces.
You could make 'http://www.w3schools.com/test/' the default namespace. This way a:objectswould match regardless of whether the document says <a:objects> or <objects>.
If memory usage is not a issue you can even do it with a textual replacement, e.g.
$data = '<?xml version="1.0" encoding="UTF-8"?>
<struct xmlns:b="http://www.w3schools.com/test/">
<objects>
<object>
<node_1>value1</node_1>
<node_2>value2</node_2>
<node_3 iso_land="AFG"/>
<coords lat="12.00" long="13.00"/>
</object>
</objects>
</struct>';
$data = str_replace( // or preg_replace(,,,1) if you want to limit it to only one replacement
'xmlns:b="http://www.w3schools.com/test/"',
'xmlns="http://www.w3schools.com/test/" xmlns:b="http://www.w3schools.com/test/"',
$data
);
$xml = new SimpleXMLElement($data);
$xml->registerXPathNamespace("a", "http://www.w3schools.com/test/");
foreach($xml->xpath('//a:objects/a:object') as $n) {
echo $n->node_1;
}
You can make your XPATH statements more generic by matching on any element * and using a predicate filter to match on the local-name(), which will match on the element name with/without namespaces.
An XPATH like this:
/*[local-name()='struct']/*[local-name()='objects']/*[local-name()='object']/*[local-name()='coords']
Applied to the code sample you were using:
$obj = new stdClass();
$nodes = array('node_1', 'node_2');
$t = $xml->xpath('/*[local-name()="objects"]/*[local-name()="object"]');
foreach($nodes AS $node) {
if($t[0]->$node) {
$obj->$node = (string) $t[0]->$node;
}
}
Take a look at This
http://blog.sherifmansour.com/?p=302
It helped me a lot.

Categories