]> granicus.if.org Git - php/commitdiff
Add oo API
authorMarcus Boerger <helly@php.net>
Sun, 22 Jun 2003 17:19:46 +0000 (17:19 +0000)
committerMarcus Boerger <helly@php.net>
Sun, 22 Jun 2003 17:19:46 +0000 (17:19 +0000)
17 files changed:
ext/sqlite/php_sqlite.h
ext/sqlite/sqlite.c
ext/sqlite/tests/blankdb_oo.inc [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_001.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_002.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_003.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_008.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_009.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_010.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_011.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_012.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_013.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_014.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_015.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_016.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_oo_020.phpt [new file with mode: 0755]
ext/sqlite/tests/sqlite_spl_001.phpt [new file with mode: 0755]

index 3f937e4656e18034cdab59c82d7b09bd46dbeaad..ea1aa45389226fcc18ab1616c9d81c7000755a6b 100644 (file)
@@ -81,8 +81,10 @@ PHP_FUNCTION(sqlite_create_function);
 PHP_FUNCTION(sqlite_udf_decode_binary);
 PHP_FUNCTION(sqlite_udf_encode_binary);
 
+PHP_FUNCTION(sqlite_factory);
+
 ZEND_BEGIN_MODULE_GLOBALS(sqlite)
-        long assoc_case;
+        int assoc_case;
 ZEND_END_MODULE_GLOBALS(sqlite)
 
 #ifdef ZTS
index fdc8abf90977d4305f66f77a4c0a054096870de9..e7b15a8c932e30127f51e41262ceac45cdec8fbe 100644 (file)
@@ -24,7 +24,7 @@
 #include "config.h"
 #endif
 
-#define PHP_SQLITE_MODULE_VERSION      "1.0"
+#define PHP_SQLITE_MODULE_VERSION      "1.1-dev"
 
 #include "php.h"
 #include "php_ini.h"
 
 #include <sqlite.h>
 
+#ifdef HAVE_SPL
+#include "ext/spl/php_spl.h"
+#include "ext/spl/spl_functions.h"
+#endif
+
+#include "Zend/zend_default_classes.h"
+
 #ifndef safe_emalloc
 # define safe_emalloc(a,b,c) emalloc((a)*(b)+(c))
 #endif
@@ -81,6 +88,42 @@ PHP_INI_END()
 
 #define DB_FROM_ZVAL(db, zv)   ZEND_FETCH_RESOURCE2(db, struct php_sqlite_db *, zv, -1, "sqlite database", le_sqlite_db, le_sqlite_pdb)
 
+#define DB_FROM_OBJECT(db, object) \
+       { \
+               sqlite_object *obj = (sqlite_object*) zend_object_store_get_object(object TSRMLS_CC); \
+               db = obj->u.db; \
+               if (!db) { \
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "The database wasn't opened"); \
+                       RETURN_NULL(); \
+               } \
+       }
+
+#define RES_FROM_OBJECT(res, object) \
+       { \
+               sqlite_object *obj = (sqlite_object*) zend_object_store_get_object(object TSRMLS_CC); \
+               res = obj->u.res; \
+               if (!res) { \
+                       php_error_docref(NULL TSRMLS_CC, E_WARNING, "No result set available"); \
+                       RETURN_NULL(); \
+               } \
+       }
+
+#define SQLITE_THROW(message) \
+       PG(suppress_errors) = 0; \
+       EG(exception) = sqlite_instanciate(sqlite_ce_exception, NULL TSRMLS_CC); \
+       { \
+               zval *tmp; \
+               MAKE_STD_ZVAL(tmp); \
+               ZVAL_STRING(tmp, message, 1); \
+               zend_hash_update(Z_OBJPROP_P(EG(exception)), "message", sizeof("message"), (void **) message, sizeof(zval *), NULL); \
+               MAKE_STD_ZVAL(tmp); \
+               ZVAL_STRING(tmp, zend_get_executed_filename(TSRMLS_C), 1); \
+               zend_hash_update(Z_OBJPROP_P(EG(exception)), "file", sizeof("file"), (void **) &tmp, sizeof(zval *), NULL); \
+               MAKE_STD_ZVAL(tmp); \
+               ZVAL_LONG(tmp, zend_get_executed_lineno(TSRMLS_C)); \
+               zend_hash_update(Z_OBJPROP_P(EG(exception)), "line", sizeof("line"), (void **) &tmp, sizeof(zval *), NULL); \
+       }
+
 struct php_sqlite_result {
        struct php_sqlite_db *db;
        sqlite_vm *vm;
@@ -142,11 +185,69 @@ function_entry sqlite_functions[] = {
        PHP_FE(sqlite_unbuffered_query, NULL)
        PHP_FE(sqlite_create_aggregate, NULL)
        PHP_FE(sqlite_create_function, NULL)
+       PHP_FE(sqlite_factory, NULL)
        PHP_FE(sqlite_udf_encode_binary, NULL)
        PHP_FE(sqlite_udf_decode_binary, NULL)
        {NULL, NULL, NULL}
 };
 
+#define PHP_ME_MAPPING(name, func_name, arg_types) \
+       ZEND_NAMED_FE(name, ZEND_FN(func_name), arg_types)
+
+function_entry sqlite_funcs_db[] = {
+       PHP_ME_MAPPING(sqlite_db, sqlite_open, NULL)
+       PHP_ME_MAPPING(close, sqlite_close, NULL)
+       PHP_ME_MAPPING(query, sqlite_query, NULL)
+       PHP_ME_MAPPING(array_query, sqlite_array_query, NULL)
+       PHP_ME_MAPPING(unbuffered_query, sqlite_unbuffered_query, NULL)
+       PHP_ME_MAPPING(last_insert_rowid, sqlite_last_insert_rowid, NULL)
+       PHP_ME_MAPPING(create_aggregate, sqlite_create_aggregate, NULL)
+       PHP_ME_MAPPING(create_function, sqlite_create_function, NULL)
+       PHP_ME_MAPPING(busy_timeout, sqlite_busy_timeout, NULL)
+       PHP_ME_MAPPING(last_error, sqlite_last_error, NULL)
+/*     PHP_ME_MAPPING(error_string, sqlite_error_string, NULL) static */
+/*     PHP_ME_MAPPING(escape_string, sqlite_escape_string, NULL) static */
+       {NULL, NULL, NULL}
+};
+
+function_entry sqlite_funcs_query[] = {
+       PHP_ME_MAPPING(fetch_array, sqlite_fetch_array, NULL)
+       PHP_ME_MAPPING(fetch_string, sqlite_fetch_string, NULL)
+       PHP_ME_MAPPING(fetch_all, sqlite_fetch_all, NULL)
+       PHP_ME_MAPPING(column, sqlite_column, NULL)
+       PHP_ME_MAPPING(changes, sqlite_changes, NULL)
+       PHP_ME_MAPPING(num_fields, sqlite_num_fields, NULL)
+       PHP_ME_MAPPING(field_name, sqlite_field_name, NULL)
+       /* spl_forward */
+       PHP_ME_MAPPING(current, sqlite_current, NULL)
+       PHP_ME_MAPPING(next, sqlite_next, NULL)
+       PHP_ME_MAPPING(has_more, sqlite_has_more, NULL)
+       /* spl_sequence */
+       PHP_ME_MAPPING(rewind, sqlite_rewind, NULL)
+       /* additional */
+       PHP_ME_MAPPING(num_rows, sqlite_num_rows, NULL)
+       PHP_ME_MAPPING(seek, sqlite_seek, NULL)
+       {NULL, NULL, NULL}
+};
+
+function_entry sqlite_funcs_ub_query[] = {
+       PHP_ME_MAPPING(fetch_array, sqlite_fetch_array, NULL)
+       PHP_ME_MAPPING(fetch_string, sqlite_fetch_string, NULL)
+       PHP_ME_MAPPING(fetch_all, sqlite_fetch_all, NULL)
+       PHP_ME_MAPPING(column, sqlite_column, NULL)
+       PHP_ME_MAPPING(changes, sqlite_changes, NULL)
+       PHP_ME_MAPPING(num_fields, sqlite_num_fields, NULL)
+       PHP_ME_MAPPING(field_name, sqlite_field_name, NULL)
+       /* spl_forward */
+       PHP_ME_MAPPING(current, sqlite_current, NULL)
+       PHP_ME_MAPPING(next, sqlite_next, NULL)
+       PHP_ME_MAPPING(has_more, sqlite_has_more, NULL)
+       {NULL, NULL, NULL}
+};
+
+function_entry sqlite_funcs_exception[] = {
+       {NULL, NULL, NULL}
+};
 
 zend_module_entry sqlite_module_entry = {
 #if ZEND_MODULE_API_NO >= 20010901
@@ -156,7 +257,7 @@ zend_module_entry sqlite_module_entry = {
        sqlite_functions,
        PHP_MINIT(sqlite),
        NULL,
-       NULL,
+       PHP_RINIT(sqlite),
        PHP_RSHUTDOWN(sqlite),
        PHP_MINFO(sqlite),
 #if ZEND_MODULE_API_NO >= 20010901
@@ -609,6 +710,150 @@ static int php_sqlite_authorizer(void *autharg, int access_type, const char *arg
 }
 /* }}} */
 
+/* {{{ OO init/structure stuff */
+#define REGISTER_SQLITE_CLASS(name, parent) \
+       { \
+               zend_class_entry ce; \
+               INIT_CLASS_ENTRY(ce, "sqlite_" # name, sqlite_funcs_ ## name); \
+               ce.create_object = sqlite_object_new_ ## name; \
+               sqlite_ce_ ## name = zend_register_internal_class_ex(&ce, parent, NULL TSRMLS_CC); \
+               memcpy(&sqlite_object_handlers_ ## name, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); \
+       }
+
+zend_class_entry *sqlite_ce_db, *sqlite_ce_exception;
+zend_class_entry *sqlite_ce_query, *sqlite_ce_ub_query;
+
+static zend_object_handlers sqlite_object_handlers_db;
+static zend_object_handlers sqlite_object_handlers_query;
+static zend_object_handlers sqlite_object_handlers_ub_query;
+static zend_object_handlers sqlite_object_handlers_exception;
+
+typedef enum {
+       is_db,
+       is_result
+} sqlite_obj_type;
+
+typedef struct _sqlite_object {
+       zend_object       std;
+       sqlite_obj_type   type;
+       union {
+               struct php_sqlite_db     *db;
+               struct php_sqlite_result *res;
+               void *ptr;
+       } u;
+} sqlite_object;
+
+static void sqlite_object_clone(void *object, void **object_clone TSRMLS_DC)
+{
+       /* TODO */
+}
+
+static int sqlite_free_persistent(list_entry *le, void *ptr TSRMLS_DC)
+{
+       return le->ptr == ptr;
+}
+
+static void sqlite_object_dtor(void *object, zend_object_handle handle TSRMLS_DC)
+{
+       sqlite_object *intern = (sqlite_object *)object;
+
+       zend_hash_destroy(intern->std.properties);
+       FREE_HASHTABLE(intern->std.properties);
+
+       if (intern->u.ptr) {
+               if (intern->type == is_db) {
+                       if (intern->u.db->rsrc_id) {
+                               zend_list_delete(intern->u.db->rsrc_id);
+                               zend_hash_apply_with_argument(&EG(persistent_list), (apply_func_arg_t) sqlite_free_persistent, &intern->u.ptr TSRMLS_CC);
+                       }
+               } else {
+                       real_result_dtor(intern->u.res TSRMLS_CC);
+               }
+       }
+
+       efree(object);
+}
+
+static void sqlite_object_new(zend_class_entry *class_type, zend_object_handlers *handlers, zend_object_value *retval TSRMLS_DC)
+{
+       sqlite_object *intern;
+       zval *tmp;
+
+       intern = emalloc(sizeof(sqlite_object));
+       memset(intern, 0, sizeof(sqlite_object));
+       intern->std.ce = class_type;
+
+       ALLOC_HASHTABLE(intern->std.properties);
+       zend_hash_init(intern->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+       zend_hash_copy(intern->std.properties, &class_type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *));
+
+       retval->handle = zend_objects_store_put(intern, sqlite_object_dtor, sqlite_object_clone TSRMLS_CC);
+       retval->handlers = handlers;
+}
+
+static zend_object_value sqlite_object_new_db(zend_class_entry *class_type TSRMLS_DC)
+{
+       zend_object_value retval;
+       
+       sqlite_object_new(class_type, &sqlite_object_handlers_db, &retval TSRMLS_CC);
+       return retval;
+}
+
+static zend_object_value sqlite_object_new_query(zend_class_entry *class_type TSRMLS_DC)
+{
+       zend_object_value retval;
+       
+       sqlite_object_new(class_type, &sqlite_object_handlers_query, &retval TSRMLS_CC);
+       return retval;
+}
+
+static zend_object_value sqlite_object_new_ub_query(zend_class_entry *class_type TSRMLS_DC)
+{
+       zend_object_value retval;
+       
+       sqlite_object_new(class_type, &sqlite_object_handlers_ub_query, &retval TSRMLS_CC);
+       return retval;
+}
+
+static zend_object_value sqlite_object_new_exception(zend_class_entry *class_type TSRMLS_DC)
+{
+       zend_object_value retval;
+       
+       sqlite_object_new(class_type, &sqlite_object_handlers_exception, &retval TSRMLS_CC);
+       return retval;
+}
+
+#define SQLITE_REGISTER_OBJECT(_type, _object, _ptr) \
+       { \
+               sqlite_object *obj; \
+               obj = (sqlite_object*)zend_object_store_get_object(_object TSRMLS_CC); \
+               obj->type = is_ ## _type; \
+               obj->u._type = _ptr; \
+       }
+
+static zend_class_entry *sqlite_get_ce_query(zval *object TSRMLS_DC)
+{
+       return sqlite_ce_query;
+}
+
+static zend_class_entry *sqlite_get_ce_ub_query(zval *object TSRMLS_DC)
+{
+       return sqlite_ce_ub_query;
+}
+
+static zval * sqlite_instanciate(zend_class_entry *pce, zval *object TSRMLS_DC)
+{
+       if (!object) {
+               ALLOC_ZVAL(object);
+       }
+       Z_TYPE_P(object) = IS_OBJECT;
+       object_init_ex(object, pce);
+       object->refcount = 1;
+       object->is_ref = 1;
+       return object;
+}
+/* }}} */
+
 static int init_sqlite_globals(zend_sqlite_globals *g)
 {
        g->assoc_case = 0;
@@ -617,8 +862,15 @@ static int init_sqlite_globals(zend_sqlite_globals *g)
 
 PHP_MINIT_FUNCTION(sqlite)
 {
+       REGISTER_SQLITE_CLASS(db,        NULL);
+       REGISTER_SQLITE_CLASS(query,     NULL);
+       REGISTER_SQLITE_CLASS(ub_query,  NULL);
+       REGISTER_SQLITE_CLASS(exception, zend_exception_get_default());
+       sqlite_object_handlers_query.get_class_entry = sqlite_get_ce_query;
+       sqlite_object_handlers_ub_query.get_class_entry = sqlite_get_ce_ub_query;
+
        ZEND_INIT_MODULE_GLOBALS(sqlite, init_sqlite_globals, NULL);
-       
+
        REGISTER_INI_ENTRIES();
 
        le_sqlite_db = zend_register_list_destructors_ex(php_sqlite_db_dtor, NULL, "sqlite database", module_number);
@@ -662,6 +914,21 @@ PHP_MINIT_FUNCTION(sqlite)
        return SUCCESS;
 }
 
+PHP_RINIT_FUNCTION(sqlite)
+{
+#ifdef HAVE_SPL
+       if (!sqlite_ce_query->num_interfaces) {
+               spl_register_implement(sqlite_ce_query, spl_ce_forward TSRMLS_CC);
+               spl_register_implement(sqlite_ce_query, spl_ce_sequence TSRMLS_CC);
+       }
+       if (!sqlite_ce_ub_query->num_interfaces) {
+               spl_register_implement(sqlite_ce_ub_query, spl_ce_forward TSRMLS_CC);
+       }
+#endif
+
+       return SUCCESS;
+}
+
 PHP_MINFO_FUNCTION(sqlite)
 {
        php_info_print_table_start();
@@ -674,7 +941,7 @@ PHP_MINFO_FUNCTION(sqlite)
        DISPLAY_INI_ENTRIES();
 }
 
-static struct php_sqlite_db *php_sqlite_open(char *filename, int mode, char *persistent_id, zval *return_value, zval *errmsg TSRMLS_DC)
+static struct php_sqlite_db *php_sqlite_open(char *filename, int mode, char *persistent_id, zval *return_value, zval *errmsg, zval *object TSRMLS_DC)
 {
        char *errtext = NULL;
        sqlite *sdb = NULL;
@@ -692,7 +959,12 @@ static struct php_sqlite_db *php_sqlite_open(char *filename, int mode, char *per
 
                sqlite_freemem(errtext);
 
-               RETVAL_FALSE;
+               /* if object is not an object then we're called from the factory() function */
+               if (object && Z_TYPE_P(object) != IS_OBJECT) {
+                       RETVAL_NULL();
+               } else {
+                       RETVAL_FALSE;
+               }
                return NULL;
        }
 
@@ -715,7 +987,15 @@ static struct php_sqlite_db *php_sqlite_open(char *filename, int mode, char *per
         * and IS backwards binary compatible with earlier versions */
        sqlite_set_authorizer(sdb, php_sqlite_authorizer, NULL);
        
-       db->rsrc_id = ZEND_REGISTER_RESOURCE(return_value, db, persistent_id ? le_sqlite_pdb : le_sqlite_db);
+       db->rsrc_id = ZEND_REGISTER_RESOURCE(object ? NULL : return_value, db, persistent_id ? le_sqlite_pdb : le_sqlite_db);
+       if (object) {
+               /* if object is not an object then we're called from the factory() function */
+               if (Z_TYPE_P(object) != IS_OBJECT) {
+                       sqlite_instanciate(sqlite_ce_db, object TSRMLS_CC);
+               }
+               /* and now register the object */
+               SQLITE_REGISTER_OBJECT(db, object, db)
+       }
 
        if (persistent_id) {
                list_entry le;
@@ -790,7 +1070,7 @@ PHP_FUNCTION(sqlite_popen)
        }
 
        /* now we need to open the database */
-       php_sqlite_open(fullpath, mode, hashkey, return_value, errmsg TSRMLS_CC);
+       php_sqlite_open(fullpath, mode, hashkey, return_value, errmsg, NULL TSRMLS_CC);
 
        efree(fullpath);
        efree(hashkey);
@@ -805,23 +1085,70 @@ PHP_FUNCTION(sqlite_open)
        char *filename;
        long filename_len;
        zval *errmsg = NULL;
+       zval *object = getThis();
 
+       php_set_error_handling(object ? EH_THROW : EH_NORMAL, zend_exception_get_default() TSRMLS_CC);
        if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz/",
                                &filename, &filename_len, &mode, &errmsg)) {
+               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
                return;
        }
 
        if (strncmp(filename, ":memory:", sizeof(":memory:") - 1)) {
                if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
-                       RETURN_FALSE;
+                       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+                       if (object) {
+                               RETURN_NULL();
+                       } else {
+                               RETURN_FALSE;
+                       }
                }
-
+       
                if (php_check_open_basedir(filename TSRMLS_CC)) {
-                       RETURN_FALSE;
+                       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+                       if (object) {
+                               RETURN_NULL();
+                       } else {
+                               RETURN_FALSE;
+                       }
                }
        }
-       
-       php_sqlite_open(filename, mode, NULL, return_value, errmsg TSRMLS_CC);
+
+       php_sqlite_open(filename, mode, NULL, return_value, errmsg, object TSRMLS_CC);
+
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto object factory(string filename [, int mode, string &errmessage])
+   Opens a SQLite database and create an object for it. Will create the database if it does not exist */
+PHP_FUNCTION(sqlite_factory)
+{
+       int mode = 0666;
+       char *filename;
+       long filename_len;
+       zval *errmsg = NULL;
+
+       php_set_error_handling(EH_THROW, sqlite_ce_exception TSRMLS_CC);
+       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lz/",
+                               &filename, &filename_len, &mode, &errmsg)) {
+               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+               RETURN_NULL();
+       }
+
+       if (PG(safe_mode) && (!php_checkuid(filename, NULL, CHECKUID_CHECK_FILE_AND_DIR))) {
+               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+               RETURN_NULL();
+       }
+
+       if (php_check_open_basedir(filename TSRMLS_CC)) {
+               php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
+               RETURN_NULL();
+       }
+
+       php_sqlite_open(filename, mode, NULL, return_value, errmsg, return_value TSRMLS_CC);
+
+       php_set_error_handling(EH_NORMAL, NULL TSRMLS_CC);
 }
 /* }}} */
 
@@ -832,13 +1159,20 @@ PHP_FUNCTION(sqlite_busy_timeout)
        zval *zdb;
        struct php_sqlite_db *db;
        long ms;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zdb, &ms)) {
-               return;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ms)) {
+                       return;
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zdb, &ms)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
 
