Skip to content

Commit fd81412

Browse files
fix: keep custom extensions registered after worker reload
1 parent b72328c commit fd81412

6 files changed

Lines changed: 90 additions & 17 deletions

File tree

ext.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,26 +3,43 @@ package frankenphp
33
// #include "frankenphp.h"
44
import "C"
55
import (
6+
"errors"
67
"sync"
78
"unsafe"
89
)
910

1011
var (
11-
extensions []*C.zend_module_entry
12-
registerOnce sync.Once
12+
extensions []*C.zend_module_entry
13+
extensionsMu sync.Mutex
14+
extensionsRegistered bool
15+
registerOnce sync.Once
16+
17+
// ErrExtensionRegistrationStarted is panicked with when an extension is registered after PHP startup.
18+
ErrExtensionRegistrationStarted = errors.New("frankenphp: RegisterExtension called after PHP extension registration started")
1319
)
1420

1521
// RegisterExtension registers a new PHP extension.
1622
func RegisterExtension(me unsafe.Pointer) {
23+
extensionsMu.Lock()
24+
defer extensionsMu.Unlock()
25+
26+
if extensionsRegistered {
27+
panic(ErrExtensionRegistrationStarted)
28+
}
29+
1730
extensions = append(extensions, (*C.zend_module_entry)(me))
1831
}
1932

2033
func registerExtensions() {
21-
if len(extensions) == 0 {
22-
return
23-
}
24-
2534
registerOnce.Do(func() {
35+
extensionsMu.Lock()
36+
defer extensionsMu.Unlock()
37+
38+
extensionsRegistered = true
39+
if len(extensions) == 0 {
40+
return
41+
}
42+
2643
C.register_extensions((**C.zend_module_entry)(unsafe.Pointer(&extensions[0])), C.int(len(extensions)))
2744
extensions = nil
2845
})

frankenphp.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <stdint.h>
2727
#include <stdio.h>
2828
#include <stdlib.h>
29+
#include <string.h>
2930
#ifndef PHP_WIN32
3031
#include <unistd.h>
3132
#endif
@@ -1957,14 +1958,24 @@ int register_internal_extensions(void) {
19571958
}
19581959
}
19591960

1960-
modules = NULL;
1961-
modules_len = 0;
1962-
19631961
return SUCCESS;
19641962
}
19651963

19661964
void register_extensions(zend_module_entry **m, int len) {
1967-
modules = m;
1965+
if (len <= 0 || original_php_register_internal_extensions_func != NULL) {
1966+
return;
1967+
}
1968+
1969+
zend_module_entry **persistent_modules =
1970+
malloc(sizeof(zend_module_entry *) * len);
1971+
1972+
if (persistent_modules == NULL) {
1973+
return;
1974+
}
1975+
1976+
memcpy(persistent_modules, m, sizeof(zend_module_entry *) * len);
1977+
1978+
modules = persistent_modules;
19681979
modules_len = len;
19691980

19701981
original_php_register_internal_extensions_func =

internal/testext/extensions.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,28 @@
44

55
#include "_cgo_export.h"
66

7+
ZEND_BEGIN_MODULE_GLOBALS(ext2)
8+
zend_long minit_count;
9+
ZEND_END_MODULE_GLOBALS(ext2)
10+
11+
ZEND_DECLARE_MODULE_GLOBALS(ext2)
12+
13+
#ifdef ZTS
14+
#define EXT2_G(v) ZEND_TSRMG(ext2_globals_id, zend_ext2_globals *, v)
15+
#else
16+
#define EXT2_G(v) (ext2_globals.v)
17+
#endif
18+
19+
static PHP_GINIT_FUNCTION(ext2) { ext2_globals->minit_count = 0; }
20+
21+
PHP_MINIT_FUNCTION(ext2) {
22+
EXT2_G(minit_count)++;
23+
REGISTER_LONG_CONSTANT("EXT2_MINIT_COUNT", EXT2_G(minit_count),
24+
CONST_CS | CONST_PERSISTENT);
25+
26+
return SUCCESS;
27+
}
28+
729
zend_module_entry module1_entry = {STANDARD_MODULE_HEADER,
830
"ext1",
931
NULL, /* Functions */
@@ -18,10 +40,14 @@ zend_module_entry module1_entry = {STANDARD_MODULE_HEADER,
1840
zend_module_entry module2_entry = {STANDARD_MODULE_HEADER,
1941
"ext2",
2042
NULL, /* Functions */
21-
NULL, /* MINIT */
43+
PHP_MINIT(ext2), /* MINIT */
2244
NULL, /* MSHUTDOWN */
2345
NULL, /* RINIT */
2446
NULL, /* RSHUTDOWN */
2547
NULL, /* MINFO */
2648
"0.1.0",
27-
STANDARD_MODULE_PROPERTIES};
49+
PHP_MODULE_GLOBALS(ext2),
50+
PHP_GINIT(ext2),
51+
NULL,
52+
NULL,
53+
STANDARD_MODULE_PROPERTIES_EX};

internal/testext/exttest.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,28 @@ func testRegisterExtension(t *testing.T) {
2828

2929
err := frankenphp.Init()
3030
require.Nil(t, err)
31-
defer frankenphp.Shutdown()
31+
32+
assertRegisteredExtensions(t)
33+
34+
require.True(t, frankenphp.RestartWorkers())
35+
36+
assertRegisteredExtensions(t)
37+
38+
frankenphp.Shutdown()
39+
40+
require.PanicsWithValue(t, frankenphp.ErrExtensionRegistrationStarted, func() {
41+
frankenphp.RegisterExtension(unsafe.Pointer(&C.module1_entry))
42+
})
43+
}
44+
45+
func assertRegisteredExtensions(t *testing.T) {
46+
t.Helper()
3247

3348
req := httptest.NewRequest("GET", "http://example.com/index.php", nil)
3449
w := httptest.NewRecorder()
3550

36-
req, err = frankenphp.NewRequestWithContext(req, frankenphp.WithRequestDocumentRoot("./testdata", false))
37-
assert.NoError(t, err)
51+
req, err := frankenphp.NewRequestWithContext(req, frankenphp.WithRequestDocumentRoot("./testdata", false))
52+
require.NoError(t, err)
3853

3954
err = frankenphp.ServeHTTP(w, req)
4055
assert.NoError(t, err)
@@ -43,4 +58,5 @@ func testRegisterExtension(t *testing.T) {
4358
body, _ := io.ReadAll(resp.Body)
4459
assert.Contains(t, string(body), "ext1")
4560
assert.Contains(t, string(body), "ext2")
61+
assert.Contains(t, string(body), "EXT2_MINIT_COUNT=1")
4662
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
<?php
22

33
print_r(get_loaded_extensions());
4+
printf("\nEXT2_MINIT_COUNT=%d\n", EXT2_MINIT_COUNT);

worker.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,12 @@ func newWorker(o workerOpt) (*worker, error) {
183183
// force-kill is armed after a grace period to wake threads parked in
184184
// blocking syscalls so a stuck sleep doesn't make this hang for the
185185
// full duration of the syscall.
186-
func RestartWorkers() {
186+
func RestartWorkers() bool {
187187
if mainThread != nil {
188-
mainThread.rebootAllThreads()
188+
return mainThread.rebootAllThreads()
189189
}
190+
191+
return false
190192
}
191193

192194
func (worker *worker) attachThread(thread *phpThread) {

0 commit comments

Comments
 (0)