note 11945 deleted from phpdevel by felipe
| From: | felipe@php.net | Date: | Tue, 22 Jul 2008 19:00:28 +0000 |
| Subject: | note 11945 deleted from phpdevel by felipe | ||
| References: | 1 | Groups: | php.notes |
| Request: | Send a blank email to php-notes+get-142540@lists.php.net to get a copy of this message | ||
Note Submitter: ale at tana dot it
----
PORTING CUSTOM C FUNCTION FROM PHP3 TO PHP4
And I mean just functions, not full-fledged php extension. I had a couple
of them, working with PHP3, and I have ported them in PHP4. Here's how.
1) Makefile
you should have CFLAGS something like
-I/path/to/php/php-4.0.4pl1/Zend
-I/path/to/php/php-4.0.4pl1
-I/path/to/php/php-4.0.4pl1/TSRM
-I/path/to/php/php-4.0.4pl1/main
-fPIC
-DCOMPILE_DL=1
Only the include paths changed from PHP3.
2) Interface with PHP
the php3_module_entry is now called zend_module_entry and has
the same structure. The get-module function from which you used to
return that value is not DLEXPORT, but ZEND_DLEXPORT. You
may want to use the ZEND_GET_MODULE macro.
For the function_entry, you may want to use the PHP_FE macros,
but I found the PHP_NAMED_FUNCTION implied less changes.
It goes as
PHP_NAMED_FUNCTION(arrayret); /* declaration */
and
/* in php3 was
DLEXPORT void arrayret(INTERNAL_FUNCTION_PARAMETERS) */
PHP_NAMED_FUNCTION(arrayret) /* definition */
{
pval **arg;
char buf[512];
int ok = 0, i = -1;
if (ZEND_NUM_ARGS() != 1 ||
zend_get_parameters_ex(1, &arg) != SUCCESS ||
(*arg)->type != IS_STRING ||
(*arg)->value.str.val == NULL)
{
WRONG_PARAM_COUNT;
}
...
You've seen that *arg becomes **arg. The ..._ex() function will take
the address of a pointer to pointer. ZEND_NUM_ARGS doesn't take
any parameter (used to be ht).
3) Includes
#include <php.h>
#include <zend_globals.h>
4) Insert indirection
One difficult point is the extra level of indirection that was introduced
for reference counting. If you don't use complex data types your C
function should now work. If you use, e.g., arrays, keep that level
of indirection in mind: don't store a pval into an array, store a pointer.
I had things as
if (array_init(return_value) == SUCCESS)
{
ok = 1;
for (i = 0; i < 100 && ok; ++i) /* insert 100 arrays in return value */
{
pval entry;
char buf[80];
if ((ok = array_init(&entry) == SUCCESS) != 0)
{
static char const entry_1[] = "one";
static char const entry_2[] = "two";
pval e1, e2;
e1.value.str.val = e2.value.str.val = NULL;
e1.type = e2.type = IS_STRING;
e1.value.str.len = sprintf(buf, "%3d/%010u", i, i);
e1.value.str.val = estrndup(buf, e1.value.str.len);
ok = e1.value.str.val != NULL &&
_php3_hash_update(entry.value.ht, (char*)entry_1,
sizeof entry_1, &e1, sizeof e1, NULL) == SUCCESS;
if (ok) /* continue assigning a second string, etc... */
...
This stuff compiles due to some compatibilty macro definitions, but
it doesn't work. The quickest way around is to insert an intermediate
function. To make the code above work in PHP4 i preceded it with
the following:
#undef _php3_hash_update
#define _php3_hash_update php4_hash_update_patch
static int php4_hash_update_patch(HashTable *ht,
char *arKey, uint nKeyLength,
void *pData, uint nDataSize, void **pDest)
{
zval *tmp;
int rtc;
if (nDataSize != sizeof(zval)) /* check it was a pval */
return FAILURE;
ALLOC_ZVAL(tmp); /* you need zend_globals.h for this */
*tmp = *(zval*)pData;
tmp->is_ref = 0;
tmp->refcount = 1;
if ((rtc = zend_hash_update(ht,
arKey, nKeyLength,
(void*)&tmp, sizeof(zval*), pDest)) == FAILURE)
{
FREE_ZVAL(tmp);
}
return rtc;
}
Similar patches can be done for _php3_hash_next_index_insert,
etcetera. You got the idea.
I hope this may save some time to an occasional code porter,
keep in mind I'm not a zend (sic) guru, I just crashed my head
quite hardly before realizing what was going wrong.
And
5) good luck!