Jump to content


Photo

Calling extension function from an extension func


  • Please log in to reply
3 replies to this topic

#1 ede

ede
  • New Members
  • Pip
  • Newbie
  • 2 posts

Posted 05 March 2004 - 04:16 PM

Hello everyone!

Been wrestling with a problem for couple of days, and I seem not to get over it.

As I understand in PHP (4.3.4) there's no way to create a class chain where the parent class constructor is automatically called when a child class is created.

Thus a "kludge" with zif_parent as show below...
..register_internal_class etc.

//Parent class
ZEND_FUNCTION(Item)
{
//do something
add_property_string(...)
}

//child class extends Item
ZEND_FUNCTION(StringItem)
{
//...
zif_Item(INTERNAL_FUNCTION_PARAM_PASSTHRU);
add_property_string(...)
}


So now I have everything working... in PHP script side
$p=new Item();
//creates the parent, can access its methods and properties
$c=new StringItem();
//creates the child, CALLS the parent ctor
//can access BOTH the child and parent properties and methods..

Now if (well...when) I would like the parent constructor to have certain function arguments (some or many) (possibly) in no relation to the child's constructor what would be the step to go?

Say parent constructor takes arguments caption, link and image. The first two are mandatory, the last is optional (I've gone this far... ;-)

Now I would like to have the child ctor be called with one argument and it to call the parent constructor with proper arguments, how can I do that?
Ehm..there are many different child class types extending the parent. Like StringItem, DateItem etc.
(Let's not go into why this would have to be the case...it just is ;-)

$p=new Item("caption","link","image"); //this is the parent class
$c=new DateItem(..); //perhaps having more or less arguments, extends Item
$c=new GaugeItem(..); //perhaps having more or less arguments, extends Item

Since there's no way to easily add more function arguments to the zif_Item() call, how can I achieve this at all?
(Well I could create a generic init function in C-side which Item ctor calls to setup properties likewise the child classes then, but its not the same thing ;-)

I've burned my head off with the call_user_function_ex, I just can't it call other the userland functions. Perhaps one cannot call an extension(my own module) functions with it? Or then the function naming is something I didn't just try...

I've tried to twiddle with the argument stack(code spied from zend_execute_API.c - the details which have overall understanding only):
zend_ptr_stack_push(&EG(argument_stack),caption);
zend_ptr_stack_push(&EG(argument_stack),link);
zend_ptr_stack_push(&EG(argument_stack),image);
long param_count=3;
zend_ptr_stack_n_push(&EG(argument_stack),2,(void*)(long)param_count,NULL);
zif_Item(INTERNAL_FUNCTION_PARAM_PASSTHRU);

But alas, the original functions are passed, not the ones I just added.

I tried to see if any other ext's might have this solved, but didn't find a proper match from those either...

Apparently there's no documentation that explains this either.

*sigh* Hopefully some of you know the deal, before I just have to load up the ddd and start stepping thru the extension line by line ;-)




#2 ede

ede
  • New Members
  • Pip
  • Newbie
  • 2 posts

Posted 05 March 2004 - 07:16 PM

*must be too tired* I take one half back about earlier post..call_user_func_ex works perfectly with internal (module/extension) functions.

Still would like to see if someone can get up a simple explanation about the use of argument_stack and calling the internal function directly zif_Item(...)

#3 micha

micha
  • New Members
  • Pip
  • Newbie
  • 4 posts
  • LocationGermany

Posted 18 April 2004 - 09:13 PM

Hi ede,

I have nearly the same problem for a while now, in fact not calling own class functions but functions from extensions, e.g. mysql_connect().
There's little to nothing on documentation and the source does have all but comments ;)
I'm porting an object orientated database abstraction layer written in php to a C extension.
I was searching for the best way to call functions from already existing database extensions like mysql, etc. As I got nothing else to work than call_user_function_ex I used this until I was able to do basic benchmarks. Well, calling internal functions with call_user_function_ex is nearly as slow as executing the php version itself...

So I was searching for a better way again and came across your post and zend_ptr_stack_push.
I tried this before, but missed the call to zend_ptr_stack_n_push.
With this call it seems to work now, here's an example for mysql_escape_string():

ZEND_API zval *ext_mysql_escape_string(zval *str TSRMLS_DC)
{
  zval *retval;
  long param_count=1;
  MAKE_STD_ZVAL(retval);

  zend_ptr_stack_push(&EG(argument_stack), str);
  zend_ptr_stack_n_push(&EG(argument_stack),2,(void*)(long)param_count,NULL);
 
  zif_mysql_escape_string(param_count, retval, NULL, 1 TSRMLS_CC);
  
  if (Z_TYPE_P(retval) != IS_STRING) {
    zval_ptr_dtor(&retval);
    return NULL;
  }
  
  return retval;
}


Anyway, I do not know if this is the right way...

Your problem with calling the parent constructor I solved as follows:

[i]Parent class:[/i]
ZEND_FUNCTION(Item)
{
   php_Item(getThis() TSRMLS_CC);
}

ZEND_API void php_Item(zval *this_ptr TSRMLS_DC)
{
  if (Z_TYPE_P(this_ptr) != IS_OBJECT) {
    object_init_ex(this_ptr, Item_ce_ptr);
  }

  add_property_string(...)
  ...
}

[i]Child class:[/i]
ZEND_FUNCTION(StringItem)
{
//...
  php_StringItem(getThis() TSRMLS_CC);
}

ZEND_API void php_StringItem(zval *this_ptr TSRMLS_DC)
{
  object_init_ex(this_ptr, StringItem_ce_ptr);
  php_Item(this_ptr TSRMLS_CC);
  add_property_string(...)
}

Hope this helps.

micha

#4 micha

micha
  • New Members
  • Pip
  • Newbie
  • 4 posts
  • LocationGermany

Posted 19 April 2004 - 04:38 PM

Anyway, I do not know if this is the right way...

Well, it's not.
I compiled the code with a debug enabled php version today and got dozens of memory leaks...
Therefore I took the call_user_function_ex function and removed all not needed for calling an
internal function.

int call_internal_function(zend_function *function, zval **retval_ptr_ptr, int param_count, zval **params[] TSRMLS_DC)
{
  zval **original_return_value;
  zend_function_state *original_function_state_ptr;
  zend_execute_data execute_data;
  zend_op temp_op;
  int i;

  if (! function || function->type != ZEND_INTERNAL_FUNCTION) {
    return FAILURE;
  }
  
  /* Initialize execute_data */
  EX(fbc) = NULL;
  EX(object).ptr = NULL;
  EX(ce) = NULL;
  EX(Ts) = NULL;
  EX(op_array) = NULL;
  EX(opline) = NULL;
  EX(function_state).function = function;
  
	*retval_ptr_ptr = NULL;
	original_function_state_ptr = EG(function_state_ptr);
  
	for (i=0; i < param_count; i++) 
  {
  zval *param;

  if (EX(function_state).function->internal_function.arg_types
  	&& i < EX(function_state).function->internal_function.arg_types[0]
  	&& EX(function_state).function->internal_function.arg_types[i+1]==BYREF_FORCE
  	&& !PZVAL_IS_REF(*params[i])) {
  	if ((*params[i])->refcount>1) {
    zval *new_zval;

    ALLOC_ZVAL(new_zval);
    *new_zval = **params[i];
    zval_copy_ctor(new_zval);
    new_zval->refcount = 1;
    (*params[i])->refcount--;
    *params[i] = new_zval;
  	}
  	(*params[i])->refcount++;
  	(*params[i])->is_ref = 1;
  	param = *params[i];
  } else if (*params[i] != &EG(uninitialized_zval)) {
  	(*params[i])->refcount++;
  	param = *params[i];
  } else {
  	ALLOC_ZVAL(param);
  	*param = **(params[i]);
  	INIT_PZVAL(param);
  }
  zend_ptr_stack_push(&EG(argument_stack), param);
	}

	zend_ptr_stack_n_push(&EG(argument_stack), 2, (void *) (long) param_count, NULL);

	EG(function_state_ptr) = &EX(function_state);

	EX(prev_execute_data) = EG(current_execute_data);
	EG(current_execute_data) = &execute_data;

  ALLOC_INIT_ZVAL(*retval_ptr_ptr);
  temp_op.extended_value = param_count;
  temp_op.result.u.var = 0;
  temp_op.lineno = 0;

  EX(opline) = &temp_op;
  EX(Ts) = (temp_variable *) do_alloca(sizeof(temp_variable)*1);
  EX(Ts)[EX(opline)->result.u.var].var.ptr = *retval_ptr_ptr;

  if (!zend_execute_internal) {
    ((zend_internal_function *) EX(function_state).function)->handler(EX(opline)->extended_value, EX(Ts)[EX(opline)->result.u.var].var.ptr, EX(object).ptr, 1 TSRMLS_CC);
  } else {
    zend_execute_internal(&execute_data, 1 TSRMLS_CC);
  }
  
  INIT_PZVAL(*retval_ptr_ptr);
  free_alloca(EX(Ts));

  zend_ptr_stack_clear_multiple(TSRMLS_C);
  EG(function_state_ptr) = original_function_state_ptr;
  EG(current_execute_data) = EX(prev_execute_data);

  return SUCCESS;
}

The example with mysql_escape_string now looks as follows:

ZEND_API zval *ext_mysql_escape_string(zval *str TSRMLS_DC)
{
  zval *retval, **args[1];
  zend_function function;
     
  /* initialize internal function */
  function.internal_function.type = ZEND_INTERNAL_FUNCTION;
  function.internal_function.arg_types = NULL;
  function.internal_function.function_name = "mysql_escape_string";
  function.internal_function.handler = zif_mysql_escape_string;
  
  args[0] =& str;
  
  call_internal_function(&function, &retval, 1, args TSRMLS_CC);
  
  return retval;
}

No memory leaks are reported this way, so I hope it's ok now.
Btw, take into account that if the internal function requires a parameter passed by reference,
you need to adapt function.internal_function.arg_types so that call_internal_function is able to
separate the zval.
Example with a function that takes one argument :

  unsigned char args[] = {1, BYREF_FORCE};

  /* initialize internal function */
  function.internal_function.type = ZEND_INTERNAL_FUNCTION;
  function.internal_function.arg_types = args;
  function.internal_function.function_name = "some_function_name";
  function.internal_function.handler = zif_some_function;

Please correct me if I'm wrong !

micha




0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users