Completed
Branch middlewares (26113e)
by Marcel
02:58
created

Server::pipe()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace UMA\JsonRpc;
6
7
use Psr\Container\ContainerExceptionInterface;
8
use Psr\Container\ContainerInterface;
9
use Psr\Container\NotFoundExceptionInterface;
10
use UMA\JsonRpc\Internal\Pipeliner;
11
use UMA\JsonRpc\Internal\Validator;
12
use UMA\JsonRpc\Internal\Input;
13
14
class Server
15
{
16
    /**
17
     * @var ContainerInterface
18
     */
19
    private $container;
20
21
    /**
22
     * @var string[]
23
     */
24
    private $methods;
25
26
    /**
27
     * @var Middleware[]
28
     */
29
    private $middlewares;
30
31
    /**
32
     * @var int|null
33
     */
34
    private $batchLimit;
35
36 22
    public function __construct(ContainerInterface $container, int $batchLimit = null)
37
    {
38 22
        $this->container = $container;
39 22
        $this->batchLimit = $batchLimit;
40 22
        $this->methods = [];
41 22
        $this->middlewares = [];
42 22
    }
43
44 21
    public function set(string $method, string $serviceId): Server
45
    {
46 21
        if (!$this->container->has($serviceId)) {
47 1
            throw new \LogicException("Cannot find service '$serviceId' in the container");
48
        }
49
50 20
        $this->methods[$method] = $serviceId;
51
52 20
        return $this;
53
    }
54
55 3
    public function pipe(string $serviceId): Server
56
    {
57 3
        if (!$this->container->has($serviceId)) {
58 1
            throw new \LogicException("Cannot find service '$serviceId' in the container");
59
        }
60
61 2
        $this->middlewares[$serviceId] = null;
62
63 2
        return $this;
64
    }
65
66 20
    public function run(string $raw): ?string
67
    {
68 20
        $input = Input::fromString($raw);
69
70 20
        if (!$input->parsable()) {
71 2
            return self::end(Error::parsing());
72
        }
73
74 18
        if ($input->isArray()) {
75 5
            return $this->batch($input);
76
        }
77
78 13
        return $this->single($input);
79
    }
80
81 5
    private function batch(Input $input): ?string
82
    {
83 5
        \assert($input->isArray());
84
85 5
        if ($this->tooManyBatchRequests($input)) {
86 1
            return self::end(Error::tooManyBatchRequests($this->batchLimit));
87
        }
88
89 4
        $responses = [];
90 4
        foreach ($input->data() as $request) {
91 4
            $pseudoInput = Input::fromSafeData($request);
92
93 4
            if (null !== $response = $this->single($pseudoInput)) {
94 4
                $responses[] = $response;
95
            }
96
        }
97
98 4
        return empty($responses) ?
99 4
            null : \sprintf('[%s]', \implode(',', $responses));
100
    }
101
102 17
    private function single(Input $input): ?string
103
    {
104 17
        if (!$input->isRpcRequest()) {
105 5
            return self::end(Error::invalidRequest());
106
        }
107
108 13
        $request = new Request($input);
109
110 13
        if (!array_key_exists($request->method(), $this->methods)) {
111 3
            return self::end(Error::unknownMethod($request->id()), $request);
112
        }
113
114
        try {
115 12
            $procedure = $this->container->get($this->methods[$request->method()]);
116 1
        } catch (ContainerExceptionInterface | NotFoundExceptionInterface $e) {
117 1
            return self::end(Error::internal($request->id()), $request);
118
        }
119
120 11
        if (!$procedure instanceof Procedure) {
121 1
            return self::end(Error::internal($request->id()), $request);
122
        }
123
124 10
        $spec = $procedure->getSpec();
125
126 10
        if ($spec instanceof \stdClass && !Validator::validate($spec, $request->params())) {
127 1
            return self::end(Error::invalidParams($request->id()), $request);
128
        }
129
130
        try {
131 9
            $pipeline = Pipeliner::build($this->container, $procedure, $this->middlewares);
0 ignored issues
show
Documentation introduced by
$this->middlewares is of type array<integer,object<UMA\JsonRpc\Middleware>>, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
132 1
        } catch (\Throwable $t) {
133 1
            return self::end(Error::internal($request->id()), $request);
134
        }
135
136 8
        return self::end($pipeline($request), $request);
137
    }
138
139 5
    private function tooManyBatchRequests(Input $input): bool
140
    {
141 5
        \assert($input->isArray());
142
143 5
        return \is_int($this->batchLimit) && $this->batchLimit < \count($input->data());
144
    }
145
146 20
    private static function end(Response $response, Request $request = null): ?string
147
    {
148 20
        return $request instanceof Request && null === $request->id() ?
149 20
            null : \json_encode($response);
150
    }
151
}
152