Regexp compliquée.... transformation d'url qui ne sont pas dans un <a>

Répondre


Cette question est un moyen d’empêcher des soumissions automatisées de formulaires par des robots.
Smileys
:D :) :( :o :shock: :? 8-) :lol: :x :P :oops: :cry: :evil: :twisted: :roll: :wink: :!: :?: :idea: :arrow: :| :mrgreen: =D> #-o =P~ :^o :non: :priere: 8-|
Voir plus de smileys
  Revue du sujet
 

  Étendre la vue Revue du sujet : Regexp compliquée.... transformation d'url qui ne sont pas dans un <a>

par Berzemus » 10 juin 2009, 12:27

Et la dernière est de remplacer toutes les occurrences de > et < par les symboles correspondants. C'est plus propre, et il n'y aura plus d'erreurs.
Il n'y à pas de raison de s'ennuyer avec des caractères html quand on peux s'en défaire sans trop de soucis, non ?

par jojolapine » 10 juin 2009, 11:41

J'en convient, c'est la loose!!! :cry:
Passer d'un système basic, à fckeditor, ça nous donne pleins de choses à gérer...
Je sais pas si je vais pas me rabattre sur un petit script qui modifiera toutes les entrées avant la date de changement...

[Note : ce message a été posté de manière anonyme avant d'être réattribué à son auteur]

par savageman » 10 juin 2009, 11:30

Oui mais ton premier lien est entre balises <p>, pas balises <a>, donc il est remplacé, logique. ;)
Ca devient compliqué ton truc. ^^

par jojolapine » 10 juin 2009, 09:21

Pour répondre à savageman ça fonctionne pas trop mal, sauf si on a du texte comme ça:

Code : Tout sélectionner

<p><a href="http://www.lien1.com">lien1.com</a></p> bla <p><a href="http://www.lien2.com">lien2.com</a></p> bla <p>http://lien3.com</p>
(généré par fckeditor)
Et vu que je 'nai pas tout saisi le coup de la première fonction... :s
en gros voilà un texte:

Code : Tout sélectionner

<p><a href="http://www.lien1.com">lien1.com</a> et du bla bla bla</p> <p><a href="http://www.lien2.com">lien2.com déjà htmlisé</a> et du texte</p> <p>http://lien3.com lien à changer</p>
Dans lequel seul le lien3 devrait être modifié...

par Berzemus » 09 juin 2009, 19:15

EDIT: Une petite question...; je n'arrive pas à transformer le

Code : Tout sélectionner

[^>]
en

Code : Tout sélectionner

[^>]
ça ne fonctionne pas...
Puisque tu fais de gros efforts et que je trouve ça bien, je vais te dire pourquoi :wink:

[^>] en français correspond à "Je valide tout caractère qui n'est pas un >"

[^>] correspond donc à "Je valide tout caractère qui n'est pas un & et qui n'est pas un g et qui n'est pas un t et qui n'est pas un ;"

Une possibilité serait d'utiliser un lookahead, mais j'ai la flemme et ça apporte d'autres soucis.

