ScreenshotNeo

BlogHow-to

How to Execute JavaScript in Selenium with PHP

Run synchronous and asynchronous JavaScript with php-webdriver, pass values and elements safely, select the right browser context, and troubleshoot common failures.

By the ScreenshotNeo team4 October 20268 min read

php-webdriver/php-webdriver runs page JavaScript through RemoteWebDriver::executeScript(). Use it for synchronous code; include a JavaScript return when PHP needs a value. For asynchronous browser work, use executeAsyncScript() and call its injected completion callback. Both execute in the currently selected frame or window.

The examples below use the PHP client for Selenium WebDriver. Its RemoteWebDriver API accepts a script and an optional array of arguments. The project’s command examples demonstrate returning document.title and passing elements through arguments.

1. Set up a PHP WebDriver session

Install the client in a PHP project with Composer:

composer require php-webdriver/webdriver

You also need a reachable Selenium WebDriver server and a browser configured on that server. The following assumes a Selenium server at http://localhost:4444. Adjust the endpoint and capabilities for your environment.

<?php
require __DIR__ . '/vendor/autoload.php';

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

$serverUrl = 'http://localhost:4444';
$options = new ChromeOptions();
$options->addArguments(['--headless=new', '--no-sandbox']);
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(ChromeOptions::CAPABILITY, $options);

$driver = RemoteWebDriver::create($serverUrl, $capabilities);

try {
    $driver->get('https://example.com');
    $title = $driver->executeScript('return document.title;');
    echo $title . PHP_EOL;
} finally {
    $driver->quit();
}

The browser options shown are examples, not universal requirements. In a container or managed Selenium environment, use the browser configuration that environment supports. The essential call for JavaScript execution is $driver->executeScript(...).

2. Execute synchronous JavaScript

executeScript() treats the supplied JavaScript as a synchronous snippet. It returns the result of evaluating the snippet to PHP. Add return if you want a value back:

$title = $driver->executeScript('return document.title;');

$driver->executeScript(
    'document.body.style.backgroundColor = "red";'
);

Without a return statement, a mutation can still run, but the caller should not expect a useful result. Keep snippets focused: read a value, update page state, or perform a page-context operation.

Return useful page values

$pageInfo = $driver->executeScript(
    'return {
        title: document.title,
        url: location.href,
        readyState: document.readyState
    };'
);

The client returns a value based on the WebDriver protocol and the PHP library/browser combination. Simple strings, booleans, and numbers are convenient return values. For complex JavaScript objects, verify the representation supported by your installed versions; do not assume every object maps to a particular PHP structure. Returning a deliberately small object or primitive also makes failures easier to diagnose.

3. Pass values and elements as arguments

Use the second PHP argument for data and located elements. In JavaScript, access them as arguments[0], arguments[1], and so on. This avoids building JavaScript source by concatenating values into a string.

use Facebook\WebDriver\WebDriverBy;

$heading = $driver->findElement(WebDriverBy::cssSelector('h1'));
$text = $driver->executeScript(
    'return arguments[0].innerText;',
    [$heading]
);

echo $text . PHP_EOL;

Pass ordinary values the same way:

$label = 'automated check';
$bodyText = $driver->executeScript(
    'return document.body.innerText + " — " + arguments[0];',
    [$label]
);

Argument passing keeps values separate from executable code and correctly handles quotes in strings. The php-webdriver reference documents passing located elements through the arguments array. Avoid interpolating untrusted input into the script itself.

4. Execute asynchronous JavaScript

Use executeAsyncScript() when the result depends on asynchronous work such as a timer or callback-based browser API. The WebDriver client appends a completion callback as the last JavaScript argument and waits until the script invokes it. Its callback value becomes the PHP method result.

$result = $driver->executeAsyncScript(
    'const done = arguments[arguments.length - 1];
     setTimeout(() => done("finished"), 100);'
);

echo $result . PHP_EOL;

For example, wait for a page condition with a bounded timeout in the script as well as an appropriate WebDriver script timeout:

// Use the timeout API available in your installed php-webdriver version.
// Configure its script timeout before running the asynchronous script.

$result = $driver->executeAsyncScript(
    'const done = arguments[arguments.length - 1];
     const selector = arguments[0];
     const deadline = Date.now() + 5000;

     function check() {
         const element = document.querySelector(selector);
         if (element) {
             done({ found: true, text: element.textContent });
             return;
         }
         if (Date.now() >= deadline) {
             done({ found: false });
             return;
         }
         setTimeout(check, 50);
     }
     check();',
    ['[data-ready="true"]']
);

