Completed
Push — master ( 444469...d5f107 )
by John
24s
created

TelnetClient::connect()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5.1158

Importance

Changes 3
Bugs 1 Features 1
Metric Value
c 3
b 1
f 1
dl 0
loc 22
ccs 10
cts 12
cp 0.8333
rs 8.6737
cc 5
eloc 12
nc 16
nop 4
crap 5.1158
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 \Graze\TelnetClient\TelnetClientInterface;
18
use \Graze\TelnetClient\PromptMatcher;
19
use \Graze\TelnetClient\InterpretAsCommand;
20
use \Socket\Raw\Socket;
21
use \Socket\Raw\Factory as SocketFactory;
22
use \Graze\TelnetClient\TelnetClientBuilder;
23
use \Exception;
24
use \Graze\TelnetClient\Exception\TelnetException;
25
26
class TelnetClient implements TelnetClientInterface
27
{
28
    /**
29
     * @var SocketFactory
30
     */
31
    protected $socketFactory;
32
33
    /**
34
     * @var PromptMatcher
35
     */
36
    protected $promptMatcher;
37
38
    /**
39
     * @var InterpretAsCommand
40
     */
41
    protected $interpretAsCommand;
42
43
    /**
44
     * @var string
45
     */
46
    protected $prompt = '\$';
47
48
    /**
49
     * @var string
50
     */
51
    protected $promptError = 'ERROR';
52
53
    /**
54
     * @var string
55
     */
56
    protected $lineEnding = "\n";
57
58
    /**
59
     * @var Socket
60
     */
61
    protected $socket;
62
63
    /**
64
     * @var string
65
     */
66
    protected $buffer;
67
68
    /**
69
     * @var string
70
     */
71
    protected $NULL;
72
73
    /**
74
     * @var string
75
     */
76
    protected $DC1;
77
78
    /**
79
     * @var string
80
     */
81
    protected $IAC;
82
83
    /**
84
     * @param SocketFactory $socketFactory
85
     * @param PromptMatcher $promptMatcher
86
     * @param InterpretAsCommand $interpretAsCommand
87
     */
88 10
    public function __construct(
89
        SocketFactory $socketFactory,
90
        PromptMatcher $promptMatcher,
91
        InterpretAsCommand $interpretAsCommand
92
    ) {
93 10
        $this->socketFactory = $socketFactory;
94 10
        $this->promptMatcher = $promptMatcher;
95 10
        $this->interpretAsCommand = $interpretAsCommand;
96
97 10
        $this->NULL = chr(0);
98 10
        $this->DC1 = chr(17);
99 10
    }
100
101
    /**
102
     * @param string $dsn
103
     * @param string $prompt
104
     * @param string $promptError
105
     * @param string $lineEnding
106
     *
107
     * @throws TelnetExceptionInterface
108
     */
109 9
    public function connect($dsn, $prompt = null, $promptError = null, $lineEnding = null)
110
    {
111 9
        if ($prompt !== null) {
112 2
            $this->setPrompt($prompt);
113
        }
114
115 9
        if ($promptError !== null) {
116 2
            $this->setPromptError($promptError);
117
        }
118
119 9
        if ($lineEnding !== null) {
120 8
            $this->setLineEnding($lineEnding);
121
        }
122
123
        try {
124 9
            $socket = $this->socketFactory->createClient($dsn);
125
        } catch (Exception $e) {
126
            throw new TelnetException(sprintf('unable to create socket connection to [%s]', $dsn), 0, $e);
127
        }
128
129 9
        $this->setSocket($socket);
130 9
    }
131
132
    /**
133
     * @param string $prompt
134
     */
135 1
    public function setPrompt($prompt)
136
    {
137 1
        $this->prompt = $prompt;
138 1
    }
139
140
    /**
141
     * @param string $promptError
142
     */
143 1
    public function setPromptError($promptError)
144
    {
145 1
        $this->promptError = $promptError;
146 1
    }
147
148
    /**
149
     * @param string $lineEnding
150
     */
151 7
    public function setLineEnding($lineEnding)
152
    {
153 7
        $this->lineEnding = $lineEnding;
154 7
    }
155
156
    /**
157
     * @param Socket $socket
158
     */
159 9
    public function setSocket(Socket $socket)
160
    {
161 9
        $this->socket = $socket;
162 9
    }
163
164
    /**
165
     * @return \Socket\Raw\Socket
166
     */
167
    public function getSocket()
168
    {
169
        return $this->socket;
170
    }
171
172
    /**
173
     * @param string $command
174
     * @param string $prompt
175
     *
176
     * @return \Graze\TelnetClient\TelnetResponseInterface
177
     */
178 10
    public function execute($command, $prompt = null)
179
    {
180 10
        if (!$this->socket) {
181 1
            throw new TelnetException('attempt to execute without a connection - call connect first');
182
        }
183
184 9
        $this->write($command);
185 8
        return $this->getResponse($prompt);
186
    }
187
188
    /**
189
     * @param string $command
190
     *
191
     * @return void
192
     * @throws TelnetExceptionInterface
193
     */
194 9
    protected function write($command)
195
    {
196
        try {
197 9
            $this->socket->write($command . $this->lineEnding);
198 1
        } catch (Exception $e) {
199 1
            throw new TelnetException(sprintf('failed writing to socket [%s]', $command), 0, $e);
200
        }
201 8
    }
202
203
    /**
204
     * @param string $prompt
205
     *
206
     * @return \Graze\TelnetClient\TelnetResponseInterface
207
     * @throws TelnetExceptionInterface
208
     */
209 8
    protected function getResponse($prompt = null)
210
    {
211 8
        $isError = false;
212 8
        $buffer = '';
213
        do {
214
            // process one character at a time
215
            try {
216 8
                $character = $this->socket->read(1);
217 1
            } catch (Exception $e) {
218 1
                throw new TelnetException('failed reading from socket', 0, $e);
219
            }
220
221 7
            if (in_array($character, [$this->NULL, $this->DC1])) {
222
                break;
223
            }
224
225 7
            if ($this->interpretAsCommand->interpret($character, $this->socket)) {
226
                continue;
227
            }
228
229 7
            $buffer .= $character;
230
231
            // check for prompt
232 7
            if ($this->promptMatcher->isMatch($prompt ?: $this->prompt, $buffer, $this->lineEnding)) {
233 4
                break;
234
            }
235
236
            // check for error prompt
237 7
            if ($this->promptMatcher->isMatch($this->promptError, $buffer, $this->lineEnding)) {
238 3
                $isError = true;
239 3
                break;
240
            }
241
242 7
        } while (true);
243
244 7
        return new TelnetResponse(
245
            $isError,
246 7
            $this->promptMatcher->getResponseText(),
247 7
            $this->promptMatcher->getMatches()
248
        );
249
    }
250
251
    /**
252
     * @return TelnetClientInterface
253
     */
254 1
    public static function factory()
255
    {
256 1
        return new static(
257 1
            new SocketFactory(),
258 1
            new PromptMatcher(),
259 1
            new InterpretAsCommand()
260
        );
261
    }
262
263 8
    public function __destruct()
264
    {
265 8
        if (!$this->socket) {
266 1
            return;
267
        }
268
269 7
        $this->socket->close();
270 7
    }
271
}
272