From MySQL to XML with PHP

PHP XML generator can be written in less then 100 lines. In this example, data from the MySQL database are converted to the XML. PHP function sql2xml has an optional second parameter to describe the XML hierarchy, while first parameter is SQL query. Second parameter has the form of integers separated by commas. For example, to describe XML structure in three levels, second parameter can be "2,3". Each integer defines number of columns from SQL in XML hierarchy. The last level is not necessary to specify.

Here is example of how to use the sql2xml PHP function.

<? include('sql2xml.php') ?>
<DOCUMENT>
	<? sql2xml('select a.alb_id, a.alb_name,
										 s.sng_number, s.sng_name
							from album a, song s
							where a.alb_id = s.alb_id and	s.sng_number < 3
							order by a.alb_id, s.sng_number', '2') ?>
</DOCUMENT>

PHP function will transform SQL to the XML and here is the result. Column names from the SQL are used for XML tags. This example has a hierarchical structure in two levels because only first level is defined. Two columns (alb_id and alb_name) are the first level, while the other columns from the SQL are in the second level.

<DOCUMENT>
	<ROW0>
		<ALB_ID>1</ALB_ID>
		<ALB_NAME>Nevermind</ALB_NAME>
		<ROW1>
			<SNG_NUMBER>1</SNG_NUMBER>
			<SNG_NAME>Breed</SNG_NAME>
		</ROW1>
		<ROW1>
			<SNG_NUMBER>2</SNG_NUMBER>
			<SNG_NAME>Come As You Are</SNG_NAME>
		</ROW1>
	</ROW0>
	<ROW0>
		<ALB_ID>2</ALB_ID>
		<ALB_NAME>Band of Gypsys</ALB_NAME>
		<ROW1>
			<SNG_NUMBER>1</SNG_NUMBER>
			<SNG_NAME>Who Knows</SNG_NAME>
		</ROW1>
		<ROW1>
			<SNG_NUMBER>2</SNG_NUMBER>
			<SNG_NAME>Machine Gun</SNG_NAME>
		</ROW1>
	</ROW0>
</DOCUMENT>

PHP function will make connection to the MySQL database and transform SQL to the XML. Please, don't forget to set MySQL username and password in mysql_pconnect line at the beginning of sql2xml().

/**
 * sql2xml prints structured XML
 *
 * @param string  $sql       - SQL statement
 * @param string  $structure - XML hierarchy
 * @param integer $indent    - starting indent (number of spaces)
 */
function sql2xml($sql, $structure=0, $indent=0){
	// init variables for row processing
	$row_current = $row_previous = null;
	// set MySQL username and password
	$db_cn = mysql_pconnect('localhost', 'username', 'password');
	mysql_select_db('test', $db_cn); // connect to the database test
  $result = mysql_query($sql, $db_cn);
	// get number of columns in result
  $ncols = mysql_num_fields($result);
  // is there a hierarchical structure
  if ($structure == 0) {$deep = -1; $pos = 0;}
  else{
    $hierarchy = explode(',', $structure); // hierarchy levels
    $deep      = count($hierarchy);        // number of levels
    // set flags for opened tags
    for ($i=0; $i <= $deep; $i++) $tagOpened[$i] = false;
    // set initial row
    for ($i=0; $i < $ncols; $i++) $rowPrev[$i] = microtime();
  }
  // loop through result set
  while($row = mysql_fetch_row($result)){
    // loop through hierarchy levels (data set columns)
    for ($level=0,$pos=0; $level<$deep; $level++){
      // prepare row segments to compare
      for ($i=$pos; $i < $pos+$hierarchy[$level]; $i++){
        $row_current  .= trim($row[$i]);
        $row_previous .= trim($rowPrev[$i]);
      }
      // test row segments between row_current and row_previous
      if ($row_current != $row_previous){
        // close current tag and all tags below
        for ($i=$deep; $i >= $level; $i--){
          if ($tagOpened[$i]) printf('%'.($i*2+$indent)."s</ROW%d>\n", '', $i);
          $tagOpened[$i] = false;
        }
        // reset the rest of rowPrev
        for ($i=$pos; $i < $ncols; $i++) $rowPrev[$i] = microtime();
        // set flag to open
        $tagOpened[$level] = true;
        printf('%'.($level*2+$indent)."s<ROW%d>\n", '', $level);
        // loop through hierarchy levels
        for ($i=$pos; $i < $pos+$hierarchy[$level]; $i++){
          $name = strtoupper(mysql_field_name($result, $i));
          printf('%'.(($level+1)*2+$indent)."s<%s>%s</%s>\n",
          				'', $name, trim(htmlspecialchars($row[$i],$i)), $name);
        }
      }
      // increment row position
      $pos += $hierarchy[$level];
      // reset row segments (part of columns)
      $row_current = $row_previous = '';
    }
    // print rest
 		printf('%'.($level*2+$indent)."s<ROW%d>\n", '', $level);
    for ($i=$pos; $i < $ncols; $i++){
      $name = strtoupper(mysql_field_name($result, $i));
      printf('%'.(($level+1)*2+$indent)."s<%s>%s</%s>\n",
      				'', $name, trim(htmlspecialchars($row[$i],$i)), $name);
    }
    printf('%'.($level*2+$indent)."s</ROW%d>\n", '', $level);
    $rowPrev = $row; // remember previous row
  }
  // close opened tags
  for ($level=$deep; $level>=0; $level--)
  	if ($tagOpened[$level])
  		printf('%'.($level*2+$indent)."s</ROW%d>\n", '', $level);
}

Here are create table statements for tables album and song used in this example.

create table album (
alb_id int(11) not null auto_increment,
alb_name varchar(32) not null,
PRIMARY KEY (alb_id)
);

insert into album values (1,'Nevermind'), (2,'Band of Gypsys');

create table song (
sng_id int(11) not null auto_increment,
alb_id int(11) not null,
sng_number int(11) not null,
sng_name varchar(64) not null,
PRIMARY KEY (sng_id)
);

insert into song values
(1, 1, 1,'Breed'),
(2, 1, 2,'Come As You Are'),
(3, 1, 3,'Drain You'),
(4, 1, 4,'Endless, Nameless'),
(5, 1, 5,'In Bloom Lyrics'),
(6, 1, 6,'Lithium'),
(7, 1, 7,'Lounge Act'),
(8, 1, 8,'On A Plain'),
(9, 1, 9,'Polly'),
(10,1,10,'Smells Like Teen Spirit'),
(11,1,11,'Something In The Way'),
(12,1,12,'Stay Away'),
(13,1,13,'Territorial Pissings'),

(14,2, 1,'Who Knows'),
(15,2, 2,'Machine Gun'),
(16,2, 3,'Changes'),
(17,2, 4,'Power to Love'),
(18,2, 5,'Message to Love'),
(19,2, 6,'We Gotta Live Together');

In my next post From MySQL to HTML with PHP and XML, you can find how to transform XML from this example to the HTML. Sounds a bit complicated, but if the WEB architecture is set in this way, you will have a separate presentation layer with cleaner and simpler PHP code. You can also download redips1.tar.gz (3KB) package. It contains files and examples from this and next post.

Related posts

Bookmark and Share

8 Responses to “From MySQL to XML with PHP”

  1. Sphinx says:

    is it possible to import data from XML to MySQL? if so can this be automated?
    Any help would be greatly appreciated
    Thanks

  2. dbunic says:

    Yes, it's possible to parse received XML document and save it to the MySQL database (actually, to the any database). You will have to create interface - perhaps in PHP - to extract XML nodes. I will point you to the SimpleXML PHP extension which provides a very simple and easily usable toolset to convert XML to an object that can be processed with normal property selectors and array iterators.

    Hope this will help.

  3. Jasen says:

    I have two tables. one is holding the gallery name
    and the second table is holding the gallery name with the image name and caption.

    I need to be able to output the xml script in the exact same way as you have yours here.
    but when I typed in all your info, and followed all the tutorial on this page, nothing happened.

    here is my code that I have now....

    <?php
    require("config.php");

    function parseToXML($htmlStr)
    {
    $xmlStr=str_replace('','>',$xmlStr);
    $xmlStr=str_replace('"','"',$xmlStr);
    $xmlStr=str_replace("'",''',$xmlStr);
    $xmlStr=str_replace("&",'&',$xmlStr);
    return $xmlStr;
    }

    // Opens a connection to a MySQL server
    $connection=mysql_connect ('localhost', $username, $password);
    if (!$connection) {
    die('Not connected : ' . mysql_error());
    }

    // Set the active MySQL database
    $db_selected = mysql_select_db($database, $connection);
    if (!$db_selected) {
    die ('Can\'t use db : ' . mysql_error());
    }

    // Select all the rows in the markers table
    $query = "SELECT image FROM photogallery WHERE image ORDER BY title DESC";
    $result = mysql_query($query);
    if (!$result) {
    die('Invalid query: ' . mysql_error());
    }

    header("Content-type: text/xml");

    // Start XML file, echo parent node
    echo '';
    echo '';
    // Iterate through the rows, printing XML nodes for each
    while ($row = @mysql_fetch_assoc($result)){
    // ADD TO XML DOCUMENT NODE
    echo '';
    echo '' . parseToXML($row['title']) . '';
    echo 'http://www.cbchangar.com/uploads/images/' . $row['image'] . '';
    echo '' . $row['description'] . '';
    echo '';
    }
    echo '';
    // End XML file
    ?>

    how would I get it to output the xml like this

    1
    Gallery 1

    2
    Gallery 2

    basically I need to have the php script output the mysql data, but it needs to list all images associated with the gallery name. so when the client updates the photo gallery, when they create a new gallery they give it a name... that name is stored into the database by gname. so the xml file would list say gallery 1 then list all the images that are tagged or associated with gallery 1, then it would close that gname tag and then go to gname again which would say gallery 2, and so forth.

    any help would be greatly appreciated

  4. dbunic says:

    Jasen,
    as I can read from your comment, you have MySQL table "photogallery" with attributes gname, title and image (gname is gallery name). To produce XML hierarchy with sql2xml function from this post, you will have to create small PHP file:

    < ? include('sql2xml.php') ?>
    <DOCUMENT>
    < ? sql2xml('select gname, image from photogallery order by gname, title', '1') ?>
    </DOCUMENT>

    Optional parameter '1' means to have first column (gname) in level 1 while other columns from the select will be placed to the lower level. Please download redips1.tar.gz where you can find prepared examples and short readme.txt file. You will have to set MySQL username and password in sql2xml.php file (line 13) and edit test.php file (just replace select statement).

  5. How do I get it to structure the xml file like so ...

    <gname>Cars</gname>
      <imgName>Image 1</imgName>
        <caption>Image 1 Caption</caption>
    <gname>Jasen and Jenn</gname>

    basically..your code works, I mean obviously..
    but it does not format the xml into the above format.
    it just lists it out in a straight line.

    so the code is doing what it needs to do, but its not
    structuring it.

    am I missing something somewhere?

    thanks
    Jasen

  6. dbunic says:

    Yes, sql2xml listed data in a straight line. I made a little modification in PHP code and instead of print, function uses printf, so XML output will be nicely indented. Function now has one optional parameter more - starting indent - to add additional spaces before printed line.

    I will like to mention that each record (row) fetched from the database will be closed in <ROW></ROW> tags. Fetched records should be somehow grouped and nested if you need.

    Anyway, XML should be well formed - properly opened and closed tags, to be parse able with XML processors. Indentation is nice to have but not necessary. You can try to save output from sql2xml to the XML file (don't forget xml suffix) and open with FF or IE. Browser should recognize XML document type and display it indented - no matter if original XML is listed in straight line or not.

    Please comment if anything has been left out or is not clear.

  7. bino says:

    Dear Sir.

    I have a "some.sql" file in my hand, just like the one you use for creating your "test" database, it's 2 tables and their record.

    So .. How to convert this file to XML format ?
    Actualy the final result I need is XML format that compatible with PgSQL

    Sincerely
    -bino-

  8. dbunic says:

    Bino - If you have LAMP server near to your hands, and "some.sql" is MySQL compatible, then sql2xml() can help you. Procedure can go:

    1) save "some.sql" to the database
    2) create XML with PHP function sql2xml()

    I assume that generated XML will not be compatible with PgSQL, but you can apply XSLT on XML to get wanted XML format. Yes, that means to create XSL to transform XML to another XML. Whole procedure seems complicated. I'm just thinking, but did you try to modify "some.sql" like search and replace attribute types for PgSQL syntax - if the goal is to make "some.sql" compatible with PgSQL? Please comment if anything has been left out or is not clear.

Leave a Reply