Documentation
De Patchwork.
Sommaire
|
Introduction
intro. à refaire !
Avec Patchwork, c’est le navigateur, s'il accepte le JavaScript, qui se chargera d’assembler les templates. Ainsi au lieu d’utiliser uniquement un cache sur le serveur, on a une possibilité de mise en cache de morceaux de pages par le navigateur ou le proxy, ce qui permet de faire des application très performantes. Même utilisée uniquement en mode serveur, sans JavaScript, l’architecture est très simple, et sépare proprement les différents aspects d’une application Web.
Le serveur est ainsi déchargé de toutes les étapes d’exécution des templates : la bande passante et le processeur ne sont plus utilisés que pour les données qui changent effectivement d’une page à l’autre.
Le système fonctionne à 100% en UTF-8, il permet aussi l’internationalisation de template ce qui permettra dans le futur de faire des applications multilingues.
Une des grandes caractéristiques de l’architecture est l’héritage multiple d’application. Celle-ci se fait à l’aide d’une configuration spéciale, qui définira l’héritage, de la fonction __autoload () qui permet de retrouver et charger automatiquement une classe lors de son appel, et aussi grâce au travail de renommage en interne effectué par un pré-processeur. On réutilise donc les propriétés de classes existantes, ce qui allège considérablement le code et nous permet d’éviter les copier/coller inutiles.
Le système tient compte des principales failles de sécurité rencontrées sur le Web (CSRF, XSS, injection SQL, fixation de session,..), pour remédier à cela, Patchwork implémente un mécanisme de protection spécifique pour chacune de ces attaques.
Un des points forts de l’architecture se trouve aussi au niveau de la performance avec l’utilisation de différents systèmes de mise en cache, comme d’utilisation d’un cache HTTP, PHP, de template, des données.
Pour finir, Patchwork implémente de nombreux outils permettant de faciliter le développement d'applications (ex.: Debug, pForm, Session, Ajax, pTask, pMail, etc..).
Exploration
Schéma
Structure de fichiers
Patchwork dispose d'un mécanisme d'héritage d'applications, qui permet d'établir un lien entre une application et d'autres applications préexistantes. Cette relation d'héritage permet à une application de disposer des fonctionnalités de ses applications parentes. Par exemple, l'ancêtre de toutes les applications est l'architecture Patchwork elle-même. Grâce à ce mécanisme d'héritage, le dossier d'une application pourra ne contenir que les fichiers spécifiques à l'application elle-même ; toutes les fonctionnalités plus générales étant héritées automatiquement des applications parentes.
De façon moins abstraite, lorsqu'un fichier n'est pas trouvé dans le répertoire de l'application courante, il est recherché dans le répertoire de l'application parente, et ainsi de suite. En première approche, ce mécanisme est similaire à l'include_path natif de PHP. Les différences sont significatives et font l'objet d'un chapitre spécifique : Héritage multiple d'applications.
Généralement, le dossier d'une application construite sur Patchwork est organisé comme ceci :
- config.patchwork.php
C'est le seul fichier dont la présence est obligatoire. Il sert à définir la configuration de l'application (mot de passe de base de données, etc.), ainsi que les applications dont dérive cette application (au moyen de la directive spéciale #patchwork).
Voir Configuration pour en savoir plus.
- public/__/
Ce dossier contient des ressources qui sont directement accessibles depuis une URL particulière. Ces ressources sont de deux types : d'une part les ressources statiques (feuilles de style CSS, images, etc.), et d'autre par des fichiers de template (extension .ptl), qui permettent, au moyen d'un langage de programmation simplifié, de définir tous les aspects de mise en page de l'application.
Note : le sous répertoire «__/» permettra plus tard d'internationaliser l'application, si besoin. Voir I18N pour plus d'infos.
- class/
Patchwork encourage l'utilisation d'objets PHP5. Ce dossier contient les fichiers qui définissent les classes utilisées par l'application. Si le nom de ces fichiers respecte une convention de nommage particulière (le fichier class/foo/bar.php contient la définition de la foo_bar), alors le chargement de ces classes se fera automatiquement, sans require ou include.
Correspondances entre URLs et système de fichiers
Il s'agit ici d’aiguiller les requêtes du client vers le bon code à exécuter, ou vers la bonne ressource à distribuer.
Imaginons que l'URL de base de notre application est http://www.example.com/mon_application.php/. Patchwork va permettre d'accéder à trois types de ressources :
- les ressources statiques
Ce sont tous les fichiers dont le contenu ne change jamais. C'est le cas en général des images, feuilles de style CSS, etc. Tous les fichiers stockés dans le dossier public/__/ sont directement disponibles pour les clients. Par exemple une image enregistrée dans le fichier public/__/example.gif pourra être consultée à l'adresse http://www.example.com/mon_application.php/example.gif
- les fichiers de template
Chaque fichier qui se trouve dans le dossier public/__/ et dont l'extension est .ptl est traité de façon particulière par Patchwork : si ce fichier contient des instructions écrites dans le langage de template de Patchwork, ces instructions seront exécutées et le résultat sera envoyé au navigateur. Un fichier public/__/example.ptl sera distribué à l'adresse http://www.example.com/mon_application.php/example (sans extension donc !). Cette fonctionnalité ne permet pas encore de réaliser des sites dont le contenu est dynamique, mais elle permet déjà de faire des sites organisés, qui évitent au maximum les redondances (en-têtes, menus, etc.)
- les agents de données
Pour ajouter du contenu dynamique, Patchwork associe à chaque template une classe PHP qui est chargée de générer ce contenu. Par exemple si le fichier public/__/example.ptl doit afficher un contenu dynamique, ce contenu sera demandé à un objet de classe agent_example, c'est à dire à un objet dont la classe est définie dans le fichier class/agent/example.php. Cet objet sera ainsi automatiquement instancié et la methode compose() sera appelée pour remplir les variables du template.
De façon plus précise, si un fichier class/agent/example.php existe, une requête à l'URL http://www.example.com/mon_application.php/example (toujours sans extension !) déclenchera l'instanciation d'un objet de classe agent_example. La méthode getTemplate() de cet objet donnera le nom du template chargé de mettre en page les données retournée par la méthode compose() de l'objet. (Par défaut, getTemplate() retournera ici public/__/example.ptl.)
Cas particulier : L'URL http://www.example.com/mon_application.php/ instanciera un objet agent_index définit dans le fichier class/agent/index.php. Si ce fichier n'existe pas, le template public/__/index.ptl sera appelé.
En pratique
Installation
Pour des raison de sécurité, et si vous en avez la possibilité, il est conseillé de placer Patchwork hors du dossier Web.
Récupérer Patchwork sur sa machine
Patchwork est disponible via un dépôt Git, vous devez donc installer Git sur la machine qui accueillera Patchwork.
Convention
$ en début de ligne signifie que la ligne doit être saisie dans "terminal shell"
Petit rappel de commande utiles
mkdir [nom du nouveau répertoire]: création d'un répertoire
cd [nom_du_répertoire] : changer de répertoire
ls : liste des fichiers et répertoires où je suis
linux
En fonction de votre distribution, saisissez la ligne de commande ci-dessous
$ yum install git-core
$ apt-get install git-core
Windows
Pour installer git sur windows, nous vous conseillons d'installer MsysGit
Cela vous permettra de lancer Git dans un terminal et de saisir les commandes telles qu'elles sont si après.
Mac 10.4
Si MacPorts n'est pas installé, veuillez tout d'abord l'installer
Lorsque cela est fait, ouvrez un terminal et saisissez la commande ci-dessous :
$ sudo port install git-core
Installation de Patchwork seul
Créer un répertoire patchwork qui contiendra le noyau de patchwork et vos applications
$ cd _la_où_vous_souhaitez_installer_Patchwork_
$ mkdir patchwork
$ cd patchwork
$ git clone git://github.com/nicolas-grekas/Patchwork kernel
Installation des pièces de Patchwork (geodb, fckeditor, pStudio, ...)
$ cd patchwork/kernel
$ git submodule update --init
Installation dans le dossier Web
Si vous ne pouvez pas faire autrement, ou si vous êtes pressé, vous pouvez installer Patchwork dans votre dossier Web. Pour cela, il suffit de déposer les fichiers de Patchwork dans un répertoire (patchwork dans l'exemple qui suit) à la racine de ce dossier Web .
Pour vérifier que tout fonctionne, consultez l’URL http://votresiteweb.com/patchwork/example/hello.php. Si votre navigateur affiche le message "Hello World !", c'est que l'installation s'est effectuée avec succès.
Installation hors du dossier Web
C'est la méthode préférée pour installer Patchwork en production car elle permet d'être certain que les parties internes de vos applications ne seront pas accessibles aux utilisateurs finaux. Cette installation n'est cependant possible que si votre hébergeur vous permet de mettre des fichiers hors du dossier Web.
Déposez tout d'abord les fichiers de Patchwork dans un dossier vierge de votre choix, hors du dossier Web.
Ensuite, pour créer un point d’entrée vers une application de démonstration et la rendre accessible depuis le navigateur :
- copiez le fichier example/hello.php (dans le dossier de Patchwork) et déposez-le à la racine du dossier Web,
- éditez ce fichier et suivez les instructions qui s'y trouvent en commentaire (il n'y en a que deux).
Pour vérifier que tout fonctionne, consultez l’URL http://votresiteweb.com/hello.php. Si votre navigateur affiche le message "Hello World !", c'est que l'installation s'est effectuée avec succès.
Template
Nous pouvons grâce aux templates faire de la logique de présentation. Vous pourrez ainsi définir votre charte graphique, créer par exemple vos menus sans devoir les dupliquer pour toutes vos pages.
Nous allons dans cette partie apprendre à créer et utiliser les fichiers de template, le chapitre suivant consistera à associer ces fichiers à des données.
Suivant la structure de fichiers d’une application construite sur Patchwork, que nous avons vu précédemment, nous allons pour commencer créer le fichier config.patchwork.php ce fichier ne contiendra aucune directive pour le moment.
- Ensuite, nous allons recréer l’arborescence des dossiers qui contiendra les fichiers de templates, soit l’arborescence suivante : /public/__/index.ptl.
- Editez le fichier /public/__/index.ptl, vous pouvez y insérer le code suivant :
<!-- AGENT 'header' --> {* appel à l’agent header, insère les balises d’entête *} Entrez ici votre texte {* Message qui apparaîtra à l’écran *} <!-- AGENT 'footer' --> {* appel à l’agent footer, insère les balises de pied de page *}
- Créez maintenant un point d'entrée à votre application dans le dossier web de la même manière que nous l'avions fait pour l'installation d'une application hors du dossier web. Lors de l'exécution de la page vous devriez voir apparaître dans votre navigateur le message que vous avez inséré dans le fichier de template.
Agent
Nous pouvons maintenant associer des données (des données qui plus tard pourront être dynamique), à vos fichier de template.
Pour commencer nous allons procéder de la même manière que pour les templates, nous recréons l'arborescence, soit la structure de dossier suivante :
- class/agent/index.php
Dans le fichier class/agent/index.php nous définirons des variables associées à des données, elles seront calculer à l'aide de la méthode compose().
Dans notre exemple nous prendrons des variables simples, par la suite vous verrez une utilisation avancée des variables en faisant par exemple des compteurs, en créant des expressions ou encore en appliquant des modificateurs.
Exemple:
- Fichier index.php
<?php class extends agent { function compose($o) //calcule les données et les insère dans le template associé { $o->name = 'à tous'; //définit une variable qui sera disponible via {$name} dans le template return $o; } }
- Fichier index.ptl
<!-- AGENT 'header' --> Bienvenue {$name} !!! {* lors de l'exécution {$name} est substitué par sa valeur "à tous" dans notre exemple *} <!-- AGENT 'footer' -->
Dans notre navigateur le message "Bienvenue à tous" s'affiche.
Iterateur objet
la variable $iteratorPosition nous retourne un élément tant qu'il est disponible sinon retourne false.
Templates
Parler des chemins absolus, de {~} et {/}
Introduction
Template est un mot anglais signifiant modèle ou gabarit.
(voir compléments dans la page Templates)
Dans son architecture, Patchwork implémente un moteur de template pour séparer logique applicative et la présentation, cela signifie que les templates peuvent contenir des traitements, du moment qu'il soit relatif à de la présentation. Le designer de templates, quand a lui, édite les templates et utilise une combinaison de balises HTML et de balises de templates pour formater la présentation, il a entre les mains tous les outils pour implémenter sa logique, il devient indépendant au développeur PHP.
Caractéristiques
Les fichiers de template, définissant la structure de la page, ils sont composés de variables. Ces variables sont définit lors de l'exécution du code PHP associé au template.
Rappelons que les fichiers de templates sont analysés et compilés en PHP ou JavaScript suivant le contexte de la demande.
Lors de la conception de ce pseudo-langage, il a fallu trouver une frontière pour séparer logique applicative et la présentation.
Nous en sommes venus au fait que tous les fichiers de template doivent pouvoir être compilés en JavaScript pour exécution sur le navigateur. Il est donc impossible de lui faire faire autre chose que ce que permet le navigateur, c'est à dire de la logique de présentation.
Pour cette raison, nous avons implémenté à ce langage un maximum de possibilités tout en restant de toute façon encadré par ce que les navigateurs permettent de faire en JavaScript.
De même, tous les fichiers de template doivent pouvoir être compilés en PHP pour exécution côté serveur. Il aurait été aberrant de lui faire faire autre chose que ce que PHP permet.
Les variables
Il existe plusieurs groupes de variables, en fonction de leur origine et de leur portée :
- {$varname} -> donnée relative de l'agent associé, dépendante du niveau de profondeur des boucles.
- {a$var1} -> variables fournies en argument lors de l'appel à l'agent, arguments données au template.
- {g$var2} -> variables globales dans l'espace de nommage des templates.
- {d$var3} -> référence absolue aux données de l'agent associé, égal à $ quand t-on se trouve hors des boucles.
Le $ peut être répété plusieurs fois pour remonter dans l'arborescence parente.
Par exemple, dans une boucle de niveau de profondeur minimale 1, {d$var} et {$$var} référencent la même valeur.
Il existe cinq variables globales prédéfinies :
- {g$__DEBUG__} : contient le niveau de debug.
- {g$__HOST__} (raccourci : {/}) : racine du site (ex. http://exemple.com/).
- {g$__LANG__} : langue actuelle (ex. fr).
- {g$__HOME__} (raccourci : {~}) : racine de l'application (ex. http://exemple.com/myapp/fr/).
- {g$__AGENT__} : chemin de l'agent associé au template, avec slash final (ex. monagent/special/).
- {g$__URI__} : url affichée dans la barre d'adresse du navigateur.
Utilisation avancée des variables
- Compteurs : {a+2$var} affiche a$var puis l'incrémente de 2.
- Expressions : {2*(a$var1+$var2)} est correctement interprété.
- Modificateurs de variable : {'Bonjour'|upper} affichera BONJOUR (cf. section suivante pour la liste des modificateurs disponibles).
- Traductions : les caractères entre double quote sont traduits avec le mécanisme de table de traduction intégré. Par exemple,{"Hello"} peut devenir Bonjour.
Blocs
<!-- SET %variable% -->...<!-- END:SET --> {* met le contenu du bloc dans la variable %variable% au lieu de l'afficher. *} <!-- LOOP %variable% -->...<!-- END:LOOP --> {* répète le bloc en utilisant les données dans %variable%, la variable spéciale $iteratorPosition contient l'indice de l'itération. *} <!-- AGENT %agent% param=%argument% --> {* appelle l'agent %agent% en lui passant le paramètre param. *} <!-- IF %expression% --><!-- ELSEIF %expression% --><!-- ELSE --><!-- END:IF --> {* affichages conditionnels. *}
Blocs spéciaux
<!-- CLIENTSIDE -->...<!-- END:CLIENTSIDE --> {* affichage conditionnel résolu à la compilation du template, présent uniquement si l'exécution du template se fait coté client. *} <!-- SERVERSIDE -->...<!-- END:SERVERSIDE --> {* idem, mais condition exécution coté serveur. *} <!-- EXOAGENT ... --> {* expérimental, comme AGENT, mais cross application. *} <!-- INLINE %template%[:%niveau%] --> {* insère le template %template% de niveau %niveau% (par défaut à -1) de même nom. *}
Les modificateurs de variable
Les modificateurs de variable aussi appelés pipe.
Ils peuvent être appliqués aux variables, fonctions utilisateurs ou chaînes de caractères. Pour appliquer un modificateur de variable, tapez une valeur suivie de |(pipe) et du nom du modificateur. Un modificateur de variable est susceptible d'accepter des paramètres additionnels, qui en affectent le comportement.
Il est tout à fait possible d'enchaîner les modificateurs de variable.
Exemple d'application d'un modificateur à une variable
- {$variable|urlize|lower}
Le modificateur urlize permet de détecter l'adresse email et la transforme en lien.
Le modificateur lower permet de mettre la chaîne de caractère en minuscule.
Supposons que la donnée associée à $variable soit une adresse email tel que "TOTO@patchwork.com", après exécution nous auront ceci qui va s'afficher : "toto@patchwork.com"
Voici la liste des modificateurs actuellement disponibles :
- allowhtml : autorise le HTML, réciproque de htmlspecialchars().
- bytes : formate un nombre d'octets pour lecture humaine (Go, Mo, Ko).
- capitalize : met en majuscule le premier caractère de chaque mot.
- cycle : affiche une alternance de valeurs, pour changer alternativement la couleur d'une ligne dans un tableau par exemple.
- date : idem date() de PHP.
- default : affiche une valeur par defaut si une variable est vide.
- echo : variante simpliste de printf.
- void : empêche une variable de s'afficher.
- home : transforme une url relative à l'application en url absolue, laisse les url absolues inchangées.
- htmlArgs : affiche une version linéarisée d'une classe de variables de template pour utilisation comme liste d'attributs HTML.
- indent : répète un même caractère au début de chaque ligne.
- js : échappe une variable pour qu'elle soit une chaîne de caractère JavaScript valide.
- length : retourne la longueur d'une chaîne de caractères.
- linkto : crée un lien autour d'un texte à condition que la cible du lien soit différente de la page en cours d'affichage.
- lower : met une chaîne en minuscule.
- mailto : crée un lien de type mailto, en protégeant l'email des sniffers.
- nl2br : transforme les lignes en <br />.
- now : affiche le timestamp du moment.
- printf : idem printf() de PHP.
- random : génère un nombre aléatoire.
- repeat : répète une chaîne de caractères.
- replace : remplace au moyen d'une expression régulière.
- rgb : encode une couleur RGB en HTML.
- spacify : insère un caractère entre chaque caractère d'une chaîne.
- stripAccents : enlève les accents d'une chaîne de caractères 10 sur 12.
- substr : idem substr() de PHP.
- test : opérateur conditionnel ternaire.
- trim : enlève les caractères blancs en début et fin de chaîne.
- truncate : tronque proprement une chaîne de caractères.
- upper : met une chaîne en majuscule.
- urlencode : encode une chaîne pour utilisation dans une url.
- urlize : détecte les emails, adresses Internet,... et les transforme en lien.
- wordwrap : découpe une chaîne en plusieurs lignes de longueur déterminée.
- ASCIIMathML : traduit une chaîne d'une notation mathématique en présentation MathML.
Formulaires
La classe pForm implémente des mécanismes pour simplifier l'écriture de formulaires HTML. (Même principe que PEAR/HTML/QuickForm). Elle permet automatiquement de bénéficier de la persistance des formulaires, de la validation coté serveur ET coté client, des workarounds pour bug de navigateurs, et des best practices en ce qui concerne l'accessibilité des formulaires. C'est une classe qui permet de bénéficier sans y penser d'un savoir faire important dans le domaine des formulaires HTML.
I18N
L'internationalisation d'un logiciel consiste à le préparer à la localisation, à l'adaptation à des langues et des cultures différentes. Contrairement à la localisation, qui nécessite surtout des compétences linguistiques, l'internationalisation est un travail essentiellement technique, mené par des programmeurs.
UTF-8 / Unicode
Le but est de pouvoir afficher sur une même page des caractères de tous les alphabets possibles (latin, arabe, chinois, ...)
Vos scripts et vos templates doivent être encodés en UTF-8, et les entrées et sorties qu'ils génèrent sont aussi en UTF-8. Le système fonctionne à 100% en UTF-8. La classe u (u pour utf8) permet de manipuler les chaînes de caractères encodées en UTF-8.
Pour en savoir plus :
- http://www.phpwact.org/php/i18n/charsets
- http://www.phpwact.org/php/i18n/utf-8
- http://www.webtuesday.ch/_media/meetings/utf-8_survival.pdf
- http://www.joelonsoftware.com/articles/Unicode.html
- http://phputf8.sourceforge.net/
Internationaliser un template
Il y a deux manières d'internationaliser un template : soit en déclarant dans le template lui-même les séquences à faire varier en fonction de la langue, soit en créant un template différent par langue.
1re méthode
Elle consiste à écrire les chaînes à traduire sous forme de variable de template entre double quote: {"Comme ceci par exemple"}. La traduction est effectuée au moment de la compilation du template, et ne pénalise donc pas les performances.
2e méthode
La seconde exploite un mécanisme de recherche du fichier source. Par exemple en français, le template exemple.ptl sera d'abord cherché dans public/fr/ puis s'il n'y est pas, dans public/__/.
Pour en savoir plus :
Fonction T()
Objectif : Retourner la traduction si elle existe.
Grâce à la fonction T(), il est possible également de traduire des textes directement dans le code PHP. Le mécanisme de traduction s'occupe alors de mettre en cache les morceaux utilisés pour une performance optimale.
Heritage multiple d'application
__autoload()
foobar::__init() foobar::__free()
Introduction
La fonction magique __autoload () est chargée de charger la définition des classes au moment où l'application en a besoin.
Lors de l’exécution d’un programme, PHP appellera automatiquement la fonction dès qu’il rencontrera une classe inconnue. La fonction __autoload () tentera alors de trouver la classe nécessaire.
Elle est une alternative aux require et include qui surchargent le script. Même si l'arborescence d'un projet est modifiée, qu'un répertoire est renommé ou encore qu'un fichier est supprimé, lors de son appel, la fonction __autoload() va parcourir tout le projet pour trouver la classe nécessaire, si elle existe elle est chargée sinon une erreur sera alors générée.
Cette fonction présente cependant un inconvénient. Lorsque l'on a un nombre important de classes avec une arborescence importante, la fonction sera alors souvent invoquée et suivant la liste de fichiers class à parcourir, le délai de traitement peut être rallongé et de ce fait ralentir l'exécution de l'application.
Notre objectif sera de réduire tant que possible le délai de traitement de la fonction __autoload(), c'est à dire de réduire les temps de boucle et minimiser l'appel à la fonction.
Une solution serait de pouvoir mémoriser l'arborescence, cela reviendrait à la mettre en cache. Lors du premier appel à la fonction __autoload() on met en cache les fichiers contenant nos classes en tenant compte de l'héritage des classes entre elles. Ensuite on n'a plus qu'à charger le cache ce qui réduit le nombre de fichiers à parcourir et ce qui réduit considérablement la durée d'exécution.
Mécanisme de recherche de classe
Dans notre architecture, le mécanisme de la fonction __autoload () se déroule de la manière suivante :
- 1re étape.
Résolution du nom : le mécanisme de résolution de nom se fait de la manière suivante
Ex: function __autoload('toto') => Par défaut, la classe toto sera recherchée dans le fichier class/toto.php.
- 2e étape.
Recherche dans le graphe d'héritage d'applications : recherche du fichier dans les dossiers des applications parentes, suivant l'algorithme C3MRO pour résoudre les ambigüités liées à l'héritage multiple.
Préprocesseur
En informatique, un préprocesseur est un programme qui procède à des transformations sur un code source, avant l'étape de compilation ou d'interprétation proprement dite.
Analyse de la syntaxe
#> [code sur une ligne] : activé en mode DEBUG.
/*<
Bloc activé en mode DEBUG.
>*/
#>>>
Bloc jamais activé.
#<<<
Utilisation
- Supprime des caractères blancs et les commentaires.
- Renomme les classes à l'aide d'un suffixe.
- Renomme self:: en <class>::.
- Normalise les balises <?php et ?>.
- Renommage de fonctions (header() devient Patchwork::header(), ...) (à développer)
- Compatibilité PHP4 (constructeur PHP5 automatique, suppression de warning et notice...)
- Optimisations inline : marqueurs pour court-circuiter __autoload, resolution des resolvePath()s statiques, ...
Superposition de classe
La superposition de classe est un mécanisme qui permet de redéfinir ou d'étendre une classe d'une ou plusieurs applications mères sans en changer le nom.
- Par exemple :
On désire créer dans notre application une classe appelé SESSION, on souhaite que cette classe dérive de la classe parente de notre architecture. Le problème est que la classe parente porte aussi le même nom, dans notre exemple: class SESSION extends SESSION ne fonctionne pas en php et on comprend très bien pourquoi.
Les seules possibilités dont nous disposerions avec PHP seraient soit de reprendre le contenu de la classe mère en faisant un copier/coller dans un fichier de la classe fille, ce qui serait en contradiction le principe DRY (Don't Repeat Yourself), soit de donner un nom different à la classe dérivée, ce qui imposerait un nouveau nom à gérer (!).
Patchwork introduit la syntaxe class foobar extends self, qui permet d'aller chercher la classe en dessous, et grâce à un renommage des classe en interne fait par le pré-processeur, de faire dériver une classe foobar dans l'application fille d'une (ou plusieurs) classe nommée également foobar dans les applications mères.
Configuration
config.patchwork.php
Comme nous l'avons vu précédemment dans la structure de fichier, ce fichier est indispensable, il permet, grâce à un mécanisme similaire à celui de l'include_path de PHP, de faire des applications dérivées et ceci à l'aide de la directive #patchwork suivie du chemin de l'application parente.
Par exemple, soit deux applications A et B :
A :
config.patchwork.php class/agent/index.php public/__/index.ptl
B :
config.patchwork.php
Si le fichier de configuration de B définit une relation d'héritage avec A, alors B est complètement défini, et est identique à A, aux paramètres de configuration près. Par exemple, lorsque le contrôleur cherchera le fichier class/agent/index.php dans B, il ne le trouvera pas, et du fait de l'héritage, ira le chercher dans A.
Toutes les applications sont ainsi dérivées de l'application de base qu'est l'architecture elle-même. Ce mécanisme permet d'implémenter très facilement la notion de thème, ou de faire des applications dérivées sans dupliquer le code source.
auto-chargement de classe
La fonction Patchwork_Superloader::registerPrefix() permet de modifier la convention de nommage de fichier par défaut. Par exemple pour intégrer la convention de nommage de la librairie ezComponents :
registerAutoloadPrefix('ezc', array('adapter_ezc', 'getAutoload'));
function __autoload("ezcBase");
$fichier = adapter_ezc::getAutoload("ezcBase"); # class/ezc/Base/base.php
php.ini
Le fichier php.ini est un fichier de configuration du serveur PHP.
Le fichier patchwork/example/php.ini contient les réglages recommandés.
httpd.conf
Fichier de configuration du serveur Web Apache.
Un fichier patchwork/example/httpd.conf contient des exemples de réglages pour utiliser la réécriture d'URL.
Boîte à outils
Suivi d'erreurs et profiling
L'architecture est composée de nombreux outils dont celui-ci qui se présente sous la forme d'une fenêtre qui apparaitra lors de l'exécution de vos scripts après un clic sur le label DEBUG qui s'affiche en haut à droite de chaque page.
- Coté serveur, y sont redirigés tous les messages d'erreur, le chemin des requêtes effectuées, et le temps et la mémoire que chaque script a consommé.
- Coté client, y sont affichées les variables transmises aux templates, ainsi que le temps d'affichage de la page par le navigateur.
Cette outil est indispensable lors de vos développements, il vous permettra de garder une fenêtre de travail propre, de débugger vos scripts plus simplement et faire ensuite de l'optimisation pour améliorer leurs performances.
Note : le mode DEBUG, est contrôlé dans le fichier config.patchwork.php par les directives :
<?php $CONFIG += array( 'debug.allowed' => true, 'debug.password' => '', // Si vide, DEBUG activé. Sinon mode DEBUG protégé par mot de passe dans un cookie nommé DEBUG. );
SESSION
DB
AJAX
QJsrs
public/__/js/QJsrs.js implémente une librairie pour ce qui est maintenant connu sous le nom de AJAX (http://fr.wikipedia.org/wiki/AJAX) :
- mécanisme de file d'attente pour chaîner les requêtes asynchrones
- requêtes GET ou POST
- cross-domain
- sélection automatique de la meilleur technique disponible : XMLHttpRequest, ActiveX, IFRAME ou SCRIPT
liveAgent
QJsrs sert de base à public/__/js/liveAgent.js, qui permet d'interroger un agent sans recharger la page. De cette façon, il n'y a rien de spécifique à programmer coté serveur pour faire de l'AJAX. Le découpage en agents est naturellement utilisé par liveAgent.
pMail
pTask
upload progress meter
Barre de progression durant le téléchargement.
Sécurité
XSRF
Principe de l'attaque
Le Cross-Site-Request-Forgery, véritable nom, est une attaque assez courante qui a pour but d’inciter un internaute à cliquer ou se rendre sur une page donnée pour qu'une tâche spécifique soit effectuée par son intermédiaire sur un site Internet. Elle exploite la confiance qu’un site web a envers un utilisateur. Elle a donc pour but de faire exécuter des commandes involontaires à des utilisateurs accrédités d’un site.
Solution implémentée dans l'architecture
Pour remédier à cette attaque il suffit d'insérer un champ caché dans chaque formulaire, ce champ de type hidden aura comme nom token et comme valeur un nombre code aléatoire. Ce token sera ensuite inséré dans un cookie. Avant chaque validation de formulaire il y aura un contrôle systèmatique de cette valeur et seulement ensuite la validation pourra s'effectuer. Cette pratique oblige une activation des cookies coté client.
Ce qui ce fait ici et la...
Certaines méthodes sont utilisées sur le net pour éviter ces attaques, cependant ces methodes ne fonctionnent que dans certaines circonstances, même couplées entre elles ces methodes ne sont pas fiable.
- Redemande de mot de passe / CAPTCHA :
C'est une méthode très efficace, mais très contraignante.
- POST au lieu de GET :
Il est tout à fait juste de dire qu'il faut utiliser la méthode POST dans l'envoi de formulaire pour justement ne pas voir les paramètres passer dans l'url qui cette fois apparaîtrait en clair, mais cela ne protégerait en aucun cas d'une telle attaque.
Rappel sur l'utilisation des méthodes POST et GET.
Utiliser la methode POST pour tout envoi de formulaire qui aurait une incidence sur une base de donnée, mais seulement dans ce cas, sinon impossible de bookmarker des pages ; et effets de bords non souhaités en perspective.
- Le réferant ou referer en anglais.
C'est la solution qui revient le plus souvent, vérifier le référant pourrait être une bonne méthode mais cependant il existe des anti-référants qui permettent de faire un lien vers un site sans divulguer le référant et cela ne serait pas une bonne idée que de donner un accés qu'aux personnes possédant ce referer et éliminer ceux qui souhaiteraient rester anonymes.
JavaScript Hijacking
Cette attaque est une variante du XSRF exploitable spécifiquement dans le cas des JavaScript. Si un JavaScript contient des informations personnelles, un site "pirate" peut très facilement récupérer les infos qu'il contient en entraînant un internaute sur son site, puis en faisant appel à ce JavaScript. Ce problème est résolu de façon analogue au XSRF : un token est ajouté dans l'url des JavaScript à protéger, qui est comparé à un cookie. La ressource n'est distribuée que si les deux correspondent.
XSS
Principe
Le principe est d'injecter des données arbitraires dans un site web, par exemple en déposant un message dans un forum, mais aussi par des paramètres d'URL, etc. Si ces données arrivent telles quelles dans la page web transmise au navigateur (par les paramètres d'URL, un message posté, etc.) sans avoir été vérifié, alors il existe une faille : on peut s'en servir pour faire exécuter du code malveillant en langage de script (du JavaScript le plus souvent) par le navigateur web qui consulte cette page.
Patchwork
- On encode systematiquement le HTML de sortie à l'aide de la fonction htmlspecialchars().
- Contrôle systèmatique des entrées avec filtrage des données en sortie.
- Le travail est effectué par l'agent dans notre application, on déclare les arguments dont nous avons besoin et ont les insère dans un tableau.
- Il y a contrôle aussi sur les paramètres, si un paramètre est passé dans l'URL, il est rejeté obligatoirement.
Dans notre exemple nous attendons dans un premier temps un argument égal ou superieur à 1, le contrôle par la suite n'est plus nécessaire.
Note : le bug de type IE mime sniffing est résolu lorsque vous utilisez l'architecture Patchwork.
Session fixation
Principe
La fixation de session est un vol de session, elle pour but d'usurper l'identité d'un utilisateur. Elle fonctionne de manière inversée, au lieu d'essayer de récupérer l'identifiant d'un utilisateur, le pirate va lui même attribuer un numero identifiant à sa victime de façon assez simple si le session.use_only_cookies est désactivé. Il fera suivre un lien à la victime en passant comme paramètre ?PHPESSID=xxx, xxx étant l'ID de session. De ce fait, le hacker connaitra forcement cet identifiant puisque c'est lui même qui le fournit.
Comment s'en prémunir ?
- Utiliser la fonction session_regenerate_id() qui a pour but de créer un nouvel ID à chaque identification d'un utilisateur. Ce n'est pas exactement la fonction qui est utilisée dans patchwork mais elle a le même objectif.
- utiliser un timeout absolu, dans notre application il sera de 12h ce qui nous semble convenable.
- Il est aussi important de lier les 2 premiers numéros de l'adresse IP, public ou privé s'il passe par un proxy, avec l'ID. Cela nous permettra de verifier son identité.
- Dans notre application nous n'utilisons que les sessions connues.
- utiliser l'extension HttpOnly.
PHP supporte les cookies HttpOnly. Ce mode de transmission de cookie permet de restreindre l'accès aux cookies posés par le serveur. En mode HttpOnly, un cookie ne doit être échangé que via HTTP, et il est notamment interdit d'accès via JavaScript.
XST
Cette méthode permet a un pirate de pouvoir récupérer un mot de passe par exemple.
Pour qu'elle fonctionne il faut 2 conditions :
- que le pirate soit sur le même nom de domaine que sa victime, ce qui est le cas si celle-ci a été victime d'une attaque par cross-site-scripting.
- que la méthode TRACE soit activée.
Si ces conditions sont remplies, le pirate, à l'aide d'un petit script, peut facilement récupérer le mot de passe de l'utilisateur.
Exemple de script testé sous IE6 :
Fichier pirate.php
var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP"); xmlHttp.open("TRACE", "http://localhost/test/victime/victime.php", false); xmlHttp.send(); xmlDoc=xmlHttp.responseText; alert(xmlDoc);
La méthode TRACE nous retourne donc le mot de passe en clair de la victime. Pour se prémunir de cette attaque il suffit de désactiver la methode TRACE.
Note : La méthode TRACE est désactivée par défaut pour les versions de IE7 et firefox 2.0.
Voir aussi http://www.webappsec.org/lists/websecurity/archive/2006-05/msg00025.html
Performance
Cache HTTP
Le cache contient les pages distribuées par l'application, c'est à dire les templates et agent associés. Ces fichiers ne sont donc exécutés que si le cache associé a expiré.
Cache PHP
Le cache PHP contient quant à lui les sources PHP modifiées par le préprocesseur.
Cache Template
Le cache de template est un dossier contenant les fichier .ptl compilé en JavaScript et en PHP. Dès lors, si un fichier de cache est disponible lors d'un appel, il sera affiché sans qu'il ne soit nécessaire de regénérer le résultat. Le système de cache de template améliore considérablement le traitement en particulier les templates dont la compilation est très longue.
Cache des données
Lors de l'éxécution de la méthode compose(), les données sont mises en cache, cette fonction n'est donc éxécutée que si le cache associé a expiré. On minimise alors l'appel à la méthode compose() ce qui améliore nettement les performances.
Méthode pull
Les deux modes de fonctionnement d'un moteur de templates sont le mode Push et le mode Pull.
Le mode le plus repandu est le mode push, c'est le mode de fonctionnement utilisé par la plupart des moteurs de templates reconnus.
Ce mode présente un réel inconvénient qui se situe au niveau de l'éxecution des instructions. On peut dire que le mode push est lui deux fois plus lent, on pourrait s'en rendre compte si l'on devait retourner des données important provenant d'une table. Le problème vient du fait, qu'en mode push, les données que l'on souhaite afficher doivent être stockés au préalable dans une variable avant de pouvoir les exploiter, cela présente un inconvénient majeur car on stockera surment à ca moment là des données que l'on n'utilisera sans doute pas par la suite. Le code est lui aussi en partie allégé. il est donc préferable d'utiliser se mode de fonctionnement.
Pour plus d'information à ce sujet vous pouvez consulter le site phpwact
jsqueez
La classe Jsqueez permet de compresser les codes JavaScript avant de les envoyer au navigateur.
Fonctionne avec la plupart des code JavaScript même ceux où les points virgules sont manquants.
Cette classe permet de :
- Supprimer les espaces et les commentaires.
- Renommer toute les variables locals, typiquement par un simple caractère.
- Raccourcir aussi les variables globales, méthodes et propriétés, mais seulement si elles ont un marqueur spécial par certaines convention de nommage. Par defaut, les noms de variables spécials commençant par "_" ou "$".
- Racourcir aussi les variables locales/globales trouvées dans les chaînes de caractères seulement si elle ont un marqueur spécial.
- Respecter des commentaires conditionnels Microsoft.
Note : Les noms raccourcis sont choisis en considérant les closures, la fréquence des variables et la fréquence des caractères seuls.
Négociations HTTP
Le protocole HTTP implémente un processus de compression, toutes les pages de Patchwork sont donc compressées quand le navigateur l'accepte.
Sélection automatique de la langue adaptée au navigateur.
Requêtes partielles HTTP_RANGE
Bibliographie
- Fonction PHP
- Expression régulière en PHP
http://www.expreg.com/index.php
- Moteur de template
http://smarty.php.net/manual/fr/
http://www.phpwact.org/pattern/template_view
- I18N
http://www.phpwact.org/php/i18n
http://fr.wikipedia.org/wiki/I18N
- __autoload ()
http://www.directionphp.biz/2006-04
- Sécurité
http://phpsec.org/projects/guide/fr/
- CSRF
http://devloop.lyua.org/blog/index.php?2006/11/20/352-les-attaques-par-cross-site-request-forgery
http://freedom-to-tinker.com/sites/default/files/csrf.pdf
- httponly
http://msdn.microsoft.com/library/default.asp?url=/workshop/author/dhtml/httponly_cookies.asp
- Mode pull
http://www.phpwact.org/pattern/template_view#pull
- JSqueez
http://www.prettyprinter.de/module.php?name=PrettyPrinter
- Aide à la rédaction