Extension Globals

If it were possible to guarantee that only one PHP script were ever active in a single process at any given time, your extension could declare any global variables it wanted to and access them with the knowledge that no other script actions will corrupt the values between opcodes. For non-threaded SAPIs, this actually is true because any given process space can only execute one code path at a time.

In the case of threaded SAPIs however, two or more requests could wind up trying to reador worse writethe same value at once. To combat this problem, the concept of extension globals was introduced to provide a unique bucket of data storage for each extension's data.

Declaring Extension Globals

To request a storage bucket for your extension, you first need to declare all your "global" variables in a unified structure somewhere within your php_sample4.h file. For example, if your extension kept track of a counter for the number of times a particular function was called within a request, you might define a structure containing an unsigned long:

ZEND_BEGIN_MODULE_GLOBALS(sample4) unsigned long counter; ZEND_END_MODULE_GLOBALS(sample4)

The ZEND_BEGIN_MODULE_GLOBALS and ZEND_END_MODULE_GLOBALS macros provide a consistent framework for defining extension global structs. If you were to look at the expansion of this block, you'd see it was simply:

typedef struct _zend_sample4_globals { unsigned long counter; } zend_sample4_globals;

Additional members could then be added as you would with any other C struct. Now that you have a definition for your storage bucket, it's time to declare it within your extension's sample4.c file just after the #include "php_sample4.h" statement:

ZEND_DECLARE_MODULE_GLOBALS(sample4);

Depending on whether thread safety is enabled, this will resolve to one of two forms. For nonthread-safe builds, such as Apache1, Apache2-prefork, CGI, CLI, and many others, this declares the zend_sample4_globals structure as an immediate value within the true global scope:

zend_sample4_globals sample4_globals;

This is really no different than any other global scope variable you would declare in any other single-threaded application. The counter value is accessed directly through sample4_globals.counter. For thread-safe builds, on the other hand, only an integer is declared, which will later act as a reference to the real data:

int sample4_globals_id;

Populating this ID means declaring your extension globals to the engine. Using the information provided, the engine will allocate a block of memory at the spawning of each new thread for private storage space to be used by the individual requests that thread services. Add the following block of lines to your MINIT function:

#ifdef ZTS ts_allocate_id(&sample4_globals_id, sizeof(zend_sample4_globals), NULL, NULL); #endif

Notice that this statement has been wrapped in a set of ifdefs to prevent it from executing when Zend Thread Safety (ZTS) is not enabled. This makes sense because the sample4_globals_id is only declared (or needed) in builds that will be used in a threaded environment. Non-threaded builds will use the immediate sample4_globals variable declared earlier.

Per-Thread Initializing and Shutdown

In non-threaded builds, only one copy of your zend_sample4_globals struct will ever exist within a given process. To initialize it, you could assign default values or allocate resources within MINIT or RINIT and, if necessary, free those resources during MSHUTDOWN or RSHUTDOWN as appropriate.

However, for threaded builds, a new structure is allocated every time a new thread is spun. In practice, this may occur a dozen times during web server startup alone and hundredspossibly thousandsof times during the lifetime of the webserver process. In order to know how to initialize and shut down your extension's globals, the engine requires a set of callbacks to issue. This is where the NULL parameters you passed to ts_allocate_id() earlier come into play; add the following two methods above your MINIT function:

static void php_sample4_globals_ctor( zend_sample4_globals *sample4_globals TSRMLS_DC) { /* Initialize a new zend_sample4_globals struct * During thread spin-up */ sample4_globals->counter = 0; } static void php_sample4_globals_dtor( zend_sample4_globals *sample4_globals TSRMLS_DC) { /* Any resources allocated during initialization * May be freed here */ }

Then use those functions for startup and shutdown:

