Passed
Pull Request — master (#32)
by Rustam
05:29 queued 03:17
created

RequestModelFactory::parameterIsRequestModel()   A

Complexity

Conditions 6
Paths 7

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6.027

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 11
c 1
b 0
f 0
nc 7
nop 2
dl 0
loc 23
ccs 10
cts 11
cp 0.9091
crap 6.027
rs 9.2222
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\RequestModel;
6
7
use Psr\Http\Message\ServerRequestInterface;
8
use ReflectionClass;
9
use ReflectionException;
10
use ReflectionNamedType;
11
use ReflectionParameter;
12
use ReflectionType;
13
use ReflectionUnionType;
14
use Yiisoft\Injector\Injector;
15
use Yiisoft\Router\CurrentRouteInterface;
16
use Yiisoft\Validator\RulesProviderInterface;
17
use Yiisoft\Validator\ValidatorInterface;
18
19
final class RequestModelFactory
20
{
21
    private Injector $injector;
22
    private ValidatorInterface $validator;
23
    private CurrentRouteInterface $currentRoute;
24
25 16
    public function __construct(ValidatorInterface $validator, Injector $injector, CurrentRouteInterface $currentRoute)
26
    {
27 16
        $this->validator = $validator;
28 16
        $this->injector = $injector;
29 16
        $this->currentRoute = $currentRoute;
30 16
    }
31
32
    /**
33
     * @param ServerRequestInterface $request
34
     * @param ReflectionParameter[] $handlerParameters
35
     *
36
     * @throws ReflectionException
37
     *
38
     * @return array
39
     */
40 10
    public function createInstances(ServerRequestInterface $request, array $handlerParameters): array
41
    {
42 10
        $requestModelInstances = [];
43 10
        foreach ($this->getModelRequestClasses($handlerParameters) as $modelClass) {
44 9
            $requestModelInstances[] = $this->processModel($request, $this->injector->make($modelClass));
45
        }
46
47 9
        return $requestModelInstances;
48
    }
49
50 9
    private function processModel(ServerRequestInterface $request, RequestModelInterface $model): RequestModelInterface
51
    {
52 9
        $requestData = $this->getRequestData($request);
53 9
        $model->setRequestData($requestData);
54 9
        if ($model instanceof RulesProviderInterface) {
55 2
            $result = $this->validator->validate($model, $model->getRules());
56 2
            if (!$result->isValid()) {
57 1
                throw new RequestValidationException($result->getErrors());
58
            }
59
        }
60
61 8
        return $model;
62
    }
63
64
    /**
65
     * @param ReflectionParameter[] $handlerParameters
66
     *
67
     * @throws ReflectionException
68
     *
69
     * @return class-string<RequestModelInterface>[]
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<RequestModelInterface>[] at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<RequestModelInterface>[].
Loading history...
70
     */
71 10
    private function getModelRequestClasses(array $handlerParameters): array
72
    {
73 10
        $modelClasses = [];
74 10
        foreach ($handlerParameters as $parameter) {
75 10
            if ($this->parameterIsRequestModel($parameter, $parameterType)) {
76
                /** @var ReflectionNamedType $parameterType */
77 9
                $parameterName = $parameterType->getName();
78
                /** @var class-string<RequestModelInterface> $parameterName */
79 9
                $modelClasses[] = $parameterName;
80
            }
81
        }
82
83 10
        return $modelClasses;
84
    }
85
86
    /**
87
     * @param ReflectionParameter $parameter
88
     * @param ReflectionType $parameterType
89
     *
90
     * @psalm-suppress UndefinedClass
91
     *
92
     * @throws ReflectionException
93
     *
94
     * @return bool
95
     */
96 10
    private function parameterIsRequestModel(
97
        ReflectionParameter $parameter,
98
        ReflectionType &$parameterType = null
99
    ): bool {
100 10
        if (!$parameter->hasType()) {
101
            return false;
102
        }
103
        /** @var ReflectionNamedType|ReflectionUnionType $reflectionType */
104 10
        $reflectionType = $parameter->getType();
105
106
        /** @var ReflectionNamedType[] $types */
107 10
        $types = $reflectionType instanceof ReflectionNamedType ? [$reflectionType] : $reflectionType->getTypes();
108
109 10
        foreach ($types as $type) {
110
            if (
111 10
                !$type->isBuiltin()
112 10
                && (new ReflectionClass($type->getName()))->implementsInterface(RequestModelInterface::class)
113
            ) {
114 9
                $parameterType = $type;
115 9
                return true;
116
            }
117
        }
118 1
        return false;
119
    }
120
121 9
    private function getRequestData(ServerRequestInterface $request): array
122
    {
123
        return [
124 9
            'query' => $request->getQueryParams(),
125 9
            'body' => $request->getParsedBody(),
126 9
            'attributes' => $request->getAttributes(),
127 9
            'headers' => $request->getHeaders(),
128 9
            'files' => $request->getUploadedFiles(),
129 9
            'cookie' => $request->getCookieParams(),
130 9
            'router' => $this->currentRoute->getArguments(),
131
        ];
132
    }
133
}
134