]> granicus.if.org Git - php/commitdiff
- Allow to derive PDOStatement
authorMarcus Boerger <helly@php.net>
Tue, 22 Feb 2005 19:27:34 +0000 (19:27 +0000)
committerMarcus Boerger <helly@php.net>
Tue, 22 Feb 2005 19:27:34 +0000 (19:27 +0000)
- Verify fetch modes
- Add last fetch mode PDO_FETCH_FUNC (only valid inside fetchAll()) that
  allows to completley customize the way data is treated on the fly

ext/pdo/pdo.c
ext/pdo/pdo_dbh.c
ext/pdo/pdo_stmt.c
ext/pdo/php_pdo_driver.h
ext/pdo/php_pdo_int.h

index 117729636d7f6843ccabfc2dbddfacec0fe348d4..a99edac3688e227e704340470a8788ecd8dd9f1d 100755 (executable)
@@ -237,6 +237,7 @@ PHP_MINIT_FUNCTION(pdo)
        REGISTER_LONG_CONSTANT("PDO_FETCH_COLUMN",(long)PDO_FETCH_COLUMN,       CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_FETCH_CLASS",(long)PDO_FETCH_CLASS, CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_FETCH_INTO", (long)PDO_FETCH_INTO,  CONST_CS|CONST_PERSISTENT);
+       REGISTER_LONG_CONSTANT("PDO_FETCH_FUNC", (long)PDO_FETCH_FUNC,  CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_FETCH_GROUP",(long)PDO_FETCH_GROUP, CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_FETCH_UNIQUE",(long)PDO_FETCH_UNIQUE, CONST_CS|CONST_PERSISTENT);
        REGISTER_LONG_CONSTANT("PDO_FETCH_CLASSTYPE",(long)PDO_FETCH_CLASSTYPE, CONST_CS|CONST_PERSISTENT);
@@ -297,17 +298,17 @@ PHP_MINIT_FUNCTION(pdo)
        zend_declare_property_null(pdo_exception_ce, "errorInfo", sizeof("errorInfo")-1, ZEND_ACC_PUBLIC 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);
+       pdo_dbh_ce->create_object = pdo_dbh_new;
 
        INIT_CLASS_ENTRY(ce, "PDOStatement", pdo_dbstmt_functions);
-       ce.create_object = pdo_dbstmt_new;
        pdo_dbstmt_ce = zend_register_internal_class(&ce TSRMLS_CC);
        pdo_dbstmt_ce->get_iterator = pdo_stmt_iter_get;
+       pdo_dbstmt_ce->create_object = pdo_dbstmt_new;
 
        INIT_CLASS_ENTRY(ce, "PDORow", pdo_row_functions);
-       ce.create_object = pdo_row_new;
        pdo_row_ce = zend_register_internal_class(&ce TSRMLS_CC);
+       pdo_row_ce->create_object = pdo_row_new;
 
        return SUCCESS;
 }
index 32a6e5865cd8614dbb71a4bb6bf0813c556a49a2..98280d430465c6b8a5647619168fc3cf2b6e21ed 100755 (executable)
@@ -385,50 +385,143 @@ static PHP_FUNCTION(dbh_constructor)
 }
 /* }}} */
 
