You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

619 lines
20 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
/**
* \brief A basic library to handle a cache of some Tiki Objects,
* usage is simple and feel free to improve it
*/
class Cachelib
{
private $implementation;
public function __construct()
{
global $prefs;
if (isset($prefs['memcache_enabled']) && $prefs['memcache_enabled'] == 'y') {
$this->implementation = new CacheLibMemcache();
} elseif (isset($prefs['redis_enabled']) && $prefs['redis_enabled'] == 'y') {
require_once('lib/cache/redislib.php');
$this->implementation = new CacheLibRedis();
} else {
$this->implementation = new CacheLibFileSystem();
}
}
public function replaceImplementation($implementation)
{
$old = $this->implementation;
$this->implementation = $implementation;
return $old;
}
public function cacheItem($key, $data, $type = '')
{
return $this->implementation->cacheItem($key, $data, $type);
}
public function isCached($key, $type = '')
{
return $this->implementation->isCached($key, $type);
}
public function getCached($key, $type = '', $lastModif = false)
{
return $this->implementation->getCached($key, $type, $lastModif);
}
public function getSerialized($key, $type = '', $lastModif = false)
{
$data = $this->getCached($key, $type, $lastModif);
if ($data) {
return unserialize($data);
}
}
public function invalidate($key, $type = '')
{
return $this->implementation->invalidate($key, $type);
}
/**
* Empty one or more caches
*
* Checks for existance of libs because it's called from the installer
*
* @param mixed $dir_names all|templates_c|temp_cache|temp_public|modules_cache|prefs (default all)
* @param string $log_section Type of log message. Default 'system'
*/
public function empty_cache($dir_names = ['all'], $log_section = 'system')
{
global $tikidomain, $prefs;
$logslib = TikiLib::lib('logs');
$inInstaller = defined('TIKI_IN_INSTALLER');
if (! is_array($dir_names)) {
$dir_names = [$dir_names];
}
if (in_array('all', $dir_names)) {
$this->erase_dir_content("temp/templates_c/$tikidomain");
$this->erase_dir_content("temp/public/$tikidomain");
$this->erase_dir_content("temp/cache/$tikidomain");
$banner = glob("temp/banner*.*");
array_map('unlink', $banner);
$banner = glob("temp/TMPIMG*");
array_map('unlink', $banner);
$this->flush_opcode_cache();
$this->flush_memcache();
$this->flush_redis();
$this->invalidate('global_preferences');
if (! $inInstaller) {
$logslib->add_log($log_section, 'erased all cache content');
}
}
if (in_array('templates_c', $dir_names)) {
$this->erase_dir_content("temp/templates_c/$tikidomain");
$this->flush_opcode_cache();
if (! $inInstaller) {
$logslib->add_log($log_section, 'erased templates_c content');
}
}
if (in_array('temp_cache', $dir_names)) {
$this->erase_dir_content("temp/cache/$tikidomain");
// Next case is needed to clean also cached data created through mod PluginR
if ((isset($prefs['wikiplugin_rr']) && $prefs['wikiplugin_rr'] == 'y') or (isset($prefs['wikiplugin_r']) && $prefs['wikiplugin_r'] == 'y')) {
$this->erase_dir_content("temp/cache/$tikidomain/R_*/");
}
$this->flush_redis();
if (! $inInstaller) {
$logslib->add_log($log_section, 'erased temp/cache content');
}
}
if (in_array('temp_public', $dir_names)) {
$this->erase_dir_content("temp/public/$tikidomain");
if (! $inInstaller) {
$logslib->add_log($log_section, 'erased temp/public content');
}
}
if (in_array('prefs', $dir_names)) {
$this->invalidate('global_preferences');
}
}
public function empty_type_cache($type)
{
return $this->implementation->empty_type_cache($type);
}
public function count_cache_files($path, $begin = null)
{
global $tikidomain;
if (! $path or ! is_dir($path)) {
return (['total' => 0,'cant' => 0]);
}
$total = 0;
$cant = 0;
$back = [];
$all = opendir($path);
// If using multiple Tikis but flushing cache on default install...
if (empty($tikidomain) && is_file('db/virtuals.inc')) {
$virtuals = array_map('trim', file('db/virtuals.inc'));
} else {
$virtuals = false;
}
while ($file = readdir($all)) {
if (
substr($file, 0, 1) == "." or
$file == 'CVS' or
$file == '.svn' or
$file == "index.php" or
$file == "README" or
$file == "web.config" or
($virtuals && in_array($file, $virtuals))
) {
continue;
}
if (is_dir($path . '/' . $file) and $file <> ".." and $file <> "." and $file <> "CVS" and $file <> ".svn") {
$du = $this->count_cache_files($path . '/' . $file);
$total += $du['total'];
$cant += $du['cant'];
unset($file);
} elseif (! is_dir($path . '/' . $file)) {
if (isset($begin) && substr($file, 0, strlen($begin)) != $begin) {
continue; // the file name doesn't begin with the good beginning
}
$stats = @stat($path . '/' . $file); // avoid the warning if safe mode on
$total += $stats['size'];
$cant++;
unset($file);
}
}
closedir($all);
unset($all);
$back['total'] = $total;
$back['cant'] = $cant;
return $back;
}
public function flush_opcode_cache()
{
if (function_exists('apc_clear_cache')) {
apc_clear_cache();
}
if (function_exists('xcache_clear_cache') && ! ini_get('xcache.admin.enable_auth')) {
foreach (range(0, xcache_count(XC_TYPE_PHP) - 1) as $index) {
xcache_clear_cache(XC_TYPE_PHP, $index);
}
}
}
/**
* Flush memcache if endabled
*
* @return void
*/
public function flush_memcache()
{
global $prefs;
if (isset($prefs['memcache_enabled']) && $prefs['memcache_enabled'] == 'y') {
$memcachelib = TikiLib::lib("memcache");
if ($memcachelib->isEnabled()) {
$memcachelib->flush();
}
}
return;
}
public function flush_redis()
{
global $prefs;
if (isset($prefs['redis_enabled']) && $prefs['redis_enabled'] == 'y') {
$this->implementation->flush();
}
return;
}
public function erase_dir_content($path)
{
global $tikidomain, $prefs;
$path = rtrim($path, '/');
if (! $path or ! is_dir($path)) {
return 0;
}
if ($dir = opendir($path)) {
// If using multiple Tikis but flushing cache on default install...
if (empty($tikidomain) && is_file('db/virtuals.inc')) {
$virtuals = array_map('trim', file('db/virtuals.inc'));
} else {
$virtuals = false;
}
// Next case is needed to clean also cached data created through mod PluginR
if (
(isset($prefs['wikiplugin_rr']) && $prefs['wikiplugin_rr'] == 'y') ||
(isset($prefs['wikiplugin_r']) && $prefs['wikiplugin_r'] == 'y')
) {
$extracheck = 'RData';
} else {
$extracheck = '';
}
// Folders created by unoconv/libreoffice that should be removed
$unoconvFolders = ['.cache', '.config'];
while (false !== ($file = readdir($dir))) {
if (
// .RData case needed to clean also cached data created through mod PluginR
( substr($file, 0, 1) == "." && substr($file, -5) != $extracheck ) or
$file == 'CVS' or
$file == '.svn' or
$file == "index.php" or
$file == "README" or
$file == "web.config" or
($virtuals && in_array($file, $virtuals)) and
! in_array($file, $unoconvFolders)
) {
continue;
}
if (is_dir($path . "/" . $file)) {
$this->erase_dir_content($path . "/" . $file);
@rmdir($path . "/" . $file); // dir won't be empty if there are multitiki dirs inside
} else {
try {
unlink($path . "/" . $file);
} catch(Error $e) {
Feedback::error(tr('Cache file %0 is not writable (%1)', $path . "/" . $file, $e->getMessage()));
}
}
}
closedir($dir);
}
}
public function cache_templates($path, $newlang)
{
global $prefs;
$smarty = TikiLib::lib('smarty');
$smarty->refreshLanguage();
$oldlang = $prefs['language'];
$prefs['language'] = $newlang;
if (! $path or ! is_dir($path)) {
return 0;
}
if ($dir = opendir($path)) {
while (false !== ($file = readdir($dir))) {
$a = explode(".", $file);
$ext = strtolower(end($a));
if (substr($file, 0, 1) == "." or $file == 'CVS') {
continue;
}
if (is_dir($path . "/" . $file)) {
$prefs['language'] = $oldlang;
$this->cache_templates($path . "/" . $file, $newlang);
$prefs['language'] = $newlang;
} else {
if ($ext == "tpl") {
$template_file = substr($path . "/" . $file, 10);
try {
$_tpl = $smarty->createTemplate($template_file, null, null, null, false);
$_tpl->compileTemplateSource();
} catch (Exception $e) {
$errors_found = true;
}
}
}
}
closedir($dir);
}
$prefs['language'] = $oldlang;
}
/**
* Generates caches for templates, modules and other features.
*
* @param mixed $dir_names all|templates_c|modules_cache|misc (default all)
*
* @throws Exception
*/
public function generateCache($dir_names = ['all'])
{
if (! is_array($dir_names)) {
$dir_names = [$dir_names];
}
if (in_array('all', $dir_names)) {
$this->generateTemplateCache();
$this->generateModuleCache();
$this->generateMiscCache();
}
if (in_array('templates', $dir_names)) {
$this->generateTemplateCache();
}
if (in_array('modules', $dir_names)) {
$this->generateModuleCache();
}
if (in_array('misc', $dir_names)) {
$this->generateMiscCache();
}
}
/**
* Compile all Smarty templates
* @param string $logSection Section to log the request
*/
protected function generateTemplateCache($logSection = 'system')
{
global $prefs;
$logslib = TikiLib::lib('logs');
$inInstaller = defined('TIKI_IN_INSTALLER');
$lang = $prefs['language'];
$ctempl = 'templates';
$this->cache_templates($ctempl, $lang);
if (! $inInstaller) {
$logslib->add_log($logSection, 'generated templates cache, language = ' . $lang);
}
}
/**
* Compile all module cache
* @param string $logSection Section to log the request
*/
protected function generateModuleCache($logSection = 'system')
{
$logslib = TikiLib::lib('logs');
$modlib = TikiLib::lib('mod');
$inInstaller = defined('TIKI_IN_INSTALLER');
$assigned_modules = $modlib->get_assigned_modules();
foreach ($assigned_modules as $zone => $modules) {
foreach ($modules as $pos => $module) {
/** Pre-execute module to cache its content */
$result = $modlib->execute_module($module);
if (! $inInstaller) {
$logslib->add_log($logSection, 'generated module-cache for ' . $module['name']);
}
}
}
}
/**
* Compile Misc caches like language, categories, users.
*
* @param string $logSection
*
* @throws Exception
*/
protected function generateMiscCache($logSection = 'system')
{
$logslib = TikiLib::lib('logs');
$inInstaller = defined('TIKI_IN_INSTALLER');
TikiLib::lib('language')->list_languages();
if (! $inInstaller) {
$logslib->add_log($logSection, 'cached language list');
}
TikiLib::lib('categ')->getCategories();
if (! $inInstaller) {
$logslib->add_log($logSection, 'cached category list');
}
TikiLib::lib('user')->list_all_users();
TikiLib::lib('user')->list_all_groups();
TikiLib::lib('user')->list_all_groupIds();
if (! $inInstaller) {
$logslib->add_log($logSection, 'cached user/group list');
}
}
public function get_cache_purge_rules($type = 'all')
{
if ($this->isCached($type, 'cachepurgerules')) {
return $this->getSerialized($type, 'cachepurgerules');
}
if ($type != 'all') {
$rules = TikiLib::lib('tiki')->fetchAll("select * from tiki_object_relations where relation = 'tiki.cache.purge' and source_type = ?", array($type));
} else {
$rules = TikiLib::lib('tiki')->fetchAll("select * from tiki_object_relations where relation = 'tiki.cache.purge'");
}
$this->cacheItem($type, serialize($rules), 'cachepurgerules');
return $rules;
}
public function get_purge_rules_for_cache($cacheType, $cacheKey)
{
return TikiLib::lib('tiki')->fetchAll("select source_type as type, source_itemId as object from tiki_object_relations where relation = 'tiki.cache.purge' and target_type = ? and target_itemId = ?", array($cacheType, $cacheKey));
}
public function clear_purge_rules_for_cache($cacheType, $cacheKey)
{
return TikiLib::lib('tiki')->query("delete from tiki_object_relations where relation = 'tiki.cache.purge' and target_type = ? and target_itemId = ?", array($cacheType, $cacheKey));
}
public function set_cache_purge_rule($type, $object, $cacheType, $cacheKey)
{
$relationId = TikiLib::lib('relation')->add_relation('tiki.cache.purge', $type, $object, $cacheType, $cacheKey, true);
if ($relationId) {
// Rule is added (if it already existed, nothing would have happened)
$this->invalidate($type, 'cachepurgerules');
}
return $relationId;
}
public function invalidate_by_cache_purge_rules($args)
{
// First get all candidates which match type (source_itemId does not matter for now - see below)
$cache_purge_rules = $this->get_cache_purge_rules($args['type']);
foreach ($cache_purge_rules as $c) {
if ($c['source_itemId'] == $args['object'] || $c['source_itemId'] == 0) {
TikiLib::lib('cache')->invalidate($c['target_itemId'], $c['target_type']);
} elseif ($args['type'] != 'wiki page' && $colonpos = strpos($c['source_itemId'], ':')) {
// Examples: trackeritem:20, trackerId:3, galleryId:5, forum_id:7, parent_id:8 etc...
$prefix = substr($c['source_itemId'], 0, $colonpos);
$itemId = substr($c['source_itemId'], $colonpos + 1);
if (isset($args[$prefix]) && $itemId == $args[$prefix]) {
TikiLib::lib('cache')->invalidate($c['target_itemId'], $c['target_type']);
}
}
}
}
}
class CacheLibFileSystem
{
public $folder;
public function __construct()
{
global $tikidomain;
$this->folder = realpath("temp/cache");
if ($tikidomain) {
$this->folder .= "/$tikidomain";
}
if (! is_dir($this->folder)) {
mkdir($this->folder);
chmod($this->folder, 0777);
}
}
public function cacheItem($key, $data, $type = '')
{
$key = $type . md5($key);
@file_put_contents($this->folder . "/$key", $data);
return true;
}
public function isCached($key, $type = '')
{
$key = $type . md5($key);
return is_file($this->folder . "/$key");
}
public function getCached($key, $type = '', $lastModif = false)
{
$key = $type . md5($key);
$file = $this->folder . "/$key";
if (is_readable($file)) {
// If a last date is given for cache validity, make sure the file is younger
if ($lastModif !== false && filemtime($file) < $lastModif) {
unlink($file);
return false;
}
return @file_get_contents($file);
} else {
return false;
}
}
public function invalidate($key, $type = '')
{
$key = $type . md5($key);
if (is_file($this->folder . "/$key")) {
unlink($this->folder . "/$key");
}
}
public function empty_type_cache($type)
{
$path = $this->folder;
$all = opendir($path);
while ($file = readdir($all)) {
if (strpos($file, $type) === 0) {
unlink("$path/$file");
}
}
}
}
class CacheLibMemcache
{
private function getKey($key, $type)
{
return $type . md5($key);
}
public function cacheItem($key, $data, $type = '')
{
TikiLib::lib("memcache")->set($this->getKey($key, $type), $data);
return true;
}
public function isCached($key, $type = '')
{
return false;
}
public function getCached($key, $type = '', $lastModif = false)
{
return TikiLib::lib("memcache")->get($this->getKey($key, $type));
}
public function invalidate($key, $type = '')
{
return TikiLib::lib("memcache")->delete($this->getKey($key, $type));
}
public function empty_type_cache($type)
{
return TikiLib::lib("memcache")->flush();
}
}
class CacheLibNoCache
{
public function cacheItem($key, $data, $type = '')
{
return false;
}
public function isCached($key, $type = '')
{
return false;
}
public function getCached($key, $type = '', $lastModif = false)
{
return false;
}
public function invalidate($key, $type = '')
{
return false;
}
public function empty_type_cache($type)
{
return false;
}
}