Skip to content

Instantly share code, notes, and snippets.

@lgescobar
Created June 10, 2018 18:55
Show Gist options
  • Save lgescobar/7addb3b2397cce827ab4bf51d1ee9d15 to your computer and use it in GitHub Desktop.
Save lgescobar/7addb3b2397cce827ab4bf51d1ee9d15 to your computer and use it in GitHub Desktop.
Simple LRU cache for TYPO3
<?php
if (!defined('TYPO3_MODE')) {
die('Access denied.');
}
/* *************************************************************
* Copyright notice
*
* (c) 2018 Luis Antonio García Escobar <lgescobar@outlook.com>
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
// Register cache
if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname'])) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname'] = [];
}
if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['backend'])) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['backend'] =
\Vendor\Product\Cache\Backend\TransientMemoryLRUBackend::class;
}
// Define string frontend as default frontend because only the token (of type string) will be stored in cache.
if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['frontend'])) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['frontend'] =
\Vendor\Product\Cache\Frontend\RawFrontend::class;
}
if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['options'])) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['options'] =
['maximumSize' => 10];
}
if (!isset($GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['groups'])) {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['extname']['groups'] = ['all'];
}
<?php
namespace Vendor\Product\Cache\Frontend;
/* *************************************************************
* Copyright notice
*
* (c) 2018 Luis Antonio García Escobar <lgescobar@outlook.com>
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use TYPO3\CMS\Core\Cache\Frontend\StringFrontend;
/**
* A cache frontend for the LRU cache.
*/
class RawFrontend extends StringFrontend
{
/**
* Saves the value of a PHP variable in the cache.
*
* @param string|int|float $entryIdentifier An identifier used for this cache entry
* @param mixed $string The variable to cache
* @param array $tags Tags to associate with this cache entry
* @param int $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is used. "0" means unlimited liftime.
* @return void
*
* @throws \InvalidArgumentException If the identifier or tag is not valid
* @throws \TYPO3\CMS\Core\Cache\Exception If no cache frontend has been set
* @throws \TYPO3\CMS\Core\Cache\Exception\InvalidDataException It doesn't apply
*/
public function set($entryIdentifier, $string, array $tags = [], $lifetime = null)
{
if (!$this->isValidEntryIdentifier($entryIdentifier)) {
throw new \InvalidArgumentException('"' . $entryIdentifier . '" is not a valid cache entry identifier.', 1233057566);
}
foreach ($tags as $tag) {
if (!$this->isValidTag($tag)) {
throw new \InvalidArgumentException('"' . $tag . '" is not a valid tag for a cache entry.', 1233057512);
}
}
$this->backend->set($entryIdentifier, $string, $tags, $lifetime);
}
/**
* Checks the validity of an entry identifier. Returns TRUE if it's valid.
*
* @param string $identifier An identifier to be checked for validity
* @return bool
*/
public function isValidEntryIdentifier($identifier)
{
return is_string($identifier) || is_int($identifier) || is_float($identifier);
}
/**
* Checks the validity of a tag. Returns TRUE if it's valid.
*
* @param string $tag An identifier to be checked for validity
* @return bool
*/
public function isValidTag($tag)
{
return is_string($tag);
}
}
<?php
namespace Vendor\Product\Domain\Model;
/* *************************************************************
* Copyright notice
*
* (c) 2018 Luis Antonio García Escobar <lgescobar@outlook.com>
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use TYPO3\CMS\Extbase\Persistence\Generic\LazyLoadingProxy;
trait RelationUidTrait
{
/**
* @param string $propertyName relation name (plural, lower camelCase)
* @return int
* @throws \ReflectionException
*/
protected function getRelationUid($propertyName)
{
if ($this->$propertyName instanceof LazyLoadingProxy) {
/** @var \ReflectionClass $reflectionClass */
$reflectionClass = new \ReflectionClass(LazyLoadingProxy::class);
$reflectionProperty = $reflectionClass->getProperty('fieldValue');
$reflectionProperty->setAccessible(true);
$uid = (int)$reflectionProperty->getValue($this->$propertyName);
} elseif (is_numeric($this->$propertyName)) {
$uid = $this->$propertyName;
} elseif (is_null($this->$propertyName)) {
$uid = 0;
} else {
$uid = $this->$propertyName->getUid();
}
return $uid;
}
}
<?php
namespace Vendor\Product\Cache\Backend;
/* *************************************************************
* Copyright notice
*
* (c) 2018 Luis Antonio García Escobar <lgescobar@outlook.com>
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use TYPO3\CMS\Core\Cache\Backend\AbstractBackend;
use TYPO3\CMS\Core\Cache\Backend\TransientBackendInterface;
class TransientMemoryLRUBackend extends AbstractBackend implements TransientBackendInterface
{
const FALLBACK_CACHE_SIZE = 25;
/**
* @var int
*/
protected $maximumSize;
/**
* @var array
*/
protected $entries = [];
protected $accessCount = 0;
protected $hitCount = 0;
protected $missCount = 0;
protected $setCount = 0;
protected $evictionCount = 0;
/**
* @param int $maximumSize
*/
public function setMaximumSize($maximumSize)
{
if (is_numeric($maximumSize)) {
$maximumSize = (int)$maximumSize;
}
if (is_int($maximumSize) && $maximumSize > 0) {
$this->maximumSize = $maximumSize;
} else {
$this->maximumSize = self::FALLBACK_CACHE_SIZE;
}
}
function __destruct()
{
if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
echo json_encode([
'accessCount' => $this->accessCount,
'hitCount' => $this->hitCount,
'missCount' => $this->missCount,
'setCount' => $this->setCount,
'evictionCount' => $this->evictionCount,
'memoryPeakUsage' => memory_get_peak_usage() . ' B',
]);
}
}
/**
* Saves data in the cache.
*
* @param string $entryIdentifier An identifier for this specific cache entry
* @param string $data The data to be stored
* @param array $tags Tags to associate with this cache entry
* @param int $lifetime Lifetime of this cache entry in seconds. If NULL is specified, the default lifetime is
* used. "0" means unlimited liftime.
* @return void
*
* @throws \TYPO3\CMS\Core\Cache\Exception if no cache frontend has been set.
*/
public function set($entryIdentifier, $data, array $tags = [], $lifetime = null)
{
if (!$this->cache instanceof \TYPO3\CMS\Core\Cache\Frontend\FrontendInterface) {
throw new \TYPO3\CMS\Core\Cache\Exception('No cache frontend has been set yet via setCache().', 1238244992);
}
$this->setCount++;
if (isset($this->entries[$entryIdentifier])) {
$data = $this->entries[$entryIdentifier];
unset($this->entries[$entryIdentifier]);
$this->entries[$entryIdentifier] = $data;
} else {
if (count($this->entries) >= $this->maximumSize) {
// remove least recently used element (front of array)
$this->evictionCount++;
reset($this->entries);
unset($this->entries[key($this->entries)]);
}
$this->entries[$entryIdentifier] = $data;
}
}
/**
* Loads data from the cache.
*
* @param string $entryIdentifier An identifier which describes the cache entry to load
* @return mixed The cache entry's content as a string or FALSE if the cache entry could not be loaded
* @api
*/
public function get($entryIdentifier)
{
$this->accessCount++;
if (isset($this->entries[$entryIdentifier])) {
$this->hitCount++;
$data = $this->entries[$entryIdentifier];
unset($this->entries[$entryIdentifier]);
$this->entries[$entryIdentifier] = $data;
return $data;
} else {
$this->missCount++;
return false;
}
}
/**
* Checks if a cache entry with the specified identifier exists.
*
* @param string $entryIdentifier An identifier specifying the cache entry
* @return bool TRUE if such an entry exists, FALSE if not
* @api
*/
public function has($entryIdentifier)
{
return isset($this->entries[$entryIdentifier]);
}
/**
* Removes all cache entries matching the specified identifier.
*
* @param string $entryIdentifier Specifies the cache entry to remove
* @return bool TRUE if the entry could be removed or FALSE if no entry was found
* @api
*/
public function remove($entryIdentifier)
{
if (isset($this->entries[$entryIdentifier])) {
unset($this->entries[$entryIdentifier]);
return true;
} else {
return false;
}
}
/**
* Removes all cache entries of this cache.
*
* @return void
* @api
*/
public function flush()
{
$this->entries = [];
}
/**
* Does nothing
*
* @return void
*/
public function collectGarbage()
{
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment