chrome-php/chrome
Control headless Chrome from PHP. chrome-php/chrome launches Chromium/Chrome, lets you navigate pages, evaluate JavaScript, take screenshots/PDFs, intercept network events, and automate workflows via the DevTools protocol—ideal for scraping, testing, and rendering.
## Getting Started
### Minimal Setup
1. **Installation**
```bash
composer require chrome-php/chrome
Requires PHP 8.1+ and Chromium/Chrome installed on the system.
First Use Case: Launch Headless Chrome
use ChromePhp\Chrome\Chrome;
$chrome = new Chrome();
$chrome->launch([
'headless' => true,
'args' => ['--no-sandbox', '--disable-setuid-sandbox']
]);
$tab = $chrome->createTab();
$tab->navigate('https://example.com');
// Wait for page to load
$tab->waitForNavigation();
// Capture screenshot
$screenshot = $tab->screenshot();
file_put_contents('screenshot.png', $screenshot);
$chrome->close();
Key Entry Points
Chrome::launch(): Start a Chrome instance.Chrome::createTab(): Create a new tab.Tab::navigate(): Load a URL.Tab::waitFor*(): Wait for DOM/network conditions (e.g., waitForSelector, waitForNavigation).Tab::evaluate(): Run JavaScript in the page context.Tab::screenshot()/Tab::pdf(): Capture artifacts.Where to Look First
src/ChromePhp/Chrome/ for core classes (Chrome, Tab, Page)./tests for real-world usage patterns.$chrome = new Chrome();
$chrome->launch(['headless' => true]);
$tab = $chrome->createTab();
$tab->navigate('https://example.com/dynamic-page');
$tab->waitForSelector('#dynamic-content'); // Wait for element to load
$content = $tab->evaluate('() => document.querySelector("#dynamic-content").innerText');
$chrome->close();
return $content;
$chrome = new Chrome();
$chrome->launch(['headless' => true]);
$tab = $chrome->createTab();
$tab->navigate('https://example.com/ssr-page');
$tab->waitForNavigation();
$html = $tab->evaluate('() => document.documentElement.outerHTML');
$chrome->close();
return $html; // Use in Laravel views or APIs
use ChromePhp\Chrome\Chrome;
use Illuminate\Support\Facades\Http;
public function testHeadless()
{
$chrome = new Chrome();
$chrome->launch(['headless' => true]);
$tab = $chrome->createTab();
$tab->navigate(url('/api/test-endpoint'));
$tab->waitForSelector('body'); // Wait for API response to render
$screenshot = $tab->screenshot();
$chrome->close();
// Assert screenshot or content
Storage::put('test-screenshot.png', $screenshot);
}
// app/Services/ChromeService.php
namespace App\Services;
use ChromePhp\Chrome\Chrome;
class ChromeService
{
protected ?Chrome $chrome = null;
public function getChrome(): Chrome
{
if (!$this->chrome) {
$this->chrome = new Chrome();
$this->chrome->launch(['headless' => true]);
}
return $this->chrome;
}
public function close(): void
{
$this->chrome?->close();
$this->chrome = null;
}
}
$chrome = new Chrome();
$chrome->launch(['headless' => true]);
$tabs = [];
foreach (['url1', 'url2', 'url3'] as $url) {
$tab = $chrome->createTab();
$tab->navigate($url);
$tabs[] = $tab;
}
// Process tabs in parallel (e.g., with Laravel Queues)
foreach ($tabs as $tab) {
$tab->waitForNavigation();
$title = $tab->evaluate('() => document.title');
// Store $title...
}
$chrome->close();
$chrome->launch([
'headless' => env('CHROME_HEADLESS', true),
'args' => [
'--disable-gpu',
'--window-size=1920,1080',
'--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
],
'timeout' => 30000, // 30 seconds
]);
// app/Jobs/ScrapeJob.php
namespace App\Jobs;
use ChromePhp\Chrome\Chrome;
use Illuminate\Bus\Queueable;
class ScrapeJob implements Queueable
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function handle()
{
$chrome = new Chrome();
$chrome->launch(['headless' => true]);
try {
$tab = $chrome->createTab();
$tab->navigate('https://example.com/scrape');
$data = $tab->evaluate('() => scrapeData()'); // Custom JS
// Save $data to DB...
} finally {
$chrome->close();
}
}
}
$tab->navigate('https://example.com/api-heavy-page');
$tab->waitForNetworkIdle(); // Wait for network to settle
$tab->waitForSelector('.loaded-data'); // Wait for DOM update
$tab->navigate('https://example.com');
$tab->waitForNavigation();
// Execute JS and return result
$results = $tab->evaluate(<<<'JS'
() => {
const data = [];
document.querySelectorAll('.item').forEach(el => {
data.push({
text: el.textContent,
href: el.href
});
});
return data;
}
JS);
// $results is now an array of objects
$tab->navigate('https://example.com/invoice');
$tab->waitForNavigation();
$pdf = $tab->pdf([
'format' => 'A4',
'margin' => ['top' => '1cm', 'right' => '1cm', 'bottom' => '1cm', 'left' => '1cm'],
]);
file_put_contents('invoice.pdf', $pdf);
Chrome/Chromium Installation
which chromium or which chrome.sudo apt-get install chromium-chromedriver
brew install chromedriver
Sandboxing Issues
'args' => ['--no-sandbox', '--disable-setuid-sandbox']
--no-sandbox in trusted environments (e.g., local development, CI).Memory Leaks
$chrome->close() to free resources. Forgetting this can exhaust system memory.Timeouts
$chrome->launch(['timeout' => 60000]); // 60 seconds
waitFor* methods with custom timeouts:
$tab->waitForSelector('#element', 10000); // 10s timeout
JavaScript Evaluation
evaluate() runs in the page context. Avoid window or document in the returned JS to prevent security errors.() => window.someUnsafeFunction() // May throw "Access to 'window' denied"
() => document.querySelector('.safe-element').textContent
Headless vs. Non-Headless
'headless' => true) is faster but may miss some rendering issues.$chrome->launch(['headless' => false]);
Viewport and Emulation
How can I help you explore Laravel packages today?