1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* DronePHP (http://www.dronephp.com) |
4
|
|
|
* |
5
|
|
|
* @link http://github.com/Pleets/DronePHP |
6
|
|
|
* @copyright Copyright (c) 2016-2018 Pleets. (http://www.pleets.org) |
7
|
|
|
* @license http://www.dronephp.com/license |
8
|
|
|
* @author Darío Rivera <[email protected]> |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Drone\FileSystem; |
12
|
|
|
|
13
|
|
|
use Drone\Error\Errno; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Shell class |
17
|
|
|
* |
18
|
|
|
* This class represents a system terminal |
19
|
|
|
*/ |
20
|
|
|
class Shell implements ShellInterface |
21
|
|
|
{ |
22
|
|
|
use \Drone\Error\ErrorTrait; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Home directory (~) |
26
|
|
|
* |
27
|
|
|
* @var string |
28
|
|
|
*/ |
29
|
|
|
private $home; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* Result of last command (optional) |
33
|
|
|
* |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
private $buffer = null; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* Returns the home attribute |
40
|
|
|
* |
41
|
|
|
* @return string |
42
|
|
|
*/ |
43
|
|
|
public function getHome() |
44
|
|
|
{ |
45
|
|
|
return $this->home; |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* Returns the buffer attribute |
50
|
|
|
* |
51
|
|
|
* @return mixed |
52
|
|
|
*/ |
53
|
|
|
public function getBuffer() |
54
|
|
|
{ |
55
|
|
|
return $this->buffer; |
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Constructor |
60
|
|
|
* |
61
|
|
|
* @param string $home |
62
|
|
|
*/ |
63
|
|
|
public function __construct($home = null) |
64
|
|
|
{ |
65
|
|
|
$this->home = (is_null($home) || empty($home)) ? $this->pwd() : $home; |
|
|
|
|
66
|
|
|
$this->cd($this->home); |
|
|
|
|
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Iterative function for directories and files |
71
|
|
|
* |
72
|
|
|
* @param string $directory |
73
|
|
|
* @param callable $fileCallback |
74
|
|
|
* @param callable $dirCallback |
75
|
|
|
* @param callable $callback |
76
|
|
|
* @param boolean $showParentPath |
77
|
|
|
* |
78
|
|
|
* @return array |
79
|
|
|
*/ |
80
|
|
|
public function getContents($directory, $fileCallback, $dirCallback, $callback = null, $showParentPath = false) |
81
|
|
|
{ |
82
|
|
|
$contents = []; |
83
|
|
|
|
84
|
|
|
if (is_dir($directory)) |
85
|
|
|
{ |
86
|
|
|
foreach ($this->ls($directory) as $item) |
87
|
|
|
{ |
88
|
|
|
if ($item != '.' && $item != '..') |
89
|
|
|
$contents[] = $item; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
//$allContents = $contents; |
93
|
|
|
|
94
|
|
|
if (count($contents) > 0) |
95
|
|
|
{ |
96
|
|
|
foreach ($contents as $i) |
97
|
|
|
{ |
98
|
|
|
if (is_file($directory.'/'.$i)) |
99
|
|
|
{ |
100
|
|
|
$allContents[] = ($showParentPath) ? $directory.'/'.$i : $i; |
101
|
|
|
|
102
|
|
|
$this->buffer = $directory.'/'.$i; |
103
|
|
|
call_user_func($fileCallback, $this); |
104
|
|
|
} |
105
|
|
|
else if (is_dir($directory.'/'.$i)) |
106
|
|
|
{ |
107
|
|
|
$allContents[] = ($showParentPath) ? $directory.'/'.$i : $i; |
108
|
|
|
$allContents[] = $this->getContents($directory.'/'.$i, $fileCallback, $dirCallback, null, false); |
109
|
|
|
|
110
|
|
|
$this->buffer = $directory.'/'.$i; |
111
|
|
|
call_user_func($dirCallback, $this); |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
} |
116
|
|
|
else if (is_file($directory)) |
117
|
|
|
throw new \InvalidArgumentException("'$directory' is actually a file"); |
118
|
|
|
else |
119
|
|
|
throw new \InvalidArgumentException("The directory '$directory' does not exists"); |
120
|
|
|
|
121
|
|
|
if (!is_null($callback)) |
122
|
|
|
call_user_func($callback, $this); |
123
|
|
|
|
124
|
|
|
return $allContents; |
|
|
|
|
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
/** |
128
|
|
|
* Returns the curent directory |
129
|
|
|
* |
130
|
|
|
* @return string|boolean |
131
|
|
|
*/ |
132
|
|
|
public function pwd() |
133
|
|
|
{ |
134
|
|
|
if (getcwd()) |
135
|
|
|
$this->buffer = getcwd(); |
136
|
|
|
else |
137
|
|
|
return false; |
138
|
|
|
|
139
|
|
|
return $this->buffer; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
/** |
143
|
|
|
* Returns a list with directory contents |
144
|
|
|
* |
145
|
|
|
* @param string|null $path |
146
|
|
|
* @param boolean $recursive |
147
|
|
|
* |
148
|
|
|
* @return array |
149
|
|
|
*/ |
150
|
|
|
public function ls($path = null, $recursive = false) |
151
|
|
|
{ |
152
|
|
|
$filesToReturn = []; |
153
|
|
|
|
154
|
|
|
$path = (is_null($path) || empty($path)) ? '.' : $path; |
155
|
|
|
|
156
|
|
|
if (is_file($path)) |
157
|
|
|
$filesToReturn = array($path); |
158
|
|
|
else if (is_dir($path)) |
159
|
|
|
{ |
160
|
|
|
$pathIns = dir($path); |
161
|
|
|
|
162
|
|
|
if ($recursive) |
163
|
|
|
{ |
164
|
|
|
$dirs = $files = []; |
165
|
|
|
|
166
|
|
|
$filesToReturn = $this->getContents($path, |
167
|
|
|
# file's callback |
168
|
|
|
function($event) use (&$files) |
169
|
|
|
{ |
170
|
|
|
$files[] = $event->getBuffer(); |
171
|
|
|
}, |
172
|
|
|
# folder's callback |
173
|
|
|
function($event) use (&$dirs) |
174
|
|
|
{ |
175
|
|
|
$dirs[] = $event->getBuffer(); |
176
|
|
|
} |
177
|
|
|
); |
178
|
|
|
|
179
|
|
|
/*foreach ($dirs as $item) |
180
|
|
|
$filesToReturn[] = $item; |
181
|
|
|
|
182
|
|
|
foreach ($files as $item) |
183
|
|
|
$filesToReturn[] = $item;*/ |
184
|
|
|
} |
185
|
|
|
else |
186
|
|
|
{ |
187
|
|
|
while (($item = $pathIns->read()) !== false) |
188
|
|
|
{ |
189
|
|
|
if ($item != '.' && $item != '..') |
190
|
|
|
$filesToReturn[] = $item; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
$pathIns->close(); |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
else { |
197
|
|
|
|
198
|
|
|
$contents = $this->ls(); |
199
|
|
|
|
200
|
|
|
foreach ($contents as $item) |
201
|
|
|
{ |
202
|
|
|
if (!empty($path)) |
203
|
|
|
if (!strlen(stristr($item, $path)) > 0) |
204
|
|
|
continue; |
205
|
|
|
if (strstr($item,'~') === false && $item != '.' && $item != '..') |
206
|
|
|
$filesToReturn[] = $item; |
207
|
|
|
} |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
return $filesToReturn; |
211
|
|
|
} |
212
|
|
|
|
213
|
|
|
/** |
214
|
|
|
* Changes the current directory |
215
|
|
|
* |
216
|
|
|
* @param string|null $path |
217
|
|
|
* |
218
|
|
|
* @return boolean |
219
|
|
|
*/ |
220
|
|
|
public function cd($path = null) |
221
|
|
|
{ |
222
|
|
|
$path = (is_null($path) || empty($path)) ? $this->home : $path; |
223
|
|
|
|
224
|
|
|
if (is_dir($path)) |
225
|
|
|
{ |
226
|
|
|
if (chdir($path)) |
227
|
|
|
return true; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
return false; |
231
|
|
|
} |
232
|
|
|
|
233
|
|
|
/** |
234
|
|
|
* Changes the file's date |
235
|
|
|
* |
236
|
|
|
* @param string |
237
|
|
|
* |
238
|
|
|
* @return boolean |
239
|
|
|
*/ |
240
|
|
|
public function touch($file) |
241
|
|
|
{ |
242
|
|
|
$contents = file_exists($file) ? file_get_contents($file) : ""; |
243
|
|
|
|
244
|
|
|
$hd = @fopen($file, "w+"); |
245
|
|
|
|
246
|
|
|
if (!$hd || @fwrite($hd, $contents) === false) |
|
|
|
|
247
|
|
|
{ |
248
|
|
|
$this->error(Errno::FILE_PERMISSION_DENIED, $file); |
249
|
|
|
return false; |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
@fclose($hd); |
|
|
|
|
253
|
|
|
|
254
|
|
|
return true; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
/** |
258
|
|
|
* Deletes one or more files |
259
|
|
|
* |
260
|
|
|
* @param string $file |
261
|
|
|
* @param boolean|null $recursive |
262
|
|
|
* |
263
|
|
|
* @return boolean |
264
|
|
|
*/ |
265
|
|
|
public function rm($file, $recursive = null) |
266
|
|
|
{ |
267
|
|
|
$recursive = is_null($recursive) ? false : $recursive; |
268
|
|
|
|
269
|
|
|
if (is_null($file)) |
|
|
|
|
270
|
|
|
throw new \InvalidArgumentException("Missing first parameter"); |
271
|
|
|
|
272
|
|
|
if (file_exists($file) && !$recursive) |
273
|
|
|
unlink($file); |
274
|
|
|
elseif (is_dir($file) && $recursive) |
275
|
|
|
{ |
276
|
|
|
$that = $this; |
277
|
|
|
|
278
|
|
|
$this->getContents($file, function() use ($that) { |
279
|
|
|
unlink($that->getBuffer()); |
280
|
|
|
}, function() use ($that) { |
281
|
|
|
rmdir($that->getBuffer()); |
282
|
|
|
}, function() use ($file) { |
283
|
|
|
@rmdir($file); |
|
|
|
|
284
|
|
|
}); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
return true; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
/** |
291
|
|
|
* Copies one or more files or directories |
292
|
|
|
* |
293
|
|
|
* @param string $file |
294
|
|
|
* @param string $dest |
295
|
|
|
* @param boolean|null $recursive |
296
|
|
|
* |
297
|
|
|
* @return boolean |
298
|
|
|
*/ |
299
|
|
|
public function cp($file, $dest, $recursive = null) |
300
|
|
|
{ |
301
|
|
|
$recursive = (is_null($recursive)) ? false : $recursive; |
302
|
|
|
|
303
|
|
|
if (empty($file) || empty($dest)) |
304
|
|
|
throw new \InvalidArgumentException("Missing parameters"); |
305
|
|
|
|
306
|
|
|
if (is_dir($file) && !$recursive) |
307
|
|
|
throw new \RuntimeException("Ommiting directory <<$foo>>"); |
|
|
|
|
308
|
|
|
|
309
|
|
|
if (is_dir($file) && (is_dir($dest) || !file_exists($dest))) |
310
|
|
|
{ |
311
|
|
|
$files = [ |
312
|
|
|
"files" => [], |
313
|
|
|
"folders" => [] |
314
|
|
|
]; |
315
|
|
|
|
316
|
|
|
if (is_dir($dest)) |
317
|
|
|
$files["folders"][] = basename($file); |
318
|
|
|
|
319
|
|
|
if (!file_exists($dest)) |
320
|
|
|
mkdir($dest); |
321
|
|
|
|
322
|
|
|
$that = $this; |
323
|
|
|
|
324
|
|
|
$this->getContents($file, |
325
|
|
|
# file's callback |
326
|
|
|
function() use($that, &$files) |
327
|
|
|
{ |
328
|
|
|
$files["files"][] = $that->getBuffer(); |
329
|
|
|
}, |
330
|
|
|
# folder's callback |
331
|
|
|
function() use($that, &$files) |
332
|
|
|
{ |
333
|
|
|
$files["folders"][] = $that->getBuffer(); |
334
|
|
|
}, |
335
|
|
|
# final callback |
336
|
|
|
function() use (&$files, $dest) |
337
|
|
|
{ |
338
|
|
|
if (count($files["folders"])) |
339
|
|
|
{ |
340
|
|
|
foreach ($files["folders"] as $folder) |
341
|
|
|
{ |
342
|
|
|
if (!file_exists($dest.'/'.$folder)) |
343
|
|
|
mkdir("$dest/$folder", 0777, true); |
344
|
|
|
} |
345
|
|
|
} |
346
|
|
|
|
347
|
|
|
if (count($files["files"])) |
348
|
|
|
{ |
349
|
|
|
foreach ($files["files"] as $item) |
350
|
|
|
{ |
351
|
|
|
if (!file_exists("$dest/$files")) |
352
|
|
|
copy($item, $dest.'/'.$item); |
353
|
|
|
} |
354
|
|
|
} |
355
|
|
|
} |
356
|
|
|
); |
357
|
|
|
} |
358
|
|
|
|
359
|
|
|
if (is_dir($dest)) |
360
|
|
|
{ |
361
|
|
|
if (!$recursive) |
362
|
|
|
copy($file, $dest.'/'.$file); |
363
|
|
|
else { |
364
|
|
|
|
365
|
|
|
$files = array(); |
366
|
|
|
$files[0] = array(); |
367
|
|
|
$files[1] = array(); |
368
|
|
|
|
369
|
|
|
$_SESSION["BUFFER"]["EXO"]["cp"][2] = $dest; |
370
|
|
|
|
371
|
|
|
$that = $this; |
372
|
|
|
|
373
|
|
|
$this->getContents($file, function() use($that, &$files) { |
374
|
|
|
$files[0][] = $that->getBuffer(); |
375
|
|
|
}, function() use($that, &$files) { |
376
|
|
|
$files[1][] = $that->getBuffer(); |
377
|
|
|
}, function() use ($files, $dest) { |
378
|
|
|
|
379
|
|
|
foreach ($files[1] as $item) |
380
|
|
|
{ |
381
|
|
|
if (!file_exists($dest.'/'.$item)) |
382
|
|
|
mkdir("$dest/$item", 0777, true); |
383
|
|
|
} |
384
|
|
|
|
385
|
|
|
foreach ($files[0] as $item) |
386
|
|
|
{ |
387
|
|
|
if (!file_exists("$dest/$files")) |
388
|
|
|
copy($item, $dest.'/'.$item); |
389
|
|
|
} |
390
|
|
|
}); |
391
|
|
|
} |
392
|
|
|
} |
393
|
|
|
else |
394
|
|
|
copy($file, $dest); |
395
|
|
|
|
396
|
|
|
return true; |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
/** |
400
|
|
|
* Moves or renames files |
401
|
|
|
* |
402
|
|
|
* @param string $oldfile |
403
|
|
|
* @param string $newfile |
404
|
|
|
* |
405
|
|
|
* @return boolean |
406
|
|
|
*/ |
407
|
|
|
public function mv($oldfile, $newfile) |
408
|
|
|
{ |
409
|
|
|
if (empty($oldfile)) |
410
|
|
|
throw new \InvalidArgumentException("Missing first parameter"); |
411
|
|
|
|
412
|
|
|
if (is_dir($newfile)) |
413
|
|
|
$newfile .= '/'.basename($oldfile); |
414
|
|
|
|
415
|
|
|
if ($oldfile == $newfile) |
416
|
|
|
throw new \Exception("'$oldfile' and '$newfile' are the same file"); |
417
|
|
|
|
418
|
|
|
if(!rename($oldfile, $newfile)) |
419
|
|
|
return false; |
420
|
|
|
|
421
|
|
|
return true; |
422
|
|
|
} |
423
|
|
|
|
424
|
|
|
/** |
425
|
|
|
* Creates a directory |
426
|
|
|
* |
427
|
|
|
* @param string $dir |
428
|
|
|
* @param string|null $dest |
429
|
|
|
* @param boolean|null $recursive |
430
|
|
|
* |
431
|
|
|
* @return boolean |
432
|
|
|
*/ |
433
|
|
|
public function mkdir($dir, $dest = null, $recursive = null) |
434
|
|
|
{ |
435
|
|
|
if (empty($dir)) |
436
|
|
|
throw new \InvalidArgumentException("Missing first parameter"); |
437
|
|
|
|
438
|
|
|
if (empty($dest)) |
439
|
|
|
$dest = '.'; |
440
|
|
|
|
441
|
|
|
$recursive = (is_null($recursive)) ? false : $recursive; |
442
|
|
|
|
443
|
|
|
if ($recursive) |
444
|
|
|
mkdir("$dest/$dir", 0777, true); |
445
|
|
|
else { |
446
|
|
|
if (!is_dir($dir)) |
447
|
|
|
{ |
448
|
|
|
if(!mkdir("$dir", 0777)) |
449
|
|
|
return false; |
450
|
|
|
} |
451
|
|
|
} |
452
|
|
|
return true; |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* Deletes a directory |
457
|
|
|
* |
458
|
|
|
* @param string $dir |
459
|
|
|
* |
460
|
|
|
* @return boolean |
461
|
|
|
*/ |
462
|
|
|
public function rmdir($dir) |
463
|
|
|
{ |
464
|
|
|
if (is_null($dir) || empty($dir)) |
465
|
|
|
throw new \RuntimeException("Missing first parameter"); |
466
|
|
|
|
467
|
|
|
if (rmdir($dir)) |
468
|
|
|
return true; |
469
|
|
|
else |
470
|
|
|
return false; |
471
|
|
|
} |
472
|
|
|
} |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.