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