2017-06-27 4 views
0

Je sais que d'autres personnes ont soumis des questions sur cette erreur, mais je ne vois pas comment cette regex ou la chaîne de sujet pourrait être plus simple.PHP 7 preg_replace PREG_JIT_STACKLIMIT_ERROR avec une simple chaîne

Pour moi, c'est un bug, mais avant de le soumettre à PHP, j'ai pensé à m'assurer et obtenir de l'aide pour voir si cela peut être plus simple.

Voici un petit script de test montrant 2 chaînes; une avec 1024 x et un avec 1023:

// 1024 x's 
$str = '_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; 

// Outputs nothing (bug?) 
echo preg_replace('/(?<=[^\w]|^)_([^_\n\t ](.|\n(?!\n))*?)_(?=[^\w]|$)/', '[i]${1}[/i]', $str); 

echo "\n\n"; 

// 1023 x's 
$str = '_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; 

// Outputs the unchanged string as expected 
echo preg_replace('/(?<=[^\w]|^)_([^_\n\t ](.|\n(?!\n))*?)_(?=[^\w]|$)/', '[i]${1}[/i]', $str); 

Comme vous pouvez le voir, avec seulement un peu plus longue chaîne (supérieure à 1024 caractères) ne nous obtenons une erreur. Les chaînes qui seront traitées par ce va être une longueur - ils seront les messages du forum, des articles de presse, etc.

Regex Explication

Juste essayer de faire un peu l'analyse syntaxique de démarquage pour convertir une chaîne comme _I am italic_, à une version héritée du balisage que nous utilisons sur notre ancien site dans certaines situations. Les raisons/utilisations ne sont pas importantes. Ce qui est important, c'est que je pense que cela devrait fonctionner correctement, et en fait comme partout ailleurs sauf PHP 7.

Il ne doit correspondre à ces traits de soulignement que si cela représente un mot ou une phrase indépendante. Il ne doit pas correspondre au premier trait de soulignement s'il est précédé d'un caractère basé sur un mot et ne doit pas correspondre au dernier trait de soulignement s'il est suivi d'un caractère basé sur un mot.

Environnement: Centos 7, PHP: 7.1.6

+0

Le '(. | \ N (?! \ N)) *?' Est en train de tuer. Vous pouvez utiliser '. * (?: \ R (?! \ R). *) *' À la place. –

+0

BTW, [ici] (https://ideone.com/WvryB1), PHP 7.1.0, les deux chaînes sont affichées. –

+0

Ici ne s'affiche pas seulement sur PHP 7.0.0 à 7.0.5. Il semble être un bug https://3v4l.org/DfHDJ –

Répondre

1

Le modèle (.|\n(?!\n))*? est très inefficace et provoque beaucoup de retours en arrière redondant lorsqu'il est utilisé pas à la fin du motif (où il n'a pas de sens à tout). Plus il est situé à gauche du motif, plus la performance est mauvaise.

Comme il ne fait correspond à tout omble chevalier, mais un saut de ligne, puis une nouvelle ligne qui n'est pas suivi avec un autre saut de ligne, d'une manière paresseuse, vous pouvez ré-écrire le motif comme .*?(?:\R(?!\R).*?)*:

'~\b_([^_\n\t ].*?(?:\R(?!\R).*?)*)_\b~' 

Voir le regex demo.

Note:

  • (?<=[^\w]|^) = \b parce qu'il ya une _ (un mot char) après la lookbehind
  • (?=[^\w]|$) = \b parce qu'il ya une _ avant la préanalyse
  • .*?(?:\R(?!\R).*?)* - matches:
    • .*? - tous les 0+ caractères autres que le saut de ligne ch ars, aussi peu que possible, puis
    • (?:\R(?!\R).*?)* - zéro ou plusieurs séquences de:
      • \R(?!\R) - une séquence de saut de ligne non suivie par une autre séquence de saut de ligne (\R = \n, \r\n ou \r)
      • .*? - tout autre que 0+ caractères caractères de saut de ligne, aussi peu que possible