Skip to content

Pdo sub classing rough WIP #8707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 35 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
86f5b53
Adding PDOSQLite in the wrong place.
Danack Jun 4, 2022
9d69e0e
Version with PDOSqlite apparently working. Which seems unlikely.
Danack Jun 4, 2022
c5317fc
Typo on class name.
Danack Jun 4, 2022
357fffa
Initial version of PDOSQLite::loadExtension.
Danack Jun 21, 2022
a3f77cc
PDOSQLite::blobOpen is working.
Danack Jun 24, 2022
b60f678
Improve test to search for sqlite extension location.
Danack Mar 3, 2023
d95dbfb
Moved code to a more correct place.
Danack Mar 4, 2023
6377bb0
First draft of pdo_dblib extension class.
Danack May 5, 2023
479859e
Added PDOMySql class and a driver specific method.
Danack May 5, 2023
6cbe69e
Initial version of PDOPgSql sub-class
Danack May 5, 2023
df94bbf
Added missed file.
Danack May 5, 2023
a1b050e
Copied methods across from PDO class.
Danack May 5, 2023
fd96e22
PDO_DBLIB tests are passing.
Danack May 6, 2023
835c38b
All subclasses created, ODBC works.
Danack May 6, 2023
a4b3fcf
pdo_firebird subclass is working.
Danack May 6, 2023
a987f0c
Changed config files name to be not ignored.
Danack May 6, 2023
5f80549
Fix PDOOci tests
tontonsb May 6, 2023
4454698
Fix PDOFirebird test names
tontonsb May 6, 2023
cd97c9d
Use RECREATE for PDOFirebird tests
tontonsb May 6, 2023
524c893
Add PDOMysql testing setup helper
tontonsb May 6, 2023
83bb08c
Copy methods from being the magic implementations, to being declared …
Danack May 7, 2023
8803312
Standardized class names.
Danack Jul 3, 2023
08abcf4
Corrected constants to not have prefix.
Danack Jul 3, 2023
9f95d8b
Added note on safety.
Danack Jul 3, 2023
51487c2
WS.
Danack Jul 3, 2023
4572b8d
Tidying up.
Danack Jul 3, 2023
4439f57
Standardised directory name.
Danack Jul 3, 2023
6aafba0
Fix tpyo.
Danack Jul 3, 2023
43acd48
Copy function detection in m4 file across from sqlite3, and separate …
Danack Jul 4, 2023
23aab3c
Standardized case.
Danack Jul 11, 2023
61d8ae2
Remove trailing ?>
Danack Jul 18, 2023
bb3a634
Removed un-needed includes.
Danack Jul 18, 2023
589c950
CS
Danack Jul 18, 2023
48e458b
Change to ZEND_ASSERT
Danack Jul 18, 2023
b51d423
Change to unsigned int. remove setting something to null.
Danack Jul 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 74 additions & 4 deletions ext/pdo/pdo_dbh.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,42 @@ 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
void pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry)
{
if (number_of_pdo_driver_class_entries >= MAX_PDO_SUB_CLASSES) {
// TODO - return false
}

pdo_driver_class_entries[number_of_pdo_driver_class_entries] = driver_class_entry;
number_of_pdo_driver_class_entries += 1;
}


static
void create_specific_pdo_object(zval *new_object, const char *driver_name)
{
for (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;
Expand Down Expand Up @@ -288,7 +320,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) {
Expand Down Expand Up @@ -429,8 +473,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)) {
Expand Down Expand Up @@ -1323,6 +1383,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(void)
Expand Down Expand Up @@ -1423,6 +1485,14 @@ void pdo_dbh_init(void)

REGISTER_PDO_CLASS_CONST_LONG("CURSOR_FWDONLY", (zend_long)PDO_CURSOR_FWDONLY);
REGISTER_PDO_CLASS_CONST_LONG("CURSOR_SCROLL", (zend_long)PDO_CURSOR_SCROLL);


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)
Expand Down
7 changes: 7 additions & 0 deletions ext/pdo/pdo_dbh.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ class PDO
{
public function __construct(string $dsn, ?string $username = null, ?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 {}

Expand Down
11 changes: 10 additions & 1 deletion ext/pdo/pdo_dbh_arginfo.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 7d10dbdfd55eb4a4dc779cbf4fa000cdf4fb3539 */
* Stub hash: 9ad9db0f01bb0f36d1ff11c15428799d38a1a22f */

ZEND_BEGIN_ARG_INFO_EX(arginfo_class_PDO___construct, 0, 0, 1)
ZEND_ARG_TYPE_INFO(0, dsn, IS_STRING, 0)
Expand All @@ -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()

Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions ext/pdo/php_pdo.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
extern zend_module_entry pdo_module_entry;
#define phpext_pdo_ptr &pdo_module_entry

extern zend_class_entry *pdo_dbh_ce;
extern zend_object *pdo_dbh_new(zend_class_entry *ce);

#include "php_version.h"
#define PHP_PDO_VERSION PHP_VERSION

Expand Down
10 changes: 10 additions & 0 deletions ext/pdo/php_pdo_driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it makes sense to have this be a zend_string that is interned by the driver on startup. But that can be a future improvement.

zend_class_entry *driver_ce;
} pdo_driver_class_entry;

void pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry);

