/** * Checks PHP version * * @return array */ public function checkPhpVersion() { try { $requiredVersion = $this->composerInformation->getRequiredPhpVersion(); } catch (\Exception $e) { return [ 'responseType' => ResponseTypeInterface::RESPONSE_TYPE_ERROR, 'data' => [ 'error' => 'phpVersionError', 'message' => 'Cannot determine required PHP version: ' . $e->getMessage() ], ]; } $multipleConstraints = $this->versionParser->parseConstraints($requiredVersion); try { $normalizedPhpVersion = $this->versionParser->normalize(PHP_VERSION); } catch (\UnexpectedValueException $e) { $prettyVersion = preg_replace('#^([^~+-]+).*$#', '$1', PHP_VERSION); $normalizedPhpVersion = $this->versionParser->normalize($prettyVersion); } $currentPhpVersion = $this->versionParser->parseConstraints($normalizedPhpVersion); $responseType = ResponseTypeInterface::RESPONSE_TYPE_SUCCESS; if (!$multipleConstraints->matches($currentPhpVersion)) { $responseType = ResponseTypeInterface::RESPONSE_TYPE_ERROR; } return [ 'responseType' => $responseType, 'data' => [ 'required' => $requiredVersion, 'current' => PHP_VERSION, ], ]; }
/** * {@inheritdoc} */ protected function execute(InputInterface $input, OutputInterface $output) { if (!$this->deploymentConfig->isAvailable()) { $output->writeln("<info>No information is available: the Magento application is not installed.</info>"); return; } /** @var DbVersionInfo $dbVersionInfo */ $dbVersionInfo = $this->objectManagerProvider->get()->get('Magento\\Framework\\Module\\DbVersionInfo'); $outdated = $dbVersionInfo->getDbVersionErrors(); if (!empty($outdated)) { $output->writeln("<info>The module code base doesn't match the DB schema and data.</info>"); $versionParser = new VersionParser(); $codebaseUpdateNeeded = false; foreach ($outdated as $row) { if (!$codebaseUpdateNeeded && $row[DbVersionInfo::KEY_CURRENT] !== 'none') { // check if module code base update is needed $currentVersion = $versionParser->parseConstraints($row[DbVersionInfo::KEY_CURRENT]); $requiredVersion = $versionParser->parseConstraints('>' . $row[DbVersionInfo::KEY_REQUIRED]); if ($requiredVersion->matches($currentVersion)) { $codebaseUpdateNeeded = true; } } $output->writeln(sprintf("<info>%20s %10s: %11s -> %-11s</info>", $row[DbVersionInfo::KEY_MODULE], $row[DbVersionInfo::KEY_TYPE], $row[DbVersionInfo::KEY_CURRENT], $row[DbVersionInfo::KEY_REQUIRED])); } if ($codebaseUpdateNeeded) { $output->writeln('<info>Some modules use code versions newer or older than the database. ' . "First update the module code, then run 'setup:upgrade'.</info>"); // we must have an exit code higher than zero to indicate something was wrong return \Magento\Framework\Console\Cli::RETURN_FAILURE; } else { $output->writeln("<info>Run 'setup:upgrade' to update your DB schema and data.</info>"); } } else { $output->writeln('<info>All modules are up to date.</info>'); } }
/** * Return true if $version matches $constraint (expressed as a Composer constraint string) * * @param string $version * @param string $constraint * @return bool */ public function isVersionMatching($version, $constraint) { $versionParser = new VersionParser(); $normalizedVersion = $versionParser->normalize($version); $versionAsContraint = $versionParser->parseConstraints($normalizedVersion); $linkConstraint = $versionParser->parseConstraints($constraint); return $linkConstraint->matches($versionAsContraint); }
protected function selectPackage(IOInterface $io, $packageName, $version = null) { $io->writeError('<info>Searching for the specified package.</info>'); if ($composer = $this->getComposer(false)) { $localRepo = $composer->getRepositoryManager()->getLocalRepository(); $repos = new CompositeRepository(array_merge(array($localRepo), $composer->getRepositoryManager()->getRepositories())); } else { $defaultRepos = Factory::createDefaultRepositories($this->getIO()); $io->writeError('No composer.json found in the current directory, searching packages from ' . implode(', ', array_keys($defaultRepos))); $repos = new CompositeRepository($defaultRepos); } $pool = new Pool(); $pool->addRepository($repos); $parser = new VersionParser(); $constraint = $version ? $parser->parseConstraints($version) : null; $packages = $pool->whatProvides($packageName, $constraint, true); if (count($packages) > 1) { $package = reset($packages); $io->writeError('<info>Found multiple matches, selected ' . $package->getPrettyString() . '.</info>'); $io->writeError('Alternatives were ' . implode(', ', array_map(function ($p) { return $p->getPrettyString(); }, $packages)) . '.'); $io->writeError('<comment>Please use a more specific constraint to pick a different package.</comment>'); } elseif ($packages) { $package = reset($packages); $io->writeError('<info>Found an exact match ' . $package->getPrettyString() . '.</info>'); } else { $io->writeError('<error>Could not find a package matching ' . $packageName . '.</error>'); return false; } return $package; }
/** * finds a package by name and version if provided * * @param RepositoryInterface $installedRepo * @param RepositoryInterface $repos * @param string $name * @param ConstraintInterface|string $version * @throws \InvalidArgumentException * @return array array(CompletePackageInterface, array of versions) */ protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null) { $name = strtolower($name); $constraint = is_string($version) ? $this->versionParser->parseConstraints($version) : $version; $policy = new DefaultPolicy(); $pool = new Pool('dev'); $pool->addRepository($repos); $matchedPackage = null; $versions = array(); $matches = $pool->whatProvides($name, $constraint); foreach ($matches as $index => $package) { // skip providers/replacers if ($package->getName() !== $name) { unset($matches[$index]); continue; } // select an exact match if it is in the installed repo and no specific version was required if (null === $version && $installedRepo->hasPackage($package)) { $matchedPackage = $package; } $versions[$package->getPrettyVersion()] = $package->getVersion(); $matches[$index] = $package->getId(); } // select preferred package according to policy rules if (!$matchedPackage && $matches && ($preferred = $policy->selectPreferredPackages($pool, array(), $matches))) { $matchedPackage = $pool->literalToPackage($preferred[0]); } return array($matchedPackage, $versions); }
/** * Verifies php version * * @return JsonModel */ public function phpVersionAction() { try { $requiredVersion = $this->phpInformation->getRequiredPhpVersion(); } catch (\Exception $e) { return new JsonModel(['responseType' => ResponseTypeInterface::RESPONSE_TYPE_ERROR, 'data' => ['error' => 'phpVersionError', 'message' => 'Cannot determine required PHP version: ' . $e->getMessage()]]); } $multipleConstraints = $this->versionParser->parseConstraints($requiredVersion); $currentPhpVersion = new VersionConstraint('=', PHP_VERSION); $responseType = ResponseTypeInterface::RESPONSE_TYPE_SUCCESS; if (!$multipleConstraints->matches($currentPhpVersion)) { $responseType = ResponseTypeInterface::RESPONSE_TYPE_ERROR; } $data = ['responseType' => $responseType, 'data' => ['required' => $requiredVersion, 'current' => PHP_VERSION]]; return new JsonModel($data); }
public function parse_basic_syntax($args) { $version_parser = new VersionParser(); $requires = array(); foreach ($args as $requirement) { $name_length = strcspn($requirement, '>=|~'); if (strlen($requirement) === $name_length) { // No version constraint specified, use wildcard $dependency_name = $requirement; $constraint_text = '*'; } else { $dependency_name = substr($requirement, 0, $name_length); $constraint_text = substr($requirement, $name_length); // If the constraint is exactly '=', trim it for // Composer syntax compatibility if (strlen($constraint_text) >= 2 && $constraint_text[0] === '=' && is_numeric($constraint_text[1])) { $constraint_text = substr($constraint_text, 1); } } if (strpos($dependency_name, '/') === false) { $dependency_name = 'wpackagist-plugin/' . $dependency_name; } $constraint = $version_parser->parseConstraints($constraint_text); $requires[] = new Link($this->plugin_name, $dependency_name, $constraint, 'requires', $constraint_text); } $this->setRequires($requires); }
private function updateCompatibility(Addon $addon, AddonVersion $version, CompletePackage $package) { $require = null; if ($package->getRequire()) { foreach ($package->getRequire() as $name => $link) { if ((string) $link == 'self.version') { continue; } if ($name == 'silverstripe/framework') { $require = $link; break; } if ($name == 'silverstripe/cms') { $require = $link; } } } if (!$require) { return; } $addon->CompatibleVersions()->removeAll(); $version->CompatibleVersions()->removeAll(); foreach ($this->silverstripes as $id => $link) { try { $constraint = $this->versionParser->parseConstraints($require); if ($link->matches($constraint)) { $addon->CompatibleVersions()->add($id); $version->CompatibleVersions()->add($id); } } catch (Exception $e) { // An exception here shouldn't prevent further updates. Debug::log($addon->Name . "\t" . $addon->ID . "\t" . $e->getMessage()); } } }
/** * Search for a given package version. * * Usage examples : Composition::has('php', '5.3.*') // PHP version * Composition::has('ext-memcache') // PHP extension * Composition::has('vendor/package', '>2.1') // Package version * * @param type $packageName The package name * @param type $prettyString An optional version constraint * * @return boolean Wether or not the package has been found. */ public static function has($packageName, $prettyString = '*') { if (null === self::$pool) { if (null === self::$rootDir) { self::$rootDir = getcwd(); if (!file_exists(self::$rootDir . '/composer.json')) { throw new \RuntimeException('Unable to guess the project root dir, please specify it manually using the Composition::setRootDir method.'); } } $minimumStability = 'dev'; $config = new Config(); $file = new JsonFile(self::$rootDir . '/composer.json'); if ($file->exists()) { $projectConfig = $file->read(); $config->merge($projectConfig); if (isset($projectConfig['minimum-stability'])) { $minimumStability = $projectConfig['minimum-stability']; } } $vendorDir = self::$rootDir . '/' . $config->get('vendor-dir'); $pool = new Pool($minimumStability); $pool->addRepository(new PlatformRepository()); $pool->addRepository(new InstalledFilesystemRepository(new JsonFile($vendorDir . '/composer/installed.json'))); $pool->addRepository(new InstalledFilesystemRepository(new JsonFile($vendorDir . '/composer/installed_dev.json'))); self::$pool = $pool; } $parser = new VersionParser(); $constraint = $parser->parseConstraints($prettyString); $packages = self::$pool->whatProvides($packageName, $constraint); return empty($packages) ? false : true; }
protected function prepare() { if (Type::determinePickle($this->path, $matches) < 1) { throw new \Exception('Not a pickle git URI'); } $this->name = $matches['package']; $extension = $this->fetchPackageJson(); $versionParser = new VersionParser(); if ($matches['version'] == '') { $versions = array_keys($extension['packages'][$this->name]); if (count($versions) > 1) { $versionToUse = $versions[1]; } else { $versionToUse = $versions[0]; } } else { $versionConstraints = $versionParser->parseConstraints($matches['version']); /* versions are sorted decreasing */ foreach ($extension['packages'][$this->name] as $version => $release) { $constraint = new VersionConstraint('=', $versionParser->normalize($version)); if ($versionConstraints->matches($constraint)) { $versionToUse = $version; break; } } } $package = $extension['packages'][$this->name][$versionToUse]; $this->version = $versionToUse; $this->normalizedVersion = $versionParser->normalize($versionToUse); $this->name = $matches['package']; $this->prettyVersion = $this->version; $this->url = $package['source']['url']; $this->reference = $package['source']['reference']; $this->type = $package['source']['type']; }
/** * Get the link constraint of normalized version. * * @param string $normalizedVersion The normalized version * * @return LinkConstraintInterface The constraint */ protected function getVersionConstraint($normalizedVersion) { if (preg_match('/^\d+(\.\d+)(\.\d+)(\.\d+)\-[A-Za-z0-9]+$/', $normalizedVersion)) { $normalizedVersion = substr($normalizedVersion, 0, strpos($normalizedVersion, '-')); } return $this->versionParser->parseConstraints($normalizedVersion); }
/** * Add a requirement to the root package. * * @param array $requires * @param OptionalPackage $package * @return array */ private function addRootPackageRequirement(array $requires, OptionalPackage $package) { $name = $package->getName(); $constraint = $package->getConstraint(); $description = $package->isDev() ? 'requires for development' : 'requires'; $requires[$name] = new Link('__root__', $name, $this->versionParser->parseConstraints($constraint), $description, $constraint); return $requires; }
protected function execute(InputInterface $input, OutputInterface $output) { $file = Factory::getComposerFile(); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { $output->writeln('<error>' . $file . ' could not be created.</error>'); return 1; } if (!is_readable($file)) { $output->writeln('<error>' . $file . ' is not readable.</error>'); return 1; } if (!is_writable($file)) { $output->writeln('<error>' . $file . ' is not writable.</error>'); return 1; } $json = new JsonFile($file); $composer = $json->read(); $composerBackup = file_get_contents($json->getPath()); $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages')); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; $baseRequirements = array_key_exists($requireKey, $composer) ? $composer[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); // validate requirements format $versionParser = new VersionParser(); foreach ($requirements as $constraint) { $versionParser->parseConstraints($constraint); } if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; if (isset($composer[$removeKey][$package])) { unset($composer[$removeKey][$package]); } } $composer[$requireKey] = $baseRequirements; $json->write($composer); } $output->writeln('<info>' . $file . ' has been updated</info>'); if ($input->getOption('no-update')) { return 0; } $updateDevMode = !$input->getOption('update-no-dev'); // Update packages $composer = $this->getComposer(); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $io = $this->getIO(); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $install = Installer::create($io, $composer); $install->setVerbose($input->getOption('verbose'))->setPreferSource($input->getOption('prefer-source'))->setPreferDist($input->getOption('prefer-dist'))->setDevMode($updateDevMode)->setUpdate(true)->setUpdateWhitelist(array_keys($requirements))->setWhitelistDependencies($input->getOption('update-with-dependencies')); $status = $install->run(); if ($status !== 0) { $output->writeln("\n" . '<error>Installation failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($json->getPath(), $composerBackup); } return $status; }
/** * Check if two modules are conflicted and get the message for display * * @param string $moduleA * @param string $moduleB * @return string[] */ private function getConflictMessages($moduleA, $moduleB) { $messages = []; $versionParser = new VersionParser(); if (isset($this->packageInfo->getConflict($moduleB)[$moduleA]) && $this->packageInfo->getConflict($moduleB)[$moduleA] && $this->packageInfo->getVersion($moduleA)) { $constraintA = $versionParser->parseConstraints($this->packageInfo->getConflict($moduleB)[$moduleA]); $constraintB = $versionParser->parseConstraints($this->packageInfo->getVersion($moduleA)); if ($constraintA->matches($constraintB)) { $messages[] = "{$moduleB} conflicts with current {$moduleA} version " . $this->packageInfo->getVersion($moduleA) . ' (version should not be ' . $this->packageInfo->getConflict($moduleB)[$moduleA] . ')'; } } if (isset($this->packageInfo->getConflict($moduleA)[$moduleB]) && $this->packageInfo->getConflict($moduleA)[$moduleB] && $this->packageInfo->getVersion($moduleB)) { $constraintA = $versionParser->parseConstraints($this->packageInfo->getConflict($moduleA)[$moduleB]); $constraintB = $versionParser->parseConstraints($this->packageInfo->getVersion($moduleB)); if ($constraintA->matches($constraintB)) { $messages[] = "{$moduleA} conflicts with current {$moduleB} version " . $this->packageInfo->getVersion($moduleA) . ' (version should not be ' . $this->packageInfo->getConflict($moduleA)[$moduleB] . ')'; } } return $messages; }
/** * Require (install) a package. * * @param array $package Package names and version to require * - Format: ['name' => '', 'version' => ''] * * @throws \Bolt\Exception\PackageManagerException * * @return int 0 on success or a positive error code on failure */ public function execute(array $package) { $this->getComposer(); $io = $this->getIO(); /** @var \Bolt\Filesystem\Handler\JsonFile $jsonFile */ $jsonFile = $this->getOptions()->composerJson(); $newlyCreated = !$jsonFile->exists(); if ($newlyCreated) { $this->app['extend.manager.json']->update(); } // Format the package array $package = $this->formatRequirements($package); // Validate requirements format $versionParser = new VersionParser(); foreach ($package as $constraint) { $versionParser->parseConstraints($constraint); } // Get a back up of the file contents $composerBackup = $jsonFile->parse(); // Update our JSON file now with a specific version. // This is what Composer will read, and use, internally during the process. // After that is complete, we'll re-save with a constraint $this->updateComposerJson($jsonFile, $package, false); // JSON file has been created/updated, if we're not installing, exit if ($this->getOptions()->noUpdate()) { return 0; } // Reload Composer config $composer = $this->resetComposer(); /** @var $install \Composer\Installer */ $install = Installer::create($io, $composer)->setVerbose($this->getOptions()->verbose())->setPreferSource($this->getOptions()->preferSource())->setPreferDist($this->getOptions()->preferDist())->setDevMode(!$this->getOptions()->updateNoDev())->setOptimizeAutoloader($this->getOptions()->optimizeAutoloader())->setClassMapAuthoritative($this->getOptions()->classmapAuthoritative())->setUpdate($this->getOptions()->update())->setUpdateWhitelist(array_keys($package))->setWhitelistDependencies($this->getOptions()->updateWithDependencies())->setIgnorePlatformRequirements($this->getOptions()->ignorePlatformReqs())->setRunScripts(!$this->getOptions()->noScripts()); try { $status = $install->run(); if ($status !== 0) { if ($newlyCreated) { // Installation failed, deleting JSON $jsonFile->delete(); } else { // Installation failed, reverting JSON to its original content $jsonFile->dump($composerBackup); } } // Update our JSON file now with a constraint $this->updateComposerJson($jsonFile, $package, true); return $status; } catch (\Exception $e) { // Installation failed, reverting JSON to its original content $jsonFile->dump($composerBackup); $msg = sprintf('%s recieved an error from Composer: %s in %s::%s', __METHOD__, $e->getMessage(), $e->getFile(), $e->getLine()); $this->app['logger.system']->critical($msg, ['event' => 'exception', 'exception' => $e]); throw new PackageManagerException($e->getMessage(), $e->getCode(), $e); } }
/** * Execute the command. * * @param InputInterface $input * @param OutputInterface $output * @param bool $inverted Whether to invert matching process (why-not vs why behaviour) * @return int|null Exit code of the operation. */ protected function doExecute(InputInterface $input, OutputInterface $output, $inverted = false) { // Emit command event on startup $composer = $this->getComposer(); $commandEvent = new CommandEvent(PluginEvents::COMMAND, $this->getName(), $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); // Prepare repositories and set up a pool $platformOverrides = $composer->getConfig()->get('platform') ?: array(); $repository = new CompositeRepository(array(new ArrayRepository(array($composer->getPackage())), $composer->getRepositoryManager()->getLocalRepository(), new PlatformRepository(array(), $platformOverrides))); $pool = new Pool(); $pool->addRepository($repository); // Parse package name and constraint list($needle, $textConstraint) = array_pad(explode(':', $input->getArgument(self::ARGUMENT_PACKAGE)), 2, $input->getArgument(self::ARGUMENT_CONSTRAINT)); // Find packages that are or provide the requested package first $packages = $pool->whatProvides($needle); if (empty($packages)) { throw new \InvalidArgumentException(sprintf('Could not find package "%s" in your project', $needle)); } // Include replaced packages for inverted lookups as they are then the actual starting point to consider $needles = array($needle); if ($inverted) { foreach ($packages as $package) { $needles = array_merge($needles, array_map(function (Link $link) { return $link->getTarget(); }, $package->getReplaces())); } } // Parse constraint if one was supplied if ('*' !== $textConstraint) { $versionParser = new VersionParser(); $constraint = $versionParser->parseConstraints($textConstraint); } else { $constraint = null; } // Parse rendering options $renderTree = $input->getOption(self::OPTION_TREE); $recursive = $renderTree || $input->getOption(self::OPTION_RECURSIVE); // Resolve dependencies $results = $repository->getDependents($needles, $constraint, $inverted, $recursive); if (empty($results)) { $extra = null !== $constraint ? sprintf(' in versions %smatching %s', $inverted ? 'not ' : '', $textConstraint) : ''; $this->getIO()->writeError(sprintf('<info>There is no installed package depending on "%s"%s</info>', $needle, $extra)); } elseif ($renderTree) { $this->initStyles($output); $root = $packages[0]; $this->getIO()->write(sprintf('<info>%s</info> %s %s', $root->getPrettyName(), $root->getPrettyVersion(), $root->getDescription())); $this->printTree($results); } else { $this->printTable($output, $results); } return 0; }
/** * {@inheritDoc} */ public function findPackages($name, $constraint = null) { // normalize name $name = strtolower($name); $packages = array(); if (null !== $constraint && !$constraint instanceof ConstraintInterface) { $versionParser = new VersionParser(); $constraint = $versionParser->parseConstraints($constraint); } foreach ($this->getPackages() as $package) { if ($name === $package->getName()) { $pkgConstraint = new Constraint('==', $package->getVersion()); if (null === $constraint || $constraint->matches($pkgConstraint)) { $packages[] = $package; } } } return $packages; }
/** * Checks if PHP version >= 5.6.0 and always_populate_raw_post_data is set to -1 * * Beginning PHP 7.0, support for 'always_populate_raw_post_data' is going to removed. * And beginning PHP 5.6, a deprecated message is displayed if 'always_populate_raw_post_data' * is set to a value other than -1. * * @return array */ private function checkPopulateRawPostSetting() { // HHVM and PHP 7does not support 'always_populate_raw_post_data' to be set to -1 if (version_compare(PHP_VERSION, '7.0.0-beta') >= 0 || defined('HHVM_VERSION')) { return []; } $data = []; $error = false; $iniSetting = intVal(ini_get('always_populate_raw_post_data')); $checkVersionConstraint = $this->versionParser->parseConstraints('~5.6.0'); $normalizedPhpVersion = $this->getNormalizedCurrentPhpVersion(PHP_VERSION); $currentVersion = $this->versionParser->parseConstraints($normalizedPhpVersion); if ($checkVersionConstraint->matches($currentVersion) && $iniSetting !== -1) { $error = true; } $message = sprintf('Your PHP Version is %s, but always_populate_raw_post_data = %d. $HTTP_RAW_POST_DATA is deprecated from PHP 5.6 onwards and will be removed in PHP 7.0. This will stop the installer from running. Please open your php.ini file and set always_populate_raw_post_data to -1. If you need more help please call your hosting provider.', PHP_VERSION, intVal(ini_get('always_populate_raw_post_data'))); $data['always_populate_raw_post_data'] = ['message' => $message, 'helpUrl' => 'http://php.net/manual/en/ini.core.php#ini.always-populate-settings-data', 'error' => $error]; return $data; }
/** * Finds a package by name and version if provided. * * @param RepositoryInterface $installedRepo * @param RepositoryInterface $repos * @param string $name * @param string|null $version * * @throws \InvalidArgumentException * * @return array [CompletePackageInterface, array of versions] */ protected function getPackage(RepositoryInterface $installedRepo, RepositoryInterface $repos, $name, $version = null) { $name = strtolower($name); $constraint = null; if ($version !== null) { $constraint = $this->versionParser->parseConstraints($version); } $policy = new DefaultPolicy(); $pool = new Pool('dev'); $pool->addRepository($repos); $matchedPackage = null; $versions = []; $matches = $pool->whatProvides($name, $constraint); foreach ($matches as $index => $package) { // Skip providers/replacers. if ($package->getName() !== $name) { unset($matches[$index]); continue; } // Select an exact match if it is in the installed repo and no specific version was required. if ($version === null && $installedRepo->hasPackage($package)) { $matchedPackage = $package; } $versions[$package->getPrettyVersion()] = $package->getVersion(); $matches[$index] = $package->getId(); } // Select prefered package according to policy rules. if (!$matchedPackage && !empty($matches) && ($prefered = $policy->selectPreferredPackages($pool, [], $matches))) { $matchedPackage = $pool->literalToPackage($prefered[0]); } // If we have package result, return them. if ($matchedPackage) { return [$matchedPackage->getName() => ['package' => $matchedPackage, 'versions' => $versions]]; } return null; }
/** * {@inheritDoc} */ public function findPackages($name, $constraint = null) { if (!$this->hasProviders()) { return parent::findPackages($name, $constraint); } // normalize name $name = strtolower($name); if (null !== $constraint && !$constraint instanceof ConstraintInterface) { $versionParser = new VersionParser(); $constraint = $versionParser->parseConstraints($constraint); } $packages = array(); foreach ($this->getProviderNames() as $providerName) { if ($name === $providerName) { $candidates = $this->whatProvides(new Pool('dev'), $providerName); foreach ($candidates as $package) { if ($name === $package->getName()) { $pkgConstraint = new Constraint('==', $package->getVersion()); if (null === $constraint || $constraint->matches($pkgConstraint)) { $packages[] = $package; } } } break; } } return $packages; }
/** * {@inheritdoc} * * @SuppressWarnings(PHPMD.LongVariable) */ public function handle(\Input $input) { $packageName = $input->get('solve'); $version = base64_decode(rawurldecode($input->get('version'))); if ($input->post('mark') || $input->post('install')) { // make a backup copy(TL_ROOT . '/' . $this->configPathname, TL_ROOT . '/' . $this->configPathname . '~'); // update requires $json = new JsonFile(TL_ROOT . '/' . $this->configPathname); $config = $json->read(); if (!array_key_exists('require', $config)) { $config['require'] = array(); } $config['require'][$packageName] = $version; $json->write($config); Messages::addInfo(sprintf($GLOBALS['TL_LANG']['composer_client']['added_candidate'], $packageName, $version)); $_SESSION['COMPOSER_OUTPUT'] .= $this->io->getOutput(); if ($input->post('install')) { $this->redirect('contao/main.php?do=composer&update=packages'); } $this->redirect('contao/main.php?do=composer'); } /** @var RootPackage $rootPackage */ $rootPackage = $this->composer->getPackage(); $installedRootPackage = clone $rootPackage; $installedRootPackage->setRequires(array()); $installedRootPackage->setDevRequires(array()); $repositoryManager = $this->getRepositoryManager(); $localRepository = $repositoryManager->getLocalRepository(); $platformRepo = new PlatformRepository(); $installedRepository = new CompositeRepository(array($localRepository, new InstalledArrayRepository(array($installedRootPackage)), $platformRepo)); $versionParser = new VersionParser(); $constraint = $versionParser->parseConstraints($version); $stability = $versionParser->parseStability($version); $aliases = $this->getRootAliases($rootPackage); $this->aliasPlatformPackages($platformRepo, $aliases); $stabilityFlags = $rootPackage->getStabilityFlags(); $stabilityFlags[$packageName] = BasePackage::$stabilities[$stability]; $pool = $this->getPool($rootPackage->getMinimumStability(), $stabilityFlags); $pool->addRepository($installedRepository, $aliases); $policy = new DefaultPolicy($rootPackage->getPreferStable()); $request = new Request($pool); // add root package $rootPackageConstraint = $this->createConstraint('=', $rootPackage->getVersion()); $rootPackageConstraint->setPrettyString($rootPackage->getPrettyVersion()); $request->install($rootPackage->getName(), $rootPackageConstraint); // add requirements $links = $rootPackage->getRequires(); /** @var Link $link */ foreach ($links as $link) { if ($link->getTarget() != $packageName) { $request->install($link->getTarget(), $link->getConstraint()); } } /** @var PackageInterface $package */ foreach ($installedRepository->getPackages() as $package) { $request->install($package->getName(), $this->createConstraint('=', $package->getVersion())); } $operations = array(); try { $solver = new Solver($policy, $pool, $installedRepository); $beforeOperations = $solver->solve($request); $request->install($packageName, $constraint); $operations = $solver->solve($request); /** @var \Composer\DependencyResolver\Operation\SolverOperation $beforeOperation */ foreach ($beforeOperations as $beforeOperation) { /** @var \Composer\DependencyResolver\Operation\InstallOperation $operation */ foreach ($operations as $index => $operation) { if ($operation->getPackage()->getName() != $packageName && $beforeOperation->__toString() == $operation->__toString()) { unset($operations[$index]); } } } } catch (SolverProblemsException $e) { Messages::addError(sprintf('<span style="white-space: pre-line">%s</span>', trim($e->getMessage()))); } $template = new \BackendTemplate('be_composer_client_solve'); $template->composer = $this->composer; $template->packageName = $packageName; $template->packageVersion = $version; $template->operations = $operations; return $template->parse(); }
/** * @dataProvider failingConstraints * @expectedException UnexpectedValueException */ public function testParseConstraintsFails($input) { $parser = new VersionParser(); $parser->parseConstraints($input); }
/** * @param Composer $composer * @param IOInterface $io */ public function activate(Composer $composer, IOInterface $io) { $repositoryManager = $composer->getRepositoryManager(); $extra = $composer->getPackage()->getExtra(); if (!isset($extra['connect-packages'])) { return; } $versionParser = new VersionParser(); $links = []; foreach ($extra['connect-packages'] as $connectPackage => $version) { try { $releases = $this->getVersionsForPackage($connectPackage); } catch (InvalidArgumentException $e) { $message = '<error>Could not find release manifest for module with extension key: "%s". '; $message .= 'Did you get the casing right? Error: "%s"</error>'; $io->writeError(sprintf($message, $connectPackage, $e->getMessage()), true); continue; } catch (UnexpectedValueException $e) { $message = '<error>Non valid XML return from connect for module with extension key: "%s".</error>'; $message .= $e->getMessage(); $io->writeError(sprintf($message, $connectPackage), true); continue; } $repository = $this->addPackages($releases, $connectPackage); $repositoryManager->addRepository($repository); $constraint = $versionParser->parseConstraints($version); $links[] = new Link($composer->getPackage()->getName(), $connectPackage, $constraint); } if (!empty($links)) { $requires = $composer->getPackage()->getRequires(); $requires = array_merge($requires, $links); $composer->getPackage()->setRequires($requires); } }
protected function setCakephpVersion($rm, $version) { $parser = new VersionParser(); list(, $version) = explode(' ', $parser->parseConstraints($version)); $installed = new InstalledArrayRepository(); $package = new Package('cakephp/cakephp', $version, $version); $installed->addPackage($package); $rm->setLocalRepository($installed); }
/** * Builds CompletePackages from PEAR package definition data. * * @param ChannelInfo $channelInfo * @param VersionParser $versionParser * @return CompletePackage */ private function buildComposerPackages(ChannelInfo $channelInfo, VersionParser $versionParser) { $result = array(); foreach ($channelInfo->getPackages() as $packageDefinition) { foreach ($packageDefinition->getReleases() as $version => $releaseInfo) { try { $normalizedVersion = $versionParser->normalize($version); } catch (\UnexpectedValueException $e) { if ($this->io->isVerbose()) { $this->io->writeError('Could not load ' . $packageDefinition->getPackageName() . ' ' . $version . ': ' . $e->getMessage()); } continue; } $composerPackageName = $this->buildComposerPackageName($packageDefinition->getChannelName(), $packageDefinition->getPackageName()); // distribution url must be read from /r/{packageName}/{version}.xml::/r/g:text() // but this location is 'de-facto' standard $urlBits = parse_url($this->url); $scheme = isset($urlBits['scheme']) && 'https' === $urlBits['scheme'] && extension_loaded('openssl') ? 'https' : 'http'; $distUrl = "{$scheme}://{$packageDefinition->getChannelName()}/get/{$packageDefinition->getPackageName()}-{$version}.tgz"; $requires = array(); $suggests = array(); $conflicts = array(); $replaces = array(); // alias package only when its channel matches repository channel, // cause we've know only repository channel alias if ($channelInfo->getName() == $packageDefinition->getChannelName()) { $composerPackageAlias = $this->buildComposerPackageName($channelInfo->getAlias(), $packageDefinition->getPackageName()); $aliasConstraint = new VersionConstraint('==', $normalizedVersion); $replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint); } // alias package with user-specified prefix. it makes private pear channels looks like composer's. if (!empty($this->vendorAlias) && ($this->vendorAlias != 'pear-' . $channelInfo->getAlias() || $channelInfo->getName() != $packageDefinition->getChannelName())) { $composerPackageAlias = "{$this->vendorAlias}/{$packageDefinition->getPackageName()}"; $aliasConstraint = new VersionConstraint('==', $normalizedVersion); $replaces[] = new Link($composerPackageName, $composerPackageAlias, $aliasConstraint, 'replaces', (string) $aliasConstraint); } foreach ($releaseInfo->getDependencyInfo()->getRequires() as $dependencyConstraint) { $dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName()); $constraint = $versionParser->parseConstraints($dependencyConstraint->getConstraint()); $link = new Link($composerPackageName, $dependencyPackageName, $constraint, $dependencyConstraint->getType(), $dependencyConstraint->getConstraint()); switch ($dependencyConstraint->getType()) { case 'required': $requires[] = $link; break; case 'conflicts': $conflicts[] = $link; break; case 'replaces': $replaces[] = $link; break; } } foreach ($releaseInfo->getDependencyInfo()->getOptionals() as $group => $dependencyConstraints) { foreach ($dependencyConstraints as $dependencyConstraint) { $dependencyPackageName = $this->buildComposerPackageName($dependencyConstraint->getChannelName(), $dependencyConstraint->getPackageName()); $suggests[$group . '-' . $dependencyPackageName] = $dependencyConstraint->getConstraint(); } } $package = new CompletePackage($composerPackageName, $normalizedVersion, $version); $package->setType('pear-library'); $package->setDescription($packageDefinition->getDescription()); $package->setLicense(array($packageDefinition->getLicense())); $package->setDistType('file'); $package->setDistUrl($distUrl); $package->setAutoload(array('classmap' => array(''))); $package->setIncludePaths(array('/')); $package->setRequires($requires); $package->setConflicts($conflicts); $package->setSuggests($suggests); $package->setReplaces($replaces); $result[] = $package; } } return $result; }
/** * Require (install) a package. * * @param $package array Package names and version to require * - Format: ['name' => '', 'version' => ''] * * @throws \Bolt\Exception\PackageManagerException * * @return int 0 on success or a positive error code on failure */ public function execute(array $package) { $this->versionSelector = new VersionSelector($this->getPool()); /** @var $composer \Composer\Composer */ $composer = $this->getComposer(); $io = $this->getIO(); $file = $this->getOption('composerjson'); $newlyCreated = !file_exists($file); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { // JSON could not be created return 1; } if (!is_readable($file)) { // JSON is not readable return 1; } if (!is_writable($file)) { // JSON is not writable return 1; } // Get the Composer repos $repos = $composer->getRepositoryManager()->getRepositories(); $this->repos = new CompositeRepository(array_merge([new PlatformRepository()], $repos)); // Format the package array $package = $this->formatRequirements($package); // Validate requirements format $versionParser = new VersionParser(); foreach ($package as $constraint) { $versionParser->parseConstraints($constraint); } // Get the JSON object $json = new JsonFile($this->getOption('composerjson')); // Update our JSON file with the selected version until we reset Composer $composerBackup = $this->updateComposerJson($json, $package, false); // Reload Composer config $composer = $this->resetComposer(); // Update our JSON file now with a contraint $this->updateComposerJson($json, $package, true); // JSON file has been created/updated, if we're not installing, exit if ($this->getOption('noupdate')) { return 0; } /** @var $install \Composer\Installer */ $install = Installer::create($io, $composer); try { $install->setVerbose($this->getOption('verbose'))->setPreferSource($this->getOption('prefersource'))->setPreferDist($this->getOption('preferdist'))->setDevMode(!$this->getOption('updatenodev'))->setUpdate($this->getOption('update'))->setUpdateWhitelist(array_keys($package))->setWhitelistDependencies($this->getOption('updatewithdependencies'))->setIgnorePlatformRequirements($this->getOption('ignoreplatformreqs')); $status = $install->run(); if ($status !== 0) { if ($newlyCreated) { // Installation failed, deleting JSON unlink($json->getPath()); } else { // Installation failed, reverting JSON to its original content file_put_contents($json->getPath(), $composerBackup); } } return $status; } catch (\Exception $e) { // Installation failed, reverting JSON to its original content file_put_contents($json->getPath(), $composerBackup); $msg = __CLASS__ . '::' . __FUNCTION__ . ' recieved an error from Composer: ' . $e->getMessage() . ' in ' . $e->getFile() . '::' . $e->getLine(); $this->app['logger.system']->critical($msg, ['event' => 'exception', 'exception' => $e]); throw new PackageManagerException($e->getMessage(), $e->getCode(), $e); } }
protected function execute(InputInterface $input, OutputInterface $output) { $file = Factory::getComposerFile(); $io = $this->getIO(); $newlyCreated = !file_exists($file); if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) { $io->writeError('<error>' . $file . ' could not be created.</error>'); return 1; } if (!is_readable($file)) { $io->writeError('<error>' . $file . ' is not readable.</error>'); return 1; } if (!is_writable($file)) { $io->writeError('<error>' . $file . ' is not writable.</error>'); return 1; } if (filesize($file) === 0) { file_put_contents($file, "{\n}\n"); } $json = new JsonFile($file); $composerDefinition = $json->read(); $composerBackup = file_get_contents($json->getPath()); $composer = $this->getComposer(true, $input->getOption('no-plugins')); $repos = $composer->getRepositoryManager()->getRepositories(); $platformOverrides = $composer->getConfig()->get('platform') ?: array(); // initialize $this->repos as it is used by the parent InitCommand $this->repos = new CompositeRepository(array_merge(array(new PlatformRepository(array(), $platformOverrides)), $repos)); $phpVersion = $this->repos->findPackage('php', '*')->getVersion(); $requirements = $this->determineRequirements($input, $output, $input->getArgument('packages'), $phpVersion); $requireKey = $input->getOption('dev') ? 'require-dev' : 'require'; $removeKey = $input->getOption('dev') ? 'require' : 'require-dev'; $baseRequirements = array_key_exists($requireKey, $composerDefinition) ? $composerDefinition[$requireKey] : array(); $requirements = $this->formatRequirements($requirements); // validate requirements format $versionParser = new VersionParser(); foreach ($requirements as $constraint) { $versionParser->parseConstraints($constraint); } $sortPackages = $input->getOption('sort-packages') || $composer->getConfig()->get('sort-packages'); if (!$this->updateFileCleanly($json, $baseRequirements, $requirements, $requireKey, $removeKey, $sortPackages)) { foreach ($requirements as $package => $version) { $baseRequirements[$package] = $version; if (isset($composerDefinition[$removeKey][$package])) { unset($composerDefinition[$removeKey][$package]); } } $composerDefinition[$requireKey] = $baseRequirements; $json->write($composerDefinition); } $io->writeError('<info>' . $file . ' has been ' . ($newlyCreated ? 'created' : 'updated') . '</info>'); if ($input->getOption('no-update')) { return 0; } $updateDevMode = !$input->getOption('update-no-dev'); $optimize = $input->getOption('optimize-autoloader') || $composer->getConfig()->get('optimize-autoloader'); $authoritative = $input->getOption('classmap-authoritative') || $composer->getConfig()->get('classmap-authoritative'); // Update packages $this->resetComposer(); $composer = $this->getComposer(true, $input->getOption('no-plugins')); $composer->getDownloadManager()->setOutputProgress(!$input->getOption('no-progress')); $commandEvent = new CommandEvent(PluginEvents::COMMAND, 'require', $input, $output); $composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent); $install = Installer::create($io, $composer); $install->setVerbose($input->getOption('verbose'))->setPreferSource($input->getOption('prefer-source'))->setPreferDist($input->getOption('prefer-dist'))->setDevMode($updateDevMode)->setOptimizeAutoloader($optimize)->setClassMapAuthoritative($authoritative)->setUpdate(true)->setUpdateWhitelist(array_keys($requirements))->setWhitelistDependencies($input->getOption('update-with-dependencies'))->setIgnorePlatformRequirements($input->getOption('ignore-platform-reqs'))->setPreferStable($input->getOption('prefer-stable'))->setPreferLowest($input->getOption('prefer-lowest')); $exception = null; try { $status = $install->run(); } catch (\Exception $exception) { $status = 1; } if ($status !== 0) { if ($newlyCreated) { $io->writeError("\n" . '<error>Installation failed, deleting ' . $file . '.</error>'); unlink($json->getPath()); } else { $io->writeError("\n" . '<error>Installation failed, reverting ' . $file . ' to its original content.</error>'); file_put_contents($json->getPath(), $composerBackup); } } if ($exception) { throw $exception; } return $status; }
protected function installRootPackage(IOInterface $io, $config, $packageName, $directory = null, $packageVersion = null, $stability = 'stable', $preferSource = false, $preferDist = false, $installDevPackages = false, $repositoryUrl = null, $disablePlugins = false, $noScripts = false, $keepVcs = false, $noProgress = false) { if (null === $repositoryUrl) { $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); } elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) { $sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io))); } elseif (0 === strpos($repositoryUrl, 'http')) { $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config); } else { throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url."); } $parser = new VersionParser(); $candidates = array(); $requirements = $parser->parseNameVersionPairs(array($packageName)); $name = strtolower($requirements[0]['name']); if (!$packageVersion && isset($requirements[0]['version'])) { $packageVersion = $requirements[0]['version']; } if (null === $stability) { if (preg_match('{^[^,\\s]*?@(' . implode('|', array_keys(BasePackage::$stabilities)) . ')$}i', $packageVersion, $match)) { $stability = $match[1]; } else { $stability = VersionParser::parseStability($packageVersion); } } $stability = VersionParser::normalizeStability($stability); if (!isset(BasePackage::$stabilities[$stability])) { throw new \InvalidArgumentException('Invalid stability provided (' . $stability . '), must be one of: ' . implode(', ', array_keys(BasePackage::$stabilities))); } $pool = new Pool($stability); $pool->addRepository($sourceRepo); $constraint = $packageVersion ? $parser->parseConstraints($packageVersion) : null; $candidates = $pool->whatProvides($name, $constraint); foreach ($candidates as $key => $candidate) { if ($candidate->getName() !== $name) { unset($candidates[$key]); } } if (!$candidates) { throw new \InvalidArgumentException("Could not find package {$name}" . ($packageVersion ? " with version {$packageVersion}." : " with stability {$stability}.")); } if (null === $directory) { $parts = explode("/", $name, 2); $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); } $package = reset($candidates); foreach ($candidates as $candidate) { if (version_compare($package->getVersion(), $candidate->getVersion(), '<')) { $package = $candidate; } } unset($candidates); $io->write('<info>Installing ' . $package->getName() . ' (' . VersionParser::formatVersion($package, false) . ')</info>'); if ($disablePlugins) { $io->write('<info>Plugins have been disabled.</info>'); } if (0 === strpos($package->getPrettyVersion(), 'dev-') && in_array($package->getSourceType(), array('git', 'hg'))) { $package->setSourceReference(substr($package->getPrettyVersion(), 4)); } $dm = $this->createDownloadManager($io, $config); $dm->setPreferSource($preferSource)->setPreferDist($preferDist)->setOutputProgress(!$noProgress); $projectInstaller = new ProjectInstaller($directory, $dm); $im = $this->createInstallationManager(); $im->addInstaller($projectInstaller); $im->install(new InstalledFilesystemRepository(new JsonFile('php://memory')), new InstallOperation($package)); $im->notifyInstalls(); $installedFromVcs = 'source' === $package->getInstallationSource(); $io->write('<info>Created project in ' . $directory . '</info>'); chdir($directory); putenv('COMPOSER_ROOT_VERSION=' . $package->getPrettyVersion()); return $installedFromVcs; }
protected function determineRequirements(InputInterface $input, OutputInterface $output, $requires = array(), $phpVersion = null) { if ($requires) { $requires = $this->normalizeRequirements($requires); $result = array(); $io = $this->getIO(); foreach ($requires as $requirement) { if (!isset($requirement['version'])) { // determine the best version automatically $version = $this->findBestVersionForPackage($input, $requirement['name'], $phpVersion); $requirement['version'] = $version; $io->writeError(sprintf('Using version <info>%s</info> for <info>%s</info>', $requirement['version'], $requirement['name'])); } $result[] = $requirement['name'] . ' ' . $requirement['version']; } return $result; } $versionParser = new VersionParser(); $io = $this->getIO(); while (null !== ($package = $io->ask('Search for a package: '))) { $matches = $this->findPackages($package); if (count($matches)) { $exactMatch = null; $choices = array(); foreach ($matches as $position => $foundPackage) { $choices[] = sprintf(' <info>%5s</info> %s', "[{$position}]", $foundPackage['name']); if ($foundPackage['name'] === $package) { $exactMatch = true; break; } } // no match, prompt which to pick if (!$exactMatch) { $io->writeError(array('', sprintf('Found <info>%s</info> packages matching <info>%s</info>', count($matches), $package), '')); $io->writeError($choices); $io->writeError(''); $validator = function ($selection) use($matches, $versionParser) { if ('' === $selection) { return false; } if (is_numeric($selection) && isset($matches[(int) $selection])) { $package = $matches[(int) $selection]; return $package['name']; } if (preg_match('{^\\s*(?P<name>[\\S/]+)(?:\\s+(?P<version>\\S+))?\\s*$}', $selection, $packageMatches)) { if (isset($packageMatches['version'])) { // parsing `acme/example ~2.3` // validate version constraint $versionParser->parseConstraints($packageMatches['version']); return $packageMatches['name'] . ' ' . $packageMatches['version']; } // parsing `acme/example` return $packageMatches['name']; } throw new \Exception('Not a valid selection'); }; $package = $io->askAndValidate('Enter package # to add, or the complete package name if it is not listed: ', $validator, 3, false); } // no constraint yet, determine the best version automatically if (false !== $package && false === strpos($package, ' ')) { $validator = function ($input) { $input = trim($input); return $input ?: false; }; $constraint = $io->askAndValidate('Enter the version constraint to require (or leave blank to use the latest version): ', $validator, 3, false); if (false === $constraint) { $constraint = $this->findBestVersionForPackage($input, $package, $phpVersion); $io->writeError(sprintf('Using version <info>%s</info> for <info>%s</info>', $constraint, $package)); } $package .= ' ' . $constraint; } if (false !== $package) { $requires[] = $package; } } } return $requires; }
/** * Get the patches for a package, identified by name and version * * @param string $name * @param string $version * * @throws Exception * * @return Patch[] */ public function getPatches($name, $version) { if (!is_array($this->patches)) { $this->source = $this->read($this->source); $this->patches = array(); } if (!array_key_exists($name, $this->patches)) { $this->source[$name] = $this->read($this->source, $name); $this->patches[$name] = array(); } if (!array_key_exists($version, $this->patches[$name])) { $patchInfos = $this->read($this->source[$name]); if ($this->isPatch($patchInfos)) { $rawPatches = array($patchInfos); } else { $rawPatches = array(); $hasConstraints = null; $requiredConstraint = new VersionConstraint('==', $version); $versionParser = new VersionParser(); foreach ($patchInfos as $constraint => $patchInfo) { if ($this->isPatch($patchInfo)) { $isConstraint = false; $rawPatches[] = $patchInfo; } else { $patchInfo = $this->read($patchInfo); $isConstraint = true; $constraint = $versionParser->parseConstraints($constraint); if ($constraint->matches($requiredConstraint)) { foreach ($patchInfo as $i => $rawPatch) { if (!$this->isPatch($rawPatch)) { throw new Exception("Entry {$name}.{$constraint}[{$i}] is not a valid patch"); } $rawPatches[] = $rawPatch; } } } if ($hasConstraints !== null) { if ($hasConstraints !== $isConstraint) { throw new Exception('Mixing patches with constraints and without constraints is not possible'); } } else { $hasConstraints = $isConstraint; } } } $patches = array(); foreach ($rawPatches as $rawPatch) { $patch = new Patch((object) $rawPatch, $this); $patches[$patch->getChecksum()] = $patch; } $this->patches[$name][$version] = $patches; } return $this->patches[$name][$version]; }