🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 捕获输出 除非你开发的是非常简单的控制台应用, 否则你应该不希望php脚本代码产生的输出 直接被扔到激活的终端上. 捕获这些输出和你刚才用以覆写启动处理器的方法类似. 在sapi_module_struct中还有⼀些有用的回调: ````c typedef struct _sapi_module_struct { ... int (*ub_write)(const char *str, unsigned int str_length TSRMLS_DC); void (*flush)(void *server_context); void (*sapi_error)(int type, const char *error_msg, ...); void (*log_message)(char *message); ... } sapi_module_struct; ```` #### 标准输出: ub_write 所有用户空间的echo和print语句产生的输出, 以及其他内部通过php_printf()或 PHPWRITE()产生的输出, 最终都将被发送到激活的SAPI的ub_write()方法. 默认情况, 嵌入式SAPI直接将这些数据交给stdout管道, 而不关心你的应用的输出策略. 假设你的应用想要把所有的输出都发送到⼀个独立的控制台窗口; 你可能需要实现⼀个类似于下面伪代码块所描述的回调: ````c static int embed4_ub_write(const char *str, unsigned int str_length TSRMLS_DC) { output_string_to_window(CONSOLE_WINDOW_ID, str, str_length); return str_length; } ```` 要让这个函数能够处理php产生的内容, 你需要在调用php_embed_init()之前对 php_embed_module结构做适当的修改: ````c php_embed_module.ub_write = embed4_ub_write; ```` 注意: 哪怕你决定你的应用不需要php产生的输出, 也必须为ub_write设置⼀个回调. 将它的值设置为NULL将导致引擎崩溃, 当然, 你的应用也不能幸免. #### 缓冲输出: Flush 你的应用可能会使用缓冲php产生的输出进行优化, sapi层提供了⼀个回调用以通知 你的应用"现在请发送你的缓冲区数据", 你的应用并没有义务去实施这个通知; 不过, 由于 这个信息通常是由于足够的理由(比如到达请求结束位置)才产生的, 听从这个意见并不会有什么坏处. 下面的这对回调函数, 以256字节缓冲区缓冲数据由引擎安排执行flush. ````c char buffer[256]; int buffer_pos = 0; static int embed4_ubwrite(const char *str, unsigned int str_length TSRMLS_DC) { char *s = str; char *d = buffer + buffer_pos; int consumed = 0; /* 缓冲区够用, 直接追加到缓冲区后面 */ if (str_length < (256 - buffer_pos)) { memcpy(d, s, str_length); buffer_pos += str_length; return str_length; } consumed = 256 - buffer_pos; memcpy(d, s, consumed); embed4_output_chunk(buffer, 256); str_length -= consumed; s += consumed; /* 消耗整个传入的块 */ while (str_length >= 256) { embed4_output_chunk(s, 256); s += 256; consumed += 256; } /* 重置缓冲区头指针内容 */ memcpy(buffer, s, str_length); buffer_pos = str_length; consumed += str_length; return consumed; } static void embed4_flush(void *server_context) { if (buffer_pos < 0) { /* 输出缓冲区中剩下的内容 */ embed4_output_chunk(buffer, buffer_pos); buffer_pos = 0; } } ```` 在startup_php()中增加下面的代码, 这个基础的缓冲机制就就绪了: ````c php_embed_module.ub_write = embed4_ub_write; php_embed_module.flush = embed4_flush; ```` #### 标准错误: log_message 在启用了log_errors INI设置时, 在启动或执行脚本时如果碰到错误, 将激活 log_message回调. 默认的php错误处理程序会在处理显示(这里是调用log_message回调)之前, 格式化这些错误消息, 使其称为整齐的, 人类可读的内容. 关于log_message回调, 这里你需要注意的第⼀件事是它并不包含长度参数, 因此它并不是二进制安全的. 也就是说, 它只是按照NULL终止来处理字符串末尾. 使用它来做错误报告通常不会有什么问题, 实际上, 它可以用于在错误消息的呈现上 做更多的事情. 默认情况下, sapi/embed将会通过这个简单的内建回调, 发送这些错误消息到标准错误管道: ````c static void php_embed_log_message(char *message) { fprintf (stderr, "%s\n", message); } ```` 如果你想发送这些消息到日志文件, 则可以使用下面的版本替代: ````c static void embed4_log_message(char *message) { FILE *log; log = fopen("/var/log/embed4.log", "a"); fprintf (log, "%s\n", message); fclose(log); } ```` #### 特殊错误: sapi_error 少数特殊情况的错误属于某个sapi, 因此将绕过php的主错误处理程序. 这些错误一般 是由于使用不当造成的, 比如非web应用不应该使用header()函数, 上传文件到控制台应用程序等. 由于这些情况都离你所开发的sapi/embed应用非常遥远, 因此最好保持这个回调为空. 不过, 如果你非要坚持去捕获每种类型错误的源, 也只需要实现⼀个回调函数, 并在调 用php_embed_init()之前覆写它就可以了. ## links * [目录](<preface.md>) * 20.4 [覆写INI_SYSTEM和INI_PERDIR选项](<20.4.md>) * 20.6 [同时扩展和嵌入](<20.6.md>)