diff --git a/.github/workflows/static.yaml b/.github/workflows/static.yaml index 43e111943..3e4c2f265 100644 --- a/.github/workflows/static.yaml +++ b/.github/workflows/static.yaml @@ -297,7 +297,6 @@ jobs: targets: static-builder-gnu set: | ${{ (github.event_name == 'pull_request' || matrix.platform == 'linux/arm64') && 'static-builder-gnu.args.NO_COMPRESS=1' || '' }} - static-builder-gnu.args.BUILD_PACKAGES=1 *.tags= *.platform=${{ matrix.platform }} *.cache-from=type=gha,scope=${{ needs.prepare.outputs.ref || github.ref }}-static-builder-gnu diff --git a/README.md b/README.md index 79fd7c38c..7f4ebbe2a 100644 --- a/README.md +++ b/README.md @@ -16,49 +16,69 @@ FrankenPHP can also be used as a standalone Go library to embed PHP in any app u ## Getting Started -### Standalone Binary - -We provide static FrankenPHP binaries for Linux and macOS -containing [PHP 8.4](https://www.php.net/releases/8.4/en.php) and most popular PHP extensions. - On Windows, use [WSL](https://learn.microsoft.com/windows/wsl/) to run FrankenPHP. -[Download FrankenPHP](https://github.com/php/frankenphp/releases) or copy this line into your -terminal to automatically install the version appropriate for your platform: +### Install Script + +You can copy this line into your terminal to automatically +install an appropriate version for your platform: ```console curl https://frankenphp.dev/install.sh | sh mv frankenphp /usr/local/bin/ ``` -To serve the content of the current directory, run: +### Standalone Binary + +We provide static FrankenPHP binaries for development purposes on Linux and macOS +containing [PHP 8.4](https://www.php.net/releases/8.4/en.php) and most popular PHP extensions. + +[Download FrankenPHP](https://github.com/php/frankenphp/releases) + +### rpm Packages + +Our maintainers offer rpm packages for all systems using `dnf`. To install, run: ```console -frankenphp php-server +sudo dnf install https://rpm.henderkes.com/static-php-1-0.noarch.rpm +sudo dnf module enable php-zts:static-8.4 # 8.2-8.5 available +sudo dnf install frankenphp +# to install extensions: +sudo dnf install php-zts-xdebug +# if an extension is not available by default, install it with pie +sudo dnf install php-zts-devel +sudo pie install asgrim/example-pie-extension --with-php-config=php-config-zts ``` -You can also run command-line scripts with: +### deb Packages + +Our maintainers offer deb packages for all systems using `apt`. To install, run: ```console -frankenphp php-cli /path/to/your/script.php +sudo curl -fsSL https://key.henderkes.com/static-php.gpg -o /usr/share/keyrings/static-php.gpg && \ +echo "deb [signed-by=/usr/share/keyrings/static-php.gpg] https://deb.henderkes.com/ stable main" | sudo tee /etc/apt/sources.list.d/static-php.list && \ +sudo apt update +sudo apt install frankenphp +# to install extensions: +sudo apt install php-zts-xdebug +# if an extension is not available by default, install it with pie +sudo apt install php-zts-devel +sudo pie install asgrim/example-pie-extension --with-php-config=php-config-zts ``` -### Docker +### Usage -Alternatively, [Docker images](https://frankenphp.dev/docs/docker/) are available: +To serve the content of the current directory, run: ```console -docker run -v .:/app/public \ - -p 80:80 -p 443:443 -p 443:443/udp \ - dunglas/frankenphp +frankenphp php-server ``` -Go to `https://localhost`, and enjoy! +You can also run command-line scripts with: -> [!TIP] -> -> Do not attempt to use `https://127.0.0.1`. Use `https://localhost` and accept the self-signed certificate. -> Use the [`SERVER_NAME` environment variable](docs/config.md#environment-variables) to change the domain to use. +```console +frankenphp php-cli /path/to/your/script.php +``` ### Homebrew @@ -76,6 +96,25 @@ To serve the content of the current directory, run: frankenphp php-server ``` +If you need extensions, you will have to install them with [pie](https://github.com/php/pie). + +### Docker + +Alternatively, [Docker images](https://frankenphp.dev/docs/docker/) are available: + +```console +docker run -v .:/app/public \ + -p 80:80 -p 443:443 -p 443:443/udp \ + dunglas/frankenphp +``` + +Go to `https://localhost`, and enjoy! + +> [!TIP] +> +> Do not attempt to use `https://127.0.0.1`. Use `https://localhost` and accept the self-signed certificate. +> Use the [`SERVER_NAME` environment variable](docs/config.md#environment-variables) to change the domain to use. + ## Docs - [Classic mode](https://frankenphp.dev/docs/classic/) diff --git a/build-packages.sh b/build-packages.sh deleted file mode 100755 index 1467a04cb..000000000 --- a/build-packages.sh +++ /dev/null @@ -1,119 +0,0 @@ -#!/bin/bash - -set -o errexit -set -x - -# Ensure required tools are installed -if ! command -v rpmbuild &>/dev/null; then - echo "Error: rpm-build is required to create RPM packages." - echo "Install it with: sudo dnf install rpm-build" - exit 1 -fi - -if ! command -v ruby &>/dev/null; then - echo "Error: Ruby is required by FPM." - echo "Install it with: sudo dnf install ruby" - exit 1 -fi - -if ! command -v fpm &>/dev/null; then - echo "Error: FPM (rubygem-fpm) is required to create RPM packages." - echo "Install it with: sudo gem install fpm" - exit 1 -fi - -arch="$(uname -m)" -os="$(uname -s | tr '[:upper:]' '[:lower:]')" -bin="frankenphp-${os}-${arch}" - -if [ ! -f "dist/$bin" ]; then - echo "Error: dist/$bin not found. Run './build-static.sh' first" - exit 1 -fi - -version_output="$(dist/"$bin" version)" -frankenphp_version=$(echo "$version_output" | grep -oP 'FrankenPHP\s+\K[^ ]+' || true) -frankenphp_version=${frankenphp_version#v} - -if [[ ! "${frankenphp_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Warning: frankenphp_version must be set to X.Y.Z (e.g. 1.5.1), got '${frankenphp_version}'" - echo "Falling back to non-release version 0.0.0" - frankenphp_version=0.0.0 -fi - -group_preexists=0 -user_preexists=0 - -if getent group frankenphp >/dev/null; then - group_preexists=1 -else - sudo groupadd --system frankenphp -fi - -if getent passwd frankenphp >/dev/null; then - user_preexists=1 -else - sudo useradd --system \ - --gid frankenphp \ - --create-home \ - --home-dir /var/lib/frankenphp \ - --shell /usr/sbin/nologin \ - --comment "FrankenPHP web server" \ - frankenphp -fi - -mkdir -p package/empty -mkdir -p package/etc -[ -f ./dist/static-php-cli/source/php-src/php.ini-production ] && cp -f ./dist/static-php-cli/source/php-src/php.ini-production ./package/etc/php.ini - -cd dist -iteration=1 -glibc_version=$(ldd -v "$bin" | awk '/GLIBC_/ {gsub(/[()]/, "", $2); print $2}' | grep -v GLIBC_PRIVATE | sort -V | tail -n1) -cxxabi_version=$(strings "$bin" | grep -oP 'CXXABI_\d+\.\d+(\.\d+)?' | sort -V | tail -n1) - -fpm -s dir -t rpm -n frankenphp -v "${frankenphp_version}" \ - --config-files /etc/frankenphp/Caddyfile \ - --config-files /etc/frankenphp/php.ini \ - --depends "libc.so.6(${glibc_version})(64bit)" \ - --depends "libstdc++.so.6(${cxxabi_version})(64bit)" \ - --before-install ../package/rhel/preinstall.sh \ - --after-install ../package/rhel/postinstall.sh \ - --before-remove ../package/rhel/preuninstall.sh \ - --after-remove ../package/rhel/postuninstall.sh \ - --iteration "${iteration}" \ - --rpm-user frankenphp --rpm-group frankenphp \ - "${bin}=/usr/bin/frankenphp" \ - "../package/rhel/frankenphp.service=/usr/lib/systemd/system/frankenphp.service" \ - "../package/Caddyfile=/etc/frankenphp/Caddyfile" \ - "../package/content/=/usr/share/frankenphp" \ - "../package/etc/php.ini=/etc/frankenphp/php.ini" \ - "../package/empty/=/etc/frankenphp/php.d" \ - "../package/empty/=/usr/lib/frankenphp/modules" \ - "../package/empty/=/var/lib/frankenphp" - -glibc_version=$(ldd -v "$bin" | awk '/GLIBC_/ {gsub(/[()]/, "", $2); print $2}' | grep -v GLIBC_PRIVATE | sed 's/GLIBC_//' | sort -V | tail -n1) -cxxabi_version=$(strings "$bin" | grep -oP 'CXXABI_\d+\.\d+(\.\d+)?' | sed 's/CXXABI_//' | sort -V | tail -n1) - -fpm -s dir -t deb -n frankenphp -v "${frankenphp_version}" \ - --config-files /etc/frankenphp/Caddyfile \ - --config-files /etc/frankenphp/php.ini \ - --depends "libc6 (>= ${glibc_version})" \ - --depends "libstdc++6 (>= ${cxxabi_version})" \ - --after-install ../package/debian/postinst.sh \ - --before-remove ../package/debian/prerm.sh \ - --after-remove ../package/debian/postrm.sh \ - --iteration "${iteration}" \ - --deb-user frankenphp --deb-group frankenphp \ - "${bin}=/usr/bin/frankenphp" \ - "../package/debian/frankenphp.service=/usr/lib/systemd/system/frankenphp.service" \ - "../package/Caddyfile=/etc/frankenphp/Caddyfile" \ - "../package/content/=/usr/share/frankenphp" \ - "../package/etc/php.ini=/etc/frankenphp/php.ini" \ - "../package/empty/=/etc/frankenphp/php.d" \ - "../package/empty/=/usr/lib/frankenphp/modules" \ - "../package/empty/=/var/lib/frankenphp" - -[ "$user_preexists" -eq 0 ] && sudo userdel frankenphp -[ "$group_preexists" -eq 0 ] && (sudo groupdel frankenphp || true) - -cd .. diff --git a/build-static.sh b/build-static.sh index a3c731a52..1c24cf723 100755 --- a/build-static.sh +++ b/build-static.sh @@ -203,8 +203,9 @@ for pkg in ${SPC_OPT_INSTALL_ARGS}; do done # shellcheck disable=SC2086 ${spcCommand} download --with-php="${PHP_VERSION}" --for-extensions="${PHP_EXTENSIONS}" --for-libs="${PHP_EXTENSION_LIBS}" ${SPC_OPT_DOWNLOAD_ARGS} +export FRANKENPHP_SOURCE_PATH="${CURRENT_DIR}" # shellcheck disable=SC2086 -FRANKENPHP_SOURCE_DIR=${CURRENT_DIR} ${spcCommand} build --enable-zts --build-embed --build-frankenphp ${SPC_OPT_BUILD_ARGS} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" +${spcCommand} build --enable-zts --build-embed --build-frankenphp ${SPC_OPT_BUILD_ARGS} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}" if [ -n "$CI" ]; then rm -rf ./downloads diff --git a/docs/performance.md b/docs/performance.md index 469f0d28c..7910b2651 100644 --- a/docs/performance.md +++ b/docs/performance.md @@ -41,11 +41,9 @@ especially when compiled in ZTS mode (thread-safe), which is required for Franke Also, [some bugs only happen when using musl](https://github.com/php/php-src/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen+label%3ABug+musl). -In production environments, we recommend using FrankenPHP linked against glibc. +In production environments, we recommend using FrankenPHP linked against glibc, compiled with an appropriate optimization level. -This can be achieved by using the Debian Docker images (the default), downloading the -gnu suffix binary from our [Releases](https://github.com/php/frankenphp/releases), or by [compiling FrankenPHP from sources](compile.md). - -Alternatively, we provide static musl binaries compiled with [the mimalloc allocator](https://github.com/microsoft/mimalloc), which alleviates the problems in threaded scenarios. +This can be achieved by using the Debian Docker images, using our maintainers [.deb](https://debs.henderkes.com) or [.rpm](https://rpms.henderkes.com) packages, or by [compiling FrankenPHP from sources](compile.md). ## Go Runtime Configuration diff --git a/install.sh b/install.sh index 4ec9a2219..d50defd8a 100755 --- a/install.sh +++ b/install.sh @@ -2,6 +2,11 @@ set -e +SUDO="" +if [ "$(id -u)" -ne 0 ]; then + SUDO="sudo" +fi + if [ -z "${BIN_DIR}" ]; then BIN_DIR=$(pwd) fi @@ -13,6 +18,11 @@ OS=$(uname -s) ARCH=$(uname -m) GNU="" +if ! command -v curl >/dev/null 2>&1; then + echo "Please install curl to download FrankenPHP" + exit 1 +fi + if type "tput" >/dev/null 2>&1; then bold=$(tput bold || true) italic=$(tput sitm || true) @@ -21,6 +31,46 @@ fi case ${OS} in Linux*) + if [ "${ARCH}" = "aarch64" ] || [ "${ARCH}" = "x86_64" ]; then + if command -v dnf >/dev/null 2>&1; then + echo "📦 Detected dnf. Installing FrankenPHP from RPM repository..." + if [ -n "${SUDO}" ]; then + echo "❗ Enter your password to grant sudo powers for package installation" + ${SUDO} -v || true + fi + ${SUDO} dnf -y install https://rpm.henderkes.com/static-php-1-0.noarch.rpm + ${SUDO} dnf -y module enable php-zts:static-8.4 || true + ${SUDO} dnf -y install frankenphp + echo + echo "🥳 FrankenPHP installed successfully" + echo + echo "⭐ If you like FrankenPHP, please give it a star on GitHub: ${italic}https://github.com/php/frankenphp${normal}" + exit 0 + fi + + if command -v apt >/dev/null 2>&1 || command -v apt-get >/dev/null 2>&1; then + echo "📦 Detected apt. Installing FrankenPHP from DEB repository..." + if [ -n "${SUDO}" ]; then + echo "❗ Enter your password to grant sudo powers for package installation" + ${SUDO} -v || true + fi + ${SUDO} sh -c 'curl -fsSL https://key.henderkes.com/static-php.gpg -o /usr/share/keyrings/static-php.gpg' + ${SUDO} sh -c 'echo "deb [signed-by=/usr/share/keyrings/static-php.gpg] https://deb.henderkes.com/ stable main" > /etc/apt/sources.list.d/static-php.list' + if command -v apt >/dev/null 2>&1; then + ${SUDO} apt update + ${SUDO} apt -y install frankenphp + else + ${SUDO} apt-get update + ${SUDO} apt-get -y install frankenphp + fi + echo + echo "🥳 FrankenPHP installed successfully." + echo + echo "⭐ If you like FrankenPHP, please give it a star on GitHub: ${italic}https://github.com/php/frankenphp${normal}" + exit 0 + fi + fi + case ${ARCH} in aarch64) THE_ARCH_BIN="frankenphp-linux-aarch64" @@ -63,8 +113,6 @@ if [ -z "${THE_ARCH_BIN}" ]; then exit 1 fi -SUDO="" - echo "📦 Downloading ${bold}FrankenPHP${normal} for ${OS}${GNU} (${ARCH}):" # check if $DEST is writable and suppress an error message @@ -76,20 +124,20 @@ if [ $? -eq 1 ]; then SUDO="sudo" fi -if type "curl" >/dev/null 2>&1; then - curl -L --progress-bar "https://github.com/php/frankenphp/releases/latest/download/${THE_ARCH_BIN}" -o "${DEST}" -elif type "wget" >/dev/null 2>&1; then - ${SUDO} wget "https://github.com/php/frankenphp/releases/latest/download/${THE_ARCH_BIN}" -O "${DEST}" -else - echo "❗ Please install ${italic}curl${normal} or ${italic}wget${normal} to download FrankenPHP" - exit 1 -fi +curl -L --progress-bar "https://github.com/php/frankenphp/releases/latest/download/${THE_ARCH_BIN}" -o "${DEST}" ${SUDO} chmod +x "${DEST}" +# Allow binding to ports 80/443 without running as root (if setcap is available) +if command -v setcap >/dev/null 2>&1; then + ${SUDO} setcap 'cap_net_bind_service=+ep' "${DEST}" || true +else + echo "❗ install setcap (e.g. libcap2-bin) to allow FrankenPHP to bind to ports 80/443 without root:" + echo " sudo setcap 'cap_net_bind_service=+ep' \"${DEST}\"" +fi echo echo "🥳 FrankenPHP downloaded successfully to ${italic}${DEST}${normal}" echo "🔧 Move the binary to ${italic}/usr/local/bin/${normal} or another directory in your ${italic}PATH${normal} to use it globally:" -echo " ${bold}sudo mv ${DEST} /usr/local/bin/${normal}" +echo " ${bold}sudo mv ${DEST} /usr/local/bin/${normal}" echo echo "⭐ If you like FrankenPHP, please give it a star on GitHub: ${italic}https://github.com/php/frankenphp${normal}" diff --git a/static-builder-gnu.Dockerfile b/static-builder-gnu.Dockerfile index 7257b12c0..96d03472d 100644 --- a/static-builder-gnu.Dockerfile +++ b/static-builder-gnu.Dockerfile @@ -6,8 +6,6 @@ FROM centos:7 ARG FRANKENPHP_VERSION='' ENV FRANKENPHP_VERSION=${FRANKENPHP_VERSION} -ARG BUILD_PACKAGES='' - ARG PHP_VERSION='' ENV PHP_VERSION=${PHP_VERSION} @@ -130,29 +128,6 @@ ENV EXTENSION_DIR='/usr/lib/frankenphp/modules' # not sure if this is needed ENV COMPOSER_ALLOW_SUPERUSER=1 -# install tools to build packages, if requested - needs gcc 10 -RUN if [ -n "${BUILD_PACKAGES}" ]; then \ - yum install -y \ - bzip2 \ - libffi-devel \ - libyaml \ - libyaml-devel \ - make \ - openssl-devel \ - rpm-build \ - sudo \ - zlib-devel && \ - curl -o ruby.tar.gz -fsSL https://cache.ruby-lang.org/pub/ruby/3.4/ruby-3.4.4.tar.gz && \ - tar -xzf ruby.tar.gz && \ - cd ruby-* && \ - ./configure --without-baseruby && \ - make && \ - make install && \ - cd .. && \ - rm -rf ruby* && \ - gem install fpm; \ -fi - WORKDIR /go/src/app COPY go.mod go.sum ./ RUN go mod download