捕获输出
除非你开发的是非常简单的控制台应用, 否则你应该不希望php脚本代码产生的输出 直接被扔到激活的终端上. 捕获这些输出和你刚才用以覆写启动处理器的方法类似.
在sapi_module_struct中还有⼀些有用的回调:
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管道, 而不关心你的应用的输出策略.
假设你的应用想要把所有的输出都发送到⼀个独立的控制台窗口; 你可能需要实现⼀个类似于下面伪代码块所描述的回调:
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结构做适当的修改:
php_embed_module.ub_write = embed4_ub_write;
注意: 哪怕你决定你的应用不需要php产生的输出, 也必须为ub_write设置⼀个回调. 将它的值设置为NULL将导致引擎崩溃, 当然, 你的应用也不能幸免.
缓冲输出: Flush
你的应用可能会使用缓冲php产生的输出进行优化, sapi层提供了⼀个回调用以通知 你的应用”现在请发送你的缓冲区数据”, 你的应用并没有义务去实施这个通知; 不过, 由于 这个信息通常是由于足够的理由(比如到达请求结束位置)才产生的, 听从这个意见并不会有什么坏处.
下面的这对回调函数, 以256字节缓冲区缓冲数据由引擎安排执行flush.
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()中增加下面的代码, 这个基础的缓冲机制就就绪了:
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将会通过这个简单的内建回调, 发送这些错误消息到标准错误管道:
static void php_embed_log_message(char *message)
{
fprintf (stderr, "%s\n", message);
}
如果你想发送这些消息到日志文件, 则可以使用下面的版本替代:
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()之前覆写它就可以了.