I'm having trouble including a PHAR into my application bootstrapping code. I'm attempting to include the phpunit/phpunit PHAR to have the PHPUnit classes available in my application.
Given I have the following directory structure:
/path/to/code/
app.php
phpunit
Where phpunit is the PHPUnit as a PHAR, and app.php contains:
<?php
$phar = 'phar://' . __DIR__ . '/phpunit';
include $phar;
assert(class_exists('\\PHPUnit\\Framework\\TestCase'));
I get the following error:
PHP Warning: include(phar:///path/to/code/phpunit): failed to open stream:
phar error: no directory in "phar:///path/to/code/phpunit",
must have at least phar:///path/to/code/phpunit/ for root directory
(always use full path to a new phar) in /path/to/code/app.php on line 4
But when I rename the PHAR to phpunit.phar, and include that instead using
<?php
$phar = 'phar://' . __DIR__ . '/phpunit.phar';
include $phar;
assert(class_exists('\\PHPUnit\\Framework\\TestCase'));
The code works fine, and the TestCase class exists.
Why doesn't the first version work, and what does the error about "full path to a new phar" mean?
My PHP version is 7.2, and the PHPUnit PHAR is on version 7.* which has been installed with Phive.
"Why don't you just include using the .phar extension?"
I want to be able to use the PHPUnit PHAR as a binary without the .phar extension to keep things cleaner.
Why doesn't the first version work
Based on the source code, you should be able to use a mark the phar as executable and use it without an extension, but if you do that, I am not sure you can run it.
* if executable is 1, only returns SUCCESS if the extension is one of the tar/zip .phar extensions
* if executable is 0, it returns SUCCESS only if the filename does *not* contain ".phar" anywhere, and treats
* the first extension as the filename extension
*
* if an extension is found, it sets ext_str to the location of the file extension in filename,
* and ext_len to the length of the extension.
* for urls like "phar://alias/oops" it instead sets ext_len to -1 and returns FAILURE, which tells
* the calling function to use "alias" as the phar alias
*
* the last parameter should be set to tell the thing to assume that filename is the full path, and only to check the
* extension rules, not to iterate.
what does the error about "full path to a new phar" mean?
I think they are trying to convey that you cannot pass a partial path to a phar.
I want to be able to use the PHPUnit PHAR as a binary without the .phar extension to keep things cleaner.
You could always just symlink or alias the phar file to an extensionless name.
Omit using the phar:// stream since you're accessing the (outer) Phar file. Using phar:// actually tries to access resources inside a Phar archive.
<?php
$phar = __DIR__ . '/phpunit';
include $phar;
assert(class_exists('\\PHPUnit\\Framework\\TestCase'));
Related
Using PHPs Phar, how can I specify which type of file I have, when it does not have and extension?
I have a directory of files with randomly generated names, all files are tar.gz and have the same structure, I want to extract only a single file, config.xml:
Using:
$phar = new PharData($source_file);
$phar->extractTo($tmp, 'config.xml');
I get the following error:
PHP Fatal error: Uncaught UnexpectedValueException: Cannot create phar '/path/to/fkd20c3nd', file extension (or combination) not recognised or the directory does not exist in /path/to/my_script.php:123
However, this is only because the file is randomly named on disk, i.e.
fkd20c3nd
If I just rename this to
fkd20c3nd.tar.gz
Phar works just fine.
Question being
How can I use Phar to extract a file that is missing the extension, besides making a copy of it first, with a new name? Can I simply tell Phar the file type?
.phar file names need to contain at least a dot in the middle of its name so that the file name.
The technical reason for this is that you can access files inside a phar by passing its combined path like "/path/to/my.phar/README.rst", and without a dot it would not work.
I know in your case you don't need this, but the same internal functions are used to detect the correctness of a phar name.
I know that you can call files using require 'phar://pharname.phar/path/to/file.php'; but what about when both files are in the same PHAR? I tried using require 'path/to/file.php'; but that throws an error which makes sense since the file is in the packaged phar and not in the directory it's in.
A phar file has an alias that can be used to address files inside the archive:
Inside a phar file with the alias foo.phar you can do
include 'phar://foo.phar/path/to/file.php';
to include a file from the same archive. See the Phar::__construct() manual page for another example.
I am trying to create a php executable (a phar file) for generating some files, and I would like to know how to get the real path of the phar file (within the phar file code).
What I want to do is to create a folder in the same level of the phar file and create the new files there, but realpath(__DIR__.'/../') does not seem to work.
Thanks
As shown in https://stackoverflow.com/a/28775172/282601 the __FILE__ constant has the full path with the scheme:
phar:///home/cweiske/Dev/test/phar/test.phar/path/to/foo.php
__DIR__ is similar:
phar:///home/cweiske/Dev/test/phar/test.phar/path/to
So when calling realpath(__DIR__) you still have the phar:// prefix that prevents you from loading the file.
You have to remove the phar:// scheme as well as the path of the file inside the .phar to get the phar location with __DIR__.
Much easier is Phar::running(false), which simply returns the path:
/home/cweiske/Dev/test/phar/test.phar
the answer is
$string = 'phar://E:/php/www/my.phar';
$string = pathinfo($string);
$_dir_ = parse_url($string['dirname']);
echo $_dir_['host'].':/'.$_dir_['path'];
result
E:/php/www
I'm trying to make a Phar archive with one of my lib. The lib is just a bunch of classes organized into folders and subfolders. No index.php at all here, just a static Config class to call to initiate the autoloader.
Anyway, I built a archive like this :
$phar = new Phar(__DIR__ . '/lis.phar',0,'lib.phar');
$phar->buildFromDirectory(__DIR__ . '/class','/\.php$');
$phar->stopBuffering();
After that I'm trying to use the phar like this :
require('lib.phar');
Config::register(); // Config is in the phar
But I get the following error :
Warning: include(phar://D:\wamp\www_test\phar\lib.phar/index.php)
[function.include]: failed to open stream: phar error: "index.php" is
not a file in phar "D:/wamp/www/_test/phar/lib.phar" in
D:\wamp\www_test\phar\lib.phar on line 9
How can I make a phar archive without any index.php file inside it ? In fact I just need the archive to be a container for my files, no need to auto execute anything.
First of all, i think you have to startBuffering() before stopBuffering(). And I might think that buildFromDirectory does this internally for you.
You don't need to do stopBuffering() for "sealing" the archive. Its ready "on the fly".
So second: You can watch the defaultStub (which is used in your code implicity) like this:
$phar->setDefaultStub();
var_dump($phar->getStub());
Its a littly bit cryptic, but you will figure it out. It does check for phar stream wrapper support (in 5.3) and if not it extracts the contents to temp file and then executes the Phar::START constant File - which is by default "index.php". And of course it does Phar::interceptFileFuncs() and sets the include path, which makes the phar working "magic". But your question sounds like you only need an archive for your libs. So you're better off with using the "PharData" class. Haven't tried it yet, but the documentation says so.
For example if I have a php script called "RunAllTests.php" in '/var/www/tests/RunAllTests.php' and I execute phpunit within the that directory, the includes in "RunAllTests.php" are found.
If I execute phpunit on that same file, from another directory, lets say '/var/www/', the included files in "RunAllTests.php" cannot be located. - "failed to open stream: No such file or directory in"
I kept this a little bit vague, let me know if you need some more specifics.
Your requires are wrong / not portable: they expect the directory of the file (or some other fixed directory) to be the working dir.
Your options are:
Just start the process from the correct dir.
chdir(__DIR__) in your script.
include / require relative to the __DIR__ constant in your files (which IMHO is most portable)
define a working dir in code beforehand, use that value (related to (3) but more fixed).