[PHP] Krijg RegExp niet voor elkaar...

Pagina: 1
Acties:

  • Rashann
  • Registratie: Maart 2000
  • Laatst online: 24-08 07:20

Rashann

Zoek de hond...

Topicstarter
Hoi,

Ik ben bezig met een site in PHP waarbij de gebruiker straks HTML pagina's vanuit een editor via Crtl-C/V kan pasten in de bestaande pagina. Echter, die editor heeft wel eens de neiging om binnen een tag onnodige enters mee te geven, die dus door PHP nl2br worden vervangen door <br>-etjes. Probleem dat volgt: De tag uit de pagina is niet meer geldig.

Nou heb ik geprobeerd via een RegExp alle \n's die binnen een < en een > zitten te vervangen, maar dit lukt me niet :(

Iemand die zo'n regexp wel kan maken?

(Of een manier kan verzinnen waarmee nl2br niet de \n binnen een tag <br>'t?)

If nothing is written below, I was the last to reply...


  • goalgetter
  • Registratie: Juni 1999
  • Laatst online: 25-08 15:24
Geen antwoord op je vraag, maar toch een opmerking...

Er wordt HTML code in een box ingevoerd, waarom zou je dan de enters die in die code zitten om willen zetten naar <br />?? Als je <br>'s nodig hebt zouden ze in de code al moeten staan.

Met andere woorden, dit probleem hoef je volgens mij helemaal niet op te lossen.

  • tomato
  • Registratie: November 1999
  • Niet online
Inderdaad, goalgetter heeft volgens mij gelijk. In de HTML code zouden toch al de juiste <br/> tags moeten staan :?

Maar goed, ik heb even wat geprobeerd. Ik heb als oplossing gekozen voor een alternatief voor nl2br() die tags met tust laat (in tegenstelling tot een oplossing die newlines uit tags verwijdert).

Ik kwam in eerste instantie met dit:
PHP:
1
2
3
4
5
6
<?
$p = '/(?:(<\s*[^\s>]+(?:\s+(?:[^=]+="[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|[^=]+=\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^\s>]+))*\s*>)|(\r\n?|\n))/e';
$r = '("$2")?"<br/>":"$1"';

echo preg_replace($p, $r, $s);
?>

Maar door een 'onvolkomenheid' (of bug :?, heb niet veel zin om het verder uit te gaan zoeken), gaat dat in sommige gevallen verkeerd (heeft te maken met quoted attributes, als iemand er meer van wil weten zal ik nog wat info plaatsen).

Dus uiteindelijk heb ik er toen maar dit van gemaakt:
PHP:
1
2
3
4
5
6
7
<?
$p = '/(?:(<\s*[^\s>]+(?:\s+(?:[^=]+="[^"\\\\]*(?:\\\\.[^"\\\\]*)*"|[^=]+=\'[^\'\\\\]*(?:\\\\.[^\'\\\\]*)*\'|[^\s>]+))*\s*>)|(\r\n?|\n))/';
$l = preg_split($p, $s, -1, PREG_SPLIT_DELIM_CAPTURE);

for ($i=0; $i<count($l); $i++)
    $s .= ($i%2) ? $l[$i]:nl2br($l[$i]);
?>

(de PREG_SPLIT_DELIM_CAPTURE flag werkt pas vanaf PHP 4.0.5)

  • tomato
  • Registratie: November 1999
  • Niet online
Nog wat uitleg over het pattern ;)

Dit stuk
code:
1
<\s* .... \s*>

doet eigenlijk niets anders dan een tag matchen. Met wat comments erin is dat misschien iets duidelijker:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<\s*[^\s>]+         # Start of tag

   (?:

    # Allow optional attributes in the tag

    \s+           # Start with space

    # Different types of attributes,
    # open brackets for alternation

    (?:

       # Doublequoted attribute

       [^=]+=         # Attribute and the = sign
       "             # Opening quote

        [^"\\]*     # Normal
        (?:
           \\.       # Special
           [^"\\]*   # Normal
        )*

       "             # Closing quote (escaped)

    |  # OR
       # Singlequoted attribute

       [^=]+=         # Attribute and the = sign
       '             # Opening quote

        [^'\\]*     # Normal
        (?:
           \\.       # Special
           [^'\\]*   # Normal
        )*

       '             # Closing quote

    |  # OR
       # non-quoted attribute

       [^\s>]+       # Anything but space or >

    )               # Close alternation

    # End of attribute, allow it zero or more times

   )*

   \s*               # Optional spaces

>                   # End of tag

Als we dat dan even zien als 'tag' ziet het hele pattern er eigenlijk zo uit:
code:
1
/(?:(tag)|(\n\r?|\r))/

Wat zo misschien iets duidelijker is:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/

   # Search for a tag or a newline

   (?:       # Open alternation brackets

    (tag)     # Matches a tag -> $1

   |

    (\r\n?|\n)   # Matches a newline (\n, \r, or \r\n) -> $2

   )           # Close alternation brackets

/e           # /e modifier to evaluate the substitution string

Zoals je ziet wordt $1 gevuld als er een tag gevonden is, als er een newline (buiten een tag) gevonden is wordt $2 daarmee gevuld.

Dus we gebruiken dit als replacement:
code:
1
($2) ? "<br/>" : $1

Wanneer er een newline gevonden is vervangen we deze door een <br/> tag en wanneer er een tag gevonden is vervangen we deze door zichzelf (blijft dus gewoon staan). Alleen dit laatste blijkt dus niet altijd helemaal goed te gaan, daarom had ik een alternatief wat voor zich zou moeten spreken

(Perl doet het overigens itt PHP wel slim, daar werkt de eerste oplossing met de /e modifier dus ook gewoon goed.)