Hello PDO.

Still more to come.  Give it a couple of days before starting to write drivers
for the other databases; a few things might change, so I'd like to coordinate
that, but in a couple of days.
This commit is contained in:
Wez Furlong 2004-05-17 15:41:51 +00:00
parent 3a4f33e31f
commit 684be9cf36
13 changed files with 1881 additions and 0 deletions

2
ext/pdo/CREDITS Executable file
View File

@ -0,0 +1,2 @@
PHP Data Objects
Wez Furlong, Marcus Boerger, Sterling Hughes, George Schlossnagle

0
ext/pdo/EXPERIMENTAL Normal file
View File

56
ext/pdo/README Executable file
View File

@ -0,0 +1,56 @@
$Id$
PHP Data Objects
================
Concept: Data Access Abstraction
Goals:
1/ Be light-weight
2/ Provide common API for common database operations
3/ Be performant
4/ Keep majority of PHP specific stuff in the PDO core (such as persistent
resource management); drivers should only have to worry about getting the
data and not about PHP internals.
Transactions and autocommit
===========================
When you create a database handle, you *should* specify the autocommit
behaviour that you require. PDO will default to autocommit on.
$dbh = new PDO("...", $user, $pass, array(PDO_ATTR_AUTOCOMMIT => true));
When auto-commit is on, the driver will implicitly commit each query as it is
executed. This works fine for most simple tasks but can be significantly
slower when you are making a large number of udpates.
$dbh = new PDO("...", $user, $pass, array(PDO_ATTR_AUTOCOMMIT => false));
When auto-commit is off, you must then use $dbh->beginWork() to initiate a
transaction. When your work is done, you then call $dbh->commit() or
$dbh->rollBack() to persist or abort your changes respectively.
Not all databases support transactions.
You can change the auto-commit mode at run-time:
$dbh->setAttribute(PDO_ATTR_AUTOCOMMIT, false);
Regardless of the error handling mode set on the database handle, if the
autocommit mode cannot be changed, an exception will be thrown.
Some drivers will allow you to temporarily disable autocommit if you call
$dbh->beginWork(). When you commit() or rollBack() such a transaction, the
handle will switch back to autocommit mode again. If the mode could not be
changed, an exception will be raised, as noted above.
When the database handle is closed or destroyed (or at request end for
persistent handles), the driver will implicitly rollBack(). It is your
responsibility to call commit() when you are done making changes and
autocommit is turned off.
vim:tw=78:et

25
ext/pdo/TODO Executable file
View File

@ -0,0 +1,25 @@
$Id$
In no particular order:
Low-level:
- Scanner for :named placeholders and prepare()/execute() emulation (George)
- $dbh->quote()
- Exceptions and unified error API
- Scrollable cursors
- meta data from driver, such as server version and supported features
- field meta data from statement handles
- $dbh->exec() for one-shot SQL
- LOB support via Streams API
- persistent handles
- iterator support
- "lazy" fetch support
Not-so-low-level:
- hash key case folding for portabilitiy, ala sqlite.assoc_case, but not using an ini option.
- fetchAll(), single row, single column fetches etc.
Could be more...
vim:tw=78:et

9
ext/pdo/config.m4 Executable file
View File

@ -0,0 +1,9 @@
dnl $Id$
dnl config.m4 for extension pdo
PHP_ARG_ENABLE(pdo, whether to enable PDO support,
[ --enable-pdo Enable PHP Data Objects support])
if test "$PHP_PDO" != "no"; then
PHP_NEW_EXTENSION(pdo, pdo.c pdo_dbh.c pdo_stmt.c, $ext_shared)
fi

9
ext/pdo/config.w32 Executable file
View File

@ -0,0 +1,9 @@
// $Id$
// vim:ft=javascript
ARG_ENABLE("pdo", "Enable PHP Data Objects support", "no");
if (PHP_PDO != "no") {
EXTENSION('pdo', 'pdo.c pdo_dbh.c pdo_stmt.c');
}

263
ext/pdo/pdo.c Executable file
View File

