2010-12-03 70 views
5

我最近開始着眼於編寫PHP擴展,並通過this article進行了閱讀,其中描述了使用C++構建擴展的一個起點。當我開始自定義時,我遇到了一些麻煩,試圖將一些功能拆分成單獨的文件。一切都編譯和鏈接沒有問題,但是當我嘗試實際使用擴展時發生錯誤。確切的信息是:使用C++的PHP擴展

$ php -dextension=test.so -r "var_dump(new Test);" 
php: symbol lookup error: /etc/php/ext/test.so: undefined symbol: _ZN9ContainerI4TestEC1EP17_zend_class_entry 

我在兩臺電腦上試過這個,兩者都遇到同樣的問題。我知道它無法找到Container構造函數的實際實現,但我不知道如何讓它在正確的位置查找。

我試過在發佈之前儘可能多地剪掉絨毛,但在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);" 

感謝您的幫助,您可以提供

回答

3

當編譯模板類,實現必須是可用從頭文件開始,因爲C++編譯器需要模板參數才能編譯模板類。如果一個C++編譯器只編譯internals.cpp,它將不能創建任何代碼,因爲類型T是未知的。它只能在interface.cpp的上下文中進行編譯,但當時編譯器無法使用Container的實際實現。

所以問題是,編譯器從來沒有編譯過。

您可以在internals.h文件中簡單地在其聲明下添加Compiler的實現。

+0

這是有道理的。我不能說我喜歡像這樣污染我的頭文件,但它工作,我不是語言設計師。感謝您的幫助 – Nycto 2010-12-03 16:48:12

2

你已經創建了一個模板,但從未在擴展中創建過一個實例 - 根據定義,模板不是一個具體的東西,而是在需要的時候按需創建的。但是,這種創建是在編譯時發生的,而不是運行時發生的,所以您的擴展需要您的PHP應用程序將用於顯式實例化的所有模板。

這可以簡單地通過創建一個完成,把這個internals.cpp

template class Container<float>; 

或者任何一種你需要的容器。

+0

這工作,但我不喜歡它作爲一種解決方案。這似乎造成了很多維護開銷。在我看來,跟蹤所需模板實現的列表應該是編譯器的責任。不過,謝謝你的迴應。 – Nycto 2010-12-03 17:06:45