Compare commits

..

64 Commits

Author SHA1 Message Date
Jerry Ma
29be3a2e39 set version to 2.0-rc7 2023-10-05 22:07:44 +08:00
crazywhalecc
fbe149bcac reuse static-php-cli-hosted workflows 2023-10-05 21:58:42 +08:00
crazywhalecc
92cafb36f6 fix bugs when building micro and embed at the same time 2023-10-01 10:01:45 +08:00
Joseph Bielawski
8636f2e7c9 Throw proper exception when PHP source is not available 2023-10-01 01:24:02 +08:00
crazywhalecc
8f43a09533 separate alternative libs 2023-10-01 01:19:35 +08:00
DubbleClick
654c5cba0c fix musl install on rhel 2023-10-01 01:19:35 +08:00
crazywhalecc
f9685b82a1 adjust tool check level 2023-10-01 01:19:35 +08:00
crazywhalecc
f2371d3702 bugfix: complete ldap support for macOS and Linux 2023-10-01 01:19:35 +08:00
crazywhalecc
fe39aecd72 adjust config order 2023-10-01 01:19:35 +08:00
crazywhalecc
68d176ad26 prevent c compiler not found error 2023-10-01 01:19:35 +08:00
crazywhalecc
d7627dd81a correct libc name for linux 2023-10-01 01:19:35 +08:00
crazywhalecc
a4e173f16d add configure flags for unix builder 2023-10-01 01:19:35 +08:00
DubbleClick
1437be3a9d gmp and libsodium for ldap if enabled
only enable openssl when zlib ext is also enabled (missing 'deflate' otherwise)
move back from source/php-src/ext/ldap to source/ldap (fix "LICENSE not found")
2023-10-01 01:19:35 +08:00
DubbleClick
059c32e59c add ext-ldap (openldap) support 2023-10-01 01:19:35 +08:00
DubbleClick
e3a4cd6e1d fix spelling mistake in README-en.md 2023-10-01 01:19:35 +08:00
DubbleClick
dd9a5d8316 add rhel and almalinux support 2023-10-01 01:19:35 +08:00
Joseph Bielawski
3b83c1fa7b Remove dead code from ConsoleApplication class 2023-09-30 13:52:29 +08:00
Rif'at Ahdi R
ff128df76b Update libwebp dependency (fix #208) 2023-09-29 16:07:59 +08:00
crazywhalecc
cd04a9ea4f change all related url to static-php.dev 2023-09-26 22:27:44 +08:00
Jerry Ma
3b793005fe Change domain 2023-09-26 18:32:18 +08:00
Jerry Ma
87e073a0b9 Change domain 2023-09-26 18:29:45 +08:00
crazywhalecc
db8aa15677 adjust micro's phar patch execution location 2023-09-25 10:35:31 +08:00
crazywhalecc
6ed9749732 cs fix and composer update 2023-09-23 17:03:20 +08:00
crazywhalecc
4e99211bc3 add hardcoded-ini option for embed SAPI 2023-09-17 12:21:06 +08:00
crazywhalecc
824748c2a7 remove download cache and minimize download sources 2023-09-16 15:15:46 +08:00
crazywhalecc
fe6a98b30d remove debug option 2023-09-16 15:15:46 +08:00
crazywhalecc
49149cebf1 Add new GH action to build binary SPC 2023-09-16 15:15:46 +08:00
Joseph Bielawski
7b6a1b4212 Simplify adding composer in bin/spc-alpine-docker
Co-authored-by: Kévin Dunglas <kevin@dunglas.fr>
2023-09-15 09:39:27 +08:00
Joseph Bielawski
965e7a25e2 Add composer.lock into repository 2023-09-15 09:39:27 +08:00
Kévin Dunglas
0c3885c33d fix(docker): php82-sodium is required 2023-09-14 10:02:54 +08:00
Joseph Bielawski
be32190829 Sanity check extensions by inline their test code 2023-09-13 23:42:10 +08:00
crazywhalecc
f1d5916090 remove unused old deploy command 2023-09-13 21:45:19 +08:00
Joseph Bielawski
b2ea479fac Remove the phar workflow temporary 2023-09-13 21:45:19 +08:00
Joseph Bielawski
16cc5df66d Temporary disable phar build in GH actions 2023-09-13 21:45:19 +08:00
Joseph Bielawski
a63e3f4575 Add version into phar 2023-09-13 21:45:19 +08:00
Joseph Bielawski
6b061e6332 Adjust box configuration for smaller phar size 2023-09-13 21:45:19 +08:00
Joseph Bielawski
4a17491aaa Initial code to get Box for phar building 2023-09-13 21:45:19 +08:00
Jerry Ma
06f29712e2 Update README.md 2023-09-13 10:03:31 +08:00
Kévin Dunglas
4b1d59c5a9 Link to FrankenPHP docs about embed SAPI in README-en.md 2023-09-13 10:03:31 +08:00
crazywhalecc
1a81fe6a0d change help message for dev:extensions 2023-09-12 23:30:23 +08:00
crazywhalecc
c63136d484 change dev:extensions argument to comma separated 2023-09-12 23:30:23 +08:00
crazywhalecc
4b500f2dd8 remove redundant libssh2 cmake build target option 2023-09-12 23:09:58 +08:00
crazywhalecc
9da20497cc fix ln src and dst for brotli 2023-09-12 22:53:47 +08:00
crazywhalecc
5efc15b404 fix libwebp pkgconfig patch, add custom patch argument 2023-09-12 22:52:20 +08:00
Joseph Bielawski
fa0740f216 Explicitly define application commands 2023-09-12 22:51:50 +08:00
Joseph Bielawski
52430cbdde Rework doctor command 2023-09-12 22:46:44 +08:00
crazywhalecc
01c4538ce0 update README for embed SAPI 2023-09-12 22:44:19 +08:00
Kévin Dunglas
3183ecceaf --disable-opcache-jit 2023-09-12 22:30:13 +08:00
Kévin Dunglas
dd2e7cc129 static opcache patch 2023-09-12 22:30:13 +08:00
Kévin Dunglas
adfa620ef4 Revert "always enable micro"
This reverts commit eb8cefd9ca.
2023-09-12 22:30:13 +08:00
Kévin Dunglas
1a32ddc70a Revert "temporary workaround for PECL certificate expiration"
This reverts commit 7c78003c9f.
2023-09-12 22:30:13 +08:00
Kévin Dunglas
4bfca6fe93 always enable micro 2023-09-12 22:30:13 +08:00
Kévin Dunglas
dbec043894 temporary workaround for PECL certificate expiration 2023-09-12 22:30:13 +08:00
Kévin Dunglas
727d78a689 Revert "fix: replace down URL"
This reverts commit 6eadbca21bf9a3b57058160a2f7ea8f4cc5f21dd.
2023-09-12 22:30:13 +08:00
Kévin Dunglas
9b1e784604 fix: replace down URL 2023-09-12 22:30:13 +08:00
Kévin Dunglas
50d44d8310 explicitly disable unneeded SAPIs 2023-09-12 22:30:13 +08:00
Kévin Dunglas
3b300698f3 use lixml GH mirror because gitlab.gnome.org is down 2023-09-12 22:30:13 +08:00
Kévin Dunglas
899eb94b8b remove useless ls 2023-09-12 22:30:13 +08:00
Kévin Dunglas
451a0c0e34 workaround for macOS 2023-09-12 22:30:13 +08:00
Kévin Dunglas
085c1a159c feat: add support for libphp and the embed SAPI 2023-09-12 22:30:13 +08:00
Joseph Bielawski
ca3f8a350d Prevent fatal error when php source files are missing 2023-09-12 18:19:32 +08:00
crazywhalecc
5025850f71 fix Alpine docker install packages 2023-09-12 12:45:51 +08:00
crazywhalecc
fe2f658e08 dev:sort-config add \n at the end of json 2023-09-12 00:24:01 +08:00
crazywhalecc
00a49c662b add build:libs option --rebuild 2023-09-11 23:47:02 +08:00
46 changed files with 6534 additions and 399 deletions

47
.github/workflows/release-build.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: Upload SPC Binary (Release)
on:
release:
types:
- published
workflow_dispatch:
jobs:
build-release-artifacts:
name: "Upload SPC Binary (Release)"
runs-on: ubuntu-latest
strategy:
matrix:
operating-system:
- "linux-x86_64"
- "macos-x86_64"
- "linux-aarch64"
- "macos-aarch64"
steps:
- name: "Checkout"
uses: "actions/checkout@v4"
- name: Reuse static-php-cli-hosted artifacts
uses: dawidd6/action-download-artifact@v2
with:
repo: crazywhalecc/static-php-cli-hosted
branch: master
workflow: build-spc-release.yml
name: "spc-${{ matrix.operating-system }}"
- name: "Archive Executable"
run: |
tar -czf spc-${{ matrix.operating-system }}.tar.gz spc
echo "filename=spc-${{ matrix.operating-system }}.tar.gz" >> $GITHUB_ENV
- name: upload binaries to release
uses: softprops/action-gh-release@v1
if: ${{startsWith(github.ref, 'refs/tags/') }}
with:
files: ${{ env.filename }}
- name: "Upload Artifact"
uses: actions/upload-artifact@v3
with:
path: spc
name: spc-${{ matrix.operating-system }}

3
.gitignore vendored
View File

@@ -4,8 +4,7 @@ docker/libraries/
docker/extensions/
docker/source/
# Composer file
composer.lock
# Vendor files
/vendor/
# default source extract directory

View File

@@ -2,7 +2,7 @@
Build single static PHP binary, with PHP project together, with popular extensions included.
The project name is static-php-cli, but it actually supports cli, fpm, micro and embed (on the way) SAPI 😎
The project name is static-php-cli, but it actually supports cli, fpm, micro and embed SAPI 😎
Compile a purely static php-cli binary file with various extensions to make PHP applications more portable! (cli SAPI)
@@ -41,7 +41,7 @@ Currently supported PHP versions for compilation are: `7.3`, `7.4`, `8.0`, `8.1`
## Docs
docs here: <https://static-php-cli.zhamao.me>.
docs here: <https://static-php.dev>.
## Simple Usage
@@ -49,13 +49,13 @@ Please first select the extension you want to compile based on the extension lis
### Direct Download
If you don't compile yourself, you can download pre-compiled artifact from Actions, or from self-hosted server: [Here](https://dl.zhamao.xin/static-php-cli/)
If you don't compile yourself, you can download pre-compiled artifact from Actions, or from self-hosted server: [Here](https://dl.static-php.dev/static-php-cli/common/)
> self-hosted server contains extensions: `bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,gd,gmp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,redis,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip`
### Supported Extensions
[Supported Extension List](https://static-php-cli.zhamao.me/en/guide/extensions.html)
[Supported Extension List](https://static-php.dev/en/guide/extensions.html)
> If there is no extension you need here, you can submit an issue.
@@ -126,6 +126,7 @@ Now we support `cli`, `micro`, `fpm`, you can use one or more of the following p
- `--build-cli`: build static cli executable
- `--build-micro`: build static phpmicro self-extracted executable
- `--build-fpm`: build static fpm binary
- `--build-embed`: build embed (libphp)
- `--build-all`: build all
If anything goes wrong, use `--debug` option to display full terminal output:
@@ -194,15 +195,26 @@ Because php-fpm must specify a configuration file before running, the php-fpm co
Specifying `php-fpm.conf` can use the command parameter `-y`, for example: `./php-fpm -y php-fpm.conf`.
### Embed Usage
When using the project parameters `--build-embed` or `--build-all`,
the final compilation result will output a `libphp.a`, `php-config` and a series of header files,
stored in `buildroot/`. You can introduce them in your other projects.
If you know [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed), you should know how to use it.
You may require the introduction of other libraries during compilation,
you can use `buildroot/bin/php-config` to obtain the compile-time configuration.
For an advanced example of how to use this feature, take a look at [how to use it to build a static version of FrankenPHP](https://github.com/dunglas/frankenphp/blob/main/docs/static.md).
## Contribution
Currently, there are only a few supported extensions.
If the extension you need is missing, you can create an issue.
If you are familiar with this project, you are also welcome to initiate a pull request.
The basic principles for contributing are as follows:
- This project uses php-cs-fixer and phpstan as code formatting tools. Before contributing, please run `composer analyze` and `composer cs-fix` on the updated code.
- This project uses php-cs-fixer and phpstan as code formatting tools. Before contributing, please run `composer analyse` and `composer cs-fix` on the updated code.
- If other open source libraries are involved, the corresponding licenses should be provided.
Also, configuration files should be sorted using the command `sort-config` after modification.
For more information about sorting commands, see the documentation.
@@ -242,4 +254,4 @@ and comply with the corresponding project's LICENSE.
The refactoring branch of this project is written modularly.
If you are interested in this project and want to join the development,
you can refer to the [Contribution Guide](https://static-php-cli.zhamao.me) of the documentation to contribute code or documentation.
you can refer to the [Contribution Guide](https://static-php.dev) of the documentation to contribute code or documentation.

View File

@@ -19,7 +19,7 @@ Build single static PHP binary, with PHP project together, with popular extensio
[![](https://img.shields.io/badge/Extension%20Counter-55+-yellow.svg?style=flat-square)]()
[![](https://img.shields.io/github/search/crazywhalecc/static-php-cli/TODO?label=TODO%20Counter&style=flat-square)]()
> 项目名称是 static-php-cli但其实支持 cli、fpm、micro 和 embed(正在路上)SAPI 😎
> 项目名称是 static-php-cli但其实支持 cli、fpm、micro 和 embed SAPI 😎
## 编译环境需求
@@ -40,7 +40,7 @@ Build single static PHP binary, with PHP project together, with popular extensio
## 文档
点击这里查看文档:<https://static-php-cli.zhamao.me>。
点击这里查看文档:<https://static-php.dev>。
## 使用
@@ -48,13 +48,13 @@ Build single static PHP binary, with PHP project together, with popular extensio
### 自托管直接下载
如果你不想自行编译,可以从本项目现有的 Action 下载 Artifact也可以从自托管的服务器下载[进入](https://dl.zhamao.xin/static-php-cli/)
如果你不想自行编译,可以从本项目现有的 Action 下载 Artifact也可以从自托管的服务器下载[进入](https://dl.static-php.dev/static-php-cli/common/)
> 自托管的服务器默认包含的扩展有:`bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,filter,ftp,gd,gmp,iconv,xml,mbstring,mbregex,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,redis,session,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip`
### 支持的扩展情况
[扩展支持列表](https://static-php-cli.zhamao.me/zh/guide/extensions.html)
[扩展支持列表](https://static-php.dev/zh/guide/extensions.html)
> 如果这里没有你需要的扩展,可以提交 Issue。
@@ -120,6 +120,7 @@ chmod +x bin/spc
- `--build-cli`:构建 cli 二进制
- `--build-micro`:构建 phpmicro 自执行二进制
- `--build-fpm`:构建 fpm
- `--build-embed`:构建 embedlibphp
- `--build-all`:构建所有
如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误:
@@ -181,9 +182,17 @@ cat micro.sfx code.php > single-app && chmod +x single-app
指定 `php-fpm.conf` 可以使用命令参数 `-y`,例如:`./php-fpm -y php-fpm.conf`
### 使用 php-embed
采用项目参数 `--build-embed``--build-all` 时,最后编译结果会输出一个 `libphp.a``php-config` 以及一系列头文件,存放在 `buildroot/`,你可以在你的其他代码中引入它们。
如果你知道 [embed SAPI](https://github.com/php/php-src/tree/master/sapi/embed),你应该知道如何使用它。对于有可能编译用到引入其他库的问题,你可以使用 `buildroot/bin/php-config` 来获取编译时的配置。
另外,有关如何使用此功能的高级示例,请查看[如何使用它构建 FrankenPHP 的静态版本](https://github.com/dunglas/frankenphp/blob/main/docs/static.md)。
## 贡献
目前支持的扩展较少,如果缺少你需要的扩展,可发起 Issue。如果你对本项目较熟悉也欢迎为本项目发起 Pull Request。
如果缺少你需要的扩展,可发起 Issue。如果你对本项目较熟悉也欢迎为本项目发起 Pull Request。
贡献基本原则如下:
@@ -202,7 +211,7 @@ cat micro.sfx code.php > single-app && chmod +x single-app
## 开源协议
本项目依据旧版本惯例采用 MIT License 开源,自身的部分代码引用或修改自以下项目:
本项目依据旧版本惯例采用 MIT License 开源,部分扩展的集成编译命令参考或修改自以下项目:
- [dixyes/lwmbs](https://github.com/dixyes/lwmbs)(木兰宽松许可证)
- [swoole/swoole-cli](https://github.com/swoole/swoole-cli)Apache 2.0 LICENSE、SWOOLE-CLI LICENSE
@@ -212,4 +221,4 @@ cat micro.sfx code.php > single-app && chmod +x single-app
## 进阶
本项目重构分支为模块化编写。如果你对本项目感兴趣,想加入开发,可以参照文档的 [贡献指南](https://static-php-cli.zhamao.me) 贡献代码或文档。
本项目重构分支为模块化编写。如果你对本项目感兴趣,想加入开发,可以参照文档的 [贡献指南](https://static-php.dev) 贡献代码或文档。

View File

@@ -25,7 +25,7 @@ __DIR__=$(cd "$(dirname "$0")" && pwd)
__PROJECT__=$(cd "${__DIR__}"/../ && pwd)
# set download dir
__PHP_RUNTIME_URL__="https://dl.zhamao.xin/static-php-cli/php-8.2.6-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/common/php-8.2.10-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__COMPOSER_URL__="https://getcomposer.org/download/latest-stable/composer.phar"
# use china mirror
@@ -46,7 +46,7 @@ done
case "$mirror" in
china)
__PHP_RUNTIME_URL__="https://dl.zhamao.xin/static-php-cli/php-8.2.6-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__PHP_RUNTIME_URL__="https://dl.static-php.dev/static-php-cli/common/php-8.2.10-cli-${__OS_FIXED__}-${__ARCH__}.tar.gz"
__COMPOSER_URL__="https://mirrors.aliyun.com/composer/composer.phar"
;;

View File

@@ -64,7 +64,6 @@ RUN apk update; \
bison \
build-base \
cmake \
composer \
curl \
file \
flex \
@@ -77,21 +76,27 @@ RUN apk update; \
linux-headers \
m4 \
make \
php81 \
php81-common \
php81-pcntl \
php81-phar \
php81-posix \
php81-tokenizer \
php81-xml \
php82 \
php82-common \
php82-pcntl \
php82-phar \
php82-posix \
php82-sodium \
php82-tokenizer \
php82-dom \
php82-xml \
php82-xmlwriter \
composer \
pkgconfig \
wget \
xz
xz ; \
ln -s /usr/bin/php82 /usr/bin/php
WORKDIR /app
ADD ./src /app/src
ADD ./composer.json /app/composer.json
COPY ./composer.* /app/
ADD ./bin /app/bin
RUN composer update --no-dev --classmap-authoritative
RUN composer install --no-dev --classmap-authoritative
EOF
fi

18
box.json Normal file
View File

@@ -0,0 +1,18 @@
{
"alias": "spc-php.phar",
"banner": false,
"blacklist": [
".github"
],
"directories": [
"config",
"src",
"vendor/psr",
"vendor/laravel/prompts",
"vendor/symfony",
"vendor/zhamao"
],
"git-commit-short": "git_commit_short",
"metadata": "ConsoleApplication::VERSION",
"output": "spc.phar"
}

View File

@@ -20,6 +20,7 @@
"captainhook/captainhook": "^5.10",
"captainhook/plugin-composer": "^5.3",
"friendsofphp/php-cs-fixer": "^3.25",
"humbug/box": "^4.3",
"nunomaduro/collision": "^7.8",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^10.3"
@@ -44,7 +45,8 @@
"scripts": {
"analyse": "phpstan analyse --memory-limit 300M",
"cs-fix": "php-cs-fixer fix",
"test": "vendor/bin/phpunit tests/ --no-coverage"
"test": "vendor/bin/phpunit tests/ --no-coverage",
"build:phar": "vendor/bin/box compile"
},
"config": {
"allow-plugins": {

5950
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -153,9 +153,16 @@
},
"ldap": {
"type": "builtin",
"arg-type": "with",
"arg-type": "with-prefix",
"lib-depends": [
"ldap"
],
"lib-suggests": [
"gmp",
"libsodium"
],
"ext-suggests": [
"openssl"
]
},
"mbregex": {
@@ -229,6 +236,9 @@
"arg-type": "with",
"lib-depends": [
"openssl"
],
"ext-depends": [
"zlib"
]
},
"pcntl": {

View File

@@ -139,6 +139,18 @@
"libxml2"
]
},
"ldap": {
"source": "ldap",
"static-libs-unix": [
"liblber.a",
"libldap.a"
],
"lib-suggests": [
"openssl",
"gmp",
"libsodium"
]
},
"libavif": {
"source": "libavif",
"static-libs-unix": [

View File

@@ -153,6 +153,15 @@
"path": "LICENSE"
}
},
"ldap": {
"type": "filelist",
"url": "https://www.openldap.org/software/download/OpenLDAP/openldap-release/",
"regex": "/href=\"(?<file>openldap-(?<version>[^\"]+)\\.tgz)\"/",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"libavif": {
"type": "ghtar",
"repo": "AOMediaCodec/libavif",
@@ -241,7 +250,7 @@
},
"libwebp": {
"type": "url",
"url": "https://github.com/webmproject/libwebp/archive/refs/tags/v1.3.0.tar.gz",
"url": "https://github.com/webmproject/libwebp/archive/refs/tags/v1.3.2.tar.gz",
"license": {
"type": "file",
"path": "COPYING"
@@ -249,7 +258,7 @@
},
"libxml2": {
"type": "url",
"url": "https://gitlab.gnome.org/GNOME/libxml2/-/archive/v2.9.14/libxml2-v2.9.14.tar.gz",
"url": "https://github.com/GNOME/libxml2/archive/refs/tags/v2.9.14.tar.gz",
"license": {
"type": "file",
"path": "Copyright"

View File

@@ -1,3 +1,3 @@
# Extension List
See: [Docs - Extensions](https://static-php-cli.zhamao.me/en/guide/extensions.html)
See: [Docs - Extensions](https://static-php.dev/en/guide/extensions.html)

View File

@@ -4,7 +4,16 @@ declare(strict_types=1);
namespace SPC;
use SPC\store\FileSystem;
use SPC\command\BuildCliCommand;
use SPC\command\BuildLibsCommand;
use SPC\command\dev\AllExtCommand;
use SPC\command\dev\PhpVerCommand;
use SPC\command\dev\SortConfigCommand;
use SPC\command\DoctorCommand;
use SPC\command\DownloadCommand;
use SPC\command\DumpLicenseCommand;
use SPC\command\ExtractCommand;
use SPC\command\MicroCombineCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\HelpCommand;
use Symfony\Component\Console\Command\ListCommand;
@@ -12,38 +21,30 @@ use Symfony\Component\Console\Command\ListCommand;
/**
* static-php-cli console app entry
*/
class ConsoleApplication extends Application
final class ConsoleApplication extends Application
{
public const VERSION = '2.0-rc6';
public const VERSION = '2.0-rc7';
/**
* @throws \ReflectionException
* @throws exception\FileSystemException
*/
public function __construct()
{
parent::__construct('static-php-cli', self::VERSION);
global $argv;
$this->addCommands(
[
new BuildCliCommand(),
new BuildLibsCommand(),
new DoctorCommand(),
new DownloadCommand(),
new DumpLicenseCommand(),
new ExtractCommand(),
new MicroCombineCommand(),
// Detailed debugging errors are not displayed in the production environment. Only the error display provided by Symfony console is used.
$this->setCatchExceptions(file_exists(ROOT_DIR . '/.prod') || !in_array('--debug', $argv));
// Add subcommands by scanning the directory src/static-php-cli/command/
$commands = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/command', 'SPC\\command');
$phar = class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar');
$commands = array_filter($commands, function ($y) use ($phar) {
$archive_blacklist = [
'SPC\command\dev\SortConfigCommand',
'SPC\command\DeployCommand',
];
if ($phar && in_array($y, $archive_blacklist)) {
return false;
}
$reflection = new \ReflectionClass($y);
return !$reflection->isAbstract() && !$reflection->isInterface();
});
$this->addCommands(array_map(function ($x) { return new $x(); }, $commands));
// Dev commands
new AllExtCommand(),
new PhpVerCommand(),
new SortConfigCommand(),
]
);
}
protected function getDefaultCommands(): array

View File

@@ -85,7 +85,7 @@ abstract class BuilderBase
// build all libs
foreach ($this->libs as $lib) {
match ($lib->tryBuild()) {
match ($lib->tryBuild($this->getOption('rebuild', false))) {
BUILD_STATUS_OK => logger()->info('lib [' . $lib::NAME . '] build success'),
BUILD_STATUS_ALREADY => logger()->notice('lib [' . $lib::NAME . '] already built'),
BUILD_STATUS_FAILED => logger()->error('lib [' . $lib::NAME . '] build failed'),
@@ -227,12 +227,22 @@ abstract class BuilderBase
/**
* Get PHP Version ID from php-src/main/php_version.h
*
* @throws RuntimeException
* @throws WrongUsageException
*/
public function getPHPVersionID(): int
{
if (!file_exists(SOURCE_PATH . '/php-src/main/php_version.h')) {
throw new WrongUsageException('PHP source files are not available, you need to download them first');
}
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');
preg_match('/PHP_VERSION_ID (\d+)/', $file, $match);
return intval($match[1]);
if (preg_match('/PHP_VERSION_ID (\d+)/', $file, $match) !== 0) {
return intval($match[1]);
}
throw new RuntimeException('PHP version file format is malformed, please remove it and download again');
}
/**
@@ -252,6 +262,9 @@ abstract class BuilderBase
if (($type & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) {
$ls[] = 'fpm';
}
if (($type & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED) {
$ls[] = 'embed';
}
return implode(', ', $ls);
}

View File

@@ -6,7 +6,6 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\util\CustomExt;
use SPC\util\Util;
#[CustomExt('openssl')]
class openssl extends Extension
@@ -14,12 +13,13 @@ class openssl extends Extension
public function patchBeforeMake(): bool
{
// patch openssl3 with php8.0 bug
if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && Util::getPHPVersionID() < 80100) {
if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && $this->builder->getPHPVersionID() < 80100) {
$openssl_c = file_get_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c');
$openssl_c = preg_replace('/REGISTER_LONG_CONSTANT\s*\(\s*"OPENSSL_SSLV23_PADDING"\s*.+;/', '', $openssl_c);
file_put_contents(SOURCE_PATH . '/php-src/ext/openssl/openssl.c', $openssl_c);
return true;
}
return false;
}
}

View File

@@ -7,7 +7,6 @@ namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\exception\RuntimeException;
use SPC\util\CustomExt;
use SPC\util\Util;
#[CustomExt('swow')]
class swow extends Extension
@@ -25,7 +24,7 @@ class swow extends Extension
*/
public function patchBeforeBuildconf(): bool
{
if (Util::getPHPVersionID() >= 80000 && !is_link(SOURCE_PATH . '/php-src/ext/swow')) {
if ($this->builder->getPHPVersionID() >= 80000 && !is_link(SOURCE_PATH . '/php-src/ext/swow')) {
if (PHP_OS_FAMILY === 'Windows') {
f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext');
} else {

View File

@@ -10,6 +10,7 @@ use SPC\builder\traits\UnixBuilderTrait;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\store\SourcePatcher;
class LinuxBuilder extends BuilderBase
@@ -52,7 +53,7 @@ class LinuxBuilder extends BuilderBase
// ---------- set necessary compile environments ----------
// set libc
$this->libc = 'musl'; // SystemUtil::selectLibc($this->cc);
$this->libc = $this->getOption('cc', 'gcc') === 'musl-gcc' ? 'musl_wrapper' : 'musl'; // SystemUtil::selectLibc($this->cc);
// concurrency
$this->concurrency = SystemUtil::getCpuCount();
// cflags
@@ -179,6 +180,11 @@ class LinuxBuilder extends BuilderBase
$zts = '';
}
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
'./configure ' .
@@ -189,12 +195,13 @@ class LinuxBuilder extends BuilderBase
'--disable-all ' .
'--disable-cgi ' .
'--disable-phpdbg ' .
'--enable-cli ' .
'--enable-fpm ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? '--enable-embed=static --disable-opcache-jit ' : '--disable-embed ') .
$json_74 .
$zts .
$maxExecutionTimers .
'--enable-micro=all-static ' .
($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') .
$this->makeExtensionArgs() . ' ' .
$envs
);
@@ -203,26 +210,29 @@ class LinuxBuilder extends BuilderBase
$this->cleanMake();
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
if ($enableCli) {
logger()->info('building cli');
$this->buildCli($extra_libs, $use_lld);
}
if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) {
if ($enableFpm) {
logger()->info('building fpm');
$this->buildFpm($extra_libs, $use_lld);
}
if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) {
if ($enableMicro) {
logger()->info('building micro');
$this->buildMicro($extra_libs, $use_lld, $cflags);
}
if ($enableEmbed) {
logger()->info('building embed');
if ($enableMicro) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
}
$this->buildEmbed($extra_libs, $use_lld);
}
if (php_uname('m') === $this->getOption('arch')) {
$this->sanityCheck($build_target);
}
if ($this->phar_patched) {
SourcePatcher::patchMicro(['phar'], true);
}
}
/**
@@ -280,6 +290,10 @@ class LinuxBuilder extends BuilderBase
}
$this->deployBinary(BUILD_TARGET_MICRO);
if ($this->phar_patched) {
SourcePatcher::patchMicro(['phar'], true);
}
}
/**
@@ -306,4 +320,18 @@ class LinuxBuilder extends BuilderBase
$this->deployBinary(BUILD_TARGET_FPM);
}
public function buildEmbed(string $extra_libs, string $use_lld): void
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)),
'EXTRA_LIBS' => $extra_libs,
'EXTRA_LDFLAGS_PROGRAM' => "{$use_lld} -all-static",
]);
shell()
->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec('make INSTALL_ROOT=' . BUILD_ROOT_PATH . " -j{$this->concurrency} {$vars} install");
}
}

View File

@@ -121,7 +121,7 @@ class SystemUtil
public static function checkCCFlag(string $flag, string $cc): string
{
[$ret] = shell()->execWithResult("echo | {$cc} -E -x c - {$flag}");
[$ret] = shell()->execWithResult("echo | {$cc} -E -x c - {$flag} 2>/dev/null");
if ($ret != 0) {
return '';
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\linux\library;
class ldap extends LinuxLibraryBase
{
use \SPC\builder\unix\library\ldap;
public const NAME = 'ldap';
}

View File

@@ -10,6 +10,7 @@ use SPC\builder\traits\UnixBuilderTrait;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\FileSystem;
use SPC\store\SourcePatcher;
class MacOSBuilder extends BuilderBase
@@ -151,6 +152,11 @@ class MacOSBuilder extends BuilderBase
$json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : '';
$zts = $this->getOption('enable-zts', false) ? '--enable-zts --disable-zend-signals ' : '';
$enableCli = ($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI;
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
shell()->cd(SOURCE_PATH . '/php-src')
->exec(
'./configure ' .
@@ -162,9 +168,10 @@ class MacOSBuilder extends BuilderBase
'--disable-all ' .
'--disable-cgi ' .
'--disable-phpdbg ' .
'--enable-cli ' .
'--enable-fpm ' .
'--enable-micro ' .
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableFpm ? '--enable-fpm ' : '--disable-fpm ') .
($enableEmbed ? '--enable-embed=static ' : '--disable-embed ') .
($enableMicro ? '--enable-micro ' : '--disable-micro ') .
$json_74 .
$zts .
$this->makeExtensionArgs() . ' ' .
@@ -175,26 +182,29 @@ class MacOSBuilder extends BuilderBase
$this->cleanMake();
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
if ($enableCli) {
logger()->info('building cli');
$this->buildCli();
}
if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) {
if ($enableFpm) {
logger()->info('building fpm');
$this->buildFpm();
}
if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) {
if ($enableMicro) {
logger()->info('building micro');
$this->buildMicro();
}
if ($enableEmbed) {
logger()->info('building embed');
if ($enableMicro) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'OVERALL_TARGET =', 'OVERALL_TARGET = libphp.la');
}
$this->buildEmbed();
}
if (php_uname('m') === $this->getOption('arch')) {
$this->sanityCheck($build_target);
}
if ($this->phar_patched) {
SourcePatcher::patchMicro(['phar'], true);
}
}
/**
@@ -207,7 +217,7 @@ class MacOSBuilder extends BuilderBase
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", // link resolv library (macOS need it)
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", // link resolv library (macOS needs it)
]);
$shell = shell()->cd(SOURCE_PATH . '/php-src');
@@ -237,7 +247,7 @@ class MacOSBuilder extends BuilderBase
$vars = [
// with debug information, optimize for size, remove identifiers, patch fake cli for micro
'EXTRA_CFLAGS' => '-g -Os -fno-ident' . $enable_fake_cli,
// link resolv library (macOS need it)
// link resolv library (macOS needs it)
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv",
];
if (!$this->getOption('no-strip', false)) {
@@ -248,6 +258,10 @@ class MacOSBuilder extends BuilderBase
shell()->cd(SOURCE_PATH . '/php-src')
->exec("make -j{$this->concurrency} {$vars} micro");
$this->deployBinary(BUILD_TARGET_MICRO);
if ($this->phar_patched) {
SourcePatcher::patchMicro(['phar'], true);
}
}
/**
@@ -260,7 +274,7 @@ class MacOSBuilder extends BuilderBase
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", // link resolv library (macOS need it)
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", // link resolv library (macOS needs it)
]);
$shell = shell()->cd(SOURCE_PATH . '/php-src');
@@ -270,4 +284,24 @@ class MacOSBuilder extends BuilderBase
}
$this->deployBinary(BUILD_TARGET_FPM);
}
public function buildEmbed(): void
{
$vars = SystemUtil::makeEnvVarString([
'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size
'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", // link resolv library (macOS needs it)
]);
shell()
->cd(SOURCE_PATH . '/php-src')
->exec('make INSTALL_ROOT=' . BUILD_ROOT_PATH . " -j{$this->concurrency} {$vars} install")
// Workaround for https://github.com/php/php-src/issues/12082
->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o')
->exec('mkdir ' . BUILD_ROOT_PATH . '/lib/php-o')
->cd(BUILD_ROOT_PATH . '/lib/php-o')
->exec('ar x ' . BUILD_ROOT_PATH . '/lib/libphp.a')
->exec('rm ' . BUILD_ROOT_PATH . '/lib/libphp.a')
->exec('ar rcs ' . BUILD_ROOT_PATH . '/lib/libphp.a *.o')
->exec('rm -Rf ' . BUILD_ROOT_PATH . '/lib/php-o');
}
}

View File

@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace SPC\builder\macos\library;
class ldap extends MacOSLibraryBase
{
use \SPC\builder\unix\library\ldap;
public const NAME = 'ldap';
}

View File

@@ -66,14 +66,23 @@ trait UnixBuilderTrait
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new RuntimeException('cli failed sanity check');
}
foreach ($this->exts as $ext) {
logger()->debug('testing ext: ' . $ext->getName());
[$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php --ri "' . $ext->getDistName() . '"', false);
if ($ret !== 0) {
throw new RuntimeException('extension ' . $ext->getName() . ' failed compile check');
}
if (file_exists(ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php')) {
[$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php ' . ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php');
// Trim additional content & escape special characters to allow inline usage
$test = str_replace(
['<?php', 'declare(strict_types=1);', "\n", '"', '$'],
['', '', '', '\"', '\$'],
file_get_contents(ROOT_DIR . '/src/globals/tests/' . $ext->getName() . '.php')
);
[$ret] = shell()->execWithResult(BUILD_ROOT_PATH . '/bin/php -r "' . trim($test) . '"');
if ($ret !== 0) {
throw new RuntimeException('extension ' . $ext->getName() . ' failed sanity check');
}
@@ -144,4 +153,26 @@ trait UnixBuilderTrait
"-DCMAKE_INSTALL_INCLUDEDIR={$include} " .
"-DCMAKE_TOOLCHAIN_FILE={$this->cmake_toolchain_file}";
}
/**
* Generate configure flags
*/
public function makeAutoconfFlags(int $flag = AUTOCONF_ALL): string
{
$extra = '';
// TODO: add auto pkg-config support
if (($flag & AUTOCONF_LIBS) === AUTOCONF_LIBS) {
$extra .= 'LIBS="' . BUILD_LIB_PATH . '" ';
}
if (($flag & AUTOCONF_CFLAGS) === AUTOCONF_CFLAGS) {
$extra .= 'CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ';
}
if (($flag & AUTOCONF_CPPFLAGS) === AUTOCONF_CPPFLAGS) {
$extra .= 'CPPFLAGS="-I' . BUILD_INCLUDE_PATH . '" ';
}
if (($flag & AUTOCONF_LDFLAGS) === AUTOCONF_LDFLAGS) {
$extra .= 'LDFLAGS="-L' . BUILD_LIB_PATH . '" ';
}
return $extra;
}
}

View File

@@ -66,7 +66,7 @@ trait UnixLibraryTrait
* @throws FileSystemException
* @throws RuntimeException
*/
public function patchPkgconfPrefix(array $files, int $patch_option = PKGCONF_PATCH_ALL): void
public function patchPkgconfPrefix(array $files, int $patch_option = PKGCONF_PATCH_ALL, ?array $custom_replace = null): void
{
logger()->info('Patching library [' . static::NAME . '] pkgconfig');
foreach ($files as $name) {
@@ -81,6 +81,7 @@ trait UnixLibraryTrait
$file = ($patch_option & PKGCONF_PATCH_EXEC_PREFIX) === PKGCONF_PATCH_EXEC_PREFIX ? preg_replace('/^exec_prefix=.*$/m', 'exec_prefix=${prefix}', $file) : $file;
$file = ($patch_option & PKGCONF_PATCH_LIBDIR) === PKGCONF_PATCH_LIBDIR ? preg_replace('/^libdir=.*$/m', 'libdir=${prefix}/lib', $file) : $file;
$file = ($patch_option & PKGCONF_PATCH_INCLUDEDIR) === PKGCONF_PATCH_INCLUDEDIR ? preg_replace('/^includedir=.*$/m', 'includedir=${prefix}/include', $file) : $file;
$file = ($patch_option & PKGCONF_PATCH_CUSTOM) === PKGCONF_PATCH_CUSTOM && $custom_replace !== null ? preg_replace($custom_replace[0], $custom_replace[1], $file) : $file;
FileSystem::writeFile($realpath, $file);
}
}

View File

@@ -28,9 +28,9 @@ trait brotli
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libbrotlicommon.pc', 'libbrotlidec.pc', 'libbrotlienc.pc']);
shell()->cd(BUILD_ROOT_PATH . '/lib')
->exec('ln -s libbrotlicommon-static.a libbrotlicommon.a')
->exec('ln -s libbrotlidec-static.a libbrotlidec.a')
->exec('ln -s libbrotlienc-static.a libbrotlienc.a');
->exec('ln -s libbrotlicommon.a libbrotlicommon-static.a')
->exec('ln -s libbrotlidec.a libbrotlidec-static.a')
->exec('ln -s libbrotlienc.a libbrotlienc-static.a');
foreach (FileSystem::scanDirFiles(BUILD_ROOT_PATH . '/lib/', false, true) as $filename) {
if (str_starts_with($filename, 'libbrotli') && (str_contains($filename, '.so') || str_ends_with($filename, '.dylib'))) {
unlink(BUILD_ROOT_PATH . '/lib/' . $filename);

View File

@@ -39,8 +39,8 @@ trait curl
} else {
$extra .= '-DUSE_NGHTTP2=OFF ';
}
// TODO: ldap is not supported yet
$extra .= '-DCURL_DISABLE_LDAP=ON ';
// lib:ldap
$extra .= $this->builder->getLib('ldap') ? '-DCURL_DISABLE_LDAP=OFF ' : '-DCURL_DISABLE_LDAP=ON ';
// lib:zstd
$extra .= $this->builder->getLib('zstd') ? '-DCURL_ZSTD=ON ' : '-DCURL_ZSTD=OFF ';
// lib:idn2

View File

@@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
trait ldap
{
protected function build(): void
{
$alt = '';
// openssl support
$alt .= $this->builder->getLib('openssl') && $this->builder->getExt('zlib') ? '--with-tls=openssl ' : '';
// gmp support
$alt .= $this->builder->getLib('gmp') ? '--with-mp=gmp ' : '';
// libsodium support
$alt .= $this->builder->getLib('libsodium') ? '--with-argon2=libsodium ' : '';
shell()->cd($this->source_dir)
->exec(
$this->builder->configure_env . ' ' .
$this->builder->makeAutoconfFlags(AUTOCONF_LDFLAGS | AUTOCONF_CPPFLAGS) .
' ./configure ' .
'--enable-static ' .
'--disable-shared ' .
'--disable-slapd ' .
'--disable-slurpd ' .
'--without-systemd ' .
'--without-cyrus-sasl ' .
$alt .
'--prefix='
)
->exec('make clean')
// remove tests and doc to prevent compile failed with error: soelim not found
->exec('sed -i -e "s/SUBDIRS= include libraries clients servers tests doc/SUBDIRS= include libraries clients servers/g" Makefile')
->exec("make -j{$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['ldap.pc', 'lber.pc']);
}
}

View File

@@ -29,7 +29,7 @@ trait libssh2
"-DENABLE_ZLIB_COMPRESSION={$enable_zlib} " .
'..'
)
->exec("cmake --build . -j {$this->builder->concurrency} --target libssh2")
->exec("cmake --build . -j {$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
$this->patchPkgconfPrefix(['libssh2.pc']);
}

View File

@@ -32,7 +32,8 @@ trait libwebp
->exec("cmake --build . -j {$this->builder->concurrency}")
->exec('make install DESTDIR=' . BUILD_ROOT_PATH);
// patch pkgconfig
$this->patchPkgconfPrefix(['libsharpyuv.pc', 'libwebp.pc', 'libwebpdecoder.pc', 'libwebpdemux.pc', 'libwebpmux.pc'], PKGCONF_PATCH_PREFIX);
$this->patchPkgconfPrefix(['libsharpyuv.pc', 'libwebp.pc', 'libwebpdecoder.pc', 'libwebpdemux.pc', 'libwebpmux.pc'], PKGCONF_PATCH_PREFIX | PKGCONF_PATCH_LIBDIR);
$this->patchPkgconfPrefix(['libsharpyuv.pc'], PKGCONF_PATCH_CUSTOM, ['/^includedir=.*$/m', 'includedir=${prefix}/include/webp']);
$this->cleanLaFiles();
}
}

View File

@@ -57,15 +57,14 @@ trait postgresql
'--with-ssl=openssl ' .
'--with-readline ' .
'--with-libxml ' .
($this->builder->getLib('ldap') ? '--with-ldap ' : '--without-ldap ') .
($this->builder->getLib('icu') ? '--with-icu ' : '--without-icu ') .
'--without-ldap ' .
'--without-libxslt ' .
'--without-lz4 ' .
'--without-zstd ' .
'--without-perl ' .
'--without-python ' .
'--without-pam ' .
'--without-ldap ' .
'--without-bonjour ' .
'--without-tcl '
);

View File

@@ -25,7 +25,8 @@ class BuildCliCommand extends BuildCommand
$this->addOption('build-micro', null, null, 'build micro');
$this->addOption('build-cli', null, null, 'build cli');
$this->addOption('build-fpm', null, null, 'build fpm');
$this->addOption('build-all', null, null, 'build cli, micro, fpm');
$this->addOption('build-embed', null, null, 'build embed');
$this->addOption('build-all', null, null, 'build cli, micro, fpm, embed');
$this->addOption('no-strip', null, null, 'build without strip, in order to debug and load external extensions');
$this->addOption('enable-zts', null, null, 'enable ZTS support');
$this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI');
@@ -40,16 +41,18 @@ class BuildCliCommand extends BuildCommand
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions'))));
$rule = BUILD_TARGET_NONE;
$rule = $rule | ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE);
$rule = $rule | ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE);
$rule = $rule | ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE);
$rule = $rule | ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-cli') ? BUILD_TARGET_CLI : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-micro') ? BUILD_TARGET_MICRO : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-fpm') ? BUILD_TARGET_FPM : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-embed') ? BUILD_TARGET_EMBED : BUILD_TARGET_NONE);
$rule |= ($this->getOption('build-all') ? BUILD_TARGET_ALL : BUILD_TARGET_NONE);
if ($rule === BUILD_TARGET_NONE) {
$this->output->writeln('<error>Please add at least one build target!</error>');
$this->output->writeln("<comment>\t--build-cli\tBuild php-cli SAPI</comment>");
$this->output->writeln("<comment>\t--build-micro\tBuild phpmicro SAPI</comment>");
$this->output->writeln("<comment>\t--build-fpm\tBuild php-fpm SAPI</comment>");
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm</comment>");
$this->output->writeln("<comment>\t--build-embed\tBuild embed SAPI/libphp</comment>");
$this->output->writeln("<comment>\t--build-all\tBuild all SAPI: cli, micro, fpm, embed</comment>");
return static::FAILURE;
}
try {

View File

@@ -20,6 +20,7 @@ class BuildLibsCommand extends BuildCommand
$this->addArgument('libraries', InputArgument::REQUIRED, 'The libraries will be compiled, comma separated');
$this->addOption('clean', null, null, 'Clean old download cache and source before fetch');
$this->addOption('all', 'A', null, 'Build all libs that static-php-cli needed');
$this->addOption('rebuild', 'r', null, 'Delete old build and rebuild');
}
public function initialize(InputInterface $input, OutputInterface $output): void

View File

@@ -1,166 +0,0 @@
<?php
declare(strict_types=1);
namespace SPC\command;
use SPC\store\FileSystem;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\text;
#[AsCommand('deploy', 'Deploy static-php-cli self to an .phar application')]
class DeployCommand extends BaseCommand
{
public function configure(): void
{
$this->addArgument('target', InputArgument::OPTIONAL, 'The file or directory to pack.');
$this->addOption('auto-phar-fix', null, InputOption::VALUE_NONE, 'Automatically fix ini option.');
$this->addOption('overwrite', 'W', InputOption::VALUE_NONE, 'Overwrite existing files.');
$this->addOption('with-no-dev', 'D', InputOption::VALUE_NONE, 'Automatically use non-dev composer dependencies to reduce size');
$this->addOption('with-dev', 'd', InputOption::VALUE_NONE, 'Automatically use dev composer dependencies');
}
/**
* @throws \PharException
*/
public function handle(): int
{
$composer = require ROOT_DIR . '/vendor/composer/installed.php';
if (($composer['root']['dev'] ?? false) === true) {
if (!$this->getOption('with-no-dev')) {
$this->output->writeln('<comment>Current static-php-cli dependencies have installed dev-dependencies</comment>');
$this->output->writeln('<comment>If you want to remove, you can choose "Yes" to run command "composer update --no-dev" to remove.</comment>');
$this->output->writeln('<comment>Or choose "No", just pack, deploy.</comment>');
$ask = confirm('Do you want to remove dev-dependencies to reduce size of phar file?');
} elseif (!$this->getOption('with-dev')) {
$ask = true;
} else {
$ask = false;
}
if ($ask) {
[$code] = shell()->execWithResult('composer update --no-dev');
if ($code !== 0) {
$this->output->writeln('<error>"composer update --no-dev" failed with exit code [' . $code . ']</error>');
$this->output->writeln('<error>You may need to run this command by your own.</error>');
return static::FAILURE;
}
$this->output->writeln('<info>Update successfully, you need to re-run deploy command to pack.</info>');
return static::SUCCESS;
}
}
// 首先得确认是不是关闭了readonly模式
if (ini_get('phar.readonly') == 1) {
if ($this->getOption('auto-phar-fix')) {
$ask = true;
} else {
$this->output->writeln('<comment>pack command needs "phar.readonly" = "Off" !</comment>');
$ask = confirm('Do you want to automatically set it and continue ?');
// $ask = $prompt->requireBool('<comment>pack command needs "phar.readonly" = "Off" !</comment>' . PHP_EOL . 'If you want to automatically set it and continue, just Enter', true);
}
if ($ask) {
global $argv;
$args = array_merge(['-d', 'phar.readonly=0'], $_SERVER['argv'], ['--no-motd']);
if (function_exists('pcntl_exec')) {
$this->output->writeln('<info>Changing to phar.readonly=0 mode ...</info>');
if (pcntl_exec(PHP_BINARY, $args) === false) {
throw new \PharException('Switching to read write mode failed, please check the environment.');
}
} else {
$this->output->writeln('<info>Now running command in child process.</info>');
passthru(PHP_BINARY . ' -d phar.readonly=0 ' . implode(' ', $argv), $retcode);
exit($retcode);
}
}
}
// 获取路径
$path = WORKING_DIR;
// 如果是目录,则将目录下的所有文件打包
$phar_path = text('Please input the phar target filename', default: '/tmp/static-php-cli.phar');
// $phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar');
if (FileSystem::isRelativePath($phar_path)) {
$phar_path = WORKING_DIR . '/' . $phar_path;
}
if (file_exists($phar_path)) {
if (!$this->getOption('overwrite')) {
$this->output->writeln('<comment>The file "' . $phar_path . '" already exists.</comment>');
$ask = confirm('Do you want to overwrite it?');
} else {
$ask = true;
}
if (!$ask) {
$this->output->writeln('<comment>User canceled.</comment>');
return static::FAILURE;
}
@unlink($phar_path);
}
$phar = new \Phar($phar_path);
$phar->startBuffering();
$all = FileSystem::scanDirFiles($path, true, true);
$all = array_filter($all, function ($x) {
$dirs = preg_match('/(^(config|src|vendor)\\/|^(composer\\.json|README\\.md|source\\.json|LICENSE|README-en\\.md)$)/', $x);
return !($dirs !== 1);
});
sort($all);
$map = [];
foreach ($all as $v) {
$map[$v] = $path . '/' . $v;
}
$this->output->writeln('<info>Start packing files...</info>');
try {
foreach ($this->progress()->iterate($map) as $file => $origin_file) {
$phar->addFromString($file, php_strip_whitespace($origin_file));
}
// $phar->buildFromIterator(new SeekableArrayIterator($map, new ProgressBar($output)));
$phar->addFromString(
'.phar-entry.php',
str_replace(
'/../vendor/autoload.php',
'/vendor/autoload.php',
file_get_contents(ROOT_DIR . '/bin/spc')
)
);
$stub = '.phar-entry.php';
$phar->setStub($phar->createDefaultStub($stub));
} catch (\Throwable $e) {
$this->output->writeln($e);
return static::FAILURE;
}
$phar->addFromString('.prod', 'true');
$phar->stopBuffering();
$this->output->writeln(PHP_EOL . 'Done! Phar file is generated at "' . $phar_path . '".');
if (file_exists(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx')) {
$this->output->writeln('Detected you have already compiled micro binary, I will make executable now for you!');
file_put_contents(
pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc',
file_get_contents(SOURCE_PATH . '/php-src/sapi/micro/micro.sfx') .
file_get_contents($phar_path)
);
chmod(pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc', 0755);
$this->output->writeln('<info>Binary Executable: ' . pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc</info>');
}
chmod($phar_path, 0755);
$this->output->writeln('<info>Phar Executable: ' . $phar_path . '</info>');
return static::SUCCESS;
}
private function progress(): ProgressBar
{
$progress = new ProgressBar($this->output, 0);
$progress->setBarCharacter('<fg=green>⚬</>');
$progress->setEmptyBarCharacter('<fg=red>⚬</>');
$progress->setProgressCharacter('<fg=green>➤</>');
$progress->setFormat(
"%current%/%max% [%bar%] %percent:3s%%\n🪅 %estimated:-20s% %memory:20s%" . PHP_EOL
);
return $progress;
}
}

View File

@@ -5,7 +5,11 @@ declare(strict_types=1);
namespace SPC\command;
use SPC\doctor\CheckListHandler;
use SPC\doctor\CheckResult;
use SPC\exception\RuntimeException;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Question\ConfirmationQuestion;
#[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')]
class DoctorCommand extends BaseCommand
@@ -18,14 +22,65 @@ class DoctorCommand extends BaseCommand
public function handle(): int
{
try {
$checker = new CheckListHandler($this->input, $this->output);
$checker->runCheck($this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT);
$checker = new CheckListHandler();
$fix_policy = $this->input->getOption('auto-fix') ? FIX_POLICY_AUTOFIX : FIX_POLICY_PROMPT;
foreach ($checker->runChecks() as $check) {
if ($check->limit_os !== null && $check->limit_os !== PHP_OS_FAMILY) {
continue;
}
$this->output->write('Checking <comment>' . $check->item_name . '</comment> ... ');
$result = call_user_func($check->callback);
if ($result === null) {
$this->output->writeln('skipped');
} elseif ($result instanceof CheckResult) {
if ($result->isOK()) {
$this->output->writeln($result->getMessage() ?? 'ok');
continue;
}
// Failed
$this->output->writeln('<error>' . $result->getMessage() . '</error>');
switch ($fix_policy) {
case FIX_POLICY_DIE:
throw new RuntimeException('Some check items can not be fixed !');
case FIX_POLICY_PROMPT:
if ($result->getFixItem() !== '') {
$helper = new QuestionHelper();
$question = new ConfirmationQuestion('Do you want to fix it? [Y/n] ', true);
if ($helper->ask($this->input, $this->output, $question)) {
$checker->emitFix($this->output, $result);
} else {
throw new RuntimeException('You cancelled fix');
}
} else {
throw new RuntimeException('Some check items can not be fixed !');
}
break;
case FIX_POLICY_AUTOFIX:
if ($result->getFixItem() !== '') {
$this->output->writeln('Automatically fixing ' . $result->getFixItem() . ' ...');
$checker->emitFix($this->output, $result);
} else {
throw new RuntimeException('Some check items can not be fixed !');
}
break;
}
}
}
$this->output->writeln('<info>Doctor check complete !</info>');
} catch (\Throwable $e) {
$this->output->writeln('<error>' . $e->getMessage() . '</error>');
pcntl_signal(SIGINT, SIG_IGN);
return static::FAILURE;
}
return static::SUCCESS;
}
}

View File

@@ -6,6 +6,7 @@ namespace SPC\command\dev;
use SPC\command\BaseCommand;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\util\DependencyUtil;
@@ -18,15 +19,17 @@ class AllExtCommand extends BaseCommand
{
public function configure(): void
{
$this->addArgument('extensions', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Extension name', null);
$this->addArgument('extensions', InputArgument::OPTIONAL, 'List of extensions that will be displayed, comma separated');
}
/**
* @throws FileSystemException
* @throws WrongUsageException
* @throws RuntimeException
*/
public function handle(): int
{
$extensions = $this->input->getArgument('extensions') ?: [];
$extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions') ?? '')));
$style = new SymfonyStyle($this->input, $this->output);
$style->writeln($extensions ? 'Available extensions:' : 'Extensions:');

View File

@@ -9,7 +9,7 @@ use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand('dev:php-ver', 'Dev command')]
#[AsCommand('dev:php-version', 'Returns version of PHP located source directory', ['dev:php-ver'])]
class PhpVerCommand extends BaseCommand
{
public function initialize(InputInterface $input, OutputInterface $output): void
@@ -22,11 +22,19 @@ class PhpVerCommand extends BaseCommand
{
// Find php from source/php-src
$file = SOURCE_PATH . '/php-src/main/php_version.h';
if (!file_exists($file)) {
$this->output->writeln('<error>PHP source not found, maybe you need to extract first ?</error>');
return static::FAILURE;
}
$result = preg_match('/#define PHP_VERSION "([^"]+)"/', file_get_contents($file), $match);
if ($result === false) {
$this->output->writeln('<error>PHP source not found, maybe you need to extract first ?</error>');
return static::FAILURE;
}
$this->output->writeln('<info>' . $match[1] . '</info>');
return static::SUCCESS;
}

View File

@@ -15,7 +15,7 @@ use Symfony\Component\Console\Input\InputArgument;
/**
* Modify config file: sort lib, ext, source by name.
*/
#[AsCommand('dev:sort-config', 'After config edited, sort it by alphabet', ['sort-config'])]
#[AsCommand('dev:sort-config', 'After config edited, sort it by alphabet', ['sort-config'], true)]
class SortConfigCommand extends BaseCommand
{
public function configure(): void
@@ -34,7 +34,7 @@ class SortConfigCommand extends BaseCommand
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true);
ConfigValidator::validateLibs($file);
ksort($file);
if (!file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
if (!file_put_contents(ROOT_DIR . '/config/lib.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n")) {
$this->output->writeln('<error>Write file lib.json failed!</error>');
return static::FAILURE;
}
@@ -43,7 +43,7 @@ class SortConfigCommand extends BaseCommand
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/source.json'), true);
ConfigValidator::validateSource($file);
uksort($file, fn ($a, $b) => $a === 'php-src' ? -1 : ($b === 'php-src' ? 1 : ($a < $b ? -1 : 1)));
if (!file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
if (!file_put_contents(ROOT_DIR . '/config/source.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n")) {
$this->output->writeln('<error>Write file source.json failed!</error>');
return static::FAILURE;
}
@@ -52,7 +52,7 @@ class SortConfigCommand extends BaseCommand
$file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true);
ConfigValidator::validateExts($file);
ksort($file);
if (!file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE))) {
if (!file_put_contents(ROOT_DIR . '/config/ext.json', json_encode($file, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . "\n")) {
$this->output->writeln('<error>Write file ext.json failed!</error>');
return static::FAILURE;
}

View File

@@ -7,105 +7,62 @@ namespace SPC\doctor;
use SPC\exception\FileSystemException;
use SPC\exception\RuntimeException;
use SPC\store\FileSystem;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
class CheckListHandler
final class CheckListHandler
{
/** @var AsCheckItem[] */
private array $check_list = [];
private array $fix_map = [];
public function __construct() {}
/**
* @return array<AsCheckItem>
* @throws \ReflectionException
* @throws RuntimeException
* @throws FileSystemException
* @throws RuntimeException
*/
public function __construct(private readonly InputInterface $input, private readonly OutputInterface $output, bool $include_manual = false)
public function runChecks(bool $include_manual = false): array
{
$this->loadCheckList($include_manual);
return $this->loadCheckList($include_manual);
}
/**
* @throws RuntimeException
*/
public function runSingleCheck(string $item_name, int $fix_policy = FIX_POLICY_DIE): void
public function emitFix(OutputInterface $output, CheckResult $result): void
{
foreach ($this->check_list as $item) {
if ($item->item_name === $item_name) {
$this->check_list = [$item];
break;
}
}
$this->runCheck($fix_policy);
}
pcntl_signal(SIGINT, function () use ($output) {
$output->writeln('<error>You cancelled fix</error>');
});
/**
* @throws RuntimeException
*/
public function runCheck(int $fix_policy = FIX_POLICY_DIE): void
{
foreach ($this->check_list as $item) {
if ($item->limit_os !== null && $item->limit_os !== PHP_OS_FAMILY) {
continue;
}
$this->output->write('Checking <comment>' . $item->item_name . '</comment> ... ');
$result = call_user_func($item->callback);
if ($result === null) {
$this->output->writeln('skipped');
} elseif ($result instanceof CheckResult) {
if ($result->isOK()) {
$this->output->writeln($result->getMessage() ?? 'ok');
continue;
}
// Failed
$this->output->writeln('<error>' . $result->getMessage() . '</error>');
switch ($fix_policy) {
case FIX_POLICY_DIE:
throw new RuntimeException('Some check items can not be fixed !');
case FIX_POLICY_PROMPT:
if ($result->getFixItem() !== '') {
$helper = new QuestionHelper();
$question = new ConfirmationQuestion('Do you want to fix it? [Y/n] ', true);
if ($helper->ask($this->input, $this->output, $question)) {
$this->emitFix($result);
} else {
throw new RuntimeException('You cancelled fix');
}
} else {
throw new RuntimeException('Some check items can not be fixed !');
}
break;
case FIX_POLICY_AUTOFIX:
if ($result->getFixItem() !== '') {
$this->output->writeln('Automatically fixing ' . $result->getFixItem() . ' ...');
$this->emitFix($result);
} else {
throw new RuntimeException('Some check items can not be fixed !');
}
break;
}
}
$fix_result = call_user_func($this->fix_map[$result->getFixItem()], ...$result->getFixParams());
pcntl_signal(SIGINT, SIG_IGN);
if ($fix_result) {
$output->writeln('<info>Fix done</info>');
} else {
$output->writeln('<error>Fix failed</error>');
throw new RuntimeException('Some check item are not fixed');
}
}
/**
* Load Doctor check item list
*
* @return array<AsCheckItem>
* @throws \ReflectionException
* @throws RuntimeException
* @throws FileSystemException
*/
private function loadCheckList(bool $include_manual = false): void
private function loadCheckList(bool $include_manual = false): array
{
foreach (FileSystem::getClassesPsr4(__DIR__ . '/item', 'SPC\\doctor\\item') as $class) {
$ref = new \ReflectionClass($class);
foreach ($ref->getMethods() as $method) {
$attr = $method->getAttributes();
foreach ($attr as $a) {
foreach ($method->getAttributes() as $a) {
if (is_a($a->getName(), AsCheckItem::class, true)) {
/** @var AsCheckItem $instance */
$instance = $a->newInstance();
@@ -126,26 +83,10 @@ class CheckListHandler
}
}
}
// sort check list by level
usort($this->check_list, fn ($a, $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1));
}
/**
* @throws RuntimeException
*/
private function emitFix(CheckResult $result): void
{
pcntl_signal(SIGINT, function () {
$this->output->writeln('<error>You cancelled fix</error>');
});
$fix = $this->fix_map[$result->getFixItem()];
$fix_result = call_user_func($fix, ...$result->getFixParams());
pcntl_signal(SIGINT, SIG_IGN);
if ($fix_result) {
$this->output->writeln('<info>Fix done</info>');
} else {
$this->output->writeln('<error>Fix failed</error>');
throw new RuntimeException('Some check item are not fixed');
}
// sort check list by level
usort($this->check_list, fn (AsCheckItem $a, AsCheckItem $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1));
return $this->check_list;
}
}

View File

@@ -24,7 +24,7 @@ class LinuxMuslCheck
// non-exist, need to recognize distro
$distro = SystemUtil::getOSRelease();
return match ($distro['dist']) {
'ubuntu', 'alpine', 'debian' => CheckResult::fail('musl-libc is not installed on your system', 'fix-musl', [$distro]),
'ubuntu', 'alpine', 'debian', 'rhel', 'almalinux' => CheckResult::fail('musl-libc is not installed on your system', 'fix-musl', [$distro]),
default => CheckResult::fail('musl-libc is not installed on your system'),
};
}
@@ -36,10 +36,18 @@ class LinuxMuslCheck
#[AsFixItem('fix-musl')]
public function fixMusl(array $distro): bool
{
$rhel_install = 'wget https://musl.libc.org/releases/musl-1.2.4.tar.gz && tar -zxvf musl-1.2.4.tar.gz && \
rm -f musl-1.2.4.tar.gz && cd musl-1.2.4 &&
if [[ ! "$PATH" =~ (^|:)"/usr/local/musl/bin"(:|$) ]]; then echo "export PATH=/usr/local/musl/bin:$PATH" >> ~/.bash_profile
fi && \
./configure --enable-wrapper=gcc && \
make -j && make install && cd .. && rm -rf musl-1.2.4';
$install_cmd = match ($distro['dist']) {
'ubuntu', 'debian' => 'apt-get install musl musl-tools -y',
'alpine' => 'apk add musl musl-utils musl-dev',
default => throw new RuntimeException('Current linux distro is not supported for auto-install musl packages'),
'rhel' => $rhel_install,
'almalinux' => $rhel_install,
default => throw new RuntimeException('Current linux distro does not have an auto-install script for musl packages yet.'),
};
$prefix = '';
if (get_current_user() !== 'root') {

View File

@@ -31,14 +31,24 @@ class LinuxToolCheckList
'xz',
];
public const TOOLS_RHEL = [
'perl', 'make', 'bison', 'flex',
'git', 'autoconf', 'automake',
'tar', 'unzip', 'gzip', 'gcc',
'bzip2', 'cmake', 'patch',
'xz', 'wget', // to get musl
];
/** @noinspection PhpUnused */
#[AsCheckItem('if necessary tools are installed', limit_os: 'Linux')]
#[AsCheckItem('if necessary tools are installed', limit_os: 'Linux', level: 999)]
public function checkCliTools(): ?CheckResult
{
$distro = SystemUtil::getOSRelease();
$required = match ($distro['dist']) {
'alpine' => self::TOOLS_ALPINE,
'almalinux' => self::TOOLS_RHEL,
'rhel' => self::TOOLS_RHEL,
default => self::TOOLS_DEBIAN,
};
$missing = [];
@@ -49,7 +59,11 @@ class LinuxToolCheckList
}
if (!empty($missing)) {
return match ($distro['dist']) {
'ubuntu', 'alpine', 'debian' => CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]),
'ubuntu',
'alpine',
'rhel',
'almalinux',
'debian' => CheckResult::fail(implode(', ', $missing) . ' not installed on your system', 'install-linux-tools', [$distro, $missing]),
default => CheckResult::fail(implode(', ', $missing) . ' not installed on your system'),
};
}
@@ -80,7 +94,9 @@ class LinuxToolCheckList
$install_cmd = match ($distro['dist']) {
'ubuntu', 'debian' => 'apt-get install -y',
'alpine' => 'apk add',
default => throw new RuntimeException('Current linux distro is not supported for auto-install musl packages'),
'rhel' => 'dnf install -y',
'almalinux' => 'dnf install -y',
default => throw new RuntimeException('Current linux distro does not have an auto-install script for musl packages yet.'),
};
$prefix = '';
if (get_current_user() !== 'root') {
@@ -88,7 +104,9 @@ class LinuxToolCheckList
logger()->warning('Current user is not root, using sudo for running command');
}
try {
shell(true)->exec($prefix . $install_cmd . ' ' . implode(' ', str_replace('xz', 'xz-utils', $missing)));
$is_rhel = in_array($distro['dist'], ['rhel', 'almalinux']);
$to_install = $is_rhel ? $missing : str_replace('xz', 'xz-utils', $missing);
shell(true)->exec($prefix . $install_cmd . ' ' . implode(' ', $to_install));
} catch (RuntimeException) {
return false;
}

View File

@@ -141,6 +141,8 @@ class SourcePatcher
}
FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', '');
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', '');
// call extension patch before make
foreach ($builder->getExts() as $ext) {
if ($ext->patchBeforeMake() === true) {
@@ -158,6 +160,8 @@ class SourcePatcher
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
$embed_c = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c';
$embed_c_bak = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c.bak';
// Try to reverse backup file
$find_str = 'const char HARDCODED_INI[] =';
@@ -168,13 +172,14 @@ class SourcePatcher
$patch_str = "const char HARDCODED_INI[] =\n{$patch_str}";
// Detect backup, if we have backup, it means we need to reverse first
if (file_exists($cli_c_bak) || file_exists($micro_c_bak)) {
if (file_exists($cli_c_bak) || file_exists($micro_c_bak) || file_exists($embed_c_bak)) {
self::unpatchHardcodedINI();
}
// Backup it
$result = file_put_contents($cli_c_bak, file_get_contents($cli_c));
$result = $result && file_put_contents($micro_c_bak, file_get_contents($micro_c));
$result = $result && file_put_contents($embed_c_bak, file_get_contents($embed_c));
if ($result === false) {
return false;
}
@@ -182,6 +187,7 @@ class SourcePatcher
// Patch it
FileSystem::replaceFileStr($cli_c, $find_str, $patch_str);
FileSystem::replaceFileStr($micro_c, $find_str, $patch_str);
FileSystem::replaceFileStr($embed_c, $find_str, $patch_str);
return true;
}
@@ -191,13 +197,17 @@ class SourcePatcher
$cli_c_bak = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c.bak';
$micro_c = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c';
$micro_c_bak = SOURCE_PATH . '/php-src/sapi/micro/php_micro.c.bak';
if (!file_exists($cli_c_bak) && !file_exists($micro_c_bak)) {
$embed_c = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c';
$embed_c_bak = SOURCE_PATH . '/php-src/sapi/embed/php_embed.c.bak';
if (!file_exists($cli_c_bak) && !file_exists($micro_c_bak) && !file_exists($embed_c_bak)) {
return false;
}
$result = file_put_contents($cli_c, file_get_contents($cli_c_bak));
$result = $result && file_put_contents($micro_c, file_get_contents($micro_c_bak));
$result = $result && file_put_contents($embed_c, file_get_contents($embed_c_bak));
@unlink($cli_c_bak);
@unlink($micro_c_bak);
@unlink($embed_c_bak);
return $result;
}
}

View File

@@ -45,7 +45,6 @@ class PhpSource extends CustomSourceBase
return [
'type' => 'url',
'url' => "https://www.php.net/distributions/php-{$version}.tar.gz",
// 'url' => "https://mirrors.zhamao.xin/php/php-{$version}.tar.gz",
];
}
}

View File

@@ -1,18 +0,0 @@
<?php
declare(strict_types=1);
namespace SPC\util;
class Util
{
/**
* Get current PHP version ID (downloaded)
*/
public static function getPHPVersionID(): int
{
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');
preg_match('/PHP_VERSION_ID (\d+)/', $file, $match);
return intval($match[1]);
}
}

View File

@@ -43,7 +43,8 @@ const BUILD_TARGET_NONE = 0; // no target
const BUILD_TARGET_CLI = 1; // build cli
const BUILD_TARGET_MICRO = 2; // build micro
const BUILD_TARGET_FPM = 4; // build fpm
const BUILD_TARGET_ALL = 7; // build all
const BUILD_TARGET_EMBED = 8; // build embed
const BUILD_TARGET_ALL = 15; // build all
// doctor error fix policy
const FIX_POLICY_DIE = 1; // die directly
@@ -55,6 +56,14 @@ const PKGCONF_PATCH_PREFIX = 1;
const PKGCONF_PATCH_EXEC_PREFIX = 2;
const PKGCONF_PATCH_LIBDIR = 4;
const PKGCONF_PATCH_INCLUDEDIR = 8;
const PKGCONF_PATCH_ALL = 15;
const PKGCONF_PATCH_CUSTOM = 16;
const PKGCONF_PATCH_ALL = 31;
// autoconf flags
const AUTOCONF_LIBS = 1;
const AUTOCONF_CFLAGS = 2;
const AUTOCONF_CPPFLAGS = 4;
const AUTOCONF_LDFLAGS = 8;
const AUTOCONF_ALL = 15;
ConsoleLogger::$date_format = 'H:i:s';

View File

@@ -4,5 +4,5 @@ declare(strict_types=1);
assert(class_exists('\\DOMDocument'));
$doc = new DOMDocument();
$doc->loadHtml("<html><head><meta charset=\"UTF-8\"><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"></head><body id='app'>Hello</body></html>");
$doc->loadHtml('<html><head><meta charset="UTF-8"><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body id="app">Hello</body></html>');
assert($doc->getElementById('app')->nodeValue === 'Hello');

View File

@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace SPC\Tests\doctor;
use PHPUnit\Framework\TestCase;
use SPC\doctor\CheckListHandler;
/**
* @internal
*/
final class CheckListHandlerTest extends TestCase
{
public function testRunChecksReturnsListOfCheck(): void
{
$list = new CheckListHandler();
$this->assertCount(6, $list->runChecks());
}
}