vendor/dompdf/dompdf/src/Image/Cache.php line 58

Open in your IDE?
  1. <?php
  2. /**
  3.  * @package dompdf
  4.  * @link    http://dompdf.github.com/
  5.  * @author  Benj Carson <benjcarson@digitaljunkies.ca>
  6.  * @author  Helmut Tischer <htischer@weihenstephan.org>
  7.  * @author  Fabien Ménager <fabien.menager@gmail.com>
  8.  * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
  9.  */
  10. namespace Dompdf\Image;
  11. use Dompdf\Options;
  12. use Dompdf\Helpers;
  13. use Dompdf\Exception\ImageException;
  14. /**
  15.  * Static class that resolves image urls and downloads and caches
  16.  * remote images if required.
  17.  *
  18.  * @package dompdf
  19.  */
  20. class Cache
  21. {
  22.     /**
  23.      * Array of downloaded images.  Cached so that identical images are
  24.      * not needlessly downloaded.
  25.      *
  26.      * @var array
  27.      */
  28.     protected static $_cache = [];
  29.     /**
  30.      * @var array
  31.      */
  32.     protected static $tempImages = [];
  33.     /**
  34.      * The url to the "broken image" used when images can't be loaded
  35.      *
  36.      * @var string
  37.      */
  38.     public static $broken_image "data:image/svg+xml;charset=utf8,%3C?xml version='1.0'?%3E%3Csvg width='64' height='64' xmlns='http://www.w3.org/2000/svg'%3E%3Cg%3E%3Crect stroke='%23666666' id='svg_1' height='60.499994' width='60.166667' y='1.666669' x='1.999998' stroke-width='1.5' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_3' y2='59.333253' x2='59.749916' y1='4.333415' x1='4.250079' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3Cline stroke-linecap='null' stroke-linejoin='null' id='svg_4' y2='59.999665' x2='4.062838' y1='3.750342' x1='60.062164' stroke-width='1.5' stroke='%23999999' fill='none'/%3E%3C/g%3E%3C/svg%3E";
  39.     public static $error_message "Image not found or type unknown";
  40.     
  41.     /**
  42.      * Resolve and fetch an image for use.
  43.      *
  44.      * @param string $url       The url of the image
  45.      * @param string $protocol  Default protocol if none specified in $url
  46.      * @param string $host      Default host if none specified in $url
  47.      * @param string $base_path Default path if none specified in $url
  48.      * @param Options $options  An instance of Dompdf\Options
  49.      *
  50.      * @return array            An array with three elements: The local path to the image, the image
  51.      *                          extension, and an error message if the image could not be cached
  52.      */
  53.     static function resolve_url($url$protocol$host$base_pathOptions $options)
  54.     {
  55.         $tempfile null;
  56.         $resolved_url null;
  57.         $type null;
  58.         $message null;
  59.         
  60.         try {
  61.             $full_url Helpers::build_url($protocol$host$base_path$url);
  62.             if ($full_url === null) {
  63.                 throw new ImageException("Unable to parse image URL $url."E_WARNING);
  64.             }
  65.             $parsed_url Helpers::explode_url($full_url);
  66.             $protocol strtolower($parsed_url["protocol"]);
  67.             $is_data_uri strpos($protocol"data:") === 0;
  68.             
  69.             if (!$is_data_uri) {
  70.                 $allowed_protocols $options->getAllowedProtocols();
  71.                 if (!array_key_exists($protocol$allowed_protocols)) {
  72.                     throw new ImageException("Permission denied on $url. The communication protocol is not supported."E_WARNING);
  73.                 }
  74.                 foreach ($allowed_protocols[$protocol]["rules"] as $rule) {
  75.                     [$result$message] = $rule($full_url);
  76.                     if (!$result) {
  77.                         throw new ImageException("Error loading $url$message"E_WARNING);
  78.                     }
  79.                 }
  80.             }
  81.             if ($protocol === "file://") {
  82.                 $resolved_url $full_url;
  83.             } elseif (isset(self::$_cache[$full_url])) {
  84.                 $resolved_url self::$_cache[$full_url];
  85.             } else {
  86.                 $tmp_dir $options->getTempDir();
  87.                 if (($resolved_url = @tempnam($tmp_dir"ca_dompdf_img_")) === false) {
  88.                     throw new ImageException("Unable to create temporary image in " $tmp_dirE_WARNING);
  89.                 }
  90.                 $tempfile $resolved_url;
  91.                 $image null;
  92.                 if ($is_data_uri) {
  93.                     if (($parsed_data_uri Helpers::parse_data_uri($url)) !== false) {
  94.                         $image $parsed_data_uri["data"];
  95.                     }
  96.                 } else {
  97.                     list($image$http_response_header) = Helpers::getFileContent($full_url$options->getHttpContext());
  98.                 }
  99.                 // Image not found or invalid
  100.                 if ($image === null) {
  101.                     $msg = ($is_data_uri "Data-URI could not be parsed" "Image not found");
  102.                     throw new ImageException($msgE_WARNING);
  103.                 }
  104.                 if (@file_put_contents($resolved_url$image) === false) {
  105.                     throw new ImageException("Unable to create temporary image in " $tmp_dirE_WARNING);
  106.                 }
  107.                 self::$_cache[$full_url] = $resolved_url;
  108.             }
  109.             // Check if the local file is readable
  110.             if (!is_readable($resolved_url) || !filesize($resolved_url)) {
  111.                 throw new ImageException("Image not readable or empty"E_WARNING);
  112.             }
  113.             list($width$height$type) = Helpers::dompdf_getimagesize($resolved_url$options->getHttpContext());
  114.             if (($width && $height && in_array($type, ["gif""png""jpeg""bmp""svg","webp"], true)) === false) {
  115.                 throw new ImageException("Image type unknown"E_WARNING);
  116.             }
  117.             if ($type === "svg") {
  118.                 $parser xml_parser_create("utf-8");
  119.                 xml_parser_set_option($parserXML_OPTION_CASE_FOLDINGfalse);
  120.                 xml_set_element_handler(
  121.                     $parser,
  122.                     function ($parser$name$attributes) use ($options$parsed_url$full_url) {
  123.                         if ($name === "image") {
  124.                             $attributes array_change_key_case($attributesCASE_LOWER);
  125.                             $url $attributes["xlink:href"] ?? $attributes["href"];
  126.                             if (!empty($url)) {
  127.                                 $inner_full_url Helpers::build_url($parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $url);
  128.                                 if ($inner_full_url === $full_url) {
  129.                                     throw new ImageException("SVG self-reference is not allowed"E_WARNING);
  130.                                 }
  131.                                 [$resolved_url$type$message] = self::resolve_url($url$parsed_url["protocol"], $parsed_url["host"], $parsed_url["path"], $options);
  132.                                 if (!empty($message)) {
  133.                                     throw new ImageException("This SVG document references a restricted resource. $message"E_WARNING);
  134.                                 }
  135.                             }
  136.                         }
  137.                     },
  138.                     false
  139.                 );
  140.         
  141.                 if (($fp fopen($resolved_url"r")) !== false) {
  142.                     while ($line fread($fp8192)) {
  143.                         xml_parse($parser$linefalse);
  144.                     }
  145.                     fclose($fp);
  146.                 }
  147.                 xml_parser_free($parser);
  148.             }
  149.         } catch (ImageException $e) {
  150.             if ($tempfile) {
  151.                 unlink($tempfile);
  152.             }
  153.             $resolved_url self::$broken_image;
  154.             list($width$height$type) = Helpers::dompdf_getimagesize($resolved_url$options->getHttpContext());
  155.             $message self::$error_message;
  156.             Helpers::record_warnings($e->getCode(), $e->getMessage() . " \n $url"$e->getFile(), $e->getLine());
  157.             self::$_cache[$full_url] = $resolved_url;
  158.         }
  159.         return [$resolved_url$type$message];
  160.     }
  161.     /**
  162.      * Register a temp file for the given original image file.
  163.      *
  164.      * @param string $filePath The path of the original image.
  165.      * @param string $tempPath The path of the temp file to register.
  166.      * @param string $key      An optional key to register the temp file at.
  167.      */
  168.     static function addTempImage(string $filePathstring $tempPathstring $key "default"): void
  169.     {
  170.         if (!isset(self::$tempImages[$filePath])) {
  171.             self::$tempImages[$filePath] = [];
  172.         }
  173.         self::$tempImages[$filePath][$key] = $tempPath;
  174.     }
  175.     /**
  176.      * Get the path of a temp file registered for the given original image file.
  177.      *
  178.      * @param string $filePath The path of the original image.
  179.      * @param string $key      The key the temp file is registered at.
  180.      */
  181.     static function getTempImage(string $filePathstring $key "default"): ?string
  182.     {
  183.         return self::$tempImages[$filePath][$key] ?? null;
  184.     }
  185.     /**
  186.      * Unlink all cached images (i.e. temporary images either downloaded
  187.      * or converted) except for the bundled "broken image"
  188.      */
  189.     static function clear(bool $debugPng false)
  190.     {
  191.         foreach (self::$_cache as $file) {
  192.             if ($file === self::$broken_image) {
  193.                 continue;
  194.             }
  195.             if ($debugPng) {
  196.                 print "[clear unlink $file]";
  197.             }
  198.             if (file_exists($file)) {
  199.                 unlink($file);
  200.             }
  201.         }
  202.         foreach (self::$tempImages as $versions) {
  203.             foreach ($versions as $file) {
  204.                 if ($file === self::$broken_image) {
  205.                     continue;
  206.                 }
  207.                 if ($debugPng) {
  208.                     print "[unlink temp image $file]";
  209.                 }
  210.                 if (file_exists($file)) {
  211.                     unlink($file);
  212.                 }
  213.             }
  214.         }
  215.         self::$_cache = [];
  216.         self::$tempImages = [];
  217.     }
  218.     static function detect_type($file$context null)
  219.     {
  220.         list(, , $type) = Helpers::dompdf_getimagesize($file$context);
  221.         return $type;
  222.     }
  223.     static function is_broken($url)
  224.     {
  225.         return $url === self::$broken_image;
  226.     }
  227. }
  228. if (file_exists(realpath(__DIR__ "/../../lib/res/broken_image.svg"))) {
  229.     Cache::$broken_image realpath(__DIR__ "/../../lib/res/broken_image.svg");
  230. }