Skip to content

Commit b55473a

Browse files
Danackkocsismate
andcommitted
Implement PDO driver specific sub-classes
RFC: https://wiki.php.net/rfc/pdo_driver_specific_subclasses Co-authored-by: Máté Kocsis <[email protected]>
1 parent 4d51d58 commit b55473a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+3088
-42
lines changed

ext/pdo/pdo_dbh.c

+73-4
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,42 @@ static char *dsn_from_uri(char *uri, char *buf, size_t buflen) /* {{{ */
221221
}
222222
/* }}} */
223223

224-
/* {{{ */
225-
PHP_METHOD(PDO, __construct)
224+
225+
#define MAX_PDO_SUB_CLASSES 64
226+
static unsigned int number_of_pdo_driver_class_entries = 0;
227+
static pdo_driver_class_entry *pdo_driver_class_entries[MAX_PDO_SUB_CLASSES];
228+
229+
// It would be possible remove this and roll it into the standard driver class entries
230+
// I chose not to do it at this time, as that would break existing PDO extensions
231+
void pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry)
232+
{
233+
if (number_of_pdo_driver_class_entries >= MAX_PDO_SUB_CLASSES) {
234+
// TODO - return false
235+
}
236+
237+
pdo_driver_class_entries[number_of_pdo_driver_class_entries] = driver_class_entry;
238+
number_of_pdo_driver_class_entries += 1;
239+
}
240+
241+
242+
static
243+
void create_specific_pdo_object(zval *new_object, const char *driver_name)
244+
{
245+
for (int i=0; i < number_of_pdo_driver_class_entries; i += 1) {
246+
pdo_driver_class_entry *driver_class_entry = pdo_driver_class_entries[i];
247+
if (strcmp(driver_class_entry->driver_name, driver_name) == 0) {
248+
object_init_ex(new_object, driver_class_entry->driver_ce);
249+
return;
250+
}
251+
}
252+
253+
// No specific DB implementation found
254+
object_init_ex(new_object, pdo_dbh_ce);
255+
}
256+
257+
static
258+
void internal_construct(INTERNAL_FUNCTION_PARAMETERS, zval *object, zval *new_zval_object)
226259
{
227-
zval *object = ZEND_THIS;
228260
pdo_dbh_t *dbh = NULL;
229261
bool is_persistent = 0;
230262
char *data_source;
@@ -291,7 +323,19 @@ PHP_METHOD(PDO, __construct)
291323
RETURN_THROWS();
292324
}
293325

294-
dbh = Z_PDO_DBH_P(object);
326+
if (object == NULL) {
327+
ZEND_ASSERT((driver->driver_name != NULL) && "PDO driver name is null");
328+
create_specific_pdo_object(new_zval_object, driver->driver_name);
329+
330+
if (new_zval_object == NULL) {
331+
zend_throw_exception_ex(php_pdo_get_exception(), 0, "Failed to create specific PDO class");
332+
RETURN_THROWS();
333+
}
334+
335+
dbh = Z_PDO_DBH_P(new_zval_object);
336+
} else {
337+
dbh = Z_PDO_DBH_P(object);
338+
}
295339

296340
/* is this supposed to be a persistent connection ? */
297341
if (options) {
@@ -432,8 +476,24 @@ PHP_METHOD(PDO, __construct)
432476
zend_throw_exception(pdo_exception_ce, "Constructor failed", 0);
433477
}
434478
}
479+
480+
/* {{{ */
481+
PHP_METHOD(PDO, __construct)
482+
{
483+
zval *object = ZEND_THIS;
484+
internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, object, NULL);
485+
}
435486
/* }}} */
436487

488+
489+
/* {{{ */
490+
PHP_METHOD(PDO, connect)
491+
{
492+
internal_construct(INTERNAL_FUNCTION_PARAM_PASSTHRU, NULL, return_value);
493+
}
494+
/* }}} */
495+
496+
437497
static zval *pdo_stmt_instantiate(pdo_dbh_t *dbh, zval *object, zend_class_entry *dbstmt_ce, zval *ctor_args) /* {{{ */
438498
{
439499
if (!Z_ISUNDEF_P(ctor_args)) {
@@ -1329,6 +1389,8 @@ static HashTable *dbh_get_gc(zend_object *object, zval **gc_data, int *gc_count)
13291389
}
13301390

13311391
static zend_object_handlers pdo_dbh_object_handlers;
1392+
static zend_object_handlers pdosqlite_dbh_object_handlers;
1393+
13321394
static void pdo_dbh_free_storage(zend_object *std);
13331395

13341396
void pdo_dbh_init(int module_number)
@@ -1344,6 +1406,13 @@ void pdo_dbh_init(int module_number)
13441406
pdo_dbh_object_handlers.get_method = dbh_method_get;
13451407
pdo_dbh_object_handlers.compare = zend_objects_not_comparable;
13461408
pdo_dbh_object_handlers.get_gc = dbh_get_gc;
1409+
1410+
memcpy(&pdosqlite_dbh_object_handlers, &std_object_handlers, sizeof(zend_object_handlers));
1411+
pdosqlite_dbh_object_handlers.offset = XtOffsetOf(pdo_dbh_object_t, std);
1412+
pdosqlite_dbh_object_handlers.free_obj = pdo_dbh_free_storage;
1413+
pdosqlite_dbh_object_handlers.get_method = dbh_method_get;
1414+
pdosqlite_dbh_object_handlers.compare = zend_objects_not_comparable;
1415+
pdosqlite_dbh_object_handlers.get_gc = dbh_get_gc;
13471416
}
13481417

13491418
static void dbh_free(pdo_dbh_t *dbh, bool free_persistent)

ext/pdo/pdo_dbh.stub.php

+7
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ class PDO
166166

167167
public function __construct(string $dsn, ?string $username = null, #[\SensitiveParameter] ?string $password = null, ?array $options = null) {}
168168

169+
public static function connect(
170+
string $dsn,
171+
?string $username = null,
172+
#[\SensitiveParameter] ?string $password = null,
173+
?array $options = null
174+
): static {}
175+
169176
/** @tentative-return-type */
170177
public function beginTransaction(): bool {}
171178

ext/pdo/pdo_dbh_arginfo.h

+12-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ext/pdo/php_pdo.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
extern zend_module_entry pdo_module_entry;
2323
#define phpext_pdo_ptr &pdo_module_entry
2424

25+
extern zend_class_entry *pdo_dbh_ce;
26+
extern zend_object *pdo_dbh_new(zend_class_entry *ce);
27+
2528
#include "php_version.h"
2629
#define PHP_PDO_VERSION PHP_VERSION
2730

@@ -50,9 +53,6 @@ PHP_MINFO_FUNCTION(pdo);
5053
#define REGISTER_PDO_CLASS_CONST_LONG(const_name, value) \
5154
zend_declare_class_constant_long(php_pdo_get_dbh_ce(), const_name, sizeof(const_name)-1, (zend_long)value);
5255

53-
#define REGISTER_PDO_CLASS_CONST_STRING(const_name, value) \
54-
zend_declare_class_constant_stringl(php_pdo_get_dbh_ce(), const_name, sizeof(const_name)-1, value, sizeof(value)-1);
55-
5656
#define LONG_CONST(c) (zend_long) c
5757

5858
#define PDO_CONSTRUCT_CHECK \

ext/pdo/php_pdo_driver.h

+10
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,16 @@ typedef struct {
215215

216216
} pdo_driver_t;
217217

218+
// NOTE - This separate struct, could be rolled it into pdo_driver_t
219+
// I chose not to, as that would cause BC break and require a lot of
220+
// downstream work.
221+
typedef struct {
222+
char *driver_name;
223+
zend_class_entry *driver_ce;
224+
} pdo_driver_class_entry;
225+
226+
void pdo_register_driver_specific_class(pdo_driver_class_entry *driver_class_entry);
227+
218228
/* {{{ methods for a database handle */
219229

220230
/* close or otherwise disconnect the database */

ext/pdo/php_pdo_int.h

-2
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ int php_pdo_list_entry(void);
2828
void pdo_dbh_init(int module_number);
2929
void pdo_stmt_init(void);
3030

31-
extern zend_object *pdo_dbh_new(zend_class_entry *ce);
3231
extern const zend_function_entry pdo_dbh_functions[];
33-
extern zend_class_entry *pdo_dbh_ce;
3432
extern ZEND_RSRC_DTOR_FUNC(php_pdo_pdbh_dtor);
3533

3634
extern zend_object *pdo_dbstmt_new(zend_class_entry *ce);

ext/pdo/tests/pdo_test.inc

+2-2
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,12 @@ class PDOTest {
5454
}
5555
}
5656

