Wstęp.
Dziś odcinek w stylu Indiany Jones'a. Pobawimy się trochę linami... tzn. linkami. Szykuj się na przemierzanie gęstej dżungli węzłów!
Scena pierwsza: "Linki zewnętrzne".
//a[substring(@href,1,7)="http://" or substring(@href,1,8)="https://" or substring(@href,1,6)="ftp://"]
Ten przykład w razie potrzeby należy uzupełnić o inne protokoły.
Scena druga: "Zamiana IMG w A dla potrzeb lightbox'a".
Wykorzystamy pierwszą lepszą bibliotekę do tworzenia lightbox'a, czyli otwierania zdjęć w powiększeniu po kliknięciu na miniaturę. Jest to Lightbox by Lokesh Dhakar. Link znajdziesz na końcu wpisu.
Wymagania co do struktury HTML są następujące:
<a href="/img/duzy1.jpg" data-lightbox="gallery1"><img src="/img/maly1.jpg" alt="Obrazek 1"></a>
<a href="/img/duzy2.jpg" data-lightbox="gallery1"><img src="/img/maly2.jpg" alt="Obrazek 2"></a>
<a href="/img/duzy3.jpg" data-lightbox="gallery2"><img src="/img/maly3.jpg" alt="Obrazek 3"></a>
<a href="/img/duzy4.jpg" data-lightbox="gallery2"><img src="/img/maly4.jpg" alt="Obrazek 4"></a>
<a href="/img/duzy5.jpg" data-lightbox="gallery2"><img src="/img/maly5.jpg" alt="Obrazek 5"></a>
Każdy IMG jest owinięty w A, który posiada odnośnik do dużego zdjęcia.
Atrybut data-lightbox przypisuje obrazek do indywidualnej galerii. Mamy więc 5 obrazków i 2 odrębne galerie.
My posiadając w kodzie tylko tagi IMG zmienimy je tak, aby można było podpiąć pod wspomnianą bibliotekę.
Startujemy od:
<div>
<img src="/img/maly1.jpg" data-big-src="/img/duzy1.jpg" data-gallery-name="gallery1" alt="Obrazek 1"/>
<span>Tekst</span>
</div>
<img src="/img/maly2.jpg" data-big-src="/img/duzy2.jpg" data-gallery-name="gallery1" alt="Obrazek 2"/>
<img src="/img/maly3.jpg" data-big-src="/img/duzy3.jpg" data-gallery-name="gallery2" alt="Obrazek 3"/>
<img src="/img/maly4.jpg" data-big-src="/img/duzy4.jpg" data-gallery-name="gallery2" alt="Obrazek 4"/>
Dlaczego tam się znalazł DIV i SPAN wyjaśnię Ci za chwilę. Na razie wstępny kod PHP:
/* inicjalizacja DOMDocument jak zwykle w poprzedniej części wpisu */
$images = $xpath->query('//img[@data-big-src]');
foreach($images as $img){
$parent = $img->parentNode; /* rodzic <IMG> */
$a = $dom->createElement('a'); /* tworzymy <A> */
$a->setAttribute('href', $img->getAttribute('data-big-src')); /* kopiujemy href */
$a->setAttribute('data-lightbox', $img->getAttribute('data-gallery-name')); /* kopiujemy data-lightbox */
$img->removeAttribute('data-big-src'); /* usuwamy atrybuty z obrazka */
$img->removeAttribute('data-gallery-name');
$a->appendChild($img); /* wstawiamy obrazek do <A> */
$parent->appendChild($a); /* dołączamy <A> do rodzica <IMG> */
}
echo $dom->saveHTML();
Otrzymamy:
<div>
<span>Tekst</span>
<a href="/img/duzy1.jpg" data-lightbox="gallery1"><img src="/img/maly1.jpg" alt="Obrazek 1"></a>
</div>
<a href="/img/duzy2.jpg" data-lightbox="gallery1"><img src="/img/maly2.jpg" alt="Obrazek 2"></a>
<a href="/img/duzy3.jpg" data-lightbox="gallery2"><img src="/img/maly3.jpg" alt="Obrazek 3"></a>
<a href="/img/duzy4.jpg" data-lightbox="gallery2"><img src="/img/maly4.jpg" alt="Obrazek 4"></a>
Na pierwszy rzut oka wygląda ok, prawda? No, nie do końca, A dla pierwszego obrazka wpadł za SPAN, czyli zmienił miejsce, a być może tego nie chcemy. Odpowiedzialność za to zdarzenie ponosi metoda appendChild, która wstawiła A na koniec rodzica obrazka (czyli do DIV).
Jak tego uniknąć?
Musimy zmienić element IMG w A, w miejscu jego istnienia. Przysłuży nam się metoda replaceChild. Wywołujemy ją na rodzicu węzła, który chcemy podmienić, przekazując jako pierwszy argument nowy węzeł, a jako drugi stary węzeł. Ale w naszym przypadku nie możemy podmienić IMG na A, bo IMG jest już dzieckiem A. Musimy skopiować węzeł, wstawić kopię do A, a potem zmienić oryginalny IMG na A.
Wnętrzne pętli będzie teraz wyglądać tak:
foreach($images as $img){
$parent = $img->parentNode;
$imgCopy = $img->cloneNode(false); /* tworzymy kopię <IMG> */
$a = $dom->createElement('a');
$a->setAttribute('href', $imgCopy->getAttribute('data-big-src'));
$a->setAttribute('data-lightbox', $imgCopy->getAttribute('data-gallery-name'));
$imgCopy->removeAttribute('data-big-src');
$imgCopy->removeAttribute('data-gallery-name');
$a->appendChild($imgCopy);
$parent->replaceChild($a, $img); /* tu już swobodnie zmieniamy <IMG> na <A> */
}
Tym razem otrzymamy:
<div>
<a href="/img/duzy1.jpg" data-lightbox="gallery1"><img src="/img/maly1.jpg" alt="Obrazek 1"></a>
<span>Tekst</span>
</div>
<a href="/img/duzy2.jpg" data-lightbox="gallery1"><img src="/img/maly2.jpg" alt="Obrazek 2"></a>
<a href="/img/duzy3.jpg" data-lightbox="gallery2"><img src="/img/maly3.jpg" alt="Obrazek 3"></a>
<a href="/img/duzy4.jpg" data-lightbox="gallery2"><img src="/img/maly4.jpg" alt="Obrazek 4"></a>
Scena trzecia: "Kolorowanie linków w zależności od statusu strony".
Wykorzystamy tu XPath na linki zewnętrzne (scena pierwsza). Odwiedzimy każdą stronę docelową i w zależności czy jest aktywna czy nie, dodamy odpowiednią klasę. Linki niedziałające (nieistniejące domeny, niedziałające serwery, strony nieistniejące) oznaczymy kolorem czerwonym, działające zielonym.
Nasza nieprzetworzona strona:
i jej źródło:
<!DOCTYPE html>
<html>
<head>
<style>
a.link-broken{
color:#f00;
}
a.link-ok{
color:#080;
}
</style>
</head>
<body>
<ul>
<li>
<a href="http://tastronanapewnonieistnieje.com" class="class1">http://blablablabla.com</a>
</li>
<li>
<a href="http://www.google.com" class="class1">http://www.google.com</a>
</li>
<li>
<a href="http://kawalekkodu.pl/fire-in-the-hole-czyli-o-dziurach-w-mysql" class="class2">http://kawalekkodu.pl/fire-in-the-hole-czyli-o-dziurach-w-mysql</a>
</li>
<li>
<a href="http://kawalekkodu.pl/post/fire-in-the-hole-czyli-o-dziurach-w-mysql" class="class2">http://kawalekkodu.pl/post/fire-in-the-hole-czyli-o-dziurach-w-mysql</a>
</li>
<li>
<a href="http://kawalekkodu.pl/strona-nieistniejaca" class="class2 last">http://kawalekkodu.pl/strona-nieistniejaca</a>
</li>
</ul>
</body>
</html>
Kod PHP:
header('Content-Type:text/html; charset=utf-8');
/* wspieramy się własnym handlerem błędów, który zwróci wyjątek,
jeśli użyta w kodzie funkcja zwróci Warning
*/
set_error_handler(
function ($errno, $errstr, $errfile, $errline, array $errcontext)
{
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
});
/* tu powinna znaleźć się inicjalizacja DOMDocument,
DOMXPath oraz załadowanie źródła strony
*/
$anchors = $xpath->query('//a[substring(@href,1,7)="http://" or substring(@href,1,8)="https://"]');
foreach($anchors as $anchor) {
/* pobieramy atrybut href */
$url = $anchor->getAttribute('href');
/* sprawdzamy czy to poprawny URL */
if(filter_var($url, FILTER_VALIDATE_URL)){
try {
$headers = get_headers($url);
/* sprawdzamy wszystkie nagłówki, bo mogliśmy otrzymać przekierowanie 301 */
foreach($headers as $header) {
if (substr($header, 0, 5) === 'HTTP/') {
$status = explode(' ', $header)[1];
}
}
}
/* dzięki własnemu handlerowi możemy obsłużyć Warning */
catch(Exception $e) {
$status = false;
}
}
else {
$status = false;
}
/* jeśli status inny niż 200, to będziemy dodawać 'link-broken' */
if ($status != '200') {
$class = 'link-broken';
}
else {
$class = 'link-ok';
}
/* pobieramy aktualny atrybut class */
$currentClass = $anchor->getAttribute('class');
/* dodajemy do niego ustaloną klasę */
$anchor->setAttribute('class', $currentClass . (!empty($currentClass) ? ' ' : '') . $class);
}
/* zwracamy kod strony
ustawiliśmy na początku odpowiednie nagłówki,
więc dostaniemy wygenerowaną stronę,
a nie widoczne źródło
*/
echo $dom->saveHTML();
Przy przetworzeniu przez powyższy kod, otrzymamy:
Zakończenie.
Dotarliśmy do kresu dżungli. Należy Ci się odpoczynek. Nie zapominaj jednak o kapeluszu, przyda się jeszcze w kolejnych odcinkach.
Przydatne linki (znów linki!):
Biblioteka Lightbox by Lokesh Dhakar
Metoda replaceChild
Metoda cloneNode
Funkcja get_headers
"The tag is out there", czyli DOMXPath S01E01
"The tag is out there", czyli DOMXPath S01E02
"The tag is out there", czyli DOMXPath S01E03