17 mai 2011

CMD, une facade PHP pour les commandes système

Dans la lignée de mes précédents billets, voici une nouvelle classe de type Facade. Cette fois-ci, l'idée est de pouvoir lancer des commandes système autrement qu'avec les fonctions system, shell_exec, popen, etc. Car pour les rares habitués de ce blog, vous aurez remarqué que ma lubie du moment est de chercher à voir de quelle manière on peut écrire le moins de code PHP, tout en gardant un code lisible.

Démonstration


       // Afficher le résultat
       echo CMD::factory('/bin/ls')
            ->option('all')
            ->option('b')
            ->option('color', 'never')
            ->option('format', 'single-column')
            ->option('t')
            ->option('reverse')
            ->param('/usr')
            ->fire()
            ->fetchAll()
            ->toString();

       // Envoyer le résultat dans un fichier
       CMD::factory('/bin/ls')
            ->option('all')
            ->param('/usr')
            ->bind(1, '/tmp/t.txt')            
            ->fire();


       // On lit le résultat en "streaming"
       $c = CMD::factory('/usr/bin/curl', array('long_option_operator' => ' '))
           ->option('no-buffer')
           ->option('request', 'GET')
           ->param('http://www.google.com')
           ->fire();

       while($buf = $c->fetch()) {
           echo $buf;
       }

       // On lance une commande en parallèle de l'exécution du script courant
       $c = CMD::factory('/bin/sleep')
            ->param('1')
            ->fire();
       echo 'On attend';

       while($c->isAlive()) {
            usleep(500000);
            echo '.';
       }
       echo 'Fini !';

Vous remarquerez que l'interface est quasiment identique à la classe PQO. Parcourir le résultat d'une commande ou d'une requête SQL se traduit par un code simple et similaire.

PSOStream

Lancer une commande, c'est bien mais pouvoir lui envoyer et recevoir des données, c'est mieux. Et dans ce cas précis, la classe PSOStream a tout son sens. Celle-ci va permettre de lier entrée et sortie standard avec une chaîne de caractère PHP. Le paradigme POO imposerait du polymorphisme or ici, on impose une chaîne de caractères contenant une URL


// Envoie d'une chaîne de caractères sur l'entrée standard
$in = new PSO('Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lacinia placerat tortor auctor ultricies.');
$out = new PSO;
CMD::factory('/usr/bin/fmt')
    ->option('width', '40')
    ->bind(0, $in->toURL())
    ->bind(1, $out->toURL())
    ->fire();

echo $out;

// Récupération de la sortie standard
$out = new PSO;

CMD::factory('/bin/ls')
    ->option('all')
    ->option('b')
    ->option('color', 'never')
    ->option('format', 'single-column')
    ->option('t')
    ->option('reverse')
    ->param('/usr')
    ->bind(1, $out->toURL())
    ->fire();

echo $out;

Téléchargement et code source

Le code source est disponible sur GitHub : http://github.com/touv/plor

Ou, on peut directement l'installer avec PEAR en s'abonnant au Channel Respear :


% pear channel-discover pear.respear.net
% pear install respear/plor

05 mai 2011

PSOStream, string et stream enfin réuni

Il y a bien longtemps déjà, à l'époque où j'ai écrit Pxxo, j'avais cherché à manipuler des buffers qui pouvait être indifféremment en mémoire ou sur disque. Pxxo intègre donc des classes qui implémentent plus ou moins bien cette idée. Jusqu'à récemment, je n'avais plus jamais eu besoin d'un tel mécanisme. Mais voilà qu'au détour d'un besoin fonctionnel, celle-ci réapparaît ...

Cette fois-ci, pas question de développer une myriade de classe. Dans la logique de mes derniers développements, j'ai cherché à interfacer simplement ce que sait faire PHP nativement.

PSOStream

Le principe est de coupler l'usage de la classe PSO et des streams PHP afin de pouvoir utiliser une variable comme on le ferait avec un fichier


        $h = fopen('pso://t3', 'w');
        fwrite($h, 'azerty');
        fclose($h);

        $h = fopen('pso://t3', 'r');
        for($c = ''; !feof($h); $c .= fread($h, 10));
        fclose($h);

        echo $c; // azerty

Rien de neuf ?

Tous les gourous PHP auront reconnu une implémentation légèrement améliorée d'un exemple enfuit dans la documentation. Mais ce qui m'intéresse est ailleurs, c'est le couplage avec la classe PSO :

        // Pour lecture 
        $s = new PSO('azerty');
        $h = fopen($s->toURL(), 'r');
        for($c = ''; !feof($h); $c .= fread($h, 10));
        fclose($h);
        echo $c; // azerty

        // Pour écriture       
        $s = new PSO();
        $h = fopen($s->toURL(), 'w');
        fwrite($h, 'azerty');       
        fclose($h);
        echo $s; // azerty

Inutile ?

Comme ça, de but en blanc, ça n'a l'air de rien, ça semble ne servir à rien, et ça ne révolutionne pas les usages. Mais dans mon cas très précis, c'est utile et ça rend le code extrêmement lisible. Pour vous en convaincre, je vous invite à attendre mon prochain billet qui je l'espère exposera un cas d'utilisation...