pull/10250/head
parent
bcbd8fdb61
commit
90087b4fb3
|
@ -255,17 +255,13 @@ class BinaryInstaller
|
||||||
$binContents = file_get_contents($bin);
|
$binContents = file_get_contents($bin);
|
||||||
// For php files, we generate a PHP proxy instead of a shell one,
|
// For php files, we generate a PHP proxy instead of a shell one,
|
||||||
// which allows calling the proxy with a custom php process
|
// which allows calling the proxy with a custom php process
|
||||||
if (preg_match('{^(?:#!(?:/usr)?/bin/env php|#!(?:/usr)?/bin/php|<?php)\r?\n}', $binContents, $match)) {
|
if (preg_match('{^(#!.*\r?\n)?<\?php}', $binContents, $match)) {
|
||||||
// verify the file is not a phar file, because those do not support php-proxying
|
// carry over the existing shebang if present, otherwise add our own
|
||||||
if (false === ($pos = strpos($binContents, '__HALT_COMPILER')) || false === strpos(substr($binContents, 0, $pos), 'Phar::mapPhar')) {
|
$proxyCode = empty($match[1]) ? '#!/usr/bin/env php' : trim($match[1]);
|
||||||
$proxyCode = trim($match[0]);
|
|
||||||
// carry over the existing shebang if present, otherwise add our own
|
|
||||||
if ($proxyCode === "<?php") {
|
|
||||||
$proxyCode = "#!/usr/bin/env php";
|
|
||||||
}
|
|
||||||
$binPathExported = var_export($binPath, true);
|
|
||||||
|
|
||||||
return $proxyCode . "\n" . <<<PROXY
|
$binPathExported = var_export($binPath, true);
|
||||||
|
|
||||||
|
return $proxyCode . "\n" . <<<PROXY
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,18 +273,95 @@ class BinaryInstaller
|
||||||
* @generated
|
* @generated
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (PHP_VERSION_ID < 80000) {
|
namespace Composer;
|
||||||
ob_start(function (\$buffer, \$phase) {
|
|
||||||
return (PHP_OUTPUT_HANDLER_START & \$phase) && '#!' === substr(\$buffer, 0, 2) ? '' : \$buffer;
|
|
||||||
}, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
include __DIR__ . "/" . $binPathExported;
|
\$binPath = __DIR__ . "/" . $binPathExported;
|
||||||
PROXY;
|
|
||||||
|
if (PHP_VERSION_ID < 80000) {
|
||||||
|
if (!class_exists('Composer\BinProxyWrapper')) {
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class BinProxyWrapper
|
||||||
|
{
|
||||||
|
private \$handle;
|
||||||
|
private \$position;
|
||||||
|
|
||||||
|
public function stream_open(\$path, \$mode, \$options, &\$opened_path)
|
||||||
|
{
|
||||||
|
// get rid of composer-bin-proxy:// prefix for __FILE__ & __DIR__ resolution
|
||||||
|
\$opened_path = substr(\$path, 21);
|
||||||
|
\$this->handle = fopen(\$opened_path, \$mode);
|
||||||
|
\$this->position = 0;
|
||||||
|
|
||||||
|
// remove all traces of this stream wrapper once it has been used
|
||||||
|
stream_wrapper_unregister('composer-bin-proxy');
|
||||||
|
|
||||||
|
return (bool) \$this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_read(\$count)
|
||||||
|
{
|
||||||
|
\$data = fread(\$this->handle, \$count);
|
||||||
|
|
||||||
|
if (\$this->position === 0) {
|
||||||
|
\$data = preg_replace('{^#!.*\\r?\\n}', '', \$data);
|
||||||
|
}
|
||||||
|
|
||||||
|
\$this->position += strlen(\$data);
|
||||||
|
|
||||||
|
return \$data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_cast(\$castAs)
|
||||||
|
{
|
||||||
|
return \$this->handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_close()
|
||||||
|
{
|
||||||
|
fclose(\$this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_lock(\$operation)
|
||||||
|
{
|
||||||
|
return \$operation ? flock(\$this->handle, \$operation) : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_tell()
|
||||||
|
{
|
||||||
|
return \$this->position;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_eof()
|
||||||
|
{
|
||||||
|
return feof(\$this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_stat()
|
||||||
|
{
|
||||||
|
return fstat(\$this->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stream_set_option(\$option, \$arg1, \$arg2)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$proxyCode = <<<PROXY
|
if (function_exists('stream_wrapper_register') && stream_wrapper_register('composer-bin-proxy', 'Composer\BinProxyWrapper')) {
|
||||||
|
include("composer-bin-proxy://" . \$binPath);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include \$binPath;
|
||||||
|
|
||||||
|
PROXY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <<<PROXY
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
dir=\$(cd "\${0%[/\\\\]*}" > /dev/null; cd $binDir && pwd)
|
dir=\$(cd "\${0%[/\\\\]*}" > /dev/null; cd $binDir && pwd)
|
||||||
|
@ -305,7 +378,5 @@ fi
|
||||||
"\${dir}/$binFile" "\$@"
|
"\${dir}/$binFile" "\$@"
|
||||||
|
|
||||||
PROXY;
|
PROXY;
|
||||||
|
|
||||||
return $proxyCode;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Composer.
|
||||||
|
*
|
||||||
|
* (c) Nils Adermann <naderman@naderman.de>
|
||||||
|
* Jordi Boggiano <j.boggiano@seld.be>
|
||||||
|
*
|
||||||
|
* For the full copyright and license information, please view the LICENSE
|
||||||
|
* file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Composer\Test\Installer;
|
||||||
|
|
||||||
|
use Composer\Installer\BinaryInstaller;
|
||||||
|
use Composer\Util\Filesystem;
|
||||||
|
use Composer\Test\TestCase;
|
||||||
|
use Composer\Composer;
|
||||||
|
use Composer\Config;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
|
||||||
|
class BinaryInstallerTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $rootDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $vendorDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $binDir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Composer\IO\IOInterface&\PHPUnit\Framework\MockObject\MockObject
|
||||||
|
*/
|
||||||
|
protected $io;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var \Composer\Util\Filesystem
|
||||||
|
*/
|
||||||
|
protected $fs;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->fs = new Filesystem;
|
||||||
|
|
||||||
|
$this->rootDir = $this->getUniqueTmpDirectory();
|
||||||
|
$this->vendorDir = $this->rootDir.DIRECTORY_SEPARATOR.'vendor';
|
||||||
|
$this->ensureDirectoryExistsAndClear($this->vendorDir);
|
||||||
|
|
||||||
|
$this->binDir = $this->rootDir.DIRECTORY_SEPARATOR.'bin';
|
||||||
|
$this->ensureDirectoryExistsAndClear($this->binDir);
|
||||||
|
|
||||||
|
$this->io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tearDown()
|
||||||
|
{
|
||||||
|
$this->fs->removeDirectory($this->rootDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider executableBinaryProvider
|
||||||
|
* @param string $contents
|
||||||
|
*/
|
||||||
|
public function testInstallAndExecBinaryWithFullCompat($contents)
|
||||||
|
{
|
||||||
|
$package = $this->createPackageMock();
|
||||||
|
$package->expects($this->any())
|
||||||
|
->method('getBinaries')
|
||||||
|
->willReturn(array('binary'));
|
||||||
|
|
||||||
|
$this->ensureDirectoryExistsAndClear($this->vendorDir.'/foo/bar');
|
||||||
|
file_put_contents($this->vendorDir.'/foo/bar/binary', $contents);
|
||||||
|
|
||||||
|
$installer = new BinaryInstaller($this->io, $this->binDir, 'full', $this->fs);
|
||||||
|
$installer->installBinaries($package, $this->vendorDir.'/foo/bar');
|
||||||
|
|
||||||
|
$proc = new ProcessExecutor();
|
||||||
|
$proc->execute($this->binDir.'/binary arg', $output);
|
||||||
|
$this->assertEquals('', $proc->getErrorOutput());
|
||||||
|
$this->assertEquals('success arg', $output);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executableBinaryProvider()
|
||||||
|
{
|
||||||
|
$tests = array(
|
||||||
|
'simple php file' => array(<<<'EOL'
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo 'success '.$_SERVER['argv'][1];
|
||||||
|
EOL
|
||||||
|
),
|
||||||
|
'php file with shebang' => array(<<<'EOL'
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
echo 'success '.$_SERVER['argv'][1];
|
||||||
|
EOL
|
||||||
|
),
|
||||||
|
'phar file' => array(
|
||||||
|
base64_decode('IyEvdXNyL2Jpbi9lbnYgcGhwCjw/cGhwCgpQaGFyOjptYXBQaGFyKCd0ZXN0LnBoYXInKTsKCnJlcXVpcmUgJ3BoYXI6Ly90ZXN0LnBoYXIvcnVuLnBocCc7CgpfX0hBTFRfQ09NUElMRVIoKTsgPz4NCj4AAAABAAAAEQAAAAEACQAAAHRlc3QucGhhcgAAAAAHAAAAcnVuLnBocCoAAADb9n9hKgAAAMUDDWGkAQAAAAAAADw/cGhwIGVjaG8gInN1Y2Nlc3MgIi4kX1NFUlZFUlsiYXJndiJdWzFdO1SOC0IE3+UN0yzrHIwyspp9slhmAgAAAEdCTUI=')
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (PHP_VERSION_ID >= 70000) {
|
||||||
|
$tests += array(
|
||||||
|
'shebang with strict types declare' => array(<<<'EOL'
|
||||||
|
#!/usr/bin/env php
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
echo 'success '.$_SERVER['argv'][1];
|
||||||
|
EOL
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tests;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return \Composer\Package\PackageInterface&\PHPUnit\Framework\MockObject\MockObject
|
||||||
|
*/
|
||||||
|
protected function createPackageMock()
|
||||||
|
{
|
||||||
|
return $this->getMockBuilder('Composer\Package\Package')
|
||||||
|
->setConstructorArgs(array(md5((string) mt_rand()), '1.0.0.0', '1.0.0'))
|
||||||
|
->getMock();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue