PHP扩展之冲突、依赖及加载顺序


#1

PHP扩展加载顺序


在PHP开发中使用扩展会大大提高编码和运行效率,但在使用过程中(尤其是一些开源扩展)难免遇到各种各样的问题,其中一个就是模块间冲突或依赖

一般来讲,比较好的扩展会提示你跟它冲突或有依赖的模块,运行php -m会有类似下面的这个提示

说明扩展testso所依赖的apm.so并没有被安装

如果我们继续深入了解这种功能如何实现的,那么就要研究一下该扩展的源码。其实做到这点并不复杂,也不需要太多的扩展开发知识,只需要了解几个结构体的相关功能即可

首先,每个扩展都会声明一个zend_module_entry结构的扩展说明模块,告诉Zend所有该模块相关信息。自然也包含所依赖和冲突的模块。当依赖关系出错的时候Zend就会报错,给出详细的出错信息,并停止运行

struct _zend_module_entry {
	unsigned short size;
	unsigned int zend_api;
	unsigned char zend_debug;
	unsigned char zts;
	const struct _zend_ini_entry *ini_entry;
	const struct _zend_module_dep *deps;         //我们要看的就是这个属性
	const char *name;
	const struct _zend_function_entry *functions;
	int (*module_startup_func)(INIT_FUNC_ARGS);
	int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS);
	int (*request_startup_func)(INIT_FUNC_ARGS);
	int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS);
	void (*info_func)(ZEND_MODULE_INFO_FUNC_ARGS);
	const char *version;
	size_t globals_size;
#ifdef ZTS
	ts_rsrc_id* globals_id_ptr;
#else
	void* globals_ptr;
#endif
	void (*globals_ctor)(void *global TSRMLS_DC);
	void (*globals_dtor)(void *global TSRMLS_DC);
	int (*post_deactivate_func)(void);
	int module_started;
	unsigned char type;
	void *handle;
	int module_number;
	const char *build_id;
};

deps就是用来注册依赖、冲突模块的,它的结构zend_module_dep如下:

struct _zend_module_dep {
	const char *name;		/* module name */
	const char *rel;		        /* version relationship: NULL (exists), lt|le|eq|ge|gt (to given version) */
	const char *version;	        /* version */
	unsigned char type;		/* dependency type */
};

我们可以通过研究代码及注释来了解这个结构体的使用方法,不过还有更好的方法,原来PHP贴心的提供了几个宏来完成zend_module_dep的填写:

ZEND_MOD_REQUIRED
ZEND_MOD_CONFLICTS
ZEND_MOD_OPTIONAL

比如在刚开始说到的testso中,我就是这么写的:

static const zend_module_dep test_deps[] = {
        ZEND_MOD_REQUIRED("apm")
        ZEND_MOD_END
};

zend_module_entry testso_module_entry = {
        STANDARD_MODULE_HEADER_EX,
        NULL,
        test_deps,                            //zend_module_dep
        "testso",
        testso_functions,
        PHP_MINIT(testso),
        PHP_MSHUTDOWN(testso),
        PHP_RINIT(testso),              
        PHP_RSHUTDOWN(testso), 
        PHP_MINFO(testso),
        PHP_TESTSO_VERSION,
        STANDARD_MODULE_PROPERTIES
};

至于第三个宏ZEND_MOD_OPTIONAL怎么用,大家有兴趣可以自行研究源码。在PHP这种开源项目中,源码本身就向你说明了一切

最后再讲一下扩展加载顺序的问题,这个规则更简单,只有一条:写在php.ini中的扩展按从上到下的顺序加载

比如在上面的情况中,testso的顺序就在apm之前。但是尽管放心,扩展依赖并不取决于加载顺序,所以把apm放在后面,PHP仍然可以正常执行


#2

写的很好,点赞.....