PHP 扩展与 C++
我最近开始考虑编写 PHP 扩展,并通读了 这篇文章,它描述了使用 C++ 构建扩展的起点。当我开始自定义时,我在尝试将某些功能拆分到单独的文件中时遇到了麻烦。一切编译和链接都没有问题,但是当我尝试实际使用扩展时会发生错误。确切的消息是:
$ php -dextension=test.so -r "var_dump( new Test );"
php: symbol lookup error: /etc/php/ext/test.so: undefined symbol: _ZN9ContainerI4TestEC1EP17_zend_class_entry
我在两台计算机上尝试过此操作,并且都遇到了相同的问题。我知道它无法找到容器构造函数的实际实现,但我不知道如何让它在正确的位置查找。
在发布到这里之前,我已经尝试尽可能多地删去一些废话,但是 php 接口代码中仍然存在很多废话。代码如下:
config.m4:
PHP_ARG_ENABLE(test,
[Whether to enable the "test" extension],
[ --enable-test Enable "test" extension support])
if test $PHP_TEST != "no"; then
PHP_REQUIRE_CXX()
PHP_SUBST(TEST_SHARED_LIBADD)
PHP_ADD_LIBRARY(stdc++, 1, TEST_SHARED_LIBADD)
PHP_NEW_EXTENSION(test, interface.cpp internals.cpp, $ext_shared)
fi
interface.h:
#ifndef INTERFACE_H_
#define INTERFACE_H_
#define PHP_TEST_EXTNAME "test"
#define PHP_TEST_EXTVER "0.1"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#endif
interface.cpp:
#include "interface.h"
#include "internals.h"
#include "php.h"
class Test {};
extern zend_module_entry test_module_entry;
zend_object_handlers test_object_handlers;
zend_class_entry *test_ce;
void test_free_storage(void *object TSRMLS_DC)
{
delete (Container<Test> *) object;
}
zend_object_value test_create_handler( zend_class_entry* classInfo TSRMLS_DC )
{
Container<Test> *obj = new Container<Test>( classInfo );
zend_object_value retval;
retval.handle = zend_objects_store_put(
obj, NULL, test_free_storage, NULL TSRMLS_CC
);
retval.handlers = &test_object_handlers;
return retval;
}
PHP_METHOD(Test, __construct)
{
Test* test = new Test;
Container<Test> *obj = (Container<Test> *) zend_object_store_get_object(getThis() TSRMLS_CC);
obj->cpp = test;
}
function_entry test_methods[] = {
PHP_ME(Test, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
{NULL, NULL, NULL}
};
PHP_MINIT_FUNCTION(test)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "Test", test_methods);
test_ce = zend_register_internal_class(&ce TSRMLS_CC);
test_ce->create_object = test_create_handler;
memcpy(
&test_object_handlers,
zend_get_std_object_handlers(),
sizeof(zend_object_handlers)
);
test_object_handlers.clone_obj = NULL;
return SUCCESS;
}
zend_module_entry test_module_entry = {
STANDARD_MODULE_HEADER,
PHP_TEST_EXTNAME,
NULL, /* Functions */
PHP_MINIT(test), /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
PHP_TEST_EXTVER,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_TEST
extern "C" {
ZEND_GET_MODULE(test)
}
#endif
internals.h:
#ifndef INTERNALS_H_
#define INTERNALS_H_
#include "zend.h"
template <class T>
class Container
{
private:
zend_object zend;
public:
T* cpp;
Container ( zend_class_entry* classInfo );
};
#endif /* INTERNALS_H_ */
internals.cpp
#include "internals.h"
#include "zend.h"
template <class T>
Container<T>::Container ( zend_class_entry* classInfo )
: zend()
{
zend.ce = classInfo;
}
我正在使用以下命令构建它:
$ phpize
$ ./configure --enable-test
$ make && make install
$ php -dextension=test.so -r "var_dump( new Test );"
感谢您提供的任何帮助
I recently started looking at writing PHP extensions and I read through this article, which describes a starting point for building an extension using C++. As I've started to customize, I've run into a hitch trying to split some of the functionality into a separate file. Everything compiles and links without problems, but an error occurs when I try to actually use the extension. The exact message is:
$ php -dextension=test.so -r "var_dump( new Test );"
php: symbol lookup error: /etc/php/ext/test.so: undefined symbol: _ZN9ContainerI4TestEC1EP17_zend_class_entry
I tried this on two computers and both experience the same problem. I understand that it can't find the actual implementation for the Container constructor, but I don't know how to get it to look in the right place.
I've tried to cut out as much of the fluff as I can before posting here, but there is still a lot of cruft in the php interface code. The code is as follows:
config.m4:
PHP_ARG_ENABLE(test,
[Whether to enable the "test" extension],
[ --enable-test Enable "test" extension support])
if test $PHP_TEST != "no"; then
PHP_REQUIRE_CXX()
PHP_SUBST(TEST_SHARED_LIBADD)
PHP_ADD_LIBRARY(stdc++, 1, TEST_SHARED_LIBADD)
PHP_NEW_EXTENSION(test, interface.cpp internals.cpp, $ext_shared)
fi
interface.h:
#ifndef INTERFACE_H_
#define INTERFACE_H_
#define PHP_TEST_EXTNAME "test"
#define PHP_TEST_EXTVER "0.1"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#endif
interface.cpp:
#include "interface.h"
#include "internals.h"
#include "php.h"
class Test {};
extern zend_module_entry test_module_entry;
zend_object_handlers test_object_handlers;
zend_class_entry *test_ce;
void test_free_storage(void *object TSRMLS_DC)
{
delete (Container<Test> *) object;
}
zend_object_value test_create_handler( zend_class_entry* classInfo TSRMLS_DC )
{
Container<Test> *obj = new Container<Test>( classInfo );
zend_object_value retval;
retval.handle = zend_objects_store_put(
obj, NULL, test_free_storage, NULL TSRMLS_CC
);
retval.handlers = &test_object_handlers;
return retval;
}
PHP_METHOD(Test, __construct)
{
Test* test = new Test;
Container<Test> *obj = (Container<Test> *) zend_object_store_get_object(getThis() TSRMLS_CC);
obj->cpp = test;
}
function_entry test_methods[] = {
PHP_ME(Test, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
{NULL, NULL, NULL}
};
PHP_MINIT_FUNCTION(test)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "Test", test_methods);
test_ce = zend_register_internal_class(&ce TSRMLS_CC);
test_ce->create_object = test_create_handler;
memcpy(
&test_object_handlers,
zend_get_std_object_handlers(),
sizeof(zend_object_handlers)
);
test_object_handlers.clone_obj = NULL;
return SUCCESS;
}
zend_module_entry test_module_entry = {
STANDARD_MODULE_HEADER,
PHP_TEST_EXTNAME,
NULL, /* Functions */
PHP_MINIT(test), /* MINIT */
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
PHP_TEST_EXTVER,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_TEST
extern "C" {
ZEND_GET_MODULE(test)
}
#endif
internals.h:
#ifndef INTERNALS_H_
#define INTERNALS_H_
#include "zend.h"
template <class T>
class Container
{
private:
zend_object zend;
public:
T* cpp;
Container ( zend_class_entry* classInfo );
};
#endif /* INTERNALS_H_ */
internals.cpp
#include "internals.h"
#include "zend.h"
template <class T>
Container<T>::Container ( zend_class_entry* classInfo )
: zend()
{
zend.ce = classInfo;
}
I'm building it using the following commands:
$ phpize
$ ./configure --enable-test
$ make && make install
$ php -dextension=test.so -r "var_dump( new Test );"
Thanks for any help you can offer
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编译模板类时,必须可从头文件获取实现,因为 C++ 编译器需要模板参数才能编译模板类。如果 C++ 编译器仅编译Internals.cpp,则它将无法创建任何代码,因为类型 T 未知。它只能在interface.cpp的上下文中编译它,但当时编译器无法获得Container的实际实现。
所以问题很简单,Complier 从未被编译过。
您可以简单地将 Compiler 的实现添加到其声明下方,即 insides.h 文件中。
When compiling template classes, the implementation must be available from the header file since the C++ complier needs the template arguments in order to compile a template class. If a C++ compiler were to compile just internals.cpp, it would not be able to create any code as the type T is not known. It would only be able to compile it in the context of interface.cpp but the actual implementation of Container is not available to the compiler at that time.
So the problem is simply that Complier is never compiled.
You could simply add the implementation of Compiler below its declaration, in the internals.h file.
您已经创建了一个模板,但从未在扩展中创建该模板的实例 - 根据定义,模板不是一个具体的东西,而是在需要时“按需”创建的。但是,此创建发生在编译时,而不是运行时,因此您的扩展需要 PHP 应用程序将使用的所有模板进行显式实例化。
这可以通过简单地创建一个容器来完成,将其放入internals.cpp
或您需要的任何类型的容器中。
You've created a template, but never created an instance of that in the extension - templates are, by definition, not a concrete thing, but are created 'on demand' when something needs one. However, this creation happens at compile-time, not runtime, so your extension needs all templates that your PHP apps will use to be explicitly instantiated.
This can be done simply by creating one, put this in internals.cpp
or whatever type of container you need.