PHP插件获取网页返回的html源码
字数 956 2025-08-26 22:12:02
PHP插件获取网页返回的HTML源码 - 详细教学文档
1. 背景与目标
本教学文档详细讲解如何通过PHP扩展插件来获取网页返回的HTML源码。这种方法不受ob_start()等输出缓冲函数的影响,能够直接捕获PHP脚本的所有输出内容。
2. 技术原理
2.1 PHP输出缓冲机制分析
PHP的输出缓冲机制主要通过output_globals全局变量和一系列输出处理函数实现:
ob_start()函数在main/output.c和main/php_output.h中定义- 所有输出相关的函数最终都会调用
php_output_op函数 - PHP通过判断
OG(active)是否为NULL来决定是否进入缓冲区
2.2 关键数据结构
typedef struct _php_output_buffer {
char *data; // 缓冲区数据
size_t size; // 缓冲区大小
size_t used; // 已使用大小
uint free:1; // 是否释放标志
uint _reserved:31; // 保留位
} php_output_buffer;
3. 插件开发步骤
3.1 创建PHP扩展
在PHP源码的ext目录下执行:
./ext_skel --extname=myext
3.2 定义全局变量
在php_hook_output_ext.h中定义全局变量:
ZEND_BEGIN_MODULE_GLOBALS(myext)
char *data; // 缓存区
size_t size; // 缓存区大小
size_t used; // 数据长度
ZEND_END_MODULE_GLOBALS(myext)
3.3 初始化与析构函数
static void php_myext_globals_ctor(zend_myext_globals *G TSRMLS_DC)
{
G->data = NULL;
G->size = 0;
G->used = 0;
}
static void php_myext_globals_dtor(zend_myext_globals *G TSRMLS_DC)
{
efree(G->data);
}
3.4 Hook输出操作码
以ZEND_ECHO操作码为例:
static int hookecho(ZEND_OPCODE_HANDLER_ARGS)
{
zend_op *opline = execute_data->opline;
zval *z = EX_CONSTANT(opline->op1);
if (Z_TYPE_P(z) == IS_STRING) {
zend_string *str = Z_STR_P(z);
if (ZSTR_LEN(str) != 0) {
get_data(ZSTR_VAL(str), ZSTR_LEN(str));
}
} else {
zend_string *str = _zval_get_string_func(z);
if (ZSTR_LEN(str) != 0) {
get_data(ZSTR_VAL(str), ZSTR_LEN(str));
}
zend_string_release(str);
}
return ZEND_USER_OPCODE_DISPATCH;
}
3.5 数据缓冲区处理
static int get_data(char *str, size_t str_len)
{
if(str_len){
// 空间不足时扩展缓冲区
if ((MYEXT_G(size) - MYEXT_G(used)) <= str_len){
size_t grow_int = PHP_OUTPUT_HANDLER_INITBUF_SIZE(MYEXT_G(size));
size_t grow_buf = PHP_OUTPUT_HANDLER_INITBUF_SIZE(str_len - (MYEXT_G(size) - MYEXT_G(used)));
size_t grow_max = MAX(grow_int, grow_buf);
MYEXT_G(data) = erealloc(MYEXT_G(data), MYEXT_G(size) + grow_max);
MYEXT_G(size) += grow_max;
}
memcpy(MYEXT_G(data) + MYEXT_G(used), str, str_len);
MYEXT_G(used) += str_len;
}
return 1;
}
3.6 模块生命周期管理
PHP_MINIT_FUNCTION(myext)
{
#ifdef ZTS
ts_allocate_id(&myext_globals_id,
sizeof(zend_myext_globals),
(ts_allocate_ctor)php_myext_globals_ctor,
(ts_allocate_dtor)php_myext_globals_dtor);
#else
php_myext_globals_ctor(&myext_globals TSRMLS_CC);
#endif
zend_set_user_opcode_handler(ZEND_ECHO, hookecho);
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(myext)
{
#ifndef ZTS
php_myext_globals_dtor(&myext_globals TSRMLS_CC);
#endif
FILE *fp;
fp = fopen("/web/php/log","a");
fwrite(MYEXT_G(data), MYEXT_G(used), 1, fp);
fwrite("\n------------------\n", 21, 1, fp);
fclose(fp);
return SUCCESS;
}
4. 完整代码实现
4.1 头文件 (php_myext.h)
#ifndef PHP_MYEXT_H
#define PHP_MYEXT_H
extern zend_module_entry myext_module_entry;
#define phpext_myext_ptr &myext_module_entry
#define PHP_MYEXT_VERSION "0.1.0"
// ... 其他标准头文件内容 ...
#define MYEXT_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(myext,v)
ZEND_BEGIN_MODULE_GLOBALS(myext)
char *data;
size_t size;
size_t used;
ZEND_END_MODULE_GLOBALS(myext)
#define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data
PHP_FUNCTION(confirm_myext_compiled);
static int hookecho(ZEND_OPCODE_HANDLER_ARGS);
static int get_data(char *str, size_t str_len);
static void init_myext_global();
#endif
4.2 主实现文件 (myext.c)
// ... 标准头文件包含 ...
ZEND_DECLARE_MODULE_GLOBALS(myext);
// ... 其他函数实现 ...
const zend_function_entry myext_functions[] = {
PHP_FE(confirm_myext_compiled, NULL)
PHP_FE_END
};
zend_module_entry myext_module_entry = {
STANDARD_MODULE_HEADER,
"myext",
myext_functions,
PHP_MINIT(myext),
PHP_MSHUTDOWN(myext),
PHP_RINIT(myext),
PHP_RSHUTDOWN(myext),
PHP_MINFO(myext),
PHP_MYEXT_VERSION,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_MYEXT
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
#endif
ZEND_GET_MODULE(myext)
#endif
5. 编译与使用
- 将扩展代码放入PHP源码的ext目录
- 运行
phpize生成配置 - 执行
./configure和make编译扩展 - 将生成的.so文件添加到php.ini中
6. 结果验证
插件会将捕获的所有输出内容保存到/web/php/log文件中,每条记录以分隔线------------------分隔。
7. 注意事项
- 使用Apache或Nginx时,需要确保日志文件路径在web根目录下,或修改相关配置
- 在多线程环境(ZTS)下需要特别注意线程安全
- 缓冲区大小需要根据实际输出内容调整
8. 扩展思路
- 可以Hook更多输出相关的操作码,如
print、printf等 - 可以添加过滤功能,只捕获特定格式的输出
- 可以实现网络传输功能,将捕获的内容发送到远程服务器
9. 参考资源
- PHP源码中的
main/output.c和main/php_output.h - PHP操作码Hook技术
- PHP扩展开发官方文档