+ Add php-webdriver tools.

This commit is contained in:
liyang
2024-04-22 14:25:49 +08:00
parent 211cf51e4e
commit b840a6c946
209 changed files with 20958 additions and 1 deletions
@@ -0,0 +1,12 @@
<?php
// autoload.php @generated by Composer
if (PHP_VERSION_ID < 50600) {
echo 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL;
exit(1);
}
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInita9d739e5f031abd9c929e6b2f18db2f1::getLoader();
@@ -0,0 +1,572 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Autoload;
/**
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
*
* $loader = new \Composer\Autoload\ClassLoader();
*
* // register classes with namespaces
* $loader->add('Symfony\Component', __DIR__.'/component');
* $loader->add('Symfony', __DIR__.'/framework');
*
* // activate the autoloader
* $loader->register();
*
* // to enable searching the include path (eg. for PEAR packages)
* $loader->setUseIncludePath(true);
*
* In this example, if you try to use a class in the Symfony\Component
* namespace or one of its children (Symfony\Component\Console for instance),
* the autoloader will first look for the class under the component/
* directory, and it will then fallback to the framework/ directory if not
* found before giving up.
*
* This class is loosely based on the Symfony UniversalClassLoader.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
if ($this->classMap) {
$this->classMap = array_merge($this->classMap, $classMap);
} else {
$this->classMap = $classMap;
}
}
/**
* Registers a set of PSR-0 directories for a given prefix, either
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
if (!$prefix) {
if ($prepend) {
$this->fallbackDirsPsr0 = array_merge(
(array) $paths,
$this->fallbackDirsPsr0
);
} else {
$this->fallbackDirsPsr0 = array_merge(
$this->fallbackDirsPsr0,
(array) $paths
);
}
return;
}
$first = $prefix[0];
if (!isset($this->prefixesPsr0[$first][$prefix])) {
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
return;
}
if ($prepend) {
$this->prefixesPsr0[$first][$prefix] = array_merge(
(array) $paths,
$this->prefixesPsr0[$first][$prefix]
);
} else {
$this->prefixesPsr0[$first][$prefix] = array_merge(
$this->prefixesPsr0[$first][$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-4 directories for a given namespace, either
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
if (!$prefix) {
// Register directories for the root namespace.
if ($prepend) {
$this->fallbackDirsPsr4 = array_merge(
(array) $paths,
$this->fallbackDirsPsr4
);
} else {
$this->fallbackDirsPsr4 = array_merge(
$this->fallbackDirsPsr4,
(array) $paths
);
}
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
// Register directories for a new namespace.
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
} elseif ($prepend) {
// Prepend directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
(array) $paths,
$this->prefixDirsPsr4[$prefix]
);
} else {
// Append directories for an already registered namespace.
$this->prefixDirsPsr4[$prefix] = array_merge(
$this->prefixDirsPsr4[$prefix],
(array) $paths
);
}
}
/**
* Registers a set of PSR-0 directories for a given prefix,
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr0 = (array) $paths;
} else {
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
}
}
/**
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {
$length = strlen($prefix);
if ('\\' !== $prefix[$length - 1]) {
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
}
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
$this->prefixDirsPsr4[$prefix] = (array) $paths;
}
}
/**
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
$this->useIncludePath = $useIncludePath;
}
/**
* Can be used to check if the autoloader uses the include path to check
* for classes.
*
* @return bool
*/
public function getUseIncludePath()
{
return $this->useIncludePath;
}
/**
* Turns off searching the prefix and fallback directories for classes
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
$this->classMapAuthoritative = $classMapAuthoritative;
}
/**
* Should class lookup fail if not found in the current class map?
*
* @return bool
*/
public function isClassMapAuthoritative()
{
return $this->classMapAuthoritative;
}
/**
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
}
/**
* The APCu prefix in use, or null if APCu caching is not enabled.
*
* @return string|null
*/
public function getApcuPrefix()
{
return $this->apcuPrefix;
}
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
if ($file = $this->findFile($class)) {
includeFile($file);
return true;
}
return null;
}
/**
* Finds the path to the file where the class is defined.
*
* @param string $class The name of the class
*
* @return string|false The path if found, false otherwise
*/
public function findFile($class)
{
// class map lookup
if (isset($this->classMap[$class])) {
return $this->classMap[$class];
}
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
return false;
}
if (null !== $this->apcuPrefix) {
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
if ($hit) {
return $file;
}
}
$file = $this->findFileWithExtension($class, '.php');
// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
$file = $this->findFileWithExtension($class, '.hh');
}
if (null !== $this->apcuPrefix) {
apcu_add($this->apcuPrefix.$class, $file);
}
if (false === $file) {
// Remember that this class does not exist.
$this->missingClasses[$class] = true;
}
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
$first = $class[0];
if (isset($this->prefixLengthsPsr4[$first])) {
$subPath = $class;
while (false !== $lastPos = strrpos($subPath, '\\')) {
$subPath = substr($subPath, 0, $lastPos);
$search = $subPath . '\\';
if (isset($this->prefixDirsPsr4[$search])) {
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
foreach ($this->prefixDirsPsr4[$search] as $dir) {
if (file_exists($file = $dir . $pathEnd)) {
return $file;
}
}
}
}
}
// PSR-4 fallback dirs
foreach ($this->fallbackDirsPsr4 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
return $file;
}
}
// PSR-0 lookup
if (false !== $pos = strrpos($class, '\\')) {
// namespaced class name
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
} else {
// PEAR-like class name
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
}
if (isset($this->prefixesPsr0[$first])) {
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
if (0 === strpos($class, $prefix)) {
foreach ($dirs as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
}
}
}
// PSR-0 fallback dirs
foreach ($this->fallbackDirsPsr0 as $dir) {
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
return $file;
}
}
// PSR-0 include paths.
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
return $file;
}
return false;
}
}
/**
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{
include $file;
}
@@ -0,0 +1,352 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*
* @final
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}
@@ -0,0 +1,21 @@
Copyright (c) Nils Adermann, Jordi Boggiano
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is furnished
to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
@@ -0,0 +1,15 @@
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Attribute' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'PhpToken' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Stringable' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'UnhandledMatchError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => $vendorDir . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);
@@ -0,0 +1,12 @@
<?php
// autoload_files.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
'2a3c2110e8e0295330dc3d11a4cbc4cb' => $vendorDir . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php',
);
@@ -0,0 +1,9 @@
<?php
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
);
@@ -0,0 +1,13 @@
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
return array(
'Symfony\\Polyfill\\Php80\\' => array($vendorDir . '/symfony/polyfill-php80'),
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Facebook\\WebDriver\\' => array($vendorDir . '/php-webdriver/webdriver/lib'),
);
@@ -0,0 +1,57 @@
<?php
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita9d739e5f031abd9c929e6b2f18db2f1
{
private static $loader;
public static function loadClassLoader($class)
{
if ('Composer\Autoload\ClassLoader' === $class) {
require __DIR__ . '/ClassLoader.php';
}
}
/**
* @return \Composer\Autoload\ClassLoader
*/
public static function getLoader()
{
if (null !== self::$loader) {
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInita9d739e5f031abd9c929e6b2f18db2f1', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInita9d739e5f031abd9c929e6b2f18db2f1', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita9d739e5f031abd9c929e6b2f18db2f1::getInitializer($loader));
$loader->register(true);
$includeFiles = \Composer\Autoload\ComposerStaticInita9d739e5f031abd9c929e6b2f18db2f1::$files;
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirea9d739e5f031abd9c929e6b2f18db2f1($fileIdentifier, $file);
}
return $loader;
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequirea9d739e5f031abd9c929e6b2f18db2f1($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}
@@ -0,0 +1,65 @@
<?php
// autoload_static.php @generated by Composer
namespace Composer\Autoload;
class ComposerStaticInita9d739e5f031abd9c929e6b2f18db2f1
{
public static $files = array (
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
'2a3c2110e8e0295330dc3d11a4cbc4cb' => __DIR__ . '/..' . '/php-webdriver/webdriver/lib/Exception/TimeoutException.php',
);
public static $prefixLengthsPsr4 = array (
'S' =>
array (
'Symfony\\Polyfill\\Php80\\' => 23,
'Symfony\\Polyfill\\Mbstring\\' => 26,
'Symfony\\Component\\Process\\' => 26,
),
'F' =>
array (
'Facebook\\WebDriver\\' => 19,
),
);
public static $prefixDirsPsr4 = array (
'Symfony\\Polyfill\\Php80\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-php80',
),
'Symfony\\Polyfill\\Mbstring\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
),
'Symfony\\Component\\Process\\' =>
array (
0 => __DIR__ . '/..' . '/symfony/process',
),
'Facebook\\WebDriver\\' =>
array (
0 => __DIR__ . '/..' . '/php-webdriver/webdriver/lib',
),
);
public static $classMap = array (
'Attribute' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Attribute.php',
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'PhpToken' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/PhpToken.php',
'Stringable' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/Stringable.php',
'UnhandledMatchError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/UnhandledMatchError.php',
'ValueError' => __DIR__ . '/..' . '/symfony/polyfill-php80/Resources/stubs/ValueError.php',
);
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInita9d739e5f031abd9c929e6b2f18db2f1::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInita9d739e5f031abd9c929e6b2f18db2f1::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInita9d739e5f031abd9c929e6b2f18db2f1::$classMap;
}, null, ClassLoader::class);
}
}
@@ -0,0 +1,311 @@
{
"packages": [
{
"name": "php-webdriver/webdriver",
"version": "1.12.1",
"version_normalized": "1.12.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-webdriver/php-webdriver.git",
"reference": "b27ddf458d273c7d4602106fcaf978aa0b7fe15a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-webdriver/php-webdriver/zipball/b27ddf458d273c7d4602106fcaf978aa0b7fe15a",
"reference": "b27ddf458d273c7d4602106fcaf978aa0b7fe15a",
"shasum": ""
},
"require": {
"ext-curl": "*",
"ext-json": "*",
"ext-zip": "*",
"php": "^5.6 || ~7.0 || ^8.0",
"symfony/polyfill-mbstring": "^1.12",
"symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0 || ^6.0"
},
"replace": {
"facebook/webdriver": "*"
},
"require-dev": {
"ondram/ci-detector": "^2.1 || ^3.5 || ^4.0",
"php-coveralls/php-coveralls": "^2.4",
"php-mock/php-mock-phpunit": "^1.1 || ^2.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9",
"squizlabs/php_codesniffer": "^3.5",
"symfony/var-dumper": "^3.3 || ^4.0 || ^5.0 || ^6.0"
},
"suggest": {
"ext-SimpleXML": "For Firefox profile creation"
},
"time": "2022-05-03T12:16:34+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"files": [
"lib/Exception/TimeoutException.php"
],
"psr-4": {
"Facebook\\WebDriver\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.",
"homepage": "https://github.com/php-webdriver/php-webdriver",
"keywords": [
"Chromedriver",
"geckodriver",
"php",
"selenium",
"webdriver"
],
"support": {
"issues": "https://github.com/php-webdriver/php-webdriver/issues",
"source": "https://github.com/php-webdriver/php-webdriver/tree/1.12.1"
},
"install-path": "../php-webdriver/webdriver"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.26.0",
"version_normalized": "1.26.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-mbstring": "*"
},
"suggest": {
"ext-mbstring": "For best performance"
},
"time": "2022-05-24T11:49:31+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Mbstring\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill for the Mbstring extension",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"mbstring",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"install-path": "../symfony/polyfill-mbstring"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.26.0",
"version_normalized": "1.26.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"time": "2022-05-10T07:21:04+00:00",
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"installation-source": "dist",
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php80\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Ion Bazan",
"email": "ion.bazan@gmail.com"
},
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"install-path": "../symfony/polyfill-php80"
},
{
"name": "symfony/process",
"version": "v5.4.8",
"version_normalized": "5.4.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/597f3fff8e3e91836bb0bd38f5718b56ddbde2f3",
"reference": "597f3fff8e3e91836bb0bd38f5718b56ddbde2f3",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/polyfill-php80": "^1.16"
},
"time": "2022-04-08T05:07:18+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v5.4.8"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"install-path": "../symfony/process"
}
],
"dev": true,
"dev-package-names": []
}
@@ -0,0 +1,65 @@
<?php return array(
'root' => array(
'name' => '__root__',
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => NULL,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev' => true,
),
'versions' => array(
'__root__' => array(
'pretty_version' => '1.0.0+no-version-set',
'version' => '1.0.0.0',
'reference' => NULL,
'type' => 'library',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'dev_requirement' => false,
),
'facebook/webdriver' => array(
'dev_requirement' => false,
'replaced' => array(
0 => '*',
),
),
'php-webdriver/webdriver' => array(
'pretty_version' => '1.12.1',
'version' => '1.12.1.0',
'reference' => 'b27ddf458d273c7d4602106fcaf978aa0b7fe15a',
'type' => 'library',
'install_path' => __DIR__ . '/../php-webdriver/webdriver',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-mbstring' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => '9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-mbstring',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php80' => array(
'pretty_version' => 'v1.26.0',
'version' => '1.26.0.0',
'reference' => 'cfa0ae98841b9e461207c13ab093d76b0fa7bace',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php80',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/process' => array(
'pretty_version' => 'v5.4.8',
'version' => '5.4.8.0',
'reference' => '597f3fff8e3e91836bb0bd38f5718b56ddbde2f3',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/process',
'aliases' => array(),
'dev_requirement' => false,
),
),
);
@@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 70205)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 7.2.5". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}
@@ -0,0 +1,117 @@
<?php
$finder = PhpCsFixer\Finder::create()
->in([__DIR__ . '/lib', __DIR__ . '/tests']);
return (new PhpCsFixer\Config())
->setRules([
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'binary_operator_spaces' => true,
'blank_line_before_statement' => ['statements' => ['return', 'try']],
'braces' => ['allow_single_line_anonymous_class_with_empty_body' => true, 'allow_single_line_closure' => true],
'cast_spaces' => true,
'class_attributes_separation' => ['elements' => ['method' => 'one']],
'clean_namespace' => true,
'compact_nullable_typehint' => true,
'concat_space' => ['spacing' => 'one'],
'declare_equal_normalize' => true,
'fopen_flag_order' => true,
'fopen_flags' => true,
'full_opening_tag' => true,
'function_typehint_space' => true,
'implode_call' => true,
'is_null' => true,
'lambda_not_used_import' => true,
'linebreak_after_opening_tag' => true,
'lowercase_cast' => true,
'lowercase_static_reference' => true,
'magic_constant_casing' => true,
'magic_method_casing' => true,
'mb_str_functions' => true,
'native_function_casing' => true,
'native_function_type_declaration_casing' => true,
'new_with_braces' => true,
'no_alias_functions' => true,
'no_blank_lines_after_class_opening' => true,
'no_blank_lines_after_phpdoc' => true,
'no_empty_comment' => true,
'no_empty_phpdoc' => true,
'no_empty_statement' => true,
'normalize_index_brace' => true,
'no_extra_blank_lines' => [
'tokens' => [
'break',
'case',
'continue',
'curly_brace_block',
'default',
'extra',
'parenthesis_brace_block',
'return',
'square_brace_block',
'switch',
'throw',
'use',
'use_trait',
],
],
'no_leading_import_slash' => true,
'no_leading_namespace_whitespace' => true,
'no_singleline_whitespace_before_semicolons' => true,
'no_trailing_comma_in_singleline_array' => true,
'no_unreachable_default_argument_value' => true,
'no_unused_imports' => true,
'no_useless_else' => true,
'no_useless_return' => true,
'no_useless_sprintf' => true,
'no_whitespace_in_blank_line' => true,
'object_operator_without_whitespace' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'php_unit_construct' => true,
'php_unit_dedicate_assert' => false,
'php_unit_expectation' => ['target' => '5.6'],
'php_unit_method_casing' => ['case' => 'camel_case'],
'php_unit_mock_short_will_return' => true,
'php_unit_mock' => true,
'php_unit_namespaced' => ['target' => '5.7'],
'php_unit_no_expectation_annotation' => true,
'php_unit_set_up_tear_down_visibility' => true,
'php_unit_test_case_static_method_calls' => ['call_type' => 'this'],
'phpdoc_add_missing_param_annotation' => true,
'phpdoc_indent' => true,
'phpdoc_no_access' => true,
// 'phpdoc_no_empty_return' => true, // disabled to allow forward compatibility with PHP 8.1
'phpdoc_no_package' => true,
'phpdoc_order_by_value' => ['annotations' => ['covers', 'group', 'throws']],
'phpdoc_order' => true,
'phpdoc_return_self_reference' => true,
'phpdoc_scalar' => true,
'phpdoc_single_line_var_spacing' => true,
'phpdoc_trim' => true,
'phpdoc_types' => true,
'phpdoc_var_annotation_correct_order' => true,
'psr_autoloading' => true,
'self_accessor' => true,
'set_type_to_cast' => true,
'short_scalar_cast' => true,
'single_blank_line_before_namespace' => true,
'single_quote' => true,
'single_space_after_construct' => true,
'single_trait_insert_per_statement' => true,
'space_after_semicolon' => true,
'standardize_not_equals' => true,
'strict_param' => true,
'switch_continue_to_break' => true,
'ternary_operator_spaces' => true,
'ternary_to_elvis_operator' => true,
'trailing_comma_in_multiline' => ['elements' => ['arrays']],
'trim_array_spaces' => true,
'unary_operator_spaces' => true,
'visibility_required' => ['elements' => ['method', 'property']],
'whitespace_after_comma_in_array' => true,
'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false],
])
->setRiskyAllowed(true)
->setFinder($finder);
@@ -0,0 +1,99 @@
{
"name": "php-webdriver/webdriver",
"description": "A PHP client for Selenium WebDriver. Previously facebook/webdriver.",
"license": "MIT",
"type": "library",
"keywords": [
"webdriver",
"selenium",
"php",
"geckodriver",
"chromedriver"
],
"homepage": "https://github.com/php-webdriver/php-webdriver",
"require": {
"php": "^5.6 || ~7.0 || ^8.0",
"ext-curl": "*",
"ext-json": "*",
"ext-zip": "*",
"symfony/polyfill-mbstring": "^1.12",
"symfony/process": "^2.8 || ^3.1 || ^4.0 || ^5.0 || ^6.0"
},
"require-dev": {
"ondram/ci-detector": "^2.1 || ^3.5 || ^4.0",
"php-coveralls/php-coveralls": "^2.4",
"php-mock/php-mock-phpunit": "^1.1 || ^2.0",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpunit/phpunit": "^5.7 || ^7 || ^8 || ^9",
"squizlabs/php_codesniffer": "^3.5",
"symfony/var-dumper": "^3.3 || ^4.0 || ^5.0 || ^6.0"
},
"replace": {
"facebook/webdriver": "*"
},
"suggest": {
"ext-SimpleXML": "For Firefox profile creation"
},
"minimum-stability": "dev",
"autoload": {
"psr-4": {
"Facebook\\WebDriver\\": "lib/"
},
"files": [
"lib/Exception/TimeoutException.php"
]
},
"autoload-dev": {
"psr-4": {
"Facebook\\WebDriver\\": [
"tests/unit",
"tests/functional"
]
},
"classmap": [
"tests/functional/"
]
},
"config": {
"allow-plugins": {
"ergebnis/composer-normalize": true
},
"sort-packages": true
},
"scripts": {
"post-install-cmd": [
"php -r 'if (PHP_VERSION_ID > 70103) { exit(1); }' || composer install --working-dir=tools/php-cs-fixer --no-progress --no-interaction"
],
"post-update-cmd": [
"php -r 'if (PHP_VERSION_ID > 70103) { exit(1); }' || composer update --working-dir=tools/php-cs-fixer --no-progress --no-interaction"
],
"all": [
"@lint",
"@analyze",
"@test"
],
"analyze": [
"vendor/bin/phpstan analyze -c phpstan.neon --ansi",
"tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --diff --dry-run -vvv --ansi",
"vendor/bin/phpcs --standard=PSR2 ./lib/ ./tests/"
],
"fix": [
"@composer normalize",
"tools/php-cs-fixer/vendor/bin/php-cs-fixer fix --diff -vvv || exit 0",
"vendor/bin/phpcbf --standard=PSR2 ./lib/ ./tests/"
],
"lint": [
"vendor/bin/parallel-lint -j 10 ./lib ./tests example.php",
"@composer validate",
"@composer normalize --dry-run"
],
"preinstall": [
"@composer update --no-progress --no-interaction",
"@composer require --dev phpstan/phpstan",
"@composer require --dev ergebnis/composer-normalize"
],
"test": [
"vendor/bin/phpunit --colors=always"
]
}
}
@@ -0,0 +1,240 @@
<?php
namespace Facebook\WebDriver;
use Facebook\WebDriver\Exception\NoSuchElementException;
use Facebook\WebDriver\Exception\UnexpectedTagNameException;
use Facebook\WebDriver\Exception\WebDriverException;
use Facebook\WebDriver\Support\XPathEscaper;
/**
* Provides helper methods for checkboxes and radio buttons.
*/
abstract class AbstractWebDriverCheckboxOrRadio implements WebDriverSelectInterface
{
/** @var WebDriverElement */
protected $element;
/** @var string */
protected $type;
/** @var string */
protected $name;
public function __construct(WebDriverElement $element)
{
$tagName = $element->getTagName();
if ($tagName !== 'input') {
throw new UnexpectedTagNameException('input', $tagName);
}
$this->name = $element->getAttribute('name');
if ($this->name === null) {
throw new WebDriverException('The input does not have a "name" attribute.');
}
$this->element = $element;
}
public function getOptions()
{
return $this->getRelatedElements();
}
public function getAllSelectedOptions()
{
$selectedElement = [];
foreach ($this->getRelatedElements() as $element) {
if ($element->isSelected()) {
$selectedElement[] = $element;
if (!$this->isMultiple()) {
return $selectedElement;
}
}
}
return $selectedElement;
}
public function getFirstSelectedOption()
{
foreach ($this->getRelatedElements() as $element) {
if ($element->isSelected()) {
return $element;
}
}
throw new NoSuchElementException(
sprintf('No %s are selected', $this->type === 'radio' ? 'radio buttons' : 'checkboxes')
);
}
public function selectByIndex($index)
{
$this->byIndex($index);
}
public function selectByValue($value)
{
$this->byValue($value);
}
public function selectByVisibleText($text)
{
$this->byVisibleText($text);
}
public function selectByVisiblePartialText($text)
{
$this->byVisibleText($text, true);
}
/**
* Selects or deselects a checkbox or a radio button by its value.
*
* @param string $value
* @param bool $select
* @throws NoSuchElementException
*/
protected function byValue($value, $select = true)
{
$matched = false;
foreach ($this->getRelatedElements($value) as $element) {
$select ? $this->selectOption($element) : $this->deselectOption($element);
if (!$this->isMultiple()) {
return;
}
$matched = true;
}
if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate %s with value: %s', $this->type, $value)
);
}
}
/**
* Selects or deselects a checkbox or a radio button by its index.
*
* @param int $index
* @param bool $select
* @throws NoSuchElementException
*/
protected function byIndex($index, $select = true)
{
$elements = $this->getRelatedElements();
if (!isset($elements[$index])) {
throw new NoSuchElementException(sprintf('Cannot locate %s with index: %d', $this->type, $index));
}
$select ? $this->selectOption($elements[$index]) : $this->deselectOption($elements[$index]);
}
/**
* Selects or deselects a checkbox or a radio button by its visible text.
*
* @param string $text
* @param bool $partial
* @param bool $select
*/
protected function byVisibleText($text, $partial = false, $select = true)
{
foreach ($this->getRelatedElements() as $element) {
$normalizeFilter = sprintf(
$partial ? 'contains(normalize-space(.), %s)' : 'normalize-space(.) = %s',
XPathEscaper::escapeQuotes($text)
);
$xpath = 'ancestor::label';
$xpathNormalize = sprintf('%s[%s]', $xpath, $normalizeFilter);
$id = $element->getAttribute('id');
if ($id !== null) {
$idFilter = sprintf('@for = %s', XPathEscaper::escapeQuotes($id));
$xpath .= sprintf(' | //label[%s]', $idFilter);
$xpathNormalize .= sprintf(' | //label[%s and %s]', $idFilter, $normalizeFilter);
}
try {
$element->findElement(WebDriverBy::xpath($xpathNormalize));
} catch (NoSuchElementException $e) {
if ($partial) {
continue;
}
try {
// Since the mechanism of getting the text in xpath is not the same as
// webdriver, use the expensive getText() to check if nothing is matched.
if ($text !== $element->findElement(WebDriverBy::xpath($xpath))->getText()) {
continue;
}
} catch (NoSuchElementException $e) {
continue;
}
}
$select ? $this->selectOption($element) : $this->deselectOption($element);
if (!$this->isMultiple()) {
return;
}
}
}
/**
* Gets checkboxes or radio buttons with the same name.
*
* @param string|null $value
* @return WebDriverElement[]
*/
protected function getRelatedElements($value = null)
{
$valueSelector = $value ? sprintf(' and @value = %s', XPathEscaper::escapeQuotes($value)) : '';
$formId = $this->element->getAttribute('form');
if ($formId === null) {
$form = $this->element->findElement(WebDriverBy::xpath('ancestor::form'));
$formId = $form->getAttribute('id');
if ($formId === '' || $formId === null) {
return $form->findElements(WebDriverBy::xpath(
sprintf('.//input[@name = %s%s]', XPathEscaper::escapeQuotes($this->name), $valueSelector)
));
}
}
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form
return $this->element->findElements(
WebDriverBy::xpath(sprintf(
'//form[@id = %1$s]//input[@name = %2$s%3$s'
. ' and ((boolean(@form) = true() and @form = %1$s) or boolean(@form) = false())]'
. ' | //input[@form = %1$s and @name = %2$s%3$s]',
XPathEscaper::escapeQuotes($formId),
XPathEscaper::escapeQuotes($this->name),
$valueSelector
))
);
}
/**
* Selects a checkbox or a radio button.
*/
protected function selectOption(WebDriverElement $element)
{
if (!$element->isSelected()) {
$element->click();
}
}
/**
* Deselects a checkbox or a radio button.
*/
protected function deselectOption(WebDriverElement $element)
{
if ($element->isSelected()) {
$element->click();
}
}
}
@@ -0,0 +1,46 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Remote\RemoteWebDriver;
/**
* Provide access to Chrome DevTools Protocol (CDP) commands via HTTP endpoint of Chromedriver.
*
* @see https://chromedevtools.github.io/devtools-protocol/
*/
class ChromeDevToolsDriver
{
const SEND_COMMAND = [
'method' => 'POST',
'url' => '/session/:sessionId/goog/cdp/execute',
];
/**
* @var RemoteWebDriver
*/
private $driver;
public function __construct(RemoteWebDriver $driver)
{
$this->driver = $driver;
}
/**
* Executes a Chrome DevTools command
*
* @param string $command The DevTools command to execute
* @param array $parameters Optional parameters to the command
* @return array The result of the command
*/
public function execute($command, array $parameters = [])
{
$params = ['cmd' => $command, 'params' => (object) $parameters];
return $this->driver->executeCustomCommand(
self::SEND_COMMAND['url'],
self::SEND_COMMAND['method'],
$params
);
}
}
@@ -0,0 +1,105 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Local\LocalWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\Service\DriverCommandExecutor;
use Facebook\WebDriver\Remote\WebDriverCommand;
class ChromeDriver extends LocalWebDriver
{
/** @var ChromeDevToolsDriver */
private $devTools;
/**
* Creates a new ChromeDriver using default configuration.
* This includes starting a new chromedriver process each time this method is called. However this may be
* unnecessary overhead - instead, you can start the process once using ChromeDriverService and pass
* this instance to startUsingDriverService() method.
*
* @todo Remove $service parameter. Use `ChromeDriver::startUsingDriverService` to pass custom $service instance.
* @return static
*/
public static function start(DesiredCapabilities $desired_capabilities = null, ChromeDriverService $service = null)
{
if ($service === null) { // TODO: Remove the condition (always create default service)
$service = ChromeDriverService::createDefaultService();
}
return static::startUsingDriverService($service, $desired_capabilities);
}
/**
* Creates a new ChromeDriver using given ChromeDriverService.
* This is usable when you for example don't want to start new chromedriver process for each individual test
* and want to reuse the already started chromedriver, which will lower the overhead associated with spinning up
* a new process.
* @return static
*/
public static function startUsingDriverService(
ChromeDriverService $service,
DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::chrome();
}
$executor = new DriverCommandExecutor($service);
$newSessionCommand = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()],
],
'desiredCapabilities' => (object) $capabilities->toArray(),
]
);
$response = $executor->execute($newSessionCommand);
/*
* TODO: in next major version we may not need to use this method, because without OSS compatibility the
* driver creation is straightforward.
*/
return static::createFromResponse($response, $executor);
}
/**
* @todo Remove in next major version. The class is internally no longer used and is kept only to keep BC.
* @deprecated Use start or startUsingDriverService method instead.
* @codeCoverageIgnore
* @internal
*/
public function startSession(DesiredCapabilities $desired_capabilities)
{
$command = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $desired_capabilities->toW3cCompatibleArray()],
],
'desiredCapabilities' => (object) $desired_capabilities->toArray(),
]
);
$response = $this->executor->execute($command);
$value = $response->getValue();
if (!$this->isW3cCompliant = isset($value['capabilities'])) {
$this->executor->disableW3cCompliance();
}
$this->sessionID = $response->getSessionID();
}
/**
* @return ChromeDevToolsDriver
*/
public function getDevTools()
{
if ($this->devTools === null) {
$this->devTools = new ChromeDevToolsDriver($this);
}
return $this->devTools;
}
}
@@ -0,0 +1,37 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Remote\Service\DriverService;
class ChromeDriverService extends DriverService
{
/**
* The environment variable storing the path to the chrome driver executable.
* @deprecated Use ChromeDriverService::CHROME_DRIVER_EXECUTABLE
*/
const CHROME_DRIVER_EXE_PROPERTY = 'webdriver.chrome.driver';
/** @var string The environment variable storing the path to the chrome driver executable */
const CHROME_DRIVER_EXECUTABLE = 'WEBDRIVER_CHROME_DRIVER';
/**
* @var string Default executable used when no other is provided
* @internal
*/
const DEFAULT_EXECUTABLE = 'chromedriver';
/**
* @return static
*/
public static function createDefaultService()
{
$pathToExecutable = getenv(self::CHROME_DRIVER_EXECUTABLE) ?: getenv(self::CHROME_DRIVER_EXE_PROPERTY);
if ($pathToExecutable === false || $pathToExecutable === '') {
$pathToExecutable = static::DEFAULT_EXECUTABLE;
}
$port = 9515; // TODO: Get another port if the default port is used.
$args = ['--port=' . $port];
return new static($pathToExecutable, $port, $args);
}
}
@@ -0,0 +1,182 @@
<?php
namespace Facebook\WebDriver\Chrome;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use JsonSerializable;
use ReturnTypeWillChange;
/**
* The class manages the capabilities in ChromeDriver.
*
* @see https://sites.google.com/a/chromium.org/chromedriver/capabilities
*/
class ChromeOptions implements JsonSerializable
{
/**
* The key of chromeOptions in desired capabilities (in legacy OSS JsonWire protocol)
* @todo Replace value with 'goog:chromeOptions' after JsonWire protocol support is removed
*/
const CAPABILITY = 'chromeOptions';
/**
* The key of chromeOptions in desired capabilities (in W3C compatible protocol)
*/
const CAPABILITY_W3C = 'goog:chromeOptions';
/**
* @var array
*/
private $arguments = [];
/**
* @var string
*/
private $binary = '';
/**
* @var array
*/
private $extensions = [];
/**
* @var array
*/
private $experimentalOptions = [];
/**
* Return a version of the class which can JSON serialized.
*
* @return array
*/
#[ReturnTypeWillChange]
public function jsonSerialize()
{
return $this->toArray();
}
/**
* Sets the path of the Chrome executable. The path should be either absolute
* or relative to the location running ChromeDriver server.
*
* @param string $path
* @return ChromeOptions
*/
public function setBinary($path)
{
$this->binary = $path;
return $this;
}
/**
* @param array $arguments
* @return ChromeOptions
*/
public function addArguments(array $arguments)
{
$this->arguments = array_merge($this->arguments, $arguments);
return $this;
}
/**
* Add a Chrome extension to install on browser startup. Each path should be
* a packed Chrome extension.
*
* @param array $paths
* @return ChromeOptions
*/
public function addExtensions(array $paths)
{
foreach ($paths as $path) {
$this->addExtension($path);
}
return $this;
}
/**
* @param array $encoded_extensions An array of base64 encoded of the extensions.
* @return ChromeOptions
*/
public function addEncodedExtensions(array $encoded_extensions)
{
foreach ($encoded_extensions as $encoded_extension) {
$this->addEncodedExtension($encoded_extension);
}
return $this;
}
/**
* Sets an experimental option which has not exposed officially.
*
* @param string $name
* @param mixed $value
* @return ChromeOptions
*/
public function setExperimentalOption($name, $value)
{
$this->experimentalOptions[$name] = $value;
return $this;
}
/**
* @return DesiredCapabilities The DesiredCapabilities for Chrome with this options.
*/
public function toCapabilities()
{
$capabilities = DesiredCapabilities::chrome();
$capabilities->setCapability(self::CAPABILITY, $this);
return $capabilities;
}
/**
* @return \ArrayObject|array
*/
public function toArray()
{
// The selenium server expects a 'dictionary' instead of a 'list' when
// reading the chrome option. However, an empty array in PHP will be
// converted to a 'list' instead of a 'dictionary'. To fix it, we work
// with `ArrayObject`
$options = new \ArrayObject($this->experimentalOptions);
if (!empty($this->binary)) {
$options['binary'] = $this->binary;
}
if (!empty($this->arguments)) {
$options['args'] = $this->arguments;
}
if (!empty($this->extensions)) {
$options['extensions'] = $this->extensions;
}
return $options;
}
/**
* Add a Chrome extension to install on browser startup. Each path should be a
* packed Chrome extension.
*
* @param string $path
* @return ChromeOptions
*/
private function addExtension($path)
{
$this->addEncodedExtension(base64_encode(file_get_contents($path)));
return $this;
}
/**
* @param string $encoded_extension Base64 encoded of the extension.
* @return ChromeOptions
*/
private function addEncodedExtension($encoded_extension)
{
$this->extensions[] = $encoded_extension;
return $this;
}
}
@@ -0,0 +1,278 @@
<?php
namespace Facebook\WebDriver;
use InvalidArgumentException;
/**
* Set values of an cookie.
*
* Implements ArrayAccess for backwards compatibility.
*
* @see https://w3c.github.io/webdriver/webdriver-spec.html#cookies
*/
class Cookie implements \ArrayAccess
{
/** @var array */
protected $cookie = [];
/**
* @param string $name The name of the cookie; may not be null or an empty string.
* @param string $value The cookie value; may not be null.
*/
public function __construct($name, $value)
{
$this->validateCookieName($name);
$this->validateCookieValue($value);
$this->cookie['name'] = $name;
$this->cookie['value'] = $value;
}
/**
* @param array $cookieArray The cookie fields; must contain name and value.
* @return Cookie
*/
public static function createFromArray(array $cookieArray)
{
if (!isset($cookieArray['name'])) {
throw new InvalidArgumentException('Cookie name should be set');
}
if (!isset($cookieArray['value'])) {
throw new InvalidArgumentException('Cookie value should be set');
}
$cookie = new self($cookieArray['name'], $cookieArray['value']);
if (isset($cookieArray['path'])) {
$cookie->setPath($cookieArray['path']);
}
if (isset($cookieArray['domain'])) {
$cookie->setDomain($cookieArray['domain']);
}
if (isset($cookieArray['expiry'])) {
$cookie->setExpiry($cookieArray['expiry']);
}
if (isset($cookieArray['secure'])) {
$cookie->setSecure($cookieArray['secure']);
}
if (isset($cookieArray['httpOnly'])) {
$cookie->setHttpOnly($cookieArray['httpOnly']);
}
if (isset($cookieArray['sameSite'])) {
$cookie->setSameSite($cookieArray['sameSite']);
}
return $cookie;
}
/**
* @return string
*/
public function getName()
{
return $this->offsetGet('name');
}
/**
* @return string
*/
public function getValue()
{
return $this->offsetGet('value');
}
/**
* The path the cookie is visible to. Defaults to "/" if omitted.
*
* @param string $path
*/
public function setPath($path)
{
$this->offsetSet('path', $path);
}
/**
* @return string|null
*/
public function getPath()
{
return $this->offsetGet('path');
}
/**
* The domain the cookie is visible to. Defaults to the current browsing context's document's URL domain if omitted.
*
* @param string $domain
*/
public function setDomain($domain)
{
if (mb_strpos($domain, ':') !== false) {
throw new InvalidArgumentException(sprintf('Cookie domain "%s" should not contain a port', $domain));
}
$this->offsetSet('domain', $domain);
}
/**
* @return string|null
*/
public function getDomain()
{
return $this->offsetGet('domain');
}
/**
* The cookie's expiration date, specified in seconds since Unix Epoch.
*
* @param int $expiry
*/
public function setExpiry($expiry)
{
$this->offsetSet('expiry', (int) $expiry);
}
/**
* @return int|null
*/
public function getExpiry()
{
return $this->offsetGet('expiry');
}
/**
* Whether this cookie requires a secure connection (https). Defaults to false if omitted.
*
* @param bool $secure
*/
public function setSecure($secure)
{
$this->offsetSet('secure', $secure);
}
/**
* @return bool|null
*/
public function isSecure()
{
return $this->offsetGet('secure');
}
/**
* Whether the cookie is an HTTP only cookie. Defaults to false if omitted.
*
* @param bool $httpOnly
*/
public function setHttpOnly($httpOnly)
{
$this->offsetSet('httpOnly', $httpOnly);
}
/**
* @return bool|null
*/
public function isHttpOnly()
{
return $this->offsetGet('httpOnly');
}
/**
* The cookie's same-site value.
*
* @param string $sameSite
*/
public function setSameSite($sameSite)
{
$this->offsetSet('sameSite', $sameSite);
}
/**
* @return string|null
*/
public function getSameSite()
{
return $this->offsetGet('sameSite');
}
/**
* @return array
*/
public function toArray()
{
$cookie = $this->cookie;
if (!isset($cookie['secure'])) {
// Passing a boolean value for the "secure" flag is mandatory when using geckodriver
$cookie['secure'] = false;
}
return $cookie;
}
/**
* @param mixed $offset
* @return bool
*/
#[\ReturnTypeWillChange]
public function offsetExists($offset)
{
return isset($this->cookie[$offset]);
}
/**
* @param mixed $offset
* @return mixed
*/
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->cookie[$offset] : null;
}
/**
* @param mixed $offset
* @param mixed $value
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetSet($offset, $value)
{
if ($value === null) {
unset($this->cookie[$offset]);
} else {
$this->cookie[$offset] = $value;
}
}
/**
* @param mixed $offset
* @return void
*/
#[\ReturnTypeWillChange]
public function offsetUnset($offset)
{
unset($this->cookie[$offset]);
}
/**
* @param string $name
*/
protected function validateCookieName($name)
{
if ($name === null || $name === '') {
throw new InvalidArgumentException('Cookie name should be non-empty');
}
if (mb_strpos($name, ';') !== false) {
throw new InvalidArgumentException('Cookie name should not contain a ";"');
}
}
/**
* @param string $value
*/
protected function validateCookieValue($value)
{
if ($value === null) {
throw new InvalidArgumentException('Cookie value is required when setting a cookie');
}
}
}
@@ -0,0 +1,15 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The driver server process is unexpectedly no longer available.
*/
class DriverServerDiedException extends WebDriverException
{
public function __construct(\Exception $previous = null)
{
parent::__construct('The driver server has died.');
\Exception::__construct($this->getMessage(), $this->getCode(), $previous);
}
}
@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The Element Click command could not be completed because the element receiving the events is obscuring the element
* that was requested clicked.
*/
class ElementClickInterceptedException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command could not be completed because the element is not pointer- or keyboard interactable.
*/
class ElementNotInteractableException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\ElementNotInteractableException
*/
class ElementNotSelectableException extends ElementNotInteractableException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class ElementNotVisibleException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class ExpectedException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class IMEEngineActivationFailedException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class IMENotAvailableException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class IndexOutOfBoundsException extends WebDriverException
{
}
@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Navigation caused the user agent to hit a certificate warning, which is usually the result of an expired
* or invalid TLS certificate.
*/
class InsecureCertificateException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The arguments passed to a command are either invalid or malformed.
*/
class InvalidArgumentException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An illegal attempt was made to set a cookie under a different domain than the current page.
*/
class InvalidCookieDomainException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class InvalidCoordinatesException extends WebDriverException
{
}
@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command could not be completed because the element is in an invalid state, e.g. attempting to clear an element
* that isn’t both editable and resettable.
*/
class InvalidElementStateException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Argument was an invalid selector.
*/
class InvalidSelectorException extends WebDriverException
{
}
@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Occurs if the given session id is not in the list of active sessions, meaning the session either does not exist
* or that it’s not active.
*/
class InvalidSessionIdException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An error occurred while executing JavaScript supplied by the user.
*/
class JavascriptErrorException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The target for mouse interaction is not in the browser’s viewport and cannot be brought into that viewport.
*/
class MoveTargetOutOfBoundsException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\NoSuchAlertException
*/
class NoAlertOpenException extends NoSuchAlertException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoCollectionException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoScriptResultException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoStringException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoStringLengthException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoStringWrapperException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An attempt was made to operate on a modal dialog when one was not open.
*/
class NoSuchAlertException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoSuchCollectionException extends WebDriverException
{
}
@@ -0,0 +1,11 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* No cookie matching the given path name was found amongst the associated cookies of the current browsing context’s
* active document.
*/
class NoSuchCookieException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\NoSuchWindowException
*/
class NoSuchDocumentException extends NoSuchWindowException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NoSuchDriverException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An element could not be located on the page using the given search parameters.
*/
class NoSuchElementException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command to switch to a frame could not be satisfied because the frame could not be found.
*/
class NoSuchFrameException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command to switch to a window could not be satisfied because the window could not be found.
*/
class NoSuchWindowException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class NullPointerException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A script did not complete before its timeout expired.
*/
class ScriptTimeoutException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A new session could not be created.
*/
class SessionNotCreatedException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command failed because the referenced element is no longer attached to the DOM.
*/
class StaleElementReferenceException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An operation did not complete before its timeout expired.
*/
class TimeoutException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A screen capture was made impossible.
*/
class UnableToCaptureScreenException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command to set a cookie’s value could not be satisfied.
*/
class UnableToSetCookieException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A modal dialog was open, blocking this operation.
*/
class UnexpectedAlertOpenException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\JavascriptErrorException
*/
class UnexpectedJavascriptException extends JavascriptErrorException
{
}
@@ -0,0 +1,23 @@
<?php
namespace Facebook\WebDriver\Exception;
class UnexpectedTagNameException extends WebDriverException
{
/**
* @param string $expected_tag_name
* @param string $actual_tag_name
*/
public function __construct(
$expected_tag_name,
$actual_tag_name
) {
parent::__construct(
sprintf(
'Element should have been "%s" but was "%s"',
$expected_tag_name,
$actual_tag_name
)
);
}
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* A command could not be executed because the remote end is not aware of it.
*/
class UnknownCommandException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* An unknown error occurred in the remote end while processing the command.
*/
class UnknownErrorException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* The requested command matched a known URL but did not match an method for that URL.
*/
class UnknownMethodException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Use Facebook\WebDriver\Exception\UnknownErrorException
*/
class UnknownServerException extends UnknownErrorException
{
}
@@ -0,0 +1,7 @@
<?php
namespace Facebook\WebDriver\Exception;
class UnrecognizedExceptionException extends WebDriverException
{
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* Indicates that a command that should have executed properly cannot be supported for some reason.
*/
class UnsupportedOperationException extends WebDriverException
{
}
@@ -0,0 +1,7 @@
<?php
namespace Facebook\WebDriver\Exception;
class WebDriverCurlException extends WebDriverException
{
}
@@ -0,0 +1,222 @@
<?php
namespace Facebook\WebDriver\Exception;
use Exception;
/**
* @see https://w3c.github.io/webdriver/#errors
*/
class WebDriverException extends Exception
{
private $results;
/**
* @param string $message
* @param mixed $results
*/
public function __construct($message, $results = null)
{
parent::__construct($message);
$this->results = $results;
}
/**
* @return mixed
*/
public function getResults()
{
return $this->results;
}
/**
* Throw WebDriverExceptions based on WebDriver status code.
*
* @param int|string $status_code
* @param string $message
* @param mixed $results
*
* @throws ElementClickInterceptedException
* @throws ElementNotInteractableException
* @throws ElementNotSelectableException
* @throws ElementNotVisibleException
* @throws ExpectedException
* @throws IMEEngineActivationFailedException
* @throws IMENotAvailableException
* @throws IndexOutOfBoundsException
* @throws InsecureCertificateException
* @throws InvalidArgumentException
* @throws InvalidCookieDomainException
* @throws InvalidCoordinatesException
* @throws InvalidElementStateException
* @throws InvalidSelectorException
* @throws InvalidSessionIdException
* @throws JavascriptErrorException
* @throws MoveTargetOutOfBoundsException
* @throws NoAlertOpenException
* @throws NoCollectionException
* @throws NoScriptResultException
* @throws NoStringException
* @throws NoStringLengthException
* @throws NoStringWrapperException
* @throws NoSuchAlertException
* @throws NoSuchCollectionException
* @throws NoSuchCookieException
* @throws NoSuchDocumentException
* @throws NoSuchDriverException
* @throws NoSuchElementException
* @throws NoSuchFrameException
* @throws NoSuchWindowException
* @throws NullPointerException
* @throws ScriptTimeoutException
* @throws SessionNotCreatedException
* @throws StaleElementReferenceException
* @throws TimeoutException
* @throws UnableToCaptureScreenException
* @throws UnableToSetCookieException
* @throws UnexpectedAlertOpenException
* @throws UnexpectedJavascriptException
* @throws UnknownCommandException
* @throws UnknownErrorException
* @throws UnknownMethodException
* @throws UnknownServerException
* @throws UnrecognizedExceptionException
* @throws UnsupportedOperationException
* @throws XPathLookupException
*/
public static function throwException($status_code, $message, $results)
{
if (is_string($status_code)) {
// @see https://w3c.github.io/webdriver/#errors
switch ($status_code) {
case 'element click intercepted':
throw new ElementClickInterceptedException($message, $results);
case 'element not interactable':
throw new ElementNotInteractableException($message, $results);
case 'insecure certificate':
throw new InsecureCertificateException($message, $results);
case 'invalid argument':
throw new InvalidArgumentException($message, $results);
case 'invalid cookie domain':
throw new InvalidCookieDomainException($message, $results);
case 'invalid element state':
throw new InvalidElementStateException($message, $results);
case 'invalid selector':
throw new InvalidSelectorException($message, $results);
case 'invalid session id':
throw new InvalidSessionIdException($message, $results);
case 'javascript error':
throw new JavascriptErrorException($message, $results);
case 'move target out of bounds':
throw new MoveTargetOutOfBoundsException($message, $results);
case 'no such alert':
throw new NoSuchAlertException($message, $results);
case 'no such cookie':
throw new NoSuchCookieException($message, $results);
case 'no such element':
throw new NoSuchElementException($message, $results);
case 'no such frame':
throw new NoSuchFrameException($message, $results);
case 'no such window':
throw new NoSuchWindowException($message, $results);
case 'script timeout':
throw new ScriptTimeoutException($message, $results);
case 'session not created':
throw new SessionNotCreatedException($message, $results);
case 'stale element reference':
throw new StaleElementReferenceException($message, $results);
case 'timeout':
throw new TimeoutException($message, $results);
case 'unable to set cookie':
throw new UnableToSetCookieException($message, $results);
case 'unable to capture screen':
throw new UnableToCaptureScreenException($message, $results);
case 'unexpected alert open':
throw new UnexpectedAlertOpenException($message, $results);
case 'unknown command':
throw new UnknownCommandException($message, $results);
case 'unknown error':
throw new UnknownErrorException($message, $results);
case 'unknown method':
throw new UnknownMethodException($message, $results);
case 'unsupported operation':
throw new UnsupportedOperationException($message, $results);
default:
throw new UnrecognizedExceptionException($message, $results);
}
}
switch ($status_code) {
case 1:
throw new IndexOutOfBoundsException($message, $results);
case 2:
throw new NoCollectionException($message, $results);
case 3:
throw new NoStringException($message, $results);
case 4:
throw new NoStringLengthException($message, $results);
case 5:
throw new NoStringWrapperException($message, $results);
case 6:
throw new NoSuchDriverException($message, $results);
case 7:
throw new NoSuchElementException($message, $results);
case 8:
throw new NoSuchFrameException($message, $results);
case 9:
throw new UnknownCommandException($message, $results);
case 10:
throw new StaleElementReferenceException($message, $results);
case 11:
throw new ElementNotVisibleException($message, $results);
case 12:
throw new InvalidElementStateException($message, $results);
case 13:
throw new UnknownServerException($message, $results);
case 14:
throw new ExpectedException($message, $results);
case 15:
throw new ElementNotSelectableException($message, $results);
case 16:
throw new NoSuchDocumentException($message, $results);
case 17:
throw new UnexpectedJavascriptException($message, $results);
case 18:
throw new NoScriptResultException($message, $results);
case 19:
throw new XPathLookupException($message, $results);
case 20:
throw new NoSuchCollectionException($message, $results);
case 21:
throw new TimeoutException($message, $results);
case 22:
throw new NullPointerException($message, $results);
case 23:
throw new NoSuchWindowException($message, $results);
case 24:
throw new InvalidCookieDomainException($message, $results);
case 25:
throw new UnableToSetCookieException($message, $results);
case 26:
throw new UnexpectedAlertOpenException($message, $results);
case 27:
throw new NoAlertOpenException($message, $results);
case 28:
throw new ScriptTimeoutException($message, $results);
case 29:
throw new InvalidCoordinatesException($message, $results);
case 30:
throw new IMENotAvailableException($message, $results);
case 31:
throw new IMEEngineActivationFailedException($message, $results);
case 32:
throw new InvalidSelectorException($message, $results);
case 33:
throw new SessionNotCreatedException($message, $results);
case 34:
throw new MoveTargetOutOfBoundsException($message, $results);
default:
throw new UnrecognizedExceptionException($message, $results);
}
}
}
@@ -0,0 +1,10 @@
<?php
namespace Facebook\WebDriver\Exception;
/**
* @deprecated Removed in W3C WebDriver, see https://github.com/php-webdriver/php-webdriver/pull/686
*/
class XPathLookupException extends WebDriverException
{
}
@@ -0,0 +1,61 @@
<?php
namespace Facebook\WebDriver\Firefox;
use Facebook\WebDriver\Local\LocalWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\Service\DriverCommandExecutor;
use Facebook\WebDriver\Remote\WebDriverCommand;
class FirefoxDriver extends LocalWebDriver
{
const PROFILE = 'firefox_profile';
/**
* Creates a new FirefoxDriver using default configuration.
* This includes starting a new geckodriver process each time this method is called. However this may be
* unnecessary overhead - instead, you can start the process once using FirefoxDriverService and pass
* this instance to startUsingDriverService() method.
*
* @return static
*/
public static function start(DesiredCapabilities $capabilities = null)
{
$service = FirefoxDriverService::createDefaultService();
return static::startUsingDriverService($service, $capabilities);
}
/**
* Creates a new FirefoxDriver using given FirefoxDriverService.
* This is usable when you for example don't want to start new geckodriver process for each individual test
* and want to reuse the already started geckodriver, which will lower the overhead associated with spinning up
* a new process.
*
* @return static
*/
public static function startUsingDriverService(
FirefoxDriverService $service,
DesiredCapabilities $capabilities = null
) {
if ($capabilities === null) {
$capabilities = DesiredCapabilities::firefox();
}
$executor = new DriverCommandExecutor($service);
$newSessionCommand = WebDriverCommand::newSession(
[
'capabilities' => [
'firstMatch' => [(object) $capabilities->toW3cCompatibleArray()],
],
]
);
$response = $executor->execute($newSessionCommand);
$returnedCapabilities = DesiredCapabilities::createFromW3cCapabilities($response->getValue()['capabilities']);
$sessionId = $response->getSessionID();
return new static($executor, $sessionId, $returnedCapabilities, true);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Facebook\WebDriver\Firefox;
use Facebook\WebDriver\Remote\Service\DriverService;
class FirefoxDriverService extends DriverService
{
/**
* @var string Name of the environment variable storing the path to the driver binary
*/
const WEBDRIVER_FIREFOX_DRIVER = 'WEBDRIVER_FIREFOX_DRIVER';
/**
* @var string Default executable used when no other is provided
* @internal
*/
const DEFAULT_EXECUTABLE = 'geckodriver';
/**
* @return static
*/
public static function createDefaultService()
{
$pathToExecutable = getenv(static::WEBDRIVER_FIREFOX_DRIVER);
if ($pathToExecutable === false || $pathToExecutable === '') {
$pathToExecutable = static::DEFAULT_EXECUTABLE;
}
$port = 9515; // TODO: Get another free port if the default port is used.
$args = ['-p=' . $port];
return new static($pathToExecutable, $port, $args);
}
}
@@ -0,0 +1,111 @@
<?php
namespace Facebook\WebDriver\Firefox;
use ReturnTypeWillChange;
/**
* Class to manage Firefox-specific capabilities
*
* @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions
*/
class FirefoxOptions implements \JsonSerializable
{
/** @var string The key of FirefoxOptions in desired capabilities */
const CAPABILITY = 'moz:firefoxOptions';
/** @var string */
const OPTION_ARGS = 'args';
/** @var string */
const OPTION_PREFS = 'prefs';
/** @var array */
private $options = [];
/** @var array */
private $arguments = [];
/** @var array */
private $preferences = [];
public function __construct()
{
// Set default preferences:
// disable the "Reader View" help tooltip, which can hide elements in the window.document
$this->setPreference(FirefoxPreferences::READER_PARSE_ON_LOAD_ENABLED, false);
// disable JSON viewer and let JSON be rendered as raw data
$this->setPreference(FirefoxPreferences::DEVTOOLS_JSONVIEW, false);
}
/**
* Directly set firefoxOptions.
* Use `addArguments` to add command line arguments and `setPreference` to set Firefox about:config entry.
*
* @param string $name
* @param mixed $value
* @return self
*/
public function setOption($name, $value)
{
if ($name === self::OPTION_PREFS) {
throw new \InvalidArgumentException('Use setPreference() method to set Firefox preferences');
}
if ($name === self::OPTION_ARGS) {
throw new \InvalidArgumentException('Use addArguments() method to add Firefox arguments');
}
$this->options[$name] = $value;
return $this;
}
/**
* Command line arguments to pass to the Firefox binary.
* These must include the leading dash (-) where required, e.g. ['-headless'].
*
* @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions#args
* @param string[] $arguments
* @return self
*/
public function addArguments(array $arguments)
{
$this->arguments = array_merge($this->arguments, $arguments);
return $this;
}
/**
* Set Firefox preference (about:config entry).
*
* @see http://kb.mozillazine.org/About:config_entries
* @see https://developer.mozilla.org/en-US/docs/Web/WebDriver/Capabilities/firefoxOptions#prefs
* @param string $name
* @param string|bool|int $value
* @return self
*/
public function setPreference($name, $value)
{
$this->preferences[$name] = $value;
return $this;
}
/**
* @return array
*/
public function toArray()
{
$array = $this->options;
if (!empty($this->arguments)) {
$array[self::OPTION_ARGS] = $this->arguments;
}
if (!empty($this->preferences)) {
$array[self::OPTION_PREFS] = $this->preferences;
}
return $array;
}
#[ReturnTypeWillChange]
public function jsonSerialize()
{
return new \ArrayObject($this->toArray());
}
}
@@ -0,0 +1,25 @@
<?php
namespace Facebook\WebDriver\Firefox;
/**
* Constants of common Firefox profile preferences (about:config values).
* @see http://kb.mozillazine.org/Firefox_:_FAQs_:_About:config_Entries
*
* @codeCoverageIgnore
*/
class FirefoxPreferences
{
/** @var string Port WebDriver uses to communicate with Firefox instance */
const WEBDRIVER_FIREFOX_PORT = 'webdriver_firefox_port';
/** @var string Should the reader view (FF 38+) be enabled? */
const READER_PARSE_ON_LOAD_ENABLED = 'reader.parse-on-load.enabled';
/** @var string Browser homepage */
const BROWSER_STARTUP_HOMEPAGE = 'browser.startup.homepage';
/** @var string Should the Devtools JSON view be enabled? */
const DEVTOOLS_JSONVIEW = 'devtools.jsonview.enabled';
private function __construct()
{
}
}
@@ -0,0 +1,291 @@
<?php
namespace Facebook\WebDriver\Firefox;
use Facebook\WebDriver\Exception\WebDriverException;
use FilesystemIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ZipArchive;
class FirefoxProfile
{
/**
* @var array
*/
private $preferences = [];
/**
* @var array
*/
private $extensions = [];
/**
* @var array
*/
private $extensions_datas = [];
/**
* @var string
*/
private $rdf_file;
/**
* @param string $extension The path to the xpi extension.
* @return FirefoxProfile
*/
public function addExtension($extension)
{
$this->extensions[] = $extension;
return $this;
}
/**
* @param string $extension_datas The path to the folder containing the datas to add to the extension
* @return FirefoxProfile
*/
public function addExtensionDatas($extension_datas)
{
if (!is_dir($extension_datas)) {
return null;
}
$this->extensions_datas[basename($extension_datas)] = $extension_datas;
return $this;
}
/**
* @param string $rdf_file The path to the rdf file
* @return FirefoxProfile
*/
public function setRdfFile($rdf_file)
{
if (!is_file($rdf_file)) {
return null;
}
$this->rdf_file = $rdf_file;
return $this;
}
/**
* @param string $key
* @param string|bool|int $value
* @throws WebDriverException
* @return FirefoxProfile
*/
public function setPreference($key, $value)
{
if (is_string($value)) {
$value = sprintf('"%s"', $value);
} else {
if (is_int($value)) {
$value = sprintf('%d', $value);
} else {
if (is_bool($value)) {
$value = $value ? 'true' : 'false';
} else {
throw new WebDriverException(
'The value of the preference should be either a string, int or bool.'
);
}
}
}
$this->preferences[$key] = $value;
return $this;
}
/**
* @param mixed $key
* @return mixed
*/
public function getPreference($key)
{
if (array_key_exists($key, $this->preferences)) {
return $this->preferences[$key];
}
return null;
}
/**
* @return string
*/
public function encode()
{
$temp_dir = $this->createTempDirectory('WebDriverFirefoxProfile');
if (isset($this->rdf_file)) {
copy($this->rdf_file, $temp_dir . DIRECTORY_SEPARATOR . 'mimeTypes.rdf');
}
foreach ($this->extensions as $extension) {
$this->installExtension($extension, $temp_dir);
}
foreach ($this->extensions_datas as $dirname => $extension_datas) {
mkdir($temp_dir . DIRECTORY_SEPARATOR . $dirname);
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($extension_datas, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$target_dir = $temp_dir . DIRECTORY_SEPARATOR . $dirname . DIRECTORY_SEPARATOR
. $iterator->getSubPathName();
if ($item->isDir()) {
mkdir($target_dir);
} else {
copy($item, $target_dir);
}
}
}
$content = '';
foreach ($this->preferences as $key => $value) {
$content .= sprintf("user_pref(\"%s\", %s);\n", $key, $value);
}
file_put_contents($temp_dir . '/user.js', $content);
// Intentionally do not use `tempnam()`, as it creates empty file which zip extension may not handle.
$temp_zip = sys_get_temp_dir() . '/' . uniqid('WebDriverFirefoxProfileZip', false);
$zip = new ZipArchive();
$zip->open($temp_zip, ZipArchive::CREATE);
$dir = new RecursiveDirectoryIterator($temp_dir);
$files = new RecursiveIteratorIterator($dir);
$dir_prefix = preg_replace(
'#\\\\#',
'\\\\\\\\',
$temp_dir . DIRECTORY_SEPARATOR
);
foreach ($files as $name => $object) {
if (is_dir($name)) {
continue;
}
$path = preg_replace("#^{$dir_prefix}#", '', $name);
$zip->addFile($name, $path);
}
$zip->close();
$profile = base64_encode(file_get_contents($temp_zip));
// clean up
$this->deleteDirectory($temp_dir);
unlink($temp_zip);
return $profile;
}
/**
* @param string $extension The path to the extension.
* @param string $profile_dir The path to the profile directory.
* @return string The path to the directory of this extension.
*/
private function installExtension($extension, $profile_dir)
{
$temp_dir = $this->createTempDirectory('WebDriverFirefoxProfileExtension');
$this->extractTo($extension, $temp_dir);
// This is a hacky way to parse the id since there is no offical RDF parser library.
// Find the correct namespace for the id element.
$install_rdf_path = $temp_dir . '/install.rdf';
$xml = simplexml_load_file($install_rdf_path);
$ns = $xml->getDocNamespaces();
$prefix = '';
if (!empty($ns)) {
foreach ($ns as $key => $value) {
if (mb_strpos($value, '//www.mozilla.org/2004/em-rdf') > 0) {
if ($key != '') {
$prefix = $key . ':'; // Separate the namespace from the name.
}
break;
}
}
}
// Get the extension id from the install manifest.
$matches = [];
preg_match('#<' . $prefix . 'id>([^<]+)</' . $prefix . 'id>#', $xml->asXML(), $matches);
if (isset($matches[1])) {
$ext_dir = $profile_dir . '/extensions/' . $matches[1];
mkdir($ext_dir, 0777, true);
$this->extractTo($extension, $ext_dir);
} else {
$this->deleteDirectory($temp_dir);
throw new WebDriverException('Cannot get the extension id from the install manifest.');
}
$this->deleteDirectory($temp_dir);
return $ext_dir;
}
/**
* @param string $prefix Prefix of the temp directory.
*
* @throws WebDriverException
* @return string The path to the temp directory created.
*/
private function createTempDirectory($prefix = '')
{
$temp_dir = tempnam(sys_get_temp_dir(), $prefix);
if (file_exists($temp_dir)) {
unlink($temp_dir);
mkdir($temp_dir);
if (!is_dir($temp_dir)) {
throw new WebDriverException('Cannot create firefox profile.');
}
}
return $temp_dir;
}
/**
* @param string $directory The path to the directory.
*/
private function deleteDirectory($directory)
{
$dir = new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS);
$paths = new RecursiveIteratorIterator($dir, RecursiveIteratorIterator::CHILD_FIRST);
foreach ($paths as $path) {
if ($path->isDir() && !$path->isLink()) {
rmdir($path->getPathname());
} else {
unlink($path->getPathname());
}
}
rmdir($directory);
}
/**
* @param string $xpi The path to the .xpi extension.
* @param string $target_dir The path to the unzip directory.
*
* @throws \Exception
* @return FirefoxProfile
*/
private function extractTo($xpi, $target_dir)
{
$zip = new ZipArchive();
if (file_exists($xpi)) {
if ($zip->open($xpi)) {
$zip->extractTo($target_dir);
$zip->close();
} else {
throw new \Exception("Failed to open the firefox extension. '$xpi'");
}
} else {
throw new \Exception("Firefox extension doesn't exist. '$xpi'");
}
return $this;
}
}
@@ -0,0 +1,16 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
/**
* Move to the location and then release the mouse key.
*/
class WebDriverButtonReleaseAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseUp($this->getActionLocation());
}
}
@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
class WebDriverClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->click($this->getActionLocation());
}
}
@@ -0,0 +1,16 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
/**
* Move the the location, click and hold.
*/
class WebDriverClickAndHoldAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseDown($this->getActionLocation());
}
}
@@ -0,0 +1,16 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
/**
* You can call it 'Right Click' if you like.
*/
class WebDriverContextClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->contextClick($this->getActionLocation());
}
}
@@ -0,0 +1,78 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Exception\UnsupportedOperationException;
use Facebook\WebDriver\WebDriverPoint;
/**
* Interface representing basic mouse operations.
*/
class WebDriverCoordinates
{
/**
* @var null
*/
private $onScreen;
/**
* @var callable
*/
private $inViewPort;
/**
* @var callable
*/
private $onPage;
/**
* @var string
*/
private $auxiliary;
/**
* @param null $on_screen
* @param callable $in_view_port
* @param callable $on_page
* @param string $auxiliary
*/
public function __construct($on_screen, callable $in_view_port, callable $on_page, $auxiliary)
{
$this->onScreen = $on_screen;
$this->inViewPort = $in_view_port;
$this->onPage = $on_page;
$this->auxiliary = $auxiliary;
}
/**
* @throws UnsupportedOperationException
* @return WebDriverPoint
*/
public function onScreen()
{
throw new UnsupportedOperationException(
'onScreen is planned but not yet supported by Selenium'
);
}
/**
* @return WebDriverPoint
*/
public function inViewPort()
{
return call_user_func($this->inViewPort);
}
/**
* @return WebDriverPoint
*/
public function onPage()
{
return call_user_func($this->onPage);
}
/**
* @return string The attached object id.
*/
public function getAuxiliary()
{
return $this->auxiliary;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
class WebDriverDoubleClickAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->doubleClick($this->getActionLocation());
}
}
@@ -0,0 +1,12 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
class WebDriverKeyDownAction extends WebDriverSingleKeyAction
{
public function perform()
{
$this->focusOnElement();
$this->keyboard->pressKey($this->key);
}
}
@@ -0,0 +1,12 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
class WebDriverKeyUpAction extends WebDriverSingleKeyAction
{
public function perform()
{
$this->focusOnElement();
$this->keyboard->releaseKey($this->key);
}
}
@@ -0,0 +1,48 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverMouse;
/**
* Base class for all keyboard-related actions.
*/
abstract class WebDriverKeysRelatedAction
{
/**
* @var WebDriverKeyboard
*/
protected $keyboard;
/**
* @var WebDriverMouse
*/
protected $mouse;
/**
* @var WebDriverLocatable|null
*/
protected $locationProvider;
/**
* @param WebDriverKeyboard $keyboard
* @param WebDriverMouse $mouse
* @param WebDriverLocatable $location_provider
*/
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null
) {
$this->keyboard = $keyboard;
$this->mouse = $mouse;
$this->locationProvider = $location_provider;
}
protected function focusOnElement()
{
if ($this->locationProvider) {
$this->mouse->click($this->locationProvider->getCoordinates());
}
}
}
@@ -0,0 +1,48 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverMouse;
/**
* Base class for all mouse-related actions.
*/
class WebDriverMouseAction
{
/**
* @var WebDriverMouse
*/
protected $mouse;
/**
* @var WebDriverLocatable
*/
protected $locationProvider;
/**
* @param WebDriverMouse $mouse
* @param WebDriverLocatable|null $location_provider
*/
public function __construct(WebDriverMouse $mouse, WebDriverLocatable $location_provider = null)
{
$this->mouse = $mouse;
$this->locationProvider = $location_provider;
}
/**
* @return null|WebDriverCoordinates
*/
protected function getActionLocation()
{
if ($this->locationProvider !== null) {
return $this->locationProvider->getCoordinates();
}
return null;
}
protected function moveToLocation()
{
$this->mouse->mouseMove($this->locationProvider);
}
}
@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\WebDriverAction;
class WebDriverMouseMoveAction extends WebDriverMouseAction implements WebDriverAction
{
public function perform()
{
$this->mouse->mouseMove($this->getActionLocation());
}
}
@@ -0,0 +1,45 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverMouse;
class WebDriverMoveToOffsetAction extends WebDriverMouseAction implements WebDriverAction
{
/**
* @var int|null
*/
private $xOffset;
/**
* @var int|null
*/
private $yOffset;
/**
* @param WebDriverMouse $mouse
* @param WebDriverLocatable|null $location_provider
* @param int|null $x_offset
* @param int|null $y_offset
*/
public function __construct(
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$x_offset = null,
$y_offset = null
) {
parent::__construct($mouse, $location_provider);
$this->xOffset = $x_offset;
$this->yOffset = $y_offset;
}
public function perform()
{
$this->mouse->mouseMove(
$this->getActionLocation(),
$this->xOffset,
$this->yOffset
);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverMouse;
class WebDriverSendKeysAction extends WebDriverKeysRelatedAction implements WebDriverAction
{
/**
* @var string
*/
private $keys = '';
/**
* @param WebDriverKeyboard $keyboard
* @param WebDriverMouse $mouse
* @param WebDriverLocatable $location_provider
* @param string $keys
*/
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$keys = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);
$this->keys = $keys;
}
public function perform()
{
$this->focusOnElement();
$this->keyboard->sendKeys($this->keys);
}
}
@@ -0,0 +1,53 @@
<?php
namespace Facebook\WebDriver\Interactions\Internal;
use Facebook\WebDriver\Internal\WebDriverLocatable;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverKeyboard;
use Facebook\WebDriver\WebDriverKeys;
use Facebook\WebDriver\WebDriverMouse;
abstract class WebDriverSingleKeyAction extends WebDriverKeysRelatedAction implements WebDriverAction
{
const MODIFIER_KEYS = [
WebDriverKeys::SHIFT,
WebDriverKeys::LEFT_SHIFT,
WebDriverKeys::RIGHT_SHIFT,
WebDriverKeys::CONTROL,
WebDriverKeys::LEFT_CONTROL,
WebDriverKeys::RIGHT_CONTROL,
WebDriverKeys::ALT,
WebDriverKeys::LEFT_ALT,
WebDriverKeys::RIGHT_ALT,
WebDriverKeys::META,
WebDriverKeys::RIGHT_META,
WebDriverKeys::COMMAND,
];
/** @var string */
protected $key;
/**
* @param string $key
* @todo Remove default $key value in next major version (BC)
*/
public function __construct(
WebDriverKeyboard $keyboard,
WebDriverMouse $mouse,
WebDriverLocatable $location_provider = null,
$key = ''
) {
parent::__construct($keyboard, $mouse, $location_provider);
if (!in_array($key, self::MODIFIER_KEYS, true)) {
throw new \InvalidArgumentException(
sprintf(
'keyDown / keyUp actions can only be used for modifier keys, but "%s" was given',
$key
)
);
}
$this->key = $key;
}
}
@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverDoubleTapAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->doubleTap($this->locationProvider);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverDownAction extends WebDriverTouchAction implements WebDriverAction
{
/**
* @var int
*/
private $x;
/**
* @var int
*/
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->down($this->x, $this->y);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverFlickAction extends WebDriverTouchAction implements WebDriverAction
{
/**
* @var int
*/
private $x;
/**
* @var int
*/
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->flick($this->x, $this->y);
}
}
@@ -0,0 +1,52 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverElement;
class WebDriverFlickFromElementAction extends WebDriverTouchAction implements WebDriverAction
{
/**
* @var int
*/
private $x;
/**
* @var int
*/
private $y;
/**
* @var int
*/
private $speed;
/**
* @param WebDriverTouchScreen $touch_screen
* @param WebDriverElement $element
* @param int $x
* @param int $y
* @param int $speed
*/
public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverElement $element,
$x,
$y,
$speed
) {
$this->x = $x;
$this->y = $y;
$this->speed = $speed;
parent::__construct($touch_screen, $element);
}
public function perform()
{
$this->touchScreen->flickFromElement(
$this->locationProvider,
$this->x,
$this->y,
$this->speed
);
}
}
@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverLongPressAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->longPress($this->locationProvider);
}
}
@@ -0,0 +1,28 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverMoveAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->move($this->x, $this->y);
}
}
@@ -0,0 +1,28 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverScrollAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param int $x
* @param int $y
*/
public function __construct(WebDriverTouchScreen $touch_screen, $x, $y)
{
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen);
}
public function perform()
{
$this->touchScreen->scroll($this->x, $this->y);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
use Facebook\WebDriver\WebDriverElement;
class WebDriverScrollFromElementAction extends WebDriverTouchAction implements WebDriverAction
{
private $x;
private $y;
/**
* @param WebDriverTouchScreen $touch_screen
* @param WebDriverElement $element
* @param int $x
* @param int $y
*/
public function __construct(
WebDriverTouchScreen $touch_screen,
WebDriverElement $element,
$x,
$y
) {
$this->x = $x;
$this->y = $y;
parent::__construct($touch_screen, $element);
}
public function perform()
{
$this->touchScreen->scrollFromElement(
$this->locationProvider,
$this->x,
$this->y
);
}
}
@@ -0,0 +1,13 @@
<?php
namespace Facebook\WebDriver\Interactions\Touch;
use Facebook\WebDriver\WebDriverAction;
class WebDriverTapAction extends WebDriverTouchAction implements WebDriverAction
{
public function perform()
{
$this->touchScreen->tap($this->locationProvider);
}
}

Some files were not shown because too many files have changed in this diff Show More