SSH::set_filename()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
namespace LibSSH2\Sessions;
3
4
use LibSSH2\Authentication\Authentication;
5
use LibSSH2\Configuration;
6
use LibSSH2\Connection;
7
use LibSSH2\Terminal;
8
9
/**
10
 * SSH class.
11
 *
12
 * Execute remote commands via SSH.
13
 *
14
 * @package LibSSH2\Sessions
15
 */
16
class SSH extends Connection
17
{
18
19
    /**
20
     * Wait for command execution to finish.
21
     *
22
     * @var int
23
     */
24
    const WAIT = 'execute.wait';
25
26
    /**
27
     * Produce realtime output.
28
     *
29
     * @var int
30
     */
31
    const REALTIME = 'execute.realtime';
32
33
    /**
34
     * Write output to file (realtime).
35
     *
36
     * @var int
37
     */
38
    const FILE = 'execute.file';
39
40
    /**
41
     * Execution mode type.
42
     *
43
     * @var string
44
     */
45
    public $mode = null;
46
47
    /**
48
     * Filename path.
49
     *
50
     * @var string
51
     */
52
    public $filename = null;
53
54
    /**
55
     * Constructor.
56
     *
57
     * @param  instance $configuration  Configuration instance
58
     * @param  instance $authentication Authentication instance
59
     * @return void
60
     */
61
    public function __construct(Configuration $configuration, Authentication $authentication)
62
    {
63
        parent::__construct($configuration, $authentication);
64
    }
65
66
    /**
67
     * Set execution mode.
68
     *
69
     * @return object
70
     */
71
    final public function set_mode($mode = self::WAIT)
72
    {
73
        $this->mode = $mode;
74
        return $this;
75
    }
76
77
    /**
78
     * Set filename path.
79
     *
80
     * @return object
81
     */
82
    final public function set_filename($filename)
83
    {
84
        $this->filename = $filename;
85
        return $this;
86
    }
87
88
    /**
89
     * Get execution mode.
90
     *
91
     * @return string execution mode type
92
     */
93
    final public function get_mode()
94
    {
95
        return ($this->mode === null) ? self::WAIT : $this->mode;
0 ignored issues
show
introduced by
The condition $this->mode === null can never be true.
Loading history...
96
    }
97
98
    /**
99
     * Get filename path.
100
     *
101
     * @return string filename path
102
     */
103
    final public function get_filename()
104
    {
105
        return $this->filename;
106
    }
107
108
    /**
109
     * Execute remote command via SSH.
110
     *
111
     * @param  string   $command  command being executed
112
     * @param  instance $terminal Terminal instance
113
     * @return void
114
     */
115
    final public function exec($command, Terminal $terminal = null)
116
    {
117
        if (!$terminal instanceof Terminal)
118
        {
119
            $terminal = new Terminal();
120
        }
121
122
        switch ($this->get_mode())
123
        {
124
            case 'execute.wait':
125
                $stream = $this->get_stream($command, $terminal);
126
                $this->exec_wait($stream);
0 ignored issues
show
Bug introduced by
$stream of type LibSSH2\Sessions\stream is incompatible with the type resource expected by parameter $stream of LibSSH2\Sessions\SSH::exec_wait(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

126
                $this->exec_wait(/** @scrutinizer ignore-type */ $stream);
Loading history...
127
                break;
128
129
            case 'execute.realtime':
130
                $command .= ' 2>&1';
131
                $stream = $this->get_stream($command, $terminal);
132
                $this->exec_realtime($stream);
0 ignored issues
show
Bug introduced by
$stream of type LibSSH2\Sessions\stream is incompatible with the type resource expected by parameter $stream of LibSSH2\Sessions\SSH::exec_realtime(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

132
                $this->exec_realtime(/** @scrutinizer ignore-type */ $stream);
Loading history...
133
                break;
134
135
            case 'execute.file':
136
                $command .= ' 2>&1';
137
                $stream = $this->get_stream($command, $terminal);
138
                $this->exec_file($stream);
0 ignored issues
show
Bug introduced by
$stream of type LibSSH2\Sessions\stream is incompatible with the type resource expected by parameter $stream of LibSSH2\Sessions\SSH::exec_file(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

138
                $this->exec_file(/** @scrutinizer ignore-type */ $stream);
Loading history...
139
                break;
140
141
            default:
142
                throw new \RuntimeException('Unknown output mode type: '.$this->get_mode());
143
        }
144
    }
145
146
    /**
147
     * Create channel stream.
148
     *
149
     * @param  string   $command  command being executed
150
     * @param  instance $terminal Terminal instance
151
     * @return stream   SSH connection resource stream
0 ignored issues
show
Bug introduced by
The type LibSSH2\Sessions\stream was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
152
     */
153
    final private function get_stream($command, Terminal $terminal)
154
    {
155
        if (!is_resource($this->connection))
156
        {
157
            throw new \RuntimeException(__FUNCTION__.': not a valid SSH2 Session resource.');
158
        }
159
160
        $command .= '; echo "RETURN_CODE:[$?]"';
161
        $stream = @ssh2_exec(
162
            $this->connection,
163
            $command,
164
            $terminal->get_pty(),
165
            $terminal->get_env(),
166
            $terminal->get_width(),
167
            $terminal->get_height()
168
        );
169
        
170
        if ($stream === false)
0 ignored issues
show
introduced by
The condition $stream === false can never be true.
Loading history...
171
        {
172
            throw new \RuntimeException($this->get_error_message());
173
        }
174
        stream_set_blocking($stream, true);
175
        return $stream;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $stream returns the type resource which is incompatible with the documented return type LibSSH2\Sessions\stream.
Loading history...
176
    }
177
178
    /**
179
     * Executes a command on a remote server.
180
     *
181
     * @param  resource $stream SSH resource stream
182
     * @return void
183
     */
184
    final private function exec_wait($stream)
185
    {
186
        $out = ssh2_fetch_stream($stream, SSH2_STREAM_STDIO);
187
        $err = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR);
188
189
        $stdout = '';
190
        $stderr = '';
191
        do
192
        {
193
            sleep(1);
194
            if ($out === false || $err === false)
195
            {
196
                $stderr .= 'STDOUT and/or STDERR stream(s) closed unexpectedly.';
197
                return 1;
0 ignored issues
show
Bug Best Practice introduced by
The expression return 1 returns the type integer which is incompatible with the documented return type void.
Loading history...
198
            }
199
200
            $stdout .= stream_get_contents($out);
201
            $stderr .= stream_get_contents($err);
202
        } while (!preg_match('/RETURN_CODE:\[([0-9]+)\]/', $stdout, $retval));
203
204
        fclose($out);
205
        fclose($err);
206
207
        $this->set_output(trim(preg_replace('/RETURN_CODE:\[([0-9]+)\]/', '', $stdout)));
208
        $this->set_error(trim($stderr));
209
        $this->set_exitstatus($retval[1]);
210
    }
211
212
    /**
213
     * Executes a command on a remote server (realtime output).
214
     *
215
     * @param  resource $stream SSH resource stream
216
     * @return void
217
     */
218
    final private function exec_realtime($stream)
219
    {
220
        while ($buffer = fgets($stream))
221
        {
222
            if (!preg_match('/RETURN_CODE:\[([0-9]+)\]/', $buffer, $retval))
223
            {
224
                print $buffer;
225
            }
226
            flush();
227
        }
228
        fclose($stream);
229
        $this->set_exitstatus($retval[1]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $retval does not seem to be defined for all execution paths leading up to this point.
Loading history...
230
    }
231
232
    /**
233
     * Executes a command on a remote server (writes output to local file).
234
     *
235
     * @param  resource $stream SSH resource stream
236
     * @return void
237
     */
238
    final private function exec_file($stream)
239
    {
240
        if ($this->get_filename() === null)
0 ignored issues
show
introduced by
The condition $this->get_filename() === null can never be true.
Loading history...
241
        {
242
            throw new \RuntimeException('A valid filename path must be provided.');
243
        }
244
245
        while ($line = fgets($stream))
246
        {
247
            flush();
248
            if (!preg_match('/RETURN_CODE:\[([0-9]+)\]/', $line, $retval))
249
            {
250
                file_put_contents($this->get_filename(), $line, FILE_APPEND | LOCK_EX);
251
            }
252
        }
253
        fclose($stream);
254
255
        $this->set_exitstatus($retval[1]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $retval does not seem to be defined for all execution paths leading up to this point.
Loading history...
256
    }
257
}
258