vendor/contao/image/src/DeferredResizer.php line 53

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4.  * This file is part of Contao.
  5.  *
  6.  * (c) Leo Feyer
  7.  *
  8.  * @license LGPL-3.0-or-later
  9.  */
  10. namespace Contao\Image;
  11. use Contao\Image\Exception\InvalidArgumentException;
  12. use Contao\Image\Exception\RuntimeException;
  13. use Contao\Image\Metadata\MetadataReaderWriter;
  14. use Imagine\Image\Box;
  15. use Imagine\Image\ImagineInterface;
  16. use Imagine\Image\Point;
  17. use Symfony\Component\Filesystem\Filesystem;
  18. use Symfony\Component\Filesystem\Path;
  19. /**
  20.  * @method __construct(string $cacheDir, string $secret, ResizeCalculator $calculator = null, Filesystem $filesystem = null, DeferredImageStorageInterface $storage = null, MetadataReaderWriter $metadataReaderWriter = null)
  21.  */
  22. class DeferredResizer extends Resizer implements DeferredResizerInterface
  23. {
  24.     /**
  25.      * @var DeferredImageStorageInterface
  26.      *
  27.      * @internal
  28.      */
  29.     private $storage;
  30.     /**
  31.      * @param string                             $cacheDir
  32.      * @param string                             $secret
  33.      * @param ResizeCalculator|null              $calculator
  34.      * @param Filesystem|null                    $filesystem
  35.      * @param DeferredImageStorageInterface|null $storage
  36.      * @param MetadataReaderWriter|null          $metadataReaderWriter
  37.      */
  38.     public function __construct(string $cacheDir/*, string $secret, ResizeCalculator $calculator = null, Filesystem $filesystem = null, DeferredImageStorageInterface $storage = null, MetadataReaderWriter $metadataReaderWriter = null*/)
  39.     {
  40.         if (\func_num_args() > && \is_string(func_get_arg(1))) {
  41.             $secret func_get_arg(1);
  42.             $calculator \func_num_args() > func_get_arg(2) : null;
  43.             $filesystem \func_num_args() > func_get_arg(3) : null;
  44.             $storage \func_num_args() > func_get_arg(4) : null;
  45.             $metadataReaderWriter \func_num_args() > func_get_arg(5) : null;
  46.         } else {
  47.             trigger_deprecation('contao/image''1.2''Not passing a secret to "%s()" has been deprecated and will no longer work in version 2.0.'__METHOD__);
  48.             $secret null;
  49.             $calculator \func_num_args() > func_get_arg(1) : null;
  50.             $filesystem \func_num_args() > func_get_arg(2) : null;
  51.             $storage \func_num_args() > func_get_arg(3) : null;
  52.             $metadataReaderWriter \func_num_args() > func_get_arg(4) : null;
  53.         }
  54.         if (null === $storage) {
  55.             $storage = new DeferredImageStorageFilesystem($cacheDir);
  56.         }
  57.         if (!$storage instanceof DeferredImageStorageInterface) {
  58.             throw new \TypeError(sprintf('%s(): Argument #5 ($storage) must be of type DeferredImageStorageInterface|null, %s given'__METHOD__get_debug_type($storage)));
  59.         }
  60.         if (null === $secret) {
  61.             parent::__construct($cacheDir$calculator$filesystem$metadataReaderWriter);
  62.         } else {
  63.             parent::__construct($cacheDir$secret$calculator$filesystem$metadataReaderWriter);
  64.         }
  65.         $this->storage $storage;
  66.     }
  67.     /**
  68.      * {@inheritdoc}
  69.      */
  70.     public function getDeferredImage(string $targetPathImagineInterface $imagine): ?DeferredImageInterface
  71.     {
  72.         if (Path::isAbsolute($targetPath)) {
  73.             if (!Path::isBasePath($this->cacheDir$targetPath)) {
  74.                 return null;
  75.             }
  76.             $targetPath Path::makeRelative($targetPath$this->cacheDir);
  77.         }
  78.         if (!$this->storage->has($targetPath)) {
  79.             return null;
  80.         }
  81.         try {
  82.             $config $this->storage->get($targetPath);
  83.         } catch (\Throwable $exception) {
  84.             // Ignore storage failure
  85.             return null;
  86.         }
  87.         return new DeferredImage(
  88.             Path::join($this->cacheDir$targetPath),
  89.             $imagine,
  90.             new ImageDimensions(
  91.                 new Box(
  92.                     $config['coordinates']['crop']['width'],
  93.                     $config['coordinates']['crop']['height']
  94.                 )
  95.             )
  96.         );
  97.     }
  98.     /**
  99.      * {@inheritdoc}
  100.      */
  101.     public function resizeDeferredImage(DeferredImageInterface $imagebool $blocking true): ?ImageInterface
  102.     {
  103.         if (!Path::isBasePath($this->cacheDir$image->getPath())) {
  104.             throw new InvalidArgumentException(sprintf('Path "%s" is not inside cache directory "%s"'$image->getPath(), $this->cacheDir));
  105.         }
  106.         $targetPath Path::makeRelative($image->getPath(), $this->cacheDir);
  107.         try {
  108.             $config $this->storage->getLocked($targetPath$blocking);
  109.         } catch (\Throwable $exception) {
  110.             // Getting the lock might fail if the image was already generated
  111.             if ($this->filesystem->exists($image->getPath())) {
  112.                 return $blocking ? new Image($image->getPath(), $image->getImagine(), $this->filesystem) : null;
  113.             }
  114.             throw $exception;
  115.         }
  116.         if (null === $config) {
  117.             if ($blocking) {
  118.                 throw new RuntimeException(sprintf('Unable to acquire lock for "%s"'$targetPath));
  119.             }
  120.             return null;
  121.         }
  122.         try {
  123.             $resizedImage $this->executeDeferredResize($targetPath$config$image->getImagine());
  124.             $this->storage->delete($targetPath);
  125.         } catch (\Throwable $exception) {
  126.             $this->storage->releaseLock($targetPath);
  127.             throw $exception;
  128.         }
  129.         return $resizedImage;
  130.     }
  131.     /**
  132.      * {@inheritdoc}
  133.      */
  134.     protected function processResize(ImageInterface $imageResizeConfiguration $configResizeOptions $options): ImageInterface
  135.     {
  136.         // Resize the source image if it is deferred
  137.         if ($image instanceof DeferredImageInterface) {
  138.             $image $this->resizeDeferredImage($image);
  139.         }
  140.         return parent::processResize($image$config$options);
  141.     }
  142.     /**
  143.      * {@inheritdoc}
  144.      */
  145.     protected function executeResize(ImageInterface $imageResizeCoordinates $coordinatesstring $pathResizeOptions $options): ImageInterface
  146.     {
  147.         if (null !== $options->getTargetPath() || $options->getBypassCache()) {
  148.             return parent::executeResize($image$coordinates$path$options);
  149.         }
  150.         $this->storeResizeData($image->getPath(), $path$coordinates$options);
  151.         return new DeferredImage($path$image->getImagine(), new ImageDimensions($coordinates->getCropSize()));
  152.     }
  153.     private function storeResizeData(string $sourcePathstring $targetPathResizeCoordinates $coordinatesResizeOptions $options): void
  154.     {
  155.         $targetPath Path::makeRelative($targetPath$this->cacheDir);
  156.         if ($this->storage->has($targetPath)) {
  157.             return;
  158.         }
  159.         $this->storage->set($targetPath, [
  160.             'path' => Path::makeRelative($sourcePath$this->cacheDir),
  161.             'coordinates' => [
  162.                 'size' => [
  163.                     'width' => $coordinates->getSize()->getWidth(),
  164.                     'height' => $coordinates->getSize()->getHeight(),
  165.                 ],
  166.                 'crop' => [
  167.                     'x' => $coordinates->getCropStart()->getX(),
  168.                     'y' => $coordinates->getCropStart()->getY(),
  169.                     'width' => $coordinates->getCropSize()->getWidth(),
  170.                     'height' => $coordinates->getCropSize()->getHeight(),
  171.                 ],
  172.             ],
  173.             'options' => [
  174.                 'imagine_options' => $options->getImagineOptions(),
  175.                 'preserve_copyright' => $options->getPreserveCopyrightMetadata(),
  176.             ],
  177.         ]);
  178.     }
  179.     private function executeDeferredResize(string $targetPath, array $configImagineInterface $imagine): ImageInterface
  180.     {
  181.         $coordinates = new ResizeCoordinates(
  182.             new Box($config['coordinates']['size']['width'], $config['coordinates']['size']['height']),
  183.             new Point($config['coordinates']['crop']['x'], $config['coordinates']['crop']['y']),
  184.             new Box($config['coordinates']['crop']['width'], $config['coordinates']['crop']['height'])
  185.         );
  186.         $options = new ResizeOptions();
  187.         $options->setImagineOptions($config['options']['imagine_options']);
  188.         if (isset($config['options']['preserve_copyright'])) {
  189.             $options->setPreserveCopyrightMetadata($config['options']['preserve_copyright']);
  190.         }
  191.         $path Path::join($this->cacheDir$config['path']);
  192.         return parent::executeResize(
  193.             new Image($path$imagine$this->filesystem),
  194.             $coordinates,
  195.             Path::join($this->cacheDir$targetPath),
  196.             $options
  197.         );
  198.     }
  199. }