最佳方式不是“暴力删文件”,而是“先盘点依赖 -> 做白名单编译 -> 扩展分层打包 -> 自动验收回归”。                          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. 没保留“标准全量包”导致回滚困难

  ---
  最后一句收住:

  第三方依赖剥离与扩展轻量化的最优解,是“白名单编译 + 分层扩展包 + 自动验收 + 可回滚双版本”,不是手工乱裁剪。

更多推荐