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.
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($hrefENT_QUOTES'UTF-8');
    if(
preg_match('#^[a-z]+:#i'$href)) /*void*/// href ist vollständige URL "http://example.com/file.htm"
    
elseif(substr($href02)==='//'$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($href02)==='./'$href $levels['dir'].substr($href2); // href: gleiches Verzeichnis "./file.htm"
    
else{ // href: höheres oder gleiches Verzeichnis "../file.htm", "file.htm"
      
$levels['tmpdir'] = $levels['dir'];
      while(
substr($href03)==='../'){
        
$href substr($href3);
        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$urlfalse);
// (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.
 


Andere Einträge