Router::resolveResourceIdentity()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2.0078

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 2
eloc 8
c 3
b 0
f 0
nc 2
nop 2
dl 0
loc 13
ccs 7
cts 8
cp 0.875
crap 2.0078
rs 10
1
<?php
2
/**
3
 * Copyright (c) 2020.
4
 * @author Paweł Antosiak <[email protected]>
5
 */
6
7
declare(strict_types=1);
8
9
namespace Gorynych\Http\Routing;
10
11
use Cake\Collection\Collection;
12
use Gorynych\Http\Exception\MethodNotAllowedHttpException;
13
use Gorynych\Http\Exception\NotFoundHttpException;
14
use Gorynych\Operation\ResourceOperationInterface;
15
use Gorynych\Resource\AbstractResource;
16
use Gorynych\Resource\ResourceLoader;
17
use Symfony\Component\HttpFoundation\Request;
18
19
final class Router
20
{
21
    private ResourceLoader $resourceLoader;
22
    private ?Request $request;
23
24 3
    public function __construct(ResourceLoader $resourceLoader)
25
    {
26 3
        $this->resourceLoader = $resourceLoader;
27 3
    }
28
29
    /**
30
     * Finds resource operation able to process given request
31
     *
32
     * @param Request $request
33
     * @return ResourceOperationInterface
34
     * @throws NotFoundHttpException if there is no proper operation mapped to any resource
35
     */
36 3
    public function findOperation(Request $request): ResourceOperationInterface
37
    {
38 3
        $this->request = $request;
39 3
        $resource = $operation = null;
40
41 3
        foreach ($this->resourceLoader->getResources() as $resourceClass) {
42
            try {
43 3
                $resource = $this->resourceLoader->loadResource($resourceClass);
44 3
                $operation = $this->filterOperationsByMethod($this->filterOperationsByUri($resource));
45
46 1
                break;
47 3
            } catch (NotFoundHttpException $e) {
48 3
                continue;
49
            }
50
        }
51
52 2
        if (!$operation instanceof ResourceOperationInterface) {
53 1
            throw new NotFoundHttpException();
54
        }
55
56 1
        $this->resolveResourceIdentity($resource, $operation);
0 ignored issues
show
Bug introduced by
It seems like $resource can also be of type null; however, parameter $resource of Gorynych\Http\Routing\Ro...solveResourceIdentity() does only seem to accept Gorynych\Resource\AbstractResource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

56
        $this->resolveResourceIdentity(/** @scrutinizer ignore-type */ $resource, $operation);
Loading history...
57
58 1
        return $operation;
59
    }
60
61
    /**
62
     * Returns resource operations matching request URI
63
     *
64
     * @param AbstractResource $resource
65
     * @return Collection
66
     * @throws NotFoundHttpException if even one operation does not match URI pattern
67
     */
68 3
    private function filterOperationsByUri(AbstractResource $resource): Collection
69
    {
70 3
        $request = $this->request;
71
72 3
        $operations = $resource->getOperations()->filter(
73 3
            static function (ResourceOperationInterface $operation) use ($request, $resource): bool {
74 3
                return UriMatcher::matchUri($request->getPathInfo(), $resource->getPath(), $operation->getPath());
0 ignored issues
show
Bug introduced by
The method getPathInfo() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

74
                return UriMatcher::matchUri($request->/** @scrutinizer ignore-call */ getPathInfo(), $resource->getPath(), $operation->getPath());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
75 3
            }
76
        );
77
78 3
        if (true === $operations->isEmpty()) {
79 3
            throw new NotFoundHttpException();
80
        }
81
82 2
        return $operations;
83
    }
84
85
    /**
86
     * Returns resource operation matching request HTTP method
87
     *
88
     * @param Collection $operations matching request URI
89
     * @return ResourceOperationInterface
90
     * @throws MethodNotAllowedHttpException if method does not match the method of any available operations
91
     */
92 2
    private function filterOperationsByMethod(Collection $operations): ResourceOperationInterface
93
    {
94 2
        $method = $this->request->getMethod();
0 ignored issues
show
Bug introduced by
The method getMethod() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

94
        /** @scrutinizer ignore-call */ 
95
        $method = $this->request->getMethod();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
95
96 2
        $operations = $operations->filter(
97 2
            static function (ResourceOperationInterface $operation) use ($method): bool {
98 2
                return $method === $operation->getMethod();
99 2
            }
100
        );
101
102 2
        if (true === $operations->isEmpty()) {
103 1
            throw new MethodNotAllowedHttpException();
104
        }
105
106 1
        return $operations->first();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $operations->first() could return the type null which is incompatible with the type-hinted return Gorynych\Operation\ResourceOperationInterface. Consider adding an additional type-check to rule them out.
Loading history...
107
    }
108
109
    /**
110
     * Resolves identity of the resource
111
     */
112 1
    private function resolveResourceIdentity(AbstractResource $resource, ResourceOperationInterface $operation): void
113
    {
114 1
        UriMatcher::matchUri(
115 1
            $this->request->getPathInfo(),
116 1
            $resource->getPath(),
117 1
            $operation->getPath(),
118
            $matches
119
        );
120
121 1
        $resource->id = $matches['id'] ?? null;
122
123 1
        if (true === property_exists($resource, 'subId')) {
124
            $resource->subId = $matches['subId'] ?? null;
125
        }
126 1
    }
127
}
128