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 coeur 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::TARPhar::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 (nouveauté de Phar 2.0.0). 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.

Depuis la version 2.0.0, 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();
?>