Optimiser les pages PHP

Transcription

Optimiser les pages PHP
Optimiser les pages PHP
Toutes les astuces permettant d'optimiser la vitesse d'exécution de vos scripts PHP...
Généralités
Voici quelques règles qui vous permettront d'optimiser facilement votre script :
•
•
•
Les structures du langage sont toujours préférable aux fonctions : privilégier par exemple $var === NULL
à is_null($var).
Les fonctions préexistantes de PHP sont préférables aux expressions régulières : privilégier par exemple
ctype_alpha($var) à eregi("^[a-z]*$", $var).
Toujours privilégier des fonctions internes de PHP à des fonctions utilisateur, notamment dans les boucles
et opérations sur des tableaux.
Trouver la fonction adaptée
Utiliser $str = file_get_contents($file); plutôt que
$fp = fopen($file, 'r');
while (($line = fgets($fp)) !== FALSE)
$str.= $line;
fclose($fp);
Evaluer les performances d'un script
Voici un code très simple vous permettant de facilement évaluer le temps d'exécution d'une portion de code.
function getMicroTime() {
list($usec, $sec) = explode(' ', microtime());
return ((float) $usec + (float) $sec);
}
$timeStart = getMicroTime();
for ($i = 0; $i < 10000; $i++) { /* Nombre en fonction des performances de
votre machine et du script a évaluer. */
// Portion de code a tester.
}
$timeEnd = getMicroTime();
// Affichage du temps d'exécution.
echo ($timeEnd - $timeStart);
Exemple
Dans cet exemple, nous allons tenter d'optimiser un code permettant d'ôter la chaîne "[]" à la fin d'une chaine de
caractère, si elle est présente.
Cas 1 :
$str = preg_replace('/\\[\\]$/D', '', $str);
Chaîne
essai
essai[]
Temps d'exécution
0,54s
0,74s
Cas 2 :
$nameLength = strlen($name);
if ($name{$nameLength - 2} == '[' && $name{$nameLength - 1} == ']')
$name = substr($name, 0, -2);
Chaîne
essai
essai[]
Temps d'exécution
0,51s
0,87s
Cas 3 :
if (substr($name, -2) == '[]')
$name = substr($name, 0, -2);
Chaîne
essai
essai[]
Temps d'exécution
0,39s
0,63s
Conclusions
Il apparaît que la dernière méthode est la plus performante, ce était relativement prévisible, car c'est la méthode qui
nécessite le moins d'opérations élémentaires. Il est toutefois surprenant que la méthode 2 soit la moins
performante, mais cela s'interpréter par le fait que l'utilisation des crochets pour accéder à un caractère équivaut
plus ou moins à l'utilisation de la fonction substr().
Enfin, on peut constater que la méthode 1 n'est pas trop mauvaise, malgré l'utilisation d'une expression régulière.
Ainsi, il est souvent préférable d'utiliser une expression régulière plutôt que plusieurs appels à des fonctions tels
que strpos() ou substr().
Au niveau de la configuration PHP
Certaines directives de configuration de PHP limitent les performances. Voici la configuration recommandée pour
certaines d'entre elles :
register_globals à off : réduit le temps d'enregistrement des variables en début de script et l'utilisation mémoire
(évite la redondance de données).
De nombreux scripts relève sur cette option et ne fonctionneront pas sans.
Mettre cette option à off permet d'améliorer la propreté et la sécurité de votre code PHP, tout en assurant un
meilleur débuggage.
register_long_arrays à off : réduit le temps d'enregistrement des variables en début de script et l'utilisation mémoire
(évite la redondance de données).
D'anciens scripts peuvent encore utiliser ces variables.
output_buffering à 4096 (par exemple) : plutôt que d'envoyer les données directement sur la sortie standard à
chaque appel de echo ou print par exemple, conserver les données en mémoire avant de les envoyer permet de
réduire le nombre d'accès à la sortie standard et de diminuer le nombre de paquets envoyés sur le réseau. Le gain
de performance dépend toutefois également grandement de la configuration du serveur et du type de script PHP
dont il s'agit.
register_argc_argv à off : réduit le temps d'enregistrement des variables en début de script et l'utilisation mémoire
(évite la redondance de données).
magic_quotes_gpc à off : parce que toutes les données utilisateurs n'ont pas besoin d'être systématiquement
échappés (en l'occurrence, cette option sert généralement pour les données enregistrées dans une base de
données), désactiver cette option peut améliorer les performances.
Cette option a un impact sur la sécurité ! De nombreux scripts ne relève que sur cette option pour assurer le bon
formatage des données lors d'insertions dans une base de données notamment.
variables_order à GPCS : ainsi, la variable d'environnement $_ENV n'est pas générée, ce qui réduit le temps
d'enregistrement des variables en début de script et l'utilisation mémoire (il est possible alors d'accéder aux
variables d'environnement avec la fonction PHP getenv()).
Note : les réglages des paramètres décris ci avant font partie de la configuration recommandée de PHP et certaines
options, comme register_long_arrays, sont déjà dépréciées.
La désactivation de certaines options PHP ayant trait avec la sécurité peut améliorer considérablement les
performances, en évitant certaines vérifications. Cependant, l'aspect sécurité prime sur l'aspect performances en ce
qui concerne ces options, tout particulièrement chez pour des hébergements mutualisée.
Les options principales sont : safe_mode, safe_mode_gid et open_basedir.
Les options suivantes n'ont qu'un léger impact sur les performances :
always_populate_raw_post_data à off : réduit le temps d'enregistrement des variables en début de script et
l'utilisation mémoire en ne créant pas la variable $HTTP_RAW_POST_DATA lorsqu'elle ne contient pas de donnée.
expose_php à off : réduit la quantité de donnée envoyée.
Les options suivantes influes sur les performances lors de l'utilisation des sessions :
session.auto_start à off : évite d'ouvrir une session à chaque script PHP (utiliser la fonction session_start() au
besoin).
session.use_trans_sid à off : évite à PHP le travail de réécriture des URLs de la page (si vous tenez vraiment à ce
que l'ID de session soit transmis via l'URL, faite-le manuellement, au moins vous serez sûr(e) que l'ID sera bien
transmis dans tous les cas de figure).
Utilisation des expressions régulières
Quand les utiliser ?
Une règle d'or : ne jamais utiliser une expression régulière là où une autre fonction PHP peut faire l'affaire. Ainsi,
préférer str_replace() à preg_replace(), explode() à preg_split().
Voici une liste de quelques fonctions pouvant parfois remplacer avantageusement les expressions régulières :
•
•
•
Remplacements : str_replace(), substr_replace(), strtr()...
Recherches : strpos(), stripos(), strchr(), strrchr()...
Comparaisons : strcmp(), strncasecmp()...
ereg vs preg
Les fonctions preg sont généralement plus performantes que les ereg pour des expressions régulières complexes
(la différence reste cependant très faible lorsqu'il s'agit d'expressions régulières simples). De plus, les fonctions
preg, en plus de supporter l'UTF-8, permettent d'effectuer des masques plus sophistiqués que leurs équivalents
ereg.
Méfiez-vous lorsque vous passer d'une fonction à l'autres, il y a des différences entres les syntaxes POSIX (pour
les fonctions ereg) et les syntaxes compatibles Perl (pour les fonctions preg) qui peuvent parfois amener à des
comportements différents et modifier le résultat final.
Macros
Il arrive couramment de vouloir remplacer certaines occurences d'une chaîne de caractère par des valeurs
correspondantes. C'est le cas par exemple dans un système de cache où l'on souhaite gérer des variables
dynamiques, comme une ID de session par exemple.
Voici par exemple un tableau où les clefs correspondent aux occurences à remplacer par leur valeur respectives :
$vars = array(
'{var1}' =>
'{var2}' =>
'{var3}' =>
'{var4}' =>
'{var5}' =>
);
'value1',
'value2',
'value3',
'value4',
'value5',
Voici diverses implémentations courantes permettant d'atteindre notre but :
$str = strtr($str, $vars);
$str = str_replace(array_keys($vars), array_values($vars), $str);
function replaceVar($sub) {
global $vars;
if (isset($vars[$sub[1]]))
return $vars[$sub[1]];
else
return '{'.$sub[1].'}';
}
$str = preg_replace_callback('/\\{([^\\}]+)\\}/', 'replaceVar', $str);
Cette méthode, utilisant la fonction preg_replace_callback() devrait en principe être la méthode la plus
rapide, car il s'agit grossièrement d'un algorithme O(n) tandis que les méthodes précédentes sont O(n*m).
Cependant, sa relative lenteur est dûe au coût de l'appel à la fonction replaceVar().
$vars = array(
'/{var1}/'
'/{var2}/'
'/{var3}/'
'/{var4}/'
'/{var5}/'
);
=>
=>
=>
=>
=>
'value1',
'value2',
'value3',
'value4',
'value5',
$str = preg_replace(array_keys($vars), array_values($vars), $str);
Comparatif de performances
Méthode
Temps d'exécution
strtr
0,42s
str_replace
0,13s
preg_replace_callback
0,19s
preg_replace
0,26s
Il est important de noter que la méthode utilisant preg_replace_callback devient la plus rapide lorsqu'il y a peu
d'occurences à remplacer. La performance de l'algorithme compense alors les appels à la fonction de
remplacement.
On peut constater que la méthode utilisant la fonction strtr() est relativement peu performante. Ceci est dû au
fait que cette fonction a en réalité un comportement un peu différent que str_replace() : elle cherche toujours
en priorité la chaîne la plus longue et ne travaille pas sur des segments déjà modifiés. Le code ci-dessous illustre
cette différence :
$str = 'Le premier sera le dernier';
$replace = array(
'premier' => 'dernier',
'dernier' => 'premier'
);
echo str_replace(array_keys($replace), array_values($replace), $str); /* Donne :
"Le premier sera le premier" */
echo strtr($str, $replace); // Donne : "Le dernier sera le premier"
Divers
Ne pas générer systématiquement les variables :
Il est inutile de générer une variable si vous n'êtes pas sûr(e) de vous en servir, notamment lorsqu'il s'agit de longs
tableaux statiques.
$LANG = array(
// ...
'ro' => 'Roumain (Moldavie)',
'ro-mo' => 'Roumain (Moldavie)',
'ru' => 'Russe',
'ru-mo' => 'Russe (Moldavie)',
'sb' => 'Sorbian',
'sk' => 'Slovaque',
'sl' => 'Slovène',
'sq' => 'Albanais',
// ...
);
function getLang() {
return array(
// ...
'ro' => 'Roumain (Moldavie)',
'ro-mo' => 'Roumain (Moldavie)',
'ru' => 'Russe',
'ru-mo' => 'Russe (Moldavie)',
'sb' => 'Sorbian',
'sk' => 'Slovaque',
'sl' => 'Slovène',
'sq' => 'Albanais',
// ...
);
}
substr($str, $i, 1) vs $str{$i}
substr($str, $i, 1) à remplacer par $str{$i}
substr($str, $i, 2) à remplacer par $str{$i}.$str{$i+1}
substr($str, $i, 3) à remplacer par $str{$i}.$str{$i+1}.$str{$i+2}
L'appel à la fonction substr() est plus lent que l'utilisation d'une structure du langage, cependant au-delà de 3
caractères, substr() se montre à nouveau plus rapide.
echo (et print)
echo "str".$var."str"; à remplacer par echo "str", $var, "str";
Avec des points, la chaîne est d'abord concaténée avant d'être envoyée sur la sortie, ce qui a tendance à diminuer
très légèrement les performances (ne fonctionne qu'avec echo).
On recommande souvent de privilégier echo à print pour de meilleures performances, parce que print retourne
quelque chose (toujours la valeur 1) contrairement à echo. La différence de performance que j'ai pu constater est
toutefois franchement négligeable...
for, while
for ($i = 0; $i < strlen($str); $i++) à remplacer par for ($i = 0, $length =
strlen($str); $i < $length; $i++)
Le fait d'intégrer strlen($str) dans la partie conditionnel n'est pas une bonne manière de programmer et
diminue les performances, car la longueur de la chaîne (qui reste en principe fixe) est réévaluée à chaque passage
de la boucle. strlen() n'est qu'un exemple de fonction possible.
A ce propos, une meilleure structure est possible dans l'exemple ci-dessus :
for ($i = 0, $length = strlen($str); $i < $length; $i++) à remplacer par for ($i = 0;
isset($str{$i}); $i++)
Si cette structure est plus rapide, c'est tout simplement qu'elle ne fait que reproduire l'algorithme de la fonction
strlen(). Voici un équivalent de la première proposition (qui n'est pas un code PHP valide, mais une illustration) :
for ($i = 0, for ($length = 0; isset($str{$length}); $length++); $i < $length; $i++).
On comprend mieux maintenant pourquoi la deuxième proposition est plus performante, malgré le fait que l'on
effectue l'opération isset(), qui est en fait également utilisé en interne (du moins l'équivalent en C) lors de l'appel à
strlen().
unset
Lorsque vous n'utiliser plus une variable, penser à la détruire, cela fera autant d'espace mémoire en moins (même
si cela n'agit pas directement sur la vitesse d'exécution).
Contrôler le type de donnée
Vérifier qu'une chaîne représente un entier
if ($str == (string) (int) $str)
Cette structure est à préférer de tests du type if (preg_match("/^[0-9]+$/", $str)) ou if
(ereg("^[0-9]+$", $str)).
Vérifier qu'une chaîne représente un nombre
if ($str == (string) (float) $str)
Vérifier qu'une chaîne représente un booléen
if ($str == (string) (bool) $str)
Ecrit par Olivier BICHLER, le 15/02/2007 22:14:24

Documents pareils