Skip to content

Commit f0616bb

Browse files
authored
Merge pull request #6 from andersundsehr/TYPO3v13
Put the optimizer to a scheduleable command
2 parents e4898c3 + 79947ae commit f0616bb

16 files changed

Lines changed: 298 additions & 225 deletions

Classes/Cache/Backend/LuceneCacheBackend.php

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
use TYPO3\CMS\Core\Context\Context;
1414
use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException;
1515
use TYPO3\CMS\Core\Utility\GeneralUtility;
16-
use Weakbit\LuceneCache\Tokenizer\SingleSpaceTokenzier;
16+
use Weakbit\LuceneCache\Tokenizer\SingleSpaceTokenizer;
1717
use Zend_Search_Exception;
1818
use Zend_Search_Lucene;
1919
use Zend_Search_Lucene_Analysis_Analyzer;
@@ -30,6 +30,8 @@
3030
use Zend_Search_Lucene_Search_QueryParser;
3131
use Zend_Search_Lucene_Search_QueryParserException;
3232

33+
use function trigger_deprecation;
34+
3335
class LuceneCacheBackend extends SimpleFileBackend implements TaggableBackendInterface
3436
{
3537
protected Zend_Search_Lucene_Interface $index;
@@ -58,7 +60,7 @@ public function __construct(string $context, array $options = [])
5860
parent::__construct($context, $options);
5961

6062
Zend_Search_Lucene::setTermsPerQueryLimit(PHP_INT_MAX);
61-
Zend_Search_Lucene_Analysis_Analyzer::setDefault(new SingleSpaceTokenzier());
63+
Zend_Search_Lucene_Analysis_Analyzer::setDefault(new SingleSpaceTokenizer());
6264
$this->execTime = GeneralUtility::makeInstance(Context::class)->getPropertyFromAspect('date', 'timestamp');
6365
register_shutdown_function([$this, 'shutdown']);
6466
}
@@ -172,9 +174,7 @@ private function getIndex(): Zend_Search_Lucene_Proxy
172174
}
173175
}
174176

175-
if (false === GeneralUtility::mkdir_deep($this->cacheDirectory)) {
176-
throw new Exception('Could not create temporary directory ' . $this->cacheDirectory);
177-
}
177+
GeneralUtility::mkdir_deep($this->cacheDirectory);
178178

179179
$proxy = Zend_Search_Lucene::create($this->cacheDirectory);
180180
assert($proxy instanceof Zend_Search_Lucene_Proxy);
@@ -281,10 +281,6 @@ public function flush(): void
281281
foreach ($hits as $hit) {
282282
$index->delete($hit->id);
283283
}
284-
285-
if ($this->optimize) {
286-
$index->optimize();
287-
}
288284
}
289285

290286
/**
@@ -394,9 +390,21 @@ public function setCompression(bool $compression): void
394390

395391
public function setOptimize(bool $optimize): void
396392
{
393+
trigger_deprecation('weakbit/lucene-cache', '2.0.3', 'Optimization flag is deprecated, remove it from your cache configuration');
397394
$this->optimize = $optimize;
398395
}
399396

397+
/**
398+
* Optimize the Lucene index
399+
* @throws Zend_Search_Exception
400+
* @throws Zend_Search_Lucene_Exception
401+
*/
402+
public function optimize(): void
403+
{
404+
$index = $this->getIndex();
405+
$index->optimize();
406+
}
407+
400408
/**
401409
* @param int $compressionLevel -1 to 9: Compression level
402410
*/

Classes/Cache/Frontend/VariableFrontend.php

Lines changed: 3 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -4,127 +4,15 @@
44

55
namespace Weakbit\LuceneCache\Cache\Frontend;
66

7-
use TYPO3\CMS\Core\Cache\Exception;
8-
use TYPO3\CMS\Core\Cache\Exception\InvalidDataException;
9-
use MessagePack\BufferUnpacker;
10-
use MessagePack\Packer;
11-
use TYPO3\CMS\Core\Cache\Backend\TransientBackendInterface;
12-
use TYPO3\CMS\Core\Utility\GeneralUtility;
137
use TYPO3\CMS\Core\Cache\Backend\BackendInterface;
14-
use Weakbit\LuceneCache\Transformer\PageTransformer;
15-
use Weakbit\LuceneCache\Transformer\UriTransformer;
8+
9+
use function trigger_deprecation;
1610

