Shuffling xml output before echoing - php

I have a script that grabs an xml of my database generated by php and I would like to shuffle the rows before the php script echos the xml so that each time i access the database xml file, I'll receive the database in a different order.
here's part of my php script that outputs the xml:
$dom = new DOMDocument("1.0");
$node = $dom->createElement("database");
$parnode = $dom->appendChild($node);
$query = "SELECT * FROM database WHERE 1";
$result = mysql_query($query);
header("Content-type: text/xml");
while ($row = #mysql_fetch_assoc($result)){
// ADD TO XML DOCUMENT NODE
$node = dom->createElement("data");
$newnode = $parnode->appendChild($node);
$newnode->setAttribute("id", $row['id']);
$newnode->setAttribute("name", $row['name']);
$newnode->setAttribute("date", $row['date']);
$newnode->setAttribute("latitude", $row['latitude']);
$newnode->setattribute("longitude", $row['longitude']);
}
This is where I'd like to randomize the xml output if possible. This seems like the most logical place, but if there's a better place, that's fine with me.
echo $dom->saveXML();
Here's a sample of my xml:
<database>
<data id="1" name="blah" date="2012-10-10" latitude="0" longitude="0"/>
<data id="3" name="blah" date="2012-10-10" latitude="0" longitude="0"/>
<data id="4" name="blah" date="2012-10-10" latitude="0" longitude="0"/>
</database>
Simply put, I would like the xml rows to be in a different order each time i access it. Thanks for your help.

One way around this problem is to use a temporary array to store the rows fetched from DB, then shuffle this array, then walk through it:
$records = array();
while ($row = #mysql_fetch_assoc($result)){
$records[] = $row;
}
shuffle($records);
foreach ($records as $row) {
$node = dom->createElement("data");
$newnode = $parnode->appendChild($node);
$newnode->setAttribute("id", $row['id']);
...
}
The other way is use the original array as it is, but randomize the insertion process instead:
$prevnode = null;
while ($row = #mysql_fetch_assoc($result)){
$node = dom->createElement("data");
if ($prevnode && rand(0, 1) === 0) {
$newnode = $prevnode->insertBefore($node);
} else {
$newnode = $parnode->appendChild($node);
}
$prevnode = $newnode;
$newnode->setAttribute("id", $row['id']);
...
}

If your result set is not very large and your database supports it, you can have the database shuffle the results for you. For example, MySQL can do this:
SELECT * FROM database ORDER BY rand();
Your database may differ.
You could also separate XML generation from result collection. Shuffle the intermediate array:
function get_database_results($dbconn=null) {
$rows = false;
$query = 'SELECT * FROM database';
$res = mysql_query($query, $dbconn);
if ($res) {
$rows = array();
while ($row = mysql_fetch_assoc($res)) {
$rows[] = $row;
}
}
return $rows;
}
function array_to_sxe($rows) {
$sxe = simplexml_load_string('<database></database>');
foreach ($rows as $row) {
$data = $sxe->addChild('data');
foreach ($row as $key => $value) {
$data->addAttribute($key, $value);
}
}
return $sxe;
}
$rows = get_database_results();
shuffle($rows);
$sxe = array_to_sxe($rows);
header('Content-Type: application/xml'); // NOT text/xml!
echo $sxe->asXML();
I will now rant about encodings.
You have to be more careful about your encodings! As it is, you can very easily generate malformed XML.
text/xml means that the xml is in 7-bit ascii. That is very unlikely what you meant.
application/xml means that the xml is utf-8. This is what DOMDocument and SimpleXML output by default, and only accept utf-8 values for methods like setAttribute! This means you need to be absolutely sure:
That the strings you put into the database are utf-8.
That the connection between php and the database is utf-8. (Using SET NAMES utf8 or the charset parameter to the DSN with PDO--which you should be using instead of mysql_*)
That the strings you pull out of the database are utf-8, or at least are converted to utf-8 before use.

