From df2520e4b2c87e2302ee4c6cec1e672091efebfb Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 8 Sep 2013 16:26:11 -0700 Subject: Refactoring function class, and making it even more easy to directly enable native C functions in PHP --- include/argument.h | 127 ++++++++++++++---------------------- include/arguments.h | 69 ++++++++++++++++++-- include/extension.h | 45 ++++++++++++- include/function.h | 108 +++++++++++++++++++++---------- include/functions.h | 2 +- include/hiddenpointer.h | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ include/member.h | 2 +- include/parameters.h | 38 +++++++++++ include/request.h | 2 +- include/type.h | 2 +- include/value.h | 2 +- phpcpp.h | 4 ++ src/arginfo.h | 2 +- src/argument.cpp | 74 +++++++++++---------- src/arguments.cpp | 46 +++++++++++++- src/callable.cpp | 4 +- src/callable.h | 2 +- src/extension.cpp | 130 +++++++++++++++++++++++++++---------- src/function.cpp | 76 +++++++++++++++++++--- src/functions.cpp | 54 ---------------- src/hiddenpointer.h | 166 ------------------------------------------------ src/includes.h | 5 +- src/parameters.cpp | 39 ++++++++++++ src/value.cpp | 2 +- tests/simple/simple.cpp | 28 ++++++-- 25 files changed, 758 insertions(+), 437 deletions(-) create mode 100644 include/hiddenpointer.h create mode 100644 include/parameters.h delete mode 100644 src/functions.cpp delete mode 100644 src/hiddenpointer.h create mode 100644 src/parameters.cpp diff --git a/include/argument.h b/include/argument.h index 5c0c23f..7a95f7d 100644 --- a/include/argument.h +++ b/include/argument.h @@ -9,14 +9,14 @@ */ /** - * Set up namespace + * Forward declaration */ -namespace PhpCpp { +struct _zend_arg_info; /** - * Forward definitions + * Set up namespace */ -class ArgInfo; +namespace Php { /** * Class definition @@ -25,105 +25,72 @@ class Argument { public: /** - * Constructor if this argument should be an instance of a certain class - * @param name Name of the argument - * @param classname If a specific class is required, the class type - * @param null Are NULL values allowed in stead of an instance? - * @param ref Is this a pass-by-reference argument? + * Prevent copying + * @param argument */ - Argument(const std::string &name, const std::string &classname, bool null = true, bool ref = false); - + Argument(const Argument &argument) = delete; + /** - * Constructor if the argument can be anything - * Note that only arrayType and callableType are supported type-hints - * @param name Name of the argument - * @param type Type hint (arrayType or callableType) - * @param ref Is this a pass-by-reference argument? + * Move constructor + * @param argument */ - Argument(const std::string &name, Type type = nullType, bool ref = false); - + Argument(Argument &&argument); + /** - * Constructor if the argument can be anything - * @param name Name of the argument - * @param ref Is this a pass-by-reference argument? + * Destructor */ - Argument(const std::string &name, bool ref = false); + virtual ~Argument() {}; /** - * Copy constructor - * @param argument The argument to copy + * Change the name + * @param name + * @return Argument */ - Argument(const Argument &argument) - { - // copy members - _refcount = argument._refcount; - _info = argument._info; - - // increase references - (*_refcount)++; - } + Argument &name(const char *name); /** - * Destructor + * Change the type + * @param type + * @return Argument */ - virtual ~Argument() - { - // cleanup current object - cleanup(); - } - + Argument &type(Type type = nullType); + /** - * Copy operator - * @param argument The argument to copy + * Require the parameter to be a certain class + * @param name Name of the class + * @param null Are null values allowed? * @return Argument */ - Argument &operator=(const Argument &argument) - { - // skip self assignment - if (this == &argument) return *this; - - // clean up current object - cleanup(); - - // copy members - _refcount = argument._refcount; - _info = argument._info; - - // increase references - (*_refcount)++; - - // done - return *this; - } + Argument &object(const char *classname, bool null = true); /** - * Retrieve argument info - * @return ArgInfo - * @internal + * Is this a by-ref argument? + * @param bool Mark as by-ref variable + * @return Argument */ - ArgInfo *internal() const - { - return _info; - } - -private: + Argument &byref(bool value = true); + /** - * Number of references - * @var int + * Prevent copy + * @param argument The argument to copy + * @return Argument */ - int *_refcount; - + Argument &operator=(const Argument &argument) = delete; + +protected: /** - * Pointer to the implementation - * @var ArgInfo + * Protected constructor, to prevent that users can instantiate the + * argument object themselves + * @param info */ - ArgInfo *_info; - + Argument(struct _zend_arg_info *info) : _info(info) {} + +private: /** - * Remove one reference from the object + * The argument info + * @var zend_arg_info */ - void cleanup(); - + struct _zend_arg_info *_info; }; /** diff --git a/include/arguments.h b/include/arguments.h index 65476c3..e57903f 100644 --- a/include/arguments.h +++ b/include/arguments.h @@ -8,27 +8,86 @@ * @copyright 2013 Copernica BV */ +/** + * Forward declaration + */ +struct _zend_arg_info; + /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Class definition */ -class Arguments : public std::vector +class Arguments { public: /** * Constructor - * @param argc The number of arguments + * @param min The min number of arguments + * @param max The max number of arguments + */ + Arguments(int min, int max); + + /** + * No copy or move operations + * @param arguments */ - Arguments(int argc); + Arguments(const Arguments &arguments) = delete; + Arguments(Arguments &&arguments) = delete; /** * Destructor */ - virtual ~Arguments() {} + virtual ~Arguments(); + + /** + * Number of arguments + * @return int + */ + int argc() + { + return _max; + } + + /** + * Number of required arguments + * @return int + */ + int required() + { + return _min; + } + + /** + * Get access to internal data + * @return zend_arg_info* + */ + struct _zend_arg_info *internal() + { + return _argv; + } + +private: + /** + * Min number of arguments + * @var integer + */ + int _min; + + /** + * Max number of arguments + * @var integer + */ + int _max; + + /** + * The arguments + * @var zend_arg_info[] + */ + struct _zend_arg_info *_argv; }; diff --git a/include/extension.h b/include/extension.h index 1962fd7..5dabb8e 100644 --- a/include/extension.h +++ b/include/extension.h @@ -26,7 +26,7 @@ struct _zend_module_entry; /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Class definition @@ -40,7 +40,7 @@ public: * @param version Extension version string * @param functions The functions that are defined */ - Extension(const char *name, const char *version, const Functions &functions); + Extension(const char *name = NULL, const char *version = NULL); /** * No copy'ing and no moving @@ -51,7 +51,7 @@ public: /** * Destructor */ - virtual ~Extension() { delete _entry; } + virtual ~Extension(); /** * Initialize the extension. @@ -117,6 +117,8 @@ public: // // // and initialize it // return _request->initialize(); + + return true; } /** @@ -140,9 +142,46 @@ public: // // // done // return result; + + return true; } + /** + * Add a function to the extension + * + * It is only possible to create functions during the initialization of + * the library, before the Extension::module() method is called. + * + * @param name Name of the function + * @param function The function to add + * @return Function The added function + */ + Function &add(const char *name, const Function &function); + + /** + * 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(); + + private: + /** + * Map of function objects defined in the library + * @var map + */ + std::map _functions; + + /** + * Hidden pointer to self + * @var HiddenPointer + */ + HiddenPointer _ptr; + /** * The information that is passed to the Zend engine * diff --git a/include/function.h b/include/function.h index ef185ce..67d2332 100644 --- a/include/function.h +++ b/include/function.h @@ -8,16 +8,17 @@ * @author Emiel Bruijntjes * @copyright 2013 Copernica BV */ - -/** - * Set up namespace - */ -namespace PhpCpp { /** * Forward definitions */ -class Callable; +struct _zend_function_entry; +struct _zend_internal_function_info; + +/** + * Set up namespace + */ +namespace Php { /** * Class definition @@ -27,22 +28,25 @@ class Function public: /** * Constructor - * @param name Name of the function - * @param arguments The arguments that can be passed to the function - */ - Function(const std::string &name, const std::initializer_list &arguments); - - /** - * Constructor - * @param name Name of the function + * @param min Min number of arguments + * @param max Max number of arguments */ - Function(const char *name) : Function(name, {}) {} + Function(int min = 0, int max = 0) + { + // construct the arguments + _arguments = std::shared_ptr(new Arguments(min, max)); + } /** * No copy constructor * @param function The other function */ - Function(const Function &function) = delete; + Function(const Function &function) + { + // copy members + _arguments = function._arguments; + _type = function._type; + } /** * Move constructor @@ -50,46 +54,80 @@ public: */ Function(Function &&function) { - _callable = function._callable; - function._callable = nullptr; + // copy arguments + _arguments = function._arguments; + _type = function._type; + + // no longer need the other arguments + function._arguments.reset(); } /** * Destructor */ - virtual ~Function(); + virtual ~Function() {} /** - * No assignment operator + * Assignment operator * @param function The other function * @return Function */ - Function &operator=(const Function &function) {} + Function &operator=(const Function &function) + { + // skip self reference + if (this == &function) return *this; + + // copy members + _arguments = function._arguments; + _type = function._type; + + // done + return *this; + } /** * Method that gets called every time the function is executed - * @param request The request during which the call was made - * @param arguments The actual arguments that were passed + * @param params The parameters that were passed * @return Variable Return value */ - virtual Value invoke(const Request *request, const std::initializer_list &arguments); - - /** - * Get access to the internal object - * @return Callable - * @internal - */ - Callable *internal() const + virtual Value invoke(Parameters ¶ms) { - return _callable; + return 0; } protected: /** - * Pointer to the callable object - * @var smart_ptr + * Suggestion for the return type + * @var Type + */ + Type _type = nullType; + + /** + * Pointer to the arguments + * @var shared_ptr */ - Callable *_callable; + std::shared_ptr _arguments; + +private: + /** + * Fill a function entry + * @param name Name of the function + * @param entry Entry to be filled + */ + void fill(const char *name, struct _zend_function_entry *entry); + + /** + * Fill function info + * @param name Name of the function + * @param info Info object to be filled + */ + void fill(const char *name, struct _zend_internal_function_info *info); + + /** + * Extension has access to the private members + */ + friend class Extension; + }; /** diff --git a/include/functions.h b/include/functions.h index fc44f1c..69653b4 100644 --- a/include/functions.h +++ b/include/functions.h @@ -16,7 +16,7 @@ struct _zend_function_entry; /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Class definition diff --git a/include/hiddenpointer.h b/include/hiddenpointer.h new file mode 100644 index 0000000..bcb44ba --- /dev/null +++ b/include/hiddenpointer.h @@ -0,0 +1,166 @@ +/** + * 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 + * @copyright 2013 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +template +class HiddenPointer +{ +public: + /** + * Constructor to hide the pointer in a buffer + * @param pointer The pointer to hide + * @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 *); + } + + /** + * Hide pointer in buffer + * @param pointer + * @param text + */ + HiddenPointer(Type *pointer, const std::string &text) : HiddenPointer(pointer, text.c_str(), text.size()) {} + + /** + * 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 &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 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/include/member.h b/include/member.h index d56b495..5166535 100644 --- a/include/member.h +++ b/include/member.h @@ -16,7 +16,7 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Forward definitions diff --git a/include/parameters.h b/include/parameters.h new file mode 100644 index 0000000..138d973 --- /dev/null +++ b/include/parameters.h @@ -0,0 +1,38 @@ +/** + * Parameters.h + * + * Wrapper around parameters that are passed to a + + * @author Emiel Bruijntjes + * @copyright 2013 Copernica BV + */ + +/** + * Namespace + */ +namespace Php { + +/** + * Class definition + */ +class Parameters : public std::vector +{ +public: + /** + * Constructor + * @param argc Number of arguments + * @param tsrm_ls + */ + Parameters(int argc);// TSRMLS_DC); + + /** + * Destructor + */ + virtual ~Parameters() {} +}; + +/** + * End of namespace + */ +} + diff --git a/include/request.h b/include/request.h index f37a449..4183966 100644 --- a/include/request.h +++ b/include/request.h @@ -17,7 +17,7 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Forward definitions diff --git a/include/type.h b/include/type.h index 200d658..04e488b 100644 --- a/include/type.h +++ b/include/type.h @@ -11,7 +11,7 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Supported types for variables diff --git a/include/value.h b/include/value.h index 2cc9029..64f92df 100644 --- a/include/value.h +++ b/include/value.h @@ -26,7 +26,7 @@ struct _zval_struct; /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Forward definitions diff --git a/phpcpp.h b/phpcpp.h index 35b790a..55234e9 100644 --- a/phpcpp.h +++ b/phpcpp.h @@ -14,16 +14,20 @@ #include #include #include +#include +#include /** * Include all headers files that are related to this library */ +#include #include #include #include #include #include #include +#include #include #include #include diff --git a/src/arginfo.h b/src/arginfo.h index 0080667..39e0e99 100644 --- a/src/arginfo.h +++ b/src/arginfo.h @@ -10,7 +10,7 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Class definition diff --git a/src/argument.cpp b/src/argument.cpp index 627ee05..387151a 100644 --- a/src/argument.cpp +++ b/src/argument.cpp @@ -11,59 +11,65 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** - * Constructor if this argument should be an instance of a certain class - * @param name Name of the argument - * @param classname If a specific class is required, the class type - * @param null Are NULL values allowed in stead of an instance? - * @param ref Is this a pass-by-reference argument? + * Move constructor + * @param argument */ -Argument::Argument(const std::string &name, const std::string &classname, bool null, bool ref) +Argument::Argument(Argument &&argument) { - _refcount = new int(1); - _info = new ArgInfo(name, classname, null, ref); + // copy data + _info = argument._info; } /** - * Constructor if the argument can be anything - * Note that only arrayType and callableType are supported type-hints - * @param name Name of the argument - * @param type Type hint (arrayType or callableType) - * @param ref Is this a pass-by-reference argument? + * Change the name + * @param name + * @return Argument */ -Argument::Argument(const std::string &name, Type type, bool ref) +Argument &Argument::name(const char *name) { - _refcount = new int(1); - _info = new ArgInfo(name, type, ref); + _info->name = name; + _info->name_len = strlen(name); + return *this; } /** - * Constructor if the argument can be anything - * @param name Name of the argument - * @param ref Is this a pass-by-reference argument? + * Change the type + * @param type + * @return Argument */ -Argument::Argument(const std::string &name, bool ref) +Argument &Argument::type(Type type) { - _refcount = new int(1); - _info = new ArgInfo(name, ref); + _info->type_hint = type; + return *this; } /** - * Clean up the object + * Require the parameter to be a certain class + * @param name Name of the class + * @param null Are null values allowed? + * @return Argument */ -void Argument::cleanup() +Argument &Argument::object(const char *classname, bool null) { - // one reference less - (*_refcount)--; - - // leap out if still in use - if (*_refcount > 0) return; - - // release memory - delete _refcount; - delete _info; + _info->type_hint = objectType; + _info->class_name = classname; + _info->class_name_len = strlen(classname); + _info->allow_null = null; + return *this; +} + +/** + * Is this a by-ref argument? + * @param bool Mark as by-ref variable + * @return Argument + */ +Argument &Argument::byref(bool value) +{ + _info->pass_by_reference = value; + return *this; } /** diff --git a/src/arguments.cpp b/src/arguments.cpp index e5e7ae5..53f030b 100644 --- a/src/arguments.cpp +++ b/src/arguments.cpp @@ -11,13 +11,55 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { + +/** + * Constructor + * @param min The min number of arguments + * @param max The max number of arguments + */ +Arguments::Arguments(int min, int max) +{ + // copy arguments + _min = min; + _max = max; + // max should be appropriate + if (_max < _min) _max = _min; + + // allocate memory for the arguments, with one extra record to hold information + _argv = new zend_arg_info[_max + 1]; + + // initialize the arguments + for (int i=1; i<_max+1; i++) + { + // initialize the argument + _argv[i].name = NULL; + _argv[i].name_len = 0; + _argv[i].class_name = NULL; + _argv[i].class_name_len = 0; + _argv[i].type_hint = nullType; + _argv[i].allow_null = false; + _argv[i].pass_by_reference = false; + } +} + +/** + * Destructor + */ +Arguments::~Arguments() +{ + // deallocate arguments + delete[] _argv; +} + /** * Constructor * @param argc Number of arguments * @param tsrm_ls */ +/* + Arguments::Arguments(int argc TSRMLS_DC) { // reserve plenty of space @@ -33,6 +75,8 @@ Arguments::Arguments(int argc TSRMLS_DC) push_back(Value(*arg)); } } +* +*/ /** * End of namespace diff --git a/src/callable.cpp b/src/callable.cpp index 69ef148..4db463c 100644 --- a/src/callable.cpp +++ b/src/callable.cpp @@ -11,7 +11,7 @@ /** * Namespace */ -namespace PhpCpp { +namespace Php { /** * Function that is called by the Zend engine every time that a function gets called @@ -101,7 +101,7 @@ void Callable::process(const std::initializer_list &arguments) for (auto it = begin(arguments); it != arguments.end(); it++) { // fill the argument structure - it->internal()->fill(&_argv[++i]); +// it->internal()->fill(&_argv[++i]); } } diff --git a/src/callable.h b/src/callable.h index b6f1e98..293e3aa 100644 --- a/src/callable.h +++ b/src/callable.h @@ -14,7 +14,7 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * Class definition diff --git a/src/extension.cpp b/src/extension.cpp index 2ed4fa7..2d9ef9f 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -9,7 +9,7 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** * If this extension is compiled for a PHP version with multi @@ -142,43 +142,109 @@ static int request_shutdown(INIT_FUNC_ARGS) * @param name Name of the extension * @param version Version number */ -Extension::Extension(const char *name, const char *version, const Functions &functions) +Extension::Extension(const char *name, const char *version) : _ptr(this, name) { - // allocate memory + // 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; - // 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(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 + // 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 = _ptr; // extension name, with a hidden pointer to the extension object + _entry->functions = NULL; // functions supported by this module (none for now) + _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; + _entry->globals_id_ptr = NULL; #else - _entry->globals_ptr = NULL; + _entry->globals_ptr = NULL; #endif + +} + +/** + * Destructor + */ +Extension::~Extension() +{ + // deallocate functions + if (_entry->functions) delete[] _entry->functions; + + // deallocate entry + delete _entry; +} + +/** + * Add a function to the library + * @param name Function name + * @param function Function object + * @return Function + */ +Function &Extension::add(const char *name, const Function &function) +{ + // add the function to the map + return _functions[name] = function; +} + +/** + * Retrieve the module entry + * @return zend_module_entry + */ +zend_module_entry *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 *functions = 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++) + { + // retrieve entry + zend_function_entry *entry = &functions[i]; + + // let the function fill the entry + it->second.fill(it->first, entry); + } + + // last entry should be set to all zeros + zend_function_entry *last = &functions[i]; + + // all should be set to zero + memset(last, 0, sizeof(zend_function_entry)); + + // store functions in entry object + _entry->functions = functions; + + // return the entry + return _entry; } /** diff --git a/src/function.cpp b/src/function.cpp index cb56d78..8612cf1 100644 --- a/src/function.cpp +++ b/src/function.cpp @@ -11,25 +11,83 @@ /** * Set up namespace */ -namespace PhpCpp { +namespace Php { /** - * Constructor + * Function that is called by the Zend engine every time that a function gets called + * @param ht + * @param return_value + * @param return_value_ptr + * @param this_ptr + * @param return_value_used + * @param tsrm_ls + * @return integer + */ +void invoke_function(INTERNAL_FUNCTION_PARAMETERS) +{ + // find the function name + const char *name = get_active_function_name(TSRMLS_C); + + // uncover the hidden pointer inside the function name + Function *function = HiddenPointer(name); + + // wrap the return value + Value ret(return_value, true); + + // construct parameters + Parameters params(ZEND_NUM_ARGS()); + + // call the appropriate object + ret = function->invoke(params); +} + +/** + * Fill a function entry + * + * This method is called when the extension is registering itself, when the + * function or method introces himself + * * @param name Name of the function - * @param arguments The arguments that can be passed to the function + * @param entry Entry to be filled */ -Function::Function(const std::string &name, const std::initializer_list &arguments) +void Function::fill(const char *name, zend_function_entry *entry) { - // create callable object - _callable = new Callable(name, arguments); + // fill the members of the entity, and hide a pointer to the current object in the name + entry->fname = HiddenPointer(this, name); + entry->handler = invoke_function; + entry->arg_info = _arguments->internal(); + entry->num_args = _arguments->argc(); + + // there are no flags like deprecated, private or protected + entry->flags = 0; + + // we should fill the first argument as well + fill(name, (zend_internal_function_info *)entry->arg_info); } /** - * Destructor + * Fill a function entry + * @param name Name of the function + * @param info Info to be filled */ -Function::~Function() +void Function::fill(const char *name, zend_internal_function_info *info) { - if (_callable) delete _callable; + // fill in all the members, note that return reference is false by default, + // because we do want to return references, inside the name we hide a pointer + // to the current object + info->_name = HiddenPointer(this, name); + info->_name_len = strlen(name); + info->_class_name = NULL; + + // number of required arguments, and the expected return type + info->required_num_args = _arguments->required(); + info->_type_hint = _type; + + // we do not support return-by-reference + info->return_reference = false; + + // passing by reference is not used + info->pass_rest_by_reference = false; } /** diff --git a/src/functions.cpp b/src/functions.cpp deleted file mode 100644 index f9e0ffb..0000000 --- a/src/functions.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Functions.cpp - * - * Implementation for the functions class - * - * @author Emiel Bruijntjes - * @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 &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 deleted file mode 100644 index 8e03eb2..0000000 --- a/src/hiddenpointer.h +++ /dev/null @@ -1,166 +0,0 @@ -/** - * 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 - * @copyright 2013 Copernica BV - */ - -/** - * Set up namespace - */ -namespace PhpCpp { - -/** - * Class definition - */ -template -class HiddenPointer -{ -public: - /** - * Constructor to hide the pointer in a buffer - * @param pointer The pointer to hide - * @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 *); - } - - /** - * Hide pointer in buffer - * @param pointer - * @param text - */ - HiddenPointer(Type *pointer, const std::string &text) : HiddenPointer(pointer, text.c_str(), text.size()) {} - - /** - * 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 &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 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 28d8b51..1a464e0 100644 --- a/src/includes.h +++ b/src/includes.h @@ -14,6 +14,8 @@ #include #include #include +#include +#include // for debugging #include @@ -31,12 +33,14 @@ /** * Include other files from this library */ +#include "../include/hiddenpointer.h" #include "../include/type.h" #include "../include/request.h" #include "../include/argument.h" #include "../include/value.h" #include "../include/member.h" #include "../include/arguments.h" +#include "../include/parameters.h" #include "../include/function.h" #include "../include/functions.h" #include "../include/extension.h" @@ -46,4 +50,3 @@ */ #include "callable.h" #include "arginfo.h" -#include "hiddenpointer.h" diff --git a/src/parameters.cpp b/src/parameters.cpp new file mode 100644 index 0000000..d31f49c --- /dev/null +++ b/src/parameters.cpp @@ -0,0 +1,39 @@ +/** + * Parameters.cpp + * + * @author Emiel Bruijntjes + * @copyright 2013 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Parameters + * @param argc Number of arguments + * @param tsrm_ls + */ +Parameters::Parameters(int argc TSRMLS_DC) +{ + // reserve plenty of space + reserve(argc); + + // loop through the arguments + for (int i=0; i