Server   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 141
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 58
c 1
b 0
f 0
dl 0
loc 141
rs 10
wmc 30

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getData() 0 8 3
C run() 0 41 14
B validate() 0 11 7
A getResponse() 0 20 4
A getRawContent() 0 3 1
1
<?php
2
3
namespace veejay\jsonrpc;
4
5
use Error;
6
use veejay\jsonrpc\batch\Request;
7
use veejay\jsonrpc\batch\Response;
8
use veejay\jsonrpc\exception\Exception;
9
10
class Server
11
{
12
    /**
13
     * Api for responses.
14
     * @var Api
15
     */
16
    protected $api;
17
18
    /**
19
     * Max amount of requests.
20
     * 0 - unlimited.
21
     * @var int|null
22
     */
23
    protected $limit;
24
25
    /**
26
     * @param Api $api
27
     * @param int $limit
28
     */
29
    public function __construct(Api $api, int $limit = 0)
30
    {
31
        $this->api = $api;
32
        $this->limit = $limit;
33
    }
34
35
    /**
36
     * Run server.
37
     * @return string
38
     */
39
    public function run()
40
    {
41
        try {
42
            $data = $this->getData();
43
            $multiple = is_array($data);
44
            if (is_object($data)) {
45
                $data = [$data];
46
            }
47
48
            if (empty($data)) {
49
                throw new Exception(Response::INVALID_REQUEST);
50
            } elseif ($this->limit !== 0 && $this->limit < count($data)) {
51
                throw new Exception(Response::LIMIT_EXCEEDED);
52
            }
53
54
            $responses = [];
55
            foreach ((array)$data as $datum) {
56
                $request = (new Request)->setProperties($datum);
57
                $response = $this->getResponse($request);
58
                if ($response->isNotification() && !$response->hasError()) {
59
                    continue;
60
                }
61
                $responses[] = $response->getData();
62
            }
63
64
            // Check if ID already exists
65
            $ids = [];
66
            foreach ($responses as $response) {
67
                if ($response->id === null) continue;
68
                if (in_array($response->id, $ids, true)) {
69
                    throw new Exception(Response::DUPLICATED_ID);
70
                }
71
                array_push($ids, $response->id);
72
            }
73
74
        } catch (Exception $exception) {
75
            return (string)$exception;
76
        }
77
78
        if (empty($responses)) return '';
79
        return json_encode($multiple ? $responses : current($responses));
80
    }
81
82
    /**
83
     * Create Response object for Request.
84
     * @param Request $request
85
     * @return Response
86
     */
87
    protected function getResponse(Request $request): Response
88
    {
89
        $response = new Response;
90
        $response->jsonrpc = Response::VERSION;
91
        $response->id = $request->id;
92
93
        $this->validate($request, $response);
94
        if ($response->hasError()) {
95
            return $response;
96
        }
97
98
        try {
99
            $response->result = call_user_func_array([$this->api, $request->method], [(array)$request->params]);
100
        } catch (Exception $e) {
101
            $response->setError($e->getCode(), $e->getMessage());
102
        } catch (Error $e) {
103
            $response->setError(Response::INTERNAL_ERROR);
104
        }
105
106
        return $response;
107
    }
108
109
    /**
110
     * Validate Request and set error.
111
     * @param Request $request
112
     * @param Response $response
113
     * @return void
114
     */
115
    protected function validate(Request $request, Response $response)
116
    {
117
        $type = gettype($request->id);
118
        if (
119
            $request->jsonrpc !== Response::VERSION ||
120
            !is_string($request->method) ||
121
            !in_array($type, ['NULL', 'integer', 'string'])
122
        ) {
123
            $response->setError(Response::INVALID_REQUEST);
124
        } elseif (!is_array($request->params) && !is_object($request->params) && !is_null($request->params)) {
125
            $response->setError(Response::INVALID_PARAMS);
126
        }
127
    }
128
129
    /**
130
     * Get data from request.
131
     * @return mixed
132
     * @throws Exception
133
     */
134
    protected function getData()
135
    {
136
        $content = $this->getRawContent();
137
        $data = is_string($content) ? @json_decode($content) : null;
138
        if ($data === null) {
139
            throw new Exception(Response::PARSE_ERROR);
140
        }
141
        return $data;
142
    }
143
144
    /**
145
     * Read raw content.
146
     * @return string|false
147
     */
148
    protected function getRawContent()
149
    {
150
        return @file_get_contents('php://input');
151
    }
152
}
153