2018-03-24 19:39:13 +01:00
< ? php
2020-02-09 15:45:36 +01:00
/**
2023-01-01 15:36:24 +01:00
* @ copyright Copyright ( C ) 2010 - 2023 , the Friendica project
2020-02-09 15:45:36 +01:00
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program 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 Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
*/
2018-03-24 19:39:13 +01:00
2021-10-23 10:49:27 +02:00
namespace Friendica\Core\Cache\Type ;
2018-03-24 19:39:13 +01:00
2021-10-23 10:49:27 +02:00
use Friendica\Core\Cache\Enum\Duration ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\Cache\Capability\ICanCacheInMemory ;
2021-10-23 10:49:27 +02:00
use Friendica\Core\Cache\Enum\Type ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\Cache\Exception\CachePersistenceException ;
use Friendica\Core\Cache\Exception\InvalidCacheDriverException ;
use Friendica\Core\Config\Capability\IManageConfigValues ;
2018-07-11 00:55:01 +02:00
use Memcached ;
2019-08-03 20:48:56 +02:00
use Psr\Log\LoggerInterface ;
2018-07-11 00:55:01 +02:00
2018-03-24 19:39:13 +01:00
/**
2019-08-04 10:26:53 +02:00
* Memcached Cache
2018-03-24 19:39:13 +01:00
*/
2021-10-26 21:44:29 +02:00
class MemcachedCache extends AbstractCache implements ICanCacheInMemory
2018-03-24 19:39:13 +01:00
{
2021-10-26 21:44:29 +02:00
use CompareSetTrait ;
use CompareDeleteTrait ;
use MemcacheCommandTrait ;
2018-07-04 23:37:22 +02:00
2018-03-24 19:39:13 +01:00
/**
2018-07-05 20:57:31 +02:00
* @ var \Memcached
2018-03-24 19:39:13 +01:00
*/
private $memcached ;
2019-08-03 20:48:56 +02:00
/**
* @ var LoggerInterface
*/
private $logger ;
2018-07-08 07:46:46 +02:00
/**
* Due to limitations of the INI format , the expected configuration for Memcached servers is the following :
* array {
* 0 => " hostname, port(, weight) " ,
* 1 => ...
* }
*
2021-10-26 21:44:29 +02:00
* @ param string $hostname
* @ param IManageConfigValues $config
* @ param LoggerInterface $logger
2019-08-04 15:51:49 +02:00
*
2021-10-26 21:44:29 +02:00
* @ throws InvalidCacheDriverException
* @ throws CachePersistenceException
2018-07-08 07:46:46 +02:00
*/
2021-10-26 21:44:29 +02:00
public function __construct ( string $hostname , IManageConfigValues $config , LoggerInterface $logger )
2018-03-24 19:39:13 +01:00
{
if ( ! class_exists ( 'Memcached' , false )) {
2021-10-26 21:44:29 +02:00
throw new InvalidCacheDriverException ( 'Memcached class isn\'t available' );
2018-03-24 19:39:13 +01:00
}
2019-08-03 20:48:56 +02:00
parent :: __construct ( $hostname );
$this -> logger = $logger ;
2018-07-11 00:55:01 +02:00
$this -> memcached = new Memcached ();
2018-03-24 19:39:13 +01:00
2019-08-03 20:48:56 +02:00
$memcached_hosts = $config -> get ( 'system' , 'memcached_hosts' );
2018-07-08 07:46:46 +02:00
array_walk ( $memcached_hosts , function ( & $value ) {
2018-07-17 08:05:06 +02:00
if ( is_string ( $value )) {
$value = array_map ( 'trim' , explode ( ',' , $value ));
}
2018-07-08 07:46:46 +02:00
});
2019-09-24 17:52:38 +02:00
$this -> server = $memcached_hosts [ 0 ][ 0 ] ? ? 'localhost' ;
2021-10-26 21:44:29 +02:00
$this -> port = $memcached_hosts [ 0 ][ 1 ] ? ? 11211 ;
2019-08-15 13:58:01 +02:00
2018-03-24 19:39:13 +01:00
$this -> memcached -> addServers ( $memcached_hosts );
if ( count ( $this -> memcached -> getServerList ()) == 0 ) {
2021-10-26 21:44:29 +02:00
throw new CachePersistenceException ( 'Expected Memcached servers aren\'t available, config:' . var_export ( $memcached_hosts , true ));
2018-03-24 19:39:13 +01:00
}
}
2022-06-26 11:56:13 +02:00
/**
* Memcached doesn ' t allow spaces in keys
*
* @ param string $key
* @ return string
*/
protected function getCacheKey ( string $key ) : string
{
return str_replace ( ' ' , '_' , parent :: getCacheKey ( $key ));
}
2018-09-26 04:52:32 +02:00
/**
* ( @ inheritdoc )
*/
2021-10-26 21:44:29 +02:00
public function getAllKeys ( ? string $prefix = null ) : array
2018-09-26 04:52:32 +02:00
{
2019-09-24 17:52:38 +02:00
$keys = $this -> getOriginalKeys ( $this -> getMemcacheKeys ());
2018-10-07 00:27:54 +02:00
2019-08-15 13:58:01 +02:00
return $this -> filterArrayKeysByPrefix ( $keys , $prefix );
}
2018-09-26 04:51:41 +02:00
/**
* ( @ inheritdoc )
*/
2021-10-26 21:44:29 +02:00
public function get ( string $key )
2018-03-24 19:39:13 +01:00
{
2021-10-26 21:44:29 +02:00
$cacheKey = $this -> getCacheKey ( $key );
2018-03-24 19:39:13 +01:00
// We fetch with the hostname as key to avoid problems with other applications
2021-10-26 21:44:29 +02:00
$value = $this -> memcached -> get ( $cacheKey );
2018-03-24 19:39:13 +01:00
2018-07-11 00:55:01 +02:00
if ( $this -> memcached -> getResultCode () === Memcached :: RES_SUCCESS ) {
2021-10-26 21:44:29 +02:00
return $value ;
} elseif ( $this -> memcached -> getResultCode () === Memcached :: RES_NOTFOUND ) {
2022-11-08 10:20:19 +01:00
$this -> logger -> debug ( 'Try to use unknown key.' , [ 'key' => $key ]);
2021-10-26 21:44:29 +02:00
return null ;
2018-10-07 10:35:37 +02:00
} else {
2021-10-26 21:44:29 +02:00
throw new CachePersistenceException ( sprintf ( 'Cannot get cache entry with key %s' , $key ), new \MemcachedException ( $this -> memcached -> getResultMessage (), $this -> memcached -> getResultCode ()));
2018-03-24 19:39:13 +01:00
}
}
2018-09-26 04:51:41 +02:00
/**
* ( @ inheritdoc )
*/
2021-10-26 21:44:29 +02:00
public function set ( string $key , $value , int $ttl = Duration :: FIVE_MINUTES ) : bool
2018-03-24 19:39:13 +01:00
{
2021-10-26 21:44:29 +02:00
$cacheKey = $this -> getCacheKey ( $key );
2018-07-05 21:47:52 +02:00
2018-03-24 19:39:13 +01:00
// We store with the hostname as key to avoid problems with other applications
2018-07-04 23:37:22 +02:00
if ( $ttl > 0 ) {
return $this -> memcached -> set (
2021-10-26 21:44:29 +02:00
$cacheKey ,
2018-07-04 23:37:22 +02:00
$value ,
2018-07-07 19:46:16 +02:00
$ttl
2018-07-04 23:37:22 +02:00
);
} else {
return $this -> memcached -> set (
2021-10-26 21:44:29 +02:00
$cacheKey ,
2018-07-04 23:37:22 +02:00
$value
);
}
2018-03-24 19:39:13 +01:00
}
2018-09-26 04:51:41 +02:00
/**
* ( @ inheritdoc )
*/
2021-10-26 21:44:29 +02:00
public function delete ( string $key ) : bool
2018-03-24 19:39:13 +01:00
{
2021-10-26 21:44:29 +02:00
$cacheKey = $this -> getCacheKey ( $key );
return $this -> memcached -> delete ( $cacheKey );
2018-03-24 19:39:13 +01:00
}
2018-09-26 04:51:41 +02:00
/**
* ( @ inheritdoc )
*/
2021-10-26 21:44:29 +02:00
public function clear ( bool $outdated = true ) : bool
2018-03-24 19:39:13 +01:00
{
2018-07-07 19:46:16 +02:00
if ( $outdated ) {
return true ;
} else {
return $this -> memcached -> flush ();
}
2018-03-24 19:39:13 +01:00
}
2018-07-04 23:37:22 +02:00
/**
2018-09-26 04:51:41 +02:00
* ( @ inheritdoc )
2018-07-04 23:37:22 +02:00
*/
2021-10-26 21:44:29 +02:00
public function add ( string $key , $value , int $ttl = Duration :: FIVE_MINUTES ) : bool
2018-07-04 23:37:22 +02:00
{
2021-10-26 21:44:29 +02:00
$cacheKey = $this -> getCacheKey ( $key );
return $this -> memcached -> add ( $cacheKey , $value , $ttl );
2018-07-04 23:37:22 +02:00
}
2019-08-04 15:42:39 +02:00
2019-08-04 16:13:53 +02:00
/**
* { @ inheritDoc }
*/
2021-10-26 21:44:29 +02:00
public function getName () : string
2019-08-04 15:42:39 +02:00
{
2020-01-18 15:41:19 +01:00
return Type :: MEMCACHED ;
2019-08-04 15:42:39 +02:00
}
2018-03-24 19:39:13 +01:00
}