Skip to content

PHP 扩展概览

概述

PHP 的强大生态很大程度上得益于其丰富的扩展体系。PHP 扩展(Extension)是用 C/C++ 编写的动态链接库,通过扩展 PHP 的核心功能来提供额外的能力。从数据库驱动到性能分析,从协程框架到加密算法,PHP 扩展覆盖了几乎所有的应用场景。了解 PHP 扩展的类型、加载机制和开发方法,有助于深入理解 PHP 的运行原理,以及在需要时开发自定义扩展来满足特定需求。

前置知识

阅读本节前,建议先了解 OPcache 配置详解PECL 扩展管理

基础概念

扩展的分类

PHP 扩展按其接入方式主要分为两类:

  1. PHP 扩展(Standard Extensions):通过 extension= 指令加载,实现具体的 PHP 函数
  2. Zend 扩展(Zend Extensions):通过 zend_extension= 指令加载,可以修改 Zend 引擎的核心行为
PHP 扩展加载层次:

┌─────────────────────────┐
│       用户 PHP 代码      │
├─────────────────────────┤
│     PHP 函数/API 层      │  ← PHP 扩展注册函数
├─────────────────────────┤
│      Zend 引擎层         │  ← Zend 扩展修改引擎行为
├─────────────────────────┤
│       操作系统层          │
└─────────────────────────┘

扩展生命周期

PHP 扩展有四个生命周期阶段:

  1. MINIT(Module Init):PHP 进程启动时执行一次
  2. RINIT(Request Init):每个请求开始时执行
  3. RSHUTDOWN(Request Shutdown):每个请求结束时执行
  4. MSHUTDOWN(Module Shutdown):PHP 进程退出时执行

详细说明

PHP 扩展 vs Zend 扩展

特性PHP 扩展Zend 扩展
加载方式extension=xxx.sozend_extension=xxx.so
注册方式注册 PHP 函数注册 Zend Hook
加载时机在 Zend 引擎启动后在 Zend 引擎启动前
功能范围提供新函数修改引擎行为
典型代表mysqli、gd、jsonOPcache、Xdebug
数量数百个极少数

常用扩展分类

核心扩展(内置)

bash
# 查看已加载的核心扩展
php -m
分类扩展说明
字符串/数组SPL、JSON、CBOR数据处理
数学bcmath、GMP高精度计算
加密OpenSSL、Sodium安全相关
数据库PDO、SQLite3数据存储
网络sockets、stream网络通信
进程PCNTL、POSIX进程控制
文件系统FileInfo、intl文件处理
日期时间DateTime时间处理

PECL 扩展

bash
# 列出可用的 PECL 扩展
pecl search <keyword>
分类扩展说明
性能OPcache、APCu缓存加速
调试Xdebug开发调试
异步Swoole、ReactPHP高并发
并行parallel多线程
搜索redis、mongodbNoSQL 驱动
监控tideways性能分析

扩展加载机制

加载顺序

ini
; php.ini 中的扩展加载顺序很重要

; 1. Zend 扩展必须先加载(修改引擎行为)
zend_extension=opcache.so
zend_extension=xdebug.so

; 2. PHP 扩展在之后加载
extension=mysqli.so
extension=pdo_mysql.so
extension=redis.so
extension=json.so

加载顺序

Zend 扩展必须在 PHP 扩展之前加载。如果有多个 Zend 扩展,它们的加载顺序也很重要(如 OPcache 通常应在 Xdebug 之前)。

按目录加载

bash
# PHP 扩展目录
php-config --extension-dir

# 查看扩展搜索路径
php -i | grep "extension_dir"

# 扫描目录自动加载
# php.ini 中:
; extension_dir = "/usr/lib/php/20210902"
; 所有 .so 文件会被自动加载

运行时加载

php
<?php
declare(strict_types=1);

// 使用 dl() 动态加载扩展(不推荐)
// dl('redis.so'); // PHP 7.0+ 已在 CLI 中移除

// 更好的方式:通过 php.ini 配置
// extension=redis.so

// 检查扩展是否已加载
if (!extension_loaded('redis')) {
    throw new RuntimeException('Redis 扩展未安装');
}

// 获取扩展版本
echo phpversion('redis'); // e.g., 6.0.2

