IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

Imbrication de balises [FAQ] [RegEx]


Sujet :

Langage PHP

  1. #1
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 7
    Points : 2
    Points
    2
    Par défaut Imbrication de balises
    Bonjour.
    Je suis en train de créer un parseur de BBCode avec des balises imbriquées.
    J'ai vu dans la documentation PHP pour la fonction preg_replace_callback une regex particulière qui permet de gérer l'imbrication.Ccependant j'ai du mal à l'adapter pour une utilisation légèrement plus complexe.

    Citation Envoyé par manuel php
    Exemple 1813. Exemple avec preg_replace_callback() en utilisant une structure récursive pour gérer du BB code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    <?php
    $input = "plain<div style="margin-left:40px"> deep<div style="margin-left:40px"> deeper</div>deep</div>plain";
     
    function parseTagsRecursive($input)
    {
     
      $regex = '#\<div style="margin-left:40px">((?:[^[]|\[(?!/?indent])|(?R))+)\</div>#';
     
      if (is_array($input)) {
        $input = '<div style="margin-left: 10px">'.$input[1].'</div>';
      }
     
      return preg_replace_callback($regex, 'parseTagsRecursive', $input);
    }
     
    $output = parseTagsRecursive($input);
     
    echo $output;
    ?>
    J'ai trouvé dans le manuel PHP la signification de (?R), il s'agit apparemment d'une option pour effectuer une recherche récursive.

    Je ne maîtrise pas cette recherche récursive et j'ai un problème car je veux parser une balise BBCode avec un argument (par exemple je veux capturer aussi bien [indent=10] que [indent]), et en laissant la même regex il me capture seulement les tags sans argument.

    Merci de m'éclairer.

  2. #2
    Membre éclairé Avatar de Korko Fain
    Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    632
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2005
    Messages : 632
    Points : 718
    Points
    718
    Par défaut
    [indent(?:=\d+)?]
    te capture
    [indent=10] mais aussi [indent]

    Et merci pour le ?R je ne connaissait pas.

  3. #3
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 7
    Points : 2
    Points
    2
    Par défaut
    Justement non, j'avais essayé et ça ne marche pas, l'imbrication n'est plus supportée.
    En gros je veux faire une balise pour créer un tableau. J'ai besoin de pouvoir y ajouter un style éventuellement, pour cela je dois aussi bien capturer que [table style="margin:auto;"]...[/table] par exemple...
    Voilà ma regex telle qu'elle est maintenant, je précise qu'elle ne supporte pas l'imbrication quand je met l'argument style mais qu'elle supporte l'imbrication lorsque je met simplement (elle capture mais pas de façon récursive en fait).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <?php
    $regex = '`(\[table(?: style="(?:[ ()a-zA-Z0-9:;#/\'-]+)")?\](?:[^[]|\[(?!/?table(?: style="(?:[ ()a-zA-Z0-9:;#/\'-]+)")?\])|(?R))+\[/table\])`';
    ?>
    Je pense que je n'arrive pas à régler mon problème parce que je n'ai pas vraiment compris le fonctionnement de ?R mais je n'ai pas trouvé davantage d'explications que sur la page syntaxe des masques du manuel php.

    Merci d'avance.

  4. #4
    Rédacteur

    Avatar de Yogui
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2004
    Messages
    13 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yonne (Bourgogne)

    Informations professionnelles :
    Activité : Directeur technique

    Informations forums :
    Inscription : Février 2004
    Messages : 13 721
    Points : 29 985
    Points
    29 985
    Par défaut
    Je viens d'essayer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    function recursive($input)
    {
        $nbMatches = preg_match_all('#\[([^\]]+)(?:="[^"]*")?\] ((?: (?:[^\[\]]) | ((?R)) )*) \[/\1\]#x', $input, $matches, PREG_SET_ORDER);
        if($nbMatches)
        {
            foreach($matches as $match)
            {
                if(!empty($match[2]))
                {
                    recursive($match[2]);
                }
            }
            print_r($matches);
        }
    }
     
    recursive('some [it]italics [bd]bolded [un]underlined [address="uri"]link[/address][/un][/bd] italics[/it], [bd]bold[/bd] raw');
    Sortie :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    Array
    (
        [0] => Array
            (
                [0] => [address="uri"]link[/address]
                [1] => address
                [2] => link
            )
     
    )
    Array
    (
        [0] => Array
            (
                [0] => [un]underlined [address="uri"]link[/address][/un]
                [1] => un
                [2] => underlined [address="uri"]link[/address]
                [3] => [address="uri"]link[/address]
            )
     
    )
    Array
    (
        [0] => Array
            (
                [0] => [bd]bolded [un]underlined [address="uri"]link[/address][/un][/bd]
                [1] => bd
                [2] => bolded [un]underlined [address="uri"]link[/address][/un]
                [3] => [un]underlined [address="uri"]link[/address][/un]
            )
     
    )
    Array
    (
        [0] => Array
            (
                [0] => [it]italics [bd]bolded [un]underlined [address="uri"]link[/address][/un][/bd] italics[/it]
                [1] => it
                [2] => italics [bd]bolded [un]underlined [address="uri"]link[/address][/un][/bd] italics
                [3] => [bd]bolded [un]underlined [address="uri"]link[/address][/un][/bd]
            )
     
        [1] => Array
            (
                [0] => [bd]bold[/bd]
                [1] => bd
                [2] => bold
            )
     
    )

  5. #5
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 7
    Points : 2
    Points
    2
    Par défaut
    En fait j'utilise une preg_split pour décomposer un texte en séparant les tableaux et le texte entre les tableaux.

    La regex que j'avais, à savoir
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <?php
    $contents = preg_split('`\[table\]((?:[^[]|\[(?!/?table\])|(?R))+)\[/table\]`', $contents, -1, PREG_SPLIT_DELIM_CAPTURE);
    ?>
    me traite seulement les tableaux de niveau 1 et en l'appelant récursivement j'arrive à faire ce que je veux, par exemple avec le code suivant : Mais si je prends en compte l'argument style, la récursivité ne fonctionne plus, il s'arrête à la première occurrence trouvée.
    J'ai mis cette regex
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    <?php
    $contents = preg_split('`\[table(?: style="(?:[ ()a-zA-Z0-9:;#/\'-]+)")?\]((?:[^[]|\[(?!/?table\])|(?R))+)\[/table\]`', $contents, -1, PREG_SPLIT_DELIM_CAPTURE);
    ?>
    et la récursivité ne fonctionne pas lorsque je rajoute un argument, par exemple :
    [table style="margin:auto;"]contenu du tableau[/table]
    Je ne comprends pas pourquoi la récursivité ne fonctionne pas dans cette regex, si quelqu'un pouvait m'expliquer le fonctionnement exact de l'option ?R je pense que j'arriverais à régler le problème tout seul par la suite.

    Merci à ceux qui me liront.

  6. #6
    Rédacteur

    Avatar de Yogui
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2004
    Messages
    13 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yonne (Bourgogne)

    Informations professionnelles :
    Activité : Directeur technique

    Informations forums :
    Inscription : Février 2004
    Messages : 13 721
    Points : 29 985
    Points
    29 985
    Par défaut
    Ta regex est trop complexe

    Tel que je te l'ai proposé ci-dessus, ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    style="(?:[ ()a-zA-Z0-9:;#/\'-]+)"
    Peut être abrégé en :
    Bon, c'est pas exactement ça mais l'idée est là : ta regex est trop complexe. Tu gagnerais beaucoup à utiliser des classes négatives [^...] plutôt qu'énumératives [azdv;,è_çà876], et en plus c'est + flexible. Par exemple entre des guillemets, le seul caractère interdit est... Le guillemet
    C'est plus simple de dire "tout sauf un guillemet" que d'énumérer toutes les solutions possibles.

  7. #7
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 7
    Points : 2
    Points
    2
    Par défaut
    Bon à ce niveau je suis d'accord avec ta remarque, bien que je me sois embêté à lister tous les caractères autorisés pour des raisons de sécurité (pour éviter le plus possible du css invalide et aussi du javascript).
    Mais cette remarque ne règle en aucun cas mon problème, elle permet d'optimiser le fonctionnement de la regex certes, mais le problème reste inchangé. Ce qui ne fonctionne plus en revanche lorsque je rajoute un argument optionnel c'est la récursivité et là je ne sais plus comment faire, je n'arrive pas à comprendre comment fonctionne ?R et surtout pourquoi il se trouve dans un "ou" (| en regex).

    Merci.

  8. #8
    Rédacteur

    Avatar de Yogui
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2004
    Messages
    13 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yonne (Bourgogne)

    Informations professionnelles :
    Activité : Directeur technique

    Informations forums :
    Inscription : Février 2004
    Messages : 13 721
    Points : 29 985
    Points
    29 985
    Par défaut
    C'est que... La récursivité pour un preg_replace, je ne sais pas si c'est possible

    As-tu essayé d'adapter le code que j'ai proposé ci-dessus ? Il fonctionne avec match, l'as-tu essayé avec replace ? J'ai peu que cela donne des résultats complètement erratiques.

    Je pense que dans ton cas, il est plus intéressant de définir une fonction récursive perso, utilisant preg_match (appel récursif à ta fonction perso) et str_replace (gain de perfs).

  9. #9
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 7
    Points : 2
    Points
    2
    Par défaut
    C'est preg_split que j'utilise et pas preg_replace.
    J'ai réussi à régler le problème, j'avais fait une énorme étourderie dans la condition pour rappeler la fonction récursivement, je faisais un strpos('[table'],..), c'est donc normal que ça ne fonctionnait pas.
    Merci quand même pour l'aide.

  10. #10
    Rédacteur

    Avatar de Yogui
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2004
    Messages
    13 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yonne (Bourgogne)

    Informations professionnelles :
    Activité : Directeur technique

    Informations forums :
    Inscription : Février 2004
    Messages : 13 721
    Points : 29 985
    Points
    29 985
    Par défaut
    Euh oui

    Peux-tu proposer ici la solution finale ? J'aimerais le mettre dans la FAQ.


  11. #11
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 7
    Points : 2
    Points
    2
    Par défaut
    D'accord j'enverrai ma fonction.
    Il y en a en tout pour une bonne cinquantaine de lignes, elle est presque terminée mais il me reste encore quelques détails à régler (je dois la combiner avec un htmlentities). Dès qu'elle est finie et testé je vous la donne, c'est promis

  12. #12
    Candidat au Club
    Profil pro
    Inscrit en
    Juin 2007
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2007
    Messages : 7
    Points : 2
    Points
    2
    Par défaut
    Bonjour, me revoilà avec la balise tableau qui supporte l'imbrication.
    Comme vous me l'avez demandé, je publie le code source de mes fonctions récursives. J'ai essayé de bien les commenter pour qu'elles soient plus compréhensibles mais j'avoue que ça reste très complexe.
    Voici les fonctions (je les ai élaborées dans le cadre du développement du CMS PHPBoost).

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    //Fonction pour éclater la chaîne selon les tableaux (gestion de l'imbrication infinie)
    function split_imbricated_table(&$contents)
    {
    	$contents = preg_split('`\[table( style="[^"]+")?\]((?:[^[]|\[(?!/?table(?: style="[^"]+")?\])|(?R))+)\[/table\]`', $contents, -1, PREG_SPLIT_DELIM_CAPTURE);
    	//1 élément représente les inter tableaux, un le style du tableau et l'autre le contenu
    	$nbr_occur = count($contents);
    	for( $i = 0; $i < $nbr_occur; $i++)
    	{
    		if( $i % 3 === 2 && preg_match('`\[table(?: style="[^"]+")?\].+\[/table\]`s', $contents[$i]) )
    		{
    			//C'est le contenu d'un tableau, il contient un sous tableau donc on éclate
    			split_imbricated_table($contents[$i]);
    		}
    	}
    }
     
    //Fonction qui parse les tableaux dans l'ordre inverse à l'ordre hiérarchique
    function parse_imbricated_table(&$contents)
    {
    	if( is_array($contents) )
    	{
    		$string_contents = '';
    		$nbr_occur = count($contents);
    		for($i = 0; $i < $nbr_occur; $i++)
    		{
    			//Si c'est le contenu d'un tableau on le parse
    			if( $i % 3 === 2 )
    			{
    				//On parse d'abord les sous tableaux éventuels
    				parse_imbricated_table($contents[$i]);
    				//On parse le tableau concerné (il doit commencer par [row] puis [col] ou [head] et se fermer pareil moyennant espaces et retours à la ligne sinon il n'est pas valide)
    				if( preg_match('`^(?:\s|<br />)*\[row(?: rowspan="[0-9]+")?\](?:\s|<br />)*\[(?:col|head)(?: colspan="[0-9]+")?(?: style="[^"]+")?\].*\[/col|head\](?:\s|<br />)*\[/row\](?:\s|<br />)*$`s', $contents[$i]) )
    				{
    					//On nettoie les caractères éventuels (espaces ou retours à la ligne) entre les différentes cellules du tableau pour éviter les erreurs xhtml
    					$contents[$i] = preg_replace('`^(?:\s|<br />)*(\[row\].*\[/row\])(?:\s|<br />)*$`s', '$1', $contents[$i]);
    					$contents[$i] = preg_replace('`(\[/?(?:col|row|head)(?: colspan="[0-9]+")?(?: style="[^"]+")?\])(\s|<br />)+(\[/?(?:col|row|head)(?: colspan="[0-9]+")?(?: style="[^"]+")?\])`U', '$1$3', $contents[$i]);
    					//Parsage de row, col et head
    					$contents[$i] = preg_replace('`\[row\](.*)\[/row\]`sU', '<tr>$1</tr>', $contents[$i]);
    					$contents[$i] = preg_replace('`\[col((?: rowspan="[0-9]+")?(?: colspan="[0-9]+")?(?: style="[^"]+")?)?\](.*)\[/col\]`sU', '<td class="bb_table_col"$1>$2</td>', $contents[$i]);
    					$contents[$i] = preg_replace('`\[head((?: colspan="[0-9]+")?(?: style="[^"]+")?)?\](.*)\[/head\]`sU', '<th class="bb_table_head"$1>$2</th>', $contents[$i]);
    					//parsage réussi (tableau valide), on rajoute le tableau devant
    					$contents[$i] = '<table class="bb_table"' . $contents[$i - 1] . '>' . $contents[$i] . '</table>';
    				}
    				else
    				{
    					//le tableau n'est pas valide, on met des balises temporaires afin qu'elles ne soient pas parsées au niveau inférieur
    					$contents[$i] = str_replace(array('[col', '[row', '[/col', '[/row', '[head', '[/head'), array('[\col', '[\row', '[\/col', '[\/row', '[\head', '[\/head'), $contents[$i]);
    					$contents[$i] = '[table' . $contents[$i - 1] . ']' . $contents[$i] . '[table]';
    				}
    			}
    			//On concatène la chaîne finale si ce n'est pas le style du tableau
    			if( $i % 3 !== 1 )
    				$string_contents .= $contents[$i];
    		}
    		$contents = $string_contents;
    	}
    }
     
    function parse_table(&$contents)
    {
    	//Cette ligne n'est à mettre que si vous avez passé un htmlentities à votre code au préalable
    	$contents = preg_replace_callback('`\[(?:table|col|row|head)(?: colspan=&quot;[0-9]+&quot;)?(?: rowspan=&quot;[0-9]+&quot;)?( style=&quot;(?:[^&]+)&quot;)?\]`U', create_function('$matches', 'return str_replace(\'&quot;\', \'"\', $matches[0]);'), $contents);
    	split_imbricated_table($contents);
    	parse_imbricated_table($contents);
    	//On remet les tableaux invalides tels qu'ils étaient avant
    	$contents = str_replace(array('[\col', '[\row', '[\/col', '[\/row', '[\head', '[\/head'), array('[col', '[row', '[/col', '[/row', '[head', '[/head'), $contents);
    }
    L'appel se fait comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    //Parsage de la balise table.
    	if( strpos($contents, '[table') !== false )
    		parse_table($contents);
    A noter que toutes ces fonctions contiennent des passages par référence pour les arguments, il ne faut donc pas mettre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     $contents = parse_table($contents);
    L'utilisation est semblable à celle en html :
    ).

    Il y a aussi la fonction pour repasser du html au BBCode, si certains sont intéressés qu'ils me fassent signe !

  13. #13
    Rédacteur

    Avatar de Yogui
    Homme Profil pro
    Directeur technique
    Inscrit en
    Février 2004
    Messages
    13 721
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Yonne (Bourgogne)

    Informations professionnelles :
    Activité : Directeur technique

    Informations forums :
    Inscription : Février 2004
    Messages : 13 721
    Points : 29 985
    Points
    29 985
    Par défaut
    Ok, merci bien !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 3
    Dernier message: 26/09/2009, 16h25
  2. Imbrication de balises
    Par solorac dans le forum Balisage (X)HTML et validation W3C
    Réponses: 4
    Dernier message: 12/08/2009, 08h59
  3. CSS imbrication sur la balise BODY
    Par bouliz dans le forum Mise en page CSS
    Réponses: 1
    Dernier message: 17/02/2008, 12h59
  4. probleme d'imbrications de balises PHP/html
    Par rapanui dans le forum Langage
    Réponses: 6
    Dernier message: 08/12/2006, 19h06
  5. [XML] Imbrication balises/texte
    Par bourbaki2003 dans le forum XML/XSL et SOAP
    Réponses: 3
    Dernier message: 10/11/2004, 18h00

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo