define('FRIENDICA_CODENAME', 'Asparagus');
define('FRIENDICA_VERSION', '3.6-dev');
define('DFRN_PROTOCOL_VERSION', '2.23');
-define('DB_UPDATE_VERSION', 1255);
+define('DB_UPDATE_VERSION', 1256);
define('NEW_UPDATE_ROUTINE_VERSION', 1170);
/**
*/
namespace Friendica\Core;
+use Friendica\Core\Cache;
use Friendica\Core\Config;
-use Friendica\Database\DBM;
-use Friendica\Util\DateTimeFormat;
-use dba;
-use Memcache;
-
-require_once 'include/dba.php';
/**
* @brief Class for storing data for a short time
*/
class Cache
{
+ const MONTH = 0;
+ const WEEK = 1;
+ const DAY = 2;
+ const HOUR = 3;
+ const HALF_HOUR = 4;
+ const QUARTER_HOUR = 5;
+ const FIVE_MINUTES = 6;
+ const MINUTE = 7;
+
/**
- * @brief Check for Memcache and open a connection if configured
- *
- * @return Memcache|boolean The Memcache object - or "false" if not successful
+ * @var Cache\ICacheDriver
*/
- public static function memcache()
- {
- if (!class_exists('Memcache', false)) {
- return false;
- }
-
- if (!Config::get('system', 'memcache')) {
- return false;
- }
+ static $driver = null;
- $memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
- $memcache_port = Config::get('system', 'memcache_port', 11211);
+ public static function init()
+ {
+ switch(Config::get('system', 'cache_driver', 'database')) {
+ case 'memcache':
+ $memcache_host = Config::get('system', 'memcache_host', '127.0.0.1');
+ $memcache_port = Config::get('system', 'memcache_port', 11211);
- $memcache = new Memcache();
+ self::$driver = new Cache\MemcacheCacheDriver($memcache_host, $memcache_port);
+ break;
+ case 'memcached':
+ $memcached_host = Config::get('system', 'memcached_host', '127.0.0.1');
+ $memcached_port = Config::get('system', 'memcached_port', 11211);
- if (!$memcache->connect($memcache_host, $memcache_port)) {
- return false;
+ self::$driver = new Cache\MemcachedCacheDriver($memcached_host, $memcached_port);
+ break;
+ default:
+ self::$driver = new Cache\DatabaseCacheDriver();
}
-
- return $memcache;
}
/**
*
* @return integer The cache duration in seconds
*/
- private static function duration($level)
+ public static function duration($level)
{
switch ($level) {
- case CACHE_MONTH:
+ case self::MONTH:
$seconds = 2592000;
break;
- case CACHE_WEEK:
+ case self::WEEK:
$seconds = 604800;
break;
- case CACHE_DAY:
+ case self::DAY:
$seconds = 86400;
break;
- case CACHE_HOUR:
+ case self::HOUR:
$seconds = 3600;
break;
- case CACHE_HALF_HOUR:
+ case self::HALF_HOUR:
$seconds = 1800;
break;
- case CACHE_QUARTER_HOUR:
+ case self::QUARTER_HOUR:
$seconds = 900;
break;
- case CACHE_FIVE_MINUTES:
+ case self::FIVE_MINUTES:
$seconds = 300;
break;
- case CACHE_MINUTE:
+ case self::MINUTE:
default:
$seconds = 60;
break;
return $seconds;
}
+ /**
+ * Returns the current cache driver
+ *
+ * @return Cache\ICacheDriver
+ */
+ private static function getDriver()
+ {
+ if (self::$driver === null) {
+ self::init();
+ }
+
+ return self::$driver;
+ }
+
/**
* @brief Fetch cached data according to the key
*
*/
public static function get($key)
{
- $memcache = self::memcache();
- if (is_object($memcache)) {
- // We fetch with the hostname as key to avoid problems with other applications
- $cached = $memcache->get(get_app()->get_hostname().":".$key);
- $value = @unserialize($cached);
-
- // Only return a value if the serialized value is valid.
- // We also check if the db entry is a serialized
- // boolean 'false' value (which we want to return).
- if ($cached === serialize(false) || $value !== false) {
- return $value;
- }
-
- return null;
- }
-
- // Frequently clear cache
- self::clear();
-
- $cache = dba::selectFirst('cache', ['v'], ['k' => $key]);
-
- if (DBM::is_result($cache)) {
- $cached = $cache['v'];
- $value = @unserialize($cached);
-
- // Only return a value if the serialized value is valid.
- // We also check if the db entry is a serialized
- // boolean 'false' value (which we want to return).
- if ($cached === serialize(false) || $value !== false) {
- return $value;
- }
- }
-
- return null;
+ return self::getDriver()->get($key);
}
/**
* @param mixed $value The value that is about to be stored
* @param integer $duration The cache lifespan
*
- * @return void
+ * @return bool
*/
- public static function set($key, $value, $duration = CACHE_MONTH)
+ public static function set($key, $value, $duration = self::MONTH)
{
- // Do we have an installed memcache? Use it instead.
- $memcache = self::memcache();
- if (is_object($memcache)) {
- // We store with the hostname as key to avoid problems with other applications
- $memcache->set(get_app()->get_hostname().":".$key, serialize($value), MEMCACHE_COMPRESSED, self::duration($duration));
- return;
- }
- $fields = ['v' => serialize($value), 'expire_mode' => $duration, 'updated' => DateTimeFormat::utcNow()];
- $condition = ['k' => $key];
- dba::update('cache', $fields, $condition, true);
+ return self::getDriver()->set($key, $value, $duration);
}
/**
*
* @return void
*/
- public static function clear($max_level = CACHE_MONTH)
+ public static function clear()
{
- // Clear long lasting cache entries only once a day
- if (Config::get("system", "cache_cleared_day") < time() - self::duration(CACHE_DAY)) {
- if ($max_level == CACHE_MONTH) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 30 days"),
- CACHE_MONTH];
- dba::delete('cache', $condition);
- }
-
- if ($max_level <= CACHE_WEEK) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 7 days"),
- CACHE_WEEK];
- dba::delete('cache', $condition);
- }
-
- if ($max_level <= CACHE_DAY) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 1 days"),
- CACHE_DAY];
- dba::delete('cache', $condition);
- }
- Config::set("system", "cache_cleared_day", time());
- }
-
- if (($max_level <= CACHE_HOUR) && (Config::get("system", "cache_cleared_hour")) < time() - self::duration(CACHE_HOUR)) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 1 hours"),
- CACHE_HOUR];
- dba::delete('cache', $condition);
-
- Config::set("system", "cache_cleared_hour", time());
- }
-
- if (($max_level <= CACHE_HALF_HOUR) && (Config::get("system", "cache_cleared_half_hour")) < time() - self::duration(CACHE_HALF_HOUR)) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 30 minutes"),
- CACHE_HALF_HOUR];
- dba::delete('cache', $condition);
-
- Config::set("system", "cache_cleared_half_hour", time());
- }
-
- if (($max_level <= CACHE_QUARTER_HOUR) && (Config::get("system", "cache_cleared_quarter_hour")) < time() - self::duration(CACHE_QUARTER_HOUR)) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 15 minutes"),
- CACHE_QUARTER_HOUR];
- dba::delete('cache', $condition);
-
- Config::set("system", "cache_cleared_quarter_hour", time());
- }
-
- if (($max_level <= CACHE_FIVE_MINUTES) && (Config::get("system", "cache_cleared_five_minute")) < time() - self::duration(CACHE_FIVE_MINUTES)) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 5 minutes"),
- CACHE_FIVE_MINUTES];
- dba::delete('cache', $condition);
-
- Config::set("system", "cache_cleared_five_minute", time());
- }
-
- if (($max_level <= CACHE_MINUTE) && (Config::get("system", "cache_cleared_minute")) < time() - self::duration(CACHE_MINUTE)) {
- $condition = ["`updated` < ? AND `expire_mode` = ?",
- DateTimeFormat::utc("now - 1 minutes"),
- CACHE_MINUTE];
- dba::delete('cache', $condition);
-
- Config::set("system", "cache_cleared_minute", time());
- }
+ return self::getDriver()->clear();
}
}
--- /dev/null
+<?php\r
+\r
+namespace Friendica\Core\Cache;\r
+\r
+use dba;\r
+use Friendica\Core\Cache;\r
+use Friendica\Database\DBM;\r
+use Friendica\Util\DateTimeFormat;\r
+\r
+/**\r
+ * Database Cache Driver\r
+ *\r
+ * @author Hypolite Petovan <mrpetovan@gmail.com>\r
+ */\r
+class DatabaseCacheDriver implements ICacheDriver\r
+{\r
+ public function get($key)\r
+ {\r
+ $cache = dba::selectFirst('cache', ['v'], ['`k` = ? AND `expires` >= NOW()`', $key]);\r
+\r
+ if (DBM::is_result($cache)) {\r
+ $cached = $cache['v'];\r
+ $value = @unserialize($cached);\r
+\r
+ // Only return a value if the serialized value is valid.\r
+ // We also check if the db entry is a serialized\r
+ // boolean 'false' value (which we want to return).\r
+ if ($cached === serialize(false) || $value !== false) {\r
+ return $value;\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ public function set($key, $value, $duration = Cache::MONTH)\r
+ {\r
+ $fields = [\r
+ 'v' => serialize($value),\r
+ 'expires' => DateTimeFormat::utc('now + ' . Cache::duration($duration) . ' seconds'),\r
+ 'updated' => DateTimeFormat::utcNow()\r
+ ];\r
+\r
+ return dba::update('cache', $fields, ['k' => $key], true);\r
+ }\r
+\r
+ public function delete($key)\r
+ {\r
+ return dba::delete('cache', ['k' => $key]);\r
+ }\r
+\r
+ public function clear()\r
+ {\r
+ return dba::delete('cache', ['`expires` < NOW()']);\r
+ }\r
+}\r
--- /dev/null
+<?php\r
+\r
+namespace Friendica\Core\Cache;\r
+\r
+use Friendica\Core\Cache;\r
+\r
+/**\r
+ * Cache Driver Interface\r
+ *\r
+ * @author Hypolite Petovan <mrpetovan@gmail.com>\r
+ */\r
+interface ICacheDriver\r
+{\r
+ /**\r
+ * Fetches cached data according to the key\r
+ *\r
+ * @param string $key The key to the cached data\r
+ *\r
+ * @return mixed Cached $value or "null" if not found\r
+ */\r
+ public function get($key);\r
+\r
+ /**\r
+ * Stores data in the cache identified by the key. The input $value can have multiple formats.\r
+ *\r
+ * @param string $key The cache key\r
+ * @param mixed $value The value to store\r
+ * @param integer $duration The cache lifespan, must be one of the Cache constants\r
+ *\r
+ * @return bool\r
+ */\r
+ public function set($key, $value, $duration = Cache::MONTH);\r
+\r
+\r
+ /**\r
+ * Delete a key from the cache\r
+ *\r
+ * @param string $key\r
+ *\r
+ * @return bool\r
+ */\r
+ public function delete($key);\r
+\r
+ /**\r
+ * Remove outdated data from the cache\r
+ *\r
+ * @return bool\r
+ */\r
+ public function clear();\r
+}\r
--- /dev/null
+<?php\r
+\r
+namespace Friendica\Core\Cache;\r
+\r
+use Friendica\BaseObject;\r
+use Friendica\Core\Cache;\r
+\r
+/**\r
+ * Memcache Cache Driver\r
+ *\r
+ * @author Hypolite Petovan <mrpetovan@gmail.com>\r
+ */\r
+class MemcacheCacheDriver extends BaseObject implements ICacheDriver\r
+{\r
+ /**\r
+ * @var Memcache\r
+ */\r
+ private $memcache;\r
+\r
+ public function __construct($memcache_host, $memcache_port)\r
+ {\r
+ if (!class_exists('Memcache', false)) {\r
+ throw new \Exception('Memcache class isn\'t available');\r
+ }\r
+\r
+ $this->memcache = new \Memcache();\r
+\r
+ if (!$this->memcache->connect($memcache_host, $memcache_port)) {\r
+ throw new \Exception('Expected Memcache server at ' . $memcache_host . ':' . $memcache_port . ' isn\'t available');\r
+ }\r
+ }\r
+\r
+ public function get($key)\r
+ {\r
+ $return = null;\r
+\r
+ // We fetch with the hostname as key to avoid problems with other applications\r
+ $cached = $this->memcache->get(self::getApp()->get_hostname() . ':' . $key);\r
+\r
+ // @see http://php.net/manual/en/memcache.get.php#84275\r
+ if (is_bool($cached) || is_double($cached) || is_long($cached)) {\r
+ return $return;\r
+ }\r
+\r
+ $value = @unserialize($cached);\r
+\r
+ // Only return a value if the serialized value is valid.\r
+ // We also check if the db entry is a serialized\r
+ // boolean 'false' value (which we want to return).\r
+ if ($cached === serialize(false) || $value !== false) {\r
+ $return = $value;\r
+ }\r
+\r
+ return $return;\r
+ }\r
+\r
+ public function set($key, $value, $duration = Cache::MONTH)\r
+ {\r
+ // We store with the hostname as key to avoid problems with other applications\r
+ return $this->memcache->set(\r
+ self::getApp()->get_hostname() . ":" . $key,\r
+ serialize($value),\r
+ MEMCACHE_COMPRESSED,\r
+ Cache::duration($duration)\r
+ );\r
+ }\r
+\r
+ public function delete($key)\r
+ {\r
+ return $this->memcache->delete($key);\r
+ }\r
+\r
+ public function clear()\r
+ {\r
+ return true;\r
+ }\r
+}\r
--- /dev/null
+<?php\r
+\r
+namespace Friendica\Core\Cache;\r
+\r
+use Friendica\BaseObject;\r
+use Friendica\Core\Cache;\r
+\r
+/**\r
+ * Memcached Cache Driver\r
+ *\r
+ * @author Hypolite Petovan <mrpetovan@gmail.com>\r
+ */\r
+class MemcachedCacheDriver extends BaseObject implements ICacheDriver\r
+{\r
+ /**\r
+ * @var Memcached\r
+ */\r
+ private $memcached;\r
+\r
+ public function __construct($memcached_host, $memcached_port)\r
+ {\r
+ if (!class_exists('Memcached', false)) {\r
+ throw new \Exception('Memcached class isn\'t available');\r
+ }\r
+\r
+ $this->memcached = new \Memcached();\r
+\r
+ if (!$this->memcached->addServer($memcached_host, $memcached_port)) {\r
+ throw new \Exception('Expected Memcached server at ' . $memcached_host . ':' . $memcached_port . ' isn\'t available');\r
+ }\r
+ }\r
+\r
+ public function get($key)\r
+ {\r
+ $return = null;\r
+\r
+ // We fetch with the hostname as key to avoid problems with other applications\r
+ $value = $this->memcached->get(self::getApp()->get_hostname() . ':' . $key);\r
+\r
+ if ($this->memcached->getResultCode() === \Memcached::RES_SUCCESS) {\r
+ $return = $value;\r
+ }\r
+\r
+ return $return;\r
+ }\r
+\r
+ public function set($key, $value, $duration = Cache::MONTH)\r
+ {\r
+ // We store with the hostname as key to avoid problems with other applications\r
+ return $this->memcached->set(\r
+ self::getApp()->get_hostname() . ":" . $key,\r
+ $value,\r
+ Cache::duration($duration)\r
+ );\r
+ }\r
+\r
+ public function delete($key)\r
+ {\r
+ return $this->memcached->delete($key);\r
+ }\r
+\r
+ public function clear()\r
+ {\r
+ return true;\r
+ }\r
+}\r
*/
namespace Friendica\Core;
+use Friendica\Core\Session\CacheSessionHandler;
use Friendica\Core\Session\DatabaseSessionHandler;
-use Friendica\Core\Session\MemcacheSessionHandler;
/**
* High-level Session service class
ini_set('session.cookie_secure', 1);
}
- if (!Config::get('system', 'disable_database_session')) {
- $memcache = Cache::memcache();
- if (is_object($memcache)) {
- $SessionHandler = new MemcacheSessionHandler($memcache);
+ $session_handler = Config::get('system', 'session_handler', 'database');
+ if ($session_handler != 'native') {
+ if ($session_handler == 'cache' && Config::get('system', 'cache_driver', 'database') != 'database') {
+ $SessionHandler = new CacheSessionHandler();
} else {
$SessionHandler = new DatabaseSessionHandler();
}
--- /dev/null
+<?php
+
+namespace Friendica\Core\Session;
+
+use Friendica\BaseObject;
+use Friendica\Core\Cache;
+use Friendica\Core\Session;
+use SessionHandlerInterface;
+
+require_once 'boot.php';
+require_once 'include/text.php';
+
+/**
+ * SessionHandler using Friendica Cache
+ *
+ * @author Hypolite Petovan <mrpetovan@gmail.com>
+ */
+class CacheSessionHandler extends BaseObject implements SessionHandlerInterface
+{
+ public function open($save_path, $session_name)
+ {
+ return true;
+ }
+
+ public function read($session_id)
+ {
+ if (!x($session_id)) {
+ return '';
+ }
+
+ $data = Cache::get('session:' . $session_id);
+ if (!empty($data)) {
+ Session::$exists = true;
+ return $data;
+ }
+ logger("no data for session $session_id", LOGGER_TRACE);
+ return '';
+ }
+
+ /**
+ * @brief Standard PHP session write callback
+ *
+ * This callback updates the stored session data and/or the expiration depending
+ * on the case. Uses the Session::expire for existing session, 5 minutes
+ * for newly created session.
+ *
+ * @param string $session_id Session ID with format: [a-z0-9]{26}
+ * @param string $session_data Serialized session data
+ * @return boolean Returns false if parameters are missing, true otherwise
+ */
+ public function write($session_id, $session_data)
+ {
+ if (!$session_id) {
+ return false;
+ }
+
+ if (!$session_data) {
+ return true;
+ }
+
+ Cache::set('session:' . $session_id, $session_data, Session::$expire);
+
+ return true;
+ }
+
+ public function close()
+ {
+ return true;
+ }
+
+ public function destroy($id)
+ {
+ Cache::delete('session:' . $id);
+ return true;
+ }
+
+ public function gc($maxlifetime)
+ {
+ return true;
+ }
+}
+++ /dev/null
-<?php
-
-namespace Friendica\Core\Session;
-
-use Friendica\BaseObject;
-use Friendica\Core\Session;
-use SessionHandlerInterface;
-use Memcache;
-
-require_once 'boot.php';
-require_once 'include/text.php';
-
-/**
- * SessionHandler using Memcache
- *
- * @author Hypolite Petovan <mrpetovan@gmail.com>
- */
-class MemcacheSessionHandler extends BaseObject implements SessionHandlerInterface
-{
- /**
- * @var Memcache
- */
- private $memcache = null;
-
- /**
- *
- * @param Memcache $memcache
- */
- public function __construct(Memcache $memcache)
- {
- $this->memcache = $memcache;
- }
-
- public function open($save_path, $session_name)
- {
- return true;
- }
-
- public function read($session_id)
- {
- if (!x($session_id)) {
- return '';
- }
-
- $data = $this->memcache->get(self::getApp()->get_hostname() . ":session:" . $session_id);
- if (!is_bool($data)) {
- Session::$exists = true;
- return $data;
- }
- logger("no data for session $session_id", LOGGER_TRACE);
- return '';
- }
-
- /**
- * @brief Standard PHP session write callback
- *
- * This callback updates the stored session data and/or the expiration depending
- * on the case. Uses the Session::expire for existing session, 5 minutes
- * for newly created session.
- *
- * @param string $session_id Session ID with format: [a-z0-9]{26}
- * @param string $session_data Serialized session data
- * @return boolean Returns false if parameters are missing, true otherwise
- */
- public function write($session_id, $session_data)
- {
- if (!$session_id) {
- return false;
- }
-
- if (!$session_data) {
- return true;
- }
-
- $expire = time() + Session::$expire;
-
- $this->memcache->set(
- self::getApp()->get_hostname() . ":session:" . $session_id,
- $session_data,
- MEMCACHE_COMPRESSED,
- $expire
- );
-
- return true;
- }
-
- public function close()
- {
- return true;
- }
-
- public function destroy($id)
- {
- $this->memcache->delete(self::getApp()->get_hostname() . ":session:" . $id);
- return true;
- }
-
- public function gc($maxlifetime)
- {
- return true;
- }
-}
]
];
$database["cache"] = [
- "comment" => "Used to store different data that doesn't to be stored for a long time",
+ "comment" => "Stores temporary data",
"fields" => [
- "k" => ["type" => "varbinary(255)", "not null" => "1", "primary" => "1", "comment" => ""],
- "v" => ["type" => "mediumtext", "comment" => ""],
- "expire_mode" => ["type" => "tinyint unsigned", "not null" => "1", "default" => "0", "comment" => ""],
- "updated" => ["type" => "datetime", "not null" => "1", "default" => NULL_DATE, "comment" => ""],
+ "k" => ["type" => "varbinary(255)", "not null" => "1", "primary" => "1", "comment" => "cache key"],
+ "v" => ["type" => "mediumtext", "comment" => "cached serialized value"],
+ "expires" => ["type" => "datetime", "not null" => "1", "default" => NULL_DATE, "comment" => "datetime of cache expiration"],
+ "updated" => ["type" => "datetime", "not null" => "1", "default" => NULL_DATE, "comment" => "datetime of cache insertion"],
],
"indexes" => [
"PRIMARY" => ["k"],
- "expire_mode_updated" => ["expire_mode", "updated"],
+ "k_expires" => ["k", "expires"],
]
];
$database["challenge"] = [