-       DB_FROM_ZVAL(db, &zdb);
-
        sqlite_busy_timeout(db->db, ms);
 }
 /* }}} */
@@ -849,18 +1183,17 @@ PHP_FUNCTION(sqlite_close)
 {
        zval *zdb;
        struct php_sqlite_db *db;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
-               return;
-       }
-       DB_FROM_ZVAL(db, &zdb);
-
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
-               return;
+       if (object) {
+               php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Ignored, you must destruct the object instead");
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
 
-       DB_FROM_ZVAL(db, &zdb);
-
        zend_list_delete(Z_RESVAL_P(zdb));
 }
 /* }}} */
@@ -955,7 +1288,7 @@ next_row:
 /* }}} */
 
 /* {{{ sqlite_query */
-void sqlite_query(struct php_sqlite_db *db, char *sql, long sql_len, int mode, int buffered, zval *return_value, struct php_sqlite_result *rres TSRMLS_DC)
+void sqlite_query(zval *object, struct php_sqlite_db *db, char *sql, long sql_len, int mode, int buffered, zval *return_value, struct php_sqlite_result *rres TSRMLS_DC)
 {
        struct php_sqlite_result res;
        int ret;
@@ -992,8 +1325,18 @@ void sqlite_query(struct php_sqlite_db *db, char *sql, long sql_len, int mode, i
        
        rres->curr_row = 0;
 
-       if (return_value) {
-               ZEND_REGISTER_RESOURCE(return_value, rres, le_sqlite_result);
+       if (object) {
+               sqlite_object *obj;
+               if (buffered) {
+                       sqlite_instanciate(sqlite_ce_query, return_value TSRMLS_CC);
+               } else {
+                       sqlite_instanciate(sqlite_ce_ub_query, return_value TSRMLS_CC);
+               }
+               obj = (sqlite_object *) zend_object_store_get_object(return_value TSRMLS_CC);
+               obj->type = is_result;
+               obj->u.res = rres;
+       } else if (return_value) {
+               ZEND_REGISTER_RESOURCE(object ? NULL : return_value, rres, le_sqlite_result);
        }
 }
 /* }}} */
@@ -1008,15 +1351,22 @@ PHP_FUNCTION(sqlite_unbuffered_query)
        long sql_len;
        int mode = PHPSQLITE_BOTH;
        char *errtext = NULL;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &sql, &sql_len, &mode)) {
+                       return;
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
                                ZEND_NUM_ARGS() TSRMLS_CC, "sr|l", &sql, &sql_len, &zdb, &mode) && 
                        FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &zdb, &sql, &sql_len, &mode)) {
-               return;
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
 
-       DB_FROM_ZVAL(db, &zdb);
-
        /* avoid doing work if we can */
        if (!return_value_used) {
                db->last_err_code = sqlite_exec(db->db, sql, NULL, NULL, &errtext);
@@ -1027,8 +1377,8 @@ PHP_FUNCTION(sqlite_unbuffered_query)
                }
                return;
        }
