From f3bf7db3aa168a90087d8fce0989c85e0f7d021e Mon Sep 17 00:00:00 2001 From: Alliballibaba Date: Sat, 10 Jan 2026 19:53:47 +0100 Subject: [PATCH 01/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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/16] 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