Issues (125)

ParameterResolver/RequestBodyParameterResolver.php (1 issue)

Severity
1
<?php
2
3
/**
4
 * It's free open-source software released under the MIT License.
5
 *
6
 * @author Anatoly Nekhay <[email protected]>
7
 * @copyright Copyright (c) 2018, Anatoly Nekhay
8
 * @license https://github.com/sunrise-php/http-router/blob/master/LICENSE
9
 * @link https://github.com/sunrise-php/http-router
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sunrise\Http\Router\ParameterResolver;
15
16
use Generator;
17
use InvalidArgumentException;
18
use Psr\Http\Message\ServerRequestInterface;
19
use ReflectionAttribute;
20
use ReflectionNamedType;
21
use ReflectionParameter;
22
use Sunrise\Http\Router\Annotation\RequestBody;
23
use Sunrise\Http\Router\Exception\HttpException;
24
use Sunrise\Http\Router\Exception\HttpExceptionFactory;
25
use Sunrise\Http\Router\ParameterResolverChain;
26
use Sunrise\Http\Router\ParameterResolverInterface;
27
use Sunrise\Http\Router\Validation\ConstraintViolation\HydratorConstraintViolationAdapter;
28
use Sunrise\Http\Router\Validation\ConstraintViolation\ValidatorConstraintViolationAdapter;
29
use Sunrise\Hydrator\Exception\InvalidDataException;
30
use Sunrise\Hydrator\Exception\InvalidObjectException;
31
use Sunrise\Hydrator\HydratorInterface;
32
use Symfony\Component\Validator\Validator\ValidatorInterface;
33
34
use function array_map;
35
use function sprintf;
36
37
/**
38
 * @since 3.0.0
39
 */
40
final class RequestBodyParameterResolver implements ParameterResolverInterface
41
{
42 16
    public function __construct(
43
        private readonly HydratorInterface $hydrator,
44
        private readonly ?ValidatorInterface $validator = null,
45
        private readonly ?int $defaultErrorStatusCode = null,
46
        private readonly ?string $defaultErrorMessage = null,
47
        /** @var array<string, mixed> */
48
        private readonly array $hydratorContext = [],
49
        private readonly bool $defaultValidationEnabled = true,
50
    ) {
51 16
    }
52
53
    /**
54
     * @inheritDoc
55
     *
56
     * @throws HttpException
57
     * @throws InvalidArgumentException
58
     * @throws InvalidObjectException
59
     */
60 15
    public function resolveParameter(ReflectionParameter $parameter, mixed $context): Generator
61
    {
62 15
        if (! $context instanceof ServerRequestInterface) {
63 1
            return;
64
        }
65
66
        /** @var list<ReflectionAttribute<RequestBody>> $annotations */
67 14
        $annotations = $parameter->getAttributes(RequestBody::class);
68 14
        if ($annotations === []) {
0 ignored issues
show
The condition $annotations === array() is always false.
Loading history...
69 1
            return;
70
        }
71
72 13
        $type = $parameter->getType();
73 13
        if (! $type instanceof ReflectionNamedType || $type->isBuiltin()) {
74 2
            throw new InvalidArgumentException(sprintf(
75 2
                'To use the #[RequestBody] annotation, the parameter "%s" must be typed with an object.',
76 2
                ParameterResolverChain::stringifyParameter($parameter),
77 2
            ));
78
        }
79
80
        /** @var class-string $className */
81 11
        $className = $type->getName();
82 11
        $processParams = $annotations[0]->newInstance();
83 11
        $errorStatusCode = $processParams->errorStatusCode ?? $this->defaultErrorStatusCode;
84 11
        $errorMessage = $processParams->errorMessage ?? $this->defaultErrorMessage;
85 11
        $hydratorContext = $processParams->hydratorContext + $this->hydratorContext;
86 11
        $validationEnabled = $processParams->validationEnabled ?? $this->defaultValidationEnabled;
87
88
        try {
89 11
            $argument = $this->hydrator->hydrate(
90 11
                $className,
91 11
                (array) $context->getParsedBody(),
92 11
                context: $hydratorContext,
93 11
            );
94 5
        } catch (InvalidDataException $e) {
95 5
            throw HttpExceptionFactory::invalidBody($errorMessage, $errorStatusCode, previous: $e)
96 5
                ->addConstraintViolation(...array_map(
97 5
                    HydratorConstraintViolationAdapter::create(...),
98 5
                    $e->getExceptions(),
99 5
                ));
100
        }
101
102 6
        if ($this->validator !== null && $validationEnabled) {
103 2
            $violations = $this->validator->validate($argument);
104 2
            if ($violations->count() > 0) {
105 1
                throw HttpExceptionFactory::invalidBody($errorMessage, $errorStatusCode)
106 1
                    ->addConstraintViolation(...array_map(
107 1
                        ValidatorConstraintViolationAdapter::create(...),
108 1
                        [...$violations],
109 1
                    ));
110
            }
111
        }
112
113 5
        yield $argument;
114
    }
115
116 1
    public function getWeight(): int
117
    {
118 1
        return 0;
119
    }
120
}
121