/ Forside / Teknologi / Udvikling / PHP / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
PHP
#NavnPoint
rfh 3959
natmaden 3372
poul_from 3310
funbreak 2700
stone47 2230
Jin2k 1960
Angband 1743
Bjerner 1249
refi 1185
10  Interkril.. 1146
Mere link-klikbarhed
Fra : Peter Brodersen


Dato : 15-05-02 01:19

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å &lt;<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å &lt;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å &lt;&lt;a
href=&quot;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__","&lt;",$string);
> $string = str_replace("!__ENCODE_RIGHTTAG__","&gt;",$string);
> $string = str_replace("__ENCODE_AMPERSAND__","&amp;",$string);
> $string = str_replace("!__ENCODE_QUOTE__","&quot;",$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

 
 
Peter Brodersen (15-05-2002)
Kommentar
Fra : Peter Brodersen


Dato : 15-05-02 02:34

On Wed, 15 May 2002 02:19:14 +0200, Peter Brodersen
<professionel@nerd.dk> wrote:

>3. Endnu en mulighed er et regulært udtryk med /e-modifieren, hvor vi
>løbende matcher brødtekst,

Nezar og jeg brainstormede lidt på den. Første udgave var fin, men den
matchede blot ikke tekst efter sidste link:
>$string = preg_replace("!(.*?)((ht|f)tp://[/a-zA-Z0-9:%_\.,~#?=&;+-]+[/a-zA-Z0-9:%_~#?-])!es","htmlspecialchars('\\1').'<a href=\"\\2\">\\2</a>'",$string);

Næste udkast var så også at matche evt. lazy efter linket for at få
slutningen med, hvilket ikke var nogen succes.

Vi prøvede så ganske enkelt blot at lade det være valgfrit om linket
skulle matches med (bemærk spørgsmålstegnet efter link-delen).
Problemet her var, at man så matchede hvert eneste tegn (hvilket kan
være et performance-problem på lange tekster), og endte med en række
<a href=""></a> undervejs:
>$string = preg_replace("!(.*?)((ht|f)tp://[/a-zA-Z0-9:%_\.,~#?=&;+-]+[/a-zA-Z0-9:%_~#?-])?!es","htmlspecialchars('\\1').'<a href=\"\\2\">\\2</a>'",$string);

Vi endte så indtil videre med at tage højde for det ved at tjekke om
der var matchet noget link (og i øvrigt også hælde htmlspecialchars på
URI'en undervejs):
>$string = preg_replace("!(.*?)((ht|f)tp://[/a-zA-Z0-9:%_\.,~#?=&;+-]+[/a-zA-Z0-9:%_~#?-])?!es","htmlspecialchars('\\1').(strlen('\\2')>0?'<a href=\"'.htmlspecialchars('\\2').'\">'.htmlspecialchars('\\2').'</a>':'')",$string);

Det er altså vores umiddelbare bud. Det er uden tvivl umådeligt tungt
på lange tekster, så forslag til optimeringer er velkomne.

Eneste, der lige slår mig, er at man kan erstatte (.*?) i starten med
(.?), når vi nu alligevel kun matcher ét tegn af gangen. Men det
ændrer næppe på det store...

--
- Peter Brodersen

Thomas Lindgaard (16-05-2002)
Kommentar
Fra : Thomas Lindgaard


Dato : 16-05-02 21:26

Peter Brodersen <professionel@nerd.dk> wrote in news:6PhE8.3803
$4f4.238206@news000.worldonline.dk:

> "&"-tegnet bliver vi i sagens natur n›dt til at medtage i vores link,
> da det jo sagtens kan optr‘de i en URL.

En lille ting... selve adressen og en evt. query string er vel adskilt af
"?", hvorimod "&" adskiller key=value-par i query string. Kan man så ikke
undgå en del af "hønen og ægget" ved at finde links efter opskriften

1) URL starter med noget a la "http://www.blabla.dk"
2) Så kommer der enten
a) Ingenting
b) "/" og evt. "bla.php"
c) ("/?" eller "/bla.php?") og en query string med key=value-par
adskilt af "&"

Kan det ikke gøre noget med problemet med, at &gt; bliver taget med i dit
link? Eller sagt med lidt færre ord:

"http://www.bla.dk&noget" er ikke en lovlig URL, så den bør ikke blive
matchet af dit regulære udtryk.

Mvh.
/Thomas

Søg
Reklame
Statistik
Spørgsmål : 177554
Tips : 31968
Nyheder : 719565
Indlæg : 6408852
Brugere : 218888

Månedens bedste
Årets bedste
Sidste års bedste