11package frankenphp
22
3+ // #cgo nocallback frankenphp_init_persistent_string
4+ // #cgo nocallback frankenphp_add_assoc_str_ex
5+ // #cgo noescape frankenphp_init_persistent_string
6+ // #cgo noescape frankenphp_add_assoc_str_ex
37// #include "frankenphp.h"
48import "C"
59import (
@@ -8,67 +12,106 @@ import (
812 "unsafe"
913)
1014
15+ func initializeEnv () map [string ]* C.zend_string {
16+ env := os .Environ ()
17+ envMap := make (map [string ]* C.zend_string , len (env ))
18+
19+ for _ , envVar := range env {
20+ key , val , _ := strings .Cut (envVar , "=" )
21+ envMap [key ] = C .frankenphp_init_persistent_string (toUnsafeChar (val ), C .size_t (len (val )))
22+ }
23+
24+ return envMap
25+ }
26+
27+ // get the main thread env or the thread specific env
28+ func getSandboxedEnv (thread * phpThread ) map [string ]* C.zend_string {
29+ if thread .sandboxedEnv != nil {
30+ return thread .sandboxedEnv
31+ }
32+
33+ return mainThread .sandboxedEnv
34+ }
35+
36+ func clearSandboxedEnv (thread * phpThread ) {
37+ if thread .sandboxedEnv == nil {
38+ return
39+ }
40+
41+ for key , val := range thread .sandboxedEnv {
42+ valInMainThread , ok := mainThread .sandboxedEnv [key ]
43+ if ! ok || val != valInMainThread {
44+ C .free (unsafe .Pointer (val ))
45+ }
46+ }
47+
48+ thread .sandboxedEnv = nil
49+ }
50+
51+ // if an env var already exists, it needs to be freed
52+ func removeEnvFromThread (thread * phpThread , key string ) {
53+ valueInThread , existsInThread := thread .sandboxedEnv [key ]
54+ if ! existsInThread {
55+ return
56+ }
57+
58+ valueInMainThread , ok := mainThread .sandboxedEnv [key ]
59+ if ! ok || valueInThread != valueInMainThread {
60+ C .free (unsafe .Pointer (valueInThread ))
61+ }
62+
63+ delete (thread .sandboxedEnv , key )
64+ }
65+
66+ // copy the main thread env to the thread specific env
67+ func cloneSandboxedEnv (thread * phpThread ) {
68+ if thread .sandboxedEnv != nil {
69+ return
70+ }
71+ thread .sandboxedEnv = make (map [string ]* C.zend_string , len (mainThread .sandboxedEnv ))
72+ for key , value := range mainThread .sandboxedEnv {
73+ thread .sandboxedEnv [key ] = value
74+ }
75+ }
76+
1177//export go_putenv
12- func go_putenv (str * C.char , length C.int ) C.bool {
78+ func go_putenv (threadIndex C.uintptr_t , str * C.char , length C.int ) C.bool {
79+ thread := phpThreads [threadIndex ]
1380 envString := C .GoStringN (str , length )
81+ cloneSandboxedEnv (thread )
1482
1583 // Check if '=' is present in the string
1684 if key , val , found := strings .Cut (envString , "=" ); found {
85+ removeEnvFromThread (thread , key )
86+ thread .sandboxedEnv [key ] = C .frankenphp_init_persistent_string (toUnsafeChar (val ), C .size_t (len (val )))
1787 return os .Setenv (key , val ) == nil
1888 }
1989
2090 // No '=', unset the environment variable
91+ removeEnvFromThread (thread , envString )
2192 return os .Unsetenv (envString ) == nil
2293}
2394
2495//export go_getfullenv
25- func go_getfullenv (threadIndex C.uintptr_t ) ( * C.go_string , C. size_t ) {
96+ func go_getfullenv (threadIndex C.uintptr_t , trackVarsArray * C.zval ) {
2697 thread := phpThreads [threadIndex ]
98+ env := getSandboxedEnv (thread )
2799
28- env := os .Environ ()
29- goStrings := make ([]C.go_string , len (env )* 2 )
30-
31- for i , envVar := range env {
32- key , val , _ := strings .Cut (envVar , "=" )
33- goStrings [i * 2 ] = C.go_string {C .size_t (len (key )), thread .pinString (key )}
34- goStrings [i * 2 + 1 ] = C.go_string {C .size_t (len (val )), thread .pinString (val )}
100+ for key , val := range env {
101+ C .frankenphp_add_assoc_str_ex (trackVarsArray , toUnsafeChar (key ), C .size_t (len (key )), val )
35102 }
36-
37- value := unsafe .SliceData (goStrings )
38- thread .Pin (value )
39-
40- return value , C .size_t (len (env ))
41103}
42104
43105//export go_getenv
44- func go_getenv (threadIndex C.uintptr_t , name * C.go_string ) (C.bool , * C.go_string ) {
106+ func go_getenv (threadIndex C.uintptr_t , name * C.char ) (C.bool , * C.zend_string ) {
45107 thread := phpThreads [threadIndex ]
46108
47- // Create a byte slice from C string with a specified length
48- envName := C .GoStringN (name .data , C .int (name .len ))
49-
50109 // Get the environment variable value
51- envValue , exists := os . LookupEnv ( envName )
110+ envValue , exists := getSandboxedEnv ( thread )[ C . GoString ( name )]
52111 if ! exists {
53112 // Environment variable does not exist
54113 return false , nil // Return 0 to indicate failure
55114 }
56115
57- // Convert Go string to C string
58- value := & C.go_string {C .size_t (len (envValue )), thread .pinString (envValue )}
59- thread .Pin (value )
60-
61- return true , value // Return 1 to indicate success
62- }
63-
64- //export go_sapi_getenv
65- func go_sapi_getenv (threadIndex C.uintptr_t , name * C.go_string ) * C.char {
66- envName := C .GoStringN (name .data , C .int (name .len ))
67-
68- envValue , exists := os .LookupEnv (envName )
69- if ! exists {
70- return nil
71- }
72-
73- return phpThreads [threadIndex ].pinCString (envValue )
116+ return true , envValue // Return 1 to indicate success
74117}
0 commit comments