Files
Chamilo/vendor/symfony/framework-bundle/Command/ServerStartCommand.php
2025-08-14 22:41:49 +02:00

235 lines
7.5 KiB
PHP

<?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\Console\Style\SymfonyStyle;
use Symfony\Component\Process\PhpExecutableFinder;
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'),
new InputOption('port', 'p', InputOption::VALUE_REQUIRED, 'Address port number', '8000'),
new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', null),
new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'),
new InputOption('force', 'f', InputOption::VALUE_NONE, 'Force web server startup'),
))
->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>php %command.full_name%</info>
To change the default bind address and the default port use the <info>address</info> argument:
<info>php %command.full_name% 127.0.0.1:8080</info>
To change the default document root directory use the <info>--docroot</info> option:
<info>php %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>php %command.full_name% --router=app/config/router.php</info>
Specifying a router script is required when the used environment is not <comment>"dev"</comment> or
<comment>"prod"</comment>.
See also: http://www.php.net/manual/en/features.commandline.webserver.php
EOF
)
;
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$io = new SymfonyStyle($input, $cliOutput = $output);
if (!extension_loaded('pcntl')) {
$io->error(array(
'This command needs the pcntl extension to run.',
'You can either install it or use the "server:run" command instead to run the built-in web server.',
));
if ($io->ask('Do you want to execute <info>server:run</info> immediately? [Yn] ', true)) {
$command = $this->getApplication()->find('server:run');
return $command->run($input, $cliOutput);
}
return 1;
}
$documentRoot = $input->getOption('docroot');
if (null === $documentRoot) {
$documentRoot = $this->getContainer()->getParameter('kernel.root_dir').'/../web';
}
if (!is_dir($documentRoot)) {
$io->error(sprintf('The given document root directory "%s" does not exist.', $documentRoot));
return 1;
}
$env = $this->getContainer()->getParameter('kernel.environment');
if (false === $router = $this->determineRouterScript($input->getOption('router'), $env, $io)) {
return 1;
}
$address = $input->getArgument('address');
if (false === strpos($address, ':')) {
$address = $address.':'.$input->getOption('port');
}
if (!$input->getOption('force') && $this->isOtherServerProcessRunning($address)) {
$io->error(array(
sprintf('A process is already listening on http://%s.', $address),
'Use the --force option if the server process terminated unexpectedly to start a new web server process.',
));
return 1;
}
if ('prod' === $env) {
$io->error('Running PHP built-in server in production environment is NOT recommended!');
}
$pid = pcntl_fork();
if ($pid < 0) {
$io->error('Unable to start the server process.');
return 1;
}
if ($pid > 0) {
$io->success(sprintf('Web server listening on http://%s', $address));
return;
}
if (posix_setsid() < 0) {
$io->error('Unable to set the child process as session leader');
return 1;
}
if (null === $process = $this->createServerProcess($io, $address, $documentRoot, $router)) {
return 1;
}
$process->disableOutput();
$process->start();
$lockFile = $this->getLockFile($address);
touch($lockFile);
if (!$process->isRunning()) {
$io->error('Unable to start the server process');
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);
}
}
/**
* Determine the absolute file path for the router script, using the environment to choose a standard script
* if no custom router script is specified.
*
* @param string|null $router File path of the custom router script, if set by the user; otherwise null
* @param string $env The application environment
* @param SymfonyStyle $io An SymfonyStyle instance
*
* @return string|bool The absolute file path of the router script, or false on failure
*/
private function determineRouterScript($router, $env, SymfonyStyle $io)
{
if (null === $router) {
$router = $this
->getContainer()
->get('kernel')
->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env))
;
}
if (false === $path = realpath($router)) {
$io->error(sprintf('The given router script "%s" does not exist.', $router));
return false;
}
return $path;
}
/**
* Creates a process to start PHP's built-in web server.
*
* @param SymfonyStyle $io A SymfonyStyle instance
* @param string $address IP address and port to listen to
* @param string $documentRoot The application's document root
* @param string $router The router filename
*
* @return Process The process
*/
private function createServerProcess(SymfonyStyle $io, $address, $documentRoot, $router)
{
$finder = new PhpExecutableFinder();
if (false === $binary = $finder->find()) {
$io->error('Unable to find PHP binary to start server.');
return;
}
$script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), array(
$binary,
'-S',
$address,
$router,
)));
return new Process('exec '.$script, $documentRoot, null, null, null);
}
}