8722 sujets

Développement web côté serveur, CMS

Modérateur
Bonsoir,

Je tente de récupérer le premier paragraphe d'un texte formaté en html et comportant plusieurs paragraphes.
Je souhaiterais pouvoir afficher ce premier paragraphe et masquer les autres pour les faire apparaitre au clic sur un lien. Voici ce que j'ai fait :
// $content est le texte formaté en html
preg_match('@<p>.*</p>@U', $content, $parag);
$v_content = $parag[0]; //le contenu visible
$h_content = str_replace($v_content, '', $content); // le contenu masqué
$new_content = $v_content."\n".'<a href="#c'.$id.'" onclick="$(\'#c'.$id.'\').slideToggle(1000); return false;">Lire la suite…</a>'."\n".'<div id="c'.$id.'" style="display: none;">'."\n".$h_content."\n".'</div>'."\n";


J'obtiens l'erreur suivante :
Notice: Undefined offset: 0 in G:\wamp\www\lzdlm\inc.z.fetching.php on line 234


la ligne 234 est la ligne définissant $v_content.

J'ai fait un print_r() pour voir s'il y avait quelque chose dans le tableau $parag, mais rien de rien, j'ai juste un array() qui s'affiche. C'est sûrement un problème de regexp...

Merci d'avance pour votre aide.
Modérateur
Merci pour ta réponse benj,

non, ça ne fonctionne pas mieux avec la barre oblique inversée. Je me demandais si ça ne venait pas du preg_match_all() que je faisais juste avant :

<?php
$parag_count = preg_match_all('@</p>@', $content, $matches);
if ($parag_count > 1)
{
    preg_match('@<p>.*<\/p>@U', $content, $parag);
    $v_content = $parag[0];
    $h_content = str_replace($v_content, '', $content);
    $new_content = $v_content."\n".'<a href="#c'.$cid.'" onclick="$(\'#c'.$cid.'\').slideToggle(1000); return false;">Lire la suite…</a>'."\n".'<div id="c'.$cid.'" style="display: none;">'."\n".$h_content."\n".'</div>'."\n";
    $desc_display .= $new_content;
    $cid++;
}
?>


Je vérifie d'abord s'il y a au moins deux paragraphes pour afficher le lien et masquer le deuxième paragraphe avec preg_match_all(). Je me demandais s'il fallait pas demander au script de reprendre la recherche au début du $content...
Modifié par jojaba (09 Oct 2013 - 08:08)
Pas besoin d'échapper le / vu que le délimiteur est @.

Par contre, c'est un peu inutile de rematcher la première occurence avec preg_match si tu as déjà matché toutes les occurences avec preg_match_all juste avant. Pourquoi tu n'utilise pas le tableau $matches ? Il contient déjà tout ce dont tu as besoin.

En plus tu peux faire en sorte de récupérer les positions matchées, un substr sera toujours plus efficace qu'un str_replace. En plus, ici c'est plus logique, et ça évite un effet inattendu si jamais il y avait deux paragraphes identiques (c'est peu probable, mais au cas où...)

Je ne crois pas que php mette en cache les regexp et sujets déjà utilisées donc je ne pense pas qu'il y ait un problème de running match.
Modérateur
>Bonne idée de récupérer les paragraphes dans le tableau des résultats du preg_match_all() !
Merci !
J'ai changé le motif pour le preg_match_all() ainsi :
$parag_count = preg_match_all('@<p>.*</p>@U', $content, $matches);

Il ne trouve aucune correspondance ainsi. C'est donc bien la regexp qui pose problème.
Est-ce que .* prend également en compte les retours à la ligne et d'autres joyeusetés de ce genre (je pense aux tabulations par exemple) ? A ma connaissance oui.
Code testé :
$content = strval($desc->content);
$parag_count = preg_match_all('@<p>.*</p>@Um', $content, $matches);
if ($parag_count > 1)
{
    //preg_match('@<p>(.*)<\/p>@Ui', $content, $parag);
    //$v_content = $parag[0];
    $v_content = $matches[0][0];
    $h_content = str_replace($v_content, '', $content);
    $new_content = $v_content."\n".'<a href="#c'.$cid.'" onclick="$(\'#c'.$cid.'\').slideToggle(1000); return false;">Lire la suite…</a>'."\n".'<div id="c'.$cid.'" style="display: none;">'."\n".$h_content."\n".'</div>'."\n";
    $desc_display .= $new_content;
    $cid++;
}
else
{
    $desc_display .= $content."\n";
}


********* Edit *********************
Bon, j'ai trouvé ce qui n'allait pas : les retours à la ligne. J'ai modifié le $content pour ne plus avoir de retour à la ligne et ça fonctionne bien. Pourtant j'avais ajouté m après le U !
Il me faudrait donc trouver une solution pour pouvoir "matcher" également les retour à la ligne (ce que le point "." ne fait pas...)
********** /Edit *******************
Modifié par jojaba (09 Oct 2013 - 10:17)
Modérateur
Bon, j'ai tenté différentes choses, mais la plus simple à mon avis est de supprimer les retours à la ligne avant de faire la recherche des correspondances. J'ai donc fait ça :
$search = array('\r\n', '\n');
$content = str_replace($search, '', strval($desc->content));

(Je travaille en local sur Windows)
Mais cela n'a rien changé. J'avais tenté dans un premier temps de créer une regexp qui prenait en compte le sreturs à la ligne, mais ça n'a pas fonctionné alors je me suis rabattu sur la solution que je vous propose plus haut.
Il y a sûrement d'autres choses à trouver et supprimer... Smiley confus
J'ai un peu du mal à comprendre, tu souhaites récupérer les X premiers mots d'un paragraphe ?
Modérateur
Dge-06 a écrit :
J'ai un peu du mal à comprendre, tu souhaites récupérer les X premiers mots d'un paragraphe ?

Non, je souhaite récupérer le premier paragraphe (en entier) de 6 (dans le cas que je traite actuellement, mais le nombre de paragraphe pourra varier).
Ce qu'il reste à résoudre est la suppression des retours à la ligne avant recherche de correspondance.
Je ne sais pas si cette information est pertinente, mais je la donne quand-même : le $content est tiré d'un fichier xml :
                       <content><![CDATA[
<p>
Premier paragraphe
</p>
<p>
Deuxième paragraphe
</p>
<p>
Troisième paragraphe
</p>
<p>
Quatrième paragraphe
</p>
...
			]]></content>

Modifié par jojaba (09 Oct 2013 - 11:12)
Est-ce qu'avec des <br /> au lieu des retours ça marche ?

Parce que si oui, transforme tes retours à la ligne en <br /> (nl2br)
Modérateur
Lothindil a écrit :
Est-ce qu'avec des &lt;br /&gt; au lieu des retours ça marche ?

Parce que si oui, transforme tes retours à la ligne en &lt;br /&gt; (nl2br)

Je voudrais que l'utilisateur qui écrit le texte garde la liberté de formater en pargaraphes.
Ce qui est bizarre c'est que quand je change la structure que je donne un message plus haut comme ça :
                        <content><![CDATA[
<p>Premier paragraphe</p>
<p>Deuxième paragraphe</p>
<p>Troisième paragraphe</p>
<p>Quatrième paragraphe</p>
...
			]]></content>
Modérateur
Ça y est, j'ai trouvé !
Pour supprimer les retours à la ligne, il faut utiliser les lignes suivantes :
$search = array("\r\n", "\n", "\r");
$content = str_replace($search, '', strval($desc->content)); 

Bien faire attention d'utiliser des guillemets (double-quote) et non des apostrophes (simple-quote) pour les caractères de retour à la ligne, sinon, ça ne fonctionne pas.
Merci à tous pour votre aide, c'est chouette d'être soutenu ainsi. Smiley smile
Modifié par jojaba (09 Oct 2013 - 13:05)
J'aurais dû y penser plus tôt mais en effet, le point ne prend par défaut pas les sauts de ligne. IL faut pour qu'il les prenne ajouter l'option s (Nom interne: PCRE_DOT_ALL). C'est plus simple que d'essayer de les supprimer.

La confusion avec l'option m (PCRE_MULTILINE) est courante. L'effet de m est de considérer que ^et $ peuvent indiquer respectivement le début et la fin d'une ligne. Par défaut, ils indiquent uniquement le début et la fin de la chaîne entière. Comme vous voyez, ça n'a rien à voir...

L'option s autorise le point à manger les caractères de saut de ligne qu'il ne prend par défaut pas, c'est bien ce qu'on veut ici.

Pour info, ça n'aurait pas fonctionné avec nl2br dans l'état où c'était, parce que nl2br ne supprime pas effectivement les saut de ligne, il se contente d'ajouter <br /> juste avant chaque. Nl2br correspond en fait approximativement à :

function nl2br ($str) {
return preg_replace('@$@m', '<br />', $str);
}
Modérateur
Merci pour ces précisions Quentin.
Je confirme que l'option s répond à mon souci de prise encompte des retours à la ligne par le .
Voilà ce que ça donne donc finalement :
$content = strval($desc->content); 
$parag_count = preg_match_all('@<p>.+</p>@Us', $content, $matches);
if ($parag_count > 1)
{
    $v_content = $matches[0][0];
    $h_content = str_replace($v_content, '', $content);
    $new_content = $v_content."\n".'<a href="#c'.$cid.'" onclick="$(\'#c'.$cid.'\').slideToggle(1000); return false;">Lire la suite…</a>'."\n".'<div id="c'.$cid.'" style="display: none;">'."\n".$h_content."\n".'</div>'."\n";
    $desc_display .= $new_content;
    $cid++;
}

Une toute petite lettre ajoutée dans la ligne du preg_match_all() et ça roule ! Smiley smile
J'ai conservé le str_replace que tu m'avais recommandé de remplacer par des substr, je ne pense pas que cela change grand-chose, n'est-ce pas (en tout cas, impossible d'avoir deux paragraphes pareils dans mon cas.
Merci encore pour cette aide "pédagogique" Smiley smile