Commit 98dc74b6 authored by doctrine's avatar doctrine

Added a folder remotely

parent 539c7f3d
<?php
/**
* Doctrine_Access
*
* the purpose of Doctrine_Access is to provice array access
* and property overload interface for subclasses
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
abstract class Doctrine_Access implements ArrayAccess {
/**
* setArray
* @param array $array an array of key => value pairs
*/
public function setArray(array $array) {
foreach($array as $k=>$v):
$this->set($k,$v);
endforeach;
}
/**
* __set -- an alias of set()
* @see set, offsetSet
* @param $name
* @param $value
*/
public function __set($name,$value) {
$this->set($name,$value);
}
/**
* __get -- an alias of get()
* @see get, offsetGet
* @param mixed $name
* @return mixed
*/
public function __get($name) {
return $this->get($name);
}
/**
* @param mixed $offset
* @return boolean -- whether or not the data has a field $offset
*/
public function offsetExists($offset) {
return (bool) isset($this->data[$offset]);
}
/**
* offsetGet -- an alias of get()
* @see get, __get
* @param mixed $offset
* @return mixed
*/
public function offsetGet($offset) {
return $this->get($offset);
}
/**
* sets $offset to $value
* @see set, __set
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet($offset, $value) {
if( ! isset($offset)) {
$this->add($value);
} else
$this->set($offset,$value);
}
/**
* unset a given offset
* @see set, offsetSet, __set
* @param mixed $offset
*/
public function offsetUnset($offset) {
if($this instanceof Doctrine_Collection) {
return $this->remove($offset);
} else {
$this->set($offset,null);
}
}
}
?>
<?php
require_once("Relation.php");
/**
* Doctrine_Association this class takes care of association mapping
* (= many-to-many relationships, where the relationship is handled with an additional relational table
* which holds 2 foreign keys)
*
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class Doctrine_Association extends Doctrine_Relation {
/**
* @var Doctrine_Table $associationTable
*/
private $associationTable;
/**
* the constructor
* @param Doctrine_Table $table foreign factory object
* @param Doctrine_Table $associationTable factory which handles the association
* @param string $local local field name
* @param string $foreign foreign field name
* @param integer $type type of relation
* @see Doctrine_Table constants
*/
public function __construct(Doctrine_Table $table, Doctrine_Table $associationTable, $local, $foreign, $type) {
parent::__construct($table, $local, $foreign, $type);
$this->associationTable = $associationTable;
}
/**
* @return Doctrine_Table
*/
public function getAssociationFactory() {
return $this->associationTable;
}
}
?>
<?php
interface iDoctrine_Cache {
public function store(Doctrine_Record $record);
public function clean();
public function delete($id);
public function fetch($id);
public function exists($id);
}
class Doctrine_Cache implements iDoctrine_Cache {
/**
* implemented by child classes
* @param Doctrine_Record $record
* @return boolean
*/
public function store(Doctrine_Record $record) {
return false;
}
/**
* implemented by child classes
* @return boolean
*/
public function clean() {
return false;
}
/**
* implemented by child classes
* @return boolean
*/
public function delete($id) {
return false;
}
/**
* implemented by child classes
* @throws InvalidKeyException
* @return Doctrine_Record found Data Access Object
*/
public function fetch($id) {
throw new InvalidKeyException();
}
/**
* implemented by child classes
* @param array $keys
* @return boolean
*/
public function fetchMultiple($keys) {
return false;
}
/**
* implemented by child classes
* @param integer $id
* @return boolean
*/
public function exists($id) {
return false;
}
/**
* implemented by child classes
*/
public function deleteMultiple($keys) {
return 0;
}
/**
* implemented by child classes
* @return integer
*/
public function deleteAll() {
return 0;
}
}
?>
<?php
require_once("Access.php");
/**
* Doctrine_Collection
* Collection of Doctrine_Record objects.
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class Doctrine_Collection extends Doctrine_Access implements Countable, IteratorAggregate {
/**
* @var array $data an array containing the data access objects of this collection
*/
protected $data = array();
/**
* @var Doctrine_Table $table each collection has only records of specified table
*/
protected $table;
/**
* @var Doctrine_Record $reference collection can belong to a record
*/
protected $reference;
/**
* @var string $reference_field the reference field of the collection
*/
protected $reference_field;
/**
* @var Doctrine_Relation the record this collection is related to, if any
*/
protected $relation;
/**
* @var boolean $expandable whether or not this collection has been expanded
*/
protected $expandable = true;
/**
* @var array $expanded
*/
protected $expanded = array();
/**
* @var mixed $generator
*/
protected $generator;
/**
* @var Doctrine_Null $null used for extremely fast SQL null value testing
*/
protected static $null;
/**
* constructor
*/
public function __construct(Doctrine_Table $table) {
$this->table = $table;
$name = $table->getAttribute(Doctrine::ATTR_COLL_KEY);
if($name !== null) {
$this->generator = new Doctrine_IndexGenerator($name);
}
}
/**
* initNullObject
*/
public static function initNullObject(Doctrine_Null $null) {
self::$null = $null;
}
/**
* @return object Doctrine_Table
*/
public function getTable() {
return $this->table;
}
/**
* whether or not an offset batch has been expanded
* @return boolean
*/
public function isExpanded($offset) {
return isset($this->expanded[$offset]);
}
/**
* whether or not this collection is expandable
* @return boolean
*/
public function isExpandable() {
return $this->expandable;
}
/**
* @param Doctrine_IndexGenerator $generator
* @return void
*/
public function setGenerator($generator) {
$this->generator = $generator;
}
/**
* @return Doctrine_IndexGenerator
*/
public function getGenerator() {
return $this->generator;
}
/**
* @return array
*/
public function getData() {
return $this->data;
}
/**
* @param array $data
*/
public function addData(array $data) {
$this->data[] = $data;
}
/**
* @return mixed
*/
public function getLast() {
return end($this->data);
}
/**
* @return void
*/
public function setReference(Doctrine_Record $record,Doctrine_Relation $relation) {
$this->reference = $record;
$this->relation = $relation;
if($relation instanceof Doctrine_ForeignKey ||
$relation instanceof Doctrine_LocalKey) {
$this->reference_field = $relation->getForeign();
$value = $record->get($relation->getLocal());
foreach($this->getNormalIterator() as $record) {
if($value !== null) {
$record->rawSet($this->reference_field, $value);
} else {
$record->rawSet($this->reference_field, $this->reference);
}
}
}
}
/**
* @return mixed
*/
public function getReference() {
return $this->reference;
}
/**
* @return boolean
*/
public function expand($key) {
$where = array();
$params = array();
$limit = null;
$offset = null;
switch(get_class($this)):
case "Doctrine_Collection_Offset":
$limit = $this->getLimit();
$offset = (floor($key / $limit) * $limit);
if( ! $this->expandable && isset($this->expanded[$offset]))
return false;
$fields = implode(", ",$this->table->getColumnNames());
break;
default:
if( ! $this->expandable)
return false;
if( ! isset($this->reference))
return false;
$id = $this->reference->getID();
if(empty($id))
return false;
switch(get_class($this)):
case "Doctrine_Collection_Immediate":
$fields = implode(", ",$this->table->getColumnNames());
break;
default:
$fields = implode(", ",$this->table->getPrimaryKeys());
endswitch;
endswitch;
if(isset($this->relation)) {
if($this->relation instanceof Doctrine_ForeignKey) {
$params = array($this->reference->getID());
$where[] = $this->reference_field." = ?";
if( ! isset($offset)) {
$ids = $this->getPrimaryKeys();
if( ! empty($ids)) {
$where[] = $this->table->getIdentifier()." NOT IN (".substr(str_repeat("?, ",count($ids)),0,-2).")";
$params = array_merge($params,$ids);
}
$this->expandable = false;
}
} elseif($this->relation instanceof Doctrine_Association) {
$asf = $fk->getAssociationFactory();
$query = "SELECT ".$foreign." FROM ".$asf->getTableName()." WHERE ".$local."=".$this->getID();
$table = $fk->getTable();
$graph = new Doctrine_DQL_Parser($table->getSession());
$q = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$table->getIdentifier()." IN ($query)";
}
}
$query = "SELECT ".$fields." FROM ".$this->table->getTableName();
// apply column aggregation inheritance
foreach($this->table->getInheritanceMap() as $k => $v) {
$where[] = $k." = ?";
$params[] = $v;
}
if( ! empty($where)) {
$query .= " WHERE ".implode(" AND ",$where);
}
$params = array_merge($params, array_values($this->table->getInheritanceMap()));
$coll = $this->table->execute($query, $params, $limit, $offset);
if( ! isset($offset)) {
foreach($coll as $record) {
if(isset($this->reference_field))
$record->rawSet($this->reference_field,$this->reference);
$this->reference->addReference($record);
}
} else {
$i = $offset;
foreach($coll as $record) {
if(isset($this->reference)) {
$this->reference->addReference($record,$i);
} else
$this->data[$i] = $record;
$i++;
}
$this->expanded[$offset] = true;
// check if the fetched collection's record count is smaller
// than the query limit, if so this collection has been expanded to its max size
if(count($coll) < $limit) {
$this->expandable = false;
}
}
return $coll;
}
/**
* @return boolean
*/
public function remove($key) {
if( ! isset($this->data[$key]))
throw new InvalidKeyException();
$removed = $this->data[$key];
unset($this->data[$key]);
return $removed;
}
/**
* @param mixed $key
* @return boolean
*/
public function contains($key) {
return isset($this->data[$key]);
}
/**
* @param mixed $key
* @return object Doctrine_Record return a specified record
*/
public function get($key) {
if( ! isset($this->data[$key])) {
$this->expand($key);
if( ! isset($this->data[$key]))
$this->data[$key] = $this->table->create();
if(isset($this->reference_field)) {
$value = $this->reference->get($this->relation->getLocal());
if($value !== null) {
$this->data[$key]->rawSet($this->reference_field, $value);
} else {
$this->data[$key]->rawSet($this->reference_field, $this->reference);
}
}
}
return $this->data[$key];
}
/**
* @return array an array containing all primary keys
*/
public function getPrimaryKeys() {
$list = array();
$name = $this->table->getIdentifier();
foreach($this->data as $record):
if(is_array($record) && isset($record[$name])) {
$list[] = $record[$name];
} else {
$list[] = $record->getID();
}
endforeach;
return $list;
}
/**
* returns all keys
* @return array
*/
public function getKeys() {
return array_keys($this->data);
}
/**
* count
* this class implements interface countable
* @return integer number of records in this collection
*/
public function count() {
return count($this->data);
}
/**
* set
* @param integer $key
* @param Doctrine_Record $record
* @return void
*/
public function set($key,Doctrine_Record $record) {
if(isset($this->reference_field))
$record->rawSet($this->reference_field,$this->reference);
$this->data[$key] = $record;
}
/**
* adds a record to collection
* @param Doctrine_Record $record record to be added
* @param string $key optional key for the record
* @return boolean
*/
public function add(Doctrine_Record $record,$key = null) {
if(isset($this->reference_field))
$record->rawSet($this->reference_field,$this->reference);
if(isset($key)) {
if(isset($this->data[$key]))
return false;
$this->data[$key] = $record;
return true;
}
if(isset($this->generator)) {
$key = $this->generator->getIndex($record);
$this->data[$key] = $record;
} else
$this->data[] = $record;
return true;
}
/**
* @param Doctrine_Query $query
* @param integer $key
*/
public function populate(Doctrine_Query $query) {
$name = $this->table->getComponentName();
if($this instanceof Doctrine_Collection_Immediate ||
$this instanceof Doctrine_Collection_Offset) {
$data = $query->getData($name);
if(is_array($data)) {
foreach($data as $k=>$v):
$this->table->setData($v);
$this->add($this->table->getRecord());
endforeach;
}
} elseif($this instanceof Doctrine_Collection_Batch) {
$this->data = $query->getData($name);
if(isset($this->generator)) {
foreach($this->data as $k => $v) {
$record = $this->get($k);
$i = $this->generator->getIndex($record);
$this->data[$i] = $record;
unset($this->data[$k]);
}
}
}
}
/**
* @return Doctrine_Iterator_Normal
*/
public function getNormalIterator() {
return new Doctrine_Iterator_Normal($this);
}
/**
* save
* saves all records
*/
public function save() {
$this->table->getSession()->saveCollection($this);
}
/**
* single shot delete
* deletes all records from this collection
* uses only one database query to perform this operation
* @return boolean
*/
public function delete() {
$ids = $this->table->getSession()->deleteCollection($this);
$this->data = array();
}
/**
* getIterator
* @return object ArrayIterator
*/
public function getIterator() {
$data = $this->data;
return new ArrayIterator($data);
}
/**
* returns a string representation of this object
*/
public function __toString() {
return Doctrine_Lib::getCollectionAsString($this);
}
}
?>
<?php
/**
* Doctrine_Configurable
* the base for Doctrine_Table, Doctrine_Manager and Doctrine_Session
*
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
abstract class Doctrine_Configurable {
/**
* @var array $attributes an array of containing all attributes
*/
private $attributes = array();
/**
* @var $parent the parents of this component
*/
private $parent;
/**
* sets a given attribute
*
* @throws Doctrine_Exception if the value is invalid
* @param integer $attribute
* @param mixed $value
* @return void
*/
final public function setAttribute($attribute,$value) {
switch($attribute):
case Doctrine::ATTR_BATCH_SIZE:
if($value < 0)
throw new Doctrine_Exception("Batch size should be greater than or equal to zero");
break;
case Doctrine::ATTR_CACHE_DIR:
if(substr(trim($value),0,6) == "%ROOT%") {
$dir = dirname(__FILE__);
$value = $dir.substr($value,6);
}
if(! is_dir($value) && ! file_exists($value))
mkdir($value,0777);
break;
case Doctrine::ATTR_CACHE_TTL:
if($value < 1)
throw new Doctrine_Exception("Cache TimeToLive should be greater than or equal to 1");
break;
case Doctrine::ATTR_CACHE_SIZE:
if($value < 1)
throw new Doctrine_Exception("Cache size should be greater than or equal to 1");
break;
case Doctrine::ATTR_CACHE_SLAM:
if($value < 0 || $value > 1)
throw new Doctrine_Exception("Cache slam defense should be a floating point number between 0 and 1");
break;
case Doctrine::ATTR_FETCHMODE:
if($value < 0)
throw new Doctrine_Exception("Unknown fetchmode. See Doctrine::FETCH_* constants.");
break;
case Doctrine::ATTR_LISTENER:
$this->setEventListener($value);
break;
case Doctrine::ATTR_PK_COLUMNS:
if( ! is_array($value))
throw new Doctrine_Exception("The value of Doctrine::ATTR_PK_COLUMNS attribute must be an array");
break;
case Doctrine::ATTR_PK_TYPE:
if($value != Doctrine::INCREMENT_KEY && $value != Doctrine::UNIQUE_KEY)
throw new Doctrine_Exception("The value of Doctrine::ATTR_PK_TYPE attribute must be either Doctrine::INCREMENT_KEY or Doctrine::UNIQUE_KEY");
break;
case Doctrine::ATTR_LOCKMODE:
if($this instanceof Doctrine_Session) {
if($this->getState() != Doctrine_Session::STATE_OPEN)
throw new Doctrine_Exception("Couldn't set lockmode. There are transactions open.");
} elseif($this instanceof Doctrine_Manager) {
foreach($this as $session) {
if($session->getState() != Doctrine_Session::STATE_OPEN)
throw new Doctrine_Exception("Couldn't set lockmode. There are transactions open.");
}
} else {
throw new Doctrine_Exception("Lockmode attribute can only be set at the global or session level.");
}
break;
case Doctrine::ATTR_CREATE_TABLES:
$value = (bool) $value;
break;
case Doctrine::ATTR_COLL_LIMIT:
if($value < 1) {
throw new Doctrine_Exception("Collection limit should be a value greater than or equal to 1.");
}
break;
case Doctrine::ATTR_COLL_KEY:
if( ! ($this instanceof Doctrine_Table))
throw new Doctrine_Exception("This attribute can only be set at table level.");
if( ! $this->hasColumn($value))
throw new Doctrine_Exception("Couldn't set collection key attribute. No such column '$value'");
break;
case Doctrine::ATTR_VLD:
break;
case Doctrine::ATTR_CACHE:
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:
throw new Doctrine_Exception("Unknown attribute.");
endswitch;
$this->attributes[$attribute] = $value;
}
/**
* @param Doctrine_EventListener $listener
* @return void
*/
final public function setEventListener(Doctrine_EventListener $listener) {
$i = Doctrine::ATTR_LISTENER;
$this->attributes[$i] = $listener;
}
/**
* returns the value of an attribute
*
* @param integer $attribute
* @return mixed
*/
final public function getAttribute($attribute) {
$attribute = (int) $attribute;
if($attribute < 1 || $attribute > 16)
throw new InvalidKeyException();
if( ! isset($this->attributes[$attribute])) {
if(isset($this->parent))
return $this->parent->getAttribute($attribute);
return null;
}
return $this->attributes[$attribute];
}
/**
* getAttributes
* returns all attributes as an array
*
* @return array
*/
final public function getAttributes() {
return $this->attributes;
}
/**
* sets a parent for this configurable component
* the parent must be configurable component itself
*
* @param Doctrine_Configurable $component
* @return void
*/
final public function setParent(Doctrine_Configurable $component) {
$this->parent = $component;
}
/**
* getParent
* returns the parent of this component
*
* @return Doctrine_Configurable
*/
final public function getParent() {
return $this->parent;
}
}
?>
<?php
class Doctrine_DB extends PDO implements Countable, IteratorAggregate {
/**
* default DSN
*/
const DSN = "mysql://root:dc34@localhost/test";
/**
* executed queries
*/
private $queries = array();
/**
* execution times of the executed queries
*/
private $exectimes = array();
/**
* constructor
* @param string $dsn
* @param string $username
* @param string $password
*/
public function __construct($dsn,$username,$password) {
parent::__construct($dsn,$username,$password);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array("Doctrine_DBStatement",array($this)));
}
public static function getConn($dsn,$username = null, $password = null) {
static $instance;
if( ! isset($instance)) {
$instance = new Doctrine_DB($dsn,$username,$password);
}
return $instance;
}
/**
* @param string $dsn PEAR::DB like DSN
* format: schema://user:password@address/dbname
*/
public static function getConnection($dsn = null) {
static $instance = array();
$md5 = md5($dsn);
if( ! isset($instance[$md5])) {
if( ! isset($dsn)) {
$a = parse_url(self::DSN);
} else {
$a = parse_url($dsn);
}
$e = array();
$e[0] = $a["scheme"].":host=".$a["host"].";dbname=".substr($a["path"],1);
$e[1] = $a["user"];
$e[2] = $a["pass"];
$instance[$md5] = new Doctrine_DB($e[0],$e[1],$e[2]);
}
return $instance[$md5];
}
/**
* @param string $query query to be executed
*/
public function query($query) {
try {
$this->queries[] = $query;
$time = microtime();
$stmt = parent::query($query);
$this->exectimes[] = (microtime() - $time);
return $stmt;
} catch(PDOException $e) {
throw $e;
}
}
/**
* @param string $query query to be prepared
*/
public function prepare($query) {
$this->queries[] = $query;
return parent::prepare($query);
}
/**
* @param string $time exectime of the last executed query
* @return void
*/
public function addExecTime($time) {
$this->exectimes[] = $time;
}
public function getExecTimes() {
return $this->exectimes;
}
/**
* @return array an array of executed queries
*/
public function getQueries() {
return $this->queries;
}
/**
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->queries);
}
/**
* returns the number of executed queries
* @return integer
*/
public function count() {
return count($this->queries);
}
}
class Doctrine_DBStatement extends PDOStatement {
/**
* @param Doctrine_DB $dbh Doctrine Database Handler
*/
private $dbh;
/**
* @param Doctrine_DB $dbh
*/
private function __construct(Doctrine_DB $dbh) {
$this->dbh = $dbh;
}
/**
* @param array $params
*/
public function execute(array $params = null) {
$time = microtime();
$result = parent::execute($params);
$exectime = (microtime() - $time);
$this->dbh->addExecTime($exectime);
return $result;
}
}
?>
<?php
class Doctrine_DataDict {
private $dbh;
public function __construct(PDO $dbh) {
$manager = Doctrine_Manager::getInstance();
require_once($manager->getRoot()."/adodb-hack/adodb.inc.php");
$this->dbh = $dbh;
$this->dict = NewDataDictionary($dbh);
}
public function metaColumns(Doctrine_Table $table) {
return $this->dict->metaColumns($table->getTableName());
}
public function createTable($tablename, $columns) {
foreach($columns as $name => $args) {
$r[] = $name." ".$this->getADOType($args[0],$args[1])." ".str_replace("|"," ",$args[2]);
}
$r = implode(", ",$r);
$a = $this->dict->createTableSQL($tablename,$r);
$return = true;
foreach($a as $sql) {
try {
$this->dbh->query($sql);
} catch(PDOException $e) {
if($this->dbh->getAttribute(PDO::ATTR_DRIVER_NAME) == "sqlite")
throw $e;
$return = false;
}
}
return $return;
}
/**
* converts doctrine type to adodb type
*
* @param string $type column type
* @param integer $length column length
*/
public function getADOType($type,$length) {
switch($type):
case "string":
case "s":
if($length < 255)
return "C($length)";
elseif($length < 4000)
return "X";
else
return "X2";
break;
case "mbstring":
if($length < 255)
return "C2($length)";
return "X2";
case "clob":
return "XL";
break;
case "d":
case "date":
return "D";
break;
case "float":
case "f":
case "double":
return "F";
break;
case "timestamp":
case "t":
return "T";
break;
case "boolean":
case "bool":
return "L";
break;
case "integer":
case "int":
case "i":
if(empty($length))
return "I8";
elseif($length < 4)
return "I1";
elseif($length < 6)
return "I2";
elseif($length < 10)
return "I4";
elseif($length <= 20)
return "I8";
else
throw new Doctrine_Exception("Too long integer (max length is 20).");
break;
endswitch;
}
}
?>
<?php
require_once("EventListener.php");
class Doctrine_DebugMessage {
private $code;
private $object;
public function __construct($object, $code) {
$this->object = $object;
$this->code = $code;
}
final public function getCode() {
return $this->code;
}
final public function getObject() {
return $this->object;
}
}
class Doctrine_Debugger extends Doctrine_EventListener {
const EVENT_LOAD = 1;
const EVENT_PRELOAD = 2;
const EVENT_SLEEP = 3;
const EVENT_WAKEUP = 4;
const EVENT_UPDATE = 5;
const EVENT_PREUPDATE = 6;
const EVENT_CREATE = 7;
const EVENT_PRECREATE = 8;
const EVENT_SAVE = 9;
const EVENT_PRESAVE = 10;
const EVENT_INSERT = 11;
const EVENT_PREINSERT = 12;
const EVENT_DELETE = 13;
const EVENT_PREDELETE = 14;
const EVENT_EVICT = 15;
const EVENT_PREEVICT = 16;
const EVENT_CLOSE = 17;
const EVENT_PRECLOSE = 18;
const EVENT_OPEN = 19;
const EVENT_COMMIT = 20;
const EVENT_PRECOMMIT = 21;
const EVENT_ROLLBACK = 22;
const EVENT_PREROLLBACK = 23;
const EVENT_BEGIN = 24;
const EVENT_PREBEGIN = 25;
const EVENT_COLLDELETE = 26;
const EVENT_PRECOLLDELETE = 27;
private $debug;
public function getMessages() {
return $this->debug;
}
public function onLoad(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_LOAD);
}
public function onPreLoad(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PRELOAD);
}
public function onSleep(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_SLEEP);
}
public function onWakeUp(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_WAKEUP);
}
public function onUpdate(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_UPDATE);
}
public function onPreUpdate(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREUPDATE);
}
public function onCreate(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_CREATE);
}
public function onPreCreate(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PRECREATE);
}
public function onSave(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_SAVE);
}
public function onPreSave(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PRESAVE);
}
public function onInsert(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_INSERT);
}
public function onPreInsert(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREINSERT);
}
public function onDelete(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_DELETE);
}
public function onPreDelete(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREDELETE);
}
public function onEvict(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_EVICT);
}
public function onPreEvict(Doctrine_Record $record) {
$this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREEVICT);
}
public function onClose(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_CLOSE);
}
public function onPreClose(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PRECLOSE);
}
public function onOpen(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_OPEN);
}
public function onTransactionCommit(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_COMMIT);
}
public function onPreTransactionCommit(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PRECOMMIT);
}
public function onTransactionRollback(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_ROLLBACK);
}
public function onPreTransactionRollback(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PREROLLBACK);
}
public function onTransactionBegin(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_BEGIN);
}
public function onPreTransactionBegin(Doctrine_Session $session) {
$this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PREBEGIN);
}
public function onCollectionDelete(Doctrine_Collection $collection) {
$this->debug[] = new Doctrine_DebugMessage($collection,self::EVENT_COLLDELETE);
}
public function onPreCollectionDelete(Doctrine_Collection $collection) {
$this->debug[] = new Doctrine_DebugMessage($collection,self::EVENT_PRECOLLDELETE);
}
}
?>
<?php
/**
* interface for event listening, forces all classes that extend
* Doctrine_EventListener to have the same method arguments as their parent
*/
interface iDoctrine_EventListener {
public function onLoad(Doctrine_Record $record);
public function onPreLoad(Doctrine_Record $record);
public function onUpdate(Doctrine_Record $record);
public function onPreUpdate(Doctrine_Record $record);
public function onCreate(Doctrine_Record $record);
public function onPreCreate(Doctrine_Record $record);
public function onSave(Doctrine_Record $record);
public function onPreSave(Doctrine_Record $record);
public function onInsert(Doctrine_Record $record);
public function onPreInsert(Doctrine_Record $record);
public function onDelete(Doctrine_Record $record);
public function onPreDelete(Doctrine_Record $record);
public function onEvict(Doctrine_Record $record);
public function onPreEvict(Doctrine_Record $record);
public function onSaveCascade(Doctrine_Record $record);
public function onPreSaveCascade(Doctrine_Record $record);
public function onDeleteCascade(Doctrine_Record $record);
public function onPreDeleteCascade(Doctrine_Record $record);
public function onSleep(Doctrine_Record $record);
public function onWakeUp(Doctrine_Record $record);
public function onClose(Doctrine_Session $session);
public function onPreClose(Doctrine_Session $session);
public function onOpen(Doctrine_Session $session);
public function onTransactionCommit(Doctrine_Session $session);
public function onPreTransactionCommit(Doctrine_Session $session);
public function onTransactionRollback(Doctrine_Session $session);
public function onPreTransactionRollback(Doctrine_Session $session);
public function onTransactionBegin(Doctrine_Session $session);
public function onPreTransactionBegin(Doctrine_Session $session);
public function onCollectionDelete(Doctrine_Collection $collection);
public function onPreCollectionDelete(Doctrine_Collection $collection);
}
/**
* Doctrine_EventListener all event listeners extend this base class
* the empty methods allow child classes to only implement the methods they need to implement
*
*
* @author Konsta Vesterinen
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
* @version 1.0 alpha
*
*/
abstract class Doctrine_EventListener implements iDoctrine_EventListener {
public function onLoad(Doctrine_Record $record) { }
public function onPreLoad(Doctrine_Record $record) { }
public function onSleep(Doctrine_Record $record) { }
public function onWakeUp(Doctrine_Record $record) { }
public function onUpdate(Doctrine_Record $record) { }
public function onPreUpdate(Doctrine_Record $record) { }
public function onCreate(Doctrine_Record $record) { }
public function onPreCreate(Doctrine_Record $record) { }
public function onSave(Doctrine_Record $record) { }
public function onPreSave(Doctrine_Record $record) { }
public function onInsert(Doctrine_Record $record) { }
public function onPreInsert(Doctrine_Record $record) { }
public function onDelete(Doctrine_Record $record) { }
public function onPreDelete(Doctrine_Record $record) { }
public function onEvict(Doctrine_Record $record) { }
public function onPreEvict(Doctrine_Record $record) { }
public function onSaveCascade(Doctrine_Record $record) { }
public function onPreSaveCascade(Doctrine_Record $record) { }
public function onDeleteCascade(Doctrine_Record $record) { }
public function onPreDeleteCascade(Doctrine_Record $record) { }
public function onClose(Doctrine_Session $session) { }
public function onPreClose(Doctrine_Session $session) { }
public function onOpen(Doctrine_Session $session) { }
public function onTransactionCommit(Doctrine_Session $session) { }
public function onPreTransactionCommit(Doctrine_Session $session) { }
public function onTransactionRollback(Doctrine_Session $session) { }
public function onPreTransactionRollback(Doctrine_Session $session) { }
public function onTransactionBegin(Doctrine_Session $session) { }
public function onPreTransactionBegin(Doctrine_Session $session) { }
public function onCollectionDelete(Doctrine_Collection $collection) { }
public function onPreCollectionDelete(Doctrine_Collection $collection) { }
}
/**
* an emtpy listener all components use this by default
*/
class EmptyEventListener extends Doctrine_EventListener { }
?>
<?php
class InvalidKeyException extends Exception { }
class InvalidTypeException extends Exception { }
class Doctrine_Exception extends Exception { }
class DQLException extends Doctrine_Exception { }
?>
<?php
require_once("Relation.php");
/**
* Foreign Key
*/
class Doctrine_ForeignKey extends Doctrine_Relation { }
?>
<?php
class Doctrine_Form implements Iterator {
protected $record;
protected $elements = array();
protected $columns;
protected $current;
protected $keys;
protected $index;
protected $count;
public function __construct(Doctrine_Record $record) {
$this->record = $record;
$this->columns = $record->getTable()->getColumns();
$this->keys = array_keys($this->columns);
$this->index = 0;
$this->count = count($this->keys);
}
public function current() {
$i = $this->index;
$column = $this->keys[$i];
$definitions = $this->columns[$column];
$e = explode("|",$definitions[2]);
$enum = false;
foreach($e as $v) {
$e2 = explode(":",$v);
if($e2[0] == "enum") {
$enum = explode("-",$e2[1]);
break;
}
}
$length = $definitions[1];
if( ! in_array("autoincrement",$e) && ! in_array("protected",$e)) {
if($enum) {
$elements[$column] = "<select name='data[$column]'>\n";
foreach($enum as $k => $v) {
if($this->record->get($column) == $k) {
$str = 'selected';
} else
$str = '';
$elements[$column] .= " <option value='$k' $str>$v</option>\n";
}
$elements[$column] .= "</select>\n";
} else {
if($length <= 255) {
$elements[$column] = "<input name='data[$column]' type='text' value='".$this->record->get($column)."' maxlength=$length \>\n";
} else {
$elements[$column] = "<textarea name='data[$column]' cols=40 rows=10>".$this->record->get($column)."</textarea>\n";
}
}
return $elements[$column];
} else {
$this->index++;
if($this->index < $this->count)
return self::current();
}
}
public function key() {
$i = $this->index;
return $this->keys[$i];
}
public function next() {
$this->index++;
}
public function rewind() {
$this->index = 0;
}
public function valid() {
if($this->index >= $this->count)
return false;
return true;
}
}
?>
<?php
class Doctrine_Identifier {
/**
* constant for unique identifier
*/
const UNIQUE = 0;
/**
* constant for auto_increment identifier
*/
const AUTO_INCREMENT = 1;
/**
* constant for sequence identifier
*/
const SEQUENCE = 2;
/**
* constant for normal identifier
*/
const NORMAL = 3;
/**
* constant for composite identifier
*/
const COMPOSITE = 4;
}
?>
<?php
abstract class Doctrine_Creator {
protected $_table;
/**
* constructor
*
* @param Doctrine_Table $table
*/
public function __construct(Doctrine_Table $table) {
$this->_table = $_table;
}
abstract public function get();
}
class Doctrine_IdentityMap {
private $identityMap = array();
/**
* first checks if record exists in identityMap, if not
* returns a new record
*
* @return Doctrine_Record
*/
public function get() {
$key = $this->getIdentifier();
if( ! is_array($key))
$key = array($key);
foreach($key as $k) {
if( ! isset($this->data[$k]))
throw new Doctrine_Exception("No primary key found");
$id[] = $this->data[$k];
}
$id = implode(' ', $id);
if(isset($this->identityMap[$id]))
$record = $this->identityMap[$id];
else {
$record = new $this->name($this);
$this->identityMap[$id] = $record;
}
$this->data = array();
return $record;
}
}
?>
<?php
class Doctrine_IndexGenerator {
/**
* @var string $name
*/
private $name;
/**
* @param string $name
*/
public function __construct($name) {
$this->name = $name;
}
/**
* @param Doctrine_Record $record
* @return mixed
*/
public function getIndex(Doctrine_Record $record) {
$value = $record->get($this->name);
if($value === null)
throw new Doctrine_Exception("Couldn't create collection index. Record field '".$this->name."' was null.");
return $value;
}
}
?>
<?php
/**
* Doctrine_Iterator
* iterates through Doctrine_Collection
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
abstract class Doctrine_Iterator implements Iterator {
/**
* @var Doctrine_Collection $collection
*/
protected $collection;
/**
* @var array $keys
*/
protected $keys;
/**
* @var mixed $key
*/
protected $key;
/**
* @var integer $index
*/
protected $index;
/**
* @var integer $count
*/
protected $count;
/**
* constructor
* @var Doctrine_Collection $collection
*/
public function __construct(Doctrine_Collection $collection) {
$this->collection = $collection;
$this->keys = $this->collection->getKeys();
$this->count = $this->collection->count();
}
/**
* rewinds the iterator
*
* @return void
*/
public function rewind() {
$this->index = 0;
$i = $this->index;
if(isset($this->keys[$i]))
$this->key = $this->keys[$i];
}
/**
* returns the current key
*
* @return integer
*/
public function key() {
return $this->key;
}
/**
* returns the current record
*
* @return Doctrine_Record
*/
public function current() {
return $this->collection->get($this->key);
}
/**
* advances the internal pointer
*
* @return void
*/
public function next() {
$this->index++;
$i = $this->index;
if(isset($this->keys[$i]))
$this->key = $this->keys[$i];
}
}
?>
<?php
class Doctrine_Lib {
/**
* @param integer $state the state of record
* @see Doctrine_Record::STATE_* constants
* @return string string representation of given state
*/
public static function getRecordStateAsString($state) {
switch($state):
case Doctrine_Record::STATE_PROXY:
return "proxy";
break;
case Doctrine_Record::STATE_CLEAN:
return "persistent clean";
break;
case Doctrine_Record::STATE_DIRTY:
return "persistent dirty";
break;
case Doctrine_Record::STATE_TDIRTY:
return "transient dirty";
break;
case Doctrine_Record::STATE_TCLEAN:
return "transient clean";
break;
endswitch;
}
/**
* returns a string representation of Doctrine_Record object
* @param Doctrine_Record $record
* @return string
*/
public function getRecordAsString(Doctrine_Record $record) {
$r[] = "<pre>";
$r[] = "Component : ".$record->getTable()->getComponentName();
$r[] = "ID : ".$record->getID();
$r[] = "References : ".count($record->getReferences());
$r[] = "State : ".Doctrine_Lib::getRecordStateAsString($record->getState());
$r[] = "OID : ".$record->getOID();
$r[] = "</pre>";
return implode("\n",$r)."<br />";
}
/**
* getStateAsString
* returns a given session state as string
* @param integer $state session state
*/
public static function getSessionStateAsString($state) {
switch($state):
case Doctrine_Session::STATE_OPEN:
return "open";
break;
case Doctrine_Session::STATE_CLOSED:
return "closed";
break;
case Doctrine_Session::STATE_BUSY:
return "busy";
break;
case Doctrine_Session::STATE_ACTIVE:
return "active";
break;
endswitch;
}
/**
* returns a string representation of Doctrine_Session object
* @param Doctrine_Session $session
* @return string
*/
public function getSessionAsString(Doctrine_Session $session) {
$r[] = "<pre>";
$r[] = "Doctrine_Session object";
$r[] = "State : ".Doctrine_Lib::getSessionStateAsString($session->getState());
$r[] = "Open Transactions : ".$session->getTransactionLevel();
$r[] = "Open Factories : ".$session->count();
$sum = 0;
$rsum = 0;
$csum = 0;
foreach($session->getTables() as $objTable) {
if($objTable->getCache() instanceof Doctrine_Cache_File) {
$sum += array_sum($objTable->getCache()->getStats());
$rsum += $objTable->getRepository()->count();
$csum += $objTable->getCache()->count();
}
}
$r[] = "Cache Hits : ".$sum." hits ";
$r[] = "Cache : ".$csum." objects ";
$r[] = "Repositories : ".$rsum." objects ";
$queries = false;
if($session->getDBH() instanceof Doctrine_DB) {
$handler = "Doctrine Database Handler";
$queries = count($session->getDBH()->getQueries());
$sum = array_sum($session->getDBH()->getExecTimes());
} elseif($session->getDBH() instanceof PDO) {
$handler = "PHP Native PDO Driver";
} else
$handler = "Unknown Database Handler";
$r[] = "DB Handler : ".$handler;
if($queries) {
$r[] = "Executed Queries : ".$queries;
$r[] = "Sum of Exec Times : ".$sum;
}
$r[] = "</pre>";
return implode("\n",$r)."<br>";
}
/**
* returns a string representation of Doctrine_Table object
* @param Doctrine_Table $table
* @return string
*/
public function getTableAsString(Doctrine_Table $table) {
$r[] = "<pre>";
$r[] = "Component : ".$this->getComponentName();
$r[] = "Table : ".$this->getTableName();
$r[] = "Repository : ".$this->getRepository()->count()." objects";
if($table->getCache() instanceof Doctrine_Cache_File) {
$r[] = "Cache : ".$this->getCache()->count()." objects";
$r[] = "Cache hits : ".array_sum($this->getCache()->getStats())." hits";
}
$r[] = "</pre>";
return implode("\n",$r)."<br>";
}
/**
* returns a string representation of Doctrine_Collection object
* @param Doctrine_Collection $collection
* @return string
*/
public function getCollectionAsString(Doctrine_Collection $collection) {
$r[] = "<pre>";
$r[] = get_class($collection);
foreach($collection as $key => $record) {
$r[] = "Key : ".$key." ID : ".$record->getID();
}
$r[] = "</pre>";
return implode("\n",$r);
}
}
?>
<?php
require_once("Relation.php");
/**
* Local Key
*/
class Doctrine_LocalKey extends Doctrine_Relation { }
?>
<?php
require_once("Configurable.php");
require_once("EventListener.php");
/**
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*
* Doctrine_Manager is the base component of all doctrine based projects.
* It opens and keeps track of all sessions (database connections).
*/
class Doctrine_Manager extends Doctrine_Configurable implements Countable, IteratorAggregate {
/**
* @var array $session an array containing all the opened sessions
*/
private $sessions = array();
/**
* @var integer $index the incremented index
*/
private $index = 0;
/**
* @var integer $currIndex the current session index
*/
private $currIndex = 0;
/**
* @var string $root root directory
*/
private $root;
/**
* @var Doctrine_Null $null Doctrine_Null object, used for extremely fast null value checking
*/
private $null;
/**
* constructor
*/
private function __construct() {
$this->root = dirname(__FILE__);
$this->null = new Doctrine_Null;
Doctrine_Record::initNullObject($this->null);
Doctrine_Collection::initNullObject($this->null);
}
/**
* @return Doctrine_Null
*/
final public function getNullObject() {
return $this->null;
}
/**
* setDefaultAttributes
* sets default attributes
*
* @return boolean
*/
final public function setDefaultAttributes() {
static $init = false;
if( ! $init) {
$init = true;
$attributes = array(
Doctrine::ATTR_CACHE_DIR => "%ROOT%".DIRECTORY_SEPARATOR."cachedir",
Doctrine::ATTR_FETCHMODE => Doctrine::FETCH_LAZY,
Doctrine::ATTR_CACHE_TTL => 100,
Doctrine::ATTR_CACHE_SIZE => 100,
Doctrine::ATTR_CACHE => Doctrine::CACHE_NONE,
Doctrine::ATTR_BATCH_SIZE => 5,
Doctrine::ATTR_COLL_LIMIT => 5,
Doctrine::ATTR_LISTENER => new EmptyEventListener(),
Doctrine::ATTR_PK_COLUMNS => array("id"),
Doctrine::ATTR_PK_TYPE => Doctrine::INCREMENT_KEY,
Doctrine::ATTR_LOCKMODE => 1,
Doctrine::ATTR_VLD => false,
Doctrine::ATTR_CREATE_TABLES => true
);
foreach($attributes as $attribute => $value) {
$old = $this->getAttribute($attribute);
if($old === null)
$this->setAttribute($attribute,$value);
}
return true;
}
return false;
}
/**
* returns the root directory of Doctrine
*
* @return string
*/
final public function getRoot() {
return $this->root;
}
/**
* getInstance
* returns an instance of this class
* (this class uses the singleton pattern)
*
* @return Doctrine_Manager
*/
final public static function getInstance() {
static $instance;
if( ! isset($instance))
$instance = new self();
return $instance;
}
/**
* openSession
* opens a new session and saves it to Doctrine_Manager->sessions
*
* @param PDO $pdo PDO database driver
* @param string $name name of the session, if empty numeric key is used
* @return Doctrine_Session
*/
final public function openSession(PDO $pdo, $name = null) {
// initialize the default attributes
$this->setDefaultAttributes();
if($name !== null) {
$name = (string) $name;
if(isset($this->sessions[$name]))
throw new InvalidKeyException();
} else {
$name = $this->index;
$this->index++;
}
switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)):
case "mysql":
$this->sessions[$name] = new Doctrine_Session_Mysql($this,$pdo);
break;
case "sqlite":
$this->sessions[$name] = new Doctrine_Session_Sqlite($this,$pdo);
break;
case "pgsql":
$this->sessions[$name] = new Doctrine_Session_Pgsql($this,$pdo);
break;
case "oci":
$this->sessions[$name] = new Doctrine_Session_Oracle($this,$pdo);
break;
case "mssql":
$this->sessions[$name] = new Doctrine_Session_Mssql($this,$pdo);
break;
case "firebird":
$this->sessions[$name] = new Doctrine_Session_Firebird($this,$pdo);
break;
case "informix":
$this->sessions[$name] = new Doctrine_Session_Informix($this,$pdo);
break;
endswitch;
$this->currIndex = $name;
return $this->sessions[$name];
}
/**
* getSession
* @param integer $index
* @return object Doctrine_Session
* @throws InvalidKeyException
*/
final public function getSession($index) {
if( ! isset($this->sessions[$index]))
throw new InvalidKeyException();
$this->currIndex = $index;
return $this->sessions[$index];
}
/**
* closes the session
*
* @param Doctrine_Session $session
* @return void
*/
final public function closeSession(Doctrine_Session $session) {
$session->close();
unset($session);
}
/**
* getSessions
* returns all opened sessions
*
* @return array
*/
final public function getSessions() {
return $this->sessions;
}
/**
* setCurrentSession
* sets the current session to $key
*
* @param mixed $key the session key
* @throws InvalidKeyException
* @return void
*/
final public function setCurrentSession($key) {
$key = (string) $key;
if( ! isset($this->sessions[$key]))
throw new InvalidKeyException();
$this->currIndex = $key;
}
/**
* count
* returns the number of opened sessions
*
* @return integer
*/
public function count() {
return count($this->sessions);
}
/**
* getIterator
* returns an ArrayIterator that iterates through all sessions
*
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->sessions);
}
/**
* getCurrentSession
* returns the current session
*
* @throws Doctrine_Session_Exception if there are no open sessions
* @return Doctrine_Session
*/
final public function getCurrentSession() {
$i = $this->currIndex;
if( ! isset($this->sessions[$i]))
throw new Doctrine_Session_Exception();
return $this->sessions[$i];
}
/**
* __toString
* returns a string representation of this object
*
* @return string
*/
public function __toString() {
$r[] = "<pre>";
$r[] = "Doctrine_Manager";
$r[] = "Sessions : ".count($this->sessions);
$r[] = "</pre>";
return implode("\n",$r);
}
}
?>
<?php
class Doctrine_Module implements IteratorAggregate, Countable {
/**
* @var array $components an array containing all the components in this module
*/
private $components = array();
/**
* @var string $name the name of this module
*/
private $name;
/**
* constructor
*
* @param string $name the name of this module
*/
public function __construct($name) {
$this->name = $name;
}
/**
* returns the name of this module
*
* @return string
*/
public function getName() {
return $this->name;
}
/**
* flush
* saves all components
*
* @return void
*/
public function flush() {
$session = Doctrine_Manager::getInstance()->getCurrentSession();
$tree = $session->buildFlushTree($this->components);
}
/**
* getIterator
* this class implements IteratorAggregate interface
* returns an iterator that iterates through the components
* in this module
*
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->components);
}
/**
* count
* this class implements Countable interface
* returns the number of components in this module
*
* @return integer
*/
public function count() {
return count($this->components);
}
}
?>
<?php
/**
* Doctrine_Null
*/
class Doctrine_Null { }
?>
<?php
require_once("Access.php");
/**
* Doctrine_Query
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
* @version 1.0 alpha
*/
class Doctrine_Query extends Doctrine_Access {
/**
* @var array $fetchmodes an array containing all fetchmodes
*/
private $fetchModes = array();
/**
* @var array $tables an array containing all the tables used in the query
*/
private $tables = array();
/**
* @var array $collections an array containing all collections this parser has created/will create
*/
private $collections = array();
private $joined = array();
private $joins = array();
/**
* @var array $data fetched data
*/
private $data = array();
/**
* @var Doctrine_Session $session Doctrine_Session object
*/
private $session;
private $inheritanceApplied = false;
private $aggregate = false;
/**
* @var array $connectors component connectors
*/
private $connectors = array();
/**
* @var array $dql DQL query string parts
*/
protected $dql = array(
"columns" => array(),
"from" => array(),
"join" => array(),
"where" => array(),
"group" => array(),
"having" => array(),
"orderby" => array(),
"limit" => false,
"offset" => false,
);
/**
* @var array $parts SQL query string parts
*/
protected $parts = array(
"columns" => array(),
"from" => array(),
"join" => array(),
"where" => array(),
"group" => array(),
"having" => array(),
"orderby" => array(),
"limit" => false,
"offset" => false,
);
/**
* constructor
*
* @param Doctrine_Session $session
*/
public function __construct(Doctrine_Session $session) {
$this->session = $session;
}
/**
* clear
* resets all the variables
*
* @return void
*/
private function clear() {
$this->fetchModes = array();
$this->tables = array();
$this->parts = array(
"columns" => array(),
"from" => array(),
"join" => array(),
"where" => array(),
"group" => array(),
"having" => array(),
"orderby" => array(),
"limit" => false,
"offset" => false,
);
$this->inheritanceApplied = false;
$this->aggregate = false;
$this->data = array();
$this->connectors = array();
$this->collections = array();
$this->joined = array();
$this->joins = array();
}
/**
* loadFields
* loads fields for a given table and
* constructs a little bit of sql for every field
*
* fields of the tables become: [tablename].[fieldname] as [tablename]__[fieldname]
*
* @access private
* @param object Doctrine_Table $table a Doctrine_Table object
* @param integer $fetchmode fetchmode the table is using eg. Doctrine::FETCH_LAZY
* @param array $names fields to be loaded (only used in lazy property loading)
* @return void
*/
private function loadFields(Doctrine_Table $table, $fetchmode, array $names) {
$name = $table->getComponentName();
switch($fetchmode):
case Doctrine::FETCH_OFFSET:
$this->limit = $table->getAttribute(Doctrine::ATTR_COLL_LIMIT);
case Doctrine::FETCH_IMMEDIATE:
if( ! empty($names))
throw new Doctrine_Exception("Lazy property loading can only be used with fetching strategies lazy, batch and lazyoffset.");
$names = $table->getColumnNames();
break;
case Doctrine::FETCH_LAZY_OFFSET:
$this->limit = $table->getAttribute(Doctrine::ATTR_COLL_LIMIT);
case Doctrine::FETCH_LAZY:
case Doctrine::FETCH_BATCH:
$names = array_merge($table->getPrimaryKeys(), $names);
break;
default:
throw new Doctrine_Exception("Unknown fetchmode.");
endswitch;
$cname = $table->getComponentName();
$this->fetchModes[$cname] = $fetchmode;
$tablename = $table->getTableName();
$count = count($this->tables);
foreach($names as $name) {
if($count == 0) {
$this->parts["columns"][] = $tablename.".".$name;
} else {
$this->parts["columns"][] = $tablename.".".$name." AS ".$cname."__".$name;
}
}
}
/**
* sets a query part
*
* @param string $name
* @param array $args
* @return void
*/
public function __call($name, $args) {
$name = strtolower($name);
if(isset($this->parts[$name])) {
$method = "parse".ucwords($name);
switch($name):
case "where":
$this->parts[$name] = array($this->$method($args[0]));
break;
case "limit":
case "offset":
if($args[0] == null)
$args[0] = false;
$this->parts[$name] = $args[0];
break;
case "from":
$this->parts['columns'] = array();
$this->joins = array();
$this->tables = array();
$this->fetchModes = array();
default:
$this->parts[$name] = array();
$this->$method($args[0]);
endswitch;
}
return $this;
}
/**
* returns a query part
*
* @param $name query part name
* @return mixed
*/
public function get($name) {
if( ! isset($this->parts[$name]))
return false;
return $this->parts[$name];
}
/**
* sets a query part
*
* @param $name query part name
* @param $value query part value
* @return boolean
*/
public function set($name, $value) {
if(isset($this->parts[$name])) {
$method = "parse".ucwords($name);
switch($name):
case "where":
$this->parts[$name] = array($this->$method($value));
break;
case "limit":
case "offset":
if($value == null)
$value = false;
$this->parts[$name] = $value;
break;
case "from":
$this->parts['columns'] = array();
$this->joins = array();
$this->tables = array();
$this->fetchModes = array();
default:
$this->parts[$name] = array();
$this->$method($value);
endswitch;
return true;
}
return false;
}
/**
* returns the built sql query
*
* @return string
*/
final public function getQuery() {
if(empty($this->parts["columns"]) || empty($this->parts["from"]))
return false;
// build the basic query
$q = "SELECT ".implode(", ",$this->parts["columns"]).
" FROM ";
foreach($this->parts["from"] as $tname => $bool) {
$a[] = $tname;
}
$q .= implode(", ",$a);
if( ! empty($this->parts['join'])) {
foreach($this->parts['join'] as $part) {
$q .= " ".implode(' ', $part);
}
}
$this->applyInheritance();
if( ! empty($this->parts["where"]))
$q .= " WHERE ".implode(" ",$this->parts["where"]);
if( ! empty($this->parts["orderby"]))
$q .= " ORDER BY ".implode(", ",$this->parts["orderby"]);
if( ! empty($this->parts["limit"]) || ! empty($this->offset))
$q = $this->session->modifyLimitQuery($q,$this->parts["limit"],$this->offset);
return $q;
}
/**
* sql delete for mysql
*/
final public function buildDelete() {
if(empty($this->parts["columns"]) || empty($this->parts["from"]))
return false;
$a = array_merge(array_keys($this->parts["from"]),$this->joined);
$q = "DELETE ".implode(", ",$a)." FROM ";
$a = array();
foreach($this->parts["from"] as $tname => $bool) {
$str = $tname;
if(isset($this->parts["join"][$tname]))
$str .= " ".$this->parts["join"][$tname];
$a[] = $str;
}
$q .= implode(", ",$a);
$this->applyInheritance();
if( ! empty($this->parts["where"]))
$q .= " WHERE ".implode(" ",$this->parts["where"]);
if( ! empty($this->parts["orderby"]))
$q .= " ORDER BY ".implode(", ",$this->parts["orderby"]);
if( ! empty($this->parts["limit"]) && ! empty($this->offset))
$q = $this->session->modifyLimitQuery($q,$this->parts["limit"],$this->offset);
return $q;
}
/**
* applyInheritance
* applies column aggregation inheritance to DQL query
*
* @return boolean
*/
final public function applyInheritance() {
if($this->inheritanceApplied)
return false;
// get the inheritance maps
$array = array();
foreach($this->tables as $objTable):
$tname = $objTable->getTableName();
$array[$tname][] = $objTable->getInheritanceMap();
endforeach;
// apply inheritance maps
$str = "";
$c = array();
foreach($array as $tname => $maps) {
$a = array();
foreach($maps as $map) {
$b = array();
foreach($map as $field=>$value) {
$b[] = $tname.".$field = $value";
}
if( ! empty($b)) $a[] = implode(" AND ",$b);
}
if( ! empty($a)) $c[] = implode(" || ",$a);
}
$str .= implode(" || ",$c);
$this->addWhere($str);
$this->inheritanceApplied = true;
return true;
}
/**
* @param string $where
* @return boolean
*/
final public function addWhere($where) {
if(empty($where))
return false;
if($this->parts["where"]) {
$this->parts["where"][] = "AND (".$where.")";
} else {
$this->parts["where"][] = "(".$where.")";
}
return true;
}
/**
* getData
* @param $key the component name
* @return array the data row for the specified component
*/
final public function getData($key) {
if(isset($this->data[$key]) && is_array($this->data[$key]))
return $this->data[$key];
return array();
}
/**
* execute
* executes the dql query and populates all collections
*
* @param string $params
* @return Doctrine_Collection the root collection
*/
public function execute($params = array()) {
$this->data = array();
$this->collections = array();
switch(count($this->tables)):
case 0:
throw new DQLException();
break;
case 1:
$query = $this->getQuery();
$keys = array_keys($this->tables);
$name = $this->tables[$keys[0]]->getComponentName();
$stmt = $this->session->execute($query,$params);
while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
foreach($data as $key => $value):
$e = explode("__",$key);
if(count($e) > 1) {
$data[$e[1]] = $value;
} else {
$data[$e[0]] = $value;
}
unset($data[$key]);
endforeach;
$this->data[$name][] = $data;
endwhile;
return $this->getCollection($keys[0]);
break;
default:
$query = $this->getQuery();
$keys = array_keys($this->tables);
$root = $keys[0];
$stmt = $this->session->execute($query,$params);
$previd = array();
$coll = $this->getCollection($root);
$prev[$root] = $coll;
$array = $this->parseData($stmt);
$colls = array();
foreach($array as $data) {
/**
* remove duplicated data rows and map data into objects
*/
foreach($data as $key => $row) {
if(empty($row))
continue;
$ids = $this->tables[$key]->getIdentifier();
if(is_array($ids)) {
$emptyID = false;
foreach($ids as $id) {
if($row[$id] == null) {
$emptyID = true;
break;
}
}
if($emptyID)
continue;
} else {
if($row[$ids] === null)
continue;
}
$name = $this->tables[$key]->getComponentName();
if( ! isset($previd[$name]))
$previd[$name] = array();
if($previd[$name] !== $row) {
// set internal data
$this->tables[$name]->setData($row);
// initialize a new record
$record = $this->tables[$name]->getRecord();
if($name == $root) {
// add record into root collection
$coll->add($record);
} else {
$pointer = $this->joins[$name];
$fk = $this->tables[$pointer]->getForeignKey($this->tables[$pointer]->getAlias($name));
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::ONE_AGGREGATE:
$last = $prev[$pointer]->getLast();
$last->rawSet($this->connectors[$name]->getLocal(), $record->getID());
$last->initSingleReference($record);
$prev[$name] = $record;
break;
default:
// one-to-many relation or many-to-many relation
$last = $prev[$pointer]->getLast();
if( ! $last->hasReference($name)) {
$prev[$name] = $this->getCollection($name);
$last->initReference($prev[$name],$this->connectors[$name]);
}
$last->addReference($record);
endswitch;
}
}
$previd[$name] = $row;
}
}
return $coll;
endswitch;
}
/**
* parseData
* parses the data returned by PDOStatement
*
* @return array
*/
public function parseData(PDOStatement $stmt) {
$array = array();
$keys = array();
foreach(array_keys($this->tables) as $key) {
$k = strtolower($key);
$keys[$k] = $key;
}
while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
/**
* parse the data into two-dimensional array
*/
foreach($data as $key => $value):
$e = explode("__",$key);
if(count($e) > 1) {
$data[$keys[$e[0]]][$e[1]] = $value;
} else {
$data[0][$e[0]] = $value;
}
unset($data[$key]);
endforeach;
$array[] = $data;
endwhile;
$stmt->closeCursor();
return $array;
}
/**
* returns a Doctrine_Table for given name
*
* @param string $name component name
* @return Doctrine_Table
*/
public function getTable($name) {
return $this->tables[$name];
}
/**
* getCollection
*
* @parma string $name component name
* @param integer $index
*/
private function getCollection($name) {
$table = $this->session->getTable($name);
switch($this->fetchModes[$name]):
case Doctrine::FETCH_BATCH:
$coll = new Doctrine_Collection_Batch($table);
break;
case Doctrine::FETCH_LAZY:
$coll = new Doctrine_Collection_Lazy($table);
break;
case Doctrine::FETCH_OFFSET:
$coll = new Doctrine_Collection_Offset($table);
break;
case Doctrine::FETCH_IMMEDIATE:
$coll = new Doctrine_Collection_Immediate($table);
break;
case Doctrine::FETCH_LAZY_OFFSET:
$coll = new Doctrine_Collection_LazyOffset($table);
break;
endswitch;
$coll->populate($this);
return $coll;
}
/**
* query the database with DQL (Doctrine Query Language)
*
* @param string $query DQL query
* @param array $params parameters
*/
public function query($query,$params = array()) {
$this->parseQuery($query);
if($this->aggregate) {
$keys = array_keys($this->tables);
$query = $this->getQuery();
$stmt = $this->tables[$keys[0]]->getSession()->select($query,$this->parts["limit"],$this->offset);
$data = $stmt->fetch(PDO::FETCH_ASSOC);
if(count($data) == 1) {
return current($data);
} else {
return $data;
}
} else {
return $this->execute($params);
}
}
/**
* DQL PARSER
*
* @param string $query DQL query
* @return void
*/
final public function parseQuery($query) {
$this->clear();
$e = self::bracketExplode($query," ","(",")");
$parts = array();
foreach($e as $k=>$part):
switch(strtolower($part)):
case "select":
case "from":
case "where":
case "limit":
case "offset":
$p = $part;
$parts[$part] = array();
break;
case "order":
$p = $part;
$i = $k+1;
if(isset($e[$i]) && strtolower($e[$i]) == "by") {
$parts[$part] = array();
}
break;
case "by":
continue;
default:
$parts[$p][] = $part;
endswitch;
endforeach;
foreach($parts as $k => $part) {
$part = implode(" ",$part);
switch(strtoupper($k)):
case "SELECT":
$this->parseSelect($part);
break;
case "FROM":
$this->parseFrom($part);
break;
case "WHERE":
$this->addWhere($this->parseWhere($part));
break;
case "ORDER":
$this->parseOrderBy($part);
break;
case "LIMIT":
$this->parts["limit"] = trim($part);
break;
case "OFFSET":
$this->offset = trim($part);
break;
endswitch;
}
}
/**
* DQL ORDER BY PARSER
* parses the order by part of the query string
*
* @param string $str
* @return void
*/
private function parseOrderBy($str) {
foreach(explode(",",trim($str)) as $r) {
$r = trim($r);
$e = explode(" ",$r);
$a = explode(".",$e[0]);
if(count($a) > 1) {
$field = array_pop($a);
$reference = implode(".",$a);
$name = end($a);
$this->load($reference, false);
$tname = $this->tables[$name]->getTableName();
$r = $tname.".".$field;
if(isset($e[1]))
$r .= " ".$e[1];
}
$this->parts["orderby"][] = $r;
}
}
/**
* DQL SELECT PARSER
* parses the select part of the query string
*
* @param string $str
* @return void
*/
private function parseSelect($str) {
$this->aggregate = true;
foreach(explode(",",trim($str)) as $reference) {
$e = explode(" AS ",trim($reference));
$f = explode("(",$e[0]);
$a = explode(".",$f[1]);
$field = substr(array_pop($a),0,-1);
$reference = trim(implode(".",$a));
$objTable = $this->load($reference);
if(isset($e[1]))
$s = " AS $e[1]";
$this->parts["columns"][] = $f[0]."(".$objTable->getTableName().".$field)$s";
}
}
/**
* DQL FROM PARSER
* parses the from part of the query string
* @param string $str
* @return void
*/
private function parseFrom($str) {
foreach(explode(",",trim($str)) as $reference) {
$reference = trim($reference);
$table = $this->load($reference);
}
}
/**
* returns Doctrine::FETCH_* constant
*
* @param string $mode
* @return integer
*/
private function parseFetchMode($mode) {
switch(strtolower($mode)):
case "i":
case "immediate":
$fetchmode = Doctrine::FETCH_IMMEDIATE;
break;
case "b":
case "batch":
$fetchmode = Doctrine::FETCH_BATCH;
break;
case "l":
case "lazy":
$fetchmode = Doctrine::FETCH_LAZY;
break;
case "o":
case "offset":
$fetchmode = Doctrine::FETCH_OFFSET;
break;
case "lo":
case "lazyoffset":
$fetchmode = Doctrine::FETCH_LAZYOFFSET;
default:
throw new DQLException("Unknown fetchmode '$mode'. The availible fetchmodes are 'i', 'b' and 'l'.");
endswitch;
return $fetchmode;
}
/**
* DQL WHERE PARSER
* parses the where part of the query string
*
*
* @param string $str
* @return string
*/
private function parseWhere($str) {
$tmp = trim($str);
$str = self::bracketTrim($tmp,"(",")");
$brackets = false;
while($tmp != $str) {
$brackets = true;
$tmp = $str;
$str = self::bracketTrim($str,"(",")");
}
$parts = self::bracketExplode($str," && ","(",")");
if(count($parts) > 1) {
$ret = array();
foreach($parts as $part) {
$ret[] = $this->parseWhere($part);
}
$r = implode(" AND ",$ret);
} else {
$parts = self::bracketExplode($str," || ","(",")");
if(count($parts) > 1) {
$ret = array();
foreach($parts as $part) {
$ret[] = $this->parseWhere($part);
}
$r = implode(" OR ",$ret);
} else {
return $this->loadWhere($parts[0]);
}
}
if($brackets)
return "(".$r.")";
else
return $r;
}
/**
* trims brackets
*
* @param string $str
* @param string $e1 the first bracket, usually '('
* @param string $e2 the second bracket, usually ')'
*/
public static function bracketTrim($str,$e1,$e2) {
if(substr($str,0,1) == $e1 && substr($str,-1) == $e2)
return substr($str,1,-1);
else
return $str;
}
/**
* bracketExplode
* usage:
* $str = (age < 20 AND age > 18) AND email LIKE 'John@example.com'
* now exploding $str with parameters $d = ' AND ', $e1 = '(' and $e2 = ')'
* would return an array:
* array("(age < 20 AND age > 18)", "email LIKE 'John@example.com'")
*
* @param string $str
* @param string $d the delimeter which explodes the string
* @param string $e1 the first bracket, usually '('
* @param string $e2 the second bracket, usually ')'
*
*/
public static function bracketExplode($str,$d,$e1,$e2) {
$str = explode("$d",$str);
$i = 0;
$term = array();
foreach($str as $key=>$val) {
if (empty($term[$i])) {
$term[$i] = trim($val);
$s1 = substr_count($term[$i],"$e1");
$s2 = substr_count($term[$i],"$e2");
if($s1 == $s2) $i++;
} else {
$term[$i] .= "$d".trim($val);
$c1 = substr_count($term[$i],"$e1");
$c2 = substr_count($term[$i],"$e2");
if($c1 == $c2) $i++;
}
}
return $term;
}
/**
* loadWhere
*
* @param string $where
*/
private function loadWhere($where) {
$e = explode(" ",$where);
$r = array_shift($e);
$a = explode(".",$r);
if(count($a) > 1) {
$field = array_pop($a);
$operator = array_shift($e);
$value = implode(" ",$e);
$reference = implode(".",$a);
if(count($a) > 1)
$objTable = $this->tables[$a[0]]->getForeignKey(end($a))->getTable();
else
$objTable = $this->session->getTable(end($a));
$where = $objTable->getTableName().".".$field." ".$operator." ".$value;
if(count($a) > 1 && isset($a[1])) {
$root = $a[0];
$fk = $this->tables[$root]->getForeignKey($a[1]);
if($fk instanceof Doctrine_Association) {
$asf = $fk->getAssociationFactory();
switch($fk->getType()):
case Doctrine_Relation::ONE_AGGREGATE:
case Doctrine_Relation::ONE_COMPOSITE:
break;
case Doctrine_Relation::MANY_AGGREGATE:
case Doctrine_Relation::MANY_COMPOSITE:
// subquery needed
$where = $objTable->getComponentName().".".$field." ".$operator." ".$value;
$b = $fk->getTable()->getComponentName();
$graph = new Doctrine_Query($this->session);
$graph->parseQuery("FROM $b-l WHERE $where");
$where = $this->tables[$root]->getTableName().".".$this->tables[$root]->getIdentifier()." IN (SELECT ".$fk->getLocal()." FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." IN (".$graph->getQuery()."))";
break;
endswitch;
} else
$this->load($reference, false);
} else
$this->load($reference, false);
}
return $where;
}
/**
* @param string $path the path of the loadable component
* @param integer $fetchmode optional fetchmode, if not set the components default fetchmode will be used
* @throws DQLException
*/
final public function load($path, $loadFields = true) {
$e = preg_split("/[.:]/",$path);
$index = 0;
foreach($e as $key => $fullname) {
try {
$e2 = preg_split("/[-(]/",$fullname);
$name = $e2[0];
if($key == 0) {
$table = $this->session->getTable($name);
$tname = $table->getTableName();
$this->parts["from"][$tname] = true;
} else {
$index += strlen($e[($key - 1)]) + 1;
// the mark here is either '.' or ':'
$mark = substr($path,($index - 1),1);
$fk = $table->getForeignKey($name);
$name = $fk->getTable()->getComponentName();
$tname = $table->getTableName();
$tname2 = $fk->getTable()->getTableName();
$this->connectors[$name] = $fk;
if($fk instanceof Doctrine_ForeignKey ||
$fk instanceof Doctrine_LocalKey) {
switch($mark):
case ":":
$this->parts["join"][$tname][$tname2] = "INNER JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign();
break;
case ".":
$this->parts["join"][$tname][$tname2] = "LEFT JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign();
break;
endswitch;
$c = $table->getComponentName();
$this->joins[$name] = $c;
} elseif($fk instanceof Doctrine_Association) {
$asf = $fk->getAssociationFactory();
switch($fk->getType()):
case Doctrine_Relation::ONE_AGGREGATE:
case Doctrine_Relation::ONE_COMPOSITE:
break;
case Doctrine_Relation::MANY_AGGREGATE:
case Doctrine_Relation::MANY_COMPOSITE:
//$this->addWhere("SELECT ".$fk->getLocal()." FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." IN (SELECT ".$fk->getTable()->getComponentName().")");
$this->parts["from"][$tname] = true;
break;
endswitch;
}
$table = $fk->getTable();
}
if( ! isset($this->tables[$name])) {
$this->tables[$name] = $table;
if($loadFields && ! $this->aggregate) {
$fields = array();
if(strpos($fullname, "-") === false) {
$fetchmode = $table->getAttribute(Doctrine::ATTR_FETCHMODE);
if(isset($e2[1]))
$fields = explode(",",substr($e2[1],0,-1));
} else {
if(isset($e2[1])) {
$fetchmode = $this->parseFetchMode($e2[1]);
} else
$fetchmode = $table->getAttribute(Doctrine::ATTR_FETCHMODE);
if(isset($e2[2]))
$fields = explode(",",substr($e2[2],0,-1));
}
$this->loadFields($table, $fetchmode, $fields);
}
}
} catch(Exception $e) {
throw new DQLException($e->getMessage(),$e->getCode());
}
}
}
}
?>
<?php
require_once("Access.php");
/**
* Doctrine_Record
*/
abstract class Doctrine_Record extends Doctrine_Access implements Countable, IteratorAggregate {
/**
* STATE CONSTANTS
*/
/**
* DIRTY STATE
* a Doctrine_Record is in dirty state when its properties are changed
*/
const STATE_DIRTY = 1;
/**
* TDIRTY STATE
* a Doctrine_Record is in transient dirty state when it is created and some of its fields are modified
* but it is NOT yet persisted into database
*/
const STATE_TDIRTY = 2;
/**
* CLEAN STATE
* a Doctrine_Record is in clean state when all of its properties are loaded from the database
* and none of its properties are changed
*/
const STATE_CLEAN = 3;
/**
* PROXY STATE
* a Doctrine_Record is in proxy state when its properties are not fully loaded
*/
const STATE_PROXY = 4;
/**
* NEW TCLEAN
* a Doctrine_Record is in transient clean state when it is created and none of its fields are modified
*/
const STATE_TCLEAN = 5;
/**
* DELETED STATE
* a Doctrine_Record turns into deleted state when it is deleted
*/
const STATE_DELETED = 6;
/**
* FETCHMODE CONSTANTS
*/
/**
* @var object Doctrine_Table $table the factory that created this data access object
*/
protected $table;
/**
* @var integer $id the primary key of this object
*/
protected $id;
/**
* @var array $data the record data
*/
protected $data = array();
/**
* @var array $modified an array containing properties that have been modified
*/
private $modified = array();
/**
* @var integer $state the state of this record
* @see STATE_* constants
*/
private $state;
/**
* @var array $collections the collections this record is in
*/
private $collections = array();
/**
* @var mixed $references an array containing all the references
*/
private $references = array();
/**
* @var mixed $originals an array containing all the original references
*/
private $originals = array();
/**
* @var integer $index this index is used for creating object identifiers
*/
private static $index = 1;
/**
* @var Doctrine_Null $nullObject a Doctrine_Null object used for extremely fast
* SQL null value testing
*/
private static $null;
/**
* @var integer $oid object identifier
*/
private $oid;
/**
* constructor
* @param Doctrine_Table $table a Doctrine_Table object
* @throws Doctrine_Session_Exception if object is created using the new operator and there are no
* open sessions
*/
public function __construct($table = null) {
if(isset($table) && $table instanceof Doctrine_Table) {
$this->table = $table;
$exists = ( ! $this->table->isNewEntry());
} else {
$this->table = Doctrine_Manager::getInstance()->getCurrentSession()->getTable(get_class($this));
$exists = false;
}
// Check if the current session has the records table in its registry
// If not this is record is only used for creating table definition and setting up
// relations.
if($this->table->getSession()->hasTable($this->table->getComponentName())) {
$this->oid = self::$index;
self::$index++;
$keys = $this->table->getPrimaryKeys();
if( ! $exists) {
// listen the onPreCreate event
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onPreCreate($this);
} else {
// listen the onPreLoad event
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onPreLoad($this);
}
// get the data array
$this->data = $this->table->getData();
// get the column count
$count = count($this->data);
// clean data array
$cols = $this->cleanData();
$this->prepareIdentifiers($exists);
if( ! $exists) {
if($cols > 0)
$this->state = Doctrine_Record::STATE_TDIRTY;
else
$this->state = Doctrine_Record::STATE_TCLEAN;
// listen the onCreate event
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onCreate($this);
} else {
$this->state = Doctrine_Record::STATE_CLEAN;
if($count < $this->table->getColumnCount()) {
$this->state = Doctrine_Record::STATE_PROXY;
}
// listen the onLoad event
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onLoad($this);
}
$this->table->getRepository()->add($this);
}
}
/**
* initNullObject
*/
public static function initNullObject(Doctrine_Null $null) {
self::$null = $null;
}
/**
* setUp
* implemented by child classes
*/
public function setUp() { }
/**
* return the object identifier
*
* @return integer
*/
public function getOID() {
return $this->oid;
}
/**
* cleanData
* modifies data array
* example:
*
* $data = array("name"=>"John","lastname"=> null,"id"=>1,"unknown"=>"unknown");
* $names = array("name","lastname","id");
* $data after operation:
* $data = array("name"=>"John","lastname" => array(),"id"=>1);
*/
private function cleanData() {
$cols = 0;
$tmp = $this->data;
$this->data = array();
foreach($this->table->getColumnNames() as $name) {
if( ! isset($tmp[$name])) {
$this->data[$name] = self::$null;
} else {
$cols++;
$this->data[$name] = $tmp[$name];
}
}
return $cols;
}
/**
* prepares identifiers
*
* @return void
*/
private function prepareIdentifiers($exists = true) {
switch($this->table->getIdentifierType()):
case Doctrine_Identifier::AUTO_INCREMENT:
case Doctrine_Identifier::SEQUENCE:
if($exists) {
$name = $this->table->getIdentifier();
if(isset($this->data[$name]))
$this->id = $this->data[$name];
unset($this->data[$name]);
}
break;
case Doctrine_Identifier::COMPOSITE:
$names = $this->table->getIdentifier();
$this->id = array();
foreach($names as $name) {
if($this->data[$name] === self::$null)
$this->id[$name] = null;
else
$this->id[$name] = $this->data[$name];
}
break;
endswitch;
}
/**
* this method is automatically called when this Doctrine_Record is serialized
*
* @return array
*/
public function __sleep() {
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onSleep($this);
$this->table = $this->table->getComponentName();
// unset all vars that won't need to be serialized
unset($this->modified);
unset($this->associations);
unset($this->state);
unset($this->collections);
unset($this->references);
unset($this->originals);
unset($this->oid);
foreach($this->data as $k=>$v) {
if($v instanceof Doctrine_Record)
$this->data[$k] = array();
}
return array_keys(get_object_vars($this));
}
/**
* unseralize
* this method is automatically called everytime a Doctrine_Record object is unserialized
*
* @return void
*/
public function __wakeup() {
$this->modified = array();
$this->state = Doctrine_Record::STATE_CLEAN;
$name = $this->table;
$manager = Doctrine_Manager::getInstance();
$sess = $manager->getCurrentSession();
$this->oid = self::$index;
self::$index++;
$this->table = $sess->getTable($name);
$this->table->getRepository()->add($this);
$this->cleanData();
//unset($this->data['id']);
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onWakeUp($this);
}
/**
* addCollection
* @param Doctrine_Collection $collection
* @param mixed $key
*/
final public function addCollection(Doctrine_Collection $collection,$key = null) {
if($key !== null) {
if(isset($this->collections[$key]))
throw InvalidKeyException();
$this->collections[$key] = $collection;
} else {
$this->collections[] = $collection;
}
}
/**
* getCollection
* @param integer $key
* @return Doctrine_Collection
*/
final public function getCollection($key) {
return $this->collections[$key];
}
/**
* hasCollections
* whether or not this record is part of a collection
*
* @return boolean
*/
final public function hasCollections() {
return (! empty($this->collections));
}
/**
* getState
* returns the current state of the object
*
* @see Doctrine_Record::STATE_* constants
* @return integer
*/
final public function getState() {
return $this->state;
}
/**
* refresh
* refresh internal data from the database
*
* @return boolean
*/
final public function refresh() {
$id = $this->getID();
if( ! is_array($id))
$id = array($id);
if(empty($id))
return false;
$id = array_values($id);
$query = $this->table->getQuery()." WHERE ".implode(" = ? && ",$this->table->getPrimaryKeys())." = ?";
$this->data = $this->table->getSession()->execute($query,$id)->fetch(PDO::FETCH_ASSOC);
$this->modified = array();
$this->cleanData();
$this->prepareIdentifiers();
$this->state = Doctrine_Record::STATE_CLEAN;
return true;
}
/**
* factoryRefresh
* @throws Doctrine_Exception
* @return void
*/
final public function factoryRefresh() {
$data = $this->table->getData();
$id = $this->id;
$this->prepareIdentifiers();
if($this->id != $id)
throw new Doctrine_Record_Exception();
$this->data = $data;
$this->cleanData();
$this->state = Doctrine_Record::STATE_CLEAN;
$this->modified = array();
}
/**
* return the factory that created this data access object
* @return object Doctrine_Table a Doctrine_Table object
*/
final public function getTable() {
return $this->table;
}
/**
* return all the internal data
* @return array an array containing all the properties
*/
final public function getData() {
return $this->data;
}
/**
* get
* returns a value of a property or a related component
*
* @param $name name of the property or related component
* @throws InvalidKeyException
* @return mixed
*/
public function get($name) {
if(isset($this->data[$name])) {
// check if the property is null (= it is the Doctrine_Null object located in self::$null)
if($this->data[$name] === self::$null) {
// no use trying to load the data from database if the Doctrine_Record is not a proxy
if($this->state == Doctrine_Record::STATE_PROXY) {
if( ! empty($this->collections)) {
foreach($this->collections as $collection) {
$collection->load($this);
}
} else {
$this->refresh();
}
$this->state = Doctrine_Record::STATE_CLEAN;
}
if($this->data[$name] === self::$null)
return null;
}
return $this->data[$name];
}
if($name == $this->table->getIdentifier())
return $this->id;
if( ! isset($this->references[$name]))
$this->loadReference($name);
return $this->references[$name];
}
/**
* rawSet
* doctrine uses this function internally, not recommended for developers
*
* @param mixed $name name of the property or reference
* @param mixed $value value of the property or reference
*/
final public function rawSet($name,$value) {
if($value instanceof Doctrine_Record)
$id = $value->getID();
if( ! empty($id))
$value = $id;
if(isset($this->data[$name])) {
if($this->data[$name] === self::$null) {
if($this->data[$name] !== $value) {
switch($this->state):
case Doctrine_Record::STATE_CLEAN:
$this->state = Doctrine_Record::STATE_DIRTY;
break;
case Doctrine_Record::STATE_TCLEAN:
$this->state = Doctrine_Record::STATE_TDIRTY;
endswitch;
}
}
if($this->state == Doctrine_Record::STATE_TCLEAN)
$this->state = Doctrine_Record::STATE_TDIRTY;
$this->data[$name] = $value;
$this->modified[] = $name;
}
}
/**
* set
* method for altering properties and Doctrine_Record references
*
* @param mixed $name name of the property or reference
* @param mixed $value value of the property or reference
* @throws InvalidKeyException
* @throws InvalidTypeException
* @return void
*/
public function set($name,$value) {
if(isset($this->data[$name])) {
if($value instanceof Doctrine_Record) {
$id = $value->getID();
if( ! empty($id))
$value = $value->getID();
}
$old = $this->get($name);
if($old !== $value) {
$this->data[$name] = $value;
$this->modified[] = $name;
switch($this->state):
case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_PROXY:
$this->state = Doctrine_Record::STATE_DIRTY;
break;
case Doctrine_Record::STATE_TCLEAN:
$this->state = Doctrine_Record::STATE_TDIRTY;
break;
endswitch;
}
} else {
// if not found, throws InvalidKeyException
$fk = $this->table->getForeignKey($name);
// one-to-many or one-to-one relation
if($fk instanceof Doctrine_ForeignKey ||
$fk instanceof Doctrine_LocalKey) {
switch($fk->getType()):
case Doctrine_Relation::MANY_COMPOSITE:
case Doctrine_Relation::MANY_AGGREGATE:
// one-to-many relation found
if( ! ($value instanceof Doctrine_Collection))
throw new Doctrine_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Collection when setting one-to-many references.");
$value->setReference($this,$fk);
break;
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::ONE_AGGREGATE:
// one-to-one relation found
if( ! ($value instanceof Doctrine_Record))
throw new Doctrine_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Record when setting one-to-one references.");
if($fk->getLocal() == $this->table->getIdentifier()) {
$this->references[$name]->set($fk->getForeign(),$this);
} else {
$this->set($fk->getLocal(),$value);
}
break;
endswitch;
} elseif($fk instanceof Doctrine_Association) {
// join table relation found
if( ! ($value instanceof Doctrine_Collection))
throw new Doctrine_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Collection when setting one-to-many references.");
}
$this->references[$name] = $value;
}
}
/**
* __isset
*
* @param string $name
* @return boolean
*/
public function __isset($name) {
if(isset($this->data[$name]))
return true;
if(isset($this->references[$name]))
return true;
return false;
}
/**
* @param string $name
* @return void
*/
public function __unset($name) {
if(isset($this->data[$name]))
$this->data[$name] = array();
// todo: what to do with references ?
}
/**
* applies the changes made to this object into database
* this method is smart enough to know if any changes are made
* and whether to use INSERT or UPDATE statement
*
* this method also saves the related composites
*
* @return void
*/
final public function save() {
$this->table->getSession()->beginTransaction();
// listen the onPreSave event
$this->table->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($this);
$saveLater = $this->table->getSession()->saveRelated($this);
$this->table->getSession()->save($this);
foreach($saveLater as $fk) {
$table = $fk->getTable();
$alias = $this->table->getAlias($table->getComponentName());
if(isset($this->references[$alias])) {
$obj = $this->references[$alias];
$obj->save();
}
}
// save the MANY-TO-MANY associations
$this->saveAssociations();
$this->table->getSession()->commit();
}
/**
* returns an array of modified fields and associated values
* @return array
*/
final public function getModified() {
$a = array();
foreach($this->modified as $k=>$v) {
$a[$v] = $this->data[$v];
}
return $a;
}
/**
* returns an array of modified fields and values with data preparation
* adds column aggregation inheritance and converts Records into primary key values
*
* @return array
*/
final public function getPrepared() {
$a = array();
foreach($this->table->getInheritanceMap() as $k => $v) {
$this->set($k,$v);
}
foreach($this->modified as $k => $v) {
if($this->data[$v] instanceof Doctrine_Record) {
$this->data[$v] = $this->data[$v]->getID();
}
$a[$v] = $this->data[$v];
}
return $a;
}
/**
* this class implements countable interface
* @return integer the number of columns
*/
public function count() {
return count($this->data);
}
/**
* getIterator
* @return ArrayIterator an ArrayIterator that iterates through the data
*/
public function getIterator() {
return new ArrayIterator($this->data);
}
/**
* saveAssociations
* save the associations of many-to-many relations
* this method also deletes associations that do not exist anymore
* @return void
*/
final public function saveAssociations() {
foreach($this->table->getForeignKeys() as $fk):
$table = $fk->getTable();
$name = $table->getComponentName();
$alias = $this->table->getAlias($name);
if($fk instanceof Doctrine_Association) {
switch($fk->getType()):
case Doctrine_Relation::MANY_COMPOSITE:
break;
case Doctrine_Relation::MANY_AGGREGATE:
$asf = $fk->getAssociationFactory();
if(isset($this->references[$alias])) {
$new = $this->references[$alias];
if( ! isset($this->originals[$alias])) {
$this->loadReference($alias);
}
$r = $this->getRelationOperations($alias,$new);
foreach($r["delete"] as $record) {
$query = "DELETE FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." = ?"
." && ".$fk->getLocal()." = ?";
$this->table->getSession()->execute($query, array($record->getID(),$this->getID()));
}
foreach($r["add"] as $record) {
$reldao = $asf->create();
$reldao->set($fk->getForeign(),$record);
$reldao->set($fk->getLocal(),$this);
$reldao->save();
}
$this->originals[$alias] = clone $this->references[$alias];
}
break;
endswitch;
} elseif($fk instanceof Doctrine_ForeignKey ||
$fk instanceof Doctrine_LocalKey) {
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
if(isset($this->originals[$alias]) && $this->originals[$alias]->getID() != $this->references[$alias]->getID())
$this->originals[$alias]->delete();
break;
case Doctrine_Relation::MANY_COMPOSITE:
if(isset($this->references[$alias])) {
$new = $this->references[$alias];
if( ! isset($this->originals[$alias]))
$this->loadReference($alias);
$r = $this->getRelationOperations($alias,$new);
foreach($r["delete"] as $record) {
$record->delete();
}
$this->originals[$alias] = clone $this->references[$alias];
}
break;
endswitch;
}
endforeach;
}
/**
* get the records that need to be added
* and/or deleted in order to change the old collection
* to the new one
*
* The algorithm here is very simple and definitely not
* the fastest one, since we have to iterate through the collections twice.
* the complexity of this algorithm is O(n^2)
*
* First we iterate through the new collection and get the
* records that do not exist in the old collection (Doctrine_Records that need to be added).
*
* Then we iterate through the old collection and get the records
* that do not exists in the new collection (Doctrine_Records that need to be deleted).
*/
final public function getRelationOperations($name, Doctrine_Collection $new) {
$r["add"] = array();
$r["delete"] = array();
foreach($new as $k=>$record) {
$found = false;
if($record->getID() !== null) {
foreach($this->originals[$name] as $k2 => $record2) {
if($record2->getID() == $record->getID()) {
$found = true;
break;
}
}
}
if( ! $found) {
$this->originals[$name][] = $record;
$r["add"][] = $record;
}
}
foreach($this->originals[$name] as $k => $record) {
if($record->getID() === null)
continue;
$found = false;
foreach($new as $k2=>$record2) {
if($record2->getID() == $record->getID()) {
$found = true;
break;
}
}
if( ! $found) {
$r["delete"][] = $record;
unset($this->originals[$name][$k]);
}
}
return $r;
}
/**
* getOriginals
*/
final public function getOriginals($name) {
if( ! isset($this->originals[$name]))
throw new InvalidKeyException();
return $this->originals[$name];
}
/**
* deletes this data access object and all the related composites
* this operation is isolated by a transaction
*
* this event can be listened by the onPreDelete and onDelete listeners
*
* @return boolean true on success, false on failure
*/
public function delete() {
$this->table->getSession()->delete($this);
}
/**
* returns a copy of this object
* @return DAO
*/
public function copy() {
return $this->table->create($this->data);
}
/**
* @param integer $id
* @return void
*/
final public function setID($id = false) {
if($id === false) {
$this->id = false;
$this->cleanData();
$this->state = Doctrine_Record::STATE_TCLEAN;
$this->modified = array();
} elseif($id === true) {
$this->prepareIdentifiers(false);
$this->state = Doctrine_Record::STATE_CLEAN;
$this->modified = array();
} else {
$this->id = $id;
$this->state = Doctrine_Record::STATE_CLEAN;
$this->modified = array();
}
}
/**
* return the primary key(s) this object is pointing at
* @return mixed id
*/
final public function getID() {
return $this->id;
}
/**
* getLast
* this method is used internally be Doctrine_Query
* it is needed to provide compatibility between
* records and collections
*
* @return Doctrine_Record
*/
public function getLast() {
return $this;
}
/**
* hasRefence
* @param string $name
* @return boolean
*/
public function hasReference($name) {
return isset($this->references[$name]);
}
/**
* initalizes a one-to-one relation
*
* @param Doctrine_Record $record
* @param Doctrine_Relation $connector
* @return void
*/
public function initSingleReference(Doctrine_Record $record) {
$name = $this->table->getAlias($record->getTable()->getComponentName());
$this->references[$name] = $record;
}
/**
* initalizes a one-to-many / many-to-many relation
*
* @param Doctrine_Collection $coll
* @param Doctrine_Relation $connector
* @return void
*/
public function initReference(Doctrine_Collection $coll, Doctrine_Relation $connector) {
$name = $this->table->getAlias($coll->getTable()->getComponentName());
$coll->setReference($this, $connector);
$this->references[$name] = $coll;
$this->originals[$name] = clone $coll;
}
/**
* addReference
* @param Doctrine_Record $record
* @param mixed $key
* @return void
*/
public function addReference(Doctrine_Record $record, $key = null) {
$name = $this->table->getAlias($record->getTable()->getComponentName());
$this->references[$name]->add($record, $key);
$this->originals[$name]->add($record, $key);
}
/**
* getReferences
* @return array all references
*/
public function getReferences() {
return $this->references;
}
/**
* @throws InvalidKeyException
* @param name
* @return void
*/
final public function loadReference($name) {
$fk = $this->table->getForeignKey($name);
$table = $fk->getTable();
$local = $fk->getLocal();
$foreign = $fk->getForeign();
$graph = $table->getQueryObject();
$type = $fk->getType();
switch($this->getState()):
case Doctrine_Record::STATE_TDIRTY:
case Doctrine_Record::STATE_TCLEAN:
if($type == Doctrine_Relation::ONE_COMPOSITE ||
$type == Doctrine_Relation::ONE_AGGREGATE) {
// ONE-TO-ONE
$this->references[$name] = $table->create();
if($fk instanceof Doctrine_ForeignKey) {
$this->references[$name]->set($fk->getForeign(),$this);
} else {
$this->set($fk->getLocal(),$this->references[$name]);
}
} else {
$this->references[$name] = new Doctrine_Collection($table);
if($fk instanceof Doctrine_ForeignKey) {
// ONE-TO-MANY
$this->references[$name]->setReference($this,$fk);
}
$this->originals[$name] = new Doctrine_Collection($table);
}
break;
case Doctrine_Record::STATE_DIRTY:
case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_PROXY:
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::ONE_AGGREGATE:
// ONE-TO-ONE
$id = $this->get($local);
if($fk instanceof Doctrine_LocalKey) {
if(empty($id)) {
$this->references[$name] = $table->create();
$this->set($fk->getLocal(),$this->references[$name]);
} else {
try {
$this->references[$name] = $table->find($id);
} catch(Doctrine_Find_Exception $e) {
}
}
} elseif ($fk instanceof Doctrine_ForeignKey) {
if(empty($id)) {
$this->references[$name] = $table->create();
$this->references[$name]->set($fk->getForeign(), $this);
} else {
$dql = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$fk->getForeign()." = ?";
$coll = $graph->query($dql, array($id));
$this->references[$name] = $coll[0];
$this->references[$name]->set($fk->getForeign(), $this);
}
}
break;
default:
// ONE-TO-MANY
if($fk instanceof Doctrine_ForeignKey) {
$id = $this->get($local);
$query = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$fk->getForeign()." = ?";
$coll = $graph->query($query,array($id));
$this->references[$name] = $coll;
$this->references[$name]->setReference($this, $fk);
$this->originals[$name] = clone $coll;
} elseif($fk instanceof Doctrine_Association) {
$asf = $fk->getAssociationFactory();
$query = "SELECT ".$foreign." FROM ".$asf->getTableName()." WHERE ".$local." = ?";
$graph = new Doctrine_Query($table->getSession());
$query = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$table->getIdentifier()." IN ($query)";
$coll = $graph->query($query, array($this->getID()));
$this->references[$name] = $coll;
$this->originals[$name] = clone $coll;
}
endswitch;
break;
endswitch;
}
/**
* binds One-to-One composite relation
*
* @param string $objTableName
* @param string $fkField
* @return void
*/
final public function ownsOne($componentName,$foreignKey, $localKey = null) {
$this->table->bind($componentName,$foreignKey,Doctrine_Relation::ONE_COMPOSITE, $localKey);
}
/**
* binds One-to-Many composite relation
*
* @param string $objTableName
* @param string $fkField
* @return void
*/
final public function ownsMany($componentName,$foreignKey, $localKey = null) {
$this->table->bind($componentName,$foreignKey,Doctrine_Relation::MANY_COMPOSITE, $localKey);
}
/**
* binds One-to-One aggregate relation
*
* @param string $objTableName
* @param string $fkField
* @return void
*/
final public function hasOne($componentName,$foreignKey, $localKey = null) {
$this->table->bind($componentName,$foreignKey,Doctrine_Relation::ONE_AGGREGATE, $localKey);
}
/**
* binds One-to-Many aggregate relation
*
* @param string $objTableName
* @param string $fkField
* @return void
*/
final public function hasMany($componentName,$foreignKey, $localKey = null) {
$this->table->bind($componentName,$foreignKey,Doctrine_Relation::MANY_AGGREGATE, $localKey);
}
/**
* setInheritanceMap
* @param array $inheritanceMap
* @return void
*/
final public function setInheritanceMap(array $inheritanceMap) {
$this->table->setInheritanceMap($inheritanceMap);
}
/**
* setPrimaryKey
* @param mixed $key
*/
final public function setPrimaryKey($key) {
$this->table->setPrimaryKey($key);
}
/**
* setTableName
* @param string $name table name
* @return void
*/
final public function setTableName($name) {
$this->table->setTableName($name);
}
/**
* setAttribute
* @param integer $attribute
* @param mixed $value
* @see Doctrine::ATTR_* constants
* @return void
*/
final public function setAttribute($attribute, $value) {
$this->table->setAttribute($attribute,$value);
}
/**
* hasColumn
* sets a column definition
*
* @param string $name
* @param string $type
* @param integer $length
* @param mixed $options
* @return void
*/
final public function hasColumn($name, $type, $length = 20, $options = "") {
$this->table->setColumn($name, $type, $length, $options);
}
/**
* returns a string representation of this object
*/
public function __toString() {
return Doctrine_Lib::getRecordAsString($this);
}
}
?>
<?php
/**
* Doctrine_Relation
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class Doctrine_Relation {
/**
* RELATION CONSTANTS
*/
/**
* constant for ONE_TO_ONE and MANY_TO_ONE aggregate relationships
*/
const ONE_AGGREGATE = 0;
/**
* constant for ONE_TO_ONE and MANY_TO_ONE composite relationships
*/
const ONE_COMPOSITE = 1;
/**
* constant for MANY_TO_MANY and ONE_TO_MANY aggregate relationships
*/
const MANY_AGGREGATE = 2;
/**
* constant for MANY_TO_MANY and ONE_TO_MANY composite relationships
*/
const MANY_COMPOSITE = 3;
/**
* @var Doctrine_Table $table foreign factory
*/
private $table;
/**
* @var string $local local field
*/
private $local;
/**
* @var string $foreign foreign field
*/
private $foreign;
/**
* @var integer $type bind type
*/
private $type;
/**
* @param Doctrine_Table $table
* @param string $local
* @param string $foreign
* @param integer $type
*/
public function __construct(Doctrine_Table $table,$local,$foreign,$type) {
$this->table = $table;
$this->local = $local;
$this->foreign = $foreign;
$this->type = $type;
}
/**
* @return integer bind type 1 or 0
*/
public function getType() {
return $this->type;
}
/**
* @return object Doctrine_Table foreign factory object
*/
public function getTable() {
return $this->table;
}
/**
* @return string the name of the local column
*/
public function getLocal() {
return $this->local;
}
/**
* @return string the name of the foreign column where
* the local column is pointing at
*/
public function getForeign() {
return $this->foreign;
}
/**
* __toString
*/
public function __toString() {
$r[] = "<pre>";
$r[] = "Class : ".get_class($this);
$r[] = "Component : ".$this->table->getComponentName();
$r[] = "Table : ".$this->table->getTableName();
$r[] = "Local key : ".$this->local;
$r[] = "Foreign key : ".$this->foreign;
$r[] = "Type : ".$this->type;
$r[] = "</pre>";
return implode("\n", $r);
}
}
?>
<?php
/**
* Doctrine_Repository
* each record is added into Doctrine_Repository at the same time they are created,
* loaded from the database or retrieved from the cache
*
* @author Konsta Vesterinen
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
* @version 1.0 alpha
*
*/
class Doctrine_Repository implements Countable, IteratorAggregate {
/**
* @var object Doctrine_Table $table
*/
private $table;
/**
* @var array $registry
* an array of all records
* keys representing record object identifiers
*/
private $registry = array();
/**
* constructor
*/
public function __construct(Doctrine_Table $table) {
$this->table = $table;
}
/**
* @return object Doctrine_Table
*/
public function getTable() {
return $this->table;
}
/**
* add
* @param Doctrine_Record $record record to be added into registry
*/
public function add(Doctrine_Record $record) {
$oid = $record->getOID();
if(isset($this->registry[$oid]))
return false;
$this->registry[$oid] = $record;
return true;
}
/**
* get
* @param integer $oid
* @throws InvalidKeyException
*/
public function get($oid) {
if( ! isset($this->registry[$oid]))
throw new InvalidKeyException();
return $this->registry[$oid];
}
/**
* count
* Doctrine_Registry implements interface Countable
* @return integer the number of records this registry has
*/
public function count() {
return count($this->registry);
}
/**
* @param integer $oid object identifier
* @return boolean whether ot not the operation was successful
*/
public function evict($oid) {
if( ! isset($this->registry[$oid]))
return false;
unset($this->registry[$oid]);
return true;
}
/**
* @return integer number of records evicted
*/
public function evictAll() {
$evicted = 0;
foreach($this->registry as $oid=>$record) {
if($this->evict($oid))
$evicted++;
}
return $evicted;
}
/**
* getIterator
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->registry);
}
/**
* contains
* @param integer $oid object identifier
*/
public function contains($oid) {
return isset($this->registry[$oid]);
}
/**
* loadAll
* @return void
*/
public function loadAll() {
$this->table->findAll();
}
}
?>
<?php
require_once("Configurable.php");
require_once("Record.php");
/**
* Doctrine_Session
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
* @version 1.0 alpha
*/
abstract class Doctrine_Session extends Doctrine_Configurable implements Countable, IteratorAggregate {
/**
* Doctrine_Session is in open state when it is opened and there are no active transactions
*/
const STATE_OPEN = 0;
/**
* Doctrine_Session is in closed state when it is closed
*/
const STATE_CLOSED = 1;
/**
* Doctrine_Session is in active state when it has one active transaction
*/
const STATE_ACTIVE = 2;
/**
* Doctrine_Session is in busy state when it has multiple active transactions
*/
const STATE_BUSY = 3;
/**
* @var $dbh the database handle
*/
private $dbh;
/**
* @see Doctrine_Session::STATE_* constants
* @var boolean $state the current state of the session
*/
private $state = 0;
/**
* @var integer $transaction_level the nesting level of transactions, used by transaction methods
*/
private $transaction_level = 0;
/**
* @var PDO $cacheHandler cache handler
*/
private $cacheHandler;
/**
* @var array $tables an array containing all the initialized Doctrine_Table objects
* keys representing Doctrine_Table component names and values as Doctrine_Table objects
*/
protected $tables = array();
/**
* @var Doctrine_Validator $validator transaction validator
*/
protected $validator;
/**
* @var array $update two dimensional pending update list, the records in
* this list will be updated when transaction is committed
*/
protected $update = array();
/**
* @var array $insert two dimensional pending insert list, the records in
* this list will be inserted when transaction is committed
*/
protected $insert = array();
/**
* @var array $delete two dimensional pending delete list, the records in
* this list will be deleted when transaction is committed
*/
protected $delete = array();
/**
* the constructor
* @param PDO $pdo -- database handle
*/
public function __construct(Doctrine_Manager $manager,PDO $pdo) {
$this->dbh = $pdo;
$this->setParent($manager);
$this->state = Doctrine_Session::STATE_OPEN;
$this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
switch($this->getAttribute(Doctrine::ATTR_CACHE)):
case Doctrine::CACHE_SQLITE:
$dir = $this->getAttribute(Doctrine::ATTR_CACHE_DIR).DIRECTORY_SEPARATOR;
$dsn = "sqlite:".$dir."data.cache";
$this->cacheHandler = Doctrine_DB::getConn($dsn);
$this->cacheHandler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->cacheHandler->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
break;
endswitch;
$this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this);
}
public function getCacheHandler() {
return $this->cacheHandler;
}
/**
* returns the state of this session
*
* @see Doctrine_Session::STATE_* constants
* @return integer the session state
*/
public function getState() {
return $this->state;
}
/**
* returns the manager that created this session
*
* @return Doctrine_Manager
*/
public function getManager() {
return $this->getParent();
}
/**
* returns the database handler of which this session uses
*
* @return object PDO the database handler
*/
public function getDBH() {
return $this->dbh;
}
/**
* query
* queries the database with Doctrine Query Language
*
* @param string $query DQL query
* @param array $params query parameters
*/
final public function query($query,array $params = array()) {
$parser = new Doctrine_Query($this);
return $parser->query($query, $params);
}
/**
* queries the database with limit and offset
* added to the query and returns a PDOStatement object
*
* @param string $query
* @param integer $limit
* @param integer $offset
* @return PDOStatement
*/
public function select($query,$limit = 0,$offset = 0) {
if($limit > 0 || $offset > 0)
$query = $this->modifyLimitQuery($query,$limit,$offset);
return $this->dbh->query($query);
}
/**
* @param string $query sql query
* @param array $params query parameters
*
* @return PDOStatement
*/
public function execute($query, array $params = array()) {
if( ! empty($params)) {
$stmt = $this->dbh->prepare($query);
$stmt->execute($params);
return $stmt;
} else {
return $this->dbh->query($query);
}
}
/**
* whether or not this session has table $name initialized
*
* @param $mixed $name
* @return boolean
*/
public function hasTable($name) {
return isset($this->tables[$name]);
}
/**
* returns a table object for given component name
*
* @param string $name component name
* @return object Doctrine_Table
*/
public function getTable($name) {
if(isset($this->tables[$name]))
return $this->tables[$name];
$class = $name."Table";
if(class_exists($class) && in_array("Doctrine_Table", class_parents($class))) {
return new $class($name);
} else {
return new Doctrine_Table($name);
}
}
/**
* returns an array of all initialized tables
*
* @return array
*/
public function getTables() {
return $this->tables;
}
/**
* returns an iterator that iterators through all
* initialized table objects
*
* @return ArrayIterator
*/
public function getIterator() {
return new ArrayIterator($this->tables);
}
/**
* returns the count of initialized table objects
*
* @return integer
*/
public function count() {
return count($this->tables);
}
/**
* @param $objTable a Doctrine_Table object to be added into registry
* @return boolean
*/
public function addTable(Doctrine_Table $objTable) {
$name = $objTable->getComponentName();
if(isset($this->tables[$name]))
return false;
$this->tables[$name] = $objTable;
return true;
}
/**
* creates a record
*
* create creates a record
* @param string $name component name
* @return Doctrine_Record Doctrine_Record object
*/
public function create($name) {
return $this->getTable($name)->create();
}
/**
* buildFlushTree
* builds a flush tree that is used in transactions
*
* @return array
*/
public function buildFlushTree(array $tables) {
$tree = array();
foreach($tables as $k => $table) {
$k = $k.$table;
if( ! ($table instanceof Doctrine_Table))
$table = $this->getTable($table);
$nm = $table->getComponentName();
$index = array_search($nm,$tree);
if($index === false) {
$tree[] = $nm;
$index = max(array_keys($tree));
//print "$k -- adding <b>$nm</b>...<br \>";
}
$rels = $table->getForeignKeys();
// group relations
foreach($rels as $key => $rel) {
if($rel instanceof Doctrine_ForeignKey) {
unset($rels[$key]);
array_unshift($rels, $rel);
}
}
foreach($rels as $rel) {
$name = $rel->getTable()->getComponentName();
$index2 = array_search($name,$tree);
$type = $rel->getType();
// skip self-referenced relations
if($name === $nm)
continue;
if($rel instanceof Doctrine_ForeignKey) {
if($index2 !== false) {
if($index2 >= $index)
continue;
unset($tree[$index]);
array_splice($tree,$index2,0,$nm);
$index = $index2;
//print "$k -- pushing $nm into $index2...<br \>";
} else {
$tree[] = $name;
//print "$k -- adding $nm :$name...<br>";
}
} elseif($rel instanceof Doctrine_LocalKey) {
if($index2 !== false) {
if($index2 <= $index)
continue;
unset($tree[$index2]);
array_splice($tree,$index,0,$name);
//print "$k -- pushing $name into <b>$index</b>...<br \>";
} else {
//array_splice($tree, $index, 0, $name);
array_unshift($tree,$name);
$index++;
//print "$k -- pushing <b>$name</b> into 0...<br \>";
}
} elseif($rel instanceof Doctrine_Association) {
$t = $rel->getAssociationFactory();
$n = $t->getComponentName();
if($index2 !== false)
unset($tree[$index2]);
array_splice($tree,$index, 0,$name);
$index++;
$index3 = array_search($n,$tree);
if($index3 !== false) {
if($index3 >= $index)
continue;
unset($tree[$index]);
array_splice($tree,$index3,0,$n);
$index = $index2;
//print "$k -- pushing $nm into $index3...<br \>";
} else {
$tree[] = $n;
//print "$k -- adding $nm :$name...<br>";
}
}
//print_r($tree);
}
//print_r($tree);
}
return array_values($tree);
}
/**
* flush
* saves all the records from all tables
* this operation is isolated using a transaction
*
* @return void
*/
public function flush() {
$this->beginTransaction();
$this->saveAll();
$this->commit();
}
/**
* saveAll
* saves all the records from all tables
*
* @return void
*/
private function saveAll() {
$tree = $this->buildFlushTree($this->tables);
foreach($tree as $name) {
$table = $this->tables[$name];
foreach($table->getRepository() as $record) {
$this->save($record);
}
}
foreach($tree as $name) {
$table = $this->tables[$name];
foreach($table->getRepository() as $record) {
$record->saveAssociations();
}
}
}
/**
* clear
* clears all repositories
*
* @return void
*/
public function clear() {
foreach($this->tables as $k => $table) {
$table->getRepository()->evictAll();
$table->clear();
}
$this->tables = array();
}
/**
* close
* closes the session
*
* @return void
*/
public function close() {
$this->getAttribute(Doctrine::ATTR_LISTENER)->onPreClose($this);
$this->clear();
$this->state = Doctrine_Session::STATE_CLOSED;
$this->getAttribute(Doctrine::ATTR_LISTENER)->onClose($this);
}
/**
* get the current transaction nesting level
*
* @return integer
*/
public function getTransactionLevel() {
return $this->transaction_level;
}
/**
* beginTransaction
* starts a new transaction
* @return void
*/
public function beginTransaction() {
if($this->transaction_level == 0) {
if($this->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_PESSIMISTIC) {
$this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this);
$this->dbh->beginTransaction();
$this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this);
}
$this->state = Doctrine_Session::STATE_ACTIVE;
} else {
$this->state = Doctrine_Session::STATE_BUSY;
}
$this->transaction_level++;
}
/**
* commits the current transaction
* if lockmode is optimistic this method starts a transaction
* and commits it instantly
* @return void
*/
public function commit() {
$this->transaction_level--;
if($this->transaction_level == 0) {
if($this->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_OPTIMISTIC) {
$this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this);
$this->dbh->beginTransaction();
$this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this);
}
if($this->getAttribute(Doctrine::ATTR_VLD))
$this->validator = new Doctrine_Validator();
try {
$this->bulkInsert();
$this->bulkUpdate();
$this->bulkDelete();
if($this->getAttribute(Doctrine::ATTR_VLD)) {
if($this->validator->hasErrors()) {
$this->rollback();
throw new Doctrine_Validator_Exception($this->validator);
}
}
} catch(PDOException $e) {
$this->rollback();
throw new Doctrine_Exception($e->getMessage());
}
$this->dbh->commit();
$this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this);
$this->delete = array();
$this->state = Doctrine_Session::STATE_OPEN;
$this->validator = null;
} elseif($this->transaction_level == 1)
$this->state = Doctrine_Session::STATE_ACTIVE;
}
/**
* rollback
* rolls back all transactions
* @return void
*/
public function rollback() {
$this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionRollback($this);
$this->transaction_level = 0;
$this->dbh->rollback();
$this->state = Doctrine_Session::STATE_OPEN;
$this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this);
}
/**
* bulkInsert
* inserts all the objects in the pending insert list into database
* @return void
*/
public function bulkInsert() {
if(empty($this->insert))
return false;
foreach($this->insert as $name => $inserts) {
if( ! isset($inserts[0]))
continue;
$record = $inserts[0];
$table = $record->getTable();
$seq = $table->getSequenceName();
$increment = false;
$keys = $table->getPrimaryKeys();
$id = null;
if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
$increment = true;
}
foreach($inserts as $k => $record) {
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
// listen the onPreInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record);
$this->insert($record);
if($increment) {
if($k == 0) {
// record uses auto_increment column
$id = $table->getMaxIdentifier();
}
$record->setID($id);
$id++;
} else
$record->setID(true);
// listen the onInsert event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
}
}
$this->insert = array();
return true;
}
/**
* returns maximum identifier values
*
* @param array $names an array of component names
* @return array
*/
public function getMaximumValues(array $names) {
$values = array();
foreach($names as $name) {
$table = $this->tables[$name];
$keys = $table->getPrimaryKeys();
$tablename = $table->getTableName();
if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
// record uses auto_increment column
$sql = "SELECT MAX(".$table->getIdentifier().") FROM ".$tablename;
$stmt = $this->dbh->query($sql);
$data = $stmt->fetch(PDO::FETCH_NUM);
$values[$tablename] = $data[0];
$stmt->closeCursor();
}
}
return $values;
}
/**
* bulkUpdate
* updates all objects in the pending update list
*
* @return void
*/
public function bulkUpdate() {
foreach($this->update as $name => $updates) {
$ids = array();
foreach($updates as $k => $record) {
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
// listen the onPreUpdate event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record);
$this->update($record);
// listen the onUpdate event
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record);
$record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
$ids[] = $record->getID();
}
if(isset($record))
$record->getTable()->getCache()->deleteMultiple($ids);
}
$this->update = array();
}
/**
* bulkDelete
* deletes all records from the pending delete list
*
* @return void
*/
public function bulkDelete() {
foreach($this->delete as $name => $deletes) {
$record = false;
$ids = array();
foreach($deletes as $k => $record) {
$ids[] = $record->getID();
$record->setID(false);
}
if($record instanceof Doctrine_Record) {
$table = $record->getTable();
$params = substr(str_repeat("?, ",count($ids)),0,-2);
$query = "DELETE FROM ".$record->getTable()->getTableName()." WHERE ".$table->getIdentifier()." IN(".$params.")";
$this->execute($query,$ids);
$record->getTable()->getCache()->deleteMultiple($ids);
}
}
$this->delete = array();
}
/**
* saves a collection
*
* @param Doctrine_Collection $coll
* @return void
*/
public function saveCollection(Doctrine_Collection $coll) {
$this->beginTransaction();
foreach($coll as $key=>$record):
$record->save();
endforeach;
$this->commit();
}
/**
* deletes all records from collection
*
* @param Doctrine_Collection $coll
* @return void
*/
public function deleteCollection(Doctrine_Collection $coll) {
$this->beginTransaction();
foreach($coll as $k=>$record) {
$record->delete();
}
$this->commit();
}
/**
* saves the given record
*
* @param Doctrine_Record $record
* @return void
*/
public function save(Doctrine_Record $record) {
switch($record->getState()):
case Doctrine_Record::STATE_TDIRTY:
$this->addInsert($record);
break;
case Doctrine_Record::STATE_DIRTY:
case Doctrine_Record::STATE_PROXY:
$this->addUpdate($record);
break;
case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_TCLEAN:
// do nothing
break;
endswitch;
}
/**
* saves all related records to $record
*
* @param Doctrine_Record $record
*/
final public function saveRelated(Doctrine_Record $record) {
$saveLater = array();
foreach($record->getReferences() as $k=>$v) {
$fk = $record->getTable()->getForeignKey($k);
if($fk instanceof Doctrine_ForeignKey ||
$fk instanceof Doctrine_LocalKey) {
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::MANY_COMPOSITE:
$local = $fk->getLocal();
$foreign = $fk->getForeign();
if($record->getTable()->hasPrimaryKey($fk->getLocal())) {
switch($record->getState()):
case Doctrine_Record::STATE_TDIRTY:
case Doctrine_Record::STATE_TCLEAN:
$saveLater[$k] = $fk;
break;
case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_DIRTY:
$v->save();
break;
endswitch;
} else {
// ONE-TO-ONE relationship
$obj = $record->get($fk->getTable()->getComponentName());
if($obj->getState() != Doctrine_Record::STATE_TCLEAN)
$obj->save();
}
break;
endswitch;
} elseif($fk instanceof Doctrine_Association) {
$v->save();
}
}
return $saveLater;
}
/**
* updates the given record
*
* @param Doctrine_Record $record
* @return boolean
*/
private function update(Doctrine_Record $record) {
$array = $record->getModified();
if(empty($array))
return false;
$set = array();
foreach($array as $name => $value):
$set[] = $name." = ?";
if($value instanceof Doctrine_Record) {
switch($value->getState()):
case Doctrine_Record::STATE_TCLEAN:
case Doctrine_Record::STATE_TDIRTY:
$record->save();
default:
$array[$name] = $value->getID();
$record->set($name, $value->getID());
endswitch;
}
endforeach;
if(isset($this->validator)) {
if( ! $this->validator->validateRecord($record)) {
return false;
}
}
$params = array_values($array);
$id = $record->getID();
if( ! is_array($id))
$id = array($id);
$id = array_values($id);
$params = array_merge($params, $id);
$sql = "UPDATE ".$record->getTable()->getTableName()." SET ".implode(", ",$set)." WHERE ".implode(" = ? && ",$record->getTable()->getPrimaryKeys())." = ?";
$stmt = $this->dbh->prepare($sql);
$stmt->execute($params);
$record->setID(true);
return true;
}
/**
* inserts a record into database
*
* @param Doctrine_Record $record
* @return boolean
*/
private function insert(Doctrine_Record $record) {
$array = $record->getPrepared();
if(empty($array))
return false;
$seq = $record->getTable()->getSequenceName();
if( ! empty($seq)) {
$id = $this->getNextID($seq);
$name = $record->getTable()->getIdentifier();
$array[$name] = $id;
}
if(isset($this->validator)) {
if( ! $this->validator->validateRecord($record)) {
return false;
}
}
$strfields = join(", ", array_keys($array));
$strvalues = substr(str_repeat("?, ",count($array)),0,-2);
$sql = "INSERT INTO ".$record->getTable()->getTableName()." (".$strfields.") VALUES (".$strvalues.")";
$stmt = $this->dbh->prepare($sql);
$stmt->execute(array_values($array));
return true;
}
/**
* deletes all related composites
* this method is always called internally when a record is deleted
*
* @return void
*/
final public function deleteComposites(Doctrine_Record $record) {
foreach($record->getTable()->getForeignKeys() as $fk) {
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::MANY_COMPOSITE:
$obj = $record->get($fk->getTable()->getComponentName());
$obj->delete();
break;
endswitch;
}
}
/**
* deletes this data access object and all the related composites
* this operation is isolated by a transaction
*
* this event can be listened by the onPreDelete and onDelete listeners
*
* @return boolean true on success, false on failure
*/
final public function delete(Doctrine_Record $record) {
switch($record->getState()):
case Doctrine_Record::STATE_PROXY:
case Doctrine_Record::STATE_CLEAN:
case Doctrine_Record::STATE_DIRTY:
$this->beginTransaction();
$this->deleteComposites($record);
$this->addDelete($record);
$this->commit();
return true;
break;
default:
return false;
endswitch;
}
/**
* adds record into pending insert list
* @param Doctrine_Record $record
*/
public function addInsert(Doctrine_Record $record) {
$name = $record->getTable()->getComponentName();
$this->insert[$name][] = $record;
}
/**
* adds record into penging update list
* @param Doctrine_Record $record
*/
public function addUpdate(Doctrine_Record $record) {
$name = $record->getTable()->getComponentName();
$this->update[$name][] = $record;
}
/**
* adds record into pending delete list
* @param Doctrine_Record $record
*/
public function addDelete(Doctrine_Record $record) {
$name = $record->getTable()->getComponentName();
$this->delete[$name][] = $record;
}
/**
* returns the pending insert list
*
* @return array
*/
public function getInserts() {
return $this->insert;
}
/**
* returns the pending update list
*
* @return array
*/
public function getUpdates() {
return $this->update;
}
/**
* returns the pending delete list
*
* @return array
*/
public function getDeletes() {
return $this->delete;
}
/**
* returns a string representation of this object
* @return string
*/
public function __toString() {
return Doctrine_Lib::getSessionAsString($this);
}
}
?>
<?php
require_once("Access.php");
/**
* Doctrine_Statement
*
* Doctrine_Statement is a wrapper for PDOStatement with DQL support
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class Doctrine_Statement extends Doctrine_Access {
/**
* @var Doctrine_Query $query
*/
private $query;
/**
* @var PDOStatement $stmt
*/
private $stmt;
/**
* @var array $reserved
*/
private $reserved = array();
/**
* constructor
*
* @param Doctrine_Query $query
* @param PDOStatement $stmt
*/
public function __construct(Doctrine_Query $query, PDOStatement $stmt) {
$this->query = $query;
$this->stmt = $stmt;
}
public function set($name, $value) { }
public function get($name) { }
/**
* getCollection
* returns Doctrine_Collection object
*
* @parma string $name component name
* @param integer $index
* @return Doctrine_Collection
*/
private function getCollection($name) {
$table = $this->session->getTable($name);
switch($this->fetchModes[$name]):
case Doctrine::FETCH_BATCH:
$coll = new Doctrine_Collection_Batch($table);
break;
case Doctrine::FETCH_LAZY:
$coll = new Doctrine_Collection_Lazy($table);
break;
case Doctrine::FETCH_OFFSET:
$coll = new Doctrine_Collection_Offset($table);
break;
case Doctrine::FETCH_IMMEDIATE:
$coll = new Doctrine_Collection_Immediate($table);
break;
case Doctrine::FETCH_LAZY_OFFSET:
$coll = new Doctrine_Collection_LazyOffset($table);
break;
endswitch;
$coll->populate($this);
return $coll;
}
/**
* execute
* executes the dql query, populates all collections
* and returns the root collection
*
* @param array $params
* @return Doctrine_Collection
*/
public function execute($params = array()) {
switch(count($this->tables)):
case 0:
throw new DQLException();
break;
case 1:
$query = $this->getQuery();
$keys = array_keys($this->tables);
$name = $this->tables[$keys[0]]->getComponentName();
$stmt = $this->session->execute($query,$params);
while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
foreach($data as $key => $value):
$e = explode("__",$key);
if(count($e) > 1) {
$data[$e[1]] = $value;
} else {
$data[$e[0]] = $value;
}
unset($data[$key]);
endforeach;
$this->data[$name][] = $data;
endwhile;
return $this->getCollection($keys[0]);
break;
default:
$query = $this->getQuery();
$keys = array_keys($this->tables);
$root = $keys[0];
$stmt = $this->session->execute($query,$params);
$previd = array();
$coll = $this->getCollection($root);
$array = $this->parseData($stmt);
foreach($array as $data):
/**
* remove duplicated data rows and map data into objects
*/
foreach($data as $key => $row):
if(empty($row))
continue;
$key = ucwords($key);
$name = $this->tables[$key]->getComponentName();
if( ! isset($previd[$name]))
$previd[$name] = array();
if($previd[$name] !== $row) {
$this->tables[$name]->setData($row);
$record = $this->tables[$name]->getRecord();
if($name == $root) {
$this->tables[$name]->setData($row);
$record = $this->tables[$name]->getRecord();
$coll->add($record);
} else {
$last = $coll->getLast();
if( ! $last->hasReference($name)) {
$last->initReference($this->getCollection($name),$this->connectors[$name]);
}
$last->addReference($record);
}
}
$previd[$name] = $row;
endforeach;
endforeach;
return $coll;
endswitch;
}
/**
* parseData
* parses the data returned by PDOStatement
*
* @param PDOStatement $stmt
* @return array
*/
public function parseData(PDOStatement $stmt) {
$array = array();
while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
/**
* parse the data into two-dimensional array
*/
foreach($data as $key => $value):
$e = explode("__",$key);
if(count($e) > 1) {
$data[$e[0]][$e[1]] = $value;
} else {
$data[0][$e[0]] = $value;
}
unset($data[$key]);
endforeach;
$array[] = $data;
endwhile;
$stmt->closeCursor();
return $array;
}
}
?>
<?php
require_once("Exception/Find.class.php");
require_once("Exception/Mapping.class.php");
require_once("Exception/PrimaryKey.class.php");
require_once("Configurable.php");
/**
* Doctrine_Table represents a database table
* each Doctrine_Table holds the information of foreignKeys and associations
*
*
* @author Konsta Vesterinen
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
* @version 1.0 alpha
*/
class Doctrine_Table extends Doctrine_Configurable {
/**
* @var boolean $isNewEntry whether ot not this table created a new record or not, used only internally
*/
private $isNewEntry = false;
/**
* @var array $data temporary data which is then loaded into Doctrine_Record::$data
*/
private $data = array();
/**
* @var array $relations an array containing all the Doctrine_Relation objects for this table
*/
private $relations = array();
/**
* @var array $primaryKeys an array containing all primary key column names
*/
private $primaryKeys = array();
/**
* @var mixed $identifier
*/
private $identifier;
/**
* @var integer $identifierType
*/
private $identifierType;
/**
* @var string $query cached simple query
*/
private $query;
/**
* @var Doctrine_Session $session Doctrine_Session object that created this table
*/
private $session;
/**
* @var string $name name of the component, for example component name of the GroupTable is 'Group'
*/
private $name;
/**
* @var string $tableName database table name, in most cases this is the same as component name but in some cases
* where one-table-multi-class inheritance is used this will be the name of the inherited table
*/
private $tableName;
/**
* @var string $sequenceName Some databases need sequences instead of auto incrementation primary keys, you can set specific
* sequence for your table by calling setSequenceName()
*/
private $sequenceName;
/**
* @var array $identityMap first level cache
*/
private $identityMap = array();
private $repository;
/**
* @var Doctrine_Cache $cache second level cache
*/
private $cache;
/**
* @var array $columns an array of column definitions
*/
private $columns;
/**
* @var array $bound bound relations
*/
private $bound = array();
/**
* @var array $boundAliases bound relation aliases
*/
private $boundAliases = array();
/**
* @var integer $columnCount cached column count
*/
private $columnCount;
/**
* @var array $inheritanceMap inheritanceMap is used for inheritance mapping, keys representing columns and values
* the column values that should correspond to child classes
*/
private $inheritanceMap = array();
/**
* @var array $parents the parent classes of this component
*/
private $parents = array();
/**
* the constructor
* @throws Doctrine_ManagerException if there are no opened sessions
* @throws Doctrine_TableException if there is already an instance of this table
* @return void
*/
public function __construct($name) {
$this->session = Doctrine_Manager::getInstance()->getCurrentSession();
$this->setParent($this->session);
$this->name = $name;
if( ! class_exists($name) || empty($name))
throw new Doctrine_Exception("Couldn't find class $name");
$record = new $name($this);
$names = array();
$class = $name;
// get parent classes
do {
if($class == "Doctrine_Record") break;
$name = $class;
$names[] = $name;
} while($class = get_parent_class($class));
// reverse names
$names = array_reverse($names);
// create database table
if(method_exists($record,"setTableDefinition")) {
$record->setTableDefinition();
$this->columnCount = count($this->columns);
if(isset($this->columns)) {
$method = new ReflectionMethod($this->name,"setTableDefinition");
$class = $method->getDeclaringClass();
if( ! isset($this->tableName))
$this->tableName = strtolower($class->getName());
switch(count($this->primaryKeys)):
case 0:
$this->columns = array_merge(array("id" => array("integer",11,"autoincrement|primary")), $this->columns);
$this->primaryKeys[] = "id";
$this->identifier = "id";
$this->identifierType = Doctrine_Identifier::AUTO_INCREMENT;
break;
default:
if(count($this->primaryKeys) > 1) {
$this->identifier = $this->primaryKeys;
$this->identifierType = Doctrine_Identifier::COMPOSITE;
} else {
foreach($this->primaryKeys as $pk) {
$o = $this->columns[$pk][2];
$e = explode("|",$o);
$found = false;
foreach($e as $option) {
if($found)
break;
$e2 = explode(":",$option);
switch(strtolower($e2[0])):
case "unique":
$this->identifierType = Doctrine_Identifier::UNIQUE;
$found = true;
break;
case "autoincrement":
$this->identifierType = Doctrine_Identifier::AUTO_INCREMENT;
$found = true;
break;
case "seq":
$this->identifierType = Doctrine_Identifier::SEQUENCE;
$found = true;
break;
endswitch;
}
if( ! isset($this->identifierType))
$this->identifierType = Doctrine_Identifier::NORMAL;
$this->identifier = $pk;
}
}
endswitch;
if($this->getAttribute(Doctrine::ATTR_CREATE_TABLES)) {
$dict = new Doctrine_DataDict($this->getSession()->getDBH());
$dict->createTable($this->tableName, $this->columns);
}
}
} else {
throw new Doctrine_Exception("Class '$name' has no table definition.");
}
$record->setUp();
// save parents
array_pop($names);
$this->parents = $names;
$this->query = "SELECT ".implode(", ",array_keys($this->columns))." FROM ".$this->getTableName();
// check if an instance of this table is already initialized
if( ! $this->session->addTable($this))
throw new Doctrine_Table_Exception();
$this->initComponents();
}
/**
* initializes components this table uses
*
* @return void
*/
final public function initComponents() {
$this->repository = new Doctrine_Repository($this);
switch($this->getAttribute(Doctrine::ATTR_CACHE)):
case Doctrine::CACHE_SQLITE:
$this->cache = new Doctrine_Cache_Sqlite($this);
break;
case Doctrine::CACHE_NONE:
$this->cache = new Doctrine_Cache($this);
break;
endswitch;
}
/**
* @return Doctrine_Repository
*/
public function getRepository() {
return $this->repository;
}
/**
* setColumn
* @param string $name
* @param string $type
* @param integer $length
* @param mixed $options
* @return void
*/
final public function setColumn($name, $type, $length, $options = "") {
$this->columns[$name] = array($type,$length,$options);
$e = explode("|",$options);
if(in_array("primary",$e)) {
$this->primaryKeys[] = $name;
}
}
/**
* @return mixed
*/
final public function getIdentifier() {
return $this->identifier;
}
/**
* @return integer
*/
final public function getIdentifierType() {
return $this->identifierType;
}
/**
* hasColumn
* @return boolean
*/
final public function hasColumn($name) {
return isset($this->columns[$name]);
}
/**
* @param mixed $key
* @return void
*/
final public function setPrimaryKey($key) {
switch(gettype($key)):
case "array":
$this->primaryKeys = array_values($key);
break;
case "string":
$this->primaryKeys[] = $key;
break;
endswitch;
}
/**
* returns all primary keys
* @return array
*/
final public function getPrimaryKeys() {
return $this->primaryKeys;
}
/**
* @return boolean
*/
final public function hasPrimaryKey($key) {
return in_array($key,$this->primaryKeys);
}
/**
* @param $sequence
* @return void
*/
final public function setSequenceName($sequence) {
$this->sequenceName = $sequence;
}
/**
* @return string sequence name
*/
final public function getSequenceName() {
return $this->sequenceName;
}
/**
* setInheritanceMap
* @param array $inheritanceMap
* @return void
*/
final public function setInheritanceMap(array $inheritanceMap) {
$this->inheritanceMap = $inheritanceMap;
}
/**
* @return array inheritance map (array keys as fields)
*/
final public function getInheritanceMap() {
return $this->inheritanceMap;
}
/**
* return all composite paths in the form [component1].[component2]. . .[componentN]
* @return array
*/
final public function getCompositePaths() {
$array = array();
$name = $this->getComponentName();
foreach($this->bound as $k=>$a) {
try {
$fk = $this->getForeignKey($k);
switch($fk->getType()):
case Doctrine_Relation::ONE_COMPOSITE:
case Doctrine_Relation::MANY_COMPOSITE:
$n = $fk->getTable()->getComponentName();
$array[] = $name.".".$n;
$e = $fk->getTable()->getCompositePaths();
if( ! empty($e)) {
foreach($e as $name) {
$array[] = $name.".".$n.".".$name;
}
}
break;
endswitch;
} catch(InvalidKeyException $e) {
}
}
return $array;
}
/**
* returns all bound relations
*
* @return array
*/
final public function getBounds() {
return $this->bound;
}
/**
* returns a bound relation array
*
* @param string $name
* @return array
*/
final public function getBound($name) {
if( ! isset($this->bound[$name]))
throw new InvalidKeyException();
return $this->bound[$name];
}
/**
* returns a bound relation array
*
* @param string $name
* @return array
*/
final public function getBoundForName($name) {
foreach($this->bound as $k => $bound) {
if($bound[3] == $name) {
return $this->bound[$k];
}
}
throw new InvalidKeyException();
}
/**
* returns the alias for given component name
*
* @param string $name
* @return string
*/
final public function getAlias($name) {
if(isset($this->boundAliases[$name]))
return $this->boundAliases[$name];
return $name;
}
/**
* returns component name for given alias
*
* @param string $alias
* @return string
*/
final public function getAliasName($alias) {
if($name = array_search($this->boundAliases,$alias))
return $name;
throw new InvalidKeyException();
}
/**
* unbinds all relations
*
* @return void
*/
final public function unbindAll() {
$this->bound = array();
$this->relations = array();
$this->boundAliases = array();
}
/**
* unbinds a relation
* returns true on success, false on failure
*
* @param $name
* @return boolean
*/
final public function unbind() {
if( ! isset($this->bound[$name]))
return false;
unset($this->bound[$name]);
if(isset($this->relations[$name]))
unset($this->relations[$name]);
if(isset($this->boundAliases[$name]))
unset($this->boundAliases[$name]);
return true;
}
/**
* binds a relation
*
* @param string $name
* @param string $field
* @return void
*/
final public function bind($name,$field,$type,$localKey) {
if(isset($this->relations[$name]))
throw new InvalidKeyException();
$e = explode(" as ",$name);
$name = $e[0];
if(isset($e[1])) {
$alias = $e[1];
$this->boundAliases[$name] = $alias;
} else
$alias = $name;
$this->bound[$alias] = array($field,$type,$localKey,$name);
}
/**
* getComponentName
* @return string the component name
*/
final public function getComponentName() {
return $this->name;
}
/**
* @return Doctrine_Session
*/
final public function getSession() {
return $this->session;
}
/**
* @return Doctrine_Cache
*/
final public function getCache() {
return $this->cache;
}
/**
* @param string $name component name of which a foreign key object is bound
* @return Doctrine_Relation
*/
final public function getForeignKey($name) {
if(isset($this->relations[$name]))
return $this->relations[$name];
if(isset($this->bound[$name])) {
$type = $this->bound[$name][1];
$local = $this->bound[$name][2];
list($component, $foreign) = explode(".",$this->bound[$name][0]);
$alias = $name;
$name = $this->bound[$alias][3];
$table = $this->session->getTable($name);
if($component == $this->name || in_array($component, $this->parents)) {
// ONE-TO-ONE
if($type == Doctrine_Relation::ONE_COMPOSITE ||
$type == Doctrine_Relation::ONE_AGGREGATE) {
if( ! isset($local))
$local = $table->getIdentifier();
$relation = new Doctrine_LocalKey($table,$foreign,$local,$type);
} else
throw new Doctrine_Mapping_Exception("Only one-to-one relations are possible when local reference key is used.");
} elseif($component == $name || ($component == $alias && $name == $this->name)) {
if( ! isset($local))
$local = $this->identifier;
// ONE-TO-MANY or ONE-TO-ONE
$relation = new Doctrine_ForeignKey($table,$local,$foreign,$type);
} else {
// MANY-TO-MANY
// only aggregate relations allowed
if($type != Doctrine_Relation::MANY_AGGREGATE)
throw new Doctrine_Mapping_Exception("Only aggregate relations are allowed for many-to-many relations");
$classes = array_merge($this->parents, array($this->name));
foreach(array_reverse($classes) as $class) {
try {
$bound = $table->getBoundForName($class);
break;
} catch(InvalidKeyException $exc) { }
}
if( ! isset($local))
$local = $this->identifier;
$e2 = explode(".",$bound[0]);
$fields = explode("-",$e2[1]);
if($e2[0] != $component)
throw new Doctrine_Mapping_Exception($e2[0]." doesn't match ".$component);
$associationTable = $this->session->getTable($e2[0]);
if(count($fields) > 1) {
// SELF-REFERENCING THROUGH JOIN TABLE
$this->relations[$e2[0]] = new Doctrine_ForeignKey($associationTable,$local,$fields[0],Doctrine_Relation::MANY_COMPOSITE);
$relation = new Doctrine_Association($table,$associationTable,$fields[0],$fields[1],$type);
} else {
// NORMAL MANY-TO-MANY RELATIONSHIP
$this->relations[$e2[0]] = new Doctrine_ForeignKey($associationTable,$local,$e2[1],Doctrine_Relation::MANY_COMPOSITE);
$relation = new Doctrine_Association($table,$associationTable,$e2[1],$foreign,$type);
}
}
$this->relations[$alias] = $relation;
return $this->relations[$alias];
}
throw new InvalidKeyException();
}
/**
* returns an array containing all foreign key objects
*
* @return array
*/
final public function getForeignKeys() {
$a = array();
foreach($this->bound as $k=>$v) {
$this->getForeignKey($k);
}
return $this->relations;
}
/**
* sets the database table name
*
* @param string $name database table name
* @return void
*/
final public function setTableName($name) {
$this->tableName = $name;
}
/**
* returns the database table name
*
* @return string
*/
final public function getTableName() {
return $this->tableName;
}
/**
* create
* creates a new record
*
* @param $array an array where keys are field names and values representing field values
* @return Doctrine_Record
*/
public function create(array $array = array()) {
$this->data = $array;
$this->isNewEntry = true;
$record = new $this->name($this);
$this->isNewEntry = false;
$this->data = array();
return $record;
}
/**
* finds a record by its identifier
*
* @param $id database row id
* @throws Doctrine_Find_Exception
* @return Doctrine_Record a record for given database identifier
*/
public function find($id) {
if($id !== null) {
if( ! is_array($id))
$id = array($id);
else
$id = array_values($id);
$query = $this->query." WHERE ".implode(" = ? AND ",$this->primaryKeys)." = ?";
$query = $this->applyInheritance($query);
$params = array_merge($id, array_values($this->inheritanceMap));
$this->data = $this->session->execute($query,$params)->fetch(PDO::FETCH_ASSOC);
if($this->data === false)
throw new Doctrine_Find_Exception();
}
return $this->getRecord();
}
/**
* applyInheritance
* @param $where query where part to be modified
* @return string query where part with column aggregation inheritance added
*/
final public function applyInheritance($where) {
if( ! empty($this->inheritanceMap)) {
$a = array();
foreach($this->inheritanceMap as $field => $value) {
$a[] = $field." = ?";
}
$i = implode(" AND ",$a);
$where .= " AND $i";
}
return $where;
}
/**
* findAll
* returns a collection of records
*
* @return Doctrine_Collection
*/
public function findAll() {
$graph = new Doctrine_Query($this->session);
$users = $graph->query("FROM ".$this->name);
return $users;
}
/**
* findBySql
* finds records with given sql where clause
* returns a collection of records
*
* @param string $sql SQL after WHERE clause
* @param array $params query parameters
* @return Doctrine_Collection
*/
public function findBySql($sql, array $params = array()) {
$q = new Doctrine_Query($this->session);
$users = $q->query("FROM ".$this->name." WHERE ".$sql, $params);
return $users;
}
/**
* clear
* clears the first level cache (identityMap)
*
* @return void
*/
public function clear() {
$this->identityMap = array();
}
/**
* getRecord
* first checks if record exists in identityMap, if not
* returns a new record
*
* @return Doctrine_Record
*/
public function getRecord() {
$key = $this->getIdentifier();
if( ! is_array($key))
$key = array($key);
foreach($key as $k) {
if( ! isset($this->data[$k]))
throw new Doctrine_Exception("No primary key found");
$id[] = $this->data[$k];
}
$id = implode(' ', $id);
if(isset($this->identityMap[$id]))
$record = $this->identityMap[$id];
else {
$record = new $this->name($this);
$this->identityMap[$id] = $record;
}
$this->data = array();
return $record;
}
/**
* @param $id database row id
* @throws Doctrine_Find_Exception
* @return DAOProxy a proxy for given identifier
*/
final public function getProxy($id = null) {
if($id !== null) {
$query = "SELECT ".implode(", ",$this->primaryKeys)." FROM ".$this->getTableName()." WHERE ".implode(" = ? && ",$this->primaryKeys)." = ?";
$query = $this->applyInheritance($query);
$params = array_merge(array($id), array_values($this->inheritanceMap));
$this->data = $this->session->execute($query,$params)->fetch(PDO::FETCH_ASSOC);
if($this->data === false)
throw new Doctrine_Find_Exception();
}
return $this->getRecord();
}
/**
* getTableDescription
* @return Doctrine_Table_Description
*/
final public function getTableDescription() {
return $this->columns;
}
/**
* @return Doctrine_Query a Doctrine_Query object
*/
public function getQueryObject() {
$graph = new Doctrine_Query($this->getSession());
$graph->load($this->getComponentName());
return $graph;
}
/**
* execute
* @param string $query
* @param array $array
* @param integer $limit
* @param integer $offset
*/
public function execute($query, array $array = array(), $limit = null, $offset = null) {
$coll = new Doctrine_Collection($this);
$query = $this->session->modifyLimitQuery($query,$limit,$offset);
if( ! empty($array)) {
$stmt = $this->session->getDBH()->prepare($query);
$stmt->execute($array);
} else {
$stmt = $this->session->getDBH()->query($query);
}
$data = $stmt->fetchAll(PDO::FETCH_ASSOC);
$stmt->closeCursor();
foreach($data as $row) {
$this->data = $row;
$record = $this->getRecord();
$coll->add($record);
}
return $coll;
}
/**
* @return integer
*/
final public function getColumnCount() {
return $this->columnCount;
}
/**
* returns all columns and their definitions
*
* @return array
*/
final public function getColumns() {
return $this->columns;
}
/**
* returns an array containing all the column names
*
* @return array
*/
public function getColumnNames() {
return array_keys($this->columns);
}
/**
* setData
* doctrine uses this function internally
* users are strongly discouraged to use this function
*
* @param array $data internal data
* @return void
*/
public function setData(array $data) {
$this->data = $data;
}
/**
* returns the maximum primary key value
*
* @return integer
*/
final public function getMaxIdentifier() {
$sql = "SELECT MAX(".$this->getIdentifier().") FROM ".$this->getTableName();
$stmt = $this->session->getDBH()->query($sql);
$data = $stmt->fetch(PDO::FETCH_NUM);
return isset($data[0])?$data[0]:1;
}
/**
* return whether or not a newly created object is new or not
*
* @return boolean
*/
final public function isNewEntry() {
return $this->isNewEntry;
}
/**
* returns simple cached query
*
* @return string
*/
final public function getQuery() {
return $this->query;
}
/**
* returns internal data, used by Doctrine_Record instances
* when retrieving data from database
*
* @return array
*/
final public function getData() {
return $this->data;
}
/**
* returns a string representation of this object
*
* @return string
*/
public function __toString() {
return Doctrine_Lib::getTableAsString($this);
}
}
?>
<?php
/**
* Doctrine_Validator
* Doctrine_Session uses this class for transaction validation
*
* @package Doctrine ORM
* @url www.phpdoctrine.com
* @license LGPL
*/
class Doctrine_Validator {
/**
* ERROR CONSTANTS
*/
/**
* constant for length validation error
*/
const ERR_LENGTH = 0;
/**
* constant for type validation error
*/
const ERR_TYPE = 1;
/**
* constant for general validation error
*/
const ERR_VALID = 2;
/**
* constant for unique validation error
*/
const ERR_UNIQUE = 3;
/**
* constant for blank validation error
*/
const ERR_BLANK = 4;
/**
* constant for date validation error
*/
const ERR_DATE = 5;
/**
* constant for null validation error
*/
const ERR_NULL = 6;
/**
* constant for enum validation error
*/
const ERR_ENUM = 7;
/**
* constant for range validation error
*/
const ERR_RANGE = 8;
/**
* @var array $stack error stack
*/
private $stack = array();
/**
* @var array $validators an array of validator objects
*/
private static $validators = array();
/**
* returns a validator object
*
* @param string $name
* @return Doctrine_Validator_Interface
*/
public static function getValidator($name) {
if( ! isset(self::$validators[$name])) {
$class = "Doctrine_Validator_".ucwords(strtolower($name));
if(class_exists($class)) {
self::$validators[$name] = new $class;
} elseif(class_exists($name."Validator")) {
self::$validators[$name] = new $name."Validator";
} else
throw new Doctrine_Exception("Validator named '$name' not availible.");
}
return self::$validators[$name];
}
/**
* validates a given record and saves possible errors
* in Doctrine_Validator::$stack
*
* @param Doctrine_Record $record
* @return void
*/
public function validateRecord(Doctrine_Record $record) {
$modified = $record->getModified();
$columns = $record->getTable()->getColumns();
$name = $record->getTable()->getComponentName();
$err = array();
foreach($modified as $key => $value) {
$column = $columns[$key];
if(strlen($value) > $column[1]) {
$err[$key] = Doctrine_Validator::ERR_LENGTH;
continue;
}
if(self::gettype($value) !== $column[0]) {
$err[$key] = Doctrine_Validator::ERR_TYPE;
continue;
}
$e = explode("|",$column[2]);
foreach($e as $k => $arg) {
if(empty($arg) || $arg == "primary" || $arg == "protected" || $arg == "autoincrement")
continue;
$args = explode(":",$arg);
if( ! isset($args[1]))
$args[1] = '';
$validator = self::getValidator($args[0]);
if( ! $validator->validate($record, $key, $value, $args[1])) {
switch(strtolower($args[0])):
case "unique":
$err[$key] = Doctrine_Validator::ERR_UNIQUE;
break;
case "notnull":
$err[$key] = Doctrine_Validator::ERR_NULL;
break;
case "notblank":
$err[$key] = Doctrine_Validator::ERR_BLANK;
break;
case "enum":
$err[$key] = Doctrine_Validator::ERR_VALID;
break;
default:
$err[$key] = Doctrine_Validator::ERR_VALID;
break;
endswitch;
}
// errors found quit validation looping for this column
if(isset($err[$key]))
break;
}
}
if( ! empty($err)) {
$this->stack[$name][] = $err;
return false;
}
return true;
}
/**
* whether or not this validator has errors
*
* @return boolean
*/
public function hasErrors() {
return (count($this->stack) > 0);
}
/**
* returns the error stack
*
* @return array
*/
public function getErrorStack() {
return $this->stack;
}
/**
* returns the type of loosely typed variable
*
* @param mixed $var
* @return string
*/
public static function gettype($var) {
$type = gettype($var);
switch($type):
case "string":
if(preg_match("/^[0-9]+$/",$var)) return "integer";
elseif(is_numeric($var)) return "float";
else return $type;
break;
default:
return $type;
endswitch;
}
}
?>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment