Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5af7a1a

Browse files
committed
new helper commands for PHP's built-in server
1 parent c51f3f3 commit 5af7a1a

File tree

4 files changed

+335
-0
lines changed

4 files changed

+335
-0
lines changed
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
/**
15+
* Base methods for commands related to PHP's built-in web server.
16+
*
17+
* @author Christian Flothmann <[email protected]>
18+
*/
19+
abstract class ServerCommand extends ContainerAwareCommand
20+
{
21+
/**
22+
* {@inheritdoc}
23+
*/
24+
public function isEnabled()
25+
{
26+
if (version_compare(phpversion(), '5.4.0', '<') || defined('HHVM_VERSION')) {
27+
return false;
28+
}
29+
30+
if (!extension_loaded('pcntl')) {
31+
return false;
32+
}
33+
34+
return parent::isEnabled();
35+
}
36+
37+
/**
38+
* Determines the name of the lock file for a particular PHP web server process.
39+
*
40+
* @param string $address An address/port tuple
41+
*
42+
* @return string The filename
43+
*/
44+
protected function getLockFile($address)
45+
{
46+
return sys_get_temp_dir().'/'.strtr($address, '.:', '--').'.pid';
47+
}
48+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Input\InputOption;
17+
use Symfony\Component\Console\Output\OutputInterface;
18+
use Symfony\Component\Process\Process;
19+
20+
/**
21+
* Runs PHP's built-in web server in a background process.
22+
*
23+
* @author Christian Flothmann <[email protected]>
24+
*/
25+
class ServerStartCommand extends ServerCommand
26+
{
27+
/**
28+
* {@inheritdoc}
29+
*/
30+
protected function configure()
31+
{
32+
$this
33+
->setDefinition(array(
34+
new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'),
35+
new InputOption('docroot', 'd', InputOption::VALUE_REQUIRED, 'Document root', 'web/'),
36+
new InputOption('router', 'r', InputOption::VALUE_REQUIRED, 'Path to custom router script'),
37+
))
38+
->setName('server:start')
39+
->setDescription('Starts PHP built-in web server in the background')
40+
->setHelp(<<<EOF
41+
The <info>%command.name%</info> runs PHP's built-in web server:
42+
43+
<info>%command.full_name%</info>
44+
45+
To change the default bind address and the default port use the <info>address</info> argument:
46+
47+
<info>%command.full_name% 127.0.0.1:8080</info>
48+
49+
To change the default document root directory use the <info>--docroot</info> option:
50+
51+
<info>%command.full_name% --docroot=htdocs/</info>
52+
53+
If you have a custom document root directory layout, you can specify your own
54+
router script using the <info>--router</info> option:
55+
56+
<info>%command.full_name% --router=app/config/router.php</info>
57+
58+
Specifying a router script is required when the used environment is not "dev" or
59+
"prod".
60+
61+
See also: http://www.php.net/manual/en/features.commandline.webserver.php
62+
63+
EOF
64+
)
65+
;
66+
}
67+
68+
/**
69+
* {@inheritdoc}
70+
*/
71+
protected function execute(InputInterface $input, OutputInterface $output)
72+
{
73+
$env = $this->getContainer()->getParameter('kernel.environment');
74+
75+
if ('prod' === $env) {
76+
$output->writeln('<error>Running PHP built-in server in production environment is NOT recommended!</error>');
77+
}
78+
79+
$pid = pcntl_fork();
80+
81+
if ($pid < 0) {
82+
$output->writeln('<error>Unable to start the server process</error>');
83+
84+
return 1;
85+
} elseif ($pid > 0) {
86+
$output->writeln('<info>Server started successfully</info>');
87+
} else {
88+
if (posix_setsid() < 0) {
89+
return 1;
90+
}
91+
92+
$process = $this->createServerProcess(
93+
$input->getArgument('address'),
94+
$input->getOption('docroot'),
95+
$input->getOption('router'),
96+
$env,
97+
null
98+
);
99+
$process->disableOutput();
100+
$process->start();
101+
$lockFile = $this->getLockFile($input->getArgument('address'));
102+
touch($lockFile);
103+
104+
if (!$process->isRunning()) {
105+
$output->writeln('<error>Unable to start the server process</error>');
106+
unlink($lockFile);
107+
108+
return 1;
109+
}
110+
111+
// stop the web server when the lock file is removed
112+
while ($process->isRunning()) {
113+
if (!file_exists($lockFile)) {
114+
$process->stop();
115+
}
116+
117+
sleep(1);
118+
}
119+
}
120+
}
121+
122+
/**
123+
* Creates a process to start PHP's built-in web server.
124+
*
125+
* @param string $address IP address and port to listen to
126+
* @param string $documentRoot The application's document root
127+
* @param string $router The router filename
128+
* @param string $env The application environment
129+
* @param int $timeout Process timeout
130+
*
131+
* @return Process The process
132+
*/
133+
private function createServerProcess($address, $documentRoot, $router, $env, $timeout = null)
134+
{
135+
$router = $router ?: $this
136+
->getContainer()
137+
->get('kernel')
138+
->locateResource(sprintf('@FrameworkBundle/Resources/config/router_%s.php', $env))
139+
;
140+
$script = implode(' ', array_map(array('Symfony\Component\Process\ProcessUtils', 'escapeArgument'), array(
141+
PHP_BINARY,
142+
'-S',
143+
$address,
144+
$router,
145+
)));
146+
147+
return new Process('exec '.$script, $documentRoot, null, null, $timeout);
148+
}
149+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
18+
/**
19+
* Shows the status of a process that is running PHP's built-in web server in
20+
* the background.
21+
*
22+
* @author Christian Flothmann <[email protected]>
23+
*/
24+
class ServerStatusCommand extends ServerCommand
25+
{
26+
/**
27+
* {@inheritdoc}
28+
*/
29+
protected function configure()
30+
{
31+
$this
32+
->setDefinition(array(
33+
new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'),
34+
))
35+
->setName('server:status')
36+
->setDescription('Outputs the status of the built-in web server for the given address')
37+
;
38+
}
39+
40+
/**
41+
* {@inheritdoc}
42+
*/
43+
protected function execute(InputInterface $input, OutputInterface $output)
44+
{
45+
$address = $input->getArgument('address');
46+
47+
// remove an orphaned lock file
48+
if (file_exists($this->getLockFile($address)) && !$this->isServerRunning($address)) {
49+
unlink($this->getLockFile($address));
50+
}
51+
52+
if (file_exists($this->getLockFile($address))) {
53+
$output->writeln(sprintf('<info>Web server still listening on %s</info>', $address));
54+
} else {
55+
$output->writeln(sprintf('<error>No web server is listening on %s</error>', $address));
56+
}
57+
}
58+
59+
private function isServerRunning($address)
60+
{
61+
list($hostname, $port) = explode(':', $address);
62+
63+
if (false !== $fp = @fsockopen($hostname, $port, $errno, $errstr, 1)) {
64+
fclose($fp);
65+
66+
return true;
67+
}
68+
69+
return false;
70+
}
71+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
18+
/**
19+
* Stops a background process running PHP's built-in web server.
20+
*
21+
* @author Christian Flothmann <[email protected]>
22+
*/
23+
class ServerStopCommand extends ServerCommand
24+
{
25+
/**
26+
* {@inheritdoc}
27+
*/
28+
protected function configure()
29+
{
30+
$this
31+
->setDefinition(array(
32+
new InputArgument('address', InputArgument::OPTIONAL, 'Address:port', '127.0.0.1:8000'),
33+
))
34+
->setName('server:stop')
35+
->setDescription('Stops PHP\'s built-in web server that was started with the server:start command')
36+
->setHelp(<<<EOF
37+
The <info>%command.name%</info> stops PHP's built-in web server:
38+
39+
<info>%command.full_name%</info>
40+
41+
To change the default bind address and the default port use the <info>address</info> argument:
42+
43+
<info>%command.full_name% 127.0.0.1:8080</info>
44+
45+
EOF
46+
)
47+
;
48+
}
49+
50+
/**
51+
* {@inheritdoc}
52+
*/
53+
protected function execute(InputInterface $input, OutputInterface $output)
54+
{
55+
$address = $input->getArgument('address');
56+
$lockFile = $this->getLockFile($address);
57+
58+
if (!file_exists($lockFile)) {
59+
$output->writeln(sprintf('<error>No web server is listening on %s</error>', $address));
60+
61+
return 1;
62+
}
63+
64+
unlink($lockFile);
65+
$output->writeln(sprintf('<info>Stopped the web server listening on %s</info>', $address));
66+
}
67+
}

0 commit comments

Comments
 (0)