Esempio n. 1
0
static zend_object *zend_closure_clone(zval *zobject) /* {{{ */
{
	zend_closure *closure = (zend_closure *)Z_OBJ_P(zobject);
	zval result;

	zend_create_closure(&result, &closure->func, closure->func.common.scope, &closure->this_ptr);
	return Z_OBJ(result);
}
Esempio n. 2
0
/* {{{ proto Closure Closure::bind(callable old, object to [, mixed scope])
   Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure, bind)
{
	zval *newthis, *zclosure, *scope_arg = NULL;
	zend_closure *closure, *new_closure;
	zend_class_entry *ce, *called_scope;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
		RETURN_NULL();
	}

	closure = (zend_closure *)Z_OBJ_P(zclosure);

	if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) {
		zend_error(E_WARNING, "Cannot bind an instance to a static closure");
	}

	if (scope_arg != NULL) { /* scope argument was given */
		if (Z_TYPE_P(scope_arg) == IS_OBJECT) {
			ce = Z_OBJCE_P(scope_arg);
		} else if (Z_TYPE_P(scope_arg) == IS_NULL) {
			ce = NULL;
		} else {
			zend_string *class_name = zval_get_string(scope_arg);
			if (zend_string_equals_literal(class_name, "static")) {
				ce = closure->func.common.scope;
			} else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) {
				zend_error(E_WARNING, "Class '%s' not found", ZSTR_VAL(class_name));
				zend_string_release(class_name);
				RETURN_NULL();
			}
			zend_string_release(class_name);
		}
		if(ce && ce != closure->func.common.scope && ce->type == ZEND_INTERNAL_CLASS) {
			/* rebinding to internal class is not allowed */
			zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s", ZSTR_VAL(ce->name));
			return;
		}
	} else { /* scope argument not given; do not change the scope by default */
		ce = closure->func.common.scope;
	}

	if (newthis) {
		called_scope = Z_OBJCE_P(newthis);
	} else {
		called_scope = ce;
	}

	zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
	new_closure = (zend_closure *) Z_OBJ_P(return_value);

	/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
	if (ZEND_USER_CODE(closure->func.type) && (closure->func.common.scope != new_closure->func.common.scope || (closure->func.op_array.fn_flags & ZEND_ACC_NO_RT_ARENA))) {
		new_closure->func.op_array.run_time_cache = emalloc(new_closure->func.op_array.cache_size);
		memset(new_closure->func.op_array.run_time_cache, 0, new_closure->func.op_array.cache_size);

		new_closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA;
	}
}
Esempio n. 3
0
ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
{
	zend_closure *closure;

	zend_create_closure(res, func, scope, called_scope, this_ptr);

	closure = (zend_closure *)Z_OBJ_P(res);
	closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;
}
Esempio n. 4
0
/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] )
   Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure, bind) /* {{{ */
{
	zval *newthis, *zclosure, *scope_arg = NULL;
	zend_closure *closure;
	zend_class_entry *ce, **ce_p;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
		RETURN_NULL();
	}

	closure = (zend_closure *)zend_object_store_get_object(zclosure TSRMLS_CC);	

	if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) {
		zend_error(E_WARNING, "Cannot bind an instance to a static closure");
	}

	if (scope_arg != NULL) { /* scope argument was given */
		if (IS_ZEND_STD_OBJECT(*scope_arg)) {
			ce = Z_OBJCE_P(scope_arg);
		} else if (Z_TYPE_P(scope_arg) == IS_NULL) {
			ce = NULL;
		} else {
			char *class_name;
			int class_name_len;
			zval tmp_zval;
			INIT_ZVAL(tmp_zval);

			if (Z_TYPE_P(scope_arg) == IS_STRING) {
				class_name = Z_STRVAL_P(scope_arg);
				class_name_len = Z_STRLEN_P(scope_arg);
			} else {
				tmp_zval = *scope_arg;
				zval_copy_ctor(&tmp_zval);
				convert_to_string(&tmp_zval);
				class_name = Z_STRVAL(tmp_zval);
				class_name_len = Z_STRLEN(tmp_zval);
			}

			if ((class_name_len == sizeof("static") - 1) &&
				(memcmp("static", class_name, sizeof("static") - 1) == 0)) {
				ce = closure->func.common.scope;
			}
			else if (zend_lookup_class_ex(class_name, class_name_len, NULL, 1, &ce_p TSRMLS_CC) == FAILURE) {
				zend_error(E_WARNING, "Class '%s' not found", class_name);
				zval_dtor(&tmp_zval);
				RETURN_NULL();
			} else {
				ce = *ce_p;
			}
			zval_dtor(&tmp_zval);
		}
	} else { /* scope argument not given; do not change the scope by default */
		ce = closure->func.common.scope;
	}

	zend_create_closure(return_value, &closure->func, ce, newthis TSRMLS_CC);
}
Esempio n. 5
0
/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] )
   Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure, bind)
{
    zval *newthis, *zclosure, *scope_arg = NULL;
    zend_closure *closure;
    zend_class_entry *ce, *called_scope;

    if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
        RETURN_NULL();
    }

    closure = (zend_closure *)Z_OBJ_P(zclosure);

    if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) {
        zend_error(E_WARNING, "Cannot bind an instance to a static closure");
    }

    if (scope_arg != NULL) { /* scope argument was given */
        if (Z_TYPE_P(scope_arg) == IS_OBJECT) {
            ce = Z_OBJCE_P(scope_arg);
        } else if (Z_TYPE_P(scope_arg) == IS_NULL) {
            ce = NULL;
        } else {
            zend_string *class_name = zval_get_string(scope_arg);
            if (zend_string_equals_literal(class_name, "static")) {
                ce = closure->func.common.scope;
            } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) {
                zend_error(E_WARNING, "Class '%s' not found", class_name->val);
                zend_string_release(class_name);
                RETURN_NULL();
            }
            zend_string_release(class_name);
        }
        if(ce && ce != closure->func.common.scope && ce->type == ZEND_INTERNAL_CLASS) {
            /* rebinding to internal class is not allowed */
            zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s", ce->name->val);
            return;
        }
    } else { /* scope argument not given; do not change the scope by default */
        ce = closure->func.common.scope;
    }

    if (newthis) {
        called_scope = Z_OBJCE_P(newthis);
    } else {
        called_scope = ce;
    }

    zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
}
Esempio n. 6
0
/**
 * Creates a closure
 */
