PHP源码之 ext/mysql扩展部分

前端之家收集整理的这篇文章主要介绍了PHP源码之 ext/mysql扩展部分前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

我写过一个外部模块扩展,现在开始看PHP源码中的MysqL扩展,它是可以被集成到PHP内部的,所以应该算是内置的扩展了。
该扩展需要用到MysqL数据库提供的一些接口,所以需要安装了MysqL,并能够确定MysqL.h的位置。
该扩展的位置一般在 PHP-source-code/ext/MysqL 下。
在linux下,主要需要注意的文件是: config.m4,PHP_MysqL.c,PHP_MysqL_structs.h。
ps:该目录下有tags文件,所以可以利用ctags的各种特性,直接找到函数、宏定义等。
ps:linux下MysqL的启动 sudo MysqL-dir/bin/MysqLd_safe &
之后会有两个进程运行:

代码如下:
root 5297 0.0 0.0 5920 1416 pts/5 S 11:08 0:00 /bin/sh /usr/local/MysqL/bin/MysqLd_safe
MysqL 5320 1.4 1.1 202728 23796 pts/5 Sl 11:08 1:47 /usr/local/MysqL/libexec/MysqLd --basedir=/usr/local/MysqL --datadir=/usr/local/MysqL/var --user=MysqL --pid-file=/usr/local/MysqL/var/tj1clnxweb0004.pid --skip-external-locking --port=3306 --socket=/tmp/MysqL.sock

----------------------------------------------
以下先记录阅读过程中的一些细节问题:
1、PHP_MysqL_do_query_general函数
该扩展提供的函数MysqL_query和MysqL_unbuffered_query最后都要用到PHP_MysqL_do_query_general来执行核心功能
首先看下trace模式:
if (MySG(trace_mode)) { .... }
PHP.ini中有配置:
MysqL.trace_mode = Off
而如果配置是打开的,那么就会执行if中的句子,而如果执行的句子是select的话,就会在前面加上explain,分析sql句子的性能
然后看一下MysqL_use_result和MysqL_store_result的区别:
可以看到,MysqL_query使用的是MysqL_store_result函数,而MysqL_unbuffered_query是用的是MysqL_use_result。
参考文章(http://school.cnd8.com/MysqL/jiaocheng/25143_8.htm),并总结如下:
MysqL_store_result 查询获取所有的结果集,保存在客户端,准备供客户端使用,这样对于客户端的内存和性能要求较大。
MysqL_use_result 仅查询,而将结果获取延迟。相当于是在服务前端维护了一个结果集。
调用MysqL_store_result ,使用MysqL_fetch_row获取结果时,是直接从客户端获取结果,如果返回为NULL,就是没有结果了。
而当调用MysqL_use_result,使用MysqL_fetch_row获取结果时,是从服务前端获取结果,如果返回为NULL,那么可能是没用结果了,也可能是网络连接出错等原因。
由于结果集的维护地方不同,MysqL_store_result 的结果可以提供更多的处理函数,比如任意的seek、获取总数、非顺序访问等。而MysqL_use_result的结果集就不可以。
另外,由于MysqL_use_result的结果集是维持在服务器端,那么它就提出一个要求:客户端对结果集中的每一行都必须调用MysqL_fetch_row,否则,结果集中剩余的记录就会成为下一个查询结果集中的一部分,并且发生“不同步”的错误
那么,为什么还要用到MysqL_use_result呢?看下这个情况:
MysqLMysqLdump 缺省时,使用MysqL_store_result,但是如果指定--quick 选项,则使用MysqL_use_result。
那说明MysqL_use_result在效率方面占有优势?
看下MysqL的帮助手册:
-q,--quick Don't cache result,print it row by row. This may slow
down the server if the output is suspended. Doesn't use
history file.
MysqLdump的帮助手册:
-q,--quick Don't buffer query,dump directly to stdout.
那么在我没有彻底弄明白为什么quick对应着MysqL_use_result的时候,先搞明白什么时候不要用MysqL_use_result吧。由于MysqL_use_result的结果集维护在服务器端,那么如果客户端程序可能被挂起,别用它。如果结果集的行与行之间有过多操作,别用它。也就是一句话,如果查询完,不是立马用完结果,free掉,那么就别用MysqL_use_result。
为了尝试一下效果,写了以下测试代码
代码如下:
$sql = sprintf("select * from pet;");
$result = MysqL_unbuffered_query($sql,$conn);
$rows = MysqL_fetch_row($result);
var_dump($rows);
$sql = sprintf("select * from shop");
$result = MysqL_unbuffered_query($sql,$conn);
$rows = MysqL_fetch_row($result);
var_dump($rows);

执行的结果是,第二次fetch不会显示第一次的结果,但是PHP会报notice:
PHP Notice: MysqL_unbuffered_query(): Function called without first fetching all rows from a prevIoUs unbuffered query in /home/yicheng/test-all/MysqLtest/test.PHP on line 28
修改测试代码
代码如下:
$i = 1000000;
while($i--){
$sql = sprintf("select * from pet;");
$result = MysqL_query($sql,$conn);
#$result = MysqL_unbuffered_query($sql,$conn);
while($rows = MysqL_fetch_row($result)){ }
if ($result){
MysqL_free_result($result);
}
}

使用unbuffered的结果:
:!time ./test.PHP
real 1m10.220s
user 0m17.853s
sys 0m9.541s
使用MysqL_query的结果:
:!time ./test.PHP
real 1m11.191s
user 0m19.297s
sys 0m10.133s
貌似时间差别也不大嘛
2、一些资源相关的宏定义
代码如下:
#define ZEND_VERIFY_RESOURCE(rsrc) \
if (!rsrc) { \
RETURN_FALSE; \
}
#define ZEND_FETCH_RESOURCE(rsrc,rsrc_type,passed_id,default_id,resource_type_name,resource_type) \
rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC,NULL,1,resource_type); \
ZEND_VERIFY_RESOURCE(rsrc);
#define ZEND_FETCH_RESOURCE2(rsrc,resource_type1,resource_type2) \
rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC,2,resource_type2); \
ZEND_VERIFY_RESOURCE(rsrc);
#define ZEND_REGISTER_RESOURCE(rsrc_result,rsrc_pointer,rsrc_type) \
zend_register_resource(rsrc_result,rsrc_type);
#define ZEND_GET_RESOURCE_TYPE_ID(le_id,le_type_name) \
if (le_id == 0) { \
le_id = zend_fetch_list_dtor_id(le_type_name); \
}

