Skip to content

Commit

Permalink
feat: embed PHP apps into the FrankenPHP binary
Browse files Browse the repository at this point in the history
  • Loading branch information
dunglas committed Dec 2, 2023
1 parent bb931c9 commit 6509cdd
Show file tree
Hide file tree
Showing 13 changed files with 227 additions and 40 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@
!testdata/*.php
!testdata/*.txt
!build-static.sh
!embed/*
2 changes: 1 addition & 1 deletion .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
-
name: Build
id: build
uses: docker/bake-action@v3
uses: docker/bake-action@v4
with:
pull: true
load: ${{!fromJson(needs.prepare.outputs.push)}}
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/static.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,30 @@ jobs:
uses: docker/setup-buildx-action@v3
with:
version: latest
-
name: Login to DockerHub
if: ${{toJson(startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request'))}}
uses: docker/login-action@v3
with:
username: ${{secrets.REGISTRY_USERNAME}}
password: ${{secrets.REGISTRY_PASSWORD}}
-
name: Build
id: build
uses: docker/bake-action@v3
uses: docker/bake-action@v4
with:
pull: true
load: true
push: ${{toJson(startsWith(github.ref, 'refs/tags/') || (github.ref == 'refs/heads/main' && github.event_name != 'pull_request'))}}
targets: static-builder
set: |
*.cache-from=type=gha,scope=${{github.ref}}-static-builder
*.cache-from=type=gha,scope=refs/heads/main-static-builder
*.cache-to=type=gha,scope=${{github.ref}}-static-builder
env:
VERSION: ${{github.ref_type == 'tag' && github.ref_name || github.sha}}
LATEST: '1' # TODO: unset this variable when releasing the first stable version
SHA: ${{github.sha}}
VERSION: ${{github.ref_type == 'tag' && github.ref_name || github.sha}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
name: Copy binary
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ WORKDIR /go/src/app
COPY --link *.* ./
COPY --link caddy caddy
COPY --link C-Thread-Pool C-Thread-Pool
COPY --link embed embed
COPY --link internal internal
COPY --link testdata testdata

Expand Down
1 change: 1 addition & 0 deletions alpine.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ WORKDIR /go/src/app
COPY --link *.* ./
COPY --link caddy caddy
COPY --link C-Thread-Pool C-Thread-Pool
COPY --link embed embed
COPY --link internal internal
COPY --link testdata testdata

Expand Down
74 changes: 48 additions & 26 deletions build-static.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/bin/sh

set -o errexit
set -o xtrace

if ! type "git" > /dev/null; then
echo "The \"git\" command must be installed."
Expand Down Expand Up @@ -48,37 +47,47 @@ fi

bin="frankenphp-$os-$arch"

mkdir -p dist/
cd dist/
if [ "$CLEAN" ]; then
rm -Rf dist/
go clean -cache
fi

if [ -d "static-php-cli/" ]; then
cd static-php-cli/
git pull
# Build libphp if ncessary
if [ -f "dist/static-php-cli/buildroot/lib/libphp.a" ]; then
cd dist/static-php-cli
else
git clone --depth 1 https://github.com/crazywhalecc/static-php-cli
cd static-php-cli/
fi
mkdir -p dist/
cd dist/

composer install --no-dev -a
if [ -d "static-php-cli/" ]; then
cd static-php-cli/
git pull
else
git clone --depth 1 https://github.com/crazywhalecc/static-php-cli
cd static-php-cli/
fi

if type "brew" > /dev/null; then
packages="composer"
if [ "$RELEASE" ]; then
packages="$packages gh"
fi

if type "brew" > /dev/null; then
packages="composer"
if [ "$RELEASE" ]; then
packages="$packages gh"
brew install --formula --quiet "$packages"
fi

brew install --formula --quiet "$packages"
fi
composer install --no-dev -a

if [ "$os" = "linux" ]; then
extraOpts="--disable-opcache-jit"
fi

if [ "$os" = "linux" ]; then
extraOpts="--disable-opcache-jit"
./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"
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
Expand All @@ -92,15 +101,28 @@ export CGO_LDFLAGS
LIBPHP_VERSION="$(./buildroot/bin/php-config --version)"
export LIBPHP_VERSION
cd ../../caddy/frankenphp/
cd ../..
# Embed PHP app, if any
if [ -d "$EMBED" ]; then
mv embed embed.bak
cp -R "$EMBED" embed
fi
cd caddy/frankenphp/
go env
go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags -static-pie -w -s -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION PHP $LIBPHP_VERSION Caddy'" -o "../../dist/$bin"
cd ../..
if [ -d "$EMBED" ]; then
rm -Rf embed
mv embed.bak embed
fi
cd ../../dist/
"./$bin" version
"dist/$bin" version
if [ "$RELEASE" ]; then
gh release upload "$FRANKENPHP_VERSION" "$bin" --repo dunglas/frankenphp --clobber
gh release upload "$FRANKENPHP_VERSION" "dist/$bin" --repo dunglas/frankenphp --clobber
fi
if [ "$CURRENT_REF" ]; then
Expand Down
34 changes: 30 additions & 4 deletions caddy/caddy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/json"
"errors"
"net/http"
"path/filepath"
"strconv"

"github.com/caddyserver/caddy/v2"
Expand All @@ -20,6 +21,8 @@ import (
"go.uber.org/zap"
)

const defaultDocumentRoot = "public"

func init() {
caddy.RegisterModule(FrankenPHPApp{})
caddy.RegisterModule(FrankenPHPModule{})
Expand Down Expand Up @@ -93,8 +96,6 @@ func (f *FrankenPHPApp) Start() error {
}
}

logger.Info("FrankenPHP started 🐘", zap.String("php_version", frankenphp.Version().Version))

return nil
}

Expand Down Expand Up @@ -169,6 +170,10 @@ func (f *FrankenPHPApp) UnmarshalCaddyfile(d *caddyfile.Dispenser) error {
if wc.FileName == "" {
return errors.New(`The "file" argument must be specified`)
}

if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(wc.FileName) {
wc.FileName = filepath.Join(frankenphp.EmbeddedAppPath, wc.FileName)
}
}

f.Workers = append(f.Workers, wc)
Expand All @@ -193,7 +198,7 @@ func parseGlobalOption(d *caddyfile.Dispenser, _ interface{}) (interface{}, erro
}

type FrankenPHPModule struct {
// Root sets the root folder to the site. Default: `root` directive.
// Root sets the root folder to the site. Default: `root` directive, or the path of the public directory of the embed app it exists.
Root string `json:"root,omitempty"`
// SplitPath sets the substrings for splitting the URI into two parts. The first matching substring will be used to split the "path info" from the path. The first piece is suffixed with the matching substring and will be assumed as the actual resource (CGI script) name. The second piece will be set to PATH_INFO for the CGI script to use. Default: `.php`.
SplitPath []string `json:"split_path,omitempty"`
Expand All @@ -217,8 +222,18 @@ func (f *FrankenPHPModule) Provision(ctx caddy.Context) error {
f.logger = ctx.Logger(f)

if f.Root == "" {
f.Root = "{http.vars.root}"
if frankenphp.EmbeddedAppPath == "" {
f.Root = "{http.vars.root}"
} else {
f.Root = filepath.Join(frankenphp.EmbeddedAppPath, defaultDocumentRoot)
f.ResolveRootSymlink = false
}
} else {
if frankenphp.EmbeddedAppPath != "" && filepath.IsLocal(f.Root) {
f.Root = filepath.Join(frankenphp.EmbeddedAppPath, f.Root)
}
}

if len(f.SplitPath) == 0 {
f.SplitPath = []string{".php"}
}
Expand Down Expand Up @@ -425,6 +440,17 @@ func parsePhpServer(h httpcaddyfile.Helper) ([]httpcaddyfile.ConfigValue, error)
// unmarshaler can read it from the start
dispenser.Reset()

if frankenphp.EmbeddedAppPath != "" {
if phpsrv.Root == "" {
phpsrv.Root = filepath.Join(frankenphp.EmbeddedAppPath, defaultDocumentRoot)
fsrv.Root = phpsrv.Root
phpsrv.ResolveRootSymlink = false
} else if filepath.IsLocal(fsrv.Root) {
phpsrv.Root = filepath.Join(frankenphp.EmbeddedAppPath, phpsrv.Root)
fsrv.Root = phpsrv.Root
}
}

// set up a route list that we'll append to
routes := caddyhttp.RouteList{}

Expand Down
10 changes: 10 additions & 0 deletions caddy/php-server.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"log"
"net/http"
"path/filepath"
"strconv"
"time"

Expand All @@ -15,6 +16,7 @@ import (
"github.com/caddyserver/caddy/v2/modules/caddyhttp/fileserver"
"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
"github.com/caddyserver/certmagic"
"github.com/dunglas/frankenphp"
"go.uber.org/zap"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -60,6 +62,14 @@ func cmdPHPServer(fs caddycmd.Flags) (int, error) {
debug := fs.Bool("debug")
compress := !fs.Bool("no-compress")

if frankenphp.EmbeddedAppPath != "" {
if root == "" {
root = filepath.Join(frankenphp.EmbeddedAppPath, defaultDocumentRoot)
} else if filepath.IsLocal(root) {
root = filepath.Join(frankenphp.EmbeddedAppPath, root)
}
}

const indexFile = "index.php"
extensions := []string{"php"}
tryFiles := []string{"{http.request.uri.path}", "{http.request.uri.path}/" + indexFile, indexFile}
Expand Down
17 changes: 14 additions & 3 deletions docs/static.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_s
# ...
```

See [the list of supported extensions](https://static-php.dev/en/guide/extensions.html).

To add libraries enabling additional functionality to the extensions you've enabled, you can pass use the `PHP_EXTENSION_LIBS` Docker ARG:

```console
Expand All @@ -43,6 +41,8 @@ docker buildx bake \
static-builder
```

See also: [customizing the build](#customizing-the-build)

### GitHub Token

If you hit the GitHub API rate limit, set a GitHub Personal Access Token in an environment variable named `GITHUB_TOKEN`:
Expand All @@ -64,4 +64,15 @@ cd frankenphp

Note: this script also works on Linux (and probably on other Unixes), and is used internally by the Docker based static builder we provide.

See [the list of supported extensions](https://static-php.dev/en/guide/extensions.html).
## Customizing The Build

The following environment variables can be passed to `docker build` and to the `build-static.sh`
script to customize the static build:

* `FRANKENPHP_VERSION`: the version of FrankenPHP to use
* `PHP_VERSION`: the version of PHP to use
* `PHP_EXTENSIONS`: the PHP extensions to build ([list of supported extensions](https://static-php.dev/en/guide/extensions.html))
* `PHP_EXTENSION_LIBS`: extra libraries to build that add extra features to the extensions
* `EMBED`: path of the PHP application to embed in the binary
* `CLEAN`: when set, libphp and all its dependencies are built from scratch (no cache)
* `RELEASE`: (maintainers only) when set, the resulting binary will be uploaded on GitHub
Loading

0 comments on commit 6509cdd

Please sign in to comment.