PHP Conference Nagoya 2025

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 atteint 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 de OpenSSL. Pour tirer parti de la signature OpenSSL, vous devez 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 relatif 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 de votre 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
15
shaun at shaunfreeman dot co dot uk
14 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
9
ch1902
10 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
11 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.
To Top