335 lines
9.3 KiB
PHP
335 lines
9.3 KiB
PHP
|
<?php
|
||
|
/**
|
||
|
* Calculer la taille du cache
|
||
|
*
|
||
|
* @package SPIP\memoization\Options
|
||
|
**/
|
||
|
|
||
|
// Sécurité
|
||
|
if (!defined('_ECRIRE_INC_VERSION')){
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Interface MCacheBackend
|
||
|
* Implémentée par les différentes méthodes disponibles
|
||
|
*/
|
||
|
interface MCacheBackend {
|
||
|
public function init($params = null);
|
||
|
public function get($key);
|
||
|
public function set($key, $value, $ttl = null);
|
||
|
public function exists($key);
|
||
|
public function del($key);
|
||
|
public function inc($key, $value = null, $ttl = null);
|
||
|
public function dec($key, $value = null, $ttl = null);
|
||
|
public function lock($key, /* private */ $unlock = false);
|
||
|
public function unlock($key);
|
||
|
public function size();
|
||
|
public function purge();
|
||
|
}
|
||
|
|
||
|
/* objet MCache */
|
||
|
|
||
|
class MCache {
|
||
|
private $methode;
|
||
|
private $backend;
|
||
|
private $is_memory;
|
||
|
|
||
|
public function __construct($methode = null, array $params = []){
|
||
|
// Initialiser les namespace si pas encore fait
|
||
|
if (isset($GLOBALS['meta']['cache_namespace'])){
|
||
|
if (!defined('_CACHE_NAMESPACE')){
|
||
|
define('_CACHE_NAMESPACE',
|
||
|
(isset($_SERVER["SERVER_NAME"]) ? $_SERVER["SERVER_NAME"] : "cli")
|
||
|
. ':'
|
||
|
. (isset($_SERVER["SERVER_PORT"]) ? $_SERVER["SERVER_PORT"] : "cli")
|
||
|
. ':'
|
||
|
. $GLOBALS['meta']['cache_namespace']
|
||
|
. ':'
|
||
|
);
|
||
|
}
|
||
|
if (isset($GLOBALS['meta']['cache_key']) and !defined('_CACHE_KEY')){
|
||
|
define('_CACHE_KEY', $GLOBALS['meta']['cache_key']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// autodetect si pas fournie
|
||
|
$this->methode = $methode ? $methode : self::autodetect();
|
||
|
|
||
|
if (!$f = find_in_path($this->methode . '.inc', "memo/", true)){
|
||
|
spip_log("Methode " . $this->methode . " non implementee - fallback filecache", "memoization" . _LOG_ERREUR);
|
||
|
$this->methode = 'filecache';
|
||
|
require_once(find_in_path($this->methode . '.inc', "memo/"));
|
||
|
}
|
||
|
$this->is_memory = (!in_array($this->methode, ['filecache', 'nocache']));
|
||
|
|
||
|
$obj = 'MCacheBackend_' . $this->methode;
|
||
|
$this->backend = new $obj;
|
||
|
$this->backend->init($params);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Indiquer si la methode de cache est une méthode rapide
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function is_memory(): bool{
|
||
|
return $this->is_memory;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Lire la méthode utilisée
|
||
|
* @return string
|
||
|
*/
|
||
|
public function methode(): string{
|
||
|
return $this->methode;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Autodetection de la methode disponible
|
||
|
* @return string
|
||
|
*/
|
||
|
static public function autodetect(): string{
|
||
|
$methodes = ['apcu', 'apc', 'xcache', 'filecache', 'nocache'];
|
||
|
foreach ($methodes as $methode){
|
||
|
if (self::methode_disponible($methode)){
|
||
|
return $methode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// ne doit jamais arriver, car nocache est toujours disponible
|
||
|
return $methode;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param string $methode
|
||
|
* @return bool|string
|
||
|
*/
|
||
|
static public function methode_disponible(string $methode){
|
||
|
|
||
|
switch ($methode) {
|
||
|
case 'apc':
|
||
|
return function_exists('apc_exists');
|
||
|
|
||
|
case 'apcu':
|
||
|
return function_exists('apcu_exists');
|
||
|
|
||
|
case 'xcache':
|
||
|
if (!function_exists('xcache_set')){
|
||
|
return false;
|
||
|
}
|
||
|
@xcache_set('xcache_autodetect', 1234);
|
||
|
return @xcache_get('xcache_autodetect')==1234;
|
||
|
|
||
|
case 'memcache':
|
||
|
return function_exists('memcache_set');
|
||
|
|
||
|
case 'memcached':
|
||
|
return class_exists('Memcached');
|
||
|
|
||
|
case 'redis':
|
||
|
return extension_loaded('redis');
|
||
|
|
||
|
case 'filecache':
|
||
|
case 'nocache':
|
||
|
return true;
|
||
|
|
||
|
// Methode inconnue ?
|
||
|
// TODO : charger le fichier memo/xxx et chercher une methode static detect ?
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Méthode de memoization (si explicitement configurée)
|
||
|
*
|
||
|
* @return string|null
|
||
|
*/
|
||
|
public static function config_methode(){
|
||
|
if (empty($GLOBALS['meta']['memoization'])){
|
||
|
return null;
|
||
|
}
|
||
|
$cfg = @unserialize($GLOBALS['meta']['memoization']);
|
||
|
if (isset($cfg['methode'])){
|
||
|
return preg_replace(",\W,", "", $cfg['methode']);
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
static public function config_redis_server(){
|
||
|
$cfg = @unserialize($GLOBALS['meta']['memoization']);
|
||
|
if (!$cfg || !isset($cfg['redis_type']) || empty($cfg['redis_type'])){
|
||
|
$cfg = array(
|
||
|
'redis_type' => 'serveur',
|
||
|
'redis_server' => '127.0.0.1:6379',
|
||
|
'redis_sock' => '/tmp/redis.sock',
|
||
|
'redis_auth' => '',
|
||
|
'redis_dbindex' => 0,
|
||
|
'redis_serializer' => 'php',
|
||
|
);
|
||
|
}
|
||
|
return $cfg;
|
||
|
}
|
||
|
|
||
|
// outil pour memcache (hosts et ports a configurer dans le CFG)
|
||
|
static public function config_memcache_servers(){
|
||
|
$cfg = @unserialize($GLOBALS['meta']['memoization']);
|
||
|
if (!$cfg = $cfg['memcache_servers']){
|
||
|
$cfg = 'localhost:11211';
|
||
|
}
|
||
|
preg_match_all('/[a-z0-9._-]*(?::\d+)/', $cfg, $s, PREG_PATTERN_ORDER);
|
||
|
return $s[0];
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Proxies vers le backend */
|
||
|
public function get($key){return $this->backend->get($key);}
|
||
|
public function set($key, $value, $ttl = null){return $this->backend->set($key, $value, $ttl);}
|
||
|
public function exists($key){return $this->backend->exists($key);}
|
||
|
public function del($key){return $this->backend->del($key);}
|
||
|
public function inc($key, $value = null, $ttl = null){return $this->backend->inc($key, $value, $ttl);}
|
||
|
public function dec($key, $value = null, $ttl = null){return $this->backend->dec($key, $value, $ttl);}
|
||
|
public function lock($key){return $this->backend->lock($key);}
|
||
|
public function unlock($key){return $this->backend->unlock($key);}
|
||
|
public function size(){return $this->backend->size();}
|
||
|
public function purge(){return $this->backend->purge();}
|
||
|
|
||
|
/* Cache Editorial */
|
||
|
/**
|
||
|
* Recuperer de l'editorial cache, mais invalide avec la meta derniere_modif ou avec un var_mode
|
||
|
* @param string $key
|
||
|
* @return string
|
||
|
*/
|
||
|
public function edito_get($key){
|
||
|
if (!_VAR_MODE
|
||
|
and $cache = $this->get("::edito::{$key}")
|
||
|
and isset($cache['time'])
|
||
|
and isset($cache['value'])
|
||
|
and (!isset($GLOBALS['meta']['derniere_modif']) or $cache['time']>$GLOBALS['meta']['derniere_modif'])){
|
||
|
return $cache['value'];
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Stocker de l'editorial cache, avec un timestamp pour gerer l'invalidation
|
||
|
* @param string $key
|
||
|
* @param mixed $value
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function edito_set($key, $value){
|
||
|
$cache = array('value' => $value, 'time' => $_SERVER['REQUEST_TIME']);
|
||
|
$this->set("::edito::$key", $cache);
|
||
|
return $value;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Interface principale d'utilisation de Memoization
|
||
|
* @return MCache
|
||
|
*/
|
||
|
function memoization(): MCache{
|
||
|
static $Memoization = null;
|
||
|
if (is_null($Memoization)){
|
||
|
$Memoization = new MCache(MCache::config_methode());
|
||
|
}
|
||
|
return $Memoization;
|
||
|
}
|
||
|
|
||
|
/* Interface procedurale */
|
||
|
|
||
|
function cache_get($key){return memoization()->get($key);}
|
||
|
function cache_set($key, $value, $ttl = null): bool{return memoization()->set($key, $value, $ttl);}
|
||
|
function cache_exists($key): bool{return memoization()->exists($key);}
|
||
|
function cache_del($key): bool{return memoization()->del($key);}
|
||
|
function cache_inc($key, $value = null, $ttl = null): int{return memoization()->inc($key, $value, $ttl);}
|
||
|
function cache_dec($key, $value = null, $ttl = null): int{return memoization()->dec($key, $value, $ttl);}
|
||
|
function cache_lock($key){return memoization()->lock($key);}
|
||
|
function cache_unlock($key){return memoization()->unlock($key);}
|
||
|
function cache_purge(){return memoization()->purge();}
|
||
|
|
||
|
/**
|
||
|
* Cache Editorial : le contenu memoizé est invalidé quand le cache SPIP est invalidé
|
||
|
*/
|
||
|
|
||
|
function cache_edito_get($key){return memoization()->edito_get($key);}
|
||
|
function cache_edito_set($key, $value){return memoization()->edito_set($key, $value);}
|
||
|
|
||
|
/**
|
||
|
* Cache a function's result cache_me()
|
||
|
* (c) Fil 2009 - Double-licensed under the GNU/LGPL and MIT licenses
|
||
|
* http://zzz.rezo.net/-SPIP-
|
||
|
* Usage:
|
||
|
* In any cacheable function add at top:
|
||
|
* if(!is_null($c=cache_me())) return$c;
|
||
|
*/
|
||
|
if (!function_exists('debug_backtrace')){
|
||
|
function cache_me($vars = null, $ttl = 3600){
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
/**
|
||
|
* @param null|mixed $vars other variables that could change the result
|
||
|
* in addition to the function's args that are automatically taken into account
|
||
|
* @param int $ttl time to live
|
||
|
* @return false|mixed
|
||
|
*/
|
||
|
function cache_me($vars = null, $ttl = 3600){
|
||
|
// uniquement si on a une methode de cache rapide (en memoire)
|
||
|
if (!memoization()->is_memory()){
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
$trace = debug_backtrace();
|
||
|
$trace = $trace[1];
|
||
|
if (isset($trace['object'])){
|
||
|
$fun = [$trace['object'], $trace['function']];
|
||
|
} else {
|
||
|
$fun = $trace['function'];
|
||
|
}
|
||
|
|
||
|
$key = md5(json_encode(['function' => $fun, 'args' => $trace['args'], 'vars' => $vars]));
|
||
|
if (!memoization()->exists($key)){
|
||
|
memoization()->set($key, null, $ttl);
|
||
|
// eviter une boucle infinie par re-entrance si memoization est actif mais n'a pas de memoire dispo
|
||
|
if (!memoization()->exists($key)) {
|
||
|
return null;
|
||
|
}
|
||
|
$r = call_user_func_array($fun, $trace['args']);
|
||
|
memoization()->set($key, $r, $ttl);
|
||
|
return $r;
|
||
|
}
|
||
|
return memoization()->get($key);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Filtres pour le formulaire de configuration
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @param null|string $methode
|
||
|
* @return bool|string
|
||
|
*/
|
||
|
function memoization_methode($methode = null){
|
||
|
return MCache::methode_disponible($methode);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return array
|
||
|
*/
|
||
|
function memoization_redis_liste_serializer(){
|
||
|
$serializers = array();
|
||
|
if (defined('Redis::SERIALIZER_IGBINARY') && extension_loaded('igbinary')){
|
||
|
$serializers['igbinary'] = array('libelle' => 'SERIALIZER_IGBINARY', 'statut' => 'actif');
|
||
|
} else {
|
||
|
$serializers['igbinary'] = array('libelle' => 'SERIALIZER_IGBINARY', 'statut' => 'inactif');
|
||
|
}
|
||
|
$serializers['php'] = array('libelle' => 'SERIALIZER_PHP', 'statut' => 'actif');
|
||
|
return $serializers;
|
||
|
}
|