2010-12-03 4 views
5

J'ai récemment commencé à chercher à écrire des extensions PHP et j'ai lu this article, qui décrit un point de départ pour construire une extension en C++. Comme j'ai commencé à personnaliser, j'ai rencontré un problème en essayant de partager certaines fonctionnalités dans un fichier séparé. Tout compile et relie sans problèmes, mais une erreur se produit lorsque j'essaie d'utiliser réellement l'extension. Le message exact est:PHP Extension avec 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 

J'ai essayé ceci sur deux ordinateurs et les deux éprouvent le même problème. Je comprends qu'il ne peut pas trouver l'implémentation réelle pour le constructeur Container, mais je ne sais pas comment l'obtenir au bon endroit.

J'ai essayé de découper autant de fluff que je peux avant de poster ici, mais il y a encore beaucoup de cruft dans le code d'interface php. Le code est le suivant:

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

Je construis à l'aide des commandes suivantes:

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

Merci pour toute aide que vous pouvez offrir

Répondre

3

Lors de la compilation des classes de modèle, la mise en œuvre doit être disponible à partir du fichier d'en-tête car le compilateur C++ a besoin des arguments de modèle pour compiler une classe de modèle. Si un compilateur C++ devait compiler uniquement internals.cpp, il ne serait pas capable de créer du code car le type T n'est pas connu. Il serait seulement capable de le compiler dans le contexte d'interface.cpp mais l'implémentation réelle de Container n'est pas disponible pour le compilateur à ce moment-là.

Donc le problème est simplement que Complier n'est jamais compilé.

Vous pouvez simplement ajouter l'implémentation du compilateur sous sa déclaration, dans le fichier internals.h.

+0

Cela a du sens. Je ne peux pas dire que j'aime polluer mes fichiers d'en-tête comme ça, mais ça a marché et je ne suis pas le concepteur de langage.Merci pour votre aide – Nycto

2

Vous avez créé un modèle, mais jamais créé une instance de celui-ci dans l'extension - les modèles ne sont pas, par définition, concrets, mais sont créés «à la demande» lorsque quelque chose en a besoin. Cependant, cette création a lieu au moment de la compilation, et non lors de l'exécution. Votre extension a donc besoin de tous les modèles que vos applications PHP utiliseront pour être explicitement instanciés.

Cela peut être fait simplement en créer un, mettre cela en internals.cpp

template class Container<float>; 

ou tout autre type de conteneur dont vous avez besoin.

+0

Cela a fonctionné, mais je ne l'aime pas comme une solution. Il semble créer beaucoup de frais généraux de maintenance. Le suivi de la liste des implémentations de modèles requis devrait être de la responsabilité du compilateur, à mon avis. Cependant, merci pour la réponse. – Nycto