int zephir_create_closure_ex(zval *return_value, zval *this_ptr, zend_class_entry *ce, const char *method_name, zend_uint method_length)
{
	zend_function *function_ptr;
	zend_closure *closure;

	if ((function_ptr = zend_hash_str_find_ptr(&ce->function_table, method_name, method_length)) == NULL) {
		ZVAL_NULL(return_value);
		return FAILURE;
	}

	zend_create_closure(return_value, function_ptr, ce, ce, this_ptr);
	// Make sure we can use a closure multiple times
	closure = (zend_closure*)Z_OBJ_P(return_value);
	closure->func.internal_function.handler = closure->orig_internal_handler;
	return SUCCESS;
}
Esempio n. 7
0
/* {{{ proto Closure Closure::bind(callable old, object to [, mixed scope])
   Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure, bind)
{
	zval *newthis, *zclosure, *scope_arg = NULL;
	zend_closure *closure;
	zend_class_entry *ce, *called_scope;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
		return;
	}

	closure = (zend_closure *)Z_OBJ_P(zclosure);

	if (scope_arg != NULL) { /* scope argument was given */
		if (Z_TYPE_P(scope_arg) == IS_OBJECT) {
			ce = Z_OBJCE_P(scope_arg);
		} else if (Z_TYPE_P(scope_arg) == IS_NULL) {
			ce = NULL;
		} else {
			zend_string *tmp_class_name;
			zend_string *class_name = zval_get_tmp_string(scope_arg, &tmp_class_name);
			if (zend_string_equals_literal(class_name, "static")) {
				ce = closure->func.common.scope;
			} else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) {
				zend_error(E_WARNING, "Class '%s' not found", ZSTR_VAL(class_name));
				zend_string_release_ex(class_name, 0);
				RETURN_NULL();
			}
			zend_tmp_string_release(tmp_class_name);
		}
	} else { /* scope argument not given; do not change the scope by default */
		ce = closure->func.common.scope;
	}

	if (!zend_valid_closure_binding(closure, newthis, ce)) {
		return;
	}

	if (newthis) {
		called_scope = Z_OBJCE_P(newthis);
	} else {
		called_scope = ce;
	}

	zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
}
Esempio n. 8
0
/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] )
   Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure, bind)
{
	zval *newthis, *zclosure, *scope_arg = NULL;
	zend_closure *closure;
	zend_class_entry *ce;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
		RETURN_NULL();
	}

	closure = (zend_closure *)Z_OBJ_P(zclosure);

	if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) {
		zend_error(E_WARNING, "Cannot bind an instance to a static closure");
	}

	if (scope_arg != NULL) { /* scope argument was given */
		if (IS_ZEND_STD_OBJECT(*scope_arg)) {
			ce = Z_OBJCE_P(scope_arg);
		} else if (Z_TYPE_P(scope_arg) == IS_NULL) {
			ce = NULL;
		} else {
			zend_string *class_name = zval_get_string(scope_arg);
			if ((class_name->len == sizeof("static") - 1) &&
				(memcmp("static", class_name->val, sizeof("static") - 1) == 0)) {
				ce = closure->func.common.scope;
			} else if ((ce = zend_lookup_class_ex(class_name, NULL, 1 TSRMLS_CC)) == NULL) {
				zend_error(E_WARNING, "Class '%s' not found", class_name->val);
				STR_RELEASE(class_name);
				RETURN_NULL();
			}
			STR_RELEASE(class_name);
		}
	} else { /* scope argument not given; do not change the scope by default */
		ce = closure->func.common.scope;
	}

	zend_create_closure(return_value, &closure->func, ce, newthis TSRMLS_CC);
}
Esempio n. 9
0
/* {{{ proto mixed Closure::call(object to [, mixed parameter] [, mixed ...] )
   Call closure, binding to a given object with its class as the scope */
