summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/call.h49
-rw-r--r--include/hashmember.h119
-rw-r--r--include/hashparent.h39
-rw-r--r--include/namespace.h6
-rw-r--r--include/value.h99
-rw-r--r--zend/callable.cpp51
-rw-r--r--zend/classimpl.h2
-rw-r--r--zend/extensionimpl.cpp4
-rw-r--r--zend/globals.cpp2
-rw-r--r--zend/namespace.cpp8
-rw-r--r--zend/object.cpp4
-rw-r--r--zend/stringmember.h4
-rw-r--r--zend/super.cpp4
-rw-r--r--zend/value.cpp61
14 files changed, 400 insertions, 52 deletions
diff --git a/include/call.h b/include/call.h
index 3ad4776..9be5c91 100644
--- a/include/call.h
+++ b/include/call.h
@@ -44,17 +44,44 @@ Value call(const char *name, Params&&... params)
* make the code run on the highway), it is not expected that these functions
* are going to be used very often anyway.
*/
-inline Value array_keys(const Value &value) { return call("array_keys", value); }
-inline Value array_values(const Value &value) { return call("array_values", value); }
-inline Value echo(const char *input) { out << input; return nullptr; }
-inline Value echo(const std::string &input) { out << input; return nullptr; }
-inline Value empty(const Value &value) { return value.isNull() || !value.boolValue(); }
-inline Value empty(const HashMember<std::string> &member) { return !member.exists() || empty(member.value()); }
-inline Value empty(const HashMember<int> &member) { return !member.exists() || empty(member.value()); }
-inline Value ini_get(const Value &value) { return call("ini_get", value); }
-//inline Value isset(const Value &value) { return call("isset", value); }
-//inline Value isset(const HashMember<std::string> &member) { return member.exists() && isset(member.value()); }
-//inline Value isset(const HashMember<int> &member) { return member.exists() && isset(member.value()); }
+inline Value array_key_exists(const Value &key, const Value &array) { return array.contains(key); }
+inline Value array_key_exists(int key, const Value &array) { return array.contains(key); }
+inline Value array_key_exists(const char *key, const Value &array) { return array.contains(key); }
+inline Value array_key_exists(const std::string &key, const Value &array) { return array.contains(key); }
+inline Value array_keys(const Value &value) { return call("array_keys", value); }
+inline Value array_push(const Value &array, const Value &value) { return call("array_push", array, value); }
+inline Value array_values(const Value &value) { return call("array_values", value); }
+inline Value count(const Value &value) { return call("count", value); }
+inline Value echo(const char *input) { out << input; return nullptr; }
+inline Value echo(const std::string &input) { out << input; return nullptr; }
+inline Value empty(const Value &value) { return value.isNull() || !value.boolValue(); }
+inline Value empty(const HashMember<std::string> &member) { return !member.exists() || empty(member.value()); }
+inline Value empty(const HashMember<int> &member) { return !member.exists() || empty(member.value()); }
+inline Value ini_get(const Value &value) { return call("ini_get", value); }
+inline Value is_array(const Value &value) { return value.isArray(); }
+inline Value strlen(const Value &value) { return call("strlen", value); }
+inline void unset(const HashMember<std::string> &member) { member.unset(); }
+inline void unset(const HashMember<int> &member) { member.unset(); }
+inline void unset(const HashMember<Value> &member) { member.unset(); }
+
+/**
+ * The isset function conflicts with the 'isset' macro defined by the Zend engine
+ */
+#pragma push_macro("isset");
+#undef isset
+
+/**
+ * Define the isset function
+ */
+inline Value isset(const Value &value) { return call("isset", value); }
+inline Value isset(const HashMember<std::string> &member) { return member.exists() && isset(member.value()); }
+inline Value isset(const HashMember<int> &member) { return member.exists() && isset(member.value()); }
+inline Value isset(const HashMember<Value> &member) { return member.exists() && isset(member.value()); }
+
+/**
+ * Re-install the ISSET macro
+ */
+#pragma pop_macro("isset");
/**
* End of namespace
diff --git a/include/hashmember.h b/include/hashmember.h
index 9a2d5e8..6853228 100644
--- a/include/hashmember.h
+++ b/include/hashmember.h
@@ -381,7 +381,7 @@ public:
/**
* Check if a certain index exists in the array/object
- * @param key
+ * @param index
* @return bool
*/
virtual bool contains(int index) const override
@@ -389,6 +389,17 @@ public:
// object must exist, and the value must contain the key
return exists() && value().contains(index);
}
+
+ /**
+ * Check if a certain index exists in the array/object
+ * @param key
+ * @return bool
+ */
+ virtual bool contains(const Value &key) const override
+ {
+ // object must exist, and the value must contain the key
+ return exists() && value().contains(key);
+ }
/**
* Retrieve the value at a string index
@@ -417,6 +428,20 @@ public:
// ask the value
return value().get(index);
}
+
+ /**
+ * Retrieve the value at a variant index
+ * @param key
+ * @return Value
+ */
+ virtual Value get(const Value &key) const override
+ {
+ // return null if it does not exist
+ if (!exists()) return nullptr;
+
+ // ask the value
+ return value().get(key);
+ }
/**
* Overwrite the value at a certain string index
@@ -452,6 +477,97 @@ public:
_parent->set(_index, current);
}
+ /**
+ * Overwrite the value at a certain variant index
+ * @param key
+ * @param value
+ */
+ virtual void set(const Value &key, const Value &value) override
+ {
+ // get the current value
+ Value current(this->value());
+
+ // add the value
+ current[key] = value;
+
+ // pass this to the base
+ _parent->set(_index, current);
+ }
+
+ /**
+ * Unset the member
+ */
+ void unset() const
+ {
+ _parent->unset(_index);
+ }
+
+ /**
+ * Unset a member by its index
+ * @param index
+ */
+ virtual void unset(int index) override
+ {
+ // if the current property does not even exist, we do not have to add anything
+ if (!exists()) return;
+
+ // get the current value
+ Value current(this->value());
+
+ // skip if the property does not exist
+ if (!current.contains(index)) return;
+
+ // remove the index
+ current.unset(index);
+
+ // pass the new value to the base
+ _parent->set(_index, current);
+ }
+
+ /**
+ * Unset a member by its key
+ * @param key
+ */
+ virtual void unset(const std::string &key) override
+ {
+ // if the current property does not even exist, we do not have to add anything
+ if (!exists()) return;
+
+ // get the current value
+ Value current(this->value());
+
+ // skip if the property does not exist
+ if (!current.contains(key)) return;
+
+ // remove the index
+ current.unset(key);
+
+ // pass the new value to the base
+ _parent->set(_index, current);
+ }
+
+ /**
+ * Unset a member by its key
+ * @param key
+ */
+ virtual void unset(const Value &key) override
+ {
+ // if the current property does not even exist, we do not have to add anything
+ if (!exists()) return;
+
+ // get the current value
+ Value current(this->value());
+
+ // skip if the property does not exist
+ if (!current.contains(key)) return;
+
+ // remove the index
+ current.unset(key);
+
+ // pass the new value to the base
+ _parent->set(_index, current);
+ }
+
protected:
/**
* Protected copy constructor
@@ -483,6 +599,7 @@ private:
* Friend classes
*/
friend class HashMember<std::string>;
+ friend class HashMember<Value>;
friend class HashMember<int>;
friend class Base;
friend class Value;
diff --git a/include/hashparent.h b/include/hashparent.h
index 4c2ee68..5ba05bd 100644
--- a/include/hashparent.h
+++ b/include/hashparent.h
@@ -56,6 +56,13 @@ public:
virtual bool contains(int index) const = 0;
/**
+ * Check if a certain index exists in the array/object
+ * @param key
+ * @return bool
+ */
+ virtual bool contains(const Value &index) const = 0;
+
+ /**
* Retrieve the value at a string index
* @param key
* @return Value
@@ -68,6 +75,13 @@ public:
* @return Value
*/
virtual Value get(int index) const = 0;
+
+ /**
+ * Retrieve the value at a value index
+ * @param key
+ * @return Value
+ */
+ virtual Value get(const Value &key) const = 0;
/**
* Overwrite the value at a certain string index
@@ -83,6 +97,31 @@ public:
*/
virtual void set(int index, const Value &value) = 0;
+ /**
+ * Overwrite the value at a certain variant index
+ * @param key
+ * @param value
+ */
+ virtual void set(const Value &key, const Value &value) = 0;
+
+ /**
+ * Unset a member by its index
+ * @param index
+ */
+ virtual void unset(int index) = 0;
+
+ /**
+ * Unset a member by its key
+ * @param key
+ */
+ virtual void unset(const std::string &key) = 0;
+
+ /**
+ * Unset a member by its key
+ * @param key
+ */
+ virtual void unset(const Value &key) = 0;
+
};
/**
diff --git a/include/namespace.h b/include/namespace.h
index 468a423..1c9d457 100644
--- a/include/namespace.h
+++ b/include/namespace.h
@@ -225,7 +225,7 @@ public:
*
* @param callback
*/
- void apply(const std::function<void(const std::string &ns, Function &func)> &callback);
+ void functions(const std::function<void(const std::string &ns, Function &func)> &callback);
/**
* Apply a callback to each registered class
@@ -235,7 +235,7 @@ public:
*
* @param callback
*/
- void apply(const std::function<void(const std::string &ns, ClassBase &clss)> &callback);
+ void classes(const std::function<void(const std::string &ns, ClassBase &clss)> &callback);
/**
* Filling ini entries into external zend_ini_entry array
@@ -243,8 +243,6 @@ public:
*/
void fill_ini(_zend_ini_entry *ini_entries, int module_number);
-
-
};
/**
diff --git a/include/value.h b/include/value.h
index 95985d7..d1e6c8a 100644
--- a/include/value.h
+++ b/include/value.h
@@ -575,6 +575,18 @@ public:
}
/**
+ * Is a certain key set in the array, when that key is stored as value object
+ * @param key
+ * @return bool
+ */
+ virtual bool contains(const Value &value) const override
+ {
+ if (value.isNumeric()) return contains(value.numericValue());
+ if (value.isString()) return contains(value.rawValue(), value.size());
+ return contains(value.stringValue());
+ }
+
+ /**
* Cast to a number
* @return int32_t
*/
@@ -692,6 +704,18 @@ public:
}
/**
+ * Get access to a certain variant member
+ * @param key
+ * @return Value
+ */
+ virtual Value get(const Value &key) const override
+ {
+ if (key.isNumeric()) return get(key.numericValue());
+ if (key.isString()) return get(key.rawValue(), key.size());
+ return get(key.stringValue());
+ }
+
+ /**
* Set a certain property
* Calling this method will turn the value into an array
* @param index Index of the property to set
@@ -732,6 +756,61 @@ public:
}
/**
+ * Overwrite the value at a certain variant index
+ * @param key
+ * @param value
+ */
+ virtual void set(const Value &key, const Value &value) override
+ {
+ if (key.isNumeric()) return set(key.numericValue(), value);
+ if (key.isString()) return set(key.rawValue(), key.size(), value);
+ return set(key.stringValue(), value);
+ }
+
+ /**
+ * Unset a member by its index
+ * @param index
+ */
+ virtual void unset(int index) override;
+
+ /**
+ * Unset by key name and length of the key
+ * @param key
+ * @param size
+ */
+ void unset(const char *key, int size);
+
+ /**
+ * Unset by key name and length of the key
+ * @param key
+ * @param size
+ */
+ void unset(const char *key)
+ {
+ unset(key, strlen(key));
+ }
+
+ /**
+ * Unset a member by its key
+ * @param key
+ */
+ virtual void unset(const std::string &key) override
+ {
+ return unset(key.c_str(), key.size());
+ }
+
+ /**
+ * Unset a member by its key
+ * @param key
+ */
+ virtual void unset(const Value &key) override
+ {
+ if (key.isNumeric()) return unset(key.numericValue());
+ if (key.isString()) return unset(key.rawValue(), key.size());
+ return unset(key.stringValue());
+ }
+
+ /**
* Array access operator
* This can be used for accessing arrays
* @param index
@@ -787,6 +866,26 @@ public:
{
return get(key);
}
+
+ /**
+ * Index by other value object
+ * @param key
+ * @return HashMember<std::string>
+ */
+ HashMember<Value> operator[](const Value &key);
+
+ /**
+ * Index by other value object
+ * @param key
+ * @return HashMember<std::string>
+ */
+ Value operator[](const Value &key) const
+ {
+ if (key.isNumeric()) return get(key.numericValue());
+ if (key.isString()) return get(key.rawValue(), key.size());
+ return get(key.stringValue());
+ }
+
/**
* Call the function in PHP
diff --git a/zend/callable.cpp b/zend/callable.cpp
index 96f5f90..a85ab72 100644
--- a/zend/callable.cpp
+++ b/zend/callable.cpp
@@ -31,27 +31,40 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
// uncover the hidden pointer inside the function name
Callable *callable = HiddenPointer<Callable>(name);
- // construct parameters
- ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);
-
- // the function could throw an exception
- try
+ // check if sufficient parameters were passed (for some reason this check
+ // is not done by Zend, so we do it here ourselves)
+ if (ZEND_NUM_ARGS() < callable->_required)
{
- // get the result
- Value result(callable->invoke(params));
-
- // detach the zval (we don't want it to be destructed)
- zval *val = result.detach();
-
- // @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(val, 1, 0);
+ // PHP itself only generates a warning when this happens, so we do the same too
+ Php::warning << name << "() expects at least " << callable->_required << " parameters, " << ZEND_NUM_ARGS() << " given" << std::flush;
+
+ // and we return null
+ RETURN_NULL();
}
- catch (Exception &exception)
+ else
{
- // process the exception
- process(exception TSRMLS_CC);
+ // construct parameters
+ ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);
+
+ // the function could throw an exception
+ try
+ {
+ // get the result
+ Value result(callable->invoke(params));
+
+ // detach the zval (we don't want it to be destructed)
+ zval *val = result.detach();
+
+ // @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(val, 1, 0);
+ }
+ catch (Exception &exception)
+ {
+ // process the exception
+ process(exception TSRMLS_CC);
+ }
}
}
@@ -94,7 +107,7 @@ void Callable::initialize(zend_arg_info *info, const char *classname) const
// 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->_name_len = ::strlen(_ptr);
finfo->_class_name = classname;
// number of required arguments, and the expected return type
diff --git a/zend/classimpl.h b/zend/classimpl.h
index febdbca..ec28322 100644
--- a/zend/classimpl.h
+++ b/zend/classimpl.h
@@ -408,7 +408,7 @@ public:
void property(const char *name, bool value, int flags = Php::Public) { _members.push_back(std::make_shared<BoolMember> (name, value, flags & PropertyModifiers)); }
void property(const char *name, char value, int flags = Php::Public) { _members.push_back(std::make_shared<StringMember> (name, &value, 1, flags & PropertyModifiers)); }
void property(const char *name, const std::string &value, int flags = Php::Public) { _members.push_back(std::make_shared<StringMember> (name, value, flags & PropertyModifiers)); }
- void property(const char *name, const char *value, int flags = Php::Public) { _members.push_back(std::make_shared<StringMember> (name, value, strlen(value), flags & PropertyModifiers)); }
+ void property(const char *name, const char *value, int flags = Php::Public) { _members.push_back(std::make_shared<StringMember> (name, value, ::strlen(value), flags & PropertyModifiers)); }
void property(const char *name, double value, int flags = Php::Public) { _members.push_back(std::make_shared<FloatMember> (name, value, flags & PropertyModifiers)); }
/**
diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp
index 545b590..0308c63 100644
--- a/zend/extensionimpl.cpp
+++ b/zend/extensionimpl.cpp
@@ -273,7 +273,7 @@ zend_module_entry *ExtensionImpl::module()
int i = 0;
// apply a function to each function
- _data->apply([&i, entries](const std::string &prefix, Function &function) {
+ _data->functions([&i, entries](const std::string &prefix, Function &function) {
// initialize the function
function.initialize(prefix, &entries[i]);
@@ -302,7 +302,7 @@ zend_module_entry *ExtensionImpl::module()
void ExtensionImpl::initialize(TSRMLS_D)
{
// we need to register each class, find out all classes
- _data->apply([TSRMLS_C](const std::string &prefix, ClassBase &c) {
+ _data->classes([TSRMLS_C](const std::string &prefix, ClassBase &c) {
// forward to implementation class
c.implementation()->initialize(&c, prefix TSRMLS_CC);
diff --git a/zend/globals.cpp b/zend/globals.cpp
index 8132ef3..5299c90 100644
--- a/zend/globals.cpp
+++ b/zend/globals.cpp
@@ -43,7 +43,7 @@ Global Globals::operator[](const char *name)
TSRMLS_FETCH();
// check if the variable already exists
- if (zend_hash_find(&EG(symbol_table), name, strlen(name)+1, (void**)&varvalue) == FAILURE)
+ if (zend_hash_find(&EG(symbol_table), name, ::strlen(name)+1, (void**)&varvalue) == FAILURE)
{
// the variable does not already exist, return a global object
// that will automatically set the value when it is updated
diff --git a/zend/namespace.cpp b/zend/namespace.cpp
index e9ec631..8b97c52 100644
--- a/zend/namespace.cpp
+++ b/zend/namespace.cpp
@@ -85,13 +85,13 @@ Namespace &Namespace::add(const char *name, const native_callback_3 &function, c
*
* @param callback
*/
-void Namespace::apply(const std::function<void(const std::string &ns, Function &func)> &callback)
+void Namespace::functions(const std::function<void(const std::string &ns, Function &func)> &callback)
{
// loop through the functions, and apply the callback
for (auto &function : _functions) callback(_name, *function);
// loop through the other namespaces
- for (auto &ns : _namespaces) ns->apply([this, callback](const std::string &ns, Function &func) {
+ for (auto &ns : _namespaces) ns->functions([this, callback](const std::string &ns, Function &func) {
// if this is the root namespace, we don't have to change the prefix
if (_name.size() == 0) return callback(ns, func);
@@ -110,13 +110,13 @@ void Namespace::apply(const std::function<void(const std::string &ns, Function &
*
* @param callback
*/
-void Namespace::apply(const std::function<void(const std::string &ns, ClassBase &clss)> &callback)
+void Namespace::classes(const std::function<void(const std::string &ns, ClassBase &clss)> &callback)
{
// loop through the classes, and apply the callback
for (auto &c : _classes) callback(_name, *c);
// loop through the other namespaces
- for (auto &ns : _namespaces) ns->apply([this, callback](const std::string &ns, ClassBase &clss) {
+ for (auto &ns : _namespaces) ns->classes([this, callback](const std::string &ns, ClassBase &clss) {
// if this is the root namespace, we don't have to change the prefix
if (_name.size() == 0) return callback(ns, clss);
diff --git a/zend/object.cpp b/zend/object.cpp
index d7fc158..08553b5 100644
--- a/zend/object.cpp
+++ b/zend/object.cpp
@@ -33,7 +33,7 @@ Object::Object(const char *name, Base *base)
// this is a brand new object that should be allocated, the C++ instance
// is already there (created by the extension) but it is not yet stored
// in PHP, find out the classname first
- auto *entry = zend_fetch_class(name, strlen(name), 0 TSRMLS_CC);
+ auto *entry = zend_fetch_class(name, ::strlen(name), 0 TSRMLS_CC);
if (!entry) throw Php::Exception(std::string("Unknown class name ") + name);
// construct an implementation (this will also set the implementation
@@ -55,7 +55,7 @@ void Object::instantiate(const char *name)
TSRMLS_FETCH();
// convert the name into a class_entry
- auto *entry = zend_fetch_class(name, strlen(name), 0 TSRMLS_CC);
+ auto *entry = zend_fetch_class(name, ::strlen(name), 0 TSRMLS_CC);
if (!entry) throw Php::Exception(std::string("Unknown class name ") + name);
// initiate the zval (which was already allocated in the base constructor)
diff --git a/zend/stringmember.h b/zend/stringmember.h
index e58cd3d..05fdb11 100644
--- a/zend/stringmember.h
+++ b/zend/stringmember.h
@@ -1,7 +1,7 @@
/**
* StringMember.h
*
- * Implementation for a property that is initially set to a strnig value
+ * Implementation for a property that is initially set to a string value
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2013 Copernica BV
@@ -40,7 +40,7 @@ public:
* @param value
* @param flags
*/
- StringMember(const char *name, const char *value, int flags) : StringMember(name, value, strlen(value), flags) {}
+ StringMember(const char *name, const char *value, int flags) : StringMember(name, value, ::strlen(value), flags) {}
/**
* Constructor
diff --git a/zend/super.cpp b/zend/super.cpp
index 506d4a5..c25efeb 100644
--- a/zend/super.cpp
+++ b/zend/super.cpp
@@ -34,7 +34,7 @@ Value Super::operator[](const std::string &key)
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) { zend_is_auto_global(_name, ::strlen(_name) TSRMLS_CC); _name = nullptr; }
// create a value object that wraps around the actual zval
Value value(PG(http_globals)[_index]);
@@ -55,7 +55,7 @@ Value Super::operator[](const char *key)
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) { zend_is_auto_global(_name, ::strlen(_name) TSRMLS_CC); _name = nullptr; }
// create a value object that wraps around the actual zval
Value value(PG(http_globals)[_index]);
diff --git a/zend/value.cpp b/zend/value.cpp
index 2ca1585..dd371b9 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -126,7 +126,7 @@ 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);
+ ZVAL_STRINGL(_val, value, size < 0 ? ::strlen(value) : size, 1);
}
/**
@@ -1640,7 +1640,7 @@ bool Value::contains(int index) const
bool Value::contains(const char *key, int size) const
{
// calculate size
- if (size < 0) size = strlen(key);
+ if (size < 0) size = ::strlen(key);
// deal with arrays
if (isArray())
@@ -1704,7 +1704,7 @@ Value Value::get(const char *key, int size) const
if (!isArray() && !isObject()) return Value();
// calculate size
- if (size < 0) size = strlen(key);
+ if (size < 0) size = ::strlen(key);
// are we in an object or an array?
if (isArray())
@@ -1841,6 +1841,51 @@ void Value::set(const char *key, int size, const Value &value)
}
/**
+ * Unset a member by its index
+ * @param index
+ */
+void Value::unset(int index)
+{
+ // only necessary for arrays
+ 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);
+
+ // remove the index
+ zend_hash_index_del(Z_ARRVAL_P(_val), index);
+}
+
+/**
+ * Unset by key name and length of the key
+ * @param key
+ * @param size
+ */
+void Value::unset(const char *key, int size)
+{
+ // 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();
+
+ // in the zend header files, unsetting properties is redirected to setting it to null...
+ add_property_null_ex(_val, key, size TSRMLS_CC);
+ }
+ 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);
+
+ // remove the index
+ zend_hash_del(Z_ARRVAL_P(_val), key, size);
+ }
+}
+
+/**
* Array access operator
* This can be used for accessing arrays
* @param index
@@ -1852,6 +1897,16 @@ HashMember<int> Value::operator[](int index)
}
/**
+ * Index by other value object
+ * @param key
+ * @return HashMember<std::string>
+ */
+HashMember<Value> Value::operator[](const Value &key)
+{
+ return HashMember<Value>(this, key);
+}
+
+/**
* Array access operato
* This can be used for accessing associative arrays
* @param key