Passed
Pull Request — master (#32)
by Rustam
02:26
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\CurrentRoute;
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 CurrentRoute $currentRoute;
24
25 16
    public function __construct(ValidatorInterface $validator, Injector $injector, CurrentRoute $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
     * @psalm-return class-string<RequestModelInterface>[]
70
     *
71
     * @return string[]
72
     */
73 10
    private function getModelRequestClasses(array $handlerParameters): array
74
    {
75 10
        $modelClasses = [];
76 10
        foreach ($handlerParameters as $parameter) {
77 10
            if ($this->parameterIsRequestModel($parameter, $parameterType)) {
78
                /** @var ReflectionNamedType $parameterType */
79 9
                $parameterName = $parameterType->getName();
80
                /** @var class-string<RequestModelInterface> $parameterName */
81 9
                $modelClasses[] = $parameterName;
82
            }
83
        }
84
85 10
        return $modelClasses;
86
    }
87
88
    /**
89
     * @param ReflectionParameter $parameter
90
     * @param ReflectionType $parameterType
91
     *
92
     * @psalm-suppress UndefinedClass
93
     *
94
     * @throws ReflectionException
95
     *
96
     * @return bool
97
     */
98 10
    private function parameterIsRequestModel(
99
        ReflectionParameter $parameter,
100
        ReflectionType &$parameterType = null
101
    ): bool {
102 10
        if (!$parameter->hasType()) {
103
            return false;
104
        }
105
        /** @var ReflectionNamedType|ReflectionUnionType $reflectionType */
106 10
        $reflectionType = $parameter->getType();
107
108
        /** @var ReflectionNamedType[] $types */
109 10
        $types = $reflectionType instanceof ReflectionNamedType ? [$reflectionType] : $reflectionType->getTypes();
110
111 10
        foreach ($types as $type) {
112
            if (
113 10
                !$type->isBuiltin()
114 10
                && (new ReflectionClass($type->getName()))->implementsInterface(RequestModelInterface::class)
115
            ) {
116 9
                $parameterType = $type;
117 9
                return true;
118
            }
119
        }
120 1
        return false;
121
    }
122
123 9
    private function getRequestData(ServerRequestInterface $request): array
124
    {
125
        return [
126 9
            'query' => $request->getQueryParams(),
127 9
            'body' => $request->getParsedBody(),
128 9
            'attributes' => $request->getAttributes(),
129 9
            'headers' => $request->getHeaders(),
130 9
            'files' => $request->getUploadedFiles(),
131 9
            'cookie' => $request->getCookieParams(),
132 9
            'router' => $this->currentRoute->getArguments(),
133
        ];
134
    }
135
}
136