diff --git a/Doctrine/Null.php b/Doctrine/Null.php index 7ff0711c4..420bd2689 100644 --- a/Doctrine/Null.php +++ b/Doctrine/Null.php @@ -26,7 +26,6 @@ * * @package Doctrine ORM * @url www.phpdoctrine.com - * @license LGPL + * @license LGPL */ -class Doctrine_Null { } - +final class Doctrine_Null { } diff --git a/Doctrine/Record.php b/Doctrine/Record.php index 2b5c46428..e5c3170ae 100644 --- a/Doctrine/Record.php +++ b/Doctrine/Record.php @@ -18,7 +18,7 @@ * and is licensed under the LGPL. For more information, see * . */ - +Doctrine::autoload('Doctrine_Access'); /** * Doctrine_Record * All record classes should inherit this super class @@ -225,10 +225,14 @@ abstract class Doctrine_Record extends Doctrine_Access implements Countable, Ite } /** * setUp - * implemented by child classes + * this method is used for setting up relations and attributes + * it should be implemented by child classes + * + * @return void */ public function setUp() { } /** + * getOID * return the object identifier * * @return integer diff --git a/draft/DB.php b/draft/DB.php new file mode 100644 index 000000000..c1212a8b8 --- /dev/null +++ b/draft/DB.php @@ -0,0 +1,476 @@ +. + */ + +/** + * Doctrine_DB + * A thin layer on top of PDO + * + * @author Konsta Vesterinen + * @license LGPL + * @package Doctrine + */ +class Doctrine_DB implements Countable, IteratorAggregate { + /** + * default DSN + */ + const DSN = "mysql://root:dc34@localhost/test"; + /** + * @var array $instances all the instances of this class + */ + protected static $instances = array(); + /** + * @var array $queries all the executed queries + */ + protected $queries = array(); + /** + * @var array $exectimes execution times of the executed queries + */ + protected $exectimes = array(); + /** + * @var array $isConnected whether or not a connection has been established + */ + protected $isConnected = false; + /** + * @var string $dsn data source name + */ + protected $dsn; + /** + * @var string $username database username + */ + protected $username; + /** + * @var string $password database password + */ + protected $password; + /** + * @var PDO $dbh the database handler + */ + + /** + * constructor + * + * @param string $dsn data source name + * @param string $username database username + * @param string $password database password + */ + public function __construct($dsn,$username,$password) { + $this->dsn = $dsn; + $this->username = $username; + $this->password = $password; + } + /** + * getDSN + * + * @return string + */ + public function getDSN() { + return $this->dsn; + } + /** + * getUsername + */ + public function getUsername() { + return $this->username; + } + /** + * getPassword + */ + public function getPassword() { + return $this->password; + } + + /** + * connect + * connects into database + * + * @return boolean + */ + public function connect() { + if($this->isConnected) + return false; + + $this->dbh = new PDO($this->dsn,$this->username,$this->password); + $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->dbh->setAttribute(PDO::ATTR_STATEMENT_CLASS, array("Doctrine_DBStatement",array($this))); + + return true; + } + + /** + * getConnection + * + * @param string $dsn PEAR::DB like DSN + * format: schema://user:password@address/dbname + * + * @return + */ + public static function getConnection($dsn = null, $username = null, $password = null) { + $md5 = md5($dsn); + + if(isset($username)) { + self::$instances[$md5] = new Doctrine_DB($dsn, $username, $password); + } + + if( ! isset(self::$instances[$md5])) { + if( ! isset($dsn)) { + $a = self::parseDSN(self::DSN); + } else { + $a = self::parseDSN($dsn); + } + $e = array(); + + $dsn = $a["phptype"].":host=".$a["hostspec"].";dbname=".$a["database"]; + $username = isset($a["username"])?$a['username']:null; + $password = isset($a["password"])?$a['password']:null; + + self::$instances[$md5] = new Doctrine_DB($dsn,$username,$password); + } + return self::$instances[$md5]; + } + /** + * clear + * clears all instances from the memory + * + * @return void + */ + public static function clear() { + self::$instances = array(); + } + + /** + * errorCode + * Fetch the SQLSTATE associated with the last operation on the database handle + * + * @return integer + */ + public function errorCode() { + $this->connect(); + + return $this->dbh->errorCode(); + } + /** + * errorInfo + * Fetch extended error information associated with the last operation on the database handle + * + * @return array + */ + public function errorInfo() { + $this->connect(); + + return $this->dbh->errorInfo(); + } + /** + * + * + * @param string $statement + */ + public function prepare ($statement) { + $this->connect(); + $this->queries[] = $query; + return $this->dbh->prepare($statement); + } + /** + * query + * + * @param string $statement + * @return Doctrine_DB_Statement|boolean + */ + public function query($statement, $fetchMode = null, $arg = null, $arg2 = null) { + $this->connect(); + + $this->queries[] = $query; + $time = microtime(); + + $stmt = $this->dbh->query($query, $fetchMode, $arg, $arg2); + + $this->exectimes[] = (microtime() - $time); + + return $stmt; + } + /** + * quote + * quotes a string for use in a query + * + * @param string $input + * @return string + */ + public function quote($input) { + $this->connect(); + + return $this->dbh->quote($input); + } + /** + * exec + * executes an SQL statement and returns the number of affected rows + * + * @param string $statement + * @return integer + */ + public function exec($statement) { + $this->connect(); + + return $this->dbh->exec($statement); + } + /** + * lastInsertId + * + * + */ + public function lastInsertId() { + $this->connect(); + + return $this->dbh->lastInsertId(); + } + /** + * begins a transaction + * + * @return boolean + */ + public function beginTransaction() { + $this->connect(); + + return $this->dbh->beginTransaction(); + } + /** + * commits a transaction + * + * @return boolean + */ + public function commit() { + $this->connect(); + + return $this->dbh->commit(); + } + /** + * rollBack + * + * @return boolean + */ + public function rollBack() { + $this->connect(); + + $this->dbh->rollBack(); + } + /** + * getAttribute + * retrieves a database connection attribute + * + * @param integer $attribute + * @return mixed + */ + public function getAttribute($attribute) { + $this->connect(); + + $this->dbh->getAttribute($attribute); + } + /** + * returns an array of available PDO drivers + */ + public static function getAvailableDrivers() { + return PDO::getAvailibleDrivers(); + } + /** + * setAttribute + * sets an attribute + * + * @param integer $attribute + * @param mixed $value + * @return boolean + */ + public function setAttribute($attribute, $value) { + $this->connect(); + + $this->dbh->setAttribute($attribute, $value); + } + /** + * @param string $time exectime of the last executed query + * @return void + */ + public function addExecTime($time) { + $this->exectimes[] = $time; + } + + public function getExecTimes() { + return $this->exectimes; + } + /** + * getQueries + * returns an array of executed queries + * + * @return array + */ + public function getQueries() { + return $this->queries; + } + /** + * getIterator + * + * @return ArrayIterator + */ + public function getIterator() { + return new ArrayIterator($this->queries); + } + /** + * count + * returns the number of executed queries + * + * @return integer + */ + public function count() { + return count($this->queries); + } + /** + * parseDSN + * Parse a data source name. + * + * Additional keys can be added by appending a URI query string to the + * end of the DSN. + * + * The format of the supplied DSN is in its fullest form: + * + * phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true + * + * + * Most variations are allowed: + * + * phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644 + * phptype://username:password@hostspec/database_name + * phptype://username:password@hostspec + * phptype://username@hostspec + * phptype://hostspec/database + * phptype://hostspec + * phptype(dbsyntax) + * phptype + * + * + * @param string Data Source Name to be parsed + * + * @return array an associative array with the following keys: + * + phptype: Database backend used in PHP (mysql, odbc etc.) + * + dbsyntax: Database used with regards to SQL syntax etc. + * + protocol: Communication protocol to use (tcp, unix etc.) + * + hostspec: Host specification (hostname[:port]) + * + database: Database to use on the DBMS server + * + username: User name for login + * + password: Password for login + * + * @access public + * @author Tomas V.V.Cox + */ + public static function parseDSN($dsn) { + // Find phptype and dbsyntax + if (($pos = strpos($dsn, '://')) !== false) { + $str = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 3); + } else { + $str = $dsn; + $dsn = null; + } + + // Get phptype and dbsyntax + // $str => phptype(dbsyntax) + if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) { + $parsed['phptype'] = $arr[1]; + $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2]; + } else { + $parsed['phptype'] = $str; + $parsed['dbsyntax'] = $str; + } + + if (!count($dsn)) { + return $parsed; + } + + // Get (if found): username and password + // $dsn => username:password@protocol+hostspec/database + if (($at = strrpos($dsn,'@')) !== false) { + $str = substr($dsn, 0, $at); + $dsn = substr($dsn, $at + 1); + if (($pos = strpos($str, ':')) !== false) { + $parsed['username'] = rawurldecode(substr($str, 0, $pos)); + $parsed['password'] = rawurldecode(substr($str, $pos + 1)); + } else { + $parsed['username'] = rawurldecode($str); + } + } + + // Find protocol and hostspec + + // $dsn => proto(proto_opts)/database + if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) { + $proto = $match[1]; + $proto_opts = $match[2] ? $match[2] : false; + $dsn = $match[3]; + + // $dsn => protocol+hostspec/database (old format) + } else { + if (strpos($dsn, '+') !== false) { + list($proto, $dsn) = explode('+', $dsn, 2); + } + if (strpos($dsn, '/') !== false) { + list($proto_opts, $dsn) = explode('/', $dsn, 2); + } else { + $proto_opts = $dsn; + $dsn = null; + } + } + + // process the different protocol options + $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp'; + $proto_opts = rawurldecode($proto_opts); + if (strpos($proto_opts, ':') !== false) { + list($proto_opts, $parsed['port']) = explode(':', $proto_opts); + } + if ($parsed['protocol'] == 'tcp') { + $parsed['hostspec'] = $proto_opts; + } elseif ($parsed['protocol'] == 'unix') { + $parsed['socket'] = $proto_opts; + } + + // Get dabase if any + // $dsn => database + if ($dsn) { + // /database + if (($pos = strpos($dsn, '?')) === false) { + $parsed['database'] = $dsn; + // /database?param1=value1¶m2=value2 + } else { + $parsed['database'] = substr($dsn, 0, $pos); + $dsn = substr($dsn, $pos + 1); + if (strpos($dsn, '&') !== false) { + $opts = explode('&', $dsn); + } else { // database?param1=value1 + $opts = array($dsn); + } + foreach ($opts as $opt) { + list($key, $value) = explode('=', $opt); + if (!isset($parsed[$key])) { + // don't allow params overwrite + $parsed[$key] = rawurldecode($value); + } + } + } + } + + return $parsed; + } +} + diff --git a/tests/run.php b/tests/run.php index 18d726c7b..baaf388df 100644 --- a/tests/run.php +++ b/tests/run.php @@ -25,7 +25,7 @@ require_once("FilterTestCase.php"); require_once("ValueHolderTestCase.php"); require_once("QueryLimitTestCase.php"); require_once("QueryReferenceModelTestCase.php"); - +require_once("DBTestCase.php"); require_once("SchemaTestCase.php"); require_once("ImportTestCase.php"); @@ -34,7 +34,7 @@ error_reporting(E_ALL); $test = new GroupTest("Doctrine Framework Unit Tests"); - +//$test->addTestCase(new Doctrine_DB_TestCase()); $test->addTestCase(new Doctrine_ConnectionTestCase());