Pagination Filtered XML file - php

I want to paginate the following filtered results from xml file:
<?php
//load up the XML file as the variable $xml (which is now an array)
$xml = simplexml_load_file('inventory.xml');
//create the function xmlfilter, and tell it which arguments it will be handling
function xmlfilter ($xml, $color, $weight, $maxprice)
{
$res = array();
foreach ($xml->widget as $w)
{
//initially keep all elements in the array by setting keep to 1
$keep = 1;
//now start checking to see if these variables have been set
if ($color!='')
{
//if color has been set, and the element's color does not match, don't keep this element
if ((string)$w->color != $color) $keep = 0;
}
//if the max weight has been set, ensure the elements weight is less, or don't keep this element
if ($weight)
{
if ((int)$w->weight > $weight) $keep = 0;
}
//same goes for max price
if ($maxprice)
{
if ((int)$w->price > $maxprice) $keep = 0;
}
if ($keep) $res[] = $w;
}
return $res;
}
//check to see if the form was submitted (the url will have '?sub=Submit' at the end)
if (isset($_GET['sub']))
{
//$color will equal whatever value was chosen in the form (url will show '?color=Blue')
$color = isset($_GET['color'])? $_GET['color'] : '';
//same goes for these fellas
$weight = $_GET['weight'];
$price = $_GET['price'];
//now pass all the variables through the filter and create a new array called $filtered
$filtered = xmlfilter($xml ,$color, $weight, $price);
//finally, echo out each element from $filtered, along with its properties in neat little spans
foreach ($filtered as $widget) {
echo "<div class='widget'>";
echo "<span class='name'>" . $widget->name . "</span>";
echo "<span class='color'>" . $widget->color . "</span>";
echo "<span class='weight'>" . $widget->weight . "</span>";
echo "<span class='price'>" . $widget->price . "</span>";
echo "</div>";
}
}
Where $xml->widget represents the following xml:
<hotels xmlns="">
<hotels>
<hotel>
<noofrooms>10</noofrooms>
<website></website>
<imageref>oias-sunset-2.jpg|villas-agios-nikolaos-1.jpg|villas-agios-nikolaos-24​.jpg|villas-agios-nikolaos-41.jpg</imageref>
<descr>blah blah blah</descr>
<hotelid>119</hotelid>
</hotel>
</hotels>
</hotels>
Any good ideas?

Honestly if you're already using XML and want to do Pagination then use XSL. It'll allow for formatting of the results and for pagination with ease. PHP has a built in XSL transformer iirc
See http://www.codeproject.com/Articles/11277/Pagination-using-XSL for a decent example.

Related

Parse results from Zend_Dom_Query

