PDO::mysql_client_encodingの実装についての研究
カテゴリー: ぷろぐらみんぐ/メモ
2011-02-14
PDO::mysql_client_encodingが 無くてSQLで発行すると不便なので実装してみた。
PDO::mysql_set_charset($charsetname)
PDO::mysql_client_encoding()
PDO::mysql_select_db($dbname)
使い方
$dbh->mysql_set_charset('utf8');
echo $dbh->mysql_client_encoding();
同じことをsqlで発行するには
$dbh->exec('SET CHARACTER SET utf8 ');
show variables like 'character_set_client' で Value列を取得
SQLレベルでPDOに実装をまねるなら PDOクラスを継承するといい。
もし、外部から値を受け取る場合は、
その際は、外部から値を受け付けないようにSQLインジェクション対策が必要だろう。
SHOW CHARACTER SETを使うと一覧がとれるので、その中と照合して、設定するようにすれば、よい。
今回は、PDO_MysqlのCソースレベルで対応した。
PHP_ME , PHP_METHOD の PHP_をZEND_にするようにとヘルプにはあったが、
・名前が嫌い、銘銘方法がきにくわない(企業名だし)
・他のPDOが使っていない
ということで今回は使うのは避けた。
有効桁の低い+中途半端なスクリプト言語に期待しても意味がないし、
わざわざ、PDO Cソースレベルので改造は、
コンパイルなどの手間がかかるので
素直に、適当な関数を作るか、PDOを継承したクラスにスクリプトコードで実装するほうが現実的だろう。
「実行結果」
sql: mysql_client_encoding: latin1
get_class :PDO
is_a($dbh ,'PDO') :true
is_a($dbh ,'PDO_MYSQL') :false
$dbh instanceof PDO_MYSQL :false
PDO::ATTR_DRIVER_NAME :mysql
PDO::ATTR_SERVER_VERSION :5.1.31-community
PDO::ATTR_CLIENT_VERSION :5.0.67
check encoding
mysql_client_encoding: latin1
set utf8 :true
mysql_client_encoding: utf8
mysql_select_db true
[スクリプトソース]
<?php
// ../../php-test/bin/php -d extension_dir=$PWD/modules/ ../../pdo_mysql_charset_check.php
// php-test/bin/php -d extension_dir=ext/pdo_mysql/modules/ pdo_mysql_charset_check.php
$ext_mysql_name = "pdo_mysql";
if (!extension_loaded ($ext_mysql_name))
dl($ext_mysql_name.((preg_match('/win/i',PHP_OS))?".dll":".so"));
if (preg_match('/win/i',PHP_OS))
$host = "localhost";
else
$host = "192.168.1.1"; //
try {
$dbh = new PDO("mysql:host=$host;dbname=test;","test","");
} catch (PDOException $e) {
}
if (!method_exists($dbh, "mysql_set_charset"))
{
exit("no function: PDO::mysql_set_charset\n");
}
echo "\n";
// show variables like 'character_set_client'
// show variables like 'character_set_server'
// result: Variable_name , Value
if ( $sth = $dbh->prepare("show variables like 'character_set_client'") )
if ($sth->execute())
echo "sql: mysql_client_encoding: ".$sth->fetchColumn(1)."\n";
// sleep(10); // discnnnect test: waite 10 sec
echo " get_class :". get_class($dbh)."\n";
echo " is_a(\$dbh ,'PDO') :". (is_a($dbh, "PDO") ? "true" : "false") ."\n";
echo " is_a(\$dbh ,'PDO_MYSQL') :". (is_a($dbh, "PDO_MYSQL") ? "true" : "false") ."\n";
echo " \$dbh instanceof PDO_MYSQL :". (($dbh instanceof PDO_MYSQL) ? "true" : "false") ."\n";
echo " PDO::ATTR_DRIVER_NAME :". $dbh->getAttribute(PDO::ATTR_DRIVER_NAME) ."\n";
echo " PDO::ATTR_SERVER_VERSION :". $dbh->getAttribute(PDO::ATTR_SERVER_VERSION) ."\n";
echo " PDO::ATTR_CLIENT_VERSION :". $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION) ."\n";
echo "\n";
echo "check encoding "."\n";
echo " mysql_client_encoding: ".$dbh->mysql_client_encoding()."\n";
echo " set utf8 :". ($dbh->mysql_set_charset("utf8") ? "true" : "false") ."\n";
echo " mysql_client_encoding: ".$dbh->mysql_client_encoding()."\n";
// echo "mysql_client_encoding: ".PDO::mysql_client_encoding()."\n";
echo "\n";
echo "mysql_select_db ". ($dbh->mysql_select_db("test") ? "true" : "false") ."\n";
?>
[php pdoドライバソース差分] copyright okamerin.com
--- php-5.2.17/ext/pdo_mysql/mysql_driver.c 2010-01-12 21:46:54.000000000 +0900
+++ php-5.2.17/ext/pdo_mysql/mysql_driver.c 2011-02-10 00:00:00.000000000 +0900
@@ -33,6 +33,9 @@
#include <mysqld_error.h>
#include "zend_exceptions.h"
+#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007
+#define MYSQL_HAS_SET_CHARSET
+#endif
const char *pdo_mysql_get_sqlstate(unsigned int my_errno) {
switch (my_errno) {
@@ -408,6 +411,93 @@
}
/* }}} */
+#ifdef MYSQL_HAS_SET_CHARSET
+static PHP_METHOD(PDO, mysql_set_charset)
+{
+ pdo_dbh_t *dbh;
+ pdo_mysql_db_handle *H;
+ char *csname;
+ int csname_len;
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_mysql_db_handle *)dbh->driver_data;
+ if (!H->server) { RETURN_FALSE; }
+
+ if (ZEND_NUM_ARGS() <= 0) { WRONG_PARAM_COUNT; }
+
+ if (zend_parse_parameters(1 TSRMLS_CC, "s", &csname, &csname_len) == FAILURE) { RETURN_FALSE; }
+
+ // Zero for success.
+ if (0 == mysql_set_character_set(H->server, csname) )
+ { RETURN_TRUE; }
+ else
+ { RETURN_FALSE; }
+}
+#endif
+
+static PHP_METHOD(PDO, mysql_client_encoding)
+{
+ pdo_dbh_t *dbh;
+ pdo_mysql_db_handle *H;
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_mysql_db_handle *)dbh->driver_data;
+ if (!H->server) { RETURN_EMPTY_STRING(); }
+
+ if (ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; }
+
+ RETURN_STRING((char *)mysql_character_set_name(H->server) , 1);
+}
+
+static PHP_METHOD(PDO, mysql_select_db)
+{
+ pdo_dbh_t *dbh;
+ pdo_mysql_db_handle *H;
+ char *dbname;
+ int dbname_len;
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_mysql_db_handle *)dbh->driver_data;
+ if (!H->server) { RETURN_FALSE; }
+
+ if (ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
+
+ if (zend_parse_parameters(1 TSRMLS_CC, "s", &dbname, &dbname_len) == FAILURE) { RETURN_FALSE; }
+
+ // Zero for success.
+ if (0 == mysql_select_db(H->server, dbname) )
+ { RETURN_TRUE; }
+ else
+ { RETURN_FALSE; }
+}
+
+static zend_function_entry dbh_methods[] = {
+#ifdef MYSQL_HAS_SET_CHARSET
+ PHP_ME(PDO, mysql_set_charset, NULL, ZEND_ACC_PUBLIC)
+#endif
+ PHP_ME(PDO, mysql_client_encoding, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(PDO, mysql_select_db, NULL, ZEND_ACC_PUBLIC)
+ {NULL, NULL, NULL}
+};
+// mysql_get_client_version() , PDO::getAttribute(PDO::ATTR_CLIENT_VERSION)
+// mysql_get_server_version() , PDO::getAttribute(PDO::ATTR_SERVER_VERSION)
+
+static zend_function_entry *pdo_mysql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
+{
+ switch (kind) {
+ case PDO_DBH_DRIVER_METHOD_KIND_DBH:
+ return dbh_methods;
+ default:
+ return NULL;
+ }
+}
+
static struct pdo_dbh_methods mysql_methods = {
mysql_handle_closer,
mysql_handle_preparer,
@@ -420,7 +510,8 @@
pdo_mysql_last_insert_id,
pdo_mysql_fetch_error_func,
pdo_mysql_get_attribute,
- pdo_mysql_check_liveness
+ pdo_mysql_check_liveness,
+ pdo_mysql_get_driver_methods /* get_driver_methods */
};
#ifndef PDO_MYSQL_UNIX_ADDR
PDO::mysql_set_charset($charsetname)
PDO::mysql_client_encoding()
PDO::mysql_select_db($dbname)
使い方
$dbh->mysql_set_charset('utf8');
echo $dbh->mysql_client_encoding();
同じことをsqlで発行するには
$dbh->exec('SET CHARACTER SET utf8 ');
show variables like 'character_set_client' で Value列を取得
SQLレベルでPDOに実装をまねるなら PDOクラスを継承するといい。
もし、外部から値を受け取る場合は、
その際は、外部から値を受け付けないようにSQLインジェクション対策が必要だろう。
SHOW CHARACTER SETを使うと一覧がとれるので、その中と照合して、設定するようにすれば、よい。
今回は、PDO_MysqlのCソースレベルで対応した。
PHP_ME , PHP_METHOD の PHP_をZEND_にするようにとヘルプにはあったが、
・名前が嫌い、銘銘方法がきにくわない(企業名だし)
・他のPDOが使っていない
ということで今回は使うのは避けた。
有効桁の低い+中途半端なスクリプト言語に期待しても意味がないし、
わざわざ、PDO Cソースレベルので改造は、
コンパイルなどの手間がかかるので
素直に、適当な関数を作るか、PDOを継承したクラスにスクリプトコードで実装するほうが現実的だろう。
「実行結果」
sql: mysql_client_encoding: latin1
get_class :PDO
is_a($dbh ,'PDO') :true
is_a($dbh ,'PDO_MYSQL') :false
$dbh instanceof PDO_MYSQL :false
PDO::ATTR_DRIVER_NAME :mysql
PDO::ATTR_SERVER_VERSION :5.1.31-community
PDO::ATTR_CLIENT_VERSION :5.0.67
check encoding
mysql_client_encoding: latin1
set utf8 :true
mysql_client_encoding: utf8
mysql_select_db true
[スクリプトソース]
<?php
// ../../php-test/bin/php -d extension_dir=$PWD/modules/ ../../pdo_mysql_charset_check.php
// php-test/bin/php -d extension_dir=ext/pdo_mysql/modules/ pdo_mysql_charset_check.php
$ext_mysql_name = "pdo_mysql";
if (!extension_loaded ($ext_mysql_name))
dl($ext_mysql_name.((preg_match('/win/i',PHP_OS))?".dll":".so"));
if (preg_match('/win/i',PHP_OS))
$host = "localhost";
else
$host = "192.168.1.1"; //
try {
$dbh = new PDO("mysql:host=$host;dbname=test;","test","");
} catch (PDOException $e) {
}
if (!method_exists($dbh, "mysql_set_charset"))
{
exit("no function: PDO::mysql_set_charset\n");
}
echo "\n";
// show variables like 'character_set_client'
// show variables like 'character_set_server'
// result: Variable_name , Value
if ( $sth = $dbh->prepare("show variables like 'character_set_client'") )
if ($sth->execute())
echo "sql: mysql_client_encoding: ".$sth->fetchColumn(1)."\n";
// sleep(10); // discnnnect test: waite 10 sec
echo " get_class :". get_class($dbh)."\n";
echo " is_a(\$dbh ,'PDO') :". (is_a($dbh, "PDO") ? "true" : "false") ."\n";
echo " is_a(\$dbh ,'PDO_MYSQL') :". (is_a($dbh, "PDO_MYSQL") ? "true" : "false") ."\n";
echo " \$dbh instanceof PDO_MYSQL :". (($dbh instanceof PDO_MYSQL) ? "true" : "false") ."\n";
echo " PDO::ATTR_DRIVER_NAME :". $dbh->getAttribute(PDO::ATTR_DRIVER_NAME) ."\n";
echo " PDO::ATTR_SERVER_VERSION :". $dbh->getAttribute(PDO::ATTR_SERVER_VERSION) ."\n";
echo " PDO::ATTR_CLIENT_VERSION :". $dbh->getAttribute(PDO::ATTR_CLIENT_VERSION) ."\n";
echo "\n";
echo "check encoding "."\n";
echo " mysql_client_encoding: ".$dbh->mysql_client_encoding()."\n";
echo " set utf8 :". ($dbh->mysql_set_charset("utf8") ? "true" : "false") ."\n";
echo " mysql_client_encoding: ".$dbh->mysql_client_encoding()."\n";
// echo "mysql_client_encoding: ".PDO::mysql_client_encoding()."\n";
echo "\n";
echo "mysql_select_db ". ($dbh->mysql_select_db("test") ? "true" : "false") ."\n";
?>
[php pdoドライバソース差分] copyright okamerin.com
--- php-5.2.17/ext/pdo_mysql/mysql_driver.c 2010-01-12 21:46:54.000000000 +0900
+++ php-5.2.17/ext/pdo_mysql/mysql_driver.c 2011-02-10 00:00:00.000000000 +0900
@@ -33,6 +33,9 @@
#include <mysqld_error.h>
#include "zend_exceptions.h"
+#if (MYSQL_VERSION_ID >= 40113 && MYSQL_VERSION_ID < 50000) || MYSQL_VERSION_ID >= 50007
+#define MYSQL_HAS_SET_CHARSET
+#endif
const char *pdo_mysql_get_sqlstate(unsigned int my_errno) {
switch (my_errno) {
@@ -408,6 +411,93 @@
}
/* }}} */
+#ifdef MYSQL_HAS_SET_CHARSET
+static PHP_METHOD(PDO, mysql_set_charset)
+{
+ pdo_dbh_t *dbh;
+ pdo_mysql_db_handle *H;
+ char *csname;
+ int csname_len;
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_mysql_db_handle *)dbh->driver_data;
+ if (!H->server) { RETURN_FALSE; }
+
+ if (ZEND_NUM_ARGS() <= 0) { WRONG_PARAM_COUNT; }
+
+ if (zend_parse_parameters(1 TSRMLS_CC, "s", &csname, &csname_len) == FAILURE) { RETURN_FALSE; }
+
+ // Zero for success.
+ if (0 == mysql_set_character_set(H->server, csname) )
+ { RETURN_TRUE; }
+ else
+ { RETURN_FALSE; }
+}
+#endif
+
+static PHP_METHOD(PDO, mysql_client_encoding)
+{
+ pdo_dbh_t *dbh;
+ pdo_mysql_db_handle *H;
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_mysql_db_handle *)dbh->driver_data;
+ if (!H->server) { RETURN_EMPTY_STRING(); }
+
+ if (ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; }
+
+ RETURN_STRING((char *)mysql_character_set_name(H->server) , 1);
+}
+
+static PHP_METHOD(PDO, mysql_select_db)
+{
+ pdo_dbh_t *dbh;
+ pdo_mysql_db_handle *H;
+ char *dbname;
+ int dbname_len;
+
+ dbh = zend_object_store_get_object(getThis() TSRMLS_CC);
+ PDO_CONSTRUCT_CHECK;
+
+ H = (pdo_mysql_db_handle *)dbh->driver_data;
+ if (!H->server) { RETURN_FALSE; }
+
+ if (ZEND_NUM_ARGS() != 1) { WRONG_PARAM_COUNT; }
+
+ if (zend_parse_parameters(1 TSRMLS_CC, "s", &dbname, &dbname_len) == FAILURE) { RETURN_FALSE; }
+
+ // Zero for success.
+ if (0 == mysql_select_db(H->server, dbname) )
+ { RETURN_TRUE; }
+ else
+ { RETURN_FALSE; }
+}
+
+static zend_function_entry dbh_methods[] = {
+#ifdef MYSQL_HAS_SET_CHARSET
+ PHP_ME(PDO, mysql_set_charset, NULL, ZEND_ACC_PUBLIC)
+#endif
+ PHP_ME(PDO, mysql_client_encoding, NULL, ZEND_ACC_PUBLIC)
+ PHP_ME(PDO, mysql_select_db, NULL, ZEND_ACC_PUBLIC)
+ {NULL, NULL, NULL}
+};
+// mysql_get_client_version() , PDO::getAttribute(PDO::ATTR_CLIENT_VERSION)
+// mysql_get_server_version() , PDO::getAttribute(PDO::ATTR_SERVER_VERSION)
+
+static zend_function_entry *pdo_mysql_get_driver_methods(pdo_dbh_t *dbh, int kind TSRMLS_DC)
+{
+ switch (kind) {
+ case PDO_DBH_DRIVER_METHOD_KIND_DBH:
+ return dbh_methods;
+ default:
+ return NULL;
+ }
+}
+
static struct pdo_dbh_methods mysql_methods = {
mysql_handle_closer,
mysql_handle_preparer,
@@ -420,7 +510,8 @@
pdo_mysql_last_insert_id,
pdo_mysql_fetch_error_func,
pdo_mysql_get_attribute,
- pdo_mysql_check_liveness
+ pdo_mysql_check_liveness,
+ pdo_mysql_get_driver_methods /* get_driver_methods */
};
#ifndef PDO_MYSQL_UNIX_ADDR