-/* {{{ proto object PDO::prepare(string statment [, array driver_options])
+static zval * pdo_instanciate_stmt(zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args TSRMLS_DC) /* {{{ */
+{
+       if (ctor_args) {
+               if (Z_TYPE_P(ctor_args) != IS_ARRAY) {
+                       zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "Parameter ctor_args must be an array");
+                       return NULL;
+               }
+               if (!dbstmt_ce->constructor) {
+                       zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "Statement object's constructor does not take any arguments", dbstmt_ce->name);
+                       return NULL;
+               }
+       }
+
+       Z_TYPE_P(object) = IS_OBJECT;
+       object_init_ex(object, dbstmt_ce);
+       object->refcount = 1;
+       object->is_ref = 1;
+       
+       if (dbstmt_ce->constructor) {
+               zend_fcall_info fci;
+               zend_fcall_info_cache fcc;
+               zval *retval;
+
+               fci.size = sizeof(zend_fcall_info);
+               fci.function_table = &dbstmt_ce->function_table;
+               fci.function_name = NULL;
+               fci.object_pp = &object;
+               fci.symbol_table = NULL;
+               fci.retval_ptr_ptr = &retval;
+               if (ctor_args) {
+                       HashTable *ht = Z_ARRVAL_P(ctor_args);
+                       Bucket *p;
+
+                       fci.param_count = 0;
+                       fci.params = safe_emalloc(sizeof(zval*), ht->nNumOfElements, 0);
+                       p = ht->pListHead;
+                       while (p != NULL) {
+                               fci.params[fci.param_count++] = (zval**)p->pData;
+                               p = p->pListNext;
+                       }
+               } else {
+                       fci.param_count = 0;
+                       fci.params = NULL;
+               }
+               fci.no_separation = 1;
+
+               fcc.initialized = 1;
+               fcc.function_handler = dbstmt_ce->constructor;
+               fcc.calling_scope = EG(scope);
+               fcc.object_pp = &object;
+
+               if (zend_call_function(&fci, &fcc TSRMLS_CC) == FAILURE) {
+                       zval_dtor(object);
+                       ZVAL_NULL(object);
+                       object = NULL; /* marks failure */
+               } else {
+                       zval_ptr_dtor(&retval);
+               }
+                       
+               if (fci.params) {
+                       efree(fci.params);
+               }
+       }
+
+       return object;
+}
+/* }}} */
+
+/* {{{ proto object PDO::prepare(string statment [, array driver_options [, string classname ]])
    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;
-       int statement_len;
-       zval *driver_options = NULL;
+       char *statement, *class_name = NULL;
+       int statement_len, class_name_len;
+       zval *driver_options = NULL, *ctor_args = NULL;
+       zend_class_entry *dbstmt_ce, **pce;
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &statement,
-                       &statement_len, &driver_options)) {
+       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|asz", &statement,
+                       &statement_len, &driver_options, &class_name, &class_name_len, &ctor_args)) {
                RETURN_FALSE;
        }
        
        PDO_DBH_CLEAR_ERR();
-       stmt = ecalloc(1, sizeof(*stmt));
+
+       switch(ZEND_NUM_ARGS()) {
+       case 4:
+       case 3:
+               if (zend_lookup_class(class_name, class_name_len, &pce TSRMLS_CC) == FAILURE) {
+                       RETURN_FALSE;
+               }
+               if (!instanceof_function(*pce, pdo_dbstmt_ce TSRMLS_CC)) {
+                       zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "The provided statement class must be derived from %s", pdo_dbstmt_ce->name);
+                       RETURN_FALSE;
+               }
+               dbstmt_ce = *pce;
+               if (dbstmt_ce->constructor && !(dbstmt_ce->constructor->common.fn_flags & ZEND_ACC_PRIVATE)) {
+                       zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "The provided statement class %s must not have a protected or public constructor", dbstmt_ce->name);
+                       RETURN_FALSE;
+               }
+               break;
+
+       case 2:
+       case 1:
+       case 0:
+               dbstmt_ce = pdo_dbstmt_ce;
+               break;
+       }
+       
+       if (!pdo_instanciate_stmt(return_value, dbstmt_ce, ctor_args TSRMLS_CC)) {
+               zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "Failed to instanciate statement class %s", dbstmt_ce->name);
+               return;
+       }
+       stmt = (pdo_stmt_t*)zend_object_store_get_object(return_value TSRMLS_CC);
+       
        /* unconditionally keep this for later reference */
        stmt->query_string = estrndup(statement, statement_len);
        stmt->query_stringlen = statement_len;
        stmt->default_fetch_type = PDO_FETCH_BOTH;
        stmt->dbh = dbh;
+       /* give it a reference to me */
+       zend_objects_store_add_ref(getThis() TSRMLS_CC);
+       stmt->database_object_handle = *getThis();
+       /* we haven't created a lazy object yet */
+       ZVAL_NULL(&stmt->lazy_object_ref);
 
        if (dbh->methods->preparer(dbh, statement, statement_len, stmt, driver_options 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;
-
-               /* we haven't created a lazy object yet */
-               ZVAL_NULL(&stmt->lazy_object_ref);
 
-               stmt->refcount = 1;
                return;
        }
 
-       efree(stmt->query_string);
-       efree(stmt);
        PDO_HANDLE_DBH_ERR();
+
+       /* kill the object handle for the stmt here */
+       zval_dtor(return_value);
+
        RETURN_FALSE;
 }
 /* }}} */
@@ -718,33 +811,28 @@ static PHP_METHOD(PDO, query)
        }
        
        PDO_DBH_CLEAR_ERR();
-       stmt = ecalloc(1, sizeof(*stmt));
+
+       if (!pdo_instanciate_stmt(return_value, pdo_dbstmt_ce, NULL TSRMLS_CC)) {
+               zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "Failed to instanciate statement class %s", pdo_dbstmt_ce->name);
+               return;
+       }
+       stmt = (pdo_stmt_t*)zend_object_store_get_object(return_value TSRMLS_CC);
+       
        /* unconditionally keep this for later reference */
        stmt->query_string = estrndup(statement, statement_len);
        stmt->query_stringlen = statement_len;
        stmt->default_fetch_type = PDO_FETCH_BOTH;
        stmt->active_query_string = stmt->query_string;
        stmt->active_query_stringlen = statement_len;
+       stmt->dbh = dbh;
+       /* give it a reference to me */
+       zend_objects_store_add_ref(getThis() TSRMLS_CC);
+       stmt->database_object_handle = *getThis();
+       /* we haven't created a lazy object yet */
+       ZVAL_NULL(&stmt->lazy_object_ref);
 
        if (dbh->methods->preparer(dbh, statement, statement_len, stmt, driver_options 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;
-
-               /* we haven't created a lazy object yet */
-               ZVAL_NULL(&stmt->lazy_object_ref);
-
-               stmt->refcount = 1;
-
-               if (ZEND_NUM_ARGS() == 1 ||
-                               SUCCESS == pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU,
-                               stmt, 1)) {
+               if (1) {//ZEND_NUM_ARGS() > 1 || SUCCESS == pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, stmt, 1)) {
                        PDO_STMT_CLEAR_ERR();
 
                        /* now execute the statement */
@@ -763,14 +851,13 @@ static PHP_METHOD(PDO, query)
                        }
                }
                /* something broke */
-               PDO_HANDLE_STMT_ERR();
-               
-               /* TODO: kill the object handle for the stmt here */
-       } else {
-               efree(stmt->query_string);
-               efree(stmt);
-               PDO_HANDLE_DBH_ERR();
        }
+
+       PDO_HANDLE_STMT_ERR();
+               
+       /* kill the object handle for the stmt here */
+       zval_dtor(return_value);
+
        RETURN_FALSE;
 }
 /* }}} */
@@ -1060,7 +1147,7 @@ static void dbh_free(pdo_dbh_t *dbh TSRMLS_DC)
        pefree(dbh, dbh->is_persistent);
 }
 
-static void pdo_dbh_free_storage(zend_object *object TSRMLS_DC)
+static void pdo_dbh_free_storage(void *object TSRMLS_DC)
 {
        pdo_dbh_t *dbh = (pdo_dbh_t*)object;
        if (!dbh) {
index 77d93717ac9491edc55dc09c09e2679554ac995b..94eef51c3335d6e7a91e905e22545f1988afa2a8 100755 (executable)
@@ -73,7 +73,7 @@ static PHP_FUNCTION(dbstmt_constructor) /* {{{ */
 }
 /* }}} */
 
-static inline int rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_param_data *param TSRMLS_DC)
+static inline int rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_param_data *param TSRMLS_DC) /* {{{ */
 {
        if (stmt->bound_param_map) {
                /* rewriting :name to ? style.
@@ -103,9 +103,10 @@ static inline int rewrite_name_to_position(pdo_stmt_t *stmt, struct pdo_bound_pa
        }
        return 1;       
 }
+/* }}} */
 
 /* trigger callback hook for parameters */
-static int dispatch_param_event(pdo_stmt_t *stmt, enum pdo_param_event event_type TSRMLS_DC)
+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;
@@ -137,8 +138,9 @@ iterate:
 
        return ret;
 }
+/* }}} */
 
-int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC)
+int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
 {
        int col;
 
@@ -194,12 +196,13 @@ int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC)
        }
        return 1;
 }
+/* }}} */
 
-static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value TSRMLS_DC)
+static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value TSRMLS_DC) /* {{{ */
 {
        if (Z_TYPE(stmt->lazy_object_ref) == IS_NULL) {
                Z_TYPE(stmt->lazy_object_ref) = IS_OBJECT;
-               Z_OBJ_HANDLE(stmt->lazy_object_ref) = zend_objects_store_put(stmt, NULL, pdo_row_free_storage, NULL TSRMLS_CC);
+               Z_OBJ_HANDLE(stmt->lazy_object_ref) = zend_objects_store_put(stmt, zend_objects_destroy_object, pdo_row_free_storage, NULL TSRMLS_CC);
                Z_OBJ_HT(stmt->lazy_object_ref) = &pdo_row_object_handlers;
                stmt->refcount++;
        }
@@ -207,8 +210,9 @@ static void get_lazy_object(pdo_stmt_t *stmt, zval *return_value TSRMLS_DC)
        Z_OBJ_HANDLE_P(return_value) = Z_OBJ_HANDLE(stmt->lazy_object_ref);
        Z_OBJ_HT_P(return_value) = Z_OBJ_HT(stmt->lazy_object_ref);
 }
+/* }}} */
 
-static void param_dtor(void *data)
+static void param_dtor(void *data) /* {{{ */
 {
        struct pdo_bound_param_data *param = (struct pdo_bound_param_data *)data;
        TSRMLS_FETCH();
@@ -227,8 +231,9 @@ static void param_dtor(void *data)
                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)
+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;
@@ -296,7 +301,7 @@ static int really_register_bound_param(struct pdo_bound_param_data *param, pdo_s
 
        return 1;
 }
-
+/* }}} */
 
 /* {{{ proto bool PDOStatement::execute([array $bound_input_params])
    Execute a prepared statement, optionally binding parameters */
@@ -401,7 +406,7 @@ static PHP_METHOD(PDOStatement, execute)
 }
 /* }}} */
 
-static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno TSRMLS_DC)
+static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno TSRMLS_DC) /* {{{ */
 {
        struct pdo_column_data *col;
        char *value = NULL;
@@ -475,9 +480,10 @@ static inline void fetch_value(pdo_stmt_t *stmt, zval *dest, int colno TSRMLS_DC
                efree(value);
        }
 }
+/* }}} */
 
 static int do_fetch_common(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,
-       long offset, int do_bind TSRMLS_DC)
+       long offset, int do_bind TSRMLS_DC) /* {{{ */
 {
        if (!dispatch_param_event(stmt, PDO_PARAM_EVT_FETCH_PRE TSRMLS_CC)) {
                return 0;
@@ -523,8 +529,9 @@ static int do_fetch_common(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,
 
        return 1;
 }
+/* }}} */
 
-static int do_fetch_class_prepare(pdo_stmt_t *stmt TSRMLS_DC)
+static int do_fetch_class_prepare(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
 {
        zend_class_entry * ce = stmt->fetch.cls.ce;
 
@@ -569,28 +576,131 @@ static int do_fetch_class_prepare(pdo_stmt_t *stmt TSRMLS_DC)
                return 1; /* no ctor no args is also ok */
        }
 }
+/* }}} */
+
+static int make_callable_ex(zval *callable, zend_fcall_info * fci, zend_fcall_info_cache * fcc, int num_args TSRMLS_DC) /* {{{ */
+{
+       zval **object = NULL, **method;
+       char *fname, *cname;
+       zend_class_entry * ce = NULL, **pce;
+       zend_function *function_handler;
+       
+       if (Z_TYPE_P(callable) == IS_ARRAY) {
+               if (Z_ARRVAL_P(callable)->nNumOfElements < 2) {
+                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Argument function must be a valid function name or an array specifying object or class and method name");
+                       return 0;
+               }
+               object = (zval**)Z_ARRVAL_P(callable)->pListHead->pData;
+               method = (zval**)Z_ARRVAL_P(callable)->pListHead->pListNext->pData;
+
+               if (Z_TYPE_PP(object) == IS_STRING) { /* static call */
+                       object = NULL;
+               } else if (Z_TYPE_PP(object) == IS_OBJECT) { /* object call */
+                       ce = Z_OBJCE_PP(object);
+               } else {
+                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Argument function may be an array but the provided array did neither contain a class name nor an object as first member");
+                       return 0;
+               }
+               
+               if (Z_TYPE_PP(method) != IS_STRING) {
+                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Argument function may be an array but the provided array did not contain a method name as second member");
+               }
+       }
+       
+       if (!zend_is_callable(callable, 0, &fname)) {
+               zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Argument function must contain something callable");
+               return 0;
+       }
+       
+       /* ATM we do not support array($obj, "CLASS::FUNC") or "CLASS_FUNC" */
+       cname = fname;
+       if ((fname = strstr(fname, "::")) == NULL) {
+               fname = cname;
+               cname = NULL;
+       } else {
+               *fname = '\0';
+               fname += 2;
+       }
+       if (cname) {
+               if (zend_lookup_class(cname, strlen(cname), &pce TSRMLS_CC) == FAILURE) {
+                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Argument function references non existing class %s", cname);
+               } else {
+                       if (ce) {
+                               /* pce must be base of ce or ce itself */
+                               if (ce != *pce && !instanceof_function(ce, *pce TSRMLS_CC)) {
+                                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Argument function would result in calling %s::%s but class %s is not base of %s", (*pce)->name, fname, (*pce)->name, ce->name);
+                               }
+                       }
+                       ce = *pce;
+               }
+       }
+       
+       fci->function_table = ce ? &ce->function_table : EG(function_table);
+       if (zend_hash_find(fci->function_table, fname, strlen(fname)+1, (void **)&function_handler) == FAILURE) {
+               zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Argument function '%s%s%s' not found", cname ? cname : "", cname ? "::" : "", fname);
+               efree(cname ? cname : fname);
+               return 0;
+       }
+       efree(cname ? cname : fname);
+
+       fci->size = sizeof(zend_fcall_info);
+       fci->function_name = NULL;
+       fci->symbol_table = NULL;
+       fci->param_count = num_args; /* probably less */
+       fci->params = safe_emalloc(sizeof(zval**), num_args, 0);
+       fci->object_pp = object;
+
+       fcc->initialized = 1;
+       fcc->function_handler = function_handler;
+       fcc->calling_scope = EG(scope);
+       fcc->object_pp = object;
+       
+       return 1;
+}
+/* }}} */
 
-static int do_fetch_class_finish(pdo_stmt_t *stmt, int free_ctor_agrs TSRMLS_DC)
+static int do_fetch_func_prepare(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */
+{
+       zend_fcall_info * fci = &stmt->fetch.cls.fci;
+       zend_fcall_info_cache * fcc = &stmt->fetch.cls.fcc;
+
+       if (!make_callable_ex(stmt->fetch.func.function, fci, fcc, stmt->column_count TSRMLS_CC)) {
+               return 0;
+       } else {
+               stmt->fetch.func.values = safe_emalloc(sizeof(zval*), stmt->column_count, 0);
+               return 1;
+       }
+}
+/* }}} */
+
+static int do_fetch_opt_finish(pdo_stmt_t *stmt, int free_ctor_agrs TSRMLS_DC) /* {{{ */
 {
        /* fci.size is used to check if it is valid */
        if (stmt->fetch.cls.fci.size && stmt->fetch.cls.fci.params) {
                efree(stmt->fetch.cls.fci.params);
+               stmt->fetch.cls.fci.params = NULL;
                stmt->fetch.cls.fci.size = 0;
        }
        if (stmt->fetch.cls.ctor_args && free_ctor_agrs) {
                FREE_ZVAL(stmt->fetch.cls.ctor_args);
                stmt->fetch.cls.ctor_args = NULL;
        }
+       if (stmt->fetch.func.values && free_ctor_agrs) {
+               FREE_ZVAL(stmt->fetch.func.values);
+               stmt->fetch.func.values = NULL;
+       }
        return 1;
 }
+/* }}} */
 
 /* 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, enum pdo_fetch_orientation ori, long offset, zval *return_all TSRMLS_DC)
+       enum pdo_fetch_type how, enum pdo_fetch_orientation ori, long offset, zval *return_all TSRMLS_DC) /* {{{ */
 {
-       int flags = how & PDO_FETCH_FLAGS;
+       int flags = how & PDO_FETCH_FLAGS, idx;
        zend_class_entry * ce, * old_ce;
+       zval grp_val, *grp, **pgrp, *retval;
 
        how = how & ~PDO_FETCH_FLAGS;
        if (how == PDO_FETCH_USE_DEFAULT) {
@@ -637,7 +747,7 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value,
                                        zval val;
                                        zend_class_entry **cep;
 
-                                       do_fetch_class_finish(stmt, 0 TSRMLS_CC);
+                                       do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
                                        old_ce = stmt->fetch.cls.ce;
 
                                        INIT_PZVAL(&val);
@@ -675,33 +785,29 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value,
                                }
                                break;
 
+                       case PDO_FETCH_FUNC:
+                               if (!stmt->fetch.func.fci.size) {
+                                       if (!do_fetch_func_prepare(stmt TSRMLS_CC))
+                                       {
+                                               return 0;
+                                       }
+                               }
+                               break;
+                               
+
                        default:
                                /* shouldn't happen */
                                return 0;
                }
                
                if (return_all) {
-                       zval val, *grp, **pgrp;
-                       INIT_PZVAL(&val);
-                       fetch_value(stmt, &val, i TSRMLS_CC);
-                       convert_to_string(&val);
-                       if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
-                               add_assoc_zval(return_all, Z_STRVAL(val), return_value);
-                       } else {
-                               if (zend_symtable_find(Z_ARRVAL_P(return_all), Z_STRVAL(val), Z_STRLEN(val)+1, (void**)&pgrp) == FAILURE) {
-                                       MAKE_STD_ZVAL(grp);
-                                       array_init(grp);
-                                       add_assoc_zval(return_all, Z_STRVAL(val), grp);
-                               } else {
-                                       grp = *pgrp;
-                               }
-                               add_next_index_zval(grp, return_value);
-                       }
-                       zval_dtor(&val);
+                       INIT_PZVAL(&grp_val);
+                       fetch_value(stmt, &grp_val, i TSRMLS_CC);
+                       convert_to_string(&grp_val);
                        i++;
                }
 
-               for (; i < stmt->column_count; i++) {
+               for (idx = 0; i < stmt->column_count; i++, idx++) {
                        zval *val;
                        MAKE_STD_ZVAL(val);
                        fetch_value(stmt, val, i TSRMLS_CC);
@@ -734,7 +840,13 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value,
                                                val TSRMLS_CC);
                                        break;
                                
+                               case PDO_FETCH_FUNC:
+                                       stmt->fetch.func.values[idx] = val;
+                                       stmt->fetch.cls.fci.params[idx] = &stmt->fetch.func.values[idx];
+                                       break;
+                               
                                default:
+                                       zval_ptr_dtor(&val);
                                        pdo_raise_impl_error(stmt->dbh, stmt, "22003", "mode is out of range" TSRMLS_CC);
                                        break;
                        }
@@ -754,18 +866,91 @@ static int do_fetch(pdo_stmt_t *stmt, int do_bind, zval *return_value,
                                        }
                                }
                                if (flags & PDO_FETCH_CLASSTYPE) {
-                                       do_fetch_class_finish(stmt, 0 TSRMLS_CC);
+                                       do_fetch_opt_finish(stmt, 0 TSRMLS_CC);
                                        stmt->fetch.cls.ce = old_ce;
                                }
                                break;
+
+                       case PDO_FETCH_FUNC:
+                               stmt->fetch.func.fci.param_count = idx;
+                               stmt->fetch.func.fci.retval_ptr_ptr = &retval;
+                               if (zend_call_function(&stmt->fetch.func.fci, &stmt->fetch.func.fcc TSRMLS_CC) == FAILURE) {
+                                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Could not execute %s%s%s()", ce->name, ce->constructor->common.function_name);
+                               } else {
+                                       if (return_all) {
+                                               zval_ptr_dtor(&return_value); /* we don't need that */
+                                               return_value = retval;
+                                       } else {
+                                               *return_value = *retval;
+                                               zval_copy_ctor(return_value);
+                                               zval_ptr_dtor(&retval);
+                                       }
+                               }
+                               while(idx--) {
+                                       zval_ptr_dtor(&stmt->fetch.func.values[idx]);
+                               }
+                               break;
                        
                        default:
                                break;
                }
