1 | <?php |
||||
2 | namespace LibSSH2\Sessions; |
||||
3 | |||||
4 | use LibSSH2\Authentication\Authentication; |
||||
5 | use LibSSH2\Configuration; |
||||
6 | use LibSSH2\Connection; |
||||
7 | |||||
8 | /** |
||||
9 | * SFTP class. |
||||
10 | * |
||||
11 | * SFTP interface. |
||||
12 | * |
||||
13 | * @package LibSSH2\Sessions |
||||
14 | */ |
||||
15 | class SFTP extends Connection |
||||
16 | { |
||||
17 | |||||
18 | /** |
||||
19 | * SFTP connection resource. |
||||
20 | * |
||||
21 | * @var resource |
||||
22 | */ |
||||
23 | private $sftp; |
||||
24 | |||||
25 | /** |
||||
26 | * Constructor. |
||||
27 | * |
||||
28 | * @param instance $configuration Configuration instance |
||||
29 | * @param instance $authentication Authentication instance |
||||
30 | * @return void |
||||
31 | */ |
||||
32 | public function __construct(Configuration $configuration, Authentication $authentication) |
||||
33 | { |
||||
34 | parent::__construct($configuration, $authentication); |
||||
35 | |||||
36 | $this->sftp = @ssh2_sftp($this->connection); |
||||
37 | if (!$this->sftp) |
||||
38 | { |
||||
39 | throw new \RuntimeException($this->get_error_message()); |
||||
40 | } |
||||
41 | } |
||||
42 | |||||
43 | /** |
||||
44 | * Copies file. |
||||
45 | * |
||||
46 | * @param string $srcfile path to the source file |
||||
47 | * @param string $destfile path to the destination file |
||||
48 | * @return void |
||||
49 | */ |
||||
50 | final public function copy($srcfile, $destfile) |
||||
51 | { |
||||
52 | if ($this->is_file($srcfile) === FALSE) |
||||
53 | { |
||||
54 | $this->set_error('Local file '.$srcfile.' does not exist.'); |
||||
55 | $this->set_exitstatus(1); |
||||
56 | return; |
||||
57 | } |
||||
58 | |||||
59 | if (@copy($this->sftp_url($srcfile), $this->sftp_url($destfile)) === FALSE) |
||||
60 | { |
||||
61 | $this->set_error($this->get_error_message()); |
||||
62 | $this->set_exitstatus(1); |
||||
63 | return; |
||||
64 | } |
||||
65 | $this->set_output('Successfully copied file at: '.$srcfile.' to '.$destfile); |
||||
66 | $this->set_exitstatus(0); |
||||
67 | } |
||||
68 | |||||
69 | /** |
||||
70 | * Removes files. |
||||
71 | * |
||||
72 | * Caveats: |
||||
73 | * - accepts one or more files (recursive) |
||||
74 | * |
||||
75 | * @param mixed $files files to remove |
||||
76 | * @return void |
||||
77 | */ |
||||
78 | final public function delete($files) |
||||
79 | { |
||||
80 | $files = !is_array($files) ? [$files] : $files; |
||||
81 | foreach ($files as $file) |
||||
82 | { |
||||
83 | if ($this->is_file($file)) |
||||
84 | { |
||||
85 | if (@unlink($this->sftp_url($file)) === false) |
||||
86 | { |
||||
87 | $this->set_error($this->get_error_message()); |
||||
88 | $this->set_exitstatus(1); |
||||
89 | return; |
||||
90 | } |
||||
91 | continue; |
||||
92 | } |
||||
93 | } |
||||
94 | $this->set_output('Successfully deleted remote file(s) at: '.implode(', ', $files)); |
||||
95 | $this->set_exitstatus(0); |
||||
96 | } |
||||
97 | |||||
98 | /** |
||||
99 | * Tells whether the filename is a directory. |
||||
100 | * |
||||
101 | * @param string $path directory path |
||||
102 | * @return boolean |
||||
103 | */ |
||||
104 | final public function is_dir($path) |
||||
105 | { |
||||
106 | return is_dir($this->sftp_url($path)); |
||||
107 | } |
||||
108 | |||||
109 | /** |
||||
110 | * Tells whether the filename is a file. |
||||
111 | * |
||||
112 | * @param string $path directory path |
||||
113 | * @return boolean |
||||
114 | */ |
||||
115 | final public function is_file($path) |
||||
116 | { |
||||
117 | return is_file($this->sftp_url($path)); |
||||
118 | } |
||||
119 | |||||
120 | /** |
||||
121 | * Directory listing. |
||||
122 | * |
||||
123 | * @param string $path directory path |
||||
124 | * @return void |
||||
125 | */ |
||||
126 | final public function ls($path) |
||||
127 | { |
||||
128 | $files = []; |
||||
129 | if ($handle = @opendir($this->sftp_url($path))) |
||||
130 | { |
||||
131 | while (($file = @readdir($handle)) !== false) |
||||
132 | { |
||||
133 | if ($file != '.' && $file != '..') |
||||
134 | { |
||||
135 | $filename = rtrim($path, '/').'/'.$file; |
||||
136 | if ($this->is_dir($filename)) |
||||
137 | { |
||||
138 | $files['directories'][] = $filename; |
||||
139 | } |
||||
140 | |||||
141 | if ($this->is_dir($filename) === false) |
||||
142 | { |
||||
143 | $files['files'][] = $filename; |
||||
144 | } |
||||
145 | } |
||||
146 | } |
||||
147 | closedir($handle); |
||||
148 | } |
||||
149 | $this->set_output($files); |
||||
150 | $this->set_exitstatus(0); |
||||
151 | } |
||||
152 | |||||
153 | /** |
||||
154 | * Returns pathnames matching a pattern (for files only & hidden files). |
||||
155 | * |
||||
156 | * @param string $directory directory |
||||
157 | * @param mixed $pattern pattern (does not support tilde expansion) |
||||
158 | * @return mixed matched files or null |
||||
159 | */ |
||||
160 | final public function glob($directory, $pattern = '') |
||||
161 | { |
||||
162 | if ($this->is_dir($directory) == false) |
||||
0 ignored issues
–
show
|
|||||
163 | { |
||||
164 | $this->set_error($this->get_error_message()); |
||||
165 | $this->set_exitstatus(1); |
||||
166 | return; |
||||
167 | } |
||||
168 | |||||
169 | $handle = opendir($this->sftp_url($directory)); |
||||
170 | while (($file = readdir($handle)) !== false) |
||||
0 ignored issues
–
show
It seems like
$handle can also be of type false ; however, parameter $dir_handle of readdir() does only seem to accept resource , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
171 | { |
||||
172 | $files[] = preg_grep('/(^.*'.$pattern.'.*$)/', explode(PHP_EOL, $file)); |
||||
173 | } |
||||
174 | |||||
175 | if (empty($files)) |
||||
176 | { |
||||
177 | $this->set_output([]); |
||||
178 | $this->set_exitstatus(0); |
||||
179 | return; |
||||
180 | } |
||||
181 | |||||
182 | $files = array_reduce($files, 'array_merge', []); |
||||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||||
183 | $files = array_diff($files, ['.', '..']); |
||||
184 | |||||
185 | $_files = []; |
||||
186 | foreach ($files as $file) |
||||
187 | { |
||||
188 | $_files[] = rtrim($directory, '/').'/'.$file; |
||||
189 | } |
||||
190 | $this->set_output($_files); |
||||
191 | $this->set_exitstatus(0); |
||||
192 | } |
||||
193 | |||||
194 | /** |
||||
195 | * Create new directory. |
||||
196 | * |
||||
197 | * @param mizxed $directories path to the directory |
||||
0 ignored issues
–
show
The type
LibSSH2\Sessions\mizxed 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. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||||
198 | * @param int $mode directory permission mode value (octal) |
||||
199 | * @param bool $recursive create directories as needed |
||||
200 | * @param int $chgrp change gid for directory |
||||
201 | * @return bool |
||||
202 | */ |
||||
203 | final public function mkdir($directories, $mode = 0777, $recursive = false) |
||||
204 | { |
||||
205 | $directories = !is_array($directories) ? [$directories] : $directories; |
||||
0 ignored issues
–
show
|
|||||
206 | foreach ($directories as $directory) |
||||
207 | { |
||||
208 | if (@mkdir($this->sftp_url($directory), $mode, $recursive) === false) |
||||
209 | { |
||||
210 | $this->set_error($this->get_error_message()); |
||||
211 | $this->set_exitstatus(1); |
||||
212 | return; |
||||
213 | } |
||||
214 | } |
||||
215 | $this->set_output('Successfully created remote directory(ies) at: '.implode(', ', $directories)); |
||||
216 | $this->set_exitstatus(0); |
||||
217 | } |
||||
218 | |||||
219 | /** |
||||
220 | * Moves file to different path (rename). |
||||
221 | * |
||||
222 | * @param string $oldfile path to the old file |
||||
223 | * @param string $newfile path to the new file |
||||
224 | * @return void |
||||
225 | */ |
||||
226 | final public function rename($oldfile, $newfile) |
||||
227 | { |
||||
228 | if ($this->is_file($oldfile) === false) |
||||
229 | { |
||||
230 | $this->set_error('Local file '.$oldfile.' does not exist.'); |
||||
231 | $this->set_exitstatus(1); |
||||
232 | return; |
||||
233 | } |
||||
234 | |||||
235 | if (!@ssh2_sftp_rename($this->sftp, $oldfile, $newfile)) |
||||
236 | { |
||||
237 | $this->set_error($this->get_error_message()); |
||||
238 | $this->set_exitstatus(1); |
||||
239 | return; |
||||
240 | } |
||||
241 | $this->set_output('Successfully renamed remote file at: '.$oldfile.' to '.$newfile); |
||||
242 | $this->set_exitstatus(0); |
||||
243 | } |
||||
244 | |||||
245 | /** |
||||
246 | * Removes directory. |
||||
247 | * |
||||
248 | * Caveats: |
||||
249 | * - directories that are not empty are emptied then deleted |
||||
250 | * - accepts one or more directories (recursive) |
||||
251 | * |
||||
252 | * @param mixed $directories directory or directories to remove |
||||
253 | * @return boolean |
||||
254 | */ |
||||
255 | final public function rmdir($directories) |
||||
256 | { |
||||
257 | $directories = !is_array($directories) ? [$directories] : $directories; |
||||
258 | foreach ($directories as $directory) |
||||
259 | { |
||||
260 | if ($this->is_dir($directory)) |
||||
261 | { |
||||
262 | start: |
||||
263 | $this->ls($directory)['files']; |
||||
0 ignored issues
–
show
Are you sure the usage of
$this->ls($directory) targeting LibSSH2\Sessions\SFTP::ls() seems to always return null.
This check looks for function or method calls that always return null and whose return value is used. class A
{
function getObject()
{
return null;
}
}
$a = new A();
if ($a->getObject()) {
The method The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes. ![]() |
|||||
264 | $stdout = $this->get_output(); |
||||
265 | $files = array_key_exists('files', $stdout) ? $stdout['files'] : []; |
||||
266 | if (count($files) == 0) |
||||
267 | { |
||||
268 | if (@rmdir($this->sftp_url($directory)) === false) |
||||
269 | { |
||||
270 | $this->set_error($this->get_error_message()); |
||||
271 | $this->set_exitstatus(1); |
||||
272 | return; |
||||
273 | } |
||||
274 | continue; |
||||
275 | } |
||||
276 | |||||
277 | if (count($files) != 0) |
||||
278 | { |
||||
279 | $this->delete($files); |
||||
280 | goto start; |
||||
281 | } |
||||
282 | } |
||||
283 | } |
||||
284 | $this->set_output('Successfully removed remote directory(ies) at: '.implode(', ', $directories)); |
||||
285 | $this->set_exitstatus(0); |
||||
286 | } |
||||
287 | |||||
288 | /** |
||||
289 | * Sends a file via SCP. |
||||
290 | * |
||||
291 | * @param mixed $local source file(s) (local) |
||||
292 | * @param string $remote_dir destination directory (remote) |
||||
293 | * @param integer $mode permissions on the new file |
||||
294 | * @return void |
||||
295 | */ |
||||
296 | final public function put($local_files, $remote_dir, $mode = 0750) |
||||
297 | { |
||||
298 | $local_files = !is_array($local_files) ? [$local_files] : $local_files; |
||||
299 | foreach ($local_files as $local_file) |
||||
300 | { |
||||
301 | if (!@ssh2_scp_send($this->connection, $local_file, rtrim($remote_dir, '/').'/'.basename($local_file), $mode)) |
||||
302 | { |
||||
303 | $this->set_error($this->get_error_message()); |
||||
304 | $this->set_exitstatus(1); |
||||
305 | return; |
||||
306 | } |
||||
307 | } |
||||
308 | $this->set_output('Successfully sent local files to remote host at: '.implode(', ', $local_files)); |
||||
309 | $this->set_exitstatus(0); |
||||
310 | } |
||||
311 | |||||
312 | /** |
||||
313 | * Recieves a file via SCP. |
||||
314 | * |
||||
315 | * @param mixed $local source file(s) (local) |
||||
316 | * @param string $remote_dir destination directory (remote) |
||||
317 | * @return void |
||||
318 | */ |
||||
319 | final public function get($remote_files, $local_dir) |
||||
320 | { |
||||
321 | $remote_files = !is_array($remote_files) ? [$remote_files] : $remote_files; |
||||
322 | foreach ($remote_files as $remote_file) |
||||
323 | { |
||||
324 | if (!@ssh2_scp_recv($this->connection, $remote_file, rtrim($local_dir, '/').'/'.basename($remote_file))) |
||||
325 | { |
||||
326 | $this->set_error($this->get_error_message()); |
||||
327 | $this->set_exitstatus(1); |
||||
328 | return; |
||||
329 | } |
||||
330 | } |
||||
331 | $this->set_output('Successfully received remote files: '.implode(', ', $remote_files)); |
||||
332 | $this->set_exitstatus(0); |
||||
333 | } |
||||
334 | |||||
335 | /** |
||||
336 | * Returns stat a file on a remote filesystem. |
||||
337 | * |
||||
338 | * @param string $path directory path |
||||
339 | * @return void |
||||
340 | */ |
||||
341 | final public function stat($path) |
||||
342 | { |
||||
343 | $statinfo = ssh2_sftp_stat($this->sftp, $path); |
||||
344 | |||||
345 | if ($statinfo === false) |
||||
0 ignored issues
–
show
|
|||||
346 | { |
||||
347 | $this->set_error($this->get_error_message()); |
||||
348 | $this->set_exitstatus(0); |
||||
349 | return; |
||||
350 | } |
||||
351 | |||||
352 | $statinfo = [ |
||||
353 | 'size' => $statinfo['size'], |
||||
354 | 'groupid' => $statinfo['gid'], |
||||
355 | 'userid' => $statinfo['uid'], |
||||
356 | 'atime' => date('c', $statinfo['atime']), |
||||
357 | 'mtime' => date('c', $statinfo['mtime']), |
||||
358 | 'mode' => $statinfo['mode'], |
||||
359 | ]; |
||||
360 | $this->set_output($statinfo); |
||||
361 | $this->set_exitstatus(0); |
||||
362 | } |
||||
363 | |||||
364 | /** |
||||
365 | * Create SFTP URL wrapper for unsupported commands. |
||||
366 | * |
||||
367 | * @param string $path directory path |
||||
368 | * @return string SFTP connection wrapprer |
||||
369 | */ |
||||
370 | final private function sftp_url($path = '') |
||||
371 | { |
||||
372 | return 'ssh2.sftp://'.$this->sftp.$path; |
||||
0 ignored issues
–
show
Are you sure
$this->sftp of type resource can be used in concatenation ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||
373 | } |
||||
374 | |||||
375 | } |
||||
376 |
When comparing two booleans, it is generally considered safer to use the strict comparison operator.