diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php
index 2046dd1da..88b4ea1d5 100644
--- a/src/Composer/Installer.php
+++ b/src/Composer/Installer.php
@@ -1301,11 +1301,6 @@ class Installer
$rootRequires = array_merge($rootRequires, $rootDevRequires);
- $requiredPackageNames = array();
- foreach ($rootRequires as $require) {
- $requiredPackageNames[] = $require->getTarget();
- }
-
$skipPackages = array();
if (!$this->whitelistAllDependencies) {
foreach ($rootRequires as $require) {
@@ -1323,22 +1318,26 @@ class Installer
foreach ($this->updateWhitelist as $packageName => $void) {
$packageQueue = new \SplQueue;
- $depPackages = [$pool->whatProvides($packageName)];
-
- $nameMatchesRequiredPackage = in_array($packageName, $requiredPackageNames, true);
-
+ $depPackages = $pool->whatProvides($packageName);
+ $matchesByPattern = [];
// check if the name is a glob pattern that did not match directly
- if (!$nameMatchesRequiredPackage) {
+ if (empty($depPackages)) {
+ $whitelistPatternSearchRegexp = BasePackage::packageNameToRegexp($packageName, '^%s$');
+ foreach ($localOrLockRepo->search($whitelistPatternSearchRegexp) as $installedPackage) {
+ $matchesByPattern[] = $pool->whatProvides($installedPackage['name']);
+ }
$whitelistPatternRegexp = BasePackage::packageNameToRegexp($packageName);
foreach ($rootRequiredPackageNames as $rootRequiredPackageName) {
if (preg_match($whitelistPatternRegexp, $rootRequiredPackageName)) {
- $depPackages[] = $pool->whatProvides($rootRequiredPackageName);
$nameMatchesRequiredPackage = true;
+ break;
}
}
}
- $depPackages = array_merge(...$depPackages);
+ if (!empty($matchesByPattern)) {
+ $depPackages = array_merge($depPackages, array_merge(...$matchesByPattern));
+ }
if (count($depPackages) == 0 && !$nameMatchesRequiredPackage && !in_array($packageName, array('nothing', 'lock', 'mirrors'))) {
$this->io->writeError('Package "' . $packageName . '" listed for update is not installed. Ignoring.');
@@ -1371,7 +1370,7 @@ class Installer
continue;
}
- if (isset($skipPackages[$requirePackage->getName()])) {
+ if (isset($skipPackages[$requirePackage->getName()]) && !preg_match(BasePackage::packageNameToRegexp($packageName), $requirePackage->getName())) {
$this->io->writeError('Dependency "' . $requirePackage->getName() . '" is also a root requirement, but is not explicitly whitelisted. Ignoring.');
continue;
}
diff --git a/src/Composer/Package/BasePackage.php b/src/Composer/Package/BasePackage.php
index 65ea6860f..f2f5be707 100644
--- a/src/Composer/Package/BasePackage.php
+++ b/src/Composer/Package/BasePackage.php
@@ -239,12 +239,13 @@ abstract class BasePackage implements PackageInterface
* Build a regexp from a package name, expanding * globs as required
*
* @param string $whiteListedPattern
+ * @param bool $wrap Wrap the cleaned string by the given string
* @return string
*/
- public static function packageNameToRegexp($whiteListedPattern)
+ public static function packageNameToRegexp($whiteListedPattern, $wrap = '{^%s$}i')
{
$cleanedWhiteListedPattern = str_replace('\\*', '.*', preg_quote($whiteListedPattern));
- return "{^" . $cleanedWhiteListedPattern . "$}i";
+ return sprintf($wrap, $cleanedWhiteListedPattern);
}
}
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test
new file mode 100644
index 000000000..8ea177cad
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-all-dependencies.test
@@ -0,0 +1,46 @@
+--TEST--
+Update with a package whitelist pattern and all-dependencies flag updates packages and their dependencies, even if defined as root dependency, matching the pattern
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed", "version": "1.1.0" },
+ { "name": "fixed", "version": "1.0.0" },
+ { "name": "whitelisted-component1", "version": "1.1.0" },
+ { "name": "whitelisted-component1", "version": "1.0.0" },
+ { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*" } },
+ { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*" } },
+ { "name": "dependency", "version": "1.1.0" },
+ { "name": "dependency", "version": "1.0.0" },
+ { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated-dependency", "version": "1.1.0" },
+ { "name": "unrelated-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed": "1.*",
+ "whitelisted-component1": "1.*",
+ "whitelisted-component2": "1.*",
+ "dependency": "1.*",
+ "unrelated": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed", "version": "1.0.0" },
+ { "name": "whitelisted-component1", "version": "1.0.0" },
+ { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
+ { "name": "dependency", "version": "1.0.0" },
+ { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated-dependency", "version": "1.0.0" }
+]
+--RUN--
+update whitelisted-* --with-all-dependencies
+--EXPECT--
+Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0)
+Updating dependency (1.0.0) to dependency (1.1.0)
+Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test
new file mode 100644
index 000000000..c685f14ce
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-dependencies.test
@@ -0,0 +1,49 @@
+--TEST--
+Update with a package whitelist only updates those packages and their dependencies matching the pattern but no dependencies defined as roo package
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed", "version": "1.1.0" },
+ { "name": "fixed", "version": "1.0.0" },
+ { "name": "whitelisted-component1", "version": "1.1.0" },
+ { "name": "whitelisted-component1", "version": "1.0.0" },
+ { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*", "root-dependency": "1.*" } },
+ { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*", "root-dependency": "1.*" } },
+ { "name": "dependency", "version": "1.1.0" },
+ { "name": "dependency", "version": "1.0.0" },
+ { "name": "root-dependency", "version": "1.1.0" },
+ { "name": "root-dependency", "version": "1.0.0" },
+ { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated-dependency", "version": "1.1.0" },
+ { "name": "unrelated-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed": "1.*",
+ "whitelisted-component1": "1.*",
+ "whitelisted-component2": "1.*",
+ "root-dependency": "1.*",
+ "unrelated": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed", "version": "1.0.0" },
+ { "name": "whitelisted-component1", "version": "1.0.0" },
+ { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
+ { "name": "root-dependency", "version": "1.0.0" },
+ { "name": "dependency", "version": "1.0.0" },
+ { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated-dependency", "version": "1.0.0" }
+]
+--RUN--
+update whitelisted-* --with-dependencies
+--EXPECT--
+Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0)
+Updating dependency (1.0.0) to dependency (1.1.0)
+Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0)
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test
index 360ef49f5..a24bafb91 100644
--- a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test
+++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-with-root-dependencies.test
@@ -10,8 +10,14 @@ Update with a package whitelist only updates those packages and their dependenci
{ "name": "fixed", "version": "1.0.0" },
{ "name": "whitelisted-component1", "version": "1.1.0", "require": { "whitelisted-component2": "1.1.0" } },
{ "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } },
- { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.1.0" } },
+ { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.1.0", "whitelisted-component5": "1.0.0" } },
{ "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
+ { "name": "whitelisted-component3", "version": "1.1.0", "require": { "whitelisted-component4": "1.1.0" } },
+ { "name": "whitelisted-component3", "version": "1.0.0", "require": { "whitelisted-component4": "1.0.0" } },
+ { "name": "whitelisted-component4", "version": "1.1.0" },
+ { "name": "whitelisted-component4", "version": "1.0.0" },
+ { "name": "whitelisted-component5", "version": "1.1.0" },
+ { "name": "whitelisted-component5", "version": "1.0.0" },
{ "name": "dependency", "version": "1.1.0" },
{ "name": "dependency", "version": "1.0.0" },
{ "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
@@ -25,6 +31,7 @@ Update with a package whitelist only updates those packages and their dependenci
"fixed": "1.*",
"whitelisted-component1": "1.*",
"whitelisted-component2": "1.*",
+ "whitelisted-component3": "1.0.0",
"unrelated": "1.*"
}
}
@@ -33,6 +40,9 @@ Update with a package whitelist only updates those packages and their dependenci
{ "name": "fixed", "version": "1.0.0" },
{ "name": "whitelisted-component1", "version": "1.0.0", "require": { "whitelisted-component2": "1.0.0" } },
{ "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
+ { "name": "whitelisted-component3", "version": "1.0.0", "require": { "whitelisted-component4": "1.0.0" } },
+ { "name": "whitelisted-component4", "version": "1.0.0" },
+ { "name": "whitelisted-component5", "version": "1.0.0" },
{ "name": "dependency", "version": "1.0.0" },
{ "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
{ "name": "unrelated-dependency", "version": "1.0.0" }
diff --git a/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test
new file mode 100644
index 000000000..e5551b43f
--- /dev/null
+++ b/tests/Composer/Test/Fixtures/installer/update-whitelist-patterns-without-dependencies.test
@@ -0,0 +1,44 @@
+--TEST--
+Update with a package whitelist only updates those packages matching the pattern
+--COMPOSER--
+{
+ "repositories": [
+ {
+ "type": "package",
+ "package": [
+ { "name": "fixed", "version": "1.1.0" },
+ { "name": "fixed", "version": "1.0.0" },
+ { "name": "whitelisted-component1", "version": "1.1.0" },
+ { "name": "whitelisted-component1", "version": "1.0.0" },
+ { "name": "whitelisted-component2", "version": "1.1.0", "require": { "dependency": "1.*" } },
+ { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.*" } },
+ { "name": "dependency", "version": "1.1.0" },
+ { "name": "dependency", "version": "1.0.0" },
+ { "name": "unrelated", "version": "1.1.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated-dependency", "version": "1.1.0" },
+ { "name": "unrelated-dependency", "version": "1.0.0" }
+ ]
+ }
+ ],
+ "require": {
+ "fixed": "1.*",
+ "whitelisted-component1": "1.*",
+ "whitelisted-component2": "1.*",
+ "unrelated": "1.*"
+ }
+}
+--INSTALLED--
+[
+ { "name": "fixed", "version": "1.0.0" },
+ { "name": "whitelisted-component1", "version": "1.0.0" },
+ { "name": "whitelisted-component2", "version": "1.0.0", "require": { "dependency": "1.0.0" } },
+ { "name": "dependency", "version": "1.0.0" },
+ { "name": "unrelated", "version": "1.0.0", "require": { "unrelated-dependency": "1.*" } },
+ { "name": "unrelated-dependency", "version": "1.0.0" }
+]
+--RUN--
+update whitelisted-*
+--EXPECT--
+Updating whitelisted-component1 (1.0.0) to whitelisted-component1 (1.1.0)
+Updating whitelisted-component2 (1.0.0) to whitelisted-component2 (1.1.0)