protected function execute(InputInterface $input, OutputInterface $output) { $arguments = $this->getCommandArguments($input); $builder = $this->getEnvironmentHelper()->getProcessBuilder($arguments); $command = $builder->getProcess()->getCommandLine(); $loop = Factory::create(); $process = new Process($command, $this->getEnvironmentHelper()->getCwd()); $port = (int) $input->getOption('port'); $server = $this->initializeServer($loop, $port); $started = false; $this->addEnvironmentInfo($port, $command); $loop->addPeriodicTimer(self::LOOP_TIMER_PERIOD, function (Timer $timer) use($output, $process, $server, &$started) { $clients = $server->getConnections(); if (true === $started && false === $process->isRunning()) { exit($process->getExitCode()); } if ($clients->count()) { if (!$process->isRunning()) { $process->start($timer->getLoop()); $started = true; $this->broadcastToClients($clients); } $callable = function ($output) use($clients) { $this->buffer .= $output; $this->broadcastToClients($clients); }; $process->stdin->on('data', $callable); $process->stdout->on('data', $callable); $process->stderr->on('data', $callable); } }); $server->bind(); $loop->run(); }
public function close() { if ($this->out !== null) { $this->out->removeAllListeners(); } if ($this->in !== null) { $this->in->removeAllListeners(); } if ($this->err !== null) { $this->err->removeAllListeners(); } $this->process->removeAllListeners(); if ($this->process->isRunning()) { $this->process->terminate(); } }
/** * launch the given interactive $command shell * * Its STDOUT will be used to parse responses, the STDIN will be used * to pass commands. * * If the command prints output to STDERR, make sure to redirect it to * STDOUT by appending " 2>&1". * * @param string|Process $process accepts either a command string to execute or a Process instance * @return DeferredShell */ public function createDeferredShell($process) { if (!$process instanceof Process) { $process = new Process($process); } $process->start($this->loop); $stream = new CompositeStream($process->stdout, $process->stdin); // forcefully terminate process when stream closes $stream->on('close', function () use($process) { if ($process->isRunning()) { $process->terminate(SIGKILL); } }); return new DeferredShell($stream); }
public function testTerminateWithStopAndContinueSignalsUsingEventLoop() { if (defined('PHP_WINDOWS_VERSION_BUILD')) { $this->markTestSkipped('Windows does not report signals via proc_get_status()'); } if (!defined('SIGSTOP') && !defined('SIGCONT')) { $this->markTestSkipped('SIGSTOP and/or SIGCONT is not defined'); } $loop = $this->createloop(); $process = new Process('sleep 1; exit 0'); $called = false; $exitCode = 'initial'; $termSignal = 'initial'; $process->on('exit', function () use(&$called, &$exitCode, &$termSignal) { $called = true; $exitCode = func_get_arg(0); $termSignal = func_get_arg(1); }); $loop->addTimer(0.001, function (Timer $timer) use($process) { $process->start($timer->getLoop()); $process->terminate(SIGSTOP); $this->assertSoon(function () use($process) { $this->assertTrue($process->isStopped()); $this->assertTrue($process->isRunning()); $this->assertEquals(SIGSTOP, $process->getStopSignal()); }); $process->terminate(SIGCONT); $this->assertSoon(function () use($process) { $this->assertFalse($process->isStopped()); $this->assertEquals(SIGSTOP, $process->getStopSignal()); }); }); $loop->run(); $this->assertTrue($called); $this->assertSame(0, $exitCode); $this->assertNull($termSignal); $this->assertFalse($process->isRunning()); $this->assertSame(0, $process->getExitCode()); $this->assertNull($process->getTermSignal()); $this->assertFalse($process->isTerminated()); }