13

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.

hakre
  • 193,403
  • 52
  • 435
  • 836
  • That's a tough question. – wesside Nov 01 '12 at 15:43
  • Could you do something ridiculous and include the base phar file and then try and access the sub phar file through a variable? – wesside Nov 01 '12 at 15:44
  • this question is +1 simply because otherwise hakre wouldn't have asked the question. lol. – goat Nov 01 '12 at 15:51
  • @bigman: No, that would not work, at least not via the `phar://` wrapper which I'd like to use here. Instantiating a `Phar` and reading out file contents *should* work. Then including via `data://` *might* work, but it requires remote file inclusion set to on. – hakre Nov 01 '12 at 15:51
  • @hakre ya, I hear you. Let me ask, are their other sub phars in there? – wesside Nov 01 '12 at 15:53
  • The code is exactly as outlined. The includes won't work in row because the first one throws an exception so stops. The includes are therefore documented (but it's the actual code). The first code-example is how I create the phar files. – hakre Nov 01 '12 at 15:56
  • So something like creating a zip based phar, extracting to get the sub phars is out of the question? – wesside Nov 01 '12 at 15:57
  • Right because I would like to include a file from the phar inside the phar. But it might just not be possible. Maybe something with the `Phar::mapPhar();` call in the first stub. – hakre Nov 01 '12 at 15:58
  • Ya, I'm not sure if its possible. That's like asking a zip file to unzip a zip file within, without unzipping it first. Might have to use `Phar::extractTo` – wesside Nov 01 '12 at 16:01
  • The `phar:://` wrapper can handle phar files. But it's now to make clear that there is a phar file inside the phar. – hakre Nov 01 '12 at 16:03
  • `Phar::loadPhar('/test2.phar/test.phar', 'sub.phar');` maybe? `include('phar://sub.phar');` – wesside Nov 01 '12 at 16:03
  • @bigman: No I don't come close to anything with that. Also the aliasing does not work that way. I even removed my own aliases, however the aliasing seems only to be working inside the file or something. – hakre Nov 01 '12 at 16:21

1 Answers1

5

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.

Danack
  • 24,939
  • 16
  • 90
  • 122