From f3bf7db3aa168a90087d8fce0989c85e0f7d021e Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 10 Jan 2026 19:53:47 +0100 Subject: [PATCH 01/32] test. --- cgi.go | 152 ++++++++++++++++++------------ frankenphp.c | 3 + internal/phpheaders/phpheaders.go | 81 ++++++++++++++++ phpmainthread.go | 21 ++--- 4 files changed, 184 insertions(+), 73 deletions(-) diff --git a/cgi.go b/cgi.go index 63fb1339b9..13e0baa13c 100644 --- a/cgi.go +++ b/cgi.go @@ -32,37 +32,36 @@ var tlsProtocolStrings = map[uint16]string{ tls.VersionTLS13: "TLSv1.3", } -// Known $_SERVER keys -var knownServerKeys = []string{ - "CONTENT_LENGTH", - "DOCUMENT_ROOT", - "DOCUMENT_URI", - "GATEWAY_INTERFACE", - "HTTP_HOST", - "HTTPS", - "PATH_INFO", - "PHP_SELF", - "REMOTE_ADDR", - "REMOTE_HOST", - "REMOTE_PORT", - "REQUEST_SCHEME", - "SCRIPT_FILENAME", - "SCRIPT_NAME", - "SERVER_NAME", - "SERVER_PORT", - "SERVER_PROTOCOL", - "SERVER_SOFTWARE", - "SSL_PROTOCOL", - "SSL_CIPHER", - "AUTH_TYPE", - "REMOTE_IDENT", - "CONTENT_TYPE", - "PATH_TRANSLATED", - "QUERY_STRING", - "REMOTE_USER", - "REQUEST_METHOD", - "REQUEST_URI", -} +var ( + contentLengthKey *C.zend_string + documentRoot *C.zend_string + documentURI *C.zend_string + gatewayIface *C.zend_string + httpHost *C.zend_string + httpsKey *C.zend_string + pathInfo *C.zend_string + phpSelf *C.zend_string + remoteAddr *C.zend_string + remoteHost *C.zend_string + remotePort *C.zend_string + requestScheme *C.zend_string + scriptFilename *C.zend_string + scriptName *C.zend_string + serverName *C.zend_string + serverPortKey *C.zend_string + serverProtocolKey *C.zend_string + serverSoftware *C.zend_string + sslProtocolKey *C.zend_string + sslCipherKey *C.zend_string + authType *C.zend_string + remoteIdent *C.zend_string + contentTypeKey *C.zend_string + pathTranslated *C.zend_string + queryString *C.zend_string + remoteUser *C.zend_string + requestMethodKey *C.zend_string + requestURIKey *C.zend_string +) // computeKnownVariables returns a set of CGI environment variables for the request. // @@ -70,7 +69,6 @@ var knownServerKeys = []string{ // Inspired by https://github.com/caddyserver/caddy/blob/master/modules/caddyhttp/reverseproxy/fastcgi/fastcgi.go func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { request := fc.request - keys := mainThread.knownServerKeys // Separate remote IP and port; more lenient than net.SplitHostPort var ip, port string if idx := strings.LastIndex(request.RemoteAddr, ":"); idx > -1 { @@ -140,45 +138,45 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { C.frankenphp_register_bulk( trackVarsArray, - packCgiVariable(keys["REMOTE_ADDR"], ip), - packCgiVariable(keys["REMOTE_HOST"], ip), - packCgiVariable(keys["REMOTE_PORT"], port), - packCgiVariable(keys["DOCUMENT_ROOT"], fc.documentRoot), - packCgiVariable(keys["PATH_INFO"], fc.pathInfo), - packCgiVariable(keys["PHP_SELF"], request.URL.Path), - packCgiVariable(keys["DOCUMENT_URI"], fc.docURI), - packCgiVariable(keys["SCRIPT_FILENAME"], fc.scriptFilename), - packCgiVariable(keys["SCRIPT_NAME"], fc.scriptName), - packCgiVariable(keys["HTTPS"], https), - packCgiVariable(keys["SSL_PROTOCOL"], sslProtocol), - packCgiVariable(keys["REQUEST_SCHEME"], rs), - packCgiVariable(keys["SERVER_NAME"], reqHost), - packCgiVariable(keys["SERVER_PORT"], serverPort), + packCgiVariable(remoteAddr, ip), + packCgiVariable(remoteHost, ip), + packCgiVariable(remotePort, port), + packCgiVariable(documentRoot, fc.documentRoot), + packCgiVariable(pathInfo, fc.pathInfo), + packCgiVariable(phpSelf, request.URL.Path), + packCgiVariable(documentURI, fc.docURI), + packCgiVariable(scriptFilename, fc.scriptFilename), + packCgiVariable(scriptName, fc.scriptName), + packCgiVariable(httpsKey, https), + packCgiVariable(sslProtocolKey, sslProtocol), + packCgiVariable(requestScheme, rs), + packCgiVariable(serverName, reqHost), + packCgiVariable(serverPortKey, serverPort), // Variables defined in CGI 1.1 spec // Some variables are unused but cleared explicitly to prevent // the parent environment from interfering. // These values can not be overridden - packCgiVariable(keys["CONTENT_LENGTH"], contentLength), - packCgiVariable(keys["GATEWAY_INTERFACE"], "CGI/1.1"), - packCgiVariable(keys["SERVER_PROTOCOL"], request.Proto), - packCgiVariable(keys["SERVER_SOFTWARE"], "FrankenPHP"), - packCgiVariable(keys["HTTP_HOST"], request.Host), + packCgiVariable(contentLengthKey, contentLength), + packCgiVariable(gatewayIface, "CGI/1.1"), + packCgiVariable(serverProtocolKey, request.Proto), + packCgiVariable(serverSoftware, "FrankenPHP"), + packCgiVariable(httpHost, request.Host), // These values are always empty but must be defined: - packCgiVariable(keys["AUTH_TYPE"], ""), - packCgiVariable(keys["REMOTE_IDENT"], ""), + packCgiVariable(authType, ""), + packCgiVariable(remoteIdent, ""), // Request uri of the original request - packCgiVariable(keys["REQUEST_URI"], requestURI), - packCgiVariable(keys["SSL_CIPHER"], sslCipher), + packCgiVariable(requestURIKey, requestURI), + packCgiVariable(sslCipherKey, sslCipher), ) // These values are already present in the SG(request_info), so we'll register them from there C.frankenphp_register_variables_from_request_info( trackVarsArray, - keys["CONTENT_TYPE"], - keys["PATH_TRANSLATED"], - keys["QUERY_STRING"], - keys["REMOTE_USER"], - keys["REQUEST_METHOD"], + contentTypeKey, + pathTranslated, + queryString, + remoteUser, + requestMethodKey, ) } @@ -347,3 +345,37 @@ func toUnsafeChar(s string) *C.char { sData := unsafe.StringData(s) return (*C.char)(unsafe.Pointer(sData)) } +func initKnownServerKeys() { + contentLengthKey = internedZendString("CONTENT_LENGTH") + documentRoot = internedZendString("DOCUMENT_ROOT") + documentURI = internedZendString("DOCUMENT_URI") + gatewayIface = internedZendString("GATEWAY_INTERFACE") + httpHost = internedZendString("HTTP_HOST") + httpsKey = internedZendString("HTTPS") + pathInfo = internedZendString("PATH_INFO") + phpSelf = internedZendString("PHP_SELF") + remoteAddr = internedZendString("REMOTE_ADDR") + remoteHost = internedZendString("REMOTE_HOST") + remotePort = internedZendString("REMOTE_PORT") + requestScheme = internedZendString("REQUEST_SCHEME") + scriptFilename = internedZendString("SCRIPT_FILENAME") + scriptName = internedZendString("SCRIPT_NAME") + serverName = internedZendString("SERVER_NAME") + serverPortKey = internedZendString("SERVER_PORT") + serverProtocolKey = internedZendString("SERVER_PROTOCOL") + serverSoftware = internedZendString("SERVER_SOFTWARE") + sslProtocolKey = internedZendString("SSL_PROTOCOL") + sslCipherKey = internedZendString("SSL_CIPHER") + authType = internedZendString("AUTH_TYPE") + remoteIdent = internedZendString("REMOTE_IDENT") + contentTypeKey = internedZendString("CONTENT_TYPE") + pathTranslated = internedZendString("PATH_TRANSLATED") + queryString = internedZendString("QUERY_STRING") + remoteUser = internedZendString("REMOTE_USER") + requestMethodKey = internedZendString("REQUEST_METHOD") + requestURIKey = internedZendString("REQUEST_URI") +} + +func internedZendString(s string) *C.zend_string { + return C.frankenphp_init_persistent_string(toUnsafeChar(s), C.size_t(len(s))) +} diff --git a/frankenphp.c b/frankenphp.c index fd487edb8e..f65c80f1e8 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -458,6 +458,9 @@ PHP_FUNCTION(frankenphp_handle_request) { if (zend_call_function(&fci, &fcc) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { callback_ret = &retval; + if (Z_TYPE(retval) == IS_NULL) { + callback_ret = NULL; + } } /* diff --git a/internal/phpheaders/phpheaders.go b/internal/phpheaders/phpheaders.go index 71f23e7d92..2dc7acdba9 100644 --- a/internal/phpheaders/phpheaders.go +++ b/internal/phpheaders/phpheaders.go @@ -117,6 +117,87 @@ var CommonRequestHeaders = map[string]string{ "X-Real-Ip": "HTTP_X_REAL_IP", } +var CommonHeaderValuesByKey = []string{ + // -------------------- + // Content negotiation + // -------------------- + "application/json", + "application/xml", + "text/plain", + "text/html", + "text/css", + "text/javascript", + "application/javascript", + "application/x-www-form-urlencoded", + "multipart/form-data", + "application/octet-stream", + "*/*", + "application/json", + "text/plain", + "text/html", + "application/xml", +"gzip", + "deflate", + "br", + "identity", + "en", + "en-US", + "en-GB", + "fr", + "de", + "es", + "zh-CN", + "ja", + "no-cache", + "no-store", + "max-age=0", + "max-age=3600", + "public", + "private", + "must-revalidate", + "keep-alive", + "close", +"chunked", +"websocket", + "Basic", + "Bearer", + "*/*", +"true", + "false", + + + "document", + "image", + "script", + "style", + "empty", + + "navigate", + "no-cors", + "cors", + "same-origin", + + "same-origin", + "same-site", + "cross-site", + "none", + + "?1", + + "nosniff", + + + "no-referrer", + "no-referrer-when-downgrade", + "same-origin", + "origin", + "strict-origin", + "strict-origin-when-cross-origin", + + "100-continue", +} + + // Cache up to 256 uncommon headers // This is ~2.5x faster than converting the header each time var headerKeyCache = otter.Must[string, string](&otter.Options[string, string]{MaximumSize: 256}) diff --git a/phpmainthread.go b/phpmainthread.go index cecadc1653..f3cce6505f 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -20,14 +20,13 @@ import ( // represents the main PHP thread // the thread needs to keep running as long as all other threads are running type phpMainThread struct { - state *state.ThreadState - done chan struct{} - numThreads int - maxThreads int - phpIni map[string]string - commonHeaders map[string]*C.zend_string - knownServerKeys map[string]*C.zend_string - sandboxedEnv map[string]*C.zend_string + state *state.ThreadState + done chan struct{} + numThreads int + maxThreads int + phpIni map[string]string + commonHeaders map[string]*C.zend_string + sandboxedEnv map[string]*C.zend_string } var ( @@ -115,11 +114,7 @@ func (mainThread *phpMainThread) start() error { mainThread.commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) } - // cache $_SERVER keys as zend_strings (SERVER_PROTOCOL, SERVER_SOFTWARE, etc.) - mainThread.knownServerKeys = make(map[string]*C.zend_string, len(knownServerKeys)) - for _, phpKey := range knownServerKeys { - mainThread.knownServerKeys[phpKey] = C.frankenphp_init_persistent_string(toUnsafeChar(phpKey), C.size_t(len(phpKey))) - } + initKnownServerKeys() return nil } From 84be9df7a6ea4474f7fb8683272f7d4fd61ce212 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Mon, 12 Jan 2026 21:22:40 +0100 Subject: [PATCH 02/32] Extracts zend_strings. --- cgi.go | 204 ++++++++++++++---------------- frankenphp.c | 117 +++++++++-------- frankenphp.h | 111 +++++++++++++--- internal/phpheaders/phpheaders.go | 81 ------------ phpmainthread.go | 22 +--- phpmainthread_test.go | 2 +- watcher.go | 2 +- zstrings.go | 91 +++++++++++++ 8 files changed, 347 insertions(+), 283 deletions(-) create mode 100644 zstrings.go diff --git a/cgi.go b/cgi.go index 13e0baa13c..29cbfc7ea1 100644 --- a/cgi.go +++ b/cgi.go @@ -32,37 +32,6 @@ var tlsProtocolStrings = map[uint16]string{ tls.VersionTLS13: "TLSv1.3", } -var ( - contentLengthKey *C.zend_string - documentRoot *C.zend_string - documentURI *C.zend_string - gatewayIface *C.zend_string - httpHost *C.zend_string - httpsKey *C.zend_string - pathInfo *C.zend_string - phpSelf *C.zend_string - remoteAddr *C.zend_string - remoteHost *C.zend_string - remotePort *C.zend_string - requestScheme *C.zend_string - scriptFilename *C.zend_string - scriptName *C.zend_string - serverName *C.zend_string - serverPortKey *C.zend_string - serverProtocolKey *C.zend_string - serverSoftware *C.zend_string - sslProtocolKey *C.zend_string - sslCipherKey *C.zend_string - authType *C.zend_string - remoteIdent *C.zend_string - contentTypeKey *C.zend_string - pathTranslated *C.zend_string - queryString *C.zend_string - remoteUser *C.zend_string - requestMethodKey *C.zend_string - requestURIKey *C.zend_string -) - // computeKnownVariables returns a set of CGI environment variables for the request. // // TODO: handle this case https://github.com/caddyserver/caddy/issues/3718 @@ -136,57 +105,112 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { requestURI = request.URL.RequestURI() } - C.frankenphp_register_bulk( - trackVarsArray, - packCgiVariable(remoteAddr, ip), - packCgiVariable(remoteHost, ip), - packCgiVariable(remotePort, port), - packCgiVariable(documentRoot, fc.documentRoot), - packCgiVariable(pathInfo, fc.pathInfo), - packCgiVariable(phpSelf, request.URL.Path), - packCgiVariable(documentURI, fc.docURI), - packCgiVariable(scriptFilename, fc.scriptFilename), - packCgiVariable(scriptName, fc.scriptName), - packCgiVariable(httpsKey, https), - packCgiVariable(sslProtocolKey, sslProtocol), - packCgiVariable(requestScheme, rs), - packCgiVariable(serverName, reqHost), - packCgiVariable(serverPortKey, serverPort), - // Variables defined in CGI 1.1 spec - // Some variables are unused but cleared explicitly to prevent - // the parent environment from interfering. - // These values can not be overridden - packCgiVariable(contentLengthKey, contentLength), - packCgiVariable(gatewayIface, "CGI/1.1"), - packCgiVariable(serverProtocolKey, request.Proto), - packCgiVariable(serverSoftware, "FrankenPHP"), - packCgiVariable(httpHost, request.Host), - // These values are always empty but must be defined: - packCgiVariable(authType, ""), - packCgiVariable(remoteIdent, ""), - // Request uri of the original request - packCgiVariable(requestURIKey, requestURI), - packCgiVariable(sslCipherKey, sslCipher), - ) + C.frankenphp_register_bulk(trackVarsArray, C.frankenphp_server_vars{ + remote_addr_key: zStrRemoteAddr, + remote_addr_val: toUnsafeChar(ip), + remote_addr_len: C.size_t(len(ip)), + + remote_host_key: zStrRemoteHost, + remote_host_val: toUnsafeChar(ip), + remote_host_len: C.size_t(len(ip)), + + remote_port_key: zStrRemotePort, + remote_port_val: toUnsafeChar(port), + remote_port_len: C.size_t(len(port)), + + document_root_key: zStrDocumentRoot, + document_root_val: toUnsafeChar(fc.documentRoot), + document_root_len: C.size_t(len(fc.documentRoot)), + + path_info_key: zStrPathInfo, + path_info_val: toUnsafeChar(fc.pathInfo), + path_info_len: C.size_t(len(fc.pathInfo)), + + php_self_key: zStrPhpSelf, + php_self_val: toUnsafeChar(request.URL.Path), + php_self_len: C.size_t(len(request.URL.Path)), + + document_uri_key: zStrDocumentURI, + document_uri_val: toUnsafeChar(fc.docURI), + document_uri_len: C.size_t(len(fc.docURI)), + + script_filename_key: zStrScriptFilename, + script_filename_val: toUnsafeChar(fc.scriptFilename), + script_filename_len: C.size_t(len(fc.scriptFilename)), + + script_name_key: zStrScriptName, + script_name_val: toUnsafeChar(fc.scriptName), + script_name_len: C.size_t(len(fc.scriptName)), + + https_key: zStrHttps, + https_val: toUnsafeChar(https), + https_len: C.size_t(len(https)), + + ssl_protocol_key: zStrSslProtocol, + ssl_protocol_val: toUnsafeChar(sslProtocol), + ssl_protocol_len: C.size_t(len(sslProtocol)), + + request_scheme_key: zStrRequestScheme, + request_scheme_val: toUnsafeChar(rs), + request_scheme_len: C.size_t(len(rs)), + + server_name_key: zStrServerName, + server_name_val: toUnsafeChar(reqHost), + server_name_len: C.size_t(len(reqHost)), + + server_port_key: zStrServerPort, + server_port_val: toUnsafeChar(serverPort), + server_port_len: C.size_t(len(serverPort)), + + content_length_key: zStrContentLength, + content_length_val: toUnsafeChar(contentLength), + content_length_len: C.size_t(len(contentLength)), + + gateway_interface_key: zStrGatewayIface, + gateway_interface_str: zStrCgi1, + + server_protocol_key: zStrServerProtocol, + server_protocol_val: toUnsafeChar(request.Proto), + server_protocol_len: C.size_t(len(request.Proto)), + + server_software_key: zStrServerSoftware, + server_software_str: zStrFrankenPHP, + + http_host_key: zStrHttpHost, + http_host_val: toUnsafeChar(request.Host), + http_host_len: C.size_t(len(request.Host)), + + auth_type_key: zStrAuthType, + auth_type_val: toUnsafeChar(""), + auth_type_len: C.size_t(0), + + remote_ident_key: zStrRemoteIdent, + remote_ident_val: toUnsafeChar(""), + remote_ident_len: C.size_t(0), + + request_uri_key: zStrRequestURI, + request_uri_val: toUnsafeChar(requestURI), + request_uri_len: C.size_t(len(requestURI)), + + ssl_cipher_key: zStrSslCipher, + ssl_cipher_val: toUnsafeChar(sslCipher), + ssl_cipher_len: C.size_t(len(sslCipher)), + }) // These values are already present in the SG(request_info), so we'll register them from there C.frankenphp_register_variables_from_request_info( trackVarsArray, - contentTypeKey, - pathTranslated, - queryString, - remoteUser, - requestMethodKey, + zStrContentType, + zStrPathTranslated, + zStrQueryString, + zStrRemoteUser, + zStrRequestMethod, ) } -func packCgiVariable(key *C.zend_string, value string) C.ht_key_value_pair { - return C.ht_key_value_pair{key, toUnsafeChar(value), C.size_t(len(value))} -} - func addHeadersToServer(ctx context.Context, request *http.Request, trackVarsArray *C.zval) { for field, val := range request.Header { - if k := mainThread.commonHeaders[field]; k != nil { + if k := commonHeaders[field]; k != nil { v := strings.Join(val, ", ") C.frankenphp_register_single(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) continue @@ -345,37 +369,3 @@ func toUnsafeChar(s string) *C.char { sData := unsafe.StringData(s) return (*C.char)(unsafe.Pointer(sData)) } -func initKnownServerKeys() { - contentLengthKey = internedZendString("CONTENT_LENGTH") - documentRoot = internedZendString("DOCUMENT_ROOT") - documentURI = internedZendString("DOCUMENT_URI") - gatewayIface = internedZendString("GATEWAY_INTERFACE") - httpHost = internedZendString("HTTP_HOST") - httpsKey = internedZendString("HTTPS") - pathInfo = internedZendString("PATH_INFO") - phpSelf = internedZendString("PHP_SELF") - remoteAddr = internedZendString("REMOTE_ADDR") - remoteHost = internedZendString("REMOTE_HOST") - remotePort = internedZendString("REMOTE_PORT") - requestScheme = internedZendString("REQUEST_SCHEME") - scriptFilename = internedZendString("SCRIPT_FILENAME") - scriptName = internedZendString("SCRIPT_NAME") - serverName = internedZendString("SERVER_NAME") - serverPortKey = internedZendString("SERVER_PORT") - serverProtocolKey = internedZendString("SERVER_PROTOCOL") - serverSoftware = internedZendString("SERVER_SOFTWARE") - sslProtocolKey = internedZendString("SSL_PROTOCOL") - sslCipherKey = internedZendString("SSL_CIPHER") - authType = internedZendString("AUTH_TYPE") - remoteIdent = internedZendString("REMOTE_IDENT") - contentTypeKey = internedZendString("CONTENT_TYPE") - pathTranslated = internedZendString("PATH_TRANSLATED") - queryString = internedZendString("QUERY_STRING") - remoteUser = internedZendString("REMOTE_USER") - requestMethodKey = internedZendString("REQUEST_METHOD") - requestURIKey = internedZendString("REQUEST_URI") -} - -func internedZendString(s string) *C.zend_string { - return C.frankenphp_init_persistent_string(toUnsafeChar(s), C.size_t(len(s))) -} diff --git a/frankenphp.c b/frankenphp.c index f65c80f1e8..fc74afa4e7 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -459,8 +459,8 @@ PHP_FUNCTION(frankenphp_handle_request) { if (zend_call_function(&fci, &fcc) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { callback_ret = &retval; if (Z_TYPE(retval) == IS_NULL) { - callback_ret = NULL; - } + callback_ret = NULL; + } } /* @@ -703,65 +703,62 @@ void frankenphp_register_single(zend_string *z_key, char *value, size_t val_len, } /* Register known $_SERVER variables in bulk to avoid cgo overhead */ -void frankenphp_register_bulk( - zval *track_vars_array, ht_key_value_pair remote_addr, - ht_key_value_pair remote_host, ht_key_value_pair remote_port, - ht_key_value_pair document_root, ht_key_value_pair path_info, - ht_key_value_pair php_self, ht_key_value_pair document_uri, - ht_key_value_pair script_filename, ht_key_value_pair script_name, - ht_key_value_pair https, ht_key_value_pair ssl_protocol, - ht_key_value_pair request_scheme, ht_key_value_pair server_name, - ht_key_value_pair server_port, ht_key_value_pair content_length, - ht_key_value_pair gateway_interface, ht_key_value_pair server_protocol, - ht_key_value_pair server_software, ht_key_value_pair http_host, - ht_key_value_pair auth_type, ht_key_value_pair remote_ident, - ht_key_value_pair request_uri, ht_key_value_pair ssl_cipher) { +void frankenphp_register_bulk(zval *track_vars_array, + frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); - frankenphp_register_trusted_var(remote_addr.key, remote_addr.val, - remote_addr.val_len, ht); - frankenphp_register_trusted_var(remote_host.key, remote_host.val, - remote_host.val_len, ht); - frankenphp_register_trusted_var(remote_port.key, remote_port.val, - remote_port.val_len, ht); - frankenphp_register_trusted_var(document_root.key, document_root.val, - document_root.val_len, ht); - frankenphp_register_trusted_var(path_info.key, path_info.val, - path_info.val_len, ht); - frankenphp_register_trusted_var(php_self.key, php_self.val, php_self.val_len, - ht); - frankenphp_register_trusted_var(document_uri.key, document_uri.val, - document_uri.val_len, ht); - frankenphp_register_trusted_var(script_filename.key, script_filename.val, - script_filename.val_len, ht); - frankenphp_register_trusted_var(script_name.key, script_name.val, - script_name.val_len, ht); - frankenphp_register_trusted_var(https.key, https.val, https.val_len, ht); - frankenphp_register_trusted_var(ssl_protocol.key, ssl_protocol.val, - ssl_protocol.val_len, ht); - frankenphp_register_trusted_var(ssl_cipher.key, ssl_cipher.val, - ssl_cipher.val_len, ht); - frankenphp_register_trusted_var(request_scheme.key, request_scheme.val, - request_scheme.val_len, ht); - frankenphp_register_trusted_var(server_name.key, server_name.val, - server_name.val_len, ht); - frankenphp_register_trusted_var(server_port.key, server_port.val, - server_port.val_len, ht); - frankenphp_register_trusted_var(content_length.key, content_length.val, - content_length.val_len, ht); - frankenphp_register_trusted_var(gateway_interface.key, gateway_interface.val, - gateway_interface.val_len, ht); - frankenphp_register_trusted_var(server_protocol.key, server_protocol.val, - server_protocol.val_len, ht); - frankenphp_register_trusted_var(server_software.key, server_software.val, - server_software.val_len, ht); - frankenphp_register_trusted_var(http_host.key, http_host.val, - http_host.val_len, ht); - frankenphp_register_trusted_var(auth_type.key, auth_type.val, - auth_type.val_len, ht); - frankenphp_register_trusted_var(remote_ident.key, remote_ident.val, - remote_ident.val_len, ht); - frankenphp_register_trusted_var(request_uri.key, request_uri.val, - request_uri.val_len, ht); + frankenphp_register_trusted_var(vars.remote_addr_key, vars.remote_addr_val, + vars.remote_addr_len, ht); + frankenphp_register_trusted_var(vars.remote_host_key, vars.remote_host_val, + vars.remote_host_len, ht); + frankenphp_register_trusted_var(vars.remote_port_key, vars.remote_port_val, + vars.remote_port_len, ht); + frankenphp_register_trusted_var(vars.document_root_key, + vars.document_root_val, + vars.document_root_len, ht); + frankenphp_register_trusted_var(vars.path_info_key, vars.path_info_val, + vars.path_info_len, ht); + frankenphp_register_trusted_var(vars.php_self_key, vars.php_self_val, + vars.php_self_len, ht); + frankenphp_register_trusted_var(vars.document_uri_key, vars.document_uri_val, + vars.document_uri_len, ht); + frankenphp_register_trusted_var(vars.script_filename_key, + vars.script_filename_val, + vars.script_filename_len, ht); + frankenphp_register_trusted_var(vars.script_name_key, vars.script_name_val, + vars.script_name_len, ht); + frankenphp_register_trusted_var(vars.https_key, vars.https_val, + vars.https_len, ht); + frankenphp_register_trusted_var(vars.ssl_protocol_key, vars.ssl_protocol_val, + vars.ssl_protocol_len, ht); + frankenphp_register_trusted_var(vars.ssl_cipher_key, vars.ssl_cipher_val, + vars.ssl_cipher_len, ht); + frankenphp_register_trusted_var(vars.request_scheme_key, + vars.request_scheme_val, + vars.request_scheme_len, ht); + frankenphp_register_trusted_var(vars.server_name_key, vars.server_name_val, + vars.server_name_len, ht); + frankenphp_register_trusted_var(vars.server_port_key, vars.server_port_val, + vars.server_port_len, ht); + frankenphp_register_trusted_var(vars.content_length_key, + vars.content_length_val, + vars.content_length_len, ht); + frankenphp_register_trusted_var(vars.server_protocol_key, + vars.server_protocol_val, + vars.server_protocol_len, ht); + frankenphp_register_trusted_var(vars.http_host_key, vars.http_host_val, + vars.http_host_len, ht); + frankenphp_register_trusted_var(vars.auth_type_key, vars.auth_type_val, + vars.auth_type_len, ht); + frankenphp_register_trusted_var(vars.remote_ident_key, vars.remote_ident_val, + vars.remote_ident_len, ht); + frankenphp_register_trusted_var(vars.request_uri_key, vars.request_uri_val, + vars.request_uri_len, ht); + + zval zv; + ZVAL_STR(&zv, vars.gateway_interface_str); + zend_hash_update_ind(ht, vars.gateway_interface_key, &zv); + ZVAL_STR(&zv, vars.server_software_str); + zend_hash_update_ind(ht, vars.server_software_key, &zv); } /** Create an immutable zend_string that lasts for the whole process **/ diff --git a/frankenphp.h b/frankenphp.h index c833c44f97..cf0d13b47a 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -17,11 +17,97 @@ typedef struct go_string { char *data; } go_string; -typedef struct ht_key_value_pair { - zend_string *key; - char *val; - size_t val_len; -} ht_key_value_pair; +typedef struct frankenphp_server_vars { + zend_string *remote_addr_key; + char *remote_addr_val; + size_t remote_addr_len; + + zend_string *remote_host_key; + char *remote_host_val; + size_t remote_host_len; + + zend_string *remote_port_key; + char *remote_port_val; + size_t remote_port_len; + + zend_string *document_root_key; + char *document_root_val; + size_t document_root_len; + + zend_string *path_info_key; + char *path_info_val; + size_t path_info_len; + + zend_string *php_self_key; + char *php_self_val; + size_t php_self_len; + + zend_string *document_uri_key; + char *document_uri_val; + size_t document_uri_len; + + zend_string *script_filename_key; + char *script_filename_val; + size_t script_filename_len; + + zend_string *script_name_key; + char *script_name_val; + size_t script_name_len; + + zend_string *https_key; + char *https_val; + size_t https_len; + + zend_string *ssl_protocol_key; + char *ssl_protocol_val; + size_t ssl_protocol_len; + + zend_string *request_scheme_key; + char *request_scheme_val; + size_t request_scheme_len; + + zend_string *server_name_key; + char *server_name_val; + size_t server_name_len; + + zend_string *server_port_key; + char *server_port_val; + size_t server_port_len; + + zend_string *content_length_key; + char *content_length_val; + size_t content_length_len; + + zend_string *gateway_interface_key; + zend_string *gateway_interface_str; + + zend_string *server_protocol_key; + char *server_protocol_val; + size_t server_protocol_len; + + zend_string *server_software_key; + zend_string *server_software_str; + + zend_string *http_host_key; + char *http_host_val; + size_t http_host_len; + + zend_string *auth_type_key; + char *auth_type_val; + size_t auth_type_len; + + zend_string *remote_ident_key; + char *remote_ident_val; + size_t remote_ident_len; + + zend_string *request_uri_key; + char *request_uri_val; + size_t request_uri_len; + + zend_string *ssl_cipher_key; + char *ssl_cipher_val; + size_t ssl_cipher_len; +} frankenphp_server_vars; typedef struct frankenphp_version { unsigned char major_version; @@ -62,19 +148,8 @@ int frankenphp_get_current_memory_limit(); void frankenphp_register_single(zend_string *z_key, char *value, size_t val_len, zval *track_vars_array); -void frankenphp_register_bulk( - zval *track_vars_array, ht_key_value_pair remote_addr, - ht_key_value_pair remote_host, ht_key_value_pair remote_port, - ht_key_value_pair document_root, ht_key_value_pair path_info, - ht_key_value_pair php_self, ht_key_value_pair document_uri, - ht_key_value_pair script_filename, ht_key_value_pair script_name, - ht_key_value_pair https, ht_key_value_pair ssl_protocol, - ht_key_value_pair request_scheme, ht_key_value_pair server_name, - ht_key_value_pair server_port, ht_key_value_pair content_length, - ht_key_value_pair gateway_interface, ht_key_value_pair server_protocol, - ht_key_value_pair server_software, ht_key_value_pair http_host, - ht_key_value_pair auth_type, ht_key_value_pair remote_ident, - ht_key_value_pair request_uri, ht_key_value_pair ssl_cipher); +void frankenphp_register_bulk(zval *track_vars_array, + frankenphp_server_vars vars); void register_extensions(zend_module_entry **m, int len); diff --git a/internal/phpheaders/phpheaders.go b/internal/phpheaders/phpheaders.go index 2dc7acdba9..71f23e7d92 100644 --- a/internal/phpheaders/phpheaders.go +++ b/internal/phpheaders/phpheaders.go @@ -117,87 +117,6 @@ var CommonRequestHeaders = map[string]string{ "X-Real-Ip": "HTTP_X_REAL_IP", } -var CommonHeaderValuesByKey = []string{ - // -------------------- - // Content negotiation - // -------------------- - "application/json", - "application/xml", - "text/plain", - "text/html", - "text/css", - "text/javascript", - "application/javascript", - "application/x-www-form-urlencoded", - "multipart/form-data", - "application/octet-stream", - "*/*", - "application/json", - "text/plain", - "text/html", - "application/xml", -"gzip", - "deflate", - "br", - "identity", - "en", - "en-US", - "en-GB", - "fr", - "de", - "es", - "zh-CN", - "ja", - "no-cache", - "no-store", - "max-age=0", - "max-age=3600", - "public", - "private", - "must-revalidate", - "keep-alive", - "close", -"chunked", -"websocket", - "Basic", - "Bearer", - "*/*", -"true", - "false", - - - "document", - "image", - "script", - "style", - "empty", - - "navigate", - "no-cors", - "cors", - "same-origin", - - "same-origin", - "same-site", - "cross-site", - "none", - - "?1", - - "nosniff", - - - "no-referrer", - "no-referrer-when-downgrade", - "same-origin", - "origin", - "strict-origin", - "strict-origin-when-cross-origin", - - "100-continue", -} - - // Cache up to 256 uncommon headers // This is ~2.5x faster than converting the header each time var headerKeyCache = otter.Must[string, string](&otter.Options[string, string]{MaximumSize: 256}) diff --git a/phpmainthread.go b/phpmainthread.go index f3cce6505f..3060e94b3b 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -13,20 +13,18 @@ import ( "sync" "github.com/dunglas/frankenphp/internal/memory" - "github.com/dunglas/frankenphp/internal/phpheaders" "github.com/dunglas/frankenphp/internal/state" ) // represents the main PHP thread // the thread needs to keep running as long as all other threads are running type phpMainThread struct { - state *state.ThreadState - done chan struct{} - numThreads int - maxThreads int - phpIni map[string]string - commonHeaders map[string]*C.zend_string - sandboxedEnv map[string]*C.zend_string + state *state.ThreadState + done chan struct{} + numThreads int + maxThreads int + phpIni map[string]string + sandboxedEnv map[string]*C.zend_string } var ( @@ -108,13 +106,7 @@ func (mainThread *phpMainThread) start() error { mainThread.state.WaitFor(state.Ready) - // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) - mainThread.commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) - for key, phpKey := range phpheaders.CommonRequestHeaders { - mainThread.commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) - } - - initKnownServerKeys() + initZendStrings() return nil } diff --git a/phpmainthread_test.go b/phpmainthread_test.go index 7e6bf32c1e..b54647ae07 100644 --- a/phpmainthread_test.go +++ b/phpmainthread_test.go @@ -96,7 +96,7 @@ func TestTransitionThreadsWhileDoingRequests(t *testing.T) { var ( isDone atomic.Bool - wg sync.WaitGroup + wg sync.WaitGroup ) numThreads := 10 diff --git a/watcher.go b/watcher.go index eb2b09e29f..cfe133e5ab 100644 --- a/watcher.go +++ b/watcher.go @@ -10,7 +10,7 @@ import ( ) type hotReloadOpt struct { - hotReload []*watcher.PatternGroup + hotReload []*watcher.PatternGroup } var restartWorkers atomic.Bool diff --git a/zstrings.go b/zstrings.go new file mode 100644 index 0000000000..443f3ae1dd --- /dev/null +++ b/zstrings.go @@ -0,0 +1,91 @@ +package frankenphp + +// #include "frankenphp.h" +import "C" +import ( + "github.com/dunglas/frankenphp/internal/phpheaders" +) + +// cached zend_strings for optimization +var ( + commonHeaders map[string]*C.zend_string + + zStrContentLength *C.zend_string + zStrDocumentRoot *C.zend_string + zStrDocumentURI *C.zend_string + zStrGatewayIface *C.zend_string + zStrHttpHost *C.zend_string + zStrHttps *C.zend_string + zStrPathInfo *C.zend_string + zStrPhpSelf *C.zend_string + zStrRemoteAddr *C.zend_string + zStrRemoteHost *C.zend_string + zStrRemotePort *C.zend_string + zStrRequestScheme *C.zend_string + zStrScriptFilename *C.zend_string + zStrScriptName *C.zend_string + zStrServerName *C.zend_string + zStrServerPort *C.zend_string + zStrServerProtocol *C.zend_string + zStrServerSoftware *C.zend_string + zStrSslProtocol *C.zend_string + zStrSslCipher *C.zend_string + zStrAuthType *C.zend_string + zStrRemoteIdent *C.zend_string + zStrContentType *C.zend_string + zStrPathTranslated *C.zend_string + zStrQueryString *C.zend_string + zStrRemoteUser *C.zend_string + zStrRequestMethod *C.zend_string + zStrRequestURI *C.zend_string + zStrCgi1 *C.zend_string + zStrFrankenPHP *C.zend_string +) + +func initZendStrings() { + if commonHeaders != nil { + return // already initialized + } + + // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) + commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) + for key, phpKey := range phpheaders.CommonRequestHeaders { + commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) + } + + // cache known $_SERVER KEYs as zend_strings + zStrContentLength = internedZendString("CONTENT_LENGTH") + zStrDocumentRoot = internedZendString("DOCUMENT_ROOT") + zStrDocumentURI = internedZendString("DOCUMENT_URI") + zStrGatewayIface = internedZendString("GATEWAY_INTERFACE") + zStrHttpHost = internedZendString("HTTP_HOST") + zStrHttps = internedZendString("HTTPS") + zStrPathInfo = internedZendString("PATH_INFO") + zStrPhpSelf = internedZendString("PHP_SELF") + zStrRemoteAddr = internedZendString("REMOTE_ADDR") + zStrRemoteHost = internedZendString("REMOTE_HOST") + zStrRemotePort = internedZendString("REMOTE_PORT") + zStrRequestScheme = internedZendString("REQUEST_SCHEME") + zStrScriptFilename = internedZendString("SCRIPT_FILENAME") + zStrScriptName = internedZendString("SCRIPT_NAME") + zStrServerName = internedZendString("SERVER_NAME") + zStrServerPort = internedZendString("SERVER_PORT") + zStrServerProtocol = internedZendString("SERVER_PROTOCOL") + zStrServerSoftware = internedZendString("SERVER_SOFTWARE") + zStrSslProtocol = internedZendString("SSL_PROTOCOL") + zStrSslCipher = internedZendString("SSL_CIPHER") + zStrAuthType = internedZendString("AUTH_TYPE") + zStrRemoteIdent = internedZendString("REMOTE_IDENT") + zStrContentType = internedZendString("CONTENT_TYPE") + zStrPathTranslated = internedZendString("PATH_TRANSLATED") + zStrQueryString = internedZendString("QUERY_STRING") + zStrRemoteUser = internedZendString("REMOTE_USER") + zStrRequestMethod = internedZendString("REQUEST_METHOD") + zStrRequestURI = internedZendString("REQUEST_URI") + zStrCgi1 = internedZendString("CGI/1.1") + zStrFrankenPHP = internedZendString("FrankenPHP") +} + +func internedZendString(s string) *C.zend_string { + return C.frankenphp_init_persistent_string(toUnsafeChar(s), C.size_t(len(s))) +} From 9eb215d4634f659fdff13272c93f58bd0126477f Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 25 Jan 2026 12:12:30 +0100 Subject: [PATCH 03/32] Rehashes to total num of vars. --- cgi.go | 4 ++++ frankenphp.c | 10 ++++++---- frankenphp.h | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/cgi.go b/cgi.go index 29cbfc7ea1..b503e1d2a2 100644 --- a/cgi.go +++ b/cgi.go @@ -106,6 +106,10 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { } C.frankenphp_register_bulk(trackVarsArray, C.frankenphp_server_vars{ + // approximate total length to avoid array re-hashing: + // 28 CGI vars + headers + environment + total_num_vars: C.size_t(28 + len(fc.env) + len(request.Header) + len(mainThread.sandboxedEnv)), + remote_addr_key: zStrRemoteAddr, remote_addr_val: toUnsafeChar(ip), remote_addr_len: C.size_t(len(ip)), diff --git a/frankenphp.c b/frankenphp.c index fc74afa4e7..2df8a94471 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -682,7 +682,7 @@ static inline void frankenphp_register_trusted_var(zend_string *z_key, if (value == NULL) { zval empty; ZVAL_EMPTY_STRING(&empty); - zend_hash_update_ind(ht, z_key, &empty); + zend_hash_update(ht, z_key, &empty); return; } size_t new_val_len = val_len; @@ -692,7 +692,7 @@ static inline void frankenphp_register_trusted_var(zend_string *z_key, new_val_len, &new_val_len)) { zval z_value; ZVAL_STRINGL_FAST(&z_value, value, new_val_len); - zend_hash_update_ind(ht, z_key, &z_value); + zend_hash_update(ht, z_key, &z_value); } } @@ -706,6 +706,7 @@ void frankenphp_register_single(zend_string *z_key, char *value, size_t val_len, void frankenphp_register_bulk(zval *track_vars_array, frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); + zend_hash_extend(ht, vars.total_num_vars, 0); frankenphp_register_trusted_var(vars.remote_addr_key, vars.remote_addr_val, vars.remote_addr_len, ht); frankenphp_register_trusted_var(vars.remote_host_key, vars.remote_host_val, @@ -754,11 +755,12 @@ void frankenphp_register_bulk(zval *track_vars_array, frankenphp_register_trusted_var(vars.request_uri_key, vars.request_uri_val, vars.request_uri_len, ht); + // update interned strings zval zv; ZVAL_STR(&zv, vars.gateway_interface_str); - zend_hash_update_ind(ht, vars.gateway_interface_key, &zv); + zend_hash_update(ht, vars.gateway_interface_key, &zv); ZVAL_STR(&zv, vars.server_software_str); - zend_hash_update_ind(ht, vars.server_software_key, &zv); + zend_hash_update(ht, vars.server_software_key, &zv); } /** Create an immutable zend_string that lasts for the whole process **/ diff --git a/frankenphp.h b/frankenphp.h index cf0d13b47a..d73dd7a9b2 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -18,6 +18,8 @@ typedef struct go_string { } go_string; typedef struct frankenphp_server_vars { + size_t total_num_vars; + zend_string *remote_addr_key; char *remote_addr_val; size_t remote_addr_len; From 90aff626a58d4b5d7503815b65e763f5d888e3d8 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 25 Jan 2026 12:20:21 +0100 Subject: [PATCH 04/32] Cleanup. --- phpmainthread.go | 2 -- zstrings.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/phpmainthread.go b/phpmainthread.go index 53a98930a7..92a7b631f6 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -1,9 +1,7 @@ package frankenphp // #cgo nocallback frankenphp_new_main_thread -// #cgo nocallback frankenphp_init_persistent_string // #cgo noescape frankenphp_new_main_thread -// #cgo noescape frankenphp_init_persistent_string // #include // #include "frankenphp.h" import "C" diff --git a/zstrings.go b/zstrings.go index 443f3ae1dd..9658c636c2 100644 --- a/zstrings.go +++ b/zstrings.go @@ -50,7 +50,7 @@ func initZendStrings() { // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) for key, phpKey := range phpheaders.CommonRequestHeaders { - commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) + commonHeaders[key] = internedZendString(phpKey) } // cache known $_SERVER KEYs as zend_strings From 416905dd8647bc960a2338b62ebc7de64a805e9a Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 13:40:00 +0100 Subject: [PATCH 05/32] Cleanup --- cgi.go | 25 +++++++++++-------------- frankenphp.c | 11 ++++++----- frankenphp.go | 2 +- frankenphp.h | 23 +++++++++-------------- 4 files changed, 27 insertions(+), 34 deletions(-) diff --git a/cgi.go b/cgi.go index 229943429f..e22bac3451 100644 --- a/cgi.go +++ b/cgi.go @@ -190,28 +190,14 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { content_length_val: toUnsafeChar(contentLength), content_length_len: C.size_t(len(contentLength)), - gateway_interface_key: zStrGatewayIface, - gateway_interface_str: zStrCgi1, - server_protocol_key: zStrServerProtocol, server_protocol_val: toUnsafeChar(request.Proto), server_protocol_len: C.size_t(len(request.Proto)), - server_software_key: zStrServerSoftware, - server_software_str: zStrFrankenPHP, - http_host_key: zStrHttpHost, http_host_val: toUnsafeChar(request.Host), http_host_len: C.size_t(len(request.Host)), - auth_type_key: zStrAuthType, - auth_type_val: toUnsafeChar(""), - auth_type_len: C.size_t(0), - - remote_ident_key: zStrRemoteIdent, - remote_ident_val: toUnsafeChar(""), - remote_ident_len: C.size_t(0), - request_uri_key: zStrRequestURI, request_uri_val: toUnsafeChar(requestURI), request_uri_len: C.size_t(len(requestURI)), @@ -219,6 +205,17 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { ssl_cipher_key: zStrSslCipher, ssl_cipher_val: toUnsafeChar(sslCipher), ssl_cipher_len: C.size_t(len(sslCipher)), + + // unchanging + server_software_key: zStrServerSoftware, + server_software_str: zStrFrankenPHP, + + gateway_interface_key: zStrGatewayIface, + gateway_interface_str: zStrCgi1, + + // always empty, but must be present + auth_type_key: zStrAuthType, + remote_ident_key: zStrRemoteIdent, }) // These values are already present in the SG(request_info), so we'll register them from there diff --git a/frankenphp.c b/frankenphp.c index 987c60a25c..eb3b2e4dd7 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -981,19 +981,20 @@ void frankenphp_register_bulk(zval *track_vars_array, vars.server_protocol_len, ht); frankenphp_register_trusted_var(vars.http_host_key, vars.http_host_val, vars.http_host_len, ht); - frankenphp_register_trusted_var(vars.auth_type_key, vars.auth_type_val, - vars.auth_type_len, ht); - frankenphp_register_trusted_var(vars.remote_ident_key, vars.remote_ident_val, - vars.remote_ident_len, ht); frankenphp_register_trusted_var(vars.request_uri_key, vars.request_uri_val, vars.request_uri_len, ht); - // update interned strings + // update values with unchanging strings zval zv; ZVAL_STR(&zv, vars.gateway_interface_str); zend_hash_update(ht, vars.gateway_interface_key, &zv); ZVAL_STR(&zv, vars.server_software_str); zend_hash_update(ht, vars.server_software_key, &zv); + + // update values with empty strings + ZVAL_EMPTY_STRING(&zv); + zend_hash_update(ht, vars.auth_type_key, &zv); + zend_hash_update(ht, vars.remote_ident_key, &zv); } /** Create an immutable zend_string that lasts for the whole process **/ diff --git a/frankenphp.go b/frankenphp.go index c651de3cf1..fc603cf7da 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -36,7 +36,7 @@ import ( "time" "unsafe" // debug on Linux - //_ "github.com/ianlancetaylor/cgosymbolizer" + _ "github.com/ianlancetaylor/cgosymbolizer" ) type contextKeyStruct struct{} diff --git a/frankenphp.h b/frankenphp.h index d73dd7a9b2..153e85e8f4 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -80,28 +80,14 @@ typedef struct frankenphp_server_vars { char *content_length_val; size_t content_length_len; - zend_string *gateway_interface_key; - zend_string *gateway_interface_str; - zend_string *server_protocol_key; char *server_protocol_val; size_t server_protocol_len; - zend_string *server_software_key; - zend_string *server_software_str; - zend_string *http_host_key; char *http_host_val; size_t http_host_len; - zend_string *auth_type_key; - char *auth_type_val; - size_t auth_type_len; - - zend_string *remote_ident_key; - char *remote_ident_val; - size_t remote_ident_len; - zend_string *request_uri_key; char *request_uri_val; size_t request_uri_len; @@ -109,6 +95,15 @@ typedef struct frankenphp_server_vars { zend_string *ssl_cipher_key; char *ssl_cipher_val; size_t ssl_cipher_len; + + zend_string *server_software_key; + zend_string *server_software_str; + + zend_string *gateway_interface_key; + zend_string *gateway_interface_str; + + zend_string *auth_type_key; + zend_string *remote_ident_key; } frankenphp_server_vars; typedef struct frankenphp_version { From 3b2847aaeffc01590b6f9ffabbca2032eb0a1863 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 14:29:42 +0100 Subject: [PATCH 06/32] Moves struct to the C side. --- cgi.go | 42 ----------------- cgi_test.go | 2 +- frankenphp.c | 83 +++++++++++++++------------------- frankenphp.h | 33 -------------- internal/strings/strings.h | 92 ++++++++++++++++++++++++++++++++++++++ phpmainthread.go | 14 ++++-- worker.go | 1 - zstrings.go | 91 ------------------------------------- 8 files changed, 141 insertions(+), 217 deletions(-) create mode 100644 internal/strings/strings.h delete mode 100644 zstrings.go diff --git a/cgi.go b/cgi.go index e22bac3451..d4051cf172 100644 --- a/cgi.go +++ b/cgi.go @@ -1,11 +1,9 @@ package frankenphp // #cgo nocallback frankenphp_register_bulk -// #cgo nocallback frankenphp_register_variables_from_request_info // #cgo nocallback frankenphp_register_variable_safe // #cgo nocallback frankenphp_register_single // #cgo noescape frankenphp_register_bulk -// #cgo noescape frankenphp_register_variables_from_request_info // #cgo noescape frankenphp_register_variable_safe // #cgo noescape frankenphp_register_single // #include @@ -130,103 +128,63 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { // 28 CGI vars + headers + environment total_num_vars: C.size_t(28 + len(fc.env) + len(request.Header) + len(mainThread.sandboxedEnv)), - remote_addr_key: zStrRemoteAddr, remote_addr_val: toUnsafeChar(ip), remote_addr_len: C.size_t(len(ip)), - remote_host_key: zStrRemoteHost, remote_host_val: toUnsafeChar(ip), remote_host_len: C.size_t(len(ip)), - remote_port_key: zStrRemotePort, remote_port_val: toUnsafeChar(port), remote_port_len: C.size_t(len(port)), - document_root_key: zStrDocumentRoot, document_root_val: toUnsafeChar(fc.documentRoot), document_root_len: C.size_t(len(fc.documentRoot)), - path_info_key: zStrPathInfo, path_info_val: toUnsafeChar(fc.pathInfo), path_info_len: C.size_t(len(fc.pathInfo)), - php_self_key: zStrPhpSelf, php_self_val: toUnsafeChar(requestPath), php_self_len: C.size_t(len(requestPath)), - document_uri_key: zStrDocumentURI, document_uri_val: toUnsafeChar(fc.docURI), document_uri_len: C.size_t(len(fc.docURI)), - script_filename_key: zStrScriptFilename, script_filename_val: toUnsafeChar(fc.scriptFilename), script_filename_len: C.size_t(len(fc.scriptFilename)), - script_name_key: zStrScriptName, script_name_val: toUnsafeChar(fc.scriptName), script_name_len: C.size_t(len(fc.scriptName)), - https_key: zStrHttps, https_val: toUnsafeChar(https), https_len: C.size_t(len(https)), - ssl_protocol_key: zStrSslProtocol, ssl_protocol_val: toUnsafeChar(sslProtocol), ssl_protocol_len: C.size_t(len(sslProtocol)), - request_scheme_key: zStrRequestScheme, request_scheme_val: toUnsafeChar(rs), request_scheme_len: C.size_t(len(rs)), - server_name_key: zStrServerName, server_name_val: toUnsafeChar(reqHost), server_name_len: C.size_t(len(reqHost)), - server_port_key: zStrServerPort, server_port_val: toUnsafeChar(serverPort), server_port_len: C.size_t(len(serverPort)), - content_length_key: zStrContentLength, content_length_val: toUnsafeChar(contentLength), content_length_len: C.size_t(len(contentLength)), - server_protocol_key: zStrServerProtocol, server_protocol_val: toUnsafeChar(request.Proto), server_protocol_len: C.size_t(len(request.Proto)), - http_host_key: zStrHttpHost, http_host_val: toUnsafeChar(request.Host), http_host_len: C.size_t(len(request.Host)), - request_uri_key: zStrRequestURI, request_uri_val: toUnsafeChar(requestURI), request_uri_len: C.size_t(len(requestURI)), - ssl_cipher_key: zStrSslCipher, ssl_cipher_val: toUnsafeChar(sslCipher), ssl_cipher_len: C.size_t(len(sslCipher)), - - // unchanging - server_software_key: zStrServerSoftware, - server_software_str: zStrFrankenPHP, - - gateway_interface_key: zStrGatewayIface, - gateway_interface_str: zStrCgi1, - - // always empty, but must be present - auth_type_key: zStrAuthType, - remote_ident_key: zStrRemoteIdent, }) - - // These values are already present in the SG(request_info), so we'll register them from there - C.frankenphp_register_variables_from_request_info( - trackVarsArray, - zStrContentType, - zStrPathTranslated, - zStrQueryString, - zStrRemoteUser, - zStrRequestMethod, - ) } func addHeadersToServer(ctx context.Context, request *http.Request, trackVarsArray *C.zval) { diff --git a/cgi_test.go b/cgi_test.go index d7c0e854d7..c4c7a7701c 100644 --- a/cgi_test.go +++ b/cgi_test.go @@ -25,7 +25,7 @@ func TestEnsureLeadingSlash(t *testing.T) { } for _, tt := range tests { - t.Run(tt.input + "-" + tt.expected, func(t *testing.T) { + t.Run(tt.input+"-"+tt.expected, func(t *testing.T) { t.Parallel() assert.Equal(t, tt.expected, ensureLeadingSlash(tt.input), "ensureLeadingSlash(%q)", tt.input) diff --git a/frankenphp.c b/frankenphp.c index eb3b2e4dd7..dfe676c522 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -29,6 +29,7 @@ #include "_cgo_export.h" #include "frankenphp_arginfo.h" +#include "internal/strings/strings.h" #if defined(PHP_WIN32) && defined(ZTS) ZEND_TSRMLS_CACHE_DEFINE() @@ -71,6 +72,7 @@ frankenphp_config frankenphp_get_config() { } bool should_filter_var = 0; +frankenphp_interned_strings_t interned_strings; __thread uintptr_t thread_index; __thread bool is_worker_thread = false; __thread zval *os_environment = NULL; @@ -940,73 +942,61 @@ void frankenphp_register_bulk(zval *track_vars_array, frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); zend_hash_extend(ht, vars.total_num_vars, 0); - frankenphp_register_trusted_var(vars.remote_addr_key, vars.remote_addr_val, + frankenphp_register_trusted_var(interned_strings.remote_addr, vars.remote_addr_val, vars.remote_addr_len, ht); - frankenphp_register_trusted_var(vars.remote_host_key, vars.remote_host_val, + frankenphp_register_trusted_var(interned_strings.remote_host, vars.remote_host_val, vars.remote_host_len, ht); - frankenphp_register_trusted_var(vars.remote_port_key, vars.remote_port_val, + frankenphp_register_trusted_var(interned_strings.remote_port, vars.remote_port_val, vars.remote_port_len, ht); - frankenphp_register_trusted_var(vars.document_root_key, + frankenphp_register_trusted_var(interned_strings.document_root, vars.document_root_val, vars.document_root_len, ht); - frankenphp_register_trusted_var(vars.path_info_key, vars.path_info_val, + frankenphp_register_trusted_var(interned_strings.path_info, vars.path_info_val, vars.path_info_len, ht); - frankenphp_register_trusted_var(vars.php_self_key, vars.php_self_val, + frankenphp_register_trusted_var(interned_strings.php_self, vars.php_self_val, vars.php_self_len, ht); - frankenphp_register_trusted_var(vars.document_uri_key, vars.document_uri_val, + frankenphp_register_trusted_var(interned_strings.document_uri, vars.document_uri_val, vars.document_uri_len, ht); - frankenphp_register_trusted_var(vars.script_filename_key, + frankenphp_register_trusted_var(interned_strings.script_filename, vars.script_filename_val, vars.script_filename_len, ht); - frankenphp_register_trusted_var(vars.script_name_key, vars.script_name_val, + frankenphp_register_trusted_var(interned_strings.script_name, vars.script_name_val, vars.script_name_len, ht); - frankenphp_register_trusted_var(vars.https_key, vars.https_val, + frankenphp_register_trusted_var(interned_strings.https, vars.https_val, vars.https_len, ht); - frankenphp_register_trusted_var(vars.ssl_protocol_key, vars.ssl_protocol_val, + frankenphp_register_trusted_var(interned_strings.ssl_protocol, vars.ssl_protocol_val, vars.ssl_protocol_len, ht); - frankenphp_register_trusted_var(vars.ssl_cipher_key, vars.ssl_cipher_val, + frankenphp_register_trusted_var(interned_strings.ssl_cipher, vars.ssl_cipher_val, vars.ssl_cipher_len, ht); - frankenphp_register_trusted_var(vars.request_scheme_key, + frankenphp_register_trusted_var(interned_strings.request_scheme, vars.request_scheme_val, vars.request_scheme_len, ht); - frankenphp_register_trusted_var(vars.server_name_key, vars.server_name_val, + frankenphp_register_trusted_var(interned_strings.server_name, vars.server_name_val, vars.server_name_len, ht); - frankenphp_register_trusted_var(vars.server_port_key, vars.server_port_val, + frankenphp_register_trusted_var(interned_strings.server_port, vars.server_port_val, vars.server_port_len, ht); - frankenphp_register_trusted_var(vars.content_length_key, + frankenphp_register_trusted_var(interned_strings.content_length, vars.content_length_val, vars.content_length_len, ht); - frankenphp_register_trusted_var(vars.server_protocol_key, + frankenphp_register_trusted_var(interned_strings.server_protocol, vars.server_protocol_val, vars.server_protocol_len, ht); - frankenphp_register_trusted_var(vars.http_host_key, vars.http_host_val, + frankenphp_register_trusted_var(interned_strings.http_host, vars.http_host_val, vars.http_host_len, ht); - frankenphp_register_trusted_var(vars.request_uri_key, vars.request_uri_val, + frankenphp_register_trusted_var(interned_strings.request_uri, vars.request_uri_val, vars.request_uri_len, ht); // update values with unchanging strings zval zv; - ZVAL_STR(&zv, vars.gateway_interface_str); - zend_hash_update(ht, vars.gateway_interface_key, &zv); - ZVAL_STR(&zv, vars.server_software_str); - zend_hash_update(ht, vars.server_software_key, &zv); + ZVAL_STR(&zv, interned_strings.gateway_interface_str); + zend_hash_update(ht, interned_strings.gateway_interface, &zv); + ZVAL_STR(&zv, interned_strings.server_software_str); + zend_hash_update(ht, interned_strings.server_software, &zv); // update values with empty strings ZVAL_EMPTY_STRING(&zv); - zend_hash_update(ht, vars.auth_type_key, &zv); - zend_hash_update(ht, vars.remote_ident_key, &zv); -} - -/** Create an immutable zend_string that lasts for the whole process **/ -zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { - /* persistent strings will be ignored by the GC at the end of a request */ - zend_string *z_string = zend_string_init(string, len, 1); - zend_string_hash_val(z_string); - - /* interned strings will not be ref counted by the GC */ - GC_ADD_FLAGS(z_string, IS_STR_INTERNED); - - return z_string; + zend_hash_update(ht, interned_strings.auth_type, &zv); + zend_hash_update(ht, interned_strings.remote_ident, &zv); } static void @@ -1022,22 +1012,19 @@ frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, } } -void frankenphp_register_variables_from_request_info( - zval *track_vars_array, zend_string *content_type, - zend_string *path_translated, zend_string *query_string, - zend_string *auth_user, zend_string *request_method) { +void frankenphp_register_variables_from_request_info(zval *track_vars_array) { frankenphp_register_variable_from_request_info( - content_type, (char *)SG(request_info).content_type, true, + interned_strings.content_type, (char *)SG(request_info).content_type, true, track_vars_array); frankenphp_register_variable_from_request_info( - path_translated, (char *)SG(request_info).path_translated, false, + interned_strings.path_translated, (char *)SG(request_info).path_translated, false, track_vars_array); frankenphp_register_variable_from_request_info( - query_string, SG(request_info).query_string, true, track_vars_array); + interned_strings.query_string, SG(request_info).query_string, true, track_vars_array); frankenphp_register_variable_from_request_info( - auth_user, (char *)SG(request_info).auth_user, false, track_vars_array); + interned_strings.auth_user, (char *)SG(request_info).auth_user, false, track_vars_array); frankenphp_register_variable_from_request_info( - request_method, (char *)SG(request_info).request_method, false, + interned_strings.request_method, (char *)SG(request_info).request_method, false, track_vars_array); } @@ -1080,6 +1067,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { get_full_env(track_vars_array); // php_import_environment_variables(track_vars_array); go_register_variables(thread_index, track_vars_array); + frankenphp_register_variables_from_request_info(track_vars_array); return; } @@ -1099,6 +1087,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { (copy_ctor_func_t)zval_add_ref); go_register_variables(thread_index, track_vars_array); + frankenphp_register_variables_from_request_info(track_vars_array); } static void frankenphp_log_message(const char *message, int syslog_type_int) { @@ -1237,6 +1226,8 @@ static void *php_main(void *arg) { frankenphp_sapi_module.ini_entries = php_ini_overrides; } + interned_strings = frankenphp_init_interned_strings(); + frankenphp_sapi_module.startup(&frankenphp_sapi_module); /* check if a default filter is set in php.ini and only filter if diff --git a/frankenphp.h b/frankenphp.h index 153e85e8f4..8ab8b73914 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -20,90 +20,62 @@ typedef struct go_string { typedef struct frankenphp_server_vars { size_t total_num_vars; - zend_string *remote_addr_key; char *remote_addr_val; size_t remote_addr_len; - zend_string *remote_host_key; char *remote_host_val; size_t remote_host_len; - zend_string *remote_port_key; char *remote_port_val; size_t remote_port_len; - zend_string *document_root_key; char *document_root_val; size_t document_root_len; - zend_string *path_info_key; char *path_info_val; size_t path_info_len; - zend_string *php_self_key; char *php_self_val; size_t php_self_len; - zend_string *document_uri_key; char *document_uri_val; size_t document_uri_len; - zend_string *script_filename_key; char *script_filename_val; size_t script_filename_len; - zend_string *script_name_key; char *script_name_val; size_t script_name_len; - zend_string *https_key; char *https_val; size_t https_len; - zend_string *ssl_protocol_key; char *ssl_protocol_val; size_t ssl_protocol_len; - zend_string *request_scheme_key; char *request_scheme_val; size_t request_scheme_len; - zend_string *server_name_key; char *server_name_val; size_t server_name_len; - zend_string *server_port_key; char *server_port_val; size_t server_port_len; - zend_string *content_length_key; char *content_length_val; size_t content_length_len; - zend_string *server_protocol_key; char *server_protocol_val; size_t server_protocol_len; - zend_string *http_host_key; char *http_host_val; size_t http_host_len; - zend_string *request_uri_key; char *request_uri_val; size_t request_uri_len; - zend_string *ssl_cipher_key; char *ssl_cipher_val; size_t ssl_cipher_len; - - zend_string *server_software_key; - zend_string *server_software_str; - - zend_string *gateway_interface_key; - zend_string *gateway_interface_str; - - zend_string *auth_type_key; - zend_string *remote_ident_key; } frankenphp_server_vars; typedef struct frankenphp_version { @@ -132,11 +104,6 @@ void frankenphp_update_local_thread_context(bool is_worker); int frankenphp_execute_script_cli(char *script, int argc, char **argv, bool eval); - -void frankenphp_register_variables_from_request_info( - zval *track_vars_array, zend_string *content_type, - zend_string *path_translated, zend_string *query_string, - zend_string *auth_user, zend_string *request_method); void frankenphp_register_variable_safe(char *key, char *var, size_t val_len, zval *track_vars_array); zend_string *frankenphp_init_persistent_string(const char *string, size_t len); diff --git a/internal/strings/strings.h b/internal/strings/strings.h new file mode 100644 index 0000000000..120f0669e1 --- /dev/null +++ b/internal/strings/strings.h @@ -0,0 +1,92 @@ +#ifndef _FRANKENPHP_STRINGS_H +#define _FRANKENPHP_STRINGS_H + +#include +#include +#include +#include + +/** + * Cached interned strings for memory and performance benefits + */ +typedef struct frankenphp_interned_strings_t { + zend_string *remote_addr; + zend_string *remote_host; + zend_string *remote_port; + zend_string *document_root; + zend_string *path_info; + zend_string *php_self; + zend_string *document_uri; + zend_string *script_filename; + zend_string *script_name; + zend_string *https; + zend_string *ssl_protocol; + zend_string *request_scheme; + zend_string *server_name; + zend_string *server_port; + zend_string *content_length; + zend_string *server_protocol; + zend_string *http_host; + zend_string *request_uri; + zend_string *ssl_cipher; + zend_string *server_software; + zend_string *server_software_str; + zend_string *gateway_interface; + zend_string *gateway_interface_str; + zend_string *auth_type; + zend_string *remote_ident; + zend_string *content_type; + zend_string *path_translated; + zend_string *query_string; + zend_string *auth_user; + zend_string *request_method; +} frankenphp_interned_strings_t; + +zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { + /* persistent strings will be ignored by the GC at the end of a request */ + zend_string *z_string = zend_string_init(string, len, 1); + zend_string_hash_val(z_string); + + /* interned strings will not be ref counted by the GC */ + GC_ADD_FLAGS(z_string, IS_STR_INTERNED); + + return z_string; +} + +frankenphp_interned_strings_t frankenphp_init_interned_strings() { + frankenphp_interned_strings_t interned_strings = {0}; + interned_strings.remote_addr = frankenphp_init_persistent_string("REMOTE_ADDR", sizeof("REMOTE_ADDR") - 1); + interned_strings.remote_host = frankenphp_init_persistent_string("REMOTE_HOST", sizeof("REMOTE_HOST") - 1); + interned_strings.remote_port = frankenphp_init_persistent_string("REMOTE_PORT", sizeof("REMOTE_PORT") - 1); + interned_strings.document_root = frankenphp_init_persistent_string("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1); + interned_strings.path_info = frankenphp_init_persistent_string("PATH_INFO", sizeof("PATH_INFO") - 1); + interned_strings.php_self = frankenphp_init_persistent_string("PHP_SELF", sizeof("PHP_SELF") - 1); + interned_strings.document_uri = frankenphp_init_persistent_string("DOCUMENT_URI", sizeof("DOCUMENT_URI") - 1); + interned_strings.script_filename = frankenphp_init_persistent_string("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME") - 1); + interned_strings.script_name = frankenphp_init_persistent_string("SCRIPT_NAME", sizeof("SCRIPT_NAME") - 1); + interned_strings.https = frankenphp_init_persistent_string("HTTPS", sizeof("HTTPS") - 1); + interned_strings.ssl_protocol = frankenphp_init_persistent_string("SSL_PROTOCOL", sizeof("SSL_PROTOCOL") - 1); + interned_strings.request_scheme = frankenphp_init_persistent_string("REQUEST_SCHEME", sizeof("REQUEST_SCHEME") - 1); + interned_strings.server_name = frankenphp_init_persistent_string("SERVER_NAME", sizeof("SERVER_NAME") - 1); + interned_strings.server_port = frankenphp_init_persistent_string("SERVER_PORT", sizeof("SERVER_PORT") - 1); + interned_strings.content_length = frankenphp_init_persistent_string("CONTENT_LENGTH", sizeof("CONTENT_LENGTH") - 1); + interned_strings.server_protocol = frankenphp_init_persistent_string("SERVER_PROTOCOL", sizeof("SERVER_PROTOCOL") - 1); + interned_strings.http_host = frankenphp_init_persistent_string("HTTP_HOST", sizeof("HTTP_HOST") - 1); + interned_strings.request_uri = frankenphp_init_persistent_string("REQUEST_URI", sizeof("REQUEST_URI") - 1); + interned_strings.ssl_cipher = frankenphp_init_persistent_string("SSL_CIPHER", sizeof("SSL_CIPHER") - 1); + interned_strings.server_software = frankenphp_init_persistent_string("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE") - 1); + interned_strings.server_software_str = frankenphp_init_persistent_string("FrankenPHP", sizeof("FrankenPHP") - 1); + interned_strings.gateway_interface = frankenphp_init_persistent_string("GATEWAY_INTERFACE", sizeof("GATEWAY_INTERFACE") - 1); + interned_strings.gateway_interface_str = frankenphp_init_persistent_string("CGI/1.1", sizeof("CGI/1.1") - 1); + interned_strings.auth_type = frankenphp_init_persistent_string("AUTH_TYPE", sizeof("AUTH_TYPE") - 1); + interned_strings.remote_ident = frankenphp_init_persistent_string("REMOTE_IDENT", sizeof("REMOTE_IDENT") - 1); + interned_strings.content_type = frankenphp_init_persistent_string("CONTENT_TYPE", sizeof("CONTENT_TYPE") - 1); + interned_strings.path_translated = frankenphp_init_persistent_string("PATH_TRANSLATED", sizeof("PATH_TRANSLATED") - 1); + interned_strings.query_string = frankenphp_init_persistent_string("QUERY_STRING", sizeof("QUERY_STRING") - 1); + interned_strings.auth_user = frankenphp_init_persistent_string("AUTH_USER", sizeof("AUTH_USER") - 1); + interned_strings.request_method = frankenphp_init_persistent_string("REQUEST_METHOD", sizeof("REQUEST_METHOD") - 1); + + return interned_strings; +} + +#endif diff --git a/phpmainthread.go b/phpmainthread.go index a22f47897d..f2fcc813a3 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -11,6 +11,7 @@ import ( "sync" "github.com/dunglas/frankenphp/internal/memory" + "github.com/dunglas/frankenphp/internal/phpheaders" "github.com/dunglas/frankenphp/internal/state" ) @@ -26,8 +27,9 @@ type phpMainThread struct { } var ( - phpThreads []*phpThread - mainThread *phpMainThread + phpThreads []*phpThread + mainThread *phpMainThread + commonHeaders map[string]*C.zend_string ) // initPHPThreads starts the main PHP thread, @@ -107,7 +109,13 @@ func (mainThread *phpMainThread) start() error { mainThread.state.WaitFor(state.Ready) - initZendStrings() + // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) + if commonHeaders == nil { + commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) + for key, phpKey := range phpheaders.CommonRequestHeaders { + commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) + } + } return nil } diff --git a/worker.go b/worker.go index edba017218..d87848bad2 100644 --- a/worker.go +++ b/worker.go @@ -98,7 +98,6 @@ func initWorkers(opt []workerOpt) error { return nil } - func newWorker(o workerOpt) (*worker, error) { // Order is important! // This order ensures that FrankenPHP started from inside a symlinked directory will properly resolve any paths. diff --git a/zstrings.go b/zstrings.go deleted file mode 100644 index 9658c636c2..0000000000 --- a/zstrings.go +++ /dev/null @@ -1,91 +0,0 @@ -package frankenphp - -// #include "frankenphp.h" -import "C" -import ( - "github.com/dunglas/frankenphp/internal/phpheaders" -) - -// cached zend_strings for optimization -var ( - commonHeaders map[string]*C.zend_string - - zStrContentLength *C.zend_string - zStrDocumentRoot *C.zend_string - zStrDocumentURI *C.zend_string - zStrGatewayIface *C.zend_string - zStrHttpHost *C.zend_string - zStrHttps *C.zend_string - zStrPathInfo *C.zend_string - zStrPhpSelf *C.zend_string - zStrRemoteAddr *C.zend_string - zStrRemoteHost *C.zend_string - zStrRemotePort *C.zend_string - zStrRequestScheme *C.zend_string - zStrScriptFilename *C.zend_string - zStrScriptName *C.zend_string - zStrServerName *C.zend_string - zStrServerPort *C.zend_string - zStrServerProtocol *C.zend_string - zStrServerSoftware *C.zend_string - zStrSslProtocol *C.zend_string - zStrSslCipher *C.zend_string - zStrAuthType *C.zend_string - zStrRemoteIdent *C.zend_string - zStrContentType *C.zend_string - zStrPathTranslated *C.zend_string - zStrQueryString *C.zend_string - zStrRemoteUser *C.zend_string - zStrRequestMethod *C.zend_string - zStrRequestURI *C.zend_string - zStrCgi1 *C.zend_string - zStrFrankenPHP *C.zend_string -) - -func initZendStrings() { - if commonHeaders != nil { - return // already initialized - } - - // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) - commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) - for key, phpKey := range phpheaders.CommonRequestHeaders { - commonHeaders[key] = internedZendString(phpKey) - } - - // cache known $_SERVER KEYs as zend_strings - zStrContentLength = internedZendString("CONTENT_LENGTH") - zStrDocumentRoot = internedZendString("DOCUMENT_ROOT") - zStrDocumentURI = internedZendString("DOCUMENT_URI") - zStrGatewayIface = internedZendString("GATEWAY_INTERFACE") - zStrHttpHost = internedZendString("HTTP_HOST") - zStrHttps = internedZendString("HTTPS") - zStrPathInfo = internedZendString("PATH_INFO") - zStrPhpSelf = internedZendString("PHP_SELF") - zStrRemoteAddr = internedZendString("REMOTE_ADDR") - zStrRemoteHost = internedZendString("REMOTE_HOST") - zStrRemotePort = internedZendString("REMOTE_PORT") - zStrRequestScheme = internedZendString("REQUEST_SCHEME") - zStrScriptFilename = internedZendString("SCRIPT_FILENAME") - zStrScriptName = internedZendString("SCRIPT_NAME") - zStrServerName = internedZendString("SERVER_NAME") - zStrServerPort = internedZendString("SERVER_PORT") - zStrServerProtocol = internedZendString("SERVER_PROTOCOL") - zStrServerSoftware = internedZendString("SERVER_SOFTWARE") - zStrSslProtocol = internedZendString("SSL_PROTOCOL") - zStrSslCipher = internedZendString("SSL_CIPHER") - zStrAuthType = internedZendString("AUTH_TYPE") - zStrRemoteIdent = internedZendString("REMOTE_IDENT") - zStrContentType = internedZendString("CONTENT_TYPE") - zStrPathTranslated = internedZendString("PATH_TRANSLATED") - zStrQueryString = internedZendString("QUERY_STRING") - zStrRemoteUser = internedZendString("REMOTE_USER") - zStrRequestMethod = internedZendString("REQUEST_METHOD") - zStrRequestURI = internedZendString("REQUEST_URI") - zStrCgi1 = internedZendString("CGI/1.1") - zStrFrankenPHP = internedZendString("FrankenPHP") -} - -func internedZendString(s string) *C.zend_string { - return C.frankenphp_init_persistent_string(toUnsafeChar(s), C.size_t(len(s))) -} From bbdf3f8ec4ab7f6bf622294be68e186e162c4b3e Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 14:44:05 +0100 Subject: [PATCH 07/32] Cleanup. --- cgi.go | 90 +++++++++++++++----------------------- frankenphp.c | 42 +++++++++--------- frankenphp.h | 57 ++++++++---------------- internal/strings/strings.h | 76 ++++++++++++++++---------------- 4 files changed, 114 insertions(+), 151 deletions(-) diff --git a/cgi.go b/cgi.go index d4051cf172..58db555f54 100644 --- a/cgi.go +++ b/cgi.go @@ -128,62 +128,44 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { // 28 CGI vars + headers + environment total_num_vars: C.size_t(28 + len(fc.env) + len(request.Header) + len(mainThread.sandboxedEnv)), - remote_addr_val: toUnsafeChar(ip), - remote_addr_len: C.size_t(len(ip)), - - remote_host_val: toUnsafeChar(ip), - remote_host_len: C.size_t(len(ip)), - - remote_port_val: toUnsafeChar(port), - remote_port_len: C.size_t(len(port)), - - document_root_val: toUnsafeChar(fc.documentRoot), - document_root_len: C.size_t(len(fc.documentRoot)), - - path_info_val: toUnsafeChar(fc.pathInfo), - path_info_len: C.size_t(len(fc.pathInfo)), - - php_self_val: toUnsafeChar(requestPath), - php_self_len: C.size_t(len(requestPath)), - - document_uri_val: toUnsafeChar(fc.docURI), - document_uri_len: C.size_t(len(fc.docURI)), - - script_filename_val: toUnsafeChar(fc.scriptFilename), + remote_addr: toUnsafeChar(ip), + remote_addr_len: C.size_t(len(ip)), + remote_host: toUnsafeChar(ip), + remote_host_len: C.size_t(len(ip)), + remote_port: toUnsafeChar(port), + remote_port_len: C.size_t(len(port)), + document_root: toUnsafeChar(fc.documentRoot), + document_root_len: C.size_t(len(fc.documentRoot)), + path_info: toUnsafeChar(fc.pathInfo), + path_info_len: C.size_t(len(fc.pathInfo)), + php_self: toUnsafeChar(requestPath), + php_self_len: C.size_t(len(requestPath)), + document_uri: toUnsafeChar(fc.docURI), + document_uri_len: C.size_t(len(fc.docURI)), + script_filename: toUnsafeChar(fc.scriptFilename), script_filename_len: C.size_t(len(fc.scriptFilename)), - - script_name_val: toUnsafeChar(fc.scriptName), - script_name_len: C.size_t(len(fc.scriptName)), - - https_val: toUnsafeChar(https), - https_len: C.size_t(len(https)), - - ssl_protocol_val: toUnsafeChar(sslProtocol), - ssl_protocol_len: C.size_t(len(sslProtocol)), - - request_scheme_val: toUnsafeChar(rs), - request_scheme_len: C.size_t(len(rs)), - - server_name_val: toUnsafeChar(reqHost), - server_name_len: C.size_t(len(reqHost)), - - server_port_val: toUnsafeChar(serverPort), - server_port_len: C.size_t(len(serverPort)), - - content_length_val: toUnsafeChar(contentLength), - content_length_len: C.size_t(len(contentLength)), - - server_protocol_val: toUnsafeChar(request.Proto), + script_name: toUnsafeChar(fc.scriptName), + script_name_len: C.size_t(len(fc.scriptName)), + https: toUnsafeChar(https), + https_len: C.size_t(len(https)), + ssl_protocol: toUnsafeChar(sslProtocol), + ssl_protocol_len: C.size_t(len(sslProtocol)), + request_scheme: toUnsafeChar(rs), + request_scheme_len: C.size_t(len(rs)), + server_name: toUnsafeChar(reqHost), + server_name_len: C.size_t(len(reqHost)), + server_port: toUnsafeChar(serverPort), + server_port_len: C.size_t(len(serverPort)), + content_length: toUnsafeChar(contentLength), + content_length_len: C.size_t(len(contentLength)), + server_protocol: toUnsafeChar(request.Proto), server_protocol_len: C.size_t(len(request.Proto)), - - http_host_val: toUnsafeChar(request.Host), - http_host_len: C.size_t(len(request.Host)), - - request_uri_val: toUnsafeChar(requestURI), - request_uri_len: C.size_t(len(requestURI)), - - ssl_cipher_val: toUnsafeChar(sslCipher), - ssl_cipher_len: C.size_t(len(sslCipher)), + http_host: toUnsafeChar(request.Host), + http_host_len: C.size_t(len(request.Host)), + request_uri: toUnsafeChar(requestURI), + request_uri_len: C.size_t(len(requestURI)), + ssl_cipher: toUnsafeChar(sslCipher), + ssl_cipher_len: C.size_t(len(sslCipher)), }) } diff --git a/frankenphp.c b/frankenphp.c index dfe676c522..140d9a5fdb 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -690,6 +690,8 @@ PHP_FUNCTION(frankenphp_handle_request) { if (zend_call_function(&fci, &fcc) == SUCCESS && Z_TYPE(retval) != IS_UNDEF) { callback_ret = &retval; + + /* pass NULL instead of the NULL zval as return value */ if (Z_TYPE(retval) == IS_NULL) { callback_ret = NULL; } @@ -942,48 +944,48 @@ void frankenphp_register_bulk(zval *track_vars_array, frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); zend_hash_extend(ht, vars.total_num_vars, 0); - frankenphp_register_trusted_var(interned_strings.remote_addr, vars.remote_addr_val, + frankenphp_register_trusted_var(interned_strings.remote_addr, vars.remote_addr, vars.remote_addr_len, ht); - frankenphp_register_trusted_var(interned_strings.remote_host, vars.remote_host_val, + frankenphp_register_trusted_var(interned_strings.remote_host, vars.remote_host, vars.remote_host_len, ht); - frankenphp_register_trusted_var(interned_strings.remote_port, vars.remote_port_val, + frankenphp_register_trusted_var(interned_strings.remote_port, vars.remote_port, vars.remote_port_len, ht); frankenphp_register_trusted_var(interned_strings.document_root, - vars.document_root_val, + vars.document_root, vars.document_root_len, ht); - frankenphp_register_trusted_var(interned_strings.path_info, vars.path_info_val, + frankenphp_register_trusted_var(interned_strings.path_info, vars.path_info, vars.path_info_len, ht); - frankenphp_register_trusted_var(interned_strings.php_self, vars.php_self_val, + frankenphp_register_trusted_var(interned_strings.php_self, vars.php_self, vars.php_self_len, ht); - frankenphp_register_trusted_var(interned_strings.document_uri, vars.document_uri_val, + frankenphp_register_trusted_var(interned_strings.document_uri, vars.document_uri, vars.document_uri_len, ht); frankenphp_register_trusted_var(interned_strings.script_filename, - vars.script_filename_val, + vars.script_filename, vars.script_filename_len, ht); - frankenphp_register_trusted_var(interned_strings.script_name, vars.script_name_val, + frankenphp_register_trusted_var(interned_strings.script_name, vars.script_name, vars.script_name_len, ht); - frankenphp_register_trusted_var(interned_strings.https, vars.https_val, + frankenphp_register_trusted_var(interned_strings.https, vars.https, vars.https_len, ht); - frankenphp_register_trusted_var(interned_strings.ssl_protocol, vars.ssl_protocol_val, + frankenphp_register_trusted_var(interned_strings.ssl_protocol, vars.ssl_protocol, vars.ssl_protocol_len, ht); - frankenphp_register_trusted_var(interned_strings.ssl_cipher, vars.ssl_cipher_val, + frankenphp_register_trusted_var(interned_strings.ssl_cipher, vars.ssl_cipher, vars.ssl_cipher_len, ht); frankenphp_register_trusted_var(interned_strings.request_scheme, - vars.request_scheme_val, + vars.request_scheme, vars.request_scheme_len, ht); - frankenphp_register_trusted_var(interned_strings.server_name, vars.server_name_val, + frankenphp_register_trusted_var(interned_strings.server_name, vars.server_name, vars.server_name_len, ht); - frankenphp_register_trusted_var(interned_strings.server_port, vars.server_port_val, + frankenphp_register_trusted_var(interned_strings.server_port, vars.server_port, vars.server_port_len, ht); frankenphp_register_trusted_var(interned_strings.content_length, - vars.content_length_val, + vars.content_length, vars.content_length_len, ht); frankenphp_register_trusted_var(interned_strings.server_protocol, - vars.server_protocol_val, + vars.server_protocol, vars.server_protocol_len, ht); - frankenphp_register_trusted_var(interned_strings.http_host, vars.http_host_val, + frankenphp_register_trusted_var(interned_strings.http_host, vars.http_host, vars.http_host_len, ht); - frankenphp_register_trusted_var(interned_strings.request_uri, vars.request_uri_val, + frankenphp_register_trusted_var(interned_strings.request_uri, vars.request_uri, vars.request_uri_len, ht); // update values with unchanging strings @@ -1022,7 +1024,7 @@ void frankenphp_register_variables_from_request_info(zval *track_vars_array) { frankenphp_register_variable_from_request_info( interned_strings.query_string, SG(request_info).query_string, true, track_vars_array); frankenphp_register_variable_from_request_info( - interned_strings.auth_user, (char *)SG(request_info).auth_user, false, track_vars_array); + interned_strings.remote_user, (char *)SG(request_info).auth_user, false, track_vars_array); frankenphp_register_variable_from_request_info( interned_strings.request_method, (char *)SG(request_info).request_method, false, track_vars_array); diff --git a/frankenphp.h b/frankenphp.h index 8ab8b73914..f52735ed60 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -19,62 +19,43 @@ typedef struct go_string { typedef struct frankenphp_server_vars { size_t total_num_vars; - - char *remote_addr_val; + char *remote_addr; size_t remote_addr_len; - - char *remote_host_val; + char *remote_host; size_t remote_host_len; - - char *remote_port_val; + char *remote_port; size_t remote_port_len; - - char *document_root_val; + char *document_root; size_t document_root_len; - - char *path_info_val; + char *path_info; size_t path_info_len; - - char *php_self_val; + char *php_self; size_t php_self_len; - - char *document_uri_val; + char *document_uri; size_t document_uri_len; - - char *script_filename_val; + char *script_filename; size_t script_filename_len; - - char *script_name_val; + char *script_name; size_t script_name_len; - - char *https_val; + char *https; size_t https_len; - - char *ssl_protocol_val; + char *ssl_protocol; size_t ssl_protocol_len; - - char *request_scheme_val; + char *request_scheme; size_t request_scheme_len; - - char *server_name_val; + char *server_name; size_t server_name_len; - - char *server_port_val; + char *server_port; size_t server_port_len; - - char *content_length_val; + char *content_length; size_t content_length_len; - - char *server_protocol_val; + char *server_protocol; size_t server_protocol_len; - - char *http_host_val; + char *http_host; size_t http_host_len; - - char *request_uri_val; + char *request_uri; size_t request_uri_len; - - char *ssl_cipher_val; + char *ssl_cipher; size_t ssl_cipher_len; } frankenphp_server_vars; diff --git a/internal/strings/strings.h b/internal/strings/strings.h index 120f0669e1..f890d76df6 100644 --- a/internal/strings/strings.h +++ b/internal/strings/strings.h @@ -1,11 +1,6 @@ #ifndef _FRANKENPHP_STRINGS_H #define _FRANKENPHP_STRINGS_H -#include -#include -#include -#include - /** * Cached interned strings for memory and performance benefits */ @@ -38,7 +33,7 @@ typedef struct frankenphp_interned_strings_t { zend_string *content_type; zend_string *path_translated; zend_string *query_string; - zend_string *auth_user; + zend_string *remote_user; zend_string *request_method; } frankenphp_interned_strings_t; @@ -53,40 +48,43 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } -frankenphp_interned_strings_t frankenphp_init_interned_strings() { - frankenphp_interned_strings_t interned_strings = {0}; - interned_strings.remote_addr = frankenphp_init_persistent_string("REMOTE_ADDR", sizeof("REMOTE_ADDR") - 1); - interned_strings.remote_host = frankenphp_init_persistent_string("REMOTE_HOST", sizeof("REMOTE_HOST") - 1); - interned_strings.remote_port = frankenphp_init_persistent_string("REMOTE_PORT", sizeof("REMOTE_PORT") - 1); - interned_strings.document_root = frankenphp_init_persistent_string("DOCUMENT_ROOT", sizeof("DOCUMENT_ROOT") - 1); - interned_strings.path_info = frankenphp_init_persistent_string("PATH_INFO", sizeof("PATH_INFO") - 1); - interned_strings.php_self = frankenphp_init_persistent_string("PHP_SELF", sizeof("PHP_SELF") - 1); - interned_strings.document_uri = frankenphp_init_persistent_string("DOCUMENT_URI", sizeof("DOCUMENT_URI") - 1); - interned_strings.script_filename = frankenphp_init_persistent_string("SCRIPT_FILENAME", sizeof("SCRIPT_FILENAME") - 1); - interned_strings.script_name = frankenphp_init_persistent_string("SCRIPT_NAME", sizeof("SCRIPT_NAME") - 1); - interned_strings.https = frankenphp_init_persistent_string("HTTPS", sizeof("HTTPS") - 1); - interned_strings.ssl_protocol = frankenphp_init_persistent_string("SSL_PROTOCOL", sizeof("SSL_PROTOCOL") - 1); - interned_strings.request_scheme = frankenphp_init_persistent_string("REQUEST_SCHEME", sizeof("REQUEST_SCHEME") - 1); - interned_strings.server_name = frankenphp_init_persistent_string("SERVER_NAME", sizeof("SERVER_NAME") - 1); - interned_strings.server_port = frankenphp_init_persistent_string("SERVER_PORT", sizeof("SERVER_PORT") - 1); - interned_strings.content_length = frankenphp_init_persistent_string("CONTENT_LENGTH", sizeof("CONTENT_LENGTH") - 1); - interned_strings.server_protocol = frankenphp_init_persistent_string("SERVER_PROTOCOL", sizeof("SERVER_PROTOCOL") - 1); - interned_strings.http_host = frankenphp_init_persistent_string("HTTP_HOST", sizeof("HTTP_HOST") - 1); - interned_strings.request_uri = frankenphp_init_persistent_string("REQUEST_URI", sizeof("REQUEST_URI") - 1); - interned_strings.ssl_cipher = frankenphp_init_persistent_string("SSL_CIPHER", sizeof("SSL_CIPHER") - 1); - interned_strings.server_software = frankenphp_init_persistent_string("SERVER_SOFTWARE", sizeof("SERVER_SOFTWARE") - 1); - interned_strings.server_software_str = frankenphp_init_persistent_string("FrankenPHP", sizeof("FrankenPHP") - 1); - interned_strings.gateway_interface = frankenphp_init_persistent_string("GATEWAY_INTERFACE", sizeof("GATEWAY_INTERFACE") - 1); - interned_strings.gateway_interface_str = frankenphp_init_persistent_string("CGI/1.1", sizeof("CGI/1.1") - 1); - interned_strings.auth_type = frankenphp_init_persistent_string("AUTH_TYPE", sizeof("AUTH_TYPE") - 1); - interned_strings.remote_ident = frankenphp_init_persistent_string("REMOTE_IDENT", sizeof("REMOTE_IDENT") - 1); - interned_strings.content_type = frankenphp_init_persistent_string("CONTENT_TYPE", sizeof("CONTENT_TYPE") - 1); - interned_strings.path_translated = frankenphp_init_persistent_string("PATH_TRANSLATED", sizeof("PATH_TRANSLATED") - 1); - interned_strings.query_string = frankenphp_init_persistent_string("QUERY_STRING", sizeof("QUERY_STRING") - 1); - interned_strings.auth_user = frankenphp_init_persistent_string("AUTH_USER", sizeof("AUTH_USER") - 1); - interned_strings.request_method = frankenphp_init_persistent_string("REQUEST_METHOD", sizeof("REQUEST_METHOD") - 1); +#define S(str) frankenphp_init_persistent_string(str, sizeof(str) - 1) - return interned_strings; +static frankenphp_interned_strings_t frankenphp_init_interned_strings() { + return (frankenphp_interned_strings_t){ + .remote_addr = S("REMOTE_ADDR"), + .remote_host = S("REMOTE_HOST"), + .remote_port = S("REMOTE_PORT"), + .document_root = S("DOCUMENT_ROOT"), + .path_info = S("PATH_INFO"), + .php_self = S("PHP_SELF"), + .document_uri = S("DOCUMENT_URI"), + .script_filename = S("SCRIPT_FILENAME"), + .script_name = S("SCRIPT_NAME"), + .https = S("HTTPS"), + .ssl_protocol = S("SSL_PROTOCOL"), + .request_scheme = S("REQUEST_SCHEME"), + .server_name = S("SERVER_NAME"), + .server_port = S("SERVER_PORT"), + .content_length = S("CONTENT_LENGTH"), + .server_protocol = S("SERVER_PROTOCOL"), + .http_host = S("HTTP_HOST"), + .request_uri = S("REQUEST_URI"), + .ssl_cipher = S("SSL_CIPHER"), + .server_software = S("SERVER_SOFTWARE"), + .server_software_str = S("FrankenPHP"), + .gateway_interface = S("GATEWAY_INTERFACE"), + .gateway_interface_str = S("CGI/1.1"), + .auth_type = S("AUTH_TYPE"), + .remote_ident = S("REMOTE_IDENT"), + .content_type = S("CONTENT_TYPE"), + .path_translated = S("PATH_TRANSLATED"), + .query_string = S("QUERY_STRING"), + .remote_user = S("REMOTE_USER"), + .request_method = S("REQUEST_METHOD"), + }; } +#undef S + #endif From 2d5546f31065fa596aa319b7e3311c9a96dc345b Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 14:47:35 +0100 Subject: [PATCH 08/32] Formatting. --- frankenphp.c | 72 ++++++++++++++++++++------------------ internal/strings/strings.h | 60 +++++++++++++++---------------- 2 files changed, 68 insertions(+), 64 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 140d9a5fdb..85b0222198 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -944,49 +944,49 @@ void frankenphp_register_bulk(zval *track_vars_array, frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); zend_hash_extend(ht, vars.total_num_vars, 0); - frankenphp_register_trusted_var(interned_strings.remote_addr, vars.remote_addr, - vars.remote_addr_len, ht); - frankenphp_register_trusted_var(interned_strings.remote_host, vars.remote_host, - vars.remote_host_len, ht); - frankenphp_register_trusted_var(interned_strings.remote_port, vars.remote_port, - vars.remote_port_len, ht); + frankenphp_register_trusted_var(interned_strings.remote_addr, + vars.remote_addr, vars.remote_addr_len, ht); + frankenphp_register_trusted_var(interned_strings.remote_host, + vars.remote_host, vars.remote_host_len, ht); + frankenphp_register_trusted_var(interned_strings.remote_port, + vars.remote_port, vars.remote_port_len, ht); frankenphp_register_trusted_var(interned_strings.document_root, - vars.document_root, - vars.document_root_len, ht); + vars.document_root, vars.document_root_len, + ht); frankenphp_register_trusted_var(interned_strings.path_info, vars.path_info, vars.path_info_len, ht); frankenphp_register_trusted_var(interned_strings.php_self, vars.php_self, vars.php_self_len, ht); - frankenphp_register_trusted_var(interned_strings.document_uri, vars.document_uri, - vars.document_uri_len, ht); + frankenphp_register_trusted_var(interned_strings.document_uri, + vars.document_uri, vars.document_uri_len, ht); frankenphp_register_trusted_var(interned_strings.script_filename, vars.script_filename, vars.script_filename_len, ht); - frankenphp_register_trusted_var(interned_strings.script_name, vars.script_name, - vars.script_name_len, ht); + frankenphp_register_trusted_var(interned_strings.script_name, + vars.script_name, vars.script_name_len, ht); frankenphp_register_trusted_var(interned_strings.https, vars.https, vars.https_len, ht); - frankenphp_register_trusted_var(interned_strings.ssl_protocol, vars.ssl_protocol, - vars.ssl_protocol_len, ht); + frankenphp_register_trusted_var(interned_strings.ssl_protocol, + vars.ssl_protocol, vars.ssl_protocol_len, ht); frankenphp_register_trusted_var(interned_strings.ssl_cipher, vars.ssl_cipher, vars.ssl_cipher_len, ht); frankenphp_register_trusted_var(interned_strings.request_scheme, - vars.request_scheme, - vars.request_scheme_len, ht); - frankenphp_register_trusted_var(interned_strings.server_name, vars.server_name, - vars.server_name_len, ht); - frankenphp_register_trusted_var(interned_strings.server_port, vars.server_port, - vars.server_port_len, ht); + vars.request_scheme, vars.request_scheme_len, + ht); + frankenphp_register_trusted_var(interned_strings.server_name, + vars.server_name, vars.server_name_len, ht); + frankenphp_register_trusted_var(interned_strings.server_port, + vars.server_port, vars.server_port_len, ht); frankenphp_register_trusted_var(interned_strings.content_length, - vars.content_length, - vars.content_length_len, ht); + vars.content_length, vars.content_length_len, + ht); frankenphp_register_trusted_var(interned_strings.server_protocol, vars.server_protocol, vars.server_protocol_len, ht); frankenphp_register_trusted_var(interned_strings.http_host, vars.http_host, vars.http_host_len, ht); - frankenphp_register_trusted_var(interned_strings.request_uri, vars.request_uri, - vars.request_uri_len, ht); + frankenphp_register_trusted_var(interned_strings.request_uri, + vars.request_uri, vars.request_uri_len, ht); // update values with unchanging strings zval zv; @@ -1014,20 +1014,24 @@ frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, } } -void frankenphp_register_variables_from_request_info(zval *track_vars_array) { - frankenphp_register_variable_from_request_info( - interned_strings.content_type, (char *)SG(request_info).content_type, true, - track_vars_array); - frankenphp_register_variable_from_request_info( - interned_strings.path_translated, (char *)SG(request_info).path_translated, false, - track_vars_array); +/* Register variables from SG(request_info) into $_SERVER */ +static void +frankenphp_register_variables_from_request_info(zval *track_vars_array) { frankenphp_register_variable_from_request_info( - interned_strings.query_string, SG(request_info).query_string, true, track_vars_array); + interned_strings.content_type, (char *)SG(request_info).content_type, + true, track_vars_array); frankenphp_register_variable_from_request_info( - interned_strings.remote_user, (char *)SG(request_info).auth_user, false, track_vars_array); + interned_strings.path_translated, + (char *)SG(request_info).path_translated, false, track_vars_array); + frankenphp_register_variable_from_request_info(interned_strings.query_string, + SG(request_info).query_string, + true, track_vars_array); frankenphp_register_variable_from_request_info( - interned_strings.request_method, (char *)SG(request_info).request_method, false, + interned_strings.remote_user, (char *)SG(request_info).auth_user, false, track_vars_array); + frankenphp_register_variable_from_request_info( + interned_strings.request_method, (char *)SG(request_info).request_method, + false, track_vars_array); } /* variables with user-defined keys must be registered safely diff --git a/internal/strings/strings.h b/internal/strings/strings.h index f890d76df6..08630396b1 100644 --- a/internal/strings/strings.h +++ b/internal/strings/strings.h @@ -52,36 +52,36 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { static frankenphp_interned_strings_t frankenphp_init_interned_strings() { return (frankenphp_interned_strings_t){ - .remote_addr = S("REMOTE_ADDR"), - .remote_host = S("REMOTE_HOST"), - .remote_port = S("REMOTE_PORT"), - .document_root = S("DOCUMENT_ROOT"), - .path_info = S("PATH_INFO"), - .php_self = S("PHP_SELF"), - .document_uri = S("DOCUMENT_URI"), - .script_filename = S("SCRIPT_FILENAME"), - .script_name = S("SCRIPT_NAME"), - .https = S("HTTPS"), - .ssl_protocol = S("SSL_PROTOCOL"), - .request_scheme = S("REQUEST_SCHEME"), - .server_name = S("SERVER_NAME"), - .server_port = S("SERVER_PORT"), - .content_length = S("CONTENT_LENGTH"), - .server_protocol = S("SERVER_PROTOCOL"), - .http_host = S("HTTP_HOST"), - .request_uri = S("REQUEST_URI"), - .ssl_cipher = S("SSL_CIPHER"), - .server_software = S("SERVER_SOFTWARE"), - .server_software_str = S("FrankenPHP"), - .gateway_interface = S("GATEWAY_INTERFACE"), - .gateway_interface_str = S("CGI/1.1"), - .auth_type = S("AUTH_TYPE"), - .remote_ident = S("REMOTE_IDENT"), - .content_type = S("CONTENT_TYPE"), - .path_translated = S("PATH_TRANSLATED"), - .query_string = S("QUERY_STRING"), - .remote_user = S("REMOTE_USER"), - .request_method = S("REQUEST_METHOD"), + .remote_addr = S("REMOTE_ADDR"), + .remote_host = S("REMOTE_HOST"), + .remote_port = S("REMOTE_PORT"), + .document_root = S("DOCUMENT_ROOT"), + .path_info = S("PATH_INFO"), + .php_self = S("PHP_SELF"), + .document_uri = S("DOCUMENT_URI"), + .script_filename = S("SCRIPT_FILENAME"), + .script_name = S("SCRIPT_NAME"), + .https = S("HTTPS"), + .ssl_protocol = S("SSL_PROTOCOL"), + .request_scheme = S("REQUEST_SCHEME"), + .server_name = S("SERVER_NAME"), + .server_port = S("SERVER_PORT"), + .content_length = S("CONTENT_LENGTH"), + .server_protocol = S("SERVER_PROTOCOL"), + .http_host = S("HTTP_HOST"), + .request_uri = S("REQUEST_URI"), + .ssl_cipher = S("SSL_CIPHER"), + .server_software = S("SERVER_SOFTWARE"), + .server_software_str = S("FrankenPHP"), + .gateway_interface = S("GATEWAY_INTERFACE"), + .gateway_interface_str = S("CGI/1.1"), + .auth_type = S("AUTH_TYPE"), + .remote_ident = S("REMOTE_IDENT"), + .content_type = S("CONTENT_TYPE"), + .path_translated = S("PATH_TRANSLATED"), + .query_string = S("QUERY_STRING"), + .remote_user = S("REMOTE_USER"), + .request_method = S("REQUEST_METHOD"), }; } From 63becf11e0d232e786d6897bc24334f60d4afe5b Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 14:53:36 +0100 Subject: [PATCH 09/32] Makes registration nicer. --- frankenphp.c | 70 ++++++++++++++++++++-------------------------------- 1 file changed, 27 insertions(+), 43 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 85b0222198..e8d1e9b17d 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -944,49 +944,33 @@ void frankenphp_register_bulk(zval *track_vars_array, frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); zend_hash_extend(ht, vars.total_num_vars, 0); - frankenphp_register_trusted_var(interned_strings.remote_addr, - vars.remote_addr, vars.remote_addr_len, ht); - frankenphp_register_trusted_var(interned_strings.remote_host, - vars.remote_host, vars.remote_host_len, ht); - frankenphp_register_trusted_var(interned_strings.remote_port, - vars.remote_port, vars.remote_port_len, ht); - frankenphp_register_trusted_var(interned_strings.document_root, - vars.document_root, vars.document_root_len, - ht); - frankenphp_register_trusted_var(interned_strings.path_info, vars.path_info, - vars.path_info_len, ht); - frankenphp_register_trusted_var(interned_strings.php_self, vars.php_self, - vars.php_self_len, ht); - frankenphp_register_trusted_var(interned_strings.document_uri, - vars.document_uri, vars.document_uri_len, ht); - frankenphp_register_trusted_var(interned_strings.script_filename, - vars.script_filename, - vars.script_filename_len, ht); - frankenphp_register_trusted_var(interned_strings.script_name, - vars.script_name, vars.script_name_len, ht); - frankenphp_register_trusted_var(interned_strings.https, vars.https, - vars.https_len, ht); - frankenphp_register_trusted_var(interned_strings.ssl_protocol, - vars.ssl_protocol, vars.ssl_protocol_len, ht); - frankenphp_register_trusted_var(interned_strings.ssl_cipher, vars.ssl_cipher, - vars.ssl_cipher_len, ht); - frankenphp_register_trusted_var(interned_strings.request_scheme, - vars.request_scheme, vars.request_scheme_len, - ht); - frankenphp_register_trusted_var(interned_strings.server_name, - vars.server_name, vars.server_name_len, ht); - frankenphp_register_trusted_var(interned_strings.server_port, - vars.server_port, vars.server_port_len, ht); - frankenphp_register_trusted_var(interned_strings.content_length, - vars.content_length, vars.content_length_len, - ht); - frankenphp_register_trusted_var(interned_strings.server_protocol, - vars.server_protocol, - vars.server_protocol_len, ht); - frankenphp_register_trusted_var(interned_strings.http_host, vars.http_host, - vars.http_host_len, ht); - frankenphp_register_trusted_var(interned_strings.request_uri, - vars.request_uri, vars.request_uri_len, ht); + + // update values with variable strings +#define REG_VAR(name) \ + frankenphp_register_trusted_var(interned_strings.name, vars.name, \ + vars.name##_len, ht) + + REG_VAR(remote_addr); + REG_VAR(remote_host); + REG_VAR(remote_port); + REG_VAR(document_root); + REG_VAR(path_info); + REG_VAR(php_self); + REG_VAR(document_uri); + REG_VAR(script_filename); + REG_VAR(script_name); + REG_VAR(https); + REG_VAR(ssl_protocol); + REG_VAR(ssl_cipher); + REG_VAR(request_scheme); + REG_VAR(server_name); + REG_VAR(server_port); + REG_VAR(content_length); + REG_VAR(server_protocol); + REG_VAR(http_host); + REG_VAR(request_uri); + +#undef REG_VAR // update values with unchanging strings zval zv; From 5e6b36d4f21ce44c11dc4c4c2aea261628a1c2cd Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 14:57:16 +0100 Subject: [PATCH 10/32] Makes macro names less generic. --- frankenphp.c | 44 +++++++++++++------------- internal/strings/strings.h | 64 +++++++++++++++++++------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index e8d1e9b17d..b34891b5b6 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -946,31 +946,31 @@ void frankenphp_register_bulk(zval *track_vars_array, zend_hash_extend(ht, vars.total_num_vars, 0); // update values with variable strings -#define REG_VAR(name) \ +#define F_REG_VAR(name) \ frankenphp_register_trusted_var(interned_strings.name, vars.name, \ vars.name##_len, ht) - REG_VAR(remote_addr); - REG_VAR(remote_host); - REG_VAR(remote_port); - REG_VAR(document_root); - REG_VAR(path_info); - REG_VAR(php_self); - REG_VAR(document_uri); - REG_VAR(script_filename); - REG_VAR(script_name); - REG_VAR(https); - REG_VAR(ssl_protocol); - REG_VAR(ssl_cipher); - REG_VAR(request_scheme); - REG_VAR(server_name); - REG_VAR(server_port); - REG_VAR(content_length); - REG_VAR(server_protocol); - REG_VAR(http_host); - REG_VAR(request_uri); - -#undef REG_VAR + F_REG_VAR(remote_addr); + F_REG_VAR(remote_host); + F_REG_VAR(remote_port); + F_REG_VAR(document_root); + F_REG_VAR(path_info); + F_REG_VAR(php_self); + F_REG_VAR(document_uri); + F_REG_VAR(script_filename); + F_REG_VAR(script_name); + F_REG_VAR(https); + F_REG_VAR(ssl_protocol); + F_REG_VAR(ssl_cipher); + F_REG_VAR(request_scheme); + F_REG_VAR(server_name); + F_REG_VAR(server_port); + F_REG_VAR(content_length); + F_REG_VAR(server_protocol); + F_REG_VAR(http_host); + F_REG_VAR(request_uri); + +#undef F_REG_VAR // update values with unchanging strings zval zv; diff --git a/internal/strings/strings.h b/internal/strings/strings.h index 08630396b1..fe5ecbd5a9 100644 --- a/internal/strings/strings.h +++ b/internal/strings/strings.h @@ -48,43 +48,43 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } -#define S(str) frankenphp_init_persistent_string(str, sizeof(str) - 1) +#define F_INTERN_STR(str) frankenphp_init_persistent_string(str, sizeof(str) - 1) static frankenphp_interned_strings_t frankenphp_init_interned_strings() { return (frankenphp_interned_strings_t){ - .remote_addr = S("REMOTE_ADDR"), - .remote_host = S("REMOTE_HOST"), - .remote_port = S("REMOTE_PORT"), - .document_root = S("DOCUMENT_ROOT"), - .path_info = S("PATH_INFO"), - .php_self = S("PHP_SELF"), - .document_uri = S("DOCUMENT_URI"), - .script_filename = S("SCRIPT_FILENAME"), - .script_name = S("SCRIPT_NAME"), - .https = S("HTTPS"), - .ssl_protocol = S("SSL_PROTOCOL"), - .request_scheme = S("REQUEST_SCHEME"), - .server_name = S("SERVER_NAME"), - .server_port = S("SERVER_PORT"), - .content_length = S("CONTENT_LENGTH"), - .server_protocol = S("SERVER_PROTOCOL"), - .http_host = S("HTTP_HOST"), - .request_uri = S("REQUEST_URI"), - .ssl_cipher = S("SSL_CIPHER"), - .server_software = S("SERVER_SOFTWARE"), - .server_software_str = S("FrankenPHP"), - .gateway_interface = S("GATEWAY_INTERFACE"), - .gateway_interface_str = S("CGI/1.1"), - .auth_type = S("AUTH_TYPE"), - .remote_ident = S("REMOTE_IDENT"), - .content_type = S("CONTENT_TYPE"), - .path_translated = S("PATH_TRANSLATED"), - .query_string = S("QUERY_STRING"), - .remote_user = S("REMOTE_USER"), - .request_method = S("REQUEST_METHOD"), + .remote_addr = F_INTERN_STR("REMOTE_ADDR"), + .remote_host = F_INTERN_STR("REMOTE_HOST"), + .remote_port = F_INTERN_STR("REMOTE_PORT"), + .document_root = F_INTERN_STR("DOCUMENT_ROOT"), + .path_info = F_INTERN_STR("PATH_INFO"), + .php_self = F_INTERN_STR("PHP_SELF"), + .document_uri = F_INTERN_STR("DOCUMENT_URI"), + .script_filename = F_INTERN_STR("SCRIPT_FILENAME"), + .script_name = F_INTERN_STR("SCRIPT_NAME"), + .https = F_INTERN_STR("HTTPS"), + .ssl_protocol = F_INTERN_STR("SSL_PROTOCOL"), + .request_scheme = F_INTERN_STR("REQUEST_SCHEME"), + .server_name = F_INTERN_STR("SERVER_NAME"), + .server_port = F_INTERN_STR("SERVER_PORT"), + .content_length = F_INTERN_STR("CONTENT_LENGTH"), + .server_protocol = F_INTERN_STR("SERVER_PROTOCOL"), + .http_host = F_INTERN_STR("HTTP_HOST"), + .request_uri = F_INTERN_STR("REQUEST_URI"), + .ssl_cipher = F_INTERN_STR("SSL_CIPHER"), + .server_software = F_INTERN_STR("SERVER_SOFTWARE"), + .server_software_str = F_INTERN_STR("FrankenPHP"), + .gateway_interface = F_INTERN_STR("GATEWAY_INTERFACE"), + .gateway_interface_str = F_INTERN_STR("CGI/1.1"), + .auth_type = F_INTERN_STR("AUTH_TYPE"), + .remote_ident = F_INTERN_STR("REMOTE_IDENT"), + .content_type = F_INTERN_STR("CONTENT_TYPE"), + .path_translated = F_INTERN_STR("PATH_TRANSLATED"), + .query_string = F_INTERN_STR("QUERY_STRING"), + .remote_user = F_INTERN_STR("REMOTE_USER"), + .request_method = F_INTERN_STR("REQUEST_METHOD"), }; } -#undef S +#undef F_INTERN_STR #endif From 419257c4d82fc2412a51bdbbe83ca208e7821a4f Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 14:59:30 +0100 Subject: [PATCH 11/32] Makes macro names less generic. --- frankenphp.c | 44 +++++++++++++------------- internal/strings/strings.h | 65 +++++++++++++++++++------------------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index b34891b5b6..2fd1ab48ef 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -946,31 +946,31 @@ void frankenphp_register_bulk(zval *track_vars_array, zend_hash_extend(ht, vars.total_num_vars, 0); // update values with variable strings -#define F_REG_VAR(name) \ +#define FRANKENPHP_REGISTER_VAR(name) \ frankenphp_register_trusted_var(interned_strings.name, vars.name, \ vars.name##_len, ht) - F_REG_VAR(remote_addr); - F_REG_VAR(remote_host); - F_REG_VAR(remote_port); - F_REG_VAR(document_root); - F_REG_VAR(path_info); - F_REG_VAR(php_self); - F_REG_VAR(document_uri); - F_REG_VAR(script_filename); - F_REG_VAR(script_name); - F_REG_VAR(https); - F_REG_VAR(ssl_protocol); - F_REG_VAR(ssl_cipher); - F_REG_VAR(request_scheme); - F_REG_VAR(server_name); - F_REG_VAR(server_port); - F_REG_VAR(content_length); - F_REG_VAR(server_protocol); - F_REG_VAR(http_host); - F_REG_VAR(request_uri); - -#undef F_REG_VAR + FRANKENPHP_REGISTER_VAR(remote_addr); + FRANKENPHP_REGISTER_VAR(remote_host); + FRANKENPHP_REGISTER_VAR(remote_port); + FRANKENPHP_REGISTER_VAR(document_root); + FRANKENPHP_REGISTER_VAR(path_info); + FRANKENPHP_REGISTER_VAR(php_self); + FRANKENPHP_REGISTER_VAR(document_uri); + FRANKENPHP_REGISTER_VAR(script_filename); + FRANKENPHP_REGISTER_VAR(script_name); + FRANKENPHP_REGISTER_VAR(https); + FRANKENPHP_REGISTER_VAR(ssl_protocol); + FRANKENPHP_REGISTER_VAR(ssl_cipher); + FRANKENPHP_REGISTER_VAR(request_scheme); + FRANKENPHP_REGISTER_VAR(server_name); + FRANKENPHP_REGISTER_VAR(server_port); + FRANKENPHP_REGISTER_VAR(content_length); + FRANKENPHP_REGISTER_VAR(server_protocol); + FRANKENPHP_REGISTER_VAR(http_host); + FRANKENPHP_REGISTER_VAR(request_uri); + +#undef FRANKENPHP_REGISTER_VAR // update values with unchanging strings zval zv; diff --git a/internal/strings/strings.h b/internal/strings/strings.h index fe5ecbd5a9..ea346746d8 100644 --- a/internal/strings/strings.h +++ b/internal/strings/strings.h @@ -48,43 +48,44 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } -#define F_INTERN_STR(str) frankenphp_init_persistent_string(str, sizeof(str) - 1) +#define FRANKENPHP_INTERN_STR(str) \ + frankenphp_init_persistent_string(str, sizeof(str) - 1) static frankenphp_interned_strings_t frankenphp_init_interned_strings() { return (frankenphp_interned_strings_t){ - .remote_addr = F_INTERN_STR("REMOTE_ADDR"), - .remote_host = F_INTERN_STR("REMOTE_HOST"), - .remote_port = F_INTERN_STR("REMOTE_PORT"), - .document_root = F_INTERN_STR("DOCUMENT_ROOT"), - .path_info = F_INTERN_STR("PATH_INFO"), - .php_self = F_INTERN_STR("PHP_SELF"), - .document_uri = F_INTERN_STR("DOCUMENT_URI"), - .script_filename = F_INTERN_STR("SCRIPT_FILENAME"), - .script_name = F_INTERN_STR("SCRIPT_NAME"), - .https = F_INTERN_STR("HTTPS"), - .ssl_protocol = F_INTERN_STR("SSL_PROTOCOL"), - .request_scheme = F_INTERN_STR("REQUEST_SCHEME"), - .server_name = F_INTERN_STR("SERVER_NAME"), - .server_port = F_INTERN_STR("SERVER_PORT"), - .content_length = F_INTERN_STR("CONTENT_LENGTH"), - .server_protocol = F_INTERN_STR("SERVER_PROTOCOL"), - .http_host = F_INTERN_STR("HTTP_HOST"), - .request_uri = F_INTERN_STR("REQUEST_URI"), - .ssl_cipher = F_INTERN_STR("SSL_CIPHER"), - .server_software = F_INTERN_STR("SERVER_SOFTWARE"), - .server_software_str = F_INTERN_STR("FrankenPHP"), - .gateway_interface = F_INTERN_STR("GATEWAY_INTERFACE"), - .gateway_interface_str = F_INTERN_STR("CGI/1.1"), - .auth_type = F_INTERN_STR("AUTH_TYPE"), - .remote_ident = F_INTERN_STR("REMOTE_IDENT"), - .content_type = F_INTERN_STR("CONTENT_TYPE"), - .path_translated = F_INTERN_STR("PATH_TRANSLATED"), - .query_string = F_INTERN_STR("QUERY_STRING"), - .remote_user = F_INTERN_STR("REMOTE_USER"), - .request_method = F_INTERN_STR("REQUEST_METHOD"), + .remote_addr = FRANKENPHP_INTERN_STR("REMOTE_ADDR"), + .remote_host = FRANKENPHP_INTERN_STR("REMOTE_HOST"), + .remote_port = FRANKENPHP_INTERN_STR("REMOTE_PORT"), + .document_root = FRANKENPHP_INTERN_STR("DOCUMENT_ROOT"), + .path_info = FRANKENPHP_INTERN_STR("PATH_INFO"), + .php_self = FRANKENPHP_INTERN_STR("PHP_SELF"), + .document_uri = FRANKENPHP_INTERN_STR("DOCUMENT_URI"), + .script_filename = FRANKENPHP_INTERN_STR("SCRIPT_FILENAME"), + .script_name = FRANKENPHP_INTERN_STR("SCRIPT_NAME"), + .https = FRANKENPHP_INTERN_STR("HTTPS"), + .ssl_protocol = FRANKENPHP_INTERN_STR("SSL_PROTOCOL"), + .request_scheme = FRANKENPHP_INTERN_STR("REQUEST_SCHEME"), + .server_name = FRANKENPHP_INTERN_STR("SERVER_NAME"), + .server_port = FRANKENPHP_INTERN_STR("SERVER_PORT"), + .content_length = FRANKENPHP_INTERN_STR("CONTENT_LENGTH"), + .server_protocol = FRANKENPHP_INTERN_STR("SERVER_PROTOCOL"), + .http_host = FRANKENPHP_INTERN_STR("HTTP_HOST"), + .request_uri = FRANKENPHP_INTERN_STR("REQUEST_URI"), + .ssl_cipher = FRANKENPHP_INTERN_STR("SSL_CIPHER"), + .server_software = FRANKENPHP_INTERN_STR("SERVER_SOFTWARE"), + .server_software_str = FRANKENPHP_INTERN_STR("FrankenPHP"), + .gateway_interface = FRANKENPHP_INTERN_STR("GATEWAY_INTERFACE"), + .gateway_interface_str = FRANKENPHP_INTERN_STR("CGI/1.1"), + .auth_type = FRANKENPHP_INTERN_STR("AUTH_TYPE"), + .remote_ident = FRANKENPHP_INTERN_STR("REMOTE_IDENT"), + .content_type = FRANKENPHP_INTERN_STR("CONTENT_TYPE"), + .path_translated = FRANKENPHP_INTERN_STR("PATH_TRANSLATED"), + .query_string = FRANKENPHP_INTERN_STR("QUERY_STRING"), + .remote_user = FRANKENPHP_INTERN_STR("REMOTE_USER"), + .request_method = FRANKENPHP_INTERN_STR("REQUEST_METHOD"), }; } -#undef F_INTERN_STR +#undef FRANKENPHP_INTERN_STR #endif From 26c3b7fafc98db25d27bfec3a70708745703412f Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 14 Feb 2026 15:14:18 +0100 Subject: [PATCH 12/32] Removes unwanted commits. --- frankenphp.go | 2 +- worker.go | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frankenphp.go b/frankenphp.go index fc603cf7da..8ce554a234 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -36,7 +36,7 @@ import ( "time" "unsafe" // debug on Linux - _ "github.com/ianlancetaylor/cgosymbolizer" + // _ "github.com/ianlancetaylor/cgosymbolizer" ) type contextKeyStruct struct{} diff --git a/worker.go b/worker.go index d87848bad2..edba017218 100644 --- a/worker.go +++ b/worker.go @@ -98,6 +98,7 @@ func initWorkers(opt []workerOpt) error { return nil } + func newWorker(o workerOpt) (*worker, error) { // Order is important! // This order ensures that FrankenPHP started from inside a symlinked directory will properly resolve any paths. From 806f558fd2e5478ad59b02e22871a41bcb4ffa04 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 15 Feb 2026 21:45:18 +0100 Subject: [PATCH 13/32] Resets to indirct update. --- frankenphp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 2fd1ab48ef..9c17d2fd57 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -919,7 +919,7 @@ static inline void frankenphp_register_trusted_var(zend_string *z_key, if (value == NULL) { zval empty; ZVAL_EMPTY_STRING(&empty); - zend_hash_update(ht, z_key, &empty); + zend_hash_update_ind(ht, z_key, &empty); return; } size_t new_val_len = val_len; @@ -929,7 +929,7 @@ static inline void frankenphp_register_trusted_var(zend_string *z_key, new_val_len, &new_val_len)) { zval z_value; ZVAL_STRINGL_FAST(&z_value, value, new_val_len); - zend_hash_update(ht, z_key, &z_value); + zend_hash_update_ind(ht, z_key, &z_value); } } From 4897775d667dd92f3ae8e1c3e8723bc8e637dc1c Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 15 Feb 2026 21:54:51 +0100 Subject: [PATCH 14/32] Makes info registration nicer. --- frankenphp.c | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 9c17d2fd57..78958b8515 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -985,37 +985,29 @@ void frankenphp_register_bulk(zval *track_vars_array, zend_hash_update(ht, interned_strings.remote_ident, &zv); } -static void -frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, - bool must_be_present, - zval *track_vars_array) { - if (value != NULL) { - frankenphp_register_trusted_var(zKey, value, strlen(value), - Z_ARRVAL_P(track_vars_array)); - } else if (must_be_present) { - frankenphp_register_trusted_var(zKey, NULL, 0, - Z_ARRVAL_P(track_vars_array)); - } -} - /* Register variables from SG(request_info) into $_SERVER */ static void frankenphp_register_variables_from_request_info(zval *track_vars_array) { - frankenphp_register_variable_from_request_info( - interned_strings.content_type, (char *)SG(request_info).content_type, - true, track_vars_array); - frankenphp_register_variable_from_request_info( - interned_strings.path_translated, - (char *)SG(request_info).path_translated, false, track_vars_array); - frankenphp_register_variable_from_request_info(interned_strings.query_string, - SG(request_info).query_string, - true, track_vars_array); - frankenphp_register_variable_from_request_info( - interned_strings.remote_user, (char *)SG(request_info).auth_user, false, - track_vars_array); - frankenphp_register_variable_from_request_info( - interned_strings.request_method, (char *)SG(request_info).request_method, - false, track_vars_array); + HashTable *ht = Z_ARRVAL_P(track_vars_array); + +#define FRANKENPHP_REGISTER_FROM_INFO(key, field, required) \ + do { \ + char *value = (char *)SG(request_info).field; \ + if (value != NULL) { \ + frankenphp_register_trusted_var(interned_strings.key, value, \ + strlen(value), ht); \ + } else if (required) { \ + frankenphp_register_trusted_var(interned_strings.key, NULL, 0, ht); \ + } \ + } while (0) + + FRANKENPHP_REGISTER_FROM_INFO(content_type, content_type, true); + FRANKENPHP_REGISTER_FROM_INFO(path_translated, path_translated, false); + FRANKENPHP_REGISTER_FROM_INFO(query_string, query_string, true); + FRANKENPHP_REGISTER_FROM_INFO(remote_user, auth_user, false); + FRANKENPHP_REGISTER_FROM_INFO(request_method, request_method, false); + +#undef FRANKENPHP_REGISTER_FROM_INFO } /* variables with user-defined keys must be registered safely From 04509c846e2ac2410a036bba05f503a6bec0fd65 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 15 Feb 2026 22:05:31 +0100 Subject: [PATCH 15/32] Naming for clarity. --- cgi.go | 6 +++--- frankenphp.c | 14 ++++++++------ frankenphp.h | 12 +++++++----- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/cgi.go b/cgi.go index 58db555f54..7d6fb87ef1 100644 --- a/cgi.go +++ b/cgi.go @@ -2,10 +2,10 @@ package frankenphp // #cgo nocallback frankenphp_register_bulk // #cgo nocallback frankenphp_register_variable_safe -// #cgo nocallback frankenphp_register_single +// #cgo nocallback frankenphp_register_variable_unsafe // #cgo noescape frankenphp_register_bulk // #cgo noescape frankenphp_register_variable_safe -// #cgo noescape frankenphp_register_single +// #cgo noescape frankenphp_register_variable_unsafe // #include // #include "frankenphp.h" import "C" @@ -173,7 +173,7 @@ func addHeadersToServer(ctx context.Context, request *http.Request, trackVarsArr for field, val := range request.Header { if k := commonHeaders[field]; k != nil { v := strings.Join(val, ", ") - C.frankenphp_register_single(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) + C.frankenphp_register_variable_unsafe(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) continue } diff --git a/frankenphp.c b/frankenphp.c index 78958b8515..4068869c89 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -933,12 +933,6 @@ static inline void frankenphp_register_trusted_var(zend_string *z_key, } } -void frankenphp_register_single(zend_string *z_key, char *value, size_t val_len, - zval *track_vars_array) { - HashTable *ht = Z_ARRVAL_P(track_vars_array); - frankenphp_register_trusted_var(z_key, value, val_len, ht); -} - /* Register known $_SERVER variables in bulk to avoid cgo overhead */ void frankenphp_register_bulk(zval *track_vars_array, frankenphp_server_vars vars) { @@ -1010,6 +1004,14 @@ frankenphp_register_variables_from_request_info(zval *track_vars_array) { #undef FRANKENPHP_REGISTER_FROM_INFO } +/* Only hard-coded keys may be registered this way */ +void frankenphp_register_variable_unsafe(zend_string *z_key, char *value, + size_t val_len, + zval *track_vars_array) { + frankenphp_register_trusted_var(z_key, value, val_len, + Z_ARRVAL_P(track_vars_array)); +} + /* variables with user-defined keys must be registered safely * see: php_variables.c -> php_register_variable_ex (#1106) */ void frankenphp_register_variable_safe(char *key, char *val, size_t val_len, diff --git a/frankenphp.h b/frankenphp.h index f52735ed60..f79bfed885 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -85,17 +85,19 @@ void frankenphp_update_local_thread_context(bool is_worker); int frankenphp_execute_script_cli(char *script, int argc, char **argv, bool eval); + +void frankenphp_register_variable_unsafe(zend_string *z_key, char *value, + size_t val_len, + zval *track_vars_array); void frankenphp_register_variable_safe(char *key, char *var, size_t val_len, zval *track_vars_array); +void frankenphp_register_bulk(zval *track_vars_array, + frankenphp_server_vars vars); + zend_string *frankenphp_init_persistent_string(const char *string, size_t len); int frankenphp_reset_opcache(void); int frankenphp_get_current_memory_limit(); -void frankenphp_register_single(zend_string *z_key, char *value, size_t val_len, - zval *track_vars_array); -void frankenphp_register_bulk(zval *track_vars_array, - frankenphp_server_vars vars); - void register_extensions(zend_module_entry **m, int len); #endif From 9fa5c31542c59f43f671b2fd8ba464575e03efc8 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 15 Feb 2026 22:09:04 +0100 Subject: [PATCH 16/32] Cleanup. --- internal/strings/strings.h | 65 +++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/internal/strings/strings.h b/internal/strings/strings.h index ea346746d8..ce0d9edc5b 100644 --- a/internal/strings/strings.h +++ b/internal/strings/strings.h @@ -3,6 +3,7 @@ /** * Cached interned strings for memory and performance benefits + * Add more hard-coded strings here if needed */ typedef struct frankenphp_interned_strings_t { zend_string *remote_addr; @@ -48,44 +49,44 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } -#define FRANKENPHP_INTERN_STR(str) \ +#define FRANKENPHP_INTERNED_STR(str) \ frankenphp_init_persistent_string(str, sizeof(str) - 1) static frankenphp_interned_strings_t frankenphp_init_interned_strings() { return (frankenphp_interned_strings_t){ - .remote_addr = FRANKENPHP_INTERN_STR("REMOTE_ADDR"), - .remote_host = FRANKENPHP_INTERN_STR("REMOTE_HOST"), - .remote_port = FRANKENPHP_INTERN_STR("REMOTE_PORT"), - .document_root = FRANKENPHP_INTERN_STR("DOCUMENT_ROOT"), - .path_info = FRANKENPHP_INTERN_STR("PATH_INFO"), - .php_self = FRANKENPHP_INTERN_STR("PHP_SELF"), - .document_uri = FRANKENPHP_INTERN_STR("DOCUMENT_URI"), - .script_filename = FRANKENPHP_INTERN_STR("SCRIPT_FILENAME"), - .script_name = FRANKENPHP_INTERN_STR("SCRIPT_NAME"), - .https = FRANKENPHP_INTERN_STR("HTTPS"), - .ssl_protocol = FRANKENPHP_INTERN_STR("SSL_PROTOCOL"), - .request_scheme = FRANKENPHP_INTERN_STR("REQUEST_SCHEME"), - .server_name = FRANKENPHP_INTERN_STR("SERVER_NAME"), - .server_port = FRANKENPHP_INTERN_STR("SERVER_PORT"), - .content_length = FRANKENPHP_INTERN_STR("CONTENT_LENGTH"), - .server_protocol = FRANKENPHP_INTERN_STR("SERVER_PROTOCOL"), - .http_host = FRANKENPHP_INTERN_STR("HTTP_HOST"), - .request_uri = FRANKENPHP_INTERN_STR("REQUEST_URI"), - .ssl_cipher = FRANKENPHP_INTERN_STR("SSL_CIPHER"), - .server_software = FRANKENPHP_INTERN_STR("SERVER_SOFTWARE"), - .server_software_str = FRANKENPHP_INTERN_STR("FrankenPHP"), - .gateway_interface = FRANKENPHP_INTERN_STR("GATEWAY_INTERFACE"), - .gateway_interface_str = FRANKENPHP_INTERN_STR("CGI/1.1"), - .auth_type = FRANKENPHP_INTERN_STR("AUTH_TYPE"), - .remote_ident = FRANKENPHP_INTERN_STR("REMOTE_IDENT"), - .content_type = FRANKENPHP_INTERN_STR("CONTENT_TYPE"), - .path_translated = FRANKENPHP_INTERN_STR("PATH_TRANSLATED"), - .query_string = FRANKENPHP_INTERN_STR("QUERY_STRING"), - .remote_user = FRANKENPHP_INTERN_STR("REMOTE_USER"), - .request_method = FRANKENPHP_INTERN_STR("REQUEST_METHOD"), + .remote_addr = FRANKENPHP_INTERNED_STR("REMOTE_ADDR"), + .remote_host = FRANKENPHP_INTERNED_STR("REMOTE_HOST"), + .remote_port = FRANKENPHP_INTERNED_STR("REMOTE_PORT"), + .document_root = FRANKENPHP_INTERNED_STR("DOCUMENT_ROOT"), + .path_info = FRANKENPHP_INTERNED_STR("PATH_INFO"), + .php_self = FRANKENPHP_INTERNED_STR("PHP_SELF"), + .document_uri = FRANKENPHP_INTERNED_STR("DOCUMENT_URI"), + .script_filename = FRANKENPHP_INTERNED_STR("SCRIPT_FILENAME"), + .script_name = FRANKENPHP_INTERNED_STR("SCRIPT_NAME"), + .https = FRANKENPHP_INTERNED_STR("HTTPS"), + .ssl_protocol = FRANKENPHP_INTERNED_STR("SSL_PROTOCOL"), + .request_scheme = FRANKENPHP_INTERNED_STR("REQUEST_SCHEME"), + .server_name = FRANKENPHP_INTERNED_STR("SERVER_NAME"), + .server_port = FRANKENPHP_INTERNED_STR("SERVER_PORT"), + .content_length = FRANKENPHP_INTERNED_STR("CONTENT_LENGTH"), + .server_protocol = FRANKENPHP_INTERNED_STR("SERVER_PROTOCOL"), + .http_host = FRANKENPHP_INTERNED_STR("HTTP_HOST"), + .request_uri = FRANKENPHP_INTERNED_STR("REQUEST_URI"), + .ssl_cipher = FRANKENPHP_INTERNED_STR("SSL_CIPHER"), + .server_software = FRANKENPHP_INTERNED_STR("SERVER_SOFTWARE"), + .server_software_str = FRANKENPHP_INTERNED_STR("FrankenPHP"), + .gateway_interface = FRANKENPHP_INTERNED_STR("GATEWAY_INTERFACE"), + .gateway_interface_str = FRANKENPHP_INTERNED_STR("CGI/1.1"), + .auth_type = FRANKENPHP_INTERNED_STR("AUTH_TYPE"), + .remote_ident = FRANKENPHP_INTERNED_STR("REMOTE_IDENT"), + .content_type = FRANKENPHP_INTERNED_STR("CONTENT_TYPE"), + .path_translated = FRANKENPHP_INTERNED_STR("PATH_TRANSLATED"), + .query_string = FRANKENPHP_INTERNED_STR("QUERY_STRING"), + .remote_user = FRANKENPHP_INTERNED_STR("REMOTE_USER"), + .request_method = FRANKENPHP_INTERNED_STR("REQUEST_METHOD"), }; } -#undef FRANKENPHP_INTERN_STR +#undef FRANKENPHP_INTERNED_STR #endif From d7cdcb429b92f59bbc874226acfe83756a6db2e1 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Fri, 27 Feb 2026 12:55:10 +0100 Subject: [PATCH 17/32] Gathers all string caches in a separate module --- .codespellrc | 2 +- cgi.go | 26 ++++++--- env.go | 13 +++-- frankenphp.c | 57 +++++++++++-------- frankenphp.h | 4 +- .../stringcache.go} | 2 +- .../strings.h => stringcache/stringcache.h} | 2 +- .../stringcache_test.go} | 2 +- phpmainthread.go | 19 +++---- 9 files changed, 74 insertions(+), 53 deletions(-) rename internal/{phpheaders/phpheaders.go => stringcache/stringcache.go} (99%) rename internal/{strings/strings.h => stringcache/stringcache.h} (99%) rename internal/{phpheaders/phpheaders_test.go => stringcache/stringcache_test.go} (97%) diff --git a/.codespellrc b/.codespellrc index 15e29a7bfc..42da17a109 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,3 +1,3 @@ [codespell] check-hidden = -skip = .git,docs/*/*,docs,*/go.mod,*/go.sum,./internal/phpheaders/phpheaders.go +skip = .git,docs/*/*,docs,*/go.mod,*/go.sum,./internal/stringcache/stringcache.go diff --git a/cgi.go b/cgi.go index 9de64f6870..ec129b3a4d 100644 --- a/cgi.go +++ b/cgi.go @@ -1,11 +1,13 @@ package frankenphp -// #cgo nocallback frankenphp_register_bulk +// #cgo nocallback frankenphp_register_server_vars // #cgo nocallback frankenphp_register_variable_safe // #cgo nocallback frankenphp_register_variable_unsafe -// #cgo noescape frankenphp_register_bulk +// #cgo nocallback frankenphp_init_persistent_string +// #cgo noescape frankenphp_register_server_vars // #cgo noescape frankenphp_register_variable_safe -// #cgo noescape frankenphp_register_single +// #cgo noescape frankenphp_register_variable_unsafe +// #cgo noescape frankenphp_init_persistent_string // #include // #include "frankenphp.h" import "C" @@ -19,7 +21,7 @@ import ( "unicode/utf8" "unsafe" - "github.com/dunglas/frankenphp/internal/phpheaders" + "github.com/dunglas/frankenphp/internal/stringcache" "golang.org/x/text/language" "golang.org/x/text/search" ) @@ -123,10 +125,10 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { requestPath := ensureLeadingSlash(request.URL.Path) - C.frankenphp_register_bulk(trackVarsArray, C.frankenphp_server_vars{ + C.frankenphp_register_server_vars(trackVarsArray, C.frankenphp_server_vars{ // approximate total length to avoid array re-hashing: // 28 CGI vars + headers + environment - total_num_vars: C.size_t(28 + len(fc.env) + len(request.Header) + len(mainThread.sandboxedEnv)), + total_num_vars: C.size_t(28 + len(fc.env) + len(request.Header) + lengthOfEnv), remote_addr: toUnsafeChar(ip), remote_addr_len: C.size_t(len(ip)), @@ -179,7 +181,7 @@ func addHeadersToServer(ctx context.Context, request *http.Request, trackVarsArr // if the header name could not be cached, it needs to be registered safely // this is more inefficient but allows additional sanitizing by PHP - k := phpheaders.GetUnCommonHeader(ctx, field) + k := stringcache.GetUnCommonHeader(ctx, field) v := strings.Join(val, ", ") C.frankenphp_register_variable_safe(toUnsafeChar(k), toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) } @@ -375,8 +377,14 @@ func ensureLeadingSlash(path string) string { return "/" + path } +// Use a go string on the C side without allocations (C may not modify the string) +// Best case scenario: The string is implicitly pinned +// eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case func toUnsafeChar(s string) *C.char { - sData := unsafe.StringData(s) + return (*C.char)(unsafe.Pointer(unsafe.StringData(s))) +} - return (*C.char)(unsafe.Pointer(sData)) +// initialize a global zend_string that must never be freed and is ignored by CG +func newPersistentZendString(str string) *C.zend_string { + return C.frankenphp_init_persistent_string(toUnsafeChar(str), C.size_t(len(str))) } diff --git a/env.go b/env.go index 6e99f1769a..fbf6c86e66 100644 --- a/env.go +++ b/env.go @@ -1,7 +1,5 @@ package frankenphp -// #cgo nocallback frankenphp_init_persistent_string -// #cgo noescape frankenphp_init_persistent_string // #include "frankenphp.h" // #include "types.h" import "C" @@ -10,12 +8,17 @@ import ( "strings" ) +var lengthOfEnv = 0 + //export go_init_os_env func go_init_os_env(mainThreadEnv *C.zend_array) { - for _, envVar := range os.Environ() { + fullEnv := os.Environ() + lengthOfEnv = len(fullEnv) + + for _, envVar := range fullEnv { key, val, _ := strings.Cut(envVar, "=") - zkey := C.frankenphp_init_persistent_string(toUnsafeChar(key), C.size_t(len(key))) - zStr := C.frankenphp_init_persistent_string(toUnsafeChar(val), C.size_t(len(val))) + zkey := newPersistentZendString(key) + zStr := newPersistentZendString(val) C.__hash_update_string__(mainThreadEnv, zkey, zStr) } } diff --git a/frankenphp.c b/frankenphp.c index 8786892af0..d79c5043fc 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -37,7 +37,7 @@ #include "_cgo_export.h" #include "frankenphp_arginfo.h" -#include "internal/strings/strings.h" +#include "internal/stringcache/stringcache.h" #if defined(PHP_WIN32) && defined(ZTS) ZEND_TSRMLS_CACHE_DEFINE() @@ -810,8 +810,8 @@ static inline void frankenphp_register_trusted_var(zend_string *z_key, } /* Register known $_SERVER variables in bulk to avoid cgo overhead */ -void frankenphp_register_bulk(zval *track_vars_array, - frankenphp_server_vars vars) { +void frankenphp_register_server_vars(zval *track_vars_array, + frankenphp_server_vars vars) { HashTable *ht = Z_ARRVAL_P(track_vars_array); zend_hash_extend(ht, vars.total_num_vars, 0); @@ -856,28 +856,36 @@ void frankenphp_register_bulk(zval *track_vars_array, } /* Register variables from SG(request_info) into $_SERVER */ +static inline void +frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, + bool must_be_present, + zval *track_vars_array) { + if (value != NULL) { + frankenphp_register_trusted_var(zKey, value, strlen(value), + Z_ARRVAL_P(track_vars_array)); + } else if (must_be_present) { + frankenphp_register_trusted_var(zKey, NULL, 0, + Z_ARRVAL_P(track_vars_array)); + } +} + static void frankenphp_register_variables_from_request_info(zval *track_vars_array) { - HashTable *ht = Z_ARRVAL_P(track_vars_array); - -#define FRANKENPHP_REGISTER_FROM_INFO(key, field, required) \ - do { \ - char *value = (char *)SG(request_info).field; \ - if (value != NULL) { \ - frankenphp_register_trusted_var(interned_strings.key, value, \ - strlen(value), ht); \ - } else if (required) { \ - frankenphp_register_trusted_var(interned_strings.key, NULL, 0, ht); \ - } \ - } while (0) - - FRANKENPHP_REGISTER_FROM_INFO(content_type, content_type, true); - FRANKENPHP_REGISTER_FROM_INFO(path_translated, path_translated, false); - FRANKENPHP_REGISTER_FROM_INFO(query_string, query_string, true); - FRANKENPHP_REGISTER_FROM_INFO(remote_user, auth_user, false); - FRANKENPHP_REGISTER_FROM_INFO(request_method, request_method, false); - -#undef FRANKENPHP_REGISTER_FROM_INFO + frankenphp_register_variable_from_request_info( + interned_strings.content_type, (char *)SG(request_info).content_type, + true, track_vars_array); + frankenphp_register_variable_from_request_info( + interned_strings.path_translated, + (char *)SG(request_info).path_translated, false, track_vars_array); + frankenphp_register_variable_from_request_info(interned_strings.query_string, + SG(request_info).query_string, + true, track_vars_array); + frankenphp_register_variable_from_request_info( + interned_strings.remote_user, (char *)SG(request_info).auth_user, false, + track_vars_array); + frankenphp_register_variable_from_request_info( + interned_strings.request_method, (char *)SG(request_info).request_method, + false, track_vars_array); } /* Only hard-coded keys may be registered this way */ @@ -924,7 +932,10 @@ static void frankenphp_register_variables(zval *track_vars_array) { */ zend_hash_copy(Z_ARR_P(track_vars_array), main_thread_env, NULL); + /* All CGI variables are imported from the request context in go */ go_register_variables(thread_index, track_vars_array); + + /* Some variables are already present in SG(request_info) */ frankenphp_register_variables_from_request_info(track_vars_array); } diff --git a/frankenphp.h b/frankenphp.h index 391b44509d..aae8025a1e 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -131,8 +131,8 @@ void frankenphp_register_variable_unsafe(zend_string *z_key, char *value, zval *track_vars_array); void frankenphp_register_variable_safe(char *key, char *var, size_t val_len, zval *track_vars_array); -void frankenphp_register_bulk(zval *track_vars_array, - frankenphp_server_vars vars); +void frankenphp_register_server_vars(zval *track_vars_array, + frankenphp_server_vars vars); zend_string *frankenphp_init_persistent_string(const char *string, size_t len); int frankenphp_reset_opcache(void); diff --git a/internal/phpheaders/phpheaders.go b/internal/stringcache/stringcache.go similarity index 99% rename from internal/phpheaders/phpheaders.go rename to internal/stringcache/stringcache.go index 71f23e7d92..f9edce8d33 100644 --- a/internal/phpheaders/phpheaders.go +++ b/internal/stringcache/stringcache.go @@ -1,4 +1,4 @@ -package phpheaders +package stringcache import "C" import ( diff --git a/internal/strings/strings.h b/internal/stringcache/stringcache.h similarity index 99% rename from internal/strings/strings.h rename to internal/stringcache/stringcache.h index ce0d9edc5b..d78276e881 100644 --- a/internal/strings/strings.h +++ b/internal/stringcache/stringcache.h @@ -49,7 +49,7 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } -#define FRANKENPHP_INTERNED_STR(str) \ +#define FRANKENPHP_INTERNED_STR(str) \ frankenphp_init_persistent_string(str, sizeof(str) - 1) static frankenphp_interned_strings_t frankenphp_init_interned_strings() { diff --git a/internal/phpheaders/phpheaders_test.go b/internal/stringcache/stringcache_test.go similarity index 97% rename from internal/phpheaders/phpheaders_test.go rename to internal/stringcache/stringcache_test.go index 5382c1ed9c..53ba0773b0 100644 --- a/internal/phpheaders/phpheaders_test.go +++ b/internal/stringcache/stringcache_test.go @@ -1,4 +1,4 @@ -package phpheaders +package stringcache import ( "net/http/httptest" diff --git a/phpmainthread.go b/phpmainthread.go index f79d2fee66..360069278e 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -3,7 +3,6 @@ package frankenphp // #cgo nocallback frankenphp_new_main_thread // #cgo noescape frankenphp_new_main_thread // #include -// #cgo noescape frankenphp_init_persistent_string // #include "frankenphp.h" // #include import "C" @@ -13,18 +12,18 @@ import ( "sync" "github.com/dunglas/frankenphp/internal/memory" - "github.com/dunglas/frankenphp/internal/phpheaders" "github.com/dunglas/frankenphp/internal/state" + "github.com/dunglas/frankenphp/internal/stringcache" ) // represents the main PHP thread // the thread needs to keep running as long as all other threads are running type phpMainThread struct { - state *state.ThreadState - done chan struct{} - numThreads int - maxThreads int - phpIni map[string]string + state *state.ThreadState + done chan struct{} + numThreads int + maxThreads int + phpIni map[string]string } var ( @@ -111,9 +110,9 @@ func (mainThread *phpMainThread) start() error { // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) if commonHeaders == nil { - commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) - for key, phpKey := range phpheaders.CommonRequestHeaders { - commonHeaders[key] = C.frankenphp_init_persistent_string(C.CString(phpKey), C.size_t(len(phpKey))) + commonHeaders = make(map[string]*C.zend_string, len(stringcache.CommonRequestHeaders)) + for key, phpKey := range stringcache.CommonRequestHeaders { + commonHeaders[key] = newPersistentZendString(phpKey) } } From 782fe9b4e9858bebcd1e0e575fc005bd6222f4dd Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Fri, 27 Feb 2026 15:55:31 +0100 Subject: [PATCH 18/32] More refactoring. --- cgi.go | 24 ++-- frankenphp.c | 106 +++++++++++++----- frankenphp.h | 43 ++++++- .../phpheaders.go} | 2 +- .../phpheaders_test.go} | 2 +- internal/stringcache/stringcache.h | 92 --------------- phpmainthread.go | 6 +- 7 files changed, 139 insertions(+), 136 deletions(-) rename internal/{stringcache/stringcache.go => phpheaders/phpheaders.go} (99%) rename internal/{stringcache/stringcache_test.go => phpheaders/phpheaders_test.go} (97%) delete mode 100644 internal/stringcache/stringcache.h diff --git a/cgi.go b/cgi.go index ec129b3a4d..53f8b5b6ac 100644 --- a/cgi.go +++ b/cgi.go @@ -21,7 +21,7 @@ import ( "unicode/utf8" "unsafe" - "github.com/dunglas/frankenphp/internal/stringcache" + "github.com/dunglas/frankenphp/internal/phpheaders" "golang.org/x/text/language" "golang.org/x/text/search" ) @@ -69,15 +69,16 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { ip = ip[1 : len(ip)-1] } - var https, sslProtocol, sslCipher, rs string + var https, sslProtocol, sslCipher string + var rs *C.zend_string if request.TLS == nil { - rs = "http" + rs = C.frankenphp_interned_strings.http https = "" sslProtocol = "" sslCipher = "" } else { - rs = "https" + rs = C.frankenphp_interned_strings.https https = "on" // and pass the protocol details in a manner compatible with Apache's mod_ssl @@ -106,9 +107,9 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { // even if the port is the default port for the scheme and could otherwise be omitted from a URI. // https://tools.ietf.org/html/rfc3875#section-4.1.15 switch rs { - case "https": + case C.frankenphp_interned_strings.https: reqPort = "443" - case "http": + case C.frankenphp_interned_strings.http: reqPort = "80" } } @@ -152,8 +153,6 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { https_len: C.size_t(len(https)), ssl_protocol: toUnsafeChar(sslProtocol), ssl_protocol_len: C.size_t(len(sslProtocol)), - request_scheme: toUnsafeChar(rs), - request_scheme_len: C.size_t(len(rs)), server_name: toUnsafeChar(reqHost), server_name_len: C.size_t(len(reqHost)), server_port: toUnsafeChar(serverPort), @@ -168,6 +167,7 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { request_uri_len: C.size_t(len(requestURI)), ssl_cipher: toUnsafeChar(sslCipher), ssl_cipher_len: C.size_t(len(sslCipher)), + request_scheme: rs, }) } @@ -181,7 +181,7 @@ func addHeadersToServer(ctx context.Context, request *http.Request, trackVarsArr // if the header name could not be cached, it needs to be registered safely // this is more inefficient but allows additional sanitizing by PHP - k := stringcache.GetUnCommonHeader(ctx, field) + k := phpheaders.GetUnCommonHeader(ctx, field) v := strings.Join(val, ", ") C.frankenphp_register_variable_safe(toUnsafeChar(k), toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) } @@ -194,8 +194,8 @@ func addPreparedEnvToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { fc.env = nil } -//export go_register_variables -func go_register_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) { +//export go_register_server_variables +func go_register_server_variables(threadIndex C.uintptr_t, trackVarsArray *C.zval) { thread := phpThreads[threadIndex] fc := thread.frankenPHPContext() @@ -312,7 +312,7 @@ func go_update_request_info(threadIndex C.uintptr_t, info *C.sapi_request_info) } if m, ok := cStringHTTPMethods[request.Method]; ok { - info.request_method = m + info.request_method = (*C.char)(m) } else { info.request_method = thread.pinCString(request.Method) } diff --git a/frankenphp.c b/frankenphp.c index d79c5043fc..ac21ac81e2 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -37,7 +37,6 @@ #include "_cgo_export.h" #include "frankenphp_arginfo.h" -#include "internal/stringcache/stringcache.h" #if defined(PHP_WIN32) && defined(ZTS) ZEND_TSRMLS_CACHE_DEFINE() @@ -81,7 +80,7 @@ frankenphp_config frankenphp_get_config() { bool should_filter_var = 0; bool original_user_abort_setting = 0; -frankenphp_interned_strings_t interned_strings; +frankenphp_interned_strings_t frankenphp_interned_strings = {0}; HashTable *main_thread_env = NULL; __thread uintptr_t thread_index; @@ -817,7 +816,7 @@ void frankenphp_register_server_vars(zval *track_vars_array, // update values with variable strings #define FRANKENPHP_REGISTER_VAR(name) \ - frankenphp_register_trusted_var(interned_strings.name, vars.name, \ + frankenphp_register_trusted_var(frankenphp_interned_strings.name, vars.name, \ vars.name##_len, ht) FRANKENPHP_REGISTER_VAR(remote_addr); @@ -832,7 +831,6 @@ void frankenphp_register_server_vars(zval *track_vars_array, FRANKENPHP_REGISTER_VAR(https); FRANKENPHP_REGISTER_VAR(ssl_protocol); FRANKENPHP_REGISTER_VAR(ssl_cipher); - FRANKENPHP_REGISTER_VAR(request_scheme); FRANKENPHP_REGISTER_VAR(server_name); FRANKENPHP_REGISTER_VAR(server_port); FRANKENPHP_REGISTER_VAR(content_length); @@ -842,19 +840,77 @@ void frankenphp_register_server_vars(zval *track_vars_array, #undef FRANKENPHP_REGISTER_VAR - // update values with unchanging strings + // update values with known zend_strings zval zv; - ZVAL_STR(&zv, interned_strings.gateway_interface_str); - zend_hash_update(ht, interned_strings.gateway_interface, &zv); - ZVAL_STR(&zv, interned_strings.server_software_str); - zend_hash_update(ht, interned_strings.server_software, &zv); - - // update values with empty strings + ZVAL_STR(&zv, frankenphp_interned_strings.gateway_interface_str); + zend_hash_update(ht, frankenphp_interned_strings.gateway_interface, &zv); + ZVAL_STR(&zv, frankenphp_interned_strings.server_software_str); + zend_hash_update(ht, frankenphp_interned_strings.server_software, &zv); + ZVAL_STR(&zv, vars.request_scheme); + zend_hash_update(ht, frankenphp_interned_strings.request_scheme, &zv); + + // update values with always empty strings ZVAL_EMPTY_STRING(&zv); - zend_hash_update(ht, interned_strings.auth_type, &zv); - zend_hash_update(ht, interned_strings.remote_ident, &zv); + zend_hash_update(ht, frankenphp_interned_strings.auth_type, &zv); + zend_hash_update(ht, frankenphp_interned_strings.remote_ident, &zv); +} + +zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { + /* persistent strings will be ignored by the GC at the end of a request */ + zend_string *z_string = zend_string_init(string, len, 1); + zend_string_hash_val(z_string); + + /* interned strings will not be ref counted by the GC */ + GC_ADD_FLAGS(z_string, IS_STR_INTERNED); + + return z_string; +} + +#define FRANKENPHP_INTERNED_STR(str) \ + frankenphp_init_persistent_string(str, sizeof(str) - 1) + +void frankenphp_init_interned_strings(void) { + if (frankenphp_interned_strings.remote_addr != NULL) { + return; /* already initialized */ + } + + frankenphp_interned_strings = (frankenphp_interned_strings_t){ + .remote_addr = FRANKENPHP_INTERNED_STR("REMOTE_ADDR"), + .remote_host = FRANKENPHP_INTERNED_STR("REMOTE_HOST"), + .remote_port = FRANKENPHP_INTERNED_STR("REMOTE_PORT"), + .document_root = FRANKENPHP_INTERNED_STR("DOCUMENT_ROOT"), + .path_info = FRANKENPHP_INTERNED_STR("PATH_INFO"), + .php_self = FRANKENPHP_INTERNED_STR("PHP_SELF"), + .document_uri = FRANKENPHP_INTERNED_STR("DOCUMENT_URI"), + .script_filename = FRANKENPHP_INTERNED_STR("SCRIPT_FILENAME"), + .script_name = FRANKENPHP_INTERNED_STR("SCRIPT_NAME"), + .https = FRANKENPHP_INTERNED_STR("https"), + .http = FRANKENPHP_INTERNED_STR("http"), + .ssl_protocol = FRANKENPHP_INTERNED_STR("SSL_PROTOCOL"), + .request_scheme = FRANKENPHP_INTERNED_STR("REQUEST_SCHEME"), + .server_name = FRANKENPHP_INTERNED_STR("SERVER_NAME"), + .server_port = FRANKENPHP_INTERNED_STR("SERVER_PORT"), + .content_length = FRANKENPHP_INTERNED_STR("CONTENT_LENGTH"), + .server_protocol = FRANKENPHP_INTERNED_STR("SERVER_PROTOCOL"), + .http_host = FRANKENPHP_INTERNED_STR("HTTP_HOST"), + .request_uri = FRANKENPHP_INTERNED_STR("REQUEST_URI"), + .ssl_cipher = FRANKENPHP_INTERNED_STR("SSL_CIPHER"), + .server_software = FRANKENPHP_INTERNED_STR("SERVER_SOFTWARE"), + .server_software_str = FRANKENPHP_INTERNED_STR("FrankenPHP"), + .gateway_interface = FRANKENPHP_INTERNED_STR("GATEWAY_INTERFACE"), + .gateway_interface_str = FRANKENPHP_INTERNED_STR("CGI/1.1"), + .auth_type = FRANKENPHP_INTERNED_STR("AUTH_TYPE"), + .remote_ident = FRANKENPHP_INTERNED_STR("REMOTE_IDENT"), + .content_type = FRANKENPHP_INTERNED_STR("CONTENT_TYPE"), + .path_translated = FRANKENPHP_INTERNED_STR("PATH_TRANSLATED"), + .query_string = FRANKENPHP_INTERNED_STR("QUERY_STRING"), + .remote_user = FRANKENPHP_INTERNED_STR("REMOTE_USER"), + .request_method = FRANKENPHP_INTERNED_STR("REQUEST_METHOD"), + }; } +#undef FRANKENPHP_INTERNED_STR + /* Register variables from SG(request_info) into $_SERVER */ static inline void frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, @@ -872,20 +928,20 @@ frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, static void frankenphp_register_variables_from_request_info(zval *track_vars_array) { frankenphp_register_variable_from_request_info( - interned_strings.content_type, (char *)SG(request_info).content_type, - true, track_vars_array); + frankenphp_interned_strings.content_type, + (char *)SG(request_info).content_type, true, track_vars_array); frankenphp_register_variable_from_request_info( - interned_strings.path_translated, + frankenphp_interned_strings.path_translated, (char *)SG(request_info).path_translated, false, track_vars_array); - frankenphp_register_variable_from_request_info(interned_strings.query_string, - SG(request_info).query_string, - true, track_vars_array); frankenphp_register_variable_from_request_info( - interned_strings.remote_user, (char *)SG(request_info).auth_user, false, - track_vars_array); + frankenphp_interned_strings.query_string, SG(request_info).query_string, + true, track_vars_array); + frankenphp_register_variable_from_request_info( + frankenphp_interned_strings.remote_user, + (char *)SG(request_info).auth_user, false, track_vars_array); frankenphp_register_variable_from_request_info( - interned_strings.request_method, (char *)SG(request_info).request_method, - false, track_vars_array); + frankenphp_interned_strings.request_method, + (char *)SG(request_info).request_method, false, track_vars_array); } /* Only hard-coded keys may be registered this way */ @@ -933,7 +989,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { zend_hash_copy(Z_ARR_P(track_vars_array), main_thread_env, NULL); /* All CGI variables are imported from the request context in go */ - go_register_variables(thread_index, track_vars_array); + go_register_server_variables(thread_index, track_vars_array); /* Some variables are already present in SG(request_info) */ frankenphp_register_variables_from_request_info(track_vars_array); @@ -1079,7 +1135,7 @@ static void *php_main(void *arg) { frankenphp_sapi_module.ini_entries = php_ini_overrides; } - interned_strings = frankenphp_init_interned_strings(); + frankenphp_init_interned_strings(); frankenphp_sapi_module.startup(&frankenphp_sapi_module); diff --git a/frankenphp.h b/frankenphp.h index aae8025a1e..b0f849440d 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -81,8 +81,6 @@ typedef struct frankenphp_server_vars { size_t https_len; char *ssl_protocol; size_t ssl_protocol_len; - char *request_scheme; - size_t request_scheme_len; char *server_name; size_t server_name_len; char *server_port; @@ -97,8 +95,49 @@ typedef struct frankenphp_server_vars { size_t request_uri_len; char *ssl_cipher; size_t ssl_cipher_len; + zend_string *request_scheme; } frankenphp_server_vars; +/** + * Cached interned strings for memory and performance benefits + * Add more hard-coded strings here if needed + */ +typedef struct frankenphp_interned_strings_t { + zend_string *remote_addr; + zend_string *remote_host; + zend_string *remote_port; + zend_string *document_root; + zend_string *path_info; + zend_string *php_self; + zend_string *document_uri; + zend_string *script_filename; + zend_string *script_name; + zend_string *http; + zend_string *https; + zend_string *ssl_protocol; + zend_string *request_scheme; + zend_string *server_name; + zend_string *server_port; + zend_string *content_length; + zend_string *server_protocol; + zend_string *http_host; + zend_string *request_uri; + zend_string *ssl_cipher; + zend_string *server_software; + zend_string *server_software_str; + zend_string *gateway_interface; + zend_string *gateway_interface_str; + zend_string *auth_type; + zend_string *remote_ident; + zend_string *content_type; + zend_string *path_translated; + zend_string *query_string; + zend_string *remote_user; + zend_string *request_method; +} frankenphp_interned_strings_t; + +extern frankenphp_interned_strings_t frankenphp_interned_strings; + typedef struct frankenphp_version { unsigned char major_version; unsigned char minor_version; diff --git a/internal/stringcache/stringcache.go b/internal/phpheaders/phpheaders.go similarity index 99% rename from internal/stringcache/stringcache.go rename to internal/phpheaders/phpheaders.go index f9edce8d33..71f23e7d92 100644 --- a/internal/stringcache/stringcache.go +++ b/internal/phpheaders/phpheaders.go @@ -1,4 +1,4 @@ -package stringcache +package phpheaders import "C" import ( diff --git a/internal/stringcache/stringcache_test.go b/internal/phpheaders/phpheaders_test.go similarity index 97% rename from internal/stringcache/stringcache_test.go rename to internal/phpheaders/phpheaders_test.go index 53ba0773b0..5382c1ed9c 100644 --- a/internal/stringcache/stringcache_test.go +++ b/internal/phpheaders/phpheaders_test.go @@ -1,4 +1,4 @@ -package stringcache +package phpheaders import ( "net/http/httptest" diff --git a/internal/stringcache/stringcache.h b/internal/stringcache/stringcache.h deleted file mode 100644 index d78276e881..0000000000 --- a/internal/stringcache/stringcache.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef _FRANKENPHP_STRINGS_H -#define _FRANKENPHP_STRINGS_H - -/** - * Cached interned strings for memory and performance benefits - * Add more hard-coded strings here if needed - */ -typedef struct frankenphp_interned_strings_t { - zend_string *remote_addr; - zend_string *remote_host; - zend_string *remote_port; - zend_string *document_root; - zend_string *path_info; - zend_string *php_self; - zend_string *document_uri; - zend_string *script_filename; - zend_string *script_name; - zend_string *https; - zend_string *ssl_protocol; - zend_string *request_scheme; - zend_string *server_name; - zend_string *server_port; - zend_string *content_length; - zend_string *server_protocol; - zend_string *http_host; - zend_string *request_uri; - zend_string *ssl_cipher; - zend_string *server_software; - zend_string *server_software_str; - zend_string *gateway_interface; - zend_string *gateway_interface_str; - zend_string *auth_type; - zend_string *remote_ident; - zend_string *content_type; - zend_string *path_translated; - zend_string *query_string; - zend_string *remote_user; - zend_string *request_method; -} frankenphp_interned_strings_t; - -zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { - /* persistent strings will be ignored by the GC at the end of a request */ - zend_string *z_string = zend_string_init(string, len, 1); - zend_string_hash_val(z_string); - - /* interned strings will not be ref counted by the GC */ - GC_ADD_FLAGS(z_string, IS_STR_INTERNED); - - return z_string; -} - -#define FRANKENPHP_INTERNED_STR(str) \ - frankenphp_init_persistent_string(str, sizeof(str) - 1) - -static frankenphp_interned_strings_t frankenphp_init_interned_strings() { - return (frankenphp_interned_strings_t){ - .remote_addr = FRANKENPHP_INTERNED_STR("REMOTE_ADDR"), - .remote_host = FRANKENPHP_INTERNED_STR("REMOTE_HOST"), - .remote_port = FRANKENPHP_INTERNED_STR("REMOTE_PORT"), - .document_root = FRANKENPHP_INTERNED_STR("DOCUMENT_ROOT"), - .path_info = FRANKENPHP_INTERNED_STR("PATH_INFO"), - .php_self = FRANKENPHP_INTERNED_STR("PHP_SELF"), - .document_uri = FRANKENPHP_INTERNED_STR("DOCUMENT_URI"), - .script_filename = FRANKENPHP_INTERNED_STR("SCRIPT_FILENAME"), - .script_name = FRANKENPHP_INTERNED_STR("SCRIPT_NAME"), - .https = FRANKENPHP_INTERNED_STR("HTTPS"), - .ssl_protocol = FRANKENPHP_INTERNED_STR("SSL_PROTOCOL"), - .request_scheme = FRANKENPHP_INTERNED_STR("REQUEST_SCHEME"), - .server_name = FRANKENPHP_INTERNED_STR("SERVER_NAME"), - .server_port = FRANKENPHP_INTERNED_STR("SERVER_PORT"), - .content_length = FRANKENPHP_INTERNED_STR("CONTENT_LENGTH"), - .server_protocol = FRANKENPHP_INTERNED_STR("SERVER_PROTOCOL"), - .http_host = FRANKENPHP_INTERNED_STR("HTTP_HOST"), - .request_uri = FRANKENPHP_INTERNED_STR("REQUEST_URI"), - .ssl_cipher = FRANKENPHP_INTERNED_STR("SSL_CIPHER"), - .server_software = FRANKENPHP_INTERNED_STR("SERVER_SOFTWARE"), - .server_software_str = FRANKENPHP_INTERNED_STR("FrankenPHP"), - .gateway_interface = FRANKENPHP_INTERNED_STR("GATEWAY_INTERFACE"), - .gateway_interface_str = FRANKENPHP_INTERNED_STR("CGI/1.1"), - .auth_type = FRANKENPHP_INTERNED_STR("AUTH_TYPE"), - .remote_ident = FRANKENPHP_INTERNED_STR("REMOTE_IDENT"), - .content_type = FRANKENPHP_INTERNED_STR("CONTENT_TYPE"), - .path_translated = FRANKENPHP_INTERNED_STR("PATH_TRANSLATED"), - .query_string = FRANKENPHP_INTERNED_STR("QUERY_STRING"), - .remote_user = FRANKENPHP_INTERNED_STR("REMOTE_USER"), - .request_method = FRANKENPHP_INTERNED_STR("REQUEST_METHOD"), - }; -} - -#undef FRANKENPHP_INTERNED_STR - -#endif diff --git a/phpmainthread.go b/phpmainthread.go index 360069278e..1801e396ce 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -12,8 +12,8 @@ import ( "sync" "github.com/dunglas/frankenphp/internal/memory" + "github.com/dunglas/frankenphp/internal/phpheaders" "github.com/dunglas/frankenphp/internal/state" - "github.com/dunglas/frankenphp/internal/stringcache" ) // represents the main PHP thread @@ -110,8 +110,8 @@ func (mainThread *phpMainThread) start() error { // cache common request headers as zend_strings (HTTP_ACCEPT, HTTP_USER_AGENT, etc.) if commonHeaders == nil { - commonHeaders = make(map[string]*C.zend_string, len(stringcache.CommonRequestHeaders)) - for key, phpKey := range stringcache.CommonRequestHeaders { + commonHeaders = make(map[string]*C.zend_string, len(phpheaders.CommonRequestHeaders)) + for key, phpKey := range phpheaders.CommonRequestHeaders { commonHeaders[key] = newPersistentZendString(phpKey) } } From 66bcee98156f34063e8b08d87b550586880cda29 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Fri, 27 Feb 2026 16:02:31 +0100 Subject: [PATCH 19/32] Cleanup. --- .codespellrc | 2 +- cgi.go | 2 +- frankenphp.c | 3 ++- frankenphp.go | 2 +- phpmainthread.go | 1 - 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.codespellrc b/.codespellrc index 42da17a109..15e29a7bfc 100644 --- a/.codespellrc +++ b/.codespellrc @@ -1,3 +1,3 @@ [codespell] check-hidden = -skip = .git,docs/*/*,docs,*/go.mod,*/go.sum,./internal/stringcache/stringcache.go +skip = .git,docs/*/*,docs,*/go.mod,*/go.sum,./internal/phpheaders/phpheaders.go diff --git a/cgi.go b/cgi.go index 53f8b5b6ac..124939d109 100644 --- a/cgi.go +++ b/cgi.go @@ -312,7 +312,7 @@ func go_update_request_info(threadIndex C.uintptr_t, info *C.sapi_request_info) } if m, ok := cStringHTTPMethods[request.Method]; ok { - info.request_method = (*C.char)(m) + info.request_method = m } else { info.request_method = thread.pinCString(request.Method) } diff --git a/frankenphp.c b/frankenphp.c index ac21ac81e2..86e99864cc 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -855,6 +855,7 @@ void frankenphp_register_server_vars(zval *track_vars_array, zend_hash_update(ht, frankenphp_interned_strings.remote_ident, &zv); } +/** Create an immutable zend_string that lasts for the whole process **/ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { /* persistent strings will be ignored by the GC at the end of a request */ zend_string *z_string = zend_string_init(string, len, 1); @@ -988,7 +989,7 @@ static void frankenphp_register_variables(zval *track_vars_array) { */ zend_hash_copy(Z_ARR_P(track_vars_array), main_thread_env, NULL); - /* All CGI variables are imported from the request context in go */ + /* import CGI variables from the request context in go */ go_register_server_variables(thread_index, track_vars_array); /* Some variables are already present in SG(request_info) */ diff --git a/frankenphp.go b/frankenphp.go index 06be5cc0d1..3d571b226c 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -36,7 +36,7 @@ import ( "time" "unsafe" // debug on Linux - // _ "github.com/ianlancetaylor/cgosymbolizer" + //_ "github.com/ianlancetaylor/cgosymbolizer" ) type contextKeyStruct struct{} diff --git a/phpmainthread.go b/phpmainthread.go index 1801e396ce..ba3917e846 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -2,7 +2,6 @@ package frankenphp // #cgo nocallback frankenphp_new_main_thread // #cgo noescape frankenphp_new_main_thread -// #include // #include "frankenphp.h" // #include import "C" From 0a29f826ebf5eb3e81188945d17291476ba4446b Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Fri, 27 Feb 2026 16:07:27 +0100 Subject: [PATCH 20/32] Lowercase fixes. --- cgi.go | 8 ++++---- frankenphp.c | 5 +++-- frankenphp.h | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/cgi.go b/cgi.go index 124939d109..299769f761 100644 --- a/cgi.go +++ b/cgi.go @@ -73,12 +73,12 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { var rs *C.zend_string if request.TLS == nil { - rs = C.frankenphp_interned_strings.http + rs = C.frankenphp_interned_strings.httpLowercase https = "" sslProtocol = "" sslCipher = "" } else { - rs = C.frankenphp_interned_strings.https + rs = C.frankenphp_interned_strings.httpsLowercase https = "on" // and pass the protocol details in a manner compatible with Apache's mod_ssl @@ -107,9 +107,9 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { // even if the port is the default port for the scheme and could otherwise be omitted from a URI. // https://tools.ietf.org/html/rfc3875#section-4.1.15 switch rs { - case C.frankenphp_interned_strings.https: + case C.frankenphp_interned_strings.httpsLowercase: reqPort = "443" - case C.frankenphp_interned_strings.http: + case C.frankenphp_interned_strings.httpLowercase: reqPort = "80" } } diff --git a/frankenphp.c b/frankenphp.c index 86e99864cc..12b1ccbc3c 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -885,8 +885,9 @@ void frankenphp_init_interned_strings(void) { .document_uri = FRANKENPHP_INTERNED_STR("DOCUMENT_URI"), .script_filename = FRANKENPHP_INTERNED_STR("SCRIPT_FILENAME"), .script_name = FRANKENPHP_INTERNED_STR("SCRIPT_NAME"), - .https = FRANKENPHP_INTERNED_STR("https"), - .http = FRANKENPHP_INTERNED_STR("http"), + .https = FRANKENPHP_INTERNED_STR("HTTPS"), + .httpsLowercase = FRANKENPHP_INTERNED_STR("https"), + .httpLowercase = FRANKENPHP_INTERNED_STR("http"), .ssl_protocol = FRANKENPHP_INTERNED_STR("SSL_PROTOCOL"), .request_scheme = FRANKENPHP_INTERNED_STR("REQUEST_SCHEME"), .server_name = FRANKENPHP_INTERNED_STR("SERVER_NAME"), diff --git a/frankenphp.h b/frankenphp.h index b0f849440d..9e646dae4f 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -112,8 +112,9 @@ typedef struct frankenphp_interned_strings_t { zend_string *document_uri; zend_string *script_filename; zend_string *script_name; - zend_string *http; zend_string *https; + zend_string *httpsLowercase; + zend_string *httpLowercase; zend_string *ssl_protocol; zend_string *request_scheme; zend_string *server_name; From 39bf5dc209add02e335666d0fa87a50f2a27ad8e Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 28 Feb 2026 23:52:47 +0100 Subject: [PATCH 21/32] More macro shenanigans. --- cgi.go | 2 +- frankenphp.c | 45 +++++----------------------------- frankenphp.h | 69 ++++++++++++++++++++++++++++------------------------ 3 files changed, 44 insertions(+), 72 deletions(-) diff --git a/cgi.go b/cgi.go index 299769f761..1b47cdbefb 100644 --- a/cgi.go +++ b/cgi.go @@ -8,8 +8,8 @@ package frankenphp // #cgo noescape frankenphp_register_variable_safe // #cgo noescape frankenphp_register_variable_unsafe // #cgo noescape frankenphp_init_persistent_string -// #include // #include "frankenphp.h" +// #include import "C" import ( "context" diff --git a/frankenphp.c b/frankenphp.c index 12b1ccbc3c..ac8f8b3e27 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -867,51 +867,18 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } -#define FRANKENPHP_INTERNED_STR(str) \ - frankenphp_init_persistent_string(str, sizeof(str) - 1) - void frankenphp_init_interned_strings(void) { if (frankenphp_interned_strings.remote_addr != NULL) { return; /* already initialized */ } - frankenphp_interned_strings = (frankenphp_interned_strings_t){ - .remote_addr = FRANKENPHP_INTERNED_STR("REMOTE_ADDR"), - .remote_host = FRANKENPHP_INTERNED_STR("REMOTE_HOST"), - .remote_port = FRANKENPHP_INTERNED_STR("REMOTE_PORT"), - .document_root = FRANKENPHP_INTERNED_STR("DOCUMENT_ROOT"), - .path_info = FRANKENPHP_INTERNED_STR("PATH_INFO"), - .php_self = FRANKENPHP_INTERNED_STR("PHP_SELF"), - .document_uri = FRANKENPHP_INTERNED_STR("DOCUMENT_URI"), - .script_filename = FRANKENPHP_INTERNED_STR("SCRIPT_FILENAME"), - .script_name = FRANKENPHP_INTERNED_STR("SCRIPT_NAME"), - .https = FRANKENPHP_INTERNED_STR("HTTPS"), - .httpsLowercase = FRANKENPHP_INTERNED_STR("https"), - .httpLowercase = FRANKENPHP_INTERNED_STR("http"), - .ssl_protocol = FRANKENPHP_INTERNED_STR("SSL_PROTOCOL"), - .request_scheme = FRANKENPHP_INTERNED_STR("REQUEST_SCHEME"), - .server_name = FRANKENPHP_INTERNED_STR("SERVER_NAME"), - .server_port = FRANKENPHP_INTERNED_STR("SERVER_PORT"), - .content_length = FRANKENPHP_INTERNED_STR("CONTENT_LENGTH"), - .server_protocol = FRANKENPHP_INTERNED_STR("SERVER_PROTOCOL"), - .http_host = FRANKENPHP_INTERNED_STR("HTTP_HOST"), - .request_uri = FRANKENPHP_INTERNED_STR("REQUEST_URI"), - .ssl_cipher = FRANKENPHP_INTERNED_STR("SSL_CIPHER"), - .server_software = FRANKENPHP_INTERNED_STR("SERVER_SOFTWARE"), - .server_software_str = FRANKENPHP_INTERNED_STR("FrankenPHP"), - .gateway_interface = FRANKENPHP_INTERNED_STR("GATEWAY_INTERFACE"), - .gateway_interface_str = FRANKENPHP_INTERNED_STR("CGI/1.1"), - .auth_type = FRANKENPHP_INTERNED_STR("AUTH_TYPE"), - .remote_ident = FRANKENPHP_INTERNED_STR("REMOTE_IDENT"), - .content_type = FRANKENPHP_INTERNED_STR("CONTENT_TYPE"), - .path_translated = FRANKENPHP_INTERNED_STR("PATH_TRANSLATED"), - .query_string = FRANKENPHP_INTERNED_STR("QUERY_STRING"), - .remote_user = FRANKENPHP_INTERNED_STR("REMOTE_USER"), - .request_method = FRANKENPHP_INTERNED_STR("REQUEST_METHOD"), - }; -} +#define INITIALIZE_FIELD(name, str) \ + frankenphp_interned_strings.name = \ + frankenphp_init_persistent_string(str, sizeof(str) - 1); -#undef FRANKENPHP_INTERNED_STR + FRANKENPHP_INTERNED_STRINGS_LIST(INITIALIZE_FIELD) +#undef INITIALIZE_FIELD +} /* Register variables from SG(request_info) into $_SERVER */ static inline void diff --git a/frankenphp.h b/frankenphp.h index 9e646dae4f..12408cde09 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -102,39 +102,44 @@ typedef struct frankenphp_server_vars { * Cached interned strings for memory and performance benefits * Add more hard-coded strings here if needed */ +#define FRANKENPHP_INTERNED_STRINGS_LIST(X) \ + X(remote_addr, "REMOTE_ADDR") \ + X(remote_host, "REMOTE_HOST") \ + X(remote_port, "REMOTE_PORT") \ + X(document_root, "DOCUMENT_ROOT") \ + X(path_info, "PATH_INFO") \ + X(php_self, "PHP_SELF") \ + X(document_uri, "DOCUMENT_URI") \ + X(script_filename, "SCRIPT_FILENAME") \ + X(script_name, "SCRIPT_NAME") \ + X(https, "HTTPS") \ + X(httpsLowercase, "https") \ + X(httpLowercase, "http") \ + X(ssl_protocol, "SSL_PROTOCOL") \ + X(request_scheme, "REQUEST_SCHEME") \ + X(server_name, "SERVER_NAME") \ + X(server_port, "SERVER_PORT") \ + X(content_length, "CONTENT_LENGTH") \ + X(server_protocol, "SERVER_PROTOCOL") \ + X(http_host, "HTTP_HOST") \ + X(request_uri, "REQUEST_URI") \ + X(ssl_cipher, "SSL_CIPHER") \ + X(server_software, "SERVER_SOFTWARE") \ + X(server_software_str, "FrankenPHP") \ + X(gateway_interface, "GATEWAY_INTERFACE") \ + X(gateway_interface_str, "CGI/1.1") \ + X(auth_type, "AUTH_TYPE") \ + X(remote_ident, "REMOTE_IDENT") \ + X(content_type, "CONTENT_TYPE") \ + X(path_translated, "PATH_TRANSLATED") \ + X(query_string, "QUERY_STRING") \ + X(remote_user, "REMOTE_USER") \ + X(request_method, "REQUEST_METHOD") + typedef struct frankenphp_interned_strings_t { - zend_string *remote_addr; - zend_string *remote_host; - zend_string *remote_port; - zend_string *document_root; - zend_string *path_info; - zend_string *php_self; - zend_string *document_uri; - zend_string *script_filename; - zend_string *script_name; - zend_string *https; - zend_string *httpsLowercase; - zend_string *httpLowercase; - zend_string *ssl_protocol; - zend_string *request_scheme; - zend_string *server_name; - zend_string *server_port; - zend_string *content_length; - zend_string *server_protocol; - zend_string *http_host; - zend_string *request_uri; - zend_string *ssl_cipher; - zend_string *server_software; - zend_string *server_software_str; - zend_string *gateway_interface; - zend_string *gateway_interface_str; - zend_string *auth_type; - zend_string *remote_ident; - zend_string *content_type; - zend_string *path_translated; - zend_string *query_string; - zend_string *remote_user; - zend_string *request_method; +#define DEFINE_STRUCT_FIELD(name, str) zend_string *name; + FRANKENPHP_INTERNED_STRINGS_LIST(DEFINE_STRUCT_FIELD) +#undef DEFINE_STRUCT_FIELD } frankenphp_interned_strings_t; extern frankenphp_interned_strings_t frankenphp_interned_strings; From 848a864a4b68548ee6fd48e797fd2453f73cdf0f Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 28 Feb 2026 23:58:51 +0100 Subject: [PATCH 22/32] Cleanup. --- frankenphp.c | 8 ++++---- frankenphp.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index ac8f8b3e27..17777ed217 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -867,17 +867,17 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } -void frankenphp_init_interned_strings(void) { +static void frankenphp_init_interned_strings(void) { if (frankenphp_interned_strings.remote_addr != NULL) { return; /* already initialized */ } -#define INITIALIZE_FIELD(name, str) \ +#define F_INITIALIZE_FIELD(name, str) \ frankenphp_interned_strings.name = \ frankenphp_init_persistent_string(str, sizeof(str) - 1); - FRANKENPHP_INTERNED_STRINGS_LIST(INITIALIZE_FIELD) -#undef INITIALIZE_FIELD + FRANKENPHP_INTERNED_STRINGS_LIST(F_INITIALIZE_FIELD) +#undef F_INITIALIZE_FIELD } /* Register variables from SG(request_info) into $_SERVER */ diff --git a/frankenphp.h b/frankenphp.h index 12408cde09..0fc6a79152 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -137,9 +137,9 @@ typedef struct frankenphp_server_vars { X(request_method, "REQUEST_METHOD") typedef struct frankenphp_interned_strings_t { -#define DEFINE_STRUCT_FIELD(name, str) zend_string *name; - FRANKENPHP_INTERNED_STRINGS_LIST(DEFINE_STRUCT_FIELD) -#undef DEFINE_STRUCT_FIELD +#define F_DEFINE_STRUCT_FIELD(name, str) zend_string *name; + FRANKENPHP_INTERNED_STRINGS_LIST(F_DEFINE_STRUCT_FIELD) +#undef F_DEFINE_STRUCT_FIELD } frankenphp_interned_strings_t; extern frankenphp_interned_strings_t frankenphp_interned_strings; From e322077535b8fc688dc79606ccf65fbd2f6ff2a4 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 1 Mar 2026 00:20:32 +0100 Subject: [PATCH 23/32] More hard-coded zend_strings. --- cgi.go | 40 ++++++++++++++++++++++------------------ frankenphp.c | 17 +++++++++++------ frankenphp.h | 9 ++++++--- 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/cgi.go b/cgi.go index 1b47cdbefb..62aaa28cde 100644 --- a/cgi.go +++ b/cgi.go @@ -26,15 +26,6 @@ import ( "golang.org/x/text/search" ) -// Protocol versions, in Apache mod_ssl format: https://httpd.apache.org/docs/current/mod/mod_ssl.html -// Note that these are slightly different from SupportedProtocols in caddytls/config.go -var tlsProtocolStrings = map[uint16]string{ - tls.VersionTLS10: "TLSv1", - tls.VersionTLS11: "TLSv1.1", - tls.VersionTLS12: "TLSv1.2", - tls.VersionTLS13: "TLSv1.3", -} - // cStringHTTPMethods caches C string versions of common HTTP methods // to avoid allocations in pinCString on every request. var cStringHTTPMethods = map[string]*C.char{ @@ -69,13 +60,14 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { ip = ip[1 : len(ip)-1] } - var https, sslProtocol, sslCipher string + var https, sslCipher string + var sslProtocol *C.zend_string var rs *C.zend_string if request.TLS == nil { rs = C.frankenphp_interned_strings.httpLowercase https = "" - sslProtocol = "" + sslProtocol = nil sslCipher = "" } else { rs = C.frankenphp_interned_strings.httpsLowercase @@ -83,11 +75,7 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { // and pass the protocol details in a manner compatible with Apache's mod_ssl // (which is why these have an SSL_ prefix and not TLS_). - if v, ok := tlsProtocolStrings[request.TLS.Version]; ok { - sslProtocol = v - } else { - sslProtocol = "" - } + sslProtocol = tlsProtocol(request.TLS.Version) if request.TLS.CipherSuite != 0 { sslCipher = tls.CipherSuiteName(request.TLS.CipherSuite) @@ -151,8 +139,6 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { script_name_len: C.size_t(len(fc.scriptName)), https: toUnsafeChar(https), https_len: C.size_t(len(https)), - ssl_protocol: toUnsafeChar(sslProtocol), - ssl_protocol_len: C.size_t(len(sslProtocol)), server_name: toUnsafeChar(reqHost), server_name_len: C.size_t(len(reqHost)), server_port: toUnsafeChar(serverPort), @@ -168,6 +154,7 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { ssl_cipher: toUnsafeChar(sslCipher), ssl_cipher_len: C.size_t(len(sslCipher)), request_scheme: rs, + ssl_protocol: sslProtocol, }) } @@ -388,3 +375,20 @@ func toUnsafeChar(s string) *C.char { func newPersistentZendString(str string) *C.zend_string { return C.frankenphp_init_persistent_string(toUnsafeChar(str), C.size_t(len(str))) } + +// Protocol versions, in Apache mod_ssl format: https://httpd.apache.org/docs/current/mod/mod_ssl.html +// Note that these are slightly different from SupportedProtocols in caddytls/config.go +func tlsProtocol(proto uint16) *C.zend_string { + switch proto { + case tls.VersionTLS10: + return C.frankenphp_interned_strings.tls1 + case tls.VersionTLS11: + return C.frankenphp_interned_strings.tls11 + case tls.VersionTLS12: + return C.frankenphp_interned_strings.tls12 + case tls.VersionTLS13: + return C.frankenphp_interned_strings.tls13 + default: + return nil + } +} diff --git a/frankenphp.c b/frankenphp.c index 17777ed217..974d77a949 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -829,7 +829,6 @@ void frankenphp_register_server_vars(zval *track_vars_array, FRANKENPHP_REGISTER_VAR(script_filename); FRANKENPHP_REGISTER_VAR(script_name); FRANKENPHP_REGISTER_VAR(https); - FRANKENPHP_REGISTER_VAR(ssl_protocol); FRANKENPHP_REGISTER_VAR(ssl_cipher); FRANKENPHP_REGISTER_VAR(server_name); FRANKENPHP_REGISTER_VAR(server_port); @@ -840,7 +839,7 @@ void frankenphp_register_server_vars(zval *track_vars_array, #undef FRANKENPHP_REGISTER_VAR - // update values with known zend_strings + /* update values with hard-coded zend_strings */ zval zv; ZVAL_STR(&zv, frankenphp_interned_strings.gateway_interface_str); zend_hash_update(ht, frankenphp_interned_strings.gateway_interface, &zv); @@ -848,11 +847,17 @@ void frankenphp_register_server_vars(zval *track_vars_array, zend_hash_update(ht, frankenphp_interned_strings.server_software, &zv); ZVAL_STR(&zv, vars.request_scheme); zend_hash_update(ht, frankenphp_interned_strings.request_scheme, &zv); + if (vars.ssl_protocol == NULL) { + ZVAL_EMPTY_STRING(&zv); + } else { + ZVAL_STR(&zv, vars.ssl_protocol); + } + zend_hash_update(ht, frankenphp_interned_strings.ssl_protocol, &zv); - // update values with always empty strings + /* update values with always empty strings */ ZVAL_EMPTY_STRING(&zv); - zend_hash_update(ht, frankenphp_interned_strings.auth_type, &zv); - zend_hash_update(ht, frankenphp_interned_strings.remote_ident, &zv); + zend_hash_update_empty_string(ht, frankenphp_interned_strings.auth_type, &zv); + zend_hash_update_empty_string(ht, frankenphp_interned_strings.remote_ident, &zv); } /** Create an immutable zend_string that lasts for the whole process **/ @@ -872,7 +877,7 @@ static void frankenphp_init_interned_strings(void) { return; /* already initialized */ } -#define F_INITIALIZE_FIELD(name, str) \ +#define F_INITIALIZE_FIELD(name, str) \ frankenphp_interned_strings.name = \ frankenphp_init_persistent_string(str, sizeof(str) - 1); diff --git a/frankenphp.h b/frankenphp.h index 0fc6a79152..0c624e8aef 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -79,8 +79,6 @@ typedef struct frankenphp_server_vars { size_t script_name_len; char *https; size_t https_len; - char *ssl_protocol; - size_t ssl_protocol_len; char *server_name; size_t server_name_len; char *server_port; @@ -96,6 +94,7 @@ typedef struct frankenphp_server_vars { char *ssl_cipher; size_t ssl_cipher_len; zend_string *request_scheme; + zend_string *ssl_protocol; } frankenphp_server_vars; /** @@ -134,7 +133,11 @@ typedef struct frankenphp_server_vars { X(path_translated, "PATH_TRANSLATED") \ X(query_string, "QUERY_STRING") \ X(remote_user, "REMOTE_USER") \ - X(request_method, "REQUEST_METHOD") + X(request_method, "REQUEST_METHOD") \ + X(tls1, "TLSv1") \ + X(tls11, "TLSv1.1") \ + X(tls12, "TLSv1.2") \ + X(tls13, "TLSv1.3") typedef struct frankenphp_interned_strings_t { #define F_DEFINE_STRUCT_FIELD(name, str) zend_string *name; From fd55e6a87237cb0efb82152b05a5e14f8da721a6 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 1 Mar 2026 00:27:23 +0100 Subject: [PATCH 24/32] More hard-coded zend_strings. --- cgi.go | 16 +++++++--------- frankenphp.c | 13 +++++-------- frankenphp.h | 6 +++--- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/cgi.go b/cgi.go index 62aaa28cde..b7972b9760 100644 --- a/cgi.go +++ b/cgi.go @@ -60,18 +60,17 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { ip = ip[1 : len(ip)-1] } - var https, sslCipher string - var sslProtocol *C.zend_string - var rs *C.zend_string + var rs, https, sslProtocol *C.zend_string + var sslCipher string if request.TLS == nil { rs = C.frankenphp_interned_strings.httpLowercase - https = "" - sslProtocol = nil + https = C.zend_empty_string + sslProtocol = C.zend_empty_string sslCipher = "" } else { rs = C.frankenphp_interned_strings.httpsLowercase - https = "on" + https = C.frankenphp_interned_strings.on // and pass the protocol details in a manner compatible with Apache's mod_ssl // (which is why these have an SSL_ prefix and not TLS_). @@ -137,8 +136,6 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { script_filename_len: C.size_t(len(fc.scriptFilename)), script_name: toUnsafeChar(fc.scriptName), script_name_len: C.size_t(len(fc.scriptName)), - https: toUnsafeChar(https), - https_len: C.size_t(len(https)), server_name: toUnsafeChar(reqHost), server_name_len: C.size_t(len(reqHost)), server_port: toUnsafeChar(serverPort), @@ -155,6 +152,7 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { ssl_cipher_len: C.size_t(len(sslCipher)), request_scheme: rs, ssl_protocol: sslProtocol, + https: https, }) } @@ -389,6 +387,6 @@ func tlsProtocol(proto uint16) *C.zend_string { case tls.VersionTLS13: return C.frankenphp_interned_strings.tls13 default: - return nil + return C.zend_empty_string } } diff --git a/frankenphp.c b/frankenphp.c index 974d77a949..78494df677 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -828,7 +828,6 @@ void frankenphp_register_server_vars(zval *track_vars_array, FRANKENPHP_REGISTER_VAR(document_uri); FRANKENPHP_REGISTER_VAR(script_filename); FRANKENPHP_REGISTER_VAR(script_name); - FRANKENPHP_REGISTER_VAR(https); FRANKENPHP_REGISTER_VAR(ssl_cipher); FRANKENPHP_REGISTER_VAR(server_name); FRANKENPHP_REGISTER_VAR(server_port); @@ -847,17 +846,15 @@ void frankenphp_register_server_vars(zval *track_vars_array, zend_hash_update(ht, frankenphp_interned_strings.server_software, &zv); ZVAL_STR(&zv, vars.request_scheme); zend_hash_update(ht, frankenphp_interned_strings.request_scheme, &zv); - if (vars.ssl_protocol == NULL) { - ZVAL_EMPTY_STRING(&zv); - } else { - ZVAL_STR(&zv, vars.ssl_protocol); - } + ZVAL_STR(&zv, vars.ssl_protocol); zend_hash_update(ht, frankenphp_interned_strings.ssl_protocol, &zv); + ZVAL_STR(&zv, vars.https); + zend_hash_update(ht, frankenphp_interned_strings.https, &zv); /* update values with always empty strings */ ZVAL_EMPTY_STRING(&zv); - zend_hash_update_empty_string(ht, frankenphp_interned_strings.auth_type, &zv); - zend_hash_update_empty_string(ht, frankenphp_interned_strings.remote_ident, &zv); + zend_hash_update(ht, frankenphp_interned_strings.auth_type, &zv); + zend_hash_update(ht, frankenphp_interned_strings.remote_ident, &zv); } /** Create an immutable zend_string that lasts for the whole process **/ diff --git a/frankenphp.h b/frankenphp.h index 0c624e8aef..2ea5ce54ba 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -77,8 +77,6 @@ typedef struct frankenphp_server_vars { size_t script_filename_len; char *script_name; size_t script_name_len; - char *https; - size_t https_len; char *server_name; size_t server_name_len; char *server_port; @@ -95,6 +93,7 @@ typedef struct frankenphp_server_vars { size_t ssl_cipher_len; zend_string *request_scheme; zend_string *ssl_protocol; + zend_string *https; } frankenphp_server_vars; /** @@ -137,7 +136,8 @@ typedef struct frankenphp_server_vars { X(tls1, "TLSv1") \ X(tls11, "TLSv1.1") \ X(tls12, "TLSv1.2") \ - X(tls13, "TLSv1.3") + X(tls13, "TLSv1.3") \ + X(on, "on") typedef struct frankenphp_interned_strings_t { #define F_DEFINE_STRUCT_FIELD(name, str) zend_string *name; From 28d0443d98ebd7dda84ec33d77daa4444ff517ea Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 1 Mar 2026 00:30:17 +0100 Subject: [PATCH 25/32] Cleanup. --- cgi.go | 6 +++--- frankenphp.c | 4 ++-- frankenphp.h | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cgi.go b/cgi.go index b7972b9760..f99b14e470 100644 --- a/cgi.go +++ b/cgi.go @@ -2,11 +2,11 @@ package frankenphp // #cgo nocallback frankenphp_register_server_vars // #cgo nocallback frankenphp_register_variable_safe -// #cgo nocallback frankenphp_register_variable_unsafe +// #cgo nocallback frankenphp_register_known_variable // #cgo nocallback frankenphp_init_persistent_string // #cgo noescape frankenphp_register_server_vars // #cgo noescape frankenphp_register_variable_safe -// #cgo noescape frankenphp_register_variable_unsafe +// #cgo noescape frankenphp_register_known_variable // #cgo noescape frankenphp_init_persistent_string // #include "frankenphp.h" // #include @@ -160,7 +160,7 @@ func addHeadersToServer(ctx context.Context, request *http.Request, trackVarsArr for field, val := range request.Header { if k := commonHeaders[field]; k != nil { v := strings.Join(val, ", ") - C.frankenphp_register_variable_unsafe(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) + C.frankenphp_register_known_variable(k, toUnsafeChar(v), C.size_t(len(v)), trackVarsArray) continue } diff --git a/frankenphp.c b/frankenphp.c index 78494df677..33fc1e0359 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -842,7 +842,7 @@ void frankenphp_register_server_vars(zval *track_vars_array, zval zv; ZVAL_STR(&zv, frankenphp_interned_strings.gateway_interface_str); zend_hash_update(ht, frankenphp_interned_strings.gateway_interface, &zv); - ZVAL_STR(&zv, frankenphp_interned_strings.server_software_str); + ZVAL_STR(&zv, frankenphp_interned_strings.frankenphp); zend_hash_update(ht, frankenphp_interned_strings.server_software, &zv); ZVAL_STR(&zv, vars.request_scheme); zend_hash_update(ht, frankenphp_interned_strings.request_scheme, &zv); @@ -916,7 +916,7 @@ frankenphp_register_variables_from_request_info(zval *track_vars_array) { } /* Only hard-coded keys may be registered this way */ -void frankenphp_register_variable_unsafe(zend_string *z_key, char *value, +void frankenphp_register_known_variable(zend_string *z_key, char *value, size_t val_len, zval *track_vars_array) { frankenphp_register_trusted_var(z_key, value, val_len, diff --git a/frankenphp.h b/frankenphp.h index 2ea5ce54ba..7a7a5d6822 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -123,7 +123,7 @@ typedef struct frankenphp_server_vars { X(request_uri, "REQUEST_URI") \ X(ssl_cipher, "SSL_CIPHER") \ X(server_software, "SERVER_SOFTWARE") \ - X(server_software_str, "FrankenPHP") \ + X(frankenphp, "FrankenPHP") \ X(gateway_interface, "GATEWAY_INTERFACE") \ X(gateway_interface_str, "CGI/1.1") \ X(auth_type, "AUTH_TYPE") \ @@ -174,7 +174,7 @@ void frankenphp_update_local_thread_context(bool is_worker); int frankenphp_execute_script_cli(char *script, int argc, char **argv, bool eval); -void frankenphp_register_variable_unsafe(zend_string *z_key, char *value, +void frankenphp_register_known_variable(zend_string *z_key, char *value, size_t val_len, zval *track_vars_array); void frankenphp_register_variable_safe(char *key, char *var, size_t val_len, From a97a1263b4a1fc75d5fd7634fe2293f84101c94e Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 1 Mar 2026 16:03:44 +0100 Subject: [PATCH 26/32] Uses own empty string for window compatibiltiy. --- cgi.go | 35 +++++++++++++++++++---------------- frankenphp.c | 47 ++++++++++++++++++++++++----------------------- frankenphp.h | 8 ++++---- 3 files changed, 47 insertions(+), 43 deletions(-) diff --git a/cgi.go b/cgi.go index f99b14e470..06802d086e 100644 --- a/cgi.go +++ b/cgi.go @@ -64,13 +64,13 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { var sslCipher string if request.TLS == nil { - rs = C.frankenphp_interned_strings.httpLowercase - https = C.zend_empty_string - sslProtocol = C.zend_empty_string + rs = C.frankenphp_strings.httpLowercase + https = C.frankenphp_strings.empty + sslProtocol = C.frankenphp_strings.empty sslCipher = "" } else { - rs = C.frankenphp_interned_strings.httpsLowercase - https = C.frankenphp_interned_strings.on + rs = C.frankenphp_strings.httpsLowercase + https = C.frankenphp_strings.on // and pass the protocol details in a manner compatible with Apache's mod_ssl // (which is why these have an SSL_ prefix and not TLS_). @@ -94,9 +94,9 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { // even if the port is the default port for the scheme and could otherwise be omitted from a URI. // https://tools.ietf.org/html/rfc3875#section-4.1.15 switch rs { - case C.frankenphp_interned_strings.httpsLowercase: + case C.frankenphp_strings.httpsLowercase: reqPort = "443" - case C.frankenphp_interned_strings.httpLowercase: + case C.frankenphp_strings.httpLowercase: reqPort = "80" } } @@ -116,8 +116,9 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { C.frankenphp_register_server_vars(trackVarsArray, C.frankenphp_server_vars{ // approximate total length to avoid array re-hashing: // 28 CGI vars + headers + environment - total_num_vars: C.size_t(28 + len(fc.env) + len(request.Header) + lengthOfEnv), + total_num_vars: C.size_t(28 + len(request.Header) + len(fc.env) + lengthOfEnv), + // CGI vars with variable values remote_addr: toUnsafeChar(ip), remote_addr_len: C.size_t(len(ip)), remote_host: toUnsafeChar(ip), @@ -150,9 +151,11 @@ func addKnownVariablesToServer(fc *frankenPHPContext, trackVarsArray *C.zval) { request_uri_len: C.size_t(len(requestURI)), ssl_cipher: toUnsafeChar(sslCipher), ssl_cipher_len: C.size_t(len(sslCipher)), - request_scheme: rs, - ssl_protocol: sslProtocol, - https: https, + + // CGI vars with known values + request_scheme: rs, // "http" or "https" + ssl_protocol: sslProtocol, // values from tlsProtocol + https: https, // "on" or empty }) } @@ -379,14 +382,14 @@ func newPersistentZendString(str string) *C.zend_string { func tlsProtocol(proto uint16) *C.zend_string { switch proto { case tls.VersionTLS10: - return C.frankenphp_interned_strings.tls1 + return C.frankenphp_strings.tls1 case tls.VersionTLS11: - return C.frankenphp_interned_strings.tls11 + return C.frankenphp_strings.tls11 case tls.VersionTLS12: - return C.frankenphp_interned_strings.tls12 + return C.frankenphp_strings.tls12 case tls.VersionTLS13: - return C.frankenphp_interned_strings.tls13 + return C.frankenphp_strings.tls13 default: - return C.zend_empty_string + return C.frankenphp_strings.empty } } diff --git a/frankenphp.c b/frankenphp.c index 33fc1e0359..6672a8c9ab 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -80,7 +80,7 @@ frankenphp_config frankenphp_get_config() { bool should_filter_var = 0; bool original_user_abort_setting = 0; -frankenphp_interned_strings_t frankenphp_interned_strings = {0}; +frankenphp_interned_strings_t frankenphp_strings = {0}; HashTable *main_thread_env = NULL; __thread uintptr_t thread_index; @@ -816,7 +816,7 @@ void frankenphp_register_server_vars(zval *track_vars_array, // update values with variable strings #define FRANKENPHP_REGISTER_VAR(name) \ - frankenphp_register_trusted_var(frankenphp_interned_strings.name, vars.name, \ + frankenphp_register_trusted_var(frankenphp_strings.name, vars.name, \ vars.name##_len, ht) FRANKENPHP_REGISTER_VAR(remote_addr); @@ -840,21 +840,21 @@ void frankenphp_register_server_vars(zval *track_vars_array, /* update values with hard-coded zend_strings */ zval zv; - ZVAL_STR(&zv, frankenphp_interned_strings.gateway_interface_str); - zend_hash_update(ht, frankenphp_interned_strings.gateway_interface, &zv); - ZVAL_STR(&zv, frankenphp_interned_strings.frankenphp); - zend_hash_update(ht, frankenphp_interned_strings.server_software, &zv); + ZVAL_STR(&zv, frankenphp_strings.gateway_interface_str); + zend_hash_update_ind(ht, frankenphp_strings.gateway_interface, &zv); + ZVAL_STR(&zv, frankenphp_strings.frankenphp); + zend_hash_update_ind(ht, frankenphp_strings.server_software, &zv); ZVAL_STR(&zv, vars.request_scheme); - zend_hash_update(ht, frankenphp_interned_strings.request_scheme, &zv); + zend_hash_update_ind(ht, frankenphp_strings.request_scheme, &zv); ZVAL_STR(&zv, vars.ssl_protocol); - zend_hash_update(ht, frankenphp_interned_strings.ssl_protocol, &zv); + zend_hash_update_ind(ht, frankenphp_strings.ssl_protocol, &zv); ZVAL_STR(&zv, vars.https); - zend_hash_update(ht, frankenphp_interned_strings.https, &zv); + zend_hash_update_ind(ht, frankenphp_strings.https, &zv); /* update values with always empty strings */ ZVAL_EMPTY_STRING(&zv); - zend_hash_update(ht, frankenphp_interned_strings.auth_type, &zv); - zend_hash_update(ht, frankenphp_interned_strings.remote_ident, &zv); + zend_hash_update_ind(ht, frankenphp_strings.auth_type, &zv); + zend_hash_update_ind(ht, frankenphp_strings.remote_ident, &zv); } /** Create an immutable zend_string that lasts for the whole process **/ @@ -869,13 +869,14 @@ zend_string *frankenphp_init_persistent_string(const char *string, size_t len) { return z_string; } +/* initialize all hard-coded zend_strings once per process */ static void frankenphp_init_interned_strings(void) { - if (frankenphp_interned_strings.remote_addr != NULL) { + if (frankenphp_strings.remote_addr != NULL) { return; /* already initialized */ } #define F_INITIALIZE_FIELD(name, str) \ - frankenphp_interned_strings.name = \ + frankenphp_strings.name = \ frankenphp_init_persistent_string(str, sizeof(str) - 1); FRANKENPHP_INTERNED_STRINGS_LIST(F_INITIALIZE_FIELD) @@ -899,26 +900,26 @@ frankenphp_register_variable_from_request_info(zend_string *zKey, char *value, static void frankenphp_register_variables_from_request_info(zval *track_vars_array) { frankenphp_register_variable_from_request_info( - frankenphp_interned_strings.content_type, - (char *)SG(request_info).content_type, true, track_vars_array); + frankenphp_strings.content_type, (char *)SG(request_info).content_type, + true, track_vars_array); frankenphp_register_variable_from_request_info( - frankenphp_interned_strings.path_translated, + frankenphp_strings.path_translated, (char *)SG(request_info).path_translated, false, track_vars_array); frankenphp_register_variable_from_request_info( - frankenphp_interned_strings.query_string, SG(request_info).query_string, - true, track_vars_array); + frankenphp_strings.query_string, SG(request_info).query_string, true, + track_vars_array); frankenphp_register_variable_from_request_info( - frankenphp_interned_strings.remote_user, - (char *)SG(request_info).auth_user, false, track_vars_array); + frankenphp_strings.remote_user, (char *)SG(request_info).auth_user, false, + track_vars_array); frankenphp_register_variable_from_request_info( - frankenphp_interned_strings.request_method, + frankenphp_strings.request_method, (char *)SG(request_info).request_method, false, track_vars_array); } /* Only hard-coded keys may be registered this way */ void frankenphp_register_known_variable(zend_string *z_key, char *value, - size_t val_len, - zval *track_vars_array) { + size_t val_len, + zval *track_vars_array) { frankenphp_register_trusted_var(z_key, value, val_len, Z_ARRVAL_P(track_vars_array)); } diff --git a/frankenphp.h b/frankenphp.h index 7a7a5d6822..2b459193db 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -137,7 +137,8 @@ typedef struct frankenphp_server_vars { X(tls11, "TLSv1.1") \ X(tls12, "TLSv1.2") \ X(tls13, "TLSv1.3") \ - X(on, "on") + X(on, "on") \ + X(empty, "") typedef struct frankenphp_interned_strings_t { #define F_DEFINE_STRUCT_FIELD(name, str) zend_string *name; @@ -145,7 +146,7 @@ typedef struct frankenphp_interned_strings_t { #undef F_DEFINE_STRUCT_FIELD } frankenphp_interned_strings_t; -extern frankenphp_interned_strings_t frankenphp_interned_strings; +extern frankenphp_interned_strings_t frankenphp_strings; typedef struct frankenphp_version { unsigned char major_version; @@ -175,8 +176,7 @@ int frankenphp_execute_script_cli(char *script, int argc, char **argv, bool eval); void frankenphp_register_known_variable(zend_string *z_key, char *value, - size_t val_len, - zval *track_vars_array); + size_t val_len, zval *track_vars_array); void frankenphp_register_variable_safe(char *key, char *var, size_t val_len, zval *track_vars_array); void frankenphp_register_server_vars(zval *track_vars_array, From ce84ef119409c1ca4fab3ad726c836be604faee8 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 1 Mar 2026 16:20:44 +0100 Subject: [PATCH 27/32] Cleanup. --- frankenphp.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frankenphp.go b/frankenphp.go index 3d571b226c..49c40201a4 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -36,7 +36,7 @@ import ( "time" "unsafe" // debug on Linux - //_ "github.com/ianlancetaylor/cgosymbolizer" + _ "github.com/ianlancetaylor/cgosymbolizer" ) type contextKeyStruct struct{} @@ -500,11 +500,11 @@ func go_apache_request_headers(threadIndex C.uintptr_t) (*C.go_string, C.size_t) return sd, C.size_t(len(fc.request.Header)) } -func addHeader(ctx context.Context, fc *frankenPHPContext, cString *C.char, length C.int) { - key, val := splitRawHeader(cString, int(length)) +func addHeader(ctx context.Context, fc *frankenPHPContext, h *C.sapi_header_struct) { + key, val := splitRawHeader(h.header, int(h.header_len)) if key == "" { if fc.logger.Enabled(ctx, slog.LevelDebug) { - fc.logger.LogAttrs(ctx, slog.LevelDebug, "invalid header", slog.String("header", C.GoStringN(cString, length))) + fc.logger.LogAttrs(ctx, slog.LevelDebug, "invalid header", slog.String("header", C.GoStringN(h.header, C.int(h.header_len)))) } return @@ -564,7 +564,7 @@ func go_write_headers(threadIndex C.uintptr_t, status C.int, headers *C.zend_lli for current != nil { h := (*C.sapi_header_struct)(unsafe.Pointer(&(current.data))) - addHeader(thread.context(), fc, h.header, C.int(h.header_len)) + addHeader(thread.context(), fc, h) current = current.next } From 378a1a472cf792dc4acb734026a2ad816a439dc6 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 1 Mar 2026 16:53:18 +0100 Subject: [PATCH 28/32] Fixes import --- frankenphp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frankenphp.go b/frankenphp.go index 49c40201a4..501c7d64d9 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -36,7 +36,7 @@ import ( "time" "unsafe" // debug on Linux - _ "github.com/ianlancetaylor/cgosymbolizer" + //_ "github.com/ianlancetaylor/cgosymbolizer" ) type contextKeyStruct struct{} From 44ac7a5e3faef963d8f04869656dde8fa790b390 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sun, 1 Mar 2026 22:04:17 +0100 Subject: [PATCH 29/32] Adjusts var name. --- frankenphp.c | 2 +- frankenphp.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frankenphp.c b/frankenphp.c index 6672a8c9ab..73bfd7cf7e 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -840,7 +840,7 @@ void frankenphp_register_server_vars(zval *track_vars_array, /* update values with hard-coded zend_strings */ zval zv; - ZVAL_STR(&zv, frankenphp_strings.gateway_interface_str); + ZVAL_STR(&zv, frankenphp_strings.cgi11); zend_hash_update_ind(ht, frankenphp_strings.gateway_interface, &zv); ZVAL_STR(&zv, frankenphp_strings.frankenphp); zend_hash_update_ind(ht, frankenphp_strings.server_software, &zv); diff --git a/frankenphp.h b/frankenphp.h index 2b459193db..f25cb85128 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -125,7 +125,7 @@ typedef struct frankenphp_server_vars { X(server_software, "SERVER_SOFTWARE") \ X(frankenphp, "FrankenPHP") \ X(gateway_interface, "GATEWAY_INTERFACE") \ - X(gateway_interface_str, "CGI/1.1") \ + X(cgi11, "CGI/1.1") \ X(auth_type, "AUTH_TYPE") \ X(remote_ident, "REMOTE_IDENT") \ X(content_type, "CONTENT_TYPE") \ From 5dc941630bf9cf9a791e109cd04ca66e17dd7d50 Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Tue, 3 Mar 2026 21:36:15 +0100 Subject: [PATCH 30/32] Adjusts comments --- cgi.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cgi.go b/cgi.go index 06802d086e..98dc4f7516 100644 --- a/cgi.go +++ b/cgi.go @@ -365,14 +365,14 @@ func ensureLeadingSlash(path string) string { return "/" + path } -// Use a go string on the C side without allocations (C may not modify the string) -// Best case scenario: The string is implicitly pinned -// eg: C.call_funcion(toUnsafeChar(string)) <- go will not GC the string in this case +// toUnsafeChar returns a *C.char pointing at the backing bytes of s. +// If C does not modify or store the string it may be passed directly in a Cgo call. +// If the pointer survives the Cgo call, it must be pinned instead (inefficient). func toUnsafeChar(s string) *C.char { return (*C.char)(unsafe.Pointer(unsafe.StringData(s))) } -// initialize a global zend_string that must never be freed and is ignored by CG +// initialize a global zend_string that must never be freed and is ignored by GC func newPersistentZendString(str string) *C.zend_string { return C.frankenphp_init_persistent_string(toUnsafeChar(str), C.size_t(len(str))) } From 2ab3d54bec43be471b719f6475e6d1baae871dbc Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Tue, 3 Mar 2026 21:36:55 +0100 Subject: [PATCH 31/32] Adjusts comments. --- cgi.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cgi.go b/cgi.go index 98dc4f7516..aa76badfad 100644 --- a/cgi.go +++ b/cgi.go @@ -365,9 +365,10 @@ func ensureLeadingSlash(path string) string { return "/" + path } -// toUnsafeChar returns a *C.char pointing at the backing bytes of s. -// If C does not modify or store the string it may be passed directly in a Cgo call. -// If the pointer survives the Cgo call, it must be pinned instead (inefficient). +// toUnsafeChar returns a *C.char pointing at the backing bytes the Go string. +// If C does not store the string, it may be passed directly in a Cgo call (most efficient). +// If C stores the string, it must be pinned explicitlyinstead (inefficient). +// C may never modify the string. func toUnsafeChar(s string) *C.char { return (*C.char)(unsafe.Pointer(unsafe.StringData(s))) } From d9bbced8152675424644aced678e7c99f6f7260e Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Tue, 3 Mar 2026 21:41:58 +0100 Subject: [PATCH 32/32] fixes comments. --- cgi.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cgi.go b/cgi.go index aa76badfad..e0b1857317 100644 --- a/cgi.go +++ b/cgi.go @@ -367,7 +367,7 @@ func ensureLeadingSlash(path string) string { // toUnsafeChar returns a *C.char pointing at the backing bytes the Go string. // If C does not store the string, it may be passed directly in a Cgo call (most efficient). -// If C stores the string, it must be pinned explicitlyinstead (inefficient). +// If C stores the string, it must be pinned explicitly instead (inefficient). // C may never modify the string. func toUnsafeChar(s string) *C.char { return (*C.char)(unsafe.Pointer(unsafe.StringData(s)))