ScreenshotNeo

BlogHow-to

Selenium with PHP: A Beginner’s Tutorial

Learn how to install php-webdriver, connect PHP to ChromeDriver, automate a browser, handle waits, and clean up sessions.

By the ScreenshotNeo team4 October 202610 min read

Selenium with PHP lets you automate a real browser from PHP code. Install the community-maintained php-webdriver/webdriver package with Composer, run Chrome and a compatible ChromeDriver endpoint, then use the PHP client to navigate, find elements, interact, check results, and close the browser session.

This tutorial builds a small local Chrome example first, then covers waits, locators, configuration, common failures, and when to use Selenium Server or Grid. For screenshots without managing a browser and driver, see ScreenshotNeo.

1. Understand the pieces

Selenium WebDriver is an API and protocol for controlling a browser. In a PHP setup, the PHP client sends WebDriver commands to a browser-specific driver, and that driver controls Chrome or another browser. The browser is the thing rendering the page; the PHP library does not include the browser or driver.

  • PHP binding: the php-webdriver/webdriver client library in your project.
  • WebDriver: the browser-control API and protocol.
  • Browser driver: a remote endpoint such as ChromeDriver that receives commands and controls a specific browser.
  • Browser: Chrome/Chromium, Firefox, or another supported browser running locally or remotely.

Selenium’s setup guidance describes the essential components as a language binding, browser, and browser driver. The PHP binding discussed here is community maintained; do not mistake it for a PHP binding maintained by the Selenium project. See Selenium’s getting started guide and WebDriver documentation.

2. Install the PHP WebDriver client

Install Composer if your project does not already use it, then add the package from your project directory:

composer require php-webdriver/webdriver

Composer creates or updates composer.json, resolves the library, and installs its dependencies. The script will load Composer’s autoloader from vendor/autoload.php.

At the time of the research snapshot, Packagist showed version 1.16.0, published 2025-12-28, with PHP ^7.3 || ^8.0 and the curl, json, and zip extensions. These are registry details that can change: check the current Packagist package record and project README before pinning a version or diagnosing a dependency conflict. Use the current package name, php-webdriver/webdriver; older examples may use its former name, facebook/webdriver.

3. Install and start ChromeDriver

You need Chrome or Chromium installed, plus a compatible ChromeDriver executable. ChromeDriver is a separate program; installing the Composer package does not install it. Browser and driver compatibility changes as Chrome releases, so follow the current ChromeDriver setup instructions rather than relying on an old version pin.

Start ChromeDriver so it listens on the local endpoint http://localhost:4444. Keep that process running while your PHP script runs. For example, after installing ChromeDriver and ensuring it is on your executable path, the command is commonly:

chromedriver --port=4444

Check the current ChromeDriver instructions for installation and platform-specific commands. The PHP client connects to that endpoint. For the first local exercise, you do not need Selenium Server: a direct browser-driver endpoint is enough.

4. Create a browser session and navigate

Save the following as first-browser.php in the Composer project. It opens example.com, prints the page title, and always asks the remote session to close in a finally block.

<?php
declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;

$serverUrl = 'http://localhost:4444';
$driver = RemoteWebDriver::create(
    $serverUrl,
    DesiredCapabilities::chrome()
);

try {
    $driver->get('https://example.com');
    $title = $driver->getTitle();
    echo "Page title: {$title}\n";

    if ($title !== 'Example Domain') {
        throw new RuntimeException("Unexpected page title: {$title}");
    }
} finally {
    $driver->quit();
}

Run it with:

php first-browser.php

The sequence is: create a remote WebDriver session with Chrome capabilities, navigate with get(), inspect the browser with getTitle(), and close the session with quit(). If session creation fails, confirm ChromeDriver is running at the exact endpoint and can launch your installed browser.

5. Find and interact with an element

Locators identify elements in the page’s DOM. Prefer a stable ID or CSS selector that the application owns. The following example visits a small HTML data page, finds its link by CSS selector, checks the visible label, clicks it, and checks the destination title. Using a controlled data URL keeps the example self-contained.

