Expand library version checking capabilities (closes #9093)
parent
657ae5519e
commit
7e1ef19a5a
@ -0,0 +1,59 @@
|
|||||||
|
<?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\Platform;
|
||||||
|
|
||||||
|
use Composer\Util\Platform;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Symfony\Component\Process\ExecutableFinder;
|
||||||
|
|
||||||
|
class HhvmDetector
|
||||||
|
{
|
||||||
|
private static $hhvmVersion;
|
||||||
|
private $executableFinder;
|
||||||
|
private $processExecutor;
|
||||||
|
|
||||||
|
public function __construct(ExecutableFinder $executableFinder = null, ProcessExecutor $processExecutor = null) {
|
||||||
|
|
||||||
|
$this->executableFinder = $executableFinder;
|
||||||
|
$this->processExecutor = $processExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
self::$hhvmVersion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersion() {
|
||||||
|
if (null !== self::$hhvmVersion) {
|
||||||
|
return self::$hhvmVersion ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null;
|
||||||
|
if (self::$hhvmVersion === null && !Platform::isWindows()) {
|
||||||
|
self::$hhvmVersion = false;
|
||||||
|
$this->executableFinder = $this->executableFinder ?: new ExecutableFinder();
|
||||||
|
$hhvmPath = $this->executableFinder->find('hhvm');
|
||||||
|
if ($hhvmPath !== null) {
|
||||||
|
$this->processExecutor = $this->processExecutor ?: new ProcessExecutor();
|
||||||
|
$exitCode = $this->processExecutor->execute(
|
||||||
|
ProcessExecutor::escape($hhvmPath).
|
||||||
|
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
|
||||||
|
self::$hhvmVersion
|
||||||
|
);
|
||||||
|
if ($exitCode !== 0) {
|
||||||
|
self::$hhvmVersion = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self::$hhvmVersion;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
<?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\Platform;
|
||||||
|
|
||||||
|
class Runtime
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $constant
|
||||||
|
* @param class-string $class
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasConstant($constant, $class = null) {
|
||||||
|
return defined(ltrim($class.'::'.$constant, ':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $constant
|
||||||
|
* @param class-string $class
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function getConstant($constant, $class = null) {
|
||||||
|
return constant(ltrim($class.'::'.$constant, ':'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param callable $callable
|
||||||
|
* @param array $arguments
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function invoke($callable, array $arguments = array()) {
|
||||||
|
return call_user_func_array($callable, $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param class-string $class
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hasClass($class) {
|
||||||
|
return class_exists($class, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param class-string $class
|
||||||
|
* @param array $arguments
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public function construct($class, array $arguments = array()) {
|
||||||
|
if (empty($arguments)) {
|
||||||
|
return new $class;
|
||||||
|
}
|
||||||
|
|
||||||
|
$refl = new \ReflectionClass($class);
|
||||||
|
return $refl->newInstanceArgs($arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return string[] */
|
||||||
|
public function getExtensions()
|
||||||
|
{
|
||||||
|
return get_loaded_extensions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $extension
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExtensionVersion($extension)
|
||||||
|
{
|
||||||
|
return phpversion($extension);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $extension
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getExtensionInfo($extension)
|
||||||
|
{
|
||||||
|
$reflector = new \ReflectionExtension($extension);
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$reflector->info();
|
||||||
|
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,104 @@
|
|||||||
|
<?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\Util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lars Strojny <lars@strojny.net>
|
||||||
|
*/
|
||||||
|
class Version
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param string $opensslVersion
|
||||||
|
* @param bool $isFips
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function parseOpenssl($opensslVersion, &$isFips)
|
||||||
|
{
|
||||||
|
$isFips = false;
|
||||||
|
|
||||||
|
if (!preg_match('/^(?<version>[0-9.]+)(?<patch>[a-z]{0,2})?(?<suffix>(?:-?(?:dev|pre|alpha|beta|rc|fips)[\d]*)*)?$/', $opensslVersion, $matches)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isFips = strpos($matches['suffix'], 'fips') !== false;
|
||||||
|
$suffix = strtr('-'.ltrim($matches['suffix'], '-'), array('-fips' => '', '-pre' => '-alpha'));
|
||||||
|
$patch = self::convertAlphaVersionToIntVersion($matches['patch']);
|
||||||
|
|
||||||
|
return rtrim($matches['version'].'.'.$patch.$suffix, '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $libjpegVersion
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function parseLibjpeg($libjpegVersion)
|
||||||
|
{
|
||||||
|
if (!preg_match('/^(?<major>\d+)(?<minor>[a-z]*)$/', $libjpegVersion, $matches)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $matches['major'].'.'.self::convertAlphaVersionToIntVersion($matches['minor']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $zoneinfoVersion
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public static function parseZoneinfoVersion($zoneinfoVersion)
|
||||||
|
{
|
||||||
|
if (!preg_match('/^(?<year>\d{4})(?<revision>[a-z]*)$/', $zoneinfoVersion, $matches)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $matches['year'].'.'.self::convertAlphaVersionToIntVersion($matches['revision']);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "" => 0, "a" => 1, "zg" => 33
|
||||||
|
*
|
||||||
|
* @param string $alpha
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private static function convertAlphaVersionToIntVersion($alpha)
|
||||||
|
{
|
||||||
|
return strlen($alpha) * (-ord('a')+1) + array_sum(array_map('ord', str_split($alpha)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $versionId
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function convertLibxpmVersionId($versionId)
|
||||||
|
{
|
||||||
|
return self::convertVersionId($versionId, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $versionId
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function convertOpenldapVersionId($versionId)
|
||||||
|
{
|
||||||
|
return self::convertVersionId($versionId, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function convertVersionId($versionId, $base)
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'%d.%d.%d',
|
||||||
|
$versionId / ($base * $base),
|
||||||
|
($versionId / $base) % $base,
|
||||||
|
$versionId % $base
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
<?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\Platform;
|
||||||
|
|
||||||
|
use Composer\Platform\HhvmDetector;
|
||||||
|
use Composer\Test\TestCase;
|
||||||
|
use Composer\Util\Platform;
|
||||||
|
use Composer\Util\ProcessExecutor;
|
||||||
|
use Symfony\Component\Process\ExecutableFinder;
|
||||||
|
|
||||||
|
class HhvmDetectorTest extends TestCase
|
||||||
|
{
|
||||||
|
private $hhvmDetector;
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
$this->hhvmDetector = new HhvmDetector();
|
||||||
|
$this->hhvmDetector->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHHVMVersionWhenExecutingInHHVM() {
|
||||||
|
if (!defined('HHVM_VERSION_ID')) {
|
||||||
|
self::markTestSkipped('Not running with HHVM');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$version = $this->hhvmDetector->getVersion();
|
||||||
|
self::assertSame(self::versionIdToVersion(), $version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testHHVMVersionWhenExecutingInPHP() {
|
||||||
|
if (defined('HHVM_VERSION_ID')) {
|
||||||
|
self::markTestSkipped('Running with HHVM');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (PHP_VERSION_ID < 50400) {
|
||||||
|
self::markTestSkipped('Test only works on PHP 5.4+');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Platform::isWindows()) {
|
||||||
|
self::markTestSkipped('Test does not run on Windows');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$finder = new ExecutableFinder();
|
||||||
|
$hhvm = $finder->find('hhvm');
|
||||||
|
if ($hhvm === null) {
|
||||||
|
self::markTestSkipped('HHVM is not installed');
|
||||||
|
}
|
||||||
|
|
||||||
|
$detectedVersion = $this->hhvmDetector->getVersion();
|
||||||
|
self::assertNotNull($detectedVersion, 'Failed to detect HHVM version');
|
||||||
|
|
||||||
|
$process = new ProcessExecutor();
|
||||||
|
$exitCode = $process->execute(
|
||||||
|
ProcessExecutor::escape($hhvm).
|
||||||
|
' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
|
||||||
|
$version
|
||||||
|
);
|
||||||
|
self::assertSame(0, $exitCode);
|
||||||
|
|
||||||
|
self::assertSame(self::getVersionParser()->normalize($version), self::getVersionParser()->normalize($detectedVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @runInSeparateProcess */
|
||||||
|
public function testHHVMVersionWhenRunningInHHVMWithMockedConstant()
|
||||||
|
{
|
||||||
|
if (!defined('HHVM_VERSION_ID')) {
|
||||||
|
define('HHVM_VERSION', '2.2.1');
|
||||||
|
define('HHVM_VERSION_ID', 20201);
|
||||||
|
}
|
||||||
|
$version = $this->hhvmDetector->getVersion();
|
||||||
|
self::assertSame(self::getVersionParser()->normalize(self::versionIdToVersion()), self::getVersionParser()->normalize($version));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function versionIdToVersion()
|
||||||
|
{
|
||||||
|
if (!defined('HHVM_VERSION_ID')) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sprintf(
|
||||||
|
'%d.%d.%d',
|
||||||
|
HHVM_VERSION_ID / 10000,
|
||||||
|
(HHVM_VERSION_ID / 100) % 100,
|
||||||
|
HHVM_VERSION_ID % 100
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,131 @@
|
|||||||
|
<?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\Util;
|
||||||
|
|
||||||
|
use Composer\Util\Version;
|
||||||
|
use Composer\Test\TestCase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Lars Strojny <lars@strojny.net>
|
||||||
|
*/
|
||||||
|
class VersionTest extends TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create normalized test data set
|
||||||
|
*
|
||||||
|
* 1) Clone OpenSSL repository
|
||||||
|
* 2) git log --pretty=%h --all -- crypto/opensslv.h include/openssl/opensslv.h | while read hash ; do (git show $hash:crypto/opensslv.h; git show $hash:include/openssl/opensslv.h) | grep "define OPENSSL_VERSION_TEXT" ; done > versions.txt
|
||||||
|
* 3) cat versions.txt | awk -F "OpenSSL " '{print $2}' | awk -F " " '{print $1}' | sed -e "s:\([0-9]*\.[0-9]*\.[0-9]*\):1.2.3:g" -e "s:1\.2\.3[a-z]\(-.*\)\{0,1\}$:1.2.3a\1:g" -e "s:1\.2\.3[a-z]\{2\}\(-.*\)\{0,1\}$:1.2.3zh\1:g" -e "s:beta[0-9]:beta3:g" -e "s:pre[0-9]*:pre2:g" | sort | uniq
|
||||||
|
*/
|
||||||
|
public static function getOpenSslVersions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
// Generated
|
||||||
|
array('1.2.3', '1.2.3.0'),
|
||||||
|
array('1.2.3-beta3', '1.2.3.0-beta3'),
|
||||||
|
array('1.2.3-beta3-dev', '1.2.3.0-beta3-dev'),
|
||||||
|
array('1.2.3-beta3-fips', '1.2.3.0-beta3', true),
|
||||||
|
array('1.2.3-beta3-fips-dev', '1.2.3.0-beta3-dev', true),
|
||||||
|
array('1.2.3-dev', '1.2.3.0-dev'),
|
||||||
|
array('1.2.3-fips', '1.2.3.0', true),
|
||||||
|
array('1.2.3-fips-beta3', '1.2.3.0-beta3', true),
|
||||||
|
array('1.2.3-fips-beta3-dev', '1.2.3.0-beta3-dev', true),
|
||||||
|
array('1.2.3-fips-dev', '1.2.3.0-dev', true),
|
||||||
|
array('1.2.3-pre2', '1.2.3.0-alpha2'),
|
||||||
|
array('1.2.3-pre2-dev', '1.2.3.0-alpha2-dev'),
|
||||||
|
array('1.2.3-pre2-fips', '1.2.3.0-alpha2', true),
|
||||||
|
array('1.2.3-pre2-fips-dev', '1.2.3.0-alpha2-dev', true),
|
||||||
|
array('1.2.3a', '1.2.3.1'),
|
||||||
|
array('1.2.3a-beta3','1.2.3.1-beta3'),
|
||||||
|
array('1.2.3a-beta3-dev', '1.2.3.1-beta3-dev'),
|
||||||
|
array('1.2.3a-dev', '1.2.3.1-dev'),
|
||||||
|
array('1.2.3a-dev-fips', '1.2.3.1-dev', true),
|
||||||
|
array('1.2.3a-fips', '1.2.3.1', true),
|
||||||
|
array('1.2.3a-fips-beta3', '1.2.3.1-beta3', true),
|
||||||
|
array('1.2.3a-fips-dev', '1.2.3.1-dev', true),
|
||||||
|
array('1.2.3beta3', '1.2.3.0-beta3'),
|
||||||
|
array('1.2.3beta3-dev', '1.2.3.0-beta3-dev'),
|
||||||
|
array('1.2.3zh', '1.2.3.34'),
|
||||||
|
array('1.2.3zh-dev', '1.2.3.34-dev'),
|
||||||
|
array('1.2.3zh-fips', '1.2.3.34',true),
|
||||||
|
array('1.2.3zh-fips-dev', '1.2.3.34-dev', true),
|
||||||
|
// Additional cases
|
||||||
|
array('1.2.3zh-fips-rc3', '1.2.3.34-rc3', true, '1.2.3.34-RC3'),
|
||||||
|
array('1.2.3zh-alpha10-fips', '1.2.3.34-alpha10', true),
|
||||||
|
// Check that alphabetical patch levels overflow correctly
|
||||||
|
array('1.2.3', '1.2.3.0'),
|
||||||
|
array('1.2.3a', '1.2.3.1'),
|
||||||
|
array('1.2.3z', '1.2.3.26'),
|
||||||
|
array('1.2.3za', '1.2.3.27'),
|
||||||
|
array('1.2.3zy', '1.2.3.51'),
|
||||||
|
array('1.2.3zz', '1.2.3.52'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getOpenSslVersions
|
||||||
|
* @param string $input
|
||||||
|
* @param string $parsedVersion
|
||||||
|
* @param bool $fipsExpected
|
||||||
|
* @param string|null $normalizedVersion
|
||||||
|
*/
|
||||||
|
public function testParseOpensslVersions($input, $parsedVersion, $fipsExpected = false, $normalizedVersion = null)
|
||||||
|
{
|
||||||
|
self::assertSame($parsedVersion, Version::parseOpenssl($input, $isFips));
|
||||||
|
self::assertSame($fipsExpected, $isFips);
|
||||||
|
|
||||||
|
$normalizedVersion = $normalizedVersion ? $normalizedVersion : $parsedVersion;
|
||||||
|
self::assertSame($normalizedVersion, $this->getVersionParser()->normalize($parsedVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLibJpegVersions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('9', '9.0'),
|
||||||
|
array('9a', '9.1'),
|
||||||
|
array('9b', '9.2'),
|
||||||
|
// Never seen in the wild, just for overflow correctness
|
||||||
|
array('9za', '9.27'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getLibJpegVersions
|
||||||
|
* @param string $input
|
||||||
|
* @param string $parsedVersion
|
||||||
|
*/
|
||||||
|
public function testParseLibjpegVersion($input, $parsedVersion)
|
||||||
|
{
|
||||||
|
self::assertSame($parsedVersion, Version::parseLibjpeg($input));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getZoneinfoVersions()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('2019c', '2019.3'),
|
||||||
|
array('2020a', '2020.1'),
|
||||||
|
// Never happened so far but fixate overflow behavior
|
||||||
|
array('2020za', '2020.27'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider getZoneinfoVersions
|
||||||
|
* @param string $input
|
||||||
|
* @param string $parsedVersion
|
||||||
|
*/
|
||||||
|
public function testParseZoneinfoVersion($input, $parsedVersion)
|
||||||
|
{
|
||||||
|
self::assertSame($parsedVersion, Version::parseZoneinfoVersion($input));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue