php pdo mysql 超时_为 PDO 增加读写超时

本文介绍了如何在PHP PDO MySQL中自定义枚举类型,为PDO连接增加读写超时设置。通过在`php_pdo_mysql_int.h`中定义新的枚举常量,并在`pdo_mysql.c`中注册这些常量,然后在PHP代码中通过`pdo_attr_lval`获取超时值,判断调用环境(CLI或WEB)并使用`mysql_options`设置超时选项。
摘要由CSDN通过智能技术生成

定义枚举类型

首先,我们先要创建枚举类型,也就是未来在 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 即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值