Capturing Output
Unless you're developing an incredibly simple console application, you probably don't want output generated by PHP script code to simply spill out onto the active terminal. Catching this output can be performed in a similar manner to the technique you just used to override the startup handler.
Hiding out in the sapi_module_struct are a few more useful callbacks:
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;
Standard Out: ub_write
Any output produced by userspace echo and print statements, as well as any other internally generated output issued via php_printf() or PHPWRITE(), ultimately winds up being sent to the active SAPI's ub_write() method. By default, the embed SAPI shuttles this data directly to the stdout pipe with no regard for your application's output strategy.
Imagine for a moment that your application wants all PHP output sent to a separate console window; you might implement a callback similar to the following hypothetical block of code:
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; }
To make this method the output handler for PHP-generated content, you'll need to make the appropriate modification to the php_embed_module struct just prior to calling php_embed_init():
php_embed_module.ub_write = embed4_ub_write;
Note
Even if you decide your application has no need for PHP-generated output, you must set ub_write to a valid callback. Setting it to a value of NULL will crash the engine and take your application with it.
Buffering Output: Flush
Because it might be optimal for your application to buffer output generated by PHP, the SAPI layer provides a callback to inform your application "It's important for you to send your buffered data NOW!"Your application isn't obligated to heed this advice; however, because this signal is usually generated for a very good reason (such as the end of a request), it probably wouldn't hurt to listen.
The following pair of callback buffers output in 256 byte increments, optionally flushing when ordered to by the engine:
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; /* Finish prior block */ if (str_length < (256 - buffer_pos)) { /* Add to buffer and exit */ 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; /* Consume whole passed blocks */ while (str_length >= 256) { embed4_output_chunk(s, 256); s += 256; consumed += 256; } /* Buffer remaining partial */ 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) { /* Output an unfinished block */ embed4_output_chunk(buffer, buffer_pos); buffer_pos = 0; } }
Add the appropriate lines to startup_php() and this rudimentary buffering mechanism is ready to go:
php_embed_module.ub_write = embed4_ub_write; php_embed_module.flush = embed4_flush;
Standard Error: log_message
The log_message callback is activated by the default PHP error handler when an error has occurred during startup or script execution and the log_errors INI setting has been enabled. The default PHP error handler takes care of formatting these error messages into tidy, human readable content before handing if off to the display, or in this case, the log_message callback.
The first thing you'll notice about the log_message callback is that it does not contain a length parameter and is thus not binary safe. That is, it will only ever contain a single NULL character, located at the end of the string.
For error reporting uses this is almost never a problem; in fact, it's helpful as more assumptions can be made about what can be done with the error message. By default, sapi/embed will send such error messages to the standard error pipe via this simple builtin callback:
static void php_embed_log_message(char *message) { fprintf (stderr, "%s ", message); }
If you'd rather send these messages to a logfile, you might replace this version with something like the following:
static void embed4_log_message(char *message) { FILE *log; log = fopen("/var/log/embed4.log", "a"); fprintf (log, "%s ", message); fclose(log); }
Special Errors: sapi_error
A few case-specific errors belong solely to the SAPI and bypass the main PHP error handler. These errors generally revolve around inappropriate use of the header() functionsomething your nonweb-based application shouldn't have to worry aboutand poorly formatted HTTP file uploadseven less of an issue for a console application.
Because these cases are so far removed from what you'll likely be doing with sapi/embed, it will probably be best to leave this callback alone. However, if you insist on catching each type of error at its source, just implement the callback proto already provided, and override it prior to calling php_embed_init().
Extending and Embedding at Once
|