ZEND_METHOD(Closure, call)
{
	zval *zclosure, *newthis, closure_result;
	zend_closure *closure;
	zend_fcall_info fci;
	zend_fcall_info_cache fci_cache;
	zend_function my_function;
	zend_object *newobj;

	fci.param_count = 0;
	fci.params = NULL;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o*", &newthis, &fci.params, &fci.param_count) == FAILURE) {
		return;
	}

	zclosure = getThis();
	closure = (zend_closure *) Z_OBJ_P(zclosure);

	newobj = Z_OBJ_P(newthis);

	if (!zend_valid_closure_binding(closure, newthis, Z_OBJCE_P(newthis))) {
		return;
	}

	if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
		zval new_closure;
		zend_create_closure(&new_closure, &closure->func, Z_OBJCE_P(newthis), closure->called_scope, newthis);
		closure = (zend_closure *) Z_OBJ(new_closure);
		fci_cache.function_handler = &closure->func;
	} else {
		memcpy(&my_function, &closure->func, closure->func.type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
		my_function.common.fn_flags &= ~ZEND_ACC_CLOSURE;
		/* use scope of passed object */
		my_function.common.scope = Z_OBJCE_P(newthis);
		fci_cache.function_handler = &my_function;

		/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
		if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
			my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size);
			memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size);
		}
	}

	fci_cache.called_scope = newobj->ce;
	fci_cache.object = fci.object = newobj;

	fci.size = sizeof(fci);
	ZVAL_COPY_VALUE(&fci.function_name, zclosure);
	fci.retval = &closure_result;
	fci.no_separation = 1;

	if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
		if (Z_ISREF(closure_result)) {
			zend_unwrap_reference(&closure_result);
		}
		ZVAL_COPY_VALUE(return_value, &closure_result);
	}

	if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
		/* copied upon generator creation */
		GC_DELREF(&closure->std);
	} else if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
		efree(my_function.op_array.run_time_cache);
	}
}
Esempio n. 10
0
PHP_METHOD(jsonrpc_server, register)
{
  zval *closure=NULL;
  zval *val;
  zval *name;
  zval *callbacks;
  char *lcname;
  char *name_str, *tmp;
  int name_len, tmp_len;
  zval *classname;

  zend_class_entry **pce;
  zend_class_entry *ce;
  zend_function *fptr;

  zval *object = getThis();

  if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "zO", &name, &closure, zend_ce_closure) == SUCCESS) {
    fptr = (zend_function*)zend_get_closure_method_def(closure TSRMLS_CC);
    Z_ADDREF_P(closure);
  } else if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zs", &name, &name_str, &name_len) == SUCCESS) { 
    if ((tmp = strstr(name_str, "::")) == NULL) {
      char *nsname;

      lcname = zend_str_tolower_dup(name_str, name_len);

      // Ignore leading "\" 
      nsname = lcname;
      if (lcname[0] == '\\') {
      nsname = &lcname[1];
      name_len--;
      }

      if (zend_hash_find(EG(function_table), nsname, name_len + 1, (void **)&fptr) == FAILURE) {
        efree(lcname);
        php_error_docref(NULL TSRMLS_CC, E_WARNING, 
          "Function %s() does not exist", name_str);
        return;
      }
      efree(lcname);
    }else {
      tmp_len = tmp - name_str;
      MAKE_STD_ZVAL(classname);
      ZVAL_STRINGL(classname, name_str, tmp_len, 1);
      name_len = name_len - (tmp_len + 2);
      name_str = tmp + 2;
      //php_printf("classname: %s, method: %s\n", Z_STRVAL_P(classname), name_str);
      if (zend_lookup_class(Z_STRVAL_P(classname), Z_STRLEN_P(classname), &pce TSRMLS_CC) == FAILURE) {
        php_error_docref(NULL TSRMLS_CC, E_WARNING,
          "Class %s does exist", Z_STRVAL_P(classname));
        //zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC,
        //    "Class %s does not exist", Z_STRVAL_P(classname)); 
        zval_dtor(classname);
        return;
      }
      ce = *pce;

      lcname = zend_str_tolower_dup(name_str, name_len);

      if (zend_hash_find(&ce->function_table, lcname, name_len + 1, (void **) &fptr) == FAILURE) {
        efree(lcname);
        php_error_docref(NULL TSRMLS_CC, E_WARNING,
          "Method %s::%s() does not exist", ce->name, name_str);
        //zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, 
        //  "Method %s::%s() does not exist", ce->name, name_str);
        return;
      }
      efree(lcname);
    }
  }else {
    return ;
  }


  callbacks = zend_read_property(
    php_jsonrpc_server_entry, object, "callbacks", sizeof("callbacks")-1, 0 TSRMLS_CC
  );

  MAKE_STD_ZVAL(val);

