11<?php
2-
32namespace CatPaw \Core ;
43
54use function Amp \async ;
6- use Amp \DeferredFuture ;
7- use function Amp \delay ;
8- use function Amp \File \isDirectory ;
5+ use function Amp \ByteStream \getStdin ;
96use CatPaw \Core \Implementations \Environment \SimpleEnvironment ;
107use CatPaw \Core \Interfaces \EnvironmentInterface ;
118use Error ;
12- use function preg_split ;
139use Psr \Log \LoggerInterface ;
14- use function realpath ;
1510use ReflectionFunction ;
1611use Revolt \EventLoop ;
1712use Throwable ;
1813
1914class Bootstrap {
2015 private function __construct () {
2116 }
22-
2317
2418 /**
2519 * Initialize an application from a source file (that usually defines a global "main" function).
@@ -49,7 +43,7 @@ public static function initialize(string $fileName):Result {
4943 * @param array<string> $libraries libraries to load
5044 * @param array<string> $resources resources to load
5145 * @param string $environment
52- * @param bool $dieOnChange die when a change to the main file, libraries or resources is detected
46+ * @param bool $dieOnStdin die when stdin receives data
5347 * @return void
5448 */
5549 public static function start (
@@ -58,7 +52,7 @@ public static function start(
5852 array $ libraries ,
5953 array $ resources ,
6054 string $ environment ,
61- bool $ dieOnChange = false ,
55+ bool $ dieOnStdin = false ,
6256 ):void {
6357 try {
6458 foreach ($ libraries as $ library ) {
@@ -90,7 +84,7 @@ public static function start(
9084 $ env ->set ('MAIN ' , $ main );
9185 $ env ->set ('LIBRARIES ' , $ libraries );
9286 $ env ->set ('RESOURCES ' , $ resources );
93- $ env ->set ('DIE_ON_CHANGE ' , $ dieOnChange );
87+ $ env ->set ('DIE_ON_CHANGE ' , $ dieOnStdin );
9488
9589 if ($ environment ) {
9690 $ env ->withFileName ($ environment );
@@ -116,18 +110,11 @@ public static function start(
116110 $ resource = $ resourceLocal ;
117111 }
118112
119- if ($ dieOnChange ) {
120- if (isPhar ()) {
121- self ::kill ("Watch mode is intended for development only, compiled phar applications cannot watch files for changes. " );
122- }
123- self ::onFileChange (
124- main: $ main ,
125- libraries: $ libraries ,
126- resources: $ resources ,
127- function: static function () {
128- self ::kill ("Killing application... " , 0 );
129- },
130- );
113+ if ($ dieOnStdin ) {
114+ async (function () {
115+ getStdin ()->read ();
116+ self ::kill ("Killing application... " , 0 );
117+ });
131118 }
132119
133120 self ::initialize ($ main )->unwrap ($ initializeError );
@@ -178,220 +165,4 @@ public static function kill(false|string|Error $error = false, false|int $code =
178165 die ($ code );
179166 }
180167 }
181-
182- /**
183- * @param string $initializer
184- * @param string $spawner
185- * @param string $fileName
186- * @param array<string> $arguments
187- * @param string $main
188- * @param array<string> $libraries
189- * @param array<string> $resources
190- * @return void
191- */
192- public static function spawn (
193- string $ initializer ,
194- string $ spawner ,
195- string $ fileName ,
196- array $ arguments ,
197- string $ main ,
198- array $ libraries ,
199- array $ resources ,
200- ):void {
201- try {
202- EventLoop::onSignal (SIGHUP , static fn () => self ::kill ("Killing application... " ));
203- EventLoop::onSignal (SIGINT , static fn () => self ::kill ("Killing application... " ));
204- EventLoop::onSignal (SIGQUIT , static fn () => self ::kill ("Killing application... " ));
205- EventLoop::onSignal (SIGTERM , static fn () => self ::kill ("Killing application... " ));
206-
207- foreach ($ libraries as $ library ) {
208- Container::requireLibraries ($ library )->unwrap ($ requireError );
209- if ($ requireError ) {
210- self ::kill ((string )$ requireError );
211- }
212- }
213-
214- Container::loadDefaultProviders ("Watcher " )->unwrap ($ loadDefaultProvidersError );
215- if ($ loadDefaultProvidersError ) {
216- self ::kill ((string )$ loadDefaultProvidersError );
217- }
218-
219- async (static function () use (
220- $ initializer ,
221- $ spawner ,
222- $ fileName ,
223- $ arguments ,
224- $ main ,
225- $ libraries ,
226- $ resources ,
227- ) {
228- if (!Container::isProvided (LoggerInterface::class)) {
229- $ logger = LoggerFactory::create ()->unwrap ($ error );
230- if ($ error ) {
231- return error ($ error );
232- }
233- Container::provide (LoggerInterface::class, $ logger );
234- } else {
235- $ logger = Container::get (LoggerInterface::class)->unwrap ($ error );
236- if ($ error ) {
237- return error ($ error );
238- }
239- }
240-
241- foreach ($ arguments as &$ argument ) {
242- $ parts = preg_split ('/=|\s/ ' , $ argument , 2 );
243- if (count ($ parts ) < 2 ) {
244- continue ;
245- }
246-
247- $ left = $ parts [0 ];
248- $ right = $ parts [1 ];
249- $ slashed = addslashes ($ right );
250- $ argument = "$ left= \"$ slashed \"" ;
251- }
252-
253- $ argumentsStringified = join (' ' , $ arguments );
254- $ instruction = "$ spawner $ fileName $ argumentsStringified " ;
255-
256- echo "Spawning $ instruction " .PHP_EOL ;
257-
258- if (DIRECTORY_SEPARATOR === '/ ' ) {
259- EventLoop::onSignal (SIGINT , static function () {
260- self ::kill ();
261- });
262- }
263-
264- /** @var false|DeferredFuture<void> $ready */
265- $ ready = false ;
266-
267- self ::onFileChange (
268- main: $ main ,
269- libraries: $ libraries ,
270- resources: $ resources ,
271- function: static function () use (&$ ready ) {
272- if (!$ ready ) {
273- return ;
274- }
275- $ ready ->complete ();
276- },
277- );
278-
279- while (true ) {
280- if ($ ready ) {
281- $ ready ->getFuture ()->await ();
282- }
283-
284- if ('' !== $ initializer ) {
285- Process::execute ($ initializer , out ())->unwrap ($ error );
286- if ($ error ) {
287- self ::kill ($ error );
288- }
289- }
290-
291- $ code = Process::execute ($ instruction , out ())->unwrap ($ error );
292- if ($ error || $ code > 0 ) {
293- echo $ error .PHP_EOL ;
294- $ ready = new DeferredFuture ;
295- }
296- }
297- });
298-
299- EventLoop::run ();
300- } catch (Throwable $ error ) {
301- self ::kill ($ error );
302- }
303- }
304-
305- /**
306- * Start a watcher which will detect file changes.
307- * Useful for development mode.
308- * @param string $main
309- * @param array<string> $libraries
310- * @param array<string> $resources
311- * @param callable $function
312- * @return void
313- */
314- private static function onFileChange (
315- string $ main ,
316- array $ libraries ,
317- array $ resources ,
318- callable $ function ,
319- ):void {
320- async (function () use (
321- $ main ,
322- $ libraries ,
323- $ resources ,
324- $ function ,
325- ) {
326- $ changes = [];
327- $ firstPass = true ;
328-
329- while (true ) {
330- clearstatcache ();
331- $ countLastPass = count ($ changes );
332-
333- $ fileNames = match ($ main ) {
334- '' => [],
335- default => [$ main => false ]
336- };
337- /** @var array<string> $files */
338- $ files = [...$ libraries , ...$ resources ];
339-
340- foreach ($ files as $ file ) {
341- if (!File::exists ($ file )) {
342- continue ;
343- }
344-
345- if (!isDirectory ($ file )) {
346- $ fileNames [$ file ] = false ;
347- continue ;
348- }
349-
350- $ directory = $ file ;
351-
352- $ flatList = Directory::flat (realpath ($ directory ))->unwrap ($ error );
353-
354- if ($ error ) {
355- return error ($ error );
356- }
357-
358- foreach ($ flatList as $ fileName ) {
359- $ fileNames [$ fileName ] = false ;
360- }
361- }
362-
363-
364- $ countThisPass = count ($ fileNames );
365- if (!$ firstPass && $ countLastPass !== $ countThisPass ) {
366- $ function ();
367- }
368-
369- foreach (array_keys ($ fileNames ) as $ fileName ) {
370- if (!File::exists ($ fileName )) {
371- unset($ changes [$ fileName ]);
372- continue ;
373- }
374-
375- $ mtime = filemtime ($ fileName );
376-
377- if (false === $ mtime ) {
378- return error ("Could not read file $ fileName modification time. " );
379- }
380-
381- if (!isset ($ changes [$ fileName ])) {
382- $ changes [$ fileName ] = $ mtime ;
383- continue ;
384- }
385-
386- if ($ changes [$ fileName ] !== $ mtime ) {
387- $ changes [$ fileName ] = $ mtime ;
388- $ function ();
389- }
390- }
391-
392- $ firstPass = false ;
393- delay (2 );
394- }
395- });
396- }
397168}
0 commit comments