Instrument headless chrome/chromium instances from PHP

Overview

Chrome PHP

Latest Stable Version License

This library lets you start playing with chrome/chromium in headless mode from PHP.

Can be used synchronously and asynchronously!

Features

  • Open chrome or chromium browser from php
  • Create pages and navigate to pages
  • Take screenshots
  • Evaluate javascript in the page
  • Make PDF
  • Emulate mouse
  • Emulate keyboard
  • Always IDE friendly

Happy browsing!

Requirements

Requires PHP 7.3-8.0 and a chrome/chromium 65+ executable.

Note that the library is only tested on Linux but is compatible with MacOS and Windows.

Installation

The library can be installed with Composer and is available on packagist under chrome-php/chrome:

$ composer require chrome-php/chrome

Usage

It uses a simple and understandable API to start chrome, to open pages, to take screenshots, to crawl websites... and almost everything that you can do with chrome as a human.

use HeadlessChromium\BrowserFactory;

$browserFactory = new BrowserFactory();

// starts headless chrome
$browser = $browserFactory->createBrowser();

try {
    // creates a new page and navigate to an url
    $page = $browser->createPage();
    $page->navigate('http://example.com')->waitForNavigation();

    // get page title
    $pageTitle = $page->evaluate('document.title')->getReturnValue();

    // screenshot - Say "Cheese"! 😄
    $page->screenshot()->saveToFile('/foo/bar.png');

    // pdf
    $page->pdf(['printBackground' => false])->saveToFile('/foo/bar.pdf');
} finally {
    // bye
    $browser->close();
}

Using different chrome executable

When starting, the factory will look for the environment variable "CHROME_PATH" to use as the chrome executable. If the variable is not found, it will try to guess the correct executable path according to your OS or use "chrome" as the default.

You also explicitly set any executable of your choice when creating a new object. For instance "chromium-browser":

use HeadlessChromium\BrowserFactory;

// replace default 'chrome' with 'chromium-browser'
$browserFactory = new BrowserFactory('chromium-browser');

Debugging

The following example disables headless mode to ease debugging

use HeadlessChromium\BrowserFactory;

$browserFactory = new BrowserFactory();

$browser = $browserFactory->createBrowser([
    'headless'        => false,          // disable headless mode
]);

Other debug options:

[
    'connectionDelay' => 0.8,            // add 0.8 second of delay between each instruction sent to chrome,
    'debugLogger'     => 'php://stdout', // will enable verbose mode
]

About debugLogger: this can be any of a resource string, a resource or an object implementing LoggerInterface from Psr\Log (such as monolog or apix/log).

API

Browser Factory

use HeadlessChromium\BrowserFactory;

$browserFactory = new BrowserFactory();
$browser = $browserFactory->createBrowser([
    'windowSize'      => [1920, 1000],
    'enableImages'    => false,
]);

Options

Here are the options available for the browser factory:

Option name Default Description
connectionDelay 0 Delay to apply between each operation for debugging purposes
customFlags none Array of flags to pass to the command line. Eg: ['--option1', '--option2=someValue']
debugLogger null A string (e.g "php://stdout"), or resource, or PSR-3 logger instance to print debug messages
enableImages true Toggles loading of images
envVariables none Array of environment variables to pass to the process (example DISPLAY variable)
headless true Enable or disable headless mode
ignoreCertificateErrors false Set chrome to ignore ssl errors
keepAlive false Set to true to keep alive the chrome instance when the script terminates
noSandbox false Useful to run in a docker container
proxyServer none Proxy server to use. usage: 127.0.0.1:8080 (authorisation with credentials does not work)
sendSyncDefaultTimeout 5000 Default timeout (ms) for sending sync messages
startupTimeout 30 Maximum time in seconds to wait for chrome to start
userAgent none User agent to use for the whole browser (see page api for alternative)
userDataDir none Chrome user data dir (default: a new empty dir is generated temporarily)
windowSize none Size of the window. usage: $width, $height - see also Page::setViewport

Browser API

Create a new page (tab)

$page = $browser->createPage();

// destination can be specified
$uri = 'http://example.com';
$page = $browser->createPage($uri);

Close the browser

    $browser->close();

Set a script to evaluate before every page created by this browser will navigate

$browser->setPagePreScript('// Simulate navigator permissions;
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
    parameters.name === 'notifications' ?
        Promise.resolve({ state: Notification.permission }) :
        originalQuery(parameters)
);');

Page API

Navigate to an url

// navigate
$navigation = $page->navigate('http://example.com');

// wait for the page to be loaded
$navigation->waitForNavigation();

When Using $navigation->waitForNavigation() you will wait for 30sec until the page event "loaded" is triggered. You can change the timeout or the event to listen for:

// wait 10secs for the event "DOMContentLoaded" to be triggered
$navigation->waitForNavigation(Page::DOM_CONTENT_LOADED, 10000);

Available events (in the order they trigger):

  • Page::DOM_CONTENT_LOADED: dom has completely loaded
  • Page::LOAD: (default) page and all resources are loaded
  • Page::NETWORK_IDLE: page has loaded, and no network activity has occurred for at least 500ms

When you want to wait for the page to navigate there are 2 main issues that may occur. First the page is too long to load and second the page you were waiting to be loaded has been replaced. The good news is that you can handle those issues using a good old try catch:

use HeadlessChromium\Exception\OperationTimedOut;
use HeadlessChromium\Exception\NavigationExpired;

try {
    $navigation->waitForNavigation()
} catch (OperationTimedOut $e) {
    // too long to load
} catch (NavigationExpired $e) {
    // An other page was loaded
}

Evaluate script on the page

Once the page has completed the navigation you can evaluate arbitrary script on this page:

// navigate
$navigation = $page->navigate('http://example.com');

// wait for the page to be loaded
$navigation->waitForNavigation();

// evaluate script in the browser
$evaluation = $page->evaluate('document.documentElement.innerHTML');

// wait for the value to return and get it
$value = $evaluation->getReturnValue();

Sometime the script you evaluate will click a link or submit a form, in this case the page will reload and you will want to wait for the new page to reload.

You can achieve this by using $page->evaluate('some js that will reload the page')->waitForPageReload(). An example is available in form-submit.php

Call a function

This is an alternative to evaluate that allows to call a given function with the given arguments in the page context:

$evaluation = $page->callFunction(
    "function(a, b) {\n    window.foo = a + b;\n}",
    [1, 2]
);

$value = $evaluation->getReturnValue();

Add a script tag

That's useful if you want to add jQuery (or anything else) to the page:

$page->addScriptTag([
    'content' => file_get_contents('path/to/jquery.js')
])->waitForResponse();

$page->evaluate('$(".my.element").html()');

You can also use an url to feed the src attribute:

$page->addScriptTag([
    'url' => 'https://code.jquery.com/jquery-3.3.1.min.js'
])->waitForResponse();

$page->evaluate('$(".my.element").html()');

Get the page HTML

You can get the page HTML as a string using the getHtml method.

$html = $page->getHtml();

Add a script to evaluate upon page navigation

