简体   繁体   English

PHP扩展与C ++

[英]PHP Extension with C++

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++. 我最近开始研究编写PHP扩展,并通读了本文 ,它描述了使用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. 我知道它找不到Container构造函数的实际实现,但是我不知道如何使它在正确的位置显示。

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. 在发布到这里之前,我已经尽力减少了绒毛,但是php接口代码中仍然有很多不足之处。 The code is as follows: 代码如下:

config.m4: 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: 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: 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: 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 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 谢谢你尽你所能的帮助

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. 编译模板类时,由于C ++编译器需要模板参数才能编译模板类,因此必须可从头文件中获得实现。 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. 如果C ++编译器仅编译internals.cpp,则由于类型T未知,因此将无法创建任何代码。 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. 它只能在interface.cpp的上下文中进行编译,但当时容器的实际实现尚不适用于编译器。

So the problem is simply that Complier is never compiled. 因此,问题很简单,就是Complier从未被编译。

You could simply add the implementation of Compiler below its declaration, in the internals.h file. 您只需在internals.h文件中的声明下面添加Compiler的实现即可。

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. 但是,此创建是在编译时而不是在运行时发生的,因此您的扩展程序需要使用PHP应用程序显式实例化的所有模板。

This can be done simply by creating one, put this in internals.cpp 只需创建一个并将其放入internals.cpp即可完成

template class Container<float>;

or whatever type of container you need. 或您需要的任何类型的容器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM