Pour diffuser du code sous la forme d'une archive phar, il faut commencer par la générer.

Cela n'a rien de très compliqué en soit, d'autant que Pascal a réalisé un excellent travail de vulgarisation sur le sujet.

Dans le cas de sparkline, la méthode la plus simple consiste :

  1. À créer une instance de la classe \Phar en indiquant l'emplacement final du fichier phar.
  2. À demander à cette instance de lire le contenu du répertoire de sparkline et d'inclure dans le fichier phar tous les fichiers se terminant par .php.

Ceci revient donc à écrire dans le fichier phar.php le code suivant :

<?php
$phar = new \Phar('path/to/sparkline.phar');
$phar->buildFromDirectory('path/to/sparkline/directory', '/\.php$/');
?>

Il suffit ensuite d'effectuer en ligne de commande l'instruction qui suit pour obtenir l'archive désirée à l'emplacement path/to/phar :

# php -d phar.readonly=0 phar.php

Évidement, pour les projets plus complexes que sparkline, PHP 5.3 dispose d'une méthode plus puissante pour sélectionner les fichiers à mettre dans une archive phar.

Notre archive obtenue, nous pouvons l'intégrer directement dans du code PHP, de la manière suivante :

<?php

\Phar::loadPhar(__DIR__ . '/mageekguy.sparkline.phar');
require('phar://mageekguy.sparkline.phar/autoloader.php');

use mageekguy\sparkline;
use mageekguy\sparkline\writers;
use mageekguy\sparkline\layer\linechart;

$data = array(1, 5, 6, -9, 10, -4, 8, 10, 11, 34, 3, 1, -7, -23, 38, 21, 10, 8, 6);

$sparkline = new sparkline(100, 20, $data);

$sparkline
->setPadding(4)
->addLayer(new linechart\curves\line(3, 40, 50, 255))
->addWriter(new writers\png(__DIR__ . '/sparkline.png'))
->write()
;

?>

Nous avons donc une archive qu'il est possible d'intégrer directement dans un script PHP, mais cela nécessite que le développeur écrive un code spécifique à cette tâche.

Pour palier à cet inconvénient, il est possible d'indiquer à l'archive phar le code qui doit être exécuté lorsqu'elle est incluse dans un script PHP à l'aide de la méthode \Phar::setStub().

Ainsi, il est par exemple possible d'indiquer à PHP la méthode d'auto-chargement de classes qui doit être utilisée :

<?php
$phar = new \Phar('/path/to/sparkline.phar');
$phar->buildFromDirectory('/path/to/sparkline/directory', '/\.php$/');
$phar->setStub('<?php Phar::mapPhar(\'mageekguy.sparkline.phar\'); require(\'phar://' . self::phar . '/autoloader.php\'); __HALT_COMPILER(); ?>');
?>

De cette manière, l'utilisation de l'archive est considérablement simplifiée, puisqu'il suffit de l'inclure d'une manière tout à fait classique :

<?php
...
require(__DIR__ . '/mageekguy.sparkline.phar');

use mageekguy\sparkline;
use mageekguy\sparkline\writers;
use mageekguy\sparkline\layer\linechart;

$data = array(1, 5, 6, -9, 10, -4, 8, 10, 11, 34, 3, 1, -7, -23, 38, 21, 10, 8, 6);

$sparkline = new sparkline(100, 20, $data);
...
?>

Nous avons donc maintenant une archive phar qui permet la diffusion de sparkline sous la forme d'un fichier unique et qui de plus facilite son utilisation par le développeur, puisqu'il n'a plus qu'à inclure le fichier phar dans son code pour pouvoir utiliser son contenu.

Nous allons maintenant voir qu'il est possible d'aller plus loin, avec la définition de méta-données, qui s'effectue de la manière suivante :

<?php
$phar = new \Phar('/path/to/sparkline.phar');
$phar->setMetadata(array(
'version' => \mageekguy\sparkline::version,
'author' => 'Frederic Hardy',
'support' => 'sparkline[AT]mageekbox.net,
'description' => file_get_contents(\mageekguy\sparkline::directory . DIRECTORY_SEPARATOR . 'ABOUT'),
'licence' => file_get_contents(\mageekguy\sparkline::directory . DIRECTORY_SEPARATOR . 'COPYING')
)
);
$phar->setStub('<?php Phar::mapPhar(\'mageekguy.sparkline.phar\'); require(\'phar://mageekguy.sparkline.phar/autoloader.php\'); __HALT_COMPILER(); ?>');