1711
class VariableFrontend extends \TYPO3\CMS\Core\Cache\Frontend\VariableFrontend
1812
{
19-
protected bool $messagePack = false;
20-
21-
protected bool $igBinary = false;
22-
2313
public function __construct($identifier, BackendInterface $backend)
2414
{
2515
parent::__construct($identifier, $backend);
26-
$this->messagePack = class_exists(Packer::class);
27-
$this->igBinary = class_exists('igbinary_serialize');
28-
}
29-
30-
/**
31-
* @param array<string> $tags
32-
* @throws Exception
33-
* @throws InvalidDataException
34-
*/
35-
public function set($entryIdentifier, $variable, array $tags = [], $lifetime = null): void
36-
{
37-
if (!$this->isValidEntryIdentifier($entryIdentifier)) {
38-
throw new \InvalidArgumentException(
39-
'"' . $entryIdentifier . '" is not a valid cache entry identifier.',
40-
1233058264
41-
);
42-
}
43-
44-
foreach ($tags as $tag) {
45-
if (!$this->isValidTag($tag)) {
46-
throw new \InvalidArgumentException('"' . $tag . '" is not a valid tag for a cache entry.', 1233058269);
47-
}
48-
}
49-
50-
foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/cache/frontend/class.t3lib_cache_frontend_variablefrontend.php']['set'] ?? [] as $_funcRef) {
51-
$params = [
52-
'entryIdentifier' => &$entryIdentifier,
53-
'variable' => &$variable,
54-
'tags' => &$tags,
55-
'lifetime' => &$lifetime,
56-
];
57-
GeneralUtility::callUserFunction($_funcRef, $params, $this);
58-
}
59-
60-
if (!$this->backend instanceof TransientBackendInterface) {
61-
$variable = $this->serialize($variable);
62-
if (null === $variable) {
63-
throw new \RuntimeException('Could not serialize variable for cache');
64-
}
65-
}
66-
67-
$this->backend->set($entryIdentifier, $variable, $tags, $lifetime);
68-
}
69-
70-
/**
71-
* Finds and returns a variable value from the cache.
72-
*
73-
* @param string $entryIdentifier Identifier of the cache entry to fetch
74-
*
75-
* @return mixed The value
76-
* @throws \InvalidArgumentException if the identifier is not valid
77-
*/
78-
public function get($entryIdentifier)
79-
{
80-
if (!$this->isValidEntryIdentifier($entryIdentifier)) {
81-
throw new \InvalidArgumentException(
82-
'"' . $entryIdentifier . '" is not a valid cache entry identifier.',
83-
1233058294
84-
);
85-
}
86-
87-
$rawResult = $this->backend->get($entryIdentifier);
88-
if ($rawResult === false) {
89-
return false;
90-
}
91-
92-
return $this->backend instanceof TransientBackendInterface ? $rawResult : $this->unserialize($rawResult);
93-
}
94-
95-
protected function serialize(mixed $variable): ?string
96-
{
97-
if ($this->igBinary) {
98-
return igbinary_serialize($variable);
99-
}
100-
101-
if ($this->messagePack) {
102-
$packer = GeneralUtility::makeInstance(Packer::class, null, [
103-
new PageTransformer(10),
104-
new UriTransformer(11),
105-
]);
106-
assert($packer instanceof Packer);
107-
return $packer->pack($variable);
108-
}
109-
110-
return serialize($variable);
111-
}
112-
113-
protected function unserialize(string $rawResult): mixed
114-
{
115-
if ($this->igBinary) {
116-
return igbinary_unserialize($rawResult);
117-
}
118-
119-
if ($this->messagePack) {
120-
$unpacker = GeneralUtility::makeInstance(BufferUnpacker::class, $rawResult, null, [
121-
new PageTransformer(10),
122-
new UriTransformer(11),
123-
]);
124-
assert($unpacker instanceof BufferUnpacker);
125-
return $unpacker->unpack();
126-
}
127-
128-
return unserialize($rawResult);
16+
trigger_deprecation('weakbit/lucene-cache', '2.0.3', 'The class %s is deprecated and will be removed in a future version.', self::class);
12917
}
13018
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
/** @noinspection PhpUnused */
4+
5+
declare(strict_types=1);
6+
7+
namespace Weakbit\LuceneCache\Command;
8+
9+
use Symfony\Component\Console\Command\Command;
10+
use Symfony\Component\Console\Input\InputInterface;
11+
use Symfony\Component\Console\Output\OutputInterface;
12+
use TYPO3\CMS\Core\Cache\CacheManager;
13+
use TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException;
14+
use TYPO3\CMS\Core\EventDispatcher\EventDispatcher;
15+
use TYPO3\CMS\Core\Utility\GeneralUtility;
16+
use Weakbit\LuceneCache\Cache\Backend\LuceneCacheBackend;
17+
use Weakbit\LuceneCache\Event\CacheOptimizationRequestedEvent;
18+
use Zend_Search_Exception;
19+
use Zend_Search_Lucene_Exception;
20+
21+
class OptimizeLuceneCacheCommand extends Command
22+
{
23+
/**
24+
* Configure the command
25+
*/
26+
protected function configure(): void
27+
{
28+
$this->setDescription('Optimize Lucene cache indices that are flagged for optimization');
29+
$this->setHelp('This command checks all Lucene cache backends for optimization flags and optimizes them if needed.');
30+
}
31+
32+
/**
33+
* @throws NoSuchCacheException
34+
* @throws Zend_Search_Exception
35+
* @throws Zend_Search_Lucene_Exception
36+
*/
37+
protected function execute(InputInterface $input, OutputInterface $output): int
38+
{
39+
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
40+
assert($cacheManager instanceof CacheManager);
41+
$optimizedCaches = 0;
42+
$totalCaches = 0;
43+
44+
// Get cache configurations from TYPO3_CONF_VARS instead
45+
$cacheConfigurations = $GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations'] ?? [];
46+
47+
// Use the event system to determine if optimization should happen
48+
$eventDispatcher = GeneralUtility::makeInstance(EventDispatcher::class);
49+
$event = new CacheOptimizationRequestedEvent();
50+
$eventDispatcher->dispatch($event);
51+
52+
if (!$event->shouldOptimize()) {
53+
$output->writeln('Event system prevented optimization');
54+
return Command::FAILURE;
55+
}
56+
57+
58+
foreach ($cacheConfigurations as $identifier => $configuration) {
59+
// Check if this cache uses the LuceneCacheBackend
60+
if (
61+
isset($configuration['backend']) &&
62+
$configuration['backend'] === LuceneCacheBackend::class
63+
) {
64+
$totalCaches++;
65+
66+
// Check if cache exists before trying to get it
67+
if ($cacheManager->hasCache($identifier)) {
68+
$cache = $cacheManager->getCache($identifier);
69+
$backend = $cache->getBackend();
70+
71+
if ($backend instanceof LuceneCacheBackend) {
72+
$backend->optimize();
73+
$optimizedCaches++;
74+
}
75+
} else {
76+
$output->writeln(sprintf("<comment>Cache '%s' is configured but not available</comment>", $identifier), OutputInterface::VERBOSITY_VERBOSE);
77+
}
78+
}
79+
}
80+
81+
if ($totalCaches === 0) {
82+
$output->writeln('<info>No Lucene cache backends found in configuration</info>');
83+
} else {
84+
$output->writeln(sprintf('<info>Processed %d Lucene cache(s), optimized %d</info>', $totalCaches, $optimizedCaches));
85+
}
86+
87+
return Command::SUCCESS;
88+
}
89+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Weakbit\LuceneCache\Event;
6+
7+
use Weakbit\LuceneCache\Cache\Backend\LuceneCacheBackend;
8+
9+
/**
10+
* Event dispatched when a Lucene cache backend needs optimization
11+
*/
12+
final class CacheOptimizationRequestedEvent
13+
{
14+
public function __construct(
15+
private bool $shouldOptimize = true
16+
) {
17+
}
18+
19+
public function shouldOptimize(): bool
20+
{
21+
return $this->shouldOptimize;
22+
}
23+
24+
public function preventOptimization(): void
25+
{
26+
$this->shouldOptimize = false;
27+
}
28+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Weakbit\LuceneCache\EventListener;
6+
7+
use Exception;
8+
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
9+
use TYPO3\CMS\Core\Utility\GeneralUtility;
10+
11+
abstract class AbstractExtensionConfigListener
12+
{
13+
/**
14+
* @return array<string, mixed>
15+
*/
16+
protected function getExtensionConfiguration(): array
17+
{
18+
try {
19+
$extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class);
20+
return $extensionConfiguration->get('lucene_cache') ?? [];
21+
} catch (Exception) {
22+
return [];
23+
}
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Weakbit\LuceneCache\EventListener;
6+
7+
use Weakbit\LuceneCache\Event\CacheOptimizationRequestedEvent;
8+
9+
/**
10+
* Listens for CacheOptimizationRequestedEvent and disables optimization if not enabled in config
11+
*/
12+
class EnableOptimizationListener extends AbstractExtensionConfigListener
13+
{
14+
public function __invoke(CacheOptimizationRequestedEvent $event): void
15+
{
16+
if (!$event->shouldOptimize()) {
17+
return;
18+
}
19+
20+
$extensionConfig = $this->getExtensionConfiguration();
21+
if (!($extensionConfig['enableOptimization'] ?? true)) {
22+
$event->preventOptimization();
23+
}
24+
}
25+
}

0 commit comments

Comments
 (0)