@ -0,0 +1,263 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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: Wez Furlong <wez@php.net> |
| Marcus Boerger <helly@php.net> |
| Sterling Hughes <sterling@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_pdo.h"
#include "php_pdo_driver.h"
#include "php_pdo_int.h"
#include "zend_exceptions.h"
ZEND_DECLARE_MODULE_GLOBALS(pdo)
/* True global resources - no need for thread safety here */
/* the registry of PDO drivers */
static HashTable pdo_driver_hash;
/* we use persistent resources for the driver connection stuff */
static int le_ppdo;
/* for exceptional circumstances */
zend_class_entry *pdo_exception_ce;
zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce;
/* {{{ pdo_functions[] */
function_entry pdo_functions[] = {
{NULL, NULL, NULL}
};
/* }}} */
/* {{{ pdo_module_entry */
zend_module_entry pdo_module_entry = {
STANDARD_MODULE_HEADER,
"pdo",
pdo_functions,
PHP_MINIT(pdo),
PHP_MSHUTDOWN(pdo),
PHP_RINIT(pdo),
PHP_RSHUTDOWN(pdo),
PHP_MINFO(pdo),
"0.1",
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_PDO
ZEND_GET_MODULE(pdo)
#endif
/* {{{ PHP_INI */
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("pdo.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_pdo_globals, pdo_globals)
PHP_INI_END()
/* }}} */
/* {{{ php_pdo_init_globals */
static void php_pdo_init_globals(zend_pdo_globals *pdo_globals)
{
pdo_globals->global_value = 0;
}
/* }}} */
PDO_API int php_pdo_register_driver(pdo_driver_t *driver)
{
if (driver->api_version != PDO_DRIVER_API)
return FAILURE;
printf("registering PDO driver %s\n", driver->driver_name);
return zend_hash_add(&pdo_driver_hash, (char*)driver->driver_name, driver->driver_name_len,
(void**)&driver, sizeof(driver), NULL);
}
PDO_API void php_pdo_unregister_driver(pdo_driver_t *driver)
{
zend_hash_del(&pdo_driver_hash, (char*)driver->driver_name, driver->driver_name_len);
}
pdo_driver_t *pdo_find_driver(const char *name, int namelen)
{
pdo_driver_t **driver = NULL;
zend_hash_find(&pdo_driver_hash, (char*)name, namelen, (void**)&driver);
return driver ? *driver : NULL;
}
PDO_API int php_pdo_parse_data_source(const char *data_source,
unsigned long data_source_len, struct pdo_data_src_parser *parsed,
int nparams)
{
int i, j;
int valstart = -1;
int semi = -1;
int optstart = 0;
int nlen;
int n_matches = 0;
i = 0;
while (i < data_source_len) {
/* looking for NAME= */
if (data_source[i] == '\0') {
break;
}
if (data_source[i] != '=') {
++i;
continue;
}
valstart = ++i;
/* now we're looking for VALUE; or just VALUE<NUL> */
semi = -1;
while (i < data_source_len) {
if (data_source[i] == '\0') {
semi = i++;
break;
}
if (data_source[i] == ';') {
semi = i++;
break;
}
++i;
}
if (semi == -1) {
semi = i;
}
/* find the entry in the array */
nlen = valstart - optstart - 1;
for (j = 0; j < nparams; j++) {
if (0 == strncmp(data_source + optstart, parsed[j].optname, nlen) && parsed[j].optname[nlen] == '\0') {
/* got a match */
if (parsed[j].freeme) {
efree(parsed[j].optval);
}
parsed[j].optval = estrndup(data_source + valstart, semi - valstart);
parsed[j].freeme = 1;
++n_matches;
break;
}
}
optstart = i;
}
return n_matches;
}
/* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(pdo)
{
zend_class_entry ce;
ZEND_INIT_MODULE_GLOBALS(pdo, php_pdo_init_globals, NULL);
REGISTER_INI_ENTRIES();
zend_hash_init(&pdo_driver_hash, 0, NULL, NULL, 1);
REGISTER_LONG_CONSTANT("PDO_PARAM_NULL", (long)PDO_PARAM_NULL, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_PARAM_INT", (long)PDO_PARAM_INT, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_PARAM_STR", (long)PDO_PARAM_STR, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_PARAM_LOB", (long)PDO_PARAM_LOB, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_PARAM_STMT", (long)PDO_PARAM_STMT, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_FETCH_LAZY", (long)PDO_FETCH_LAZY, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_FETCH_ASSOC",(long)PDO_FETCH_ASSOC, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_FETCH_NUM", (long)PDO_FETCH_NUM, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_FETCH_BOTH", (long)PDO_FETCH_BOTH, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_FETCH_OBJ", (long)PDO_FETCH_OBJ, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ATTR_AUTOCOMMIT", (long)PDO_ATTR_AUTOCOMMIT, CONST_CS|CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("PDO_ATTR_SCROLL", (long)PDO_ATTR_SCROLL, CONST_CS|CONST_PERSISTENT);
INIT_CLASS_ENTRY(ce, "PDOException", NULL);
pdo_exception_ce = zend_register_internal_class_ex(&ce, zend_exception_get_default(), NULL TSRMLS_CC);
INIT_CLASS_ENTRY(ce, "PDO", pdo_dbh_functions);
ce.create_object = pdo_dbh_new;
pdo_dbh_ce = zend_register_internal_class(&ce TSRMLS_CC);
INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
ce.create_object = pdo_dbstmt_new;
pdo_dbstmt_ce = zend_register_internal_class(&ce TSRMLS_CC);
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION */
PHP_MSHUTDOWN_FUNCTION(pdo)
{
UNREGISTER_INI_ENTRIES();
zend_hash_destroy(&pdo_driver_hash);
return SUCCESS;
}
/* }}} */
/* {{{ PHP_RINIT_FUNCTION */
PHP_RINIT_FUNCTION(pdo)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_RSHUTDOWN_FUNCTION */
PHP_RSHUTDOWN_FUNCTION(pdo)
{
/* TODO: visit persistent handles: for each persistent statement handle,
* remove bound parameter associations */
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION */
PHP_MINFO_FUNCTION(pdo)
{
php_info_print_table_start();
php_info_print_table_header(2, "pdo support", "enabled");
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

35
ext/pdo/pdo.php Executable file
View File

@ -0,0 +1,35 @@
<?php
//$x = new PDO("oci:dbname=hostname", 'php', 'php');
$x = new PDO("odbc:ram", 'php', 'php', array(PDO_ATTR_AUTOCOMMIT => 0));
$stmt = $x->prepare("select NAME, VALUE from test where value like ?");
$the_name = 'bar%';
$stmt->execute(array($the_name)) or die("failed to execute!");
$stmt->bindColumn('VALUE', $value);
while ($row = $stmt->fetch()) {
echo "name=$row[NAME] value=$row[VALUE]\n";
echo "value is $value\n";
echo "\n";
}
echo "Let's try an update\n";
$stmt = $x->prepare("INSERT INTO test (NAME, VALUE) VALUES (:name, :value)");
$stmt->bindParam(":name", $the_name, PDO_PARAM_STR, 32);
$stmt->bindParam(":value", $the_value, PDO_PARAM_STR, 32);
for ($i = 0; $i < 4; $i++) {
$the_name = "foo" . rand();
$the_value = "bar" . rand();
if (!$stmt->execute()) {
break;
}
}
echo "All done\n";
?>

431
ext/pdo/pdo_dbh.c Executable file
View File

@ -0,0 +1,431 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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: Wez Furlong <wez@php.net> |
| Marcus Boerger <helly@php.net> |
| Sterling Hughes <sterling@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* The PDO Database Handle Class */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_pdo.h"
#include "php_pdo_driver.h"
#include "php_pdo_int.h"
#include "zend_exceptions.h"
/* {{{ proto object PDO::__construct(string dsn, string username, string passwd [, array driver_opts])
*/
static PHP_FUNCTION(dbh_constructor)
{
zval *object = getThis();
pdo_dbh_t *dbh = NULL;
zend_bool is_persistent = FALSE;
char *data_source;
long data_source_len;
char *driver_name_length;
char *colon;
char *username, *password;
long usernamelen, passwordlen;
pdo_driver_t *driver = NULL;
zval *driver_options = NULL;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|a!", &data_source, &data_source_len,
&username, &usernamelen, &password, &passwordlen, &driver_options)) {
ZVAL_NULL(object);
return;
}
/* parse the data source name */
colon = strchr(data_source, ':');
if (!colon) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid data source name");
ZVAL_NULL(object);
return;
}
driver = pdo_find_driver(data_source, colon - data_source);
if (!driver) {
/* NB: don't want to include the data_source in the error message as
* it might contain a password */
php_error_docref(NULL TSRMLS_CC, E_WARNING, "could not find driver");
ZVAL_NULL(object);
return;
}
dbh = pemalloc(sizeof(*dbh), is_persistent);
if (dbh == NULL) {
/* need this check for persistent allocations */
php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory");
}
memset(dbh, 0, sizeof(*dbh));
dbh->is_persistent = is_persistent;
dbh->data_source_len = strlen(colon + 1);
/* when persistent stuff is done, we should check the return values here
* too */
dbh->data_source = (const char*)pestrdup(colon + 1, is_persistent);
dbh->username = pestrdup(username, is_persistent);
dbh->password = pestrdup(password, is_persistent);
if (driver_options) {
dbh->auto_commit = pdo_attr_lval(driver_options, PDO_ATTR_AUTOCOMMIT, 1 TSRMLS_CC);
} else {
dbh->auto_commit = 1;
}
if (driver->db_handle_factory(dbh, driver_options TSRMLS_CC)) {
/* all set */
if (is_persistent) {
/* register in the persistent list etc. */
;
}
zend_object_store_set_object(object, dbh TSRMLS_CC);
return;
}
/* the connection failed; tidy things up */
pefree((char*)dbh->data_source, is_persistent);
pefree(dbh, is_persistent);
/* XXX raise exception */
ZVAL_NULL(object);
}
/* }}} */
/* {{{ proto object PDO::prepare(string statment)
Prepares a statement for execution and returns a statement object */
static PHP_METHOD(PDO, prepare)
{
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
pdo_stmt_t *stmt;
char *statement;
long statement_len;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &statement, &statement_len)) {
RETURN_FALSE;
}
stmt = ecalloc(1, sizeof(*stmt));
if (dbh->methods->preparer(dbh, statement, statement_len, stmt TSRMLS_CC)) {
/* prepared; create a statement object for PHP land to access it */
Z_TYPE_P(return_value) = IS_OBJECT;
Z_OBJ_HANDLE_P(return_value) = zend_objects_store_put(stmt, NULL, pdo_dbstmt_free_storage, NULL TSRMLS_CC);
Z_OBJ_HT_P(return_value) = &pdo_dbstmt_object_handlers;
/* give it a reference to me */
stmt->database_object_handle = *getThis();
zend_objects_store_add_ref(getThis() TSRMLS_CC);
stmt->dbh = dbh;
return;
}
efree(stmt);
RETURN_FALSE;
}
/* {{{ proto bool PDO::beginWork()
Initiates a transaction */
static PHP_METHOD(PDO, beginWork)
{
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (dbh->in_txn) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "There is already an active transaction");
RETURN_FALSE;
}
if (!dbh->methods->begin) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver does not support transactions");
RETURN_FALSE;
}
if (dbh->methods->begin(dbh TSRMLS_CC)) {
dbh->in_txn = 1;
RETURN_TRUE;
}
RETURN_FALSE;
}
/* }}} */
/* {{{ proto bool PDO::commit()
Commit a transaction */
static PHP_METHOD(PDO, commit)
{
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!dbh->in_txn) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "There is no active transaction");
RETURN_FALSE;
}
if (dbh->methods->commit(dbh TSRMLS_CC)) {
dbh->in_txn = 0;
RETURN_TRUE;
}
RETURN_FALSE;
}
/* }}} */
/* {{{ proto bool PDO::rollBack()
roll back a transaction */
static PHP_METHOD(PDO, rollBack)
{
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
if (!dbh->in_txn) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "There is no active transaction");
RETURN_FALSE;
}
if (dbh->methods->rollback(dbh TSRMLS_CC)) {
dbh->in_txn = 0;
RETURN_TRUE;
}
RETURN_FALSE;
}
/* }}} */
/* {{{ proto bool PDO::setAttribute(long attribute, mixed value)
Set an attribute */
static PHP_METHOD(PDO, setAttribute)
{
pdo_dbh_t *dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
long attr;
zval *value = NULL;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz!", &attr, &value)) {
RETURN_FALSE;
}
if (!dbh->methods->set_attribute) {
goto fail;
}
if (dbh->methods->set_attribute(dbh, attr, value TSRMLS_CC)) {
RETURN_TRUE;
}
fail:
if (attr == PDO_ATTR_AUTOCOMMIT) {
/* Feature: if the auto-commit mode cannot be changed, throw an
* exception. Until I've added the code for that, raise an
* E_ERROR */
php_error_docref(NULL TSRMLS_CC, E_ERROR, "The auto commit mode cannot be changed for this driver");
} else if (!dbh->methods->set_attribute) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver doesn't support setting attributes");
}
RETURN_FALSE;
}
/* }}} */
function_entry pdo_dbh_functions[] = {
PHP_ME(PDO, prepare, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, beginWork, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, commit, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, rollBack, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDO, setAttribute, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
/* {{{ overloaded object handlers for PDO class */
static zval *dbh_prop_read(zval *object, zval *member, int type TSRMLS_DC)
{
zval *return_value;
MAKE_STD_ZVAL(return_value);
ZVAL_NULL(return_value);
return return_value;
}
static void dbh_prop_write(zval *object, zval *member, zval *value TSRMLS_DC)
{
return;
}
static zval *dbh_read_dim(zval *object, zval *offset, int type TSRMLS_DC)
{
zval *return_value;
MAKE_STD_ZVAL(return_value);
ZVAL_NULL(return_value);
return return_value;
}
static void dbh_write_dim(zval *object, zval *offset, zval *value TSRMLS_DC)
{
return;
}
static int dbh_prop_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
{
return 0;
}
static int dbh_dim_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
{
return 0;
}
static void dbh_prop_delete(zval *object, zval *offset TSRMLS_DC)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a PDO DBH");
}
static void dbh_dim_delete(zval *object, zval *offset TSRMLS_DC)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete dimensions from a PDO DBH");
}
static HashTable *dbh_get_properties(zval *object TSRMLS_DC)
{
return NULL;
}
static union _zend_function *dbh_method_get(zval *object, char *method_name, int method_len TSRMLS_DC)
{
zend_function *fbc;
char *lc_method_name;
lc_method_name = do_alloca(method_len + 1);
zend_str_tolower_copy(lc_method_name, method_name, method_len);
if (zend_hash_find(&pdo_dbh_ce->function_table, lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
free_alloca(lc_method_name);
return NULL;
}
free_alloca(lc_method_name);
return fbc;
}
static int dbh_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
{
return FAILURE;
}
static union _zend_function *dbh_get_ctor(zval *object TSRMLS_DC)
{
static zend_internal_function ctor = {0};
ctor.type = ZEND_INTERNAL_FUNCTION;
ctor.function_name = "__construct";
ctor.scope = pdo_dbh_ce;
ctor.handler = ZEND_FN(dbh_constructor);
return (union _zend_function*)&ctor;
}
static zend_class_entry *dbh_get_ce(zval *object TSRMLS_DC)
{
return pdo_dbh_ce;
}
static int dbh_get_classname(zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
{
*class_name = estrndup("PDO", sizeof("PDO")-1);
*class_name_len = sizeof("PDO")-1;
return 0;
}
static int dbh_compare(zval *object1, zval *object2 TSRMLS_DC)
{
return -1;
}
static zend_object_handlers pdo_dbh_object_handlers = {
ZEND_OBJECTS_STORE_HANDLERS,
dbh_prop_read,
dbh_prop_write,
dbh_read_dim,
dbh_write_dim,
NULL,
NULL,
NULL,
dbh_prop_exists,
dbh_prop_delete,
dbh_dim_exists,
dbh_dim_delete,
dbh_get_properties,
dbh_method_get,
dbh_call_method,
dbh_get_ctor,
dbh_get_ce,
dbh_get_classname,
dbh_compare,
NULL, /* cast */
NULL
};
static void pdo_dbh_free_storage(void *object TSRMLS_DC)
{
pdo_dbh_t *dbh = (pdo_dbh_t*)object;
if (!dbh) {
return;
}
if (dbh->is_persistent) {
/* XXX: don't really free it, just delete the rsrc id */
return;
}
dbh->methods->closer(dbh TSRMLS_CC);
efree(dbh);
}
zend_object_value pdo_dbh_new(zend_class_entry *ce TSRMLS_DC)
{
zend_object_value retval;
retval.handle = zend_objects_store_put(NULL, NULL, pdo_dbh_free_storage, NULL TSRMLS_CC);
retval.handlers = &pdo_dbh_object_handlers;
return retval;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

641
ext/pdo/pdo_stmt.c Executable file
View File

@ -0,0 +1,641 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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: Wez Furlong <wez@php.net> |
| Marcus Boerger <helly@php.net> |
| Sterling Hughes <sterling@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* The PDO Statement Handle Class */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_pdo.h"
#include "php_pdo_driver.h"
#include "php_pdo_int.h"
#include "zend_exceptions.h"
#include "zend_arg_defs.c"
static PHP_FUNCTION(dbstmt_constructor) /* {{{ */
{
php_error_docref(NULL TSRMLS_CC, E_ERROR, "You should not create a PDOStatement manually");
}
/* }}} */
/* trigger callback hook for parameters */
static int dispatch_param_event(pdo_stmt_t *stmt, enum pdo_param_event event_type TSRMLS_DC)
{
int ret = 1, is_param = 1;
struct pdo_bound_param_data *param;
HashTable *ht;
if (!stmt->methods->param_hook) {
return 1;
}
ht = stmt->bound_params;
iterate:
if (ht) {
zend_hash_internal_pointer_reset(ht);
while (SUCCESS == zend_hash_get_current_data(ht, (void**)&param)) {
if (!stmt->methods->param_hook(stmt, param, event_type TSRMLS_CC)) {
ret = 0;
break;
}
zend_hash_move_forward(ht);
}
}
if (ret && is_param) {
ht = stmt->bound_columns;
is_param = 0;
goto iterate;
}
return ret;
}
static int describe_columns(pdo_stmt_t *stmt TSRMLS_DC)
{
int col;
stmt->columns = ecalloc(stmt->column_count, sizeof(struct pdo_column_data));
for (col = 0; col < stmt->column_count; col++) {
if (!stmt->methods->describer(stmt, col TSRMLS_CC)) {
return 0;
}
/* XXX: if we are applying case conversions on column names, do so now */
/* update the column index on named bound parameters */
if (stmt->dbh->placeholders_can_be_strings && stmt->bound_params) {
struct pdo_bound_param_data *param;
if (SUCCESS == zend_hash_find(stmt->bound_params, stmt->columns[col].name,
stmt->columns[col].namelen, (void**)&param)) {
param->paramno = col;
}
}
if (stmt->bound_columns) {
struct pdo_bound_param_data *param;
if (SUCCESS == zend_hash_find(stmt->bound_columns, stmt->columns[col].name,
stmt->columns[col].namelen, (void**)&param)) {
param->paramno = col;
}
}
}
return 1;
}
static void param_dtor(void *data)
{
TSRMLS_FETCH();
struct pdo_bound_param_data *param = (struct pdo_bound_param_data *)data;
/* tell the driver that it is going away */
if (param->stmt->methods->param_hook) {
param->stmt->methods->param_hook(param->stmt, param, PDO_PARAM_EVT_FREE TSRMLS_CC);
}
zval_ptr_dtor(&(param->parameter));
zval_ptr_dtor(&(param->driver_params));
}
static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_stmt_t *stmt, int is_param TSRMLS_DC)
{
HashTable *hash;
struct pdo_bound_param_data *pparam = NULL;
hash = is_param ? stmt->bound_params : stmt->bound_columns;
if (!hash) {
ALLOC_HASHTABLE(hash);
zend_hash_init(hash, 13, NULL, param_dtor, 0);
if (is_param) {
stmt->bound_params = hash;
} else {
stmt->bound_columns = hash;
}
}
if (param->param_type == PDO_PARAM_STR && param->max_value_len <= 0) {
convert_to_string(param->parameter);
/* XXX: need to provide a way to set this to something sane, or
* investigate a better way to set the length of output parameters in
* the drivers themselves */
param->max_value_len = Z_STRLEN_P(param->parameter);
}
param->stmt = stmt;
param->is_param = is_param;
ZVAL_ADDREF(param->parameter);
if (param->driver_params) {
ZVAL_ADDREF(param->driver_params);
}
if (param->name && stmt->columns) {
/* try to map the name to the column */
int i;
for (i = 0; i < stmt->column_count; i++) {
if (strcmp(stmt->columns[i].name, param->name) == 0) {
param->paramno = i;
break;
}
}
if (param->paramno == -1) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Did not found column name '%s' in the defined columns; it will not be bound", param->name);
}
}
if (param->name) {
param->name = estrndup(param->name, param->namelen);
zend_hash_update(hash, param->name, param->namelen, param, sizeof(*param), &pparam);
} else {
zend_hash_index_update(hash, param->paramno, param, sizeof(*param), &pparam);
}
/* tell the driver we just created a parameter */
if (stmt->methods->param_hook) {
return stmt->methods->param_hook(stmt, pparam, PDO_PARAM_EVT_ALLOC TSRMLS_CC);
}
return 1;
}
/* {{{ proto bool PDOStatement::execute([array $bound_input_params])
Execute a prepared statement, optionally binding parameters */
static PHP_METHOD(PDOStatement, execute)
{
pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
zval *input_params = NULL;
int ret = 1;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &input_params)) {
RETURN_FALSE;
}
if (input_params) {
int i;
struct pdo_bound_param_data param;
zval **tmp;
uint str_length;
ulong num_index;
zend_hash_internal_pointer_reset(Z_ARRVAL_P(input_params));
while (SUCCESS == zend_hash_get_current_data(Z_ARRVAL_P(input_params), (void*)&tmp)) {
memset(&param, 0, sizeof(param));
if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(Z_ARRVAL_P(input_params),
&param.name, &str_length, &num_index, 0, NULL)) {
param.namelen = str_length;
param.paramno = -1;
if (!stmt->dbh->placeholders_can_be_strings) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver doesn't support named placeholders");
RETURN_FALSE;
}
} else {
/* we're okay to be zero based here */
if (num_index < 0) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter index");
RETURN_FALSE;
}
param.paramno = num_index;
}
param.param_type = PDO_PARAM_STR;
param.parameter = *tmp;
if (!really_register_bound_param(&param, stmt, 1 TSRMLS_CC)) {
RETURN_FALSE;
}
zend_hash_move_forward(Z_ARRVAL_P(input_params));
}
}
if (!dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_PRE TSRMLS_CC)) {
RETURN_FALSE;
}
if (stmt->methods->executer(stmt TSRMLS_CC)) {
printf("Execute ok: flag=%d\n", stmt->executed);
if (!stmt->executed) {
/* this is the first execute */
if (stmt->dbh->alloc_own_columns) {
/* for "big boy" drivers, we need to allocate memory to fetch
* the results into, so lets do that now */
ret = describe_columns(stmt TSRMLS_CC);
}
stmt->executed = 1;
}
if (ret && !dispatch_param_event(stmt, PDO_PARAM_EVT_EXEC_POST TSRMLS_CC)) {
RETURN_FALSE;
}
RETURN_BOOL(ret);
}
RETURN_FALSE;
}
/* }}} */
static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno TSRMLS_DC)
{
struct pdo_column_data *col;
char *value = NULL;
unsigned long value_len = 0;
col = &stmt->columns[colno];
value = NULL;
value_len = 0;
stmt->methods->get_col(stmt, colno, &value, &value_len TSRMLS_CC);
switch (col->param_type) {
case PDO_PARAM_STR:
if (value) {
ZVAL_STRINGL(dest, value, value_len, 1);
}
break;
default:
ZVAL_NULL(dest);
}
}
/* perform a fetch. If do_bind is true, update any bound columns.
* If return_value is not null, store values into it according to HOW. */
static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value, enum pdo_fetch_type how TSRMLS_DC)
{
int i;
zval *val;
if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE TSRMLS_CC)) {
return 0;
}
if (!stmt->methods->fetcher(stmt TSRMLS_CC)) {
return 0;
}
/* some drivers might need to describe the columns now */
if (!stmt->columns && !describe_columns(stmt TSRMLS_CC)) {
return 0;
}
if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_POST TSRMLS_CC)) {
return 0;
}
if (return_value) {
array_init(return_value);
}
if (do_bind && stmt->bound_columns) {
/* update those bound column variables now */
struct pdo_bound_param_data *param;
zend_hash_internal_pointer_reset(stmt->bound_columns);
while (SUCCESS == zend_hash_get_current_data(stmt->bound_columns, (void**)&param)) {
if (param->paramno >= 0) {
convert_to_string(param->parameter);
/* delete old value */
zval_dtor(param->parameter);
/* set new value */
fetch_value(stmt, param->parameter, param->paramno TSRMLS_CC);
/* TODO: some smart thing that avoids duplicating the value in the
* general loop below. For now, if you're binding output columns,
* it's better to use LAZY or NONE fetches if you want to shave
* off those cycles */
}
zend_hash_move_forward(stmt->bound_columns);
}
}
if (return_value) {
if (how == PDO_FETCH_LAZY || how == PDO_FETCH_OBJ) {
how = PDO_FETCH_BOTH;
}
for (i = 0; i < stmt->column_count; i++) {
MAKE_STD_ZVAL(val);
fetch_value(stmt, val, i TSRMLS_CC);
if (how == PDO_FETCH_ASSOC || how == PDO_FETCH_BOTH) {
add_assoc_zval(return_value, stmt->columns[i].name, val);
}
if (how == PDO_FETCH_NUM || how == PDO_FETCH_BOTH) {
add_next_index_zval(return_value, val);
}
if (how == PDO_FETCH_BOTH) {
ZVAL_ADDREF(val);
}
}
}
return 1;
}
/* {{{ proto mixed PDOStatement::fetch([int $how = PDO_FETCH_BOTH])
Fetches the next row and returns it, or false if there are no more rows */
static PHP_METHOD(PDOStatement, fetch)
{
long how = PDO_FETCH_BOTH;
pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
int i;
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &how)) {
RETURN_FALSE;
}
if (!do_fetch(stmt, TRUE, return_value, how TSRMLS_CC)) {
RETURN_FALSE;
}
}
/* }}} */
static int register_bound_param(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, int is_param)
{
struct pdo_bound_param_data param = {0}, *pparam = NULL;
param.paramno = -1;
param.param_type = PDO_PARAM_STR;
if (stmt->dbh->placeholders_can_be_strings || !is_param) {
if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
ZEND_NUM_ARGS() TSRMLS_CC, "sz|llz!",
&param.name, &param.namelen, &param.parameter, &param.param_type,
&param.max_value_len,
&param.driver_params)) {
if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz|llz!", &param.paramno,
&param.parameter, &param.param_type, &param.max_value_len, &param.driver_params)) {
return 0;
}
}
} else if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "lz|llz!", &param.paramno,
&param.parameter, &param.param_type, &param.max_value_len, &param.driver_params)) {
return 0;
}
if (param.paramno > 0) {
--param.paramno; /* make it zero-based internally */
} else if (!param.name) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter number: columns are 1-based");
return 0;
}
return really_register_bound_param(&param, stmt, is_param TSRMLS_CC);
}
/* {{{ proto bool PDOStatement::bindParam(mixed $paramno, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
bind a parameter to a PHP variable. $paramno is the 1-based position of the placeholder in the SQL statement (but can be the parameter name for drivers that support named placeholders). This isn't supported by all drivers. It should be called prior to execute(). */
static PHP_METHOD(PDOStatement, bindParam)
{
pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
if (!stmt->dbh->supports_placeholders) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "This driver doesn't support placeholders");
RETURN_FALSE;
}
RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, TRUE));
}
/* }}} */
/* {{{ proto bool PDOStatement::bindColumn(mixed $column, mixed &$param [, int $type [, int $maxlen [, mixed $driverdata]]])
bind a column to a PHP variable. On each row fetch $param will contain the value of the corresponding column. $column is the 1-based offset of the column, or the column name. For portability, don't call this before execute(). */
static PHP_METHOD(PDOStatement, bindColumn)
{
pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
RETURN_BOOL(register_bound_param(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, FALSE));
}
/* }}} */
/* {{{ proto int PDOStatement::rowCount()
Returns the number of rows in a result set, or the number of rows affected by the last execute(). It is not always meaningful. */
static PHP_METHOD(PDOStatement, rowCount)
{
pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
RETURN_LONG(stmt->row_count);
}
/* }}} */
extern function_entry pdo_dbstmt_functions[] = {
PHP_ME(PDOStatement, execute, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDOStatement, fetch, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PDOStatement, bindParam, second_arg_force_ref, ZEND_ACC_PUBLIC)
PHP_ME(PDOStatement, bindColumn, second_arg_force_ref, ZEND_ACC_PUBLIC)
PHP_ME(PDOStatement, rowCount, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
/* {{{ overloaded handlers for PDOStatement class */
static zval *dbstmt_prop_read(zval *object, zval *member, int type TSRMLS_DC)
{
zval *return_value;
MAKE_STD_ZVAL(return_value);
ZVAL_NULL(return_value);
return return_value;
}
static void dbstmt_prop_write(zval *object, zval *member, zval *value TSRMLS_DC)
{
return;
}
static zval *dbstmt_read_dim(zval *object, zval *offset, int type TSRMLS_DC)
{
zval *return_value;
MAKE_STD_ZVAL(return_value);
ZVAL_NULL(return_value);
return return_value;
}
static void dbstmt_write_dim(zval *object, zval *offset, zval *value TSRMLS_DC)
{
return;
}
static int dbstmt_prop_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
{
return 0;
}
static int dbstmt_dim_exists(zval *object, zval *member, int check_empty TSRMLS_DC)
{
return 0;
}
static void dbstmt_prop_delete(zval *object, zval *offset TSRMLS_DC)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete properties from a PDO STMT");
}
static void dbstmt_dim_delete(zval *object, zval *offset TSRMLS_DC)
{
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot delete dimensions from a PDO STMT");
}
static HashTable *dbstmt_get_properties(zval *object TSRMLS_DC)
{
return NULL;
}
static union _zend_function *dbstmt_method_get(zval *object, char *method_name, int method_len TSRMLS_DC)
{
zend_function *fbc;
char *lc_method_name;
lc_method_name = do_alloca(method_len + 1);
zend_str_tolower_copy(lc_method_name, method_name, method_len);
if (zend_hash_find(&pdo_dbstmt_ce->function_table, lc_method_name, method_len+1, (void**)&fbc) == FAILURE) {
free_alloca(lc_method_name);
return NULL;
}
free_alloca(lc_method_name);
return fbc;
}
static int dbstmt_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
{
return FAILURE;
}
static union _zend_function *dbstmt_get_ctor(zval *object TSRMLS_DC)
{
static zend_internal_function ctor = {0};
ctor.type = ZEND_INTERNAL_FUNCTION;
ctor.function_name = "__construct";
ctor.scope = pdo_dbstmt_ce;
ctor.handler = ZEND_FN(dbstmt_constructor);
return (union _zend_function*)&ctor;
}
static zend_class_entry *dbstmt_get_ce(zval *object TSRMLS_DC)
{
return pdo_dbstmt_ce;
}
static int dbstmt_get_classname(zval *object, char **class_name, zend_uint *class_name_len, int parent TSRMLS_DC)
{
*class_name = estrndup("PDOStatement", sizeof("PDOStatement")-1);
*class_name_len = sizeof("PDOStatement")-1;
return 0;
}
static int dbstmt_compare(zval *object1, zval *object2 TSRMLS_DC)
{
return -1;
}
zend_object_handlers pdo_dbstmt_object_handlers = {
ZEND_OBJECTS_STORE_HANDLERS,
dbstmt_prop_read,
dbstmt_prop_write,
dbstmt_read_dim,
dbstmt_write_dim,
NULL,
NULL,
NULL,
dbstmt_prop_exists,
dbstmt_prop_delete,
dbstmt_dim_exists,
dbstmt_dim_delete,
dbstmt_get_properties,
dbstmt_method_get,
dbstmt_call_method,
dbstmt_get_ctor,
dbstmt_get_ce,
dbstmt_get_classname,
dbstmt_compare,
NULL, /* cast */
NULL
};
void pdo_dbstmt_free_storage(void *object TSRMLS_DC)
{
pdo_stmt_t *stmt = (pdo_stmt_t*)object;
if (stmt->methods && stmt->methods->dtor) {
stmt->methods->dtor(stmt TSRMLS_CC);
}
zend_objects_store_del_ref(&stmt->database_object_handle TSRMLS_CC);
efree(stmt);
}
zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC)
{
zend_object_value retval;
retval.handle = zend_objects_store_put(NULL, NULL, pdo_dbstmt_free_storage, NULL TSRMLS_CC);
retval.handlers = &pdo_dbstmt_object_handlers;
return retval;
}
/* }}} */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

