Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify package name(s) when using an update whitelist #1112

Closed
wants to merge 12 commits into from
Closed
4 changes: 4 additions & 0 deletions doc/03-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ If you just want to update a few packages and not all, you can list them as such

$ php composer.phar update vendor/package vendor/package2

To rewrite the lock file without updating any packages:

$ php composer.phar update nothing

### Options

* **--prefer-source:** Install packages from `source` when available.
Expand Down
2 changes: 1 addition & 1 deletion src/Composer/Command/RequireCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
->setPreferSource($input->getOption('prefer-source'))
->setDevMode($input->getOption('dev'))
->setUpdate(true)
->setUpdateWhitelist($requirements);
->setUpdateWhitelist(array_keys($requirements));
;

return $install->run() ? 0 : 1;
Expand Down
25 changes: 16 additions & 9 deletions src/Composer/Command/UpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Composer\Command;

use Composer\Exception\UnknownPackageException;
use Composer\Installer;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand Down Expand Up @@ -59,15 +60,21 @@ protected function execute(InputInterface $input, OutputInterface $output)
$io = $this->getIO();
$install = Installer::create($io, $composer);

$install
->setDryRun($input->getOption('dry-run'))
->setVerbose($input->getOption('verbose'))
->setPreferSource($input->getOption('prefer-source'))
->setDevMode($input->getOption('dev'))
->setRunScripts(!$input->getOption('no-scripts'))
->setUpdate(true)
->setUpdateWhitelist($input->getArgument('packages'))
;
try {
$install
->setDryRun($input->getOption('dry-run'))
->setVerbose($input->getOption('verbose'))
->setPreferSource($input->getOption('prefer-source'))
->setDevMode($input->getOption('dev'))
->setRunScripts(!$input->getOption('no-scripts'))
->setUpdate(true)
->setUpdateWhitelist($input->getArgument('packages'))
;
} catch (UnknownPackageException $e) {
$io->write('<error>' . $e->getMessage() . '</error>');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a great idea, if you throw a very specific exception and catch that ok, but as such it will basically prevent us from seeing the backtrace when a real exception occurs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point; there aren't any Composer exceptions yet, ok to create something like Composer\Exception\UnknownPackageNameException which extends \UnexpectedValueException? Couldn't see a non-exception method to halt the flow here (without rather ugly refactoring).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that's alright (the new exception)


return 1;
}

if ($input->getOption('no-custom-installers')) {
$install->disableCustomInstallers();
Expand Down
24 changes: 24 additions & 0 deletions src/Composer/Exception/UnknownPackageException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?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.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong indentation here (the * should be aligned with the opening one)


namespace Composer\Exception;

/**
* Unknown package exception.
*
* Used when a package isn't found in a list of valid packages.
*
* @author Chris Wilkinson <chriswilkinson84@gmail.com>
*/
class UnknownPackageException extends \UnexpectedValueException
{
}
37 changes: 35 additions & 2 deletions src/Composer/Installer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
use Composer\DependencyResolver\Solver;
use Composer\DependencyResolver\SolverProblemsException;
use Composer\Downloader\DownloadManager;
use Composer\Exception\UnknownPackageException;
use Composer\Installer\InstallationManager;
use Composer\Config;
use Composer\Installer\NoopInstaller;
Expand Down Expand Up @@ -740,12 +741,44 @@ public function setVerbose($verbose = true)
* restrict the update operation to a few packages, all other packages
* that are already installed will be kept at their current version
*
* @param array $packages
* @param array $packages Array of package names
* @return Installer
* @throws UnknownPackageException If a package name is not known
*/
public function setUpdateWhitelist(array $packages)
{
$this->updateWhitelist = array_flip(array_map('strtolower', $packages));
if (count($packages) === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (count($packages) === 0 || (isset($packages[0]) && strtolower($packages[0]) === 'nothing')) {

With such you could remove the if below as well, as the array_map() and move strtolower() into just last loop (with change of $lowercasePackages to $packages).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stloyd changing only the condition here would not be equivalent

$this->updateWhitelist = array();
return $this;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not equivalent to the previous code. You should reset the update whitelist to an empty array here, otherwise resetting it is not possible anymore

}

$lowercasePackages = array_map('strtolower', $packages);

if (count($packages) > 1 || $packages[0] !== 'nothing') {
$knownPackages = array();
foreach ($this->repositoryManager->getLocalRepository()->getPackages() as $localPackage) {
$knownPackages = array_merge($knownPackages, $localPackage->getNames());
}
foreach ($this->package->getRequires() as $requiredPackage) {
$knownPackages[] = $requiredPackage->getTarget();
}
if ($this->devMode) {
foreach ($this->repositoryManager->getLocalDevRepository()->getPackages() as $localPackage) {
$knownPackages = array_merge($knownPackages, $localPackage->getNames());
}
foreach ($this->package->getDevRequires() as $requiredPackage) {
$knownPackages[] = $requiredPackage->getTarget();
}
}

foreach ($lowercasePackages as $key => $package) {
if (!in_array($package, $knownPackages)) {
throw new UnknownPackageException('Package ' . $packages[$key] . ' not known');
}
}
}

$this->updateWhitelist = array_flip($lowercasePackages);

return $this;
}
Expand Down