3

I'm just experimenting with a PHP extension and I would like to know what is the suggested/preferred way to call an object constructor within the extension. I've read that, by calling the object_init_ex function the constructor of that object is not automatically called. This seems true also from the tests I've made. Let us say that I have the following code where 'Person' is a valid class name:

zend_class_entry *class_entry = NULL;  
zend_string *class_name = zend_string_init("Person", sizeof("Person") - 1, false);

class_entry = zend_lookup_class(class_name);

if (class_entry != NULL) {
  object_init_ex(return_value, class_entry);    
  /* call the Person::_construct method */
} else {
  RETURN_NULL();
}  

How can I call the constructor after the object_init_ext call? Also, will there be any differences between php 5 and php 7 in this regard?

5
  • 1
    @Mike The duplicate is about pure PHP, this question is about PHP extensions, written in C. Commented Sep 6, 2016 at 21:01
  • @CharlotteDunois Good point. I've retracted my vote. Commented Sep 6, 2016 at 21:07
  • It's too late for me to write this as an answer - but I think you want to duplicate the functionality in ReflectionClass::newInstance - php-lxr.adamharvey.name/source/xref/master/ext/reflection/… Commented Sep 6, 2016 at 21:28
  • Oh, and this is where having constructors be callables, would be rather useful grumble grumble. Commented Sep 6, 2016 at 21:29
  • 1
    @Danack Or rather, what would be usable is a helper that directly calls a zend_function without requiring manual fci/fcc setup. It's weird that doing a call when you already know exactly what needs to be called is harder than doing a callable lookup... Commented Sep 6, 2016 at 22:38

1 Answer 1

5

There's two things you need to do: Fetch the constructor and then call it. The first part is easily done: You simply need to call the get_constructor() handler of the object:

zend_function *ctor = Z_OBJ_HT_P(obj)->get_constructor(Z_OBJ_P(obj));

Next, you need to call this function. Interestingly, there is no easy way to do this in the PHP API, because the usual call helpers deal with calling something by name (or by callable), rather than calling a function pointer directly. This means that you're required to manually initialize an fcall_info entry and fcall_info_cache. I'll provide a general purpose function here:

int call_function_by_ptr(zend_function *fbc, zend_object *obj, zval *retval, uint32_t num_params, zval *params) {
    zend_fcall_info fci;
    zend_fcall_info_cache fcc;

    fci.size = sizeof(fci);
    fci.object = obj;
    fci.retval = retval;
    fci.param_count = num_params;
    fci.params = params;
    fci.no_separation = 1;          // Don't allow creating references into params
    ZVAL_UNDEF(&fci.function_name); // Unused if fcc is provided

    fcc.initialized = 1;
    fcc.function_handler = fbc;
    fcc.calling_scope = NULL;       // Appears to be dead
    fcc.called_scope = obj ? obj->ce : fbc->common.scope;
    fcc.object = obj;

    return zend_call_function(&fci, &fcc);
}

Assuming your constructor has no arguments, the actual call would then look something like this:

zval retval;
int result = call_function_by_ptr(ctor, Z_OBJ_P(obj), &retval, 0, NULL);
if (result == FAILURE || Z_ISUNDEF(retval)) {
    // Error
} else {
    // Success
}
zval_ptr_dtor(&retval);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.