3
3
namespace Illuminate \Foundation \Console ;
4
4
5
5
use Illuminate \Console \Command ;
6
+ use Illuminate \Support \Carbon ;
6
7
use Illuminate \Support \Env ;
7
8
use Symfony \Component \Console \Attribute \AsCommand ;
8
9
use Symfony \Component \Console \Input \InputOption ;
9
10
use Symfony \Component \Process \PhpExecutableFinder ;
10
11
use Symfony \Component \Process \Process ;
12
+ use function Termwind \terminal ;
11
13
12
14
#[AsCommand(name: 'serve ' )]
13
15
class ServeCommand extends Command
@@ -44,6 +46,13 @@ class ServeCommand extends Command
44
46
*/
45
47
protected $ portOffset = 0 ;
46
48
49
+ /**
50
+ * The list of requests being handled and their start time.
51
+ *
52
+ * @var array<int, \Illuminate\Support\Carbon>
53
+ */
54
+ protected $ requestsPool ;
55
+
47
56
/**
48
57
* The environment variables that should be passed from host machine to the PHP server process.
49
58
*
@@ -69,8 +78,6 @@ class ServeCommand extends Command
69
78
*/
70
79
public function handle ()
71
80
{
72
- $ this ->line ("<info>Starting Laravel development server:</info> http:// {$ this ->host ()}: {$ this ->port ()}" );
73
-
74
81
$ environmentFile = $ this ->option ('env ' )
75
82
? base_path ('.env ' ).'. ' .$ this ->option ('env ' )
76
83
: base_path ('.env ' );
@@ -93,7 +100,9 @@ public function handle()
93
100
filemtime ($ environmentFile ) > $ environmentLastModified ) {
94
101
$ environmentLastModified = filemtime ($ environmentFile );
95
102
96
- $ this ->comment ('Environment modified. Restarting server... ' );
103
+ $ this ->newLine ();
104
+
105
+ $ this ->components ->info ('Environment modified. Restarting server... ' );
97
106
98
107
$ process ->stop (5 );
99
108
@@ -130,9 +139,7 @@ protected function startProcess($hasEnvironment)
130
139
return in_array ($ key , static ::$ passthroughVariables ) ? [$ key => $ value ] : [$ key => false ];
131
140
})->all ());
132
141
133
- $ process ->start (function ($ type , $ buffer ) {
134
- $ this ->output ->write ($ buffer );
135
- });
142
+ $ process ->start ($ this ->handleProcessOutput ());
136
143
137
144
return $ process ;
138
145
}
@@ -212,6 +219,66 @@ protected function canTryAnotherPort()
212
219
($ this ->input ->getOption ('tries ' ) > $ this ->portOffset );
213
220
}
214
221
222
+ /**
223
+ * Returns a "callable" to handle the process output.
224
+ *
225
+ * @return callable(string, string): void
226
+ */
227
+ protected function handleProcessOutput ()
228
+ {
229
+ return fn ($ type , $ buffer ) => str ($ buffer )->explode (PHP_EOL )->each (function ($ line ) {
230
+ $ parts = explode ('] ' , $ line );
231
+
232
+ if (str ($ line )->contains ('Development Server (http ' )) {
233
+ $ this ->components ->info ("Server running on [http:// {$ this ->host ()}: {$ this ->port ()}]. " );
234
+ $ this ->comment (' <fg=yellow;options=bold>Press Ctrl+C to stop the server</> ' );
235
+
236
+ $ this ->newLine ();
237
+ } elseif (str ($ line )->contains (' Accepted ' )) {
238
+ $ startDate = Carbon::createFromFormat ('D M d H:i:s Y ' , ltrim ($ parts [0 ], '[ ' ));
239
+
240
+ preg_match ('/\:(\d+)/ ' , $ parts [1 ], $ matches );
241
+
242
+ $ this ->requestsPool [$ matches [1 ]] = [$ startDate , false ];
243
+ } elseif (str ($ line )->contains ([' [200]: GET ' ])) {
244
+ preg_match ('/\:(\d+)/ ' , $ parts [1 ], $ matches );
245
+
246
+ $ this ->requestsPool [$ matches [1 ]][1 ] = trim (explode ('[200]: GET ' , $ line )[1 ]);
247
+ } elseif (str ($ line )->contains (' Closing ' )) {
248
+ preg_match ('/\:(\d+)/ ' , $ parts [1 ], $ matches );
249
+
250
+ $ request = $ this ->requestsPool [$ matches [1 ]];
251
+
252
+ [$ startDate , $ file ] = $ request ;
253
+ $ formattedStartedAt = $ startDate ->format ('Y-m-d H:i:s ' );
254
+
255
+ unset($ this ->requestsPool [$ matches [1 ]]);
256
+
257
+ [$ date , $ time ] = explode (' ' , $ formattedStartedAt );
258
+
259
+ $ this ->output ->write (" <fg=gray> $ date</> $ time " );
260
+
261
+ $ runTime = Carbon::createFromFormat ('D M d H:i:s Y ' , ltrim ($ parts [0 ], '[ ' ))
262
+ ->diffInSeconds ($ startDate );
263
+
264
+ if ($ file ) {
265
+ $ this ->output ->write ($ file = " $ file " );
266
+ }
267
+
268
+ $ dots = max (terminal ()->width () - mb_strlen ($ formattedStartedAt ) - mb_strlen ($ file ) - mb_strlen ($ runTime ) - 9 , 0 );
269
+
270
+ $ this ->output ->write (' ' .str_repeat ('<fg=gray>.</> ' , $ dots ));
271
+ $ this ->output ->writeln (" <fg=gray>~ {$ runTime }s</> " );
272
+ } elseif (str ($ line )->contains (['Closed without sending a request ' , ']: ' ])) {
273
+ // ...
274
+ } elseif (isset ($ parts [1 ])) {
275
+ $ this ->components ->warn ($ parts [1 ]);
276
+ } elseif (! empty ($ line )) {
277
+ $ this ->components ->warn ($ line );
278
+ }
279
+ });
280
+ }
281
+
215
282
/**
216
283
* Get the console command options.
217
284
*
0 commit comments