Booster drastiquement les performances de PHP

Historiquement, PHP était réputée comme une solution lente et pour amateurs par rapport à d’autres technologies telle que Java.

Depuis ces 10 dernières années, de l’eau a coulé sous les ponts et PHP a grandement évolué au point d’avoir des performances incroyables. Découvrez différentes optimisations serveur et code pour améliorer les performances de vos sites PHP.   Avant de vous livrer les précieux conseils, regardons d’abord comment fonctionne PHP. Lorsque l’on charge une page PHP, le serveur Web (Apache) remarque qu’il s’agit d’un fichier PHP et instancie le moteur d’exécution PHP en lui donnant le fichier cible. Ensuite, ce moteur parcourt le code source de ce fichier et génère un binaire en C, et c’est ce fichier qui est exécuté par le processeur. Enfin, lorsque tout est terminé, le moteur d’exécution est détruit, de même que le binaire. Quand on voit ce parcours, on se rend compte que même les lenteurs d’il y a 2 ans avec PHP 5 n’étaient au final pas si mal. A présent que vous avez ce plan en tête, commençons les optimisations ! 

Serveur

Les optimisations côté serveur sont les plus nombreuses, mais aussi les plus simples à mettre en place car elles demandent peu d’investissement et améliorent grandement les performances de toutes les pages PHP.

Tout d’abord, si vous ne l’avez pas encore fait, migrer votre vieille installation de PHP par PHP 7.1-fpm ou 7.2-fpm. Et si votre serveur ne dispose pas dans son repository de versions récentes de PHP, prenez 15 minutes et installez un repository dédié à jour. La différence de performances entre PHP 5.6 et 7.x valent vraiment ce petit investissement de temps.

Vous l’aurez peut-être remarqué, je parle de PHP7.1 et je vous dis d’installer PHP7.1-fpm. Pourquoi ? La différence entre PHP et PHP-FPM est la manière dont le moteur d’exécution est géré. En mode FPM, nous avons un service PHP qui instancie automatiquement X moteur d’exécutions et les gardes allumés non-stop, gagnant ainsi du temps à chaque appel.

Attention néanmoins, la configuration de Apache doit être revue pour utiliser le socket FPM plutôt que le module apache nommé « mod_php ». Il faudra revoir vos vhosts.

Si vous utilisez Nginx au lieu de Apache, alors vous utilisez probablement déjà PHP-FPM. D’ailleurs, je vous conseille d’utiliser Nginx plutôt que Apache car ce premier gère mieux le fort trafic ainsi que les ressources statiques (images, js, etc…). Néanmoins, pour l’exécution de PHP-FPM, les deux serveurs Web se valent.

A présent que PHP est installé et que votre vhost est à jour, vous voulez tester la différence… et vous allez me traiter de charlatan, car la différence de vitesse est légère. Et pour cause !

Comme je vous l’ai expliqué, l’exécution d’une page PHP nécessite de parcourir le code source et de générer un binaire en C, et à partir de PHP 7.0, il y a eu de grosses modifications dans la génération de ce binaire. La création de ce fichier prend plus de temps que sur PHP 5.6, car il y a énormément d’optimisations qui sont effectuées pour générer un binaire plus rapide et performant.

Ainsi, la génération du binaire prend plus de temps avec les versions récentes de PHP mais l’exécution de ce fichier est plus rapide. En termes de balance, l’exécution de bout en bout d’un fichier PHP est de l’ordre de 10% plus rapide avec les versions récentes de PHP.

Et là vous allez me dire « Mais ! J’ai lu partout que PHP 7 était 50% plus rapide que PHP 5. C’était des mensonges ? » Oui… Et non. Disons plutôt que les benchmarks possèdent l’arme secrète pour booster PHP 7 : Opcache !

Opcache est une extensions PHP qui se place entre le serveur Web et le processeur, et optimise le workflow. Ainsi par exemple, Opcache met en cache le binaire généré, afin de le fournir dès le second appel sans avoir à reparcourir le code source ou générer à nouveau un binaire en C, retirant ainsi tout le temps de compilation du workflow. C’est magique !

Si vous souhaitez aller encore plus loin, voici différentes configurations Opcache que vous pouvez utiliser :

