Source for file ado.php
Documentation is available at ado.php
// +----------------------------------------------------------------------+
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2003 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Alexios Fakos (alexios@php.net) |
// +----------------------------------------------------------------------+
// $Id: ado.php,v 1.4 2003/02/24 18:37:16 alexios Exp $
// "dbsyntax" => "access", // "mssql" or "odbc"
// "username" => "Admin",
// "database" => "Provider=Microsoft.Jet.OLEDB.4.0;
// Data Source=C:\\Programs\\Microsoft Office\\Office\\Samples\\Nordwind.mdb;
// Persist Security Info=False"
// $conn = DB::connect($dsn);
* Database independent query interface definition for Microsoft's ADODB
* library using PHP's COM extension
* @author Alexios Fakos <alexios@php.net>
* @version $Revision: 1.4 $
include_once(dirname (__FILE__ ) . '/ado_constants.php');
require_once ('DB/common.php');
class DB_ado extends DB_common
* Points on ADODB.Connection
* @var object ADODB.Connection (COM)
* @see connect(), simpleQuery(), disconnect()
* Database backend used in PHP (mysql, odbc etc.)
* @see connect(), toString()
* Database used with regards to SQL syntax etc.
* @todo defining special OLEDB-Provider (mssql, access, odbc)
* @see connect(), createSequence(), toString()
* Flag for method commit(), default no commit on every query
* @var boolean default true
* @see autoCommit(), adoTrans(), commit(), rollback()
* Flag to see how often a commit was started, Default no commit
* @var boolean default true
* @see autoCommit(), simpleQuery(), rollback()
var $transaction_opcount = 0; // flag
* Result of affected rows
* @see affectedRows(), simpleQuery()
* Points on ADODB.Recordset
* @var object ADODB.Recordset (COM)
* @see simpleQuery(), disconnect()
* Specific max records to be retreived on execution.
* Default value 0 stays for return all records.
* Specific option for ADODB.Connection and ADODB.Recordset.
* Valid values are: -1 || 1 || 2 || 4 || 8 || 256 || 512
* @var integer default -1
* @see setExecuteOption()
var $_execute_option = -1;
* Specific cursor location.
* Valid values are: 2 || 3
* @see setCursorLocation()
var $_cursor_location = adUseClient; // 3;
* Valid values are: -1 || 0 || 1 || 2 || 3
var $_cursor_type = adOpenStatic; //3;
* Valid values are: -1 || 0 || 1 || 2 || 3 || 4
* @var integer default -1
////////////////////////////////////////////// ///////////////////////////
////////////////////////////////////////////// ///////////////////////////
* http://support.microsoft.com/default.aspx?scid=kb;EN-US;q168354
* @see DB::common(), $dbsyntax, $phptype
$this->errorcode_map = array (
-2147483647 => DB_ERROR_UNSUPPORTED ,
-2147467263 => DB_ERROR_UNSUPPORTED ,
-2147467259 => DB_ERROR_UNSUPPORTED ,
-2147217865 => DB_ERROR_NOSUCHTABLE ,
-2147217900 => DB_ERROR_NOSUCHFIELD ,
2147749392 => DB_ERROR_NOSUCHFIELD ,
-2147217857 => DB_ERROR_ALREADY_EXISTS ,
-2147217843 => DB_ERROR_CONNECT_FAILED
$this->affected = new VARIANT ();
* Connect to a database and log in as the specified user.
* @param $dsn the data source name (see DB::parseDSN for syntax)
* @param $persistent (optional) whether the connection should be
* persistent (actually not supported persistent
* @return mixed DB_OK on success or DB_Error on failure
* @see adoIsError(), adoRaiseError(), raiseError()
function connect ($dsninfo, $persistent = false )
return $this->raiseError (DB_ERROR_EXTENSION_NOT_FOUND , null ,
null , null , 'This class runs only on Windows OS');
$connstr = (string) $dsninfo['database'];
$user = (string) $dsninfo['username'];
$pw = (string) $dsninfo['password'];
$this->connection = new COM ("ADODB.Connection");
if (!$this->connection) {
$errMsg = 'Could not create an instance of ADODB.Connection.\n';
$errMsg .= 'Check if you have installed MDAC on your machine.\n';
$errMsg .= 'Take a look at ';
$errMsg .= 'http://www.microsoft.com/data/download.htm';
return $this->raiseError (DB_ERROR_CONNECT_FAILED , null ,
//check some valid link properties
$link = 'PROVIDER=|DRIVER=|DATA+SOURCE=|PERSIST+SECURITY+INFO=|UID=|';
$link .= 'USER+ID=|PASSWORD=|PWD=|INITIAL+CATALOG=';
if (!empty ($connstr) && preg_match('/\b\s*('. $link. ')\b/i', $connstr)) {
@$this->connection->Open ($connstr, $user, $pw);
if (!$this->connection || $this->adoIsError ()) {
return $this->adoRaiseErrorEx (DB_ERROR_CONNECT_FAILED );
return $this->adoRaiseErrorEx (DB_ERROR_INVALID_DSN );
* Close ADODB.Recordset, ADODB.Connection and delete vars to free memory.
* @return boolean always TRUE
* @see $connection, $recordset
@$this->recordset->Close ();
@$this->connection->Close ();
$this->connection = null;
* Sending a query through ADODB.Connection and recieve ADO.Recordset
* For manip queries we use ADODB.Connection execute method instead of
* ADO.Recordset open method.
* @param string $query sql statement
* @return mixed object ADODB.Recordset or DB_Error on failure
* @see $connection, $recordset, $_max_records, $_cursor_type,
* $_lock_type, $_execute_option, $transaction_opcount,
function simpleQuery ($query)
$ismanip = DB ::isManip ($query);
$this->last_query = $query;
$query = $this->modifyQuery ($query);
if (!$this->autocommit && $ismanip) {
// transaction supported?
$this->transaction_opcount++;
$this->recordset = @$this->connection->Execute ($query,
$this->recordset = new COM ('ADODB.Recordset');
$errMsg = 'Creating an instance of ADODB.Recordset ';
return $this->raiseError (DB_ERROR_EXTENSION_NOT_FOUND ,
null , null , null , $errMsg);
// close other open recordset to open a new one
@$this->recordset->Close ();
$this->recordset->MaxRecords = $this->_max_records;
@$this->recordset->Open ($query, $this->connection, $this->_cursor_type,
$this->_lock_type, $this->_execute_option);
if ($this->adoIsError ()) {
return $this->adoRaiseErrorEx ($this->errorNative ());
* Move the internal ADODB.Recordset result pointer to the next
* @param object (reference) ADODB.Recordset
function nextResult ($result)
* Fetch and return a row of current ADODB.Recordset.
* Internally we do some important transformations for right php
* Take a look at ADODB.DataTypeEnum for details.
* @param object $result ADODB.Recordset
* @param array $arr (reference) where data from the row is stored
* @param integer $fetchmode how the array data should be indexed
* @param integer $rownum the row number to fetch
* @return mixed DB_OK on success, NULL on no more rows
* @todo BINARY DATA handling
function fetchInto ($result, &$arr, $fetchmode, $rownum=null )
if ($rownum !== null && !@$result->EOF ()) {
$this->pushErrorHandling (PEAR_ERROR_RETURN );
// adBookmarkFirst, start at the first record
@$result->Move ($rownum, 1 );
$this->popErrorHandling ();
if ($this->adoIsError ()) {
return $this->adoRaiseErrorEx ();
$count = $this->numCols ($result);
for($i = 0; $i < $count; $i++ ) {
$field = $result->Fields ($i);
// avoiding 1970-01-01 01:00:00 on date values if
// $fvalue is null or < 0
if ($this->isTypeOfCurrency ($type)) {
$value = (float) $fvalue;
// adDate == 7 + adDBDate DBTYPE_DBDATE == 133
} elseif ($this->isTypeOfDate ($type)) {
$value = date('Y-m-d', (integer) $fvalue);
// adDBTime DBTYPE_DBTIME == 134
} elseif ($this->isTypeOfTime ($type)) {
$value = date('H:i:s', (integer) $fvalue);
// adDBTimeStamp DBTYPE_DBTIMESTAMP == 135
} elseif ($this->isTypeOfTimestamp ($type)) {
$value = date('Y-m-d H:i:s', (integer) $fvalue);
} elseif ($this->isTypeOfBinary ($type)) {
foreach ($fvalue as $value) {
$value .= pack('C', $value);
if ($fetchmode !== DB_FETCHMODE_ASSOC ) {
$arr[$field->Name ] = $value;
* Get the number of columns in a recordset.
* @param object $result ADODB.Recordset
* @return integer the number of columns per row in $result
function numCols ($result)
$cols = @$result->Fields ->Count ();
return ($cols !== -1 ) ? $cols : 0;
* Get the number of rows in a ADODB.Recordset.
* Note: If cursor type supports does not support RecordCount,
* the result is always adUnknown = -1.
* @param object $result ADODB.Recordset
* @return integer number of rows in ADODB.Recordset
function numRows ($result)
$count = @$result->RecordCount ();
return ($count !== -1 ) ? $count : 0;
* Gets the number of rows affected by the last manip query.
* @return integer number of rows affected by the last query
return $this->affected->value;
* Get the next value in a sequence. Depends on $dbsyntax which type
* @param $seq_name the name of the sequence
* @param $ondemand whether to create the sequence table on
* demand (default is true)
* @return mixed a sequence integer or DB_Error
function nextId ($seq_name, $ondemand = true )
$this->pushErrorHandling (PEAR_ERROR_RETURN );
$rs = $this->query (" UPDATE ${sqn}_seq SET id = id + 1" );
$this->popErrorHandling ();
if ($ondemand && DB ::isError ($rs) &&
$this->errorCode ($rs->getCode ()) == DB_ERROR_NOSUCHTABLE ) {
$rs = $this->createSequence ($seq_name);
$rs = $this->getOne (" SELECT MAX(id) FROM ${sqn}_seq" );
return $this->raiseError ($result);
function createSequence ($seq_name)
if ($this->_tableExists (" ${sqn}_seq" )) {
$ftype = $this->_helpCreateSequence ();
$rs = $this->query (" CREATE TABLE ${sqn}_seq " . $ftype);
$rs = $this->query (" INSERT INTO ${sqn}_seq (id) VALUES(0)" );
function dropSequence ($seq_name)
// close recordset first to avoid table is locked
if (@$this->recordset->State != 0 ) {
@$this->recordset->Close ();
return $this->query (" DROP TABLE ${sqn}_seq" );
* Free the internal resources associated with $result
* (basicly it means close recordset)
* @param object $result ADODB.Recordset
* @return boolean always TRUE
function freeResult ($result)
if (@$result->State != 0 ) {
$this->affected->value = 0;
$this->transaction_opcount = 0;
* Enable automatic commit.
* @param boolean $onoff (optional) default false
* @return boolean always TRUE
function autoCommit ($onoff = false )
$this->autocommit = $onoff ? true : false;
* Checking of transaction support by OLEDB-Provider.
* Normally the startpoint of an transaction will be set here
* @return mixed TRUE on success, FALSE if transaction is not
* @see $autocommit, $transaction_opcount
$ret = $this->connection->Properties ('Transaction DDL');
if ($this->transaction_opcount == 0 ) {
@$this->connection->BeginTrans ();
if ($this->adoIsError ()) {
return $this->adoRaiseErrorEx ();
* @return boolean TRUE on success
if ($this->transaction_opcount > 0 ) {
@$this->connection->CommitTrans ();
if ($this->adoIsError ()) {
return $this->adoRaiseErrorEx ();
$this->transaction_opcount = 0;
* @return boolean TRUE on success
if ($this->transaction_opcount > 0 ) {
@$this->connection->RollbackTrans ();
if ($this->adoIsError ()) {
return $this->adoRaiseErrorEx ();
$this->transaction_opcount = 0;
* Get the extended error collection of the current ADODB.Connection
* @return string ADODB.Error
$errors = $this->connection->Errors ();
if ($errors->Count () == 0 ) {
return DB_ERROR_NOT_CAPABLE;
$count = $errors->Count ();
for($i = 0; $i < $count; $i++ ) {
$item = $errors->Item ($i);
$msg = $item->Description;
$native = $item->NativeError;
$ret .= " Source: $source - Description: $msg - SQLState: ";
$ret .= " $sql - Number: $nr - Native: $native \n";
* Get the native error code of the last error (if any) that
* occured on the current ADODB.Connection
* @return string ADODB.Error
$errors = $this->connection->Errors ();
if ($errors->Count () == 0 ) {
return DB_ERROR_NOT_CAPABLE;
$item = $errors->Item (0 );
$ret = (int) $item->Number;
* Raise an error and set as param nativecode on raiseError()
* result-string of errorNative()
* @param integer $errno (optional) default null
* @see errorNative(), raiseError()
function adoRaiseError ($errno = null )
return $this->raiseError ($errno, null , null , null ,
* Raise an error and set as param nativecode on raiseError()
* result-string of errorNative()
* @param integer $errno (optional) default null
* @see errorNativeEx(), raiseError()
function adoRaiseErrorEx ($errno = null )
$errno = $this->errorCode ($errno);
return $this->raiseError ($errno, null , null , null ,
* Checking if an ADODB.Connection error occured
* @return boolean TRUE error(s) occured,
if ($this->connection->Errors ->Count () > 0 &&
* Set execute option for ADODB.Connection. Only for manip queries.
* @param integer $value (optional) default -1
* @return boolean TRUE we set the $value, FALSE wrong Enum was
function setExecuteOption ($value = -1 )
if ($value > 0 && $value < 3 ) {
} elseif ($value == -1 ) {
} elseif ($value == 256 ) {
} elseif ($value == 512 ) {
$this->_execute_option = $value;
* Set the cursor type for ADODB.Recordset.
* Only for catching records.
* Use adOpenKeyset = 1 or adOpenstatic = 3 to get a
* value > 0 of numRows().
* @param integer $value (optional) default -1
* @return boolean TRUE we set the $value,
* FALSE wrong Enum was given
* @see numRows(), $_cursor_type
function setCursorType ($value = -1 )
if ($value >= 0 || $value < 4 ) {
$this->_cursor_type = $value;
* Set the cursor location for ADODB.Connection
* @param integer $value (optional) default 3
* @return boolean TRUE we set the $value,
* FALSE wrong Enum was given
function setCursorLocation ($value = 3 )
if ($value > 1 && $value < 4 ) {
$this->_cursor_location = $value;
* Set the lock type for ADODB.Recordset. Only for catching records.
* adLockBatchOptimistic = 4
* @param integer $value (optional) default -1
* @return boolean TRUE we set the $value,
* FALSE wrong Enum was given
function setLockType ($value = -1 )
if ($value > 0 || $value < 5 ) {
$this->_lock_type = $value;
* Setting of MaxRecords to get only xx records on every query
* Note: msdn-article (Q186267)
* "PRB: MaxRecords Property Is Not Used in Access Queries with ADO"
* @param integer $value (optional) default 0
* @return boolean TRUE we set the $value,
* FALSE no integer value was given
function setMaxRecords ($value = 0 )
$this->_max_records = $value;
* Help function to get string of field declaration
* @return string fieldtype and conditions
function _helpCreateSequence ()
if ($dbsyntax == 'access') {
$ftype = '(id LONG NOT NULL, PRIMARY KEY(id))';
$ftype = '(id BIGINT NOT NULL, PRIMARY KEY(id))';
* Help function to know if table exists
* param string $value tablename
* @return boolean TRUE table exists, FALSE table does not exist
function _tableExists ($value)
$cat = new COM ('ADOX.Catalog');
// @$cat->ActiveConnection = $this->connection;
// can use it in this way, i get always a seq fault of php.exe,
// maybe a bug reported bug-id #16720
// so we use an alternative way ...
@$cat->ActiveConnection = $this->connection->ConnectionString;
$count = $tables->Count ();
for($i = 0; $i < $count; $i++ ) {
$table = $tables->Item ($i);
function tableInfo ($result, $mode = null ) {
// if $result is a string, then we want information about a
// table without a resultset
$sql = $this->last_query;
$table_name = strtolower($this->_getTableNameFromSQL ($sql));
if (empty ($table_name)) {
return $this->raiseError (DB_ERROR_INVALID , null , null ,
null , 'Empty table name in tableInfo() ' . __LINE__ );
$cat = new COM ('ADOX.Catalog');
@$cat->ActiveConnection = $this->connection->ConnectionString;
$count = $tables->Count ();
$res['num_fields']= $count;
for($i = 0; $i < $count; $i++ ) {
$table = $tables->Item ($i);
for($x = 0; $x < $table->Columns ->Count (); $x++ ) {
$column = $table->Columns ->Item ($x);
$res[$x]['table']= (string) @$table->Name;
$res[$x]['name'] = (string) @$column->Name;
$type = $this->_getStringOfADOType ($column->Type );
$res[$x]['type'] = (string) $type;
$res[$x]['len'] = (string) @$column->DefinedSize;
$flags = $this->_getFlags4TableInfo ($column);
$res[$x]['flags']= (string) $flags;
if ($mode & DB_TABLEINFO_ORDER ) {
$res['order'][$res[$x]['name']] = $x;
if ($mode & DB_TABLEINFO_ORDERTABLE ) {
$res['ordertable'][$res[$x]['table']][$res[$x]['name']] = $x;
// todo: transforming ret int-value to string
* Get MS ADODB integer value of field type
* @param string $value integer value of ADODB field type
* @return string human string
function _getStringOfADOType ($value)
if ($this->isTypeOfBinary ($value)) {
} elseif ($this->isTypeOfBit ($value)) {
} elseif ($this->isTypeOfDecimal ($value)) {
} elseif ($this->isTypeOfNumeric ($value)) {
} elseif ($this->isTypeOfDouble ($value)) {
} elseif ($this->isTypeOfFloat ($value)) {
} elseif ($this->isTypeOfReal ($value)) {
} elseif ($this->isTypeOfCurrency ($value)) {
} elseif ($this->isTypeOfInteger ($value)) {
} elseif ($this->isTypeOfTime ($value)) {
} elseif ($this->isTypeOfTimestamp ($value)) {
} elseif ($this->isTypeOfDate ($value)) {
* Receiving of the table name in a SQL query string
* @param string $value SQL query string
* @return string table name in the SQL query
function _getTableNameFromSQL ($value)
$from_part = stristr($sql, 'from');
$from_array = split(' ', $from_part);
return (string) $from_array[1 ];
* Transform MS ADODB Enum into php readable string
* @param object $column column item
* @return string empty string or the name type of column
function _getFlags4TableInfo ($column)
$this->pushErrorHandling (PEAR_ERROR_RETURN );
if ($column->Properties ->Count () > 0 ) {
$property = @$column->Properties ('Nullable');
$ret .= (string) (@$property->Value == false ) ? 'not_null' : '';
$property = @$column->Properties ('Autoincrement');
$ret .= (string) (@$property->Value == true ) ? 'auto_increment' : '';
$property = @$column->Properties ('Primary Key');
$ret .= (string) (@$property->Value == true ) ? 'primary_key' : '';
$property = @$column->Properties ('Unique');
$ret .= (string) (@$property->Value == true ) ? 'unique' : '';
$this->popErrorHandling ();
* @return string ADODB version
return (string) $this->connection->Version;
* @return string ADODB version
return (string) $this->connection->Provider;
* Checks if field is a binary type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfBinary ($value)
* Checks if field is a bit type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfBit ($value)
* Checks if field is a char type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfChar ($value)
* Checks if field is a date type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfDate ($value)
* Checks if field is a decimal type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfDecimal ($value)
* Checks if field is a numeric type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfNumeric ($value)
* Checks if field is a double type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfDouble ($value)
* Checks if field is a float type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfFloat ($value)
* Checks if field is a real type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfReal ($value)
* Checks if field is a integer type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfInteger ($value)
* Checks if field is a currency type
* (which is not supported by php)
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfCurrency ($value)
* Checks if field is a time type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfTime ($value)
* Checks if field is a timestamp type
* @param integer $value fieldtype
* @return boolean TRUE on match, FALSE if other type
function isTypeOfTimestamp ($value)
Documentation generated on Mon, 02 Mar 2009 13:00:31 +0000 by phpDocumentor 1.4.2. PEAR Logo Copyright © PHP Group 2004.
|