本文档总结 PHPX 开发中的最佳实践、常见陷阱和性能优化技巧。
推荐的项目结构:
myext/
├── src/
│ ├── myext.cc # 扩展入口
│ ├── functions/ # 函数实现
│ │ ├── string.cc
│ │ ├── array.cc
│ │ └── math.cc
│ ├── classes/ # 类实现
│ │ ├── MyClass.cc
│ │ └── YourClass.cc
│ └── helpers/ # 辅助函数
│ └── utils.cc
├── include/
│ ├── myext.h # 主头文件
│ ├── functions.h
│ ├── classes.h
│ └── utils.h
├── tests/ # 测试代码
├── examples/ # 示例代码
└── CMakeLists.txt # 构建配置
// ✅ 使用小写字母和下划线
PHPX_FUNCTION(my_function);
PHPX_FUNCTION(array_helper);
PHPX_FUNCTION(process_data);
// ❌ 避免驼峰命名
PHPX_FUNCTION(myFunction); // 不推荐// ✅ 使用大写字母开头
class MyClass : public Class {
// ...
};
// 注册时使用驼峰
ext.registerClass(new MyClass("MyClass"));// ✅ 使用驼峰命名
Variant myVariable;
Array dataList;
Object userObject;
// ✅ 使用有意义的名称
Variant userAge; // ✅
Variant ua; // ❌/**
* 处理用户数据
*
* @param args[0] string 用户名
* @param args[1] int 年龄
* @return Variant 处理后的用户对象
*
* @throws InvalidArgumentException 参数无效时
*/
PHPX_FUNCTION(process_user) {
// 参数验证
if (args.size() < 2) {
throwError("Expected 2 parameters");
return;
}
String name = args[0].toString();
Int age = args[1].toInt();
// 业务逻辑
Object user = createUser(name, age);
return user;
}// ✅ 使用 RAII 管理资源
void example() {
std::unique_ptr<Resource> res(createResource());
// 自动释放,无需手动 delete
}
// ✅ 使用 shared_ptr 共享所有权
std::shared_ptr<Data> data1 = std::make_shared<Data>();
std::shared_ptr<Data> data2 = data1; // 引用计数增加// ✅ 正确的 Variant 使用
void good_example() {
Variant var = "hello";
echo(var.toString());
// 析构时自动释放
}
// ❌ 避免内存泄漏
void bad_example() {
zval *zv = new zval; // 手动分配
ZVAL_STRING(zv, "test");
// 忘记释放 - 内存泄漏!
}
// ✅ 如果必须使用原始 zval
void safe_zval_usage() {
zval zv;
ZVAL_STRING(&zv, "test");
// 使用完后释放
zval_ptr_dtor(&zv);
}// ✅ 理解引用计数
void reference_counting() {
Variant var1 = "hello"; // refcount = 1
Variant var2 = var1; // refcount = 2
var1.unset(); // refcount = 1
// var2 仍然有效
}
// ⚠️ 注意循环引用
void circular_reference() {
Array arr1;
Array arr2;
arr1.set("child", arr2); // arr2 refcount++
arr2.set("parent", arr1); // arr1 refcount++
// 可能导致内存泄漏
}// ✅ 使用 String 类
String str = "hello world";
echo(str.data());
// ✅ 长字符串使用 std::string
std::string long_str = createLongString();
Variant var = long_str.c_str();
// ⚠️ 注意字符串生命周期
void string_lifetime() {
const char *data;
{
String temp = "temporary";
data = temp.data(); // ⚠️ temp 销毁后 data 无效
}
echo(data); // ❌ 未定义行为
}PHPX_FUNCTION(validate_params) {
// 检查参数数量
if (args.size() < 1) {
throwError("At least one parameter required");
return;
}
// 检查参数类型
if (!args[0].isString()) {
throwError("Parameter 1 must be string");
return;
}
if (!args[1].isInt()) {
throwError("Parameter 2 must be integer");
return;
}
// 检查参数范围
Int value = args[1].toInt();
if (value < 0 || value > 100) {
throwError("Parameter 2 must be between 0 and 100");
return;
}
// 检查空值
if (args[0].length() == 0) {
throwError("Parameter 1 cannot be empty");
return;
}
// 正常处理
process(args[0], args[1]);
}// ✅ 使用具体的异常类型
void throw_specific_exception() {
if (invalid_argument) {
throwException(
"InvalidArgumentException",
"Invalid argument provided"
);
} else if (not_found) {
throwException(
"RuntimeException",
"Resource not found"
);
}
}
// ✅ 提供有用的错误信息
void informative_error() {
String param = args[0].toString();
throwError(
"User '%s' not found in database",
param.data()
);
}
// ❌ 避免模糊的错误信息
void vague_error() {
throwError("Error occurred"); // ❌ 无用信息
}// ✅ 尝试 - 回退模式
PHPX_FUNCTION(safe_operation) {
try {
return riskyOperation();
} catch (zend_object *ex) {
// 记录错误
error(E_WARNING, "Operation failed, using fallback");
// 回退到默认值
return getDefaultValue();
}
}
// ✅ 条件执行
PHPX_FUNCTION(conditional_exec) {
if (canExecuteSafely()) {
execute();
} else {
throwError("Cannot execute safely");
}
}// ✅ 使用引用传递
void processLargeData(const Variant &data) {
// 不会复制数据
String str = data.toString();
}
// ❌ 值传递会复制
void processLargeDataBad(Variant data) {
// 会复制整个数据
}
// ✅ 使用 move 语义
Variant createData() {
Variant result = generateData();
return result; // RVO 优化
}// ✅ 缓存类入口
static zend_class_entry *cached_ce = nullptr;
if (!cached_ce) {
cached_ce = getClassEntry("DateTime");
}
Object date = newObject(cached_ce);
// ✅ 缓存函数
static zend_function *cached_func = nullptr;
if (!cached_func) {
cached_func = findFunction("strlen");
}
Variant result = call(cached_func, args);// ✅ 预分配数组大小
Array createLargeArray() {
Array arr(1000); // 预分配空间
for (int i = 0; i < 1000; i++) {
arr.set(i, i * 2);
}
return arr;
}
// ✅ 批量插入数据库
void batchInsert(const Array &items) {
startTransaction();
for (const auto &item : items) {
insertItem(item);
}
commit(); // 一次提交,而不是每次插入都提交
}// ✅ 直接使用正确类型
Variant num = 42; // 直接创建整数
// ❌ 避免字符串转数字
Variant num_bad = "42"; // 创建字符串
Int val = num_bad.toInt(); // 需要转换
// ✅ 复用变量
void reuse_variables() {
Variant buffer;
for (int i = 0; i < 100; i++) {
buffer = i; // 复用已有变量
process(buffer);
}
}// ✅ 只在需要时计算
class LazyLoader {
private:
mutable Variant cached_data;
mutable bool loaded = false;
public:
Variant getData() const {
if (!loaded) {
cached_data = expensiveComputation();
loaded = true;
}
return cached_data;
}
};// ✅ 启用调试模式
enableDebugInfo(true);
// ✅ 输出变量信息
void debug_example() {
Variant var = getValue();
printf("Type: %d\n", var.type());
printf("Refcount: %d\n", var.getRefCount());
var.debug(); // 输出详细信息
var.print(); // var_dump 格式
}// ✅ 分级日志
#define LOG_DEBUG(fmt, ...) \
error(E_NOTICE, "[DEBUG] " fmt, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) \
error(E_INFO, "[INFO] " fmt, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) \
error(E_ERROR, "[ERROR] " fmt, ##__VA_ARGS__)
void example() {
LOG_DEBUG("Starting function with param=%s", param.data());
// ... 业务逻辑
LOG_INFO("Function completed");
}// ✅ 测量执行时间
#include <chrono>
void profile_function() {
auto start = std::chrono::high_resolution_clock::now();
// 要测量的代码
processData();
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(
end - start
);
LOG_INFO("Execution time: %ld μs", duration.count());
}// ✅ 测试边界条件
TEST(array_test, boundary_conditions) {
Array arr;
// 空数组
ASSERT_EQ(arr.count(), 0);
// 单个元素
arr.append(1);
ASSERT_EQ(arr.count(), 1);
// 大量元素
for (int i = 0; i < 10000; i++) {
arr.append(i);
}
ASSERT_GE(arr.count(), 10000);
}
// ✅ 测试异常情况
TEST(divide_test, division_by_zero) {
ASSERT_THROW({
divide(10, 0);
}, InvalidArgumentException);
}<?php
// ✅ 测试完整流程
class IntegrationTest extends TestCase {
public function testFullWorkflow() {
// 创建用户
$userId = create_user('john', 25);
// 获取用户
$user = get_user($userId);
$this->assertEquals('john', $user['name']);
// 更新用户
update_user($userId, ['age' => 26]);
// 验证更新
$user = get_user($userId);
$this->assertEquals(26, $user['age']);
// 删除用户
delete_user($userId);
// 验证删除
$this->assertNull(get_user($userId));
}
}// ✅ 基准测试
TEST(performance_test, array_operations) {
Array arr;
auto start = std::chrono::high_resolution_clock::now();
for (int i = 0; i < 100000; i++) {
arr.append(i);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end - start
);
printf("Append 100k items: %ld ms\n", duration.count());
ASSERT_LT(duration.count(), 1000); // 应该小于 1 秒
}// ✅ 严格验证所有输入
PHPX_FUNCTION(safe_function) {
// 类型检查
if (!args[0].isString()) {
throwError("Parameter must be string");
return;
}
// 长度检查
String input = args[0].toString();
if (input.length() > MAX_LENGTH) {
throwError("Input too long");
return;
}
// 内容检查
if (!isValidFormat(input)) {
throwError("Invalid input format");
return;
}
// SQL 注入防护
String sanitized = escapeSql(input);
executeQuery(sanitized);
}// ✅ 限制资源使用
PHPX_FUNCTION(read_file) {
String filename = args[0].toString();
// 检查文件大小
struct stat st;
if (stat(filename.data(), &st) != 0) {
throwError("File not found");
return;
}
if (st.st_size > MAX_FILE_SIZE) {
throwError("File too large");
return;
}
// 读取文件
String content = file_get_contents(filename);
return content;
}// ✅ 检查内存使用
void* safe_alloc(size_t size) {
static size_t total_allocated = 0;
if (total_allocated + size > MAX_MEMORY) {
throwError("Memory limit exceeded");
return nullptr;
}
void *ptr = malloc(size);
if (ptr) {
total_allocated += size;
}
return ptr;
}原因:
- 访问已释放的内存
- 空指针解引用
- 数组越界
解决方法:
# 使用 gdb 调试
gdb php core.php
(gdb) run
(gdb) bt # 查看堆栈
# 使用 valgrind 检查
valgrind --leak-check=full php test.php检测:
valgrind --leak-check=full --show-leak-kinds=all php test.php预防:
// ✅ 使用 RAII
void safe_function() {
Variant var = createValue();
// 自动释放
}
// ❌ 手动管理容易泄漏
void unsafe_function() {
zval *zv = new zval;
// 忘记 delete
}症状:
- 变量意外改变
- 数据损坏
解决:
// ✅ 正确复制
Variant copy = original; // 增加引用计数
// ✅ 分离副本
zval_copy_ctor(&dest);检查:
php -i | grep "Thread Safety"处理:
#ifdef ZTS
#define THREAD_LOCAL thread_local
#else
#define THREAD_LOCAL
#endif
THREAD_LOCAL static int counter = 0;- 参数验证完整
- 错误处理适当
- 返回值正确
- 边界条件处理
- 无不必要的拷贝
- 使用了缓存
- 批量操作优化
- 无内存泄漏
- 输入已验证
- SQL 已转义
- 路径已清理
- 资源有限制
- 代码有注释
- 命名清晰
- 函数简短
- 有单元测试
- 《PHP 内核剖析》
- 《深入理解 PHP 内核》
本文档最后更新:2026-03-27