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:
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!
Now that we have defined
what our resource is and
our resource destruction handler
create a global variable within the extension holding
the resource ID so it can be accessed from every function
which needs it
define the resource name
write the resource destruction handler
and finally register the handler
// 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); |
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) |
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.