This commit is contained in:
crazywhalecc
2026-04-19 11:49:55 +08:00
parent a175c5862d
commit a348e838d7
120 changed files with 6346 additions and 3391 deletions

View File

@@ -0,0 +1,6 @@
# Build Lifecycle
<!-- TODO: Describe the full build pipeline stage sequence.
When each hook fires: #[BeforeStage], #[AfterStage], #[PatchBeforeBuild].
How platform selection (#[BuildFor]) works at runtime.
Diagram of stage order for a typical library and extension build. -->

View File

@@ -4,4 +4,4 @@ aside: false
# craft.yml Configuration
<!--@include: ../../deps-craft-yml.md-->
<!-- TODO: Full reference for craft.yml fields. -->

View File

@@ -1,70 +1,4 @@
# Doctor module
# Doctor Module
The Doctor module is a relatively independent module used to check the system environment, which can be entered with the command `bin/spc doctor`, and the entry command class is in `DoctorCommand.php`.
The Doctor module is a checklist with a series of check items and automatic repair items.
These items are stored in the `src/SPC/doctor/item/` directory,
And two Attributes are used as check item tags and auto-fix item tags: `#[AsCheckItem]` and `#[AsFixItem]`.
Take the existing check item `if necessary tools are installed`,
which is used to check whether the packages necessary for compilation are installed in the macOS system.
The following is its source code:
```php
use SPC\doctor\AsCheckItem;
use SPC\doctor\AsFixItem;
use SPC\doctor\CheckResult;
#[AsCheckItem('if necessary tools are installed', limit_os: 'Darwin', level: 997)]
public function checkCliTools(): ?CheckResult
{
$missing = [];
foreach (self::REQUIRED_COMMANDS as $cmd) {
if ($this->findCommand($cmd) === null) {
$missing[] = $cmd;
}
}
if (!empty($missing)) {
return CheckResult::fail('missing system commands: ' . implode(', ', $missing), 'build-tools', [$missing]);
}
return CheckResult::ok();
}
```
The first parameter of the attribute is the name of the check item,
and the following `limit_os` parameter restricts the check item to be triggered only under the specified system,
and `level` is the priority of executing the check item, the larger the number, the higher the priority higher.
The `$this->findCommand()` method used in it is the method of `SPC\builder\traits\UnixSystemUtilTrait`,
the purpose is to find the location of the system command, and return NULL if it cannot be found.
Each check item method should return a `SPC\doctor\CheckResult`:
- When returning `CheckResult::fail()`, the first parameter is used to output the error prompt of the terminal,
and the second parameter is the name of the repair item when this check item can be automatically repaired.
- When `CheckResult::ok()` is returned, the check passed. You can also pass a parameter to return the check result, for example: `CheckResult::ok('OS supported')`.
- When returning `CheckResult::fail()`, if the third parameter is included, the array of the third parameter will be used as the parameter of `AsFixItem`.
The following is the method for automatically repairing items corresponding to this check item:
```php
#[AsFixItem('build-tools')]
public function fixBuildTools(array $missing): bool
{
foreach ($missing as $cmd) {
try {
shell(true)->exec('brew install ' . escapeshellarg($cmd));
} catch (RuntimeException) {
return false;
}
}
return true;
}
```
`#[AsFixItem()]` first parameter is the name of the fix item, and this method must return True or False.
When False is returned, the automatic repair failed and manual handling is required.
In the code here, `shell()->exec()` is the method of executing commands of the project,
which is used to replace `exec()` and `system()`, and also provides debugging, obtaining execution status,
entering directories, etc. characteristic.
<!-- TODO: Migrate and update from v2 doctor-module.md.
Cover v3 changes: --auto-fix, .spc-doctor.lock, new check items for v3 toolchain. -->

View File

@@ -1,35 +1,4 @@
# Start Developing
Developing this project requires the installation and deployment of a PHP environment,
as well as some extensions and Composer commonly used in PHP projects.
The development environment and running environment of the project are almost exactly the same.
You can refer to the **Manual Build** section to install system PHP or use the pre-built static PHP of this project as the environment.
I will not go into details here.
Regardless of its purpose, this project itself is actually a `php-cli` program. You can edit and develop it as a normal PHP project.
At the same time, you need to understand the Shell languages of different systems.
The current purpose of this project is to compile statically compiled independent PHP,
but the main part also includes compiling static versions of many dependent libraries,
so you can reuse this set of compilation logic to build independent binary versions of other programs, such as Nginx, etc.
## Environment preparation
A PHP environment is required to develop this project. You can use the PHP that comes with the system,
or you can use the static PHP built by this project.
Regardless of which PHP you use, in your development environment you need to install these extensions:
```
curl,dom,filter,mbstring,openssl,pcntl,phar,posix,sodium,tokenizer,xml,xmlwriter
```
The static-php-cli project itself does not require so many extensions, but during the development process,
you will use tools such as Composer and PHPUnit, which require these extensions.
> For micro self-executing binaries built by static-php-cli itself, only `pcntl,posix,mbstring,tokenizer,phar` is required.
## Start development
Continuing down to see the project structure documentation, you can learn how `static-php-cli` works.
<!-- TODO: Developer introduction, environment setup, required PHP extensions.
Link to Vendor Mode for library authors, and Contributing for code contributors. -->

View File

@@ -0,0 +1,6 @@
# Package Model
<!-- TODO: Explain the unified package model: library / php-extension / target types.
Cover the per-package YAML format (config/pkg/), the `depends` field,
platform overrides (@windows / @unix notation), artifact.source and artifact.binary.
Show annotated example YAML for a library and an extension. -->

View File

@@ -1,59 +1,4 @@
# Modifications to PHP source code
# PHP Source Modifications
During the static compilation process, static-php-cli made some modifications to the PHP source code
in order to achieve good compatibility, performance, and security.
The following is a description of the current modifications to the PHP source code.
## Micro related patches
Based on the patches provided by the phpmicro project,
static-php-cli has made some modifications to the PHP source code to meet the needs of static compilation.
The patches currently used by static-php-cli during compilation in the [patch list](https://github.com/easysoft/phpmicro/tree/master/patches) are:
- static_opcache
- static_extensions_win32
- cli_checks
- disable_huge_page
- vcruntime140
- win32
- zend_stream
- cli_static
- macos_iconv
- phar
## PHP <= 8.1 libxml patch
Because PHP only provides security updates for 8.1 and stops updating older versions,
static-php-cli applies the libxml compilation patch that has been applied in newer versions of PHP to PHP 8.1 and below.
## gd extension Windows patch
Compiling the gd extension under Windows requires major changes to the `config.w32` file.
static-php-cli has made some changes to the gd extension to make it easier to compile under Windows.
## YAML extension Windows patch
YAML extension needs to modify the `config.w32` file to compile under Windows.
static-php-cli has made some modifications to the YAML extension to make it easier to compile under Windows.
## static-php-cli version information insertion
When compiling, static-php-cli will insert the static-php-cli version information into the PHP version information for easy identification.
## Add option to hardcode INI
When using the `-I` parameter to hardcode INI into static PHP functionality,
static-php-cli will modify the PHP source code to insert the hardcoded content.
## Linux system repair patch
Some compilation environments may lack some system header files or libraries.
static-php-cli will automatically fix these problems during compilation, such as:
- HAVE_STRLCAT missing problem
- HAVE_STRLCPY missing problem
## Fiber issue fix patch for Windows
When compiling PHP on Windows, there will be some issues with the Fiber extension.
static-php-cli will automatically fix these issues during compilation (modify `config.w32` in php-src).
<!-- TODO: Migrate and update from v2 php-src-changes.md.
Add v3-specific patches (FrankenPHP embed, Windows fiber fix, etc.). -->

View File

@@ -0,0 +1,6 @@
# Registry & Plugin System
<!-- TODO: Explain spc.registry.yml structure.
How to add an external registry via SPC_REGISTRIES env var.
Vendor-specific configurations, overriding core packages.
Registry resolution order and conflict rules. -->

View File

@@ -1,372 +1,5 @@
# Source module
# Source Module
The download source module of static-php-cli is a major module.
It includes dependent libraries, external extensions, PHP source code download methods and file decompression methods.
The download configuration file mainly involves the `source.json` and `pkg.json` file, which records the download method of all downloadable sources.
The main commands involved in the download function are `bin/spc download` and `bin/spc extract`.
The `download` command is a downloader that downloads sources according to the configuration file,
and the `extract` command is an extractor that extract sources from downloaded files.
Generally speaking, downloading sources may be slow because these sources come from various official websites, GitHub,
and other different locations.
At the same time, they also occupy a large space, so you can download the sources once and reuse them.
The configuration file of the downloader is `source.json`, which contains the download methods of all sources.
You can add the source download methods you need, or modify the existing source download methods.
The download configuration structure of each source is as follows.
The following is the source download configuration corresponding to the `libevent` extension:
```json
{
"libevent": {
"type": "ghrel",
"repo": "libevent/libevent",
"match": "libevent.+\\.tar\\.gz",
"provide-pre-built": true,
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
The most important field here is `type`. Currently, the types it supports are:
- `url`: Directly use URL to download, for example: `https://download.libsodium.org/libsodium/releases/libsodium-1.0.18.tar.gz`.
- `pie`: Download PHP extensions from Packagist using the PIE (PHP Installer for Extensions) standard.
- `ghrel`: Use the GitHub Release API to download, download the artifacts uploaded from the latest version released by maintainers.
- `ghtar`: Use the GitHub Release API to download.
Different from `ghrel`, `ghtar` is downloaded from the `source code (tar.gz)` in the latest Release of the project.
- `ghtagtar`: Use GitHub Release API to download.
Compared with `ghtar`, `ghtagtar` can find the latest one from the `tags` list and download the source code in `tar.gz` format
(because some projects only use `tag` release version).
- `bitbuckettag`: Download using BitBucket API, basically the same as `ghtagtar`, except this one applies to BitBucket.
- `git`: Clone the project directly from a Git address to download sources, applicable to any public Git repository.
- `filelist`: Use a crawler to crawl the Web download site that provides file index,
and get the latest version of the file name and download it.
- `custom`: If none of the above download methods are satisfactory, you can write `custom`,
create a new class under `src/SPC/store/source/`, extends `CustomSourceBase`, and write the download script yourself.
## source.json Common parameters
Each source file in source.json has the following params:
- `license`: the open source license of the source code, see **Open Source License** section below
- `type`: must be one of the types mentioned above
- `path` (optional): release the source code to the specified directory instead of `source/{name}`
- `provide-pre-built` (optional): whether to provide precompiled binary files.
If `true`, it will automatically try to download precompiled binary files when running `bin/spc download`
::: tip
The `path` parameter in `source.json` can specify a relative or absolute path. When specified as a relative path, the path is based on `source/`.
:::
## Download type - url
URL type sources refer to downloading files directly from the URL.
The parameters included are:
- `url`: The download address of the file, such as `https://example.com/file.tgz`
- `filename` (optional): The file name saved to the local area. If not specified, the file name of the url will be used.
Example (download the imagick extension and extract it to the extension storage path of the php source code):
```json
{
"ext-imagick": {
"type": "url",
"url": "https://pecl.php.net/get/imagick",
"path": "php-src/ext/imagick",
"filename": "imagick.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## Download type - pie
PIE (PHP Installer for Extensions) type sources refer to downloading PHP extensions from Packagist that follow the PIE standard.
This method automatically fetches extension information from the Packagist repository and downloads the appropriate distribution file.
The parameters included are:
- `repo`: The Packagist vendor/package name, such as `vendor/package-name`
Example (download a PHP extension from Packagist using PIE):
```json
{
"ext-example": {
"type": "pie",
"repo": "vendor/example-extension",
"path": "php-src/ext/example",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
::: tip
The PIE download type will automatically detect the extension information from Packagist metadata,
including the download URL, version, and distribution type.
The extension must be marked as `type: php-ext` or contain `php-ext` metadata in its Packagist package definition.
:::
## Download type - ghrel
ghrel will download files from Assets uploaded in GitHub Release.
First use the GitHub Release API to get the latest version, and then download the corresponding files according to the regular matching method.
The parameters included are:
- `repo`: GitHub repository name
- `match`: regular expression matching Assets files
- `prefer-stable`: Whether to download stable versions first (default is `false`)
Example (download the libsodium library, matching the libsodium-x.y.tar.gz file in Release):
```json
{
"libsodium": {
"type": "ghrel",
"repo": "jedisct1/libsodium",
"match": "libsodium-\\d+(\\.\\d+)*\\.tar\\.gz",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## Download type - ghtar
ghtar will download the file from the GitHub Release Tag.
Unlike `ghrel`, `ghtar` will download the `source code (tar.gz)` from the latest Release of the project.
The parameters included are:
- `repo`: GitHub repository name
- `prefer-stable`: Whether to download stable versions first (default is `false`)
Example (brotli library):
```json
{
"brotli": {
"type": "ghtar",
"repo": "google/brotli",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## Download type - ghtagtar
Use the GitHub Release API to download.
Compared with `ghtar`, `ghtagtar` can find the latest one from the `tags` list and download the source code in `tar.gz` format
(because some projects only use the `tag` version).
The parameters included are:
- `repo`: GitHub repository name
- `prefer-stable`: Whether to download stable versions first (default is `false`)
Example (gmp library):
```json
{
"gmp": {
"type": "ghtagtar",
"repo": "alisw/GMP",
"license": {
"type": "text",
"text": "EXAMPLE LICENSE"
}
}
}
```
## Download Type - bitbuckettag
Download using BitBucket API, basically the same as `ghtagtar`, except this one works with BitBucket.
The parameters included are:
- `repo`: BitBucket repository name
## Download type - git
Clone the project directly from a Git address to download sources, applicable to any public Git repository.
The parameters included are:
- `url`: Git link (HTTPS only)
- `rev`: branch name
```json
{
"imap": {
"type": "git",
"url": "https://github.com/static-php/imap.git",
"rev": "master",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
## Download type - filelist
Use a crawler to crawl a web download site that provides a file index and get the latest version of the file name and download it.
Note that this method is only applicable to static sites with page index functions such as mirror sites and GNU official websites.
The parameters included are:
- `url`: The URL of the page to crawl the latest version of the file
- `regex`: regular expression matching file names and download links
Example (download the libiconv library from the GNU official website):
```json
{
"libiconv": {
"type": "filelist",
"url": "https://ftp.gnu.org/gnu/libiconv/",
"regex": "/href=\"(?<file>libiconv-(?<version>[^\"]+)\\.tar\\.gz)\"/",
"license": {
"type": "file",
"path": "COPYING"
}
}
}
```
## Download type - custom
If the above downloading methods are not satisfactory, you can write `custom`,
create a new class under `src/SPC/store/source/`, extends `CustomSourceBase`, and write the download script yourself.
I wont go into details here, you can look at `src/SPC/store/source/PhpSource.php` or `src/SPC/store/source/PostgreSQLSource.php` as examples.
## pkg.json General parameters
pkg.json stores non-source-code files, such as precompiled tools musl-toolchain and UPX. It includes:
- `type`: The same type as `source.json` and different kinds of parameters.
- `extract` (optional): The path to decompress after downloading, the default is `pkgroot/{pkg_name}`.
- `extract-files` (optional): Extract only the specified files to the specified location after downloading.
It should be noted that `pkg.json` does not involve compilation, modification and distribution of source code,
so there is no `license` open source license field.
And you cannot use the `extract` and `extract-files` parameters at the same time.
Example (download nasm locally and extract only program files to PHP SDK):
```json
{
"nasm-x86_64-win": {
"type": "url",
"url": "https://www.nasm.us/pub/nasm/releasebuilds/2.16.01/win64/nasm-2.16.01-win64.zip",
"extract-files": {
"nasm-2.16.01/nasm.exe": "{php_sdk_path}/bin/nasm.exe",
"nasm-2.16.01/ndisasm.exe": "{php_sdk_path}/bin/ndisasm.exe"
}
}
}
```
The key name in `extract-files` is the file in the source folder, and the key value is the storage path. The storage path can use the following variables:
- `{php_sdk_path}`: (Windows only) PHP SDK path
- `{pkg_root_path}`: `pkgroot/`
- `{working_dir}`: current working directory
- `{download_path}`: download directory
- `{source_path}`: source code decompression directory
When `extract-files` does not use variables and is a relative path, the directory of the relative path is `{working_dir}`.
## Open source license
For `source.json`, each source file should contain an open source license.
The `license` field stores the open source license information.
Each `license` contains the following parameters:
- `type`: `file` or `text`
- `path`: the license file in the source code directory (required when `type` is `file`)
- `text`: License text (required when `type` is `text`)
Example (yaml extension source code with LICENSE file):
```json
{
"yaml": {
"type": "git",
"path": "php-src/ext/yaml",
"rev": "php7",
"url": "https://github.com/php/pecl-file_formats-yaml",
"license": {
"type": "file",
"path": "LICENSE"
}
}
}
```
When an open source project has multiple licenses, multiple files can be specified:
```json
{
"libuv": {
"type": "ghtar",
"repo": "libuv/libuv",
"license": [
{
"type": "file",
"path": "LICENSE"
},
{
"type": "file",
"path": "LICENSE-extra"
}
]
}
}
```
When the license of an open source project uses different files between versions,
`path` can be used as an array to list the possible license files:
```json
{
"redis": {
"type": "git",
"path": "php-src/ext/redis",
"rev": "release/6.0.2",
"url": "https://github.com/phpredis/phpredis",
"license": {
"type": "file",
"path": [
"LICENSE",
"COPYING"
]
}
}
}
```
<!-- TODO: Migrate and update from v2 source-module.md.
Document v3 source types: url, ghrel, ghtar, ghtagtar, git, pecl (new), filelist, custom.
Per-package YAML source block format. Parallel download (--parallel N). -->

View File

@@ -1,180 +1,5 @@
# Introduction to project structure
# Project Structure
static-php-cli mainly contains three logical components: sources, dependent libraries, and extensions.
These components contains 4 configuration files: `source.json`, `pkg.json`, `lib.json`, and `ext.json`.
A complete process for building standalone static PHP is:
1. Use the source download module `Downloader` to download specified or all source codes.
These sources include PHP source code, dependent library source code, and extension source code.
2. Use the source decompression module `SourceExtractor` to decompress the downloaded sources to the compilation directory.
3. Use the dependency tool to calculate the dependent extensions and dependent libraries of the currently added extension,
and then compile each library that needs to be compiled in the order of dependencies.
4. After building each dependent library using `Builder` under the corresponding operating system, install it to the `buildroot` directory.
5. If external extensions are included (the source code does not contain extensions within PHP),
copy the external extensions to the `source/php-src/ext/` directory.
6. Use `Builder` to build the PHP source code and build target to the `buildroot` directory.
The project is mainly divided into several folders:
- `bin/`: used to store program entry files, including `bin/spc`, `bin/spc-alpine-docker`, `bin/setup-runtime`.
- `config/`: Contains all the extensions and dependent libraries supported by the project,
as well as the download link and download methods of these sources. It is divided into files: `lib.json`, `ext.json`, `source.json`, `pkg.json`, `pre-built.json` .
- `src/`: The core code of the project, including the entire framework and commands for compiling various extensions and libraries.
- `vendor/`: The directory that Composer depends on, you do not need to make any modifications to it.
The operating principle is to start a `ConsoleApplication` of `symfony/console`, and then parse the commands entered by the user in the terminal.
## Basic command line structure
`bin/spc` is an entry file, including the Unix common `#!/usr/bin/env php`,
which is used to allow the system to automatically execute with the PHP interpreter installed on the system.
After the project executes `new ConsoleApplication()`, the framework will automatically register them as commands.
The project does not directly use the Command registration method and command execution method recommended by Symfony. Here are small changes:
1. Each command uses the `#[AsCommand()]` Attribute to register the name and description.
2. Abstract `execute()` so that all commands are based on `BaseCommand` (which is based on `Symfony\Component\Console\Command\Command`),
and the execution code of each command itself is written in the `handle()` method .
3. Added variable `$no_motd` to `BaseCommand`, which is used to display the Figlet greeting when the command is executed.
4. `BaseCommand` saves `InputInterface` and `OutputInterface` as member variables. You can use `$this->input` and `$this->output` within the command class.
## Basic source code structure
The source code of the project is located in the `src/SPC` directory,
supports automatic loading of the PSR-4 standard, and contains the following subdirectories and classes:
- `src/SPC/builder/`: The core compilation command code used to build libraries,
PHP and related extensions under different operating systems, and also includes some compilation system tool methods.
- `src/SPC/command/`: All commands of the project are here.
- `src/SPC/doctor/`: Doctor module, which is a relatively independent module used to check the system environment.
It can be entered using the command `bin/spc doctor`.
- `src/SPC/exception/`: exception class.
- `src/SPC/store/`: Classes related to storage, files and sources are all here.
- `src/SPC/util/`: Some reusable tool methods are here.
- `src/SPC/ConsoleApplication.php`: command line program entry file.
If you have read the source code, you may find that there is also a `src/globals/` directory,
which is used to store some global variables, global methods,
and non-PSR-4 standard PHP source code that is relied upon during the build process, such as extension sanity check code etc.
## Phar application directory issue
Like other php-cli projects, spc itself has additional considerations for paths.
Because spc can run in multiple modes such as `php-cli directly`, `micro SAPI`, `php-cli with Phar`, `vendor with Phar`, etc.,
there are ambiguities in various root directories. A complete explanation is given here.
This problem is generally common in the base class path selection problem of accessing files in PHP projects, especially when used with `micro.sfx`.
Note that this may only be useful for you when developing Phar projects or PHP frameworks.
> Next, we will treat `static-php-cli` (that is, spc) as a normal `php` command line program. You can understand spc as any of your own php-cli applications for reference.
There are three basic constant theoretical values below. We recommend that you introduce these three constants when writing PHP projects:
- `WORKING_DIR`: the working directory when executing PHP scripts
- `SOURCE_ROOT_DIR` or `ROOT_DIR`: the root directory of the project folder, generally the directory where `composer.json` is located
- `FRAMEWORK_ROOT_DIR`: the root directory of the framework used, which may be used by self-developed frameworks. Generally, the framework directory is read-only
You can define these constants in your framework entry or cli applications to facilitate the use of paths in your project.
The following are PHP built-in constant values, which have been defined inside the PHP interpreter:
- `__DIR__`: the directory where the file of the currently executed script is located
- `__FILE__`: the file path of the currently executed script
### Git project mode (source)
Git project mode refers to a framework or program itself stored in plain text in the current folder, and running through `php path/to/entry.php`.
Assume that your project is stored in the `/home/example/static-php-cli/` directory, or your project is the framework itself,
which contains project files such as `composer.json`:
```
composer.json
src/App/MyCommand.app
vendor/*
bin/entry.php
```
We assume that the above constants are obtained from `src/App/MyCommand.php`:
| Constant | Value |
|----------------------|------------------------------------------------------|
| `WORKING_DIR` | `/home/example/static-php-cli` |
| `SOURCE_ROOT_DIR` | `/home/example/static-php-cli` |
| `FRAMEWORK_ROOT_DIR` | `/home/example/static-php-cli` |
| `__DIR__` | `/home/example/static-php-cli/src/App` |
| `__FILE__` | `/home/example/static-php-cli/src/App/MyCommand.php` |
In this case, the values of `WORKING_DIR`, `SOURCE_ROOT_DIR`, and `FRAMEWORK_ROOT_DIR` are exactly the same: `/home/example/static-php-cli`.
The source code of the framework and the source code of the application are both in the current path.
### Vendor library mode (vendor)
The vendor library mode generally means that your project is a framework or is installed into the project as a composer dependency by other applications,
and the storage location is in the `vendor/author/XXX` directory.
Suppose your project is `crazywhalecc/static-php-cli`, and you or others install this project in another project using `composer require`.
We assume that static-php-cli contains all files except the `vendor` directory with the same `Git mode`, and get the constant value from `src/App/MyCommand`,
Directory constant should be:
| Constant | Value |
|----------------------|--------------------------------------------------------------------------------------|
| `WORKING_DIR` | `/home/example/another-app` |
| `SOURCE_ROOT_DIR` | `/home/example/another-app` |
| `FRAMEWORK_ROOT_DIR` | `/home/example/another-app/vendor/crazywhalecc/static-php-cli` |
| `__DIR__` | `/home/example/another-app/vendor/crazywhalecc/static-php-cli/src/App` |
| `__FILE__` | `/home/example/another-app/vendor/crazywhalecc/static-php-cli/src/App/MyCommand.php` |
Here `SOURCE_ROOT_DIR` refers to the root directory of the project using `static-php-cli`.
### Git project Phar mode (source-phar)
Git project Phar mode refers to the mode of packaging the project directory of the Git project mode into a `phar` file. We assume that `/home/example/static-php-cli` will be packaged into a Phar file, and the directory has the following files:
```
composer.json
src/App/MyCommand.app
vendor/*
bin/entry.php
```
When packaged into `app.phar` and stored in the `/home/example/static-php-cli` directory, `app.phar` is executed at this time. Assuming that the `src/App/MyCommand` code is executed, the constant is obtained in the file:
| Constant | Value |
|----------------------|----------------------------------------------------------------------|
| `WORKING_DIR` | `/home/example/static-php-cli` |
| `SOURCE_ROOT_DIR` | `phar:///home/example/static-php-cli/app.phar/` |
| `FRAMEWORK_ROOT_DIR` | `phar:///home/example/static-php-cli/app.phar/` |
| `__DIR__` | `phar:///home/example/static-php-cli/app.phar/src/App` |
| `__FILE__` | `phar:///home/example/static-php-cli/app.phar/src/App/MyCommand.php` |
Because the `phar://` protocol is required to read files in the phar itself, the project root directory and the framework directory will be different from `WORKING_DIR`.
### Vendor Library Phar Mode (vendor-phar)
Vendor Library Phar Mode means that your project is installed as a framework in other projects and stored in the `vendor` directory.
We assume that your project directory structure is as follows:
```
composer.json # Composer configuration file of the current project
box.json # Configuration file for packaging Phar
another-app.php # Entry file of another project
vendor/crazywhalecc/static-php-cli/* # Your project is used as a dependent library
```
When packaging these files under the directory `/home/example/another-app/` into `app.phar`, the value of the following constant for your project should be:
| Constant | Value |
|----------------------|------------------------------------------------------------------------------------------------------|
| `WORKING_DIR` | `/home/example/another-app` |
| `SOURCE_ROOT_DIR` | `phar:///home/example/another-app/app.phar/` |
| `FRAMEWORK_ROOT_DIR` | `phar:///home/example/another-app/app.phar/vendor/crazywhalecc/static-php-cli` |
| `__DIR__` | `phar:///home/example/another-app/app.phar/vendor/crazywhalecc/static-php-cli/src/App` |
| `__FILE__` | `phar:///home/example/another-app/app.phar/vendor/crazywhalecc/static-php-cli/src/App/MyCommand.php` |
<!-- TODO: v3 directory layout (bin/, config/pkg/, src/StaticPHP/, src/Package/, etc.).
Explain the role of each top-level directory. Internal class structure kept brief;
deep dives belong in the Concepts pages. -->

View File

@@ -1,242 +1,4 @@
# Compilation Tools
static-php-cli uses many system compilation tools when building static PHP. These tools mainly include:
- `autoconf`: used to generate `configure` scripts.
- `make`: used to execute `Makefile`.
- `cmake`: used to execute `CMakeLists.txt`.
- `pkg-config`: Used to find the installation path of dependent libraries.
- `gcc`: used to compile C/C++ projects under Linux.
- `clang`: used to compile C/C++ projects under macOS.
For Linux and macOS operating systems,
these tools can usually be installed through the package manager, which is written in the doctor module.
Theoretically we can also compile and download these tools manually,
but this will increase the complexity of compilation, so we do not recommend this.
## Linux Compilation Tools
For Linux systems, different distributions have different installation methods for compilation tools.
And for static compilation, the package management of some distributions cannot install libraries and tools for pure static compilation.
Therefore, for the Linux platform and its different distributions,
we currently provide a variety of compilation environment preparations.
### Glibc Environment
The glibc environment refers to the underlying `libc` library of the system
(that is, the C standard library that all programs written in C language are dynamically linked to) uses `glibc`,
which is the default environment for most distributions.
For example: Ubuntu, Debian, CentOS, RHEL, openSUSE, Arch Linux, etc.
In the glibc environment, the package management and compiler we use point to glibc by default,
and glibc cannot be statically linked well.
One of the reasons it cannot be statically linked is that its network library `nss` cannot be compiled statically.
For the glibc environment, in static-php-cli and spc in 2.0-RC8 and later, you can choose two ways to build static PHP:
1. Use Docker to build, you can use `bin/spc-alpine-docker` to build, it will build an Alpine Linux docker image.
2. Use `bin/spc doctor --auto-fix` to install the `musl-wrapper` and `musl-cross-make` packages, and then build directly.
([Related source code](https://github.com/crazywhalecc/static-php-cli/blob/main/src/SPC/doctor/item/LinuxMuslCheck.php))
Generally speaking, the build results in these two environments are consistent, and you can choose according to actual needs.
In the doctor module, static-php-cli will first detect the current Linux distribution.
If the current distribution is a glibc environment, you will be prompted to install the musl-wrapper and musl-cross-make packages.
The process of installing `musl-wrapper` in the glibc environment is as follows:
1. Download the specific version of [musl-wrapper source code](https://musl.libc.org/releases/) from the musl official website.
2. Use `gcc` installed from the package management to compile the musl-wrapper source code and generate `musl-libc` and other libraries: `./configure --disable-gcc-wrapper && make -j && sudo make install`.
3. The musl-wrapper related libraries will be installed in the `/usr/local/musl` directory.
The process of installing `musl-cross-make` in the glibc environment is as follows:
1. Download the precompiled [musl-cross-make](https://dl.static-php.dev/static-php-cli/deps/musl-toolchain/) compressed package from dl.static-php.dev .
2. Unzip to the `/usr/local/musl` directory.
::: tip
In the glibc environment, static compilation can be achieved by directly installing musl-wrapper,
but musl-wrapper only contains `musl-gcc` and not `musl-g++`, which means that C++ code cannot be compiled.
So we need musl-cross-make to provide `musl-g++`.
The reason why the musl-cross-make package cannot be compiled directly locally is that
its compilation environment requirements are relatively high (requires more than 36GB of memory, compiled under Alpine Linux),
so we provide precompiled binary packages that can be used for all Linux distributions.
At the same time, the package management of some distributions provides musl-wrapper,
but musl-cross-make needs to match the corresponding musl-wrapper version,
so we do not use package management to install musl-wrapper.
Compiling musl-cross-make will be introduced in the **musl-cross-make Toolchain Compilation** section of this chapter.
:::
### Musl Environment
The musl environment refers to the system's underlying `libc` library that uses `musl`,
which is a lightweight C standard library that can be well statically linked.
For the currently popular Linux distributions, Alpine Linux uses the musl environment,
so static-php-cli can directly build static PHP under Alpine Linux.
You only need to install basic compilation tools (such as `gcc`, `cmake`, etc.) directly from the package management.
For other distributions, if your distribution uses the musl environment,
you can also use static-php-cli to build static PHP directly after installing the necessary compilation tools.
::: tip
In the musl environment, static-php-cli will automatically skip the installation of musl-wrapper and musl-cross-make.
:::
### Docker Environment
The Docker environment refers to using Docker containers to build static PHP. You can use `bin/spc-alpine-docker` to build.
Before executing this command, you need to install Docker first, and then execute `bin/spc-alpine-docker` in the project root directory.
After executing `bin/spc-alpine-docker`, static-php-cli will automatically download the Alpine Linux image and then build a `cwcc-spc-x86_64` or `cwcc-spc-aarch64` image.
Then all build process is performed within this image, which is equivalent to compiling in Alpine Linux.
## musl-cross-make Toolchain Compilation
In Linux, although you do not need to manually compile the musl-cross-make tool,
if you want to understand its compilation process, you can refer here.
Another important reason is that this may not be compiled using automated tools such as CI and Actions,
because the existing CI service compilation environment does not meet the compilation requirements of musl-cross-make,
and the configuration that meets the requirements is too expensive.
The compilation process of musl-cross-make is as follows:
Prepare an Alpine Linux environment (either directly installed or using Docker).
The compilation process requires more than **36GB** of memory,
so you need to compile on a machine with larger memory.
Without this much memory, compilation may fail.
Then write the following content into the `config.mak` file:
```makefile
STAT = -static --static
FLAG = -g0 -Os -Wno-error
ifneq ($(NATIVE),)
COMMON_CONFIG += CC="$(HOST)-gcc ${STAT}" CXX="$(HOST)-g++ ${STAT}"
else
COMMON_CONFIG += CC="gcc ${STAT}" CXX="g++ ${STAT}"
endif
COMMON_CONFIG += CFLAGS="${FLAG}" CXXFLAGS="${FLAG}" LDFLAGS="${STAT}"
BINUTILS_CONFIG += --enable-gold=yes --enable-gprofng=no
GCC_CONFIG += --enable-static-pie --disable-cet --enable-default-pie
#--enable-default-pie
CONFIG_SUB_REV = 888c8e3d5f7b
GCC_VER = 13.2.0
BINUTILS_VER = 2.40
MUSL_VER = 1.2.4
GMP_VER = 6.2.1
MPC_VER = 1.2.1
MPFR_VER = 4.2.0
LINUX_VER = 6.1.36
```
And also you need to add `gcc-13.2.0.tar.xz.sha1` file, contents here:
```
5f95b6d042fb37d45c6cbebfc91decfbc4fb493c gcc-13.2.0.tar.xz
```
If you are using Docker to build, create a new `Dockerfile` file and write the following content:
```dockerfile
FROM alpine:edge
RUN apk add --no-cache \
gcc g++ git make curl perl \
rsync patch wget libtool \
texinfo autoconf automake \
bison tar xz bzip2 zlib \
file binutils flex \
linux-headers libintl \
gettext gettext-dev icu-libs pkgconf \
pkgconfig icu-dev bash \
ccache libarchive-tools zip
WORKDIR /opt
RUN git clone https://git.zv.io/toolchains/musl-cross-make.git
WORKDIR /opt/musl-cross-make
COPY config.mak /opt/musl-cross-make
COPY gcc-13.2.0.tar.xz.sha1 /opt/musl-cross-make/hashes
RUN make TARGET=x86_64-linux-musl -j || :
RUN sed -i 's/poison calloc/poison/g' ./gcc-13.2.0/gcc/system.h
RUN make TARGET=x86_64-linux-musl -j
RUN make TARGET=x86_64-linux-musl install -j
RUN tar cvzf x86_64-musl-toolchain.tgz output/*
```
If you are using Alpine Linux in a non-Docker environment, you can directly execute the commands in the Dockerfile, for example:
```bash
apk add --no-cache \
gcc g++ git make curl perl \
rsync patch wget libtool \
texinfo autoconf automake \
bison tar xz bzip2 zlib \
file binutils flex \
linux-headers libintl \
gettext gettext-dev icu-libs pkgconf \
pkgconfig icu-dev bash \
ccache libarchive-tools zip
git clone https://git.zv.io/toolchains/musl-cross-make.git
# Copy config.mak to the working directory of musl-cross-make.
# You need to replace /path/to/config.mak with your config.mak file path.
cp /path/to/config.mak musl-cross-make/
cp /path/to/gcc-13.2.0.tar.xz.sha1 musl-cross-make/hashes
make TARGET=x86_64-linux-musl -j || :
sed -i 's/poison calloc/poison/g' ./gcc-13.2.0/gcc/system.h
make TARGET=x86_64-linux-musl -j
make TARGET=x86_64-linux-musl install -j
tar cvzf x86_64-musl-toolchain.tgz output/*
```
::: tip
All the above scripts are suitable for x86_64 architecture Linux.
If you need to build musl-cross-make for the ARM environment, just replace all `x86_64` above with `aarch64`.
:::
This compilation process may fail due to insufficient memory, network problems, etc.
You can try a few more times, or use a machine with larger memory to compile.
If you encounter problems or you have better improvement solutions, go to [Discussion](https://github.com/crazywhalecc/static-php-cli-hosted/issues/1).
## macOS Environment
For macOS systems, the main compilation tool we use is `clang`,
which is the default compiler for macOS systems and is also the compiler of Xcode.
Compiling under macOS mainly relies on Xcode or Xcode Command Line Tools.
You can download Xcode from the App Store,
or execute `xcode-select --install` in the terminal to install Xcode Command Line Tools.
In addition, in the `doctor` environment check module, static-php-cli will check whether Homebrew,
compilation tools, etc. are installed on the macOS system.
If not, you will be prompted to install them. I will not go into details here.
## FreeBSD Environment
FreeBSD is also a Unix system, and its compilation tools are similar to macOS.
You can directly use the package management `pkg` to install `clang` and other compilation tools through the `doctor` command.
## pkg-config Compilation (*nix only)
If you observe the compilation log when using static-php-cli to build static PHP, you will find that no matter what is compiled,
`pkg-config` will be compiled first. This is because `pkg-config` is a library used to find dependencies.
In earlier versions of static-php-cli, we directly used the `pkg-config` tool installed by package management,
but this would cause some problems, such as:
- Even if `PKG_CONFIG_PATH` is specified, `pkg-config` will try to find dependent packages from the system path.
- Since `pkg-config` will look for dependent packages from the system path,
if a dependent package with the same name exists in the system, compilation may fail.
In order to avoid the above problems, we compile `pkg-config` into `buildroot/bin` in user mode and use it.
We use parameters such as `--without-sysroot` to avoid looking for dependent packages from the system path.
<!-- TODO: Migrate and update from v2 system-build-tools.md.
Cover v3 additions: WindowsCMakeExecutor, vswhere.exe detection, LLVM/Clang for FrankenPHP. -->

View File

@@ -0,0 +1,6 @@
# Annotations Reference
<!-- TODO: Complete reference for all v3 PHP attributes.
#[Library], #[Extension], #[BuildFor], #[BeforeStage], #[AfterStage],
#[PatchBeforeBuild], #[CustomPhpConfigureArg], #[AsCheckItem], #[AsFixItem].
Per-attribute: parameters, types, allowed targets, example. -->

View File

@@ -0,0 +1,6 @@
# Dependency Injection
<!-- TODO: How PHP-DI autowiring works in v3.
ApplicationContext::get() usage.
Registering custom services.
Injecting into command classes, build classes, and stage methods. -->

View File

@@ -0,0 +1,6 @@
# Vendor Mode
<!-- TODO: What vendor mode is and when to use it.
Installation: `composer require crazywhalecc/static-php-cli`.
How to register an external registry pointing to your custom package classes.
Minimal working example: one Library class, one config YAML, run spc. -->

View File

@@ -0,0 +1,6 @@
# Lifecycle Hooks
<!-- TODO: Detailed explanation of hook execution order and method signatures.
#[BeforeStage('lib-name', 'build')], #[AfterStage(...)], #[PatchBeforeBuild].
How hooks from different packages are merged and ordered.
Common patterns: patching config.m4, injecting compile flags, post-install fixups. -->

View File

@@ -0,0 +1,6 @@
# Writing Package Classes
<!-- TODO: Step-by-step guide to writing a Library class and an Extension class.
Full annotated code examples using #[Library], #[Extension], #[BuildFor].
UnixAutoconfExecutor / UnixCmakeExecutor / WindowsCMakeExecutor usage.
File placement (src/Package/Library/, src/Package/Extension/) and autoloading. -->