搜尋
首頁後端開發php教程PHP源码之 ext/mysql扩展部分_PHP

我写过一个外部模块扩展,现在开始看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呢?看下这个情况:
mysql 和mysqldump 缺省时,使用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, default_id, resource_type_name, NULL, 1, resource_type); \
ZEND_VERIFY_RESOURCE(rsrc);
#define ZEND_FETCH_RESOURCE2(rsrc, rsrc_type, passed_id, default_id, resource_type_name, resource_type1, resource_type2) \
rsrc = (rsrc_type) zend_fetch_resource(passed_id TSRMLS_CC, default_id, resource_type_name, NULL, 2, resource_type1, resource_type2); \
ZEND_VERIFY_RESOURCE(rsrc);
#define ZEND_REGISTER_RESOURCE(rsrc_result, rsrc_pointer, rsrc_type) \
zend_register_resource(rsrc_result, rsrc_pointer, 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;

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
簡單地說明PHP會話的概念。簡單地說明PHP會話的概念。Apr 26, 2025 am 12:09 AM

phpsessionstrackuserdataacrossmultiplepagerequestsusingauniqueIdStoredInAcookie.here'showtomanageThemeffectionaly:1)startAsessionWithSessionWwithSession_start()和stordoredAtain $ _session.2)

您如何循環中存儲在PHP會話中的所有值?您如何循環中存儲在PHP會話中的所有值?Apr 26, 2025 am 12:06 AM

在PHP中,遍歷會話數據可以通過以下步驟實現:1.使用session_start()啟動會話。 2.通過foreach循環遍歷$_SESSION數組中的所有鍵值對。 3.處理複雜數據結構時,使用is_array()或is_object()函數,並用print_r()輸出詳細信息。 4.優化遍歷時,可採用分頁處理,避免一次性處理大量數據。這將幫助你在實際項目中更有效地管理和使用PHP會話數據。

說明如何使用會話進行用戶身份驗證。說明如何使用會話進行用戶身份驗證。Apr 26, 2025 am 12:04 AM

會話通過服務器端的狀態管理機制實現用戶認證。 1)會話創建並生成唯一ID,2)ID通過cookies傳遞,3)服務器存儲並通過ID訪問會話數據,4)實現用戶認證和狀態管理,提升應用安全性和用戶體驗。

舉一個如何在PHP會話中存儲用戶名的示例。舉一個如何在PHP會話中存儲用戶名的示例。Apr 26, 2025 am 12:03 AM

Tostoreauser'snameinaPHPsession,startthesessionwithsession_start(),thenassignthenameto$_SESSION['username'].1)Usesession_start()toinitializethesession.2)Assigntheuser'snameto$_SESSION['username'].Thisallowsyoutoaccessthenameacrossmultiplepages,enhanc

哪些常見問題會導致PHP會話失敗?哪些常見問題會導致PHP會話失敗?Apr 25, 2025 am 12:16 AM

PHPSession失效的原因包括配置錯誤、Cookie問題和Session過期。 1.配置錯誤:檢查並設置正確的session.save_path。 2.Cookie問題:確保Cookie設置正確。 3.Session過期:調整session.gc_maxlifetime值以延長會話時間。

您如何在PHP中調試與會話相關的問題?您如何在PHP中調試與會話相關的問題?Apr 25, 2025 am 12:12 AM

在PHP中調試會話問題的方法包括:1.檢查會話是否正確啟動;2.驗證會話ID的傳遞;3.檢查會話數據的存儲和讀取;4.查看服務器配置。通過輸出會話ID和數據、查看會話文件內容等方法,可以有效診斷和解決會話相關的問題。

如果session_start()被多次調用會發生什麼?如果session_start()被多次調用會發生什麼?Apr 25, 2025 am 12:06 AM

多次調用session_start()會導致警告信息和可能的數據覆蓋。 1)PHP會發出警告,提示session已啟動。 2)可能導致session數據意外覆蓋。 3)使用session_status()檢查session狀態,避免重複調用。

您如何在PHP中配置會話壽命?您如何在PHP中配置會話壽命?Apr 25, 2025 am 12:05 AM

在PHP中配置會話生命週期可以通過設置session.gc_maxlifetime和session.cookie_lifetime來實現。 1)session.gc_maxlifetime控制服務器端會話數據的存活時間,2)session.cookie_lifetime控制客戶端cookie的生命週期,設置為0時cookie在瀏覽器關閉時過期。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

Atom編輯器mac版下載

Atom編輯器mac版下載

最受歡迎的的開源編輯器

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中