add Redis for cache and Mysqli for database query

This commit is contained in:
xiaomlove
2020-12-26 20:09:15 +08:00
parent a3ba82be64
commit c961f32bb3
9 changed files with 582 additions and 16 deletions

View File

@@ -0,0 +1,304 @@
<?php
class RedisCache {
public $isEnabled;
public $clearCache = 0;
public $language = 'en';
public $Page = array();
public $Row = 1;
public $Part = 0;
public $MemKey = "";
public $Duration = 0;
public $cacheReadTimes = 0;
public $cacheWriteTimes = 0;
public $keyHits = array();
public $languageFolderArray = array();
/** @var Redis */
public $redis;
function __construct() {
$success = $this->createRedisClient(); // Connect to Redis
if ($success) {
$this->isEnabled = 1;
} else {
$this->isEnabled = 0;
}
}
private function createRedisClient()
{
global $BASIC;
$redis = new Redis();
$params = [
$BASIC['redis_host'],
];
if (!empty($BASIC['redis_port'])) {
$params[] = $BASIC['redis_port'];
}
if (!empty($BASIC['redis_timeout'])) {
$params[] = $BASIC['redis_timeout'];
}
$connectResult = $redis->connect(...$params);
$auth = [];
if (!empty($BASIC['redis_password'])) {
$auth['pass'] = $BASIC['redis_password'];
if (!empty($BASIC['redis_username'])) {
$auth['user'] = $BASIC['redis_username'];
}
$connectResult = $connectResult && $redis->auth($auth);
}
if ($connectResult) {
if (is_numeric($BASIC['redis_database'])) {
$selectDatabaseResult = $redis->select($BASIC['redis_database']);
if (!$selectDatabaseResult) {
$msg = "select redis database: {$BASIC['redis_database']} fail";
write_log($msg);
throw new \RuntimeException($msg);
}
}
$this->redis = $redis;
} else {
write_log(sprintf('connect to redis with params: %s , with auth: %s fail', json_encode($params), json_encode($auth)));
}
return $connectResult;
}
function getIsEnabled() {
return $this->isEnabled;
}
function setClearCache($isEnabled) {
$this->clearCache = $isEnabled;
}
function getLanguageFolderArray() {
return $this->languageFolderArray;
}
function setLanguageFolderArray($languageFolderArray) {
$this->languageFolderArray = $languageFolderArray;
}
function getClearCache() {
return $this->clearCache;
}
function setLanguage($language) {
$this->language = $language;
}
function getLanguage() {
return $this->language;
}
function new_page($MemKey = '', $Duration = 3600, $Lang = true) {
if ($Lang) {
$language = $this->getLanguage();
$this->MemKey = $language."_".$MemKey;
} else {
$this->MemKey = $MemKey;
}
$this->Duration = $Duration;
$this->Row = 1;
$this->Part = 0;
$this->Page = array();
}
function set_key(){
}
//---------- Adding functions ----------//
function add_row(){
$this->Part = 0;
$this->Page[$this->Row] = array();
}
function end_row(){
$this->Row++;
}
function add_part(){
ob_start();
}
function end_part(){
$this->Page[$this->Row][$this->Part]=ob_get_clean();
$this->Part++;
}
// Shorthand for:
// add_row();
// add_part();
// You should only use this function if the row is only going to have one part in it (convention),
// although it will theoretically work with multiple parts.
function add_whole_row(){
$this->Part = 0;
$this->Page[$this->Row] = array();
ob_start();
}
// Shorthand for:
// end_part();
// end_row();
// You should only use this function if the row is only going to have one part in it (convention),
// although it will theoretically work with multiple parts.
function end_whole_row(){
$this->Page[$this->Row][$this->Part]=ob_get_clean();
$this->Row++;
}
// Set a variable that will only be availabe when the system is on its row
// This variable is stored in the same way as pages, so don't use an integer for the $Key.
function set_row_value($Key, $Value){
$this->Page[$this->Row][$Key] = $Value;
}
// Set a variable that will always be available, no matter what row the system is on.
// This variable is stored in the same way as rows, so don't use an integer for the $Key.
function set_constant_value($Key, $Value){
$this->Page[$Key] = $Value;
}
// Inserts a 'false' value into a row, which breaks out of while loops.
// This is not necessary if the end of $this->Page is also the end of the while loop.
function break_loop(){
if(count($this->Page)>0){
$this->Page[$this->Row] = FALSE;
$this->Row++;
}
}
//---------- Locking functions ----------//
// These functions 'lock' a key.
// Users cannot proceed until it is unlocked.
function lock($Key){
$this->cache_value('lock_'.$Key, 'true', 3600);
}
function unlock($Key) {
// $this->delete('lock_'.$Key);
$this->redis->del('lock_'.$Key);
}
//---------- Caching functions ----------//
// Cache $this->Page and resets $this->Row and $this->Part
function cache_page(){
$this->cache_value($this->MemKey,$this->Page, $this->Duration);
$this->Row = 0;
$this->Part = 0;
}
// Exact same as cache_page, but does not store the page in cache
// This is so that we can use classes that normally cache values in
// situations where caching is not required
function setup_page(){
$this->Row = 0;
$this->Part = 0;
}
// Wrapper for Memcache::set, with the zlib option removed and default duration of 1 hour
function cache_value($Key, $Value, $Duration = 3600){
// $this->set($Key,$Value, 0, $Duration);
$this->redis->set($Key, $Value, $Duration);
$this->cacheWriteTimes++;
$this->keyHits['write'][$Key] = !$this->keyHits['write'][$Key] ? 1 : $this->keyHits['write'][$Key]+1;
}
//---------- Getting functions ----------//
// Returns the next row in the page
// If there's only one part in the row, return that part.
function next_row(){
$this->Row++;
$this->Part = 0;
if($this->Page[$this->Row] == false){
return false;
}
elseif(count($this->Page[$this->Row]) == 1){
return $this->Page[$this->Row][0];
}
else {
return $this->Page[$this->Row];
}
}
// Returns the next part in the row
function next_part(){
$Return = $this->Page[$this->Row][$this->Part];
$this->Part++;
return $Return;
}
// Returns a 'row value' (a variable that changes for each row - see above).
function get_row_value($Key){
return $this->Page[$this->Row][$Key];
}
// Returns a 'constant value' (a variable that doesn't change with the rows - see above)
function get_constant_value($Key){
return $this->Page[$Key];
}
// If a cached version of the page exists, set $this->Page to it and return true.
// Otherwise, return false.
function get_page(){
$Result = $this->get_value($this->MemKey);
if($Result){
$this->Row = 0;
$this->Part = 0;
$this->Page = $Result;
return true;
} else {
return false;
}
}
// Wrapper for Memcache::get. Why? Because wrappers are cool.
function get_value($Key) {
if($this->getClearCache()){
$this->delete_value($Key);
return false;
}
// If we've locked it
// Xia Zuojie: we disable the following lock feature 'cause we don't need it and it doubles the time to fetch a value from a key
/*while($Lock = $this->get('lock_'.$Key)){
sleep(2);
}*/
$Return = $this->redis->get($Key);
$this->cacheReadTimes++;
$this->keyHits['read'][$Key] = !$this->keyHits['read'][$Key] ? 1 : $this->keyHits['read'][$Key]+1;
return $Return;
}
// Wrapper for Memcache::delete. For a reason, see above.
function delete_value($Key, $AllLang = false){
if ($AllLang){
$langfolder_array = $this->getLanguageFolderArray();
foreach($langfolder_array as $lf)
$this->redis->del($lf."_".$Key);
}
else {
$this->redis->del($Key);
}
}
function getCacheReadTimes() {
return $this->cacheReadTimes;
}
function getCacheWriteTimes() {
return $this->cacheWriteTimes;
}
function getKeyHits ($type='read') {
return (array)$this->keyHits[$type];
}
}

