mirror of
https://github.com/php/php-src.git
synced 2024-11-27 11:53:33 +08:00
Merge branch 'PHP-7.4'
* PHP-7.4: Fixed bug #77706 (Improve error messages in FFI for incompatible arguments)
This commit is contained in:
commit
87f8b1b709
167
ext/ffi/ffi.c
167
ext/ffi/ffi.c
@ -194,6 +194,9 @@ static int zend_ffi_is_same_type(zend_ffi_type *type1, zend_ffi_type *type2);
|
||||
static zend_ffi_type *zend_ffi_remember_type(zend_ffi_type *type);
|
||||
static char *zend_ffi_parse_directives(const char *filename, char *code_pos, char **scope_name, char **lib, zend_bool preload);
|
||||
static ZEND_FUNCTION(ffi_trampoline);
|
||||
static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type);
|
||||
static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type);
|
||||
static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type);
|
||||
|
||||
#if FFI_CLOSURES
|
||||
static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value);
|
||||
@ -320,7 +323,6 @@ static ffi_type *zend_ffi_make_fake_struct_type(zend_ffi_type *type) /* {{{ */
|
||||
break;
|
||||
default:
|
||||
efree(t);
|
||||
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible struct/union");
|
||||
return NULL;
|
||||
}
|
||||
i++;
|
||||
@ -374,17 +376,10 @@ again:
|
||||
case ZEND_FFI_TYPE_STRUCT:
|
||||
if (!(type->attr & ZEND_FFI_ATTR_UNION)) {
|
||||
ffi_type *t = zend_ffi_make_fake_struct_type(type);
|
||||
if (t) {
|
||||
return t;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI return struct/union is not implemented");
|
||||
break;
|
||||
case ZEND_FFI_TYPE_ARRAY:
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI return array is not implemented");
|
||||
break;
|
||||
default:
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
@ -654,7 +649,7 @@ again:
|
||||
if (ZSTR_LEN(str) == 1) {
|
||||
*(char*)ptr = ZSTR_VAL(str)[0];
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of incompatible C type");
|
||||
zend_ffi_assign_incompatible(value, type);
|
||||
return FAILURE;
|
||||
}
|
||||
zend_tmp_string_release(tmp_str);
|
||||
@ -702,7 +697,7 @@ again:
|
||||
}
|
||||
#endif
|
||||
}
|
||||
zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of incompatible C type");
|
||||
zend_ffi_assign_incompatible(value, type);
|
||||
return FAILURE;
|
||||
case ZEND_FFI_TYPE_STRUCT:
|
||||
case ZEND_FFI_TYPE_ARRAY:
|
||||
@ -715,7 +710,7 @@ again:
|
||||
return SUCCESS;
|
||||
}
|
||||
}
|
||||
zend_throw_error(zend_ffi_exception_ce, "Attempt to perform assign of incompatible C type");
|
||||
zend_ffi_assign_incompatible(value, type);
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
@ -841,6 +836,7 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ *
|
||||
arg_type = ZEND_FFI_TYPE(arg_type);
|
||||
callback_data->arg_types[n] = zend_ffi_get_type(arg_type);
|
||||
if (!callback_data->arg_types[n]) {
|
||||
zend_ffi_pass_unsupported(arg_type);
|
||||
efree(callback_data);
|
||||
ffi_closure_free(callback);
|
||||
return NULL;
|
||||
@ -850,6 +846,7 @@ static void *zend_ffi_create_callback(zend_ffi_type *type, zval *value) /* {{{ *
|
||||
}
|
||||
callback_data->ret_type = zend_ffi_get_type(type->func.ret_type);
|
||||
if (!callback_data->ret_type) {
|
||||
zend_ffi_return_unsupported(type->func.ret_type);
|
||||
efree(callback_data);
|
||||
ffi_closure_free(callback);
|
||||
return NULL;
|
||||
@ -1326,6 +1323,86 @@ static int zend_ffi_ctype_name(zend_ffi_ctype_name_buf *buf, const zend_ffi_type
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static ZEND_COLD void zend_ffi_return_unsupported(zend_ffi_type *type) /* {{{ */
|
||||
{
|
||||
type = ZEND_FFI_TYPE(type);
|
||||
if (type->kind == ZEND_FFI_TYPE_STRUCT) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI return struct/union is not implemented");
|
||||
} else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI return array is not implemented");
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI internal error. Unsupported return type");
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static ZEND_COLD void zend_ffi_pass_unsupported(zend_ffi_type *type) /* {{{ */
|
||||
{
|
||||
type = ZEND_FFI_TYPE(type);
|
||||
if (type->kind == ZEND_FFI_TYPE_STRUCT) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI passing struct/union is not implemented");
|
||||
} else if (type->kind == ZEND_FFI_TYPE_ARRAY) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI passing array is not implemented");
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI internal error. Unsupported parameter type");
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static ZEND_COLD void zend_ffi_pass_incompatible(zval *arg, zend_ffi_type *type, uint32_t n, zend_execute_data *execute_data) /* {{{ */
|
||||
{
|
||||
zend_ffi_ctype_name_buf buf1, buf2;
|
||||
|
||||
buf1.start = buf1.end = buf1.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
|
||||
if (!zend_ffi_ctype_name(&buf1, type)) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name));
|
||||
} else {
|
||||
*buf1.end = 0;
|
||||
if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
|
||||
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
|
||||
|
||||
type = ZEND_FFI_TYPE(cdata->type);
|
||||
buf2.start = buf2.end = buf2.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
|
||||
if (!zend_ffi_ctype_name(&buf2, type)) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start);
|
||||
} else {
|
||||
*buf2.end = 0;
|
||||
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s', found '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start, buf2.start);
|
||||
}
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible argument %d of C function '%s', expecting '%s', found PHP '%s'", n + 1, ZSTR_VAL(EX(func)->internal_function.function_name), buf1.start, zend_get_type_by_const(Z_TYPE_P(arg)));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static ZEND_COLD void zend_ffi_assign_incompatible(zval *arg, zend_ffi_type *type) /* {{{ */
|
||||
{
|
||||
zend_ffi_ctype_name_buf buf1, buf2;
|
||||
|
||||
buf1.start = buf1.end = buf1.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
|
||||
if (!zend_ffi_ctype_name(&buf1, type)) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning");
|
||||
} else {
|
||||
*buf1.end = 0;
|
||||
if (Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == zend_ffi_cdata_ce) {
|
||||
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
|
||||
|
||||
type = ZEND_FFI_TYPE(cdata->type);
|
||||
buf2.start = buf2.end = buf2.buf + ((MAX_TYPE_NAME_LEN * 3) / 4);
|
||||
if (!zend_ffi_ctype_name(&buf2, type)) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s'", buf1.start);
|
||||
} else {
|
||||
*buf2.end = 0;
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s' from type '%s'", buf1.start, buf2.start);
|
||||
}
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incompatible types when assigning to type '%s' from PHP '%s'", buf1.start, zend_get_type_by_const(Z_TYPE_P(arg)));
|
||||
}
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_string *zend_ffi_get_class_name(zend_string *prefix, const zend_ffi_type *type) /* {{{ */
|
||||
{
|
||||
zend_ffi_ctype_name_buf buf;
|
||||
@ -2096,7 +2173,7 @@ static zval *zend_ffi_write_var(zend_object *obj, zend_string *var_name, zval *v
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zend_ffi_pass_arg(zval *arg, zend_ffi_type *type, ffi_type **pass_type, void **arg_values, uint32_t n) /* {{{ */
|
||||
static int zend_ffi_pass_arg(zval *arg, zend_ffi_type *type, ffi_type **pass_type, void **arg_values, uint32_t n, zend_execute_data *execute_data) /* {{{ */
|
||||
{
|
||||
zend_long lval;
|
||||
double dval;
|
||||
@ -2201,7 +2278,7 @@ again:
|
||||
}
|
||||
#endif
|
||||
}
|
||||
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible pointer");
|
||||
zend_ffi_pass_incompatible(arg, type, n, execute_data);
|
||||
return FAILURE;
|
||||
case ZEND_FFI_TYPE_BOOL:
|
||||
*pass_type = &ffi_type_uint8;
|
||||
@ -2212,7 +2289,7 @@ again:
|
||||
*pass_type = &ffi_type_sint8;
|
||||
*(char*)arg_values[n] = ZSTR_VAL(str)[0];
|
||||
if (ZSTR_LEN(str) != 1) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Attempt to pass incompatible C type");
|
||||
zend_ffi_pass_incompatible(arg, type, n, execute_data);
|
||||
}
|
||||
zend_tmp_string_release(tmp_str);
|
||||
break;
|
||||
@ -2227,31 +2304,24 @@ again:
|
||||
if (zend_ffi_is_compatible_type(type, ZEND_FFI_TYPE(cdata->type))) {
|
||||
/* Create a fake structure type */
|
||||
ffi_type *t = zend_ffi_make_fake_struct_type(type);
|
||||
if (!t) {
|
||||
return FAILURE;
|
||||
if (t) {
|
||||
*pass_type = t;
|
||||
arg_values[n] = cdata->ptr;
|
||||
break;
|
||||
}
|
||||
*pass_type = t;
|
||||
arg_values[n] = cdata->ptr;
|
||||
break;
|
||||
} else {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Passing incompatible struct/union");
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI passing struct/union is not implemented");
|
||||
return FAILURE;
|
||||
case ZEND_FFI_TYPE_ARRAY:
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI passing array is not implemented");
|
||||
zend_ffi_pass_incompatible(arg, type, n, execute_data);
|
||||
return FAILURE;
|
||||
default:
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
|
||||
zend_ffi_pass_unsupported(type);
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static int zend_ffi_pass_var_arg(zval *arg, ffi_type **pass_type, void **arg_values, uint32_t n) /* {{{ */
|
||||
static int zend_ffi_pass_var_arg(zval *arg, ffi_type **pass_type, void **arg_values, uint32_t n, zend_execute_data *execute_data) /* {{{ */
|
||||
{
|
||||
ZVAL_DEREF(arg);
|
||||
switch (Z_TYPE_P(arg)) {
|
||||
@ -2289,11 +2359,11 @@ static int zend_ffi_pass_var_arg(zval *arg, ffi_type **pass_type, void **arg_val
|
||||
zend_ffi_cdata *cdata = (zend_ffi_cdata*)Z_OBJ_P(arg);
|
||||
zend_ffi_type *type = ZEND_FFI_TYPE(cdata->type);
|
||||
|
||||
return zend_ffi_pass_arg(arg, type, pass_type, arg_values, n);
|
||||
return zend_ffi_pass_arg(arg, type, pass_type, arg_values, n, execute_data);
|
||||
}
|
||||
/* break missing intentionally */
|
||||
default:
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
|
||||
zend_throw_error(zend_ffi_exception_ce, "Unsupported argument type");
|
||||
return FAILURE;
|
||||
}
|
||||
return SUCCESS;
|
||||
@ -2318,8 +2388,8 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
|
||||
arg_count = type->func.args ? zend_hash_num_elements(type->func.args) : 0;
|
||||
if (type->attr & ZEND_FFI_ATTR_VARIADIC) {
|
||||
if (arg_count > EX_NUM_ARGS()) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s'", ZSTR_VAL(EX(func)->internal_function.function_name));
|
||||
return;
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s', expecting at least %d parameter%s", ZSTR_VAL(EX(func)->internal_function.function_name), arg_count, (arg_count != 1) ? "s" : "");
|
||||
goto exit;
|
||||
}
|
||||
if (EX_NUM_ARGS()) {
|
||||
arg_types = do_alloca(
|
||||
@ -2331,39 +2401,40 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
|
||||
ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
|
||||
arg_type = ZEND_FFI_TYPE(arg_type);
|
||||
arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (FFI_SIZEOF_ARG * n);
|
||||
if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n) != SUCCESS) {
|
||||
if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n, execute_data) != SUCCESS) {
|
||||
free_alloca(arg_types, arg_types_use_heap);
|
||||
free_alloca(arg_values, arg_values_use_heap);
|
||||
return;
|
||||
goto exit;
|
||||
}
|
||||
n++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
for (; n < EX_NUM_ARGS(); n++) {
|
||||
arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (FFI_SIZEOF_ARG * n);
|
||||
if (zend_ffi_pass_var_arg(EX_VAR_NUM(n), &arg_types[n], arg_values, n) != SUCCESS) {
|
||||
if (zend_ffi_pass_var_arg(EX_VAR_NUM(n), &arg_types[n], arg_values, n, execute_data) != SUCCESS) {
|
||||
free_alloca(arg_types, arg_types_use_heap);
|
||||
free_alloca(arg_values, arg_values_use_heap);
|
||||
return;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
|
||||
if (!ret_type) {
|
||||
zend_ffi_return_unsupported(type->func.ret_type);
|
||||
free_alloca(arg_types, arg_types_use_heap);
|
||||
free_alloca(arg_values, arg_values_use_heap);
|
||||
return;
|
||||
goto exit;
|
||||
}
|
||||
if (ffi_prep_cif_var(&cif, type->func.abi, arg_count, EX_NUM_ARGS(), ret_type, arg_types) != FFI_OK) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
|
||||
zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
|
||||
free_alloca(arg_types, arg_types_use_heap);
|
||||
free_alloca(arg_values, arg_values_use_heap);
|
||||
return;
|
||||
goto exit;
|
||||
}
|
||||
} else {
|
||||
if (arg_count != EX_NUM_ARGS()) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s'", ZSTR_VAL(EX(func)->internal_function.function_name));
|
||||
return;
|
||||
zend_throw_error(zend_ffi_exception_ce, "Incorrect number of arguments for C function '%s', expecting exactly %d parameter%s", ZSTR_VAL(EX(func)->internal_function.function_name), arg_count, (arg_count != 1) ? "s" : "");
|
||||
goto exit;
|
||||
}
|
||||
if (EX_NUM_ARGS()) {
|
||||
arg_types = do_alloca(
|
||||
@ -2375,10 +2446,10 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
|
||||
ZEND_HASH_FOREACH_PTR(type->func.args, arg_type) {
|
||||
arg_type = ZEND_FFI_TYPE(arg_type);
|
||||
arg_values[n] = ((char*)arg_values) + (sizeof(void*) * EX_NUM_ARGS()) + (FFI_SIZEOF_ARG * n);
|
||||
if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n) != SUCCESS) {
|
||||
if (zend_ffi_pass_arg(EX_VAR_NUM(n), arg_type, &arg_types[n], arg_values, n, execute_data) != SUCCESS) {
|
||||
free_alloca(arg_types, arg_types_use_heap);
|
||||
free_alloca(arg_values, arg_values_use_heap);
|
||||
return;
|
||||
goto exit;
|
||||
}
|
||||
n++;
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
@ -2386,15 +2457,16 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
|
||||
}
|
||||
ret_type = zend_ffi_get_type(ZEND_FFI_TYPE(type->func.ret_type));
|
||||
if (!ret_type) {
|
||||
zend_ffi_return_unsupported(type->func.ret_type);
|
||||
free_alloca(arg_types, arg_types_use_heap);
|
||||
free_alloca(arg_values, arg_values_use_heap);
|
||||
return;
|
||||
goto exit;
|
||||
}
|
||||
if (ffi_prep_cif(&cif, type->func.abi, arg_count, ret_type, arg_types) != FFI_OK) {
|
||||
zend_throw_error(zend_ffi_exception_ce, "FFI internal error");
|
||||
zend_throw_error(zend_ffi_exception_ce, "Cannot prepare callback CIF");
|
||||
free_alloca(arg_types, arg_types_use_heap);
|
||||
free_alloca(arg_values, arg_values_use_heap);
|
||||
return;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2416,6 +2488,7 @@ static ZEND_FUNCTION(ffi_trampoline) /* {{{ */
|
||||
|
||||
zend_ffi_cdata_to_zval(NULL, (void*)&ret, ZEND_FFI_TYPE(type->func.ret_type), BP_VAR_R, return_value, 0, 1);
|
||||
|
||||
exit:
|
||||
zend_string_release(EX(func)->common.function_name);
|
||||
if (EX(func)->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) {
|
||||
zend_free_trampoline(EX(func));
|
||||
|
60
ext/ffi/tests/bug77706.phpt
Normal file
60
ext/ffi/tests/bug77706.phpt
Normal file
@ -0,0 +1,60 @@
|
||||
--TEST--
|
||||
Bug #77632 (FFI Segfaults When Called With Variadics)
|
||||
--SKIPIF--
|
||||
<?php
|
||||
require_once('skipif.inc');
|
||||
try {
|
||||
$libc = FFI::cdef("int printf(const char *format, ...);", "libc.so.6");
|
||||
} catch (Throwable $_) {
|
||||
die('skip libc.so.6 not available');
|
||||
}
|
||||
?>
|
||||
--INI--
|
||||
ffi.enable=1
|
||||
--FILE--
|
||||
<?php
|
||||
$header = '
|
||||
typedef struct _IO_FILE FILE;
|
||||
extern FILE *stdout;
|
||||
extern FILE *stdin;
|
||||
extern FILE *stderr;
|
||||
|
||||
typedef uint64_t time_t;
|
||||
typedef uint32_t pid_t;
|
||||
|
||||
time_t time(time_t*);
|
||||
pid_t getpid(void);
|
||||
int fprintf(FILE *, const char *, ...);
|
||||
';
|
||||
|
||||
$ffi = FFI::cdef($header, 'libc.so.6');
|
||||
|
||||
try {
|
||||
$ffi->time();
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e) . ": " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$ffi->time(null, null);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e) . ": " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$ffi->fprintf($ffi->stdout);
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e) . ": " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
try {
|
||||
$ffi->fprintf($ffi->stdout, 123, "Hello %s\n", "World");
|
||||
} catch (Throwable $e) {
|
||||
echo get_class($e) . ": " . $e->getMessage() . "\n";
|
||||
}
|
||||
?>
|
||||
--EXPECT--
|
||||
FFI\Exception: Incorrect number of arguments for C function 'time', expecting exactly 1 parameter
|
||||
FFI\Exception: Incorrect number of arguments for C function 'time', expecting exactly 1 parameter
|
||||
FFI\Exception: Incorrect number of arguments for C function 'fprintf', expecting at least 2 parameters
|
||||
FFI\Exception: Passing incompatible argument 2 of C function 'fprintf', expecting 'char*', found PHP 'int'
|
Loading…
Reference in New Issue
Block a user