Compare commits

...

47 Commits

Author SHA1 Message Date
crazywhalecc
1a6c044ee9 Add google analytics for docs 2025-11-04 09:45:25 +08:00
Marc
ae15d6c5f5 fix postgresql libraries when --with-suggested-libs is false (#952) 2025-10-31 09:46:21 +01:00
henderkes
ff15973a25 suggestions 2025-10-31 09:08:15 +01:00
henderkes
b88a68dab8 fix postgresql libraries when --with-suggested-libs is false 2025-10-29 18:55:26 +01:00
Jerry Ma
b05bdcd83d Add snmp and net-snmp support for macOS and Linux (#946)
Co-authored-by: Marc <m@pyc.ac>
2025-10-29 11:56:36 +08:00
Jerry Ma
6a4ad34324 Add trader extension support (#951) 2025-10-29 11:54:35 +08:00
Jerry Ma
f1d1d4fe10 Beautify docs (#948) 2025-10-28 18:56:18 +08:00
crazywhalecc
fa6fa1c425 Add docs 2025-10-27 09:56:47 +08:00
Jerry Ma
f8c8300c9c Add Windows CGI SAPI build support (#927) 2025-10-25 16:04:00 +08:00
crazywhalecc
09198b431f Use unified configure args format 2025-10-25 15:41:48 +08:00
crazywhalecc
f426ced789 Use phpmicro patches instead 2025-10-25 13:42:04 +08:00
crazywhalecc
bab330b64e Unify CGI deploy functions 2025-10-25 03:03:40 +08:00
crazywhalecc
b59a06face Add backward patches for win32 2025-10-25 02:56:38 +08:00
crazywhalecc
bb44e88c3b Merge branch 'refs/heads/main' into sapi/cgi-win
# Conflicts:
#	src/globals/test-extensions.php
2025-10-25 00:11:45 +08:00
Marc
5476385553 Update PHP 8.5 to RC3, enable swoole-stdext for upcoming 6.1.0 release. (#944) 2025-10-24 12:09:46 +02:00
henderkes
56bac35768 do both in one go 2025-10-24 12:09:24 +02:00
henderkes
4cdc6a07ae fix php 8.5 2025-10-24 11:49:11 +02:00
crazywhalecc
c30b34ae5c Fix gettext missing symbols on macOS 2025-10-24 17:02:15 +08:00
Marc
da5c6fd084 Merge branch 'main' into fix/php85 2025-10-24 10:47:29 +02:00
Jerry Ma
a4b6499530 Fix libevent build, use newer cmake options (#945) 2025-10-24 16:34:07 +08:00
henderkes
eb4445ea59 use release tarballs from Daniel rather than automatically generated ones from github 2025-10-24 10:06:29 +02:00
crazywhalecc
be51bcfdfc Fix libevent build, use newer cmake options 2025-10-24 13:55:33 +08:00
henderkes
7bc4131c02 use correct license for gettext as we only build libs now, update gmp to dynamically check version 2025-10-23 23:11:30 +02:00
henderkes
da8debdade test php 8.5 with gettext 2025-10-23 22:19:53 +02:00
henderkes
ef5e664981 don't build iconv program, or gettext programs 2025-10-23 22:14:57 +02:00
henderkes
8e50af3a7e update to rc3 2025-10-23 21:28:49 +02:00
henderkes
8e96c64918 add stdext to swoole 2025-10-23 21:28:48 +02:00
henderkes
8a9c8a279d why does this fix compilation? 2025-10-23 21:28:48 +02:00
Marc
72ca0cecd1 Update license path for attr package (#941) 2025-10-23 13:32:43 +02:00
Marc
3564f6d0a7 Fix zip archive strip function for cross-device move (#942) 2025-10-23 13:32:12 +02:00
henderkes
d3e2b4b5b2 don't even build tools in the first place 2025-10-23 13:11:20 +02:00
crazywhalecc
c6de6e7056 test zip unarchive 2025-10-23 16:29:46 +08:00
crazywhalecc
55322a282c Fix rename cross-device link bug (using copy-delete) 2025-10-23 15:19:45 +08:00
Marc
6789ea81ff Update license path for acl in source.json 2025-10-22 19:52:21 +02:00
Marc
ac8a9af89c Update license path for attr package 2025-10-22 19:43:54 +02:00
Marc
553b817b2a prevent libedit from building strlcat/strlcpy, no idea why their sour… (#937) 2025-10-21 21:14:39 +07:00
henderkes
96592bce3e fix 2025-10-21 14:26:44 +02:00
henderkes
4e393886aa no idea why our source has that uncommented 2025-10-21 14:23:55 +02:00
henderkes
2e13be2a7a prevent libedit from building strlcat/strlcpy, no idea why their soure code doesn't prevent this correctly itself 2025-10-21 13:54:34 +02:00
Jerry Ma
41fb29eba4 Feat/pie (#936) 2025-10-21 18:58:04 +08:00
Marc
6a153f9aa0 also install-modules of course... (#933) 2025-10-21 10:06:56 +07:00
henderkes
bba390dbcc add runtime libs to unixshell (debian 11 is stupid and doesn't add them automatically) 2025-10-20 23:04:33 +02:00
henderkes
32efeb970c don't build ossfuzzer for zip 2025-10-20 23:00:23 +02:00
henderkes
310335813f use libedit for postgreqsql 2025-10-20 21:24:21 +02:00
Marc
4cdefeab81 test with modules to build
linux should use install-modules now, macOS should not (zip is not built with php)
2025-10-19 11:03:39 +02:00
henderkes
8ab09898f0 conditional install-modules 2025-10-19 10:41:14 +02:00
henderkes
9c8b4d627c also install-modules of course... 2025-10-19 09:08:50 +02:00
42 changed files with 1188 additions and 85 deletions

View File

@@ -65,7 +65,7 @@ PHP_SDK_PATH="${WORKING_DIR}\php-sdk-binary-tools"
; upx executable path
UPX_EXEC="${PKG_ROOT_PATH}\bin\upx.exe"
; 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 can use different build toolchains.

View File

@@ -341,6 +341,7 @@
"ext-depends": [
"xml"
],
"build-with-php": true,
"target": [
"static"
]
@@ -461,6 +462,7 @@
"mysqli": {
"type": "builtin",
"arg-type": "with",
"build-with-php": true,
"ext-depends": [
"mysqlnd"
]
@@ -468,6 +470,7 @@
"mysqlnd": {
"type": "builtin",
"arg-type-windows": "with",
"build-with-php": true,
"lib-depends": [
"zlib"
]
@@ -753,6 +756,18 @@
"apcu"
]
},
"snmp": {
"support": {
"Windows": "wip",
"BSD": "wip"
},
"type": "builtin",
"arg-type-unix": "with",
"arg-type-windows": "with",
"lib-depends": [
"net-snmp"
]
},
"soap": {
"support": {
"BSD": "wip"
@@ -797,6 +812,7 @@
"type": "builtin",
"arg-type": "with-path",
"arg-type-windows": "with",
"build-with-php": true,
"lib-depends": [
"sqlite"
]
@@ -984,6 +1000,14 @@
"type": "builtin",
"build-with-php": true
},
"trader": {
"support": {
"BSD": "wip",
"Windows": "wip"
},
"type": "external",
"source": "ext-trader"
},
"uuid": {
"support": {
"Windows": "wip",
@@ -1177,6 +1201,7 @@
"lib-depends": [
"zlib"
],
"build-with-php": true,
"target": [
"static"
]

View File

@@ -607,8 +607,8 @@
},
"libxml2": {
"source": "libxml2",
"static-libs-unix": [
"libxml2.a"
"pkg-configs": [
"libxml-2.0"
],
"static-libs-windows": [
"libxml2s.lib",
@@ -697,6 +697,17 @@
"libncurses.a"
]
},
"net-snmp": {
"source": "net-snmp",
"pkg-configs": [
"netsnmp",
"netsnmp-agent"
],
"lib-depends": [
"openssl",
"zlib"
]
},
"nghttp2": {
"source": "nghttp2",
"static-libs-unix": [
@@ -795,7 +806,7 @@
"libxml2",
"openssl",
"zlib",
"readline"
"libedit"
],
"lib-suggests": [
"icu",

View File

@@ -47,7 +47,7 @@
"provide-pre-built": true,
"license": {
"type": "file",
"path": "doc/COPYING"
"path": "doc/COPYING.LGPL"
}
},
"brotli": {
@@ -233,6 +233,16 @@
"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": {
"type": "url",
"url": "https://pecl.php.net/get/uuid",
@@ -306,16 +316,17 @@
"regex": "/href=\"(?<file>gettext-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"license": {
"type": "file",
"path": "COPYING"
"path": "gettext-runtime/intl/COPYING.LIB"
}
},
"gmp": {
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/gmp/gmp-6.3.0.tar.xz",
"type": "filelist",
"url": "https://gmplib.org/download/gmp/",
"regex": "/href=\"(?<file>gmp-(?<version>[^\"]+)\\.tar\\.xz)\"/",
"provide-pre-built": true,
"alt": {
"type": "ghtagtar",
"repo": "alisw/GMP"
"type": "url",
"url": "https://dl.static-php.dev/static-php-cli/deps/gmp/gmp-6.3.0.tar.xz"
},
"license": {
"type": "text",
@@ -439,7 +450,7 @@
"provide-pre-built": true,
"license": {
"type": "file",
"path": "doc/COPYING"
"path": "doc/COPYING.LGPL"
}
},
"libaom": {
@@ -761,7 +772,7 @@
"micro": {
"type": "git",
"path": "php-src/sapi/micro",
"rev": "php-85-win",
"rev": "master",
"url": "https://github.com/static-php/phpmicro",
"license": {
"type": "file",
@@ -809,6 +820,14 @@
"path": "COPYING"
}
},
"net-snmp": {
"type": "ghtagtar",
"repo": "net-snmp/net-snmp",
"license": {
"type": "file",
"path": "COPYING"
}
},
"nghttp2": {
"type": "ghrel",
"repo": "nghttp2/nghttp2",

View File

@@ -744,10 +744,26 @@ h2 {
}
select {
border-radius: 4px;
border-radius: 8px;
border: 1px solid var(--vp-c-divider);
padding: 0 4px;
padding: 8px 12px;
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 {
@@ -781,17 +797,160 @@ select {
.textarea {
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
width: calc(100% - 12px);
padding: 4px 8px;
border-radius: 8px;
width: calc(100% - 24px);
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 {
display: block;
border: 1px solid var(--vp-c-divider);
border-radius: 4px;
border-radius: 8px;
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 {

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

@@ -1,10 +1,11 @@
import sidebarEn from "./sidebar.en";
import sidebarZh from "./sidebar.zh";
import sidebarJa from "./sidebar.ja";
// https://vitepress.dev/reference/site-config
export default {
title: "static-php-cli",
title: "Static PHP",
description: "Build single static PHP binary, with PHP project together, with popular extensions included.",
locales: {
en: {
@@ -44,6 +45,7 @@ export default {
},
themeConfig: {
// https://vitepress.dev/reference/default-theme-config
logo: '/images/static-php_nobg.png',
nav: [],
socialLinks: [
{icon: 'github', link: 'https://github.com/crazywhalecc/static-php-cli'}
@@ -60,5 +62,19 @@ export default {
indexName: 'static-php docs',
},
},
}
},
head: [
[
'script',
{ async: '', src: 'https://www.googletagmanager.com/gtag/js?id=G-0MBJ29BD7F' }
],
[
'script',
{},
`window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-0MBJ29BD7F');`
]
]
}

View File

@@ -4,3 +4,21 @@
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`.
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?
Some extensions that rely on closed source libraries, such as `oci8`, `sourceguardian`, etc.,

View File

@@ -3,11 +3,14 @@
layout: home
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."
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions:
- theme: brand
text: Guide
text: Get Started
link: ./guide/
features:
@@ -19,3 +22,121 @@ features:
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
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."
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions:
- theme: brand
text: Get Started
@@ -24,13 +27,121 @@ features:
<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>
## 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 />

BIN
docs/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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`
3. `xdebug.so` 文件可用于版本和线程安全相同的 php。
对于 Windows 平台,由于官方构建的扩展(如 `php_yaml.dll`)强制使用了 `php8.dll` 动态库作为链接,静态构建的 PHP 不包含任何系统库以外的动态库,
所以 Windows 下无法加载官方构建的动态扩展。 由于 static-php-cli 还暂未支持构建动态扩展,所以目前还没有让 static-php 加载动态扩展的方法。
不过Windows 可以正常使用 `FFI` 扩展加载其他的 dll 文件并调用。
## 可以支持 Oracle 数据库扩展吗?
部分依赖库闭源的扩展,如 `oci8``sourceguardian` 等,它们没有提供纯静态编译的依赖库文件(`.a`),仅提供了动态依赖库文件(`.so`

View File

@@ -3,18 +3,140 @@
layout: home
hero:
name: "static-php-cli"
name: "Static PHP"
tagline: "在 Linux、macOS、FreeBSD、Windows 上与 PHP 项目一起构建独立的 PHP 二进制文件,并包含流行的扩展。"
image:
src: /images/static-php_nobg.png
alt: Static PHP CLI Logo
actions:
- theme: brand
text: 指南
text: 开始使用
link: ./guide/
features:
- title: 静态二进制
details: 您可以轻松地编译一个独立的 PHP 二进制文件以供嵌入程序使用包括 cli、fpm、micro
- title: phpmicro 自执行二进制
details: 您可以使用 micro SAPI 编译一个自解压的可执行文件,并将 PHP 代码与二进制文件打包为一个文件
- title: 依赖管理
details: static-php-cli 附带依赖项管理,支持安装不同类型的 PHP 扩展和不同的依赖库
- title: 静态 CLI 二进制
details: 您可以轻松地编译一个独立的 PHP 二进制文件以供通用使用包括 CLI、FPM SAPI
- title: Micro 自解压可执行文件
details: 您可以编译一个自解压的可执行文件,并将 PHP 代码与二进制文件打包在一起
- title: 依赖管理
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

@@ -15,7 +15,11 @@ class gettext extends Extension
public function patchBeforeBuildconf(): bool
{
if ($this->builder instanceof MacOSBuilder) {
FileSystem::replaceFileStr(SOURCE_PATH . '/php-src/ext/gettext/config.m4', 'AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB(intl');
FileSystem::replaceFileStr(
SOURCE_PATH . '/php-src/ext/gettext/config.m4',
['AC_CHECK_LIB($GETTEXT_CHECK_IN_LIB', 'AC_CHECK_LIB([$GETTEXT_CHECK_IN_LIB'],
['AC_CHECK_LIB(intl', 'AC_CHECK_LIB([intl'] // new php versions use a bracket
);
}
return true;
}

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

@@ -69,12 +69,15 @@ class swoole extends Extension
$arg .= $this->builder->getExt('swoole-hook-pgsql') ? ' --enable-swoole-pgsql' : ' --disable-swoole-pgsql';
$arg .= $this->builder->getExt('swoole-hook-mysql') ? ' --enable-mysqlnd' : ' --disable-mysqlnd';
$arg .= $this->builder->getExt('swoole-hook-sqlite') ? ' --enable-swoole-sqlite' : ' --disable-swoole-sqlite';
if ($this->builder->getExt('swoole-hook-odbc')) {
$config = (new SPCConfigUtil($this->builder, ['libs_only_deps' => true]))->config([], ['unixodbc']);
$arg .= ' --with-swoole-odbc=unixODBC,' . BUILD_ROOT_PATH . ' SWOOLE_ODBC_LIBS="' . $config['libs'] . '"';
}
if ($this->getExtVersion() >= '6.1.0') {
$arg .= ' --enable-swoole-stdext';
}
if (SPCTarget::getTargetOS() === 'Darwin') {
$arg .= ' ac_cv_lib_pthread_pthread_barrier_init=no';
}

View File

@@ -7,6 +7,7 @@ namespace SPC\builder\linux;
use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\PatchException;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\store\SourcePatcher;
use SPC\util\GlobalEnvManager;
@@ -283,12 +284,17 @@ class LinuxBuilder extends UnixBuilderBase
*/
protected function buildEmbed(): void
{
$sharedExts = array_filter($this->exts, static fn ($ext) => $ext->isBuildShared());
$sharedExts = array_filter($sharedExts, static function ($ext) {
return Config::getExt($ext->getName(), 'build-with-php') === true;
});
$install_modules = $sharedExts ? 'install-modules' : '';
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
shell()->cd(SOURCE_PATH . '/php-src')
->exec('sed -i "s|//lib|/lib|g" Makefile')
->exec('sed -i "s|^EXTENSION_DIR = .*|EXTENSION_DIR = /' . basename(BUILD_MODULES_PATH) . '|" Makefile')
->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi install-build install-headers install-programs");
->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi {$install_modules} install-build install-headers install-programs");
$ldflags = getenv('SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS') ?: '';
$libDir = BUILD_LIB_PATH;

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=')) {
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")');
}
}

View File

@@ -7,6 +7,7 @@ namespace SPC\builder\macos;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\builder\unix\UnixBuilderBase;
use SPC\exception\WrongUsageException;
use SPC\store\Config;
use SPC\store\FileSystem;
use SPC\store\SourcePatcher;
use SPC\util\GlobalEnvManager;
@@ -264,10 +265,15 @@ class MacOSBuilder extends UnixBuilderBase
*/
protected function buildEmbed(): void
{
$sharedExts = array_filter($this->exts, static fn ($ext) => $ext->isBuildShared());
$sharedExts = array_filter($sharedExts, static function ($ext) {
return Config::getExt($ext->getName(), 'build-with-php') === true;
});
$install_modules = $sharedExts ? 'install-modules' : '';
$vars = SystemUtil::makeEnvVarString($this->getMakeExtraVars());
$concurrency = getenv('SPC_CONCURRENCY') ? '-j' . getenv('SPC_CONCURRENCY') : '';
shell()->cd(SOURCE_PATH . '/php-src')
->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi install-build install-headers install-programs");
->exec("make {$concurrency} INSTALL_ROOT=" . BUILD_ROOT_PATH . " {$vars} install-sapi {$install_modules} install-build install-headers install-programs");
if (getenv('SPC_CMD_VAR_PHP_EMBED_TYPE') === 'static') {
$AR = getenv('AR') ?: 'ar';

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=')) {
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")');
}
}

View File

@@ -17,7 +17,7 @@ trait attr
->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if')
->configure('--disable-nls')
->make();
->make('install-attributes_h install-data install-libattr_h install-libLTLIBRARIES install-pkgincludeHEADERS install-pkgconfDATA', with_install: false);
$this->patchPkgconfPrefix(['libattr.pc'], PKGCONF_PATCH_PREFIX);
}
}

View File

@@ -31,7 +31,7 @@ trait gettext
$autoconf->addConfigureArgs('--disable-threads');
}
$autoconf->configure()->make();
$autoconf->configure()->make(dir: $this->getSourceDir() . '/gettext-runtime/intl');
$this->patchLaDependencyPrefix();
}
}

View File

@@ -26,7 +26,7 @@ trait libacl
->exec('libtoolize --force --copy')
->exec('./autogen.sh || autoreconf -if')
->configure('--disable-nls', '--disable-tests')
->make();
->make('install-acl_h install-libacl_h install-data install-libLTLIBRARIES install-pkgincludeHEADERS install-sysincludeHEADERS install-pkgconfDATA', with_install: false);
$this->patchPkgconfPrefix(['libacl.pc'], PKGCONF_PATCH_PREFIX);
}
}

View File

@@ -4,10 +4,21 @@ declare(strict_types=1);
namespace SPC\builder\unix\library;
use SPC\store\FileSystem;
use SPC\util\executor\UnixAutoconfExecutor;
trait libedit
{
public function patchBeforeBuild(): bool
{
FileSystem::replaceFileRegex(
$this->source_dir . '/src/sys.h',
'|//#define\s+strl|',
'#define strl'
);
return true;
}
protected function build(): void
{
UnixAutoconfExecutor::create($this)

View File

@@ -10,7 +10,13 @@ trait libiconv
{
protected function build(): void
{
UnixAutoconfExecutor::create($this)->configure('--enable-extra-encodings')->make();
UnixAutoconfExecutor::create($this)
->configure(
'--enable-extra-encodings',
'--enable-year2038',
)
->make('install-lib', with_install: false)
->make('install-lib', with_install: false, dir: $this->getSourceDir() . '/libcharset');
$this->patchLaDependencyPrefix();
}
}

View File

@@ -22,6 +22,7 @@ trait libzip
'-DBUILD_EXAMPLES=OFF',
'-DBUILD_REGRESS=OFF',
'-DBUILD_TOOLS=OFF',
'-DBUILD_OSSFUZZ=OFF',
)
->build();
$this->patchPkgconfPrefix(['libzip.pc'], PKGCONF_PATCH_PREFIX);

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
{
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies());
$spc = new SPCConfigUtil($this->getBuilder(), ['no_php' => true, 'libs_only_deps' => true]);
$libs = array_map(fn ($x) => $x->getName(), $this->getDependencies(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'));
$env_vars = [

View File

@@ -103,14 +103,13 @@ class WindowsBuilder extends BuilderBase
->exec(
"{$this->sdk_prefix} configure.bat --task-args \"" .
'--disable-all ' .
'--disable-cgi ' .
'--with-php-build=' . BUILD_ROOT_PATH . ' ' .
'--with-extra-includes=' . BUILD_INCLUDE_PATH . ' ' .
'--with-extra-libs=' . BUILD_LIB_PATH . ' ' .
($enableCli ? '--enable-cli=yes ' : '--enable-cli=no ') .
($enableMicro ? ('--enable-micro=yes ' . $micro_logo . $micro_w32) : '--enable-micro=no ') .
($enableEmbed ? '--enable-embed=yes ' : '--enable-embed=no ') .
($enableCgi ? '--enable-cgi=yes ' : '--enable-cgi=no ') .
($enableCli ? '--enable-cli ' : '--disable-cli ') .
($enableMicro ? ('--enable-micro ' . $micro_logo . $micro_w32) : '--disable-micro ') .
($enableEmbed ? '--enable-embed ' : '--disable-embed ') .
($enableCgi ? '--enable-cgi ' : '--disable-cgi ') .
$config_file_scan_dir .
$opcache_jit_arg .
"{$this->makeStaticExtensionArgs()} " .
@@ -176,11 +175,7 @@ class WindowsBuilder extends BuilderBase
cmd()->cd(SOURCE_PATH . '\php-src')->exec("{$this->sdk_prefix} nmake_cgi_wrapper.bat --task-args php-cgi.exe");
// deploy cgi binary
logger()->info('Deploying cgi file');
FileSystem::createDir(BUILD_BIN_PATH);
cmd()->exec('copy ' . escapeshellarg(SOURCE_PATH . '\php-src\x64\Release' . ($this->zts ? '_TS' : '') . '\php-cgi.exe') . ' ' . escapeshellarg(BUILD_BIN_PATH . '\php-cgi.exe'));
$this->deployBinary(BUILD_TARGET_CGI);
}
public function buildEmbed(): void
@@ -346,12 +341,13 @@ class WindowsBuilder extends BuilderBase
$src = match ($type) {
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_CGI => SOURCE_PATH . "\\php-src\\x64\\Release{$ts}\\php-cgi.exe",
default => throw new SPCInternalException("Deployment does not accept type {$type}"),
};
// with-upx-pack for cli and micro
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));
}
}

View File

@@ -40,7 +40,7 @@ class LinuxToolCheckList
'tar', 'unzip', 'gzip', 'gcc', 'g++',
'bzip2', 'cmake', 'patch', 'which',
'xz', 'libtool', 'gettext-devel',
'patchelf',
'patchelf', 'file',
];
public const TOOLS_ARCH = [
@@ -82,14 +82,14 @@ class LinuxToolCheckList
return CheckResult::ok();
}
#[AsCheckItem('if cmake version >= 3.18', limit_os: 'Linux')]
#[AsCheckItem('if cmake version >= 3.22', limit_os: 'Linux')]
public function checkCMakeVersion(): ?CheckResult
{
$ver = get_cmake_version();
if ($ver === null) {
return CheckResult::fail('Failed to get cmake version');
}
if (version_compare($ver, '3.18.0') < 0) {
if (version_compare($ver, '3.22.0') < 0) {
return CheckResult::fail('cmake version is too low (' . $ver . '), please update it manually!');
}
return CheckResult::ok($ver);

View File

@@ -645,6 +645,36 @@ class FileSystem
};
}
/**
* Move file or directory, handling cross-device scenarios
* Uses rename() if possible, falls back to copy+delete for cross-device moves
*
* @param string $source Source path
* @param string $dest Destination path
*/
private static function moveFileOrDir(string $source, string $dest): void
{
$source = self::convertPath($source);
$dest = self::convertPath($dest);
// Try rename first (fast, atomic)
if (@rename($source, $dest)) {
return;
}
if (is_dir($source)) {
self::copyDir($source, $dest);
self::removeDir($source);
} else {
if (!copy($source, $dest)) {
throw new FileSystemException("Failed to copy file from {$source} to {$dest}");
}
if (!unlink($source)) {
throw new FileSystemException("Failed to remove source file: {$source}");
}
}
}
/**
* Unzip file with stripping top-level directory
*/
@@ -675,10 +705,10 @@ class FileSystem
if (is_dir($extract_path)) {
self::removeDir($extract_path);
}
// if only one dir, move its contents to extract_path using rename
// if only one dir, move its contents to extract_path
$subdir = self::convertPath("{$temp_dir}/{$contents[0]}");
if (count($contents) === 1 && is_dir($subdir)) {
rename($subdir, $extract_path);
self::moveFileOrDir($subdir, $extract_path);
} else {
// else, if it contains only one dir, strip dir and copy other files
$dircount = 0;
@@ -701,17 +731,20 @@ class FileSystem
throw new FileSystemException("Cannot scan unzip temp sub-dir: {$dir[0]}");
}
foreach ($sub_contents as $sub_item) {
rename(self::convertPath("{$temp_dir}/{$dir[0]}/{$sub_item}"), self::convertPath("{$extract_path}/{$sub_item}"));
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$dir[0]}/{$sub_item}"), self::convertPath("{$extract_path}/{$sub_item}"));
}
} else {
foreach ($dir as $item) {
rename(self::convertPath("{$temp_dir}/{$item}"), self::convertPath("{$extract_path}/{$item}"));
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$item}"), self::convertPath("{$extract_path}/{$item}"));
}
}
// move top-level files to extract_path
foreach ($top_files as $top_file) {
rename(self::convertPath("{$temp_dir}/{$top_file}"), self::convertPath("{$extract_path}/{$top_file}"));
self::moveFileOrDir(self::convertPath("{$temp_dir}/{$top_file}"), self::convertPath("{$extract_path}/{$top_file}"));
}
}
// Clean up temp directory
self::removeDir($temp_dir);
}
}

View File

@@ -44,7 +44,7 @@ class SourcePatcher
}
foreach ($builder->getLibs() as $lib) {
if ($lib->patchBeforeBuildconf() === true) {
logger()->info("Library [{$lib->getName()}]patched before buildconf");
logger()->info("Library [{$lib->getName()}] patched before buildconf");
}
}
// patch windows php 8.1 bug
@@ -554,7 +554,7 @@ class SourcePatcher
*/
public static function patchWindowsCGITarget(): void
{
// search Makefile code line contains "$(BUILD_DIR)\php.exe:"
// 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;

View File

@@ -16,7 +16,7 @@ class PhpSource extends CustomSourceBase
{
$major = defined('SPC_BUILD_PHP_VERSION') ? SPC_BUILD_PHP_VERSION : '8.4';
if ($major === '8.5') {
Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://github.com/php/php-src/archive/refs/tags/php-8.5.0RC2.tar.gz'], $force);
Downloader::downloadSource('php-src', ['type' => 'url', 'url' => 'https://downloads.php.net/~daniels/php-8.5.0RC3.tar.xz'], $force);
} elseif ($major === 'git') {
Downloader::downloadSource('php-src', ['type' => 'git', 'url' => 'https://github.com/php/php-src.git', 'rev' => 'master'], $force);
} else {

View File

@@ -50,18 +50,22 @@ class UnixAutoconfExecutor extends Executor
* @param bool $with_clean Whether to clean before building
* @param array $after_env_vars Environment variables postfix
*/
public function make(string $target = '', false|string $with_install = 'install', bool $with_clean = true, array $after_env_vars = []): static
public function make(string $target = '', false|string $with_install = 'install', bool $with_clean = true, array $after_env_vars = [], ?string $dir = null): static
{
return $this->seekLogFileOnException(function () use ($target, $with_install, $with_clean, $after_env_vars) {
return $this->seekLogFileOnException(function () use ($target, $with_install, $with_clean, $after_env_vars, $dir) {
$shell = $this->shell;
if ($dir) {
$shell = $shell->cd($dir);
}
if ($with_clean) {
$this->shell->exec('make clean');
$shell->exec('make clean');
}
$after_env_vars_str = $after_env_vars !== [] ? shell()->setEnv($after_env_vars)->getEnvString() : '';
$this->shell->exec("make -j{$this->library->getBuilder()->concurrency} {$target} {$after_env_vars_str}");
$shell->exec("make -j{$this->library->getBuilder()->concurrency} {$target} {$after_env_vars_str}");
if ($with_install !== false) {
$this->shell->exec("make {$with_install}");
$shell->exec("make {$with_install}");
}
return $this->shell;
return $shell;
});
}
@@ -111,6 +115,12 @@ class UnixAutoconfExecutor extends Executor
return $this;
}
public function setEnv(array $env): static
{
$this->shell->setEnv($env);
return $this;
}
public function appendEnv(array $env): static
{
$this->shell->appendEnv($env);

View File

@@ -205,7 +205,7 @@ SET(CMAKE_INSTALL_PREFIX "{$root}")
SET(CMAKE_INSTALL_LIBDIR "lib")
set(PKG_CONFIG_EXECUTABLE "{$pkgConfigExecutable}")
list(APPEND PKG_CONFIG_EXECUTABLE "--static")
set(PKG_CONFIG_ARGN "--static" CACHE STRING "Extra arguments for pkg-config" FORCE)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

View File

@@ -8,6 +8,7 @@ use SPC\builder\freebsd\library\BSDLibraryBase;
use SPC\builder\linux\library\LinuxLibraryBase;
use SPC\builder\macos\library\MacOSLibraryBase;
use SPC\exception\SPCInternalException;
use SPC\util\SPCTarget;
use ZM\Logger\ConsoleColor;
/**
@@ -28,6 +29,7 @@ class UnixShell extends Shell
public function exec(string $cmd): static
{
$cmd = clean_spaces($cmd);
/* @phpstan-ignore-next-line */
logger()->info(ConsoleColor::yellow('[EXEC] ') . ConsoleColor::green($cmd));
$original_command = $cmd;
@@ -48,7 +50,7 @@ class UnixShell extends Shell
'CFLAGS' => $library->getLibExtraCFlags(),
'CXXFLAGS' => $library->getLibExtraCXXFlags(),
'LDFLAGS' => $library->getLibExtraLdFlags(),
'LIBS' => $library->getLibExtraLibs(),
'LIBS' => $library->getLibExtraLibs() . SPCTarget::getRuntimeLibs(),
]);
return $this;
}

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

@@ -23,15 +23,15 @@ $test_php_version = [
// test os (macos-15-intel, macos-15, ubuntu-latest, windows-latest are available)
$test_os = [
// 'macos-15-intel', // bin/spc for x86_64
// 'macos-15', // bin/spc for arm64
// 'ubuntu-latest', // bin/spc-alpine-docker for x86_64
// 'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
// 'ubuntu-24.04', // bin/spc for x86_64
// 'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
// 'ubuntu-24.04-arm', // bin/spc for arm64
'macos-15-intel', // bin/spc for x86_64
'macos-15', // bin/spc for arm64
'ubuntu-latest', // bin/spc-alpine-docker for x86_64
'ubuntu-22.04', // bin/spc-gnu-docker for x86_64
'ubuntu-24.04', // bin/spc for x86_64
'ubuntu-22.04-arm', // bin/spc-gnu-docker for arm64
'ubuntu-24.04-arm', // bin/spc for arm64
// 'windows-2022', // .\bin\spc.ps1
'windows-2025',
// 'windows-2025',
];
// whether enable thread safe
@@ -50,7 +50,7 @@ $prefer_pre_built = false;
// If you want to test your added extensions and libs, add below (comma separated, example `bcmath,openssl`).
$extensions = match (PHP_OS_FAMILY) {
'Linux', 'Darwin' => 'pdo_pgsql',
'Linux', 'Darwin' => 'trader',
'Windows' => 'bcmath,bz2,calendar,ctype,curl,dba,dom,exif,ffi,fileinfo,filter,ftp,iconv,libxml,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pdo,pdo_mysql,pdo_sqlite,pdo_sqlsrv,phar,session,shmop,simdjson,simplexml,soap,sockets,sqlite3,sqlsrv,ssh2,sysvshm,tokenizer,xml,xmlreader,xmlwriter,yaml,zip,zlib',
};