Chia sẻ: Thanh Cong | Ngày: | Loại File: PDF | Số trang:50

0
47
lượt xem
9

Mô tả tài liệu

Tham khảo tài liệu 'advanced php programming- p13', công nghệ thông tin, kỹ thuật lập trình phục vụ nhu cầu học tập, nghiên cứu và làm việc hiệu quả

Chủ đề:

Bình luận(0)

Lưu

## Nội dung Text: Advanced PHP Programming- P13

1. 578 Chapter 22 Extending PHP: Part II if((fd = open(filename, O_RDWR)) < -1) { return NULL; } if(!file_length) { if(fstat(fd, &sb) == -1) { close(fd); return NULL; } file_length = sb.st_size; } if((mpos = mmap(NULL, file_length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0)) == (void *) -1) { return NULL; } data = emalloc(sizeof(struct mmap_stream_data)); data->base_pos = mpos; data->current_pos = mpos; data->len = file_length; close(fd); stream = php_stream_alloc(&mmap_ops, data, NULL, “mode”); if(opened_path) { *opened_path = estrdup(filename); } return stream; } Now you only need to register this function with the engine.To do so, you add a regis- tration hook to the MINIT function, as follows: PHP_MINIT_FUNCTION(mmap_session) { php_register_url_stream_wrapper(“mmap”, &mmap_wrapper TSRMLS_CC); } Here the first argument, “mmap”, instructs the streams subsystem to dispatch to the wrap- per any URLs with the protocol mmap.You also need to register a de-registration func- tion for the wrapper in MSHUTDOWN: PHP_MSHUTDOWN_FUNCTION(mmap_session) { php_unregister_url_stream_wrapper(“mmap” TSRMLS_CC); } This section provides only a brief treatment of the streams API. Another of its cool fea- tures is the ability to write stacked stream filters.These stream filters allow you to trans- parently modify data read from or written to a stream. PHP 5 features a number of stock stream filters, including the following:
2. Further Reading 579 n Content compression n HTTP 1.1 chunked encoding/decoding n Streaming cryptographic ciphers via mcrypt n Whitespace folding The streams API’s ability to allow you to transparently affect all the internal I/O func- tions in PHP is extremely powerful. It is only beginning to be fully explored, but I expect some very ingenious uses of its capabilities over the coming years. Further Reading The official PHP documentation of how to author classes and streams is pretty sparse. As the saying goes, “Use the force, read the source.”That having been said, there are some resources out there. For OOP extension code, the following are some good resources: n The Zend Engine2 Reflection API, in the PHP source tree under Zend/ reflection_api.c, is a good reference for writing classes in C. n The streams API is documented in the online PHP manual at http://www.php.net/manual/en/streams.php. In addition,Wez Furlong, the streams API architect, has an excellent talk on the subject, which is available at http://talks.php.net/index.php/Streams.
3. 23 Writing SAPIs and Extending the Zend Engine T HE FLIP SIDE TO WRITING PHP EXTENSIONS in C is writing applications in C that run PHP.There are a number of reasons you might want to do this: To allow PHP to efficiently operate on a new Web server platform. n To harness the ease of use of a scripting language inside an application. PHP pro- n vides powerful templating capabilities that can be validly embedded in many appli- cations. An example of this is the PHP filter SAPI, which provides a PHP interface for writing sendmail mail filters in PHP. For easy extensibility.You can allow end users to customize parts of an application n with code written in PHP. Understanding how PHP embeds into applications is also important because it helps you get the most out of the existing SAPI implementations. Do you like mod_php but feel like it’s missing a feature? Understanding how SAPIs work can help you solve your problems. Do you like PHP but wish the Zend Engine had some additional features? Understanding how to modify its behavior can help you solve your problems. SAPIs SAPIs provide the glue for interfacing PHP into an application.They define the ways in which data is passed between an application and PHP. The following sections provide an in-depth look at a moderately simple SAPI, the PHP CGI SAPI, and the embed SAPI, for embedding PHP into an application with minimal custom needs.
10. 588 Chapter 23 Writing SAPIs and Extending the Zend Engine getenv callback to extract it, as shown here: static char *sapi_cgi_read_cookies(TSRMLS_D) { return sapi_cgibin_getenv((char *)”HTTP_COOKIE”,0 TSRMLS_CC); } Again, filtering this data is covered in the section “SAPI Input Filters.” Next comes sapi_module_struct.register_server_variables. As the name implies, this function is passed in what will become the $_SERVER autoglobal array, and the SAPI has the option of adding elements to the array.The following is the top-level register_server_variables callback for the CGI SAPI: static void sapi_cgi_register_variables(zval *track_vars_array TSRMLS_DC) { php_import_environment_variables(track_vars_array TSRMLS_CC); php_register_variable(“PHP_SELF”, (SG(request_info).request_uri ? SG(request_info).request_uri:””), track_vars_array TSRMLS_CC); } This calls php_import_environment_variables(), which loops through all the shell environment variables and creates entries for them in$_SERVER.Then it sets $_SERVER[‘PHP_SELF’] to be the requested script. The last declared element in the CGI module is sapi_module_struct.log_message. This is a fallback function when no other error logging facility is specified. If error_log is not set in the php.ini file, then this is the function that will be called to print out any errors you receive.The CGI module implements this by printing to stderr, as follows: static void sapi_cgi_log_message(char *message) { fprintf(stderr, “%s\n”, message); } We’ve now covered the standard sapi_module_struct elements.The filtering callbacks default_post_reader, treat_data, and input_filter are covered later in this chapter, in the section “SAPI Input Filters.”The others are special-purpose elements that are not covered here. The CGI SAPI Application You need to incorporate the CGI SAPI into an application that can actually run it.The actual CGI main() routine is very long, as it supports a wide variety of options and flags. Instead of covering that (which could easily take an entire chapter), this section provides a very stripped-down version of the main() routine that implements no optional flags. Here is the stripped-down version of the CGI main() routine: int main(int argc, char **argv) { 11. SAPIs 589 int exit_status = SUCCESS; zend_file_handle file_handle; int retval = FAILURE; signal(SIGPIPE, SIG_IGN); /* ignore disconnecting clients */ sapi_startup(&cgi_sapi_module); cgi_sapi_module.executable_location = argv[0]; if (php_module_startup(&cgi_sapi_module, NULL, 0) == FAILURE) { return FAILURE; } zend_first_try { SG(server_context) = (void *) 1; /* avoid server_context==NULL checks */ init_request_info(TSRMLS_C); file_handle.type = ZEND_HANDLE_FILENAME; file_handle.filename = SG(request_info).path_translated; file_handle.handle.fp = NULL; file_handle.opened_path = NULL; file_handle.free_filename = 0; if (php_request_startup(TSRMLS_C)==FAILURE) { php_module_shutdown(TSRMLS_C); return FAILURE; } retval = php_fopen_primary_script(&file_handle TSRMLS_CC); if (retval == FAILURE && file_handle.handle.fp == NULL) { SG(sapi_headers).http_response_code = 404; PUTS(“No input file specified.\n”); php_request_shutdown((void *) 0); php_module_shutdown(TSRMLS_C); return FAILURE; } php_execute_script(&file_handle TSRMLS_CC); if (SG(request_info).path_translated) { char *path_translated; path_translated = strdup(SG(request_info).path_translated); efree(SG(request_info).path_translated); SG(request_info).path_translated = path_translated; } php_request_shutdown((void *) 0); if (exit_status == 0) { exit_status = EG(exit_status); } if (SG(request_info).path_translated) { free(SG(request_info).path_translated); SG(request_info).path_translated = NULL; 12. 590 Chapter 23 Writing SAPIs and Extending the Zend Engine } } zend_catch { exit_status = 255; } zend_end_try(); php_module_shutdown(TSRMLS_C); sapi_shutdown(); return exit_status; } The following is the helper function init_request_info(), which sets the SAPI globals for script locations and query string parameters from the environment as per the CGI specification: static void init_request_info(TSRMLS_D) { char *env_script_filename = sapi_cgibin_getenv(“SCRIPT_FILENAME”,0 TSRMLS_CC); char *env_path_translated = sapi_cgibin_getenv(“PATH_TRANSLATED”,0 TSRMLS_CC); char *script_path_translated = env_script_filename; /* initialize the defaults */ SG(request_info).path_translated = NULL; SG(request_info).request_method = NULL; SG(request_info).query_string = NULL; SG(request_info).request_uri = NULL; SG(request_info).content_type = NULL; SG(request_info).content_length = 0; SG(sapi_headers).http_response_code = 200; /* script_path_translated being set is a good indication that we are running in a cgi environment, since it is always null otherwise. otherwise, the filename of the script will be retrieved later via argc/argv */ if (script_path_translated) { const char *auth; char *content_length = sapi_cgibin_getenv(“CONTENT_LENGTH”,0 TSRMLS_CC); char *content_type = sapi_cgibin_getenv(“CONTENT_TYPE”,0 TSRMLS_CC); SG(request_info).request_method = sapi_cgibin_getenv(“REQUEST_METHOD”,0 TSRMLS_CC); SG(request_info).query_string = sapi_cgibin_getenv(“QUERY_STRING”,0 TSRMLS_CC); if (script_path_translated && !strstr(script_path_translated, “..”)) { SG(request_info).path_translated = estrdup(script_path_translated); } SG(request_info).content_type = (content_type ? content_type : “” ); SG(request_info).content_length = (content_length?atoi(content_length):0); 13. SAPIs 591 /* The CGI RFC allows servers to pass on unvalidated Authorization data */ auth = sapi_cgibin_getenv(“HTTP_AUTHORIZATION”,0 TSRMLS_CC); php_handle_auth_data(auth TSRMLS_CC); } } The following is the basic execution order of this script: 1. Call sapi_startup(&cgi_sapi_module).This sets up all the default SAPI struc- tures. 2. Call php_module_startup(&cgi_sapi_module, NULL, 0).This actually loads, initializes, and registers this SAPI. 3. Call init_request_info().This function sets the necessary SAPI global’s request_info values from the environment.This is how the CGI SAPI knows what file you want to execute and what parameters are being passed to it. Every SAPI implements this differently. For example, mod_php extracts all this informa- tion from the Apache request_rec data structure. 4. Initialize zend_file_handle with the location of the script to execute. 5. Call php_request_startup().This function does a large amount of work: It ini- tializes the output buffering system for the request, creates all autoglobal variables, calls the RINIT hooks of all registered extensions, and calls the activate callback for the SAPI. 6. Open and execute the script with php_fopen_primary_script(&file_handle TSRMLS_CC) and php_execute_script(&file_handle TSRMLS_CC).Technically, it is not necessary to open the script, but doing so allows an easy way to check whether the script actually exists.When php_execute_script() returns, the script has completed. 7. Call php_request_shutdown((void *) 0) to complete the request.This calls the RSHUTDOWN hooks for modules, calls the deactivate callback registered by the SAPI, and ends output buffering and sends all data to the client. 8. Call php_module_shutdown.This shuts down the SAPI permanently because the CGI SAPI serves only a single request per invocation. 9. Call sapi_shutdown().This performs final cleanup of the SAPI environment. This is the complete process of embedding the PHP interpreter into an application, using the SAPI interface. The Embed SAPI The CGI SAPI seems like quite a bit of work, but the majority of it involves handling automatic importing of data from the caller’s environment. PHP goes to great trouble to 14. 592 Chapter 23 Writing SAPIs and Extending the Zend Engine allow transparent access to user environment data, and much of that work has to be done in the SAPI implementation. If your goals are less ambitious than full custom PHP integration and you only want to execute PHP code as part of an application, the embed SAPI may be the right solu- tion for you.The embed SAPI exposes PHP as a shared library that you can link against and run code. To build the embed library, you need to compile PHP with the following configura- tion line: --enable-embed This creates libphp5.so. The embed SAPI exposes two macros to the user: PHP_EMBED_START_BLOCK(int argc, char **argv) PHP_EMBED_END_BLOCK() Inside the block defined by those macros is a running PHP environment where you can execute scripts with this: php_execute_script(zend_file_handle *primary_file TSRMLS_DC); or this: zend_eval_string(char *str, zval *retval_ptr, char *string_name TSRMLS_DC); As an example of just how simple this is, here is a working PHP shell that interactively executes anything you pass to it: #include #include #include #include int main(int argc, char **argv) { char *code; PHP_EMBED_START_BLOCK(argc,argv); while((code = readline(“> “)) != NULL) { zend_eval_string(code, NULL, argv[0] TSRMLS_CC); } PHP_EMBED_END_BLOCK(); return 0; } You then compile this, as shown here: > gcc -pipe -g -O2 -I/usr/local/include/php -I/usr/local/include/php/Zend \ -I/usr/local/include/php/TSRM -I/usr/local/include/php/main -c psh.c > gcc -pipe -g -O2 -L/usr/local/lib -lreadline -lncurses -lphp5 psh.o -o psh 15. SAPIs 593 Note that the embed SAPI sets the$argc and $argv autoglobals from what is passed to PHP_EMBED_START_BLOCK(). Check out the following psh session: > ./psh foo bar > print_r($argv); Array ( [0] => ./psh [1] => foo [2] => bar ) > $a = 1; > print “$a\n”; 1 > This is a toy example in that psh is pretty featureless, but it demonstrates how you can leverage all of PHP in under 15 lines of C. Later in this chapter you will use the embed SAPI to build a more significant application: the opcode dumper described in Chapter 20. SAPI Input Filters In Chapter 13, “User Authentication and Session Security,” you learned a bit about cross- site scripting and SQL injection attacks. Although they manifest differently, both attacks involve getting a Web application to accidentally execute (or in the case of cross-site scripting, getting a third-party user to execute) malicious code in your application’s space. The solution to all attacks of this sort is simple:You must be fanatical about validating and sanitizing any input a user gives you.The responsibility for this sanitization process lies with the developer, but leaving it at that can be unsatisfactory for two reasons: n Developers sometimes make mistakes. Cross-site scripting is an extremely serious security issue, and relying on everyone who touches PHP code to always perform the correct security measures may not be good enough. n Sanitizing all your data in PHP on every request can be slow. To help address this issue, the SAPI interface provides a set of three callbacks that can be used to automatically sanitize data on every incoming request: input_filter, treat_data, and default_post_reader. Because they are registered at the SAPI level, they are invisible to the developer and are executed automatically.This makes it impossi- ble to forget to apply them on a page. Further, because they are implemented in C and occur before data is inserted into the autoglobal arrays, the implementations can be much faster than anything written in PHP.
16. 594 Chapter 23 Writing SAPIs and Extending the Zend Engine input_filter The most useful of the filter callbacks is sapi_module_struct.input_filter. A regis- tered input_filter callback is called on the input to be populated into the auto-globals $_POST,$_GET, and $_COOKIE before the input data is actually inserted into the arrays. An input_filter callback provides a blanket mechanism for sanitizing all user- submitted data before it is available to userspace code. This section describes an input_filter that removes all HTML from POST, GET, and COOKIE data using the C code from the strip_tags() PHP function.This is a variation of the input_filter example in the PHP distribution, with a few extra bells and whis- tles. A new set of autoglobal arrays—$_RAW_POST, $_RAW_GET, and$_RAW_COOOKIE—is created, and the original contents of each variable are placed in that new array, with the cleaned data going into the standard arrays.That way, if a developer needs access to the original source, he or she can still have access to it, but the standard arrays will be free of HTML. Input filters of all kinds can be registered post-SAPI startup, and this one is imple- mented as an extension.This is nice because it means you do not have to actually modify the code of the SAPI you use. First is the standard module header.You add a global zval * for each of the new autoglobal arrays you are creating. Here is the code for this: #ifdef HAVE_CONFIG_H # include “config.h” #endif #include “php.h” #include “php_globals.h” #include “php_variables.h” #include “ext/standard/info.h” #include “ext/standard/php_string.h” ZEND_BEGIN_MODULE_GLOBALS(raw_filter) zval *post_array; zval *get_array; zval *cookie_array; ZEND_END_MODULE_GLOBALS(raw_filter) #ifdef ZTS #define IF_G(v) TSRMG(raw_filter_globals_id, zend_raw_filter_globals *, v) #else #define IF_G(v) (raw_filter_globals.v) #endif ZEND_DECLARE_MODULE_GLOBALS(raw_filter)