addOption('connect', null, InputOption::VALUE_NONE, 'Probe the backend over HTTP after the static checks.'); $this->addOption('url', null, InputOption::VALUE_REQUIRED, 'URL to probe when --connect is given.', 'http://127.0.0.1:8765/healthz'); } protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $io->title('php-qml bridge โ€” readiness checks'); $checks = [ ['PHP version >= 8.3', PHP_VERSION_ID >= 80300, 'Upgrade PHP to 8.3 or newer; the bundle requires it.'], ['ext-curl available', extension_loaded('curl'), 'Install the PHP curl extension.'], ['ext-json available', extension_loaded('json'), 'Install the PHP json extension.'], ['Publisher service wired', $this->publisher instanceof Publisher, 'Bundle services failed to load โ€” register PhpQml\\Bridge\\BridgeBundle in config/bundles.php.'], ['BRIDGE_TOKEN env set', $this->bridgeToken !== '', 'Set BRIDGE_TOKEN in .env.local; the Qt host expects this as the bearer token.'], ['MERCURE_URL env set', $this->mercureUrl !== '', 'Set MERCURE_URL in .env, e.g. http://127.0.0.1:8765/.well-known/mercure.'], ['MERCURE_PUBLISHER_JWT_KEY env set', $this->mercurePublisherKey !== '', 'Set MERCURE_PUBLISHER_JWT_KEY in .env.local; mercure-bundle uses it to sign publish requests.'], ['MERCURE_SUBSCRIBER_JWT_KEY env set', $this->mercureSubscriberKey !== '', 'Set MERCURE_SUBSCRIBER_JWT_KEY in .env.local; or rely on the Caddy `anonymous` directive in dev mode.'], ]; $rows = []; $allPass = true; foreach ($checks as [$label, $ok, $hint]) { $rows[] = [ $ok ? 'PASS' : 'FAIL', $label, $ok ? '' : $hint, ]; $allPass = $allPass && $ok; } $io->table(['', 'Check', 'Hint'], $rows); if ($input->getOption('connect')) { $url = (string) $input->getOption('url'); $io->section("Probing {$url}"); $probe = $this->probe($url); if ($probe['ok']) { $io->success("Backend reachable (HTTP {$probe['status']})."); } else { $io->error("Backend probe failed: {$probe['detail']}"); $allPass = false; } } if ($allPass) { $io->success('All checks passed. Bridge is ready.'); return Command::SUCCESS; } $io->warning('Some checks failed โ€” fix the items above before running the bridge.'); return Command::FAILURE; } /** * @return array{ok: bool, status?: int, detail: string} */ private function probe(string $url, float $timeoutSeconds = 2.0): array { $ctx = stream_context_create([ 'http' => [ 'method' => 'GET', 'timeout' => $timeoutSeconds, 'ignore_errors' => true, ], ]); $body = @file_get_contents($url, false, $ctx); if ($body === false) { $err = error_get_last()['message'] ?? 'unknown error'; return ['ok' => false, 'detail' => $err]; } $statusLine = $http_response_header[0] ?? ''; preg_match('#HTTP/\S+\s+(\d+)#', $statusLine, $m); $status = (int) ($m[1] ?? 0); return [ 'ok' => $status === 200, 'status' => $status, 'detail' => $status === 200 ? '' : "expected 200, got {$status}", ]; } }