您的位置:首页 > 编程语言 > PHP开发

通过实例来了解PHP中的persistent resource和non persistent resource

2010-12-10 10:23 375 查看
例子代码来源于网页http://devzone.zend.com/article/1021,其原本是讲解如何编写PHP中的扩展(extension)的,在此提取了其中的部分代码,并增加了二个函数,用于对PHP中的non persistent/persistent resource进行尝试; 当然以下这部分代码也相对完整,可以独立进行编译及运行。

config.m4

PHP_ARG_ENABLE(hello, [whether to enable Hello World support],
[ --enable-hello Enable Hello World support])
if test "$PHP_HELLO" = "yes"; then
AC_DEFINE(HAVE_HELLO, 1, [Whether you have Hello World])
PHP_NEW_EXTENSION(hello, hello.c, $ext_shared)
fi


php_hello.h

#ifndef PHP_HELLO_H
#define PHP_HELLO_H 1
#ifdef ZTS
#include "TSRM.h"
#endif
#define PHP_HELLO_WORLD_VERSION "1.0"
#define PHP_HELLO_WORLD_EXTNAME "hello"
typedef struct _php_hello_person {
char *name;
int name_len;
long age;
} php_hello_person;
#define PHP_HELLO_PERSON_RES_NAME "Person Data"
PHP_MINIT_FUNCTION(hello);
PHP_FUNCTION(hello_person_new);
PHP_FUNCTION(hello_person_pnew);
PHP_FUNCTION(hello_person_get_age);
PHP_FUNCTION(hello_person_update_age);
extern zend_module_entry hello_module_entry;
#define phpext_hello_ptr &hello_module_entry
#endif


hello.c

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "php_hello.h"
int le_hello_person;
int le_hello_person_persist;
static function_entry hello_functions[] = {
PHP_FE(hello_person_new, NULL)
PHP_FE(hello_person_pnew, NULL)
PHP_FE(hello_person_get_age, NULL)
PHP_FE(hello_person_update_age, NULL)
{NULL, NULL, NULL}
};
zend_module_entry hello_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_HELLO_WORLD_EXTNAME,
hello_functions,
PHP_MINIT(hello),
NULL,
NULL,
NULL,
NULL,
#if ZEND_MODULE_API_NO >= 20010901
PHP_HELLO_WORLD_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_HELLO
ZEND_GET_MODULE(hello)
#endif
static void php_hello_person_persist_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
php_hello_person *person = (php_hello_person*)rsrc->ptr;
if (person) {
if (person->name) {
pefree(person->name, 1);
}
pefree(person, 1);
}
}
static void php_hello_person_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
{
php_hello_person *person = (php_hello_person*)rsrc->ptr;
if (person) {
if (person->name) {
efree(person->name);
}
efree(person);
}
}
PHP_MINIT_FUNCTION(hello)
{
le_hello_person = zend_register_list_destructors_ex(php_hello_person_dtor, NULL, PHP_HELLO_PERSON_RES_NAME, module_number);
le_hello_person_persist = zend_register_list_destructors_ex (NULL, php_hello_person_persist_dtor, PHP_HELLO_PERSON_RES_NAME
, module_number);
return SUCCESS;
}
PHP_FUNCTION(hello_person_new)
{
php_hello_person *person;
char *name;
int name_len;
long age;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &name, &name_len, &age) == FAILURE) {
RETURN_FALSE;
}
person = emalloc(sizeof(php_hello_person));
person->name = estrndup(name, name_len);
person->name_len = name_len;
person->age = age;
ZEND_REGISTER_RESOURCE(return_value, person, le_hello_person);
}
PHP_FUNCTION(hello_person_pnew)
{
php_hello_person *person;
char *name, *key;
int name_len, key_len;
long age;
list_entry *le, new_le;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &name, &name_len, &age) == FAILURE) {
RETURN_FALSE;
}
/* Look for an established resource */
key_len = spprintf(&key, 0, "hello_person_%s_%d ", name, age);
if (zend_hash_find(&EG(persistent_list), key, key_len + 1, ≤) == SUCCESS) {
/* An entry for this person already exists */
ZEND_REGISTER_RESOURCE(return_value, le->ptr, le_hello_person_persist);
efree(key);
return;
}
/* New person, allocate a structure */
person = pemalloc(sizeof(php_hello_person), 1);
person->name = pemalloc(name_len + 1, 1);
memcpy(person->name, name, name_len + 1);
person->name_len = name_len;
person->age = age;
ZEND_REGISTER_RESOURCE(return_value, person, le_hello_person_persist);
/* Store a reference in the persistence list */
new_le.ptr = person;
new_le.type = le_hello_person_persist;
zend_hash_add(&EG(persistent_list), key, key_len + 1, &new_le, sizeof(list_entry), NULL);
efree(key);
}
PHP_FUNCTION(hello_person_update_age)
{
php_hello_person *person;
zval *zperson;
long age;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &zperson, &age) == FAILURE) {
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE2(person, php_hello_person*, &zperson, -1, PHP_HELLO_PERSON_RES_NAME, le_hello_person, le_hello_person_persist);
person->age = age;
RETURN_TRUE;
}
PHP_FUNCTION(hello_person_get_age)
{
php_hello_person *person;
zval *zperson;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &zperson) == FAILURE) {
RETURN_FALSE;
}
ZEND_FETCH_RESOURCE2(person, php_hello_person*, &zperson, -1, PHP_HELLO_PERSON_RES_NAME, le_hello_person, le_hello_person_persist);
RETURN_LONG(person->age);
}


