RequestParser   A
last analyzed

Complexity

Total Complexity 30

Size/Duplication

Total Lines 151
Duplicated Lines 23.84 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 8
dl 36
loc 151
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B parse() 0 24 4
A onPreParse() 0 4 1
A decodeCall() 0 14 3
A isSingleRequest() 0 4 1
B isValidCall() 18 18 9
B isValidNotification() 18 18 9
A preParse() 0 10 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace PhpJsonRpc\Server;
4
5
use PhpJsonRpc\Common\Interceptor\Interceptor;
6
use PhpJsonRpc\Core\Invoke\AbstractInvoke;
7
use PhpJsonRpc\Core\Invoke\Invoke;
8
use PhpJsonRpc\Core\Invoke\Error;
9
use PhpJsonRpc\Core\Invoke\Notification;
10
use PhpJsonRpc\Core\InvokeSpec;
11
use PhpJsonRpc\Error\InvalidRequestException;
12
use PhpJsonRpc\Error\ParseErrorException;
13
use PhpJsonRpc\Server\RequestParser\ParserContainer;
14
15
/**
16
 * Request parser
17
 */
18
class RequestParser
19
{
20
    /**
21
     * @var Interceptor
22
     */
23
    private $preParse;
24
25
    /**
26
     * RequestParser constructor.
27
     */
28
    public function __construct()
29
    {
30
        $this->preParse = Interceptor::createBase();
31
    }
32
33
    /**
34
     * Parse request data
35
     *
36
     * @param string $data
37
     *
38
     * @return InvokeSpec
39
     */
40
    public function parse(string $data): InvokeSpec
41
    {
42
        $payload = @json_decode($data, true);
43
44
        if (!is_array($payload)) {
45
            return new InvokeSpec([new Error(new ParseErrorException())], true);
46
        }
47
48
        $units = [];
49
50
        // Single request
51
        if ($this->isSingleRequest($payload)) {
52
            $units[] = $this->decodeCall($payload);
53
            return new InvokeSpec($units, true);
54
        }
55
56
        // Batch request
57
        /** @var array $payload */
58
        foreach ($payload as $record) {
59
            $units[] = $this->decodeCall($record);
60
        }
61
62
        return new InvokeSpec($units, false);
63
    }
64
65
    /**
66
     * Get pre-parse chain
67
     *
68
     * @return Interceptor
69
     */
70
    public function onPreParse(): Interceptor
71
    {
72
        return $this->preParse;
73
    }
74
75
    /**
76
     * @param $record
77
     *
78
     * @return AbstractInvoke
79
     */
80
    private function decodeCall($record): AbstractInvoke
81
    {
82
        $record = $this->preParse($record);
83
84
        if ($this->isValidCall($record)) {
85
            $unit = new Invoke($record['id'], $record['method'], $record['params'] ?? []);
86
        } elseif ($this->isValidNotification($record)) {
87
            $unit = new Notification($record['method'], $record['params']);
88
        } else {
89
            $unit = new Error(new InvalidRequestException());
90
        }
91
92
        return $unit;
93
    }
94
95
    /**
96
     * @param array $payload
97
     *
98
     * @return bool
99
     */
100
    private function isSingleRequest(array $payload): bool
101
    {
102
        return array_keys($payload) !== range(0, count($payload) - 1);
103
    }
104
105
    /**
106
     * @param array $payload
107
     *
108
     * @return bool
109
     */
110 View Code Duplication
    private function isValidCall($payload): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
111
    {
112
        if (!is_array($payload)) {
113
            return false;
114
        }
115
116
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
117
        $methodValid = array_key_exists('method', $payload)  && is_string($payload['method']);
118
        $idValid     = array_key_exists('id', $payload);
119
120
        // This member MAY be omitted
121
        $paramsValid = true;
122
        if (array_key_exists('params', $payload) && !is_array($payload['params'])) {
123
            $paramsValid = false;
124
        }
125
126
        return $headerValid && $methodValid && $paramsValid && $idValid;
127
    }
128
129
    /**
130
     * @param array $payload
131
     *
132
     * @return bool
133
     */
134 View Code Duplication
    private function isValidNotification($payload): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
    {
136
        if (!is_array($payload)) {
137
            return false;
138
        }
139
140
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
141
        $methodValid = array_key_exists('method', $payload)  && is_string($payload['method']);
142
        $idValid     = !array_key_exists('id', $payload);
143
144
        // This member MAY be omitted
145
        $paramsValid = true;
146
        if (array_key_exists('params', $payload) && !is_array($payload['params'])) {
147
            $paramsValid = false;
148
        }
149
150
        return $headerValid && $methodValid && $paramsValid && $idValid;
151
    }
152
153
    /**
154
     * @param mixed $record
155
     *
156
     * @return mixed
157
     */
158
    private function preParse($record)
159
    {
160
        $container = $this->preParse->handle(new ParserContainer($this, $record));
161
162
        if ($container instanceof ParserContainer) {
163
            return $container->getValue();
164
        }
165
166
        throw new \RuntimeException();
167
    }
168
}
169