2010-12-03 19 views
5

Recientemente comencé a buscar extensiones de PHP y leo por this article, que describe un punto de partida para construir una extensión usando C++. A medida que comencé a personalizar, me encontré con un problema al intentar dividir parte de la funcionalidad en un archivo separado. Todo compila y enlaces sin problemas, pero se produce un error cuando trato de usar la extensión. El mensaje exacto es:Extensión de PHP con 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 

Intenté esto en dos computadoras y ambas experimentan el mismo problema. Entiendo que no puede encontrar la implementación real para el constructor del contenedor, pero no sé cómo hacer que se vea en el lugar correcto.

He tratado de cortar la mayor cantidad de pelusa que puedo antes de publicar aquí, pero todavía hay muchos errores en el código de la interfaz de php. El código es el siguiente:

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; 
} 

estoy construyendo mediante los siguientes comandos:

$ phpize 
$ ./configure --enable-test 
$ make && make install 
$ php -dextension=test.so -r "var_dump(new Test);" 

Gracias por cualquier ayuda que pueda ofrecer

Respuesta

3

Al compilar clases de plantilla, la aplicación debe estar disponible desde el archivo de encabezado, ya que el compilador de C++ necesita los argumentos de la plantilla para compilar una clase de plantilla. Si un compilador de C++ compilara solo internals.cpp, no podría crear ningún código ya que no se conoce el tipo T. Solo podría compilarlo en el contexto de interface.cpp, pero la implementación real de Container no está disponible para el compilador en ese momento.

Así que el problema es simplemente que Complier nunca se compila.

Simplemente puede agregar la implementación del compilador debajo de su declaración, en el archivo internals.h.

+0

Esto tiene sentido. No puedo decir que me guste tener que contaminar mis archivos de encabezado como este, pero funcionó y no soy el diseñador de idiomas.Gracias por su ayuda – Nycto

2

Ha creado una plantilla, pero nunca creó una instancia de eso en la extensión - las plantillas son, por definición, no una cosa concreta, pero se crean 'a pedido' cuando algo necesita una. Sin embargo, esta creación ocurre en tiempo de compilación, no en tiempo de ejecución, por lo que su extensión necesita todas las plantillas que sus aplicaciones PHP usarán para crear instancias explícitas.

Esto puede hacerse simplemente mediante la creación de una, poner esto en internals.cpp

template class Container<float>; 

o cualquier tipo de contenedor que necesita.

+0

Esto funcionó, pero no me gusta como una solución. Parece crear una gran cantidad de gastos generales de mantenimiento. El seguimiento de la lista de implementaciones de plantillas requeridas debería ser responsabilidad del compilador, en mi opinión. Sin embargo, gracias por la respuesta. – Nycto