I am trying to parse screen-scraped data using Zend_Dom_Query, but I am struggling how to apply it properly for my case, and all other answers I have seen on SO make assumptions that quite frankly scare me with their naiveté.
A typical example is How to Pass Array from Zend Dom Query Results to table where pairs of data points are being extracted from the documents body through the use of separate calls to the query() method.
$year = $dom->query('.secondaryInfo');
$rating = $dom->query('.ratingColumn');
Where the underlying assumptions are that an equal number of $year and $rating results exist AND that they are correctly aligned with each other within the document. If either of those assumptions are wrong, then the extracted data is less than worthless - in fact it becomes all lies.
In my case I am trying to extract multiple chunks of data from a site, where each chunk is nominally of the form:
<p class="main" atrb1="value1">
<a href="#1" >href text 1</a>
<span class="sub1">
<span class="span1"></span>
<span class="sub2">
<span class="span2">data span2</span>
href text 2
</span>
<span class="sub3">
<span class="span3">
<p>Some other data</p>
<span class="sub4">
<span class="sub5">More data</span>
</span>
</span>
</span>
</span>
</p>
For each chunk, I need to grab data from various sections:
".main"
".main a"
".main .span2"
".main .sub2 a"
".main .span3 p"
etc
And then process the set of data as one distinct unit, and not as multiple collections of different data.
I know I can hard code the selection of each element (and I currently do that), but that produces brittle code reliant on the source data being stable. And this week the data source yet again changed and I was bitten by my hard coded scraping failing to work. Thus I am trying to write robust code that can locate what I want without me having to care/know about the overall structure (Hmmm - Linq for php?)
So in my mind, I want the code to look something like
$dom = new Zend_Dom_Query($body);
$results = $dom->query('.main');
foreach ($results as $result)
{
$data1 = $result->query(".main a");
$data2 = $result->query(".main .span2");
$data3 = $result->query(".main .sub a");
etc
if ($data1 && $data2 && $data3) {
Do something
} else {
Do something else
}
}
Is it possible to do what I want with stock Zend/PHP function calls? Or do I need to write some sort of custom function to implement $result->query()?
OK .. so I bit the bullet and wrote my own solution to the problem. This code recurses through the results from the Zend_Dom_Query and looks for matching css selectors. As presented the code works for me and has also helped clean up my code. Performance wasn't an issue for me, but as always Caveat Emptor. I have also left in some commented out code that enables visualization of where the search is leading. The code was also part of a class, hence the use of $this-> in places.
The code is used as:
$dom = new Zend_Dom_Query($body);
$results = $dom->query('.main');
foreach ($results as $result)
{
$data1 = $this->domQuery($result, ".sub2 a");
if (!is_null($data1))
{
Do Something
}
}
Which finds the href text 2 element under the <span class="sub2"> element.
// Function that recurses through a Zend_Dom_Query_Result, looking for css selectors
private function recurseDomQueryResult($dom, $depth, $targets, $index, $count)
{
// Gross checking
if ($index<0 || $index >= $count) return NULL;
// Document where we are
$element = $dom->nodeName;
$class = NULL;
$id = NULL;
// $href = NULL;
// Skip unwanted elements
if ($element == '#text') return NULL;
if ($dom->hasAttributes()) {
if ($dom->hasAttribute('class'))
{
$class = trim($dom->getAttribute('class'));
}
if ($dom->hasAttribute('id'))
{
$id = trim($dom->getAttribute('id'));
}
// if ($element == 'a')
// {
// if ($dom->hasAttribute('href'))
// {
// $href = trim($dom->getAttribute('href'));
// }
// }
}
// $padding = str_repeat('==', $depth);
// echo "$padding<$element";
// if (!($class === NULL)) echo ' class="'.$class.'"';
// if (!($href === NULL)) echo ' href="'.$href.'"';
// echo '><br />'. "\n";
// See if we have a match for the current element
$target = $targets[$index];
$sliced = substr($target,1);
switch($target[0])
{
case '.':
if ($sliced === $class) {
$index++;
}
break;
case '#':
if ($sliced === $id) {
$index++;
}
break;
default:
if ($target === $element) {
$index++;
}
break;
}
// Check for having matched all
if ($index == $count) return $dom;
// We didn't have a match at this level
// So recursively look at all the children
$children = $dom->childNodes;
if ($children) {
foreach($children as $child)
{
if (!is_null(($result = $this->recurseDomQueryResult($child, $depth+1, $targets, $index, $count)))) return $result;
}
}
// Did not find anything
// echo "$padding</$element><br />\n";
return NULL;
}
// User function that you call to find a single element in a Zend_Dom_Query_Result
// $dom is the Zend_Dom_Query_Result object
// $path is a path of css selectors, e.g. ".sub2 a"
private function domQuery($dom, $path)
{
$depth = 0;
$index = 0;
$targets = explode(' ', $path);
$count = count($targets);
return $this->recurseDomQueryResult($dom, $depth, $targets, $index, $count);
}

Sorting strings, in a function, is my logic faulty or?

i need to sort some strings and match them with links, this is what i do:
$name_link = $dom->find('div[class=link] strong');
Returns array [0]-[5] containing strings such as NowDownload.eu
$code_link = $dom->find('div[class=link] code');
Returns links that match the names from 0-5, as in link [0] belongs to name [0]
I do not know the order in which they are returned, NowDownload.Eu, could be $code_link[4] or $code_link [3], but the name array will match it in order.
Now, i need $code_link[4] // lets say its NowDownload.Eu to become $link1 every time
so i do this
$i = 0;
while (!empty($code_link[$i]))
SortLinks($name_link, $code_link, $i); // pass all links and names to function, and counter
$i++;
}
function SortLinks($name_link, $code_link, &$i) { // counter is passed by reference since it has to increase after the function
$string = $name_link[$i]->plaintext; // name_link is saved as string
$string = serialize($string); // They are returned in a odd format, not searcheble unless i serialize
if (strpos($string, 'NowDownload.eu')) { // if string contains NowDownload.eu
$link1 = $code_link[$i]->plaintext;
$link1 = html_entity_decode($link1);
return $link1; // return link1
}
elseif (strpos($string, 'Fileswap')) {
$link2 = $code_link[$i]->plaintext;
$link2 = html_entity_decode($link2);
return $link2;
}
elseif (strpos($string, 'Mirrorcreator')) {
$link3 = $code_link[$i]->plaintext;
$link3 = html_entity_decode($link3);
return $link3;
}
elseif (strpos($string, 'Uploaded')) {
$link4 = $code_link[$i]->plaintext;
$link4 = html_entity_decode($link4);
return $link4;
}
elseif (strpos($string, 'Ziddu')) {
$link5 = $code_link[$i]->plaintext;
$link5 = html_entity_decode($link5);
return $link5;
}
elseif (strpos($string, 'ZippyShare')) {
$link6 = $code_link[$i]->plaintext;
$link6 = html_entity_decode($link6);
return $link6;
}
}
echo $link1 . '<br>';
echo $link2 . '<br>';
echo $link3 . '<br>';
echo $link4 . '<br>';
echo $link5 . '<br>';
echo $link6 . '<br>';
die();
I know they it finds the link, i have tested it before, but i wanted to make it a function, and it messed up, is my logic faulty or is there an issue with the way i pass the variables/ararys ?
I don't know why you pass $i as reference since you use it just for reading it. You could return an array contaning the named links and using it like so :
$all_links = SortLinks($name_link,$code_link);
echo $all_links['link1'].'<br/>';
echo $all_links['link2'].'<br/>';
You will have to put your loop inside the function, not outside.

