SshProcess::connect()   B
last analyzed

Complexity

Conditions 10
Paths 9

Size

Total Lines 35
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 15
c 1
b 0
f 0
nc 9
nop 7
dl 0
loc 35
rs 7.6666

How to fix   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
 * This file is part of the GitCommandBundle package.
4
 *
5
 * (c) Paul Schweppe <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace VersionControl\GitCommandBundle\Service;
12
13
use InvalidArgumentException;
14
use RuntimeException;
15
16
/**
17
 * Uses php SSH2 library to run SSH Process.
18
 *
19
 * @author Paul Schweppe <[email protected]>
20
 */
21
class SshProcess implements SshProcessInterface
22
{
23
    /**
24
     * @var array
25
     */
26
    protected $config;
27
28
    /**
29
     * @var resource
30
     */
31
    protected $session;
32
33
    /**
34
     * @var resource
35
     */
36
    protected $shell;
37
38
    /**
39
     * @var array
40
     */
41
    protected $stdout = [];
42
43
    /**
44
     * @var array
45
     */
46
    protected $stderr = [];
47
48
    /**
49
     * @var array
50
     */
51
    private $stdin = [];
52
53
    /**
54
     * @param string $glue
55
     *
56
     * @return array|string
57
     */
58
    public function getStdout($glue = "\n")
59
    {
60
        if (!$glue) {
61
            $output = $this->stdout;
62
        } else {
63
            $output = implode($glue, $this->stdout);
64
        }
65
66
        return $output;
67
    }
68
69
    /**
70
     * @param string $glue
71
     *
72
     * @return array|string
73
     */
74
    public function getStderr($glue = "\n")
75
    {
76
        if (!$glue) {
77
            return $this->stderr;
78
        }
79
80
        return implode($glue, $this->stderr);
81
    }
82
83
    /**
84
     * @param array $commands
85
     * @param string $host
86
     * @param string $username
87
     * @param int $port
88
     * @param string $password
89
     * @param string|null $publicKeyFile
90
     * @param string|null $privateKeyFile
91
     * @param string $passphrase
92
     *
93
     * @return void
94
     */
95
    public function run(
96
        array $commands,
97
        string $host,
98
        string $username,
99
        int $port = 22,
100
        ?string $password = null,
101
        ?string $publicKeyFile = null,
102
        ?string $privateKeyFile = null,
103
        ?string $passphrase = null
104
    ): array {
105
        $this->reset();
106
107
        if ($this->shell === null) {
108
            $this->connect($host, $username, $port, $password, $publicKeyFile, $privateKeyFile, $passphrase);
109
        }
110
111
        foreach ($commands as $command) {
112
            $this->execute($command);
113
        }
114
115
        return $this->stdout;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->stdout returns the type array which is incompatible with the documented return type void.
Loading history...
116
    }
117
118
    /**
119
     * Resets out puts for next command.
120
     */
121
    protected function reset(): void
122
    {
123
        $this->stdout = [];
124
        $this->stdin = [];
125
        $this->stderr = [];
126
    }
127
128
    /**
129
     * @param string $host
130
     * @param string $username
131
     * @param int $port
132
     * @param null|string $password
133
     * @param null|string $publicKeyFile
134
     * @param null|string $privateKeyFile
135
     * @param null|string $passphrase
136
     *
137
     * @throws InvalidArgumentException
138
     * @throws RuntimeException
139
     */
140
    protected function connect(
141
        string $host,
142
        string $username,
143
        int $port = 22,
144
        ?string $password = null,
145
        ?string $publicKeyFile = null,
146
        ?string $privateKeyFile = null,
147
        ?string $passphrase = null
148
    ): void {
149
        $this->session = ssh2_connect($host, $port);
150
151
        if (!$this->session) {
152
            throw new InvalidArgumentException(sprintf('SSH connection failed on "%s:%s"', $host, $port));
153
        }
154
155
        if (isset($username) && $publicKeyFile !== null && $privateKeyFile !== null) {
156
            if (!ssh2_auth_pubkey_file($this->session, $username, $publicKeyFile, $privateKeyFile, $passphrase)) {
157
                throw new InvalidArgumentException(
158
                    sprintf('SSH authentication failed for user "%s" with public key "%s"', $username, $publicKeyFile)
159
                );
160
            }
161
        } elseif ($username && $password) {
162
            if (!ssh2_auth_password($this->session, $username, $password)) {
163
                throw new InvalidArgumentException(sprintf('SSH authentication failed for user "%s"', $username));
164
            }
165
        }
166
167
        $this->shell = ssh2_shell($this->session);
168
169
        if (!$this->shell) {
170
            throw new RuntimeException(sprintf('Failed opening shell'));
171
        }
172
173
        $this->stdout = [];
174
        $this->stdin = [];
175
    }
176
177
    public function disconnect(): void
178
    {
179
        if ($this->shell) {
180
            fclose($this->shell);
181
        }
182
    }
183
184
    /**
185
     * @param string $command
186
     *
187
     * @throws RuntimeException
188
     */
189
    protected function execute(string $command): void
190
    {
191
        $outStream = ssh2_exec($this->session, $command);
192
        $errStream = ssh2_fetch_stream($outStream, SSH2_STREAM_STDERR);
193
194
        stream_set_blocking($outStream, true);
195
        stream_set_blocking($errStream, true);
196
197
        $stdout = explode("\n", stream_get_contents($outStream));
198
        $stderr = explode("\n", stream_get_contents($errStream));
199
200
        if (count($stderr) > 1) {
201
            throw new RuntimeException(
202
                sprintf(
203
                    "Error in command shell:%s \n Error Response:%s",
204
                    $command,
205
                    implode("\n", $stderr)
206
                )
207
            );
208
        }
209
210
        $this->stdout = array_merge($this->stdout, $stdout);
211
212
        if (is_array($stderr)) {
0 ignored issues
show
introduced by
The condition is_array($stderr) is always true.
Loading history...
213
            $this->stderr = array_merge($this->stderr, $stderr);
214
        }
215
216
        fclose($outStream);
217
        fclose($errStream);
218
    }
219
220
    public function __destruct()
221
    {
222
        $this->disconnect();
223
    }
224
225
    public function getExitStatus()
226
    {
227
        if (count($this->stderr) > 0) {
228
            return 1;
229
        }
230
231
        return 0;
232
    }
233
}
234