SSH2   A
last analyzed

Complexity

Total Complexity 23

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 9

Test Coverage

Coverage 94.29%

Importance

Changes 0
Metric Value
dl 0
loc 228
rs 10
c 0
b 0
f 0
ccs 66
cts 70
cp 0.9429
wmc 23
lcom 1
cbo 9

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 13 1
A __destruct() 0 5 1
B connect() 0 23 5
B disconnect() 0 24 5
A isConnected() 0 4 2
B createDriver() 0 34 5
A handleConnect() 0 4 1
A handleDisconnect() 0 4 1
A handleError() 0 5 1
A createConnection() 0 4 1
1
<?php
2
3
namespace Dazzle\SSH;
4
5
use Dazzle\Event\BaseEventEmitterTrait;
6
use Dazzle\Loop\LoopAwareTrait;
7
use Dazzle\Loop\LoopInterface;
8
use Dazzle\SSH\Driver\Sftp;
9
use Dazzle\SSH\Driver\Shell;
10
use Dazzle\Throwable\Exception\Logic\InvalidArgumentException;
11
use Dazzle\Throwable\Exception\Runtime\ExecutionException;
12
use Error;
13
use Exception;
14
15
class SSH2 implements SSH2Interface
16
{
17
    use BaseEventEmitterTrait;
18
    use LoopAwareTrait;
19
20
    /**
21
     * @var string
22
     */
23
    const DRIVER_SHELL = 'shell';
24
25
    /**
26
     * @var string
27
     */
28
    const DRIVER_SFTP = 'sftp';
29
30
    /**
31
     * @var SSH2AuthInterface
32
     */
33
    protected $auth;
34
35
    /**
36
     * @var SSH2ConfigInterface
37
     */
38
    protected $config;
39
40
    /**
41
     * @var resource
42
     */
43
    protected $conn;
44
45
    /**
46
     * @var SSH2DriverInterface[]
47
     */
48
    protected $drivers;
49
50
    /**
51
     * @var string
52
     */
53
    private $host;
54
55
    /**
56
     * @var int
57
     */
58
    private $port;
59
60
    /**
61
     * @var mixed
62
     */
63
    private $methods;
64
65
    /**
66
     * Constructor.
67
     *
68
     * @param SSH2AuthInterface $auth
69
     * @param SSH2ConfigInterface $config
70
     * @param LoopInterface $loop
71
     */
72 22
    public function __construct(SSH2AuthInterface $auth, SSH2ConfigInterface $config, LoopInterface $loop)
73
    {
74 22
        $this->auth = $auth;
75 22
        $this->config = $config;
76 22
        $this->loop = $loop;
77
78 22
        $this->host = $config->getHost();
79 22
        $this->port = $config->getPort();
80 22
        $this->methods = $config->getMethods();
81
82 22
        $this->conn = null;
83 22
        $this->drivers = [];
84 22
    }
85
86
    /**
87
     *
88
     */
89 8
    public function __destruct()
90
    {
91 8
        $this->disconnect();
92 8
        $this->destructEventEmitterTrait();
93 8
    }
94
95
    /**
96
     * @override
97
     * @inheritDoc
98
     */
99 8
    public function connect()
100
    {
101 8
        if ($this->conn !== null)
102
        {
103 1
            return;
104
        }
105
106 7
        $this->conn = $this->createConnection($this->host, $this->port, $this->methods, []);
107
108 7
        if (!$this->conn || !is_resource($this->conn))
109
        {
110 2
            $this->emit('error', [ $this, new ExecutionException('SSH2 connection could not be established.') ]);
111 2
            return;
112
        }
113
114 5
        if (!$this->auth->authenticate($this->conn))
115
        {
116 1
            $this->emit('error', [ $this, new ExecutionException('SSH2 connection could not be authenticated.') ]);
117 1
            return;
118
        }
119
120 4
        $this->emit('connect', [ $this ]);
121 4
    }
122
123
    /**
124
     * @override
125
     * @inheritDoc
126
     */
127 15
    public function disconnect()
128
    {
129 15
        if ($this->conn === null || !is_resource($this->conn))
130
        {
131 12
            return;
132
        }
133
134 6
        foreach ($this->drivers as $driver)
135
        {
136 3
            $driver->disconnect();
137
        }
138
139 6
        foreach ($this->drivers as $driver)
140
        {
141 3
            $driver->removeListener('connect', [ $this, 'handleConnect' ]);
142 3
            $driver->removeListener('disconnect', [ $this, 'handleDisconnect' ]);
143 3
            $driver->removeListener('error', [ $this, 'handleError' ]);
144
        }
145
146 6
        $this->conn = null;
147 6
        $this->drivers = [];
148
149 6
        $this->emit('disconnect', [ $this ]);
150 6
    }
151
152
    /**
153
     * @override
154
     * @inheritDoc
155
     */
156 6
    public function isConnected()
157
    {
158 6
        return $this->conn !== null && is_resource($this->conn);
159
    }
160
161
    /**
162
     * @override
163
     * @inheritDoc
164
     */
165 7
    public function createDriver($name)
166
    {
167 7
        if (isset($this->drivers[$name]))
168
        {
169 1
            return $this->drivers[$name];
170
        }
171
172 6
        if (!$this->isConnected())
173
        {
174 1
            throw new ExecutionException("The driver can be created only after the connection has been established!");
175
        }
176
177
        switch ($name)
178
        {
179 5
            case self::DRIVER_SHELL:
180 3
                $driver = new Shell($this, $this->conn);
181 3
                break;
182
183 2
            case self::DRIVER_SFTP:
184 1
                $driver = new Sftp($this, $this->conn);
185 1
                break;
186
187
            default:
188 1
                throw new InvalidArgumentException("The driver [$name] is not supported.");
189
        }
190
191 4
        $driver->on('connect', [ $this, 'handleConnect' ]);
192 4
        $driver->on('disconnect', [ $this, 'handleDisconnect' ]);
193 4
        $driver->on('error', [ $this, 'handleError' ]);
194
195 4
        $this->drivers[$name] = $driver;
196
197 4
        return $driver;
198
    }
199
200
    /**
201
     * @internal
202
     * @param SSH2DriverInterface $driver
203
     */
204 2
    public function handleConnect(SSH2DriverInterface $driver)
205
    {
206 2
        $this->emit('connect:' . $driver->getName(), [ $driver ]);
207 2
    }
208
209
    /**
210
     * @internal
211
     * @param SSH2DriverInterface $driver
212
     */
213 2
    public function handleDisconnect(SSH2DriverInterface $driver)
214
    {
215 2
        $this->emit('disconnect:' . $driver->getName(), [ $driver ]);
216 2
    }
217
218
    /**
219
     * @internal
220
     * @param SSH2DriverInterface $driver
221
     * @param Error|Exception $ex
222
     */
223
    public function handleError(SSH2DriverInterface $driver, $ex)
224
    {
225
        $this->emit('error:' . $driver->getName(), [ $driver, $ex ]);
226
        $this->emit('error', [ $this, $ex ]);
227
    }
228
229
    /**
230
     * Create SSH2 connection.
231
     *
232
     * @param string $host
233
     * @param int $port
234
     * @param mixed[] $methods
235
     * @param callable[] $callbacks
236
     * @return resource
237
     */
238 3
    protected function createConnection($host, $port, $methods, $callbacks)
239
    {
240 3
        return @ssh2_connect($host, $port, $methods, $callbacks);
241
    }
242
}
243