* @copyright 2013-2015 Florian Eckerstorfer * @license http://opensource.org/licenses/MIT The MIT License * @link https://florian.ec/articles/running-background-processes-in-php/ Running background processes in PHP */ class BackgroundProcess { const OS_WINDOWS = 1; const OS_NIX = 2; const OS_OTHER = 3; /** * @var string */ private $command; /** * @var int */ private $pid; /** * @var int */ protected $serverOS; /** * @param string $command The command to execute * * @codeCoverageIgnore */ public function __construct($command = null) { $this->command = $command; $this->serverOS = $this->getOS(); } /** * Runs the command in a background process. * * @param string $outputFile File to write the output of the process to; defaults to /dev/null * currently $outputFile has no effect when used in conjunction with a Windows server * @param bool $append - set to true if output should be appended to $outputfile */ public function run($outputFile = '/dev/null', $append = false) { if($this->command === null) { return; } switch ($this->getOS()) { case self::OS_WINDOWS: if (class_exists("COM")) { $shell = new \COM("WScript.Shell"); $shell->CurrentDirectory = realpath(DOC_ROOT . "/exec"); $shell->run("cmd /C \"{$this->command}\"", 0, false); } else { shell_exec(sprintf('"cd ' . realpath(DOC_ROOT . "/exec") . ' && %s %s %s"', $this->command, ($append ? '>>' : '>'), $outputFile)); } break; case self::OS_NIX: $script = "cd " . realpath(DOC_ROOT . "/exec") . " && " . sprintf('%s %s %s 2>&1 &', $this->command, ($append ? ">>" : ">"), $outputFile); pclose(popen($script, "r")); break; default: throw new RuntimeException(sprintf( 'Could not execute command "%s" because operating system "%s" is not supported by '. 'Cocur\BackgroundProcess.', $this->command, PHP_OS )); } } /** * Returns if the process is currently running. * * @return bool TRUE if the process is running, FALSE if not. */ public function isRunning() { $this->checkSupportingOS('Cocur\BackgroundProcess can only check if a process is running on *nix-based '. 'systems, such as Unix, Linux or Mac OS X. You are running "%s".'); try { $result = shell_exec(sprintf('ps %d 2>&1', $this->pid)); if (count(preg_split("/\n/", $result)) > 2 && !preg_match('/ERROR: Process ID out of range/', $result)) { return true; } } catch (Exception $e) { } return false; } /** * Stops the process. * * @return bool `true` if the processes was stopped, `false` otherwise. */ public function stop() { $this->checkSupportingOS('Cocur\BackgroundProcess can only stop a process on *nix-based systems, such as '. 'Unix, Linux or Mac OS X. You are running "%s".'); try { $result = shell_exec(sprintf('kill %d 2>&1', $this->pid)); if (!preg_match('/No such process/', $result)) { return true; } } catch (Exception $e) { } return false; } /** * Returns the ID of the process. * * @return int The ID of the process */ public function getPid() { $this->checkSupportingOS('Cocur\BackgroundProcess can only return the PID of a process on *nix-based systems, '. 'such as Unix, Linux or Mac OS X. You are running "%s".'); return $this->pid; } /** * Set the process id. * * @param $pid */ protected function setPid($pid) { $this->pid = $pid; } /** * @return int */ protected function getOS() { $os = strtoupper(PHP_OS); if (substr($os, 0, 3) === 'WIN') { return self::OS_WINDOWS; } elseif ($os === 'LINUX' || $os === 'FREEBSD' || $os === 'DARWIN') { return self::OS_NIX; } return self::OS_OTHER; } /** * @param string $message Exception message if the OS is not supported * * @throws RuntimeException if the operating system is not supported by Cocur\BackgroundProcess * * @codeCoverageIgnore */ protected function checkSupportingOS($message) { if ($this->getOS() !== self::OS_NIX) { throw new RuntimeException(sprintf($message, PHP_OS)); } } /** * @param int $pid PID of process to resume * * @return Cocur\BackgroundProcess\BackgroundProcess */ static public function createFromPID($pid) { $process = new self(); $process->setPid($pid); return $process; } }