CakeFest 2024: The Official CakePHP Conference

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::TAR, Phar::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();
?>

add a note

User Contributed Notes 3 notes

up
15
shaun at shaunfreeman dot co dot uk
13 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
3
frame86 at live dot com
10 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