diff --git a/.gitattributes b/.gitattributes index 8da698cf..7a7cb7f9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,8 +4,8 @@ .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore +.styleci.yml export-ignore CHANGELOG.md export-ignore -phpcs.xml.dist export-ignore phpstan.neon.dist export-ignore phpunit.xml.dist export-ignore README.md export-ignore diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 3232ba4b..52b61271 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -5,38 +5,6 @@ on: pull_request: jobs: - phpcs: - name: PHP CodeSniffer - runs-on: ubuntu-20.04 - - steps: - - name: Checkout Code - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: '7.4' - tools: composer:v2 - coverage: none - - - name: Install Dependencies - uses: nick-invision/retry@v1 - with: - timeout_minutes: 5 - max_attempts: 5 - command: composer update --no-interaction --no-progress - - - name: Install PHP CodeSniffer - uses: nick-invision/retry@v1 - with: - timeout_minutes: 5 - max_attempts: 5 - command: composer bin phpcs update --no-interaction --no-progress - - - name: Execute PHP CodeSniffer - run: vendor/bin/phpcs - phpstan: name: PHPStan runs-on: ubuntu-20.04 diff --git a/.gitignore b/.gitignore index 202cace8..04626b2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ .phpunit.result.cache composer.lock -phpcs.xml phpstan.neon phpunit.xml vendor diff --git a/.styleci.yml b/.styleci.yml new file mode 100644 index 00000000..a923f78e --- /dev/null +++ b/.styleci.yml @@ -0,0 +1,20 @@ +preset: symfony + +risky: true + +enabled: + - align_phpdoc + - alpha_ordered_imports + - array_indentation + - const_visibility_required + - native_constant_invocation + - native_function_invocation + - phpdoc_order + - void_return + +disabled: + - native_constant_invocation_symfony + - native_function_invocation_symfony + - no_superfluous_phpdoc_tags_symfony + - phpdoc_to_comment + - phpdoc_var_without_name diff --git a/examples/form-submit.php b/examples/form-submit.php index f7c0ae67..83a2ba04 100644 --- a/examples/form-submit.php +++ b/examples/form-submit.php @@ -1,6 +1,6 @@ createPage(); -$page->navigate('file://' . __DIR__ . '/html/form.html')->waitForNavigation(); +$page->navigate('file://'.__DIR__.'/html/form.html')->waitForNavigation(); // put 'hello' in the input and submit the form $evaluation = $page->evaluate( @@ -26,4 +26,4 @@ // get value in the new page $value = $page->evaluate('document.querySelector("#value").innerHTML')->getReturnValue(); -var_dump($value); +\var_dump($value); diff --git a/examples/persistent-browser.php b/examples/persistent-browser.php index 07deffc9..9d37f506 100644 --- a/examples/persistent-browser.php +++ b/examples/persistent-browser.php @@ -10,7 +10,7 @@ * of creating a new one. If chrome was closed or crashed, a new instance is started again. */ -require(__DIR__ . '/../vendor/autoload.php'); +require __DIR__.'/../vendor/autoload.php'; // path to the file to store websocket's uri $socketFile = '/tmp/chrome-php-demo-socket'; @@ -19,12 +19,12 @@ $browser = null; // try to connect to chrome instance if it exists -if (file_exists($socketFile)) { - $socket = file_get_contents($socketFile); +if (\file_exists($socketFile)) { + $socket = \file_get_contents($socketFile); try { $browser = \HeadlessChromium\BrowserFactory::connectToBrowser($socket, [ - 'debugLogger' => 'php://stdout' + 'debugLogger' => 'php://stdout', ]); } catch (\HeadlessChromium\Exception\BrowserConnectionFailed $e) { // The browser was probably closed @@ -37,14 +37,14 @@ $factory = new \HeadlessChromium\BrowserFactory(); $browser = $factory->createBrowser([ 'headless' => false, - 'keepAlive' => true + 'keepAlive' => true, ]); // save the uri to be able to connect again to browser - file_put_contents($socketFile, $browser->getSocketUri()); + \file_put_contents($socketFile, $browser->getSocketUri()); } // do something with the browser $page = $browser->createPage(); -$page->navigate('http://example.com')->waitForNavigation(); \ No newline at end of file +$page->navigate('http://example.com')->waitForNavigation(); diff --git a/phpcs.xml.dist b/phpcs.xml.dist deleted file mode 100644 index b8ff9c5d..00000000 --- a/phpcs.xml.dist +++ /dev/null @@ -1,36 +0,0 @@ - - - - ./src/ - ./tests/ - - - - - - - - - - - ./tests/* - - - - ./tests/* - - - - - - - - - - - - - - - - diff --git a/src/AutoDiscover.php b/src/AutoDiscover.php index f8f8921c..b1be6ec0 100644 --- a/src/AutoDiscover.php +++ b/src/AutoDiscover.php @@ -15,7 +15,7 @@ class AutoDiscover { public function getChromeBinaryPath(): string { - if (array_key_exists('CHROME_PATH', $_SERVER)) { + if (\array_key_exists('CHROME_PATH', $_SERVER)) { return $_SERVER['CHROME_PATH']; } @@ -37,11 +37,11 @@ private static function windows(): string { try { // accessing the registry can be costly, but this specific key is likely to be already cached in memory - $registryKey = shell_exec( + $registryKey = \shell_exec( 'reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe" /ve' ); - preg_match('/.:(?!.*:).*/', $registryKey, $matches); + \preg_match('/.:(?!.*:).*/', $registryKey, $matches); return $matches[0]; } catch (\Throwable $e) { @@ -52,6 +52,6 @@ private static function windows(): string public function getOS(): string { - return PHP_OS; + return \PHP_OS; } } diff --git a/src/Browser.php b/src/Browser.php index e5b1e241..23bb1ed8 100644 --- a/src/Browser.php +++ b/src/Browser.php @@ -15,8 +15,8 @@ use HeadlessChromium\Communication\Message; use HeadlessChromium\Communication\Target; use HeadlessChromium\Exception\CommunicationException; -use HeadlessChromium\Exception\NoResponseAvailable; use HeadlessChromium\Exception\CommunicationException\ResponseHasError; +use HeadlessChromium\Exception\NoResponseAvailable; use HeadlessChromium\Exception\OperationTimedOut; class Browser @@ -32,7 +32,8 @@ class Browser protected $targets = []; /** - * A preScript to be automatically added on every new pages + * A preScript to be automatically added on every new pages. + * * @var string|null */ protected $pagePreScript; @@ -42,15 +43,13 @@ public function __construct(Connection $connection) $this->connection = $connection; // listen for target created - $this->connection->on(Connection::EVENT_TARGET_CREATED, function (array $params) { - + $this->connection->on(Connection::EVENT_TARGET_CREATED, function (array $params): void { // create and store the target $this->targets[$params['targetInfo']['targetId']] = new Target($params['targetInfo'], $this->connection); }); // listen for target info changed - $this->connection->on(Connection::EVENT_TARGET_INFO_CHANGED, function (array $params) { - + $this->connection->on(Connection::EVENT_TARGET_INFO_CHANGED, function (array $params): void { // get target by id $target = $this->getTarget($params['targetInfo']['targetId']); @@ -60,8 +59,7 @@ public function __construct(Connection $connection) }); // listen for target destroyed - $this->connection->on(Connection::EVENT_TARGET_DESTROYED, function (array $params) { - + $this->connection->on(Connection::EVENT_TARGET_DESTROYED, function (array $params): void { // get target by id $target = $this->getTarget($params['targetId']); @@ -71,7 +69,7 @@ public function __construct(Connection $connection) $target->destroy(); $this->connection ->getLogger() - ->debug('✘ target(' . $params['targetId'] . ') was destroyed and unreferenced.'); + ->debug('✘ target('.$params['targetId'].') was destroyed and unreferenced.'); } }); @@ -93,26 +91,27 @@ public function getConnection(): Connection * * @param string|null $script */ - public function setPagePreScript(string $script = null) + public function setPagePreScript(string $script = null): void { $this->pagePreScript = $script; } /** - * Closes the browser + * Closes the browser. * * @throws \Exception */ - public function close() + public function close(): void { $this->sendCloseMessage(); } /** - * Send close message to the browser + * Send close message to the browser. + * * @throws OperationTimedOut */ - final public function sendCloseMessage() + final public function sendCloseMessage(): void { $r = $this->connection->sendMessageSync(new Message('Browser.close')); if (!$r->isSuccessful()) { @@ -123,15 +122,16 @@ final public function sendCloseMessage() } /** - * Creates a new page + * Creates a new page. + * * @throws NoResponseAvailable * @throws CommunicationException * @throws OperationTimedOut + * * @return Page */ public function createPage(): Page { - // page url $params = ['url' => 'about:blank']; @@ -179,14 +179,16 @@ public function createPage(): Page /** * @param string $targetId + * * @return Target|null */ public function getTarget($targetId) { // make sure target was created (via Target.targetCreated event) - if (!array_key_exists($targetId, $this->targets)) { + if (!\array_key_exists($targetId, $this->targets)) { return null; } + return $this->targets[$targetId]; } @@ -195,6 +197,6 @@ public function getTarget($targetId) */ public function getTargets() { - return array_values($this->targets); + return \array_values($this->targets); } } diff --git a/src/Browser/BrowserProcess.php b/src/Browser/BrowserProcess.php index 42bfe41e..333e210a 100644 --- a/src/Browser/BrowserProcess.php +++ b/src/Browser/BrowserProcess.php @@ -13,7 +13,6 @@ use HeadlessChromium\Browser; use HeadlessChromium\Communication\Connection; -use HeadlessChromium\Communication\Message; use HeadlessChromium\Exception\OperationTimedOut; use HeadlessChromium\Utils; use Psr\Log\LoggerAwareInterface; @@ -25,14 +24,15 @@ use Wrench\Exception\SocketException; /** - * A browser process starter. Don't use directly, use BrowserFactory instead + * A browser process starter. Don't use directly, use BrowserFactory instead. */ class BrowserProcess implements LoggerAwareInterface { use LoggerAwareTrait; /** - * chrome instance's user data data + * chrome instance's user data data. + * * @var string */ protected $userDataDir; @@ -43,7 +43,8 @@ class BrowserProcess implements LoggerAwareInterface protected $process; /** - * True if the user data dir is temporary and should be deleted on process closes + * True if the user data dir is temporary and should be deleted on process closes. + * * @var bool */ protected $userDataDirIsTemp; @@ -75,21 +76,22 @@ class BrowserProcess implements LoggerAwareInterface /** * BrowserProcess constructor. + * * @param LoggerInterface|null $logger */ public function __construct(LoggerInterface $logger = null) { - // set or create logger $this->setLogger($logger ?? new NullLogger()); } /** - * Starts the browser + * Starts the browser. + * * @param string $binary * @param array $options */ - public function start($binary, $options) + public function start($binary, $options): void { if ($this->wasStarted) { // cannot start twice because once started this class contains the necessary data to cleanup the browser. @@ -103,7 +105,7 @@ public function start($binary, $options) $this->logger->debug('process: initializing'); // user data dir - if (!array_key_exists('userDataDir', $options) || !$options['userDataDir']) { + if (!\array_key_exists('userDataDir', $options) || !$options['userDataDir']) { // if no data dir specified create it $options['userDataDir'] = $this->createTempDir(); @@ -113,13 +115,13 @@ public function start($binary, $options) $this->userDataDir = $options['userDataDir']; // log - $this->logger->debug('process: using directory: ' . $options['userDataDir']); + $this->logger->debug('process: using directory: '.$options['userDataDir']); // get args for command line $args = $this->getArgsFromOptions($binary, $options); // setup chrome process - if (!array_key_exists('keepAlive', $options) || !$options['keepAlive']) { + if (!\array_key_exists('keepAlive', $options) || !$options['keepAlive']) { $process = new Process($args); } else { $process = new ProcessKeepAlive($args); @@ -127,7 +129,7 @@ public function start($binary, $options) $this->process = $process; // log - $this->logger->debug('process: starting process: ' . $process->getCommandLine()); + $this->logger->debug('process: starting process: '.$process->getCommandLine()); // and start $process->start(); @@ -137,14 +139,14 @@ public function start($binary, $options) $this->wsUri = $this->waitForStartup($process, $startupTimeout * 1000 * 1000); // log - $this->logger->debug('process: connecting using ' . $this->wsUri); + $this->logger->debug('process: connecting using '.$this->wsUri); // connect to browser $connection = new Connection($this->wsUri, $this->logger, $options['sendSyncDefaultTimeout'] ?? 5000); $connection->connect(); // connection delay - if (array_key_exists('connectionDelay', $options)) { + if (\array_key_exists('connectionDelay', $options)) { $connection->setConnectionDelay($options['connectionDelay']); } @@ -172,18 +174,19 @@ public function getSocketUri() } /** - * Kills the process and clean temporary files + * Kills the process and clean temporary files. + * * @throws OperationTimedOut */ - public function kill() + public function kill(): void { - // log $this->logger->debug('process: killing chrome'); if ($this->wasKilled) { // log $this->logger->debug('process: chrome already killed, ignoring'); + return; } @@ -249,7 +252,7 @@ public function kill() $exitCode = $this->process->stop(); // log - $this->logger->debug('process: process stopped with exit code ' . $exitCode); + $this->logger->debug('process: process stopped with exit code '.$exitCode); } } @@ -257,7 +260,7 @@ public function kill() if ($this->userDataDirIsTemp && $this->userDataDir) { try { // log - $this->logger->debug('process: cleaning temporary resources:' . $this->userDataDir); + $this->logger->debug('process: cleaning temporary resources:'.$this->userDataDir); // cleaning $fs = new Filesystem(); @@ -270,8 +273,10 @@ public function kill() } /** - * Get args for creating chrome's startup command + * Get args for creating chrome's startup command. + * * @param array $options + * * @return array */ private function getArgsFromOptions($binary, array $options) @@ -308,7 +313,7 @@ private function getArgsFromOptions($binary, array $options) ]; // enable headless mode - if (!array_key_exists('headless', $options) || $options['headless']) { + if (!\array_key_exists('headless', $options) || $options['headless']) { $args[] = '--headless'; $args[] = '--disable-gpu'; $args[] = '--font-render-hinting=none'; @@ -317,62 +322,62 @@ private function getArgsFromOptions($binary, array $options) } // disable loading of images (currently can't be done via devtools, only CLI) - if (array_key_exists('enableImages', $options) && ($options['enableImages'] === false)) { + if (\array_key_exists('enableImages', $options) && (false === $options['enableImages'])) { $args[] = '--blink-settings=imagesEnabled=false'; } // window's size - if (array_key_exists('windowSize', $options) && $options['windowSize']) { + if (\array_key_exists('windowSize', $options) && $options['windowSize']) { if ( - !is_array($options['windowSize']) || - count($options['windowSize']) !== 2 || - !is_numeric($options['windowSize'][0]) || - !is_numeric($options['windowSize'][1]) + !\is_array($options['windowSize']) || + 2 !== \count($options['windowSize']) || + !\is_numeric($options['windowSize'][0]) || + !\is_numeric($options['windowSize'][1]) ) { - throw new \InvalidArgumentException( - 'Option "windowSize" must be an array of dimensions (eg: [1000, 1200])' - ); + throw new \InvalidArgumentException('Option "windowSize" must be an array of dimensions (eg: [1000, 1200])'); } - $args[] = '--window-size=' . implode(',', $options['windowSize']) ; + $args[] = '--window-size='.\implode(',', $options['windowSize']); } // sandbox mode - useful if you want to use chrome headless inside docker - if (array_key_exists('noSandbox', $options) && $options['noSandbox']) { + if (\array_key_exists('noSandbox', $options) && $options['noSandbox']) { $args[] = '--no-sandbox'; } // user agent - if (array_key_exists('userAgent', $options)) { - $args[] = '--user-agent=' . $options['userAgent']; + if (\array_key_exists('userAgent', $options)) { + $args[] = '--user-agent='.$options['userAgent']; } // ignore certificate errors - if (array_key_exists('ignoreCertificateErrors', $options) && $options['ignoreCertificateErrors']) { + if (\array_key_exists('ignoreCertificateErrors', $options) && $options['ignoreCertificateErrors']) { $args[] = '--ignore-certificate-errors'; } // add custom flags - if (array_key_exists('customFlags', $options) && is_array($options['customFlags'])) { - $args = array_merge($args, $options['customFlags']); + if (\array_key_exists('customFlags', $options) && \is_array($options['customFlags'])) { + $args = \array_merge($args, $options['customFlags']); } // add user data dir to args - $args[] = '--user-data-dir=' . $options['userDataDir']; + $args[] = '--user-data-dir='.$options['userDataDir']; return $args; } /** - * Wait for chrome to startup (given a process) and return the ws uri to connect to + * Wait for chrome to startup (given a process) and return the ws uri to connect to. + * * @param Process $process - * @param int $timeout + * @param int $timeout + * * @return mixed */ private function waitForStartup(Process $process, int $timeout) { // log - $this->logger->debug('process: waiting for ' . $timeout / 1000000 . ' seconds for startup'); + $this->logger->debug('process: waiting for '.$timeout / 1000000 .' seconds for startup'); try { $generator = function (Process $process) { @@ -383,23 +388,23 @@ private function waitForStartup(Process $process, int $timeout) // exception $message = 'Chrome process stopped before startup completed.'; - $error = trim($process->getErrorOutput()); + $error = \trim($process->getErrorOutput()); if (!empty($error)) { - $message .= ' Additional info: ' . $error; + $message .= ' Additional info: '.$error; } throw new \RuntimeException($message); } - $output = trim($process->getIncrementalErrorOutput()); + $output = \trim($process->getIncrementalErrorOutput()); if ($output) { // log - $this->logger->debug('process: chrome output:' . $output); + $this->logger->debug('process: chrome output:'.$output); - $outputs = explode(PHP_EOL, $output); + $outputs = \explode(\PHP_EOL, $output); foreach ($outputs as $output) { - $output = trim($output); + $output = \trim($output); // ignore empty line if (empty($output)) { @@ -407,13 +412,14 @@ private function waitForStartup(Process $process, int $timeout) } // find socket uri - if (preg_match('/DevTools listening on (ws:\/\/.*)/', $output, $matches)) { + if (\preg_match('/DevTools listening on (ws:\/\/.*)/', $output, $matches)) { // log $this->logger->debug('process: ✓ accepted output'); + return $matches[1]; } else { // log - $this->logger->debug('process: ignoring output:' . trim($output)); + $this->logger->debug('process: ignoring output:'.\trim($output)); } } } @@ -422,6 +428,7 @@ private function waitForStartup(Process $process, int $timeout) yield 10 * 1000; } }; + return Utils::tryWithTimeout($timeout, $generator($process)); } catch (OperationTimedOut $e) { throw new \RuntimeException('Cannot start browser', 0, $e); @@ -429,15 +436,16 @@ private function waitForStartup(Process $process, int $timeout) } /** - * Creates a temp directory for the app + * Creates a temp directory for the app. + * * @return string path to the new temp directory */ private function createTempDir() { - $tmpFile = tempnam(sys_get_temp_dir(), 'chromium-php-'); + $tmpFile = \tempnam(\sys_get_temp_dir(), 'chromium-php-'); - unlink($tmpFile); - mkdir($tmpFile); + \unlink($tmpFile); + \mkdir($tmpFile); return $tmpFile; } diff --git a/src/Browser/ProcessAwareBrowser.php b/src/Browser/ProcessAwareBrowser.php index 954e8e3b..6a57d27e 100644 --- a/src/Browser/ProcessAwareBrowser.php +++ b/src/Browser/ProcessAwareBrowser.php @@ -13,7 +13,6 @@ use HeadlessChromium\Browser; use HeadlessChromium\Communication\Connection; -use Symfony\Component\Process\Process; class ProcessAwareBrowser extends Browser { @@ -30,9 +29,9 @@ public function __construct(Connection $connection, BrowserProcess $browserProce } /** - * @inheritdoc + * {@inheritdoc} */ - public function close() + public function close(): void { $this->browserProcess->kill(); } diff --git a/src/BrowserFactory.php b/src/BrowserFactory.php index f6f8b02d..d190326e 100644 --- a/src/BrowserFactory.php +++ b/src/BrowserFactory.php @@ -29,43 +29,42 @@ public function __construct(string $chromeBinary = null) } /** - * Start a chrome process and allows to interact with it + * Start a chrome process and allows to interact with it. * * @param array $options options for browser creation: - * - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none) - * - customFlags: array of custom flag to flags to pass to the command line - * - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none) - * - enableImages: toggle the loading of images (default: true) - * - headless: whether chrome should be started headless (default: true) - * - ignoreCertificateErrors: set chrome to ignore ssl errors - * - keepAlive: true to keep alive the chrome instance when the script terminates (default: false) - * - noSandbox: enable no sandbox mode (default: false) - * - sendSyncDefaultTimeout: maximum time in ms to wait for synchronous messages to send (default 5000 ms) - * - startupTimeout: maximum time in seconds to wait for chrome to start (default: 30 sec) - * - userAgent: user agent to use for the browser - * - userDataDir: chrome user data dir (default: a new empty dir is generated temporarily) - * - windowSize: size of the window, ex: [1920, 1080] (default: none) + * - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none) + * - customFlags: array of custom flag to flags to pass to the command line + * - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none) + * - enableImages: toggle the loading of images (default: true) + * - headless: whether chrome should be started headless (default: true) + * - ignoreCertificateErrors: set chrome to ignore ssl errors + * - keepAlive: true to keep alive the chrome instance when the script terminates (default: false) + * - noSandbox: enable no sandbox mode (default: false) + * - sendSyncDefaultTimeout: maximum time in ms to wait for synchronous messages to send (default 5000 ms) + * - startupTimeout: maximum time in seconds to wait for chrome to start (default: 30 sec) + * - userAgent: user agent to use for the browser + * - userDataDir: chrome user data dir (default: a new empty dir is generated temporarily) + * - windowSize: size of the window, ex: [1920, 1080] (default: none) * * @return ProcessAwareBrowser a Browser instance to interact with the new chrome process */ public function createBrowser(array $options = []): ProcessAwareBrowser { - // create logger from options $logger = self::createLogger($options); // log chrome version if ($logger) { $chromeVersion = $this->getChromeVersion(); - $logger->debug('Factory: chrome version: ' . $chromeVersion); + $logger->debug('Factory: chrome version: '.$chromeVersion); } // create browser process $browserProcess = new BrowserProcess($logger); // instruct the runtime to kill chrome and clean temp files on exit - if (!array_key_exists('keepAlive', $options) || !$options['keepAlive']) { - register_shutdown_function([$browserProcess, 'kill']); + if (!\array_key_exists('keepAlive', $options) || !$options['keepAlive']) { + \register_shutdown_function([$browserProcess, 'kill']); } // start the browser and connect to it @@ -75,7 +74,8 @@ public function createBrowser(array $options = []): ProcessAwareBrowser } /** - * Get chrome version + * Get chrome version. + * * @return string */ public function getChromeVersion() @@ -84,19 +84,19 @@ public function getChromeVersion() $exitCode = $process->run(); - if ($exitCode != 0) { + if (0 != $exitCode) { $message = 'Cannot read chrome version, make sure you provided the correct chrome executable'; - $message .= ' using: "' . $this->chromeBinary . '". '; + $message .= ' using: "'.$this->chromeBinary.'". '; - $error = trim($process->getErrorOutput()); + $error = \trim($process->getErrorOutput()); if (!empty($error)) { - $message .= 'Additional info: ' . $error; + $message .= 'Additional info: '.$error; } throw new \RuntimeException($message); } - return trim($process->getOutput()); + return \trim($process->getOutput()); } /** @@ -114,20 +114,21 @@ public function getChromeVersion() * ``` * * @param string $uri - * @param array $options options when creating the connection to the browser: - * - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none) - * - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none) - * - sendSyncDefaultTimeout: maximum time in ms to wait for synchronous messages to send (default 5000 ms) + * @param array $options options when creating the connection to the browser: + * - connectionDelay: amount of time in seconds to slows down connection for debugging purposes (default: none) + * - debugLogger: resource string ("php://stdout"), resource or psr-3 logger instance (default: none) + * - sendSyncDefaultTimeout: maximum time in ms to wait for synchronous messages to send (default 5000 ms) * - * @return Browser * @throws BrowserConnectionFailed + * + * @return Browser */ public static function connectToBrowser(string $uri, array $options = []): Browser { $logger = self::createLogger($options); if ($logger) { - $logger->debug('Browser Factory: connecting using ' . $uri); + $logger->debug('Browser Factory: connecting using '.$uri); } // connect to browser @@ -146,7 +147,7 @@ public static function connectToBrowser(string $uri, array $options = []): Brows } // connection delay - if (array_key_exists('connectionDelay', $options)) { + if (\array_key_exists('connectionDelay', $options)) { $connection->setConnectionDelay($options['connectionDelay']); } @@ -154,8 +155,10 @@ public static function connectToBrowser(string $uri, array $options = []): Brows } /** - * Create a logger instance from given options + * Create a logger instance from given options. + * * @param array $options + * * @return StreamLogger|null */ private static function createLogger($options) @@ -164,7 +167,7 @@ private static function createLogger($options) $logger = $options['debugLogger'] ?? null; // create logger from string name or resource - if (is_string($logger) || is_resource($logger)) { + if (\is_string($logger) || \is_resource($logger)) { $logger = new StreamLogger($logger); } diff --git a/src/Clip.php b/src/Clip.php index a32077c6..7be7cd7e 100644 --- a/src/Clip.php +++ b/src/Clip.php @@ -21,6 +21,7 @@ class Clip /** * Clip constructor. + * * @param int $x * @param int $y * @param int $height @@ -79,7 +80,7 @@ public function getScale() /** * @param mixed $x */ - public function setX($x) + public function setX($x): void { $this->x = $x; } @@ -87,7 +88,7 @@ public function setX($x) /** * @param mixed $y */ - public function setY($y) + public function setY($y): void { $this->y = $y; } @@ -95,7 +96,7 @@ public function setY($y) /** * @param mixed $height */ - public function setHeight($height) + public function setHeight($height): void { $this->height = $height; } @@ -103,7 +104,7 @@ public function setHeight($height) /** * @param mixed $width */ - public function setWidth($width) + public function setWidth($width): void { $this->width = $width; } @@ -111,7 +112,7 @@ public function setWidth($width) /** * @param mixed $scale */ - public function setScale($scale) + public function setScale($scale): void { $this->scale = $scale; } diff --git a/src/Communication/Connection.php b/src/Communication/Connection.php index 55d3fe54..d5faff7b 100644 --- a/src/Communication/Connection.php +++ b/src/Communication/Connection.php @@ -15,8 +15,8 @@ use HeadlessChromium\Communication\Socket\SocketInterface; use HeadlessChromium\Communication\Socket\Wrench; use HeadlessChromium\Exception\CommunicationException; -use HeadlessChromium\Exception\CommunicationException\InvalidResponse; use HeadlessChromium\Exception\CommunicationException\CannotReadResponse; +use HeadlessChromium\Exception\CommunicationException\InvalidResponse; use HeadlessChromium\Exception\OperationTimedOut; use HeadlessChromium\Exception\TargetDestroyed; use Psr\Log\LoggerAwareInterface; @@ -34,21 +34,24 @@ class Connection extends EventEmitter implements LoggerAwareInterface public const EVENT_TARGET_DESTROYED = 'method:Target.targetDestroyed'; /** - * When strict mode is enabled communication error will result in exceptions + * When strict mode is enabled communication error will result in exceptions. + * * @var bool */ protected $strict = true; /** * time in ms to wait between each message to be sent - * That helps to see what is happening when debugging + * That helps to see what is happening when debugging. + * * @var int */ protected $delay; /** * time in ms when the previous message was sent. Used to know how long to wait for before send next message - * (only when $delay is set) + * (only when $delay is set). + * * @var int */ private $lastMessageSentTime; @@ -59,13 +62,15 @@ class Connection extends EventEmitter implements LoggerAwareInterface protected $wsClient; /** - * List of response sent from the remote host and that are waiting to be read + * List of response sent from the remote host and that are waiting to be read. + * * @var array */ protected $responseBuffer = []; /** - * Default timeout for send sync in ms + * Default timeout for send sync in ms. + * * @var int */ protected $sendSyncDefaultTimeout; @@ -82,8 +87,9 @@ class Connection extends EventEmitter implements LoggerAwareInterface /** * CommunicationChannel constructor. + * * @param SocketInterface|string $socketClient - * @param int|null $sendSyncDefaultTimeout + * @param int|null $sendSyncDefaultTimeout */ public function __construct($socketClient, LoggerInterface $logger = null, int $sendSyncDefaultTimeout = null) { @@ -94,12 +100,10 @@ public function __construct($socketClient, LoggerInterface $logger = null, int $ $this->sendSyncDefaultTimeout = $sendSyncDefaultTimeout ?? 5000; // create socket client - if (is_string($socketClient)) { + if (\is_string($socketClient)) { $socketClient = new Wrench(new WrenchBaseClient($socketClient, 'http://127.0.0.1'), $this->logger); - } elseif (!is_object($socketClient) && !$socketClient instanceof SocketInterface) { - throw new \InvalidArgumentException( - '$socketClient param should be either a SockInterface instance or a web socket uri string' - ); + } elseif (!\is_object($socketClient) && !$socketClient instanceof SocketInterface) { + throw new \InvalidArgumentException('$socketClient param should be either a SockInterface instance or a web socket uri string'); } $this->wsClient = $socketClient; @@ -114,16 +118,18 @@ public function getLogger(): LoggerInterface } /** - * Set the delay to apply everytime before data are sent + * Set the delay to apply everytime before data are sent. + * * @param int $delay */ - public function setConnectionDelay(int $delay) + public function setConnectionDelay(int $delay): void { $this->delay = $delay; } /** - * Gets the default timeout used when sending a message synchronously + * Gets the default timeout used when sending a message synchronously. + * * @return int */ public function getSendSyncDefaultTimeout(): int @@ -142,13 +148,14 @@ public function isStrict(): bool /** * @param bool $strict */ - public function setStrict(bool $strict) + public function setStrict(bool $strict): void { $this->strict = $strict; } /** - * Connects to the server + * Connects to the server. + * * @return bool Whether a new connection was made */ public function connect() @@ -157,7 +164,8 @@ public function connect() } /** - * Disconnects the underlying socket, and marks the client as disconnected + * Disconnects the underlying socket, and marks the client as disconnected. + * * @return bool */ public function disconnect() @@ -166,7 +174,8 @@ public function disconnect() } /** - * Returns whether the client is currently connected + * Returns whether the client is currently connected. + * * @return bool true if connected */ public function isConnected() @@ -175,37 +184,39 @@ public function isConnected() } /** - * Wait before sending next message + * Wait before sending next message. */ - private function waitForDelay() + private function waitForDelay(): void { if ($this->lastMessageSentTime) { - $currentTime = (int) (microtime(true) * 1000); + $currentTime = (int) (\microtime(true) * 1000); // if not enough time was spent until last message was sent, wait if ($this->lastMessageSentTime + $this->delay > $currentTime) { $timeToWait = ($this->lastMessageSentTime + $this->delay) - $currentTime; - usleep($timeToWait * 1000); + \usleep($timeToWait * 1000); } } - $this->lastMessageSentTime = (int) (microtime(true) * 1000); + $this->lastMessageSentTime = (int) (\microtime(true) * 1000); } /** - * Sends the given message and returns a response reader + * Sends the given message and returns a response reader. + * * @param Message $message + * * @throws CommunicationException + * * @return ResponseReader */ public function sendMessage(Message $message): ResponseReader { - // if delay enabled wait before sending message if ($this->delay > 0) { $this->waitForDelay(); } - $sent = $this->wsClient->sendData((string)$message); + $sent = $this->wsClient->sendData((string) $message); if (!$sent) { $message = 'Message could not be sent.'; @@ -223,9 +234,11 @@ public function sendMessage(Message $message): ResponseReader } /** - * @param Message $message + * @param Message $message * @param int|null $timeout + * * @throws OperationTimedOut + * * @return Response */ public function sendMessageSync(Message $message, int $timeout = null): Response @@ -237,8 +250,10 @@ public function sendMessageSync(Message $message, int $timeout = null): Response } /** - * Create a session for the given target id + * Create a session for the given target id. + * * @param string $targetId + * * @return Session */ public function createSession($targetId): Session @@ -254,8 +269,8 @@ public function createSession($targetId): Session $this->sessions[$sessionId] = $session; - $session->on('destroyed', function () use ($sessionId) { - $this->logger->debug('✘ session(' . $sessionId . ') was destroyed and unreferenced.'); + $session->on('destroyed', function () use ($sessionId): void { + $this->logger->debug('✘ session('.$sessionId.') was destroyed and unreferenced.'); unset($this->sessions[$sessionId]); }); @@ -263,19 +278,20 @@ public function createSession($targetId): Session } /** - * Receive and stack data from the socket + * Receive and stack data from the socket. */ - private function receiveData() + private function receiveData(): void { - $this->receivedData = array_merge($this->receivedData, $this->wsClient->receiveData()); + $this->receivedData = \array_merge($this->receivedData, $this->wsClient->receiveData()); } /** - * Read data from CRI and store messages + * Read data from CRI and store messages. * - * @return bool true if data were received * @throws CannotReadResponse * @throws InvalidResponse + * + * @return bool true if data were received */ public function readData() { @@ -296,7 +312,7 @@ public function readLine() } // dispatch first line of buffer - $datum = array_shift($this->receivedData); + $datum = \array_shift($this->receivedData); if ($datum) { return $this->dispatchMessage($datum); } @@ -305,64 +321,64 @@ public function readLine() } /** - * Dispatches the message and either stores the response or emits an event - * @return bool + * Dispatches the message and either stores the response or emits an event. + * * @throws InvalidResponse + * + * @return bool + * * @internal */ private function dispatchMessage(string $message, Session $session = null) { - // responses come as json string - $response = json_decode($message, true); + $response = \json_decode($message, true); // if json not valid throw exception - $jsonError = json_last_error(); - if ($jsonError !== JSON_ERROR_NONE) { + $jsonError = \json_last_error(); + if (\JSON_ERROR_NONE !== $jsonError) { if ($this->isStrict()) { - throw new CannotReadResponse( - sprintf( - 'Response from chrome remote interface is not a valid json response. JSON error: %s', - $jsonError - ) - ); + throw new CannotReadResponse(\sprintf('Response from chrome remote interface is not a valid json response. JSON error: %s', $jsonError)); } + return false; } // response must be array - if (!is_array($response)) { + if (!\is_array($response)) { if ($this->isStrict()) { throw new CannotReadResponse('Response from chrome remote interface was not a valid array'); } + return false; } // id is required to identify the response if (!isset($response['id'])) { if (isset($response['method'])) { - if ($response['method'] == 'Target.receivedMessageFromTarget') { + if ('Target.receivedMessageFromTarget' == $response['method']) { $session = $this->sessions[$response['params']['sessionId']]; + return $this->dispatchMessage($response['params']['message'], $session); } else { if ($session) { $this->logger->debug( - 'session(' . $session->getSessionId() . '): ⇶ dispatching method:' . $response['method'] + 'session('.$session->getSessionId().'): ⇶ dispatching method:'.$response['method'] ); - $session->emit('method:' . $response['method'], [$response['params']]); + $session->emit('method:'.$response['method'], [$response['params']]); } else { - $this->logger->debug('connection: ⇶ dispatching method:' . $response['method']); - $this->emit('method:' . $response['method'], [$response['params']]); + $this->logger->debug('connection: ⇶ dispatching method:'.$response['method']); + $this->emit('method:'.$response['method'], [$response['params']]); } } + return false; } if ($this->isStrict()) { - throw new InvalidResponse( - 'Response from chrome remote interface did not provide a valid message id' - ); + throw new InvalidResponse('Response from chrome remote interface did not provide a valid message id'); } + return false; } @@ -373,24 +389,28 @@ private function dispatchMessage(string $message, Session $session = null) } /** - * True if a response for the given id exists + * True if a response for the given id exists. + * * @param string $id + * * @return bool */ public function hasResponseForId($id) { - return array_key_exists($id, $this->responseBuffer); + return \array_key_exists($id, $this->responseBuffer); } /** * @param string $id + * * @return array|null */ public function getResponseForId($id) { - if (array_key_exists($id, $this->responseBuffer)) { + if (\array_key_exists($id, $this->responseBuffer)) { $data = $this->responseBuffer[$id]; unset($this->responseBuffer[$id]); + return $data; } diff --git a/src/Communication/Message.php b/src/Communication/Message.php index d5243c88..52c58850 100644 --- a/src/Communication/Message.php +++ b/src/Communication/Message.php @@ -15,11 +15,11 @@ class Message { /** * global message id auto incremented for each message sent. + * * @var int */ private static $messageId = 0; - /** * @var int */ @@ -36,7 +36,8 @@ class Message protected $params; /** - * get the last generated message id + * get the last generated message id. + * * @return int */ public static function getLastMessageId() @@ -46,7 +47,7 @@ public static function getLastMessageId() /** * @param string $method - * @param array $params + * @param array $params */ public function __construct(string $method, array $params = []) { @@ -79,15 +80,12 @@ public function getParams(): array return $this->params; } - /** - * @inheritdoc - */ public function __toString(): string { - return json_encode([ - 'id' => $this->getId(), - 'method' => $this->getMethod(), - 'params' => (object) $this->getParams() + return \json_encode([ + 'id' => $this->getId(), + 'method' => $this->getMethod(), + 'params' => (object) $this->getParams(), ]); } } diff --git a/src/Communication/Response.php b/src/Communication/Response.php index e245a415..9129e90a 100644 --- a/src/Communication/Response.php +++ b/src/Communication/Response.php @@ -27,16 +27,18 @@ public function __construct(array $data, Message $message) } /** - * True if the response is error free + * True if the response is error free. + * * @return bool */ public function isSuccessful() { - return !array_key_exists('error', $this->data); + return !\array_key_exists('error', $this->data); } /** * Get the error message if set. + * * @return string|null */ public function getErrorMessage(bool $extended = true) @@ -51,15 +53,16 @@ public function getErrorMessage(bool $extended = true) $message[] = $this->data['error']['message']; } - if ($extended && isset($this->data['error']['data']) && is_string($this->data['error']['data'])) { + if ($extended && isset($this->data['error']['data']) && \is_string($this->data['error']['data'])) { $message[] = $this->data['error']['data']; } - return implode(' - ', $message); + return \implode(' - ', $message); } /** * Get the error code if set. + * * @return string|null */ public function getErrorCode() @@ -69,6 +72,7 @@ public function getErrorCode() /** * @param string $name + * * @return mixed */ public function getResultData($name) @@ -85,7 +89,8 @@ public function getMessage(): Message } /** - * The data returned by chrome dev tools + * The data returned by chrome dev tools. + * * @return array */ public function getData(): array @@ -94,15 +99,15 @@ public function getData(): array } /** - * @inheritdoc + * {@inheritdoc} */ public function offsetExists($offset) { - return array_key_exists($offset, $this->data); + return \array_key_exists($offset, $this->data); } /** - * @inheritdoc + * {@inheritdoc} */ public function offsetGet($offset) { @@ -110,17 +115,17 @@ public function offsetGet($offset) } /** - * @inheritdoc + * {@inheritdoc} */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { throw new \Exception('Responses are immutable'); } /** - * @inheritdoc + * {@inheritdoc} */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { throw new \Exception('Responses are immutable'); } diff --git a/src/Communication/ResponseReader.php b/src/Communication/ResponseReader.php index 8cf373b9..ee978943 100644 --- a/src/Communication/ResponseReader.php +++ b/src/Communication/ResponseReader.php @@ -12,8 +12,8 @@ namespace HeadlessChromium\Communication; use HeadlessChromium\Exception\NoResponseAvailable; -use HeadlessChromium\Utils; use HeadlessChromium\Exception\OperationTimedOut; +use HeadlessChromium\Utils; class ResponseReader { @@ -34,7 +34,8 @@ class ResponseReader /** * Response constructor. - * @param Message $message + * + * @param Message $message * @param Connection $connection */ public function __construct(Message $message, Connection $connection) @@ -44,16 +45,18 @@ public function __construct(Message $message, Connection $connection) } /** - * True if a response is available + * True if a response is available. + * * @return bool */ public function hasResponse() { - return $this->response !== null; + return null !== $this->response; } /** - * the message to get a response for + * the message to get a response for. + * * @return Message */ public function getMessage(): Message @@ -62,7 +65,8 @@ public function getMessage(): Message } /** - * The connection to check messages for + * The connection to check messages for. + * * @return Connection */ public function getConnection(): Connection @@ -71,13 +75,14 @@ public function getConnection(): Connection } /** - * Get the response + * Get the response. * * Note: response will always be missing until checkForResponse is called * and the response is available in the buffer * - * @return Response * @throws NoResponseAvailable + * + * @return Response */ public function getResponse(): Response { @@ -89,12 +94,14 @@ public function getResponse(): Response } /** - * Wait for a response + * Wait for a response. + * * @param int $timeout time to wait for a response (milliseconds) - * @return Response * * @throws NoResponseAvailable * @throws OperationTimedOut + * + * @return Response */ public function waitForResponse(int $timeout = null): Response { @@ -109,9 +116,12 @@ public function waitForResponse(int $timeout = null): Response } /** - * To be used in waitForResponse method - * @return \Generator|Response + * To be used in waitForResponse method. + * * @throws NoResponseAvailable + * + * @return \Generator|Response + * * @internal */ private function waitForResponseGenerator() @@ -135,6 +145,7 @@ private function waitForResponseGenerator() /** * Check in the connection if a response exists for the message and store it if the response exists. + * * @return bool */ public function checkForResponse() @@ -149,6 +160,7 @@ public function checkForResponse() // if response exists store it if ($this->connection->hasResponseForId($id)) { $this->response = new Response($this->connection->getResponseForId($id), $this->message); + return true; } @@ -162,6 +174,7 @@ public function checkForResponse() // if response store it if ($this->connection->hasResponseForId($id)) { $this->response = new Response($this->connection->getResponseForId($id), $this->message); + return true; } diff --git a/src/Communication/Session.php b/src/Communication/Session.php index 999b23a2..28dec10e 100644 --- a/src/Communication/Session.php +++ b/src/Communication/Session.php @@ -40,21 +40,24 @@ class Session extends EventEmitter /** * Session constructor. - * @param string $targetId - * @param string $sessionId + * + * @param string $targetId + * @param string $sessionId * @param Connection $connection */ public function __construct(string $targetId, string $sessionId, Connection $connection) { - $this->sessionId = $sessionId; - $this->targetId = $targetId; + $this->sessionId = $sessionId; + $this->targetId = $targetId; $this->connection = $connection; } /** * @param Message $message - * @return SessionResponseReader + * * @throws CommunicationException + * + * @return SessionResponseReader */ public function sendMessage(Message $message): SessionResponseReader { @@ -64,7 +67,7 @@ public function sendMessage(Message $message): SessionResponseReader $topResponse = $this->getConnection()->sendMessage(new Message('Target.sendMessageToTarget', [ 'message' => (string) $message, - 'sessionId' => $this->getSessionId() + 'sessionId' => $this->getSessionId(), ])); return new SessionResponseReader($topResponse, $message); @@ -72,10 +75,12 @@ public function sendMessage(Message $message): SessionResponseReader /** * @param Message $message - * @param int $timeout - * @return Response + * @param int $timeout + * * @throws NoResponseAvailable * @throws CommunicationException + * + * @return Response */ public function sendMessageSync(Message $message, int $timeout = null): Response { @@ -114,14 +119,16 @@ public function getConnection() if ($this->destroyed) { throw new TargetDestroyed('The session was destroyed.'); } + return $this->connection; } /** - * Marks the session as destroyed + * Marks the session as destroyed. + * * @internal */ - public function destroy() + public function destroy(): void { if ($this->destroyed) { throw new TargetDestroyed('The session was already destroyed.'); diff --git a/src/Communication/Socket/MockSocket.php b/src/Communication/Socket/MockSocket.php index 00393f74..66a450b5 100644 --- a/src/Communication/Socket/MockSocket.php +++ b/src/Communication/Socket/MockSocket.php @@ -12,7 +12,7 @@ namespace HeadlessChromium\Communication\Socket; /** - * A mock adapter for unit tests + * A mock adapter for unit tests. */ class MockSocket implements SocketInterface { @@ -25,9 +25,8 @@ class MockSocket implements SocketInterface protected $shouldConnect = true; - /** - * @inheritdoc + * {@inheritdoc} */ public function sendData($data) { @@ -38,17 +37,17 @@ public function sendData($data) $this->sentData[] = $data; if (!empty($this->receivedDataForNextMessage)) { - $data = json_decode($data, true); + $data = \json_decode($data, true); if ($data['id']) { - $next = array_shift($this->receivedDataForNextMessage); - $next = json_decode($next, true); + $next = \array_shift($this->receivedDataForNextMessage); + $next = \json_decode($next, true); $next['id'] = $data['id']; - $this->receivedData[] = json_encode($next); + $this->receivedData[] = \json_encode($next); - if (isset($data['method']) && $data['method'] == 'Target.sendMessageToTarget') { - $next['id']--; - $this->receivedData[] = json_encode($next); + if (isset($data['method']) && 'Target.sendMessageToTarget' == $data['method']) { + --$next['id']; + $this->receivedData[] = \json_encode($next); } } } @@ -57,15 +56,15 @@ public function sendData($data) } /** - * resets the data stored with sendData + * resets the data stored with sendData. */ - public function flushData() + public function flushData(): void { $this->sentData = []; } /** - * gets the data stored with sendData + * gets the data stored with sendData. */ public function getSentData() { @@ -73,21 +72,23 @@ public function getSentData() } /** - * @inheritdoc + * {@inheritdoc} */ public function receiveData(): array { $data = $this->receivedData; $this->receivedData = []; + return $data; } /** - * Add data to be returned with receiveData + * Add data to be returned with receiveData. + * * @param bool $forNextMessage true to set the response id automatically - * for next message (can stack for multiple messages + * for next message (can stack for multiple messages */ - public function addReceivedData($data, $forNextMessage = false) + public function addReceivedData($data, $forNextMessage = false): void { if ($forNextMessage) { $this->receivedDataForNextMessage[] = $data; @@ -97,16 +98,17 @@ public function addReceivedData($data, $forNextMessage = false) } /** - * @inheritdoc + * {@inheritdoc} */ public function connect() { $this->isConnected = $this->shouldConnect; + return $this->isConnected; } /** - * @inheritdoc + * {@inheritdoc} */ public function isConnected() { @@ -114,11 +116,12 @@ public function isConnected() } /** - * @inheritdoc + * {@inheritdoc} */ public function disconnect($reason = 1000) { $this->isConnected = false; + return true; } } diff --git a/src/Communication/Socket/SocketInterface.php b/src/Communication/Socket/SocketInterface.php index f8c37ab5..d151617c 100644 --- a/src/Communication/Socket/SocketInterface.php +++ b/src/Communication/Socket/SocketInterface.php @@ -12,43 +12,44 @@ namespace HeadlessChromium\Communication\Socket; /** - * A simplified interface to wrap a socket client + * A simplified interface to wrap a socket client. */ interface SocketInterface { /** - * Sends data to the socket + * Sends data to the socket. * * @return bool whether the data were sent */ public function sendData($data); /** - * Receives data sent by the server + * Receives data sent by the server. * * @return array Payload received since the last call to receive() */ public function receiveData(): array; /** - * Connect to the server + * Connect to the server. * * @return bool Whether a new connection was made */ public function connect(); /** - * Whether the client is currently connected + * Whether the client is currently connected. * - * @return boolean + * @return bool */ public function isConnected(); /** - * Disconnects the underlying socket, and marks the client as disconnected + * Disconnects the underlying socket, and marks the client as disconnected. * * @param int $reason see http://tools.ietf.org/html/rfc6455#section-7.4 - * @return boolean + * + * @return bool */ public function disconnect($reason = 1000); } diff --git a/src/Communication/Socket/Wrench.php b/src/Communication/Socket/Wrench.php index f2ba5f4e..fa3ffa38 100644 --- a/src/Communication/Socket/Wrench.php +++ b/src/Communication/Socket/Wrench.php @@ -23,7 +23,8 @@ class Wrench implements SocketInterface, LoggerAwareInterface use LoggerAwareTrait; /** - * An auto incremented counter to uniquely identify each socket instance + * An auto incremented counter to uniquely identify each socket instance. + * * @var int */ private static $socketIdCounter = 0; @@ -34,7 +35,8 @@ class Wrench implements SocketInterface, LoggerAwareInterface protected $client; /** - * Id of this socket generated from self::$socketIdCounter + * Id of this socket generated from self::$socketIdCounter. + * * @var int */ protected $socketId = 0; @@ -52,19 +54,19 @@ public function __construct(WrenchClient $client, LoggerInterface $logger = null } /** - * @inheritdoc + * {@inheritdoc} */ public function sendData($data) { // log - $this->logger->debug('socket(' . $this->socketId . '): → sending data:' . $data); + $this->logger->debug('socket('.$this->socketId.'): → sending data:'.$data); // send data return $this->client->sendData($data); } /** - * @inheritdoc + * {@inheritdoc} */ public function receiveData(): array { @@ -79,7 +81,7 @@ public function receiveData(): array $data[] = $dataString; // log - $this->logger->debug('socket(' . $this->socketId . '): ← receiving data:' . $dataString); + $this->logger->debug('socket('.$this->socketId.'): ← receiving data:'.$dataString); } } @@ -87,28 +89,28 @@ public function receiveData(): array } /** - * @inheritdoc + * {@inheritdoc} */ public function connect() { // log - $this->logger->debug('socket(' . $this->socketId . '): connecting'); + $this->logger->debug('socket('.$this->socketId.'): connecting'); $connected = $this->client->connect(); if ($connected) { // log - $this->logger->debug('socket(' . $this->socketId . '): ✓ connected'); + $this->logger->debug('socket('.$this->socketId.'): ✓ connected'); } else { // log - $this->logger->debug('socket(' . $this->socketId . '): ✗ could not connect'); + $this->logger->debug('socket('.$this->socketId.'): ✗ could not connect'); } return $connected; } /** - * @inheritdoc + * {@inheritdoc} */ public function isConnected() { @@ -116,21 +118,21 @@ public function isConnected() } /** - * @inheritdoc + * {@inheritdoc} */ public function disconnect($reason = 1000) { // log - $this->logger->debug('socket(' . $this->socketId . '): disconnecting'); + $this->logger->debug('socket('.$this->socketId.'): disconnecting'); $disconnected = $this->client->disconnect($reason); if ($disconnected) { // log - $this->logger->debug('socket(' . $this->socketId . '): ✓ disconnected'); + $this->logger->debug('socket('.$this->socketId.'): ✓ disconnected'); } else { // log - $this->logger->debug('socket(' . $this->socketId . '): ✗ could not disconnect'); + $this->logger->debug('socket('.$this->socketId.'): ✗ could not disconnect'); } return $disconnected; diff --git a/src/Communication/Target.php b/src/Communication/Target.php index 550ed954..7b8aa605 100644 --- a/src/Communication/Target.php +++ b/src/Communication/Target.php @@ -62,10 +62,11 @@ public function getSession(): Session } /** - * Marks the target as destroyed + * Marks the target as destroyed. + * * @internal */ - public function destroy() + public function destroy(): void { if ($this->destroyed) { throw new TargetDestroyed('The target was already destroyed.'); @@ -88,8 +89,10 @@ public function isDestroyed(): bool } /** - * Get target info value by it's name or null if it does not exist + * Get target info value by it's name or null if it does not exist. + * * @param string $infoName + * * @return mixed */ public function getTargetInfo($infoName) @@ -99,10 +102,12 @@ public function getTargetInfo($infoName) /** * To be called when Target.targetInfoChanged is triggered. + * * @param array $targetInfo + * * @internal */ - public function targetInfoChanged($targetInfo) + public function targetInfoChanged($targetInfo): void { $this->targetInfo = $targetInfo; } diff --git a/src/Cookies/Cookie.php b/src/Cookies/Cookie.php index 63021f88..37651d81 100644 --- a/src/Cookies/Cookie.php +++ b/src/Cookies/Cookie.php @@ -23,8 +23,8 @@ class Cookie implements \ArrayAccess */ public function __construct(array $data) { - if (isset($data['expires']) && is_string($data['expires']) && !is_numeric($data['expires'])) { - $data['expires'] = strtotime($data['expires']); + if (isset($data['expires']) && \is_string($data['expires']) && !\is_numeric($data['expires'])) { + $data['expires'] = \strtotime($data['expires']); } $this->data = $data; @@ -55,15 +55,15 @@ public function getDomain() } /** - * @inheritdoc + * {@inheritdoc} */ public function offsetExists($offset) { - return array_key_exists($offset, $this->data); + return \array_key_exists($offset, $this->data); } /** - * @inheritdoc + * {@inheritdoc} */ public function offsetGet($offset) { @@ -71,17 +71,17 @@ public function offsetGet($offset) } /** - * @inheritdoc + * {@inheritdoc} */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { throw new \RuntimeException('Cannot set cookie values'); } /** - * @inheritdoc + * {@inheritdoc} */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { throw new \RuntimeException('Cannot unset cookie values'); } @@ -89,13 +89,15 @@ public function offsetUnset($offset) /** * @param string $name * @param string $value - * @param array $params + * @param array $params + * * @return Cookie */ public static function create($name, $value, array $params = []) { $params['name'] = $name; $params['value'] = $value; - return new Cookie($params); + + return new self($params); } } diff --git a/src/Cookies/CookiesCollection.php b/src/Cookies/CookiesCollection.php index a4b2217a..fcc1ca6f 100644 --- a/src/Cookies/CookiesCollection.php +++ b/src/Cookies/CookiesCollection.php @@ -25,7 +25,7 @@ public function __construct(array $cookies = null) { if ($cookies) { foreach ($cookies as $cookie) { - if (is_array($cookie)) { + if (\is_array($cookie)) { $cookie = new Cookie($cookie); } $this->addCookie($cookie); @@ -34,15 +34,15 @@ public function __construct(array $cookies = null) } /** - * Adds a cookie + * Adds a cookie. */ - public function addCookie(Cookie $cookie) + public function addCookie(Cookie $cookie): void { $this->cookies[] = $cookie; } /** - * @inheritdoc + * {@inheritdoc} */ public function getIterator() { @@ -50,28 +50,31 @@ public function getIterator() } /** - * @inheritdoc + * {@inheritdoc} */ public function count() { - return count($this->cookies); + return \count($this->cookies); } /** - * Get the cookie at the given index + * Get the cookie at the given index. + * * @param int $i + * * @return Cookie */ public function getAt($i): Cookie { if (!isset($this->cookies[$i])) { - throw new \RuntimeException(sprintf('No cookie at index %s', $i)); + throw new \RuntimeException(\sprintf('No cookie at index %s', $i)); } + return $this->cookies[$i]; } /** - * Find cookies with matching values + * Find cookies with matching values. * * usage: * @@ -85,17 +88,18 @@ public function getAt($i): Cookie * * @param string $param * @param string $value + * * @return CookiesCollection */ public function filterBy(string $param, string $value) { - return new CookiesCollection(array_filter($this->cookies, function (Cookie $cookie) use ($param, $value) { + return new self(\array_filter($this->cookies, function (Cookie $cookie) use ($param, $value) { return $cookie[$param] == $value; })); } /** - * Find first cookies with matching value + * Find first cookies with matching value. * * usage: * @@ -110,6 +114,7 @@ public function filterBy(string $param, string $value) * * @param string $param * @param string $value + * * @return Cookie|null */ public function findOneBy(string $param, string $value) @@ -119,6 +124,7 @@ public function findOneBy(string $param, string $value) return $cookie; } } + return null; } } diff --git a/src/Exception/BrowserConnectionFailed.php b/src/Exception/BrowserConnectionFailed.php index 82d4a7a8..5eb8f18e 100644 --- a/src/Exception/BrowserConnectionFailed.php +++ b/src/Exception/BrowserConnectionFailed.php @@ -13,5 +13,4 @@ class BrowserConnectionFailed extends \Exception { - } diff --git a/src/Exception/CommunicationException.php b/src/Exception/CommunicationException.php index 80e5d88f..f6a48819 100644 --- a/src/Exception/CommunicationException.php +++ b/src/Exception/CommunicationException.php @@ -13,5 +13,4 @@ class CommunicationException extends \Exception { - } diff --git a/src/Exception/CommunicationException/CannotReadResponse.php b/src/Exception/CommunicationException/CannotReadResponse.php index f450368c..d2578fef 100644 --- a/src/Exception/CommunicationException/CannotReadResponse.php +++ b/src/Exception/CommunicationException/CannotReadResponse.php @@ -15,5 +15,4 @@ class CannotReadResponse extends CommunicationException { - } diff --git a/src/Exception/CommunicationException/InvalidResponse.php b/src/Exception/CommunicationException/InvalidResponse.php index 1decd21e..f77ad25f 100644 --- a/src/Exception/CommunicationException/InvalidResponse.php +++ b/src/Exception/CommunicationException/InvalidResponse.php @@ -15,5 +15,4 @@ class InvalidResponse extends CommunicationException { - } diff --git a/src/Exception/CommunicationException/ResponseHasError.php b/src/Exception/CommunicationException/ResponseHasError.php index ce01ab8a..d734fbec 100644 --- a/src/Exception/CommunicationException/ResponseHasError.php +++ b/src/Exception/CommunicationException/ResponseHasError.php @@ -15,5 +15,4 @@ class ResponseHasError extends CommunicationException { - } diff --git a/src/Exception/EvaluationFailed.php b/src/Exception/EvaluationFailed.php index f3fd368f..46a6fa7b 100644 --- a/src/Exception/EvaluationFailed.php +++ b/src/Exception/EvaluationFailed.php @@ -13,5 +13,4 @@ class EvaluationFailed extends \Exception { - } diff --git a/src/Exception/FilesystemException.php b/src/Exception/FilesystemException.php index 53aeef46..9e81f312 100644 --- a/src/Exception/FilesystemException.php +++ b/src/Exception/FilesystemException.php @@ -13,5 +13,4 @@ class FilesystemException extends \Exception { - } diff --git a/src/Exception/JavascriptException.php b/src/Exception/JavascriptException.php index 6cfb7f11..383b5d79 100644 --- a/src/Exception/JavascriptException.php +++ b/src/Exception/JavascriptException.php @@ -13,5 +13,4 @@ class JavascriptException extends \Exception { - } diff --git a/src/Exception/NavigationExpired.php b/src/Exception/NavigationExpired.php index de72acba..b58fad29 100644 --- a/src/Exception/NavigationExpired.php +++ b/src/Exception/NavigationExpired.php @@ -13,5 +13,4 @@ class NavigationExpired extends \Exception { - } diff --git a/src/Exception/NoResponseAvailable.php b/src/Exception/NoResponseAvailable.php index 75f327fe..d4df245c 100644 --- a/src/Exception/NoResponseAvailable.php +++ b/src/Exception/NoResponseAvailable.php @@ -13,5 +13,4 @@ class NoResponseAvailable extends \Exception { - } diff --git a/src/Exception/OperationTimedOut.php b/src/Exception/OperationTimedOut.php index d51cb8dc..73b569c7 100644 --- a/src/Exception/OperationTimedOut.php +++ b/src/Exception/OperationTimedOut.php @@ -13,5 +13,4 @@ class OperationTimedOut extends \Exception { - } diff --git a/src/Exception/PdfFailed.php b/src/Exception/PdfFailed.php index 5593af9b..ff96f3a1 100644 --- a/src/Exception/PdfFailed.php +++ b/src/Exception/PdfFailed.php @@ -13,5 +13,4 @@ class PdfFailed extends \Exception { - } diff --git a/src/Exception/ScreenshotFailed.php b/src/Exception/ScreenshotFailed.php index a659888d..cc6a6d89 100644 --- a/src/Exception/ScreenshotFailed.php +++ b/src/Exception/ScreenshotFailed.php @@ -13,5 +13,4 @@ class ScreenshotFailed extends \Exception { - } diff --git a/src/Exception/TargetDestroyed.php b/src/Exception/TargetDestroyed.php index dcff21a9..b6ca33f6 100644 --- a/src/Exception/TargetDestroyed.php +++ b/src/Exception/TargetDestroyed.php @@ -13,5 +13,4 @@ class TargetDestroyed extends \RuntimeException { - } diff --git a/src/Frame.php b/src/Frame.php index 038e0583..cdb05e64 100644 --- a/src/Frame.php +++ b/src/Frame.php @@ -42,6 +42,7 @@ class Frame /** * Frame constructor. + * * @param array $frameData */ public function __construct(array $frameData) @@ -54,7 +55,7 @@ public function __construct(array $frameData) /** * @internal */ - public function onLifecycleEvent(array $params) + public function onLifecycleEvent(array $params): void { if (self::LIFECYCLE_INIT === $params['name']) { $this->lifeCycleEvents = []; @@ -62,7 +63,6 @@ public function onLifecycleEvent(array $params) $this->frameId = $params['frameId']; } - $this->lifeCycleEvents[$params['name']] = $params['timestamp']; } @@ -77,7 +77,7 @@ public function getExecutionContextId(): int /** * @param int $executionContextId */ - public function setExecutionContextId(int $executionContextId) + public function setExecutionContextId(int $executionContextId): void { $this->executionContextId = $executionContextId; } @@ -100,6 +100,7 @@ public function getLatestLoaderId(): string /** * Gets the life cycle events of the frame with the time they occurred at. + * * @return array */ public function getLifeCycle(): array diff --git a/src/FrameManager.php b/src/FrameManager.php index 5efac0e5..8c13aa47 100644 --- a/src/FrameManager.php +++ b/src/FrameManager.php @@ -46,7 +46,7 @@ public function __construct(Page $page, array $frameTree) // TODO listen for frame events // update frame on init - $this->page->getSession()->on('method:Page.lifecycleEvent', function (array $params) { + $this->page->getSession()->on('method:Page.lifecycleEvent', function (array $params): void { if (isset($this->frames[$params['frameId']])) { $frame = $this->frames[$params['frameId']]; $frame->onLifecycleEvent($params); @@ -54,7 +54,7 @@ public function __construct(Page $page, array $frameTree) }); // attach context id to frame - $this->page->getSession()->on('method:Runtime.executionContextCreated', function (array $params) { + $this->page->getSession()->on('method:Runtime.executionContextCreated', function (array $params): void { if (isset($params['context']['auxData']['frameId']) && $params['context']['auxData']['isDefault']) { if ($this->hasFrame($params['context']['auxData']['frameId'])) { $frame = $this->getFrame($params['context']['auxData']['frameId']); @@ -67,31 +67,36 @@ public function __construct(Page $page, array $frameTree) } /** - * Checks if the given frame exists + * Checks if the given frame exists. + * * @param string $frameId + * * @return bool */ public function hasFrame($frameId): bool { - return array_key_exists($frameId, $this->frames); + return \array_key_exists($frameId, $this->frames); } /** - * Get a frame given its id + * Get a frame given its id. + * * @param string $frameId + * * @return Frame */ public function getFrame($frameId): Frame { if (!isset($this->frames[$frameId])) { - throw new \RuntimeException(sprintf('No such frame "%s"', $frameId)); + throw new \RuntimeException(\sprintf('No such frame "%s"', $frameId)); } return $this->frames[$frameId]; } /** - * Gets the main frame + * Gets the main frame. + * * @return Frame */ public function getMainFrame(): Frame diff --git a/src/Input/Keyboard.php b/src/Input/Keyboard.php index a75e0d19..61cf3a41 100644 --- a/src/Input/Keyboard.php +++ b/src/Input/Keyboard.php @@ -35,17 +35,18 @@ public function __construct(Page $page) } /** - * Type a text string, char by char + * Type a text string, char by char. * - * @return $this * @throws \HeadlessChromium\Exception\CommunicationException * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this */ public function typeText(string $text) { $this->page->assertNotClosed(); - $length = strlen($text); + $length = \strlen($text); // apparently the first character doesn't work $this->page->getSession()->sendMessageSync(new Message('Input.dispatchKeyEvent', [ @@ -53,20 +54,20 @@ public function typeText(string $text) 'text' => '', ])); - for ($i = 0; $i < $length; $i++) { + for ($i = 0; $i < $length; ++$i) { $this->page->getSession()->sendMessageSync(new Message('Input.dispatchKeyEvent', [ 'type' => 'char', 'text' => $text[$i], ])); - usleep($this->sleep); + \usleep($this->sleep); } return $this; } /** - * Type a single raw key wich rawKeyDown + * Type a single raw key wich rawKeyDown. * * Example: * @@ -74,9 +75,10 @@ public function typeText(string $text) * $page->keyboard()->typeRawKey('Tab'); * ``` * - * @return $this * @throws \HeadlessChromium\Exception\CommunicationException * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this */ public function typeRawKey(string $key) { @@ -87,13 +89,13 @@ public function typeRawKey(string $key) 'key' => $key, ])); - usleep($this->sleep); + \usleep($this->sleep); return $this; } /** - * Sets the time interval between key strokes in milliseconds + * Sets the time interval between key strokes in milliseconds. * * @param int $milliseconds * diff --git a/src/Input/Mouse.php b/src/Input/Mouse.php index d5b15fe2..a8b372b2 100644 --- a/src/Input/Mouse.php +++ b/src/Input/Mouse.php @@ -40,12 +40,14 @@ public function __construct(Page $page) } /** - * @param int $x - * @param int $y + * @param int $x + * @param int $y * @param array|null $options - * @return $this + * * @throws \HeadlessChromium\Exception\CommunicationException * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this */ public function move(int $x, int $y, array $options = null) { @@ -66,11 +68,11 @@ public function move(int $x, int $y, array $options = null) } // move - for ($i = 1; $i <= $steps; $i++) { + for ($i = 1; $i <= $steps; ++$i) { $this->page->getSession()->sendMessageSync(new Message('Input.dispatchMouseEvent', [ 'x' => $originX + ($this->x - $originX) * ($i / $steps), 'y' => $originY + ($this->y - $originY) * ($i / $steps), - 'type' => 'mouseMoved' + 'type' => 'mouseMoved', ])); } @@ -89,7 +91,7 @@ public function press(array $options = null) 'y' => $this->y, 'type' => 'mousePressed', 'button' => $options['button'] ?? self::BUTTON_LEFT, - 'clickCount' => 1 + 'clickCount' => 1, ])); return $this; @@ -107,7 +109,7 @@ public function release(array $options = null) 'y' => $this->y, 'type' => 'mouseReleased', 'button' => $options['button'] ?? self::BUTTON_LEFT, - 'clickCount' => 1 + 'clickCount' => 1, ])); return $this; @@ -115,6 +117,7 @@ public function release(array $options = null) /** * @param array|null $options + * * @throws \HeadlessChromium\Exception\CommunicationException * @throws \HeadlessChromium\Exception\NoResponseAvailable */ @@ -127,38 +130,44 @@ public function click(array $options = null) } /** - * Scroll up using the mouse wheel + * Scroll up using the mouse wheel. * * @param int $distance Distance in pixels - * @return $this + * * @throws \HeadlessChromium\Exception\CommunicationException * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this */ public function scrollUp(int $distance) { - return $this->scroll((-1 * abs($distance))); + return $this->scroll((-1 * \abs($distance))); } /** - * Scroll down using the mouse wheel + * Scroll down using the mouse wheel. * * @param int $distance Distance in pixels - * @return $this + * * @throws \HeadlessChromium\Exception\CommunicationException * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this */ public function scrollDown(int $distance) { - return $this->scroll(abs($distance)); + return $this->scroll(\abs($distance)); } /** - * Scroll a positive or negative distance using the mouseWheel event type + * Scroll a positive or negative distance using the mouseWheel event type. * * @param int $distance Distance in pixels - * @return $this + * * @throws \HeadlessChromium\Exception\CommunicationException * @throws \HeadlessChromium\Exception\NoResponseAvailable + * + * @return $this */ private function scroll(int $distance) { @@ -169,11 +178,11 @@ private function scroll(int $distance) // scroll $this->page->getSession()->sendMessageSync(new Message('Input.dispatchMouseEvent', [ - 'type' => 'mouseWheel', - 'x' => $this->x, - 'y' => $this->y, + 'type' => 'mouseWheel', + 'x' => $this->x, + 'y' => $this->y, 'deltaX' => 0, - 'deltaY' => $distance + 'deltaY' => $distance, ])); // set new position after move diff --git a/src/Page.php b/src/Page.php index 02182c83..b59e1844 100644 --- a/src/Page.php +++ b/src/Page.php @@ -19,14 +19,14 @@ use HeadlessChromium\Exception\CommunicationException; use HeadlessChromium\Exception\NoResponseAvailable; use HeadlessChromium\Exception\TargetDestroyed; -use HeadlessChromium\Input\Mouse; use HeadlessChromium\Input\Keyboard; +use HeadlessChromium\Input\Mouse; use HeadlessChromium\PageUtils\CookiesGetter; use HeadlessChromium\PageUtils\PageEvaluation; use HeadlessChromium\PageUtils\PageLayoutMetrics; use HeadlessChromium\PageUtils\PageNavigation; -use HeadlessChromium\PageUtils\PageScreenshot; use HeadlessChromium\PageUtils\PagePdf; +use HeadlessChromium\PageUtils\PageScreenshot; use HeadlessChromium\PageUtils\ResponseWaiter; class Page @@ -46,19 +46,20 @@ class Page protected $frameManager; /** - * @var Mouse|Null + * @var Mouse|null */ protected $mouse; /** - * @var Keyboard|Null + * @var Keyboard|null */ protected $keyboard; /** * Page constructor. + * * @param Target $target - * @param array $frameTree + * @param array $frameTree */ public function __construct(Target $target, array $frameTree) { @@ -67,19 +68,20 @@ public function __construct(Target $target, array $frameTree) } /** - * Adds a script to be evaluated upon page navigation + * Adds a script to be evaluated upon page navigation. * * @param string $script - * @param array $options - * - onLoad: defer script execution after page has loaded (useful for scripts that require the dom to be populated) + * @param array $options + * - onLoad: defer script execution after page has loaded (useful for scripts that require the dom to be populated) + * * @throws CommunicationException * @throws NoResponseAvailable */ - public function addPreScript(string $script, array $options = []) + public function addPreScript(string $script, array $options = []): void { // defer script execution if (isset($options['onLoad']) && $options['onLoad']) { - $script = 'window.onload = () => {' . $script . '}'; + $script = 'window.onload = () => {'.$script.'}'; } // add script @@ -89,7 +91,7 @@ public function addPreScript(string $script, array $options = []) } /** - * Retrieves layout metrics of the page + * Retrieves layout metrics of the page. * * Example: * @@ -98,8 +100,9 @@ public function addPreScript(string $script, array $options = []) * $contentSize = $metrics->getContentSize(); * ``` * - * @return PageLayoutMetrics * @throws CommunicationException + * + * @return PageLayoutMetrics */ public function getLayoutMetrics() { @@ -123,7 +126,8 @@ public function getFrameManager(): FrameManager } /** - * Get the session this page is attached to + * Get the session this page is attached to. + * * @return Session */ public function getSession(): Session @@ -135,23 +139,25 @@ public function getSession(): Session /** * Sets the HTTP header necessary for basic authentication. + * * @param string $username * @param string $password */ - public function setBasicAuthHeader(string $username, string $password) + public function setBasicAuthHeader(string $username, string $password): void { - $header = base64_encode($username . ':' . $password); + $header = \base64_encode($username.':'.$password); $this->getSession()->sendMessage(new Message( 'Network.setExtraHTTPHeaders', - ['headers' => ['Authorization' => 'Basic ' . $header]] + ['headers' => ['Authorization' => 'Basic '.$header]] )); } /** * Sets the path to save downloaded files. + * * @param string $path */ - public function setDownloadPath(string $path) + public function setDownloadPath(string $path): void { $this->getSession()->sendMessage(new Message( 'Page.setDownloadBehavior', @@ -161,11 +167,12 @@ public function setDownloadPath(string $path) /** * @param string $url - * @param array $options - * - strict: make waitForNAvigation to fail if a new navigation is initiated. Default: false + * @param array $options + * - strict: make waitForNAvigation to fail if a new navigation is initiated. Default: false * - * @return PageNavigation * @throws Exception\CommunicationException + * + * @return PageNavigation */ public function navigate(string $url, array $options = []) { @@ -175,7 +182,7 @@ public function navigate(string $url, array $options = []) } /** - * Evaluates the given string in the page context + * Evaluates the given string in the page context. * * Example: * @@ -185,8 +192,10 @@ public function navigate(string $url, array $options = []) * ``` * * @param string $expression - * @return PageEvaluation + * * @throws Exception\CommunicationException + * + * @return PageEvaluation */ public function evaluate(string $expression) { @@ -200,15 +209,16 @@ public function evaluate(string $expression) 'awaitPromise' => true, 'returnByValue' => true, 'expression' => $expression, - 'userGesture' => true + 'userGesture' => true, ] ) ); + return new PageEvaluation($reader, $currentLoaderId, $this); } /** - * Call a js function with the given argument in the page context + * Call a js function with the given argument in the page context. * * Example: * @@ -220,9 +230,11 @@ public function evaluate(string $expression) * ``` * * @param string $functionDeclaration - * @param array $arguments - * @return PageEvaluation + * @param array $arguments + * * @throws CommunicationException + * + * @return PageEvaluation */ public function callFunction(string $functionDeclaration, array $arguments = []): PageEvaluation { @@ -235,15 +247,15 @@ public function callFunction(string $functionDeclaration, array $arguments = []) 'Runtime.callFunctionOn', [ 'functionDeclaration' => $functionDeclaration, - 'arguments' => array_map(function ($arg) { + 'arguments' => \array_map(function ($arg) { return [ - 'value' => $arg + 'value' => $arg, ]; }, $arguments), 'executionContextId' => $executionContextId, 'awaitPromise' => true, 'returnByValue' => true, - 'userGesture' => true + 'userGesture' => true, ] ) ); @@ -252,7 +264,7 @@ public function callFunction(string $functionDeclaration, array $arguments = []) } /** - * Add a script tag to the page (ie.