/* {{{ methods for a database handle */

/* close or otherwise disconnect the database */
Expand Down
2 changes: 0 additions & 2 deletions ext/pdo/php_pdo_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ int php_pdo_list_entry(void);
void pdo_dbh_init(void);
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);
Expand Down
11 changes: 11 additions & 0 deletions ext/pdo_dblib/pdo_dblib.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand Down
9 changes: 9 additions & 0 deletions ext/pdo_dblib/pdo_dblib.stub.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

/** @generate-class-entries */

/** @not-serializable */
class PdoDblib extends PDO
{

}
20 changes: 20 additions & 0 deletions ext/pdo_dblib/pdo_dblib_arginfo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* This is a generated file, edit the .stub.php file instead.
* Stub hash: 06c2f52a638c1ca9c56bcbebdc00367bb3eb9b26 */




static const zend_function_entry class_PdoDblib_methods[] = {
ZEND_FE_END
};

static zend_class_entry *register_class_PdoDblib(zend_class_entry *class_entry_PDO)
{
zend_class_entry ce, *class_entry;

INIT_CLASS_ENTRY(ce, "PdoDblib", class_PdoDblib_methods);
class_entry = zend_register_internal_class_ex(&ce, class_entry_PDO);
class_entry->ce_flags |= ZEND_ACC_NOT_SERIALIZABLE;

return class_entry;
}
24 changes: 24 additions & 0 deletions ext/pdo_dblib/tests/config_functions.inc
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

function getDsnUserAndPassword()
{

if (false !== getenv('PDO_DBLIB_TEST_DSN')) {
$dsn = getenv('PDO_DBLIB_TEST_DSN');
} else {
$dsn = 'dblib:host=localhost;dbname=test';
}

if (false !== getenv('PDO_DBLIB_TEST_USER')) {
$user = getenv('PDO_DBLIB_TEST_USER');
} else {
$user = 'php';
}

if (false !== getenv('PDO_DBLIB_TEST_PASS')) {
$pass = getenv('PDO_DBLIB_TEST_PASS');
} else {
$pass = 'password';
}
return [$dsn, $user, $pass];
}
49 changes: 49 additions & 0 deletions ext/pdo_dblib/tests/subclasses/pdodblib_001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
--TEST--
PdoDblib basic
--EXTENSIONS--
pdo
--FILE--
<?php

require_once __DIR__ . "/../config_functions.inc";

if (class_exists(PdoDblib::class) === false) {
echo "PdoDblib class does not exist.\n";
exit(-1);
}
echo "PdoDblib class exists.\n";


[$dsn, $user, $pass] = getDsnUserAndPassword();

$db = new PdoDblib($dsn, $user, $pass);

$db->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.
53 changes: 53 additions & 0 deletions ext/pdo_dblib/tests/subclasses/pdodblib_002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
--TEST--
PdoDblib create through PDO::connect
--EXTENSIONS--
pdo
--FILE--
<?php

require_once __DIR__ . "/../config_functions.inc";

if (class_exists(PdoDblib::class) === false) {
echo "PdoDblib class does not exist.\n";
exit(-1);
}

echo "PdoDblib class exists.\n";

[$dsn, $user, $pass] = getDsnUserAndPassword();

$db = Pdo::connect($dsn, $user, $pass);

if (!$db instanceof PdoDblib) {
echo "Wrong class type. Should be PdoDblib but is [" . get_class($db) . "\n";
}

$db->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.
Loading