/** * Runs the application, returning the status to exit with. * * @return integer The exit status. * @access public */ public function run() { $exit_status = 0; // Curator doesn't do anything without at least one argument, besides the curator command itself. if ($this->argc === 0) { Console::stdout('Use \'' . $this->cmd . ' --help\' for usage information.'); } else { try { $parser = $this->buildCommandLineParser(); $result = $parser->parse(); // determine where our relevant directories are. if (!empty($result->options['proj_path'])) { $project_dir = realpath($result->options['proj_path']); } if (empty($project_dir)) { $project_dir = $_SERVER['PWD']; } $this->createDirectoryAtPath($project_dir); try { $project = new Project($project_dir); switch ($result->command_name) { case 'new': $project->install(); break; case 'clean': $project->clean(); break; case 'build': $project->build(); break; } } catch (\Exception $e) { Console::stderr('** Command \'' . $result->command_name . '\' has failed:'); Console::stderr(' ' . $e->getMessage()); } } catch (\Exception $e) { $parser->displayError($e->getMessage()); $exit_status = $e->getCode(); } } return $exit_status; }
/** * Execute a Closure as another process in the background while showing a * status update. The status update can be an indefinite spinner or a string * periodically sent from the background process, depending on whether the * provided Closure object has a $socket parameter or not. Messaging to the * main process is done by socket_* functions. The return value is either * the return value of the background process, or false if the process fork * failed. * * @param callable $callable Closure object * @return bool|int * @throws \Exception */ public static function work(\Closure $callable) { if (!extension_loaded('pcntl')) { throw new \Exception('pcntl extension required'); } if (!extension_loaded('sockets')) { throw new \Exception('sockets extension required'); } $spinner = array('|', '/', '-', '\\'); $i = 0; $l = count($spinner); $delay = 100000; $func = new \ReflectionFunction($callable); $socket = (bool) $func->getNumberOfParameters(); if ($socket) { $sockets = array(); if (socket_create_pair(AF_UNIX, SOCK_STREAM, 0, $sockets) === false) { return false; } } $pid = pcntl_fork(); if ($pid > 0) { $done = false; $retval = 0; pcntl_signal(SIGCHLD, function () use($pid, &$done, &$retval) { $child_pid = pcntl_waitpid($pid, $status); if (pcntl_wifexited($status)) { $retval = pcntl_wexitstatus($status); } $done = true; }); if ($socket) { $text = ''; while (!$done) { $r = array($sockets[1]); $w = null; $e = null; if ($status = socket_select($r, $w, $e, 0)) { $data = socket_read($sockets[1], 4096, PHP_NORMAL_READ); if ($data === false) { throw new \Exception(sprintf('socket write error %s', socket_strerror(socket_last_error($sockets[1])))); } echo str_repeat(chr(8), strlen($text)); $text = rtrim($data, "\n"); Console::stdout($text); } else { pcntl_signal_dispatch(); } usleep($delay); } echo str_repeat(chr(8), strlen($text)); socket_close($sockets[0]); socket_close($sockets[1]); } else { while (!$done) { pcntl_signal_dispatch(); echo $spinner[$i]; usleep($delay); echo chr(8); $i = $i === $l - 1 ? 0 : $i + 1; } } return $retval; } elseif ($pid === 0) { if ($socket) { call_user_func($callable, $sockets[0]); } else { call_user_func($callable); } exit; } else { // Unable to fork process. return false; } }