You can add
ORDER BY rand()
to your SQL-Query

Related

How to loop through two XML files and print result

I've been trying unsuccessfully with PHP to loop through two XML files and print the result to the screen. The aim is to take a country's name and output its regions/states/provinces as the case may be.
The first block of code successfully prints all the countries but the loop through both files gives me a blank screen.
The countries file is in the format:
<row>
<id>6</id>
<name>Andorra</name>
<iso2>AD</iso2>
<phone_code>376</phone_code>
</row>
And the states.xml:
<row>
<id>488</id>
<name>Andorra la Vella</name>
<country_id>6</country_id>
<country_code>AD</country_code>
<state_code>07</state_code>
</row>
so that country_id = id.
This gives a perfect list of countries:
$xml = simplexml_load_file("countries.xml");
$xml1 = simplexml_load_file("states.xml");
foreach($xml->children() as $key => $children) {
print((string)$children->name); echo "<br>";
}
This gives me a blank screen except for the HTML stuff on the page:
$xml = simplexml_load_file("countries.xml");
$xml1 = simplexml_load_file("states.xml");
$s = "Jamaica";
foreach($xml->children() as $child) {
foreach($xml1->children() as $child2){
if ($child->id == $child2->country_id && $child->name == $s) {
print((string)$child2->name);
echo "<br>";
}
}
}
Where have I gone wrong?
Thanks.
I suspect your problem is not casting the name to a string before doing your comparison. But why are you starting the second loop before checking if it's needed? You're looping through every single item in states.xml needlessly.
$countries = simplexml_load_file("countries.xml");
$states = simplexml_load_file("states.xml");
$search = "Jamaica";
foreach($countries->children() as $country) {
if ((string)$country->name !== $search) {
continue;
}
foreach($states->children() as $state) {
if ((string)$country->id === (string)$state->country_id) {
echo (string)$state->name . "<br/>";
}
}
}
Also, note that naming your variables in a descriptive manner makes it much easier to figure out what's going on with code.
You could probably get rid of the loops altogether using an XPath query to match the sibling value. I don't use SimpleXML, but here's what it would look like with DomDocument:
$search = "Jamaica";
$countries = new DomDocument();
$countries->load("countries.xml");
$xpath = new DomXPath($countries);
$country = $xpath->query("//row[name/text() = '$search']/id/text()");
$country_id = $country[0]->nodeValue;
$states = new DomDocument();
$states->load("states.xml");
$xpath = new DomXPath($states);
$states = $xpath->query("//row[country_id/text() = '$country_id']/name/text()");
foreach ($states as $state) {
echo $state->nodeValue . "<br/>";
}

How can export mysql data in to xml using php

