PHP with DOMXPath - Sum values after selection of items - php

I have this html structure:
<div class="wanted-list">
<div class="headline"></div>
<div class="entry">
<div></div>
<div></div>
<div class="length">1100</div>
<div></div>
<div class="status">
<img src="xxxx" alt="open">
</div>
</div>
<div class="entry mark">
<div></div>
<div></div>
<div class="length">800</div>
<div></div>
<div class="status">
<img src="xxxx" alt="open">
</div>
</div>
<div class="entry">
<div></div>
<div></div>
<div class="length">2300</div>
<div></div>
<div class="status">
<img src="xxxx" alt="closed">
</div>
</div>
</div>
I want to select only the items that are 'open', so I do:
$doc4 = new DOMDocument();
$doc4->loadHtmlFile('http://www.whatever.com');
$doc4->preserveWhiteSpace = false;
$xpath4 = new DOMXPath($doc4);
$elements4 = $xpath4->query("//div[#class='wanted-list']/div/div[5]/img[#alt='open']");
Now, if I'm not mistaken, we have isolated the 'open' items we wanted. Now, I need to get the 'length' values, and sum them to make a total length so I can echo it. I've spent several hours trying different solutions and researching, but I haven't found anything similar. Can you guys help?
Thanks in advance.
EDITED the wrong div's, sorry.

I'm not sure if you mean for the calculations all to be done in the xsl or whether you are just wanting the sum of the lengths to be available to you in php, however this captures and sums the lengths. As noted by #Chris85 in the comment - the html is invalid - there are spare closing div tags within each entry ~ presumably the image is supposed to be a child of div.status? If that is so the below would need slight modification when trying to target the correct parent. That said, I received no warnings from DOMDocument whilst parsing it but better to fix than ignore!
$strhtml='
<div class="wanted-list">
<div class="headline"></div>
<div class="entry">
<div></div>
<div></div>
<div class="length">1100</div>
<div></div>
<div class="status">
<img src="xxxx" alt="open">
</div>
</div>
<div class="entry mark">
<div></div>
<div></div>
<div class="length">800</div>
<div></div>
<div class="status">
<img src="xxxx" alt="open">
</div>
</div>
<div class="entry">
<div></div>
<div></div>
<div class="length">2300</div>
<div></div>
<div class="status">
<img src="xxxx" alt="closed">
</div>
</div>
</div>';
$dom = new DOMDocument();
$dom->loadHtml( $strhtml );/* alternative to loading a file directly */
$dom->preserveWhiteSpace = false;
$xp = new DOMXPath($dom);
$col=$xp->query('//img[#alt="open"]');/* target the nodes with the attribute you need to look for */
/* variable to increment with values found from DOM values */
$length=0;
foreach( $col as $n ) {/* loop through the found nodes collection */
$parent=$n->parentNode->parentNode;/* corrected here to account for change in html layout ~ get the suitable parent node */
/* based on original code, find value from particular node */
$length += $parent->childNodes->item(5)->nodeValue;
}
echo 'Length:'.$length;

Related

select children of the first element of a certain class using XPath