-       
-       sqlite_query(db, sql, sql_len, mode, 0, return_value, NULL TSRMLS_CC);
+
+       sqlite_query(object, db, sql, sql_len, mode, 0, return_value, NULL TSRMLS_CC);
 }
 /* }}} */
 
@@ -1042,13 +1392,21 @@ PHP_FUNCTION(sqlite_query)
        long sql_len;
        int mode = PHPSQLITE_BOTH;
        char *errtext = NULL;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
-                       ZEND_NUM_ARGS() TSRMLS_CC, "sr|l", &sql, &sql_len, &zdb, &mode) && 
-               FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &zdb, &sql, &sql_len, &mode)) {
-               return;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &sql, &sql_len, &mode)) {
+                       return;
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+                               ZEND_NUM_ARGS() TSRMLS_CC, "sr|l", &sql, &sql_len, &zdb, &mode) && 
+                       FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &zdb, &sql, &sql_len, &mode)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
-       DB_FROM_ZVAL(db, &zdb);
 
        /* avoid doing work if we can */
        if (!return_value_used) {
@@ -1060,17 +1418,17 @@ PHP_FUNCTION(sqlite_query)
                }
                return;
        }
-       
-       sqlite_query(db, sql, sql_len, mode, 1, return_value, NULL TSRMLS_CC);
+
+       sqlite_query(object, db, sql, sql_len, mode, 1, return_value, NULL TSRMLS_CC);
 }
 /* }}} */
 
 /* {{{ php_sqlite_fetch_array */
 static void php_sqlite_fetch_array(struct php_sqlite_result *res, int mode, zend_bool decode_binary, int move_next, zval *return_value TSRMLS_DC)
 {
-       int j, buffered = res->buffered;
+       int j, n = res->ncolumns, buffered = res->buffered;
        const char **rowdata, **colnames;
-       
+
        /* check range of the row */
        if (res->curr_row >= res->nrows) {
                /* no more */
@@ -1086,7 +1444,7 @@ static void php_sqlite_fetch_array(struct php_sqlite_result *res, int mode, zend
        /* now populate the result */
        array_init(return_value);
 
-       for (j = 0; j < res->ncolumns; j++) {
+       for (j = 0; j < n; j++) {
                zval *decoded;
                MAKE_STD_ZVAL(decoded);
 
@@ -1168,7 +1526,7 @@ static void php_sqlite_fetch_column(struct php_sqlite_result *res, zval *which,
 
        if (rowdata[j] == NULL) {
                RETURN_NULL();
-       } else if (decode_binary && rowdata[j][0] == '\x01') {
+       } else if (decode_binary && rowdata[j] != NULL && rowdata[j][0] == '\x01') {
                int l = strlen(rowdata[j]);
                char *decoded = emalloc(l);
                l = sqlite_decode_binary(rowdata[j]+1, decoded);
@@ -1177,12 +1535,12 @@ static void php_sqlite_fetch_column(struct php_sqlite_result *res, zval *which,
                if (!res->buffered) {
                        efree((char*)rowdata[j]);
                        rowdata[j] = NULL;
-               }
+               }               
        } else {
                RETVAL_STRING((char*)rowdata[j], res->buffered);
                if (!res->buffered) {
                        rowdata[j] = NULL;
-               }
+               }               
        }
 }
 /* }}} */
@@ -1195,13 +1553,24 @@ PHP_FUNCTION(sqlite_fetch_all)
        int mode = PHPSQLITE_BOTH;
        zend_bool decode_binary = 1;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) {
-               return;
-       }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
-       if (ZEND_NUM_ARGS() < 2) {
-               mode = res->mode;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &mode, &decode_binary)) {
+                       return;
+               }
+               RES_FROM_OBJECT(res, object);
+               if (!ZEND_NUM_ARGS()) {
+                       mode = res->mode;
+               }
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
+               if (ZEND_NUM_ARGS() < 2) {
+                       mode = res->mode;
+               }
        }
 
        if (res->curr_row >= res->nrows && res->nrows) {
@@ -1230,13 +1599,24 @@ PHP_FUNCTION(sqlite_fetch_array)
        int mode = PHPSQLITE_BOTH;
        zend_bool decode_binary = 1;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) {
-               return;
-       }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
-       if (ZEND_NUM_ARGS() < 2) {
-               mode = res->mode;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &mode, &decode_binary)) {
+                       return;
+               }
+               RES_FROM_OBJECT(res, object);
+               if (!ZEND_NUM_ARGS()) {
+                       mode = res->mode;
+               }
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
+               if (ZEND_NUM_ARGS() < 2) {
+                       mode = res->mode;
+               }
        }
 
        php_sqlite_fetch_array(res, mode, decode_binary, 1, return_value TSRMLS_CC);
