summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--include/extension.h98
-rw-r--r--include/function.h4
-rw-r--r--include/functions.h (renamed from src/functions.h)41
-rw-r--r--include/request.h6
-rw-r--r--src/extension.cpp176
-rw-r--r--src/functions.cpp54
-rw-r--r--src/hiddenpointer.h159
-rw-r--r--src/includes.h4
9 files changed, 379 insertions, 165 deletions
diff --git a/Makefile b/Makefile
index 496e051..8209a7e 100644
--- a/Makefile
+++ b/Makefile
@@ -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"