summaryrefslogtreecommitdiff
path: root/zend/extensionimpl.cpp
diff options
context:
space:
mode:
authorvalmat <ufabiz@gmail.com>2014-04-09 11:00:05 +0600
committervalmat <ufabiz@gmail.com>2014-04-09 11:00:05 +0600
commit6c7c846edd5b74450b76532da33c25e6cc6a10a4 (patch)
tree51b0e0be5c43ddba6ca9351026fc94bf8ae7bc07 /zend/extensionimpl.cpp
parent08ed8866a5bba0b23a8d5587116a968512df2568 (diff)
parent33760c3efba4207eac826ff080b5f9b9672fc60e (diff)
Merge branch 'master' into ini-master
Conflicts: include/namespace.h zend/extensionimpl.cpp
Diffstat (limited to 'zend/extensionimpl.cpp')
-rw-r--r--zend/extensionimpl.cpp321
1 files changed, 321 insertions, 0 deletions
diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp
new file mode 100644
index 0000000..3534cdb
--- /dev/null
+++ b/zend/extensionimpl.cpp
@@ -0,0 +1,321 @@
+/**
+ * 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::processStartup(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);
+
+ // array contains ini settings
+ static zend_ini_entry *ini_entries = new zend_ini_entry[ extension->_ini_entries.size()+1 ];
+
+ // Filling ini_entries
+ unsigned int Ind = 0;
+ for (auto &ini : extension->_ini_entries) ini->fill(&ini_entries[Ind++], module_number);
+
+ // add last empty ini entry (Zend, for some reason, it requires)
+ zend_ini_entry empty_entry { 0, 0, nullptr, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, nullptr, 0, 0, 0, nullptr };
+ ini_entries[Ind] = empty_entry;
+
+ // register
+ REGISTER_INI_ENTRIES();
+
+ // initialize the extension
+ extension->initialize(TSRMLS_C);
+
+ // 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::processShutdown(int type, int module_number TSRMLS_DC)
+{
+ // get the extension
+ auto *extension = find(module_number TSRMLS_CC);
+
+
+ UNREGISTER_INI_ENTRIES();
+ // free memory from array ini entries
+ static zend_ini_entry *ini_entries;
+ delete [] ini_entries;
+
+ // 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::processRequest(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::processIdle(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) : ExtensionBase(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, will be filled by Zend engine
+ _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::processStartup; // startup function for the whole extension
+ _entry.module_shutdown_func = &ExtensionImpl::processShutdown; // shutdown function for the whole extension
+ _entry.request_startup_func = &ExtensionImpl::processRequest; // startup function per request
+ _entry.request_shutdown_func = &ExtensionImpl::processIdle; // 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_CC);
+ });
+}
+
+/**
+ * End of namespace
+ */
+}
+