@@ -1255,13 +1635,21 @@ PHP_FUNCTION(sqlite_array_query)
        int mode = PHPSQLITE_BOTH;
        char *errtext = NULL;
        zend_bool decode_binary = 1;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
-                       ZEND_NUM_ARGS() TSRMLS_CC, "sr|l", &sql, &sql_len, &zdb, &mode, &decode_binary) && 
-               FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|l", &zdb, &sql, &sql_len, &mode, &decode_binary)) {
-               return;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|lb", &sql, &sql_len, &mode, &decode_binary)) {
+                       return;
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET,
+                               ZEND_NUM_ARGS() TSRMLS_CC, "sr|lb", &sql, &sql_len, &zdb, &mode, &decode_binary) && 
+                       FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|lb", &zdb, &sql, &sql_len, &mode, &decode_binary)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
-       DB_FROM_ZVAL(db, &zdb);
 
        /* avoid doing work if we can */
        if (!return_value_used) {
@@ -1273,9 +1661,9 @@ PHP_FUNCTION(sqlite_array_query)
                }
                return;
        }
-
+       
        rres = (struct php_sqlite_result *)emalloc(sizeof(*rres));
-       sqlite_query(db, sql, sql_len, mode, 0, NULL, rres TSRMLS_CC);
+       sqlite_query(NULL, db, sql, sql_len, mode, 0, NULL, rres TSRMLS_CC);
 
        array_init(return_value);
 
