array(), 'expires' => array(), 'expiresMode' => 'OR', 'get' => array('name', 'expires', 'data'), ); $database = $this->wire()->database; $options = array_merge($defaults, $options); $where = array(); $whereNames = array(); $whereExpires = array(); $binds = array(); $cols = array(); if(count($options['names'])) { $n = 0; foreach($options['names'] as $name) { $n++; if(strpos($name, '*') !== false) { $name = str_replace('*', '%', $name); $whereNames[] = "name LIKE :name$n"; } else { $whereNames[] = "name=:name$n"; } $binds[":name$n"] = $name; } } if(count($options['expires'])) { $n = 0; foreach($options['expires'] as $expires) { $operator = '='; if(strpos($expires, ' ')) { // string in format: '>= YYYY-MM-DD HH:MM:SS' list($op, $expires) = explode(' ', $expires, 2); if($database->isOperator($op)) $operator = $op; } $n++; $whereExpires[] = "expires$operator:expires$n"; $binds[":expires$n"] = $expires; } } if(count($whereNames)) { $where[] = '(' . implode(' OR ', $whereNames) . ')'; } if(count($whereExpires)) { $mode = strtoupper($options['expiresMode']) === 'AND' ? 'AND' : 'OR'; $where[] = '(' . implode(" $mode ", $whereExpires) . ')'; } foreach($options['get'] as $col) { if($col === 'name' || $col === 'expires' || $col === 'data') $cols[] = $col; if($col === 'size') $cols[] = 'LENGTH(data) AS size'; } if(empty($cols)) return array(); $sql = 'SELECT ' . implode(',', $cols) . ' FROM caches '; if(count($where)) { $sql .= 'WHERE ' . implode(' AND ', $where); } else { // getting all $sql .= 'ORDER BY name'; } $query = $database->prepare($sql); foreach($binds as $bindKey => $bindValue) { $query->bindValue($bindKey, $bindValue); } if(!$this->executeQuery($query)) return array(); $rows = $query->fetchAll(\PDO::FETCH_ASSOC); $query->closeCursor(); return $rows; } /** * Save a cache * * @param string $name Name of cache * @param string $data Data to save in cache * @param string $expire String in ISO-8601 date format * @return bool * */ public function save($name, $data, $expire) { $sql = 'INSERT INTO caches (`name`, `data`, `expires`) VALUES(:name, :data, :expires) ' . 'ON DUPLICATE KEY UPDATE `data`=VALUES(`data`), `expires`=VALUES(`expires`)'; $query = $this->wire()->database->prepare($sql, "cache.save($name)"); $query->bindValue(':name', $name); $query->bindValue(':data', $data); $query->bindValue(':expires', $expire); $result = $this->executeQuery($query); return $result; } /** * Delete a cache by name * * @param string $name * @return bool * */ public function delete($name) { $sql = 'DELETE FROM caches WHERE name=:name'; $query = $this->wire()->database->prepare($sql, "cache.delete($name)"); $query->bindValue(':name', $name); if(!$this->executeQuery($query)) return false; $query->closeCursor(); return true; } /** * Delete all caches (except those reserved by the system) * * @return int * */ public function deleteAll() { return $this->_deleteAll(); } /** * Expire all caches (except those that should never expire) * * @return int * */ public function expireAll() { return $this->_deleteAll(true); } /** * Implementation for deleteAll and expireAll methods * * @param bool $expireAll * @return int * @throws WireException * */ protected function _deleteAll($expireAll = false) { $sql = 'DELETE FROM caches WHERE ' . ($expireAll ? 'expires>:expires' : 'expires!=:expires'); $query = $this->wire()->database->prepare($sql, "cache.deleteAll()"); $query->bindValue(':expires', ($expireAll ? WireCache::expireNever : WireCache::expireReserved)); if(!$this->executeQuery($query)) return 0; $qty = $query->rowCount(); $query->closeCursor(); return $qty; } /** * Execute query * * @param \PDOStatement $query * @return bool * */ protected function executeQuery(\PDOStatement $query) { $install = false; try { $result = $query->execute(); } catch(\PDOException $e) { $result = false; $install = $e->getCode() === '42S02'; // table does not exist if(!$install) throw $e; } if($install) $this->install(); return $result; } /** * Create the caches table if it happens to have been deleted * */ protected function install() { $database = $this->wire()->database; $config = $this->wire()->config; $dbEngine = $config->dbEngine; $dbCharset = $config->dbCharset; if($database->tableExists('caches')) return; try { $this->wire()->database->exec(" CREATE TABLE caches ( `name` VARCHAR(191) NOT NULL PRIMARY KEY, `data` MEDIUMTEXT NOT NULL, `expires` DATETIME NOT NULL, INDEX `expires` (`expires`) ) ENGINE=$dbEngine DEFAULT CHARSET=$dbCharset; "); $this->message("Re-created 'caches' table"); } catch(\Exception $e) { $this->error("Unable to create 'caches' table"); } } }