Completed
Push — master ( 50031e...8ec7ac )
by Denis
02:08
created

RequestParser::parse()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 23
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 8.7972
c 0
b 0
f 0
cc 4
eloc 11
nc 4
nop 1
1
<?php
2
3
namespace PhpJsonRpc\Server;
4
5
use PhpJsonRpc\Core\Call\AbstractCall;
6
use PhpJsonRpc\Core\Call\CallUnit;
7
use PhpJsonRpc\Core\Call\CallError;
8
use PhpJsonRpc\Core\Call\CallNotification;
9
use PhpJsonRpc\Core\CallSpecifier;
10
use PhpJsonRpc\Error\InvalidRequestException;
11
use PhpJsonRpc\Error\ParseErrorException;
12
13
/**
14
 * Request parser
15
 */
16
class RequestParser
17
{
18
    /**
19
     * Parse request data
20
     *
21
     * @param string $data
22
     * @return CallSpecifier
23
     * @throws ParseErrorException
24
     */
25
    public function parse(string $data): CallSpecifier
26
    {
27
        $payload = @json_decode($data, true);
28
29
        if (!is_array($payload)) {
30
            return new CallSpecifier([new CallError(new ParseErrorException())], true);
31
        }
32
33
        $units = [];
34
35
        // Single request
36
        if ($this->isSingleRequest($payload)) {
37
            $units[] = $this->decodeCall($payload);
38
            return new CallSpecifier($units, true);
39
        }
40
41
        // Batch request
42
        foreach ($payload as $record) {
43
            $units[] = $this->decodeCall($record);
44
        }
45
46
        return new CallSpecifier($units, false);
47
    }
48
49
    /**
50
     * @param $record
51
     *
52
     * @return AbstractCall
53
     */
54
    private function decodeCall($record): AbstractCall
55
    {
56
        if ($this->isValidCall($record)) {
57
            $unit = new CallUnit($record['id'], $record['method'], $record['params'] ?? []);
58
        } elseif ($this->isValidNotification($record)) {
59
            $unit = new CallNotification($record['method'], $record['params']);
60
        } else {
61
            $unit = new CallError(new InvalidRequestException());
62
        }
63
64
        return $unit;
65
    }
66
67
    /**
68
     * @param array $payload
69
     * @return bool
70
     */
71
    private function isSingleRequest(array $payload): bool
72
    {
73
        return array_keys($payload) !== range(0, count($payload) - 1);
74
    }
75
76
    /**
77
     * @param array $payload
78
     * @return bool
79
     */
80 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...
81
    {
82
        if (!is_array($payload)) {
83
            return false;
84
        }
85
86
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
87
        $methodValid = array_key_exists('method', $payload)  && is_string($payload['method']);
88
        $idValid     = array_key_exists('id', $payload);
89
90
        // This member MAY be omitted
91
        $paramsValid = true;
92
        if (array_key_exists('params', $payload) && !is_array($payload['params'])) {
93
            $paramsValid = false;
94
        }
95
96
        return $headerValid && $methodValid && $paramsValid && $idValid;
97
    }
98
99
    /**
100
     * @param array $payload
101
     * @return bool
102
     */
103 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...
104
    {
105
        if (!is_array($payload)) {
106
            return false;
107
        }
108
109
        $headerValid = array_key_exists('jsonrpc', $payload) && $payload['jsonrpc'] === '2.0';
110
        $methodValid = array_key_exists('method', $payload)  && is_string($payload['method']);
111
        $idValid     = !array_key_exists('id', $payload);
112
113
        // This member MAY be omitted
114
        $paramsValid = true;
115
        if (array_key_exists('params', $payload) && !is_array($payload['params'])) {
116
            $paramsValid = false;
117
        }
118
119
        return $headerValid && $methodValid && $paramsValid && $idValid;
120
    }
121
}
122