Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Pour migrer des fonctions regex en fonctions pcre automatiquement.

23 réponses
Avatar
Jean-Francois Ortolo
Bonjour

J'ai l'honneur ( excusez du peu ;) ) de présenter une solution de
migration semi automatique, des fonctions de type regex: split(),
ereg(), eregi(), ereg_replace(), et eregi_replace(), vers leurs
équivalents de type pcre : preg_split(), preg_match(), et preg_replace().

En effet, ces fonctiosn regex, disparaîtront à la version PHP 6, et
il est donc souhaitable de pouvoir migrer ses sites vers les nouvelles
fonctions.

Compte tenu du fait que celà intéresse avant tout les programmeurs
php, j'espère que le modérateur va laisser passer ce message, bien que
ses outils relèvent plutôt de la programmation Shell et Awk. ;(


Cette solution se compose d'un script en Bourne Shell migration.sh, à
placer dans la racine du site web à migrer, et d'un script awk
filtre.awk à placer aussi dans la racine du site à migrer.

Voici le code des ces deux scripts :

D'abord, migration.sh

La variable dir_init doit être alimentée avec le répertoire dans
lequel est installé ce script ( répertoire racine du site web ).

Ce script est censé être lancé à partir du compte root.

La variable perm doit être alimentée avec le propriétaire et groupe
des scripts php à migrer.

Excusez-moi du fait que ces instructions sont sous forme de
citations, j'ai la flemme d'enlever manuellement les débuts de lignes.


> #!/bin/sh
>
> # Propriétaire et groupe
> # des scripts *.php
> perm="root:root"
>
> # Variables de configuration, c'est le répertoire de départ.
> dir_init="/var/www/html/php"
>
> # Et le script awk de filtre.
> filtre_awk=${dir_init}"/filtre.awk"
>
> # On calcule le répertoire absolu courant.
> # Quand le paramètre n'est pas présent,
> # c'est le répertoire de départ, sinon
> # c'est le paramètre du script.
> if [ $# -eq 0 ]; then
> dir=${dir_init}
> else
> dir=$1
> fi
>
> # Positionnement dans le répertoire courant.
> cd ${dir}
>
> # On parcourt le répertoire courant en sélectionnant
> # tous les scripts php.
> for i in `ls *.php 2>/dev/null`; do
> if [ -f $i ]; then
> file=${dir}"/"${i}
> file2=${dir}"/super_new_"${i}".txt"
>
> # Traitement du script *.php,
> # là on se contente d'afficher son nom
> # avec son chemin absolu.
> #
> echo ${file} > /root/tmp2.txt
> echo ${file2} >> /root/tmp2.txt
> echo "" >> /root/tmp2.txt
>
> awk -f ${filtre_awk} -- ${file} 1>${file2} 2>>/root/tmp2.txt
>
> chown $perm ${file2}
> chmod 777 ${file2}
>
> t1=`ls -l ${file} | awk '{ print $5; }'`
> t2=`ls -l ${file2} | awk '{ print $5; }'`
>
> if [ ${t1} -ne ${t2} ]; then
> cat /root/tmp2.txt >> /root/tmp.txt
> fi
> fi
> done
>
> # On lit tous les sous-répertoires du répertoire courant,
> # et on relance le même script, avec ces sous-répertoires
> # comme paramètre, pour que ces processus fils
> # se positionnent sur ces sous-répertoires.
> #
> for j in `ls -d * 2>/dev/null`; do
> if [ -d $j ]; then
>
> # Calcul du sous-répertoire absolu.
> direct=${dir}"/"${j}
>
> ${dir_init}/migration.sh ${direct}
> fi
> done
>
> # A la fin du script, on remonte vers le répertoire
> # père.
> cd ..
>
> exit 0


Ensuite, voici le code du script filtre.awk :


> function change(param)
> {
> p=split(param, tableau, "");
>
> drapeau=0;
> n=0;
>
> for(i=1; i<=p; i++)
> {
> t=tableau[i];
>
> if(drapeau!=0)
> {
> if(n==0)
> {
> if(t=="\\")
> {
> n++;
> }
> else
> {
> if(t==s)
> {
> chaine=chaine "/" s;
> break;
> }
> else
> chaine=chaine t;
> }
> }
> else
> {
> if(t=="\\")
> n++;
> else
> {
> d="";
> for(k=1; k<=n; k++)
> d=d "\\";
>
> chaine=chaine d;
>
> q=n;
> if(((2*int(q/2.0))==n)&&(t==s))
> {
> chaine=chaine "/" s;
> break;
> }
> else
> chaine=chaine t;
>
> n=0;
> }
> }
>
> }
> else
> {
> if((t=="'")||(t=="\""))
> {
> drapeau=1;
> s=t;
> chaine=t "/";
> }
> # Début de chr(nombre)
> # ou de variable masque.
> else if((t=="c")||(t=="$"))
> {
> chaine=t;
> break;
> }
> }
> }
>
> i++;
>
> for(; i<=p; i++)
> {
> t=tableau[i];
> chaine=chaine t;
> }
>
> return(chaine);
> }
> function change_i(param)
> {
> p=split(param, tableau, "");
>
> drapeau=0;
> n=0;
>
> for(i=1; i<=p; i++)
> {
> t=tableau[i];
>
> if(drapeau!=0)
> {
> if(n==0)
> {
> if(t=="\\")
> {
> n++;
> }
> else
> {
> if(t==s)
> {
> chaine=chaine "/i" s;
> break;
> }
> else
> chaine=chaine t;
> }
> }
> else
> {
> if(t=="\\")
> n++;
> else
> {
> d="";
> for(k=1; k<=n; k++)
> d=d "\\";
>
> chaine=chaine d;
>
> q=n;
> if(((2*int(q/2.0))==n)&&(t==s))
> {
> chaine=chaine "/i" s;
> break;
> }
> else
> chaine=chaine t;
>
> n=0;
> }
> }
>
> }
> else
> {
> if((t=="'")||(t=="\""))
> {
> drapeau=1;
> s=t;
> chaine=t "/";
> }
> # Début de chr(nombre)
> # ou de variable masque.
> else if((t=="c")||(t=="$"))
> {
> chaine=t;
> break;
> }
> }
> }
>
> i++;
>
> for(; i<=p; i++)
> {
> t=tableau[i];
> chaine=chaine t;
> }
>
> return(chaine);
> }
> {
> line=$0;
> line2=$0;
>
> if(line ~ /=[ \t]*split[ ]*\(/)
> {
> r=split(line, tableau, /=[ \t]*split[ ]*\(/);
>
> for(l=1; l<=r; l++)
> {
> u=tableau[l];
>
> if(l==1)
> line=u;
> else
> {
> line=line "= preg_split(";
> v=change(u);
> line=line v;
> }
> }
> }
>
> if(line ~ /eregi[ ]*\(/)
> {
> split(line, tableau, /eregi[ ]*\(/);
>
> for(l=1; l<=r; l++)
> {
> u=tableau[l];
>
> if(l==1)
> line=u;
> else
> {
> line=line "preg_match(";
> v=change_i(u);
> line=line v;
> }
> }
> }
>
> if(line ~ /ereg[ ]*\(/)
> {
> split(line, tableau, /ereg[ ]*\(/);
>
> for(l=1; l<=r; l++)
> {
> u=tableau[l];
>
> if(l==1)
> line=u;
> else
> {
> line=line "preg_match(";
> v=change(u);
> line=line v;
> }
> }
> }
>
> if(line ~ /eregi_replace[ ]*\(/)
> {
> split(line, tableau, /eregi_replace[ ]*\(/);
>
> for(l=1; l<=r; l++)
> {
> u=tableau[l];
>
> if(l==1)
> line=u;
> else
> {
> line=line "preg_replace(";
> v=change_i(u);
> line=line v;
> }
> }
> }
>
> if(line ~ /ereg_replace[ ]*\(/)
> {
> split(line, tableau, /ereg_replace[ ]*\(/);
>
> for(l=1; l<=r; l++)
> {
> u=tableau[l];
>
> if(l==1)
> line=u;
> else
> {
> line=line "preg_replace(";
> v=change(u);
> line=line v;
> }
> }
> }
>
> printf ("%s\n", line);
>
> if(line!=line2)
> {
> print "D'abord:" line2 " Ensuite:" line > "/dev/stderr"
> }
> }


A la fin de la migration, vous avez le fichier de logs /root/tmp.txt
qui contient les chemins absolus des scripts php avant et après
migration, ainsi que les lignes des fonctions migrées.

Ce fichier ne contient que les scripts modifiés, pas les scripts
inchangés.

Pour arranger le formattage, je suggère de séparer ce fichier de
logs, en deux fichiers indexé de 1 à n, n étant le nombre de fichiers
modifiés.

Pour celà :

cat tmp.txt | awk 'BEGIN{ i=0; j=0; }{ if(length($0)<5) { i++; j++;
if(j==1) print i; } else if ($0 !~ /Ensuite/) { j=0; print $0; } }' >
fichiers.txt


cat tmp.txt | awk 'BEGIN{ i=0; j=0; }{ if(length($0)<5) { i++; j++;
if(j==1) print i; } else if ($0 ~ /Ensuite/) { j=0; print $0; } }' | awk
'{ for(i=2; i<=NF; i++) if($i != "Ensuite:") printf("%s ", $i); else
printf(" "); printf("\n"); }' > fonctions.txt


Ensuite, vous aurez dans fichiers.txt la liste indexés de 1 à n des
chemins absolus des fichiers sources et cibles par paires, et dans
fonctions.txt la liste indexée des fonctions avant et après migration,
par groupes de fonctions pour chaque fichiers modifiés.

Dans les deux cas, l'index va de 1 à n, et n a la même valeur dans
les deux cas.

Il faut obligatoirement vérifier dans le fichier fonctions.txt qu'il
n'y a pas d'erreur lors de la migration des fonctions.

Théoriquement la seule erreur peut résulter du fait que l'expression
rationnelle est contenue dans une variable php ( commençant par $ le
caractère dollar ). Dans ce cas évidemment, il n'y a pas d'autre
solution que d'éditer manuellement le fichier cible correspondant ( de
même index dans le fichiers fichiers.txt ), pour modifier cette variable
php qui sert d'expression rationnelle.


Comme conclusion, je serais intéressé à ce que vous m'indiquiez les
erreurs de programmation éventuelles que vous voyez, car je suis en
train de faire cette migration sur mon ordinateur, pour mon site
www.pronostics-courses.fr versions locale et remote, ainsi que pour le
site www.lescourses.com, mon site partenaire, que j'ai copié sur mon
ordinateur. ;) Celà me permettrait de diminuer le risque d'erreurs.

Je suis aussi intéressé par vos remarques et suggestions, compte tenu
du fait que cette topique de migration automatisée de fonctions regex
vers des fonctions pcre, avait été abordée il y de celà quelques
semaines ou mois.

Bien à vous.

Amicalement.

Jean-François Ortolo

--
Visitez le site http://www.pronostics-courses.fr/
donnant des Statistiques, Pronostics et Historiques graphiques
très élaborés.

Les Statistiques sont calculées d'après une base de données
allant du 1er Janvier 2000 jusqu'à très récemment.

10 réponses

1 2 3
Avatar
Jean-Francois Ortolo
Bonjour

Je vous prie de bien vouloir m'excuser, ma solution ne fonctionne que
si les scripts php sont codés en mode iso-8859-1 ou iso-8859-15, à
l'exclusion du mode utf8. Donc, en mode ascii étendu ( caractères de 0 à
255 ).

J'accepte avec plaisir et reconnaissance toutes les remarques et
critiques.

Bien à vous.

Amicalement.

Jean-François Ortolo

--
Visitez le site http://www.pronostics-courses.fr/
donnant des Statistiques, Pronostics et Historiques graphiques
très élaborés.

Les Statistiques sont calculées d'après une base de données
allant du 1er Janvier 2000 jusqu'à très récemment.
Avatar
Olivier Miakinen
Bonjour,

Le 07/01/2010 18:36, Jean-Francois Ortolo a écrit :

J'ai l'honneur ( excusez du peu ;) ) de présenter une solution de
migration semi automatique, des fonctions de type regex: split(),
ereg(), eregi(), ereg_replace(), et eregi_replace(), vers leurs
équivalents de type pcre : preg_split(), preg_match(), et preg_replace().



Excellent ! Je n'aurais moi-même pas eu le courage de me lancer dans
une telle entreprise. Merci aussi de venir nous en faire profiter.

[...]

Compte tenu du fait que cela intéresse avant tout les programmeurs
php, j'espère que le modérateur va laisser passer ce message, bien que
ses outils relèvent plutôt de la programmation Shell et Awk. ;(



Je suis d'accord avec ça. Néanmoins, je me permets de copier ma réponse
dans fr.comp.lang.regexp car cela pourrait en intéresser d'autres. Le
suivi est positionné sur fr.comp.lang.php malgré tout.

[...]

Comme conclusion, je serais intéressé à ce que vous m'indiquiez les
erreurs de programmation éventuelles que vous voyez, [...]



Je suis volontaire pour regarder. Pour optimiser cette relecture, est-ce
que tu pourrais commencer par epliciter un peu les limites que tu t'es
fixées ?

Par exemple :
- comment reconnais-tu un fichier PHP (extension du fichier ou présence
de <?php ... ?>) ?
- supportes-tu la syntaxe <?= ... ?> ?
- la fonction à traduire peut-elle s'étendre sur plusieurs lignes ?
- quels délimiteurs reconnais-tu pour les chaînes (guillemets simples,
guillemets doubles, HEREDOC, NOWDOC) ?
- une regexp peut-elle être scindée en plusieurs chaînes concaténées
avec l'opérateur « . » ?
- si oui, peut-on avoir un mélange de délimiteurs ('abc' . "def") ?
- en gros, quelles sont les transformations faites à la regexp ?


Cordialement,
--
Olivier Miakinen
Avatar
Jean-Francois Ortolo
Le 07/01/2010 20:52, Olivier Miakinen a écrit :
Bonjour,


Comme conclusion, je serais intéressé à ce que vous m'indiquiez les
erreurs de programmation éventuelles que vous voyez, [...]



Je suis volontaire pour regarder. Pour optimiser cette relecture, est-ce
que tu pourrais commencer par epliciter un peu les limites que tu t'es
fixées ?

Par exemple :
- comment reconnais-tu un fichier PHP (extension du fichier ou présence
de<?php ... ?>) ?
- supportes-tu la syntaxe<?= ... ?> ?
- la fonction à traduire peut-elle s'étendre sur plusieurs lignes ?
- quels délimiteurs reconnais-tu pour les chaînes (guillemets simples,
guillemets doubles, HEREDOC, NOWDOC) ?
- une regexp peut-elle être scindée en plusieurs chaînes concaténées
avec l'opérateur « . » ?
- si oui, peut-on avoir un mélange de délimiteurs ('abc' . "def") ?
- en gros, quelles sont les transformations faites à la regexp ?


Cordialement,




Bonjour Monsieur

Une remarque de mise au point pour mes scripts.

D'abord, pour le script migration.sh, il faut que l'instruction echo
"" > /root/tmp2.txt soit située avant et non pas après les deux lignes
echo ${file} >> /root/tmp2.txt et echo ${file2} >> /root/tmp2.txt ( >>
dans les deux cas, et non pas > ).

Celà permet de n'avoir qu'un seule ligne vide avant chaque groupes de
noms de fichiers suivis par les fonctions contenues dans ces fichiers.

Et puis, j'apporte un changement au traitement pour arranger le
fichier de logs :

Pour les fonctions, indexées par groupes pour chaque fichiers traités :

awk 'BEGIN { i=0; j=0; }{ if(length($0)<6) { i++; j++; if(j==1) print
i; } else if($0 ~ /Ensuite/) { j=0; q=split($0, tableau, ""); line="";
for(i=9; i<=q; i++) line=line tableau[i]; printf("%sn", line); } }'
/root/tmp.txt | awk '{ split($0, tableau, "Ensuite:"); line=tableau[1] "
" tableau[2]; printf("%sn", line); }' > fonctions.txt

Pour les fichiers, indexés par fichiers traités :

awk 'BEGIN { i=0; j=0; }{ if(length($0)<6) { i++; j++; if(j==1) print
i; } else if($0 !~ /Ensuite/) { j=0; print $0; } }' /root/tmp.txt >
fichiers.txt

