Passed
Push — master ( 081b04...39be92 )
by Marcel
01:57
created

Server::end()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

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