php内核 第三方依赖剥离与扩展轻量化裁剪
·
最佳方式不是“暴力删文件”,而是“先盘点依赖 -> 做白名单编译 -> 扩展分层打包 -> 自动验收回归”。 for shortcuts
这样才能又轻量、又不把业务打挂。──────────────────────────────────────────────────────────────────────────────────────
---
给你一套可直接落地的完整方案(大白话 + 完整脚本模板)。
1) 先明白“剥离什么”
你要剥离的是两类:
1. 第三方库依赖(openssl/curl/icu/libxml2 等)
2. PHP 扩展依赖(gd/intl/snmp/ldap/imap/pgsql...)
目标:
- 减少体积
- 减少漏洞面
- 提高启动和发布效率
- 保证业务不受影响
---
2) 最佳策略(四步走)
第一步:盘点实际用量(别靠猜)
- 扫代码里函数/类调用
- 扫运行时扩展加载
- 扫二进制动态库依赖(ldd)
第二步:白名单编译
- 只保留必须扩展
- 其他全部关掉
第三步:扩展分层
- 核心包:必须扩展
- 可选包:少量业务扩展单独 so 包,按需装
第四步:自动验收
- 模块校验
- 冒烟测试
- 性能对比
- 回滚包保留
---
3) 完整脚本 1:代码静态扫描(推断扩展使用)
文件:scan_extension_usage.php
<?php
declare(strict_types=1);
$root = $argv[1] ?? getcwd();
if (!is_dir($root)) {
fwrite(STDERR, "invalid path: {$root}\n");
exit(1);
}
$rules = [
'curl_' => 'curl',
'mysqli_' => 'mysqli',
'PDO' => 'pdo',
'imagecreate' => 'gd',
'imagick' => 'imagick',
'mb_' => 'mbstring',
'intl' => 'intl',
'openssl_' => 'openssl',
'simplexml_' => 'xml',
'SoapClient' => 'soap',
'ldap_' => 'ldap',
'snmp_' => 'snmp',
'imap_' => 'imap',
'zip_' => 'zip',
'pcntl_' => 'pcntl',
'redis' => 'redis',
];
$hits = [];
$files = 0;
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($root, FilesystemIterator::SKIP_DOTS)
);
foreach ($it as $f) {
if (!$f->isFile()) continue;
$path = $f->getPathname();
if (!preg_match('/\.(php|phtml|inc)$/i', $path)) continue;
$files++;
$content = @file_get_contents($path);
if ($content === false) continue;
foreach ($rules as $needle => $ext) {
if (stripos($content, $needle) !== false) {
$hits[$ext] = ($hits[$ext] ?? 0) + 1;
}
}
}
ksort($hits);
echo "Scanned files: {$files}\n";
echo "Possible extension usage:\n";
foreach ($hits as $ext => $count) {
echo str_pad($ext, 14) . " : {$count}\n";
}
---
4) 完整脚本 2:轻量化编译(白名单)
文件:build_php_lite.sh
#!/usr/bin/env bash
set -euo pipefail
PHP_SRC="${1:-}"
PREFIX="${2:-/opt/php-lite}"
if [[ -z "${PHP_SRC}" || ! -d "${PHP_SRC}" ]]; then
echo "Usage: $0 /path/to/php-src [/install/prefix]"
exit 1
fi
cd "${PHP_SRC}"
./buildconf --force
# 只开核心能力,其他默认不带
./configure \
--prefix="${PREFIX}" \
--enable-cli \
--enable-fpm \
--disable-cgi \
--enable-opcache \
--enable-mbstring \
--with-openssl \
--with-zlib \
--with-curl \
--with-mysqli \
--with-pdo-mysql \
--enable-sockets \
--without-pear \
--disable-phpdbg
make -j"$(nproc)"
make install
echo "Installed to ${PREFIX}"
"${PREFIX}/bin/php" -v
"${PREFIX}/bin/php" -m
---
5) 完整脚本 3:依赖剥离检查(动态库)
文件:check_runtime_deps.sh
#!/usr/bin/env bash
set -euo pipefail
PHP_BIN="${1:-/opt/php-lite/bin/php}"
if [[ ! -x "${PHP_BIN}" ]]; then
echo "php binary not found: ${PHP_BIN}"
exit 1
fi
echo "== php binary deps =="
ldd "${PHP_BIN}" | sort
echo
echo "== extension deps =="
EXT_DIR="$("${PHP_BIN}" -i | awk -F'=> ' '/^extension_dir/ {print $2}' | xargs)"
if [[ -d "${EXT_DIR}" ]]; then
for so in "${EXT_DIR}"/*.so; do
[[ -e "$so" ]] || continue
echo "--- $(basename "$so") ---"
ldd "$so" | sort
done
fi
---
6) 完整脚本 4:验收与防误删
文件:verify_php_lite.sh
#!/usr/bin/env bash
set -euo pipefail
PHP_BIN="${1:-/opt/php-lite/bin/php}"
required=(openssl curl mbstring mysqli pdo_mysql zlib opcache)
forbidden=(snmp imap ldap odbc pgsql pdo_pgsql soap)
echo "== Required =="
for ext in "${required[@]}"; do
if "${PHP_BIN}" -m | grep -qi "^${ext}$"; then
echo "[OK] ${ext}"
else
echo "[FAIL] missing ${ext}"
exit 1
fi
done
echo
echo "== Forbidden =="
for ext in "${forbidden[@]}"; do
if "${PHP_BIN}" -m | grep -qi "^${ext}$"; then
echo "[FAIL] should be removed: ${ext}"
exit 2
else
echo "[OK] not loaded: ${ext}"
fi
done
echo
echo "== Smoke =="
"${PHP_BIN}" -r 'echo "ok\n";'
echo "verify passed"
---
7) 扩展轻量化“分层打包”建议
- php-core:cli/fpm + 核心扩展
- php-ext-db:pgsql/sqlite 等数据库可选
- php-ext-legacy:soap/ldap/snmp 旧业务专用
- php-ext-debug:xdebug(生产默认不装)
这样业务机只装自己需要的层,最省。
---
8) 最容易踩坑的点
1. 一刀切关 intl,多语言/金额日期格式全错
2. 关 openssl/curl,支付和第三方 API 全挂
3. 只看功能通过,不看 P95/P99
4. 没保留“标准全量包”导致回滚困难
---
最后一句收住:
第三方依赖剥离与扩展轻量化的最优解,是“白名单编译 + 分层扩展包 + 自动验收 + 可回滚双版本”,不是手工乱裁剪。
更多推荐

所有评论(0)