Pour finir, j'ai commis une erreur ( un lapsus ), en oubliant
d'affecter la variable r à partir de tous les split à la fin du script
filtre.awk

Par exemple, pour la fonction ereg() :

r=split($0, tableau2, /ereg[ ]*(/);

au lieu de :

split($0, tableau, /ereg[ ]*(/);

En effet, il faut bien avoir le nombre des élément de l'array
tableau2. L'erreur faisait que la valeur précédente de r était prise, ce
qui occasionnait des erreurs.

Il vaut mieux je crois, mettre tableau2 systématiquement au lieu de
tableau pour le corps principal du script, car les fonctions utilisent
tableau, et je ne sais pas très bien comment fonctionnent les variables
locales aux fonctions, si elles sont réellement indépendantes des
variables externes aux fonctions. La honte... ;(

Voilà donc toutes ces erreurs corrigées.


Pour répondre à votre question, je reconnais un script php uniquement
à son extension *.php

Si la nécessité se fait jour d'identifier les scripts php d'une autre
manière, normalement ce sera le rôle du script migration.sh de le faire,
probablement d'après le contenu de ces fichiers, qui peut être lu est
interprété avec un cat.

Le problème pourrait éventuellement se poser pour des scripts php
sasn extension *.php , destinés exclusivement à être inclus dans
d'autres scripts php. J'aurai à faire cette vérification pour mon site
partenaire que j'ai copié sur mon ordinateur.


A part çà, effectivement le script filtre.awk suppose que l'expression
rationnelle source Posix, est une chaîne entourée de doubles quotes ou
de simples quotes, ou bien un caractère du type chr(nombre), ou bien une
variable php commençant par le signe dollar $

Pour traiter le cas où il pourrait y avoir plusieurs chaînes de
caractères accolées, ou bien des chaînes associées avec des variables ou
autre, il faudrait complexifier la reconnaissance de la fin de
l'expression rationnelle.

J'assume, que pour transformer une expression rationnelle Posix
simple en expression rationnelle pcre, il suffit de rajouter / avant le
début après la première quote double ou simple, et / ou /i après la fin,
avant la dernière quote double ou simple.

Cà marche avec des expressions rationnelles Posix simples, sauf
qu'évidemment quand l'expression rationnelle est sous forme de variable
php, il faut intervenir manuellement sur cette variable dans le fichier
cible, pour qu'elle devienne conforme à la norme pcre.

Dans le cas où l'expression rationnelle est dans une variable php (
détectée par le signe dollar en début d'expression ) ou est de la forme
chr(nombre), celle-ci n'est pas modiifée, mais la fonction est modifiée,
donc le fichier cible voit son nombre de caractères, augmenter.

Il n'y a donc pas de cas où un fichier source soit inchangé, bien
qu'il contienne quand même au moins une fonction à migrer. Le processus
est donc suffisamment sécurisé, pour peu que tous les scripts php
sources soient détectés.

Il faut donc, faire une vérification patiente de toutes les fonctions
migrées, ce pourquoi j'arrange le fichier de logs, vers les fichiers
fichiers.txt et fonctions.txt

fonctions.txt contient pour chaque numéro d'index ( correspondant à
un fichier source et cible dans fichiers.txt ) un groupe de lignes où
figurent à gauche la ligne du fichier avant migration, et à droite la
ligne du fichier après migration.

Il est ainsi possible, de fractionner cette vérification, en
supprimant progressivement de fonctions.txt toutes les lignes
correctement migrées, pour ne laisser que celle qui nécessitent un
traitement manuel.

Pour infos, j'ai migré sur mon ordinateur mon site web
www.pronostics-courses.fr versions locale et remote, ainsi que mon site
partenaire www.lescourses.com préalablement copié.

Globalement, j'ai mesuré que j'ai 352 fichiers migrés, et 17418
lignes migrées.

Celà signifie, que pour la vérification, j'aurai à me taper la
lecture de 17418 lignes, en regardant soigneusement les expressions
rationnelles, pour vérifier qu'il n'y a pas d'erreur.

Et encore, dans tous les cas où ces expressions rationnelles sont
contenues dans des variables php, il me faudra éditer le fichier cible
manuellement, pour modifier ces variables, en faisant attention que celà
n'entraîne pas d'effet de bord sur le reste des programmes.

Je pense honnêtement, qu'une migration entièrement automatique n'est
pas envisageable, et que ma solution n'est valable que dans le contexte
où les expressions rationnelles Posix sont simples. Enfin, il est
nécéssaire que je ne passe pas à côté de fonctions à migrer qui ne
soient pas modifiées, à cause d'erreurs imprévisibles et inapercues.

Je reconnais les limitations de mon approche.

Merci beaucoup de vos réponses.

Bien à vous.

Amicalement.

Jean-François Ortolo

--
Visitez le site http://www.pronostics-courses.fr/
donnant des Statistiques, Pronostics et Historiques graphiques
très élaborés.

Les Statistiques sont calculées d'après une base de données
allant du 1er Janvier 2000 jusqu'à très récemment.
Avatar
Olivier Masson
Le 07/01/2010 20:52, Jean-Francois Ortolo a écrit :
Bonjour

Je vous prie de bien vouloir m'excuser, ma solution ne fonctionne que si
les scripts php sont codés en mode iso-8859-1 ou iso-8859-15, à
l'exclusion du mode utf8. Donc, en mode ascii étendu ( caractères de 0 à
255 ).




Bonjour,

Ben crotte, c'est un sacré boulot mais tout est en utf-8 pour moi.
Bon, d'un autre côté, je ne dois plus avoir beaucoup d'ereg...
Avatar
Jean-Francois Ortolo
Le 08/01/2010 11:00, Olivier Masson a écrit :

Bonjour,

Ben crotte, c'est un sacré boulot mais tout est en utf-8 pour moi.
Bon, d'un autre côté, je ne dois plus avoir beaucoup d'ereg...




Bonjour Monsieur

En fait... Le problème concernerait la reconnaissance de caractères
utf-8, donc sur deux octets, ce qui dépendrait avant tout des facultés
qu'aurait awk lui-même, et le Shell bash ( ou sh ) éventuellement, à
reconnaître les caractères utf-8.

Il me semble cependant, qu'en utf-8, les caractères ascii purs sont
conservés par rapport à de l'iso-8859-1, quant aux caractères étendus (
128-255 en iso ), je ne suis pas compétent pour savoir comment ça se
traduit en utf-8.

