summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--include/class.h5
-rw-r--r--include/extension.h129
-rw-r--r--include/namespace.h155
-rw-r--r--src/callable.h12
-rw-r--r--src/extension.cpp275
-rw-r--r--src/extensionimpl.cpp300
-rw-r--r--src/extensionimpl.h213
-rw-r--r--src/function.h2
-rw-r--r--src/includes.h1
-rw-r--r--src/namespace.cpp79
11 files changed, 694 insertions, 481 deletions
diff --git a/Makefile b/Makefile
index 60e82e8..6bac3de 100644
--- a/Makefile
+++ b/Makefile
@@ -184,8 +184,8 @@ install:
${MKDIR} ${INSTALL_HEADERS}/phpcpp
${CP} phpcpp.h ${INSTALL_HEADERS}
${CP} include/*.h ${INSTALL_HEADERS}/phpcpp
- ${CP} ${PHP_LIBRARY} ${INSTALL_LIB}
- ${CP} ${HHVM_LIBRARY} ${INSTALL_LIB}
+ if [ -e ${PHP_LIBRARY} ]; then ${CP} ${PHP_LIBRARY} ${INSTALL_LIB}; fi
+ if [ -e ${HHVM_LIBRARY} ]; then ${CP} ${HHVM_LIBRARY} ${INSTALL_LIB}; fi
${CONFIG_UTILITY} > ${INSTALL_HEADERS}/phpcpp/config.h
test:
diff --git a/include/class.h b/include/class.h
index 7b9a928..e0316df 100644
--- a/include/class.h
+++ b/include/class.h
@@ -16,11 +16,6 @@
*/
/**
- * Zend/SPL interfaces that we support
- */
-extern struct _zend_class_entry *zend_ce_arrayaccess;
-
-/**
* Set up namespace
*/
namespace Php {
diff --git a/include/extension.h b/include/extension.h
index f9361b3..84789d3 100644
--- a/include/extension.h
+++ b/include/extension.h
@@ -6,7 +6,7 @@
* apache process starts - and will be used for all subsequent requests that
* are handled by Apache.
*
- * For some environments (for example CLI scripts and FastCGI calls) only one
+ * For some environments (for example CLI scripts and CGI calls) only one
* request is handled by an extension instance, but for others (when PHP runs
* as module in a webserver) many requests are handled by the same extension
* instance.
@@ -16,11 +16,6 @@
*/
/**
- * Structures referenced in this class
- */
-struct _zend_module_entry;
-
-/**
* Set up namespace
*/
namespace Php {
@@ -28,7 +23,7 @@ namespace Php {
/**
* Forward declaration
*/
-class Extension;
+class ExtensionImpl;
/**
* Signature of a callback
@@ -46,7 +41,7 @@ public:
* @param name Extension name
* @param version Extension version string
*/
- Extension(const char *name = NULL, const char *version = NULL);
+ Extension(const char *name, const char *version = "1.0");
/**
* No copy'ing and no moving
@@ -67,13 +62,10 @@ public:
* is handled. You can register this callback if you want to be notified
* when the engine is ready, for example to initialize certain things.
*
- * @param callback
+ * @param callback Function to be called
+ * @return Extension Same object to allow chaining
*/
- void onStartup(const Callback &callback)
- {
- // copy callback
- _onStartup = callback;
- }
+ Extension &onStartup(const Callback &callback);
/**
* Register a function to be called when the PHP engine is going to stop
@@ -81,13 +73,10 @@ public:
* The callback will be called right before the process is going to stop.
* You can register a function if you want to clean up certain things.
*
- * @param callback
+ * @param callback Function to be called
+ * @return Extension Same object to allow chaining
*/
- void onShutdown(const Callback &callback)
- {
- // copy callback
- _onShutdown = callback;
- }
+ Extension &onShutdown(const Callback &callback);
/**
* Register a callback that is called at the beginning of each pageview/request
@@ -97,13 +86,10 @@ public:
* multiple requests after each other, and you may want to set back certain
* global variables to their initial variables in front of each request
*
- * @param callback
+ * @param callback Function to be called
+ * @return Extension Same object to allow chaining
*/
- void onRequest(const Callback &callback)
- {
- // copy callback
- _onRequest = callback;
- }
+ Extension &onRequest(const Callback &callback);
/**
* Register a callback that is called to cleanup things after a pageview/request
@@ -113,105 +99,38 @@ public:
* This method is called onIdle because the extension is idle in between
* requests.
*
- * @param callback
+ * @param callback Function to be called
+ * @return Extension Same object to allow chaining
*/
- void onIdle(const Callback &callback)
- {
- // copy callback
- _onIdle = callback;
- }
+ Extension &onIdle(const Callback &callback);
/**
- * Retrieve the module entry
+ * Retrieve the module pointer
*
* This is the memory address that should be exported by the get_module()
* function.
*
- * @return _zend_module_entry
+ * @return void*
*/
- _zend_module_entry *module();
+ void *module();
/**
* Cast to a module entry
- * @return _zend_module_entry*
+ *
+ * @return void*
*/
- operator _zend_module_entry * ()
+ operator void * ()
{
return module();
}
private:
/**
- * The information that is passed to the Zend engine
- *
- * Although it would be slightly faster to not make this a pointer, this
- * would require that client code also includes the PHP header files, which
- * we try to prevent with the PHP-CPP library, so we allocate it dynamically.
+ * The implementation object
*
- * @var zend_module_entry
- */
- _zend_module_entry *_entry;
-
- /**
- * Callback that is called after the engine is initialized and before the
- * pageviews are going to be handled
- * @var Callback
- */
- Callback _onStartup;
-
- /**
- * Callback that is called in front of each request
- * @var Callback
- */
- Callback _onRequest;
-
- /**
- * Callback that is called right after each request
- * @var Callback
- */
- Callback _onIdle;
-
- /**
- * Callback that is called right before the engine is closing down
- * @var Callback
- */
- Callback _onShutdown;
-
- /**
- * Function that is called when the extension initializes
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int 0 on success
- */
- static int onStartup(int type, int module_number TSRMLS_DC);
-
- /**
- * Function that is called when the extension is about to be stopped
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int
- */
- static int onShutdown(int type, int module_number TSRMLS_DC);
-
- /**
- * Function that is called when a request starts
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int 0 on success
- */
- static int onRequest(int type, int module_number TSRMLS_DC);
-
- /**
- * Function that is called when a request is ended
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int 0 on success
+ * @var ExtensionImpl
*/
- static int onIdle(int type, int module_number TSRMLS_DC);
+ ExtensionImpl *_impl;
};
/**
diff --git a/include/namespace.h b/include/namespace.h
index de73924..cd871d4 100644
--- a/include/namespace.h
+++ b/include/namespace.h
@@ -23,32 +23,37 @@ class Function;
*/
class Namespace
{
-public:
+protected:
/**
- * Constructor
- * @param name Name of the namespace
+ * Name of the namespace
+ * @var string
*/
- Namespace(const char *name) : _name(name) {}
+ std::string _name;
+
+ /**
+ * Functions defined in the namespace
+ * @var list
+ */
+ std::list<std::shared_ptr<Function>> _functions;
+
+ /**
+ * Classes defined in the namespace
+ * @var list
+ */
+ std::list<std::shared_ptr<ClassBase>> _classes;
/**
- * Copy constructor
- * @param ns Namespace to copy
+ * Namespaces defined inside the namespace
+ * @var list
*/
- Namespace(const Namespace &ns) :
- _name(ns._name),
- _functions(ns._functions),
- _classes(ns._classes),
- _namespaces(ns._namespaces) {}
-
+ std::list<std::shared_ptr<Namespace>> _namespaces;
+
+public:
/**
- * Move constructor
- * @param ns
+ * Constructor
+ * @param name Name of the namespace
*/
- Namespace(Namespace &&ns) :
- _name(std::move(ns._name)),
- _functions(std::move(ns._functions)),
- _classes(std::move(ns._classes)),
- _namespaces(std::move(ns._namespaces)) {}
+ Namespace(const char *name) : _name(name) {}
/**
* Destructor
@@ -56,7 +61,7 @@ public:
virtual ~Namespace() {}
/**
- * Add a native function directly to the extension
+ * Add a native function directly to the namespace
* @param name Name of the function
* @param function The function to add
* @param arguments Optional argument specification
@@ -68,131 +73,91 @@ public:
Namespace &add(const char *name, const native_callback_3 &function, const Arguments &arguments = {});
/**
- * Add a native class to the extension by moving it
+ * Add a native class to the namespace by moving it
* @param type The class implementation
* @return Namespace Same object to allow chaining
*/
template<typename T>
Namespace &add(Class<T> &&type)
{
- // make a copy of the object
- auto *copy = new Class<T>(std::move(type));
-
- // and add it to the list of classes
- _classes.push_back(std::unique_ptr<ClassBase>(copy));
+ // make a copy of the object, and add it to the list of classes
+ _classes.push_back(std::unique_ptr<ClassBase>(new Class<T>(std::move(type))));
// allow chaining
return *this;
}
/**
- * Add a native class to the extension by copying it
+ * Add a native class to the namespace by copying it
* @param type The class implementation
- * @param Namespace Same object to allow chaining
+ * @return Namespace Same object to allow chaining
*/
template<typename T>
Namespace &add(const Class<T> &type)
{
- // make a copy of the object
- auto *copy = new Class<T>(std::move(type));
-
// and add it to the list of classes
- _classes.push_back(std::unique_ptr<ClassBase>(copy));
+ _classes.push_back(std::unique_ptr<ClassBase>(new Class<T>(type)));
// allow chaining
return *this;
}
/**
- * Add an interface to the extension by moving it
+ * Add an interface to the namespace by moving it
* @param interface The interface properties
+ * @return Namespace Same object to allow chaining
*/
Namespace &add(Interface &&interface)
{
- // make a copy of the object
- auto *copy = new Interface(std::move(interface));
-
- // and add it to the list of classes
- _classes.push_back(std::unique_ptr<ClassBase>(copy));
+ // make a copy and add it to the list of classes
+ _classes.push_back(std::unique_ptr<ClassBase>(new Interface(std::move(interface))));
// allow chaining
return *this;
}
/**
- * Add an interface to the extension by copying it
+ * Add an interface to the namespace by copying it
* @param interface The interface properties
+ * @return Namespace Same object to allow chaining
*/
Namespace &add(const Interface &interface)
{
- // make a copy of the object
- auto *copy = new Interface(interface);
-
- // and add it to the list of classes
- _classes.push_back(std::unique_ptr<ClassBase>(copy));
+ // make a copy and add it to the list of classes
+ _classes.push_back(std::unique_ptr<ClassBase>(new Interface(interface)));
// allow chaining
return *this;
}
/**
- * Add a namespace to the extension by moving it
+ * Add a namespace to the namespace by moving it
* @param ns The namespace
+ * @return Namespace Same object to allow chaining
*/
Namespace &add(Namespace &&ns)
{
- // make a copy of the object
- auto *copy = new Namespace(std::move(ns));
-
// add it to the list of namespaces
- _namespaces.push_back(std::unique_ptr<Namespace>(copy));
+ _namespaces.push_back(std::unique_ptr<Namespace>(new Namespace(std::move(ns))));
// allow chaining
return *this;
}
/**
- * Add a namespace to the extension by copying it
+ * Add a namespace to the namespace by copying it
* @param ns The namespace
+ * @return Namespace Same object to allow chaining
*/
Namespace &add(const Namespace &ns)
{
- // make a copy of the object
- auto *copy = new Namespace(std::move(ns));
-
- // add it to the list of namespaces
- _namespaces.push_back(std::unique_ptr<Namespace>(copy));
+ // make a copy and add it to the list of namespaces
+ _namespaces.push_back(std::unique_ptr<Namespace>(new Namespace(ns)));
// allow chaining
return *this;
}
-
-protected:
- /**
- * Name of the namespace
- * @var string
- */
- std::string _name;
-
- /**
- * Functions defined by the extension
- * @var list
- */
- std::list<std::shared_ptr<Function>> _functions;
-
- /**
- * Classes defined by the extension
- * @var list
- */
- std::list<std::shared_ptr<ClassBase>> _classes;
-
- /**
- * Namespaces defined by the extension
- * @var list
- */
- std::list<std::shared_ptr<Namespace>> _namespaces;
-
/**
* The total number of functions
* @return size_t
@@ -208,21 +173,27 @@ protected:
// done
return result;
}
-
+
/**
- * Initialize all functions in this namespace
- * @param ns Namespace prefix
- * @param entries The array to be filled
- * @return int Number of functions that were initialized
+ * Apply a callback to each registered function
+ *
+ * The callback will be called with the name of the namespace, and
+ * a reference to the registered function.
+ *
+ * @param callback
*/
- size_t initialize(const std::string &ns, struct _zend_function_entry entries[]);
-
+ void apply(const std::function<void(const std::string &ns, Function &func)> &callback);
+
/**
- * Initialize the namespace after it was registered
- * @param parent Parent namespace
- * @param tsrm_ls
+ * Apply a callback to each registered class
+ *
+ * The callback will be called with the name of the namespace, and
+ * a reference to the registered class.
+ *
+ * @param callback
*/
- void initialize(const std::string &parent TSRMLS_DC);
+ void apply(const std::function<void(const std::string &ns, ClassBase &clss)> &callback);
+
};
/**
diff --git a/src/callable.h b/src/callable.h
index aef5649..65ce719 100644
--- a/src/callable.h
+++ b/src/callable.h
@@ -9,12 +9,6 @@
*/
/**
- * Forward definitions
- */
-struct _zend_function_entry;
-struct _zend_arg_info;
-
-/**
* Set up namespace
*/
namespace Php {
@@ -99,7 +93,7 @@ public:
* @param classname Optional class name
* @param flags Access flags
*/
- void initialize(struct _zend_function_entry *entry, const char *classname = nullptr, int flags = 0) const;
+ void initialize(zend_function_entry *entry, const char *classname = nullptr, int flags = 0) const;
/**
* Fill function info
@@ -107,7 +101,7 @@ public:
* @param ns Active namespace
* @param classname Optional class name
*/
- void initialize(struct _zend_arg_info *info, const char *classname = nullptr) const;
+ void initialize(zend_arg_info *info, const char *classname = nullptr) const;
protected:
@@ -139,7 +133,7 @@ protected:
* The arguments
* @var zend_arg_info[]
*/
- struct _zend_arg_info *_argv = nullptr;
+ zend_arg_info *_argv = nullptr;
/**
* Private helper method to fill an argument object
diff --git a/src/extension.cpp b/src/extension.cpp
index 949efa9..9685b32 100644
--- a/src/extension.cpp
+++ b/src/extension.cpp
@@ -2,7 +2,7 @@
* Extension.cpp
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
- * @copyright 2013 Copernica BV
+ * @copyright 2013, 2014 Copernica BV
*/
#include "includes.h"
@@ -12,265 +12,88 @@
namespace Php {
/**
- * If this extension is compiled for a PHP version with multi
- * threading support, we need an additional header file
+ * Constructor that defines a number of functions right away
+ * @param name Extension name
+ * @param version Extension version string
*/
-#ifdef ZTS
-#include "TSRM.h"
-#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.
- */
-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 init_globals(zend_phpcpp_globals *globals) {}
-
-/**
- * The *startup() and *shutdown() callback functions are passed a module_number
- * variable. However, there does not seem to be a decent API call in Zend to
- * get back the original module_entry linked to this number. So we have to
- * look up entries in a hash table to find the right module entry. To make things
- * even worse, the records in this hash table are copies of the original
- * zend_module_entry structure, so we can also not hide the C++ extension
- * object pointer in the entry that we created ourselves.
- *
- * We have an ugly solution, we keep track of a map of all C++ extension names
- * and their associated extension object, and a map of all module number and
- * the linked extension object.
- *
- * @var map
- */
-static std::map<std::string,Extension*> name2extension;
-static std::map<int,Extension*> number2extension;
-
-/**
- * Handler function that is used in combination with zend_hash_apply()
- *
- * This function is called when we need to find an extension object based on
- * an extension number. We loop through the list of all registered modules, and
- * for each module we check if we know the extension based on the name
- *
- * @param _zend_module_entry
- */
-static int match_module(_zend_module_entry *entry)
-{
- // check if there is an extension with this name
- auto iter = name2extension.find(entry->name);
- if (iter == name2extension.end()) return ZEND_HASH_APPLY_KEEP;
-
- // we have the extension, store in combination with the number
- number2extension[entry->module_number] = iter->second;
-
- // done
- return ZEND_HASH_APPLY_KEEP;
-}
-
-/**
- * Find an extension based on the module number
- * @param number
- * @param tsrm_ls
- * @return Extension*
- */
-static Extension *find(int number TSRMLS_DC)
-{
- // do we already have an extension with this number?
- auto iter = number2extension.find(number);
- if (iter != number2extension.end()) return iter->second;
-
- // no, not yet, loop through all modules
- zend_hash_apply(&module_registry, (apply_func_t)match_module TSRMLS_CC);
-
- // find again
- iter = number2extension.find(number);
- if (iter == number2extension.end()) return nullptr;
-
- // found!
- return iter->second;
-}
-
-/**
- * Function that is called when the extension initializes
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int 0 on success
- */
-int Extension::onStartup(int type, int module_number TSRMLS_DC)
-{
- // initialize and allocate the "global" variables
- ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL);
+Extension::Extension(const char *name, const char *version) :
+ Namespace(""), _impl(new ExtensionImpl(this, name, version)) {}
- // get the extension
- Extension *extension = find(module_number TSRMLS_CC);
-
- // initialize namespace
- extension->initialize("" TSRMLS_CC);
-
- // is the callback registered?
- if (extension->_onStartup) extension->_onStartup();
-
- // done
- return BOOL2SUCCESS(true);
-}
-
/**
- * Function that is called when the extension is about to be stopped
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int
+ * Destructor
*/
-int Extension::onShutdown(int type, int module_number TSRMLS_DC)
+Extension::~Extension()
{
- // get the extension
- Extension *extension = find(module_number TSRMLS_CC);
-
- // is the callback registered?
- if (extension->_onShutdown) extension->_onShutdown();
-
- // done
- return BOOL2SUCCESS(true);
+ // get rid of the implementation object
+ delete _impl;
}
/**
- * Function that is called when a request starts
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int 0 on success
+ * Register a function to be called when the PHP engine is ready
+ * @param callback
+ * @return Extension
*/
-int Extension::onRequest(int type, int module_number TSRMLS_DC)
+Extension &Extension::onStartup(const Callback &callback)
{
- // get the extension
- Extension *extension = find(module_number TSRMLS_CC);
-
- // is the callback registered?
- if (extension->_onRequest) extension->_onRequest();
+ // pass on to the implementation
+ _impl->onStartup(callback);
- // done
- return BOOL2SUCCESS(true);
+ // allow chaining
+ return *this;
}
/**
- * Function that is called when a request is ended
- * @param type Module type
- * @param number Module number
- * @param tsrm_ls
- * @return int 0 on success
+ * Register a function to be called when the PHP engine is going to stop
+ * @param callback
+ * @return Extension
*/
-int Extension::onIdle(int type, int module_number TSRMLS_DC)
+Extension &Extension::onShutdown(const Callback &callback)
{
- // get the extension
- Extension *extension = find(module_number TSRMLS_CC);
+ // pass on to the implementation
+ _impl->onShutdown(callback);
- // is the callback registered?
- if (extension->_onIdle) extension->_onIdle();
-
- // done
- return BOOL2SUCCESS(true);
+ // allow chaining
+ return *this;
}
/**
- * Constructor
- * @param name Name of the extension
- * @param version Version number
- * @param start Request start callback
- * @param stop Request stop callback
+ * Register a callback that is called at the beginning of each pageview/request
+ * @param callback
*/
-Extension::Extension(const char *name, const char *version) : Namespace("")
+Extension &Extension::onRequest(const Callback &callback)
{
- // keep extension pointer based on the name
- name2extension[name] = this;
-
- // allocate memory (we allocate this on the heap so that the size of the
- // entry does not have to be defined in the .h file. We pay a performance
- // price for this, but we pay this price becuase the design goal of the
- // PHP-C++ library is to have an interface that is as simple as possible
- _entry = new _zend_module_entry;
+ // pass on to the implementation
+ _impl->onRequest(callback);
- // 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 = name; // extension name
- _entry->functions = NULL; // functions supported by this module (none for now)
- _entry->module_startup_func = &Extension::onStartup; // startup function for the whole extension
- _entry->module_shutdown_func = &Extension::onShutdown; // shutdown function for the whole extension
- _entry->request_startup_func = &Extension::onRequest; // startup function per request
- _entry->request_shutdown_func = &Extension::onIdle; // 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_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 = (char *)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
-
+ // allow chaining
+ return *this;
}
/**
- * Destructor
+ * Register a callback that is called to cleanup things after a pageview/request
+ * @param callback
*/
-Extension::~Extension()
+Extension &Extension::onIdle(const Callback &callback)
{
- // deallocate functions
- if (_entry->functions) delete[] _entry->functions;
+ // pass on to the implementation
+ _impl->onIdle(callback);
- // deallocate entry
- delete _entry;
+ // allow chaining
+ return *this;
}
/**
- * Retrieve the module entry
- * @return zend_module_entry
+ * Retrieve the module pointer
+ *
+ * This is the memory address that should be exported by the get_module()
+ * function.
+ *
+ * @return void*
*/
-zend_module_entry *Extension::module()
+void *Extension::module()
{
- // check if functions we're already defined
- if (_entry->functions || _functions.size() == 0) return _entry;
-
- // allocate memory for the functions
- zend_function_entry *entries = new zend_function_entry[functions() + 1];
-
- // initialize the entries
- int count = Namespace::initialize("", entries);
-
- // last entry should be set to all zeros
- zend_function_entry *last = &entries[count];
-
- // all should be set to zero
- memset(last, 0, sizeof(zend_function_entry));
-
- // store functions in entry object
- _entry->functions = entries;
-
- // return the entry
- return _entry;
+ // pass on to the implementation
+ return _impl->module();
}
/**
diff --git a/src/extensionimpl.cpp b/src/extensionimpl.cpp
new file mode 100644
index 0000000..0197757
--- /dev/null
+++ b/src/extensionimpl.cpp
@@ -0,0 +1,300 @@
+/**
+ * Extension.cpp
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2013 Copernica BV
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * If this extension is compiled for a PHP version with multi
+ * threading support, we need an additional header file
+ */
+#ifdef ZTS
+#include "TSRM.h"
+#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.
+ */
+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 init_globals(zend_phpcpp_globals *globals) {}
+
+/**
+ * The *startup() and *shutdown() callback functions are passed a module_number
+ * variable. However, there does not seem to be a decent API call in Zend to
+ * get back the original module_entry linked to this number. So we have to
+ * look up entries in a hash table to find the right module entry. To make things
+ * even worse, the records in this hash table are copies of the original
+ * zend_module_entry structure, so we can also not hide the C++ extension
+ * object pointer in the entry that we created ourselves.
+ *
+ * We have an ugly solution, we keep track of a map of all C++ extension names
+ * and their associated extension object, and a map of all module number and
+ * the linked extension object.
+ *
+ * @var map
+ */
+static std::map<std::string,ExtensionImpl*> name2extension;
+static std::map<int,ExtensionImpl*> number2extension;
+
+/**
+ * Handler function that is used in combination with zend_hash_apply()
+ *
+ * This function is called when we need to find an extension object based on
+ * an extension number. We loop through the list of all registered modules, and
+ * for each module we check if we know the extension based on the name
+ *
+ * @param zend_module_entry
+ */
+static int match_module(zend_module_entry *entry)
+{
+ // check if there is an extension with this name
+ auto iter = name2extension.find(entry->name);
+ if (iter == name2extension.end()) return ZEND_HASH_APPLY_KEEP;
+
+ // we have the extension, store in combination with the number
+ number2extension[entry->module_number] = iter->second;
+
+ // done
+ return ZEND_HASH_APPLY_KEEP;
+}
+
+/**
+ * Find an extension based on the module number
+ * @param number
+ * @param tsrm_ls
+ * @return Extension*
+ */
+static ExtensionImpl *find(int number TSRMLS_DC)
+{
+ // do we already have an extension with this number?
+ auto iter = number2extension.find(number);
+ if (iter != number2extension.end()) return iter->second;
+
+ // no, not yet, loop through all modules
+ zend_hash_apply(&module_registry, (apply_func_t)match_module TSRMLS_CC);
+
+ // find again
+ iter = number2extension.find(number);
+ if (iter == number2extension.end()) return nullptr;
+
+ // found!
+ return iter->second;
+}
+
+/**
+ * Function that is called when the extension initializes
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int 0 on success
+ */
+int ExtensionImpl::onStartup(int type, int module_number TSRMLS_DC)
+{
+ // initialize and allocate the "global" variables
+ ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL);
+
+ // get the extension
+ auto *extension = find(module_number TSRMLS_CC);
+
+ // initialize the extension
+ extension->initialize(TSRMLS_CC);
+
+ // is the callback registered?
+ if (extension->_onStartup) extension->_onStartup();
+
+ // done
+ return BOOL2SUCCESS(true);
+}
+
+/**
+ * Function that is called when the extension is about to be stopped
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int
+ */
+int ExtensionImpl::onShutdown(int type, int module_number TSRMLS_DC)
+{
+ // get the extension
+ auto *extension = find(module_number TSRMLS_CC);
+
+ // is the callback registered?
+ if (extension->_onShutdown) extension->_onShutdown();
+
+ // done
+ return BOOL2SUCCESS(true);
+}
+
+/**
+ * Function that is called when a request starts
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int 0 on success
+ */
+int ExtensionImpl::onRequest(int type, int module_number TSRMLS_DC)
+{
+ // get the extension
+ auto *extension = find(module_number TSRMLS_CC);
+
+ // is the callback registered?
+ if (extension->_onRequest) extension->_onRequest();
+
+ // done
+ return BOOL2SUCCESS(true);
+}
+
+/**
+ * Function that is called when a request is ended
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int 0 on success
+ */
+int ExtensionImpl::onIdle(int type, int module_number TSRMLS_DC)
+{
+ // get the extension
+ auto *extension = find(module_number TSRMLS_CC);
+
+ // is the callback registered?
+ if (extension->_onIdle) extension->_onIdle();
+
+ // done
+ return BOOL2SUCCESS(true);
+}
+
+/**
+ * Constructor
+ * @param data Pointer to the extension object created by the extension programmer
+ * @param name Name of the extension
+ * @param version Version number
+ */
+ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version) : _data(data)
+{
+ // keep extension pointer based on the name
+ name2extension[name] = this;
+
+ // 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 = name; // extension name
+ _entry.functions = NULL; // functions supported by this module (none for now)
+ _entry.module_startup_func = &ExtensionImpl::onStartup; // startup function for the whole extension
+ _entry.module_shutdown_func = &ExtensionImpl::onShutdown; // shutdown function for the whole extension
+ _entry.request_startup_func = &ExtensionImpl::onRequest; // startup function per request
+ _entry.request_shutdown_func = &ExtensionImpl::onIdle; // 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_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 = (char *)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
+
+}
+
+/**
+ * Destructor
+ */
+ExtensionImpl::~ExtensionImpl()
+{
+ // deallocate functions
+ if (_entry.functions) delete[] _entry.functions;
+}
+
+/**
+ * Retrieve the module entry
+ * @return zend_module_entry
+ */
+zend_module_entry *ExtensionImpl::module()
+{
+ // check if functions we're already defined
+ if (_entry.functions) return &_entry;
+
+ // the number of functions
+ int count = _data->functions();
+
+ // skip if there are no functions
+ if (count == 0) return &_entry;
+
+ // allocate memory for the functions
+ zend_function_entry *entries = new zend_function_entry[count + 1];
+
+ // index being processed
+ int i = 0;
+
+ // apply a function to each function
+ _data->apply([&i, entries](const std::string &prefix, Function &function) {
+
+ // initialize the function
+ function.initialize(prefix, &entries[i]);
+
+ // move on to the next iteration
+ i++;
+ });
+
+ // last entry should be set to all zeros
+ zend_function_entry *last = &entries[count];
+
+ // all should be set to zero
+ memset(last, 0, sizeof(zend_function_entry));
+
+ // store functions in entry object
+ _entry.functions = entries;
+
+ // return the entry
+ return &_entry;
+}
+
+/**
+ * Initialize the extension after it was started
+ * @param tsrm_ls
+ */
+void ExtensionImpl::initialize(TSRMLS_D)
+{
+ // we need to register each class, find out all classes
+ _data->apply([TSRMLS_C](const std::string &prefix, ClassBase &c) {
+
+ // forward to implementation class
+ c.implementation()->initialize(&c, prefix TSRMLS_C);
+ });
+}
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/src/extensionimpl.h b/src/extensionimpl.h
new file mode 100644
index 0000000..8c9ae58
--- /dev/null
+++ b/src/extensionimpl.h
@@ -0,0 +1,213 @@
+/**
+ * ExtensionImpl.h
+ *
+ * Extension implementation for the Zend engine.
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2013, 2014 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class ExtensionImpl
+{
+protected:
+ /**
+ * Pointer to the extension object that is filled by the extension programmer
+ * @var Extension
+ */
+ Extension *_data;
+
+ /**
+ * The information that is passed to the Zend engine
+ *
+ * Although it would be slightly faster to not make this a pointer, this
+ * would require that client code also includes the PHP header files, which
+ * we try to prevent with the PHP-CPP library, so we allocate it dynamically.
+ *
+ * @var zend_module_entry
+ */
+ zend_module_entry _entry;
+
+ /**
+ * Callback that is called after the engine is initialized and before the
+ * pageviews are going to be handled
+ * @var Callback
+ */
+ Callback _onStartup;
+
+ /**
+ * Callback that is called in front of each request
+ * @var Callback
+ */
+ Callback _onRequest;
+
+ /**
+ * Callback that is called right after each request
+ * @var Callback
+ */
+ Callback _onIdle;
+
+ /**
+ * Callback that is called right before the engine is closing down
+ * @var Callback
+ */
+ Callback _onShutdown;
+
+public:
+ /**
+ * Constructor
+ * @param data Extension object created by the extension programmer
+ * @param name Name of the extension
+ * @param version Version number
+ */
+ ExtensionImpl(Extension *data, const char *name, const char *version);
+
+ /**
+ * No copy'ing and no moving
+ */
+ ExtensionImpl(const ExtensionImpl &extension) = delete;
+ ExtensionImpl(ExtensionImpl &&extension) = delete;
+
+ /**
+ * Destructor
+ */
+ virtual ~ExtensionImpl();
+
+ /**
+ * Register a function to be called when the PHP engine is ready
+ *
+ * The callback will be called after all extensions are loaded, and all
+ * functions and classes are available, but before the first pageview/request
+ * is handled. You can register this callback if you want to be notified
+ * when the engine is ready, for example to initialize certain things.
+ *
+ * @param callback
+ */
+ void onStartup(const Callback &callback)
+ {
+ // copy callback
+ _onStartup = callback;
+ }
+
+ /**
+ * Register a function to be called when the PHP engine is going to stop
+ *
+ * The callback will be called right before the process is going to stop.
+ * You can register a function if you want to clean up certain things.
+ *
+ * @param callback
+ */
+ void onShutdown(const Callback &callback)
+ {
+ // copy callback
+ _onShutdown = callback;
+ }
+
+ /**
+ * Register a callback that is called at the beginning of each pageview/request
+ *
+ * You can register a callback if you want to initialize certain things
+ * at the beginning of each request. Remember that the extension can handle
+ * multiple requests after each other, and you may want to set back certain
+ * global variables to their initial variables in front of each request
+ *
+ * @param callback
+ */
+ void onRequest(const Callback &callback)
+ {
+ // copy callback
+ _onRequest = callback;
+ }
+
+ /**
+ * Register a callback that is called to cleanup things after a pageview/request
+ *
+ * The callback will be called after _each_ request, so that you can clean up
+ * certain things and make your extension ready to handle the next request.
+ * This method is called onIdle because the extension is idle in between
+ * requests.
+ *
+ * @param callback
+ */
+ void onIdle(const Callback &callback)
+ {
+ // copy callback
+ _onIdle = callback;
+ }
+
+ /**
+ * Retrieve the module entry
+ *
+ * This is the memory address that should be exported by the get_module()
+ * function.
+ *
+ * @return _zend_module_entry
+ */
+ zend_module_entry *module();
+
+ /**
+ * Cast to a module entry
+ * @return _zend_module_entry*
+ */
+ operator zend_module_entry * ()
+ {
+ return module();
+ }
+
+private:
+ /**
+ * Initialize the namespace after it was registered
+ * @param tsrm_ls
+ */
+ void initialize(TSRMLS_DC);
+
+ /**
+ * Function that is called when the extension initializes
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int 0 on success
+ */
+ static int onStartup(int type, int module_number TSRMLS_DC);
+
+ /**
+ * Function that is called when the extension is about to be stopped
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int
+ */
+ static int onShutdown(int type, int module_number TSRMLS_DC);
+
+ /**
+ * Function that is called when a request starts
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int 0 on success
+ */
+ static int onRequest(int type, int module_number TSRMLS_DC);
+
+ /**
+ * Function that is called when a request is ended
+ * @param type Module type
+ * @param number Module number
+ * @param tsrm_ls
+ * @return int 0 on success
+ */
+ static int onIdle(int type, int module_number TSRMLS_DC);
+};
+
+/**
+ * End of namespace
+ */
+}
+
+
diff --git a/src/function.h b/src/function.h
index fd60944..4c34ac7 100644
--- a/src/function.h
+++ b/src/function.h
@@ -67,7 +67,7 @@ public:
* @param prefix Active namespace prefix
* @param entry Entry to be filled
*/
- void initialize(const std::string &prefix, struct _zend_function_entry *entry)
+ void initialize(const std::string &prefix, zend_function_entry *entry)
{
// if there is a namespace prefix, we should adjust the name
if (prefix.size()) _ptr = HiddenPointer<Callable>(this, prefix+"\\"+(const char *)_ptr);
diff --git a/src/includes.h b/src/includes.h
index f725ae1..48df910 100644
--- a/src/includes.h
+++ b/src/includes.h
@@ -100,6 +100,7 @@
#include "iteratorimpl.h"
#include "streambuf.h"
#include "classimpl.h"
+#include "extensionimpl.h"
#ifndef ZVAL_COPY_VALUE
#define ZVAL_COPY_VALUE(z, v) \
diff --git a/src/namespace.cpp b/src/namespace.cpp
index d462684..bea31a1 100644
--- a/src/namespace.cpp
+++ b/src/namespace.cpp
@@ -78,58 +78,55 @@ Namespace &Namespace::add(const char *name, const native_callback_3 &function, c
}
/**
- * Initialize all functions in this namespace
- * @param parent Namespace prefix of the parent
- * @param entries The array to be filled
- * @return int Number of functions that were initialized
+ * Apply a callback to each registered function
+ *
+ * The callback will be called with the name of the namespace, and
+ * a reference to the registered function.
+ *
+ * @param callback
*/
-size_t Namespace::initialize(const std::string &parent, struct _zend_function_entry entries[])
+void Namespace::apply(const std::function<void(const std::string &ns, Function &func)> &callback)
{
- // keep iterator counter
- int count = 0;
-
- // the namespace to use
- std::string prefix = parent.size() ? parent + "\\" + _name : _name;
-
- // loop through the functions
- for (auto &function : _functions)
- {
- // retrieve entry
- zend_function_entry *entry = &entries[count++];
-
- // let the function fill the entry
- function->initialize(prefix, entry);
- }
+ // loop through the functions, and apply the callback
+ for (auto &function : _functions) callback(_name, *function);
- // loop through the namespace
- for (auto &ns : _namespaces)
- {
- // let the namespace initialize
- count += ns->initialize(prefix, &entries[count]);
- }
-
- // done
- return count;
+ // loop through the other namespaces
+ for (auto &ns : _namespaces) ns->apply([this, callback](const std::string &ns, Function &func) {
+
+ // if this is the root namespace, we don't have to change the prefix
+ if (_name.size() == 0) return callback(ns, func);
+
+ // construct a new prefix
+ // @todo this could be slightly inefficient
+ return callback(_name + "\\" + ns, func);
+ });
}
/**
- * Initialize the namespace after it was registered
- * @param parent Parent namespace
- * @param tsrm_ls
+ * Apply a callback to each registered class
+ *
+ * The callback will be called with the name of the namespace, and
+ * a reference to the registered class.
+ *
+ * @param callback
*/
-void Namespace::initialize(const std::string &parent TSRMLS_DC)
+void Namespace::apply(const std::function<void(const std::string &ns, ClassBase &clss)> &callback)
{
- // the namespace to use
- std::string prefix = parent.size() ? parent + "\\" + _name : _name;
+ // loop through the classes, and apply the callback
+ for (auto &c : _classes) callback(_name, *c);
- // loop through the classes in this namespace
- for (auto &c : _classes) c->implementation()->initialize(c.get(), prefix TSRMLS_CC);
-
- // and loop through the other namespaces
- for (auto &n : _namespaces) n->initialize(prefix TSRMLS_CC);
+ // loop through the other namespaces
+ for (auto &ns : _namespaces) ns->apply([this, callback](const std::string &ns, ClassBase &clss) {
+
+ // if this is the root namespace, we don't have to change the prefix
+ if (_name.size() == 0) return callback(ns, clss);
+
+ // construct a new prefix
+ // @todo this could be slightly inefficient
+ return callback(_name + "\\" + ns, clss);
+ });
}
-
/**
* End namespace
*/