From e03b78ecfadc4244bd2d6e1abc24d16c9ed92555 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 11 Oct 2024 20:08:35 +0200 Subject: [PATCH 01/23] implement getenv and putenv in go --- frankenphp.c | 112 +++++++++++++++++++++++++++++++++++++++++- frankenphp.go | 66 +++++++++++++++++++++++++ testdata/test-env.php | 31 ++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 testdata/test-env.php diff --git a/frankenphp.c b/frankenphp.c index 1f59893e7..b07f8caa0 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -242,6 +242,93 @@ PHP_FUNCTION(frankenphp_finish_request) { /* {{{ */ RETURN_TRUE; } /* }}} */ +/* {{{ Call go's putenv to prevent race conditions */ +PHP_FUNCTION(frankenphp_putenv) +{ + char *setting; + size_t setting_len; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(setting, setting_len) + ZEND_PARSE_PARAMETERS_END(); + + // Cast str_len to int (ensure it fits in an int) + if (setting_len > INT_MAX) { + php_error(E_WARNING, "String length exceeds maximum integer value"); + RETURN_FALSE; + } + + int result = go_putenv(setting, (int)setting_len); + + if (result) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } +} /* }}} */ + +/* {{{ Call go's getenv to prevent race conditions */ +PHP_FUNCTION(frankenphp_getenv) { + char *name = NULL; + size_t name_len = 0; + bool local_only = 0; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_STRING_OR_NULL(name, name_len) + Z_PARAM_BOOL(local_only) + ZEND_PARSE_PARAMETERS_END(); + + char *value = NULL; + int value_len = 0; + + int result = go_getenv(name, (int)name_len, &value, &value_len); + + if (result) { + if (name == NULL) { + // Initialize an empty array + array_init(return_value); + + char *env = value; + char *end = value + value_len; + while (env < end && *env != '\0') { + size_t len = strlen(env); + char *key_value = env; // key=value format + + // Find the position of '=' to split key and value + char *equal_sign = strchr(key_value, '='); + if (equal_sign != NULL) { + size_t key_len = equal_sign - key_value; + size_t val_len = len - key_len - 1; // Exclude '=' + + // Create PHP strings for key and value + zend_string *key = zend_string_init(key_value, key_len, 0); + zend_string *val = zend_string_init(equal_sign + 1, val_len, 0); + + // Add to the associative array + add_assoc_str(return_value, ZSTR_VAL(key), val); + + // Release the key string + zend_string_release(key); + } + + // Move to the next environment variable + env += len + 1; + } + + // Free the C string allocated in Go + free(value); + } else { + // Return the single environment variable as a string + RETVAL_STRINGL(value, value_len); + free(value); + } + } else { + // Environment variable does not exist + RETVAL_FALSE; + } +} /* }}} */ + /* {{{ Fetch all HTTP request headers */ PHP_FUNCTION(frankenphp_request_headers) { if (zend_parse_parameters_none() == FAILURE) { @@ -408,11 +495,34 @@ PHP_FUNCTION(headers_send) { RETURN_LONG(sapi_send_headers()); } +PHP_MINIT_FUNCTION(frankenphp) +{ + zend_function *func; + + // Override putenv + func = zend_hash_str_find_ptr(CG(function_table), "putenv", sizeof("putenv") - 1); + if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) { + ((zend_internal_function *)func)->handler = ZEND_FN(frankenphp_putenv); + } else { + php_error(E_WARNING, "Failed to find built-in putenv function"); + } + + // Override putenv + func = zend_hash_str_find_ptr(CG(function_table), "getenv", sizeof("getenv") - 1); + if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) { + ((zend_internal_function *)func)->handler = ZEND_FN(frankenphp_getenv); + } else { + php_error(E_WARNING, "Failed to find built-in getenv function"); + } + + return SUCCESS; +} + static zend_module_entry frankenphp_module = { STANDARD_MODULE_HEADER, "frankenphp", ext_functions, /* function table */ - NULL, /* initialization */ + PHP_MINIT(frankenphp), /* initialization */ NULL, /* shutdown */ NULL, /* request initialization */ NULL, /* request shutdown */ diff --git a/frankenphp.go b/frankenphp.go index b24f132ea..69c7db768 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -501,6 +501,72 @@ func ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) error return nil } +//export go_putenv +func go_putenv(str *C.char, length C.int) C.int { + // Create a byte slice from C string with a specified length + s := C.GoBytes(unsafe.Pointer(str), length) + + // Convert byte slice to string + envString := string(s) + + // Check if '=' is present in the string + if idx := strings.IndexByte(envString, '='); idx != -1 { + // '=' found, set the environment variable + key := envString[:idx] + val := envString[idx+1:] + + err := os.Setenv(key, val) + if err != nil { + return C.int(0) // Failure + } + } else { + // No '=', unset the environment variable + err := os.Unsetenv(envString) + if err != nil { + return C.int(0) // Failure + } + } + + return C.int(1) // Success +} + +//export go_getenv +func go_getenv(name *C.char, length C.int, value **C.char, value_len *C.int) C.int { + if name == nil { + // Get all environment variables + env := os.Environ() + // Concatenate them with null separators + concatenatedEnv := strings.Join(env, "\x00") + "\x00" // Add an extra null terminator at the end + // Convert to C string + *value = C.CString(concatenatedEnv) + // Set the length of the concatenated string + *value_len = C.int(len(concatenatedEnv)) + return C.int(1) // Success + } + + // Create a byte slice from C string with a specified length + nameBytes := C.GoBytes(unsafe.Pointer(name), length) + + // Convert byte slice to string + envName := string(nameBytes) + + // Get the environment variable value + envValue, exists := os.LookupEnv(envName) + if !exists { + // Environment variable does not exist + *value = nil + *value_len = 0 + return C.int(0) // Return 0 to indicate failure + } + + // Convert Go string to C string + cValue := C.CString(envValue) + *value = cValue + *value_len = C.int(len(envValue)) + + return C.int(1) // Return 1 to indicate success +} + //export go_handle_request func go_handle_request(threadIndex C.uintptr_t) bool { select { diff --git a/testdata/test-env.php b/testdata/test-env.php new file mode 100644 index 000000000..0d5b77a34 --- /dev/null +++ b/testdata/test-env.php @@ -0,0 +1,31 @@ + Date: Fri, 11 Oct 2024 20:19:01 +0200 Subject: [PATCH 02/23] fix typo --- frankenphp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frankenphp.c b/frankenphp.c index b07f8caa0..e2b2aaeda 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -507,7 +507,7 @@ PHP_MINIT_FUNCTION(frankenphp) php_error(E_WARNING, "Failed to find built-in putenv function"); } - // Override putenv + // Override getenv func = zend_hash_str_find_ptr(CG(function_table), "getenv", sizeof("getenv") - 1); if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) { ((zend_internal_function *)func)->handler = ZEND_FN(frankenphp_getenv); From 59ab1ef3806c1e1a64ce344939df821102d1c91d Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 11 Oct 2024 21:03:40 +0200 Subject: [PATCH 03/23] apply formatting --- frankenphp.c | 196 +++++++++++++++++++++++++-------------------------- 1 file changed, 98 insertions(+), 98 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index e2b2aaeda..8e91d5250 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -243,90 +243,89 @@ PHP_FUNCTION(frankenphp_finish_request) { /* {{{ */ } /* }}} */ /* {{{ Call go's putenv to prevent race conditions */ -PHP_FUNCTION(frankenphp_putenv) -{ - char *setting; - size_t setting_len; +PHP_FUNCTION(frankenphp_putenv) { + char *setting; + size_t setting_len; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_STRING(setting, setting_len) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STRING(setting, setting_len) + ZEND_PARSE_PARAMETERS_END(); - // Cast str_len to int (ensure it fits in an int) - if (setting_len > INT_MAX) { - php_error(E_WARNING, "String length exceeds maximum integer value"); - RETURN_FALSE; - } + // Cast str_len to int (ensure it fits in an int) + if (setting_len > INT_MAX) { + php_error(E_WARNING, "String length exceeds maximum integer value"); + RETURN_FALSE; + } - int result = go_putenv(setting, (int)setting_len); + int result = go_putenv(setting, (int)setting_len); - if (result) { - RETURN_TRUE; - } else { - RETURN_FALSE; - } + if (result) { + RETURN_TRUE; + } else { + RETURN_FALSE; + } } /* }}} */ /* {{{ Call go's getenv to prevent race conditions */ PHP_FUNCTION(frankenphp_getenv) { - char *name = NULL; - size_t name_len = 0; - bool local_only = 0; - - ZEND_PARSE_PARAMETERS_START(0, 2) - Z_PARAM_OPTIONAL - Z_PARAM_STRING_OR_NULL(name, name_len) - Z_PARAM_BOOL(local_only) - ZEND_PARSE_PARAMETERS_END(); - - char *value = NULL; - int value_len = 0; - - int result = go_getenv(name, (int)name_len, &value, &value_len); - - if (result) { - if (name == NULL) { - // Initialize an empty array - array_init(return_value); - - char *env = value; - char *end = value + value_len; - while (env < end && *env != '\0') { - size_t len = strlen(env); - char *key_value = env; // key=value format - - // Find the position of '=' to split key and value - char *equal_sign = strchr(key_value, '='); - if (equal_sign != NULL) { - size_t key_len = equal_sign - key_value; - size_t val_len = len - key_len - 1; // Exclude '=' - - // Create PHP strings for key and value - zend_string *key = zend_string_init(key_value, key_len, 0); - zend_string *val = zend_string_init(equal_sign + 1, val_len, 0); - - // Add to the associative array - add_assoc_str(return_value, ZSTR_VAL(key), val); - - // Release the key string - zend_string_release(key); - } - - // Move to the next environment variable - env += len + 1; - } - - // Free the C string allocated in Go - free(value); - } else { - // Return the single environment variable as a string - RETVAL_STRINGL(value, value_len); - free(value); - } - } else { - // Environment variable does not exist - RETVAL_FALSE; + char *name = NULL; + size_t name_len = 0; + bool local_only = 0; + + ZEND_PARSE_PARAMETERS_START(0, 2) + Z_PARAM_OPTIONAL + Z_PARAM_STRING_OR_NULL(name, name_len) + Z_PARAM_BOOL(local_only) + ZEND_PARSE_PARAMETERS_END(); + + char *value = NULL; + int value_len = 0; + + int result = go_getenv(name, (int)name_len, &value, &value_len); + + if (result) { + if (name == NULL) { + // Initialize an empty array + array_init(return_value); + + char *env = value; + char *end = value + value_len; + while (env < end && *env != '\0') { + size_t len = strlen(env); + char *key_value = env; // key=value format + + // Find the position of '=' to split key and value + char *equal_sign = strchr(key_value, '='); + if (equal_sign != NULL) { + size_t key_len = equal_sign - key_value; + size_t val_len = len - key_len - 1; // Exclude '=' + + // Create PHP strings for key and value + zend_string *key = zend_string_init(key_value, key_len, 0); + zend_string *val = zend_string_init(equal_sign + 1, val_len, 0); + + // Add to the associative array + add_assoc_str(return_value, ZSTR_VAL(key), val); + + // Release the key string + zend_string_release(key); } + + // Move to the next environment variable + env += len + 1; + } + + // Free the C string allocated in Go + free(value); + } else { + // Return the single environment variable as a string + RETVAL_STRINGL(value, value_len); + free(value); + } + } else { + // Environment variable does not exist + RETVAL_FALSE; + } } /* }}} */ /* {{{ Fetch all HTTP request headers */ @@ -495,38 +494,39 @@ PHP_FUNCTION(headers_send) { RETURN_LONG(sapi_send_headers()); } -PHP_MINIT_FUNCTION(frankenphp) -{ - zend_function *func; +PHP_MINIT_FUNCTION(frankenphp) { + zend_function *func; - // Override putenv - func = zend_hash_str_find_ptr(CG(function_table), "putenv", sizeof("putenv") - 1); - if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) { - ((zend_internal_function *)func)->handler = ZEND_FN(frankenphp_putenv); - } else { - php_error(E_WARNING, "Failed to find built-in putenv function"); - } + // Override putenv + func = zend_hash_str_find_ptr(CG(function_table), "putenv", + sizeof("putenv") - 1); + if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) { + ((zend_internal_function *)func)->handler = ZEND_FN(frankenphp_putenv); + } else { + php_error(E_WARNING, "Failed to find built-in putenv function"); + } - // Override getenv - func = zend_hash_str_find_ptr(CG(function_table), "getenv", sizeof("getenv") - 1); - if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) { - ((zend_internal_function *)func)->handler = ZEND_FN(frankenphp_getenv); - } else { - php_error(E_WARNING, "Failed to find built-in getenv function"); - } + // Override getenv + func = zend_hash_str_find_ptr(CG(function_table), "getenv", + sizeof("getenv") - 1); + if (func != NULL && func->type == ZEND_INTERNAL_FUNCTION) { + ((zend_internal_function *)func)->handler = ZEND_FN(frankenphp_getenv); + } else { + php_error(E_WARNING, "Failed to find built-in getenv function"); + } - return SUCCESS; + return SUCCESS; } static zend_module_entry frankenphp_module = { STANDARD_MODULE_HEADER, "frankenphp", - ext_functions, /* function table */ + ext_functions, /* function table */ PHP_MINIT(frankenphp), /* initialization */ - NULL, /* shutdown */ - NULL, /* request initialization */ - NULL, /* request shutdown */ - NULL, /* information */ + NULL, /* shutdown */ + NULL, /* request initialization */ + NULL, /* request shutdown */ + NULL, /* information */ TOSTRING(FRANKENPHP_VERSION), STANDARD_MODULE_PROPERTIES}; From 969ceeaa760cd5332da27629a16ca7f888121086 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 11 Oct 2024 21:05:29 +0200 Subject: [PATCH 04/23] return a bool --- frankenphp.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/frankenphp.go b/frankenphp.go index 69c7db768..de335259c 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -502,7 +502,7 @@ func ServeHTTP(responseWriter http.ResponseWriter, request *http.Request) error } //export go_putenv -func go_putenv(str *C.char, length C.int) C.int { +func go_putenv(str *C.char, length C.int) C.bool { // Create a byte slice from C string with a specified length s := C.GoBytes(unsafe.Pointer(str), length) @@ -517,21 +517,21 @@ func go_putenv(str *C.char, length C.int) C.int { err := os.Setenv(key, val) if err != nil { - return C.int(0) // Failure + return false // Failure } } else { // No '=', unset the environment variable err := os.Unsetenv(envString) if err != nil { - return C.int(0) // Failure + return false // Failure } } - return C.int(1) // Success + return true // Success } //export go_getenv -func go_getenv(name *C.char, length C.int, value **C.char, value_len *C.int) C.int { +func go_getenv(name *C.char, length C.int, value **C.char, value_len *C.int) C.bool { if name == nil { // Get all environment variables env := os.Environ() @@ -541,7 +541,7 @@ func go_getenv(name *C.char, length C.int, value **C.char, value_len *C.int) C.i *value = C.CString(concatenatedEnv) // Set the length of the concatenated string *value_len = C.int(len(concatenatedEnv)) - return C.int(1) // Success + return true // Success } // Create a byte slice from C string with a specified length @@ -556,7 +556,7 @@ func go_getenv(name *C.char, length C.int, value **C.char, value_len *C.int) C.i // Environment variable does not exist *value = nil *value_len = 0 - return C.int(0) // Return 0 to indicate failure + return false // Return 0 to indicate failure } // Convert Go string to C string @@ -564,7 +564,7 @@ func go_getenv(name *C.char, length C.int, value **C.char, value_len *C.int) C.i *value = cValue *value_len = C.int(len(envValue)) - return C.int(1) // Return 1 to indicate success + return true // Return 1 to indicate success } //export go_handle_request From 098c79567512ec08726b753afac141d090c399cc Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 11 Oct 2024 21:06:23 +0200 Subject: [PATCH 05/23] prevent ENV= from crashing --- frankenphp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frankenphp.go b/frankenphp.go index de335259c..6964de870 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -510,7 +510,7 @@ func go_putenv(str *C.char, length C.int) C.bool { envString := string(s) // Check if '=' is present in the string - if idx := strings.IndexByte(envString, '='); idx != -1 { + if idx := strings.IndexByte(envString, '='); idx != -1 && idx < len(envString)-1 { // '=' found, set the environment variable key := envString[:idx] val := envString[idx+1:] From 660dc991764d27e637f1ab708324b9213870ea09 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 11 Oct 2024 21:07:22 +0200 Subject: [PATCH 06/23] optimization --- frankenphp.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frankenphp.go b/frankenphp.go index 6964de870..ad763e9ea 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -515,8 +515,7 @@ func go_putenv(str *C.char, length C.int) C.bool { key := envString[:idx] val := envString[idx+1:] - err := os.Setenv(key, val) - if err != nil { + if os.Setenv(key, val) != nil { return false // Failure } } else { From 0c795b91ef99d30004972aa8679cf574188d49e0 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 11 Oct 2024 21:07:50 +0200 Subject: [PATCH 07/23] optimization --- frankenphp.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frankenphp.go b/frankenphp.go index ad763e9ea..dea0f5e26 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -520,8 +520,7 @@ func go_putenv(str *C.char, length C.int) C.bool { } } else { // No '=', unset the environment variable - err := os.Unsetenv(envString) - if err != nil { + if os.Unsetenv(envString) != nil { return false // Failure } } From e6031cdf4bdef8736ec6e64b765b4d0d44f18816 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 12 Oct 2024 08:28:43 +0200 Subject: [PATCH 08/23] split env workflows and use go_strings --- frankenphp.c | 77 ++++++++++++++++++++++++++++++++------------------- frankenphp.go | 56 ++++++++++++++++++++++--------------- 2 files changed, 81 insertions(+), 52 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 8e91d5250..2248412f5 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -278,48 +278,67 @@ PHP_FUNCTION(frankenphp_getenv) { Z_PARAM_BOOL(local_only) ZEND_PARSE_PARAMETERS_END(); - char *value = NULL; - int value_len = 0; + if (!name) { + struct go_getfullenv_return full_env = go_getfullenv(thread_index); - int result = go_getenv(name, (int)name_len, &value, &value_len); + array_init(return_value); + for (int i = 0; i < full_env.r1; i++) { + go_string key = full_env.r0[i * 2]; + go_string val = full_env.r0[i * 2 + 1]; - if (result) { - if (name == NULL) { - // Initialize an empty array - array_init(return_value); + // create PHP strings for key and value + zend_string *key_str = zend_string_init(key.data, key.len, 0); + zend_string *val_str = zend_string_init(val.data, val.len, 0); + + // add to the associative array + add_assoc_str(return_value, ZSTR_VAL(key_str), val_str); + + // release the key string + zend_string_release(key_str); + } - char *env = value; - char *end = value + value_len; - while (env < end && *env != '\0') { - size_t len = strlen(env); - char *key_value = env; // key=value format + return; + } - // Find the position of '=' to split key and value - char *equal_sign = strchr(key_value, '='); - if (equal_sign != NULL) { - size_t key_len = equal_sign - key_value; - size_t val_len = len - key_len - 1; // Exclude '=' + go_string *value = NULL; + int value_len = 0; - // Create PHP strings for key and value - zend_string *key = zend_string_init(key_value, key_len, 0); - zend_string *val = zend_string_init(equal_sign + 1, val_len, 0); + go_string gname = {name_len, name}; - // Add to the associative array - add_assoc_str(return_value, ZSTR_VAL(key), val); + struct go_getenv_return result = go_getenv(thread_index, &gname); - // Release the key string - zend_string_release(key); - } + if (result.r0) { + if (name == NULL) { + // Initialize an empty array + array_init(return_value); - // Move to the next environment variable - env += len + 1; + char *key; + char *val; + + for (int i = 0; i < value_len; i++) { + go_string *env = value + i; + key = env->data; + // find the equal sign + val = strchr(key, '='); + if (val != NULL) { + // create PHP strings for key and value + zend_string *key_str = zend_string_init(key, val - key, 0); + zend_string *val_str = + zend_string_init(val + 1, env->len - (val - key) - 1, 0); + + // add to the associative array + add_assoc_str(return_value, ZSTR_VAL(key_str), val_str); + + // release the key string + zend_string_release(key_str); + } } - // Free the C string allocated in Go + // Free the strings allocated in Go free(value); } else { // Return the single environment variable as a string - RETVAL_STRINGL(value, value_len); + RETVAL_STRINGL(result.r1->data, result.r1->len); free(value); } } else { diff --git a/frankenphp.go b/frankenphp.go index dea0f5e26..caa16e122 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -528,41 +528,51 @@ func go_putenv(str *C.char, length C.int) C.bool { return true // Success } -//export go_getenv -func go_getenv(name *C.char, length C.int, value **C.char, value_len *C.int) C.bool { - if name == nil { - // Get all environment variables - env := os.Environ() - // Concatenate them with null separators - concatenatedEnv := strings.Join(env, "\x00") + "\x00" // Add an extra null terminator at the end - // Convert to C string - *value = C.CString(concatenatedEnv) - // Set the length of the concatenated string - *value_len = C.int(len(concatenatedEnv)) - return true // Success +//export go_getfullenv +func go_getfullenv(threadIndex C.uintptr_t) (*C.go_string, C.size_t) { + thread := phpThreads[threadIndex] + + env := os.Environ() + goStrings := make([]C.go_string, len(env)*2) + + for i, envVar := range env { + key, val, _ := strings.Cut(envVar, "=") + k := unsafe.StringData(key) + v := unsafe.StringData(val) + thread.Pin(k) + thread.Pin(v) + + goStrings[i*2] = C.go_string{C.size_t(len(key)), (*C.char)(unsafe.Pointer(k))} + goStrings[i*2+1] = C.go_string{C.size_t(len(val)), (*C.char)(unsafe.Pointer(v))} } - // Create a byte slice from C string with a specified length - nameBytes := C.GoBytes(unsafe.Pointer(name), length) + value := unsafe.SliceData(goStrings) + thread.Pin(value) - // Convert byte slice to string - envName := string(nameBytes) + return value, C.size_t(len(env)) +} + +//export go_getenv +func go_getenv(threadIndex C.uintptr_t, name *C.go_string) (C.bool, *C.go_string) { + thread := phpThreads[threadIndex] + + // Create a byte slice from C string with a specified length + envName := C.GoStringN(name.data, C.int(name.len)) // Get the environment variable value envValue, exists := os.LookupEnv(envName) if !exists { // Environment variable does not exist - *value = nil - *value_len = 0 - return false // Return 0 to indicate failure + return false, nil // Return 0 to indicate failure } // Convert Go string to C string - cValue := C.CString(envValue) - *value = cValue - *value_len = C.int(len(envValue)) + val := unsafe.StringData(envValue) + thread.Pin(val) + value := &C.go_string{C.size_t(len(envValue)), (*C.char)(unsafe.Pointer(val))} + thread.Pin(value) - return true // Return 1 to indicate success + return true, value // Return 1 to indicate success } //export go_handle_request From 7b4c8ab3815c69d4613eeb78341ffcb1f489a01f Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 12 Oct 2024 08:41:45 +0200 Subject: [PATCH 09/23] clean up unused code --- frankenphp.c | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 2248412f5..986e9b063 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -300,47 +300,13 @@ PHP_FUNCTION(frankenphp_getenv) { return; } - go_string *value = NULL; - int value_len = 0; - go_string gname = {name_len, name}; struct go_getenv_return result = go_getenv(thread_index, &gname); if (result.r0) { - if (name == NULL) { - // Initialize an empty array - array_init(return_value); - - char *key; - char *val; - - for (int i = 0; i < value_len; i++) { - go_string *env = value + i; - key = env->data; - // find the equal sign - val = strchr(key, '='); - if (val != NULL) { - // create PHP strings for key and value - zend_string *key_str = zend_string_init(key, val - key, 0); - zend_string *val_str = - zend_string_init(val + 1, env->len - (val - key) - 1, 0); - - // add to the associative array - add_assoc_str(return_value, ZSTR_VAL(key_str), val_str); - - // release the key string - zend_string_release(key_str); - } - } - - // Free the strings allocated in Go - free(value); - } else { - // Return the single environment variable as a string - RETVAL_STRINGL(result.r1->data, result.r1->len); - free(value); - } + // Return the single environment variable as a string + RETVAL_STRINGL(result.r1->data, result.r1->len); } else { // Environment variable does not exist RETVAL_FALSE; From 4ef55f942e12dd41fed63d789e0663854b2d499d Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 12 Oct 2024 08:55:19 +0200 Subject: [PATCH 10/23] update tests --- frankenphp_test.go | 19 ++++++++++++++ testdata/test-env.php | 58 ++++++++++++++++++++++++------------------- 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/frankenphp_test.go b/frankenphp_test.go index d6ca541ab..ea2294ece 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -622,6 +622,25 @@ func TestFailingWorker(t *testing.T) { }, &testOptions{workerScript: "failing-worker.php"}) } +func TestEnv(t *testing.T) { + testEnv(t, &testOptions{}) +} +func TestEnvWorker(t *testing.T) { + testEnv(t, &testOptions{workerScript: "test-env.php"}) +} +func testEnv(t *testing.T, opts *testOptions) { + runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) { + req := httptest.NewRequest("GET", fmt.Sprintf("http://example.com/test-env.php?var=%d", i), nil) + w := httptest.NewRecorder() + handler(w, req) + + resp := w.Result() + body, _ := io.ReadAll(resp.Body) + + assert.Equal(t, fmt.Sprintf("Set MY_VAR successfully.\nMY_VAR = HelloWorld\nUnset MY_VAR successfully.\nMY_VAR is unset.\nUnset NON_EXISTING_VAR successfully.\n"), string(body)) + }, opts) +} + func TestFileUpload_module(t *testing.T) { testFileUpload(t, &testOptions{}) } func TestFileUpload_worker(t *testing.T) { testFileUpload(t, &testOptions{workerScript: "file-upload.php"}) diff --git a/testdata/test-env.php b/testdata/test-env.php index 0d5b77a34..bac4a2654 100644 --- a/testdata/test-env.php +++ b/testdata/test-env.php @@ -1,31 +1,37 @@ Date: Sat, 12 Oct 2024 08:57:58 +0200 Subject: [PATCH 11/23] remove useless sprintf --- frankenphp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frankenphp_test.go b/frankenphp_test.go index ea2294ece..54b980bed 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -637,7 +637,7 @@ func testEnv(t *testing.T, opts *testOptions) { resp := w.Result() body, _ := io.ReadAll(resp.Body) - assert.Equal(t, fmt.Sprintf("Set MY_VAR successfully.\nMY_VAR = HelloWorld\nUnset MY_VAR successfully.\nMY_VAR is unset.\nUnset NON_EXISTING_VAR successfully.\n"), string(body)) + assert.Equal(t, "Set MY_VAR successfully.\nMY_VAR = HelloWorld\nUnset MY_VAR successfully.\nMY_VAR is unset.\nUnset NON_EXISTING_VAR successfully.\n", string(body)) }, opts) } From 929a2c0a783c9cf04f20138af90091f1e2e83eb3 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 12 Oct 2024 09:11:03 +0200 Subject: [PATCH 12/23] see if this fixes the asan issues --- frankenphp.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 986e9b063..45d354327 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -753,6 +753,25 @@ void frankenphp_register_bulk_variables(go_string known_variables[27], } } +void get_full_env(zval *track_vars_array) { + struct go_getfullenv_return full_env = go_getfullenv(thread_index); + + for (int i = 0; i < full_env.r1; i++) { + go_string key = full_env.r0[i * 2]; + go_string val = full_env.r0[i * 2 + 1]; + + // create PHP strings for key and value + zend_string *key_str = zend_string_init(key.data, key.len, 0); + zend_string *val_str = zend_string_init(val.data, val.len, 0); + + // add to the associative array + add_assoc_str(track_vars_array, ZSTR_VAL(key_str), val_str); + + // release the key string + zend_string_release(key_str); + } +} + static void frankenphp_register_variables(zval *track_vars_array) { /* https://www.php.net/manual/en/reserved.variables.server.php */ @@ -764,7 +783,8 @@ static void frankenphp_register_variables(zval *track_vars_array) { /* in non-worker mode we import the os environment regularly */ if (!ctx->has_main_request) { - php_import_environment_variables(track_vars_array); + get_full_env(track_vars_array); + //php_import_environment_variables(track_vars_array); go_register_variables(thread_index, track_vars_array); return; } @@ -773,7 +793,8 @@ static void frankenphp_register_variables(zval *track_vars_array) { if (os_environment == NULL) { os_environment = malloc(sizeof(zval)); array_init(os_environment); - php_import_environment_variables(os_environment); + get_full_env(os_environment); + //php_import_environment_variables(os_environment); } zend_hash_copy(Z_ARR_P(track_vars_array), Z_ARR_P(os_environment), (copy_ctor_func_t)zval_add_ref); From 71279ff64dd15aaff5c3fc1285d74c21e83be00f Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 12 Oct 2024 09:22:47 +0200 Subject: [PATCH 13/23] clean up comments --- frankenphp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frankenphp.c b/frankenphp.c index 45d354327..8d156c651 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -776,7 +776,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { /* https://www.php.net/manual/en/reserved.variables.server.php */ /* In CGI mode, we consider the environment to be a part of the server - * variables + * variables. */ frankenphp_server_context *ctx = SG(server_context); From 2518bbb551dc00d12a05900ed611717a971eaa93 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 12 Oct 2024 16:03:29 +0200 Subject: [PATCH 14/23] check that VAR= works correctly and use actual php to validate the behavior --- frankenphp.go | 2 +- frankenphp_test.go | 7 ++++++- testdata/test-env.php | 17 +++++++++++++++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/frankenphp.go b/frankenphp.go index caa16e122..70e7f6f67 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -510,7 +510,7 @@ func go_putenv(str *C.char, length C.int) C.bool { envString := string(s) // Check if '=' is present in the string - if idx := strings.IndexByte(envString, '='); idx != -1 && idx < len(envString)-1 { + if idx := strings.IndexByte(envString, '='); idx != -1 { // '=' found, set the environment variable key := envString[:idx] val := envString[idx+1:] diff --git a/frankenphp_test.go b/frankenphp_test.go index 54b980bed..81e4c0091 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -637,7 +637,12 @@ func testEnv(t *testing.T, opts *testOptions) { resp := w.Result() body, _ := io.ReadAll(resp.Body) - assert.Equal(t, "Set MY_VAR successfully.\nMY_VAR = HelloWorld\nUnset MY_VAR successfully.\nMY_VAR is unset.\nUnset NON_EXISTING_VAR successfully.\n", string(body)) + // execute the script as regular php script + cmd := exec.Command("php", "testdata/test-env.php", strconv.Itoa(i)) + stdoutStderr, err := cmd.CombinedOutput() + require.NoError(t, err) + + assert.Equal(t, string(stdoutStderr), string(body)) }, opts) } diff --git a/testdata/test-env.php b/testdata/test-env.php index bac4a2654..7588fa646 100644 --- a/testdata/test-env.php +++ b/testdata/test-env.php @@ -3,7 +3,7 @@ require_once __DIR__.'/_executor.php'; return function() { - $var = 'MY_VAR_' . $_GET['var']; + $var = 'MY_VAR_' . ($_GET['var'] ?? ''); // Setting an environment variable $result = putenv("$var=HelloWorld"); if ($result) { @@ -27,8 +27,21 @@ echo "Failed to unset MY_VAR.\n"; } + $result = putenv("$var="); + if ($result) { + echo "MY_VAR set to empty successfully.\n"; + $value = getenv($var); + if ($value === false) { + echo "MY_VAR is unset.\n"; + } else { + echo "MY_VAR = " . $value . "\n"; + } + } else { + echo "Failed to set MY_VAR.\n"; + } + // Attempt to unset a non-existing variable - $result = putenv('NON_EXISTING_VAR' . $_GET['var']); + $result = putenv('NON_EXISTING_VAR' . ($_GET['var'] ?? '')); if ($result) { echo "Unset NON_EXISTING_VAR successfully.\n"; } else { From 61e4e20b580177337af4c4c1e05af5511a9ae9e0 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sat, 12 Oct 2024 16:12:33 +0200 Subject: [PATCH 15/23] move all unpinning to the end of the request --- frankenphp.c | 2 -- frankenphp.go | 8 +------- worker.go | 2 ++ 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 8d156c651..f3feab57c 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -331,8 +331,6 @@ PHP_FUNCTION(frankenphp_request_headers) { add_assoc_stringl_ex(return_value, key.data, key.len, val.data, val.len); } - - go_apache_request_cleanup(thread_index); } /* }}} */ diff --git a/frankenphp.go b/frankenphp.go index 70e7f6f67..8c4d4158e 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -592,6 +592,7 @@ func go_handle_request(threadIndex C.uintptr_t) bool { defer func() { maybeCloseContext(fc) thread.mainRequest = nil + thread.Unpin() }() if err := updateServerContext(r, true, false); err != nil { @@ -715,8 +716,6 @@ func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) { C.frankenphp_register_bulk_variables(&knownVariables[0], dvsd, C.size_t(l), trackVarsArray) - thread.Unpin() - fc.env = nil } @@ -759,11 +758,6 @@ func go_apache_request_headers(threadIndex C.uintptr_t, hasActiveRequest bool) ( return sd, C.size_t(len(r.Header)) } -//export go_apache_request_cleanup -func go_apache_request_cleanup(threadIndex C.uintptr_t) { - phpThreads[threadIndex].Unpin() -} - func addHeader(fc *FrankenPHPContext, cString *C.char, length C.int) { parts := strings.SplitN(C.GoStringN(cString, length), ": ", 2) if len(parts) != 2 { diff --git a/worker.go b/worker.go index 0aaa2c2a5..0a7fa0fb3 100644 --- a/worker.go +++ b/worker.go @@ -310,4 +310,6 @@ func go_frankenphp_finish_request(threadIndex C.uintptr_t, isWorkerRequest bool) c.Write(fields...) } + + thread.Unpin() } From a3cec5b2595741a48678e732ffa07ede7f55f590 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 13 Oct 2024 11:01:49 +0200 Subject: [PATCH 16/23] handle the case where php is not installed --- frankenphp_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frankenphp_test.go b/frankenphp_test.go index 81e4c0091..9d75c6076 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -640,7 +640,10 @@ func testEnv(t *testing.T, opts *testOptions) { // execute the script as regular php script cmd := exec.Command("php", "testdata/test-env.php", strconv.Itoa(i)) stdoutStderr, err := cmd.CombinedOutput() - require.NoError(t, err) + if err != nil { + // php is not installed or other issue, use the hardcoded output below: + stdoutStderr = []byte("Set MY_VAR successfully.\nMY_VAR = HelloWorld\nUnset MY_VAR successfully.\nMY_VAR is unset.\nMY_VAR set to empty successfully.\nMY_VAR =\nUnset NON_EXISTING_VAR successfully.\n") + } assert.Equal(t, string(stdoutStderr), string(body)) }, opts) From 7d64751a49aaaf9c55815a98504f5c527b959d94 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Sun, 13 Oct 2024 11:41:42 +0200 Subject: [PATCH 17/23] fix copy-paste --- frankenphp_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frankenphp_test.go b/frankenphp_test.go index 9d75c6076..b02ef9e74 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -642,7 +642,7 @@ func testEnv(t *testing.T, opts *testOptions) { stdoutStderr, err := cmd.CombinedOutput() if err != nil { // php is not installed or other issue, use the hardcoded output below: - stdoutStderr = []byte("Set MY_VAR successfully.\nMY_VAR = HelloWorld\nUnset MY_VAR successfully.\nMY_VAR is unset.\nMY_VAR set to empty successfully.\nMY_VAR =\nUnset NON_EXISTING_VAR successfully.\n") + stdoutStderr = []byte("Set MY_VAR successfully.\nMY_VAR = HelloWorld\nUnset MY_VAR successfully.\nMY_VAR is unset.\nMY_VAR set to empty successfully.\nMY_VAR = \nUnset NON_EXISTING_VAR successfully.\n") } assert.Equal(t, string(stdoutStderr), string(body)) From 67a547c7cf97723bcc8de58c3941e5eb7bc96bcc Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Tue, 15 Oct 2024 19:22:54 +0200 Subject: [PATCH 18/23] optimization --- frankenphp.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index f3feab57c..57261d166 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -257,9 +257,7 @@ PHP_FUNCTION(frankenphp_putenv) { RETURN_FALSE; } - int result = go_putenv(setting, (int)setting_len); - - if (result) { + if (go_putenv(setting, (int)setting_len)) { RETURN_TRUE; } else { RETURN_FALSE; From 02e85457e0ce0d6285b7807412e2e48ec3202c7f Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Tue, 15 Oct 2024 19:24:30 +0200 Subject: [PATCH 19/23] use strings.cut --- frankenphp.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frankenphp.go b/frankenphp.go index 8c4d4158e..29a54d902 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -510,11 +510,7 @@ func go_putenv(str *C.char, length C.int) C.bool { envString := string(s) // Check if '=' is present in the string - if idx := strings.IndexByte(envString, '='); idx != -1 { - // '=' found, set the environment variable - key := envString[:idx] - val := envString[idx+1:] - + if key, val, found := strings.Cut(envString, "="); found { if os.Setenv(key, val) != nil { return false // Failure } From d2bf2d97d91ca22567774cd850e7fa818d866510 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Tue, 15 Oct 2024 19:47:14 +0200 Subject: [PATCH 20/23] fix lint --- frankenphp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 57261d166..00e044ea5 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -780,7 +780,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { /* in non-worker mode we import the os environment regularly */ if (!ctx->has_main_request) { get_full_env(track_vars_array); - //php_import_environment_variables(track_vars_array); + // php_import_environment_variables(track_vars_array); go_register_variables(thread_index, track_vars_array); return; } @@ -790,7 +790,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { os_environment = malloc(sizeof(zval)); array_init(os_environment); get_full_env(os_environment); - //php_import_environment_variables(os_environment); + // php_import_environment_variables(os_environment); } zend_hash_copy(Z_ARR_P(track_vars_array), Z_ARR_P(os_environment), (copy_ctor_func_t)zval_add_ref); From f97a34b6f6ee2fbcea7a2267007f66bb6fac6ba7 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Tue, 15 Oct 2024 21:25:22 +0200 Subject: [PATCH 21/23] override how env is filled --- frankenphp.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 00e044ea5..3884a7561 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -563,7 +563,28 @@ int frankenphp_update_server_context( return SUCCESS; } +PHPAPI void get_full_env(zval *track_vars_array) { + struct go_getfullenv_return full_env = go_getfullenv(thread_index); + + for (int i = 0; i < full_env.r1; i++) { + go_string key = full_env.r0[i * 2]; + go_string val = full_env.r0[i * 2 + 1]; + + // create PHP strings for key and value + zend_string *key_str = zend_string_init(key.data, key.len, 0); + zend_string *val_str = zend_string_init(val.data, val.len, 0); + + // add to the associative array + add_assoc_str(track_vars_array, ZSTR_VAL(key_str), val_str); + + // release the key string + zend_string_release(key_str); + } +} + static int frankenphp_startup(sapi_module_struct *sapi_module) { + php_import_environment_variables = get_full_env; + return php_module_startup(sapi_module, &frankenphp_module); } @@ -749,25 +770,6 @@ void frankenphp_register_bulk_variables(go_string known_variables[27], } } -void get_full_env(zval *track_vars_array) { - struct go_getfullenv_return full_env = go_getfullenv(thread_index); - - for (int i = 0; i < full_env.r1; i++) { - go_string key = full_env.r0[i * 2]; - go_string val = full_env.r0[i * 2 + 1]; - - // create PHP strings for key and value - zend_string *key_str = zend_string_init(key.data, key.len, 0); - zend_string *val_str = zend_string_init(val.data, val.len, 0); - - // add to the associative array - add_assoc_str(track_vars_array, ZSTR_VAL(key_str), val_str); - - // release the key string - zend_string_release(key_str); - } -} - static void frankenphp_register_variables(zval *track_vars_array) { /* https://www.php.net/manual/en/reserved.variables.server.php */ From 1e5abb203edcf17304f17c4b88cb5b1c2feadc44 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 18 Oct 2024 12:36:36 +0200 Subject: [PATCH 22/23] reuse fullenv --- frankenphp.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 3884a7561..ee6453b25 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -277,23 +277,8 @@ PHP_FUNCTION(frankenphp_getenv) { ZEND_PARSE_PARAMETERS_END(); if (!name) { - struct go_getfullenv_return full_env = go_getfullenv(thread_index); - array_init(return_value); - for (int i = 0; i < full_env.r1; i++) { - go_string key = full_env.r0[i * 2]; - go_string val = full_env.r0[i * 2 + 1]; - - // create PHP strings for key and value - zend_string *key_str = zend_string_init(key.data, key.len, 0); - zend_string *val_str = zend_string_init(val.data, val.len, 0); - - // add to the associative array - add_assoc_str(return_value, ZSTR_VAL(key_str), val_str); - - // release the key string - zend_string_release(key_str); - } + go_getfullenv((uintptr_t) return_value); return; } From b68cf50b1d85b470829f17115a2d14a2db0fa0b7 Mon Sep 17 00:00:00 2001 From: Robert Landers Date: Fri, 18 Oct 2024 12:43:13 +0200 Subject: [PATCH 23/23] use corect function --- frankenphp.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index ee6453b25..0cf9d550f 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -150,6 +150,25 @@ static void frankenphp_worker_request_shutdown() { SG(rfc1867_uploaded_files) = NULL; } +PHPAPI void get_full_env(zval *track_vars_array) { + struct go_getfullenv_return full_env = go_getfullenv(thread_index); + + for (int i = 0; i < full_env.r1; i++) { + go_string key = full_env.r0[i * 2]; + go_string val = full_env.r0[i * 2 + 1]; + + // create PHP strings for key and value + zend_string *key_str = zend_string_init(key.data, key.len, 0); + zend_string *val_str = zend_string_init(val.data, val.len, 0); + + // add to the associative array + add_assoc_str(track_vars_array, ZSTR_VAL(key_str), val_str); + + // release the key string + zend_string_release(key_str); + } +} + /* Adapted from php_request_startup() */ static int frankenphp_worker_request_startup() { int retval = SUCCESS; @@ -278,7 +297,7 @@ PHP_FUNCTION(frankenphp_getenv) { if (!name) { array_init(return_value); - go_getfullenv((uintptr_t) return_value); + get_full_env(return_value); return; } @@ -548,25 +567,6 @@ int frankenphp_update_server_context( return SUCCESS; } -PHPAPI void get_full_env(zval *track_vars_array) { - struct go_getfullenv_return full_env = go_getfullenv(thread_index); - - for (int i = 0; i < full_env.r1; i++) { - go_string key = full_env.r0[i * 2]; - go_string val = full_env.r0[i * 2 + 1]; - - // create PHP strings for key and value - zend_string *key_str = zend_string_init(key.data, key.len, 0); - zend_string *val_str = zend_string_init(val.data, val.len, 0); - - // add to the associative array - add_assoc_str(track_vars_array, ZSTR_VAL(key_str), val_str); - - // release the key string - zend_string_release(key_str); - } -} - static int frankenphp_startup(sapi_module_struct *sapi_module) { php_import_environment_variables = get_full_env;