+
+               if (return_all) {
+                       if ((flags & PDO_FETCH_UNIQUE) == PDO_FETCH_UNIQUE) {
+                               add_assoc_zval(return_all, Z_STRVAL(grp_val), return_value);
+                       } else {
+                               if (zend_symtable_find(Z_ARRVAL_P(return_all), Z_STRVAL(grp_val), Z_STRLEN(grp_val)+1, (void**)&pgrp) == FAILURE) {
+                                       MAKE_STD_ZVAL(grp);
+                                       array_init(grp);
+                                       add_assoc_zval(return_all, Z_STRVAL(grp_val), grp);
+                               } else {
+                                       grp = *pgrp;
+                               }
+                               add_next_index_zval(grp, return_value);
+                       }
+                       zval_dtor(&grp_val);
+               }
+
        }
 
        return 1;
 }
+/* }}} */
+
+static int pdo_stmt_verify_mode(pdo_stmt_t *stmt, int mode, int fetch_all TSRMLS_DC) /* {{{ */
+{
+       int flags = mode & PDO_FETCH_FLAGS;
+       
+       mode = mode & ~PDO_FETCH_FLAGS;
+
+       if (mode == PDO_FETCH_USE_DEFAULT) {
+               mode = stmt->default_fetch_type;
+       }
+
+       switch(mode) {
+       case PDO_FETCH_FUNC:
+               if (!fetch_all) {
+                       zend_throw_exception(pdo_exception_ce, "Fetch mode PDO_FETCH_FUNC is only allowed in PDOStatement::fetchAll()", 0 TSRMLS_CC);
+                       return 0;
+               }
+               return 1;
+       
+       default:
+               if ((flags & PDO_FETCH_CLASSTYPE) == PDO_FETCH_CLASSTYPE) {
+                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Fetch mode flag PDO_FETCH_CLASSTYPE requires mode PDO_FETCH_CLASS", mode);
+                       return 0;
+               }
+               /* no break; */
+
+       case PDO_FETCH_CLASS:
+               if (mode >= PDO_FETCH__MAX) {
+                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Fetch mode %d is invalid", mode);
+                       return 0;
+               }
+               return 1;
+       }
+}
+/* }}} */
 
 /* {{{ proto mixed PDOStatement::fetch([int $how = PDO_FETCH_BOTH [, int $orientation [, int $offset]]])
    Fetches the next row and returns it, or false if there are no more rows */
