Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
},
"require-dev": {
"composer/composer": "^2.1",
"symfony/dotenv": "^6.4|^7.4|^8.0",
"symfony/dotenv": "^6.4.41|^7.4.13|^8.0.13",
"symfony/filesystem": "^6.4|^7.4|^8.0",
"symfony/phpunit-bridge": "^6.4|^7.4|^8.0",
"symfony/process": "^6.4|^7.4|^8.0"
Expand Down
54 changes: 52 additions & 2 deletions src/Flex.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
use Composer\Script\Event;
use Composer\Script\ScriptEvents;
use Composer\Semver\VersionParser;
use Symfony\Component\Console\Exception\ExceptionInterface as ConsoleExceptionInterface;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Flex\Event\UpdateEvent;
Expand Down Expand Up @@ -161,9 +162,11 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__)

$resolver = new PackageResolver($this->downloader);

$commandObj = null;
try {
$command = $input->getFirstArgument();
$command = $command ? $app->find($command)->getName() : null;
$commandObj = $command ? $app->find($command) : null;
$command = $commandObj ? $commandObj->getName() : null;
} catch (\InvalidArgumentException $e) {
}

Expand All @@ -178,8 +181,25 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__)
}

if (isset(self::$aliasResolveCommands[$command])) {
// When the command name is abbreviated (e.g. "req" for "require"), Composer
// activates plugins early to look up potential script commands, before the
// input has been bound to the command definition. In that case "packages"
// isn't a known argument yet, so bind the command definition first.
if (null !== $commandObj && !$input->hasArgument('packages')) {
$commandObj->mergeApplicationDefinition();
try {
$input->bind($commandObj->getDefinition());
} catch (ConsoleExceptionInterface $e) {
}
}
if ($input->hasArgument('packages')) {
$input->setArgument('packages', $resolver->resolve($input->getArgument('packages'), self::$aliasResolveCommands[$command]));
$packages = $input->getArgument('packages');
$resolved = $resolver->resolve($packages, self::$aliasResolveCommands[$command]);
$input->setArgument('packages', $resolved);
// The command will bind its definition again at execution time, which
// re-parses the raw tokens. Rewrite them too so the resolved package
// names survive that rebinding.
$this->rewritePackageTokens($input, $packages, $resolved);
}
}

Expand All @@ -206,6 +226,36 @@ class_exists(__NAMESPACE__.str_replace('/', '\\', substr($file, \strlen(__DIR__)
}
}

/**
* Rewrites the raw input tokens so resolved package names survive a later rebinding
* of the input to the command definition (which re-parses the tokens from scratch).
*/
private function rewritePackageTokens(ArgvInput $input, array $original, array $resolved): void
{
if ($original === $resolved) {
return;
}

try {
$property = new \ReflectionProperty(ArgvInput::class, 'tokens');
} catch (\ReflectionException $e) {
return;
}
$tokens = $property->getValue($input);

// Drop the tokens matching the original package arguments (each one once, keeping
// options and the command name in place), then append the resolved ones at the end.
// Argument order relative to options is irrelevant when the tokens are re-parsed.
$remaining = $original;
foreach ($tokens as $i => $token) {
if (false !== $pos = array_search($token, $remaining, true)) {
unset($tokens[$i], $remaining[$pos]);
}
}

$property->setValue($input, array_merge(array_values($tokens), $resolved));
}

/**
* @return void
*/
Expand Down
2 changes: 1 addition & 1 deletion tests/Command/DumpEnvCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public function testEnvCanBeReferenced()
$vars = require $envLocal;
$this->assertSame([
'APP_ENV' => 'prod',
'BAR' => 'Foo',
'BAR' => '123',
'FOO' => '123',
], $vars);

Expand Down
Loading