PDO::__constructで取得したインスタンスから、$dsnを取得できるか
TOP > てきとうにこらむ > ゲーム作りとプログラミング日記 > PDO::__constructで取得したインスタンスから、$dsnを取得できるか
PDO
php-src上の実装では、ext/pdo/pdo_dbh.c 840行目。 (commit id: e2ef4e9e2fa8cdb749ce4c2afdd3499765ffb721)
static PHP_METHOD(PDO, getAttribute)
PDOは、データベースごとに実装が違うため、それを抽象化するという概念。だから、データベースごとにpdoウンタラカンタラとある。
例えばいかのような感じ。
- pdo_mysql
- pdo_pgsql
- pdo_sqlite
DSNを取得できるか
DSNを取得できるだろうか。まずは、DSNを変数として確保しているかどうか。コンストラクタの実装は 195行目。
static PHP_METHOD(PDO, dbh_constructor)
211行目で、データソース(dsn)を取得している。
ZEND_PARSE_PARAMETERS_START_EX(ZEND_PARSE_PARAMS_THROW, 1, 4)
Z_PARAM_STRING(data_source, data_source_len)
なんやかんやあって接続。356行目
if (driver->db_handle_factory(dbh, options)) {
db_handle_factoryは、ext/pdo/php_pdo_driver.hの242行目。pdo_driver_tという構造体。
int (*db_handle_factory)(pdo_dbh_t *dbh, zval *driver_options);
SQLiteの場合はどうか。 ext/pdo_sqlite/php_pdo_sqliteint.hの69行目にある
extern const pdo_driver_t pdo_sqlite_driver;
pdo_sqlite_driver構造体は、ext/pdo_sqlite/sqlitedriver.cの846行目
const pdo_driver_t pdo_sqlite_driver = {
PDO_DRIVER_HEADER(sqlite),
pdo_sqlite_handle_factory
};
pdo_sqlite_handle_factoryは788行目にある。
static int pdo_sqlite_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
MySQLの場合、ext/pdo_mysql/mysqldriver.c。560行目。
static int pdo_mysql_handle_factory(pdo_dbh_t *dbh, zval *driver_options)
接続するのはここ。821行目。
if (mysqlnd_connect(H->server, host, dbh->username, dbh->password, password_len, dbn
パスワードを引数に取らないと認証できないから、dbhにはpasswordというメンバがある。
抜き出すように改造してみる
興味が湧いたので、dbhという変数から、dsnを取得してみるように改造しよう。なんとなく改造して楽しむだけなので、品質は保証しない。プロダクションに入れるべきではない。
- バージョンは、PHP 7.3系
- 手間の関係で、SQLiteだけにする
- 定数PDO::ATTR_CALL_DSNを新たに作成する
- PDO::getAttributeにPDO::ATTR_CALL_DSNを受け取れるようにして、返り値をDSNにする
これで取得できるはず。
patch
たぶん、こうすればできると思う。patchを作成しよう
$ git diff > sqlite_call_dsn.patch
作成したpatchは以下
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index 36bb2a17ac..17003d628c 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -1441,6 +1441,7 @@ void pdo_dbh_init(void)
REGISTER_PDO_CLASS_CONST_LONG("ATTR_CLIENT_VERSION", (zend_long)PDO_ATTR_CLIENT_VERSION);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_SERVER_INFO", (zend_long)PDO_ATTR_SERVER_INFO);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_CONNECTION_STATUS", (zend_long)PDO_ATTR_CONNECTION_STATUS);
+ REGISTER_PDO_CLASS_CONST_LONG("ATTR_CALL_DSN", (zend_long)PDO_ATTR_CALL_DSN);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_CASE", (zend_long)PDO_ATTR_CASE);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR_NAME", (zend_long)PDO_ATTR_CURSOR_NAME);
REGISTER_PDO_CLASS_CONST_LONG("ATTR_CURSOR", (zend_long)PDO_ATTR_CURSOR);
diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h
index 3350211a5e..9ba4b6d4ac 100644
--- a/ext/pdo/php_pdo_driver.h
+++ b/ext/pdo/php_pdo_driver.h
@@ -147,6 +147,7 @@ enum pdo_attribute_type {
PDO_ATTR_DEFAULT_FETCH_MODE, /* Set the default fetch mode */
PDO_ATTR_EMULATE_PREPARES, /* use query emulation rather than native */
PDO_ATTR_DEFAULT_STR_PARAM, /* set the default string parameter type (see the PDO::PARAM_STR_* magic flags) */
+ PDO_ATTR_CALL_DSN,
/* this defines the start of the range for driver specific options.
* Drivers should define their own attribute constants beginning with this
diff --git a/ext/pdo_sqlite/sqlite_driver.c b/ext/pdo_sqlite/sqlite_driver.c
index 2cc7f72475..75f64d6ca3 100644
--- a/ext/pdo_sqlite/sqlite_driver.c
+++ b/ext/pdo_sqlite/sqlite_driver.c
@@ -289,7 +289,9 @@ static int pdo_sqlite_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *return
case PDO_ATTR_SERVER_VERSION:
ZVAL_STRING(return_value, (char *)sqlite3_libversion());
break;
-
+ case PDO_ATTR_CALL_DSN:
+ ZVAL_STRING(return_value, dbh->data_source);
+ break;
default:
return 0;
}
patchコマンドを使って反映できる。
$ patch -p1 < sqlite_call_dsn.patch
これでmakeすれば反映できる
$ make
実行してみる
$ sapi/cli/php -r '$p = new PDO("sqlite::memory;"); var_dump($p->getAttribute(PDO::ATTR_CALL_DSN));'
string(8) ":memory;"
sqlite:memory;から、:memory;を取り出すことができた。sqlite:が抜けたのは想定外だった。
感想
改造するのは楽しいですね。
参考
- Git で変更を patch ファイルにする / patch コマンドで適用する
- https://www.php.net/manual/ja/pdo.drivers.php データベースごとのDSL
- https://www.php.net/manual/ja/ref.pdo-pgsql.connection.php PostgreSQLはDSNに対して、パスワードがかける
元ネタ
Slack phpusers-ja