Skip to content

Pdo subclassing #11740

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 53 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
551ab85
Adding PDOSQLite in the wrong place.
Danack Jun 4, 2022
6fb4c7f
Version with PDOSqlite apparently working. Which seems unlikely.
Danack Jun 4, 2022
770658e
Typo on class name.
Danack Jun 4, 2022
ecb641a
Initial version of PDOSQLite::loadExtension.
Danack Jun 21, 2022
f79a562
PDOSQLite::blobOpen is working.
Danack Jun 24, 2022
28e2a5c
Improve test to search for sqlite extension location.
Danack Mar 3, 2023
4134358
Moved code to a more correct place.
Danack Mar 4, 2023
5df9630
First draft of pdo_dblib extension class.
Danack May 5, 2023
a3f690a
Added PDOMySql class and a driver specific method.
Danack May 5, 2023
af73177
Initial version of PDOPgSql sub-class
Danack May 5, 2023
2e78541
Added missed file.
Danack May 5, 2023
fbe86b7
Copied methods across from PDO class.
Danack May 5, 2023
497e192
PDO_DBLIB tests are passing.
Danack May 6, 2023
6d137ff
All subclasses created, ODBC works.
Danack May 6, 2023
c579fd8
pdo_firebird subclass is working.
Danack May 6, 2023
b5c92d3
Changed config files name to be not ignored.
Danack May 6, 2023
8a5bcdd
Fix PDOOci tests
tontonsb May 6, 2023
da341c2
Fix PDOFirebird test names
tontonsb May 6, 2023
f184b17
Use RECREATE for PDOFirebird tests
tontonsb May 6, 2023
d20705f
Add PDOMysql testing setup helper
tontonsb May 6, 2023
4fc8305
Copy methods from being the magic implementations, to being declared …
Danack May 7, 2023
0ebe730
Standardized class names.
Danack Jul 3, 2023
0ddc2d2
Corrected constants to not have prefix.
Danack Jul 3, 2023
b820c99
Added note on safety.
Danack Jul 3, 2023
0a785cc
WS.
Danack Jul 3, 2023
e0652bf
Tidying up.
Danack Jul 3, 2023
82b7a83
Standardised directory name.
Danack Jul 3, 2023
157cd85
Fix tpyo.
Danack Jul 3, 2023
abcd936
Copy function detection in m4 file across from sqlite3, and separate …
Danack Jul 4, 2023
c6ea6f5
Standardized case.
Danack Jul 11, 2023
e18dcea
Remove trailing ?>
Danack Jul 18, 2023
4989bbc
Removed un-needed includes.
Danack Jul 18, 2023
b009e04
CS
Danack Jul 18, 2023
ada37de
Change to ZEND_ASSERT
Danack Jul 18, 2023
fbfc99b
Change to unsigned int. remove setting something to null.
Danack Jul 18, 2023
b6aa681
Remove inappropriate file.
Danack Jul 18, 2023
4313d96
Changed stubs to have type in public const.
Danack Jul 18, 2023
c82219a
Change to throwing an exception.
Danack Jul 18, 2023
344935f
It's fine there.
Danack Jul 18, 2023
14f2db0
Return success or failure....though it is unclear what people would d…
Danack Jul 18, 2023
716e1d0
Fixing minor issues.
Danack Jul 18, 2023
1b5c4c4
Added check against parameter being invalid.
Danack Jul 18, 2023
9207ade
Trying to fix windows issue.
Danack Jul 18, 2023
d2db467
Revert previous change.
Danack Jul 18, 2023
95a2226
Attempting to fix pdo_firebird symbol problem.
Danack Jul 18, 2023
46dd89a
Back to extern.
Danack Jul 18, 2023
fd3a4ae
Externed some things that is working for pdo_oci...
Danack Jul 18, 2023
f21948e
Oh, both.
Danack Jul 18, 2023
67e0deb
Oh, both.
Danack Jul 18, 2023
67fa7b1
Remove class constants which apparently are now generated automatically.
Danack Jul 18, 2023
c0ff7ea
Added alpine location.
Danack Jul 18, 2023
6d42f7e
Relax test criteria.
Danack Jul 18, 2023
e7a1137
Update ext/pdo_firebird/tests/subclasses/pdofirebird_002.phpt
Danack Aug 7, 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
3 changes: 2 additions & 1 deletion ext/pdo/pdo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
86 changes: 82 additions & 4 deletions ext/pdo/pdo_dbh.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; i<number_of_pdo_driver_class_entries; i+=1) {
if (strcmp(pdo_driver_class_entries[i]->driver_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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)
Expand All @@ -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)
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 @@ -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,
Copy link
Member

Choose a reason for hiding this comment

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

Might fix some of the tests

Suggested change
?string $password = null,
#[\SensitiveParameter]
?string $password = null,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean php-src/ext/pdo/tests/sensitive_parameter.phpt? If so, I think that might not be my fault, as I haven't touched that test.

But yes, that probably should be there, and a test for it.

?array $options = null
): PDO|PDOSqlite {}
Copy link

Choose a reason for hiding this comment

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

|PDOSqlite might be a leftover from initial attempts, I think they all extend PDO now (?)


/** @tentative-return-type */
public function beginTransaction(): bool {}

Expand Down
11 changes: 10 additions & 1 deletion ext/pdo/pdo_dbh_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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_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

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;
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 */
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(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);
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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