new helper commands for PHP's built-in server
This commit is contained in:
parent
c51f3f36ab
commit
b601454448
48
src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php
Normal file
48
src/Symfony/Bundle/FrameworkBundle/Command/ServerCommand.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
/**
|
||||
* Base methods for commands related to PHP's built-in web server.
|
||||
*
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
abstract class ServerCommand extends ContainerAwareCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function isEnabled()
|
||||
{
|
||||
if (version_compare(phpversion(), '5.4.0', '<') || defined('HHVM_VERSION')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!extension_loaded('pcntl')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the name of the lock file for a particular PHP web server process.
|
||||
*
|
||||
* @param string $address An address/port tuple
|
||||
*
|
||||
* @return string The filename
|
||||
*/
|
||||
protected function getLockFile($address)
|
||||
{
|
||||
return sys_get_temp_dir().'/'.strtr($address, '.:', '--').'.pid';
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
/**
|
||||
* Runs PHP's built-in web server in a background process.
|
||||
*
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
class ServerStartCommand extends ServerCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDefinition(array(
|
||||
new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'),
|
||||
new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', 'web/'),
|
||||
new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'),
|
||||
))
|
||||
->setName('server:start')
|
||||
->setDescription('Starts PHP built-in web server in the background')
|
||||
->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> runs PHP's built-in web server:
|
||||
|
||||
<info>%command.full_name%</info>
|
||||
|
||||
To change the default bind address and the default port use the <info>address</info> argument:
|
||||
|
||||
<info>%command.full_name% 127.0.0.1:8080</info>
|
||||
|
||||
To change the default document root directory use the <info>--docroot</info> option:
|
||||
|
||||
<info>%command.full_name% --docroot=htdocs/</info>
|
||||
|
||||
If you have a custom document root directory layout, you can specify your own
|
||||
router script using the <info>--router</info> option:
|
||||
|
||||
<info>%command.full_name% --router=app/config/router.php</info>
|
||||
|
||||
Specifying a router script is required when the used environment is not "dev" or
|
||||
"prod".
|
||||
|
||||
See also: http://www.php.net/manual/en/features.commandline.webserver.php
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$env = $this->getContainer()->getParameter('kernel.environment');
|
||||
|
||||
if ('prod' === $env) {
|
||||
$output->writeln('<error>Running PHP built-in server in production environment is NOT recommended!</error>');
|
||||
}
|
||||
|
||||
$pid = pcntl_fork();
|
||||
|
||||
if ($pid < 0) {
|
||||
$output->writeln('<error>Unable to start the server process</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($pid > 0) {
|
||||
$output->writeln('<info>Server started successfully</info>');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (posix_setsid() < 0) {
|
||||
$output->writeln('<error>Unable to set the child process as session leader</error>');
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
$process = $this->createServerProcess(
|
||||
$input->getArgument('address'),
|
||||
$input->getOption('docroot'),
|
||||
$input->getOption('router'),
|
||||
$env,
|
||||
null
|
||||
);
|
||||
$process->disableOutput();
|
||||
$process->start();
|
||||
$lockFile = $this->getLockFile($input->getArgument('address'));
|
||||
touch($lockFile);
|
||||
|
||||
if (!$process->isRunning()) {
|
||||
$output->writeln('<error>Unable to start the server process</error>');
|
||||
unlink($lockFile);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// stop the web server when the lock file is removed
|
||||
while ($process->isRunning()) {
|
||||
if (!file_exists($lockFile)) {
|
||||
$process->stop();
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a process to start PHP's built-in web server.
|
||||
*
|
||||
* @param string $address IP address and port to listen to
|
||||
* @param string $documentRoot The application's document root
|
||||
* @param string $router The router filename
|
||||
* @param string $env The application environment
|
||||
* @param int $timeout Process timeout
|
||||
*
|
||||
* @return Process The process
|
||||
*/
|
||||
private function createServerProcess($address, $documentRoot, $router, $env, $timeout = null)
|
||||
{
|
||||
$router = $router ?: $this
|
||||
->getContainer()
|
||||
->get('kernel')
|
||||
->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env))
|
||||
;
|
||||
$script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), array(
|
||||
PHP_BINARY,
|
||||
'-S',
|
||||
$address,
|
||||
$router,
|
||||
)));
|
||||
|
||||
return new Process('exec '.$script, $documentRoot, null, null, $timeout);
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Shows the status of a process that is running PHP's built-in web server in
|
||||
* the background.
|
||||
*
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
class ServerStatusCommand extends ServerCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDefinition(array(
|
||||
new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'),
|
||||
))
|
||||
->setName('server:status')
|
||||
->setDescription('Outputs the status of the built-in web server for the given address')
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$address = $input->getArgument('address');
|
||||
|
||||
// remove an orphaned lock file
|
||||
if (file_exists($this->getLockFile($address)) && !$this->isServerRunning($address)) {
|
||||
unlink($this->getLockFile($address));
|
||||
}
|
||||
|
||||
if (file_exists($this->getLockFile($address))) {
|
||||
$output->writeln(sprintf('<info>Web server still listening on %s</info>', $address));
|
||||
} else {
|
||||
$output->writeln(sprintf('<error>No web server is listening on %s</error>', $address));
|
||||
}
|
||||
}
|
||||
|
||||
private function isServerRunning($address)
|
||||
{
|
||||
list($hostname, $port) = explode(':', $address);
|
||||
|
||||
if (false !== $fp = @fsockopen($hostname, $port, $errno, $errstr, 1)) {
|
||||
fclose($fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bundle\FrameworkBundle\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
/**
|
||||
* Stops a background process running PHP's built-in web server.
|
||||
*
|
||||
* @author Christian Flothmann <christian.flothmann@xabbuh.de>
|
||||
*/
|
||||
class ServerStopCommand extends ServerCommand
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setDefinition(array(
|
||||
new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'),
|
||||
))
|
||||
->setName('server:stop')
|
||||
->setDescription('Stops PHP\'s built-in web server that was started with the server:start command')
|
||||
->setHelp(<<<EOF
|
||||
The <info>%command.name%</info> stops PHP's built-in web server:
|
||||
|
||||
<info>%command.full_name%</info>
|
||||
|
||||
To change the default bind address and the default port use the <info>address</info> argument:
|
||||
|
||||
<info>%command.full_name% 127.0.0.1:8080</info>
|
||||
|
||||
EOF
|
||||
)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$address = $input->getArgument('address');
|
||||
$lockFile = $this->getLockFile($address);
|
||||
|
||||
if (!file_exists($lockFile)) {
|
||||
$output->writeln(sprintf('<error>No web server is listening on %s</error>', $address));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
unlink($lockFile);
|
||||
$output->writeln(sprintf('<info>Stopped the web server listening on %s</info>', $address));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user