PHP_MINIT_FUNCTION(sample4) { REGISTER_STRING_CONSTANT("SAMPLE4_VERSION", PHP_SAMPLE4_EXTVER, CONST_CS | CONST_PERSISTENT); #ifdef ZTS ts_allocate_id(&sample4_globals_id, sizeof(zend_sample4_globals), (ts_allocate_ctor)php_sample4_globals_ctor, (ts_allocate_dtor)php_sample4_globals_dtor); #else php_sample4_globals_ctor(&sample4_globals TSRMLS_CC); #endif return SUCCESS; } PHP_MSHUTDOWN_FUNCTION(sample4) { #ifndef ZTS php_sample4_globals_dtor(&sample4_globals TSRMLS_CC); #endif return SUCCESS; }

Notice that the ctor and dtor functions were called manually when ZTS is not defined. Don't forget that non-threaded builds need initialization and shutdown too!

Note

You might be wondering why TSRMLS_CC was used for the direct calls to php_sampl4_globals_ctor() and php_sample4_globals_dtor(). If you're thinking "That's completely unnecessary, those evaluate to nothing at all when ZTS is disabled, and because of the #ifdef directives I know that ZTS is disabled!", then you're absolutely right. These counterparts to the TSRMLS_DC directives in the declaration are used purely as a matter of consistency. On the positive side, if the Zend API ever changes in such a way that these values do become relevant even for non-ZTS builds, your code will be right and ready to accommodate it.

 

Accessing Extension Globals

Now that your extension has a set of globals, you can start accessing them in your code. In non-ZTS mode this is nice and simple; just access the sample4_globals variable in the process's global scope and use the relevant member such as in the following userspace function which increments the counter you defined earlier and returns its current value:

PHP_FUNCTION(sample4_counter) { RETURN_LONG(++sample4_globals.counter); }

Nice and clean. Unfortunately, this approach won't work with threaded PHP builds. For these, you'll need to do a lot more work. Here's that function's return statement again, this time using ZTS semantics:

RETURN_LONG(++TSRMG(sample4_globals_id, zend_sample4_globals*, counter));

The TSRMG() macro takes that TSRMLS_CC parameter you've been passing around ad infinitum to find the current thread's pool of resource structures. From there, it uses the sample4_globals_id index to map into the specific point in that pool where your extension's specific global structure is. Finally, it uses the data type to map the element name to an offset within that structure. Because you typically don't know whether your extension will be used in ZTS or non-ZTS mode, you'll need to accommodate both. To do that, you could rewrite the function like so:

PHP_FUNCTION(sample4_counter) { #ifdef ZTS RETURN_LONG(++TSRMG(sample4_globals_id, zend_sample4_globals*, counter)); #else /* non-ZTS */ RETURN_LONG(++sample4_globals.counter); #endif }

Look ugly? It is. Imagine your entire codebase peppered with these ifdef directives every time a thread-safe global is accessed. It'd look worse than Perl! This is why all core extensions, as well as those found in PECL, use an extra macro layer to abstract this case out. Drop the following definition into your php_sample4.h file:

#ifdef ZTS #include "TSRM.h" #define SAMPLE4_G(v) TSRMG(sample4_globals_id, zend_sample4_globals*, v) #else #define SAMPLE4_G(v) (sample4_globals.v) #endif

Then replace your new function definition with this simpler, more legible form:

PHP_FUNCTION(sample4_counter) { RETURN_LONG(++SAMPLE4_G(counter)); }

Does that macro strike a sense of deja vu? It should. It's the same concept and practice that you've already seen when working with EG(symbol_table) and EG(active_symbol_table). While looking through various parts of the PHP source tree and other extensions, you'll come across this kind of macro frequently. A few common global access macros are listed in Table 12.1.

Table 12.1. Common Global Access Macros

Accessor Macro

Associated Data

EG()

Executor Globals. This structure is primarily used by the engine internals to track the state of the current request. Information such as symbol tables, function and class tables, constants, and resources can be found here.

CG()

Core Globals. Used primarily by the Zend Engine during script compilation and an assortment of deep-core execution steps. It's rare that your extension will examine these values directly.

PG()

PHP Globals. Most of the "Core" php.ini directives map to one or more elements of the php globals structure. PG(register_globals), PG(safe_mode), and PG(memory_limit) are just a few examples.

FG()

File Globals. Most file I/Oor streamsrelated global variables are tucked into this structure exported by the standard extension.

Категории