Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to place a phar file inside a phar file?

Tags:

php

phar

I'd like to place a phar file inside a phar file. I tried it most straight forward:

$p = new Phar('test.phar', null, 'self.phar');
$p->setStub('<?php Phar::mapPhar();
include \'phar://self.phar/index.php\'; __HALT_COMPILER(); ?>');
$p['index.php'] = '<?php
echo "hello world\n";';

$p = new Phar('test2.phar', null, 'self.phar');
$p->setStub('<?php Phar::mapPhar();
include \'phar://self.phar/index.php\'; __HALT_COMPILER(); ?>');
$p['index.php'] = '<?php
echo "hello phar world\n";';
$p['test.phar'] = file_get_contents('test.phar');

However PHP just does not want to open it. It does not accept any of the following includes:

// Warning: Phar::mapPhar(phar://path/to/test2.phar/test.phar): failed to open
// stream: Invalid argument in phar://path/to/test2.phar/test.phar
include('phar://test2.phar/test.phar');

// Warning: include(phar://test2.phar/test.phar/index.php): failed to open
// stream: phar error: "test.phar/index.php" is not a file in phar "test2.phar"
include('phar://test2.phar/test.phar/index.php');

// Warning: include(phar://test2.phar/phar://test.phar/index.php): failed to
// open stream: phar error: "phar:/test.phar/index.php" is not a file in phar
// "test2.phar"
include('phar://test2.phar/phar://test.phar/index.php');

I know the constructiveness of this question is limited, because it might just not work with phar-in-phar however probably I've just been missing a way how to do that and I'm just not seeing the wood for the trees.

like image 515
hakre Avatar asked Nov 01 '12 15:11

hakre


1 Answers

The function that you should use to load a phar file in PHP is Phar::loadPhar e.g.

Phar::loadPhar("test2.phar", "test2");

Would make the phar file test2.phar be loaded and accessible via the alias test2, so you could include files from it by doing:

include ('phar://test2/index.php');

However it appears that this does not work if that file is inside a phar itself. The PHP code for loadPhar is:

fp = php_stream_open_wrapper(fname, "rb", IGNORE_URL|STREAM_MUST_SEEK, &actual);

and apparently the IGNORE_URL flag makes it impossible for the file to be opened.

There is a workaround - extract the phar file that is contained inside the other phar to a temporary file, and then it apparently can be loaded without any issue. The following code will extract a phar file (phar1.phar) that is contained in a 2nd phar file, and then call loadPhar.

function extractAndLoadPhar(){

    $tempFilename =  tempnam( sys_get_temp_dir() , "phar");

    $readHandle = fopen("phar://phar2/phar1.phar", "r");
    $writeHandle =  fopen($tempFilename, "w");

    while (!feof($readHandle)) {
        $result = fread($readHandle, 512);
        fwrite($writeHandle, $result);
    }

    fclose($readHandle);
    fclose($writeHandle);

    $result = Phar::loadPhar($tempFilename, "phar1");
}

extractAndLoadPhar(); //Extract and load the phar
include ('phar://phar1/src1.php'); //It can now be referenced by 'phar1'

I have placed a working copy of this code here https://github.com/Danack/pharphar which creates a phar, embeds it in a 2nd phar, and then loads and calls a function from the 1st phar inside the 2nd phar.

To note - I'm not convinced that this technique is a good idea. There seems to be some ambiguity (aka I didn't understand) what happens to the stub files for each of the phar files. i.e. do they both get loaded, or is it just the outermost phar file that has it's stub loaded and run.

like image 104
Danack Avatar answered Sep 24 '22 15:09

Danack