Skip to content

Conversation

@daifma
Copy link

@daifma daifma commented Feb 8, 2026

Problem

The filter() method in WebDriver.php has three bugs:

Bug 1: Callback receives values instead of keys

The current code:

array_filter($capabilities, function ($capability) {
    return self::$w3cCapabilities[$capability] ?? 0;
})

array_filter passes values to the callback by default. When a capability value is an array
(e.g. goog:chromeOptions['args' => ['--no-sandbox']]), PHP tries to use the array as
an array key in self::$w3cCapabilities[$capability], causing a TypeError crash.

Bug 2: Extension capabilities are stripped

Even if Bug 1 were fixed, the filter only allows keys present in $w3cCapabilities. This strips
all extension capabilities like
goog:chromeOptions, moz:firefoxOptions, ms:edgeOptions, etc. Per the W3C spec, extension
capabilities are identified by containing a : in the key name and MUST be allowed through.

Bug 3: array_values destroys key-value pairs

The original code wraps the result in array_values(), which reindexes the array numerically.
This destroys the capability name → value mapping, turning
{"browserName": "chrome"} into ["chrome"].

Fix

- return $capabilities ? array_values(array_filter($capabilities, function ($capability) {
-     return self::$w3cCapabilities[$capability] ?? 0;
- })) : null;
+ return $capabilities ? array_filter($capabilities, function ($key) {
+     return isset(self::$w3cCapabilities[$key]) || str_contains($key, ':');
+ }, ARRAY_FILTER_USE_KEY) : null;

Changes:

  1. Use ARRAY_FILTER_USE_KEY so the callback receives keys not values
  2. Allow extension capabilities (keys containing :) per W3C spec
  3. Remove array_values() to preserve the key-value mapping

How to reproduce

  1. Configure capabilities with goog:chromeOptions:
    $capabilities = ['goog:chromeOptions' => ['args' => ['--no-sandbox']]];
  2. Call $webDriver->session('chrome', $capabilities)
  3. Observe: TypeError because array(['args' => ...]) is used as array key

References

The filter() method had three bugs:

1. array_filter callback received values instead of keys, causing
   TypeError when capability values are arrays (e.g. goog:chromeOptions)

2. Extension capabilities (keys containing ':') were stripped, but
   W3C spec requires them to be allowed through

3. array_values() destroyed the key-value mapping, turning
   {"browserName": "chrome"} into ["chrome"]

Fix by using ARRAY_FILTER_USE_KEY, allowing extension capabilities
per W3C spec, and removing array_values() wrapper.

References:
- https://www.w3.org/TR/webdriver2/#dfn-extension-capability
- https://www.php.net/array_filter
@aik099
Copy link
Contributor

aik099 commented Feb 10, 2026

@daifma , what the filter method is supposed to do: delete capabilities with the empty values?

If that is so, then we might risk deleting some capabilities that are enabled by default, but the user wants them disabled.

@daifma
Copy link
Author

daifma commented Feb 10, 2026

@aik099 Good question, thanks for the review!

No, the filter() method is not meant to remove capabilities with empty/falsy values. Its purpose is to whitelist valid capability keys — keeping only W3C standard capabilities and vendor extension capabilities (those containing : like goog:chromeOptions).

The critical part of the fix is the ARRAY_FILTER_USE_KEY flag: it makes array_filter() pass only the key (e.g. "acceptInsecureCerts") to the callback. The value is never examined. So:

$capabilities = [
    'acceptInsecureCerts' => false,  // kept — key is in $w3cCapabilities
    'browserName' => 'chrome',       // kept — key is in $w3cCapabilities  
    'goog:chromeOptions' => [...],   // kept — key contains ':'
    'unknownCap' => 'value',         // removed — not W3C, no ':'
];

Capabilities set to false, 0, null, or any falsy value are preserved correctly as long as their key is valid.

The old code had the opposite problem: without ARRAY_FILTER_USE_KEY, the callback received values instead of keys (e.g. 'chrome', false, [...]). It tried to use these values as lookup keys in $w3cCapabilities, which either removed everything (since 'chrome' isn't a key in the whitelist) or threw a TypeError when the value was an array.

Add WebDriverFilterTest covering 8 test cases:
- Non-W3C capabilities are stripped
- All W3C standard capabilities pass through
- Vendor extension capabilities (with ':') are preserved
- Falsy values (false, 0) are NOT stripped (addresses review concern)
- Null capabilities handled gracefully
- requiredCapabilities (alwaysMatch) also filtered
- Legacy capability names remapped to W3C equivalents
- Key-value mapping preserved (not reindexed numerically)
@daifma daifma force-pushed the fix/w3c-filter-capabilities branch from a39a3c0 to 9d89e62 Compare February 10, 2026 13:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants