Repository: zoonru/puphpeteer
Branch: zoon
Commit: 1c35dc91fc6b
Files: 64
Total size: 122.6 KB
Directory structure:
gitextract_n_rqg4xg/
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin/
│ └── console
├── composer.json
├── examples/
│ ├── 01_page_open.php
│ ├── 02_page_screenshot.php
│ └── 03_browserless.php
├── package.json
├── phpstan.neon
├── phpunit.xml
├── src/
│ ├── Command/
│ │ └── GenerateDocumentationCommand.php
│ ├── Puppeteer.php
│ ├── PuppeteerConnectionDelegate.js
│ ├── PuppeteerProcessDelegate.php
│ ├── Resources/
│ │ ├── Accessibility.php
│ │ ├── Browser.php
│ │ ├── BrowserContext.php
│ │ ├── BrowserLauncher.php
│ │ ├── BrowserWebSocketTransport.php
│ │ ├── Buffer.php
│ │ ├── CDPSession.php
│ │ ├── ConsoleMessage.php
│ │ ├── Coverage.php
│ │ ├── DevToolsTarget.php
│ │ ├── Dialog.php
│ │ ├── ElementHandle.php
│ │ ├── EventEmitter.php
│ │ ├── FileChooser.php
│ │ ├── Frame.php
│ │ ├── HTTPRequest.php
│ │ ├── HTTPResponse.php
│ │ ├── JSHandle.php
│ │ ├── Keyboard.php
│ │ ├── Mouse.php
│ │ ├── OtherTarget.php
│ │ ├── Page.php
│ │ ├── PageTarget.php
│ │ ├── Realm.php
│ │ ├── ScreenRecorder.php
│ │ ├── SecurityDetails.php
│ │ ├── Target.php
│ │ ├── TaskManager.php
│ │ ├── TimeoutError.php
│ │ ├── TimeoutSettings.php
│ │ ├── Touchscreen.php
│ │ ├── Tracing.php
│ │ ├── Uint8Array.php
│ │ ├── WaitTask.php
│ │ ├── WebWorker.php
│ │ └── WorkerTarget.php
│ ├── Traits/
│ │ ├── AliasesEvaluationMethods.php
│ │ └── AliasesSelectionMethods.php
│ ├── doc-generator.ts
│ └── get-puppeteer-version.js
└── tests/
├── DownloadTest.php
├── PuphpeteerTest.php
├── ResourceInstantiator.php
├── RiskyResource.php
├── TestCase.php
├── UntestableResource.php
└── resources/
├── index.html
├── stylesheet.css
└── worker.js
================================================
FILE CONTENTS
================================================
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
**Note:** PuPHPeteer is heavily based on [Rialto](https://github.com/nesk/rialto). For a complete overview of the changes, you might want to check out [Rialto's changelog](https://github.com/nesk/rialto/blob/master/CHANGELOG.md) too.
## [Unreleased]
_In progress…_
## [2.0.0] - 2020-12-01
### Added
- Support Puppeteer v5.5
- Support PHP 8
- Add documentation on all resources to provide autocompletion in IDEs
### Removed
- Drop support for PHP 7.1 and 7.2
## [1.6.0] - 2019-07-01
### Added
- Support Puppeteer v1.18
## [1.5.0] - 2019-03-17
### Added
- Support Puppeteer v1.13
- Make the `ElementHandle` resource extend the `JSHandle` one
### Fixed
- Add missing `Accessibility` resource
## [1.4.1] - 2018-11-27
### Added
- Support Puppeteer v1.10
## [1.4.0] - 2018-09-22
### Added
- Support Puppeteer v1.8
### Changed
- Detect resource types by using the constructor name
### Fixed
- Logs of initial pages are now retrieved
## [1.3.0] - 2018-08-20
### Added
- Add a `log_browser_console` option to log the output of Browser's console methods (`console.log`, `console.debug`, `console.table`, etc…) to the PHP logger
- Support Puppeteer v1.7
## [1.2.0] - 2018-07-25
### Added
- Support Puppeteer v1.6
### Changed
- Upgrade to Rialto v1.1
## [1.1.0] - 2018-06-12
### Added
- Support Puppeteer v1.5
- Add aliases for evaluation methods to the `ElementHandle` resource
- Support new Puppeteer v1.5 resources:
- `BrowserContext`
- `Worker`
### Fixed
- Fix Travis tests
## [1.0.0] - 2018-06-05
### Changed
- Change PHP's vendor name from `extractr-io` to `nesk`
- Change NPM's scope name from `@extractr-io` to `@nesk`
- Upgrade to Rialto v1
## [0.2.2] - 2018-04-20
### Added
- Support Puppeteer v1.3
- Test missing Puppeteer resources: `ConsoleMessage` and `Dialog`
- Show a warning in logs if Puppeteer's version doesn't match requirements
## [0.2.1] - 2018-04-09
### Changed
- Update Rialto version requirements
## [0.2.0] - 2018-02-19
### Added
- Support new Puppeteer v1.1 resources:
- `BrowserFetcher`
- `CDPSession`
- `Coverage`
- `SecurityDetails`
- Test Puppeteer resources
- Support PHPUnit v7
- Add Travis integration
### Changed
- Lock Puppeteer's version to v1.1 to avoid issues with forward compatibility
## 0.1.0 - 2018-01-29
First release
[Unreleased]: https://github.com/nesk/puphpeteer/compare/2.0.0...HEAD
[2.0.0]: https://github.com/nesk/puphpeteer/compare/1.6.0...2.0.0
[1.6.0]: https://github.com/nesk/puphpeteer/compare/1.5.0...1.6.0
[1.5.0]: https://github.com/nesk/puphpeteer/compare/1.4.1...1.5.0
[1.4.1]: https://github.com/nesk/puphpeteer/compare/1.4.0...1.4.1
[1.4.0]: https://github.com/nesk/puphpeteer/compare/1.3.0...1.4.0
[1.3.0]: https://github.com/nesk/puphpeteer/compare/1.2.0...1.3.0
[1.2.0]: https://github.com/nesk/puphpeteer/compare/1.1.0...1.2.0
[1.1.0]: https://github.com/nesk/puphpeteer/compare/1.0.0...1.1.0
[1.0.0]: https://github.com/nesk/puphpeteer/compare/0.2.2...1.0.0
[0.2.2]: https://github.com/nesk/puphpeteer/compare/0.2.1...0.2.2
[0.2.1]: https://github.com/nesk/puphpeteer/compare/0.2.0...0.2.1
[0.2.0]: https://github.com/nesk/puphpeteer/compare/0.1.0...0.2.0
================================================
FILE: LICENSE
================================================
Copyright (c) Johann Pardanaud
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.
================================================
FILE: README.md
================================================
# PuPHPeteer
<img src="https://user-images.githubusercontent.com/817508/100672192-dd258500-3361-11eb-845f-e8b5109752e4.png" style="max-width:100%;" width="190px" align="right">
[](http://php.net/)
[](https://packagist.org/packages/zoon/puphpeteer)
A [Puppeteer](https://github.com/GoogleChrome/puppeteer/) bridge for PHP, supporting the entire API. Based on [Rialto](https://github.com/zoonru/rialto/), a package to manage Node resources from PHP.
Here are some examples [borrowed from Puppeteer's documentation](https://github.com/GoogleChrome/puppeteer/blob/master/README.md#usage) and adapted to PHP's syntax:
**Example** - navigating to https://example.com and saving a screenshot as *example.png*:
```php
use Nesk\Puphpeteer\Puppeteer;
$puppeteer = new Puppeteer;
$browser = $puppeteer->launch();
$page = $browser->newPage();
$page->goto('https://example.com');
$page->screenshot(['path' => 'example.png']);
$browser->close();
```
**Example** - evaluate a script in the context of the page:
```php
use Nesk\Puphpeteer\Puppeteer;
use Nesk\Rialto\Data\JsFunction;
$puppeteer = new Puppeteer;
$browser = $puppeteer->launch();
$page = $browser->newPage();
$page->goto('https://example.com');
// Get the "viewport" of the page, as reported by the page.
$dimensions = $page->evaluate(JsFunction::createWithBody("
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
"));
printf('Dimensions: %s', print_r($dimensions, true));
$browser->close();
```
## Requirements and installation
This package requires PHP >= 7.3 and Node >= 8.
Install it with these two command lines:
```shell
composer require zoon/puphpeteer
npm install git+https://git@github.com/zoonru/puphpeteer.git#zoon
```
## Use with browserless
```shell
docker run --rm -p 3000:3000 ghcr.io/browserless/chrome
```
```php
$puppeteer = new Nesk\Puphpeteer\Puppeteer;
$options = [
'headless' => false,
'stealth'=> true,
'timeout'=> 5000,
'args' => [
'--window-size=1366,768',
],
];
$browser = $puppeteer->connect(['browserWSEndpoint' => 'ws://127.0.0.1:3000/chrome?launch='.urlencode(json_encode($options, JSON_UNESCAPED_UNICODE))]);
$page = $browser->newPage();
$page->goto('https://www.example.com');
$page->screenshot(['path' => 'example.png']);
$browser->close();
```
## Notable differences between PuPHPeteer and Puppeteer
### Puppeteer's class must be instantiated
Instead of requiring Puppeteer:
```js
const puppeteer = require('puppeteer');
```
You have to instantiate the `Puppeteer` class:
```php
$puppeteer = new Puppeteer;
```
This will create a new Node process controlled by PHP.
You can also pass some options to the constructor, see [Rialto's documentation](https://github.com/nesk/rialto/blob/master/docs/api.md#options). PuPHPeteer also extends these options:
```php
[
// Logs the output of Browser's console methods (console.log, console.debug, etc...) to the PHP logger
'log_browser_console' => false,
]
```
<details>
<summary><strong>⏱ Want to use some timeouts higher than 30 seconds in Puppeteer's API?</strong></summary> <br>
If you use some timeouts higher than 30 seconds, you will have to set a higher value for the `read_timeout` option (default: `35`):
```php
$puppeteer = new Puppeteer([
'read_timeout' => 65, // In seconds
]);
$puppeteer->launch()->newPage()->goto($url, [
'timeout' => 60000, // In milliseconds
]);
```
</details>
### No need to use the `await` keyword
With PuPHPeteer, every method call or property getting/setting is synchronous.
### Some methods have been aliased
The following methods have been aliased because PHP doesn't support the `$` character in method names:
- `$` => `querySelector`
- `$$` => `querySelectorAll`
- `$eval` => `querySelectorEval`
- `$$eval` => `querySelectorAllEval`
Use these aliases just like you would have used the original methods:
```php
$divs = $page->querySelectorAll('div');
// Runs the `//h2` as the XPath expression.
$xpath = $page->querySelectorAll('::-p-xpath(//h2)');
// div element that has Checkout as the inner text.
$text = $page->querySelector('div ::-p-text(Checkout)');
```
### Evaluated functions must be created with `JsFunction`
Functions evaluated in the context of the page must be written [with the `JsFunction` class](https://github.com/nesk/rialto/blob/master/docs/api.md#javascript-functions), the body of these functions must be written in JavaScript instead of PHP.
```php
use Nesk\Rialto\Data\JsFunction;
$pageFunction = JsFunction::createWithParameters(['element'])
->body("return element.textContent");
```
### Exceptions must be caught with `->tryCatch`
If an error occurs in Node, a `Node\FatalException` will be thrown and the process closed, you will have to create a new instance of `Puppeteer`.
To avoid that, you can ask Node to catch these errors by prepending your instruction with `->tryCatch`:
```php
use Nesk\Rialto\Exceptions\Node;
try {
$page->tryCatch->goto('invalid_url');
} catch (Node\Exception $exception) {
// Handle the exception...
}
```
Instead, a `Node\Exception` will be thrown, the Node process will stay alive and usable.
### Puppeteer plugins
Puppeteer-extra and puppeteer-extra-plugin-stealth plugins already added in npm requirements.
To use them, override js inclusion with js_extra option
```php
$puppeteer = new Puppeteer([
'js_extra' => /** @lang JavaScript */ "
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
instruction.setDefaultResource(puppeteer);
"
]);
```
## License
The MIT License (MIT). Please see [License File](LICENSE) for more information.
## Logo attribution
PuPHPeteer's logo is composed of:
- [Puppet](https://thenounproject.com/search/?q=puppet&i=52120) by Luis Prado from [the Noun Project](http://thenounproject.com/).
- [Elephant](https://thenounproject.com/search/?q=elephant&i=954119) by Lluisa Iborra from [the Noun Project](http://thenounproject.com/).
Thanks to [Laravel News](https://laravel-news.com/) for picking the icons and colors of the logo.
================================================
FILE: bin/console
================================================
#!/usr/bin/env php
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Symfony\Component\Console\Application;
use Nesk\Puphpeteer\Command\GenerateDocumentationCommand;
(new Application())
->add(new GenerateDocumentationCommand)
->getApplication()
->run();
================================================
FILE: composer.json
================================================
{
"name": "zoon/puphpeteer",
"description": "A Puppeteer bridge for PHP, supporting the entire API.",
"keywords": [
"php",
"puppeteer",
"headless-chrome",
"testing",
"web",
"developer-tools",
"automation"
],
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Johann Pardanaud",
"email": "pardanaud.j@gmail.com"
}
],
"require": {
"php": "^8.1",
"ext-json": "*",
"composer/semver": "^3.0",
"psr/log": "^3.0",
"zoon/rialto": "^1.6"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.2",
"monolog/monolog": "^3.0",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^11",
"symfony/console": "^7",
"symfony/filesystem": "^7",
"symfony/process": "^7",
"symfony/var-dumper": "^7"
},
"autoload": {
"psr-4": {
"Nesk\\Puphpeteer\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Nesk\\Puphpeteer\\Tests\\": "tests/"
}
},
"scripts": {
"post-install-cmd": "npm install",
"test": "./vendor/bin/phpunit",
"update-docs": "php bin/console doc:generate",
"stan": "vendor/bin/phpstan analyze src",
"format": "vendor/bin/php-cs-fixer fix src"
},
"config": {
"sort-packages": true
}
}
================================================
FILE: examples/01_page_open.php
================================================
<?php
require __DIR__ . '/../vendor/autoload.php';
use Nesk\Puphpeteer\Puppeteer;
use Nesk\Rialto\Data\JsFunction;
$puppeteer = new Puppeteer;
$browser = $puppeteer->launch();
$page = $browser->newPage();
$page->goto('https://example.com');
// Get the "viewport" of the page, as reported by the page.
$dimensions = $page->evaluate(JsFunction::createWithBody(/** @lang JavaScript */"
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
"));
printf('Dimensions: %s', print_r($dimensions, true));
$browser->close();
================================================
FILE: examples/02_page_screenshot.php
================================================
<?php
require __DIR__ . '/../vendor/autoload.php';
use Nesk\Puphpeteer\Puppeteer;
$puppeteer = new Puppeteer;
$browser = $puppeteer->launch();
$page = $browser->newPage();
$page->setViewport(['width' => 1366, 'height' => 768]);
$page->goto('https://example.com');
$page->screenshot(['path' => 'example.png']);
$browser->close();
================================================
FILE: examples/03_browserless.php
================================================
<?php
require __DIR__ . '/../vendor/autoload.php';
use Nesk\Puphpeteer\Puppeteer;
use Nesk\Rialto\Data\JsFunction;
use Symfony\Component\Console\Output\ConsoleOutput;
// TIMEOUT=-1 dont work properly
$dockerId = `docker run --rm -d -p 3000:3000 --platform=linux/amd64 -e "TOKEN=abc" -e "TIMEOUT=-1" --name=chrome ghcr.io/browserless/chrome:v2.39.0`;
echo "Started browserless with id $dockerId" . PHP_EOL;
sleep(2);
try {
$puppeteer = new Puppeteer([
'js_extra' => /** @lang JavaScript */ "
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
instruction.setDefaultResource(puppeteer);
",
'idle_timeout' => 90,
'read_timeout' => 120,
'log_browser_console' => true,
'log_node_console' => true,
'logger' => new Symfony\Component\Console\Logger\ConsoleLogger(new ConsoleOutput(\Symfony\Component\Console\Output\OutputInterface::VERBOSITY_VERY_VERBOSE)),
]);
$options = [
'timeout' => 120000,
'token' => 'abc',
'launch' => json_encode([
'headless' => false,
'stealth'=> true,
'blockAds' => false,
'timeout'=> 600_000,
'args' => [
'--window-size=1366,768',
'--lang=ru-RU',
'--incognito',
'--0',
],
], JSON_UNESCAPED_UNICODE),
];
$browser = $puppeteer->connect([
'browserWSEndpoint' => sprintf(
'ws://127.0.0.1:3000/chrome?%s',
http_build_query($options)
),
'ignoreHTTPSErrors' => true,
'ignoreDefaultArgs' => true,
]);
$page = $browser->newPage();
$page->setViewport(['width' => 1366, 'height' => 768]);
$page->goto('https://bot.sannysoft.com', ['timeout' => 10_000, 'waitUntil' => 'domcontentloaded']);
$page->waitForNetworkIdle(['idleTime' => 1_000, 'concurrency' => 1]);
// Get the "viewport" of the page, as reported by the page.
$dimensions = $page->evaluate(JsFunction::createWithBody(/** @lang JavaScript */"
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
"));
printf('Dimensions: %s', print_r($dimensions, true));
$page->screenshot(['path' => 'example_browserless.png', "fullPage" => true]);
$browser->close();
} finally {
`docker stop $dockerId`;
}
================================================
FILE: package.json
================================================
{
"name": "@zoon/puphpeteer",
"version": "2.4.3",
"description": "A Puppeteer bridge for PHP, supporting the entire API.",
"keywords": [
"php",
"puppeteer",
"headless-chrome",
"testing",
"web",
"developer-tools",
"automation"
],
"author": {
"name": "Johann Pardanaud",
"email": "pardanaud.j@gmail.com",
"url": "https://johann.pardanaud.com/"
},
"license": "MIT",
"repository": "git+https://git@github.com/zoonru/puphpeteer.git#zoon",
"engines": {
"node": ">=9.0.0"
},
"dependencies": {
"@zoon/rialto": "git+https://git@github.com/zoonru/rialto.git#zoon",
"puppeteer": "24.36.1",
"puppeteer-extra": "^3.3.6",
"puppeteer-extra-plugin-stealth": "^2.11.2"
},
"devDependencies": {
"@types/yargs": "^15.0.10",
"typescript": "^5",
"yargs": "^16.1.1"
}
}
================================================
FILE: phpstan.neon
================================================
parameters:
level: 4
fileExtensions:
- php
================================================
FILE: phpunit.xml
================================================
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuite name="default">
<directory suffix="Test.php">tests</directory>
</testsuite>
</phpunit>
================================================
FILE: src/Command/GenerateDocumentationCommand.php
================================================
<?php
namespace Nesk\Puphpeteer\Command;
use Nesk\Puphpeteer\Puppeteer;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Process;
final class GenerateDocumentationCommand extends Command
{
private const DOC_FILE_NAME = 'doc-generator';
private const BUILD_DIR = __DIR__.'/../../.build';
private const NODE_MODULES_DIR = __DIR__.'/../../node_modules';
private const RESOURCES_DIR = __DIR__.'/../Resources';
private const RESOURCES_NAMESPACE = 'Nesk\\Puphpeteer\\Resources';
private const DOC_FORMAT_PHP = 'php';
private const DOC_FORMAT_PHPSTAN = 'phpstan';
private const DOC_FORMATS = [self::DOC_FORMAT_PHP, self::DOC_FORMAT_PHPSTAN];
public function getName(): ?string
{
return 'doc:generate';
}
protected function configure(): void
{
$this->addOption(
'puppeteerPath',
null,
InputOption::VALUE_OPTIONAL,
'The path where Puppeteer is installed.',
self::NODE_MODULES_DIR.'/puppeteer-core'
);
}
/**
* Builds the documentation generator from TypeScript to JavaScript.
*/
private static function buildDocumentationGenerator(): void
{
self::rmdirRecursive(self::BUILD_DIR);
$process = new Process([
self::NODE_MODULES_DIR.'/.bin/tsc',
'--outDir',
self::BUILD_DIR,
__DIR__.'/../../src/'.self::DOC_FILE_NAME.'.ts',
]);
$process->run();
}
/**
* Gets the documentation from the TypeScript documentation generator.
*/
private static function getDocumentation(string $puppeteerPath, array $resourceNames): array
{
self::buildDocumentationGenerator();
$files = \glob($puppeteerPath . '/lib/esm/puppeteer/{common,node,api,cdp}/*.d.ts', GLOB_BRACE);
if ($files === false) {
$error = \error_get_last();
throw new \ErrorException($error['message'] ?? 'An error occurred', 0, $error['type'] ?? 1);
}
$result = [];
foreach (self::DOC_FORMATS as $format) {
$process = new Process(
array_merge(
['node', self::BUILD_DIR.'/'.self::DOC_FILE_NAME.'.js', $format],
$files,
['--resources-namespace', self::RESOURCES_NAMESPACE, '--resources'],
$resourceNames
)
);
$process->mustRun();
echo $process->getErrorOutput().\PHP_EOL;
$data = \json_decode($process->getOutput(), true);
if (JSON_ERROR_NONE !== \json_last_error()) {
throw new \JsonException(json_last_error_msg(), json_last_error());
}
foreach ($data as &$class) {
$result[$class['name']]['name'] = $class['name'];
$result[$class['name']][$format] = [
'properties' => $class['properties'],
'getters' => $class['getters'],
'methods' => $class['methods'],
];
}
}
return $result;
}
private static function getResourceNames(): array
{
return array_map(static function (string $filePath): string {
return explode('.', basename($filePath))[0];
}, glob(self::RESOURCES_DIR.'/*'));
}
private static function generatePhpDocWithDocumentation(array $classDocumentation): ?string
{
$properties = array_map(function (string $property): string {
return "\n * @property {$property}";
}, $classDocumentation[self::DOC_FORMAT_PHP]['properties']);
$properties = implode('', $properties);
$getters = array_map(function (string $getter): string {
return "\n * @property-read {$getter}";
}, $classDocumentation[self::DOC_FORMAT_PHP]['getters']);
$getters = implode('', $getters);
$methods = '';
foreach ($classDocumentation[self::DOC_FORMAT_PHP]['methods'] as $pos => $method) {
$methods .= "\n * @method {$method}";
$phpStanMethod = $classDocumentation[self::DOC_FORMAT_PHPSTAN]['methods'][$pos];
// phpStorm works incorrectly if @phpstan-method is used.
// Using non-standard method-extended phpDoc:
$methods .= "\n * @method-extended {$phpStanMethod}";
}
if ('' !== $properties || '' !== $getters || '' !== $methods) {
return "/**{$properties}{$getters}{$methods}\n */";
}
return null;
}
/**
* Writes the doc comment in the PHP class.
*/
private static function writePhpDoc(string $className, string $phpDoc): void
{
$reflectionClass = new \ReflectionClass($className);
if (! $reflectionClass) {
return;
}
$fileName = $reflectionClass->getFileName();
$contents = file_get_contents($fileName);
// If there already is a doc comment, replace it.
if ($doc = $reflectionClass->getDocComment()) {
$newContents = str_replace($doc, $phpDoc, $contents);
} else {
$startLine = $reflectionClass->getStartLine();
$lines = explode("\n", $contents);
$before = \array_slice($lines, 0, $startLine - 1);
$after = \array_slice($lines, $startLine - 1);
$newContents = implode("\n", array_merge($before, explode("\n", $phpDoc), $after));
}
file_put_contents($fileName, $newContents);
}
/**
* Executes the current command.
*/
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$resourceNames = self::getResourceNames();
$documentation = self::getDocumentation($input->getOption('puppeteerPath'), $resourceNames);
foreach ($resourceNames as $resourceName) {
$classDocumentation = $documentation[$resourceName] ?? null;
if (null !== $classDocumentation) {
$phpDoc = self::generatePhpDocWithDocumentation($classDocumentation);
if (null !== $phpDoc) {
$resourceClass = self::RESOURCES_NAMESPACE.'\\'.$resourceName;
self::writePhpDoc($resourceClass, $phpDoc);
}
}
}
// Handle the specific Puppeteer class
$classDocumentation = array_replace_recursive($documentation['Puppeteer'], $documentation['PuppeteerNode']);
unset($documentation['Puppeteer'], $documentation['PuppeteerNode'], $resourceNames[array_search('Puppeteer', $resourceNames, true)]);
if (null !== $classDocumentation) {
$phpDoc = self::generatePhpDocWithDocumentation($classDocumentation);
if (null !== $phpDoc) {
self::writePhpDoc(Puppeteer::class, $phpDoc);
}
}
$missingResources = array_diff(array_keys($documentation), $resourceNames);
foreach ($missingResources as $resource) {
$io->warning("The {$resource} class in Puppeteer doesn't have any equivalent in PuPHPeteer.");
}
$inexistantResources = array_diff($resourceNames, array_keys($documentation));
foreach ($inexistantResources as $resource) {
$io->error("The {$resource} resource doesn't have any equivalent in Puppeteer.");
}
return 0;
}
private static function rmdirRecursive(string $dir): bool
{
$files = scandir($dir);
if (! \is_array($files)) {
return false;
}
$files = array_diff($files, ['.', '..']);
foreach ($files as $file) {
(is_dir("{$dir}/{$file}")) ? self::rmdirRecursive("{$dir}/{$file}") : unlink("{$dir}/{$file}");
}
return rmdir($dir);
}
}
================================================
FILE: src/Puppeteer.php
================================================
<?php
namespace Nesk\Puphpeteer;
use Composer\Semver\Semver;
use Exception;
use Nesk\Rialto\AbstractEntryPoint;
use Psr\Log\LoggerInterface;
use Symfony\Component\Process\Process;
/**
* @property string $defaultBrowserRevision
* @property mixed $configuration
* @property-read string $browserVersion
* @property-read string|null $defaultDownloadPath
* @property-read mixed $lastLaunchedBrowser
* @property-read mixed $defaultBrowser
* @property-read string $product
* @method \Nesk\Puphpeteer\Resources\Browser connect(array $options)
* @method-extended \Nesk\Puphpeteer\Resources\Browser connect(array<string, mixed> $options)
* @method \Nesk\Puphpeteer\Resources\Browser launch(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\Browser launch(array<string, mixed> $options = null)
* @method string executablePath()
* @method-extended string executablePath()
* @method string[] defaultArgs(array $options = [])
* @method-extended string[] defaultArgs(array<string, mixed> $options = null)
* @method void trimCache()
* @method-extended void trimCache()
*/
class Puppeteer extends AbstractEntryPoint
{
/**
* Default options.
*
* @var array
*/
protected $options = [
'read_timeout' => 30,
// Logs the output of Browser's console methods (console.log, console.debug, etc...) to the PHP logger
'log_browser_console' => false,
// Custom js to load puppeteer-extra plugins
'js_extra' => '',
];
/**
* Instantiate Puppeteer's entry point.
*/
public function __construct(array $userOptions = [])
{
if (! empty($userOptions['logger']) && $userOptions['logger'] instanceof LoggerInterface) {
$this->checkPuppeteerVersion($userOptions['executable_path'] ?? 'node', $userOptions['logger']);
}
parent::__construct(
__DIR__.'/PuppeteerConnectionDelegate.js',
new PuppeteerProcessDelegate(),
$this->options,
$userOptions
);
}
private function checkPuppeteerVersion(string $nodePath, LoggerInterface $logger): void
{
$currentVersion = $this->currentPuppeteerVersion($nodePath);
$acceptedVersions = $this->acceptedPuppeteerVersion();
if (null === $currentVersion) {
$logger->warning("Puppeteer doesn't seem to be installed.");
return;
}
if (! Semver::satisfies($currentVersion, $acceptedVersions)) {
$logger->warning(
"The installed version of Puppeteer (v{$currentVersion}) doesn't match the requirements"
." ({$acceptedVersions}), you may encounter issues."
);
}
}
private function currentPuppeteerVersion(string $nodePath): ?string
{
$process = new Process([$nodePath, __DIR__.'/get-puppeteer-version.js']);
$process->mustRun();
return json_decode($process->getOutput(), true, 10, JSON_THROW_ON_ERROR);
}
private function acceptedPuppeteerVersion(): string
{
$npmManifestPath = __DIR__.'/../package.json';
$contents = file_get_contents($npmManifestPath) ?: throw new Exception('Cant load file');
$npmManifest = json_decode($contents, false, 10, JSON_THROW_ON_ERROR);
return $npmManifest->dependencies->puppeteer;
}
}
================================================
FILE: src/PuppeteerConnectionDelegate.js
================================================
"use strict";
const { ConnectionDelegate } = require("@zoon/rialto"),
Logger = require("@zoon/rialto/src/node-process/Logger"),
ConsoleInterceptor = require("@zoon/rialto/src/node-process/NodeInterceptors/ConsoleInterceptor"),
StandardStreamsInterceptor = require("@zoon/rialto/src/node-process/NodeInterceptors/StandardStreamsInterceptor");
/**
* Handle the requests of a connection to control Puppeteer.
*/
class PuppeteerConnectionDelegate extends ConnectionDelegate {
/**
* Constructor.
*
* @param {Object} options
*/
constructor(options) {
super(options);
this.browsers = new Set();
this.addSignalEventListeners();
}
/**
* @inheritdoc
*/
async handleInstruction(instruction, responseHandler, errorHandler) {
if (this.options.js_extra) {
eval(this.options.js_extra);
} else {
const puppeteer = require("puppeteer");
instruction.setDefaultResource(puppeteer);
}
let value = null;
try {
value = await instruction.execute();
} catch (error) {
if (instruction.shouldCatchErrors()) {
return errorHandler(error);
}
throw error;
}
if (this.isInstanceOf(value, "Browser")) {
this.browsers.add(value);
if (this.options.log_browser_console === true) {
const initialPages = await value.pages();
initialPages.forEach((page) =>
page.on("console", this.logConsoleMessage)
);
}
}
if (
this.options.log_browser_console === true &&
this.isInstanceOf(value, "Page")
) {
value.on("console", this.logConsoleMessage);
}
if (this.isInstanceOf(value, Uint8Array.name)) {
value = Buffer.from(value)
}
responseHandler(value);
}
/**
* Checks if a value is an instance of a class. The check must be done with the `[object].constructor.name`
* property because relying on Puppeteer's constructors isn't viable since the exports aren't constrained by semver.
*
* @protected
* @param {*} value
* @param {string} className
*
* @see {@link https://github.com/GoogleChrome/puppeteer/issues/3067|Puppeteer's issue about semver on exports}
*/
isInstanceOf(value, className) {
const nonObjectValues = [undefined, null];
return (
!nonObjectValues.includes(value) &&
!nonObjectValues.includes(value.constructor) &&
(
value.constructor.name === className
|| value.constructor.name === "Cdp" + className
|| value.constructor.name === "CDP" + className
)
);
}
/**
* Log the console message.
*
* @param {ConsoleMessage} consoleMessage
*/
async logConsoleMessage(consoleMessage) {
const type = consoleMessage.type();
if (!ConsoleInterceptor.typeIsSupported(type)) {
return;
}
const level = ConsoleInterceptor.getLevelFromType(type);
const args = await Promise.all(
consoleMessage.args().map((arg) => arg.jsonValue())
);
StandardStreamsInterceptor.startInterceptingStrings((message) => {
Logger.log("Browser", level, ConsoleInterceptor.formatMessage(message));
});
ConsoleInterceptor.originalConsole[type](...args);
StandardStreamsInterceptor.stopInterceptingStrings();
}
/**
* Listen for process signal events.
*
* @protected
*/
addSignalEventListeners() {
for (let eventName of ["SIGINT", "SIGTERM", "SIGHUP"]) {
process.on(eventName, () => {
this.closeAllBrowsers();
process.exit();
});
}
}
/**
* Close all the browser instances when the process exits.
*
* Calling this method before exiting Node is mandatory since Puppeteer doesn't seem to handle that properly.
*
* @protected
*/
closeAllBrowsers() {
for (let browser of this.browsers.values()) {
browser.close();
}
}
}
module.exports = PuppeteerConnectionDelegate;
================================================
FILE: src/PuppeteerProcessDelegate.php
================================================
<?php
namespace Nesk\Puphpeteer;
use Nesk\Rialto\Interfaces\ShouldHandleProcessDelegation;
use Nesk\Rialto\Traits\UsesBasicResourceAsDefault;
class PuppeteerProcessDelegate implements ShouldHandleProcessDelegation
{
use UsesBasicResourceAsDefault;
public function resourceFromOriginalClassName(string $className): ?string
{
$class = "Nesk\\Puphpeteer\\Resources\\{$className}";
if (class_exists($class)) {
return $class;
}
$classWithoutCDP = 'Nesk\\Puphpeteer\\Resources\\'. preg_replace('/^Cdp/i', '', $className);
if (class_exists($classWithoutCDP)) {
return $classWithoutCDP;
}
return null;
}
}
================================================
FILE: src/Resources/Accessibility.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method mixed|null snapshot(array $options = [])
* @method-extended mixed|null snapshot(array<string, mixed> $options = null)
*/
class Accessibility extends BasicResource
{
}
================================================
FILE: src/Resources/Browser.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @property-read bool $connected
* @property-read mixed $protocol
* @property-read mixed $debugInfo
* @method mixed|null process()
* @method-extended mixed|null process()
* @method \Nesk\Puphpeteer\Resources\BrowserContext createBrowserContext(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\BrowserContext createBrowserContext(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\BrowserContext[] browserContexts()
* @method-extended \Nesk\Puphpeteer\Resources\BrowserContext[] browserContexts()
* @method \Nesk\Puphpeteer\Resources\BrowserContext defaultBrowserContext()
* @method-extended \Nesk\Puphpeteer\Resources\BrowserContext defaultBrowserContext()
* @method string wsEndpoint()
* @method-extended string wsEndpoint()
* @method \Nesk\Puphpeteer\Resources\Page newPage()
* @method-extended \Nesk\Puphpeteer\Resources\Page newPage()
* @method \Nesk\Puphpeteer\Resources\Target[] targets()
* @method-extended \Nesk\Puphpeteer\Resources\Target[] targets()
* @method \Nesk\Puphpeteer\Resources\Target target()
* @method-extended \Nesk\Puphpeteer\Resources\Target target()
* @method \Nesk\Puphpeteer\Resources\Target waitForTarget(\Nesk\Rialto\Data\JsFunction $predicate, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\Target waitForTarget(callable(\Nesk\Puphpeteer\Resources\Target $x): bool|Promise|bool[]|\Nesk\Rialto\Data\JsFunction $predicate, array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\Page[] pages()
* @method-extended \Nesk\Puphpeteer\Resources\Page[] pages()
* @method string version()
* @method-extended string version()
* @method string userAgent()
* @method-extended string userAgent()
* @method void close()
* @method-extended void close()
* @method void disconnect()
* @method-extended void disconnect()
* @method mixed[] cookies()
* @method-extended mixed[] cookies()
* @method void setCookie(mixed ...$cookies)
* @method-extended void setCookie(mixed ...$cookies)
* @method void deleteCookie(mixed ...$cookies)
* @method-extended void deleteCookie(mixed ...$cookies)
* @method void deleteMatchingCookies(mixed ...$filters)
* @method-extended void deleteMatchingCookies(mixed ...$filters)
* @method string installExtension(string $path)
* @method-extended string installExtension(string $path)
* @method void uninstallExtension(string $id)
* @method-extended void uninstallExtension(string $id)
* @method bool isConnected()
* @method-extended bool isConnected()
* @method bool isNetworkEnabled()
* @method-extended bool isNetworkEnabled()
*/
class Browser extends EventEmitter
{
}
================================================
FILE: src/Resources/BrowserContext.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @property-read bool $closed
* @property-read string|null $id
* @method \Nesk\Puphpeteer\Resources\Target[] targets()
* @method-extended \Nesk\Puphpeteer\Resources\Target[] targets()
* @method mixed startScreenshot()
* @method-extended mixed startScreenshot()
* @method mixed|null waitForScreenshotOperations()
* @method-extended mixed|null waitForScreenshotOperations()
* @method \Nesk\Puphpeteer\Resources\Target waitForTarget(\Nesk\Rialto\Data\JsFunction $predicate, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\Target waitForTarget(callable(\Nesk\Puphpeteer\Resources\Target $x): bool|Promise|bool[]|\Nesk\Rialto\Data\JsFunction $predicate, array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\Page[] pages()
* @method-extended \Nesk\Puphpeteer\Resources\Page[] pages()
* @method void overridePermissions(string $origin, mixed[] $permissions)
* @method-extended void overridePermissions(string $origin, mixed[] $permissions)
* @method void clearPermissionOverrides()
* @method-extended void clearPermissionOverrides()
* @method \Nesk\Puphpeteer\Resources\Page newPage()
* @method-extended \Nesk\Puphpeteer\Resources\Page newPage()
* @method \Nesk\Puphpeteer\Resources\Browser browser()
* @method-extended \Nesk\Puphpeteer\Resources\Browser browser()
* @method void close()
* @method-extended void close()
* @method mixed[] cookies()
* @method-extended mixed[] cookies()
* @method void setCookie(mixed ...$cookies)
* @method-extended void setCookie(mixed ...$cookies)
* @method void deleteCookie(mixed ...$cookies)
* @method-extended void deleteCookie(mixed ...$cookies)
* @method void deleteMatchingCookies(mixed ...$filters)
* @method-extended void deleteMatchingCookies(mixed ...$filters)
*/
class BrowserContext extends EventEmitter
{
}
================================================
FILE: src/Resources/BrowserLauncher.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @property mixed $puppeteer
* @property-read mixed $browser
* @method \Nesk\Puphpeteer\Resources\Browser launch(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\Browser launch(array<string, mixed> $options = null)
* @method string executablePath(mixed $channel = null, bool $validatePath = null)
* @method-extended string executablePath(mixed $channel = null, bool $validatePath = null)
* @method string[] defaultArgs(array $object)
* @method-extended string[] defaultArgs(array<string, mixed> $object)
* @method string resolveExecutablePath(bool|'shell' $headless = null, bool $validatePath = null)
* @method-extended string resolveExecutablePath(bool|'shell' $headless = null, bool $validatePath = null)
*/
class BrowserLauncher
{
}
================================================
FILE: src/Resources/BrowserWebSocketTransport.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @property \Nesk\Rialto\Data\JsFunction $onmessage
* @property \Nesk\Rialto\Data\JsFunction $onclose
* @method void send(string $message)
* @method-extended void send(string $message)
* @method void close()
* @method-extended void close()
*/
class BrowserWebSocketTransport
{
}
================================================
FILE: src/Resources/Buffer.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method \string toString(?string $encoding = "utf8", ?int $start = null, ?int $end = null)
*/
class Buffer extends BasicResource
{
}
================================================
FILE: src/Resources/CDPSession.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @property-read bool $detached
* @method mixed|null connection()
* @method-extended mixed|null connection()
* @method \Nesk\Puphpeteer\Resources\CDPSession|null parentSession()
* @method-extended \Nesk\Puphpeteer\Resources\CDPSession|null parentSession()
* @method mixed send(mixed $method, mixed $params = null, array $options = [])
* @method-extended mixed send(mixed $method, mixed $params = null, array<string, mixed> $options = null)
* @method void detach()
* @method-extended void detach()
* @method string id()
* @method-extended string id()
*/
class CDPSession extends EventEmitter
{
}
================================================
FILE: src/Resources/ConsoleMessage.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method mixed type()
* @method-extended mixed type()
* @method string text()
* @method-extended string text()
* @method \Nesk\Puphpeteer\Resources\JSHandle[] args()
* @method-extended \Nesk\Puphpeteer\Resources\JSHandle[] args()
* @method mixed location()
* @method-extended mixed location()
* @method mixed[] stackTrace()
* @method-extended mixed[] stackTrace()
*/
class ConsoleMessage extends BasicResource
{
}
================================================
FILE: src/Resources/Coverage.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method void updateClient(\Nesk\Puphpeteer\Resources\CDPSession $client)
* @method-extended void updateClient(\Nesk\Puphpeteer\Resources\CDPSession $client)
* @method void startJSCoverage(array $options = [])
* @method-extended void startJSCoverage(array<string, mixed> $options = null)
* @method mixed[] stopJSCoverage()
* @method-extended mixed[] stopJSCoverage()
* @method void startCSSCoverage(array $options = [])
* @method-extended void startCSSCoverage(array<string, mixed> $options = null)
* @method mixed[] stopCSSCoverage()
* @method-extended mixed[] stopCSSCoverage()
*/
class Coverage extends BasicResource
{
}
================================================
FILE: src/Resources/DevToolsTarget.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
class DevToolsTarget extends Target
{
}
================================================
FILE: src/Resources/Dialog.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method mixed type()
* @method-extended mixed type()
* @method string message()
* @method-extended string message()
* @method string defaultValue()
* @method-extended string defaultValue()
* @method void accept(string $promptText = null)
* @method-extended void accept(string $promptText = null)
* @method void dismiss()
* @method-extended void dismiss()
*/
class Dialog extends BasicResource
{
}
================================================
FILE: src/Resources/ElementHandle.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Puphpeteer\Traits\AliasesEvaluationMethods;
use Nesk\Puphpeteer\Traits\AliasesSelectionMethods;
/**
* @property mixed $isolatedHandle
* @property-read string|null $id
* @property-read bool $disposed
* @property-read \Nesk\Puphpeteer\Resources\Frame $frame
* @method mixed getProperty(mixed $propertyName)
* @method-extended mixed getProperty(mixed $propertyName)
* @method array|string[]|\Nesk\Puphpeteer\Resources\JSHandle[] getProperties()
* @method-extended array|string[]|\Nesk\Puphpeteer\Resources\JSHandle[] getProperties()
* @method mixed evaluate(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluate(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed evaluateHandle(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluateHandle(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed jsonValue()
* @method-extended mixed jsonValue()
* @method string toString()
* @method-extended string toString()
* @method mixed remoteObject()
* @method-extended mixed remoteObject()
* @method void dispose()
* @method-extended void dispose()
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] asElement()
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] asElement()
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array<string, mixed> $options = null)
* @method bool isVisible()
* @method-extended bool isVisible()
* @method bool isHidden()
* @method-extended bool isHidden()
* @method mixed toElement(mixed $tagName)
* @method-extended mixed toElement(mixed $tagName)
* @method \Nesk\Puphpeteer\Resources\Frame|null contentFrame()
* @method-extended \Nesk\Puphpeteer\Resources\Frame|null contentFrame()
* @method mixed clickablePoint(mixed $offset = null)
* @method-extended mixed clickablePoint(mixed $offset = null)
* @method void hover(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method-extended void hover(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method void click(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $options = null)
* @method-extended void click(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $options = null)
* @method mixed|null drag(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed|\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $target)
* @method-extended mixed|null drag(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed|\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $target)
* @method void dragEnter(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $data = null)
* @method-extended void dragEnter(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $data = null)
* @method void dragOver(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $data = null)
* @method-extended void dragOver(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $data = null)
* @method void drop(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $data = null)
* @method-extended void drop(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $data = null)
* @method void dragAndDrop(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $target, array $options = [])
* @method-extended void dragAndDrop(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $target, array{ $delay: float } $options = null)
* @method string[] select(string ...$values)
* @method-extended string[] select(string ...$values)
* @method void uploadFile(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, string ...$paths)
* @method-extended void uploadFile(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, string ...$paths)
* @method mixed queryAXTree(string $name = null, string $role = null)
* @method-extended mixed queryAXTree(string $name = null, string $role = null)
* @method void tap(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method-extended void tap(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method mixed touchStart(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method-extended mixed touchStart(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method void touchMove(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $touch = null)
* @method-extended void touchMove(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, mixed $touch = null)
* @method void touchEnd(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method-extended void touchEnd(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method void focus()
* @method-extended void focus()
* @method void type(string $text, mixed $options = null)
* @method-extended void type(string $text, mixed $options = null)
* @method void press(mixed $key, mixed $options = null)
* @method-extended void press(mixed $key, mixed $options = null)
* @method mixed|null boundingBox()
* @method-extended mixed|null boundingBox()
* @method mixed|null boxModel()
* @method-extended mixed|null boxModel()
* @method \Nesk\Puphpeteer\Resources\Uint8Array screenshot(mixed $options = null)
* @method-extended \Nesk\Puphpeteer\Resources\Uint8Array screenshot(mixed $options = null)
* @method bool isIntersectingViewport(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, array $options = [])
* @method-extended bool isIntersectingViewport(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector, array{ $threshold: float } $options = null)
* @method void scrollIntoView(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method-extended void scrollIntoView(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method mixed asLocator(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method-extended mixed asLocator(\Nesk\Puphpeteer\Resources\ElementHandle|mixed[] $selector)
* @method void autofill(mixed $data)
* @method-extended void autofill(mixed $data)
* @method float backendNodeId()
* @method-extended float backendNodeId()
*/
class ElementHandle extends JSHandle
{
use AliasesEvaluationMethods;
use AliasesSelectionMethods;
}
================================================
FILE: src/Resources/EventEmitter.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method mixed on(mixed $type, mixed $handler)
* @method-extended mixed on(mixed $type, mixed $handler)
* @method mixed off(mixed $type, mixed $handler = null)
* @method-extended mixed off(mixed $type, mixed $handler = null)
* @method bool emit(mixed $type, mixed $event)
* @method-extended bool emit(mixed $type, mixed $event)
* @method mixed once(mixed $type, mixed $handler)
* @method-extended mixed once(mixed $type, mixed $handler)
* @method float listenerCount(mixed $type)
* @method-extended float listenerCount(mixed $type)
* @method mixed removeAllListeners(mixed $type = null)
* @method-extended mixed removeAllListeners(mixed $type = null)
*/
class EventEmitter extends BasicResource
{
}
================================================
FILE: src/Resources/FileChooser.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method bool isMultiple()
* @method-extended bool isMultiple()
* @method void accept(string[] $paths)
* @method-extended void accept(string[] $paths)
* @method void cancel()
* @method-extended void cancel()
*/
class FileChooser extends BasicResource
{
}
================================================
FILE: src/Resources/Frame.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Puphpeteer\Traits\AliasesEvaluationMethods;
use Nesk\Puphpeteer\Traits\AliasesSelectionMethods;
use Nesk\Rialto\Data\BasicResource;
/**
* @property-read \Nesk\Puphpeteer\Resources\CDPSession $client
* @property-read \Nesk\Puphpeteer\Resources\Accessibility $accessibility
* @property-read bool $detached
* @property-read bool $disposed
* @method \Nesk\Puphpeteer\Resources\Page page()
* @method-extended \Nesk\Puphpeteer\Resources\Page page()
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null goto(string $url, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null goto(string $url, array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null waitForNavigation(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null waitForNavigation(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\Realm mainRealm()
* @method-extended \Nesk\Puphpeteer\Resources\Realm mainRealm()
* @method \Nesk\Puphpeteer\Resources\Realm isolatedRealm()
* @method-extended \Nesk\Puphpeteer\Resources\Realm isolatedRealm()
* @method void clearDocumentHandle()
* @method-extended void clearDocumentHandle()
* @method mixed|null frameElement()
* @method-extended mixed|null frameElement()
* @method mixed evaluateHandle(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluateHandle(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed evaluate(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluate(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed locator(\Nesk\Rialto\Data\JsFunction $func)
* @method-extended mixed locator(callable(): mixed|\Nesk\Rialto\Data\JsFunction $func)
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array<string, mixed> $options = null)
* @method mixed waitForFunction(\Nesk\Rialto\Data\JsFunction $pageFunction, array $options = [], mixed ...$args)
* @method-extended mixed waitForFunction(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, array<string, mixed> $options = null, mixed ...$args)
* @method string content()
* @method-extended string content()
* @method void setContent(string $html, array $options = [])
* @method-extended void setContent(string $html, array<string, mixed> $options = null)
* @method void setFrameContent(string $content)
* @method-extended void setFrameContent(string $content)
* @method string name()
* @method-extended string name()
* @method string url()
* @method-extended string url()
* @method \Nesk\Puphpeteer\Resources\Frame|null parentFrame()
* @method-extended \Nesk\Puphpeteer\Resources\Frame|null parentFrame()
* @method \Nesk\Puphpeteer\Resources\Frame[] childFrames()
* @method-extended \Nesk\Puphpeteer\Resources\Frame[] childFrames()
* @method bool isDetached()
* @method-extended bool isDetached()
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addScriptTag(array $options)
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addScriptTag(array<string, mixed> $options)
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addStyleTag(array $options)
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addStyleTag(array<string, mixed> $options)
* @method void click(string $selector, mixed $options = null)
* @method-extended void click(string $selector, mixed $options = null)
* @method void focus(string $selector)
* @method-extended void focus(string $selector)
* @method void hover(string $selector)
* @method-extended void hover(string $selector)
* @method string[] select(string $selector, string ...$values)
* @method-extended string[] select(string $selector, string ...$values)
* @method void tap(string $selector)
* @method-extended void tap(string $selector)
* @method void type(string $selector, string $text, mixed $options = null)
* @method-extended void type(string $selector, string $text, mixed $options = null)
* @method string title()
* @method-extended string title()
* @method mixed waitForDevicePrompt(array $options = [])
* @method-extended mixed waitForDevicePrompt(array<string, mixed> $options = null)
*/
class Frame extends BasicResource
{
use AliasesEvaluationMethods;
use AliasesSelectionMethods;
}
================================================
FILE: src/Resources/HTTPRequest.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @property-read string $id
* @property-read \Nesk\Puphpeteer\Resources\CDPSession $client
* @method string url()
* @method-extended string url()
* @method mixed continueRequestOverrides()
* @method-extended mixed continueRequestOverrides()
* @method mixed|null responseForRequest()
* @method-extended mixed|null responseForRequest()
* @method mixed|null abortErrorReason()
* @method-extended mixed|null abortErrorReason()
* @method mixed interceptResolutionState()
* @method-extended mixed interceptResolutionState()
* @method bool isInterceptResolutionHandled()
* @method-extended bool isInterceptResolutionHandled()
* @method void enqueueInterceptAction(\Nesk\Rialto\Data\JsFunction $pendingHandler)
* @method-extended void enqueueInterceptAction(callable(): null|mixed|\Nesk\Rialto\Data\JsFunction $pendingHandler)
* @method void finalizeInterceptions()
* @method-extended void finalizeInterceptions()
* @method mixed resourceType()
* @method-extended mixed resourceType()
* @method string method()
* @method-extended string method()
* @method string|null postData()
* @method-extended string|null postData()
* @method bool hasPostData()
* @method-extended bool hasPostData()
* @method string|null fetchPostData()
* @method-extended string|null fetchPostData()
* @method array|string[]|string[] headers()
* @method-extended array|string[]|string[] headers()
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null response()
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null response()
* @method \Nesk\Puphpeteer\Resources\Frame|null frame()
* @method-extended \Nesk\Puphpeteer\Resources\Frame|null frame()
* @method bool isNavigationRequest()
* @method-extended bool isNavigationRequest()
* @method mixed|null initiator()
* @method-extended mixed|null initiator()
* @method \Nesk\Puphpeteer\Resources\HTTPRequest[] redirectChain()
* @method-extended \Nesk\Puphpeteer\Resources\HTTPRequest[] redirectChain()
* @method array|null failure()
* @method-extended array{ $errorText: string }|null failure()
* @method void continue(mixed $overrides = null, float $priority = null)
* @method-extended void continue(mixed $overrides = null, float $priority = null)
* @method void respond(mixed $response, float $priority = null)
* @method-extended void respond(mixed $response, float $priority = null)
* @method void abort(mixed $errorCode = null, float $priority = null)
* @method-extended void abort(mixed $errorCode = null, float $priority = null)
*/
class HTTPRequest extends BasicResource
{
}
================================================
FILE: src/Resources/HTTPResponse.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method mixed remoteAddress()
* @method-extended mixed remoteAddress()
* @method string url()
* @method-extended string url()
* @method bool ok()
* @method-extended bool ok()
* @method float status()
* @method-extended float status()
* @method string statusText()
* @method-extended string statusText()
* @method array|string[]|string[] headers()
* @method-extended array|string[]|string[] headers()
* @method \Nesk\Puphpeteer\Resources\SecurityDetails|null securityDetails()
* @method-extended \Nesk\Puphpeteer\Resources\SecurityDetails|null securityDetails()
* @method mixed|null timing()
* @method-extended mixed|null timing()
* @method \Nesk\Puphpeteer\Resources\Uint8Array content()
* @method-extended \Nesk\Puphpeteer\Resources\Uint8Array content()
* @method \Nesk\Puphpeteer\Resources\Buffer buffer()
* @method-extended \Nesk\Puphpeteer\Resources\Buffer buffer()
* @method string text()
* @method-extended string text()
* @method mixed json()
* @method-extended mixed json()
* @method \Nesk\Puphpeteer\Resources\HTTPRequest request()
* @method-extended \Nesk\Puphpeteer\Resources\HTTPRequest request()
* @method bool fromCache()
* @method-extended bool fromCache()
* @method bool fromServiceWorker()
* @method-extended bool fromServiceWorker()
* @method \Nesk\Puphpeteer\Resources\Frame|null frame()
* @method-extended \Nesk\Puphpeteer\Resources\Frame|null frame()
*/
class HTTPResponse extends BasicResource
{
}
================================================
FILE: src/Resources/JSHandle.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @property \Nesk\Rialto\Data\JsFunction $move
* @property-read \Nesk\Puphpeteer\Resources\Realm $realm
* @property-read bool $disposed
* @property-read string|null $id
* @method mixed evaluate(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluate(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed evaluateHandle(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluateHandle(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method \Nesk\Puphpeteer\Resources\JSHandle|mixed[] getProperty(string $propertyName)
* @method-extended \Nesk\Puphpeteer\Resources\JSHandle|mixed[] getProperty(string $propertyName)
* @method array|string[]|\Nesk\Puphpeteer\Resources\JSHandle[] getProperties()
* @method-extended array|string[]|\Nesk\Puphpeteer\Resources\JSHandle[] getProperties()
* @method mixed jsonValue()
* @method-extended mixed jsonValue()
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null asElement()
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null asElement()
* @method void dispose()
* @method-extended void dispose()
* @method string toString()
* @method-extended string toString()
* @method mixed remoteObject()
* @method-extended mixed remoteObject()
*/
class JSHandle extends BasicResource
{
}
================================================
FILE: src/Resources/Keyboard.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method void down(mixed $key, mixed $options = null)
* @method-extended void down(mixed $key, mixed $options = null)
* @method void up(mixed $key)
* @method-extended void up(mixed $key)
* @method void sendCharacter(string $char)
* @method-extended void sendCharacter(string $char)
* @method void type(string $text, mixed $options = null)
* @method-extended void type(string $text, mixed $options = null)
* @method void press(mixed $key, mixed $options = null)
* @method-extended void press(mixed $key, mixed $options = null)
*/
class Keyboard extends BasicResource
{
}
================================================
FILE: src/Resources/Mouse.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method void reset()
* @method-extended void reset()
* @method void move(float $x, float $y, mixed $options = null)
* @method-extended void move(float $x, float $y, mixed $options = null)
* @method void down(mixed $options = null)
* @method-extended void down(mixed $options = null)
* @method void up(mixed $options = null)
* @method-extended void up(mixed $options = null)
* @method void click(float $x, float $y, mixed $options = null)
* @method-extended void click(float $x, float $y, mixed $options = null)
* @method void wheel(mixed $options = null)
* @method-extended void wheel(mixed $options = null)
* @method mixed drag(mixed $start, mixed $target)
* @method-extended mixed drag(mixed $start, mixed $target)
* @method void dragEnter(mixed $target, mixed $data)
* @method-extended void dragEnter(mixed $target, mixed $data)
* @method void dragOver(mixed $target, mixed $data)
* @method-extended void dragOver(mixed $target, mixed $data)
* @method void drop(mixed $target, mixed $data)
* @method-extended void drop(mixed $target, mixed $data)
* @method void dragAndDrop(mixed $start, mixed $target, array $options = [])
* @method-extended void dragAndDrop(mixed $start, mixed $target, array{ $delay: float } $options = null)
*/
class Mouse extends BasicResource
{
}
================================================
FILE: src/Resources/OtherTarget.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
class OtherTarget extends Target
{
}
================================================
FILE: src/Resources/Page.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Puphpeteer\Traits\AliasesEvaluationMethods;
use Nesk\Puphpeteer\Traits\AliasesSelectionMethods;
/**
* @property-read \Nesk\Puphpeteer\Resources\Keyboard $keyboard
* @property-read \Nesk\Puphpeteer\Resources\Touchscreen $touchscreen
* @property-read \Nesk\Puphpeteer\Resources\Coverage $coverage
* @property-read \Nesk\Puphpeteer\Resources\Tracing $tracing
* @property-read \Nesk\Puphpeteer\Resources\Accessibility $accessibility
* @property-read \Nesk\Puphpeteer\Resources\Mouse $mouse
* @method bool isServiceWorkerBypassed()
* @method-extended bool isServiceWorkerBypassed()
* @method bool isDragInterceptionEnabled()
* @method-extended bool isDragInterceptionEnabled()
* @method bool isJavaScriptEnabled()
* @method-extended bool isJavaScriptEnabled()
* @method mixed on(mixed $type, \Nesk\Rialto\Data\JsFunction $handler)
* @method-extended mixed on(mixed $type, callable(mixed $event): void|\Nesk\Rialto\Data\JsFunction $handler)
* @method mixed off(mixed $type, \Nesk\Rialto\Data\JsFunction $handler)
* @method-extended mixed off(mixed $type, callable(mixed $event): void|\Nesk\Rialto\Data\JsFunction $handler)
* @method \Nesk\Puphpeteer\Resources\FileChooser waitForFileChooser(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\FileChooser waitForFileChooser(array<string, mixed> $options = null)
* @method void setGeolocation(array $options)
* @method-extended void setGeolocation(array<string, mixed> $options)
* @method \Nesk\Puphpeteer\Resources\Target target()
* @method-extended \Nesk\Puphpeteer\Resources\Target target()
* @method \Nesk\Puphpeteer\Resources\Browser browser()
* @method-extended \Nesk\Puphpeteer\Resources\Browser browser()
* @method \Nesk\Puphpeteer\Resources\BrowserContext browserContext()
* @method-extended \Nesk\Puphpeteer\Resources\BrowserContext browserContext()
* @method \Nesk\Puphpeteer\Resources\Frame mainFrame()
* @method-extended \Nesk\Puphpeteer\Resources\Frame mainFrame()
* @method \Nesk\Puphpeteer\Resources\CDPSession createCDPSession()
* @method-extended \Nesk\Puphpeteer\Resources\CDPSession createCDPSession()
* @method \Nesk\Puphpeteer\Resources\Frame[] frames()
* @method-extended \Nesk\Puphpeteer\Resources\Frame[] frames()
* @method \Nesk\Puphpeteer\Resources\WebWorker[] workers()
* @method-extended \Nesk\Puphpeteer\Resources\WebWorker[] workers()
* @method void setRequestInterception(bool $value)
* @method-extended void setRequestInterception(bool $value)
* @method void setBypassServiceWorker(bool $bypass)
* @method-extended void setBypassServiceWorker(bool $bypass)
* @method void setDragInterception(bool $enabled)
* @method-extended void setDragInterception(bool $enabled)
* @method void setOfflineMode(bool $enabled)
* @method-extended void setOfflineMode(bool $enabled)
* @method void emulateNetworkConditions(mixed|null $networkConditions)
* @method-extended void emulateNetworkConditions(mixed|null $networkConditions)
* @method void setDefaultNavigationTimeout(float $timeout)
* @method-extended void setDefaultNavigationTimeout(float $timeout)
* @method void setDefaultTimeout(float $timeout)
* @method-extended void setDefaultTimeout(float $timeout)
* @method float getDefaultTimeout()
* @method-extended float getDefaultTimeout()
* @method float getDefaultNavigationTimeout()
* @method-extended float getDefaultNavigationTimeout()
* @method mixed locator(\Nesk\Rialto\Data\JsFunction $func)
* @method-extended mixed locator(callable(): mixed|\Nesk\Rialto\Data\JsFunction $func)
* @method mixed locatorRace(mixed $locators)
* @method-extended mixed locatorRace(mixed $locators)
* @method mixed evaluateHandle(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluateHandle(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method \Nesk\Puphpeteer\Resources\JSHandle|mixed[][] queryObjects(\Nesk\Puphpeteer\Resources\JSHandle|mixed[] $prototypeHandle)
* @method-extended \Nesk\Puphpeteer\Resources\JSHandle|mixed[][] queryObjects(\Nesk\Puphpeteer\Resources\JSHandle|mixed[] $prototypeHandle)
* @method mixed[] cookies(string ...$urls)
* @method-extended mixed[] cookies(string ...$urls)
* @method void deleteCookie(mixed ...$cookies)
* @method-extended void deleteCookie(mixed ...$cookies)
* @method void setCookie(mixed ...$cookies)
* @method-extended void setCookie(mixed ...$cookies)
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addScriptTag(array $options)
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addScriptTag(array<string, mixed> $options)
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addStyleTag(array $options)
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[] addStyleTag(array<string, mixed> $options)
* @method void exposeFunction(string $name, \Nesk\Rialto\Data\JsFunction|array $pptrFunction)
* @method-extended void exposeFunction(string $name, callable|\Nesk\Rialto\Data\JsFunction|array{ $default: callable|\Nesk\Rialto\Data\JsFunction } $pptrFunction)
* @method void removeExposedFunction(string $name)
* @method-extended void removeExposedFunction(string $name)
* @method void authenticate(mixed|null $credentials)
* @method-extended void authenticate(mixed|null $credentials)
* @method void setExtraHTTPHeaders(array|string[]|string[] $headers)
* @method-extended void setExtraHTTPHeaders(array|string[]|string[] $headers)
* @method void setUserAgent(array $options)
* @method-extended void setUserAgent(array{ $userAgent: string, $userAgentMetadata: mixed, $platform: string } $options)
* @method mixed metrics()
* @method-extended mixed metrics()
* @method string url()
* @method-extended string url()
* @method string content()
* @method-extended string content()
* @method void setContent(string $html, array $options = [])
* @method-extended void setContent(string $html, array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null goto(string $url, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null goto(string $url, array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null reload(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null reload(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null waitForNavigation(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null waitForNavigation(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPRequest waitForRequest(string|mixed $urlOrPredicate, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPRequest waitForRequest(string|mixed $urlOrPredicate, array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPResponse waitForResponse(string|mixed $urlOrPredicate, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse waitForResponse(string|mixed $urlOrPredicate, array<string, mixed> $options = null)
* @method void waitForNetworkIdle(array $options = [])
* @method-extended void waitForNetworkIdle(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\Frame waitForFrame(string|\Nesk\Rialto\Data\JsFunction $urlOrPredicate, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\Frame waitForFrame(string|callable(callable(\Nesk\Puphpeteer\Resources\Frame $frame): mixed|\Nesk\Rialto\Data\JsFunction): |\Nesk\Rialto\Data\JsFunction $urlOrPredicate, array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null goBack(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null goBack(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\HTTPResponse|null goForward(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\HTTPResponse|null goForward(array<string, mixed> $options = null)
* @method void bringToFront()
* @method-extended void bringToFront()
* @method void emulate(mixed $device)
* @method-extended void emulate(mixed $device)
* @method void setJavaScriptEnabled(bool $enabled)
* @method-extended void setJavaScriptEnabled(bool $enabled)
* @method void setBypassCSP(bool $enabled)
* @method-extended void setBypassCSP(bool $enabled)
* @method void emulateMediaType(string $type = null)
* @method-extended void emulateMediaType(string $type = null)
* @method void emulateCPUThrottling(float|null $factor)
* @method-extended void emulateCPUThrottling(float|null $factor)
* @method void emulateMediaFeatures(mixed[] $features = null)
* @method-extended void emulateMediaFeatures(mixed[] $features = null)
* @method void emulateTimezone(string $timezoneId = null)
* @method-extended void emulateTimezone(string $timezoneId = null)
* @method void emulateIdleState(array $overrides = [])
* @method-extended void emulateIdleState(array{ $isUserActive: bool, $isScreenUnlocked: bool } $overrides = null)
* @method void emulateVisionDeficiency(mixed $type = null)
* @method-extended void emulateVisionDeficiency(mixed $type = null)
* @method void setViewport(mixed|null $viewport)
* @method-extended void setViewport(mixed|null $viewport)
* @method mixed|null viewport()
* @method-extended mixed|null viewport()
* @method mixed evaluate(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluate(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed evaluateOnNewDocument(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluateOnNewDocument(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method void removeScriptToEvaluateOnNewDocument(string $identifier)
* @method-extended void removeScriptToEvaluateOnNewDocument(string $identifier)
* @method void setCacheEnabled(bool $enabled = null)
* @method-extended void setCacheEnabled(bool $enabled = null)
* @method \Nesk\Puphpeteer\Resources\ScreenRecorder screencast(mixed $options = null)
* @method-extended \Nesk\Puphpeteer\Resources\ScreenRecorder screencast(mixed $options = null)
* @method \Nesk\Puphpeteer\Resources\Uint8Array screenshot(mixed $options = null)
* @method-extended \Nesk\Puphpeteer\Resources\Uint8Array screenshot(mixed $options = null)
* @method mixed createPDFStream(array $options = [])
* @method-extended mixed createPDFStream(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\Uint8Array pdf(array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\Uint8Array pdf(array<string, mixed> $options = null)
* @method string title()
* @method-extended string title()
* @method void close(array $options = [])
* @method-extended void close(array{ $runBeforeUnload: bool } $options = null)
* @method bool isClosed()
* @method-extended bool isClosed()
* @method void click(string $selector, mixed $options = null)
* @method-extended void click(string $selector, mixed $options = null)
* @method void focus(string $selector)
* @method-extended void focus(string $selector)
* @method void hover(string $selector)
* @method-extended void hover(string $selector)
* @method string[] select(string $selector, string ...$values)
* @method-extended string[] select(string $selector, string ...$values)
* @method void tap(string $selector)
* @method-extended void tap(string $selector)
* @method void type(string $selector, string $text, mixed $options = null)
* @method-extended void type(string $selector, string $text, mixed $options = null)
* @method \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array $options = [])
* @method-extended \Nesk\Puphpeteer\Resources\ElementHandle|mixed[]|null waitForSelector(mixed $selector, array<string, mixed> $options = null)
* @method mixed waitForFunction(\Nesk\Rialto\Data\JsFunction $pageFunction, array $options = [], mixed ...$args)
* @method-extended mixed waitForFunction(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, array<string, mixed> $options = null, mixed ...$args)
* @method mixed waitForDevicePrompt(array $options = [])
* @method-extended mixed waitForDevicePrompt(array<string, mixed> $options = null)
* @method void resize(array $params)
* @method-extended void resize(array{ $contentWidth: float, $contentHeight: float } $params)
*/
class Page extends EventEmitter
{
use AliasesEvaluationMethods;
use AliasesSelectionMethods;
}
================================================
FILE: src/Resources/PageTarget.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @method \Nesk\Puphpeteer\Resources\Page|null page()
* @method-extended \Nesk\Puphpeteer\Resources\Page|null page()
*/
class PageTarget extends Target
{
}
================================================
FILE: src/Resources/Realm.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @property \Nesk\Puphpeteer\Resources\TaskManager $taskManager
* @property-read mixed $environment
* @property-read bool $disposed
* @method mixed adoptHandle(mixed $handle)
* @method-extended mixed adoptHandle(mixed $handle)
* @method mixed transferHandle(mixed $handle)
* @method-extended mixed transferHandle(mixed $handle)
* @method mixed evaluateHandle(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluateHandle(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed evaluate(\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method-extended mixed evaluate(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, mixed ...$args)
* @method mixed waitForFunction(\Nesk\Rialto\Data\JsFunction $pageFunction, array $options = [], mixed ...$args)
* @method-extended mixed waitForFunction(callable|\Nesk\Rialto\Data\JsFunction $pageFunction, array{ $polling: 'raf'|'mutation'|float, $timeout: float, $root: \Nesk\Puphpeteer\Resources\ElementHandle|mixed[], $signal: mixed } $options = null, mixed ...$args)
* @method \Nesk\Puphpeteer\Resources\JSHandle|mixed[] adoptBackendNode(float $backendNodeId = null)
* @method-extended \Nesk\Puphpeteer\Resources\JSHandle|mixed[] adoptBackendNode(float $backendNodeId = null)
* @method void dispose()
* @method-extended void dispose()
*/
class Realm
{
}
================================================
FILE: src/Resources/ScreenRecorder.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @method void stop()
* @method-extended void stop()
*/
class ScreenRecorder
{
}
================================================
FILE: src/Resources/SecurityDetails.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method string issuer()
* @method-extended string issuer()
* @method float validFrom()
* @method-extended float validFrom()
* @method float validTo()
* @method-extended float validTo()
* @method string protocol()
* @method-extended string protocol()
* @method string subjectName()
* @method-extended string subjectName()
* @method string[] subjectAlternativeNames()
* @method-extended string[] subjectAlternativeNames()
*/
class SecurityDetails extends BasicResource
{
}
================================================
FILE: src/Resources/Target.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method \Nesk\Puphpeteer\Resources\WebWorker|null worker()
* @method-extended \Nesk\Puphpeteer\Resources\WebWorker|null worker()
* @method \Nesk\Puphpeteer\Resources\Page|null page()
* @method-extended \Nesk\Puphpeteer\Resources\Page|null page()
* @method \Nesk\Puphpeteer\Resources\Page asPage()
* @method-extended \Nesk\Puphpeteer\Resources\Page asPage()
* @method string url()
* @method-extended string url()
* @method \Nesk\Puphpeteer\Resources\CDPSession createCDPSession()
* @method-extended \Nesk\Puphpeteer\Resources\CDPSession createCDPSession()
* @method mixed type()
* @method-extended mixed type()
* @method \Nesk\Puphpeteer\Resources\Browser browser()
* @method-extended \Nesk\Puphpeteer\Resources\Browser browser()
* @method \Nesk\Puphpeteer\Resources\BrowserContext browserContext()
* @method-extended \Nesk\Puphpeteer\Resources\BrowserContext browserContext()
* @method \Nesk\Puphpeteer\Resources\Target|null opener()
* @method-extended \Nesk\Puphpeteer\Resources\Target|null opener()
*/
class Target extends BasicResource
{
}
================================================
FILE: src/Resources/TaskManager.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @method void add(\Nesk\Puphpeteer\Resources\WaitTask|mixed[] $task)
* @method-extended void add(\Nesk\Puphpeteer\Resources\WaitTask|mixed[] $task)
* @method void delete(\Nesk\Puphpeteer\Resources\WaitTask|mixed[] $task)
* @method-extended void delete(\Nesk\Puphpeteer\Resources\WaitTask|mixed[] $task)
* @method void terminateAll(mixed $error = null)
* @method-extended void terminateAll(mixed $error = null)
* @method void rerunAll()
* @method-extended void rerunAll()
*/
class TaskManager
{
}
================================================
FILE: src/Resources/TimeoutError.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
class TimeoutError extends BasicResource
{
}
================================================
FILE: src/Resources/TimeoutSettings.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @method void setDefaultTimeout(float $timeout)
* @method-extended void setDefaultTimeout(float $timeout)
* @method void setDefaultNavigationTimeout(float $timeout)
* @method-extended void setDefaultNavigationTimeout(float $timeout)
* @method float navigationTimeout()
* @method-extended float navigationTimeout()
* @method float timeout()
* @method-extended float timeout()
*/
class TimeoutSettings
{
}
================================================
FILE: src/Resources/Touchscreen.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @property mixed $idGenerator
* @property mixed[] $touches
* @method void removeHandle(mixed $handle)
* @method-extended void removeHandle(mixed $handle)
* @method void tap(float $x, float $y)
* @method-extended void tap(float $x, float $y)
* @method mixed touchStart(float $x, float $y)
* @method-extended mixed touchStart(float $x, float $y)
* @method void touchMove(float $x, float $y)
* @method-extended void touchMove(float $x, float $y)
* @method void touchEnd()
* @method-extended void touchEnd()
*/
class Touchscreen extends BasicResource
{
}
================================================
FILE: src/Resources/Tracing.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @method void updateClient(\Nesk\Puphpeteer\Resources\CDPSession $client)
* @method-extended void updateClient(\Nesk\Puphpeteer\Resources\CDPSession $client)
* @method void start(array $options = [])
* @method-extended void start(array<string, mixed> $options = null)
* @method \Nesk\Puphpeteer\Resources\Uint8Array|null stop()
* @method-extended \Nesk\Puphpeteer\Resources\Uint8Array|null stop()
*/
class Tracing extends BasicResource
{
}
================================================
FILE: src/Resources/Uint8Array.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* Uint8Array automatically converted to Buffer to support easy base64 convertation
*/
class Uint8Array extends Buffer
{
}
================================================
FILE: src/Resources/WaitTask.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @property-read Promise|mixed[] $result
* @method void rerun()
* @method-extended void rerun()
* @method void terminate(mixed $error = null)
* @method-extended void terminate(mixed $error = null)
* @method mixed|null getBadError(mixed $error)
* @method-extended mixed|null getBadError(mixed $error)
*/
class WaitTask
{
}
================================================
FILE: src/Resources/WebWorker.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
use Nesk\Rialto\Data\BasicResource;
/**
* @property \Nesk\Puphpeteer\Resources\TimeoutSettings $timeoutSettings
* @property-read \Nesk\Puphpeteer\Resources\CDPSession $client
* @method \Nesk\Puphpeteer\Resources\Realm mainRealm()
* @method-extended \Nesk\Puphpeteer\Resources\Realm mainRealm()
* @method string url()
* @method-extended string url()
* @method mixed evaluate(mixed|string $func, mixed ...$args)
* @method-extended mixed evaluate(mixed|string $func, mixed ...$args)
* @method mixed evaluateHandle(mixed|string $func, mixed ...$args)
* @method-extended mixed evaluateHandle(mixed|string $func, mixed ...$args)
* @method void close()
* @method-extended void close()
*/
class WebWorker extends BasicResource
{
}
================================================
FILE: src/Resources/WorkerTarget.php
================================================
<?php
namespace Nesk\Puphpeteer\Resources;
/**
* @method mixed|null worker()
* @method-extended mixed|null worker()
*/
class WorkerTarget extends Target
{
}
================================================
FILE: src/Traits/AliasesEvaluationMethods.php
================================================
<?php
namespace Nesk\Puphpeteer\Traits;
use Nesk\Puphpeteer\Resources\JSHandle;
use Nesk\Rialto\Data\JsFunction;
/**
* @method null|array|bool|float|int|string querySelectorEval(string $selector, JsFunction $pageFunction, null|array|bool|float|int|JSHandle|string ...$args)
* @method null|array|bool|float|int|string querySelectorAllEval(string $selector, JsFunction $pageFunction, null|array|bool|float|int|JSHandle|string ...$args)
*/
trait AliasesEvaluationMethods
{
public function querySelectorEval(...$arguments)
{
return $this->__call('$eval', $arguments);
}
public function querySelectorAllEval(...$arguments)
{
return $this->__call('$$eval', $arguments);
}
}
================================================
FILE: src/Traits/AliasesSelectionMethods.php
================================================
<?php
namespace Nesk\Puphpeteer\Traits;
use Nesk\Puphpeteer\Resources\ElementHandle;
/**
* @method null|ElementHandle querySelector(string $selector)
* @method ElementHandle[] querySelectorAll(string $selector)
*/
trait AliasesSelectionMethods
{
public function querySelector(...$arguments)
{
return $this->__call('$', $arguments);
}
public function querySelectorAll(...$arguments)
{
return $this->__call('$$', $arguments);
}
}
================================================
FILE: src/doc-generator.ts
================================================
import * as ts from 'typescript';
const yargs = require('yargs/yargs');
const { hideBin } = require('yargs/helpers');
const callbackClass = '\\Nesk\\Rialto\\Data\\JsFunction';
type ObjectMemberAsJson = { [key: string]: string; }
type ObjectMembersAsJson = {
properties: ObjectMemberAsJson,
getters: ObjectMemberAsJson,
methods: ObjectMemberAsJson,
}
type ClassAsJson = { name: string } & ObjectMembersAsJson
type MemberContext = 'class'|'literal'
type TypeContext = 'methodReturn'
class TypeNotSupportedError extends Error {
constructor(message?: string) {
super('This type is currently not supported: ' + message);
}
}
interface SupportChecker {
supportsMethodName(methodName: string): boolean;
}
class JsSupportChecker {
supportsMethodName(methodName: string): boolean {
return true;
}
}
class PhpSupportChecker {
supportsMethodName(methodName: string): boolean {
return !methodName.includes('$');
}
}
interface DocumentationFormatter {
formatProperty(name: string, type: string, context: MemberContext): string
formatGetter(name: string, type: string): string
formatAnonymousFunction(parameters: string, returnType: string): string
formatFunction(name: string, parameters: string, returnType: string): string
formatParameter(name: string, type: string, isVariadic: boolean, isOptional: boolean): string
formatTypeAny(): string
formatTypeUnknown(): string
formatTypeVoid(): string
formatTypeUndefined(): string
formatTypeNull(): string
formatTypeBoolean(): string
formatTypeNumber(): string
formatTypeString(): string
formatTypeReference(type: string): string
formatGeneric(parentType: string, argumentTypes: string[], context?: TypeContext): string
formatQualifiedName(left: string, right: string): string
formatIndexedAccessType(object: string, index: string): string
formatLiteralType(value: string): string
formatUnion(types: string[]): string
formatIntersection(types: string[]): string
formatObject(members: string[]): string
formatArray(type: string): string
}
class JsDocumentationFormatter implements DocumentationFormatter {
formatProperty(name: string, type: string, context: MemberContext): string {
return `${name}: ${type}`;
}
formatGetter(name: string, type: string): string {
return `${name}: ${type}`;
}
formatAnonymousFunction(parameters: string, returnType: string): string {
return `(${parameters}) => ${returnType}`;
}
formatFunction(name: string, parameters: string, returnType: string): string {
return `${name}(${parameters}): ${returnType}`;
}
formatParameter(name: string, type: string, isVariadic: boolean, isOptional: boolean): string {
return `${isVariadic ? '...' : ''}${name}${isOptional ? '?' : ''}: ${type}`;
}
formatTypeAny(): string {
return 'any';
}
formatTypeUnknown(): string {
return 'unknown';
}
formatTypeVoid(): string {
return 'void';
}
formatTypeUndefined(): string {
return 'undefined';
}
formatTypeNull(): string {
return 'null';
}
formatTypeBoolean(): string {
return 'boolean';
}
formatTypeNumber(): string {
return 'number';
}
formatTypeString(): string {
return 'string';
}
formatTypeReference(type: string): string {
return type;
}
formatGeneric(parentType: string, argumentTypes: string[], context?: TypeContext): string {
return `${parentType}<${argumentTypes.join(', ')}>`;
}
formatQualifiedName(left: string, right: string): string {
return `${left}.${right}`;
}
formatIndexedAccessType(object: string, index: string): string {
return `${object}[${index}]`;
}
formatLiteralType(value: string): string {
return `'${value}'`;
}
formatUnion(types: string[]): string {
return types.join(' | ');
}
formatIntersection(types: string[]): string {
return types.join(' & ');
}
formatObject(members: string[]): string {
return `{ ${members.join(', ')} }`;
}
formatArray(type: string): string {
return `${type}[]`;
}
}
class PhpDocumentationFormatter implements DocumentationFormatter {
static readonly allowedJsClasses = ['Promise', 'Record', 'Map'];
constructor(
protected readonly resourcesNamespace: string,
protected readonly resources: string[],
) {}
formatProperty(name: string, type: string, context: MemberContext): string {
return context === 'class'
? `${type} \$${name}`
: `\$${name}: ${type}`;
}
formatGetter(name: string, type: string): string {
return `${type} \$${name}`;
}
formatAnonymousFunction(parameters: string, returnType: string): string {
return callbackClass;
}
formatFunction(name: string, parameters: string, returnType: string): string {
return `${returnType} ${name}(${parameters})`;
}
formatParameter(name: string, type: string, isVariadic: boolean, isOptional: boolean): string {
if (name === 'this') {
name = 'selector'
}
if (isVariadic && type.endsWith('[]')) {
type = type.slice(0, -2);
}
let defaultValue;
switch (type) {
case 'array' :
defaultValue = isOptional ? ' = []' : '';
break;
default:
defaultValue = isOptional ? ' = null' : '';
break;
}
return `${type} ${isVariadic ? '...' : ''}\$${name}${defaultValue}`;
}
formatTypeAny(): string {
return 'mixed';
}
formatTypeUnknown(): string {
return 'mixed';
}
formatTypeVoid(): string {
return 'void';
}
formatTypeUndefined(): string {
return 'null';
}
formatTypeNull(): string {
return 'null';
}
formatTypeBoolean(): string {
return 'bool';
}
formatTypeNumber(): string {
return 'float';
}
formatTypeString(): string {
return 'string';
}
formatTypeReference(type: string): string {
// Allow some specific JS classes to be used in phpDoc
if (PhpDocumentationFormatter.allowedJsClasses.includes(type)) {
return type;
}
// Prefix PHP resources with their namespace
if (this.resources.includes(type)) {
return this.resourcesNamespace ? `\\${this.resourcesNamespace}\\${type}` : type;
}
// If the type ends with "options" then convert it to an associative array
if (/options$/i.test(type)) {
return 'array';
}
// Types ending with "Fn" are always callables or strings
if (type.endsWith('Fn')) {
return this.formatUnion([callbackClass, 'string']);
}
if (type === 'Function') {
return callbackClass;
}
if (type === 'PuppeteerLifeCycleEvent') {
return 'string';
}
if (type === 'Serializable') {
return this.formatUnion(['int', 'float', 'string', 'bool', 'null', 'array']);
}
if (type === 'SerializableOrJSHandle') {
return this.formatUnion([this.formatTypeReference('Serializable'), this.formatTypeReference('JSHandle')]);
}
if (type === 'HandleType') {
return this.formatUnion([this.formatTypeReference('JSHandle'), this.formatTypeReference('ElementHandle')]);
}
return 'mixed';
}
formatGeneric(parentType: string, argumentTypes: string[], context?: TypeContext): string {
// Avoid generics with "mixed" as parent type
if (parentType === 'mixed') {
return 'mixed';
}
// Unwrap promises for method return types
if (context === 'methodReturn' && parentType === 'Promise' && argumentTypes.length === 1) {
return argumentTypes[0];
}
// Transform Record and Map types to associative arrays
if (['Record', 'Map'].includes(parentType) && argumentTypes.length === 2) {
parentType = 'array';
}
return `${parentType}|${argumentTypes.join('[]|')}[]`;
}
formatQualifiedName(left: string, right: string): string {
return `mixed`;
}
formatIndexedAccessType(object: string, index: string): string {
return `mixed`;
}
formatLiteralType(value: string): string {
return `'${value}'`;
}
private prepareUnionOrIntersectionTypes(types: string[]): string[] {
// Replace "void" type by "null"
types = types.map(type => type === 'void' ? 'null' : type)
// Remove duplicates
const uniqueTypes = new Set(types);
return Array.from(uniqueTypes.values());
}
formatUnion(types: string[]): string {
const result = this.prepareUnionOrIntersectionTypes(types).join('|');
// Convert enums to string type
if (/^('\w+'\|)*'\w+'$/.test(result)) {
return 'string';
}
return result;
}
formatIntersection(types: string[]): string {
return this.prepareUnionOrIntersectionTypes(types).join('&');
}
formatObject(members: string[]): string {
return `array`;
}
formatArray(type: string): string {
return `${type}[]`;
}
}
class PhpStanDocumentationFormatter extends PhpDocumentationFormatter {
formatAnonymousFunction(parameters: string, returnType: string): string {
return `callable(${parameters}): ${returnType}|` + callbackClass;
}
formatTypeReference(type: string): string {
// Allow some specific JS classes to be used in phpDoc
if (PhpDocumentationFormatter.allowedJsClasses.includes(type)) {
return type;
}
// Prefix PHP resources with their namespace
if (this.resources.includes(type)) {
return this.resourcesNamespace ? `\\${this.resourcesNamespace}\\${type}` : type;
}
// If the type ends with "options" then convert it to an associative array
if (/options$/i.test(type)) {
return 'array<string, mixed>';
}
// Types ending with "Fn" are always callables or strings
if (type.endsWith('Fn')) {
return this.formatUnion([callbackClass, 'callable', 'string']);
}
if (type === 'Function') {
return this.formatUnion(['callable', callbackClass]);
}
if (type === 'PuppeteerLifeCycleEvent') {
return 'string';
}
if (type === 'Serializable') {
return this.formatUnion(['int', 'float', 'string', 'bool', 'null', 'array']);
}
if (type === 'SerializableOrJSHandle') {
return this.formatUnion([this.formatTypeReference('Serializable'), this.formatTypeReference('JSHandle')]);
}
if (type === 'HandleType') {
return this.formatUnion([this.formatTypeReference('JSHandle'), this.formatTypeReference('ElementHandle')]);
}
return 'mixed';
}
formatObject(members: string[]): string {
return `array{ ${members.join(', ')} }`;
}
}
class DocumentationGenerator {
constructor(
private readonly supportChecker: SupportChecker,
private readonly formatter: DocumentationFormatter,
) {}
private hasModifierForNode(
node: ts.Node & { modifiers?: ts.Modifier[] },
modifier: ts.KeywordSyntaxKind,
): boolean {
if (!node.modifiers) {
return false;
}
return node.modifiers.some((node) => node.kind === modifier);
}
private isNodeAccessible(node: ts.Node): boolean {
// @ts-ignore
if (node.name && (this.getNamedDeclarationAsString(node).startsWith('_') || this.getNamedDeclarationAsString(node).startsWith('#'))) {
return false;
}
return (
this.hasModifierForNode(node, ts.SyntaxKind.PublicKeyword) ||
(!this.hasModifierForNode(node, ts.SyntaxKind.ProtectedKeyword) &&
!this.hasModifierForNode(node, ts.SyntaxKind.PrivateKeyword))
);
}
private isNodeStatic(node: ts.Node): boolean {
return this.hasModifierForNode(node, ts.SyntaxKind.StaticKeyword);
}
public getClassDeclarationAsJson(node: ts.ClassDeclaration): ClassAsJson {
return Object.assign(
{ name: this.getNamedDeclarationAsString(node) },
this.getMembersAsJson(node.members, 'class'),
);
}
private getMembersAsJson(members: ts.NodeArray<ts.NamedDeclaration>, context: MemberContext): ObjectMembersAsJson {
const json: ObjectMembersAsJson = {
properties: {},
getters: {},
methods: {},
};
for (const member of members) {
if (!this.isNodeAccessible(member) || this.isNodeStatic(member)) {
continue;
}
const name = member.name ? this.getNamedDeclarationAsString(member) : null;
if (ts.isPropertySignature(member) || ts.isPropertyDeclaration(member)) {
json.properties[name] = this.getPropertySignatureOrDeclarationAsString(member, context);
} else if (ts.isGetAccessorDeclaration(member)) {
json.getters[name] = this.getGetAccessorDeclarationAsString(member);
} else if (ts.isMethodDeclaration(member)) {
if (!this.supportChecker.supportsMethodName(name)) {
continue;
}
json.methods[name] = this.getSignatureDeclarationBaseAsString(member);
}
}
return json;
}
private getPropertySignatureOrDeclarationAsString(
node: ts.PropertySignature | ts.PropertyDeclaration,
context: MemberContext
): string {
const type = this.getTypeNodeAsString(node.type);
const name = this.getNamedDeclarationAsString(node);
return this.formatter.formatProperty(name, type, context);
}
private getGetAccessorDeclarationAsString(
node: ts.GetAccessorDeclaration
): string {
const type = this.getTypeNodeAsString(node.type);
const name = this.getNamedDeclarationAsString(node);
return this.formatter.formatGetter(name, type);
}
private getSignatureDeclarationBaseAsString(
node: ts.SignatureDeclarationBase
): string {
const name = node.name && this.getNamedDeclarationAsString(node);
const parameters = node.parameters
.map(parameter => this.getParameterDeclarationAsString(parameter))
.join(', ');
const returnType = this.getTypeNodeAsString(node.type, name ? 'methodReturn' : undefined);
return name
? this.formatter.formatFunction(name, parameters, returnType)
: this.formatter.formatAnonymousFunction(parameters, returnType);
}
private getEmptyFunctionSignatureAsString(
node: ts.ParenthesizedTypeNode
): string {
return this.formatter.formatAnonymousFunction(this.getTypeNodeAsString(node.type), '');
}
private getParameterDeclarationAsString(node: ts.ParameterDeclaration): string {
const name = this.getNamedDeclarationAsString(node);
let type = this.getTypeNodeAsString(node.type);
const isVariadic = node.dotDotDotToken !== undefined;
const isOptional = node.questionToken !== undefined;
//fix missing argument type in evaluate* methods.
if (name.includes('Function') && type.includes('mixed')) {
type = this.formatter.formatTypeReference('Function');
}
return this.formatter.formatParameter(name, type, isVariadic, isOptional);
}
private getTypeNodeAsString(node: ts.TypeNode, context?: TypeContext): string {
if (!node) {
return '';
}
if (node.kind === ts.SyntaxKind.AnyKeyword) {
return this.formatter.formatTypeAny();
} else if (node.kind === ts.SyntaxKind.UnknownKeyword) {
return this.formatter.formatTypeUnknown();
} else if (node.kind === ts.SyntaxKind.VoidKeyword) {
return this.formatter.formatTypeVoid();
} else if (node.kind === ts.SyntaxKind.UndefinedKeyword) {
return this.formatter.formatTypeUndefined();
} else if (node.kind === ts.SyntaxKind.NullKeyword) {
return this.formatter.formatTypeNull();
} else if (node.kind === ts.SyntaxKind.BooleanKeyword) {
return this.formatter.formatTypeBoolean();
} else if (node.kind === ts.SyntaxKind.NumberKeyword) {
return this.formatter.formatTypeNumber();
} else if (node.kind === ts.SyntaxKind.StringKeyword) {
return this.formatter.formatTypeString();
} else if (ts.isTypeReferenceNode(node)) {
return this.getTypeReferenceNodeAsString(node, context);
} else if (ts.isIndexedAccessTypeNode(node)) {
return this.getIndexedAccessTypeNodeAsString(node);
} else if (ts.isLiteralTypeNode(node)) {
return this.getLiteralTypeNodeAsString(node);
} else if (ts.isUnionTypeNode(node)) {
return this.getUnionTypeNodeAsString(node, context);
} else if (ts.isIntersectionTypeNode(node)) {
return this.getIntersectionTypeNodeAsString(node, context);
} else if (ts.isTypeLiteralNode(node)) {
return this.getTypeLiteralNodeAsString(node);
} else if (ts.isArrayTypeNode(node)) {
return this.getArrayTypeNodeAsString(node, context);
} else if (ts.isFunctionTypeNode(node)) {
return this.getSignatureDeclarationBaseAsString(node);
} else if (ts.isParenthesizedTypeNode(node)) {
return this.getEmptyFunctionSignatureAsString(node);
} else {
console.error('Unknown type: ' + ts.SyntaxKind[node.kind])
return this.formatter.formatTypeAny();
}
}
private getTypeReferenceNodeAsString(node: ts.TypeReferenceNode, context?: TypeContext): string {
return this.getGenericTypeReferenceNodeAsString(node, context) || this.getSimpleTypeReferenceNodeAsString(node);
}
private getGenericTypeReferenceNodeAsString(node: ts.TypeReferenceNode, context?: TypeContext): string | null {
if (!node.typeArguments || node.typeArguments.length === 0) {
return null;
}
const parentType = this.getSimpleTypeReferenceNodeAsString(node);
const argumentTypes = node.typeArguments.map((node) => this.getTypeNodeAsString(node));
return this.formatter.formatGeneric(parentType, argumentTypes, context);
}
private getSimpleTypeReferenceNodeAsString(node: ts.TypeReferenceNode): string {
return ts.isIdentifier(node.typeName)
? this.formatter.formatTypeReference(this.getIdentifierAsString(node.typeName))
: this.getQualifiedNameAsString(node.typeName);
}
private getQualifiedNameAsString(node: ts.QualifiedName): string {
const right = this.getIdentifierAsString(node.right);
const left = ts.isIdentifier(node.left)
? this.getIdentifierAsString(node.left)
: this.getQualifiedNameAsString(node.left);
return this.formatter.formatQualifiedName(left, right);
}
private getIndexedAccessTypeNodeAsString(
node: ts.IndexedAccessTypeNode
): string {
const object = this.getTypeNodeAsString(node.objectType);
const index = this.getTypeNodeAsString(node.indexType);
return this.formatter.formatIndexedAccessType(object, index);
}
private getLiteralTypeNodeAsString(node: ts.LiteralTypeNode): string {
if (node.literal.kind === ts.SyntaxKind.NullKeyword) {
return this.formatter.formatTypeNull();
} else if (node.literal.kind === ts.SyntaxKind.BooleanKeyword) {
return this.formatter.formatTypeBoolean();
} else if (ts.isLiteralExpression(node.literal)) {
return this.formatter.formatLiteralType(node.literal.text);
}
throw new TypeNotSupportedError();
}
private getUnionTypeNodeAsString(node: ts.UnionTypeNode, context?: TypeContext): string {
const types = node.types.map(typeNode => this.getTypeNodeAsString(typeNode, context));
return this.formatter.formatUnion(types);
}
private getIntersectionTypeNodeAsString(node: ts.IntersectionTypeNode, context?: TypeContext): string {
const types = node.types.map(typeNode => this.getTypeNodeAsString(typeNode, context));
return this.formatter.formatIntersection(types);
}
private getTypeLiteralNodeAsString(node: ts.TypeLiteralNode): string {
const members = this.getMembersAsJson(node.members, 'literal');
const stringMembers = Object.values(members).map(Object.values);
const flattenMembers = stringMembers.reduce((acc, val) => acc.concat(val), []);
return this.formatter.formatObject(flattenMembers);
}
private getArrayTypeNodeAsString(node: ts.ArrayTypeNode, context?: TypeContext): string {
const type = this.getTypeNodeAsString(node.elementType, context);
return this.formatter.formatArray(type);
}
private getNamedDeclarationAsString(node: ts.NamedDeclaration): string {
if (!ts.isIdentifier(node.name) && !ts.isPrivateIdentifier(node.name)) {
console.warn('Unknown type: ' + ts.SyntaxKind[node.kind]);
return '#';
}
return this.getIdentifierAsString(node.name);
}
private getIdentifierAsString(node: ts.Identifier|ts.PrivateIdentifier): string {
return String(node.escapedText);
}
}
const { argv } = yargs(hideBin(process.argv))
.command('$0 <language> <definition-files...>')
.option('resources-namespace', { type: 'string', default: '' })
.option('resources', { type: 'array', default: [] })
.option('pretty', { type: 'boolean', default: false })
let supportChecker, formatter;
switch (argv.language.toUpperCase()) {
case 'JS':
supportChecker = new JsSupportChecker();
formatter = new JsDocumentationFormatter();
break;
case 'PHP':
supportChecker = new PhpSupportChecker();
formatter = new PhpDocumentationFormatter(argv.resourcesNamespace, argv.resources);
break;
case 'PHPSTAN':
supportChecker = new PhpSupportChecker();
formatter = new PhpStanDocumentationFormatter(argv.resourcesNamespace, argv.resources);
break;
default:
console.error(`Unsupported "${argv.language}" language.`);
process.exit(1);
}
const docGenerator = new DocumentationGenerator(supportChecker, formatter);
const program = ts.createProgram(argv.definitionFiles, {});
const classes = {};
for (const fileName of argv.definitionFiles) {
const sourceFile = program.getSourceFile(fileName);
ts.forEachChild(sourceFile, node => {
if (ts.isClassDeclaration(node)) {
const classAsJson = docGenerator.getClassDeclarationAsJson(node);
classes[classAsJson.name] = classAsJson;
}
});
}
process.stdout.write(JSON.stringify(classes, null, argv.pretty ? 2 : null));
================================================
FILE: src/get-puppeteer-version.js
================================================
'use strict';
function output(value) {
process.stdout.write(JSON.stringify(value));
}
try {
const manifest = require('puppeteer/package.json');
output(manifest.version);
} catch (exception) {
output(null);
}
================================================
FILE: tests/DownloadTest.php
================================================
<?php
namespace Nesk\Puphpeteer\Tests;
use Nesk\Puphpeteer\Puppeteer;
use Nesk\Rialto\Data\JsFunction;
use PHPUnit\Framework\ExpectationFailedException;
use Nesk\Puphpeteer\Resources\ElementHandle;
class DownloadTest extends TestCase
{
public function setUp(): void
{
parent::setUp();
// Serve the content of the "/resources"-folder to test these.
$this->serveResources();
// Launch the browser to run tests on.
$this->launchBrowser();
}
/**
* Downloads an image and checks string length.
*
*/
public function testDownloadImage()
{
// Download the image
$page = $this->browser
->newPage()
->goto($this->url . '/puphpeteer-logo.png');
$base64 = $page->buffer()->toString('base64');
$imageString = base64_decode($base64);
// Get the reference image from resources
$reference = file_get_contents('tests/resources/puphpeteer-logo.png');
$this->assertTrue(
mb_strlen($reference) === mb_strlen($imageString),
'Image is not the same length after download.'
);
}
public function testDownloadPdf()
{
$page = $this->browser->newPage();
$page->goto($this->url);
$base64 = $page->pdf()->toString('base64');
$pdfBytes = base64_decode($base64);
$reference = file_get_contents('tests/resources/example.pdf');
$this->assertTrue(
mb_strlen($reference) === mb_strlen($pdfBytes),
'Pdf of the main page is not the same length after generation.'
);
}
/**
* Downloads an image and checks string length.
*
* @test
*/
// public function download_large_image()
// {
// // Download the image
// $page = $this->browser
// ->newPage()
// ->goto($this->url . '/denys-barabanov-jKcFmXCfaQ8-unsplash.jpg');
// $base64 = $page->buffer()->toString('base64');
// $imageString = base64_decode($base64);
// // Get the reference image from resources
// $reference = file_get_contents('tests/resources/denys-barabanov-jKcFmXCfaQ8-unsplash.jpg');
// $this->assertTrue(
// mb_strlen($reference) === mb_strlen($imageString),
// 'Large image is not the same length after download.'
// );
// }
}
================================================
FILE: tests/PuphpeteerTest.php
================================================
<?php
namespace Nesk\Puphpeteer\Tests;
use Nesk\Puphpeteer\Puppeteer;
use Nesk\Puphpeteer\Resources\Frame;
use Nesk\Puphpeteer\Resources\Page;
use Nesk\Rialto\Data\JsFunction;
use PHPUnit\Framework\ExpectationFailedException;
use Nesk\Puphpeteer\Resources\ElementHandle;
use Psr\Log\LoggerInterface;
class PuphpeteerTest extends TestCase
{
public function setUp(): void
{
parent::setUp();
// Serve the content of the "/resources"-folder to test these.
$this->serveResources();
// Launch the browser to run tests on.
$this->launchBrowser();
}
/** @test */
public function can_browse_website()
{
$response = $this->browser->newPage()->goto($this->url);
$this->assertTrue($response->ok(), 'Failed asserting that the response is successful.');
}
/**
* @test
*/
public function can_use_method_aliases()
{
$page = $this->browser->newPage();
$page->goto($this->url);
$select = function(Page | Frame | ElementHandle $resource) {
$elements = [
$resource->querySelector('h1'),
$resource->querySelectorAll('h1')[0],
$resource->querySelectorAll('::-p-xpath(/html/body/h1)')[0],
];
$this->assertContainsOnlyInstancesOf(ElementHandle::class, $elements);
};
$evaluate = function(Page | Frame | ElementHandle $resource) {
$strings = [
$resource->querySelectorEval('h1', JsFunction::createWithBody('return "Hello World!";')),
$resource->querySelectorAllEval('h1', JsFunction::createWithBody('return "Hello World!";')),
];
foreach ($strings as $string) {
$this->assertEquals('Hello World!', $string);
}
};
// Test method aliases for Page, Frame and ElementHandle classes
$resources = [$page, $page->mainFrame(), $page->querySelector('body')];
foreach ($resources as $resource) {
$select($resource);
$evaluate($resource);
}
}
/** @test */
public function can_evaluate_a_selection()
{
$page = $this->browser->newPage();
$page->goto($this->url);
$title = $page->querySelectorEval('h1', JsFunction::createWithParameters(['node'])
->body('return node.textContent;'));
$titleCount = $page->querySelectorAllEval('h1', JsFunction::createWithParameters(['nodes'])
->body('return nodes.length;'));
$this->assertEquals('Example Page', $title);
$this->assertEquals(1, $titleCount);
}
/** @test */
public function can_intercept_requests()
{
$page = $this->browser->newPage();
$page->setRequestInterception(true);
$page->on('request', JsFunction::createWithParameters(['request'])
->body('request.resourceType() === "stylesheet" ? request.abort() : request.continue()'));
$page->goto($this->url);
$backgroundColor = $page->querySelectorEval('h1', JsFunction::createWithParameters(['node'])
->body('return getComputedStyle(node).textTransform'));
$this->assertNotEquals('lowercase', $backgroundColor);
}
/**
* @test
* @dataProvider resourceProvider
* @dontPopulateProperties browser
*/
public function check_all_resources_are_supported(string $name)
{
$incompleteTest = false;
$resourceInstantiator = new ResourceInstantiator($this->browserOptions, $this->url);
$resource = $resourceInstantiator->{$name}(new Puppeteer, $this->browserOptions);
if ($resource instanceof UntestableResource) {
$incompleteTest = true;
} else if ($resource instanceof RiskyResource) {
if (!empty($resource->exception())) {
$incompleteTest = true;
} else {
try {
$this->assertInstanceOf("Nesk\\Puphpeteer\\Resources\\$name", $resource->value());
} catch (ExpectationFailedException $exception) {
$incompleteTest = true;
}
}
} else {
$this->assertInstanceOf("\\Nesk\\Puphpeteer\\Resources\\$name", $resource, json_encode($resource));
}
if (!$incompleteTest) return;
$reason = "The \"$name\" resource has not been tested properly, probably"
." for a good reason but you might want to have a look: \n\n ";
if ($resource instanceof UntestableResource) {
$reason .= "\e[33mMarked as untestable.\e[0m";
} else {
if (!empty($exception = $resource->exception())) {
$reason .= "\e[31mMarked as risky because of a Node error: {$exception->getMessage()}\e[0m";
} else {
$value = print_r($resource->value(), true);
$reason .= "\e[31mMarked as risky because of an unexpected value: $value\e[0m";
}
}
$this->markTestIncomplete($reason);
}
public static function resourceProvider(): \Generator
{
$resourceNames = (new ResourceInstantiator([], ''))->getResourceNames();
foreach ($resourceNames as $name) {
yield [$name];
}
}
private function createBrowserLogger(callable $onBrowserLog): LoggerInterface
{
$logger = $this->createMock(LoggerInterface::class);
$logger->expects(self::atLeastOnce())
->method('log')
->willReturnCallback(function (string $level, string $message) use ($onBrowserLog) {
if (\strpos($message, "Received a Browser log:") === 0) {
$onBrowserLog();
}
return null;
});
return $logger;
}
/**
* @test
* @dontPopulateProperties browser
*/
public function browser_console_calls_are_logged_if_enabled()
{
$browserLogOccured = false;
$logger = $this->createBrowserLogger(function () use (&$browserLogOccured) {
$browserLogOccured = true;
});
$puppeteer = new Puppeteer([
'log_browser_console' => true,
'logger' => $logger,
]);
$this->browser = $puppeteer->launch($this->browserOptions);
$this->browser->pages()[0]->goto($this->url);
static::assertTrue($browserLogOccured);
}
/**
* @test
* @dontPopulateProperties browser
*/
public function browser_console_calls_are_not_logged_if_disabled()
{
$browserLogOccured = false;
$logger = $this->createBrowserLogger(function () use (&$browserLogOccured) {
$browserLogOccured = true;
});
$puppeteer = new Puppeteer([
'log_browser_console' => false,
'logger' => $logger,
]);
$this->browser = $puppeteer->launch($this->browserOptions);
$this->browser->pages()[0]->goto($this->url);
static::assertFalse($browserLogOccured);
}
}
================================================
FILE: tests/ResourceInstantiator.php
================================================
<?php
namespace Nesk\Puphpeteer\Tests;
use Nesk\Puphpeteer\Puppeteer;
use Nesk\Puphpeteer\Resources\Browser;
use Nesk\Puphpeteer\Resources\Frame;
use Nesk\Puphpeteer\Resources\HTTPResponse;
use Nesk\Puphpeteer\Resources\Page;
use Nesk\Rialto\Data\JsFunction;
/**
* @method Page Page(Puppeteer $puppeteer)
* @method Browser Browser(Puppeteer $puppeteer)
* @method Frame Frame(Puppeteer $puppeteer)
* @method HTTPResponse HttpResponse(Puppeteer $puppeteer)
*/
class ResourceInstantiator
{
protected $resources = [];
public function __construct(
public array $browserOptions,
public string $url
) {
$this->resources = [
'Accessibility' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->accessibility;
},
'Browser' => function (Puppeteer $puppeteer): Browser {
return $puppeteer->launch($this->browserOptions);
},
'BrowserContext' => function (Puppeteer $puppeteer) {
return $this->Browser($puppeteer)->createBrowserContext();
},
'CDPSession' => function (Puppeteer $puppeteer) {
return $this->Target($puppeteer)->createCDPSession();
},
'ConsoleMessage' => function () {
return new UntestableResource;
},
'Coverage' => function ($puppeteer) {
return $this->Page($puppeteer)->coverage;
},
'Dialog' => function () {
return new UntestableResource;
},
'ElementHandle' => function ($puppeteer) {
return $this->Page($puppeteer)->querySelector('body');
},
'EventEmitter' => function (Puppeteer $puppeteer) {
return $puppeteer->launch($this->browserOptions);
},
'FileChooser' => function () {
return new UntestableResource;
},
'Frame' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->mainFrame();
},
'HTTPRequest' => function (Puppeteer $puppeteer) {
return $this->HTTPResponse($puppeteer)->request();
},
'HTTPResponse' => function (Puppeteer $puppeteer): HTTPResponse {
return $this->Page($puppeteer)->goto($this->url);
},
'JSHandle' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->evaluateHandle(JsFunction::createWithBody('window'));
},
'Keyboard' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->keyboard;
},
'Mouse' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->mouse;
},
'Page' => function (Puppeteer $puppeteer): Page {
return $this->Browser($puppeteer)->newPage();
},
'SecurityDetails' => function (Puppeteer $puppeteer) {
return new RiskyResource(function () use ($puppeteer) {
return $this->Page($puppeteer)->goto('https://example.com')->securityDetails();
});
},
'Target' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->target();
},
'TimeoutError' => function () {
return new UntestableResource;
},
'Touchscreen' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->touchscreen;
},
'Tracing' => function (Puppeteer $puppeteer) {
return $this->Page($puppeteer)->tracing;
},
'WebWorker' => function (Puppeteer $puppeteer) {
$page = $this->Page($puppeteer);
$page->goto($this->url, ['waitUntil' => 'networkidle0']);
return $page->workers()[0];
},
];
}
public function getResourceNames(): array
{
return array_keys($this->resources);
}
public function __call(string $name, array $arguments)
{
if (!isset($this->resources[$name])) {
throw new \InvalidArgumentException("The $name resource is not supported.");
}
return $this->resources[$name](...$arguments);
}
}
================================================
FILE: tests/RiskyResource.php
================================================
<?php
namespace Nesk\Puphpeteer\Tests;
use Nesk\Rialto\Exceptions\Node\FatalException as NodeFatalException;
class RiskyResource
{
protected $value = null;
protected $exception = null;
public function __construct(callable $resourceRetriever) {
try {
$this->value = $resourceRetriever();
} catch (NodeFatalException $exception) {
$this->exception = $exception;
}
}
public function value() {
return $this->value;
}
public function exception(): ?NodeFatalException {
return $this->exception;
}
}
================================================
FILE: tests/TestCase.php
================================================
<?php
namespace Nesk\Puphpeteer\Tests;
use Nesk\Puphpeteer\Puppeteer;
use Monolog\Logger;
use Nesk\Puphpeteer\Resources\Browser;
use ReflectionClass;
use Psr\Log\LogLevel;
use Symfony\Component\Process\Process;
use PHPUnit\Framework\Constraint\Callback;
use PHPUnit\Framework\TestCase as BaseTestCase;
class TestCase extends BaseTestCase
{
private $dontPopulateProperties = [];
protected string $host;
protected array $browserOptions;
protected string $url;
protected string $serverDir;
protected Process $servingProcess;
protected Browser $browser;
public function setUp(): void
{
parent::setUp();
$methodName = explode(' ', $this->name())[0] ?? '';
$testMethod = new \ReflectionMethod($this, $methodName);
$docComment = $testMethod->getDocComment();
if (preg_match('/@dontPopulateProperties (.*)/', $docComment, $matches)) {
$this->dontPopulateProperties = array_values(array_filter(explode(' ', $matches[1])));
}
}
/**
* Stops the browser and local server
*/
public function tearDown(): void
{
// Close the browser.
if (isset($this->browser)) {
$this->browser->close();
}
// Shutdown the local server
if (isset($this->servingProcess)) {
$this->servingProcess->stop(0);
}
}
/**
* Serves the resources folder locally on port 8089
*/
protected function serveResources(): void
{
// Spin up a local server to deliver the resources.
$this->host = '127.0.0.1:8089';
$this->url = "http://{$this->host}";
$this->serverDir = __DIR__.'/resources';
$this->servingProcess = new Process(['php', '-S', $this->host, '-t', $this->serverDir]);
$this->servingProcess->start();
}
/**
* Launches the PuPHPeteer-controlled browser
*/
protected function launchBrowser(): void
{
/**
* Chrome doesn't support Linux sandbox on many CI environments
*
* @see: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#chrome-headless-fails-due-to-sandbox-issues
*/
$this->browserOptions = [
'args' => ['--no-sandbox', '--disable-setuid-sandbox'],
'headless' => true,
];
if ($this->canPopulateProperty('browser')) {
$this->browser = (new Puppeteer)->launch($this->browserOptions);
}
}
public function canPopulateProperty(string $propertyName): bool
{
return !in_array($propertyName, $this->dontPopulateProperties);
}
public function isLogLevel(): Callback {
$psrLogLevels = (new ReflectionClass(LogLevel::class))->getConstants();
$monologLevels = (new ReflectionClass(Logger::class))->getConstants();
$monologLevels = array_intersect_key($monologLevels, $psrLogLevels);
return $this->callback(function ($level) use ($psrLogLevels, $monologLevels) {
if (is_string($level)) {
return in_array($level, $psrLogLevels, true);
} else if (is_int($level)) {
return in_array($level, $monologLevels, true);
}
return false;
});
}
}
================================================
FILE: tests/UntestableResource.php
================================================
<?php
namespace Nesk\Puphpeteer\Tests;
class UntestableResource
{
//
}
================================================
FILE: tests/resources/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<link rel="stylesheet" href="./stylesheet.css">
</head>
<body>
<h1>Example Page</h1>
<script>
console.log('Starting worker...');
new Worker('worker.js');
console.log('Worker started!');
</script>
</body>
</html>
================================================
FILE: tests/resources/stylesheet.css
================================================
h1 {
text-transform: lowercase;
}
================================================
FILE: tests/resources/worker.js
================================================
// There's nothing to do, just wait.
gitextract_n_rqg4xg/
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bin/
│ └── console
├── composer.json
├── examples/
│ ├── 01_page_open.php
│ ├── 02_page_screenshot.php
│ └── 03_browserless.php
├── package.json
├── phpstan.neon
├── phpunit.xml
├── src/
│ ├── Command/
│ │ └── GenerateDocumentationCommand.php
│ ├── Puppeteer.php
│ ├── PuppeteerConnectionDelegate.js
│ ├── PuppeteerProcessDelegate.php
│ ├── Resources/
│ │ ├── Accessibility.php
│ │ ├── Browser.php
│ │ ├── BrowserContext.php
│ │ ├── BrowserLauncher.php
│ │ ├── BrowserWebSocketTransport.php
│ │ ├── Buffer.php
│ │ ├── CDPSession.php
│ │ ├── ConsoleMessage.php
│ │ ├── Coverage.php
│ │ ├── DevToolsTarget.php
│ │ ├── Dialog.php
│ │ ├── ElementHandle.php
│ │ ├── EventEmitter.php
│ │ ├── FileChooser.php
│ │ ├── Frame.php
│ │ ├── HTTPRequest.php
│ │ ├── HTTPResponse.php
│ │ ├── JSHandle.php
│ │ ├── Keyboard.php
│ │ ├── Mouse.php
│ │ ├── OtherTarget.php
│ │ ├── Page.php
│ │ ├── PageTarget.php
│ │ ├── Realm.php
│ │ ├── ScreenRecorder.php
│ │ ├── SecurityDetails.php
│ │ ├── Target.php
│ │ ├── TaskManager.php
│ │ ├── TimeoutError.php
│ │ ├── TimeoutSettings.php
│ │ ├── Touchscreen.php
│ │ ├── Tracing.php
│ │ ├── Uint8Array.php
│ │ ├── WaitTask.php
│ │ ├── WebWorker.php
│ │ └── WorkerTarget.php
│ ├── Traits/
│ │ ├── AliasesEvaluationMethods.php
│ │ └── AliasesSelectionMethods.php
│ ├── doc-generator.ts
│ └── get-puppeteer-version.js
└── tests/
├── DownloadTest.php
├── PuphpeteerTest.php
├── ResourceInstantiator.php
├── RiskyResource.php
├── TestCase.php
├── UntestableResource.php
└── resources/
├── index.html
├── stylesheet.css
└── worker.js
SYMBOL INDEX (188 symbols across 50 files)
FILE: src/Command/GenerateDocumentationCommand.php
class GenerateDocumentationCommand (line 13) | final class GenerateDocumentationCommand extends Command
method getName (line 31) | public function getName(): ?string
method configure (line 36) | protected function configure(): void
method buildDocumentationGenerator (line 50) | private static function buildDocumentationGenerator(): void
method getDocumentation (line 65) | private static function getDocumentation(string $puppeteerPath, array ...
method getResourceNames (line 107) | private static function getResourceNames(): array
method generatePhpDocWithDocumentation (line 114) | private static function generatePhpDocWithDocumentation(array $classDo...
method writePhpDoc (line 146) | private static function writePhpDoc(string $className, string $phpDoc)...
method execute (line 178) | protected function execute(InputInterface $input, OutputInterface $out...
method rmdirRecursive (line 221) | private static function rmdirRecursive(string $dir): bool
FILE: src/Puppeteer.php
class Puppeteer (line 30) | class Puppeteer extends AbstractEntryPoint
method __construct (line 50) | public function __construct(array $userOptions = [])
method checkPuppeteerVersion (line 64) | private function checkPuppeteerVersion(string $nodePath, LoggerInterfa...
method currentPuppeteerVersion (line 83) | private function currentPuppeteerVersion(string $nodePath): ?string
method acceptedPuppeteerVersion (line 91) | private function acceptedPuppeteerVersion(): string
FILE: src/PuppeteerConnectionDelegate.js
class PuppeteerConnectionDelegate (line 11) | class PuppeteerConnectionDelegate extends ConnectionDelegate {
method constructor (line 17) | constructor(options) {
method handleInstruction (line 28) | async handleInstruction(instruction, responseHandler, errorHandler) {
method isInstanceOf (line 83) | isInstanceOf(value, className) {
method logConsoleMessage (line 102) | async logConsoleMessage(consoleMessage) {
method addSignalEventListeners (line 128) | addSignalEventListeners() {
method closeAllBrowsers (line 144) | closeAllBrowsers() {
FILE: src/PuppeteerProcessDelegate.php
class PuppeteerProcessDelegate (line 8) | class PuppeteerProcessDelegate implements ShouldHandleProcessDelegation
method resourceFromOriginalClassName (line 12) | public function resourceFromOriginalClassName(string $className): ?string
FILE: src/Resources/Accessibility.php
class Accessibility (line 11) | class Accessibility extends BasicResource
FILE: src/Resources/Browser.php
class Browser (line 54) | class Browser extends EventEmitter
FILE: src/Resources/BrowserContext.php
class BrowserContext (line 37) | class BrowserContext extends EventEmitter
FILE: src/Resources/BrowserLauncher.php
class BrowserLauncher (line 17) | class BrowserLauncher
FILE: src/Resources/BrowserWebSocketTransport.php
class BrowserWebSocketTransport (line 13) | class BrowserWebSocketTransport
FILE: src/Resources/Buffer.php
class Buffer (line 11) | class Buffer extends BasicResource
FILE: src/Resources/CDPSession.php
class CDPSession (line 18) | class CDPSession extends EventEmitter
FILE: src/Resources/ConsoleMessage.php
class ConsoleMessage (line 19) | class ConsoleMessage extends BasicResource
FILE: src/Resources/Coverage.php
class Coverage (line 19) | class Coverage extends BasicResource
FILE: src/Resources/DevToolsTarget.php
class DevToolsTarget (line 5) | class DevToolsTarget extends Target
FILE: src/Resources/Dialog.php
class Dialog (line 19) | class Dialog extends BasicResource
FILE: src/Resources/ElementHandle.php
class ElementHandle (line 94) | class ElementHandle extends JSHandle
FILE: src/Resources/EventEmitter.php
class EventEmitter (line 21) | class EventEmitter extends BasicResource
FILE: src/Resources/FileChooser.php
class FileChooser (line 15) | class FileChooser extends BasicResource
FILE: src/Resources/Frame.php
class Frame (line 75) | class Frame extends BasicResource
FILE: src/Resources/HTTPRequest.php
class HTTPRequest (line 57) | class HTTPRequest extends BasicResource
FILE: src/Resources/HTTPResponse.php
class HTTPResponse (line 41) | class HTTPResponse extends BasicResource
FILE: src/Resources/JSHandle.php
class JSHandle (line 31) | class JSHandle extends BasicResource
FILE: src/Resources/Keyboard.php
class Keyboard (line 19) | class Keyboard extends BasicResource
FILE: src/Resources/Mouse.php
class Mouse (line 31) | class Mouse extends BasicResource
FILE: src/Resources/OtherTarget.php
class OtherTarget (line 5) | class OtherTarget extends Target
FILE: src/Resources/Page.php
class Page (line 182) | class Page extends EventEmitter
FILE: src/Resources/PageTarget.php
class PageTarget (line 9) | class PageTarget extends Target
FILE: src/Resources/Realm.php
class Realm (line 24) | class Realm
FILE: src/Resources/ScreenRecorder.php
class ScreenRecorder (line 9) | class ScreenRecorder
FILE: src/Resources/SecurityDetails.php
class SecurityDetails (line 21) | class SecurityDetails extends BasicResource
FILE: src/Resources/Target.php
class Target (line 27) | class Target extends BasicResource
FILE: src/Resources/TaskManager.php
class TaskManager (line 15) | class TaskManager
FILE: src/Resources/TimeoutError.php
class TimeoutError (line 7) | class TimeoutError extends BasicResource
FILE: src/Resources/TimeoutSettings.php
class TimeoutSettings (line 15) | class TimeoutSettings
FILE: src/Resources/Touchscreen.php
class Touchscreen (line 21) | class Touchscreen extends BasicResource
FILE: src/Resources/Tracing.php
class Tracing (line 15) | class Tracing extends BasicResource
FILE: src/Resources/Uint8Array.php
class Uint8Array (line 11) | class Uint8Array extends Buffer
FILE: src/Resources/WaitTask.php
class WaitTask (line 14) | class WaitTask
FILE: src/Resources/WebWorker.php
class WebWorker (line 21) | class WebWorker extends BasicResource
FILE: src/Resources/WorkerTarget.php
class WorkerTarget (line 9) | class WorkerTarget extends Target
FILE: src/Traits/AliasesEvaluationMethods.php
type AliasesEvaluationMethods (line 12) | trait AliasesEvaluationMethods
method querySelectorEval (line 14) | public function querySelectorEval(...$arguments)
method querySelectorAllEval (line 19) | public function querySelectorAllEval(...$arguments)
FILE: src/Traits/AliasesSelectionMethods.php
type AliasesSelectionMethods (line 11) | trait AliasesSelectionMethods
method querySelector (line 13) | public function querySelector(...$arguments)
method querySelectorAll (line 18) | public function querySelectorAll(...$arguments)
FILE: src/doc-generator.ts
type ObjectMemberAsJson (line 6) | type ObjectMemberAsJson = { [key: string]: string; }
type ObjectMembersAsJson (line 8) | type ObjectMembersAsJson = {
type ClassAsJson (line 14) | type ClassAsJson = { name: string } & ObjectMembersAsJson
type MemberContext (line 15) | type MemberContext = 'class'|'literal'
type TypeContext (line 16) | type TypeContext = 'methodReturn'
class TypeNotSupportedError (line 18) | class TypeNotSupportedError extends Error {
method constructor (line 19) | constructor(message?: string) {
type SupportChecker (line 24) | interface SupportChecker {
class JsSupportChecker (line 28) | class JsSupportChecker {
method supportsMethodName (line 29) | supportsMethodName(methodName: string): boolean {
class PhpSupportChecker (line 34) | class PhpSupportChecker {
method supportsMethodName (line 35) | supportsMethodName(methodName: string): boolean {
type DocumentationFormatter (line 40) | interface DocumentationFormatter {
class JsDocumentationFormatter (line 65) | class JsDocumentationFormatter implements DocumentationFormatter {
method formatProperty (line 66) | formatProperty(name: string, type: string, context: MemberContext): st...
method formatGetter (line 70) | formatGetter(name: string, type: string): string {
method formatAnonymousFunction (line 74) | formatAnonymousFunction(parameters: string, returnType: string): string {
method formatFunction (line 78) | formatFunction(name: string, parameters: string, returnType: string): ...
method formatParameter (line 82) | formatParameter(name: string, type: string, isVariadic: boolean, isOpt...
method formatTypeAny (line 86) | formatTypeAny(): string {
method formatTypeUnknown (line 90) | formatTypeUnknown(): string {
method formatTypeVoid (line 94) | formatTypeVoid(): string {
method formatTypeUndefined (line 98) | formatTypeUndefined(): string {
method formatTypeNull (line 102) | formatTypeNull(): string {
method formatTypeBoolean (line 106) | formatTypeBoolean(): string {
method formatTypeNumber (line 110) | formatTypeNumber(): string {
method formatTypeString (line 114) | formatTypeString(): string {
method formatTypeReference (line 118) | formatTypeReference(type: string): string {
method formatGeneric (line 122) | formatGeneric(parentType: string, argumentTypes: string[], context?: T...
method formatQualifiedName (line 126) | formatQualifiedName(left: string, right: string): string {
method formatIndexedAccessType (line 130) | formatIndexedAccessType(object: string, index: string): string {
method formatLiteralType (line 134) | formatLiteralType(value: string): string {
method formatUnion (line 138) | formatUnion(types: string[]): string {
method formatIntersection (line 142) | formatIntersection(types: string[]): string {
method formatObject (line 146) | formatObject(members: string[]): string {
method formatArray (line 150) | formatArray(type: string): string {
class PhpDocumentationFormatter (line 155) | class PhpDocumentationFormatter implements DocumentationFormatter {
method constructor (line 158) | constructor(
method formatProperty (line 163) | formatProperty(name: string, type: string, context: MemberContext): st...
method formatGetter (line 169) | formatGetter(name: string, type: string): string {
method formatAnonymousFunction (line 173) | formatAnonymousFunction(parameters: string, returnType: string): string {
method formatFunction (line 177) | formatFunction(name: string, parameters: string, returnType: string): ...
method formatParameter (line 181) | formatParameter(name: string, type: string, isVariadic: boolean, isOpt...
method formatTypeAny (line 203) | formatTypeAny(): string {
method formatTypeUnknown (line 207) | formatTypeUnknown(): string {
method formatTypeVoid (line 211) | formatTypeVoid(): string {
method formatTypeUndefined (line 215) | formatTypeUndefined(): string {
method formatTypeNull (line 219) | formatTypeNull(): string {
method formatTypeBoolean (line 223) | formatTypeBoolean(): string {
method formatTypeNumber (line 227) | formatTypeNumber(): string {
method formatTypeString (line 231) | formatTypeString(): string {
method formatTypeReference (line 235) | formatTypeReference(type: string): string {
method formatGeneric (line 279) | formatGeneric(parentType: string, argumentTypes: string[], context?: T...
method formatQualifiedName (line 298) | formatQualifiedName(left: string, right: string): string {
method formatIndexedAccessType (line 302) | formatIndexedAccessType(object: string, index: string): string {
method formatLiteralType (line 306) | formatLiteralType(value: string): string {
method prepareUnionOrIntersectionTypes (line 310) | private prepareUnionOrIntersectionTypes(types: string[]): string[] {
method formatUnion (line 319) | formatUnion(types: string[]): string {
method formatIntersection (line 330) | formatIntersection(types: string[]): string {
method formatObject (line 334) | formatObject(members: string[]): string {
method formatArray (line 338) | formatArray(type: string): string {
class PhpStanDocumentationFormatter (line 343) | class PhpStanDocumentationFormatter extends PhpDocumentationFormatter {
method formatAnonymousFunction (line 344) | formatAnonymousFunction(parameters: string, returnType: string): string {
method formatTypeReference (line 348) | formatTypeReference(type: string): string {
method formatObject (line 392) | formatObject(members: string[]): string {
class DocumentationGenerator (line 397) | class DocumentationGenerator {
method constructor (line 398) | constructor(
method hasModifierForNode (line 403) | private hasModifierForNode(
method isNodeAccessible (line 414) | private isNodeAccessible(node: ts.Node): boolean {
method isNodeStatic (line 427) | private isNodeStatic(node: ts.Node): boolean {
method getClassDeclarationAsJson (line 431) | public getClassDeclarationAsJson(node: ts.ClassDeclaration): ClassAsJs...
method getMembersAsJson (line 438) | private getMembersAsJson(members: ts.NodeArray<ts.NamedDeclaration>, c...
method getPropertySignatureOrDeclarationAsString (line 467) | private getPropertySignatureOrDeclarationAsString(
method getGetAccessorDeclarationAsString (line 476) | private getGetAccessorDeclarationAsString(
method getSignatureDeclarationBaseAsString (line 484) | private getSignatureDeclarationBaseAsString(
method getEmptyFunctionSignatureAsString (line 499) | private getEmptyFunctionSignatureAsString(
method getParameterDeclarationAsString (line 505) | private getParameterDeclarationAsString(node: ts.ParameterDeclaration)...
method getTypeNodeAsString (line 519) | private getTypeNodeAsString(node: ts.TypeNode, context?: TypeContext):...
method getTypeReferenceNodeAsString (line 563) | private getTypeReferenceNodeAsString(node: ts.TypeReferenceNode, conte...
method getGenericTypeReferenceNodeAsString (line 567) | private getGenericTypeReferenceNodeAsString(node: ts.TypeReferenceNode...
method getSimpleTypeReferenceNodeAsString (line 577) | private getSimpleTypeReferenceNodeAsString(node: ts.TypeReferenceNode)...
method getQualifiedNameAsString (line 583) | private getQualifiedNameAsString(node: ts.QualifiedName): string {
method getIndexedAccessTypeNodeAsString (line 592) | private getIndexedAccessTypeNodeAsString(
method getLiteralTypeNodeAsString (line 600) | private getLiteralTypeNodeAsString(node: ts.LiteralTypeNode): string {
method getUnionTypeNodeAsString (line 611) | private getUnionTypeNodeAsString(node: ts.UnionTypeNode, context?: Typ...
method getIntersectionTypeNodeAsString (line 616) | private getIntersectionTypeNodeAsString(node: ts.IntersectionTypeNode,...
method getTypeLiteralNodeAsString (line 621) | private getTypeLiteralNodeAsString(node: ts.TypeLiteralNode): string {
method getArrayTypeNodeAsString (line 628) | private getArrayTypeNodeAsString(node: ts.ArrayTypeNode, context?: Typ...
method getNamedDeclarationAsString (line 633) | private getNamedDeclarationAsString(node: ts.NamedDeclaration): string {
method getIdentifierAsString (line 641) | private getIdentifierAsString(node: ts.Identifier|ts.PrivateIdentifier...
FILE: src/get-puppeteer-version.js
function output (line 3) | function output(value) {
FILE: tests/DownloadTest.php
class DownloadTest (line 10) | class DownloadTest extends TestCase
method setUp (line 12) | public function setUp(): void
method testDownloadImage (line 27) | public function testDownloadImage()
method testDownloadPdf (line 46) | public function testDownloadPdf()
FILE: tests/PuphpeteerTest.php
class PuphpeteerTest (line 13) | class PuphpeteerTest extends TestCase
method setUp (line 15) | public function setUp(): void
method can_browse_website (line 27) | public function can_browse_website()
method can_use_method_aliases (line 37) | public function can_use_method_aliases()
method can_evaluate_a_selection (line 73) | public function can_evaluate_a_selection()
method can_intercept_requests (line 90) | public function can_intercept_requests()
method check_all_resources_are_supported (line 112) | public function check_all_resources_are_supported(string $name)
method resourceProvider (line 153) | public static function resourceProvider(): \Generator
method createBrowserLogger (line 162) | private function createBrowserLogger(callable $onBrowserLog): LoggerIn...
method browser_console_calls_are_logged_if_enabled (line 182) | public function browser_console_calls_are_logged_if_enabled()
method browser_console_calls_are_not_logged_if_disabled (line 204) | public function browser_console_calls_are_not_logged_if_disabled()
FILE: tests/ResourceInstantiator.php
class ResourceInstantiator (line 18) | class ResourceInstantiator
method __construct (line 22) | public function __construct(
method getResourceNames (line 104) | public function getResourceNames(): array
method __call (line 109) | public function __call(string $name, array $arguments)
FILE: tests/RiskyResource.php
class RiskyResource (line 7) | class RiskyResource
method __construct (line 12) | public function __construct(callable $resourceRetriever) {
method value (line 20) | public function value() {
method exception (line 24) | public function exception(): ?NodeFatalException {
FILE: tests/TestCase.php
class TestCase (line 14) | class TestCase extends BaseTestCase
method setUp (line 24) | public function setUp(): void
method tearDown (line 40) | public function tearDown(): void
method serveResources (line 56) | protected function serveResources(): void
method launchBrowser (line 70) | protected function launchBrowser(): void
method canPopulateProperty (line 87) | public function canPopulateProperty(string $propertyName): bool
method isLogLevel (line 92) | public function isLogLevel(): Callback {
FILE: tests/UntestableResource.php
class UntestableResource (line 5) | class UntestableResource
Condensed preview — 64 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (134K chars).
[
{
"path": "CHANGELOG.md",
"chars": 3410,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "LICENSE",
"chars": 1055,
"preview": "Copyright (c) Johann Pardanaud\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this sof"
},
{
"path": "README.md",
"chars": 6492,
"preview": "# PuPHPeteer\n\n<img src=\"https://user-images.githubusercontent.com/817508/100672192-dd258500-3361-11eb-845f-e8b5109752e4."
},
{
"path": "bin/console",
"chars": 277,
"preview": "#!/usr/bin/env php\n<?php\n\nrequire_once __DIR__.'/../vendor/autoload.php';\n\nuse Symfony\\Component\\Console\\Application;\nus"
},
{
"path": "composer.json",
"chars": 1261,
"preview": "{\n \"name\": \"zoon/puphpeteer\",\n \"description\": \"A Puppeteer bridge for PHP, supporting the entire API.\",\n \"keywords\": "
},
{
"path": "examples/01_page_open.php",
"chars": 646,
"preview": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Rialto\\Data\\JsFunction;\n\n$p"
},
{
"path": "examples/02_page_screenshot.php",
"chars": 333,
"preview": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\nuse Nesk\\Puphpeteer\\Puppeteer;\n\n$puppeteer = new Puppeteer;\n$browse"
},
{
"path": "examples/03_browserless.php",
"chars": 2604,
"preview": "<?php\n\nrequire __DIR__ . '/../vendor/autoload.php';\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Rialto\\Data\\JsFunction;\nuse"
},
{
"path": "package.json",
"chars": 849,
"preview": "{\n \"name\": \"@zoon/puphpeteer\",\n \"version\": \"2.4.3\",\n \"description\": \"A Puppeteer bridge for PHP, supporting the entir"
},
{
"path": "phpstan.neon",
"chars": 59,
"preview": "parameters:\n level: 4\n fileExtensions:\n - php\n"
},
{
"path": "phpunit.xml",
"chars": 356,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xsi:noNam"
},
{
"path": "src/Command/GenerateDocumentationCommand.php",
"chars": 8117,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Command;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Symfony\\Component\\Console\\Command\\Command;"
},
{
"path": "src/Puppeteer.php",
"chars": 3362,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer;\n\nuse Composer\\Semver\\Semver;\nuse Exception;\nuse Nesk\\Rialto\\AbstractEntryPoint;\nuse Ps"
},
{
"path": "src/PuppeteerConnectionDelegate.js",
"chars": 3912,
"preview": "\"use strict\";\n\nconst { ConnectionDelegate } = require(\"@zoon/rialto\"),\n Logger = require(\"@zoon/rialto/src/node-process"
},
{
"path": "src/PuppeteerProcessDelegate.php",
"chars": 702,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer;\n\nuse Nesk\\Rialto\\Interfaces\\ShouldHandleProcessDelegation;\nuse Nesk\\Rialto\\Traits\\Uses"
},
{
"path": "src/Resources/Accessibility.php",
"chars": 266,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed|null snapshot(arr"
},
{
"path": "src/Resources/Browser.php",
"chars": 2689,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read bool $connected\n * @property-read mixed $protocol\n * "
},
{
"path": "src/Resources/BrowserContext.php",
"chars": 1869,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read bool $closed\n * @property-read string|null $id\n * @me"
},
{
"path": "src/Resources/BrowserLauncher.php",
"chars": 814,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property mixed $puppeteer\n * @property-read mixed $browser\n * @meth"
},
{
"path": "src/Resources/BrowserWebSocketTransport.php",
"chars": 335,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property \\Nesk\\Rialto\\Data\\JsFunction $onmessage\n * @property \\Nesk"
},
{
"path": "src/Resources/Buffer.php",
"chars": 224,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n\n/**\n * @method \\string toString(?stri"
},
{
"path": "src/Resources/CDPSession.php",
"chars": 656,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read bool $detached\n * @method mixed|null connection()\n * "
},
{
"path": "src/Resources/ConsoleMessage.php",
"chars": 512,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed type()\n * @method"
},
{
"path": "src/Resources/Coverage.php",
"chars": 722,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void updateClient(\\Nesk"
},
{
"path": "src/Resources/DevToolsTarget.php",
"chars": 85,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nclass DevToolsTarget extends Target\n{\n}\n"
},
{
"path": "src/Resources/Dialog.php",
"chars": 496,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed type()\n * @method"
},
{
"path": "src/Resources/ElementHandle.php",
"chars": 6644,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Puphpeteer\\Traits\\AliasesEvaluationMethods;\nuse Nesk\\Puphpeteer\\Tr"
},
{
"path": "src/Resources/EventEmitter.php",
"chars": 799,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed on(mixed $type, m"
},
{
"path": "src/Resources/FileChooser.php",
"chars": 349,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method bool isMultiple()\n * @m"
},
{
"path": "src/Resources/Frame.php",
"chars": 4601,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Puphpeteer\\Traits\\AliasesEvaluationMethods;\nuse Nesk\\Puphpeteer\\Tr"
},
{
"path": "src/Resources/HTTPRequest.php",
"chars": 2641,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property-read string $id\n * @p"
},
{
"path": "src/Resources/HTTPResponse.php",
"chars": 1541,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method mixed remoteAddress()\n "
},
{
"path": "src/Resources/JSHandle.php",
"chars": 1483,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property \\Nesk\\Rialto\\Data\\JsF"
},
{
"path": "src/Resources/Keyboard.php",
"chars": 668,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void down(mixed $key, m"
},
{
"path": "src/Resources/Mouse.php",
"chars": 1384,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void reset()\n * @method"
},
{
"path": "src/Resources/OtherTarget.php",
"chars": 82,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nclass OtherTarget extends Target\n{\n}\n"
},
{
"path": "src/Resources/Page.php",
"chars": 12609,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Puphpeteer\\Traits\\AliasesEvaluationMethods;\nuse Nesk\\Puphpeteer\\Tr"
},
{
"path": "src/Resources/PageTarget.php",
"chars": 208,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method \\Nesk\\Puphpeteer\\Resources\\Page|null page()\n * @method-exten"
},
{
"path": "src/Resources/Realm.php",
"chars": 1442,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property \\Nesk\\Puphpeteer\\Resources\\TaskManager $taskManager\n * @pr"
},
{
"path": "src/Resources/ScreenRecorder.php",
"chars": 133,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method void stop()\n * @method-extended void stop()\n */\nclass Screen"
},
{
"path": "src/Resources/SecurityDetails.php",
"chars": 572,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method string issuer()\n * @met"
},
{
"path": "src/Resources/Target.php",
"chars": 1150,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method \\Nesk\\Puphpeteer\\Resour"
},
{
"path": "src/Resources/TaskManager.php",
"chars": 555,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method void add(\\Nesk\\Puphpeteer\\Resources\\WaitTask|mixed[] $task)\n"
},
{
"path": "src/Resources/TimeoutError.php",
"chars": 127,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\nclass TimeoutError extends BasicResour"
},
{
"path": "src/Resources/TimeoutSettings.php",
"chars": 463,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method void setDefaultTimeout(float $timeout)\n * @method-extended v"
},
{
"path": "src/Resources/Touchscreen.php",
"chars": 651,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property mixed $idGenerator\n *"
},
{
"path": "src/Resources/Tracing.php",
"chars": 534,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @method void updateClient(\\Nesk"
},
{
"path": "src/Resources/Uint8Array.php",
"chars": 211,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n\n/**\n * Uint8Array automatically conve"
},
{
"path": "src/Resources/WaitTask.php",
"chars": 379,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @property-read Promise|mixed[] $result\n * @method void rerun()\n * @m"
},
{
"path": "src/Resources/WebWorker.php",
"chars": 782,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\nuse Nesk\\Rialto\\Data\\BasicResource;\n\n/**\n * @property \\Nesk\\Puphpeteer\\Reso"
},
{
"path": "src/Resources/WorkerTarget.php",
"chars": 162,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Resources;\n\n/**\n * @method mixed|null worker()\n * @method-extended mixed|null worker()\n"
},
{
"path": "src/Traits/AliasesEvaluationMethods.php",
"chars": 715,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Traits;\n\nuse Nesk\\Puphpeteer\\Resources\\JSHandle;\nuse Nesk\\Rialto\\Data\\JsFunction;\n\n/**\n"
},
{
"path": "src/Traits/AliasesSelectionMethods.php",
"chars": 478,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Traits;\n\nuse Nesk\\Puphpeteer\\Resources\\ElementHandle;\n\n/**\n * @method null|ElementHandl"
},
{
"path": "src/doc-generator.ts",
"chars": 23505,
"preview": "import * as ts from 'typescript';\nconst yargs = require('yargs/yargs');\nconst { hideBin } = require('yargs/helpers');\nco"
},
{
"path": "src/get-puppeteer-version.js",
"chars": 226,
"preview": "'use strict';\n\nfunction output(value) {\n process.stdout.write(JSON.stringify(value));\n}\n\ntry {\n const manifest = r"
},
{
"path": "tests/DownloadTest.php",
"chars": 2404,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Rialto\\Data\\JsFunction;\nuse PHPUnit\\Fra"
},
{
"path": "tests/PuphpeteerTest.php",
"chars": 7090,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Puphpeteer\\Resources\\Frame;\nuse Nesk\\Pu"
},
{
"path": "tests/ResourceInstantiator.php",
"chars": 4416,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Nesk\\Puphpeteer\\Resources\\Browser;\nuse Nesk\\"
},
{
"path": "tests/RiskyResource.php",
"chars": 595,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Rialto\\Exceptions\\Node\\FatalException as NodeFatalException;\n\nclass Ri"
},
{
"path": "tests/TestCase.php",
"chars": 3288,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nuse Nesk\\Puphpeteer\\Puppeteer;\nuse Monolog\\Logger;\nuse Nesk\\Puphpeteer\\Resource"
},
{
"path": "tests/UntestableResource.php",
"chars": 77,
"preview": "<?php\n\nnamespace Nesk\\Puphpeteer\\Tests;\n\nclass UntestableResource\n{\n //\n}\n"
},
{
"path": "tests/resources/index.html",
"chars": 361,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>Document</title>\n\n <link rel=\"stylesheet\" href=\"./stylesheet.css"
},
{
"path": "tests/resources/stylesheet.css",
"chars": 38,
"preview": "h1 {\n text-transform: lowercase;\n}\n"
},
{
"path": "tests/resources/worker.js",
"chars": 37,
"preview": "// There's nothing to do, just wait.\n"
}
]
About this extraction
This page contains the full source code of the zoonru/puphpeteer GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 64 files (122.6 KB), approximately 32.7k tokens, and a symbol index with 188 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.