diff options
author | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-04-06 21:53:24 +0200 |
---|---|---|
committer | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-04-06 21:53:24 +0200 |
commit | 35fd3ccbeb4def71b4d8a59dfbb5c31201b099b9 (patch) | |
tree | 915223360aed4743aa6127fde4836aa413a260e5 /zend/value.cpp | |
parent | da4710512865e6816585ac4ab8edab2fa125e2d8 (diff) |
renamed src directory to zend directory, disabled TSRM debug code
Diffstat (limited to 'zend/value.cpp')
-rw-r--r-- | zend/value.cpp | 1911 |
1 files changed, 1911 insertions, 0 deletions
diff --git a/zend/value.cpp b/zend/value.cpp new file mode 100644 index 0000000..2ca1585 --- /dev/null +++ b/zend/value.cpp @@ -0,0 +1,1911 @@ +/** + * Value.cpp + * + * Implementation for the Value class, which wraps a PHP userspace + * value (a 'zval' in Zend's terminology) into a C++ object + * + * Reminder for the implementer: + * + * A 'zval' is an object that represents a _value_ in the PHP user space, + * and thus not a variable. A 'value' or 'zval' can be used by many + * different variables at the same time. The 'refcount' property of the + * zval holds the number of variables ($a, $b, $c, et cetera) that are + * all linked to the same value. With this system, PHP can implement copy + * on write behavior. + * + * Next to the refcount, the zval also holds a is_ref property, which is + * set to true if all variables linked to the value are references of each + * other. Thus is $a, $b and $c all point to the same variable, and is_ref + * is set to true, changing the value means that the $a, $b and $c value + * are all updated. If is_res was false, a change to $a would not mean a + * change to $b, and the zval should have been copied first. + * + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013, 2014 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Constructor (value = NULL) + */ +Value::Value() +{ + // 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); +} + +/** + * Constructor based on integer value + * @param value + */ +Value::Value(int16_t value) +{ + // create an integer zval + MAKE_STD_ZVAL(_val); + ZVAL_LONG(_val, value); +} + +/** + * Constructor based on integer value + * @param value + */ +Value::Value(int32_t value) +{ + // create an integer zval + MAKE_STD_ZVAL(_val); + ZVAL_LONG(_val, value); +} + +/** + * Constructor based on int64_t value + * @param value + */ +Value::Value(int64_t value) +{ + // create an integer zval + MAKE_STD_ZVAL(_val); + ZVAL_LONG(_val, value); +} + +/** + * Constructor based on boolean value + * @param value + */ +Value::Value(bool value) +{ + // create a boolean zval + MAKE_STD_ZVAL(_val); + ZVAL_BOOL(_val, value); +} + +/** + * Constructor based on single character + * @param value + */ +Value::Value(char value) +{ + // create a string zval + MAKE_STD_ZVAL(_val); + ZVAL_STRINGL(_val, &value, 1, 1); +} + +/** + * Constructor based on string value + * @param value + */ +Value::Value(const std::string &value) +{ + // create a string zval + MAKE_STD_ZVAL(_val); + ZVAL_STRINGL(_val, value.c_str(), value.size(), 1); +} + +/** + * Constructor based on a byte array + * @param value + * @param size + */ +Value::Value(const char *value, int size) +{ + // create a string zval + MAKE_STD_ZVAL(_val); + ZVAL_STRINGL(_val, value, size < 0 ? strlen(value) : size, 1); +} + +/** + * Constructor based on decimal value + * @param value + */ +Value::Value(double value) +{ + // create a double zval + MAKE_STD_ZVAL(_val); + ZVAL_DOUBLE(_val, value); +} + +/** + * Wrap object around zval + * @param zval Value to wrap + * @param ref Force this to be a reference + */ +Value::Value(struct _zval_struct *val, bool ref) +{ + // just copy the zval into this object + _val = val; + + // 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); + } + + // we see ourselves as reference too + Z_ADDREF_P(_val); + + // we're ready if we do not have to force it as a reference + if (!ref || Z_ISREF_P(_val)) return; + + // make this a reference + Z_SET_ISREF_P(_val); +} + +/** + * Wrap around an object + * @param object + */ +Value::Value(const Base *object) +{ + // there are two options: the object was constructed from user space, + // and is already linked to a handle, or it was constructed from C++ + // space, and no handle does yet exist, find the implementation object + auto *impl = object->implementation(); + + // do we have a handle? + if (!impl) throw Php::Exception("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()]; + + // this is copy-pasted from zend_objects.c - and it is necessary too! + if (!obj_bucket->bucket.obj.handlers) obj_bucket->bucket.obj.handlers = &std_object_handlers; + + // 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; + + // run the copy constructor + zval_copy_ctor(_val); +} + +/** + * Copy constructor + * @param value + */ +Value::Value(const Value &that) +{ + // is the other variable a reference? + if (Z_ISREF_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); + } + 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); +} + +/** + * Move constructor + * @param value + */ +Value::Value(Value &&that) : _val(that._val) +{ + // clear the other object + that._val = nullptr; +} + +/** + * Destructor + */ +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); + + // destruct the zval (this function will decrement the reference counter, + // and only destruct if there are no other references left) + zval_ptr_dtor(&_val); +} + +/** + * Detach the zval + * + * This will unlink the zval internal structure from the Value object, + * so that the destructor will not reduce the number of references and/or + * 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. + * + * @return zval + */ +zval *Value::detach() +{ + // leap out if already detached + if (!_val) return nullptr; + + // copy return value + zval *result = _val; + + // decrement reference counter + Z_DELREF_P(_val); + + // reset internal object + _val = nullptr; + + // done + return result; +} + +/** + * Attach a different zval + * + * This will first detach the current zval, and link the Value object to + * a different zval. + * + * @param val + */ +void Value::attach(struct _zval_struct *val) +{ + // detach first + if (_val) detach(); + + // store the zval + _val = val; + + // add one more reference + Z_ADDREF_P(_val); +} + +/** + * Attach a different zval + * + * This will first detach the current zval, and link the Value object to + * a new zval + * + * @param hashtable + */ +void Value::attach(struct _hashtable *hashtable) +{ + // detach first + if (_val) detach(); + + // construct a new zval + MAKE_STD_ZVAL(_val); + + // store pointer to the hashtable, and mark the zval as an array + Z_ARRVAL_P(_val) = hashtable; + Z_TYPE_P(_val) = IS_ARRAY; + + // add a reference + Z_ADDREF_P(_val); +} + +/** + * Retrieve the refcount + * @return int + */ +int Value::refcount() +{ + return Z_REFCOUNT_P(_val); +} + +/** + * Move operator + * @param value + * @return Value + */ +Value &Value::operator=(Value &&value) +{ + // skip self assignment + if (this == &value) return *this; + + // is the object a reference? + if (Z_ISREF_P(_val)) + { + // @todo difference if the other object is a reference or not? + + // the current object is a reference, this means that we should + // keep the zval object, and copy the other value into it, get + // the current refcount + int refcount = Z_REFCOUNT_P(_val); + + // make the copy + *_val = *value._val; + + // restore reference and refcount setting + Z_SET_ISREF_TO_P(_val, true); + Z_SET_REFCOUNT_P(_val, refcount); + + // how many references did the old variable have? + if (Z_REFCOUNT_P(value._val) > 1) + { + // the other object already had multiple references, this + // implies that many other PHP variables are also referring + // to it, and we still need to store its contents, with one + // reference less + Z_DELREF_P(value._val); + + // and we need to run the copy constructor on the current + // value, because we're making a deep copy + zval_copy_ctor(_val); + } + else + { + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // the last and only reference to the other object was + // removed, we no longer need it + FREE_ZVAL(value._val); + + // the other object is no longer valid + value._val = nullptr; + } + } + 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); + + // just copy the zval completely + _val = value._val; + + // the other object is no longer valid + value._val = nullptr; + } + + // done + return *this; +} + +/** + * Assignment operator + * @param value + * @return Value + */ +Value &Value::operator=(const Value &value) +{ + // skip self assignment + if (this == &value) return *this; + + // is the object a reference? + if (Z_ISREF_P(_val)) + { + // the current object is a reference, this means that we should + // keep the zval object, and copy the other value into it, get + // the current refcount + int refcount = Z_REFCOUNT_P(_val); + + // clean up the current zval (but keep the zval structure) + zval_dtor(_val); + + // make the copy + *_val = *value._val; + zval_copy_ctor(_val); + + // restore refcount and reference setting + Z_SET_ISREF_TO_P(_val, true); + 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); + + // just copy the zval, and the refcounter + _val = value._val; + + // and we have one more reference + Z_ADDREF_P(_val); + } + + // update the object + return *this; +} + + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // change to null value + ZVAL_NULL(_val); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_LONG(_val, value); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_LONG(_val, value); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_LONG(_val, value); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_BOOL(_val, value); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_STRINGL(_val, &value, 1, 1); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_STRINGL(_val, value.c_str(), value.size(), 1); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_STRING(_val, value, 1); + + // update the object + return *this; +} + +/** + * Assignment operator + * @param value + * @return 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); + + // deallocate current zval (without cleaning the zval structure) + zval_dtor(_val); + + // set new value + ZVAL_DOUBLE(_val, value); + + // update the object + return *this; +} + +/** + * Add a value to the object + * @param value + * @return Value + */ +Value &Value::operator+=(const Value &value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(int16_t value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(int32_t value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(int64_t value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(bool value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(char value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(const std::string &value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(const char *value) { return Arithmetic<std::plus>(this).assign(value); } +Value &Value::operator+=(double value) { return Arithmetic<std::plus>(this).assign(value); } + +/** + * Subtract a value from the object + * @param value + * @return Value + */ +Value &Value::operator-=(const Value &value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(int16_t value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(int32_t value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(int64_t value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(bool value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(char value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(const std::string &value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(const char *value) { return Arithmetic<std::minus>(this).assign(value); } +Value &Value::operator-=(double value) { return Arithmetic<std::minus>(this).assign(value); } + +/** + * Multiply the object with a certain value + * @param value + * @return Value + */ +Value &Value::operator*=(const Value &value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(int16_t value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(int32_t value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(int64_t value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(bool value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(char value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(const std::string &value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(const char *value) { return Arithmetic<std::multiplies>(this).assign(value); } +Value &Value::operator*=(double value) { return Arithmetic<std::multiplies>(this).assign(value); } + +/** + * Divide the object with a certain value + * @param value + * @return Value + */ +Value &Value::operator/=(const Value &value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(int16_t value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(int32_t value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(int64_t value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(bool value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(char value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(const std::string &value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(const char *value) { return Arithmetic<std::divides>(this).assign(value); } +Value &Value::operator/=(double value) { return Arithmetic<std::divides>(this).assign(value); } + +/** + * Divide the object with a certain value and get the rest + * Note that this does not use the Arithmetic object, because no conversion between floats is necessary + * @param value + * @return Value + */ +Value &Value::operator%=(const Value &value) { return operator=(numericValue() % value.numericValue()); } +Value &Value::operator%=(int16_t value) { return operator=(numericValue() % value); } +Value &Value::operator%=(int32_t value) { return operator=(numericValue() % value); } +Value &Value::operator%=(int64_t value) { return operator=(numericValue() % value); } +Value &Value::operator%=(bool value) { return operator=(numericValue() % value); } +Value &Value::operator%=(char value) { return operator=(numericValue() % value); } +Value &Value::operator%=(const std::string &value) { return operator=(numericValue() % atoi(value.c_str())); } +Value &Value::operator%=(const char *value) { return operator=(numericValue() % atoi(value)); } +Value &Value::operator%=(double value) { return operator=(numericValue() % (int)value); } + +/** + * Assignment operator + * @param value + * @return Value + */ +Value Value::operator+(const Value &value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(int16_t value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(int32_t value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(int64_t value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(bool value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(char value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(const std::string &value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(const char *value) { return Arithmetic<std::plus>(this).apply(value); } +Value Value::operator+(double value) { return Arithmetic<std::plus>(this).apply(value); } + +/** + * Subtraction operator + * @param value + * @return Value + */ +Value Value::operator-(const Value &value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(int16_t value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(int32_t value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(int64_t value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(bool value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(char value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(const std::string &value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(const char *value) { return Arithmetic<std::minus>(this).apply(value); } +Value Value::operator-(double value) { return Arithmetic<std::minus>(this).apply(value); } + +/** + * Multiplication operator + * @param value + * @return Value + */ +Value Value::operator*(const Value &value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(int16_t value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(int32_t value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(int64_t value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(bool value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(char value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(const std::string &value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(const char *value) { return Arithmetic<std::multiplies>(this).apply(value); } +Value Value::operator*(double value) { return Arithmetic<std::multiplies>(this).apply(value); } + +/** + * Division operator + * @param value + * @return Value + */ +Value Value::operator/(const Value &value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(int16_t value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(int32_t value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(int64_t value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(bool value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(char value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(const std::string &value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(const char *value) { return Arithmetic<std::divides>(this).apply(value); } +Value Value::operator/(double value) { return Arithmetic<std::divides>(this).apply(value); } + +/** + * Modulus operator + * @param value + * @return Value + */ +Value Value::operator%(const Value &value) { return Value(numericValue() % value.numericValue()); } +Value Value::operator%(int16_t value) { return Value(numericValue() % value); } +Value Value::operator%(int32_t value) { return Value(numericValue() % value); } +Value Value::operator%(int64_t value) { return Value(numericValue() % value); } +Value Value::operator%(bool value) { return Value(numericValue() % value); } +Value Value::operator%(char value) { return Value(numericValue() % value); } +Value Value::operator%(const std::string &value) { return Value(numericValue() % atoi(value.c_str())); } +Value Value::operator%(const char *value) { return Value(numericValue() % atoi(value)); } +Value Value::operator%(double value) { return Value(numericValue() % (int)value); } + +/** + * Call the function in PHP + * We have ten variants of this function, depending on the number of parameters + * This call operator is only useful when the variable represents a callable + * @param p0-p10 Parameters of the function to be called. + * @return Value + */ +Value Value::operator()() const +{ + // call with zero parameters + return exec(0, NULL); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @return Value + */ +Value Value::operator()(Value p0) const +{ + // array of parameters + zval **params[1] = { &p0._val }; + + // call the function + return exec(1, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1) const +{ + // array of parameters + zval **params[2] = { &p0._val, &p1._val }; + + // call the function + return exec(2, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2) const +{ + // array of parameters + zval **params[3] = { &p0._val, &p1._val, &p2._val }; + + // call the function + return exec(3, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2, Value p3) const +{ + // array of parameters + zval **params[4] = { &p0._val, &p1._val, &p2._val, &p3._val }; + + // call the function + return exec(4, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4) const +{ + // array of parameters + zval **params[5] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val }; + + // call the function + return exec(5, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5) const +{ + // array of parameters + zval **params[6] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val }; + + // call the function + return exec(6, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6) const +{ + // array of parameters + zval **params[7] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val }; + + // call the function + return exec(7, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @param p7 The eighth parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7) const +{ + // array of parameters + zval **params[8] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val }; + + // call the function + return exec(8, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @param p7 The eighth parameter + * @param p8 The ninth parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8) const +{ + // array of parameters + zval **params[9] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val }; + + // call the function + return exec(9, params); +} + +/** + * Call the function - if the variable holds a callable thing + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @param p7 The eighth parameter + * @param p8 The ninth parameter + * @param p9 The tenth parameter + * @return Value + */ +Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8, Value p9) const +{ + // array of parameters + zval **params[10] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val, &p9._val }; + + // call the function + return exec(10, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @return Value + */ +Value Value::call(const char *name) +{ + // call with zero parameters + return exec(name, 0, NULL); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @return Value + */ +Value Value::call(const char *name, Value p0) +{ + // array of parameters + zval **params[] = { &p0._val }; + + // call with zero parameters + return exec(name, 1, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val }; + + // call with zero parameters + return exec(name, 2, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val }; + + // call with zero parameters + return exec(name, 3, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val }; + + // call with zero parameters + return exec(name, 4, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val }; + + // call with zero parameters + return exec(name, 5, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val }; + + // call with zero parameters + return exec(name, 6, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val }; + + // call with zero parameters + return exec(name, 7, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @param p7 The eighth parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val }; + + // call with zero parameters + return exec(name, 8, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @param p7 The eighth parameter + * @param p8 The ninth parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val }; + + // call with zero parameters + return exec(name, 9, params); +} + +/** + * Call the method - if the variable holds an object with the given method + * @param name name of the method to call + * @param p0 The first parameter + * @param p1 The second parameter + * @param p2 The third parameter + * @param p3 The fourth parameter + * @param p4 The fifth parameter + * @param p5 The sixth parameter + * @param p6 The seventh parameter + * @param p7 The eighth parameter + * @param p8 The ninth parameter + * @param p9 The tenth parameter + * @return Value + */ +Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8, Value p9) +{ + // array of parameters + zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val, &p9._val }; + + // call with zero parameters + return exec(name, 10, params); +} + +/** + * 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 + * @return Value + */ +static Value do_exec(zval **object, zval *method, int argc, zval ***params) +{ + // the return zval + zval *retval = nullptr; + + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // the current exception + zval *oldException = EG(exception); + + // call the function + if (call_user_function_ex(CG(function_table), object, method, &retval, argc, params, 1, NULL TSRMLS_CC) != SUCCESS) + { + // throw an exception, the function does not exist + throw Exception("Invalid call to "+Value(method).stringValue()); + + // unreachable, but let's return at least something to prevent compiler warnings + return nullptr; + } + else + { + // 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); + + // no (additional) exception was thrown + return retval ? Value(retval) : nullptr; + } +} + +/** + * Call function with a number of parameters + * @param argc Number of parameters + * @param argv The parameters + * @return Value + */ +Value Value::exec(int argc, zval ***params) const +{ + // call helper function + return do_exec(nullptr, _val, argc, params); +} + +/** + * Call method with a number of parameters + * @param name Name of method to call + * @param argc Number of parameters + * @param argv The parameters + * @return Value + */ +Value Value::exec(const char *name, int argc, struct _zval_struct ***params) +{ + // wrap the name in a Php::Value object to get a zval + Value method(name); + + // call helper function + return do_exec(&_val, method._val, argc, params); +} + +/** + * The type of object + * @return Type + */ +Type Value::type() const +{ + // return regular type + return (Type)Z_TYPE_P(_val); +} + +/** + * Change the internal type + * @param type + * @return Value + */ +Value &Value::setType(Type type) +{ + // skip if nothing changes + 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); + + // run the conversion + 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 Php::Exception("Resource types can not be handled by the PHP-CPP library"); break; + case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break; + case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break; + case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break; + } + + // done + return *this; +} + +/** + * Check if the variable holds something that is callable + * @return bool + */ +bool Value::isCallable() const +{ + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // we can not rely on the type, because strings can be callable as well + return zend_is_callable(_val, 0, NULL TSRMLS_CC); +} + +/** + * Make a clone of the type + * @return Value + */ +Value Value::clone() const +{ + // the zval that will hold the copy + zval *copy; + + // allocate memory + ALLOC_ZVAL(copy); + + // copy the data + INIT_PZVAL_COPY(copy, _val); + + // run the copy constructor to ensure that everything gets copied + zval_copy_ctor(copy); + + // done + return Value(copy); +} + +/** + * Clone the zval to a different type + * @param type + * @return Value + */ +Value Value::clone(Type type) const +{ + // regular clone if nothing changes + if (this->type() == type) return clone(); + + // make a clone + return clone().setType(type); +} + +/** + * Retrieve the value as integer + * @return long + */ +int64_t Value::numericValue() const +{ + // already a long? + if (isNumeric()) return Z_LVAL_P(_val); + + // make a clone + return clone(Type::Numeric).numericValue(); +} + +/** + * Retrieve the value as boolean + * @return bool + */ +bool Value::boolValue() const +{ + // already a bool? + if (isBool()) return Z_BVAL_P(_val); + + // make a clone + return clone(Type::Bool).boolValue(); +} + +/** + * Retrieve the value as string + * @return string + */ +std::string Value::stringValue() const +{ + // already a string? + if (isString()) return std::string(Z_STRVAL_P(_val), Z_STRLEN_P(_val)); + + // make a clone + return clone(Type::String).stringValue(); +} + +/** + * Access to the raw buffer + * @return char * + */ +char *Value::buffer() const +{ + // must be a string + if (!isString()) return nullptr; + + // already a string? + 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); +} + +/** + * Access to the raw buffer + * @return const char * + */ +const char *Value::rawValue() const +{ + // must be a string + if (isString()) return Z_STRVAL_P(_val); + + // make a clone and return that buffer + return clone(Type::String).rawValue(); +} + +/** + * Retrieve the value as decimal + * @return double + */ +double Value::floatValue() const +{ + // already a double + if (isFloat()) return Z_DVAL_P(_val); + + // make a clone + return clone(Type::Float).floatValue(); +} + +/** + * The number of members in case of an array or object + * @return int + */ +int Value::size() const +{ + // is it an array? + if (isArray()) + { + // get the number of elements + return zend_hash_num_elements(Z_ARRVAL_P(_val)); + } + + // or an object? + else if (isObject()) + { + // the count_elements member function should be defined + if (!Z_OBJ_HT_P(_val)->count_elements) return 0; + + // create a variable to hold the result + long result; + + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // call the function + return Z_OBJ_HT_P(_val)->count_elements(_val, &result TSRMLS_CC) == SUCCESS ? result : 0; + } + + // not an array, return string size if this is a string + else if (isString()) + { + // get string size + return Z_STRLEN_P(_val); + } + + // in all other situations, we convert the variable to a string + else + { + // make a copy + Value copy(*this); + + // convert the copy to a string + copy.setType(Type::String); + + // return the string size + return copy.size(); + } +} + +/** + * Convert the object to a map with string index and Php::Value value + * @return std::map + */ +std::map<std::string,Php::Value> Value::mapValue() const +{ + // result variable + std::map<std::string,Php::Value> result; + + // iterate over the object + for (auto &iter : *this) result[iter.first.rawValue()] = iter.second; + + // done + return result; +} + +/** + * Internal helper method to retrieve an iterator + * @param begin Should the iterator start at the begin + * @return iterator + */ +ValueIterator Value::createIterator(bool begin) const +{ + // check type + if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), begin)); + + // get access to the hast table + if (isObject()) + { + // we need the TSRMLS_CC variable + TSRMLS_FETCH(); + + // is a special iterator method defined in the class entry? + auto *entry = zend_get_class_entry(_val TSRMLS_CC); + + // check if there is an iterator + if (entry->get_iterator) + { + // the object implements Traversable interface, we have to use a + // special iterator to user that interface too + return ValueIterator(new TraverseIterator(_val, begin TSRMLS_CC)); + } + else + { + // construct a regular iterator + return ValueIterator(new HashIterator(Z_OBJ_HT_P(_val)->get_properties(_val TSRMLS_CC), begin)); + } + } + + // invalid + return ValueIterator(new InvalidIterator()); +} + +/** + * Return an iterator for iterating over the values + * This is only meaningful for Value objects that hold an array or an object + * @return iterator + */ +ValueIterator Value::begin() const +{ + return createIterator(true); +} + +/** + * Return an iterator for iterating over the values + * This is only meaningful for Value objects that hold an array or an object + * @return iterator + */ +ValueIterator Value::end() const +{ + return createIterator(false); +} + +/** + * Does the array contain a certain index? + * @param index + * @return bool + */ +bool Value::contains(int index) const +{ + // must be an array + 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; +} + +/** + * Does the array contain a certain key + * @param key + * @param size + * @return boolean + */ +bool Value::contains(const char *key, int size) const +{ + // calculate size + if (size < 0) size = strlen(key); + + // deal with arrays + if (isArray()) + { + // unused variable + zval **result; + + // check if index is already in the array + return zend_hash_find(Z_ARRVAL_P(_val), key, size+1, (void **)&result) != FAILURE; + } + else if (isObject()) + { + // we need the tsrmls_cc variable + TSRMLS_FETCH(); + + // retrieve the class entry + auto *entry = zend_get_class_entry(_val TSRMLS_CC); + + // read the property (cast necessary for php 5.3) + zval *property = zend_read_property(entry, _val, (char *)key, size, 0 TSRMLS_CC); + + // check if valid + return property != nullptr; + } + else + { + // scalar variable + return false; + } +} + +/** + * Get access to a certain array member + * @param index + * @return Value + */ +Value Value::get(int index) const +{ + // must be an array + if (!isArray()) return Value(); + + // zval to retrieve + zval **result; + + // check if index is in the array + if (zend_hash_index_find(Z_ARRVAL_P(_val), index, (void **)&result) == FAILURE) return Value(); + + // wrap the value + return Value(*result); +} + +/** + * Get access to a certain assoc member + * @param key + * @param size + * @return Value + */ +Value Value::get(const char *key, int size) const +{ + // must be an array + if (!isArray() && !isObject()) return Value(); + + // calculate size + if (size < 0) size = strlen(key); + + // 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); + } + else + { + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // retrieve the class entry + auto *entry = zend_get_class_entry(_val TSRMLS_CC); + + // read the property (case necessary for php 5.3) + zval *property = zend_read_property(entry, _val, (char *)key, size, 1 TSRMLS_CC); + + // wrap in value + return Value(property); + } +} + +/** + * Set a certain property without performing any checks + * This method can be used when it is already known that the object is an array + * @param index + * @param value + * @return Value + */ +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); + + // 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); +} + +/** + * Set a certain property + * @param index + * @param value + * @return Value + */ +void Value::set(int index, const Value &value) +{ + // the current value + 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) + { + // skip if nothing is going to change + if (value._val == *current) return; + } + + // must be an array + setType(Type::Array); + + // set property + setRaw(index, value); +} + +/** + * Set a certain property without running any checks + * @param key + * @param size + * @param value + */ +void Value::setRaw(const char *key, int size, const Value &value) +{ + // is this an object? + if (isObject()) + { + // if this is not a reference variable, we should detach it to implement copy on write + SEPARATE_ZVAL_IF_NOT_REF(&_val); + + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // retrieve the class entry + auto *entry = zend_get_class_entry(_val TSRMLS_CC); + + // update the property (cast necessary for php 5.3) + zend_update_property(entry, _val, (char *)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); + + // 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); + } +} + +/** + * Set a certain property + * @param key + * @param size + * @param value + * @return Value + */ +void Value::set(const char *key, int size, const Value &value) +{ + // the current value + 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) + { + // skip if nothing is going to change + if (value._val == *current) return; + } + + // this should be an object or an array + if (!isObject()) setType(Type::Array); + + // done + setRaw(key, size, value); +} + +/** + * Array access operator + * This can be used for accessing arrays + * @param index + * @return HashMember + */ +HashMember<int> Value::operator[](int index) +{ + return HashMember<int>(this, index); +} + +/** + * Array access operato + * This can be used for accessing associative arrays + * @param key + * @return HashMember + */ +HashMember<std::string> Value::operator[](const std::string &key) +{ + return HashMember<std::string>(this, key); +} + +/** + * Array access operator + * This can be used for accessing associative arrays + * @param key + * @return HashMember + */ +HashMember<std::string> Value::operator[](const char *key) +{ + return HashMember<std::string>(this, key); +} + +/** + * Retrieve the original implementation + * + * This only works for classes that were implemented using PHP-CPP, + * it returns nullptr for all other classes + * + * @return Base* + */ +Base *Value::implementation() const +{ + // must be an object + if (!isObject()) return nullptr; + + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // retrieve the mixed object that contains the base + return ObjectImpl::find(_val TSRMLS_CC)->object(); +} + +/** + * Custom output stream operator + * @param stream + * @param value + * @return ostream + */ +std::ostream &operator<<(std::ostream &stream, const Value &value) +{ + return stream << value.stringValue(); +} + +/** + * End of namespace + */ +} + |