Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 50 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
50
Dung lượng
523,88 KB
Nội dung
528 Chapter 21 Extending PHP: Part I In most functions, you are handed a resource handle zval , and you need to extract the actual resource for it. Fortunately, doing so is very easy. If you are looking in a single list, you can use the following macro: ZEND_FETCH_RESOURCE(void *rsrc_struct, rsrc_struct_type, zval **zval_id, int default_id, char *name, int rsrc_list); These are the arguments of ZEND_FETCH_RESOURCE() : n rsrc_ struct is the actual pointer you want the resource data to be stored in. n rsrc_struct_type is the type of struct the resource is (for example, FILE * ). n zval_id is a zval of resource type that contains the resource ID. n default_id is an integer that specifies the default resource to use. A common use for this is to store the last accessed resource ID in an extension’s globals.Then, if a function that requires a resource does not have one passed to it, it simply uses the last resource ID. If -1 is used, no default is attempted. n name is a character string that is used to identify the resource you were seeking. This string is used only in information warning messages and has no technical purpose. n rsrc_list is the list that should be searched for the resource. If the resource fetch fails, a warning is generated, and the current function returns NULL . The following is the function pfgets() ,which reads a line from a file resource creat- ed by pfopen() : PHP_FUNCTION(pfgets) { char *out; int length = 1024; zval *rsrc; FILE *fh; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, “ r|l ” , &rsrc, &length) == FAILURE) { return; } ZEND_FETCH_RESOURCE(fh, FILE *, rsrc, -1, “ Persistent File Handle ” , persist); out = (char *) emalloc(length); fgets(out, length, fh); RETURN_STRING(out, 0); } Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 529 Extension Basics Returning Errors Generating procedural errors in extension code is almost identical to generating errors in PHP. Instead of calling trigger_error() in PHP, you can use zend_error() in C. zend_error() has the following API: zend_error(int error_type, char *fmt, .); error_type is the full range of errors enumerated in Chapter 3, “Error Handling.” Otherwise, the API is identical to the printf() family of functions.The following func- tion generates a warning: zend_error(E_WARNING, “ Hey this is a warning ” ); Remember that if you use E_ERROR , the error is fatal, and script execution is stopped. (Chapter 23,“Writing SAPIs and Extending the Zend Engine,” describes how to over- ride this behavior). Throwing exceptions is covered in detail in Chapter 22, which looks at object-ori- ented extensions in detail. Using Module Hooks In addition to enabling you to define and export function definitions, PHP also gives extensions the ability to run code in response to certain events in the PHP runtime. These events include the following: n Module startup n Module shutdown n Request startup n Request shutdown n phpinfo registration When you create a module, one of the required components is zend_module_entry , which looks like this: zend_module_entry example_module_entry = { STANDARD_MODULE_HEADER, “ example ” , example_functions, PHP_MINIT(example), PHP_MSHUTDOWN(example), PHP_RINIT(example), PHP_RSHUTDOWN(example), PHP_MINFO(example), VERSION, STANDARD_MODULE_PROPERTIES }; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 530 Chapter 21 Extending PHP: Part I The third member of this structure, example_functions , specifies the array of functions that will be registered by the extension.The rest of the structure declares the callbacks that will be executed by the various module hooks. Module Startup and Shutdown An extension’s module initialization and shutdown hooks are called when the extension is loaded and unloaded, respectively. For most extensions (those that are either compiled statically into PHP or loaded via an INI setting), module initialization happens once, at server startup. Module shutdown is similarly called during server shutdown. In the Apache 1.3 (or Apache 2 prefork MPM), this hook is called before any children are forked off.Thus, it is an ideal place to create or initialize any sort of global or shared resource, and it’s a poor place to initialize any resource that cannot be shared between processes. The module initialization hook is registered via the following function: PHP_MINIT_FUNCTION(example) { return SUCCESS; } In general, module initialization is the ideal place to define constants, initialize global data structures, and register and parse INI options. Defining Constants Because constants are immutable, they should be created during module initialization. In contrast to userspace PHP, where using a define() is not very different performance- wise from using global variables, defining constants in extension code is a clear win.This is because extension constants (such as functions and classes) do not need to be reinstat- ed between requests (although you can specify them to be destroyed at request end).This means that declaring even a large number of constants is basically free. To define a constant, you can use the following macros: REGISTER_LONG_CONSTANT(name, value, flags) REGISTER_DOUBLE_CONSTANT(name, value, flags) REGISTER_STRING_CONSTANT(name, string, flags) REGISTER_STRNIG_CONSTANT(name, string, string_length, flags) These are the possible flags for the macros: n CONST_CS —Constant is case-sensitive. n CONST_PERSISTENT —Constant should persist across requests. Obviously, if you are defining constants during module initialization, you must specify CONST_PERSISTENT . Unless you have specific reasons that you need to use conditional defines, you should define your constants as persistent and register them during module Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 531 Extension Basics initialization. Constants defined in userspace PHP are case-sensitive, so for PHP-like behavior you should use CONST_CS as well. The following is an example of a MINIT function in the sample extension that defines two constants: PHP_MINIT_FUNCTION(example) { REGISTER_LONG_CONSTANT( “ EXAMPLE_VERSION ” , VERSION, CONST_CS | CONST_PERSISTENT); REGISTER_STRING_CONSTANT( “ BUILD_DATE ” , “ 2004/01/03 ” , CONST_CS | CONST_PERSISTENT); return SUCCESS; } Enabling Globals Most extensions carry around a few global variables, which often hold default connec- tion data, global resources, and behavioral toggles. It is easy to implement globals without using the Zend macros, but those macros are primarily useful for automatically making globals thread-safe. To start with, you use the ZEND_BEGIN_MODULE_GLOBALS and ZEND_END_MODULE_GLOBALS macros to define a struct that holds global variables: ZEND_BEGIN_MODULE_GLOBALS(example) char *default_path; int default_fd; zend_bool debug; ZEND_END_MODULE_GLOBALS(example) These macros either create a plain struct zend_example_globals with these elements or a set of thread-safe struct s with these elements, depending on whether PHP has been compiled with thread safety. Because the resultant structs will need to be accessed differently, you should also create a conditional accessor that uses the correct access method, depending on PHP’s thread-safety situation: #ifdef ZTS #define ExampleG(v) TSRMG(example_globals_id, zend_example_globals *, v) #else #define ExampleG(v) (example_globals.v) #endif You should always then access globals as follows: char *path = ExampleG(default_path); Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 532 Chapter 21 Extending PHP: Part I To initialize globals, you create an initialization and destruction function, like this: static void example_init_globals(zend_example_globals *example_globals) { example_globals->default_path = NULL; } static void example_destroy_globals(zend_example_globals *example_globals) { } Then, during the MINIT phase, you perform the registration via the ZEND_INIT_ MODULE_GLOBALS() macro, as shown here: PHP_MINIT_FUNCTION(example) { ZEND_INIT_MODULE_GLOBALS(example, example_init_globals, example_destroy_globals); /* . */ } This destructor function is usually used when there are complex data types (such as a hashtable) that need to be cleaned on shutdown. If you do not need to register a destructor, you can simply pass NULL into the macro. Parsing INI Entries One thing that you can do in extensions that is impossible in userspace PHP code is registering and acting on php.ini settings. INI settings are useful for a couple reasons: n They provide global settings, independent of scripts. n They provide access controls on settings that can restrict developers from changing the INI settings in their scripts. n They allow for configuration of module hooks that are called before any scripts are run (during MINIT and RINIT , for instance). PHP provides a set of macros for easy registration of INI directives. First, in the main body of the C file, you add a macro block, like this: PHP_INI_BEGIN() /* ini specifications go here . */ PHP_INI_END() This defines an array of zend_ini_entry entries. Inside the block you make your INI declarations via the following macro: STD_PHP_INI_ENTRY(char *ini_directive, char *default_value, int location, int type, struct_member, struct_ptr, struct_property) Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 533 Extension Basics “ ini_directive ” is the full name of the INI directive that you are creating. It is a polite convention to namespace INI directives to avoid potential conflicts. For example, if you want to create an enabled setting for the sample extension, you should name it exam- ple.enabled . default_value specifies the default value for the INI directive. Because INI values are set as strings in the php.ini file, the default value must be passed as a string, even if it is numeric.This value is copied, so using a statically allocated value is fine. location specifies the places where a user can set the value of the directive.These places are defined as constants and can of course be combined with the bitwise OR operator.The following are acceptable bit settings for location : Setting Description PHP_INI_USER Entry can be set in user scripts via ini_set() . PHP_INI_PERDIR Entry can be set in php.ini , .htaccess ,or httpd.conf . In the .htaccess or httpd.conf file, it can be applied on a per-directory basis. PHP_INI_SYSTEM Entry can be set in php.ini or httpd.conf .The setting is serverwide. PHP_INI_ALL Entry can be set anywhere.This is equivalent to PHP_INI_USER|PHP_INI_PERDIR|PHP_INI_SYSTEM . type is a function name that specifies how to handle modifications to the INI directive (via php.ini , .htaccess , httpd.conf ,or ini_set() ).The following are the standard functions that can be used in this macro: Function Destination C Type OnUpdateBool zend_bool OnUpdateLong long OnUpdateReal double OnUpdateString char * OnUpdateStringUnempty char * These functions are aptly named and should be self-explanatory. OnUpdateStringUnempty fails if an empty string is passed to it. Otherwise, it is identical to OnUpdateString . INI values are almost always stored in extension globals.This makes sense because for an individual script, the INI values are globally set. (Even when you change them using ini_set() , you are effecting a global change.) In threaded environments, INI values are stored in thread local globals, so modification of an INI value affects only the value for that specific thread.To specify which global variable the setting should be stored in, you pass the final 3 bits of information. Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 534 Chapter 21 Extending PHP: Part I struct_type specifies the type of the structure you will be setting the value into. In the normal case, where this is the globals structure you created with ZEND_BEGIN_ MODULE_GLOBALS(example) , this type would be zend_example_globals. struct_ptr gives the specific instance of the type struct_type that should be modi- fied. In the usual case, where globals are declared via the built-in macros, this is example_globals . Finally, struct_property notes the element of the struct struct_name to modify. In the case of an integer value set, the STD_PHP_INI_ENTRY() macro roughly trans- lates into the following C code: (struct_type *)struct_ptr->struct_property = default_value; The following is an example that allows setting of the default_path global in the sam- ple extension via the INI directive example.path : PHP_INI_BEGIN() STD_PHP_INI_ENTRY( “ example.path ” , NULL, PHP_INI_PERDIR|PHP_INI_SYSTEM, OnUpdateString, default_path, zend_example_globals, example_globals) STD_PHP_INI_ENTRY( “ example.debug ” , “ off ” , PHP_INI_ALL, OnUpdateBool, debug, zend_example_globals, example_globals) PHP_INI_END() The default path will be set to NULL , and access to this variable will only be allowed from the php.ini , httpd.conf ,or .htaccess files. It also allows you to set debug , with a default value of off , from anywhere. To then register these entries, you call REGISTER_INI_ENTRIES() in the MINIT func- tion, as follows: PHP_MINIT_FUNCTION(example) { ZEND_INIT_MODULE_GLOBALS(example, example_init_globals, example_destroy_globals); REGISTER_INI_ENTRIES(); } If you want to access the values in the code (via ini_get() ), you can use a number of macros, which fetch the INI values as specified C types.The macros are broken into two groups.The first set, shown in Table 21.6, returns the current value of the macro. Table 21.6 Current INI Setting Accessors Macro Return C Type INI_BOOL(name) zend_bool INI_INT(name) long INI_FLT(name) double INI_STR(name) char * Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 535 Extension Basics The second set of macros, shown in Table 21.7, returns the original value of the macro, before any modification via httpd.conf , .htaccess ,or ini_set() . Table 21.7 Original INI Setting Accessors Macro Return C Type INI_BOOL_ORIG(name) zend_bool INI_INT_ORIG(name) long INI_FLT_ORIG(name) double INI_STR_ORIG(name) char * Module Shutdown If you have registered INI entries during MINIT , it is appropriate to unregister them dur- ing shutdown.You can do this via the following code: PHP_MSHUTDOWN_FUNCTION(example) { UNREGISTER_INI_ENTRIES(); } Request Startup and Shutdown In addition to module startup and shutdown, PHP also provides hooks that are called at the beginning and end of each request.The request initialization ( RINIT ) and shutdown ( RSHUTDOWN ) hooks are useful for creating and destroying per-request data. Request Startup Often you have resources that will be used in every request and that should always start at a consistent state. For example, ExampleG(default_path) may correspond with a file that needs to be opened at the beginning of every request and closed at the end (for example, a debugging log private to the extension and whose path can be set in an .htaccess file, thus making a persistent resource impractical). In that case, you might want to open the log at the beginning of every request and exit with an error if this is not possible. The code to perform this logic is placed in a PHP_RINIT_FUNCTION() block.At the beginning of every distinct request, PHP calls this function. If the function does not return SUCCESS , the request ends with a fatal error.The following is a request startup function that opens a default file at the beginning of every request: PHP_RINIT_FUNCTION(example) { if(ExampleG(default_path)) { ExampleG(default_fd) = open(ExampleG(default_path), O_RDWR|O_CREAT, 0); if(ExampleG(default_fd) == -1) { Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 536 Chapter 21 Extending PHP: Part I return FAILURE; } } return SUCCESS; } Request Shutdown Request shutdown is the ideal place to close any resources that you need to make sure are destroyed at the end of a script. It is also an ideal place to ensure that the extension’s state is set back to where it should be before a new request. PHP_RSHUTDOWN_ FUNCTION() declares this hook. In the following example, the sample extension needs to clean its logfile at request end: PHP_RSHUTDOWN _FUNCTION(example) { if(ExampleG(default_fd) > -1) { close(ExampleG(default_fd)); ExampleG(default_fd) = -1; } return SUCCESS; } The extension needs to close the file descriptor ExampleG(default_fd) that it opened during RINIT . If you wanted to leave it open, you could, and it would persist across requests. Because it can be set on a per-directory basis via .htaccess rules, leaving it open in this case is impractical. As in RINIT , this function must return SUCCESS , or the request will terminate with a fatal error. phpinfo() Registration PHP extensions are able to register themselves with phpinfo() , so that their status and configuration can be displayed. The PHP_MINFO_FUNCTION() function is registered with the PHP_MINFO() macro: zend_module_entry mysql_module_entry = { STANDARD_MODULE_HEADER, “ example ” , example_functions, PHP_MINIT(example), PHP_MSHUTDOWN(example), PHP_RINIT(example), PHP_RSHUTDOWN(example), PHP_MINFO(example), VERSION, STANDARD_MODULE_PROPERTIES }; Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 537 An Example: The Spread Client Wrapper PHP_MINFO_FUNCTION() is basically a CGI script that outputs certain information—usual- ly an HTML table that lists the function’s status and certain configuration information. To ease output formatting and support both plain-text and HTML phpinfo() formats, you should use the built-in functions to generate output.The following is a simple MINFO block that just notes that the sample extension is enabled: PHP_MINFO_FUNCTION(example) { php_info_print_table_start(); php_info_print_table_row(2, “ Example Extension ” , “ enabled ” ); php_info_print_table_end(); } The php_info_print_table_row() function takes the number of columns and a string for each one. An Example: The Spread Client Wrapper You now have all the tools you need to build a procedural interface PHP extension in C.To tie all these parts together, a full example is called for. Chapter 15,“Building a Distributed Environment,” shows an implementation of a dis- tributed cache management system that uses Spread. Spread is a group communication toolkit that allows members to join a set of named groups and receive messages for those groups by using certain semantics (for example, that every member in the group will receive all messages in the same order as every other member).These strong rules pro- vide an excellent mechanism for tackling distributed tasks, such as building multireader distributed logging systems, master–master database replication, or, as in the case just shown, reliable messaging systems between multiple participants. The Spread library presents a very simple C API, so it is an ideal example for writing a PHP extension around.The following parts of the C API are covered here: int SP_connect( const char *spread_name, const char *private_name, int priority, int group_membership, mailbox *mbox, char *private_group ); int SP_disconnect( mailbox mbox ); int SP_join( mailbox mbox, const char *group ); int SP_multicast( mailbox mbox, service service_type, const char *group, int16 mess_type, int mess_len, const char *mess ); int SP_multigroup_multicast( mailbox mbox, service service_type, int num_groups, const char groups[][MAX_GROUP_NAME], int16 mess_type, const scatter *mess ); int SP_receive( mailbox mbox, service *service_type, char sender[MAX_GROUP_NAME], int max_groups, Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... function_entry spread_functions[] = { PHP_ FE(spread_connect, NULL) PHP_ FE(spread_multicast, NULL) PHP_ FE(spread_disconnect, NULL) PHP_ FE(spread_join, NULL) PHP_ FE(spread_receive, NULL) {NULL, NULL, NULL} }; Then you register the module: zend_module_entry spread_module_entry = { STANDARD_MODULE_HEADER, “spread”, spread_functions, PHP_ MINIT(spread), NULL, NULL, NULL, PHP_ MINFO(spread), “1.0”, STANDARD_MODULE_PROPERTIES... connects to a local spread daemon and sends a test message to the test group: < ?php $spread = new Spread_Logger(“127.0.0.1:4803”, “test”); $spread->send(“This is a test message.”); ?> Further Reading Some documentation on PHP extension authoring is available in the online PHP documentation, at http://www .php. net/manual/en/zend .php A statement about the diligence put into maintaining that section of the... 547 548 Chapter 21 Extending PHP: Part I Jim Winstead gives a regular (and evolving) talk on extension writing, titled “Hacking the PHP Source.” A recent copy of the slides is available at http://talks .php. net/ show/hacking-fall-2003 The Spread client wrapper extension is available in the PECL extension library, at http://pecl .php. net/spread LY F M A 22 E T Extending PHP: Part II N OW THAT YOU’VE... extension authoring, this chapter covers advanced extension features In this chapter you will see how to write classes and objects in extensions, how to write custom session handlers, and how to use the streams API Implementing Classes By far the largest change from PHP 4 to PHP 5 is the new object model Mirroring this, the biggest change from PHP 4 extensions to PHP 5 extensions is handling classes and... objects.The procedural extension code you learned in Chapter 21, “Extending PHP: Part I,” is almost entirely backward-portable to PHP 4.The use of macros for many of the functions helps things: Macros allow for internal reimplementation without invalidating extension code Class code, however, is substantially different in PHP 5 than in PHP 4 Not only have internal Zend Engine structures changed, but the basic... visible through the reflection API n n The preferred PHP 5 method is to declare the variable in the class definition, like this: class example { public $instanceProp = ‘default’; } In PHP 4 it is standard to create all extension class properties as dynamic instance properties, usually in the class constructor In PHP 5, extension classes should look more like PHP classes (at least in their public interface).This... this: class example { public function _ _constructor() { $this->instanceProp = ‘default’; } } 551 552 Chapter 22 Extending PHP: Part II PHP 5 allows for dynamic creation of instance variables such as these, but this type of variable creation is largely for backward compatibility with PHP 4.There are two major problems with dynamic instance properties: Because they are not part of the class entry, they... groups 6 Receiving messages to a group you belong to The strategy is to supply a PHP- level function for each of these C functions, except for SP_multicast() and SP_multigroup_multicast(), which PHP s weak typing makes ideal to combine into a single function Connections to spread will be handled via a resource To start the PHP class, you generate a standard skeleton file using this: ext_skel extname=spread... *object, char *name, int name_length, char *value TSRMLS_DC); 555 556 Chapter 22 Extending PHP: Part II These functions work identically to the zend_declare_property() functions presented in the previous section To see how this works, consider the following PHP code taken from the classic object-orientation example in the PHP manual: class Cart { public $items; function num_items() { return count($this->items);... of return_value: PHP_ FUNCTION(spread_receive) { zval **mbox_zval, *groups_zval; int *mbox; int sperrno; int i, endmis, ret, ngrps, msize; int16 mtype; service stype; static int oldmsize = 0; static int oldgsize = 0; static int newmsize = (1 . example ” , example_functions, PHP_ MINIT(example), PHP_ MSHUTDOWN(example), PHP_ RINIT(example), PHP_ RSHUTDOWN(example), PHP_ MINFO(example), VERSION, STANDARD_MODULE_PROPERTIES. spread_functions[] = { PHP_ FE(spread_connect, NULL) PHP_ FE(spread_multicast, NULL) PHP_ FE(spread_disconnect, NULL) PHP_ FE(spread_join, NULL) PHP_ FE(spread_receive,