PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/Composer/Repository/PlatformRepository.php

http://github.com/composer/composer
PHP | 365 lines | 256 code | 59 blank | 50 comment | 33 complexity | d23bf92607b1a916c137b5994ef34eb9 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Repository;
  12. use Composer\Composer;
  13. use Composer\Package\CompletePackage;
  14. use Composer\Package\PackageInterface;
  15. use Composer\Package\Version\VersionParser;
  16. use Composer\Plugin\PluginInterface;
  17. use Composer\Util\ProcessExecutor;
  18. use Composer\Util\Silencer;
  19. use Composer\Util\Platform;
  20. use Composer\XdebugHandler\XdebugHandler;
  21. use Symfony\Component\Process\ExecutableFinder;
  22. /**
  23. * @author Jordi Boggiano <j.boggiano@seld.be>
  24. */
  25. class PlatformRepository extends ArrayRepository
  26. {
  27. const PLATFORM_PACKAGE_REGEX = '{^(?:php(?:-64bit|-ipv6|-zts|-debug)?|hhvm|(?:ext|lib)-[a-z0-9](?:[_.-]?[a-z0-9]+)*|composer-(?:plugin|runtime)-api)$}iD';
  28. private $versionParser;
  29. /**
  30. * Defines overrides so that the platform can be mocked
  31. *
  32. * Should be an array of package name => version number mappings
  33. *
  34. * @var array
  35. */
  36. private $overrides = array();
  37. private $process;
  38. public function __construct(array $packages = array(), array $overrides = array(), ProcessExecutor $process = null)
  39. {
  40. $this->process = $process === null ? (new ProcessExecutor()) : $process;
  41. foreach ($overrides as $name => $version) {
  42. $this->overrides[strtolower($name)] = array('name' => $name, 'version' => $version);
  43. }
  44. parent::__construct($packages);
  45. }
  46. public function getRepoName()
  47. {
  48. return 'platform repo';
  49. }
  50. protected function initialize()
  51. {
  52. parent::initialize();
  53. $this->versionParser = new VersionParser();
  54. // Add each of the override versions as options.
  55. // Later we might even replace the extensions instead.
  56. foreach ($this->overrides as $override) {
  57. // Check that it's a platform package.
  58. if (!preg_match(self::PLATFORM_PACKAGE_REGEX, $override['name'])) {
  59. throw new \InvalidArgumentException('Invalid platform package name in config.platform: '.$override['name']);
  60. }
  61. $this->addOverriddenPackage($override);
  62. }
  63. $prettyVersion = PluginInterface::PLUGIN_API_VERSION;
  64. $version = $this->versionParser->normalize($prettyVersion);
  65. $composerPluginApi = new CompletePackage('composer-plugin-api', $version, $prettyVersion);
  66. $composerPluginApi->setDescription('The Composer Plugin API');
  67. $this->addPackage($composerPluginApi);
  68. $prettyVersion = Composer::RUNTIME_API_VERSION;
  69. $version = $this->versionParser->normalize($prettyVersion);
  70. $composerRuntimeApi = new CompletePackage('composer-runtime-api', $version, $prettyVersion);
  71. $composerRuntimeApi->setDescription('The Composer Runtime API');
  72. $this->addPackage($composerRuntimeApi);
  73. try {
  74. $prettyVersion = PHP_VERSION;
  75. $version = $this->versionParser->normalize($prettyVersion);
  76. } catch (\UnexpectedValueException $e) {
  77. $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', PHP_VERSION);
  78. $version = $this->versionParser->normalize($prettyVersion);
  79. }
  80. $php = new CompletePackage('php', $version, $prettyVersion);
  81. $php->setDescription('The PHP interpreter');
  82. $this->addPackage($php);
  83. if (PHP_DEBUG) {
  84. $phpdebug = new CompletePackage('php-debug', $version, $prettyVersion);
  85. $phpdebug->setDescription('The PHP interpreter, with debugging symbols');
  86. $this->addPackage($phpdebug);
  87. }
  88. if (defined('PHP_ZTS') && PHP_ZTS) {
  89. $phpzts = new CompletePackage('php-zts', $version, $prettyVersion);
  90. $phpzts->setDescription('The PHP interpreter, with Zend Thread Safety');
  91. $this->addPackage($phpzts);
  92. }
  93. if (PHP_INT_SIZE === 8) {
  94. $php64 = new CompletePackage('php-64bit', $version, $prettyVersion);
  95. $php64->setDescription('The PHP interpreter, 64bit');
  96. $this->addPackage($php64);
  97. }
  98. // The AF_INET6 constant is only defined if ext-sockets is available but
  99. // IPv6 support might still be available.
  100. if (defined('AF_INET6') || Silencer::call('inet_pton', '::') !== false) {
  101. $phpIpv6 = new CompletePackage('php-ipv6', $version, $prettyVersion);
  102. $phpIpv6->setDescription('The PHP interpreter, with IPv6 support');
  103. $this->addPackage($phpIpv6);
  104. }
  105. $loadedExtensions = get_loaded_extensions();
  106. // Extensions scanning
  107. foreach ($loadedExtensions as $name) {
  108. if (in_array($name, array('standard', 'Core'))) {
  109. continue;
  110. }
  111. $reflExt = new \ReflectionExtension($name);
  112. $prettyVersion = $reflExt->getVersion();
  113. $this->addExtension($name, $prettyVersion);
  114. }
  115. // Check for Xdebug in a restarted process
  116. if (!in_array('xdebug', $loadedExtensions, true) && ($prettyVersion = XdebugHandler::getSkippedVersion())) {
  117. $this->addExtension('xdebug', $prettyVersion);
  118. }
  119. // Another quick loop, just for possible libraries
  120. // Doing it this way to know that functions or constants exist before
  121. // relying on them.
  122. foreach ($loadedExtensions as $name) {
  123. $prettyVersion = null;
  124. $description = 'The '.$name.' PHP library';
  125. switch ($name) {
  126. case 'curl':
  127. $curlVersion = curl_version();
  128. $prettyVersion = $curlVersion['version'];
  129. break;
  130. case 'iconv':
  131. $prettyVersion = ICONV_VERSION;
  132. break;
  133. case 'intl':
  134. $name = 'ICU';
  135. if (defined('INTL_ICU_VERSION')) {
  136. $prettyVersion = INTL_ICU_VERSION;
  137. } else {
  138. $reflector = new \ReflectionExtension('intl');
  139. ob_start();
  140. $reflector->info();
  141. $output = ob_get_clean();
  142. preg_match('/^ICU version => (.*)$/m', $output, $matches);
  143. $prettyVersion = $matches[1];
  144. }
  145. break;
  146. case 'imagick':
  147. $imagick = new \Imagick();
  148. $imageMagickVersion = $imagick->getVersion();
  149. // 6.x: ImageMagick 6.2.9 08/24/06 Q16 http://www.imagemagick.org
  150. // 7.x: ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org
  151. preg_match('/^ImageMagick ([\d.]+)(?:-(\d+))?/', $imageMagickVersion['versionString'], $matches);
  152. if (isset($matches[2])) {
  153. $prettyVersion = "{$matches[1]}.{$matches[2]}";
  154. } else {
  155. $prettyVersion = $matches[1];
  156. }
  157. break;
  158. case 'libxml':
  159. $prettyVersion = LIBXML_DOTTED_VERSION;
  160. break;
  161. case 'openssl':
  162. $prettyVersion = preg_replace_callback('{^(?:OpenSSL|LibreSSL)?\s*([0-9.]+)([a-z]*).*}i', function ($match) {
  163. if (empty($match[2])) {
  164. return $match[1];
  165. }
  166. // OpenSSL versions add another letter when they reach Z.
  167. // e.g. OpenSSL 0.9.8zh 3 Dec 2015
  168. if (!preg_match('{^z*[a-z]$}', $match[2])) {
  169. // 0.9.8abc is garbage
  170. return 0;
  171. }
  172. $len = strlen($match[2]);
  173. $patchVersion = ($len - 1) * 26; // All Z
  174. $patchVersion += ord($match[2][$len - 1]) - 96;
  175. return $match[1].'.'.$patchVersion;
  176. }, OPENSSL_VERSION_TEXT);
  177. $description = OPENSSL_VERSION_TEXT;
  178. break;
  179. case 'pcre':
  180. $prettyVersion = preg_replace('{^(\S+).*}', '$1', PCRE_VERSION);
  181. break;
  182. case 'uuid':
  183. $prettyVersion = phpversion('uuid');
  184. break;
  185. case 'xsl':
  186. $prettyVersion = LIBXSLT_DOTTED_VERSION;
  187. break;
  188. case 'zip':
  189. if (defined('ZipArchive::LIBZIP_VERSION')) {
  190. $prettyVersion = \ZipArchive::LIBZIP_VERSION;
  191. } else {
  192. continue 2;
  193. }
  194. default:
  195. // None handled extensions have no special cases, skip
  196. continue 2;
  197. }
  198. try {
  199. $version = $this->versionParser->normalize($prettyVersion);
  200. } catch (\UnexpectedValueException $e) {
  201. continue;
  202. }
  203. $lib = new CompletePackage('lib-'.$name, $version, $prettyVersion);
  204. $lib->setDescription($description);
  205. $this->addPackage($lib);
  206. }
  207. $hhvmVersion = defined('HHVM_VERSION') ? HHVM_VERSION : null;
  208. if ($hhvmVersion === null && !Platform::isWindows()) {
  209. $finder = new ExecutableFinder();
  210. $hhvm = $finder->find('hhvm');
  211. if ($hhvm !== null) {
  212. $exitCode = $this->process->execute(
  213. ProcessExecutor::escape($hhvm).
  214. ' --php -d hhvm.jit=0 -r "echo HHVM_VERSION;" 2>/dev/null',
  215. $hhvmVersion
  216. );
  217. if ($exitCode !== 0) {
  218. $hhvmVersion = null;
  219. }
  220. }
  221. }
  222. if ($hhvmVersion) {
  223. try {
  224. $prettyVersion = $hhvmVersion;
  225. $version = $this->versionParser->normalize($prettyVersion);
  226. } catch (\UnexpectedValueException $e) {
  227. $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', $hhvmVersion);
  228. $version = $this->versionParser->normalize($prettyVersion);
  229. }
  230. $hhvm = new CompletePackage('hhvm', $version, $prettyVersion);
  231. $hhvm->setDescription('The HHVM Runtime (64bit)');
  232. $this->addPackage($hhvm);
  233. }
  234. }
  235. /**
  236. * {@inheritDoc}
  237. */
  238. public function addPackage(PackageInterface $package)
  239. {
  240. // Skip if overridden
  241. if (isset($this->overrides[$package->getName()])) {
  242. $overrider = $this->findPackage($package->getName(), '*');
  243. if ($package->getVersion() === $overrider->getVersion()) {
  244. $actualText = 'same as actual';
  245. } else {
  246. $actualText = 'actual: '.$package->getPrettyVersion();
  247. }
  248. $overrider->setDescription($overrider->getDescription().', '.$actualText);
  249. return;
  250. }
  251. // Skip if PHP is overridden and we are adding a php-* package
  252. if (isset($this->overrides['php']) && 0 === strpos($package->getName(), 'php-')) {
  253. $overrider = $this->addOverriddenPackage($this->overrides['php'], $package->getPrettyName());
  254. if ($package->getVersion() === $overrider->getVersion()) {
  255. $actualText = 'same as actual';
  256. } else {
  257. $actualText = 'actual: '.$package->getPrettyVersion();
  258. }
  259. $overrider->setDescription($overrider->getDescription().', '.$actualText);
  260. return;
  261. }
  262. parent::addPackage($package);
  263. }
  264. private function addOverriddenPackage(array $override, $name = null)
  265. {
  266. $version = $this->versionParser->normalize($override['version']);
  267. $package = new CompletePackage($name ?: $override['name'], $version, $override['version']);
  268. $package->setDescription('Package overridden via config.platform');
  269. $package->setExtra(array('config.platform' => true));
  270. parent::addPackage($package);
  271. return $package;
  272. }
  273. /**
  274. * Parses the version and adds a new package to the repository
  275. *
  276. * @param string $name
  277. * @param null|string $prettyVersion
  278. */
  279. private function addExtension($name, $prettyVersion)
  280. {
  281. $extraDescription = null;
  282. try {
  283. $version = $this->versionParser->normalize($prettyVersion);
  284. } catch (\UnexpectedValueException $e) {
  285. $extraDescription = ' (actual version: '.$prettyVersion.')';
  286. if (preg_match('{^(\d+\.\d+\.\d+(?:\.\d+)?)}', $prettyVersion, $match)) {
  287. $prettyVersion = $match[1];
  288. } else {
  289. $prettyVersion = '0';
  290. }
  291. $version = $this->versionParser->normalize($prettyVersion);
  292. }
  293. $packageName = $this->buildPackageName($name);
  294. $ext = new CompletePackage($packageName, $version, $prettyVersion);
  295. $ext->setDescription('The '.$name.' PHP extension'.$extraDescription);
  296. $this->addPackage($ext);
  297. }
  298. /**
  299. * @param string $name
  300. * @return string
  301. */
  302. private function buildPackageName($name)
  303. {
  304. return 'ext-' . str_replace(' ', '-', $name);
  305. }
  306. }