@@ -782,6 +967,11 @@ static PHP_METHOD(PDOStatement, fetch)
        }
 
        PDO_STMT_CLEAR_ERR();
+
+       if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
+               RETURN_FALSE;
+       }
+
        if (!do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
                PDO_HANDLE_STMT_ERR();
                RETURN_FALSE;
@@ -812,6 +1002,12 @@ static PHP_METHOD(PDOStatement, fetchObject)
                RETURN_FALSE;
        }
 
+       PDO_STMT_CLEAR_ERR();
+
+       if (!pdo_stmt_verify_mode(stmt, how, 0 TSRMLS_CC)) {
+               RETURN_FALSE;
+       }
+
        switch(ZEND_NUM_ARGS()) {
        case 0:
                stmt->fetch.cls.ce = zend_standard_class_def;
@@ -838,7 +1034,6 @@ static PHP_METHOD(PDOStatement, fetchObject)
                }
        }
 
-       PDO_STMT_CLEAR_ERR();
        if (!error && !do_fetch(stmt, TRUE, return_value, how, ori, off, 0 TSRMLS_CC)) {
                error = 1;
                PDO_HANDLE_STMT_ERR();
@@ -862,6 +1057,7 @@ static PHP_METHOD(PDOStatement, fetchSingle)
        }
 
        PDO_STMT_CLEAR_ERR();
+
        if (!do_fetch_common(stmt, PDO_FETCH_ORI_NEXT, 0, TRUE TSRMLS_CC)) {
                PDO_HANDLE_STMT_ERR();
                RETURN_FALSE;
@@ -878,50 +1074,83 @@ static PHP_METHOD(PDOStatement, fetchAll)
        pdo_stmt_t *stmt = (pdo_stmt_t*)zend_object_store_get_object(getThis() TSRMLS_CC);
        long how = PDO_FETCH_USE_DEFAULT;
        zval *data, *return_all;
-       char *class_name;
-       int class_name_len;
+       zval *arg2;
        zend_class_entry *old_ce;
        zval *old_ctor_args, *ctor_args;
        int error = 0;
 
        old_ce = stmt->fetch.cls.ce;
        old_ctor_args = stmt->fetch.cls.ctor_args;
+       stmt->fetch.cls.ctor_args = NULL;
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lsz", &how, &class_name, &class_name_len, &ctor_args)) {
+       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lzz", &how, &arg2, &ctor_args)) {
                RETURN_FALSE;
        }
