定义枚举类型
首先,我们先要创建枚举类型,也就是未来在 PHP 代码中,setAttribute 传入的 KEY
原生的这个 key 值都定义在 Zend/ext/pdo/php_pdo_driver.h 中:
enum pdo_attribute_type {
PDO_ATTR_AUTOCOMMIT,/* use to turn on or off auto-commit mode */
PDO_ATTR_PREFETCH,/* configure the prefetch size for drivers that support it. Size is in KB */
PDO_ATTR_TIMEOUT,/* connection timeout in seconds */
PDO_ATTR_ERRMODE,/* control how errors are handled */
PDO_ATTR_SERVER_VERSION,/* database server version */
PDO_ATTR_CLIENT_VERSION,/* client library version */
PDO_ATTR_SERVER_INFO,/* server information */
PDO_ATTR_CONNECTION_STATUS,/* connection status */
PDO_ATTR_CASE,/* control case folding for portability */
PDO_ATTR_CURSOR_NAME,/* name a cursor for use in "WHERE CURRENT OF " */
PDO_ATTR_CURSOR,/* cursor type */
PDO_ATTR_ORACLE_NULLS,/* convert empty strings to NULL */
PDO_ATTR_PERSISTENT,/* pconnect style connection */
PDO_ATTR_STATEMENT_CLASS,/* array(classname, array(ctor_args)) to specify the class of the constructed statement */
PDO_ATTR_FETCH_TABLE_NAMES, /* include table names in the column names, where available */
PDO_ATTR_FETCH_CATALOG_NAMES, /* include the catalog/db name names in the column names, where available */
PDO_ATTR_DRIVER_NAME, /* name of the driver (as used in the constructor) */
PDO_ATTR_STRINGIFY_FETCHES,/* converts integer/float types to strings during fetch */
PDO_ATTR_MAX_COLUMN_LEN,/* make database calculate maximum length of data found in a column */
PDO_ATTR_DEFAULT_FETCH_MODE, /* Set the default fetch mode */
PDO_ATTR_EMULATE_PREPARES, /* use query emulation rather than native */
/* this defines the start of the range for driver specific options.
* Drivers should define their own attribute constants beginning with this
* value. */
PDO_ATTR_DRIVER_SPECIFIC = 1000
};
注意最后的注释:
/* this defines the start of the range for driver specific options.
* Drivers should define their own attribute constants beginning with this
* value. */
我们不能去修改这个文件,而是要以 PDO_ATTR_DRIVER_SPECIFIC 为起始构造自己的枚举类型,0 到 PDO_ATTR_DRIVER_SPECIFIC 之间的值是 PHP zend 内核的保留区段
我们回到 ext/pdo_mysql 目录中,查找一下 PDO_ATTR_DRIVER_SPECIFIC,可以找到,在 php_pdo_mysql_int.h 中找到:
enum {
PDO_MYSQL_ATTR_USE_BUFFERED_QUERY = PDO_ATTR_DRIVER_SPECIFIC,
PDO_MYSQL_ATTR_LOCAL_INFILE,
PDO_MYSQL_ATTR_INIT_COMMAND,
#ifndef PDO_USE_MYSQLND
PDO_MYSQL_ATTR_READ_DEFAULT_FILE,
PDO_MYSQL_ATTR_READ_DEFAULT_GROUP,
PDO_MYSQL_ATTR_MAX_BUFFER_SIZE,
#endif
PDO_MYSQL_ATTR_COMPRESS,
PDO_MYSQL_ATTR_DIRECT_QUERY,
PDO_MYSQL_ATTR_FOUND_ROWS,
PDO_MYSQL_ATTR_IGNORE_SPACE,
PDO_MYSQL_ATTR_SSL_KEY,
PDO_MYSQL_ATTR_SSL_CERT,
PDO_MYSQL_ATTR_SSL_CA,
PDO_MYSQL_ATTR_SSL_CAPATH,
PDO_MYSQL_ATTR_SSL_CIPHER,
#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY
#endif
};
这里就是 PDO 自己设置的 PDO_ATTR_DRIVER_SPECIFIC 之后的枚举数据我们只需要在这里定义我们需要的枚举类型即可:
enum {
PDO_MYSQL_ATTR_USE_BUFFERED_QUERY = PDO_ATTR_DRIVER_SPECIFIC,
PDO_MYSQL_ATTR_LOCAL_INFILE,
PDO_MYSQL_ATTR_INIT_COMMAND,
#ifndef PDO_USE_MYSQLND
PDO_MYSQL_ATTR_READ_DEFAULT_FILE,
PDO_MYSQL_ATTR_READ_DEFAULT_GROUP,
PDO_MYSQL_ATTR_MAX_BUFFER_SIZE,
#endif
PDO_MYSQL_ATTR_COMPRESS,
PDO_MYSQL_ATTR_DIRECT_QUERY,
PDO_MYSQL_ATTR_FOUND_ROWS,
PDO_MYSQL_ATTR_IGNORE_SPACE,
PDO_MYSQL_ATTR_SSL_KEY,
PDO_MYSQL_ATTR_SSL_CERT,
PDO_MYSQL_ATTR_SSL_CA,
PDO_MYSQL_ATTR_SSL_CAPATH,
PDO_MYSQL_ATTR_SSL_CIPHER,
PDO_MYSQL_RW_ENV_ALL,
PDO_MYSQL_RW_ENV_CLI,
PDO_MYSQL_RW_ENV_WEB,
PDO_MYSQL_RW_ENV_NONE,
PDO_MYSQL_ATTR_RW_TIMEOUT_ENV,
PDO_MYSQL_ATTR_WRITE_TIMEOUT,
PDO_MYSQL_ATTR_READ_TIMEOUT,
#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY
#endif
};
这里我们添加了 7 个枚举类型:PDO_MYSQL_ATTR_READ_TIMEOUT -- 读超时
PDO_MYSQL_ATTR_WRITE_TIMEOUT -- 写超时
PDO_MYSQL_ATTR_RW_TIMEOUT_ENV -- 环境
PDO_MYSQL_ATTR_RW_TIMEOUT_ENV 这个 key 我们用来设定我们的设置在哪些环境下生效:PDO_MYSQL_RW_ENV_ALL -- 全部环境中都生效
PDO_MYSQL_RW_ENV_CLI -- 只在 cli 调用时生效
PDO_MYSQL_RW_ENV_WEB -- 只在 web 调用时生效
PDO_MYSQL_RW_ENV_NONE -- 不生效
注册枚举类型
定义枚举类型以后,我们需要向 PHP 内核注册我们用到的 key 的具体字符串值及对应的枚举类型值
在 pdo_mysql.c 中的模块初始化函数中注册我们的枚举类型及 key 值:
/* true global environment */
/* {{{ PHP_MINIT_FUNCTION
*/
static PHP_MINIT_FUNCTION(pdo_mysql)
{
REGISTER_INI_ENTRIES();
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_USE_BUFFERED_QUERY", (long)PDO_MYSQL_ATTR_USE_BUFFERED_QUERY);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_LOCAL_INFILE", (long)PDO_MYSQL_ATTR_LOCAL_INFILE);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_INIT_COMMAND", (long)PDO_MYSQL_ATTR_INIT_COMMAND);
#ifndef PDO_USE_MYSQLND
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_MAX_BUFFER_SIZE", (long)PDO_MYSQL_ATTR_MAX_BUFFER_SIZE);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_READ_DEFAULT_FILE", (long)PDO_MYSQL_ATTR_READ_DEFAULT_FILE);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_READ_DEFAULT_GROUP", (long)PDO_MYSQL_ATTR_READ_DEFAULT_GROUP);
#endif
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_COMPRESS", (long)PDO_MYSQL_ATTR_COMPRESS);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_DIRECT_QUERY", (long)PDO_MYSQL_ATTR_DIRECT_QUERY);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_FOUND_ROWS", (long)PDO_MYSQL_ATTR_FOUND_ROWS);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_IGNORE_SPACE", (long)PDO_MYSQL_ATTR_IGNORE_SPACE);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_KEY", (long)PDO_MYSQL_ATTR_SSL_KEY);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CERT", (long)PDO_MYSQL_ATTR_SSL_CERT);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CA", (long)PDO_MYSQL_ATTR_SSL_CA);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CAPATH", (long)PDO_MYSQL_ATTR_SSL_CAPATH);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SSL_CIPHER", (long)PDO_MYSQL_ATTR_SSL_CIPHER);
// 注册枚举类型及对应的 KEY 值
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_ALL", (long)PDO_MYSQL_RW_ENV_ALL);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_CLI", (long)PDO_MYSQL_RW_ENV_CLI);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_WEB", (long)PDO_MYSQL_RW_ENV_WEB);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_RW_ENV_NONE", (long)PDO_MYSQL_RW_ENV_NONE);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_RW_TIMEOUT_ENV", (long)PDO_MYSQL_ATTR_RW_TIMEOUT_ENV);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_WRITE_TIMEOUT", (long)PDO_MYSQL_ATTR_WRITE_TIMEOUT);
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_READ_TIMEOUT", (long)PDO_MYSQL_ATTR_READ_TIMEOUT);
#if MYSQL_VERSION_ID > 50605 || defined(PDO_USE_MYSQLND)
REGISTER_PDO_CLASS_CONST_LONG("MYSQL_ATTR_SERVER_PUBLIC_KEY", (long)PDO_MYSQL_ATTR_SERVER_PUBLIC_KEY);
#endif
#ifdef PDO_USE_MYSQLND
mysqlnd_reverse_api_register_api(&pdo_mysql_reverse_api TSRMLS_CC);
#endif
return php_pdo_register_driver(&pdo_mysql_driver);
}
获取传入的 value
接下来,我们需要获取调用 php 时传入的具体设置的超时时间值及限制的调用方式值,只有获取后才能实现设置
与连接超时的设置相同,我们使用 pdo 实现的 pdo_attr_lval 函数:
long write_timeout = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_WRITE_TIMEOUT, 60 TSRMLS_CC);
long read_timeout = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_READ_TIMEOUT, 60 TSRMLS_CC);
long read_write_timeout_env = pdo_attr_lval(driver_options, PDO_MYSQL_ATTR_RW_TIMEOUT_ENV, PDO_MYSQL_RW_ENV_CLI TSRMLS_CC);
判断调用方式
下面我们通过调用方式判断是否启用相应的设置
php 提供了 sapi_module.name 变量作为 cli 调用或 web 调用的标识:
int enable_read_write_timeout = 0;
//all env
if (read_write_timeout_env == PDO_MYSQL_RW_ENV_ALL) {
enable_read_write_timeout = 1;
}
// web
else if ( read_write_timeout_env == PDO_MYSQL_RW_ENV_WEB && strcmp(sapi_module.name, "cli") != 0 ) {
enable_read_write_timeout = 1;
}
// cli
else if ( read_write_timeout_env == PDO_MYSQL_RW_ENV_CLI && strcmp(sapi_module.name, "cli") == 0 ) {
enable_read_write_timeout = 1;
}
else if ( read_write_timeout_env == PDO_MYSQL_RW_ENV_NONE ) {
enable_read_write_timeout = 0;
}
设置超时时间
万事俱备,只差最后一步,设置超时时间,正如我们前面所说,这里我们使用 mysql_options 设置读写超时:
if (enable_read_write_timeout == 1) {
if (mysql_options(H->server, MYSQL_OPT_WRITE_TIMEOUT, (const char *)&write_timeout)) {
pdo_mysql_error(dbh);
goto cleanup;
}
if (mysql_options(H->server, MYSQL_OPT_READ_TIMEOUT, (const char *)&read_timeout)) {
pdo_mysql_error(dbh);
goto cleanup;
}
}
完成
重新编译 PHP 即可