// 获取扩展函数列表
$functions = get_extension_funcs('redis');

扩展开发工具

PHP-CPP

PHP-CPP 是一个 C++ 库,极大简化了 PHP 扩展的开发:

cpp
// my_extension.cpp
#include <phpcpp.h>

// 定义 PHP 函数
Php::Value hello_world(Php::Parameters &params)
{
    return "Hello, " + params[0].stringValue() + "!";
}

// 导出函数
extern "C" {
    PHPCPP_EXPORT void *get_module()
    {
        static Php::Extension extension("my_extension", "1.0");

        extension.add<hello_world>("hello_world", {
            Php::ByVal("name", Php::Type::String, true)
        });

        return extension;
    }
}
bash
# 编译 PHP-CPP 扩展
g++ -std=c++11 -fPIC -I/usr/include/php/20210902 \
    my_extension.cpp -o my_extension.so -lphpcpp

# 安装
sudo cp my_extension.so /usr/lib/php/20210902/
echo "extension=my_extension.so" >> /etc/php/8.1/mods-available/my_extension.ini
phpenmod my_extension

纯 C 扩展开发

c
/* simple_ext.c */
#include "php.h"

/* 定义 PHP 函数 */
PHP_FUNCTION(simple_add)
{
    long a, b;
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ll", &a, &b) == FAILURE) {
        RETURN_NULL();
    }
    RETURN_LONG(a + b);
}

/* 函数参数信息 */
ZEND_BEGIN_ARG_INFO_EX(arginfo_simple_add, 0, 0, 2)
    ZEND_ARG_INFO(0, a)
    ZEND_ARG_INFO(0, b)
ZEND_END_ARG_INFO()

/* 函数注册表 */
static const zend_function_entry simple_ext_functions[] = {
    PHP_FE(simple_add, arginfo_simple_add)
    PHP_FE_END
};

/* 模块信息 */
PHP_MINFO_FUNCTION(simple_ext)
{
    php_info_print_table_start();
    php_info_print_table_header(2, "Simple Extension", "enabled");
    php_info_print_table_row(2, "Version", "1.0.0");
    php_info_print_table_end();
}

/* 模块注册 */
zend_module_entry simple_ext_module_entry = {
    STANDARD_MODULE_HEADER,
    "simple_ext",
    simple_ext_functions,
    NULL,  /* MINIT */
    NULL,  /* MSHUTDOWN */
    NULL,  /* RINIT */
    NULL,  /* RSHUTDOWN */
    PHP_MINFO(simple_ext),
    "1.0.0",
    STANDARD_MODULE_PROPERTIES
};

#ifdef COMPILE_DL_SIMPLE_EXT
ZEND_GET_MODULE(simple_ext)
#endif
bash
# 编译配置
phpize
./configure --enable-simple-ext
make
sudo make install

config.m4 模板

# config.m4
PHP_ARG_ENABLE(simple_ext, whether to enable simple_ext support,
[  --enable-simple_ext   Enable simple_ext support])

if test "$PHP_SIMPLE_EXT" != "no"; then
    PHP_NEW_EXTENSION(simple_ext, simple_ext.c, $ext_shared)
fi

实战示例

扩展信息查询工具

php
<?php
declare(strict_types=1);

/**
 * PHP 扩展信息查询工具
 */
class ExtensionInfoTool
{
    /**
     * 获取所有扩展信息
     */
    public static function getAllExtensions(): array
    {
        $extensions = get_loaded_extensions();
        $info = [];

        foreach ($extensions as $ext) {
            $info[$ext] = [
                'version' => phpversion($ext) ?: 'unknown',
                'type' => self::getExtensionType($ext),
                'functions' => get_extension_funcs($ext) ?: [],
            ];
        }

        return $info;
    }

    /**
     * 获取扩展类型
     */
    private static function getExtensionType(string $name): string
    {
        $zendExtensions = ['opcache', 'xdebug', 'Zend OPcache'];
        return in_array(strtolower($name), $zendExtensions) ? 'Zend' : 'PHP';
    }

