Skip to content
Open
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
5 changes: 5 additions & 0 deletions caddy/frankenphp/cbrotli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
//go:build !nobrotli

package main

import _ "github.com/dunglas/caddy-cbrotli"
1 change: 0 additions & 1 deletion caddy/frankenphp/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

// plug in Caddy modules here.
_ "github.com/caddyserver/caddy/v2/modules/standard"
_ "github.com/dunglas/caddy-cbrotli"
_ "github.com/dunglas/frankenphp/caddy"
_ "github.com/dunglas/mercure/caddy"
_ "github.com/dunglas/vulcain/caddy"
Expand Down
2 changes: 2 additions & 0 deletions caddy/php-cli.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build !windows

package caddy

import (
Expand Down
2 changes: 1 addition & 1 deletion cgi.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ package frankenphp
// #cgo noescape frankenphp_register_variables_from_request_info
// #cgo noescape frankenphp_register_variable_safe
// #cgo noescape frankenphp_register_single
// #include <php_variables.h>
// #include "frankenphp.h"
// #include <php_variables.h>
import "C"
import (
"context"
Expand Down
6 changes: 4 additions & 2 deletions cgo.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package frankenphp

// #cgo darwin pkg-config: libxml-2.0
// #cgo CFLAGS: -Wall -Werror
// #cgo unix CFLAGS: -Wall -Werror
// #cgo linux CFLAGS: -D_GNU_SOURCE
// #cgo LDFLAGS: -lphp -lm -lutil
// #cgo unix LDFLAGS: -lphp -lm -lutil
// #cgo linux LDFLAGS: -ldl -lresolv
// #cgo darwin LDFLAGS: -Wl,-rpath,/usr/local/lib -liconv -ldl
// #cgo windows CFLAGS: -D_WINDOWS -DWINDOWS=1 -DZEND_WIN32=1 -DPHP_WIN32=1 -DWIN32 -D_MBCS -D_USE_MATH_DEFINES -DNDebug -DNDEBUG -DZEND_DEBUG=0 -DZTS=1 -DFD_SETSIZE=256
// #cgo windows LDFLAGS: -lpthreadVC3
import "C"
35 changes: 35 additions & 0 deletions cli.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//go:build !windows

// TODO: ignored on Windows for now (even if it should work with a custom PHP build),
// because static builds of the embed SAPI aren't available yet and php.exe is ship with
// the standard PHP distribution.

package frankenphp

// #include "frankenphp.h"
import "C"
import "unsafe"

// ExecuteScriptCLI executes the PHP script passed as parameter.
// It returns the exit status code of the script.
func ExecuteScriptCLI(script string, args []string) int {
// Ensure extensions are registered before CLI execution
registerExtensions()

cScript := C.CString(script)
defer C.free(unsafe.Pointer(cScript))

argc, argv := convertArgs(args)
defer freeArgs(argv)

return int(C.frankenphp_execute_script_cli(cScript, argc, (**C.char)(unsafe.Pointer(&argv[0])), false))
}

func ExecutePHPCode(phpCode string) int {
// Ensure extensions are registered before CLI execution
registerExtensions()

cCode := C.CString(phpCode)
defer C.free(unsafe.Pointer(cCode))
return int(C.frankenphp_execute_script_cli(cCode, 0, nil, true))
}
57 changes: 57 additions & 0 deletions cli_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//go:build !windows

package frankenphp_test

import (
"errors"
"log"
"os"
"os/exec"
"testing"

"github.com/dunglas/frankenphp"
"github.com/stretchr/testify/assert"
)

func TestExecuteScriptCLI(t *testing.T) {
if _, err := os.Stat("internal/testcli/testcli"); err != nil {
t.Skip("internal/testcli/testcli has not been compiled, run `cd internal/testcli/ && go build`")
}

cmd := exec.Command("internal/testcli/testcli", "testdata/command.php", "foo", "bar")
stdoutStderr, err := cmd.CombinedOutput()
assert.Error(t, err)

var exitError *exec.ExitError
if errors.As(err, &exitError) {
assert.Equal(t, 3, exitError.ExitCode())
}

stdoutStderrStr := string(stdoutStderr)

assert.Contains(t, stdoutStderrStr, `"foo"`)
assert.Contains(t, stdoutStderrStr, `"bar"`)
assert.Contains(t, stdoutStderrStr, "From the CLI")
}

func TestExecuteCLICode(t *testing.T) {
if _, err := os.Stat("internal/testcli/testcli"); err != nil {
t.Skip("internal/testcli/testcli has not been compiled, run `cd internal/testcli/ && go build`")
}

cmd := exec.Command("internal/testcli/testcli", "-r", "echo 'Hello World';")
stdoutStderr, err := cmd.CombinedOutput()
assert.NoError(t, err)

stdoutStderrStr := string(stdoutStderr)
assert.Equal(t, stdoutStderrStr, `Hello World`)
}

func ExampleExecuteScriptCLI() {
if len(os.Args) <= 1 {
log.Println("Usage: my-program script.php")
os.Exit(1)
}

os.Exit(frankenphp.ExecuteScriptCLI(os.Args[1], os.Args))
}
2 changes: 1 addition & 1 deletion ext.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package frankenphp

//#include "frankenphp.h"
// #include "frankenphp.h"
import "C"
import (
"sync"
Expand Down
16 changes: 13 additions & 3 deletions frankenphp.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
#include "frankenphp.h"
#include <SAPI.h>
#include <Zend/zend_alloc.h>
#include <Zend/zend_exceptions.h>
#include <Zend/zend_interfaces.h>
#include <Zend/zend_types.h>
#include <errno.h>
#include <ext/spl/spl_exceptions.h>
#include <ext/standard/head.h>
#include <inttypes.h>
#include <php.h>
#ifdef PHP_WIN32
#include <config.w32.h>
#else
#include <php_config.h>
#endif
#include <php_ini.h>
#include <php_main.h>
#include <php_output.h>
Expand All @@ -19,7 +23,9 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#ifndef ZEND_WIN32
#include <unistd.h>
#endif
#if defined(__linux__)
#include <sys/prctl.h>
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
Expand Down Expand Up @@ -205,7 +211,7 @@ bool frankenphp_shutdown_dummy_request(void) {
return true;
}

PHPAPI void get_full_env(zval *track_vars_array) {
void get_full_env(zval *track_vars_array) {
go_getfullenv(thread_index, track_vars_array);
}

Expand Down Expand Up @@ -959,6 +965,7 @@ static void *php_thread(void *arg) {
}

static void *php_main(void *arg) {
#ifndef ZEND_WIN32
/*
* SIGPIPE must be masked in non-Go threads:
* https://pkg.go.dev/os/signal#hdr-Go_programs_that_use_cgo_or_SWIG
Expand All @@ -971,6 +978,7 @@ static void *php_main(void *arg) {
perror("failed to block SIGPIPE");
exit(EXIT_FAILURE);
}
#endif

set_thread_name("php-main");

Expand Down Expand Up @@ -1188,6 +1196,7 @@ static void sapi_cli_register_variables(zval *track_vars_array) /* {{{ */
}
/* }}} */

#ifndef ZEND_WIN32
static void *execute_script_cli(void *arg) {
void *exit_status;
bool eval = (bool)arg;
Expand Down Expand Up @@ -1249,6 +1258,7 @@ int frankenphp_execute_script_cli(char *script, int argc, char **argv,

return (intptr_t)exit_status;
}
#endif

int frankenphp_reset_opcache(void) {
zend_function *opcache_reset =
Expand All @@ -1266,7 +1276,7 @@ static zend_module_entry **modules = NULL;
static int modules_len = 0;
static int (*original_php_register_internal_extensions_func)(void) = NULL;

PHPAPI int register_internal_extensions(void) {
int register_internal_extensions(void) {
if (original_php_register_internal_extensions_func != NULL &&
original_php_register_internal_extensions_func() != SUCCESS) {
return FAILURE;
Expand Down
26 changes: 1 addition & 25 deletions frankenphp.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ package frankenphp

// #include <stdlib.h>
// #include <stdint.h>
// #include "frankenphp.h"
// #include <php_variables.h>
// #include <zend_llist.h>
// #include <SAPI.h>
// #include "frankenphp.h"
import "C"
import (
"bytes"
Expand Down Expand Up @@ -753,30 +753,6 @@ func go_is_context_done(threadIndex C.uintptr_t) C.bool {
return C.bool(phpThreads[threadIndex].frankenPHPContext().isDone)
}

// ExecuteScriptCLI executes the PHP script passed as parameter.
// It returns the exit status code of the script.
func ExecuteScriptCLI(script string, args []string) int {
// Ensure extensions are registered before CLI execution
registerExtensions()

cScript := C.CString(script)
defer C.free(unsafe.Pointer(cScript))

argc, argv := convertArgs(args)
defer freeArgs(argv)

return int(C.frankenphp_execute_script_cli(cScript, argc, (**C.char)(unsafe.Pointer(&argv[0])), false))
}

func ExecutePHPCode(phpCode string) int {
// Ensure extensions are registered before CLI execution
registerExtensions()

cCode := C.CString(phpCode)
defer C.free(unsafe.Pointer(cCode))
return int(C.frankenphp_execute_script_cli(cCode, 0, nil, true))
}

func convertArgs(args []string) (C.int, []*C.char) {
argc := C.int(len(args))
argv := make([]*C.char, argc)
Expand Down
42 changes: 42 additions & 0 deletions frankenphp.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,46 @@
#ifndef _FRANKENPHP_H
#define _FRANKENPHP_H

#ifdef _WIN32
// Define this to prevent windows.h from including legacy winsock.h
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

// Explicitly include Winsock2 BEFORE windows.h
#include <windows.h>
#include <winerror.h>
#include <winsock2.h>
#include <ws2tcpip.h>

// Fix for missing IntSafe functions (LongLongAdd) when building with Clang
#ifdef __clang__
#ifndef INTSAFE_E_ARITHMETIC_OVERFLOW
#define INTSAFE_E_ARITHMETIC_OVERFLOW ((HRESULT)0x80070216L)
#endif

#ifndef LongLongAdd
static inline HRESULT LongLongAdd(LONGLONG llAugend, LONGLONG llAddend,
LONGLONG *pllResult) {
if (__builtin_add_overflow(llAugend, llAddend, pllResult)) {
return INTSAFE_E_ARITHMETIC_OVERFLOW;
}
return S_OK;
}
#endif

#ifndef LongLongSub
static inline HRESULT LongLongSub(LONGLONG llMinuend, LONGLONG llSubtrahend,
LONGLONG *pllResult) {
if (__builtin_sub_overflow(llMinuend, llSubtrahend, pllResult)) {
return INTSAFE_E_ARITHMETIC_OVERFLOW;
}
return S_OK;
}
#endif
#endif
#endif

#include <Zend/zend_modules.h>
#include <Zend/zend_types.h>
#include <stdbool.h>
Expand Down Expand Up @@ -47,8 +87,10 @@ bool frankenphp_shutdown_dummy_request(void);
int frankenphp_execute_script(char *file_name);
void frankenphp_update_local_thread_context(bool is_worker);

#ifndef ZEND_WIN32
int frankenphp_execute_script_cli(char *script, int argc, char **argv,
bool eval);
#endif

void frankenphp_register_variables_from_request_info(
zval *track_vars_array, zend_string *content_type,
Expand Down
43 changes: 0 additions & 43 deletions frankenphp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -733,40 +733,6 @@ func testFileUpload(t *testing.T, opts *testOptions) {
}, opts)
}

func TestExecuteScriptCLI(t *testing.T) {
if _, err := os.Stat("internal/testcli/testcli"); err != nil {
t.Skip("internal/testcli/testcli has not been compiled, run `cd internal/testcli/ && go build`")
}

cmd := exec.Command("internal/testcli/testcli", "testdata/command.php", "foo", "bar")
stdoutStderr, err := cmd.CombinedOutput()
assert.Error(t, err)

var exitError *exec.ExitError
if errors.As(err, &exitError) {
assert.Equal(t, 3, exitError.ExitCode())
}

stdoutStderrStr := string(stdoutStderr)

assert.Contains(t, stdoutStderrStr, `"foo"`)
assert.Contains(t, stdoutStderrStr, `"bar"`)
assert.Contains(t, stdoutStderrStr, "From the CLI")
}

func TestExecuteCLICode(t *testing.T) {
if _, err := os.Stat("internal/testcli/testcli"); err != nil {
t.Skip("internal/testcli/testcli has not been compiled, run `cd internal/testcli/ && go build`")
}

cmd := exec.Command("internal/testcli/testcli", "-r", "echo 'Hello World';")
stdoutStderr, err := cmd.CombinedOutput()
assert.NoError(t, err)

stdoutStderrStr := string(stdoutStderr)
assert.Equal(t, stdoutStderrStr, `Hello World`)
}

func ExampleServeHTTP() {
if err := frankenphp.Init(); err != nil {
panic(err)
Expand All @@ -786,15 +752,6 @@ func ExampleServeHTTP() {
log.Fatal(http.ListenAndServe(":8080", nil))
}

func ExampleExecuteScriptCLI() {
if len(os.Args) <= 1 {
log.Println("Usage: my-program script.php")
os.Exit(1)
}

os.Exit(frankenphp.ExecuteScriptCLI(os.Args[1], os.Args))
}

func BenchmarkHelloWorld(b *testing.B) {
require.NoError(b, frankenphp.Init())
b.Cleanup(frankenphp.Shutdown)
Expand Down
2 changes: 2 additions & 0 deletions internal/cpu/cpu_unix.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build unix

package cpu

// #include <time.h>
Expand Down
2 changes: 1 addition & 1 deletion internal/cpu/cpu_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
)

// ProbeCPUs fallback that always determines that the CPU limits are not reached
func ProbeCPUs(probeTime time.Duration, maxCPUUsage float64, abort chan struct{}) bool {
func ProbeCPUs(probeTime time.Duration, _ float64, abort chan struct{}) bool {
select {
case <-abort:
return false
Expand Down
Loading
Loading