Christian Kruse: Hyperlink und Mailto automatisch erkennen und ergänzen

Beitrag lesen

Hoi,

Wenn man sich die Daten in einer Vorschau ansieht, würde ich gern
die Hyperlinks und E-mail adressen automatisch erkennen lassen und
entsprechend verlinken.

[...]

Aber wie formuliere ich das in PHP?

Das ist gar nicht so eine leichte Frage. Aus zwei Gruenden: erstens
sind RFC-gerechte URLs sehr kompliziert zu matchen. Und zweitens gibt
es da draussen im Netz leider sehr viele idiotische Programmierer, die
nicht RFC-gerechte URLs einsetzen. Man muss also einen Weg zwischen
Idealismus und Praxis finden.

Sicher ist auf jedenfall, dass du mit dem oben beschriebenen Verfahren
nicht sehr weit kommen wirst. Das Werkzeug, was du brauchst, waere
entweder ein richtiger Parser (IMHO ein fuerchterlicher Overhead in
dem Fall) oder eine RegEx, die die Ueberpruefung auf RFC-Konformitaet
annaehert. Einen solchen RegEx kannst du hier im Archiv finden, oder
auch in den Forums-Sourcen. Dass der allerdings manche Sachen (nicht
RFC-gerechte URLs, um genau zu sein) nicht schluckt, siehst du ja.
Und das er sehr kompliziert ist (im Buch ca. zwei Seiten), kommt auch
noch erschwerend dazu.

Ein beliebter Ausdruck, um URLs zu matchen, ist z. B. der
folgende (ich verwende hier PHP-Notation):

$text = preg_replace('!^(http://)?(\w+).(\w{3,5})$!',"<a href="$1$2$3">$1$2$3</a>",$text);

Der hat IMHO jedoch einige Nachteile:

  • er matcht nicht auf EMail-Adressen
  • er ist unkorrekt (er matcht z. B: hallo_ich_bin_ein_esel.de)
  • er ist ineffizient

Ich habe mal mich hingesetzt und einen RegEx zusammengestellt, der
IMHO in ungefaehr das liefert, was man in der Praxis so benoetigt:

$text = preg_replace('!^(http://|ftp://|mailto:[a-zA-Z0-9._-]+@)?([A-Za-z0-9-]+.)*([A-Za-z0-9-]{3,}.)([a-zA-Z0-9-]+.)*([a-z]{2,})(/[a-zA-Z%0-9]*)*(?[a-z%0-9&;+=,]*)?(#.*)?$!','<a href="$1$2$3$4$5$6$7$8">$1$2$3$4$5$6$7$8</a>',$text);

Diese RegEx hat nur einen einzigen Nachteil: wenn der User kein
http:// vor die URL schreibt, dann wird leider kein Protokoll
ergaenzt. Doch das ist durch das Execute-Flag fuer den RegEx
relativ leicht auszubuegeln, ich habe nur leider kein PHP hier, mit
dem ich das pruefen koennte.

Die RegEx ist, auch wenn sie kompliziert aussehen mag, eigentlich
sehr einfach. Ich nehm sie mal stueckchenweise auseinander:

^(http://|ftp://|mailto:[a-zA-Z0-9._-]+@)?

Hier wird nach einem Protokoll gesucht. Ich habe einfach mal FTP,
HTTP und Emails definiert. Wenn es eine EMail ist, wird nach dem
Usernamen geschaut.

([A-Za-z0-9-]+.)*

Hier wird nach dem ersten Teil der Domain geschaut. Der darf *nur*
aus den Zeichen A-Za-z0-9 und einem Punkt bestehen und muss
wenigstens ein Zeichen lang sein. Er *muss* aber nicht matchen
(dafuer steht das * am Ende), es kann auch sein, dass wir direkt
zu der Domain kommen (z. B. http://wwwtech.de).

([A-Za-z0-9-]{3,}.)

Hiermit definiere ich den Hauptteil der Domain. Der muss aus
wenigstens 3 Zeichen der bekannten Zeichenklasse bestehen.

([a-zA-Z0-9-]+.)*

Dem Haupt-Teil der Domain folgen weitere Teile. Sie alle muessen
aus der benannten Zeichenklasse bestehen.

([a-z]{2,})

Hiermit ist die Subdomain gemeint. Die muss aus wenigstens zwei
Zeichen bestehen.

(/[a-zA-Z%0-9.]*)*

Nun kann aber der URL auch noch ein Directory oder eine Pfad-Angabe
folgen. Diese Pfadangabe *muss* zwar nicht folgen, aber sie darf.

(?[a-z%0-9&;+=,]*)?

Der Pfadangabe darf auch noch ein Query-String folgen. Auch der ist
kein Zwang.

Jetzt ist die eigentliche Syntax-Ueberpruefung zuende. Es kann bei
URLs aber auch zu einem Anker kommen. Der Anker wiederum hat
keine Anforderungen: er darf aus beliebigen Zeichen bestehen. Deshalb
kommen wir zu

(#.*)?

So, ich hoffe, das hat jetzt geholfen.

Gruesse,
 CK