前言:
没有写过PHP相关的文章,今天是第一篇。写的不好的地方请见谅,多多批评。
PHP版本: php-7.1.23
文章涉及源码:
https://github.com/Diamonds-ZhaoYu/php_auto_function
php源码:
https://github.com/php/php-src
开篇:
本章内容实现的方法为4个,如图:
zhaoyu_set_autofunction: 设置自动加载方法;
zhaoyu_get_all_global_vars:获得运行时所有的全局变量,返回数组。
zhaoyu_get_all_classes: 获得运行时所有的类,返回数组。
zhaoyu_get_all_functions: 获得运行时所有的方法名,返回数组。
基础知识:
一.生成一个扩展:
首先我们要下载对应的php源码,先编译好对应的php。然后我们找到源码中的ext目录,ext目录下有一个ext_skel,这个文件是我们用于生成php扩展的脚骨架文件。
我们可以输入:
#./ext_skel -h
看一下对应的ext_skel的命令参数信息,我们使用--extname=module名就可以构建一个自己的扩展。
接下来我们输入:
#./ext_skel --extname=zhaoyu
我们来一下生成扩展的具体信息:
config.m4 和 config.w32用于编译使用。
zhaoyu.c是我们生成的php扩展的入口文件,内部包含了扩展的生命周期。
php_zhaoyu.h头文件主要用于我们的一些结构体定义和宏定义,骨架框架生成也包含了一些。
二.编译一个扩展:
我的php安装的目录:
/data/soft/php7.1.23/
执行phpize
#/data/soft/php7.1.23/bin/phpize
phpize是一个运行脚本,主要作用是检测php的环境还有就是在特定的目录生成相应的configure文件,这样make install之后,生产.so文件才会自动加载到php扩展下。
初始化编译配置
# cd /Users/tal/Desktop/code-source/php-7.1.23/ext/zhaoyu/
#./configure --with-php-config=/data/soft/php7.1.23/bin/php-config
初始化配置时,可能会看到一些初始化检查错误,这时可以根据对应数据的检测信息安装对应的支持库。
接下来编译扩展就可以:
#make
#make install
三.配置一个扩展:
复制一个php.ini文件:
#cp /Users/tal/Desktop/code-source/php-7.1.23/php.ini-development /data/soft/php7.1.23/lib/php.ini
/data/soft/php7.1.23/lib/目录在哪里找,可以通过
#/data/soft/php7.1.23/bin/php -ini |grep '(php.ini) Path'
查看php.ini的目录在什么地方。
然后就可以用vim打开/data/soft/php7.1.23/lib/php.ini在最底行加入:
extension=zhaoyu.so
四.EG和CG结构介绍:
EG宏对应的executor_globals,Zend执行器相关的全局变量。
Zend引擎在执行opcode的过程中,需要记录一些状态。如当前执行的类,加载了哪些文件。
CG宏对应的compiler_globals,Zend编译器相关的全局变量。
php在转换opcode的过程汇总需要保存一些信息,这些信息就保存在CG全局变量中。
/* Compiler */
#ifdef ZTS
# define CG(v) ZEND_TSRMG(compiler_globals_id, zend_compiler_globals *, v)
#else
# define CG(v) (compiler_globals.v)
extern ZEND_API struct _zend_compiler_globals compiler_globals;
#endif
ZEND_API int zendparse(void);
/* Executor */
#ifdef ZTS
# define EG(v) ZEND_TSRMG(executor_globals_id, zend_executor_globals *, v)
#else
# define EG(v) (executor_globals.v)
extern ZEND_API zend_executor_globals executor_globals;
#endif
zend_executor_globals 结构体
struct _zend_executor_globals {
zval uninitialized_zval;
zval error_zval;
/* 全局变量缓存 */
zend_array *symtable_cache[SYMTABLE_CACHE_SIZE];
zend_array **symtable_cache_limit;
zend_array **symtable_cache_ptr;
zend_array symbol_table; /* 全局变量hashtable */
HashTable included_files; /* 已经加载的文件 */
JMP_BUF *bailout;
int error_reporting;
int exit_status;
HashTable *function_table; /* 方法hashtable */
HashTable *class_table; /* 类hashtable */
HashTable *zend_constants; /* 常量hashtable */
...
void *reserved[ZEND_MAX_RESERVED_RESOURCES];
};
zend_compiler_globals 结构体
struct _zend_compiler_globals {
zend_stack loop_var_stack;
zend_class_entry *active_class_entry;
zend_string *compiled_filename;
int zend_lineno;
zend_op_array *active_op_array;
HashTable *function_table; /* function symbol table */
HashTable *class_table; /* class table */
HashTable filenames_table;
HashTable *auto_globals;
zend_bool parse_error;
zend_bool in_compilation;
zend_bool short_tags;
zend_bool unclean_shutdown;
zend_bool ini_parser_unbuffered_errors;
zend_llist open_files;
struct _zend_ini_parser_param *ini_parser_param;
uint32_t start_lineno;
zend_bool increment_lineno;
zend_string *doc_comment;
uint32_t extra_fn_flags;
uint32_t compiler_options; /* set of ZEND_COMPILE_* constants */
HashTable const_filenames;
zend_oparray_context context;
zend_file_context file_context;
zend_arena *arena;
zend_string *empty_string;
zend_string *one_char_string[256];
zend_string **known_strings;
uint32_t known_strings_count;
HashTable interned_strings;
const zend_encoding **script_encoding_list;
size_t script_encoding_list_size;
zend_bool multibyte;
zend_bool detect_unicode;
zend_bool encoding_declared;
zend_ast *ast;
zend_arena *ast_arena;
zend_stack delayed_oplines_stack;
#ifdef ZTS
zval **static_members_table;
int last_static_member;
#endif
};
源码解析:
zhaoyu_get_all_classes方法实现:
PHP_FUNCTION(zhaoyu_get_all_classes)
{
array_init(return_value);
int num = EG(class_table)->nNumOfElements;
for (int i = 0; i < num; i++)
{
Bucket *arr = (EG(class_table)->arData + i);
add_next_index_string(return_value,arr->key->val);
}
}
在这个方法是不是发现这个方法内没有返回值,其实return_value就是返回值。
我们来看看方法体返回值的宏,在zend_API.h
...
#define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b)
#define RETVAL_NULL() ZVAL_NULL(return_value)
#define RETVAL_LONG(l) ZVAL_LONG(return_value, l)
#define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d)
#define RETVAL_STR(s) ZVAL_STR(return_value, s)
#define RETVAL_INTERNED_STR(s) ZVAL_INTERNED_STR(return_value, s)
#define RETVAL_NEW_STR(s) ZVAL_NEW_STR(return_value, s)
#define RETVAL_STR_COPY(s) ZVAL_STR_COPY(return_value, s)
#define RETVAL_STRING(s) ZVAL_STRING(return_value, s)
#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l)
#define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value)
#define RETVAL_RES(r) ZVAL_RES(return_value, r)
#define RETVAL_ARR(r) ZVAL_ARR(return_value, r)
#define RETVAL_OBJ(r) ZVAL_OBJ(return_value, r)
#define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor)
#define RETVAL_FALSE ZVAL_FALSE(return_value)
#define RETVAL_TRUE ZVAL_TRUE(return_value)
#define RETURN_BOOL(b) { RETVAL_BOOL(b); return; }
#define RETURN_NULL() { RETVAL_NULL(); return;}
#define RETURN_LONG(l) { RETVAL_LONG(l); return; }
#define RETURN_DOUBLE(d) { RETVAL_DOUBLE(d); return; }
#define RETURN_STR(s) { RETVAL_STR(s); return; }
#define RETURN_INTERNED_STR(s) { RETVAL_INTERNED_STR(s); return; }
#define RETURN_NEW_STR(s) { RETVAL_NEW_STR(s); return; }
#define RETURN_STR_COPY(s) { RETVAL_STR_COPY(s); return; }
#define RETURN_STRING(s) { RETVAL_STRING(s); return; }
#define RETURN_STRINGL(s, l) { RETVAL_STRINGL(s, l); return; }
#define RETURN_EMPTY_STRING() { RETVAL_EMPTY_STRING(); return; }
#define RETURN_RES(r) { RETVAL_RES(r); return; }
#define RETURN_ARR(r) { RETVAL_ARR(r); return; }
#define RETURN_OBJ(r) { RETVAL_OBJ(r); return; }
#define RETURN_ZVAL(zv, copy, dtor) { RETVAL_ZVAL(zv, copy, dtor); return; }
#define RETURN_FALSE { RETVAL_FALSE; return; }
#define RETURN_TRUE { RETVAL_TRUE; return; }
...
可以看到我们的返回值其实是在设置return_value。那么我们看看return_value是从哪里来的。
可以看看PHP_FUNCTION(zhaoyu_get_all_classes)的实现:
对应宏解析出来为
void zif_zhaoyu_get_all_classes(zend_execute_data *execute_data, zval *return_value)
PHP_FUNCTION(zhaoyu_get_all_classes)对应的宏:
php.h
#define PHP_FN ZEND_FN
#define PHP_MN ZEND_MN
#define PHP_NAMED_FUNCTION ZEND_NAMED_FUNCTION
#define PHP_FUNCTION ZEND_FUNCTION
#define PHP_METHOD ZEND_METHOD
zend_API.h
#define ZEND_FN(name) zif_##name
#define ZEND_MN(name) zim_##name
#define ZEND_NAMED_FUNCTION(name) void name(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_FUNCTION(name) ZEND_NAMED_FUNCTION(ZEND_FN(name))
#define ZEND_METHOD(classname, name) ZEND_NAMED_FUNCTION(ZEND_MN(classname##_##name))
#define ZEND_FN(name) zif_##name
#define
zend.h
#define INTERNAL_FUNCTION_PARAMETERS zend_execute_data *execute_data, zval *return_value
接下来我们继续看int num = EG(class_table)->nNumOfElements; 这段代码其实是获取当前hash表已有的元素个数。
EG(class_table)对应的是一个HashTable结构,HashTable对应的是zend_array结构。
zend_types.h
typedef struct _Bucket {
zval val;
zend_ulong h; /* hash value (or numeric index) */
zend_string *key; /* string key or NULL for numerics */
} Bucket;
struct _zend_array {
zend_refcounted_h gc;
union {
struct {
ZEND_ENDIAN_LOHI_4(
zend_uchar flags,
zend_uchar nApplyCount,
zend_uchar nIteratorsCount,
zend_uchar reserve)
} v;
uint32_t flags;
} u; //主要还是起辅助作用,比如flags用来设置散列表的一些属性是否持久化,是否初始化等
uint32_t nTableMask; //哈希值计算掩码,等于nTableSize的负值(nTableMask = ~nTableSize + 1)
Bucket *arData; //存储元素数组,指向第一个Bucket
uint32_t nNumUsed; //已使用Bucket个数
uint32_t nNumOfElements; //哈希表已有元素个数
uint32_t nTableSize; //哈希表总大小,为2的n次方
uint32_t nInternalPointer; //当前遍历指针
zend_long nNextFreeElement; //下一个可用的数值索引,如:$arr[] = 1;$arr["a"] = 2;$arr[] = 3; 则nNextFreeElement = 2;
dtor_func_t pDestructor; //西沟函数,在删除或覆盖某个元素时,调用该函数,可以对旧元素进行清理
};
(EG(class_table)->arData + i); 是为了遍历数组中的数据,arData是用于存储元素数组,因为它在存取类信息时是顺序存储,所以我们通过指针+1就等于下一个数组的Bucket结构的数据。
而add_next_index_string(return_value,arr->key->val); 是将数组中的键值名存储在返回变量return_value中。
操作数组的函数:
zend_API.h
/** 通过key键值添加一个数组数据 */
ZEND_API int add_assoc_long_ex(zval *arg, const char *key, size_t key_len, zend_long n);
ZEND_API int add_assoc_null_ex(zval *arg, const char *key, size_t key_len);
ZEND_API int add_assoc_bool_ex(zval *arg, const char *key, size_t key_len, int b);
ZEND_API int add_assoc_resource_ex(zval *arg, const char *key, size_t key_len, zend_resource *r);
ZEND_API int add_assoc_double_ex(zval *arg, const char *key, size_t key_len, double d);
ZEND_API int add_assoc_str_ex(zval *arg, const char *key, size_t key_len, zend_string *str);
ZEND_API int add_assoc_string_ex(zval *arg, const char *key, size_t key_len, char *str);
ZEND_API int add_assoc_stringl_ex(zval *arg, const char *key, size_t key_len, char *str, size_t length);
ZEND_API int add_assoc_zval_ex(zval *arg, const char *key, size_t key_len, zval *value);
#define add_assoc_long(__arg, __key, __n) add_assoc_long_ex(__arg, __key, strlen(__key), __n)
#define add_assoc_null(__arg, __key) add_assoc_null_ex(__arg, __key, strlen(__key))
#define add_assoc_bool(__arg, __key, __b) add_assoc_bool_ex(__arg, __key, strlen(__key), __b)
#define add_assoc_resource(__arg, __key, __r) add_assoc_resource_ex(__arg, __key, strlen(__key), __r)
#define add_assoc_double(__arg, __key, __d) add_assoc_double_ex(__arg, __key, strlen(__key), __d)
#define add_assoc_str(__arg, __key, __str) add_assoc_str_ex(__arg, __key, strlen(__key), __str)
#define add_assoc_string(__arg, __key, __str) add_assoc_string_ex(__arg, __key, strlen(__key), __str)
#define add_assoc_stringl(__arg, __key, __str, __length) add_assoc_stringl_ex(__arg, __key, strlen(__key), __str, __length)
#define add_assoc_zval(__arg, __key, __value) add_assoc_zval_ex(__arg, __key, strlen(__key), __value)
/** 通过数组索引添加一个类型数组数据 */
ZEND_API int add_index_long(zval *arg, zend_ulong idx, zend_long n);
ZEND_API int add_index_null(zval *arg, zend_ulong idx);
ZEND_API int add_index_bool(zval *arg, zend_ulong idx, int b);
ZEND_API int add_index_resource(zval *arg, zend_ulong idx, zend_resource *r);
ZEND_API int add_index_double(zval *arg, zend_ulong idx, double d);
ZEND_API int add_index_str(zval *arg, zend_ulong idx, zend_string *str);
ZEND_API int add_index_string(zval *arg, zend_ulong idx, const char *str);
ZEND_API int add_index_stringl(zval *arg, zend_ulong idx, const char *str, size_t length);
ZEND_API int add_index_zval(zval *arg, zend_ulong index, zval *value);
/** 顺序添加一个类型数组数据 */
ZEND_API int add_next_index_long(zval *arg, zend_long n);
ZEND_API int add_next_index_null(zval *arg);
ZEND_API int add_next_index_bool(zval *arg, int b);
ZEND_API int add_next_index_resource(zval *arg, zend_resource *r);
ZEND_API int add_next_index_double(zval *arg, double d);
ZEND_API int add_next_index_str(zval *arg, zend_string *str);
ZEND_API int add_next_index_string(zval *arg, const char *str);
ZEND_API int add_next_index_stringl(zval *arg, const char *str, size_t length);
ZEND_API int add_next_index_zval(zval *arg, zval *value);
zhaoyu_set_autofunction方法实现:
PHP_FUNCTION(zhaoyu_set_autofunction)
{
zend_string *name;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC,"S", &name) == FAILURE) {
RETURN_NULL();
}
zend_function *spl_func_ptr = zend_hash_find_ptr(EG(function_table), name);
if (spl_func_ptr == NULL)
{
php_printf("not found %s function! \n" ,name);
zend_string_release(name);
RETURN_NULL();
}
EG(autoload_func) = spl_func_ptr;
zend_string_release(name);
RETURN_TRUE;
}