From 99aca3ae3e9f040594ad6176563920ef9b756dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 14:37:37 +0100 Subject: [PATCH 1/8] ci: add Super-Linter --- .github/workflows/lint.yml | 33 +++++++++++++++++++++++++++++++++ .hadolint.yaml | 5 +++++ Dockerfile | 16 +++++++++------- alpine.Dockerfile | 16 ++++++++++------ build-static.sh | 2 +- dev-alpine.Dockerfile | 15 ++++++++------- dev.Dockerfile | 16 +++++++++------- static-builder.Dockerfile | 10 +++++----- 8 files changed, 80 insertions(+), 33 deletions(-) create mode 100644 .github/workflows/lint.yml create mode 100644 .hadolint.yaml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 000000000..e2cf568ca --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,33 @@ +name: Lint Code Base +on: + pull_request: + branches: + - main + push: + branches: + - main + +jobs: + build: + name: Lint Code Base + runs-on: ubuntu-latest + + permissions: + contents: read + packages: read + statuses: write + + steps: + - + name: Checkout Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - + name: Lint Code Base + uses: super-linter/super-linter@v5 + env: + VALIDATE_ALL_CODEBASE: true + DEFAULT_BRANCH: main + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + LINTER_RULES_PATH: / diff --git a/.hadolint.yaml b/.hadolint.yaml new file mode 100644 index 000000000..bdba91c5a --- /dev/null +++ b/.hadolint.yaml @@ -0,0 +1,5 @@ +ignored: + - DL3006 + - DL3008 + - DL3018 + - DL3022 diff --git a/Dockerfile b/Dockerfile index 67ecfec65..c931df72b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -46,6 +46,7 @@ LABEL org.opencontainers.image.vendor="Kévin Dunglas" FROM common AS builder ARG FRANKENPHP_VERSION='dev' +SHELL ["/bin/bash", "-o", "pipefail", "-c"] COPY --from=golang-base /usr/local/go /usr/local/go @@ -71,12 +72,11 @@ WORKDIR /go/src/app COPY --link go.mod go.sum ./ RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get -RUN mkdir caddy && cd caddy -COPY --link caddy/go.mod caddy/go.sum ./caddy/ - -RUN cd caddy && \ - go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get +WORKDIR /go/src/app/caddy +COPY --link caddy/go.mod caddy/go.sum ./ +RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get +WORKDIR /go/src/app COPY --link *.* ./ COPY --link caddy caddy COPY --link C-Thread-Pool C-Thread-Pool @@ -87,12 +87,14 @@ COPY --link testdata testdata # see https://github.com/docker-library/php/blob/master/8.2/bookworm/zts/Dockerfile#L57-L59 for PHP values ENV CGO_LDFLAGS="-lssl -lcrypto -lreadline -largon2 -lcurl -lonig -lz $PHP_LDFLAGS" CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS" CGO_CPPFLAGS=$PHP_CPPFLAGS -RUN cd caddy/frankenphp && \ - GOBIN=/usr/local/bin go install -ldflags "-X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \ +WORKDIR /go/src/app/caddy/frankenphp +RUN GOBIN=/usr/local/bin go install -ldflags "-X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \ setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \ cp Caddyfile /etc/caddy/Caddyfile && \ frankenphp version +WORKDIR /go/src/app + FROM common AS runner diff --git a/alpine.Dockerfile b/alpine.Dockerfile index 91cb9a14e..9c77a2c04 100644 --- a/alpine.Dockerfile +++ b/alpine.Dockerfile @@ -43,11 +43,13 @@ LABEL org.opencontainers.image.vendor="Kévin Dunglas" FROM common AS builder ARG FRANKENPHP_VERSION='dev' +SHELL ["/bin/ash", "-eo", "pipefail", "-c"] COPY --link --from=golang-base /usr/local/go /usr/local/go ENV PATH /usr/local/go/bin:$PATH +# hadolint ignore=SC2086 RUN apk add --no-cache --virtual .build-deps \ $PHPIZE_DEPS \ argon2-dev \ @@ -67,11 +69,11 @@ WORKDIR /go/src/app COPY --link go.mod go.sum ./ RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get -RUN mkdir caddy && cd caddy -COPY caddy/go.mod caddy/go.sum ./caddy/ - -RUN cd caddy && go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get +WORKDIR /go/src/app/caddy +COPY caddy/go.mod caddy/go.sum ./ +RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get +WORKDIR /go/src/app COPY --link *.* ./ COPY --link caddy caddy COPY --link C-Thread-Pool C-Thread-Pool @@ -82,11 +84,13 @@ COPY --link testdata testdata # see https://github.com/docker-library/php/blob/master/8.2/bookworm/zts/Dockerfile#L57-L59 for php values ENV CGO_LDFLAGS="-lssl -lcrypto -lreadline -largon2 -lcurl -lonig -lz $PHP_LDFLAGS" CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $PHP_CFLAGS" CGO_CPPFLAGS=$PHP_CPPFLAGS -RUN cd caddy/frankenphp && \ - GOBIN=/usr/local/bin go install -ldflags "-extldflags '-Wl,-z,stack-size=0x80000' -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \ +WORKDIR /go/src/app/caddy/frankenphp +RUN GOBIN=/usr/local/bin go install -ldflags "-extldflags '-Wl,-z,stack-size=0x80000' -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $PHP_VERSION Caddy'" && \ setcap cap_net_bind_service=+ep /usr/local/bin/frankenphp && \ frankenphp version +WORKDIR /go/src/app + FROM common AS runner diff --git a/build-static.sh b/build-static.sh index e4c878c7b..532b25781 100755 --- a/build-static.sh +++ b/build-static.sh @@ -1,7 +1,6 @@ #!/bin/sh set -o errexit -trap 'echo "Aborting due to errexit on line $LINENO. Exit code: $?" >&2' ERR set -o xtrace if ! type "git" > /dev/null; then @@ -72,6 +71,7 @@ fi ./bin/spc doctor ./bin/spc fetch --with-php="$PHP_VERSION" --for-extensions="$PHP_EXTENSIONS" +# shellcheck disable=SC2086 ./bin/spc build --enable-zts --build-embed $extraOpts "$PHP_EXTENSIONS" --with-libs="$PHP_EXTENSION_LIBS" CGO_CFLAGS="-DFRANKENPHP_VERSION=$FRANKENPHP_VERSION $(./buildroot/bin/php-config --includes | sed s#-I/#-I"$PWD"/buildroot/#g)" export CGO_CFLAGS diff --git a/dev-alpine.Dockerfile b/dev-alpine.Dockerfile index b240ecf25..196d67557 100644 --- a/dev-alpine.Dockerfile +++ b/dev-alpine.Dockerfile @@ -37,8 +37,8 @@ RUN apk add --no-cache \ libtool && \ echo 'set auto-load safe-path /' > /root/.gdbinit -RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git && \ - cd php-src && \ +WORKDIR /usr/local/src/php +RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git . && \ # --enable-embed is only necessary to generate libphp.so, we don't use this SAPI directly ./buildconf --force && \ ./configure \ @@ -47,18 +47,19 @@ RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git && \ --disable-zend-signals \ --enable-zend-max-execution-timers \ --enable-debug && \ - make -j$(nproc) && \ + make -j"$(nproc)" && \ make install && \ ldconfig /etc/ld.so.conf.d && \ cp php.ini-development /usr/local/lib/php.ini && \ - echo -e "zend_extension=opcache.so\nopcache.enable=1" >> /usr/local/lib/php.ini &&\ + echo "zend_extension=opcache.so" >> /usr/local/lib/php.ini && \ + echo "opcache.enable=1" >> /usr/local/lib/php.ini && \ php --version WORKDIR /go/src/app - COPY . . -RUN cd caddy/frankenphp && \ - go build +WORKDIR /go/src/app/caddy/frankenphp +RUN go build +WORKDIR /go/src/app CMD [ "zsh" ] diff --git a/dev.Dockerfile b/dev.Dockerfile index 60015e2aa..732224e5c 100644 --- a/dev.Dockerfile +++ b/dev.Dockerfile @@ -13,6 +13,7 @@ ENV PHPIZE_DEPS \ pkg-config \ re2c +# hadolint ignore=DL3009 RUN apt-get update && \ apt-get -y --no-install-recommends install \ $PHPIZE_DEPS \ @@ -41,8 +42,8 @@ RUN apt-get update && \ && \ apt-get clean -RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git && \ - cd php-src && \ +WORKDIR /usr/local/src/php +RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git . && \ # --enable-embed is only necessary to generate libphp.so, we don't use this SAPI directly ./buildconf --force && \ ./configure \ @@ -51,18 +52,19 @@ RUN git clone --branch=PHP-8.3 https://github.com/php/php-src.git && \ --disable-zend-signals \ --enable-zend-max-execution-timers \ --enable-debug && \ - make -j$(nproc) && \ + make -j"$(nproc)" && \ make install && \ ldconfig && \ cp php.ini-development /usr/local/lib/php.ini && \ - echo "zend_extension=opcache.so\nopcache.enable=1" >> /usr/local/lib/php.ini &&\ + echo "zend_extension=opcache.so" >> /usr/local/lib/php.ini && \ + echo "opcache.enable=1" >> /usr/local/lib/php.ini && \ php --version WORKDIR /go/src/app - COPY . . -RUN cd caddy/frankenphp && \ - go build +WORKDIR /go/src/app/caddy/frankenphp +RUN go build +WORKDIR /go/src/app CMD [ "zsh" ] diff --git a/static-builder.Dockerfile b/static-builder.Dockerfile index b68aed9b8..3c17d458c 100644 --- a/static-builder.Dockerfile +++ b/static-builder.Dockerfile @@ -5,6 +5,7 @@ ARG FRANKENPHP_VERSION='' ARG PHP_VERSION='' ARG PHP_EXTENSIONS='' ARG PHP_EXTENSION_LIBS='' +SHELL ["/bin/ash", "-eo", "pipefail", "-c"] RUN apk update; \ apk add --no-cache \ @@ -56,15 +57,14 @@ ENV PATH="${PATH}:/root/.composer/vendor/bin" COPY --from=composer/composer:2-bin --link /composer /usr/bin/composer WORKDIR /go/src/app - COPY go.mod go.sum ./ RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get -RUN mkdir caddy && cd caddy -COPY caddy/go.mod caddy/go.sum ./caddy/ - -RUN cd caddy && go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get +WORKDIR /go/src/app/caddy +COPY caddy/go.mod caddy/go.sum ./ +RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get +WORKDIR /go/src/app COPY *.* ./ COPY caddy caddy COPY C-Thread-Pool C-Thread-Pool From a3c3637547e5ccaf6c6d311bae21860630d70114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 15:16:18 +0100 Subject: [PATCH 2/8] fix build-static.sh --- build-static.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build-static.sh b/build-static.sh index 532b25781..ee6ff8a1c 100755 --- a/build-static.sh +++ b/build-static.sh @@ -37,7 +37,13 @@ if [ -z "$FRANKENPHP_VERSION" ]; then elif [ -d ".git/" ]; then CURRENT_REF="$(git rev-parse --abbrev-ref HEAD)" export CURRENT_REF - git checkout "v$FRANKENPHP_VERSION" + + if echo "$FRANKENPHP_VERSION" | grep -q "."; then + # Tag + git checkout "v$FRANKENPHP_VERSION" + else + git checkout "$FRANKENPHP_VERSION" + fi fi bin="frankenphp-$os-$arch" From 28a8fb3bab89922898f2c646ab900d1075675a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 15:41:46 +0100 Subject: [PATCH 3/8] fixes --- .github/workflows/docker.yml | 22 +- .github/workflows/lint.yml | 7 + .github/workflows/static.yml | 2 +- frankenphp.c | 1308 +++++++++++++++++----------------- frankenphp.h | 47 +- 5 files changed, 710 insertions(+), 676 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index f5ad6185d..ab752e640 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -31,10 +31,12 @@ jobs: name: Create variants matrix id: matrix run: | - METADATA=$(docker buildx bake --print | jq -c) - echo "metadata=$METADATA" >> "$GITHUB_OUTPUT" - echo "variants=$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< $METADATA)" >> "$GITHUB_OUTPUT" - echo "platforms=$(jq -c 'first(.target[]) | .platforms' <<< $METADATA)" >> "$GITHUB_OUTPUT" + METADATA="$(docker buildx bake --print | jq -c)" + { + echo "metadata=$METADATA" + echo "variants=$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< $METADATA)" + echo "platforms=$(jq -c 'first(.target[]) | .platforms' <<< $METADATA)" + } >> "$GITHUB_OUTPUT" env: LATEST: '1' # TODO: unset this variable when releasing the first stable version SHA: ${{github.sha}} @@ -107,10 +109,10 @@ jobs: run: | mkdir -p /tmp/metadata/builder /tmp/metadata/runner - builderDigest=$(jq -r '."builder-${{matrix.variant}}"."containerimage.digest"' <<< $METADATA) + builderDigest="$(jq -r '."builder-${{matrix.variant}}"."containerimage.digest"' <<< $METADATA)" touch "/tmp/metadata/builder/${builderDigest#sha256:}" - runnerDigest=$(jq -r '."runner-${{matrix.variant}}"."containerimage.digest"' <<< $METADATA) + runnerDigest="$(jq -r '."runner-${{matrix.variant}}"."containerimage.digest"' <<< $METADATA)" touch "/tmp/metadata/runner/${runnerDigest#sha256:}" env: METADATA: ${{steps.build.outputs.metadata}} @@ -138,7 +140,7 @@ jobs: continue-on-error: ${{fromJson(needs.prepare.outputs.push)}} run: | docker run --platform=${{matrix.platform}} --rm \ - $(jq -r '."builder-${{matrix.variant}}"."containerimage.config.digest"' <<< $METADATA) \ + "$(jq -r '."builder-${{matrix.variant}}"."containerimage.config.digest"' <<< $METADATA)" \ sh -c 'go test ${{matrix.race}} -v ./... && cd caddy && go test ${{matrix.race}} -v ./...' env: METADATA: ${{steps.build.outputs.metadata}} @@ -176,13 +178,13 @@ jobs: name: Create manifest list and push working-directory: /tmp/metadata run: | - docker buildx imagetools create $(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | map("-t " + .) | join(" ")' <<< $METADATA) \ - $(printf 'dunglas/frankenphp@sha256:%s ' *) + docker buildx imagetools create "$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | map("-t " + .) | join(" ")' <<< $METADATA)" \ + "$(printf 'dunglas/frankenphp@sha256:%s ' *)" env: METADATA: ${{needs.prepare.outputs.metadata}} - name: Inspect image run: | - docker buildx imagetools inspect $(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | first' <<< $METADATA) + docker buildx imagetools inspect '$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | first' <<< $METADATA)" env: METADATA: ${{needs.prepare.outputs.metadata}} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e2cf568ca..2f7eb68dc 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -31,3 +31,10 @@ jobs: DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} LINTER_RULES_PATH: / + FILTER_REGEX_EXCLUDE: '.*C-Thread-Pool/.*' + VALIDATE_JSCPD: false + VALIDATE_GO: false + VALIDATE_PHP_PHPCS: false + VALIDATE_PHP_PHPSTAN: false + VALIDATE_PHP_PSALM: false + VALIDATE_TERRAGRUNT: false diff --git a/.github/workflows/static.yml b/.github/workflows/static.yml index 2dd0da9e8..8c532429f 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yml @@ -52,7 +52,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Copy binary - run: docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-x86_64 frankenphp-linux-x86_64 ; docker rm static-builder + run: docker cp "$(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/dist/frankenphp-linux-x86_64" frankenphp-linux-x86_64 ; docker rm static-builder - name: Upload asset if: github.ref_type == 'tag' diff --git a/frankenphp.c b/frankenphp.c index 324f6c23c..80e418cd1 100644 --- a/frankenphp.c +++ b/frankenphp.c @@ -1,741 +1,760 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include #include -#include #include #include -#include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -#include "C-Thread-Pool/thpool.h" #include "C-Thread-Pool/thpool.c" +#include "C-Thread-Pool/thpool.h" -#include "frankenphp_arginfo.h" #include "_cgo_export.h" +#include "frankenphp_arginfo.h" #if defined(PHP_WIN32) && defined(ZTS) ZEND_TSRMLS_CACHE_DEFINE() #endif -/* Timeouts are currently fundamentally broken with ZTS except on Linux: https://bugs.php.net/bug.php?id=79464 */ +/* Timeouts are currently fundamentally broken with ZTS except on Linux: + * https://bugs.php.net/bug.php?id=79464 */ #ifndef ZEND_MAX_EXECUTION_TIMERS -static const char HARDCODED_INI[] = - "max_execution_time=0\n" - "max_input_time=-1\n\0"; +static const char HARDCODED_INI[] = "max_execution_time=0\n" + "max_input_time=-1\n\0"; #endif -static const char *MODULES_TO_RELOAD[] = { - "filter", - "session", - NULL -}; +static const char *MODULES_TO_RELOAD[] = {"filter", "session", NULL}; frankenphp_version frankenphp_get_version() { - return (frankenphp_version){ - PHP_MAJOR_VERSION, - PHP_MINOR_VERSION, - PHP_RELEASE_VERSION, - PHP_EXTRA_VERSION, - PHP_VERSION, - PHP_VERSION_ID, - }; + return (frankenphp_version){ + PHP_MAJOR_VERSION, PHP_MINOR_VERSION, PHP_RELEASE_VERSION, + PHP_EXTRA_VERSION, PHP_VERSION, PHP_VERSION_ID, + }; } frankenphp_config frankenphp_get_config() { - return (frankenphp_config){ - frankenphp_get_version(), + return (frankenphp_config){ + frankenphp_get_version(), #ifdef ZTS - true, + true, #else - false, + false, #endif #ifdef ZEND_SIGNALS - true, + true, #else - false, + false, #endif #ifdef ZEND_MAX_EXECUTION_TIMERS - true, + true, #else - false, + false, #endif - }; + }; } typedef struct frankenphp_server_context { - uintptr_t current_request; - uintptr_t main_request; - bool worker_ready; - char *cookie_data; - bool finished; + uintptr_t current_request; + uintptr_t main_request; + bool worker_ready; + char *cookie_data; + bool finished; } frankenphp_server_context; static uintptr_t frankenphp_clean_server_context() { - frankenphp_server_context *ctx = SG(server_context); - if (ctx == NULL) { - return 0; - } + frankenphp_server_context *ctx = SG(server_context); + if (ctx == NULL) { + return 0; + } - free(SG(request_info).auth_password); - SG(request_info).auth_password = NULL; + free(SG(request_info).auth_password); + SG(request_info).auth_password = NULL; - free(SG(request_info).auth_user); - SG(request_info).auth_user = NULL; + free(SG(request_info).auth_user); + SG(request_info).auth_user = NULL; - free((char *) SG(request_info).request_method); - SG(request_info).request_method = NULL; + free((char *)SG(request_info).request_method); + SG(request_info).request_method = NULL; - free(SG(request_info).query_string); - SG(request_info).query_string = NULL; + free(SG(request_info).query_string); + SG(request_info).query_string = NULL; - free((char *) SG(request_info).content_type); - SG(request_info).content_type = NULL; + free((char *)SG(request_info).content_type); + SG(request_info).content_type = NULL; - free(SG(request_info).path_translated); - SG(request_info).path_translated = NULL; + free(SG(request_info).path_translated); + SG(request_info).path_translated = NULL; - free(SG(request_info).request_uri); - SG(request_info).request_uri = NULL; + free(SG(request_info).request_uri); + SG(request_info).request_uri = NULL; - return ctx->current_request; + return ctx->current_request; } static void frankenphp_request_reset() { - zend_try { - int i; + zend_try { + int i; - for (i=0; irequest_shutdown_func(module->type, module->module_number); - } - } - - /* Shutdown output layer (send the set HTTP headers, cleanup output handlers, etc.) */ - zend_try { - php_output_deactivate(); - } zend_end_try(); - - /* Clean super globals */ - frankenphp_request_reset(); - - /* SAPI related shutdown (free stuff) */ - frankenphp_clean_server_context(); - zend_try { - sapi_deactivate(); - } zend_end_try(); - - zend_set_memory_limit(PG(memory_limit)); + /* Flush all output buffers */ + zend_try { php_output_end_all(); } + zend_end_try(); + + // TODO: store the list of modules to reload in a global module variable + const char **module_name; + zend_module_entry *module; + for (module_name = MODULES_TO_RELOAD; *module_name; module_name++) { + if ((module = zend_hash_str_find_ptr(&module_registry, *module_name, + strlen(*module_name)))) { + module->request_shutdown_func(module->type, module->module_number); + } + } + + /* Shutdown output layer (send the set HTTP headers, cleanup output handlers, + * etc.) */ + zend_try { php_output_deactivate(); } + zend_end_try(); + + /* Clean super globals */ + frankenphp_request_reset(); + + /* SAPI related shutdown (free stuff) */ + frankenphp_clean_server_context(); + zend_try { sapi_deactivate(); } + zend_end_try(); + + zend_set_memory_limit(PG(memory_limit)); } /* Adapted from php_request_startup() */ static int frankenphp_worker_request_startup() { - int retval = SUCCESS; - - zend_try { - php_output_activate(); - - /* initialize global variables */ - PG(header_is_being_sent) = 0; - PG(connection_status) = PHP_CONNECTION_NORMAL; - - /* Keep the current execution context */ - sapi_activate(); - - /* - * Timeouts are currently fundamentally broken with ZTS: https://bugs.php.net/bug.php?id=79464 - * - *if (PG(max_input_time) == -1) { - * zend_set_timeout(EG(timeout_seconds), 1); - *} else { - * zend_set_timeout(PG(max_input_time), 1); - *} - */ - - if (PG(expose_php)) { - sapi_add_header(SAPI_PHP_VERSION_HEADER, sizeof(SAPI_PHP_VERSION_HEADER)-1, 1); - } - - if (PG(output_handler) && PG(output_handler)[0]) { - zval oh; - - ZVAL_STRING(&oh, PG(output_handler)); - php_output_start_user(&oh, 0, PHP_OUTPUT_HANDLER_STDFLAGS); - zval_ptr_dtor(&oh); - } else if (PG(output_buffering)) { - php_output_start_user(NULL, PG(output_buffering) > 1 ? PG(output_buffering) : 0, PHP_OUTPUT_HANDLER_STDFLAGS); - } else if (PG(implicit_flush)) { - php_output_set_implicit_flush(1); - } - - php_hash_environment(); - - zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER)); - - // unfinish the request - frankenphp_server_context *ctx = SG(server_context); - ctx->finished = false; - - // TODO: store the list of modules to reload in a global module variable - const char **module_name; - zend_module_entry *module; - for (module_name = MODULES_TO_RELOAD; *module_name; module_name++) { - if ( - (module = zend_hash_str_find_ptr(&module_registry, *module_name, sizeof(*module_name)-1)) - && module->request_startup_func - ) { - module->request_startup_func(module->type, module->module_number); - } - } - } zend_catch { - retval = FAILURE; - } zend_end_try(); - - SG(sapi_started) = 1; - - return retval; -} + int retval = SUCCESS; + + zend_try { + php_output_activate(); + + /* initialize global variables */ + PG(header_is_being_sent) = 0; + PG(connection_status) = PHP_CONNECTION_NORMAL; + + /* Keep the current execution context */ + sapi_activate(); + + /* + * Timeouts are currently fundamentally broken with ZTS: + *https://bugs.php.net/bug.php?id=79464 + * + *if (PG(max_input_time) == -1) { + * zend_set_timeout(EG(timeout_seconds), 1); + *} else { + * zend_set_timeout(PG(max_input_time), 1); + *} + */ + + if (PG(expose_php)) { + sapi_add_header(SAPI_PHP_VERSION_HEADER, + sizeof(SAPI_PHP_VERSION_HEADER) - 1, 1); + } -PHP_FUNCTION(frankenphp_finish_request) { /* {{{ */ - if (zend_parse_parameters_none() == FAILURE) { - RETURN_THROWS(); + if (PG(output_handler) && PG(output_handler)[0]) { + zval oh; + + ZVAL_STRING(&oh, PG(output_handler)); + php_output_start_user(&oh, 0, PHP_OUTPUT_HANDLER_STDFLAGS); + zval_ptr_dtor(&oh); + } else if (PG(output_buffering)) { + php_output_start_user(NULL, + PG(output_buffering) > 1 ? PG(output_buffering) : 0, + PHP_OUTPUT_HANDLER_STDFLAGS); + } else if (PG(implicit_flush)) { + php_output_set_implicit_flush(1); } + php_hash_environment(); + + zend_is_auto_global(ZSTR_KNOWN(ZEND_STR_AUTOGLOBAL_SERVER)); + + // unfinish the request frankenphp_server_context *ctx = SG(server_context); + ctx->finished = false; + + // TODO: store the list of modules to reload in a global module variable + const char **module_name; + zend_module_entry *module; + for (module_name = MODULES_TO_RELOAD; *module_name; module_name++) { + if ((module = zend_hash_str_find_ptr(&module_registry, *module_name, + sizeof(*module_name) - 1)) && + module->request_startup_func) { + module->request_startup_func(module->type, module->module_number); + } + } + } + zend_catch { retval = FAILURE; } + zend_end_try(); - if (ctx->finished) { - RETURN_FALSE; - } + SG(sapi_started) = 1; - php_output_end_all(); - php_header(); + return retval; +} - if (ctx->current_request != 0) { - go_frankenphp_finish_request(ctx->main_request, ctx->current_request, false); - } +PHP_FUNCTION(frankenphp_finish_request) { /* {{{ */ + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } - ctx->finished = true; + frankenphp_server_context *ctx = SG(server_context); - RETURN_TRUE; + if (ctx->finished) { + RETURN_FALSE; + } + + php_output_end_all(); + php_header(); + + if (ctx->current_request != 0) { + go_frankenphp_finish_request(ctx->main_request, ctx->current_request, + false); + } + + ctx->finished = true; + + RETURN_TRUE; } /* }}} */ PHP_FUNCTION(frankenphp_handle_request) { - zend_fcall_info fci; - zend_fcall_info_cache fcc; + zend_fcall_info fci; + zend_fcall_info_cache fcc; - ZEND_PARSE_PARAMETERS_START(1, 1) - Z_PARAM_FUNC(fci, fcc) - ZEND_PARSE_PARAMETERS_END(); + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_FUNC(fci, fcc) + ZEND_PARSE_PARAMETERS_END(); - frankenphp_server_context *ctx = SG(server_context); + frankenphp_server_context *ctx = SG(server_context); - if (ctx->main_request == 0) { - // not a worker, throw an error - zend_throw_exception(spl_ce_RuntimeException, "frankenphp_handle_request() called while not in worker mode", 0); - RETURN_THROWS(); - } + if (ctx->main_request == 0) { + // not a worker, throw an error + zend_throw_exception( + spl_ce_RuntimeException, + "frankenphp_handle_request() called while not in worker mode", 0); + RETURN_THROWS(); + } - if (!ctx->worker_ready) { - /* Clean the first dummy request created to initialize the worker */ - frankenphp_worker_request_shutdown(); + if (!ctx->worker_ready) { + /* Clean the first dummy request created to initialize the worker */ + frankenphp_worker_request_shutdown(); - ctx->worker_ready = true; + ctx->worker_ready = true; - /* Mark the worker as ready to handle requests */ - go_frankenphp_worker_ready(); - } + /* Mark the worker as ready to handle requests */ + go_frankenphp_worker_ready(); + } #ifdef ZEND_MAX_EXECUTION_TIMERS - // Disable timeouts while waiting for a request to handle - zend_unset_timeout(); + // Disable timeouts while waiting for a request to handle + zend_unset_timeout(); #endif - uintptr_t request = go_frankenphp_worker_handle_request_start(ctx->main_request); - if ( - frankenphp_worker_request_startup() == FAILURE - /* Shutting down */ - || !request - ) { - RETURN_FALSE; - } + uintptr_t request = + go_frankenphp_worker_handle_request_start(ctx->main_request); + if (frankenphp_worker_request_startup() == FAILURE + /* Shutting down */ + || !request) { + RETURN_FALSE; + } #ifdef ZEND_MAX_EXECUTION_TIMERS - // Reset default timeout - // TODO: add support for max_input_time - zend_set_timeout(INI_INT("max_execution_time"), 0); + // Reset default timeout + // TODO: add support for max_input_time + zend_set_timeout(INI_INT("max_execution_time"), 0); #endif - /* Call the PHP func */ - zval retval = {0}; - fci.size = sizeof fci; - fci.retval = &retval; - if (zend_call_function(&fci, &fcc) == SUCCESS) { - zval_ptr_dtor(&retval); - } - - /* If an exception occured, print the message to the client before closing the connection */ - if (EG(exception)) { - zend_exception_error(EG(exception), E_ERROR); - } - - frankenphp_worker_request_shutdown(); - ctx->current_request = 0; - go_frankenphp_finish_request(ctx->main_request, request, true); - - RETURN_TRUE; + /* Call the PHP func */ + zval retval = {0}; + fci.size = sizeof fci; + fci.retval = &retval; + if (zend_call_function(&fci, &fcc) == SUCCESS) { + zval_ptr_dtor(&retval); + } + + /* If an exception occured, print the message to the client before closing the + * connection */ + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR); + } + + frankenphp_worker_request_shutdown(); + ctx->current_request = 0; + go_frankenphp_finish_request(ctx->main_request, request, true); + + RETURN_TRUE; } PHP_FUNCTION(headers_send) { - zend_long response_code = 200; + zend_long response_code = 200; + + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_LONG(response_code) + ZEND_PARSE_PARAMETERS_END(); - ZEND_PARSE_PARAMETERS_START(0, 1) - Z_PARAM_OPTIONAL - Z_PARAM_LONG(response_code) - ZEND_PARSE_PARAMETERS_END(); + int previous_status_code = SG(sapi_headers).http_response_code; + SG(sapi_headers).http_response_code = response_code; - int previous_status_code = SG(sapi_headers).http_response_code; - SG(sapi_headers).http_response_code = response_code; + if (response_code >= 100 && response_code < 200) { + int ret = sapi_module.send_headers(&SG(sapi_headers)); + SG(sapi_headers).http_response_code = previous_status_code; - if (response_code >= 100 && response_code < 200) { - int ret = sapi_module.send_headers(&SG(sapi_headers)); - SG(sapi_headers).http_response_code = previous_status_code; + RETURN_LONG(ret); + } - RETURN_LONG(ret); - } - - RETURN_LONG(sapi_send_headers()); + RETURN_LONG(sapi_send_headers()); } static zend_module_entry frankenphp_module = { STANDARD_MODULE_HEADER, "frankenphp", - ext_functions, /* function table */ - NULL, /* initialization */ - NULL, /* shutdown */ - NULL, /* request initialization */ - NULL, /* request shutdown */ - NULL, /* information */ + ext_functions, /* function table */ + NULL, /* initialization */ + NULL, /* shutdown */ + NULL, /* request initialization */ + NULL, /* request shutdown */ + NULL, /* information */ TOSTRING(FRANKENPHP_VERSION), - STANDARD_MODULE_PROPERTIES -}; + STANDARD_MODULE_PROPERTIES}; -static uintptr_t frankenphp_request_shutdown() -{ - frankenphp_server_context *ctx = SG(server_context); +static uintptr_t frankenphp_request_shutdown() { + frankenphp_server_context *ctx = SG(server_context); - if (ctx->main_request && ctx->current_request) { - frankenphp_request_reset(); - } + if (ctx->main_request && ctx->current_request) { + frankenphp_request_reset(); + } - php_request_shutdown((void *) 0); + php_request_shutdown((void *)0); - free(ctx->cookie_data); - ((frankenphp_server_context*) SG(server_context))->cookie_data = NULL; - uintptr_t rh = frankenphp_clean_server_context(); + free(ctx->cookie_data); + ((frankenphp_server_context *)SG(server_context))->cookie_data = NULL; + uintptr_t rh = frankenphp_clean_server_context(); - free(ctx); - SG(server_context) = NULL; + free(ctx); + SG(server_context) = NULL; #if defined(ZTS) - ts_free_thread(); + ts_free_thread(); #endif - return rh; + return rh; } int frankenphp_update_server_context( - bool create, - uintptr_t current_request, - uintptr_t main_request, - - const char *request_method, - char *query_string, - zend_long content_length, - char *path_translated, - char *request_uri, - const char *content_type, - char *auth_user, - char *auth_password, - int proto_num -) { - frankenphp_server_context *ctx; - - if (create) { + bool create, uintptr_t current_request, uintptr_t main_request, + + const char *request_method, char *query_string, zend_long content_length, + char *path_translated, char *request_uri, const char *content_type, + char *auth_user, char *auth_password, int proto_num) { + frankenphp_server_context *ctx; + + if (create) { #ifdef ZTS - /* initial resource fetch */ - (void)ts_resource(0); -# ifdef PHP_WIN32 - ZEND_TSRMLS_CACHE_UPDATE(); -# endif + /* initial resource fetch */ + (void)ts_resource(0); +#ifdef PHP_WIN32 + ZEND_TSRMLS_CACHE_UPDATE(); #endif +#endif + + /* todo: use a pool */ + ctx = (frankenphp_server_context *)calloc( + 1, sizeof(frankenphp_server_context)); + if (ctx == NULL) { + return FAILURE; + } + + ctx->cookie_data = NULL; + ctx->finished = false; + + SG(server_context) = ctx; + } else { + ctx = (frankenphp_server_context *)SG(server_context); + } - /* todo: use a pool */ - ctx = (frankenphp_server_context *) calloc(1, sizeof(frankenphp_server_context)); - if (ctx == NULL) { - return FAILURE; - } - - ctx->cookie_data = NULL; - ctx->finished = false; - - SG(server_context) = ctx; - } else { - ctx = (frankenphp_server_context *) SG(server_context); - } - - ctx->main_request = main_request; - ctx->current_request = current_request; - - SG(request_info).auth_password = auth_password; - SG(request_info).auth_user = auth_user; - SG(request_info).request_method = request_method; - SG(request_info).query_string = query_string; - SG(request_info).content_type = content_type; - SG(request_info).content_length = content_length; - SG(request_info).path_translated = path_translated; - SG(request_info).request_uri = request_uri; - SG(request_info).proto_num = proto_num; - - return SUCCESS; + ctx->main_request = main_request; + ctx->current_request = current_request; + + SG(request_info).auth_password = auth_password; + SG(request_info).auth_user = auth_user; + SG(request_info).request_method = request_method; + SG(request_info).query_string = query_string; + SG(request_info).content_type = content_type; + SG(request_info).content_length = content_length; + SG(request_info).path_translated = path_translated; + SG(request_info).request_uri = request_uri; + SG(request_info).proto_num = proto_num; + + return SUCCESS; } -static int frankenphp_startup(sapi_module_struct *sapi_module) -{ - return php_module_startup(sapi_module, &frankenphp_module); +static int frankenphp_startup(sapi_module_struct *sapi_module) { + return php_module_startup(sapi_module, &frankenphp_module); } -static int frankenphp_deactivate(void) -{ - /* TODO: flush everything */ - return SUCCESS; +static int frankenphp_deactivate(void) { + /* TODO: flush everything */ + return SUCCESS; } -static size_t frankenphp_ub_write(const char *str, size_t str_length) -{ - frankenphp_server_context* ctx = SG(server_context); +static size_t frankenphp_ub_write(const char *str, size_t str_length) { + frankenphp_server_context *ctx = SG(server_context); - if(ctx->finished) { - // TODO: maybe log a warning that we tried to write to a finished request? - return 0; - } + if (ctx->finished) { + // TODO: maybe log a warning that we tried to write to a finished request? + return 0; + } - struct go_ub_write_return result = go_ub_write(ctx->current_request ? ctx->current_request : ctx->main_request, (char *) str, str_length); + struct go_ub_write_return result = go_ub_write( + ctx->current_request ? ctx->current_request : ctx->main_request, + (char *)str, str_length); - if (result.r1) { - php_handle_aborted_connection(); - } + if (result.r1) { + php_handle_aborted_connection(); + } - return result.r0; + return result.r0; } -static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) -{ - if (SG(request_info).no_headers == 1) { - return SAPI_HEADER_SENT_SUCCESSFULLY; - } +static int frankenphp_send_headers(sapi_headers_struct *sapi_headers) { + if (SG(request_info).no_headers == 1) { + return SAPI_HEADER_SENT_SUCCESSFULLY; + } - int status; - frankenphp_server_context* ctx = SG(server_context); + int status; + frankenphp_server_context *ctx = SG(server_context); - if (ctx->current_request == 0) { - return SAPI_HEADER_SEND_FAILED; - } + if (ctx->current_request == 0) { + return SAPI_HEADER_SEND_FAILED; + } - if (SG(sapi_headers).http_status_line) { - status = atoi((SG(sapi_headers).http_status_line) + 9); - } else { - status = SG(sapi_headers).http_response_code; + if (SG(sapi_headers).http_status_line) { + status = atoi((SG(sapi_headers).http_status_line) + 9); + } else { + status = SG(sapi_headers).http_response_code; - if (!status) { - status = 200; - } - } + if (!status) { + status = 200; + } + } - go_write_headers(ctx->current_request, status, &sapi_headers->headers); + go_write_headers(ctx->current_request, status, &sapi_headers->headers); - return SAPI_HEADER_SENT_SUCCESSFULLY; + return SAPI_HEADER_SENT_SUCCESSFULLY; } -static void frankenphp_sapi_flush(void *server_context) -{ - frankenphp_server_context *ctx = (frankenphp_server_context *) server_context; +static void frankenphp_sapi_flush(void *server_context) { + frankenphp_server_context *ctx = (frankenphp_server_context *)server_context; - if (ctx && ctx->current_request != 0 && go_sapi_flush(ctx->current_request)) { - php_handle_aborted_connection(); - } + if (ctx && ctx->current_request != 0 && go_sapi_flush(ctx->current_request)) { + php_handle_aborted_connection(); + } } -static size_t frankenphp_read_post(char *buffer, size_t count_bytes) -{ - frankenphp_server_context* ctx = SG(server_context); +static size_t frankenphp_read_post(char *buffer, size_t count_bytes) { + frankenphp_server_context *ctx = SG(server_context); - return ctx->current_request ? go_read_post(ctx->current_request, buffer, count_bytes) : 0; + return ctx->current_request + ? go_read_post(ctx->current_request, buffer, count_bytes) + : 0; } -static char* frankenphp_read_cookies(void) -{ - frankenphp_server_context* ctx = SG(server_context); +static char *frankenphp_read_cookies(void) { + frankenphp_server_context *ctx = SG(server_context); - if (ctx->current_request == 0) { - return ""; - } + if (ctx->current_request == 0) { + return ""; + } - ctx->cookie_data = go_read_cookies(ctx->current_request); + ctx->cookie_data = go_read_cookies(ctx->current_request); - return ctx->cookie_data; + return ctx->cookie_data; } -static void frankenphp_register_known_variable(const char *key, char *value, zval *track_vars_array, bool f) -{ - if (value == NULL) { - return; - } - - size_t new_val_len; - if (sapi_module.input_filter(PARSE_SERVER, key, &value, strlen(value), &new_val_len)) { - php_register_variable_safe(key, value, new_val_len, track_vars_array); - } - - if (f) { - free(value); - value = NULL; - } +static void frankenphp_register_known_variable(const char *key, char *value, + zval *track_vars_array, bool f) { + if (value == NULL) { + return; + } + + size_t new_val_len; + if (sapi_module.input_filter(PARSE_SERVER, key, &value, strlen(value), + &new_val_len)) { + php_register_variable_safe(key, value, new_val_len, track_vars_array); + } + + if (f) { + free(value); + value = NULL; + } } -void frankenphp_register_bulk_variables(char *known_variables[27], char **dynamic_variables, size_t size, zval *track_vars_array) -{ - /* Not used, but must be present */ - frankenphp_register_known_variable("AUTH_TYPE", "", track_vars_array, false); - frankenphp_register_known_variable("REMOTE_IDENT", "", track_vars_array, false); - - /* Allocated in frankenphp_update_server_context() */ - frankenphp_register_known_variable("CONTENT_TYPE", (char *) SG(request_info).content_type, track_vars_array, false); - frankenphp_register_known_variable("PATH_TRANSLATED", (char *) SG(request_info).path_translated, track_vars_array, false); - frankenphp_register_known_variable("QUERY_STRING", SG(request_info).query_string, track_vars_array, false); - frankenphp_register_known_variable("REMOTE_USER", (char *) SG(request_info).auth_user, track_vars_array, false); - frankenphp_register_known_variable("REQUEST_METHOD", (char *) SG(request_info).request_method, track_vars_array, false); - frankenphp_register_known_variable("REQUEST_URI", SG(request_info).request_uri, track_vars_array,false); - - /* Known variables */ - frankenphp_register_known_variable("CONTENT_LENGTH", known_variables[0], track_vars_array, true); - frankenphp_register_known_variable("DOCUMENT_ROOT", known_variables[1], track_vars_array, true); - frankenphp_register_known_variable("DOCUMENT_URI", known_variables[2], track_vars_array, true); - frankenphp_register_known_variable("GATEWAY_INTERFACE", known_variables[3], track_vars_array, true); - frankenphp_register_known_variable("HTTP_HOST", known_variables[4], track_vars_array, true); - frankenphp_register_known_variable("HTTPS", known_variables[5], track_vars_array, true); - frankenphp_register_known_variable("PATH_INFO", known_variables[6], track_vars_array, true); - frankenphp_register_known_variable("PHP_SELF", known_variables[7], track_vars_array, true); - frankenphp_register_known_variable("REMOTE_ADDR", known_variables[8], track_vars_array, known_variables[8] != known_variables[9]); - frankenphp_register_known_variable("REMOTE_HOST", known_variables[9], track_vars_array, true); - frankenphp_register_known_variable("REMOTE_PORT", known_variables[10], track_vars_array, true); - frankenphp_register_known_variable("REQUEST_SCHEME", known_variables[11], track_vars_array, true); - frankenphp_register_known_variable("SCRIPT_FILENAME", known_variables[12], track_vars_array, true); - frankenphp_register_known_variable("SCRIPT_NAME", known_variables[13], track_vars_array, true); - frankenphp_register_known_variable("SERVER_NAME", known_variables[14], track_vars_array, true); - frankenphp_register_known_variable("SERVER_PORT", known_variables[15], track_vars_array, true); - frankenphp_register_known_variable("SERVER_PROTOCOL", known_variables[16], track_vars_array, true); - frankenphp_register_known_variable("SERVER_SOFTWARE", known_variables[17], track_vars_array, true); - frankenphp_register_known_variable("SSL_PROTOCOL", known_variables[18], track_vars_array, true); - - size_t new_val_len; - for (size_t i = 0; i < size; i = i+2) - { - if (sapi_module.input_filter(PARSE_SERVER, dynamic_variables[i], &dynamic_variables[i+1], strlen(dynamic_variables[i+1]), &new_val_len)) { - php_register_variable_safe(dynamic_variables[i], dynamic_variables[i+1], new_val_len, track_vars_array); - } - - free(dynamic_variables[i]); - free(dynamic_variables[i+1]); - } - - free(dynamic_variables); +void frankenphp_register_bulk_variables(char *known_variables[27], + char **dynamic_variables, size_t size, + zval *track_vars_array) { + /* Not used, but must be present */ + frankenphp_register_known_variable("AUTH_TYPE", "", track_vars_array, false); + frankenphp_register_known_variable("REMOTE_IDENT", "", track_vars_array, + false); + + /* Allocated in frankenphp_update_server_context() */ + frankenphp_register_known_variable("CONTENT_TYPE", + (char *)SG(request_info).content_type, + track_vars_array, false); + frankenphp_register_known_variable("PATH_TRANSLATED", + (char *)SG(request_info).path_translated, + track_vars_array, false); + frankenphp_register_known_variable( + "QUERY_STRING", SG(request_info).query_string, track_vars_array, false); + frankenphp_register_known_variable("REMOTE_USER", + (char *)SG(request_info).auth_user, + track_vars_array, false); + frankenphp_register_known_variable("REQUEST_METHOD", + (char *)SG(request_info).request_method, + track_vars_array, false); + frankenphp_register_known_variable( + "REQUEST_URI", SG(request_info).request_uri, track_vars_array, false); + + /* Known variables */ + frankenphp_register_known_variable("CONTENT_LENGTH", known_variables[0], + track_vars_array, true); + frankenphp_register_known_variable("DOCUMENT_ROOT", known_variables[1], + track_vars_array, true); + frankenphp_register_known_variable("DOCUMENT_URI", known_variables[2], + track_vars_array, true); + frankenphp_register_known_variable("GATEWAY_INTERFACE", known_variables[3], + track_vars_array, true); + frankenphp_register_known_variable("HTTP_HOST", known_variables[4], + track_vars_array, true); + frankenphp_register_known_variable("HTTPS", known_variables[5], + track_vars_array, true); + frankenphp_register_known_variable("PATH_INFO", known_variables[6], + track_vars_array, true); + frankenphp_register_known_variable("PHP_SELF", known_variables[7], + track_vars_array, true); + frankenphp_register_known_variable("REMOTE_ADDR", known_variables[8], + track_vars_array, + known_variables[8] != known_variables[9]); + frankenphp_register_known_variable("REMOTE_HOST", known_variables[9], + track_vars_array, true); + frankenphp_register_known_variable("REMOTE_PORT", known_variables[10], + track_vars_array, true); + frankenphp_register_known_variable("REQUEST_SCHEME", known_variables[11], + track_vars_array, true); + frankenphp_register_known_variable("SCRIPT_FILENAME", known_variables[12], + track_vars_array, true); + frankenphp_register_known_variable("SCRIPT_NAME", known_variables[13], + track_vars_array, true); + frankenphp_register_known_variable("SERVER_NAME", known_variables[14], + track_vars_array, true); + frankenphp_register_known_variable("SERVER_PORT", known_variables[15], + track_vars_array, true); + frankenphp_register_known_variable("SERVER_PROTOCOL", known_variables[16], + track_vars_array, true); + frankenphp_register_known_variable("SERVER_SOFTWARE", known_variables[17], + track_vars_array, true); + frankenphp_register_known_variable("SSL_PROTOCOL", known_variables[18], + track_vars_array, true); + + size_t new_val_len; + for (size_t i = 0; i < size; i = i + 2) { + if (sapi_module.input_filter( + PARSE_SERVER, dynamic_variables[i], &dynamic_variables[i + 1], + strlen(dynamic_variables[i + 1]), &new_val_len)) { + php_register_variable_safe(dynamic_variables[i], dynamic_variables[i + 1], + new_val_len, track_vars_array); + } + + free(dynamic_variables[i]); + free(dynamic_variables[i + 1]); + } + + free(dynamic_variables); } -static void frankenphp_register_variables(zval *track_vars_array) -{ - /* https://www.php.net/manual/en/reserved.variables.server.php */ - frankenphp_server_context* ctx = SG(server_context); +static void frankenphp_register_variables(zval *track_vars_array) { + /* https://www.php.net/manual/en/reserved.variables.server.php */ + frankenphp_server_context *ctx = SG(server_context); - go_register_variables(ctx->current_request ? ctx->current_request : ctx->main_request, track_vars_array); + go_register_variables(ctx->current_request ? ctx->current_request + : ctx->main_request, + track_vars_array); } -static void frankenphp_log_message(const char *message, int syslog_type_int) -{ - go_log((char *) message, syslog_type_int); +static void frankenphp_log_message(const char *message, int syslog_type_int) { + go_log((char *)message, syslog_type_int); } sapi_module_struct frankenphp_sapi_module = { - "frankenphp", /* name */ - "FrankenPHP", /* pretty name */ + "frankenphp", /* name */ + "FrankenPHP", /* pretty name */ - frankenphp_startup, /* startup */ - php_module_shutdown_wrapper, /* shutdown */ + frankenphp_startup, /* startup */ + php_module_shutdown_wrapper, /* shutdown */ - NULL, /* activate */ - frankenphp_deactivate, /* deactivate */ + NULL, /* activate */ + frankenphp_deactivate, /* deactivate */ - frankenphp_ub_write, /* unbuffered write */ - frankenphp_sapi_flush, /* flush */ - NULL, /* get uid */ - NULL, /* getenv */ + frankenphp_ub_write, /* unbuffered write */ + frankenphp_sapi_flush, /* flush */ + NULL, /* get uid */ + NULL, /* getenv */ - php_error, /* error handler */ + php_error, /* error handler */ - NULL, /* header handler */ - frankenphp_send_headers, /* send headers handler */ - NULL, /* send header handler */ + NULL, /* header handler */ + frankenphp_send_headers, /* send headers handler */ + NULL, /* send header handler */ - frankenphp_read_post, /* read POST data */ - frankenphp_read_cookies, /* read Cookies */ + frankenphp_read_post, /* read POST data */ + frankenphp_read_cookies, /* read Cookies */ - frankenphp_register_variables, /* register server variables */ - frankenphp_log_message, /* Log message */ - NULL, /* Get request time */ - NULL, /* Child terminate */ + frankenphp_register_variables, /* register server variables */ + frankenphp_log_message, /* Log message */ + NULL, /* Get request time */ + NULL, /* Child terminate */ - STANDARD_SAPI_MODULE_PROPERTIES -}; + STANDARD_SAPI_MODULE_PROPERTIES}; static void *manager_thread(void *arg) { #ifdef ZTS - // TODO: use tsrm_startup() directly as we know the number of expected threads - php_tsrm_startup(); - /*tsrm_error_set(TSRM_ERROR_LEVEL_INFO, NULL);*/ -# ifdef PHP_WIN32 - ZEND_TSRMLS_CACHE_UPDATE(); -# endif + // TODO: use tsrm_startup() directly as we know the number of expected threads + php_tsrm_startup(); + /*tsrm_error_set(TSRM_ERROR_LEVEL_INFO, NULL);*/ +#ifdef PHP_WIN32 + ZEND_TSRMLS_CACHE_UPDATE(); +#endif #endif - sapi_startup(&frankenphp_sapi_module); + sapi_startup(&frankenphp_sapi_module); #ifndef ZEND_MAX_EXECUTION_TIMERS -# if (PHP_VERSION_ID >= 80300) - frankenphp_sapi_module.ini_entries = HARDCODED_INI; -# else - frankenphp_sapi_module.ini_entries = malloc(sizeof(HARDCODED_INI)); - memcpy(frankenphp_sapi_module.ini_entries, HARDCODED_INI, sizeof(HARDCODED_INI)); -# endif +#if (PHP_VERSION_ID >= 80300) + frankenphp_sapi_module.ini_entries = HARDCODED_INI; +#else + frankenphp_sapi_module.ini_entries = malloc(sizeof(HARDCODED_INI)); + memcpy(frankenphp_sapi_module.ini_entries, HARDCODED_INI, + sizeof(HARDCODED_INI)); +#endif #endif - frankenphp_sapi_module.startup(&frankenphp_sapi_module); + frankenphp_sapi_module.startup(&frankenphp_sapi_module); - threadpool thpool = thpool_init(*((int *) arg)); - free(arg); + threadpool thpool = thpool_init(*((int *)arg)); + free(arg); - uintptr_t rh; - while ((rh = go_fetch_request())) { - thpool_add_work(thpool, go_execute_script, (void *) rh); - } + uintptr_t rh; + while ((rh = go_fetch_request())) { + thpool_add_work(thpool, go_execute_script, (void *)rh); + } - /* channel closed, shutdown gracefully */ - thpool_wait(thpool); - thpool_destroy(thpool); + /* channel closed, shutdown gracefully */ + thpool_wait(thpool); + thpool_destroy(thpool); - frankenphp_sapi_module.shutdown(&frankenphp_sapi_module); + frankenphp_sapi_module.shutdown(&frankenphp_sapi_module); - sapi_shutdown(); + sapi_shutdown(); #ifdef ZTS - tsrm_shutdown(); + tsrm_shutdown(); #endif #if (PHP_VERSION_ID < 80300) - if (frankenphp_sapi_module.ini_entries) { - free(frankenphp_sapi_module.ini_entries); - frankenphp_sapi_module.ini_entries = NULL; - } + if (frankenphp_sapi_module.ini_entries) { + free(frankenphp_sapi_module.ini_entries); + frankenphp_sapi_module.ini_entries = NULL; + } #endif - go_shutdown(); + go_shutdown(); - return NULL; + return NULL; } int frankenphp_init(int num_threads) { - pthread_t thread; + pthread_t thread; - int *num_threads_ptr = calloc(1, sizeof(int)); - *num_threads_ptr = num_threads; + int *num_threads_ptr = calloc(1, sizeof(int)); + *num_threads_ptr = num_threads; - if (pthread_create(&thread, NULL, *manager_thread, (void *) num_threads_ptr) != 0) { - go_shutdown(); + if (pthread_create(&thread, NULL, *manager_thread, (void *)num_threads_ptr) != + 0) { + go_shutdown(); - return -1; - } + return -1; + } - return pthread_detach(thread); + return pthread_detach(thread); } -int frankenphp_request_startup() -{ - if (php_request_startup() == SUCCESS) { - return SUCCESS; - } +int frankenphp_request_startup() { + if (php_request_startup() == SUCCESS) { + return SUCCESS; + } - frankenphp_server_context *ctx = SG(server_context); - SG(server_context) = NULL; - free(ctx); + frankenphp_server_context *ctx = SG(server_context); + SG(server_context) = NULL; + free(ctx); - php_request_shutdown((void *) 0); + php_request_shutdown((void *)0); - return FAILURE; + return FAILURE; } -int frankenphp_execute_script(char* file_name) -{ - if (frankenphp_request_startup() == FAILURE) { - free(file_name); +int frankenphp_execute_script(char *file_name) { + if (frankenphp_request_startup() == FAILURE) { + free(file_name); - return FAILURE; - } + return FAILURE; + } - int status = SUCCESS; + int status = SUCCESS; - zend_file_handle file_handle; - zend_stream_init_filename(&file_handle, file_name); - free(file_name); + zend_file_handle file_handle; + zend_stream_init_filename(&file_handle, file_name); + free(file_name); - file_handle.primary_script = 1; + file_handle.primary_script = 1; - zend_first_try { - EG(exit_status) = 0; - php_execute_script(&file_handle); - status = EG(exit_status); - } zend_catch { - status = EG(exit_status); - } zend_end_try(); + zend_first_try { + EG(exit_status) = 0; + php_execute_script(&file_handle); + status = EG(exit_status); + } + zend_catch { status = EG(exit_status); } + zend_end_try(); - zend_destroy_file_handle(&file_handle); + zend_destroy_file_handle(&file_handle); - frankenphp_clean_server_context(); - frankenphp_request_shutdown(); + frankenphp_clean_server_context(); + frankenphp_request_shutdown(); - return status; + return status; } // Use global variables to store CLI arguments to prevent useless allocations @@ -743,128 +762,141 @@ static char *cli_script; static int cli_argc; static char **cli_argv; -// Adapted from https://github.com/php/php-src/sapi/cli/php_cli.c (The PHP Group, The PHP License) +// Adapted from https://github.com/php/php-src/sapi/cli/php_cli.c (The PHP +// Group, The PHP License) static void cli_register_file_handles(bool no_close) /* {{{ */ { - php_stream *s_in, *s_out, *s_err; - php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL; - zend_constant ic, oc, ec; - - s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in); - s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); - s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); - - if (s_in==NULL || s_out==NULL || s_err==NULL) { - if (s_in) php_stream_close(s_in); - if (s_out) php_stream_close(s_out); - if (s_err) php_stream_close(s_err); - return; - } - - if (no_close) { - s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE; - s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE; - s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE; - } - - //s_in_process = s_in; - - php_stream_to_zval(s_in, &ic.value); - php_stream_to_zval(s_out, &oc.value); - php_stream_to_zval(s_err, &ec.value); - - ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0); - ic.name = zend_string_init_interned("STDIN", sizeof("STDIN")-1, 0); - zend_register_constant(&ic); - - ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0); - oc.name = zend_string_init_interned("STDOUT", sizeof("STDOUT")-1, 0); - zend_register_constant(&oc); - - ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0); - ec.name = zend_string_init_interned("STDERR", sizeof("STDERR")-1, 0); - zend_register_constant(&ec); + php_stream *s_in, *s_out, *s_err; + php_stream_context *sc_in = NULL, *sc_out = NULL, *sc_err = NULL; + zend_constant ic, oc, ec; + + s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in); + s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); + s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); + + if (s_in == NULL || s_out == NULL || s_err == NULL) { + if (s_in) + php_stream_close(s_in); + if (s_out) + php_stream_close(s_out); + if (s_err) + php_stream_close(s_err); + return; + } + + if (no_close) { + s_in->flags |= PHP_STREAM_FLAG_NO_CLOSE; + s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE; + s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE; + } + + // s_in_process = s_in; + + php_stream_to_zval(s_in, &ic.value); + php_stream_to_zval(s_out, &oc.value); + php_stream_to_zval(s_err, &ec.value); + + ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0); + ic.name = zend_string_init_interned("STDIN", sizeof("STDIN") - 1, 0); + zend_register_constant(&ic); + + ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0); + oc.name = zend_string_init_interned("STDOUT", sizeof("STDOUT") - 1, 0); + zend_register_constant(&oc); + + ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0); + ec.name = zend_string_init_interned("STDERR", sizeof("STDERR") - 1, 0); + zend_register_constant(&ec); } /* }}} */ static void sapi_cli_register_variables(zval *track_vars_array) /* {{{ */ { - size_t len; - char *docroot = ""; - - /* In CGI mode, we consider the environment to be a part of the server - * variables - */ - php_import_environment_variables(track_vars_array); - - /* Build the special-case PHP_SELF variable for the CLI version */ - len = strlen(cli_script); - if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &cli_script, len, &len)) { - php_register_variable_safe("PHP_SELF", cli_script, len, track_vars_array); - } - if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &cli_script, len, &len)) { - php_register_variable_safe("SCRIPT_NAME", cli_script, len, track_vars_array); - } - /* filenames are empty for stdin */ - if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &cli_script, len, &len)) { - php_register_variable_safe("SCRIPT_FILENAME", cli_script, len, track_vars_array); - } - if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &cli_script, len, &len)) { - php_register_variable_safe("PATH_TRANSLATED", cli_script, len, track_vars_array); - } - /* just make it available */ - len = 0U; - if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, &len)) { - php_register_variable_safe("DOCUMENT_ROOT", docroot, len, track_vars_array); - } + size_t len; + char *docroot = ""; + + /* In CGI mode, we consider the environment to be a part of the server + * variables + */ + php_import_environment_variables(track_vars_array); + + /* Build the special-case PHP_SELF variable for the CLI version */ + len = strlen(cli_script); + if (sapi_module.input_filter(PARSE_SERVER, "PHP_SELF", &cli_script, len, + &len)) { + php_register_variable_safe("PHP_SELF", cli_script, len, track_vars_array); + } + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_NAME", &cli_script, len, + &len)) { + php_register_variable_safe("SCRIPT_NAME", cli_script, len, + track_vars_array); + } + /* filenames are empty for stdin */ + if (sapi_module.input_filter(PARSE_SERVER, "SCRIPT_FILENAME", &cli_script, + len, &len)) { + php_register_variable_safe("SCRIPT_FILENAME", cli_script, len, + track_vars_array); + } + if (sapi_module.input_filter(PARSE_SERVER, "PATH_TRANSLATED", &cli_script, + len, &len)) { + php_register_variable_safe("PATH_TRANSLATED", cli_script, len, + track_vars_array); + } + /* just make it available */ + len = 0U; + if (sapi_module.input_filter(PARSE_SERVER, "DOCUMENT_ROOT", &docroot, len, + &len)) { + php_register_variable_safe("DOCUMENT_ROOT", docroot, len, track_vars_array); + } } /* }}} */ -static void * execute_script_cli(void *arg) { - void *exit_status; +static void *execute_script_cli(void *arg) { + void *exit_status; - // The SAPI name "cli" is hardcoded into too many programs... let's usurp it. - php_embed_module.name = "cli"; - php_embed_module.pretty_name = "PHP CLI embedded in FrankenPHP"; - php_embed_module.register_server_variables = sapi_cli_register_variables; + // The SAPI name "cli" is hardcoded into too many programs... let's usurp it. + php_embed_module.name = "cli"; + php_embed_module.pretty_name = "PHP CLI embedded in FrankenPHP"; + php_embed_module.register_server_variables = sapi_cli_register_variables; - php_embed_init(cli_argc, cli_argv); + php_embed_init(cli_argc, cli_argv); - cli_register_file_handles(false); - zend_first_try { - zend_file_handle file_handle; - zend_stream_init_filename(&file_handle, cli_script); + cli_register_file_handles(false); + zend_first_try { + zend_file_handle file_handle; + zend_stream_init_filename(&file_handle, cli_script); - php_execute_script(&file_handle); - } zend_end_try(); + php_execute_script(&file_handle); + } + zend_end_try(); - exit_status = (void *) (intptr_t) EG(exit_status); + exit_status = (void *)(intptr_t)EG(exit_status); - php_embed_shutdown(); + php_embed_shutdown(); - return exit_status; + return exit_status; } int frankenphp_execute_script_cli(char *script, int argc, char **argv) { - pthread_t thread; - int err; - void *exit_status; - - cli_script = script; - cli_argc = argc; - cli_argv = argv; - - // Start the script in a dedicated thread to prevent conflicts between Go and PHP signal handlers - err = pthread_create(&thread, NULL, execute_script_cli, NULL); - if (err != 0) { - return err; - } - - err = pthread_join(thread, &exit_status); - if (err != 0) { - return err; - } - - return (intptr_t) exit_status; + pthread_t thread; + int err; + void *exit_status; + + cli_script = script; + cli_argc = argc; + cli_argv = argv; + + // Start the script in a dedicated thread to prevent conflicts between Go and + // PHP signal handlers + err = pthread_create(&thread, NULL, execute_script_cli, NULL); + if (err != 0) { + return err; + } + + err = pthread_join(thread, &exit_status); + if (err != 0) { + return err; + } + + return (intptr_t)exit_status; } - diff --git a/frankenphp.h b/frankenphp.h index 3c723cdd1..5bbb2dfee 100644 --- a/frankenphp.h +++ b/frankenphp.h @@ -1,9 +1,9 @@ #ifndef _FRANKENPPHP_H #define _FRANKENPPHP_H -#include -#include #include +#include +#include #ifndef FRANKENPHP_VERSION #define FRANKENPHP_VERSION dev @@ -12,43 +12,36 @@ #define TOSTRING(x) STRINGIFY(x) typedef struct frankenphp_version { - unsigned char major_version; - unsigned char minor_version; - unsigned char release_version; - const char *extra_version; - const char *version; - unsigned long version_id; + unsigned char major_version; + unsigned char minor_version; + unsigned char release_version; + const char *extra_version; + const char *version; + unsigned long version_id; } frankenphp_version; frankenphp_version frankenphp_get_version(); typedef struct frankenphp_config { - frankenphp_version version; - bool zts; - bool zend_signals; - bool zend_max_execution_timers; + frankenphp_version version; + bool zts; + bool zend_signals; + bool zend_max_execution_timers; } frankenphp_config; frankenphp_config frankenphp_get_config(); int frankenphp_init(int num_threads); int frankenphp_update_server_context( - bool create, - uintptr_t current_request, - uintptr_t main_request, - - const char *request_method, - char *query_string, - zend_long content_length, - char *path_translated, - char *request_uri, - const char *content_type, - char *auth_user, - char *auth_password, - int proto_num -); + bool create, uintptr_t current_request, uintptr_t main_request, + + const char *request_method, char *query_string, zend_long content_length, + char *path_translated, char *request_uri, const char *content_type, + char *auth_user, char *auth_password, int proto_num); int frankenphp_request_startup(); int frankenphp_execute_script(char *file_name); -void frankenphp_register_bulk_variables(char *known_variables[27], char **dynamic_variables, size_t size, zval *track_vars_array); +void frankenphp_register_bulk_variables(char *known_variables[27], + char **dynamic_variables, size_t size, + zval *track_vars_array); int frankenphp_execute_script_cli(char *script, int argc, char **argv); From ddccdc56ea3693c86500e7355f1c03b19e0915d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 15:44:05 +0100 Subject: [PATCH 4/8] golangcilint --- .github/workflows/tests.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7b517409f..170a022c6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -52,3 +52,8 @@ jobs: name: Run Caddy module tests working-directory: caddy/ run: go test -race -v ./... + - + name: Lint Go code + uses: golangci/golangci-lint-action@v3 + with: + version: latest From 6e03600f37301d6a0a2ce4898807aa70f5c52a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 16:12:04 +0100 Subject: [PATCH 5/8] more fixes --- .github/FUNDING.yml | 1 - .github/workflows/{docker.yml => docker.yaml} | 17 +++--- .github/workflows/{lint.yml => lint.yaml} | 3 + .github/workflows/{static.yml => static.yaml} | 1 + .github/workflows/{tests.yml => tests.yaml} | 1 + .hadolint.yaml | 1 + .markdown-lint.yaml | 2 + CONTRIBUTING.md | 60 ++++++++++++------- docs/docker.md | 8 +-- docs/mercure.md | 2 +- docs/worker.md | 2 +- frankenphp_arginfo.h | 23 ++++--- 12 files changed, 73 insertions(+), 48 deletions(-) delete mode 100644 .github/FUNDING.yml rename .github/workflows/{docker.yml => docker.yaml} (93%) rename .github/workflows/{lint.yml => lint.yaml} (91%) rename .github/workflows/{static.yml => static.yaml} (99%) rename .github/workflows/{tests.yml => tests.yaml} (99%) create mode 100644 .markdown-lint.yaml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 3df6d0442..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: dunglas diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yaml similarity index 93% rename from .github/workflows/docker.yml rename to .github/workflows/docker.yaml index ab752e640..708710c51 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yaml @@ -1,3 +1,4 @@ +--- name: Build Docker images on: pull_request: @@ -33,9 +34,9 @@ jobs: run: | METADATA="$(docker buildx bake --print | jq -c)" { - echo "metadata=$METADATA" - echo "variants=$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< $METADATA)" - echo "platforms=$(jq -c 'first(.target[]) | .platforms' <<< $METADATA)" + echo "metadata="$METADATA"" + echo "variants="$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< "$METADATA")"" + echo "platforms="$(jq -c 'first(.target[]) | .platforms' <<< "$METADATA")"" } >> "$GITHUB_OUTPUT" env: LATEST: '1' # TODO: unset this variable when releasing the first stable version @@ -109,10 +110,10 @@ jobs: run: | mkdir -p /tmp/metadata/builder /tmp/metadata/runner - builderDigest="$(jq -r '."builder-${{matrix.variant}}"."containerimage.digest"' <<< $METADATA)" + builderDigest="$(jq -r '."builder-${{matrix.variant}}"."containerimage.digest"' <<< "$METADATA")" touch "/tmp/metadata/builder/${builderDigest#sha256:}" - runnerDigest="$(jq -r '."runner-${{matrix.variant}}"."containerimage.digest"' <<< $METADATA)" + runnerDigest="$(jq -r '."runner-${{matrix.variant}}"."containerimage.digest"' <<< "$METADATA")" touch "/tmp/metadata/runner/${runnerDigest#sha256:}" env: METADATA: ${{steps.build.outputs.metadata}} @@ -140,7 +141,7 @@ jobs: continue-on-error: ${{fromJson(needs.prepare.outputs.push)}} run: | docker run --platform=${{matrix.platform}} --rm \ - "$(jq -r '."builder-${{matrix.variant}}"."containerimage.config.digest"' <<< $METADATA)" \ + "$(jq -r '."builder-${{matrix.variant}}"."containerimage.config.digest"' <<< "$METADATA")" \ sh -c 'go test ${{matrix.race}} -v ./... && cd caddy && go test ${{matrix.race}} -v ./...' env: METADATA: ${{steps.build.outputs.metadata}} @@ -178,13 +179,13 @@ jobs: name: Create manifest list and push working-directory: /tmp/metadata run: | - docker buildx imagetools create "$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | map("-t " + .) | join(" ")' <<< $METADATA)" \ + docker buildx imagetools create "$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | map("-t " + .) | join(" ")' <<< "$METADATA")" \ "$(printf 'dunglas/frankenphp@sha256:%s ' *)" env: METADATA: ${{needs.prepare.outputs.metadata}} - name: Inspect image run: | - docker buildx imagetools inspect '$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | first' <<< $METADATA)" + docker buildx imagetools inspect '$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | first' <<< "$METADATA")" env: METADATA: ${{needs.prepare.outputs.metadata}} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yaml similarity index 91% rename from .github/workflows/lint.yml rename to .github/workflows/lint.yaml index 2f7eb68dc..37cd22c8a 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yaml @@ -1,3 +1,4 @@ +--- name: Lint Code Base on: pull_request: @@ -32,6 +33,8 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} LINTER_RULES_PATH: / FILTER_REGEX_EXCLUDE: '.*C-Thread-Pool/.*' + MARKDOWN_CONFIG_FILE: .markdown-lint.yaml + VALIDATE_CPP: false VALIDATE_JSCPD: false VALIDATE_GO: false VALIDATE_PHP_PHPCS: false diff --git a/.github/workflows/static.yml b/.github/workflows/static.yaml similarity index 99% rename from .github/workflows/static.yml rename to .github/workflows/static.yaml index 8c532429f..ad115c792 100644 --- a/.github/workflows/static.yml +++ b/.github/workflows/static.yaml @@ -1,3 +1,4 @@ +--- name: Build binary releases on: pull_request: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yaml similarity index 99% rename from .github/workflows/tests.yml rename to .github/workflows/tests.yaml index 170a022c6..8570bbd1d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yaml @@ -1,3 +1,4 @@ +--- name: Tests on: pull_request: diff --git a/.hadolint.yaml b/.hadolint.yaml index bdba91c5a..a33477db5 100644 --- a/.hadolint.yaml +++ b/.hadolint.yaml @@ -1,3 +1,4 @@ +--- ignored: - DL3006 - DL3008 diff --git a/.markdown-lint.yaml b/.markdown-lint.yaml new file mode 100644 index 000000000..436de161c --- /dev/null +++ b/.markdown-lint.yaml @@ -0,0 +1,2 @@ +--- +no-hard-tabs: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a9851e786..db0a02a81 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,11 +5,15 @@ Build the dev Docker image: - docker build -t frankenphp-dev -f dev.Dockerfile . - docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -v $PWD:/go/src/app -it frankenphp-dev +```console +docker build -t frankenphp-dev -f dev.Dockerfile . +docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -p 8080:8080 -p 443:443 -v $PWD:/go/src/app -it frankenphp-dev +``` The image contains the usual development tools (Go, GDB, Valgrind, Neovim...). + If docker version is lower than 23.0, build is failed by dockerignore [pattern issue](https://github.com/moby/moby/pull/42676). Add directories to `.dockerignore`. + ```patch !testdata/*.php !testdata/*.txt @@ -24,65 +28,79 @@ If docker version is lower than 23.0, build is failed by dockerignore [pattern i ## Running the test suite - go test -race -v ./... +```console +go test -race -v ./... +``` ## Caddy module Build Caddy with the FrankenPHP Caddy module: - cd caddy/frankenphp/ - go build - cd ../../ +```console +cd caddy/frankenphp/ +go build +cd ../../ +``` Run the Caddy with the FrankenPHP Caddy module: - cd testdata/ - ../caddy/frankenphp/frankenphp run +```console +cd testdata/ +../caddy/frankenphp/frankenphp run +``` The server is listening on `127.0.0.1:8080`: - curl -vk https://localhost/phpinfo.php +```console +curl -vk https://localhost/phpinfo.php +``` ## Minimal test server Build the minimal test server: - cd internal/testserver/ - go build - cd ../../ +```console +cd internal/testserver/ +go build +cd ../../ +``` Run the test server: - cd testdata/ - ../internal/testserver/testserver +```console +cd testdata/ +../internal/testserver/testserver +``` The server is listening on `127.0.0.1:8080`: - curl -v http://127.0.0.1:8080/phpinfo.php +```console +curl -v http://127.0.0.1:8080/phpinfo.php +``` # Building Docker Images Locally Print bake plan: -``` +```console docker buildx bake -f docker-bake.hcl --print ``` Build FrankenPHP images for amd64 locally: -``` +```console docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/amd64" ``` Build FrankenPHP images for arm64 locally: -``` +```console docker buildx bake -f docker-bake.hcl --pull --load --set "*.platform=linux/arm64" ``` Build FrankenPHP images from scratch for arm64 & amd64 and push to Docker Hub: -``` +```console docker buildx bake -f docker-bake.hcl --pull --no-cache --push ``` @@ -119,7 +137,7 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push ``` 7. Download the module: `go get` 8. In the container, you can use GDB and the like: - ```sh + ```console go test -c -ldflags=-w gdb --args ./frankenphp.test -test.run ^MyTest$ ``` @@ -145,7 +163,7 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push ## Useful Command -``` +```console apk add strace util-linux gdb strace -e 'trace=!futex,epoll_ctl,epoll_pwait,tgkill,rt_sigreturn' -p 1 ``` diff --git a/docs/docker.md b/docs/docker.md index 3baa4c8dc..f91d07d29 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -15,8 +15,8 @@ COPY . /app/public Then, run the commands to build and run the Docker image: ```console -$ docker build -t my-php-app . -$ docker run -it --rm --name my-running-app my-php-app +docker build -t my-php-app . +docker run -it --rm --name my-running-app my-php-app ``` ## How to Install More PHP Extensions @@ -70,7 +70,7 @@ COPY --from=builder /usr/local/bin/frankenphp /usr/local/bin/frankenphp The `builder` image provided by FrankenPHP contains a compiled version of libphp. [Builders images](https://hub.docker.com/r/dunglas/frankenphp/tags?name=builder) are provided for all versions of FrankenPHP and PHP, both for Alpine and Debian. -# Enabling the Worker Mode by Default +## Enabling the Worker Mode by Default Set the `FRANKENPHP_CONFIG` environment variable to start FrankenPHP with a worker script: @@ -82,7 +82,7 @@ FROM dunglas/frankenphp ENV FRANKENPHP_CONFIG="worker ./public/index.php" ``` -# Using a Volume in Development +## Using a Volume in Development To develop easily with FrankenPHP, mount the directory from your host containing the source code of the app as a volume in the Docker container: diff --git a/docs/mercure.md b/docs/mercure.md index 0a2bf597d..a3dd4e8d3 100644 --- a/docs/mercure.md +++ b/docs/mercure.md @@ -7,6 +7,6 @@ No JS library or SDK required! ![Mercure](https://mercure.rocks/static/main.png) -To enable the Mercure hub, update the `Caddyfile` as described [on Mercure's website](https://mercure.rocks/docs/hub/config). +To enable the Mercure hub, update the `Caddyfile` as described [on Mercure's site](https://mercure.rocks/docs/hub/config). To push Mercure updates from your code, we recommend the [Symfony Mercure Component](https://symfony.com/components/Mercure) (you don't need the Symfony full stack framework to use it). diff --git a/docs/worker.md b/docs/worker.md index 65cd7113d..18ab97542 100644 --- a/docs/worker.md +++ b/docs/worker.md @@ -64,7 +64,7 @@ do { $myApp->shutdown(); ``` -Then, start your app and use the `FRANKENPHP_CONFIG` environment variable to configure your worker: +Then, start your app and use the `FRANKENPHP_CONFIG` environment variable to configure your worker: ```console docker run \ diff --git a/frankenphp_arginfo.h b/frankenphp_arginfo.h index a9fcc7ab5..df1e7f408 100644 --- a/frankenphp_arginfo.h +++ b/frankenphp_arginfo.h @@ -1,29 +1,28 @@ /* This is a generated file, edit the .stub.php file instead. * Stub hash: de4dc4063fafd8c933e3068c8349889a7ece5f03 */ -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_handle_request, 0, 1, _IS_BOOL, 0) - ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_handle_request, 0, 1, + _IS_BOOL, 0) +ZEND_ARG_TYPE_INFO(0, callback, IS_CALLABLE, 0) ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_headers_send, 0, 0, IS_LONG, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, status, IS_LONG, 0, "200") +ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, status, IS_LONG, 0, "200") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_finish_request, 0, 0, _IS_BOOL, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_frankenphp_finish_request, 0, 0, + _IS_BOOL, 0) ZEND_END_ARG_INFO() #define arginfo_fastcgi_finish_request arginfo_frankenphp_finish_request - ZEND_FUNCTION(frankenphp_handle_request); ZEND_FUNCTION(headers_send); ZEND_FUNCTION(frankenphp_finish_request); - static const zend_function_entry ext_functions[] = { - ZEND_FE(frankenphp_handle_request, arginfo_frankenphp_handle_request) - ZEND_FE(headers_send, arginfo_headers_send) - ZEND_FE(frankenphp_finish_request, arginfo_frankenphp_finish_request) - ZEND_FALIAS(fastcgi_finish_request, frankenphp_finish_request, arginfo_fastcgi_finish_request) - ZEND_FE_END -}; + ZEND_FE(frankenphp_handle_request, arginfo_frankenphp_handle_request) + ZEND_FE(headers_send, arginfo_headers_send) ZEND_FE( + frankenphp_finish_request, arginfo_frankenphp_finish_request) + ZEND_FALIAS(fastcgi_finish_request, frankenphp_finish_request, + arginfo_fastcgi_finish_request) ZEND_FE_END}; From 0d987a4d0e2de30090b8cb95b02c0be56e490a5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 16:14:58 +0100 Subject: [PATCH 6/8] fix build-static.sh --- build-static.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-static.sh b/build-static.sh index ee6ff8a1c..2d2c992fe 100755 --- a/build-static.sh +++ b/build-static.sh @@ -38,7 +38,7 @@ elif [ -d ".git/" ]; then CURRENT_REF="$(git rev-parse --abbrev-ref HEAD)" export CURRENT_REF - if echo "$FRANKENPHP_VERSION" | grep -q "."; then + if echo "$FRANKENPHP_VERSION" | grep -F -q "."; then # Tag git checkout "v$FRANKENPHP_VERSION" else From 3dca62996993856779b71719699d686ecc3bc745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 16:43:54 +0100 Subject: [PATCH 7/8] more fixes --- .github/workflows/docker.yaml | 9 ++++----- .markdown-lint.yaml | 2 ++ CONTRIBUTING.md | 3 ++- README.md | 2 +- docs/compile.md | 2 +- docs/known-issues.md | 1 - 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 708710c51..1bbdc2ba5 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -34,9 +34,9 @@ jobs: run: | METADATA="$(docker buildx bake --print | jq -c)" { - echo "metadata="$METADATA"" - echo "variants="$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< "$METADATA")"" - echo "platforms="$(jq -c 'first(.target[]) | .platforms' <<< "$METADATA")"" + echo metadata="$METADATA" + echo variants="$(jq -c '.group.default.targets|map(sub("runner-|builder-"; ""))|unique' <<< "$METADATA")" + echo platforms="$(jq -c 'first(.target[]) | .platforms' <<< "$METADATA")" } >> "$GITHUB_OUTPUT" env: LATEST: '1' # TODO: unset this variable when releasing the first stable version @@ -185,7 +185,6 @@ jobs: METADATA: ${{needs.prepare.outputs.metadata}} - name: Inspect image - run: | - docker buildx imagetools inspect '$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | first' <<< "$METADATA")" + run: docker buildx imagetools inspect "$(jq -cr '.target."${{matrix.target}}-${{matrix.variant}}".tags | first' <<< "$METADATA")" env: METADATA: ${{needs.prepare.outputs.metadata}} diff --git a/.markdown-lint.yaml b/.markdown-lint.yaml index 436de161c..0fe7cd835 100644 --- a/.markdown-lint.yaml +++ b/.markdown-lint.yaml @@ -1,2 +1,4 @@ --- no-hard-tabs: false +MD013: false +MD033: false diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index db0a02a81..5aa34488a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,7 @@ # Contributing ## Compiling PHP + ### With Docker (Linux) Build the dev Docker image: @@ -78,7 +79,7 @@ The server is listening on `127.0.0.1:8080`: curl -v http://127.0.0.1:8080/phpinfo.php ``` -# Building Docker Images Locally +## Building Docker Images Locally Print bake plan: diff --git a/README.md b/README.md index e697527ca..24c221c66 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ FrankenPHP is a modern application server for PHP built on top of the [Caddy](ht FrankenPHP gives superpowers to your PHP apps thanks to its stunning features: [*Early Hints*](docs/early-hints.md), [worker mode](docs/worker.md), [real-time capabilities](docs/mercure.md), automatic HTTPS, HTTP/2, and HTTP/3 support... -FrankenPHP works with any PHP app and makes your Symfony projects faster than ever thanks to provided integration with the worker mode (Laravel Octane support coming). +FrankenPHP works with any PHP app and makes your Symfony projects faster than ever thanks to the provided integration with the worker mode (Laravel Octane support coming). FrankenPHP can also be used as a standalone Go library to embed PHP in any app using `net/http`. diff --git a/docs/compile.md b/docs/compile.md index b802cba2c..53cf02274 100644 --- a/docs/compile.md +++ b/docs/compile.md @@ -89,4 +89,4 @@ CGO_ENABLED=1 xcaddy build \ --with github.com/dunglas/mercure/caddy \ --with github.com/dunglas/vulcain/caddy # Add extra Caddy modules here -``` \ No newline at end of file +``` diff --git a/docs/known-issues.md b/docs/known-issues.md index 3f6178652..ade7d69c5 100644 --- a/docs/known-issues.md +++ b/docs/known-issues.md @@ -6,7 +6,6 @@ Calling PHP functions and language constructs that themselves call [cgo](https:/ This issue [is being worked on by the Go project](https://github.com/golang/go/issues/62130). - In the meantime, one solution is not to use constructs (like `echo`) and functions (like `header()`) that delegate to Go from inside Fibers. This code will likely crash because it uses `echo` in the Fiber: From 13fc661fcaa68476db61f000fe2eb95394149815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Fri, 1 Dec 2023 16:51:00 +0100 Subject: [PATCH 8/8] more fixes --- CONTRIBUTING.md | 9 ++++++++- docs/laravel.md | 30 ++++++++++++++++-------------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5aa34488a..c0c265e31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -109,6 +109,7 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push 1. Open `.github/workflows/tests.yml` 2. Enable PHP debug symbols + ```patch - uses: shivammathur/setup-php@v2 # ... @@ -116,7 +117,9 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push phpts: ts + debug: true ``` + 3. Enable `tmate` to connect to the container + ```patch - name: Set CGO flags @@ -129,19 +132,24 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push + - + uses: mxschmitt/action-tmate@v3 ``` + 4. Connect to the container 5. Open `frankenphp.go` 6. Enable `cgosymbolizer` + ```patch - //_ "github.com/ianlancetaylor/cgosymbolizer" + _ "github.com/ianlancetaylor/cgosymbolizer" ``` + 7. Download the module: `go get` 8. In the container, you can use GDB and the like: + ```console go test -c -ldflags=-w gdb --args ./frankenphp.test -test.run ^MyTest$ ``` + 9. When the bug is fixed, revert all these changes ## Misc Dev Resources @@ -161,7 +169,6 @@ docker buildx bake -f docker-bake.hcl --pull --no-cache --push * [Bake file definition](https://docs.docker.com/build/customize/bake/file-definition/) * [docker buildx build](https://docs.docker.com/engine/reference/commandline/buildx_build/) - ## Useful Command ```console diff --git a/docs/laravel.md b/docs/laravel.md index b015ba9a0..b1b31860d 100644 --- a/docs/laravel.md +++ b/docs/laravel.md @@ -18,20 +18,22 @@ Alternatively, you can run your Laravel projects with FrankenPHP from your local 1. [Download the binary corresponding to your system](https://github.com/dunglas/frankenphp/releases) 2. Add the following configuration to a file named `Caddyfile` in the root directory of your Laravel project: -```caddyfile -{ - frankenphp - order php_server before file_server -} - -# The domain name of your server -localhost { - # Enable compression (optional) - encode zstd gzip - # Execute PHP files in the current directory and serve assets - php_server -} -``` + + ```caddyfile + { + frankenphp + order php_server before file_server + } + + # The domain name of your server + localhost { + # Enable compression (optional) + encode zstd gzip + # Execute PHP files in the current directory and serve assets + php_server + } + ``` + 3. Start FrankenPHP from the root directory of your Laravel project: `./frankenphp run` ## Laravel Octane