94
classes/class_db.php Normal file
View File

@@ -0,0 +1,94 @@
<?php
class DB
{
private DBInterface $driver;
private static $instance;
private static array $queries = [];
private function __construct()
{
}
private function __clone()
{
}
public function setDriver(DBInterface $driver)
{
$this->driver = $driver;
return $this;
}
public static function getInstance()
{
if (self::$instance) {
return self::$instance;
}
$instance = new self;
$driver = new DBMysqli();
$instance->setDriver($driver);
return self::$instance = $instance;
}
public function connect($host, $username, $password, $database, $port)
{
return $this->driver->connect($host, $username, $password, $database, $port);
}
public function query(string $sql)
{
return $this->driver->query($sql);
}
public function error()
{
return $this->driver->error();
}
public function errno()
{
return $this->driver->errno();
}
public function numRows($result)
{
return $this->driver->numRows($result);
}
public function select_db($database)
{
return $this->driver->selectDb($database);
}
public function fetchAssoc($result)
{
return $this->driver->fetchAssoc($result);
}
public function fetchRow($result)
{
return $this->driver->fetchRow($result);
}
public function affectedRows()
{
return $this->driver->affectedRows();
}
public function escapeString(string $string)
{
return $this->driver->escapeString($string);
}
}

View File

@@ -0,0 +1,67 @@
<?php
class DBMysqli implements DBInterface
{
private mysqli $mysqli;
public function connect($host, $username, $password, $database, $port)
{
$mysqli = new mysqli($host, $username, $password, $database, $port);
/* check connection */
if (mysqli_connect_errno()) {
throw new \RuntimeException(mysqli_connect_error());
}
/* activate reporting */
$driver = new mysqli_driver();
$driver->report_mode = MYSQLI_REPORT_ALL & ~MYSQLI_REPORT_INDEX;
return $this->mysqli = $mysqli;
}
public function query(string $sql)
{
return $this->mysqli->query($sql);
}
public function error(): string
{
return $this->mysqli->error;
}
public function errno(): int
{
return $this->mysqli->errno;
}
public function numRows($mysqliResult): int
{
return $mysqliResult->num_rows;
}
public function selectDb($database)
{
return $this->mysqli->select_db($database);
}
public function fetchAssoc($mysqliResult): array|null
{
return $mysqliResult->fetch_assoc();
}
public function fetchRow($mysqliResult): array|null
{
return $mysqliResult->fetch_row();
}
public function affectedRows(): int
{
return $this->mysqli->affected_rows;
}
public function escapeString(string $string): string
{
return $this->mysqli->real_escape_string($string);
}
}

25
classes/interface_db.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
interface DBInterface
{
public function connect($host, $username, $password, $database, $port);
public function query(string $sql);
public function error(): string;
public function errno(): int;
public function numRows($result): int;
public function selectDb($database);
public function fetchAssoc($result): array|null;
public function fetchRow($result): array|null;
public function affectedRows(): int;
public function escapeString(string $string): string;
}