<?php
declare(strict_types=1);

require_once __DIR__ . '/vendor/autoload.php';

use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\WebDriverBy;

$html = '<!doctype html><html><head><title>Start</title></head>'
    . '<body><a id="continue" href="data:text/html,<title>Done</title>Finished">Continue</a></body></html>';
$url = 'data:text/html;charset=utf-8,' . rawurlencode($html);

$driver = RemoteWebDriver::create(
    'http://localhost:4444',
    DesiredCapabilities::chrome()
);

try {
    $driver->get($url);
    $link = $driver->findElement(WebDriverBy::id('continue'));

    if ($link->getText() !== 'Continue') {
        throw new RuntimeException('The expected link was not present.');
    }

    $link->click();
    $driver->wait(5)->until(
        static fn ($browser) => $browser->getTitle() === 'Done'
    );

    echo "Navigation completed: {$driver->getTitle()}\n";
} finally {
    $driver->quit();
}

For a real application, replace the data URL with your page and choose a locator tied to a stable attribute. A CSS selector such as form#login or [data-testid="submit"] is often less brittle than a long chain of ancestor and child selectors. Use XPath when relationships or text-based selection require it, not as a default.

6. Wait for pages and elements

Browser operations are asynchronous: a navigation can return before a dynamic page has finished rendering the element your test needs. A fixed sleep pauses for a guessed amount of time and is usually either wasteful or too short. Prefer an explicit wait for a specific condition.

$driver->wait(10)->until(
    static fn ($browser) => $browser->findElements(
        WebDriverBy::cssSelector('[data-testid="results"]')
    ) !== []
);

This waits up to ten seconds for at least one matching element. If the condition never becomes true, the wait times out and the test should report a failure. Wait for the condition your next action depends on: visibility, a changed title, a result count, or a URL change. Selenium’s documentation covers synchronization and waiting strategies in its waits guide.

Do not mix implicit and explicit waits casually. Combining wait strategies can produce confusing total delays. Keep waits close to the condition they protect, and make the timeout reflect the application’s expected behavior in the environment where the test runs.

7. Use assertions and clean up reliably

The tutorial uses PHP exceptions for simple checks so it runs without a test framework. In a PHPUnit test, express the same checks with the project’s normal assertions and keep browser creation and teardown in setup/teardown methods or a try/finally block.

Always call quit() when the test is done. It closes the WebDriver session and browser process managed by the endpoint. A PHP process ending is not a substitute for explicit session cleanup, especially when the endpoint is remote or shared.

8. Configuration options you will commonly need

Start with the defaults and add configuration only when your task needs it. The exact capability APIs can vary with the client release and driver; check the php-webdriver README and examples for the version installed in your project.

Need What to configure Practical note
Different browser Browser-specific capabilities and the matching browser driver endpoint. Install that browser and its compatible driver; Chrome capabilities do not launch Firefox.
Remote browser The remote WebDriver server URL and capabilities accepted by that server. Network access, authentication, and allowed capabilities depend on the remote service.
Headless CI Chrome options for headless operation and any required sandbox or shared-memory settings. Use current Chrome options and your CI provider’s guidance; do not copy flags without understanding their security and runtime effect.
Custom viewport Set the window size after session creation using the window management API. Viewport affects responsive layouts; use a consistent size for repeatable checks.
Authentication or special environment Configure the browser profile, cookies, or remote environment as appropriate. Avoid putting secrets in source control or logging them in test output.
Multiple browsers or parallel jobs Use a Selenium Server/Grid or another remote WebDriver endpoint. Each parallel run needs an isolated session and adequate browser capacity.

For deterministic tests, keep browser, driver, viewport, locale, and test data consistent. When a page depends on external services, account for network variability with meaningful waits and assertions instead of assuming an exact load duration.

9. Choose between a direct driver and Selenium Server/Grid

