Hej.
Som opfølgning til <news:Xns9208A5CE758D2hstidsen@212.54.64.135>
-tråden, vil jeg da også give mit besyv med. Eller om ikke andet
skrive lidt om min egen problemstilling (og give udtryk for at min
hjerne summer på dette tidspunkt af døgnet). Jeg har været heldig nok
til tidligere blot at have tekst i simpel udgave uden diverse
HTML-koder tillige at bekymre mig om.
Udgangspunktet er noget rå tekst, som vi skal præsentere på en
webside, hvor det, der umiddelbart forekommer som links, skal være
klikbare. Her må vi som udgangspunkt erkende, at da originalteksten
som sådan ikke har fulgt bestemte regler, kan vi allerhøjst foretage
kvalificerede gæt, og tage højde for fx almindelig grammatik som
sætningsafsluttende punktummer og udråbstegn, samt fx kommaer og
semikolon. Det er dog mindre relevant.
Eksempel:
Gå ind på
http://php.net/.
Ønsket resultat:
Gå ind på <a href="
http://php.net/">http://php.net/.
Eksempel:
Gå ind på <
http://php.net/>.
Ønsket resultat:
Gå ind på <<a href="
http://php.net/">http://php.net/>.
Som jeg ser det, har vi en "hønen eller ægget"-problemstilling, fordi
vi rent faktisk skal foretage to funktioner, der lidt modarbejder
hinanden:
1. Vi skal præsentere originalteksten på en webside. Her plejer
htmlspecialchars() at være vores ven.
2. Vi skal søge efter links med et passende regulært udtryk. Der
findes mange varianter, men lad os fx bruge
((ht|f)tp://[/a-zA-Z0-9:%_\.,~#?=&;+-]+[/a-zA-Z0-9:%_~#?-])
der bl.a. sørger for at sidste tegn ikke er et punktum eller komma.
Udtrykket er dog mindre relevant i denne forbindelse.
Udfører vi først #1, så #2, så får vi medtaget alt for meget.
Eksempelvis hvis man har tekst i stil med:
Gå ind på <
http://php.net/>.
Selvom vores regulære udtryk ikke medtager "<" og ">", så vil det
efter #1 blive til:
Gå ind på <
http://php.net/>.
"&"-tegnet bliver vi i sagens natur nødt til at medtage i vores link,
da det jo sagtens kan optræde i en URL. Det samme kan siges om ";". Så
nu kommer vi måske til at matche "
http://php.net/>" med vores
regulære udtryk.
Udfører vi først #2, så #1, så bliver den HTML, vi lægger ind over
links, også konverteret. Altså:
Gå ind på <
http://php.net/>.
bliver til
Gå ind på <<a href="
http://php.net/">http://php.net/>.
.... og derefter til:
Gå ind på <<a
href="
http://php.net/">http://php.net/</a>>.
Vi er således slet ikke nået meget længere.
Jeg har prøvet at lege lidt med hvilke muligheder, der så er. Pt. har
jeg rodet med to workarounds:
1. Lav vores egen htmlspecialchars-agtige funktionalitet, der så at
sige behandler & anderledes end andre. Dette gøres ved at ændre
tegnene til forskellige "tags", hvor kun & bliver medtaget af vores
regulære udtryk. Et eksempel:
>function autohtmlencode ($string) {
> $string = str_replace("<","!__ENCODE_LEFTTAG__",$string);
> $string = str_replace(">","!__ENCODE_RIGHTTAG__",$string);
> $string = str_replace("&","__ENCODE_AMPERSAND__",$string);
> $string = str_replace("\"","!__ENCODE_QUOTE__",$string);
> $string = preg_replace("!((ht|f)tp://[/a-zA-Z0-9:%_\.,~#?=&;+-]+[/a-zA-Z0-9:%_~#?-])!","<a href=\"\\1\">\\1",$string);
> $string = str_replace("!__ENCODE_LEFTTAG__","<",$string);
> $string = str_replace("!__ENCODE_RIGHTTAG__",">",$string);
> $string = str_replace("__ENCODE_AMPERSAND__","&",$string);
> $string = str_replace("!__ENCODE_QUOTE__",""",$string);
> return $string;
>}
Dette er dog umådeligt grimt. Funktionaliteten består i at lige præcis
__ENCODE_AMPERSAND__ ender med at kunne blive matchet af vores
regulære udtryk. Dertil kommer risikoen for at vores tags optræder i
teksten i forvejen og deslige.
2. Problemstillingen består lidt i at vi "først finder/retter vi html,
og så finder/retter vi links". Det, vi måske er interesseret i, er at
"først finder vi links, så finder/retter vi html, så retter vi det
links". Fx:
>function _autohtmlencode ($string) {
> $matches = preg_match_all("!((ht|f)tp://[/a-zA-Z0-9:%_\.,~#?=&;+-]+[/a-zA-Z0-9:%_~#?-])!",$string,$uris);
> $string = htmlspecialchars($string);
> foreach($uris[1] AS $uri) {
> $htmluri = htmlspecialchars($uri);
> $string = str_replace($htmluri,"<a href=\"$htmluri\">$htmluri</a>",$string);
> }
> return $string;
>}
Først samler vi altså alle URL's, vi finder, ind i et array. Så
htmlencoder vi, og derefter går vi de fundne URL's igennem med en god
omgang søg&erstat.
Ulempen er bare, at fanger vi noget, der optræder senere, vil dette
også blive søg&erstattet. Jeg er dog overbevist om at vi med denne
metode er nået til en mere fornuftig løsning end ellers.
3. Endnu en mulighed er et regulært udtryk med /e-modifieren, hvor vi
løbende matcher brødtekst, som bliver kastet efter htmlspecialchars,
mens vi manuelt håndterer links. Jeg håber på at smide et eksempel på
det om lidt.
Har andre klaret denne problemstilling med mere elegante metoder?
--
- Peter Brodersen