diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | include/extension.h | 98 | ||||
-rw-r--r-- | include/function.h | 4 | ||||
-rw-r--r-- | include/functions.h (renamed from src/functions.h) | 41 | ||||
-rw-r--r-- | include/request.h | 6 | ||||
-rw-r--r-- | src/extension.cpp | 176 | ||||
-rw-r--r-- | src/functions.cpp | 54 | ||||
-rw-r--r-- | src/hiddenpointer.h | 159 | ||||
-rw-r--r-- | src/includes.h | 4 |
9 files changed, 379 insertions, 165 deletions
@@ -3,7 +3,7 @@ INCLUDE_DIR = ${PREFIX}/include LIBRARY_DIR = ${PREFIX}/lib all: - cd src; $(MAKE) -j + cd src; $(MAKE) tests: cd tests; $(MAKE) diff --git a/include/extension.h b/include/extension.h index 701da86..daff45e 100644 --- a/include/extension.h +++ b/include/extension.h @@ -11,14 +11,13 @@ * as module in a webserver) many requests are handled by the same extension * instance. * + * This is a template class. You need to pass in the type of an object + * that you use for storing request specific state information. + * * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> * @copyright 2013 Copernica BV */ - -/** - * Forward declarations - */ -struct _zend_module_entry; +#include <php5/Zend/zend_modules.h> /** * Set up namespace @@ -26,28 +25,29 @@ struct _zend_module_entry; namespace PhpCpp { /** - * Forward definitions - */ -class Functions; - -/** * Class definition */ class Extension { public: /** - * Extension that defines a number of functions right away + * Constructor that defines a number of functions right away * @param name Extension name * @param version Extension version string * @param functions The functions that are defined */ - Extension(const char *name, const char *version, const std::initializer_list<Function> &functions = {}); + Extension(const char *name, const char *version, const Functions &functions); + + /** + * No copy'ing and no moving + */ + Extension(const Extension &extension) = delete; + Extension(Extension &&extension) = delete; /** * Destructor */ - virtual ~Extension(); + virtual ~Extension() {} /** * Initialize the extension. @@ -105,14 +105,14 @@ public: */ bool startRequest() { - // failure if we already have a request - if (_request) return false; - - // create the request - _request = request(); - - // and initialize it - return _request->initialize(); +// // failure if we already have a request +// if (_request) return false; +// +// // create the request +// _request = request(); +// +// // and initialize it +// return _request->initialize(); } /** @@ -125,57 +125,25 @@ public: */ bool endRequest() { - // request must exist - if (!_request) return false; - - // finalize the request - bool result = _request->finalize(); - - // destruct the request object - delete _request; - - // done - return result; +// // request must exist +// if (!_request) return false; +// +// // finalize the request +// bool result = _request->finalize(); +// +// // destruct the request object +// delete _request; +// +// // done +// return result; } - /** - * Internal method to get access to the entry - * @return zend_module_entry - * @internal - */ - _zend_module_entry *entry(); - private: /** - * Extension name - * @var char* - */ - const char *_name; - - /** - * Extension version - * @var char* - */ - const char *_version; - - /** - * The functions that are defined - * @var vector - */ - Functions *_functions; - - /** * The information that is passed to the Zend engine * @var zend_module_entry */ - _zend_module_entry *_entry = NULL; - - /** - * The current request being processed - * @var Request - */ - Request *_request = NULL; - + zend_module_entry _entry; }; diff --git a/include/function.h b/include/function.h index a7dc9b0..fec83e9 100644 --- a/include/function.h +++ b/include/function.h @@ -91,9 +91,7 @@ public: * @param arguments The actual arguments that were passed * @return Variable Return value */ - virtual Value invoke(const Request *request, const std::initializer_list<Value> &arguments) - { - } + virtual Value invoke(const Request *request, const std::initializer_list<Value> &arguments); /** * Get access to the internal object diff --git a/src/functions.h b/include/functions.h index bc08b8a..49f38b7 100644 --- a/src/functions.h +++ b/include/functions.h @@ -23,47 +23,23 @@ public: * Constructor * @param functions The functions to parse */ - Functions(const std::initializer_list<Function> &functions) : _functions(functions) - { - // allocate the function entries - _entries = new zend_function_entry[functions.size() + 1]; - - // keep iterator counter - int i = 0; - - // loop through the functions - for (auto it = begin(functions); it != functions.end(); it++) - { - // let the callable fill the array - it->internal()->fill(&_entries[i++]); - } - - // last entry should be set to all zeros - zend_function_entry *last = &_entries[i]; - - // all should be set to zero - memset(last, 0, sizeof(zend_function_entry)); - } + Functions(const std::initializer_list<Function> &functions); /** * Destructor */ - virtual ~Functions() - { - delete[] _entries; - } - + virtual ~Functions(); + +private: /** * Retrieve the internal data * @return zend_function_entry* */ - zend_function_entry *internal() + zend_function_entry *internal() const { return _entries; } - -private: /** * The internal entries * @var zend_function_entry* @@ -72,10 +48,15 @@ private: /** * Vector of functions (we need this because the function objects must - * remain in memory) + * remain in memory, so that we can call the invoke methods on them) * @var vector */ std::vector<Function> _functions; + + /** + * The extension has access to the private elements + */ + friend class Extension; }; /** diff --git a/include/request.h b/include/request.h index f1cf25b..f37a449 100644 --- a/include/request.h +++ b/include/request.h @@ -74,6 +74,12 @@ protected: * @var Extension* */ Extension *_extension; + + /** + * Optional extra data + * @var Type + */ + Type _data; }; /** diff --git a/src/extension.cpp b/src/extension.cpp index c995f0b..ee22794 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -12,35 +12,79 @@ namespace PhpCpp { /** - * Pointer to the one and only extension - * @var Extension + * If this extension is compiled for a PHP version with multi + * threading support, we need an additional header file */ -static Extension *extension; +#ifdef ZTS +#include "TSRM.h" +#endif /** - * Constructor - * @param name Name of the extension - * @param version Version number + * The way how PHP C API deals with "global" variables is stupid. + * + * This is supposed to turn into a structure that is going to be + * instantiated for each parallel running request, and for which the + * PHP engine allocates a certain amount of memory, and a magic + * pointer that is passed and should be forwarded to every thinkable + * PHP function. + * + * We don't like this architecture. We have our own request object + * that makes much more sense, and that we use. However, we need + * to assign this object somewhere, so that's what we do in this + * one and only global variable */ -Extension::Extension(const char *name, const char *version, const std::initializer_list<Function> &functions) : _name(name), _version(version) -{ - // allocate functions - _functions = new Functions(functions); - - // store pointer to the one and only extension - extension = this; -} +ZEND_BEGIN_MODULE_GLOBALS(phpcpp) + Request *request; +ZEND_END_MODULE_GLOBALS(phpcpp) + +/** + * And now we're going to define a macro. This also is a ridiculous + * architecture from PHP to get access to a variable from the + * structure above. + */ +#ifdef ZTS +#define REQUEST_G(v) TSRMG(phpcpp_globals_id, zend_phpcpp_globals *, v) +#else +#define REQUEST_G(v) (phpcpp_globals.v) +#endif + +/** + * We're almost there, we now need to declare an instance of the + * structure defined above (if building for a single thread) or some + * sort of impossible to understand magic pointer-to-a-pointer (for + * multi-threading builds). We make this a static variable because + * this already is bad enough. + */ +static ZEND_DECLARE_MODULE_GLOBALS(phpcpp) + +/** + * Function that must be defined to initialize the "globals" + * We do not have to initialize anything, but PHP needs to call this + * method (crazy) + * @param globals + */ +static void php_phpcpp_init_globals(zend_phpcpp_globals *globals) {} + +/** + * The extension is a sort of singleton, so we keep one pointer to it here + * @var Extension + */ +static Extension *extension = nullptr; + /** - * Destructor + * Helper method to get back the current extension object + * @return Extension */ -Extension::~Extension() +static Extension *get_extension() { - // deallocate functions - delete _functions; - - // deallocate entry - if (_entry) delete _entry; + // retrieve the extension or module name (because PHP of course does + // not pass such extremely useful information as they've no clue how + // to make a decent API + zend_module_entry *module = EG(current_module); + + // the pointer to the extension is hidden in front of the name + return HiddenPointer<Extension>(module->name); } /** @@ -51,8 +95,14 @@ Extension::~Extension() */ static int extension_startup(INIT_FUNC_ARGS) { + + + + // initialize and allocate the "global" variables +// ZEND_INIT_MODULE_GLOBALS(hello, php_phpcpp_init_globals, NULL); + // initialize the extension - return BOOL2SUCCESS(extension->initialize()); + return BOOL2SUCCESS(get_extension()->initialize()); } /** @@ -63,10 +113,8 @@ static int extension_startup(INIT_FUNC_ARGS) */ static int extension_shutdown(SHUTDOWN_FUNC_ARGS) { - std::cout << "extension_shutdown" << std::endl; - // finalize the extension - return BOOL2SUCCESS(extension->finalize()); + return BOOL2SUCCESS(get_extension()->finalize()); } /** @@ -78,7 +126,7 @@ static int extension_shutdown(SHUTDOWN_FUNC_ARGS) static int request_startup(INIT_FUNC_ARGS) { // create the request - return BOOL2SUCCESS(extension->startRequest()); + return BOOL2SUCCESS(get_extension()->startRequest()); } /** @@ -90,49 +138,49 @@ static int request_startup(INIT_FUNC_ARGS) static int request_shutdown(INIT_FUNC_ARGS) { // end the request - return BOOL2SUCCESS(extension->endRequest()); + return BOOL2SUCCESS(get_extension()->endRequest()); } + /** - * Retrieve a pointer to the entry - * @return zend_module_entry + * Constructor + * @param name Name of the extension + * @param version Version number */ -zend_module_entry *Extension::entry() +Extension::Extension(const char *name, const char *version, const Functions &functions) { - // already initialized? - if (_entry) return _entry; - - // allocate now - _entry = new zend_module_entry; - - // assign all members - _entry->size = sizeof(zend_module_entry); // size of the data - _entry->zend_api = ZEND_MODULE_API_NO; // api number - _entry->zend_debug = ZEND_DEBUG; // debug mode enabled? - _entry->zts = USING_ZTS; // is thread safety enabled? - _entry->ini_entry = NULL; // the php.ini record - _entry->deps = NULL; // dependencies on other modules - _entry->name = _name; // extension name - _entry->functions = _functions->internal(); // functions supported by this module - _entry->module_startup_func = extension_startup; // startup function for the whole extension - _entry->module_shutdown_func = extension_shutdown; // shutdown function for the whole extension - _entry->request_startup_func = request_startup; // startup function per request - _entry->request_shutdown_func = request_shutdown; // shutdown function per request - _entry->info_func = NULL; // information for retrieving info - _entry->version = _version; // version string - _entry->globals_size = 0; // size of the global variables - _entry->globals_ptr = NULL; // pointer to the globals - _entry->globals_ctor = NULL; // constructor for global variables - _entry->globals_dtor = NULL; // destructor for global variables - _entry->post_deactivate_func = NULL; // unknown function - _entry->module_started = 0; // module is not yet started - _entry->type = 0; // temporary or persistent module, will be filled by Zend engine - _entry->handle = NULL; // dlopen() handle, will be filled by Zend engine - _entry->module_number = 0; // module number will be filled in by Zend engine - _entry->build_id = ZEND_MODULE_BUILD_ID; // check if extension and zend engine are compatible - - // done - return _entry; + // assign all members (apart from the globals) + _entry.size = sizeof(zend_module_entry); // size of the data + _entry.zend_api = ZEND_MODULE_API_NO; // api number + _entry.zend_debug = ZEND_DEBUG; // debug mode enabled? + _entry.zts = USING_ZTS; // is thread safety enabled? + _entry.ini_entry = NULL; // the php.ini record + _entry.deps = NULL; // dependencies on other modules + _entry.name = HiddenPointer<Extension>(this, name); // extension name, with a hidden pointer to the extension object + _entry.functions = functions.internal(); // functions supported by this module + _entry.module_startup_func = extension_startup; // startup function for the whole extension + _entry.module_shutdown_func = extension_shutdown; // shutdown function for the whole extension + _entry.request_startup_func = request_startup; // startup function per request + _entry.request_shutdown_func = request_shutdown; // shutdown function per request + _entry.info_func = NULL; // information for retrieving info + _entry.version = version; // version string + _entry.globals_size = 0; // size of the global variables + _entry.globals_ptr = NULL; // pointer to the globals + _entry.globals_ctor = NULL; // constructor for global variables + _entry.globals_dtor = NULL; // destructor for global variables + _entry.post_deactivate_func = NULL; // unknown function + _entry.module_started = 0; // module is not yet started + _entry.type = 0; // temporary or persistent module, will be filled by Zend engine + _entry.handle = NULL; // dlopen() handle, will be filled by Zend engine + _entry.module_number = 0; // module number will be filled in by Zend engine + _entry.build_id = ZEND_MODULE_BUILD_ID; // check if extension and zend engine are compatible + + // things that only need to be initialized +#ifdef ZTS + _entry.globals_id_ptr = NULL; +#else + _entry.globals_ptr = NULL; +#endif } /** diff --git a/src/functions.cpp b/src/functions.cpp new file mode 100644 index 0000000..f9e0ffb --- /dev/null +++ b/src/functions.cpp @@ -0,0 +1,54 @@ +/** + * Functions.cpp + * + * Implementation for the functions class + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace PhpCpp { + +/** + * Constructor + * @param functions The functions to parse + */ +Functions::Functions(const std::initializer_list<Function> &functions) : _functions(functions) +{ + // allocate the function entries + _entries = new zend_function_entry[functions.size() + 1]; + + // keep iterator counter + int i = 0; + + // loop through the functions + for (auto it = begin(functions); it != functions.end(); it++) + { + // let the callable fill the array + it->internal()->fill(&_entries[i++]); + } + + // last entry should be set to all zeros + zend_function_entry *last = &_entries[i]; + + // all should be set to zero + memset(last, 0, sizeof(zend_function_entry)); +} + +/** + * Destructor + */ +Functions::~Functions() +{ + delete[] _entries; +} + +/** + * End of namespace + */ +} + diff --git a/src/hiddenpointer.h b/src/hiddenpointer.h new file mode 100644 index 0000000..616dfee --- /dev/null +++ b/src/hiddenpointer.h @@ -0,0 +1,159 @@ +/** + * HiddenPointer.h + * + * Helper class that we use to hide a pointer in a string. We do this + * by creating a string buffer that is a littlebit bigger, and put + * the hidden pointer in front of the name + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013 Copernica BV + */ + +/** + * Set up namespace + */ +namespace PhpCpp { + +/** + * Class definition + */ +template <typename Type> +class HiddenPointer +{ +public: + /** + * Constructor to hide the pointer in a buffer + * @param pointer The hidden pointer + * @param text The visible text + * @param size Optional text size + */ + HiddenPointer(Type *pointer, const char *text, int size=-1) + { + // calculate size + if (size < 0) size = strlen(text); + + // reserve enough room for the text and the pointer + _data.reserve(size + sizeof(Type *)); + + // store the pointer + _data.assign(std::string((const char *)&pointer, sizeof(Type *))); + + // append the text + _data.append(text, size); + + // store pointers + _pointer = pointer; + _text = _data.c_str() + sizeof(Type *); + } + + /** + * Constructor to retrieve the object given a buffer + * @param text The visible text + * @param size Size of the text + */ + HiddenPointer(const char *text, int size=-1) + { + // calculate size + if (size < 0) size = strlen(text); + + // the pointer is stored right in front of the name + _pointer = *((Type **)(text - sizeof(Type *))); + _text = text; + } + + /** + * Copy constructor + * @param that + */ + HiddenPointer(const HiddenPointer<Type> &that) : _pointer(that._pointer), _text(that._text), _data(that._data) + { + // if data is filled, the text is located inside the data + if (_data.size() > 0) _text = _data.c_str() + sizeof(Type *); + } + + /** + * Destructor + */ + virtual ~HiddenPointer() {} + + /** + * Assignment operator + * @param that + * @return HiddenPointer + */ + HiddenPointer<Type> operator=(const HiddenPointer &that) + { + // skip self assignment + if (&that == this) return *this; + + // copy members + _pointer = that._pointer; + _text = that._text; + _data = that._data; + + // if data is filled, the text is located inside the data + if (_data.size() > 0) _text = _data.c_str() + sizeof(Type *); + } + + /** + * Retrieve the pointer + * @return Type* + */ + Type *pointer() + { + return _pointer; + } + + /** + * Retrieve the text + * @return const char * + */ + const char *text() + { + return _text; + } + + /** + * Cast to the pointer + * @return Type* + */ + operator Type* () + { + return _pointer; + } + + /** + * Cast to text + * @return const char * + */ + operator const char * () + { + return _text; + } + +private: + /** + * The actual pointer + * @var Type* + */ + Type *_pointer; + + /** + * The original text + * @var text + */ + const char *_text; + + /** + * Optional data buffer + * @var string + */ + std::string _data; + +}; + +/** + * End of namespace + */ +} + diff --git a/src/includes.h b/src/includes.h index 58f14c2..28d8b51 100644 --- a/src/includes.h +++ b/src/includes.h @@ -38,6 +38,7 @@ #include "../include/member.h" #include "../include/arguments.h" #include "../include/function.h" +#include "../include/functions.h" #include "../include/extension.h" /** @@ -45,5 +46,4 @@ */ #include "callable.h" #include "arginfo.h" -#include "functions.h" - +#include "hiddenpointer.h" |