在 PHP 生态系统中,扩展是增强 PHP 功能的重要工具,通常用 C 语言编写以实现高性能和低级访问。然而 C 语言的复杂性以及对 PHP 内核深入理解的需求,使得开发 PHP 扩展对许多开发者来说是一项高门槛任务。
2025 年 PHPVerse 大会上,Kévin 宣布了一项突破性进展:通过 FrankenPHP 开发者现在可以用 Go 语言编写 PHP 扩展。这一创新不仅降低了开发门槛,还引入了 Go 的高性能并发模型,为 PHP 生态带来了全新的可能性。
FrankenPHP 是一个现代化的 PHP 应用服务器,基于 Caddy 和 Go 构建集成了 PHP 解释器,支持高性能的 Web 应用部署。它不仅提供了简化的部署方式和性能优化的“工作模式”,还通过 CGO 库实现了 Go 与 PHP 的无缝交互。
FrankenPHP 的独特之处在于,它允许开发者利用 Go 的简单性和高性能特性(如 goroutines)来扩展 PHP 的功能,而无需深入处理复杂的 C 代码。
传统上,PHP 扩展主要用 C 编写,但 C 语言的低级特性和 PHP 内部机制的复杂性使得开发过程充满挑战。近年来,Rust 和 C++ 也被用于编写 PHP 扩展,但这些语言同样需要较高的学习成本。
相比之下,Go 语言以其简洁的语法、高效的并发模型(goroutines)和强大的标准库而闻名。FrankenPHP 利用这些优势,提供了以下关键理由:
FrankenPHP 提供了两种主要方式来帮助开发者用 Go 编写 PHP 扩展:扩展生成器和工具库。这两者共同降低了开发 PHP 扩展的技术门槛。
FrankenPHP 的扩展生成器是一个革命性的工具,允许开发者通过简单的 Go 代码生成完整的 PHP 扩展,而无需手动编写 C 代码。生成器通过特定的 Go 指令(如 // export_php:function
)解析 Go 文件,自动生成所需的 C 文件、头文件和其他支持文件。以下是一个简单的例子:
package main
// export_php:function multiply(int $a, int $b): int
func multiply(a int64, b int64) int64 {
return a * b
}
// export_php:function is_even(int $a): bool
func is_even(a int64) bool {
return a%2 == 0
}
运行以下命令即可生成扩展:
frankenphp extension-init ext-dir/ext.go
生成器会创建一个 build
目录,包含所有必要的文件,包括:
ext.c
:自动生成的 C 代码,处理 PHP 和 Go 之间的交互。ext.go
:稍作调整的 Go 文件,与 C 代码兼容。ext.h
和 ext_arginfo.h
:定义函数和参数的头文件。ext.stub.php
:PHP 函数签名的存根文件。README.md
:导出的扩展元素说明。通过将生成的 build
目录提供给 Caddy,扩展即可无缝集成到 FrankenPHP 中,无需开发者接触任何 C 代码。
在 Go 和 PHP 之间传递复杂数据类型(如字符串、数组或对象)时,需要进行类型转换,这通常涉及复杂的 C 代码。FrankenPHP 提供了一系列工具函数来简化这一过程,例如:
frankenphp.GoString()
:将 C 字符串转换为 Go 字符串。frankenphp.PHPString()
:将 Go 字符串转换为 PHP 可用的字符串。以下是一个更复杂的例子,展示了如何定义类、常量和方法:
package main
// export_php:namespace Go\MyExtension
import (
"C"
"github.com/dunglas/frankenphp"
"strings"
"unsafe"
)
// export_php:const
const MY_GLOBAL_CONSTANT = "Hello, World!"
// export_php:class MySuperClass
type MyClass struct {
Name string
}
// export_php:method MySuperClass::setName(string $name): void
func (mc *MyClass) SetName(v *C.zend_string) {
mc.Name = frankenphp.GoString(unsafe.Pointer(v))
}
// export_php:method MySuperClass::getName(): string
func (mc *MyClass) GetName() unsafe.Pointer {
return frankenphp.PHPString(mc.Name, false)
}
这些工具函数抽象了底层的类型转换逻辑,让开发者可以专注于功能实现,而无需深入了解 PHP 的 Zend 引擎。
FrankenPHP 利用 Caddy 的模块化架构,通过简单的 init()
函数注册 Go 扩展。例如:
package ext
import "C"
import "github.com/dunglas/frankenphp"
func init() {
frankenphp.RegisterExtension(unsafe.Pointer(&C.ext_module_entry))
}
这一机制将扩展注册到 PHP 解释器中,消除了手动编写 C 代码的需要。
使用 Go 编写 PHP 扩展为开发者提供了多种可能性:
// export_php
指令定义函数、类或常量。frankenphp extension-init
命令生成扩展文件。build
目录提供给 Caddy,启动 FrankenPHP 服务器。FrankenPHP 扩展生成器和工具库为 PHP 开发者打开了一扇全新的大门,让他们能够用 Go 语言编写高性能的 PHP 扩展。
无论是利用 goroutines 实现并发任务,还是将 Go 生态的强大库引入,FrankenPHP 都极大地降低了开发门槛,同时保持了高性能和灵活性。随着 PHP 基金会对其官方支持的加强,FrankenPHP 及其 Go 扩展功能有望成为 PHP 生态的重要组成部分。