Transport
Understand how PHP starts the bridge, exchanges commands, receives events, and reports failures.
The transport is the private boundary between the PHP client and the Node.js bridge. It starts the bridge process, writes framed JSON-RPC messages to its standard input, reads responses and events from standard output, and maps failures to PHP exceptions.
Normal tests should not build or parse transport messages. This page is for runtime diagnosis and contribution work.
The shipped path
PHP objects
-> JsonRpcTransport
-> Node.js bridge process
-> playwright-core
-> browser
TransportFactory assembles this path:
ServerFinderlocates the bundled bridge script.NodeBinaryResolverselects a compatible Node.js executable.ProcessLauncherstarts the bridge.JsonRpcTransportsends commands and waits for responses.ErrorMapperturns transport and driver errors into PHP exceptions.
The connection is local and process-based. It does not need an HTTP server or an open TCP port.
Commands and responses
Public method calls become internal action payloads. The JSON-RPC client adds a request id, writes an LSP-style Content-Length frame, and waits for the response with the matching id.
Conceptually:
PHP: page.goto(url)
Request: action=goto, pageId=..., url=...
Bridge: call Playwright Page.goto()
Result: navigation response data
PHP: return ResponseInterface
The exact JSON shape is internal. It can change while the public PHP method remains stable. Do not copy message payloads into application tests or treat them as a supported integration protocol.
Events while PHP is synchronous
The public API is synchronous, but browsers emit asynchronous events: requests, responses, console messages, dialogs, downloads, page errors, and frame changes.
The transport drains queued events while calls are in progress and dispatches them to handlers registered on the relevant PHP object. That lets a synchronous PHP test observe browser events without exposing promises or an event loop in user code.
Register handlers before the action that may emit the event:
$messages = [];
$page->events()->onConsole(function ($message) use (&$messages): void {
$messages[] = $message->text();
});
$page->goto('https://example.com');
For event-specific method names and callback types, use Events and the API reference.
Process failures
The layer that failed determines what to inspect:
| Symptom | Likely boundary |
|---|---|
| Node.js executable not found | Node resolution |
| Bridge script not found | Package installation or server discovery |
| Browser executable missing | Browser installation |
| Process exits during startup | Bridge or browser launch |
| Request times out with a running browser | Command, page state, or timeout |
| Connection closes unexpectedly | Bridge or browser crash |
Relevant exception families include:
ProcessLaunchException;ProcessCrashedException;DisconnectedException;TransportException;ProtocolErrorException;TimeoutException.
Catch specific exceptions when recovery differs. Otherwise let the failure retain its original context and collect logs around startup.
Logging without leaking data
The client accepts a PSR-3 logger when created through PlaywrightFactory. Use a debug logger temporarily when diagnosing startup or command flow:
$client = PlaywrightFactory::create($config, $logger);
Transport logs may contain URLs, headers, form values, or response data. Keep verbose logs out of normal CI output, redact secrets, and use short retention periods for failure artifacts.
Browser servers are different
launchServer() creates a long-lived browser server and returns a WebSocket endpoint. Connecting to that endpoint is a browser deployment pattern; it does not turn the PHP client transport itself into a public WebSocket protocol.
Likewise, WebSocket, WebSocketRoute, and related classes describe WebSocket traffic from the tested application. They are not the PHP-to-bridge transport.
Contributor rules
When changing this layer:
- preserve the public API even if the bridge payload changes;
- keep request ids and event ordering deterministic;
- add fixtures for malformed, timed-out, and out-of-order responses;
- map failures to meaningful exceptions;
- never require users to understand internal payloads for normal browser work.
Go next
- Architecture for the complete runtime.
- Configuration for Node.js, browser paths, timeouts, and proxy settings.
- Errors and failures for diagnosing a failed run.