- My use case :
[A] is a main program written in C++
[B] is a C++ "plugin" of [A], compiled in a shared library dynamically loaded (dlopen-like functions)
[CX] are a libphpX.so C shared libraries compiled with "--enable-embed", where "X" is the PHP version number (5.4, 5.3, ..., 5.0)
User calls [A] with the version of PHP he/she wants to use (ex: 5.4), [A] loads [B] using dynamic loading (dlopen-like functions) and propagate the requested PHP version number, and when executed, [B] tries to load this specific version of [CX] (let's say [C5.4]) using dlopen-like functions.
- My status :
[A] is successfully compiled in program "a"
[B] is successfully compiled in shared library in file libb.so
C5.4 is successfully compiled in shared library libphp5.4.so (using ./configure --disable-all --enable-embed && make)
- My probem :
When trying to load B from A, I got the error "unknown symbol executor_globals_id" when calling dlopen("libb.so").
executor_globals seems to be a global variable of libphp5.so (there are several other global variables in libphp5.so).
I can't use dlsym() because it is a dlopen() failure.
I tried refactoring my code using PHP functions (in [B]), and I have also the same error with other global variables (such as php_embed_module, ...)
I tried to define executor_globals_id and it worked, but I felt on next global variables error.
I want to be able not to define all PHP global variable in my [B] plugin if possible.
- My environment :
MacOSX 10.7 64 bits
- Extra information :
[A] LDFLAGS = -flat_namespace -undefined suppress
[B] LDFLAGS = -flat_namespace -undefined suppress
dlopen() is used with "RTLD_LAZY | RTLD_GLOBAL"
Do you have an idea on how to reach my use case ?
Thanks !
Olivier Hoareau
Hi,
- My use case :
[A] is a main program written in C++
[B] is a C++ "plugin" of [A], compiled in a shared library dynamically loaded (dlopen-like functions)
[CX] are a libphpX.so C shared libraries compiled with "--enable-embed", where "X" is the PHP version number (5.4, 5.3, ..., 5.0)
Do you have an idea on how to reach my use case ?
The simple answer is: You can't do this this way and you have to provide
a [B] for each version of PHP linking [CX].
If you really really really want to do this I see two ways, where the
first is close to what you are doing but still evil:
The approach is to add a Loader library which is loaded by [A] and then
first loads [CX] and then the current [B]. This is evil as PHP does not
guarantee API/ABI to stay the same over minor versions. So some random
function in libphp, which your [B] might be using, can change the
signature which will easily lead to hard to debug crashes of your
application.
The better approach is to create your own SAPI which exports a for your
purpose stable API which you then can load. Building a SAPI is not
really hard.
https://github.com/johannes/pconn-sapi/blob/master/pconnect-sapi.c is
probably one of the most simple SAPIs, there I'm basically exporting
three more abstracted functions: pconn_init_php(), pconn_shutdown_php()
and pconn_do_request() which in your case would form your stable API for
your new SAPI. (mind that my pconn_do_request() includes a TSRM
parameter, in case you don't need threaded execution, can be dropped,
else you probably have to abstracted it away (make it void* and export
clean APIs to keep thread context), too)
johannes
Hi Johannes,
Many thanks !
I think SAPI approach is a good one for my purpose, I can then avoid using directly the PHP/embed global variables and use functions (i.e. the stable api you mention) to manipulate these structures. I need to create new SAPI and compile libphp5.so including this SAPI, if I understood well.
I read recently about the "load CX before B" solution, but I think it is not the way I want to do it (requiring the Loader to know about PHP's function => strong coupling) because PHP is only one plugin (I will have Python and others as well), and I want a more generic solution.
It is also possible to compile using shared but dynamic linking (and not dynamic loading) CX over B (or static), requiring me to provide many B versions (I plan to have 5.1 to 5.4 PHP support, if possible, with multiple minor versions).
I really like the SAPI approach, and probably give it a try in few days.
Thanks again !
Olivier H.
Le 15 oct. 2011 à 01:24, Johannes Schlüter a écrit :
Hi,
- My use case :
[A] is a main program written in C++
[B] is a C++ "plugin" of [A], compiled in a shared library dynamically loaded (dlopen-like functions)
[CX] are a libphpX.so C shared libraries compiled with "--enable-embed", where "X" is the PHP version number (5.4, 5.3, ..., 5.0)Do you have an idea on how to reach my use case ?
The simple answer is: You can't do this this way and you have to provide
a [B] for each version of PHP linking [CX].If you really really really want to do this I see two ways, where the
first is close to what you are doing but still evil:The approach is to add a Loader library which is loaded by [A] and then
first loads [CX] and then the current [B]. This is evil as PHP does not
guarantee API/ABI to stay the same over minor versions. So some random
function in libphp, which your [B] might be using, can change the
signature which will easily lead to hard to debug crashes of your
application.The better approach is to create your own SAPI which exports a for your
purpose stable API which you then can load. Building a SAPI is not
really hard.
https://github.com/johannes/pconn-sapi/blob/master/pconnect-sapi.c is
probably one of the most simple SAPIs, there I'm basically exporting
three more abstracted functions: pconn_init_php(), pconn_shutdown_php()
and pconn_do_request() which in your case would form your stable API for
your new SAPI. (mind that my pconn_do_request() includes a TSRM
parameter, in case you don't need threaded execution, can be dropped,
else you probably have to abstracted it away (make it void* and export
clean APIs to keep thread context), too)johannes