So far, you've seen the PHP_EMBED_START_BLOCK() and PHP_EMBED_END_BLOCK() macros used to start up, execute, and shut down a PHP request in a nice tight, atomic package. The advantage to this is that any serious errors will result in PHP bailing out only as far as the PHP_EMBED_END_BLOCK() macro for its current scope. By keeping all your code executions to small blocks located between these macros, a PHP error should be completely unable to take down your entire application.
As you just learned, the major disadvantage to this nice little theory is that each time you establish a new START/END block, you effectively create a new request with a fresh symbol table and you lose any sense of persistency.
The means by which to get the best of both worldserror isolation and persistencyis to disassemble the START and END macros into their component pieces. Listing 20.2 shows the embed2.c program from the start of this chapter again, this time split into bite-sized pieces.
Listing 20.2. embed3.cManually Initializing and Shutting Down
#include int main(int argc, char *argv[]) { #ifdef ZTS void ***tsrm_ls; #endif php_embed_init(argc, argv PTSRMLS_CC); zend_first_try { zend_eval_string("echo 'Hello World!';", NULL, "Embed 2 Eval'd string" TSRMLS_CC); } zend_end_try(); php_embed_shutdown(TSRMLS_C); return 0; } |
The same code is being executed as before, only this time you can see the open and close braces that have locked you into being unable to separate the START and END blocks. By placing php_embed_init() at the start of your application and php_emebd_shutdown() at the end, you gain the persistency of a single request for the life of your application while being able to use the zend_first_try { } zend_end_try(); construct to catch any fatal errors that would otherwise cause your entire wrapper app to bail out to the PHP_EMBED_END_BLOCK() macro at the end of your app.
Note
Notice that this time, zend_first_try was used rather than zend_try. It's important to use zend_first_try in the outermost TRy/catch block because zend_first_try performs a few extra steps that must not be stacked within each other.
To see this approach used in a more "real-world" environment, abstract out the startup and shutdown process as in the following variation of the script execution program you wrote earlier this chapter (see Listing 20.3).
Listing 20.3. embed4.cAbstracting Startup and Shutdown
#include #ifdef ZTS void ***tsrm_ls; #endif static void startup_php(void) { /* Create "dummy" argc/argv to hide the arguments * meant for our actual application */ int argc = 1; char *argv[2] = { "embed4", NULL }; php_embed_init(argc, argv PTSRMLS_CC); } static void shutdown_php(void) { php_embed_shutdown(TSRMLS_C); } static void execute_php(char *filename) { zend_first_try { char *include_script; spprintf(&include_script, 0, "include '%s';", filename); zend_eval_string(include_script, NULL, filename TSRMLS_CC); efree(include_script); } zend_end_try(); } int main(int argc, char *argv[]) { if (argc <= 1) { printf("Usage: embed4 scriptfile"); return -1; } startup_php(); execute_php(argv[1]); shutdown_php(); return 0; } |
Similar concepts can be applied to handling arbitrary code execution and other tasks. Just be sure to use zend_first_try for your outermost container, and zend_try for any blocks inside that container.
The PHP Life Cycle
Variables from the Inside Out
Memory Management
Setting Up a Build Environment
Your First Extension
Returning Values
Accepting Parameters
Working with Arrays and HashTables
The Resource Data Type
PHP4 Objects
PHP5 Objects
Startup, Shutdown, and a Few Points in Between
INI Settings
Accessing Streams
Implementing Streams
Diverting the Stream
Configuration and Linking
Extension Generators
Setting Up a Host Environment
Advanced Embedding
Appendix A. A Zend API Reference
Appendix B. PHPAPI
Appendix C. Extending and Embedding Cookbook
Appendix D. Additional Resources