Donc, dans l'ensemble, je ne sais pas quelles sont les conditions
pour que awk sache lire et écrire de l'utf-8, le problème de la
reconnaissance de patterns ( expressions rationnelles ) étant écarté
puisque tous les patterns utilisés dans mes deux scripts migration.sh et
filtre.awk, sont de l'ascii non étendu.

Il se peut très bien, que ma solution convienne aussi pour de
l'utf-8, mais je ne suis pas compétent pour le dire, car je suis
complètement nul en utf-8. ;)

Merci beaucoup de votre réponse.

Bien à vous.

Amicalement.

Jean-François Ortolo

--
Visitez le site http://www.pronostics-courses.fr/
donnant des Statistiques, Pronostics et Historiques graphiques
très élaborés.

Les Statistiques sont calculées d'après une base de données
allant du 1er Janvier 2000 jusqu'à très récemment.
Avatar
Olivier Miakinen
Le 08/01/2010 11:00, Olivier Masson répondait à Jean-Francois Ortolo :

Ben crotte, c'est un sacré boulot



Oui !

mais tout est en utf-8 pour moi.
Bon, d'un autre côté, je ne dois plus avoir beaucoup d'ereg...



... l'un allant d'ailleurs de pair avec l'autre !

Sauf erreur, il n'y a aucun moyen pour demander à ereg de traiter des
jeux de caractères sur plusieurs octets. Par conséquent, si on utilise
des ereg en UTF-8, soit c'est bugué et ce sera bugué pareil en preg,
soit ça marche et ça marchera pareil en UTF-8.
Avatar
Olivier Miakinen
Le 08/01/2010 16:06, Jean-Francois Ortolo a écrit :

