* @package cache * @link https://www.zentao.net */ namespace Zentao\Cache; \helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheInterface.php'); \helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'CacheException.php'); \helper::import(dirname(__FILE__) . DS . 'simple-cache' . DS . 'InvalidArgumentException.php'); \helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'ApcuDriver.php'); \helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'RedisDriver.php'); \helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'YacDriver.php'); \helper::import(dirname(__FILE__) . DS . 'driver' . DS . 'FileDriver.php'); use ZenTao\Cache\SimpleCache\InvalidArgumentException; class cache { const DRIVER_APCU = 'Apcu'; const DRIVER_FILE = 'File'; const DRIVER_YAC = 'Yac'; const DRIVER_REDIS = 'Redis'; const DRIVER_LIST = [self::DRIVER_APCU, self::DRIVER_FILE, self::DRIVER_YAC, self::DRIVER_REDIS]; /** * 全局应用程序对象。 * Global application object. * * @access private * @var object */ private $app; /** * 全局数据库操作对象。 * Global database operation object. * * @access private * @var object */ private $dao; /** * 全局数据库操作句柄。 * Global database operation handler. * * @access private * @var object */ private $dbh; /** * 全局配置对象。 * Global configuration object. * * @access private * @var object */ private $config; /** * 全局缓存对象。 * Global cache object. * * @access private * @var object */ private $cache; /** * 缓存状态。 * Cache status. * * @access private * @var string */ private $status = 'enabled'; /** * 缓存命名空间。 * Cache namespace. * * @access private * @var string */ private $namespace; /** * 缓存键连接符。 * Cache key connector. * * @access private * @var string */ private $connector; /** * 缓存键。 * Cache key. * * @access private * @var string */ private $key = ''; /** * 缓存标签。 * Cache label. * * @access private * @var array */ private $labels = []; /** * 影响缓存的表名。 * Table name that affects cache. * * @access private * @var string */ private $table = ''; /** * 影响缓存的事件。 * Event that affects cache. * * @access private * @var string */ private $event = ''; /** * 影响缓存的 WHERE 子句。 * WHERE clause that affects cache. * * @access private * @var string */ private $where = ''; /** * 受影响的对象列表。 * Affected object list. * * @access private * @var array */ private $objects = []; /** * 构造函数,根据配置文件初始化缓存对象。 * Constructor, initialize cache object according to the configuration file. * * @param object $app 全局应用程序对象。 * @access public * @return void */ public function __construct(object $app) { $this->app = $app; $this->dao = $app->dao; $this->dbh = $app->dbh; $this->config = $app->config; if(empty($this->config->cache->enable)) return $this->log('The cache is not enabled', __FILE__, __LINE__); $driver = ucfirst(strtolower($this->config->cache->driver)); if(!in_array($driver, self::DRIVER_LIST)) return $this->log("Driver {$driver} is not supported.", __FILE__, __LINE__); if($driver != self::DRIVER_FILE && !extension_loaded($driver)) return $this->log("Driver ext-{$driver} is not loaded.", __FILE__, __LINE__); $connector = $driver == self::DRIVER_REDIS ? ':' : '-'; $className = "ZenTao\Cache\Driver\\{$driver}Driver"; $scope = $this->config->cache->scope; $namespace = $this->config->cache->namespace; $lifetime = $this->config->cache->lifetime; $redis = $this->config->redis; $this->setNamespace($namespace); $this->setConnector($connector); if($driver == self::DRIVER_APCU) return $this->cache = new $className($namespace, $lifetime, $scope, $connector); if($driver == self::DRIVER_REDIS) return $this->cache = new $className($namespace, $lifetime, $scope, $connector, $redis); if($driver == self::DRIVER_YAC) return $this->cache = new $className($namespace, $lifetime); if($driver == self::DRIVER_FILE) return $this->cache = new $className($namespace, $lifetime, $app->getCacheRoot()); } /** * 设置缓存状态。 * Set cache status. * * @param string $status 缓存状态。enabled: 启用缓存;disabled: 禁用缓存。 * @access private * @return void */ private function setStatus(string $status) { $this->status = $status; } /** * 设置缓存命名空间。 * Set cache namespace. * * @param string $namespace 缓存命名空间。 * @access private * @return void */ private function setNamespace(string $namespace) { $this->namespace = $namespace; } /** * 设置缓存键连接符。 * Set cache key connector. * * @param string $connector 缓存键连接符。 * @access private * @return void */ private function setConnector(string $connector) { $this->connector = $connector; } /** * 设置缓存键。 * Set cache key. * * @param string $key 缓存键。 * @access private * @return void */ private function setKey(string $key) { $this->key = $key; } /** * 设置影响缓存的表名。 * Set the table name that affects the cache. * * @param string $table 表名。 * @access private * @return void */ private function setTable(string $table) { $this->table = $table; } /** * 设置影响缓存的事件。 * Set the event that affects the cache. * * @param string $event 事件。 * @access private * @return void */ private function setEvent(string $event) { $this->event = $event; } /** * 设置影响缓存的 WHERE 子句。 * Set the WHERE clause that affects the cache. * * @param string $where WHERE 子句。 * @access private * @return void */ private function setWhere(string $where) { $this->where = $where; } /** * 设置受影响的对象列表。 * Set the list of affected objects. * * @param array $objects 对象列表。 * @access private * @return void */ private function setObjects(array $objects) { $this->objects = $objects; } /** * 获取表的主键字段。 * Get the primary key field of the table. * * @access private * @return string */ public function getTableField(): string { return $this->config->cache->raw[$this->table]; } /** * 获取表的缓存代号。 * Get the cache code of the table. * * @access private * @return string */ private function getTableCode(): string { return str_replace(['`', $this->config->db->prefix], '', $this->table); } /** * 获取原始数据类型缓存的键。该缓存用于保存表的原始数据。 * Get the key of the raw data type cache. This cache is used to save the original data of the table. * * @param string $code 缓存代号。 * @param int|string $id 主键值。 * @access private * @return string */ private function getRawCacheKey(string $code, int|string $id): string { return $this->createKey('raw', $code, $id); } /** * 获取集合类型缓存的键。该缓存用于保存表的主键字段。 * Get the key of the set type cache. This cache is used to save the primary key field of the table. * * @param string $code 缓存代号。 * @access private * @return string */ private function getSetCacheKey(string $code): string { return $this->createKey('set', $code, 'list'); } /** * 获取结果类型缓存的键。该缓存用于保存表的计算结果。 * Get the key of the result type cache. This cache is used to save the calculation results of the table. * * @param string $key 缓存键。 * @access private * @return string */ private function getResCacheKey(string $key): string { $args = explode('_', str_replace('cache', 'res', strtolower($key))); return $this->createKey(...$args); } /** * 新增数据时更新缓存。 * Update cache when adding data. * * @access private * @return void */ private function create() { $code = $this->getTableCode(); $setCacheKey = $this->getSetCacheKey($code); $objectIdList = $this->cache->get($setCacheKey); if(!is_array($objectIdList)) return; $objectID = $this->dbh->lastInsertID(); if(!$objectID) return $this->log('Failed to fetch last insert id.', __FILE__, __LINE__); /* 获取新增的数据。Get the new data. */ $sql = $this->dao->select('*')->from($this->table)->where('id')->eq($objectID)->sqlobj->get(); $object = $this->query($sql, 'fetch'); if(!$object) return $this->log('Failed to fetch new object. The sql is: ' . $sql, __FILE__, __LINE__); /* 把新增的数据保存到缓存中。Save the new data to cache. */ $field = $this->getTableField(); $rawCacheKey = $this->getRawCacheKey($code, $object->$field); $this->cache->set($rawCacheKey, $object); /* 把新增的数据的 id 保存到缓存中。Save the id of the new data to cache. */ $this->cache->set($setCacheKey, $objectIdList ? array_merge($objectIdList, [$object->$field]) : [$object->$field]); if(empty($this->config->cache->res[$this->table])) return; /* 删除受影响的缓存。Delete the affected cache. */ $this->deleteAffectedCache([$object]); } /** * 更新数据时更新缓存。 * Update cache when updating data. * * @access private * @return void */ private function update() { if(empty($this->objects)) return $this->log('No objects to update.', __FILE__, __LINE__); $field = $this->getTableField(); $code = $this->getTableCode(); /* 获取被更新数据的 id 列表。Get the id list of the updated data. */ $objectIdList = array_map(function($object) use ($field) { return $object->$field; }, $this->objects); /* 获取更新后的数据。Get the updated data. */ $sql = $this->dao->select('*')->from($this->table)->where($field)->in($objectIdList)->sqlobj->get(); $objects = $this->query($sql, 'fetchAll', $field); if(!$objects) return $this->log('Failed to fetch updated objects. The sql is: ' . $sql, __FILE__, __LINE__); /* 把更新后的数据保存到缓存中。Save the updated data to cache. */ $values = []; foreach($objects as $object) { $rawCacheKey = $this->getRawCacheKey($code, $object->$field); $values[$rawCacheKey] = $object; } $this->cache->setMultiple($values); if(empty($this->config->cache->res[$this->table])) return; /* 获取受影响的数据。Get the affected data. */ foreach($objectIdList as $objectID) { if(isset($this->objects[$objectID]) && isset($objects[$objectID]) && $this->objects[$objectID] == $objects[$objectID]) unset($this->objects[$objectID], $objects[$objectID]); } $affectedObjects = array_merge($this->objects, $objects); /* 删除受影响的缓存。Delete the affected cache. */ $this->deleteAffectedCache($affectedObjects); } /** * 删除数据时更新缓存。 * Update cache when deleting data. * * @access private * @return void */ private function delete() { if(empty($this->objects)) return $this->log('No objects to delete.', __FILE__, __LINE__); $code = $this->getTableCode(); $setCacheKey = $this->getSetCacheKey($code); $objectIdList = $this->cache->get($setCacheKey); if(!is_array($objectIdList)) return; $field = $this->getTableField(); /* 把被删除的数据从缓存中删除。Delete the deleted data from cache. */ $affectedKeys = []; foreach($this->objects as $object) $affectedKeys[] = $this->getRawCacheKey($code, $object->$field); $this->cache->deleteMultiple($affectedKeys); /* 把被删除的数据的 id 从缓存中删除。Delete the id of the deleted data from cache. */ $this->cache->set($setCacheKey, array_diff($objectIdList, array_map(function($object) use ($field) { return $object->$field; }, $this->objects))); if(empty($this->config->cache->res[$this->table])) return; /* 删除受影响的缓存。Delete the affected cache. */ $this->deleteAffectedCache($this->objects); } /** * 删除受影响的缓存。 * Delete the affected cache. * * @param array $objects 受影响的对象列表。 * @access private * @return void */ private function deleteAffectedCache(array $objects) { /* 根据受影响的数据查找受影响的缓存。Find the affected cache by the affected data. */ $keys = []; foreach($this->config->cache->res[$this->table] as $res) { $res = (object)$res; /* 如果没有设置关联字段则整个缓存都受影响。If no associated fields are set, the entire cache is affected. */ if(empty($res->fields)) { $keys[] = $this->getResCacheKey($res->name); continue; } /* 根据关联字段查找受影响的缓存。Find the affected cache by the associated fields. */ foreach($objects as $object) { $key = $this->getResCacheKey($res->name); foreach($res->fields as $field) { if(!isset($object->$field)) return $this->log("Field {$field} does not exist in table {$this->table}.", __FILE__, __LINE__); $key .= $this->connector . $object->$field; } $keys[] = $key; } } /* 删除受影响的缓存。Delete the affected cache. */ $this->cache->deleteMultiple($keys); } /** * 执行数据库查询操作。 * Execute database query operation. * * @param string $sql SQL语句。 * @param string $function 查询函数。 * @param string $field 字段名。 * @access private * @return array|object */ private function query($sql, $function = 'fetchAll', $field = ''): array|object { $rows = $this->dao->query($sql)->$function(); if($function == 'fetchAll' && !empty($field)) { $objects = []; foreach($rows as $row) $objects[$row->$field] = $row; return $objects; } return $rows; } /** * 检查表是否有缓存设置。 * Check if the table has cache settings. * * @param string $table 表名。 * @access private * @return bool */ private function checkTable(string $table = ''): bool { if(empty($table)) $table = $this->table; return !empty($this->config->cache->raw[$table]); } /** * 记录日志信息。 * Record log information. * * @param string $message 日志信息。 * @param string $file 文件名。 * @param string $line 行号。 * @access private * @return false */ private function log(string $message, string $file, string $line): bool { if(!$this->config->debug) return false; $runMode = PHP_SAPI == 'cli' ? '_cli' : ''; $logFile = $this->app->getLogRoot() . 'cache' . $runMode . '.' . date('Ymd') . '.log.php'; if(!file_exists($logFile)) file_put_contents($logFile, '\n"); $content = date('Ymd H:i:s') . ': ' . $this->getURI() . "\nError: {$message} in $file on line $line\n"; file_put_contents($logFile, $content, FILE_APPEND); if($this->config->debug >= 2) $this->app->triggerError($message, __FILE__, __LINE__, true); return false; } /** * 获取 URI。 * Get URI. * * @access private * @return string */ private function getURI(): string { $uri = $this->app->getURI(); if($uri) return $uri; if($this->config->requestType == 'GET') return $_SERVER['REQUEST_URI']; if($this->config->requestType == 'PATH_INFO' || $this->config->requestType == 'PATH_INFO2') { $pathInfo = $this->app->getPathInfo(); if(empty($pathInfo)) return ''; $dotPos = strrpos($pathInfo, '.'); if($dotPos) return substr($pathInfo, 0, $dotPos); return $pathInfo; } return ''; } /** * 初始化指定表的缓存。 * Initialize the cache of the specified table. * * @access private * @return array */ private function initTableCache(): array { $field = $this->getTableField(); $sql = $this->dao->select('*')->from($this->table)->sqlobj->get(); $objects = $this->query($sql, 'fetchAll', $field); if(!$objects) return []; $values = []; $code = $this->getTableCode(); foreach($objects as $key => $object) { $rawCacheKey = $this->getRawCacheKey($code, $key); $values[$rawCacheKey] = $object; } $this->cache->setMultiple($values); $setCacheKey = $this->getSetCacheKey($code); $this->cache->set($setCacheKey, array_keys($objects)); return $objects; } /** * 生成缓存键。 * Generate cache key. * * @param mixed ...$args 缓存键的参数。 * @access public * @return string */ public function createKey(...$args): string { return $this->namespace . $this->connector . implode($this->connector, $args); } /** * 从缓存中获取指定表指定 id 的数据。 * Get the data of the specified table and id from the cache. * * @param string $table 表名。 * @param int|string $id 主键值。 * @access public * @return object|false */ public function fetch(string $table, int|string $id): object|null { if($this->status == 'disabled') return null; if(!$this->checkTable($table)) return $this->log("Table {$table} is not set in the cache configuration", __FILE__, __LINE__); if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__); if(empty($id)) return $this->log('The id is empty', __FILE__, __LINE__); $this->setTable($table); $code = $this->getTableCode(); $key = $this->getRawCacheKey($code, $id); $object = $this->cache->get($key); if($object) return $object; $objects = $this->initTableCache(); return isset($objects[$id]) ? $objects[$id] : null; } /** * 从缓存中获取指定表的所有数据。 * Get all data of the specified table from the cache. * * @param string $table 表名。 * @param array $objectIdList 主键值列表。 * @access public * @return array */ public function fetchAll(string $table, array $objectIdList = []): array { if($this->status == 'disabled') return []; if(!$this->checkTable($table)) return $this->log("Table {$table} is not set in the cache configuration", __FILE__, __LINE__); if(empty($table)) return $this->log('The table name is empty', __FILE__, __LINE__); $this->setTable($table); $code = $this->getTableCode(); /* 尝试获取指定表的所有主键字段的值。Try to get the values of all primary key fields of the specified table. */ $setCacheKey = $this->getSetCacheKey($code); $allObjectIdList = $this->cache->get($setCacheKey); /* 如果主键字段的值为空,则初始化指定表的缓存。If the value of the primary key field is empty, initialize the cache of the specified table. */ if(!$allObjectIdList) { $allData = $this->initTableCache(); if(empty($objectIdList)) return $allData; return array_intersect_key($allData, $objectIdList); } /* 如果主键字段的值不为空,则从缓存中获取数据。If the value of the primary key field is not empty, get the data from the cache. */ if(empty($objectIdList)) $objectIdList = $allObjectIdList; $keys = []; foreach($objectIdList as $objectID) { if(!$objectID) continue; $keys[$objectID] = $this->getRawCacheKey($code, $objectID); } $objects = $this->cache->getMultiple(array_values($keys)); /* 如果缓存中没有全部的数据,则从数据库中获取缺失的数据。If not all data in cache, get the missing data from the database. */ if(count($keys) > count($objects)) { $lostObjects = []; $diffIdList = array_keys(array_diff($keys, array_keys($objects))); $sql = $this->dao->select('*')->from($table)->where('id')->in($diffIdList)->sqlobj->get(); $diffObjects = $this->query($sql, 'fetchAll'); foreach($diffObjects as $object) { $rawCacheKey = $this->getRawCacheKey($code, $object->id); $lostObjects[$rawCacheKey] = $object; } /* 把缺失的数据保存到缓存中。Save the missing data to cache. */ $this->cache->setMultiple($lostObjects); $objects += $lostObjects; } if(!$objects) return []; $result = []; $field = $this->getTableField(); foreach($objects as $object) $result[$object->$field] = $object; return $result; } /** * 从自动缓存的计算结果中获取符合查询条件的数据。 * Get data that meets the query conditions from the calculation results of the automatic cache. * * @param string $table 表名。 * @param array $conditions 查询条件。 * @access public * @return array */ public function fetchAutoCache(string $table, array $conditions): array { if($this->status == 'disabled') return []; if(!$this->checkTable($table)) return $this->log("Table {$table} is not set in the cache configuration", __FILE__, __LINE__); $conditions = array_filter($conditions, function($condition) { return $condition['operator'] == 'eq'; }); if(empty($conditions)) return []; $this->setTable($table); /* 按字段排序查询条件以确保缓存的唯一性。Sort the query conditions by field to ensure the uniqueness of the cache. */ usort($conditions, function($a, $b) { return strcmp($a['field'], $b['field']); }); /* 根据查询条件生成缓存键。Generate a cache key based on the query conditions. */ $values = array_column($conditions, 'value'); $code = $this->getTableCode(); $cacheKey = $this->createKey('cache', $code, 'auto', ...$values); /* 从缓存中获取符合查询条件的主键字段的值。Get the value of the primary key field that meets the query conditions from the cache. */ $idList = $this->getByKey($cacheKey); if(is_null($idList)) { /* 如果没有缓存,从数据库中获取符合查询条件的主键字段的值并更新缓存。If there is no cache, get the value of the primary key field that meets the query conditions from the database and update the cache. */ $field = $this->getTableField(); $dao = $this->dao->select($field)->from($table)->where('1=1'); foreach($conditions as $condition) $dao->andWhere($condition['field'])->eq($condition['value']); $sql = $dao->sqlobj->get(); $objects = $this->query($sql, 'fetchAll'); foreach($objects as $object) $idList[] = $object->$field; $this->saveByKey($cacheKey, $idList); /* 把缓存键写入数据库以便执行 dao::exec() 时自动更新。Write the cache key to the database for automatic update when executing dao::exec(). */ $fields = implode(',', array_column($conditions, 'field')); $sql = $this->dao->select('1')->from(TABLE_AUTOCACHE)->where('code')->eq($code)->andWhere('fields')->eq($fields)->sqlobj->get(); $autoCache = $this->query($sql, 'fetch'); if(!$autoCache) { $autoCache = new \stdClass(); $autoCache->code = $code; $autoCache->fields = $fields; $sql = $this->dao->insert(TABLE_AUTOCACHE)->data($autoCache)->get(); try { $this->dbh->exec($sql); } catch(PDOException $e) { $this->dao->sqlError($e); } } } if(!$idList) return []; /* 根据主键字段的值从缓存中获取数据。Get data from the cache according to the value of the primary key field. */ return $this->fetchAll($table, $idList); } /** * 设置缓存键。 * Set cache key. * * @param string $key 缓存键。 * @param mixed ...$args 缓存键的参数。 * @access public * @return object */ public function key($key, ...$args) { if(empty($this->config->cache->keys[$key])) return $this->log("Key {$key} is not defined", __FILE__, __LINE__); $cache = $this->config->cache->keys[$key]; if(!empty($cache->fields) && !empty($args)) { $tableFields = $this->dao->descTable($cache->table); foreach($cache->fields as $index => $field) { if(!isset($tableFields[$field])) return $this->log("Field {$field} does not exist in table {$cache->table}", __FILE__, __LINE__); if(!isset($args[$index])) continue; $tableField = $tableFields[$field]; if(stripos($tableField->type, 'int') !== false) $args[$index] = (int) $args[$index]; if(stripos($tableField->type, 'float') !== false) $args[$index] = (float)$args[$index]; if(stripos($tableField->type, 'decimal') !== false) $args[$index] = (float)$args[$index]; if(stripos($tableField->type, 'double') !== false) $args[$index] = (float)$args[$index]; } } $key = $this->getResCacheKey(constant($key)); foreach($args as $arg) $key .= $this->connector . $arg; $this->setKey($key); return $this; } /** * 设置缓存标签。 * Set cache label. * * @param string $label 缓存标签。 * @access public * @return object */ public function label(string $label) { if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__); if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__); if(isset($this->labels[$label])) return $this->log("Label {$label} already used", __FILE__, __LINE__); $this->labels[$label] = $this->key; return $this; } /** * 根据当前缓存键获取缓存。 * Get cache according to the current cache key. * * @access public * @return mixed */ public function get() { if($this->status == 'disabled') return null; if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__); return $this->cache->get($this->key); } /** * 根据指定缓存键获取缓存。 * Get cache according to the specified cache key. * * @param string $key 缓存键。 * @access public * @return mixed */ public function getByKey(string $key) { if($this->status == 'disabled') return null; if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); return $this->cache->get($key); } /** * 根据当前缓存键保存缓存。 * Save cache according to the current cache key. * * @param mixed $value 缓存值。 * @access public * @return bool */ public function save($value) { if($this->status == 'disabled') return false; if(empty($this->key)) return $this->log('The key is empty', __FILE__, __LINE__); return $this->cache->set($this->key, $value); } /** * 根据指定缓存建保存缓存。 * Save cache according to the specified cache key. * * @param string $key 缓存键。 * @param mixed $value 缓存值。 * @param int $ttl 缓存时间。 * @access public * @return bool */ public function saveByKey(string $key, $value, int $ttl = 0) { if($this->status == 'disabled') return false; if(empty($key)) return $this->log('The key is empty', __FILE__, __LINE__); return $this->cache->set($key, $value, $ttl); } /** * 根据指定缓存标签保存缓存。 * Save cache according to the specified cache label. * * @param string $label 缓存标签。 * @param mixed $value 缓存值。 * @access public * @return bool */ public function saveByLabel(string $label, $value) { if($this->status == 'disabled') return false; if(empty($label)) return $this->log('The label is empty', __FILE__, __LINE__); if(empty($this->labels[$label])) return $this->log("Label {$label} does not exist", __FILE__, __LINE__); return $this->cache->set($this->labels[$label], $value); } /** * 重置缓存相关设置项。 * Reset cache related settings. * * @access public * @return void */ public function reset() { $this->setKey(''); $this->setTable(''); $this->setEvent(''); $this->setWhere(''); $this->setObjects([]); } /** * 执行数据库操作前准备更新缓存需要的设置项。 * Prepare the settings required to update the cache before executing the database operation. * * @param string $table 表名。 * @param string $event 事件。 * @param string $sql SQL 语句。 * @access public * @return void */ public function prepare(string $table, string $event, string $sql) { $this->reset(); if($this->status == 'disabled') return; if(!$this->checkTable($table)) return; if(empty($table)) return $this->log('Table name is required.', __FILE__, __LINE__); if(empty($event)) return $this->log('Event type is required.', __FILE__, __LINE__); if(empty($sql)) return $this->log('SQL statement is required.', __FILE__, __LINE__); $this->setTable($table); $this->setEvent($event); if($event == 'update' || $event == 'delete') { /* 获取 WHERE 子句的内容。Get the content of WHERE clause. */ $whereLen = strlen(\DAO::WHERE); $wherePOS = strrpos($sql, \DAO::WHERE); $groupPOS = strrpos($sql, \DAO::GROUPBY); $havingPOS = strrpos($sql, \DAO::HAVING); $orderPOS = strrpos($sql, \DAO::ORDERBY); $limitPOS = strrpos($sql, \DAO::LIMIT); $splitPOS = $orderPOS ? $orderPOS : $limitPOS; $splitPOS = $havingPOS ? $havingPOS : $splitPOS; $splitPOS = $groupPOS ? $groupPOS : $splitPOS; $where = ''; if($wherePOS) { if($splitPOS) { $where = substr($sql, $wherePOS + $whereLen, $splitPOS - $wherePOS - $whereLen); } else { $where = substr($sql, $wherePOS + $whereLen); } } /* 执行操作后数据已经被修改,所以需要提前获取被影响的数据。*/ $field = $this->getTableField(); $sql = $this->dao->select('*')->from($table)->beginIF($where)->where($where)->fi()->sqlobj->get(); $objects = $this->query($sql, 'fetchAll', $field); $this->setWhere($where); $this->setObjects($objects); } } /** * 执行数据库操作后更新缓存。 * Update the cache after executing the database operation. * * @access public * @return void */ public function sync() { if($this->status == 'disabled') return; if(!$this->checkTable()) return; if(empty($this->table)) return $this->log('Table name is required.', __FILE__, __LINE__); if(empty($this->event)) return $this->log('Event type is required.', __FILE__, __LINE__); if($this->event == 'insert') $this->create(); if($this->event == 'update') $this->update(); if($this->event == 'delete') $this->delete(); $this->reset(); } /** * 清空缓存。 * Clear cache. * * @access public * @return void */ public function clear() { $this->setStatus('disabled'); $this->cache->clear(); $this->setStatus('normal'); } /** * 关闭缓存连接。 * Close cache connection. * * @access public * @return void */ public function close() { if(method_exists($this->cache, 'close')) $this->cache->close(); } /** * 获取内存使用情况。 * Get memory usage. * * @param string $type * @return string */ public function memory(string $type) { return $this->cache->memory($type); } }