Skip to content

Commit 3f3f5f8

Browse files
committed
Allow capturing of requested package argument when configure options are placed before package argument
1 parent baafa28 commit 3f3f5f8

File tree

3 files changed

+105
-1
lines changed

3 files changed

+105
-1
lines changed

src/Command/ArgvInput.php

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\Pie\Command;
6+
7+
use Throwable;
8+
9+
class ArgvInput extends \Symfony\Component\Console\Input\ArgvInput
10+
{
11+
private Throwable|null $exceptionThrown = null;
12+
13+
/**
14+
* Wrap parent token parsing to collect and ignore exceptions during
15+
* parsing. This ensures that errors we meet mid-way through parsing don't
16+
* short-circuit processing the rest of the arguments. Without this, whilst
17+
* the following example works:
18+
*
19+
* pie build asgrim/example-pie-extension --with-hello-name=sup
20+
*
21+
* This does not:
22+
*
23+
* pie build --with-hello-name=sup asgrim/example-pie-extension
24+
*
25+
* This is because when Symfony tries to parse the `--with-hello-name`, it
26+
* hasn't loaded in the configure options for the package yet, and so
27+
* throws an exception and does not process the package name argument.
28+
*
29+
* Note, however, there is still a limitation, as this will not work:
30+
*
31+
* pie build --with-hello-name sup asgrim/example-pie-extension
32+
*/
33+
protected function parse(): void
34+
{
35+
$this->exceptionThrown = null;
36+
37+
parent::parse();
38+
39+
if ($this->exceptionThrown !== null) {
40+
throw $this->exceptionThrown;
41+
}
42+
}
43+
44+
protected function parseToken(string $token, bool $parseOptions): bool
45+
{
46+
try {
47+
return parent::parseToken($token, $parseOptions);
48+
} catch (Throwable $caught) {
49+
$this->exceptionThrown = $caught;
50+
51+
// Ignore the error intentionally
52+
return $parseOptions;
53+
}
54+
}
55+
}

src/Container.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use Php\Pie\Building\Build;
1414
use Php\Pie\Building\UnixBuild;
1515
use Php\Pie\Building\WindowsBuild;
16+
use Php\Pie\Command\ArgvInput;
1617
use Php\Pie\Command\BuildCommand;
1718
use Php\Pie\Command\DownloadCommand;
1819
use Php\Pie\Command\InfoCommand;
@@ -43,7 +44,6 @@
4344
use Symfony\Component\Console\Event\ConsoleCommandEvent;
4445
use Symfony\Component\Console\Formatter\OutputFormatter;
4546
use Symfony\Component\Console\Helper\QuestionHelper;
46-
use Symfony\Component\Console\Input\ArgvInput;
4747
use Symfony\Component\Console\Input\InputInterface;
4848
use Symfony\Component\Console\Output\ConsoleOutput;
4949
use Symfony\Component\Console\Output\OutputInterface;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Php\PieUnitTest\Command;
6+
7+
use Php\Pie\Command\ArgvInput;
8+
use PHPUnit\Framework\Attributes\CoversClass;
9+
use PHPUnit\Framework\Attributes\DataProvider;
10+
use PHPUnit\Framework\TestCase;
11+
use Symfony\Component\Console\Input\InputArgument;
12+
use Symfony\Component\Console\Input\InputDefinition;
13+
use Throwable;
14+
15+
#[CoversClass(ArgvInput::class)]
16+
final class ArgvInputTest extends TestCase
17+
{
18+
/** @return array<string, list<list<string>>> */
19+
public static function argvWithInvalidInputProvider(): array
20+
{
21+
return [
22+
'simple-option' => [['myapp', '--invalid-option', 'myvalue']],
23+
'value-option' => [['myapp', '--invalid-option=foo', 'myvalue']],
24+
'short-option' => [['myapp', '-i', 'myvalue']],
25+
// explicitly not supported for now; we can't tell which is the argument here
26+
// 'split-option' => [['myapp', '--invalid-option', 'foo', 'myvalue']],
27+
];
28+
}
29+
30+
/** @param list<string> $argv */
31+
#[DataProvider('argvWithInvalidInputProvider')]
32+
public function testInvalidOptionsDoNotCauseArgumentsToBeMissed(array $argv): void
33+
{
34+
$definition = new InputDefinition();
35+
$definition->addArgument(new InputArgument('myarg', InputArgument::OPTIONAL));
36+
37+
$argvInput = new ArgvInput($argv);
38+
try {
39+
$argvInput->bind($definition);
40+
self::fail('Expected an exception to be thrown because `--invalid-option` is not defined');
41+
} catch (Throwable) {
42+
// An exception is expected here, because `--invalid-option` was not defined
43+
self::addToAssertionCount(1);
44+
}
45+
46+
// But, crucially, we should have captured the following argument
47+
self::assertSame('myvalue', $argvInput->getArgument('myarg'));
48+
}
49+
}

0 commit comments

Comments
 (0)