En fait... Le problème concernerait la reconnaissance de caractères
utf-8, donc sur deux octets, ce qui dépendrait avant tout des facultés
qu'aurait awk lui-même, et le Shell bash ( ou sh ) éventuellement, à
reconnaître les caractères utf-8.



Je n'ai pas encore lu ton code, mais à priori je ne pense pas que ce
soit nécessaire : il suffit qu'aucun octet avec le 8e bit à 1 ne soit
traité de manière spéciale, et en principe c'est le cas (les seuls
caractères spéciaux sont de l'ASCII 7 bits).

Il me semble cependant, qu'en utf-8, les caractères ascii purs sont
conservés par rapport à de l'iso-8859-1, quant aux caractères étendus (
128-255 en iso ), je ne suis pas compétent pour savoir comment ça se
traduit en utf-8.



En UTF-8, tout caractère non ASCII est codé en plusieurs octets dont
aucun n'est un caractère ASCII : pas de risque de confusion, donc.

[...]

Il se peut très bien, que ma solution convienne aussi pour de
l'utf-8, mais je ne suis pas compétent pour le dire, car je suis
complètement nul en utf-8. ;)



Moi je le pense. Comme je l'ai répondu aussi à Olivier Masson, si l'ereg
était correcte en UTF-8, elle doit le rester en devenant une
preg. À l'occasion je lancerai un nouveau fil sur ce sujet.
Avatar
Jean-Francois Ortolo
Le 07/01/2010 20:52, Olivier Miakinen a écrit :
Bonjour,

