1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Hyde\Console\Commands; |
6
|
|
|
|
7
|
|
|
use Closure; |
8
|
|
|
use Hyde\Hyde; |
9
|
|
|
use Hyde\Facades\Config; |
10
|
|
|
use Illuminate\Support\Arr; |
11
|
|
|
use InvalidArgumentException; |
12
|
|
|
use Hyde\Console\Concerns\Command; |
13
|
|
|
use Hyde\RealtimeCompiler\ConsoleOutput; |
14
|
|
|
use Illuminate\Support\Facades\Process; |
15
|
|
|
|
16
|
|
|
use function rtrim; |
17
|
|
|
use function sprintf; |
18
|
|
|
use function in_array; |
19
|
|
|
use function str_replace; |
20
|
|
|
use function class_exists; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Start the realtime compiler server. |
24
|
|
|
* |
25
|
|
|
* @see https://github.com/hydephp/realtime-compiler |
26
|
|
|
*/ |
27
|
|
|
class ServeCommand extends Command |
28
|
|
|
{ |
29
|
|
|
/** @var string */ |
30
|
|
|
protected $signature = 'serve |
31
|
|
|
{--host= : <comment>[default: "localhost"]</comment>}} |
32
|
|
|
{--port= : <comment>[default: 8080]</comment>} |
33
|
|
|
{--save-preview= : Should the served page be saved to disk? (Overrides config setting)} |
34
|
|
|
{--dashboard= : Enable the realtime compiler dashboard. (Overrides config setting)} |
35
|
|
|
{--pretty-urls= : Enable pretty URLs. (Overrides config setting)} |
36
|
|
|
{--play-cdn= : Enable the Tailwind Play CDN. (Overrides config setting)} |
37
|
|
|
{--open=false : Open the site preview in the browser.} |
38
|
|
|
'; |
39
|
|
|
|
40
|
|
|
/** @var string */ |
41
|
|
|
protected $description = 'Start the realtime compiler server'; |
42
|
|
|
|
43
|
|
|
protected ConsoleOutput $console; |
44
|
|
|
|
45
|
|
|
public function safeHandle(): int |
46
|
|
|
{ |
47
|
|
|
$this->configureOutput(); |
48
|
|
|
$this->printStartMessage(); |
49
|
|
|
|
50
|
|
|
if ($this->option('open') !== 'false') { |
51
|
|
|
$this->openInBrowser((string) $this->option('open')); |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
$this->runServerProcess(sprintf('php -S %s:%d %s', |
55
|
|
|
$this->getHostSelection(), |
56
|
|
|
$this->getPortSelection(), |
57
|
|
|
$this->getExecutablePath() |
58
|
|
|
)); |
59
|
|
|
|
60
|
|
|
return Command::SUCCESS; |
61
|
|
|
} |
62
|
|
|
|
63
|
|
|
protected function getHostSelection(): string |
64
|
|
|
{ |
65
|
|
|
return (string) $this->option('host') ?: Config::getString('hyde.server.host', 'localhost'); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
protected function getPortSelection(): int |
69
|
|
|
{ |
70
|
|
|
return (int) ($this->option('port') ?: Config::getInt('hyde.server.port', 8080)); |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
protected function getExecutablePath(): string |
74
|
|
|
{ |
75
|
|
|
return Hyde::path('vendor/hyde/realtime-compiler/bin/server.php'); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
protected function runServerProcess(string $command): void |
79
|
|
|
{ |
80
|
|
|
Process::forever()->env($this->getEnvironmentVariables())->run($command, $this->getOutputHandler()); |
81
|
|
|
} |
82
|
|
|
|
83
|
|
|
protected function getEnvironmentVariables(): array |
84
|
|
|
{ |
85
|
|
|
return Arr::whereNotNull([ |
86
|
|
|
'HYDE_SERVER_REQUEST_OUTPUT' => ! $this->option('no-ansi'), |
87
|
|
|
'HYDE_SERVER_SAVE_PREVIEW' => $this->parseEnvironmentOption('save-preview'), |
|
|
|
|
88
|
|
|
'HYDE_SERVER_DASHBOARD' => $this->parseEnvironmentOption('dashboard'), |
|
|
|
|
89
|
|
|
'HYDE_PRETTY_URLS' => $this->parseEnvironmentOption('pretty-urls'), |
|
|
|
|
90
|
|
|
'HYDE_PLAY_CDN' => $this->parseEnvironmentOption('play-cdn'), |
|
|
|
|
91
|
|
|
]); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
protected function configureOutput(): void |
95
|
|
|
{ |
96
|
|
|
if (! $this->useBasicOutput()) { |
97
|
|
|
$this->console = new ConsoleOutput($this->output->isVerbose()); |
98
|
|
|
} |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
protected function printStartMessage(): void |
102
|
|
|
{ |
103
|
|
|
$this->useBasicOutput() |
104
|
|
|
? $this->output->writeln('<info>Starting the HydeRC server...</info> Press Ctrl+C to stop') |
105
|
|
|
: $this->console->printStartMessage($this->getHostSelection(), $this->getPortSelection(), $this->getEnvironmentVariables()); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
protected function getOutputHandler(): Closure |
109
|
|
|
{ |
110
|
|
|
return $this->useBasicOutput() ? function (string $type, string $line): void { |
111
|
|
|
$this->output->write($line); |
112
|
|
|
} : $this->console->getFormatter(); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
protected function useBasicOutput(): bool |
116
|
|
|
{ |
117
|
|
|
return $this->option('no-ansi') || ! class_exists(ConsoleOutput::class); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
protected function parseEnvironmentOption(string $name): ?string |
121
|
|
|
{ |
122
|
|
|
$value = $this->option($name) ?? $this->checkArgvForOption($name); |
123
|
|
|
|
124
|
|
|
if ($value !== null) { |
125
|
|
|
return match ($value) { |
126
|
|
|
'true', '' => 'enabled', |
127
|
|
|
'false' => 'disabled', |
128
|
|
|
default => throw new InvalidArgumentException(sprintf('Invalid boolean value for --%s option.', $name)) |
129
|
|
|
}; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
return null; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** Fallback check so that an environment option without a value is acknowledged as true. */ |
136
|
|
|
protected function checkArgvForOption(string $name): ?string |
137
|
|
|
{ |
138
|
|
|
if (isset($_SERVER['argv'])) { |
139
|
|
|
if (in_array("--$name", $_SERVER['argv'], true)) { |
140
|
|
|
return 'true'; |
141
|
|
|
} |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return null; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
protected function openInBrowser(string $path = '/'): void |
148
|
|
|
{ |
149
|
|
|
$binary = $this->getOpenCommand(PHP_OS_FAMILY); |
150
|
|
|
|
151
|
|
|
$command = sprintf('%s http://%s:%d', $binary, $this->getHostSelection(), $this->getPortSelection()); |
152
|
|
|
$command = rtrim("$command/$path", '/'); |
153
|
|
|
|
154
|
|
|
$process = $binary ? Process::command($command)->run() : null; |
155
|
|
|
|
156
|
|
|
if (! $process || $process->failed()) { |
157
|
|
|
$this->warn('Unable to open the site preview in the browser on your system:'); |
158
|
|
|
$this->line(sprintf(' %s', str_replace("\n", "\n ", $process ? $process->errorOutput() : "Missing suitable 'open' binary."))); |
159
|
|
|
$this->newLine(); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
protected function getOpenCommand(string $osFamily): ?string |
164
|
|
|
{ |
165
|
|
|
return match ($osFamily) { |
166
|
|
|
'Windows' => 'start', |
167
|
|
|
'Darwin' => 'open', |
168
|
|
|
'Linux' => 'xdg-open', |
169
|
|
|
default => null |
170
|
|
|
}; |
171
|
|
|
} |
172
|
|
|
} |
173
|
|
|
|
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.