mirror of
https://github.com/dunglas/frankenphp.git
synced 2025-12-24 13:38:11 +08:00
Compare commits
12 Commits
perf/optim
...
feat/frank
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5112c10950 | ||
|
|
7324e99536 | ||
|
|
618457e2df | ||
|
|
57136919e4 | ||
|
|
d13dbe786f | ||
|
|
0aa4ac1226 | ||
|
|
1142877367 | ||
|
|
bda526382b | ||
|
|
d17baa1b0d | ||
|
|
99318cfbb6 | ||
|
|
41be5a59d8 | ||
|
|
86d67e1241 |
@@ -4,9 +4,9 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/dunglas/frankenphp/internal/fastabs"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@@ -251,6 +251,7 @@ func TestAddModuleWorkerViaAdminApi(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create a Caddyfile configuration with a module worker
|
||||
workerName := "dynamiclly added worker"
|
||||
workerConfig := `
|
||||
{
|
||||
skip_install_trust
|
||||
@@ -262,7 +263,10 @@ func TestAddModuleWorkerViaAdminApi(t *testing.T) {
|
||||
route {
|
||||
root ../testdata
|
||||
php {
|
||||
worker ../testdata/worker-with-counter.php 1
|
||||
worker ../testdata/worker-with-counter.php {
|
||||
num 1
|
||||
name "` + workerName + `"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -280,11 +284,10 @@ func TestAddModuleWorkerViaAdminApi(t *testing.T) {
|
||||
updatedDebugState := getDebugState(t, tester)
|
||||
updatedWorkerCount := 0
|
||||
workerFound := false
|
||||
filename, _ := fastabs.FastAbs("../testdata/worker-with-counter.php")
|
||||
for _, thread := range updatedDebugState.ThreadDebugStates {
|
||||
if thread.Name != "" && thread.Name != "ready" {
|
||||
updatedWorkerCount++
|
||||
if thread.Name == "Worker PHP Thread - "+filename {
|
||||
if strings.Contains(thread.Name, workerName) {
|
||||
workerFound = true
|
||||
}
|
||||
}
|
||||
@@ -292,7 +295,7 @@ func TestAddModuleWorkerViaAdminApi(t *testing.T) {
|
||||
|
||||
// Assert that the worker was added
|
||||
assert.Greater(t, updatedWorkerCount, initialWorkerCount, "Worker count should have increased")
|
||||
assert.True(t, workerFound, fmt.Sprintf("Worker with name %q should be found", "Worker PHP Thread - "+filename))
|
||||
assert.True(t, workerFound, fmt.Sprintf("Worker with name '%q' should be found", workerName))
|
||||
|
||||
// Make a request to the worker to verify it's working
|
||||
tester.AssertGetResponse("http://localhost:"+testPort+"/worker-with-counter.php", http.StatusOK, "requests:1")
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package frankenphp
|
||||
|
||||
// #include "frankenphp.h"
|
||||
import "C"
|
||||
|
||||
// EXPERIMENTAL: ThreadDebugState prints the state of a single PHP thread - debugging purposes only
|
||||
type ThreadDebugState struct {
|
||||
Index int
|
||||
@@ -44,3 +47,37 @@ func threadDebugState(thread *phpThread) ThreadDebugState {
|
||||
WaitingSinceMilliseconds: thread.state.waitTime(),
|
||||
}
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: Expose the current thread's information to PHP
|
||||
//
|
||||
//export go_frankenphp_info
|
||||
func go_frankenphp_info(threadIndex C.uintptr_t) *C.zval {
|
||||
currentThread := phpThreads[threadIndex]
|
||||
_, isWorker := currentThread.handler.(*workerThread)
|
||||
|
||||
threadInfos := make([]any, 0, len(phpThreads))
|
||||
for _, thread := range phpThreads {
|
||||
if thread.state.is(stateReserved) {
|
||||
continue
|
||||
}
|
||||
threadInfos = append(threadInfos, map[string]any{
|
||||
"index": thread.threadIndex,
|
||||
"name": thread.name(),
|
||||
"state": thread.state.name(),
|
||||
"is_waiting": thread.state.isInWaitingState(),
|
||||
"waiting_since_milliseconds": thread.state.waitTime(),
|
||||
})
|
||||
}
|
||||
|
||||
zval := (*C.zval)(PHPMap(map[string]any{
|
||||
"frankenphp_version": C.GoString(C.frankenphp_get_version().frankenphp_version),
|
||||
"current_thread_index": int64(threadIndex),
|
||||
"is_worker_thread": isWorker,
|
||||
"threads": threadInfos,
|
||||
}))
|
||||
|
||||
// TODO: how to circumvent pinning?
|
||||
currentThread.Pin(zval)
|
||||
|
||||
return zval
|
||||
}
|
||||
|
||||
17
frankenphp.c
17
frankenphp.c
@@ -44,9 +44,9 @@ static const char *MODULES_TO_RELOAD[] = {"filter", "session", NULL};
|
||||
|
||||
frankenphp_version frankenphp_get_version() {
|
||||
return (frankenphp_version){
|
||||
PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION,
|
||||
PHP_EXTRA_VERSION, PHP_VERSION, PHP_VERSION_ID,
|
||||
};
|
||||
PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION,
|
||||
PHP_EXTRA_VERSION, PHP_VERSION, PHP_VERSION_ID,
|
||||
TOSTRING(FRANKENPHP_VERSION)};
|
||||
}
|
||||
|
||||
frankenphp_config frankenphp_get_config() {
|
||||
@@ -1209,3 +1209,14 @@ void register_extensions(zend_module_entry *m, int len) {
|
||||
php_register_internal_extensions_func;
|
||||
php_register_internal_extensions_func = register_internal_extensions;
|
||||
}
|
||||
|
||||
/* EXPERIMENTAL */
|
||||
PHP_FUNCTION(frankenphp_info) {
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
zval *result = go_frankenphp_info(thread_index);
|
||||
|
||||
RETURN_ARR(Z_ARR_P(result));
|
||||
}
|
||||
@@ -36,6 +36,7 @@ typedef struct frankenphp_version {
|
||||
const char *extra_version;
|
||||
const char *version;
|
||||
unsigned long version_id;
|
||||
const char *frankenphp_version;
|
||||
} frankenphp_version;
|
||||
frankenphp_version frankenphp_get_version();
|
||||
|
||||
|
||||
@@ -32,3 +32,18 @@ function frankenphp_response_headers(): array|bool {}
|
||||
*/
|
||||
function apache_response_headers(): array|bool {}
|
||||
|
||||
/**
|
||||
* @return array{
|
||||
* "frankenphp_version": string,
|
||||
* "current_thread_index": int,
|
||||
* "is_worker_thread": bool,
|
||||
* "threads": array {
|
||||
* "index": int,
|
||||
* "name": string,
|
||||
* "state": string,
|
||||
* "is_waiting": bool,
|
||||
* "waiting_since_milliseconds": int,
|
||||
* },
|
||||
* }
|
||||
*/
|
||||
function frankenphp_info(): array {}
|
||||
|
||||
@@ -30,11 +30,16 @@ ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_apache_response_headers arginfo_frankenphp_response_headers
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_info, 0, 0, IS_ARRAY,
|
||||
0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
ZEND_FUNCTION(frankenphp_handle_request);
|
||||
ZEND_FUNCTION(headers_send);
|
||||
ZEND_FUNCTION(frankenphp_finish_request);
|
||||
ZEND_FUNCTION(frankenphp_request_headers);
|
||||
ZEND_FUNCTION(frankenphp_response_headers);
|
||||
ZEND_FUNCTION(frankenphp_info);
|
||||
|
||||
// clang-format off
|
||||
static const zend_function_entry ext_functions[] = {
|
||||
@@ -47,6 +52,7 @@ static const zend_function_entry ext_functions[] = {
|
||||
ZEND_FALIAS(getallheaders, frankenphp_request_headers, arginfo_getallheaders)
|
||||
ZEND_FE(frankenphp_response_headers, arginfo_frankenphp_response_headers)
|
||||
ZEND_FALIAS(apache_response_headers, frankenphp_response_headers, arginfo_apache_response_headers)
|
||||
ZEND_FE(frankenphp_info, arginfo_frankenphp_info)
|
||||
ZEND_FE_END
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
@@ -968,6 +968,20 @@ func TestFileStreamInWorkerMode(t *testing.T) {
|
||||
}, &testOptions{workerScript: "file-stream.php", nbParallelRequests: 1, nbWorkers: 1})
|
||||
}
|
||||
|
||||
func TestFrankenPHPInfo_module(t *testing.T) {
|
||||
runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
|
||||
body, _ := testGet("http://example.com/frankenphp_info.php", handler, t)
|
||||
assert.Contains(t, body, "[is_worker_thread] => 1")
|
||||
}, &testOptions{workerScript: "frankenphp_info.php"})
|
||||
}
|
||||
|
||||
func TestFrankenPHPInfo_worker(t *testing.T) {
|
||||
runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) {
|
||||
body, _ := testGet("http://example.com/frankenphp_info.php", handler, t)
|
||||
assert.Contains(t, body, "[is_worker_thread] => \n")
|
||||
}, &testOptions{})
|
||||
}
|
||||
|
||||
// To run this fuzzing test use: go test -fuzz FuzzRequest
|
||||
// TODO: Cover more potential cases
|
||||
func FuzzRequest(f *testing.F) {
|
||||
|
||||
7
testdata/frankenphp_info.php
vendored
Normal file
7
testdata/frankenphp_info.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__.'/_executor.php';
|
||||
|
||||
return function () {
|
||||
print_r(frankenphp_info());
|
||||
};
|
||||
@@ -70,7 +70,7 @@ func (handler *workerThread) getRequestContext() *frankenPHPContext {
|
||||
}
|
||||
|
||||
func (handler *workerThread) name() string {
|
||||
return "Worker PHP Thread - " + handler.worker.fileName
|
||||
return "Worker PHP Thread - " + handler.worker.name
|
||||
}
|
||||
|
||||
func setupWorkerScript(handler *workerThread, worker *worker) {
|
||||
|
||||
Reference in New Issue
Block a user