From dd5f3a635053aa03417cdab1228e03c94b9c3136 Mon Sep 17 00:00:00 2001 From: Martijn Otto Date: Tue, 17 May 2016 13:47:20 +0200 Subject: Fixed final compilation issues --- include/base.h | 2 +- include/file.h | 13 +- include/global.h | 22 +-- include/ini.h | 50 +----- include/type.h | 28 +-- include/value.h | 41 +---- zend/callable.cpp | 65 ++----- zend/callable.h | 40 ++--- zend/classimpl.cpp | 206 +++++++++++++--------- zend/classimpl.h | 39 +++-- zend/constantfuncs.cpp | 44 ++--- zend/exists.cpp | 39 +++-- zend/extensionimpl.cpp | 88 +++++----- zend/extensionimpl.h | 38 ++-- zend/file.cpp | 59 +++---- zend/global.cpp | 69 +++++++- zend/globals.cpp | 24 +-- zend/ini.cpp | 35 ++-- zend/iteratorimpl.cpp | 15 +- zend/iteratorimpl.h | 9 +- zend/object.cpp | 28 +-- zend/objectimpl.h | 2 +- zend/super.cpp | 12 +- zend/value.cpp | 460 ++++++++++++++++++++----------------------------- 24 files changed, 663 insertions(+), 765 deletions(-) diff --git a/include/base.h b/include/base.h index 1a1643b..023d711 100644 --- a/include/base.h +++ b/include/base.h @@ -50,7 +50,7 @@ public: /** * Virtual destructor */ - virtual ~Base() {} + virtual ~Base() = default; /** * Get access to a property by name using the [] operator diff --git a/include/file.h b/include/file.h index fff3cd0..4fab1c5 100644 --- a/include/file.h +++ b/include/file.h @@ -8,6 +8,11 @@ * @copyright 2014 Copernica BV */ +/** + * Forward declarations + */ +struct _zend_string; + /** * Set up namespace */ @@ -81,15 +86,15 @@ public: private: /** * The full resolved path name - * @var const char * + * @var struct _zend_string* */ - char *_path = nullptr; + struct _zend_string *_path = nullptr; /** * The opcodes of this file - * @var Opcodes + * @var std::unique_ptr */ - Opcodes *_opcodes = nullptr; + std::unique_ptr _opcodes; /** * Compile the file diff --git a/include/global.h b/include/global.h index 7a66997..605aeec 100644 --- a/include/global.h +++ b/include/global.h @@ -9,9 +9,10 @@ */ /** - * Forward definitions + * Forward declarations */ struct _zval_struct; +struct _zend_string; /** * Namespace @@ -34,12 +35,12 @@ public: * Move constructor * @param global */ - Global(Global &&global) _NOEXCEPT : Value(std::move(global)), _name(std::move(global._name)), _exists(global._exists) {} + Global(Global &&global) _NOEXCEPT; /** * Destructor */ - virtual ~Global() {} + virtual ~Global(); /** * Assignment operator @@ -143,35 +144,36 @@ protected: private: /** * Constructor for non-existing var - * @param name + * + * @param name Name for the variable that does not exist */ - Global(const char *name) : Value(), _name(name), _exists(false) {} + Global(const char *name); /** * Alternative constructor for non-existing var * @param name */ - Global(const std::string &name) : Value(), _name(name), _exists(false) {} + Global(const std::string &name); /** * Constructor to wrap zval for existing global bar * @param name * @param val */ - Global(const char *name, struct _zval_struct *val) : Value(val, true), _name(name), _exists(true) {} + Global(const char *name, struct _zval_struct *val); /** * Alternative constructor to wrap zval * @param name * @param val */ - Global(const std::string &name, struct _zval_struct *val) : Value(val, true), _name(name), _exists(true) {} + Global(const std::string &name, struct _zval_struct *val); /** * Name of the variable - * @var string + * @var struct _zend_string* */ - std::string _name; + struct _zend_string *_name; /** * Does it already exist? diff --git a/include/ini.h b/include/ini.h index 592fba3..a1899a4 100644 --- a/include/ini.h +++ b/include/ini.h @@ -47,68 +47,46 @@ public: * * @param name Name of the php.ini variable * @param value Default value - * @param orig Original value (if the user resets the variable, it is set back to this value) * @param place Place where the ini setting can be changed */ - Ini(const char *name, const char *value, const char *orig, const Place place = Place::All) : - _name(name), _value(value), _orig(orig), _place(place) {} - Ini(const char *name, const char *value, const Place place = Place::All) : - _name(name), _value(value), _orig_empty(true), _place(place) {} + _name(name), _value(value), _place(place) {} /** * Constructors for bool values * * @param name Name of the php.ini variable * @param value Default value - * @param orig Original value (if the user resets the variable, it is set back to this value) * @param place Place where the ini setting can be changed */ - Ini(const char *name,const bool value, const bool orig, const Place place = Place::All) : - _name(name), _value(bool2str(value)), _orig(bool2str(orig)), _place(place) {} - - Ini(const char *name, const bool value, const Place place = Place::All) : - _name(name), _value(bool2str(value)), _orig_empty(true), _place(place) {} + Ini(const char *name, bool value, const Place place = Place::All) : + _name(name), _value(bool2str(value)), _place(place) {} /** * Constructors for integer values * * @param name Name of the php.ini variable * @param value Default value - * @param orig Original value (if the user resets the variable, it is set back to this value) * @param place Place where the ini setting can be changed */ - Ini(const char *name, const int16_t value, const int16_t orig, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {} - Ini(const char *name, const int16_t value, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {} - - Ini(const char *name, const int32_t value, const int32_t orig, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {} + _name(name), _value(std::to_string(value)), _place(place) {} Ini(const char *name, const int32_t value, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {} - - Ini(const char *name, const int64_t value, const int64_t orig, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {} + _name(name), _value(std::to_string(value)), _place(place) {} Ini(const char *name, const int64_t value, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {} + _name(name), _value(std::to_string(value)), _place(place) {} /** * Constructors for floating point values * * @param name Name of the php.ini variable * @param value Default value - * @param orig Original value (if the user resets the variable, it is set back to this value) * @param place Place where the ini setting can be changed */ - Ini(const char *name, const double value, const double orig, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {} - Ini(const char *name, const double value, const Place place = Place::All) : - _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {} + _name(name), _value(std::to_string(value)), _place(place) {} /** @@ -116,7 +94,7 @@ public: * @param ini_entry * @param module_number */ - void fill(struct _zend_ini_entry *ini_entry, int module_number); + void fill(struct _zend_ini_entry_def *ini_entry, int module_number); private: @@ -147,18 +125,6 @@ private: */ std::string _value; - /** - * ini entry original value - * @var std::string - */ - std::string _orig; - - /** - * Is the orig value set or empty? - * @var bool - */ - bool _orig_empty = false; - /** * Place where the configuration can be changed * @var Place diff --git a/include/type.h b/include/type.h index 151919c..fbffb1f 100644 --- a/include/type.h +++ b/include/type.h @@ -18,17 +18,23 @@ namespace Php { * The values are the same as the ones used internally in Zend */ enum class PHPCPP_EXPORT Type : unsigned char { - Null = 0, // Null will allow any type - Numeric = 1, - Float = 2, - Bool = 3, - Array = 4, - Object = 5, - String = 6, - Resource = 7, - Constant = 8, - ConstantArray = 9, - Callable = 10 + Undefined = 0, // Variable is not set + Null = 1, // Null will allow any type + False = 2, // Boolean false + True = 3, // Boolean true + Numeric = 4, // Integer type + Float = 5, // Floating point type + String = 6, // A string obviously + Array = 7, // An array of things + Object = 8, // An object + Resource = 9, // A resource + Reference = 10, // Reference to another value (can be any type!) + Constant = 11, // A constant value + ConstantAST = 12, // I think an Abstract Syntax tree, not quite sure + + // "fake types", not quite sure what that means + Bool = 13, // You will never get this back as a type + Callable = 14, // I don't know why this is a "fake" type }; /** diff --git a/include/value.h b/include/value.h index 8c10bd8..99c8cc9 100644 --- a/include/value.h +++ b/include/value.h @@ -382,7 +382,7 @@ public: */ bool isNull() const { return type() == Type::Null; } bool isNumeric() const { return type() == Type::Numeric; } - bool isBool() const { return type() == Type::Bool; } + bool isBool() const { return type() == Type::False || type() == Type::True; } bool isString() const { return type() == Type::String; } bool isFloat() const { return type() == Type::Float; } bool isObject() const { return type() == Type::Object; } @@ -402,18 +402,6 @@ public: */ char *buffer() const; - /** - * Resize buffer space. If you want to write directly to the buffer (which - * is returned by the buffer() method), you should first reserve enough - * space in it. This can be done with this reserve() method. This will also - * turn the Value object into a string (if it was not already a string). - * The writable buffer is returned. - * - * @param size - * @return char* - */ - char *reserve(size_t size); - /** * Get access to the raw buffer for read operations. Note that this * only works for string variables - other variables return nullptr. @@ -977,14 +965,9 @@ public: { // store arguments Value vargs[] = { static_cast(args)... }; - //Value vargs[] = { std::forward(args)... }; - - // array of parameters - _zval_struct **params[sizeof...(Args)]; - for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;} // call the function - return exec(sizeof...(Args), params); + return exec(sizeof...(Args), vargs); } /** @@ -1021,12 +1004,8 @@ public: // store arguments Value vargs[] = { static_cast(args)... }; - // array of parameters - _zval_struct **params[sizeof...(Args)]; - for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;} - // call the function - return exec(name, sizeof...(Args), params); + return exec(name, sizeof...(Args), vargs); } template @@ -1035,12 +1014,8 @@ public: // store arguments Value vargs[] = { static_cast(args)... }; - // array of parameters - _zval_struct **params[sizeof...(Args)]; - for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;} - // call the function - return exec(name, sizeof...(Args), params); + return exec(name, sizeof...(Args), vargs); } /** @@ -1116,7 +1091,7 @@ private: * @param argv The parameters * @return Value */ - Value exec(int argc, struct _zval_struct ***params) const; + Value exec(int argc, Value *argv) const; /** * Call method with a number of parameters @@ -1125,8 +1100,8 @@ private: * @param argv The parameters * @return Value */ - Value exec(const char *name, int argc, struct _zval_struct ***params) const; - Value exec(const char *name, int argc, struct _zval_struct ***params); + Value exec(const char *name, int argc, Value *argv) const; + Value exec(const char *name, int argc, Value *argv); /** * Refcount - the number of references to the value @@ -1137,7 +1112,7 @@ private: protected: /** * The wrapped zval - * @var struct zval + * @var struct zval* */ struct _zval_struct *_val; diff --git a/zend/callable.cpp b/zend/callable.cpp index f0dc3e8..1231b55 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -27,7 +27,7 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) { // find the function name const char *name = get_active_function_name(TSRMLS_C); - + // uncover the hidden pointer inside the function name Callable *callable = HiddenPointer(name); @@ -44,7 +44,7 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) else { // construct parameters - ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); + ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC); // the function could throw an exception try @@ -52,11 +52,6 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) // get the result Value result(callable->invoke(params)); - // we're ready if the return value is not even used - if (!return_value_used) return; - - // @todo php 5.6 has a RETVAL_ZVAL_FAST macro that can be used instead (and is faster) - // return a full copy of the zval, and do not destruct it RETVAL_ZVAL(result._val, 1, 0); } @@ -70,10 +65,10 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) /** * Fill a function entry - * - * This method is called when the extension is registering itself, when the - * function or method introces himself - * + * + * This method is called when the extension is registering itself, when the + * function or method introduces himself + * * @param entry Entry to be filled * @param classname Optional class name * @param flags Is this a public property? @@ -83,12 +78,12 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int // fill the members of the entity, and hide a pointer to the current object in the name entry->fname = (const char *)_ptr; entry->handler = &Callable::invoke; - entry->arg_info = _argv; + entry->arg_info = _argv.get(); entry->num_args = _argc; entry->flags = flags; // we should fill the first argument as well - initialize((zend_arg_info *)entry->arg_info, classname); + initialize((zend_internal_function_info*)_argv.get(), classname); } /** @@ -96,51 +91,23 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int * @param info Info to be filled * @param classname Optional classname */ -void Callable::initialize(zend_arg_info *info, const char *classname) const +void Callable::initialize(zend_internal_function_info *info, const char *classname) const { -#if PHP_VERSION_ID >= 50400 - // up until php 5.3, the first info object is filled with alternative information, - // later it is casted to a zend_internal_function object - auto *finfo = (zend_internal_function_info *)info; - - // fill in all the members, note that return reference is false by default, - // because we do not support returning references in PHP-CPP, although Zend - // engine allows it. Inside the name we hide a pointer to the current object - finfo->_name = _ptr; - finfo->_name_len = ::strlen(_ptr); - finfo->_class_name = classname; + // store the classname + info->class_name = classname; // number of required arguments, and the expected return type - finfo->required_num_args = _required; - finfo->_type_hint = (unsigned char)_return; + info->required_num_args = _required; + info->type_hint = (unsigned char)_return; // we do not support return-by-reference - finfo->return_reference = false; - -# if PHP_VERSION_ID >= 50600 + info->return_reference = false; + // since php 5.6 there are _allow_null and _is_variadic properties. It's // not exactly clear what they do (@todo find this out) so for now we set // them to false - finfo->_allow_null = false; - finfo->_is_variadic = false; - -# else - // passing by reference is not used (only for php 5.4 and php 5.5) - finfo->pass_rest_by_reference = false; -# endif - -#else - // php 5.3 code - info->name = nullptr; - info->name_len = 0; - info->class_name = nullptr; - info->class_name_len = 0; - info->array_type_hint = false; info->allow_null = false; - info->pass_by_reference = false; - info->return_reference = false; - info->required_num_args = _required; -#endif + info->_is_variadic = false; } /** diff --git a/zend/callable.h b/zend/callable.h index 272b22e..65a971f 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -24,24 +24,23 @@ public: * @param name Function or method name * @param arguments Information about the arguments */ - Callable(const char *name, const Arguments &arguments = {}) : _ptr(this, name) + Callable(const char *name, const Arguments &arguments = {}) : + _ptr(this, name), + _argc(arguments.size()), + _argv(new zend_internal_arg_info[_argc + 1]) { - // construct vector for arguments - _argc = arguments.size(); - _argv = new zend_arg_info[_argc+1]; - // the first record is initialized with information about the function, // so we skip that here int i=1; // loop through the arguments - for (auto it = arguments.begin(); it != arguments.end(); it++) + for (auto &argument : arguments) { // increment counter with number of required parameters - if (it->required()) _required++; + if (argument.required()) _required++; // fill the arg info - fill(&_argv[i++], *it); + fill(&_argv[i++], argument); } } @@ -55,6 +54,8 @@ public: _required(that._required), _argc(that._argc), _argv(nullptr) {} + // @todo: we have no arguments after copy? is this correct? + // we do have the argument count though... /** * Move constructor @@ -65,19 +66,12 @@ public: _return(that._return), _required(that._required), _argc(that._argc), - _argv(that._argv) - { - // invalidate other object - that._argv = nullptr; - } + _argv(std::move(that._argv)) {} /** * Destructor */ - virtual ~Callable() - { - if (_argv) delete[] _argv; - } + virtual ~Callable() = default; /** * Method that gets called every time the function is executed @@ -101,7 +95,7 @@ public: * @param ns Active namespace * @param classname Optional class name */ - void initialize(zend_arg_info *info, const char *classname = nullptr) const; + void initialize(zend_internal_function_info *info, const char *classname = nullptr) const; protected: @@ -131,22 +125,22 @@ protected: /** * The arguments - * @var zend_arg_info[] + * @var std::unique_ptr */ - zend_arg_info *_argv = nullptr; + std::unique_ptr _argv; /** * Private helper method to fill an argument object * @param info object from the zend engine * @param arg original object */ - void fill(zend_arg_info *info, const Argument &arg) const + void fill(zend_internal_arg_info *info, const Argument &arg) const { // fill members - info->name = zend_string_init(arg.name(), ::strlen(arg.name()), 1); + info->name = arg.name(); // are we filling an object - if (arg.type() == Type::Object) info->class_name = zend_string_init(arg.classname(), ::strlen(arg.classname()), 1); + if (arg.type() == Type::Object) info->class_name = arg.classname(); else info->class_name = nullptr; // since php 5.4 there is a type-hint, but we only support arrays, objects and callables diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 84e23ef..e4a620f 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -7,6 +7,7 @@ * @copyright 2014 Copernica BV */ #include "includes.h" +#include /** * Set up namespace @@ -19,10 +20,10 @@ namespace Php { ClassImpl::~ClassImpl() { // destruct the entries - if (_entries) delete[] _entries; + delete[] _entries; // free the stored pointer - free(_self); + if (_self) zend_string_release(_self); } /** @@ -54,14 +55,14 @@ static ClassImpl *self(zend_class_entry *entry) * the string, in case PHP tries to read it) and after that the pointer * and we leave the doc_comment_len at 0. */ - while (entry->parent && (entry->info.user.doc_comment == nullptr || entry->info.user.doc_comment_len > 0)) + while (entry->parent && (entry->info.user.doc_comment == nullptr || ZSTR_LEN(entry->info.user.doc_comment) > 0)) { // we did not create this class entry, but luckily we have a parent entry = entry->parent; } // retrieve the comment (it has a pointer hidden in it to the ClassBase object) - const char *comment = entry->info.user.doc_comment; + const char *comment = ZSTR_VAL(entry->info.user.doc_comment); // the first byte of the comment is an empty string (null character), but // the next bytes contain a pointer to the ClassBase class @@ -90,13 +91,11 @@ struct CallData void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) { // retrieve the originally called (and by us allocated) function object - // (this was copied from the zend engine source code, code looks way too - // ugly to be made by me) - CallData *data = (CallData *)EG(current_execute_data)->function_state.function; + auto *data = (CallData *)execute_data->func; zend_internal_function *func = &data->func; // retrieve the function name - const char *name = func->function_name; + const char *name = ZSTR_VAL(func->function_name); ClassBase *meta = data->self->_base; // the data structure was allocated by ourselves in the getMethod or @@ -111,7 +110,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) Value result(return_value, true); // construct parameters - ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); + ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC); // retrieve the base object Base *base = params.object(); @@ -139,9 +138,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS) { // retrieve the originally called (and by us allocated) function object - // (this was copied from the zend engine source code, code looks way too - // ugly to be made by me) - CallData *data = (CallData *)EG(current_execute_data)->function_state.function; + auto *data = (CallData *)execute_data->func; // get self reference ClassBase *meta = data->self->_base; @@ -158,7 +155,7 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS) Value result(return_value, true); // construct parameters - ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); + ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC); // retrieve the base object Base *base = params.object(); @@ -181,13 +178,13 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS) /** * Method that returns the function definition of the __call function * - * @param object_ptr Pointer to the object from which we want to retrieve the member function + * @param object Pointer to the object from which we want to retrieve the member function * @param method The method that we want information about * @param key ??? * @param tsrm_ls * @return zend_function */ -zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, const zval *key TSRMLS_DC) +zend_function *ClassImpl::getMethod(zend_object **object, zend_string *method, const zval *key TSRMLS_DC) { // something strange about the Zend engine (once more). The structure with // object-handlers has a get_method and call_method member. When a function is @@ -198,13 +195,13 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, cons // first we'll check if the default handler does not have an implementation, // in that case the method is probably already implemented as a regular method - auto *defaultFunction = std_object_handlers.get_method(object_ptr, method, key TSRMLS_CC); + auto *defaultFunction = std_object_handlers.get_method(object, method, key TSRMLS_CC); // did the default implementation do anything? if (defaultFunction) return defaultFunction; // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(*object_ptr TSRMLS_CC); + auto *entry = (*object)->ce; // this is peculiar behavior of the zend engine, we first are going to dynamically // allocate memory holding all the properties of the __call method (we initially @@ -243,11 +240,11 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, cons * @param tsrm_ls * @return zend_function */ -zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *method, const zval *key TSRMLS_DC) +zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *method TSRMLS_DC) { // first we'll check if the default handler does not have an implementation, // in that case the method is probably already implemented as a regular method - auto *defaultFunction = zend_std_get_static_method(entry, method, key TSRMLS_CC); + auto *defaultFunction = zend_std_get_static_method(entry, method, nullptr TSRMLS_CC); // did the default implementation do anything? if (defaultFunction) return defaultFunction; @@ -285,7 +282,7 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string * * @param tsrm_ls * @return int */ -int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zval **object_ptr TSRMLS_DC) +int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr TSRMLS_DC) { // it is really unbelievable how the Zend engine manages to implement every feature // in a complete different manner. You would expect the __invoke() and the @@ -294,9 +291,6 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct // to fill the function parameter with all information about the invoke() // method that is going to get called - // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(object TSRMLS_CC); - // just like we did for getMethod(), we're going to dynamically allocate memory // with all information about the function auto *data = (CallData *)emalloc(sizeof(CallData)); @@ -309,12 +303,12 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct function->arg_info = nullptr; function->num_args = 0; function->required_num_args = 0; - function->scope = entry; + function->scope = *entry_ptr; function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; function->function_name = nullptr; // store pointer to ourselves - data->self = self(entry); + data->self = self(*entry_ptr); // assign this dynamically allocated variable to the func parameter // the cast is ok, because zend_internal_function is a member of the @@ -323,7 +317,7 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct // the object_ptr should be filled with the object on which the method is // called (otherwise the Zend engine tries to call the method statically) - *object_ptr = object; + *object_ptr = Z_OBJ_P(object); // done return SUCCESS; @@ -364,12 +358,20 @@ zend_object_handlers *ClassImpl::objectHandlers() _handlers.get_method = &ClassImpl::getMethod; _handlers.get_closure = &ClassImpl::getClosure; + // register destructor and deallocator + _handlers.dtor_obj = &ClassImpl::destructObject; + _handlers.free_obj = &ClassImpl::freeObject; + // handler to cast to a different type _handlers.cast_object = &ClassImpl::cast; // method to compare two objects _handlers.compare_objects = &ClassImpl::compare; + // set the offset between our class implementation and + // the zend_object member in the allocated structure + _handlers.offset = ObjectImpl::offset(); + // remember that object is now initialized _initialized = true; @@ -400,10 +402,10 @@ int ClassImpl::compare(zval *val1, zval *val2 TSRMLS_DC) try { // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(val1 TSRMLS_CC); + auto *entry = Z_OBJCE_P(val1); // other object must be of the same type - if (entry != zend_get_class_entry(val2 TSRMLS_CC)) throw NotImplemented(); + if (entry != Z_OBJCE_P(val2)) throw NotImplemented(); // we need the C++ class meta-information object ClassBase *meta = self(entry)->_base; @@ -448,7 +450,7 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC) Base *object = ObjectImpl::find(val TSRMLS_CC)->object(); // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(val TSRMLS_CC); + auto *entry = Z_OBJCE_P(val); // we need the C++ class meta-information object ClassBase *meta = self(entry)->_base; @@ -456,7 +458,10 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC) // retval is not yet initialized --- and again feelings of disbelief, // frustration, wonder and anger come up when you see that there are not two // functions in the Zend engine that have a comparable API - INIT_PZVAL(retval); + // + // this function was removed, because it was supposedly no longer necessary + // can we get away with removing it here too? + // INIT_PZVAL(retval); // when the magic function it not implemented, an exception will be thrown, // and the extension may throw a Php::Exception @@ -506,13 +511,14 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC) /** * Function that is called to create space for a cloned object - * @param val The object to be cloned - * @return zend_obejct_value The object to be created + * + * @param val The object to be cloned + * @return zend_object The object to be created */ -zend_object ClassImpl::cloneObject(zval *val TSRMLS_DC) +zend_object *ClassImpl::cloneObject(zval *val TSRMLS_DC) { // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(val TSRMLS_CC); + auto *entry = Z_OBJCE_P(val); // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -532,17 +538,17 @@ zend_object ClassImpl::cloneObject(zval *val TSRMLS_DC) if (!cpp) zend_error(E_ERROR, "Unable to clone %s", entry->name); // store the object - auto *new_object = new ObjectImpl(entry, cpp, impl->objectHandlers, 1 TSRMLS_CC); + auto *new_object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1 TSRMLS_CC); // clone the members (this will also call the __clone() function if the user // had registered that as a visible method) - zend_objects_clone_members(new_object->php(), result, old_object->php(), Z_OBJ_HANDLE_P(val) TSRMLS_CC); + zend_objects_clone_members(new_object->php(), old_object->php() TSRMLS_CC); // was a custom clone method installed? If not we call the magic c++ __clone method if (!entry->clone) meta->callClone(cpp); // done - return new_object->object(); + return new_object->php(); } /** @@ -600,10 +606,11 @@ int ClassImpl::countElements(zval *object, long *count TSRMLS_DC) * @param object The object on which it is called * @param offset The name of the property * @param type The type of the variable??? + * @param rv Pointer to where to store the data * @param tsrm_ls * @return zval */ -zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC) +zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv TSRMLS_DC) { // what to do with the type? // @@ -633,7 +640,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC) try { // ArrayAccess is implemented, call function - return toZval(arrayaccess->offsetGet(offset), type); + return toZval(arrayaccess->offsetGet(offset), type, rv); } catch (Exception &exception) { @@ -650,7 +657,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC) if (!std_object_handlers.read_dimension) return nullptr; // call default - return std_object_handlers.read_dimension(object, offset, type TSRMLS_CC); + return std_object_handlers.read_dimension(object, offset, type, rv TSRMLS_CC); } } @@ -790,26 +797,52 @@ void ClassImpl::unsetDimension(zval *object, zval *member TSRMLS_DC) /** * Helper method to turn a property into a zval - * @param value - * @param type - * @return Value + * + * @param value The value to convert to a zval + * @param type The type of operation (read or write) + * @param rv Pointer to where to store the data + * @return The result (same as the ptr input) */ -zval *ClassImpl::toZval(Value &&value, int type) +zval *ClassImpl::toZval(Value &&value, int type, zval *rv) { - // because we do not want the value object to destruct the zval when - // it falls out of scope, we detach the zval from it, if this is a regular - // read operation we can do this right away - if (type == 0) return value.detach(false); - - // this is a more complicated read operation, the scripts wants to get - // deeper access to the returned value. This, however, is only possible - // if the value has more than once reference (if it has a refcount of one, - // the value object that we have here is the only instance of the zval, - // and it is simply impossible to return a reference or so - if (value.refcount() <= 1) return value.detach(false); - - // we're dealing with an editable zval, return a reference variable - return Value(value.detach(false), true).detach(false); + // the result zval that needs to be copied over + zval *result = nullptr; + + /** + * Because we do not want the value object to destruct the zval when + * it falls out of scope, we detach the zval from it, if this is a regular + * read operation we can do this right away. + * + * For write operations we need to check the refcount. If the refcount is + * only 1 (meaning the value object has the only reference) we cannot return + * a reference because there _is_ nothing to reference (the value will destruct) + */ + if (type == 0 || value.refcount() <= 1) + { + // first retrieve the value so we can copy it + result = value.detach(false); + } + // this is an editable zval, return a reference to it + else + { + // we're dealing with an editable zval, retrieve a reference variable + result = Value(value.detach(false), true).detach(false); + } + + // now copy the value over to the pointer + ZVAL_COPY_VALUE(rv, result); + + // if the zval has a reference count we must decrease it + Z_TRY_DELREF_P(result); + + // the pointer from the value may now be destroyed + // (it was allocated by the value and detached) + // we do not actually "destroy" the value here, + // even if the refcount reaches 0 here! + delete result; + + // return the pointer to the value + return rv; } /** @@ -819,7 +852,7 @@ zval *ClassImpl::toZval(Value &&value, int type) * @param offset The name of the property * @param type The type of the variable??? * @param cache_slot The cache slot used - * @param rv The "return value" (for errors + * @param rv Pointer to where to store the data * @param tsrm_ls * @return val */ @@ -846,7 +879,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s Base *base = ObjectImpl::find(object TSRMLS_CC)->object(); // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(object TSRMLS_CC); + auto *entry = Z_OBJCE_P(object); // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -866,12 +899,12 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s if (iter == impl->_properties.end()) { // retrieve value from the __get method - return toZval(meta->callGet(base, key), type); + return toZval(meta->callGet(base, key), type, rv); } else { // get the value - return toZval(iter->second->get(base), type); + return toZval(iter->second->get(base), type, rv); } } catch (const NotImplemented &exception) @@ -880,7 +913,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s if (!std_object_handlers.read_property) return nullptr; // call default - return std_object_handlers.read_property(object, name, type, key TSRMLS_CC); + return std_object_handlers.read_property(object, name, type, cache_slot, rv TSRMLS_CC); } catch (Exception &exception) { @@ -912,7 +945,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach Base *base = ObjectImpl::find(object TSRMLS_CC)->object(); // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(object TSRMLS_CC); + auto *entry = Z_OBJCE_P(object); // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -949,7 +982,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach if (!std_object_handlers.write_property) return; // call the default - std_object_handlers.write_property(object, name, value, key TSRMLS_CC); + std_object_handlers.write_property(object, name, value, cache_slot TSRMLS_CC); } catch (Exception &exception) { @@ -989,7 +1022,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void ** Base *base = ObjectImpl::find(object TSRMLS_CC)->object(); // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(object TSRMLS_CC); + auto *entry = Z_OBJCE_P(object); // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -1022,7 +1055,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void ** if (!std_object_handlers.has_property) return 0; // call default - return std_object_handlers.has_property(object, name, has_set_exists, key TSRMLS_CC); + return std_object_handlers.has_property(object, name, has_set_exists, cache_slot TSRMLS_CC); } catch (Exception &exception) { @@ -1052,7 +1085,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM try { // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(object TSRMLS_CC); + auto *entry = Z_OBJCE_P(object); // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -1075,7 +1108,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM if (!std_object_handlers.unset_property) return; // call the default - std_object_handlers.unset_property(object, member, key TSRMLS_CC); + std_object_handlers.unset_property(object, member, cache_slot TSRMLS_CC); } catch (Exception &exception) { @@ -1089,7 +1122,6 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM * Function that is called when an object is about to be destructed * This will call the magic __destruct method * @param object - * @param handle * @param tsrm_ls */ void ClassImpl::destructObject(zend_object *object TSRMLS_DC) @@ -1109,7 +1141,7 @@ void ClassImpl::destructObject(zend_object *object TSRMLS_DC) catch (const NotImplemented &exception) { // fallback on the default destructor call - zend_objects_destroy_object(object, handle TSRMLS_CC); + zend_objects_destroy_object(object TSRMLS_CC); } catch (Exception &exception) { @@ -1140,7 +1172,7 @@ void ClassImpl::freeObject(zend_object *object TSRMLS_DC) * @param tsrm_ls * @return zend_object_value The newly created object */ -zend_object ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC) +zend_object *ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC) { // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -1157,7 +1189,7 @@ zend_object ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC) auto *object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1 TSRMLS_CC); // return the php object stored in the implementation - return object->object(); + return object->php(); } /** @@ -1207,7 +1239,7 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje * @param tsrm_ls * @return int */ -int ClassImpl::serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) +int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data TSRMLS_DC) { // get the serializable object Serializable *serializable = dynamic_cast(ObjectImpl::find(object TSRMLS_CC)->object()); @@ -1247,13 +1279,13 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, zend_uint *buf_le * @param tsrm_ls * @return int */ -int ClassImpl::unserialize(zval **object, zend_class_entry *entry, const unsigned char *buffer, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC) +int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned char *buffer, size_t buf_len, zend_unserialize_data *data TSRMLS_DC) { // create the PHP object - object_init_ex(*object, entry); + object_init_ex(object, entry); // turn this into a serializale - Serializable *serializable = dynamic_cast(ObjectImpl::find(*object TSRMLS_CC)->object()); + Serializable *serializable = dynamic_cast(ObjectImpl::find(object TSRMLS_CC)->object()); // user may throw an exception in the serialize() function try @@ -1346,14 +1378,6 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // we need a special constructor entry.create_object = &ClassImpl::createObject; - // register destructor and deallocator - entry.dtor_obj = &ClassImpl::destructObject; - entry.free_obj = &ClassImpl::freeObject; - - // set the offset for the zend_object, to allow PHP to - // locate the original point of the allocated memory - entry.offset = ObjectImpl::offset(); - // register function that is called for static method calls entry.get_static_method = &ClassImpl::getStaticMethod; @@ -1375,7 +1399,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref if (_parent->_entry) { // register the class - _entry = zend_register_internal_class_ex(&entry, _parent->_entry, const_cast(_parent->name().c_str()) TSRMLS_CC); + _entry = zend_register_internal_class_ex(&entry, _parent->_entry TSRMLS_CC); } else { @@ -1405,6 +1429,16 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // this pointer has to be copied to temporary pointer, as &this causes compiler error ClassImpl *impl = this; + // allocate memory for the doc_comment (which we abuse for storing a pointer to ourselves) + _self = zend_string_alloc(sizeof(this), 1); + + // make the string appear empty + ZSTR_VAL(_self)[0] = '\0'; + ZSTR_LEN(_self) = 0; + + // copy over the 'this'-pointer after the null-character + std::memcpy(ZSTR_VAL(_self) + 1, &impl, sizeof(impl)); + // set access types flags for class _entry->ce_flags = (int)_type; diff --git a/zend/classimpl.h b/zend/classimpl.h index 47e7785..1754755 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -93,9 +93,9 @@ private: /** * Memory allocated by this object to hide a pointer - * @var char* + * @var zend_string* */ - char *_self = nullptr; + zend_string *_self = nullptr; /** * Retrieve an array of zend_function_entry objects that hold the @@ -109,11 +109,13 @@ private: /** * Helper method to turn a property into a zval - * @param value - * @param type - * @return Value + * + * @param value The value to convert to a zval + * @param type The type of operation (read or write) + * @param rv Pointer to where to store the data + * @return The result (same as the ptr input) */ - static zval *toZval(Value &&value, int type); + static zval *toZval(Value &&value, int type, zval *rv); public: /** @@ -176,8 +178,8 @@ public: * @param tsrm_ls * @return zend_object Object info */ - static zend_object createObject(zend_class_entry *entry TSRMLS_DC); - static zend_object cloneObject(zval *val TSRMLS_DC); + static zend_object *createObject(zend_class_entry *entry TSRMLS_DC); + static zend_object *cloneObject(zval *val TSRMLS_DC); static void destructObject(zend_object *object TSRMLS_DC); static void freeObject(zend_object *object TSRMLS_DC); @@ -190,8 +192,8 @@ public: * @param return_value_used Is the return value used or not? * @param tsrm_ls */ - static void callMethod(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC); - static void callInvoke(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC); + static void callMethod(zend_execute_data *execute_data, zval *return_value TSRMLS_DC); + static void callInvoke(zend_execute_data *execute_data, zval *return_value TSRMLS_DC); /** * Function that is used to count the number of elements in the object @@ -210,10 +212,11 @@ public: * @param offset The name of the property * @param value The new value * @param type The type of the variable??? + * @param rv Pointer to where to store the data * @param check_empty ???? * @return zval */ - static zval *readDimension(zval *object, zval *offset, int type TSRMLS_DC); + static zval *readDimension(zval *object, zval *offset, int type, zval *rv TSRMLS_DC); static void writeDimension(zval *object, zval *offset, zval *value TSRMLS_DC); static int hasDimension(zval *object, zval *offset, int check_empty TSRMLS_DC); static void unsetDimension(zval *object, zval *offset TSRMLS_DC); @@ -248,7 +251,7 @@ public: * @param offset The name of the property * @param type The type of the variable??? * @param cache_slot The cache slot used - * @param rv The "return value" (for errors + * @param rv Pointer to where to store the data * @param tsrm_ls * @return zval */ @@ -291,13 +294,13 @@ public: /** * Method that returns information about the function signature of a undefined method * - * @param object_ptr Pointer to the object from which we want to retrieve the member function + * @param object Pointer to the object from which we want to retrieve the member function * @param method The method that we want information about * @param key ??? * @param tsrm_ls * @return zend_function */ - static zend_function *getMethod(zval **object_ptr, zend_string *method, const zval *key TSRMLS_DC); + static zend_function *getMethod(zend_object **object, zend_string *method, const zval *key TSRMLS_DC); /** * Method that returns information about the function signature of an undefined static method @@ -308,7 +311,7 @@ public: * @param tsrm_ls * @return zend_function */ - static zend_function *getStaticMethod(zend_class_entry *entry, zend_string *method, const zval *key TSRMLS_DC); + static zend_function *getStaticMethod(zend_class_entry *entry, zend_string *method TSRMLS_DC); /** * Method that returns information about the __invoke() method @@ -319,7 +322,7 @@ public: * @param tsrm_ls * @return int */ - static int getClosure(zval *object, zend_class_entry **entry, zend_function **func, zval **object_ptr TSRMLS_DC); + static int getClosure(zval *object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr TSRMLS_DC); /** * Function to cast the object to a different type @@ -350,8 +353,8 @@ public: * @param tsrm_ls * @return int */ - static int serialize(zval *object, unsigned char **buffer, unsigned int *buf_len, zend_serialize_data *data TSRMLS_DC); - static int unserialize(zval **object, zend_class_entry *entry, const unsigned char *buffer, unsigned int buf_len, zend_unserialize_data *data TSRMLS_DC); + static int serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data TSRMLS_DC); + static int unserialize(zval *object, zend_class_entry *entry, const unsigned char *buffer, size_t buf_len, zend_unserialize_data *data TSRMLS_DC); /** * Add a method to the class diff --git a/zend/constantfuncs.cpp b/zend/constantfuncs.cpp index 40ea20e..46886cf 100644 --- a/zend/constantfuncs.cpp +++ b/zend/constantfuncs.cpp @@ -39,13 +39,13 @@ Value constant(const char *constant, size_t size) // we need the tsrm_ls variable TSRMLS_FETCH(); - // the value that holds the result - Value result; - // retrieve the constant - if (!zend_get_constant(constant, size, result._val TSRMLS_CC)) return nullptr; - - // zval was correctly retrieved, wrap in value + auto *result = zend_get_constant(zend_string_init(constant, size, 1) TSRMLS_CC); + + // did the constant exist? + if (!result) return nullptr; + + // return the valid result return result; } @@ -71,14 +71,13 @@ bool define(const char *name, size_t size, const Value &value) { // we need the tsrm_ls variable TSRMLS_FETCH(); - + // the constant structure from the zend engine zend_constant constant; - - // copy the name (note that name_len also includes the end-of-string '\0' byte) - constant.name = zend_strndup(name, size); - constant.name_len = size + 1; - + + // copy the name + constant.name = zend_string_init(name, size, 1); + // only scalar values can be used for constants if (value.isScalar()) { @@ -90,19 +89,19 @@ bool define(const char *name, size_t size, const Value &value) { // we're going to convert the value object into a string, and use that Value str = value.clone(Type::String); - + // use the copied value constant.value = *str._val; zval_copy_ctor(&constant.value); } - + // constants are case sensitive (but not persistent, because this is a user // space constant!) constant.flags = CONST_CS; // as module number we use a fake module number constant.module_number = PHP_USER_CONSTANT; - + // register the constant return zend_register_constant(&constant TSRMLS_CC) == SUCCESS; } @@ -137,20 +136,21 @@ bool define(const std::string &name, const Value &value) * @param size * @return bool */ -bool defined(const char *name, size_t size) +bool defined(const char *name, size_t size) { // we need the tsrm_ls variable TSRMLS_FETCH(); - // result variable - zval c; - // retrieve the constant - if (!zend_get_constant_ex(name, size, &c, NULL, ZEND_FETCH_CLASS_SILENT TSRMLS_CC)) return false; + auto *value = zend_get_constant_ex(zend_string_init(name, size, 1), nullptr, ZEND_FETCH_CLASS_SILENT TSRMLS_CC); + + // check if the value was found + if (!value) return false; // constant exists, but the returned zval should first be destructed - zval_dtor(&c); - + // @todo: is this necessary in PHP 7? + zval_dtor(value); + // done return true; } diff --git a/zend/exists.cpp b/zend/exists.cpp index 1177e47..21d3184 100644 --- a/zend/exists.cpp +++ b/zend/exists.cpp @@ -1,7 +1,7 @@ /** * Exists.cpp * - * This file holds the implementation of all *_exists() functions, + * This file holds the implementation of all *_exists() functions, * like class_exists(), et cetera * * @author andot @@ -33,41 +33,42 @@ namespace Php { * @param autoload * @return bool */ -bool class_exists(const char *classname, size_t len, bool autoload) +bool class_exists(const char *classname, size_t len, bool autoload) { // we need the tsrm_ls variable TSRMLS_FETCH(); - // we're going to load a class-entry - zend_class_entry **ce; - // should we autoload the class? - if (autoload) + if (autoload) { + // retrieve class entry + auto *ce = zend_lookup_class(zend_string_init(classname, len, 1) TSRMLS_CC); + // no auto-load - if (SUCCESS != zend_lookup_class(classname, len, &ce TSRMLS_CC)) return false; + if (!ce) return false; // the found "class" could also be an interface or trait, which we do no want - return ((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0; + return (ce->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0; } else { // starting slashes can be ignored if (len > 0 && classname[0] == '\\') { classname++; len--; } - - // all classes are in lowercase in the hash, so we make - // a temporary buffer for storing the lowercase class name - // (is this smart? memory allocation is expensive!) - std::unique_ptr lc_name(new char[len + 1]); - + + // allocate a zend_string + auto *string = zend_string_alloc(len, 1); + // copy the name to lowercase, but ignore the starting slash (if there is one) - zend_str_tolower_copy(lc_name.get(), classname, len); + zend_str_tolower_copy(ZSTR_VAL(string), classname, len); // see if there is a class with this name - if (SUCCESS != zend_hash_find(EG(class_table), lc_name.get(), len + 1, (void **) &ce)) return false; - - // the found "class" could also be an interface or trait, which we do no want - return !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS); + auto *val = zend_hash_find(EG(class_table), string); + + // check whether something was found + if (val == nullptr) return false; + + // the "something" could also be an interface or trait, which we do no want + return !(((Z_CE_P(val))->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS); } } diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp index bacf80f..5261b0d 100644 --- a/zend/extensionimpl.cpp +++ b/zend/extensionimpl.cpp @@ -41,14 +41,14 @@ static void init_globals(zend_phpcpp_globals *globals) {} * 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 + * 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 name2extension; @@ -56,11 +56,11 @@ static std::map 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 + * 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) @@ -68,10 +68,10 @@ 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; } @@ -87,14 +87,14 @@ 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; } @@ -109,7 +109,7 @@ static ExtensionImpl *find(int number TSRMLS_DC) int ExtensionImpl::processStartup(int type, int module_number TSRMLS_DC) { // initialize and allocate the "global" variables - ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL); + ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL); // get the extension auto *extension = find(module_number TSRMLS_CC); @@ -148,10 +148,10 @@ 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); } @@ -167,10 +167,10 @@ 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); } @@ -187,10 +187,10 @@ 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); } @@ -202,12 +202,12 @@ int ExtensionImpl::processMismatch(int type, int module_number TSRMLS_DC) * @param version Version number * @param apiversion API version number */ -ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion) : +ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion) : 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 @@ -242,8 +242,8 @@ ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *vers // 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 + + // 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; @@ -260,12 +260,9 @@ ExtensionImpl::~ExtensionImpl() { // remove from the array name2extension.erase(_entry.name); - - // deallocate the php.ini entries - if (_ini) delete[] _ini; - + // deallocate functions - if (_entry.functions) delete[] _entry.functions; + delete[] _entry.functions; } /** @@ -303,7 +300,7 @@ zend_module_entry *ExtensionImpl::module() // the number of functions int count = _data->functions(); - + // skip if there are no functions if (count == 0) return &_entry; @@ -315,10 +312,10 @@ zend_module_entry *ExtensionImpl::module() // apply a function to each function _data->functions([&i, entries](const std::string &prefix, NativeFunction &function) { - + // initialize the function function.initialize(prefix, &entries[i]); - + // move on to the next iteration i++; }); @@ -345,40 +342,40 @@ zend_module_entry *ExtensionImpl::module() bool ExtensionImpl::initialize(int module_number TSRMLS_DC) { // array contains ini settings - _ini = new zend_ini_entry[_data->iniVariables()+1]; + _ini.reset(new zend_ini_entry_def[_data->iniVariables()+1]); // the entry that we're filling int i = 0; // Fill the php.ini entries _data->iniVariables([this, &i, module_number](Ini &ini) { - + // initialize the function - zend_ini_entry *entry = &_ini[i]; - + zend_ini_entry_def *entry = &_ini[i]; + // fill the property ini.fill(entry, module_number); - + // move on to the next iteration - i++; + ++i; }); // last entry should be set to all zero's memset(&_ini[i], 0, sizeof(zend_ini_entry)); // register ini entries in Zend core - zend_register_ini_entries(_ini, module_number TSRMLS_CC); + zend_register_ini_entries(_ini.get(), module_number TSRMLS_CC); // the constants are registered after the module is ready _data->constants([module_number TSRMLS_CC](const std::string &prefix, Constant &c) { - + // forward to implementation class c.implementation()->initialize(prefix, module_number TSRMLS_CC); }); - + // we also need to register each class, find out all classes _data->classes([TSRMLS_C](const std::string &prefix, ClassBase &c) { - + // forward to implementation class c.implementation()->initialize(&c, prefix TSRMLS_CC); }); @@ -386,10 +383,10 @@ bool ExtensionImpl::initialize(int module_number TSRMLS_DC) // initialize the PhpCpp::Functor class Functor::initialize(TSRMLS_C); - // remember that we're initialized (when you use "apache reload" it is + // remember that we're initialized (when you use "apache reload" it is // possible that the processStartup() method is called more than once) _locked = true; - + // is the callback registered? if (_onStartup) _onStartup(); @@ -409,17 +406,14 @@ bool ExtensionImpl::shutdown(int module_number TSRMLS_DC) zend_unregister_ini_entries(module_number TSRMLS_CC); // destruct the ini entries - if (_ini) delete[] _ini; - - // forget the ini entries - _ini = nullptr; + _ini.reset(); // shutdown the functor class Functor::shutdown(TSRMLS_C); // is the callback registered? if (_onShutdown) _onShutdown(); - + // done return true; } diff --git a/zend/extensionimpl.h b/zend/extensionimpl.h index 3a0fc63..b933624 100644 --- a/zend/extensionimpl.h +++ b/zend/extensionimpl.h @@ -2,7 +2,7 @@ * ExtensionImpl.h * * Extension implementation for the Zend engine. - * + * * @author Emiel Bruijntjes * @copyright 2013, 2014 Copernica BV */ @@ -23,21 +23,21 @@ protected: * @var zend_module_entry */ zend_module_entry _entry; - + /** * Is the object locked? This prevents crashes for 'apache reload' * because we then do not have to re-initialize the entire php engine * @var bool */ bool _locked = false; - + /** * The .ini entries - * - * @var zend_ini_entry + * + * @var std::unique_ptr */ - zend_ini_entry *_ini = nullptr; - + std::unique_ptr _ini = nullptr; + public: /** * Constructor @@ -47,31 +47,31 @@ public: * @param apiversion API version number */ ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion); - + /** * No copy'ing and no moving */ ExtensionImpl(const ExtensionImpl &extension) = delete; ExtensionImpl(ExtensionImpl &&extension) = delete; - + /** * Destructor */ 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? * @return bool @@ -81,17 +81,17 @@ public: // return member return _locked; } - + /** * 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* @@ -100,7 +100,7 @@ public: { return module(); } - + private: /** * Initialize the namespace after it was registered @@ -126,7 +126,7 @@ private: * @return int 0 on success */ static int processStartup(int type, int module_number TSRMLS_DC); - + /** * Function that is called when the extension is about to be stopped * @param type Module type @@ -135,7 +135,7 @@ private: * @return int */ static int processShutdown(int type, int module_number TSRMLS_DC); - + /** * Function that is called when a request starts * @param type Module type diff --git a/zend/file.cpp b/zend/file.cpp index 2b7e77f..77b96f5 100644 --- a/zend/file.cpp +++ b/zend/file.cpp @@ -19,10 +19,10 @@ namespace Php { /** * Constructor - * + * * The constructor receives a filename as parameter. It uses the normal - * PHP include path resolve algorithms to find the location of the file. - * + * PHP include path resolve algorithms to find the location of the file. + * * @param name the filename * @param size length of the filename */ @@ -30,16 +30,9 @@ File::File(const char *name, size_t size) { // we need the tsrm_ls variable TSRMLS_FETCH(); - + // resolve the path _path = zend_resolve_path(name, size TSRMLS_CC); - - // the resolve-path function sometimes returns the original pointer, we - // do not want that because we may have to store the pathname in this object - if (_path != name) return; - - // make a full copy of the pathname - _path = estrndup(name, size); } /** @@ -48,10 +41,7 @@ File::File(const char *name, size_t size) File::~File() { // clean up path name - if (_path) efree(_path); - - // clean up opcodes - if (_opcodes) delete _opcodes; + if (_path) zend_string_release(_path); } /** @@ -62,10 +52,10 @@ bool File::compile() { // never works if the path is invalid if (!_path) return false; - + // is the file already compiled? if (_opcodes) return _opcodes->valid(); - + // we are going to open the file zend_file_handle fileHandle; @@ -73,20 +63,20 @@ bool File::compile() TSRMLS_FETCH(); // open the file - if (zend_stream_open(_path, &fileHandle TSRMLS_CC) == FAILURE) return false; + if (zend_stream_open(ZSTR_VAL(_path), &fileHandle TSRMLS_CC) == FAILURE) return false; + + // make sure the path name is stored in the handle (@todo: is this necessary? do we need the copy?) + if (!fileHandle.opened_path) fileHandle.opened_path = zend_string_copy(_path); - // make sure the path name is stored in the handle - if (!fileHandle.opened_path) fileHandle.opened_path = estrdup(_path); - // we need temporary compiler options CompilerOptions options(ZEND_COMPILE_DEFAULT TSRMLS_CC); - + // create the opcodes - _opcodes = new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE TSRMLS_CC) TSRMLS_CC); + _opcodes.reset(new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE TSRMLS_CC) TSRMLS_CC)); // close the file handle zend_destroy_file_handle(&fileHandle TSRMLS_CC); - + // done return _opcodes->valid(); } @@ -99,13 +89,13 @@ bool File::exists() { // it is of course not valid if the path could not be resolved if (!_path) return false; - + // if we have valid opcodes, we're sure that it exists if (_opcodes && _opcodes->valid()) return true; - + // retrieve stats struct stat buf; - return stat(_path, &buf) == 0; + return stat(ZSTR_VAL(_path), &buf) == 0; } /** @@ -122,7 +112,7 @@ bool File::valid() * Execute the file * @return Value */ -Value File::execute() +Value File::execute() { // do we already have the opcodes? if (_opcodes) return _opcodes->execute(); @@ -130,12 +120,9 @@ Value File::execute() // try compiling the file if (!compile()) return nullptr; - // we need the tsrm_ls variable (@todo would it be better if this was a member?) - TSRMLS_FETCH(); - // add the entry to the list of included files - zend_hash_add_empty_element(&EG(included_files), _path, ::strlen(_path) + 1); - + zend_hash_add_empty_element(&EG(included_files), _path); + // execute the opcodes return _opcodes->execute(); } @@ -144,16 +131,16 @@ Value File::execute() * Execute a file only once * @return Value */ -Value File::once() +Value File::once() { // skip if the path is invalid if (!_path) return nullptr; // we need the tsrm_ls variable (@todo would it be better if this was a member?) TSRMLS_FETCH(); - + // check if this file was already included - if (zend_hash_exists(&EG(included_files), _path, ::strlen(_path) + 1)) return nullptr; + if (zend_hash_exists(&EG(included_files), _path)) return nullptr; // execute the file return execute(); diff --git a/zend/global.cpp b/zend/global.cpp index 7eea8e7..e7fef53 100644 --- a/zend/global.cpp +++ b/zend/global.cpp @@ -13,6 +13,67 @@ */ namespace Php { +/** + * Move constructor + * @param global + */ +Global::Global(Global &&global) _NOEXCEPT : + Value(std::move(global)), + _name(global._name), + _exists(global._exists) +{ + // remove from other global to avoid double free + global._name = nullptr; +} + +/** + * Constructor for non-existing var + * + * @param name Name for the variable that does not exist + */ +Global::Global(const char *name) : + Value(), + _name(zend_string_init(name, ::strlen(name), 1)), + _exists(false) {} + +/** + * Alternative constructor for non-existing var + * @param name + */ +Global::Global(const std::string &name) : + Value(), + _name(zend_string_init(name.data(), name.size(), 1)), + _exists(false) {} + +/** + * Constructor to wrap zval for existing global bar + * @param name + * @param val + */ +Global::Global(const char *name, struct _zval_struct *val) : + Value(val, true), + _name(zend_string_init(name, ::strlen(name), 1)), + _exists(true) {} + +/** + * Alternative constructor to wrap zval + * @param name + * @param val + */ +Global::Global(const std::string &name, struct _zval_struct *val) : + Value(val, true), + _name(zend_string_init(name.data(), name.size(), 1)), + _exists(true) {} + +/** + * Destructor + */ +Global::~Global() +{ + // release the string + if (_name) zend_string_release(_name); +} + /** * Function that is called when the value is updated * @return Value @@ -21,19 +82,19 @@ Global &Global::update() { // skip if the variable already exists if (_exists) return *this; - + // we need the TSRMLS variable TSRMLS_FETCH(); // add the variable to the globals - zend_hash_add(EG(active_symbol_table), _name.c_str(), _name.size()+1, &_val, sizeof(zval*), NULL); + zend_hash_add(EG(current_execute_data)->symbol_table, _name, _val); // add one extra reference because the variable now is a global var too - Z_ADDREF_P(_val); + Z_TRY_ADDREF_P(_val); // remember that the variable now exists _exists = true; - + // done return *this; } diff --git a/zend/globals.cpp b/zend/globals.cpp index 5299c90..ea4e65e 100644 --- a/zend/globals.cpp +++ b/zend/globals.cpp @@ -36,14 +36,14 @@ Globals &GLOBALS = Globals::instance(); */ Global Globals::operator[](const char *name) { - // pointer to a zval - zval **varvalue; - // we need the TSRMLS variable TSRMLS_FETCH(); - + + // retrieve the variable (if it exists) + auto *varvalue = zend_hash_find(&EG(symbol_table), zend_string_init(name, ::strlen(name), 0)); + // check if the variable already exists - if (zend_hash_find(&EG(symbol_table), name, ::strlen(name)+1, (void**)&varvalue) == FAILURE) + if (!varvalue) { // the variable does not already exist, return a global object // that will automatically set the value when it is updated @@ -53,7 +53,7 @@ Global Globals::operator[](const char *name) { // we are in the happy situation that the variable exists, we turn // this value into a reference value, and return that - return Global(name, *varvalue); + return Global(name, varvalue); } } @@ -64,14 +64,14 @@ Global Globals::operator[](const char *name) */ Global Globals::operator[](const std::string &name) { - // pointer to a zval - zval **varvalue; - // we need the TSRMLS variable TSRMLS_FETCH(); - + + // retrieve the variable (if it exists) + auto *varvalue = zend_hash_find(&EG(symbol_table), zend_string_init(name.data(), name.size(), 0)); + // check if the variable already exists - if (zend_hash_find(&EG(symbol_table), name.c_str(), name.size()+1, (void**)&varvalue) == FAILURE) + if (!varvalue) { // the variable does not already exist, return a global object // that will automatically set the value when it is updated @@ -81,7 +81,7 @@ Global Globals::operator[](const std::string &name) { // we are in the happy situation that the variable exists, we turn // this value into a reference value, and return that - return Global(name, *varvalue); + return Global(name, varvalue); } } diff --git a/zend/ini.cpp b/zend/ini.cpp index 889e388..849f6c4 100644 --- a/zend/ini.cpp +++ b/zend/ini.cpp @@ -17,35 +17,20 @@ namespace Php { * @param zend_ini_entry *ini_entry, int module_number * @param int module_number */ -void Ini::fill(zend_ini_entry *ini_entry, int module_number) +void Ini::fill(zend_ini_entry_def *ini_entry, int module_number) { - ini_entry->module_number = module_number; - ini_entry->modifiable = static_cast(this->_place); - ini_entry->name = const_cast(this->_name.c_str()); - ini_entry->name_length = this->_name.size()+1; - ini_entry->on_modify = OnUpdateString; - ini_entry->mh_arg1 = nullptr; + ini_entry->modifiable = static_cast(_place); + ini_entry->name = _name.data(); + ini_entry->on_modify = OnUpdateString; + ini_entry->mh_arg1 = nullptr; #ifdef ZTS - ini_entry->mh_arg2 = (void *) &phpcpp_globals_id; + ini_entry->mh_arg2 = (void *) &phpcpp_globals_id; #else - ini_entry->mh_arg2 = (void *) &phpcpp_globals; + ini_entry->mh_arg2 = (void *) &phpcpp_globals; #endif - ini_entry->mh_arg3 = nullptr; - ini_entry->value = const_cast(this->_value.c_str()); - ini_entry->value_length = this->_value.size(); - if( this->_orig_empty) - { - ini_entry->orig_value = nullptr; - ini_entry->orig_value_length = 0; - } - else - { - ini_entry->orig_value = const_cast(this->_orig.c_str()); - ini_entry->orig_value_length = this->_orig.size(); - } - ini_entry->orig_modifiable = 0; - ini_entry->modified = 0; - ini_entry->displayer = nullptr; + ini_entry->mh_arg3 = nullptr; + ini_entry->value = _value.data(); + ini_entry->displayer = nullptr; } diff --git a/zend/iteratorimpl.cpp b/zend/iteratorimpl.cpp index 9d57df7..4861e9e 100644 --- a/zend/iteratorimpl.cpp +++ b/zend/iteratorimpl.cpp @@ -49,21 +49,22 @@ int IteratorImpl::valid(zend_object_iterator *iter TSRMLS_DC) /** * Fetch the current item - * @param iter - * @param data - * @param tsrm_ls + * + * @param iter The iterator to retrieve the value from + * @param tsrm_ls Thread safety variable + * @return The current value of the iterator */ -void IteratorImpl::current(zend_object_iterator *iter, zval ***data TSRMLS_DC) +zval *IteratorImpl::current(zend_object_iterator *iter TSRMLS_DC) { // get the actual iterator - IteratorImpl *iterator = self(iter); + auto *iterator = self(iter); // retrieve the value (and store it in a member so that it is not // destructed when the function returns) iterator->_current = iterator->current(); - // copy the value - *data = &iterator->_current._val; + // return the value + return iterator->_current._val; } /** diff --git a/zend/iteratorimpl.h b/zend/iteratorimpl.h index 5286309..2199e6f 100644 --- a/zend/iteratorimpl.h +++ b/zend/iteratorimpl.h @@ -114,11 +114,12 @@ private: /** * Fetch the current item - * @param iter - * @param data - * @param tsrm_ls + * + * @param iter The iterator used to retrieve the value from + * @param tsrm_ls Thread safety variable + * @return The current value of the iterator */ - static void current(zend_object_iterator *iter, zval ***data TSRMLS_DC); + static zval *current(zend_object_iterator *iter TSRMLS_DC); /** * Fetch the key for the current element (optional, may be NULL). The key diff --git a/zend/object.cpp b/zend/object.cpp index 0f6307e..e2345c1 100644 --- a/zend/object.cpp +++ b/zend/object.cpp @@ -13,7 +13,7 @@ namespace Php { /** * Constructor to create a new instance of a builtin class - * + * * @param name Name of the class to instantiate * @param base The C++ object to wrap */ @@ -36,26 +36,26 @@ Object::Object(const char *name, Base *base) : Value() // here because this function is called from C++ context, and zend_error() // would cause a longjmp() which does not clean up C++ objects created // by the extension). - auto *entry = zend_fetch_class(name, ::strlen(name), ZEND_FETCH_CLASS_SILENT TSRMLS_CC); + auto *entry = zend_fetch_class(zend_string_init(name, ::strlen(name), 0), ZEND_FETCH_CLASS_SILENT TSRMLS_CC); if (!entry) throw FatalError(std::string("Unknown class name ") + name); // construct an implementation (this will also set the implementation // member in the base object), this is a self-destructing object that // will be destructed when the last reference to it has been removed, // we already set the reference to zero - new ObjectImpl(entry, base, 0 TSRMLS_CC); + new ObjectImpl(entry, base, ClassImpl::objectHandlers(entry), 0 TSRMLS_CC); // now we can store it operator=(Value(base)); // install the object handlers - Z_OBJVAL_P(_val).handlers = ClassImpl::objectHandlers(entry); + Z_OBJ_P(_val)->handlers = ClassImpl::objectHandlers(entry); } } /** * Constructor in case the class entry is already known - * + * * @param entry Class entry * @param base The C++ object to wrap */ @@ -76,7 +76,7 @@ Object::Object(zend_class_entry *entry, Base *base) : Value() // member in the base object), this is a self-destructing object that // will be destructed when the last reference to it has been removed, // we already set the reference to zero - new ObjectImpl(entry, base, ClassImpl::objectHandlers(entry) 0 TSRMLS_CC); + new ObjectImpl(entry, base, ClassImpl::objectHandlers(entry), 0 TSRMLS_CC); // now we can store it operator=(Value(base)); @@ -92,12 +92,12 @@ Object::Object(const Value &value) : Value() { // when a string is passed in, we are going to make a new instance of the // passed in string - if (value.isString()) + if (value.isString()) { // instantiate the object if (instantiate(value)) call("__construct"); } - else + else { // this simply copies the other object operator=(value); @@ -118,23 +118,23 @@ bool Object::instantiate(const char *name) // here because this function is called from C++ context, and zend_error() // would cause a longjmp() which does not clean up C++ objects created // by the extension). - auto *entry = zend_fetch_class(name, ::strlen(name), ZEND_FETCH_CLASS_SILENT TSRMLS_CC); + auto *entry = zend_fetch_class(zend_string_init(name, ::strlen(name), 0), ZEND_FETCH_CLASS_SILENT TSRMLS_CC); if (!entry) throw FatalError(std::string("Unknown class name ") + name); // initiate the zval (which was already allocated in the base constructor) object_init_ex(_val, entry); - + // @todo should we call methods like allocating hashtables, copying and // initializing properties, et cetera????? In all example you always // see such complicated and next-to-impossible-to-understand // sequences of functions being called, but this object_init_ex // also seems to work... - - // @todo is this a memory leak? the base class first initializes a stdClass, + + // @todo is this a memory leak? the base class first initializes a stdClass, // and then we overwrite it with a specific class - + // return whether there is a __construct function - return zend_hash_exists(&entry->function_table, "__construct", 12); + return zend_hash_exists(&entry->function_table, zend_string_init("__construct", 12, 1)); } /** diff --git a/zend/objectimpl.h b/zend/objectimpl.h index b014bb1..ed48e26 100644 --- a/zend/objectimpl.h +++ b/zend/objectimpl.h @@ -77,7 +77,7 @@ public: _mixed->php.handlers = handlers; // set the initial refcount (if it is different than one, because one is the default) - if (refcount != 1) php()->gc.refcount = refcount; + if (refcount != 1) GC_REFCOUNT(php()) = refcount; // the object may remember that we are its implementation object base->_impl = this; diff --git a/zend/super.cpp b/zend/super.cpp index ea690fe..02a1e46 100644 --- a/zend/super.cpp +++ b/zend/super.cpp @@ -32,10 +32,16 @@ Value Super::value() TSRMLS_FETCH(); // call zend_is_auto_global to ensure that the just-in-time globals are loaded - if (_name) { zend_is_auto_global(_name, ::strlen(_name) TSRMLS_CC); _name = nullptr; } - + if (_name) { + // make the variable an auto global + zend_is_auto_global(zend_string_init(_name, ::strlen(_name), 1) TSRMLS_CC); + + // reset because we only need to do this once + _name = nullptr; + } + // create a value object that wraps around the actual zval - return Value(PG(http_globals)[_index]); + return &PG(http_globals)[_index]; } /** diff --git a/zend/value.cpp b/zend/value.cpp index d22830a..40308f4 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -34,31 +34,24 @@ namespace Php { /** * Constructor (value = NULL) */ -Value::Value() +Value::Value() : _val(new zval) { // create a null zval - MAKE_STD_ZVAL(_val); ZVAL_NULL(_val); } /** * Constructor for null ptr */ -Value::Value(std::nullptr_t value) -{ - // create a null zval - MAKE_STD_ZVAL(_val); - ZVAL_NULL(_val); -} +Value::Value(std::nullptr_t value) : Value() {} /** * Constructor based on integer value * @param value */ -Value::Value(int16_t value) +Value::Value(int16_t value) : _val(new zval) { // create an integer zval - MAKE_STD_ZVAL(_val); ZVAL_LONG(_val, value); } @@ -66,10 +59,9 @@ Value::Value(int16_t value) * Constructor based on integer value * @param value */ -Value::Value(int32_t value) +Value::Value(int32_t value) : _val(new zval) { // create an integer zval - MAKE_STD_ZVAL(_val); ZVAL_LONG(_val, value); } @@ -77,10 +69,9 @@ Value::Value(int32_t value) * Constructor based on int64_t value * @param value */ -Value::Value(int64_t value) +Value::Value(int64_t value) : _val(new zval) { // create an integer zval - MAKE_STD_ZVAL(_val); ZVAL_LONG(_val, value); } @@ -88,10 +79,9 @@ Value::Value(int64_t value) * Constructor based on boolean value * @param value */ -Value::Value(bool value) +Value::Value(bool value) : _val(new zval) { // create a boolean zval - MAKE_STD_ZVAL(_val); ZVAL_BOOL(_val, value); } @@ -99,22 +89,20 @@ Value::Value(bool value) * Constructor based on single character * @param value */ -Value::Value(char value) +Value::Value(char value) : _val(new zval) { // create a string zval - MAKE_STD_ZVAL(_val); - ZVAL_STRINGL(_val, &value, 1, 1); + ZVAL_STRINGL(_val, &value, 1); } /** * Constructor based on string value * @param value */ -Value::Value(const std::string &value) +Value::Value(const std::string &value) : _val(new zval) { // create a string zval - MAKE_STD_ZVAL(_val); - ZVAL_STRINGL(_val, value.c_str(), value.size(), 1); + ZVAL_STRINGL(_val, value.c_str(), value.size()); } /** @@ -122,16 +110,13 @@ Value::Value(const std::string &value) * @param value * @param size */ -Value::Value(const char *value, int size) +Value::Value(const char *value, int size) : _val(new zval) { - // allocate the zval - MAKE_STD_ZVAL(_val); - // is there a value? if (value) { // create a string zval - ZVAL_STRINGL(_val, value, size < 0 ? ::strlen(value) : size, 1); + ZVAL_STRINGL(_val, value, size < 0 ? ::strlen(value) : size); } else { @@ -144,10 +129,9 @@ Value::Value(const char *value, int size) * Constructor based on decimal value * @param value */ -Value::Value(double value) +Value::Value(double value) : _val(new zval) { // create a double zval - MAKE_STD_ZVAL(_val); ZVAL_DOUBLE(_val, value); } @@ -158,13 +142,16 @@ Value::Value(double value) */ Value::Value(struct _zval_struct *val, bool ref) : _val(val) { + // not refcounted? then there is nothing we can here + if (!Z_REFCOUNTED_P(_val)) return; + // if the variable is not already a reference, and it has more than one // variable pointing to it, we should seperate it so that any changes // we're going to make will not change the other variable if (ref && Z_REFCOUNT_P(_val) > 1) { // separate the zval - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); } // we see ourselves as reference too @@ -174,7 +161,7 @@ Value::Value(struct _zval_struct *val, bool ref) : _val(val) if (!ref || Z_ISREF_P(_val)) return; // make this a reference - Z_SET_ISREF_P(_val); + ZVAL_MAKE_REF(_val); } /** @@ -193,34 +180,19 @@ Value::Value(const Base *object) // do we have a handle? if (!impl) throw FatalError("Assigning an unassigned object to a variable"); - // make a regular zval, and set it to an object - MAKE_STD_ZVAL(_val); - Z_TYPE_P(_val) = IS_OBJECT; - Z_OBJ_HANDLE_P(_val) = impl->handle(); - - // we need the tsrm_ls variable - TSRMLS_FETCH(); - - // we have to lookup the object in the object-table - zend_object_store_bucket *obj_bucket = &EG(objects_store).object_buckets[impl->handle()]; - - // there is one more reference to the object - obj_bucket->bucket.obj.refcount += 1; + // allocate variable and set it to an object + _val = new zval; + Z_TYPE_INFO_P(_val) = IS_OBJECT; - // this is copy-pasted from zend_objects.c - and it is necessary too! - if (!obj_bucket->bucket.obj.handlers) obj_bucket->bucket.obj.handlers = ClassImpl::objectHandlers(impl->php()->ce); - - // store the handlers in the zval too (cast is necessary for php 5.3) - Z_OBJ_HT_P(_val) = (zend_object_handlers*)obj_bucket->bucket.obj.handlers; + // increase refcount + GC_REFCOUNT(impl->php())++; } /** * Wrap around a php.ini value * @param value */ -Value::Value(const IniValue &value) : Value((const char *)value) -{ -} +Value::Value(const IniValue &value) : Value((const char *)value) {} /** * Copy constructor @@ -228,57 +200,22 @@ Value::Value(const IniValue &value) : Value((const char *)value) */ Value::Value(const Value &that) { - // is the other variable a reference? - if (Z_ISREF_P(that._val)) + // is the other variable a reference? or is this a simple scalar? + if (Z_ISREF_P(that._val) || !Z_REFCOUNTED_P(that._val)) { // because this is supposed to be a COPY, we can not add ourselves // to the variable but have to allocate a new variable - ALLOC_ZVAL(_val); - INIT_PZVAL_COPY(_val, that._val); - - // we have to call the copy constructor to copy the entire other zval - zval_copy_ctor(_val); + _val = new zval; + ZVAL_DUP(_val, that._val); } else { // simply use the same zval _val = that._val; - } - - // that zval has one more reference - Z_ADDREF_P(_val); - -// Below the old implementation - I thought really hard about it and I though -// it was a correct and very smart implementation. However, it does not work -// when you swap two variables. I changed it to the implementation above, but -// maybe that implementation introduces other bugs??? Let's keep the old -// implementation for a while in this file, but commented out -// -// // how many references does the other object have? -// if (Z_REFCOUNT_P(that._val) > 1 && !Z_ISREF_P(that._val)) -// { -// // there are already multiple variables linked to this value, and it -// // is not a reference. this implies that we can not turn this variable -// // into a reference, otherwise strange things could happen, we're going -// // to create a new zval -// ALLOC_ZVAL(_val); -// INIT_PZVAL_COPY(_val, that._val); -// zval_copy_ctor(_val); -// } -// else -// { -// // simply use the same zval -// _val = that._val; -// } -// -// // the other object only has one variable using it, or it is already -// // a variable by reference, we can safely add one more reference to it -// // and make it a variable by reference if it was not already a ref -// Z_ADDREF_P(_val); -// -// // make reference -// Z_SET_ISREF_P(_val); + // and increment the reference count + Z_ADDREF_P(_val); + } } /** @@ -299,14 +236,23 @@ Value::~Value() // ignore if moved if (!_val) return; - // if there were two references or less, we're going to remove a reference - // and only one reference will remain, the object will then impossible be - // a reference - if (Z_REFCOUNT_P(_val) <= 2) Z_UNSET_ISREF_P(_val); + // are we not a refcounted variable? + if (!Z_REFCOUNTED_P(_val)) + { + // we can simply delete it + delete _val; + } + else + { + // if there were two references or less, we're going to remove a reference + // and only one reference will remain, the object will then impossible be + // a reference + if (Z_REFCOUNT_P(_val) <= 2) ZVAL_UNREF(_val); - // destruct the zval (this function will decrement the reference counter, - // and only destruct if there are no other references left) - zval_ptr_dtor(&_val); + // destruct the zval (this function will decrement the reference counter, + // and only destruct if there are no other references left) + zval_ptr_dtor(_val); + } } /** @@ -317,7 +263,7 @@ Value::~Value() * deallocate the zval structure. This is used for functions that have to * return a zval pointer, that would otherwise be deallocated the moment * the function returns. - * + * * @param keeprefcount * @return zval */ @@ -331,12 +277,12 @@ zval *Value::detach(bool keeprefcount) // reset internal object _val = nullptr; - + // we're ready if we should keep the refcounter if (keeprefcount) return result; // decrement reference counter - Z_DELREF_P(result); + Z_TRY_DELREF_P(result); // done return result; @@ -348,6 +294,10 @@ zval *Value::detach(bool keeprefcount) */ int Value::refcount() const { + // are we not a refcounted variable? + if (!Z_REFCOUNTED_P(_val)) return 0; + + // we are, retrieve the count return Z_REFCOUNT_P(_val); } @@ -375,7 +325,7 @@ Value &Value::operator=(Value &&value) _NOEXCEPT *_val = *value._val; // restore reference and refcount setting - Z_SET_ISREF_TO_P(_val, true); + ZVAL_MAKE_REF(_val); Z_SET_REFCOUNT_P(_val, refcount); // how many references did the old variable have? @@ -398,7 +348,7 @@ Value &Value::operator=(Value &&value) _NOEXCEPT // the last and only reference to the other object was // removed, we no longer need it - FREE_ZVAL(value._val); + delete value._val; // the other object is no longer valid value._val = nullptr; @@ -408,7 +358,7 @@ Value &Value::operator=(Value &&value) _NOEXCEPT { // destruct the zval (this function will decrement the reference counter, // and only destruct if there are no other references left) - if (_val) zval_ptr_dtor(&_val); + if (_val) zval_ptr_dtor(_val); // just copy the zval completely _val = value._val; @@ -447,20 +397,20 @@ Value &Value::operator=(const Value &value) zval_copy_ctor(_val); // restore refcount and reference setting - Z_SET_ISREF_TO_P(_val, true); + ZVAL_MAKE_REF(_val); Z_SET_REFCOUNT_P(_val, refcount); } else { // destruct the zval (this function will decrement the reference counter, // and only destruct if there are no other references left) - zval_ptr_dtor(&_val); + zval_ptr_dtor(_val); // just copy the zval, and the refcounter _val = value._val; // and we have one more reference - Z_ADDREF_P(_val); + Z_TRY_ADDREF_P(_val); } // update the object @@ -476,7 +426,7 @@ Value &Value::operator=(const Value &value) Value &Value::operator=(std::nullptr_t value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); @@ -496,7 +446,7 @@ Value &Value::operator=(std::nullptr_t value) Value &Value::operator=(int16_t value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); @@ -516,7 +466,7 @@ Value &Value::operator=(int16_t value) Value &Value::operator=(int32_t value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); @@ -536,7 +486,7 @@ Value &Value::operator=(int32_t value) Value &Value::operator=(int64_t value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); @@ -556,7 +506,7 @@ Value &Value::operator=(int64_t value) Value &Value::operator=(bool value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); @@ -576,13 +526,13 @@ Value &Value::operator=(bool value) Value &Value::operator=(char value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); // set new value - ZVAL_STRINGL(_val, &value, 1, 1); + ZVAL_STRINGL(_val, &value, 1); // update the object return *this; @@ -596,13 +546,13 @@ Value &Value::operator=(char value) Value &Value::operator=(const std::string &value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); // set new value - ZVAL_STRINGL(_val, value.c_str(), value.size(), 1); + ZVAL_STRINGL(_val, value.c_str(), value.size()); // update the object return *this; @@ -616,13 +566,13 @@ Value &Value::operator=(const std::string &value) Value &Value::operator=(const char *value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); // set new value - ZVAL_STRING(_val, value, 1); + ZVAL_STRINGL(_val, value, ::strlen(value)); // update the object return *this; @@ -636,7 +586,7 @@ Value &Value::operator=(const char *value) Value &Value::operator=(double value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // deallocate current zval (without cleaning the zval structure) zval_dtor(_val); @@ -844,6 +794,9 @@ Value Value::operator()() const */ bool Value::isCallable(const char *name) { + // this only makes sense if we are an object + if (!isObject()) return false; + // wrap the name in a Php::Value object to get a zval Value method(name); @@ -851,7 +804,7 @@ bool Value::isCallable(const char *name) TSRMLS_FETCH(); // ask zend nicely whether the function is callable - return zend_is_callable_ex(method._val, _val, IS_CALLABLE_CHECK_NO_ACCESS, nullptr, nullptr, nullptr, nullptr TSRMLS_CC); + return zend_is_callable_ex(method._val, Z_OBJ_P(_val), IS_CALLABLE_CHECK_NO_ACCESS, nullptr, nullptr, nullptr TSRMLS_CC); } /** @@ -880,25 +833,25 @@ Value Value::call(const char *name) * Helper function that runs the actual call * @param object The object to call it on * @param method The function or method to call - * @param args Number of arguments - * @param params The parameters + * @param argc Number of arguments + * @param argv The parameters * @return Value */ -static Value do_exec(zval *const *object, zval *method, int argc, zval ***params) +static Value do_exec(const zval *object, zval *method, int argc, zval *argv) { // the return zval - zval *retval = nullptr; + zval retval; // we need the tsrm_ls variable TSRMLS_FETCH(); // the current exception - zval *oldException = EG(exception); + // zend_object *oldException = EG(exception); // call the function // we're casting the const away here, object is only const so we can call this method // from const methods after all.. - if (call_user_function_ex(CG(function_table), (zval**) object, method, &retval, argc, params, 1, NULL TSRMLS_CC) != SUCCESS) + if (call_user_function_ex(CG(function_table), (zval*) object, method, &retval, argc, argv, 1, nullptr TSRMLS_CC) != SUCCESS) { // throw an exception, the function does not exist throw Exception("Invalid call to "+Value(method).stringValue()); @@ -910,14 +863,15 @@ static Value do_exec(zval *const *object, zval *method, int argc, zval ***params { // was an exception thrown inside the function? In that case we throw a C++ new exception // to give the C++ code the chance to catch it - if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC); - + // @todo: make OrigException except a zend_object instance + // if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC); + // leap out if nothing was returned - if (!retval) return nullptr; - + if (Z_ISUNDEF(retval)) return nullptr; + // wrap the retval in a value - Php::Value result(retval); - + Php::Value result(&retval); + // destruct the retval (this just decrements the refcounter, which is ok, because // it is already wrapped in a Php::Value so still has 1 reference) zval_ptr_dtor(&retval); @@ -933,8 +887,14 @@ static Value do_exec(zval *const *object, zval *method, int argc, zval ***params * @param argv The parameters * @return Value */ -Value Value::exec(int argc, zval ***params) const +Value Value::exec(int argc, Value *argv) const { + // array of zvals to execute + zval params[argc]; + + // convert all the values + for(unsigned i=0; i < argc; i++) { params[i] = *argv[i]._val; } + // call helper function return do_exec(nullptr, _val, argc, params); } @@ -946,13 +906,19 @@ Value Value::exec(int argc, zval ***params) const * @param argv The parameters * @return Value */ -Value Value::exec(const char *name, int argc, struct _zval_struct ***params) const +Value Value::exec(const char *name, int argc, Value *argv) const { // wrap the name in a Php::Value object to get a zval Value method(name); + // array of zvals to execute + zval params[argc]; + + // convert all the values + for(unsigned i=0; i < argc; i++) { params[i] = *argv[i]._val; } + // call helper function - return do_exec(&_val, method._val, argc, params); + return do_exec(_val, method._val, argc, params); } /** @@ -962,13 +928,19 @@ Value Value::exec(const char *name, int argc, struct _zval_struct ***params) con * @param argv The parameters * @return Value */ -Value Value::exec(const char *name, int argc, struct _zval_struct ***params) +Value Value::exec(const char *name, int argc, Value *argv) { // wrap the name in a Php::Value object to get a zval Value method(name); + // array of zvals to execute + zval params[argc]; + + // convert all the values + for(unsigned i=0; i < argc; i++) { params[i] = *argv[i]._val; } + // call helper function - return do_exec(&_val, method._val, argc, params); + return do_exec(_val, method._val, argc, params); } /** @@ -1031,24 +1003,29 @@ Value &Value::setType(Type type) if (this->type() == type) return *this; // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // run the conversion, when it fails we throw a fatal error which will // in the end result in a zend_error() call. This FatalError class is necessary // because a direct call to zend_error() will do a longjmp() which may not // clean up the C++ objects created by the extension switch (type) { - case Type::Null: convert_to_null(_val); break; - case Type::Numeric: convert_to_long(_val); break; - case Type::Float: convert_to_double(_val); break; - case Type::Bool: convert_to_boolean(_val); break; - case Type::Array: convert_to_array(_val); break; - case Type::Object: convert_to_object(_val); break; - case Type::String: convert_to_string(_val); break; - case Type::Resource: throw FatalError("Resource types can not be handled by the PHP-CPP library"); break; - case Type::Constant: throw FatalError("Constant types can not be assigned to a PHP-CPP library variable"); break; - case Type::ConstantArray: throw FatalError("Constant types can not be assigned to a PHP-CPP library variable"); break; - case Type::Callable: throw FatalError("Callable types can not be assigned to a PHP-CPP library variable"); break; + case Type::Undefined: throw FatalError{ "Cannot make a variable undefined" }; break; + case Type::Null: convert_to_null(_val); break; + case Type::Numeric: convert_to_long(_val); break; + case Type::Float: convert_to_double(_val); break; + case Type::Bool: convert_to_boolean(_val); break; + case Type::False: convert_to_boolean(_val); ZVAL_FALSE(_val); break; + case Type::True: convert_to_boolean(_val); ZVAL_TRUE(_val); break; + case Type::Array: convert_to_array(_val); break; + case Type::Object: convert_to_object(_val); break; + case Type::String: convert_to_string(_val); break; + case Type::Resource: throw FatalError{ "Resource types can not be handled by the PHP-CPP library" }; break; + case Type::Constant: throw FatalError{ "Constant types can not be assigned to a PHP-CPP library variable" }; break; + case Type::ConstantAST: throw FatalError{ "Constant types can not be assigned to a PHP-CPP library variable" }; break; + case Type::Callable: throw FatalError{ "Callable types can not be assigned to a PHP-CPP library variable" }; break; + case Type::Reference: throw FatalError{ "Reference types cannot be assigned to a PHP-CPP library variable" }; break; + } // done @@ -1081,9 +1058,6 @@ zend_class_entry *Value::classEntry(bool allowString) const // is this an object if (isObject()) { - // should have a class entry - if (!HAS_CLASS_ENTRY(*_val)) return nullptr; - // class entry can be easily found return Z_OBJCE_P(_val); } @@ -1092,14 +1066,8 @@ zend_class_entry *Value::classEntry(bool allowString) const // the value is not an object, is this allowed? if (!allowString || !isString()) return nullptr; - // temporary variable - zend_class_entry **ce; - // find the class entry - if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &ce TSRMLS_CC) == FAILURE) return nullptr; - - // found the entry - return *ce; + return zend_lookup_class(Z_STR_P(_val) TSRMLS_CC); } } @@ -1123,20 +1091,14 @@ bool Value::instanceOf(const char *classname, size_t size, bool allowString) con zend_class_entry *this_ce = classEntry(allowString); if (!this_ce) return false; - // class entry of the parameter - zend_class_entry **ce; - // now we can look up the actual class - // the signature of zend_lookup_class_ex is slightly different since 5.4 - // TODO The signature of this changed once again as of 5.6! -#if PHP_VERSION_ID >= 50400 - if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false; -#else - if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false; -#endif + auto *ce = zend_lookup_class_ex(zend_string_init(classname, size, 1), nullptr, 0 TSRMLS_CC); + + // no such class, then we are not instanceof + if (!ce) return false; // check if this is a subclass - return instanceof_function(this_ce, *ce TSRMLS_CC); + return instanceof_function(this_ce, ce TSRMLS_CC); } /** @@ -1159,23 +1121,17 @@ bool Value::derivedFrom(const char *classname, size_t size, bool allowString) co zend_class_entry *this_ce = classEntry(allowString); if (!this_ce) return false; - // class entry of the parameter - zend_class_entry **ce; - // now we can look up the actual class - // the signature of zend_lookup_class_ex is slightly different since 5.4 - // TODO The signature of this changed once again as of 5.6! -#if PHP_VERSION_ID >= 50400 - if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false; -#else - if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false; -#endif + auto *ce = zend_lookup_class_ex(zend_string_init(classname, size, 1), nullptr, 0 TSRMLS_CC); + + // unable to find the class entry? + if (!ce) return false; // should not be identical, it must be a real derived object - if (this_ce == *ce) return false; + if (this_ce == ce) return false; // check if this is a subclass - return instanceof_function(this_ce, *ce TSRMLS_CC); + return instanceof_function(this_ce, ce TSRMLS_CC); } /** @@ -1185,16 +1141,10 @@ bool Value::derivedFrom(const char *classname, size_t size, bool allowString) co Value Value::clone() const { // the zval that will hold the copy - zval *copy; - - // allocate memory - ALLOC_ZVAL(copy); + auto *copy = new zval; // copy the data - INIT_PZVAL_COPY(copy, _val); - - // run the copy constructor to ensure that everything gets copied - zval_copy_ctor(copy); + ZVAL_DUP(copy, _val); // wrap it using the Value(zval*) constructor, this will +1 the refcount!!!! Value output(copy); @@ -1239,11 +1189,13 @@ int64_t Value::numericValue() const */ bool Value::boolValue() const { - // already a bool? - if (isBool()) return Z_BVAL_P(_val); - - // make a clone - return clone(Type::Bool).boolValue(); + // what variable type do we hold? + switch (type()) + { + case Type::True: return true; + case Type::False: return false; + default: return clone(Type::Bool).boolValue(); + } } /** @@ -1272,36 +1224,6 @@ char *Value::buffer() const return Z_STRVAL_P(_val); } -/** - * Reserve enough space - * @param size - * @return char* - */ -char *Value::reserve(size_t size) -{ - // must be a string - setType(Type::String); - - // is the current buffer too small? - if (Z_STRLEN_P(_val) < (int)size) - { - // is there already a buffer? - if (!Z_STRVAL_P(_val)) Z_STRVAL_P(_val) = (char *)emalloc(size+1); - - // reallocate an existing buffer - else Z_STRVAL_P(_val) = (char *)erealloc(Z_STRVAL_P(_val), size+1); - - // last byte should be zero - Z_STRVAL_P(_val)[size] = 0; - } - - // store size - Z_STRLEN_P(_val) = size; - - // done - return Z_STRVAL_P(_val); -} - /** * Get access to the raw buffer for read operations. Note that this * only works for string variables - other variables return nullptr. @@ -1413,7 +1335,7 @@ ValueIterator Value::createIterator(bool begin) const TSRMLS_FETCH(); // is a special iterator method defined in the class entry? - auto *entry = zend_get_class_entry(_val TSRMLS_CC); + auto *entry = Z_OBJCE_P(_val); // check if there is an iterator if (entry->get_iterator) @@ -1476,14 +1398,12 @@ bool Value::contains(int index) const { // if we're an object implementing ArrayAccess it makes sense for this method to work as well, so we call offsetExists if (isObject() && instanceOf("ArrayAccess")) return call("offsetExists", index).boolValue(); + // must be an array else if (!isArray()) return false; - // unused variable - zval **result; - // check if this index is already in the array - return zend_hash_index_find(Z_ARRVAL_P(_val), index, (void**)&result) != FAILURE; + return zend_hash_index_find(Z_ARRVAL_P(_val), index) != nullptr; } /** @@ -1497,14 +1417,11 @@ bool Value::contains(const char *key, int size) const // calculate size if (size < 0) size = ::strlen(key); - // unused variable - zval **result; - // deal with arrays if (isArray()) { // check if index is already in the array - return zend_hash_find(Z_ARRVAL_P(_val), key, size+1, (void **)&result) != FAILURE; + return zend_hash_find(Z_ARRVAL_P(_val), zend_string_init(key, size, 1)) != nullptr; } else if (isObject()) { @@ -1513,7 +1430,7 @@ bool Value::contains(const char *key, int size) const // retrieve the object pointer and check whether the property we are trying to retrieve // is marked as private/protected (cast necessary for php 5.3) - if (zend_check_property_access(zend_objects_get_address(_val TSRMLS_CC), const_cast(key), size TSRMLS_CC) == FAILURE) return false; + if (zend_check_property_access(Z_OBJ_P(_val), zend_string_init(key, size, 1) TSRMLS_CC) == FAILURE) return false; // check if the 'has_property' method is available for this object auto *has_property = Z_OBJ_HT_P(_val)->has_property; @@ -1526,11 +1443,7 @@ bool Value::contains(const char *key, int size) const // call the has_property() method (0 means: check whether property exists and is not NULL, // this is not really what we want, but the closest to the possible values of that parameter) -#if PHP_VERSION_ID >= 50400 return has_property(_val, property._val, 0, nullptr TSRMLS_CC); -#else - return has_property(_val, property._val, 0 TSRMLS_CC); -#endif } else { @@ -1549,19 +1462,19 @@ Value Value::get(int index) const // if we're an actual normal array we just use the zend_hash_index_find method if (isArray()) { - // zval to retrieve - zval **result; + // retrieve the value + auto *result = zend_hash_index_find(Z_ARRVAL_P(_val), index); - // check if index is in the array - if (zend_hash_index_find(Z_ARRVAL_P(_val), index, (void **)&result) == FAILURE) return Value(); + // did the offset exist? + if (!result) return Type::Undefined; // wrap the value - return Value(*result); + return result; } // if we're an object implementing ArrayAccess it makes sense for this method to work as well, so we call offsetGet else if (isObject() && instanceOf("ArrayAccess")) return call("offsetGet", index); // if we're neither we return an empty value - else return Value(); + else return Type::Undefined; } /** @@ -1581,14 +1494,8 @@ Value Value::get(const char *key, int size) const // are we in an object or an array? if (isArray()) { - // the result value - zval **result; - - // check if this index is already in the array, otherwise we return NULL - if (zend_hash_find(Z_ARRVAL_P(_val), key, size + 1, (void **)&result) == FAILURE) return Value(); - - // wrap the value - return Value(*result); + // find the result and wrap it in a value + return Value{ zend_hash_find(Z_ARRVAL_P(_val), zend_string_init(key, size, 1)) }; } else { @@ -1598,8 +1505,11 @@ Value Value::get(const char *key, int size) const // we need the tsrm_ls variable TSRMLS_FETCH(); - // read the property (cast necessary for php 5.3) - zval *property = zend_read_property(nullptr, _val, const_cast(key), size, 0 TSRMLS_CC); + // temporary value for holding any error + zval rv; + + // read the property + zval *property = zend_read_property(nullptr, _val, key, size, 0, &rv TSRMLS_CC); // wrap in value return Value(property); @@ -1616,13 +1526,13 @@ Value Value::get(const char *key, int size) const void Value::setRaw(int index, const Value &value) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // add the value (this will decrement refcount on any current variable) add_index_zval(_val, index, value._val); // the variable has one more reference (the array entry) - Z_ADDREF_P(value._val); + Z_TRY_ADDREF_P(value._val); } /** @@ -1634,13 +1544,13 @@ void Value::setRaw(int index, const Value &value) void Value::set(int index, const Value &value) { // the current value - zval **current; + zval *current; // check if this index is already in the array, otherwise we return NULL - if (isArray() && zend_hash_index_find(Z_ARRVAL_P(_val), index, (void **)¤t) != FAILURE) + if (isArray() && (current = zend_hash_index_find(Z_ARRVAL_P(_val), index))) { // skip if nothing is going to change - if (value._val == *current) return; + if (value._val == current) return; } // must be an array @@ -1665,24 +1575,24 @@ void Value::setRaw(const char *key, int size, const Value &value) if (isObject()) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // we need the tsrm_ls variable TSRMLS_FETCH(); - // update the property (cast necessary for php 5.3) - zend_update_property(nullptr, _val, const_cast(key), size, value._val TSRMLS_CC); + // update the property + zend_update_property(nullptr, _val, key, size, value._val TSRMLS_CC); } else { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // add the value (this will reduce the refcount of the current value) add_assoc_zval_ex(_val, key, size+1, value._val); // the variable has one more reference (the array entry) - Z_ADDREF_P(value._val); + Z_TRY_ADDREF_P(value._val); } } @@ -1696,13 +1606,13 @@ void Value::setRaw(const char *key, int size, const Value &value) void Value::set(const char *key, int size, const Value &value) { // the current value - zval **current; + zval *current; // check if this index is already in the array, otherwise we return NULL - if (isArray() && zend_hash_find(Z_ARRVAL_P(_val), key, size + 1, (void **)¤t) != FAILURE) + if (isArray() && (current = zend_hash_find(Z_ARRVAL_P(_val), zend_string_init(key, size, 1)))) { // skip if nothing is going to change - if (value._val == *current) return; + if (value._val == current) return; } // this should be an object or an array @@ -1722,7 +1632,7 @@ void Value::unset(int index) if (!isArray()) return; // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // remove the index zend_hash_index_del(Z_ARRVAL_P(_val), index); @@ -1739,7 +1649,7 @@ void Value::unset(const char *key, int size) if (isObject()) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // we need the tsrm_ls variable TSRMLS_FETCH(); @@ -1750,10 +1660,10 @@ void Value::unset(const char *key, int size) else if (isArray()) { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_IF_NOT_REF(&_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // remove the index - zend_hash_del(Z_ARRVAL_P(_val), key, size + 1); + zend_hash_del(Z_ARRVAL_P(_val), zend_string_init(key, size, 1)); } } -- cgit v1.2.3