Issues (1)

src/Protocol/Parser.php (1 issue)

1
<?php
2
3
namespace seregazhuk\React\Memcached\Protocol;
4
5
use seregazhuk\React\Memcached\Exception\FailedCommandException;
6
use seregazhuk\React\Memcached\Exception\WrongCommandException;
7
use seregazhuk\React\Memcached\Protocol\Request\Request;
8
use seregazhuk\React\Memcached\Protocol\Request\SimpleRequest;
9
use seregazhuk\React\Memcached\Protocol\Request\StorageRequest;
10
use seregazhuk\React\Memcached\Protocol\Response\DeleteResponse;
11
use seregazhuk\React\Memcached\Protocol\Response\OkResponse;
12
use seregazhuk\React\Memcached\Protocol\Response\ReadResponse;
13
use seregazhuk\React\Memcached\Protocol\Response\Response;
14
use seregazhuk\React\Memcached\Protocol\Response\StatsResponse;
15
use seregazhuk\React\Memcached\Protocol\Response\TouchResponse;
16
use seregazhuk\React\Memcached\Protocol\Response\ValueResponse;
17
use seregazhuk\React\Memcached\Protocol\Response\VersionResponse;
18
use seregazhuk\React\Memcached\Protocol\Response\WriteResponse;
19
20
final class Parser
21
{
22
    public const RESPONSE_STORED = 'STORED';
23
    public const RESPONSE_DELETED = 'DELETED';
24
    public const RESPONSE_NOT_FOUND = 'NOT_FOUND';
25
    public const RESPONSE_OK = 'OK';
26
    public const RESPONSE_VERSION = 'VERSION';
27
28
    private const RESPONSE_NOT_STORED = 'NOT_STORED';
29
    private const RESPONSE_END = 'END';
30
    private const RESPONSE_EXISTS = 'EXISTS';
31
    public const RESPONSE_TOUCHED = 'TOUCHED';
32
    private const RESPONSE_ERROR = 'ERROR';
33
    private const RESPONSE_RESET = 'RESET';
34
35
    private const RESPONSE_ENDS = [
36
        self::RESPONSE_END,
37
        self::RESPONSE_DELETED,
38
        self::RESPONSE_NOT_FOUND,
39
        self::RESPONSE_OK,
40
        self::RESPONSE_EXISTS,
41
        self::RESPONSE_ERROR,
42
        self::RESPONSE_RESET,
43
        self::RESPONSE_STORED,
44
        self::RESPONSE_NOT_STORED,
45
        self::RESPONSE_TOUCHED,
46
    ];
47
48
    private const COMMAND_SET = 'set';
49
50
    public const COMMAND_SEPARATOR = "\r\n";
51
52
    private const STORAGE_COMMANDS = [
53
        self::COMMAND_SET,
54
        self::COMMAND_ADD,
55
        self::COMMAND_REPLACE,
56
    ];
57
58
    private const COMMAND_GET = 'get';
59
60
    private const COMMAND_VERSION = 'version';
61
    private const COMMAND_STATS = 'stats';
62
    private const COMMAND_TOUCH = 'touch';
63
    private const COMMAND_DELETE = 'delete';
64
    private const COMMAND_INCREMENT = 'incr';
65
    private const COMMAND_DECREMENT = 'decr';
66
    private const COMMAND_ADD = 'add';
67
    private const COMMAND_REPLACE = 'replace';
68
    private const COMMAND_VERBOSITY = 'verbosity';
69
    private const COMMAND_FLUSH_ALL = 'flushAll';
70
71
    private const COMMANDS = [
72
            self::COMMAND_ADD,
73
            self::COMMAND_DECREMENT,
74
            self::COMMAND_DELETE,
75
            self::COMMAND_FLUSH_ALL,
76
            self::COMMAND_GET,
77
            self::COMMAND_INCREMENT,
78
            self::COMMAND_REPLACE,
79
            self::COMMAND_SET,
80
            self::COMMAND_STATS,
81
            self::COMMAND_TOUCH,
82
            self::COMMAND_VERBOSITY,
83
            self::COMMAND_VERSION,
84
        ];
85
86
    /**
87
     * @param string $data
88
     * @return string[]
89
     */
90
    public function parseRawResponse(string $data = ''): array
91
    {
92
        $data = substr($data, 0, strlen($data) - strlen(self::COMMAND_SEPARATOR));
93
        $lines = explode(self::COMMAND_SEPARATOR, $data);
94
95
        $results = [];
96
        $result = '';
97
98
        foreach ($lines as $line) {
99
            $result .= $line;
100
101
            if (in_array($line, self::RESPONSE_ENDS)) {
102
                $results[] = $result;
103
                $result = '';
104
            }
105
106
            if (strpos($line, self::RESPONSE_VERSION) !== false) {
107
                $results[] = $line;
108
                $result = '';
109
            }
110
111
            $result .= self::COMMAND_SEPARATOR;
112
        }
113
114
        if (!empty($result) && $result !== self::COMMAND_SEPARATOR) {
115
            $results[] = $result;
116
        }
117
118
        return $results;
119
    }
120
121
    /**
122
     * @param string $command
123
     * @param array $args
124
     * @return string
125
     * @throws WrongCommandException
126
     */
127
    public function makeCommand($command, array $args)
128
    {
129
        return $this->createRequest($command, $args)->command();
130
    }
131
132
    /**
133
     * @param string $command
134
     * @param string $response
135
     * @return string
136
     * @throws WrongCommandException
137
     * @throws FailedCommandException
138
     */
139
    public function parseResponse($command, $response)
140
    {
141
        return $this->createResponse($command, $response)->parse();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->createResp...nd, $response)->parse() also could return the type array|true which is incompatible with the documented return type string.
Loading history...
142
    }
143
144
    /**
145
     * @param string $command
146
     * @param string $data
147
     * @return Response
148
     * @throws WrongCommandException
149
     */
150
    public function createResponse($command, $data)
151
    {
152
        switch ($command) {
153
            case self::COMMAND_GET:
154
                return new ReadResponse($data);
155
            case self::COMMAND_SET:
156
            case self::COMMAND_ADD:
157
            case self::COMMAND_REPLACE:
158
                return new WriteResponse($data);
159
            case self::COMMAND_VERSION:
160
                return new VersionResponse($data);
161
            case self::COMMAND_STATS:
162
                return new StatsResponse($data);
163
            case self::COMMAND_TOUCH:
164
                return new TouchResponse($data);
165
            case self::COMMAND_DELETE:
166
                return new DeleteResponse($data);
167
            case self::COMMAND_VERBOSITY:
168
            case self::COMMAND_FLUSH_ALL:
169
                return new OkResponse($data);
170
            case self::COMMAND_INCREMENT:
171
            case self::COMMAND_DECREMENT:
172
                return new ValueResponse($data);
173
        }
174
175
        throw new WrongCommandException("Cannot parse response for command $command");
176
    }
177
178
    /**
179
     * @param string $command
180
     * @param array $args
181
     * @return Request
182
     * @throws WrongCommandException
183
     */
184
    public function createRequest($command, $args)
185
    {
186
        if (!in_array($command, self::COMMANDS)) {
187
            throw new WrongCommandException("Unknown command: $command");
188
        }
189
190
        if (in_array($command, self::STORAGE_COMMANDS)) {
191
            return new StorageRequest($command, ...$args);
192
        }
193
194
        return new SimpleRequest($command, $args);
195
    }
196
}
197