Below code is for export data from mysql table as xml file. I have tried several code but not getting the result. Please check and help me.
Currently getting result is
8sarathsarathernakulam423432washington9rahulrahulernakulam21212121newyork10aaaa3london11bbbb1newyork12cccc2washington13dddd3london
Code
<?php
require_once "classes/dbconnection-class.php";
if(isset($_POST['export'])){
header('Content-type: text/xml');
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
$root_element = "addressbook"; //fruits
$xml .= "<$root_element>";
$query = "SELECT AB.id, AB.name, AB.firstname, AB.street, AB.zipcode, AB.city_id, CI.city FROM address_book AS AB INNER JOIN city AS CI ON AB.city_id = CI.id";
$result = $mysqli->query($query);
if (!$result) {
die('Invalid query: ' . $mysqli->error());
}
while($result_array = $result->fetch_assoc()){
$xml .= "<address>";
foreach($result_array as $key => $value)
{
//$key holds the table column name
$xml .= "<$key>";
//embed the SQL data in a CDATA element to avoid XML entity issues
$xml .= "<![CDATA[$value]]>";
//and close the element
$xml .= "</$key>";
}
$xml.="</address>";
}
$xml .= "</$root_element>";
header ("Content-Type:text/xml");
//header('Content-Disposition: attachment; filename="downloaded.xml"');
echo $xml;
}
?>
Browser shows
<?xml version="1.0" encoding="UTF-8"?><addressbook><address><id><![CDATA[8]]></id><name><![CDATA[sarath]]></name><firstname><![CDATA[sarath]]></firstname><street><![CDATA[ernakulam]]></street><zipcode><![CDATA[42343]]></zipcode><city_id><![CDATA[2]]></city_id><city><![CDATA[washington]]></city></address><address><id><![CDATA[9]]></id><name><![CDATA[rahul]]></name><firstname><![CDATA[rahul]]></firstname><street><![CDATA[ernakulam]]></street><zipcode><![CDATA[2121212]]></zipcode><city_id><![CDATA[1]]></city_id><city><![CDATA[newyork]]></city></address><address><id><![CDATA[10]]></id><name><![CDATA[a]]></name><firstname><![CDATA[a]]></firstname><street><![CDATA[a]]></street><zipcode><![CDATA[a]]></zipcode><city_id><![CDATA[3]]></city_id><city><![CDATA[london]]></city></address><address><id><![CDATA[11]]></id><name><![CDATA[b]]></name><firstname><![CDATA[b]]></firstname><street><![CDATA[b]]></street><zipcode><![CDATA[b]]></zipcode><city_id><![CDATA[1]]></city_id><city><![CDATA[newyork]]></city></address><address><id><![CDATA[12]]></id><name><![CDATA[c]]></name><firstname><![CDATA[c]]></firstname><street><![CDATA[c]]></street><zipcode><![CDATA[c]]></zipcode><city_id><![CDATA[2]]></city_id><city><![CDATA[washington]]></city></address><address><id><![CDATA[13]]></id><name><![CDATA[d]]></name><firstname><![CDATA[d]]></firstname><street><![CDATA[d]]></street><zipcode><![CDATA[d]]></zipcode><city_id><![CDATA[3]]></city_id><city><![CDATA[london]]></city></address></addressbook>
When we are dealing with XML and HTML, the best way to act is ever through a parser.
In this particular situation, operating with a parser guarantees a valid XML and a clean, short code.
After defining mySQL query, we init a new DOMDocument with version and encoding, then we set his ->formatOutput to True to print out XML in indented format:
$query = "SELECT AB.id, AB.name, AB.firstname, AB.street, AB.zipcode, AB.city_id, CI.city FROM address_book AS AB INNER JOIN city AS CI ON AB.city_id = CI.id";
$dom = new DOMDocument( '1.0', 'utf-8' );
$dom ->formatOutput = True;
Then, we create the root node and we append it to DOMDocument:
$root = $dom->createElement( 'addressbook' );
$dom ->appendChild( $root );
At this point, after executing mySQL query, we perform a while loop through each resulting row; for each row, we create an empty node <address>, then we perform a foreach loop through each row's field. For each field, we create an empty childnode with tag as field key, then we append to childnode the field value as CDATA and the same childnode to <address> node; at the end of each while loop, each <address> node is appended to root node:
$result = $mysqli->query( $query );
while( $row = $result->fetch_assoc() )
{
$node = $dom->createElement( 'address' );
foreach( $row as $key => $val )
{
$child = $dom->createElement( $key );
$child ->appendChild( $dom->createCDATASection( $val) );
$node ->appendChild( $child );
}
$root->appendChild( $node );
}
Now, your XML is ready.
If you want save it to a file, you can do it by:
$dom->save( '/Your/File/Path.xml' );
Otherwise, if you prefer send it as XML you have to use this code:
header( 'Content-type: text/xml' );
echo $dom->saveXML();
exit;
If you want instead output it in HTML page, you can write this code:
echo '<pre>';
echo htmlentities( $dom->saveXML() );
echo '</pre>';
See more about DOMDocument
Go to your phpmyadmin database export and select xml in file format.
Replace
$xml .= "<![CDATA[$value]]>";
with
$xml .= $value;
IF you want to have it format it "nicely" in the browser add an:
echo "<pre>";
before the:
echo $xml;
Please note this WILL BREAK the XML file, but it will look good in the browser.... if that is what you are after...
I would suggest to use libraries like SimpleXMLElement etc. to create XML documents.
$xml = new SimpleXMLElement("<?xml version=\"1.0\" encoding=\"UTF-8\" ?><{$root_element}></{$root_element}>");
while($result_array = $result->fetch_assoc()){
foreach($result_array as $key => $value)
{
$address = $xml->addChild("address");
//embed the SQL data in a CDATA element to avoid XML entity issues
$addressFields = $address->addChild('"' . $key . '"', "<![CDATA[$value]]>");
//No need to close the element
}
}
Header('Content-type: text/xml');
print($xml->asXML());

Generating XML from SQL using Mysqli

I'm trying to pull all the data from my users table and display it in XML format. The connection works fine and everything as I have a login and registration set up fine, but I can't seem to get this to display anything other than a white screen.
I've found lots of different tutorials on how to do it with mysql but not mysqli. what am i missing?
generatexml.php
<?php
include 'connection.php';
$xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
$root_element = $config['users'];
$xml .= "<$root_element>";
if ($result = $mysqli->query("SELECT * FROM users", MYSQLI_USE_RESULT)) {
while($row = $result->fetch_assoc())
{
$xml .= "<".$config['users'].">";
//loop through each key,value pair in row
foreach($result_array as $key => $value)
{
//$key holds the table column name
$xml .= "<$key>";
//embed the SQL data in a CDATA element to avoid XML entity issues
$xml .= "<![CDATA[$value]]>";
//and close the element
$xml .= "</$key>";
}
$xml.="</".$config['users'].">";
echo $xml;
}
}
?>
I struggle a lot to find out this solution in mysqli format but nowhere i found the solution. Below is the solution i figured. Run this demo and map it your requirement, surely it will help.
<?php
//Create file name to save
$filename = "export_xml_".date("Y-m-d_H-i",time()).".xml";
$mysql = new Mysqli('server', 'user', 'pass', 'database');
if ($mysql->connect_errno) {
throw new Exception(sprintf("Mysqli: (%d): %s", $mysql->connect_errno, $mysql->connect_error));
}
//Extract data to export to XML
$sqlQuery = 'SELECT * FROM t1';
if (!$result = $mysql->query($sqlQuery)) {
throw new Exception(sprintf('Mysqli: (%d): %s', $mysql->errno, $mysql->error));
}
//Create new document
$dom = new DOMDocument;
$dom->preserveWhiteSpace = FALSE;
//add table in document
$table = $dom->appendChild($dom->createElement('table'));
//add row in document
foreach($result as $row) {
$data = $dom->createElement('row');
$table->appendChild($data);
//add column in document
foreach($row as $name => $value) {
$col = $dom->createElement('column', $value);
$data->appendChild($col);
$colattribute = $dom->createAttribute('name');
// Value for the created attribute
$colattribute->value = $name;
$col->appendChild($colattribute);
}
}
/*
** insert more nodes
*/
$dom->formatOutput = true; // set the formatOutput attribute of domDocument to true
// save XML as string or file
$test1 = $dom->saveXML(); // put string in test1
$dom->save($filename); // save as file
$dom->save('xml/'.$filename);
?>
If you have access to the mysql CLI, here's my quick hack for achieving this:
$sql = "SELECT * FROM dockcomm WHERE listname = 'roortoor'
and status IN ('P','E') and comm_type IN ('W','O')
and comm_period NOT IN ('1','2','3','4') order by comment_num";
$cmd = "/usr/bin/mysql -u<person> -h<host> -p<password> <database> --xml -e \"$sql\";";
$res = system($cmd ,$resval);
echo $res;
Here is a solution using php only. You where close to getting it right. This was the key part of the code that I changed "$row as $key => $data" used $row instead of $result_array, ie. iterate through row not the result_array (this contains the entire dataset). Hope this helps someone.
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
$value .="<record>\r\n";
//loop through each key,value pair in row
foreach($row as $key => $data)
{
//$key holds the table column name
$vals = "\t" . "<". $key . ">" . "<![CDATA[" . $data . "]]>" . "</" . $key . ">" . "\r\n";
$value = $value . $vals;
//echo $value;
}
$value .="</record>\r\n";
$count++;
}
} else {
// echo "0 results";
}
$conn->close();
One possible issue could be this line:
if ($result = $mysqli->query("SELECT * FROM users", MYSQLI_USE_RESULT)) {
Try the procedural approach instead of the object oriented approach. I do not know if $mysqli is defined in connection.php, but it is possible that you mixed it up.
if ($result = mysqli_query('SELECT * FROM users', MYSQLI_USE_RESULT)) {
This could resolve the white screen error.
I noticed two other things:
(1) One tiny effectiveness issue:
$xml = '<?xml version="1.0" encoding="UTF-8"?>';
So you do not need to escape every single quotation mark.
(2) One serious XML issue: The root element needs to be closed before you echo your $xml.
$xml .= "</$root_element>";
echo $xml;
Generally, for your purpose, it would be safer to use PHP's XMLWriter extension, as already proposed.

How to put XML content in reverse order?

I tried a few solutions already posted here, but nothing seems to work. That might be, because I am really not that familiar with php or xml. I just need to output xml for xcoding. So hopefully someone can shed some light, on how I can reverse the order of my xml, so that the last entry is on top.
My last try what with:
$query = array_reverse($doc->xpath('query'));
but it did not like it ,e.g. it did not work.
So here is my code in order to create my xml document:
<?php
if(!$dbconnect = mysql_connect('localhost', '-', '-')) {
echo "Connection failed to the host 'localhost'.";
exit;
} // if
if (!mysql_select_db('-')) {
echo "Cannot connect to database '-'";
exit;
} // if
$table_id = 'table1';
$query = "SELECT Name,Age,Sex FROM $table_id";
$dbresult = mysql_query($query, $dbconnect);
// create a new XML document
$doc = new DomDocument('1.0');
// create root node
$root = $doc->createElement('root');
$root = $doc->appendChild($root);
// process one row at a time
while($row = mysql_fetch_assoc($dbresult)) {
// add node for each row
$occ = $doc->createElement($table_id);
$occ = $root->appendChild($occ);
// add a child node for each field
foreach ($row as $fieldname => $fieldvalue) {
$child = $doc->createElement($fieldname);
$child = $occ->appendChild($child);
$value = $doc->createTextNode($fieldvalue);
$value = $child->appendChild($value);
} // foreach
} // while
// get completed xml document
$xml_string = $doc->saveXML();
$doc->save("yolo.xml")
//echo $xml_string;
?>
The best thing to do is to issue an SQL query which returns results in the order you want. The SQL query you have here has no ORDER BY clause, so results come back in no particular order.
However, you can also reverse the order of results as you add them to your XML document: instead of appending new child nodes for each row, prepend them. Replace this line:
$occ = $root->appendChild($occ);
With
$occ = $root->insertBefore($occ, $root->firstChild);
Also generally speaking it's best to finish building a tree of XML before you add it to the document.

Parse html table using file_get_contents to php array

I am trying to parse the table shown here into a multi-dimensional php array. I am using the following code but for some reason its returning an empty array. After searching around on the web, I found this site which is where I got the parseTable() function from. From reading the comments on that website, I see that the function works perfectly. So I'm assuming there is something wrong with the way I'm getting the HTML code from file_get_contents(). Any thoughts on what I'm doing wrong?
<?php
$data = file_get_contents('http://flow935.com/playlist/flowhis.HTM');
function parseTable($html)
{
// Find the table
preg_match("/<table.*?>.*?<\/[\s]*table>/s", $html, $table_html);
// Get title for each row
preg_match_all("/<th.*?>(.*?)<\/[\s]*th>/", $table_html[0], $matches);
$row_headers = $matches[1];
// Iterate each row
preg_match_all("/<tr.*?>(.*?)<\/[\s]*tr>/s", $table_html[0], $matches);
$table = array();
foreach($matches[1] as $row_html)
{
preg_match_all("/<td.*?>(.*?)<\/[\s]*td>/", $row_html, $td_matches);
$row = array();
for($i=0; $i<count($td_matches[1]); $i++)
{
$td = strip_tags(html_entity_decode($td_matches[1][$i]));
$row[$row_headers[$i]] = $td;
}
if(count($row) > 0)
$table[] = $row;
}
return $table;
}
$output = parseTable($data);
print_r($output);
?>
I want my output array to look something like this:
1
--> 11:33AM
--> DEV
--> IN THE DARK
2
--> 11:29AM
--> LIL' WAYNE
--> SHE WILL
3
--> 11:26AM
--> KARDINAL OFFISHALL
--> NUMBA 1 (TIDE IS HIGH)
Don't cripple yourself parsing HTML with regexps! Instead, let an HTML parser library worry about the structure of the markup for you.
I suggest you to check out Simple HTML DOM (http://simplehtmldom.sourceforge.net/). It is a library specifically written to aid in solving this kind of web scraping problems in PHP. By using such a library, you can write your scraping in much less lines of code without worrying about creating working regexps.
In principle, with Simple HTML DOM you just write something like:
$html = file_get_html('http://flow935.com/playlist/flowhis.HTM');
foreach($html->find('tr') as $row) {
// Parse table row here
}
This can be then extended to capture your data in some format, for instance to create an array of artists and corresponding titles as:
<?php
require('simple_html_dom.php');
$table = array();
$html = file_get_html('http://flow935.com/playlist/flowhis.HTM');
foreach($html->find('tr') as $row) {
$time = $row->find('td',0)->plaintext;
$artist = $row->find('td',1)->plaintext;
$title = $row->find('td',2)->plaintext;
$table[$artist][$title] = true;
}
echo '<pre>';
print_r($table);
echo '</pre>';
?>
We can see that this code can be (trivially) changed to reformat the data in any other way as well.
I tried simple_html_dom but on larger files and on repeat calls to the function I am getting zend_mm_heap_corrupted on php 5.3 (GAH). I have also tried preg_match_all (but this has been failing on a larger file (5000) lines of html, which was only about 400 rows of my HTML table.
I am using this and its working fast and not spitting errors.
$dom = new DOMDocument();
//load the html
$html = $dom->loadHTMLFile("htmltable.html");
//discard white space
$dom->preserveWhiteSpace = false;
//the table by its tag name
$tables = $dom->getElementsByTagName('table');
//get all rows from the table
$rows = $tables->item(0)->getElementsByTagName('tr');
// get each column by tag name
$cols = $rows->item(0)->getElementsByTagName('th');
$row_headers = NULL;
foreach ($cols as $node) {
//print $node->nodeValue."\n";
$row_headers[] = $node->nodeValue;
}
$table = array();
//get all rows from the table
$rows = $tables->item(0)->getElementsByTagName('tr');
foreach ($rows as $row)
{
// get each column by tag name
$cols = $row->getElementsByTagName('td');
$row = array();
$i=0;
foreach ($cols as $node) {
# code...
//print $node->nodeValue."\n";
if($row_headers==NULL)
$row[] = $node->nodeValue;
else
$row[$row_headers[$i]] = $node->nodeValue;
$i++;
}
$table[] = $row;
}
var_dump($table);
This code worked well for me.
Example of original code is here.
http://techgossipz.blogspot.co.nz/2010/02/how-to-parse-html-using-dom-with-php.html

Categories