1
0
mirror of synced 2024-12-05 03:06:05 +03:00
This commit is contained in:
doctrine 2006-04-14 22:25:02 +00:00
parent 87fa5b245f
commit 2d4db7b075
12 changed files with 498 additions and 35 deletions

View File

@ -1,5 +1,5 @@
<?php
require_once(Doctrine::getPath().DIRECTORY_SEPARATOR."Cache.class.php");
require_once(Doctrine::getPath().DIRECTORY_SEPARATOR."iCache.class.php");
/**
* Doctrine_CacheFile
* @author Konsta Vesterinen
@ -8,7 +8,7 @@ require_once(Doctrine::getPath().DIRECTORY_SEPARATOR."Cache.class.php");
* @license LGPL
* @version 1.0 alpha
*/
class Doctrine_Cache_File extends Doctrine_Cache implements Countable {
class Doctrine_Cache_File implements Countable {
const STATS_FILE = "stats.cache";
/**
* @var string $path path for the cache files

View File

@ -0,0 +1,260 @@
<?php
require_once(Doctrine::getPath().DIRECTORY_SEPARATOR."Cache.class.php");
class Doctrine_Cache_Sqlite {
/**
* STATS_FILE constant
* the name of the statistics file
*/
const STATS_FILE = "stats.cache";
/**
* SELECT constant
* used as a base for SQL SELECT queries
*/
const SELECT = "SELECT object FROM %s WHERE id %s";
/**
* INSERT constant
* used as a base for SQL INSERT queries
*/
const INSERT = "INSERT INTO %s (id, object) VALUES (?, ?)";
/**
* DELETE constant
* used as a base for SQL DELETE queries
*/
const DELETE = "DELETE FROM %s WHERE id %s";
/**
* @var Doctrine_Table $table
*/
private $table;
/**
* @var PDO $dbh
*/
private $dbh;
/**
* @var array $fetched an array of fetched primary keys
*/
private $fetched = array();
public function __construct(Doctrine_Table $table) {
$this->table = $table;
$dir = $this->table->getSession()->getAttribute(Doctrine::ATTR_CACHE_DIR);
if( ! is_dir($dir))
mkdir($dir, 0777);
$this->path = $dir.DIRECTORY_SEPARATOR;
$this->dbh = new PDO("sqlite:".$this->path."data.cache");
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
try {
$this->dbh->query("CREATE TABLE ".$this->table->getTableName()." (id INTEGER, object TEXT)");
} catch(PDOException $e) {
}
/**
* create stats file
*/
if( ! file_exists($this->path.self::STATS_FILE))
touch($this->path.self::STATS_FILE);
}
/*
* stores a Doctrine_Record into cache
* @param Doctrine_Record $record record to be stored
* @return boolean whether or not storing was successful
*/
public function store(Doctrine_Record $record) {
if($record->getState() != Doctrine_Record::STATE_CLEAN)
return false;
$clone = clone $record;
$id = $clone->getID();
$stmt = $this->dbh->query(sprintf(self::INSERT,$this->table->getTableName()));
$stmt->execute(array($id, serialize($clone)));
return true;
}
/**
* fetches a Doctrine_Record from the cache
* @param integer $id
* @return mixed false on failure, Doctrine_Record on success
*/
public function fetch($id) {
$stmt = $this->dbh->query(sprintf(self::SELECT,$this->table->getTableName(),"= ?"));
$stmt->execute(array($id));
$data = $stmt->fetch(PDO::FETCH_NUM);
if($data === false)
throw new InvalidKeyException();
$this->fetched[] = $id;
$record = unserialize($data[0]);
if(is_string($record)) {
$this->delete($id);
throw new InvalidKeyException();
}
return $record;
}
/**
* fetches multiple records from the cache
* @param array $keys
* @return mixed false on failure, an array of Doctrine_Record objects on success
*/
public function fetchMultiple(array $keys) {
$count = (count($keys)-1);
$sql = sprintf(self::SELECT,$this->table->getTableName(),"IN (".str_repeat("?, ",$count)."?)");
$stmt = $this->dbh->query($sql);
$stmt->execute($keys);
while($data = $stmt->fetch(PDO::FETCH_NUM)) {
$array[] = unserialize($data[0]);
}
$this->fetched = array_merge($this->fetched, $keys);
if( ! isset($array))
return false;
return $array;
}
/**
* deletes all records from cache
* @return void
*/
public function deleteAll() {
$stmt = $this->dbh->query("DELETE FROM ".$this->table->getTableName());
return $stmt->rowCount();
}
/**
* @return void
*/
public function delete($id) {
$stmt = $this->dbh->query(sprintf(self::DELETE,$this->table->getTableName(),"= ?"));
$stmt->execute(array($id));
if($stmt->rowCount() > 0)
return true;
return false;
}
/**
* count
* @return integer
*/
public function count() {
$stmt = $this->dbh->query("SELECT COUNT(*) FROM ".$this->table->getTableName());
$data = $stmt->fetch(PDO::FETCH_NUM);
// table has two columns so we have to divide the count by two
return ($data[0] / 2);
}
/**
* @param array $keys
* @return integer
*/
public function deleteMultiple(array $keys) {
if(empty($keys))
return 0;
$count = (count($keys)-1);
$sql = sprintf(self::DELETE,$this->table->getTableName(),"IN (".str_repeat("?, ",$count)."?)");
$stmt = $this->dbh->query($sql);
$stmt->execute($keys);
return $stmt->rowCount();
}
/**
* getStats
* @return array an array of fetch statistics, keys as primary keys
* and values as fetch times
*/
public function getStats() {
$f = file_get_contents($this->path.self::STATS_FILE);
// every cache file starts with a ":"
$f = substr(trim($f),1);
$e = explode(":",$f);
return array_count_values($e);
}
/**
* clean
* @return void
*/
public function clean() {
$stats = $this->getStats();
asort($stats);
$size = $this->table->getAttribute(Doctrine::ATTR_CACHE_SIZE);
$count = count($stats);
if($count <= $size)
return 0;
$e = $count - $size;
$keys = array();
foreach($stats as $id => $count) {
if( ! $e--)
break;
$keys[] = $id;
}
return $this->deleteMultiple($keys);
}
/**
* saves statistics
* @return boolean
*/
public function saveStats() {
if( ! empty($this->fetched)) {
$fp = fopen($this->path.self::STATS_FILE,"a");
fwrite($fp,":".implode(":",$this->fetched));
fclose($fp);
$this->fetched = array();
return true;
}
return false;
}
/**
* autoClean
* $ttl is the number of page loads between each cache cleaning
* the default is 100 page loads
*
* this means that the average number of page loads between
* each cache clean is 100 page loads (= 100 constructed Doctrine_Managers)
* @return boolean
*/
public function autoClean() {
$ttl = $this->table->getAttribute(Doctrine::ATTR_CACHE_TTL);
$l1 = (mt_rand(1,$ttl) / $ttl);
$l2 = (1 - 1/$ttl);
if($l1 > $l2) {
$this->clean();
return true;
}
return false;
}
/**
* destructor
* the purpose of this destructor is to save all the fetched
* primary keys into the cache stats and to clean cache if necessary
*
*/
public function __destruct() {
$this->saveStats();
$this->autoClean();
}
}
?>

View File

@ -91,7 +91,7 @@ abstract class Doctrine_Configurable {
break;
case Doctrine::ATTR_CACHE:
if($value != Doctrine::CACHE_FILE && $value != Doctrine::CACHE_NONE)
if($value != Doctrine::CACHE_SQLITE && $value != Doctrine::CACHE_NONE)
throw new Doctrine_Exception("Unknown cache container. See Doctrine::CACHE_* constants for availible containers.");
break;
default:

View File

@ -1,36 +1,35 @@
<?php
class Doctrine_DataDict {
private $table;
public function __construct(Doctrine_Table $table) {
$this->table = $table;
$manager = $this->table->getSession()->getManager();
private $dbh;
public function __construct(PDO $dbh) {
$manager = Doctrine_Manager::getInstance();
require_once($manager->getRoot()."/adodb-hack/adodb.inc.php");
$dbh = $this->table->getSession()->getDBH();
$this->dbh = $dbh;
$this->dict = NewDataDictionary($dbh);
}
public function metaColumns() {
return $this->dict->metaColumns($this->table->getTableName());
public function metaColumns(Doctrine_Table $table) {
return $this->dict->metaColumns($table->getTableName());
}
public function createTable() {
foreach($this->table->getColumns() as $name => $args) {
public function createTable($tablename, $columns) {
foreach($columns as $name => $args) {
$r[] = $name." ".$this->getADOType($args[0],$args[1])." ".$args[2];
}
$dbh = $this->table->getSession()->getDBH();
$r = implode(", ",$r);
$a = $this->dict->createTableSQL($this->table->getTableName(),$r);
$a = $this->dict->createTableSQL($tablename,$r);
$return = true;
foreach($a as $sql) {
try {
$dbh->query($sql);
$this->dbh->query($sql);
} catch(PDOException $e) {
if($this->dbh->getAttribute(PDO::ATTR_DRIVER_NAME) == "sqlite")
throw $e;
$return = false;
}
}

View File

@ -124,9 +124,9 @@ final class Doctrine {
*/
/**
* file cache constant
* sqlite cache constant
*/
const CACHE_FILE = 0;
const CACHE_SQLITE = 0;
/**
* constant for disabling the caching
*/
@ -217,7 +217,7 @@ final class Doctrine {
$a[] = self::$path.DIRECTORY_SEPARATOR.$entry;
break;
default:
if(is_file(self::$path.DIRECTORY_SEPARATOR.$entry)) {
if(is_file(self::$path.DIRECTORY_SEPARATOR.$entry) && substr($entry,-4) == ".php") {
require_once($entry);
}
endswitch;
@ -226,7 +226,7 @@ final class Doctrine {
$dir = dir($dirname);
$path = $dirname.DIRECTORY_SEPARATOR;
while (false !== ($entry = $dir->read())) {
if(is_file($path.$entry)) {
if(is_file($path.$entry) && substr($entry,-4) == ".php") {
require_once($path.$entry);
}
}

View File

@ -45,7 +45,7 @@ class Doctrine_Manager extends Doctrine_Configurable implements Countable, Itera
Doctrine::ATTR_FETCHMODE => Doctrine::FETCH_LAZY,
Doctrine::ATTR_CACHE_TTL => 100,
Doctrine::ATTR_CACHE_SIZE => 100,
Doctrine::ATTR_CACHE => Doctrine::CACHE_FILE,
Doctrine::ATTR_CACHE => Doctrine::CACHE_NONE,
Doctrine::ATTR_BATCH_SIZE => 5,
Doctrine::ATTR_LISTENER => new EmptyEventListener(),
Doctrine::ATTR_PK_COLUMNS => array("id"),

View File

@ -162,8 +162,8 @@ class Doctrine_Table extends Doctrine_Configurable {
endswitch;
if($this->getAttribute(Doctrine::ATTR_CREATE_TABLES)) {
$dict = new Doctrine_DataDict($this);
$dict->createTable($this->columns);
$dict = new Doctrine_DataDict($this->getSession()->getDBH());
$dict->createTable($this->tableName, $this->columns);
}
}
@ -192,8 +192,8 @@ class Doctrine_Table extends Doctrine_Configurable {
$this->repository = new Doctrine_Repository($this);
switch($this->getAttribute(Doctrine::ATTR_CACHE)):
case Doctrine::CACHE_FILE:
$this->cache = new Doctrine_Cache_File($this);
case Doctrine::CACHE_SQLITE:
$this->cache = new Doctrine_Cache_Sqlite($this);
break;
case Doctrine::CACHE_NONE:
$this->cache = new Doctrine_Cache($this);

View File

@ -0,0 +1,90 @@
<?php
/**
V4.65 22 July 2005 (c) 2000-2005 John Lim (jlim@natsoft.com.my). All rights reserved.
Released under both BSD license and Lesser GPL library license.
Whenever there is any discrepancy between the two licenses,
the BSD license will take precedence.
Set tabs to 4 for best viewing.
Modified 28 August, 2005 for use with ADOdb Lite by Mark Dickenson
*/
// security - hide paths
if (!defined('ADODB_DIR')) die();
class ADODB2_sqlite extends ADODB_DataDict {
var $dbtype = 'sqlite';
var $seqField = false;
var $metaTablesSQL = "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name";
function ActualType($meta)
{
switch($meta) {
case 'C': return 'VARCHAR';
case 'XL':
case 'X': return 'VARCHAR(250)';
case 'C2': return 'VARCHAR';
case 'X2': return 'VARCHAR(250)';
case 'B': return 'VARCHAR';
case 'D': return 'DATE';
case 'T': return 'DATE';
case 'L': return 'DECIMAL(1)';
case 'I': return 'DECIMAL(10)';
case 'I1': return 'DECIMAL(3)';
case 'I2': return 'DECIMAL(5)';
case 'I4': return 'DECIMAL(10)';
case 'I8': return 'DECIMAL(20)';
case 'F': return 'DECIMAL(32,8)';
case 'N': return 'DECIMAL';
default:
return $meta;
}
}
function AlterColumnSQL($tabname, $flds)
{
if ($this->debug) $this->outp("AlterColumnSQL not supported");
return array();
}
function DropColumnSQL($tabname, $flds)
{
if ($this->debug) $this->outp("DropColumnSQL not supported");
return array();
}
// function MetaType($t,$len=-1,$fieldobj=false)
// {
// }
// function &MetaTables($ttype=false,$showSchema=false,$mask=false)
// {
// global $ADODB_FETCH_MODE;
// }
// function &MetaColumns($table,$upper=true)
// {
// global $ADODB_FETCH_MODE;
// }
// function MetaPrimaryKeys($table, $owner=false)
// {
// }
// function &MetaIndexes($table, $primary = false, $owner = false)
// {
// }
}
?>

View File

@ -0,0 +1,111 @@
<?php
require_once("UnitTestCase.class.php");
class Doctrine_Cache_SqliteTestCase extends Doctrine_UnitTestCase {
public function setUp() {
parent::setUp();
$this->manager->setAttribute(Doctrine::ATTR_CACHE,Doctrine::CACHE_NONE);
$dir = $this->session->getAttribute(Doctrine::ATTR_CACHE_DIR);
if(file_exists($dir.DIRECTORY_SEPARATOR."stats.cache"))
unlink($dir.DIRECTORY_SEPARATOR."stats.cache");
$this->cache = new Doctrine_Cache_Sqlite($this->objTable);
$this->cache->deleteAll();
}
/**
public function testStore() {
// does not store proxy objects
$this->assertFalse($this->cache->store($this->objTable->getProxy(4)));
$this->assertTrue($this->cache->store($this->objTable->find(4)));
$record = $this->cache->fetch(4);
$this->assertTrue($record instanceof Doctrine_Record);
foreach($this->old as $name => $value) {
$this->assertEqual($record->get($name), $value);
}
$this->assertEqual($record->getID(), $this->old->getID());
}
public function testFetchMultiple() {
$this->assertFalse($this->cache->fetchMultiple(array(5,6)));
$this->cache->store($this->objTable->find(5));
$array = $this->cache->fetchMultiple(array(5,6));
$this->assertEqual(gettype($array), "array");
$this->assertEqual(count($array), 1);
$this->assertTrue($array[0] instanceof Doctrine_Record);
}
public function testDeleteMultiple() {
$this->assertEqual($this->cache->deleteMultiple(array()),0);
$this->cache->store($this->objTable->find(5));
$this->cache->store($this->objTable->find(6));
$count = $this->cache->deleteMultiple(array(5,6));
$this->assertEqual($count,2);
$this->cache->store($this->objTable->find(6));
$count = $this->cache->deleteMultiple(array(5,6));
$this->assertEqual($count,1);
}
public function testDelete() {
$this->cache->store($this->objTable->find(5));
$this->assertTrue($this->cache->fetch(5) instanceof Doctrine_Record);
$this->assertEqual($this->cache->delete(5),true);
$this->assertFalse($this->cache->fetch(5));
$this->assertFalse($this->cache->delete(0));
}
public function testFetch() {
$this->assertFalse($this->cache->fetch(3));
}
public function testCount() {
$this->assertEqual($this->cache->count(), 0);
$this->cache->store($this->objTable->find(5));
$this->assertEqual($this->cache->count(), 1);
}
public function testSaveStats() {
$this->assertFalse($this->cache->saveStats());
$this->cache->store($this->objTable->find(5));
$this->cache->store($this->objTable->find(6));
$this->cache->store($this->objTable->find(7));
$this->cache->fetchMultiple(array(5,6,7));
$this->assertTrue($this->cache->saveStats());
$this->assertTrue(gettype($this->cache->getStats()), "array");
$this->assertEqual($this->cache->getStats(),array(5 => 1, 6 => 1, 7 => 1));
$this->cache->fetchMultiple(array(5,6,7));
$this->cache->fetch(5);
$this->cache->fetch(7);
$this->assertTrue($this->cache->saveStats());
$this->assertEqual($this->cache->getStats(),array(5 => 3, 6 => 2, 7 => 3));
}
public function testClean() {
$this->cache->store($this->objTable->find(4));
$this->cache->store($this->objTable->find(5));
$this->cache->store($this->objTable->find(6));
$this->cache->store($this->objTable->find(7));
$this->cache->store($this->objTable->find(8));
$this->cache->store($this->objTable->find(9));
$this->assertEqual($this->cache->count(), 6);
$this->cache->fetch(5);
$this->cache->fetch(7);
$this->cache->fetchMultiple(array(5,6,7));
$this->cache->fetchMultiple(array(5,6,7));
$this->cache->fetchMultiple(array(5,6,7));
$this->cache->fetchMultiple(array(4,5,6,7,8,9));
$this->assertTrue($this->cache->saveStats());
$this->manager->setAttribute(Doctrine::ATTR_CACHE_SIZE, 3);
$this->assertEqual($this->cache->clean(), 3);
}
*/
}
?>

View File

@ -33,9 +33,6 @@ class Doctrine_TableTestCase extends Doctrine_UnitTestCase {
public function testGetSession() {
$this->assertTrue($this->objTable->getSession() instanceof Doctrine_Session);
}
public function testGetCache() {
$this->assertTrue($this->objTable->getCache() instanceof Doctrine_Cache);
}
public function testGetData() {
$this->assertTrue($this->objTable->getData() == array());
}

View File

@ -61,13 +61,12 @@ class Doctrine_UnitTestCase extends UnitTestCase {
foreach($tables as $name) {
$table = $this->session->getTable($name);
$table->getCache()->deleteAll();
}
$this->objTable = $this->session->getTable("User");
$this->repository = $this->objTable->getRepository();
$this->cache = $this->objTable->getCache();
//$this->cache = $this->objTable->getCache();
$this->prepareData();
}

View File

@ -1,4 +1,5 @@
<?php
require_once("ConfigurableTestCase.class.php");
require_once("ManagerTestCase.class.php");
require_once("SessionTestCase.class.php");
@ -10,6 +11,10 @@ require_once("RecordTestCase.class.php");
require_once("DQLParserTestCase.class.php");
require_once("AccessTestCase.class.php");
require_once("ValidatorTestCase.class.php");
require_once("CacheSqliteTestCase.class.php");
print "<pre>";
error_reporting(E_ALL);
@ -30,14 +35,14 @@ $test->addTestCase(new Doctrine_TableTestCase());
$test->addTestCase(new Doctrine_AccessTestCase());
$test->addTestCase(new Doctrine_ConfigurableTestCase());
$test->addTestCase(new Doctrine_EventListenerTestCase());
$test->addTestCase(new Doctrine_DQL_ParserTestCase());
$test->addTestCase(new Doctrine_BatchIteratorTestCase());
//$test->addTestCase(new Doctrine_Cache_FileTestCase());
/**
$test->addTestCase(new Doctrine_Cache_FileTestCase());
$test->addTestCase(new Doctrine_Cache_SqliteTestCase());
*/
@ -46,6 +51,7 @@ $test->addTestCase(new Doctrine_BatchIteratorTestCase());
$test->run(new HtmlReporter());
$dbh = Doctrine_Manager::getInstance()->getCurrentSession()->getDBH();
$a = $dbh->getQueries();
@ -55,4 +61,5 @@ foreach($a as $query) {
$e = explode(" ",$query);
print $query."\n";
}
?>