57-
static function test_factory($file) {
57+
static function test_factory($file, $classname = 'PDO') {
5858
$config = self::get_config($file);
5959
foreach ($config['ENV'] as $k => $v) {
6060
putenv("$k=$v");
6161
}
62-
return self::factory();
62+
return self::factory($classname);
6363
}
6464

6565
static function get_config($file) {

ext/pdo_dblib/pdo_dblib.c

+11
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,14 @@
2727
#include "php_pdo_dblib.h"
2828
#include "php_pdo_dblib_int.h"
2929
#include "zend_exceptions.h"
30+
#include "pdo_dblib_arginfo.h"
3031

3132
ZEND_DECLARE_MODULE_GLOBALS(dblib)
3233
static PHP_GINIT_FUNCTION(dblib);
3334

35+
zend_class_entry *PdoDblib_ce;
36+
static pdo_driver_class_entry PdoDblib_pdo_driver_class_entry;
37+
3438
static const zend_module_dep pdo_dblib_deps[] = {
3539
ZEND_MOD_REQUIRED("pdo")
3640
ZEND_MOD_END
@@ -201,6 +205,13 @@ PHP_MINIT_FUNCTION(pdo_dblib)
201205
return FAILURE;
202206
}
203207

208+
PdoDblib_ce = register_class_PdoDblib(pdo_dbh_ce);
209+
PdoDblib_ce->create_object = pdo_dbh_new;
210+
211+
PdoDblib_pdo_driver_class_entry.driver_name = "dblib";
212+
PdoDblib_pdo_driver_class_entry.driver_ce = PdoDblib_ce;
213+
pdo_register_driver_specific_class(&PdoDblib_pdo_driver_class_entry);
214+
204215
if (FAILURE == php_pdo_register_driver(&pdo_dblib_driver)) {
205216
return FAILURE;
206217
}

ext/pdo_dblib/pdo_dblib.stub.php

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/** @generate-class-entries */
4+
5+
/**
6+
* @strict-properties
7+
* @not-serializable
8+
*/
9+
class PdoDblib extends PDO
10+
{
11+
/** @cvalue PDO_DBLIB_ATTR_CONNECTION_TIMEOUT */
12+
public const int ATTR_CONNECTION_TIMEOUT = UNKNOWN;
13+
14+
/** @cvalue PDO_DBLIB_ATTR_QUERY_TIMEOUT */
15+
public const int ATTR_QUERY_TIMEOUT = UNKNOWN;
16+
17+
/** @cvalue PDO_DBLIB_ATTR_STRINGIFY_UNIQUEIDENTIFIER */
18+
public const int ATTR_STRINGIFY_UNIQUEIDENTIFIER = UNKNOWN;
19+
20+
/** @cvalue PDO_DBLIB_ATTR_VERSION */
21+
public const int ATTR_VERSION = UNKNOWN;
22+
23+
/** @cvalue PDO_DBLIB_ATTR_TDS_VERSION */
24+
public const int ATTR_TDS_VERSION = UNKNOWN;
25+
26+
/** @cvalue PDO_DBLIB_ATTR_SKIP_EMPTY_ROWSETS */
27+
public const int ATTR_SKIP_EMPTY_ROWSETS = UNKNOWN;
28+
29+
/** @cvalue PDO_DBLIB_ATTR_DATETIME_CONVERT */
30+
public const int ATTR_DATETIME_CONVERT = UNKNOWN;
31+
}

ext/pdo_dblib/pdo_dblib_arginfo.h

+62
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
function getDsnUserAndPassword()
4+
{
5+
6+
if (false !== getenv('PDO_DBLIB_TEST_DSN')) {
7+
$dsn = getenv('PDO_DBLIB_TEST_DSN');
8+
} else {
9+
$dsn = 'dblib:host=localhost;dbname=test';
10+
}
11+
12+
if (false !== getenv('PDO_DBLIB_TEST_USER')) {
13+
$user = getenv('PDO_DBLIB_TEST_USER');
14+
} else {
15+
$user = 'php';
16+
}
17+
18+
if (false !== getenv('PDO_DBLIB_TEST_PASS')) {
19+
$pass = getenv('PDO_DBLIB_TEST_PASS');
20+
} else {
21+
$pass = 'password';
22+
}
23+
return [$dsn, $user, $pass];
24+
}

0 commit comments

Comments
 (0)