vendor/pimcore/pimcore/models/Asset/Thumbnail/ImageThumbnailTrait.php line 111

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Model\Asset\Thumbnail;
  15. use Pimcore\Config as PimcoreConfig;
  16. use Pimcore\Helper\TemporaryFileHelperTrait;
  17. use Pimcore\Model\Asset;
  18. use Pimcore\Model\Asset\Image;
  19. use Pimcore\Model\Asset\Image\Thumbnail\Config;
  20. use Pimcore\Tool;
  21. use Pimcore\Tool\Storage;
  22. use Symfony\Component\Mime\MimeTypes;
  23. trait ImageThumbnailTrait
  24. {
  25.     use TemporaryFileHelperTrait;
  26.     /**
  27.      * @internal
  28.      *
  29.      * @var Asset|null
  30.      */
  31.     protected $asset;
  32.     /**
  33.      * @internal
  34.      *
  35.      * @var Config|null
  36.      */
  37.     protected $config;
  38.     /**
  39.      * @internal
  40.      *
  41.      * @var array
  42.      */
  43.     protected array $pathReference = [];
  44.     /**
  45.      * @internal
  46.      *
  47.      * @var int|null
  48.      */
  49.     protected $width;
  50.     /**
  51.      * @internal
  52.      *
  53.      * @var int|null
  54.      */
  55.     protected $height;
  56.     /**
  57.      * @internal
  58.      *
  59.      * @var int|null
  60.      */
  61.     protected $realWidth;
  62.     /**
  63.      * @internal
  64.      *
  65.      * @var int|null
  66.      */
  67.     protected $realHeight;
  68.     /**
  69.      * @internal
  70.      *
  71.      * @var string
  72.      */
  73.     protected $mimetype;
  74.     /**
  75.      * @internal
  76.      *
  77.      * @var bool
  78.      */
  79.     protected $deferred true;
  80.     private static array $supportedFormats = [];
  81.     /**
  82.      * @return null|resource
  83.      */
  84.     public function getStream()
  85.     {
  86.         $pathReference $this->getPathReference(false);
  87.         if ($pathReference['type'] === 'asset') {
  88.             return $this->asset->getStream();
  89.         } elseif (isset($pathReference['storagePath'])) {
  90.             return Tool\Storage::get('thumbnail')->readStream($pathReference['storagePath']);
  91.         }
  92.         return null;
  93.     }
  94.     public function getPathReference(bool $deferredAllowed false): array
  95.     {
  96.         if (!$deferredAllowed && (($this->pathReference['type'] ?? '') === 'deferred')) {
  97.             $this->pathReference = [];
  98.         }
  99.         if (empty($this->pathReference)) {
  100.             $this->generate($deferredAllowed);
  101.         }
  102.         return $this->pathReference;
  103.     }
  104.     /**
  105.      * @internal
  106.      */
  107.     public function reset()
  108.     {
  109.         $this->pathReference = [];
  110.         $this->width null;
  111.         $this->height null;
  112.         $this->realHeight null;
  113.         $this->realWidth null;
  114.     }
  115.     /**
  116.      * @return int|null
  117.      */
  118.     public function getWidth()
  119.     {
  120.         if (!$this->width) {
  121.             $this->getDimensions();
  122.         }
  123.         return $this->width;
  124.     }
  125.     /**
  126.      * @return int|null
  127.      */
  128.     public function getHeight()
  129.     {
  130.         if (!$this->height) {
  131.             $this->getDimensions();
  132.         }
  133.         return $this->height;
  134.     }
  135.     /**
  136.      * @return int|null
  137.      */
  138.     public function getRealWidth()
  139.     {
  140.         if (!$this->realWidth) {
  141.             $this->getDimensions();
  142.         }
  143.         return $this->realWidth;
  144.     }
  145.     /**
  146.      * @return int|null
  147.      */
  148.     public function getRealHeight()
  149.     {
  150.         if (!$this->realHeight) {
  151.             $this->getDimensions();
  152.         }
  153.         return $this->realHeight;
  154.     }
  155.     /**
  156.      * @internal
  157.      *
  158.      * @return array{width?: int, height?: int}
  159.      */
  160.     public function readDimensionsFromFile(): array
  161.     {
  162.         $dimensions = [];
  163.         $pathReference $this->getPathReference();
  164.         if (in_array($pathReference['type'], ['thumbnail''asset'])) {
  165.             try {
  166.                 $localFile $this->getLocalFile();
  167.                 if (null !== $localFile && isset($pathReference['storagePath']) && $config $this->getConfig()) {
  168.                     $asset $this->getAsset();
  169.                     $filename basename($pathReference['storagePath']);
  170.                     $asset->addThumbnailFileToCache(
  171.                         $localFile,
  172.                         $filename,
  173.                         $config
  174.                     );
  175.                     $thumbnail $asset->getDao()->getCachedThumbnail($config->getName(), $filename);
  176.                     if (isset($thumbnail['width'], $thumbnail['height'])) {
  177.                         $dimensions['width'] = $thumbnail['width'];
  178.                         $dimensions['height'] = $thumbnail['height'];
  179.                     }
  180.                 }
  181.             } catch (\Exception $e) {
  182.                 // noting to do
  183.             }
  184.         }
  185.         return $dimensions;
  186.     }
  187.     /**
  188.      * @return array{width: ?int, height: ?int}
  189.      */
  190.     public function getDimensions()
  191.     {
  192.         if (!$this->width || !$this->height) {
  193.             $config $this->getConfig();
  194.             $asset $this->getAsset();
  195.             $dimensions = [];
  196.             if ($config) {
  197.                 $thumbnail $asset->getDao()->getCachedThumbnail($config->getName(), $this->getFilename());
  198.                 if (isset($thumbnail['width'], $thumbnail['height'])) {
  199.                     $dimensions['width'] = $thumbnail['width'];
  200.                     $dimensions['height'] = $thumbnail['height'];
  201.                 }
  202.             }
  203.             if (empty($dimensions) && $this->exists()) {
  204.                 $dimensions $this->readDimensionsFromFile();
  205.             }
  206.             // try to calculate the final dimensions based on the thumbnail configuration
  207.             if (empty($dimensions) && $config && $asset instanceof Image) {
  208.                 $dimensions $config->getEstimatedDimensions($asset);
  209.             }
  210.             if (empty($dimensions)) {
  211.                 // unable to calculate dimensions -> use fallback
  212.                 // generate the thumbnail and get dimensions from the thumbnail file
  213.                 $dimensions $this->readDimensionsFromFile();
  214.             }
  215.             // realWidth / realHeight is only relevant if using high-res option (retina, ...)
  216.             $this->width $this->realWidth $dimensions['width'] ?? null;
  217.             $this->height $this->realHeight $dimensions['height'] ?? null;
  218.             if ($config && $config->getHighResolution() > 1) {
  219.                 if ($this->width) {
  220.                     $this->width = (int)floor($this->realWidth $config->getHighResolution());
  221.                 }
  222.                 if ($this->height) {
  223.                     $this->height = (int)floor($this->realHeight $config->getHighResolution());
  224.                 }
  225.             }
  226.         }
  227.         return [
  228.             'width' => $this->width,
  229.             'height' => $this->height,
  230.         ];
  231.     }
  232.     /**
  233.      * @return Asset
  234.      */
  235.     public function getAsset()
  236.     {
  237.         return $this->asset;
  238.     }
  239.     /**
  240.      * @return Config|null
  241.      */
  242.     public function getConfig()
  243.     {
  244.         return $this->config;
  245.     }
  246.     /**
  247.      * @return string
  248.      */
  249.     public function getMimeType()
  250.     {
  251.         if (!$this->mimetype) {
  252.             $pathReference $this->getPathReference(true);
  253.             if ($pathReference['type'] === 'data-uri') {
  254.                 $this->mimetype substr($pathReference['src'], 5strpos($pathReference['src'], ';') - 5);
  255.             } else {
  256.                 $fileExt $this->getFileExtension();
  257.                 $mimeTypes MimeTypes::getDefault()->getMimeTypes($fileExt);
  258.                 if (!empty($mimeTypes)) {
  259.                     $this->mimetype $mimeTypes[0];
  260.                 } else {
  261.                     // unknown
  262.                     $this->mimetype 'application/octet-stream';
  263.                 }
  264.             }
  265.         }
  266.         return $this->mimetype;
  267.     }
  268.     /**
  269.      * @return string
  270.      */
  271.     public function getFileExtension()
  272.     {
  273.         return \Pimcore\File::getFileExtension($this->getPath());
  274.     }
  275.     /**
  276.      * TODO: Pimcore 11: remove convertArgsBcLayer method (BC layer)
  277.      *
  278.      * @param array $args
  279.      *
  280.      * @return array
  281.      */
  282.     protected function convertArgsBcLayer($args)
  283.     {
  284.         $totalArgs count($args);
  285.         if ($totalArgs 0) {
  286.             if (is_array($args[0])) {
  287.                 return $args[0];
  288.             }
  289.             trigger_deprecation('pimcore/pimcore''10.6',
  290.                 'Calling the getPath() method with arguments is deprecated since version 10.6 and will be removed in Pimcore 11.
  291.             Use an array with options (e.g. ->getPath(["deferredAllowed" => true, "cacheBuster" => false]))');
  292.             if ($totalArgs === 1) {
  293.                 return [
  294.                     'deferredAllowed' => $args[0],
  295.                 ];
  296.             }
  297.             return [
  298.                 'deferredAllowed' => $args[0],
  299.                 'cacheBuster' => $args[1],
  300.             ];
  301.         }
  302.         return [];
  303.     }
  304.     /**
  305.      * @internal
  306.      *
  307.      * @param array $pathReference
  308.      * @param bool $frontend
  309.      *
  310.      * @return string|null
  311.      */
  312.     protected function convertToWebPath(array $pathReferencebool $frontend): ?string
  313.     {
  314.         $type $pathReference['type'] ?? null;
  315.         $path $pathReference['src'] ?? null;
  316.         if ($frontend) {
  317.             if ($type === 'data-uri') {
  318.                 return $path;
  319.             } elseif ($type === 'deferred') {
  320.                 $prefix \Pimcore::getContainer()->getParameter('pimcore.config')['assets']['frontend_prefixes']['thumbnail_deferred'];
  321.                 $path $prefix urlencode_ignore_slash($path);
  322.             } elseif ($type === 'thumbnail') {
  323.                 $prefix \Pimcore::getContainer()->getParameter('pimcore.config')['assets']['frontend_prefixes']['thumbnail'];
  324.                 $path $prefix urlencode_ignore_slash($path);
  325.             } elseif ($type === 'asset') {
  326.                 $prefix \Pimcore::getContainer()->getParameter('pimcore.config')['assets']['frontend_prefixes']['source'];
  327.                 $path $prefix urlencode_ignore_slash($path);
  328.             } else {
  329.                 $path urlencode_ignore_slash($path);
  330.             }
  331.         }
  332.         return $path;
  333.     }
  334.     /**
  335.      * @return string
  336.      */
  337.     public function getFrontendPath(): string
  338.     {
  339.         $path $this->getPath(['deferredAllowed' => true'frontend' => true]);
  340.         if (!\preg_match('@^(https?|data):@'$path)) {
  341.             $path \Pimcore\Tool::getHostUrl() . $path;
  342.         }
  343.         return $path;
  344.     }
  345.     /**
  346.      * @internal
  347.      *
  348.      * @return string|null
  349.      *
  350.      * @throws \Exception
  351.      */
  352.     public function getLocalFile()
  353.     {
  354.         $stream $this->getStream();
  355.         if (null === $stream) {
  356.             return null;
  357.         }
  358.         $localFile self::getLocalFileFromStream($stream);
  359.         @fclose($stream);
  360.         return $localFile;
  361.     }
  362.     /**
  363.      * @return bool
  364.      */
  365.     public function exists(): bool
  366.     {
  367.         $pathReference $this->getPathReference(true);
  368.         $type $pathReference['type'] ?? '';
  369.         if (
  370.             $type === 'asset' ||
  371.             $type === 'data-uri' ||
  372.             $type === 'thumbnail'
  373.         ) {
  374.             return true;
  375.         } elseif ($type === 'deferred') {
  376.             return false;
  377.         } elseif (isset($pathReference['storagePath'])) {
  378.             // this is probably redundant, but as it doesn't hurt we can keep it
  379.             return $this->existsOnStorage($pathReference);
  380.         }
  381.         return false;
  382.     }
  383.     /**
  384.      * @internal
  385.      *
  386.      * @param array|null $pathReference
  387.      *
  388.      * @return bool
  389.      *
  390.      * @throws \League\Flysystem\FilesystemException
  391.      */
  392.     public function existsOnStorage(?array $pathReference = []): bool
  393.     {
  394.         $pathReference ??= $this->getPathReference(true);
  395.         if (isset($pathReference['storagePath'])) {
  396.             // this is probably redundant, but as it doesn't hurt we can keep it
  397.             return Storage::get('thumbnail')->fileExists($pathReference['storagePath']);
  398.         }
  399.         return false;
  400.     }
  401.     public static function supportsFormat(string $format): bool
  402.     {
  403.         if (!isset(self::$supportedFormats[$format])) {
  404.             self::$supportedFormats[$format] = \Pimcore\Image::getInstance()->supportsFormat($format);
  405.         }
  406.         return self::$supportedFormats[$format];
  407.     }
  408.     public function getFileSize(): ?int
  409.     {
  410.         $thumbnail $this->getAsset()->getDao()->getCachedThumbnail($this->getConfig()->getName(), $this->getFilename());
  411.         if ($thumbnail && $thumbnail['filesize']) {
  412.             return $thumbnail['filesize'];
  413.         }
  414.         $pathReference $this->getPathReference(false);
  415.         if ($pathReference['type'] === 'asset') {
  416.             return $this->asset->getFileSize();
  417.         } elseif (isset($pathReference['storagePath'])) {
  418.             return Tool\Storage::get('thumbnail')->fileSize($pathReference['storagePath']);
  419.         }
  420.         return null;
  421.     }
  422.     private function getFilename(): string
  423.     {
  424.         $pathReference $this->getPathReference(true);
  425.         return basename($pathReference['src']);
  426.     }
  427.     /**
  428.      * Returns path for thumbnail image in a given file format
  429.      *
  430.      * @param string $format
  431.      *
  432.      * @return static
  433.      */
  434.     public function getAsFormat(string $format): static
  435.     {
  436.         $thumb = clone $this;
  437.         $config $thumb->getConfig() ? clone $thumb->getConfig() : new Config();
  438.         $config->setFormat($format);
  439.         $thumb->config $config;
  440.         $thumb->reset();
  441.         return $thumb;
  442.     }
  443.     /**
  444.      * @param string $format
  445.      * @param Asset|null $asset
  446.      *
  447.      * @return bool
  448.      */
  449.     private function checkAllowedFormats(string $format, ?Asset $asset null): bool
  450.     {
  451.         $format strtolower($format);
  452.         if ($asset) {
  453.             if (
  454.                 $format === 'original' ||
  455.                 $format === 'source'
  456.             ) {
  457.                 return true;
  458.             }
  459.             $original strtolower(pathinfo($asset->getRealFullPath(), PATHINFO_EXTENSION));
  460.             if ($format === $original) {
  461.                 return true;
  462.             }
  463.         }
  464.         $assetConfig PimcoreConfig::getSystemConfiguration('assets');
  465.         return in_array(
  466.             $format,
  467.             $assetConfig['thumbnails']['allowed_formats'],
  468.             true
  469.         );
  470.     }
  471.     /**
  472.      * @param float|null $scalingFactor
  473.      *
  474.      * @return bool
  475.      */
  476.     private function checkMaxScalingFactor(?float $scalingFactor null): bool
  477.     {
  478.         if ($scalingFactor === null) {
  479.             return true;
  480.         }
  481.         $assetConfig PimcoreConfig::getSystemConfiguration('assets');
  482.         return $scalingFactor <= $assetConfig['thumbnails']['max_scaling_factor'];
  483.     }
  484. }