Skip to content

Commit c9d7c63

Browse files
kbondfabpot
authored andcommitted
[Console] Improve #[Argument]/#[Option] exception messages
1 parent 94f4d7a commit c9d7c63

File tree

3 files changed

+28
-26
lines changed

3 files changed

+28
-26
lines changed

src/Symfony/Component/Console/Attribute/Argument.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Argument
2626
private string|bool|int|float|array|null $default = null;
2727
private array|\Closure $suggestedValues;
2828
private ?int $mode = null;
29+
private string $function = '';
2930

3031
/**
3132
* Represents a console command <argument> definition.
@@ -52,17 +53,23 @@ public static function tryFrom(\ReflectionParameter $parameter): ?self
5253
return null;
5354
}
5455

56+
if (($function = $parameter->getDeclaringFunction()) instanceof \ReflectionMethod) {
57+
$self->function = $function->class.'::'.$function->name;
58+
} else {
59+
$self->function = $function->name;
60+
}
61+
5562
$type = $parameter->getType();
5663
$name = $parameter->getName();
5764

5865
if (!$type instanceof \ReflectionNamedType) {
59-
throw new LogicException(\sprintf('The parameter "$%s" must have a named type. Untyped, Union or Intersection types are not supported for command arguments.', $name));
66+
throw new LogicException(\sprintf('The parameter "$%s" of "%s()" must have a named type. Untyped, Union or Intersection types are not supported for command arguments.', $name, $self->function));
6067
}
6168

6269
$parameterTypeName = $type->getName();
6370

6471
if (!\in_array($parameterTypeName, self::ALLOWED_TYPES, true)) {
65-
throw new LogicException(\sprintf('The type "%s" of parameter "$%s" is not supported as a command argument. Only "%s" types are allowed.', $parameterTypeName, $name, implode('", "', self::ALLOWED_TYPES)));
72+
throw new LogicException(\sprintf('The type "%s" on parameter "$%s" of "%s()" is not supported as a command argument. Only "%s" types are allowed.', $parameterTypeName, $name, $self->function, implode('", "', self::ALLOWED_TYPES)));
6673
}
6774

6875
if (!$self->name) {

src/Symfony/Component/Console/Attribute/Option.php

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Option
2929
private ?int $mode = null;
3030
private string $typeName = '';
3131
private bool $allowNull = false;
32+
private string $function = '';
3233

3334
/**
3435
* Represents a console command --option definition.
@@ -57,11 +58,17 @@ public static function tryFrom(\ReflectionParameter $parameter): ?self
5758
return null;
5859
}
5960

61+
if (($function = $parameter->getDeclaringFunction()) instanceof \ReflectionMethod) {
62+
$self->function = $function->class.'::'.$function->name;
63+
} else {
64+
$self->function = $function->name;
65+
}
66+
6067
$name = $parameter->getName();
6168
$type = $parameter->getType();
6269

6370
if (!$parameter->isDefaultValueAvailable()) {
64-
throw new LogicException(\sprintf('The option parameter "$%s" must declare a default value.', $name));
71+
throw new LogicException(\sprintf('The option parameter "$%s" of "%s()" must declare a default value.', $name, $self->function));
6572
}
6673

6774
if (!$self->name) {
@@ -76,21 +83,21 @@ public static function tryFrom(\ReflectionParameter $parameter): ?self
7683
}
7784

7885
if (!$type instanceof \ReflectionNamedType) {
79-
throw new LogicException(\sprintf('The parameter "$%s" must have a named type. Untyped or Intersection types are not supported for command options.', $name));
86+
throw new LogicException(\sprintf('The parameter "$%s" of "%s()" must have a named type. Untyped or Intersection types are not supported for command options.', $name, $self->function));
8087
}
8188

8289
$self->typeName = $type->getName();
8390

8491
if (!\in_array($self->typeName, self::ALLOWED_TYPES, true)) {
85-
throw new LogicException(\sprintf('The type "%s" of parameter "$%s" is not supported as a command option. Only "%s" types are allowed.', $self->typeName, $name, implode('", "', self::ALLOWED_TYPES)));
92+
throw new LogicException(\sprintf('The type "%s" on parameter "$%s" of "%s()" is not supported as a command option. Only "%s" types are allowed.', $self->typeName, $name, $self->function, implode('", "', self::ALLOWED_TYPES)));
8693
}
8794

8895
if ('bool' === $self->typeName && $self->allowNull && \in_array($self->default, [true, false], true)) {
89-
throw new LogicException(\sprintf('The option parameter "$%s" must not be nullable when it has a default boolean value.', $name));
96+
throw new LogicException(\sprintf('The option parameter "$%s" of "%s()" must not be nullable when it has a default boolean value.', $name, $self->function));
9097
}
9198

9299
if ($self->allowNull && null !== $self->default) {
93-
throw new LogicException(\sprintf('The option parameter "$%s" must either be not-nullable or have a default of null.', $name));
100+
throw new LogicException(\sprintf('The option parameter "$%s" of "%s()" must either be not-nullable or have a default of null.', $name, $self->function));
94101
}
95102

96103
if ('bool' === $self->typeName) {
@@ -160,11 +167,11 @@ private function handleUnion(\ReflectionUnionType $type): self
160167
$this->typeName = implode('|', array_filter($types));
161168

162169
if (!\in_array($this->typeName, self::ALLOWED_UNION_TYPES, true)) {
163-
throw new LogicException(\sprintf('The union type for parameter "$%s" is not supported as a command option. Only "%s" types are allowed.', $this->name, implode('", "', self::ALLOWED_UNION_TYPES)));
170+
throw new LogicException(\sprintf('The union type for parameter "$%s" of "%s()" is not supported as a command option. Only "%s" types are allowed.', $this->name, $this->function, implode('", "', self::ALLOWED_UNION_TYPES)));
164171
}
165172

166173
if (false !== $this->default) {
167-
throw new LogicException(\sprintf('The option parameter "$%s" must have a default value of false.', $this->name));
174+
throw new LogicException(\sprintf('The option parameter "$%s" of "%s()" must have a default value of false.', $this->name, $this->function));
168175
}
169176

170177
$this->mode = InputOption::VALUE_OPTIONAL;

src/Symfony/Component/Console/Tests/Command/InvokableCommandTest.php

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,6 @@ public function testInvalidArgumentType()
138138
$command->setCode(function (#[Argument] object $any) {});
139139

140140
$this->expectException(LogicException::class);
141-
$this->expectExceptionMessage('The type "object" of parameter "$any" is not supported as a command argument. Only "string", "bool", "int", "float", "array" types are allowed.');
142141

143142
$command->getDefinition();
144143
}
@@ -149,7 +148,6 @@ public function testInvalidOptionType()
149148
$command->setCode(function (#[Option] ?object $any = null) {});
150149

151150
$this->expectException(LogicException::class);
152-
$this->expectExceptionMessage('The type "object" of parameter "$any" is not supported as a command option. Only "string", "bool", "int", "float", "array" types are allowed.');
153151

154152
$command->getDefinition();
155153
}
@@ -322,54 +320,44 @@ public static function provideNonBinaryInputOptions(): \Generator
322320
/**
323321
* @dataProvider provideInvalidOptionDefinitions
324322
*/
325-
public function testInvalidOptionDefinition(callable $code, string $expectedMessage)
323+
public function testInvalidOptionDefinition(callable $code)
326324
{
327325
$command = new Command('foo');
328326
$command->setCode($code);
329327

330328
$this->expectException(LogicException::class);
331-
$this->expectExceptionMessage($expectedMessage);
332329

333330
$command->getDefinition();
334331
}
335332

336333
public static function provideInvalidOptionDefinitions(): \Generator
337334
{
338335
yield 'no-default' => [
339-
function (#[Option] string $a) {},
340-
'The option parameter "$a" must declare a default value.',
336+
function (#[Option] string $a) {}
341337
];
342338
yield 'nullable-bool-default-true' => [
343-
function (#[Option] ?bool $a = true) {},
344-
'The option parameter "$a" must not be nullable when it has a default boolean value.',
339+
function (#[Option] ?bool $a = true) {}
345340
];
346341
yield 'nullable-bool-default-false' => [
347-
function (#[Option] ?bool $a = false) {},
348-
'The option parameter "$a" must not be nullable when it has a default boolean value.',
342+
function (#[Option] ?bool $a = false) {}
349343
];
350344
yield 'invalid-union-type' => [
351-
function (#[Option] array|bool $a = false) {},
352-
'The union type for parameter "$a" is not supported as a command option. Only "bool|string", "bool|int", "bool|float" types are allowed.',
345+
function (#[Option] array|bool $a = false) {}
353346
];
354347
yield 'union-type-cannot-allow-null' => [
355348
function (#[Option] string|bool|null $a = null) {},
356-
'The union type for parameter "$a" is not supported as a command option. Only "bool|string", "bool|int", "bool|float" types are allowed.',
357349
];
358350
yield 'union-type-default-true' => [
359351
function (#[Option] string|bool $a = true) {},
360-
'The option parameter "$a" must have a default value of false.',
361352
];
362353
yield 'union-type-default-string' => [
363354
function (#[Option] string|bool $a = 'foo') {},
364-
'The option parameter "$a" must have a default value of false.',
365355
];
366356
yield 'nullable-string-not-null-default' => [
367357
function (#[Option] ?string $a = 'foo') {},
368-
'The option parameter "$a" must either be not-nullable or have a default of null.',
369358
];
370359
yield 'nullable-array-not-null-default' => [
371360
function (#[Option] ?array $a = []) {},
372-
'The option parameter "$a" must either be not-nullable or have a default of null.',
373361
];
374362
}
375363

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy