diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 73d212c6c7..05b9ba2288 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -208,7 +208,7 @@ jobs: VARIANT: ${{ matrix.variant }} - name: Upload builder metadata if: fromJson(needs.prepare.outputs.push) - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: metadata-builder-${{ matrix.variant }}-${{ steps.prepare.outputs.sanitized_platform }} path: /tmp/metadata/builder/* @@ -216,7 +216,7 @@ jobs: retention-days: 1 - name: Upload runner metadata if: fromJson(needs.prepare.outputs.push) - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: metadata-runner-${{ matrix.variant }}-${{ steps.prepare.outputs.sanitized_platform }} path: /tmp/metadata/runner/* diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml index cb97a302be..2b7a93766a 100644 --- a/.github/workflows/static.yaml +++ b/.github/workflows/static.yaml @@ -170,7 +170,7 @@ jobs: METADATA: ${{ steps.build.outputs.metadata }} - name: Upload metadata if: fromJson(needs.prepare.outputs.push) && !matrix.debug && !matrix.mimalloc - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: metadata-static-builder-musl-${{ steps.prepare.outputs.sanitized_platform }} path: /tmp/metadata/* @@ -188,7 +188,7 @@ jobs: PLATFORM: ${{ matrix.platform }} - name: Upload artifact if: ${{ !fromJson(needs.prepare.outputs.push) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} path: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}${{ matrix.debug && '-debug' || '' }}${{ matrix.mimalloc && '-mimalloc' || '' }} @@ -320,7 +320,7 @@ jobs: METADATA: ${{ steps.build.outputs.metadata }} - name: Upload metadata if: fromJson(needs.prepare.outputs.push) - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: metadata-static-builder-gnu-${{ steps.prepare.outputs.sanitized_platform }} path: /tmp/metadata-gnu/* @@ -344,7 +344,7 @@ jobs: PLATFORM: ${{ matrix.platform }} - name: Upload artifact if: ${{ !fromJson(needs.prepare.outputs.push) }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: frankenphp-linux-${{ matrix.platform == 'linux/amd64' && 'x86_64' || 'aarch64' }}-gnu-files path: gh-output/* @@ -448,12 +448,12 @@ jobs: ref: ${{ needs.prepare.outputs.ref }} persist-credentials: false - uses: actions/setup-go@v6 - with: + with: # zizmor: ignore[cache-poisoning] go-version: "1.25" cache-dependency-path: | go.sum caddy/go.sum - cache: false + cache: ${{ github.event_name != 'release' }} - name: Set FRANKENPHP_VERSION run: | if [ "${GITHUB_REF_TYPE}" == "tag" ]; then @@ -475,7 +475,7 @@ jobs: NO_COMPRESS: ${{ github.event_name == 'pull_request' && '1' || '' }} - name: Upload logs if: ${{ failure() }} - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: path: dist/static-php-cli/log name: static-php-cli-log-${{ matrix.platform }}-${{ github.sha }} @@ -485,7 +485,7 @@ jobs: subject-path: ${{ github.workspace }}/dist/frankenphp-mac-* - name: Upload artifact if: github.ref_type == 'branch' - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v6 with: name: frankenphp-mac-${{ matrix.platform }} path: dist/frankenphp-mac-${{ matrix.platform }} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 1d48784abc..3d1e344d4b 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -35,6 +35,7 @@ jobs: env: GOMAXPROCS: 10 LIBRARY_PATH: ${{ github.workspace }}/watcher/target/lib + GOFLAGS: "-tags=nobadger,nomysql,nopgx" steps: - uses: actions/checkout@v6 with: @@ -69,7 +70,7 @@ jobs: run: ./frankenphp.test -test.v - name: Run Caddy module tests working-directory: caddy/ - run: go test -tags nobadger,nomysql,nopgx -race -v ./... + run: go test -race -v ./... - name: Run Fuzzing Tests working-directory: caddy/ run: go test -fuzz FuzzRequest -fuzztime 20s @@ -100,6 +101,8 @@ jobs: fail-fast: false matrix: php-versions: ["8.3", "8.4", "8.5"] + env: + XCADDY_GO_BUILD_FLAGS: "-tags=nobadger,nomysql,nopgx" steps: - uses: actions/checkout@v6 with: @@ -141,6 +144,7 @@ jobs: runs-on: macos-latest env: HOMEBREW_NO_AUTO_UPDATE: 1 + GOFLAGS: "-tags=nowatcher,nobadger,nomysql,nopgx" steps: - uses: actions/checkout@v6 with: @@ -172,4 +176,4 @@ jobs: run: go test -tags nowatcher -race -v ./... - name: Run Caddy module tests working-directory: caddy/ - run: go test -tags nowatcher,nobadger,nomysql,nopgx -race -v ./... + run: go test -race -v ./... diff --git a/.github/workflows/windows.yaml b/.github/workflows/windows.yaml new file mode 100644 index 0000000000..9fdf94bacb --- /dev/null +++ b/.github/workflows/windows.yaml @@ -0,0 +1,185 @@ +name: Build Windows release + +concurrency: + cancel-in-progress: true + group: ${{ github.workflow }}-${{ github.ref }} + +on: + pull_request: + branches: + - main + paths: + - "docker-bake.hcl" + - "vcpkg.json" + - ".github/workflows/static.yaml" + - "**cgo.go" + - "**Dockerfile" + - "**.c" + - "**.h" + - "**.sh" + - "**.stub.php" + push: + branches: + - main + tags: + - v*.*.* + workflow_dispatch: + inputs: + #checkov:skip=CKV_GHA_7 + version: + description: "FrankenPHP version" + required: false + type: string + schedule: + - cron: "0 8 * * *" + +permissions: + contents: read + +env: + GOTOOLCHAIN: local + GOFLAGS: "-ldflags=-extldflags=-fuse-ld=lld -tags=nobadger,nomysql,nopgx" + PHP_DOWNLOAD_BASE: "https://windows.php.net/downloads/releases" + CC: clang + CXX: clang++ + +jobs: + build: + runs-on: windows-latest + defaults: + run: + shell: powershell + + steps: + - name: Checkout Code + uses: actions/checkout@v6 + with: + path: frankenphp + persist-credentials: false + + - name: Set FRANKENPHP_VERSION + run: | + if ($env:GITHUB_REF_TYPE -eq "tag") { + $frankenphpVersion = $env:GITHUB_REF_NAME.Substring(1) + } elseif ($env:GITHUB_EVENT_NAME -eq "schedule") { + $frankenphpVersion = $env:GITHUB_REF + } else { + $frankenphpVersion = $env:GITHUB_SHA + } + + echo "FRANKENPHP_VERSION=$frankenphpVersion" | Out-File -FilePath $env:GITHUB_ENV -Append + + - name: Setup Go + uses: actions/setup-go@v6 + with: # zizmor: ignore[cache-poisoning] + go-version: "1.26.0-rc.1" + cache-dependency-path: | + frankenphp/go.sum + frankenphp/caddy/go.sum + cache: ${{ github.event_name != 'release' }} + check-latest: true + + - name: Install Vcpkg Libraries + working-directory: frankenphp + run: "vcpkg install" + + - name: Download Watcher + run: | + $latestTag = gh release list --repo e-dant/watcher --limit 1 --exclude-drafts --exclude-pre-releases --json tagName --jq '.[0].tagName' + Write-Host "Latest Watcher version: $latestTag" + + gh release download $latestTag --repo e-dant/watcher --pattern "*x86_64-pc-windows-msvc.tar" -O watcher.tar + + tar -xf "watcher.tar" -C "$env:GITHUB_WORKSPACE" + Rename-Item -Path "$env:GITHUB_WORKSPACE\x86_64-pc-windows-msvc" -NewName "watcher" + + # See https://github.com/e-dant/watcher/issues/108 + New-Item -Path .\watcher\wtr -ItemType Directory -Force + Move-Item -Path .\watcher\watcher-c.h -Destination .\watcher\wtr\watcher-c.h + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Download PHP + run: | + $webContent = Invoke-WebRequest -Uri $env:PHP_DOWNLOAD_BASE -UseBasicParsing + $links = $webContent.Links.Href | Where-Object { $_ -match "php-\d+\.\d+\.\d+-Win32-vs17-x64\.zip$" } + + if (-not $links) { throw "Could not find PHP zip files at $env:PHP_DOWNLOAD_BASE" } + + $latestFile = $links | Sort-Object { if ($_ -match '(\d+\.\d+\.\d+)') { [version]$matches[1] } } | Select-Object -Last 1 + + $version = if ($latestFile -match '(\d+\.\d+\.\d+)') { $matches[1] } + Write-Host "Detected latest PHP version: $version" + + "PHP_VERSION=$version" | Out-File -FilePath $env:GITHUB_ENV -Append + + $phpZip = "php-$version-Win32-vs17-x64.zip" + $develZip = "php-devel-pack-$version-Win32-vs17-x64.zip" + + $dirName = "frankenphp-$env:FRANKENPHP_VERSION-php-$version-Win32-vs17-x64" + + echo "DIR_NAME=$dirName" | Out-File -FilePath $env:GITHUB_ENV -Append + + Invoke-WebRequest -Uri "$env:PHP_DOWNLOAD_BASE/$phpZip" -OutFile "$env:TEMP\php.zip" + Expand-Archive -Path "$env:TEMP\php.zip" -DestinationPath "$env:GITHUB_WORKSPACE\$dirName" + + Invoke-WebRequest -Uri "$env:PHP_DOWNLOAD_BASE/$develZip" -OutFile "$env:TEMP\php-devel.zip" + Expand-Archive -Path "$env:TEMP\php-devel.zip" -DestinationPath "$env:GITHUB_WORKSPACE\php-devel" + + - name: Prepare env + run: | + $vcpkgRoot = "$env:GITHUB_WORKSPACE\frankenphp\vcpkg_installed\x64-windows" + $watcherRoot = "$env:GITHUB_WORKSPACE\watcher" + $phpBin = "$env:GITHUB_WORKSPACE\$env:DIR_NAME" + $phpDevel = "$env:GITHUB_WORKSPACE\php-devel\php-$env:PHP_VERSION-devel-vs17-x64" + + echo "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Tools\Llvm\bin" | Out-File -FilePath $env:GITHUB_PATH -Append + echo "$vcpkgRoot\bin" | Out-File -FilePath $env:GITHUB_PATH -Append + echo "$watcherRoot" | Out-File -FilePath $env:GITHUB_PATH -Append + echo "$phpBin" | Out-File -FilePath $env:GITHUB_PATH -Append + + echo "CGO_CFLAGS=-I$vcpkgRoot\include -I$watcherRoot -I$phpDevel\include -I$phpDevel\include\main -I$phpDevel\include\TSRM -I$phpDevel\include\Zend -I$phpDevel\include\ext" | Out-File -FilePath $env:GITHUB_ENV -Append + echo "CGO_LDFLAGS=-L$vcpkgRoot\lib -lbrotlienc -L$watcherRoot -llibwatcher-c -L$phpBin -L$phpDevel\lib -lphp8ts -lphp8embed" | Out-File -FilePath $env:GITHUB_ENV -Append + + - name: Build FrankenPHP + run: | + $customVersion = "FrankenPHP $env:FRANKENPHP_VERSION PHP $env:PHP_VERSION Caddy" + go build "-X 'github.com/caddyserver/caddy/v2.CustomVersion=$customVersion'" + working-directory: frankenphp\caddy\frankenphp + + - name: Create Zip Archive + run: | + Copy-Item frankenphp\caddy\frankenphp\frankenphp.exe $env:DIR_NAME + Copy-Item watcher\libwatcher-c.dll $env:DIR_NAME + Copy-Item frankenphp\vcpkg_installed\x64-windows\bin\brotlienc.dll $env:DIR_NAME + Copy-Item frankenphp\vcpkg_installed\x64-windows\bin\brotlidec.dll $env:DIR_NAME + Copy-Item frankenphp\vcpkg_installed\x64-windows\bin\brotlicommon.dll $env:DIR_NAME + Copy-Item frankenphp\vcpkg_installed\x64-windows\bin\pthreadVC3.dll $env:DIR_NAME + + Compress-Archive -Path $env:DIR_NAME -DestinationPath "$env:DIR_NAME.zip" + + - name: Upload Artifact + if: github.event_name != 'release' + uses: actions/upload-artifact@v6 + with: + name: ${{ env.DIR_NAME }}.zip + path: ${{ env.DIR_NAME }}.zip + compression-level: 0 + if-no-files-found: error + + - name: Upload Release Asset + if: github.event_name == 'release' + run: gh release upload $env:GITHUB_EVENT_RELEASE_TAG_NAME "$env:DIR_NAME.zip" --clobber + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} + + - name: Run Tests + run: | + "opcache.enable=0`r`nopcache.enable_cli=0" | Out-File php.ini + $env:PHPRC = Get-Location + + go test ./... + #cd caddy + #go test ./... + working-directory: ${{ github.workspace }}\frankenphp diff --git a/caddy/frankenphp/cbrotli.go b/caddy/frankenphp/cbrotli.go new file mode 100644 index 0000000000..ee84e60373 --- /dev/null +++ b/caddy/frankenphp/cbrotli.go @@ -0,0 +1,5 @@ +//go:build !nobrotli + +package main + +import _ "github.com/dunglas/caddy-cbrotli" diff --git a/caddy/frankenphp/main.go b/caddy/frankenphp/main.go index 5b5fc0d7c9..6b9d40561f 100644 --- a/caddy/frankenphp/main.go +++ b/caddy/frankenphp/main.go @@ -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" diff --git a/caddy/go.mod b/caddy/go.mod index ae29d7565c..60fffe9c18 100644 --- a/caddy/go.mod +++ b/caddy/go.mod @@ -62,7 +62,7 @@ require ( github.com/dunglas/skipfilter v1.0.0 // indirect github.com/dunglas/vulcain v1.2.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/e-dant/watcher/watcher-go v0.0.0-20251208164151-f88ec3b7e146 // indirect + github.com/e-dant/watcher v0.0.0-20260110212511-0b8aea576c24 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect diff --git a/caddy/go.sum b/caddy/go.sum index f190ab0fde..490909fafb 100644 --- a/caddy/go.sum +++ b/caddy/go.sum @@ -158,8 +158,8 @@ github.com/dunglas/vulcain/caddy v1.2.1/go.mod h1:8QrmLTfURmW2VgjTR6Gb9a53FrZjsp github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/e-dant/watcher/watcher-go v0.0.0-20251208164151-f88ec3b7e146 h1:h3vVM6X45PK0mAk8NqiYNQGXTyhvXy1HQ5GhuQN4eeA= -github.com/e-dant/watcher/watcher-go v0.0.0-20251208164151-f88ec3b7e146/go.mod h1:sVUOkwtftoj71nnJRG2S0oWNfXFdKpz/M9vK0z06nmM= +github.com/e-dant/watcher v0.0.0-20260110212511-0b8aea576c24 h1:azdbL1jat1ReH6wQrP+cawhsyXRLFRuCo0hGBUQHnv4= +github.com/e-dant/watcher v0.0.0-20260110212511-0b8aea576c24/go.mod h1:PmV4IVmBJVqT2NcfTGN4+sZ+qGe3PA0qkphAtOHeFG0= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= diff --git a/cgi.go b/cgi.go index 63fb1339b9..44e54786ff 100644 --- a/cgi.go +++ b/cgi.go @@ -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 // #include "frankenphp.h" +// #include import "C" import ( "context" diff --git a/cgo.go b/cgo.go index 2ec9586308..8dc2609007 100644 --- a/cgo.go +++ b/cgo.go @@ -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" diff --git a/cli.go b/cli.go new file mode 100644 index 0000000000..96821a2392 --- /dev/null +++ b/cli.go @@ -0,0 +1,29 @@ +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)) +} diff --git a/cli_test.go b/cli_test.go new file mode 100644 index 0000000000..f9ee03fea2 --- /dev/null +++ b/cli_test.go @@ -0,0 +1,55 @@ +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)) +} diff --git a/ext.go b/ext.go index 1c0656c820..b993bf83df 100644 --- a/ext.go +++ b/ext.go @@ -1,6 +1,6 @@ package frankenphp -//#include "frankenphp.h" +// #include "frankenphp.h" import "C" import ( "sync" diff --git a/frankenphp.c b/frankenphp.c index fd487edb8e..d465299509 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -1,14 +1,18 @@ +#include "frankenphp.h" #include #include #include #include -#include #include #include #include #include #include +#ifdef PHP_WIN32 +#include +#else #include +#endif #include #include #include @@ -19,7 +23,9 @@ #include #include #include +#ifndef ZEND_WIN32 #include +#endif #if defined(__linux__) #include #elif defined(__FreeBSD__) || defined(__OpenBSD__) @@ -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); } @@ -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 @@ -971,6 +978,7 @@ static void *php_main(void *arg) { perror("failed to block SIGPIPE"); exit(EXIT_FAILURE); } +#endif set_thread_name("php-main"); @@ -1266,7 +1274,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; diff --git a/frankenphp.go b/frankenphp.go index 693870e1d0..b20d6f6681 100644 --- a/frankenphp.go +++ b/frankenphp.go @@ -14,10 +14,10 @@ package frankenphp // #include // #include +// #include "frankenphp.h" // #include // #include // #include -// #include "frankenphp.h" import "C" import ( "bytes" @@ -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) diff --git a/frankenphp.h b/frankenphp.h index c833c44f97..51833cf6fc 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -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 +#include +#include +#include + +// 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 #include #include diff --git a/frankenphp_test.go b/frankenphp_test.go index 8c6f3c90da..fdebbfcdf7 100644 --- a/frankenphp_test.go +++ b/frankenphp_test.go @@ -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) @@ -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) diff --git a/go.mod b/go.mod index 56bb84b591..5140b7e43b 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ retract v1.0.0-rc.1 // Human error require ( github.com/Masterminds/sprig/v3 v3.3.0 github.com/dunglas/mercure v0.21.4 - github.com/e-dant/watcher/watcher-go v0.0.0-20251208164151-f88ec3b7e146 + github.com/e-dant/watcher v0.0.0-20260110212511-0b8aea576c24 github.com/maypok86/otter/v2 v2.2.1 github.com/prometheus/client_golang v1.23.2 github.com/stretchr/testify v1.11.1 diff --git a/go.sum b/go.sum index af58b44cb3..83dd4e2a31 100644 --- a/go.sum +++ b/go.sum @@ -22,8 +22,8 @@ github.com/dunglas/mercure v0.21.4 h1:mXPXHfB+4cYfFFCRRDY198mfef5+MQcdCpUnAHBUW2 github.com/dunglas/mercure v0.21.4/go.mod h1:l/dglCjp/OQx8/quRyceRPx2hqZQ3CNviwoLMRQiJ/k= github.com/dunglas/skipfilter v1.0.0 h1:JG9SgGg4n6BlFwuTYzb9RIqjH7PfwszvWehanrYWPF4= github.com/dunglas/skipfilter v1.0.0/go.mod h1:ryhr8j7CAHSjzeN7wI6YEuwoArQ3OQmRqWWVCEAfb9w= -github.com/e-dant/watcher/watcher-go v0.0.0-20251208164151-f88ec3b7e146 h1:h3vVM6X45PK0mAk8NqiYNQGXTyhvXy1HQ5GhuQN4eeA= -github.com/e-dant/watcher/watcher-go v0.0.0-20251208164151-f88ec3b7e146/go.mod h1:sVUOkwtftoj71nnJRG2S0oWNfXFdKpz/M9vK0z06nmM= +github.com/e-dant/watcher v0.0.0-20260110212511-0b8aea576c24 h1:azdbL1jat1ReH6wQrP+cawhsyXRLFRuCo0hGBUQHnv4= +github.com/e-dant/watcher v0.0.0-20260110212511-0b8aea576c24/go.mod h1:PmV4IVmBJVqT2NcfTGN4+sZ+qGe3PA0qkphAtOHeFG0= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= diff --git a/internal/cpu/cpu_unix.go b/internal/cpu/cpu_unix.go index 4d18215226..b33f9e3bf8 100644 --- a/internal/cpu/cpu_unix.go +++ b/internal/cpu/cpu_unix.go @@ -1,3 +1,5 @@ +//go:build unix + package cpu // #include diff --git a/internal/cpu/cpu_windows.go b/internal/cpu/cpu_windows.go index d09d552410..fae6689f08 100644 --- a/internal/cpu/cpu_windows.go +++ b/internal/cpu/cpu_windows.go @@ -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 diff --git a/internal/extgen/stub_test.go b/internal/extgen/stub_test.go index b9d689ffbc..67b4203eac 100644 --- a/internal/extgen/stub_test.go +++ b/internal/extgen/stub_test.go @@ -536,7 +536,12 @@ func TestStubGenerator_FileStructure(t *testing.T) { content, err := stubGen.buildContent() assert.NoError(t, err, "buildContent() failed") - lines := strings.Split(content, "\n") + sep := "\n" + if filepath.Separator == '\\' { + sep = "\r\n" + } + + lines := strings.Split(content, sep) assert.GreaterOrEqual(t, len(lines), 3, "Stub file should have multiple lines") assert.Equal(t, " extern zend_module_entry module1_entry; diff --git a/internal/testext/extensions.c b/internal/testext/extensions.c index 721955f621..749d00b56d 100644 --- a/internal/testext/extensions.c +++ b/internal/testext/extensions.c @@ -1,3 +1,4 @@ +#include "extension.h" #include #include diff --git a/internal/testext/exttest.go b/internal/testext/exttest.go index 1a8477d4a8..bbbe9bbd22 100644 --- a/internal/testext/exttest.go +++ b/internal/testext/exttest.go @@ -1,13 +1,14 @@ package testext // #cgo darwin pkg-config: libxml-2.0 -// #cgo CFLAGS: -Wall -Werror -// #cgo CFLAGS: -I/usr/local/include -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib +// #cgo unix CFLAGS: -Wall -Werror +// #cgo unix CFLAGS: -I/usr/local/include -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib // #cgo linux CFLAGS: -D_GNU_SOURCE // #cgo darwin CFLAGS: -I/opt/homebrew/include -// #cgo LDFLAGS: -L/usr/local/lib -L/usr/lib -lphp -lm -lutil +// #cgo unix LDFLAGS: -L/usr/local/lib -L/usr/lib -lphp -lm -lutil // #cgo linux LDFLAGS: -ldl -lresolv // #cgo darwin LDFLAGS: -Wl,-rpath,/usr/local/lib -L/opt/homebrew/lib -L/opt/homebrew/opt/libiconv/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 // #include "extension.h" import "C" import ( diff --git a/internal/watcher/pattern.go b/internal/watcher/pattern.go index 5e6fda282d..be045f747d 100644 --- a/internal/watcher/pattern.go +++ b/internal/watcher/pattern.go @@ -11,6 +11,8 @@ import ( "github.com/e-dant/watcher/watcher-go" ) +const sep = string(filepath.Separator) + type pattern struct { patternGroup *PatternGroup value string @@ -39,8 +41,11 @@ func (p *pattern) parse() (err error) { p.value = absPattern + volumeName := filepath.VolumeName(p.value) + p.value = strings.TrimPrefix(p.value, volumeName) + // then we split the pattern to determine where the directory ends and the pattern starts - splitPattern := strings.Split(absPattern, string(filepath.Separator)) + splitPattern := strings.Split(p.value, sep) patternWithoutDir := "" for i, part := range splitPattern { isFilename := i == len(splitPattern)-1 && strings.Contains(part, ".") @@ -57,11 +62,15 @@ func (p *pattern) parse() (err error) { // now we split the pattern according to the recursive '**' syntax p.parsedValues = strings.Split(patternWithoutDir, "**") for i, pp := range p.parsedValues { - p.parsedValues[i] = strings.Trim(pp, string(filepath.Separator)) + p.parsedValues[i] = strings.Trim(pp, sep) } - // remove the trailing separator and add leading separator - p.value = string(filepath.Separator) + strings.Trim(p.value, string(filepath.Separator)) + // remove the trailing separator and add leading separator (except on Windows) + if volumeName == "" { + p.value = sep + strings.Trim(p.value, sep) + } else { + p.value = volumeName + sep + strings.Trim(p.value, sep) + } // try to canonicalize the path canonicalPattern, err := filepath.EvalSymlinks(p.value) @@ -123,7 +132,7 @@ func (p *pattern) isValidPattern(fileName string) bool { } // remove the directory path and separator from the filename - fileNameWithoutDir := strings.TrimPrefix(strings.TrimPrefix(fileName, p.value), string(filepath.Separator)) + fileNameWithoutDir := strings.TrimPrefix(strings.TrimPrefix(fileName, p.value), sep) // if the pattern has size 1 we can match it directly against the filename if len(p.parsedValues) == 1 { @@ -134,12 +143,12 @@ func (p *pattern) isValidPattern(fileName string) bool { } func (p *pattern) matchPatterns(fileName string) bool { - partsToMatch := strings.Split(fileName, string(filepath.Separator)) + partsToMatch := strings.Split(fileName, sep) cursor := 0 // if there are multiple parsedValues due to '**' we need to match them individually for i, pattern := range p.parsedValues { - patternSize := strings.Count(pattern, string(filepath.Separator)) + 1 + patternSize := strings.Count(pattern, sep) + 1 // if we are at the last pattern we will start matching from the end of the filename if i == len(p.parsedValues)-1 { @@ -157,7 +166,7 @@ func (p *pattern) matchPatterns(fileName string) bool { } cursor = j - subPattern := strings.Join(partsToMatch[j:j+patternSize], string(filepath.Separator)) + subPattern := strings.Join(partsToMatch[j:j+patternSize], sep) if matchCurlyBracePattern(pattern, subPattern) { cursor = j + patternSize - 1 diff --git a/internal/watcher/pattern_test.go b/internal/watcher/pattern_test.go index 25b4dd58da..8ee3907c7e 100644 --- a/internal/watcher/pattern_test.go +++ b/internal/watcher/pattern_test.go @@ -4,6 +4,7 @@ package watcher import ( "path/filepath" + "strings" "testing" "github.com/e-dant/watcher/watcher-go" @@ -11,16 +12,42 @@ import ( "github.com/stretchr/testify/require" ) +func normalizePath(t *testing.T, path string) string { + t.Helper() + + if filepath.Separator == '/' { + return path + } + + path = filepath.FromSlash(path) + if strings.HasPrefix(path, "\\") { + path = "C:\\" + path[1:] + } + + return path +} + +func newPattern(t *testing.T, value string) pattern { + t.Helper() + + p := pattern{value: normalizePath(t, value)} + require.NoError(t, p.parse()) + + return p +} + func TestDisallowOnEventTypeBiggerThan3(t *testing.T) { - w := pattern{value: "/some/path"} - require.NoError(t, w.parse()) + t.Parallel() + + w := newPattern(t, "/some/path") assert.False(t, w.allowReload(&watcher.Event{PathName: "/some/path/watch-me.php", EffectType: watcher.EffectTypeOwner})) } func TestDisallowOnPathTypeBiggerThan2(t *testing.T) { - w := pattern{value: "/some/path"} - require.NoError(t, w.parse()) + t.Parallel() + + w := newPattern(t, "/some/path") assert.False(t, w.allowReload(&watcher.Event{PathName: "/some/path/watch-me.php", PathType: watcher.PathTypeSymLink})) } @@ -59,7 +86,7 @@ func TestValidRecursiveDirectories(t *testing.T) { data := []struct { pattern string - dir string + file string }{ {"/path", "/path/file.php"}, {"/path", "/path/subpath/file.php"}, @@ -77,7 +104,7 @@ func TestValidRecursiveDirectories(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldMatch(t, d.pattern, d.dir) + assertPatternMatch(t, d.pattern, d.file) }) } } @@ -98,7 +125,7 @@ func TestInvalidRecursiveDirectories(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldNotMatch(t, d.pattern, d.dir) + assertPatternNotMatch(t, d.pattern, d.dir) }) } } @@ -122,7 +149,7 @@ func TestValidNonRecursiveFilePatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldMatch(t, d.pattern, d.dir) + assertPatternMatch(t, d.pattern, d.dir) }) } } @@ -145,7 +172,7 @@ func TestInValidNonRecursiveFilePatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldNotMatch(t, d.pattern, d.dir) + assertPatternNotMatch(t, d.pattern, d.dir) }) } } @@ -170,7 +197,7 @@ func TestValidRecursiveFilePatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldMatch(t, d.pattern, d.dir) + assertPatternMatch(t, d.pattern, d.dir) }) } } @@ -198,7 +225,7 @@ func TestInvalidRecursiveFilePatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldNotMatch(t, d.pattern, d.dir) + assertPatternNotMatch(t, d.pattern, d.dir) }) } } @@ -225,13 +252,14 @@ func TestValidDirectoryPatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldMatch(t, d.pattern, d.dir) + assertPatternMatch(t, d.pattern, d.dir) }) } } func TestInvalidDirectoryPatterns(t *testing.T) { t.Parallel() + data := []struct { pattern string dir string @@ -254,15 +282,17 @@ func TestInvalidDirectoryPatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldNotMatch(t, d.pattern, d.dir) + assertPatternNotMatch(t, d.pattern, d.dir) }) } } func TestValidCurlyBracePatterns(t *testing.T) { + t.Parallel() + data := []struct { pattern string - dir string + file string }{ {"/path/*.{php}", "/path/file.php"}, {"/path/*.{php,twig}", "/path/file.php"}, @@ -282,12 +312,14 @@ func TestValidCurlyBracePatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldMatch(t, d.pattern, d.dir) + assertPatternMatch(t, d.pattern, d.file) }) } } func TestInvalidCurlyBracePatterns(t *testing.T) { + t.Parallel() + data := []struct { pattern string dir string @@ -306,52 +338,52 @@ func TestInvalidCurlyBracePatterns(t *testing.T) { t.Run(d.pattern, func(t *testing.T) { t.Parallel() - shouldNotMatch(t, d.pattern, d.dir) + assertPatternNotMatch(t, d.pattern, d.dir) }) } - } func TestAnAssociatedEventTriggersTheWatcher(t *testing.T) { - w := pattern{value: "/**/*.php"} - require.NoError(t, w.parse()) + t.Parallel() + + w := newPattern(t, "/**/*.php") w.events = make(chan eventHolder) - e := &watcher.Event{PathName: "/path/temporary_file", AssociatedPathName: "/path/file.php"} + e := &watcher.Event{PathName: normalizePath(t, "/path/temporary_file"), AssociatedPathName: normalizePath(t, "/path/file.php")} go w.handle(e) assert.Equal(t, e, (<-w.events).event) } func relativeDir(t *testing.T, relativePath string) string { + t.Helper() + dir, err := filepath.Abs("./" + relativePath) assert.NoError(t, err) + return dir } func hasDir(t *testing.T, p string, dir string) { t.Helper() - w := pattern{value: p} - require.NoError(t, w.parse()) + w := newPattern(t, p) - assert.Equal(t, dir, w.value) + assert.Equal(t, normalizePath(t, dir), w.value) } -func shouldMatch(t *testing.T, p string, fileName string) { +func assertPatternMatch(t *testing.T, p, fileName string) { t.Helper() - w := pattern{value: p} - require.NoError(t, w.parse()) + w := newPattern(t, p) - assert.True(t, w.allowReload(&watcher.Event{PathName: fileName})) + assert.True(t, w.allowReload(&watcher.Event{PathName: normalizePath(t, fileName)})) } -func shouldNotMatch(t *testing.T, p string, fileName string) { +func assertPatternNotMatch(t *testing.T, p, fileName string) { t.Helper() - w := pattern{value: p} - require.NoError(t, w.parse()) + w := newPattern(t, p) - assert.False(t, w.allowReload(&watcher.Event{PathName: fileName})) + assert.False(t, w.allowReload(&watcher.Event{PathName: normalizePath(t, fileName)})) } diff --git a/mercure.go b/mercure.go index 9dc27e2290..d7cf33609e 100644 --- a/mercure.go +++ b/mercure.go @@ -3,6 +3,7 @@ package frankenphp // #include +// #include "frankenphp.h" // #include import "C" import ( diff --git a/phpmainthread.go b/phpmainthread.go index cecadc1653..1d7383cc57 100644 --- a/phpmainthread.go +++ b/phpmainthread.go @@ -4,8 +4,8 @@ package frankenphp // #cgo nocallback frankenphp_init_persistent_string // #cgo noescape frankenphp_new_main_thread // #cgo noescape frankenphp_init_persistent_string -// #include // #include "frankenphp.h" +// #include import "C" import ( "log/slog" diff --git a/phpmainthread_test.go b/phpmainthread_test.go index 7e6bf32c1e..3282f46364 100644 --- a/phpmainthread_test.go +++ b/phpmainthread_test.go @@ -96,14 +96,14 @@ func TestTransitionThreadsWhileDoingRequests(t *testing.T) { var ( isDone atomic.Bool - wg sync.WaitGroup + wg sync.WaitGroup ) numThreads := 10 numRequestsPerThread := 100 - worker1Path := testDataPath + "/transition-worker-1.php" + worker1Path := filepath.Join(testDataPath, "transition-worker-1.php") worker1Name := "worker-1" - worker2Path := testDataPath + "/transition-worker-2.php" + worker2Path := filepath.Join(testDataPath, "transition-worker-2.php") worker2Name := "worker-2" assert.NoError(t, Init( diff --git a/scaling.go b/scaling.go index 37e081abb9..51acd1cd37 100644 --- a/scaling.go +++ b/scaling.go @@ -1,8 +1,5 @@ package frankenphp -//#include "frankenphp.h" -//#include -import "C" import ( "errors" "log/slog" diff --git a/scaling_test.go b/scaling_test.go index f7ecc05e05..c79ae6e044 100644 --- a/scaling_test.go +++ b/scaling_test.go @@ -1,6 +1,7 @@ package frankenphp import ( + "path/filepath" "testing" "time" @@ -33,7 +34,7 @@ func TestScaleAWorkerThreadUpAndDown(t *testing.T) { t.Cleanup(Shutdown) workerName := "worker1" - workerPath := testDataPath + "/transition-worker-1.php" + workerPath := filepath.Join(testDataPath, "transition-worker-1.php") assert.NoError(t, Init( WithNumThreads(2), WithMaxThreads(3), diff --git a/testdata/session.php b/testdata/session.php index dacc631151..9598359c6a 100644 --- a/testdata/session.php +++ b/testdata/session.php @@ -11,5 +11,5 @@ $_SESSION['count'] = 0; } - echo 'Count: '.$_SESSION['count'].PHP_EOL; + echo 'Count: '.$_SESSION['count']."\n"; }; diff --git a/types.h b/types.h index 552ddfe7fa..619603ab97 100644 --- a/types.h +++ b/types.h @@ -1,11 +1,11 @@ #ifndef TYPES_H #define TYPES_H +#include "frankenphp.h" #include #include #include #include -#include zval *get_ht_packed_data(HashTable *, uint32_t index); Bucket *get_ht_bucket_data(HashTable *, uint32_t index); diff --git a/vcpkg.json b/vcpkg.json new file mode 100644 index 0000000000..79fc2096b6 --- /dev/null +++ b/vcpkg.json @@ -0,0 +1,3 @@ +{ + "dependencies": ["brotli", "pthreads"] +} diff --git a/watcher_test.go b/watcher_test.go index 3e0d9d108a..6ccca16aec 100644 --- a/watcher_test.go +++ b/watcher_test.go @@ -50,7 +50,7 @@ func pollForWorkerReset(t *testing.T, handler func(http.ResponseWriter, *http.Re // now we spam file updates and check if the request counter resets for range limit { - updateTestFile("./testdata/files/test.txt", "updated", t) + updateTestFile(t, filepath.Join(".", "testdata", "files", "test.txt"), "updated") time.Sleep(pollingTime * time.Millisecond) body, _ := testGet("http://example.com/worker-with-counter.php", handler, t) if body == "requests:1" { @@ -61,15 +61,10 @@ func pollForWorkerReset(t *testing.T, handler func(http.ResponseWriter, *http.Re return false } -func updateTestFile(fileName string, content string, t *testing.T) { +func updateTestFile(t *testing.T, fileName, content string) { absFileName, err := filepath.Abs(fileName) require.NoError(t, err) - dirName := filepath.Dir(absFileName) - if _, err = os.Stat(dirName); os.IsNotExist(err) { - err = os.MkdirAll(dirName, 0700) - } - require.NoError(t, err) - + require.NoError(t, os.MkdirAll(filepath.Dir(absFileName), 0700)) require.NoError(t, os.WriteFile(absFileName, []byte(content), 0644)) }