Passed
Push — master ( b8091d...543f5f )
by Brendan
55s queued 11s
created

TelnetClient::getResponse()   C

Complexity

Conditions 11
Paths 7

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 11.0077

Importance

Changes 0
Metric Value
dl 0
loc 51
ccs 24
cts 25
cp 0.96
rs 6.9224
c 0
b 0
f 0
cc 11
nc 7
nop 2
crap 11.0077

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * This file is part of graze/telnet-client.
5
 *
6
 * Copyright (c) 2016 Nature Delivered Ltd. <https://www.graze.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 *
11
 * @license https://github.com/graze/telnet-client/blob/master/LICENSE
12
 * @link https://github.com/graze/telnet-client
13
 */
14
15
namespace Graze\TelnetClient;
16
17
use Exception;
18
use Graze\TelnetClient\Exception\TelnetException;
19
use Graze\TelnetClient\Exception\TelnetExceptionInterface;
20
use Socket\Raw\Factory as SocketFactory;
21
use Socket\Raw\Socket;
22
23
class TelnetClient implements TelnetClientInterface
24
{
25
    /**
26
     * @var SocketFactory
27
     */
28
    protected $socketFactory;
29
30
    /**
31
     * @var PromptMatcherInterface
32
     */
33
    protected $promptMatcher;
34
35
    /**
36
     * @var InterpretAsCommand
37
     */
38
    protected $interpretAsCommand;
39
40
    /**
41
     * @var string
42
     */
43
    protected $prompt = '\$';
44
45
    /**
46
     * @var string
47
     */
48
    protected $promptError = 'ERROR';
49
50
    /**
51
     * @var string
52
     */
53
    protected $lineEnding = "\n";
54
55
    /**
56
     * @var int
57
     */
58
    protected $maxBytesRead = 0;
59
60
    /**
61
     * @var Socket
62
     */
63
    protected $socket;
64
65
    /**
66
     * @var string
67
     */
68
    protected $buffer;
69
70
    /**
71
     * @var string
72
     */
73
    protected $NULL;
74
75
    /**
76
     * @var string
77
     */
78
    protected $DC1;
79
80
    /**
81
     * @var string
82
     */
83
    protected $IAC;
84
85 18
    /**
86
     * @param SocketFactory $socketFactory
87
     * @param PromptMatcherInterface $promptMatcher
88
     * @param InterpretAsCommand $interpretAsCommand
89
     */
90 18
    public function __construct(
91 18
        SocketFactory $socketFactory,
92 18
        PromptMatcherInterface $promptMatcher,
93
        InterpretAsCommand $interpretAsCommand
94 18
    ) {
95 18
        $this->socketFactory = $socketFactory;
96 18
        $this->promptMatcher = $promptMatcher;
97
        $this->interpretAsCommand = $interpretAsCommand;
98
99
        $this->NULL = chr(0);
100
        $this->DC1 = chr(17);
101
    }
102
103
    /**
104
     * @param string $dsn
105
     * @param string $prompt
106
     * @param string $promptError
107 17
     * @param string $lineEnding
108
     * @param float|null $timeout
109 17
     *
110 4
     * @throws TelnetExceptionInterface
111 4
     */
112
    public function connect($dsn, $prompt = null, $promptError = null, $lineEnding = null, $timeout = null)
113 17
    {
114 3
        if ($prompt !== null) {
115 3
            $this->setPrompt($prompt);
116
        }
117 17
118 16
        if ($promptError !== null) {
119 16
            $this->setPromptError($promptError);
120
        }
121
122 17
        if ($lineEnding !== null) {
123 17
            $this->setLineEnding($lineEnding);
124
        }
125
126
        try {
127 17
            $socket = $this->socketFactory->createClient($dsn, $timeout);
128 17
        } catch (Exception $e) {
129
            throw new TelnetException(sprintf('unable to create socket connection to [%s]', $dsn), 0, $e);
130
        }
131
132
        $this->setSocket($socket);
133 2
    }
134
135 2
    /**
136 2
     * @param string $prompt
137
     */
138
    public function setPrompt($prompt)
139
    {
140
        $this->prompt = $prompt;
141 1
    }
142
143 1
    /**
144 1
     * @param string $promptError
145
     */
146
    public function setPromptError($promptError)
147
    {
148
        $this->promptError = $promptError;
149 14
    }
150
151 14
    /**
152 14
     * @param string $lineEnding
153
     */
154
    public function setLineEnding($lineEnding)
155
    {
156
        $this->lineEnding = $lineEnding;
157 19
    }
158
159 19
    /**
160 19
     * Set the maximum number of bytes that can be read per request
161
     *
162
     * @param int $maxBytesRead
163
     */
164
    public function setMaxBytesRead($maxBytesRead)
165
    {
166
        $this->maxBytesRead = $maxBytesRead;
167
    }
168
169
    /**
170
     * @param Socket $socket
171
     */
172
    public function setSocket(Socket $socket)
173 4
    {
174
        $this->socket = $socket;
175 4
    }
176 1
177
    /**
178
     * @return Socket
179 3
     */
180 3
    public function getSocket()
181
    {
182 3
        return $this->socket;
183 3
    }
184
185
    /**
186
     * @param float $timeout
187
     */
188
    public function setReadTimeout($timeout)
189
    {
190
        if (!$this->socket) {
191
            throw new TelnetException('cannot set read timeout, socket does not exist (call connect() first)');
192 17
        }
193
194 17
        $sec = floor($timeout);
195 1
        $usec = round(fmod($timeout, 1) * 1000000);
196
197
        $this->socket->setOption(SOL_SOCKET, SO_RCVTIMEO, ['sec' => $sec, 'usec' => $usec]);
198 16
    }
199 15
200
    /**
201
     * @param string $command
202
     * @param string $prompt
203
     * @param string $promptError
204
     *
205
     * @return TelnetResponseInterface
206
     */
207
    public function execute($command, $prompt = null, $promptError = null)
208 16
    {
209
        if (!$this->socket) {
210
            throw new TelnetException('attempt to execute without a connection - call connect first');
211 16
        }
212 16
213 1
        $this->write($command);
214
        return $this->getResponse($prompt, $promptError);
215 15
    }
216
217
    /**
218
     * @param string $command
219
     *
220
     * @return void
221
     * @throws TelnetExceptionInterface
222
     */
223
    protected function write($command)
224 15
    {
225
        try {
226 15
            $this->socket->write($command . $this->lineEnding);
227 15
        } catch (Exception $e) {
228
            throw new TelnetException(sprintf('failed writing to socket [%s]', $command), 0, $e);
229
        }
230
    }
231 15
232 15
    /**
233 1
     * @param string $prompt
234
     * @param string $promptError
235
     *
236 14
     * @return TelnetResponseInterface
237
     * @throws TelnetExceptionInterface
238
     */
239
    protected function getResponse($prompt = null, $promptError = null)
240 14
    {
241
        $isError = false;
242
        $buffer = '';
243
        $bytesRead = 0;
244 14
        do {
245
            // process one byte at a time
246
            try {
247 14
                $byte = $this->socket->read(1);
248 8
                $bytesRead++;
249
            } catch (Exception $e) {
250
                throw new TelnetException('failed reading from socket', 0, $e);
251
            }
252 14
253 6
            if (in_array($byte, [$this->NULL, $this->DC1])) {
254 6
                break;
255
            }
256 14
257
            if ($this->interpretAsCommand->interpret($byte, $this->socket)) {
258 14
                continue;
259 14
            }
260 14
261 14
            $buffer .= $byte;
262 14
263
            // check for prompt
264
            if ($this->promptMatcher->isMatch($prompt ?: $this->prompt, $buffer, $this->lineEnding)) {
265
                break;
266
            }
267
268 1
            // check for error prompt
269
            if ($this->promptMatcher->isMatch($promptError ?: $this->promptError, $buffer, $this->lineEnding)) {
270 1
                $isError = true;
271 1
                break;
272 1
            }
273 1
274 1
            // throw an exception if the number of bytes read is greater than the limit
275
            if ($this->maxBytesRead > 0 && $bytesRead >= $this->maxBytesRead) {
276
                throw new TelnetException(sprintf(
277 15
                    'Maximum number of bytes read (%d), the last bytes were %s',
278
                    $this->maxBytesRead,
279 15
                    substr($buffer, -10)
280 1
                ));
281
            }
282
        } while (true);
283 14
284 14
        return new TelnetResponse(
285
            $isError,
286
            $this->promptMatcher->getResponseText(),
287
            $this->promptMatcher->getMatches()
288
        );
289
    }
290
291
    /**
292
     * @return TelnetClientInterface
293
     */
294
    public static function factory()
295
    {
296
        return new static(
297
            new SocketFactory(),
298
            new PromptMatcher(),
299
            new InterpretAsCommand()
300
        );
301
    }
302
303
    public function __destruct()
304
    {
305
        if (!$this->socket) {
306
            return;
307
        }
308
309
        $this->socket->close();
310
    }
311
}
312