Расширение PHP с C ++ - PullRequest
       1

Расширение PHP с C ++

5 голосов
/ 03 декабря 2010

Я недавно начал изучать написание расширений 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 );"

Спасибо за любую помощь ваммогу предложить

Ответы [ 2 ]

3 голосов
/ 03 декабря 2010

При компиляции шаблонных классов реализация должна быть доступна из заголовочного файла, поскольку компилятору C ++ требуются аргументы шаблона для компиляции шаблонного класса.Если бы компилятор C ++ компилировал только internals.cpp, он не смог бы создать никакого кода, так как тип T не известен.Он мог бы скомпилировать его только в контексте interface.cpp, но фактическая реализация Container в это время не доступна для компилятора.

Так что проблема в том, что Complier никогда не компилируется.

Вы можете просто добавить реализацию Compiler ниже ее объявления, в файле internals.h.

2 голосов
/ 03 декабря 2010

Вы создали шаблон, но никогда не создавали его экземпляр в расширении - шаблоны по определению не являются чем-то конкретным, но создаются «по требованию», когда что-то нужно.Однако это создание происходит во время компиляции, а не во время выполнения, поэтому вашему расширению нужны все шаблоны, которые ваши PHP-приложения будут использовать для явного создания экземпляров.

Это можно сделать, просто создав один, поместив его во внутреннее устройство.cpp

template class Container<float>;

или любой другой тип контейнера, который вам нужен.

...