Utiliser les archives Phar : Introduction

Les archives Phar sont identiques dans le concept aux archives JAR de Java, mais sont conçues plus spĂ©cifiquement pour les besoins et la flexibilitĂ© des applications PHP. Une archive Phar est utilisĂ©e pour distribuer une application PHP complĂšte ou une bibliothĂšque sous forme d'un fichier unique. Une application sous forme d'archive Phar est utilisĂ©e exactement de la mĂȘme façon que n'importe quelle autre application PHP :

php applicationsympa.phar
  

L'utilisation d'une bibliothĂšque sous forme d'archive Phar est la mĂȘme que n'importe quelle autre bibliothĂšque PHP :

<?php
include 'bibliothequesympa.phar';
?>

Le flux phar fournit le cƓur de l'extension phar, et est dĂ©crit en dĂ©tails ici. Le flux phar permet l'accĂšs aux fichiers contenus dans une archive phar via les fonctions standards de fichier fopen(), opendir(), et toute autre fonctionnant sur des fichiers normaux. Le flux phar supporte toutes les opĂ©rations de lecture/Ă©criture Ă  la fois sur les fichiers et sur les rĂ©pertoires.

<?php
include 'phar://bibliothequesympa.phar/fichier/interne.php';
header('Content-type: image/jpeg');
// les phars peuvent ĂȘtre atteints via le chemin complet ou via des alias
echo file_get_contents('phar:///chemin/complet/vers/bibliothequesympa.phar/images/wow.jpg');
?>

La classe Phar implémente des fonctionnalités avancées pour accéder aux fichiers et créer des archives phar. La classe Phar est décrite en détails ici.

<?php
try {
// ouvre un phar existant
$p = new Phar('bibliothequesympa.phar', 0);
// Phar étend la classe DirectoryIterator de SPL
foreach (new RecursiveIteratorIterator($p) as $file) {
// $file est une classe PharFileInfo et hérité de SplFileInfo
echo $file->getFileName() . "\n";
echo
file_get_contents($file->getPathName()) . "\n"; // affiche le contenu;
}
if (isset(
$p['fichier/interne.php'])) {
var_dump($p['fichier/interne.php']->getMetadata());
}

// crĂ©e un nouveau phar - phar.readonly doit ĂȘtre Ă  0 dans php.ini
// phar.readonly est activé par défaut pour des raisons de sécurité.
// Sur des serveurs de production, les Phars n'ont pas besoin d'ĂȘtre créés,
// juste d'ĂȘtre exĂ©cutĂ©s.
if (Phar::canWrite()) {
$p = new Phar('nouveauphar.tar.phar', 0, 'nouveauphar.tar.phar');
// On crée une archive Phar basée sur tar, compressée par gzip (.tar.gz)
$p = $p->convertToExecutable(Phar::TAR, Phar::GZ);

// crée une transaction - rien n'est écrit dans nouveauphar.phar
// jusqu'à ce que stopBuffering() ne soit appelé, bien qu'un stockage temporaire soit requis
$p->startBuffering();
// ajoute tous les fichiers de /chemin/vers/leprojet dans le phar avec le préfixe "projet"
$p->buildFromIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator('/chemin/vers/leprojet')), '/chemin/vers/');

// ajoute un nouveau fichier en utilisant l'API d'accĂšs par tableau
$p['fichier1.txt'] = 'Information';
$fp = fopen('grosfichier.dat', 'rb');
// copie toutes les données du flux
$p['data/grosfichier.dat'] = $fp;

if (
Phar::canCompress(Phar::GZ)) {
$p['data/grosfichier.dat']->compress(Phar::GZ);
}

$p['images/wow.jpg'] = file_get_contents('images/wow.jpg');
// toute valeur peut ĂȘtre sauvegardĂ©e comme mĂ©tadonnĂ©e spĂ©cifique au fichier
$p['images/wow.jpg']->setMetadata(array('mime-type' => 'image/jpeg'));
$p['index.php'] = file_get_contents('index.php');
$p->setMetadata(array('bootstrap' => 'index.php'));

// sauvegarde l'archive phar sur le disque
$p->stopBuffering();
}
} catch (
Exception $e) {
echo
'N\'a pas pu ouvrir le Phar: ', $e;
}
?>

D'autre part, la vĂ©rification du contenu du fichier phar peut ĂȘtre faite en utilisant un des algorithme de signature symĂ©trique (MD5, SHA1, SHA256 et SHA512 si ext/hash est activĂ©e) et en utilisant la signature asymĂ©trique par clĂ© publique/privĂ©e d'OpenSSL. Pour tirer parti de la signature OpenSSL, il faut gĂ©nĂ©rer une paire de clĂ©s publique/privĂ©e et utiliser la clĂ© privĂ©e pour signer avec Phar::setSignatureAlgorithm(). En plus, la clĂ© publique, extraite en utilisant ce code :

<?php
$public
= openssl_get_publickey(file_get_contents('private.pem'));
$pkey = '';
openssl_pkey_export($public, $pkey);
?>
doit ĂȘtre sauvegardĂ©e Ă  part de l'archive phar qu'elle vĂ©rifie. Si l'archive phar est sauvegardĂ©e en tant que /chemin/vers/mon.phar, la clĂ© publique doit ĂȘtre sauvegardĂ©e en tant que /chemin/vers/mon.phar.pubkey, sans quoi phar ne sera pas capable de vĂ©rifier la signature OpenSSL.

La classe Phar fournit aussi trois mĂ©thodes statiques, Phar::webPhar(), Phar::mungServer() et Phar::interceptFileFuncs() qui sont cruciales pour empaqueter des applications PHP visant Ă  ĂȘtre utilisĂ©e sur un systĂšme de fichiers classique ou en tant qu'application web. Phar::webPhar() implĂ©mente un contrĂŽleur qui route les appels HTTP vers le bon endroit de l'archive phar. Phar::mungServer() est utilisĂ© pour modifier les valeurs du tableau $_SERVER pour dire aux applications d'utiliser ces valeurs. Phar::interceptFileFuncs() dit Ă  Phar d'intercepter les appels Ă  fopen(), file_get_contents(), opendir(), et Ă  toutes les fonctions basĂ©es sur stat (file_exists(), is_readable(), etc) et route tous les chemins relatifs vers les bons endroits de l'archive phar.

Par exemple, empaqueter une version de la cĂ©lĂšbre application phpMyAdmin dans une archive phar nĂ©cessite juste ce simple script et, dĂšs lors, phpMyAdmin.phar.tar.php peut ĂȘtre accĂ©dĂ© comme un fichier classique Ă  partir du serveur web, aprĂšs avoir modifiĂ© le couple utilisateur/motdepasse :

<?php
@unlink('phpMyAdmin.phar.tar.php');
copy('phpMyAdmin-2.11.3-english.tar.gz', 'phpMyAdmin.phar.tar.php');
$a = new Phar('phpMyAdmin.phar.tar.php');
$a->startBuffering();
$a["phpMyAdmin-2.11.3-english/config.inc.php"] = '<?php
/* Servers configuration */
$i = 0;

/* Server localhost (config:root) [1] */
$i++;
$cfg[\'Servers\'][$i][\'host\'] = \'localhost\';
$cfg[\'Servers\'][$i][\'extension\'] = \'mysqli\';
$cfg[\'Servers\'][$i][\'connect_type\'] = \'tcp\';
$cfg[\'Servers\'][$i][\'compress\'] = false;
$cfg[\'Servers\'][$i][\'auth_type\'] = \'config\';
$cfg[\'Servers\'][$i][\'user\'] = \'root\';
$cfg[\'Servers\'][$i][\'password\'] = \'\';


/* End of servers configuration */
if (strpos(PHP_OS, \'WIN\') !== false) {
$cfg[\'UploadDir\'] = getcwd();
} else {
$cfg[\'UploadDir\'] = \'/tmp/pharphpmyadmin\';
@mkdir(\'/tmp/pharphpmyadmin\');
@chmod(\'/tmp/pharphpmyadmin\', 0777);
}'
;
$a->setStub('<?php
Phar::interceptFileFuncs();
Phar::webPhar("phpMyAdmin.phar", "phpMyAdmin-2.11.3-english/index.php");
echo "phpMyAdmin is intended to be executed from a web browser\n";
exit -1;
__HALT_COMPILER();
'
);
$a->stopBuffering();
?>
add a note

User Contributed Notes 3 notes

up
14
shaun at shaunfreeman dot co dot uk ¶
15 years ago
If you are trying to use Phar for a web application and just getting a blank screen, if you have enabled suhosin as well you have to add:

suhosin.executor.include.whitelist="phar"

to "/etc/php5/conf.d/suhosin.ini" file or your "php.ini" file.

once done everything works fine and dandy.
up
10
ch1902 ¶
12 years ago
If you are going to be running a webPhar from the browser, for example http://localhost/myphar.phar then you will probably have to associate the .phar extension with PHP in your webserver to interpret the PHP code. In Apache modify httpd.conf to include

AddType application/x-httpd-php .php .phar
up
2
frame86 at live dot com ¶
12 years ago
The openssl example is completely wrong. The public key must be extracted from certificate and openssl_pkey_export() is for private key only.

Working example:
<?php
$publicKey = openssl_get_publickey(file_get_contents('certificate.pem'));
$details = openssl_pkey_get_details($publicKey);
file_put_contents('my.phar.pubkey', $details['key']);
?>

No need to say that the best and strongest encryption of my.phar/.phar/signature.bin is useless if the consumer does not check against a valid fingerprint or certificate of public key as anybody can open, read, recreate and sign a new archive with new key. Do you do? Think about it.