1<?php 2// Icinga PDF Export | (c) 2018 Icinga GmbH | GPLv2 3 4namespace Icinga\Module\Pdfexport; 5 6use Icinga\File\Storage\TemporaryLocalFileStorage; 7 8class HeadlessChrome 9{ 10 /** @var string Path to the Chrome binary */ 11 protected $binary; 12 13 /** @var string Target Url */ 14 protected $url; 15 16 /** @var TemporaryLocalFileStorage */ 17 protected $fileStorage; 18 19 public function __construct() 20 { 21 $this->fileStorage = new TemporaryLocalFileStorage(); 22 } 23 24 /** 25 * Get the path to the Chrome binary 26 * 27 * @return string 28 */ 29 public function getBinary() 30 { 31 return $this->binary; 32 } 33 34 /** 35 * Set the path to the Chrome binary 36 * 37 * @param string $binary 38 * 39 * @return $this 40 */ 41 public function setBinary($binary) 42 { 43 $this->binary = $binary; 44 45 return $this; 46 } 47 48 /** 49 * Render the given argument name-value pairs as shell-escaped string 50 * 51 * @param array $arguments 52 * 53 * @return string 54 */ 55 public static function renderArgumentList(array $arguments) 56 { 57 $list = []; 58 59 foreach ($arguments as $name => $value) { 60 if ($value !== null) { 61 $value = escapeshellarg($value); 62 63 if (! is_int($name)) { 64 if (substr($name, -1) === '=') { 65 $glue = ''; 66 } else { 67 $glue = ' '; 68 } 69 70 $list[] = escapeshellarg($name) . $glue . $value; 71 } else { 72 $list[] = $value; 73 } 74 } else { 75 $list[] = escapeshellarg($name); 76 } 77 } 78 79 return implode(' ', $list); 80 } 81 82 /** 83 * Get the target Url 84 * 85 * @return string 86 */ 87 public function getUrl() 88 { 89 return $this->url; 90 } 91 92 /** 93 * Set the target Url 94 * 95 * @param string $url 96 * 97 * @return $this 98 */ 99 public function setUrl($url) 100 { 101 $this->url = $url; 102 103 return $this; 104 } 105 106 /** 107 * Use the given HTML string as input 108 * 109 * @param string $html 110 * @param bool $asFile 111 * 112 * @return $this 113 */ 114 public function fromHtml($html, $asFile = true) 115 { 116 if ($asFile) { 117 $path = uniqid('icingaweb2-pdfexport-') . '.html'; 118 119 $this->fileStorage->create($path, $html); 120 121 $path = $this->fileStorage->resolvePath($path, true); 122 123 $this->setUrl("file://$path"); 124 } else { 125 $this->setUrl('data:text/html,' . rawurlencode($html)); 126 } 127 128 return $this; 129 } 130 131 /** 132 * Export to PDF 133 * 134 * @param $filename 135 * 136 * @return string 137 */ 138 public function toPdf($filename) 139 { 140 $path = uniqid('icingaweb2-pdfexport-') . $filename; 141 142 $this->fileStorage->create($path, ''); 143 144 $path = $this->fileStorage->resolvePath($path, true); 145 146 $arguments = [ 147 '--headless', 148 '--disable-gpu', 149 '--no-sandbox', 150 '--print-to-pdf=' => $path, 151 $this->getUrl() 152 ]; 153 154 $command = new ShellCommand( 155 escapeshellarg($this->getBinary()) . ' ' . static::renderArgumentList($arguments), 156 false 157 ); 158 159 $command->execute(); 160 161 return $path; 162 } 163 164 /** 165 * Get the major version number of Chrome or false on failure 166 * 167 * @return bool|int 168 */ 169 public function getVersion() 170 { 171 $command = new ShellCommand( 172 escapeshellarg($this->getBinary()) . ' ' . static::renderArgumentList(['--version']), 173 false 174 ); 175 176 $output = $command->execute(); 177 178 if ($command->getExitCode() !== 0) { 179 return false; 180 } 181 182 $parts = explode(' ', trim($output->stdout)); 183 184 $version = (int) array_pop($parts); 185 186 return $version; 187 } 188} 189