diff --git a/frankenphp.c b/frankenphp.c index 5b7625f807..7c6dbee7a0 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -968,7 +968,8 @@ static void *php_main(void *arg) { #endif sapi_startup(&frankenphp_sapi_module); - + frankenphp_sapi_module.php_ini_ignore = go_get_php_ini_ignore(); + frankenphp_sapi_module.php_ini_ignore_cwd = go_get_php_ini_ignore_cwd(); #ifndef ZEND_MAX_EXECUTION_TIMERS #if (PHP_VERSION_ID >= 80300) frankenphp_sapi_module.ini_entries = HARDCODED_INI; diff --git a/frankenphp.go b/frankenphp.go index 3367351ee9..d0e57e68f5 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -333,7 +333,7 @@ func Init(options ...Option) error { } requestChan = make(chan *http.Request, opt.numThreads) - if err := initPHPThreads(totalThreadCount); err != nil { + if err := initPHPThreads(totalThreadCount, opt.phpIniIgnore, opt.phpIniIgnoreCwd); err != nil { return err } diff --git a/frankenphp_test.go b/frankenphp_test.go index 5d39e1d364..e664ccc77d 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -45,6 +45,7 @@ type testOptions struct { realServer bool logger *zap.Logger initOpts []frankenphp.Option + chdirToTest bool } func runTest(t *testing.T, test func(func(http.ResponseWriter, *http.Request), *httptest.Server, int), opts *testOptions) { @@ -58,6 +59,14 @@ func runTest(t *testing.T, test func(func(http.ResponseWriter, *http.Request), * cwd, _ := os.Getwd() testDataDir := cwd + "/testdata/" + if opts.chdirToTest { + errChdir := os.Chdir(testDataDir) + require.Nil(t, errChdir) + defer func() { + require.Nil(t, os.Chdir(cwd)) + }() + } + if opts.logger == nil { opts.logger = zaptest.NewLogger(t) } @@ -978,6 +987,46 @@ func TestFileStreamInWorkerMode(t *testing.T) { }, &testOptions{workerScript: "file-stream.php", nbParallelRequests: 1, nbWorkers: 1}) } +func TestSearchIniDefault(t *testing.T) { + runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) { + req := httptest.NewRequest("GET", "http://example.com/php_ini_loaded_file.php", nil) + w := httptest.NewRecorder() + handler(w, req) + + resp := w.Result() + body, _ := io.ReadAll(resp.Body) + + assert.Equal(t, "cwd", string(body)) + }, &testOptions{chdirToTest: true}) +} + +func TestSearchIniIPHPIniIgnoreCwd(t *testing.T) { + runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) { + req := httptest.NewRequest("GET", "http://example.com/php_ini_loaded_file.php", nil) + w := httptest.NewRecorder() + handler(w, req) + + resp := w.Result() + body, _ := io.ReadAll(resp.Body) + bodyString := string(body) + // none - if not set global php.ini + assert.True(t, bodyString == "none" || bodyString == "global") + }, &testOptions{chdirToTest: true, initOpts: []frankenphp.Option{frankenphp.WithPHPIniIgnoreCwd(true)}}) +} + +func TestSearchIniPHPIniIgnore(t *testing.T) { + runTest(t, func(handler func(http.ResponseWriter, *http.Request), _ *httptest.Server, i int) { + req := httptest.NewRequest("GET", "http://example.com/php_ini_loaded_file.php", nil) + w := httptest.NewRecorder() + handler(w, req) + + resp := w.Result() + body, _ := io.ReadAll(resp.Body) + + assert.Equal(t, "none", string(body)) + }, &testOptions{chdirToTest: true, initOpts: []frankenphp.Option{frankenphp.WithPHPIniIgnore(true)}}) +} + // To run this fuzzing test use: go test -fuzz FuzzRequest // TODO: Cover more potential cases func FuzzRequest(f *testing.F) { diff --git a/options.go b/options.go index 724e75c8b3..f28fab9189 100644 --- a/options.go +++ b/options.go @@ -15,6 +15,9 @@ type opt struct { workers []workerOpt logger *zap.Logger metrics Metrics + // sapi options + phpIniIgnore bool + phpIniIgnoreCwd bool } type workerOpt struct { @@ -58,3 +61,21 @@ func WithLogger(l *zap.Logger) Option { return nil } } + +// WithPHPIniIgnore don't look for php.ini +func WithPHPIniIgnore(ignore bool) Option { + return func(o *opt) error { + o.phpIniIgnore = ignore + + return nil + } +} + +// WithPHPIniIgnoreCwd don't look for php.ini in the current directory +func WithPHPIniIgnoreCwd(ignoreCwd bool) Option { + return func(o *opt) error { + o.phpIniIgnoreCwd = ignoreCwd + + return nil + } +} diff --git a/phpmainthread.go b/phpmainthread.go index ddc4778eb0..fd730ef633 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -15,6 +15,8 @@ type phpMainThread struct { state *threadState done chan struct{} numThreads int + phpIniIgnore bool + phpIniIgnoreCwd bool commonHeaders map[string]*C.zend_string knownServerKeys map[string]*C.zend_string } @@ -25,11 +27,13 @@ var ( ) // reserve a fixed number of PHP threads on the Go side -func initPHPThreads(numThreads int) error { +func initPHPThreads(numThreads int, phpIniIgnore bool, phpIniIgnoreCwd bool) error { mainThread = &phpMainThread{ - state: newThreadState(), - done: make(chan struct{}), - numThreads: numThreads, + state: newThreadState(), + done: make(chan struct{}), + numThreads: numThreads, + phpIniIgnore: phpIniIgnore, + phpIniIgnoreCwd: phpIniIgnoreCwd, } phpThreads = make([]*phpThread, numThreads) @@ -124,3 +128,21 @@ func go_frankenphp_main_thread_is_ready() { func go_frankenphp_shutdown_main_thread() { mainThread.state.set(stateDone) } + +//export go_get_php_ini_ignore +func go_get_php_ini_ignore() C.int { + if mainThread.phpIniIgnore { + return C.int(1) + } else { + return C.int(0) + } +} + +//export go_get_php_ini_ignore_cwd +func go_get_php_ini_ignore_cwd() C.int { + if mainThread.phpIniIgnoreCwd { + return C.int(1) + } else { + return C.int(0) + } +} diff --git a/phpmainthread_test.go b/phpmainthread_test.go index a5de26f932..93102a1109 100644 --- a/phpmainthread_test.go +++ b/phpmainthread_test.go @@ -18,8 +18,8 @@ import ( var testDataPath, _ = filepath.Abs("./testdata") func TestStartAndStopTheMainThreadWithOneInactiveThread(t *testing.T) { - logger = zap.NewNop() // the logger needs to not be nil - assert.NoError(t, initPHPThreads(1)) // reserve 1 thread + logger = zap.NewNop() // the logger needs to not be nil + assert.NoError(t, initPHPThreads(1, false, false)) // reserve 1 thread assert.Len(t, phpThreads, 1) assert.Equal(t, 0, phpThreads[0].threadIndex) @@ -31,7 +31,7 @@ func TestStartAndStopTheMainThreadWithOneInactiveThread(t *testing.T) { func TestTransitionRegularThreadToWorkerThread(t *testing.T) { logger = zap.NewNop() - assert.NoError(t, initPHPThreads(1)) + assert.NoError(t, initPHPThreads(1, false, false)) // transition to regular thread convertToRegularThread(phpThreads[0]) @@ -54,7 +54,7 @@ func TestTransitionRegularThreadToWorkerThread(t *testing.T) { func TestTransitionAThreadBetween2DifferentWorkers(t *testing.T) { logger = zap.NewNop() - assert.NoError(t, initPHPThreads(1)) + assert.NoError(t, initPHPThreads(1, false, false)) firstWorker := getDummyWorker("transition-worker-1.php") secondWorker := getDummyWorker("transition-worker-2.php") diff --git a/testdata/php.ini b/testdata/php.ini new file mode 100644 index 0000000000..e69de29bb2 diff --git a/testdata/php_ini_loaded_file.php b/testdata/php_ini_loaded_file.php new file mode 100644 index 0000000000..2cd2398c8e --- /dev/null +++ b/testdata/php_ini_loaded_file.php @@ -0,0 +1,14 @@ +