shell_exec () إدارة المهلة & exec ()
-
25-09-2019 - |
سؤال
أقوم بتشغيل برنامج نصي من طرف ثالث باستخدام فئة Wrapper التي كتبتها والتي تستدعي shell_exec()
والأنابيب في ملف I تحليله لاحقًا باستخدام رمز PHP. أود أن أذكر أن هذا يعمل ، لكنني أحاول تعزيز الوظيفة ، بعد أن واجهت حالة استخدام لم أفكر فيها.
كيف من الأفضل إدارة المهلة على shell_exec ()؟ كنت أفكر في لفه في try() catch()
لكنني لست متأكدًا من أفضل طريقة التعامل مع مكون الوقت.
لقد قرأت بعض الأسئلة هنا المتعلقة shell_exec()
و exec()
ويبدو أنه عن طريق تمرير المعاملات الإخراج إلى exec()
يمكنك الحصول على عائد ، ولكن هذا يعتمد على البرنامج النصي مع حالة الإرجاع. بالإضافة إلى صفحة الاختبار المصغرة الخاصة بي ، لا يمكنني الحصول على إرجاع أي إخراج!
كان الخيار الآخر الذي فكرت فيه هو استخدام مربع حوار وسيط ، مع دوران على نمط Ajax بينما يعمل البرنامج النصي الذي يعمل ، ووضع مهلة يدوية في JavaScript. التي أعطت بعد ذلك المستخدم رسالة حوار نموذج عن الفشل/المهلة والنهاية.
هل هناك أي طرق مقبولة لحالة الاستخدام هذه؟
اختباري المصغر ، يتكون من ما يلي ،
public $e_return = array();
public $e_status = '';
// Paths are absolute from /
public function execCheck($domain){
exec($this->ssl_check_path." -s ".$domain." -p 443 > ".$this->folder.$this->filename." 2>&1 &", &$this->e_return, &$this->e_status);
}
// Returns
Array
(
)
0
باستخدام هذا السؤال كمرجع ، لا يمكن تنفيذ البرنامج النصي PHP باستخدام PHP EXEC
المحلول
أود أن أقترح عليك النظر في استخدام proc_open
. يمكنك تكوينه لإرجاع مورد تيار ، والاحتفاظ به يدويًا مؤقتًا ، وإذا انتهت صلاحية المؤقت قبل اكتمال العملية ، يمكنك إنهاءه proc_terminate
. إذا اكتمل قبل انتهاء صلاحية المؤقت ، فيمكنك الاستخدام proc_close
من ثم stream_get_contents
للاستيلاء على البيانات التي كان من شأنها أن تتم كتابتها إلى stdout.
نصائح أخرى
أكتب بعضًا من الكود العاملة لهذه المهمة. تقوم الدالة بإرجاع رمز الخروج (0 - موافق ،> 0 - خطأ) ويكتب stdout ، Stderr للمرجع المتغيرات.
/*execute program and write all output to $out
terminate program if it runs more than 30 seconds */
execute("program --option", null, $out, $out, 30);
echo $out;
function execute($cmd, $stdin=null, &$stdout, &$stderr, $timeout=false)
{
$pipes = array();
$process = proc_open(
$cmd,
array(array('pipe','r'),array('pipe','w'),array('pipe','w')),
$pipes
);
$start = time();
$stdout = '';
$stderr = '';
if(is_resource($process))
{
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
fwrite($pipes[0], $stdin);
fclose($pipes[0]);
}
while(is_resource($process))
{
//echo ".";
$stdout .= stream_get_contents($pipes[1]);
$stderr .= stream_get_contents($pipes[2]);
if($timeout !== false && time() - $start > $timeout)
{
proc_terminate($process, 9);
return 1;
}
$status = proc_get_status($process);
if(!$status['running'])
{
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($process);
return $status['exitcode'];
}
usleep(100000);
}
return 1;
}
حاولت مع popen()
, ، ولكن لا توجد طريقة لإنهاء Proccess بعد ذلك. ايضا، stream_get_contents()
يحجب الدفق حتى عند استخدام Stream_set_blocking على Windows ، لذلك اضطررت إلى استخدام Fread بدلاً من ذلك. علاوة على ذلك ، لا يعمل Proc_erminate بشكل صحيح على Windows ، لذلك اضطررت إلى استخدام وظيفة القتل البديلة.
لقد توصلت إلى هذا ، يجب أن يعمل على كل من Windows و Linux الآن:
function execute($command, $timeout = 5) {
$handle = proc_open($command, [['pipe', 'r'], ['pipe', 'w'], ['pipe', 'w']], $pipe);
$startTime = microtime(true);
/* Read the command output and kill it if the proccess surpassed the timeout */
while(!feof($pipe[1])) {
$read .= fread($pipe[1], 8192);
if($startTime + $timeout < microtime(true)) break;
}
kill(proc_get_status($handle)['pid']);
proc_close($handle);
return $read;
}
/* The proc_terminate() function doesn't end proccess properly on Windows */
function kill($pid) {
return strstr(PHP_OS, 'WIN') ? exec("taskkill /F /T /PID $pid") : exec("kill -9 $pid");
}