diff --git a/Async/ResolveCacheProcessor.php b/Async/ResolveCacheProcessor.php
index ef5a673b2..32158ca6c 100644
--- a/Async/ResolveCacheProcessor.php
+++ b/Async/ResolveCacheProcessor.php
@@ -57,10 +57,9 @@ public function process(Message $psrMessage, Context $psrContext)
$filters = $message->getFilters() ?: array_keys($this->filterManager->getFilterConfiguration()->all());
$path = $message->getPath();
$results = [];
+
foreach ($filters as $filter) {
- if ($message->isForce()) {
- $this->filterService->bustCache($path, $filter);
- }
+ $this->filterService->warmUpCache($path, $filter, null, $message->isForce());
$results[$filter] = $this->filterService->getUrlOfFilteredImage($path, $filter);
}
diff --git a/Command/RemoveCacheCommand.php b/Command/RemoveCacheCommand.php
index 68786a676..17725fdd3 100644
--- a/Command/RemoveCacheCommand.php
+++ b/Command/RemoveCacheCommand.php
@@ -13,6 +13,7 @@
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
+use Liip\ImagineBundle\Service\FilterService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -22,14 +23,21 @@
class RemoveCacheCommand extends Command
{
use CacheCommandTrait;
+
protected static $defaultName = 'liip:imagine:cache:remove';
- public function __construct(CacheManager $cacheManager, FilterManager $filterManager)
+ /**
+ * @var FilterService
+ */
+ private $filterService;
+
+ public function __construct(CacheManager $cacheManager, FilterManager $filterManager, FilterService $filterService)
{
parent::__construct();
$this->cacheManager = $cacheManager;
$this->filterManager = $filterManager;
+ $this->filterService = $filterService;
}
protected function configure(): void
@@ -79,9 +87,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
if (empty($images)) {
$this->cacheManager->remove(null, $filters);
} else {
- foreach ($images as $i) {
- foreach ($filters as $f) {
- $this->runCacheImageRemove($i, $f);
+ foreach ($images as $image) {
+ foreach ($filters as $filter) {
+ $this->runCacheImageRemove($image, $filter);
}
}
}
@@ -99,8 +107,7 @@ private function runCacheImageRemove(string $image, string $filter): void
$this->io->group($image, $filter, 'blue');
- if ($this->cacheManager->isStored($image, $filter)) {
- $this->cacheManager->remove($image, $filter);
+ if ($this->filterService->bustCache($image, $filter)) {
$this->io->status('removed', 'green');
} else {
$this->io->status('skipped', 'yellow');
diff --git a/Command/ResolveCacheCommand.php b/Command/ResolveCacheCommand.php
index 3c2634f7c..d16b3c52e 100644
--- a/Command/ResolveCacheCommand.php
+++ b/Command/ResolveCacheCommand.php
@@ -12,8 +12,8 @@
namespace Liip\ImagineBundle\Command;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
-use Liip\ImagineBundle\Imagine\Data\DataManager;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
+use Liip\ImagineBundle\Service\FilterService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
@@ -23,20 +23,21 @@
class ResolveCacheCommand extends Command
{
use CacheCommandTrait;
+
protected static $defaultName = 'liip:imagine:cache:resolve';
/**
- * @var DataManager
+ * @var FilterService
*/
- private $dataManager;
+ private $filterService;
- public function __construct(DataManager $dataManager, CacheManager $cacheManager, FilterManager $filterManager)
+ public function __construct(CacheManager $cacheManager, FilterManager $filterManager, FilterService $filterService)
{
parent::__construct();
- $this->dataManager = $dataManager;
$this->cacheManager = $cacheManager;
$this->filterManager = $filterManager;
+ $this->filterService = $filterService;
}
protected function configure(): void
@@ -90,9 +91,9 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$forced = $input->getOption('force');
[$images, $filters] = $this->resolveInputFiltersAndPaths($input);
- foreach ($images as $i) {
- foreach ($filters as $f) {
- $this->runCacheImageResolve($i, $f, $forced);
+ foreach ($images as $image) {
+ foreach ($filters as $filter) {
+ $this->runCacheImageResolve($image, $filter, $forced);
}
}
@@ -110,8 +111,7 @@ private function runCacheImageResolve(string $image, string $filter, bool $force
$this->io->group($image, $filter, 'blue');
try {
- if ($forced || !$this->cacheManager->isStored($image, $filter)) {
- $this->cacheManager->store($this->filterManager->applyFilter($this->dataManager->find($filter, $image), $filter), $image, $filter);
+ if ($this->filterService->warmUpCache($image, $filter, null, $forced)) {
$this->io->status('resolved', 'green');
} else {
$this->io->status('cached', 'white');
diff --git a/Message/Handler/WarmupCacheHandler.php b/Message/Handler/WarmupCacheHandler.php
index f64d8142d..fdbc74b7b 100644
--- a/Message/Handler/WarmupCacheHandler.php
+++ b/Message/Handler/WarmupCacheHandler.php
@@ -41,9 +41,7 @@ public function __invoke(WarmupCache $message): void
$path = $message->getPath();
foreach ($filters as $filter) {
- if ($message->isForce()) {
- $this->filterService->bustCache($path, $filter);
- }
+ $this->filterService->warmUpCache($path, $filter, null, $message->isForce());
$this->filterService->getUrlOfFilteredImage($path, $filter);
}
diff --git a/Resources/config/commands.xml b/Resources/config/commands.xml
index 9001349eb..4b8fac819 100644
--- a/Resources/config/commands.xml
+++ b/Resources/config/commands.xml
@@ -8,13 +8,14 @@
+
-
+
diff --git a/Service/FilterService.php b/Service/FilterService.php
index 86149e293..ca8df8ae8 100644
--- a/Service/FilterService.php
+++ b/Service/FilterService.php
@@ -47,7 +47,7 @@ class FilterService
private $webpGenerate;
/**
- * @var array
+ * @var mixed[]
*/
private $webpOptions;
@@ -70,21 +70,44 @@ public function __construct(
/**
* @param string $path
* @param string $filter
+ *
+ * @return bool Returns true if we removed at least one cached image
*/
public function bustCache($path, $filter)
{
- $basePathContainer = new FilterPathContainer($path);
- $filterPathContainers = [$basePathContainer];
-
- if ($this->webpGenerate) {
- $filterPathContainers[] = $basePathContainer->createWebp($this->webpOptions);
- }
+ $busted = false;
- foreach ($filterPathContainers as $filterPathContainer) {
+ foreach ($this->buildFilterPathContainers($path) as $filterPathContainer) {
if ($this->cacheManager->isStored($filterPathContainer->getTarget(), $filter)) {
$this->cacheManager->remove($filterPathContainer->getTarget(), $filter);
+
+ $busted = true;
}
}
+
+ return $busted;
+ }
+
+ /**
+ * @param bool $forced Force warm up cache
+ *
+ * @return bool Returns true if the cache is warmed up
+ */
+ public function warmUpCache(
+ string $path,
+ string $filter,
+ ?string $resolver = null,
+ bool $forced = false
+ ): bool {
+ $warmedUp = false;
+
+ foreach ($this->buildFilterPathContainers($path) as $filterPathContainer) {
+ if ($this->warmUpCacheFilterPathContainer($filterPathContainer, $filter, $resolver, $forced)) {
+ $warmedUp = true;
+ }
+ }
+
+ return $warmedUp;
}
/**
@@ -96,9 +119,11 @@ public function bustCache($path, $filter)
*/
public function getUrlOfFilteredImage($path, $filter, $resolver = null, bool $webpSupported = false)
{
- $basePathContainer = new FilterPathContainer($path);
+ foreach ($this->buildFilterPathContainers($path) as $filterPathContainer) {
+ $this->warmUpCacheFilterPathContainer($filterPathContainer, $filter, $resolver);
+ }
- return $this->getUrlOfFilteredImageByContainer($basePathContainer, $filter, $resolver, $webpSupported);
+ return $this->resolveFilterPathContainer(new FilterPathContainer($path), $filter, $resolver, $webpSupported);
}
/**
@@ -116,42 +141,77 @@ public function getUrlOfFilteredImageWithRuntimeFilters(
bool $webpSupported = false
) {
$runtimePath = $this->cacheManager->getRuntimePath($path, $runtimeFilters);
- $basePathContainer = new FilterPathContainer($path, $runtimePath, [
+ $runtimeOptions = [
'filters' => $runtimeFilters,
- ]);
+ ];
+
+ foreach ($this->buildFilterPathContainers($path, $runtimePath, $runtimeOptions) as $filterPathContainer) {
+ $this->warmUpCacheFilterPathContainer($filterPathContainer, $filter, $resolver);
+ }
+
+ return $this->resolveFilterPathContainer(
+ new FilterPathContainer($path, $runtimePath, $runtimeOptions),
+ $filter,
+ $resolver,
+ $webpSupported
+ );
+ }
+
+ /**
+ * @param mixed[] $options
+ *
+ * @return FilterPathContainer[]
+ */
+ private function buildFilterPathContainers(string $source, string $target = '', array $options = []): array
+ {
+ $basePathContainer = new FilterPathContainer($source, $target, $options);
+ $filterPathContainers = [$basePathContainer];
+
+ if ($this->webpGenerate) {
+ $filterPathContainers[] = $basePathContainer->createWebp($this->webpOptions);
+ }
- return $this->getUrlOfFilteredImageByContainer($basePathContainer, $filter, $resolver, $webpSupported);
+ return $filterPathContainers;
}
- private function getUrlOfFilteredImageByContainer(
- FilterPathContainer $basePathContainer,
+ private function resolveFilterPathContainer(
+ FilterPathContainer $filterPathContainer,
string $filter,
?string $resolver = null,
bool $webpSupported = false
): string {
- $filterPathContainers = [$basePathContainer];
+ $path = $filterPathContainer->getTarget();
- if ($this->webpGenerate) {
- $webpPathContainer = $basePathContainer->createWebp($this->webpOptions);
- $filterPathContainers[] = $webpPathContainer;
+ if ($webpSupported) {
+ $path = $filterPathContainer->createWebp($this->webpOptions)->getTarget();
}
- foreach ($filterPathContainers as $filterPathContainer) {
- if (!$this->cacheManager->isStored($filterPathContainer->getTarget(), $filter, $resolver)) {
- $this->cacheManager->store(
- $this->createFilteredBinary($filterPathContainer, $filter),
- $filterPathContainer->getTarget(),
- $filter,
- $resolver
- );
- }
- }
+ return $this->cacheManager->resolve($path, $filter, $resolver);
+ }
+
+ /**
+ * @param bool $forced Force warm up cache
+ *
+ * @return bool Returns true if the cache is warmed up
+ */
+ private function warmUpCacheFilterPathContainer(
+ FilterPathContainer $filterPathContainer,
+ string $filter,
+ ?string $resolver = null,
+ bool $forced = false
+ ): bool {
+ if ($forced || !$this->cacheManager->isStored($filterPathContainer->getTarget(), $filter, $resolver)) {
+ $this->cacheManager->store(
+ $this->createFilteredBinary($filterPathContainer, $filter),
+ $filterPathContainer->getTarget(),
+ $filter,
+ $resolver
+ );
- if ($webpSupported && isset($webpPathContainer)) {
- return $this->cacheManager->resolve($webpPathContainer->getTarget(), $filter, $resolver);
+ return true;
}
- return $this->cacheManager->resolve($basePathContainer->getTarget(), $filter, $resolver);
+ return false;
}
/**
diff --git a/Tests/Async/ResolveCacheProcessorTest.php b/Tests/Async/ResolveCacheProcessorTest.php
index a880ba514..13365c565 100644
--- a/Tests/Async/ResolveCacheProcessorTest.php
+++ b/Tests/Async/ResolveCacheProcessorTest.php
@@ -294,6 +294,7 @@ public function testShouldCreateOneImagePerRequestedFilter(): void
public function testShouldBurstCacheWhenResolvingForced(): void
{
+ $force = true;
$filterName = 'fooFilter';
$imagePath = 'theImagePath';
@@ -308,8 +309,8 @@ public function testShouldBurstCacheWhenResolvingForced(): void
$filterServiceMock = $this->createFilterServiceMock();
$filterServiceMock
->expects($this->once())
- ->method('bustCache')
- ->with($imagePath, $filterName);
+ ->method('warmUpCache')
+ ->with($imagePath, $filterName, null, $force);
$processor = new ResolveCacheProcessor(
$filterManagerMock,
@@ -318,7 +319,7 @@ public function testShouldBurstCacheWhenResolvingForced(): void
);
$message = new NullMessage();
- $message->setBody(json_encode(['path' => $imagePath, 'force' => true]));
+ $message->setBody(json_encode(['path' => $imagePath, 'force' => $force]));
$result = $processor->process($message, new NullContext());
diff --git a/Tests/Service/FilterPathContainerTest.php b/Tests/Service/FilterPathContainerTest.php
new file mode 100644
index 000000000..5ab6e7ef2
--- /dev/null
+++ b/Tests/Service/FilterPathContainerTest.php
@@ -0,0 +1,114 @@
+assertSame($source, $container->getSource());
+ $this->assertSame($source, $container->getTarget());
+ $this->assertSame($options, $container->getOptions());
+ }
+
+ public function testCustomTarget(): void
+ {
+ $source = 'images/cats.jpeg';
+ $target = 'images/cats.jpeg.webp';
+ $options = [
+ 'format' => 'webp',
+ ];
+
+ $container = new FilterPathContainer($source, $target, $options);
+
+ $this->assertSame($source, $container->getSource());
+ $this->assertSame($target, $container->getTarget());
+ $this->assertSame($options, $container->getOptions());
+ }
+
+ public function provideWebpOptions(): \Traversable
+ {
+ yield 'empty options' => [
+ [],
+ [],
+ [
+ 'format' => 'webp',
+ ],
+ ];
+
+ yield 'custom webp options' => [
+ [],
+ [
+ 'quality' => 100,
+ ],
+ [
+ 'format' => 'webp',
+ 'quality' => 100,
+ ],
+ ];
+
+ yield 'overwrite base options' => [
+ [
+ 'format' => 'jpeg',
+ 'quality' => 80,
+ 'jpeg_quality' => 80,
+ 'post_processors' => [
+ 'jpegoptim' => [
+ 'strip_all' => true,
+ 'max' => 80,
+ 'progressive' => true,
+ ],
+ ],
+ ],
+ [
+ 'quality' => 100,
+ 'post_processors' => [
+ 'my_custom_webp_post_processor' => [],
+ ],
+ ],
+ [
+ 'format' => 'webp',
+ 'quality' => 100,
+ 'post_processors' => [
+ 'my_custom_webp_post_processor' => [],
+ ],
+ 'jpeg_quality' => 80,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider provideWebpOptions
+ */
+ public function testCreateWebp(array $baseOptions, array $webpOptions, array $expectedOptions): void
+ {
+ $source = 'images/cats.jpeg';
+ $target = 'images/cats.jpeg.webp';
+
+ $container = (new FilterPathContainer($source, '', $baseOptions))->createWebp($webpOptions);
+
+ $this->assertSame($source, $container->getSource());
+ $this->assertSame($target, $container->getTarget());
+ $this->assertSame($expectedOptions, $container->getOptions());
+ }
+}
diff --git a/Tests/Service/FilterServiceTest.php b/Tests/Service/FilterServiceTest.php
new file mode 100644
index 000000000..a2983c45f
--- /dev/null
+++ b/Tests/Service/FilterServiceTest.php
@@ -0,0 +1,930 @@
+ [
+ 'size' => [50, 50],
+ ],
+ ];
+ private const WEBP_OPTIONS = [
+ 'quality' => 100,
+ 'post_processors' => [
+ 'my_custom_webp_post_processor' => [],
+ ],
+ ];
+
+ /**
+ * @var MockObject|DataManager
+ */
+ private $dataManager;
+
+ /**
+ * @var MockObject|FilterManager
+ */
+ private $filterManager;
+
+ /**
+ * @var MockObject|CacheManager
+ */
+ private $cacheManager;
+
+ /**
+ * @var MockObject|LoggerInterface
+ */
+ private $logger;
+
+ protected function setUp(): void
+ {
+ parent::setUp();
+
+ $this->dataManager = $this
+ ->getMockBuilder(DataManager::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->filterManager = $this
+ ->getMockBuilder(FilterManager::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->cacheManager = $this
+ ->getMockBuilder(CacheManager::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->logger = $this
+ ->getMockBuilder(LoggerInterface::class)
+ ->getMock();
+ }
+
+ public function provideWebpGeneration(): \Traversable
+ {
+ yield 'WebP generation enabled' => [true];
+
+ yield 'WebP generation disabled' => [false];
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testBustCache(bool $webpGenerate): void
+ {
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE],
+ [self::WEBP_IMAGE]
+ )
+ ->willReturn(true);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('remove')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER],
+ [self::WEBP_IMAGE, self::FILTER]
+ );
+
+ $this->assertTrue($service->bustCache(self::SOURCE_IMAGE, self::FILTER));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testNothingBustCache(bool $webpGenerate): void
+ {
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE],
+ [self::WEBP_IMAGE]
+ )
+ ->willReturn(false);
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('remove');
+
+ $this->assertFalse($service->bustCache(self::SOURCE_IMAGE, self::FILTER));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testWarmsUpCache(bool $webpGenerate): void
+ {
+ $resolver = null;
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [self::WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(true);
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('store');
+
+ $this->dataManager
+ ->expects($this->never())
+ ->method('find');
+
+ $this->filterManager
+ ->expects($this->never())
+ ->method('applyFilter');
+
+ $this->assertFalse($service->warmUpCache(self::SOURCE_IMAGE, self::FILTER, $resolver));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testWarmsUpCacheForced(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $filteredBinary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('isStored');
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('store')
+ ->withConsecutive(
+ [$filteredBinary, self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [$filteredBinary, self::WEBP_IMAGE, self::FILTER, $resolver]
+ );
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, []],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS]
+ )
+ ->willReturn($binary);
+
+ $this->assertTrue($service->warmUpCache(self::SOURCE_IMAGE, self::FILTER, $resolver, true));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testWarmsUpCacheNotStored(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $filteredBinary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [self::WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(false);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('store')
+ ->withConsecutive(
+ [$filteredBinary, self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [$filteredBinary, self::WEBP_IMAGE, self::FILTER, $resolver]
+ );
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, []],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS]
+ )
+ ->willReturn($binary);
+
+ $this->assertTrue($service->warmUpCache(self::SOURCE_IMAGE, self::FILTER, $resolver));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testWarmsUpCacheNotStoredForced(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $filteredBinary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('isStored');
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('store')
+ ->withConsecutive(
+ [$filteredBinary, self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [$filteredBinary, self::WEBP_IMAGE, self::FILTER, $resolver]
+ );
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, []],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS]
+ )
+ ->willReturn($binary);
+
+ $this->assertTrue($service->warmUpCache(self::SOURCE_IMAGE, self::FILTER, $resolver, true));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testWarmsUpCacheNonExistingFilter(bool $webpGenerate): void
+ {
+ $this->expectException(NonExistingFilterException::class);
+
+ $resolver = null;
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $exception = new NonExistingFilterException('Filter not found');
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, []],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS]
+ )
+ ->willThrowException($exception);
+
+ $this->logger
+ ->expects($this->atLeastOnce())
+ ->method('debug')
+ ->with(sprintf(
+ 'Could not locate filter "%s" for path "%s". Message was "%s"',
+ self::FILTER,
+ self::SOURCE_IMAGE,
+ $exception->getMessage()
+ ));
+
+ $service->warmUpCache(self::SOURCE_IMAGE, self::FILTER, $resolver, true);
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImage(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::SOURCE_IMAGE;
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [self::WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(true);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::SOURCE_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('store');
+
+ $this->dataManager
+ ->expects($this->never())
+ ->method('find');
+
+ $this->filterManager
+ ->expects($this->never())
+ ->method('applyFilter');
+
+ $this->assertSame($url, $service->getUrlOfFilteredImage(self::SOURCE_IMAGE, self::FILTER, $resolver));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageWebpSupported(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::WEBP_IMAGE;
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [self::WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(true);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::WEBP_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('store');
+
+ $this->dataManager
+ ->expects($this->never())
+ ->method('find');
+
+ $this->filterManager
+ ->expects($this->never())
+ ->method('applyFilter');
+
+ $this->assertSame($url, $service->getUrlOfFilteredImage(self::SOURCE_IMAGE, self::FILTER, $resolver, true));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageNotStored(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::SOURCE_IMAGE;
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $filteredBinary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [self::WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(false);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('store')
+ ->withConsecutive(
+ [$filteredBinary, self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [$filteredBinary, self::WEBP_IMAGE, self::FILTER, $resolver]
+ );
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::SOURCE_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, []],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS]
+ )
+ ->willReturn($binary);
+
+ $this->assertSame($url, $service->getUrlOfFilteredImage(self::SOURCE_IMAGE, self::FILTER, $resolver));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageNotStoredWebpSupported(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::WEBP_IMAGE;
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $filteredBinary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [self::WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(false);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('store')
+ ->withConsecutive(
+ [$filteredBinary, self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [$filteredBinary, self::WEBP_IMAGE, self::FILTER, $resolver]
+ );
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::WEBP_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, []],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS]
+ )
+ ->willReturn($binary);
+
+ $this->assertSame($url, $service->getUrlOfFilteredImage(self::SOURCE_IMAGE, self::FILTER, $resolver, true));
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageNotExistingFilter(bool $webpGenerate): void
+ {
+ $this->expectException(NonExistingFilterException::class);
+
+ $resolver = null;
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $exception = new NonExistingFilterException('Filter not found');
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::SOURCE_IMAGE, self::FILTER, $resolver],
+ [self::WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(false);
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, []],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS]
+ )
+ ->willThrowException($exception);
+
+ $this->logger
+ ->expects($this->atLeastOnce())
+ ->method('debug')
+ ->with(sprintf(
+ 'Could not locate filter "%s" for path "%s". Message was "%s"',
+ self::FILTER,
+ self::SOURCE_IMAGE,
+ $exception->getMessage()
+ ));
+
+ $service->getUrlOfFilteredImage(self::SOURCE_IMAGE, self::FILTER, $resolver);
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredWithRuntimeFiltersImage(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::RUNTIME_IMAGE;
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::RUNTIME_IMAGE, self::FILTER, $resolver],
+ [self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(true);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::RUNTIME_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('store');
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('getRuntimePath')
+ ->with(self::SOURCE_IMAGE, self::RUNTIME_FILTERS)
+ ->willReturn(self::RUNTIME_IMAGE);
+
+ $this->dataManager
+ ->expects($this->never())
+ ->method('find');
+
+ $this->filterManager
+ ->expects($this->never())
+ ->method('applyFilter');
+
+ $result = $service->getUrlOfFilteredImageWithRuntimeFilters(
+ self::SOURCE_IMAGE,
+ self::FILTER,
+ self::RUNTIME_FILTERS,
+ $resolver
+ );
+
+ $this->assertSame($url, $result);
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageWithRuntimeFiltersWebpSupported(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::RUNTIME_WEBP_IMAGE;
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::RUNTIME_IMAGE, self::FILTER, $resolver],
+ [self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(true);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+ $this->cacheManager
+ ->expects($this->never())
+ ->method('store');
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('getRuntimePath')
+ ->with(self::SOURCE_IMAGE, self::RUNTIME_FILTERS)
+ ->willReturn(self::RUNTIME_IMAGE);
+
+ $this->dataManager
+ ->expects($this->never())
+ ->method('find');
+
+ $this->filterManager
+ ->expects($this->never())
+ ->method('applyFilter');
+
+ $result = $service->getUrlOfFilteredImageWithRuntimeFilters(
+ self::SOURCE_IMAGE,
+ self::FILTER,
+ self::RUNTIME_FILTERS,
+ $resolver,
+ true
+ );
+
+ $this->assertSame($url, $result);
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageWithRuntimeFiltersNotStored(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::RUNTIME_IMAGE;
+ $runtimeOptions = [
+ 'filters' => self::RUNTIME_FILTERS,
+ ];
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $filteredBinary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::RUNTIME_IMAGE, self::FILTER, $resolver],
+ [self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(false);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('store')
+ ->withConsecutive(
+ [$filteredBinary, self::RUNTIME_IMAGE, self::FILTER, $resolver],
+ [$filteredBinary, self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver]
+ );
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::RUNTIME_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('getRuntimePath')
+ ->with(self::SOURCE_IMAGE, self::RUNTIME_FILTERS)
+ ->willReturn(self::RUNTIME_IMAGE);
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, $runtimeOptions],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS + $runtimeOptions]
+ )
+ ->willReturn($binary);
+
+ $result = $service->getUrlOfFilteredImageWithRuntimeFilters(
+ self::SOURCE_IMAGE,
+ self::FILTER,
+ self::RUNTIME_FILTERS,
+ $resolver
+ );
+
+ $this->assertSame($url, $result);
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageWithRuntimeFiltersNotStoredWebpSupported(bool $webpGenerate): void
+ {
+ $resolver = null;
+ $url = 'https://example.com/cache'.self::RUNTIME_WEBP_IMAGE;
+ $runtimeOptions = [
+ 'filters' => self::RUNTIME_FILTERS,
+ ];
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $filteredBinary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::RUNTIME_IMAGE, self::FILTER, $resolver],
+ [self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(false);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('store')
+ ->withConsecutive(
+ [$filteredBinary, self::RUNTIME_IMAGE, self::FILTER, $resolver],
+ [$filteredBinary, self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver]
+ );
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('resolve')
+ ->with(self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver)
+ ->willReturn($url);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('getRuntimePath')
+ ->with(self::SOURCE_IMAGE, self::RUNTIME_FILTERS)
+ ->willReturn(self::RUNTIME_IMAGE);
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, $runtimeOptions],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS + $runtimeOptions]
+ )
+ ->willReturn($binary);
+
+ $result = $service->getUrlOfFilteredImageWithRuntimeFilters(
+ self::SOURCE_IMAGE,
+ self::FILTER,
+ self::RUNTIME_FILTERS,
+ $resolver,
+ true
+ );
+
+ $this->assertSame($url, $result);
+ }
+
+ /**
+ * @dataProvider provideWebpGeneration
+ */
+ public function testGetUrlOfFilteredImageWithRuntimeFiltersNotExistingFilter(bool $webpGenerate): void
+ {
+ $this->expectException(NonExistingFilterException::class);
+ $resolver = null;
+ $runtimeOptions = [
+ 'filters' => self::RUNTIME_FILTERS,
+ ];
+ $binary = $this
+ ->getMockBuilder(BinaryInterface::class)
+ ->getMock();
+ $exception = new NonExistingFilterException('Filter not found');
+
+ $service = $this->createFilterService($webpGenerate);
+
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('isStored')
+ ->withConsecutive(
+ [self::RUNTIME_IMAGE, self::FILTER, $resolver],
+ [self::RUNTIME_WEBP_IMAGE, self::FILTER, $resolver]
+ )
+ ->willReturn(false);
+ $this->cacheManager
+ ->expects($this->atLeastOnce())
+ ->method('getRuntimePath')
+ ->with(self::SOURCE_IMAGE, self::RUNTIME_FILTERS)
+ ->willReturn(self::RUNTIME_IMAGE);
+
+ $this->dataManager
+ ->expects($this->atLeastOnce())
+ ->method('find')
+ ->with(self::FILTER, self::SOURCE_IMAGE)
+ ->willReturn($binary);
+
+ $this->filterManager
+ ->expects($this->atLeastOnce())
+ ->method('applyFilter')
+ ->withConsecutive(
+ [$binary, self::FILTER, $runtimeOptions],
+ [$binary, self::FILTER, [
+ 'format' => 'webp',
+ ] + self::WEBP_OPTIONS + $runtimeOptions]
+ )
+ ->willThrowException($exception);
+
+ $this->logger
+ ->expects($this->atLeastOnce())
+ ->method('debug')
+ ->with(sprintf(
+ 'Could not locate filter "%s" for path "%s". Message was "%s"',
+ self::FILTER,
+ self::SOURCE_IMAGE,
+ $exception->getMessage()
+ ));
+
+ $service->getUrlOfFilteredImageWithRuntimeFilters(
+ self::SOURCE_IMAGE,
+ self::FILTER,
+ self::RUNTIME_FILTERS,
+ $resolver
+ );
+ }
+
+ private function createFilterService(bool $webpGenerate): FilterService
+ {
+ return new FilterService(
+ $this->dataManager,
+ $this->filterManager,
+ $this->cacheManager,
+ $webpGenerate,
+ self::WEBP_OPTIONS,
+ $this->logger
+ );
+ }
+}