69
ext/pdo/php_pdo.h Executable file
View File

@ -0,0 +1,69 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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: Wez Furlong <wez@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
#ifndef PHP_PDO_H
#define PHP_PDO_H
extern zend_module_entry pdo_module_entry;
#define phpext_pdo_ptr &pdo_module_entry
#ifdef PHP_WIN32
# ifdef PDO_EXPORTS
# define PDO_API __declspec(dllexport)
# elif defined(COMPILE_DL_PDO)
# define PDO_API __declspec(dllimport)
# else
# define PDO_API /* nothing special */
# endif
#else
# define PDO_API /* nothing special */
#endif
#ifdef ZTS
# include "TSRM.h"
#endif
PHP_MINIT_FUNCTION(pdo);
PHP_MSHUTDOWN_FUNCTION(pdo);
PHP_RINIT_FUNCTION(pdo);
PHP_RSHUTDOWN_FUNCTION(pdo);
PHP_MINFO_FUNCTION(pdo);
ZEND_BEGIN_MODULE_GLOBALS(pdo)
long global_value;
ZEND_END_MODULE_GLOBALS(pdo)
#ifdef ZTS
# define PDOG(v) TSRMG(pdo_globals_id, zend_pdo_globals *, v)
#else
# define PDOG(v) (pdo_globals.v)
#endif
#endif /* PHP_PDO_H */
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

295
ext/pdo/php_pdo_driver.h Executable file
View File

@ -0,0 +1,295 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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: Wez Furlong <wez@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* forward declarations */
typedef struct _pdo_dbh_t pdo_dbh_t;
typedef struct _pdo_stmt_t pdo_stmt_t;
#define PDO_DRIVER_API 20040513
enum pdo_param_type {
PDO_PARAM_NULL,
PDO_PARAM_INT,
PDO_PARAM_STR,
PDO_PARAM_LOB,
PDO_PARAM_STMT, /* hierarchical result set */
};
enum pdo_fetch_type {
PDO_FETCH_LAZY,
PDO_FETCH_ASSOC,
PDO_FETCH_NUM,
PDO_FETCH_BOTH,
PDO_FETCH_OBJ,
PDO_FETCH_BOUND, /* return true/false only; rely on bound columns */
};
enum pdo_attribute_type {
PDO_ATTR_AUTOCOMMIT, /* use to turn on or off auto-commit mode */
PDO_ATTR_SCROLL, /* ask for a scrollable cursor (when you prepare()) */
};
/* {{{ utils for reading attributes set as driver_options */
static inline long pdo_attr_lval(zval *options, enum pdo_fetch_type option_name, long defval TSRMLS_DC)
{
zval **v;
if (SUCCESS == zend_hash_index_find(Z_ARRVAL_P(options), option_name, (void**)&v)) {
convert_to_long_ex(v);
return Z_LVAL_PP(v);
}
return defval;
}
/* }}} */
/* This structure is registered with PDO when a PDO driver extension is
* initialized */
typedef struct {
const char *driver_name;
unsigned long driver_name_len;
unsigned long api_version; /* needs to be compatible with PDO */
#define PDO_DRIVER_HEADER(name) \
#name, sizeof(#name)-1, \
PDO_DRIVER_API
/* create driver specific portion of the database handle and stash it into
* the dbh. dbh contains the data source string and flags for this
* instance */
int (*db_handle_factory)(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC);
} pdo_driver_t;
/* {{{ methods for a database handle */
/* close or otherwise disconnect the database */
typedef int (*pdo_dbh_close_func)(pdo_dbh_t *dbh TSRMLS_DC);
/* prepare a statement and stash driver specific portion into stmt */
typedef int (*pdo_dbh_prepare_func)(pdo_dbh_t *dbh, const char *sql, long sql_len, pdo_stmt_t *stmt TSRMLS_DC);
/* execute a statement (that does not return a result set) */
typedef int (*pdo_dbh_do_func)(pdo_dbh_t *dbh, const char *sql TSRMLS_DC);
/* quote a string */
typedef int (*pdo_dbh_quote_func)(pdo_dbh_t *dbh, const char *unquoted, char **quoted, int *quotedlen TSRMLS_DC);
/* transaction related */
typedef int (*pdo_dbh_txn_func)(pdo_dbh_t *dbh TSRMLS_DC);
/* setting and getting of attributes */
typedef int (*pdo_dbh_set_attr_func)(pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC);
struct pdo_dbh_methods {
pdo_dbh_close_func closer;
pdo_dbh_prepare_func preparer;
pdo_dbh_do_func doer;
pdo_dbh_quote_func quoter;
pdo_dbh_txn_func begin;
pdo_dbh_txn_func commit;
pdo_dbh_txn_func rollback;
pdo_dbh_set_attr_func set_attribute;
};
/* }}} */
/* {{{ methods for a statement handle */
/* free the statement handle */
typedef int (*pdo_stmt_dtor_func)(pdo_stmt_t *stmt TSRMLS_DC);
/* start the query */
typedef int (*pdo_stmt_execute_func)(pdo_stmt_t *stmt TSRMLS_DC);
/* causes the next row in the set to be fetched; indicates if there are no
* more rows */
typedef int (*pdo_stmt_fetch_func)(pdo_stmt_t *stmt TSRMLS_DC);
/* queries information about the type of a column, by index (0 based).
* Driver should populate stmt->columns[colno] with appropriate info */
typedef int (*pdo_stmt_describe_col_func)(pdo_stmt_t *stmt, int colno TSRMLS_DC);
/* retrieves pointer and size of the value for a column */
typedef int (*pdo_stmt_get_col_data_func)(pdo_stmt_t *stmt, int colno, char **ptr, unsigned long *len TSRMLS_DC);
/* hook for bound params */
enum pdo_param_event {
PDO_PARAM_EVT_ALLOC,
PDO_PARAM_EVT_FREE,
PDO_PARAM_EVT_EXEC_PRE,
PDO_PARAM_EVT_EXEC_POST,
PDO_PARAM_EVT_FETCH_PRE,
PDO_PARAM_EVT_FETCH_POST,
};
typedef int (*pdo_stmt_param_hook_func)(pdo_stmt_t *stmt, struct pdo_bound_param_data *param, enum pdo_param_event event_type TSRMLS_DC);
struct pdo_stmt_methods {
pdo_stmt_dtor_func dtor;
pdo_stmt_execute_func executer;
pdo_stmt_fetch_func fetcher;
pdo_stmt_describe_col_func describer;
pdo_stmt_get_col_data_func get_col;
pdo_stmt_param_hook_func param_hook;
};
/* }}} */
/* represents a connection to a database */
struct _pdo_dbh_t {
/* driver specific methods */
struct pdo_dbh_methods *methods;
/* driver specific data */
void *driver_data;
/* credentials */
char *username, *password;
/* if true, then data stored and pointed at by this handle must all be
* persistently allocated */
unsigned is_persistent:1;
/* if true, driver should act as though a COMMIT were executed between
* each executed statement; otherwise, COMMIT must be carried out manually
* */
unsigned auto_commit:1;
/* if true, the handle has been closed and will not function anymore */
unsigned is_closed:1;
/* if true, the driver requires that memory be allocated explicitly for
* the columns that are returned */
unsigned alloc_own_columns:1;
/* if true, the driver supports placeholders and can implement
* bindParam() for its prepared statements */
unsigned supports_placeholders:1;
/* if true, the driver allows named placeholders */
unsigned placeholders_can_be_strings:1;
/* if true, commit or rollBack is allowed to be called */
unsigned in_txn:1;
/* if true, PDO should emulate prepare() and bound input parameters for
* the driver */
unsigned emulate_prepare:1;
/* the sum of the number of bits here and the bit fields preceeding should
* equal 32 */
unsigned _reserved_flags:24;
/* data source string used to open this handle */
const char *data_source;
unsigned long data_source_len;
#if 0
/* persistent hash key associated with this handle */
const char *persistent_id;
/* and the list id associated with it */
int persistent_rsrc_id;
#endif
};
/* describes a column */
struct pdo_column_data {
char *name;
long namelen;
unsigned long maxlen;
enum pdo_param_type param_type;
unsigned long precision;
};
/* describes a bound parameter */
struct pdo_bound_param_data {
long paramno; /* if -1, then it has a name, and we don't know the index *yet* */
char *name;
long namelen;
long max_value_len; /* as a hint for pre-allocation */
zval *parameter; /* the variable itself */
enum pdo_param_type param_type; /* desired or suggested type */
zval *driver_params; /* optional parameter(s) for the driver */
void *driver_data;
pdo_stmt_t *stmt; /* for convenience in dtor */
int is_param; /* parameter or column ? */
};
/* represents a prepared statement */
struct _pdo_stmt_t {
/* driver specifics */
struct pdo_stmt_methods *methods;
void *driver_data;
/* if true, we've already successfully executed this statement at least
* once */
unsigned executed:1;
unsigned _reserved:31;
/* the number of columns in the result set; not valid until after
* the statement has been executed at least once. In some cases, might
* not be valid until fetch (at the driver level) has been called at least once.
* */
int column_count;
struct pdo_column_data *columns;
/* we want to keep the dbh alive while we live, so we own a reference */
zval database_object_handle;
pdo_dbh_t *dbh;
/* keep track of bound input parameters. Some drivers support
* input/output parameters, but you can't rely on that working */
HashTable *bound_params;
/* keep track of PHP variables bound to named (or positional) columns
* in the result set */
HashTable *bound_columns;
/* not always meaningful */
long row_count;
};
/* call this in MINIT to register your PDO driver */
PDO_API int php_pdo_register_driver(pdo_driver_t *driver);
/* call this in MSHUTDOWN to unregister your PDO driver */
PDO_API void php_pdo_unregister_driver(pdo_driver_t *driver);
/* For the convenience of drivers, this function will parse a data source
* string, of the form "name=value; name2=value2" and populate variables
* according to the data you pass in and array of pdo_data_src_parser structures */
struct pdo_data_src_parser {
const char *optname;
char *optval;
int freeme;
};
PDO_API int php_pdo_parse_data_source(const char *data_source,
unsigned long data_source_len, struct pdo_data_src_parser *parsed,
int nparams);
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/

