Moderniser son application legacy PHP
5 septembre 2019
Je travaille depuis plus de 8 ans sur un produit dont la partie web est développée en PHP. Lorsque je suis arrivé, nous étions en PHP 5.2 et le code était très procédurale. Au fil des années, nous avons fait évoluer le produit : mise en place d'un développement plus orientée objet, passage en PHP 5.3. Certains points techniques nous bloquaient pour monter en version supérieure à PHP 5.3. Nous nous retrouvions donc assez rapidement dans l'impossibilité d'utiliser bons nombres de modules et librairies développés par la communauté. Et avec la fin de vie de PHP 5.3 puis l'arrivée de PHP 7.0, il nous fallait absolument pouvoir résoudre ces problèmes.
Pour pouvoir être compatible avec PHP 5.4+, nous devions trouver un moyen :
magic quotes
.La seule solution qui nous est venue à l'esprit au début était de repasser sur l'intégralité du code et de corriger ce qui nous bloquait. Mais autant dire que la charge en termes de développements et de tests de non-régression était énorme et même pas envisageable ! Et en cherchant sur le net, un de mes collègues a trouvé une directive PHP qui allait résoudre tous ces problèmes.
PHP propose une directive qui permet de charger un fichier automatiquement avant chaque exécution d'un script PHP : auto_prepend_file
C'est grace à cette directive que nous allons pouvoir contourner tous nos problèmes de compatibilité et monter de version PHP.
Cette solution n'est évidemment pas la plus propre possible, mais nous a permis d'avoir un très bon retour sur investissement ! Donc je suis sûr qu'elle peut intéresser certains d'entre vous 😀.
auto_prepend_file
Fichier composer.json
{
"autoload": {
"psr-4": {"MyProject\\": "src/"}
}
}
et notre polyfill :
<?php
require_once(__DIR__.'/../vendor/autoload.php');
Ayant désormais utiliser composer, nous pouvons inclure cette librairie en exécutant la commande suivante à la racine de notre projet :
composer require yidas/magic-quotes
Aperçu de notre polyfill
<?php
require_once(__DIR__.'/../vendor/autoload.php');
MagicQuotesGpc::init();
<?php
require_once(__DIR__.'/../vendor/autoload.php');
MagicQuotesGpc::init();
require_once(__DIR__.'/config.php');
Exemple (la variable $link
correspond à une ressource retournée par mysqli_connect
):
function mysql_query($query, $link)
{
return mysqli_query($link, $query);
}
<?php
require_once(__DIR__.'/../vendor/autoload.php');
MagicQuotesGpc::init();
// Toutes les fonctions mysql_* peuvent être redéclarées dans un fichier à inclure ici
function mysql_query($query, $link)
{
return mysqli_query($link, $query);
}
Nous avons désormais résolu tout nos problèmes de compatibilité. Une fois validé, nous pouvons monter en version de PHP (jusqu'à 7.1 dans notre cas à ce moment-là) et moderniser encore notre legacy.
Si vous utilisez composer (et je vous le recommande fortement), vous pouvez désormais importer et utiliser toutes les librairies présentes sur le net. Quelques exemples :
composer require symfony/dependency-injection
Vous pouvez désormais définir toutes vos dépendances dans un fichier services.yml
Aperçu de notre polyfill
<?php
require_once(__DIR__.'/../vendor/autoload.php');
MagicQuotesGpc::init();
// Toutes les fonctions mysql_* peuvent être redéclarées dans un fichier à inclure ici
function mysql_query($query, $link)
{
return mysqli_query($link, $query);
}
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
use Symfony\Component\Config\FileLocator;
$container = new ContainerBuilder();
$loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/..'));
$loader->load('services.yml');
Nous pouvons donc utiliser notre variable $container
dans tous nos fichiers de notre projet legacy.
Guzzle pour les requêtes HTTP : plus besoin d'écrire à la main ses requêtes HTTP avec curl.
Ramsey/UUID pour générer des UUID.
Et bien d'autres...
Afin que cette clause soit prise en compte, elle peut être appliquée :
php.ini
de votre serveur.user.ini
à la racine de votre projet (exemple : /var/www/my_project/.user.ini
)auto_prepend_file = /var/www/my_project/polyfill-php-compatibility.php
Le fichier polyfill-php-compatibility.php
sera donc exécuté avant tout autre fichier PHP situé dans le projet my_project
Il est possible de spécifier cette directive lors de l'exécution en ligne de commande d'un script php
php -d auto_prepend_file="/var/www/my_project/polyfill-php-compatibility.php" mon_script.php
J'espère que cet article vous donnera des idées pour moderniser vos vieux projets PHP. Et si vous avez des questions, contactez-moi sur twitter.
Mon programme "S'entraîner pour progresser en PHP" est disponible. Il vous permettra de recevoir chaque semaine un kata de code directement dans votre boîte mail, ainsi que des aides à la réalisation, des vidéos explicatives et des défis supplémentaires.