vendor/liip/imagine-bundle/Imagine/Filter/FilterManager.php line 154

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the `liip/LiipImagineBundle` project.
  4.  *
  5.  * (c) https://github.com/liip/LiipImagineBundle/graphs/contributors
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE.md
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Liip\ImagineBundle\Imagine\Filter;
  11. use Imagine\Image\ImageInterface;
  12. use Imagine\Image\ImagineInterface;
  13. use Liip\ImagineBundle\Binary\BinaryInterface;
  14. use Liip\ImagineBundle\Binary\FileBinaryInterface;
  15. use Liip\ImagineBundle\Binary\MimeTypeGuesserInterface;
  16. use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface;
  17. use Liip\ImagineBundle\Imagine\Filter\PostProcessor\PostProcessorInterface;
  18. use Liip\ImagineBundle\Model\Binary;
  19. class FilterManager
  20. {
  21.     /**
  22.      * @var FilterConfiguration
  23.      */
  24.     protected $filterConfig;
  25.     /**
  26.      * @var ImagineInterface
  27.      */
  28.     protected $imagine;
  29.     /**
  30.      * @var MimeTypeGuesserInterface
  31.      */
  32.     protected $mimeTypeGuesser;
  33.     /**
  34.      * @var LoaderInterface[]
  35.      */
  36.     protected $loaders = [];
  37.     /**
  38.      * @var PostProcessorInterface[]
  39.      */
  40.     protected $postProcessors = [];
  41.     /**
  42.      * @param FilterConfiguration      $filterConfig
  43.      * @param ImagineInterface         $imagine
  44.      * @param MimeTypeGuesserInterface $mimeTypeGuesser
  45.      */
  46.     public function __construct(FilterConfiguration $filterConfigImagineInterface $imagineMimeTypeGuesserInterface $mimeTypeGuesser)
  47.     {
  48.         $this->filterConfig $filterConfig;
  49.         $this->imagine $imagine;
  50.         $this->mimeTypeGuesser $mimeTypeGuesser;
  51.     }
  52.     /**
  53.      * Adds a loader to handle the given filter.
  54.      *
  55.      * @param string          $filter
  56.      * @param LoaderInterface $loader
  57.      */
  58.     public function addLoader(string $filterLoaderInterface $loader): void
  59.     {
  60.         $this->loaders[$filter] = $loader;
  61.     }
  62.     /**
  63.      * Adds a post-processor to handle binaries.
  64.      *
  65.      * @param string                 $name
  66.      * @param PostProcessorInterface $postProcessor
  67.      */
  68.     public function addPostProcessor(string $namePostProcessorInterface $postProcessor): void
  69.     {
  70.         $this->postProcessors[$name] = $postProcessor;
  71.     }
  72.     /**
  73.      * @return FilterConfiguration
  74.      */
  75.     public function getFilterConfiguration(): FilterConfiguration
  76.     {
  77.         return $this->filterConfig;
  78.     }
  79.     /**
  80.      * @param BinaryInterface $binary
  81.      * @param array           $config
  82.      *
  83.      * @throws \InvalidArgumentException
  84.      *
  85.      * @return BinaryInterface
  86.      */
  87.     public function apply(BinaryInterface $binary, array $config): BinaryInterface
  88.     {
  89.         $config += [
  90.             'quality' => 100,
  91.             'animated' => false,
  92.         ];
  93.         return $this->applyPostProcessors($this->applyFilters($binary$config), $config);
  94.     }
  95.     /**
  96.      * @param BinaryInterface $binary
  97.      * @param array           $config
  98.      *
  99.      * @return BinaryInterface
  100.      */
  101.     public function applyFilters(BinaryInterface $binary, array $config): BinaryInterface
  102.     {
  103.         if ($binary instanceof FileBinaryInterface) {
  104.             $image $this->imagine->open($binary->getPath());
  105.         } else {
  106.             $image $this->imagine->load($binary->getContent());
  107.         }
  108.         foreach ($this->sanitizeFilters($config['filters'] ?? []) as $name => $options) {
  109.             $prior $image;
  110.             $image $this->loaders[$name]->load($image$options);
  111.             if ($prior !== $image) {
  112.                 $this->destroyImage($prior);
  113.             }
  114.         }
  115.         return $this->exportConfiguredImageBinary($binary$image$config);
  116.     }
  117.     /**
  118.      * Apply the provided filter set on the given binary.
  119.      *
  120.      * @param BinaryInterface $binary
  121.      * @param string          $filter
  122.      * @param array           $runtimeConfig
  123.      *
  124.      * @throws \InvalidArgumentException
  125.      *
  126.      * @return BinaryInterface
  127.      */
  128.     public function applyFilter(BinaryInterface $binary$filter, array $runtimeConfig = [])
  129.     {
  130.         $config array_replace_recursive(
  131.             $this->getFilterConfiguration()->get($filter),
  132.             $runtimeConfig
  133.         );
  134.         return $this->apply($binary$config);
  135.     }
  136.     /**
  137.      * @param BinaryInterface $binary
  138.      * @param array           $config
  139.      *
  140.      * @throws \InvalidArgumentException
  141.      *
  142.      * @return BinaryInterface
  143.      */
  144.     public function applyPostProcessors(BinaryInterface $binary, array $config): BinaryInterface
  145.     {
  146.         foreach ($this->sanitizePostProcessors($config['post_processors'] ?? []) as $name => $options) {
  147.             $binary $this->postProcessors[$name]->process($binary$options);
  148.         }
  149.         return $binary;
  150.     }
  151.     /**
  152.      * @param BinaryInterface $binary
  153.      * @param ImageInterface  $image
  154.      * @param array           $config
  155.      *
  156.      * @return BinaryInterface
  157.      */
  158.     private function exportConfiguredImageBinary(BinaryInterface $binaryImageInterface $image, array $config): BinaryInterface
  159.     {
  160.         $options = [
  161.             'quality' => $config['quality'],
  162.         ];
  163.         if (isset($config['jpeg_quality'])) {
  164.             $options['jpeg_quality'] = $config['jpeg_quality'];
  165.         }
  166.         if (isset($config['png_compression_level'])) {
  167.             $options['png_compression_level'] = $config['png_compression_level'];
  168.         }
  169.         if (isset($config['png_compression_filter'])) {
  170.             $options['png_compression_filter'] = $config['png_compression_filter'];
  171.         }
  172.         if ('gif' === $binary->getFormat() && $config['animated']) {
  173.             $options['animated'] = $config['animated'];
  174.         }
  175.         $filteredFormat $config['format'] ?? $binary->getFormat();
  176.         $filteredString $image->get($filteredFormat$options);
  177.         $this->destroyImage($image);
  178.         return new Binary(
  179.             $filteredString,
  180.             $filteredFormat === $binary->getFormat() ? $binary->getMimeType() : $this->mimeTypeGuesser->guess($filteredString),
  181.             $filteredFormat
  182.         );
  183.     }
  184.     /**
  185.      * @param array $filters
  186.      *
  187.      * @return array
  188.      */
  189.     private function sanitizeFilters(array $filters): array
  190.     {
  191.         $sanitized array_filter($filters, function (string $name): bool {
  192.             return isset($this->loaders[$name]);
  193.         }, ARRAY_FILTER_USE_KEY);
  194.         if (\count($filters) !== \count($sanitized)) {
  195.             throw new \InvalidArgumentException(sprintf('Could not find filter(s): %s'implode(', 'array_map(function (string $name): string {
  196.                 return sprintf('"%s"'$name);
  197.             }, array_diff(array_keys($filters), array_keys($sanitized))))));
  198.         }
  199.         return $sanitized;
  200.     }
  201.     /**
  202.      * @param array $processors
  203.      *
  204.      * @return array
  205.      */
  206.     private function sanitizePostProcessors(array $processors): array
  207.     {
  208.         $sanitized array_filter($processors, function (string $name): bool {
  209.             return isset($this->postProcessors[$name]);
  210.         }, ARRAY_FILTER_USE_KEY);
  211.         if (\count($processors) !== \count($sanitized)) {
  212.             throw new \InvalidArgumentException(sprintf('Could not find post processor(s): %s'implode(', 'array_map(function (string $name): string {
  213.                 return sprintf('"%s"'$name);
  214.             }, array_diff(array_keys($processors), array_keys($sanitized))))));
  215.         }
  216.         return $sanitized;
  217.     }
  218.     /**
  219.      * We are done with the image object so we can destruct the this because imagick keeps consuming memory if we don't.
  220.      * See https://github.com/liip/LiipImagineBundle/pull/682
  221.      *
  222.      * @param ImageInterface $image
  223.      */
  224.     private function destroyImage(ImageInterface $image): void
  225.     {
  226.         if (method_exists($image'__destruct')) {
  227.             $image->__destruct();
  228.         }
  229.     }
  230. }