From 5235f87126cc2bca3907daada9f59e0c7c7bc834 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 20 Jan 2015 13:45:17 +0100 Subject: PHP-CPP now checks whether an already compiled extension is still compatible with the PHP-CPP library. This prevents weird crashes when users update their PHP-CPP library, without recompiling their extensions --- include/extension.h | 11 +++++++++- include/version.h | 17 +++++++++++++++ phpcpp.h | 1 + zend/extension.cpp | 5 +++-- zend/extensionimpl.cpp | 59 +++++++++++++++++++++++++++++++++++++++++++++++++- zend/extensionimpl.h | 29 ++++++++++++++++++++++--- zend/includes.h | 1 + 7 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 include/version.h diff --git a/include/extension.h b/include/extension.h index 562fceb..15ad5e1 100644 --- a/include/extension.h +++ b/include/extension.h @@ -38,10 +38,19 @@ class Extension : public Namespace public: /** * Constructor that defines a number of functions right away + * + * The first two parameters should be filled by the extension programmer with the + * name of the extension, and the version number of the extension (like "1.0"). + * The third parameter, apiversion, does not have to be supplied and is best kept + * to the default value. This third parameter checks whether the PHP-CPP version + * that is currently installed on the server is the same as the PHP-CPP version + * that was used to compile the extension with. + * * @param name Extension name * @param version Extension version string + * @param apiversion PHP API version (this should always be PHPCPP_API_VERSION, so you better not supply it) */ - Extension(const char *name, const char *version = "1.0"); + Extension(const char *name, const char *version = "1.0", int apiversion = PHPCPP_API_VERSION); /** * No copy'ing and no moving diff --git a/include/version.h b/include/version.h new file mode 100644 index 0000000..85d2b66 --- /dev/null +++ b/include/version.h @@ -0,0 +1,17 @@ +/** + * Version.h + * + * Macro with API version. The API version number prevents that + * extensions are loaded that are incompatible with the libphpcpp.so + * library + * + * @author Emiel Bruijntjes + * @copyright 2015 Copernica BV + */ + +/** + * Macro with version number (this is incremented with every release) + */ +#define PHPCPP_API_VERSION 20150120 + + diff --git a/phpcpp.h b/phpcpp.h index 231e112..b5216ff 100644 --- a/phpcpp.h +++ b/phpcpp.h @@ -27,6 +27,7 @@ /** * Include all headers files that are related to this library */ +#include #include #include #include diff --git a/zend/extension.cpp b/zend/extension.cpp index 1545f89..4ad54a3 100644 --- a/zend/extension.cpp +++ b/zend/extension.cpp @@ -15,9 +15,10 @@ namespace Php { * Constructor that defines a number of functions right away * @param name Extension name * @param version Extension version string + * @param apiversion API version number */ -Extension::Extension(const char *name, const char *version) : - Namespace(""), _impl(new ExtensionImpl(this, name, version)) {} +Extension::Extension(const char *name, const char *version, int apiversion) : + Namespace(""), _impl(new ExtensionImpl(this, name, version, apiversion)) {} /** * Destructor diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp index 262ecdb..fd97a76 100644 --- a/zend/extensionimpl.cpp +++ b/zend/extensionimpl.cpp @@ -219,13 +219,35 @@ int ExtensionImpl::processIdle(int type, int module_number TSRMLS_DC) return BOOL2SUCCESS(true); } +/** + * Function that is called when the PHP engine initializes with a different PHP-CPP + * version for the libphpcpp.so file than the version the extension was compiled for + * @param type Module type + * @param number Module number + * @param tsrm_ls + * @return int 0 on success + */ +int ExtensionImpl::processMismatch(int type, int module_number TSRMLS_DC) +{ + // get the extension + auto *extension = find(module_number TSRMLS_CC); + + // report a warning + warning << "Version mismatch between PHP-CPP and extension " << extension->name() << " " << extension->version() << " (recompile needed?)" << std::endl; + + // 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 + * @param apiversion API version number */ -ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version) : ExtensionBase(data) +ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion) : + ExtensionBase(data) { // keep extension pointer based on the name name2extension[name] = this; @@ -262,6 +284,17 @@ ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *vers _entry.globals_ptr = NULL; #endif + // everything is ok if the api versions match + if (apiversion == PHPCPP_API_VERSION) return; + + // mismatch between api versions, the extension is invalid, we use a + // different startup function to report to the user + _entry.module_startup_func = &ExtensionImpl::processMismatch; + + // the other callback functions are no longer necessary + _entry.module_shutdown_func = nullptr; + _entry.request_startup_func = nullptr; + _entry.request_shutdown_func = nullptr; } /** @@ -276,6 +309,26 @@ ExtensionImpl::~ExtensionImpl() if (_entry.functions) delete[] _entry.functions; } +/** + * The extension name + * @return const char * + */ +const char *ExtensionImpl::name() const +{ + // name is stored in the struct + return _entry.name; +} + +/** + * The extension version + * @return const char * + */ +const char *ExtensionImpl::version() const +{ + // version is stored in the struct + return _entry.version; +} + /** * Retrieve the module entry * @return zend_module_entry @@ -285,6 +338,10 @@ zend_module_entry *ExtensionImpl::module() // check if functions were already defined if (_entry.functions) return &_entry; + // if the 'processMismatch' function is installed, the API version is wrong, + // and nothing should be initialized + if (_entry.module_startup_func == &ExtensionImpl::processMismatch) return &_entry; + // the number of functions int count = _data->functions(); diff --git a/zend/extensionimpl.h b/zend/extensionimpl.h index 0e9a289..0a366ba 100644 --- a/zend/extensionimpl.h +++ b/zend/extensionimpl.h @@ -37,15 +37,16 @@ protected: * @var zend_ini_entry */ zend_ini_entry *_ini = nullptr; - + public: /** * Constructor * @param data Extension object created by the extension programmer * @param name Name of the extension * @param version Version number + * @param apiversion API version number */ - ExtensionImpl(Extension *data, const char *name, const char *version); + ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion); /** * No copy'ing and no moving @@ -58,6 +59,18 @@ public: */ virtual ~ExtensionImpl(); + /** + * The extension name + * @return const char * + */ + const char *name() const; + + /** + * The extension version + * @return const char * + */ + const char *version() const; + /** * Is the object locked (true) or is it still possible to add more functions, * classes and other elements to it? @@ -94,7 +107,7 @@ private: * @param tsrm_ls */ void initialize(TSRMLS_D); - + /** * Function that is called when the extension initializes * @param type Module type @@ -130,6 +143,16 @@ private: * @return int 0 on success */ static int processIdle(int type, int module_number TSRMLS_DC); + + /** + * Function that is called when the PHP engine initializes with a different PHP-CPP + * version for the libphpcpp.so file than the version the extension was compiled for + * @param type Module type + * @param number Module number + * @param tsrm_ls + * @return int 0 on success + */ + static int processMismatch(int type, int module_number TSRMLS_DC); }; /** diff --git a/zend/includes.h b/zend/includes.h index 83b1086..fb14b87 100644 --- a/zend/includes.h +++ b/zend/includes.h @@ -46,6 +46,7 @@ /** * Include other files from this library */ +#include "../include/version.h" #include "../include/inivalue.h" #include "../include/ini.h" #include "../include/exception.h" -- cgit v1.2.3