diff --git a/ext/pdo/pdo.c b/ext/pdo/pdo.c index 918036f54af8d..26bd8903a23c6 100644 --- a/ext/pdo/pdo.c +++ b/ext/pdo/pdo.c @@ -31,7 +31,8 @@ #include "ext/spl/spl_exceptions.h" #include "pdo_arginfo.h" -zend_class_entry *pdo_dbh_ce, *pdo_dbstmt_ce, *pdo_row_ce; +ZEND_API zend_class_entry *pdo_dbh_ce; +zend_class_entry *pdo_dbstmt_ce, *pdo_row_ce; /* for exceptional circumstances */ zend_class_entry *pdo_exception_ce; diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c index 833afa0d0f579..00e381be52651 100644 --- a/ext/pdo/pdo_dbh.c +++ b/ext/pdo/pdo_dbh.c @@ -221,10 +221,51 @@ static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */ } /* }}} */ -/* {{{ */ -PHP_METHOD(PDO, __construct) + +#define MAX_PDO_SUB_CLASSES 64 +static unsigned int number_of_pdo_driver_class_entries = 0; +static pdo_driver_class_entry *pdo_driver_class_entries[MAX_PDO_SUB_CLASSES]; + +// It would be possible remove this and roll it into the standard driver class entries +// I chose not to do it at this time, as that would break existing PDO extensions +zend_result pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry) +{ + if (number_of_pdo_driver_class_entries >= MAX_PDO_SUB_CLASSES) { + php_error_docref(NULL, E_ERROR, "Too many PDO driver subclasses."); + return FAILURE; + } + + for (unsigned int i=0; idriver_name, driver_class_entry->driver_name) == 0) { + php_error_docref(NULL, E_ERROR, "Cannot register duplicate PDO subclass name."); + return FAILURE; + } + } + + pdo_driver_class_entries[number_of_pdo_driver_class_entries] = driver_class_entry; + number_of_pdo_driver_class_entries += 1; + + return SUCCESS; +} + + +static void create_specific_pdo_object(zval *new_object, const char *driver_name) +{ + for (unsigned int i=0; i < number_of_pdo_driver_class_entries; i += 1) { + pdo_driver_class_entry *driver_class_entry = pdo_driver_class_entries[i]; + if (strcmp(driver_class_entry->driver_name, driver_name) == 0) { + object_init_ex(new_object, driver_class_entry->driver_ce); + return; + } + } + + // No specific DB implementation found + object_init_ex(new_object, pdo_dbh_ce); +} + +static +void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zval *object, zval *new_zval_object) { - zval *object = ZEND_THIS; pdo_dbh_t *dbh = NULL; bool is_persistent = 0; char *data_source; @@ -291,7 +332,19 @@ PHP_METHOD(PDO, __construct) RETURN_THROWS(); } - dbh = Z_PDO_DBH_P(object); + if (object == NULL) { + ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null"); + create_specific_pdo_object(new_zval_object, driver->driver_name); + + if (new_zval_object == NULL) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Failed to create specific PDO class"); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(new_zval_object); + } else { + dbh = Z_PDO_DBH_P(object); + } /* is this supposed to be a persistent connection ? */ if (options) { @@ -432,8 +485,24 @@ PHP_METHOD(PDO, __construct) zend_throw_exception(pdo_exception_ce, "Constructor failed", 0); } } + +/* {{{ */ +PHP_METHOD(PDO, __construct) +{ + zval *object = ZEND_THIS; + internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, object, NULL); +} +/* }}} */ + + +/* {{{ */ +PHP_METHOD(PDO, connect) +{ + internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, return_value); +} /* }}} */ + 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)) { @@ -1344,6 +1413,8 @@ static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count) } static zend_object_handlers pdo_dbh_object_handlers; +static zend_object_handlers pdosqlite_dbh_object_handlers; + static void pdo_dbh_free_storage(zend_object *std); void pdo_dbh_init(int module_number) @@ -1359,6 +1430,13 @@ void pdo_dbh_init(int module_number) pdo_dbh_object_handlers.get_method = dbh_method_get; pdo_dbh_object_handlers.compare = zend_objects_not_comparable; pdo_dbh_object_handlers.get_gc = dbh_get_gc; + + memcpy(&pdosqlite_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); + pdosqlite_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std); + pdosqlite_dbh_object_handlers.free_obj = pdo_dbh_free_storage; + pdosqlite_dbh_object_handlers.get_method = dbh_method_get; + pdosqlite_dbh_object_handlers.compare = zend_objects_not_comparable; + pdosqlite_dbh_object_handlers.get_gc = dbh_get_gc; } static void dbh_free(pdo_dbh_t *dbh, bool free_persistent) diff --git a/ext/pdo/pdo_dbh.stub.php b/ext/pdo/pdo_dbh.stub.php index 45364147230f4..60b60b00e4cf4 100644 --- a/ext/pdo/pdo_dbh.stub.php +++ b/ext/pdo/pdo_dbh.stub.php @@ -388,6 +388,13 @@ class PDO public function __construct(string $dsn, ?string $username = null, #[\SensitiveParameter] ?string $password = null, ?array $options = null) {} + public static function connect( + string $dsn, + ?string $username = null, + ?string $password = null, + ?array $options = null + ): PDO|PDOSqlite {} + /** @tentative-return-type */ public function beginTransaction(): bool {} diff --git a/ext/pdo/pdo_dbh_arginfo.h b/ext/pdo/pdo_dbh_arginfo.h index afbbc935548e5..ec9686eed23a0 100644 --- a/ext/pdo/pdo_dbh_arginfo.h +++ b/ext/pdo/pdo_dbh_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7dcba884671fd90b891fab7e3f0d4cc9a4ac76a1 */ + * Stub hash: 4fcd71c395e341df54e8f5f05f02aba56272acf0 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, dsn, IS_STRING, 0) @@ -8,6 +8,13 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_class_PDO_connect, 0, 1, PDO|PDOSqlite, 0) + ZEND_ARG_TYPE_INFO(0, dsn, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, username, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, password, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, options, IS_ARRAY, 1, "null") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_PDO_beginTransaction, 0, 0, _IS_BOOL, 0) ZEND_END_ARG_INFO() @@ -60,6 +67,7 @@ ZEND_END_ARG_INFO() ZEND_METHOD(PDO, __construct); +ZEND_METHOD(PDO, connect); ZEND_METHOD(PDO, beginTransaction); ZEND_METHOD(PDO, commit); ZEND_METHOD(PDO, errorCode); @@ -78,6 +86,7 @@ ZEND_METHOD(PDO, setAttribute); static const zend_function_entry class_PDO_methods[] = { ZEND_ME(PDO, __construct, arginfo_class_PDO___construct, ZEND_ACC_PUBLIC) + ZEND_ME(PDO, connect, arginfo_class_PDO_connect, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_ME(PDO, beginTransaction, arginfo_class_PDO_beginTransaction, ZEND_ACC_PUBLIC) ZEND_ME(PDO, commit, arginfo_class_PDO_commit, ZEND_ACC_PUBLIC) ZEND_ME(PDO, errorCode, arginfo_class_PDO_errorCode, ZEND_ACC_PUBLIC) diff --git a/ext/pdo/php_pdo.h b/ext/pdo/php_pdo.h index 023372ebc8e6e..fae71ceb66a24 100644 --- a/ext/pdo/php_pdo.h +++ b/ext/pdo/php_pdo.h @@ -22,6 +22,9 @@ extern zend_module_entry pdo_module_entry; #define phpext_pdo_ptr &pdo_module_entry +extern ZEND_API zend_class_entry *pdo_dbh_ce; +extern ZEND_API zend_object *pdo_dbh_new(zend_class_entry *ce); + #include "php_version.h" #define PHP_PDO_VERSION PHP_VERSION diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h index c832284627750..fb61e0eb3d559 100644 --- a/ext/pdo/php_pdo_driver.h +++ b/ext/pdo/php_pdo_driver.h @@ -215,6 +215,16 @@ typedef struct { } pdo_driver_t; +// NOTE - This separate struct, could be rolled it into pdo_driver_t +// I chose not to, as that would cause BC break and require a lot of +// downstream work. +typedef struct { + char *driver_name; + zend_class_entry *driver_ce; +} pdo_driver_class_entry; + +extern ZEND_API zend_result pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry); + /* {{{ methods for a database handle */ /* close or otherwise disconnect the database */ diff --git a/ext/pdo/php_pdo_int.h b/ext/pdo/php_pdo_int.h index aa83c6603b5aa..a276a7972e74a 100644 --- a/ext/pdo/php_pdo_int.h +++ b/ext/pdo/php_pdo_int.h @@ -28,9 +28,7 @@ int php_pdo_list_entry(void); void pdo_dbh_init(int module_number); void pdo_stmt_init(void); -extern zend_object *pdo_dbh_new(zend_class_entry *ce); extern const zend_function_entry pdo_dbh_functions[]; -extern zend_class_entry *pdo_dbh_ce; extern ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor); extern zend_object *pdo_dbstmt_new(zend_class_entry *ce); diff --git a/ext/pdo_dblib/pdo_dblib.c b/ext/pdo_dblib/pdo_dblib.c index 3230f9151bf4d..04d504b0b8aca 100644 --- a/ext/pdo_dblib/pdo_dblib.c +++ b/ext/pdo_dblib/pdo_dblib.c @@ -27,10 +27,14 @@ #include "php_pdo_dblib.h" #include "php_pdo_dblib_int.h" #include "zend_exceptions.h" +#include "pdo_dblib_arginfo.h" ZEND_DECLARE_MODULE_GLOBALS(dblib) static PHP_GINIT_FUNCTION(dblib); +zend_class_entry *PdoDblib_ce; +static pdo_driver_class_entry PdoDblib_pdo_driver_class_entry; + static const zend_module_dep pdo_dblib_deps[] = { ZEND_MOD_REQUIRED("pdo") ZEND_MOD_END @@ -201,6 +205,13 @@ PHP_MINIT_FUNCTION(pdo_dblib) return FAILURE; } + PdoDblib_ce = register_class_PdoDblib(pdo_dbh_ce); + PdoDblib_ce->create_object = pdo_dbh_new; + + PdoDblib_pdo_driver_class_entry.driver_name = "dblib"; + PdoDblib_pdo_driver_class_entry.driver_ce = PdoDblib_ce; + pdo_register_driver_specific_class(&PdoDblib_pdo_driver_class_entry); + if (FAILURE == php_pdo_register_driver(&pdo_dblib_driver)) { return FAILURE; } diff --git a/ext/pdo_dblib/pdo_dblib.stub.php b/ext/pdo_dblib/pdo_dblib.stub.php new file mode 100644 index 0000000000000..2556fb639cfa1 --- /dev/null +++ b/ext/pdo_dblib/pdo_dblib.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_dblib/tests/config_functions.inc b/ext/pdo_dblib/tests/config_functions.inc new file mode 100644 index 0000000000000..cee246c9a7ff6 --- /dev/null +++ b/ext/pdo_dblib/tests/config_functions.inc @@ -0,0 +1,24 @@ +query('drop table if exists #foobar;'); + +$db->query("create table #foobar(name varchar(32)); "); +$db->query("insert into #foobar values('PHP');"); +$db->query("insert into #foobar values('PHP6');"); + +foreach ($db->query('SELECT name FROM #foobar') as $row) { + var_dump($row); +} + +$db->query('drop table #foobar;'); + +echo "Fin."; +?> +--EXPECT-- +PdoDblib class exists. +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_dblib/tests/subclasses/pdodblib_002.phpt b/ext/pdo_dblib/tests/subclasses/pdodblib_002.phpt new file mode 100644 index 0000000000000..43c6072fc2d53 --- /dev/null +++ b/ext/pdo_dblib/tests/subclasses/pdodblib_002.phpt @@ -0,0 +1,53 @@ +--TEST-- +PdoDblib create through PDO::connect +--EXTENSIONS-- +pdo +--FILE-- +query('drop table if exists #test;'); + +$db->query("create table #test(name varchar(32)); "); +$db->query("insert into #test values('PHP');"); +$db->query("insert into #test values('PHP6');"); + +foreach ($db->query('SELECT name FROM #test') as $row) { + var_dump($row); +} + +$db->query('drop table #test;'); + +echo "Fin."; +?> +--EXPECT-- +PdoDblib class exists. +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_firebird/pdo_firebird.c b/ext/pdo_firebird/pdo_firebird.c index efbebccb0e127..3897fd791f898 100644 --- a/ext/pdo_firebird/pdo_firebird.c +++ b/ext/pdo_firebird/pdo_firebird.c @@ -25,6 +25,10 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_firebird.h" #include "php_pdo_firebird_int.h" +#include "pdo_firebird_arginfo.h" + +zend_class_entry *PdoFirebird_ce; +static pdo_driver_class_entry PdoFirebird_pdo_driver_class_entry; /* {{{ pdo_firebird_deps */ static const zend_module_dep pdo_firebird_deps[] = { @@ -62,6 +66,13 @@ PHP_MINIT_FUNCTION(pdo_firebird) /* {{{ */ return FAILURE; } + PdoFirebird_ce = register_class_PdoFirebird(pdo_dbh_ce); + PdoFirebird_ce->create_object = pdo_dbh_new; + + PdoFirebird_pdo_driver_class_entry.driver_name = "firebird"; + PdoFirebird_pdo_driver_class_entry.driver_ce = PdoFirebird_ce; + pdo_register_driver_specific_class(&PdoFirebird_pdo_driver_class_entry); + #ifdef ZEND_SIGNALS /* firebird replaces some signals at runtime, suppress warnings. */ SIGG(check) = 0; diff --git a/ext/pdo_firebird/pdo_firebird.stub.php b/ext/pdo_firebird/pdo_firebird.stub.php new file mode 100644 index 0000000000000..9b1501b58ed5e --- /dev/null +++ b/ext/pdo_firebird/pdo_firebird.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_firebird/tests/config_functions.inc b/ext/pdo_firebird/tests/config_functions.inc new file mode 100644 index 0000000000000..79e9a4ce6481b --- /dev/null +++ b/ext/pdo_firebird/tests/config_functions.inc @@ -0,0 +1,23 @@ +query('RECREATE TABLE foobar (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); +$db->query("INSERT INTO foobar VALUES (1, 'PHP')"); +$db->query("INSERT INTO foobar VALUES (2, 'PHP6')"); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoFirebird class exists. +array(2) { + ["NAME"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["NAME"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_firebird/tests/subclasses/pdofirebird_002.phpt b/ext/pdo_firebird/tests/subclasses/pdofirebird_002.phpt new file mode 100644 index 0000000000000..09a1654ad8bbe --- /dev/null +++ b/ext/pdo_firebird/tests/subclasses/pdofirebird_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO_firebird connect through PDO::connect +--EXTENSIONS-- +PDO_odbc +--FILE-- +query('RECREATE TABLE test (idx int NOT NULL PRIMARY KEY, name VARCHAR(20))'); + +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoFirebird class exists. +array(2) { + ["NAME"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["NAME"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["NAME"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_mysql/config.m4 b/ext/pdo_mysql/config.m4 index 7f09aabefa73a..c1047dd1488f9 100644 --- a/ext/pdo_mysql/config.m4 +++ b/ext/pdo_mysql/config.m4 @@ -99,7 +99,7 @@ if test "$PHP_PDO_MYSQL" != "no"; then AC_DEFINE_UNQUOTED(PDO_MYSQL_UNIX_ADDR, "$PDO_MYSQL_SOCKET", [ ]) fi - PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c mysql_driver.c mysql_statement.c, $ext_shared,,-I$pdo_cv_inc_path -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) + PHP_NEW_EXTENSION(pdo_mysql, pdo_mysql.c pdo_mysql_class.c mysql_driver.c mysql_statement.c, $ext_shared,,-I$pdo_cv_inc_path -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1) PHP_ADD_EXTENSION_DEP(pdo_mysql, pdo) diff --git a/ext/pdo_mysql/config.w32 b/ext/pdo_mysql/config.w32 index 48e47f7871866..6af0c0947dd95 100644 --- a/ext/pdo_mysql/config.w32 +++ b/ext/pdo_mysql/config.w32 @@ -6,7 +6,7 @@ if (PHP_PDO_MYSQL != "no") { if (PHP_PDO_MYSQL == "yes" || PHP_PDO_MYSQL == "mysqlnd") { AC_DEFINE('PDO_USE_MYSQLND', 1, 'Using MySQL native driver'); STDOUT.WriteLine("INFO: mysqlnd build"); - EXTENSION("pdo_mysql", "pdo_mysql.c mysql_driver.c mysql_statement.c"); + EXTENSION("pdo_mysql", "pdo_mysql.c pdo_mysql_class.c mysql_driver.c mysql_statement.c"); ADD_EXTENSION_DEP('pdo_mysql', 'pdo'); } else { if (CHECK_LIB("libmysql.lib", "pdo_mysql", PHP_PDO_MYSQL) && diff --git a/ext/pdo_mysql/pdo_mysql.c b/ext/pdo_mysql/pdo_mysql.c index 7aa39ca35a6bd..cdcb7e9cc2576 100644 --- a/ext/pdo_mysql/pdo_mysql.c +++ b/ext/pdo_mysql/pdo_mysql.c @@ -26,6 +26,10 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_mysql.h" #include "php_pdo_mysql_int.h" +#include "pdo_mysql_arginfo.h" + +zend_class_entry *pdomysql_ce; +static pdo_driver_class_entry pdomysql_pdo_driver_class_entry; #ifdef COMPILE_DL_PDO_MYSQL #ifdef ZTS @@ -132,6 +136,13 @@ static PHP_MINIT_FUNCTION(pdo_mysql) mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api); #endif + pdomysql_ce = register_class_PdoMysql(pdo_dbh_ce); + pdomysql_ce->create_object = pdo_dbh_new; + + pdomysql_pdo_driver_class_entry.driver_name = "mysql"; + pdomysql_pdo_driver_class_entry.driver_ce = pdomysql_ce; + pdo_register_driver_specific_class(&pdomysql_pdo_driver_class_entry); + return php_pdo_register_driver(&pdo_mysql_driver); } /* }}} */ diff --git a/ext/pdo_mysql/pdo_mysql.stub.php b/ext/pdo_mysql/pdo_mysql.stub.php new file mode 100644 index 0000000000000..8759d98ba1aa4 --- /dev/null +++ b/ext/pdo_mysql/pdo_mysql.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_mysql/pdo_mysql_class.c b/ext/pdo_mysql/pdo_mysql_class.c new file mode 100644 index 0000000000000..5d4f06000a17a --- /dev/null +++ b/ext/pdo_mysql/pdo_mysql_class.c @@ -0,0 +1,45 @@ + +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Danack + +----------------------------------------------------------------------+ +*/ + +/* The PDO Database Handle Class */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "../pdo/php_pdo.h" +#include "../pdo/php_pdo_driver.h" +#include "../pdo/php_pdo_int.h" +#include "php_pdo_mysql.h" +#include "php_pdo_mysql_int.h" + +/* {{{ proto string PDO::mysqlGetWarningCount() + Returns the number of SQL warnings during the execution of the last statement +*/ +PHP_METHOD(PdoMysql, getWarningCount) +{ + pdo_dbh_t *dbh; + pdo_mysql_db_handle *H; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_mysql_db_handle *)dbh->driver_data; + RETURN_LONG(mysql_warning_count(H->server)); +} +/* }}} */ diff --git a/ext/pdo_mysql/tests/config_functions.inc b/ext/pdo_mysql/tests/config_functions.inc new file mode 100644 index 0000000000000..0f6019e0b7b7c --- /dev/null +++ b/ext/pdo_mysql/tests/config_functions.inc @@ -0,0 +1,24 @@ +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); + $db->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER); + + return $db; + } + + static function createTestTable($db, $engine = null) { if (!$engine) $engine = PDO_MYSQL_TEST_ENGINE; diff --git a/ext/pdo_mysql/tests/pdo_mysql_interface.phpt b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt index aeb441bc0363a..5b6db319261b8 100644 --- a/ext/pdo_mysql/tests/pdo_mysql_interface.phpt +++ b/ext/pdo_mysql/tests/pdo_mysql_interface.phpt @@ -17,6 +17,7 @@ if (false == MySQLPDOTest::detect_transactional_mysql_engine($db)) $expected = array( '__construct' => true, + 'connect' => true, 'prepare' => true, 'beginTransaction' => true, 'commit' => true, diff --git a/ext/pdo_mysql/tests/subclasses/pdomysql_001.phpt b/ext/pdo_mysql/tests/subclasses/pdomysql_001.phpt new file mode 100644 index 0000000000000..b4c08497882f7 --- /dev/null +++ b/ext/pdo_mysql/tests/subclasses/pdomysql_001.phpt @@ -0,0 +1,48 @@ +--TEST-- +PDO_mysql subclass basic +--EXTENSIONS-- +pdo_mysql +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoMysql class exists. +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_mysql/tests/subclasses/pdomysql_002.phpt b/ext/pdo_mysql/tests/subclasses/pdomysql_002.phpt new file mode 100644 index 0000000000000..081c9329aafe7 --- /dev/null +++ b/ext/pdo_mysql/tests/subclasses/pdomysql_002.phpt @@ -0,0 +1,60 @@ +--TEST-- +PDO_mysql connect through PDO::connect +--EXTENSIONS-- +pdo_mysql +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoMysql class exists. +array(2) { + ["name"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["name"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["name"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_mysql/tests/subclasses/pdomysql_003.phpt b/ext/pdo_mysql/tests/subclasses/pdomysql_003.phpt new file mode 100644 index 0000000000000..fdf9966f10925 --- /dev/null +++ b/ext/pdo_mysql/tests/subclasses/pdomysql_003.phpt @@ -0,0 +1,32 @@ +--TEST-- +PDO_mysql getWarningCount +--EXTENSIONS-- +pdo_mysql +--FILE-- +query($q); + printf("Query %s produced %d warnings\n", $q, $db->getWarningCount()); +}; +$assertWarnings($db, 'SELECT 1 = 1', 0); +$assertWarnings($db, 'SELECT 1 = "A"', 1); + +--EXPECT-- +Query SELECT 1 = 1 produced 0 warnings +Query SELECT 1 = "A" produced 1 warnings diff --git a/ext/pdo_oci/pdo_oci.c b/ext/pdo_oci/pdo_oci.c index deb73e05f296c..d8ea0c0a2eb16 100644 --- a/ext/pdo_oci/pdo_oci.c +++ b/ext/pdo_oci/pdo_oci.c @@ -28,6 +28,11 @@ #ifdef ZTS #include #endif +#include "pdo_oci_arginfo.h" + +zend_class_entry *pdooci_ce; + +static pdo_driver_class_entry pdooci_pdo_driver_class_entry; /* {{{ pdo_oci_module_entry */ @@ -94,6 +99,13 @@ PHP_MINIT_FUNCTION(pdo_oci) // Defer OCI init to PHP_RINIT_FUNCTION because with php-fpm, // NLS_LANG is not yet available here. + pdooci_ce = register_class_PdoOci(pdo_dbh_ce); + pdooci_ce->create_object = pdo_dbh_new; + + pdooci_pdo_driver_class_entry.driver_name = "oci"; + pdooci_pdo_driver_class_entry.driver_ce = pdooci_ce; + pdo_register_driver_specific_class(&pdooci_pdo_driver_class_entry); + #ifdef ZTS pdo_oci_env_mutex = tsrm_mutex_alloc(); #endif diff --git a/ext/pdo_oci/pdo_oci.stub.php b/ext/pdo_oci/pdo_oci.stub.php new file mode 100644 index 0000000000000..b1db5d64c4075 --- /dev/null +++ b/ext/pdo_oci/pdo_oci.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_oci/tests/config_functions.inc b/ext/pdo_oci/tests/config_functions.inc new file mode 100644 index 0000000000000..de872fc79617c --- /dev/null +++ b/ext/pdo_oci/tests/config_functions.inc @@ -0,0 +1,23 @@ +query('CREATE TABLE IF NOT EXISTS foobar(id NUMBER, name VARCHAR2(6))'); + +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP')"); +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP6')"); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoOci class exists. +array(2) { + ["NAME"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["NAME"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_oci/tests/subclasses/pdooci_002.phpt b/ext/pdo_oci/tests/subclasses/pdooci_002.phpt new file mode 100644 index 0000000000000..6b1e0141bf393 --- /dev/null +++ b/ext/pdo_oci/tests/subclasses/pdooci_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO_oci connect through PDO::connect +--EXTENSIONS-- +pdo_oci +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id NUMBER NOT NULL PRIMARY KEY, name VARCHAR2(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoOci class exists. +array(2) { + ["NAME"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["NAME"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["NAME"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_odbc/pdo_odbc.c b/ext/pdo_odbc/pdo_odbc.c index 98e684bc5a95b..64e302ebc422a 100644 --- a/ext/pdo_odbc/pdo_odbc.c +++ b/ext/pdo_odbc/pdo_odbc.c @@ -25,6 +25,10 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_odbc.h" #include "php_pdo_odbc_int.h" +#include "pdo_odbc_arginfo.h" + +zend_class_entry *pdoodbc_ce; +static pdo_driver_class_entry pdoodbc_pdo_driver_class_entry; /* {{{ pdo_odbc_deps[] */ static const zend_module_dep pdo_odbc_deps[] = { @@ -102,6 +106,13 @@ PHP_MINIT_FUNCTION(pdo_odbc) REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_DRIVER", SQL_CUR_USE_DRIVER); REGISTER_PDO_CLASS_CONST_LONG("ODBC_SQL_USE_ODBC", SQL_CUR_USE_ODBC); + pdoodbc_ce = register_class_PdoOdbc(pdo_dbh_ce); + pdoodbc_ce->create_object = pdo_dbh_new; + + pdoodbc_pdo_driver_class_entry.driver_name = "odbc"; + pdoodbc_pdo_driver_class_entry.driver_ce = pdoodbc_ce; + pdo_register_driver_specific_class(&pdoodbc_pdo_driver_class_entry); + return SUCCESS; } /* }}} */ diff --git a/ext/pdo_odbc/pdo_odbc.stub.php b/ext/pdo_odbc/pdo_odbc.stub.php new file mode 100644 index 0000000000000..81f4e793e350a --- /dev/null +++ b/ext/pdo_odbc/pdo_odbc.stub.php @@ -0,0 +1,9 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + return class_entry; +} diff --git a/ext/pdo_odbc/tests/config_functions.inc b/ext/pdo_odbc/tests/config_functions.inc new file mode 100644 index 0000000000000..99fee513d905d --- /dev/null +++ b/ext/pdo_odbc/tests/config_functions.inc @@ -0,0 +1,27 @@ +query('CREATE TABLE IF NOT EXISTS foobar (id INT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoOdbc class exists. +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_odbc/tests/subclasses/pdoodbc_002.phpt b/ext/pdo_odbc/tests/subclasses/pdoodbc_002.phpt new file mode 100644 index 0000000000000..cf362dac70791 --- /dev/null +++ b/ext/pdo_odbc/tests/subclasses/pdoodbc_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PDO_mysql connect through PDO::connect +--EXTENSIONS-- +PDO_odbc +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoOdbc class exists. +array(2) { + ["name"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["name"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["name"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_pgsql/config.m4 b/ext/pdo_pgsql/config.m4 index 80ffd97ac2ece..624b561a9410d 100644 --- a/ext/pdo_pgsql/config.m4 +++ b/ext/pdo_pgsql/config.m4 @@ -78,6 +78,6 @@ if test "$PHP_PDO_PGSQL" != "no"; then PHP_CHECK_PDO_INCLUDES - PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pgsql_driver.c pgsql_statement.c, $ext_shared,,-I$pdo_cv_inc_path) + PHP_NEW_EXTENSION(pdo_pgsql, pdo_pgsql.c pdo_pgsql_class.c pgsql_driver.c pgsql_statement.c, $ext_shared,,-I$pdo_cv_inc_path) PHP_ADD_EXTENSION_DEP(pdo_pgsql, pdo) fi diff --git a/ext/pdo_pgsql/config.w32 b/ext/pdo_pgsql/config.w32 index cda62a64dba56..3d32f21943812 100644 --- a/ext/pdo_pgsql/config.w32 +++ b/ext/pdo_pgsql/config.w32 @@ -5,7 +5,7 @@ ARG_WITH("pdo-pgsql", "PostgreSQL support for PDO", "no"); if (PHP_PDO_PGSQL != "no") { if (CHECK_LIB("libpq.lib", "pdo_pgsql", PHP_PDO_PGSQL) && CHECK_HEADER_ADD_INCLUDE("libpq-fe.h", "CFLAGS_PDO_PGSQL", PHP_PDO_PGSQL + "\\include;" + PHP_PHP_BUILD + "\\include\\pgsql;" + PHP_PHP_BUILD + "\\include\\libpq;")) { - EXTENSION("pdo_pgsql", "pdo_pgsql.c pgsql_driver.c pgsql_statement.c"); + EXTENSION("pdo_pgsql", "pdo_pgsql.c pdo_pgsql_class.c pgsql_driver.c pgsql_statement.c"); if (X64) { ADD_FLAG('CFLAGS_PDO_PGSQL', "/D HAVE_PG_LO64=1"); diff --git a/ext/pdo_pgsql/pdo_pgsql.c b/ext/pdo_pgsql/pdo_pgsql.c index 582915a5a34f3..db76df24078ca 100644 --- a/ext/pdo_pgsql/pdo_pgsql.c +++ b/ext/pdo_pgsql/pdo_pgsql.c @@ -25,6 +25,16 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_pgsql.h" #include "php_pdo_pgsql_int.h" +#include "pdo_pgsql_arginfo.h" + +#define REGISTER_PdoPgsql_CLASS_CONST_LONG(const_name, value) \ + zend_declare_class_constant_long(php_pdo_get_dbh_ce(), const_name, sizeof(const_name)-1, (zend_long)value); + +#define REGISTER_PdoPgsql_CLASS_CONST_STRING(const_name, value) \ + zend_declare_class_constant_stringl(php_pdo_get_dbh_ce(), const_name, sizeof(const_name)-1, value, sizeof(value)-1); + +zend_class_entry *PdoPgsql_ce; +static pdo_driver_class_entry PdoPgsql_pdo_driver_class_entry; /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_pgsql_deps[] = { @@ -65,6 +75,13 @@ PHP_MINIT_FUNCTION(pdo_pgsql) REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_INERROR", (zend_long)PGSQL_TRANSACTION_INERROR); REGISTER_PDO_CLASS_CONST_LONG("PGSQL_TRANSACTION_UNKNOWN", (zend_long)PGSQL_TRANSACTION_UNKNOWN); + PdoPgsql_ce = register_class_PdoPgsql(pdo_dbh_ce); + PdoPgsql_ce->create_object = pdo_dbh_new; + + PdoPgsql_pdo_driver_class_entry.driver_name = "pgsql"; + PdoPgsql_pdo_driver_class_entry.driver_ce = PdoPgsql_ce; + pdo_register_driver_specific_class(&PdoPgsql_pdo_driver_class_entry); + return php_pdo_register_driver(&pdo_pgsql_driver); } /* }}} */ diff --git a/ext/pdo_pgsql/pdo_pgsql.stub.php b/ext/pdo_pgsql/pdo_pgsql.stub.php new file mode 100644 index 0000000000000..d2b5f7c1dfa0e --- /dev/null +++ b/ext/pdo_pgsql/pdo_pgsql.stub.php @@ -0,0 +1,59 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + zval const_ATTR_DISABLE_PREPARES_value; + ZVAL_LONG(&const_ATTR_DISABLE_PREPARES_value, PDO_PGSQL_ATTR_DISABLE_PREPARES); + zend_string *const_ATTR_DISABLE_PREPARES_name = zend_string_init_interned("ATTR_DISABLE_PREPARES", sizeof("ATTR_DISABLE_PREPARES") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_DISABLE_PREPARES_name, &const_ATTR_DISABLE_PREPARES_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_DISABLE_PREPARES_name); + + zval const_TRANSACTION_IDLE_value; + ZVAL_LONG(&const_TRANSACTION_IDLE_value, PGSQL_TRANSACTION_IDLE); + zend_string *const_TRANSACTION_IDLE_name = zend_string_init_interned("TRANSACTION_IDLE", sizeof("TRANSACTION_IDLE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_IDLE_name, &const_TRANSACTION_IDLE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_IDLE_name); + + zval const_TRANSACTION_ACTIVE_value; + ZVAL_LONG(&const_TRANSACTION_ACTIVE_value, PGSQL_TRANSACTION_ACTIVE); + zend_string *const_TRANSACTION_ACTIVE_name = zend_string_init_interned("TRANSACTION_ACTIVE", sizeof("TRANSACTION_ACTIVE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_ACTIVE_name, &const_TRANSACTION_ACTIVE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_ACTIVE_name); + + zval const_TRANSACTION_INTRANS_value; + ZVAL_LONG(&const_TRANSACTION_INTRANS_value, PGSQL_TRANSACTION_INTRANS); + zend_string *const_TRANSACTION_INTRANS_name = zend_string_init_interned("TRANSACTION_INTRANS", sizeof("TRANSACTION_INTRANS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INTRANS_name, &const_TRANSACTION_INTRANS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_INTRANS_name); + + zval const_TRANSACTION_INERROR_value; + ZVAL_LONG(&const_TRANSACTION_INERROR_value, PGSQL_TRANSACTION_INERROR); + zend_string *const_TRANSACTION_INERROR_name = zend_string_init_interned("TRANSACTION_INERROR", sizeof("TRANSACTION_INERROR") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_INERROR_name, &const_TRANSACTION_INERROR_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_INERROR_name); + + zval const_TRANSACTION_UNKNOWN_value; + ZVAL_LONG(&const_TRANSACTION_UNKNOWN_value, PGSQL_TRANSACTION_UNKNOWN); + zend_string *const_TRANSACTION_UNKNOWN_name = zend_string_init_interned("TRANSACTION_UNKNOWN", sizeof("TRANSACTION_UNKNOWN") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_TRANSACTION_UNKNOWN_name, &const_TRANSACTION_UNKNOWN_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_TRANSACTION_UNKNOWN_name); + + return class_entry; +} diff --git a/ext/pdo_pgsql/pdo_pgsql_class.c b/ext/pdo_pgsql/pdo_pgsql_class.c new file mode 100644 index 0000000000000..e39694ebcf121 --- /dev/null +++ b/ext/pdo_pgsql/pdo_pgsql_class.c @@ -0,0 +1,131 @@ + +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Danack + +----------------------------------------------------------------------+ +*/ + +/* The PDO Database Handle Class */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "../pdo/php_pdo.h" +#include "../pdo/php_pdo_driver.h" +#include "../pdo/php_pdo_int.h" +#include "php_pdo_pgsql.h" +#include "php_pdo_pgsql_int.h" +#include "zend_exceptions.h" + +/* {{{ Escape a identifier for insertion into a text field */ +PHP_METHOD(PdoPgsql, escapeIdentifier) +{ + zend_string *from = NULL; + char *tmp; + pdo_dbh_t *dbh; + pdo_pgsql_db_handle *H; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "S", &from) == FAILURE) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + PDO_DBH_CLEAR_ERR(); + + /* Obtain db Handle */ + H = (pdo_pgsql_db_handle *)dbh->driver_data; + + tmp = PQescapeIdentifier(H->server, ZSTR_VAL(from), ZSTR_LEN(from)); + + if (!tmp) { + // TODO - currently this has no test, as it is unclear on what strings PQescapeIdentifier + // can fail. + // Also, "On error, PQescapeIdentifier returns NULL and a suitable message + // is stored in the conn object." using the suitable message might be nice. + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Failed to escape identifier"); + RETURN_THROWS(); + } + + RETVAL_STRING(tmp); + PQfreemem(tmp); +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyFromArray) +{ + pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyFromFile) +{ + pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyToFile) +{ + pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); + +} +/* }}} */ + +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PdoPgsql, copyToArray) +{ + pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobCreate) +{ + pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobOpen) +{ + pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PdoPgsql, lobUnlink) +{ + pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PdoPgsql, getNotify) +{ + pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PdoPgsql, getPid) +{ + pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ diff --git a/ext/pdo_pgsql/pgsql_driver.c b/ext/pdo_pgsql/pgsql_driver.c index 46b3f25f4086b..cadeb6cdeab80 100644 --- a/ext/pdo_pgsql/pgsql_driver.c +++ b/ext/pdo_pgsql/pgsql_driver.c @@ -604,8 +604,7 @@ static bool pgsql_handle_rollback(pdo_dbh_t *dbh) return ret; } -/* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) +void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -715,10 +714,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) RETURN_FALSE; } } -/* }}} */ /* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromArray) +{ + pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -812,11 +816,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) RETURN_FALSE; } } -/* }}} */ - /* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyFromFile) +{ + pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +void pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -907,10 +915,16 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) RETURN_FALSE; } } -/* }}} */ /* {{{ Returns true if the copy worked fine or false if error */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToFile) +{ + pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); + +} +/* }}} */ + +void pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -982,11 +996,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) RETURN_FALSE; } } -/* }}} */ +/* {{{ Returns true if the copy worked fine or false if error */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlCopyToArray) +{ + pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ -/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) +void pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1011,10 +1029,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) PDO_HANDLE_DBH_ERR(); RETURN_FALSE; } + +/* {{{ Creates a new large object, returning its identifier. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBCreate) +{ + pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} /* }}} */ -/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) +void pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1062,10 +1085,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) PDO_HANDLE_DBH_ERR(); RETURN_FALSE; } + +/* {{{ Opens an existing large object stream. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBOpen) +{ + pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} /* }}} */ -/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) +void pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1097,10 +1125,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) PDO_HANDLE_DBH_ERR(); RETURN_FALSE; } + +/* {{{ Deletes the large object identified by oid. Must be called inside a transaction. */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlLOBUnlink) +{ + pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} /* }}} */ -/* {{{ Get asynchronous notification */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +void pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1177,10 +1210,15 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) PQfreemem(pgsql_notify); } + +/* {{{ Get asynchronous notification */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetNotify) +{ + pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} /* }}} */ -/* {{{ Get backend(server) pid */ -PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +void pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS) { pdo_dbh_t *dbh; pdo_pgsql_db_handle *H; @@ -1194,6 +1232,12 @@ PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) RETURN_LONG(PQbackendPID(H->server)); } + +/* {{{ Get backend(server) pid */ +PHP_METHOD(PDO_PGSql_Ext, pgsqlGetPid) +{ + pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} /* }}} */ static const zend_function_entry *pdo_pgsql_get_driver_methods(pdo_dbh_t *dbh, int kind) diff --git a/ext/pdo_pgsql/php_pdo_pgsql_int.h b/ext/pdo_pgsql/php_pdo_pgsql_int.h index f45718f0c96a2..303aff6006058 100644 --- a/ext/pdo_pgsql/php_pdo_pgsql_int.h +++ b/ext/pdo_pgsql/php_pdo_pgsql_int.h @@ -109,4 +109,14 @@ extern const php_stream_ops pdo_pgsql_lob_stream_ops; void pdo_libpq_version(char *buf, size_t len); void pdo_pgsql_close_lob_streams(pdo_dbh_t *dbh); +void pgsqlCopyFromArray_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlCopyFromFile_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlCopyToArray_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlCopyToFile_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlLOBCreate_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlLOBOpen_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlLOBUnlink_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlGetNotify_internal(INTERNAL_FUNCTION_PARAMETERS); +void pgsqlGetPid_internal(INTERNAL_FUNCTION_PARAMETERS); + #endif /* PHP_PDO_PGSQL_INT_H */ diff --git a/ext/pdo_pgsql/tests/config_functions.inc b/ext/pdo_pgsql/tests/config_functions.inc new file mode 100644 index 0000000000000..269b3380ab7c3 --- /dev/null +++ b/ext/pdo_pgsql/tests/config_functions.inc @@ -0,0 +1,25 @@ +query('CREATE TABLE IF NOT EXISTS foobar (id INT, name TEXT)'); + +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP')"); +$db->query("INSERT INTO foobar VALUES (NULL, 'PHP6')"); + +foreach ($db->query('SELECT name FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoPgsql class exists. +array(2) { + ["name"]=> + string(3) "PHP" + [0]=> + string(3) "PHP" +} +array(2) { + ["name"]=> + string(4) "PHP6" + [0]=> + string(4) "PHP6" +} +Fin. diff --git a/ext/pdo_pgsql/tests/subclasses/pdopgsql_002.phpt b/ext/pdo_pgsql/tests/subclasses/pdopgsql_002.phpt new file mode 100644 index 0000000000000..8e25c8c3beaef --- /dev/null +++ b/ext/pdo_pgsql/tests/subclasses/pdopgsql_002.phpt @@ -0,0 +1,59 @@ +--TEST-- +PdoPgsql connect through PDO::connect +--EXTENSIONS-- +pdo_mysql +--FILE-- +query('DROP TABLE IF EXISTS test'); +$db->exec('CREATE TABLE IF NOT EXISTS test(id int NOT NULL PRIMARY KEY, name VARCHAR(10))'); +$db->exec("INSERT INTO test VALUES(1, 'A')"); +$db->exec("INSERT INTO test VALUES(2, 'B')"); +$db->exec("INSERT INTO test VALUES(3, 'C')"); + +foreach ($db->query('SELECT name FROM test') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE test'); + +echo "Fin."; +?> +--EXPECT-- +PdoPgsql class exists. +array(2) { + ["name"]=> + string(1) "A" + [0]=> + string(1) "A" +} +array(2) { + ["name"]=> + string(1) "B" + [0]=> + string(1) "B" +} +array(2) { + ["name"]=> + string(1) "C" + [0]=> + string(1) "C" +} +Fin. diff --git a/ext/pdo_pgsql/tests/subclasses/pdopgsql_003.phpt b/ext/pdo_pgsql/tests/subclasses/pdopgsql_003.phpt new file mode 100644 index 0000000000000..babef145460d6 --- /dev/null +++ b/ext/pdo_pgsql/tests/subclasses/pdopgsql_003.phpt @@ -0,0 +1,30 @@ +--TEST-- +PdoPgsql getWarningCount +--EXTENSIONS-- +pdo_mysql +--FILE-- +escapeIdentifier("This is a quote\""); + +// TODO - ask someone who knows about postgresql if this is correct: +echo "Result is [$result]\n"; + +--EXPECT-- +Result is ["This is a quote"""] + diff --git a/ext/pdo_sqlite/config.m4 b/ext/pdo_sqlite/config.m4 index 64311eaa6436e..51b50c1de7ee0 100644 --- a/ext/pdo_sqlite/config.m4 +++ b/ext/pdo_sqlite/config.m4 @@ -26,8 +26,14 @@ if test "$PHP_PDO_SQLITE" != "no"; then AC_DEFINE(HAVE_SQLITE3_COLUMN_TABLE_NAME, 1, [have sqlite3_column_table_name]) ], [], [$PDO_SQLITE_SHARED_LIBADD]) + PHP_CHECK_LIBRARY(sqlite3, sqlite3_load_extension, + [], + [AC_DEFINE(PDO_SQLITE_OMIT_LOAD_EXTENSION, 1, [have sqlite3 with extension support])], + [$PDO_SQLITE_SHARED_LIBADD] + ) + PHP_SUBST(PDO_SQLITE_SHARED_LIBADD) - PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c sqlite_driver.c sqlite_statement.c, + PHP_NEW_EXTENSION(pdo_sqlite, pdo_sqlite.c pdo_sqlite_class.c sqlite_driver.c sqlite_statement.c, $ext_shared,,-I$pdo_cv_inc_path) PHP_ADD_EXTENSION_DEP(pdo_sqlite, pdo) diff --git a/ext/pdo_sqlite/config.w32 b/ext/pdo_sqlite/config.w32 index 1ad3b74e8c338..35812bd3a5ddd 100644 --- a/ext/pdo_sqlite/config.w32 +++ b/ext/pdo_sqlite/config.w32 @@ -4,7 +4,7 @@ ARG_WITH("pdo-sqlite", "for pdo_sqlite support", "no"); if (PHP_PDO_SQLITE != "no") { if (SETUP_SQLITE3("pdo_sqlite", PHP_PDO_SQLITE, PHP_PDO_SQLITE_SHARED)) { - EXTENSION("pdo_sqlite", "pdo_sqlite.c sqlite_driver.c sqlite_statement.c"); + EXTENSION("pdo_sqlite", "pdo_sqlite.c pdo_sqlite_class.c sqlite_driver.c sqlite_statement.c"); ADD_EXTENSION_DEP('pdo_sqlite', 'pdo'); AC_DEFINE("HAVE_SQLITE3_COLUMN_TABLE_NAME", 1, "have sqlite3_column_table_name"); diff --git a/ext/pdo_sqlite/pdo_sqlite.c b/ext/pdo_sqlite/pdo_sqlite.c index 6da7708576368..d61ee9c433c3b 100644 --- a/ext/pdo_sqlite/pdo_sqlite.c +++ b/ext/pdo_sqlite/pdo_sqlite.c @@ -25,8 +25,11 @@ #include "pdo/php_pdo_driver.h" #include "php_pdo_sqlite.h" #include "php_pdo_sqlite_int.h" +#include "pdo_sqlite_arginfo.h" #include "zend_exceptions.h" +zend_class_entry *pdosqlite_ce; + /* {{{ pdo_sqlite_deps */ static const zend_module_dep pdo_sqlite_deps[] = { ZEND_MOD_REQUIRED("pdo") @@ -50,6 +53,8 @@ zend_module_entry pdo_sqlite_module_entry = { }; /* }}} */ +static pdo_driver_class_entry pdosqlite_pdo_driver_class_entry; + #if defined(COMPILE_DL_PDO_SQLITE) || defined(COMPILE_DL_PDO_SQLITE_EXTERNAL) ZEND_GET_MODULE(pdo_sqlite) #endif @@ -68,6 +73,13 @@ 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); + pdosqlite_ce = register_class_PdoSqlite(pdo_dbh_ce); + pdosqlite_ce->create_object = pdo_dbh_new; + + pdosqlite_pdo_driver_class_entry.driver_name = "sqlite"; + pdosqlite_pdo_driver_class_entry.driver_ce = pdosqlite_ce; + pdo_register_driver_specific_class(&pdosqlite_pdo_driver_class_entry); + return php_pdo_register_driver(&pdo_sqlite_driver); } /* }}} */ diff --git a/ext/pdo_sqlite/pdo_sqlite.stub.php b/ext/pdo_sqlite/pdo_sqlite.stub.php new file mode 100644 index 0000000000000..e9299be6a607a --- /dev/null +++ b/ext/pdo_sqlite/pdo_sqlite.stub.php @@ -0,0 +1,75 @@ +ce_flags |= ZEND_ACC_NOT_SERIALIZABLE; + + zval const_DETERMINISTIC_value; + ZVAL_LONG(&const_DETERMINISTIC_value, SQLITE_DETERMINISTIC); + zend_string *const_DETERMINISTIC_name = zend_string_init_interned("DETERMINISTIC", sizeof("DETERMINISTIC") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_DETERMINISTIC_name, &const_DETERMINISTIC_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_DETERMINISTIC_name); + + zval const_OPEN_READONLY_value; + ZVAL_LONG(&const_OPEN_READONLY_value, SQLITE_OPEN_READONLY); + zend_string *const_OPEN_READONLY_name = zend_string_init_interned("OPEN_READONLY", sizeof("OPEN_READONLY") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_OPEN_READONLY_name, &const_OPEN_READONLY_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_OPEN_READONLY_name); + + zval const_OPEN_READWRITE_value; + ZVAL_LONG(&const_OPEN_READWRITE_value, SQLITE_OPEN_READWRITE); + zend_string *const_OPEN_READWRITE_name = zend_string_init_interned("OPEN_READWRITE", sizeof("OPEN_READWRITE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_OPEN_READWRITE_name, &const_OPEN_READWRITE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_OPEN_READWRITE_name); + + zval const_OPEN_CREATE_value; + ZVAL_LONG(&const_OPEN_CREATE_value, SQLITE_OPEN_CREATE); + zend_string *const_OPEN_CREATE_name = zend_string_init_interned("OPEN_CREATE", sizeof("OPEN_CREATE") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_OPEN_CREATE_name, &const_OPEN_CREATE_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_OPEN_CREATE_name); + + zval const_ATTR_OPEN_FLAGS_value; + ZVAL_LONG(&const_ATTR_OPEN_FLAGS_value, PDO_SQLITE_ATTR_OPEN_FLAGS); + zend_string *const_ATTR_OPEN_FLAGS_name = zend_string_init_interned("ATTR_OPEN_FLAGS", sizeof("ATTR_OPEN_FLAGS") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_OPEN_FLAGS_name, &const_ATTR_OPEN_FLAGS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_OPEN_FLAGS_name); + + zval const_ATTR_READONLY_STATEMENT_value; + ZVAL_LONG(&const_ATTR_READONLY_STATEMENT_value, PDO_SQLITE_ATTR_READONLY_STATEMENT); + zend_string *const_ATTR_READONLY_STATEMENT_name = zend_string_init_interned("ATTR_READONLY_STATEMENT", sizeof("ATTR_READONLY_STATEMENT") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_READONLY_STATEMENT_name, &const_ATTR_READONLY_STATEMENT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_READONLY_STATEMENT_name); + + zval const_ATTR_EXTENDED_RESULT_CODES_value; + ZVAL_LONG(&const_ATTR_EXTENDED_RESULT_CODES_value, PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES); + zend_string *const_ATTR_EXTENDED_RESULT_CODES_name = zend_string_init_interned("ATTR_EXTENDED_RESULT_CODES", sizeof("ATTR_EXTENDED_RESULT_CODES") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_ATTR_EXTENDED_RESULT_CODES_name, &const_ATTR_EXTENDED_RESULT_CODES_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release(const_ATTR_EXTENDED_RESULT_CODES_name); + + return class_entry; +} diff --git a/ext/pdo_sqlite/pdo_sqlite_class.c b/ext/pdo_sqlite/pdo_sqlite_class.c new file mode 100644 index 0000000000000..6e137d316d753 --- /dev/null +++ b/ext/pdo_sqlite/pdo_sqlite_class.c @@ -0,0 +1,658 @@ + +/* + +----------------------------------------------------------------------+ + | Copyright (c) The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | https://www.php.net/license/3_01.txt | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Danack + +----------------------------------------------------------------------+ +*/ + +/* The PDO Database Handle Class */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php.h" +#include "../pdo/php_pdo.h" +#include "../pdo/php_pdo_driver.h" +#include "../pdo/php_pdo_int.h" +#include "zend_exceptions.h" +#include "zend_object_handlers.h" +#include "zend_hash.h" +#include "SAPI.h" + +#include "../pdo_sqlite/php_pdo_sqlite.h" +#include "../pdo_sqlite/php_pdo_sqlite_int.h" + +typedef struct { + zval val; + zend_long row; +} pdosqlite3_aggregate_context; + +static int do_callback(struct pdo_sqlite_fci *fc, zval *cb, + int argc, sqlite3_value **argv, sqlite3_context *context, + int is_agg) +{ + zval *zargs = NULL; + zval retval; + int i; + int ret; + int fake_argc; + pdosqlite3_aggregate_context *agg_context = NULL; + + if (is_agg) { + is_agg = 2; + } + + fake_argc = argc + is_agg; + + fc->fci.size = sizeof(fc->fci); + ZVAL_COPY_VALUE(&fc->fci.function_name, cb); + fc->fci.object = NULL; + fc->fci.retval = &retval; + fc->fci.param_count = fake_argc; + + /* build up the params */ + + if (fake_argc) { + zargs = safe_emalloc(fake_argc, sizeof(zval), 0); + } + + if (is_agg) { + agg_context = sqlite3_aggregate_context(context, sizeof(pdosqlite3_aggregate_context)); + if (!agg_context) { + efree(zargs); + return FAILURE; + } + if (Z_ISUNDEF(agg_context->val)) { + ZVAL_NEW_REF(&agg_context->val, &EG(uninitialized_zval)); + } + ZVAL_COPY_VALUE(&zargs[0], &agg_context->val); + ZVAL_LONG(&zargs[1], ++agg_context->row); + } + + for (i = 0; i < argc; i++) { + /* get the value */ + switch (sqlite3_value_type(argv[i])) { + case SQLITE_INTEGER: + ZVAL_LONG(&zargs[i + is_agg], sqlite3_value_int(argv[i])); + break; + + case SQLITE_FLOAT: + ZVAL_DOUBLE(&zargs[i + is_agg], sqlite3_value_double(argv[i])); + break; + + case SQLITE_NULL: + ZVAL_NULL(&zargs[i + is_agg]); + break; + + case SQLITE_BLOB: + case SQLITE3_TEXT: + default: + ZVAL_STRINGL(&zargs[i + is_agg], (char*)sqlite3_value_text(argv[i]), sqlite3_value_bytes(argv[i])); + break; + } + } + + fc->fci.params = zargs; + + if ((ret = zend_call_function(&fc->fci, &fc->fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } + + /* clean up the params */ + if (zargs) { + for (i = is_agg; i < fake_argc; i++) { + zval_ptr_dtor(&zargs[i]); + } + if (is_agg) { + zval_ptr_dtor(&zargs[1]); + } + efree(zargs); + } + + if (!is_agg || !argv) { + /* only set the sqlite return value if we are a scalar function, + * or if we are finalizing an aggregate */ + if (!Z_ISUNDEF(retval)) { + switch (Z_TYPE(retval)) { + case IS_LONG: + sqlite3_result_int(context, Z_LVAL(retval)); + break; + + case IS_NULL: + sqlite3_result_null(context); + break; + + case IS_DOUBLE: + sqlite3_result_double(context, Z_DVAL(retval)); + break; + + default: + if (!try_convert_to_string(&retval)) { + ret = FAILURE; + break; + } + sqlite3_result_text(context, Z_STRVAL(retval), Z_STRLEN(retval), SQLITE_TRANSIENT); + break; + } + } else { + sqlite3_result_error(context, "failed to invoke callback", 0); + } + + if (agg_context) { + zval_ptr_dtor(&agg_context->val); + } + } else { + /* we're stepping in an aggregate; the return value goes into + * the context */ + if (agg_context) { + if (Z_ISUNDEF(retval)) { + zval_ptr_dtor(&agg_context->val); + return FAILURE; + } + zval_ptr_dtor(Z_REFVAL(agg_context->val)); + ZVAL_COPY_VALUE(Z_REFVAL(agg_context->val), &retval); + ZVAL_UNDEF(&retval); + } + } + + if (!Z_ISUNDEF(retval)) { + zval_ptr_dtor(&retval); + } + + return ret; +} + + +void php_pgsql_func_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afunc, &func->func, argc, argv, context, 0); +} + +static void php_pgsql_func_step_callback(sqlite3_context *context, int argc, + sqlite3_value **argv) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->astep, &func->step, argc, argv, context, 1); +} + +static void php_pgsql_func_final_callback(sqlite3_context *context) +{ + struct pdo_sqlite_func *func = (struct pdo_sqlite_func*)sqlite3_user_data(context); + + do_callback(&func->afini, &func->fini, 0, NULL, context, 1); +} + +/* {{{ proto bool PdoSqlite::createFunction(string $function_name, callable $callback, int $num_args = -1, int $flags = 0) + Creates a function that can be used in a query +*/ +PHP_METHOD(PdoSqlite, createFunction) +{ + //copied from sqlite_driver.c + + struct pdo_sqlite_func *func; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + zend_long flags = 0; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(fci, fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + Z_PARAM_LONG(flags) + ZEND_PARSE_PARAMETERS_END(); + + if (argc < -1) { + zend_argument_value_error(3 , "must be either -1 (for any number of parameters) or greater than equal to zero."); + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + H = (pdo_sqlite_db_handle *)dbh->driver_data; + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, flags | SQLITE_UTF8, + func, php_pgsql_func_callback, NULL, NULL); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->func, &fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +#ifndef PDO_SQLITE_OMIT_LOAD_EXTENSION +/* {{{ Attempts to load an SQLite extension library. */ +PHP_METHOD(PdoSqlite, loadExtension) +{ + char *extension, *errtext = NULL; + char fullpath[MAXPATHLEN]; + size_t extension_len; + + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *db_handle; + + if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS(), "s", &extension, &extension_len)) { + RETURN_THROWS(); + } + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + + db_handle = (pdo_sqlite_db_handle *)dbh->driver_data; + +#ifdef ZTS + if ((strncmp(sapi_module.name, "cgi", 3) != 0) && + (strcmp(sapi_module.name, "cli") != 0) && + (strncmp(sapi_module.name, "embed", 5) != 0) + ) { + // TODO - needs test. + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Not supported in multithreaded Web servers"); + RETURN_FALSE; + } +#endif + + + if (!VCWD_REALPATH(extension, fullpath)) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension '%s'", extension); + RETURN_FALSE; + } + + sqlite3 *sqlite_handle; + sqlite_handle = db_handle->db; + + // This only enables extension loading for the C api, not for SQL + sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, NULL); + + if (sqlite3_load_extension(sqlite_handle, fullpath, 0, &errtext) != SQLITE_OK) { + zend_throw_exception_ex(php_pdo_get_exception(), 0, "Unable to load extension '%s'", errtext); + sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL); + RETURN_FALSE; + } + + // We disable extension loading for a vague feeling of safety. This is probably not necessary + // as extensions can only be loaded through C code, not through SQL, and if someone can get + // some C code to run on the server, they can do anything. + sqlite3_db_config(sqlite_handle, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 0, NULL); + + RETURN_TRUE; +} +/* }}} */ +#endif + +typedef struct { + sqlite3_blob *blob; + size_t position; + size_t size; + int flags; +} php_stream_pdosqlite3_data; + +static ssize_t php_pdosqlite3_stream_write(php_stream *stream, const char *buf, size_t count) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_stream->flags & SQLITE_OPEN_READONLY) { + php_error_docref(NULL, E_WARNING, "Can't write to blob stream: is open as read only"); + return -1; + } + + if (sqlite3_stream->position + count > sqlite3_stream->size) { + php_error_docref(NULL, E_WARNING, "It is not possible to increase the size of a BLOB"); + return -1; + } + + if (sqlite3_blob_write(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) { + return -1; + } + + if (sqlite3_stream->position + count >= sqlite3_stream->size) { + stream->eof = 1; + sqlite3_stream->position = sqlite3_stream->size; + } + else { + sqlite3_stream->position += count; + } + + return count; +} + +static ssize_t php_pdosqlite3_stream_read(php_stream *stream, char *buf, size_t count) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_stream->position + count >= sqlite3_stream->size) { + count = sqlite3_stream->size - sqlite3_stream->position; + stream->eof = 1; + } + if (count) { + if (sqlite3_blob_read(sqlite3_stream->blob, buf, count, sqlite3_stream->position) != SQLITE_OK) { + return -1; + } + sqlite3_stream->position += count; + } + return count; +} + +static int php_pdosqlite3_stream_close(php_stream *stream, int close_handle) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + if (sqlite3_blob_close(sqlite3_stream->blob) != SQLITE_OK) { + /* Error occurred, but it still closed */ + } + + efree(sqlite3_stream); + + return 0; +} + +static int php_pdosqlite3_stream_flush(php_stream *stream) +{ + /* do nothing */ + return 0; +} + +/* {{{ */ +static int php_pdosqlite3_stream_seek(php_stream *stream, zend_off_t offset, int whence, zend_off_t *newoffs) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + + switch(whence) { + case SEEK_CUR: + if (offset < 0) { + if (sqlite3_stream->position < (size_t)(-offset)) { + sqlite3_stream->position = 0; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->position + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + } else { + if (sqlite3_stream->position + (size_t)(offset) > sqlite3_stream->size) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->position + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + } + case SEEK_SET: + if (sqlite3_stream->size < (size_t)(offset)) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + case SEEK_END: + if (offset > 0) { + sqlite3_stream->position = sqlite3_stream->size; + *newoffs = -1; + return -1; + } else if (sqlite3_stream->size < (size_t)(-offset)) { + sqlite3_stream->position = 0; + *newoffs = -1; + return -1; + } else { + sqlite3_stream->position = sqlite3_stream->size + offset; + *newoffs = sqlite3_stream->position; + stream->eof = 0; + return 0; + } + default: + *newoffs = sqlite3_stream->position; + return -1; + } +} +/* }}} */ + + +static int php_pdosqlite3_stream_cast(php_stream *stream, int castas, void **ret) +{ + return FAILURE; +} + +static int php_pdosqlite3_stream_stat(php_stream *stream, php_stream_statbuf *ssb) +{ + php_stream_pdosqlite3_data *sqlite3_stream = (php_stream_pdosqlite3_data *) stream->abstract; + ssb->sb.st_size = sqlite3_stream->size; + return 0; +} + +static const php_stream_ops php_stream_pdosqlite3_ops = { + php_pdosqlite3_stream_write, + php_pdosqlite3_stream_read, + php_pdosqlite3_stream_close, + php_pdosqlite3_stream_flush, + "PDOSQLite", + php_pdosqlite3_stream_seek, + php_pdosqlite3_stream_cast, + php_pdosqlite3_stream_stat, + NULL +}; + + +/* {{{ Open a blob as a stream which we can read / write to. */ +PHP_METHOD(PdoSqlite, openBlob) +{ + char *table, *column, *dbname = "main", *mode = "rb"; + size_t table_len, column_len, dbname_len; + zend_long rowid, flags = SQLITE_OPEN_READONLY, sqlite_flags = 0; + sqlite3_blob *blob = NULL; + php_stream_pdosqlite3_data *sqlite3_stream; + php_stream *stream; + + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *db_handle; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + db_handle = (pdo_sqlite_db_handle *)dbh->driver_data; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "ssl|pl", &table, &table_len, &column, &column_len, &rowid, &dbname, &dbname_len, &flags) == FAILURE) { + RETURN_THROWS(); + } + + sqlite3 *sqlite_handle; + sqlite_handle = db_handle->db; + sqlite_flags = (flags & SQLITE_OPEN_READWRITE) ? 1 : 0; + + if (sqlite3_blob_open(sqlite_handle, dbname, table, column, rowid, sqlite_flags, &blob) != SQLITE_OK) { + // TODO - exception needed here. +// php_sqlite3_error(db_obj, "Unable to open blob: %s", sqlite3_errmsg(db_obj->db)); + // and converting to exception zend_throw_exception_ex(php_pdo_get_exception(), 0, errtext); + RETURN_FALSE; + } + + sqlite3_stream = emalloc(sizeof(php_stream_pdosqlite3_data)); + sqlite3_stream->blob = blob; + sqlite3_stream->flags = flags; + sqlite3_stream->position = 0; + sqlite3_stream->size = sqlite3_blob_bytes(blob); + + if (sqlite_flags != 0) { + mode = "r+b"; + } + + stream = php_stream_alloc(&php_stream_pdosqlite3_ops, sqlite3_stream, 0, mode); + + if (stream) { + php_stream_to_zval(stream, return_value); + } else { + RETURN_FALSE; + } +} +/* }}} */ + +static int php_pdosqlite_collation_callback(void *context, + int string1_len, const void *string1, + int string2_len, const void *string2) +{ + int ret; + zval zargs[2]; + zval retval; + struct pdo_sqlite_collation *collation = (struct pdo_sqlite_collation*) context; + + collation->fc.fci.size = sizeof(collation->fc.fci); + ZVAL_COPY_VALUE(&collation->fc.fci.function_name, &collation->callback); + collation->fc.fci.object = NULL; + collation->fc.fci.retval = &retval; + + // Prepare the arguments. + ZVAL_STRINGL(&zargs[0], (char *) string1, string1_len); + ZVAL_STRINGL(&zargs[1], (char *) string2, string2_len); + collation->fc.fci.param_count = 2; + collation->fc.fci.params = zargs; + + if ((ret = zend_call_function(&collation->fc.fci, &collation->fc.fcc)) == FAILURE) { + php_error_docref(NULL, E_WARNING, "An error occurred while invoking the callback"); + } else if (!Z_ISUNDEF(retval)) { + if (Z_TYPE(retval) != IS_LONG) { + convert_to_long(&retval); + } + ret = 0; + if (Z_LVAL(retval) > 0) { + ret = 1; + } else if (Z_LVAL(retval) < 0) { + ret = -1; + } + zval_ptr_dtor(&retval); + } + + zval_ptr_dtor(&zargs[0]); + zval_ptr_dtor(&zargs[1]); + + return ret; +} + + +PHP_METHOD(PdoSqlite, createAggregate) +{ + struct pdo_sqlite_func *func; + zend_fcall_info step_fci, fini_fci; + zend_fcall_info_cache step_fcc, fini_fcc; + char *func_name; + size_t func_name_len; + zend_long argc = -1; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + ZEND_PARSE_PARAMETERS_START(3, 4) + Z_PARAM_STRING(func_name, func_name_len) + Z_PARAM_FUNC(step_fci, step_fcc) + Z_PARAM_FUNC(fini_fci, fini_fcc) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(argc) + ZEND_PARSE_PARAMETERS_END(); + + func = (struct pdo_sqlite_func*)ecalloc(1, sizeof(*func)); + + ret = sqlite3_create_function(H->db, func_name, argc, SQLITE_UTF8, + func, NULL, php_pgsql_func_step_callback, php_pgsql_func_final_callback); + if (ret == SQLITE_OK) { + func->funcname = estrdup(func_name); + + ZVAL_COPY(&func->step, &step_fci.function_name); + + ZVAL_COPY(&func->fini, &fini_fci.function_name); + + func->argc = argc; + + func->next = H->funcs; + H->funcs = func; + + RETURN_TRUE; + } + + efree(func); + RETURN_FALSE; +} +/* }}} */ + +/* {{{ bool SQLite::createCollation(string name, callable callback) + Registers a collation with the sqlite db handle */ +PHP_METHOD(PdoSqlite, createCollation) +{ + struct pdo_sqlite_collation *collation; + zend_fcall_info fci; + zend_fcall_info_cache fcc; + char *collation_name; + size_t collation_name_len; + pdo_dbh_t *dbh; + pdo_sqlite_db_handle *H; + int ret; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STRING(collation_name, collation_name_len) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); + + dbh = Z_PDO_DBH_P(ZEND_THIS); + PDO_CONSTRUCT_CHECK; + H = (pdo_sqlite_db_handle *)dbh->driver_data; + + collation = (struct pdo_sqlite_collation*)ecalloc(1, sizeof(*collation)); + + ret = sqlite3_create_collation(H->db, collation_name, SQLITE_UTF8, collation, php_pdosqlite_collation_callback); + if (ret == SQLITE_OK) { + collation->name = estrdup(collation_name); + + ZVAL_COPY(&collation->callback, &fci.function_name); + + collation->next = H->collations; + H->collations = collation; + + RETURN_TRUE; + } + + efree(collation); + RETURN_FALSE; +} +/* }}} */ diff --git a/ext/pdo_sqlite/php_pdo_sqlite_int.h b/ext/pdo_sqlite/php_pdo_sqlite_int.h index c9aeee08a3a59..08d37af9e5e58 100644 --- a/ext/pdo_sqlite/php_pdo_sqlite_int.h +++ b/ext/pdo_sqlite/php_pdo_sqlite_int.h @@ -78,4 +78,7 @@ enum { PDO_SQLITE_ATTR_EXTENDED_RESULT_CODES }; +#define REGISTER_PDO_SQLITE_CLASS_CONST_LONG(const_name, value) \ + zend_declare_class_constant_long(pdosqlite_ce, const_name, sizeof(const_name)-1, (zend_long)value); + #endif diff --git a/ext/pdo_sqlite/tests/subclasses/gc.phpt b/ext/pdo_sqlite/tests/subclasses/gc.phpt new file mode 100644 index 0000000000000..e09b7dd48889f --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/gc.phpt @@ -0,0 +1,28 @@ +--TEST-- +GC support for PDO Sqlite driver data +--EXTENSIONS-- +pdo_sqlite +--FILE-- +a = Pdo::connect('sqlite::memory:'); + +if (!$obj->a instanceof PdoSqlite) { + echo "Wrong class type. Should be PdoSqlite but is [" . get_class($obj->a) . "]\n"; +} + +$obj->a->createFunction('func1', function() use ($obj) {}, 1); +$obj->a->createAggregate('func2', function() use ($obj) {}, function() use($obj) {}); +$obj->a->createCollation('col', function() use ($obj) {}); + +?> +===DONE=== +--EXPECT-- +===DONE=== diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt new file mode 100644 index 0000000000000..da48a02c7cfe8 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_constants.phpt @@ -0,0 +1,26 @@ +--TEST-- +PDO_sqlite: Testing constants exist +--EXTENSIONS-- +pdo_sqlite +--FILE-- + +--EXPECTF-- +Hello +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) +int(%d) diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt new file mode 100644 index 0000000000000..cc4763e4dbd91 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate.phpt @@ -0,0 +1,32 @@ +--TEST-- +PDO_sqlite: Testing createAggregate() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +$db->createAggregate('testing', function(&$a, $b) { $a .= $b; return $a; }, function(&$v) { return $v; }); + + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(2) "12" + [0]=> + string(2) "12" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt new file mode 100644 index 0000000000000..e4152c06e81e8 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createaggregate_002.phpt @@ -0,0 +1,25 @@ +--TEST-- +PDO_sqlite: Testing invalid callback for createAggregate() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +createAggregate('foo', 'a', ''); +} catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; +} +try { + $pdo->createAggregate('foo', 'strlen', ''); +} catch (\TypeError $e) { + echo $e->getMessage() . \PHP_EOL; +} + +?> +--EXPECT-- +PdoSqlite::createAggregate(): Argument #2 ($step) must be a valid callback, function "a" not found or invalid function name +PdoSqlite::createAggregate(): Argument #3 ($finalize) must be a valid callback, function "" not found or invalid function name diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt new file mode 100644 index 0000000000000..80344de0a59f2 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createcollation.phpt @@ -0,0 +1,39 @@ +--TEST-- +PDO_sqlite: Testing sqliteCreateCollation() +--EXTENSIONS-- +pdo_sqlite +--FILE-- +setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$db->query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "1")'); +$db->query('INSERT INTO foobar VALUES (NULL, "2")'); +$db->query('INSERT INTO foobar VALUES (NULL, "10")'); + +$db->createCollation('MYCOLLATE', function($a, $b) { return strnatcmp($a, $b); }); + +$result = $db->query('SELECT name FROM foobar ORDER BY name COLLATE MYCOLLATE'); +foreach ($result as $row) { + echo $row['name'] . "\n"; +} + +$result = $db->query('SELECT name FROM foobar ORDER BY name'); +foreach ($result as $row) { + echo $row['name'] . "\n"; +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +1 +2 +10 +1 +10 +2 diff --git a/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt new file mode 100644 index 0000000000000..d29e79fc903df --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdo_sqlite_createfunction_with_flags.phpt @@ -0,0 +1,38 @@ +--TEST-- +PDO_sqlite: Testing createFunction() with flags +--EXTENSIONS-- +pdo_sqlite +--SKIPIF-- + +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +?> +--EXPECT-- +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt new file mode 100644 index 0000000000000..0fe00b15e4ef4 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_001.phpt @@ -0,0 +1,47 @@ +--TEST-- +PdoSqlite basic +--EXTENSIONS-- +pdo +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); + +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +echo "Fin."; +?> +--EXPECT-- +PdoSqlite class exists. +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt new file mode 100644 index 0000000000000..98a07de40493a --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_002.phpt @@ -0,0 +1,60 @@ +--TEST-- +PdoSqlite create through PDO::connect and function define. +--EXTENSIONS-- +pdo +--FILE-- +query('CREATE TABLE IF NOT EXISTS foobar (id INT AUTO INCREMENT, name TEXT)'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP")'); +$db->query('INSERT INTO foobar VALUES (NULL, "PHP6")'); + + +$db->createFunction('testing', function($v) { return strtolower($v); }, 1, PdoSqlite::DETERMINISTIC); + + +foreach ($db->query('SELECT testing(name) FROM foobar') as $row) { + var_dump($row); +} + +$db->query('DROP TABLE foobar'); + +try { + $db->createFunction('not_going_to_work', function($v) { return strtolower($v); }, -5, PdoSqlite::DETERMINISTIC); + echo "Failed to throw exception with invalid parameter.\n"; +} +catch (ValueError $ve) { + echo $ve->getMessage() . "\n"; +} + +echo "Fin."; +?> +--EXPECT-- +PdoSqlite class exists. +array(2) { + ["testing(name)"]=> + string(3) "php" + [0]=> + string(3) "php" +} +array(2) { + ["testing(name)"]=> + string(4) "php6" + [0]=> + string(4) "php6" +} +PdoSqlite::createFunction(): Argument #3 ($num_args) must be either -1 (for any number of parameters) or greater than equal to zero. +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt new file mode 100644 index 0000000000000..bdc9d2be66114 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_003.phpt @@ -0,0 +1,68 @@ +--TEST-- +PdoSqlite load extension +--EXTENSIONS-- +pdo +--FILE-- +loadExtension($extension_location); + +if ($result !== true) { + echo "Failed to load extension mod_spatialite.so"; + exit(-1); +} + +$result = $db->query('SELECT AsText(Buffer(GeomFromText("LINESTRING(0 0, 1 0)"), 0.2)) as geometry_data;'); + +$row = $result->fetch(PDO::FETCH_ASSOC); +if ($row === false) { + echo "Failed to get data from geometry."; + exit(-1); +} + +if (array_key_exists('geometry_data', $row) !== true) { + echo "Data is not under key 'geometry_data'. Available array keys are:"; + var_dump(array_keys($row)); + exit(-1); +} + +echo $row['geometry_data'] . "\n"; + +$row = $result->fetch(PDO::FETCH_ASSOC); + +if ($row !== false) { + echo "We appear to have more data than expected."; + var_dump($row); + exit(-1); +} + +echo "Fin."; +?> +--EXPECT-- +POLYGON((1 0.2, 1.010467 0.199726, 1.020906 0.198904, 1.031287 0.197538, 1.041582 0.19563, 1.051764 0.193185, 1.061803 0.190211, 1.071674 0.186716, 1.081347 0.182709, 1.090798 0.178201, 1.1 0.173205, 1.108928 0.167734, 1.117557 0.161803, 1.125864 0.155429, 1.133826 0.148629, 1.141421 0.141421, 1.148629 0.133826, 1.155429 0.125864, 1.161803 0.117557, 1.167734 0.108928, 1.173205 0.1, 1.178201 0.090798, 1.182709 0.081347, 1.186716 0.071674, 1.190211 0.061803, 1.193185 0.051764, 1.19563 0.041582, 1.197538 0.031287, 1.198904 0.020906, 1.199726 0.010467, 1.2 0, 1.199726 -0.010467, 1.198904 -0.020906, 1.197538 -0.031287, 1.19563 -0.041582, 1.193185 -0.051764, 1.190211 -0.061803, 1.186716 -0.071674, 1.182709 -0.081347, 1.178201 -0.090798, 1.173205 -0.1, 1.167734 -0.108928, 1.161803 -0.117557, 1.155429 -0.125864, 1.148629 -0.133826, 1.141421 -0.141421, 1.133826 -0.148629, 1.125864 -0.155429, 1.117557 -0.161803, 1.108928 -0.167734, 1.1 -0.173205, 1.090798 -0.178201, 1.081347 -0.182709, 1.071674 -0.186716, 1.061803 -0.190211, 1.051764 -0.193185, 1.041582 -0.19563, 1.031287 -0.197538, 1.020906 -0.198904, 1.010467 -0.199726, 1 -0.2, 0 -0.2, -0.010467 -0.199726, -0.020906 -0.198904, -0.031287 -0.197538, -0.041582 -0.19563, -0.051764 -0.193185, -0.061803 -0.190211, -0.071674 -0.186716, -0.081347 -0.182709, -0.090798 -0.178201, -0.1 -0.173205, -0.108928 -0.167734, -0.117557 -0.161803, -0.125864 -0.155429, -0.133826 -0.148629, -0.141421 -0.141421, -0.148629 -0.133826, -0.155429 -0.125864, -0.161803 -0.117557, -0.167734 -0.108928, -0.173205 -0.1, -0.178201 -0.090798, -0.182709 -0.081347, -0.186716 -0.071674, -0.190211 -0.061803, -0.193185 -0.051764, -0.19563 -0.041582, -0.197538 -0.031287, -0.198904 -0.020906, -0.199726 -0.010467, -0.2 0, -0.199726 0.010467, -0.198904 0.020906, -0.197538 0.031287, -0.19563 0.041582, -0.193185 0.051764, -0.190211 0.061803, -0.186716 0.071674, -0.182709 0.081347, -0.178201 0.090798, -0.173205 0.1, -0.167734 0.108928, -0.161803 0.117557, -0.155429 0.125864, -0.148629 0.133826, -0.141421 0.141421, -0.133826 0.148629, -0.125864 0.155429, -0.117557 0.161803, -0.108928 0.167734, -0.1 0.173205, -0.090798 0.178201, -0.081347 0.182709, -0.071674 0.186716, -0.061803 0.190211, -0.051764 0.193185, -0.041582 0.19563, -0.031287 0.197538, -0.020906 0.198904, -0.010467 0.199726, 0 0.2, 1 0.2)) +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt new file mode 100644 index 0000000000000..256cea80ca812 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_004_blobopen.phpt @@ -0,0 +1,78 @@ +--TEST-- +PdoSqlite::blobOpen stream test +--EXTENSIONS-- +pdo +--FILE-- +exec('CREATE TABLE test (id STRING, data BLOB)'); + +echo "PREPARING insert\n"; +$insert_stmt = $db->prepare("INSERT INTO test (id, data) VALUES (?, ?)"); + +echo "BINDING Parameter\n"; +var_dump($insert_stmt->bindValue(1, 'a', PDO::PARAM_STR)); +var_dump($insert_stmt->bindValue(2, 'TEST TEST', PDO::PARAM_LOB)); +$insert_stmt->execute(); +echo "\n"; + +$stream = $db->openBlob('test', 'data', 1); +var_dump($stream); +echo "Stream Contents\n"; +var_dump(stream_get_contents($stream)); +echo "Writing to read-only stream\n"; +var_dump(fwrite($stream, 'ABCD')); +echo "Closing Stream\n"; +var_dump(fclose($stream)); +echo "Opening stream in write mode\n"; +$stream = $db->openBlob('test', 'data', 1, 'main', PdoSqlite::OPEN_READWRITE); +var_dump($stream); +echo "Writing to blob\n"; +var_dump(fwrite($stream, 'ABCD')); +echo "Stream Contents\n"; +fseek($stream, 0); +var_dump(stream_get_contents($stream)); +echo "Expanding blob size\n"; +var_dump(fwrite($stream, 'ABCD ABCD ABCD')); +echo "Closing Stream\n"; +var_dump(fclose($stream)); + +echo "Done\n"; +?> +--EXPECTF-- +Creating Table +PREPARING insert +BINDING Parameter +bool(true) +bool(true) + +resource(%d) of type (stream) +Stream Contents +string(9) "TEST TEST" +Writing to read-only stream + +Warning: fwrite(): Can't write to blob stream: is open as read only in %s on line %d +bool(false) +Closing Stream +bool(true) +Opening stream in write mode +resource(%d) of type (stream) +Writing to blob +int(4) +Stream Contents +string(9) "ABCD TEST" +Expanding blob size + +Warning: fwrite(): It is not possible to increase the size of a BLOB in %s on line %d +bool(false) +Closing Stream +bool(true) +Done diff --git a/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt new file mode 100644 index 0000000000000..7902c54ca4b5a --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/pdosqlite_load_extension_failure.phpt @@ -0,0 +1,43 @@ +--TEST-- +PdoSqlite load extension +--EXTENSIONS-- +pdo +--FILE-- +loadExtension("/this/does/not_exist"); + echo "Failed to throw exception"; +} +catch (PDOException $pdoException) { + echo $pdoException->getMessage() . "\n"; +} + + + +try { + echo "Loading invalid file.\n"; + $result = $db->loadExtension(__FILE__); + echo "Failed to throw exception"; +} +catch (PDOException $pdoException) { + echo $pdoException->getMessage() . "\n"; +} + + +echo "Fin."; +?> +--EXPECTF-- +Loading non-existent file. +Unable to load extension '/this/does/not_exist' +Loading invalid file. +Unable to load extension '%s: No such file or directory' +Fin. diff --git a/ext/pdo_sqlite/tests/subclasses/stream_test.inc b/ext/pdo_sqlite/tests/subclasses/stream_test.inc new file mode 100644 index 0000000000000..d4bee82fbc492 --- /dev/null +++ b/ext/pdo_sqlite/tests/subclasses/stream_test.inc @@ -0,0 +1,46 @@ +position = 0; + return true; + } + + public function stream_read($count) + { + $ret = substr(self::$string, $this->position, $count); + $this->position += strlen($ret); + return $ret; + } + + public function stream_write($data) + { + return 0; + } + + public function stream_stat() + { + return array('size' => self::$string_length); + } + + public function stream_tell() + { + return $this->position; + } + + public function stream_eof() + { + return ($this->position >= self::$string_length); + } +} + +stream_wrapper_register('pdosqliteBlobTest', PDOSQLite_Test_Stream::class) or die("Unable to register pdosqliteBlobTest stream");