diff --git a/.github/workflows/build-linux-x86_64.yml b/.github/workflows/build-linux-x86_64.yml index 0fbe82d8..90705f33 100644 --- a/.github/workflows/build-linux-x86_64.yml +++ b/.github/workflows/build-linux-x86_64.yml @@ -40,11 +40,6 @@ jobs: steps: - uses: actions/checkout@v3 - # Install Ubuntu missing packages and mark os suffix - - run: | - sudo apt install musl-tools -y - echo "SPC_BUILD_OS=linux" >> $GITHUB_ENV - # Cache composer dependencies - id: cache-composer-deps uses: actions/cache@v3 @@ -54,7 +49,7 @@ jobs: # If there's no Composer cache, install dependencies - if: steps.cache-composer-deps.outputs.cache-hit != 'true' - run: composer update --no-dev + run: composer update --no-dev --classmap-authoritative # Cache downloaded source - id: cache-download @@ -77,10 +72,10 @@ jobs: # If there's no dependencies cache, fetch sources, with or without debug - if: steps.cache-download.outputs.cache-hit != 'true' - run: CACHE_API_EXEC=yes ./bin/spc download --with-php=${{ inputs.version }} --all ${{ env.SPC_BUILD_DEBUG }} + run: CACHE_API_EXEC=yes ./bin/spc-alpine-docker download --with-php=${{ inputs.version }} --all ${{ env.SPC_BUILD_DEBUG }} # Run build command - - run: ./bin/spc build ${{ inputs.extensions }} ${{ env.SPC_BUILD_DEBUG }} ${{ env.SPC_BUILD_CLI }} ${{ env.SPC_BUILD_MICRO }} ${{ env.SPC_BUILD_FPM }} + - run: ./bin/spc-alpine-docker build ${{ inputs.extensions }} ${{ env.SPC_BUILD_DEBUG }} ${{ env.SPC_BUILD_CLI }} ${{ env.SPC_BUILD_MICRO }} ${{ env.SPC_BUILD_FPM }} # Upload cli executable - if: ${{ inputs.build-cli == true }} @@ -115,8 +110,3 @@ jobs: buildroot/build-extensions.json buildroot/build-libraries.json - # Upload downloaded files - - uses: actions/upload-artifact@v3 - with: - name: download-files - path: downloads/ diff --git a/.github/workflows/build-macos-x86_64.yml b/.github/workflows/build-macos-x86_64.yml index d37900ec..6727fcbd 100644 --- a/.github/workflows/build-macos-x86_64.yml +++ b/.github/workflows/build-macos-x86_64.yml @@ -54,7 +54,7 @@ jobs: # If there's no Composer cache, install dependencies - if: steps.cache-composer-deps.outputs.cache-hit != 'true' - run: composer update --no-dev + run: composer update --no-dev --classmap-authoritative # Cache downloaded source - id: cache-download @@ -115,8 +115,3 @@ jobs: buildroot/build-extensions.json buildroot/build-libraries.json - # Upload downloaded files - - uses: actions/upload-artifact@v3 - with: - name: download-files - path: downloads/ diff --git a/.github/workflows/download-cache.yml b/.github/workflows/download-cache.yml new file mode 100644 index 00000000..86b4322e --- /dev/null +++ b/.github/workflows/download-cache.yml @@ -0,0 +1,40 @@ +name: archive download sources weekly + +on: + workflow_dispatch: + schedule: + - cron: "* 14 * * 5" + +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + +jobs: + download: + name: cache download sources + runs-on: ubuntu-latest + strategy: + matrix: + php-version: [ "8.0", "8.1", "8.2" ] + steps: + - uses: actions/checkout@v3 + + # Cache composer dependencies + - id: cache-composer-deps + uses: actions/cache@v3 + with: + path: vendor + key: composer-dependencies + + # If there's no Composer cache, install dependencies + - if: steps.cache-composer-deps.outputs.cache-hit != 'true' + run: composer update --no-dev + + # If there's no dependencies cache, fetch sources, with or without debug + - if: steps.cache-download.outputs.cache-hit != 'true' + run: ./bin/spc download --with-php=${{ matrix.php-version }} --all --debug + + # Upload downloaded files + - uses: actions/upload-artifact@v3 + with: + name: download-files-${{ matrix.php-version }} + path: downloads/ diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml new file mode 100644 index 00000000..cb08ba23 --- /dev/null +++ b/.github/workflows/release-build.yml @@ -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: static-php/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 }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 00000000..d7223f64 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,98 @@ +name: Tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +permissions: + contents: read + +jobs: + php-cs-fixer: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + extensions: curl, openssl, mbstring + ini-values: memory_limit=-1 + tools: pecl, composer, php-cs-fixer + + - name: Run PHP-CS-Fixer fix + run: php-cs-fixer fix --dry-run --diff --ansi + + phpstan: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + extensions: curl, openssl, mbstring + ini-values: memory_limit=-1 + tools: composer + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-phpstan-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-phpstan- + + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Run phpstan + run: vendor/bin/phpstan analyse + + phpunit: + name: PHPUnit (PHP ${{ matrix.php }}) + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + matrix: + include: + - php: '8.1' + - php: '8.2' + fail-fast: false + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: "${{ matrix.php }}" + tools: pecl, composer + extensions: curl, openssl, mbstring + ini-values: memory_limit=-1 + + - name: Cache Composer packages + id: composer-cache + uses: actions/cache@v3 + with: + path: vendor + key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-php- + + - name: Install Dependencies + run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + + - name: Run PHPUnit tests + run: | + vendor/bin/phpunit tests/ --no-coverage diff --git a/.gitignore b/.gitignore index 23162fad..1f749916 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,7 @@ docker/libraries/ docker/extensions/ docker/source/ -# Composer file -composer.lock +# Vendor files /vendor/ # default source extract directory @@ -17,11 +16,15 @@ composer.lock # default source build root directory /buildroot/ -# php cs fixer cache file +# tools cache files .php-cs-fixer.cache +.phpunit.result.cache # exclude self-runtime /bin/* !/bin/spc !/bin/setup-runtime !/bin/spc-alpine-docker + +# default test directory +/tests/var/ diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index e3778666..143407b8 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -65,5 +65,5 @@ return (new PhpCsFixer\Config()) 'phpdoc_var_without_name' => false, ]) ->setFinder( - PhpCsFixer\Finder::create()->in(__DIR__ . '/src') + PhpCsFixer\Finder::create()->in([__DIR__ . '/src', __DIR__ . '/tests/SPC']) ); diff --git a/README-en.md b/README-en.md index 6627e9d9..505fd70a 100755 --- a/README-en.md +++ b/README-en.md @@ -1,241 +1,3 @@ # static-php-cli -Compile A Statically Linked PHP With Swoole and other Extensions. - -Compile a purely static php-cli binary file with various extensions to make PHP applications more portable! (cli SAPI) - -截屏2023-05-02 15 53 13 - -You can also use the micro binary file to combine php binary and php source code into one for distribution! -This feature is provided by [dixyes/phpmicro](https://github.com/dixyes/phpmicro). (micro SAPI) - -截屏2023-05-02 15 52 33 - -[![Version](https://img.shields.io/badge/Version-2.0--rc1-pink.svg?style=flat-square)]() -[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)]() -[![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-linux-x86_64.yml?branch=refactor&label=Linux%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) -[![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-macos-x86_64.yml?branch=refactor&label=macOS%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) - -[![](https://img.shields.io/badge/Extension%20Counter-50+-yellow.svg?style=flat-square)]() -[![](https://img.shields.io/github/search/crazywhalecc/static-php-cli/TODO?label=TODO%20Counter&style=flat-square)]() - -## Compilation Requirements - -Yes, this project is written in PHP, pretty funny. -But static-php-cli runtime only requires an environment above PHP 8.0 and `tokenizer`, `iconv` extension. - -Here is the architecture support status, where `CI` represents support for GitHub Action builds, -`Local` represents support for local builds, and blank represents not currently supported. - -| | x86_64 | aarch64 | -|---------|-----------|-----------| -| macOS | CI, Local | Local | -| Linux | CI, Local | CI, Local | -| Windows | | | - -> macOS-arm64 is not supported for GitHub Actions, if you are going to build on arm, you can build it manually on your own machine. - -Currently supported PHP versions for compilation are: `7.4`, `8.0`, `8.1`, `8.2`. - -## Usage - -Please first select the extension you want to compile based on the extension list below. - -### 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/) - -> 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](/ext-support.md) - -> If there is no extension you need here, you can submit an issue. - -### GitHub Actions Build - -Use GitHub Action to easily build a statically compiled PHP and phpmicro, -and at the same time define the extensions to be compiled by yourself. - -1. Fork me. -2. Go to the Actions of the project and select `CI`. -3. Select `Run workflow`, fill in the PHP version you want to compile, the target type, and the list of extensions. (extensions comma separated, e.g. `bcmath,curl,mbstring`) -4. After waiting for about a period of time, enter the corresponding task and get `Artifacts`. - -If you enable `debug`, all logs will be output at build time, including compiled logs, for troubleshooting. - -- When using ubuntu-latest, it will build linux-x86_64 binary. -- When using macos-latest, it will build macOS-x86_64 binary. - -### Manual Build - -Clone repo first: - -```bash -git clone https://github.com/crazywhalecc/static-php-cli.git -``` - -If you have not installed php on your system, you can download single-file php binary and composer first. - -The PHP runtime for static-php-cli itself will be downloaded at `bin/php`, and composer is at `bin/composer`. - -```bash -cd static-php-cli -chmod +x bin/setup-runtime -./bin/setup-runtime - -# Use this php runtime to run static-php-cli compiler -./bin/php bin/spc - -# Use composer -./bin/php bin/composer -``` - -Basic usage for building php and micro with some extensions: - -```bash -cd static-php-cli -composer update -chmod +x bin/spc -# Check system tool dependencies, fix them automatically (only support macOS) (TODO: Linux distro support) -./bin/spc doctor -# fetch all libraries -./bin/spc fetch --all -# with bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl extension, build both CLI and phpmicro SAPI -./bin/spc build bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl --build-cli --build-micro -``` - -You can also use the parameter `--with-php=x.y` to specify the downloaded PHP version, currently supports 7.4 ~ 8.2: - -```bash -# Using PHP >= 8.0 is recommended, because 7.4 cannot use phpmicro -./bin/spc fetch --with-php=8.2 --all -``` - -Now we support `cli`, `micro`, `fpm`, you can use one or more of the following parameters to specify the compiled SAPI: - -- `--build-cli`: build static cli executable -- `--build-micro`: build static phpmicro self-extracted executable -- `--build-fpm`: build static fpm binary -- `--build-all`: build all - -If anything goes wrong, use `--debug` option to display full terminal output: - -```bash -./bin/spc build openssl,pcntl,mbstring --debug --build-all -./bin/spc fetch --all --debug -``` - -In addition, we build NTS by default. If you are going to build ZTS version, just add `--enable-zts` option. - -```bash -./bin/spc build openssl,pcntl --build-all --enable-zts -``` - -Adding option `--no-strip` can produce binaries with debug symbols, in order to debug (using gdb). Disabling strip will increase the size of static binary. - -### php-cli Usage - -> php-cli is a single static binary, you can use it like normal php installed on your system. - -When using the parameter `--build-cli` or `--build-all`, -the final compilation result will output a binary file named `./php`, -which can be distributed and used directly. -This file will be located in the directory `buildroot/bin/`, copy it out for use. - -```bash -cd buildroot/bin/ -./php -v # check version -./php -m # check extensions -./php your_code.php # run your php code -./php your_project.phar # run your phar (project archive) -``` - -### micro.sfx Usage - -> phpmicro is a SelF-extracted eXecutable SAPI module, -> provided by [dixyes/phpmicro](https://github.com/dixyes/phpmicro). -> It can put php runtime and your source code together. - -When using the parameter `--build-all` or `--build-micro`, -the final compilation result will output a file named `./micro.sfx`, -which needs to be used with your PHP source code like `code.php`. -This file will be located in the path `buildroot/bin/micro.sfx`, simply copy it out for use. - -Prepare your project source code, which can be a single PHP file or a Phar file, for use. - -```bash -echo " code.php -cat micro.sfx code.php > single-app && chmod +x single-app -./single-app - -# If packing a PHAR file, replace code.php with the Phar file path. -``` - -> In some cases, PHAR files may not run in a micro environment. - -### php-fpm Usage - -When using the parameter `--build-all` or `--build-fpm`, -the final compilation result will output a file named `./php-fpm`, -This file will be located in the path `buildroot/bin/`, simply copy it out for use. - -In normal Linux distributions and macOS systems, the package manager will automatically generate a default fpm configuration file after installing php-fpm. -Because php-fpm must specify a configuration file before running, the php-fpm compiled by this project will not have any configuration files, so you need to write `php-fpm.conf` and `pool.conf` configuration files yourself. - -Specifying `php-fpm.conf` can use the command parameter `-y`, for example: `./php-fpm -y php-fpm.conf`. - -## Current Status - -- [X] Basic CLI framework (by `symfony/console`) -- [X] Linux support -- [X] macOS support -- [X] Exception handler -- [ ] Windows support -- [X] PHP 7.4 support -- [X] fpm support - -More functions and features are coming soon, Bugs and TODOs: https://github.com/crazywhalecc/static-php-cli/issues/32 - -## 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. -- 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. -- Naming conventions should be followed, such as using the extension name registered in PHP for the extension name itself, - and external library names should follow the project's own naming conventions. For internal logic functions, class names, variables, etc., - camelCase and underscore formats should be followed, and mixing within the same module is prohibited. -- When compiling external libraries and creating patches, compatibility with different operating systems should be considered. - -## Sponsor this project - -You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md). - -## Open-Source License - -This project is based on the tradition of using the MIT License for old versions, -while the new version references source code from some other projects: - -- [dixyes/lwmbs](https://github.com/dixyes/lwmbs) (Mulun Permissive License) -- [swoole/swoole-cli](https://github.com/swoole/swoole-cli) (Apache 2.0 LICENSE+SWOOLE-CLI LICENSE) - -Due to the special nature of this project, -many other open source projects such as curl and protobuf will be used during the project compilation process, -and they all have their own open source licenses. - -Please use the `bin/spc dump-license` command to export the open source licenses used in the project after compilation, -and comply with the corresponding project's LICENSE. - -## Advanced - -This project is pure open source project, and some modules are separated for developing. - -This section will be improved after refactor version released. +English README has been moved to [README.md](README.md). diff --git a/README-zh.md b/README-zh.md new file mode 100755 index 00000000..33cb1d64 --- /dev/null +++ b/README-zh.md @@ -0,0 +1,252 @@ +# static-php-cli + +Build single static PHP binary, with PHP project together, with popular extensions included. + +🌐 **[中文](README-zh.md)** | **[English](README.md)** + +编译纯静态的 PHP Binary 二进制文件,带有各种扩展,让 PHP-cli 应用变得更便携!(cli SAPI) + +截屏2023-05-02 15 53 13 + +同时可以使用 micro 二进制文件,将 PHP 源码和 PHP 二进制构建为一个文件分发!(micro SAPI) + +截屏2023-05-02 15 52 33 + +> 该 SAPI 源自 [dixyes/phpmicro](https://github.com/dixyes/phpmicro) 的 [Fork 仓库](https://github.com/static-php/phpmicro)。 + +[![Version](https://img.shields.io/badge/Version-2.0--rc8-pink.svg?style=flat-square)]() +[![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)]() +[![](https://img.shields.io/github/actions/workflow/status/static-php/static-php-cli-hosted/build-php-common.yml?branch=refactor&label=Build%20Common%20Extensions&style=flat-square)](https://github.com/static-php/static-php-cli-hosted/actions/workflows/build-php-common.yml) +[![](https://img.shields.io/badge/Extension%20Counter-65+-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 😎 + +## 文档 + +目前 README 编写了基本用法。有关 static-php-cli 所有的功能,请点击这里查看文档:。 + +## 自托管直接下载 + +如果你不想自行编译 PHP,可以从本项目现有的示例 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` + +## 使用 static-php-cli 构建 PHP + +### 编译环境需求 + +是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。 +但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 mbstring、pcntl 扩展和 PHP 版本大于等于 8.1 即可。 + +下面是架构支持情况,:octocat: 代表支持 GitHub Action 构建,:computer: 代表支持本地构建,空 代表暂不支持。 + +| | x86_64 | aarch64 | +|---------|----------------------|----------------------| +| macOS | :octocat: :computer: | :computer: | +| Linux | :octocat: :computer: | :octocat: :computer: | +| Windows | | | +| FreeBSD | :computer: | :computer: | + +> macOS-arm64 因 GitHub 暂未提供 arm runner,如果要构建 arm 二进制,可以使用手动构建。 + +目前支持编译的 PHP 版本为:`7.3`,`7.4`,`8.0`,`8.1`,`8.2`,`8.3`。 + +### 支持的扩展情况 + +请先根据下方扩展列表选择你要编译的扩展。 + +- [扩展支持列表](https://static-php.dev/zh/guide/extensions.html) +- [编译命令生成器](https://static-php.dev/zh/guide/cli-generator.html) + +> 如果这里没有你需要的扩展,可以提交 Issue。 + +### 使用 Actions 构建 + +使用 GitHub Action 可以方便地构建一个静态编译的 PHP,同时可以自行定义要编译的扩展。 + +1. Fork 本项目。 +2. 进入项目的 Actions,选择 CI。 +3. 选择 `Run workflow`,填入你要编译的 PHP 版本、目标类型、扩展列表。(扩展列表使用英文逗号分割,例如 `bcmath,curl,mbstring`) +4. 等待大约一段时间后,进入对应的任务中,获取 `Artifacts`。 + +如果你选择了 `debug`,则会在构建时输出所有日志,包括编译的日志,以供排查错误。 + +### 手动构建(使用 SPC 二进制) + +本项目提供了一个 static-php-cli 的二进制文件,你可以直接下载对应平台的二进制文件,然后使用它来构建静态的 PHP。目前 `spc` 二进制支持的平台有 Linux 和 macOS。 + +下面是从 GitHub Action 下载的方法: + +1. 进入 [GitHub Action](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml)。 +2. 选择一个最新的构建任务,进入后选择 `Artifacts`,下载对应平台的二进制文件。 +3. 解压 `.zip` 文件。解压后,为其添加执行权限:`chmod +x ./spc`。 + +你也可以从自托管的服务器下载二进制文件:[进入](https://dl.static-php.dev/static-php-cli/spc-bin/nightly/)。 + +### 手动构建(使用源码) + +先克隆本项目: + +```bash +git clone https://github.com/crazywhalecc/static-php-cli.git +``` + +如果你本机没有安装 PHP,你需要先使用包管理(例如 brew、apt、yum、apk 等)安装 php。 + +你也可以通过 `bin/setup-runtime` 命令下载静态编译好的 php-cli 和 Composer。下载的 php 和 Composer 将保存为 `bin/php` 和 `bin/composer`。 + +```bash +cd static-php-cli +chmod +x bin/setup-runtime +./bin/setup-runtime + +# 使用独立的 php 运行 static-php-cli +./bin/php bin/spc + +# 使用 composer +./bin/php bin/composer + +# 初始化本项目 +cd static-php-cli +composer update +chmod +x bin/spc +``` + +### 使用 static-php-cli 命令行程序 + +下面是使用 static-php-cli 编译静态 php 和 micro 的基础用法: + +> 如果你使用的是打包好的 `spc` 二进制,你需要将下列命令的 `bin/spc` 替换为 `./spc`。 + +```bash +# 检查环境依赖,并根据提示的命令安装缺失的编译工具 +./bin/spc doctor +# 拉取所有依赖库 +./bin/spc fetch --all +# 只拉取编译指定扩展需要的所有依赖 +./bin/spc download --for-extensions=openssl,pcntl,mbstring,pdo_sqlite +# 构建包含 bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl 扩展的 php-cli 和 micro.sfx +./bin/spc build "bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl" --build-cli --build-micro +``` + +你也可以使用参数 `--with-php=x.y` 来指定下载的 PHP 版本,目前支持 7.3 ~ 8.2: + +```bash +# 优先考虑使用 >= 8.0 的 PHP 版本,因为 phpmicro 不支持在 PHP7 中构建 +./bin/spc download --with-php=8.2 --all +``` + +其中,目前支持构建 cli,micro,fpm 三种静态二进制,使用以下参数的一个或多个来指定编译的 SAPI: + +- `--build-cli`:构建 cli 二进制 +- `--build-micro`:构建 phpmicro 自执行二进制 +- `--build-fpm`:构建 fpm +- `--build-embed`:构建 embed(libphp) +- `--build-all`:构建所有 + +如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误: + +```bash +./bin/spc build openssl,pcntl,mbstring --debug --build-all +./bin/spc fetch --all --debug +``` + +此外,默认编译的 PHP 为 NTS 版本。如需编译线程安全版本(ZTS),只需添加参数 `--enable-zts` 即可。 + +```bash +./bin/spc build openssl,pcntl --build-all --enable-zts +``` + +同时,你也可以使用参数 `--no-strip` 来关闭裁剪,关闭裁剪后可以使用 gdb 等工具调试,但这样会让静态二进制体积变大。 + +## 不同 SAPI 的使用 + +### 使用 cli + +> php-cli 是一个静态的二进制文件,类似 Go、Rust 语言编译后的单个可移植的二进制文件。 + +采用参数 `--build-cli` 或`--build-all` 参数时,最后编译结果会输出一个 `./php` 的二进制文件,此文件可分发、可直接使用。 +该文件编译后会存放在 `buildroot/bin/` 目录中,名称为 `php`,拷贝出来即可。 + +```bash +cd buildroot/bin/ +./php -v # 检查版本 +./php -m # 检查编译的扩展 +./php your_code.php # 运行代码 +./php your_project.phar # 运行打包为 phar 单文件的项目 +``` + +### 使用 micro + +> phpmicro 是一个提供自执行二进制 PHP 的项目,本项目依赖 phpmicro 进行编译自执行二进制。详见 [dixyes/phpmicro](https://github.com/dixyes/phpmicro)。 + +采用项目参数 `--build-micro` 或 `--build-all` 时,最后编译结果会输出一个 `./micro.sfx` 的文件,此文件需要配合你的 PHP 源码使用。 +该文件编译后会存放在 `buildroot/bin/` 目录中,拷贝出来即可。 + +使用时应准备好你的项目源码文件,可以是单个 PHP 文件,也可以是 Phar 文件。 + +```bash +echo " code.php +cat micro.sfx code.php > single-app && chmod +x single-app +./single-app +``` + +如果打包 PHAR 文件,仅需把 code.php 更换为 phar 文件路径即可。 +你可以使用 [box-project/box](https://github.com/box-project/box) 将你的 CLI 项目打包为 Phar, +然后将它与 phpmicro 结合,生成独立可执行的二进制文件。 + +```bash +# 使用 static-php-cli 生成的 micro.sfx 结合,也可以直接使用 cat 命令结合它们 +bin/spc micro:combine my-app.phar +cat buildroot/bin/micro.sfx my-app.phar > my-app && chmod +x my-app + +# 使用 micro:combine 结合可以将 INI 选项注入到二进制中 +bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=system" --output my-app-2 +``` + +> 有些情况下的 phar 文件或 PHP 项目可能无法在 micro 环境下运行。 + +### 使用 fpm + +采用项目参数 `--build-fpm` 或 `--build-all` 时,最后编译结果会输出一个 `./php-fpm` 的文件。 +该文件存放在 `buildroot/bin/` 目录,拷贝出来即可使用。 + +在正常的 Linux 发行版和 macOS 系统中,安装 php-fpm 后包管理会自动生成默认的 fpm 配置文件。 +因为 php-fpm 必须指定配置文件才可启动,本项目编译的 php-fpm 不会带任何配置文件,所以需自行编写 `php-fpm.conf` 和 `pool.conf` 配置文件。 + +指定 `php-fpm.conf` 可以使用命令参数 `-y`,例如:`./php-fpm -y php-fpm.conf`。 + +### 使用 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。 + +另外,添加新扩展的贡献方式,可以参考下方 `进阶`。 + +如果你想贡献文档内容,请到项目仓库 [static-php/static-php-cli-docs](https://github.com/static-php/static-php-cli-docs) 贡献。 + +## 赞助本项目 + +你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。 + +## 开源协议 + +本项目依据旧版本惯例采用 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) + +因本项目的特殊性,使用项目编译过程中会使用很多其他开源项目,例如 curl、protobuf 等,它们都有各自的开源协议。 +请在编译完成后,使用命令 `bin/spc dump-license` 导出项目使用项目的开源协议,并遵守对应项目的 LICENSE。 + +## 进阶 + +本项目重构分支为模块化编写。如果你对本项目感兴趣,想加入开发,可以参照文档的 [贡献指南](https://static-php.dev) 贡献代码或文档。 diff --git a/README.md b/README.md index d4f436ff..c89abc91 100755 --- a/README.md +++ b/README.md @@ -1,221 +1,292 @@ # static-php-cli -Compile A Statically Linked PHP With Swoole and other Extensions. +Build single static PHP binary, with PHP project together, with popular extensions included. -If you are using English, see [English README](README-en.md). +🌐 **[中文](README-zh.md)** | **[English](README.md)** -编译纯静态的 PHP Binary 二进制文件,带有各种扩展,让 PHP-cli 应用变得更便携!(cli SAPI) +The project name is static-php-cli, but it actually supports cli, fpm, micro and embed SAPI 😎 -截屏2023-05-02 15 53 13 +Compile a purely static php-cli binary file with various extensions to make PHP applications more portable! (cli SAPI) -同时可以使用 micro 二进制文件,将 PHP 源码和 PHP 二进制构建为一个文件分发!(由 [dixyes/phpmicro](https://github.com/dixyes/phpmicro) 提供支持)(micro SAPI) +2023-05-02 15 53 13 -截屏2023-05-02 15 52 33 +You can also use the micro binary file to combine php binary and php source code into one for distribution! (micro SAPI) -[![Version](https://img.shields.io/badge/Version-2.0--rc1-pink.svg?style=flat-square)]() +2023-05-02 15 52 33 + +> This SAPI feature is from the [Fork](https://github.com/static-php/phpmicro) of [dixyes/phpmicro](https://github.com/dixyes/phpmicro). + +[![Version](https://img.shields.io/badge/Version-2.0--rc8-pink.svg?style=flat-square)]() [![License](https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square)]() [![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-linux-x86_64.yml?branch=refactor&label=Linux%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) [![](https://img.shields.io/github/actions/workflow/status/crazywhalecc/static-php-cli/build-macos-x86_64.yml?branch=refactor&label=macOS%20Build&style=flat-square)](https://github.com/crazywhalecc/static-php-cli/actions/workflows/build.yml) -[![](https://img.shields.io/badge/Extension%20Counter-50+-yellow.svg?style=flat-square)]() + +[![](https://img.shields.io/badge/Extension%20Counter-65+-yellow.svg?style=flat-square)]() [![](https://img.shields.io/github/search/crazywhalecc/static-php-cli/TODO?label=TODO%20Counter&style=flat-square)]() -## 编译环境需求 +## Docs -是的,本项目采用 PHP 编写,编译前需要一个 PHP 环境,比较滑稽。 -但本项目默认可通过自身构建的 micro 和 static-php 二进制运行,其他只需要包含 tokenizer 扩展和 PHP 版本大于等于 8.0 即可。 +The current README contains basic usage. For all the features of static-php-cli, +see . -下面是架构支持情况,`CI` 代表支持 GitHub Action 构建,`Local` 代表支持本地构建,空 代表暂不支持。 +## Direct Download -| | x86_64 | aarch64 | -|---------|-----------|-----------| -| macOS | CI, Local | Local | -| Linux | CI, Local | CI, Local | -| Windows | | | +If you don't want to compile yourself, you can download example pre-compiled artifact from [Actions](https://github.com/static-php/static-php-cli-hosted/actions/workflows/build-php-common.yml), or from [self-hosted server](https://dl.static-php.dev/static-php-cli/common/). -> macOS-arm64 因 GitHub 暂未提供 arm runner,如果要构建 arm 二进制,可以使用手动构建。 +> 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` -目前支持编译的 PHP 版本为:`7.4`,`8.0`,`8.1`,`8.2`。 +## Use static-php-cli to build PHP -## 使用 +### Compilation Requirements -请先根据下方扩展列表选择你要编译的扩展。 +Yes, this project is written in PHP, pretty funny. +But static-php-cli runtime only requires an environment above PHP 8.1 and `mbstring`, `pcntl` extension. -### 自托管直接下载 +Here is the architecture support status, where :octocat: represents support for GitHub Action builds, +:computer: represents support for local manual builds, and blank represents not currently supported. -如果你不想自行编译,可以从本项目现有的 Action 下载 Artifact,也可以从自托管的服务器下载:[进入](https://dl.zhamao.xin/static-php-cli/) +| | x86_64 | aarch64 | +|---------|----------------------|----------------------| +| macOS | :octocat: :computer: | :computer: | +| Linux | :octocat: :computer: | :octocat: :computer: | +| Windows | | | +| FreeBSD | :computer: | :computer: | -> 自托管的服务器默认包含的扩展有:`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` +> macOS-arm64 is not supported for GitHub Actions, if you are going to build on arm, you can build it manually on your own machine. -### 支持的扩展情况 +Currently supported PHP versions for compilation are: `7.3`, `7.4`, `8.0`, `8.1`, `8.2`, `8.3`. -[扩展支持列表](/ext-support.md) +### Supported Extensions -> 如果这里没有你需要的扩展,可以提交 Issue。 +Please first select the extension you want to compile based on the extension list below. -### 使用 Actions 构建 +- [Supported Extension List](https://static-php.dev/en/guide/extensions.html) +- [Command Generator](https://static-php.dev/en/guide/cli-generator.html) -使用 GitHub Action 可以方便地构建一个静态编译的 PHP 和 phpmicro,同时可以自行定义要编译的扩展。 +> If an extension you need is missing, you can submit an issue. -1. Fork 本项目。 -2. 进入项目的 Actions,选择 CI。 -3. 选择 `Run workflow`,填入你要编译的 PHP 版本、目标类型、扩展列表。(扩展列表使用英文逗号分割,例如 `bcmath,curl,mbstring`) -4. 等待大约一段时间后,进入对应的任务中,获取 `Artifacts`。 +### GitHub Actions Build -如果你选择了 `debug`,则会在构建时输出所有日志,包括编译的日志,以供排查错误。 +Use GitHub Action to easily build a statically compiled PHP, +and at the same time define the extensions to be compiled by yourself. -### 手动构建 +1. Fork me. +2. Go to the Actions of the project and select `CI`. +3. Select `Run workflow`, fill in the PHP version you want to compile, the target type, and the list of extensions. (extensions comma separated, e.g. `bcmath,curl,mbstring`) +4. After waiting for about a period of time, enter the corresponding task and get `Artifacts`. -先克隆本项目: +If you enable `debug`, all logs will be output at build time, including compiled logs, for troubleshooting. + +- When using ubuntu-latest, it will build linux-x86_64 binary. +- When using macos-latest, it will build macOS-x86_64 binary. + +### Manual build (using SPC binary) + +This project provides a binary file of static-php-cli. +You can directly download the binary file of the corresponding platform and then use it to build static PHP. +Currently, the platforms supported by `spc` binary are Linux and macOS. + +Here's how to download from GitHub Actions: + +1. Enter [GitHub Actions](https://github.com/crazywhalecc/static-php-cli/actions/workflows/release-build.yml). +2. Select the latest build task, select `Artifacts`, and download the binary file of the corresponding platform. +3. Unzip the `.zip` file. After decompressing, add execution permissions to it: `chmod +x ./spc`. + +You can also download binaries from a self-hosted server: [enter](https://dl.static-php.dev/static-php-cli/spc-bin/nightly/). + +> SPC single-file binary is built by phpmicro and box. + +### Manual build (using source code) + +Clone repo first: ```bash git clone https://github.com/crazywhalecc/static-php-cli.git ``` -如果你本机没有安装 PHP,你可以通过命令下载静态编译好的 php-cli 和 Composer。 +If you have not installed php on your system, you can use package management to install PHP (such as brew, apt, yum, apk etc.). -下载的 php 和 Composer 将保存为 `bin/php` 和 `bin/composer`。 +And you can also download single-file php binary and composer using command `bin/setup-runtime`. +The PHP runtime for static-php-cli itself will be downloaded at `bin/php`, and composer is at `bin/composer`. ```bash cd static-php-cli chmod +x bin/setup-runtime +# It will download php-cli from self-hosted server and composer from getcomposer.org ./bin/setup-runtime -# 使用独立的 php 运行 static-php-cli +# Use this php runtime to run static-php-cli compiler ./bin/php bin/spc -# 使用 composer +# Use composer ./bin/php bin/composer -``` -下面是使用 static-php-cli 编译静态 php 和 micro 的基础用法: - -```bash -# 克隆本项目 +# Initialize this project cd static-php-cli composer update chmod +x bin/spc -# 检查环境依赖,并根据提示的命令安装缺失的编译工具(目前仅支持 macOS,Linux 后续会支持) -./bin/spc doctor -# 拉取所有依赖库 -./bin/spc fetch --all -# 构建包含 bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl 扩展的 php-cli 和 micro.sfx -./bin/spc build "bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl" --build-cli --build-micro ``` -你也可以使用参数 `--with-php=x.y` 来指定下载的 PHP 版本,目前支持 7.4 ~ 8.2: +### Use static-php-cli + +Basic usage for building php and micro with some extensions: + +> If you are using the packaged `spc` binary, you need to replace `bin/spc` with `./spc` in the following commands. ```bash -# 优先考虑使用 >= 8.0 的 PHP 版本 +# Check system tool dependencies, fix them automatically +./bin/spc doctor +# fetch all libraries +./bin/spc download --all +# only fetch necessary sources by needed extensions +./bin/spc download --for-extensions=openssl,pcntl,mbstring,pdo_sqlite +# with bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl extension, build both CLI and phpmicro SAPI +./bin/spc build bcmath,openssl,tokenizer,sqlite3,pdo_sqlite,ftp,curl --build-cli --build-micro +``` + +You can also use the parameter `--with-php=x.y` to specify the downloaded PHP version, currently supports 7.4 ~ 8.2: + +```bash +# Using PHP >= 8.0 is recommended, because PHP7 cannot use phpmicro ./bin/spc fetch --with-php=8.2 --all ``` -其中,目前支持构建 cli,micro,fpm 三种静态二进制,使用以下参数的一个或多个来指定编译的 SAPI: +Now we support `cli`, `micro`, `fpm`, you can use one or more of the following parameters to specify the compiled SAPI: -- `--build-cli`:构建 cli 二进制 -- `--build-micro`:构建 phpmicro 自执行二进制 -- `--build-fpm`:构建 fpm -- `--build-all`:构建所有 +- `--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 -如果出现了任何错误,可以使用 `--debug` 参数来展示完整的输出日志,以供排查错误: +If anything goes wrong, use `--debug` option to display full terminal output: ```bash ./bin/spc build openssl,pcntl,mbstring --debug --build-all ./bin/spc fetch --all --debug ``` -此外,默认编译的 PHP 为 NTS 版本。如需编译线程安全版本(ZTS),只需添加参数 `--enable-zts` 即可。 +In addition, we build NTS by default. If you are going to build ZTS version, just add `--enable-zts` option. ```bash ./bin/spc build openssl,pcntl --build-all --enable-zts ``` -同时,你也可以使用参数 `--no-strip` 来关闭裁剪,关闭裁剪后可以使用 gdb 等工具调试,但这样会让静态二进制体积变大。 +Adding option `--no-strip` can produce binaries with debug symbols, in order to debug (using gdb). Disabling strip will increase the size of static binary. -### 使用 php-cli +## Different SAPI Usage -> php-cli 是一个静态的二进制文件,类似 Go、Rust 语言编译后的单个可移植的二进制文件。 +### Use cli -采用参数 `--build-cli` 或`--build-all` 参数时,最后编译结果会输出一个 `./php` 的二进制文件,此文件可分发、可直接使用。 -该文件编译后会存放在 `buildroot/bin/` 目录中,名称为 `php`,拷贝出来即可。 +> php-cli is a single static binary, you can use it like normal php installed on your system. + +When using the parameter `--build-cli` or `--build-all`, +the final compilation result will output a binary file named `./php`, +which can be distributed and used directly. +This file will be located in the directory `buildroot/bin/`, copy it out for use. ```bash cd buildroot/bin/ -./php -v # 检查版本 -./php -m # 检查编译的扩展 -./php your_code.php # 运行代码 -./php your_project.phar # 运行打包为 phar 单文件的项目 +./php -v # check version +./php -m # check extensions +./php your_code.php # run your php code +./php your_project.phar # run your phar (project archive) ``` -### 使用 micro.sfx +### Use micro -> phpmicro 是一个提供自执行二进制 PHP 的项目,本项目依赖 phpmicro 进行编译自执行二进制。详见 [dixyes/phpmicro](https://github.com/dixyes/phpmicro)。 +> phpmicro is a SelF-extracted eXecutable SAPI module, +> provided by [phpmicro](https://github.com/dixyes/phpmicro) project. +> But this project is using a [fork](https://github.com/static-php/phpmicro) of phpmicro, because we need to add some features to it. +> It can put php runtime and your source code together. -采用项目参数 `--build-micro` 或 `--build-all` 时,最后编译结果会输出一个 `./micro.sfx` 的文件,此文件需要配合你的 PHP 源码使用。 -该文件编译后会存放在 `buildroot/bin/` 目录中,拷贝出来即可。 +When using the parameter `--build-all` or `--build-micro`, +the final compilation result will output a file named `./micro.sfx`, +which needs to be used with your PHP source code like `code.php`. +This file will be located in the path `buildroot/bin/micro.sfx`, simply copy it out for use. -使用时应准备好你的项目源码文件,可以是单个 PHP 文件,也可以是 Phar 文件。 +Prepare your project source code, which can be a single PHP file or a Phar file, for use. ```bash echo " code.php cat micro.sfx code.php > single-app && chmod +x single-app ./single-app - -# 如果打包 PHAR 文件,仅需把 code.php 更换为 phar 文件路径即可 ``` -> 有些情况下的 phar 文件可能无法在 micro 环境下运行。 +If you package a PHAR file, just replace `code.php` with the phar file path. +You can use [box-project/box](https://github.com/box-project/box) to package your CLI project as Phar, +It is then combined with phpmicro to produce a standalone executable binary. -### 使用 php-fpm +```bash +# Use the micro.sfx generated by static-php-cli to combine, +bin/spc micro:combine my-app.phar +# or you can directly use the cat command to combine them. +cat buildroot/bin/micro.sfx my-app.phar > my-app && chmod +x my-app -采用项目参数 `--build-fpm` 或 `--build-all` 时,最后编译结果会输出一个 `./php-fpm` 的文件。 -该文件存放在 `buildroot/bin/` 目录,拷贝出来即可使用。 +# Use micro:combine combination to inject INI options into the binary. +bin/spc micro:combine my-app.phar -I "memory_limit=4G" -I "disable_functions=system" --output my-app-2 +``` -在正常的 Linux 发行版和 macOS 系统中,安装 php-fpm 后包管理会自动生成默认的 fpm 配置文件。 -因为 php-fpm 必须指定配置文件才可启动,本项目编译的 php-fpm 不会带任何配置文件,所以需自行编写 `php-fpm.conf` 和 `pool.conf` 配置文件。 +> In some cases, PHAR files may not run in a micro environment. -指定 `php-fpm.conf` 可以使用命令参数 `-y`,例如:`./php-fpm -y php-fpm.conf`。 +### Use fpm -## 项目支持情况 +When using the parameter `--build-all` or `--build-fpm`, +the final compilation result will output a file named `./php-fpm`, +This file will be located in the path `buildroot/bin/`, simply copy it out for use. -- [X] 基础结构编写(采用 `symfony/console`) -- [X] 错误处理 -- [X] macOS 支持 -- [ ] Windows 支持 -- [X] Linux 支持 -- [X] PHP 7.4 支持 -- [X] fpm 支持 +In normal Linux distributions and macOS systems, the package manager will automatically generate a default fpm configuration file after installing php-fpm. +Because php-fpm must specify a configuration file before running, the php-fpm compiled by this project will not have any configuration files, so you need to write `php-fpm.conf` and `pool.conf` configuration files yourself. -更多功能和特性正在陆续支持中,详见:https://github.com/crazywhalecc/static-php-cli/issues/32 +Specifying `php-fpm.conf` can use the command parameter `-y`, for example: `./php-fpm -y php-fpm.conf`. -## 贡献 +### Use embed -目前支持的扩展较少,如果缺少你需要的扩展,可发起 Issue。如果你对本项目较熟悉,也欢迎为本项目发起 Pull Request。 +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. -- 项目采用了 php-cs-fixer、phpstan 作为代码规范工具,贡献前请对更新的代码执行 `composer analyze` 和 `composer cs-fix`。 -- 涉及到其他开源库的部分应提供对应库的协议,同时对配置文件在修改后采用命令 `sort-config` 排序。有关排序的命令,见文档。 -- 应遵循命名规范,例如扩展名称应采取 PHP 内注册的扩展名本身,外部库名应遵循项目本身的名称,内部逻辑的函数、类名、变量等应遵循驼峰、下划线等格式,禁止同一模块混用。 -- 涉及编译外部库的命令和 Patch 时应注意兼容不同操作系统。 +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 -## 赞助本项目 +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. -你可以在 [我的个人赞助页](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md) 支持我和我的项目。 +If you want to contribute documentation, please go to [static-php/static-php-cli-docs](https://github.com/static-php/static-php-cli-docs). -## 开源协议 +Now there is a [static-php](https://github.com/static-php) organization, which is used to store the repo related to the project. -本项目依据旧版本惯例采用 MIT License 开源,自身的部分代码引用或修改自以下项目: +Part of the English document is written by me, and part is translated by Google, +and there may be inaccurate descriptions, strange or offensive expressions. +If you are a native English speaker, some corrections to the documentation are welcome. -- [dixyes/lwmbs](https://github.com/dixyes/lwmbs)(木兰宽松许可证) -- [swoole/swoole-cli](https://github.com/swoole/swoole-cli)(Apache 2.0 LICENSE、SWOOLE-CLI LICENSE) +## Sponsor this project -因本项目的特殊性,使用项目编译过程中会使用很多其他开源项目,例如 curl、protobuf 等,它们都有各自的开源协议。 -请在编译完成后,使用命令 `bin/spc dump-license` 导出项目使用项目的开源协议,并遵守对应项目的 LICENSE。 +You can sponsor my project on [this page](https://github.com/crazywhalecc/crazywhalecc/blob/master/FUNDING.md). -## 进阶 +## Open-Source License -本项目重构分支为模块化编写。 +This project itself is based on MIT License, +some newly added extensions and dependencies may originate from the following projects (including but not limited to), +and the headers of these code files will also be given additional instructions LICENSE and AUTHOR: -TODO:这部分将在基础功能完成后编写完成。 +- [dixyes/lwmbs](https://github.com/dixyes/lwmbs) (Mulun Permissive License) +- [swoole/swoole-cli](https://github.com/swoole/swoole-cli) (Apache 2.0 LICENSE+SWOOLE-CLI LICENSE) + +Due to the special nature of this project, +many other open source projects such as curl and protobuf will be used during the project compilation process, +and they all have their own open source licenses. + +Please use the `bin/spc dump-license` command to export the open source licenses used in the project after compilation, +and comply with the corresponding project's LICENSE. + +## Advanced + +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.dev) of the documentation to contribute code or documentation. diff --git a/bin/setup-runtime b/bin/setup-runtime index 48144f97..bd7144b7 100755 --- a/bin/setup-runtime +++ b/bin/setup-runtime @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # set error-quit, verbose, non-variable-quit set -eu @@ -22,10 +22,10 @@ esac # set project dir __DIR__=$(cd "$(dirname "$0")" && pwd) -__PROJECT__=$(cd ${__DIR__}/../ && 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,23 +46,27 @@ 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" ;; esac -test -d ${__PROJECT__}/downloads || mkdir ${__PROJECT__}/downloads +if ! command -v curl > /dev/null && command -v apk > /dev/null; then + apk add --no-cache curl +fi + +test -d "${__PROJECT__}"/downloads || mkdir "${__PROJECT__}"/downloads # download static php binary -test -f ${__PROJECT__}/downloads/runtime.tar.gz || { echo "Downloading $__PHP_RUNTIME_URL__ ..." && curl -#fSL -o ${__PROJECT__}/downloads/runtime.tar.gz "$__PHP_RUNTIME_URL__" ; } -test -f ${__DIR__}/php || { tar -xf ${__PROJECT__}/downloads/runtime.tar.gz -C ${__DIR__}/ ; } -chmod +x ${__DIR__}/php +test -f "${__PROJECT__}"/downloads/runtime.tar.gz || { echo "Downloading $__PHP_RUNTIME_URL__ ..." && curl -#fSL -o "${__PROJECT__}"/downloads/runtime.tar.gz "$__PHP_RUNTIME_URL__" ; } +test -f "${__DIR__}"/php || { tar -xf "${__PROJECT__}"/downloads/runtime.tar.gz -C "${__DIR__}"/ ; } +chmod +x "${__DIR__}"/php # download composer -test -f ${__DIR__}/composer || curl -#fSL -o ${__DIR__}/composer "$__COMPOSER_URL__" -chmod +x ${__DIR__}/composer +test -f "${__DIR__}"/composer || curl -#fSL -o "${__DIR__}"/composer "$__COMPOSER_URL__" +chmod +x "${__DIR__}"/composer # sanity check for php and composer -${__DIR__}/php -v >/dev/null || { echo "Failed to run php" && exit 1; } -${__DIR__}/php ${__DIR__}/composer --version >/dev/null || { echo "Failed to run composer" && exit 1; } +"${__DIR__}"/php -v >/dev/null || { echo "Failed to run php" && exit 1; } +"${__DIR__}"/php "${__DIR__}"/composer --version >/dev/null || { echo "Failed to run composer" && exit 1; } echo "Setup runtime OK!" echo "runtime bin path needs to add manually by command below:" diff --git a/bin/spc b/bin/spc index 960f24ff..58749f61 100755 --- a/bin/spc +++ b/bin/spc @@ -1,6 +1,9 @@ #!/usr/bin/env php run(); + (new ConsoleApplication())->run(); } catch (Exception $e) { - \SPC\exception\ExceptionHandler::getInstance()->handle($e); + ExceptionHandler::getInstance()->handle($e); } diff --git a/bin/spc-alpine-docker b/bin/spc-alpine-docker index 7f444426..e807a2df 100755 --- a/bin/spc-alpine-docker +++ b/bin/spc-alpine-docker @@ -2,18 +2,18 @@ # This file is using docker to run commands -self_dir=$(cd "$(dirname "$0")";pwd) - # Detect docker can run if ! which docker >/dev/null; then echo "Docker is not installed, please install docker first !" exit 1 fi DOCKER_EXECUTABLE="docker" +# shellcheck disable=SC2046 if [ $(id -u) -ne 0 ]; then if ! docker info > /dev/null 2>&1; then if [ "$SPC_USE_SUDO" != "yes" ]; then echo "Docker command requires sudo" + # shellcheck disable=SC2039 echo -n 'To use sudo to run docker, run "export SPC_USE_SUDO=yes" and run command again' exit 1 fi @@ -33,6 +33,7 @@ x86_64) ;; aarch64) ALPINE_FROM=multiarch/alpine:aarch64-edge + # shellcheck disable=SC2039 echo -e "\e[033m* Using different arch needs to setup qemu-static for docker !\e[0m" $DOCKER_EXECUTABLE run --rm --privileged multiarch/qemu-user-static:register --reset > /dev/null ;; @@ -51,24 +52,52 @@ fi # Detect docker env is setup if ! $DOCKER_EXECUTABLE images | grep -q cwcc-spc-$SPC_USE_ARCH; then echo "Docker container does not exist. Building docker image ..." - ALPINE_DOCKERFILE=$(cat << EOF + $DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH -f- . < $(pwd)/Dockerfile - - $DOCKER_EXECUTABLE build -t cwcc-spc-$SPC_USE_ARCH . - rm $(pwd)/Dockerfile fi # Check if in ci (local terminal can execute with -it) @@ -79,4 +108,5 @@ else fi # Run docker -$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT=$(pwd) -v $(pwd)/config:/app/config -v $(pwd)/src:/app/src -v $(pwd)/buildroot:/app/buildroot -v $(pwd)/source:/app/source -v $(pwd)/downloads:/app/downloads cwcc-spc-$SPC_USE_ARCH bin/spc $@ +# shellcheck disable=SC2068 +$DOCKER_EXECUTABLE run --rm $INTERACT -e SPC_FIX_DEPLOY_ROOT="$(pwd)" -v "$(pwd)"/config:/app/config -v "$(pwd)"/src:/app/src -v "$(pwd)"/buildroot:/app/buildroot -v "$(pwd)"/source:/app/source -v "$(pwd)"/downloads:/app/downloads cwcc-spc-$SPC_USE_ARCH bin/spc $@ diff --git a/box.json b/box.json new file mode 100644 index 00000000..83904e36 --- /dev/null +++ b/box.json @@ -0,0 +1,19 @@ +{ + "alias": "spc-php.phar", + "banner": false, + "blacklist": [ + ".github" + ], + "directories": [ + "config", + "src", + "vendor/psr", + "vendor/laravel/prompts", + "vendor/illuminate", + "vendor/symfony", + "vendor/zhamao" + ], + "git-commit-short": "git_commit_short", + "metadata": "ConsoleApplication::VERSION", + "output": "spc.phar" +} diff --git a/composer.json b/composer.json index c7b9f9f9..2a8b4c96 100644 --- a/composer.json +++ b/composer.json @@ -9,20 +9,21 @@ } ], "require": { - "php": ">= 8.0", - "ext-tokenizer": "*", - "ext-iconv": "*", - "symfony/console": "^6 || ^5 || ^4", - "zhamao/logger": "^1.0", - "crazywhalecc/cli-helper": "^0.1.0", - "nunomaduro/collision": "*", - "ext-pcntl": "*" + "php": ">= 8.1", + "ext-mbstring": "*", + "ext-pcntl": "*", + "laravel/prompts": "^0.1.12", + "symfony/console": "^5.4 || ^6 || ^7", + "zhamao/logger": "^1.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2 != 3.7.0", - "phpstan/phpstan": "^1.1", "captainhook/captainhook": "^5.10", - "captainhook/plugin-composer": "^5.3" + "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" }, "autoload": { "psr-4": { @@ -33,13 +34,19 @@ "src/globals/functions.php" ] }, + "autoload-dev": { + "psr-4": { + "SPC\\Tests\\": "tests/SPC" + } + }, "bin": [ "bin/spc" ], "scripts": { "analyse": "phpstan analyse --memory-limit 300M", "cs-fix": "php-cs-fixer fix", - "test": "bin/phpunit --no-coverage" + "test": "vendor/bin/phpunit tests/ --no-coverage", + "build:phar": "vendor/bin/box compile" }, "config": { "allow-plugins": { diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000..109c39b3 --- /dev/null +++ b/composer.lock @@ -0,0 +1,6030 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "198b6207aabb33778c51282cb7aa0c52", + "packages": [ + { + "name": "illuminate/collections", + "version": "v10.29.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "bb8784ce913bd46f944b4bd67cd857f40d9cfe68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/bb8784ce913bd46f944b4bd67cd857f40d9cfe68", + "reference": "bb8784ce913bd46f944b4bd67cd857f40d9cfe68", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "php": "^8.1" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-10-10T12:55:25+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v10.29.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/d0958e4741fc9d6f516a552060fd1b829a85e009", + "reference": "d0958e4741fc9d6f516a552060fd1b829a85e009", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-02-03T08:06:17+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v10.29.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "6c39fba7b2311e28f5c6ac7d729e3d49a2a98406" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/6c39fba7b2311e28f5c6ac7d729e3d49a2a98406", + "reference": "6c39fba7b2311e28f5c6ac7d729e3d49a2a98406", + "shasum": "" + }, + "require": { + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-09-05T19:07:46+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v10.29.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-05T12:46:42+00:00" + }, + { + "name": "laravel/prompts", + "version": "v0.1.12", + "source": { + "type": "git", + "url": "https://github.com/laravel/prompts.git", + "reference": "b35f249028c22016e45e48626e19e5d42fd827ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/prompts/zipball/b35f249028c22016e45e48626e19e5d42fd827ff", + "reference": "b35f249028c22016e45e48626e19e5d42fd827ff", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "illuminate/collections": "^10.0|^11.0", + "php": "^8.1", + "symfony/console": "^6.2|^7.0" + }, + "conflict": { + "illuminate/console": ">=10.17.0 <10.25.0", + "laravel/framework": ">=10.17.0 <10.25.0" + }, + "require-dev": { + "mockery/mockery": "^1.5", + "pestphp/pest": "^2.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-mockery": "^1.1" + }, + "suggest": { + "ext-pcntl": "Required for the spinner to be animated." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.x-dev" + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Laravel\\Prompts\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "support": { + "issues": "https://github.com/laravel/prompts/issues", + "source": "https://github.com/laravel/prompts/tree/v0.1.12" + }, + "time": "2023-10-18T14:18:57+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "symfony/console", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/eca495f2ee845130855ddf1cf18460c38966c8b6", + "reference": "eca495f2ee845130855ddf1cf18460c38966c8b6", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/event-dispatcher": "^5.4|^6.0", + "symfony/lock": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/var-dumper": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v6.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-16T10:10:12+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf", + "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "875e90aeea2777b6f135677f618529449334a612" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", + "reference": "875e90aeea2777b6f135677f618529449334a612", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-01-26T09:26:14+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.28.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "42292d99c55abe617799667f454222c54c60e229" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", + "reference": "42292d99c55abe617799667f454222c54c60e229", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.28-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-28T09:04:16+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "reference": "40da9cc13ec349d9e4966ce18b5fbcd724ab10a4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/string", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/13d76d0fb049051ed12a04bef4f9de8715bea339", + "reference": "13d76d0fb049051ed12a04bef4f9de8715bea339", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/intl": "^6.2", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-18T10:38:32+00:00" + }, + { + "name": "zhamao/logger", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/zhamao-robot/zhamao-logger.git", + "reference": "1b7e34349330a842887d816f0344aba996cfeec0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zhamao-robot/zhamao-logger/zipball/1b7e34349330a842887d816f0344aba996cfeec0", + "reference": "1b7e34349330a842887d816f0344aba996cfeec0", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^7.3 || ^7.4 || ^8.0 || ^8.1", + "psr/log": "^1 || ^2 || ^3", + "symfony/polyfill-mbstring": "^1.0" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.8", + "friendsofphp/php-cs-fixer": "^3.2", + "phpstan/phpstan": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.0", + "roave/security-advisories": "dev-latest" + }, + "suggest": { + "ext-mbstring": "Use C/C++ extension instead of polyfill will be more efficient" + }, + "type": "library", + "extra": { + "hooks": { + "post-merge": "composer install", + "pre-commit": [ + "echo committing as $(git config user.name)", + "composer cs-fix -- --diff" + ], + "pre-push": [ + "composer cs-fix -- --dry-run --diff", + "composer analyse" + ] + } + }, + "autoload": { + "psr-4": { + "ZM\\Logger\\": "src/ZM/Logger" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "jerry", + "email": "admin@zhamao.me" + }, + { + "name": "sunxyw", + "email": "dev@sunxyw.xyz" + } + ], + "description": "Another Console Logger for CLI Applications", + "support": { + "issues": "https://github.com/zhamao-robot/zhamao-logger/issues", + "source": "https://github.com/zhamao-robot/zhamao-logger/tree/1.1.1" + }, + "time": "2023-03-09T15:41:10+00:00" + } + ], + "packages-dev": [ + { + "name": "amphp/amp", + "version": "v2.6.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/amp.git", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/amp/zipball/9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "reference": "9d5100cebffa729aaffecd3ad25dc5aeea4f13bb", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "ext-json": "*", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^7 | ^8 | ^9", + "psalm/phar": "^3.11@dev", + "react/promise": "^2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php", + "lib/Internal/functions.php" + ], + "psr-4": { + "Amp\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Lowrey", + "email": "rdlowrey@php.net" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A non-blocking concurrency framework for PHP applications.", + "homepage": "https://amphp.org/amp", + "keywords": [ + "async", + "asynchronous", + "awaitable", + "concurrency", + "event", + "event-loop", + "future", + "non-blocking", + "promise" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/amp/issues", + "source": "https://github.com/amphp/amp/tree/v2.6.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-20T17:52:18+00:00" + }, + { + "name": "amphp/byte-stream", + "version": "v1.8.1", + "source": { + "type": "git", + "url": "https://github.com/amphp/byte-stream.git", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/byte-stream/zipball/acbd8002b3536485c997c4e019206b3f10ca15bd", + "reference": "acbd8002b3536485c997c4e019206b3f10ca15bd", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.4", + "friendsofphp/php-cs-fixer": "^2.3", + "jetbrains/phpstorm-stubs": "^2019.3", + "phpunit/phpunit": "^6 || ^7 || ^8", + "psalm/phar": "^3.11.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\ByteStream\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A stream abstraction to make working with non-blocking I/O simple.", + "homepage": "http://amphp.org/byte-stream", + "keywords": [ + "amp", + "amphp", + "async", + "io", + "non-blocking", + "stream" + ], + "support": { + "irc": "irc://irc.freenode.org/amphp", + "issues": "https://github.com/amphp/byte-stream/issues", + "source": "https://github.com/amphp/byte-stream/tree/v1.8.1" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-03-30T17:13:30+00:00" + }, + { + "name": "amphp/parallel", + "version": "v1.4.3", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel.git", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel/zipball/3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "reference": "3aac213ba7858566fd83d38ccb85b91b2d652cb0", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.6.1", + "amphp/parser": "^1", + "amphp/process": "^1", + "amphp/serialization": "^1", + "amphp/sync": "^1.0.1", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "lib/Context/functions.php", + "lib/Sync/functions.php", + "lib/Worker/functions.php" + ], + "psr-4": { + "Amp\\Parallel\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Parallel processing component for Amp.", + "homepage": "https://github.com/amphp/parallel", + "keywords": [ + "async", + "asynchronous", + "concurrent", + "multi-processing", + "multi-threading" + ], + "support": { + "issues": "https://github.com/amphp/parallel/issues", + "source": "https://github.com/amphp/parallel/tree/v1.4.3" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2023-03-23T08:04:23+00:00" + }, + { + "name": "amphp/parallel-functions", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parallel-functions.git", + "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parallel-functions/zipball/04e92fcacfc921a56dfe12c23b3265e62593a7cb", + "reference": "04e92fcacfc921a56dfe12c23b3265e62593a7cb", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.0.3", + "amphp/parallel": "^1.4", + "amphp/serialization": "^1.0", + "laravel/serializable-closure": "^1.0", + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "v2.x-dev", + "amphp/phpunit-util": "^2.0", + "phpunit/phpunit": "^9.5.11" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\ParallelFunctions\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Parallel processing made simple.", + "support": { + "issues": "https://github.com/amphp/parallel-functions/issues", + "source": "https://github.com/amphp/parallel-functions/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-02-03T19:32:41+00:00" + }, + { + "name": "amphp/parser", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/parser.git", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/parser/zipball/ff1de4144726c5dad5fab97f66692ebe8de3e151", + "reference": "ff1de4144726c5dad5fab97f66692ebe8de3e151", + "shasum": "" + }, + "require": { + "php": ">=7.4" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "^2", + "phpunit/phpunit": "^9", + "psalm/phar": "^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Amp\\Parser\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "A generator parser to make streaming parsers simple.", + "homepage": "https://github.com/amphp/parser", + "keywords": [ + "async", + "non-blocking", + "parser", + "stream" + ], + "support": { + "issues": "https://github.com/amphp/parser/issues", + "source": "https://github.com/amphp/parser/tree/v1.1.0" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-12-30T18:08:47+00:00" + }, + { + "name": "amphp/process", + "version": "v1.1.4", + "source": { + "type": "git", + "url": "https://github.com/amphp/process.git", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/process/zipball/76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "reference": "76e9495fd6818b43a20167cb11d8a67f7744ee0f", + "shasum": "" + }, + "require": { + "amphp/amp": "^2", + "amphp/byte-stream": "^1.4", + "php": ">=7" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1", + "phpunit/phpunit": "^6" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Amp\\Process\\": "lib" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bob Weinand", + "email": "bobwei9@hotmail.com" + }, + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Asynchronous process manager.", + "homepage": "https://github.com/amphp/process", + "support": { + "issues": "https://github.com/amphp/process/issues", + "source": "https://github.com/amphp/process/tree/v1.1.4" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2022-07-06T23:50:12+00:00" + }, + { + "name": "amphp/serialization", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/amphp/serialization.git", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/serialization/zipball/693e77b2fb0b266c3c7d622317f881de44ae94a1", + "reference": "693e77b2fb0b266c3c7d622317f881de44ae94a1", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Amp\\Serialization\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Niklas Keller", + "email": "me@kelunik.com" + } + ], + "description": "Serialization tools for IPC and data storage in PHP.", + "homepage": "https://github.com/amphp/serialization", + "keywords": [ + "async", + "asynchronous", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/amphp/serialization/issues", + "source": "https://github.com/amphp/serialization/tree/master" + }, + "time": "2020-03-25T21:39:07+00:00" + }, + { + "name": "amphp/sync", + "version": "v1.4.2", + "source": { + "type": "git", + "url": "https://github.com/amphp/sync.git", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/amphp/sync/zipball/85ab06764f4f36d63b1356b466df6111cf4b89cf", + "reference": "85ab06764f4f36d63b1356b466df6111cf4b89cf", + "shasum": "" + }, + "require": { + "amphp/amp": "^2.2", + "php": ">=7.1" + }, + "require-dev": { + "amphp/php-cs-fixer-config": "dev-master", + "amphp/phpunit-util": "^1.1", + "phpunit/phpunit": "^9 || ^8 || ^7" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php", + "src/ConcurrentIterator/functions.php" + ], + "psr-4": { + "Amp\\Sync\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Aaron Piotrowski", + "email": "aaron@trowski.com" + }, + { + "name": "Stephen Coakley", + "email": "me@stephencoakley.com" + } + ], + "description": "Mutex, Semaphore, and other synchronization tools for Amp.", + "homepage": "https://github.com/amphp/sync", + "keywords": [ + "async", + "asynchronous", + "mutex", + "semaphore", + "synchronization" + ], + "support": { + "issues": "https://github.com/amphp/sync/issues", + "source": "https://github.com/amphp/sync/tree/v1.4.2" + }, + "funding": [ + { + "url": "https://github.com/amphp", + "type": "github" + } + ], + "time": "2021-10-25T18:29:10+00:00" + }, + { + "name": "captainhook/captainhook", + "version": "5.18.2", + "source": { + "type": "git", + "url": "https://github.com/captainhookphp/captainhook.git", + "reference": "61c24442f71ea216e9e172861d48d7676439dd18" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/captainhookphp/captainhook/zipball/61c24442f71ea216e9e172861d48d7676439dd18", + "reference": "61c24442f71ea216e9e172861d48d7676439dd18", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-spl": "*", + "ext-xml": "*", + "php": ">=7.4", + "sebastianfeldmann/camino": "^0.9.2", + "sebastianfeldmann/cli": "^3.3", + "sebastianfeldmann/git": "^3.9", + "symfony/console": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0", + "symfony/filesystem": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0", + "symfony/process": "^2.7 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "replace": { + "sebastianfeldmann/captainhook": "*" + }, + "require-dev": { + "composer/composer": "~1 || ^2.0", + "mikey179/vfsstream": "~1" + }, + "bin": [ + "bin/captainhook" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0.x-dev" + }, + "captainhook": { + "config": "captainhook.json" + } + }, + "autoload": { + "psr-4": { + "CaptainHook\\App\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "PHP git hook manager", + "homepage": "https://github.com/captainhookphp/captainhook", + "keywords": [ + "commit-msg", + "git", + "hooks", + "post-merge", + "pre-commit", + "pre-push", + "prepare-commit-msg" + ], + "support": { + "issues": "https://github.com/captainhookphp/captainhook/issues", + "source": "https://github.com/captainhookphp/captainhook/tree/5.18.2" + }, + "funding": [ + { + "url": "https://github.com/sponsors/sebastianfeldmann", + "type": "github" + } + ], + "time": "2023-10-16T15:13:42+00:00" + }, + { + "name": "captainhook/plugin-composer", + "version": "5.3.3", + "source": { + "type": "git", + "url": "https://github.com/captainhookphp/plugin-composer.git", + "reference": "0a802aaf7742ef22b5cbccd586d99e16d9d23a39" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/captainhookphp/plugin-composer/zipball/0a802aaf7742ef22b5cbccd586d99e16d9d23a39", + "reference": "0a802aaf7742ef22b5cbccd586d99e16d9d23a39", + "shasum": "" + }, + "require": { + "captainhook/captainhook": "^5.0", + "composer-plugin-api": "^1.1|^2.0", + "php": ">=7.1" + }, + "require-dev": { + "composer/composer": "*" + }, + "type": "composer-plugin", + "extra": { + "class": "CaptainHook\\Plugin\\Composer\\ComposerPlugin", + "branch-alias": { + "dev-fluffy_hedgehog": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "CaptainHook\\Plugin\\Composer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Heigl", + "email": "andreas@heigl.org" + }, + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "Composer-Plugin handling your git-hooks", + "support": { + "issues": "https://github.com/captainhookphp/plugin-composer/issues", + "source": "https://github.com/captainhookphp/plugin-composer/tree/5.3.3" + }, + "time": "2022-01-28T04:35:22+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.1", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.1" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-10-11T07:11:09+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-31T09:50:34+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "reference": "4f2d4f2836e7ec4e7a8625e75c6aa916004db931", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "1.4.10 || 1.10.15", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "psalm/plugin-phpunit": "0.18.4", + "psr/log": "^1 || ^2 || ^3", + "vimeo/psalm": "4.30.0 || 5.12.0" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.2" + }, + "time": "2023-09-27T20:04:15+00:00" + }, + { + "name": "fidry/console", + "version": "0.5.5", + "source": { + "type": "git", + "url": "https://github.com/theofidry/console.git", + "reference": "bc1fe03f600c63f12ec0a39c6b746c1a1fb77bf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/console/zipball/bc1fe03f600c63f12ec0a39c6b746c1a1fb77bf7", + "reference": "bc1fe03f600c63f12ec0a39c6b746c1a1fb77bf7", + "shasum": "" + }, + "require": { + "php": "^7.4.0 || ^8.0.0", + "symfony/console": "^4.4 || ^5.4 || ^6.1", + "symfony/event-dispatcher-contracts": "^1.0 || ^2.5 || ^3.0", + "symfony/service-contracts": "^1.0 || ^2.5 || ^3.0", + "thecodingmachine/safe": "^1.3 || ^2.0", + "webmozart/assert": "^1.11" + }, + "conflict": { + "symfony/dependency-injection": "<5.3.0", + "symfony/framework-bundle": "<5.3.0", + "symfony/http-kernel": "<5.3.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4", + "composer/semver": "^3.3", + "ergebnis/composer-normalize": "^2.28", + "infection/infection": "^0.26", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.4.3", + "symfony/dependency-injection": "^4.4 || ^5.4 || ^6.1", + "symfony/framework-bundle": "^4.4 || ^5.4 || ^6.1", + "symfony/http-kernel": "^4.4 || ^5.4 || ^6.1", + "symfony/phpunit-bridge": "^4.4.47 || ^5.4 || ^6.0", + "symfony/yaml": "^4.4 || ^5.4 || ^6.1", + "webmozarts/strict-phpunit": "^7.3" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fidry\\Console\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Library to create CLI applications", + "keywords": [ + "cli", + "console", + "symfony" + ], + "support": { + "issues": "https://github.com/theofidry/console/issues", + "source": "https://github.com/theofidry/console/tree/0.5.5" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2022-12-18T10:49:34+00:00" + }, + { + "name": "fidry/filesystem", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/filesystem.git", + "reference": "1dd372ab3eb8b84ffe9578bff576b00c9a44ee46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/filesystem/zipball/1dd372ab3eb8b84ffe9578bff576b00c9a44ee46", + "reference": "1dd372ab3eb8b84ffe9578bff576b00c9a44ee46", + "shasum": "" + }, + "require": { + "php": "^8.1", + "symfony/filesystem": "^6.3", + "thecodingmachine/safe": "^2.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4", + "ergebnis/composer-normalize": "^2.28", + "infection/infection": "^0.26", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^10.3", + "symfony/finder": "^6.3", + "symfony/phpunit-bridge": "^6.2" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Fidry\\FileSystem\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Symfony Filesystem with a few more utilities.", + "keywords": [ + "filesystem" + ], + "support": { + "issues": "https://github.com/theofidry/filesystem/issues", + "source": "https://github.com/theofidry/filesystem/tree/1.1.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2023-10-07T07:32:54+00:00" + }, + { + "name": "filp/whoops", + "version": "2.15.3", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187", + "reference": "c83e88a30524f9360b11f585f71e6b17313b7187", + "shasum": "" + }, + "require": { + "php": "^5.5.9 || ^7.0 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" + }, + "require-dev": { + "mockery/mockery": "^0.9 || ^1.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Whoops\\": "src/Whoops/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.15.3" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2023-07-13T12:00:00+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.37.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "c3fe76976081ab871aa654e872da588077e19679" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/c3fe76976081ab871aa654e872da588077e19679", + "reference": "c3fe76976081ab871aa654e872da588077e19679", + "shasum": "" + }, + "require": { + "composer/semver": "^3.3", + "composer/xdebug-handler": "^3.0.3", + "ext-json": "*", + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0", + "sebastian/diff": "^4.0 || ^5.0", + "symfony/console": "^5.4 || ^6.0", + "symfony/event-dispatcher": "^5.4 || ^6.0", + "symfony/filesystem": "^5.4 || ^6.0", + "symfony/finder": "^5.4 || ^6.0", + "symfony/options-resolver": "^5.4 || ^6.0", + "symfony/polyfill-mbstring": "^1.27", + "symfony/polyfill-php80": "^1.27", + "symfony/polyfill-php81": "^1.27", + "symfony/process": "^5.4 || ^6.0", + "symfony/stopwatch": "^5.4 || ^6.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3 || ^2.0", + "justinrainbow/json-schema": "^5.2", + "keradus/cli-executor": "^2.0", + "mikey179/vfsstream": "^1.6.11", + "php-coveralls/php-coveralls": "^2.5.3", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.2", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.2.1", + "phpspec/prophecy": "^1.16", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.5", + "symfony/phpunit-bridge": "^6.2.3", + "symfony/yaml": "^5.4 || ^6.0" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.37.1" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2023-10-29T20:51:23+00:00" + }, + { + "name": "humbug/box", + "version": "4.3.8", + "source": { + "type": "git", + "url": "https://github.com/box-project/box.git", + "reference": "55344067891d8be61e6efa50f7c2e59114f32704" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/box-project/box/zipball/55344067891d8be61e6efa50f7c2e59114f32704", + "reference": "55344067891d8be61e6efa50f7c2e59114f32704", + "shasum": "" + }, + "require": { + "amphp/parallel-functions": "^1.1", + "composer-plugin-api": "^2.2", + "composer/semver": "^3.3.2", + "composer/xdebug-handler": "^3.0.3", + "ext-phar": "*", + "ext-sodium": "*", + "fidry/console": "^0.5.3", + "humbug/php-scoper": "^0.18.3", + "justinrainbow/json-schema": "^5.2.12", + "laravel/serializable-closure": "^1.2.2", + "nikic/iter": "^2.2", + "nikic/php-parser": "^4.15.2", + "paragonie/constant_time_encoding": "^2.6", + "php": "^8.1", + "phpdocumentor/reflection-docblock": "^5.3", + "psr/log": "^3.0", + "seld/jsonlint": "^1.9", + "symfony/console": "^6.1.7", + "symfony/filesystem": "^6.1.5", + "symfony/finder": "^6.1.3", + "symfony/process": "^6.1.3", + "symfony/var-dumper": "^6.1.6", + "webmozart/assert": "^1.11" + }, + "replace": { + "paragonie/sodium_compat": "*", + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ergebnis/composer-normalize": "^2.29", + "fidry/makefile": "^0.2.1", + "mikey179/vfsstream": "^1.6.11", + "phpspec/prophecy-phpunit": "^2.0.1", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^6.1.6", + "symfony/yaml": "^6.2", + "webmozarts/strict-phpunit": "^7.6" + }, + "suggest": { + "ext-openssl": "To accelerate private key generation." + }, + "bin": [ + "bin/box" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-main": "4.x-dev" + } + }, + "autoload": { + "files": [ + "src/FileSystem/file_system.php", + "src/consts.php", + "src/functions.php" + ], + "psr-4": { + "KevinGH\\Box\\": "src" + }, + "exclude-from-classmap": [ + "/Test/", + "vendor/humbug/php-scoper/vendor-hotfix" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kevin Herrera", + "email": "kevin@herrera.io", + "homepage": "http://kevin.herrera.io" + }, + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Fast, zero config application bundler with PHARs.", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/box-project/box/issues", + "source": "https://github.com/box-project/box/tree/4.3.8" + }, + "time": "2023-03-17T08:30:03+00:00" + }, + { + "name": "humbug/php-scoper", + "version": "0.18.4", + "source": { + "type": "git", + "url": "https://github.com/humbug/php-scoper.git", + "reference": "d79c1486537280c21c907e9a8a610eceb391407f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/humbug/php-scoper/zipball/d79c1486537280c21c907e9a8a610eceb391407f", + "reference": "d79c1486537280c21c907e9a8a610eceb391407f", + "shasum": "" + }, + "require": { + "fidry/console": "^0.5.0", + "fidry/filesystem": "^1.1", + "jetbrains/phpstorm-stubs": "^v2022.2", + "nikic/php-parser": "^4.12", + "php": "^8.1", + "symfony/console": "^5.2 || ^6.0", + "symfony/filesystem": "^5.2 || ^6.0", + "symfony/finder": "^5.2 || ^6.0", + "thecodingmachine/safe": "^2.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.1", + "ergebnis/composer-normalize": "^2.28", + "fidry/makefile": "^1.0", + "humbug/box": "^4.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpunit/phpunit": "^9.0", + "symfony/yaml": "^6.1" + }, + "bin": [ + "bin/php-scoper" + ], + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": false, + "forward-command": false + }, + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Humbug\\PhpScoper\\": "src/" + }, + "classmap": [ + "vendor-hotfix/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Théo Fidry", + "email": "theo.fidry@gmail.com" + }, + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com" + } + ], + "description": "Prefixes all PHP namespaces in a file or directory.", + "support": { + "issues": "https://github.com/humbug/php-scoper/issues", + "source": "https://github.com/humbug/php-scoper/tree/0.18.4" + }, + "time": "2023-10-20T17:14:04+00:00" + }, + { + "name": "jetbrains/phpstorm-stubs", + "version": "v2022.3", + "source": { + "type": "git", + "url": "https://github.com/JetBrains/phpstorm-stubs.git", + "reference": "6b568c153cea002dc6fad96285c3063d07cab18d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/JetBrains/phpstorm-stubs/zipball/6b568c153cea002dc6fad96285c3063d07cab18d", + "reference": "6b568c153cea002dc6fad96285c3063d07cab18d", + "shasum": "" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "@stable", + "nikic/php-parser": "@stable", + "php": "^8.0", + "phpdocumentor/reflection-docblock": "@stable", + "phpunit/phpunit": "@stable" + }, + "type": "library", + "autoload": { + "files": [ + "PhpStormStubsMap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "description": "PHP runtime & extensions header files for PhpStorm", + "homepage": "https://www.jetbrains.com/phpstorm", + "keywords": [ + "autocomplete", + "code", + "inference", + "inspection", + "jetbrains", + "phpstorm", + "stubs", + "type" + ], + "support": { + "source": "https://github.com/JetBrains/phpstorm-stubs/tree/v2022.3" + }, + "time": "2022-10-17T09:21:37+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "v5.2.13", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" + }, + "bin": [ + "bin/validate-json" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "support": { + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13" + }, + "time": "2023-09-26T02:20:38+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/laravel/serializable-closure.git", + "reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/076fe2cf128bd54b4341cdc6d49b95b34e101e4c", + "reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c", + "shasum": "" + }, + "require": { + "php": "^7.3|^8.0" + }, + "require-dev": { + "nesbot/carbon": "^2.61", + "pestphp/pest": "^1.21.3", + "phpstan/phpstan": "^1.8.2", + "symfony/var-dumper": "^5.4.11" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Laravel\\SerializableClosure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + }, + { + "name": "Nuno Maduro", + "email": "nuno@laravel.com" + } + ], + "description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.", + "keywords": [ + "closure", + "laravel", + "serializable" + ], + "support": { + "issues": "https://github.com/laravel/serializable-closure/issues", + "source": "https://github.com/laravel/serializable-closure" + }, + "time": "2023-10-17T13:38:16+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.11.1", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3,<3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2023-03-08T13:26:56+00:00" + }, + { + "name": "nikic/iter", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/iter.git", + "reference": "d9f88bc04b5b453914373e70c041353d8e67c3f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/iter/zipball/d9f88bc04b5b453914373e70c041353d8e67c3f5", + "reference": "d9f88bc04b5b453914373e70c041353d8e67c3f5", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "vimeo/psalm": "^4.18 || ^5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/iter.func.php", + "src/iter.php", + "src/iter.rewindable.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Iteration primitives using generators", + "keywords": [ + "functional", + "generator", + "iterator" + ], + "support": { + "issues": "https://github.com/nikic/iter/issues", + "source": "https://github.com/nikic/iter/tree/v2.3.0" + }, + "time": "2023-07-25T19:55:40+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v4.17.1", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "reference": "a6303e50c90c355c7eeee2c4a8b27fe8dc8fef1d", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": ">=7.0" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.9-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v4.17.1" + }, + "time": "2023-08-13T19:53:39+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v7.10.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2", + "reference": "49ec67fa7b002712da8526678abd651c09f375b2", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.15.3", + "nunomaduro/termwind": "^1.15.1", + "php": "^8.1.0", + "symfony/console": "^6.3.4" + }, + "conflict": { + "laravel/framework": ">=11.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.3.0", + "laravel/framework": "^10.28.0", + "laravel/pint": "^1.13.3", + "laravel/sail": "^1.25.0", + "laravel/sanctum": "^3.3.1", + "laravel/tinker": "^2.8.2", + "nunomaduro/larastan": "^2.6.4", + "orchestra/testbench-core": "^8.13.0", + "pestphp/pest": "^2.23.2", + "phpunit/phpunit": "^10.4.1", + "sebastian/environment": "^6.0.1", + "spatie/laravel-ignition": "^2.3.1" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2023-10-11T15:45:01+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v1.15.1", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "reference": "8ab0b32c8caa4a2e09700ea32925441385e4a5dc", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.0", + "symfony/console": "^5.3.0|^6.0.0" + }, + "require-dev": { + "ergebnis/phpstan-rules": "^1.0.", + "illuminate/console": "^8.0|^9.0", + "illuminate/support": "^8.0|^9.0", + "laravel/pint": "^1.0.0", + "pestphp/pest": "^1.21.0", + "pestphp/pest-plugin-mock": "^1.0", + "phpstan/phpstan": "^1.4.6", + "phpstan/phpstan-strict-rules": "^1.1.0", + "symfony/var-dumper": "^5.2.7|^6.0.0", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v1.15.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2023-02-08T01:06:31+00:00" + }, + { + "name": "paragonie/constant_time_encoding", + "version": "v2.6.3", + "source": { + "type": "git", + "url": "https://github.com/paragonie/constant_time_encoding.git", + "reference": "58c3f47f650c94ec05a151692652a868995d2938" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938", + "reference": "58c3f47f650c94ec05a151692652a868995d2938", + "shasum": "" + }, + "require": { + "php": "^7|^8" + }, + "require-dev": { + "phpunit/phpunit": "^6|^7|^8|^9", + "vimeo/psalm": "^1|^2|^3|^4" + }, + "type": "library", + "autoload": { + "psr-4": { + "ParagonIE\\ConstantTime\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com", + "role": "Maintainer" + }, + { + "name": "Steve 'Sc00bz' Thomas", + "email": "steve@tobtu.com", + "homepage": "https://www.tobtu.com", + "role": "Original Developer" + } + ], + "description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)", + "keywords": [ + "base16", + "base32", + "base32_decode", + "base32_encode", + "base64", + "base64_decode", + "base64_encode", + "bin2hex", + "encoding", + "hex", + "hex2bin", + "rfc4648" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/constant_time_encoding/issues", + "source": "https://github.com/paragonie/constant_time_encoding" + }, + "time": "2022-06-14T06:56:20+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", + "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.3" + }, + "time": "2021-07-20T11:28:43+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "php": "^7.2 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.3", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "account@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + }, + "time": "2021-10-19T17:43:47+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.7.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "reference": "3219c6ee25c9ea71e3d9bbaf39c67c9ebd499419", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.13" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.3" + }, + "time": "2023-08-12T11:01:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "1.24.2", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "bcad8d995980440892759db0c32acae7c8e79442" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bcad8d995980440892759db0c32acae7c8e79442", + "reference": "bcad8d995980440892759db0c32acae7c8e79442", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^4.15", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.24.2" + }, + "time": "2023-09-26T12:28:12+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "1.10.40", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "93c84b5bf7669920d823631e39904d69b9c7dc5d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/93c84b5bf7669920d823631e39904d69b9c7dc5d", + "reference": "93c84b5bf7669920d823631e39904d69b9c7dc5d", + "shasum": "" + }, + "require": { + "php": "^7.2|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", + "type": "tidelift" + } + ], + "time": "2023-10-30T14:48:31+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.7", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "355324ca4980b8916c18b9db29f3ef484078f26e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/355324ca4980b8916c18b9db29f3ef484078f26e", + "reference": "355324ca4980b8916c18b9db29f3ef484078f26e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.15", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-text-template": "^3.0", + "sebastian/code-unit-reverse-lookup": "^3.0", + "sebastian/complexity": "^3.0", + "sebastian/environment": "^6.0", + "sebastian/lines-of-code": "^2.0", + "sebastian/version": "^4.0", + "theseer/tokenizer": "^1.2.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.7" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-10-04T15:34:17+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.4.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", + "reference": "cacd8b9dd224efa8eb28beb69004126c7ca1a1a1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.10.1", + "phar-io/manifest": "^2.0.3", + "phar-io/version": "^3.0.2", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.5", + "phpunit/php-file-iterator": "^4.0", + "phpunit/php-invoker": "^4.0", + "phpunit/php-text-template": "^3.0", + "phpunit/php-timer": "^6.0", + "sebastian/cli-parser": "^2.0", + "sebastian/code-unit": "^2.0", + "sebastian/comparator": "^5.0", + "sebastian/diff": "^5.0", + "sebastian/environment": "^6.0", + "sebastian/exporter": "^5.1", + "sebastian/global-state": "^6.0.1", + "sebastian/object-enumerator": "^5.0", + "sebastian/recursion-context": "^5.0", + "sebastian/type": "^4.0", + "sebastian/version": "^4.0" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.4-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.4.2" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2023-10-26T07:21:45+00:00" + }, + { + "name": "psr/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/efdc130dbbbb8ef0b545a994fd811725c5282cae", + "reference": "efdc130dbbbb8ef0b545a994fd811725c5282cae", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:15+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", + "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-14T13:18:12+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68cfb347a44871f01e33ab0ef8215966432f6957", + "reference": "68cfb347a44871f01e33ab0ef8215966432f6957", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.10", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-28T11:50:59+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-05-01T07:48:21+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", + "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-04-11T05:39:26+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "reference": "64f51654862e0f5e318db7e9dcc2292c63cdbddc", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-09-24T13:22:09+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/7ea9ead78f6d380d2a667864c132c2f7b83055e4", + "reference": "7ea9ead78f6d380d2a667864c132c2f7b83055e4", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-07-19T07:19:23+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "reference": "649e40d279e243d985aa8fb6e74dd5bb28dc185d", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.10", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T09:25:50+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "sebastianfeldmann/camino", + "version": "0.9.5", + "source": { + "type": "git", + "url": "https://github.com/sebastianfeldmann/camino.git", + "reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianfeldmann/camino/zipball/bf2e4c8b2a029e9eade43666132b61331e3e8184", + "reference": "bf2e4c8b2a029e9eade43666132b61331e3e8184", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "SebastianFeldmann\\Camino\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "Path management the OO way", + "homepage": "https://github.com/sebastianfeldmann/camino", + "keywords": [ + "file system", + "path" + ], + "support": { + "issues": "https://github.com/sebastianfeldmann/camino/issues", + "source": "https://github.com/sebastianfeldmann/camino/tree/0.9.5" + }, + "funding": [ + { + "url": "https://github.com/sebastianfeldmann", + "type": "github" + } + ], + "time": "2022-01-03T13:15:10+00:00" + }, + { + "name": "sebastianfeldmann/cli", + "version": "3.4.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianfeldmann/cli.git", + "reference": "8a932e99e9455981fb32fa6c085492462fe8f8cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianfeldmann/cli/zipball/8a932e99e9455981fb32fa6c085492462fe8f8cf", + "reference": "8a932e99e9455981fb32fa6c085492462fe8f8cf", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "symfony/process": "^4.3 | ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "SebastianFeldmann\\Cli\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "PHP cli helper classes", + "homepage": "https://github.com/sebastianfeldmann/cli", + "keywords": [ + "cli" + ], + "support": { + "issues": "https://github.com/sebastianfeldmann/cli/issues", + "source": "https://github.com/sebastianfeldmann/cli/tree/3.4.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianfeldmann", + "type": "github" + } + ], + "time": "2021-12-20T14:59:49+00:00" + }, + { + "name": "sebastianfeldmann/git", + "version": "3.9.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianfeldmann/git.git", + "reference": "eb2ca84a2b45a461f0bf5d4fd400df805649e83a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianfeldmann/git/zipball/eb2ca84a2b45a461f0bf5d4fd400df805649e83a", + "reference": "eb2ca84a2b45a461f0bf5d4fd400df805649e83a", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-xml": "*", + "php": ">=7.2", + "sebastianfeldmann/cli": "^3.0" + }, + "require-dev": { + "mikey179/vfsstream": "^1.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "SebastianFeldmann\\Git\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sebastian Feldmann", + "email": "sf@sebastian-feldmann.info" + } + ], + "description": "PHP git wrapper", + "homepage": "https://github.com/sebastianfeldmann/git", + "keywords": [ + "git" + ], + "support": { + "issues": "https://github.com/sebastianfeldmann/git/issues", + "source": "https://github.com/sebastianfeldmann/git/tree/3.9.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianfeldmann", + "type": "github" + } + ], + "time": "2023-10-13T09:10:48+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2023-05-11T13:16:46+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v6.3.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "reference": "adb01fe097a4ee930db9258a3cc906b5beb5cf2e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/error-handler": "^5.4|^6.0", + "symfony/expression-language": "^5.4|^6.0", + "symfony/http-foundation": "^5.4|^6.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/event-dispatcher/tree/v6.3.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-07-06T06:56:43+00:00" + }, + { + "name": "symfony/event-dispatcher-contracts", + "version": "v3.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/a76aed96a42d2b521153fb382d418e30d18b59df", + "reference": "a76aed96a42d2b521153fb382d418e30d18b59df", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/event-dispatcher": "^1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.4-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-23T14:45:45+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-06-01T08:30:39+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.3.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/a1b31d88c0e998168ca7792f222cbecee47428c4", + "reference": "a1b31d88c0e998168ca7792f222cbecee47428c4", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.3.5" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-09-26T12:56:25+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/a10f19f5198d589d5c33333cffe98dc9820332dd", + "reference": "a10f19f5198d589d5c33333cffe98dc9820332dd", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], + "support": { + "source": "https://github.com/symfony/options-resolver/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-05-12T14:21:09+00:00" + }, + { + "name": "symfony/process", + "version": "v6.3.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/0b5c29118f2e980d455d2e34a5659f4579847c54", + "reference": "0b5c29118f2e980d455d2e34a5659f4579847c54", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v6.3.4" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-08-07T10:39:22+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v6.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "reference": "fc47f1015ec80927ff64ba9094dfe8b9d48fe9f2", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v6.3.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-02-16T10:14:28+00:00" + }, + { + "name": "symfony/var-dumper", + "version": "v6.3.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-dumper.git", + "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/999ede244507c32b8e43aebaa10e9fce20de7c97", + "reference": "999ede244507c32b8e43aebaa10e9fce20de7c97", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/console": "<5.4" + }, + "require-dev": { + "ext-iconv": "*", + "symfony/console": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/uid": "^5.4|^6.0", + "twig/twig": "^2.13|^3.0.4" + }, + "bin": [ + "Resources/bin/var-dump-server" + ], + "type": "library", + "autoload": { + "files": [ + "Resources/functions/dump.php" + ], + "psr-4": { + "Symfony\\Component\\VarDumper\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides mechanisms for walking through any arbitrary PHP variable", + "homepage": "https://symfony.com", + "keywords": [ + "debug", + "dump" + ], + "support": { + "source": "https://github.com/symfony/var-dumper/tree/v6.3.6" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-10-12T18:45:56+00:00" + }, + { + "name": "thecodingmachine/safe", + "version": "v2.5.0", + "source": { + "type": "git", + "url": "https://github.com/thecodingmachine/safe.git", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thecodingmachine/safe/zipball/3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "reference": "3115ecd6b4391662b4931daac4eba6b07a2ac1f0", + "shasum": "" + }, + "require": { + "php": "^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "^3.2", + "thecodingmachine/phpstan-strict-rules": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2.x-dev" + } + }, + "autoload": { + "files": [ + "deprecated/apc.php", + "deprecated/array.php", + "deprecated/datetime.php", + "deprecated/libevent.php", + "deprecated/misc.php", + "deprecated/password.php", + "deprecated/mssql.php", + "deprecated/stats.php", + "deprecated/strings.php", + "lib/special_cases.php", + "deprecated/mysqli.php", + "generated/apache.php", + "generated/apcu.php", + "generated/array.php", + "generated/bzip2.php", + "generated/calendar.php", + "generated/classobj.php", + "generated/com.php", + "generated/cubrid.php", + "generated/curl.php", + "generated/datetime.php", + "generated/dir.php", + "generated/eio.php", + "generated/errorfunc.php", + "generated/exec.php", + "generated/fileinfo.php", + "generated/filesystem.php", + "generated/filter.php", + "generated/fpm.php", + "generated/ftp.php", + "generated/funchand.php", + "generated/gettext.php", + "generated/gmp.php", + "generated/gnupg.php", + "generated/hash.php", + "generated/ibase.php", + "generated/ibmDb2.php", + "generated/iconv.php", + "generated/image.php", + "generated/imap.php", + "generated/info.php", + "generated/inotify.php", + "generated/json.php", + "generated/ldap.php", + "generated/libxml.php", + "generated/lzf.php", + "generated/mailparse.php", + "generated/mbstring.php", + "generated/misc.php", + "generated/mysql.php", + "generated/network.php", + "generated/oci8.php", + "generated/opcache.php", + "generated/openssl.php", + "generated/outcontrol.php", + "generated/pcntl.php", + "generated/pcre.php", + "generated/pgsql.php", + "generated/posix.php", + "generated/ps.php", + "generated/pspell.php", + "generated/readline.php", + "generated/rpminfo.php", + "generated/rrd.php", + "generated/sem.php", + "generated/session.php", + "generated/shmop.php", + "generated/sockets.php", + "generated/sodium.php", + "generated/solr.php", + "generated/spl.php", + "generated/sqlsrv.php", + "generated/ssdeep.php", + "generated/ssh2.php", + "generated/stream.php", + "generated/strings.php", + "generated/swoole.php", + "generated/uodbc.php", + "generated/uopz.php", + "generated/url.php", + "generated/var.php", + "generated/xdiff.php", + "generated/xml.php", + "generated/xmlrpc.php", + "generated/yaml.php", + "generated/yaz.php", + "generated/zip.php", + "generated/zlib.php" + ], + "classmap": [ + "lib/DateTime.php", + "lib/DateTimeImmutable.php", + "lib/Exceptions/", + "deprecated/Exceptions/", + "generated/Exceptions/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHP core functions that throw exceptions instead of returning FALSE on error", + "support": { + "issues": "https://github.com/thecodingmachine/safe/issues", + "source": "https://github.com/thecodingmachine/safe/tree/v2.5.0" + }, + "time": "2023-04-05T11:54:14+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/34a41e998c2183e22995f158c581e7b5e755ab9e", + "reference": "34a41e998c2183e22995f158c581e7b5e755ab9e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.2.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2021-07-28T10:34:58+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">= 8.1", + "ext-mbstring": "*", + "ext-pcntl": "*" + }, + "platform-dev": [], + "plugin-api-version": "2.2.0" +} diff --git a/config/ext.json b/config/ext.json index 727907f8..f60637f4 100644 --- a/config/ext.json +++ b/config/ext.json @@ -100,6 +100,14 @@ "gettext" ] }, + "glfw": { + "type": "external", + "arg-type": "custom", + "source": "ext-glfw", + "lib-depends": [ + "glfw" + ] + }, "gmp": { "type": "builtin", "arg-type": "with-prefix", @@ -144,15 +152,23 @@ }, "ldap": { "type": "builtin", - "arg-type": "with", + "arg-type": "with-prefix", "lib-depends": [ "ldap" + ], + "lib-suggests": [ + "gmp", + "libsodium" + ], + "ext-suggests": [ + "openssl" ] }, "mbregex": { "type": "builtin", - "lib-depends": [ - "onig" + "arg-type": "custom", + "ext-depends": [ + "mbstring" ] }, "mbstring": { @@ -162,16 +178,39 @@ "onig" ] }, + "memcache": { + "type": "external", + "source": "ext-memcache", + "arg-type": "custom", + "lib-depends": [ + "zlib" + ], + "ext-depends": [ + "session" + ] + }, + "memcached": { + "type": "external", + "source": "memcached", + "arg-type": "custom", + "cpp-extension": true, + "lib-depends": [ + "libmemcached" + ], + "ext-depends": [ + "session", + "zlib" + ] + }, "mongodb": { "type": "external", "source": "mongodb", "arg-type": "custom", - "lib-depends": [ - "zstd" - ], "lib-suggests": [ "icu", - "openssl" + "openssl", + "zstd", + "zlib" ] }, "mysqli": { @@ -183,16 +222,22 @@ }, "mysqlnd": { "type": "builtin", - "arg-type-windows": "with" + "arg-type-windows": "with", + "lib-depends": [ + "zlib" + ] }, "opcache": { "type": "builtin" }, "openssl": { "type": "builtin", - "arg-type": "with", + "arg-type": "custom", "lib-depends": [ "openssl" + ], + "ext-depends": [ + "zlib" ] }, "password-argon2": { @@ -219,12 +264,13 @@ }, "pdo_pgsql": { "type": "builtin", - "arg-type": "with", + "arg-type": "with-prefix", "ext-depends": [ - "pdo" + "pdo", + "pgsql" ], "lib-depends": [ - "pq" + "postgresql" ] }, "pdo_sqlite": { @@ -238,6 +284,13 @@ "sqlite" ] }, + "pgsql": { + "type": "builtin", + "arg-type": "with-prefix", + "lib-depends": [ + "postgresql" + ] + }, "phar": { "type": "builtin", "ext-depends": [ @@ -269,7 +322,10 @@ "redis": { "type": "external", "source": "redis", - "arg-type": "custom" + "arg-type": "custom", + "ext-suggests": [ + "session" + ] }, "session": { "type": "builtin" @@ -285,6 +341,18 @@ "libxml2" ] }, + "snappy": { + "type": "external", + "source": "ext-snappy", + "cpp-extension": true, + "arg-type": "custom", + "lib-depends": [ + "snappy" + ], + "ext-suggest": [ + "apcu" + ] + }, "snmp": { "type": "builtin", "arg-type": "with", @@ -328,6 +396,7 @@ "type": "external", "source": "swoole", "arg-type": "custom", + "cpp-extension": true, "lib-depends": [ "openssl" ], @@ -335,7 +404,8 @@ "openssl" ], "ext-suggests": [ - "curl" + "curl", + "pgsql" ], "unix-only": true }, @@ -374,6 +444,15 @@ "tokenizer": { "type": "builtin" }, + "xlswriter": { + "type": "external", + "source": "xlswriter", + "arg-type": "custom", + "ext-depends": [ + "zlib", + "zip" + ] + }, "xml": { "type": "builtin", "arg-type": "custom", @@ -398,9 +477,13 @@ }, "xsl": { "type": "builtin", - "arg-type": "with", + "arg-type": "with-prefix", "lib-depends": [ "libxslt" + ], + "ext-depends": [ + "xml", + "dom" ] }, "yaml": { @@ -421,7 +504,7 @@ }, "zlib": { "type": "builtin", - "arg-type": "with", + "arg-type": "custom", "arg-type-windows": "enable", "lib-depends": [ "zlib" diff --git a/config/lib.json b/config/lib.json index a68fbf74..13f1f1b8 100644 --- a/config/lib.json +++ b/config/lib.json @@ -42,14 +42,14 @@ "curl" ], "lib-depends-unix": [ + "openssl", "zlib" ], "lib-suggests": [ "libssh2", "brotli", "nghttp2", - "zstd", - "openssl" + "zstd" ], "lib-suggests-windows": [ "zlib", @@ -63,6 +63,7 @@ ], "frameworks": [ "CoreFoundation", + "CoreServices", "SystemConfiguration" ] }, @@ -84,6 +85,18 @@ "brotli" ] }, + "glfw": { + "source": "ext-glfw", + "static-libs-unix": [ + "libglfw3.a" + ], + "frameworks": [ + "CoreVideo", + "OpenGL", + "Cocoa", + "IOKit" + ] + }, "gmp": { "source": "gmp", "static-libs-unix": [ @@ -98,6 +111,7 @@ }, "icu": { "source": "icu", + "cpp-library": true, "static-libs-unix": [ "libicui18n.a", "libicuio.a", @@ -109,20 +123,20 @@ "source": "imagemagick", "static-libs-unix": [ "libMagick++-7.Q16HDRI.a", - "libMagickCore-7.Q16HDRI.a", - "libMagickWand-7.Q16HDRI.a" + "libMagickWand-7.Q16HDRI.a", + "libMagickCore-7.Q16HDRI.a" ], "lib-depends": [ "zlib", "libpng", "libjpeg", - "bzip2", "libwebp", "freetype" ], "lib-suggests": [ "zstd", "xz", + "bzip2", "libzip", "libxml2" ] @@ -133,6 +147,18 @@ "libargon2.a" ] }, + "ldap": { + "source": "ldap", + "static-libs-unix": [ + "liblber.a", + "libldap.a" + ], + "lib-suggests": [ + "openssl", + "gmp", + "libsodium" + ] + }, "libavif": { "source": "libavif", "static-libs-unix": [ @@ -194,10 +220,17 @@ "libmcrypt.a" ] }, + "libmemcached": { + "source": "libmemcached", + "static-libs-unix": [ + "libmemcached.a", + "libmemcachedutil.a" + ] + }, "libpng": { "source": "libpng", "static-libs-unix": [ - "libpng.a" + "libpng16.a" ], "static-libs-windows": [ "libpng16_static.lib" @@ -266,19 +299,29 @@ "libxml2" ], "lib-depends": [ - "libiconv" + "libiconv", + "zlib" ], "lib-suggests": [ "xz", - "zlib" + "icu" ], "lib-suggests-windows": [ "icu", "xz", - "zlib", "pthreads4w" ] }, + "libxslt": { + "source": "libxslt", + "static-libs-unix": [ + "libxslt.a", + "libexslt.a" + ], + "lib-depends": [ + "libxml2" + ] + }, "libyaml": { "source": "libyaml", "static-libs-unix": [ @@ -386,7 +429,22 @@ "postgresql": { "source": "postgresql", "static-libs-unix": [ - "libpg.a" + "libpq.a", + "libpgport.a", + "libpgcommon.a" + ], + "lib-depends": [ + "libiconv", + "libxml2", + "openssl", + "zlib", + "readline" + ], + "lib-suggests": [ + "icu", + "libxslt", + "ldap", + "zstd" ] }, "pthreads4w": { @@ -410,6 +468,21 @@ "ncurses" ] }, + "snappy": { + "source": "snappy", + "static-libs-unix": [ + "libsnappy.a" + ], + "headers-unix": [ + "snappy.h", + "snappy-c.h", + "snappy-sinksource.h", + "snappy-stubs-public.h" + ], + "lib-depends": [ + "zlib" + ] + }, "sqlite": { "source": "sqlite", "static-libs-unix": [ @@ -476,4 +549,4 @@ "zstd_errors.h" ] } -} \ No newline at end of file +} diff --git a/config/source.json b/config/source.json index 169143c7..9df19068 100644 --- a/config/source.json +++ b/config/source.json @@ -8,7 +8,7 @@ }, "apcu": { "type": "url", - "url": "http://pecl.php.net/get/APCu", + "url": "https://pecl.php.net/get/APCu", "path": "php-src/ext/apcu", "filename": "apcu.tgz", "license": { @@ -51,6 +51,15 @@ "path": "LICENSE" } }, + "ext-glfw": { + "type": "git", + "url": "https://github.com/mario-deluna/php-glfw", + "rev": "master", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ext-imagick": { "type": "url", "url": "https://pecl.php.net/get/imagick", @@ -61,9 +70,29 @@ "path": "LICENSE" } }, + "ext-memcache": { + "type": "url", + "url": "https://pecl.php.net/get/memcache", + "path": "php-src/ext/memcache", + "filename": "memcache.tgz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, + "ext-snappy": { + "type": "git", + "path": "php-src/ext/snappy", + "rev": "master", + "url": "https://github.com/kjdev/php-ext-snappy", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "ext-ssh2": { "type": "url", - "url": "http://pecl.php.net/get/ssh2", + "url": "https://pecl.php.net/get/ssh2", "path": "php-src/ext/ssh2", "filename": "ssh2.tgz", "license": { @@ -91,17 +120,17 @@ } }, "gmp": { - "type": "filelist", - "url": "https://gmplib.org/download/gmp/", - "regex": "/href=\"(?gmp-(?[^\"]+)\\.tar\\.xz)\"/", + "type": "ghtagtar", + "repo": "alisw/GMP", "license": { "type": "text", "text": "Since version 6, GMP is distributed under the dual licenses, GNU LGPL v3 and GNU GPL v2. These licenses make the library free to use, share, and improve, and allow you to pass on the result. The GNU licenses give freedoms, but also set firm restrictions on the use with non-free programs." } }, "icu": { - "type": "url", - "url": "https://github.com/unicode-org/icu/releases/download/release-73-1/icu4c-73_1-src.tgz", + "type": "ghrel", + "repo": "unicode-org/icu", + "match": "icu4c.+-src\\.tgz", "license": { "type": "file", "path": "LICENSE" @@ -117,7 +146,7 @@ }, "inotify": { "type": "url", - "url": "http://pecl.php.net/get/inotify", + "url": "https://pecl.php.net/get/inotify", "path": "php-src/ext/inotify", "filename": "inotify.tgz", "license": { @@ -134,6 +163,15 @@ "path": "LICENSE" } }, + "ldap": { + "type": "filelist", + "url": "https://www.openldap.org/software/download/OpenLDAP/openldap-release/", + "regex": "/href=\"(?openldap-(?[^\"]+)\\.tgz)\"/", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "libavif": { "type": "ghtar", "repo": "AOMediaCodec/libavif", @@ -185,22 +223,22 @@ "path": "COPYING" } }, + "libmemcached": { + "type": "git", + "url": "https://github.com/static-php/libmemcached-macos.git", + "rev": "master", + "license": { + "type": "file", + "path": "COPYING" + } + }, "libpng": { "type": "git", "url": "https://git.code.sf.net/p/libpng/code", "rev": "libpng16", - "license": { - "type": "text", - "text": "COPYRIGHT NOTICE, DISCLAIMER, and LICENSE\n=========================================\n\nPNG Reference Library License version 2\n---------------------------------------\n\n * Copyright (c) 1995-2019 The PNG Reference Library Authors.\n * Copyright (c) 2018-2019 Cosmin Truta.\n * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson.\n * Copyright (c) 1996-1997 Andreas Dilger.\n * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n\nThe software is supplied \"as is\", without warranty of any kind,\nexpress or implied, including, without limitation, the warranties\nof merchantability, fitness for a particular purpose, title, and\nnon-infringement. In no event shall the Copyright owners, or\nanyone distributing the software, be liable for any damages or\nother liability, whether in contract, tort or otherwise, arising\nfrom, out of, or in connection with the software, or the use or\nother dealings in the software, even if advised of the possibility\nof such damage.\n\nPermission is hereby granted to use, copy, modify, and distribute\nthis software, or portions hereof, for any purpose, without fee,\nsubject to the following restrictions:\n\n 1. The origin of this software must not be misrepresented; you\n must not claim that you wrote the original software. If you\n use this software in a product, an acknowledgment in the product\n documentation would be appreciated, but is not required.\n\n 2. Altered source versions must be plainly marked as such, and must\n not be misrepresented as being the original software.\n\n 3. This Copyright notice may not be removed or altered from any\n source or altered source distribution.\n\n\nPNG Reference Library License version 1 (for libpng 0.5 through 1.6.35)\n-----------------------------------------------------------------------\n\nlibpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are\nCopyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are\nderived from libpng-1.0.6, and are distributed according to the same\ndisclaimer and license as libpng-1.0.6 with the following individuals\nadded to the list of Contributing Authors:\n\n Simon-Pierre Cadieux\n Eric S. Raymond\n Mans Rullgard\n Cosmin Truta\n Gilles Vollant\n James Yu\n Mandar Sahastrabuddhe\n Google Inc.\n Vadim Barkov\n\nand with the following additions to the disclaimer:\n\n There is no warranty against interference with your enjoyment of\n the library or against infringement. There is no warranty that our\n efforts or the library will fulfill any of your particular purposes\n or needs. This library is provided with all faults, and the entire\n risk of satisfactory quality, performance, accuracy, and effort is\n with the user.\n\nSome files in the \"contrib\" directory and some configure-generated\nfiles that are distributed with libpng have other copyright owners, and\nare released under other open source licenses.\n\nlibpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are\nCopyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from\nlibpng-0.96, and are distributed according to the same disclaimer and\nlicense as libpng-0.96, with the following individuals added to the\nlist of Contributing Authors:\n\n Tom Lane\n Glenn Randers-Pehrson\n Willem van Schaik\n\nlibpng versions 0.89, June 1996, through 0.96, May 1997, are\nCopyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88,\nand are distributed according to the same disclaimer and license as\nlibpng-0.88, with the following individuals added to the list of\nContributing Authors:\n\n John Bowler\n Kevin Bracey\n Sam Bushell\n Magnus Holmgren\n Greg Roelofs\n Tom Tanner\n\nSome files in the \"scripts\" directory have other copyright owners,\nbut are released under this license.\n\nlibpng versions 0.5, May 1995, through 0.88, January 1996, are\nCopyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.\n\nFor the purposes of this copyright and license, \"Contributing Authors\"\nis defined as the following set of individuals:\n\n Andreas Dilger\n Dave Martindale\n Guy Eric Schalnat\n Paul Schmidt\n Tim Wegner\n\nThe PNG Reference Library is supplied \"AS IS\". The Contributing\nAuthors and Group 42, Inc. disclaim all warranties, expressed or\nimplied, including, without limitation, the warranties of\nmerchantability and of fitness for any purpose. The Contributing\nAuthors and Group 42, Inc. assume no liability for direct, indirect,\nincidental, special, exemplary, or consequential damages, which may\nresult from the use of the PNG Reference Library, even if advised of\nthe possibility of such damage.\n\nPermission is hereby granted to use, copy, modify, and distribute this\nsource code, or portions hereof, for any purpose, without fee, subject\nto the following restrictions:\n\n 1. The origin of this source code must not be misrepresented.\n\n 2. Altered versions must be plainly marked as such and must not\n be misrepresented as being the original source.\n\n 3. This Copyright notice may not be removed or altered from any\n source or altered source distribution.\n\nThe Contributing Authors and Group 42, Inc. specifically permit,\nwithout fee, and encourage the use of this source code as a component\nto supporting the PNG file format in commercial products. If you use\nthis source code in a product, acknowledgment is not required but would\nbe appreciated.\n" - } - }, - "libressl": { - "type": "filelist", - "url": "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/", - "regex": "/href=\"(?libressl-(?[^\"]+)\\.tar\\.gz)\"/", "license": { "type": "file", - "path": "COPYING" + "path": "LICENSE" } }, "libsodium": { @@ -221,8 +259,8 @@ } }, "libwebp": { - "type": "ghtagtar", - "repo": "webmproject/libwebp", + "type": "url", + "url": "https://github.com/webmproject/libwebp/archive/refs/tags/v1.3.2.tar.gz", "license": { "type": "file", "path": "COPYING" @@ -230,12 +268,21 @@ }, "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" } }, + "libxslt": { + "type": "filelist", + "url": "https://download.gnome.org/sources/libxslt/1.1/", + "regex": "/href=\"(?libxslt-(?[^\"]+)\\.tar\\.xz)\"/", + "license": { + "type": "file", + "path": "COPYING" + } + }, "libyaml": { "type": "ghrel", "repo": "yaml/libyaml", @@ -262,11 +309,21 @@ "path": "COPYING" } }, + "memcached": { + "type": "url", + "url": "https://pecl.php.net/get/memcached", + "path": "php-src/ext/memcached", + "filename": "memcached.tgz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "micro": { "type": "git", "path": "php-src/sapi/micro", "rev": "master", - "url": "https://github.com/crazywhalecc/phpmicro", + "url": "https://github.com/static-php/phpmicro", "license": { "type": "file", "path": "LICENSE" @@ -328,7 +385,8 @@ } }, "postgresql": { - "type": "custom", + "type": "url", + "url": "https://ftp.postgresql.org/pub/source/v15.1/postgresql-15.1.tar.gz", "license": { "type": "file", "path": "COPYRIGHT" @@ -336,7 +394,7 @@ }, "protobuf": { "type": "url", - "url": "http://pecl.php.net/get/protobuf", + "url": "https://pecl.php.net/get/protobuf", "path": "php-src/ext/protobuf", "filename": "protobuf.tgz", "license": { @@ -372,9 +430,19 @@ "path": "COPYING" } }, + "snappy": { + "type": "git", + "repo": "google/snappy", + "rev": "main", + "url": "https://github.com/google/snappy", + "license": { + "type": "file", + "path": "COPYING" + } + }, "sqlite": { "type": "url", - "url": "https://www.sqlite.org/2023/sqlite-autoconf-3410100.tar.gz", + "url": "https://www.sqlite.org/2023/sqlite-autoconf-3430200.tar.gz", "license": { "type": "text", "text": "The author disclaims copyright to this source code. In place of\na legal notice, here is a blessing:\n\n * May you do good and not evil.\n * May you find forgiveness for yourself and forgive others.\n * May you share freely, never taking more than you give." @@ -400,6 +468,16 @@ "path": "LICENSE" } }, + "xlswriter": { + "type": "url", + "url": "https://pecl.php.net/get/xlswriter", + "path": "php-src/ext/xlswriter", + "filename": "xlswriter.tgz", + "license": { + "type": "file", + "path": "LICENSE" + } + }, "xz": { "type": "filelist", "url": "https://tukaani.org/xz/", @@ -437,4 +515,4 @@ "path": "LICENSE" } } -} \ No newline at end of file +} diff --git a/ext-support.md b/ext-support.md index 5889ff1e..a7068e65 100644 --- a/ext-support.md +++ b/ext-support.md @@ -1,111 +1,3 @@ # Extension List -> - yes: supported and tested -> - untested: supported, but not tested -> - empty: not supported yet -> - no with issue link: not supported yet due to issue -> - partial with issue link: supported but not perfect due to issue - -| | Linux | macOS | Windows | -|------------|---------------------------------------------------------------------|----------------------------------------------------------------|---------| -| apcu | yes, untested | yes, untested | | -| bcmath | yes | yes | | -| bz2 | yes | yes | | -| calendar | yes | yes | | -| ctype | yes | yes | | -| curl | yes | yes | | -| dba | yes | yes | | -| dom | yes | yes | | -| enchant | | | | -| event | yes | yes | | -| exif | yes | yes | | -| ffi | | yes, [docs]() | | -| filter | yes | yes | | -| fileinfo | yes | yes | | -| ftp | yes | yes | | -| gd | yes | yes | | -| gettext | | | | -| gmp | yes | yes | | -| iconv | yes | yes | | -| imagick | yes | yes | | -| inotify | yes | yes | | -| intl | | | | -| mbstring | yes | yes | | -| mbregex | yes | yes | | -| mcrypt | | [no](https://github.com/crazywhalecc/static-php-cli/issues/32) | | -| mongodb | yes | yes | | -| mysqli | yes | yes | | -| mysqlnd | yes | yes | | -| openssl | yes | yes | | -| password-argon2 | | | | -| pcntl | yes | yes | | -| pdo | yes | yes | | -| pdo_mysql | yes | yes | | -| pdo_sqlite | yes | yes | | -| pdo_pgsql | | | | -| phar | yes | yes | | -| posix | yes | yes | | -| protobuf | yes | yes | | -| readline | yes, untested | yes, untested | | -| redis | yes | yes | | -| session | yes | yes | | -| shmop | yes | yes | | -| simplexml | yes | yes | | -| soap | yes | yes | | -| sockets | yes | yes | | -| sodium | yes | yes | | -| sqlite3 | yes | yes | | -| ssh2 | yes, untested | yes, untested | | -| swow | yes | yes | | -| swoole | [partial](https://github.com/crazywhalecc/static-php-cli/issues/51) | yes | | -| tokenizer | yes | yes | | -| xlswriter | | | | -| xml | yes | yes | | -| xmlreader | yes, untested | yes, untested | | -| xmlwriter | yes, untested | yes, untested | | -| zip | yes, untested | yes, untested | | -| zlib | yes | yes | | -| zstd | yes | yes | | - -## Additional Requirements - -- phpmicro requires PHP >= 8.0 -- swoole >= 5.0 requires PHP >= 8.0 -- swow requires PHP >= 8.0 - -## Typical Extension List Example - -Here are some extension list example for different use. - -- For general use: `"bcmath,bz2,calendar,ctype,curl,dom,exif,fileinfo,ftp,filter,gd,iconv,xml,mbstring,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,redis,simplexml,soap,sockets,sqlite3,tokenizer,xmlwriter,xmlreader,zlib,zip"` -- For static-php-cli self: `"posix,pcntl,phar,tokenizer,iconv,zlib"` -- For static-php-cli self (with dev dependencies): `"posix,pcntl,phar,tokenizer,iconv,zlib,xml,dom,xmlwriter,xmlreader,fileinfo"` -- Minimum, with no extension: `""` - -Compile with all supported extensions (exclude `swow`, `swoole`, because these will change the default behavior of php): - -```bash -bin/spc build --build-all bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,iconv,mbregex,mbstring,mongodb,mysqli,mysqlnd,openssl,pcntl,pdo,pdo_mysql,pdo_sqlite,phar,posix,protobuf,redis,session,shmop,simplexml,soap,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,yaml,zip,zlib,zstd --with-libs=libjpeg,freetype,libwebp,libavif --debug -``` - -## Additional Libraries - -Some extensions have soft dependencies, you can enable extra features by adding these libs using `--with-libs`. - -For example, to compile with gd extension, with `libwebp, libgif, libavif, libjpeg, freetype` extra features: - -```bash -bin/spc build gd --with-libs=libjpeg,freetype,libwebp,libavif --build-cli -``` - -> If you don't add them, your compilation will not enable these features. - -## Limitations - -- swow and swoole cannot be compiled at the same time. -- openssl needs manual configuration for ssl certificate. (TODO: I will write a wiki about it) -- some extensions need system configuration, e.g. `curl` and `openssl` will search ssl certificate on your system. - -## Bugs and TODOs - -See [#32](https://github.com/crazywhalecc/static-php-cli/issues/32). +See: [Docs - Extensions](https://static-php.dev/en/guide/extensions.html) diff --git a/quickstart/linux/README.md b/quickstart/linux/README.md deleted file mode 100644 index c6090a30..00000000 --- a/quickstart/linux/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# 快速启动容器环境 - -> 提供了 debian 11 构建 和 alpine 构建环境 -> 任意选一个就可以 - -## debian 11 构建环境 - -```bash - -# 启动 debian 11 容器环境 -sh quickstart/linux/run-debian-11-container.sh - -# 进入容器 -sh quickstart/linux/connection-static-php-cli.sh - -# 准备构建基础软件 -sh quickstart/linux/debian-11-init.sh - -``` - -## aline 构建环境 - -```bash - -# 启动 alpine 容器环境 -sh quickstart/linux/run-alpine-3.16-container.sh - -# 进入容器 -sh sh quickstart/linux/connection-static-php-cli.sh - -# 准备构建基础软件 -sh quickstart/linux/alpine-3.16-init.sh - -``` \ No newline at end of file diff --git a/quickstart/linux/alpine-3.16-init.sh b/quickstart/linux/alpine-3.16-init.sh deleted file mode 100644 index 8aed86b6..00000000 --- a/quickstart/linux/alpine-3.16-init.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -set -exu -__DIR__=$( - cd "$(dirname "$0")" - pwd -) -cd ${__DIR__} - - -# use china mirror -# bash quickstart/linux/x86_64/alpine-3.16-init.sh --mirror china -mirror='' -while [ $# -gt 0 ]; do - case "$1" in - --mirror) - mirror="$2" - shift - ;; - --*) - echo "Illegal option $1" - ;; - esac - shift $(( $# > 0 ? 1 : 0 )) -done - -case "$mirror" in - china) - test -f /etc/apk/repositories.save || cp /etc/apk/repositories /etc/apk/repositories.save - sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories - ;; - -esac - - -apk update - -apk add vim alpine-sdk xz autoconf automake linux-headers clang-dev clang lld libtool cmake bison re2c gettext coreutils -apk add bash p7zip zip unzip flex pkgconf ca-certificates -apk add wget git curl \ No newline at end of file diff --git a/quickstart/linux/connection-static-php-cli.sh b/quickstart/linux/connection-static-php-cli.sh deleted file mode 100644 index daabc010..00000000 --- a/quickstart/linux/connection-static-php-cli.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -exu -__DIR__=$( - cd "$(dirname "$0")" - pwd -) - -cd ${__DIR__} - -docker exec -it static-php-cli-dev-1 sh diff --git a/quickstart/linux/debian-11-init.sh b/quickstart/linux/debian-11-init.sh deleted file mode 100644 index dbd8478c..00000000 --- a/quickstart/linux/debian-11-init.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -set -exu -__DIR__=$( - cd "$(dirname "$0")" - pwd -) -cd ${__DIR__} - -# use china mirror -# bash quickstart/linux/x86_64/debian-11-init.sh --mirror china -mirror='' -while [ $# -gt 0 ]; do - case "$1" in - --mirror) - mirror="$2" - shift - ;; - --*) - echo "Illegal option $1" - ;; - esac - shift $(( $# > 0 ? 1 : 0 )) -done - -case "$mirror" in - china) - test -f /etc/apt/sources.list.save || cp /etc/apt/sources.list /etc/apt/sources.list.save - sed -i "s@deb.debian.org@mirrors.ustc.edu.cn@g" /etc/apt/sources.list && \ - sed -i "s@security.debian.org@mirrors.ustc.edu.cn@g" /etc/apt/sources.list - ;; - -esac - - -apt update -y -apt install -y git curl wget ca-certificates -apt install -y xz-utils autoconf automake lld libtool cmake bison re2c gettext coreutils lzip zip unzip -apt install -y pkg-config bzip2 flex p7zip -apt install -y musl-tools g++ -apt install -y clang diff --git a/quickstart/linux/run-alpine-3.16-container.sh b/quickstart/linux/run-alpine-3.16-container.sh deleted file mode 100644 index fa3ea0e5..00000000 --- a/quickstart/linux/run-alpine-3.16-container.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -exu -__DIR__=$( - cd "$(dirname "$0")" - pwd -) -__PROJECT__=$( - cd ${__DIR__}/../../ - pwd -) -cd ${__DIR__} - - -{ - docker stop static-php-cli-dev-1 -} || { - echo $? -} -cd ${__DIR__} -IMAGE=alpine:3.16 - -cd ${__DIR__} -docker run --rm --name static-php-cli-dev-1 -d -v ${__PROJECT__}:/work -w /work $IMAGE tail -f /dev/null - diff --git a/quickstart/linux/run-debian-11-container.sh b/quickstart/linux/run-debian-11-container.sh deleted file mode 100644 index c00ae1a6..00000000 --- a/quickstart/linux/run-debian-11-container.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -set -exu -__DIR__=$( - cd "$(dirname "$0")" - pwd -) -__PROJECT__=$( - cd ${__DIR__}/../../ - pwd -) -cd ${__DIR__} - - -{ - docker stop static-php-cli-dev-1 -} || { - echo $? -} -cd ${__DIR__} -IMAGE=debian:11 - -cd ${__DIR__} -docker run --rm --name static-php-cli-dev-1 -d -v ${__PROJECT__}:/work -w /work $IMAGE tail -f /dev/null - diff --git a/quickstart/prepare.sh b/quickstart/prepare.sh deleted file mode 100644 index 9f4669d1..00000000 --- a/quickstart/prepare.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/bin/bash - -set -exu -__DIR__=$( - cd "$(dirname "$0")" - pwd -) -__PROJECT__=$( - cd ${__DIR__}/../ - pwd -) -cd ${__PROJECT__} - -OS=$(uname -s) -ARCH=$(uname -m) - -if [[ $OS = "Linux" && -f /etc/os-release ]]; then - OS_NAME=$(cat /etc/os-release | grep '^ID=' | awk -F '=' '{print $2}') - # debian ubuntu alpine -fi - -composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ - -chmod +x bin/spc - -./bin/spc fetch --all --debug - -./bin/spc list-ext - -EXTENSIONS="calendar,ctype,exif,fileinfo,filter,ftp" -EXTENSIONS="${EXTENSIONS},session,tokenizer" -EXTENSIONS="${EXTENSIONS},phar,posix" -EXTENSIONS="${EXTENSIONS},iconv" -EXTENSIONS="${EXTENSIONS},xml,dom,simplexml,xmlwriter,xmlreader" -EXTENSIONS="${EXTENSIONS},phar,posix" -EXTENSIONS="${EXTENSIONS},soap" -EXTENSIONS="${EXTENSIONS},mbstring,mbregex" -EXTENSIONS="${EXTENSIONS},openssl" -EXTENSIONS="${EXTENSIONS},sockets,gmp,bcmath" -EXTENSIONS="${EXTENSIONS},pcntl" -EXTENSIONS="${EXTENSIONS},curl" -EXTENSIONS="${EXTENSIONS},zlib,zip,bz2" -EXTENSIONS="${EXTENSIONS},gd" -EXTENSIONS="${EXTENSIONS},redis" -EXTENSIONS="${EXTENSIONS},pdo,pdo_mysql,pdo_sqlite" -EXTENSIONS="${EXTENSIONS},mysqlnd,sqlite3" -EXTENSIONS="${EXTENSIONS},mongodb" -# EXTENSIONS="${EXTENSIONS},swoole" -EXTENSIONS="${EXTENSIONS},swow" - -./bin/spc build "${EXTENSIONS}" --build-cli --cc=clang --cxx=clang++ --debug -# ./bin/spc build "${EXTENSIONS}" --build-cli --cc=gcc --cxx=g++ --debug diff --git a/src/SPC/ConsoleApplication.php b/src/SPC/ConsoleApplication.php index 9253aa15..2de132e0 100644 --- a/src/SPC/ConsoleApplication.php +++ b/src/SPC/ConsoleApplication.php @@ -4,47 +4,49 @@ declare(strict_types=1); namespace SPC; -use SPC\command\DeployCommand; -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\Command; use Symfony\Component\Console\Command\HelpCommand; use Symfony\Component\Console\Command\ListCommand; /** - * spc 应用究级入口 + * static-php-cli console app entry */ -class ConsoleApplication extends Application +final class ConsoleApplication extends Application { - public const VERSION = '2.0-rc1'; + public const VERSION = '2.0.0-rc8'; - /** - * @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(), - // 生产环境不显示详细的调试错误,只使用 symfony console 自带的错误显示 - $this->setCatchExceptions(file_exists(ROOT_DIR . '/.prod') || !in_array('--debug', $argv)); - - // 通过扫描目录 src/static-php-cli/command/ 添加子命令 - $commands = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/command', 'SPC\\command'); - $this->addCommands(array_map(function ($x) { return new $x(); }, array_filter($commands, function ($y) { - if (is_a($y, DeployCommand::class, true) && (class_exists('\\Phar') && \Phar::running() || !class_exists('\\Phar'))) { - return false; - } - $reflection = new \ReflectionClass($y); - return !$reflection->isAbstract() && !$reflection->isInterface(); - }))); + // Dev commands + new AllExtCommand(), + new PhpVerCommand(), + new SortConfigCommand(), + ] + ); } - /** - * 重载以去除一些不必要的默认命令 - */ protected function getDefaultCommands(): array { return [new HelpCommand(), new ListCommand()]; diff --git a/src/SPC/builder/BuilderBase.php b/src/SPC/builder/BuilderBase.php index 900bb4ed..6e46886f 100644 --- a/src/SPC/builder/BuilderBase.php +++ b/src/SPC/builder/BuilderBase.php @@ -9,48 +9,38 @@ use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\FileSystem; +use SPC\store\SourceExtractor; use SPC\util\CustomExt; use SPC\util\DependencyUtil; abstract class BuilderBase { - /** @var bool 是否启用 ZTS 线程安全 */ - public bool $zts = false; - - /** @var string 编译目标架构 */ - public string $arch; - - /** @var string GNU 格式的编译目标架构 */ - public string $gnu_arch; - - /** @var int 编译进程数 */ + /** @var int Concurrency */ public int $concurrency = 1; - /** @var array 要编译的 libs 列表 */ + /** @var array libraries */ protected array $libs = []; - /** @var array 要编译的扩展列表 */ + /** @var array extensions */ protected array $exts = []; - /** @var array 要编译的扩展列表(仅名字列表,用于最后生成编译的扩展列表给 micro) */ - protected array $plain_extensions = []; - - /** @var bool 本次编译是否只编译 libs,不编译 PHP */ + /** @var bool compile libs only (just mark it) */ protected bool $libs_only = false; - /** @var bool 是否 strip 最终的二进制 */ - protected bool $strip = true; + /** @var array compile options */ + protected array $options = []; /** - * 构建指定列表的 libs + * Build libraries * + * @param array $libraries Libraries to build * @throws FileSystemException * @throws RuntimeException * @throws WrongUsageException */ public function buildLibs(array $libraries): void { - // 通过扫描目录查找 lib + // search all supported libs $support_lib_list = []; $classes = FileSystem::getClassesPsr4( ROOT_DIR . '/src/SPC/builder/' . osfamily2dir() . '/library', @@ -62,19 +52,22 @@ abstract class BuilderBase } } - // 如果传入了空,则默认检查和安置所有支持的lib,libraries为要build的,support_lib_list为支持的列表 + // if no libs specified, compile all supported libs if ($libraries === [] && $this->isLibsOnly()) { $libraries = array_keys($support_lib_list); } + + // pkg-config must be compiled first, whether it is specified or not if (!in_array('pkg-config', $libraries)) { array_unshift($libraries, 'pkg-config'); } - // 排序 libs,根据依赖计算一个新的列表出来 + // append dependencies $libraries = DependencyUtil::getLibsByDeps($libraries); - // 过滤不支持的库后添加 + // add lib object for builder foreach ($libraries as $library) { + // if some libs are not supported (but in config "lib.json", throw exception) if (!isset($support_lib_list[$library])) { throw new RuntimeException('library [' . $library . '] is in the lib.json list but not supported to compile, but in the future I will support it!'); } @@ -82,16 +75,17 @@ abstract class BuilderBase $this->addLib($lib); } - // 计算依赖,经过这里的遍历,如果没有抛出异常,说明依赖符合要求,可以继续下面的 + // calculate and check dependencies foreach ($this->libs as $lib) { $lib->calcDependency(); } - $this->initSource(libs: $libraries); + // extract sources + SourceExtractor::initSource(libs: $libraries); - // 构建库 + // 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'), @@ -101,9 +95,9 @@ abstract class BuilderBase } /** - * 添加要编译的 Lib 库 + * Add library to build. * - * @param LibraryBase $library Lib 库对象 + * @param LibraryBase $library Library object */ public function addLib(LibraryBase $library): void { @@ -111,9 +105,7 @@ abstract class BuilderBase } /** - * 获取要编译的 Lib 库对象 - * - * @param string $name 库名称 + * Get library object by name. */ public function getLib(string $name): ?LibraryBase { @@ -121,9 +113,17 @@ abstract class BuilderBase } /** - * 添加要编译的扩展 + * Get all library objects. * - * @param Extension $extension 扩展对象 + * @return LibraryBase[] + */ + public function getLibs(): array + { + return $this->libs; + } + + /** + * Add extension to build. */ public function addExt(Extension $extension): void { @@ -131,9 +131,7 @@ abstract class BuilderBase } /** - * 获取要编译的扩展对象 - * - * @param string $name 扩展名称 + * Get extension object by name. */ public function getExt(string $name): ?Extension { @@ -141,7 +139,41 @@ abstract class BuilderBase } /** - * 设置本次 Builder 是否为仅编译库的模式 + * Get all extension objects. + * + * @return Extension[] + */ + public function getExts(): array + { + return $this->exts; + } + + /** + * Check if there is a cpp extensions or libraries. + * + * @throws FileSystemException + * @throws WrongUsageException + */ + public function hasCpp(): bool + { + // judge cpp-extension + $exts = array_keys($this->getExts()); + foreach ($exts as $ext) { + if (Config::getExt($ext, 'cpp-extension', false) === true) { + return true; + } + } + $libs = array_keys($this->getLibs()); + foreach ($libs as $lib) { + if (Config::getLib($lib, 'cpp-library', false) === true) { + return true; + } + } + return false; + } + + /** + * Set libs only mode. */ public function setLibsOnly(bool $status = true): void { @@ -149,19 +181,21 @@ abstract class BuilderBase } /** - * 检验 ext 扩展列表是否合理,并声明 Extension 对象,检查扩展的依赖 + * Verify the list of "ext" extensions for validity and declare an Extension object to check the dependencies of the extensions. * * @throws FileSystemException * @throws RuntimeException + * @throws \ReflectionException + * @throws WrongUsageException */ public function proveExts(array $extensions): void { CustomExt::loadCustomExt(); - $this->initSource(sources: ['php-src']); + SourceExtractor::initSource(sources: ['php-src']); if ($this->getPHPVersionID() >= 80000) { - $this->initSource(sources: ['micro']); + SourceExtractor::initSource(sources: ['micro']); } - $this->initSource(exts: $extensions); + SourceExtractor::initSource(exts: $extensions); foreach ($extensions as $extension) { $class = CustomExt::getExtClass($extension); $ext = new $class($extension, $this); @@ -169,27 +203,23 @@ abstract class BuilderBase } foreach ($this->exts as $ext) { - // 检查下依赖就行了,作用是导入依赖给 Extension 对象,今后可以对库依赖进行选择性处理 $ext->checkDependency(); } - - $this->plain_extensions = $extensions; } /** - * 开始构建 PHP + * Start to build PHP * - * @param int $build_target 规则 - * @param bool $bloat 保留 + * @param int $build_target Build target, see BUILD_TARGET_* */ - abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false); + abstract public function buildPHP(int $build_target = BUILD_TARGET_NONE); /** - * 生成依赖的扩展编译启用参数 - * 例如 --enable-mbstring 等 + * Generate extension enable arguments for configure. + * e.g. --enable-mbstring * - * @throws RuntimeException * @throws FileSystemException + * @throws WrongUsageException */ public function makeExtensionArgs(): string { @@ -202,7 +232,7 @@ abstract class BuilderBase } /** - * 返回是否只编译 libs 的模式 + * Get libs only mode. */ public function isLibsOnly(): bool { @@ -210,15 +240,30 @@ abstract class BuilderBase } /** - * 获取当前即将编译的 PHP 的版本 ID,五位数那个 + * 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'); } + /** + * Get build type name string to display. + * + * @param int $type Build target type + */ public function getBuildTypeName(int $type): string { $ls = []; @@ -231,16 +276,64 @@ 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); } - public function setStrip(bool $strip): void + /** + * Get builder options (maybe changed by user) + * + * @param string $key Option key + * @param mixed $default If not exists, return this value + */ + public function getOption(string $key, mixed $default = null): mixed { - $this->strip = $strip; + return $this->options[$key] ?? $default; } /** - * 检查是否存在 lib 库对应的源码,如果不存在,则抛出异常 + * Get all builder options + */ + public function getOptions(): array + { + return $this->options; + } + + /** + * Set builder options if not exists. + */ + public function setOptionIfNotExist(string $key, mixed $value): void + { + if (!isset($this->options[$key])) { + $this->options[$key] = $value; + } + } + + /** + * Set builder options. + */ + public function setOption(string $key, mixed $value): void + { + $this->options[$key] = $value; + } + + public function getEnvString(array $vars = ['cc', 'cxx', 'ar', 'ld']): string + { + $env = []; + foreach ($vars as $var) { + $var = strtoupper($var); + if (getenv($var) !== false) { + $env[] = "{$var}=" . getenv($var); + } + } + return implode(' ', $env); + } + + /** + * Check if all libs are downloaded. + * If not, throw exception. * * @throws RuntimeException */ @@ -261,52 +354,4 @@ abstract class BuilderBase ); } } - - protected function initSource(?array $sources = null, ?array $libs = null, ?array $exts = null): void - { - if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) { - throw new WrongUsageException('Download lock file "downloads/.lock.json" not found, maybe you need to download sources first ?'); - } - $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true); - - $sources_extracted = []; - // source check exist - if (is_array($sources)) { - foreach ($sources as $source) { - $sources_extracted[$source] = true; - } - } - // lib check source exist - if (is_array($libs)) { - foreach ($libs as $lib) { - // get source name for lib - $source = Config::getLib($lib, 'source'); - $sources_extracted[$source] = true; - } - } - // ext check source exist - if (is_array($exts)) { - foreach ($exts as $ext) { - // get source name for ext - if (Config::getExt($ext, 'type') !== 'external') { - continue; - } - $source = Config::getExt($ext, 'source'); - $sources_extracted[$source] = true; - } - } - - // start check - foreach ($sources_extracted as $source => $item) { - if (!isset($lock[$source])) { - throw new WrongUsageException('Source [' . $source . '] not downloaded, you should download it first !'); - } - - // check source dir exist - $check = $lock[$source]['move_path'] === null ? SOURCE_PATH . '/' . $source : SOURCE_PATH . '/' . $lock[$source]['move_path']; - if (!is_dir($check)) { - FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$source]['filename'] ?? $lock[$source]['dirname']), $lock[$source]['move_path']); - } - } - } } diff --git a/src/SPC/builder/BuilderProvider.php b/src/SPC/builder/BuilderProvider.php index a4e54032..552af229 100644 --- a/src/SPC/builder/BuilderProvider.php +++ b/src/SPC/builder/BuilderProvider.php @@ -4,9 +4,10 @@ declare(strict_types=1); namespace SPC\builder; +use SPC\builder\freebsd\BSDBuilder; use SPC\builder\linux\LinuxBuilder; use SPC\builder\macos\MacOSBuilder; -use SPC\builder\windows\WindowsBuilder; +use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; use Symfony\Component\Console\Input\InputInterface; @@ -17,7 +18,9 @@ use Symfony\Component\Console\Input\InputInterface; class BuilderProvider { /** + * @throws FileSystemException * @throws RuntimeException + * @throws WrongUsageException */ public static function makeBuilderByInput(InputInterface $input): BuilderBase { @@ -27,18 +30,9 @@ class BuilderProvider // vs_ver: $input->getOption('vs-ver'), // arch: $input->getOption('arch'), // ), - 'Darwin' => new MacOSBuilder( - cc: $input->getOption('cc'), - cxx: $input->getOption('cxx'), - arch: $input->getOption('arch'), - zts: $input->hasOption('enable-zts') ? $input->getOption('enable-zts') : false, - ), - 'Linux' => new LinuxBuilder( - cc: $input->getOption('cc'), - cxx: $input->getOption('cxx'), - arch: $input->getOption('arch'), - zts: $input->hasOption('enable-zts') ? $input->getOption('enable-zts') : false, - ), + 'Darwin' => new MacOSBuilder($input->getOptions()), + 'Linux' => new LinuxBuilder($input->getOptions()), + 'BSD' => new BSDBuilder($input->getOptions()), default => throw new WrongUsageException('Current OS "' . PHP_OS_FAMILY . '" is not supported yet'), }; } diff --git a/src/SPC/builder/Extension.php b/src/SPC/builder/Extension.php index 7e81cb88..922d4f3d 100644 --- a/src/SPC/builder/Extension.php +++ b/src/SPC/builder/Extension.php @@ -34,7 +34,7 @@ class Extension /** * 获取开启该扩展的 PHP 编译添加的参数 * - * @throws FileSystemException|RuntimeException + * @throws FileSystemException * @throws WrongUsageException */ public function getConfigureArg(): string @@ -46,6 +46,7 @@ class Extension break; case 'Darwin': case 'Linux': + case 'BSD': $arg .= $this->getUnixConfigureArg(); break; } @@ -56,7 +57,6 @@ class Extension * 根据 ext 的 arg-type 获取对应开启的参数,一般都是 --enable-xxx 和 --with-xxx * * @throws FileSystemException - * @throws RuntimeException * @throws WrongUsageException */ public function getEnableArg(): string @@ -136,6 +136,33 @@ class Extension return ''; } + /** + * Patch code before ./buildconf + * If you need to patch some code, overwrite this and return true + */ + public function patchBeforeBuildconf(): bool + { + return false; + } + + /** + * Patch code before ./configure + * If you need to patch some code, overwrite this and return true + */ + public function patchBeforeConfigure(): bool + { + return false; + } + + /** + * Patch code before make + * If you need to patch some code, overwrite this and return true + */ + public function patchBeforeMake(): bool + { + return false; + } + /** * @throws RuntimeException */ diff --git a/src/SPC/builder/LibraryBase.php b/src/SPC/builder/LibraryBase.php index 7f234a5b..bd7752ac 100644 --- a/src/SPC/builder/LibraryBase.php +++ b/src/SPC/builder/LibraryBase.php @@ -4,25 +4,22 @@ declare(strict_types=1); namespace SPC\builder; -use SPC\builder\macos\library\MacOSLibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\Config; -/** - * Lib 库的基类操作对象 - */ abstract class LibraryBase { - /** @var string lib 依赖名称,必须重写 */ + /** @var string */ public const NAME = 'unknown'; - /** @var string lib 依赖的根目录 */ protected string $source_dir; - /** @var array 依赖列表 */ protected array $dependencies = []; + protected bool $patched = false; + /** * @throws RuntimeException */ @@ -35,7 +32,7 @@ abstract class LibraryBase } /** - * 获取 lib 库的根目录 + * Get current lib source root dir. */ public function getSourceDir(): string { @@ -43,10 +40,9 @@ abstract class LibraryBase } /** - * 获取当前 lib 库的所有依赖列表 + * Get current lib dependencies. * - * @param bool $recursive 是否递归获取(默认为 False) - * @return array 依赖的 Map + * @return array */ public function getDependencies(bool $recursive = false): array { @@ -55,7 +51,6 @@ abstract class LibraryBase return $this->dependencies; } - // 下面为递归获取依赖列表,根据依赖顺序 $deps = []; $added = 1; @@ -78,20 +73,21 @@ abstract class LibraryBase } /** - * 计算依赖列表,不符合依赖将抛出异常 + * Calculate dependencies for current library. * * @throws RuntimeException * @throws FileSystemException + * @throws WrongUsageException */ public function calcDependency(): void { - // 先从配置文件添加依赖,这里根据不同的操作系统分别选择不同的元信息 + // Add dependencies from the configuration file. Here, choose different metadata based on the operating system. /* - 选择规则: - 如果是 Windows 系统,则依次尝试有无 lib-depends-windows、lib-depends-win、lib-depends。 - 如果是 macOS 系统,则依次尝试 lib-depends-darwin、lib-depends-unix、lib-depends。 - 如果是 Linux 系统,则依次尝试 lib-depends-linux、lib-depends-unix、lib-depends。 - */ + Rules: + If it is a Windows system, try the following dependencies in order: lib-depends-windows, lib-depends-win, lib-depends. + If it is a macOS system, try the following dependencies in order: lib-depends-darwin, lib-depends-unix, lib-depends. + If it is a Linux system, try the following dependencies in order: lib-depends-linux, lib-depends-unix, lib-depends. + */ foreach (Config::getLib(static::NAME, 'lib-depends', []) as $dep_name) { $this->addLibraryDependency($dep_name); } @@ -101,11 +97,10 @@ abstract class LibraryBase } /** - * 获取当前库编译出来获取到的静态库文件列表 + * Get config static libs. * - * @return string[] 获取编译出来后的需要的静态库文件列表 * @throws FileSystemException - * @throws RuntimeException + * @throws WrongUsageException */ public function getStaticLibs(): array { @@ -113,11 +108,10 @@ abstract class LibraryBase } /** - * 获取当前 lib 编译出来的 C Header 文件列表 + * Get config headers. * - * @return string[] 获取编译出来后需要的 C Header 文件列表 * @throws FileSystemException - * @throws RuntimeException + * @throws WrongUsageException */ public function getHeaders(): array { @@ -125,73 +119,89 @@ abstract class LibraryBase } /** - * 证明该库是否已编译好且就绪,如果没有就绪,内部会调用 build 来进行构建该库 + * Try to build this library, before build, we check first. + * + * BUILD_STATUS_OK if build success + * BUILD_STATUS_ALREADY if already built + * BUILD_STATUS_FAILED if build failed * * @throws RuntimeException * @throws FileSystemException + * @throws WrongUsageException */ public function tryBuild(bool $force_build = false): int { - // 传入 true,表明直接编译 + if (file_exists($this->source_dir . '/.spc.patched')) { + $this->patched = true; + } + // force means just build if ($force_build) { logger()->info('Building required library [' . static::NAME . ']'); + if (!$this->patched && $this->patchBeforeBuild()) { + file_put_contents($this->source_dir . '/.spc.patched', 'PATCHED!!!'); + } $this->build(); return BUILD_STATUS_OK; } - // 看看这些库是不是存在,如果不存在,则调用编译并返回结果状态 + // check if these libraries exist, if not, invoke compilation and return the result status foreach ($this->getStaticLibs() as $name) { if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { $this->tryBuild(true); return BUILD_STATUS_OK; } } - // 头文件同理 + // header files the same foreach ($this->getHeaders() as $name) { if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { $this->tryBuild(true); return BUILD_STATUS_OK; } } - // pkg-config 做特殊处理,如果是 pkg-config 就检查有没有 pkg-config 二进制 - if ($this instanceof MacOSLibraryBase && static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) { + // pkg-config is treated specially. If it is pkg-config, check if the pkg-config binary exists + if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) { $this->tryBuild(true); return BUILD_STATUS_OK; } - // 到这里说明所有的文件都存在,就跳过编译 + // if all the files exist at this point, skip the compilation process return BUILD_STATUS_ALREADY; } /** - * 获取构建当前 lib 的 Builder 对象 + * Patch before build, overwrite this and return true to patch libs. + */ + public function patchBeforeBuild(): bool + { + return false; + } + + /** + * Get current builder object. */ abstract public function getBuilder(): BuilderBase; /** - * 构建该库需要调用的命令和操作 + * Build this library. * * @throws RuntimeException */ abstract protected function build(); /** - * 添加 lib 库的依赖库 + * Add lib dependency * - * @param string $name 依赖名称 - * @param bool $optional 是否是可选依赖(默认为 False) * @throws RuntimeException */ protected function addLibraryDependency(string $name, bool $optional = false): void { - // Log::i("add $name as dep of {$this->name}"); $dep_lib = $this->getBuilder()->getLib($name); - if (!$dep_lib) { - if (!$optional) { - throw new RuntimeException(static::NAME . " requires library {$name}"); - } - logger()->debug('enabling ' . static::NAME . " without {$name}"); - } else { + if ($dep_lib) { $this->dependencies[$name] = $dep_lib; + return; } + if (!$optional) { + throw new RuntimeException(static::NAME . " requires library {$name}"); + } + logger()->debug('enabling ' . static::NAME . " without {$name}"); } } diff --git a/src/SPC/builder/extension/bz2.php b/src/SPC/builder/extension/bz2.php new file mode 100644 index 00000000..88f22e56 --- /dev/null +++ b/src/SPC/builder/extension/bz2.php @@ -0,0 +1,27 @@ +builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : ''; + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-lbz2/', $this->getLibFilesString() . $frameworks); + return true; + } +} diff --git a/src/SPC/builder/extension/curl.php b/src/SPC/builder/extension/curl.php new file mode 100644 index 00000000..d4f8b078 --- /dev/null +++ b/src/SPC/builder/extension/curl.php @@ -0,0 +1,57 @@ +info('patching before-configure for curl checks'); + $file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])"; + $files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4'); + $file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [ + save_old_LDFLAGS=$LDFLAGS + ac_stuff="$5" + + save_ext_shared=$ext_shared + ext_shared=yes + PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS) + AC_CHECK_LIB([$1],[$2],[ + LDFLAGS=$save_old_LDFLAGS + ext_shared=$save_ext_shared + $3 + ],[ + LDFLAGS=$save_old_LDFLAGS + ext_shared=$save_ext_shared + unset ac_cv_lib_$1[]_$2 + $4 + ])dnl +])'; + file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2); + return true; + } + + /** + * @throws FileSystemException + * @throws WrongUsageException + */ + public function patchBeforeConfigure(): bool + { + $frameworks = $this->builder instanceof MacOSBuilder ? ' ' . $this->builder->getFrameworks(true) . ' ' : ''; + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-lcurl/', $this->getLibFilesString() . $frameworks); + return true; + } +} diff --git a/src/SPC/builder/extension/event.php b/src/SPC/builder/extension/event.php index bee7553c..7339ad17 100644 --- a/src/SPC/builder/extension/event.php +++ b/src/SPC/builder/extension/event.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\exception\FileSystemException; +use SPC\store\FileSystem; use SPC\util\CustomExt; #[CustomExt('event')] @@ -23,4 +25,13 @@ class event extends Extension } return $arg; } + + /** + * @throws FileSystemException + */ + public function patchBeforeConfigure(): bool + { + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/-levent_openssl/', $this->getLibFilesString()); + return true; + } } diff --git a/src/SPC/builder/extension/glfw.php b/src/SPC/builder/extension/glfw.php new file mode 100644 index 00000000..ad1be3a7 --- /dev/null +++ b/src/SPC/builder/extension/glfw.php @@ -0,0 +1,37 @@ +builder instanceof MacOSBuilder) { + return false; + } + $extra_libs = $this->builder->getOption('extra-libs', ''); + if (!str_contains($extra_libs, '-liconv')) { + $extra_libs .= ' -liconv'; + } + $this->builder->setOption('extra-libs', $extra_libs); + return true; + } +} diff --git a/src/SPC/builder/extension/imagick.php b/src/SPC/builder/extension/imagick.php index ed456e17..ef965d58 100644 --- a/src/SPC/builder/extension/imagick.php +++ b/src/SPC/builder/extension/imagick.php @@ -10,6 +10,15 @@ use SPC\util\CustomExt; #[CustomExt('imagick')] class imagick extends Extension { + public function patchBeforeMake(): bool + { + // imagick may call omp_pause_all which requires -lgomp + $extra_libs = $this->builder->getOption('extra-libs', ''); + $extra_libs .= ' -lgomp '; + $this->builder->setOption('extra-libs', $extra_libs); + return true; + } + public function getUnixConfigureArg(): string { return '--with-imagick=' . BUILD_ROOT_PATH; diff --git a/src/SPC/builder/extension/mbregex.php b/src/SPC/builder/extension/mbregex.php index 48d15128..6a4d1457 100644 --- a/src/SPC/builder/extension/mbregex.php +++ b/src/SPC/builder/extension/mbregex.php @@ -14,4 +14,9 @@ class mbregex extends Extension { return 'mbstring'; } + + public function getConfigureArg(): string + { + return ''; + } } diff --git a/src/SPC/builder/extension/memcache.php b/src/SPC/builder/extension/memcache.php new file mode 100644 index 00000000..eb3e2969 --- /dev/null +++ b/src/SPC/builder/extension/memcache.php @@ -0,0 +1,50 @@ +builder->getLib('openssl')) { $arg .= '--with-mongodb-ssl=openssl'; } - if ($this->builder->getLib('icu')) { - $arg .= ' --with-mongodb-icu=yes '; - } else { - $arg .= ' --with-mongodb-icu=no '; - } + $arg .= $this->builder->getLib('icu') ? ' --with-mongodb-icu=yes ' : ' --with-mongodb-icu=no '; + $arg .= $this->builder->getLib('zstd') ? ' --with-mongodb-zstd=yes ' : ' --with-mongodb-zstd=no '; + // $arg .= $this->builder->getLib('snappy') ? ' --with-mongodb-snappy=yes ' : ' --with-mongodb-snappy=no '; + $arg .= $this->builder->getLib('zlib') ? ' --with-mongodb-zlib=yes ' : ' --with-mongodb-zlib=bundled '; return $arg; } } diff --git a/src/SPC/builder/extension/openssl.php b/src/SPC/builder/extension/openssl.php new file mode 100644 index 00000000..44d0c5c7 --- /dev/null +++ b/src/SPC/builder/extension/openssl.php @@ -0,0 +1,30 @@ +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; + } + + public function getUnixConfigureArg(): string + { + return '--with-openssl=' . BUILD_ROOT_PATH . ' --with-openssl-dir=' . BUILD_ROOT_PATH; + } +} diff --git a/src/SPC/builder/extension/pdo_sqlite.php b/src/SPC/builder/extension/pdo_sqlite.php new file mode 100644 index 00000000..a33dbbba --- /dev/null +++ b/src/SPC/builder/extension/pdo_sqlite.php @@ -0,0 +1,27 @@ +getLibFilesString() + ); + return true; + } +} diff --git a/src/SPC/builder/extension/readline.php b/src/SPC/builder/extension/readline.php new file mode 100644 index 00000000..c66e7afa --- /dev/null +++ b/src/SPC/builder/extension/readline.php @@ -0,0 +1,27 @@ +getLibFilesString() + ); + return true; + } +} diff --git a/src/SPC/builder/extension/redis.php b/src/SPC/builder/extension/redis.php index 0c0d6600..ffe50d1d 100644 --- a/src/SPC/builder/extension/redis.php +++ b/src/SPC/builder/extension/redis.php @@ -12,7 +12,12 @@ class redis extends Extension { public function getUnixConfigureArg(): string { - $arg = '--enable-redis --disable-redis-session'; + $arg = '--enable-redis'; + if (!$this->builder->getExt('session')) { + $arg .= ' --disable-redis-session'; + } else { + $arg .= ' --enable-redis-session'; + } if ($this->builder->getLib('zstd')) { $arg .= ' --enable-redis-zstd --with-libzstd="' . BUILD_ROOT_PATH . '"'; } diff --git a/src/SPC/builder/extension/snappy.php b/src/SPC/builder/extension/snappy.php new file mode 100644 index 00000000..2dc2a33a --- /dev/null +++ b/src/SPC/builder/extension/snappy.php @@ -0,0 +1,33 @@ +getLibFilesString() . ($this->builder instanceof MacOSBuilder ? ' -lc++' : ' -lstdc++') + ); + return true; + } + + public function getUnixConfigureArg(): string + { + return '--enable-snappy --with-snappy-includedir="' . BUILD_ROOT_PATH . '"'; + } +} diff --git a/src/SPC/builder/extension/ssh2.php b/src/SPC/builder/extension/ssh2.php new file mode 100644 index 00000000..eeabaa72 --- /dev/null +++ b/src/SPC/builder/extension/ssh2.php @@ -0,0 +1,27 @@ +getLibFilesString() + ); + return true; + } +} diff --git a/src/SPC/builder/extension/swoole.php b/src/SPC/builder/extension/swoole.php index 492ff12c..987a0cab 100644 --- a/src/SPC/builder/extension/swoole.php +++ b/src/SPC/builder/extension/swoole.php @@ -13,10 +13,11 @@ class swoole extends Extension public function getUnixConfigureArg(): string { $arg = '--enable-swoole'; + // pgsql hook is buggy for static php + $arg .= ' --disable-swoole-pgsql'; $arg .= $this->builder->getLib('openssl') ? ' --enable-openssl' : ' --disable-openssl --without-openssl'; $arg .= $this->builder->getLib('brotli') ? (' --enable-brotli --with-brotli-dir=' . BUILD_ROOT_PATH) : ''; - // curl hook is buggy for static php - $arg .= ' --disable-swoole-curl'; + $arg .= $this->builder->getExt('curl') ? ' --enable-swoole-curl' : ' --disable-swoole-curl'; return $arg; } } diff --git a/src/SPC/builder/extension/swow.php b/src/SPC/builder/extension/swow.php index 97b5c337..a43fdc1d 100644 --- a/src/SPC/builder/extension/swow.php +++ b/src/SPC/builder/extension/swow.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace SPC\builder\extension; use SPC\builder\Extension; +use SPC\exception\RuntimeException; use SPC\util\CustomExt; #[CustomExt('swow')] @@ -17,4 +18,20 @@ class swow extends Extension $arg .= $this->builder->getLib('curl') ? ' --enable-swow-curl' : ' --disable-swow-curl'; return $arg; } + + /** + * @throws RuntimeException + */ + public function patchBeforeBuildconf(): bool + { + 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 { + f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow'); + } + return true; + } + return false; + } } diff --git a/src/SPC/builder/extension/xlswriter.php b/src/SPC/builder/extension/xlswriter.php new file mode 100644 index 00000000..37fbf970 --- /dev/null +++ b/src/SPC/builder/extension/xlswriter.php @@ -0,0 +1,17 @@ +options = $options; + + // ---------- set necessary options ---------- + // set C Compiler (default: clang) + f_putenv('CC=' . $this->getOption('cc', 'clang')); + // set C++ Composer (default: clang++) + f_putenv('CXX=' . $this->getOption('cxx', 'clang++')); + // set PATH + f_putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH')); + // set PKG_CONFIG + f_putenv('PKG_CONFIG=' . BUILD_ROOT_PATH . '/bin/pkg-config'); + // set PKG_CONFIG_PATH + f_putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig/'); + + // set arch (default: current) + $this->setOptionIfNotExist('arch', php_uname('m')); + $this->setOptionIfNotExist('gnu-arch', arch2gnu($this->getOption('arch'))); + + // ---------- set necessary compile environments ---------- + // concurrency + $this->concurrency = SystemUtil::getCpuCount(); + // cflags + $this->arch_c_flags = SystemUtil::getArchCFlags($this->getOption('arch')); + $this->arch_cxx_flags = SystemUtil::getArchCFlags($this->getOption('arch')); + // cmake toolchain + $this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('BSD', $this->getOption('arch'), $this->arch_c_flags); + + // create pkgconfig and include dir (some libs cannot create them automatically) + f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true); + f_mkdir(BUILD_INCLUDE_PATH, recursive: true); + } + + /** + * Just start to build statically linked php binary + * + * @param int $build_target build target + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + public function buildPHP(int $build_target = BUILD_TARGET_NONE): void + { + // ---------- Update extra-libs ---------- + $extra_libs = $this->getOption('extra-libs', ''); + // add libc++, some extensions or libraries need it (C++ cannot be linked statically) + $extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lc++ ' : ''); + if (!$this->getOption('bloat', false)) { + $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles()); + } else { + logger()->info('bloat linking'); + $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles()))); + } + $this->setOption('extra-libs', $extra_libs); + + SourcePatcher::patchBeforeBuildconf($this); + + shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force'); + + SourcePatcher::patchBeforeConfigure($this); + + $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 ' . + '--prefix= ' . + '--with-valgrind=no ' . // Not detect memory leak + '--enable-shared=no ' . + '--enable-static=yes ' . + "CFLAGS='{$this->arch_c_flags} -Werror=unknown-warning-option' " . + '--disable-all ' . + '--disable-cgi ' . + '--disable-phpdbg ' . + ($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() + ); + + SourcePatcher::patchBeforeMake($this); + + $this->cleanMake(); + + if ($enableCli) { + logger()->info('building cli'); + $this->buildCli(); + } + if ($enableFpm) { + logger()->info('building fpm'); + $this->buildFpm(); + } + 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); + } + } + + /** + * Build cli sapi + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function buildCli(): void + { + $vars = SystemUtil::makeEnvVarString([ + 'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size + 'EXTRA_LIBS' => "{$this->getOption('extra-libs')} /usr/lib/libm.a", + ]); + + $shell = shell()->cd(SOURCE_PATH . '/php-src'); + $shell->exec('sed -ie "s|//lib|/lib|g" Makefile'); + $shell->exec("make -j{$this->concurrency} {$vars} cli"); + if (!$this->getOption('no-strip', false)) { + $shell->exec('strip sapi/cli/php'); + } + $this->deployBinary(BUILD_TARGET_CLI); + } + + /** + * Build phpmicro sapi + * + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + public function buildMicro(): void + { + if ($this->getPHPVersionID() < 80000) { + throw new WrongUsageException('phpmicro only support PHP >= 8.0!'); + } + if ($this->getExt('phar')) { + $this->phar_patched = true; + SourcePatcher::patchMicro(['phar']); + } + + $enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : ''; + $vars = [ + // with debug information, optimize for size, remove identifiers, patch fake cli for micro + 'EXTRA_CFLAGS' => '-g -Os' . $enable_fake_cli, + // link resolv library (macOS needs it) + 'EXTRA_LIBS' => "{$this->getOption('extra-libs')} /usr/lib/libm.a", + ]; + $vars = SystemUtil::makeEnvVarString($vars); + + shell()->cd(SOURCE_PATH . '/php-src') + ->exec("make -j{$this->concurrency} {$vars} micro"); + + if (!$this->getOption('no-strip', false)) { + shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-all micro.sfx'); + } + $this->deployBinary(BUILD_TARGET_MICRO); + + if ($this->phar_patched) { + SourcePatcher::patchMicro(['phar'], true); + } + } + + /** + * Build fpm sapi + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function buildFpm(): void + { + $vars = SystemUtil::makeEnvVarString([ + 'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size + 'EXTRA_LIBS' => "{$this->getOption('extra-libs')} /usr/lib/libm.a", // link resolv library (macOS needs it) + ]); + + $shell = shell()->cd(SOURCE_PATH . '/php-src'); + $shell->exec("make -j{$this->concurrency} {$vars} fpm"); + if (!$this->getOption('no-strip', false)) { + $shell->exec('strip sapi/fpm/php-fpm'); + } + $this->deployBinary(BUILD_TARGET_FPM); + } + + /** + * Build embed sapi + * + * @throws RuntimeException + */ + public function buildEmbed(): void + { + $vars = SystemUtil::makeEnvVarString([ + 'EXTRA_CFLAGS' => '-g -Os', // with debug information, but optimize for size + 'EXTRA_LIBS' => "{$this->getOption('extra-libs')} /usr/lib/libm.a", // 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'); + } +} diff --git a/src/SPC/builder/freebsd/SystemUtil.php b/src/SPC/builder/freebsd/SystemUtil.php new file mode 100644 index 00000000..13aaafcc --- /dev/null +++ b/src/SPC/builder/freebsd/SystemUtil.php @@ -0,0 +1,46 @@ +execWithResult('sysctl -n hw.ncpu'); + if ($ret !== 0) { + throw new RuntimeException('Failed to get cpu count'); + } + + return (int) $output[0]; + } + + /** + * Get Target Arch CFlags + * + * @param string $arch Arch Name + * @return string return Arch CFlags string + * @throws WrongUsageException + */ + public static function getArchCFlags(string $arch): string + { + return match ($arch) { + 'amd64', 'x86_64' => '--target=x86_64-unknown-freebsd', + 'arm64','aarch64' => '--target=aarch-unknown-freebsd', + default => throw new WrongUsageException('unsupported arch: ' . $arch), + }; + } +} diff --git a/src/SPC/builder/freebsd/library/BSDLibraryBase.php b/src/SPC/builder/freebsd/library/BSDLibraryBase.php new file mode 100644 index 00000000..6267fbbe --- /dev/null +++ b/src/SPC/builder/freebsd/library/BSDLibraryBase.php @@ -0,0 +1,27 @@ +builder; + } +} diff --git a/src/SPC/builder/freebsd/library/bzip2.php b/src/SPC/builder/freebsd/library/bzip2.php new file mode 100644 index 00000000..8695adac --- /dev/null +++ b/src/SPC/builder/freebsd/library/bzip2.php @@ -0,0 +1,12 @@ +builder->getLib('openssl')) { + $this->builder->setOption('extra-libs', $this->builder->getOption('extra-libs') . ' /usr/lib/libpthread.a /usr/lib/libdl.a'); + } + return $libs; + } +} diff --git a/src/SPC/builder/freebsd/library/onig.php b/src/SPC/builder/freebsd/library/onig.php new file mode 100644 index 00000000..0ad9731a --- /dev/null +++ b/src/SPC/builder/freebsd/library/onig.php @@ -0,0 +1,12 @@ + + * + * lwmbs is licensed under Mulan PSL v2. You can use this + * software according to the terms and conditions of the + * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: + * + * http://license.coscl.org.cn/MulanPSL2 + * + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, + * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * + * See the Mulan PSL v2 for more details. + */ + +declare(strict_types=1); + +namespace SPC\builder\freebsd\library; + +use SPC\builder\macos\library\MacOSLibraryBase; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; + +class openssl extends BSDLibraryBase +{ + public const NAME = 'openssl'; + + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + protected function build(): void + { + [$lib,,$destdir] = SEPARATED_PATH; + + // lib:zlib + $extra = ''; + $ex_lib = ''; + $zlib = $this->builder->getLib('zlib'); + if ($zlib instanceof MacOSLibraryBase) { + $extra = 'zlib'; + $ex_lib = trim($zlib->getStaticLibFiles() . ' ' . $ex_lib); + } + + shell()->cd($this->source_dir) + ->exec( + "./Configure no-shared {$extra} " . + '--prefix=/ ' . // use prefix=/ + "--libdir={$lib} " . + '--openssldir=/etc/ssl ' . + 'BSD-' . arch2gnu($this->builder->getOption('arch')) + ) + ->exec('make clean') + ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") + ->exec("make install_sw DESTDIR={$destdir}"); + $this->patchPkgconfPrefix(['libssl.pc', 'openssl.pc', 'libcrypto.pc']); + } +} diff --git a/src/SPC/builder/freebsd/library/pkgconfig.php b/src/SPC/builder/freebsd/library/pkgconfig.php new file mode 100644 index 00000000..62bd8ace --- /dev/null +++ b/src/SPC/builder/freebsd/library/pkgconfig.php @@ -0,0 +1,15 @@ +cc = $cc ?? match (SystemUtil::getOSRelease()['dist']) { - 'alpine' => 'gcc', - default => 'musl-gcc' - }; - $this->cxx = $cxx ?? 'g++'; - $this->arch = $arch ?? php_uname('m'); - $this->gnu_arch = arch2gnu($this->arch); - $this->zts = $zts; - $this->libc = 'musl'; // SystemUtil::selectLibc($this->cc); + $this->options = $options; - // 根据 CPU 线程数设置编译进程数 + // ---------- set necessary options ---------- + // set C/C++ compilers (default: alpine: gcc, others: musl-cross-make) + if (SystemUtil::isMuslDist()) { + f_putenv("CC={$this->getOption('cc', 'gcc')}"); + f_putenv("CXX={$this->getOption('cxx', 'g++')}"); + f_putenv("AR={$this->getOption('ar', 'ar')}"); + f_putenv("LD={$this->getOption('ld', 'ld.gold')}"); + } else { + $arch = arch2gnu(php_uname('m')); + f_putenv("CC={$this->getOption('cc', "{$arch}-linux-musl-gcc")}"); + f_putenv("CXX={$this->getOption('cxx', "{$arch}-linux-musl-g++")}"); + f_putenv("AR={$this->getOption('ar', "{$arch}-linux-musl-ar")}"); + f_putenv("LD={$this->getOption('ld', 'ld.gold')}"); + f_putenv("PATH=/usr/local/musl/bin:/usr/local/musl/{$arch}-linux-musl/bin:" . BUILD_ROOT_PATH . '/bin:' . getenv('PATH')); + + // set library path, some libraries need it. (We cannot use `putenv` here, because cmake will be confused) + $this->setOptionIfNotExist('library_path', "LIBRARY_PATH=/usr/local/musl/{$arch}-linux-musl/lib"); + $this->setOptionIfNotExist('ld_library_path', "LD_LIBRARY_PATH=/usr/local/musl/{$arch}-linux-musl/lib"); + + // check musl-cross make installed if we use musl-cross-make + if (str_ends_with(getenv('CC'), 'linux-musl-gcc') && !file_exists("/usr/local/musl/bin/{$arch}-linux-musl-gcc")) { + throw new WrongUsageException('musl-cross-make not installed, please install it first. (You can use `doctor` command to install it)'); + } + } + + // set PKG_CONFIG + f_putenv('PKG_CONFIG=' . BUILD_ROOT_PATH . '/bin/pkg-config'); + // set PKG_CONFIG_PATH + f_putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig'); + + // set arch (default: current) + $this->setOptionIfNotExist('arch', php_uname('m')); + $this->setOptionIfNotExist('gnu-arch', arch2gnu($this->getOption('arch'))); + + // concurrency $this->concurrency = SystemUtil::getCpuCount(); - // 设置 cflags - $this->arch_c_flags = SystemUtil::getArchCFlags($this->cc, $this->arch); - $this->arch_cxx_flags = SystemUtil::getArchCFlags($this->cxx, $this->arch); - $this->tune_c_flags = SystemUtil::checkCCFlags(SystemUtil::getTuneCFlags($this->arch), $this->cc); - // 设置 cmake + // cflags + $this->arch_c_flags = SystemUtil::getArchCFlags(getenv('CC'), $this->getOption('arch')); + $this->arch_cxx_flags = SystemUtil::getArchCFlags(getenv('CXX'), $this->getOption('arch')); + $this->tune_c_flags = SystemUtil::checkCCFlags(SystemUtil::getTuneCFlags($this->getOption('arch')), getenv('CC')); + // cmake toolchain $this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile( - os: 'Linux', - target_arch: $this->arch, - cflags: $this->arch_c_flags, - cc: $this->cc, - cxx: $this->cxx + 'Linux', + $this->getOption('arch'), + $this->arch_c_flags, + getenv('CC'), + getenv('CXX'), ); - // 设置 pkgconfig - $this->pkgconf_env = 'PKG_CONFIG="' . BUILD_ROOT_PATH . '/bin/pkg-config" PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig"'; - // 设置 configure 依赖的环境变量 - $this->configure_env = - $this->pkgconf_env . ' ' . - "CC='{$this->cc}' " . - "CXX='{$this->cxx}' " . - (php_uname('m') === $this->arch ? '' : "CFLAGS='{$this->arch_c_flags}'"); - // 交叉编译依赖的,TODO - if (php_uname('m') !== $this->arch) { + + // cross-compiling is not supported yet + /*if (php_uname('m') !== $this->arch) { $this->cross_compile_prefix = SystemUtil::getCrossCompilePrefix($this->cc, $this->arch); logger()->info('using cross compile prefix: ' . $this->cross_compile_prefix); $this->configure_env .= " CROSS_COMPILE='{$this->cross_compile_prefix}'"; - } + }*/ - $missing = []; - foreach (self::REQUIRED_COMMANDS as $cmd) { - if (SystemUtil::findCommand($cmd) === null) { - $missing[] = $cmd; - } - } - if (!empty($missing)) { - throw new WrongUsageException('missing system commands: ' . implode(', ', $missing)); - } - - // 创立 pkg-config 和放头文件的目录 + // create pkgconfig and include dir (some libs cannot create them automatically) f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true); f_mkdir(BUILD_INCLUDE_PATH, recursive: true); } + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ public function makeAutoconfArgs(string $name, array $libSpecs): string { $ret = ''; @@ -124,64 +123,56 @@ class LinuxBuilder extends BuilderBase /** * @throws RuntimeException * @throws FileSystemException + * @throws WrongUsageException */ - public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $with_clean = false, bool $bloat = false) + public function buildPHP(int $build_target = BUILD_TARGET_NONE): void { - if (!$bloat) { - $extra_libs = implode(' ', $this->getAllStaticLibFiles()); + // ---------- Update extra-libs ---------- + $extra_libs = $this->getOption('extra-libs', ''); + // non-bloat linking + if (!$this->getOption('bloat', false)) { + $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles()); } else { - logger()->info('bloat linking'); - $extra_libs = implode( - ' ', - array_map( - fn ($x) => "-Xcompiler {$x}", - array_filter($this->getAllStaticLibFiles()) - ) - ); - } - if ($this->getExt('swoole') || $this->getExt('intl')) { - $extra_libs .= ' -lstdc++'; - } - if ($this->getExt('imagick')) { - $extra_libs .= ' /usr/lib/libMagick++-7.Q16HDRI.a /usr/lib/libMagickCore-7.Q16HDRI.a /usr/lib/libMagickWand-7.Q16HDRI.a'; + $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", array_filter($this->getAllStaticLibFiles()))); } + // add libstdc++, some extensions or libraries need it + $extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lstdc++ ' : ''); + $this->setOption('extra-libs', $extra_libs); - $envs = $this->pkgconf_env . ' ' . - "CC='{$this->cc}' " . - "CXX='{$this->cxx}' "; $cflags = $this->arch_c_flags; - $use_lld = ''; - switch ($this->libc) { - case 'musl_wrapper': - case 'glibc': - $cflags .= ' -static-libgcc -I"' . BUILD_INCLUDE_PATH . '"'; - break; - case 'musl': - if (str_ends_with($this->cc, 'clang') && SystemUtil::findCommand('lld')) { - $use_lld = '-Xcompiler -fuse-ld=lld'; - } - break; - default: - throw new WrongUsageException('libc ' . $this->libc . ' is not implemented yet'); - } + // prepare build php envs + $envs_build_php = SystemUtil::makeEnvVarString([ + 'CFLAGS' => $cflags, + 'LIBS' => '-ldl -lpthread', + ]); - $envs = "{$envs} CFLAGS='{$cflags}' LIBS='-ldl -lpthread'"; - - SourcePatcher::patchPHPBuildconf($this); + SourcePatcher::patchBeforeBuildconf($this); shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force'); - SourcePatcher::patchPHPConfigure($this); + SourcePatcher::patchBeforeConfigure($this); - if ($this->getPHPVersionID() < 80000) { - $json_74 = '--enable-json '; + $phpVersionID = $this->getPHPVersionID(); + $json_74 = $phpVersionID < 80000 ? '--enable-json ' : ''; + + if ($this->getOption('enable-zts', false)) { + $maxExecutionTimers = $phpVersionID >= 80100 ? '--enable-zend-max-execution-timers ' : ''; + $zts = '--enable-zts --disable-zend-signals '; } else { - $json_74 = ''; + $maxExecutionTimers = ''; + $zts = ''; } + $disable_jit = $this->getOption('disable-opcache-jit', false) ? '--disable-opcache-jit ' : ''; + + $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( + "{$this->getOption('ld_library_path')} " . './configure ' . '--prefix= ' . '--with-valgrind=no ' . @@ -190,43 +181,96 @@ 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-embed ') . + ($enableMicro ? '--enable-micro=all-static ' : '--disable-micro ') . + $disable_jit . $json_74 . - '--enable-micro=all-static ' . - ($this->zts ? '--enable-zts' : '') . ' ' . + $zts . + $maxExecutionTimers . $this->makeExtensionArgs() . ' ' . - $envs + $envs_build_php ); - SourcePatcher::patchPHPAfterConfigure($this); + SourcePatcher::patchBeforeMake($this); - file_put_contents('/tmp/comment', $this->note_section); - - // 清理 $this->cleanMake(); - if ($bloat) { - logger()->info('bloat linking'); - $extra_libs = "-Wl,--whole-archive {$extra_libs} -Wl,--no-whole-archive"; - } - - if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { + if ($enableCli) { logger()->info('building cli'); - $this->buildCli($extra_libs, $use_lld); + $this->buildCli(); } - if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) { + if ($enableFpm) { logger()->info('building fpm'); - $this->buildFpm($extra_libs, $use_lld); + $this->buildFpm(); } - if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) { + if ($enableMicro) { logger()->info('building micro'); - $this->buildMicro($extra_libs, $use_lld, $cflags); + $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->arch) { + if (php_uname('m') === $this->getOption('arch')) { $this->sanityCheck($build_target); } + } + + /** + * Build cli sapi + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function buildCli(): void + { + $vars = SystemUtil::makeEnvVarString($this->getBuildVars()); + shell()->cd(SOURCE_PATH . '/php-src') + ->exec('sed -i "s|//lib|/lib|g" Makefile') + ->exec("make -j{$this->concurrency} {$vars} cli"); + + if (!$this->getOption('no-strip', false)) { + shell()->cd(SOURCE_PATH . '/php-src/sapi/cli')->exec('strip --strip-all php'); + } + + $this->deployBinary(BUILD_TARGET_CLI); + } + + /** + * Build phpmicro sapi + * + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + public function buildMicro(): void + { + if ($this->getPHPVersionID() < 80000) { + throw new WrongUsageException('phpmicro only support PHP >= 8.0!'); + } + if ($this->getExt('phar')) { + $this->phar_patched = true; + SourcePatcher::patchMicro(['phar']); + } + + $vars = SystemUtil::makeEnvVarString($this->getBuildVars([ + 'EXTRA_CFLAGS' => $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : '', + ])); + shell()->cd(SOURCE_PATH . '/php-src') + ->exec('sed -i "s|//lib|/lib|g" Makefile') + ->exec("make -j{$this->concurrency} {$vars} micro"); + + if (!$this->getOption('no-strip', false)) { + shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec('strip --strip-all micro.sfx'); + } + + $this->deployBinary(BUILD_TARGET_MICRO); if ($this->phar_patched) { SourcePatcher::patchMicro(['phar'], true); @@ -234,78 +278,54 @@ class LinuxBuilder extends BuilderBase } /** - * @throws RuntimeException - */ - public function buildCli(string $extra_libs, string $use_lld): void - { - shell()->cd(SOURCE_PATH . '/php-src') - ->exec('sed -i "s|//lib|/lib|g" Makefile') - ->exec( - 'make -j' . $this->concurrency . - ' 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' " . - 'cli' - ); - - shell()->cd(SOURCE_PATH . '/php-src/sapi/cli') - ->exec("{$this->cross_compile_prefix}objcopy --only-keep-debug php php.debug") - ->exec('elfedit --output-osabi linux php') - ->exec("{$this->cross_compile_prefix}strip --strip-all php") - ->exec("{$this->cross_compile_prefix}objcopy --update-section .comment=/tmp/comment --add-gnu-debuglink=php.debug --remove-section=.note php"); - $this->deployBinary(BUILD_TARGET_CLI); - } - - /** - * @throws RuntimeException - */ - public function buildMicro(string $extra_libs, string $use_lld, string $cflags): void - { - if ($this->getPHPVersionID() < 80000) { - throw new RuntimeException('phpmicro only support PHP >= 8.0!'); - } - if ($this->getExt('phar')) { - $this->phar_patched = true; - SourcePatcher::patchMicro(['phar']); - } - - shell()->cd(SOURCE_PATH . '/php-src') - ->exec('sed -i "s|//lib|/lib|g" Makefile') - ->exec( - "make -j{$this->concurrency} " . - 'EXTRA_CFLAGS=' . quote('-g -Os -fno-ident ' . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags))) . ' ' . - 'EXTRA_LIBS=' . quote($extra_libs) . ' ' . - 'EXTRA_LDFLAGS_PROGRAM=' . quote("{$cflags} {$use_lld}" . ' -all-static', "'") . ' ' . - 'micro' - ); - - shell()->cd(SOURCE_PATH . '/php-src/sapi/micro')->exec("{$this->cross_compile_prefix}strip --strip-all micro.sfx"); - - $this->deployBinary(BUILD_TARGET_MICRO); - } - - /** - * 构建 fpm + * Build fpm sapi * - * @throws FileSystemException|RuntimeException + * @throws FileSystemException + * @throws RuntimeException */ - public function buildFpm(string $extra_libs, string $use_lld): void + public function buildFpm(): void { + $vars = SystemUtil::makeEnvVarString($this->getBuildVars()); shell()->cd(SOURCE_PATH . '/php-src') ->exec('sed -i "s|//lib|/lib|g" Makefile') - ->exec( - 'make -j' . $this->concurrency . - ' 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' " . - 'fpm' - ); + ->exec("make -j{$this->concurrency} {$vars} fpm"); + + if (!$this->getOption('no-strip', false)) { + shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm')->exec('strip --strip-all php-fpm'); + } - shell()->cd(SOURCE_PATH . '/php-src/sapi/fpm') - ->exec("{$this->cross_compile_prefix}objcopy --only-keep-debug php-fpm php-fpm.debug") - ->exec('elfedit --output-osabi linux php-fpm') - ->exec("{$this->cross_compile_prefix}strip --strip-all php-fpm") - ->exec("{$this->cross_compile_prefix}objcopy --update-section .comment=/tmp/comment --add-gnu-debuglink=php-fpm.debug --remove-section=.note php-fpm"); $this->deployBinary(BUILD_TARGET_FPM); } + + /** + * Build embed sapi + * + * @throws RuntimeException + */ + public function buildEmbed(): void + { + $vars = SystemUtil::makeEnvVarString($this->getBuildVars()); + + 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"); + } + + private function getBuildVars($input = []): array + { + $use_lld = ''; + if (str_ends_with(getenv('CC'), 'clang') && SystemUtil::findCommand('lld')) { + $use_lld = '-Xcompiler -fuse-ld=lld'; + } + $optimization = $this->getOption('no-strip', false) ? '-g -O0' : '-g0 -Os'; + $cflags = isset($input['EXTRA_CFLAGS']) && $input['EXTRA_CFLAGS'] ? " {$input['EXTRA_CFLAGS']}" : ''; + $libs = isset($input['EXTRA_LIBS']) && $input['EXTRA_LIBS'] ? " {$input['EXTRA_LIBS']}" : ''; + $ldflags = isset($input['EXTRA_LDFLAGS_PROGRAM']) && $input['EXTRA_LDFLAGS_PROGRAM'] ? " {$input['EXTRA_LDFLAGS_PROGRAM']}" : ''; + return [ + 'EXTRA_CFLAGS' => "{$optimization} -fno-ident -fPIE " . implode(' ', array_map(fn ($x) => "-Xcompiler {$x}", $this->tune_c_flags)) . $cflags, + 'EXTRA_LIBS' => $this->getOption('extra-libs', '') . $libs, + 'EXTRA_LDFLAGS_PROGRAM' => "{$use_lld} -all-static" . $ldflags, + ]; + } } diff --git a/src/SPC/builder/linux/SystemUtil.php b/src/SPC/builder/linux/SystemUtil.php index 5f2df6a3..34b9cdef 100644 --- a/src/SPC/builder/linux/SystemUtil.php +++ b/src/SPC/builder/linux/SystemUtil.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace SPC\builder\linux; -use JetBrains\PhpStorm\ArrayShape; use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; @@ -13,7 +12,7 @@ class SystemUtil { use UnixSystemUtilTrait; - #[ArrayShape(['dist' => 'mixed|string', 'ver' => 'mixed|string'])] + /** @noinspection PhpMissingBreakStatementInspection */ public static function getOSRelease(): array { $ret = [ @@ -21,6 +20,19 @@ class SystemUtil 'ver' => 'unknown', ]; switch (true) { + case file_exists('/etc/centos-release'): + $lines = file('/etc/centos-release'); + goto rh; + case file_exists('/etc/redhat-release'): + $lines = file('/etc/redhat-release'); + rh: + foreach ($lines as $line) { + if (preg_match('/release\s+(\d*(\.\d+)*)/', $line, $matches)) { + $ret['dist'] = 'redhat'; + $ret['ver'] = $matches[1]; + } + } + break; case file_exists('/etc/os-release'): $lines = file('/etc/os-release'); foreach ($lines as $line) { @@ -37,23 +49,15 @@ class SystemUtil $ret['dist'] = 'redhat'; } break; - case file_exists('/etc/centos-release'): - $lines = file('/etc/centos-release'); - goto rh; - case file_exists('/etc/redhat-release'): - $lines = file('/etc/redhat-release'); - rh: - foreach ($lines as $line) { - if (preg_match('/release\s+(\d+(\.\d+)*)/', $line, $matches)) { - $ret['dist'] = 'redhat'; - $ret['ver'] = $matches[1]; - } - } - break; } return $ret; } + public static function isMuslDist(): bool + { + return static::getOSRelease()['dist'] === 'alpine'; + } + public static function getCpuCount(): int { $ncpu = 1; @@ -81,6 +85,8 @@ class SystemUtil /** * @throws RuntimeException + * @throws WrongUsageException + * @throws WrongUsageException */ public static function getArchCFlags(string $cc, string $arch): string { @@ -120,7 +126,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 ''; } @@ -129,6 +135,7 @@ class SystemUtil /** * @throws RuntimeException + * @noinspection PhpUnused */ public static function getCrossCompilePrefix(string $cc, string $arch): string { @@ -159,6 +166,7 @@ class SystemUtil return null; } + /** @noinspection PhpUnused */ public static function findStaticLibs(array $names): ?array { $ret = []; @@ -187,6 +195,7 @@ class SystemUtil return null; } + /** @noinspection PhpUnused */ public static function findHeaders(array $names): ?array { $ret = []; diff --git a/src/SPC/builder/linux/library/LinuxLibraryBase.php b/src/SPC/builder/linux/library/LinuxLibraryBase.php index 2712976b..2a496590 100644 --- a/src/SPC/builder/linux/library/LinuxLibraryBase.php +++ b/src/SPC/builder/linux/library/LinuxLibraryBase.php @@ -8,7 +8,6 @@ use SPC\builder\BuilderBase; use SPC\builder\LibraryBase; use SPC\builder\linux\LinuxBuilder; use SPC\builder\traits\UnixLibraryTrait; -use SPC\exception\RuntimeException; abstract class LinuxLibraryBase extends LibraryBase { @@ -34,50 +33,4 @@ abstract class LinuxLibraryBase extends LibraryBase { return $this->builder; } - - /** - * @throws RuntimeException - */ - public function tryBuild(bool $force_build = false): int - { - // 传入 true,表明直接编译 - if ($force_build) { - logger()->info('Building required library [' . static::NAME . ']'); - $this->build(); - return BUILD_STATUS_OK; - } - - // 看看这些库是不是存在,如果不存在,则调用编译并返回结果状态 - foreach ($this->getStaticLibs() as $name) { - if (!file_exists(BUILD_LIB_PATH . "/{$name}")) { - $this->tryBuild(true); - return BUILD_STATUS_OK; - } - } - // 头文件同理 - foreach ($this->getHeaders() as $name) { - if (!file_exists(BUILD_INCLUDE_PATH . "/{$name}")) { - $this->tryBuild(true); - return BUILD_STATUS_OK; - } - } - // pkg-config 做特殊处理,如果是 pkg-config 就检查有没有 pkg-config 二进制 - if (static::NAME === 'pkg-config' && !file_exists(BUILD_ROOT_PATH . '/bin/pkg-config')) { - $this->tryBuild(true); - return BUILD_STATUS_OK; - } - // 到这里说明所有的文件都存在,就跳过编译 - return BUILD_STATUS_ALREADY; - } - - protected function makeFakePkgconfs() - { - $workspace = BUILD_ROOT_PATH; - if ($workspace === '/') { - $workspace = ''; - } - foreach ($this->pkgconfs as $name => $content) { - file_put_contents(BUILD_LIB_PATH . "/pkgconfig/{$name}", "prefix={$workspace}\n" . $content); - } - } } diff --git a/src/SPC/builder/linux/library/icu.php b/src/SPC/builder/linux/library/icu.php index c6ef6c37..c2d579f5 100644 --- a/src/SPC/builder/linux/library/icu.php +++ b/src/SPC/builder/linux/library/icu.php @@ -8,13 +8,15 @@ class icu extends LinuxLibraryBase { public const NAME = 'icu'; - protected function build() + protected function build(): void { - $root = BUILD_ROOT_PATH; - $cppflag = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1"'; + $cppflags = 'CPPFLAGS="-DU_CHARSET_IS_UTF8=1 -DU_USING_ICU_NAMESPACE=1 -DU_STATIC_IMPLEMENTATION=1"'; + $cxxflags = 'CXXFLAGS="-std=c++11"'; + $ldflags = 'LDFLAGS="-static"'; shell()->cd($this->source_dir . '/source') ->exec( - "{$this->builder->configure_env} {$cppflag} ./runConfigureICU Linux " . + "{$cppflags} {$cxxflags} {$ldflags} " . + './runConfigureICU Linux ' . '--enable-static ' . '--disable-shared ' . '--with-data-packaging=static ' . @@ -25,7 +27,7 @@ class icu extends LinuxLibraryBase '--enable-tools=yes ' . '--enable-tests=no ' . '--enable-samples=no ' . - "--prefix={$root}" + '--prefix=' . BUILD_ROOT_PATH ) ->exec('make clean') ->exec("make -j{$this->builder->concurrency}") diff --git a/src/SPC/builder/linux/library/ldap.php b/src/SPC/builder/linux/library/ldap.php new file mode 100644 index 00000000..2d3f591b --- /dev/null +++ b/src/SPC/builder/linux/library/ldap.php @@ -0,0 +1,12 @@ +builder->pkgconf_env . ' CFLAGS="' . $this->builder->arch_c_flags . '"'; + + $env .= match ($this->builder->libc) { + 'musl_wrapper' => " CC='{$this->builder->getOption('cc')} --static -idirafter " . BUILD_INCLUDE_PATH . + ($this->builder->getOption('arch') === php_uname('m') ? '-idirafter /usr/include/ ' : '') . + "-idirafter /usr/include/{$this->builder->getOption('arch')}-linux-gnu/'", + 'musl', 'glibc' => " CC='{$this->builder->getOption('cc')}'", + default => throw new RuntimeException('unsupported libc: ' . $this->builder->libc), + };*/ + + shell()->cd($this->source_dir) + ->exec( + './configure ' . + '--enable-static ' . + '--disable-shared ' . + "--host={$this->builder->getOption('arch')}-unknown-linux " . + "--target={$this->builder->getOption('arch')}-unknown-linux " . + '--prefix= ' . // use prefix=/ + "--libdir={$lib}" + ) + ->exec('make clean') + ->exec("make -j{$this->builder->concurrency}") + ->exec("make install DESTDIR={$destdir}"); + + if (is_file(BUILD_ROOT_PATH . '/lib64/libffi.a')) { + copy(BUILD_ROOT_PATH . '/lib64/libffi.a', BUILD_ROOT_PATH . '/lib/libffi.a'); + unlink(BUILD_ROOT_PATH . '/lib64/libffi.a'); + } + $this->patchPkgconfPrefix(['libffi.pc']); + } +} diff --git a/src/SPC/builder/linux/library/libmemcached.php b/src/SPC/builder/linux/library/libmemcached.php new file mode 100644 index 00000000..ab393d2e --- /dev/null +++ b/src/SPC/builder/linux/library/libmemcached.php @@ -0,0 +1,20 @@ +builder->arch) { + $optimizations = match ($this->builder->getOption('arch')) { 'x86_64' => '--enable-intel-sse ', 'arm64' => '--enable-arm-neon ', default => '', }; - - // patch configure - SourcePatcher::patchUnixLibpng(); - shell()->cd($this->source_dir) ->exec('chmod +x ./configure') + ->exec('chmod +x ./install-sh') ->exec( - "{$this->builder->configure_env} ./configure " . - "--host={$this->builder->gnu_arch}-unknown-linux " . + 'LDFLAGS="-L' . BUILD_LIB_PATH . '" ' . + './configure ' . '--disable-shared ' . '--enable-static ' . '--enable-hardware-optimizations ' . @@ -56,10 +54,8 @@ class libpng extends LinuxLibraryBase '--prefix=' ) ->exec('make clean') - ->exec("make -j{$this->builder->concurrency} DEFAULT_INCLUDES='-I. -I" . BUILD_INCLUDE_PATH . "' LIBS= libpng16.la") - ->exec('make install-libLTLIBRARIES install-data-am DESTDIR=' . BUILD_ROOT_PATH) - ->cd(BUILD_LIB_PATH) - ->exec('ln -sf libpng16.a libpng.a'); + ->exec("make -j{$this->builder->concurrency} DEFAULT_INCLUDES='-I{$this->source_dir} -I" . BUILD_INCLUDE_PATH . "' LIBS= libpng16.la") + ->exec('make install-libLTLIBRARIES install-data-am DESTDIR=' . BUILD_ROOT_PATH); $this->patchPkgconfPrefix(['libpng16.pc'], PKGCONF_PATCH_PREFIX); $this->cleanLaFiles(); } diff --git a/src/SPC/builder/linux/library/libxml2.php b/src/SPC/builder/linux/library/libxml2.php index 0389b3ea..82327c58 100644 --- a/src/SPC/builder/linux/library/libxml2.php +++ b/src/SPC/builder/linux/library/libxml2.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SPC\builder\linux\library; +use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; @@ -13,23 +14,22 @@ class libxml2 extends LinuxLibraryBase /** * @throws RuntimeException + * @throws FileSystemException */ - public function build() + public function build(): void { $enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF'; $enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF'; $enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF'; - [$lib, $include, $destdir] = SEPARATED_PATH; - FileSystem::resetDir($this->source_dir . '/build'); shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} " . ' cmake ' . + 'cmake ' . "{$this->builder->makeCmakeArgs()} " . '-DBUILD_SHARED_LIBS=OFF ' . - '-DLIBXML2_WITH_ICONV=ON ' . '-DIconv_IS_BUILT_IN=OFF ' . + '-DLIBXML2_WITH_ICONV=ON ' . "-DLIBXML2_WITH_ZLIB={$enable_zlib} " . "-DLIBXML2_WITH_ICU={$enable_icu} " . "-DLIBXML2_WITH_LZMA={$enable_xz} " . @@ -39,15 +39,12 @@ class libxml2 extends LinuxLibraryBase '..' ) ->exec("cmake --build . -j {$this->builder->concurrency}") - ->exec("make install DESTDIR={$destdir}"); + ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); - if (is_dir(BUILD_INCLUDE_PATH . '/libxml2/libxml')) { - if (is_dir(BUILD_INCLUDE_PATH . '/libxml')) { - shell()->exec('rm -rf "' . BUILD_INCLUDE_PATH . '/libxml"'); - } - $path = FileSystem::convertPath(BUILD_INCLUDE_PATH . '/libxml2/libxml'); - $dst_path = FileSystem::convertPath(BUILD_INCLUDE_PATH . '/'); - shell()->exec('mv "' . $path . '" "' . $dst_path . '"'); - } + FileSystem::replaceFileStr( + BUILD_LIB_PATH . '/pkgconfig/libxml-2.0.pc', + '-licudata -licui18n -licuuc', + '-licui18n -licuuc -licudata' + ); } } diff --git a/src/SPC/builder/linux/library/libxslt.php b/src/SPC/builder/linux/library/libxslt.php new file mode 100644 index 00000000..4bc4a27e --- /dev/null +++ b/src/SPC/builder/linux/library/libxslt.php @@ -0,0 +1,15 @@ +builder->makeAutoconfArgs(static::NAME, [ 'zlib' => null, @@ -46,10 +55,10 @@ class nghttp2 extends LinuxLibraryBase shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static ' . '--disable-shared ' . - "--host={$this->builder->gnu_arch}-unknown-linux " . + "--host={$this->builder->getOption('gnu-arch')}-unknown-linux " . '--enable-lib-only ' . '--with-boost=no ' . $args . ' ' . diff --git a/src/SPC/builder/linux/library/openssl.php b/src/SPC/builder/linux/library/openssl.php index d5cfa7cb..5a9c5bfd 100644 --- a/src/SPC/builder/linux/library/openssl.php +++ b/src/SPC/builder/linux/library/openssl.php @@ -23,26 +23,28 @@ namespace SPC\builder\linux\library; use SPC\builder\linux\SystemUtil; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; class openssl extends LinuxLibraryBase { public const NAME = 'openssl'; /** - * @throws RuntimeException * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException */ - public function build() + public function build(): void { - [$lib,$include,$destdir] = SEPARATED_PATH; + [,,$destdir] = SEPARATED_PATH; $extra = ''; $ex_lib = '-ldl -pthread'; - $env = $this->builder->pkgconf_env . " CFLAGS='{$this->builder->arch_c_flags}'"; - $env .= " CC='{$this->builder->cc} -static -idirafter " . BUILD_INCLUDE_PATH . + $env = "CFLAGS='{$this->builder->arch_c_flags}'"; + $env .= " CC='" . getenv('CC') . ' -static -idirafter ' . BUILD_INCLUDE_PATH . ' -idirafter /usr/include/ ' . - ' -idirafter /usr/include/' . $this->builder->arch . '-linux-gnu/ ' . + ' -idirafter /usr/include/' . $this->builder->getOption('arch') . '-linux-gnu/ ' . "' "; // lib:zlib $zlib = $this->builder->getLib('zlib'); @@ -58,17 +60,17 @@ class openssl extends LinuxLibraryBase $ex_lib = trim($ex_lib); - $clang_postfix = SystemUtil::getCCType($this->builder->cc) === 'clang' ? '-clang' : ''; + $clang_postfix = SystemUtil::getCCType(getenv('CC')) === 'clang' ? '-clang' : ''; shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} {$env} ./Configure no-shared {$extra} " . + "{$env} ./Configure no-shared {$extra} " . '--prefix=/ ' . '--libdir=lib ' . '-static ' . "{$zlib_extra}" . 'no-legacy ' . - "linux-{$this->builder->arch}{$clang_postfix}" + "linux-{$this->builder->getOption('arch')}{$clang_postfix}" ) ->exec('make clean') ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") diff --git a/src/SPC/builder/linux/library/postgresql.php b/src/SPC/builder/linux/library/postgresql.php new file mode 100644 index 00000000..04fb76e7 --- /dev/null +++ b/src/SPC/builder/linux/library/postgresql.php @@ -0,0 +1,12 @@ +set_x = defined('DEBUG_MODE') ? 'set -x' : 'true'; - // 初始化一些默认参数 - $this->cc = $cc ?? 'clang'; - $this->cxx = $cxx ?? 'clang++'; - $this->arch = $arch ?? php_uname('m'); - $this->gnu_arch = arch2gnu($this->arch); - $this->zts = $zts; - // 根据 CPU 线程数设置编译进程数 - $this->concurrency = SystemUtil::getCpuCount(); - // 设置 cflags - $this->arch_c_flags = SystemUtil::getArchCFlags($this->arch); - $this->arch_cxx_flags = SystemUtil::getArchCFlags($this->arch); - // 设置 cmake - $this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', $this->arch, $this->arch_c_flags); - // 设置 configure 依赖的环境变量 - $this->configure_env = - 'PKG_CONFIG="' . BUILD_ROOT_PATH . '/bin/pkg-config" ' . - 'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' . - "CC='{$this->cc}' " . - "CXX='{$this->cxx}' " . - "CFLAGS='{$this->arch_c_flags} -Wimplicit-function-declaration'"; + $this->options = $options; - // 创立 pkg-config 和放头文件的目录 + // ---------- set necessary options ---------- + // set C Compiler (default: clang) + f_putenv('CC=' . $this->getOption('cc', 'clang')); + // set C++ Composer (default: clang++) + f_putenv('CXX=' . $this->getOption('cxx', 'clang++')); + // set PATH + f_putenv('PATH=' . BUILD_ROOT_PATH . '/bin:' . getenv('PATH')); + // set PKG_CONFIG + f_putenv('PKG_CONFIG=' . BUILD_ROOT_PATH . '/bin/pkg-config'); + // set PKG_CONFIG_PATH + f_putenv('PKG_CONFIG_PATH=' . BUILD_LIB_PATH . '/pkgconfig/'); + + // set arch (default: current) + $this->setOptionIfNotExist('arch', php_uname('m')); + $this->setOptionIfNotExist('gnu-arch', arch2gnu($this->getOption('arch'))); + + // ---------- set necessary compile environments ---------- + // concurrency + $this->concurrency = SystemUtil::getCpuCount(); + // cflags + $this->arch_c_flags = SystemUtil::getArchCFlags($this->getOption('arch')); + $this->arch_cxx_flags = SystemUtil::getArchCFlags($this->getOption('arch')); + // cmake toolchain + $this->cmake_toolchain_file = SystemUtil::makeCmakeToolchainFile('Darwin', $this->getOption('arch'), $this->arch_c_flags); + + // create pkgconfig and include dir (some libs cannot create them automatically) f_mkdir(BUILD_LIB_PATH . '/pkgconfig', recursive: true); f_mkdir(BUILD_INCLUDE_PATH, recursive: true); } /** - * 生成库构建采用的 autoconf 参数列表 + * [deprecated] 生成库构建采用的 autoconf 参数列表 * * @param string $name 要构建的 lib 库名,传入仅供输出日志 * @param array $lib_specs 依赖的 lib 库的 autoconf 文件 @@ -76,7 +75,6 @@ class MacOSBuilder extends BuilderBase $arr = $arr ?? []; $disableArgs = $arr[0] ?? null; - $prefix = $arr[1] ?? null; if ($lib instanceof MacOSLibraryBase) { logger()->info("{$name} \033[32;1mwith\033[0;1m {$libName} support"); $ret .= '--with-' . $libName . '=yes '; @@ -89,9 +87,11 @@ class MacOSBuilder extends BuilderBase } /** - * 返回 macOS 系统依赖的框架列表 + * Get dynamically linked macOS frameworks * - * @param bool $asString 是否以字符串形式返回(默认为 False) + * @param bool $asString If true, return as string + * @throws FileSystemException + * @throws WrongUsageException */ public function getFrameworks(bool $asString = false): array|string { @@ -118,82 +118,145 @@ class MacOSBuilder extends BuilderBase } /** - * @throws RuntimeException + * Just start to build statically linked php binary + * + * @param int $build_target build target * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException */ - public function buildPHP(int $build_target = BUILD_TARGET_NONE, bool $bloat = false): void + public function buildPHP(int $build_target = BUILD_TARGET_NONE): void { - $extra_libs = $this->getFrameworks(true) . ' ' . ($this->getExt('swoole') || $this->getExt('intl') ? '-lc++ ' : ''); - if (!$bloat) { - $extra_libs .= implode(' ', $this->getAllStaticLibFiles()); + // ---------- Update extra-libs ---------- + $extra_libs = $this->getOption('extra-libs', ''); + // add macOS frameworks + $extra_libs .= (empty($extra_libs) ? '' : ' ') . $this->getFrameworks(true); + // add libc++, some extensions or libraries need it (C++ cannot be linked statically) + $extra_libs .= (empty($extra_libs) ? '' : ' ') . ($this->hasCpp() ? '-lc++ ' : ''); + if (!$this->getOption('bloat', false)) { + $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', $this->getAllStaticLibFiles()); } else { logger()->info('bloat linking'); - $extra_libs .= implode( - ' ', - array_map( - fn ($x) => "-Wl,-force_load,{$x}", - array_filter($this->getAllStaticLibFiles()) - ) - ); + $extra_libs .= (empty($extra_libs) ? '' : ' ') . implode(' ', array_map(fn ($x) => "-Wl,-force_load,{$x}", array_filter($this->getAllStaticLibFiles()))); } + $this->setOption('extra-libs', $extra_libs); - // patch before configure - SourcePatcher::patchPHPBuildconf($this); + SourcePatcher::patchBeforeBuildconf($this); shell()->cd(SOURCE_PATH . '/php-src')->exec('./buildconf --force'); - SourcePatcher::patchPHPConfigure($this); + SourcePatcher::patchBeforeConfigure($this); - if ($this->getLib('libxml2') || $this->getExt('iconv')) { - $extra_libs .= ' -liconv'; - } + $json_74 = $this->getPHPVersionID() < 80000 ? '--enable-json ' : ''; + $zts = $this->getOption('enable-zts', false) ? '--enable-zts --disable-zend-signals ' : ''; - if ($this->getPHPVersionID() < 80000) { - $json_74 = '--enable-json '; - } else { - $json_74 = ''; - } + $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 ' . '--prefix= ' . - '--with-valgrind=no ' . // 不检测内存泄漏 + '--with-valgrind=no ' . // Not detect memory leak '--enable-shared=no ' . '--enable-static=yes ' . "CFLAGS='{$this->arch_c_flags} -Werror=unknown-warning-option' " . '--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-embed ') . + ($enableMicro ? '--enable-micro ' : '--disable-micro ') . $json_74 . - '--enable-micro ' . - ($this->zts ? '--enable-zts' : '') . ' ' . - $this->makeExtensionArgs() . ' ' . - $this->configure_env + $zts . + $this->makeExtensionArgs() ); - SourcePatcher::patchPHPAfterConfigure($this); + SourcePatcher::patchBeforeMake($this); $this->cleanMake(); - if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { + if ($enableCli) { logger()->info('building cli'); - $this->buildCli($extra_libs); + $this->buildCli(); } - if (($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) { + if ($enableFpm) { logger()->info('building fpm'); - $this->buildFpm($extra_libs); + $this->buildFpm(); } - if (($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO) { + if ($enableMicro) { logger()->info('building micro'); - $this->buildMicro($extra_libs); + $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->arch) { + if (php_uname('m') === $this->getOption('arch')) { $this->sanityCheck($build_target); } + } + + /** + * Build cli sapi + * + * @throws RuntimeException + * @throws FileSystemException + */ + public function buildCli(): 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 = shell()->cd(SOURCE_PATH . '/php-src'); + $shell->exec("make -j{$this->concurrency} {$vars} cli"); + if (!$this->getOption('no-strip', false)) { + $shell->exec('dsymutil -f sapi/cli/php')->exec('strip sapi/cli/php'); + } + $this->deployBinary(BUILD_TARGET_CLI); + } + + /** + * Build phpmicro sapi + * + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + public function buildMicro(): void + { + if ($this->getPHPVersionID() < 80000) { + throw new WrongUsageException('phpmicro only support PHP >= 8.0!'); + } + if ($this->getExt('phar')) { + $this->phar_patched = true; + SourcePatcher::patchMicro(['phar']); + } + + $enable_fake_cli = $this->getOption('with-micro-fake-cli', false) ? ' -DPHP_MICRO_FAKE_CLI' : ''; + $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 needs it) + 'EXTRA_LIBS' => "{$this->getOption('extra-libs')} -lresolv", + ]; + if (!$this->getOption('no-strip', false)) { + $vars['STRIP'] = 'dsymutil -f '; + } + $vars = SystemUtil::makeEnvVarString($vars); + + 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); @@ -201,54 +264,48 @@ class MacOSBuilder extends BuilderBase } /** - * 构建 cli + * Build fpm sapi * * @throws RuntimeException * @throws FileSystemException */ - public function buildCli(string $extra_libs): void + public function buildFpm(): 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 = shell()->cd(SOURCE_PATH . '/php-src'); - $shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" cli"); - if ($this->strip) { - $shell->exec('dsymutil -f sapi/cli/php')->exec('strip sapi/cli/php'); - } - $this->deployBinary(BUILD_TARGET_CLI); - } - - /** - * 构建 phpmicro - * - * @throws FileSystemException|RuntimeException - */ - public function buildMicro(string $extra_libs): void - { - if ($this->getPHPVersionID() < 80000) { - throw new RuntimeException('phpmicro only support PHP >= 8.0!'); - } - if ($this->getExt('phar')) { - $this->phar_patched = true; - SourcePatcher::patchMicro(['phar']); - } - - shell()->cd(SOURCE_PATH . '/php-src') - ->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" " . ($this->strip ? 'STRIP="dsymutil -f " ' : '') . 'micro'); - $this->deployBinary(BUILD_TARGET_MICRO); - } - - /** - * 构建 fpm - * - * @throws RuntimeException - * @throws FileSystemException - */ - public function buildFpm(string $extra_libs): void - { - $shell = shell()->cd(SOURCE_PATH . '/php-src'); - $shell->exec("make -j{$this->concurrency} EXTRA_CFLAGS=\"-g -Os -fno-ident\" EXTRA_LIBS=\"{$extra_libs} -lresolv\" fpm"); - if ($this->strip) { + $shell->exec("make -j{$this->concurrency} {$vars} fpm"); + if (!$this->getOption('no-strip', false)) { $shell->exec('dsymutil -f sapi/fpm/php-fpm')->exec('strip sapi/fpm/php-fpm'); } $this->deployBinary(BUILD_TARGET_FPM); } + + /** + * Build embed sapi + * + * @throws RuntimeException + */ + 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'); + } } diff --git a/src/SPC/builder/macos/SystemUtil.php b/src/SPC/builder/macos/SystemUtil.php index e810ae22..22d7847e 100644 --- a/src/SPC/builder/macos/SystemUtil.php +++ b/src/SPC/builder/macos/SystemUtil.php @@ -10,11 +10,11 @@ use SPC\exception\WrongUsageException; class SystemUtil { - /** macOS 兼容 unix 的系统工具 */ + /** Unix System Util Compatible */ use UnixSystemUtilTrait; /** - * 获取系统 CPU 逻辑内核数 + * Get Logic CPU Count for macOS * * @throws RuntimeException */ @@ -29,9 +29,10 @@ class SystemUtil } /** - * 获取不同架构对应的 cflags 参数 + * Get Target Arch CFlags * - * @param string $arch 架构名称 + * @param string $arch Arch Name + * @return string return Arch CFlags string * @throws WrongUsageException */ public static function getArchCFlags(string $arch): string diff --git a/src/SPC/builder/macos/library/MacOSLibraryBase.php b/src/SPC/builder/macos/library/MacOSLibraryBase.php index 3401e168..336a21b0 100644 --- a/src/SPC/builder/macos/library/MacOSLibraryBase.php +++ b/src/SPC/builder/macos/library/MacOSLibraryBase.php @@ -8,6 +8,8 @@ use SPC\builder\BuilderBase; use SPC\builder\LibraryBase; use SPC\builder\macos\MacOSBuilder; use SPC\builder\traits\UnixLibraryTrait; +use SPC\exception\FileSystemException; +use SPC\exception\WrongUsageException; use SPC\store\Config; abstract class MacOSLibraryBase extends LibraryBase @@ -27,7 +29,8 @@ abstract class MacOSLibraryBase extends LibraryBase } /** - * 获取当前 lib 库依赖的 macOS framework + * @throws WrongUsageException + * @throws FileSystemException */ public function getFrameworks(): array { diff --git a/src/SPC/builder/macos/library/curl.php b/src/SPC/builder/macos/library/curl.php index 5432aa87..3a24f31a 100644 --- a/src/SPC/builder/macos/library/curl.php +++ b/src/SPC/builder/macos/library/curl.php @@ -20,19 +20,35 @@ declare(strict_types=1); namespace SPC\builder\macos\library; -use SPC\store\SourcePatcher; +use SPC\exception\FileSystemException; +use SPC\store\FileSystem; class curl extends MacOSLibraryBase { - use \SPC\builder\unix\library\curl { - build as unixBuild; - } + use \SPC\builder\unix\library\curl; public const NAME = 'curl'; - protected function build() + /** + * @throws FileSystemException + */ + public function patchBeforeBuild(): bool { - SourcePatcher::patchCurlMacOS(); - $this->unixBuild(); + FileSystem::replaceFileRegex( + SOURCE_PATH . '/curl/CMakeLists.txt', + '/NOT COREFOUNDATION_FRAMEWORK/m', + 'FALSE' + ); + FileSystem::replaceFileRegex( + SOURCE_PATH . '/curl/CMakeLists.txt', + '/NOT SYSTEMCONFIGURATION_FRAMEWORK/m', + 'FALSE' + ); + FileSystem::replaceFileRegex( + SOURCE_PATH . '/curl/CMakeLists.txt', + '/NOT CORESERVICES_FRAMEWORK/m', + 'FALSE' + ); + return true; } } diff --git a/src/SPC/builder/macos/library/glfw.php b/src/SPC/builder/macos/library/glfw.php new file mode 100644 index 00000000..91e8cb70 --- /dev/null +++ b/src/SPC/builder/macos/library/glfw.php @@ -0,0 +1,28 @@ +cd(SOURCE_PATH . '/ext-glfw/vendor/glfw') + ->exec("cmake . {$this->builder->makeCmakeArgs()} -DBUILD_SHARED_LIBS=OFF -DGLFW_BUILD_EXAMPLES=OFF -DGLFW_BUILD_TESTS=OFF") + ->exec("make -j{$this->builder->concurrency}") + ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + // patch pkgconf + $this->patchPkgconfPrefix(['glfw3.pc']); + } +} diff --git a/src/SPC/builder/macos/library/icu.php b/src/SPC/builder/macos/library/icu.php index dd7967a1..14a0ae94 100644 --- a/src/SPC/builder/macos/library/icu.php +++ b/src/SPC/builder/macos/library/icu.php @@ -8,11 +8,11 @@ class icu extends MacOSLibraryBase { public const NAME = 'icu'; - protected function build() + protected function build(): void { $root = BUILD_ROOT_PATH; shell()->cd($this->source_dir . '/source') - ->exec("{$this->builder->configure_env} ./runConfigureICU MacOSX --enable-static --disable-shared --prefix={$root}") + ->exec("./runConfigureICU MacOSX --enable-static --disable-shared --prefix={$root}") ->exec('make clean') ->exec("make -j{$this->builder->concurrency}") ->exec('make install'); diff --git a/src/SPC/builder/macos/library/ldap.php b/src/SPC/builder/macos/library/ldap.php new file mode 100644 index 00000000..227e0809 --- /dev/null +++ b/src/SPC/builder/macos/library/ldap.php @@ -0,0 +1,12 @@ + - * - * lwmbs is licensed under Mulan PSL v2. You can use this - * software according to the terms and conditions of the - * Mulan PSL v2. You may obtain a copy of Mulan PSL v2 at: - * - * http://license.coscl.org.cn/MulanPSL2 - * - * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, - * WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, - * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * - * See the Mulan PSL v2 for more details. - */ declare(strict_types=1); namespace SPC\builder\macos\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; + class libffi extends MacOSLibraryBase { public const NAME = 'libffi'; - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { - [$lib, , $destdir] = SEPARATED_PATH; + [, , $destdir] = SEPARATED_PATH; shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static ' . '--disable-shared ' . - "--host={$this->builder->arch}-apple-darwin " . - "--target={$this->builder->arch}-apple-darwin " . + "--host={$this->builder->getOption('arch')}-apple-darwin " . + "--target={$this->builder->getOption('arch')}-apple-darwin " . '--prefix= ' // use prefix=/ ) ->exec('make clean') diff --git a/src/SPC/builder/macos/library/libmemcached.php b/src/SPC/builder/macos/library/libmemcached.php new file mode 100644 index 00000000..09a600a2 --- /dev/null +++ b/src/SPC/builder/macos/library/libmemcached.php @@ -0,0 +1,31 @@ +cd($this->source_dir) + ->exec('chmod +x configure') + ->exec( + './configure ' . + '--enable-static --disable-shared ' . + '--disable-sasl ' . + "--prefix={$rootdir}" + ) + ->exec('make clean') + ->exec('sed -ie "s/-Werror//g" ' . $this->source_dir . '/Makefile') + ->exec("make -j{$this->builder->concurrency}") + ->exec('make install'); + } +} diff --git a/src/SPC/builder/macos/library/libpng.php b/src/SPC/builder/macos/library/libpng.php index 76a0645e..b41f8510 100644 --- a/src/SPC/builder/macos/library/libpng.php +++ b/src/SPC/builder/macos/library/libpng.php @@ -22,6 +22,7 @@ namespace SPC\builder\macos\library; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; class libpng extends MacOSLibraryBase { @@ -30,19 +31,21 @@ class libpng extends MacOSLibraryBase /** * @throws FileSystemException * @throws RuntimeException + * @throws WrongUsageException */ - protected function build() + protected function build(): void { - $optimizations = match ($this->builder->arch) { + $optimizations = match ($this->builder->getOption('arch')) { 'x86_64' => '--enable-intel-sse ', 'arm64' => '--enable-arm-neon ', default => '', }; shell()->cd($this->source_dir) ->exec('chmod +x ./configure') + ->exec('chmod +x ./install-sh') ->exec( - "{$this->builder->configure_env} ./configure " . - "--host={$this->builder->gnu_arch}-apple-darwin " . + './configure ' . + "--host={$this->builder->getOption('gnu-arch')}-apple-darwin " . '--disable-shared ' . '--enable-static ' . '--enable-hardware-optimizations ' . diff --git a/src/SPC/builder/macos/library/libxml2.php b/src/SPC/builder/macos/library/libxml2.php index 1333c89f..50cb3a5a 100644 --- a/src/SPC/builder/macos/library/libxml2.php +++ b/src/SPC/builder/macos/library/libxml2.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SPC\builder\macos\library; +use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; @@ -13,21 +14,20 @@ class libxml2 extends MacOSLibraryBase /** * @throws RuntimeException + * @throws FileSystemException */ - protected function build() + protected function build(): void { $enable_zlib = $this->builder->getLib('zlib') ? 'ON' : 'OFF'; $enable_icu = $this->builder->getLib('icu') ? 'ON' : 'OFF'; $enable_xz = $this->builder->getLib('xz') ? 'ON' : 'OFF'; - [$lib, $include, $destdir] = SEPARATED_PATH; - FileSystem::resetDir($this->source_dir . '/build'); shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} " . ' cmake ' . + 'cmake ' . // '--debug-find ' . - '-DCMAKE_BUILD_TYPE=Release ' . + "{$this->builder->makeCmakeArgs()} " . '-DBUILD_SHARED_LIBS=OFF ' . '-DLIBXML2_WITH_ICONV=ON ' . "-DLIBXML2_WITH_ZLIB={$enable_zlib} " . @@ -36,13 +36,9 @@ class libxml2 extends MacOSLibraryBase '-DLIBXML2_WITH_PYTHON=OFF ' . '-DLIBXML2_WITH_PROGRAMS=OFF ' . '-DLIBXML2_WITH_TESTS=OFF ' . - '-DCMAKE_INSTALL_PREFIX=/ ' . - "-DCMAKE_INSTALL_LIBDIR={$lib} " . - "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . - "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . '..' ) ->exec("cmake --build . -j {$this->builder->concurrency}") - ->exec("make install DESTDIR={$destdir}"); + ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); } } diff --git a/src/SPC/builder/macos/library/libxslt.php b/src/SPC/builder/macos/library/libxslt.php new file mode 100644 index 00000000..015be43d --- /dev/null +++ b/src/SPC/builder/macos/library/libxslt.php @@ -0,0 +1,15 @@ +builder->makeAutoconfArgs(static::NAME, [ 'zlib' => null, @@ -46,10 +53,10 @@ class nghttp2 extends MacOSLibraryBase shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} " . ' ./configure ' . + './configure ' . '--enable-static ' . '--disable-shared ' . - "--host={$this->builder->gnu_arch}-apple-darwin " . + "--host={$this->builder->getOption('gnu-arch')}-apple-darwin " . '--enable-lib-only ' . '--with-boost=no ' . $args . ' ' . diff --git a/src/SPC/builder/macos/library/openssl.php b/src/SPC/builder/macos/library/openssl.php index 7349e9ee..c768512d 100644 --- a/src/SPC/builder/macos/library/openssl.php +++ b/src/SPC/builder/macos/library/openssl.php @@ -20,11 +20,20 @@ declare(strict_types=1); namespace SPC\builder\macos\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; + class openssl extends MacOSLibraryBase { public const NAME = 'openssl'; - protected function build() + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + protected function build(): void { [$lib,,$destdir] = SEPARATED_PATH; @@ -39,10 +48,11 @@ class openssl extends MacOSLibraryBase shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./Configure no-shared {$extra} " . + "./Configure no-shared {$extra} " . '--prefix=/ ' . // use prefix=/ "--libdir={$lib} " . - " darwin64-{$this->builder->arch}-cc" + '--openssldir=/System/Library/OpenSSL ' . + "darwin64-{$this->builder->getOption('arch')}-cc" ) ->exec('make clean') ->exec("make -j{$this->builder->concurrency} CNF_EX_LIBS=\"{$ex_lib}\"") diff --git a/src/SPC/builder/macos/library/postgresql.php b/src/SPC/builder/macos/library/postgresql.php new file mode 100644 index 00000000..2ef7ffa1 --- /dev/null +++ b/src/SPC/builder/macos/library/postgresql.php @@ -0,0 +1,12 @@ +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( + ['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'); } @@ -125,7 +127,7 @@ trait UnixBuilderTrait } /** - * 清理编译好的文件 + * Run php clean * * @throws RuntimeException */ @@ -140,12 +142,35 @@ trait UnixBuilderTrait */ public function makeCmakeArgs(): string { - [$lib, $include] = SEPARATED_PATH; - $extra = $this instanceof LinuxBuilder ? '-DCMAKE_C_COMPILER=' . $this->cc . ' ' : ''; - return $extra . '-DCMAKE_BUILD_TYPE=Release ' . + $extra = $this instanceof LinuxBuilder ? '-DCMAKE_C_COMPILER=' . getenv('CC') . ' ' : ''; + return $extra . + '-DCMAKE_BUILD_TYPE=Release ' . '-DCMAKE_INSTALL_PREFIX=/ ' . - "-DCMAKE_INSTALL_LIBDIR={$lib} " . - "-DCMAKE_INSTALL_INCLUDEDIR={$include} " . + '-DCMAKE_INSTALL_BINDIR=/bin ' . + '-DCMAKE_INSTALL_LIBDIR=/lib ' . + '-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; + } } diff --git a/src/SPC/builder/traits/UnixLibraryTrait.php b/src/SPC/builder/traits/UnixLibraryTrait.php index 4bc210fa..0e4665a0 100644 --- a/src/SPC/builder/traits/UnixLibraryTrait.php +++ b/src/SPC/builder/traits/UnixLibraryTrait.php @@ -7,6 +7,7 @@ namespace SPC\builder\traits; use SPC\builder\LibraryBase; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\FileSystem; trait UnixLibraryTrait @@ -16,6 +17,7 @@ trait UnixLibraryTrait /** * @throws RuntimeException * @throws FileSystemException + * @throws WrongUsageException */ public function getStaticLibFiles(string $style = 'autoconf', bool $recursive = true): string { @@ -43,6 +45,11 @@ trait UnixLibraryTrait return implode($sep, $ret); } + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ public function makeAutoconfEnv(string $prefix = null): string { if ($prefix === null) { @@ -59,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) { @@ -74,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); } } @@ -82,7 +90,7 @@ trait UnixLibraryTrait * remove libtool archive files * * @throws FileSystemException - * @throws RuntimeException + * @throws WrongUsageException */ public function cleanLaFiles(): void { diff --git a/src/SPC/builder/traits/UnixSystemUtilTrait.php b/src/SPC/builder/traits/UnixSystemUtilTrait.php index 6e25ab7a..5aa325ab 100644 --- a/src/SPC/builder/traits/UnixSystemUtilTrait.php +++ b/src/SPC/builder/traits/UnixSystemUtilTrait.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SPC\builder\traits; +use SPC\exception\FileSystemException; use SPC\store\FileSystem; /** @@ -14,11 +15,12 @@ trait UnixSystemUtilTrait /** * 生成 toolchain.cmake,用于 cmake 构建 * - * @param string $os 操作系统代号 - * @param string $target_arch 目标架构 - * @param string $cflags CFLAGS 参数 - * @param null|string $cc CC 参数(默认空) - * @param null|string $cxx CXX 参数(默认空) + * @param string $os 操作系统代号 + * @param string $target_arch 目标架构 + * @param string $cflags CFLAGS 参数 + * @param null|string $cc CC 参数(默认空) + * @param null|string $cxx CXX 参数(默认空) + * @throws FileSystemException */ public static function makeCmakeToolchainFile( string $os, @@ -76,4 +78,20 @@ CMAKE; } return null; } + + /** + * @param array $vars Variables, like: ["CFLAGS" => "-Ixxx"] + * @return string like: CFLAGS="-Ixxx" + */ + public static function makeEnvVarString(array $vars): string + { + $str = ''; + foreach ($vars as $key => $value) { + if ($str !== '') { + $str .= ' '; + } + $str .= $key . '=' . escapeshellarg($value); + } + return $str; + } } diff --git a/src/SPC/builder/unix/library/brotli.php b/src/SPC/builder/unix/library/brotli.php index 21869601..92c04de4 100644 --- a/src/SPC/builder/unix/library/brotli.php +++ b/src/SPC/builder/unix/library/brotli.php @@ -4,16 +4,22 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\FileSystem; trait brotli { + /** + * @throws FileSystemException + * @throws RuntimeException + */ protected function build(): void { FileSystem::resetDir($this->source_dir . '/build-dir'); shell()->cd($this->source_dir . '/build-dir') ->exec( - $this->builder->configure_env . ' cmake ' . + 'cmake ' . "{$this->builder->makeCmakeArgs()} " . '-DBUILD_SHARED_LIBS=OFF ' . '..' @@ -22,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); diff --git a/src/SPC/builder/unix/library/bzip2.php b/src/SPC/builder/unix/library/bzip2.php index bb4f0d9e..71990125 100644 --- a/src/SPC/builder/unix/library/bzip2.php +++ b/src/SPC/builder/unix/library/bzip2.php @@ -9,8 +9,8 @@ trait bzip2 protected function build(): void { shell()->cd($this->source_dir) - ->exec("make {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' clean") - ->exec("make -j{$this->builder->concurrency} {$this->builder->configure_env} PREFIX='" . BUILD_ROOT_PATH . "' libbz2.a") + ->exec("make PREFIX='" . BUILD_ROOT_PATH . "' clean") + ->exec("make -j{$this->builder->concurrency} {$this->builder->getEnvString()} PREFIX='" . BUILD_ROOT_PATH . "' libbz2.a") ->exec('cp libbz2.a ' . BUILD_LIB_PATH) ->exec('cp bzlib.h ' . BUILD_INCLUDE_PATH); } diff --git a/src/SPC/builder/unix/library/curl.php b/src/SPC/builder/unix/library/curl.php index fe03acf6..216f42b5 100644 --- a/src/SPC/builder/unix/library/curl.php +++ b/src/SPC/builder/unix/library/curl.php @@ -4,11 +4,17 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\FileSystem; trait curl { - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { $extra = ''; // lib:openssl @@ -33,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 @@ -45,7 +51,8 @@ trait curl FileSystem::resetDir($this->source_dir . '/build'); // compile! shell()->cd($this->source_dir . '/build') - ->exec("{$this->builder->configure_env} cmake {$this->builder->makeCmakeArgs()} -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF {$extra} ..") + ->exec('sed -i.save s@\${CMAKE_C_IMPLICIT_LINK_LIBRARIES}@@ ../CMakeLists.txt') + ->exec("cmake {$this->builder->makeCmakeArgs()} -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF {$extra} ..") ->exec("make -j{$this->builder->concurrency}") ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); // patch pkgconf diff --git a/src/SPC/builder/unix/library/freetype.php b/src/SPC/builder/unix/library/freetype.php index 18b77997..8ef3368f 100644 --- a/src/SPC/builder/unix/library/freetype.php +++ b/src/SPC/builder/unix/library/freetype.php @@ -4,11 +4,19 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\FileSystem; trait freetype { - protected function build() + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + protected function build(): void { $suggested = $this->builder->getLib('libpng') ? '--with-png' : '--without-png'; $suggested .= ' '; @@ -19,7 +27,7 @@ trait freetype shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static --disable-shared --without-harfbuzz --prefix= ' . $suggested ) @@ -27,9 +35,8 @@ trait freetype ->exec("make -j{$this->builder->concurrency}") ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); $this->patchPkgconfPrefix(['freetype2.pc']); - FileSystem::replaceFile( + FileSystem::replaceFileStr( BUILD_ROOT_PATH . '/lib/pkgconfig/freetype2.pc', - REPLACE_FILE_STR, ' -L/lib ', ' -L' . BUILD_ROOT_PATH . '/lib ' ); diff --git a/src/SPC/builder/unix/library/gmp.php b/src/SPC/builder/unix/library/gmp.php index 934e9b02..f1902dde 100644 --- a/src/SPC/builder/unix/library/gmp.php +++ b/src/SPC/builder/unix/library/gmp.php @@ -4,13 +4,20 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; + trait gmp { - protected function build() + /** + * @throws FileSystemException + * @throws RuntimeException + */ + protected function build(): void { shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static --disable-shared ' . '--prefix=' ) diff --git a/src/SPC/builder/unix/library/imagemagick.php b/src/SPC/builder/unix/library/imagemagick.php index 6f1fd461..bd4d0424 100644 --- a/src/SPC/builder/unix/library/imagemagick.php +++ b/src/SPC/builder/unix/library/imagemagick.php @@ -4,29 +4,46 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\builder\linux\library\LinuxLibraryBase; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\FileSystem; trait imagemagick { + /** + * @throws RuntimeException + * @throws FileSystemException + */ protected function build(): void { - $extra = '--without-jxl --without-xml --without-zstd --without-x --disable-openmp '; - // libzip support - $extra .= $this->builder->getLib('libzip') ? '--with-zip ' : '--without-zip '; - // jpeg support - $extra .= $this->builder->getLib('libjpeg') ? '--with-jpeg ' : ''; - // png support - $extra .= $this->builder->getLib('libpng') ? '--with-png ' : ''; - // webp support - $extra .= $this->builder->getLib('libwebp') ? '--with-webp ' : ''; - // zstd support - // $extra .= $this->builder->getLib('zstd') ? '--with-zstd ' : '--without-zstd '; - // freetype support - $extra .= $this->builder->getLib('freetype') ? '--with-freetype ' : '--without-freetype '; + // TODO: imagemagick build with bzip2 failed with bugs, we need to fix it in the future + $extra = '--without-jxl --without-x --disable-openmp --without-bzlib '; + $required_libs = ''; + $optional_libs = [ + 'libzip' => 'zip', + 'libjpeg' => 'jpeg', + 'libpng' => 'png', + 'libwebp' => 'webp', + 'libxml2' => 'xml', + 'zlib' => 'zlib', + 'xz' => 'lzma', + 'zstd' => 'zstd', + 'freetype' => 'freetype', + ]; + foreach ($optional_libs as $lib => $option) { + $extra .= $this->builder->getLib($lib) ? "--with-{$option} " : "--without-{$option} "; + if ($this->builder->getLib($lib) instanceof LinuxLibraryBase) { + $required_libs .= ' ' . $this->builder->getLib($lib)->getStaticLibFiles(); + } + } + $ldflags = $this instanceof LinuxLibraryBase ? ('LDFLAGS="-static" ') : ''; shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + $ldflags . + "LIBS='{$required_libs}' " . + './configure ' . '--enable-static --disable-shared ' . $extra . '--prefix=' @@ -46,9 +63,8 @@ trait imagemagick ]; $this->patchPkgconfPrefix($filelist); foreach ($filelist as $file) { - FileSystem::replaceFile( + FileSystem::replaceFileRegex( BUILD_LIB_PATH . '/pkgconfig/' . $file, - REPLACE_FILE_PREG, '#includearchdir=/include/ImageMagick-7#m', 'includearchdir=${prefix}/include/ImageMagick-7' ); diff --git a/src/SPC/builder/unix/library/ldap.php b/src/SPC/builder/unix/library/ldap.php new file mode 100644 index 00000000..9b4080df --- /dev/null +++ b/src/SPC/builder/unix/library/ldap.php @@ -0,0 +1,38 @@ +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->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']); + } +} diff --git a/src/SPC/builder/unix/library/libavif.php b/src/SPC/builder/unix/library/libavif.php index ac27a5d9..592899b2 100644 --- a/src/SPC/builder/unix/library/libavif.php +++ b/src/SPC/builder/unix/library/libavif.php @@ -4,22 +4,25 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\FileSystem; trait libavif { - protected function build() + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + protected function build(): void { // CMake needs a clean build directory FileSystem::resetDir($this->source_dir . '/build'); // Start build shell()->cd($this->source_dir . '/build') - ->exec( - "{$this->builder->configure_env} cmake " . - $this->builder->makeCmakeArgs() . ' ' . - '-DBUILD_SHARED_LIBS=OFF ' . - '..' - ) + ->exec("cmake {$this->builder->makeCmakeArgs()} -DBUILD_SHARED_LIBS=OFF ..") ->exec("cmake --build . -j {$this->builder->concurrency}") ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); // patch pkgconfig diff --git a/src/SPC/builder/unix/library/libevent.php b/src/SPC/builder/unix/library/libevent.php index bd8f3b76..d9b2d81b 100644 --- a/src/SPC/builder/unix/library/libevent.php +++ b/src/SPC/builder/unix/library/libevent.php @@ -4,30 +4,36 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\FileSystem; trait libevent { - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { // CMake needs a clean build directory FileSystem::resetDir($this->source_dir . '/build'); // Start build shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} cmake " . + 'cmake ' . '-DCMAKE_INSTALL_PREFIX=' . BUILD_ROOT_PATH . ' ' . "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . '-DCMAKE_BUILD_TYPE=Release ' . '-DEVENT__LIBRARY_TYPE=STATIC ' . '-DEVENT__DISABLE_BENCHMARK=ON ' . '-DEVENT__DISABLE_THREAD_SUPPORT=ON ' . + '-DEVENT__DISABLE_MBEDTLS=ON ' . '-DEVENT__DISABLE_TESTS=ON ' . '-DEVENT__DISABLE_SAMPLES=ON ' . '..' ) ->exec("cmake --build . -j {$this->builder->concurrency}") ->exec('make install'); - // patch pkgconfig } } diff --git a/src/SPC/builder/unix/library/libiconv.php b/src/SPC/builder/unix/library/libiconv.php index 12ca1ada..5096e724 100644 --- a/src/SPC/builder/unix/library/libiconv.php +++ b/src/SPC/builder/unix/library/libiconv.php @@ -6,13 +6,13 @@ namespace SPC\builder\unix\library; trait libiconv { - protected function build() + protected function build(): void { [,,$destdir] = SEPARATED_PATH; shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static ' . '--disable-shared ' . '--prefix=' diff --git a/src/SPC/builder/unix/library/libjpeg.php b/src/SPC/builder/unix/library/libjpeg.php index 65da0c08..df02bd13 100644 --- a/src/SPC/builder/unix/library/libjpeg.php +++ b/src/SPC/builder/unix/library/libjpeg.php @@ -4,18 +4,26 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\FileSystem; trait libjpeg { - protected function build() + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + protected function build(): void { // CMake needs a clean build directory FileSystem::resetDir($this->source_dir . '/build'); // Start build shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} cmake {$this->builder->makeCmakeArgs()} " . + "cmake {$this->builder->makeCmakeArgs()} " . '-DENABLE_STATIC=ON ' . '-DENABLE_SHARED=OFF ' . '..' diff --git a/src/SPC/builder/unix/library/libsodium.php b/src/SPC/builder/unix/library/libsodium.php index 2f093d03..555b48d6 100644 --- a/src/SPC/builder/unix/library/libsodium.php +++ b/src/SPC/builder/unix/library/libsodium.php @@ -6,11 +6,11 @@ namespace SPC\builder\unix\library; trait libsodium { - protected function build() + protected function build(): void { $root = BUILD_ROOT_PATH; shell()->cd($this->source_dir) - ->exec("{$this->builder->configure_env} ./configure --enable-static --disable-shared --prefix={$root}") + ->exec("./configure --enable-static --disable-shared --prefix={$root}") ->exec('make clean') ->exec("make -j{$this->builder->concurrency}") ->exec('make install'); diff --git a/src/SPC/builder/unix/library/libssh2.php b/src/SPC/builder/unix/library/libssh2.php index c0dccdf2..32d96b9a 100644 --- a/src/SPC/builder/unix/library/libssh2.php +++ b/src/SPC/builder/unix/library/libssh2.php @@ -4,18 +4,24 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\FileSystem; trait libssh2 { - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { $enable_zlib = $this->builder->getLib('zlib') !== null ? 'ON' : 'OFF'; FileSystem::resetDir($this->source_dir . '/build'); shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} " . ' cmake ' . + 'cmake ' . "{$this->builder->makeCmakeArgs()} " . '-DBUILD_SHARED_LIBS=OFF ' . '-DBUILD_EXAMPLES=OFF ' . @@ -23,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']); } diff --git a/src/SPC/builder/unix/library/libwebp.php b/src/SPC/builder/unix/library/libwebp.php index f6e7a596..52eb8071 100644 --- a/src/SPC/builder/unix/library/libwebp.php +++ b/src/SPC/builder/unix/library/libwebp.php @@ -4,30 +4,38 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; +use SPC\store\FileSystem; + trait libwebp { - protected function build() + /** + * @throws FileSystemException + * @throws RuntimeException + * @throws WrongUsageException + */ + protected function build(): void { - [,,$destdir] = SEPARATED_PATH; - - shell()->cd($this->source_dir) - ->exec('./autogen.sh') + // CMake needs a clean build directory + FileSystem::resetDir($this->source_dir . '/build'); + // Start build + shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} ./configure " . - '--enable-static ' . - '--disable-shared ' . - '--prefix= ' . - '--enable-libwebpdecoder ' . - '--enable-libwebpextras ' . - '--disable-tiff ' . - '--disable-gl ' . - '--disable-sdl ' . - '--disable-wic' + 'cmake ' . + $this->builder->makeCmakeArgs() . ' ' . + '-DBUILD_SHARED_LIBS=OFF ' . + '-DWEBP_BUILD_EXTRAS=ON ' . + '..' ) - ->exec('make clean') - ->exec("make -j{$this->builder->concurrency}") - ->exec('make install DESTDIR=' . $destdir); - $this->patchPkgconfPrefix(['libsharpyuv.pc', 'libwebp.pc', 'libwebpdecoder.pc', 'libwebpdemux.pc', 'libwebpmux.pc'], PKGCONF_PATCH_PREFIX); + ->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 | PKGCONF_PATCH_LIBDIR); + $this->patchPkgconfPrefix(['libsharpyuv.pc'], PKGCONF_PATCH_CUSTOM, ['/^includedir=.*$/m', 'includedir=${prefix}/include/webp']); $this->cleanLaFiles(); + // fix imagemagick binary linking issue + $this->patchPkgconfPrefix(['libwebp.pc'], PKGCONF_PATCH_CUSTOM, ['/-lwebp$/m', '-lwebp -lsharpyuv']); } } diff --git a/src/SPC/builder/unix/library/libxslt.php b/src/SPC/builder/unix/library/libxslt.php new file mode 100644 index 00000000..17fb2f1e --- /dev/null +++ b/src/SPC/builder/unix/library/libxslt.php @@ -0,0 +1,49 @@ +getDependencies() as $dep) { + if ($dep instanceof LinuxLibraryBase) { + $required_libs .= ' ' . $dep->getStaticLibFiles(); + } + } + shell()->cd($this->source_dir) + ->exec( + 'CFLAGS="-I' . BUILD_INCLUDE_PATH . '" ' . + "{$this->builder->getOption('library_path')} " . + "{$this->builder->getOption('ld_library_path')} " . + 'LDFLAGS="-L' . BUILD_LIB_PATH . '" ' . + "LIBS='{$required_libs} -lstdc++' " . + './configure ' . + '--enable-static --disable-shared ' . + '--without-python ' . + '--without-mem-debug ' . + '--without-crypto ' . + '--without-debug ' . + '--without-debugger ' . + '--with-libxml-prefix=' . escapeshellarg(BUILD_ROOT_PATH) . ' ' . + '--prefix=' + ) + ->exec('make clean') + ->exec("make -j{$this->builder->concurrency}") + ->exec('make install DESTDIR=' . escapeshellarg(BUILD_ROOT_PATH)); + $this->patchPkgconfPrefix(['libexslt.pc']); + } +} diff --git a/src/SPC/builder/unix/library/libyaml.php b/src/SPC/builder/unix/library/libyaml.php index 58ea9fe3..b1b545cf 100644 --- a/src/SPC/builder/unix/library/libyaml.php +++ b/src/SPC/builder/unix/library/libyaml.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\FileSystem; @@ -11,8 +12,9 @@ trait libyaml { /** * @throws RuntimeException + * @throws FileSystemException */ - protected function build() + protected function build(): void { // prepare cmake/config.h.in if (!is_file(SOURCE_PATH . '/libyaml/cmake/config.h.in')) { @@ -58,7 +60,7 @@ EOF FileSystem::resetDir($this->source_dir . '/build'); shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} cmake " . + 'cmake ' . // '--debug-find ' . '-DCMAKE_BUILD_TYPE=Release ' . '-DBUILD_TESTING=OFF ' . diff --git a/src/SPC/builder/unix/library/libzip.php b/src/SPC/builder/unix/library/libzip.php index b4b8f99f..83e0600c 100644 --- a/src/SPC/builder/unix/library/libzip.php +++ b/src/SPC/builder/unix/library/libzip.php @@ -4,11 +4,17 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\FileSystem; trait libzip { - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { $extra = ''; // lib:bzip2 @@ -23,7 +29,7 @@ trait libzip FileSystem::resetDir($this->source_dir . '/build'); shell()->cd($this->source_dir . '/build') ->exec( - "{$this->builder->configure_env} " . ' cmake ' . + 'cmake ' . "{$this->builder->makeCmakeArgs()} " . '-DENABLE_GNUTLS=OFF ' . '-DENABLE_MBEDTLS=OFF ' . diff --git a/src/SPC/builder/unix/library/ncurses.php b/src/SPC/builder/unix/library/ncurses.php index f09246eb..790c77b6 100644 --- a/src/SPC/builder/unix/library/ncurses.php +++ b/src/SPC/builder/unix/library/ncurses.php @@ -6,11 +6,11 @@ namespace SPC\builder\unix\library; trait ncurses { - protected function build() + protected function build(): void { shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static ' . '--disable-shared ' . '--enable-overwrite ' . diff --git a/src/SPC/builder/unix/library/onig.php b/src/SPC/builder/unix/library/onig.php index 72969d79..b43d14b3 100644 --- a/src/SPC/builder/unix/library/onig.php +++ b/src/SPC/builder/unix/library/onig.php @@ -4,19 +4,21 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; + trait onig { - protected function build() + /** + * @throws FileSystemException + * @throws RuntimeException + */ + protected function build(): void { [,,$destdir] = SEPARATED_PATH; shell()->cd($this->source_dir) - ->exec( - "{$this->builder->configure_env} " . ' ./configure ' . - '--enable-static ' . - '--disable-shared ' . - '--prefix=' - ) + ->exec('./configure --enable-static --disable-shared --prefix=') ->exec('make clean') ->exec("make -j{$this->builder->concurrency}") ->exec("make install DESTDIR={$destdir}"); diff --git a/src/SPC/builder/unix/library/pkgconfig.php b/src/SPC/builder/unix/library/pkgconfig.php index 6cc475d9..52ec5e4d 100644 --- a/src/SPC/builder/unix/library/pkgconfig.php +++ b/src/SPC/builder/unix/library/pkgconfig.php @@ -6,20 +6,11 @@ namespace SPC\builder\unix\library; trait pkgconfig { - protected function build() + protected function build(): void { - $macos_env = 'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig/" ' . - "CC='{$this->builder->cc}' " . - "CXX='{$this->builder->cxx}' " . - "CFLAGS='{$this->builder->arch_c_flags} -Wimplicit-function-declaration' "; - $linux_env = 'PKG_CONFIG_PATH="' . BUILD_LIB_PATH . '/pkgconfig" ' . - "CC='{$this->builder->cc}' " . - "CXX='{$this->builder->cxx}' "; + $macos_env = "CFLAGS='{$this->builder->arch_c_flags} -Wimplicit-function-declaration' "; + $linux_env = 'LDFLAGS=--static '; - $extra = match (PHP_OS_FAMILY) { - 'Darwin' => '', - default => '--with-internal-glib ', - }; shell()->cd($this->source_dir) ->exec( match (PHP_OS_FAMILY) { @@ -29,7 +20,7 @@ trait pkgconfig './configure ' . '--disable-shared ' . '--enable-static ' . - $extra . + '--with-internal-glib ' . '--prefix=' . BUILD_ROOT_PATH . ' ' . '--without-sysroot ' . '--without-system-include-path ' . diff --git a/src/SPC/builder/unix/library/postgresql.php b/src/SPC/builder/unix/library/postgresql.php new file mode 100644 index 00000000..38be1bf2 --- /dev/null +++ b/src/SPC/builder/unix/library/postgresql.php @@ -0,0 +1,103 @@ + 'libzstd', + 'ldap' => 'ldap', + 'libpam' => 'libpam', + 'libxslt' => 'libxslt', + 'icu' => 'icu-i18n', + ]; + foreach ($optional_packages as $lib => $pkg) { + if ($this->getBuilder()->getLib($lib)) { + $packages .= ' ' . $pkg; + } + } + + $output = shell()->execWithResult("pkg-config --cflags-only-I --static {$packages}"); + if (!empty($output[1][0])) { + $cppflags = $output[1][0]; + $envs .= " CPPFLAGS=\"{$cppflags}\""; + } + $output = shell()->execWithResult("pkg-config --libs-only-L --static {$packages}"); + if (!empty($output[1][0])) { + $ldflags = $output[1][0]; + $envs .= $this instanceof MacOSLibraryBase ? " LDFLAGS=\"{$ldflags}\" " : " LDFLAGS=\"{$ldflags} -static\" "; + } + $output = shell()->execWithResult("pkg-config --libs-only-l --static {$packages}"); + if (!empty($output[1][0])) { + $libs = $output[1][0]; + $libcpp = ''; + if ($this->builder->getLib('icu')) { + $libcpp = $this instanceof LinuxLibraryBase ? ' -lstdc++' : ' -lc++'; + } + $envs .= " LIBS=\"{$libs}{$libcpp}\" "; + } + + FileSystem::resetDir($this->source_dir . '/build'); + + # 有静态链接配置 参考文件: src/interfaces/libpq/Makefile + shell()->cd($this->source_dir . '/build') + ->exec('sed -i.backup "s/invokes exit\'; exit 1;/invokes exit\';/" ../src/interfaces/libpq/Makefile') + ->exec('sed -i.backup "293 s/^/#$/" ../src/Makefile.shlib') + ->exec('sed -i.backup "441 s/^/#$/" ../src/Makefile.shlib'); + + // configure + shell()->cd($this->source_dir . '/build') + ->exec( + "{$envs} ../configure " . + "--prefix={$builddir} " . + '--disable-thread-safety ' . + '--enable-coverage=no ' . + '--with-ssl=openssl ' . + '--with-readline ' . + '--with-libxml ' . + ($this->builder->getLib('icu') ? '--with-icu ' : '--without-icu ') . + ($this->builder->getLib('ldap') ? '--with-ldap ' : '--without-ldap ') . + ($this->builder->getLib('libpam') ? '--with-pam ' : '--without-pam ') . + ($this->builder->getLib('libxslt') ? '--with-libxslt ' : '--without-libxslt ') . + ($this->builder->getLib('zstd') ? '--with-zstd ' : '--without-zstd ') . + '--without-lz4 ' . + '--without-perl ' . + '--without-python ' . + '--without-bonjour ' . + '--without-tcl ' + ); + + // build + shell()->cd($this->source_dir . '/build') + ->exec($envs . ' make -C src/bin/pg_config install') + ->exec($envs . ' make -C src/include install') + ->exec($envs . ' make -C src/common install') + ->exec($envs . ' make -C src/backend/port install') + ->exec($envs . ' make -C src/port install') + ->exec($envs . ' make -C src/backend/libpq install') + ->exec($envs . ' make -C src/interfaces/libpq install'); + + // remove dynamic libs + shell()->cd($this->source_dir . '/build') + ->exec("rm -rf {$builddir}/lib/*.so.*") + ->exec("rm -rf {$builddir}/lib/*.so") + ->exec("rm -rf {$builddir}/lib/*.dylib"); + } +} diff --git a/src/SPC/builder/unix/library/readline.php b/src/SPC/builder/unix/library/readline.php index 31ea26aa..4f725129 100644 --- a/src/SPC/builder/unix/library/readline.php +++ b/src/SPC/builder/unix/library/readline.php @@ -4,13 +4,20 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; + trait readline { - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static=yes ' . '--enable-shared=no ' . '--prefix= ' . diff --git a/src/SPC/builder/unix/library/snappy.php b/src/SPC/builder/unix/library/snappy.php new file mode 100644 index 00000000..d7a5ade0 --- /dev/null +++ b/src/SPC/builder/unix/library/snappy.php @@ -0,0 +1,33 @@ +source_dir . '/cmake/build'); + + shell()->cd($this->source_dir . '/cmake/build') + ->exec( + 'cmake ' . + "-DCMAKE_TOOLCHAIN_FILE={$this->builder->cmake_toolchain_file} " . + "{$this->builder->makeCmakeArgs()} " . + '-DSNAPPY_BUILD_TESTS=OFF ' . + '-DSNAPPY_BUILD_BENCHMARKS=OFF ' . + '../..' + ) + ->exec("cmake --build . -j {$this->builder->concurrency}") + ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); + } +} diff --git a/src/SPC/builder/unix/library/sqlite.php b/src/SPC/builder/unix/library/sqlite.php index add31e53..b706a39f 100644 --- a/src/SPC/builder/unix/library/sqlite.php +++ b/src/SPC/builder/unix/library/sqlite.php @@ -6,10 +6,10 @@ namespace SPC\builder\unix\library; trait sqlite { - protected function build() + protected function build(): void { shell()->cd($this->source_dir) - ->exec("{$this->builder->configure_env} ./configure --enable-static --disable-shared --prefix=") + ->exec('./configure --enable-static --disable-shared --prefix=') ->exec('make clean') ->exec("make -j{$this->builder->concurrency}") ->exec('make install DESTDIR=' . BUILD_ROOT_PATH); diff --git a/src/SPC/builder/unix/library/xz.php b/src/SPC/builder/unix/library/xz.php index 006ed6c2..7cd7e74b 100644 --- a/src/SPC/builder/unix/library/xz.php +++ b/src/SPC/builder/unix/library/xz.php @@ -4,16 +4,23 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; + trait xz { - public function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + public function build(): void { shell()->cd($this->source_dir) ->exec( - "{$this->builder->configure_env} ./configure " . + './configure ' . '--enable-static ' . '--disable-shared ' . - "--host={$this->builder->gnu_arch}-unknown-linux " . + "--host={$this->builder->getOption('gnu-arch')}-unknown-linux " . '--disable-scripts ' . '--disable-doc ' . '--with-libiconv ' . diff --git a/src/SPC/builder/unix/library/zlib.php b/src/SPC/builder/unix/library/zlib.php index 542d918d..b380f8b0 100644 --- a/src/SPC/builder/unix/library/zlib.php +++ b/src/SPC/builder/unix/library/zlib.php @@ -4,14 +4,21 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; + trait zlib { - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { [,,$destdir] = SEPARATED_PATH; shell()->cd($this->source_dir) - ->exec("{$this->builder->configure_env} ./configure --static --prefix=") + ->exec('./configure --static --prefix=') ->exec('make clean') ->exec("make -j{$this->builder->concurrency}") ->exec("make install DESTDIR={$destdir}"); diff --git a/src/SPC/builder/unix/library/zstd.php b/src/SPC/builder/unix/library/zstd.php index f6b4ea32..399eeedc 100644 --- a/src/SPC/builder/unix/library/zstd.php +++ b/src/SPC/builder/unix/library/zstd.php @@ -4,16 +4,22 @@ declare(strict_types=1); namespace SPC\builder\unix\library; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\FileSystem; trait zstd { - protected function build() + /** + * @throws RuntimeException + * @throws FileSystemException + */ + protected function build(): void { FileSystem::resetDir($this->source_dir . '/build/cmake/build'); shell()->cd($this->source_dir . '/build/cmake/build') ->exec( - "{$this->builder->configure_env} cmake " . + 'cmake ' . "{$this->builder->makeCmakeArgs()} " . '-DZSTD_BUILD_STATIC=ON ' . '-DZSTD_BUILD_SHARED=OFF ' . diff --git a/src/SPC/command/BaseCommand.php b/src/SPC/command/BaseCommand.php index 11ea6215..998336e9 100644 --- a/src/SPC/command/BaseCommand.php +++ b/src/SPC/command/BaseCommand.php @@ -17,16 +17,8 @@ abstract class BaseCommand extends Command { protected bool $no_motd = false; - /** - * 输入 - */ protected InputInterface $input; - /** - * 输出 - * - * 一般来说同样会是 ConsoleOutputInterface - */ protected OutputInterface $output; public function __construct(string $name = null) @@ -36,12 +28,12 @@ abstract class BaseCommand extends Command $this->addOption('no-motd', null, null, 'Disable motd'); } - public function initialize(InputInterface $input, OutputInterface $output) + public function initialize(InputInterface $input, OutputInterface $output): void { if ($input->getOption('no-motd')) { $this->no_motd = true; } - // 注册全局错误处理器 + set_error_handler(static function ($error_no, $error_msg, $error_file, $error_line) { $tips = [ E_WARNING => ['PHP Warning: ', 'warning'], @@ -77,6 +69,9 @@ abstract class BaseCommand extends Command } } + /** + * @throws WrongUsageException + */ abstract public function handle(): int; protected function execute(InputInterface $input, OutputInterface $output): int @@ -91,9 +86,8 @@ abstract class BaseCommand extends Command foreach ($msg as $v) { logger()->error($v); } - return self::FAILURE; + return static::FAILURE; } catch (\Throwable $e) { - // 不开 debug 模式就不要再显示复杂的调试栈信息了 if ($this->getOption('debug')) { ExceptionHandler::getInstance()->handle($e); } else { @@ -102,10 +96,10 @@ abstract class BaseCommand extends Command logger()->error($v); } } - return self::FAILURE; + return static::FAILURE; } } - return self::SUCCESS; + return static::SUCCESS; } protected function getOption(string $name): mixed @@ -118,11 +112,6 @@ abstract class BaseCommand extends Command return $this->input->getArgument($name); } - /** - * 是否应该执行 - * - * @return bool 返回 true 以继续执行,返回 false 以中断执行 - */ protected function shouldExecute(): bool { return true; diff --git a/src/SPC/command/BuildCliCommand.php b/src/SPC/command/BuildCliCommand.php index 6aad5509..72235847 100644 --- a/src/SPC/command/BuildCliCommand.php +++ b/src/SPC/command/BuildCliCommand.php @@ -7,6 +7,8 @@ namespace SPC\command; use SPC\builder\BuilderProvider; use SPC\exception\ExceptionHandler; use SPC\exception\WrongUsageException; +use SPC\store\FileSystem; +use SPC\store\SourcePatcher; use SPC\util\DependencyUtil; use SPC\util\LicenseDumper; use Symfony\Component\Console\Attribute\AsCommand; @@ -17,44 +19,48 @@ use ZM\Logger\ConsoleColor; #[AsCommand('build', 'build CLI binary')] class BuildCliCommand extends BuildCommand { - public function configure() + public function configure(): void { $this->addArgument('extensions', InputArgument::REQUIRED, 'The extensions will be compiled, comma separated'); $this->addOption('with-libs', null, InputOption::VALUE_REQUIRED, 'add additional libraries, comma separated', ''); $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('disable-opcache-jit', null, null, 'disable opcache jit'); + $this->addOption('with-hardcoded-ini', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Patch PHP source code, inject hardcoded INI'); + $this->addOption('with-micro-fake-cli', null, null, 'Enable phpmicro fake cli'); } public function handle(): int { - // 从参数中获取要编译的 libraries,并转换为数组 + // transform string to array $libraries = array_map('trim', array_filter(explode(',', $this->getOption('with-libs')))); - // 从参数中获取要编译的 extensions,并转换为数组 + // transform string to array $extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions')))); - define('BUILD_ALL_STATIC', true); - $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('Please add at least one build target!'); $this->output->writeln("\t--build-cli\tBuild php-cli SAPI"); $this->output->writeln("\t--build-micro\tBuild phpmicro SAPI"); $this->output->writeln("\t--build-fpm\tBuild php-fpm SAPI"); - $this->output->writeln("\t--build-all\tBuild all SAPI: cli, micro, fpm"); - return 1; + $this->output->writeln("\t--build-embed\tBuild embed SAPI/libphp"); + $this->output->writeln("\t--build-all\tBuild all SAPI: cli, micro, fpm, embed"); + return static::FAILURE; } try { - // 构建对象 + // create builder $builder = BuilderProvider::makeBuilderByInput($this->input); - // 根据提供的扩展列表获取依赖库列表并编译 + // calculate dependencies [$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions, $libraries); /* @phpstan-ignore-next-line */ logger()->info('Build target: ' . ConsoleColor::yellow($builder->getBuildTypeName($rule))); @@ -66,17 +72,34 @@ class BuildCliCommand extends BuildCommand logger()->warning('some extensions will be enabled due to dependencies: ' . implode(',', $not_included)); } sleep(2); - // 编译和检查库是否完整 + if ($this->input->getOption('with-clean')) { + logger()->info('Cleaning source dir...'); + FileSystem::removeDir(SOURCE_PATH); + } + // compile libraries $builder->buildLibs($libraries); - // 执行扩展检测 + // check extensions $builder->proveExts($extensions); - // strip - $builder->setStrip(false); - // 构建 - $builder->buildPHP($rule, $this->getOption('bloat')); - // 统计时间 + + // Process -I option + $custom_ini = []; + foreach ($this->input->getOption('with-hardcoded-ini') as $value) { + [$source_name, $ini_value] = explode('=', $value, 2); + $custom_ini[$source_name] = $ini_value; + logger()->info('Adding hardcoded INI [' . $source_name . ' = ' . $ini_value . ']'); + } + if (!empty($custom_ini)) { + SourcePatcher::patchHardcodedINI($custom_ini); + } + + // start to build + $builder->buildPHP($rule); + + // compile stopwatch :P $time = round(microtime(true) - START_TIME, 3); logger()->info('Build complete, used ' . $time . ' s !'); + + // ---------- When using bin/spc-alpine-docker, the build root path is different from the host system ---------- $build_root_path = BUILD_ROOT_PATH; $cwd = getcwd(); $fixed = ''; @@ -94,17 +117,19 @@ class BuildCliCommand extends BuildCommand if (($rule & BUILD_TARGET_FPM) === BUILD_TARGET_FPM) { logger()->info('Static php-fpm binary path' . $fixed . ': ' . $build_root_path . '/bin/php-fpm'); } - // 导出相关元数据 + + // export metadata file_put_contents(BUILD_ROOT_PATH . '/build-extensions.json', json_encode($extensions, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); file_put_contents(BUILD_ROOT_PATH . '/build-libraries.json', json_encode($libraries, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - // 导出 LICENSE + // export licenses $dumper = new LicenseDumper(); $dumper->addExts($extensions)->addLibs($libraries)->addSources(['php-src'])->dump(BUILD_ROOT_PATH . '/license'); logger()->info('License path' . $fixed . ': ' . $build_root_path . '/license/'); - return 0; + return static::SUCCESS; } catch (WrongUsageException $e) { + // WrongUsageException is not an exception, it's a user error, so we just print the error message logger()->critical($e->getMessage()); - return 1; + return static::FAILURE; } catch (\Throwable $e) { if ($this->getOption('debug')) { ExceptionHandler::getInstance()->handle($e); @@ -112,7 +137,7 @@ class BuildCliCommand extends BuildCommand logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage()); logger()->critical('Please check with --debug option to see more details.'); } - return 1; + return static::FAILURE; } } } diff --git a/src/SPC/command/BuildCommand.php b/src/SPC/command/BuildCommand.php index 11507546..4d3f7d06 100644 --- a/src/SPC/command/BuildCommand.php +++ b/src/SPC/command/BuildCommand.php @@ -12,7 +12,6 @@ abstract class BuildCommand extends BaseCommand { parent::__construct($name); - // 根据运行的操作系统分配允许不同的命令行参数,Windows 需要额外的 VS 和 SDK等,*nix 需要提供架构 switch (PHP_OS_FAMILY) { case 'Windows': $this->addOption('with-sdk-binary-dir', null, InputOption::VALUE_REQUIRED, 'path to binary sdk'); @@ -20,18 +19,17 @@ abstract class BuildCommand extends BaseCommand $this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture, "x64" or "arm64"', 'x64'); break; case 'Linux': - $this->addOption('no-system-static', null, null, 'do not use system static libraries'); - // no break case 'Darwin': $this->addOption('cc', null, InputOption::VALUE_REQUIRED, 'C compiler'); $this->addOption('cxx', null, InputOption::VALUE_REQUIRED, 'C++ compiler'); + $this->addOption('ar', null, InputOption::VALUE_REQUIRED, 'ar'); + $this->addOption('ld', null, InputOption::VALUE_REQUIRED, 'ld'); $this->addOption('arch', null, InputOption::VALUE_REQUIRED, 'architecture', php_uname('m')); break; } - // 是否在编译 make 前清除旧的文件 - $this->addOption('with-clean', null, null, 'fresh build, `make clean` before `make`'); - // 是否采用强制链接,让链接器强制加载静态库文件 + $this->addOption('with-clean', null, null, 'fresh build, remove `source` dir before `make`'); $this->addOption('bloat', null, null, 'add all libraries into binary'); + $this->addOption('rebuild', 'r', null, 'Delete old build and rebuild'); } } diff --git a/src/SPC/command/BuildLibsCommand.php b/src/SPC/command/BuildLibsCommand.php index d4a23a2a..be49f86a 100644 --- a/src/SPC/command/BuildLibsCommand.php +++ b/src/SPC/command/BuildLibsCommand.php @@ -15,14 +15,14 @@ use Symfony\Component\Console\Output\OutputInterface; #[AsCommand('build:libs', 'Build dependencies')] class BuildLibsCommand extends BuildCommand { - public function configure() + public function configure(): void { $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'); } - public function initialize(InputInterface $input, OutputInterface $output) + public function initialize(InputInterface $input, OutputInterface $output): void { // --all 等于 "" if ($input->getOption('all')) { @@ -63,7 +63,7 @@ class BuildLibsCommand extends BuildCommand $time = round(microtime(true) - START_TIME, 3); logger()->info('Build libs complete, used ' . $time . ' s !'); - return 0; + return static::SUCCESS; } catch (\Throwable $e) { if ($this->getOption('debug')) { ExceptionHandler::getInstance()->handle($e); @@ -71,7 +71,7 @@ class BuildLibsCommand extends BuildCommand logger()->critical('Build failed with ' . get_class($e) . ': ' . $e->getMessage()); logger()->critical('Please check with --debug option to see more details.'); } - return 1; + return static::FAILURE; } } } diff --git a/src/SPC/command/DeployCommand.php b/src/SPC/command/DeployCommand.php deleted file mode 100644 index dc97b035..00000000 --- a/src/SPC/command/DeployCommand.php +++ /dev/null @@ -1,131 +0,0 @@ -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.'); - } - - public function handle(): int - { - // 第一阶段流程:如果没有写path,将会提示输入要打包的path - $prompt = new ArgFixer($this->input, $this->output); - // 首先得确认是不是关闭了readonly模式 - if (ini_get('phar.readonly') == 1) { - if ($this->getOption('auto-phar-fix')) { - $ask = true; - } else { - $ask = $prompt->requireBool('pack command needs "phar.readonly" = "Off" !' . 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']); - if (function_exists('pcntl_exec')) { - $this->output->writeln('Changing to phar.readonly=0 mode ...'); - if (pcntl_exec(PHP_BINARY, $args) === false) { - throw new \PharException('切换到读写模式失败,请检查环境。'); - } - } else { - $this->output->writeln('Now running command in child process.'); - passthru(PHP_BINARY . ' -d phar.readonly=0 ' . implode(' ', $argv), $retcode); - exit($retcode); - } - } - } - // 获取路径 - $path = WORKING_DIR; - // 如果是目录,则将目录下的所有文件打包 - $phar_path = $prompt->requireArgument('target', 'Please input the phar target filename', 'static-php-cli.phar'); - - if (DataProvider::isRelativePath($phar_path)) { - $phar_path = '/tmp/' . $phar_path; - } - if (file_exists($phar_path)) { - $ask = $this->getOption('overwrite') ? true : $prompt->requireBool('The file "' . $phar_path . '" already exists, do you want to overwrite it?' . PHP_EOL . 'If you want to, just Enter'); - if (!$ask) { - $this->output->writeln('User canceled.'); - return 1; - } - @unlink($phar_path); - } - $phar = new \Phar($phar_path); - $phar->startBuffering(); - - $all = DataProvider::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('Start packing files...'); - 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 1; - } - $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('Binary Executable: ' . pathinfo($phar_path, PATHINFO_DIRNAME) . '/spc'); - } - chmod($phar_path, 0755); - $this->output->writeln('Phar Executable: ' . $phar_path . ''); - return 0; - } - - private function progress(int $max = 0): ProgressBar - { - $progress = new ProgressBar($this->output, $max); - $progress->setBarCharacter('⚬'); - $progress->setEmptyBarCharacter('⚬'); - $progress->setProgressCharacter('➤'); - $progress->setFormat( - "%current%/%max% [%bar%] %percent:3s%%\n🪅 %estimated:-20s% %memory:20s%" . PHP_EOL - ); - return $progress; - } -} diff --git a/src/SPC/command/DoctorCommand.php b/src/SPC/command/DoctorCommand.php index bf914f32..9aabdeca 100644 --- a/src/SPC/command/DoctorCommand.php +++ b/src/SPC/command/DoctorCommand.php @@ -5,22 +5,81 @@ 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 function Laravel\Prompts\confirm; + #[AsCommand('doctor', 'Diagnose whether the current environment can compile normally')] class DoctorCommand extends BaseCommand { + public function configure(): void + { + $this->addOption('auto-fix', null, null, 'Automatically fix failed items (if possible)'); + } + public function handle(): int { try { - $checker = new CheckListHandler($this->input, $this->output); - $checker->runCheck(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 ' . $check->item_name . ' ... '); + + $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('' . $result->getMessage() . ''); + switch ($fix_policy) { + case FIX_POLICY_DIE: + throw new RuntimeException('Some check items can not be fixed !'); + case FIX_POLICY_PROMPT: + if ($result->getFixItem() !== '') { + $question = confirm('Do you want to fix it?'); + if ($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('Doctor check complete !'); } catch (\Throwable $e) { $this->output->writeln('' . $e->getMessage() . ''); + pcntl_signal(SIGINT, SIG_IGN); - return 1; + + return static::FAILURE; } - return 0; + + return static::SUCCESS; } } diff --git a/src/SPC/command/DownloadCommand.php b/src/SPC/command/DownloadCommand.php index 640030f9..15d55e88 100644 --- a/src/SPC/command/DownloadCommand.php +++ b/src/SPC/command/DownloadCommand.php @@ -4,11 +4,14 @@ declare(strict_types=1); namespace SPC\command; +use SPC\builder\traits\UnixSystemUtilTrait; use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; use SPC\store\Config; use SPC\store\Downloader; +use SPC\util\DependencyUtil; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -18,22 +21,32 @@ use Symfony\Component\Console\Output\OutputInterface; #[AsCommand('download', 'Download required sources', ['fetch'])] class DownloadCommand extends BaseCommand { + use UnixSystemUtilTrait; + protected string $php_major_ver; - public function configure() + public function configure(): void { $this->addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated'); $this->addOption('shallow-clone', null, null, 'Clone shallow'); $this->addOption('with-openssl11', null, null, 'Use openssl 1.1'); - $this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format like 8.1', '8.1'); + $this->addOption('with-php', null, InputOption::VALUE_REQUIRED, 'version in major.minor format (default 8.2)', '8.2'); $this->addOption('clean', null, null, 'Clean old download cache and source before fetch'); $this->addOption('all', 'A', null, 'Fetch all sources that static-php-cli needed'); + $this->addOption('custom-url', 'U', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'Specify custom source download url, e.g "php-src:https://downloads.php.net/~eric/php-8.3.0beta1.tar.gz"'); + $this->addOption('from-zip', 'Z', InputOption::VALUE_REQUIRED, 'Fetch from zip archive'); + $this->addOption('for-extensions', 'e', InputOption::VALUE_REQUIRED, 'Fetch by extensions, e.g "openssl,mbstring"'); + $this->addOption('without-suggestions', null, null, 'Do not fetch suggested sources when using --for-extensions'); } - public function initialize(InputInterface $input, OutputInterface $output) + public function initialize(InputInterface $input, OutputInterface $output): void { - // --all 等于 "" "",也就是所有东西都要下载 - if ($input->getOption('all') || $input->getOption('clean')) { + if ( + $input->getOption('all') + || $input->getOption('clean') + || $input->getOption('from-zip') + || $input->getOption('for-extensions') + ) { $input->setArgument('sources', ''); } parent::initialize($input, $output); @@ -46,69 +59,169 @@ class DownloadCommand extends BaseCommand */ public function handle(): int { - // 删除旧资源 - if ($this->getOption('clean')) { + try { + if ($this->getOption('clean')) { + logger()->warning('You are doing some operations that not recoverable: removing directories below'); + logger()->warning(SOURCE_PATH); + logger()->warning(DOWNLOAD_PATH); + logger()->warning(BUILD_ROOT_PATH); + logger()->alert('I will remove these dir after 5 seconds !'); + sleep(5); + if (PHP_OS_FAMILY === 'Windows') { + f_passthru('rmdir /s /q ' . SOURCE_PATH); + f_passthru('rmdir /s /q ' . DOWNLOAD_PATH); + f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH); + } else { + f_passthru('rm -rf ' . SOURCE_PATH . '/*'); + f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*'); + f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*'); + } + return static::FAILURE; + } + + // --from-zip + if ($path = $this->getOption('from-zip')) { + return $this->downloadFromZip($path); + } + + // Define PHP major version + $ver = $this->php_major_ver = $this->getOption('with-php') ?? '8.1'; + define('SPC_BUILD_PHP_VERSION', $ver); + preg_match('/^\d+\.\d+$/', $ver, $matches); + if (!$matches) { + logger()->error("bad version arg: {$ver}, x.y required!"); + return static::FAILURE; + } + + // Use shallow-clone can reduce git resource download + if ($this->getOption('shallow-clone')) { + define('GIT_SHALLOW_CLONE', true); + } + + // To read config + Config::getSource('openssl'); + + // use openssl 1.1 + if ($this->getOption('with-openssl11')) { + logger()->debug('Using openssl 1.1'); + Config::$source['openssl']['regex'] = '/href="(?openssl-(?1.[^"]+)\.tar\.gz)\"/'; + } + + // --for-extensions + if ($for_ext = $this->getOption('for-extensions')) { + $ext = array_map('trim', array_filter(explode(',', $for_ext))); + $sources = $this->calculateSourcesByExt($ext, !$this->getOption('without-suggestions')); + array_unshift($sources, 'php-src', 'micro', 'pkg-config'); + } else { + // get source list that will be downloaded + $sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources')))); + if (empty($sources)) { + $sources = array_keys(Config::getSources()); + } + } + $chosen_sources = $sources; + + // Process -U options + $custom_urls = []; + foreach ($this->input->getOption('custom-url') as $value) { + [$source_name, $url] = explode(':', $value, 2); + $custom_urls[$source_name] = $url; + } + + // Download them + f_mkdir(DOWNLOAD_PATH); + $cnt = count($chosen_sources); + $ni = 0; + foreach ($chosen_sources as $source) { + ++$ni; + if (isset($custom_urls[$source])) { + $config = Config::getSource($source); + $new_config = [ + 'type' => 'url', + 'url' => $custom_urls[$source], + ]; + if (isset($config['path'])) { + $new_config['path'] = $config['path']; + } + if (isset($config['filename'])) { + $new_config['filename'] = $config['filename']; + } + logger()->info("Fetching source {$source} from custom url [{$ni}/{$cnt}]"); + Downloader::downloadSource($source, $new_config, true); + } else { + logger()->info("Fetching source {$source} [{$ni}/{$cnt}]"); + Downloader::downloadSource($source, Config::getSource($source)); + } + } + $time = round(microtime(true) - START_TIME, 3); + logger()->info('Download complete, used ' . $time . ' s !'); + return static::SUCCESS; + } catch (DownloaderException $e) { + logger()->error($e->getMessage()); + return static::FAILURE; + } catch (WrongUsageException $e) { + logger()->critical($e->getMessage()); + return static::FAILURE; + } + } + + private function downloadFromZip(string $path): int + { + if (!file_exists($path)) { + logger()->critical('File ' . $path . ' not exist or not a zip archive.'); + return static::FAILURE; + } + // remove old download files first + if (is_dir(DOWNLOAD_PATH)) { logger()->warning('You are doing some operations that not recoverable: removing directories below'); - logger()->warning(SOURCE_PATH); logger()->warning(DOWNLOAD_PATH); - logger()->warning(BUILD_ROOT_PATH); logger()->alert('I will remove these dir after 5 seconds !'); sleep(5); - if (PHP_OS_FAMILY === 'Windows') { - f_passthru('rmdir /s /q ' . SOURCE_PATH); - f_passthru('rmdir /s /q ' . DOWNLOAD_PATH); - f_passthru('rmdir /s /q ' . BUILD_ROOT_PATH); - } else { - f_passthru('rm -rf ' . SOURCE_PATH . '/*'); - f_passthru('rm -rf ' . DOWNLOAD_PATH . '/*'); - f_passthru('rm -rf ' . BUILD_ROOT_PATH . '/*'); + f_passthru((PHP_OS_FAMILY === 'Windows' ? 'rmdir /s /q ' : 'rm -rf ') . DOWNLOAD_PATH); + } + // unzip command check + if (PHP_OS_FAMILY !== 'Windows' && !$this->findCommand('unzip')) { + logger()->critical('Missing unzip command, you need to install it first !'); + logger()->critical('You can use "bin/spc doctor" command to check and install required tools'); + return static::FAILURE; + } + // create downloads + try { + if (PHP_OS_FAMILY !== 'Windows') { + f_passthru('mkdir ' . DOWNLOAD_PATH . ' && cd ' . DOWNLOAD_PATH . ' && unzip ' . escapeshellarg($path)); } - return 0; - } + // Windows TODO - // Define PHP major version - $ver = $this->php_major_ver = $this->getOption('with-php') ?? '8.1'; - define('SPC_BUILD_PHP_VERSION', $ver); - preg_match('/^\d+\.\d+$/', $ver, $matches); - if (!$matches) { - logger()->error("bad version arg: {$ver}, x.y required!"); - return 1; + if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) { + throw new RuntimeException('.lock.json not exist in "downloads/"'); + } + } catch (RuntimeException $e) { + logger()->critical('Extract failed: ' . $e->getMessage()); + return static::FAILURE; } + logger()->info('Extract success'); + return static::SUCCESS; + } - // Use shallow-clone can reduce git resource download - if ($this->getOption('shallow-clone')) { - define('GIT_SHALLOW_CLONE', true); + /** + * Calculate the sources by extensions + * + * @param array $extensions extension list + * @throws FileSystemException + * @throws WrongUsageException + */ + private function calculateSourcesByExt(array $extensions, bool $include_suggests = true): array + { + [$extensions, $libraries] = $include_suggests ? DependencyUtil::getAllExtLibsByDeps($extensions) : DependencyUtil::getExtLibsByDeps($extensions); + $sources = []; + foreach ($extensions as $extension) { + if (Config::getExt($extension, 'type') === 'external') { + $sources[] = Config::getExt($extension, 'source'); + } } - - // To read config - Config::getSource('openssl'); - - // use openssl 1.1 - if ($this->getOption('with-openssl11')) { - logger()->debug('Using openssl 1.1'); - // 手动修改配置 - Config::$source['openssl']['regex'] = '/href="(?openssl-(?1.[^"]+)\.tar\.gz)\"/'; + foreach ($libraries as $library) { + $sources[] = Config::getLib($library, 'source'); } - - // get source list that will be downloaded - $sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources')))); - if (empty($sources)) { - $sources = array_keys(Config::getSources()); - } - $chosen_sources = $sources; - - // Download them - f_mkdir(DOWNLOAD_PATH); - $cnt = count($chosen_sources); - $ni = 0; - foreach ($chosen_sources as $source) { - ++$ni; - logger()->info("Fetching source {$source} [{$ni}/{$cnt}]"); - Downloader::downloadSource($source, Config::getSource($source)); - } - // 打印拉取资源用时 - $time = round(microtime(true) - START_TIME, 3); - logger()->info('Download complete, used ' . $time . ' s !'); - return 0; + return array_values(array_unique($sources)); } } diff --git a/src/SPC/command/DumpLicenseCommand.php b/src/SPC/command/DumpLicenseCommand.php index 00f6f3fd..5535f820 100644 --- a/src/SPC/command/DumpLicenseCommand.php +++ b/src/SPC/command/DumpLicenseCommand.php @@ -18,9 +18,9 @@ use Symfony\Component\Console\Input\InputOption; #[AsCommand('dump-license', 'Dump licenses for required libraries')] class DumpLicenseCommand extends BaseCommand { - public function configure() + public function configure(): void { - $this->addOption('by-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null); + $this->addOption('for-extensions', null, InputOption::VALUE_REQUIRED, 'Dump by extensions and related libraries', null); $this->addOption('without-php', null, InputOption::VALUE_NONE, 'Dump without php-src'); $this->addOption('by-libs', null, InputOption::VALUE_REQUIRED, 'Dump by libraries', null); $this->addOption('by-sources', null, InputOption::VALUE_REQUIRED, 'Dump by original sources (source.json)', null); @@ -35,11 +35,11 @@ class DumpLicenseCommand extends BaseCommand public function handle(): int { $dumper = new LicenseDumper(); - if ($this->getOption('by-extensions') !== null) { + if ($this->getOption('for-extensions') !== null) { // 从参数中获取要编译的 extensions,并转换为数组 - $extensions = array_map('trim', array_filter(explode(',', $this->getOption('by-extensions')))); + $extensions = array_map('trim', array_filter(explode(',', $this->getOption('for-extensions')))); // 根据提供的扩展列表获取依赖库列表并编译 - [$extensions, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps($extensions); + [$extensions, $libraries] = DependencyUtil::getExtLibsByDeps($extensions); $dumper->addExts($extensions); $dumper->addLibs($libraries); if (!$this->getOption('without-php')) { @@ -50,7 +50,7 @@ class DumpLicenseCommand extends BaseCommand $this->output->writeln('Dump license with libraries: ' . implode(', ', $libraries)); $this->output->writeln('Dump license with' . ($this->getOption('without-php') ? 'out' : '') . ' php-src'); $this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir')); - return 0; + return static::SUCCESS; } if ($this->getOption('by-libs') !== null) { $libraries = array_map('trim', array_filter(explode(',', $this->getOption('by-libs')))); @@ -58,16 +58,16 @@ class DumpLicenseCommand extends BaseCommand $dumper->addLibs($libraries); $dumper->dump($this->getOption('dump-dir')); $this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir')); - return 0; + return static::SUCCESS; } if ($this->getOption('by-sources') !== null) { $sources = array_map('trim', array_filter(explode(',', $this->getOption('by-sources')))); $dumper->addSources($sources); $dumper->dump($this->getOption('dump-dir')); $this->output->writeln('Dump target dir: ' . $this->getOption('dump-dir')); - return 0; + return static::SUCCESS; } - $this->output->writeln('You must use one of "--by-extensions=", "--by-libs=", "--by-sources=" to dump'); - return 1; + $this->output->writeln('You must use one of "--for-extensions=", "--by-libs=", "--by-sources=" to dump'); + return static::FAILURE; } } diff --git a/src/SPC/command/ExtractCommand.php b/src/SPC/command/ExtractCommand.php new file mode 100644 index 00000000..84768866 --- /dev/null +++ b/src/SPC/command/ExtractCommand.php @@ -0,0 +1,41 @@ +addArgument('sources', InputArgument::REQUIRED, 'The sources will be compiled, comma separated'); + } + + /** + * @throws WrongUsageException + * @throws FileSystemException + * @throws RuntimeException + */ + public function handle(): int + { + $sources = array_map('trim', array_filter(explode(',', $this->getArgument('sources')))); + if (empty($sources)) { + $this->output->writeln('sources cannot be empty, at least contain one !'); + return static::FAILURE; + } + SourceExtractor::initSource(sources: $sources); + logger()->info('Extract done !'); + return static::SUCCESS; + } +} diff --git a/src/SPC/command/ListExtCommand.php b/src/SPC/command/ListExtCommand.php deleted file mode 100644 index f12466db..00000000 --- a/src/SPC/command/ListExtCommand.php +++ /dev/null @@ -1,26 +0,0 @@ - $meta) { - echo $ext . PHP_EOL; - } - return 0; - } -} diff --git a/src/SPC/command/MicroCombineCommand.php b/src/SPC/command/MicroCombineCommand.php new file mode 100644 index 00000000..1b14979a --- /dev/null +++ b/src/SPC/command/MicroCombineCommand.php @@ -0,0 +1,116 @@ +addArgument('file', InputArgument::REQUIRED, 'The php or phar file to be combined'); + $this->addOption('with-micro', 'M', InputOption::VALUE_REQUIRED, 'Customize your micro.sfx file'); + $this->addOption('with-ini-set', 'I', InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, 'ini to inject into micro.sfx when combining'); + $this->addOption('with-ini-file', 'N', InputOption::VALUE_REQUIRED, 'ini file to inject into micro.sfx when combining'); + $this->addOption('output', 'O', InputOption::VALUE_REQUIRED, 'Customize your output binary file name'); + } + + public function handle(): int + { + // 0. Initialize path variables + $internal = FileSystem::convertPath(BUILD_ROOT_PATH . '/bin/micro.sfx'); + $micro_file = $this->input->getOption('with-micro'); + $file = $this->getArgument('file'); + $ini_set = $this->input->getOption('with-ini-set'); + $ini_file = $this->input->getOption('with-ini-file'); + $target_ini = []; + $output = $this->input->getOption('output') ?? 'my-app'; + $ini_part = ''; + // 1. Make sure specified micro.sfx file exists + if ($micro_file !== null && !file_exists($micro_file)) { + $this->output->writeln('The micro.sfx file you specified is incorrect or does not exist!'); + return static::FAILURE; + } + // 2. Make sure buildroot/bin/micro.sfx exists + if ($micro_file === null && !file_exists($internal)) { + $this->output->writeln('You haven\'t compiled micro.sfx yet, please use "build" command and "--build-micro" to compile phpmicro first!'); + return static::FAILURE; + } + // 3. Use buildroot/bin/micro.sfx + if ($micro_file === null) { + $micro_file = $internal; + } + // 4. Make sure php or phar file exists + if (!is_file(FileSystem::convertPath($file))) { + $this->output->writeln('The file to combine does not exist!'); + return static::FAILURE; + } + // 5. Confirm ini files (ini-set has higher priority) + if ($ini_file !== null) { + // Check file exist first + if (!file_exists($ini_file)) { + $this->output->writeln('The ini file to combine does not exist! (' . $ini_file . ')'); + return static::FAILURE; + } + $arr = parse_ini_file($ini_file); + if ($arr === false) { + $this->output->writeln('Cannot parse ini file'); + return static::FAILURE; + } + $target_ini = array_merge($target_ini, $arr); + } + // 6. Confirm ini sets + if ($ini_set !== []) { + foreach ($ini_set as $item) { + $arr = parse_ini_string($item); + if ($arr === false) { + $this->output->writeln('--with-ini-set parse failed'); + return static::FAILURE; + } + $target_ini = array_merge($target_ini, $arr); + } + } + // 7. Generate ini injection parts + if (!empty($target_ini)) { + $ini_str = $this->encodeINI($target_ini); + logger()->debug('Injecting ini parts: ' . PHP_EOL . $ini_str); + $ini_part = "\xfd\xf6\x69\xe6"; + $ini_part .= pack('N', strlen($ini_str)); + $ini_part .= $ini_str; + } + // 8. Combine ! + $output = FileSystem::isRelativePath($output) ? (WORKING_DIR . '/' . $output) : $output; + $file_target = file_get_contents($micro_file) . $ini_part . file_get_contents($file); + $result = file_put_contents($output, $file_target); + if ($result === false) { + $this->output->writeln('Combine failed.'); + return static::FAILURE; + } + // 9. chmod +x + chmod($output, 0755); + $this->output->writeln('Combine success! Binary file: ' . $output . ''); + return static::SUCCESS; + } + + private function encodeINI(array $array): string + { + $res = []; + foreach ($array as $key => $val) { + if (is_array($val)) { + $res[] = "[{$key}]"; + foreach ($val as $skey => $sval) { + $res[] = "{$skey}=" . (is_numeric($sval) ? $sval : '"' . $sval . '"'); + } + } else { + $res[] = "{$key}=" . (is_numeric($val) ? $val : '"' . $val . '"'); + } + } + return implode("\n", $res); + } +} diff --git a/src/SPC/command/dev/AllExtCommand.php b/src/SPC/command/dev/AllExtCommand.php new file mode 100644 index 00000000..196de39f --- /dev/null +++ b/src/SPC/command/dev/AllExtCommand.php @@ -0,0 +1,75 @@ +addArgument('extensions', InputArgument::OPTIONAL, 'List of extensions that will be displayed, comma separated'); + } + + /** + * @throws FileSystemException + * @throws WrongUsageException + * @throws RuntimeException + */ + public function handle(): int + { + $extensions = array_map('trim', array_filter(explode(',', $this->getArgument('extensions') ?? ''))); + + $style = new SymfonyStyle($this->input, $this->output); + $style->writeln($extensions ? 'Available extensions:' : 'Extensions:'); + + $data = []; + foreach (Config::getExts() as $extension => $details) { + if ($extensions !== [] && !\in_array($extension, $extensions, true)) { + continue; + } + + try { + [, $libraries, $not_included] = DependencyUtil::getExtLibsByDeps([$extension]); + } catch (WrongUsageException) { + $libraries = $not_included = []; + } + + $lib_suggests = Config::getExt($extension, 'lib-suggests', []); + $ext_suggests = Config::getExt($extension, 'ext-suggests', []); + + $data[] = [ + $extension, + implode(', ', $libraries), + implode(', ', $lib_suggests), + implode(',', $not_included), + implode(', ', $ext_suggests), + Config::getExt($extension, 'unix-only', false) ? 'true' : 'false', + ]; + } + + if ($data === []) { + $style->warning('Unknown extension selected: ' . implode(',', $extensions)); + } else { + table( + ['Extension', 'lib-depends', 'lib-suggests', 'ext-depends', 'ext-suggests', 'unix-only'], + $data + ); + } + + return static::SUCCESS; + } +} diff --git a/src/SPC/command/dev/PhpVerCommand.php b/src/SPC/command/dev/PhpVerCommand.php new file mode 100644 index 00000000..05af1267 --- /dev/null +++ b/src/SPC/command/dev/PhpVerCommand.php @@ -0,0 +1,41 @@ +no_motd = true; + parent::initialize($input, $output); + } + + public function handle(): int + { + // Find php from source/php-src + $file = SOURCE_PATH . '/php-src/main/php_version.h'; + if (!file_exists($file)) { + $this->output->writeln('PHP source not found, maybe you need to extract first ?'); + + return static::FAILURE; + } + + $result = preg_match('/#define PHP_VERSION "([^"]+)"/', file_get_contents($file), $match); + if ($result === false) { + $this->output->writeln('PHP source not found, maybe you need to extract first ?'); + + return static::FAILURE; + } + + $this->output->writeln('' . $match[1] . ''); + return static::SUCCESS; + } +} diff --git a/src/SPC/command/SortConfigCommand.php b/src/SPC/command/dev/SortConfigCommand.php similarity index 56% rename from src/SPC/command/SortConfigCommand.php rename to src/SPC/command/dev/SortConfigCommand.php index 3de55770..8eb7680a 100644 --- a/src/SPC/command/SortConfigCommand.php +++ b/src/SPC/command/dev/SortConfigCommand.php @@ -2,8 +2,9 @@ declare(strict_types=1); -namespace SPC\command; +namespace SPC\command\dev; +use SPC\command\BaseCommand; use SPC\exception\FileSystemException; use SPC\exception\ValidationException; use SPC\store\FileSystem; @@ -12,12 +13,12 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputArgument; /** - * 修改 config 后对其 kv 进行排序的操作 + * Modify config file: sort lib, ext, source by name. */ -#[AsCommand('sort-config', 'After config edited, sort it by alphabet')] +#[AsCommand('dev:sort-config', 'After config edited, sort it by alphabet', ['sort-config'], true)] class SortConfigCommand extends BaseCommand { - public function configure() + public function configure(): void { $this->addArgument('config-name', InputArgument::REQUIRED, 'Your config to be sorted, you can sort "lib", "source" and "ext".'); } @@ -33,25 +34,34 @@ class SortConfigCommand extends BaseCommand $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/lib.json'), true); ConfigValidator::validateLibs($file); ksort($file); - 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('Write file lib.json failed!'); + return static::FAILURE; + } break; case 'source': $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))); - 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('Write file source.json failed!'); + return static::FAILURE; + } break; case 'ext': $file = json_decode(FileSystem::readFile(ROOT_DIR . '/config/ext.json'), true); ConfigValidator::validateExts($file); ksort($file); - 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('Write file ext.json failed!'); + return static::FAILURE; + } break; default: $this->output->writeln("invalid config name: {$name}"); return 1; } $this->output->writeln('sort success'); - return 0; + return static::SUCCESS; } } diff --git a/src/SPC/doctor/AsCheckItem.php b/src/SPC/doctor/AsCheckItem.php index 6c20e38c..f64d914b 100644 --- a/src/SPC/doctor/AsCheckItem.php +++ b/src/SPC/doctor/AsCheckItem.php @@ -12,7 +12,7 @@ class AsCheckItem public function __construct( public string $item_name, public ?string $limit_os = null, - public int $level = 100 - ) { - } + public int $level = 100, + public bool $manual = false, + ) {} } diff --git a/src/SPC/doctor/AsFixItem.php b/src/SPC/doctor/AsFixItem.php index 92f5e35e..200bd1f7 100644 --- a/src/SPC/doctor/AsFixItem.php +++ b/src/SPC/doctor/AsFixItem.php @@ -4,10 +4,8 @@ declare(strict_types=1); namespace SPC\doctor; -#[\Attribute(\Attribute::TARGET_METHOD)] +#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] class AsFixItem { - public function __construct(public string $name) - { - } + public function __construct(public string $name) {} } diff --git a/src/SPC/doctor/CheckListHandler.php b/src/SPC/doctor/CheckListHandler.php index b2f53e45..bfeb39c4 100644 --- a/src/SPC/doctor/CheckListHandler.php +++ b/src/SPC/doctor/CheckListHandler.php @@ -7,126 +7,86 @@ 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 * @throws \ReflectionException - * @throws FileSystemException * @throws RuntimeException + * @throws FileSystemException */ - public function __construct(private InputInterface $input, private OutputInterface $output) + public function runChecks(bool $include_manual = false): array { - $this->loadCheckList(); + return $this->loadCheckList($include_manual); } /** * @throws RuntimeException */ - public function runCheck(int $fix_policy = FIX_POLICY_DIE): void + public function emitFix(OutputInterface $output, CheckResult $result): void { - foreach ($this->check_list as $item) { - if ($item->limit_os !== null && $item->limit_os !== PHP_OS_FAMILY) { - continue; - } - $this->output->write('Checking ' . $item->item_name . ' ... '); - $result = call_user_func($item->callback); - if ($result === null) { - $this->output->writeln('skipped'); - } elseif ($result instanceof CheckResult) { - if ($result->isOK()) { - $this->output->writeln('ok'); - continue; - } - // Failed - $this->output->writeln('' . $result->getMessage() . ''); - 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; - } - } + pcntl_signal(SIGINT, function () use ($output) { + $output->writeln('You cancelled fix'); + }); + + $fix_result = call_user_func($this->fix_map[$result->getFixItem()], ...$result->getFixParams()); + pcntl_signal(SIGINT, SIG_IGN); + + if ($fix_result) { + $output->writeln('Fix done'); + } else { + $output->writeln('Fix failed'); + throw new RuntimeException('Some check item are not fixed'); } } /** * Load Doctor check item list * + * @return array * @throws \ReflectionException * @throws RuntimeException * @throws FileSystemException */ - private function loadCheckList(): 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(AsCheckItem::class); - if (isset($attr[0])) { - /** @var AsCheckItem $instance */ - $instance = $attr[0]->newInstance(); - $instance->callback = [new $class(), $method->getName()]; - $this->check_list[] = $instance; - continue; - } - $attr = $method->getAttributes(AsFixItem::class); - if (isset($attr[0])) { - /** @var AsFixItem $instance */ - $instance = $attr[0]->newInstance(); - // Redundant fix item - if (isset($this->fix_map[$instance->name])) { - throw new RuntimeException('Redundant doctor fix item: ' . $instance->name); + foreach ($method->getAttributes() as $a) { + if (is_a($a->getName(), AsCheckItem::class, true)) { + /** @var AsCheckItem $instance */ + $instance = $a->newInstance(); + if (!$include_manual && $instance->manual) { + continue; + } + $instance->callback = [new $class(), $method->getName()]; + $this->check_list[] = $instance; + } elseif (is_a($a->getName(), AsFixItem::class, true)) { + /** @var AsFixItem $instance */ + $instance = $a->newInstance(); + // Redundant fix item + if (isset($this->fix_map[$instance->name])) { + throw new RuntimeException('Redundant doctor fix item: ' . $instance->name); + } + $this->fix_map[$instance->name] = [new $class(), $method->getName()]; } - $this->fix_map[$instance->name] = [new $class(), $method->getName()]; } } } - // sort check list by level - usort($this->check_list, fn ($a, $b) => $a->level > $b->level ? -1 : ($a->level == $b->level ? 0 : 1)); - } - private function emitFix(CheckResult $result) - { - pcntl_signal(SIGINT, function () { - $this->output->writeln('You cancelled fix'); - }); - $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('Fix done'); - } else { - $this->output->writeln('Fix failed'); - 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; } } diff --git a/src/SPC/doctor/CheckResult.php b/src/SPC/doctor/CheckResult.php index fef036e4..406460ec 100644 --- a/src/SPC/doctor/CheckResult.php +++ b/src/SPC/doctor/CheckResult.php @@ -6,21 +6,19 @@ namespace SPC\doctor; class CheckResult { - public function __construct(private string $message = '', private string $fix_item = '', private array $fix_params = []) - { - } + public function __construct(private readonly bool $ok, private readonly ?string $message = null, private string $fix_item = '', private array $fix_params = []) {} public static function fail(string $message, string $fix_item = '', array $fix_params = []): CheckResult { - return new static($message, $fix_item, $fix_params); + return new static(false, $message, $fix_item, $fix_params); } - public static function ok(): CheckResult + public static function ok(?string $message = null): CheckResult { - return new static(); + return new static(true, $message); } - public function getMessage(): string + public function getMessage(): ?string { return $this->message; } @@ -37,10 +35,10 @@ class CheckResult public function isOK(): bool { - return empty($this->message); + return $this->ok; } - public function setFixItem(string $fix_item = '', array $fix_params = []) + public function setFixItem(string $fix_item = '', array $fix_params = []): void { $this->fix_item = $fix_item; $this->fix_params = $fix_params; diff --git a/src/SPC/doctor/item/BSDToolCheckList.php b/src/SPC/doctor/item/BSDToolCheckList.php new file mode 100644 index 00000000..7f28e57e --- /dev/null +++ b/src/SPC/doctor/item/BSDToolCheckList.php @@ -0,0 +1,66 @@ +findCommand($cmd) === null) { + $missing[] = $cmd; + } + } + if (!empty($missing)) { + return CheckResult::fail('missing system commands: ' . implode(', ', $missing), 'build-tools-bsd', [$missing]); + } + return CheckResult::ok(); + } + + #[AsFixItem('build-tools-bsd')] + public function fixBuildTools(array $missing): bool + { + if (get_current_user() !== 'root') { + $prefix = 'sudo '; + logger()->warning('Current user is not root, using sudo for running command'); + } else { + $prefix = ''; + } + try { + shell(true)->exec("ASSUME_ALWAYS_YES=yes {$prefix} pkg install -y " . implode(' ', $missing)); + } catch (RuntimeException) { + return false; + } + return true; + } +} diff --git a/src/SPC/doctor/item/LinuxMuslCheck.php b/src/SPC/doctor/item/LinuxMuslCheck.php index c9cb956b..c9639ff4 100644 --- a/src/SPC/doctor/item/LinuxMuslCheck.php +++ b/src/SPC/doctor/item/LinuxMuslCheck.php @@ -8,42 +8,105 @@ use SPC\builder\linux\SystemUtil; use SPC\doctor\AsCheckItem; use SPC\doctor\AsFixItem; use SPC\doctor\CheckResult; +use SPC\exception\DownloaderException; +use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; +use SPC\exception\WrongUsageException; +use SPC\store\Downloader; +use SPC\store\FileSystem; class LinuxMuslCheck { - #[AsCheckItem('if musl-libc is installed', limit_os: 'Linux')] - public function checkMusl(): ?CheckResult + /** @noinspection PhpUnused */ + #[AsCheckItem('if musl-wrapper is installed', limit_os: 'Linux', level: 800)] + public function checkMusl(): CheckResult { - $file = '/lib/ld-musl-x86_64.so.1'; - $result = null; - if (file_exists($file)) { + if (SystemUtil::isMuslDist()) { + return CheckResult::ok('musl-based distro, skipped'); + } + + $musl_wrapper_lib = sprintf('/lib/ld-musl-%s.so.1', php_uname('m')); + if (file_exists($musl_wrapper_lib) && file_exists('/usr/local/musl/lib/libc.a')) { return CheckResult::ok(); } - - // 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]), - default => CheckResult::fail('musl-libc is not installed on your system'), - }; + return CheckResult::fail('musl-wrapper is not installed on your system', 'fix-musl-wrapper'); } - #[AsFixItem('fix-musl')] - public function fixMusl(array $distro): bool + #[AsCheckItem('if musl-cross-make is installed', limit_os: 'Linux', level: 799)] + public function checkMuslCrossMake(): CheckResult { - $install_cmd = match ($distro['dist']) { - 'ubuntu', 'debian' => 'apt 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'), - }; - $prefix = ''; - if (get_current_user() !== 'root') { - $prefix = 'sudo '; - logger()->warning('Current user is not root, using sudo for running command'); + if (SystemUtil::isMuslDist()) { + return CheckResult::ok('musl-based distro, skipped'); } + $arch = arch2gnu(php_uname('m')); + $cross_compile_lib = "/usr/local/musl/{$arch}-linux-musl/lib/libc.a"; + $cross_compile_gcc = "/usr/local/musl/bin/{$arch}-linux-musl-gcc"; + if (file_exists($cross_compile_lib) && file_exists($cross_compile_gcc)) { + return CheckResult::ok(); + } + return CheckResult::fail('musl-cross-make is not installed on your system', 'fix-musl-cross-make'); + } + + /** @noinspection PhpUnused */ + /** + * @throws DownloaderException + * @throws FileSystemException + */ + #[AsFixItem('fix-musl-wrapper')] + public function fixMusl(): bool + { try { - shell(true)->exec($prefix . $install_cmd); + $prefix = ''; + if (get_current_user() !== 'root') { + $prefix = 'sudo '; + logger()->warning('Current user is not root, using sudo for running command'); + } + // The hardcoded version here is to be consistent with the version compiled by `musl-cross-toolchain`. + $musl_version_name = 'musl-1.2.4'; + $musl_source = [ + 'type' => 'url', + 'url' => "https://musl.libc.org/releases/{$musl_version_name}.tar.gz", + ]; + logger()->info('Downloading ' . $musl_source['url']); + Downloader::downloadSource($musl_version_name, $musl_source); + FileSystem::extractSource($musl_version_name, DOWNLOAD_PATH . "/{$musl_version_name}.tar.gz"); + logger()->info('Installing musl wrapper'); + shell()->cd(SOURCE_PATH . "/{$musl_version_name}") + ->exec('./configure --disable-gcc-wrapper') + ->exec('make -j') + ->exec("{$prefix}make install"); + // TODO: add path using putenv instead of editing /etc/profile + return true; + } catch (RuntimeException) { + return false; + } + } + + /** @noinspection PhpUnused */ + /** + * @throws DownloaderException + * @throws FileSystemException + * @throws WrongUsageException + */ + #[AsFixItem('fix-musl-cross-make')] + public function fixMuslCrossMake(): bool + { + try { + $prefix = ''; + if (get_current_user() !== 'root') { + $prefix = 'sudo '; + logger()->warning('Current user is not root, using sudo for running command'); + } + $arch = arch2gnu(php_uname('m')); + $musl_compile_source = [ + 'type' => 'url', + 'url' => "https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/{$arch}-musl-toolchain.tgz", + ]; + logger()->info('Downloading ' . $musl_compile_source['url']); + Downloader::downloadSource('musl-compile', $musl_compile_source); + logger()->info('Extracting musl-cross'); + FileSystem::extractSource('musl-compile', DOWNLOAD_PATH . "/{$arch}-musl-toolchain.tgz"); + shell()->exec($prefix . 'cp -rf ' . SOURCE_PATH . '/musl-compile/* /usr/local/musl'); return true; } catch (RuntimeException) { return false; diff --git a/src/SPC/doctor/item/LinuxToolCheckList.php b/src/SPC/doctor/item/LinuxToolCheckList.php new file mode 100644 index 00000000..ee9394f3 --- /dev/null +++ b/src/SPC/doctor/item/LinuxToolCheckList.php @@ -0,0 +1,115 @@ + self::TOOLS_ALPINE, + 'almalinux' => self::TOOLS_RHEL, + 'rhel' => self::TOOLS_RHEL, + default => self::TOOLS_DEBIAN, + }; + $missing = []; + foreach ($required as $cmd) { + if ($this->findCommand($cmd) === null) { + $missing[] = $cmd; + } + } + if (!empty($missing)) { + return match ($distro['dist']) { + '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'), + }; + } + return CheckResult::ok(); + } + + /** @noinspection PhpUnused */ + #[AsCheckItem('if necessary packages are installed', limit_os: 'Linux')] + public function checkSystemOSPackages(): ?CheckResult + { + $distro = SystemUtil::getOSRelease(); + if ($distro['dist'] === 'alpine') { + // check linux-headers installation + if (!file_exists('/usr/include/linux/mman.h')) { + return CheckResult::fail('linux-headers not installed on your system', 'install-linux-tools', [$distro, ['linux-headers']]); + } + } + return CheckResult::ok(); + } + + /** + * @throws RuntimeException + * @noinspection PhpUnused + */ + #[AsFixItem('install-linux-tools')] + public function fixBuildTools(array $distro, array $missing): bool + { + $install_cmd = match ($distro['dist']) { + 'ubuntu', 'debian' => 'apt-get install -y', + 'alpine' => 'apk add', + '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') { + $prefix = 'sudo '; + logger()->warning('Current user is not root, using sudo for running command'); + } + try { + $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; + } + return true; + } +} diff --git a/src/SPC/doctor/item/MacOSToolCheckList.php b/src/SPC/doctor/item/MacOSToolCheckList.php index cf564863..df1e6ed4 100644 --- a/src/SPC/doctor/item/MacOSToolCheckList.php +++ b/src/SPC/doctor/item/MacOSToolCheckList.php @@ -29,7 +29,6 @@ class MacOSToolCheckList 'xz', 'gzip', 'bzip2', - 'gotop', 'cmake', ]; @@ -74,7 +73,7 @@ class MacOSToolCheckList { foreach ($missing as $cmd) { try { - shell(true)->exec('brew install ' . escapeshellarg($cmd)); + shell(true)->exec('brew install --formula ' . escapeshellarg($cmd)); } catch (RuntimeException) { return false; } diff --git a/src/SPC/doctor/item/OSCheckList.php b/src/SPC/doctor/item/OSCheckList.php index 1bab92cd..ad6b2551 100644 --- a/src/SPC/doctor/item/OSCheckList.php +++ b/src/SPC/doctor/item/OSCheckList.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace SPC\doctor\item; +use SPC\builder\linux\SystemUtil; use SPC\builder\traits\UnixSystemUtilTrait; use SPC\doctor\AsCheckItem; use SPC\doctor\CheckResult; @@ -12,12 +13,13 @@ class OSCheckList { use UnixSystemUtilTrait; - #[AsCheckItem('if current os are supported', level: 999)] + #[AsCheckItem('if current OS are supported', level: 999)] public function checkOS(): ?CheckResult { - if (!in_array(PHP_OS_FAMILY, ['Darwin', 'Linux'])) { + if (!in_array(PHP_OS_FAMILY, ['Darwin', 'Linux', 'BSD'])) { return CheckResult::fail('Current OS is not supported'); } - return CheckResult::ok(); + $distro = PHP_OS_FAMILY === 'Linux' ? (' ' . SystemUtil::getOSRelease()['dist']) : ''; + return CheckResult::ok(PHP_OS_FAMILY . ' ' . php_uname('m') . $distro . ', supported'); } } diff --git a/src/SPC/exception/DownloaderException.php b/src/SPC/exception/DownloaderException.php index 41a1d423..a2faec9d 100644 --- a/src/SPC/exception/DownloaderException.php +++ b/src/SPC/exception/DownloaderException.php @@ -4,6 +4,4 @@ declare(strict_types=1); namespace SPC\exception; -class DownloaderException extends \Exception -{ -} +class DownloaderException extends \Exception {} diff --git a/src/SPC/exception/ExceptionHandler.php b/src/SPC/exception/ExceptionHandler.php index 40773da3..544575dc 100644 --- a/src/SPC/exception/ExceptionHandler.php +++ b/src/SPC/exception/ExceptionHandler.php @@ -6,9 +6,9 @@ namespace SPC\exception; class ExceptionHandler { - protected $whoops; + protected mixed $whoops = null; - private static $obj; + private static ?ExceptionHandler $obj = null; private function __construct() { @@ -32,14 +32,6 @@ class ExceptionHandler return self::$obj; } - public function getWhoops() - { - return $this->whoops; - } - - /** - * 处理异常 - */ public function handle(\Throwable $e): void { if (is_null($this->whoops)) { @@ -47,7 +39,8 @@ class ExceptionHandler logger()->error($e->getTraceAsString()); return; } - $this->whoops->handleException($e); + + logger()->critical('You can report this exception to static-php-cli GitHub repo.'); } } diff --git a/src/SPC/exception/FileSystemException.php b/src/SPC/exception/FileSystemException.php index ca72968e..d27e22af 100644 --- a/src/SPC/exception/FileSystemException.php +++ b/src/SPC/exception/FileSystemException.php @@ -4,6 +4,4 @@ declare(strict_types=1); namespace SPC\exception; -class FileSystemException extends \Exception -{ -} +class FileSystemException extends \Exception {} diff --git a/src/SPC/exception/InvalidArgumentException.php b/src/SPC/exception/InvalidArgumentException.php index 360a89bc..16db5524 100644 --- a/src/SPC/exception/InvalidArgumentException.php +++ b/src/SPC/exception/InvalidArgumentException.php @@ -4,6 +4,4 @@ declare(strict_types=1); namespace SPC\exception; -class InvalidArgumentException extends \Exception -{ -} +class InvalidArgumentException extends \Exception {} diff --git a/src/SPC/exception/RuntimeException.php b/src/SPC/exception/RuntimeException.php index d612e533..2d96acb9 100644 --- a/src/SPC/exception/RuntimeException.php +++ b/src/SPC/exception/RuntimeException.php @@ -4,6 +4,4 @@ declare(strict_types=1); namespace SPC\exception; -class RuntimeException extends \Exception -{ -} +class RuntimeException extends \Exception {} diff --git a/src/SPC/exception/ValidationException.php b/src/SPC/exception/ValidationException.php index 83ff86b7..08e7b4b9 100644 --- a/src/SPC/exception/ValidationException.php +++ b/src/SPC/exception/ValidationException.php @@ -4,6 +4,4 @@ declare(strict_types=1); namespace SPC\exception; -class ValidationException extends \Exception -{ -} +class ValidationException extends \Exception {} diff --git a/src/SPC/exception/WrongUsageException.php b/src/SPC/exception/WrongUsageException.php index c03000da..fa23c1f8 100644 --- a/src/SPC/exception/WrongUsageException.php +++ b/src/SPC/exception/WrongUsageException.php @@ -4,6 +4,4 @@ declare(strict_types=1); namespace SPC\exception; -class WrongUsageException extends \Exception -{ -} +class WrongUsageException extends \Exception {} diff --git a/src/SPC/store/Config.php b/src/SPC/store/Config.php index 2f6e992b..01328be2 100644 --- a/src/SPC/store/Config.php +++ b/src/SPC/store/Config.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace SPC\store; use SPC\exception\FileSystemException; -use SPC\exception\RuntimeException; use SPC\exception\WrongUsageException; /** @@ -55,6 +54,7 @@ class Config 'Windows' => ['-windows', '-win', ''], 'Darwin' => ['-macos', '-unix', ''], 'Linux' => ['-linux', '-unix', ''], + 'BSD' => ['-freebsd', '-bsd', '-unix', ''], default => throw new WrongUsageException('OS ' . PHP_OS_FAMILY . ' is not supported'), }; foreach ($m_key as $v) { @@ -83,7 +83,6 @@ class Config /** * @throws FileSystemException - * @throws RuntimeException * @throws WrongUsageException */ public static function getExt(string $name, ?string $key = null, mixed $default = null) @@ -100,6 +99,7 @@ class Config 'Windows' => ['-windows', '-win', ''], 'Darwin' => ['-macos', '-unix', ''], 'Linux' => ['-linux', '-unix', ''], + 'BSD' => ['-freebsd', '-bsd', '-unix', ''], default => throw new WrongUsageException('OS ' . PHP_OS_FAMILY . ' is not supported'), }; foreach ($m_key as $v) { @@ -126,6 +126,9 @@ class Config return self::$ext; } + /** + * @throws FileSystemException + */ public static function getSources(): array { if (self::$source === null) { diff --git a/src/SPC/store/CurlHook.php b/src/SPC/store/CurlHook.php index 2cf7b15b..ce27cf3d 100644 --- a/src/SPC/store/CurlHook.php +++ b/src/SPC/store/CurlHook.php @@ -13,7 +13,7 @@ class CurlHook * @param string $url 修改的链接 * @param array $headers 修改的 headers */ - public static function setupGithubToken(string &$method, string &$url, array &$headers): void + public static function setupGithubToken(string $method, string $url, array &$headers): void { if (!getenv('GITHUB_TOKEN')) { return; diff --git a/src/SPC/store/Downloader.php b/src/SPC/store/Downloader.php index 39a22494..affeac88 100644 --- a/src/SPC/store/Downloader.php +++ b/src/SPC/store/Downloader.php @@ -4,23 +4,22 @@ declare(strict_types=1); namespace SPC\store; -use JetBrains\PhpStorm\ArrayShape; use SPC\exception\DownloaderException; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\source\CustomSourceBase; /** - * 资源下载器 + * Source Downloader. */ class Downloader { /** - * 获取 BitBucket 仓库的最新 Tag + * Get latest version from BitBucket tag * - * @param string $name 资源名称 - * @param array $source 资源的元信息,包含字段 repo - * @return array 返回下载 url 链接和文件名 + * @param string $name source name + * @param array $source source meta info: [repo] + * @return array [url, filename] * @throws DownloaderException */ public static function getLatestBitbucketTag(string $name, array $source): array @@ -49,10 +48,13 @@ class Downloader } /** - * 获取 GitHub 最新的打包地址和文件名 + * Get latest version from GitHub tarball + * + * @param string $name source name + * @param array $source source meta info: [repo] + * @param string $type type of tarball, default is 'releases' + * @return array [url, filename] * - * @param string $name 包名称 - * @param array $source 源信息 * @throws DownloaderException */ public static function getLatestGithubTarball(string $name, array $source, string $type = 'releases'): array @@ -82,10 +84,11 @@ class Downloader } /** - * 获取 GitHub 最新的 Release 下载信息 + * Get latest version from GitHub release (uploaded archive) * - * @param string $name 资源名 - * @param array $source 资源的元信息,包含字段 repo、match + * @param string $name source name + * @param array $source source meta info: [repo, match] + * @return array [url, filename] * @throws DownloaderException */ public static function getLatestGithubRelease(string $name, array $source): array @@ -96,12 +99,18 @@ class Downloader hooks: [[CurlHook::class, 'setupGithubToken']], ), true); $url = null; - foreach ($data[0]['assets'] as $asset) { - if (preg_match('|' . $source['match'] . '|', $asset['name'])) { - $url = $asset['browser_download_url']; - break; + foreach ($data as $release) { + if ($release['prerelease'] === true) { + continue; + } + foreach ($release['assets'] as $asset) { + if (preg_match('|' . $source['match'] . '|', $asset['name'])) { + $url = $asset['browser_download_url']; + break 2; + } } } + if (!$url) { throw new DownloaderException("failed to find {$name} source"); } @@ -111,10 +120,11 @@ class Downloader } /** - * 获取文件列表的资源链接和名称 + * Get latest version from file list (regex based crawler) * - * @param string $name 资源名称 - * @param array $source 资源元信息,包含 url、regex + * @param string $name source name + * @param array $source source meta info: [url, regex] + * @return array [url, filename] * @throws DownloaderException */ public static function getFromFileList(string $name, array $source): array @@ -149,6 +159,8 @@ class Downloader } /** + * Just download file using system curl command, and lock it + * * @throws DownloaderException * @throws RuntimeException * @throws FileSystemException @@ -168,6 +180,11 @@ class Downloader self::lockSource($name, ['source_type' => 'archive', 'filename' => $filename, 'move_path' => $move_path]); } + /** + * Try to lock source. + * + * @throws FileSystemException + */ public static function lockSource(string $name, array $data): void { if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) { @@ -180,40 +197,11 @@ class Downloader } /** - * 通过链接下载资源到本地并解压 + * Download git source, and lock it. * - * @param string $name 资源名称 - * @param string $url 下载链接 - * @param string $filename 下载到下载目录的目标文件名称,例如 xz.tar.gz - * @param null|string $path 如果指定了此参数,则会移动该资源目录到目标目录 * @throws FileSystemException * @throws RuntimeException - * @throws DownloaderException */ - public static function downloadUrl(string $name, string $url, string $filename, ?string $path = null): void - { - if (!file_exists(DOWNLOAD_PATH . "/{$filename}")) { - logger()->debug("downloading {$url}"); - self::curlDown(url: $url, path: DOWNLOAD_PATH . "/{$filename}"); - } else { - logger()->notice("{$filename} already exists"); - } - FileSystem::extractSource($name, DOWNLOAD_PATH . "/{$filename}"); - if ($path) { - $path = FileSystem::convertPath(SOURCE_PATH . "/{$path}"); - $src_path = FileSystem::convertPath(SOURCE_PATH . "/{$name}"); - switch (PHP_OS_FAMILY) { - case 'Windows': - f_passthru('move "' . $src_path . '" "' . $path . '"'); - break; - case 'Linux': - case 'Darwin': - f_passthru('mv "' . $src_path . '" "' . $path . '"'); - break; - } - } - } - public static function downloadGit(string $name, string $url, string $branch, ?string $move_path = null): void { $download_path = DOWNLOAD_PATH . "/{$name}"; @@ -257,20 +245,29 @@ class Downloader } /** - * 拉取资源 + * Download source by name and meta. * - * @param string $name 资源名称 - * @param null|array $source 资源参数,包含 type、path、rev、url、filename、regex、license + * @param string $name source name + * @param null|array $source source meta info: [type, path, rev, url, filename, regex, license] * @throws DownloaderException * @throws FileSystemException * @throws RuntimeException */ - public static function downloadSource(string $name, ?array $source = null): void + public static function downloadSource(string $name, ?array $source = null, bool $force = false): void { if ($source === null) { $source = Config::getSource($name); } + if ($source === null) { + logger()->warning('Source {name} unknown. Skipping.', ['name' => $name]); + return; + } + + if (!is_dir(DOWNLOAD_PATH)) { + FileSystem::createDir(DOWNLOAD_PATH); + } + // load lock file if (!file_exists(DOWNLOAD_PATH . '/.lock.json')) { $lock = []; @@ -278,7 +275,7 @@ class Downloader $lock = json_decode(FileSystem::readFile(DOWNLOAD_PATH . '/.lock.json'), true) ?? []; } // If lock file exists, skip downloading - if (isset($lock[$name])) { + if (isset($lock[$name]) && !$force) { if ($lock[$name]['source_type'] === 'archive' && file_exists(DOWNLOAD_PATH . '/' . $lock[$name]['filename'])) { logger()->notice("source [{$name}] already downloaded: " . $lock[$name]['filename']); return; @@ -291,35 +288,35 @@ class Downloader try { switch ($source['type']) { - case 'bitbuckettag': // 从 BitBucket 的 Tag 拉取 + case 'bitbuckettag': // BitBucket Tag [$url, $filename] = self::getLatestBitbucketTag($name, $source); self::downloadFile($name, $url, $filename, $source['path'] ?? null); break; - case 'ghtar': // 从 GitHub 的 TarBall 拉取 + case 'ghtar': // GitHub Release (tar) [$url, $filename] = self::getLatestGithubTarball($name, $source); self::downloadFile($name, $url, $filename, $source['path'] ?? null); break; - case 'ghtagtar': // 根据 GitHub 的 Tag 拉取相应版本的 Tar + case 'ghtagtar': // GitHub Tag (tar) [$url, $filename] = self::getLatestGithubTarball($name, $source, 'tags'); self::downloadFile($name, $url, $filename, $source['path'] ?? null); break; - case 'ghrel': // 通过 GitHub Release 来拉取 + case 'ghrel': // GitHub Release (uploaded) [$url, $filename] = self::getLatestGithubRelease($name, $source); self::downloadFile($name, $url, $filename, $source['path'] ?? null); break; - case 'filelist': // 通过网站提供的 filelist 使用正则提取后拉取 + case 'filelist': // Basic File List (regex based crawler) [$url, $filename] = self::getFromFileList($name, $source); self::downloadFile($name, $url, $filename, $source['path'] ?? null); break; - case 'url': // 通过直链拉取 + case 'url': // Direct download URL $url = $source['url']; $filename = $source['filename'] ?? basename($source['url']); self::downloadFile($name, $url, $filename, $source['path'] ?? null); break; - case 'git': // 通过拉回 Git 仓库的形式拉取 + case 'git': // Git repo self::downloadGit($name, $source['url'], $source['rev'], $source['path'] ?? null); break; - case 'custom': // 自定义,可能是通过复杂 API 形式获取的文件,需要手写 crawler + case 'custom': // Custom download method, like API-based download or other $classes = FileSystem::getClassesPsr4(ROOT_DIR . '/src/SPC/store/source', 'SPC\\store\\source'); foreach ($classes as $class) { if (is_a($class, CustomSourceBase::class, true) && $class::NAME === $name) { @@ -332,37 +329,18 @@ class Downloader throw new DownloaderException('unknown source type: ' . $source['type']); } } catch (RuntimeException $e) { - // 因为某些时候通过命令行下载的文件在失败后不会删除,这里检测到文件存在需要手动删一下 + // Because sometimes files downloaded through the command line are not automatically deleted after a failure. + // Here we need to manually delete the file if it is detected to exist. if (isset($filename) && file_exists(DOWNLOAD_PATH . '/' . $filename)) { logger()->warning('Deleting download file: ' . $filename); unlink(DOWNLOAD_PATH . '/' . $filename); } - throw $e; + throw new DownloaderException('Download failed! ' . $e->getMessage()); } } /** - * 获取 PHP x.y 的具体版本号,例如通过 8.1 来获取 8.1.10 - * - * @throws DownloaderException - */ - #[ArrayShape(['type' => 'string', 'path' => 'string', 'rev' => 'string', 'url' => 'string'])] - public static function getLatestPHPInfo(string $major_version): array - { - // 查找最新的小版本号 - $info = json_decode(self::curlExec(url: "https://www.php.net/releases/index.php?json&version={$major_version}"), true); - $version = $info['version']; - - // 从官网直接下载 - return [ - 'type' => 'url', - 'url' => "https://www.php.net/distributions/php-{$version}.tar.gz", - // 'url' => "https://mirrors.zhamao.xin/php/php-{$version}.tar.gz", - ]; - } - - /** - * 使用 curl 命令拉取元信息 + * Use curl command to get http response * * @throws DownloaderException */ @@ -408,7 +386,7 @@ class Downloader } /** - * 使用 curl 命令下载文件 + * Use curl to download sources from url * * @throws DownloaderException * @throws RuntimeException diff --git a/src/SPC/store/FileSystem.php b/src/SPC/store/FileSystem.php index 6c4b515a..0480ff14 100644 --- a/src/SPC/store/FileSystem.php +++ b/src/SPC/store/FileSystem.php @@ -55,23 +55,25 @@ class FileSystem /** * @throws FileSystemException */ - public static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): bool|int + public static function replaceFileStr(string $filename, mixed $search = null, mixed $replace = null): false|int { - logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename); - $file = self::readFile($filename); - switch ($replace_type) { - case REPLACE_FILE_STR: - default: - $file = str_replace($callback_or_search, $to_replace, $file); - break; - case REPLACE_FILE_PREG: - $file = preg_replace($callback_or_search, $to_replace, $file); - break; - case REPLACE_FILE_USER: - $file = $callback_or_search($file); - break; - } - return file_put_contents($filename, $file); + return self::replaceFile($filename, REPLACE_FILE_STR, $search, $replace); + } + + /** + * @throws FileSystemException + */ + public static function replaceFileRegex(string $filename, mixed $search = null, mixed $replace = null): false|int + { + return self::replaceFile($filename, REPLACE_FILE_PREG, $search, $replace); + } + + /** + * @throws FileSystemException + */ + public static function replaceFileUser(string $filename, mixed $callback = null): false|int + { + return self::replaceFile($filename, REPLACE_FILE_USER, $callback); } /** @@ -117,6 +119,9 @@ class FileSystem return null; } + /** + * @throws RuntimeException + */ public static function copyDir(string $from, string $to): void { $dst_path = FileSystem::convertPath($to); @@ -127,6 +132,7 @@ class FileSystem break; case 'Linux': case 'Darwin': + case 'BSD': f_passthru('cp -r "' . $src_path . '" "' . $dst_path . '"'); break; } @@ -146,10 +152,13 @@ class FileSystem if (self::$_extract_hook === []) { SourcePatcher::init(); } + if (!is_dir(SOURCE_PATH)) { + self::createDir(SOURCE_PATH); + } if ($move_path !== null) { $move_path = SOURCE_PATH . '/' . $move_path; } - logger()->info("extracting {$name} source"); + logger()->info("extracting {$name} source to " . ($move_path ?? SOURCE_PATH . "/{$name}") . ' ...'); try { $target = $move_path ?? (SOURCE_PATH . "/{$name}"); // Git source, just move @@ -158,8 +167,7 @@ class FileSystem self::emitSourceExtractHook($name); return; } - - if (PHP_OS_FAMILY === 'Darwin' || PHP_OS_FAMILY === 'Linux') { + if (in_array(PHP_OS_FAMILY, ['Darwin', 'Linux', 'BSD'])) { if (f_mkdir(directory: $target, recursive: true) !== true) { throw new FileSystemException('create ' . $name . 'source dir failed'); } @@ -251,14 +259,13 @@ class FileSystem /** * 递归或非递归扫描目录,可返回相对目录的文件列表或绝对目录的文件列表 * - * @param string $dir 目录 - * @param bool $recursive 是否递归扫描子目录 - * @param bool|string $relative 是否返回相对目录,如果为true则返回相对目录,如果为false则返回绝对目录 - * @param bool $include_dir 非递归模式下,是否包含目录 - * @return array|false + * @param string $dir 目录 + * @param bool $recursive 是否递归扫描子目录 + * @param bool|string $relative 是否返回相对目录,如果为true则返回相对目录,如果为false则返回绝对目录 + * @param bool $include_dir 非递归模式下,是否包含目录 * @since 2.5 */ - public static function scanDirFiles(string $dir, bool $recursive = true, bool|string $relative = false, bool $include_dir = false): bool|array + public static function scanDirFiles(string $dir, bool $recursive = true, bool|string $relative = false, bool $include_dir = false): array|false { $dir = self::convertPath($dir); // 不是目录不扫,直接 false 处理 @@ -320,28 +327,7 @@ class FileSystem foreach ($files as $v) { $pathinfo = pathinfo($v); if (($pathinfo['extension'] ?? '') == 'php') { - $path = rtrim($dir, '/') . '/' . rtrim($pathinfo['dirname'], './') . '/' . $pathinfo['basename']; - - // 过滤不包含类的文件 - $tokens = token_get_all(self::readFile($path)); - $found = false; - foreach ($tokens as $token) { - if (!is_array($token)) { - continue; - } - if ($token[0] === T_CLASS) { - $found = true; - break; - } - } - if (!$found) { - continue; - } - - if ($rule === null) { // 规则未设置回调时候,使用默认的识别过滤规则 - /*if (substr(file_get_contents($dir . '/' . $v), 6, 6) == '#plain') { - continue; - }*/ + if ($rule === null) { if (file_exists($dir . '/' . $pathinfo['basename'] . '.ignore')) { continue; } @@ -405,6 +391,9 @@ class FileSystem return rmdir($dir); } + /** + * @throws FileSystemException + */ public static function createDir(string $path): void { if (!is_dir($path) && !f_mkdir($path, 0755, true) && !is_dir($path)) { @@ -412,7 +401,11 @@ class FileSystem } } - public static function writeFile(string $path, $content, ...$args): bool|string|int + /** + * @param mixed ...$args Arguments passed to file_put_contents + * @throws FileSystemException + */ + public static function writeFile(string $path, mixed $content, ...$args): bool|int|string { $dir = pathinfo($path, PATHINFO_DIRNAME); if (!is_dir($dir) && !mkdir($dir, 0755, true)) { @@ -434,12 +427,47 @@ class FileSystem self::createDir($dir_name); } - public static function addSourceExtractHook(string $name, callable $callback) + public static function addSourceExtractHook(string $name, callable $callback): void { self::$_extract_hook[$name][] = $callback; } - private static function emitSourceExtractHook(string $name) + /** + * Check whether the path is a relative path (judging according to whether the first character is "/") + * + * @param string $path Path + */ + public static function isRelativePath(string $path): bool + { + if (DIRECTORY_SEPARATOR === '\\') { + return !(strlen($path) > 2 && ctype_alpha($path[0]) && $path[1] === ':'); + } + return strlen($path) > 0 && $path[0] !== '/'; + } + + /** + * @throws FileSystemException + */ + private static function replaceFile(string $filename, int $replace_type = REPLACE_FILE_STR, mixed $callback_or_search = null, mixed $to_replace = null): false|int + { + logger()->debug('Replacing file with type[' . $replace_type . ']: ' . $filename); + $file = self::readFile($filename); + switch ($replace_type) { + case REPLACE_FILE_STR: + default: + $file = str_replace($callback_or_search, $to_replace, $file); + break; + case REPLACE_FILE_PREG: + $file = preg_replace($callback_or_search, $to_replace, $file); + break; + case REPLACE_FILE_USER: + $file = $callback_or_search($file); + break; + } + return file_put_contents($filename, $file); + } + + private static function emitSourceExtractHook(string $name): void { foreach ((self::$_extract_hook[$name] ?? []) as $hook) { if ($hook() === true) { diff --git a/src/SPC/store/SourceExtractor.php b/src/SPC/store/SourceExtractor.php new file mode 100644 index 00000000..66a96c25 --- /dev/null +++ b/src/SPC/store/SourceExtractor.php @@ -0,0 +1,71 @@ + $item) { + if (Config::getSource($source) === null) { + throw new WrongUsageException("Source [{$source}] does not exist, please check the name and correct it !"); + } + if (!isset($lock[$source])) { + throw new WrongUsageException('Source [' . $source . '] not downloaded or not locked, you should download it first !'); + } + + // check source dir exist + $check = $lock[$source]['move_path'] === null ? (SOURCE_PATH . '/' . $source) : (SOURCE_PATH . '/' . $lock[$source]['move_path']); + if (!is_dir($check)) { + logger()->debug('Extracting source [' . $source . '] to ' . $check . ' ...'); + FileSystem::extractSource($source, DOWNLOAD_PATH . '/' . ($lock[$source]['filename'] ?? $lock[$source]['dirname']), $lock[$source]['move_path']); + } else { + logger()->debug('Source [' . $source . '] already extracted in ' . $check . ', skip !'); + } + } + } +} diff --git a/src/SPC/store/SourcePatcher.php b/src/SPC/store/SourcePatcher.php index aba47b92..b0edc303 100644 --- a/src/SPC/store/SourcePatcher.php +++ b/src/SPC/store/SourcePatcher.php @@ -6,139 +6,53 @@ namespace SPC\store; use SPC\builder\BuilderBase; use SPC\builder\linux\LinuxBuilder; -use SPC\builder\linux\SystemUtil; -use SPC\builder\macos\MacOSBuilder; use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; -use SPC\util\Util; class SourcePatcher { - public static function init() + public static function init(): void { - FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']); + // FileSystem::addSourceExtractHook('swow', [SourcePatcher::class, 'patchSwow']); FileSystem::addSourceExtractHook('micro', [SourcePatcher::class, 'patchMicro']); FileSystem::addSourceExtractHook('openssl', [SourcePatcher::class, 'patchOpenssl11Darwin']); } - public static function patchPHPBuildconf(BuilderBase $builder): void + /** + * Source patcher runner before buildconf + * + * @param BuilderBase $builder Builder + */ + public static function patchBeforeBuildconf(BuilderBase $builder): void { - if ($builder->getExt('curl')) { - logger()->info('patching before-configure for curl checks'); - $file1 = "AC_DEFUN([PHP_CHECK_LIBRARY], [\n $3\n])"; - $files = FileSystem::readFile(SOURCE_PATH . '/php-src/ext/curl/config.m4'); - $file2 = 'AC_DEFUN([PHP_CHECK_LIBRARY], [ - save_old_LDFLAGS=$LDFLAGS - ac_stuff="$5" - - save_ext_shared=$ext_shared - ext_shared=yes - PHP_EVAL_LIBLINE([$]ac_stuff, LDFLAGS) - AC_CHECK_LIB([$1],[$2],[ - LDFLAGS=$save_old_LDFLAGS - ext_shared=$save_ext_shared - $3 - ],[ - LDFLAGS=$save_old_LDFLAGS - ext_shared=$save_ext_shared - unset ac_cv_lib_$1[]_$2 - $4 - ])dnl -])'; - file_put_contents(SOURCE_PATH . '/php-src/ext/curl/config.m4', $file1 . "\n" . $files . "\n" . $file2); - } - - // if ($builder->getExt('pdo_sqlite')) { - // FileSystem::replaceFile() - // } - } - - public static function patchSwow(): bool - { - if (Util::getPHPVersionID() >= 80000) { - if (PHP_OS_FAMILY === 'Windows') { - f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && mklink /D swow swow-src\ext'); - } else { - f_passthru('cd ' . SOURCE_PATH . '/php-src/ext && ln -s swow-src/ext swow'); + foreach ($builder->getExts() as $ext) { + if ($ext->patchBeforeBuildconf() === true) { + logger()->info('Extension [' . $ext->getName() . '] patched before buildconf'); } - return true; } - return false; } - public static function patchPHPConfigure(BuilderBase $builder): void + /** + * Source patcher runner before configure + * + * @param BuilderBase $builder Builder + * @throws FileSystemException + */ + public static function patchBeforeConfigure(BuilderBase $builder): void { - $frameworks = $builder instanceof MacOSBuilder ? ' ' . $builder->getFrameworks(true) . ' ' : ''; - $patch = []; - if ($curl = $builder->getExt('curl')) { - $patch[] = ['curl check', '/-lcurl/', $curl->getLibFilesString() . $frameworks]; - } - if ($bzip2 = $builder->getExt('bz2')) { - $patch[] = ['bzip2 check', '/-lbz2/', $bzip2->getLibFilesString() . $frameworks]; - } - if ($pdo_sqlite = $builder->getExt('pdo_sqlite')) { - $patch[] = ['pdo_sqlite linking', '/sqlite3_column_table_name=yes/', 'sqlite3_column_table_name=no']; - } - if ($event = $builder->getExt('event')) { - $patch[] = ['event check', '/-levent_openssl/', $event->getLibFilesString()]; - } - if ($readline = $builder->getExt('readline')) { - $patch[] = ['readline patch', '/-lncurses/', $readline->getLibFilesString()]; - } - if ($ssh2 = $builder->getExt('ssh2')) { - $patch[] = ['ssh2 patch', '/-lssh2/', $ssh2->getLibFilesString()]; - } - $patch[] = ['disable capstone', '/have_capstone="yes"/', 'have_capstone="no"']; - foreach ($patch as $item) { - logger()->info('Patching configure: ' . $item[0]); - FileSystem::replaceFile(SOURCE_PATH . '/php-src/configure', REPLACE_FILE_PREG, $item[1], $item[2]); + foreach ($builder->getExts() as $ext) { + if ($ext->patchBeforeConfigure() === true) { + logger()->info('Extension [' . $ext->getName() . '] patched before configure'); + } } + // patch capstone + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/configure', '/have_capstone="yes"/', 'have_capstone="no"'); } - public static function patchUnixLibpng(): void - { - FileSystem::replaceFile( - SOURCE_PATH . '/libpng/configure', - REPLACE_FILE_STR, - '-lz', - BUILD_LIB_PATH . '/libz.a' - ); - if (SystemUtil::getOSRelease()['dist'] === 'alpine') { - FileSystem::replaceFile( - SOURCE_PATH . '/libpng/configure', - REPLACE_FILE_STR, - '-lm', - '/usr/lib/libm.a' - ); - } - } - - public static function patchUnixSsh2(): void - { - FileSystem::replaceFile( - SOURCE_PATH . '/php-src/configure', - REPLACE_FILE_STR, - '-lssh2', - BUILD_LIB_PATH . '/libssh2.a' - ); - } - - public static function patchCurlMacOS(): void - { - FileSystem::replaceFile( - SOURCE_PATH . '/curl/CMakeLists.txt', - REPLACE_FILE_PREG, - '/NOT COREFOUNDATION_FRAMEWORK/m', - 'FALSE' - ); - FileSystem::replaceFile( - SOURCE_PATH . '/curl/CMakeLists.txt', - REPLACE_FILE_PREG, - '/NOT SYSTEMCONFIGURATION_FRAMEWORK/m', - 'FALSE' - ); - } - + /** + * @throws RuntimeException + * @throws FileSystemException + */ public static function patchMicro(?array $list = null, bool $reverse = false): bool { if (!file_exists(SOURCE_PATH . '/php-src/sapi/micro/php_micro.c')) { @@ -156,7 +70,7 @@ class SourcePatcher if ($major_ver === '74') { return false; } - $check = !defined('DEBUG_MODE') ? ' -q' : ''; + // $check = !defined('DEBUG_MODE') ? ' -q' : ''; // f_passthru('cd ' . SOURCE_PATH . '/php-src && git checkout' . $check . ' HEAD'); $default = [ @@ -176,7 +90,7 @@ class SourcePatcher } $patch_list = $list ?? $default; $patches = []; - $serial = ['80', '81', '82']; + $serial = ['80', '81', '82', '83']; foreach ($patch_list as $patchName) { if (file_exists(SOURCE_PATH . "/php-src/sapi/micro/patches/{$patchName}.patch")) { $patches[] = "sapi/micro/patches/{$patchName}.patch"; @@ -203,33 +117,121 @@ class SourcePatcher return true; } + /** + * Use existing patch file for patching + * + * @param string $patch_name Patch file name in src/globals/patch/ + * @param string $cwd Working directory for patch command + * @param bool $reverse Reverse patches (default: False) + * @throws RuntimeException + */ + public static function patchFile(string $patch_name, string $cwd, bool $reverse = false): bool + { + if (!file_exists(ROOT_DIR . "/src/globals/patch/{$patch_name}")) { + return false; + } + + $patch_file = ROOT_DIR . "/src/globals/patch/{$patch_name}"; + $patch_str = str_replace('/', DIRECTORY_SEPARATOR, $patch_file); + + f_passthru( + 'cd ' . $cwd . ' && ' . + (PHP_OS_FAMILY === 'Windows' ? 'type' : 'cat') . ' ' . $patch_str . ' | patch -p1 ' . ($reverse ? '-R' : '') + ); + return true; + } + + /** + * @throws FileSystemException + */ public static function patchOpenssl11Darwin(): bool { if (PHP_OS_FAMILY === 'Darwin' && !file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && file_exists(SOURCE_PATH . '/openssl/test/v3ext.c')) { - FileSystem::replaceFile( - SOURCE_PATH . '/openssl/test/v3ext.c', - REPLACE_FILE_STR, - '#include ', - '#include ' . PHP_EOL . '#include ' - ); + FileSystem::replaceFileStr(SOURCE_PATH . '/openssl/test/v3ext.c', '#include ', '#include ' . PHP_EOL . '#include '); return true; } return false; } - public static function patchPHPAfterConfigure(BuilderBase $param) + /** + * @throws FileSystemException + */ + public static function patchBeforeMake(BuilderBase $builder): void { - if ($param instanceof LinuxBuilder) { - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', ''); - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', ''); + // Try to fix debian environment build lack HAVE_STRLCAT problem + if ($builder instanceof LinuxBuilder) { + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCPY 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCAT 1$/m', ''); } - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_OPENPTY 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_OPENPTY 1$/m', ''); - // patch openssl3 with php8.0 bug - if (file_exists(SOURCE_PATH . '/openssl/VERSION.dat') && Util::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); + FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/Makefile', 'install-micro', ''); + + // call extension patch before make + foreach ($builder->getExts() as $ext) { + if ($ext->patchBeforeMake() === true) { + logger()->info('Extension [' . $ext->getName() . '] patched before make'); + } } } + + /** + * @throws FileSystemException + */ + public static function patchHardcodedINI(array $ini = []): bool + { + $cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c'; + $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[] ='; + $patch_str = ''; + foreach ($ini as $key => $value) { + $patch_str .= "\"{$key}={$value}\\n\"\n"; + } + $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) || 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; + } + + // 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; + } + + public static function unpatchHardcodedINI(): bool + { + $cli_c = SOURCE_PATH . '/php-src/sapi/cli/php_cli.c'; + $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'; + 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; + } } diff --git a/src/SPC/store/source/PhpSource.php b/src/SPC/store/source/PhpSource.php index 44998e4e..30e583a8 100644 --- a/src/SPC/store/source/PhpSource.php +++ b/src/SPC/store/source/PhpSource.php @@ -6,6 +6,7 @@ namespace SPC\store\source; use JetBrains\PhpStorm\ArrayShape; use SPC\exception\DownloaderException; +use SPC\exception\FileSystemException; use SPC\exception\RuntimeException; use SPC\store\Downloader; @@ -16,8 +17,9 @@ class PhpSource extends CustomSourceBase /** * @throws DownloaderException * @throws RuntimeException + * @throws FileSystemException */ - public function fetch() + public function fetch(): void { $major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.1'; Downloader::downloadSource('php-src', self::getLatestPHPInfo($major)); @@ -33,13 +35,16 @@ class PhpSource extends CustomSourceBase { // 查找最新的小版本号 $info = json_decode(Downloader::curlExec(url: "https://www.php.net/releases/index.php?json&version={$major_version}"), true); + if (!isset($info['version'])) { + throw new DownloaderException("Version {$major_version} not found."); + } + $version = $info['version']; // 从官网直接下载 return [ 'type' => 'url', 'url' => "https://www.php.net/distributions/php-{$version}.tar.gz", - // 'url' => "https://mirrors.zhamao.xin/php/php-{$version}.tar.gz", ]; } } diff --git a/src/SPC/store/source/PostgreSQLSource.php b/src/SPC/store/source/PostgreSQLSource.php index cd8a095d..66276db0 100644 --- a/src/SPC/store/source/PostgreSQLSource.php +++ b/src/SPC/store/source/PostgreSQLSource.php @@ -4,20 +4,31 @@ declare(strict_types=1); namespace SPC\store\source; +use SPC\exception\DownloaderException; +use SPC\exception\FileSystemException; +use SPC\exception\RuntimeException; use SPC\store\Downloader; class PostgreSQLSource extends CustomSourceBase { public const NAME = 'postgresql'; - public function fetch() + /** + * @throws DownloaderException + * @throws RuntimeException + * @throws FileSystemException + */ + public function fetch(): void { Downloader::downloadSource('postgresql', self::getLatestInfo()); } + /** + * @throws DownloaderException + */ public function getLatestInfo(): array { - [$url, $filename, $version] = Downloader::getFromFileList('postgresql', [ + [, $filename, $version] = Downloader::getFromFileList('postgresql', [ 'url' => 'https://www.postgresql.org/ftp/source/', 'regex' => '/href="(?v(?[^"]+)\/)"/', ]); diff --git a/src/SPC/util/ConfigValidator.php b/src/SPC/util/ConfigValidator.php index 9b818fb7..0126ddf9 100644 --- a/src/SPC/util/ConfigValidator.php +++ b/src/SPC/util/ConfigValidator.php @@ -49,10 +49,9 @@ class ConfigValidator } /** - * @param mixed $data * @throws ValidationException */ - public static function validateLibs($data, array $source_data = []): void + public static function validateLibs(mixed $data, array $source_data = []): void { is_array($data) || throw new ValidationException('lib.json is broken'); foreach ($data as $name => $lib) { @@ -64,7 +63,10 @@ class ConfigValidator } } - public static function validateExts($data, array $source_data = []): void + /** + * @throws ValidationException + */ + public static function validateExts(mixed $data): void { is_array($data) || throw new ValidationException('ext.json is broken'); } diff --git a/src/SPC/util/CustomExt.php b/src/SPC/util/CustomExt.php index 89d1b6cf..db312007 100644 --- a/src/SPC/util/CustomExt.php +++ b/src/SPC/util/CustomExt.php @@ -13,9 +13,7 @@ class CustomExt { private static array $custom_ext_class = []; - public function __construct(protected string $ext_name) - { - } + public function __construct(protected string $ext_name) {} /** * Load all custom extension classes diff --git a/src/SPC/util/DependencyUtil.php b/src/SPC/util/DependencyUtil.php index d030966d..4e0b74b1 100644 --- a/src/SPC/util/DependencyUtil.php +++ b/src/SPC/util/DependencyUtil.php @@ -15,18 +15,17 @@ use SPC\store\Config; class DependencyUtil { /** - * 根据需要的 ext 列表获取依赖的 lib 列表,同时根据依赖关系排序 + * Obtain the dependent lib list according to the required ext list, and sort according to the dependency * - * @param array $exts 要获取 libs 依赖的列表 - * @param array $additional_libs 额外要添加的库列表,用于激活 lib-suggests 触发的额外库特性 - * @return array 返回一个包含三个数组的数组,第一个是排序后的 ext 列表,第二个是排序后的 lib 列表,第三个是没有传入但是依赖了的 ext 列表 + * @param array $exts extensions list + * @param array $additional_libs List of additional libraries to add to activate the extra library features triggered by lib-suggests + * @return array Returns an array containing three arrays, [extensions, libraries, not included extensions] * @throws WrongUsageException * @throws RuntimeException * @throws FileSystemException */ public static function getExtLibsByDeps(array $exts, array $additional_libs = []): array { - // 先对扩展列表进行一个依赖筛选 $sorted = []; $visited = []; $not_included_exts = []; @@ -49,7 +48,7 @@ class DependencyUtil } } $libs = $additional_libs; - // 遍历每一个 ext 的 libs + foreach ($final as $ext) { if (!in_array($ext, $exts)) { $not_included_exts[] = $ext; @@ -100,6 +99,43 @@ class DependencyUtil return $final; } + public static function getAllExtLibsByDeps(array $exts): array + { + $sorted = []; + $visited = []; + $not_included_exts = []; + foreach ($exts as $ext) { + if (!isset($visited[$ext])) { + self::visitExtAllDeps($ext, $visited, $sorted); + } + } + $libs = []; + foreach ($sorted as $ext) { + if (!in_array($ext, $exts)) { + $not_included_exts[] = $ext; + } + foreach (array_merge(Config::getExt($ext, 'lib-depends', []), Config::getExt($ext, 'lib-suggests', [])) as $dep) { + if (!in_array($dep, $libs)) { + $libs[] = $dep; + } + } + } + return [$sorted, self::getAllLibsByDeps($libs), $not_included_exts]; + } + + public static function getAllLibsByDeps(array $libs): array + { + $sorted = []; + $visited = []; + + foreach ($libs as $lib) { + if (!isset($visited[$lib])) { + self::visitLibAllDeps($lib, $visited, $sorted); + } + } + return $sorted; + } + /** * @throws FileSystemException * @throws RuntimeException diff --git a/src/SPC/util/LicenseDumper.php b/src/SPC/util/LicenseDumper.php index 32bbdc93..196ca365 100644 --- a/src/SPC/util/LicenseDumper.php +++ b/src/SPC/util/LicenseDumper.php @@ -45,56 +45,72 @@ class LicenseDumper { // mkdir first if (is_dir($target_dir) && !FileSystem::removeDir($target_dir)) { - logger()->warning('Target dump directory is noe empty, be aware!'); + logger()->warning('Target dump directory is not empty, be aware!'); } FileSystem::createDir($target_dir); foreach ($this->exts as $ext) { if (Config::getExt($ext, 'type') !== 'external') { continue; } + $source_name = Config::getExt($ext, 'source'); - $content = $this->getSourceLicense($source_name); - file_put_contents($target_dir . '/ext_' . $ext . '.txt', $content); + foreach ($this->getSourceLicenses($source_name) as $index => $license) { + file_put_contents("{$target_dir}/ext_{$ext}_{$index}.txt", $license); + } } foreach ($this->libs as $lib) { $source_name = Config::getLib($lib, 'source'); - $content = $this->getSourceLicense($source_name); - file_put_contents($target_dir . '/lib_' . $lib . '.txt', $content); + foreach ($this->getSourceLicenses($source_name) as $index => $license) { + file_put_contents("{$target_dir}/lib_{$lib}_{$index}.txt", $license); + } } foreach ($this->sources as $source) { - file_put_contents($target_dir . '/src_' . $source . '.txt', $this->getSourceLicense($source)); + foreach ($this->getSourceLicenses($source) as $index => $license) { + file_put_contents("{$target_dir}/src_{$source}_{$index}.txt", $license); + } } return true; } /** + * @return string[] * @throws FileSystemException * @throws RuntimeException */ - private function getSourceLicense(string $source_name): ?string + private function getSourceLicenses(string $source_name): iterable { - $src = Config::getSource($source_name)['license'] ?? null; - if ($src === null) { - throw new RuntimeException('source [' . $source_name . '] license meta is not exist'); + $licenses = Config::getSource($source_name)['license'] ?? []; + if ($licenses === []) { + throw new RuntimeException('source [' . $source_name . '] license meta not exist'); } - return match ($src['type']) { - 'text' => $src['text'], - 'file' => $this->loadSourceFile($source_name, $src['path'], Config::getSource($source_name)['path'] ?? null), - default => throw new RuntimeException('source [' . $source_name . '] license type is not allowed'), - }; + if (!array_is_list($licenses)) { + $licenses = [$licenses]; + } + + foreach ($licenses as $index => $license) { + yield ($license['suffix'] ?? $index) => match ($license['type']) { + 'text' => $license['text'], + 'file' => $this->loadSourceFile($source_name, $license['path'], Config::getSource($source_name)['path'] ?? null), + default => throw new RuntimeException('source [' . $source_name . '] license type is not allowed'), + }; + } } /** * @throws RuntimeException */ - private function loadSourceFile(string $source_name, string $in_path, ?string $custom_base_path = null): string + private function loadSourceFile(string $source_name, ?string $in_path, ?string $custom_base_path = null): string { - if (!file_exists(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $in_path)) { - throw new RuntimeException('source [' . $source_name . '] license file [' . $in_path . '] is not exist'); + if (is_null($in_path)) { + throw new RuntimeException('source [' . $source_name . '] license file is not set, please check config/source.json'); } + if (!file_exists(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $in_path)) { + throw new RuntimeException('source [' . $source_name . '] license file [' . $in_path . '] not exist'); + } + return file_get_contents(SOURCE_PATH . '/' . ($custom_base_path ?? $source_name) . '/' . $in_path); } } diff --git a/src/SPC/util/Patcher.php b/src/SPC/util/Patcher.php index 3f7f7660..1419606e 100644 --- a/src/SPC/util/Patcher.php +++ b/src/SPC/util/Patcher.php @@ -19,17 +19,17 @@ class Patcher switch ($libc) { case 'musl_wrapper': // bad checks - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCPY 1$/m', ''); - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_STRLCAT 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCPY 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_STRLCAT 1$/m', ''); // no break case 'musl': - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_FUNC_ATTRIBUTE_IFUNC 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_FUNC_ATTRIBUTE_IFUNC 1$/m', ''); break; case 'glibc': // avoid lcrypt dependency - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT 1$/m', ''); - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_R 1$/m', ''); - FileSystem::replaceFile(SOURCE_PATH . '/php-src/main/php_config.h', REPLACE_FILE_PREG, '/^#define HAVE_CRYPT_H 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_CRYPT 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_CRYPT_R 1$/m', ''); + FileSystem::replaceFileRegex(SOURCE_PATH . '/php-src/main/php_config.h', '/^#define HAVE_CRYPT_H 1$/m', ''); break; default: throw new RuntimeException('not implemented'); diff --git a/src/SPC/util/UnixShell.php b/src/SPC/util/UnixShell.php index 565182ea..5fd730f9 100644 --- a/src/SPC/util/UnixShell.php +++ b/src/SPC/util/UnixShell.php @@ -63,6 +63,9 @@ class UnixShell return $this; } + /** + * @throws RuntimeException + */ public function execWithEnv(string $cmd): UnixShell { return $this->exec($this->getEnvString() . ' ' . $cmd); diff --git a/src/SPC/util/Util.php b/src/SPC/util/Util.php deleted file mode 100644 index ff017527..00000000 --- a/src/SPC/util/Util.php +++ /dev/null @@ -1,18 +0,0 @@ - 'windows', 'Darwin' => 'macos', 'Linux' => 'linux', + 'BSD' => 'freebsd', default => throw new WrongUsageException('Not support os: ' . PHP_OS_FAMILY), }; } /** - * 执行shell,直接输出在终端,出现错误抛出异常 + * Execute the shell command, and the output will be directly printed in the terminal. If there is an error, an exception will be thrown * * @throws \SPC\exception\RuntimeException */ @@ -92,12 +92,9 @@ function f_passthru(string $cmd): ?bool } /** - * 执行命令,不输出内容,返回执行结果和内容 - * - * @param mixed $output - * @param mixed $result_code + * Execute a command, return the output and result code */ -function f_exec(string $command, &$output, &$result_code): bool|string +function f_exec(string $command, mixed &$output, mixed &$result_code): bool|string { logger()->debug('Running command (no output) : ' . $command); return exec($command, $output, $result_code); @@ -113,6 +110,12 @@ function f_mkdir(string $directory, int $permissions = 0777, bool $recursive = f return mkdir($directory, $permissions, $recursive); } +function f_putenv(string $env): bool +{ + logger()->debug('Setting env: ' . $env); + return putenv($env); +} + function shell(?bool $debug = null): UnixShell { return new UnixShell($debug); diff --git a/src/globals/tests/bcmath.php b/src/globals/tests/bcmath.php index bad5f8f6..0190aa78 100644 --- a/src/globals/tests/bcmath.php +++ b/src/globals/tests/bcmath.php @@ -1,7 +1,5 @@ loadHtml("Hello"); +$doc->loadHtml('Hello'); assert($doc->getElementById('app')->nodeValue === 'Hello'); diff --git a/tests/SPC/doctor/CheckListHandlerTest.php b/tests/SPC/doctor/CheckListHandlerTest.php new file mode 100644 index 00000000..364d78ce --- /dev/null +++ b/tests/SPC/doctor/CheckListHandlerTest.php @@ -0,0 +1,24 @@ +runChecks(); + foreach ($id as $item) { + $this->assertInstanceOf('SPC\doctor\AsCheckItem', $item); + } + } +} diff --git a/tests/SPC/util/LicenseDumperTest.php b/tests/SPC/util/LicenseDumperTest.php new file mode 100644 index 00000000..e1ca06c9 --- /dev/null +++ b/tests/SPC/util/LicenseDumperTest.php @@ -0,0 +1,87 @@ + [ + 'source' => 'fake_lib', + ], + ]; + Config::$source = [ + 'fake_lib' => [ + 'license' => [ + 'type' => 'text', + 'text' => 'license', + 'suffix' => 'zend', + ], + ], + ]; + + $dumper = new LicenseDumper(); + $dumper->addLibs(['fake_lib']); + $dumper->dump(self::DIRECTORY); + + $this->assertFileExists(self::DIRECTORY . '/lib_fake_lib_zend.txt'); + } + + public function testDumpWithMultipleLicenses(): void + { + Config::$lib = [ + 'fake_lib' => [ + 'source' => 'fake_lib', + ], + ]; + Config::$source = [ + 'fake_lib' => [ + 'license' => [ + [ + 'type' => 'text', + 'text' => 'license', + ], + [ + 'type' => 'text', + 'text' => 'license', + ], + [ + 'type' => 'text', + 'text' => 'license', + 'suffix' => 'zend', + ], + ], + ], + ]; + + $dumper = new LicenseDumper(); + $dumper->addLibs(['fake_lib']); + $dumper->dump(self::DIRECTORY); + + $this->assertFileExists(self::DIRECTORY . '/lib_fake_lib_0.txt'); + $this->assertFileExists(self::DIRECTORY . '/lib_fake_lib_1.txt'); + $this->assertFileExists(self::DIRECTORY . '/lib_fake_lib_zend.txt'); + } +}