现在有一个简单的session类,来实现项目中session的统一管理.此session类可以设置session变量名前缀,使用魔术方法来注册和检查session变量,使用起来非常方便。代码如下:
<?php /* author:dengqiang */ class session { private $prefix; public function __construct($prefix) { @session_start (); $this->prefix = $prefix; } public function setPrefix() { return $this->prefix; } public function getPrefix() { return $this->prefix; } public function delete($name) { if (isset ( $_SESSION [$this->prefix . $name] )) { unset ( $_SESSION [$this->prefix . $name] ); } } public function __set($name, $value) { $_SESSION [$this->prefix . $name] = $value; } public function __get($name) { if (isset ( $_SESSION [$this->prefix . $name] )) { return $_SESSION [$this->prefix . $name]; } else { return null; } } public function isset($name){ return isset($_SESSION [$this->prefix . $name]); } public function unset($name){ unset($_SESSION [$this->prefix . $name]); return true; } } ?>现在我们将此session类开发成php扩展类,类名为Mysession。使用到的相关ZEND宏和PHPAPI如下:
在php_mysession.h声明Mysession类和方法
PHP_METHOD(Mysession, __construct); PHP_METHOD(Mysession, setPrefix); PHP_METHOD(Mysession, getPrefix); PHP_METHOD(Mysession, delete); PHP_METHOD(Mysession, isset); PHP_METHOD(Mysession, unset); PHP_METHOD(Mysession, __set); PHP_METHOD(Mysession, __get);在mysession.c中实现Mysession类扩展
#include "ext/session/php_session.h" #define IF_SESSION_VARS() \ if (PS(http_session_vars) && PS(http_session_vars)->type == IS_ARRAY)声明方法中的参数
ZEND_BEGIN_ARG_INFO(arg_construct,1) ZEND_ARG_INFO(0, prefix) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arg_setPrefix,1) ZEND_ARG_INFO(0, prefix) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arg_set,2) ZEND_ARG_INFO(0, name) ZEND_ARG_INFO(0, value) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arg_get,1) ZEND_ARG_INFO(0, name) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arg_isset,1) ZEND_ARG_INFO(0, name) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO(arg_unset,1) ZEND_ARG_INFO(0, name) ZEND_END_ARG_INFO()
将Mysession类注册到zend内部
static zend_function_entry Mysession_class_functions[] = { PHP_ME(Mysession, __construct , arg_construct, ZEND_ACC_CTOR | ZEND_ACC_PUBLIC) /* For testing, remove later. */ PHP_ME(Mysession, setPrefix , arg_setPrefix, ZEND_ACC_PUBLIC) /* For testing, remove later. */ PHP_ME(Mysession, getPrefix ,NULL, ZEND_ACC_PUBLIC) /* For testing, remove later. */ PHP_ME(Mysession, delete ,NULL, ZEND_ACC_PUBLIC) /* For testing, remove later. */ PHP_ME(Mysession, isset ,arg_isset, ZEND_ACC_PUBLIC) /* For testing, remove later. */ PHP_ME(Mysession, unset ,arg_unset, ZEND_ACC_PUBLIC) /* For testing, remove later. */ PHP_ME(Mysession, __set , arg_set, ZEND_ACC_PUBLIC) /* For testing, remove later. */ PHP_ME(Mysession, __get ,arg_get, ZEND_ACC_PUBLIC) /* For testing, remove later. */ {NULL, NULL, NULL} }; zend_class_entry *Mysession_ce; PHP_MINIT_FUNCTION(mysession) { /* If you have INI entries, uncomment these lines REGISTER_INI_ENTRIES(); */ zend_class_entry Mysession; INIT_CLASS_ENTRY(Mysession, "Mysession", Mysession_class_functions); Mysession_ce = zend_register_internal_class_ex(&Mysession, NULL, NULL TSRMLS_CC); zend_declare_property_null(Mysession_ce, ZEND_STRL("_prefix"), ZEND_ACC_PRIVATE TSRMLS_CC); //声明session前缀私有变量 return SUCCESS; }
PHP_MINFO_FUNCTION(mysession) { php_info_print_table_start(); php_info_print_table_header(2, "mysession support", "enabled"); php_info_print_table_row(2, "author", "Dengqiang"); php_info_print_table_row(2, "version", "1.0"); php_info_print_table_end(); /* Remove comments if you have entries in php.ini DISPLAY_INI_ENTRIES(); */ }实现Mysession类中方法
PHP_METHOD(Mysession, __construct) { char *prefix=""; int prefix_len=0; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &prefix, &prefix_len) == FAILURE){ return; } if(ZEND_NUM_ARGS()>0){ zend_update_property_string(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_prefix"), prefix TSRMLS_CC); } if (PS(session_status) != php_session_active && PS(session_status) != php_session_disabled) { php_session_start(TSRMLS_C); } RETURN_TRUE; } PHP_METHOD(Mysession, setPrefix) { char *prefix; int prefix_len=0; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &prefix, &prefix_len) == FAILURE){ return; } if(prefix_len>0){ zend_update_property_string(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_prefix"), prefix TSRMLS_CC); RETURN_TRUE; } RETURN_FALSE; } PHP_METHOD(Mysession, getPrefix) { zval *zPrefix; char *prefix; zPrefix = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_prefix"), 0 TSRMLS_CC); prefix = Z_STRVAL_P(zPrefix); if(prefix != NULL){ RETURN_STRINGL(prefix, strlen(prefix), 0); } RETURN_NULL(); } PHP_METHOD(Mysession, delete) { char *p_name; int p_name_len; char *session_name; int session_name_len; zval *zPrefix; char *prefix; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p_name, &p_name_len) == FAILURE) { return; } if (PS(session_status) == php_session_none) { RETURN_FALSE; } zPrefix = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_prefix"), 0 TSRMLS_CC); prefix = Z_STRVAL_P(zPrefix) == NULL ? "":Z_STRVAL_P(zPrefix); session_name_len = spprintf(&session_name, 0, "%s%s", prefix, p_name); IF_SESSION_VARS() { SEPARATE_ZVAL_IF_NOT_REF(&PS(http_session_vars)); PS_DEL_VARL(session_name, session_name_len); } RETURN_TRUE; } PHP_METHOD(Mysession, isset) { zval *p_var; char *p_name; int p_name_len; char *session_name; int session_name_len; zval *zPrefix; char *prefix; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &p_name, &p_name_len) == FAILURE) { return; } if (PS(session_status) == php_session_none) { RETURN_FALSE; } zPrefix = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_prefix"), 0 TSRMLS_CC); prefix = Z_STRVAL_P(zPrefix); session_name_len = spprintf(&session_name, 0, "%s%s", prefix, p_name); IF_SESSION_VARS() { if (zend_hash_find(Z_ARRVAL_P(PS(http_session_vars)), session_name, session_name_len+1, (void **)&p_var) == SUCCESS) { RETURN_TRUE; } } RETURN_FALSE; } PHP_METHOD(Mysession, unset) { if (PS(session_status) == php_session_none) { RETURN_FALSE; } IF_SESSION_VARS() { HashTable *ht_sess_var; SEPARATE_ZVAL_IF_NOT_REF(&PS(http_session_vars)); ht_sess_var = Z_ARRVAL_P(PS(http_session_vars)); if (PG(register_globals)) { uint str_len; char *str; ulong num_key; HashPosition pos; zend_hash_internal_pointer_reset_ex(ht_sess_var, &pos); while (zend_hash_get_current_key_ex(ht_sess_var, &str, &str_len, &num_key, 0, &pos) == HASH_KEY_IS_STRING) { zend_delete_global_variable(str, str_len - 1 TSRMLS_CC); zend_hash_move_forward_ex(ht_sess_var, &pos); } } /* Clean $_SESSION. */ zend_hash_clean(ht_sess_var); } } PHP_METHOD(Mysession, __set) { zval *value = NULL; char *name; char *session_name; int name_len,session_name_len; zval *zPrefix; char *prefix; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sz", &name, &name_len, &value) == FAILURE) { return; } if (PS(session_status) == php_session_none || PS(session_status) == php_session_disabled) { RETURN_FALSE; } zPrefix = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_prefix"), 0 TSRMLS_CC); prefix = Z_STRVAL_P(zPrefix) == NULL ? "":Z_STRVAL_P(zPrefix); session_name_len = spprintf(&session_name, 0, "%s%s", prefix, name); IF_SESSION_VARS() { SEPARATE_ZVAL_IF_NOT_REF(&PS(http_session_vars)); PS_DEL_VARL(session_name, session_name_len); } php_set_session_var(session_name, session_name_len, value, NULL TSRMLS_CC); PS_ADD_VARL(session_name, session_name_len); } PHP_METHOD(Mysession, __get) { zval **p_var; char *name; char *session_name; int name_len,session_name_len; zval *zPrefix; char *prefix; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) { return; } if (PS(session_status) == php_session_none || PS(session_status) == php_session_disabled) { RETURN_FALSE; } zPrefix = zend_read_property(Z_OBJCE_P(getThis()), getThis(), ZEND_STRL("_prefix"), 0 TSRMLS_CC); prefix = Z_STRVAL_P(zPrefix) == NULL ? "":Z_STRVAL_P(zPrefix); session_name_len = spprintf(&session_name, 0, "%s%s", prefix, name); IF_SESSION_VARS() { if (php_get_session_var(session_name, session_name_len, &p_var) == SUCCESS) { *return_value = **p_var; } } }
编译安装后重启php-fpm测试
<?php $Mysession = new Mysession('test'); $Mysession->var1 = "string"; var_dump($Mysession->isset('var1')); echo"<br>"; var_dump($Mysession->var1); echo"<br>"; $Mysession->var2 = array("a"=>'kingsoft', "b"=>"dengqiang"); var_dump($Mysession->isset('var2')); echo"<br>"; var_dump($Mysession->var2); echo"<br>"; $Mysession->delete('var1'); var_dump($Mysession->var1); echo"<br>"; $Mysession->unset(); var_dump($Mysession->var2);
结果:
整个Mysession类扩展已开发完毕,我们可以发现PHP扩展类和php程序自定义类是非常相似的。以魔术方法__set为例,在php程序中,它会将未定义的属性和值解析为第一个和第二个参数,在PHP扩展中__set方法同样是接收属性名称和属性值两个参数。还一点就是在扩展中构造方法开启一次session后在其他方法中无需再次开启,这与php程序中自定义Mysession类也是一致的。
不管是php中自定义类还是PHP内核扩展类,他们的工作机制都是一样的,php自定义类可以理解为PHP内核扩展类再次封装了一层,PHP内核扩展的意义也是少做一次php代码的解析。