Select specific Tumblr XML values with PHP

My goal is to embed Tumblr posts into a website using their provided XML. The problem is that Tumblr saves 6 different sizes of each image you post. My code below will get the first image, but it happens to be too large. How can I select one of the smaller-sized photos out of the XML if all the photos have the same tag of <photo-url>?
→ This is the XML from my Tumblr that I'm using: Tumblr XML.
→ This is my PHP code so far:
<?php
$request_url = "http://kthornbloom.tumblr.com/api/read?type=photo";
$xml = simplexml_load_file($request_url);
$title = $xml->posts->post->{'photo-caption'};
$photo = $xml->posts->post->{'photo-url'};
echo '<h1>'.$title.'</h1>';
echo '<img src="'.$photo.'"/>"';
echo "…";
echo "</br><a target=frame2 href='".$link."'>Read More</a>";
?>
The function getPhoto takes an array of $photos and a $desiredWidth. It returns the photo whose max-width is (1) closest to and (2) less than or equal to $desiredWidth. You can adapt the function to fit your needs. The important things to note are:
$xml->posts->post->{'photo-url'} is an array.
$photo['max-width'] accesses the max-width attribute on the <photo> tag.
I used echo '<pre>'; print_r($xml->posts->post); echo '</pre>'; to find out $xml->posts->post->{'photo-url'} was an array.
I found the syntax for accessing attributes (e.g., $photo['max-width']) at the documentation for SimpleXMLElement.
function getPhoto($photos, $desiredWidth) {
$currentPhoto = NULL;
$currentDelta = PHP_INT_MAX;
foreach ($photos as $photo) {
$delta = abs($desiredWidth - $photo['max-width']);
if ($photo['max-width'] <= $desiredWidth && $delta < $currentDelta) {
$currentPhoto = $photo;
$currentDelta = $delta;
}
}
return $currentPhoto;
}
$request_url = "http://kthornbloom.tumblr.com/api/read?type=photo";
$xml = simplexml_load_file($request_url);
foreach ($xml->posts->post as $post) {
echo '<h1>'.$post->{'photo-caption'}.'</h1>';
echo '<img src="'.getPhoto($post->{'photo-url'}, 450).'"/>"';
echo "...";
echo "</br><a target=frame2 href='".$post['url']."'>Read More</a>";
}
To get the photo with max-width="100":
$xml = simplexml_load_file('tumblr.xml');
echo '<h1>'.$xml->posts->post->{'photo-caption'}.'</h1>';
foreach($xml->posts->post->{'photo-url'} as $url) {
if ($url->attributes() == '100')
echo '<img src="'.$url.'" />';
}
Maybe this:
$doc = simplexml_load_file(
'http://kthornbloom.tumblr.com/api/read?type=photo'
);
foreach ($doc->posts->post as $post) {
foreach ($post->{'photo-url'} as $photo_url) {
echo $photo_url;
echo "\n";
}
}

Return in array

