
If a domain is invalid or a web server is down, cURL will return false and not have a status code. We need to catch it and distinguish the difference between page not found and cannot connect for the user.
332 lines
8.3 KiB
PHP
332 lines
8.3 KiB
PHP
<?php
|
|
/**
|
|
* http2pic by Christian Haschek (https://haschek.solutions)
|
|
*
|
|
* For more info and up2date version of this file visit https://github.com/chrisiaut/http2pic
|
|
* -------------
|
|
*
|
|
* @category Website rendering API
|
|
* @author Christian Haschek <christian@haschek.at>
|
|
* @copyright 2015 by HASCHEK SOLUTIONS
|
|
* @link https://http2pic.haschek.at
|
|
*/
|
|
|
|
|
|
//
|
|
// Stuff you can edit
|
|
//
|
|
|
|
// if true, will save all cmd queries
|
|
define(DEBUG,false);
|
|
|
|
define(MAXTIMEOUT,30);
|
|
define(ONFAILIMAGE, __DIR__.'/img/pagefailed.jpg');
|
|
define(ONDOMAINFAILIMAGE, __DIR__.'/img/domainfailed.jpg');
|
|
|
|
//rendering engine: wkhtmltoimage or phantomjs
|
|
define(RENDERINGENGINE,'wkhtmltoimage');
|
|
|
|
//location of wkhtmltoimage
|
|
define(WKHTMLTOIMAGEPATH,'/usr/sbin/wkhtmltoimage');
|
|
|
|
//location of phantomJS
|
|
define(PHANTOMJSPATH,'/usr/bin/phantomjs');
|
|
|
|
//where shoud we store cached images
|
|
define(CACHEDIR,__DIR__.'/cache/');
|
|
|
|
|
|
|
|
|
|
//
|
|
// Only edit from here if you know what you are doing
|
|
//
|
|
class http2pic
|
|
{
|
|
private $params = array();
|
|
function __construct($params)
|
|
{
|
|
//try to create the cache folder if not exists
|
|
if (!is_dir(CACHEDIR)) {
|
|
mkdir(CACHEDIR);
|
|
}
|
|
|
|
$this->params = $params;
|
|
return $this->paramsPrepare();
|
|
}
|
|
|
|
/**
|
|
* Prepare and validate params
|
|
**/
|
|
function paramsPrepare()
|
|
{
|
|
//validate file type of rendered image
|
|
switch($this->params['type'])
|
|
{
|
|
case 'png': $this->params['type'] = 'png'; break;
|
|
case 'jpg': $this->params['type'] = 'jpg'; break;
|
|
default: $this->params['type'] = 'png';
|
|
}
|
|
|
|
//validate timeout
|
|
if (!$this->params['timeout'] || !is_numeric($this->params['timeout']) || ($this->params['timeout'] > MAXTIMEOUT || $this->params['timeout'] < 1))
|
|
$this->params['timeout'] = 10;
|
|
|
|
//validate viewport
|
|
if ($this->params['viewport'])
|
|
{
|
|
$a = explode('x', $this->params['viewport']);
|
|
$w = $a[0];
|
|
$h = $a[1];
|
|
if (is_numeric($w))
|
|
$this->params['vp_w'] = $w;
|
|
if (is_numeric($h))
|
|
$this->params['vp_h'] = $h;
|
|
}
|
|
|
|
//validate resize width
|
|
if($this->params['resizewidth'])
|
|
{
|
|
if(!is_numeric($this->params['resizewidth']) || $this->params['resizewidth']<1 || $this->params['resizewidth']>8000)
|
|
unset($this->params['resizewidth']);
|
|
}
|
|
|
|
if(!$this->params['onfail'])
|
|
$this->params['onfail'] = ONFAILIMAGE;
|
|
else
|
|
$this->params['onfail'] = rawurldecode($this->params['onfail']);
|
|
|
|
if(!$this->params['ondomainfail'])
|
|
$this->params['ondomainfail'] = ONDOMAINFAILIMAGE;
|
|
else
|
|
$this->params['ondomainfail'] = rawurldecode($this->params['ondomainfail']);
|
|
|
|
|
|
//validate URL and check if exists
|
|
if ($this->isBase64($this->params['url']))
|
|
$this->params['url'] = base64_decode($url);
|
|
else
|
|
$this->params['url'] = rawurldecode($_GET['url']);
|
|
|
|
//if the url is not valid or not responding, show onfail image and leave
|
|
if(!$this->isURLValid($this->params['url']) || !(($reachableResult = $this->isURLReachable($this->params['url'])) == 0))
|
|
{
|
|
header('Content-Type: image/jpeg');
|
|
header('Content-Disposition: inline; filename="http2png.jpg"');
|
|
switch ($reachableResult) {
|
|
case 1:
|
|
header('HTTP/1.0 404 File Not Found');
|
|
$result = imagecreatefromjpeg($this->params['onfail']);
|
|
break;
|
|
case 2:
|
|
header('HTTP/1.0 404 Server Not Found');
|
|
$result = imagecreatefromjpeg($this->params['ondomainfail']);
|
|
break;
|
|
}
|
|
imagejpeg($result, NULL, 100);
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
//prepare file name
|
|
$this->params['cache'] = $this->trimToAlphaNumeric($this->params['cache']);
|
|
$hash = $this->params['cache'].'-'.preg_replace("/[^A-Za-z0-9 ]/", '', $this->params['url']).'.'.$this->params['type'];
|
|
if (!$this->params['cache'])
|
|
$hash = md5(time().rand(1,2000)).$hash;
|
|
$this->params['file'] = CACHEDIR.$hash;
|
|
|
|
$this->render();
|
|
|
|
return true;
|
|
}
|
|
|
|
function render()
|
|
{
|
|
//if phantomjs is selected and installed
|
|
if(RENDERINGENGINE=='phantomjs' && file_exists(PHANTOMJSPATH))
|
|
return $this->renderPagePHANTOMJS();
|
|
|
|
//no? well ok how about WKHTMLToImage?
|
|
else if(RENDERINGENGINE=='wkhtmltoimage' && file_exists(WKHTMLTOIMAGEPATH))
|
|
return $this->renderPageWKHTMLTOIMAGE();
|
|
|
|
//you're fucked
|
|
else
|
|
throw new Exception('No valid rendering engine found');
|
|
}
|
|
|
|
|
|
/**
|
|
* Render using PhantomJS
|
|
**/
|
|
function renderPagePHANTOMJS()
|
|
{
|
|
$cmd = 'timeout '.$this->params['timeout'].' '.PHANTOMJSPATH;
|
|
$cmd.= ' --ignore-ssl-errors=yes --ssl-protocol=any '.__DIR__.'/phantom.js ';
|
|
|
|
$cmd.= ($this->params['url']);
|
|
$cmd.= ','.($this->params['file']);
|
|
$cmd.= ','.$this->params['vp_w'];
|
|
$cmd.= ','.$this->params['vp_h'];
|
|
$cmd.= ','.$this->params['js'];
|
|
|
|
$cmd = escapeshellcmd($cmd);
|
|
shell_exec($cmd);
|
|
$this->params['cmd'] = $cmd;
|
|
|
|
$this->postRender();
|
|
if(DEBUG)
|
|
{
|
|
$fp = fopen('debug.log', 'a');
|
|
fwrite($fp, $cmd."\n");
|
|
fclose($fp);
|
|
}
|
|
return $cmd;
|
|
}
|
|
|
|
/**
|
|
* Render using WKHTMLToImage
|
|
**/
|
|
function renderPageWKHTMLTOIMAGE()
|
|
{
|
|
//escapeshellarg
|
|
|
|
//timeout
|
|
$cmd = 'timeout '.$this->params['timeout'].' '.WKHTMLTOIMAGEPATH;
|
|
|
|
//viewport vp_w und vp_h
|
|
if($this->params['vp_w'])
|
|
$cmd.=' --width '.$this->params['vp_w'];
|
|
if($this->params['vp_h'])
|
|
$cmd.=' --height '.$this->params['vp_h'];
|
|
|
|
//js or no js
|
|
if($this->params['js']=='no')
|
|
$cmd.=' -n';
|
|
|
|
//png or jpg (default)
|
|
if($this->params['type']=='png')
|
|
$cmd.=' -f png';
|
|
|
|
//add url to cmd
|
|
$cmd.=' '.escapeshellarg($this->params['url']);
|
|
|
|
//add storage path to cmd
|
|
$cmd.=' '.escapeshellarg($this->params['file']);
|
|
|
|
$cmd = escapeshellcmd($cmd);
|
|
shell_exec($cmd);
|
|
$this->params['cmd'] = $cmd;
|
|
|
|
$this->postRender();
|
|
|
|
if(DEBUG)
|
|
{
|
|
$fp = fopen('debug.log', 'a');
|
|
fwrite($fp, $cmd."\n");
|
|
fclose($fp);
|
|
}
|
|
return $cmd;
|
|
}
|
|
|
|
|
|
/**
|
|
* Called after a render took place.
|
|
* This method will print the image to the user, then
|
|
* resizes or deletes it
|
|
*/
|
|
function postRender()
|
|
{
|
|
// resize if necessary
|
|
if($this->params['resizewidth'])
|
|
$this->resizeImage($this->params['file']);
|
|
|
|
|
|
//print image to user
|
|
if ($this->params['type'] === 'png') {
|
|
|
|
header('Content-Type: image/png');
|
|
header('Content-Disposition: inline; filename="'.$this->trimToAlphaNumeric($this->params['url']).'.png"');
|
|
$result = imagecreatefrompng($this->params['file']);
|
|
imagepng($result, NULL, 9);
|
|
}
|
|
else {
|
|
header('Content-Type: image/jpeg');
|
|
header('Content-Disposition: inline; filename="'.$this->trimToAlphaNumeric($this->params['url']).'.jpg"');
|
|
$result = imagecreatefromjpeg($this->params['file']);
|
|
imagejpeg($result, NULL, 100);
|
|
}
|
|
|
|
|
|
//if no cache value specified: delete the image
|
|
if(!$this->params['cache']) unlink($this->params['file']);
|
|
}
|
|
|
|
function resizeImage($file)
|
|
{
|
|
list($width_orig, $height_orig) = getimagesize($file);
|
|
|
|
if ($width_orig != $this->params['resizewidth'])
|
|
{
|
|
$ratio_orig = $width_orig/$height_orig;
|
|
$height = $this->params['resizewidth']/$ratio_orig;
|
|
|
|
// resample
|
|
$image_p = imagecreatetruecolor($this->params['resizewidth'], $height);
|
|
if ($this->params['type'] === 'png')
|
|
$image = imagecreatefrompng($file);
|
|
else
|
|
$image = imagecreatefromjpeg($file);
|
|
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $this->params['resizewidth'], $height, $width_orig, $height_orig);
|
|
|
|
if ($this->params['type'] === 'png')
|
|
imagepng($image_p, $file, 9);
|
|
else
|
|
imagejpeg($image_p, $file, 100);
|
|
}
|
|
}
|
|
|
|
function isURLValid($url)
|
|
{
|
|
return filter_var($url, FILTER_VALIDATE_URL);
|
|
}
|
|
|
|
/**
|
|
* https://stackoverflow.com/questions/7684771/how-check-if-file-exists-from-the-url
|
|
*/
|
|
function isURLReachable($url)
|
|
{
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
if(curl_exec($ch) != false){
|
|
//We were able to connect to a webserver, what did it return?
|
|
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
|
|
if($code < 400) //status code updated so redirects will also work
|
|
$status = 0;
|
|
else
|
|
$status = 1;
|
|
curl_close($ch);
|
|
return $status;
|
|
} else {
|
|
//We were not able to connect to any webserver so we didn't get a status code
|
|
//to compare against. There must be a problem with the domain that was supplied.
|
|
curl_close($ch);
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
function trimToAlphaNumeric($string)
|
|
{
|
|
return preg_replace("/[^A-Za-z0-9 ]/", '', $string);
|
|
}
|
|
|
|
function isBase64($data)
|
|
{
|
|
if (base64_encode(base64_decode($data, true)) === $data)
|
|
return true;
|
|
return false;
|
|
}
|
|
}
|