    /**
     * 检查扩展依赖
     */
    public static function checkDependencies(array $required): array
    {
        $missing = [];
        foreach ($required as $ext => $minVersion) {
            if (!extension_loaded($ext)) {
                $missing[$ext] = '未安装';
            } elseif ($minVersion !== null) {
                $currentVersion = phpversion($ext);
                if (version_compare($currentVersion, $minVersion, '<')) {
                    $missing[$ext] = "版本过低 ({$currentVersion} < {$minVersion})";
                }
            }
        }
        return $missing;
    }
}

// 使用示例
$info = ExtensionInfoTool::getAllExtensions();
echo "已加载扩展: " . count($info) . " 个" . PHP_EOL;
echo PHP_EOL;

// 检查常用扩展
$required = [
    'PDO' => null,
    'pdo_mysql' => null,
    'json' => null,
    'curl' => null,
    'mbstring' => null,
    'openssl' => null,
    'redis' => null,
];

$missing = ExtensionInfoTool::checkDependencies($required);
if (empty($missing)) {
    echo "所有必需扩展已安装" . PHP_EOL;
} else {
    echo "缺失扩展:" . PHP_EOL;
    foreach ($missing as $ext => $reason) {
        echo "  {$ext}: {$reason}" . PHP_EOL;
    }
}

扩展加载性能分析

php
<?php
declare(strict_types=1);

/**
 * 扩展加载性能分析
 * 对比不同扩展组合对 PHP 启动时间的影响
 */
class ExtensionLoadBenchmark
{
    /**
     * 测试 PHP 启动时间
     */
    public static function benchmarkStartup(): array
    {
        // 记录脚本开始时间(越早越好)
        $startTime = hrtime(true);

        // ... PHP 已自动加载所有扩展

        $endTime = hrtime(true);
        $totalMs = ($endTime - $startTime) / 1_000_000;

        return [
            'total_ms' => round($totalMs, 3),
            'extensions' => count(get_loaded_extensions()),
        ];
    }

    /**
     * 生成扩展加载报告
     */
    public static function report(): string
    {
        $extensions = get_loaded_extensions();
        sort($extensions);

        $lines = [];
        $lines[] = "# PHP 扩展加载报告";
        $lines[] = "";
        $lines[] = "PHP 版本: " . PHP_VERSION;
        $lines[] = "已加载扩展: " . count($extensions);
        $lines[] = "";
        $lines[] = "| 类型 | 扩展名 | 版本 |";
        $lines[] = "|------|--------|------|";

        foreach ($extensions as $ext) {
            $type = in_array($ext, ['Zend OPcache', 'xdebug']) ? 'Zend' : 'PHP';
            $version = phpversion($ext) ?: '-';
            $lines[] = "| {$type} | {$ext} | {$version} |";
        }

        return implode("\n", $lines);
    }
}

echo ExtensionLoadBenchmark::report() . PHP_EOL;

注意事项

扩展安全

  1. 来源可信:仅安装来自 PECL、操作系统包管理器或知名开源项目的扩展
  2. 最小权限:只安装必需的扩展,减少攻击面
  3. 及时更新:保持扩展版本更新
  4. 禁用危险函数dl()eval()

版本兼容性

PHP 8.1 扩展 API 编号: 20210902
PHP 8.2 扩展 API 编号: 20220829
PHP 8.3 扩展 API 编号: 20230831

扩展 API 版本

每个 PHP 大版本有不同的扩展 API(ZEND_EXTENSION_API_NO)。为 PHP 8.1 编译的扩展不能在 PHP 8.2 上使用。升级 PHP 版本时需要重新编译所有扩展。

最佳实践

1. 生产环境扩展管理

ini
; 生产环境 - 最小化扩展
extension=pdo_mysql
extension=mbstring
extension=openssl
extension=curl
extension=json

; 仅在需要的 FPM pool 中加载调试扩展
; /etc/php/8.1/fpm/pool.d/debug.conf
; zend_extension=xdebug.so

2. 容器化部署

dockerfile
# Dockerfile
FROM php:8.1-fpm-alpine

# 仅安装必需扩展
RUN docker-php-ext-install pdo_mysql mbstring
RUN pecl install redis && docker-php-ext-enable redis

# 不安装调试扩展
# 不安装 Xdebug

下一节

继续学习:FFI 外部函数接口

参考链接