I have these php lines:
<?php
$start_text = '<username="';
$end_text = '" userid=';
$source = file_get_contents('http://mysites/users.xml');
$start_pos = strpos($source, $start_text) + strlen($start_text);
$end_pos = strpos($source, $end_text) - $start_pos;
$found_text = substr($source, $start_pos, $end_pos);
echo $found_text;
?>
I want to see just the names from entire file, but it shows me just the first name. I want to see all names.
I think it is something like: foreach ($found_text as $username).... but here I am stuck.
Update from OP post, below:
<?php
$xml = simplexml_load_file("users.xml");
foreach ($xml->children() as $child)
{
foreach($child->attributes() as $a => $b)
{
echo $a,'="',$b,"\"</br>";
}
foreach ($child->children() as $child2)
{
foreach($child2->attributes() as $c => $d)
{
echo "<font color='red'>".$c,'="',$d,"\"</font></br>";
}
}
}
?>
with this code, i receive all details about my users, but from all these details i want to see just 2 or 3
Now i see :
name="xxx"
type="default"
can_accept="true"
can_cancel="false"
image="avatars/trophy.png"
title="starter"
........etc
Another details from the same user "Red color(defined on script)"
reward_value="200"
reward_qty="1"
expiration_date="12/07/2012"
.....etc
what i want to see?
i.e first line from first column "name="xxx" & expiration_date="12/07/2012" from second column
You will need to repeat the loop, using the 3rd parameter, offset, of the strpos function. That way, you can look for a new name each time.
Something like this (untested)
<?php
$start_text = '<username="';
$end_text = '" userid=';
$source = file_get_contents('http://mysites/users.xml');
$offset = 0;
while (false !== ($start_pos = strpos($source, $start_text, $offset)))
{
$start_pos += strlen($start_text);
$end_pos = strpos($source, $end_text, $offset);
$offset = $end_pos;
$text_length = $end_pos - $start_pos;
$found_text = substr($source, $start_pos, $text_length);
echo $found_text;
}
?>
You should either use XMLReader or DOM or SimpleXML to read XML files. If you don't see the necessity, try the following regular expressions approach to retrieve all usernames:
<?php
$xml = '<xml><username="hello" userid="123" /> <something /> <username="foobar" userid="333" /></xml>';
if (preg_match_all('#<username="(?<name>[^"]+)"#', $xml, $matches, PREG_PATTERN_ORDER)) {
var_dump($matches['name']);
} else {
echo 'no <username="" found';
}

PHP how to count xml elements in object returned by simplexml_load_file(),

I have inherited some PHP code (but I've little PHP experience) and can't find how to count some elements in the object returned by simplexml_load_file()
The code is something like this
$xml = simplexml_load_file($feed);
for ($x=0; $x<6; $x++) {
$title = $xml->channel[0]->item[$x]->title[0];
echo "<li>" . $title . "</li>\n";
}
It assumes there will be at least 6 <item> elements but sometimes there are fewer so I get warning messages in the output on my development system (though not on live).
How do I extract a count of <item> elements in $xml->channel[0]?
Here are several options, from my most to least favourite (of the ones provided).
One option is to make use of the SimpleXMLIterator in conjunction with LimitIterator.
$xml = simplexml_load_file($feed, 'SimpleXMLIterator');
$items = new LimitIterator($xml->channel->item, 0, 6);
foreach ($items as $item) {
echo "<li>{$item->title}</li>\n";
}
If that looks too scary, or not scary enough, then another is to throw XPath into the mix.
$xml = simplexml_load_file($feed);
$items = $xml->xpath('/rss/channel/item[position() <= 6]');
foreach ($items as $item) {
echo "<li>{$item->title}</li>\n";
}
Finally, with little change to your existing code, there is also.
$xml = simplexml_load_file($feed);
for ($x=0; $x<6; $x++) {
// Break out of loop if no more items
if (!isset($xml->channel[0]->item[$x])) {
break;
}
$title = $xml->channel[0]->item[$x]->title[0];
echo "<li>" . $title . "</li>\n";
}
The easiest way is to use SimpleXMLElement::count() as:
$xml = simplexml_load_file($feed);
$num = $xml->channel[0]->count();
for ($x=0; $x<$num; $x++) {
$title = $xml->channel[0]->item[$x]->title[0];
echo "<li>" . $title . "</li>\n";
}
Also note that the return of $xml->channel[0] is a SimpleXMLElement object. This class implements the Traversable interface so we can use it directly in a foreach loop:
$xml = simplexml_load_file($feed);
foreach($xml->channel[0] as $item {
$title = $item->title[0];
echo "<li>" . $title . "</li>\n";
}
You get count by count($xml).
I always do it like this:
$xml = simplexml_load_file($feed);
foreach($xml as $key => $one_row) {
echo $one_row->some_xml_chield;
}

Categories