Passed
Push — master ( 66aaf6...e18e2b )
by Caen
05:07 queued 18s
created

ServeCommand::openInBrowser()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 5
eloc 10
c 3
b 0
f 0
nc 4
nop 0
dl 0
loc 15
rs 9.6111
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 sprintf;
17
use function str_replace;
18
use function class_exists;
19
20
/**
21
 * Start the realtime compiler server.
22
 *
23
 * @see https://github.com/hydephp/realtime-compiler
24
 */
25
class ServeCommand extends Command
26
{
27
    /** @var string */
28
    protected $signature = 'serve 
29
        {--host= : <comment>[default: "localhost"]</comment>}}
30
        {--port= : <comment>[default: 8080]</comment>}
31
        {--save-preview= : Should the served page be saved to disk? (Overrides config setting)}
32
        {--dashboard= : Enable the realtime compiler dashboard. (Overrides config setting)}
33
        {--pretty-urls= : Enable pretty URLs. (Overrides config setting)}
34
        {--play-cdn= : Enable the Tailwind Play CDN. (Overrides config setting)}
35
        {--open : Open the site preview in the browser.}
36
    ';
37
38
    /** @var string */
39
    protected $description = 'Start the realtime compiler server.';
40
41
    protected ConsoleOutput $console;
42
43
    public function safeHandle(): int
44
    {
45
        $this->configureOutput();
46
        $this->printStartMessage();
47
48
        if ($this->option('open')) {
49
            $this->openInBrowser();
50
        }
51
52
        $this->runServerProcess(sprintf('php -S %s:%d %s',
53
            $this->getHostSelection(),
54
            $this->getPortSelection(),
55
            $this->getExecutablePath()
56
        ));
57
58
        return Command::SUCCESS;
59
    }
60
61
    protected function getHostSelection(): string
62
    {
63
        return (string) $this->option('host') ?: Config::getString('hyde.server.host', 'localhost');
64
    }
65
66
    protected function getPortSelection(): int
67
    {
68
        return (int) ($this->option('port') ?: Config::getInt('hyde.server.port', 8080));
69
    }
70
71
    protected function getExecutablePath(): string
72
    {
73
        return Hyde::path('vendor/hyde/realtime-compiler/bin/server.php');
74
    }
75
76
    protected function runServerProcess(string $command): void
77
    {
78
        Process::forever()->env($this->getEnvironmentVariables())->run($command, $this->getOutputHandler());
79
    }
80
81
    protected function getEnvironmentVariables(): array
82
    {
83
        return Arr::whereNotNull([
84
            'HYDE_SERVER_REQUEST_OUTPUT' => ! $this->option('no-ansi'),
85
            'HYDE_SERVER_SAVE_PREVIEW' => $this->parseEnvironmentOption('save-preview'),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->parseEnvironmentOption('save-preview') targeting Hyde\Console\Commands\Se...arseEnvironmentOption() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

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.

Loading history...
86
            'HYDE_SERVER_DASHBOARD' => $this->parseEnvironmentOption('dashboard'),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->parseEnvironmentOption('dashboard') targeting Hyde\Console\Commands\Se...arseEnvironmentOption() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

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.

Loading history...
87
            'HYDE_PRETTY_URLS' => $this->parseEnvironmentOption('pretty-urls'),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->parseEnvironmentOption('pretty-urls') targeting Hyde\Console\Commands\Se...arseEnvironmentOption() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

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.

Loading history...
88
            'HYDE_PLAY_CDN' => $this->parseEnvironmentOption('play-cdn'),
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->parseEnvironmentOption('play-cdn') targeting Hyde\Console\Commands\Se...arseEnvironmentOption() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

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.

Loading history...
89
        ]);
90
    }
91
92
    protected function configureOutput(): void
93
    {
94
        if (! $this->useBasicOutput()) {
95
            $this->console = new ConsoleOutput($this->output->isVerbose());
96
        }
97
    }
98
99
    protected function printStartMessage(): void
100
    {
101
        $this->useBasicOutput()
102
            ? $this->output->writeln('<info>Starting the HydeRC server...</info> Press Ctrl+C to stop')
103
            : $this->console->printStartMessage($this->getHostSelection(), $this->getPortSelection(), $this->getEnvironmentVariables());
104
    }
105
106
    protected function getOutputHandler(): Closure
107
    {
108
        return $this->useBasicOutput() ? function (string $type, string $line): void {
109
            $this->output->write($line);
110
        } : $this->console->getFormatter();
111
    }
112
113
    protected function useBasicOutput(): bool
114
    {
115
        return $this->option('no-ansi') || ! class_exists(ConsoleOutput::class);
116
    }
117
118
    protected function parseEnvironmentOption(string $name): ?string
119
    {
120
        $value = $this->option($name) ?? $this->checkArgvForOption($name);
121
122
        if ($value !== null) {
123
            return match ($value) {
124
                'true', '' => 'enabled',
125
                'false' => 'disabled',
126
                default => throw new InvalidArgumentException(sprintf('Invalid boolean value for --%s option.', $name))
127
            };
128
        }
129
130
        return null;
131
    }
132
133
    /** Fallback check so that an environment option without a value is acknowledged as true. */
134
    protected function checkArgvForOption(string $name): ?string
135
    {
136
        if (isset($_SERVER['argv'])) {
137
            if (in_array("--$name", $_SERVER['argv'], true)) {
138
                return 'true';
139
            }
140
        }
141
142
        return null;
143
    }
144
145
    protected function openInBrowser(): void
146
    {
147
        $command = match (PHP_OS_FAMILY) {
148
            'Windows' => 'start',
149
            'Darwin' => 'open',
150
            'Linux' => 'xdg-open',
151
            default => null
152
        };
153
154
        $process = $command ? Process::command(sprintf('%s http://%s:%d', $command, $this->getHostSelection(), $this->getPortSelection()))->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