Chris@0: . Chris@0: */ Chris@0: Chris@0: namespace Doctrine\Common\Cache; Chris@0: Chris@0: use SQLite3; Chris@0: use SQLite3Result; Chris@0: Chris@0: /** Chris@0: * SQLite3 cache provider. Chris@0: * Chris@0: * @since 1.4 Chris@0: * @author Jake Bell Chris@0: */ Chris@0: class SQLite3Cache extends CacheProvider Chris@0: { Chris@0: /** Chris@0: * The ID field will store the cache key. Chris@0: */ Chris@0: const ID_FIELD = 'k'; Chris@0: Chris@0: /** Chris@0: * The data field will store the serialized PHP value. Chris@0: */ Chris@0: const DATA_FIELD = 'd'; Chris@0: Chris@0: /** Chris@0: * The expiration field will store a date value indicating when the Chris@0: * cache entry should expire. Chris@0: */ Chris@0: const EXPIRATION_FIELD = 'e'; Chris@0: Chris@0: /** Chris@0: * @var SQLite3 Chris@0: */ Chris@0: private $sqlite; Chris@0: Chris@0: /** Chris@0: * @var string Chris@0: */ Chris@0: private $table; Chris@0: Chris@0: /** Chris@0: * Constructor. Chris@0: * Chris@0: * Calling the constructor will ensure that the database file and table Chris@0: * exist and will create both if they don't. Chris@0: * Chris@0: * @param SQLite3 $sqlite Chris@0: * @param string $table Chris@0: */ Chris@0: public function __construct(SQLite3 $sqlite, $table) Chris@0: { Chris@0: $this->sqlite = $sqlite; Chris@0: $this->table = (string) $table; Chris@0: Chris@0: list($id, $data, $exp) = $this->getFields(); Chris@0: Chris@0: return $this->sqlite->exec(sprintf( Chris@0: 'CREATE TABLE IF NOT EXISTS %s(%s TEXT PRIMARY KEY NOT NULL, %s BLOB, %s INTEGER)', Chris@0: $table, Chris@0: $id, Chris@0: $data, Chris@0: $exp Chris@0: )); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function doFetch($id) Chris@0: { Chris@0: if ($item = $this->findById($id)) { Chris@0: return unserialize($item[self::DATA_FIELD]); Chris@0: } Chris@0: Chris@0: return false; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function doContains($id) Chris@0: { Chris@0: return null !== $this->findById($id, false); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function doSave($id, $data, $lifeTime = 0) Chris@0: { Chris@0: $statement = $this->sqlite->prepare(sprintf( Chris@0: 'INSERT OR REPLACE INTO %s (%s) VALUES (:id, :data, :expire)', Chris@0: $this->table, Chris@0: implode(',', $this->getFields()) Chris@0: )); Chris@0: Chris@0: $statement->bindValue(':id', $id); Chris@0: $statement->bindValue(':data', serialize($data), SQLITE3_BLOB); Chris@0: $statement->bindValue(':expire', $lifeTime > 0 ? time() + $lifeTime : null); Chris@0: Chris@0: return $statement->execute() instanceof SQLite3Result; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function doDelete($id) Chris@0: { Chris@0: list($idField) = $this->getFields(); Chris@0: Chris@0: $statement = $this->sqlite->prepare(sprintf( Chris@0: 'DELETE FROM %s WHERE %s = :id', Chris@0: $this->table, Chris@0: $idField Chris@0: )); Chris@0: Chris@0: $statement->bindValue(':id', $id); Chris@0: Chris@0: return $statement->execute() instanceof SQLite3Result; Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function doFlush() Chris@0: { Chris@0: return $this->sqlite->exec(sprintf('DELETE FROM %s', $this->table)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: protected function doGetStats() Chris@0: { Chris@0: // no-op. Chris@0: } Chris@0: Chris@0: /** Chris@0: * Find a single row by ID. Chris@0: * Chris@0: * @param mixed $id Chris@0: * @param bool $includeData Chris@0: * Chris@0: * @return array|null Chris@0: */ Chris@0: private function findById($id, $includeData = true) Chris@0: { Chris@0: list($idField) = $fields = $this->getFields(); Chris@0: Chris@0: if (!$includeData) { Chris@0: $key = array_search(static::DATA_FIELD, $fields); Chris@0: unset($fields[$key]); Chris@0: } Chris@0: Chris@0: $statement = $this->sqlite->prepare(sprintf( Chris@0: 'SELECT %s FROM %s WHERE %s = :id LIMIT 1', Chris@0: implode(',', $fields), Chris@0: $this->table, Chris@0: $idField Chris@0: )); Chris@0: Chris@0: $statement->bindValue(':id', $id, SQLITE3_TEXT); Chris@0: Chris@0: $item = $statement->execute()->fetchArray(SQLITE3_ASSOC); Chris@0: Chris@0: if ($item === false) { Chris@0: return null; Chris@0: } Chris@0: Chris@0: if ($this->isExpired($item)) { Chris@0: $this->doDelete($id); Chris@0: Chris@0: return null; Chris@0: } Chris@0: Chris@0: return $item; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Gets an array of the fields in our table. Chris@0: * Chris@0: * @return array Chris@0: */ Chris@0: private function getFields() Chris@0: { Chris@0: return array(static::ID_FIELD, static::DATA_FIELD, static::EXPIRATION_FIELD); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Check if the item is expired. Chris@0: * Chris@0: * @param array $item Chris@0: * Chris@0: * @return bool Chris@0: */ Chris@0: private function isExpired(array $item) Chris@0: { Chris@0: return isset($item[static::EXPIRATION_FIELD]) && Chris@0: $item[self::EXPIRATION_FIELD] !== null && Chris@0: $item[self::EXPIRATION_FIELD] < time(); Chris@0: } Chris@0: }