I. Prérequis

La version du Zend Framework 2 utilisée pour cet article est la 2.0.0RC02. L'utilisation du Zend Framework 2 nécessite au minimum la version 5.3.3 de PHP.

Zend Framework 2.0 RequirementsZend Framework 2.0 Requirements

II. Utilisation basique de la console

Afin de comprendre le fonctionnement de la console, voici un exemple simple d'affichage de ligne dans le terminal :

Affichage d'une ligne sur le terminal

Sélectionnez
$console = Console\Console::getInstance();
$console->writeLine('Hello world');

La classe Console est un singleton et possède une méthode statique permettant de récupérer l'instance de l'objet de console de sortie. Voici le code de cette méthode :

Zend/Console/Console.php

Sélectionnez
static public function getInstance($forceAdapter = null, $forceCharset = null)
{
    if (static::$instance === null) {
        if ($forceAdapter !== null) {
            if (substr($forceAdapter,0,1) == '\\') {
                $className = $forceAdapter;
            } elseif (stristr($forceAdapter,'\\')) {
                $className = __NAMESPACE__.'\\'.ltrim($forceAdapter,'\\';
            } else {
                $className = __NAMESPACE__.'\\Adapter\\'.$forceAdapter ;
            }
            if (!class_exists($className)) {
                throw new InvalidArgumentException('Cannot find Console adapter class '.$className;
            }
        } else {
            $className = static::detectBestAdapter() ;
        }
        static::$instance = new $className() ;
        if ($forceCharset !== null) {
            if (substr($forceCharset,0,1) == '\\') {
                $className = $forceCharset;
            } elseif (stristr($forceAdapter,'\\')) {
                $className = __NAMESPACE__.'\\'.ltrim($forceCharset,'\\';
            } else {
                $className = __NAMESPACE__.'\\Charset\\'.$forceCharset;
            }
            if (!class_exists($className)) {
                throw new InvalidArgumentException('Cannot find Charset class '.$className;
            }
            static::$instance->setCharset(new $className());
        }
    }
    return static::$instance;
}

Nous remarquons que la méthode « getInstance() » pilote en réalité un objet du namespace Zend\Console\Adapter qui permet de faire la correspondance avec l'environnement du développeur. La méthode prend en premier paramètre le nom de l'adaptateur, nous aurions pu alors écrire le nom de l'adaptateur pour forcer l'initialisation (l'environnement de travail courant est Linux) :

Utilisation de l'environnement courant

Sélectionnez
$console = Console\Console::getInstance(‘posix’) ;
$console->writeLine('Hello world');

Nous aurions pu passer directement par l'adaptateur pour avoir le même résultat :

Utilisation de l'adapteur

Sélectionnez
$console = new Console\Adapter\Posix() ;
$console->writeLine('Hello world';

Il est évidemment conseillé de passer par la fabrique de la classe Console afin de ne conserver qu'une seule instance de l'adaptateur. Si aucun argument n'est passé en paramètre de la fabrique, la méthode statique « detectBestAdapter() » permet de connaître l'adaptateur à utiliser.

Une fois la console initialisée, nous pouvons utiliser son API, voici quelques possibilités :

Écriture d'une ligne en couleur

Sélectionnez
$console = Console\Console::getInstance() ;
$console->writeLine('Hello world', Console\ColorInterface::GREEN);
Nettoyage du terminal

Sélectionnez
$console = Console\Console::getInstance() ;
$console->clear();

Il est également possible de récupérer la saisie de l'utilisateur :

Récupération de saisie

Sélectionnez
$console = Console\Console::getInstance();
$line = $console->readLine() ;
echo "Vous avez tape : " . $line . "\n";

La classe Console permet aussi de connaître le type de console utilisé :

Type de console

Sélectionnez
echo (integer)Console\Console::isConsole();
echo (integer)Console\Console::isWindows();

III. Les classes d'interactions

Le composant Console possède un sous-composant Zend\Console\Prompt qui permet d'interagir avec l'utilisateur en demandant la saisie d'un texte, entier ou encore de choisir parmi une liste de réponses. Voici une mise en œuvre simple :

Saisi d'un texte
Sélectionnez
$prompt = new Console\Prompt\Line('Entrez votre texte :'
);$line = $prompt->show() ;
echo "Vous avez tape : " . $line . "\n";

Si l'on observe le fonctionnement interne de la classe Console\Prompt\Line, nous remarquons que celui-ci se base sur la méthode « readLine() » de la classe Console que l'on a présentée plus haut :

Zend/Console/Prompt/Line.php
Sélectionnez
public function show()
{
    do {
        $this->getConsole()->write($this->promptText;
        $line = $this->getConsole()->readLine($this->maxLength;
    } while (!$this->allowEmpty && !$line;
    return $this->lastResponse = $line;
}

La console affiche la ligne correspondant à la question que l'on a passée en paramètre et retourne le résultat de la méthode « readLine() » en s'assurant que la ligne n'est pas vide.

Il est également possible de récupérer la saisie d'un nombre :

Saisie d'un nombre
Sélectionnez
$prompt = new Console\Prompt\Number('Entrez un nombre :'
);$number = $prompt->show() ;
echo "Vous avez tape : " . $number . "\n";

Notons que si la saisie ne correspond pas à un nombre, un message d'erreur est automatiquement affiché :

Zend/Console/Prompt/Number.php
Sélectionnez
public function show()
{
    do {
        $valid = true;
        $number = parent::show() ;
        if ($number === "" && !$this->allowEmpty) {
            $valid = false ;
        } elseif ($number === ""){
            $number = null ;
        } elseif (!is_numeric($number)){
            $this->getConsole()->writeLine("$number is not a number\n";
            $valid = false;
        } elseif (!$this->allowFloat && (round($number) != $number) ){
            $this->getConsole()->writeLine("Please enter a non-floating number, i.e. ".round($number)."\n";
            $valid = false ;
        } elseif ($this->max !== null && $number > $this->max){
            $this->getConsole()->writeLine("Please enter a number not greater than ".$this->max."\n";
            $valid = false;
        } elseif ($this->min !== null && $number < $this->min){    
            $this->getConsole()->writeLine("Please enter a number not smaller than ".$this->min."\n");
            $valid = false;
        }
    } while (!$valid);
    if ($number !== nul) {
        $number = $this->allowFloat ? (double)$number : (int)$number ;
    }    
     return $this->lastResponse = $number;
}

Comme nous le voyons, la classe s'occupe de gérer les différents types d'erreur comme le type de la saisie mais encore le minimum et maximum que l'on peut passer au constructeur de la classe :

Utilisation du minimum et maximum

Sélectionnez
$prompt = new Console\Prompt\Number('Entrez un nombre :', false, false, 30, 40);
$number = $prompt->show() ;
echo "Vous avez tape : " . $number . "\n";

Dans l'exemple ci-dessus, l'utilisateur doit alors taper un nombre plus petit ou égal à 40 et plus grand ou égal à 30.

Le composant Zend\Console\Prompt permet aussi la saisie d'une réponse parmi une liste de proposition :

Liste de proposition

Sélectionnez
$prompt = new Console\Prompt\Select('Choisissez une reponse : ', array(
    '1' => '1er choix',
    '2' => '2eme choix',
));
$option = $prompt->show();
echo "Vous avez choisi : " . $option . "\n";

Notons qu'il est important de choisir un chiffre ou lettre pour la clé du tableau d'options, car la vérification porte sur les caractères tapés par l'utilisateur et présent en clé du tableau d'options :

Zend/Console/Prompt/Select.php

Sélectionnez
public function show()
{
    $console = $this->getConsole();    
     $console->writeLine($this->promptText;
    foreach ($this->options as $k => $v) {
        $console->writeLine('  '.$k.') '.$v);
    }
    $mask = implode("",array_keys($this->options)) ;
    if ($this->allowEmpty) {
        $mask .= "\r\n";
    }
    $this->setAllowedChars($mask;
    $oldPrompt = $this->promptText;    
     $this->promptText = 'Pick one option: ' ;
    $response = parent::show();
    $this->promptText = $oldPrompt;    
     return $response;
}

La méthode « show() » affiche d'abord la liste des options disponibles et marque comme caractères autorisés la concaténation de tous les caractères des clés du tableau :

Zend/Console/Prompt/Select.php

Sélectionnez
public function show()
{
    [&#8230;]
    $mask = implode("",array_keys($this->options)) ;
    if ($this->allowEmpty) {
        $mask .= "\r\n";
    }
    $this->setAllowedChars($mask;
    [&#8230;]
    $response = parent::show();
    [&#8230;]
}

La classe Select étend la classe Char qui vérifie si un des caractères présents dans la liste des autorisés existe :

Zend/Console/Prompt/Char.php

Sélectionnez
public function show()
{
        [&#8230;]
        if (stristr($this->allowedChars,$char)) {
            if ($this->echo) {
                echo trim($char)."\n";
            } else {
                echo "\n";    
             }
            break ;
        }
    } while (true);
    return $this->lastResponse = $char ;
}

Une autre possibilité est offerte par le framework avec la confirmation d'action et la classe Zend\Console\Prompt\Confirm :

Utilisation de confirmation
Sélectionnez
$prompt = new Console\Prompt\Confirm('Souhaitez vous continuer ? (y/n)';
$continue = $prompt->show()
;if(!$continue){
    $console->writeLine("Good bye!";
    exit() ;
}

Si la question est personnalisable, le choix de confirmation « y/n » l'est aussi. Le constructeur attend en deuxième et troisième paramètres les deux caractères de confirmation :

Zend/Console/Prompt/Confirm.php

Sélectionnez
public function __construct(
    $promptText = 'Are you sure?',
    $yesChar = 'y',    $noChar = 'n') {
    if ($promptText !== null) {
        $this->setPromptText($promptText;
    }
    if ($yesChar !== null) {
        $this->setYesChar($yesChar);
    }
    if ($noChar !== null) {
        $this->setNoChar($noChar;
    }
}

Deux méthodes d'altération sont aussi possibles pour ces caractères. La méthode « show() » vérifie ensuite l'égalité avec le caractère correspondant à la confirmation, « y » par défaut :

Zend/Console/Prompt/Confirm.php
Sélectionnez
public function show()
{
    $response = parent::show() === $this->yesChar ;
    return $this->lastResponse = $response ;
}

IV. L'abstraction de l'environnement

Lorsque l'on utilise le CLI de PHP, il peut être nécessaire de récupérer les arguments ou variables d'environnement. La classe Zend\Console\Request s'occupe de gérer cela pour vous :

Utilisation de Zend\Console\Request
Sélectionnez
$request = new Console\Request();
$firstParameter = $request->getParams()->get(0);
$scriptName = $request->getScriptName();
$environment = $request->env();

Notons que le premier paramètre du tableau n'est pas le nom du script, qui est placé dans une variable à part, mais bien les arguments que l'on passe au script. Examinons la construction de cet objet :

Zend/Console/Request.php

Sélectionnez
public function __construct(array $args = null, array $env = null)
{
    if ($args === null) {
        if (!isset($_SERVER['argv'])) {    
        [&#8230;]
        }
        $args = $_SERVER['argv'] ;
    }
    if ($env === null) {
        $env = $_ENV ;
    }
    if (count($args) > 0) {
        $this->setScriptName(array_shift($args)) ;
    }
    $this->params()->fromArray($args;
    $this->setContent($args;
    $this->env()->fromArray($env;
}

Nous remarquons que le nom du script est séparé de la liste de ses arguments, et que la récupération des variables est basée sur les variables PHP $_ENV et $_SERVER.

Pour finir la présentation de ce composant, nous remarquerons que la classe Getopt est toujours présente dans cette version du framework :

Utilisation de Getopt

Sélectionnez
$getopt = new Console\Getopt(array(
    'nom|n=s'  => 'argument en chaine de caractère',
));
$getopt->parse();
$nom = $getopt->getOption('nom');
echo "Votre nom est " . $nom . "\n";
Lancement du script

Sélectionnez
php console.php --nom=blanchon

Le composant Zend\Console rend le code beaucoup plus facile lorsque l'on utilise le CLI de PHP. Tout ce que nous devions coder se trouve parfaitement intégré dans le framework pour une meilleure utilisation de la ligne de commande.

V. Conclusion et ressources

L'API, bien fourni, du composant de console va permettre de faciliter le développement des scripts en ligne de commande.
Cet article est tiré en partie du livre "Au cœur de Zend Framework 2"livre "Au cœur de Zend Framework 2" disponible dans le début du quatrième trimestre de l'année 2012.