PHUN : un cadriciel PHP minimaliste
Transcription
PHUN : un cadriciel PHP minimaliste
PHUN : un cadriciel PHP minimaliste December 2, 2015 Contents Avant-propos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Historique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 Anatomie de PHUN . . . . . . . . . . . . . . . . . . . . . . . . . 3 Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Contexte PHUN . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 Définition de services simples . . . . . . . . . . . . . . . . . . . . 5 Services paramétrés . . . . . . . . . . . . . . . . . . . . . . . . . 6 Controle des URL’s . . . . . . . . . . . . . . . . . . . . . . . . . . 11 Inter-connexions entre les services . . . . . . . . . . . . . . . . . . 12 Prédicats sur les services . . . . . . . . . . . . . . . . . . . . . . . 13 Une architecture flexible . . . . . . . . . . . . . . . . . . . . . . . 13 Meta-Query (SQL extension) . . . . . . . . . . . . . . . . . . . . . . . 13 Points particuliers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 Erreur 404 personnalisée . . . . . . . . . . . . . . . . . . . . . . . 13 Implémentation concrètes . . . . . . . . . . . . . . . . . . . . . . . . . 13 Implémenter une todo-liste . . . . . . . . . . . . . . . . . . . . . 13 Implémenter un forum complet . . . . . . . . . . . . . . . . . . . 13 Contribuer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1 PHUN est un cadriciel (framework) destiné à concevoir de petites applications web. Il a été conçu pour répondre facilement à l’implémentation de MVP’s. Il est donc très loin des outils boostés aux stéroïdes. Ce document retrace l’histoire de l’outil ainsi que la manière de l’utiliser pour déployer rapidement une application web. Actuellement, PHUN a été prévu pour fonctionner avec PHP et MySQL. Le logiciel est distribué sous licence MIT. Il s’agit donc d’un logiciel libre. Avant-propos La version actuelle de PHUN a été développée sans trop s’intéresser et se soucier de ce qui se faisait sur le marché des frameworks PHP. Il est donc évidemment possible que rien de ce qui n’est présenté dans ce manuel ne soit particulièrement novateur. Cependant, en lisant l’historique de PHUN, vous vous rendrez rapidement compte que la motivation principale de PHUN a été de proposer une approche “facile” de rédiger des applications web facilement déployables. Historique PHUN est initialement une idée de Pierre R., à la base beaucoup de gens estimaient que pour critiquer correctement PHP (qui est un langage pour lequel lui comme moi n’avions pas beaucoup d’estime), il faut le maitriser. C’est par pur provocation que PHUN a été entâmé. L’objectif principal étant de proposer un outil avec une courbe d’apprentissage très souple, pour permettre à un programmeur PHP avec une expérience moyenne de bénéficier d’un cadre de travail permettant de modeliser de petites ou de complexes applications. De ce fait, on tire partit de la facilité de déployer du PHP (sur du M/W/LAMP). La première ébauche de PHUN peut encore être trouvée ici cependant, la version actuelle n’a plus rien à voir. En effet, les propositions initiales de PHUN cadrait trop l’utilisateur dans une structure orientée classes et la courbe d’apprentissage était trop abrupt (sans proposer beaucoup de fonctionnalités) pour garder PHUN comme un projet viable. C’est bien plus tard que j’ai repris le développement d’un framework “simple”, empruntant des concepts à d’autres frameworks (dont Ocsigen). Je l’ai appelé PHUN, avec l’accord de son auteur original, car ce que je cherchais à faire s’inscrivait bien dans la dynamique initiale. Même si la version actuelle de PHUN ne ressemble absolument pas à sa version originale, j’ai eu l’occasion de partager beaucoup d’avis avec Pierre R. pour la réalisation de cette nouvelle version. 2 Introduction Dans cette rubrique, nous survolerons rapidement les concepts sous-jacents à PHUN et nous détaillerons ces concepts plus précisemment dans des sections dédiées. Il y a principalement trois pilliers qui fédèrent le logiciel PHUN : • Une application est pensée au travers des interactions entre les clients et les pages; • pour être simple à appréhender, il ne faut pas exposer une API trop complexe; • Au plus une application crash, au plus elle est facile à déboguer. Concrètement, l’enjeu premier de PHUN est de proposer une API pour écrire des services qui peuvent inter-agir entre eux. Une application web n’en devient alors qu’une simple collection de services potentiellement inter-connectés. Le travail d’un développeur PHP utilisant PHUN n’est que de créer des services. Si une implémentation est ambigüe, PHUN crashera. Le framework n’a pas été conçu dans une dynamique “défensive”, ce qui fait que PHUN ne tentera jamais de résoudre un problème à votre place. L’avantage de cette démarche est qu’elle évite au maximum les effets de bords et que vous contrôlez l’intégralité de votre application. Installation PHUN est très simple à installer, il suffit de posséder un serveur (Apache et PHP 5.6, actuellement le support de PHP 7 n’est pas encore envisagé) et de posséder GIT : git clone https://github.com/xvw/phun cd phun chmod 777 .htaccess La branche master (branche par défaut) contient une version stable et propre de PHUN vous permettant directement de commencer votre application. Anatomie de PHUN PHUN est constitué d’un répertoire “core”, dans lequel se trouve tous les fichiers nécéssaires à son bon fonctionnement. (Pour le moment, il n’est nécéssaire de s’en soucier) A la racine, on trouve le fichier “index.php”, qui est le point d’entrée de toute application et le fichier “configuration.php”, que nous détaillerons dans la rubrique précédente. 3 Configuration La configuration de PHUN est très facile, il suffit de se rendre dans le fichier “configuration.php” et de paramétrer les constantes qui y sont définies. Leur nom semble assez explicite, cependant, pour le moment, seulement deux vont nous intéresser: • DEBUG_MODE : cette constante indique si le projet est en cours de développement ou non. Si cette constante vaut true, les erreurs seront affichées et ça activera le mode interactif. Il vaut mieux toujours activé cette primitive en local; • REWRITE_HTACCESS : la contrôle des URL’s est possible via un petit hack du .htaccess. Si cette constante est activée, a chaque visite de l’index, le .htaccess sera réécrit. Je conseille de le laisser activé en développement, et la première fois que l’on “test” son site déployé, pour ensuite le remettre à false. Les constantes suivantes seront décrites dans les sections leurs correspondant. pour le moment, il n’est pas nécéssaire de s’y attarder. Contexte PHUN Le fichier “index.php” est le point d’entrée d’une application. Voyons sa forme: <?php /* Include Phun's dependancies*/ require 'core/phun.php'; // Ici vous pouvez écrire votre code ! /* Start Phun's routing */ Phun::start(); ?> La ligne require 'core/phun.php'; se charge d’inclure toutes les dépendances à PHUN. Et la ligne Phun::start(); se charge de lancer le routing. Nous en parlerons plus précisement dans la section suivante. Le code d’une application PHUN se trouvera toujours entre ces deux lignes. Il est évident que vous pouvez inclure ce que vous voulez entre ces deux lignes. Mais dans une optique didactique, pour le moment, nous écrirons chaque fois le code entre ces deux lignes. 4 Services Certains auront essayé de tester ce que donne PHUN sans qu’aucun code n’ait été ajouté. Ils auront remarqué que le site affiche une belle erreur. C’est parce que pour fonctionner, PHUN requiert d’avoir au moins un service “pour booter” l’application. Dans cette rubrique nous allons voir le principe des services et comprendre comment les utiliser. Dans une application web, chaque page accessible via un URL est caractérisé par un service. Un service est une structure de données très simple qui doit être lié à une vue. Quand une URL trouve un service candidat, ce dernier est chargé et sa vue est affichée. Définition de services simples Dans un premier temps, nous allons nous contenter d’afficher de créer des services sans paramètres. La construction d’un service se fait via la fonction Service::register($path = []). Le path est une liste des membres de l’URL. Par exemple, pour référencer la page monsite/hello/world, le path sera ['hello', 'world']. En donnant une liste vide, (la valeur par défaut de la variable $path), l’URL référencé sera la racine du site. Créons de ce pas deux services : <?php /* Include Phun's dependancies*/ require 'core/phun.php'; /* Here, write (or include) your application*/ // Une page d'accueil $root = Service::register(); // on référence la racine // Une page pour dire bonjour au monde $hello = Service::register(["Hello", "World"]); // On référence site/Hello/World /* Start Phun's routing */ Phun::start(); ?> Si maintenant vous testez votre application, vous pourrez voir… avec dépit, que l’application affiche toujours une erreur. Une erreur qui indique qu’aucune vue n’est liée au service. En effet, un service doit toujours être lié à une vue. Pour cela, on se sert de la méthode (sur un service) bindWith qui prend en argument une fonction. Voici un exemple pour nos deux services : 5 <?php /* Include Phun's dependancies*/ require 'core/phun.php'; /* Here, write (or include) your application*/ // Une page d'accueil $root = Service::register(); // on référence la racine // Une page pour dire bonjour au monde $hello = Service::register(["Hello", "World"]); // On référence site/Hello/World // Liaison des vues $root->bindWith(function(){ echo '<h1>Bienvenue sur mon site !</h1>'; }); $hello->bindWith(function(){ echo '<h1>Bonjour le monde !</h1>'; }); /* Start Phun's routing */ Phun::start(); ?> Cette fois-ci, l’application fonctionne bien ! A la racine du site, on peut voir le message (en h1) “Bienvenue sur mon site !”, et à la page monsite/Hello/World, le message “Bonjour le monde !”. La raison du découplage entre la définition du service et la liason de sa vue sera détaillé dans une section suivante, c’est très pratique notamment pour faire des liens entre les services. Comme vous avez pu le voir, dans PHUN, comme dans à peu près n’importe quel framework viable, on contrôle l’accès à une fonctionnalité par son URL. Tester les deux pages (en admettant que votre serveur tourne et que vous avez placé PHUN dans un répertoire “phun” à la racine de votre serveur) : • Page d’accueil • Page Hello World Concrètement, un service est lié à un contrat, ici uniquement défini par le chemin d’accès. Services paramétrés La fonction Service::register($path, $args) peut prendre un second argument qui correspond aux paramètres GET et POST que le service peut 6 admettre. Les paramètres sont données au via une liste de paramètres. On peut utiliser les fonction Parameter::get($name, $type='string') ou Parameter::post($name, $type='string'). Il est évidemment possible de mélanger les deux types de paramètres. Un paramètre est toujours référencé par un nom (sous forme de chaine de caractères) et un type. Par défaut le type est string. La liste des types admissible est : • • • • • 'string' : n’importe quel valeur est admise 'int' : le paramètre doit être un entier 'float' : le paramètre doit être un nombre flottant 'char' : le paramètre doit être un seul caractère 'bool' : le paramètre doit être sois “true” soit “false”. Le typage n’effectue pas de conversion, mais vérifie, pour respecter le contrat d’un service, que ce dernier rentre correctement dans le type qui lui a été attribué. Créons un service qui requiert un paramètre GET nom: <?php /* Include Phun's dependancies*/ require 'core/phun.php'; $root = Service::register(); // on référence la racine $hello = Service::register( ["Hello"], [Parameter::get('nom')] ); // Liaison des vues $root->bindWith(function(){ echo '<h1>Bienvenue sur mon site !</h1>'; }); $hello->bindWith(function(){ echo '<h1>Bonjour ' .$_GET['nom']. '</h1>'; }); /* Start Phun's routing */ Phun::start(); ?> Dorénavant, le service Hello est accessible via cet URL : monsite/Hello?nom=DuTexte. Si vous ne donnez pas la variable nom, le router refusera de booter la page. De même si vous tentez de lui donner une chaine de caractères vides, la page refusera aussi de booter. Il est évidemment possible d’utiliser plusieurs arguments. Par exemple : 7 <?php /* Include Phun's dependancies*/ require 'core/phun.php'; $root = Service::register(); // on référence la racine $hello = Service::register( ["Hello"], [ Parameter::get('nom'), Parameter::get('age', 'int') ] ); // Liaison des vues $root->bindWith(function(){ echo '<h1>Bienvenue sur mon site !</h1>'; }); $hello->bindWith(function(){ echo '<h1>Bonjour ' .$_GET['nom']. '</h1>'; echo 'Oh! tu as '.$_GET['age'].' ans!'; }); /* Start Phun's routing */ Phun::start(); ?> Cette fois ci, la page sera accessible via cet url : ̀http://monsite/Hello?nom=UnNom&age=UnNombre‘. Il est impossible d’accéder au service si l’age n’est pas un nombre ! Voici un exemple utilisant des formulaires (et donc des paramètres POST) : <?php /* Include Phun's dependancies*/ require 'core/phun.php'; $root = Service::register(); // on référence la racine $hello = Service::register( ["Hello"], [ Parameter::post('nom'), Parameter::post('age', 'int') ] ); 8 // Liaison des vues $root->bindWith(function(){ echo '<h1>Bienvenue sur mon site !</h1>'; echo '<form action="Hello" method="post">'; echo '<input type="text" name="nom" placeholder="Ton nom">'; echo '<select name="age">'; for ($i = 1; $i < 100; $i++) { echo '<option value="'.$i.'">'.$i.' ans</option>'; } echo '</select>'; echo '<input type="submit"/>'; echo '</form>'; }); $hello->bindWith(function(){ echo '<h1>Bonjour ' .$_POST['nom']. '</h1>'; echo 'Oh! tu as '.$_POST['age'].' ans!'; }); /* Start Phun's routing */ Phun::start(); ?> Mode strict Par défaut, un service est paramétré de manière stricte. Ce qui veut dire que pour respecter son contrat, il doit respecter à 100% sa structure. Donc si par exemple, vous ajoutez un paramètre qui n’est pas requis par le service, ce dernier ne pourra jamais booter. Essayez par exemple : <?php /* Include Phun's dependancies*/ require 'core/phun.php'; $root = Service::register(); // on référence la racine $hello = Service::register( ["Hello"], [ Parameter::post('nom'), Parameter::post('age', 'int') ] ); // Liaison des vues $root->bindWith(function(){ echo '<h1>Bienvenue sur mon site !</h1>'; echo '<form action="Hello" method="post">'; 9 echo '<input type="text" name="nom" placeholder="Ton nom">'; echo '<select name="age">'; for ($i = 1; $i < 100; $i++) { echo '<option value="'.$i.'">'.$i.' ans</option>'; } echo '</select>'; echo '<input name="test" type="submit"/>'; echo '</form>'; }); $hello->bindWith(function(){ echo '<h1>Bonjour ' .$_POST['nom']. '</h1>'; echo 'Oh! tu as '.$_POST['age'].' ans!'; }); /* Start Phun's routing */ Phun::start(); ?> Comme le boutton submit est possède un nom, une variable POST va être crée pour lui. Comme le contrat ne demande que 2 paramètres, le service ne pourra jamais être booté. Comme ça peut être parfois ennuyeux, il est possible, par service, de désactiver son mode strict, en utilisant la méthode strict(true|false). Par exemple, ce code fonctionnera : <?php /* Include Phun's dependancies*/ require 'core/phun.php'; $root = Service::register(); // on référence la racine $hello = Service::register( ["Hello"], [ Parameter::post('nom'), Parameter::post('age', 'int') ] )->strict(false); // Liaison des vues $root->bindWith(function(){ echo '<h1>Bienvenue sur mon site !</h1>'; echo '<form action="Hello" method="post">'; echo '<input type="text" name="nom" placeholder="Ton nom">'; echo '<select name="age">'; for ($i = 1; $i < 100; $i++) { 10 echo '<option value="'.$i.'">'.$i.' ans</option>'; } echo '</select>'; echo '<input name="test" type="submit"/>'; echo '</form>'; }); $hello->bindWith(function(){ echo '<h1>Bonjour ' .$_POST['nom']. '</h1>'; echo 'Oh! tu as '.$_POST['age'].' ans!'; }); /* Start Phun's routing */ Phun::start(); ?> Car le service hello n’est plus strict. Il existe beaucoup de méthodes à appliquer à la création du service, cependant, elles seront détaillées dans les rubriques suivantes, dès lors que ce sera pertinent. Il est par exemple possible de lier directement une vue à un service mais ce n’est pas une bonne pratique ! Controle des URL’s Les URL’s composés avec des variables GET sont souvent très moches. PHUN intègre une notion de déclaration de variables dans ses PATH, pour éviter de devoir toujours utiliser GET pour contrôler sur URL. Lorsqu’un membre du PATH est déclaré comme ceci : {varname}, l’argument entre les accolades devient une variable. Par exemple : <?php /* Include Phun's dependancies*/ require 'core/phun.php'; $hello = Service::register(["hello", "{name}"]); $hello->bindWith(function($name) { echo "<h1>Hello $name</h1>"; }); /* Start Phun's routing */ Phun::start(); ?> 11 Ce service rend l’URL monsite.com/hello/Un_Nom accessible. Par exemple, monsite.com/hello/Xavier bootera le service $hello et affichera “Hello Xavier”. On peut définir autant de variables que l’on veut. Par exemple : <?php /* Include Phun's dependancies*/ require 'core/phun.php'; $hello = Service::register(["hello", "{name}"]); $hello2 = Service::register(["hello", "{name}", "{age}"]); $hello->bindWith(function($name) { echo "<h1>Hello $name</h1>"; }); $hello2->bindWith(function($name, $age) { echo "<h1>Hi $name</h1>"; echo "Tu es agé de $age ans !"; }); /* Start Phun's routing */ Phun::start(); ?> Avec ce service en plus, il suffit de rajouter /AgeDesire au bout de l’URL du précédent service pour booter vers $hello2. Grâce à cette fonctionnalité, on peut réellement raisonner une application par ses points d’entrées et éviter de devoir créer une multitude de fichiers pour avoir de beaux liens! Attention, si le service est strict, il faut exactement le bon nombre de variables déclarée dans l’url, comme argument dans la fonction vue. Par contre si le service n’est pas strict, vous pouvez passer les arguments que vous voulez à la fonction de vue. Par contre, c’est une très mauvaise pratique ! Contraintes régulières Inter-connexions entre les services Liens Formlet 12 Prédicats sur les services Une architecture flexible Meta-Query (SQL extension) Points particuliers Erreur 404 personnalisée Implémentation concrètes Implémenter une todo-liste Implémenter un forum complet Contribuer 13