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.



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



Extending and Embedding PHP
Extending and Embedding PHP
ISBN: 067232704X
EAN: 2147483647
Year: 2007
Pages: 175
Authors: Sara Golemon

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net