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