Forum PHP de l'AFUP 2009
De Patchwork.
Page de préparation de notre conférence acceptée pour le Forum PHP de l'AFUP.
Slides de la présentation : Fichier:Patchwork-preprocesseur-AFUP2009.pdf
Sommaire |
Comment améliorer PHP ? avec un préprocesseur !
Dans cette conférence nous proposons de présenter les avantages significatifs qu'un préprocesseur de code intercalé au sein de la fonction __autoload() nous a apporté, pour :
Améliorer PHP en tant que langage de programmation :
- ajouter une nouvelle dimension d'organisation du code, proche de ce que permet la programmation orientée aspect, sous la forme d'un nouveau concept que nous avons nommé superposition de classes,
- ajouter des constructeurs et destructeurs statiques au langage,
- générer du code spécialisé en fonction du contexte d'exécution : supprimer le code mort, résoudre les constantes ou des portions de code de façon statique,
- ou de façon plus anecdotique, libérer le constructeur PHP4 sans casser le code PHP4 ou 5 existant.
Améliorer PHP en tant qu'environnement d'exécution :
- vérifier l'encodage des sources, supprimer les BOM parasites, pour un code 100% UTF-8,
- analyser le code pour anticiper certaines erreurs (divergence des tables de traduction) ou pré-remplir certains caches (tables de traduction),
- substituer des fonctions par d'autres, pour enrichir/corriger/intercepter leur comportement, et par exemple : charger une implémentation PHP de certaines extensions lorsque leur version native est nécessaire mais non présente (mbstring, iconv, etc.) ou détecter les problèmes potentiels liés à la casse des noms de fichiers sous Windows,
- désactiver l'opérateur "@" pour voir tous les messages d'erreur
- booster le mécanisme d'autoload par insertion/substitution de marqueurs à la volée.
Certains de ces sujets ont déjà été discutés dans la communauté (constructeur statique), d'autres sont possibles grâce à des extensions (runkit pour la substitution de fonctions) et d'autres encore sont à notre connaissance évoqués pour la première fois (superposition de classe, marqueurs pour autoload).
Nous baserons notre conférence sur l'implémentation 100% PHP que nous avons faite d'un tel préprocesseur dans notre framework de démonstration, Patchwork.
Biographies
Yann Bogdanovic
Yann est ingénieur R&D chez IntellAgence Technologies. Il y a deux ans, il est devenu le premier membre de la "communauté Patchwork". L'opportunité s'étant présentée de concilier centres d'intérêts et vie professionnelle, il a rejoint l'entreprise en septembre dernier.
Nicolas Grekas
Après ses études, Nicolas a fondé IntellAgence Technologies, une société spécialisée dans les services en ligne pour les chercheurs. Il est l'auteur du framework Patchwork, résultat de huit années de pratiques et de réflexions en PHP/Web. Cette conférence est pour lui l'occasion de contribuer à la communauté PHP et de sortir ses travaux de leur confidentialité.
Plan détaillé
Introduction
Présentation de Patchwork
- Protocole HTTP et gestion du cache
- Portabilité des applications
- Internationalisation
- Sécurité
- Bibliothèque de code
- Structuration du code
Mécanisme du préprocesseur
- Transformer le code source automatiquement
- Déroulement
- Contrôleur frontal (1er fichier exécuté)
- Charger __autoload et le code du préprocesseur
- Transformer les codes chargés par __autoload
- Exécuter le code source compilé
- Mettre en cache
Améliorations à l'exécution
Vérifier l'encodage des sources
- UTF-8
- Normalisation canonique composée (NFC)
http://unicode.org/reports/tr15/images/UAX15-NormFig3.jpg
- Suppression des BOM parasites
- Normalisation des fins de ligne (CRLF, CR, LF)
- Amélioration possible :
<?php declare(encoding='ISO-8859-1'); // Backport PHP6
Analyse anticipative du code
- Divergeances des tables de localisation
T('Bonjour ' . $nom) vs sprintf(T('Bonjour %s'), $noms)
- Préremplissage des tables de localisation
Substituer des fonctions
- Remplacer les appels à fonction() par maFonction()
- Usages concrets
- Remplacer par un équivalent plus performant :
rand => mt_rand
md5 => hash('md5',
- Contourner des bugs :
getcwd => patchwork_getcwd
mb_encode_mimeheader => trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead.',
- Greffer des fonctionnalités :
file_exists => win_file_exists : contrôle de la casse des noms de fichiers sous Windows header => patchwork::header : prendre le contrôle de certaines entêtes
- Ajouter des fonctionnalités :
iconv => utf8_iconv::iconv : charger un substitut PHP d'iconv à la demande normalizer_normalize => Normalizer::normalize : backporter des fonctions de PHP 5.3
- Limité par la résolution de nom à l'exécution
- Améliorations possibles : runkit
Casser l'opérateur de silence
- Idée originale : extension scream
"L'extension scream donne la possibilité de désactiver l'opérateur de contrôle d'erreur, de manière à ce que toutes les erreurs soient rapportées."
- Voir toutes les erreurs, même celles qui veulent se cacher
<?php @include 'parse_error.php'; // cherche des heures le pourquoi de cette satanée page blanche :)
- Transformation : remplacer les token "@" par un simple espace
- Limitation : pas avant l'initialisation du préprocesseur
Booster l'autoload
- Charger les classes, deux extrêmes :
- manuellement : require
- automatiquement : __autoload() + include_path
- Le plus vite possible :
- __autoload() : alourdit l'exécution mais gestion automatique des dépendances
- require : des chargements superflus car gestion difficile des dépendances
- Le meilleur des deux : substituer l'autoload par des require à l'exécution
- permettre à __autoload() de connaître son point d'appel dans le code
- insérer un require à cet endroit
- Performance
- Pertue due aux marqueurs
- Gain grâce aux substitutions
- Pertue due aux marqueurs
- Benchmark
- Fonction reject précédente : +15%
- Génération d'une page : -20%
- Fonction reject précédente : +15%
- Discussion
- PHP creux vs accès disques, BDD, etc.
- Réalisable par les compilateurs d'opcode ?
- PHP creux vs accès disques, BDD, etc.
Spécialiser en fonction du contexte d'exécution
<?php
define('IS_WINDOWS', '\\' === DIRECTORY_SEPARATOR);
if (!function_exists('utf8_decode'))
{
function utf8_decode($string)
{
// ...
}
}
$a = file_get_contents('data/utf8/quickChecks.txt');
$a = explode("\n", $a);
define('UTF8_NFC_RX', '/' . $a[1] . '/u');
<?php
define('IS_WINDOWS', /*<*/'\\' === DIRECTORY_SEPARATOR/*>*/);
/**/if (!function_exists('utf8_decode'))
/**/{
function utf8_decode($string)
{
// ...
}
/**/}
/**/$a = file_get_contents('data/utf8/quickChecks.txt');
/**/$a = explode("\n", $a);
define('UTF8_NFC_RX', /*<*/'/' . $a[1] . '/u'/*>*/);
Améliorations du langage
Superposition de classes
- Le sujet : modulariser son code
- Classiquement : décomposition fonctionelle + include_path
- Une nouvelle dimension : superposition de classes
- Programmation orientée aspect et injection de dépendances made easy
Héritage multiple d'applications
- Retour sur l'include_path
- Un développement de l'include_path
- Un fichier de configuration par application
- C3MRO : héritage multiple
Superposition de classes
- Supoerposition de fichiers
- Pas assez granulaire
- Superposition de classes
<?php
class toto extends self
{
// ...
}
- Patchwork - class/toto.php
<?php
class toto
{
function isAuth() {...};
}
- Applicatif – class/toto.php
<?php
class toto extends self
{
function isAuth()
{
$is_auth = parent::isAuth();
$this->logAuth($is_auth);
return $is_auth;
};
function logAuth() {...};
}
- Chaque méthode devient un hook : Programmation orientée aspect
- Découple interface et implémentation : Injection de dépendance
- Compatible avec les codes existants
- Réhabilite les méthodes statiques
- Transformations du préprocesseur : renommage des classes et des self
- Cas concrets
- Sur-couche fonctionnelle
- Système de plugin
- Contournement de bug
- Thème
- Aspect
- etc.
- Bonus à l'usage : un excellent guide de factorisation !
Constructeurs et destructeurs statiques
- Même intérêt que pour les objets, mais pour les classes
<?php
class toto
{
static function __constructStatic() {...}
static function __destructStatic() {...}
}
- Exemples
- Initialiser les variables statiques
- Mettre en cache les tables de localisation
- Encore plus indispensable pour la superposition de classes
Libérer le constructeur PHP4
En PHP 5 :
<?php
class toto
{
function __construct(&$un, $deux = 2) {...}
}
En PHP 4 :
<?php
class toto
{
function toto(&$un, $deux = 2) {...}
}
Rétro-compatibilité : fonctionne aussi en PHP 5
Superposition de classe :
<?php
class toto__3
{
function toto(&$un, $deux = 'default') {...}
}
Compatibilité PHP 4 :
<?php
class toto__3
{
function __construct(&$a1, $a2 = 'default') {$this->toto($a1, $a2);}
function toto(&$un, $deux = 2) {...}
}
En réalité un peu plus compliqué à cause du nombre de paramètres potentiellement variable
Conclusion
- Un bénéfice mesurable
- Utilisé en production
- Au cœur de Patchwork
- Importer certaines idées dans PHP ?
- Une sous-couche pour les autres framework ?
- A vous de jouer !