@@ -1298,11 +1686,19 @@ PHP_FUNCTION(sqlite_fetch_string)
        char *decoded = NULL;
        int decoded_len;
        const char **rowdata;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|b", &zres, &decode_binary)) {
-               return;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &decode_binary)) {
+                       return;
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|b", &zres, &decode_binary)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
 
        /* check if there are any more rows on the cursor */
        if (res->curr_row >= res->nrows) {
@@ -1360,13 +1756,24 @@ PHP_FUNCTION(sqlite_current)
        int mode = PHPSQLITE_BOTH;
        zend_bool decode_binary = 1;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) {
-               return;
-       }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
-       if (ZEND_NUM_ARGS() < 2) {
-               mode = res->mode;
+       if (object) {
+               if (ZEND_NUM_ARGS() && FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|lb", &mode, &decode_binary)) {
+                       return;
+               }
+               RES_FROM_OBJECT(res, object);
+               if (!ZEND_NUM_ARGS()) {
+                       mode = res->mode;
+               }
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|lb", &zres, &mode, &decode_binary)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
+               if (ZEND_NUM_ARGS() < 2) {
+                       mode = res->mode;
+               }
        }
 
        php_sqlite_fetch_array(res, mode, decode_binary, 0, return_value TSRMLS_CC);
@@ -1381,11 +1788,19 @@ PHP_FUNCTION(sqlite_column)
        zval *which;
        zend_bool decode_binary = 1;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zres, &which, &decode_binary)) {
-               return;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|b", &which, &decode_binary)) {
+                       return;
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rz|b", &zres, &which, &decode_binary)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
 
        php_sqlite_fetch_column(res, which, decode_binary, return_value TSRMLS_CC);
 }
@@ -1419,13 +1834,20 @@ PHP_FUNCTION(sqlite_changes)
 {
        zval *zdb;
        struct php_sqlite_db *db;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
 
-       DB_FROM_ZVAL(db, &zdb);
-
        RETURN_LONG(sqlite_changes(db->db));
 }
 /* }}} */
@@ -1436,13 +1858,20 @@ PHP_FUNCTION(sqlite_last_insert_rowid)
 {
        zval *zdb;
        struct php_sqlite_db *db;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
 
-       DB_FROM_ZVAL(db, &zdb);
-
        RETURN_LONG(sqlite_last_insert_rowid(db->db));
 }
 /* }}} */
@@ -1453,13 +1882,20 @@ PHP_FUNCTION(sqlite_num_rows)
 {
        zval *zres;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
 
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
-
        if (res->buffered) {
                RETURN_LONG(res->nrows);
        } else {
@@ -1475,13 +1911,21 @@ PHP_FUNCTION(sqlite_has_more)
 {
        zval *zres;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
 
-       RETURN_BOOL(res->nrows && res->curr_row < res->nrows); /* curr_row may be -1 */
+       RETURN_BOOL(res->curr_row < res->nrows && res->nrows); /* curr_row may be -1 */
 }
 /* }}} */
 
@@ -1491,13 +1935,20 @@ PHP_FUNCTION(sqlite_num_fields)
 {
        zval *zres;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
 
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
-
        RETURN_LONG(res->ncolumns);
 }
 /* }}} */
@@ -1509,13 +1960,20 @@ PHP_FUNCTION(sqlite_field_name)
        zval *zres;
        struct php_sqlite_result *res;
        int field;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zres, &field)) {
-               return;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &field)) {
+                       return;
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r|l", &zres, &field)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
 
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
-
        if (field < 0 || field >= res->ncolumns) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "field %d out of range", field);
                RETURN_FALSE;
@@ -1532,13 +1990,20 @@ PHP_FUNCTION(sqlite_seek)
        zval *zres;
        struct php_sqlite_result *res;
        int row;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zres, &row)) {
-               return;
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &row)) {
+                       return;
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zres, &row)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
 
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
-
        if (!res->buffered) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot seek an unbuffered result set");
                RETURN_FALSE;
@@ -1560,11 +2025,19 @@ PHP_FUNCTION(sqlite_rewind)
 {
        zval *zres;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
 
        if (!res->buffered) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot seek an unbuffered result set");
@@ -1587,11 +2060,19 @@ PHP_FUNCTION(sqlite_next)
 {
        zval *zres;
        struct php_sqlite_result *res;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               RES_FROM_OBJECT(res, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zres)) {
+                       return;
+               }
+               ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
        }