$phar->buildFromDirectory('/path/to/sparkline/directory', '/\.php$/');
?>

Bien évidement, il est possible de consulter ces méta-données à l'aide de la méthode \Phar::getMetaData(), mais nous allons voir qu'il est possible de le faire d'une manière beaucoup plus pratique.

En effet, il est possible d'éxécuter une archive phar, soit via un navigateur, soit en ligne de commande, et dans les deux cas, l'archive accepte des arguments, tout comme un script PHP traditionnel.

Il est donc tout à fait possible de définir la gestion de ces arguments dans le code de démarrage de l'archive, défini comme nous l'avons vu précédemment à l'aide de la méthode \Phar::setStub() :

<?php
...
$phar->setStub('<?php Phar::mapPhar(\'mageekguy.sparkline.phar\'); require(\'phar://mageekguy.sparkline.phar/autoloader.php\'); $stub = new \mageekguy\sparkline\phar\stub(); $stub->run(); __HALT_COMPILER(); ?>');
...
?>

Ainsi, il devient possible d'obtenir cela en ligne de commande :

# php mageekguy.sparkline.phar -i
Infos:
Compressed: Yes
version: 0.0.1
author: Frederic Hardy
support: sparkline@mageekbox.net
description: Sparkline generator.
: Sparklines are "data-intense, design-simple, word-sized graphics".
: See http://en.wikipedia.org/wiki/Sparkline for more informations.
licence: * Copyright (c) 1998, Regents of the University of California
: * All rights reserved.
: * Redistribution and use in source and binary forms, with or without
: * modification, are permitted provided that the following conditions are met:
...

Nous avons donc maintenant une archive capable de renseigner un utilisateur sur son contenu à l'aide de méta-données, lorqu'elle est appelée en ligne de commande avec le bon argument.

De plus, les plus attentifs d'entre vous auront peut être remarqué la ligne Compressed: Yes dans la sortie ci-dessus, qui ne fait pas partie des méta-données.

Il est en effet possible de compresser une archive phar, ou bien uniquement une partie de son contenu.

Dans le cas de sparkline, j'ai décidé de compresser l'ensemble des fichiers de l'archive, de la façon suivante  :

<?php
...
$phar->compressFiles(\Phar::GZ);
$phar->setSignatureAlgorithm(\Phar::SHA1);
...
?>

Le code ci-dessus demande également que la signature de l'archive soit générée à l'aide de l'algorithme sha1.

Cette signature permet de vérifier l'intégrité de l'archive en la comparant avec celle fourni par son créateur, ce qui peut être intéressant dans un contexte sensible en terme de sécurité, d'autant qu'il est possible d'utiliser différents algorithmes pour la générer.

Il est à noter que les méthodes \Phar::compressFiles() et \Phar::setSignatureAlgorithm() accepte respectivement plusieurs algorithmes de compression et de génération de signature.

L'archive phar jointe à ce billet utilise toutes ses notions et elle contient le code qui a permis de la réaliser, ainsi que le code de son fichier de démarrage.

Sa signature, visible en utilisant la commande php mageekguy.sparkline.phar -s, est 42C41AA718BBF9E32A46931AF39B190DDCC9B1AD.

L'archive a été générée à l'aide de la commande php -d phar.readonly=0 phar.php -d .., éxecutée à partir du répertoire de sparkline, que vous pourrez créer à l'aide de la commande php -d phar.readonly=0 mageekguy.sparkline.phar -e path/to/directory.

Pour ceux qui s'inquiéteraient des performances d'une archive compressée, l'archive est capable de s'auto-décompresser en ligne de commande.

Je vous invite à découvrir les arguments nécéssaires pour tout cela en éxecutant la commande php mageekguy.sparkline.phar --help, qui permet d'obtenir plus de détails sur le fonctionnement de l'archive.

J'espère que cela vous a donné envie d'en savoir plus sur le format phar, voir même de l'utiliser, d'autant que nous n'avons pas exploré l'intégralité de ses possibilités.