Approach Best for Setup and scale
Direct local driver Learning, a single browser, local debugging. Lowest setup overhead; PHP talks to a driver on the same machine.
Selenium Server/Grid Several browser types, remote machines, CI orchestration, distributed or parallel runs. More infrastructure and configuration; sessions can be routed to remote browser nodes.

Use the direct endpoint while learning. Move to Selenium Server/Grid when you need remote browsers, multiple browser types, or distributed execution. The PHP project documents both direct-driver and Selenium Server workflows in its README.

10. Troubleshoot common failures

Symptom Likely cause Fix
vendor/autoload.php not found Composer dependencies were not installed, or the script runs from a different project layout. Run composer require php-webdriver/webdriver in the project and point require_once to the correct vendor directory.
Connection refused or cannot connect to localhost:4444 ChromeDriver is not running, listens on another port, or the URL is wrong. Start ChromeDriver, confirm its listening endpoint, and use the same host and port in RemoteWebDriver::create().
Session not created / Chrome failed to start Chrome is missing, the driver and browser are incompatible, or the runtime environment blocks startup. Install Chrome/Chromium, follow current ChromeDriver pairing guidance, and inspect the driver’s startup output and environment-specific requirements.
Element not found The locator is wrong, the element has not appeared, or it is inside a frame or shadow root. Inspect the live DOM, use a stable locator, wait for the element, and switch into the relevant browsing context where needed.
Click intercepted or element not interactable An overlay covers the target, it is hidden, or layout changes before the click. Wait for the overlay to disappear and for the target to be visible and enabled; avoid using JavaScript clicks to hide a real user-facing problem.
Wait times out The expected condition never occurs, the selector is stale, or the timeout is too short for the environment. Check the assertion and selector first, then wait on the correct condition and choose an evidence-based timeout.
Browser processes accumulate Sessions are not being closed after exceptions. Put quit() in a finally block and ensure every created session has a teardown path.
Composer reports platform or extension requirements The PHP version or required extensions do not match the package constraints. Check the current Packagist requirements and enable the required PHP extensions in the CLI PHP configuration used by Composer.

11. Performance, reliability, and cost

A browser session has more startup and resource overhead than an HTTP request because it launches or connects to a real browser. Reuse a session for a sequence of related steps, but isolate test data and state so one test does not contaminate another. Parallel execution can reduce wall-clock time while increasing CPU, memory, and browser capacity requirements.

Reliability improves when tests use stable selectors, condition-based waits, controlled test data, and explicit session cleanup. External pages can change markup or depend on network services, so a tutorial locator that works today may need maintenance if the page changes. Pinning a compatible browser and driver in CI can improve repeatability, but update that pairing intentionally as releases evolve.

Open-source Selenium and php-webdriver do not imply zero operating cost: account for the machines, CI minutes, browser capacity, maintenance, and any remote browser service you choose. There is no universal runtime or cost benchmark for this setup; it depends on the page, browser, environment, and concurrency.

12. Or skip the browser setup

If your task is to capture a page as an image or PDF rather than interact with its controls, ScreenshotNeo provides a website screenshot API and MCP server. The API accepts a URL in one GET request. See the ScreenshotNeo API documentation for the request options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers report the page verdict and billing status. An MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Create a free ScreenshotNeo account to get 1,000 screenshots a month with no card.

FAQ

Is php-webdriver an official Selenium PHP binding?

No. It is a community-maintained PHP client that communicates with WebDriver endpoints.

Can I automate a browser without ChromeDriver?

Yes, with another supported browser and its corresponding driver or a remote WebDriver service. The browser and endpoint still need to support the capabilities you request.

Do I need Selenium Grid to follow this tutorial?

No. A local ChromeDriver endpoint is enough for the first example. Grid is useful when you need remote, multiple, or distributed browser sessions.

Is Selenium the right tool for every screenshot?

Selenium is suited to browser interaction and end-to-end testing. For a URL-to-image or PDF capture without browser-session setup, ScreenshotNeo is an alternative described above.