optimize frequent method calls
authorMichael Wallner <mike@php.net>
Fri, 6 Feb 2015 14:32:57 +0000 (15:32 +0100)
committerMichael Wallner <mike@php.net>
Fri, 6 Feb 2015 14:32:57 +0000 (15:32 +0100)
php_http_client.c
php_http_client.h
php_http_message.c
php_http_object.c
php_http_object.h

index 2b4a31d..3050bd8 100644 (file)
@@ -329,6 +329,8 @@ void php_http_client_object_free(void *object TSRMLS_DC)
        php_http_client_object_t *o = (php_http_client_object_t *) object;
 
        php_http_client_free(&o->client);
+       php_http_object_method_dtor(&o->notify);
+       php_http_object_method_free(&o->update);
        zend_object_std_dtor((zend_object *) o TSRMLS_CC);
        efree(o);
 }
@@ -457,14 +459,18 @@ static STATUS handle_response(void *arg, php_http_client_t *client, php_http_cli
 
 static void handle_progress(void *arg, php_http_client_t *client, php_http_client_enqueue_t *e, php_http_client_progress_state_t *progress)
 {
-       zval *zrequest, *zprogress, *retval = NULL, *zclient;
+       zval *zrequest, *zprogress, *zclient, **args[2];
+       php_http_client_object_t *client_obj = arg;
        zend_error_handling zeh;
        TSRMLS_FETCH_FROM_CTX(client->ts);
 
        MAKE_STD_ZVAL(zclient);
-       ZVAL_OBJVAL(zclient, ((php_http_client_object_t *) arg)->zv, 1);
+       ZVAL_OBJVAL(zclient, client_obj->zv, 1);
+
        MAKE_STD_ZVAL(zrequest);
        ZVAL_OBJVAL(zrequest, ((php_http_message_object_t *) e->opaque)->zv, 1);
+       args[0] = &zrequest;
+
        MAKE_STD_ZVAL(zprogress);
        object_init(zprogress);
        add_property_bool(zprogress, "started", progress->started);
@@ -474,15 +480,15 @@ static void handle_progress(void *arg, php_http_client_t *client, php_http_clien
        add_property_double(zprogress, "dlnow", progress->dl.now);
        add_property_double(zprogress, "ultotal", progress->ul.total);
        add_property_double(zprogress, "ulnow", progress->ul.now);
+       args[1] = &zprogress;
+
        zend_replace_error_handling(EH_NORMAL, NULL, &zeh TSRMLS_CC);
-       zend_call_method_with_2_params(&zclient, NULL, NULL, "notify", &retval, zrequest, zprogress);
+       php_http_object_method_call(&client_obj->notify, zclient, NULL, 2, args TSRMLS_CC);
        zend_restore_error_handling(&zeh TSRMLS_CC);
+
        zval_ptr_dtor(&zclient);
        zval_ptr_dtor(&zrequest);
        zval_ptr_dtor(&zprogress);
-       if (retval) {
-               zval_ptr_dtor(&retval);
-       }
 }
 
 static void response_dtor(void *data)
@@ -537,6 +543,8 @@ static PHP_METHOD(HttpClient, __construct)
 
                php_http_expect(obj->client = php_http_client_init(NULL, driver.client_ops, rf, NULL TSRMLS_CC), runtime, return);
 
+               php_http_object_method_init(&obj->notify, getThis(), ZEND_STRL("notify") TSRMLS_CC);
+
                obj->client->callback.response.func = handle_response;
                obj->client->callback.response.arg = obj;
                obj->client->callback.progress.func = handle_progress;
@@ -869,13 +877,20 @@ static PHP_METHOD(HttpClient, enableEvents)
        RETVAL_ZVAL(getThis(), 1, 0);
 }
 
+struct notify_arg {
+       php_http_object_method_t *cb;
+       zval **args[3];
+       int argc;
+};
+
 static int notify(zend_object_iterator *iter, void *puser TSRMLS_DC)
 {
-       zval **observer = NULL, ***args = puser;
+       zval **observer = NULL;
+       struct notify_arg *arg = puser;
 
        iter->funcs->get_current_data(iter, &observer TSRMLS_CC);
        if (observer) {
-               return php_http_method_call(*observer, ZEND_STRL("update"), args[2]?3:args[1]?2:args[0]?1:0, args, NULL TSRMLS_CC);
+               return php_http_object_method_call(arg->cb, *observer, NULL, arg->argc, arg->args TSRMLS_CC);
        }
        return FAILURE;
 }
@@ -885,10 +900,13 @@ ZEND_BEGIN_ARG_INFO_EX(ai_HttpClient_notify, 0, 0, 0)
 ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpClient, notify)
 {
-       zval *request = NULL, *zprogress = NULL, *observers, **args[3];
+       zval *request = NULL, *zprogress = NULL, *observers;
+       php_http_client_object_t *client_obj;
+       struct notify_arg arg = {NULL};
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|O!o!", &request, php_http_client_request_class_entry, &zprogress), invalid_arg, return);
 
+       client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
        observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
 
        if (Z_TYPE_P(observers) != IS_OBJECT) {
@@ -896,23 +914,34 @@ static PHP_METHOD(HttpClient, notify)
                return;
        }
 
-       Z_ADDREF_P(getThis());
-       args[0] = &getThis();
-       if (request) {
-               Z_ADDREF_P(request);
-       }
-       args[1] = &request;
-       if (zprogress) {
-               Z_ADDREF_P(zprogress);
-       }
-       args[2] = &zprogress;
-       spl_iterator_apply(observers, notify, args TSRMLS_CC);
-       zval_ptr_dtor(&getThis());
-       if (request) {
-               zval_ptr_dtor(&request);
-       }
-       if (zprogress) {
-               zval_ptr_dtor(&zprogress);
+       if (client_obj->update) {
+               arg.cb = client_obj->update;
+
+               Z_ADDREF_P(getThis());
+               arg.args[0] = &getThis();
+               arg.argc = 1;
+
+               if (request) {
+                       Z_ADDREF_P(request);
+                       arg.args[1] = &request;
+                       arg.argc += 1;
+               }
+
+               if (zprogress) {
+                       Z_ADDREF_P(zprogress);
+                       arg.args[2] = &zprogress;
+                       arg.argc += 1;
+               }
+
+               spl_iterator_apply(observers, notify, &arg TSRMLS_CC);
+
+               zval_ptr_dtor(&getThis());
+               if (request) {
+                       zval_ptr_dtor(&request);
+               }
+               if (zprogress) {
+                       zval_ptr_dtor(&zprogress);
+               }
        }
 
        RETVAL_ZVAL(getThis(), 1, 0);
@@ -924,9 +953,11 @@ ZEND_END_ARG_INFO();
 static PHP_METHOD(HttpClient, attach)
 {
        zval *observers, *observer, *retval = NULL;
+       php_http_client_object_t *client_obj;
 
        php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &observer, spl_ce_SplObserver), invalid_arg, return);
 
+       client_obj = zend_object_store_get_object(getThis() TSRMLS_CC);
        observers = zend_read_property(php_http_client_class_entry, getThis(), ZEND_STRL("observers"), 0 TSRMLS_CC);
 
        if (Z_TYPE_P(observers) != IS_OBJECT) {
@@ -934,6 +965,10 @@ static PHP_METHOD(HttpClient, attach)
                return;
        }
 
+       if (!client_obj->update) {
+               client_obj->update = php_http_object_method_init(NULL, observer, ZEND_STRL("update") TSRMLS_CC);
+       }
+
        zend_call_method_with_1_params(&observers, NULL, NULL, "attach", &retval, observer);
        if (retval) {
                zval_ptr_dtor(&retval);
index c9768b9..b536753 100644 (file)
@@ -118,6 +118,8 @@ typedef struct php_http_client_object {
        zend_object_value zv;
        php_http_client_t *client;
        long iterator;
+       php_http_object_method_t *update;
+       php_http_object_method_t notify;
 } php_http_client_object_t;
 
 PHP_HTTP_API php_http_client_t *php_http_client_init(php_http_client_t *h, php_http_client_ops_t *ops, php_resource_factory_t *rf, void *init_arg TSRMLS_DC);
index 8a7e564..5a278f6 100644 (file)
@@ -1161,17 +1161,19 @@ static PHP_METHOD(HttpMessage, getHeader)
                        if (!header_ce) {
                                RETURN_ZVAL(header, 1, 1);
                        } else if (instanceof_function(header_ce, php_http_header_class_entry TSRMLS_CC)) {
+                               php_http_object_method_t cb;
                                zval *header_name, **argv[2];
 
                                MAKE_STD_ZVAL(header_name);
                                ZVAL_STRINGL(header_name, header_str, header_len, 1);
-                               Z_ADDREF_P(header);
 
                                argv[0] = &header_name;
                                argv[1] = &header;
 
                                object_init_ex(return_value, header_ce);
-                               php_http_method_call(return_value, ZEND_STRL("__construct"), 2, argv, NULL TSRMLS_CC);
+                               php_http_object_method_init(&cb, return_value, ZEND_STRL("__construct") TSRMLS_CC);
+                               php_http_object_method_call(&cb, return_value, NULL, 2, argv TSRMLS_CC);
+                               php_http_object_method_dtor(&cb);
 
                                zval_ptr_dtor(&header_name);
                                zval_ptr_dtor(&header);
index abf7229..7d902ea 100644 (file)
@@ -53,30 +53,71 @@ STATUS php_http_new(zend_object_value *ovp, zend_class_entry *ce, php_http_new_t
        return SUCCESS;
 }
 
-STATUS php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval **argv[], zval **retval_ptr TSRMLS_DC)
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC)
+{
+       zval *zfn;
+
+       if (!cb) {
+               cb = ecalloc(1, sizeof(*cb));
+       } else {
+               memset(cb, 0, sizeof(*cb));
+       }
+
+       MAKE_STD_ZVAL(zfn);
+       ZVAL_STRINGL(zfn, method_str, method_len, 1);
+
+       cb->fci.size = sizeof(cb->fci);
+       cb->fci.function_name = zfn;
+       cb->fcc.initialized = 1;
+       cb->fcc.calling_scope = cb->fcc.called_scope = Z_OBJCE_P(zobject);
+       cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(cb->fci.function_name), Z_STRLEN_P(cb->fci.function_name), NULL TSRMLS_CC);
+
+       return cb;
+}
+
+void php_http_object_method_dtor(php_http_object_method_t *cb)
+{
+       if (cb->fci.function_name) {
+               zval_ptr_dtor(&cb->fci.function_name);
+               cb->fci.function_name = NULL;
+       }
+}
+
+void php_http_object_method_free(php_http_object_method_t **cb)
+{
+       if (*cb) {
+               php_http_object_method_dtor(*cb);
+               efree(*cb);
+               *cb = NULL;
+       }
+}
+
+STATUS php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval_ptr, int argc, zval ***args TSRMLS_DC)
 {
-       zend_fcall_info fci;
-       zval zmethod;
-       zval *retval;
        STATUS rv;
+       zval *retval = NULL;
 
-       fci.size = sizeof(fci);
-       fci.object_ptr = object;
-       fci.function_name = &zmethod;
-       fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
-       fci.param_count = argc;
-       fci.params = argv;
-       fci.no_separation = 1;
-       fci.symbol_table = NULL;
-       fci.function_table = NULL;
+       Z_ADDREF_P(zobject);
+       cb->fci.object_ptr = zobject;
+       cb->fcc.object_ptr = zobject;
 
-       INIT_PZVAL(&zmethod);
-       ZVAL_STRINGL(&zmethod, method_str, method_len, 0);
-       rv = zend_call_function(&fci, NULL TSRMLS_CC);
+       cb->fci.retval_ptr_ptr = retval_ptr ? retval_ptr : &retval;
 
+       cb->fci.param_count = argc;
+       cb->fci.params = args;
+
+       if (cb->fcc.called_scope != Z_OBJCE_P(zobject)) {
+               cb->fcc.called_scope = Z_OBJCE_P(zobject);
+               cb->fcc.function_handler = Z_OBJ_HT_P(zobject)->get_method(&zobject, Z_STRVAL_P(cb->fci.function_name), Z_STRLEN_P(cb->fci.function_name), NULL TSRMLS_CC);
+       }
+
+       rv = zend_call_function(&cb->fci, &cb->fcc TSRMLS_CC);
+
+       zval_ptr_dtor(&zobject);
        if (!retval_ptr && retval) {
                zval_ptr_dtor(&retval);
        }
+
        return rv;
 }
 
