Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/dev-server/config/dev.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

return [
'port' => 8000,
'host' => 'localhost',
'detach' => true,
'docker' => true,
'frontend' => true,
Expand Down
12 changes: 10 additions & 2 deletions packages/dev-server/src/Command/DevUpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ public function execute(
): int {
$port = (int) ($input->getOption('port') ?? $input->getOption('p') ?? $this->config->getInt('dev.port'));
$foreground = $input->hasOption('foreground') || $input->hasOption('f');
$host = $input->getOption('host') ?? $this->config->getString('dev.host');

if (!preg_match('/\A[a-zA-Z0-9.\-:]+\z/', $host)) {
throw new DevServerException(
message: "Invalid host value: '$host'",
suggestion: "Use a valid hostname or IP address, e.g. --host=0.0.0.0 or --host=localhost",
);
}
$detach = !$foreground && ($input->hasOption('detach') || $input->hasOption('d') || $this->config->getBool(
'dev.detach',
));
Expand Down Expand Up @@ -156,8 +164,8 @@ public function execute(
}

// PHP server (always) — multiple workers needed for SSE
$phpCommand = "env PHP_CLI_SERVER_WORKERS=4 php -S localhost:$port -t public/";
$output->writeLine(" Starting PHP server: php -S localhost:$port");
$phpCommand = "env PHP_CLI_SERVER_WORKERS=4 php -S {$host}:{$port} -t public/";
$output->writeLine(" Starting PHP server: php -S {$host}:{$port}");
$pid = $startProcess('php', $phpCommand);

// In foreground mode, verify PHP server is alive — if it died, port is likely in use.
Expand Down
83 changes: 45 additions & 38 deletions packages/dev-server/tests/Command/DevUpCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ public function runForeground(): void
}
}

const CONFIG_DEFAULTS = [
'dev.port' => 8000,
'dev.host' => 'localhost',
'dev.docker' => false,
'dev.frontend' => false,
'dev.pubsub' => false,
'dev.detach' => true,
'dev.processes' => [],
];

/**
* Helper to create a DevUpCommand with standard test dependencies.
*
Expand All @@ -85,15 +95,7 @@ function createDevUpCommand(
file_put_contents($dir . '/public/index.php', '<?php');
}

$configDefaults = [
'dev.port' => 8000,
'dev.docker' => false,
'dev.frontend' => false,
'dev.pubsub' => false,
'dev.detach' => true,
'dev.processes' => [],
];
$fakeConfig = new FakeConfigRepository(array_merge($configDefaults, $config));
$fakeConfig = new FakeConfigRepository(array_merge(CONFIG_DEFAULTS, $config));

$dockerDetector = new DockerDetector($dir);
$frontendDetector = new FrontendDetector($dir);
Expand Down Expand Up @@ -150,6 +152,16 @@ function readStream(mixed $stream): string
expect($pm->started['php'])->toContain('localhost:7500');
});

it('reads host from config', function (): void {
['command' => $command, 'processManager' => $pm] = createDevUpCommand(['dev.host' => '127.0.0.1']);
['output' => $output] = createMemoryOutput();

$input = new Input(['marko', 'dev:up']);
$command->execute($input, $output);

expect($pm->started['php'])->toContain('127.0.0.1:8000');
});

it('reads docker setting from config', function (): void {
['command' => $command, 'processManager' => $pm] = createDevUpCommand([
'dev.docker' => 'custom-docker-up',
Expand Down Expand Up @@ -417,6 +429,25 @@ function readStream(mixed $stream): string
expect($pm->started['php'])->toContain('localhost:9000');
});

it('overrides config host with --host flag', function (): void {
['command' => $command, 'processManager' => $pm] = createDevUpCommand(['dev.host' => 'localhost']);
['output' => $output] = createMemoryOutput();

$input = new Input(['marko', 'dev:up', '--host=0.0.0.0']);
$command->execute($input, $output);

expect($pm->started['php'])->toContain('0.0.0.0:8000')
->and($pm->started['php'])->not->toContain('localhost');
});

it('rejects invalid host values', function (): void {
['command' => $command] = createDevUpCommand(['dev.host' => 'localhost']);
['output' => $output] = createMemoryOutput();

$input = new Input(['marko', 'dev:up', '--host=0.0.0.0; evil']);
$command->execute($input, $output);
})->throws(DevServerException::class, 'Invalid host value');

it('overrides config port with -p space syntax', function (): void {
['command' => $command, 'processManager' => $pm] = createDevUpCommand(['dev.port' => 8000]);
['output' => $output] = createMemoryOutput();
Expand Down Expand Up @@ -570,14 +601,7 @@ protected function isPubSubInstalled(): bool
mkdir($dir, 0755, true);
// Deliberately no public/index.php created

$fakeConfig = new FakeConfigRepository([
'dev.port' => 8000,
'dev.docker' => false,
'dev.frontend' => false,
'dev.pubsub' => false,
'dev.detach' => false,
'dev.processes' => [],
]);
$fakeConfig = new FakeConfigRepository(CONFIG_DEFAULTS);
$command = new DevUpCommand(
config: $fakeConfig,
dockerDetector: new DockerDetector($dir),
Expand All @@ -597,14 +621,7 @@ protected function isPubSubInstalled(): bool
$dir = sys_get_temp_dir() . '/marko-no-index-msg-' . uniqid();
mkdir($dir, 0755, true);

$fakeConfig = new FakeConfigRepository([
'dev.port' => 8000,
'dev.docker' => false,
'dev.frontend' => false,
'dev.pubsub' => false,
'dev.detach' => false,
'dev.processes' => [],
]);
$fakeConfig = new FakeConfigRepository(CONFIG_DEFAULTS);
$command = new DevUpCommand(
config: $fakeConfig,
dockerDetector: new DockerDetector($dir),
Expand Down Expand Up @@ -632,14 +649,11 @@ protected function isPubSubInstalled(): bool
$dir = sys_get_temp_dir() . '/marko-no-index-proc-' . uniqid();
mkdir($dir, 0755, true);

$fakeConfig = new FakeConfigRepository([
'dev.port' => 8000,
$fakeConfig = new FakeConfigRepository(array_merge(CONFIG_DEFAULTS, [
'dev.docker' => 'docker compose up',
'dev.frontend' => 'yarn dev',
'dev.pubsub' => false,
'dev.detach' => false,
'dev.processes' => [],
]);
]));
$pm = new FakeProcessManager();
$command = new DevUpCommand(
config: $fakeConfig,
Expand Down Expand Up @@ -668,14 +682,7 @@ protected function isPubSubInstalled(): bool
mkdir($dir . '/public', 0755, true);
file_put_contents($dir . '/public/index.php', '<?php');

$fakeConfig = new FakeConfigRepository([
'dev.port' => 8000,
'dev.docker' => false,
'dev.frontend' => false,
'dev.pubsub' => false,
'dev.detach' => false,
'dev.processes' => [],
]);
$fakeConfig = new FakeConfigRepository(CONFIG_DEFAULTS);
$pm = new FakeProcessManager();
$command = new DevUpCommand(
config: $fakeConfig,
Expand Down