Manuel de référence → Hoa_Console

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 php suivit du nom de notre fichier d'amorçage, tout simplement ;
  • depuis un fichier .bat sous 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.group qui définit le motif des groupes (qui sont des dossiers, rappelons-le) ; vaut (:Group) par défaut ;
  • pattern.command.name qui 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.file qui définit le motif des noms des fichiers contenant les commandes ; vaut (:Command) par défaut, l'extension .php est automatiquement ajoutée ;
  • pattern.command.class qui définit le motif des noms des classes représentant les commandes ; vaut (:Command)Command para 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:hello le nom de la commande est main:hello.
  • Option courte désigne toute option commençant par un seul tiret : $ ./Bootstrap hello -h Il existe deux sortes d'options courtes : les longonly et les autres. Par défaut, Hoa_Console n'utilise pas les options courtes en mode longonly. La différence est la suivante ; en mode longonly : $ ./Bootstrap hello -help est équivalent à : $ ./Bootstrap hello --help En 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 -h alors h sera 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 \-value x vaut -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 b Ici, x est écrasée et vaut b. On trouve aussi des formes spéciales comme : $ ./Bootstrap hello -x=a,b,c $ ./Bootstrap hello -x=1:7 Ces 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 input Si 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 -- input Ici, x vaut true, et non pas input. Si toutefois on écrit : $ ./Bootstrap hello -x input et que x attend une valeur, alors on n'aura aucune entrée, et x vaudra input, on y reviendra. Dernier cas non ambiguë : $ ./Bootstrap hello -x value input si x attend une valeur. Et enfin, les entrées peuvent être placées partout dans la ligne de commande : $ ./Bootstrap hello -a b inputA -c d inputB On 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 :

  • n ou name qui désigne qui saluer, et prend un argument obligatoirement ;
  • q ou question qui désigne si on pose une question ou non, et ne prend pas d'argument ;
  • h, ? ou help si 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_ARGUMENT si l'argument ne prend aucune valeur ;
  • REQUIRED_ARGUMENT si l'argument prend obligatoirement une valeur ;
  • OPTIONAL_ARGUMENT si 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 --help ou encore (par exemple) : $ ./Bootstrap hello -?
  • Essayons de modifier le nom (par défaut, on salut le monde entier) : $ ./Bootstrap --name Bobby On 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 :

  • render permet de lancer l'animation ;
  • animationFrameMax donne le nombre d'images dans l'animation ;
  • animationFrame donne le numéro de l'image courante ;
  • animationRender effectue 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 $paramters qui 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 setParameter qui définit un seul paramètre ;
  • la méthode getParameter qui retourne la valeur d'un paramètre ;
  • la méthode parameterExists qui 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_couleurcouleur vaut : BLACK pour noir, RED pour rouge, GREEN pour vert, YELLOW pour jaune, BLUE pour bleu, VIOLET pour violet, CYAN pour cyan, WHITE pour blanc, pour l'avant-plan ;
  • COLOR_BACKGROUND_<