$page->addPreScript('// Simulate navigator permissions;
const originalQuery = window.navigator.permissions.query;
window.navigator.permissions.query = (parameters) => (
    parameters.name === 'notifications' ?
        Promise.resolve({ state: Notification.permission }) :
        originalQuery(parameters)
);');

If your script needs the dom to be fully populated before it runs then you can use the option "onLoad":

$page->addPreScript($script, ['onLoad' => true]);

Set viewport size

This features allows to change the size of the viewport (emulation) for the current page without affecting the size of all the browser's pages (see also option "windowSize" of BrowserFactory::createBrowser).

$width = 600;
$height = 300;
$page->setViewport($width, $height)
    ->await(); // wait for operation to complete

Make a screenshot

// navigate
$navigation = $page->navigate('http://example.com');

// wait for the page to be loaded
$navigation->waitForNavigation();

// take a screenshot
$screenshot = $page->screenshot([
    'format'  => 'jpeg',  // default to 'png' - possible values: 'png', 'jpeg',
    'quality' => 80,      // only if format is 'jpeg' - default 100
]);

// save the screenshot
$screenshot->saveToFile('/some/place/file.jpg');

choose an area

You can use the option "clip" in order to choose an area for the screenshot (TODO exemple)

take a full page screenshot

You can also take a screenshot for the full layout (not only the layout) using $page->getFullPageClip (TODO exemple)

TODO Page.getFullPageClip();

use HeadlessChromium\Clip;

// navigate
$navigation = $page->navigate('http://example.com');

// wait for the page to be loaded
$navigation->waitForNavigation();

// create a rectangle by specifying to left corner coordinates + width and height
$x = 10;
$y = 10;
$width = 100;
$height = 100;
$clip = new Clip($x, $y, $width, $height);

// take the screenshot (in memory binaries)
$screenshot = $page->screenshot([
    'clip'  => $clip,
]);

// save the screenshot
$screenshot->saveToFile('/some/place/file.jpg');

Print as PDF

// navigate
$navigation = $page->navigate('http://example.com');

// wait for the page to be loaded
$navigation->waitForNavigation();

$options = [
    'landscape'           => true,             // default to false
    'printBackground'     => true,             // default to false
    'displayHeaderFooter' => true,             // default to false
    'preferCSSPageSize'   => true,             // default to false ( reads parameters directly from @page )
    'marginTop'           => 0.0,              // defaults to ~0.4 (must be float, value in inches)
    'marginBottom'        => 1.4,              // defaults to ~0.4 (must be float, value in inches)
    'marginLeft'          => 5.0,              // defaults to ~0.4 (must be float, value in inches)
    'marginRight'         => 1.0,              // defaults to ~0.4 (must be float, value in inches)
    'paperWidth'          => 6.0,              // defaults to 8.5 (must be float, value in inches)
    'paperHeight'         => 6.0,              // defaults to 8.5 (must be float, value in inches)
    'headerTemplate'      => '<div>foo</div>', // see details above
    'footerTemplate'      => '<div>foo</div>', // see details above
    'scale'               => 1.2,              // defaults to 1.0 (must be float)
];

// print as pdf (in memory binaries)
$pdf = $page->pdf($options);

// save the pdf
$pdf->saveToFile('/some/place/file.pdf');

// or directly output pdf without saving
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename=filename.pdf');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');

echo base64_decode($pdf->getBase64());

Options headerTempalte and footerTempalte:

Should be valid HTML markup with following classes used to inject printing values into them:

  • date: formatted print date
  • title: document title
  • url: document location
  • pageNumber: current page number
  • totalPages: total pages in the document

Save downloads

You can set the path to save downloaded files.

// After creating a page.
$page->setDownloadPath('/path/to/save/downloaded/files');

Mouse API

The mouse API is dependent on the page instance and allows you to control the mouse's moves, clicks and scroll.

