Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/bundle/Resources/config/event.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ services:
$contentHandler: '@Ibexa\Core\Persistence\Cache\ContentHandler'
$isTranslationAware: '%ibexa.http_cache.translation_aware.enabled%'

Ibexa\HttpCache\EventSubscriber\CachePurge\BinaryFileHttpCachePurgeSubscriber:
arguments:
$cacheManager: '@fos_http_cache.cache_manager'

Ibexa\HttpCache\EventSubscriber\CachePurge\:
resource: '../../../lib/EventSubscriber/CachePurge/*'
exclude: '../../../lib/EventSubscriber/CachePurge/ContentEventsSubscriber.php'
exclude:
- '../../../lib/EventSubscriber/CachePurge/ContentEventsSubscriber.php'
- '../../../lib/EventSubscriber/CachePurge/BinaryFileHttpCachePurgeSubscriber.php'
arguments:
$purgeClient: '@ibexa.http_cache.purge_client'
$locationHandler: '@Ibexa\Core\Persistence\Cache\LocationHandler'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\HttpCache\EventSubscriber\CachePurge;

use FOS\HttpCacheBundle\CacheManager;
use Ibexa\Contracts\Core\Repository\Events\Content\PublishVersionEvent;
use Ibexa\Core\FieldType\BinaryBase\Value as BinaryBaseValue;
use Ibexa\Core\FieldType\Image\Value as ImageValue;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class BinaryFileHttpCachePurgeSubscriber implements EventSubscriberInterface
{
private CacheManager $cacheManager;

public function __construct(CacheManager $cacheManager)
{
$this->cacheManager = $cacheManager;
}

public static function getSubscribedEvents(): array
{
return [
PublishVersionEvent::class => 'onPublishVersion',
];
}

public function onPublishVersion(PublishVersionEvent $event): void
{
$content = $event->getContent();
$purged = [];

foreach ($content->getFields() as $field) {
$value = $field->value;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use magic where possible.

Suggested change
$value = $field->value;
$value = $field->getValue();


if (!$value instanceof ImageValue && !$value instanceof BinaryBaseValue) {
continue;
}

$uri = $value->uri;

if ($uri === null || $uri === '' || isset($purged[$uri])) {
continue;
}

$this->cacheManager->invalidatePath($uri);
Comment thread
konradoboza marked this conversation as resolved.
$purged[$uri] = true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

/**
* @copyright Copyright (C) Ibexa AS. All rights reserved.
* @license For full copyright and license information view LICENSE file distributed with this source code.
*/
declare(strict_types=1);

namespace Ibexa\Tests\HttpCache\EventSubscriber\CachePurge;

use FOS\HttpCache\ProxyClient\ProxyClient;
use FOS\HttpCacheBundle\CacheManager;
use Ibexa\Contracts\Core\Repository\Events\Content\PublishVersionEvent;
use Ibexa\Contracts\Core\Repository\Values\Content\Content;
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo;
use Ibexa\Core\FieldType\BinaryFile\Value as BinaryFileValue;
use Ibexa\Core\FieldType\Image\Value as ImageValue;
use Ibexa\HttpCache\EventSubscriber\CachePurge\BinaryFileHttpCachePurgeSubscriber;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

final class BinaryFileHttpCachePurgeSubscriberTest extends TestCase
{
private CacheManager $cacheManager;

private BinaryFileHttpCachePurgeSubscriber $subscriber;

protected function setUp(): void
{
$this->cacheManager = $this->getMockBuilder(CacheManager::class)
->setConstructorArgs([
$this->createMock(ProxyClient::class),
$this->createMock(UrlGeneratorInterface::class),
])
->getMock();
Comment thread
konradoboza marked this conversation as resolved.
Outdated

$this->subscriber = new BinaryFileHttpCachePurgeSubscriber($this->cacheManager);
}

public function testGetSubscribedEvents(): void
{
self::assertArrayHasKey(PublishVersionEvent::class, BinaryFileHttpCachePurgeSubscriber::getSubscribedEvents());
Comment thread
konradoboza marked this conversation as resolved.
Outdated
}

/**
* @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields
*/
private function buildEvent(array $fields): PublishVersionEvent
{
$content = $this->createMock(Content::class);
$content->method('getFields')->willReturn($fields);

return new PublishVersionEvent(
$content,
$this->createMock(VersionInfo::class),
[],
);
}

public function testNoFieldsDoesNotCallInvalidatePath(): void
{
$this->cacheManager->expects(self::never())->method('invalidatePath');

$this->subscriber->onPublishVersion($this->buildEvent([]));
}

public function testNonBinaryFieldIsSkipped(): void
{
$this->cacheManager->expects(self::never())->method('invalidatePath');

$field = new Field(['value' => new \stdClass()]);
$this->subscriber->onPublishVersion($this->buildEvent([$field]));
}

public function testImageValueWithUriIsInvalidated(): void
{
$imageValue = new ImageValue();
$imageValue->uri = '/var/site/storage/images/foo.jpg';

$this->cacheManager
->expects(self::once())
->method('invalidatePath')
->with('/var/site/storage/images/foo.jpg');

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $imageValue]),
]));
}

public function testBinaryFileValueWithUriIsInvalidated(): void
{
$binaryValue = new BinaryFileValue();
$binaryValue->uri = '/var/site/storage/original/application/foo.pdf';

$this->cacheManager
->expects(self::once())
->method('invalidatePath')
->with('/var/site/storage/original/application/foo.pdf');

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $binaryValue]),
]));
}

public function testImageValueWithNullUriIsSkipped(): void
{
$imageValue = new ImageValue();
$imageValue->uri = null;

$this->cacheManager->expects(self::never())->method('invalidatePath');

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $imageValue]),
]));
}

public function testImageValueWithEmptyUriIsSkipped(): void
{
$imageValue = new ImageValue();
$imageValue->uri = '';

$this->cacheManager->expects(self::never())->method('invalidatePath');

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $imageValue]),
]));
}

public function testDuplicateUriIsInvalidatedOnlyOnce(): void
{
$uri = '/var/site/storage/images/same.jpg';

$imageValue1 = new ImageValue();
$imageValue1->uri = $uri;

$imageValue2 = new ImageValue();
$imageValue2->uri = $uri;

$this->cacheManager
->expects(self::once())
->method('invalidatePath')
->with($uri);

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $imageValue1]),
new Field(['value' => $imageValue2]),
]));
}

public function testMultipleDistinctUrisAreEachInvalidated(): void
{
$imageValue = new ImageValue();
$imageValue->uri = '/var/site/storage/images/a.jpg';

$binaryValue = new BinaryFileValue();
$binaryValue->uri = '/var/site/storage/original/application/b.pdf';

$this->cacheManager
->expects(self::exactly(2))
->method('invalidatePath')
->withConsecutive(
['/var/site/storage/images/a.jpg'],
['/var/site/storage/original/application/b.pdf'],
);

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $imageValue]),
new Field(['value' => $binaryValue]),
]));
}

public function testMixedFieldsOnlyInvalidatesBinaryAndImageUris(): void
{
$imageValue = new ImageValue();
$imageValue->uri = '/var/site/storage/images/photo.jpg';

$this->cacheManager
->expects(self::once())
->method('invalidatePath')
->with('/var/site/storage/images/photo.jpg');

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => 'plain text value']),
new Field(['value' => $imageValue]),
new Field(['value' => 42]),
]));
}
}
Loading