Rectangle 27 4

Working with file paths and Phar archives

Working with file paths and Phar archives in PHP can be tricky. The PHP code inside of a Phar file will treat relative paths as being relative to the Phar archive, not relative to the current working directory. Here's a short example:

Say you have the following files:

phar/index.php
test.php
my.phar

The index.php file is located inside of the phar directory. It is the bootstrap file for the phar archive:

function does_it_exist($file){
  return file_exists($file) ? "true" : "false";
}

The bootstrap file is executed when the phar file is included from a PHP script. Our bootstrap file will simply cause the function "does_it_exist" to be declared.

Let's try running different code inside of test.php and see what the results are for each run:

//Run 1:
require_once 'phar/index.php';  //PHP file
$file = __DIR__ . "/index.php"; //absolute path
echo does_it_exist($file);      //prints "false"

//Run 2:
require_once 'phar/index.php';  //PHP file
$file = "index.php";            //relative path
echo does_it_exist($file);      //prints "false"

//Run 3:
require_once 'my.phar';         //phar file
$file = __DIR__ . "/index.php"; //absolute path
echo does_it_exist($file);      //prints "false"

//Run 4:
require_once 'my.phar';         //phar file
$file = "index.php";            //relative path
echo does_it_exist($file);      //prints "true"

Look at Run 4. This code includes the phar file and passes the function a relative path. Relative to the current working directory, index.php does not exist. But relative to the contents of the phar archive, it does exist, which is why it prints "true"!

i know, i know. thats not the question. my phar package creates a new file OUTSIDE of the phar script using fwrite. that works perfectly, a new file is created. and this new can later be read by include or require but it cant be "seen" by file_exists - which is my problem - i think it might be a bug in phar

thanks, but if include('login.php') or require('login.php') both find and read the file, than surely if(file_exists('login.php')) would return true - surely by the fact that its read by include/require shows the file path/location is correct, and so that is not the issue?

hey thanks man, i think i'm starting to see what your saying. But my problem is that WITHIN my phar file, i want to check for a file OUTSIDE of it. when i run my phar file it creates 'login.php' in the same directory as the phar, but outside of it. then when you run the phar again, i need it to check if that login.php is there, outside. what file path do i use for that?

include - PHP Phar - file_exists() issue - Stack Overflow

php include require file-exists phar
Rectangle 27 0

Not sure if it's new, but under PHP 5.4.16 this works:

phar extract -f %some phar file%

Works in Php 5.3.3 too

php - Extracting files from .phar archive - Stack Overflow

php phar
Rectangle 27 0

Phar archives can be distributed as tar archives, zip archives, or as the custom phar file format designed specifically for the phar extension. Each file format has advantages and disadvantages. The tar and zip file formats can be read or extracted by any third-party tool that can read the format, but require the phar extension in order to run with PHP. The phar file format is customized and unique to the phar extension, and can only be created by the phar extension or the PEAR package PHP_Archive, but has the advantage that applications created in this format will run even if the phar extension is not enabled.

So you can just zip some files up and name the resulting file .phar.

Its not possible to add a stub and metadata this way.

php - an Apache Ant task to generate phar packages? - Stack Overflow

php eclipse ant phar
Rectangle 27 0

My understanding of the phar file , you are suppose to open(fopen) the file in your php script which you are gonna be calling....if you are getting those characters back, it means apache is sending you the file in binary format....

if you add this "AddType application/x-gzip .gz .tgz .phar" to the apache config, it will make the file download-able like any other archive file and will not execute it like a php script

I don't want to download the file as a binary, but want to display it's contents

php - Phar archive outputs jibberish - Stack Overflow

php apache phar
Rectangle 27 0

How to create and use a Phar archive [PHP]

<?php

/*
 * PHP Phar - How to create and use a Phar archive
 */

$p = new Phar('my.phar', FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::KEY_AS_FILENAME, 'my.phar');
//issue the Phar::startBuffering() method call to buffer changes made to the archive until you issue the Phar::stopBuffering() command
$p->startBuffering();

//set the Phar file stub
//the file stub is merely a small segment of code that gets run initially when the Phar file is loaded, 
//and it always ends with a __HALT_COMPILER() 
$p->setStub('<?php Phar::mapPhar(); include "phar://my.phar/index.php"; __HALT_COMPILER(); ?>');

//Adding files to the archive
$p['text.txt'] = 'This is a text file';
//Adding files to an archive using Phar::buildFromDirectory()
//adds all of the PHP files in the stated directory to the Phar archive
$p->buildFromDirectory('include/', '$(.*)\.php$');

//Stop buffering write requests to the Phar archive, and save changes to disk
$p->stopBuffering();
echo "my.phar archive has been saved";

?>
PHP Phar
Rectangle 27 0

Solution: Whitelist the phar protocol:

$ emacs /etc/php5/cli/conf.d/suhosin.ini
.. add the following line:
suhosin.executor.include.whitelist = phar

This is explained on the composer site btw.

Thanks for answering. I did not have time to check this out yet. However I am sure this must be the problem. I did try to install codecept mainly and I do not thing there website mentions this. Thanks again!

php - Using CLI to use phar file not working - Stack Overflow

php cygwin command-line-interface phar codeception
Rectangle 27 0

First of all, with file_put_contents(), I've noticed some weirdness. First, what I did is follow the example of the link you posted. I was able to add the $context variable, but still had the error. What worked well for me was changing the file name to phar.phar. Perhaps the phar:// handler can't understand a .tar extension?

$context = stream_context_create(array('phar' =>
                                array('compress' => Phar::GZ)),
                                array('metadata' => array('user' => 'cellog')));

file_put_contents('phar://./phar.phar/test/foo.txt', 'bar', 0, $context);
$phar = new PharData('./phar.tar', 0, null, Phar::TAR);
$phar->addEmptyDir('test');
$phar->addFile('./bar', 'foo.txt');

If you really need to use file_put_contents() instead, perhaps there's a different problem that exists that maybe we can help with. Thanks and good luck!

Strangely enough, if you use the first approach and you do $phar = new PharData('./phar.phar', 0, null, Phar::TAR); $phar->addEmptyDir('test'); before, you'll get the exact same error. Without it (and only with the .phar extension) it works. Not defining the $context at all, also works (however I'm not sure about the compression method). I know that the second option is the "recommended" one, but for reasons that I don't remember anymore (I came with a different solution for my problem) , I only had the phar://... path to work with.

Oh, and one other thing that bugs me is that file_get_contents works out of the box with .phar, .zip and .tar extension files, as long as they are prefixed with the phar:// stream wrapper, you don't even have to specify $context! I would expect file_put_contents() to be consistent and behave in the same way, but no. :(

php - Using phar Stream to Write PharData Files - Stack Overflow

php stream phar
Rectangle 27 0

