xPath insert before and after - With DOM and PHP - php

I need to add a class to a HTML structure.
My class is called "container" and should start right after <div><ul><li></h4> (the child of ul and its simblings, not grandchilds) and should end right before the closing of the same element.
My whole code looks like this:
<?php
$content = '
<div class="sidebar-1">
<ul>
<li>
<h4>Title</h4>
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</li>
<li>
<p>Paragraf</p>
</li>
<li>
<h4>New title</h4>
<ul>
<li>Some text</li>
<li>Some text åäö</li>
</ul>
</li>
</ul>
</div>
';
$doc = new DOMDocument();
$doc->loadHTML($content);
$x = new DOMXPath($doc);
$start_text = '<div class="container">';
$end_text = '</div>';
foreach($x->query('//div/ul/li') as $anchor)
{
$anchor->insertBefore(new DOMText($start_text),$anchor->firstChild);
}
echo $doc->saveXML($doc->getElementsByTagName('ul')->item(0));
?>
It works as far as i can add the class opening but not the closing element. I also get strange encoding doing this. I want the output to be the same encoding as the input.
The result should be
<div class="sidebar-1">
<ul>
<li>
<h4>Title</h4>
<div class="content">
<ul>
<li>Test</li>
<li>Test</li>
</ul>
</div>
</li>
<li>
<div class="content">
<p>Paragraf</p>
</div>
</li>
<li>
<h4>New title</h4>
<div class="content">
<ul>
<li>Some text</li>
<li>Some text åäö</li>
</ul>
</div>
</li>
</ul>
</div>

I couldn't find a more elegant way to reassign all children, so I guess this will do. I think it gets what you're after, though.
(NOTE: Code updated to reflect additional requirements in the comments.)
$doc = new DOMDocument();
$doc->loadHTML($content);
$x = new DOMXPath($doc);
foreach($x->query('//div/ul/li') as $anchor)
{
$container = $doc->importNode(new DOMElement('div'));
$container->setAttribute('class', 'container');
$next = $anchor->firstChild;
while ($next !== NULL) {
$curr = $next;
$next = $curr->nextSibling;
if (($curr->nodeName != 'h4')
|| ($curr->attributes === NULL)
|| ($curr->attributes->getNamedItem('class') === NULL)
|| !preg_match('#(^| )title( |$)#', $curr->attributes->getNamedItem('class')->nodeValue)
) {
$container->appendChild($anchor->removeChild($curr));
}
}
$anchor->appendChild($container);
}
As for character encoding, I've been messing with it for a while and it's a tricky issue. The characters display correctly when you load with loadXML() but not with loadHTML(). There's a workaround in the comments, but it ain't pretty. Hopefully some of the user comments will help you can find a usable solution.

Related

PHP DOMXPath query to return value

I want to get and show the title, tel, fax and address if they exist.
HTML code could be like :
<div id="id1">
<div class="AB">
<ul>
<li class="title"> AGENCE X </li>
<li class="tel"> 060000000</li>
<li class="fax"> 06000000</li>
<li class="address> this is the address </li>
</ul>
</div>
<div class="AB"> //the same class name
<ul>
<li class="titre"> AGENCE X </li>
<li class="tel"> 060000000</li>
<li class="fax"> 06000000</li>
</ul>
</div>
<div>...</div>
</div>
I wrote this code but I didn't know how to write the condition "if a node with class name 'address' or 'fax' or 'tel' exist' then do X.
Here is my code:
$doc = new DOMDocument();
#$doc->loadHTMLFile('http://website.com');
$node = $doc->getElementsByTagName('div') ;
$xpath=new DOMXPath($doc);
$titre=$xpath->query('//div/ul/li[#class="titre"]');
$adresse=$xpath->query('//div/ul/li[#class="adresse"]');
$phone=$xpath->query('//div/ul/li[#class="phone"]');
$fax=$xpath->query('//div/ul/li[#class="fax"]');
$a=0;$b=0;$c=0;
for($i=0;$i<$titre->length;$i++)
{
echo $titre->item($i)->nodeValue.'<br/>' ;
if(a "li" has classe="adresse" existe)
{ for($a=0;$a<$adresse->length;$a++)
{
echo $adresse->item($a)->nodeValue.'<br/>' ;
$a++;
}
}
if(a "li" has classe="titre" existe){ for($b=0;$b<$phone->length;$b++)
{
echo $titre->item($b)->nodeValue.'<br/>' ;
$b++;
}
}
if(a "li" has classe="fax" existe) { for($c=0;$c<$fax->length;$c++)
{
echo $fax->item($c)->nodeValue.'<br/>' ;
$c++;
}
}
}
Could someone tell me how can I would write this condition or another solution?

Show a hierarchy in PHP