在上面的代码中:

hello_person_new 会返回一个non persistent resource

hello_person_pnew 会返回一个persistent resource

最后二个函数是我新增加的,主要是为了说明当一个资源变量的属性改变后,其他资源变量中的相应属性的变化情况。

函数hello_person_update_age完成对资源属性age进行修改,而hello_person_update_age则返回age的值。

对以上代码的编译及运行:

1)新建一个目录,比如hello,并在hello目录中创建上面提到的三个文件:config.m4, php_hello.h, hello.c

2)进入hello目录,运行phpize命令, 会得到以下的输出:

Configuring for:

PHP Api Version: 20041225

Zend Module Api No: 20060613

Zend Extension Api No: 220060519

不同的PHP版本,得到的输出也会不同。此命令会生成一些后继步骤所需的文件,如configure等

3)./configure –enable-hello

4)make

成功编译后,会在当前目录的modules/子目录中生成hello.so

5)sudo cp modules/hello.so /usr/lib/php5/20060613+lfs/

把这个hello.so文件,拷贝到其它php扩展所在的目录,我的机器上(所装系统为Ubuntu 10.04)所在的目录为:/usr/lib/php5/20060613+lfs/,在这个目录中可以看到其它的一些文件如mysql.so, xdebug.so等(如果安装了这些扩展的话)

6)创建配置文件hello.ini, 并包含以下一行内容:

extension=hello.so

然后把这个hello.ini放在php其它配置文件所在的目录,我的PC上所在的目录为:/etc/php5/conf.d/,在此目录中可以看到其它一些文件如mysql.ini, xdebug.ini等(如果安装了这些扩展的话)

注:根据不同的系统配置及PHP安装的不同情况,可以把hello.so及hello.ini放置在其它的目录中,或者不创建hello.ini而把extension=hello.so放在其它的配置文件中,但做完这些步骤后,都可以通过以下的方式来检验是否配置正确:

A)命令行php –ini的输出中应该包含所创建的hello.ini

B)命令行php –m会列出所有的扩展,输出中有一行应该为hello

C)命令行php –re hello可以列出扩展的信息,输出为这个hello扩展中的信息