$page->mouse()
    ->move(10, 20)                             // Moves mouse to position x=10;y=20
    ->click()                                  // left click on position set above
    ->move(100, 200, ['steps' => 5])           // move mouse to x=100;y=200 in 5 equal steps
    ->click(['button' => Mouse::BUTTON_RIGHT]; // right click on position set above

// given the last click was on a link, the next step will wait
// for the page to load after the link was clicked
$page->waitForReload();

You can emulate the mouse wheel to scroll up and down in a page, frame or element.

$page->mouse()
    ->scrollDown(100) // scroll down 100px
    ->scrollUp(50);   // scroll up 50px

Finding elements

The find method will search for elements using querySelector and move the cursor to a random position over it.

try {
    $page->mouse()->find('#a')->click(); // find and click on element with id "a"

    $page->mouse()->find('.a', 10); // find the 10th or last element with class "a"
} catch (ElementNotFoundException $exception) {
    // element not found
}

This method will attempt scroll right and down to bring the element to the visible screen. If the element is inside an internal scrollable section, try moving the mouse to inside that section first.

Keyboard API

The keyboard API is dependent on the page instance and allows you to type like a real user.

$page->keyboard()
    ->typeRawKey('Tab') // type a raw key, such as Tab
    ->typeText('bar');  // type the text "bar"

To impersonate a real user you may want to add a delay between each keystroke using the setKeyInterval method:

$page->keyboard()->setKeyInterval(10); // sets a delay of 10 miliseconds between keystrokes

Key combinations

The methods press, type and release can be used to send key combinations such as ctrl + v.

// ctrl + a to select all text
$page->keyboard()
    ->press(' control ') // key names are case insensitive and trimmed
        ->type('a') // press and release
    ->release('Control');

// ctrl + c to copy and ctrl + v to paste it twice
$page->keyboard()
    ->press('Ctrl') // alias for Control
        ->type('c')
        ->type('V') // upper and lower case should behave the same way
    ->release(); // release all

You can press the same key several times in sequence, this is equivalent of a user pressing and holding the key. The release event, however, will be sent only once per key.

Key aliases

Key Aliases
Control Control, Ctrl, Ctr
Alt Alt, AltGr, Alt Gr
Meta Meta, Command, Cmd
Shift Shift

Cookie API

You can set and get cookies for a page:

Set Cookie

use HeadlessChromium\Cookies\Cookie;

$page = $browser->createPage();

// example 1: set cookies for a given domain

$page->setCookies([
    Cookie::create('name', 'value', [
        'domain' => 'example.com',
        'expires' => time() + 3600 // expires in 1 day
    ])
])->await();


// example 2: set cookies for the current page

$page->navigate('http://example.com')->waitForNavigation();

$page->setCookies([
    Cookie::create('name', 'value', ['expires'])
])->await();

Get Cookies

use HeadlessChromium\Cookies\Cookie;

$page = $browser->createPage();

// example 1: get all cookies for the browser

$cookies = $page->getAllCookies();

// example 2: get cookies for the current page

$page->navigate('http://example.com')->waitForNavigation();
$cookies = $page->getCookies();

// filter cookies with name == 'foo'
$cookiesFoo = $cookies->filterBy('name', 'foo');

// find first cookie with name == 'bar'
$cookieBar = $cookies->findOneBy('name', 'bar');
if ($cookieBar) {
    // do something
}

Set user agent

You can set an user agent per page:

$page->setUserAgent('my user agent');

See also BrowserFactory option userAgent to set it for the whole browser.

Advanced usage

The library ships with tools that hide all the communication logic but you can use the tools used internally to communicate directly with chrome debug protocol.

Example:

use HeadlessChromium\Communication\Connection;
use HeadlessChromium\Communication\Message;

// chrome devtools uri
$webSocketUri = 'ws://127.0.0.1:9222/devtools/browser/xxx';

// create a connection
$connection = new Connection($webSocketUri);
$connection->connect();

// send method "Target.activateTarget"
$responseReader = $connection->sendMessage(new Message('Target.activateTarget', ['targetId' => 'xxx']));

// wait up to 1000ms for a response
$response = $responseReader->waitForResponse(1000);

Create a session and send message to the target

// given a target id
$targetId = 'yyy';

// create a session for this target (attachToTarget)
$session = $connection->createSession($targetId);

// send message to this target (Target.sendMessageToTarget)
$response = $session->sendMessageSync(new Message('Page.reload'));

Debugging

You can ease the debugging by setting a delay before each operation is made:

  $connection->setConnectionDelay(500); // wait for 500 ms between each operation to ease debugging

Browser (standalone)

use HeadlessChromium\Communication\Connection;
use HeadlessChromium\Browser;

// chrome devtools uri
$webSocketUri = 'ws://127.0.0.1:9222/devtools/browser/xxx';

// create connection given a web socket uri
$connection = new Connection($webSocketUri);
$connection->connect();

// create browser
$browser = new Browser($connection);

Interacting with DOM

Find one element on page by css selector:

$page = $browser->createPage();
$page->navigate('http://example.com')->waitForNavigation();

$elem = $page->dom()->querySelector('#index_email');

Find all elements in another element by css selector:

$elem = $page->dom()->querySelector('#index_email');
$elem->querySelectorAll('a.link');

Find all elements on page by xpath selector:

$page = $browser->createPage();
$page->navigate('http://example.com')->waitForNavigation();

$elem = $page->dom()->search('//div/*/a');

You can send text to element or click on it:

$elem->click();
$elem->sendKeys('Sample text');

You can upload file to file from input:

$elem->uploadFile('/path/to/file');

You can get element text or attribute:

$text = $elem->getText();
$attr = $elem->getAttribute('class');

Contributing

See CONTRIBUTING.md for contribution details.

License

This project is licensed under the The MIT License (MIT).

Comments
  • Noobie question how to install this?

    Noobie question how to install this?

    Hi guys,

    So I have a apache with php 7 installed on a Ubuntu VPS. I installed the library to the html directory of apache and created a php page. On the page I have: require 'vendor/autoload.php'; After that the example code of the installation page. I get the error:

    Fatal error: Uncaught RuntimeException: Chrome process stopped before startup completed in /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php:326 Stack trace: #0 /var/www/html/vendor/chrome-php/chrome/src/Utils.php(51): HeadlessChromium\Browser\BrowserProcess->HeadlessChromium\Browser{closure}(Object(Symfony\Component\Process\Process)) #1 /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php(361): HeadlessChromium\Utils::tryWithTimeout(30000000, Object(Generator)) #2 /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php(124): HeadlessChromium\Browser\BrowserProcess->waitForStartup(Object(Symfony\Component\Process\Process), 30000000) #3 /var/www/html/vendor/chrome-php/chrome/src/BrowserFactory.php(59): HeadlessChromium\Browser\BrowserProcess->start('chrome', Array) #4 /var/www/html/index.php(16): HeadlessChromium\BrowserFactory->createBrowser() #5 {main} thrown in /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php on line 326

    Any idea what to do?

    help wanted 
    opened by woodsbox 43
  • chrome: command not found

    chrome: command not found

    Hi guys,

    Got chrome installed on my local env which is OSX running php 7.2. I've got chrome installed with an aliase to the following

    alias chrome="/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome" chrome version is "Google Chrome 66.0.3359.117 "

    but i Keep getting this stack trace

    [2018-04-25 15:59:14] local.ERROR: Cannot get chrome version, make sure you provided the correct chrome binaries using (chrome). sh: chrome: command not found {"exception":"[object] (RuntimeException(code: 0): Cannot get chrome version, make sure you provided the correct chrome binaries using (chrome). sh: chrome: command not found at /Users/dansmacbook/projects/project-scrapper/vendor/chrome-php/chrome/src/BrowserFactory.php:78)

    code execution is this

      $browser = $browserFactory->createBrowser([
                    'headless'        => true,         
                    'connectionDelay' => 0.8,           
                    'debugLogger'     => 'php://stdout'
                ]);
            $page = $browser->createPage();
            $response = $page->navigate($url)->waitForNavigation();
            $browser->close();
            dd($response);
    

    any thoughts on how to resolve this?

    enhancement help wanted 
    opened by agentmarine 20
  • Why --hide-scrollbars ?

    Why --hide-scrollbars ?

    This settings render headless detectable as every browser have scrollbars, and this settings is causing the render width to be 100% of the screenwidth

    and we can't remove this parameter unless we modify the code from this library

    stale 
    opened by momala454 19
  • Cloudflare and Co bypass

    Cloudflare and Co bypass

    Hello,

    Sorry, not found questions... So is chrome-php bypass cloudflare? For example will it pass https://nowsecure.nl/ cloud please anyone try it? Or will it pass Google Recaptcha v3 challenge?

    Thanks a lot.

    opened by RestOp 17
  • Killed: 9

    Killed: 9

    Running a scan across the full site eventually fires off Killed: 9

    tried implementing a logger interface from Log::class, but getting nothing in the log, is the implementation for this correct? or is it implemented differently?

    (using the built in Log class from laravel which impliments the PSR\LoggerInterface)

    $browser = $browserFactory->createBrowser([
        'headless'        => true,         // disable headless 
        'debugLogger'     => Log::class // will enable verbose mode
    ]);
    
    bug 
    opened by agentmarine 17
  • OperationTimedOut in waitForStartup

    OperationTimedOut in waitForStartup

    I've been trying to utilise this, I've defined "chromium-browser" as the headless browser in the factory, like so;

           $browserFactory = new BrowserFactory('chromium-browser');
    
            // starts headless chrome
            $browser = $browserFactory->createBrowser();
    
            try {
                // creates a new page and navigate to an url
                $page = $browser->createPage($payload->uri);
                $page->navigate($payload->uri);
            } catch(NoResponseAvailable $exception) {
                dd($exception);
            } 
    
            $browser->close();
    

    However, I'm getting OperationTimedOut in waitForStartup, it seems like the regex does match and does get back a DevTools ws URL. A process does get started (and not removed interestingly) - I'm unsure how to debug this further. Any advice would be greatly appreciated.

    Thanks!

    bug 
    opened by andrew-s 16
  • [Feature Request] set page HTML

    [Feature Request] set page HTML

    I'm using this package to generate PDF from Laravel Blade templates. I can easily render the templates to HTML and then I want to generate a PDF. It would be nice if there was an easy built-in way to load HTML and not have to navigate to a URL.

    I have found 2 possible ways to do this. The first one is:

    $page->navigate('data:text/html,'.rawurlencode($html))->waitForNavigation();
    

    The second method mostly works but for some reason the image did not load but that might be a separate issue:

    $page->getSession()->sendMessageSync(new Message('Page.setDocumentContent', [
        'frameId' => $page->getFrameManager()->getMainFrame()->getFrameId(),
        'html' => $html,
    ]));
    

    Maybe add a loadHtml() or setHtml() method which would be much more discoverable. It was not obvious to me how to load HTML until I figured out these tricks.

    opened by ejunker 15
  • cannot touch '/.local/share/applications/mimeapps.list': Permission denied after volume change

    cannot touch '/.local/share/applications/mimeapps.list': Permission denied after volume change

    Our application is hosted on AWS, after expanding the storage volume used for the instance we receive the following error from the library

    [2022-08-02 13:43:07] DEBUG process: initializing [2022-08-02 13:43:07] DEBUG process: using directory: /tmp/chromium-php-alFho7 
    [2022-08-02 13:43:07] DEBUG process: starting process: '/usr/bin/google-chrome' '--remote-debugging-port=0' '--disable-background-networking' '--disable-background-timer-throttling' '--disable-client-side-phishing-detection' '--disable-default-apps' '--disable-extensions' '--disable-hang-monitor' '--disable-popup-blocking' '--disable-prompt-on-repost' '--disable-sync' '--disable-translate' '--metrics-recording-only' '--no-first-run' '--safebrowsing-disable-auto-update' '--enable-automation' '--password-store=basic' '--use-mock-keychain' '--headless' '--disable-gpu' '--font-render-hinting=none' '--hide-scrollbars' '--mute-audio' '--user-data-dir=/tmp/chromium-php-alFho7' 
    [2022-08-02 13:43:07] DEBUG process: waiting for 30 seconds for startup 
    [2022-08-02 13:43:07] DEBUG process: chrome output:touch: cannot touch '/.local/share/applications/mimeapps.list': Permission denied 
    [2022-08-02 13:43:07] DEBUG process: ignoring output:touch: cannot touch '/.local/share/applications/mimeapps.list': Permission denied 
    [2022-08-02 13:43:07] DEBUG process: chrome output:Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted [2022-08-02 13:43:07] DEBUG process: ignoring output:Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted 
    [2022-08-02 13:43:07] DEBUG process: ✗ chrome process stopped
    
    Fatal error: Uncaught RuntimeException: Chrome process stopped before startup completed. Additional info: touch: cannot touch '/.local/share/applications/mimeapps.list': Permission denied Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted [0802/134307.281828:ERROR:scoped_ptrace_attach.cc(27)] ptrace: Permission denied (13) in /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php:400 Stack trace: #0 /var/www/html/vendor/chrome-php/chrome/src/Utils.php(57): HeadlessChromium\Browser\BrowserProcess->HeadlessChromium\Browser\{closure}(Object(Symfony\Component\Process\Process)) #1 /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php(437): HeadlessChromium\Utils::tryWithTimeout(30000000, Object(Generator)) #2 /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php(139): HeadlessChromium\Browser\BrowserProcess->waitForStartup(Object(Symfony\Component\Process\Process), 30000000) #3 /var/www/html/vendor/ch in /var/www/html/vendor/chrome-php/chrome/src/Browser/BrowserProcess.php on line 400
    
    [2022-08-02 13:43:07] DEBUG process: killing chrome [2022-08-02 13:43:07] DEBUG process: cleaning temporary resources:/tmp/chromium-php-alFho7
    

    Code used

    require_once('../vendor/autoload.php');
    use HeadlessChromium\BrowserFactory;
    use HeadlessChromium\Page;
    
    ini_set('memory_limit', '1024M');
    set_time_limit(1800); //seconds
    ini_set('display_errors', 1);
    ini_set('display_startup_errors', 1);
    error_reporting(E_ALL);
    $browserFactory = new BrowserFactory('/usr/bin/google-chrome');
    // starts headless chrome
    $browser = $browserFactory->createBrowser([
        'debugLogger'     => 'php://output',
        'headless' => true]);
    die("test");
    ?>
    

    Code worked before the move. ENV settings

    printenv CHROME_PATH
    /usr/bin/google-chrome
    

    reinstalling google-chrome and chrome-php does not resolve the issue.

    opened by AndrewGosling 14
  • Added interacting with dom

    Added interacting with dom

    This pull request allows interact with page DOM, find elements by css selector or xpath, scroll to elements, click on it, get elements text and attributes, upload files.

    opened by Seriyyy95 14
  • ) Fatal error: Uncaught RuntimeException: Chrome process stopped before startup completed.

    ) Fatal error: Uncaught RuntimeException: Chrome process stopped before startup completed.

    I got this bug when trying to run a basic example code.

    image

    I'm using my macbook pro.

    Code `<?php

    require_once "vendor/autoload.php";

    use HeadlessChromium\BrowserFactory;

    $browserFactory = new BrowserFactory();

    $browser = $browserFactory->createBrowser();

    // creates a new page and navigate to an url $page = $browser->createPage(); $page->navigate('https://openload.co/embed/OzEFf-v-Axs/SampleVideo_1280x720_20mb.mp4')->waitForNavigation();

    $page->mouse()->move(100,100)->click();

    $page->mouse()->move(100,100)->click();

    $src = $page->evaluate("document.getElementsByTagName("video")[0].src")->getReturnValue(4);

    $headers=get_headers($src); var_dump($headers); var_dump($headers[7]);

    $browser->close();`

    opened by menjilx 14
  • Cannot make a PDF. Reason : -32000 - Printing failed

    Cannot make a PDF. Reason : -32000 - Printing failed

    Hi,

    I am printing a large (!) table as PDF and it fails after about 30s with the error Cannot make a PDF. Reason : -32000 - Printing failed.

    The table has around 90.000 cells in about 8.000 rows. When I print the same table with only 1.000 rows, it works perfectly.

    I wonder, if there is some timeout I might have missed because it looks like chrome is stopping after 30s.

    My chrome version is 104.0.5112.101 (stable)

    Thank you in advance for your help. Stephan

    PS. This is my code:

    $browser = $browserFactory->createBrowser();
    $page = $browser->createPage();
    $page->setHtml($HTML, 30000); //30s timeout for loading the HTML. Increasing that does not help. 
    $options = [
    		'landscape'           => false,
    		'printBackground'     => true,
    		'displayHeaderFooter' => true, 
    		'preferCSSPageSize'   => false,
    		'marginTop'           => 0.4,
    		'marginBottom'        => 0.4, 
    		'marginLeft'          => 0.4, 
    		'marginRight'         => 0.4,
    		'paperWidth'          => 8.2677165354,              // A4
    		'paperHeight'         => 11.692913386,              // A4
    		'headerTemplate'      => $headerTemplate,
    		'footerTemplate'      => $footerTemplate, 
    		'scale'               => 1.0, 
    	];
    
    $pdf = $page->pdf($options);
    
    set_time_limit(3600); //1h php ecxecution time does not help
    
    $pdf->saveToFile("myPDF",2000000); //2000s timeout. 
    
    debug log (stripped from the massive HTML content)
    [2022-08-26T09:26:25.387187+02:00]
     chrome.DEBUG: process: initializing [] []
    [2022-08-26T09:26:25.387570+02:00] chrome.DEBUG: process: using 
    directory: /tmp/chromium-php-mYywcO [] []
    [2022-08-26T09:26:25.387902+02:00] chrome.DEBUG: process: starting 
    process: 'google-chrome' '--remote-debugging-port=0' 
    '--disable-background-networking' 
    '--disable-background-timer-throttling' 
    '--disable-client-side-phishing-detection' '--disable-hang-monitor' 
    '--disable-popup-blocking' '--disable-prompt-on-repost' '--disable-sync'
     '--disable-translate' '--disable-features=ChromeWhatsNewUI' 
    '--metrics-recording-only' '--no-first-run' 
    '--safebrowsing-disable-auto-update' '--enable-automation' 
    '--password-store=basic' '--use-mock-keychain' '--headless' 
    '--disable-gpu' '--font-render-hinting=none' '--hide-scrollbars' 
    '--mute-audio' '--disable-dev-shm-usage' '--disable-gpu' 
    '--user-data-dir=/tmp/chromium-php-mYywcO' [] []
    [2022-08-26T09:26:25.390952+02:00] chrome.DEBUG: process: waiting for 
    120 seconds for startup [] []
    [2022-08-26T09:26:25.411473+02:00] chrome.DEBUG: process: chrome 
    output:mkdir: cannot create directory '/var/www/.local': Permission 
    denied touch: cannot touch 
    '/var/www/.local/share/applications/mimeapps.list': No such file or 
    directory [] []
    [2022-08-26T09:26:25.411542+02:00] chrome.DEBUG: process: ignoring 
    output:mkdir: cannot create directory '/var/www/.local': Permission 
    denied [] []
    [2022-08-26T09:26:25.411564+02:00] chrome.DEBUG: process: ignoring 
    output:touch: cannot touch 
    '/var/www/.local/share/applications/mimeapps.list': No such file or 
    directory [] []
    [2022-08-26T09:26:25.523254+02:00] chrome.DEBUG: process: chrome 
    output:DevTools listening on 
    ws://127.0.0.1:40683/devtools/browser/e77d2f06-3a47-401b-b663-3aed0350d527
     [0826/092625.522128:WARNING:bluez_dbus_manager.cc(247)] Floss manager 
    not present, cannot set Floss enable/disable. [] []
    [2022-08-26T09:26:25.523323+02:00] chrome.DEBUG: process: ✓ accepted 
    output [] []
    [2022-08-26T09:26:25.523351+02:00] chrome.DEBUG: process: connecting 
    using 
    ws://127.0.0.1:40683/devtools/browser/e77d2f06-3a47-401b-b663-3aed0350d527
     [] []
    [2022-08-26T09:26:25.529685+02:00] chrome.DEBUG: socket(1): connecting 
    [] []
    [2022-08-26T09:26:25.542386+02:00] chrome.DEBUG: socket(1): ✓ connected 
    [] []
    [2022-08-26T09:26:25.542609+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":1,"method":"Target.setDiscoverTargets","params":{"discover":true}}
     [] []
    [2022-08-26T09:26:25.547815+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetCreated","params":{"targetInfo":{"targetId":"3a3ce4c3-420d-48a5-b8a1-c0f8ccbf1c59","type":"browser","title":"","url":"","attached":true,"canAccessOpener":false}}}
     [] []
    [2022-08-26T09:26:25.547894+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetCreated [] []
    [2022-08-26T09:26:25.548137+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetCreated","params":{"targetInfo":{"targetId":"6309C6AC85457AC996F7FFD531AAC449","type":"page","title":"","url":"about:blank","attached":false,"canAccessOpener":false,"browserContextId":"5952BBAFAC447B8E68FDD1CF57F2B7BF"}}}
     [] []
    [2022-08-26T09:26:25.548176+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetCreated [] []
    [2022-08-26T09:26:25.548605+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetCreated","params":{"targetInfo":{"targetId":"d0bc892f-22e9-4f20-ba12-17432538e7e3","type":"browser","title":"","url":"","attached":false,"canAccessOpener":false}}}
     [] []
    [2022-08-26T09:26:25.548645+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetCreated [] []
    [2022-08-26T09:26:25.548827+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":1,"result":{}} [] []
    [2022-08-26T09:26:25.549005+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":2,"method":"Network.setExtraHTTPHeaders","params":{}} [] []
    [2022-08-26T09:26:25.549698+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":2,"error":{"code":-32601,"message":"'Network.setExtraHTTPHeaders'
     wasn't found"}} [] []
    [2022-08-26T09:26:25.549761+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":3,"method":"Target.createTarget","params":{"url":"about:blank"}}
     [] []
    [2022-08-26T09:26:25.550404+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetCreated","params":{"targetInfo":{"targetId":"58A6FB6375758E408EF1B521B3CFB491","type":"page","title":"","url":"","attached":false,"canAccessOpener":false,"browserContextId":"5952BBAFAC447B8E68FDD1CF57F2B7BF"}}}
     [] []
    [2022-08-26T09:26:25.550448+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetCreated [] []
    [2022-08-26T09:26:25.554344+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":3,"result":{"targetId":"58A6FB6375758E408EF1B521B3CFB491"}} 
    [] []
    [2022-08-26T09:26:25.554422+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":4,"method":"Target.attachToTarget","params":{"targetId":"58A6FB6375758E408EF1B521B3CFB491","flatten":true}}
     [] []
    [2022-08-26T09:26:25.557688+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":"58A6FB6375758E408EF1B521B3CFB491","type":"page","title":"","url":"about:blank","attached":true,"canAccessOpener":false,"browserContextId":"5952BBAFAC447B8E68FDD1CF57F2B7BF"}}}
     [] []
    [2022-08-26T09:26:25.557742+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetInfoChanged [] []
    [2022-08-26T09:26:25.558003+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.attachedToTarget","params":{"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D","targetInfo":{"targetId":"58A6FB6375758E408EF1B521B3CFB491","type":"page","title":"","url":"about:blank","attached":true,"canAccessOpener":false,"browserContextId":"5952BBAFAC447B8E68FDD1CF57F2B7BF"},"waitingForDebugger":false}}
     [] []
    [2022-08-26T09:26:25.558041+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.attachedToTarget [] []
    [2022-08-26T09:26:25.558286+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":4,"result":{"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}} 
    [] []
    [2022-08-26T09:26:25.558402+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":5,"method":"Page.getFrameTree","params":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.590827+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":"6309C6AC85457AC996F7FFD531AAC449","type":"page","title":"about:blank","url":"about:blank","attached":false,"canAccessOpener":false,"browserContextId":"5952BBAFAC447B8E68FDD1CF57F2B7BF"}}}
     [] []
    [2022-08-26T09:26:25.590915+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetInfoChanged [] []
    [2022-08-26T09:26:25.593580+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetInfoChanged","params":{"targetInfo":{"targetId":"58A6FB6375758E408EF1B521B3CFB491","type":"page","title":"about:blank","url":"about:blank","attached":true,"canAccessOpener":false,"browserContextId":"5952BBAFAC447B8E68FDD1CF57F2B7BF"}}}
     [] []
    [2022-08-26T09:26:25.593641+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetInfoChanged [] []
    [2022-08-26T09:26:25.597496+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":5,"result":{"frameTree":{"frame":{"id":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","url":"about:blank","domainAndRegistry":"","securityOrigin":"://","mimeType":"text/html","adFrameStatus":{"adFrameType":"none"},"secureContextType":"InsecureScheme","crossOriginIsolatedContextType":"NotIsolated","gatedAPIFeatures":[]}}},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.597969+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":6,"method":"Page.enable","params":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.599051+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":6,"result":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.599119+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":7,"method":"Network.enable","params":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.599981+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":7,"result":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.600037+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":8,"method":"Runtime.enable","params":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.609663+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Runtime.executionContextCreated","params":{"context":{"id":1,"origin":"://","name":"","uniqueId":"-543665680199210751.2331896562393979101","auxData":{"isDefault":true,"type":"default","frameId":"58A6FB6375758E408EF1B521B3CFB491"}}},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.609730+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Runtime.executionContextCreated [] []
    [2022-08-26T09:26:25.609962+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":8,"result":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.610011+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":9,"method":"Page.setLifecycleEventsEnabled","params":{"enabled":true},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.611189+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"commit","timestamp":4314570.510065},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.611233+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:25.611626+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"DOMContentLoaded","timestamp":4314570.510097},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.611659+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"load","timestamp":4314570.511243},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.611677+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"networkAlmostIdle","timestamp":4314570.511745},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.611692+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"networkIdle","timestamp":4314570.511745},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.611717+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:25.611872+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:25.612059+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:25.612209+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:25.612800+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":9,"result":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.621001+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":10,"method":"Page.setDocumentContent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","html":"2MiB of HTML Table"},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.828577+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.documentOpened","params":{"frame":{"id":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","url":"about:blank","domainAndRegistry":"","securityOrigin":"://","mimeType":"text/html","adFrameStatus":{"adFrameType":"none"},"secureContextType":"InsecureScheme","crossOriginIsolatedContextType":"NotIsolated","gatedAPIFeatures":[]}},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.828658+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.documentOpened [] []
    [2022-08-26T09:26:25.828925+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"init","timestamp":4314570.743883},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:25.828989+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:26.559966+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.domContentEventFired","params":{"timestamp":4314571.475475},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:26.560041+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.domContentEventFired [] []
    [2022-08-26T09:26:26.560248+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"DOMContentLoaded","timestamp":4314571.475475},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:26.560284+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:26.560715+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":10,"result":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:28.973978+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.loadEventFired","params":{"timestamp":4314573.889102},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:28.974052+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.loadEventFired [] []
    [2022-08-26T09:26:28.974760+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"load","timestamp":4314573.889102},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:28.974812+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:29.228360+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"firstPaint","timestamp":4314574.138263},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:29.228435+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:29.229372+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":11,"method":"Page.printToPDF","params":{"landscape":true,"printBackground":true,"displayHeaderFooter":true,"preferCSSPageSize":false,"marginTop":0.4,"marginBottom":0.4,"marginLeft":0.4,"marginRight":0.4,"paperWidth":8.2677165354,"paperHeight":11.692913386,"headerTemplate":"<table
     style=\"margin-left: auto; margin-right: auto;width:calc(100% - 
    1cm)\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td 
    style=\"width:33%;font-size:4pt\">long table<\/td>\n\t\t\t\t\t\t\t<td 
    style=\"width:33%;font-size:4pt;text-align: center\">report: long table<\/td>\n\t\t\t\t\t\t\t<td 
    style=\"font-size:4pt;text-align: right\">2022-08-26 
    09:26<\/td>\n\t\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t<\/table>","footerTemplate":"<table
     style=\"margin-left: auto; margin-right: auto;width:calc(100% - 
    1cm)\">\n\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t<td 
    style=\"width:33%;font-size:4pt\">footer left<\/td>\n\t\t\t\t\t\t\t<td 
    style=\"width:33%;font-size:4pt;text-align: center\">footer center <\/td>\n\t\t\t\t\t\t\t<td 
    style=\"font-size:4pt;text-align: right\">page <span 
    class=\"pageNumber\"><\/span> of <span 
    class=\"totalPages\"><\/span><\/td>\n\t\t\t\t\t\t<\/tr>\n\t\t\t\t\t<\/table>","scale":1},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:29.229704+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"firstContentfulPaint","timestamp":4314574.138263},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:29.229736+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Page.lifecycleEvent","params":{"frameId":"58A6FB6375758E408EF1B521B3CFB491","loaderId":"CA4DE20CCC1D1CDA464A16BDAA552485","name":"firstMeaningfulPaintCandidate","timestamp":4314574.138263},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:29.229764+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:29.229914+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Page.lifecycleEvent [] []
    [2022-08-26T09:26:56.316184+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":11,"error":{"code":-32000,"message":"Printing 
    failed"},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"} [] []
    [2022-08-26T09:26:56.965857+02:00] chrome.DEBUG: process: killing chrome
     [] []
    [2022-08-26T09:26:56.966002+02:00] chrome.DEBUG: process: trying to 
    close chrome gracefully [] []
    [2022-08-26T09:26:56.966350+02:00] chrome.DEBUG: socket(1): → sending 
    data:{"id":12,"method":"Browser.close","params":{}} [] []
    [2022-08-26T09:26:56.968385+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Inspector.targetCrashed","params":{},"sessionId":"8B2EB9C1CBB10FC5DA2ED132EF674C5D"}
     [] []
    [2022-08-26T09:26:56.968431+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"method":"Target.targetCrashed","params":{"targetId":"58A6FB6375758E408EF1B521B3CFB491","status":"crashed","errorCode":132}}
     [] []
    [2022-08-26T09:26:56.968468+02:00] chrome.DEBUG: 
    session(8B2EB9C1CBB10FC5DA2ED132EF674C5D): ⇶ dispatching 
    method:Inspector.targetCrashed [] []
    [2022-08-26T09:26:56.968823+02:00] chrome.DEBUG: connection: ⇶ 
    dispatching method:Target.targetCrashed [] []
    [2022-08-26T09:26:56.969736+02:00] chrome.DEBUG: socket(1): ← receiving 
    data:{"id":12,"result":{}} [] []
    [2022-08-26T09:26:56.969800+02:00] chrome.DEBUG: socket(1): 
    disconnecting [] []
    [2022-08-26T09:26:56.970653+02:00] chrome.DEBUG: socket(1): ✓ 
    disconnected [] []
    [2022-08-26T09:26:56.970684+02:00] chrome.DEBUG: process: waiting for 
    process to close [] []
    [2022-08-26T09:26:57.045544+02:00] chrome.DEBUG: process: cleaning 
    temporary resources:/tmp/chromium-php-mYywcO [] []
    PHP error
    HeadlessChromium\Exception\PdfFailed Object
    (
        [message:protected] => Cannot make a PDF. Reason : -32000 - Printing failed
        [string:Exception:private] => 
        [code:protected] => 0
        [file:protected] => /var/www/html/vendor/chrome-php/chrome/src/PageUtils/PagePdf.php
        [line:protected] => 119
        [trace:Exception:private] => Array
            (
                [0] => Array
                    (
                        [file] => /var/www/html/vendor/chrome-php/chrome/src/PageUtils/AbstractBinaryInput.php
                        [line] => 70
                        [function] => getException
                        [class] => HeadlessChromium\PageUtils\PagePdf
                        [type] => ->
                    )
    
            [1] => Array
                (
                    [file] => /var/www/html/print.php
                    [line] => 110
                    [function] => saveToFile
                    [class] => HeadlessChromium\PageUtils\AbstractBinaryInput
                    [type] => ->
                )
        )
    
    [previous:Exception:private] => 
    

    )

    opened by derStephan 13
  • Dom changes after the click + sleep/wait/timeout

    Dom changes after the click + sleep/wait/timeout

    Having something like that

    $elem = $page->dom()->querySelectorAll('.user-card__action');
    foreach($elem as $e)
    {
    	$e->click();
    	$content = $page->evaluate( 'document.documentElement.innerHTML' )->getReturnValue();
    	var_dump(strpos($content, 'Hello there!'));
    	//$elem2 = $page->dom()->querySelector('.messenger-ovl__close.im-close');
    	//var_dump($elem2);
    	//$elem2->click();
    	sleep(1);
    }
    
    die;
    

    It doesnt work, $elem2 evaluates to null. I think because the first click $e->click(); changes the DOM. Hello there! is always absent since it can be seen only sometimes after the click $e->click();. And the $elem2 is always null because the '.messenger-ovl__close.im-close' appears always but only after the first click. So it is not present in the beginning in the DOM. its a close button that appears after first click of any card.

    three questions. 1.So the first click changes the DOM. How to update it/reload it/...? So I can find the '.messenger-ovl__close.im-close' and click it after the first click that wasnt present before the first click? 2.Am I doing some waiting right? With sleep(1);? I want the browser and the code to wait at least 1-2s after the clicking, if its too fast it doesnt work. Or is there better/proper way? 3.How to get the actual html after the first click? Cuz sometimes Hello there! is present but only after the first click $e->click(); and it seems like $content = $page->evaluate( 'document.documentElement.innerHTML' )->getReturnValue(); returns the innerHTML before the first click happens.

    opened by Devloger 0
  • search() closes browser

    search() closes browser

    Hello,

    I am currently struggling with this line of code: $page->dom()->querySelectorAll('#filialangebote'); It somehow forces the browser to close, ends the script and does not return a value before doing so. The thrown exception is a "Operation timed out after 1000ms." one, however, I certainly know that the required element is there (node based puppeteer finds the element just fine).

    $page->getHtml() also throws a "Operation timed out after 2s." exception and all evaluete() calls time out too.

    Any ideas what might cause this behavior? Thanks in advance

    opened by AndiLeni 3
  • Listen to JavaScript events

    Listen to JavaScript events

    This might be similar to https://github.com/chrome-php/chrome/issues/444 and https://github.com/chrome-php/chrome/issues/115, but is there (already) a way to listen to JavaScript events?

    For example, I am interested in the shown.bs.tab event, fired by a Bootstrap Tab when it has become visible.

    I checked the various domains at https://chromedevtools.github.io/devtools-protocol/ but could not find something that worked...

    Use Case

    1. open page, content of the first Tab is displayed
    2. click specific Tab, which changes the DOM: the clicked Tab will get the class "active" assigned
    3. select the first heading in the opened Tab, using something like //li[@class="active]//h1

    Observations Clicking the Tab works: when creating screenshot, the content of the clicked Tab is displayed.

    However, selecting the first heading in the clicked / active tab fails, the heading on the first Tab is displayed...

    So I got the feeling that after clicking the Tab, the DOM is not updated properly, or I am trying to select the first heading too quickly...

    Any thoughts / pointers on this?

    opened by holtkamp 1
  • $browser->createPage() times out.

    $browser->createPage() times out.

    Hi everyone. I am currently trying to deploy my local PHP Script to my Linux Server with chromium already installed.

    Currently, every time I want to create a page, I get back an 500 Error. When I put the code block into a try catch block, I get the following result. Does anyone have an idea where the issue could be?

    My code:

        $browserFactory = new BrowserFactory('chromium-browser');
        $browser = $browserFactory->createBrowser([
          'debugLogger'     => 'php://stdout',
        ]);
    
        $page = $browser->createPage();
        $page->navigate("https://google.de")->waitForNavigation();
    

    The error I get:

    HeadlessChromium\Exception\OperationTimedOut Object
    (
        [message:protected] => Operation timed out after 5s.
        [string:Exception:private] => 
        [code:protected] => 0
        [file:protected] => /home/convert.a-sites.de/public_html/vendor/chrome-php/chrome/src/Exception/OperationTimedOut.php
        [line:protected] => 18
        [trace:Exception:private] => Array
            (
                [0] => Array
                    (
                        [file] => /home/convert.a-sites.de/public_html/vendor/chrome-php/chrome/src/Utils.php
                        [line] => 67
                        [function] => createFromTimeout
                        [class] => HeadlessChromium\Exception\OperationTimedOut
                        [type] => ::
                    )
    
                [1] => Array
                    (
                        [file] => /home/convert.a-sites.de/public_html/vendor/chrome-php/chrome/src/Communication/ResponseReader.php
                        [line] => 115
                        [function] => tryWithTimeout
                        [class] => HeadlessChromium\Utils
                        [type] => ::
                    )
    
                [2] => Array
                    (
                        [file] => /home/convert.a-sites.de/public_html/vendor/chrome-php/chrome/src/Communication/Session.php
                        [line] => 89
                        [function] => waitForResponse
                        [class] => HeadlessChromium\Communication\ResponseReader
                        [type] => ->
                    )
    
                [3] => Array
                    (
                        [file] => /home/convert.a-sites.de/public_html/vendor/chrome-php/chrome/src/Browser.php
                        [line] => 221
                        [function] => sendMessageSync
                        [class] => HeadlessChromium\Communication\Session
                        [type] => ->
                    )
    
                [4] => Array
                    (
                        [file] => /home/convert.a-sites.de/public_html/vendor/chrome-php/chrome/src/Browser.php
                        [line] => 156
                        [function] => getPage
                        [class] => HeadlessChromium\Browser
                        [type] => ->
                    )
    
                [5] => Array
                    (
                        [file] => /home/convert.a-sites.de/public_html/index.php
                        [line] => 34
                        [function] => createPage
                        [class] => HeadlessChromium\Browser
                        [type] => ->
                    )
    
            )
    
        [previous:Exception:private] => 
    )
    
    opened by MaxAryus 1
Releases(v1.7.1)
  • v1.7.1(Sep 4, 2022)

  • v1.7.0(Aug 28, 2022)

  • v1.6.2(Aug 28, 2022)

  • v1.6.1(May 17, 2022)

  • v1.6.0(Mar 30, 2022)

  • v1.5.0(Mar 25, 2022)

    • Added Browser::getPages method
    • Added Page::waitUntilContainsElement() method
    • Added Page::setHtml() method
    • Added support for XPath by introducing Selector
    • Added Mouse::findElement() method
    • Switch to flatten mode
    Source code(tar.gz)
    Source code(zip)
  • v1.4.1(Mar 25, 2022)

    • Added fallback to css layout metrics
    • Added missing destroyed setting
    • Prevent Node::querySelector from returning nodeId 0
    • Fixed "What's new" page opening on startup
    • More fixes to enable eventual PHP 8.1 support
    Source code(tar.gz)
    Source code(zip)
  • v1.4.0(Jan 24, 2022)

  • v1.3.1(Jan 23, 2022)

    • Fixed issues with Keyboard::typeText with multibyte strings
    • Fixed issues with retina and scaled displays
    • Fixed issues with timeouts if system time changes
    • Fixed Mouse::find() after cursor has moved
    Source code(tar.gz)
    Source code(zip)
  • v1.3.0(Dec 7, 2021)

  • v1.2.1(Dec 7, 2021)

  • v1.2.0(Nov 20, 2021)

    • Dropped --disable-default-apps and --disable-extensions by default
    • Added API for interacting with the DOM
    • Added a way to set the timezone
    • Reworked PagePdf class to improve validation
    Source code(tar.gz)
    Source code(zip)
  • v1.1.1(Nov 20, 2021)

  • v1.1.0(Sep 26, 2021)

  • v1.0.1(Sep 1, 2021)

  • v0.11.2(Sep 1, 2021)

  • v1.0.0(Aug 15, 2021)

  • v0.11.1(Aug 15, 2021)

  • v0.11.0(Jul 18, 2021)

    • Added support for proxy servers as a direct option
    • Added support for passing environment variables
    • Added support for Symfony 6
    • Removed broken getChromeVersion function
    • Implemented more robust auto-discovery
    Source code(tar.gz)
    Source code(zip)
  • v0.10.0(May 22, 2021)

    • Added Page::getHtml
    • Added keyboard API
    • Added mouse scrolling
    • Attempt to auto-detect chrome binary path
    • Added support for setDownloadPath
    • Added support for captureBeyondViewport
    Source code(tar.gz)
    Source code(zip)
  • v0.9.0(Dec 9, 2020)

    • Support PHP 8.0
    • Increase default sync timeout to 5 seconds
    • Set --font-render-hinting=none in headless mode
    • Fixed keep alive option
    • Fixed various phpdoc issues
    • Fixed sending params to newer Chrome
    • Fixed Wrench::connect() return value
    • Avoid non-thread-safe getenv function
    Source code(tar.gz)
    Source code(zip)
  • v0.8.1(Feb 20, 2020)

  • v0.8.0(Feb 20, 2020)

  • v0.7.0(Oct 4, 2019)

    • Escaping custom flags for BrowserFactory is now automatic
    • Added timeout for Page::getFullPageClip
    • Added timeout for method getBase64
    • Added options headerTemplate and footerTempalte for Page::pdf
    • Added options scale for Page::pdf
    • Handle gracefully all pages failing to close
    • Fixed deprecation from Symfony
    Source code(tar.gz)
    Source code(zip)
  • v0.6.0(Nov 14, 2018)

  • v0.5.0(Nov 13, 2018)

    Load script tags on the page

    • Features:
      • Added Page::callFunction
      • Added Page::addScriptTag
      • Enabled runtime events for pages
      • Enabled user gesture for Page::evaluate
      • Improved error messages from script evaluation
      • Added option "onLoad" for Page::addPreScript
    Source code(tar.gz)
    Source code(zip)
  • v0.4.0(Oct 25, 2018)

    • Features:
      • Added Page::getLayoutMetrics that allows to take full page screenshots (#43 #44) thanks @kaero598
      • Added Page::getFullPageClip to ease full page screenshots

    Do full page screenshots:

    $page->screenshot([
             'clip' => $page->getFullPageClip()
         ])
          ->saveToFile('/tmp/image.jpg');
    
    Source code(tar.gz)
    Source code(zip)
  • v0.3.0(Oct 13, 2018)

    Make a crawl instance sharable among multiple scripts

    • Features:
      • Added option keepAlive for browser factory.
      • Added methods BrowserProcess::getSocketUri and ProcessAwareBrowser::getSocketUri
      • Removed unused option debug
      • Added BrowserFactory::connectToBrowser
    • Bug fixes:
      • (BC Break) Page navigation now allows by default that the initial loader is replaced with a new one #40
    Source code(tar.gz)
    Source code(zip)
  • v0.2.4(Oct 4, 2018)

  • v0.2.3(Oct 2, 2018)

    • Features:
      • Added method Browser::setPagePreScript
      • Added method Page::addPreScript
      • Added option "nosandbox" for browser factory
      • Added option "sendSyncDefaultTimeout" for browser factory
      • Added option "ignoreCertificateErrors" for browser factory
      • Added option "customFlags" for browser factory
    • Bug fixes:
      • Fixed user agent string for browser factory
    Source code(tar.gz)
    Source code(zip)
Owner
Chrome PHP
Control headless chrome/chromium from php
Chrome PHP
PHP APM (Alternative PHP Monitor)

APM (Alternative PHP Monitor) APM (Alternative PHP Monitor) is a monitoring extension enabling native Application Performance Management (APM) for PHP

Patrick Allaert 310 Dec 4, 2022
Zipkin PHP is the official PHP Tracer implementation for Zipkin

Zipkin PHP is the official PHP Tracer implementation for Zipkin, supported by the OpenZipkin community. Installation composer require openz

Open Zipkin 250 Nov 12, 2022
Debug bar for PHP

PHP Debug Bar Displays a debug bar in the browser with information from php. No more var_dump() in your code! Features: Generic debug bar Easy to inte

Maxime Bouroumeau-Fuseau 4k Jan 8, 2023
Xdebug — Step Debugger and Debugging Aid for PHP

Xdebug Xdebug is a debugging tool for PHP. It provides step-debugging and a whole range of development aids, such as stack traces, a code profiler, fe

Xdebug 2.8k Jan 3, 2023
Kint - a powerful and modern PHP debugging tool.

Kint - debugging helper for PHP developers What am I looking at? At first glance Kint is just a pretty replacement for var_dump(), print_r() and debug

null 2.7k Dec 25, 2022
😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.

Tracy - PHP debugger Introduction Tracy library is a useful helper for everyday PHP programmers. It helps you to: quickly detect and correct errors lo

Nette Foundation 1.6k Dec 23, 2022
PHP Benchmarking framework

PHPBench is a benchmark runner for PHP analogous to PHPUnit but for performance rather than correctness. Features include: Revolutions: Repeat your co

PHPBench 1.7k Jan 2, 2023
The Interactive PHP Debugger

The interactive PHP debugger Implemented as a SAPI module, phpdbg can exert complete control over the environment without impacting the functionality

Joe Watkins 841 Oct 9, 2022
Dontbug is a reverse debugger for PHP

Dontbug Debugger Dontbug is a reverse debugger (aka time travel debugger) for PHP. It allows you to record the execution of PHP scripts (in command li

Sidharth Kshatriya 709 Dec 30, 2022
PHP Debug Console

PHP Console A web console to try your PHP code into Creating a test file or using php's interactive mode can be a bit cumbersome to try random php sni

Jordi Boggiano 523 Nov 7, 2022
Php Debugger to run in terminal to debug your code easily.

What is Dephpugger? Dephpugger (read depugger) is an open source lib to make a debug in php direct in terminal, without necessary configure an IDE. Th

Renato Cassino 190 Dec 3, 2022
PCOV - CodeCoverage compatible driver for PHP

PCOV A self contained CodeCoverage compatible driver for PHP Requirements and Installation See INSTALL.md API /** * Shall start recording coverage in

Joe Watkins 613 Dec 21, 2022
Low-overhead sampling profiler for PHP 7+

phpspy phpspy is a low-overhead sampling profiler for PHP. For now, it works with Linux 3.2+ x86_64 non-ZTS PHP 7.0+ with CLI, Apache, and FPM SAPIs.

Adam 1.3k Dec 24, 2022
The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. It provides a better dump() function that you can use instead of var_dump().

VarDumper Component The VarDumper component provides mechanisms for walking through any arbitrary PHP variable. It provides a better dump() function t

Symfony 7.1k Jan 1, 2023
PHP errors for cool kids

whoops PHP errors for cool kids whoops is an error handler framework for PHP. Out-of-the-box, it provides a pretty error interface that helps you debu

Filipe Dobreira 12.9k Dec 24, 2022
Clockwork - php dev tools in your browser - server-side component

Clockwork is a development tool for PHP available right in your browser. Clockwork gives you an insight into your application runtime - including requ

its 4.8k Dec 29, 2022
Laravel Debugbar (Integrates PHP Debug Bar)

Laravel Debugbar This is a package to integrate PHP Debug Bar with Laravel. It includes a ServiceProvider to register the debugbar and attach it to th

Barry vd. Heuvel 14.8k Jan 9, 2023
This package connects a Laravel Octance application with Tideways for PHP Monitoring, Profiling and Exception Tracking.

Tideways Middleware for Laravel Octane This package connects a Laravel Octance application with Tideways for PHP Monitoring, Profiling and Exception T

Tideways 7 Jan 6, 2022
A tool to profile mysql queries in php env.

MysqlQueryProfiler This tool helps you to quickly profile a mysql query in a PHP 7.4+ environnement. You can also compare 2 queries. This image shows

null 8 Jul 30, 2021