diff options
Diffstat (limited to 'zend/value.cpp')
-rw-r--r-- | zend/value.cpp | 460 |
1 files changed, 185 insertions, 275 deletions
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(); + } } /** @@ -1273,36 +1225,6 @@ char *Value::buffer() const } /** - * 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<char *>(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<char *>(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<char *>(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)); } } |