Utilizar Archivos Phar: Introduction

Los archivos phar son similares en concepto a los archivo JAR de Java, pero están adaptados a las necesidades y a la flexibilidad de aplicaciones de PHP. Un archivo Phar se usa para distribuir una aplicación o biblioteca PHP completa en un único fichero. Una aplicación de un archivo Phar se utiliza exactamente de la misma manera que otra aplicación PHP:

php aplicacion.phar
  

Utilizar una biblioteca de archivo Phar es idéntico a usar cualquier otra biblioteca de PHP:

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

La envoltura de flujos phar proporciona el núcleo de la extensión Phar, y está explicada en detalle aquí. La envoltura de flujos phar permite el acceso a los ficheros dentro de un archivo phar utilizando las funciones estándar de ficheros de PHP fopen(), opendir(), y y otras que trabajan sobre ficheros normales. La envoltura de flujos phar soporta todas las operaciones de lectura/escritura tanto en ficheros como en directorios.

<?php
include 'phar://biblioteca.phar/fichero/interno.php';
header('Content-type: image/jpeg');
// a los phar se puede acceder con la ruta completa o mediante un alias
echo file_get_contents('phar:///ruta_completa/a/biblioteca.phar/imagenes/wow.jpg');
?>

La clase Phar implementa una funcionalidad avanzada para acceder a ficheros y crear arhivos phar. La clas Phar está explicada en detalle aquí.

<?php
try {
    
// abrir un phar existente
    
$p = new Phar('biblioteca.phar'0);
    
// Phar extiende la clase DirectoryIterator de SPL
    
foreach (new RecursiveIteratorIterator($p) as $fichero) {
        
// $fichero en una clase PharFileInfo, y hereda de SplFileInfo
        
echo $fichero->getFileName() . "\n";
        echo 
file_get_contents($fichero->getPathName()) . "\n"// mostrar el contenido;
    
}
    if (isset(
$p['fichero/interno.php'])) {
        
var_dump($p['fichero/interno.php']->getMetadata());
    }

    
// crear un nuevo phar - phar.readonly debe ser 0 en php.ini
    // phar.readonly está habilitado por omisión por motivos de seguridad.
    // En servidores de producción, los archivos Phar nunca se crean,
    // sólo se ejecutan.
    
if (Phar::canWrite()) {
        
$p = new Phar('nuevo_phar.tar.phar'0'nuevo_phar.tar.phar');
        
// hacerlo un archivo phar basado en tar, comprimido con gzip (.tar.gz)
        
$p $p->convertToExecutable(Phar::TARPhar::GZ);

        
// crear una transacción -  no se escribe nada en nuevo_phar.phar
        // hasta que stopBuffering() sea llamado, aunque se necesita almacenamiento temporal
        
$p->startBuffering();
        
// añadir todos los ficheros de /ruta/del/proyecto, guardándolos en el phar con el prefijo "proyecto"
        
$p->buildFromIterator(new RecursiveIteratorIterator(new DirectoryIterator('/ruta/del/proyecto')), '/ruta/del/');

        
// añadir un nuevo fichero mediante la API de acceso a arrays
        
$p['fichero1.txt'] = 'Información';
        
$fp fopen('fichero_enorme.dat''rb');
        
// copiar toda la información del flujo
        
$p['datos/fichero_enorme.dat'] = $fp;

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

        
$p['imagenes/wow.jpg'] = file_get_contents('imagenes/wow.jpg');
        
// cualquier valor se puede guardar como metainformación específica del fichero
        
$p['imagenes/wow.jpg']->setMetadata(array('mime-type' => 'image/jpeg'));
        
$p['index.php'] = file_get_contents('index.php');
        
$p->setMetadata(array('bootstrap' => 'index.php'));

        
// cuardar el archivo phar en el disco
        
$p->stopBuffering();
    }
} catch (
Exception $e) {
    echo 
'No se pudo abrir Phar: '$e;
}
?>

Además, la verificación del contenido de ficheros phar se puede realizar utilizando cualquiera de los algoritmos hash simétricos soportados (MD5, SHA1, SHA256 y SHA512 si la extensión Hash está habilitada) y utilizando firmas de clave pública/privada asimétricas de OpenSSL (nuevo en Phar 2.0.0). Para aprovechar la firmas de OpenSSL, se necesita generar una pareja de claves pública/privada, y utilizar la clave privada para establecer la firma usando Phar::setSignatureAlgorithm(). Además, la clave pública se extrae usando este código:

<?php
$public 
openssl_get_publickey(file_get_contents('private.pem'));
$pkey '';
openssl_pkey_export($public$pkey);
?>
debe ser guardada junto al archivo phar que verifica. Si el archivo phar es guardado como /ruta/a/mi.phar, la clave pública debe guardarse como /ruta/a/mi.phar.pubkey, o phar no será capaz de verificar la firma OpenSSL.

A partir de la verisón 2.0.0, la clase Phar también proporciona tres métodos estáticos, Phar::webPhar(), Phar::mungServer() y Phar::interceptFileFuncs(), los cuales son cruiciales para empaquetar aplicaciones PHP diseñadas para un uso en sistemas de ficheros normales y aplicación basadas en web. Phar::webPhar() implementa un controlador principal que direcciona llamadas HTTP a la ubicación correcta dentro del archivo phar. Phar::mungServer() se utiliza para modificar los valores del array $_SERVER para hacer que las aplicaciones procesen estos valores. Phar::interceptFileFuncs() ordena a Phar que intercepte llamdas a fopen(), file_get_contents(), opendir(), y a todas las funciones basadas en estadísticas (file_exists(), is_readable(), etc.) y direccione todas las rutas relativas a las ubicaciones dentro del archivo phar.

Como ejemplo, empaquetar una versión de la popular aplicación phpMyAdmin para usarla cono un archivo phar, requiere solamente este sencillo script, y después se puede acceder a phpMyAdmin.phar.tar.php como un fichero normal desde el servidor web después de modificar el usuario/contraseña:

<?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 está intentando ejecutarse desde un navegador web\n";
exit -1;
__HALT_COMPILER();
'
);
$a->stopBuffering();
?>