Resources are a special kind of data type in PHP.
The term
resources
doesn't really refer to any special kind of data, but to an
abstraction method for maintaining any kind of information.
Resources are kept in a special resource list within Zend. Each
entry in the list has a correspondending type definition that
denotes the kind of resource to which it refers. Zend then
internally manages all references to this resource. Access to a
resource is never possible directly - only via a provided API.
As soon as all references to a specific resource are lost, a
corresponding shutdown function is called.
For example, resources are used to store database
links and file descriptors. The
de facto
standard implementation can be found in the MySQL module, but
other modules such as the Oracle module also make use of
resources.
注意 In fact, a resource can be a pointer to anything you need to handle in your functions (e.g. pointer to a structure) and the user only has to pass a single resource variable to your function.
To create a new resource you need to register a
resource destruction handler for it. Since you can store any
kind of data as a resource, Zend needs to know how to free this
resource if its not longer needed. This works by registering
your own resource destruction handler to Zend which in turn
gets called by Zend whenever your resource can be freed
(whether manually or automatically). Registering your resource
handler within Zend returns you the
resource type handle
for that resource. This handle is needed whenever you want to
access a resource of this type later and is most of time stored
in a global static variable within your extension. There is no
need to worry about thread safety here because you only
register your resource handler once during module
initialization.
The Zend function to register your resource
handler is defined as:
ZEND_API int zend_register_list_destructors_ex(rsrc_dtor_func_t ld, rsrc_dtor_func_t pld, char *type_name, int module_number); |
There are two different kinds of resource
destruction handlers you can pass to this function: a handler
for normal resources and a handler for persistent resources.
Persistent resources are for example used for database
connection. When registering a resource, either of these
handlers must be given. For the other handler just pass
NULL
.
zend_register_list_destructors_ex()
accepts the following parameters:
ld | Normal resource destruction handler callback |
pld | Pesistent resource destruction handler callback |
type_name | A string specifying the name of your resource. It's always a good thing to specify an unique name within PHP for the resource type so when the user for example calls var_dump($resource); he also gets the name of the resource. |
module_number | The module_number is automatically available in your PHP_MINIT_FUNCTION function and therefore you just pass it over. |
The resource destruction handler (either normal or
persistent resources) has the following prototype:
void resource_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC); |
typedef struct _zend_rsrc_list_entry { void *ptr; int type; int refcount; } zend_rsrc_list_entry; |
Now we know how to start things, we define our own
resource we want register within Zend. It is only a simple
structure with two integer members:
typedef struct { int resource_link; int resource_type; } my_resource; |
void my_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC) { // You most likely cast the void pointer to your structure type my_resource *my_rsrc = (my_resource *) rsrc- ptr; // Now do whatever needs to be done with you resource. Closing // Files, Sockets, freeing additional memory, etc. // Also, don't forget to actually free the memory for your resource too! do_whatever_needs_to_be_done_with_the_resource(my_rsrc); } |
注意 One important thing to mention: If your resource is a rather complex structure which also contains pointers to memory you allocated during runtime you have to free them before freeing the resource itself!
// Somewhere in your extension, define the variable for your registered resources. // If you wondered what 'le' stands for: it simply means 'list entry'. static int le_myresource; // It's nice to define your resource name somewhere #define le_myresource_name "My type of resource" [...] // Now actually define our resource destruction handler void my_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC) { my_resource *my_rsrc = (my_resource *) rsrc- ptr; do_whatever_needs_to_be_done_with_the_resource(my_rsrc); } [...] PHP_MINIT_FUNCTION(my_extension) { // Note that 'module_number' is already provided through the // PHP_MINIT_FUNCTION() function definition. le_myresource = zend_register_resource_destructors_ex(my_destruction_handler, NULL, le_myresource_name, module_number); // You can register additional resources, initialize // your global vars, constants, whatever. } |
To actually register a new resource you use can
either use the
zend_register_resource()
function or the
ZEND_REGISTER_RESOURE()
macro, both defined in zend_list.h . Although the arguments for
both map 1:1 it's a good idea to always use macros to be
upwards compatible:
int ZEND_REGISTER_RESOURCE(zval *rsrc_result, void *rsrc_pointer, int rsrc_type); |
rsrc_result | This is an already initialized zval * container. |
rsrc_pointer | Your resource pointer you want to store. |
rsrc_type | The type which you received when you registered the resource destruction handler. If you followed the naming scheme this would be le_myresource . |
What is really going on when you register a new
resource is it gets inserted in an internal list in Zend and
the result is just stored in the given
zval *
container:
rsrc_id = zend_list_insert(rsrc_pointer, rsrc_type); if (rsrc_result) { rsrc_result- value.lval = rsrc_id; rsrc_result- type = IS_RESOURCE; } return rsrc_id; |
RETURN_RESOURCE(rsrc_id) |
注意 It is common practice that if you want to return the resource immidiately to the user you specify the return_value as the zval * container.
Zend now keeps track of all references to this
resource. As soon as all references to the resource are lost,
the destructor that you previously registered for this resource
is called. The nice thing about this setup is that you don't
have to worry about memory leakages introduced by allocations
in your module - just register all memory allocations that your
calling script will refer to as resources. As soon as the
script decides it doesn't need them anymore, Zend will find out
and tell you.
Now that the user got his resource, at some point
he is passing it back to one of your functions. The
value.lval
inside the
zval *
container contains the key to your resource and thus can be
used to fetch the resource with the following macro:
ZEND_FETCH_RESOURCE
:
ZEND_FETCH_RESOURCE(rsrc, rsrc_type, rsrc_id, default_rsrc_id, resource_type_name, resource_type) |
rsrc | This is your pointer which will point to your previously registered resource. |
rsrc_type | This is the typecast argument for your pointer, e.g. myresource * . |
rsrc_id | This is the address of the zval * container the user passed to your function, e.g. z_resource if zval *z_resource is given. |
default_rsrc_id | This integer specifies the default resource ID if no resource could be fetched or -1. |
resource_type_name | This is the name of the requested resource. It's a string and is used when the resource can't be found or is invalid to form a meaningful error message. |
resource_type | The resource_type you got back when registering the resource destruction handler. In our example this was le_myresource . |
To force removal of a resource from the list, use
the function
zend_list_delete()
. You can also force the reference count to increase if you
know that you're creating another reference for a previously
allocated value (for example, if you're automatically reusing a
default database link). For this case, use the function
zend_list_addref()
. To search for previously allocated resource entries, use
zend_list_find()
. The complete API can be found in
zend_list.h
.