<?php
namespace wcf\system\user\storage;
+use wcf\system\cache\source\RedisCacheSource;
+use wcf\system\cache\CacheHandler;
use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\SingletonFactory;
use wcf\system\WCF;
*/
protected $updateFields = [];
+ /**
+ * redis instance
+ * @var Redis
+ */
+ protected $redis = null;
+
+ /**
+ * Checks whether Redis is available.
+ */
+ protected function init() {
+ if (CacheHandler::getInstance()->getCacheSource() instanceof RedisCacheSource) {
+ $this->redis = CacheHandler::getInstance()->getCacheSource()->getRedis();
+ }
+ }
+
/**
* Loads storage for a given set of users.
*
* @param integer[] $userIDs
*/
public function loadStorage(array $userIDs) {
+ if ($this->redis) return;
+
$tmp = [];
foreach ($userIDs as $userID) {
if (!isset($this->cache[$userID])) $tmp[] = $userID;
public function getStorage(array $userIDs, $field) {
$data = [];
+ if ($this->redis) {
+ foreach ($userIDs as $userID) {
+ $data[$userID] = $this->redis->hget($this->getRedisFieldName($field), $userID);
+ if ($data[$userID] === false) $data[$userID] = null;
+ }
+
+ return $data;
+ }
+
foreach ($userIDs as $userID) {
if (isset($this->cache[$userID][$field])) {
$data[$userID] = $this->cache[$userID][$field];
return null;
}
+ if ($this->redis) {
+ $result = $this->redis->hget($this->getRedisFieldName($field), $userID);
+ if ($result === false) return null;
+ return $result;
+ }
+
// make sure stored data is loaded
if (!isset($this->cache[$userID])) {
$this->loadStorage([$userID]);
* @param string $fieldValue
*/
public function update($userID, $field, $fieldValue) {
+ if ($this->redis) {
+ $this->redis->hset($this->getRedisFieldName($field), $userID, $fieldValue);
+ $this->redis->expire($this->getRedisFieldName($field), 86400);
+ return;
+ }
+
$this->updateFields[$userID][$field] = $fieldValue;
-
+
// update data cache for given user
if (isset($this->cache[$userID])) {
$this->cache[$userID][$field] = $fieldValue;
* @param string $field
*/
public function reset(array $userIDs, $field) {
+ if ($this->redis) {
+ foreach ($userIDs as $userID) {
+ $this->redis->hdel($this->getRedisFieldName($field), $userID);
+ }
+ return;
+ }
+
foreach ($userIDs as $userID) {
$this->resetFields[$userID][] = $field;
* @param string $field
*/
public function resetAll($field) {
+ if ($this->redis) {
+ $this->redis->del($this->getRedisFieldName($field));
+ return;
+ }
+
$sql = "DELETE FROM wcf".WCF_N."_user_storage
WHERE field = ?";
$statement = WCF::getDB()->prepareStatement($sql);
* Removes and inserts data records on shutdown.
*/
public function shutdown() {
+ if ($this->redis) return;
+
$toReset = [];
// remove outdated entries
$conditions = new PreparedStatementConditionBuilder();
$conditions->add("userID IN (?)", [$userIDs]);
$conditions->add("field = ?", [$field]);
-
+
$sql = "DELETE FROM wcf".WCF_N."_user_storage
".$conditions;
$statement = WCF::getDB()->prepareStatement($sql);
* Removes the entire user storage data.
*/
public function clear() {
+ if ($this->redis) {
+ $this->redis->setnx('ush:_flush', TIME_NOW);
+ $this->redis->incr('ush:_flush');
+ return;
+ }
+
$this->resetFields = $this->updateFields = [];
$sql = "DELETE FROM wcf".WCF_N."_user_storage";
$statement = WCF::getDB()->prepareStatement($sql);
$statement->execute();
}
+
+ /**
+ * Returns the field name for use in Redis.
+ *
+ * @param string $fieldName
+ * @return string
+ */
+ protected function getRedisFieldName($fieldName) {
+ $flush = $this->redis->get('ush:_flush');
+
+ // create flush counter if it does not exist
+ if ($flush === false) {
+ $this->redis->setnx('ush:_flush', TIME_NOW);
+ $this->redis->incr('ush:_flush');
+
+ $flush = $this->redis->get('ush:_flush');
+ }
+
+ return 'ush:'.$flush.':'.$fieldName;
+ }
}