Chapitre 10. Hoa_Console
Le paquetage Hoa_Console permet de construire une
interface en ligne de commande, et propose plusieurs outils utiles dans un
tel environnement.
Environnement en ligne de commande
Le premier objectif du paquetage Hoa_Console
est de pouvoir créer une interface en ligne de commande, ou un
environnement en ligne de commande (notions équivalentes).
Architecture de l'environnement
L'architecture de l'environnement est très simple. On en propose une par défaut, mais il faut garder en mémoire qu'elle est paramétrable, donc très modifiable (pour en savoir d'avantage, on lira la section intitulée « Paramétrer l'environnement »).
Pour commencer, toutes les commandes sont stockées dans un dossier. À la racine, on a pour habitude d'y placer les fichiers exécutables, qui serviront à démarrer l'interface en ligne de commande. Ces fichiers exécutables peuvent être placés n'importe où, tout dépend de la configuration.
Pour l'exemple, on va reprendre l'architecture utilisée dans Hoa,
c'est à dire que l'on a un dossier Bin/ qui
contient nos fichiers exécutables. Dans ce dossier, on trouve aussi le
dossier Command/ qui contient l'ensemble de nos
commandes. Ces commandes constituent notre environnement ; ce sont des
scripts que l'on peut lancer via une ligne de commande. Ces scripts
peuvent bien sûr utiliser les paquetages du framework, comme une
application normale.
Groupe de commandes
On a trouvé très pratique, par la suite, de grouper les commandes.
C'est à dire qu'un groupe contient plusieurs commandes qui agissent
dans un même but. On peut prendre pour exemple le groupe
Controller qui contient des commandes d'aide à la
création, suppression, déplacement (ou renommage)
etc. de contrôleurs d'application (dans une logique
MVC, voir Chapitre 12, Hoa_Controller).
L'avantage est que l'on peut voir un groupe comme un module. Ainsi, on peut déplacer ce groupe dans un autre environnement, et — si les conventions de nommages sont les mêmes — on pourra les utiliser.
Les groupes sont donc simplement des dossiers. Le groupe par
défaut est Main, c'est à dire que si aucun groupe
n'est précise dans la ligne de commande, ce sera ce groupe qui sera
appelé. Bien entendu, il va de soit que ce groupe par défaut est
paramétrable, tout comme le nom et la forme de tous les groupes.
Déclaration des commandes
Une commande est un script, elle va effectuer une action précise. Les commandes appartiennent à des groupes de commandes.
Une commande est un fichier PHP, et contient une classe. En effet, toutes les commandes sont des classes.
Chaque classe doit étendre la classe
Hoa_Console_Commande_Abstract qui est le seul
lien avec le framework. C'est à travers cette classe que l'on a accès
à des raccourcis pour les outils. Si la commande n'étend pas cette
classe, une exception Hoa_Console_Exception sera
levée.
Comme en C, c'est la méthode main qui sera
automatiquement appelée. Toutefois, cette méthode est d'arité nulle,
c'est à dire qu'elle ne prend aucun argument.
Par défaut, le nom des fichiers qui contiennent les commandes
est tout simplement le nom de la commande avec la première lettre en
majuscule, et le nom de classe est le nom de la commande suivi par
Command. Ainsi, la commande Hello, qui
affiche « Hello World ! », et qui fait partie du
groupe principal s'écrira de cette façon :
<?php /** * Notre commande s'apelle Hello. * Son emplacement est : Command/Main/Hello.php * Et sa classe s'apelle HelloCommand. */ class HelloCommand extends Hoa_Console_Command_Abstract { /** * Seule méthode appelée. */ public function main ( ) { /** * On affiche « Hello World ! » via la fonction * cout, que l'on verra plus tard. */ cout('Hello World !'); } }
On a pour habitude de spécifier l'auteur de la commande, ainsi
que son nom. Ces informations sont contenues dans les attributs de
classe $author et $programName :
<?php class HelloCommand extends Hoa_Console_Command_Abstract { /** * Nom de l'auteur. */ protected $author = 'Ivan Enderlin'; /** * Nom du programme. */ protected $programName = 'Hello'; /** * Notre méthode principale. */ public function main ( ) { cout('Hello World !'); } }
Ces informations sont optionnelles et ne sont donc pas critiques.
La classe Hoa_Console_Command_Abstract
force l'implémentation de deux méthodes : main,
mais également usage. La méthode
usage est utile pour afficher l'aide de la
commande utilisée. Elle est également d'arité nulle. Donc :
<?php class HelloCommand extends Hoa_Console_Command_Abstract { /** * Nom de l'auteur. */ protected $author = 'Ivan Enderlin'; /** * Nom du programme. */ protected $programName = 'Hello'; /** * Notre méthode principale. */ public function main ( ) { cout('Hello World !'); } /** * L'aide de notre commande. */ public function usage ( ) { cout('Vraiment, c\'est si difficile ?'); } }
Notre commande est prête à l'utilisation.
Commande et état
Par défaut, la méthode main retourne
HC_SUCCESS. Cette constante signifie que tout
s'est bien passé, avec succès.
Si jamais une erreur est survenue, on peut retourner la
constante HC_EXIT. Le support des autres
constantes n'est pas encore totalement supporté, il vaut mieux ne
pas les utiliser, et on ne les décrira pas ici.
Le préfixe HC signifie
Hoa Console. Ces constantes seront définies si et
seulement si elles n'existent pas déjà. Elles appartiennent à la
classe d'entrée Hoa_Console.
Utilisation de l'environnement
Pour utiliser notre environnement en ligne de commande, il nous
faut — comme dans toutes applications — un fichier d'amorçage, que l'on
appelera Bootstrap.php et qui se trouvera dans
notre dossier Bin/. Ce
dernier doit être un exécutable ou un fichier PHP. On va
tout d'abord se concentrer sur le fichier d'amorçage, puis sur la façon
de l'exécuter.
Écrire notre fichier d'amorçage d'environnement
Comme dans tout fichier d'amorçage, on commence par redéfinir
les chemins d'inclusion. Ensuite, on va inclure le fichier principal
du framework, à savoir Framework.php. Une fois ce
fichier inclus, on va pouvoir importer le paquetage
Hoa_Console :
<?php set_include_path( ... ); require_once 'Framework.php'; /** * On importe Hoa_Console. */ import('Console.~');
On va commencer par récupérer l'instance de notre classe
Hoa_Console pour ensuite lancer le dispatche.
C'est à dire que l'on va récupérer la ligne de commande, l'analyser,
et en fonction du résultat, appeler la commande et lui donner des
arguments. On aurait alors :
/** * C'est parti … */ Hoa_Console::getInstance()->dispatch();
Si on utilise les paramètres par défaut, cela suffit pour démarrer notre interface graphique.
Utiliser notre fichier d'amorçage
Notre fichier d'amorçage écrit, il faut maintenant l'exécuter. Plusieurs solutions s'offrent à nous :
-
depuis un fichier PHP, en utilisant la commande
phpsuivit du nom de notre fichier d'amorçage, tout simplement ; -
depuis un fichier
.batsous Windows, qui appellera la commande de PHP suivit du nom de notre fichier ; - et depuis Unix, beaucoup d'autres solutions sont possibles, on en développera une avec un script Shell.
Depuis PHP donc, on aurait quelques choses
comme :
$ php Bootstrap.php
Depuis un fichier .bat, on aurait quelque
chose du genre :
@echo off BREAK=ON set PHP="php.exe" set SCRIPT_DIR=%~dp0 set BOOTSTRAP=%SCRIPT_DIR%Bootstrap.php "%PHP%" %BOOTSTRAP% %*
Et on le lancera de cette façon :
> Bootstrap.bat
Enfin, depuis Unix, on aurait :
#!/usr/bin/env php <?php require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Bootstrap.php';
Et on l'exécuterait de cette façon :
$ chmod +x Bootstrap
$ ./Bootstrap
On laisse naturellement le choix à l'utilisateur de s'organiser comme bon lui semble. Il existe beaucoup de possibilités.
Appelons maintenant notre commande Hello :
$ ./Bootstrap Hello
Si on veut préciser le groupe, il faut utiliser la syntaxe
suivante : nom du groupe:nom de la
commande. Par défaut, le groupe sera Main,
et le nom de la commande sera Welcome. On rappelle que
tout ceci reste paramétrable, tout comme le séparateur de groupe et de
commande.
On précise aussi que la casse n'a pas d'importance. On peut
appeler la commande (ou le groupe) Hello, hello,
ou encore HeLlO, cela reviendra au même. Ainsi :
$ ./Bootstrap main:hello
On va étudier les paramètres de l'environnement puis l'utilisation des options (arguments de la commande).
Paramétrer l'environnement
La classe Hoa_Console contient les paramètres
du paquetage. Ces paramètres sont structurés dans un tableau. La méthode
getInstance prend un tableau en argument, il sert à
modifier les paramètres par défaut. Donc si une valeur n'est pas
explicitement précisée, elle prendra son état par défaut :
/** * On déclare nos paramètres. */ $parameters = array( // Nos paramètres. ); Hoa_Console::getInstance($parameters)->dispatch();
Paramètres du système de base
On trouve deux paramètres du système de base. Ils désignent les valeurs par défaut du groupe et de la commande quand on utilise l'interface en ligne de commande.
Par défaut, si le groupe n'est pas précisé dans une ligne de
commande, on utilisera la valeur de
system.group.default, soit Main. Et pour
la commande, on utilisera la valeur de
system.command.default, soit Welcome.
Si on veut les modifier, on ferait :
/** * On redéfinit notre groupe et commande * utilisés par défaut. */ $parameters = array( 'system.group.default' => 'Maintenance', 'system.command.default' => 'Cache' ); /** * Et on lance le système avec ces nouvelles * configurations. */ Hoa_Console::getInstance($parameters)->dispatch();
Paramètre de routage
On a dit que — par défaut — les commandes se trouvent dans le
dossier Command/. Cette valeur est donnée par le
paramètre route.directory.
Si on veut placer les commandes dans un autre dossier, on aurait :
/** * Nos commandes sont placées dans le dossier UBin/ */ $parameters = array( 'route.directory' => 'UBin/' ); /** * Et on lance le système avec cette nouvelle * configuration. */ Hoa_Console::getInstance($parameters)->dispatch();
Il existe un autre paramètre mais qui est nettement plus
sensible. On déconseille de le modifier, sauf avec beaucoup de
précautions. Il s'agit du paramètre
route.grpcmd.separator qui désigne le caractère (ou
chaîne) qui sépare le groupe de la commande dans une ligne de
commande. Par défaut, on utilise :.
On comprend que si l'on utilise un paramètre comme l'espace, l'analyse de la ligne de commande risque de ne pas très bien fonctionner ; pareil pour un séparateur vide. Aucune vérification n'est faite, attention donc.
Paramètre de motif
Les motifs de paramètre sont bien connus dans Hoa. On lira la section intitulée « Utiliser les motifs d'architecture » comme référence.
On trouve quatre motifs :
pattern.groupqui définit le motif des groupes (qui sont des dossiers, rappelons-le) ; vaut(:Group)par défaut ;pattern.command.namequi définit le motif des noms de commande, on parle du nom symbolique, c'est à dire que c'est le nom utilisé partout ; vaut(:Command)par défaut ;pattern.command.filequi définit le motif des noms des fichiers contenant les commandes ; vaut(:Command)par défaut, l'extension.phpest automatiquement ajoutée ;pattern.command.classqui définit le motif des noms des classes représentant les commandes ; vaut(:Command)Commandpara défaut.
Paramètre de ligne de commande
Il n'existe qu'un seul paramètre de ligne de commande. Ce
paramètre est cli.longonly et est utile à
l'interprétation des options courtes dans une ligne de commande.
Par défaut, il vaut false, c'est à dire que
-abc sera interprété comme -a -b -c.
Si on veut seulement utiliser les options longues, on ferait :
/** * On force l'évaluation de toutes les options * comme étant des options longues. */ $parameters = array( 'cli.longonly' => true ); /** * Et on lance le système avec cette nouvelle * configuration. */ Hoa_Console::getInstance($parameters)->dispatch();
Paramètre des classes de style
Le paramètre interface.style.directory décrit le
dossier qui contient les classes de style utilisées par la méthode
importStyle. Par défaut, il vaut
Style/.
Paramètre de commande utilisateur
Les commandes utilisateurs sont les commandes du système de l'utilisateur à appeler quand on souhaite effectuer une action spéciale.
Par exemple, si on veut lancer le navigateur, on appelera la
commande représentée par le paramètre command.broswer.
Par défaut, il faut open qui est une commande
disponible sur Mac OS X, apparue sous NextStep, et qui permet d'ouvrir à peu
prêt tout et n'importe quoi, dont les URL
dans le navigateur par défaut.
On trouve également le paramètre command.php qui
représente la commande php.
Commande et options
Les sous-paquetages Hoa_Console_Core_Cli et
Hoa_Console_Core_GetOption permettent de manipuler
les options passées aux commandes via la ligne de commande.
Analyse d'une ligne de commande
Le paquetage Hoa_Console_Core_Cli permet
d'analyser une ligne de commande, et de l'éclater en plusieurs parties,
via le sous-paquetage Hoa_Console_Core_Cli_Parser.
On a besoin d'analyser nous même la ligne de commande car selon
l'environnement sur lequel on se trouve, l'analyse n'est pas la même. Le
paquetage Hoa_Console_Core_Cli reconstruit donc la
ligne de commande que PHP reçoit, et la découpe en
plusieurs parties.
On demandera au lecteur (ou lectrice) de se reporter à la documentation API pour plus d'informations. On s'intéresse ici au fonctionnement dans le contexte de l'environnement en ligne de commande. Cet environnement assemble toutes ces informations seul, on ne détaillera donc pas le fonctionnement.
Ligne de commande et vocabulaire
On donne le vocabulaire utilisé dans le sous-paquetage
Hoa_Console_Core_Cli_Parser et utilisé partout
dans ce document ou dans notre environnement en ligne de commande. Ce
vocabulaire est nécessaire à la bonne compréhension des informations
manipulées.
On y détaille aussi la syntaxe comprise et utilisée.
Voici le vocabulaire accompagné d'explications.
- Commande est la première partie d'une ligne
de commande. La commande est le nom du fichier exécuté. Dans le
cas de
Hoa_Console, la commande est le groupe accompagné du nom de la commande. Ainsi :$ ./Bootstrap main:hellole nom de la commande est main:hello. - Option courte désigne toute option commençant
par un seul tiret :
$ ./Bootstrap hello -hIl existe deux sortes d'options courtes : les longonly et les autres. Par défaut,Hoa_Consolen'utilise pas les options courtes en mode longonly. La différence est la suivante ; en mode longonly :$ ./Bootstrap hello -helpest équivalent à :$ ./Bootstrap hello --helpEn revanche, si on n'est pas en mode longonly, alors ce sera équivalent à :$ ./Bootstrap hello -h -e -l -p - Option longue désigne toute option commençant
par deux tirets :
$ ./Bootstrap hello --help - Option booléenne désigne une option sans
valeur. Si elle est définie, elle vaut
true, la valeur par défaut donnée par l'utilisateur sinon :$ ./Bootstrap hello -halorshsera définie àtrue. - Option valuée désigne une option avec une
valeur. Une valeur peut être introduite par un espace ou un signe
égal. Elle peut également être mise entre guillemets (simples ou
doubles). Les caractères critiquent doivent être échappés. Ainsi,
toutes ces commandes sont valides :
$ ./Bootstrap hello -x=value$ ./Bootstrap hello -x=va\ lue$ ./Bootstrap hello -x="va lue"$ ./Bootstrap hello -x="va l\"ue"$ ./Bootstrap hello -x value$ ./Bootstrap hello -x va\ lue$ ./Bootstrap hello -x "value"$ ./Bootstrap hello -x "va lue"$ ./Bootstrap hello -x va\ l"ue$ ./Bootstrap hello -x 'va "l"ue'Et ainsi de suite. On notera des cas particuliers :$ ./Bootstrap hello -x=-value$ ./Bootstrap hello -x "-value"$ ./Bootstrap hello -x \-valueoùxvaut -value. Ceci est plutôt pratique pour donner des valeurs négatives. Enfin, on a des cas spéciaux qui sont largement discutables :$ ./Bootstrap hello -x a -x bIci,xest écrasée et vaut b. On trouve aussi des formes spéciales comme :$ ./Bootstrap hello -x=a,b,c$ ./Bootstrap hello -x=1:7Ces valeurs ne sont pas traitées directement, elles sont rendues telles quelles. On lira la section intitulée « Analyse des options spéciales ». - Entrée désigne toute information qui n'est
ni une commande, ni une option. On peut en utiliser de cette façon :
$ ./Bootstrap hello inputSi on utilise une option avant, et que cette option requiert un argument, on peut utiliser les doubles tirets pour signaler qu'une entrée suit :$ ./Bootstrap hello -x -- inputIci,xvauttrue, et non pas input. Si toutefois on écrit :$ ./Bootstrap hello -x inputet quexattend une valeur, alors on n'aura aucune entrée, etxvaudra input, on y reviendra. Dernier cas non ambiguë :$ ./Bootstrap hello -x value inputsixattend une valeur. Et enfin, les entrées peuvent être placées partout dans la ligne de commande :$ ./Bootstrap hello -a b inputA -c d inputBOn précise qu'une entrée comprend les guillemets, l'échappement etc.
On conseille vivement de lire la documentation
API de la méthode parse de la
classe Hoa_Console_Core_Cli_Parser pour plus
d'informations.
Utilisation des options
On se rapproche maintenant de ce qui est réellement intéressant dans le cadre de notre environnement : l'utilisation des options.
Le paquetage Hoa_Console_Core_GetOption
permet, à partir de l'analyse d'une ligne de commande, de nous fournir
les options successivement.
Description des options
On se place dans notre commande Hello. On va
supposer qu'elle a trois arguments :
nounamequi désigne qui saluer, et prend un argument obligatoirement ;qouquestionqui désigne si on pose une question ou non, et ne prend pas d'argument ;h,?ouhelpsi on veut obtenir l'aide, et ne prend pas d'argument.
On utilise alors l'attribut de classe $options
qui est un tableau de tableau. Chaque sous-tableau représente un
argument et à cette forme :
array( 'option longue', 'type d\'argument', 'option courte' );
L'option longue est une simple chaîne de caractère, et l'option courte un simple caractère. Le type d'argument est représenté par trois constantes :
NO_ARGUMENTsi l'argument ne prend aucune valeur ;REQUIRED_ARGUMENTsi l'argument prend obligatoirement une valeur ;OPTIONAL_ARGUMENTsi l'argument peut prendre une valeur.
Ainsi, on aurait :
class HelloCommand extends Hoa_Console_Command_Abstract { /** * Nos options. */ protected $options = array( array('name', parent::REQUIRED_ARGUMENT, 'n'), array('question', parent::NO_ARGUMENT, 'q'), array('help', parent::NO_ARGUMENT, 'h'), array('help', parent::NO_ARGUMENT, '?'), ); // … }
Lecture des options
Un des liens que fournit la classe
Hoa_Console_Command_Abstract est le lien avec la
classe Hoa_Console_Core_GetOption à travers la
méthode getOption.
La méthode getOption prend deux paramètres :
le premier paramètre est la variable qui va recevoir la valeur de
l'argument si elle existe, et le second paramètre est une chaîne de
caractère qui regroupe toutes les options courtes à lire. Si le second
paramètre est nul, toutes les options seront lues. On notera que
passer une chaîne vide aura pour conséquence qu'on ne lira aucune
option, ceci est différent d'une chaîne nulle.
De plus, cette méthode retourne l'option courte lue, ou un booléen.
Voyons plutôt :
class HelloCommand extends Hoa_Console_Command_Abstract { /** * Nos options. */ protected $options = array( array('name', parent::REQUIRED_ARGUMENT, 'n'), array('question', parent::NO_ARGUMENT, 'q'), array('help', parent::NO_ARGUMENT, 'h'), array('help', parent::NO_ARGUMENT, '?'), ); /** * Notre commande principale. */ public function main ( ) { /** * On définit nos paramètres par défaut. */ $name = 'World'; $mark = '!'; /** * On lit toutes les options, soit n, q, h et ?. * $c représente l'option lue, et $v la valeur de l'option. */ while(false !== $c = parent::getOption($v)) { switch($c) { /** * Si l'option est lue, on assigne la valeur. */ case 'n': $name = $v; break; /** * Si l'option est lue, on modifie la valeur. */ case 'q': if(true === $v) $mark = '?'; break; /** * Si l'option h ou ? est lue. * On utilise un retour, donc si l'option * h ou ? est utilisée avec des autres, * on aura l'aide et rien d'autres. */ case 'h': case '?': return $this->usage(); break; } } cout('Hello ' . $name . ' ' . $mark); } public function usage ( ) { cout('Ceci est l\'aide !'); return HC_SUCCESS; } }
Alors, voici ce qu'on peut faire.
-
Essayons d'obtenir l'aide :
$ ./Bootstrap hello --helpou encore (par exemple) :$ ./Bootstrap hello -? -
Essayons de modifier le nom (par défaut, on salut le monde entier) :
$ ./Bootstrap --name BobbyOn ne le précisera pas à chaque fois, mais on peut utiliser les options courtes à la place des longues, le comportement sera identique. -
Maintenant, on va poser une question au lieu d'écrire une
exclamation :
$ ./Bootstrap -q -
On peut également tout mélanger :
$ ./Bootstrap -n Billy --question - etc.
Analyse des options spéciales
On peut trouver des options spéciales, c'est à dire des options
avec des formes spéciales. L'analyseur
Hoa_Console_Core_Cli_Parser ne les traite pas
directement. Il commence par les restituer telles quelles et propose par
la suite une méthode permettant de les traiter.
Cette méthode se retrouve sous le nom
parseSpecialValue, mais un alias se trouve dans
Hoa_Console_Command_Abstract de façon à être
accessible plus facilement. Cette méthode prend deux paramètres : le
premier paramètre est la valeur à analyser, et le second un tableau de
mot-clés.
Voilà comment cette fonction procède. Tout d'abord, on sépare les valeurs entre virgule, puis on remplace les mot-clés par leur valeur donnés à travers le tableau.
Par exemple avec simplement des virgules, si on donne
a,b,c, la méthode
retournera un tableau contenant les valeurs a,
b et c.
On aurait alors :
class HelloCommand extends Hoa_Console_Command_Abstract { // … public function main ( ) { // … $names = array('World'); while(false !== $c = parent::getOption($v)) { switch($c) { /** * Imaginons que nous ayons plusieurs noms. */ case 'n': $names = parent::parseSpecialValues($v); break; // … } } cout('Hello ' . implode(' and ', $names) . ' ' . $mark); } // … }
On peut utiliser une ou plusieurs valeurs. Avec une valeur, on a
le même résultat, avec plusieurs valeurs toutefois, on salue toutes les
personnes citées :
$ ./Bootstrap -n Tata,Titi,Toto
Affichera : Hello Tata and Titi and Toto !
Utilisation des mots-clés dans les options spéciales
On peut utiliser des mots-clés dans les options spéciales. Pour
ça, on utilise le second paramètre de la méthode
parseSpecialValues qui est un tableau.
Par exemple, si on veut utiliser le mot-clé ME
pour dire Hywan, on aurait :
class HelloCommand extends Hoa_Console_Command_Abstract { // … public function main ( ) { // … $names = array('World'); while(false !== $c = parent::getOption($v)) { switch($c) { /** * Imaginons que nous ayons plusieurs noms. */ case 'n': $names = parent::parseSpecialValues( $v, array('ME' => 'Hywan') ); break; // … } } cout('Hello ' . implode(' and ', $names) . ' ' . $mark); } // … }
Si on veut utiliser notre mot-clé, on ferait :
$ ./Bootstrap hello -n Titi,ME,Toto
cela affichera : Hello Titi and Hywan and Toto !.
L'avantage des mots-clés est qu'on peut s'en servir comme
raccourci. Par exemple, les mots-clés HEAD,
PREV etc. sont très utiles.
Utilisation des intervalles dans les options spéciales
Une dernière chose que fait la méthode
parseSpecialValues est la gestion des
intervalles. Les intervalles sont séparés par le symbole deux-points.
Si on donne 1:7, on récupèrera un tableau rempli de 1
à 7. L'ordre n'a pas d'importance, les entiers seront triés selon
l'ordre naturel, ce qui implique de n'utiliser que des entiers.
Donc si on entre :
$ ./Bootstrap hello -n 1:3
on affichera : Hello 1 and 2 and 3 !
On peut bien sûr mélanger les mots-clés dans les intervalles etc.
Utilisation des entrées
Après les options, on peut utiliser les entrées (ou inputs en anglais).
On a deux façons d'obtenir les entrées.
Obtenir toutes les entrées
Il existe la méthode getInputs qui retourne
un tableau contenant toutes les entrées :
class HelloCommand extends Hoa_Console_Command_Abstract { public function main ( ) { $inputs = parent::getInputs(); cout('Nombre d\'entrées : ' . count($inputs)); foreach($inputs as $i => $input) cout('Entrée numéro ' . $i . ' a pour valeur ' . $input . '.'); } // … }
Si on utilise la commande suivante :
$ ./Bootstrap hello a b
on aura à l'affichage :
Nombre d'entrées : 2 Entrée numéro 0 a pour valeur a Entrée numéro 1 a pour valeur b
Lister les entrées
Il existe une autre méthode qui liste les entrées. Cette méthode
s'appelle listInputs et prend 26 arguments, soit
la longueur de l'alphabet latin. Chaque argument est passé en
référence, voyons plutôt :
class HelloCommand extends Hoa_Console_Command_Abstract { public function main ( ) { parent::listInputs($source, $destination); cout('La source est : ' . $source . '.'); cout('La destination est : ' . $destination . '.'); } // … }
Si la variable n'existe pas, i.e. si l'entrée
n'existe pas, alors la variable aura pour valeur
null.
Gestion des entrées/sorties
Le paquetage Hoa_Console_Core_Io permet de
manipuler les flux d'entrées et de sorties.
Quand on travaille dans une classe qui étend
Hoa_Console_Command_Abstract, il est inutile
d'importer le paquetage, cela est fait automatiquement.
Gestion des flux
On trouve différents types de flux. On peut manipuler les :
-
flux d'entrées : capturent toutes les entrées, via la constante
STDIN; -
flux de sorties : capturent toutes les sorties, via la constante
STDOUT; -
flux d'erreurs : capturent toutes les erreurs, via la constante
STDERR.
Ces constantes sont définies si et seulement si elles n'existent pas. Normalement, elles sont définies dans toutes les dernières versions de PHP (c'est à dire depuis largement la version 4).
Les flux ne sont en fait que des ressources de type fichier. Les flux se manipulent alors comme des fichiers.
On va voir que Hoa_Console_Core_Io fournit
déjà des méthodes et fonctions alias pour manipuler les flux plus
facilement.
Gestion des entrées
Notre classe Hoa_Console_Core_Io nous propose
la méthode cin. Elle prend deux arguments : le
premier argument est une chaîne de caractères qui va se placer avant la
saisie (on parle de préfixe), cette chaîne peut être nulle,
et le second paramètre décrit le type
d'entrée que l'on souhaite avoir.
Par exemple, on aurait :
import('Console.Core.Io'); /** * On demande à l'utilisateur d'entrer une * valeur. */ Hoa_Console_Core_Io::cin('Une valeur s\'il vous plaît : ');
Cet exemple va inviter l'utilisateur à entrer une valeur, avec une petite phrase d'introduction.
Comme il est long d'écrire :
Hoa_Console_Core_Io::cin( … );on propose la fonction
cin, qui est un alias.
Ainsi, un exemple totalement équivalent serait :
cin('Une valeur s\'il vous plaît : ');
Entrée de type question
Par défaut, les entrées sont de type
TYPE_NORMAL. Mais on peut modifier le
comportement des entrées pour interpréter le résultat comme une
question, c'est à dire où la réponse est oui ou
non.
Pour cela, on utilise la constante
TYPE_YES_NO. Toutes ces constantes sont des
constantes de la classe Hoa_Console_Core_Io.
Ainsi, on aurait :
cin('Aimez-vous Hoa ?', Hoa_Console_Core_Io::TYPE_YES_NO);
Toute réponse qui vaut : y, ye ou
yes sont considérées comme répondant positivement (c'est
à dire oui), donc renvoyant
true. Toutes les autres réponses seront
considérées comme fausses, donc elles vaudront
false.
Entrée de type mot de passe
Un autre type d'entrée est le type donné par la constante
TYPE_PASSWORD. Ce type d'entrée permet de cacher
le mot de passe tapé par l'utilisateur.
En réalité, cette fonctionnalité ne marche que sous les terminaux TTY. Ce sera amélioré à l'avenir pour être standard. On utilise un hack qui sera bientôt corrigé. Il n'est que temporaire.
Ainsi, on aurait :
cin('Entrez votre mot de passe : ', Hoa_Console_Core_Io::TYPE_PASSWORD);
Gestion des sorties
La méthode cout (avec son alias qui est la
fonction cout) se propose pour manipuler les
sorties. Elle prend trois arguments : le premier argument est la valeur
à sortir, le second est si on doit ajouter automatiquement un retour à
la ligne ou pas, et le dernier si on doit effectuer une césure sur le
texte.
En utilisant seulement le premier argument :
Hoa_Console_Core_Io::cout('Ceci est une sortie');avec son équivalent alias :
cout('Ceci est toujours une sortie');
Contrôler les sauts de ligne pour les sorties
Par défaut, on ajoute un saut de ligne à la fin, c'est à dire le
caractère \n. Les constantes
NEW_LINE (par défaut) et
NO_NEW_LINE contrôle ce comportement. Ainsi, si on
ne veut pas ajouter de saut de ligne à la fin, on aurait :
cout('Hop, sans saut de ligne !', Hoa_Console_Core_Io::NO_NEW_LINE);
Contrôler la césure des sorties
Par défaut, on effectue une césure sur les sorties. C'est à dire que si la sortie a une taille supérieure à la largeur du terminal, elle sera tronquer pour rentrer dans la largeur.
Les constantes WORDWRAP (par défaut) et
NO_WORDWRAP.
Ainsi :
cout('Et une très très très … très longue sortie.', Hoa_Console_Core_Io::NEW_LINE, Hoa_Console_Core_Io::NO_WORDWRAP);
Interface de l'environnement
Le sous-paquetage Hoa_Console_Interface permet
de créer une interface honorable dans un terminal.
Pour ça, elle fournit des outils pour faire des animations, des sons, et manipuler le texte : style, mise en forme etc.
Interface animation
Cette interface est expérimentale. On est curieux de voir comment l'utilisateur va l'utiliser.
On a un système générique pour créer des animations. Il se base
sur le caractère retour chariot : \r. Donc une animation
comportant un retour à la ligne ne pourrait pas fonctionner, il faudrait
nettoyer l'écran par exemple. On peut donc s'en servir pour écrire des
barres de progressions ou ce genre de chose.
Écrire une animation
Une animation s'écrit à l'aide de quatre méthodes :
renderpermet de lancer l'animation ;animationFrameMaxdonne le nombre d'images dans l'animation ;animationFramedonne le numéro de l'image courante ;animationRendereffectue le rendu de l'image courante.
Pour écrire une animation, il faut étendre la classe
Hoa_Console_Interface_Animation. C'est une classe
abstraite où les méthodes animation* sont
abstraites.
Concrètement, une animation est découpée en image (en frame avec le terme anglais). On a donc un nombre d'images maximum et on va afficher toutes les images une par une. Au final, chaque image sera calculée et affichée à la volée. Bien sûr, on parle d'image, mais il est question de texte, car on est dans un terminal.
Pour détailler les méthodes, la méthode
animationFrameMax a une arité nulle et retourne
un entier ; la méthode animationFrame prend un
entier représentant le numéro de l'image précédente, et retourne un
entier représentant le numéro de l'image courante ; et enfin la
méthode animationRender prend deux arguments : le
numéro de l'image courante à dessiner, et le nombre maximum d'images.
Le tout est utilisé par la méthode render.
On va donner un exemple concret d'une barre de progressions :
/** * On importe le paquetage */ import('Console.Interface.Animation'); class ProgressBar extends Hoa_Console_Interface_Animation { /** * On donne le nombre maximum d'image, soit * le nombre maximum d'étapes dans notre barre. * Ici, notre barre a cinq étapes. */ protected function animationFrameMax ( ) { return 5; } /** * On donne l'étape courante de la barre. * On va faire les étapes une par une. */ protected function animationFrame ( $i ) { return $i + 1; } /** * On dessine notre barre. */ protected function animationRender ( $i, $max ) { /** * On attend 1 seconde entre chaque animation. */ sleep(1); return 'Barre : [' . str_pad(str_repeat('#', $i), $max) . ']'; } }
Et pour lancer l'animation :
$bar = new ProgressBar(); $bar->render();
On concède que c'est très trivial, mais on peut imaginer que
animationFrame puise ses données autre part.
Imaginons un barre qui représente l'avancement de plusieurs
tâches. On a une méthode qui va faire avancer le compteur de tâche, et
la méthode animationFrame n'aura qu'à retourner
la valeur de ce compteur.
On aurait alors :
class Task extends Hoa_Console_Interface_Animation { /** * Notre numéro de tâche courante. */ protected $taskI = 0; /** * Nos tâches associées à l'avancement. */ protected $taskMod = array( 0 => 2, 1 => 5, 2 => 8, 3 => 17, 4 => 20 ); /** * On démarre nos tâches. */ public function __construct ( ) { parent::render(); } /** * On effectue une tâche. */ public function makeTask ( ) { switch($this->taskI) { case 1: sleep(1.8); break; case 2: sleep(1.6); break; case 3: sleep(1.5); break; case 4: sleep(.3); break; } $this->taskI++; } /** * Nos méthodes d'animations bien connues. */ protected function animationFrameMax ( ) { return 4; } protected function animationFrame ( $i ) { /** * On effectue une tâche. */ return $this->makeTask(); } protected function animationRender ( $i, $max ) { return 'Tâche ' . $i . '/' . $max . ' : [' . str_pad(str_repeat('#', $this->taskMod[$i]), 20) . ']'; } }
Et on lance nos tâches :
$task = new Task();
On a simplifié — encore une fois — à l'extrême pour une plus grande clarté du code, mais on voit comment imbriquer les animations dans une classe.
On peut aussi avoir simplement une classe
ProgressBar qui est utilisée dans une classe
Task, il suffit d'user de stratégie.
Paramètre d'animation
Les animations ont besoin parfois d'être paramétrer pour une
plus grande souplesse. C'est pourquoi on a fait un système de gestion
de paramètres, tout simple, dans la classe
Hoa_Console_Interface_Animation.
Pour faire fonctionner le système de paramètre, on a besoin de :
-
l'attribut de classe
$paramtersqui contient un tableau de paramètres ; -
la méthode
setParameters, appelée directement par le constructeur, qui définit les paramètres ; -
la méthode
setParameterqui définit un seul paramètre ; -
la méthode
getParameterqui retourne la valeur d'un paramètre ; -
la méthode
parameterExistsqui vérifie si un paramètre existe.
Pour initialiser des paramètres, on peut redéfinir directement
l'attribut de classe $parameters ou appeler le
constructeur ou setParameters en passant un
tableau de paramètre.
Comme toutes ces méthodes sont présentes dans la classe
Hoa_Console_Interface_Animation, on les retrouve
par héritage dans notre classe ProgressBar de
l'exemple précédent.
L'utilisation est triviale.
Interface son
L'interface des sons est très limité. Elle se borne à effectuer un
simple « bip ». On utilise la méthode bip qui
retourne un caractère. Pour effectuer le son, on doit le sortir, donc il
faut utiliser les entrées/sorties. On s'y prendrait de cette façon :
/** * On importe notre interface de son. */ import('Console.Interface.Sound'); /** * Comme il faut sortir le son, on utilise * les entrées/sorties. */ import('Console.Core.Io'); /** * Bip bip ! */ Hoa_Console_Core_Io::cout( Hoa_Console_Interface_Sound::bip() );
Cette méthode est accessible depuis les commandes :
class HelloCommand extends Hoa_Console_Command_Abstract { public function main ( ) { cout(parent::bip()); } // … }
Interface style
Les terminaux TTY supportent les couleurs
d'avant-plan, d'arrière-plan, une mise en gras, le soulignement
etc. La classe
Hoa_Console_Interface_Style propose de manipuler
les styles.
Liste des styles disponibles
On commence par lister tous les styles disponibles :
COLOR_FOREGROUND_couleur où couleur vaut :BLACKpour noir,REDpour rouge,GREENpour vert,YELLOWpour jaune,BLUEpour bleu,VIOLETpour violet,CYANpour cyan,WHITEpour blanc, pour l'avant-plan ;COLOR_BACKGROUND_<

