Resources
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 return value is an unique integer ID for your
resource type.
The resource destruction handler (either normal or persistent
resources) has the following prototype:
void resource_destruction_handler(zend_rsrc_list_entry *rsrc TSRMLS_DC); |
The passed
rsrc is a pointer to the following structure:
typedef struct _zend_rsrc_list_entry {
void *ptr;
int type;
int refcount;
} zend_rsrc_list_entry; |
The member
void *ptr is the actual pointer to
your resource.
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; |
Our resource destruction handler is probably going to look something like this:
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
we can go on and do the rest of the steps:
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); |
The return value is an unique integer identifier for that resource.
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; |
The returned
rsrc_id uniquly identifies the newly
registered resource. You can use the macro
RETURN_RESOURE to return it to the user:
注: 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) |
This macro has no return value.
It is for the developers convenience and takes care
of TSRMLS arguments passing and also does check if the resource
could be fetched.
It throws a warning message and returns the current PHP function
with
NULL if there was a problem retrieving the
resource.
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.