Extension [ <persistent> extension #44 hello version 1.0 ] {

- Functions {

Function [ <internal:hello> function hello_person_new ] {

}

Function [ <internal:hello> function hello_person_pnew ] {

}

Function [ <internal:hello> function hello_person_get_age ] {

}

Function [ <internal:hello> function hello_person_update_age ] {

}

}

}

7)如果打算在网页中使用这些函数,并且是用Apache作为web server的话,则需要重启一下web server

sudo /etc/init.d/apache2 restart

一切完毕后,我们就可以来使用这些函数了,在任一个php文件中:

$p1 = hello_person_new("John", 18);

$p2 = hello_person_new("John", 18); //具有相同的参数

hello_person_update_age($p1, 19); //把$p1的age属性修改掉

printf("Age of p1 is %d/n", hello_person_get_age($p1));

printf("Age of p2 is %d/n", hello_person_get_age($p2));

运行后输出为:

Age of p1 is 19

Age of p2 is 18

可以看到,对于non persistent resource,当对$p1的内容进行修改后,$p2的内容保持不变

再对persistent resource进行尝试:

$pp1 = hello_person_pnew("John", 18);

$pp2 = hello_person_pnew("John", 18); //具有相同的参数

hello_person_update_age($pp1, 19); //把$pp1的age属性修改掉

printf("Age of pp1 is %d/n", hello_person_get_age($pp1));

printf("Age of pp2 is %d/n", hello_person_get_age($pp2));

输出为:

Age of pp1 is 19

Age of pp2 is 19

可以看到,当对$pp1的内容进行修改后,$pp2的内容也会被修改

我们再在hello.c的以下一些函数的入口把函数名打印到终端或者文件中来查看其调用顺序(在上面的代码中没有包含这些打印语句)

php_hello_person_persist_dtor

php_hello_person_dtor

hello_person_new

hello_person_pnew

看一下这些resouce的dtor函数是在什么时候调用的。

并用以下的PHP代码进行测试:

$p1 = hello_person_new("Tom", 18);

$pp1 = hello_person_pnew("John", 18);

unset($p1);

unset($pp1);

$p2 = hello_person_new("Tom", 18);

$pp2 = hello_person_pnew("John", 18);

unset($p2);

unset($pp2);

如果我们在命令行运行以上的代码,得到的输出为:

Enter into hello_person_new with name = Tom

Enter into hello_person_pnew with name = John

Enter into php_hello_person_dtor

Enter into hello_person_new with name = Tom

Enter into hello_person_pnew with name = John

Enter into php_hello_person_dtor

Enter into php_hello_person_persist_dtor

可以看到,对于non persistent resource, 每当对变量进行unset时,其dtor (destructor)函数就会被调用;而对于persistent resource,只有在命令行结束时才调用了一次,这是为什么呢?

而如果我们以Apache作为web server, 在网页中运行上述内容,得到的trace内容为:

Enter into hello_person_new with name = Tom

Enter into hello_person_pnew with name = John

Enter into php_hello_person_dtor

Enter into hello_person_new with name = Tom

Enter into hello_person_pnew with name = John

Enter into php_hello_person_dtor

在这里,destructor函数一次都没有运行,但当我们尝试停止web server(命令:sudo /etc/init.d/apache2 stop)之后,才得到以下的trace:

Enter into php_hello_person_persist_dtor

原来,对于persistent resource, 一当申请,只有在其所在的扩展(extension)服务停止时,才会被释放。而PHP命令行退出,或者Web Server服务停止,都会导致其所有的extension都停止服务。这也是把这类resource称为persistent的原因。

罗嗦了一大堆,总结一下:

1)non persistent resource在调用unset之后,其destructor函数就会被调用,资源被释放;

2)而对于persistent resource, 当对一个变量调用unset时,资源并没有被释放,而只有当该扩展服务停止时,destructor函数才会被调用,资源才会真正被释放,当然也可以专门在扩展中增加一个函数,以强制释放persistent resource;

3)对于同一类的persistent resource变量,真正的资源只申请了一份;比如在本文的例子中,php_hello_person只申请了一份;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: