Calling Puppeteer via PHP
My Puppeteer screenshot script stopped working recently and it took me a while to figure out why. The script had been working fine, although it was a bit fiddly to set-up and never really felt very robust. In hindsight, I'm not sure how it ever worked, given what was required to get it fixed.
Anyhow, the script is pretty basic and its job is to take a screenshot of a website. The script is called whenever I create a bookmark and tag it with 'inspiration', I can also call it manually when I want to attach a screenshot to some content.
My website is written PHP using the CodeIgniter framework and I call the Puppeteer script inside a custom library/class using PHP's builtin exec
function. The PHP code looks like this:
$command = '/usr/bin/node ' . ROOTPATH . 'misc/node-puppeteer-screenshot/screenshot.js';
$img = $prefix . md5($url) . '.png';
$img_path = WRITEPATH . 'uploads/tmp/' . $img;
// Run the NodeJS screenshot script
$result = exec(
command: sprintf(
$command . " '%s' '%s' '%s' '%s'",
escapeshellcmd($url),
escapeshellcmd($img_path),
escapeshellcmd($width),
escapeshellcmd($height)
),
result_code: $exit_code
);
And the JavaScript looks like this:
const puppeteer = require('puppeteer');
(async() => {
let width = process.argv[4];
let height = process.argv[5];
if(width === undefined){
width = 1280;
}
if(height === undefined){
height = 720;
}
const browser = await puppeteer.launch({
args: ['--no-sandbox', '--disable-setuid-sandbox'],
headless: 'true',
defaultViewport: {
width: parseInt(width),
height: parseInt(height),
},
executablePath: '/usr/bin/chromium',
});
const page = await browser.newPage();
await page.goto(process.argv[2], {
waitUntil: 'load',
});
await page.screenshot({
captureBeyondViewport: false,
path: process.argv[3],
});
await browser.close();
})();
The PHP script passes 4 arguments to the JavaScript:
- URL - the address of the website to take a screenshot of
- Path - location to save the image to
- Width - width of the image in pixels
- Height - height of the image in pixels
As I mentioned, the script was working fine, but then it stopped working. The error messages produced by the script were not too helpful, and interestingly, the Puppeteer script worked fine when I ran it directly under my user account. The script also worked when run as root. So, I figured the issue had to be with the Apache user under Debian.
Turns out, the Apache user www-data
does not respect the XDG Base Directory Specification, which is to be expected, but this meant that Chromium was unable to start. I fixed the error by exporting the required environment variables in the PHP script directly before calling the JavaScript. The PHP now looks like this:
$command = 'export XDG_CONFIG_HOME=/tmp/.chromium && ';
$command .= 'export XDG_CACHE_HOME=/tmp/.chromium && ';
$command .= '/usr/bin/node ' . ROOTPATH . 'misc/node-puppeteer-screenshot/screenshot.js';
$img = $prefix . md5($url) . '.png';
$img_path = WRITEPATH . 'uploads/tmp/' . $img;
// Run the NodeJS screenshot script
$result = exec(
command: sprintf(
$command . " '%s' '%s' '%s' '%s'",
escapeshellcmd($url),
escapeshellcmd($img_path),
escapeshellcmd($width),
escapeshellcmd($height)
),
result_code: $exit_code
);
I thought I'd write a quick blog post about this, just in case anyone else encounters a similar issue; hopefully I can save them some debugging time.
Comments