我们由MysqL_connect函数返回的其实是一个link id(resource(4) of type (MysqL link)),通过ZEND_FETCH_RESOURCE和ZEND_FETCH_RESOURCE2宏,可以映射到对应的MysqL资源上去。这两个宏都调用了zend_fetch_resource方法,所以下面我们看下这个方法
ZEND_API void *zend_fetch_resource(zval **passed_id TSRMLS_DC,int default_id,char *resource_type_name,int *found_resource_type,int num_resource_types,...)
比如在MysqL_list_dbs函数调用ZEND_FETCH_RESOURCE2宏:
ZEND_FETCH_RESOURCE2(MysqL,PHP_MysqL_conn *,MysqL_link,id,"MysqL-Link",le_link,le_plink);
其中MysqL保存了返回的有效资源,PHP_MysqL_conn *定义了返回资源的类型,MysqL_link,id分别对应着passed_id 和 default_id(因为很多函数调用,不传入具体的conn,就是使用default值),"MysqL-Link"是resource_type_name,le_link,le_plink是zend_fetch_resource的...部分,它们俩是static int类型的值。
由zend_fetch_resource可以看出,resource(4) of type (MysqL link)的value.lval中包含了long型的id。如果default_id为-1,那么就是用passed_id传入的id,否则就使用default_id作为id,利用zend_list_find来寻找其对应的资源。
看了几个函数之后,其实该扩展也就是对MysqL提供的c接口的封装而已。但是封装的很规范也很稳定!
如果想了解进一步,那还是得看MysqL的源代码。下面贴了几个重要的数据结构。
-----------------------------------------
一些对于查错可能有用的PHP函数
代码如下:
error_reporting(E_ALL);
#var_dump(MysqL_get_host_info($conn));
#var_dump(MysqL_get_proto_info($conn));
#var_dump(MysqL_get_server_info($conn));
#var_dump(MysqL_stat($conn));
#var_dump(MysqL_errno($conn));
#var_dump(MysqL_error($conn));
#var_dump(MysqL_info($conn));

--------------------------------------------
MysqL源码中一些有用的struct
代码如下:
typedef struct st_MysqL
{
NET net; /* Communication parameters */
gptr connector_fd; /* ConnectorFd for SSL */
char *host,*user,*passwd,*unix_socket,*server_version,*host_info,*info;
char *db;
struct charset_info_st *charset;
MysqL_FIELD *fields;
MEM_ROOT field_alloc;
my_ulonglong affected_rows;
my_ulonglong insert_id; /* id if insert on table with NEXTNR */
my_ulonglong extra_info; /* Not used */
unsigned long thread_id; /* Id for connection in server */
unsigned long packet_length;
unsigned int port;
unsigned long client_flag,server_capabilities;
unsigned int protocol_version;
unsigned int field_count;
unsigned int server_status;
unsigned int server_language;
unsigned int warning_count;
struct st_MysqL_options options;
enum MysqL_status status;
my_bool free_me; /* If free in MysqL_close */
my_bool reconnect; /* set to 1 if automatic reconnect */
/* session-wide random string */
char scramble[SCRAMBLE_LENGTH+1];
/*
Set if this is the original connection,not a master or a slave we have
added though MysqL_rpl_probe() or MysqL_set_master()/ MysqL_add_slave()
*/
my_bool rpl_pivot;
/*
Pointers to the master,and the next slave connections,points to
itself if lone connection.
*/
struct st_MysqL* master,*next_slave;
struct st_MysqL* last_used_slave; /* needed for round-robin slave pick */
/* needed for send/read/store/use result to work correctly with replication */
struct st_MysqL* last_used_con;
LIST *stmts; /* list of all statements */
const struct st_MysqL_methods *methods;
void *thd;
/*
Points to boolean flag in MysqL_RES or MysqL_STMT. We set this flag
from MysqL_stmt_close if close had to cancel result set of this object.
*/
my_bool *unbuffered_fetch_owner;
#if defined(EMBEDDED_LIBRARY) || defined(EMBEDDED_LIBRARY_COMPATIBLE) || MysqL_VERSION_ID >= 50100
/* needed for embedded server - no net buffer to store the 'info' */
char *info_buffer;
#endif
} MysqL;
typedef struct st_MysqL_methods
{
my_bool (*read_query_result)(MysqL *MysqL);
my_bool (*advanced_command)(MysqL *MysqL,
enum enum_server_command command,
const char *header,
unsigned long header_length,
const char *arg,
unsigned long arg_length,
my_bool skip_check,
MysqL_STMT *stmt);
MysqL_DATA *(*read_rows)(MysqL *MysqL,MysqL_FIELD *MysqL_fields,
unsigned int fields);
MysqL_RES * (*use_result)(MysqL *MysqL);
void (*fetch_lengths)(unsigned long *to,
MysqL_ROW column,unsigned int field_count);
void (*flush_use_result)(MysqL *MysqL);
#if !defined(MysqL_SERVER) || defined(EMBEDDED_LIBRARY)
MysqL_FIELD * (*list_fields)(MysqL *MysqL);
my_bool (*read_prepare_result)(MysqL *MysqL,MysqL_STMT *stmt);
int (*stmt_execute)(MysqL_STMT *stmt);
int (*read_binary_rows)(MysqL_STMT *stmt);
int (*unbuffered_fetch)(MysqL *MysqL,char **row);
void (*free_embedded_thd)(MysqL *MysqL);
const char *(*read_statistics)(MysqL *MysqL);
my_bool (*next_result)(MysqL *MysqL);
int (*read_change_user_result)(MysqL *MysqL,char *buff,const char *passwd);
int (*read_rows_from_cursor)(MysqL_STMT *stmt);
#endif
} MysqL_METHODS;

原文链接:https://www.f2er.com/php/29125.html

猜你在找的PHP相关文章