cakephp2-php8/libs/dbo.php

480 lines
12 KiB
PHP
Raw Normal View History

<?PHP
//////////////////////////////////////////////////////////////////////////
// + $Id$
// +------------------------------------------------------------------+ //
// + Cake <https://developers.nextco.com/cake/> + //
// + Copyright: (c) 2005, Cake Authors/Developers + //
// + Author(s): Michal Tatarynowicz aka Pies <tatarynowicz@gmail.com> + //
// + Larry E. Masters aka PhpNut <nut@phpnut.com> + //
// + Kamil Dzielinski aka Brego <brego.dk@gmail.com> + //
// +------------------------------------------------------------------+ //
// + Licensed under The MIT License + //
// + Redistributions of files must retain the above copyright notice. + //
// + See: http://www.opensource.org/licenses/mit-license.php + //
//////////////////////////////////////////////////////////////////////////
/**
* Purpose: DBO/ADO
*
* Description:
* A MySQL functions wrapper. Provides ability to get results as arrays
* and query logging (with execution time).
*
* Example usage:
*
* <code>
* require_once('dbo_mysql.php'); // or 'dbo_postgres.php'
*
* // create and connect the object
* $db = new DBO_MySQL(array( // or 'DBO_Postgres'
* 'host'=>'localhost',
* 'login'=>'username',
* 'password'=>'password',
* 'database'=>'database'));
*
* // read the whole query result array (of rows)
* $all_rows = $db->all("SELECT a,b,c FROM table");
*
* // read the first row with debugging on
* $first_row_only = $db->one("SELECT a,b,c FROM table WHERE a=1", TRUE);
*
* // emulate the usual way of reading query results
* if ($db->query("SELECT a,b,c FROM table")) {
* while ( $row = $db->farr() ) {
* print $row['a'].$row['b'].$row['c'];
* }
* }
*
* // show a log of all queries, sorted by execution time
* $db->showLog(TRUE);
* </code>
*
* @filesource
* @author Cake Authors/Developers
* @copyright Copyright (c) 2005, Cake Authors/Developers
* @link https://developers.nextco.com/cake/wiki/Authors Authors/Developers
* @package cake
* @subpackage cake.libs
* @since Cake v 0.2.9
* @version $Revision$
* @modifiedby $LastChangedBy$
* @lastmodified $Date$
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
*
*/
/**
* Enter description here...
*
*/
uses('object');
/**
* Enter description here...
*
*
* @package cake
* @subpackage cake.libs
* @since Cake v 0.2.9
*
*/
class DBO extends Object {
/**
* Are we connected to the database?
*
* @var boolean
* @access public
*/
var $connected=FALSE;
/**
* Enter description here...
*
* @var boolean
* @access public
*/
var $debug=FALSE;
/**
* Enter description here...
*
* @var boolean
* @access public
*/
var $fullDebug=FALSE;
/**
* Enter description here...
*
* @var unknown_type
* @access public
*/
var $error=NULL;
/**
* String to hold how many rows were affected by the last SQL operation.
*
* @var unknown_type
* @access public
*/
var $affected=NULL;
/**
* Number of rows in current resultset
*
* @var int
* @access public
*/
var $numRows=NULL;
/**
* Time the last query took
*
* @var unknown_type
* @access public
*/
var $took=NULL;
/**
* Enter description here...
*
* @var unknown_type
* @access private
*/
var $_conn=NULL;
/**
* Enter description here...
*
* @var unknown_type
* @access private
*/
var $_result=NULL;
/**
* Queries count.
*
* @var unknown_type
* @access private
*/
var $_queriesCnt=0;
/**
* Total duration of all queries.
*
* @var unknown_type
* @access private
*/
var $_queriesTime=NULL;
/**
* Enter description here...
*
* @var unknown_type
* @access private
*/
var $_queriesLog=array();
/**
* Maximum number of items in query log, to prevent query log taking over
I've merged in Olle's changes. Larry, I've merged in _some_ of your changes, I'll merge in the scaffolding and joins code when you tell me it's ready. But I don't want to break how the Controller class works, can't we really do without the constructClasses() method call? Which reminds me, with your joins code, will we be able to use constructs like $user->post->findAll() and $user->post->save()? Also, what are your changes to the DBO_MySQL class? I mean the mysqlResultSet(), and fetchResult() methods. I didn't see any MySQL-specific code inside them, perhaps they belong to the DBO class itself? - I've changed the headers on user-editable files in /app and /config. I hope they will constitute a compromise between readability and legality. I've left file Id, copyright, and licence notices. - /libs/basic.php::uses() function logs included files in global $loaded. Please, consider it a note to myself. Also, I've moved the NeatArray class out of the /libs/basics.php (into /libs/neat_array.php). - Some cleanups in the Controller and Dispatcher classes. - DBO::Prepare() accepts strings _and_ arrays now. It's a step towards a unified params theory. - I think I've added some comments to DBO sub-classes, but it might have been Olle. - A fix in Model class (findAll didn't work properly) - Object's constructor sets $this->db to &DBO, which means all Object-descendand classes have default access to the database if it's connected. We need to clean up the code accordingly (some classes set their own $this->db references). git-svn-id: https://svn.cakephp.org/repo/trunk/cake@236 3807eeeb-6ff5-0310-8944-8be069107fe0
2005-06-05 11:05:24 +00:00
* too much memory on large amounts of queries -- I we've had problems at
* >6000 queries on one system.
*
* @var int Maximum number of queries in the queries log.
* @access private
*/
var $_queriesLogMax=200;
// specific for each database, implemented in db drivers
function connect ($config) {
die('Please implement DBO::connect() first.');
}
function disconnect () {
die('Please implement DBO::disconnect() first.');
}
function execute ($sql) {
die('Please implement DBO::execute() first.');
}
function fetchRow ($result) {
die('Please implement DBO::fetchRow() first.');
}
function tablesList() {
die('Please implement DBO::tables() first.');
}
function fields ($table_name) {
die('Please implement DBO::fields() first.');
}
function prepareField ($data) {
die('Please implement DBO::prepareField() first.');
}
function lastError ($result) {
die('Please implement DBO::lastError() first.');
}
function lastAffected () {
die('Please implement DBO::lastAffected() first.');
}
function lastNumRows ($result) {
die('Please implement DBO::lastNumRows() first.');
}
function lastInsertId () {
die('Please implement DBO::lastInsertId() first.');
}
/**
* Constructor. Sets the level of debug for dbo (fullDebug or debug).
*
* @param array $config
* @return unknown
*/
function __construct ($config=NULL) {
$this->debug = DEBUG > 0;
$this->fullDebug = DEBUG > 1;
parent::__construct();
return $this->connect($config);
}
/**
* Destructor. Closes connection to the database.
*
*/
function __destructor () {
$this->close();
}
/**
* Returns a string with a USE [databasename] SQL statement.
*
* @param string $db_name Name of database to use
* @return unknown Result of the query
*/
function useDb ($db_name) {
return $this->query("USE {$db_name}");
}
/**
* Disconnects database, kills the connection and says the connection is closed, and if DEBUG is turned on, the log for this object is shown.
*
*/
function close () {
if ($this->fullDebug) $this->showLog();
$this->disconnect();
$this->_conn = NULL;
$this->connected = false;
}
I've merged in Olle's changes. Larry, I've merged in _some_ of your changes, I'll merge in the scaffolding and joins code when you tell me it's ready. But I don't want to break how the Controller class works, can't we really do without the constructClasses() method call? Which reminds me, with your joins code, will we be able to use constructs like $user->post->findAll() and $user->post->save()? Also, what are your changes to the DBO_MySQL class? I mean the mysqlResultSet(), and fetchResult() methods. I didn't see any MySQL-specific code inside them, perhaps they belong to the DBO class itself? - I've changed the headers on user-editable files in /app and /config. I hope they will constitute a compromise between readability and legality. I've left file Id, copyright, and licence notices. - /libs/basic.php::uses() function logs included files in global $loaded. Please, consider it a note to myself. Also, I've moved the NeatArray class out of the /libs/basics.php (into /libs/neat_array.php). - Some cleanups in the Controller and Dispatcher classes. - DBO::Prepare() accepts strings _and_ arrays now. It's a step towards a unified params theory. - I think I've added some comments to DBO sub-classes, but it might have been Olle. - A fix in Model class (findAll didn't work properly) - Object's constructor sets $this->db to &DBO, which means all Object-descendand classes have default access to the database if it's connected. We need to clean up the code accordingly (some classes set their own $this->db references). git-svn-id: https://svn.cakephp.org/repo/trunk/cake@236 3807eeeb-6ff5-0310-8944-8be069107fe0
2005-06-05 11:05:24 +00:00
/**
* Prepares a value, or an array of values for database queries by quoting and escaping them.
*
* @param mixed $data A value or an array of values to prepare.
* @return mixed Prepared value or array of values.
*/
function prepare ($data)
{
if (is_array($data))
{
$out = null;
foreach ($data as $key=>$item)
{
$out[$key] = $this->prepareValue($item);
}
return $out;
I've merged in Olle's changes. Larry, I've merged in _some_ of your changes, I'll merge in the scaffolding and joins code when you tell me it's ready. But I don't want to break how the Controller class works, can't we really do without the constructClasses() method call? Which reminds me, with your joins code, will we be able to use constructs like $user->post->findAll() and $user->post->save()? Also, what are your changes to the DBO_MySQL class? I mean the mysqlResultSet(), and fetchResult() methods. I didn't see any MySQL-specific code inside them, perhaps they belong to the DBO class itself? - I've changed the headers on user-editable files in /app and /config. I hope they will constitute a compromise between readability and legality. I've left file Id, copyright, and licence notices. - /libs/basic.php::uses() function logs included files in global $loaded. Please, consider it a note to myself. Also, I've moved the NeatArray class out of the /libs/basics.php (into /libs/neat_array.php). - Some cleanups in the Controller and Dispatcher classes. - DBO::Prepare() accepts strings _and_ arrays now. It's a step towards a unified params theory. - I think I've added some comments to DBO sub-classes, but it might have been Olle. - A fix in Model class (findAll didn't work properly) - Object's constructor sets $this->db to &DBO, which means all Object-descendand classes have default access to the database if it's connected. We need to clean up the code accordingly (some classes set their own $this->db references). git-svn-id: https://svn.cakephp.org/repo/trunk/cake@236 3807eeeb-6ff5-0310-8944-8be069107fe0
2005-06-05 11:05:24 +00:00
}
else
{
return $this->prepareValue($data);
}
I've merged in Olle's changes. Larry, I've merged in _some_ of your changes, I'll merge in the scaffolding and joins code when you tell me it's ready. But I don't want to break how the Controller class works, can't we really do without the constructClasses() method call? Which reminds me, with your joins code, will we be able to use constructs like $user->post->findAll() and $user->post->save()? Also, what are your changes to the DBO_MySQL class? I mean the mysqlResultSet(), and fetchResult() methods. I didn't see any MySQL-specific code inside them, perhaps they belong to the DBO class itself? - I've changed the headers on user-editable files in /app and /config. I hope they will constitute a compromise between readability and legality. I've left file Id, copyright, and licence notices. - /libs/basic.php::uses() function logs included files in global $loaded. Please, consider it a note to myself. Also, I've moved the NeatArray class out of the /libs/basics.php (into /libs/neat_array.php). - Some cleanups in the Controller and Dispatcher classes. - DBO::Prepare() accepts strings _and_ arrays now. It's a step towards a unified params theory. - I think I've added some comments to DBO sub-classes, but it might have been Olle. - A fix in Model class (findAll didn't work properly) - Object's constructor sets $this->db to &DBO, which means all Object-descendand classes have default access to the database if it's connected. We need to clean up the code accordingly (some classes set their own $this->db references). git-svn-id: https://svn.cakephp.org/repo/trunk/cake@236 3807eeeb-6ff5-0310-8944-8be069107fe0
2005-06-05 11:05:24 +00:00
}
function tables()
{
return array_map('strtolower', $this->tablesList());
}
/**
* Executes given SQL statement.
*
* @param string $sql SQL statement
* @return unknown
*/
function rawQuery ($sql) {
$this->took = $this->error = $this->numRows = false;
return $this->execute($sql);
}
/**
* Queries the database with given SQL statement, and obtains some metadata about the result
* (rows affected, timing, any errors, number of rows in resultset). The query is also logged.
* If DEBUG is set, the log is shown all the time, else it is only shown on errors.
*
* @param string $sql
* @return unknown
*/
function query($sql)
{
$t = getMicrotime();
$this->_result = $this->execute($sql);
$this->affected = $this->lastAffected();
$this->took = round((getMicrotime()-$t)*1000, 0);
$this->error = $this->lastError();
$this->numRows = $this->lastNumRows($this->_result);
$this->logQuery($sql);
if (($this->debug && $this->error) || ($this->fullDebug))
$this->showQuery($sql);
return $this->error? false: $this->_result;
}
/**
* Returns a single row of results from the _last_ SQL query.
*
* @param resource $res
* @return array A single row of results
*/
function farr ($res=false, $assoc=false) {
return $this->fetchRow($res? $res: $this->_result, $assoc);
}
/**
* Returns a single row of results for a _given_ SQL query.
*
* @param string $sql SQL statement
* @return array A single row of results
*/
function one ($sql) {
return $this->query($sql)? $this->farr(): false;
}
/**
* Returns an array of all result rows for a given SQL query.
* Returns false if no rows matched.
*
* @param string $sql SQL statement
* @return array Array of resultset rows, or false if no rows matched
*/
function all ($sql) {
if($this->query($sql)) {
$out=array();
while ($item = $this->farr(null, true))
{
$out[] = $item;
}
return $out;
}
else {
return false;
}
}
/**
* Returns a single field of the first of query results for a given SQL query, or false if empty.
*
* @param string $name Name of the field
* @param string $sql SQL query
* @return unknown
*/
function field ($name, $sql) {
$data = $this->one($sql);
return empty($data[$name])? false: $data[$name];
}
/**
* Checks if the specified table contains any record matching specified SQL
*
* @param string $table Name of table to look in
* @param string $sql SQL WHERE clause (condition only, not the "WHERE" part)
* @return boolean True if the table has a matching record, else false
*/
function hasAny($table, $sql) {
$out = $this->one("SELECT COUNT(*) AS count FROM {$table}".($sql? " WHERE {$sql}":""));
return is_array($out)? $out['count']: false;
}
/**
* Checks if it's connected to the database
*
* @return boolean True if the database is connected, else false
*/
function isConnected() {
return $this->connected;
}
/**
* Outputs the contents of the log.
*
* @param boolean $sorted
*/
function showLog($sorted=false) {
$log = $sorted?
sortByKey($this->_queriesLog, 'took', 'desc', SORT_NUMERIC):
$this->_queriesLog;
print("<table border=1>\n<tr><th colspan=7>{$this->_queriesCnt} queries took {$this->_queriesTime} ms</th></tr>\n");
print("<tr><td>Nr</td><td>Query</td><td>Error</td><td>Affected</td><td>Num. rows</td><td>Took (ms)</td></tr>\n");
foreach($log AS $k=>$i) {
print("<tr><td>".($k+1)."</td><td>{$i['query']}</td><td>{$i['error']}</td><td align='right'>{$i['affected']}</td><td align='right'>{$i['numRows']}</td><td align='right'>{$i['took']}</td></tr>\n");
}
print("</table>\n");
}
/**
* Log given SQL query.
*
* @param string $sql SQL statement
*/
function logQuery($sql) {
$this->_queriesCnt++;
$this->_queriesTime += $this->took;
$this->_queriesLog[] = array(
'query'=>$sql,
'error'=>$this->error,
'affected'=>$this->affected,
'numRows'=>$this->numRows,
'took'=>$this->took
);
if (count($this->_queriesLog) > $this->_queriesLogMax) {
array_pop($this->_queriesLog);
}
if ($this->error)
false; // shouldn't we be logging errors somehow?
}
/**
* Output information about an SQL query. The SQL statement, number of rows in resultset,
* and execution time in microseconds. If the query fails, and error is output instead.
*
* @param string $sql
*/
function showQuery($sql) {
$error = $this->error;
if (strlen($sql)>200 && !$this->fullDebug)
$sql = substr($sql, 0, 200) .'[...]';
if ($this->debug || $error) {
print("<p style=\"text-align:left\"><b>Query:</b> {$sql} <small>[Aff:{$this->affected} Num:{$this->numRows} Took:{$this->took}ms]</small>");
if($error) {
print("<br /><span style=\"color:Red;text-align:left\"><b>ERROR:</b> {$this->error}</span>");
}
print('</p>');
}
}
}
?>