Je suis volontaire pour regarder. Pour optimiser cette relecture, est-ce
que tu pourrais commencer par epliciter un peu les limites que tu t'es
fixées ?

Par exemple :
- comment reconnais-tu un fichier PHP (extension du fichier ou présence
de<?php ... ?>) ?
- supportes-tu la syntaxe<?= ... ?> ?
- la fonction à traduire peut-elle s'étendre sur plusieurs lignes ?





Bonsoir Monsieur

Dans mon cas, j'ai reconnu des scripts pouvant approximativement être
des scripts php, ( mais qui ne l'étaient probablement pas, je n'ai pas
vérifié ), en cherchant avec cat ${file} | egrep -e "include[ t]*("
dans le script migration.sh tous les fichiers inclus dans les fihciers
de suffixe *.php ( même chose avec include_once, require et require_once ).

J'ai obtenu un certain nombre de fichiers de suffixe *.inc, que j'ai
identifiés et passé à la moulinette cat *.inc | grep split ( ou ereg ou
replace ) sans aucun résultat trouvé.

Donc, dans mon cas seul, le procédé utilisé pour trouver les scripts
php fonctionne, car le suffixe *.php est toujours utilisé pour les
scripts php. Merci la normalisation... ;(

Je vais sous peu commencer ma vérification lente et laborieuse de
toutes les fonctions migrées, il y en 17418 pour 352 fichiers modifiés,
ouf...

J'en aurai pour un bout de temps, quoi... ;(

Après, ce sera le tour de la correction précise des cas en erreur (
expressions rationnelles dans des variables pratiquement ).

Bien à vous.

Amicalement.

Jean-François Ortolo

--
Visitez le site http://www.pronostics-courses.fr/
donnant des Statistiques, Pronostics et Historiques graphiques
très élaborés.

Les Statistiques sont calculées d'après une base de données
allant du 1er Janvier 2000 jusqu'à très récemment.
Avatar
Jean-Francois Ortolo
Le 08/01/2010 23:38, Jean-Francois Ortolo a écrit :

Bonsoir Monsieur

Je vais sous peu commencer ma vérification lente et laborieuse de toutes
les fonctions migrées, il y en 17418 pour 352 fichiers modifiés, ouf...

J'en aurai pour un bout de temps, quoi... ;(

Après, ce sera le tour de la correction précise des cas en erreur (
expressions rationnelles dans des variables pratiquement ).





Bonjour

Après arrangement du fichiers fonctions.txt, et séparation entre les
fonctions avec et sans variables expressions rationnelles, j'obtiens ce
résultat :

Il y a 1638 lignes modifiées ( = à corriger ) comportant des
variables, pour un ensemble de 345 fichiers à corriger manuellement.

Et 15467 lignes sans variables, donc ne comportant d'erreur,
pratiquement, que si un caractère hors norme est situé dans l'expression
rationnelle.

Ouf... Seulement 345 fichiers à corriger, ouf, ouf.

Je vais voir comment sélectionner automatiquement ces fichiers par
leur numéros d'index.

Après, j'éditerai chacun de ces fichiers cibles, 345 fichiers
pfffooouuu... ;(

Celà montre, que même pour plusieurs sites sans préparation, on peut
migrer les sites, de manière beaucoup plus rapide avec ma méthode
qu'avec la méthode manuelle.

Bien à vous.

Amicalement.

Jean-François Ortolo

--
Visitez le site http://www.pronostics-courses.fr/
donnant des Statistiques, Pronostics et Historiques graphiques
très élaborés.

Les Statistiques sont calculées d'après une base de données
allant du 1er Janvier 2000 jusqu'à très récemment.
Avatar
Jean-Francois Ortolo
Bonjour

J'avais des difficultés ptratique avec la version précédente, qui
n'était pas strictement automatique, car elle nécessitait des
vérifications manuelles.

Il semble que j'ai réussi à mettre au point une version entièrement
automatique, qui donne des messages d'erreurs quand il y a des erreurs,
dans le fihcier /root/tmp.txt Quand ce fichier ne contient que les
fichiers migrés, ( pas de token Erreur ) il n'y a pas d'erreur durant la
migration.

Voici le code :

D'abord, le filtre filtre.awk

-----------------------------------------------------------

function change(param)
{
# Fin d'expression rationnelle
# trouvée.
fin_trouve=0;

# Quote simple ou double
# de fin d'expression
# rationnelle partielle
# ou globale.
quote_trouve=0;

p=split(param, tableau, "");

# n = nombre de caractères backslashes
# ( ) rencontrés.
n=0;

# Initialisation
# du premier paramètre
# de la fonction pcre.
chaine=""/".";

for(i=1; i<=p; i++)
{
t=tableau[i];

if(t=="$")
{
quote_trouve=0;

# On suppose que les variables
# sont toujours accolées
# aux autres expressions,
# par un point, et que
# leurs noms ne contiennent pas
# de point.
while((i<=p)&&(t!=".")&&(t!=","))
{
# Pas d'espace
# dans un nom de variable.
if((t!=" ")&&(t!="t"))
chaine=chaine t;

i++;
t=tableau[i];
}

if(i>p)
{
# Fin de tableau[i]
# rencontré, sans
# atteindre la fin
# de l'expression rationnelle,
# donc erreur.
print "Erreur: expression rationnelle sur plusieurs lignes: " param
" " chaine > "/dev/stderr";

# On sort de la boucle.
break;
}
else if(t==".")
{
# L'expression rationnelle
# continue avec une autre
# expression accolée.
chaine=chaine t;
}
else
{
# Fin de l'expression rationnelle
# rencontrée.
fin_trouve=1;

# t n'a pas été affecté,
# elle le sera en sortie de boucle.
#
#
# On sort de la boucle.
break;
}
}
else if((t=="t")||(t=="c"))
{
if(t=="t")
{
# début de trim(
pattern="trim";
}
else
{
# Début de chr(
pattern="chr";
}

w=split(pattern, tab, "");

for(x=1; ((i<=p)&&(x<=w)); x++)
{
if(t==tab[x])
{
chaine=chaine tab[x];

i++;
t=tableau[i];
}
else
{
# Erreur, token non prévu.
break;
}
}

if(x<=w)
{
# Le token n'est pas
# conforme.
print "Erreur: token non prévu: " param " " chaine > "/dev/stderr";

#" On sort de la boucle.
break;
}
else
{
# Recherche de la parenthèse
# ouvrante,
# puis suite du traitement.
while((i<=p)&&(t!="("))
{
i++;
t=tableau[i];
}

chaine=chaine "(";

i++;
t=tableau[i];

if(pattern=="chr")
{
# Dans ce cas, on cherche
# la parenthèse fermante,
# suivie le cas échéant
# d'un point ( . )
# ou la virgule de fin
# d'expression rationnelle.
while((i<=p)&&(t!=")"))
{
chain=chaine t;

i++;
t=tableau[i];
}

# On cherche un point
# Ou la virgule de fin.
i++;
t=tableau[i];
while((i<=p)&&(t!=".")&&(t!=","))
{
# On prend le point,
# mais pas la virgule.
if(t!=",")
chaine=chaine t;
i++;
t=tableau[i];
}

# La fin de l'expression rationnelle
# a été trouvée, on sort.
if(t==",")
{
fin_trouve=1;

break;
}
}
}
}
else if((t==""")||(t=="'"))
{
chaine=chaine t;

# Mémorisation du type de quote
# ( double ou simple )
# encadrant l'expression
# rationnelle.
s=t;

i++;
t=tableau[i];
while((i<=p)&&(quote_trouve==0))
{
if(t=="")
{
n++;
}
else
{
for(x=1; x<=n; x++)
chaine=chaine "";

q=n;

indic=0;
# Echappement
# au cas où.
if((n>0)&&((2*int(q/2.0))==n)&&((t=="&")||(t=="+")||(t==s)))
{
indic=1;

# On n'échappe pas
# le signe +
# s'il est précédé
# de ]
# ( expression rationnelle ).
if(t=="+")
{
if(tableau[i-1]!="]")
chaine=chaine "";
else
indic=0;
}
else if(t=="&")
chaine=chaine "";
else if(t==s)
chaine=chaine "";
}

chaine=chaine t;

n=0;

# quote sans échappement :
# La fin de l'expression
# rationnelle en cours
# a été trouvée.
if((indic==0)&&(t==s))
quote_trouve=1;
}

i++;
t=tableau[i];
}

# On a trouvé la quote terminale
# de l'expression rationnelle en cours,
# donc on cherche le point,
# ou la virgule qui marque la fin
# de l'expression rationnelle globale.

while((i<=p)&&(t!=".")&&(t!=","))
{
# On prend le point,
# mais pas la virgule.
if(t!=",")
chaine=chaine t;
i++;
t=tableau[i];
}

# La fin de l'expression rationnelle
# a été trouvée, on sort.
if(t==",")
{
fin_trouve=1;

break;
}
}
}

if(fin_trouve==1)
{
# tableau[i]==","

# On complète la chaîne,
# de manière à l'adapter
# à la norme pcre.
chaine=chaine "."/",";

# Et puis, on complète
# le reste de la fonction.
i++;

while(i<=p)
{
t=tableau[i];

chaine=chaine t;

i++;
}
}

return(chaine);
}
function change_i(param)
{
# Fin d'expression rationnelle
# trouvée.
fin_trouve=0;

# Quote simple ou double
# de fin d'expression
# rationnelle partielle
# ou globale.
quote_trouve=0;

p=split(param, tableau, "");

# n = nombre de caractères backslashes
# ( ) rencontrés.
n=0;

# Initialisation
# du premier paramètre
# de la fonction pcre.
chaine=""/".";

for(i=1; i<=p; i++)
{
t=tableau[i];

if(t=="$")
{
quote_trouve=0;

# On suppose que les variables
# sont toujours accolées
# aux autres expressions,
# par un point, et que
# leurs noms ne contiennent pas
# de point.
while((i<=p)&&(t!=".")&&(t!=","))
{
# Pas d'espace
# dans un nom de variable.
if((t!=" ")&&(t!="t"))
chaine=chaine t;

i++;
t=tableau[i];
}

if(i>p)
{
# Fin de tableau[i]
# rencontré, sans
# atteindre la fin
# de l'expression rationnelle,
# donc erreur.
print "Erreur: expression rationnelle sur plusieurs lignes: " param
" " chaine > "/dev/stderr";

# On sort de la boucle.
break;
}
else if(t==".")
{
# L'expression rationnelle
# continue avec une autre
# expression accolée.
chaine=chaine t;
}
else
{
# Fin de l'expression rationnelle
# rencontrée.
fin_trouve=1;

# t n'a pas été affecté,
# elle le sera en sortie de boucle.
#
#
# On sort de la boucle.
break;
}
}
else if((t=="t")||(t=="c"))
{
if(t=="t")
{
# début de trim(
pattern="trim";
}
else
{
# Début de chr(
pattern="chr";
}

w=split(pattern, tab, "");

for(x=1; ((i<=p)&&(x<=w)); x++)
{
if(t==tab[x])
{
chaine=chaine tab[x];

i++;
t=tableau[i];
}
else
{
# Erreur, token non prévu.
break;
}
}

if(x<=w)
{
# Le token n'est pas
# conforme.
print "Erreur: token non prévu: " param " " chaine > "/dev/stderr";

#" On sort de la boucle.
break;
}
else
{
# Recherche de la parenthèse
# ouvrante,
# puis suite du traitement.
while((i<=p)&&(t!="("))
{
i++;
t=tableau[i];
}

chaine=chaine "(";

i++;
t=tableau[i];

if(pattern=="chr")
{
# Dans ce cas, on cherche
# la parenthèse fermante,
# suivie le cas échéant
# d'un point ( . )
# ou la virgule de fin
# d'expression rationnelle.
while((i<=p)&&(t!=")"))
{
chain=chaine t;

i++;
t=tableau[i];
}

# On cherche un point
# Ou la virgule de fin.
i++;

# L'un ou l'autre
# a été trouvé, donc
# on actualise chaine.
t=tableau[i];
while((i<=p)&&(t!=".")&&(t!=","))
{
# On prend le point,
# mais pas la virgule.
if(t!=",")
chaine=chaine t;
i++;
t=tableau[i];
}

# La fin de l'expression rationnelle
# a été trouvée, on sort.
if(t==",")
{
fin_trouve=1;

break;
}
}
}
}
else if((t==""")||(t=="'"))
{
chaine=chaine t;

# Mémorisation du type de quote
# ( double ou simple )
# encadrant l'expression
# rationnelle.
s=t;

i++;
t=tableau[i];
while((i<=p)&&(quote_trouve==0))
{
if(t=="")
{
n++;
}
else
{
for(x=1; x<=n; x++)
chaine=chaine "";

q=n;

indic=0;
# Echappement
# au cas où.
if((n>0)&&((2*int(q/2.0))==n)&&((t=="&")||(t=="+")||(t==s)))
{
indic=1;

# On n'échappe pas
# le signe +
# s'il est précédé
# de ]
# ( expression rationnelle ).
if(t=="+")
{
if(tableau[i-1]!="]")
chaine=chaine "";
else
indic=0;
}
else if(t=="&")
chaine=chaine "";
else if(t==s)
chaine=chaine "";
}

chaine=chaine t;

n=0;

# quote sans échappement :
# La fin de l'expression
# rationnelle en cours
# a été trouvée.
if((indic==0)&&(t==s))
quote_trouve=1;
}

i++;
t=tableau[i];
}

# On a trouvé la quote terminale
# de l'expression rationnelle en cours,
# donc on cherche le point,
# ou la virgule qui marque la fin
# de l'expression rationnelle globale.
while((i<=p)&&(t!=".")&&(t!=","))
{
# On prend le point,
# mais pas la virgule.
if(t!=",")
chaine=chaine t;
i++;
t=tableau[i];
}

# La fin de l'expression rationnelle
# a été trouvée, on sort.
if(t==",")
{
fin_trouve=1;

break;
}
}
}

if(fin_trouve==1)
{
# tableau[i]==","

# On complète la chaîne,
# de manière à l'adapter
# à la norme pcre.
chaine=chaine "."/i",";

# Et puis, on complète
# le reste de la fonction.
i++;

while(i<=p)
{
t=tableau[i];

chaine=chaine t;

i++;
}
}

return(chaine);
}
{
line=$0;
line2=$0;

if(line ~ /=[ t]*split[ ]*(/)
{
r=split(line, tableau2, /=[ t]*split[ ]*(/);

for(l=1; l<=r; l++)
{
u=tableau2[l];

if(l==1)
line=u;
else
{
line=line "= preg_split(";
v=change(u);
line=line v;
}
}
}

if(line ~ /eregi[ ]*(/)
{
r=split(line, tableau2, /eregi[ ]*(/);

for(l=1; l<=r; l++)
{
u=tableau2[l];

if(l==1)
line=u;
else
{
line=line "preg_match(";
v=change_i(u);
line=line v;
}
}
}

if(line ~ /ereg[ ]*(/)
{
r=split(line, tableau2, /ereg[ ]*(/);

for(l=1; l<=r; l++)
{
u=tableau2[l];

if(l==1)
line=u;
else
{
line=line "preg_match(";
v=change(u);
line=line v;
}
}
}

if(line ~ /eregi_replace[ ]*(/)
{
r=split(line, tableau2, /eregi_replace[ ]*(/);

for(l=1; l<=r; l++)
{
u=tableau2[l];

if(l==1)
line=u;
else
{
line=line "preg_replace(";
v=change_i(u);
line=line v;
}
}
}

if(line ~ /ereg_replace[ ]*(/)
{
r=split(line, tableau2, /ereg_replace[ ]*(/);

for(l=1; l<=r; l++)
{
u=tableau2[l];

if(l==1)
line=u;
else
{
line=line "preg_replace(";
v=change(u);
line=line v;
}
}
}

printf ("%sn", line);

# if(line!=line2)
# {
# print "D'abord: " line2 " Ensuite: " line > "/dev/stderr"
# }
}

----------------------------------------------------------------------


Ouf, ensuite le script en Bourne Shell qui s'occupe de la récursivité :

dir_init doit être alimenté avec le répertoire racine, dans lequel
sont installés filtre.awk et migration.sh

Ce script est destiné à être lancé en tant que root.

--------------------------------------------------------------------

#!/bin/sh

# Propriétaire et groupe
# des scripts *.php
perm="root:root"

# Variables de configuration, c'est le répertoire de départ.
dir_init="/var/www/html/php"

# Et le script awk de filtre.
filtre_awk=${dir_init}"/filtre.awk"

# On calcule le répertoire absolu courant.
# Quand le paramètre n'est pas présent,
# c'est le répertoire de départ, sinon
# c'est le paramètre du script.
if [ $# -eq 0 ]; then
dir=${dir_init}
else
dir=$1
fi

# Positionnement dans le répertoire courant.
cd ${dir}

# On parcourt le répertoire courant en sélectionnant
# tous les scripts php.
for i in `ls *.php 2>/dev/null`; do
if [ -f $i ]; then
file=${dir}"/"${i}
file2=${dir}"/super_new_"${i}".txt"

# Traitement du script *.php,
# là on se contente d'afficher son nom
# avec son chemin absolu.
#
echo "" > /root/tmp2.txt
echo ${file} >> /root/tmp2.txt
echo ${file2} >> /root/tmp2.txt

awk -f ${filtre_awk} -- ${file} 1>${file2} 2>>/root/tmp2.txt

chown $perm ${file2}
chmod 777 ${file2}

t1=`ls -l ${file} | awk '{ print $5; }'`
t2=`ls -l ${file2} | awk '{ print $5; }'`

if [ ${t1} -ne ${t2} ]; then
cat /root/tmp2.txt >> /root/tmp.txt
fi
fi
done

# On lit tous les sous-répertoires du répertoire courant,
# et on relance le même script, avec ces sous-répertoires
# comme paramètre, pour que ces processus fils
# se positionnent sur ces sous-répertoires.
#
# Dans un premier temps, on teste le traitement
# effectué uniquement sur les script *.php
# du répertoire de départ, donc
# il y a annulation du caractère récursif.
#
for j in `ls -d * 2>/dev/null`; do
if [ -d $j ]; then

# Calcul du sous-répertoire absolu.
direct=${dir}"/"${j}

/var/www/html/php/migration.sh ${direct}
fi
done

# A la fin du script, on remonte vers le répertoire
# père.
cd ..

exit 0

-------------------------------------------------------------

Voilà, je vous prie de bien vouloir m'excuser de la dimension du code.

Je suis à votre entière disposition pour répondre aux questions.

Le script filtre.awk, assume que l'expression rationnelle Posix à
migrer, sera sur la même ligne que le nom de la fonction, et sera sur
une seule ligne, autrement il y aurait une erreur, il serait impossible
de détecter la suite de l'expression rationnelle... Mais qui aurait
l'idée de mettre une expression rationnelle sur plusieurs lignes ?

Je ne sais pas trop si c'est valable pour de l'utf-8 aussi, je vous
laisse le soin d'en juger.

Pour vérifier ce code, il faudrait chercher s'il y a des cas, où il y
a des erreurs qui ne donnent lieu à aucun message d'erreur.

Enfin, filtre.awk assume qu'une variable expression rationnelle, ne
peut être accolée avec une expression rationnelle partielle adjacente,
qu'avec un point ( . ) pour la concaténation. Dans le cas contraire, le
code serait plus compliqué. ;)

Théoriquement, ces scripts assurent la migration avec détection
d'erreurs, sans qu'il y ait besoin d'autres vérifications que de faire,
après :

cat /root/tmp.txt | grep Erreur

S'il y a des lignes qui apparaissent, évidemment... ;(

Merci beaucoup de vos réponses.

Bien à vous.

Amicalement.

Jean-François Ortolo

--
Visitez le site http://www.pronostics-courses.fr/
donnant des Statistiques, Pronostics et Historiques graphiques
très élaborés.

Les Statistiques sont calculées d'après une base de données
allant du 1er Janvier 2000 jusqu'à très récemment.
1 2 3