Merge branch 'main' into feat/rdkafka-ssl

This commit is contained in:
Marc
2025-11-03 20:48:19 +01:00
committed by GitHub
30 changed files with 1124 additions and 49 deletions

View File

@@ -175,7 +175,7 @@ jobs:
key: php-dependencies-${{ matrix.os }} key: php-dependencies-${{ matrix.os }}
- name: "Install Dependencies" - name: "Install Dependencies"
run: composer update -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist run: composer update -vvv --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --no-plugins
- name: "Run Build Tests (doctor)" - name: "Run Build Tests (doctor)"
run: php src/globals/test-extensions.php doctor_cmd ${{ matrix.os }} ${{ matrix.php }} run: php src/globals/test-extensions.php doctor_cmd ${{ matrix.os }} ${{ matrix.php }}

View File

@@ -65,7 +65,7 @@ PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path ; upx executable path
UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe" UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches ; phpmicro patches, for more info, see: https://github.com/easysoft/phpmicro/tree/master/patches
SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static SPC_MICRO_PATCHES=static_extensions_win32,cli_checks,disable_huge_page,vcruntime140,win32,zend_stream,cli_static,win32_api
[linux] [linux]
; Linux can use different build toolchains. ; Linux can use different build toolchains.

View File

@@ -756,6 +756,18 @@
"apcu" "apcu"
] ]
}, },
"snmp": {
"support": {
"Windows": "wip",
"BSD": "wip"
},
"type": "builtin",
"arg-type-unix": "with",
"arg-type-windows": "with",
"lib-depends": [
"net-snmp"
]
},
"soap": { "soap": {
"support": { "support": {
"BSD": "wip" "BSD": "wip"
@@ -988,6 +1000,14 @@
"type": "builtin", "type": "builtin",
"build-with-php": true "build-with-php": true
}, },
"trader": {
"support": {
"BSD": "wip",
"Windows": "wip"
},
"type": "external",
"source": "ext-trader"
},
"uuid": { "uuid": {
"support": { "support": {
"Windows": "wip", "Windows": "wip",

View File

@@ -611,8 +611,8 @@
}, },
"libxml2": { "libxml2": {
"source": "libxml2", "source": "libxml2",
"static-libs-unix": [ "pkg-configs": [
"libxml2.a" "libxml-2.0"
], ],
"static-libs-windows": [ "static-libs-windows": [
"libxml2s.lib", "libxml2s.lib",
@@ -701,6 +701,17 @@
"libncurses.a" "libncurses.a"
] ]
}, },
"net-snmp": {
"source": "net-snmp",
"pkg-configs": [
"netsnmp",
"netsnmp-agent"
],
"lib-depends": [
"openssl",
"zlib"
]
},
"nghttp2": { "nghttp2": {
"source": "nghttp2", "source": "nghttp2",
"static-libs-unix": [ "static-libs-unix": [

View File

@@ -233,6 +233,16 @@
"path": "LICENSE" "path": "LICENSE"
} }
}, },
"ext-trader": {
"type": "url",
"url": "https://pecl.php.net/get/trader",
"path": "php-src/ext/trader",
"filename": "trader.tgz",
"license": {
"type": "file",
"path": "LICENSE"
}
},
"ext-uuid": { "ext-uuid": {
"type": "url", "type": "url",
"url": "https://pecl.php.net/get/uuid", "url": "https://pecl.php.net/get/uuid",
@@ -762,7 +772,7 @@
"micro": { "micro": {
"type": "git", "type": "git",
"path": "php-src/sapi/micro", "path": "php-src/sapi/micro",
"rev": "php-85-win", "rev": "master",
"url": "https://github.com/static-php/phpmicro", "url": "https://github.com/static-php/phpmicro",
"license": { "license": {
"type": "file", "type": "file",
@@ -810,6 +820,14 @@
"path": "COPYING" "path": "COPYING"
} }
}, },
"net-snmp": {
"type": "ghtagtar",
"repo": "net-snmp/net-snmp",
"license": {
"type": "file",
"path": "COPYING"
}
},
"nghttp2": { "nghttp2": {
"type": "ghrel", "type": "ghrel",
"repo": "nghttp2/nghttp2", "repo": "nghttp2/nghttp2",

View File

@@ -744,10 +744,26 @@ h2 {
} }
select { select {
border-radius: 4px; border-radius: 8px;
border: 1px solid var(--vp-c-divider); border: 1px solid var(--vp-c-divider);
padding: 0 4px; padding: 8px 12px;
width: 300px; width: 300px;
background-color: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 14px;
transition: all 0.2s ease;
cursor: pointer;
outline: none;
}
select:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg);
}
select:focus {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
} }
.my-btn { .my-btn {
@@ -781,17 +797,160 @@ select {
.textarea { .textarea {
border: 1px solid var(--vp-c-divider); border: 1px solid var(--vp-c-divider);
border-radius: 4px; border-radius: 8px;
width: calc(100% - 12px); width: calc(100% - 24px);
padding: 4px 8px; padding: 12px;
background-color: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 14px;
font-family: var(--vp-font-family-mono);
line-height: 1.5;
transition: all 0.2s ease;
outline: none;
resize: vertical;
}
.textarea:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg);
}
.textarea:focus {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
} }
.input { .input {
display: block; display: block;
border: 1px solid var(--vp-c-divider); border: 1px solid var(--vp-c-divider);
border-radius: 4px; border-radius: 8px;
width: 100%; width: 100%;
padding: 4px 8px; padding: 10px 12px;
background-color: var(--vp-c-bg-soft);
color: var(--vp-c-text-1);
font-size: 14px;
transition: all 0.2s ease;
outline: none;
box-sizing: border-box;
}
.input:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg);
}
.input:focus {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 3px var(--vp-c-brand-soft);
}
/* Radio button styles */
input[type="radio"] {
appearance: none;
width: 18px;
height: 18px;
border: 2px solid var(--vp-c-border);
border-radius: 50%;
background-color: var(--vp-c-bg);
cursor: pointer;
position: relative;
vertical-align: middle;
margin-right: 6px;
transition: all 0.2s ease;
}
input[type="radio"]:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg-soft);
}
input[type="radio"]:checked {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-1);
}
input[type="radio"]:checked::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--vp-c-bg);
}
input[type="radio"]:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Checkbox styles */
input[type="checkbox"] {
appearance: none;
width: 18px;
height: 18px;
border: 2px solid var(--vp-c-border);
border-radius: 4px;
background-color: var(--vp-c-bg);
cursor: pointer;
position: relative;
vertical-align: middle;
margin-right: 6px;
transition: all 0.2s ease;
}
input[type="checkbox"]:hover {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-bg-soft);
}
input[type="checkbox"]:checked {
border-color: var(--vp-c-brand-1);
background-color: var(--vp-c-brand-1);
}
input[type="checkbox"]:checked::after {
content: '';
position: absolute;
top: 2px;
left: 5px;
width: 4px;
height: 8px;
border: solid var(--vp-c-bg);
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
input[type="checkbox"]:disabled {
opacity: 0.5;
cursor: not-allowed;
}
/* Label styles */
label {
cursor: pointer;
user-select: none;
color: var(--vp-c-text-1);
font-size: 14px;
line-height: 1.5;
transition: color 0.2s ease;
}
label:hover {
color: var(--vp-c-brand-1);
}
input[type="radio"]:disabled + label,
input[type="checkbox"]:disabled + label {
opacity: 0.5;
cursor: not-allowed;
}
input[type="radio"]:disabled + label:hover,
input[type="checkbox"]:disabled + label:hover {
color: var(--vp-c-text-1);
} }
.command-container { .command-container {

View File

@@ -0,0 +1,219 @@
<template>
<div class="contributors-container">
<div class="contributors-header">
<h2>Contributors</h2>
<p class="contributors-description">
Thanks to all the amazing people who have contributed to this project!
</p>
</div>
<div v-if="loading" class="loading-state">
<div class="spinner"></div>
<p>Loading contributors...</p>
</div>
<div v-else-if="error" class="error-state">
<p>{{ error }}</p>
</div>
<div v-else class="contributors-grid">
<a
v-for="contributor in contributors"
:key="contributor.id"
:href="contributor.html_url"
target="_blank"
rel="noopener noreferrer"
class="contributor-card"
:title="contributor.login"
>
<img
:src="contributor.avatar_url"
:alt="contributor.login"
class="contributor-avatar"
loading="lazy"
/>
<div class="contributor-name">{{ contributor.login }}</div>
</a>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
interface Contributor {
id: number;
login: string;
avatar_url: string;
html_url: string;
contributions: number;
}
const contributors = ref<Contributor[]>([]);
const loading = ref(true);
const error = ref('');
const fetchContributors = async () => {
try {
loading.value = true;
error.value = '';
const response = await fetch(
'https://api.github.com/repos/crazywhalecc/static-php-cli/contributors?per_page=24'
);
if (!response.ok) {
throw new Error('Failed to fetch contributors');
}
const data = await response.json();
contributors.value = data;
} catch (err) {
error.value = 'Failed to load contributors. Please try again later.';
console.error('Error fetching contributors:', err);
} finally {
loading.value = false;
}
};
onMounted(() => {
fetchContributors();
});
</script>
<style scoped>
.contributors-container {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
}
.contributors-header {
text-align: center;
margin-bottom: 24px;
}
.contributors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.contributors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
.loading-state,
.error-state {
text-align: center;
padding: 40px 20px;
color: var(--vp-c-text-2);
}
.spinner {
width: 40px;
height: 40px;
margin: 0 auto 16px;
border: 4px solid var(--vp-c-divider);
border-top-color: var(--vp-c-brand-1);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
.contributors-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
gap: 16px;
}
.contributor-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 12px;
background: var(--vp-c-bg);
border-radius: 12px;
border: 1px solid var(--vp-c-divider);
transition: all 0.3s ease;
text-decoration: none;
color: var(--vp-c-text-1);
}
.contributor-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
border-color: var(--vp-c-brand-1);
}
.contributor-avatar {
width: 60px;
height: 60px;
border-radius: 50%;
border: 2px solid var(--vp-c-divider);
transition: all 0.3s ease;
margin-bottom: 8px;
}
.contributor-card:hover .contributor-avatar {
border-color: var(--vp-c-brand-1);
transform: scale(1.05);
}
.contributor-name {
font-size: 12px;
font-weight: 500;
text-align: center;
word-break: break-word;
max-width: 100%;
}
@media (max-width: 768px) {
.contributors-container {
margin: 32px 16px;
padding: 24px 16px;
}
.contributors-header h2 {
font-size: 1.25rem;
}
.contributors-description {
font-size: 0.9rem;
}
.contributors-grid {
grid-template-columns: repeat(auto-fill, minmax(70px, 1fr));
gap: 12px;
}
.contributor-card {
padding: 8px;
}
.contributor-avatar {
width: 48px;
height: 48px;
}
.contributor-name {
font-size: 11px;
}
}
</style>

