config = $config; $this->driver = new $class; if (!empty($config['profiler'])) { $class = $config['profiler']; if (is_numeric($class) || is_bool($class)) { $class = 'DibiProfiler'; } if (!class_exists($class)) { throw new DibiException("Unable to create instance of dibi profiler '$class'."); } $this->setProfiler(new $class); } if (!empty($config['substitutes'])) { foreach ($config['substitutes'] as $key => $value) { dibi::addSubst($key, $value); } } if (empty($config['lazy'])) { $this->connect(); } } /** * Automatically frees the resources allocated for this result set. * @return void */ public function __destruct() { // disconnects and rolls back transaction - do not rely on auto-disconnect and rollback! $this->disconnect(); } /** * Connects to a database. * @return void */ final protected function connect() { if (!$this->connected) { if ($this->profiler !== NULL) { $ticket = $this->profiler->before($this, IDibiProfiler::CONNECT); } $this->driver->connect($this->config); $this->connected = TRUE; if (isset($ticket)) { $this->profiler->after($ticket); } } } /** * Disconnects from a database. * @return void */ final public function disconnect() { if ($this->connected) { $this->driver->disconnect(); $this->connected = FALSE; } } /** * Returns TRUE when connection was established. * @return bool */ final public function isConnected() { return $this->connected; } /** * Returns configuration variable. If no $key is passed, returns the entire array. * @see self::__construct * @param string * @param mixed default value to use if key not found * @return mixed */ final public function getConfig($key = NULL, $default = NULL) { if ($key === NULL) { return $this->config; } elseif (isset($this->config[$key])) { return $this->config[$key]; } else { return $default; } } /** * Apply configuration alias or default values. * @param array connect configuration * @param string key * @param string alias key * @return void */ public static function alias(&$config, $key, $alias=NULL) { if (isset($config[$key])) return; if ($alias !== NULL && isset($config[$alias])) { $config[$key] = $config[$alias]; unset($config[$alias]); } else { $config[$key] = NULL; } } /** * Returns the connection resource. * @return IDibiDriver */ final public function getDriver() { return $this->driver; } /** * Returns the connection resource. * @return resource * @deprecated use getDriver()->getResource() */ final public function getResource() { trigger_error('Deprecated: use getDriver()->getResource(...) instead.', E_USER_WARNING); return $this->driver->getResource(); } /** * Generates (translates) and executes SQL query. * @param array|mixed one or more arguments * @return DibiResult|int result set object (if any) * @throws DibiException */ final public function query($args) { $args = func_get_args(); $this->connect(); $translator = new DibiTranslator($this->driver); return $this->nativeQuery($translator->translate($args)); } /** * Generates and returns SQL query. * @param array|mixed one or more arguments * @return string * @throws DibiException */ final public function sql($args) { $args = func_get_args(); $this->connect(); $translator = new DibiTranslator($this->driver); return $translator->translate($args); } /** * Generates and prints SQL query. * @param array|mixed one or more arguments * @return bool */ final public function test($args) { $args = func_get_args(); $this->connect(); try { $translator = new DibiTranslator($this->driver); dibi::dump($translator->translate($args)); return TRUE; } catch (DibiException $e) { dibi::dump($e->getSql()); return FALSE; } } /** * Generates (translates) and returns SQL query as DibiDataSource. * @param array|mixed one or more arguments * @return DibiDataSource * @throws DibiException */ final public function dataSource($args) { $args = func_get_args(); $this->connect(); $translator = new DibiTranslator($this->driver); return new DibiDataSource($translator->translate($args), $this); } /** * Executes the SQL query. * @param string SQL statement. * @return DibiResult|int result set object (if any) * @throws DibiException */ final public function nativeQuery($sql) { $this->connect(); if ($this->profiler !== NULL) { $event = IDibiProfiler::QUERY; if (preg_match('#\s*(SELECT|UPDATE|INSERT|DELETE)#i', $sql, $matches)) { static $events = array( 'SELECT' => IDibiProfiler::SELECT, 'UPDATE' => IDibiProfiler::UPDATE, 'INSERT' => IDibiProfiler::INSERT, 'DELETE' => IDibiProfiler::DELETE, ); $event = $events[strtoupper($matches[1])]; } $ticket = $this->profiler->before($this, $event, $sql); } dibi::$sql = $sql; if ($res = $this->driver->query($sql)) { // intentionally = $res = new DibiResult($res, $this->config); } else { $res = $this->driver->getAffectedRows(); } if (isset($ticket)) { $this->profiler->after($ticket, $res); } return $res; } /** * Gets the number of affected rows by the last INSERT, UPDATE or DELETE query. * @return int number of rows * @throws DibiException */ public function getAffectedRows() { $rows = $this->driver->getAffectedRows(); if (!is_int($rows) || $rows < 0) throw new DibiException('Cannot retrieve number of affected rows.'); return $rows; } /** * Gets the number of affected rows. Alias for getAffectedRows(). * @return int number of rows * @throws DibiException */ public function affectedRows() { return $this->getAffectedRows(); } /** * Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query. * @param string optional sequence name * @return int * @throws DibiException */ public function getInsertId($sequence = NULL) { $id = $this->driver->getInsertId($sequence); if ($id < 1) throw new DibiException('Cannot retrieve last generated ID.'); return (int) $id; } /** * Retrieves the ID generated for an AUTO_INCREMENT column. Alias for getInsertId(). * @param string optional sequence name * @return int * @throws DibiException */ public function insertId($sequence = NULL) { return $this->getInsertId($sequence); } /** * Begins a transaction (if supported). * @param string optional savepoint name * @return void */ public function begin($savepoint = NULL) { $this->connect(); if ($this->profiler !== NULL) { $ticket = $this->profiler->before($this, IDibiProfiler::BEGIN, $savepoint); } $this->driver->begin($savepoint); if (isset($ticket)) { $this->profiler->after($ticket); } } /** * Commits statements in a transaction. * @param string optional savepoint name * @return void */ public function commit($savepoint = NULL) { if ($this->profiler !== NULL) { $ticket = $this->profiler->before($this, IDibiProfiler::COMMIT, $savepoint); } $this->driver->commit($savepoint); if (isset($ticket)) { $this->profiler->after($ticket); } } /** * Rollback changes in a transaction. * @param string optional savepoint name * @return void */ public function rollback($savepoint = NULL) { if ($this->profiler !== NULL) { $ticket = $this->profiler->before($this, IDibiProfiler::ROLLBACK, $savepoint); } $this->driver->rollback($savepoint); if (isset($ticket)) { $this->profiler->after($ticket); } } /** * Is in transaction? * @return bool */ public function inTransaction() { $this->connect(); return $this->driver->inTransaction(); } /** * Encodes data for use in a SQL statement. * @param string unescaped string * @param string type (dibi::TEXT, dibi::BOOL, ...) * @return string escaped and quoted string * @deprecated */ public function escape($value, $type = dibi::TEXT) { trigger_error('Deprecated: use getDriver()->escape(...) instead.', E_USER_WARNING); $this->connect(); // MySQL & PDO require connection return $this->driver->escape($value, $type); } /** * Decodes data from result set. * @param string value * @param string type (dibi::BINARY) * @return string decoded value * @deprecated */ public function unescape($value, $type = dibi::BINARY) { trigger_error('Deprecated: use getDriver()->unescape(...) instead.', E_USER_WARNING); return $this->driver->unescape($value, $type); } /** * Delimites identifier (table's or column's name, etc.). * @param string identifier * @return string delimited identifier * @deprecated */ public function delimite($value) { trigger_error('Deprecated: use getDriver()->escape(...) instead.', E_USER_WARNING); return $this->driver->escape($value, dibi::IDENTIFIER); } /** * Injects LIMIT/OFFSET to the SQL query. * @param string &$sql The SQL query that will be modified. * @param int $limit * @param int $offset * @return void * @deprecated */ public function applyLimit(&$sql, $limit, $offset) { trigger_error('Deprecated: use getDriver()->applyLimit(...) instead.', E_USER_WARNING); $this->driver->applyLimit($sql, $limit, $offset); } /********************* fluent SQL builders ****************d*g**/ /** * @return DibiFluent */ public function command() { return new DibiFluent($this); } /** * @param string column name * @return DibiFluent */ public function select($args) { $args = func_get_args(); return $this->command()->__call('select', $args); } /** * @param string table * @param array * @return DibiFluent */ public function update($table, $args) { if (!(is_array($args) || $args instanceof ArrayObject)) { throw new InvalidArgumentException('Arguments must be array or ArrayObject.'); } return $this->command()->update('%n', $table)->set($args); } /** * @param string table * @param array * @return DibiFluent */ public function insert($table, $args) { if ($args instanceof ArrayObject) { $args = (array) $args; } elseif (!is_array($args)) { throw new InvalidArgumentException('Arguments must be array or ArrayObject.'); } return $this->command()->insert() ->into('%n', $table, '(%n)', array_keys($args))->values('%l', $args); } /** * @param string table * @return DibiFluent */ public function delete($table) { return $this->command()->delete()->from('%n', $table); } /********************* profiler ****************d*g**/ /** * @param IDibiProfiler * @return DibiConnection provides a fluent interface */ public function setProfiler(IDibiProfiler $profiler = NULL) { $this->profiler = $profiler; return $this; } /** * @return IDibiProfiler */ public function getProfiler() { return $this->profiler; } /********************* shortcuts ****************d*g**/ /** * Executes SQL query and fetch result - shortcut for query() & fetch(). * @param array|mixed one or more arguments * @return DibiRow * @throws DibiException */ public function fetch($args) { $args = func_get_args(); return $this->query($args)->fetch(); } /** * Executes SQL query and fetch results - shortcut for query() & fetchAll(). * @param array|mixed one or more arguments * @return array of DibiRow * @throws DibiException */ public function fetchAll($args) { $args = func_get_args(); return $this->query($args)->fetchAll(); } /** * Executes SQL query and fetch first column - shortcut for query() & fetchSingle(). * @param array|mixed one or more arguments * @return string * @throws DibiException */ public function fetchSingle($args) { $args = func_get_args(); return $this->query($args)->fetchSingle(); } /** * Executes SQL query and fetch pairs - shortcut for query() & fetchPairs(). * @param array|mixed one or more arguments * @return string * @throws DibiException */ public function fetchPairs($args) { $args = func_get_args(); return $this->query($args)->fetchPairs(); } /********************* misc ****************d*g**/ /** * Import SQL dump from file - extreme fast! * @param string filename * @return int count of sql commands */ public function loadFile($file) { $this->connect(); @set_time_limit(0); // intentionally @ $handle = @fopen($file, 'r'); // intentionally @ if (!$handle) { throw new FileNotFoundException("Cannot open file '$file'."); } $count = 0; $sql = ''; while (!feof($handle)) { $s = fgets($handle); $sql .= $s; if (substr(rtrim($s), -1) === ';') { $this->driver->query($sql); $sql = ''; $count++; } } fclose($handle); return $count; } /** * Gets a information about the current database. * @return DibiDatabaseInfo */ public function getDatabaseInfo() { $this->connect(); return new DibiDatabaseInfo($this->driver, isset($this->config['database']) ? $this->config['database'] : NULL); } /** * Prevents unserialization. */ public function __wakeup() { throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); } /** * Prevents serialization. */ public function __sleep() { throw new NotSupportedException('You cannot serialize or unserialize ' . $this->getClass() . ' instances.'); } }