Skip to content

Commit ec06f39

Browse files
committed
Adding the ArgumentResolver component
1 parent 8c941de commit ec06f39

File tree

59 files changed

+2070
-136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+2070
-136
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.git* export-ignore

src/Symfony/Component/ArgumentResolver/.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Symfony/Component/ArgumentResolver/.github/workflows/close-pull-request.yml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ArgumentResolver\ArgumentMetadata;
13+
14+
/**
15+
* Responsible for storing metadata of an argument.
16+
*
17+
* @author Iltar van der Berg <[email protected]>
18+
*
19+
* @final
20+
*/
21+
class ArgumentMetadata
22+
{
23+
public const IS_INSTANCEOF = 2;
24+
25+
/**
26+
* @param object[] $attributes
27+
*/
28+
public function __construct(
29+
private string $name,
30+
private ?string $type,
31+
private bool $isVariadic,
32+
private bool $hasDefaultValue,
33+
private mixed $defaultValue,
34+
private bool $isNullable = false,
35+
private array $attributes = [],
36+
private string $controllerName = 'n/a',
37+
) {
38+
$this->isNullable = $isNullable || null === $type || ($hasDefaultValue && null === $defaultValue);
39+
}
40+
41+
/**
42+
* Returns the name as given in PHP, $foo would yield "foo".
43+
*/
44+
public function getName(): string
45+
{
46+
return $this->name;
47+
}
48+
49+
/**
50+
* Returns the type of the argument.
51+
*
52+
* The type is the PHP class in 5.5+ and additionally the basic type in PHP 7.0+.
53+
*/
54+
public function getType(): ?string
55+
{
56+
return $this->type;
57+
}
58+
59+
/**
60+
* Returns whether the argument is defined as "...$variadic".
61+
*/
62+
public function isVariadic(): bool
63+
{
64+
return $this->isVariadic;
65+
}
66+
67+
/**
68+
* Returns whether the argument has a default value.
69+
*
70+
* Implies whether an argument is optional.
71+
*/
72+
public function hasDefaultValue(): bool
73+
{
74+
return $this->hasDefaultValue;
75+
}
76+
77+
/**
78+
* Returns whether the argument accepts null values.
79+
*/
80+
public function isNullable(): bool
81+
{
82+
return $this->isNullable;
83+
}
84+
85+
/**
86+
* Returns the default value of the argument.
87+
*
88+
* @throws \LogicException if no default value is present; {@see self::hasDefaultValue()}
89+
*/
90+
public function getDefaultValue(): mixed
91+
{
92+
if (!$this->hasDefaultValue) {
93+
throw new \LogicException(\sprintf('Argument $%s does not have a default value. Use "%s::hasDefaultValue()" to avoid this exception.', $this->name, __CLASS__));
94+
}
95+
96+
return $this->defaultValue;
97+
}
98+
99+
/**
100+
* @param class-string $name
101+
* @param self::IS_INSTANCEOF|0 $flags
102+
*
103+
* @return array<object>
104+
*/
105+
public function getAttributes(?string $name = null, int $flags = 0): array
106+
{
107+
if (!$name) {
108+
return $this->attributes;
109+
}
110+
111+
return $this->getAttributesOfType($name, $flags);
112+
}
113+
114+
/**
115+
* @template T of object
116+
*
117+
* @param class-string<T> $name
118+
* @param self::IS_INSTANCEOF|0 $flags
119+
*
120+
* @return array<T>
121+
*/
122+
public function getAttributesOfType(string $name, int $flags = 0): array
123+
{
124+
$attributes = [];
125+
if ($flags & self::IS_INSTANCEOF) {
126+
foreach ($this->attributes as $attribute) {
127+
if ($attribute instanceof $name) {
128+
$attributes[] = $attribute;
129+
}
130+
}
131+
} else {
132+
foreach ($this->attributes as $attribute) {
133+
if ($attribute::class === $name) {
134+
$attributes[] = $attribute;
135+
}
136+
}
137+
}
138+
139+
return $attributes;
140+
}
141+
142+
public function getControllerName(): string
143+
{
144+
return $this->controllerName;
145+
}
146+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ArgumentResolver\ArgumentMetadata;
13+
14+
/**
15+
* Builds {@see ArgumentMetadata} objects based on the given Controller.
16+
*
17+
* @author Iltar van der Berg <[email protected]>
18+
*/
19+
final class ArgumentMetadataFactory implements ArgumentMetadataFactoryInterface
20+
{
21+
public function createArgumentMetadata(string|object|array $callable, ?\ReflectionFunctionAbstract $reflector = null): array
22+
{
23+
$arguments = [];
24+
$reflector ??= new \ReflectionFunction($callable(...));
25+
$callableName = $this->getPrettyName($reflector);
26+
27+
foreach ($reflector->getParameters() as $param) {
28+
$attributes = [];
29+
foreach ($param->getAttributes() as $reflectionAttribute) {
30+
if (class_exists($reflectionAttribute->getName())) {
31+
$attributes[] = $reflectionAttribute->newInstance();
32+
}
33+
}
34+
35+
$arguments[] = new ArgumentMetadata($param->getName(), $this->getType($param), $param->isVariadic(), $param->isDefaultValueAvailable(), $param->isDefaultValueAvailable() ? $param->getDefaultValue() : null, $param->allowsNull(), $attributes, $callableName);
36+
}
37+
38+
return $arguments;
39+
}
40+
41+
/**
42+
* Returns an associated type to the given parameter if available.
43+
*/
44+
private function getType(\ReflectionParameter $parameter): ?string
45+
{
46+
if (!$type = $parameter->getType()) {
47+
return null;
48+
}
49+
$name = $type instanceof \ReflectionNamedType ? $type->getName() : (string) $type;
50+
51+
return match (strtolower($name)) {
52+
'self' => $parameter->getDeclaringClass()?->name,
53+
'parent' => get_parent_class($parameter->getDeclaringClass()?->name ?? '') ?: null,
54+
default => $name,
55+
};
56+
}
57+
58+
private function getPrettyName(\ReflectionFunctionAbstract $r): string
59+
{
60+
$name = $r->name;
61+
62+
if ($r instanceof \ReflectionMethod) {
63+
return $r->class.'::'.$name;
64+
}
65+
66+
if ($r->isAnonymous() || !$class = $r->getClosureCalledClass()) {
67+
return $name;
68+
}
69+
70+
return $class->name.'::'.$name;
71+
}
72+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ArgumentResolver\ArgumentMetadata;
13+
14+
/**
15+
* Builds method argument data.
16+
*
17+
* @author Iltar van der Berg <[email protected]>
18+
*/
19+
interface ArgumentMetadataFactoryInterface
20+
{
21+
/**
22+
* @return ArgumentMetadata[]
23+
*/
24+
public function createArgumentMetadata(string|object|array $callable, ?\ReflectionFunctionAbstract $reflector = null): array;
25+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ArgumentResolver;
13+
14+
/**
15+
* An ArgumentResolverInterface instance knows how to determine the
16+
* arguments for a specific function.
17+
*
18+
* @author Robin Chalas <[email protected]>
19+
* @author Fabien Potencier <[email protected]>
20+
*/
21+
interface ArgumentResolverInterface
22+
{
23+
/**
24+
* Returns the arguments to pass to the callable.
25+
*
26+
* @throws \RuntimeException When no value could be provided for a required argument
27+
*/
28+
public function getArguments(callable $callable, ?\ReflectionFunctionAbstract $reflector = null): array;
29+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CHANGELOG
2+
=========
3+
4+
6.2
5+
---
6+
7+
* Use `SensitiveParameter` attribute to redact sensitive values in back traces
8+
9+
5.3
10+
---
11+
12+
* Add the component
13+
* Use `bcrypt` as default algorithm in `NativeArgumentResolver`
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\ArgumentResolver\Exception;
13+
14+
/**
15+
* Interface for exceptions thrown by the argument-resolver component.
16+
*
17+
* @author Robin Chalas <[email protected]>
18+
*/
19+
interface ExceptionInterface extends \Throwable
20+
{
21+
}

0 commit comments

Comments
 (0)