silverstripe-framework/model/connect/DBConnector.php
Damian Mooyman d8e9af8af8 API New Database abstraction layer. Ticket #7429
Database abstraction broken up into controller, connector, query builder, and schema manager, each independently configurable via YAML / Injector
Creation of new DBQueryGenerator for database specific generation of SQL
Support for parameterised queries, move of code base to use these over escaped conditions
Refactor of SQLQuery into separate query classes for each of INSERT UPDATE DELETE and SELECT
Support for PDO
Installation process upgraded to use new ORM
SS_DatabaseException created to handle database errors, maintaining details of raw sql and parameter details for user code designed interested in that data.
Renamed DB static methods to conform correctly to naming conventions (e.g. DB::getConn -> DB::get_conn)
3.2 upgrade docs
Performance Optimisation and simplification of code to use more concise API
API Ability for database adapters to register extensions to ConfigureFromEnv.php
2014-07-09 18:04:05 +12:00

249 lines
7.8 KiB
PHP

<?php
/**
* Represents an object responsible for wrapping DB connector api
*
* @package framework
* @subpackage model
*/
abstract class DBConnector {
/**
* List of operations to treat as write
*
* @config
* @var array
*/
private static $write_operations = array('insert', 'update', 'delete', 'replace', 'alter', 'drop');
/**
* Error handler for database errors.
* All database errors will call this function to report the error. It isn't a static function;
* it will be called on the object itself and as such can be overridden in a subclass.
* Subclasses should run all errors through this function.
*
* @todo hook this into a more well-structured error handling system.
* @param string $msg The error message.
* @param integer $errorLevel The level of the error to throw.
* @param string $sql The SQL related to this query
* @param array $parameters Parameters passed to the query
*/
protected function databaseError($msg, $errorLevel = E_USER_ERROR, $sql = null, $parameters = array()) {
// Prevent errors when error checking is set at zero level
if(empty($errorLevel)) return;
// Format query if given
if (!empty($sql)) {
$formatter = new SQLFormatter();
$formattedSQL = $formatter->formatPlain($sql);
$msg = "Couldn't run query:\n\n{$formattedSQL}\n\n{$msg}";
}
if($errorLevel === E_USER_ERROR) {
// Treating errors as exceptions better allows for responding to errors
// in code, such as credential checking during installation
throw new SS_DatabaseException($msg, 0, null, $sql, $parameters);
} else {
user_error($msg, $errorLevel);
}
}
/**
* Determines if the query should be previewed, and thus interrupted silently.
* If so, this function also displays the query via the debuging system.
* Subclasess should respect the results of this call for each query, and not
* execute any queries that generate a true response.
*
* @param string $sql The query to be executed
* @return boolean Flag indicating that the query was previewed
*/
protected function previewWrite($sql) {
// Break if not requested
if (!isset($_REQUEST['previewwrite'])) return false;
// Break if non-write operation
$operation = strtolower(substr($sql, 0, strpos($sql, ' ')));
$writeOperations = Config::inst()->get(get_class($this), 'write_operations');
if (!in_array($operation, $writeOperations)) {
return false;
}
// output preview message
Debug::message("Will execute: $sql");
return true;
}
/**
* Allows the display and benchmarking of queries as they are being run
*
* @param string $sql Query to run, and single parameter to callback
* @param callable $callback Callback to execute code
* @return mixed Result of query
*/
protected function benchmarkQuery($sql, $callback) {
if (isset($_REQUEST['showqueries']) && Director::isDev(true)) {
$starttime = microtime(true);
$result = $callback($sql);
$endtime = round(microtime(true) - $starttime, 4);
Debug::message("\n$sql\n{$endtime}ms\n", false);
return $result;
} else {
return $callback($sql);
}
}
/**
* Extracts only the parameter values for error reporting
*
* @param array $parameters
* @return array List of parameter values
*/
protected function parameterValues($parameters) {
$values = array();
foreach($parameters as $value) {
$values[] = is_array($value) ? $value['value'] : $value;
}
return $values;
}
/**
* Link this connector to the database given the specified parameters
* Will throw an exception rather than return a success state.
* The connector should not select the database once connected until
* explicitly called by selectDatabase()
*
* @param array $parameters List of parameters such as
* <ul>
* <li>type</li>
* <li>server</li>
* <li>username</li>
* <li>password</li>
* <li>database</li>
* <li>path</li>
* </ul>
* @param boolean $selectDB By default database selection should be
* handled by the database controller (to enable database creation on the
* fly if necessary), but some interfaces require that the database is
* specified during connection (SQLite, Azure, etc).
*/
abstract public function connect($parameters, $selectDB = false);
/**
* Query for the version of the currently connected database
*
* @return string Version of this database
*/
abstract public function getVersion();
/**
* Given a value escape this for use in a query for the current database
* connector. Note that this does not quote the value.
*
* @param string $value The value to be escaped
* @return string The appropritaely escaped string for value
*/
abstract public function escapeString($value);
/**
* Given a value escape and quote this appropriately for the current
* database connector.
*
* @param string $value The value to be injected into a query
* @return string The appropriately escaped and quoted string for $value
*/
abstract public function quoteString($value);
/**
* Escapes an identifier (table / database name). Typically the value
* is simply double quoted. Don't pass in already escaped identifiers in,
* as this will double escape the value!
*
* @param string $value The identifier to escape
* @param string $separator optional identifier splitter
*/
public function escapeIdentifier($value, $separator = '.') {
// ANSI standard id escape is to surround with double quotes
if(empty($separator)) return '"'.trim($value).'"';
// Split, escape, and glue back multiple identifiers
$segments = array();
foreach(explode($separator, $value) as $item) {
$segments[] = $this->escapeIdentifier($item, null);
}
return implode($separator, $segments);
}
/**
* Executes the following query with the specified error level.
* Implementations of this function should respect previewWrite and benchmarkQuery
*
* @see http://php.net/manual/en/errorfunc.constants.php
* @param string $sql The SQL query to execute
* @param integer $errorLevel For errors to this query, raise PHP errors
* using this error level.
*/
abstract public function query($sql, $errorLevel = E_USER_ERROR);
/**
* Execute the given SQL parameterised query with the specified arguments
*
* @param string $sql The SQL query to execute. The ? character will denote parameters.
* @param array $parameters An ordered list of arguments.
* @param int $errorLevel The level of error reporting to enable for the query
* @return SS_Query
*/
abstract public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR);
/**
* Select a database by name
*
* @param string $name Name of database
* @return boolean Flag indicating success
*/
abstract public function selectDatabase($name);
/**
* Retrieves the name of the currently selected database
*
* @return string Name of the database, or null if none selected
*/
abstract public function getSelectedDatabase();
/**
* De-selects the currently selected database
*/
abstract public function unloadDatabase();
/**
* Retrieves the last error generated from the database connection
*
* @return string The error message
*/
abstract public function getLastError();
/**
* Determines the last ID generated from the specified table.
* Note that some connectors may not be able to return $table specific responses,
* and this parameter may be ignored.
*
* @param string $table The target table to return the last generated ID for
* @return integer ID value
*/
abstract public function getGeneratedID($table);
/**
* Determines the number of affected rows from the last SQL query
*
* @return integer Number of affected rows
*/
abstract public function affectedRows();
/**
* Determines if we are connected to a server AND have a valid database
* selected.
*
* @return boolean Flag indicating that a valid database is connected
*/
abstract public function isActive();
}