href und URL zusammensetzen
März 2010
Wenn man beispielsweise einen Robot oder Crawler mit PHP bastelt, ist man meistens gezwungen die Hypertext-Referenzen (href-Attribute) der Links auszulesen. Doch kann man so noch nicht auf die nächste Seite zugreifen, den man besitzt ja die vollständige URL nicht.
Folgende Funktion kombiniert die href-Angabe und die gegebene URL korrekt:
Ist die dritte Variable $killAnchor auf true gesetzt (standardmässig), werden eventuell vorhandene Anker bei einer href entfernt.
Tipp: Manche Websites verwenden ein <base>-Tag. Wenn dem so ist, muss für $url der Wert des <base>-Tag genommen werden, da die Ergebnis-Links sonst womöglich ungültig sind.
Seite: http://example.com/sub/page.php
Verweis: <a href="../otherpage.html">Link</a>
Um an diese zu kommen, müssen die URL und die relative Link-Angabe korrekt zusammengesetzt werden. Das klingt zwar einfach, hat allerdings gewisse Tücken.Folgende Funktion kombiniert die href-Angabe und die gegebene URL korrekt:
<?php
function combineHrefAndUrl($hrefs, $url, $killAnchor=true){
if(!is_array($hrefs)){
$hrefs = array($hrefs);
$noArray = true;
}
// Fehlernder Root-Slash anfügen
if(preg_match('#^[a-z]+://[^/]+$#i', $url)) $url .= '/';
// ungültige URL
if(!preg_match('#^[a-z]+://[^/]+/.*$#i', $url))
trigger_error('Invalid URL: '.htmlspecialchars($url), E_USER_WARNING);
// Bestandteile der URL
$levels = array();
$levels['full'] = $url; // unveränderte URL "http://host/dir/file?get#anchor"
$levels['url'] = preg_replace('#^([^\#]*)\#.*$#', '$1', $levels['full']); // ohne Anker "http://host/dir/file?get"
$levels['file'] = preg_replace('#^([^\?]*)\?.*$#', '$1', $levels['url']); // ohne Parameter "http://host/dir/file"
$levels['dir'] = preg_replace('#/[^/]*$#s', '/', $levels['file']); // ohne Datei "http://host/dir/"
$levels['root'] = preg_replace('#^([a-z]+://[^/]+)/.*$#i', '$1', $levels['dir']); // ohne Verzeichnis "http://host"
$levels['scheme'] = preg_replace('#^([a-z]+:).*$#i', '$1', $levels['root']); // ohne Host "http:"
// Die Stücke zusammensetzen
foreach($hrefs as &$href){
$href = html_entity_decode($href, ENT_QUOTES, 'UTF-8');
if(preg_match('#^[a-z]+:#i', $href)) /*void*/; // href ist vollständige URL "http://example.com/file.htm"
elseif(substr($href, 0, 2)==='//') $href = $levels['scheme'].$href; // href ohne Protokoll "//example.com/file.htm"
elseif($href[0]==='/') $href = $levels['root'].$href; // href relativ zum Root "/sub/file.htm"
elseif($href[0]==='?') $href = $levels['file'].$href; // href: nur Parameter "?param=val"
elseif($href[0]==='#') $href = $levels['url'].$href; // href: nur Anker "#anchor"
elseif(substr($href, 0, 2)==='./') $href = $levels['dir'].substr($href, 2); // href: gleiches Verzeichnis "./file.htm"
else{ // href: höheres oder gleiches Verzeichnis "../file.htm", "file.htm"
$levels['tmpdir'] = $levels['dir'];
while(substr($href, 0, 3)==='../'){
$href = substr($href, 3);
if(substr_count($levels['tmpdir'], '/')>3)
$levels['tmpdir'] = preg_replace('#/[^/]*/[^/]*$#', '/', $levels['tmpdir']);
}
$href = $levels['tmpdir'].$href;
}
// Fehlernder Root-Slash anfügen
if(preg_match('#^[a-z]+://[^/]+$#', $href)) $href .= '/';
// Host to lowercase
$href = preg_replace('#://([^/]+)/#e', '"://".strtolower("$1")."/"', $href);
// Anker abschneiden
if($killAnchor)
$href = preg_replace('#^([^\#]*)\#.*$#', '$1', $href);
}
return isset($noArray) ? array_pop($hrefs) : $hrefs;
}
?>
Diese Funktion erlaubt als $href einen einfachen String sowohl als auch ein Array.Ist die dritte Variable $killAnchor auf true gesetzt (standardmässig), werden eventuell vorhandene Anker bei einer href entfernt.
<?php
// die Seite
$url = 'http://example.com/dir/catalog.php?page=3';
// die Verweise
$hrefs = array(
'/', # http://example.com/
'/anywhere.htm', # http://example.com/anywhere.htm
'./', # http://example.com/dir/
'./like.php', # http://example.com/dir/like.php
'like.php?sid=d82eb5', # http://example.com/dir/like.php?sid=d82eb5
'../sub/other.htm', # http://example.com/sub/other.htm
'../../../../../sub/other.htm', # http://example.com/sub/other.htm
'#bottom', # http://example.com/dir/catalog.php?page=3#bottom
'?page=4', # http://example.com/dir/catalog.php?page=4
'http://example.net/there.asp', # http://example.net/there.asp
'mailto:service@example.com', # mailto:service@example.com
);
$results = combineHrefAndUrl($hrefs, $url, false);
// (Ergebnis siehe oben)
?>
Tipp: Manche Websites verwenden ein <base>-Tag. Wenn dem so ist, muss für $url der Wert des <base>-Tag genommen werden, da die Ergebnis-Links sonst womöglich ungültig sind.
