1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This class help for executing external programs |
5
|
|
|
* This is necessary as PHP Threads are not very popular |
6
|
|
|
* It also requires an extra dependency |
7
|
|
|
* Relying on system execution |
8
|
|
|
*/ |
9
|
|
|
|
10
|
|
|
use SA\CpeSdk; |
11
|
|
|
|
12
|
|
|
class CommandExecuter |
13
|
|
|
{ |
14
|
|
|
private $cpeLogger; |
15
|
|
|
private $logKey; |
16
|
|
|
|
17
|
|
|
const EXEC_FAILED = "EXEC_FAILED"; |
18
|
|
|
|
19
|
|
|
public function __construct($cpeLogger, $logKey = null) |
20
|
|
|
{ |
21
|
|
|
$this->cpeLogger = $cpeLogger; |
22
|
|
|
$this->logKey = $logKey; |
23
|
|
|
} |
24
|
|
|
|
25
|
|
|
public function execute( |
26
|
|
|
$cmd, |
27
|
|
|
$sleep = 1, |
28
|
|
|
$descriptors = array( |
29
|
|
|
1 => array("pipe", "w"), |
30
|
|
|
2 => array("pipe", "w") |
31
|
|
|
), |
32
|
|
|
$progressCallback = null, |
33
|
|
|
$progressCallbackParams = null, |
34
|
|
|
$showProgress = false, |
35
|
|
|
$callbackTurns = 0, |
36
|
|
|
$logKey = null) |
37
|
|
|
{ |
38
|
|
|
if ($logKey) |
39
|
|
|
$this->logKey = $logKey; |
40
|
|
|
|
41
|
|
|
$this->cpeLogger->logOut("INFO", basename(__FILE__), "Executing: $cmd", $this->logKey); |
42
|
|
|
|
43
|
|
|
// Start execution of $cmd |
44
|
|
|
if (!($process = proc_open($cmd, $descriptors, $pipes)) || |
45
|
|
|
!is_resource($process)) { |
46
|
|
|
$this->cpeLogger->logOut("ERROR", |
47
|
|
|
basename(__FILE__), "Unable to execute command:\n$cmd", |
48
|
|
|
$this->logKey); |
49
|
|
|
throw new CpeSdk\CpeException("Unable to execute command:\n$cmd\n", |
50
|
|
|
self::EXEC_FAILED); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
// Set the pipes as non-blocking |
54
|
|
View Code Duplication |
if (isset($descriptors[1]) && |
55
|
|
|
$descriptors[1]) { |
56
|
|
|
stream_set_blocking($pipes[1], FALSE); |
57
|
|
|
} |
58
|
|
View Code Duplication |
if (isset($descriptors[2]) && |
59
|
|
|
$descriptors[2]) { |
60
|
|
|
stream_set_blocking($pipes[2], FALSE); |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
$i = 0; |
64
|
|
|
|
65
|
|
|
// Used to store all output |
66
|
|
|
$allOut = ""; |
67
|
|
|
$allOutErr = ""; |
68
|
|
|
|
69
|
|
|
// Check process status at every turn |
70
|
|
|
do { |
71
|
|
|
sleep($sleep); |
72
|
|
|
|
73
|
|
|
// If callback only after N turns |
74
|
|
|
if ( !$callbackTurns || in_array($i, array(0, $callbackTurns)) ) |
75
|
|
|
{ |
76
|
|
|
if ($showProgress) { |
77
|
|
|
echo ".\n"; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
// Call user provided callback. |
81
|
|
|
// Callback should be an array as per doc here: |
82
|
|
|
// http://www.php.net/manual/en/language.types.callable.php |
83
|
|
|
// Type 3: Object method call |
84
|
|
|
if (isset($progressCallback) && $progressCallback) { |
85
|
|
|
call_user_func($progressCallback, $progressCallbackParams, |
86
|
|
|
$allOut, $allOutErr); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
$i = 0; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
// Get latest status |
93
|
|
|
$procStatus = proc_get_status($process); |
94
|
|
|
if ($showProgress) { |
95
|
|
|
echo "."; |
96
|
|
|
flush(); |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
// Read prog output |
100
|
|
View Code Duplication |
if (isset($pipes[1]) && $pipes[1]) { |
|
|
|
|
101
|
|
|
$out = stream_get_contents($pipes[1], -1); |
102
|
|
|
$allOut .= $out; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
// Read prog errors |
106
|
|
View Code Duplication |
if (isset($pipes[2]) && $pipes[2]) { |
|
|
|
|
107
|
|
|
$outErr = stream_get_contents($pipes[2], -1); |
108
|
|
|
$allOutErr .= $outErr; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
$i++; |
112
|
|
|
} while ($procStatus['running']); |
113
|
|
|
|
114
|
|
|
if (isset($pipes[1])) |
115
|
|
|
fclose($pipes[1]); |
116
|
|
|
if (isset($pipes[2])) |
117
|
|
|
fclose($pipes[2]); |
118
|
|
|
|
119
|
|
|
if ($procStatus['exitcode'] > 0) |
120
|
|
|
{ |
121
|
|
|
$this->cpeLogger->logOut("ERROR", |
122
|
|
|
basename(__FILE__), |
123
|
|
|
"Can't execute: $cmd. Exit Code: ".$procStatus['exitcode'], |
124
|
|
|
$this->logKey); |
125
|
|
|
if ($allOut) |
126
|
|
|
$this->cpeLogger->logOut("ERROR", |
127
|
|
|
basename(__FILE__), "COMMAND STDOUT: ".$allOut, |
128
|
|
|
$this->logKey); |
129
|
|
|
if ($allOutErr) |
130
|
|
|
$this->cpeLogger->logOut("ERROR", |
131
|
|
|
basename(__FILE__), "COMMAND STDERR: ".$allOutErr, |
132
|
|
|
$this->logKey); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
if ($showProgress) { |
136
|
|
|
echo "\n"; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
// Process is over |
140
|
|
|
proc_close($process); |
141
|
|
|
|
142
|
|
|
return array('out' => $allOut, 'outErr' => $allOutErr); |
143
|
|
|
} |
144
|
|
|
} |
145
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.