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

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 Imagine\Image\Box;
  14. use Imagine\Image\ImagineInterface;
  15. use Imagine\Image\Point;
  16. use Symfony\Component\Filesystem\Filesystem;
  17. use Webmozart\PathUtil\Path;
  18. class DeferredResizer extends Resizer implements DeferredResizerInterface
  19. {
  20.     /**
  21.      * @var DeferredImageStorageInterface
  22.      *
  23.      * @internal
  24.      */
  25.     private $storage;
  26.     /**
  27.      * {@inheritdoc}
  28.      */
  29.     public function __construct(string $cacheDirResizeCalculator $calculator nullFilesystem $filesystem nullDeferredImageStorageInterface $storage null)
  30.     {
  31.         parent::__construct($cacheDir$calculator$filesystem);
  32.         if (null === $storage) {
  33.             $storage = new DeferredImageStorageFilesystem($cacheDir);
  34.         }
  35.         $this->storage $storage;
  36.     }
  37.     /**
  38.      * {@inheritdoc}
  39.      */
  40.     public function getDeferredImage(string $targetPathImagineInterface $imagine): ?DeferredImageInterface
  41.     {
  42.         if (Path::isAbsolute($targetPath)) {
  43.             if (!Path::isBasePath($this->cacheDir$targetPath)) {
  44.                 return null;
  45.             }
  46.             $targetPath Path::makeRelative($targetPath$this->cacheDir);
  47.         }
  48.         if (!$this->storage->has($targetPath)) {
  49.             return null;
  50.         }
  51.         $config $this->storage->get($targetPath);
  52.         return new DeferredImage(
  53.             $this->cacheDir.'/'.$targetPath,
  54.             $imagine,
  55.             new ImageDimensions(
  56.                 new Box(
  57.                     $config['coordinates']['crop']['width'],
  58.                     $config['coordinates']['crop']['height']
  59.                 )
  60.             )
  61.         );
  62.     }
  63.     /**
  64.      * {@inheritdoc}
  65.      */
  66.     public function resizeDeferredImage(DeferredImageInterface $imagebool $blocking true): ?ImageInterface
  67.     {
  68.         if (!Path::isBasePath($this->cacheDir$image->getPath())) {
  69.             throw new InvalidArgumentException(sprintf('Path "%s" is not inside cache directory "%s"'$image->getPath(), $this->cacheDir));
  70.         }
  71.         $targetPath Path::makeRelative($image->getPath(), $this->cacheDir);
  72.         try {
  73.             $config $this->storage->getLocked($targetPath$blocking);
  74.         } catch (\Throwable $exception) {
  75.             // Getting the lock might fail if the image was already generated
  76.             if ($this->filesystem->exists($image->getPath())) {
  77.                 return $blocking ? new Image($image->getPath(), $image->getImagine(), $this->filesystem) : null;
  78.             }
  79.             throw $exception;
  80.         }
  81.         if (null === $config) {
  82.             if ($blocking) {
  83.                 throw new RuntimeException(sprintf('Unable to acquire lock for "%s"'$targetPath));
  84.             }
  85.             return null;
  86.         }
  87.         try {
  88.             $resizedImage $this->executeDeferredResize($targetPath$config$image->getImagine());
  89.             $this->storage->delete($targetPath);
  90.         } catch (\Throwable $exception) {
  91.             $this->storage->releaseLock($targetPath);
  92.             throw $exception;
  93.         }
  94.         return $resizedImage;
  95.     }
  96.     /**
  97.      * {@inheritdoc}
  98.      */
  99.     protected function processResize(ImageInterface $imageResizeConfiguration $configResizeOptions $options): ImageInterface
  100.     {
  101.         // Resize the source image if it is deferred
  102.         if ($image instanceof DeferredImageInterface) {
  103.             $image $this->resizeDeferredImage($image);
  104.         }
  105.         return parent::processResize($image$config$options);
  106.     }
  107.     /**
  108.      * {@inheritdoc}
  109.      */
  110.     protected function executeResize(ImageInterface $imageResizeCoordinates $coordinatesstring $pathResizeOptions $options): ImageInterface
  111.     {
  112.         if (null !== $options->getTargetPath() || $options->getBypassCache()) {
  113.             return parent::executeResize($image$coordinates$path$options);
  114.         }
  115.         $this->storeResizeData($image->getPath(), $path$coordinates$options);
  116.         return new DeferredImage($path$image->getImagine(), new ImageDimensions($coordinates->getCropSize()));
  117.     }
  118.     private function storeResizeData(string $sourcePathstring $targetPathResizeCoordinates $coordinatesResizeOptions $options): void
  119.     {
  120.         $targetPath Path::makeRelative($targetPath$this->cacheDir);
  121.         if ($this->storage->has($targetPath)) {
  122.             return;
  123.         }
  124.         $this->storage->set($targetPath, [
  125.             'path' => Path::makeRelative($sourcePath$this->cacheDir),
  126.             'coordinates' => [
  127.                 'size' => [
  128.                     'width' => $coordinates->getSize()->getWidth(),
  129.                     'height' => $coordinates->getSize()->getHeight(),
  130.                 ],
  131.                 'crop' => [
  132.                     'x' => $coordinates->getCropStart()->getX(),
  133.                     'y' => $coordinates->getCropStart()->getY(),
  134.                     'width' => $coordinates->getCropSize()->getWidth(),
  135.                     'height' => $coordinates->getCropSize()->getHeight(),
  136.                 ],
  137.             ],
  138.             'options' => [
  139.                 'imagine_options' => $options->getImagineOptions(),
  140.             ],
  141.         ]);
  142.     }
  143.     private function executeDeferredResize(string $targetPath, array $configImagineInterface $imagine): ImageInterface
  144.     {
  145.         $coordinates = new ResizeCoordinates(
  146.             new Box($config['coordinates']['size']['width'], $config['coordinates']['size']['height']),
  147.             new Point($config['coordinates']['crop']['x'], $config['coordinates']['crop']['y']),
  148.             new Box($config['coordinates']['crop']['width'], $config['coordinates']['crop']['height'])
  149.         );
  150.         $options = new ResizeOptions();
  151.         $options->setImagineOptions($config['options']['imagine_options']);
  152.         $path Path::canonicalize($this->cacheDir.'/'.$config['path']);
  153.         return parent::executeResize(
  154.             new Image($path$imagine$this->filesystem),
  155.             $coordinates,
  156.             $this->cacheDir.'/'.$targetPath,
  157.             $options
  158.         );
  159.     }
  160. }