Set the WebDriver script timeout using the timeout API provided by the php-webdriver version in your project. The exact method chain and accepted duration type can vary by installed version, so consult that version’s API rather than copying a timeout call from a different release. Pick a limit appropriate to the operation. Every success, error, and timeout path in the JavaScript should eventually invoke the callback; otherwise the driver waits until its configured script timeout.

5. Select the right frame or window

JavaScript runs in the currently selected browser context. If a target element is inside an iframe, switch into that frame before calling executeScript(). If it is in another window or tab, switch to that window first. Selenium’s window and tab documentation describes executing JavaScript in the selected frame or window.

// Switch into a frame located by its element.
$frame = $driver->findElement(WebDriverBy::cssSelector('iframe.payment'));
$driver->switchTo()->frame($frame);

$frameTitle = $driver->executeScript('return document.title;');

// Return to the top-level document when finished.
$driver->switchTo()->defaultContent();

For a new window, switch using its handle before executing a script:

$handles = $driver->getWindowHandles();
$driver->switchTo()->window(end($handles));
$currentUrl = $driver->executeScript('return location.href;');

Do not rely on window-handle ordering to identify a particular tab in complex flows. Track the handle created by the action that opened the window, then switch to that handle.

6. Choose between JavaScript and WebDriver interactions

Need Use
Read or change a page property in the page context executeScript()
Wait for asynchronous JavaScript work and receive its result executeAsyncScript(), with a completion callback and configured script timeout
Express a normal user action such as clicking a visible button Prefer the corresponding WebDriver element interaction when it captures the intent of the test
Interact with a different frame or window Switch context first, then execute the script

A JavaScript-triggered DOM action is not automatically equivalent to a user interaction. Use the normal WebDriver interaction when the test is meant to exercise user-facing behavior; use JavaScript when the operation specifically concerns page state or an operation that is naturally expressed in page context.

7. Common errors and fixes

Symptom Likely cause Fix
The result is empty or unavailable The script performs an action but has no return. Add return to the JavaScript when PHP needs a result.
An element lookup in JavaScript returns null The selector does not match, the page has not rendered it yet, or the driver is in the wrong frame. Check the selector and readiness condition; switch to the intended frame or window.
“No such element” occurs before script execution The PHP code tried to locate an element that is absent or not yet available. Wait for the element through an appropriate WebDriver wait, verify the locator, and confirm the current context.
Async script times out The completion callback was not called, or the operation exceeded the script timeout. Call the callback on every completion path, handle errors, and set a suitable script timeout.
The script affects the wrong document The current selected frame or window differs from the target. Switch to the intended frame or window before executing, then restore the expected context.
PHP cannot use the returned object as expected The returned JavaScript value’s protocol conversion differs from the assumed PHP type. Return a simple value or inspect the actual type; check the installed php-webdriver and Selenium versions.
JavaScript changes page state but the test still fails The script may bypass the user interaction or browser behavior the test is intended to verify. Use WebDriver’s normal interaction for user actions, and reserve script execution for page-context tasks.

8. Performance, reliability, and cost

Each call to executeScript() is a WebDriver command sent to the browser session. Combine related reads into one small script when that keeps the intent clear, rather than making many round trips for adjacent values. Avoid returning large DOM structures or repeatedly polling with very short intervals. For asynchronous work, use bounded waits and a configured script timeout so a missing page condition does not leave the test waiting indefinitely.

Keep scripts deterministic and scoped to the current page. A successful JavaScript call only establishes that the snippet ran in the selected context; it does not by itself prove that a page is ready for every subsequent interaction. Synchronize on the condition the test actually needs. Browser and Selenium infrastructure costs depend on how you host or access the WebDriver server; the supplied API references do not establish a universal price or performance benchmark.

9. Or skip the browser setup

If your goal is a website screenshot rather than a Selenium interaction test, ScreenshotNeo can return an image or PDF through one GET request. It is a website screenshot API and MCP server from Yorker Media. See the API documentation for request options.

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

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers report the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up for 1,000 free screenshots a month, with no card required.

10. FAQ

Can I use JavaScript variables declared in one call in the next call?

Local variables in the snippet do not remain available after it finishes. Return needed data or pass it into the next call as an argument. Global page variables may persist, but relying on them can make scripts sensitive to page state.

Can I execute JavaScript inside a cross-origin iframe from the top-level page?

Switch WebDriver into the iframe’s browsing context before running the script. Do not assume the top-level document’s JavaScript can directly inspect a cross-origin frame.

Should I use executeAsyncScript() for promises?

Use the callback contract documented by your WebDriver client. Ensure the script invokes the injected callback with the resolved value or an error result, and configure a script timeout that bounds the wait.