#if PHP_VERSION_ID < 50399
  zend_create_closure(val, fptr TSRMLS_CC);
#else
  zend_create_closure(val, fptr, NULL, NULL TSRMLS_CC);
#endif


  add_assoc_zval(callbacks, Z_STRVAL_P(name), val);

  zend_update_property(php_jsonrpc_server_entry, object, "callbacks", sizeof("callbacks")-1, callbacks TSRMLS_CC);

  RETURN_ZVAL(object,1,0);

}
Esempio n. 11
0
/* {{{ proto mixed Closure::call(object to [, mixed parameter] [, mixed ...] )
   Call closure, binding to a given object with its class as the scope */
ZEND_METHOD(Closure, call)
{
	zval *zclosure, *newthis, closure_result;
	zend_closure *closure;
	zend_fcall_info fci;
	zend_fcall_info_cache fci_cache;
	zval *my_params;
	int my_param_count = 0;
	zend_function my_function;
	zend_object *newobj;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o*", &newthis, &my_params, &my_param_count) == FAILURE) {
		return;
	}

	zclosure = getThis();
	closure = (zend_closure *) Z_OBJ_P(zclosure);

	newobj = Z_OBJ_P(newthis);

	if (!zend_valid_closure_binding(closure, newthis, Z_OBJCE_P(newthis))) {
		return;
	}

	/* This should never happen as closures will always be callable */
	if (zend_fcall_info_init(zclosure, 0, &fci, &fci_cache, NULL, NULL) != SUCCESS) {
		ZEND_ASSERT(0);
	}

	fci.retval = &closure_result;
	fci.params = my_params;
	fci.param_count = my_param_count;
	fci.object = fci_cache.object = newobj;
	fci_cache.initialized = 1;
	fci_cache.called_scope = Z_OBJCE_P(newthis);

	if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
		zval new_closure;
		zend_create_closure(&new_closure, fci_cache.function_handler, Z_OBJCE_P(newthis), closure->called_scope, newthis);
		closure = (zend_closure *) Z_OBJ(new_closure);
		fci_cache.function_handler = &closure->func;
	} else {
		memcpy(&my_function, fci_cache.function_handler, fci_cache.function_handler->type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
		/* use scope of passed object */
		my_function.common.scope = Z_OBJCE_P(newthis);
		fci_cache.function_handler = &my_function;

		/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
		if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
			my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size);
			memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size);
		}
	}

	if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
		ZVAL_COPY_VALUE(return_value, &closure_result);
	}

	if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
		/* copied upon generator creation */
		--GC_REFCOUNT(&closure->std);
	} else if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
		efree(my_function.op_array.run_time_cache);
	}
}
Esempio n. 12
0
/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] )
   Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure, bind)
{
	zval *newthis, *zclosure, *scope_arg = NULL;
	zend_closure *closure;
	zend_class_entry *ce, **ce_p;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
		RETURN_NULL();
	}

	closure = (zend_closure *)zend_object_store_get_object(zclosure TSRMLS_CC);

	if ((newthis != NULL) && (closure->func.common.fn_flags & ZEND_ACC_STATIC)) {
		zend_error(E_WARNING, "Cannot bind an instance to a static closure");
	}

	if (newthis == NULL && !(closure->func.common.fn_flags & ZEND_ACC_STATIC)
			&& closure->func.common.scope && closure->func.type == ZEND_INTERNAL_FUNCTION) {
		zend_error(E_WARNING, "Cannot unbind $this of internal method");
		return;
	}

	if (scope_arg != NULL) { /* scope argument was given */
		if (IS_ZEND_STD_OBJECT(*scope_arg)) {
			ce = Z_OBJCE_P(scope_arg);
		} else if (Z_TYPE_P(scope_arg) == IS_NULL) {
			ce = NULL;
		} else {
			char *class_name;
			int class_name_len;
			zval tmp_zval;
			INIT_ZVAL(tmp_zval);

			if (Z_TYPE_P(scope_arg) == IS_STRING) {
				class_name = Z_STRVAL_P(scope_arg);
				class_name_len = Z_STRLEN_P(scope_arg);
			} else {
				tmp_zval = *scope_arg;
				zval_copy_ctor(&tmp_zval);
				convert_to_string(&tmp_zval);
				class_name = Z_STRVAL(tmp_zval);
				class_name_len = Z_STRLEN(tmp_zval);
			}

			if ((class_name_len == sizeof("static") - 1) &&
				(memcmp("static", class_name, sizeof("static") - 1) == 0)) {
				ce = closure->func.common.scope;
			}
			else if (zend_lookup_class_ex(class_name, class_name_len, NULL, 1, &ce_p TSRMLS_CC) == FAILURE) {
				zend_error(E_WARNING, "Class '%s' not found", class_name);
				zval_dtor(&tmp_zval);
				RETURN_NULL();
			} else {
				ce = *ce_p;
			}
			zval_dtor(&tmp_zval);
		}
	} else { /* scope argument not given; do not change the scope by default */
		ce = closure->func.common.scope;
	}

	/* verify that we aren't binding internal function to a wrong scope */
	if (closure->func.type == ZEND_INTERNAL_FUNCTION && closure->func.common.scope != NULL) {
		if (ce && !instanceof_function(ce, closure->func.common.scope TSRMLS_CC)) {
			zend_error(E_WARNING, "Cannot bind function %s::%s to scope class %s", closure->func.common.scope->name, closure->func.common.function_name, ce->name);
			return;
		}
		if (ce && newthis && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0 &&
				!instanceof_function(Z_OBJCE_P(newthis), closure->func.common.scope TSRMLS_CC)) {
			zend_error(E_WARNING, "Cannot bind internal method %s::%s() to object of class %s", closure->func.common.scope->name, closure->func.common.function_name, Z_OBJCE_P(newthis)->name);
			return;
		}
	}

	zend_create_closure(return_value, &closure->func, ce, newthis TSRMLS_CC);
}
Esempio n. 13
0
/* {{{ proto mixed Closure::call(object to [, mixed parameter] [, mixed ...] )
   Call closure, binding to a given object with its class as the scope */
