我正在学习如何编写PHP扩展,遇到了一个在Interweb上找不到答案的问题。我正在尝试使用PHP扩展中的C++类,尽管它似乎可以在CLI中工作,但valgrind报告了问题。
我的全局设置如下:
ZEND_BEGIN_MODULE_GLOBALS(myext)
MyClass *myClass;
ZEND_END_MODULE_GLOBALS(myext)
这个类被实例化为一个扩展全局如下:
static void myext_init_globals(zend_myext_globals *myext_globals TSRMLS_DC)
{
myext_globals->myClass = (MyClass*)pemalloc(sizeof(MyClass), 0);
MyClass *myClass = new (myext_globals->myClass) MyClass();
}
使用释放
static void myext_destroy_globals(zend_myext_globals *myext_globals TSRMLS_DC)
{
pefree(myext_globals->myClass, 0);
}
MyClass.h:
class MyClass
{
private:
std::string strSomeText;
public:
MyClass();
~MyClass();
};
MyClass.cpp:
MyClass::MyClass()
{
strSomeText = "ABC"; // <-- this causes valgrind errors!
}
我发现,如果我可以将strSomeText作为一个静态成员变量,那么valgrind不会报告任何错误。
此外,如果我试图在MyClass构造函数中分配内存,然后在MyClass析构函数中删除或释放内存,valgrind会再次报告泄漏。
理想情况下,我希望能够获取"任何ol类"并使用它,而不必对其进行重大更改,只需将其作为PHP全局扩展即可。
更新:我的问题是,如何使用一个"泛型"C++类来使用std::string之类的东西,或者分配/取消分配内存(在构造函数/析构函数或成员方法中)?我发现,如果我在MyClass中包含php.h,然后使用emalloc/pemalloc分配内存,然后使用efree和pefree取消分配内存,那么一切都正常,valgrind没有报告内存泄漏。然而,这并不理想。必须有一种方法可以使用"通用"C++类(即,不必重构为对PHP扩展友好的类)作为PHP全局扩展(如我上面的示例所示)。
为什么要使用pealloc/pefree?我不知道如何调用myClass的析构函数?试试这个,没有ZEND_END_MODULE_GLOBALS:
std::unique_ptr<MyClass> myClass;
PHP_MINIT_FUNCTION(myext)
{
myClass = std::unique_ptr<MyClass> (new MyClass);
}
PHP_MSHUTDOWN_FUNCTION(myext)
{
myClass.reset();
}
在PHP模块中使用C++确实很棘手。在出现错误或HTTP请求中断的情况下,PHP函数调用永远不会返回。这会中断异常和析构函数调用。因此,最好将C++代码与PHP函数调用完全分离。PHP_MINIT_FUNCTION和PHP_MSHUTDOWN_FUNCTION保证在模块启动和退出时被调用。然而,PHP作为一个Apache模块可以创建一个伪版本的模块,然后才创建真正的模块。PHP_MINIT_FUNCTION和PHP_MSHUTDOWN_FUNCTION也被调用用于伪版本,因此您应该避免在此处分配巨大的对象和/或进行大量磁盘读/写等操作。