Object model
Understand the public PHP objects, their responsibilities, and their lifecycles.
Playwright PHP exposes small PHP objects for the browser concepts you work with: clients, browsers, contexts, pages, frames, locators, network messages, and artifacts. Each object has a specific scope. Choosing the right scope keeps tests easier to isolate and debug.
Two entry styles
For a script or a first test, use the convenience entry point:
use Playwright\Playwright;
$context = Playwright::chromium();
$page = $context->newPage();
Playwright::chromium(), firefox(), and webkit() return a BrowserContextInterface directly. Browser and client setup are managed for you.
Use the explicit client when you need configuration, browser builders, a long-lived process, or more control over cleanup:
use Playwright\PlaywrightFactory;
$client = PlaywrightFactory::create();
$browser = $client->chromium()
->withHeadless(true)
->launch();
$context = $browser->newContext();
$page = $context->newPage();
Both styles reach the same browser engine. The difference is how much lifecycle and configuration you own.
The useful hierarchy
PlaywrightClient
-> Browser
-> BrowserContext
-> Page
-> Frame
-> Locator
-> Keyboard / Mouse / Touchscreen
-> Request / Response / Route
-> Download / Video
Client
PlaywrightClient owns the connection to the Node.js bridge. It creates browser builders, exposes selector configuration, and closes the transport.
Most application code does not need to pass the client around. Treat it as runtime infrastructure.
Browser
BrowserInterface represents a launched browser process. It can create contexts and report browser-level information.
Launching browsers is relatively expensive. A test suite normally reuses a browser while creating a fresh context for each isolated scenario.
Browser context
BrowserContextInterface is the main isolation boundary. It owns:
- cookies and storage;
- permissions, locale, timezone, viewport, and user agent;
- pages;
- routes and browser-level events;
- tracing and video configuration.
Use a new context when the scenario needs a new user or clean browser state. Opening another page is not equivalent: pages in one context share session state.
Page and frame
PageInterface represents one tab. It is the normal surface for navigation, locators, input, events, network observation, screenshots, and PDF capture.
A frame is a document embedded in the page. Prefer frameLocator() when you need to locate and act inside an iframe. It keeps the frame boundary visible in the test and avoids storing a frame that may be replaced during navigation.
Locator
LocatorInterface is a live description of how to find an element. It is not a cached DOM node.
That distinction matters: when the page re-renders, a locator resolves against the current document before each action or assertion. Prefer role, label, text, and test-id locators over storing element-like state yourself.
Supporting objects
Network and runtime objects describe a moment in a browser run:
RequestInterfacedescribes an outgoing request.ResponseInterfacedescribes its response.RouteInterfacecontrols an intercepted request.ConsoleMessage,DialogInterface,DownloadInterface, andWebErrorInterfacecarry event-specific information.
These are not good places to store application state. Read what you need while handling the event, then assert the user-visible result or save the relevant artifact.
Interfaces and implementations
The public API commonly returns interfaces such as PageInterface, LocatorInterface, and BrowserContextInterface. Type your helpers against those interfaces:
use Playwright\Locator\LocatorInterface;
function submitButton(LocatorInterface $form): LocatorInterface
{
return $form->getByRole('button', ['name' => 'Save']);
}
Use the API reference for exact return types. Avoid depending on transport classes from test code; they are infrastructure, not browser concepts.
Close the owner you created
Ownership flows downward:
- closing a context closes its pages;
- closing a browser closes its contexts;
- closing the client disconnects the bridge.
Use try/finally when your code creates the explicit runtime:
$client = PlaywrightFactory::create();
try {
$browser = $client->chromium()->launch();
$context = $browser->newContext();
$page = $context->newPage();
// Work with the page.
} finally {
$client->close();
}
For PHPUnit, centralize this lifecycle in the provided testing helpers instead of rebuilding it in every test.
Choose the right level
| Need | Work with |
|---|---|
| A new isolated user | Browser context |
| Another tab in the same session | Page |
| Content inside an iframe | Frame locator |
| A resilient element target | Locator |
| Observe one HTTP exchange | Request and response |
| Modify browser traffic | Route |
| Configure or supervise the runtime | Client and browser |