I have a database which stores a hierarchy of foods.
Category(id_cat,name_cat);
his_low_cat(id_cat,id_low_cat);
A category can have 0..n low category. If it had no lower category I do a id_cat,-1 field in his_low_cat.
I do not know if it's possible but I would like to show it in a kind of "pulldown menu"
(if you have any other idea on how to show a full hierarchy please suggest it)
Like this :
echo " <div id=\"menu\">
<ul class=\"niveau1\">
<li class=\"sousmenu\">Food
<ul class=\"niveau2\">
<li class=\"sousmenu\">Sous menu 1.1
<ul class=\"niveau3\">
<li>Sous sous menu 1.1.1</li>
</ul>
</li>
<li>Sous menu 1.2</li>
</ul>
</li>
</ul>
</div>";
My first cat is "food" and then it derives into 4 lowers categories, which derive themselves in more.
The problem is that it must be dynamic and load field from my database.
The goal would be to be able to catch the clicked value and use it in another .php
How would I do this?
Recursion is definitely the way to go with this problem, I've coded up this solution:
<?php
function nestElements($elements, $depth=0)
{
foreach($elements as $elementName=>$element)
{
echo str_repeat("\t", $depth).'<ul class="niveau'.($depth+1).'">'."\n";
if(is_array($element))
{
echo str_repeat("\t", $depth+1)."<li class=\"sousmenu\">${elementName}\n";
nestElements($element, $depth+2);
echo str_repeat("\t", $depth+1)."</li>\n";
}
else
{
echo str_repeat("\t", $depth+1)."<li class=\"sousmenu\">${elementName}</li>\n";
}
echo str_repeat("\t", $depth)."</ul>\n";
}
}
nestElements(array("Food"=>array("Meat"=>array("Poultry"=>array("Chicken"=>"Meat/Poultry/Chicken"), "Beef"=>array("Hamburgers"=>"Meat/Beef/Hamburgers", "Steak"=>"Meat/Beef/Steak")), "Dairy"=>array("Cow"=>"Dairy/Cow", "Sheep"=>"Dairy/Sheep")), "name"=>"url"));
?>
Testing with this:
<?php
nestElements(array("Food"=>array("Meat"=>array("Poultry"=>array("Chicken"=>"Meat/Poultry/Chicken"), "Beef"=>array("Hamburgers"=>"Meat/Beef/Hamburgers", "Steak"=>"Meat/Beef/Steak")), "Dairy"=>array("Cow"=>"Dairy/Cow", "Sheep"=>"Dairy/Sheep")), "name"=>"url"));
?>
Results in:
<ul class="niveau1">
<li class="sousmenu">Food</li>
<ul class="niveau2">
<li class="sousmenu">Meat</li>
<ul class="niveau3">
<li class="sousmenu">Poultry</li>
<ul class="niveau4">
<li class="sousmenu">Chicken</li>
</ul>
</ul>
<ul class="niveau3">
<li class="sousmenu">Beef</li>
<ul class="niveau4">
<li class="sousmenu">Hamburgers</li>
</ul>
<ul class="niveau4">
<li class="sousmenu">Steak</li>
</ul>
</ul>
</ul>
<ul class="niveau2">
<li class="sousmenu">Dairy</li>
<ul class="niveau3">
<li class="sousmenu">Cow</li>
</ul>
<ul class="niveau3">
<li class="sousmenu">Sheep</li>
</ul>
</ul>
</ul>
<ul class="niveau1">
<li class="sousmenu">name</li>
</ul>
To parse it you'd have to make a mod_rewrite which redirects to index.php?r=TheURL and from their, explode the r parameter using "/" as the delimeter, then you have a list of menus and submenus that the clicked link was from. By adding another parameter the url coul be automatically generated.
Edit: Fixed problem with original code output seen below
<li class="sousmenu">Sheep</li>
<li class="sousmenu">Sheep</li>
To generate the array:
<?php
function genArray(&$targetArray, $parentID=null){
$res=(is_null($parentID))?mysql_query("SELECT * FROM categorie WHERE id_cat NOT IN (SELECT id_low_cat FROM hislowcat) ORDER BY id_cat DESC;"):mysql_query("SELECT *, (SELECT name_cat FROM categorie WHERE id_cat= '".$parentID ."') AS name_cat FROM hislowcat WHERE id_cat= '" .$parentID ."'");
if(!is_null($parentID) && !mysql_num_rows($res))
{
$res3=mysql_query("SELECT name_cat FROM categorie WHERE id_cat='${parentID}';");
$row3=mysql_fetch_array($res3);
$targetArray[$row3['name_cat']]=$row3['name_cat'];
return;
}
while(($row=mysql_fetch_array($res)))
{
//echo $row->name_cat;
if(is_null($parentID))
{
if(!isset($targetArray[$row['name_cat']]))
{
$targetArray[$row['name_cat']]=array();
}
genArray($targetArray[$row['name_cat']], $row['id_cat']);
}
else
{
genArray($targetArray[$row['name_cat']], $row['id_low_cat']);
}
}
}
$array=array();
genArray($array);
print_r($array);
?>
Notice how $targetArray is set up as a reference, this way we can treat it one-dimensionally.