i have this type of code:
<div class="content">
<p></p>
<p></p>
<p></p>
</div>
<div class="content">
<p></p>
<p></p>
<p></p>
</div>
i wish to select all p elements from the first element with the class content.
i managed to select the first class by using:
(//div[#class="content"])[1]
but using (//div[#class="content"])[1]/p it still shows both classes
Here's an working example using PHP's SimpleXML. I've made some small changes to the HTML code you provided so the output would be more meaningful.
Regarding the XPath expression you provided I just removed the parenthesis and it all worked as expected.
NOTE: Following #LarsH's comment, I reverted the XPath expression as it was OK for starters. I took the liberty to update it based on its example.
<?php
$html = <<<HTML
<body>
<div class="content">
<p>1</p>
<p>2</p>
<p>3</p>
</div>
<div class="content">
<p>4</p>
<p>5</p>
<p>6</p>
</div>
<div>
<div class="content">
<p>7</p>
<p>8</p>
<p>9</p>
</div>
</div>
</body>
HTML;
$sxe = new SimpleXMLElement($html);
foreach ($sxe->xpath('(//div[#class="content"])[1]/p') as $p) {
echo "$p\n";
}
Output:
1
2
3
Link to codepad working example.

Php to auto populate grids

I have the following html code:
<div class="media row-fluid">
<div class="span3">
<div class="widget">
<div class="well">
<div class="view">
<img src="img/demo/media/1.png" alt="" />
</div>
<div class="item-info">
Title 1
<p>Info.</p>
<p class="item-buttons">
<i class="icon-pencil"></i>
<i class="icon-trash"></i>
</p>
</div>
</div>
</div>
<div class="widget">
<div class="well">
<div class="view">
<img src="img/demo/media/2.png" alt="" />
</div>
<div class="item-info">
This is another title
<p>Some info and details go here.</p>
<p class="item-buttons">
<i class="icon-pencil"></i>
<i class="icon-trash"></i>
</p>
</div>
</div>
</div>
</div>
Which basically alternates between a span class with the widget class, and then the widget class without the span3 class.
What I wanted to know was if there was a way to have php "echo" or populate the details for and details under the "item-info" class. Would I need to use a foreach statement to get this done? I would be storing the information in a mysql database, and while I can get it to fill in the info one by one (repeatedly entering the and echoing out each image and item title) it's not practical when the content needed to be displayed is over 15 different items. I'm not well versed in foreach statements so I could definitely use some help on it.
If someone could help me perhaps structure a php script so that it can automatically output the html based on the number individual items in the database, that'd be greatly appreciated!
I'm wondering if the html + php (not including the foreach) would look like this:
<div class="span3">
<div class="widget">
<div class="well">
<div class="view">
<img src="img/<? $file ?>" alt="" />
</div>
<div class="item-info">
<?$title?>
<p>Info.</p>
<p class="item-buttons">
<i class="icon-pencil"></i>
<i class="icon-trash"></i>
</p>
</div>
</div>
</div>
EDIT:
I wanted to add some more information. The items populated would be based on a type of subscription - which will be managed by a group id.
I was initially going to use <? (if $_SESSION['group_id']==1)>
echo <div class="item-info">
$title
<p>$info</p>
</div>
so that only the subscribed items would populate. But, I would need it to iterate through all the items for group1 table and list it. Currently I know that I can do
<? (if $_SESSION['group_id']==1)
while ($row=mysql_fetch_assoc($sqlItem))
{
$itemInfo = $row['info'];
$image = $row['image'];
$title = $row['title'];
$url = $row['url'];
};
>
$sqlItem for now can only be assigned one thing (manually - as in: $sqlItem = '123'), unless I iterate through which is what I'm trying to figure out.
Just read that 'mysql_fetch_assoc' is being depreciated with 5.5, here is the new way and looks better, easier I think.. Hope this helps, was updated today.
I hope this helps http://php.net/manual/en/mysqli-stmt.fetch.php
replace the printf with echo '//then your html stuff
This will iterate through the rows in your database until their are no more matching records.
shouldn't a while be enough? It depends on the structure of your database and website (we didn't need so much HTML I think. Some more PHP maybe). Hope this helps.

How do I loop through multiple child nodes of XML?

I am having some trouble trying to loop through an XML document. The XML looks like this:
<data>
<weather>
<hourly>
<time>0</time>
<tempC>17</tempC>
<tempF>62</tempF>
<windspeedMiles>24</windspeedMiles>
<windspeedKmph>39</windspeedKmph>
</hourly>
<hourly>
<time>3</time>
<tempC>16</tempC>
<tempF>60</tempF>
<windspeedMiles>22</windspeedMiles>
<windspeedKmph>35</windspeedKmph>
</hourly>
</weather>
<weather>
<hourly>
<time>0</time>
<tempC>17</tempC>
<tempF>62</tempF>
<windspeedMiles>24</windspeedMiles>
<windspeedKmph>39</windspeedKmph>
</hourly>
<hourly>
<time>3</time>
<tempC>16</tempC>
<tempF>60</tempF>
<windspeedMiles>22</windspeedMiles>
<windspeedKmph>35</windspeedKmph>
</hourly>
</weather>
</data>
My code (below) whilst it loops through all 'weather' nodes, it only picks out the first 'hourly' child node and completely skips the second. Would someone be able to help me as if I am honest, I do not know enough about looping to fix it and its driving me nuts! Grr.
Here is my PHP code which loads an XML document from online and then formats the XML results into div tags and obviously loops through the XML but as I said only loops through the first 'hourly' node of each 'weather' node.
<?php
// load SimpleXML
$data = new SimpleXMLElement('myOnlineXMLdocument.xml', null, true);
echo <<<EOF
<div class="observationRow">
<div class="observationTitleSmall"><br>Time</div>
<div class="observationTitleSmall"><br>Temp C</div>
<div class="observationTitleSmall"><br>Temp F</div>
<div class="observationTitleSmall"><br>Wind Speed MPH</div>
<div class="observationTitleSmall"><br>Wind Speed KMPH</div>
</div>
EOF;
foreach($data as $weather) // loop through our hours
{
echo <<<EOF
<div>
<div class="observationCellSmall"><br>{$weather->time}</div>
<div class="observationCellSmall"><br>{$weather->tempC}</div>
<div class="observationCellSmall"><br>{$weather->tempF}</div>
<div class="observationCellSmall"><br>{$weather->hourly->windspeedMiles}</div>
<div class="observationCellSmall"><br>{$weather->hourly->windspeedKmph}</div>
EOF;
}
echo '</div>';
?>
EDITED CODE:
$str = "";
foreach($data->weather as $weather)
{
foreach ($weather->hourly as $hour)
{
$str .= "
<div>";
if ($hour->time == "0") {
$str .= "
<div class='observationCellSmall'><br>$weather->date</div>
<div class='observationCellSmall'><br>$weather->maxtempC</div>
<div class='observationCellSmall'><br>$weather->mintempC</div>";
}
$str .= "
<div class='observationCellSmall'><br>$hour->time</div>
<div class='observationCellSmall'><br>$hour->tempC</div>
<div class='observationCellSmall'><br>$hour->tempF</div>
<div class='observationCellSmall'><br>$hour->windspeedMiles</div>
<div class='observationCellSmall'><br>$hour->windspeedKmph</div>
</div>
";
}
}
echo $str;
Using a slenderized version of your XML feed, that generates this:
<div>
<div class='observationCellSmall'><br>2013-08-19</div>
<div class='observationCellSmall'><br>17</div>
<div class='observationCellSmall'><br>15</div>
<div class='observationCellSmall'><br>0</div>
<div class='observationCellSmall'><br>15</div>
<div class='observationCellSmall'><br>59</div>
<div class='observationCellSmall'><br>11</div>
<div class='observationCellSmall'><br>18</div>
</div>
<div>
<div class='observationCellSmall'><br>300</div>
<div class='observationCellSmall'><br>15</div>
<div class='observationCellSmall'><br>59</div>
<div class='observationCellSmall'><br>13</div>
<div class='observationCellSmall'><br>21</div>
</div>
<div>
<div class='observationCellSmall'><br>2013-08-20</div>
<div class='observationCellSmall'><br>21</div>
<div class='observationCellSmall'><br>16</div>
<div class='observationCellSmall'><br>0</div>
<div class='observationCellSmall'><br>17</div>
<div class='observationCellSmall'><br>62</div>
<div class='observationCellSmall'><br>11</div>
<div class='observationCellSmall'><br>18</div>
</div>
<div>
<div class='observationCellSmall'><br>300</div>
<div class='observationCellSmall'><br>16</div>
<div class='observationCellSmall'><br>61</div>
<div class='observationCellSmall'><br>10</div>
<div class='observationCellSmall'><br>17</div>
</div>
You need a nested loop. One to loop over the weathers, and and another to loop over the hourlies.
foreach($data->weather as $weather) {
foreach($weather->hourly as $hourly) {
// code here
}
}
I don't remember the simplexml API 100% off my head, if that doesn't work you might need to use ->getChildren() or something to make it iterable.
Either that, or use xpath and nab the hourlies directly: /data/weather/hourly.

php changing div using DOMDocument but doesn't update page

I'm not sure what I'm doing wrong but I'm getting the right nodeValue for what I want. It's just not updating when the php script is done. Here's the code:
$dom = new DOMDocument();
//suppress HTML5 and other errors
libxml_use_internal_errors(true);
$dom->loadHTMLFile($pageURL);
libxml_use_internal_errors(false);
$xpath = new DOMXPath($dom);
$divContent = $xpath->query("//*[#id='resultStats']/p")->item(0);
$newText = new DOMText("100 results");
var_dump($divContent->nodeValue); //returns old test value "400 results" which is correct
$divContent->removeChild($divContent->firstChild);
$divContent->appendChild($newText);
var_dump($divContent->localName); //"p" because i got it from <p> in resultStats
var_dump($divContent->textContent); //"100 results"
var_dump($divContent->nodeValue); //"100 results"
more of the HTML that is around it
<div class="container">
<div class="row">
<div class="resultStats span3 offset1" id="resultStats">
<p>400 results found.</p>
</div>
</div>
<div class="row">
<div class="span12">
<div class="row">
<div class="span6 offset1">
<?php
if (isset($_POST['q'])) {
//code from above that is executing every time from tests
}
?>
</div>
<div class="span5">
span5
</div>
</div>
</div>
</div>
I'm not sure what I'm doing wrong. If I do dom->save it rewrites everything (even php code) so I don't think that's a good idea.
I don't understand why you're using DOMDocument for this. Can't you just do this:
<div class="container">
<div class="row">
<div class="resultStats span3 offset1" id="resultStats">
<?php
// get new result count somehow in $resultCount
echo '<p>'.$resultCount.' results found</p>';
?>
</div>
</div>

regex match html element with html children [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to parse and process HTML with PHP?
I wasn't sure how to phrase this question.
Basically I have this php code:
$new_html = preg_replace('!<div.*?id="spotlight".*?>.*?</div>!is', '', $html);
I want this to change html code from this (example, not actual html):
<div id="container">
<div id="spotlight">
<!-- empty -->
</div>
<div id="content">
<!-- lots of content -->
</div>
</div>
To this:
<div id="container">
<div id="content">
<!-- lots of content -->
</div>
</div>
As you can see the php code will do this successfully, because the regex is looking for:
<div{anything}id="spotlight"{anything}>{anything}</div>
However
if the div id="spotlight" contains a child div like so:
<div id="container">
<div id="spotlight">
<div></div>
</div>
<div id="content">
<!-- lots of content -->
</div>
</div>
then the regex will match the end div tag of the child div!
How do i prevent this? How to i tell regex to ignore the closing div if another div was opened?
Thanks
Use DOMDocument:
$html = '<div id="container">
<div id="spotlight">
<!-- empty -->
</div>
<div id="content">
<!-- lots of content -->
</div>
</div>';
$dom = new DOMDocument;
$dom->loadXML($html);
$xpath = new DOMXPath($dom);
$query = '//div[#id="spotlight"]';
$entries = $xpath->query($query);
foreach($entries as $one){
$one->parentNode->removeChild($one);
}
echo $dom->saveHTML();
Codepad Example
$a = preg_replace('/<div[^>]+>\\s+<\/div>/', '', $a);

Categories