-       ZEND_FETCH_RESOURCE(res, struct php_sqlite_result *, &zres, -1, "sqlite result", le_sqlite_result);
 
        if (!res->buffered && res->vm) {
                php_sqlite_fetch(res TSRMLS_CC);
@@ -1645,13 +2126,20 @@ PHP_FUNCTION(sqlite_last_error)
 {
        zval *zdb;
        struct php_sqlite_db *db;
+       zval *object = getThis();
 
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
-               return;
+       if (object) {
+               if (ZEND_NUM_ARGS() != 0) {
+                       WRONG_PARAM_COUNT
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zdb)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
 
-       DB_FROM_ZVAL(db, &zdb);
-
        RETURN_LONG(db->last_err_code);
 }
 /* }}} */
@@ -1750,11 +2238,19 @@ PHP_FUNCTION(sqlite_create_aggregate)
        struct php_sqlite_agg_functions *funcs;
        char *callable = NULL;
        long num_args = -1;
-       
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rszz|l", &zdb, &funcname, &funcname_len, &zstep, &zfinal, &num_args)) {
-               return;
+       zval *object = getThis();
+
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz|l", &zdb, &funcname, &funcname_len, &zstep, &zfinal, &num_args)) {
+                       return;
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rszz|l", &zdb, &funcname, &funcname_len, &zstep, &zfinal, &num_args)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
-       DB_FROM_ZVAL(db, &zdb);
 
        if (!zend_is_callable(zstep, 0, &callable)) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "step function `%s' is not callable", callable);
@@ -1770,6 +2266,7 @@ PHP_FUNCTION(sqlite_create_aggregate)
        }
        efree(callable);
 
+       
        if (prep_callback_struct(db, 1, funcname, zstep, zfinal, &funcs) == DO_REG) {
                sqlite_create_aggregate(db->db, funcname, num_args,
                                php_sqlite_agg_step_function_callback,
@@ -1792,10 +2289,19 @@ PHP_FUNCTION(sqlite_create_function)
        char *callable = NULL;
        long num_args = -1;
        
-       if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsz|l", &zdb, &funcname, &funcname_len, &zcall, &num_args)) {
-               return;
+       zval *object = getThis();
+
+       if (object) {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz|l", &zdb, &funcname, &funcname_len, &zcall, &num_args)) {
+                       return;
+               }
+               DB_FROM_OBJECT(db, object);
+       } else {
+               if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsz|l", &zdb, &funcname, &funcname_len, &zcall, &num_args)) {
+                       return;
+               }
+               DB_FROM_ZVAL(db, &zdb);
        }
