Skip to content

Commit 76bb726

Browse files
Update Serve.php
1 parent c7a8b9d commit 76bb726

File tree

1 file changed

+174
-125
lines changed
  • src/libraries/Console/Commands/Server

1 file changed

+174
-125
lines changed

src/libraries/Console/Commands/Server/Serve.php

Lines changed: 174 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -2,181 +2,230 @@
22

33
declare(strict_types=1);
44

5-
/**
6-
* Axm Framework PHP.
7-
*
8-
* @author Juan Cristobal <[email protected]>
9-
* @link http://www.axm.com/
10-
* @license http://www.axm.com/license/
11-
* @package Console
12-
*/
13-
145
namespace Console\Commands\Server;
156

167
use Console\BaseCommand;
178
use Console\CLI;
189
use RuntimeException;
1910

20-
/**
21-
* Class Serve
22-
*
23-
* Launch the Axm PHP Development Server
24-
* @package Console\Commands\Server
25-
*/
2611
class Serve extends BaseCommand
2712
{
28-
/**
29-
* Group
30-
*/
3113
protected string $group = 'Axm';
32-
33-
/**
34-
* Name
35-
*/
3614
protected string $name = 'serve';
37-
38-
/**
39-
* Description
40-
*/
4115
protected string $description = 'Launches the Axm PHP Development Server';
42-
43-
/**
44-
* Usage
45-
*/
4616
protected string $usage = 'serve [--host] [--port]';
47-
48-
/**
49-
* Options
50-
*/
5117
protected array $options = [
5218
'--php' => 'The PHP Binary [default: "PHP_BINARY"]',
5319
'--host' => 'The HTTP Host [default: "localhost"]',
5420
'--port' => 'The HTTP Host Port [default: "8080"]',
5521
];
5622

57-
/**
58-
* The current port offset.
59-
*/
6023
protected int $portOffset = 0;
61-
62-
/**
63-
* The max number of ports to attempt to serve from
64-
*/
6524
protected int $maxTries = 10;
66-
67-
/**
68-
* Default port number
69-
*/
7025
protected int $defaultPort = 8080;
71-
72-
/**
73-
*
74-
*/
7526
protected $process;
27+
protected float $startTime;
28+
protected bool $shouldShutdown = false;
29+
protected int $serverPid;
7630

77-
/**
78-
* Run the server
79-
*/
8031
public function run(array $params)
8132
{
82-
// Collect any user-supplied options and apply them.
8333
$php = CLI::getOption('php', PHP_BINARY);
8434
$host = CLI::getOption('host', 'localhost');
8535
$port = (int) CLI::getOption('port', $this->defaultPort);
8636

87-
// Attempt alternative ports
88-
// if (!$port = $this->findAvailablePort($host, $port)) {
89-
// CLI::error('Could not bind to any port');
90-
// exit;
91-
// }
92-
93-
CLI::loading(1);
37+
if (function_exists('pcntl_signal')) {
38+
pcntl_signal(SIGINT, [$this, 'signalHandler']);
39+
pcntl_signal(SIGTERM, [$this, 'signalHandler']);
40+
}
9441

95-
// Server up
9642
$this->startServer($php, $host, $port);
9743
}
9844

99-
/**
100-
* Find an available port
101-
*/
102-
protected function findAvailablePort(string $host, int $startPort): ?int
45+
protected function startServer(string $php, string $host, int $port, bool $forceKill = false)
46+
{
47+
$fcroot = ROOT_PATH;
48+
if (!is_dir($fcroot)) throw new RuntimeException("Invalid root directory: $fcroot");
49+
50+
if ($forceKill) $this->killExistingProcess($host, $port);
51+
52+
$command = sprintf('%s -S %s:%d -t %s', escapeshellarg($php), $host, $port, escapeshellarg($fcroot));
53+
54+
$this->printServerHeader();
55+
CLI::write(" Command: " . CLI::color($command, 'cyan'), 'dark_gray');
56+
57+
if (function_exists('pcntl_signal')) {
58+
pcntl_signal(SIGINT, [$this, 'signalHandler']);
59+
pcntl_signal(SIGTERM, [$this, 'signalHandler']);
60+
}
61+
62+
$this->process = proc_open($command, [STDIN, STDOUT, STDERR], $pipes);
63+
64+
if (!is_resource($this->process)) throw new RuntimeException("Failed to start the server process.");
65+
66+
$status = proc_get_status($this->process);
67+
$this->serverPid = $status['pid'];
68+
69+
$this->printServerInfo('http', $host, $port);
70+
71+
// Simplemente espera hasta que el proceso termine
72+
while (proc_get_status($this->process)['running']) {
73+
sleep(1);
74+
if (function_exists('pcntl_signal_dispatch'))
75+
pcntl_signal_dispatch();
76+
}
77+
78+
$this->shutdown(true, true);
79+
}
80+
81+
protected function printServerHeader()
82+
{
83+
CLI::newLine();
84+
$header = " AXM DEVELOPMENT SERVER ";
85+
$padding = str_repeat('=', strlen($header));
86+
CLI::write($padding, 'green');
87+
CLI::write($header, 'green');
88+
CLI::write($padding, 'green');
89+
CLI::newLine();
90+
}
91+
92+
protected function printServerInfo(string $scheme, string $host, int $port)
93+
{
94+
$url = "{$scheme}://{$host}:{$port}";
95+
CLI::write(" " . CLI::color('Server running at:', 'green'));
96+
CLI::write(" " . CLI::color($url, 'yellow'));
97+
CLI::newLine();
98+
CLI::write(" " . CLI::color('Document root:', 'green') . " " . CLI::color(ROOT_PATH, 'dark_gray'));
99+
CLI::write(" " . CLI::color('Environment:', 'green') . " " . CLI::color(getenv('AXM_ENV') ?: 'production', 'dark_gray'));
100+
CLI::newLine();
101+
CLI::write(" " . CLI::color('Press Ctrl+C to stop the server', 'cyan'));
102+
CLI::newLine();
103+
$this->printServerReadyMessage();
104+
}
105+
106+
protected function printServerReadyMessage()
103107
{
104-
$maxTries = $this->maxTries;
105-
for ($port = $startPort; $port < $startPort + $maxTries; $port++) {
106-
if ($this->checkPort($host, $port)) {
107-
return $port;
108-
}
108+
CLI::write(str_repeat('-', 50), 'dark_gray');
109+
CLI::write(" " . CLI::color('Server is ready to handle requests!', 'green'));
110+
CLI::write(str_repeat('-', 50), 'dark_gray');
111+
CLI::newLine();
112+
}
113+
114+
public function signalHandler($signo)
115+
{
116+
switch ($signo) {
117+
case SIGINT:
118+
case SIGTERM:
119+
$this->shutdown(true, true);
120+
exit;
109121
}
122+
}
110123

111-
return null;
124+
public function shutdown(bool $exit = false, bool $message = true)
125+
{
126+
if ($message) {
127+
CLI::newLine();
128+
CLI::write(" " . CLI::color('Shutting down the server...', 'yellow'));
129+
}
130+
131+
if (is_resource($this->process)) {
132+
proc_terminate($this->process, SIGINT);
133+
proc_close($this->process);
134+
}
135+
136+
if ($message) {
137+
CLI::write(" " . CLI::color('Server stopped successfully.', 'green'));
138+
CLI::newLine();
139+
}
140+
141+
if ($exit) exit(0);
112142
}
113143

114-
/**
115-
* Check if a port is available by attempting to connect to it.
116-
*/
117-
protected function checkPort(string $host, int $port): bool
144+
protected function killExistingProcess(string $host, int $port)
118145
{
119-
try {
120-
$url = "http://$host:$port";
121-
$headers = @get_headers($url);
122-
return !empty($headers);
123-
} catch (\Throwable $th) {
124-
return false;
146+
if (PHP_OS_FAMILY === 'Windows')
147+
exec("FOR /F \"usebackq tokens=5\" %a in (`netstat -ano ^| findstr :$port`) do taskkill /F /PID %a");
148+
else
149+
exec("lsof -ti tcp:$port | xargs kill -9");
150+
151+
sleep(1); // Dar tiempo para que el proceso se cierre completamente
152+
}
153+
154+
protected function formatAndPrintOutput($output)
155+
{
156+
$lines = explode("\n", trim($output));
157+
158+
foreach ($lines as $line) {
159+
if (preg_match('/^\[(.*?)\] (\[.*?\] )?(.*?)$/', $line, $matches)) {
160+
$timestamp = $matches[1];
161+
$clientInfo = $matches[2] ?? '';
162+
$content = $matches[3];
163+
164+
$formattedLine = $this->formatTimestampAndClientInfo($timestamp, $clientInfo);
165+
$formattedLine .= $this->formatHttpRequest($content);
166+
167+
CLI::write($formattedLine, 'light_gray');
168+
} else
169+
CLI::write(CLI::color($line, 'light_gray'));
125170
}
126171
}
127172

128-
/**
129-
* Start the server
130-
*/
131-
protected function startServer(string $php, string $host, int $port)
173+
protected function formatTimestampAndClientInfo($timestamp, $clientInfo)
174+
{
175+
$formattedTimestamp = CLI::color("[$timestamp]", 'light_gray');
176+
$formattedClientInfo = CLI::color(" $clientInfo", 'light_gray');
177+
178+
return $formattedTimestamp . $formattedClientInfo;
179+
}
180+
181+
protected function formatHttpRequest($content)
132182
{
133-
// Path Root.
134-
$fcroot = getcwd();
135-
if (is_dir($fcroot)) {
136-
$descriptors = [
137-
0 => ['pipe', 'r'], // stdin
138-
1 => STDOUT, // stdout
139-
2 => STDERR // stderr
140-
];
141-
142-
$command = "{$php} -S {$host}:{$port} -t {$fcroot}";
143-
$this->process = proc_open($command, $descriptors, $pipes);
144-
145-
if (is_resource($this->process)) {
146-
while ($output = fgets($pipes[0])) {
147-
if (strpos($output, 'SIGINT') !== false) {
148-
$this->shutdown();
149-
}
150-
}
151-
152-
$this->printServerInfo('http', $host, $port);
153-
}
154-
155-
$code = proc_close($this->process);
156-
if ($code !== 0) {
157-
throw new RuntimeException("Unknown error (code: $code)", $code);
158-
}
183+
if (preg_match('/(\[.*?\]) (\[(\d+)\]): ([A-Z]+) (.*)/', $content, $requestMatches)) {
184+
$statusCode = $requestMatches[3];
185+
$method = $requestMatches[4];
186+
$path = $requestMatches[5];
187+
188+
$coloredMethod = $this->colorizeMethod($method);
189+
$coloredPath = CLI::color($path, 'light_gray');
190+
$coloredStatus = $this->colorizeStatusCode($statusCode);
191+
192+
return "{$coloredStatus}: {$coloredMethod} {$coloredPath}";
159193
}
194+
195+
return CLI::color($content, 'light_gray');
160196
}
161197

162-
/**
163-
* Shutdown the server
164-
*/
165-
protected function shutdown()
198+
protected function formatAndPrintError($error)
166199
{
167-
CLI::info('Shutting down the server...');
168-
proc_terminate($this->process);
200+
$lines = explode("\n", trim($error));
201+
foreach ($lines as $line)
202+
CLI::write(CLI::color('ERROR: ', 'red') . CLI::color($line, 'light_red'));
169203
}
170204

171-
/**
172-
* Print server information
173-
*/
174-
protected function printServerInfo(string $scheme, string $host, int $port)
205+
protected function colorizeStatusCode($statusCode): string
175206
{
176-
CLI::info(self::ARROW_SYMBOL . 'Axm development server started on: ' . CLI::color("{$scheme}://{$host}:{$port}", 'green'));
207+
$color = match (true) {
208+
$statusCode >= 200 && $statusCode < 300 => 'green',
209+
$statusCode >= 300 && $statusCode < 400 => 'yellow',
210+
$statusCode >= 400 && $statusCode < 500 => 'light_red',
211+
default => 'red',
212+
};
213+
214+
return CLI::color("[$statusCode]", $color);
215+
}
177216

178-
CLI::newLine();
179-
CLI::write(self::ARROW_SYMBOL . 'Press Control-C to stop.', 'yellow');
180-
CLI::newLine(2);
217+
protected function colorizeMethod($method)
218+
{
219+
$colors = [
220+
'GET' => 'green',
221+
'POST' => 'yellow',
222+
'PUT' => 'blue',
223+
'DELETE' => 'red',
224+
'PATCH' => 'purple',
225+
'HEAD' => 'cyan',
226+
'OPTIONS' => 'white'
227+
];
228+
229+
return CLI::color($method, $colors[$method] ?? 'white');
181230
}
182231
}

0 commit comments

Comments
 (0)