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; }