Architecture
Understand what runs in PHP, what runs in Node.js, and where the browser fits.
Playwright PHP gives PHP code a synchronous API for controlling real Chromium, Firefox, and WebKit browsers. The browser automation engine still runs through Playwright's Node.js implementation; the PHP library starts that process, exchanges commands with it, and turns responses into PHP objects.
You normally do not need this architecture to write a test. It becomes useful when a browser cannot start, a process hangs, or you need to understand where an error originated.
The four layers
Your PHP script or test
|
v
Playwright PHP client
|
v
Node.js bridge + playwright-core
|
v
Chromium, Firefox, or WebKit
Each layer has a distinct job:
- Your code chooses the browser, creates an isolated context, drives pages, and asserts outcomes.
- The PHP client exposes typed objects, builds command payloads, waits for responses, maps errors, and dispatches events.
- The Node.js bridge receives commands over a local process transport and calls Playwright core.
- The browser renders pages, runs application JavaScript, performs network requests, and produces screenshots, downloads, video, or traces.
This is not Selenium or WebDriver. The PHP client talks to the Playwright bridge bundled with the project rather than translating calls into WebDriver HTTP commands.
What happens on the first call
The short entry point creates the whole stack for you:
use Playwright\Playwright;
$context = Playwright::chromium();
$page = $context->newPage();
Behind that call:
PlaywrightFactorycreates aPlaywrightClient.TransportFactorylocates the bundled server script and a compatible Node.js binary.- The transport starts the Node.js bridge.
- The browser builder launches Chromium.
- The library creates a browser context and returns it to PHP.
The convenience API keeps the client alive until PHP shuts down. For longer-lived tools or custom configuration, create the client explicitly and close it deliberately:
use Playwright\PlaywrightFactory;
$playwright = PlaywrightFactory::create();
$browser = $playwright->chromium()->launch();
$context = $browser->newContext();
try {
$page = $context->newPage();
$page->goto('https://example.com');
} finally {
$context->close();
$browser->close();
$playwright->close();
}
Where work happens
PHP owns orchestration. Calls such as goto(), click(), and screenshot() are sent to the bridge and block until a result or timeout comes back.
Browser behavior stays on the browser side:
- DOM queries and layout;
- page JavaScript;
- navigation and network;
- actionability checks;
- browser events;
- rendering and capture.
That boundary explains two common surprises. A PHP Locator is not a DOM node stored in memory, and closing PHP objects is not the same as terminating only the current PHP method. Those objects represent work and resources owned by another process.
Process lifecycle
A normal local run has at least three processes:
PHP
-> Node.js bridge
-> browser
The browser may create additional renderer and utility processes. That is normal.
If startup fails, diagnose the layers in order:
- Can PHP load Composer dependencies?
- Can the configured Node.js binary run?
- Can the bridge script be found?
- Are the requested browser binaries installed?
- Can the browser launch with the current operating-system dependencies?
Do not start with locators or assertions when the browser process never launched. Conversely, a visible-page assertion failure is usually not a transport problem.
Design choices that affect users
- Synchronous PHP calls. There are no promises or
await; a call returns, fails, or times out. - Isolated contexts. Contexts are the normal boundary for cookies, storage, permissions, pages, and test isolation.
- One Playwright engine. Browser behavior comes from Playwright core rather than a PHP reimplementation.
- Typed public API. Application code should use public classes and interfaces, not transport payloads.
- Explicit evidence. Screenshots, traces, logs, and video cross process boundaries and should be stored intentionally.
Go next
- Object model for the PHP objects and their ownership.
- Transport for startup, messages, events, and failures.
- Configuration for Node.js, browser, timeout, proxy, and artifact settings.
- Debugging when a test is failing rather than the runtime itself.