Adding a class name to every fouth item in a forloop

I'm used to doing this with Django and it's fairly simple, so trying to workout how it's done in PHP. What I'd like to do is within a PHP for foreach loop I would like to add the class name of 'last' to every fourth item in the list.
PHP code:
<?php
$products = array();
$product_counter = 0;
foreach ($_productCollection as $_product)
{
?>
So this is my current HTML output:
<li>
<div class="contentWrap"> content here </div>
</li>
<li>
<div class="contentWrap"> content here </div>
</li>
<li>
<div class="contentWrap"> content here </div>
</li>
<li>
<div class="contentWrap"> content here </div>
</li>
However, what I would like to acheive is:
<li>
<div class="contentWrap"> content here </div>
</li>
<li>
<div class="contentWrap"> content here </div>
</li>
<li>
<div class="contentWrap"> content here </div>
</li>
<li>
<div class="contentWrap last"> content here </div>
</li>
I was using the nth child attribute in CSS3 but it needs to be supported in IE8!
Anyway, PHP isn't my strong point and can't make use of the examples i've found online so any help would be much appreciated.
Thanks
Use something like this:
$counter = 0;
foreach ($_productCollection as $_product) {
//whatever
if ($counter % 4 == 3) {
//do stuff for every 4th element
}
$counter++;
}
Simple right?
for($i = 0; $i < $countOfArray; $i++) {
if($i % 4 == 3) {
// add last class ....
}
}

How to extract value form HTML section in PHP

I need to extract data from HTML page which looks like:
<li>
<h2>
<span>rss</span>AC Ajaccio</h2>
<div class="club-left">
<img src="http://medias.lequipe.fr/logo-football/35/60?CCH-13-40" width="60" height="60">
</div>
<div class="club-right">
<ul class="club-links">
<li><span class="plus"></span>
Fiche club
</li>
<li><span class="plus"></span>
Calendrier
</li>
<li><span class="plus"></span>Effectif
</li>
<li><span class="plus"></span>
Stats joueurs
</li>
<li><span class="plus"></span>
Stats club
</li>
</ul>
</div>
<div class="clubt hidden">35</div>
<div class="clear"></div>
</li>
I would like to extract in PHP the href value and the text of this part:
**Stats joueurs**
I use the following code, but there is something missing:
$elements = $xpath->query("//div[#id='Base']/ul/li");
if (!is_null($elements)) {
foreach ($elements as $element) {
$nodes = $element->childNodes;
foreach ($nodes as $node) {
if($node->nodeName!='#text'){
echo $node->nodeValue.";<br/>";
$stringData = trim($node->nodeValue).";";
}
}
}
UPDATE:
Try:
$elements = $xpath->query("//ul[#class='club-links']//a");
foreach ($elements as $element) {
echo $element->nodeValue." - ".$element->getAttribute("href")."<br/>";
}

php DOMDocument: html to node tree

This function converts html into a node tree (<ul> structure). However, every node is returned as a child node of the preceding node even if that node was a sibling of the current node.
$xml = '
<div>
<div>
<b></b>
</div>
<p></p>
</div>
';
function xml2array($xml,&$result = '') {
foreach($xml->children() as $name => $xmlchild) {
xml2array($xmlchild, $result);
}
$result = "<ul><li>".$xml->getName().$result."</li></ul>";
}
$result='';
$dd = xml2array(simplexml_load_string($xml), $result);
echo "<pre>";
print_r($result);
the above code returns this:
<ul>
<li>div
<ul>
<li>p
<ul>
<li>div
<ul>
<li>b</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
you notice 'div' is now a child of 'p' even though they are siblings, and 'div' comes after 'p' unlike the orignal input.
this is how it should look:
<ul>
<li>div
<ul>
<li>div
<ul>b</ul>
</li>
<li>p</li>
</ul>
</li>
</ul>
Here you go, a non-broken function, with the call by reference removed because if you don't know exactly what they are, possibilities of obscure bugs occur (and you might know how/when to use them, the next coder might struggle, references are best used with a very clear reason or not at all):
<?php
$xml = '
<div>
<div>
<b></b>
</div>
<p></p>
</div>
';
function xml2ul($xml) {
$children = $xml->children();
if(empty($children)) return '';
$result = '<ul>';
foreach($xml->children() as $name => $xmlchild) {
$result .= '<li>'.$name.xml2ul($xmlchild).'</li>';
}
$result .= '</ul>';
return $result;
}
echo xml2ul(simplexml_load_string($xml), $result);

Categories