Completed
Pull Request — master (#11)
by Burhan
63:26 queued 53:25
created

TelnetClient::factory()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
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\PromptMatcherInterface;
20
use \Graze\TelnetClient\InterpretAsCommand;
21
use \Socket\Raw\Socket;
22
use \Socket\Raw\Factory as SocketFactory;
23
use \Graze\TelnetClient\TelnetClientBuilder;
24
use \Exception;
25
use \Graze\TelnetClient\Exception\TelnetException;
26
27
class TelnetClient implements TelnetClientInterface
28
{
29
    /**
30
     * @var SocketFactory
31
     */
32
    protected $socketFactory;
33
34
    /**
35
     * @var PromptMatcherInterface
36
     */
37
    protected $promptMatcher;
38
39
    /**
40
     * @var InterpretAsCommand
41
     */
42
    protected $interpretAsCommand;
43
44
    /**
45
     * @var string
46
     */
47
    protected $prompt = '\$';
48
49
    /**
50
     * @var string
51
     */
52
    protected $promptError = 'ERROR';
53
54
    /**
55
     * @var string
56
     */
57
    protected $lineEnding = "\n";
58
59
    /**
60
     * @var Socket
61
     */
62
    protected $socket;
63
64
    /**
65
     * @var string
66
     */
67
    protected $buffer;
68
69
    /**
70
     * @var string
71
     */
72
    protected $NULL;
73
74
    /**
75
     * @var string
76
     */
77
    protected $DC1;
78
79
    /**
80
     * @var string
81
     */
82
    protected $IAC;
83
84
    /**
85
     * @param SocketFactory $socketFactory
86
     * @param PromptMatcherInterface $promptMatcher
87
     * @param InterpretAsCommand $interpretAsCommand
88
     */
89 11
    public function __construct(
90
        SocketFactory $socketFactory,
91
        PromptMatcherInterface $promptMatcher,
92
        InterpretAsCommand $interpretAsCommand
93
    ) {
94 11
        $this->socketFactory = $socketFactory;
95 11
        $this->promptMatcher = $promptMatcher;
96 11
        $this->interpretAsCommand = $interpretAsCommand;
97
98
        $this->NULL = chr(0);
99
        $this->DC1 = chr(17);
100
    }
101
102
    /**
103
     * @param string $dsn
104
     * @param string $prompt
105
     * @param string $promptError
106
     * @param string $lineEnding
107
     * @param float|null $timeout
108
     *
109
     * @throws TelnetExceptionInterface
110
     */
111 10
    public function connect($dsn, $prompt = null, $promptError = null, $lineEnding = null, $timeout = null)
112
    {
113 10
        if ($prompt !== null) {
114
            $this->setPrompt($prompt);
115
        }
116
117 10
        if ($promptError !== null) {
118
            $this->setPromptError($promptError);
119
        }
120
121 10
        if ($lineEnding !== null) {
122
            $this->setLineEnding($lineEnding);
123
        }
124
125
        try {
126
            $socket = $this->socketFactory->createClient($dsn, $timeout);
0 ignored issues
show
Unused Code introduced by
The call to Factory::createClient() has too many arguments starting with $timeout.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
127
        } catch (Exception $e) {
128
            throw new TelnetException(sprintf('unable to create socket connection to [%s]', $dsn), 0, $e);
129 10
        }
130
131
        $this->setSocket($socket);
132 3
    }
133
134
    /**
135
     * @param string $prompt
136
     */
137 1
    public function setPrompt($prompt)
138
    {
139 1
        $this->prompt = $prompt;
140 1
    }
141
142
    /**
143
     * @param string $promptError
144
     */
145 1
    public function setPromptError($promptError)
146
    {
147 1
        $this->promptError = $promptError;
148 1
    }
149
150
    /**
151
     * @param string $lineEnding
152
     */
153 7
    public function setLineEnding($lineEnding)
154
    {
155 7
        $this->lineEnding = $lineEnding;
156 7
    }
157
158
    /**
159
     * @param Socket $socket
160
     */
161 9
    public function setSocket(Socket $socket)
162
    {
163 9
        $this->socket = $socket;
164
    }
165
166
    /**
167
     * @return \Socket\Raw\Socket
168
     */
169
    public function getSocket()
170
    {
171
        return $this->socket;
172
    }
173
174
    /**
175
     * @param string $command
176
     * @param string $prompt
177
     *
178
     * @return \Graze\TelnetClient\TelnetResponseInterface
179
     */
180 10
    public function execute($command, $prompt = null)
181
    {
182 10
        if (!$this->socket) {
183
            throw new TelnetException('attempt to execute without a connection - call connect first');
184
        }
185
186 1
        $this->write($command);
187 1
        return $this->getResponse($prompt);
188 3
    }
189
190
    /**
191
     * @param string $command
192
     *
193
     * @return void
194
     * @throws TelnetExceptionInterface
195
     */
196 9
    protected function write($command)
197
    {
198
        try {
199 1
            $this->socket->write($command . $this->lineEnding);
200
        } catch (Exception $e) {
201
            throw new TelnetException(sprintf('failed writing to socket [%s]', $command), 0, $e);
202 9
        }
203 8
    }
204
205
    /**
206
     * @param string $prompt
207
     *
208
     * @return \Graze\TelnetClient\TelnetResponseInterface
209
     * @throws TelnetExceptionInterface
210
     */
211 8
    protected function getResponse($prompt = null)
212
    {
213 8
        $isError = false;
214 8
        $buffer = '';
215
        do {
216
            // process one character at a time
217
            try {
218 1
                $character = $this->socket->read(1);
219
            } catch (Exception $e) {
220
                throw new TelnetException('failed reading from socket', 0, $e);
221 1
            }
222
223
            if (in_array($character, [$this->NULL, $this->DC1])) {
224
                break;
225
            }
226
227
            if ($this->interpretAsCommand->interpret($character, $this->socket)) {
228
                continue;
229
            }
230
231
            $buffer .= $character;
232
233
            // check for prompt
234
            if ($this->promptMatcher->isMatch($prompt ?: $this->prompt, $buffer, $this->lineEnding)) {
235 4
                break;
236
            }
237
238
            // check for error prompt
239
            if ($this->promptMatcher->isMatch($this->promptError, $buffer, $this->lineEnding)) {
240 3
                $isError = true;
241 3
                break;
242
            }
243
        } while (true);
244
245
        return new TelnetResponse(
246 7
            $isError,
247
            $this->promptMatcher->getResponseText(),
248
            $this->promptMatcher->getMatches()
249
        );
250
    }
251
252
    /**
253
     * @return TelnetClientInterface
254
     */
255
    public static function factory()
256
    {
257
        return new static(
258
            new SocketFactory(),
259
            new PromptMatcher(),
260
            new InterpretAsCommand()
261
        );
262
    }
263
264 8
    public function __destruct()
265
    {
266 8
        if (!$this->socket) {
267 1
            return;
268
        }
269
270
        $this->socket->close();
271 7
    }
272
}
273