opcache.enable = 1

Cette clé est obligatoire pour utiliser Opcache.

opcache.enable_cli = 1

Cette clé est optionnelle, elle vous permet de bénéficier des performances de Opcache aussi lorsque vous appelez PHP en lignes de commandes.

opcache.interned_strings_buffer = 32

Cette clé vous permet de gérer la taille du buffer qui stockera les différentes chaines de caractères. Quand je parle de chaines de caractères, je ne parle pas seulement des variables de type « string ». Je parle aussi des noms de fonctions, des noms de classes, des namespaces, etc…

En fonction du nombre de classes ou de leur complexité, il convient d’adapter cette clé. Idéalement je recommande 32 (mo), mais pas tous les serveurs le supportent. N’hésitez pas à baisser à 16 ou 8 si besoin.

opcache.max_accelerated_files = 100000

Il s’agit ici du nombre maximum de fichiers PHP que Opcache va mettre en cache de son côté. Je vous conseille de faire un simple « count » de tous les fichiers se terminant par PHP dans votre dossier /var/www, ou si vous avez la flemme comme moi et que vous avez un gros serveur, mettez simplement le max autorisé par Opcache.

opcache.max_wasted_percentage = 15

Pourcentage de mémoire autorisée à être gaspillée avant que Opcache ne vide l’intégralité de son cache. Une valeur entre 10 et 15 pourcent est recommandée.

opcache.validate_timestamps = 1

Il s’agit ici d’un flag permettant de définir si Opcache doit vérifier si le fichier PHP a été modifié avant d’essayer de retourner le binaire associé mis en cache. Pour des performances optimales il est recommandé de mettre 0, mais cela aura pour impact de devoir redémarrer le service php-fpm a chaque modification d’un fichier PHP. Sinon mettez 1 et configurez la clé suivante.

opcache.revalidate_freq = 1

Si la clé précédente a été définie a 1, alors cette clé défini le nombre de secondes durant lesquels Opcache resservira le même fichier binaire sans vérifier si le fichier source PHP a été modifié.

A présent que le serveur est configuré et optimisé, vous devriez voir un gain de temps de chargement de l’ordre de 50%. Et ça, c’est beau !

Code

Passons à présent aux optimisations côté code. Ces optimisations sont plus compliquées à mettre en place et nécessitent un plus grand investissement de temps pour modifier le code existant. C’est pourquoi je recommande surtout de suivre ces règles pour le futur développement plutôt que de modifier un projet existant. Tout d’abord, à partir de PHP 7, il y a enfin une réelle différence entre les chaines entre guillemets et entre apostrophes. Lorsqu’il s’agit de concaténation, les guillemets sont plus rapides et nécessitent moins d’opérations processeur qu’une concaténation de plusieurs segments. Retenons donc simplement que :

 

Personnellement, cela m’a pris du temps pour perdre mon reflexe de mettre des apostrophes et des points partout. Ensuite, à partir de PHP7.2, les switch sont optimisés pour être plus performants que les suites de if/else, contrairement à 7.1 et antérieur qui transforment simplement les switch en suites de if/else. Autre point très important, les tableaux ont été optimisés à partir de PHP 7, et sur plusieurs plans. D’abord, les tableaux statiques sont super optimisés pour le temps de chargement et pour la mémoire. Je vous recommande donc vivement de les utiliser. Ensuite, les tableaux incrémentiels ont aussi été optimisés. Ainsi, si vous avez un tableau ou toutes les clés sont des nombres qui se suivent (sans trou et dans le bon ordre), alors l’impact mémoire est aussi diminué.  

Conclusion

A force d’utiliser ces optimisations sur tous mes projets, j’ai fini par les mémoriser par cœur. Cela m’a pris du temps à apprendre, mais le gain est clairement visible à l’œil nu. Et c’est d’autant plus agréable quand, lors des démos avec les clients, ces derniers sont persuadés que si les pages se chargent aussi vite c’est parce qu’elles sont mises en cache par le navigateur. Ce petit plaisir coupable que j’ai à chaque fois que je vois leur tête quand je leur montre que non, c’est bien le serveur qui a généré la page en 30 millisecondes, je ne m’en lasserai jamais.