ZEND_METHOD(Closure, call)
{
	zval *zclosure, *newthis, closure_result;
	zend_closure *closure;
	zend_fcall_info fci;
	zend_fcall_info_cache fci_cache;
	zval *my_params;
	int my_param_count = 0;
	zend_function my_function;
	zend_object *newobj;

	if (zend_parse_parameters(ZEND_NUM_ARGS(), "o*", &newthis, &my_params, &my_param_count) == FAILURE) {
		return;
	}

	zclosure = getThis();
	closure = (zend_closure *) Z_OBJ_P(zclosure);

	if (closure->func.common.fn_flags & ZEND_ACC_STATIC) {
		zend_error(E_WARNING, "Cannot bind an instance to a static closure");
		return;
	}

	if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
		/* verify that we aren't binding internal function to a wrong object */
		if ((closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0 &&
				closure->func.common.scope &&
				!instanceof_function(Z_OBJCE_P(newthis), closure->func.common.scope)) {
			zend_error(E_WARNING, "Cannot bind function %s::%s to object of class %s", ZSTR_VAL(closure->func.common.scope->name), ZSTR_VAL(closure->func.common.function_name), ZSTR_VAL(Z_OBJCE_P(newthis)->name));
			return;
		}
	}

	newobj = Z_OBJ_P(newthis);

	if (newobj->ce != closure->func.common.scope && newobj->ce->type == ZEND_INTERNAL_CLASS) {
		/* rebinding to internal class is not allowed */
		zend_error(E_WARNING, "Cannot bind closure to object of internal class %s", ZSTR_VAL(newobj->ce->name));
		return;
	}

	/* This should never happen as closures will always be callable */
	if (zend_fcall_info_init(zclosure, 0, &fci, &fci_cache, NULL, NULL) != SUCCESS) {
		ZEND_ASSERT(0);
	}

	fci.retval = &closure_result;
	fci.params = my_params;
	fci.param_count = my_param_count;
	fci.object = fci_cache.object = newobj;
	fci_cache.initialized = 1;

	if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
		zval new_closure;
		zend_create_closure(&new_closure, fci_cache.function_handler, Z_OBJCE_P(newthis), closure->called_scope, newthis);
		closure = (zend_closure *) Z_OBJ(new_closure);
		fci_cache.function_handler = &closure->func;
	} else {
		memcpy(&my_function, fci_cache.function_handler, fci_cache.function_handler->type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
		/* use scope of passed object */
		my_function.common.scope = Z_OBJCE_P(newthis);
		fci_cache.function_handler = &my_function;

		/* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
		if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
			my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size);
			memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size);
		}
	}

	if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
		ZVAL_COPY_VALUE(return_value, &closure_result);
	}

	if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
		/* copied upon generator creation */
		--GC_REFCOUNT(&closure->std);
	} else if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
		efree(my_function.op_array.run_time_cache);
	}
}