46
ext/pdo/php_pdo_int.h Executable file
View File

@ -0,0 +1,46 @@
/*
+----------------------------------------------------------------------+
| PHP Version 5 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2004 The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.0 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_0.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: Wez Furlong <wez@php.net> |
| Marcus Boerger <helly@php.net> |
| Sterling Hughes <sterling@php.net> |
+----------------------------------------------------------------------+
*/
/* $Id$ */
/* Stuff private to the PDO extension and not for consumption by PDO drivers
* */
extern zend_class_entry *pdo_exception_ce;
extern zend_object_value pdo_dbh_new(zend_class_entry *ce TSRMLS_DC);
extern function_entry pdo_dbh_functions[];
extern zend_class_entry *pdo_dbh_ce;
extern zend_object_value pdo_dbstmt_new(zend_class_entry *ce TSRMLS_DC);
extern function_entry pdo_dbstmt_functions[];
extern zend_class_entry *pdo_dbstmt_ce;
void pdo_dbstmt_free_storage(void *object TSRMLS_DC);
extern zend_object_handlers pdo_dbstmt_object_handlers;
extern pdo_driver_t *pdo_find_driver(const char *name, int namelen);
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
* vim600: noet sw=4 ts=4 fdm=marker
* vim<600: noet sw=4 ts=4
*/