Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion features/setup/symfonyCache.feature
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ index 9982c21..03ac40a 100644

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

return function (array $context) {
return static function (array $context) {
- return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
+ $kernel = new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
+ Request::enableHttpMethodParameterOverride();
Expand Down
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,192 @@
<?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\Invalidation\PurgeCapable;
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 PurgeCapable $proxyClient;

private BinaryFileHttpCachePurgeSubscriber $subscriber;

protected function setUp(): void
{
$this->proxyClient = $this->createMock(PurgeCapable::class);
$this->subscriber = new BinaryFileHttpCachePurgeSubscriber(
new CacheManager(
$this->proxyClient,
$this->createMock(UrlGeneratorInterface::class)
),
);
}

public function testGetSubscribedEvents(): void
{
self::assertArrayHasKey(
PublishVersionEvent::class,
BinaryFileHttpCachePurgeSubscriber::getSubscribedEvents()
);
}

/**
* @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 testNoFieldsDoesNotCallPurge(): void
{
$this->proxyClient->expects(self::never())->method('purge');

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

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

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => new \stdClass()]),
]));
}
Comment on lines +63 to +77
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.

These can be one test with a data provider


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

$this->proxyClient
->expects(self::once())
->method('purge')
->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->proxyClient
->expects(self::once())
->method('purge')
->with('/var/site/storage/original/application/foo.pdf', []);

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $binaryValue]),
]));
}
Comment on lines +79 to +107
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.

These are also candidates for another data provider + test


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

$this->proxyClient->expects(self::never())->method('purge');

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

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

$this->proxyClient->expects(self::never())->method('purge');

$this->subscriber->onPublishVersion($this->buildEvent([
new Field(['value' => $imageValue]),
]));
}
Comment on lines +109 to +131
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.

Seems these could also be attached to the mentioned data provider.


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

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

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

$this->proxyClient
->expects(self::once())
->method('purge')
->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->proxyClient
->expects(self::exactly(2))
->method('purge')
->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->proxyClient
->expects(self::once())
->method('purge')
->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