EAPI

You are currently browsing articles tagged EAPI.

I previously discussed how to use extension api for PHP here. To illustrate the use of it, I will discuss how it can be implemented on exif and mbstring extensions. (Exif depends on mbstring and uses functions php_mb_check_encoding_list and php_mb_convert_encoding)

As also mentioned in the previous blog post, you can find the details about the status of the project here. The source code is available at the git repository : git://github.com/vpj/PHP-Extension-API.git, and some sample test code which uses the interface : git://github.com/vpj/PHP_EXT_API_Tests.git

How to register APIs – mbstring

You should create a structure with the API functions.

1
2
3
4
static struct {
	int (*check_encoding_list)(const char * TSRMLS_DC);
	char * (*convert_encoding)(const char *, size_t, const char *, const char *, size_t * TSRMLS_DC);
} mbstring_eapi = {php_mb_check_encoding_list, php_mb_convert_encoding};

I used a global variable for this.

Then, you should register the API during module initialization. That is, following piece of code should be included in MINIT.

1
zend_eapi_register("mbstring", "1.0", (void *)&mbstring_eapi, sizeof(mbstring_eapi));

Registering the API is pretty simple, but there might be some issues when using it in exif.

Setting up callback function – exif

First we should define the structure of the API.

1
2
3
4
5
6
typedef struct {
	int (*check_encoding_list)(const char * TSRMLS_DC);
	char * (*convert_encoding)(const char *, size_t, const char *, const char *, size_t * TSRMLS_DC);
} mbstring_eapi_struct;
 
static mbstring_eapi_struct *mbstring_eapi;

Here’s the callback function.

1
2
3
4
5
6
7
8
EAPI_CALLBACK_FUNCTION(mbstring_eapi_callback)
{
	TSRMLS_FETCH();
	mbstring_eapi = pemalloc(sizeof(mbstring_eapi_struct), 1);
 
	*mbstring_eapi = *((mbstring_eapi_struct*)api);
	REGISTER_INI_ENTRIES();
}

The callback function is named mbstring_eapi_callback. We are calling REGISTER_INI_ENTRIES from the callback, since OnUpdate event handlers for INI entries use check_encoding_list function. If INIs were registered from MINIT the event handlers will be executed before the extension api callback, so we are delaying the INI registration. Since REGISTER_INI_ENTRIES require TRSMLS we fetch those threading information using TSRMLS_FETCH() at the begining of the callback function.

The callback should be registered in MINIT.

1
2
EAPI_SET_CALLBACK("mbstring", "1.0", mbstring_eapi_callback);
mbstring_eapi = NULL;

We are left with changing the mbstring function calls. I’ll include the changed code in OnUpdateEncode; rest is similar.

1
2
3
4
5
6
7
8
9
10
11
ZEND_INI_MH(OnUpdateEncode)
{
	if(mbstring_eapi)
	{
		if (new_value && strlen(new_value) && !mbstring_eapi->check_encoding_list(new_value TSRMLS_CC)) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Illegal encoding ignored: '%s'", new_value);
			return FAILURE;
		}
	}
	return OnUpdateString(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC);
}

Tags: , , ,

This is a short tutorial on how to use the PHP: Abstract Extension API and Dependency Interface.

You can find details about he status of the project here. The source code is available at the git repository : git://github.com/vpj/PHP-Extension-API.git, and some sample code which uses the interface : git://github.com/vpj/PHP_EXT_API_Tests.git

How to register APIs

You should create a structure with the API functions.

1
2
3
4
5
6
struct _SAMPLE_EXT_API {
	int (*add)(int, int);
	int (*multiply)(int, int);
};
 
typedef struct _SAMPLE_EXT_API SAMPLE_EXT_API;

Then you should register the API during module initialization

1
zend_eapi_register("calculator", "1.0.0.0", (void *)&ext, sizeof(ext));

calculator is the name of the extension and 1.0.0.0 is the version in the major.minor[.build[.revision]] format. It is possible to register multiple APIs if there are multiple versions, to support backward compatibility. For example:

1
2
    zend_eapi_register("calculator", "1.0", (void *)&ext_old, sizeof(ext_old));
    zend_eapi_register("calculator", "1.1", (void *)&ext_new, sizeof(ext_new));

Getting the required APIs

APIs could be retrieved using a callback function or using zend_ext_api_get. Callbacks are called after module initialization but before RINIT. Callbacks should be used when the dependent extension requires some extension during module initialization. APIs cannot be retrieved during MINIT, since it will depend on the order in which APIs are registered. Therefore, the dependent extension could set up the callback function during MINIT and it will be called once all extensions are initialized.

Here’s an example of a callback function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef struct _SAMPLE_EXT_API {
	int (*add)(int, int);
	int (*multiply)(int, int);
} SAMPLE_EXT_API;
 
void my_callback(void *p_api, char *ext_name, uint version)
{
	char *version_text;
	SAMPLE_EXT_API *api = (SAMPLE_EXT_API *)p_api;
 
	zend_eapi_version_toa(version, &version_text);
	php_printf("API Callback: %s - %d\n", ext_name, version_text);
	php_printf("\tsum(243, 34) = %d\n", api->sum(243, 34));
}

Callback should be registered in MINIT

1
zend_eapi_set_callback("calculator", "1.0.0.0", my_callback);

Tags: , , ,