L'autre, un peu problématique aussi, est de transformer [^>] en [^>;], mais du coup si dans le texte un url apparait derrière un point virgule, c'est foutu (ce qui n'arrive pas très fréquemment non plus).

Et la dernière est de remplacer toutes les occurrences de > et < par les symboles correspondants. C'est plus propre, et il n'y aura plus d'erreurs.

par savageman » 09 juin 2009, 17:57

A mon tour de proposer :
<?php

// remplacement hors tag html moddé
function html_replace($txt, $func)
{
    $pos = strpos($txt, '<');
    if (FALSE !== $pos) {
        $txt1 = substr($txt, 0, $pos);
        $txt2 = substr($txt, $pos);
        $txt1 = $func($txt1, $txt2);
    }
    else {
        $txt1 = '';
        $txt2 = $txt;
    }
    $rplc = $func.'("\\';
    $txt2 = preg_replace('`<([^>]*)>([^<]*)(.*)`ie', '"<\1>".'.$rplc.'2", "\\3")', $txt2);
    return $txt1.$txt2;
}

// http://www.expreg.com/lire-URL-source
// Moddé pour l'occasion (paramètre $suite)
function clicklien($url, $suite){
    if (substr($suite,0,4) == '</a>') {
        return $url.'</a>';
    }
    $in=array( 
    '`((?:https?|ftp)://\S+[[:alnum:]]/?)`si', 
    '`((?<!//)(www\.\S+[[:alnum:]]/?))`si'
    ); 
    $out=array( 
    '<a href="$1">$1</a>', 
    '<a href="http://$1">$1</a>'
    ); 
    return preg_replace($in,$out,$url); 
}

$txt='bla bla  www.machin.com 

bla http://machin.com 

machin.com 

http://www.machin.com 

<a href="http://www.truc.com">http://www.truc.com</a> 

<a href="">truc.2com</a>';

echo nl2br(htmlspecialchars(html_replace($txt, 'clicklien')));

par jojolapine » 09 juin 2009, 17:16

Bon finallement le coup des urls sans espaces, on oubli...
Je commence à avoir quelque chose de pas mal avec ta solution de tester si on a un <a> ou pas, mais il arrive que j'ai des coup de htmlspecialchars() par ci par là, et je doit faire avec...
Donc si tu n'as pas vu mon dernier edit... ;) je le repointe du doigt...

par Berzemus » 09 juin 2009, 17:08

Mert, j'ai oublié de préciser que la partie url n'était qu'un jet sans réelle validité ? Ben voilà, pourtant, je me suis répété 15 fois qu'il fallait que je le dise dans le post, et patraf, j'ai oublié.
Oui bien sur, tu fais bien de le noter, ma partie URL était juste la pour ne pas donner un regex vide, mais je ne pourrais le conseiller. C'était surtout l'avant et l'après qui étaient importants.

Pour ce qui est du point et de la gestion de fin d'url, pourquoi ne pas utiliser une liste avec des caractères autorisés en fin d'url ?

Ici appliqué à mon Regex, pour que ce soit clair:

(<a[^>]+>)?(http://[\w\.-/\?=+&%_]+[\w/])(</a>)?

Donc j'autorise ici, en fin d'url, toutes les lettres et le slash.
Et la je rebondis sur une erreur que tu as faite:

[^<a href="]

Ne veut pas du tout dire ce que tu penses. En fait, ce petit bout de regex valide tout caractère qui n'est ni un <, ni un a, ni un h, ni un r (etc..). Entre crochets [], les caractères sont considérés un à un, jamais en groupe. Dés que tu as quelque chose entre [], tu es dans un mode spécial ou les symboles ont une autre signification.

Donc il suffit d'ajouter en fin de ton regex qui valide les URL une simple liste de caractères que tu acceptes en fin d'url.

EDITH:

Ben non, j'ai mal lu ta question. Que je suis bête, j'imaginais la problématique d'une URL dans une phrase avec un point derrière, ce qui est aussi un problème. Pour ton souci, oui, c'est vrai, et c'est inévitable. Alors tu devrait travailler avec des filtres, et tester l'existence des domaines que tu trouves.

par jojolapine » 09 juin 2009, 16:11

Bon alors j'ai essayé cette façon de voir (en changeant pas mal de choses, parceque je souhaite pouvoir rendre le protocole facultatif... je sais c'est un peu pénible... donc si on rend le protocole facultatif, la partie url de ta regex est trop simple...)
Donc ça donne ceci:
<?php

$txt='<p>www.test.com</p>
<p>&nbsp;</p>
<p>http://www.testtructeetzejzlojrzelkrzelkrjzeklrjzerlkzejrlkzejrzlkrjzlkjrzelkrjzelkrjzerlkjzerlkzjrzeprzelkr-rzerzlkjrzlkerjzelrjze-zerlkzerlkzj.com j hkijb

<a href="http://blabla.com">http://blabla.com</a></p>

<a href="http://bli.fr">bli.fr</a>

blabla.com';

$replace=preg_replace_callback(

"#(<a[^>]+>)?(((http|ftp|https)://)?(([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-]+\.[a-z]{2,4}(/[a-zA-Z0-9_-]+)*([a-zA-Z0-9_.-]\.[a-z]{2,5})?)+)(</a>)?#",

'urls',

$txt

);

function urls($matches){
  

  // Si on est déjà dans un <a>
  if(
    (isset($matches[1]) && strpos($matches[1],'<a')!==false) || 
    (isset($matches[9]) && strpos($matches[9],'a>')!==false)
    )
  {
    return $matches[0];
  }
  // Sinon on traite
  else {
  
    $deb=strpos($matches[2],"//")!==false ? "": "http://";
    
    $value=strlen($matches[2]) > 30 ? substr($matches[2],0,15).'[...]'.substr($matches[2],-15) : $matches[2];
    

    return "<a href=\"".$deb.$matches[2]."\" title=\"".$matches[2]."\">".$value."</a>";
  }
}


print_r($txt);
echo '<hr />';
print_r($replace);
Seulement je ne suis pas à l'abri de ce genre de choses:
Je suis une phrase avec une fin.un point et pas d'espaces... et paf je vais me retrouver dans un <a> sans le vouloir...
C'est assez pénible :(


EDIT: Une petite question...; je n'arrive pas à transformer le

Code : Tout sélectionner

[^>]
en

Code : Tout sélectionner

[^>]
ça ne fonctionne pas...

par Berzemus » 09 juin 2009, 15:28

Beaucoup d'idées, mais malheureusement rien de bien efficace.. :wink:

La solution en Regex pur serait très complexe, puisqu'il faudrait tenir compte des balises autres que a, de tous les caractères possibles dans une URL, le placement dans le texte et le conflit avec la ponctuation...

Enfin soit, la solution que je propose repose sur les parenthèses capturantes (voir doc preg_match_all() ). Il capture tous les liens sur la page, et après coup il suffit de filtrer sur la présence ou non des deux parentèses capturantes extérieures pour voir ce qui est bon.

Code : Tout sélectionner

(<a[^>]+>)?(http://[\w\.-/\?=+&%_]+)(</a>)?
Et on peut facilement l'intégrer dans un preg_replace_callback()

par jojolapine » 09 juin 2009, 14:19

Ben en fait je me dit que l'espace de fin est pas très génant..
Disons que rajotuer un espace à l'enregistrement des posts utilisateurs c'est pas trop grave...
En ajouter un au début ça me gène un peu plus...
et puis louper des urls comme ça:

Code : Tout sélectionner

www.machin.com-Le site de machin
c'est pas trop grave, y ont qu'a mettre des espaces :p
Je vais refaire qq tests je reviens...


Edit: me revoilà avec une version qui me semble pas trop mal....
Sans plus attendre le code:
<?php

$txt='<p>www.test.com</p>
<p>&nbsp;</p>
<p>http://www.testtructeetzejzlojrzelkrzelkrjzeklrjzerlkzejrlkzejrzlkrjzlkjrzelkrjzelkrjzerlkjzerlkzjrzeprzelkr-rzerzlkjrzlkerjzelrjze-zerlkzerlkzj.com j hkijb</p>';

$replace=preg_replace_callback(

"'(^|\s|<[^a]>)(((http|ftp|https)://)?(([a-zA-Z0-9-]+\.)?[a-zA-Z0-9-]+\.[a-z]{2,4}(/[a-zA-Z0-9_-]+)*([a-zA-Z0-9_.-]\.[a-z]{2,5})?))($|\s|</[^a]>)'",

'urls',

$txt

);

function urls($matches){
  
  $deb=strpos($matches[2],"//")!==false ? "": "http://";
  
  $value=strlen($matches[2]) > 30 ? substr($matches[2],0,15).'[...]'.substr($matches[2],-15) : $matches[2];
  

  return $matches[1]."<a href=\"".$deb.$matches[2]."\" title=\"".$matches[2]."\">".$value."</a>".$matches[9];
}


print_r($txt);
echo '<hr />';
print_r($replace);
(y a en plus le tronquage d'urls)
Donc là je prend les urls qui soit sont tout au début ou tout à la fin, soit entourées d'espaces, soit avec des balsies autres que <a> (genre <p>, <i>, etc...)
Et ça me parait fonctionner :)

par geoffroy » 09 juin 2009, 14:13

C'est pas plutôt ^ pour le début et $ pour la fin ? :)

Code : Tout sélectionner

(^|[\s]) (url) ($|[\s])
Mais c'est vrai que ça prendrait pas les urls comme :
"www.machin.com-Le site de machin" ou
"www.truc.com-Le site de truc".

J'ai esssayé d'exlure les apostrophes etc comme ça, mais j'ai du mal :roll: :

Code : Tout sélectionner

[^">] (url) [^"<]

par jojolapine » 09 juin 2009, 11:24

Tient une idée me vient, est-ce qu'il est possible de dire

Code : Tout sélectionner

"Début de chaine" OU "1 caractère blanc" PUIS "url" PUIS "caractère blanc"
ça donnerai à peu près ça:

Code : Tout sélectionner

#($|\s)(url)(\s)#
Mais ça ne fonctionne pas... une idée?

par geoffroy » 08 juin 2009, 17:34

Effectivement, si tu ne veux pas te servir des espaces, ma technique n'aide pas.

Tu veux dire que ta regexp doit pouvoir transformer :
"www.machin.com-Le site à machin" ?

Donc faut plutôt essayer d'exclure les apostrophes du href="" et les >, < ?

par geoffroy » 08 juin 2009, 17:25

En fait, si :D .
J'ai confondu [\S] qui capte tout caractère "non-whitespace",
avec [ \t\r\n\v\f], qui fait l'inverse.

Donc, avec en remplaçant par cette Regexp, normalement ça devrait marcher.

Code : Tout sélectionner

/(http:\/\/|ftp:\/\/|https:\/\/)?(www\.)?([a-zA-Z0-9-.]*)(\.[a-z]{2,4})(\/[a-zA-Z0-9-._]*\/)?([a-zA-Z0-9]*\.[a-z]{2,5})?[ \t\r\n\v\f]/