index 63730b4..1fbd817 100644 (file)
@@ -24,7 +24,16 @@ zend_object_value php_http_object_new_ex(zend_class_entry *ce, void *nothing, ph
 typedef zend_object_value (*php_http_new_t)(zend_class_entry *ce, void *, void ** TSRMLS_DC);
 
 STATUS php_http_new(zend_object_value *ov, zend_class_entry *ce, php_http_new_t create, zend_class_entry *parent_ce, void *intern_ptr, void **obj_ptr TSRMLS_DC);
-STATUS php_http_method_call(zval *object, const char *method_str, size_t method_len, int argc, zval **argv[], zval **retval_ptr TSRMLS_DC);
+
+typedef struct php_http_method {
+       zend_fcall_info fci;
+       zend_fcall_info_cache fcc;
+} php_http_object_method_t;
+
+php_http_object_method_t *php_http_object_method_init(php_http_object_method_t *cb, zval *zobject, const char *method_str, size_t method_len TSRMLS_DC);
+STATUS php_http_object_method_call(php_http_object_method_t *cb, zval *zobject, zval **retval, int argc, zval ***args TSRMLS_DC);
+void php_http_object_method_dtor(php_http_object_method_t *cb);
+void php_http_object_method_free(php_http_object_method_t **cb);
 
 #endif