Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions frankenphp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2124,20 +2124,23 @@ int register_internal_extensions(void) {
return FAILURE;
}

/* modules is kept for the process lifetime: every php_module_startup()
* (main thread reboots, a new Init()) registers the modules again */
for (int i = 0; i < modules_len; i++) {
if (zend_register_internal_module(modules[i]) == NULL) {
return FAILURE;
}
}

pefree(modules, 1);
modules = NULL;
modules_len = 0;

return SUCCESS;
}

void register_extensions(zend_module_entry **m, int len) {
/* install the hook only once, or it would become its own predecessor */
if (modules != NULL || len <= 0) {
return;
}

modules = pemalloc(len * sizeof(*modules), 1);
memcpy(modules, m, len * sizeof(*modules));
modules_len = len;
Expand Down
51 changes: 44 additions & 7 deletions internal/testext/extensions.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,13 +86,50 @@ zend_module_entry module1_entry = {STANDARD_MODULE_HEADER,
"0.1.0",
STANDARD_MODULE_PROPERTIES};

/* counts MINIT calls for the whole process: each PHP startup, including the
* main thread reboots, must register the module again */
static zend_long ext2_minit_calls = 0;

ZEND_BEGIN_MODULE_GLOBALS(ext2)
zend_long answer;
ZEND_END_MODULE_GLOBALS(ext2)

ZEND_DECLARE_MODULE_GLOBALS(ext2)

#define EXT2_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(ext2, v)

static PHP_GINIT_FUNCTION(ext2) { ext2_globals->answer = 42; }

static PHP_MINIT_FUNCTION(ext2) {
REGISTER_LONG_CONSTANT("EXT2_MINIT_CALLS", ++ext2_minit_calls,
CONST_PERSISTENT);

return SUCCESS;
}

static PHP_FUNCTION(ext2_answer) {
ZEND_PARSE_PARAMETERS_NONE();

RETURN_LONG(EXT2_G(answer));
}

ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ext2_answer, 0, 0, IS_LONG, 0)
ZEND_END_ARG_INFO()

static const zend_function_entry ext2_functions[] = {
ZEND_FE(ext2_answer, arginfo_ext2_answer) ZEND_FE_END};

zend_module_entry module2_entry = {STANDARD_MODULE_HEADER,
"ext2",
NULL, /* Functions */
NULL, /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
ext2_functions, /* Functions */
PHP_MINIT(ext2), /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
"0.1.0",
STANDARD_MODULE_PROPERTIES};
PHP_MODULE_GLOBALS(ext2),
PHP_GINIT(ext2),
NULL, /* GSHUTDOWN */
NULL, /* post deactivate */
STANDARD_MODULE_PROPERTIES_EX};
35 changes: 25 additions & 10 deletions internal/testext/exttest.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package testext
// #include "extension.h"
import "C"
import (
"fmt"
"io"
"net/http/httptest"
"runtime"
Expand Down Expand Up @@ -58,17 +59,31 @@ func testRegisterExtension(t *testing.T) {
_ = *p
})

req := httptest.NewRequest("GET", "http://example.com/index.php", nil)
w := httptest.NewRecorder()
assertExtensionsLoaded(t, 1)

// rebooting the main thread (file watcher, admin restart endpoint,
// opcache_reset()) starts the PHP modules again
frankenphp.RestartWorkers()
assertExtensionsLoaded(t, 2)

// so does a new Init(), which Caddy runs on every config reload
frankenphp.Shutdown()
require.NoError(t, frankenphp.Init())
assertExtensionsLoaded(t, 3)
}

// assertExtensionsLoaded checks that both test modules are registered and
// that ext2's MINIT ran minitCalls times since the process started
func assertExtensionsLoaded(t *testing.T, minitCalls int) {
t.Helper()

req, err = frankenphp.NewRequestWithContext(req, frankenphp.WithRequestDocumentRoot("./testdata", false))
assert.NoError(t, err)
req := httptest.NewRequest("GET", "http://example.com/index.php", nil)
req, err := frankenphp.NewRequestWithContext(req, frankenphp.WithRequestDocumentRoot("./testdata", false))
require.NoError(t, err)

err = frankenphp.ServeHTTP(w, req)
assert.NoError(t, err)
w := httptest.NewRecorder()
require.NoError(t, frankenphp.ServeHTTP(w, req))

resp := w.Result()
body, _ := io.ReadAll(resp.Body)
assert.Contains(t, string(body), "ext1")
assert.Contains(t, string(body), "ext2")
body, _ := io.ReadAll(w.Result().Body)
assert.Equal(t, fmt.Sprintf("ext1: loaded\next2: loaded\next2_answer: 42\nEXT2_MINIT_CALLS: %d\n", minitCalls), string(body))
}
6 changes: 5 additions & 1 deletion internal/testext/testdata/index.php
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
<?php

print_r(get_loaded_extensions());
$extensions = get_loaded_extensions();
printf("ext1: %s\n", in_array('ext1', $extensions, true) ? 'loaded' : 'missing');
printf("ext2: %s\n", in_array('ext2', $extensions, true) ? 'loaded' : 'missing');
printf("ext2_answer: %s\n", function_exists('ext2_answer') ? ext2_answer() : 'missing');
printf("EXT2_MINIT_CALLS: %s\n", defined('EXT2_MINIT_CALLS') ? EXT2_MINIT_CALLS : 'missing');
Loading