-       
-       switch(ZEND_NUM_ARGS()) {
-       case 0:
-       case 1:
-               stmt->fetch.cls.ce = zend_standard_class_def;
-               break;
-       case 3:
-               if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
-                       zend_throw_exception(pdo_exception_ce, "Parameter ctor_args must either be NULL or an array ", 0 TSRMLS_CC);
-                       error = 1;
+
+       if (!pdo_stmt_verify_mode(stmt, how, 1 TSRMLS_CC)) {
+               RETURN_FALSE;
+       }
+
+       switch(how & ~PDO_FETCH_FLAGS) {
+       case PDO_FETCH_CLASS:
+               do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
+               switch(ZEND_NUM_ARGS()) {
+               case 0:
+               case 1:
+                       stmt->fetch.cls.ce = zend_standard_class_def;
                        break;
+               case 3:
+                       if (Z_TYPE_P(ctor_args) != IS_NULL && Z_TYPE_P(ctor_args) != IS_ARRAY) {
+                               zend_throw_exception(pdo_exception_ce, "Parameter ctor_args must either be NULL or an array ", 0 TSRMLS_CC);
+                               error = 1;
+                               break;
+                       }
+                       if (Z_TYPE_P(ctor_args) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
+                               stmt->fetch.cls.ctor_args = ctor_args;
+                       } else {
+                               stmt->fetch.cls.ctor_args = NULL;
+                       }
+                       /* no break */
+               case 2:
+                       if (Z_TYPE_P(arg2) != IS_STRING) {
+                               zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "In fetch mode PDO_FETCH_CLASS the 2nd parameter must be a class name string", Z_TYPE_P(arg2));
+                               error = 1;
+                               break;
+                       } else {
+                               stmt->fetch.cls.ce = zend_fetch_class(Z_STRVAL_P(arg2), Z_STRLEN_P(arg2), ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
+                               if (!stmt->fetch.cls.ce) {
+                                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Could not find class '%s'", Z_TYPE_P(arg2));
+                                       error = 1;
+                                       break;
+                               }
+                       }
                }
-               if (Z_TYPE_P(ctor_args) == IS_ARRAY && zend_hash_num_elements(Z_ARRVAL_P(ctor_args))) {
-                       stmt->fetch.cls.ctor_args = ctor_args;
-               } else {
-                       stmt->fetch.cls.ctor_args = NULL;
+//             do_fetch_class_prepare(stmt TSRMLS_CC);
+               break;
+
+       case PDO_FETCH_FUNC:
+               do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
+               switch(ZEND_NUM_ARGS()) {
+               case 0:
+               case 1:
+                       break;
+               case 3:
+               case 2:
+                       stmt->fetch.func.function = arg2;
+                       break;
                }
-               /* no break */
-       case 2:
-               stmt->fetch.cls.ce = zend_fetch_class(class_name, class_name_len, ZEND_FETCH_CLASS_AUTO TSRMLS_CC);
+               do_fetch_func_prepare(stmt TSRMLS_CC);
+               break;
 
-               if (!stmt->fetch.cls.ce) {
-                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Could not find class '%s'", class_name);
+       default:
+               if (ZEND_NUM_ARGS() > 1) {
+                       zend_throw_exception_ex(pdo_exception_ce, 0 TSRMLS_CC, "Additional parameter not allowed for specified fetch mode");
                        error = 1;
-                       break;
                }
        }
 
        if (!error)
        {
-               do_fetch_class_prepare(stmt TSRMLS_CC);
-
                PDO_STMT_CLEAR_ERR();
                MAKE_STD_ZVAL(data);
                if (how & PDO_FETCH_GROUP) {
@@ -952,11 +1181,11 @@ static PHP_METHOD(PDOStatement, fetchAll)
                FREE_ZVAL(data);
        }
        
+       do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
+
        stmt->fetch.cls.ce = old_ce;
        stmt->fetch.cls.ctor_args = old_ctor_args;
        
-       do_fetch_class_finish(stmt, 1 TSRMLS_CC);
-
        if (error) {
                RETURN_FALSE;
        }
@@ -1171,7 +1400,8 @@ int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, in
        
        switch (stmt->default_fetch_type) {
                case PDO_FETCH_CLASS:
-                       do_fetch_class_finish(stmt, 1 TSRMLS_CC);
+               case PDO_FETCH_FUNC:
+                       do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
                        break;
 
                case PDO_FETCH_INTO:
@@ -1200,6 +1430,10 @@ fail_out:
        
        convert_to_long_ex(args[skip]);
        mode = Z_LVAL_PP(args[skip]);
+       
+       if (!pdo_stmt_verify_mode(stmt, mode, 0 TSRMLS_CC)) {
+               return FAILURE;
+       }
 
        switch (mode & ~PDO_FETCH_FLAGS) {
                case PDO_FETCH_LAZY:
@@ -1463,26 +1697,17 @@ static int dbstmt_call_method(char *method, INTERNAL_FUNCTION_PARAMETERS)
 
 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;
+       return std_object_handlers.get_constructor(object TSRMLS_CC);
 }
 
 static zend_class_entry *dbstmt_get_ce(zval *object TSRMLS_DC)
 {
-       return pdo_dbstmt_ce;
+       return std_object_handlers.get_class_entry(object TSRMLS_CC);
 }
 
 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;
+       return std_object_handlers.get_class_name(object, class_name, class_name_len, parent TSRMLS_CC);
 }
 
 static int dbstmt_compare(zval *object1, zval *object2 TSRMLS_DC)
@@ -1516,6 +1741,12 @@ zend_object_handlers pdo_dbstmt_object_handlers = {
 
 static void free_statement(pdo_stmt_t *stmt TSRMLS_DC)
 {
+       if (stmt->properties) {
+               zend_hash_destroy(stmt->properties);
+               efree(stmt->properties);
+               stmt->properties = NULL;
+       }
+
        if (stmt->methods && stmt->methods->dtor) {
                stmt->methods->dtor(stmt TSRMLS_CC);
        }
@@ -1546,13 +1777,13 @@ static void free_statement(pdo_stmt_t *stmt TSRMLS_DC)
                FREE_HASHTABLE(stmt->bound_columns);
        }
        
-       do_fetch_class_finish(stmt, 1 TSRMLS_CC);
+       do_fetch_opt_finish(stmt, 1 TSRMLS_CC);
 
        zend_objects_store_del_ref(&stmt->database_object_handle TSRMLS_CC);
        efree(stmt);
 }
 
-void pdo_dbstmt_free_storage(zend_object *object TSRMLS_DC)
+void pdo_dbstmt_free_storage(void *object TSRMLS_DC)
 {
        pdo_stmt_t *stmt = (pdo_stmt_t*)object;
 
@@ -1565,7 +1796,15 @@ 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);
+       pdo_stmt_t *stmt;
+       stmt = emalloc(sizeof(*stmt));
+       memset(stmt, 0, sizeof(*stmt));
+       stmt->ce = ce;
+       stmt->refcount = 1;
+       ALLOC_HASHTABLE(stmt->properties);
+       zend_hash_init(stmt->properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+
+       retval.handle = zend_objects_store_put(stmt, zend_objects_destroy_object, pdo_dbstmt_free_storage, NULL TSRMLS_CC);
        retval.handlers = &pdo_dbstmt_object_handlers;
 
        return retval;
@@ -1865,7 +2104,7 @@ zend_object_handlers pdo_row_object_handlers = {
        NULL
 };
 
-void pdo_row_free_storage(zend_object *object TSRMLS_DC)
+void pdo_row_free_storage(void  *object TSRMLS_DC)
 {
        pdo_stmt_t *stmt = (pdo_stmt_t*)object;
 
@@ -1880,7 +2119,7 @@ zend_object_value pdo_row_new(zend_class_entry *ce TSRMLS_DC)
 {
        zend_object_value retval;
 
-       retval.handle = zend_objects_store_put(NULL, NULL, pdo_row_free_storage, NULL TSRMLS_CC);
+       retval.handle = zend_objects_store_put(NULL, zend_objects_destroy_object, pdo_row_free_storage, NULL TSRMLS_CC);
        retval.handlers = &pdo_row_object_handlers;
 
        return retval;
index 9e8f0cb9b1ce868131c536ebecdd54e98a30da1c..f4e70a939af584d0010374ea819c19c7b1351432 100755 (executable)
@@ -35,7 +35,7 @@ struct pdo_bound_param_data;
 # define FALSE 0
 #endif
 
-#define PDO_DRIVER_API 20050220
+#define PDO_DRIVER_API 20050222
 
 enum pdo_param_type {
        PDO_PARAM_NULL,
@@ -78,6 +78,7 @@ enum pdo_fetch_type {
        PDO_FETCH_COLUMN,       /* fetch a numbered column only */
        PDO_FETCH_CLASS,        /* create an instance of named class, call ctor and set properties */
        PDO_FETCH_INTO,         /* fetch row into an existing object */
+       PDO_FETCH_FUNC,         /* fetch into function and return its result */
        PDO_FETCH__MAX /* must be last */
 };
 
@@ -484,6 +485,16 @@ struct pdo_bound_param_data {
 
 /* represents a prepared statement */
 struct _pdo_stmt_t {
+       /* these items must appear in this order at the beginning of the
+       struct so that this can be cast as a zend_object.  we need this
+       to allow the extending class to escape all the custom handlers
+          that PDO declares.
+    */
+       zend_class_entry *ce; 
+       HashTable *properties;
+       unsigned int in_get:1;
+       unsigned int in_set:1;
+
        /* driver specifics */
        struct pdo_stmt_methods *methods;
        void *driver_data;
@@ -543,11 +554,19 @@ struct _pdo_stmt_t {
                int column;
                struct {
                        zend_class_entry *ce;   
-                       zval *ctor_args;
+                       zval *ctor_args;            /* freed */
                        zval *retval_ptr; 
                        zend_fcall_info fci;
                        zend_fcall_info_cache fcc;
                } cls;
+               struct {
+                       zval *function;
+                       zval *fetch_args;           /* freed */
+                       zval *object;
+                       zend_fcall_info fci;
+                       zend_fcall_info_cache fcc;
+                       zval **values;              /* freed */
+               } func;
                zval *into;
        } fetch;
 };
index fed5c861f447524b82dfefbd7ac40898bee69b2e..6f300ccec51450f600f2f75f61544bf8bb23d9d8 100755 (executable)
@@ -33,7 +33,7 @@ extern ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor);
 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(zend_object *object TSRMLS_DC);
+void pdo_dbstmt_free_storage(void *object TSRMLS_DC);
 zend_object_iterator *pdo_stmt_iter_get(zend_class_entry *ce, zval *object TSRMLS_DC);
 extern zend_object_handlers pdo_dbstmt_object_handlers;
 int pdo_stmt_describe_columns(pdo_stmt_t *stmt TSRMLS_DC);
@@ -42,7 +42,7 @@ int pdo_stmt_setup_fetch_mode(INTERNAL_FUNCTION_PARAMETERS, pdo_stmt_t *stmt, in
 extern zend_object_value pdo_row_new(zend_class_entry *ce TSRMLS_DC);
 extern function_entry pdo_row_functions[];
 extern zend_class_entry *pdo_row_ce;
-void pdo_row_free_storage(zend_object *object TSRMLS_DC);
+void pdo_row_free_storage(void *object TSRMLS_DC);
 extern zend_object_handlers pdo_row_object_handlers;
 
 zend_object_iterator *php_pdo_dbstmt_iter_get(zend_class_entry *ce, zval *object TSRMLS_DC);