Note that it seems not to work well with Phar archives whose contents were gzipped. At least to me on Windows, but being a development setup, it has gzip, bz2, etc. so it should be able to extract them, but the results are mangled (and don't seem to be even valid as .gz files). The directory structure comes out correctly, though.

php - Extracting files from .phar archive - Stack Overflow

php phar
Rectangle 27 0

I think this basically has to do with the fact that you are working with Phar::TAR files because

Warning: file_put_contents(phar://./test.tar/foo.txt): failed to open stream: Cannot create phar './test.tar', file extension (or combination) not recognized or the directory does not exist

file_put_contents("phar://./test.phar/foo.txt", "hello world"); // Works file
                                  ^
                                  |+---- Note This phar
//No Errors

If you look at a detail example in the PHP doc , this has been a known issue for the past 2 years and the example given are as follows

$p = new PharData(dirname(__FILE__) . '/phartest.zip', 0, 'phartest', Phar::ZIP);
$p->addFromString('testfile.txt', 'this is just some test text');

// This works
echo file_get_contents('phar://phartest.zip/testfile.txt');

// This Fails
file_put_contents('phar://phartest.zip/testfile.txt', 'Thist is text for testfile.txt');

$context = stream_context_create(array(
        'phar' => array(
                'compress' => Phar::ZIP
        )
));

// This Fails
file_put_contents('phar://phartest.zip/testfile.txt', 'Thist is text for testfile.txt', 0, $context);

// This works but only with 'r' readonly mode.
$f = fopen('phar://C:\\Inetpub\\wwwroot\\PACT\\test\\phartest.zip\\testfile.txt', 'r');

Don't use .tar or .zip has your file extension but set compression as demonstrated in the PHP DOC

$context = stream_context_create(array(
        'phar' => array(
                'compress' => Phar::GZ //set compression
        )
));

file_put_contents('phar://sample.phar/somefile.txt', "Sample Data", 0, $context);
                                   ^
                                   |= Use phar not tar
PharData

Then write your own wrapper and register it with stream_wrapper_register here is a simple Prof of Concept

stream_wrapper_register("alixphar", "AlixPhar");
file_put_contents('alixphar://phar.tar/test/foo.txt', 'hey'); 
                         ^
                         |+-- Write with alixphar

echo file_get_contents('alixphar://phar.tar/test/foo.txt'),PHP_EOL;
echo file_get_contents('phar://phar.tar/test/foo.txt'),PHP_EOL;
hey     <----- alixphar
hey     <----- phar
class AlixPhar {
    private $pos;
    private $stream;
    private $types;
    private $dir;
    private $pharContainer, $pharType, $pharFile, $pharDir = null;
    private $fp;

    function __construct() {
        $this->types = array(
                "tar" => Phar::TAR,
                "zip" => Phar::ZIP,
                "phar" => Phar::PHAR
        );
        // your phar dir to help avoid using path before the phar file
        $this->dir = __DIR__;
    }

    private function parsePath($path) {
        $url = parse_url($path);
        $this->pharContainer = $url['host'];
        $this->pharType = $this->types[pathinfo($this->pharContainer, PATHINFO_EXTENSION)];
        if (strpos($url['path'], ".") !== false) {
            list($this->pharDir, $this->pharFile, , ) = array_values(pathinfo($url['path']));
        } else {
            $this->pharDir = $url['path'];
        }
    }

    public function stream_open($path, $mode, $options, &$opened_path) {
        $this->parsePath($path);
        $this->stream = new PharData($this->dir . "/" . $this->pharContainer, 0, null, $this->pharType);
        if (! empty($this->pharDir))
            $this->stream->addEmptyDir($this->pharDir);

        if ($mode == "rb") {
            $this->fp = fopen("phar://" . $this->pharContainer . "/" . $this->pharDir . "/" . $this->pharFile, $mode);
        }
        return true;
    }

    public function stream_write($data) {
        $this->pos += strlen($data);
        $this->stream->addFromString($this->pharDir . "/" . $this->pharFile, $data);
        return $this->pos;
    }

    public function stream_read($count) {
        return fread($this->fp, $count);
    }

    public function stream_tell() {
        return ftell($this->fp);
    }

    public function stream_eof() {
        return feof($this->fp);
    }

    public function stream_stat() {
        return fstat($this->fp);
    }
}

php - Using phar Stream to Write PharData Files - Stack Overflow

php stream phar
Rectangle 27 0

Working with file paths and Phar archives

Working with file paths and Phar archives in PHP can be tricky. The PHP code inside of a Phar file will treat relative paths as being relative to the Phar archive, not relative to the current working directory. Here's a short example:

Say you have the following files:

phar/index.php
test.php
my.phar

The index.php file is located inside of the phar directory. It is the bootstrap file for the phar archive:

function does_it_exist($file){
  return file_exists($file) ? "true" : "false";
}

The bootstrap file is executed when the phar file is included from a PHP script. Our bootstrap file will simply cause the function "does_it_exist" to be declared.

Let's try running different code inside of test.php and see what the results are for each run:

//Run 1:
require_once 'phar/index.php';  //PHP file
$file = __DIR__ . "/index.php"; //absolute path
echo does_it_exist($file);      //prints "false"

//Run 2:
require_once 'phar/index.php';  //PHP file
$file = "index.php";            //relative path
echo does_it_exist($file);      //prints "false"

//Run 3:
require_once 'my.phar';         //phar file
$file = __DIR__ . "/index.php"; //absolute path
echo does_it_exist($file);      //prints "false"

//Run 4:
require_once 'my.phar';         //phar file
$file = "index.php";            //relative path
echo does_it_exist($file);      //prints "true"

Look at Run 4. This code includes the phar file and passes the function a relative path. Relative to the current working directory, index.php does not exist. But relative to the contents of the phar archive, it does exist, which is why it prints "true"!

i know, i know. thats not the question. my phar package creates a new file OUTSIDE of the phar script using fwrite. that works perfectly, a new file is created. and this new can later be read by include or require but it cant be "seen" by file_exists - which is my problem - i think it might be a bug in phar

thanks, but if include('login.php') or require('login.php') both find and read the file, than surely if(file_exists('login.php')) would return true - surely by the fact that its read by include/require shows the file path/location is correct, and so that is not the issue?

hey thanks man, i think i'm starting to see what your saying. But my problem is that WITHIN my phar file, i want to check for a file OUTSIDE of it. when i run my phar file it creates 'login.php' in the same directory as the phar, but outside of it. then when you run the phar again, i need it to check if that login.php is there, outside. what file path do i use for that?

include - PHP Phar - file_exists() issue - Stack Overflow

php include require file-exists phar