-       DB_FROM_ZVAL(db, &zdb);
 
        if (!zend_is_callable(zcall, 0, &callable)) {
                php_error_docref(NULL TSRMLS_CC, E_WARNING, "function `%s' is not callable", callable);
diff --git a/ext/sqlite/tests/blankdb_oo.inc b/ext/sqlite/tests/blankdb_oo.inc
new file mode 100755 (executable)
index 0000000..05609bd
--- /dev/null
@@ -0,0 +1,13 @@
+<?php #vim:ft=php
+$dbname = tempnam(dirname(__FILE__), "phpsql");
+function cleanup() {
+       global $db, $dbname;
+
+       if ($db) {
+               @$db->close();
+       }
+       unlink($dbname);
+}
+register_shutdown_function("cleanup");
+$db = new sqlite_db($dbname);
+?>
diff --git a/ext/sqlite/tests/sqlite_oo_001.phpt b/ext/sqlite/tests/sqlite_oo_001.phpt
new file mode 100755 (executable)
index 0000000..3886516
--- /dev/null
@@ -0,0 +1,17 @@
+--TEST--
+sqlite-oo: sqlite_open/close
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+require_once('blankdb_oo.inc');
+var_dump($db);
+$db = NULL;
+echo "Done\n";
+?>
+--EXPECTF--
+object(sqlite_db)#%d (0) {
+}
+Done
diff --git a/ext/sqlite/tests/sqlite_oo_002.phpt b/ext/sqlite/tests/sqlite_oo_002.phpt
new file mode 100755 (executable)
index 0000000..d3289b4
--- /dev/null
@@ -0,0 +1,41 @@
+--TEST--
+sqlite-oo: Simple insert/select
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+require_once('blankdb_oo.inc');
+var_dump($db);
+
+var_dump($db->query("CREATE TABLE foo(c1 date, c2 time, c3 varchar(64))"));
+var_dump($db->query("INSERT INTO foo VALUES ('2002-01-02', '12:49:00', NULL)"));
+$r = $db->query("SELECT * from foo");
+var_dump($r);
+var_dump($r->fetch_array());
+?>
+--EXPECTF--
+object(sqlite_db)#%d (0) {
+}
+object(sqlite_query)#%d (0) {
+}
+object(sqlite_query)#%d (0) {
+}
+object(sqlite_query)#%d (0) {
+}
+array(6) {
+  [0]=>
+  string(10) "2002-01-02"
+  ["c1"]=>
+  string(10) "2002-01-02"
+  [1]=>
+  string(8) "12:49:00"
+  ["c2"]=>
+  string(8) "12:49:00"
+  [2]=>
+  NULL
+  ["c3"]=>
+  NULL
+}
diff --git a/ext/sqlite/tests/sqlite_oo_003.phpt b/ext/sqlite/tests/sqlite_oo_003.phpt
new file mode 100755 (executable)
index 0000000..f498038
--- /dev/null
@@ -0,0 +1,51 @@
+--TEST--
+sqlite-oo: Simple insert/select, different result represenatation
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$db->query("CREATE TABLE foo(c1 date, c2 time, c3 varchar(64))");
+$db->query("INSERT INTO foo VALUES ('2002-01-02', '12:49:00', NULL)");
+$r = $db->query("SELECT * from foo");
+var_dump($r->fetch_array(SQLITE_BOTH));
+$r = $db->query("SELECT * from foo");
+var_dump($r->fetch_array(SQLITE_NUM));
+$r = $db->query("SELECT * from foo");
+var_dump($r->fetch_array(SQLITE_ASSOC));
+?>
+--EXPECT--
+array(6) {
+  [0]=>
+  string(10) "2002-01-02"
+  ["c1"]=>
+  string(10) "2002-01-02"
+  [1]=>
+  string(8) "12:49:00"
+  ["c2"]=>
+  string(8) "12:49:00"
+  [2]=>
+  NULL
+  ["c3"]=>
+  NULL
+}
+array(3) {
+  [0]=>
+  string(10) "2002-01-02"
+  [1]=>
+  string(8) "12:49:00"
+  [2]=>
+  NULL
+}
+array(3) {
+  ["c1"]=>
+  string(10) "2002-01-02"
+  ["c2"]=>
+  string(8) "12:49:00"
+  ["c3"]=>
+  NULL
+}
diff --git a/ext/sqlite/tests/sqlite_oo_008.phpt b/ext/sqlite/tests/sqlite_oo_008.phpt
new file mode 100755 (executable)
index 0000000..4340fb6
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+sqlite-oo: fetch all (buffered)
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       "one",
+       "two",
+       "three"
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('$str')");
+}
+
+$r = $db->query("SELECT a from strings");
+while ($row = $r->fetch_array(SQLITE_NUM)) {
+       var_dump($row);
+}
+echo "DONE!\n";
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(1) {
+  [0]=>
+  string(3) "two"
+}
+array(1) {
+  [0]=>
+  string(5) "three"
+}
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_009.phpt b/ext/sqlite/tests/sqlite_oo_009.phpt
new file mode 100755 (executable)
index 0000000..184804c
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+sqlite-oo: fetch all (unbuffered)
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       "one",
+       "two",
+       "three"
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('$str')");
+}
+
+$r = $db->unbuffered_query("SELECT a from strings");
+while ($row = $r->fetch_array(SQLITE_NUM)) {
+       var_dump($row);
+}
+echo "DONE!\n";
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(1) {
+  [0]=>
+  string(3) "two"
+}
+array(1) {
+  [0]=>
+  string(5) "three"
+}
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_010.phpt b/ext/sqlite/tests/sqlite_oo_010.phpt
new file mode 100755 (executable)
index 0000000..dd386ad
--- /dev/null
@@ -0,0 +1,44 @@
+--TEST--
+sqlite-oo: fetch all (iterator)
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       "one",
+       "two",
+       "three"
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('$str')");
+}
+
+$r = $db->unbuffered_query("SELECT a from strings", SQLITE_NUM);
+while ($row = $r->has_more()) {
+       var_dump($r->current());
+       $r->next();
+}
+echo "DONE!\n";
+?>
+--EXPECT--
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(1) {
+  [0]=>
+  string(3) "two"
+}
+array(1) {
+  [0]=>
+  string(5) "three"
+}
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_011.phpt b/ext/sqlite/tests/sqlite_oo_011.phpt
new file mode 100755 (executable)
index 0000000..c8fdfd1
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+sqlite-oo: returned associative column names
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$db->query("CREATE TABLE foo (c1 char, c2 char, c3 char)");
+$db->query("CREATE TABLE bar (c1 char, c2 char, c3 char)");
+$db->query("INSERT INTO foo VALUES ('1', '2', '3')");
+$db->query("INSERT INTO bar VALUES ('4', '5', '6')");
+$r = $db->query("SELECT * from foo, bar", SQLITE_ASSOC);
+var_dump($r->fetch_array());
+?>
+--EXPECT--
+array(3) {
+  ["c1"]=>
+  string(1) "4"
+  ["c2"]=>
+  string(1) "5"
+  ["c3"]=>
+  string(1) "6"
+}
diff --git a/ext/sqlite/tests/sqlite_oo_012.phpt b/ext/sqlite/tests/sqlite_oo_012.phpt
new file mode 100755 (executable)
index 0000000..b1a2644
--- /dev/null
@@ -0,0 +1,35 @@
+--TEST--
+sqlite-oo: read field names
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$db->query("CREATE TABLE strings(foo VARCHAR, bar VARCHAR, baz VARCHAR)");
+
+echo "Buffered\n";
+$r = $db->query("SELECT * from strings");
+for($i=0; $i<$r->num_fields(); $i++) {
+       var_dump($r->field_name($i));
+}
+echo "Unbuffered\n";
+$r = $db->unbuffered_query("SELECT * from strings");
+for($i=0; $i<$r->num_fields(); $i++) {
+       var_dump($r->field_name($i));
+}
+echo "DONE!\n";
+?>
+--EXPECT--
+Buffered
+string(3) "foo"
+string(3) "bar"
+string(3) "baz"
+Unbuffered
+string(3) "foo"
+string(3) "bar"
+string(3) "baz"
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_013.phpt b/ext/sqlite/tests/sqlite_oo_013.phpt
new file mode 100755 (executable)
index 0000000..e1bb392
--- /dev/null
@@ -0,0 +1,75 @@
+--TEST--
+sqlite-oo: fetch column
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       array (0 => 'one', 1 => 'two'),
+       array (0 => 'three', 1 => 'four')
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR, b VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('${str[0]}','${str[1]}')");
+}
+
+echo "====BUFFERED====\n";
+$r = $db->query("SELECT a, b from strings");
+while ($r->has_more()) {
+       var_dump($r->current(SQLITE_NUM));
+       var_dump($r->column(0));
+       var_dump($r->column(1));
+       var_dump($r->column('a'));
+       var_dump($r->column('b'));
+       $r->next();
+}
+echo "====UNBUFFERED====\n";
+$r = $db->unbuffered_query("SELECT a, b from strings");
+while ($r->has_more()) {
+       var_dump($r->column(0));
+       var_dump($r->column('b'));
+       var_dump($r->column(1));
+       var_dump($r->column('a'));
+       $r->next();
+}
+echo "DONE!\n";
+?>
+--EXPECT--
+====BUFFERED====
+array(2) {
+  [0]=>
+  string(3) "one"
+  [1]=>
+  string(3) "two"
+}
+string(3) "one"
+string(3) "two"
+string(3) "one"
+string(3) "two"
+array(2) {
+  [0]=>
+  string(5) "three"
+  [1]=>
+  string(4) "four"
+}
+string(5) "three"
+string(4) "four"
+string(5) "three"
+string(4) "four"
+====UNBUFFERED====
+string(3) "one"
+string(3) "two"
+NULL
+NULL
+string(5) "three"
+string(4) "four"
+NULL
+NULL
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_014.phpt b/ext/sqlite/tests/sqlite_oo_014.phpt
new file mode 100755 (executable)
index 0000000..041b18d
--- /dev/null
@@ -0,0 +1,118 @@
+--TEST--
+sqlite-oo: fetch all
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       "one",
+       "two",
+       "three"
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('$str')");
+}
+
+echo "unbuffered twice\n";
+$r = $db->unbuffered_query("SELECT a from strings", SQLITE_NUM);
+var_dump($r->fetch_all());
+var_dump($r->fetch_all());
+
+echo "unbuffered with fetch_array\n";
+$r = $db->unbuffered_query("SELECT a from strings", SQLITE_NUM);
+var_dump($r->fetch_array());
+var_dump($r->fetch_all());
+
+echo "buffered\n";
+$r = $db->query("SELECT a from strings", SQLITE_NUM);
+var_dump($r->fetch_all());
+var_dump($r->fetch_array());
+var_dump($r->fetch_all());
+
+echo "DONE!\n";
+?>
+--EXPECTF--
+unbuffered twice
+array(3) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(3) "one"
+  }
+  [1]=>
+  array(1) {
+    [0]=>
+    string(3) "two"
+  }
+  [2]=>
+  array(1) {
+    [0]=>
+    string(5) "three"
+  }
+}
+
+Notice: fetch_all(): One or more rowsets were already returned in %ssqlite_oo_014.php on line %d
+array(0) {
+}
+unbuffered with fetch_array
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(2) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(3) "two"
+  }
+  [1]=>
+  array(1) {
+    [0]=>
+    string(5) "three"
+  }
+}
+buffered
+array(3) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(3) "one"
+  }
+  [1]=>
+  array(1) {
+    [0]=>
+    string(3) "two"
+  }
+  [2]=>
+  array(1) {
+    [0]=>
+    string(5) "three"
+  }
+}
+bool(false)
+array(3) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(3) "one"
+  }
+  [1]=>
+  array(1) {
+    [0]=>
+    string(3) "two"
+  }
+  [2]=>
+  array(1) {
+    [0]=>
+    string(5) "three"
+  }
+}
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_015.phpt b/ext/sqlite/tests/sqlite_oo_015.phpt
new file mode 100755 (executable)
index 0000000..5fc90e2
--- /dev/null
@@ -0,0 +1,47 @@
+--TEST--
+sqlite-oo: array_query
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       "one",
+       "two",
+       "three"
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('$str')");
+}
+
+$res = $db->array_query("SELECT a from strings", SQLITE_NUM);
+var_dump($res);
+
+echo "DONE!\n";
+?>
+--EXPECTF--
+array(3) {
+  [0]=>
+  array(1) {
+    [0]=>
+    string(3) "one"
+  }
+  [1]=>
+  array(1) {
+    [0]=>
+    string(3) "two"
+  }
+  [2]=>
+  array(1) {
+    [0]=>
+    string(5) "three"
+  }
+}
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_016.phpt b/ext/sqlite/tests/sqlite_oo_016.phpt
new file mode 100755 (executable)
index 0000000..c346bf2
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+sqlite-oo: fetch string
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       array (0 => 'one', 1 => 'two'),
+       array (0 => 'three', 1 => 'four')
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR, b VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('${str[0]}','${str[1]}')");
+}
+
+echo "====BUFFERED====\n";
+$r = $db->query("SELECT a, b from strings");
+while ($r->has_more()) {
+       var_dump($r->fetch_string());
+}
+echo "====UNBUFFERED====\n";
+$r = $db->unbuffered_query("SELECT a, b from strings");
+while ($r->has_more()) {
+       var_dump($r->fetch_string());
+}
+echo "DONE!\n";
+?>
+--EXPECT--
+====BUFFERED====
+string(3) "one"
+string(5) "three"
+====UNBUFFERED====
+string(3) "one"
+string(5) "three"
+DONE!
diff --git a/ext/sqlite/tests/sqlite_oo_020.phpt b/ext/sqlite/tests/sqlite_oo_020.phpt
new file mode 100755 (executable)
index 0000000..e8e2d63
--- /dev/null
@@ -0,0 +1,63 @@
+--TEST--
+sqlite-oo: factory and exception
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; ?>
+--FILE--
+<?php 
+$dbname = tempnam(dirname(__FILE__), "phpsql");
+function cleanup() {
+       global $db, $dbname;
+
+       if ($db) {
+               @$db->close();
+       }
+       unlink($dbname);
+}
+register_shutdown_function("cleanup");
+
+try {
+       $db = sqlite_factory();
+} catch(sqlite_exception $err) {
+       echo "Message: ".$err->getmessage()."\n";
+       echo "File: ".$err->getfile()."\n";
+}
+
+$db = sqlite_factory($dbname);
+
+$data = array(
+       array (0 => 'one', 1 => 'two'),
+       array (0 => 'three', 1 => 'four')
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR, b VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('${str[0]}','${str[1]}')");
+}
+
+$r = $db->unbuffered_query("SELECT a, b from strings");
+while ($r->has_more()) {
+       var_dump($r->current(SQLITE_NUM));
+       $r->next();
+}
+echo "DONE!\n";
+?>
+--EXPECTF--
+Message: sqlite_factory() expects at least 1 parameter, 0 given
+File: %s/sqlite_oo_020.php
+array(2) {
+  [0]=>
+  string(3) "one"
+  [1]=>
+  string(3) "two"
+}
+array(2) {
+  [0]=>
+  string(5) "three"
+  [1]=>
+  string(4) "four"
+}
+DONE!
diff --git a/ext/sqlite/tests/sqlite_spl_001.phpt b/ext/sqlite/tests/sqlite_spl_001.phpt
new file mode 100755 (executable)
index 0000000..3220c5a
--- /dev/null
@@ -0,0 +1,114 @@
+--TEST--
+sqlite-spl: sqlite / spl integration
+--INI--
+sqlite.assoc_case=0
+--SKIPIF--
+<?php # vim:ft=php
+if (!extension_loaded("sqlite")) print "skip"; 
+if (!extension_loaded("spl")) print "skip: SPL is not installed"; 
+?>
+--FILE--
+<?php 
+include "blankdb_oo.inc";
+
+$data = array(
+       "one",
+       "two",
+       "three"
+       );
+
+$db->query("CREATE TABLE strings(a VARCHAR)");
+
+foreach ($data as $str) {
+       $db->query("INSERT INTO strings VALUES('$str')");
+}
+
+echo "====UNBUFFERED====\n";
+$r = $db->unbuffered_query("SELECT a from strings", SQLITE_NUM);
+var_dump(class_implements($r));
+foreach($r as $row) {
+       var_dump($row);
+}
+echo "====NO-MORE====\n";
+foreach($r as $row) {
+       var_dump($row);
+}
+echo "====DIRECT====\n";
+foreach($db->unbuffered_query("SELECT a from strings", SQLITE_NUM) as $row) {
+       var_dump($row);
+}
+echo "====BUFFERED====\n";
+$r = $db->query("SELECT a from strings", SQLITE_NUM);
+var_dump(class_implements($r));
+foreach($r as $row) {
+       var_dump($row);
+}
+foreach($r as $row) {
+       var_dump($row);
+}
+echo "DONE!\n";
+?>
+--EXPECT--
+====UNBUFFERED====
+array(1) {
+  ["spl_forward"]=>
+  string(11) "spl_forward"
+}
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(1) {
+  [0]=>
+  string(3) "two"
+}
+array(1) {
+  [0]=>
+  string(5) "three"
+}
+====NO-MORE====
+====DIRECT====
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(1) {
+  [0]=>
+  string(3) "two"
+}
+array(1) {
+  [0]=>
+  string(5) "three"
+}
+====BUFFERED====
+array(2) {
+  ["spl_forward"]=>
+  string(11) "spl_forward"
+  ["spl_sequence"]=>
+  string(12) "spl_sequence"
+}
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(1) {
+  [0]=>
+  string(3) "two"
+}
+array(1) {
+  [0]=>
+  string(5) "three"
+}
+array(1) {
+  [0]=>
+  string(3) "one"
+}
+array(1) {
+  [0]=>
+  string(3) "two"
+}
+array(1) {
+  [0]=>
+  string(5) "three"
+}
+DONE!