From bcbdce9134e695d4926d11f2fa1a855b3237db6e Mon Sep 17 00:00:00 2001 From: Peter Bowyer Date: Sat, 20 Jun 2020 08:59:07 +0100 Subject: [PATCH 1/6] Add a PDOSQLite class that compiles and is accessible from PHP --- ext/pdo_sqlite/pdo_sqlite.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 5c2fd63cdbd78..91ae836aff821 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -28,6 +28,10 @@ #include "php_pdo_sqlite.h" #include "php_pdo_sqlite_int.h" #include "zend_exceptions.h" +#include "zend_interfaces.h" + +/* Class entry pointers */ +PHPAPI zend_class_entry *pdo_dbh_sqlite_ptr; /* {{{ pdo_sqlite_functions[] */ static const zend_function_entry pdo_sqlite_functions[] = { @@ -78,6 +82,16 @@ PHP_MINIT_FUNCTION(pdo_sqlite) REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT); REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + zend_class_entry ce_sqlite; + INIT_CLASS_ENTRY(ce_sqlite, "PDOSQLite", pdo_sqlite_functions); + // The Reflection extension manges to set serialize and unserialize *before* calling + // zend_register_internal_class(). I couldn't make that work (something to do with + // pointers/references?) so have had to put them after. + pdo_dbh_sqlite_ptr = zend_register_internal_class(&ce_sqlite); // @TODO Add a second parameter with name of PDO ptr + pdo_dbh_sqlite_ptr->serialize = zend_class_serialize_deny; + pdo_dbh_sqlite_ptr->unserialize = zend_class_unserialize_deny; + zend_declare_property_string(pdo_dbh_sqlite_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC); + return php_pdo_register_driver(&pdo_sqlite_driver); } /* }}} */ From a1cd804bdfa4ce05e984f40326974b9157eda473 Mon Sep 17 00:00:00 2001 From: Peter Bowyer Date: Sat, 20 Jun 2020 09:49:54 +0100 Subject: [PATCH 2/6] Attempt #1 --- ext/pdo/pdo_dbh.c | 3 +++ ext/pdo/php_pdo.h | 8 ++++++++ ext/pdo_sqlite/pdo_sqlite.c | 19 ++++++++++++++++++- ext/pdo_sqlite/php_pdo_sqlite.h | 2 ++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index aa0edbbb472fd..fb1c37746003d 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -37,6 +37,9 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value); +/* Class entry pointers */ +PHPAPI zend_class_entry *pdo_dbh_ce; + void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *sqlstate, const char *supp) /* {{{ */ { pdo_error_type *pdo_err = &dbh->error_code; diff --git a/ext/pdo/php_pdo.h b/ext/pdo/php_pdo.h index 27c9c984d633d..08391c8c33d2e 100644 --- a/ext/pdo/php_pdo.h +++ b/ext/pdo/php_pdo.h @@ -27,6 +27,14 @@ extern zend_module_entry pdo_module_entry; #include "php_version.h" #define PHP_PDO_VERSION PHP_VERSION +// Copied from php_reflection.h +BEGIN_EXTERN_C() + +/* Class entry pointers */ +extern PHPAPI zend_class_entry *pdo_dbh_ce; + +END_EXTERN_C() + #ifdef PHP_WIN32 # if defined(PDO_EXPORTS) || (!defined(COMPILE_DL_PDO)) # define PDO_API __declspec(dllexport) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 91ae836aff821..cfce03ad1a6ea 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -87,7 +87,7 @@ PHP_MINIT_FUNCTION(pdo_sqlite) // The Reflection extension manges to set serialize and unserialize *before* calling // zend_register_internal_class(). I couldn't make that work (something to do with // pointers/references?) so have had to put them after. - pdo_dbh_sqlite_ptr = zend_register_internal_class(&ce_sqlite); // @TODO Add a second parameter with name of PDO ptr + pdo_dbh_sqlite_ptr = zend_register_internal_class(&ce_sqlite, pdo_pdh_ce); // @TODO Second parameter doesn't resolve pdo_dbh_sqlite_ptr->serialize = zend_class_serialize_deny; pdo_dbh_sqlite_ptr->unserialize = zend_class_unserialize_deny; zend_declare_property_string(pdo_dbh_sqlite_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC); @@ -114,3 +114,20 @@ PHP_MINFO_FUNCTION(pdo_sqlite) php_info_print_table_end(); } /* }}} */ + +///* {{{ spl_register_sub_class */ +//PHPAPI void spl_register_sub_class(zend_class_entry ** ppce, zend_class_entry * parent_ce, char * class_name, void *obj_ctor, const zend_function_entry * function_list) +//{ +// zend_class_entry ce; +// +// INIT_CLASS_ENTRY_EX(ce, class_name, strlen(class_name), function_list); +// *ppce = zend_register_internal_class_ex(&ce, parent_ce); +// +// /* entries changed by initialize */ +// if (obj_ctor) { +// (*ppce)->create_object = obj_ctor; +// } else { +// (*ppce)->create_object = parent_ce->create_object; +// } +//} +///* }}} */ \ No newline at end of file diff --git a/ext/pdo_sqlite/php_pdo_sqlite.h b/ext/pdo_sqlite/php_pdo_sqlite.h index 73d8a98675b2f..9dda44d8bf333 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite.h +++ b/ext/pdo_sqlite/php_pdo_sqlite.h @@ -36,3 +36,5 @@ PHP_RSHUTDOWN_FUNCTION(pdo_sqlite); PHP_MINFO_FUNCTION(pdo_sqlite); #endif /* PHP_PDO_SQLITE_H */ + +void spl_register_sub_class(zend_class_entry ** ppce, zend_class_entry * parent_ce, char * class_name, create_object_func_t ctor, const zend_function_entry * function_list); From 90a9c20fa1f8a626df8636f6c9e4f974fd789daf Mon Sep 17 00:00:00 2001 From: Peter Bowyer Date: Sat, 20 Jun 2020 10:27:23 +0100 Subject: [PATCH 3/6] Attempt #2 based on SimpleXML which WORKS! --- ext/pdo/pdo_dbh.c | 3 --- ext/pdo/php_pdo.h | 8 -------- ext/pdo_sqlite/pdo_sqlite.c | 29 ++++++++++------------------- ext/pdo_sqlite/php_pdo_sqlite.h | 4 +--- 4 files changed, 11 insertions(+), 33 deletions(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index fb1c37746003d..aa0edbbb472fd 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -37,9 +37,6 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, zend_long attr, zval *value); -/* Class entry pointers */ -PHPAPI zend_class_entry *pdo_dbh_ce; - void pdo_raise_impl_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char *sqlstate, const char *supp) /* {{{ */ { pdo_error_type *pdo_err = &dbh->error_code; diff --git a/ext/pdo/php_pdo.h b/ext/pdo/php_pdo.h index 08391c8c33d2e..27c9c984d633d 100644 --- a/ext/pdo/php_pdo.h +++ b/ext/pdo/php_pdo.h @@ -27,14 +27,6 @@ extern zend_module_entry pdo_module_entry; #include "php_version.h" #define PHP_PDO_VERSION PHP_VERSION -// Copied from php_reflection.h -BEGIN_EXTERN_C() - -/* Class entry pointers */ -extern PHPAPI zend_class_entry *pdo_dbh_ce; - -END_EXTERN_C() - #ifdef PHP_WIN32 # if defined(PDO_EXPORTS) || (!defined(COMPILE_DL_PDO)) # define PDO_API __declspec(dllexport) diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index cfce03ad1a6ea..4e14c6ae343fc 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -32,6 +32,7 @@ /* Class entry pointers */ PHPAPI zend_class_entry *pdo_dbh_sqlite_ptr; +PHPAPI zend_class_entry *ce_pdo; /* {{{ pdo_sqlite_functions[] */ static const zend_function_entry pdo_sqlite_functions[] = { @@ -82,12 +83,19 @@ PHP_MINIT_FUNCTION(pdo_sqlite) REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_READONLY_STATEMENT", (zend_long)PDO_SQLITE_ATTR_READONLY_STATEMENT); REGISTER_PDO_CLASS_CONST_LONG("SQLITE_ATTR_EXTENDED_RESULT_CODES", (zend_long)PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + zend_class_entry *pce; zend_class_entry ce_sqlite; + + if ((pce = zend_hash_str_find_ptr(CG(class_table), "pdo", sizeof("PDO") - 1)) == NULL) { + return SUCCESS; /* SimpleXML must be initialized before */ + } + + ce_pdo = pce; INIT_CLASS_ENTRY(ce_sqlite, "PDOSQLite", pdo_sqlite_functions); // The Reflection extension manges to set serialize and unserialize *before* calling // zend_register_internal_class(). I couldn't make that work (something to do with // pointers/references?) so have had to put them after. - pdo_dbh_sqlite_ptr = zend_register_internal_class(&ce_sqlite, pdo_pdh_ce); // @TODO Second parameter doesn't resolve + pdo_dbh_sqlite_ptr = zend_register_internal_class_ex(&ce_sqlite, ce_pdo); // @TODO Second parameter doesn't resolve pdo_dbh_sqlite_ptr->serialize = zend_class_serialize_deny; pdo_dbh_sqlite_ptr->unserialize = zend_class_unserialize_deny; zend_declare_property_string(pdo_dbh_sqlite_ptr, "name", sizeof("name")-1, "", ZEND_ACC_PUBLIC); @@ -113,21 +121,4 @@ PHP_MINFO_FUNCTION(pdo_sqlite) php_info_print_table_row(2, "SQLite Library", sqlite3_libversion()); php_info_print_table_end(); } -/* }}} */ - -///* {{{ spl_register_sub_class */ -//PHPAPI void spl_register_sub_class(zend_class_entry ** ppce, zend_class_entry * parent_ce, char * class_name, void *obj_ctor, const zend_function_entry * function_list) -//{ -// zend_class_entry ce; -// -// INIT_CLASS_ENTRY_EX(ce, class_name, strlen(class_name), function_list); -// *ppce = zend_register_internal_class_ex(&ce, parent_ce); -// -// /* entries changed by initialize */ -// if (obj_ctor) { -// (*ppce)->create_object = obj_ctor; -// } else { -// (*ppce)->create_object = parent_ce->create_object; -// } -//} -///* }}} */ \ No newline at end of file +/* }}} */ \ No newline at end of file diff --git a/ext/pdo_sqlite/php_pdo_sqlite.h b/ext/pdo_sqlite/php_pdo_sqlite.h index 9dda44d8bf333..a4e2b951b333e 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite.h +++ b/ext/pdo_sqlite/php_pdo_sqlite.h @@ -35,6 +35,4 @@ PHP_RINIT_FUNCTION(pdo_sqlite); PHP_RSHUTDOWN_FUNCTION(pdo_sqlite); PHP_MINFO_FUNCTION(pdo_sqlite); -#endif /* PHP_PDO_SQLITE_H */ - -void spl_register_sub_class(zend_class_entry ** ppce, zend_class_entry * parent_ce, char * class_name, create_object_func_t ctor, const zend_function_entry * function_list); +#endif /* PHP_PDO_SQLITE_H */ \ No newline at end of file From 51654eeae9407dc8991d62155719fbfea211f735 Mon Sep 17 00:00:00 2001 From: Peter Bowyer Date: Sat, 20 Jun 2020 15:05:03 +0100 Subject: [PATCH 4/6] Duplicate the constructor as PDO::connect(...) and slim down --- ext/pdo/pdo_dbh.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index aa0edbbb472fd..d31d30c14e029 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -396,6 +396,79 @@ static PHP_METHOD(PDO, dbh_constructor) } /* }}} */ +/* {{{ proto PDO::connect(string dsn[, string username[, string passwd [, array options]]]) + */ +// Needs a jolly good refactor, as it starts as an almost exact copy of `static PHP_METHOD(PDO, dbh_constructor)` +static PHP_METHOD(PDO, connect) +{ + char *data_source; + size_t data_source_len; + char *colon; + char *username=NULL, *password=NULL; + size_t usernamelen, passwordlen; + pdo_driver_t *driver = NULL; + zval *options = NULL; + char alt_dsn[512]; + + ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4) + Z_PARAM_STRING(data_source, data_source_len) + Z_PARAM_OPTIONAL + Z_PARAM_STRING_EX(username, usernamelen, 1, 0) + Z_PARAM_STRING_EX(password, passwordlen, 1, 0) + Z_PARAM_ARRAY_EX(options, 1, 0) + ZEND_PARSE_PARAMETERS_END(); + + /* parse the data source name */ + colon = strchr(data_source, ':'); + + if (!colon) { + /* let's see if this string has a matching dsn in the php.ini */ + char *ini_dsn = NULL; + + snprintf(alt_dsn, sizeof(alt_dsn), "pdo.dsn.%s", data_source); + if (FAILURE == cfg_get_string(alt_dsn, &ini_dsn)) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source name"); + return; + } + + data_source = ini_dsn; + colon = strchr(data_source, ':'); + + if (!colon) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source name (via INI: %s)", alt_dsn); + return; + } + } + + if (!strncmp(data_source, "uri:", sizeof("uri:")-1)) { + /* the specified URI holds connection details */ + data_source = dsn_from_uri(data_source + sizeof("uri:")-1, alt_dsn, sizeof(alt_dsn)); + if (!data_source) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source URI"); + return; + } + colon = strchr(data_source, ':'); + if (!colon) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "invalid data source name (via URI)"); + return; + } + } + + driver = pdo_find_driver(data_source, colon - data_source); + + if (!driver) { + /* NB: don't want to include the data_source in the error message as + * it might contain a password */ + zend_throw_exception_ex(php_pdo_get_exception(), 0, "could not find driver"); + return; + } + + // Now the driver has been identified, this is where it should be doing `new PDOSQLite()` + + // Somehow return the new object... +} +/* }}} */ + static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */ { if (!Z_ISUNDEF_P(ctor_args)) { @@ -1214,6 +1287,7 @@ ZEND_END_ARG_INFO() const zend_function_entry pdo_dbh_functions[] = /* {{{ */ { ZEND_MALIAS(PDO, __construct, dbh_constructor, arginfo_pdo___construct, ZEND_ACC_PUBLIC) + PHP_ME(PDO, connect, arginfo_pdo___construct, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) PHP_ME(PDO, prepare, arginfo_pdo_prepare, ZEND_ACC_PUBLIC) PHP_ME(PDO, beginTransaction, arginfo_pdo__void, ZEND_ACC_PUBLIC) PHP_ME(PDO, commit, arginfo_pdo__void, ZEND_ACC_PUBLIC) From e29b69eaeb66867a6c797aae9be3540034de657b Mon Sep 17 00:00:00 2001 From: Peter Bowyer Date: Sat, 20 Jun 2020 19:08:26 +0100 Subject: [PATCH 5/6] Improve comment in last commit --- ext/pdo/pdo_dbh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index d31d30c14e029..ebee2706056a6 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -463,7 +463,7 @@ static PHP_METHOD(PDO, connect) return; } - // Now the driver has been identified, this is where it should be doing `new PDOSQLite()` + // Now the driver has been identified, this is where we should be doing `new PDOSQLite()` for sqlite datasources // Somehow return the new object... } From f1751e97ae6077c4b691f87f49d0f39cc17202fc Mon Sep 17 00:00:00 2001 From: Peter Bowyer Date: Wed, 1 Jul 2020 14:08:32 +0100 Subject: [PATCH 6/6] Rough it out in pseudocode --- ext/pdo/pdo_dbh.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index ebee2706056a6..d2c1ca26456e5 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -464,8 +464,13 @@ static PHP_METHOD(PDO, connect) } // Now the driver has been identified, this is where we should be doing `new PDOSQLite()` for sqlite datasources + if (driver->driver_name == 'sqlite') { + thingtoreturn = new PDOSQLite(data_source, username, password, options) + } + // @TODO Add a catch-all for drivers without this implemented // Somehow return the new object... + ZVAL_OBJ(return_value, thingtoreturn); // Or pointer to *thingtoreturn? } /* }}} */