{"record":{"id":"22fa8da0c216e6dd","repo":"sebastianbergmann/phpunit","slug":"s-does-not-match-socket-hostname-port-forma","errorCode":null,"errorMessage":"\"%s\" does not match \"socket://hostname:port\" format","messagePattern":"\"(.+?)\" does not match \"socket://hostname:port\" format","errorType":"exception","errorClass":"PHPUnit\\TextUI\\InvalidSocketException","httpStatus":null,"severity":"error","filePath":"src/TextUI/Output/Printer/DefaultPrinter.php","lineNumber":84,"sourceCode":"    public static function standardError(): self\n    {\n        return new self('php://stderr');\n    }\n\n    /**\n     * @throws CannotOpenSocketException\n     * @throws DirectoryDoesNotExistException\n     * @throws InvalidSocketException\n     */\n    private function __construct(string $out)\n    {\n        $this->isPhpStream = str_starts_with($out, 'php://');\n\n        if (str_starts_with($out, 'socket://')) {\n            $tmp = explode(':', str_replace('socket://', '', $out));\n\n            if (count($tmp) !== 2) {\n                throw new InvalidSocketException($out);\n            }\n\n            $stream = @fsockopen($tmp[0], (int) $tmp[1]);\n\n            if ($stream === false) {\n                throw new CannotOpenSocketException($tmp[0], (int) $tmp[1]);\n            }\n\n            $this->stream = $stream;\n            $this->isOpen = true;\n\n            return;\n        }\n\n        if (!$this->isPhpStream && !Filesystem::createDirectory(dirname($out))) {\n            throw new DirectoryDoesNotExistException(dirname($out));\n        }\n","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/sebastianbergmann/phpunit/blob/f123cdb2a2d49f15025794166cfed8bda8627dd2/src/TextUI/Output/Printer/DefaultPrinter.php#L66-L102","documentation":"DefaultPrinter accepts php://, socket://, and plain file targets for output options such as --log-teamcity, --log-junit, --testdox-text, and --testdox-html. For socket:// targets it strips the scheme, explodes on ':' and requires exactly two parts (host, port); any other count throws InvalidSocketException. Missing ports, extra colons, and IPv6 literals (which contain multiple colons, e.g. socket://[::1]:9000) all fail this check. A syntactically valid host:port that cannot be connected to is reported separately by CannotOpenSocketException.","triggerScenarios":"`--log-teamcity socket://localhost` (no port); `socket://logs.example.com:9514:extra`; `socket://[::1]:9514` (IPv6 literal splits into 3 parts); trailing colon `socket://host:`. Note PHPUnit itself catches InvalidSocketException for --log-teamcity and downgrades it to a PHPUnit warning, but direct users of the printer API get the exception.","commonSituations":"Shipping test results to a log collector (Logstash/Graphite-style TCP input) and forgetting the port; attempting IPv6 endpoints, which this simple parser does not support; port supplied as an empty CI variable.","solutions":["Use the exact form socket://hostname:port, e.g. `--log-teamcity socket://localhost:9514`","For IPv6, use a hostname (DNS name) that resolves to the v6 address instead of the literal","Default the port in CI: `socket://collector.example.com:${LOG_PORT:-9514}`"],"exampleFix":"# before\nvendor/bin/phpunit --log-teamcity socket://localhost\n\n# after\nvendor/bin/phpunit --log-teamcity socket://localhost:9514","handlingStrategy":"validation","validationCode":"/** True when DefaultPrinter will accept the target (host:port exactly, no IPv6 literal). */\nfunction validSocketTarget(string $target): bool\n{\n    if (!str_starts_with($target, 'socket://')) {\n        return true; // php:// and file paths take another code path\n    }\n\n    $parts = explode(':', substr($target, strlen('socket://')));\n\n    return count($parts) === 2\n        && $parts[0] !== ''\n        && is_numeric($parts[1])\n        && (int) $parts[1] > 0;\n}\n\n// before passing --log-teamcity / --log-junit / --testdox-* targets:\nif (!validSocketTarget($target)) {\n    $target = 'socket://collector.example.com:9514'; // or abort\n}","typeGuard":null,"tryCatchPattern":"try {\n    $printer = \\PHPUnit\\TextUI\\Output\\DefaultPrinter::from($target);\n} catch (\\PHPUnit\\TextUI\\Output\\InvalidSocketException $e) {\n    // malformed socket://host:port; fix the string (add/repair the port, avoid IPv6 literals)\n} catch (\\PHPUnit\\TextUI\\Output\\CannotOpenSocketException $e) {\n    // well-formed but unreachable: start the collector or fix DNS/firewall, then retry\n}","preventionTips":["Use the exact shape socket://hostname:port — one colon, both parts present","IPv6 literal addresses are not supported by this parser; use a hostname that resolves to the v6 address","Default the port in CI templates (${LOG_PORT:-9514}) so empty variables cannot strip it"],"tags":["phpunit","output","printer","socket","url-format","logging"],"backgroundTag":"invalid-socket-url","analyzedSha":"f123cdb2a2d49f15025794166cfed8bda8627dd2","analyzedAt":"2026-08-23T01:20:58.058Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}