View File

@@ -4,7 +4,7 @@ import sidebarZh from "./sidebar.zh";
// https://vitepress.dev/reference/site-config // https://vitepress.dev/reference/site-config
export default { export default {
title: "static-php-cli", title: "Static PHP",
description: "Build single static PHP binary, with PHP project together, with popular extensions included.", description: "Build single static PHP binary, with PHP project together, with popular extensions included.",
locales: { locales: {
en: { en: {
@@ -44,6 +44,7 @@ export default {
}, },
themeConfig: { themeConfig: {
// https://vitepress.dev/reference/default-theme-config // https://vitepress.dev/reference/default-theme-config
logo: '/images/static-php_nobg.png',
nav: [], nav: [],
socialLinks: [ socialLinks: [
{icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'} {icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'}

View File

@@ -4,3 +4,21 @@
max-width: 1000px !important; max-width: 1000px !important;
} }
.vp-doc .contributors-header h2 {
padding-top: 0;
border-top: none;
}
.vp-doc .sponsors-header h2 {
padding-top: 0;
border-top: none;
}
.dark .VPImage.logo {
filter: contrast(0.7);
}
.dark .VPImage.image-src {
filter: contrast(0.7);
}

View File

@@ -44,6 +44,11 @@ So on macOS, you can **directly** use SPC to build statically compiled PHP binar
2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`. 2. You will get `buildroot/modules/xdebug.so` and `buildroot/bin/php`.
3. The `xdebug.so` file could be used for php that version and thread-safe are the same. 3. The `xdebug.so` file could be used for php that version and thread-safe are the same.
For the Windows platform, since officially built extensions (such as `php_yaml.dll`) force the use of the `php8.dll` dynamic library as a link, and statically built PHP does not include any dynamic libraries other than system libraries,
php.exe built by static-php cannot load officially built dynamic extensions. Since static-php-cli does not yet support building dynamic extensions, there is currently no way to load dynamic extensions with static-php.
However, Windows can normally use the `FFI` extension to load other dll files and call them.
## Can it support Oracle database extension? ## Can it support Oracle database extension?
Some extensions that rely on closed source libraries, such as `oci8`, `sourceguardian`, etc., Some extensions that rely on closed source libraries, such as `oci8`, `sourceguardian`, etc.,

View File

@@ -3,11 +3,14 @@
layout: home layout: home
hero: hero:
name: "static-php-cli" name: "Static PHP"
tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included." tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included."
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions: actions:
- theme: brand - theme: brand
text: Guide text: Get Started
link: ./guide/ link: ./guide/
features: features:
@@ -19,3 +22,121 @@ features:
details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions. details: static-php-cli comes with dependency management and supports installation of different types of PHP extensions.
--- ---
<script setup>
import {VPSponsors} from "vitepress/theme";
import Contributors from '../.vitepress/components/Contributors.vue';
const sponsors = [
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
];
</script>
<div class="sponsors-section">
<div class="sponsors-header">
<h2>Special Sponsors</h2>
<p class="sponsors-description">
Thank you to our amazing sponsors for supporting this project!
</p>
</div>
<VPSponsors :data="sponsors"/>
</div>
<style scoped>
.sponsors-section {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.sponsors-section:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.sponsors-header {
text-align: center;
margin-bottom: 24px;
}
.sponsors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sponsors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
@media (max-width: 768px) {
.sponsors-section {
margin: 32px 16px;
padding: 24px 16px;
}
.sponsors-header h2 {
font-size: 1.25rem;
}
.sponsors-description {
font-size: 0.9rem;
}
}
/* Hero logo styling */
:deep(.VPImage.image-src) {
border-radius: 20px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
:deep(.VPImage.image-src:hover) {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
/* Dark mode adjustments for logo */
.dark :deep(.VPImage.image-src) {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
opacity: 0.9;
}
.dark :deep(.VPImage.image-src:hover) {
opacity: 1;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
}
/* Additional styling for the logo image itself */
:deep(.VPImage.image-src img) {
max-height: 280px;
width: auto;
}
@media (max-width: 768px) {
:deep(.VPImage.image-src) {
padding: 24px;
}
:deep(.VPImage.image-src img) {
max-height: 200px;
}
}
</style>
<Contributors />

View File

@@ -3,8 +3,11 @@
layout: home layout: home
hero: hero:
name: "static-php-cli" name: "Static PHP"
tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included." tagline: "Build standalone PHP binary on Linux, macOS, FreeBSD, Windows, with PHP project together, with popular extensions included."
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions: actions:
- theme: brand - theme: brand
text: Get Started text: Get Started
@@ -24,13 +27,121 @@ features:
<script setup> <script setup>
import {VPSponsors} from "vitepress/theme"; import {VPSponsors} from "vitepress/theme";
import Contributors from './.vitepress/components/Contributors.vue';
const sponsors = [ const sponsors = [
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' }, { name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' }, { name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
]; ];
</script> </script>
## Special Sponsors <div class="sponsors-section">
<div class="sponsors-header">
<h2>Special Sponsors</h2>
<p class="sponsors-description">
Thank you to our amazing sponsors for supporting this project!
</p>
</div>
<VPSponsors :data="sponsors"/>
</div>
<VPSponsors :data="sponsors"/> <style scoped>
.sponsors-section {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.sponsors-section:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.sponsors-header {
text-align: center;
margin-bottom: 24px;
}
.sponsors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sponsors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
@media (max-width: 768px) {
.sponsors-section {
margin: 32px 16px;
padding: 24px 16px;
}
.sponsors-header h2 {
font-size: 1.25rem;
}
.sponsors-description {
font-size: 0.9rem;
}
}
/* Hero logo styling */
:deep(.VPImage.image-src) {
border-radius: 20px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
:deep(.VPImage.image-src:hover) {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
/* Dark mode adjustments for logo */
.dark :deep(.VPImage.image-src) {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
opacity: 0.9;
}
.dark :deep(.VPImage.image-src:hover) {
opacity: 1;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
}
/* Additional styling for the logo image itself */
:deep(.VPImage.image-src img) {
max-height: 280px;
width: auto;
}
@media (max-width: 768px) {
:deep(.VPImage.image-src) {
padding: 24px;
}
:deep(.VPImage.image-src img) {
max-height: 200px;
}
}
</style>
<Contributors />

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 KiB

View File

@@ -41,6 +41,11 @@ buildroot/bin/php -d "zend_extension=/path/to/php{PHP_VER}-{ts/nts}/xdebug.so" -
2. 你将获得 `buildroot/modules/xdebug.so``buildroot/bin/php` 2. 你将获得 `buildroot/modules/xdebug.so``buildroot/bin/php`
3. `xdebug.so` 文件可用于版本和线程安全相同的 php。 3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
对于 Windows 平台,由于官方构建的扩展(如 `php_yaml.dll`)强制使用了 `php8.dll` 动态库作为链接,静态构建的 PHP 不包含任何系统库以外的动态库,
所以 Windows 下无法加载官方构建的动态扩展。 由于 static-php-cli 还暂未支持构建动态扩展,所以目前还没有让 static-php 加载动态扩展的方法。
不过Windows 可以正常使用 `FFI` 扩展加载其他的 dll 文件并调用。
## 可以支持 Oracle 数据库扩展吗? ## 可以支持 Oracle 数据库扩展吗?
部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so` 部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so`

View File

@@ -3,18 +3,140 @@
layout: home layout: home
hero: hero:
name: "static-php-cli" name: "Static PHP"
tagline: "在 Linux、macOS、FreeBSD、Windows 上与 PHP 项目一起构建独立的 PHP 二进制文件,并包含流行的扩展。" tagline: "在 Linux、macOS、FreeBSD、Windows 上与 PHP 项目一起构建独立的 PHP 二进制文件,并包含流行的扩展。"
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions: actions:
- theme: brand - theme: brand
text: 指南 text: 开始使用
link: ./guide/ link: ./guide/
features: features:
- title: 静态二进制 - title: 静态 CLI 二进制
details: 您可以轻松地编译一个独立的 PHP 二进制文件以供嵌入程序使用包括 cli、fpm、micro details: 您可以轻松地编译一个独立的 PHP 二进制文件以供通用使用包括 CLI、FPM SAPI
- title: phpmicro 自执行二进制 - title: Micro 自解压可执行文件
details: 您可以使用 micro SAPI 编译一个自解压的可执行文件,并将 PHP 代码与二进制文件打包为一个文件 details: 您可以编译一个自解压的可执行文件,并将 PHP 代码与二进制文件打包在一起
- title: 依赖管理 - title: 依赖管理
details: static-php-cli 附带依赖项管理,支持安装不同类型的 PHP 扩展和不同的依赖库 details: static-php-cli 附带依赖项管理,支持安装不同类型的 PHP 扩展。
--- ---
<script setup>
import {VPSponsors} from "vitepress/theme";
import Contributors from '../.vitepress/components/Contributors.vue';
const sponsors = [
{ name: 'Beyond Code', img: '/images/beyondcode-seeklogo.png', url: 'https://beyondco.de/' },
{ name: 'NativePHP', img: '/images/nativephp-logo.svg', url: 'https://nativephp.com/' },
];
</script>
<div class="sponsors-section">
<div class="sponsors-header">
<h2>特别赞助商</h2>
<p class="sponsors-description">
感谢我们出色的赞助商对本项目的支持!
</p>
</div>
<VPSponsors :data="sponsors"/>
</div>
<style scoped>
.sponsors-section {
margin: 48px auto;
padding: 32px 24px;
max-width: 1152px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-bg) 100%);
border-radius: 16px;
border: 1px solid var(--vp-c-divider);
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
}
.sponsors-section:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
.sponsors-header {
text-align: center;
margin-bottom: 24px;
}
.sponsors-header h2 {
font-size: 1.5rem;
font-weight: 700;
margin: 0 0 8px 0;
background: linear-gradient(120deg, var(--vp-c-brand-1), var(--vp-c-brand-2));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.sponsors-description {
font-size: 0.95rem;
color: var(--vp-c-text-2);
margin: 0;
line-height: 1.5;
}
@media (max-width: 768px) {
.sponsors-section {
margin: 32px 16px;
padding: 24px 16px;
}
.sponsors-header h2 {
font-size: 1.25rem;
}
.sponsors-description {
font-size: 0.9rem;
}
}
/* Hero logo styling */
:deep(.VPImage.image-src) {
border-radius: 20px;
background: linear-gradient(135deg, var(--vp-c-bg-soft) 0%, var(--vp-c-default-soft) 100%);
padding: 40px;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
}
:deep(.VPImage.image-src:hover) {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
}
/* Dark mode adjustments for logo */
.dark :deep(.VPImage.image-src) {
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
opacity: 0.9;
}
.dark :deep(.VPImage.image-src:hover) {
opacity: 1;
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.7);
}
/* Additional styling for the logo image itself */
:deep(.VPImage.image-src img) {
max-height: 280px;
width: auto;
}
@media (max-width: 768px) {
:deep(.VPImage.image-src) {
padding: 24px;
}
:deep(.VPImage.image-src img) {
max-height: 200px;
}
}
</style>
<Contributors />

View File

@@ -34,7 +34,7 @@ use Symfony\Component\Console\Application;
*/ */
final class ConsoleApplication extends Application final class ConsoleApplication extends Application
{ {
public const string VERSION = '2.7.5'; public const string VERSION = '2.7.6';
public function __construct() public function __construct()
{ {

View File

@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace SPC\builder\extension;
use SPC\builder\Extension;
use SPC\store\FileSystem;
use SPC\util\CustomExt;
use SPC\util\PkgConfigUtil;
#[CustomExt('snmp')]
class snmp extends Extension
{
public function patchBeforeBuildconf(): bool
{
// Overwrite m4 config using newer PHP version
if ($this->builder->getPHPVersionID() < 80400) {
FileSystem::copy(ROOT_DIR . '/src/globals/extra/snmp-ext-config-old.m4', "{$this->source_dir}/config.m4");
}
$libs = implode(' ', PkgConfigUtil::getLibsArray('netsnmp'));
FileSystem::replaceFileStr(
"{$this->source_dir}/config.m4",
'PHP_EVAL_LIBLINE([$SNMP_LIBS], [SNMP_SHARED_LIBADD])',
"SNMP_LIBS=\"{$libs}\"\nPHP_EVAL_LIBLINE([\$SNMP_LIBS], [SNMP_SHARED_LIBADD])"
);
return true;
}
}

View File

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

View File

@@ -78,7 +78,7 @@ class openssl extends LinuxLibraryBase
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
} }
} }

View File

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

View File

@@ -63,7 +63,7 @@ class openssl extends MacOSLibraryBase
if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) { if (!str_contains($file = FileSystem::readFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc'), 'prefix=')) {
FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file); FileSystem::writeFile(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', 'prefix=' . BUILD_ROOT_PATH . "\n" . $file);
} }
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Libs.private: ${libdir}/libz.a'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/pkgconfig/libcrypto.pc', '/Libs.private:.*/m', 'Requires.private: zlib');
FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")'); FileSystem::replaceFileRegex(BUILD_LIB_PATH . '/cmake/OpenSSL/OpenSSLConfig.cmake', '/set\(OPENSSL_LIBCRYPTO_DEPENDENCIES .*\)/m', 'set(OPENSSL_LIBCRYPTO_DEPENDENCIES "${OPENSSL_LIBRARY_DIR}/libz.a")');
} }
} }

View File

@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait net_snmp
{
public function patchBeforeBuild(): bool
{
if (PHP_OS_FAMILY === 'Linux') {
FileSystem::replaceFileStr("{$this->source_dir}/configure", 'LIBS="-lssl ${OPENSSL_LIBS}"', 'LIBS="-lssl ${OPENSSL_LIBS} -lpthread -ldl"');
return true;
}
return false;
}
protected function build(): void
{
// use --static for PKG_CONFIG
UnixAutoconfExecutor::create($this)
->setEnv(['PKG_CONFIG' => getenv('PKG_CONFIG') . ' --static'])
->configure(
'--disable-mibs',
'--without-nl',
'--disable-agent',
'--disable-applications',
'--disable-manuals',
'--disable-scripts',
'--disable-embedded-perl',
'--without-perl-modules',
'--with-out-mib-modules="if-mib host disman/event-mib ucd-snmp/diskio mibII"',
'--with-out-transports="Unix"',
'--with-mib-modules=""',
'--enable-mini-agent',
'--with-default-snmp-version="3"',
'--with-sys-contact="@@no.where"',
'--with-sys-location="Unknown"',
'--with-logfile="/var/log/snmpd.log"',
'--with-persistent-directory="/var/lib/net-snmp"',
'--with-openssl=' . BUILD_ROOT_PATH,
'--with-zlib=' . BUILD_ROOT_PATH,
)->make(with_install: 'installheaders installlibs install_pkgconfig');
$this->patchPkgconfPrefix();
}
}

View File

@@ -45,8 +45,8 @@ trait postgresql
protected function build(): void protected function build(): void
{ {
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies()); $libs = array_map(fn ($x) => $x->getName(), $this->getDependencies(true));
$spc = new SPCConfigUtil($this->getBuilder(), ['no_php' => true, 'libs_only_deps' => true]); $spc = new SPCConfigUtil($this->builder, ['no_php' => true, 'libs_only_deps' => true]);
$config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs')); $config = $spc->config(libraries: $libs, include_suggest_lib: $this->builder->getOption('with-suggested-libs'));
$env_vars = [ $env_vars = [

View File

@@ -57,6 +57,7 @@ class WindowsBuilder extends BuilderBase
$enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM; $enableFpm = ($build_target & BUILD_TARGET_FPM) === BUILD_TARGET_FPM;
$enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO; $enableMicro = ($build_target & BUILD_TARGET_MICRO) === BUILD_TARGET_MICRO;
$enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED; $enableEmbed = ($build_target & BUILD_TARGET_EMBED) === BUILD_TARGET_EMBED;
$enableCgi = ($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI;
SourcePatcher::patchBeforeBuildconf($this); SourcePatcher::patchBeforeBuildconf($this);
@@ -102,13 +103,13 @@ class WindowsBuilder extends BuilderBase
->exec( ->exec(
"{$this->sdk_prefix} configure.bat --task-args \"" . "{$this->sdk_prefix} configure.bat --task-args \"" .
'--disable-all ' . '--disable-all ' .
'--disable-cgi ' .
'--with-php-build=' . BUILD_ROOT_PATH . ' ' . '--with-php-build=' . BUILD_ROOT_PATH . ' ' .
'--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' . '--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' .
'--with-extra-libs=' . BUILD_LIB_PATH . ' ' . '--with-extra-libs=' . BUILD_LIB_PATH . ' ' .
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') . ($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') . ($enableMicro ? ('--enable-micro ' . $micro_logo . $micro_w32) : '--disable-micro ') .
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') . ($enableEmbed ? '--enable-embed ' : '--disable-embed ') .
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_scan_dir . $config_file_scan_dir .
$opcache_jit_arg . $opcache_jit_arg .
"{$this->makeStaticExtensionArgs()} " . "{$this->makeStaticExtensionArgs()} " .
@@ -127,6 +128,10 @@ class WindowsBuilder extends BuilderBase
if ($enableFpm) { if ($enableFpm) {
logger()->warning('Windows does not support fpm SAPI, I will skip it.'); logger()->warning('Windows does not support fpm SAPI, I will skip it.');
} }
if ($enableCgi) {
logger()->info('building cgi');
$this->buildCgi();
}
if ($enableMicro) { if ($enableMicro) {
logger()->info('building micro'); logger()->info('building micro');
$this->buildMicro(); $this->buildMicro();
@@ -159,6 +164,20 @@ class WindowsBuilder extends BuilderBase
$this->deployBinary(BUILD_TARGET_CLI); $this->deployBinary(BUILD_TARGET_CLI);
} }
public function buildCgi(): void
{
SourcePatcher::patchWindowsCGITarget();
$extra_libs = getenv('SPC_EXTRA_LIBS') ?: '';
// add nmake wrapper
FileSystem::writeFile(SOURCE_PATH . '\php-src\nmake_cgi_wrapper.bat', "nmake /nologo LIBS_CGI=\"ws2_32.lib kernel32.lib advapi32.lib {$extra_libs}\" EXTRA_LD_FLAGS_PROGRAM= %*");
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cgi_wrapper.bat --task-args php-cgi.exe");
$this->deployBinary(BUILD_TARGET_CGI);
}
public function buildEmbed(): void public function buildEmbed(): void
{ {
// TODO: add embed support for windows // TODO: add embed support for windows
@@ -265,7 +284,7 @@ class WindowsBuilder extends BuilderBase
// sanity check for php-cli // sanity check for php-cli
if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) { if (($build_target & BUILD_TARGET_CLI) === BUILD_TARGET_CLI) {
logger()->info('running cli sanity check'); logger()->info('running cli sanity check');
[$ret, $output] = cmd()->execWithResult(BUILD_ROOT_PATH . '\bin\php.exe -n -r "echo \"hello\";"'); [$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php.exe -n -r "echo \"hello\";"');
if ($ret !== 0 || trim(implode('', $output)) !== 'hello') { if ($ret !== 0 || trim(implode('', $output)) !== 'hello') {
throw new ValidationException('cli failed sanity check', validation_module: 'php-cli function check'); throw new ValidationException('cli failed sanity check', validation_module: 'php-cli function check');
} }
@@ -284,7 +303,7 @@ class WindowsBuilder extends BuilderBase
if (file_exists($test_file)) { if (file_exists($test_file)) {
@unlink($test_file); @unlink($test_file);
} }
file_put_contents($test_file, file_get_contents(BUILD_ROOT_PATH . '\bin\micro.sfx') . $task['content']); file_put_contents($test_file, file_get_contents(BUILD_BIN_PATH . '\micro.sfx') . $task['content']);
chmod($test_file, 0755); chmod($test_file, 0755);
[$ret, $out] = cmd()->execWithResult($test_file); [$ret, $out] = cmd()->execWithResult($test_file);
foreach ($task['conditions'] as $condition => $closure) { foreach ($task['conditions'] as $condition => $closure) {
@@ -298,6 +317,17 @@ class WindowsBuilder extends BuilderBase
} }
} }
} }
// sanity check for php-cgi
if (($build_target & BUILD_TARGET_CGI) === BUILD_TARGET_CGI) {
logger()->info('running cgi sanity check');
FileSystem::writeFile(SOURCE_PATH . '\php-cgi-test.php', '<?php echo "<h1>Hello, World!</h1>"; ?>');
[$ret, $output] = cmd()->execWithResult(BUILD_BIN_PATH . '\php-cgi.exe -n -f ' . SOURCE_PATH . '\php-cgi-test.php');
$raw_output = implode("\n", $output);
if ($ret !== 0 || !str_contains($raw_output, 'Hello, World!')) {
throw new ValidationException("cgi failed sanity check. code: {$ret}, output: {$raw_output}", validation_module: 'php-cgi sanity check');
}
}
} }
/** /**
@@ -311,20 +341,21 @@ class WindowsBuilder extends BuilderBase
$src = match ($type) { $src = match ($type) {
BUILD_TARGET_CLI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php.exe", BUILD_TARGET_CLI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php.exe",
BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx", BUILD_TARGET_MICRO => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\micro.sfx",
BUILD_TARGET_CGI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php-cgi.exe",
default => throw new SPCInternalException("Deployment does not accept type {$type}"), default => throw new SPCInternalException("Deployment does not accept type {$type}"),
}; };
// with-upx-pack for cli and micro // with-upx-pack for cli and micro
if ($this->getOption('with-upx-pack', false)) { if ($this->getOption('with-upx-pack', false)) {
if ($type === BUILD_TARGET_CLI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) { if ($type === BUILD_TARGET_CLI || $type === BUILD_TARGET_CGI || ($type === BUILD_TARGET_MICRO && version_compare($this->getMicroVersion(), '0.2.0') >= 0)) {
cmd()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($src)); cmd()->exec(getenv('UPX_EXEC') . ' --best ' . escapeshellarg($src));
} }
} }
logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file'); logger()->info('Deploying ' . $this->getBuildTypeName($type) . ' file');
FileSystem::createDir(BUILD_ROOT_PATH . '\bin'); FileSystem::createDir(BUILD_BIN_PATH);
cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_ROOT_PATH . '\bin\\')); cmd()->exec('copy ' . escapeshellarg($src) . ' ' . escapeshellarg(BUILD_BIN_PATH . '\\'));
return true; return true;
} }

View File

@@ -33,7 +33,7 @@ class BuildPHPCommand extends BuildCommand
$this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)'); $this->addOption('build-fpm', null, null, 'Build fpm SAPI (not available on Windows)');
$this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)'); $this->addOption('build-embed', null, null, 'Build embed SAPI (not available on Windows)');
$this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)'); $this->addOption('build-frankenphp', null, null, 'Build FrankenPHP SAPI (not available on Windows)');
$this->addOption('build-cgi', null, null, 'Build cgi SAPI (not available on Windows)'); $this->addOption('build-cgi', null, null, 'Build cgi SAPI');
$this->addOption('build-all', null, null, 'Build all SAPI'); $this->addOption('build-all', null, null, 'Build all SAPI');
$this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug'); $this->addOption('no-strip', null, null, 'build without strip, keep symbols to debug');
$this->addOption('disable-opcache-jit', null, null, 'disable opcache jit'); $this->addOption('disable-opcache-jit', null, null, 'disable opcache jit');

View File

@@ -40,7 +40,7 @@ class LinuxToolCheckList
'tar', 'unzip', 'gzip', 'gcc', 'g++', 'tar', 'unzip', 'gzip', 'gcc', 'g++',
'bzip2', 'cmake', 'patch', 'which', 'bzip2', 'cmake', 'patch', 'which',
'xz', 'libtool', 'gettext-devel', 'xz', 'libtool', 'gettext-devel',
'patchelf', 'patchelf', 'file',
]; ];
public const TOOLS_ARCH = [ public const TOOLS_ARCH = [

View File

@@ -549,6 +549,39 @@ class SourcePatcher
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines)); FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
} }
/**
* Patch cgi SAPI Makefile for Windows.
*/
public static function patchWindowsCGITarget(): void
{
// search Makefile code line contains "$(BUILD_DIR)\php-cgi.exe:"
$content = FileSystem::readFile(SOURCE_PATH . '/php-src/Makefile');
$lines = explode("\r\n", $content);
$line_num = 0;
$found = false;
foreach ($lines as $v) {
if (str_contains($v, '$(BUILD_DIR)\php-cgi.exe:')) {
$found = $line_num;
break;
}
++$line_num;
}
if ($found === false) {
throw new PatchException('Windows Makefile patching for php-cgi.exe target', 'Cannot patch windows CGI Makefile, Makefile does not contain "$(BUILD_DIR)\php-cgi.exe:" line');
}
// cli: $(BUILD_DIR)\php.exe: $(DEPS_CLI) $(CLI_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest
// $lines[$line_num] = '$(BUILD_DIR)\php.exe: generated_files $(DEPS_CLI) $(CLI_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php.exe.res $(BUILD_DIR)\php.exe.manifest';
// cgi: $(BUILD_DIR)\php-cgi.exe: $(DEPS_CGI) $(CGI_GLOBAL_OBJS) $(BUILD_DIR)\$(PHPLIB) $(BUILD_DIR)\php-cgi.exe.res $(BUILD_DIR)\php-cgi.exe.manifest
$lines[$line_num] = '$(BUILD_DIR)\php-cgi.exe: $(DEPS_CGI) $(CGI_GLOBAL_OBJS) $(PHP_GLOBAL_OBJS) $(STATIC_EXT_OBJS) $(ASM_OBJS) $(BUILD_DIR)\php-cgi.exe.res $(BUILD_DIR)\php-cgi.exe.manifest';
// cli: @"$(LINK)" /nologo $(CGI_GLOBAL_OBJS_RESP) $(BUILD_DIR)\$(PHPLIB) $(LIBS_CGI) $(BUILD_DIR)\php-cgi.exe.res /out:$(BUILD_DIR)\php-cgi.exe $(LDFLAGS) $(LDFLAGS_CGI)
$lines[$line_num + 1] = "\t" . '@"$(LINK)" /nologo $(PHP_GLOBAL_OBJS_RESP) $(CGI_GLOBAL_OBJS_RESP) $(STATIC_EXT_OBJS_RESP) $(STATIC_EXT_LIBS) $(ASM_OBJS) $(LIBS) $(LIBS_CGI) $(BUILD_DIR)\php-cgi.exe.res /out:$(BUILD_DIR)\php-cgi.exe $(LDFLAGS) $(LDFLAGS_CGI) /ltcg /nodefaultlib:msvcrt /nodefaultlib:msvcrtd /ignore:4286';
FileSystem::writeFile(SOURCE_PATH . '/php-src/Makefile', implode("\r\n", $lines));
// Patch cgi-static, comment ZEND_TSRMLS_CACHE_DEFINE()
FileSystem::replaceFileRegex(SOURCE_PATH . '\php-src\sapi\cgi\cgi_main.c', '/^ZEND_TSRMLS_CACHE_DEFINE\(\)/m', '// ZEND_TSRMLS_CACHE_DEFINE()');
}
public static function patchPhpLibxml212(): bool public static function patchPhpLibxml212(): bool
{ {
$file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h'); $file = file_get_contents(SOURCE_PATH . '/php-src/main/php_version.h');

View File

@@ -115,6 +115,12 @@ class UnixAutoconfExecutor extends Executor
return $this; return $this;
} }
public function setEnv(array $env): static
{
$this->shell->setEnv($env);
return $this;
}
public function appendEnv(array $env): static public function appendEnv(array $env): static
{ {
$this->shell->appendEnv($env); $this->shell->appendEnv($env);

View File

@@ -0,0 +1,92 @@
PHP_ARG_WITH([snmp],
[for SNMP support],
[AS_HELP_STRING([[--with-snmp[=DIR]]],
[Include SNMP support. Use PKG_CONFIG_PATH (or SNMP_CFLAGS and SNMP_LIBS)
environment variables, or alternatively the optional DIR argument to
customize where to look for the net-snmp-config utility of the NET-SNMP
library.])])
if test "$PHP_SNMP" != "no"; then
snmp_found=no
AS_VAR_IF([PHP_SNMP], [yes],
[PKG_CHECK_MODULES([SNMP], [netsnmp >= 5.3], [snmp_found=yes], [:])])
AS_VAR_IF([snmp_found], [no], [
AS_VAR_IF([PHP_SNMP], [yes],
[AC_PATH_PROG([SNMP_CONFIG], [net-snmp-config],, [/usr/local/bin:$PATH])],
[SNMP_CONFIG="$PHP_SNMP/bin/net-snmp-config"])
AS_IF([test ! -x "$SNMP_CONFIG"],
[AC_MSG_ERROR(m4_text_wrap([
Could not find net-snmp-config binary. Please check your net-snmp
installation.
]))])
snmp_version=$($SNMP_CONFIG --version)
AS_VERSION_COMPARE([$snmp_version], [5.3],
[AC_MSG_ERROR(m4_text_wrap([
Net-SNMP version 5.3 or greater required (detected $snmp_version).
]))])
SNMP_PREFIX=$($SNMP_CONFIG --prefix)
SNMP_CFLAGS="-I${SNMP_PREFIX}/include"
SNMP_LIBS=$($SNMP_CONFIG --netsnmp-libs)
SNMP_LIBS="$SNMP_LIBS $($SNMP_CONFIG --external-libs)"
AS_IF([test -z "$SNMP_LIBS" || test -z "$SNMP_PREFIX"],
[AC_MSG_ERROR(m4_text_wrap([
Could not find the required paths. Please check your net-snmp
installation.
]))])
])
PHP_EVAL_INCLINE([$SNMP_CFLAGS])
PHP_EVAL_LIBLINE([$SNMP_LIBS], [SNMP_SHARED_LIBADD])
SNMP_LIBNAME=netsnmp
dnl Test build.
PHP_CHECK_LIBRARY([$SNMP_LIBNAME], [init_snmp],
[AC_DEFINE([HAVE_SNMP], [1],
[Define to 1 if the PHP extension 'snmp' is available.])],
[AC_MSG_FAILURE([SNMP sanity check failed.])],
[$SNMP_SHARED_LIBADD])
dnl Check whether shutdown_snmp_logging() exists.
PHP_CHECK_LIBRARY([$SNMP_LIBNAME], [shutdown_snmp_logging],
[AC_DEFINE([HAVE_SHUTDOWN_SNMP_LOGGING], [1],
[Define to 1 if SNMP library has the 'shutdown_snmp_logging' function.])],
[],
[$SNMP_SHARED_LIBADD])
CFLAGS_SAVE=$CFLAGS
LIBS_SAVE=$LIBS
CFLAGS="$CFLAGS $SNMP_CFLAGS"
LIBS="$LIBS $SNMP_LIBS"
AC_CHECK_DECL([usmHMAC192SHA256AuthProtocol],
[AC_DEFINE([HAVE_SNMP_SHA256], [1],
[Define to 1 if SNMP library has the 'usmHMAC192SHA256AuthProtocol'
array.])],
[],
[
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
])
AC_CHECK_DECL([usmHMAC384SHA512AuthProtocol],
[AC_DEFINE([HAVE_SNMP_SHA512], [1],
[Define to 1 if SNMP library has the 'usmHMAC384SHA512AuthProtocol'
array.])],
[],
[
#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
])
CFLAGS=$CFLAGS_SAVE
LIBS=$LIBS_SAVE
PHP_NEW_EXTENSION([snmp], [snmp.c], [$ext_shared])
PHP_ADD_EXTENSION_DEP(snmp, spl)
PHP_SUBST([SNMP_SHARED_LIBADD])
fi

View File

@@ -14,9 +14,9 @@ declare(strict_types=1);
// test php version (8.1 ~ 8.4 available, multiple for matrix) // test php version (8.1 ~ 8.4 available, multiple for matrix)
$test_php_version = [ $test_php_version = [
'8.1', '8.1',
// '8.2', '8.2',
// '8.3', '8.3',
// '8.4', '8.4',
'8.5', '8.5',
// 'git', // 'git',
]; ];
@@ -30,11 +30,12 @@ $test_os = [
'ubuntu-24.04', // bin/spc for x86_64 'ubuntu-24.04', // bin/spc for x86_64
'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
'ubuntu-24.04-arm', // bin/spc for arm64 'ubuntu-24.04-arm', // bin/spc for arm64
// 'windows-latest', // .\bin\spc.ps1 // 'windows-2022', // .\bin\spc.ps1
// 'windows-2025',
]; ];
// whether enable thread safe // whether enable thread safe
$zts = false; $zts = true;
$no_strip = false; $no_strip = false;
@@ -61,7 +62,7 @@ $shared_extensions = match (PHP_OS_FAMILY) {
}; };
// If you want to test lib-suggests for all extensions and libraries, set it to true. // If you want to test lib-suggests for all extensions and libraries, set it to true.
$with_suggested_libs = true; $with_suggested_libs = false;
// If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true. // If you want to test extra libs for extensions, add them below (comma separated, example `libwebp,libavif`). Unnecessary, when $with_suggested_libs is true.
$with_libs = match (PHP_OS_FAMILY) { $with_libs = match (PHP_OS_FAMILY) {
@@ -208,7 +209,7 @@ switch ($argv[1] ?? null) {
passthru($prefix . $down_cmd, $retcode); passthru($prefix . $down_cmd, $retcode);
break; break;
case 'build_cmd': case 'build_cmd':
passthru($prefix . $build_cmd . ' --build-cli --build-micro', $retcode); passthru($prefix . $build_cmd . ' --build-cli --build-micro --build-cgi', $retcode);
break; break;
case 'build_embed_cmd': case 'build_embed_cmd':
if ($frankenphp) { if ($frankenphp) {