RequestHeaderParameterResolver::getWeight()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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 Psr\Http\Message\ServerRequestInterface;
18
use ReflectionAttribute;
19
use ReflectionParameter;
20
use Sunrise\Http\Router\Annotation\RequestHeader;
21
use Sunrise\Http\Router\Dictionary\PlaceholderCode;
22
use Sunrise\Http\Router\Exception\HttpException;
23
use Sunrise\Http\Router\Exception\HttpExceptionFactory;
24
use Sunrise\Http\Router\ParameterResolverInterface;
25
use Sunrise\Http\Router\Validation\Constraint\ArgumentConstraint;
26
use Sunrise\Http\Router\Validation\ConstraintViolation\HydratorConstraintViolationAdapter;
27
use Sunrise\Http\Router\Validation\ConstraintViolation\ValidatorConstraintViolationAdapter;
28
use Sunrise\Hydrator\Exception\InvalidDataException;
29
use Sunrise\Hydrator\Exception\InvalidObjectException;
30
use Sunrise\Hydrator\Exception\InvalidValueException;
31
use Sunrise\Hydrator\HydratorInterface;
32
use Sunrise\Hydrator\Type;
33
use Symfony\Component\Validator\Validator\ValidatorInterface;
34
35
use function array_map;
36
37
/**
38
 * @since 3.0.0
39
 */
40
final class RequestHeaderParameterResolver implements ParameterResolverInterface
41
{
42 17
    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 17
    }
52
53
    /**
54
     * @inheritDoc
55
     *
56
     * @throws HttpException
57
     * @throws InvalidObjectException
58
     */
59 16
    public function resolveParameter(ReflectionParameter $parameter, mixed $context): Generator
60
    {
61 16
        if (! $context instanceof ServerRequestInterface) {
62 1
            return;
63
        }
64
65
        /** @var list<ReflectionAttribute<RequestHeader>> $annotations */
66 15
        $annotations = $parameter->getAttributes(RequestHeader::class);
67 15
        if ($annotations === []) {
0 ignored issues
show
introduced by
The condition $annotations === array() is always false.
Loading history...
68 1
            return;
69
        }
70
71 14
        $processParams = $annotations[0]->newInstance();
72 14
        $headerName = $processParams->name;
73 14
        $errorStatusCode = $processParams->errorStatusCode ?? $this->defaultErrorStatusCode;
74 14
        $errorMessage = $processParams->errorMessage ?? $this->defaultErrorMessage;
75 14
        $hydratorContext = $processParams->hydratorContext + $this->hydratorContext;
76 14
        $validationEnabled = $processParams->validationEnabled ?? $this->defaultValidationEnabled;
77
78 14
        if (!$context->hasHeader($headerName)) {
79 2
            if ($parameter->isDefaultValueAvailable()) {
80 1
                return yield $parameter->getDefaultValue();
81
            }
82
83 1
            throw HttpExceptionFactory::missingHeader($errorMessage, $errorStatusCode)
84 1
                ->addMessagePlaceholder(PlaceholderCode::HEADER_NAME, $headerName);
85
        }
86
87
        try {
88 12
            $argument = $this->hydrator->castValue(
89 12
                $context->getHeaderLine($headerName),
90 12
                Type::fromParameter($parameter),
91 12
                path: [$headerName],
92 12
                context: $hydratorContext,
93 12
            );
94 6
        } catch (InvalidValueException $e) {
95 5
            throw HttpExceptionFactory::invalidHeader($errorMessage, $errorStatusCode, previous: $e)
96 5
                ->addMessagePlaceholder(PlaceholderCode::HEADER_NAME, $headerName)
97 5
                ->addConstraintViolation(new HydratorConstraintViolationAdapter($e));
98 1
        } catch (InvalidDataException $e) {
99 1
            throw HttpExceptionFactory::invalidHeader($errorMessage, $errorStatusCode, previous: $e)
100 1
                ->addMessagePlaceholder(PlaceholderCode::HEADER_NAME, $headerName)
101 1
                ->addConstraintViolation(...array_map(
102 1
                    HydratorConstraintViolationAdapter::create(...),
103 1
                    $e->getExceptions(),
104 1
                ));
105
        }
106
107 6
        if ($this->validator !== null && $validationEnabled) {
108 2
            $violations = $this->validator
109 2
                ->startContext()
110 2
                ->atPath($headerName)
111 2
                ->validate($argument, new ArgumentConstraint($parameter))
112 2
                ->getViolations();
113
114 2
            if ($violations->count() > 0) {
115 1
                throw HttpExceptionFactory::invalidHeader($errorMessage, $errorStatusCode)
116 1
                    ->addMessagePlaceholder(PlaceholderCode::HEADER_NAME, $headerName)
117 1
                    ->addConstraintViolation(...array_map(
118 1
                        ValidatorConstraintViolationAdapter::create(...),
119 1
                        [...$violations],
120 1
                    ));
121
            }
122
        }
123
124 5
        yield $argument;
125
    }
126
127 1
    public function getWeight(): int
128
    {
129 1
        return 0;
130
    }
131
}
132