Parsing XML file using CodeIgniter’s SimpleXML library

This Simplexml class provides an alternative implementation of the SimpleXML API that works under PHP 4, so if you have an application that is running under PHP4 environment this is really helpful for you.
The original class was created by Taha Paksu of http://www.phpclasses.org and it was modified by Chris Brainard so that it would work with codeigniter.
Lets take a look at the code.
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class Simplexml{
var $result = array();
var $ignore_level = 0;
var $skip_empty_values = false;
var $php_errormsg;
var $evalCode="";
function xml_parse($data){
$php_errormsg="";
$this->result="";
$this->evalCode="";
$values="";
$encoding = 'UTF-8';
$parser = xml_parser_create($encoding);
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
$ok = xml_parse_into_struct($parser, $data, $values);
if (!$ok) {
$errmsg = sprintf("XML parse error %d '%s' at line %d, column %d (byte index %d)",
xml_get_error_code($parser),
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser),
xml_get_current_column_number($parser),
xml_get_current_byte_index($parser));
}
xml_parser_free($parser);
return $this->xml_reorganize($values);
}
function xml_reorganize($array)
{
$count = count($array);
$repeat = $this->xml_tags($array);
$repeatedone = false;
$tags = array();
$k = 0;
for ($i = 0; $i < $count; $i++) {
switch ($array[$i]['type']) {
case 'open':
array_push($tags, $array[$i]['tag']);
if ($i > 0 && ($array[$i]['tag'] == $array[$i-1]['tag']) && ($array[$i-1]['type'] == 'close'))
$k++;
if (isset($array[$i]['value']) && ($array[$i]['value'] || !$this->skip_empty_values)) {
array_push($tags, '@content');
$this->array_insert(count($tags), $tags, $array[$i]['value'], "open");
array_pop($tags);
}
if (in_array($array[$i]['tag'] . $array[$i]['level'], $repeat)) {
if (($repeatedone == $array[$i]['tag'] . $array[$i]['level']) && ($repeatedone)) {
array_push($tags, strval($k++));
} else {
$repeatedone = $array[$i]['tag'] . $array[$i]['level'];
array_push($tags, strval($k));
}
}
if (isset($array[$i]['attributes']) && $array[$i]['attributes'] && $array[$i]['level'] != $this->ignore_level) {
array_push($tags, '@attributes');
foreach ($array[$i]['attributes'] as $attrkey => $attr) {
array_push($tags, $attrkey);
$this->array_insert(count($tags), $tags, $attr, "open");
array_pop($tags);
}
array_pop($tags);
}
break;
case 'close':
array_pop($tags);
if (in_array($array[$i]['tag'] . $array[$i]['level'], $repeat)) {
if ($repeatedone == $array[$i]['tag'] . $array[$i]['level']) {
array_pop($tags);
} else {
$repeatedone = $array[$i + 1]['tag'] . $array[$i + 1]['level'];
array_pop($tags);
}
}
break;
case 'complete':
array_push($tags, $array[$i]['tag']);
if (in_array($array[$i]['tag'] . $array[$i]['level'], $repeat)) {
if ($repeatedone == $array[$i]['tag'] . $array[$i]['level'] && $repeatedone) {
array_push($tags, strval($k));
} else {
$repeatedone = $array[$i]['tag'] . $array[$i]['level'];
array_push($tags, strval($k));
}
}
if (isset($array[$i]['value']) && ($array[$i]['value'] || !$this->skip_empty_values)) {
if (isset($array[$i]['attributes']) && $array[$i]['attributes']) {
array_push($tags, '@content');
$this->array_insert(count($tags), $tags, $array[$i]['value'], "complete");
array_pop($tags);
} else {
$this->array_insert(count($tags), $tags, $array[$i]['value'], "complete");
}
}
if (isset($array[$i]['attributes']) && $array[$i]['attributes']) {
array_push($tags, '@attributes');
foreach ($array[$i]['attributes'] as $attrkey => $attr) {
array_push($tags, $attrkey);
$this->array_insert(count($tags), $tags, $attr, "complete");
array_pop($tags);
}
array_pop($tags);
}
if (in_array($array[$i]['tag'] . $array[$i]['level'], $repeat)) {
array_pop($tags);
$k++;
}
array_pop($tags);
break;
}
}
eval($this->evalCode);
$last = $this->array_reindex($this->result);
return $last;
}
function array_insert($level, $tags, $value, $type)
{
$temp = '';
for ($c = $this->ignore_level + 1; $c < $level + 1; $c++) {
if (isset($tags[$c]) && (is_numeric(trim($tags[$c])) || trim($tags[$c]))) {
if (is_numeric($tags[$c])) {
$temp .= '[' . $tags[$c] . ']';
} else {
$temp .= '["' . $tags[$c] . '"]';
}
}
}
$this->evalCode .= '$this->result' . $temp . "=\"" . addslashes($value) . "\";//(" . $type . ")\n";
#echo $code. "\n";
}
/**
* Define the repeated tags in XML file so we can set an index
*
* @param array $array
* @return array
*/
function xml_tags($array)
{ $repeats_temp = array();
$repeats_count = array();
$repeats = array();
if (is_array($array)) {
$n = count($array) - 1;
for ($i = 0; $i < $n; $i++) {
$idn = $array[$i]['tag'].$array[$i]['level'];
if(in_array($idn,$repeats_temp)){
$repeats_count[array_search($idn,$repeats_temp)]+=1;
}else{
array_push($repeats_temp,$idn);
$repeats_count[array_search($idn,$repeats_temp)]=1;
}
}
}
$n = count($repeats_count);
for($i=0;$i<$n;$i++){
if($repeats_count[$i]>1){
array_push($repeats,$repeats_temp[$i]);
}
}
unset($repeats_temp);
unset($repeats_count);
return array_unique($repeats);
}
/**
* Converts Array Variable to Object Variable
*
* @param array $arg_array
* @return $tmp
*/
function array2object ($arg_array)
{
if (is_array($arg_array)) {
$keys = array_keys($arg_array);
if(!is_numeric($keys[0])) $tmp = new Xml;
foreach ($keys as $key) {
if (is_numeric($key)) $has_number = true;
if (is_string($key)) $has_string = true;
}
if (isset($has_number) and !isset($has_string)) {
foreach ($arg_array as $key => $value) {
$tmp[] = $this->array2object($value);
}
} elseif (isset($has_string)) {
foreach ($arg_array as $key => $value) {
if (is_string($key))
$tmp->$key = $this->array2object($value);
}
}
} elseif (is_object($arg_array)) {
foreach ($arg_array as $key => $value) {
if (is_array($value) or is_object($value))
$tmp->$key = $this->array2object($value);
else
$tmp->$key = $value;
}
} else {
$tmp = $arg_array;
}
return $tmp; //return the object
}
/**
* Reindexes the whole array with ascending numbers
*
* @param array $array
* @return array
*/
function array_reindex($array)
{
if (is_array($array)) {
if(count($array) == 1 && $array[0]){
return $this->array_reindex($array[0]);
}else{
foreach($array as $keys => $items) {
if (is_array($items)) {
if (is_numeric($keys)) {
$array[$keys] = $this->array_reindex($items);
} else {
$array[$keys] = $this->array_reindex(array_merge(array(), $items));
}
}
}
}
}
return $array;
}
}
In this article we don’t need to understand what is in the code above. What is my aim is to show you how to use it.
Supposing you have an xml format like below, and you need it to present as a tabular data in an html page.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <products xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <item> <id>1</id> <name>iPhone</name> <category>Mobile</category> <price>300</price> </item> <item> <id>2</id> <name>iMac</name> <category>Desktop Computers</category> <price>2500</price> </item> <item> <id>3</id> <name>MacBook Pro</name> <category>Mobile PC</category> <price>2000</price> </item> <item> <id>4</id> <name>iTouch</name> <category>Gadgets</category> <price>150</price> </item> <item> <id>5</id> <name>Wii</name> <category>Gaming</category> <price>1250</price> </item> <item> <id>6</id> <name>Time Capsule</name> <category>Acessories</category> <price>1000</price> </item> <item> <id>7</id> <name>Apple TV</name> <category>Gadgets</category> <price>800</price> </item> </products>
Lets start by creating our controller, to make it easier lets make use of the default controller in fresh CI installation which is the welcome controller. Now open you welcome controller and in you index function populate the code below.
function index()
{
//load the parser library
$this->load->library('parser');
$data['title'] = 'Parsing XML using Simplexml class of CodeIgniter';
$data['products'] = $this->_getXML('myxml');
$this->parser->parse('table_view', $data);
}
In this function we load the parser library, for those who dont know what a parser library is, its a simple templating engine of codeIgniter. Im using this almost always for me to get rid if the php tag in my view. Next we have a varialble title, and a variable products which calls the _getXML function with a string parameter myxml, we use this as reference of the filename for our XML file to be parse. Then load our table_view.
Lets add the _getXML function to our controller. Add this code in your welcome controller.
function _getXML($fname)
{
$filename = $fname.'.xml';
$xmlfile="./xml/".$filename;
$xmlRaw = file_get_contents($xmlfile);
$this->load->library('simplexml');
$xmlData = $this->simplexml->xml_parse($xmlRaw);
foreach($xmlData['item'] as $row)
{
$result .= '<tr>';
$result .= '<td>'.$row['id'].'</td>';
$result .= '<td>'.$row['name'].'</td>';
$result .= '<td>'.$row['category'].'</td>';
$result .= '<td>$ '.$row['price'].'</td>';
$result .= '</tr>';
}
return $result;
}
This assume that the file location of your xml file is in the root of your CI installation an inside the xml folder. Then using the file_get_contents() function we load the xml data to $xmlRaw varialble. We loaded the simple XML library and then we populate it to the table element using foreach() function.
Now you only need to add a very little code in your view file.
<table cellpadding="0" cellspacing="0">
<thead>
<th>
<td>PRODUCT ID</td>
<td>PRODUCT NAME</td>
<td>CATEGORY</td>
<td>PRICE</td>
</th>
</thead>
<tbody>
{products}
</tbody>
</table>
Thats it!. Adding some quick css styling and a jQuery for table row stripe effect. You get something like this.

Adding a quick table stripe effect.
Download and include the jQuery library file in your view.
<script type="text/javascript" src="public/js/jquery.js"></script>
Then add this line before the tag in your view file.
<script type="text/javascript">
$(document).ready(function(){
$("table tr:nth-child(even)").addClass("even");
});
</script>
And you must have a style like this in your css file.
table tr.even td{
background: #cdded6;
}
Were done. Thanks for reading.


13. Mar, 2009 








that was really swift and easy xml parsing. nice stuff.
by the way are you planning to add this in you e-commerce application? Just asking coz your sample data is products.
cheers
Nice post insic…. very helpful…. thanx for sharing….
@sam I dont think so, maybe not. It just happens I choose product list as sample
.
@Saurabh Shah thanks, and thanks for the tweet also.
A lot cleaner then I thought it would be. This can be really useful. Thanks!
I think you missed some MVC concepts here. Indeed, what if you wanna use the _getXML() function over various controllers inside your application? _getXML() should be declared outside the controller. Also, it should return $xmlData directly. Avoid HTML returns from your functions if you want pure MVC. Leave the for each inside your view, it’s going to be way clearer.
This is a really nice article, I like the way you used jQuery here
@Ben, Exactly. We can get rid actually with that _getXML() function and do the foreach() in the view.
You can do it this way, Put this in your index() funtion in the controller.
$this->load->library(’simplexml’);
$data['xmlData'] = $this->simplexml->xml_parse($xmlRaw);
Then do the iteration in your view.
Excellent post!! thanks for sharing =)
I have seen your smiling avatar on the TUTs sites for sometime, decided to come check out your website. I am very impressed! You are right up there with your knowledge to back up your comments. I am RSSing this site and look forward to learning a thing or two. I just got into Codeigniter, seems like you know a thing or two! Maybe you should do a tutorial for us NETTUTS fans?
Thanks, keep up the great work, great site!
I keep getting this…
Severity: Notice
Message: Undefined offset: 0
Filename: libraries/Simplexml.php
Line Number: 229
@shane you can do this
for the mean time, I havent figure out it yet.
error_reporting(E_ALL^E_NOTICE);
@shane here is the solution,
on line 242 locate
if(count($array) == 1 && $array[0]) {change it to
if(count($array) == 1 && isset($array[0])) {Thanks that helped although initially I had problems with
foreach($xmlData['item'] as $row)
returning just the first node of each item.
Then I added a second xml item and all worked fine.
Great stuff, thanks for sharing. Working on my first CodeIgniter project and this will come in handy.
Great post
Too difficult for me.
i’m not understanding where will palce this class “class Simplexml” in codeIgniter frame work
$this->load->library(‘parser’); is a default parser liberary ??
@Ellen Yes parser library is in the default package in CodeIgniter.
And you need to save the Simplexml.php in your libraries folder.
Very interesting posts and well written. Thx for sharing it, keep up the good work..
Hi insic2.0, thanx for your guideline can i ask you more about codeigniter ?
Excellent post!! thanks for sharing =)
Did u post any thing about captcha Security image for CodeIgniter?
Veeery nice.
Thanks very much but there are some problem when the xml file is compose by only one field
Works great, wonderful post.
How do you pull attributes out of the node?
ahh figured it out, but for anyones reference:
attributes can be pulled out by doing $row['@attributes']['YOURATTRIBUTE'];
be sure to include the @, thats what was messing me up.
How do I get the attributes of ARTICLE?
<NEWSFEED>
<ARTICLE ID=”627835″ POSTING_DATE=”16-Jun-2009″ POSTING_TIME=”09:00″ ARCHIVE_DATE=”05-Jun-2010″>
<NEWS_TYPE>News</NEWS_TYPE>
@Bob try to use print_r($youVar)
so you can see the structure of your array.
This is a pretty basic. Parsing such a simple XML can be done easily with raw PHP 5 using curl,simple XML object, and foreach. I would like to how easy it would be to parse a complex multidimensional XML file using codeignitor otherwise this is useless as the same can be done within 5 minutes with raw PHP.
Hey I have to work with XML parser to MySQL tables (not HTML) but is almost the same thing, so I made some changes to the test controller and table view to suit MVC, please view the solution in my blog (thanks insic2.0 for this Simplexml library):
MVC Solution on Carlos Alcala Blog
and thanks for your comments and ads clicks…
Great!!!!
My aplication have to go trough a firewall and get raw xml data, just what is needed in this library so i could take this part out from you _getXML function
# $filename = $fname.’.xml’;
# $xmlfile=”./xml/”.$filename;
# $xmlRaw = file_get_contents($xmlfile);
Hoooah!!
Thousand Thanks!
–
The more we listen, the more we learn. The more we learn, the more we can help.
How did you get your remote XML to work mine gives an error.
—
A PHP Error was encountered
Severity: Notice
Message: Uninitialized string offset: 0
Filename: controllers/welcome.php
Line Number: 30
—
A PHP Error was encountered
Severity: Warning
Message: Invalid argument supplied for foreach()
Filename: controllers/welcome.php
Line Number: 30
—
A PHP Error was encountered
Severity: Notice
Message: Undefined variable: result
Filename: controllers/welcome.php
Line Number: 39
—
why not simply use the php’s built in simpleXML class?
nice work and really working fine please can you update this library because i have seen in comments
if(count($array) == 1 && isset($array[0]))
please upate it.
Thank you for posting, awesome article!
I have proble with parsing some czech chars like “ř,ž,š”. When parser find this char, close xmlement
.
Pls help.
I second the isset(array[0]) on line 290 to prevent Errors.
Thanks!
I got a Error when i call
$this->simplexml->xml_parse($xmlRaw); in codeIgniter
The Error is in line 229
——————————-
A PHP Error was encountered
Severity: Notice
Message: Undefined offset: 0
Filename: libraries/simplexml.php
Line Number: 229
——————that means the line is———–
if (count ( $array ) == 1 && $array [0]) {
———————————
how can i solve it….
Very well.
A nice solution.
Thanks
the result show up, but,
I am always getting an error message,
“Undefined variable: result”.
why is that?
can someone shine the light for me?
thanks