/** * Check to see if any packages in the list of packages to be installed * satisfy this dependency, and return one if found, otherwise * instantiate a new dependency package object * @return \Pyrus\PackageInterface|NULL */ function retrieve(\Pyrus\PackageFile\v2\Dependencies\Package $info) { if (isset(self::$localPackages[$info->channel . '/' . $info->name]) || $this->childProcessed($info->channel . '/' . $info->name)) { // we can safely ignore this dependency, an explicit local // package is being installed, and we will use it // or the dependency has been previously processed, and we will // simply result in a duplicate return; } $reg = Config::current()->registry; // first check to see if the dependency is installed $canupgrade = false; if (isset($reg->package[$info->channel . '/' . $info->name])) { if (!isset(\Pyrus\Main::$options['upgrade'])) { // we don't attempt to upgrade a dep unless we're upgrading return; } $version = $reg->info($info->name, $info->channel, 'version'); $stability = $reg->info($info->name, $info->channel, 'state'); if ($this->node->isRemote() && $this->node->getExplicitState()) { $installedstability = \Pyrus\Installer::betterStates($stability); $parentstability = \Pyrus\Installer::betterStates($this->node->getExplicitState()); if (count($parentstability) > count($installedstability)) { $stability = $this->node->getExplicitState(); } } else { $installedstability = \Pyrus\Installer::betterStates($stability); $prefstability = \Pyrus\Installer::betterStates(Config::current()->preferred_state); if (count($prefstability) > count($installedstability)) { $stability = Config::current()->preferred_state; } } // see if there are new versions in our stability or better if (isset($info->uri)) { return; } $remote = new \Pyrus\Channel\RemotePackage(Config::current()->channelregistry[$info->channel], $stability); $found = false; foreach ($remote[$info->name] as $remoteversion => $rinfo) { if (version_compare($remoteversion, $version, '<=')) { continue; } if (version_compare($rinfo['minimumphp'], static::getPHPversion(), '>')) { continue; } // found one, so upgrade is possible if dependencies pass $found = true; break; } // the installed package version satisfies this dependency, don't do anything if (!$found) { return; } $canupgrade = true; } if (isset($info->uri)) { $ret = new \Pyrus\Package\Remote($info->uri); // set up the basics $ret->name = $info->name; $ret->uri = $info->uri; $this->addChild($ret); return; } if ($this->node->isRemote() && $this->node->getExplicitState()) { // pass the same explicit state to the child dependency $ret = new \Pyrus\Package\Remote($info->channel . '/' . $info->name . '-' . $this->node->getExplicitState()); if ($canupgrade) { $ret->setUpgradeable(); } $this->addChild($ret); return; } $ret = new \Pyrus\Package\Remote($info->channel . '/' . $info->name); if ($canupgrade) { $ret->setUpgradeable(); } $this->addChild($ret); return; }
/** * * @param string|array pass in an array of format * array( * 'package' => 'pname', * ['channel' => 'channame',] * ['version' => 'version',] * ['state' => 'state',]) * or a string of format [channame/]pname[-version|-state] */ protected function fromString($param) { try { $pname = Config::parsePackageName($param, true); } catch (\Pyrus\ChannelRegistry\ParseException $e) { if ($e->why !== 'channel') { throw new Exception('invalid package name/package file "' . $param . '"', $e); } if (Config::current()->auto_discover) { try { try { $chan = new \Pyrus\Channel(new \Pyrus\ChannelFile('https://' . $e->params['channel'] . '/channel.xml')); } catch (\Exception $e) { $chan = new \Pyrus\Channel(new \Pyrus\ChannelFile('http://' . $e->params['channel'] . '/channel.xml')); } } catch (\Exception $e) { throw new Exception('Cannot auto-discover channel ' . $e->params['channel'], $e); } Config::current()->channelregistry[] = $chan; try { Config::parsePackageName($param, Config::current()->default_channel); } catch (\Exception $e) { throw new Exception('invalid package name/package file "' . $param . '"', $e); } } else { \Pyrus\Logger::log(0, 'Channel "' . $param['channel'] . '" is not initialized, use ' . '"pyrus channel-discover ' . $param['channel'] . '" to initialize' . 'or pyrus set auto_discover 1'); } } $this->parsedname = $pname; $this->explicitVersion = isset($pname['version']) ? $pname['version'] : false; $this->explicitState = isset($pname['state']) ? $pname['state'] : false; $this->explicitGroup = isset($pname['group']) ? true : false; $reg = Config::current()->registry; $version = $reg->info($pname['package'], $pname['channel'], 'version'); $stability = $reg->info($pname['package'], $pname['channel'], 'state'); if (!isset(\Pyrus\Main::$options['force']) && !isset(\Pyrus\Main::$options['downloadonly']) && $version && $this->explicitVersion && !isset($pname['group'])) { if (version_compare($version, $pname['version'], '>=')) { throw new InstalledException(Config::parsedPackageNameToString($pname, true) . ' is already installed and is newer than detected ' . 'release version ' . $pname['version']); } } if (!$this->explicitVersion && $stability) { // if installed, use stability of the installed package, // but only if it is less restrictive than preferred_state. // This allows automatic upgrade to a newer beta for 1 package // even if preferred_state is stable, for instance. $states = \Pyrus\Installer::betterStates(Config::current()->preferred_state); $newstates = \Pyrus\Installer::betterStates($stability); if (count($newstates) > count($states)) { $this->explicitState = $stability; } } $this->type = 'abstract'; $ret = $this->getRemotePackage($pname); if ($this->explicitVersion) { $ret->setExplicitVersion($this->explicitVersion); $ret->version['release'] = $this->explicitVersion; } if ($this->explicitState) { $ret->setExplicitState($this->explicitState); } return $ret; }
function rewind() { $url = $this->parent->protocols->rest['REST1.0']->baseurl . 'p/packages.xml'; $this->packageList = $this->rest->retrieveCacheFirst($url, false, false, 'text/xml'); $this->packageList = $this->packageList['p']; if (!is_array($this->packageList)) { $this->packageList = array($this->packageList); } if (isset($this->stability)) { // filter the package list for packages of this stability or better $ok = \Pyrus\Installer::betterStates($this->stability, true); $filtered = array(); foreach ($this->packageList as $lowerpackage) { if (isset($this->parent->protocols->rest['REST1.3'])) { $info = $this->rest->retrieveCacheFirst($this->parent->protocols->rest['REST1.3']->baseurl . 'r/' . $lowerpackage . '/allreleases2.xml', false, false, 'text/xml'); } else { $info = $this->rest->retrieveCacheFirst($this->parent->protocols->rest['REST1.0']->baseurl . 'r/' . $lowerpackage . '/allreleases.xml', false, false, 'text/xml'); } if (!isset($info['r'][0])) { $info['r'] = array($info['r']); } $releases = array(); foreach ($info['r'] as $release) { if (!in_array($release['s'], $ok)) { continue; } if (!isset($release['m'])) { $release['m'] = '5.2.0'; } $releases[] = $release; } if (!count($releases)) { continue; } $filtered[] = array($lowerpackage, $releases); } $this->packageList = $filtered; } }
/** * Figure out which version is best, and use this, or error out if none work * @param \Pyrus\PackageFile\v2\Dependencies\Package $compositeDep * the composite of all dependencies on this package, as calculated * by {@link \Pyrus\Package\Dependency::getCompositeDependency()} */ function figureOutBestVersion(\Pyrus\PackageFile\v2\Dependencies\Package $compositeDep, $versions = null, \Pyrus\PackageFile\v2\Dependencies\Package $compositeConflictingDep = null) { // set up release list if not done yet $this->rewind(); $ok = \Pyrus\Installer::betterStates($this->minimumStability, true); $v = $this->explicitVersion; $n = $this->channel . '/' . $this->name; $failIfExplicit = function ($versioninfo) use($v, $n) { if ($v && $versioninfo['v'] == $v) { throw new Exception($n . ' Cannot be installed, it does not satisfy ' . 'all dependencies'); } }; foreach ($this->releaseList as $versioninfo) { if (isset(\Pyrus\Main::$options['force'])) { // found one if ($this->versionSet && $versioninfo['v'] != $this->version['release']) { // inform the installer we need to reset dependencies $this->version['release'] = $versioninfo['v']; return true; } $this->version['release'] = $versioninfo['v']; return; } if ($versions && !in_array($versioninfo['v'], $versions)) { continue; } if (!isset(\Pyrus\Main::$options['force']) && isset($versioninfo['m'])) { // minimum PHP version required if (version_compare($versioninfo['m'], $this->getPHPVersion(), '>')) { $failIfExplicit($versioninfo); continue; } } if (!in_array($versioninfo['s'], $ok) && !isset(\Pyrus\Main::$options['force'])) { // release is not stable enough continue; } if ($this->explicitVersion && $versioninfo['v'] != $this->explicitVersion) { continue; } if (!$compositeDep->satisfied($versioninfo['v'])) { $failIfExplicit($versioninfo); continue; } if ($compositeConflictingDep && !$compositeConflictingDep->satisfied($versioninfo['v'])) { $failIfExplicit($versioninfo); continue; } $paranoia = \Pyrus\Main::getParanoiaLevel(); if (!$this->explicitVersion && $paranoia > 1) { // first, we check to see if we are upgrading if (isset(\Pyrus\Main::$options['upgrade'])) { // now we check to see if we are installed if (isset(Config::current()->registry->package[$n])) { $installed = Config::current()->registry->info($this->name, $this->channel, 'apiversion'); $installed = explode('.', $installed); if (count($installed) == 2) { $installed[] = '0'; } if (count($installed) == 1) { $installed[] = '0'; $installed[] = '0'; } if (isset($this->parent->protocols->rest['REST1.3'])) { $api = $this->rest->retrieveCacheFirst($this->parent->protocols->rest['REST1.3']->baseurl . 'r/' . strtolower($this->name) . '/v2.' . $versioninfo['v'] . '.xml', false, false, 'text/xml'); } else { throw new Exception('Channel ' . $this->channel . ' does not support ' . 'a paranoia greater than 1'); } $api = explode('.', $api['a']); if (count($api) == 2) { $api[] = '0'; } if (count($api) == 1) { $api[] = '0'; $api[] = '0'; } if ($paranoia > 4) { $paranoia = 4; } switch ($paranoia) { case 4: if ($installed != $api) { Logger::log(0, 'Skipping ' . $this->channel . '/' . $this->name . ' version ' . $versioninfo['v'] . ', API has changed'); continue 2; } break; case 3: if ($installed[0] == $api[0] && $installed[1] != $api[1]) { Logger::log(0, 'Skipping ' . $this->channel . '/' . $this->name . ' version ' . $versioninfo['v'] . ', API has added' . ' new features'); continue 2; } // break intentionally omitted // break intentionally omitted case 2: if ($installed[0] != $api[0]) { Logger::log(0, 'Skipping ' . $this->channel . '/' . $this->name . ' version ' . $versioninfo['v'] . ', API breaks' . ' backwards compatibility'); continue 2; } break; } } } } // found one if ($this->versionSet && $versioninfo['v'] != $this->version['release']) { // inform the installer we need to reset dependencies $this->version['release'] = $versioninfo['v']; return true; } $this->version['release'] = $versioninfo['v']; return; } throw new Exception('Unable to locate a package release for ' . $this->channel . '/' . $this->name . ' that can satisfy all dependencies'); }
/** * install a local or remote package * * @param array $args */ function install($args, $options) { if ($options['plugin']) { \Pyrus\Main::$options['install-plugins'] = true; } if ($options['force']) { \Pyrus\Main::$options['force'] = true; } if (isset($options['packagingroot']) && $options['packagingroot']) { \Pyrus\Main::$options['packagingroot'] = $options['packagingroot']; } if ($options['optionaldeps']) { \Pyrus\Main::$options['optionaldeps'] = $options['optionaldeps']; } \Pyrus\Installer::begin(); try { $packages = array(); foreach ($args['package'] as $arg) { \Pyrus\Installer::prepare($packages[] = new \Pyrus\Package($arg)); } \Pyrus\Installer::commit(); foreach (\Pyrus\Installer::getInstalledPackages() as $package) { echo 'Installed ' . $package->channel . '/' . $package->name . '-' . $package->version['release'] . "\n"; if ($package->type === 'extsrc' || $package->type === 'zendextsrc') { echo " ==> To build this PECL package, use the build command\n"; } } $optionals = \Pyrus\Installer::getIgnoredOptionalDeps(); if (count($optionals)) { echo "Optional dependencies that will not be installed, use --optionaldeps:\n"; } foreach ($optionals as $dep => $packages) { echo $dep, ' depended on by ', implode(', ', array_keys($packages)), "\n"; } } catch (\Exception $e) { if ($e instanceof \Pyrus\Channel\Exception && strpos($arg, '/') === false) { echo "Sorry there was an error retrieving " . \Pyrus\Config::current()->default_channel . "/{$arg} from the default channel\n"; } // If this is an undiscovered channel, handle it gracefully if ($e instanceof \Pyrus\Package\Exception && $e->getPrevious() instanceof \Pyrus\ChannelRegistry\Exception && $e->getPrevious()->getPrevious() instanceof \Pyrus\ChannelRegistry\ParseException && $e->getPrevious()->getPrevious()->why == 'channel' && strpos($arg, '/') !== false) { $channel = substr($arg, 0, strrpos($arg, '/')); echo "Sorry, the channel \"{$channel}\" is unknown.\n"; return $this->installUnknownChannelExceptionHandler($args, $options, $e, $channel); } if ($e instanceof \Pyrus\ChannelRegistry\Exception && $e->getPrevious() instanceof \Pyrus\ChannelRegistry\ParseException && $e->getPrevious()->why == 'channel' && preg_match('/^unknown channel \\"(.*)\\" in \\"(.*)\\"$/', $e->getPrevious()->getMessage(), $matches)) { echo "Sorry, {$arg} references an unknown channel {$matches[1]} for {$matches[2]}\n"; return $this->installUnknownChannelExceptionHandler($args, $options, $e, $matches[1]); } $this->exceptionHandler($e); exit(1); } }