diff options
author | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-16 14:55:51 +0100 |
---|---|---|
committer | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-16 14:55:51 +0100 |
commit | ec6d84016bec41b05e275641f990831572171969 (patch) | |
tree | ff76497cf1a000d96892dd9b2ed2ab6144a3bf89 | |
parent | a83e9b3af70f35400ec5d62a4b400d55b05c3492 (diff) |
added Value::begin() and Value::end() methods to make it possible to iterate over a value
-rw-r--r-- | include/value.h | 21 | ||||
-rw-r--r-- | include/valueiterator.h | 129 | ||||
-rw-r--r-- | phpcpp.h | 1 | ||||
-rw-r--r-- | src/includes.h | 1 | ||||
-rw-r--r-- | src/value.cpp | 134 | ||||
-rw-r--r-- | src/valueiterator.cpp | 135 |
6 files changed, 325 insertions, 96 deletions
diff --git a/include/value.h b/include/value.h index 8fea5a6..94cc1cf 100644 --- a/include/value.h +++ b/include/value.h @@ -32,6 +32,7 @@ namespace Php { * Forward definitions */ class Base; +class ValueIterator; template <class Type> class HashMember; /** @@ -498,6 +499,25 @@ public: } /** + * Define the iterator type + */ + typedef ValueIterator iterator; + + /** + * Return an iterator for iterating over the values + * This is only meaningful for Value objects that hold an array or an object + * @return iterator + */ + iterator begin() const; + + /** + * Return an iterator for iterating over the values + * This is only meaningful for Value objects that hold an array or an object + * @return iterator + */ + iterator end() const; + + /** * The number of members in case of an array or object * @return int */ @@ -926,6 +946,7 @@ protected: friend class ClassBase; friend class Iterator; friend class Extension; + friend class ValueIterator; }; /** diff --git a/include/valueiterator.h b/include/valueiterator.h new file mode 100644 index 0000000..11c412e --- /dev/null +++ b/include/valueiterator.h @@ -0,0 +1,129 @@ +/** + * ValueIterator.h + * + * This is an internal helper class that is used when iterating over a + * Php::Value object - stl style. + * + * Thus, when you do c++ things like "for (auto &iter : value)", internally + * a ValueIterator object is being used. + * + * @author Emiel Bruijntjes + * @copyright 2014 Copernica BV + */ + +/** + * Forward declaration + */ +struct _hashtable; +struct bucket; + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class ValueIterator +{ +public: + /** + * Constructor + * @param hashtable The hashtable to iterate over + * @param first Should it start on the first position? + */ + ValueIterator(struct _hashtable *hashtable, bool first); + + /** + * Destructor + */ + virtual ~ValueIterator() {} + + /** + * Increment position + * @return ValueIterator + */ + ValueIterator &operator++(); + + /** + * Decrement position + * @return ValueIterator + */ + ValueIterator &operator--(); + + /** + * Compare with other iterator + * @param that + * @return bool + */ + bool operator==(const ValueIterator &that) const + { + return _position == that._position; + } + + /** + * Compare with other iterator + * @param that + * @return bool + */ + bool operator!=(const ValueIterator &that) const + { + return _position != that._position; + } + + /** + * Derefecence, this returns a std::pair with the current key and value + * @return std::pair + */ + const std::pair<Value,Value> &operator*() const + { + return _current; + } + + /** + * Dereference, this returns a std::pair with the current key and value + * @return std::pair + */ + const std::pair<Value,Value> *operator->() const + { + return &_current; + } + +private: + /** + * The hash table over which is being iterated + * @var HashTable + */ + struct _hashtable *_table = nullptr; + + /** + * The position in the hash table + * @var HashPosition + */ + struct bucket *_position = nullptr; + + /** + * The current key and value + * @var std::pair + */ + std::pair<Value,Value> _current; + + /** + * Read current key and value + * @return ValueIterator + */ + ValueIterator &read(); + + /** + * Invalidate the iterator + * @return ValueIterator + */ + ValueIterator &invalidate(); +}; + +/** + * End namespace + */ +} + @@ -28,6 +28,7 @@ #include <phpcpp/exception.h> #include <phpcpp/type.h> #include <phpcpp/value.h> +#include <phpcpp/valueiterator.h> #include <phpcpp/array.h> #include <phpcpp/object.h> #include <phpcpp/hiddenpointer.h> diff --git a/src/includes.h b/src/includes.h index 0c00df3..42b52ce 100644 --- a/src/includes.h +++ b/src/includes.h @@ -47,6 +47,7 @@ #include "../include/exception.h" #include "../include/type.h" #include "../include/value.h" +#include "../include/valueiterator.h" #include "../include/array.h" #include "../include/object.h" #include "../include/hiddenpointer.h" diff --git a/src/value.cpp b/src/value.cpp index 1579341..a041967 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -1550,106 +1550,48 @@ 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; +} +/** + * 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 +{ // check type - if (isArray()) - { - // get access to the hast table - HashTable *arr = Z_ARRVAL_P(_val); - - // reset iterator to beginning of the hash table - zend_hash_internal_pointer_reset(arr); - - // loop through the records - while (true) - { - // pointer that will be set to hash key and index - char *key; - unsigned long ind; - - // get current key - int hash_key_type = zend_hash_get_current_key(arr, &key, &ind, 0); - - // required variable - zval **value; - - // check the type - switch (hash_key_type) { - - // was it a string? - case HASH_KEY_IS_STRING: - - // retrieve data, and add to result - zend_hash_get_current_data(arr, (void **) &value); - result[key] = Value(*value); - break; - - // was it numeric? - case HASH_KEY_IS_LONG: - - // retrieve data, and add to result - zend_hash_get_current_data(arr, (void **) &value); - result[std::to_string(ind)] = Value(*value); - break; - - default: - - // we're ready - return result; - } - - // next iteration - zend_hash_move_forward(arr); - } - } - else if (isObject()) - { - // get access to the hast table - HashTable *arr = Z_OBJ_HT_P(_val)->get_properties(_val); - - // reset iterator to beginning of the hash table - zend_hash_internal_pointer_reset(arr); - - // loop through the records - while (true) - { - // pointer that will be set to hash key and index - char *key; - unsigned long ind; - - // get current key - int hash_key_type = zend_hash_get_current_key(arr, &key, &ind, 0); - - // check the type - switch (hash_key_type) { - - // was it a string? - case HASH_KEY_IS_STRING: - - // inaccessible properties (privates) start with a null character - if (*key == '\0') break; - - // required variable - zval **value; - - // retrieve data, and add to result - zend_hash_get_current_data(arr, (void **) &value); - result[key] = Value(*value); - break; - - default: - - // we're ready - return result; - } + if (isArray()) return ValueIterator(Z_ARRVAL_P(_val), true); + + // get access to the hast table + if (isObject()) return ValueIterator(Z_OBJ_HT_P(_val)->get_properties(_val), true); + + // invalid + // @todo fix iterators without a hashtable + // @todo test Php::empty() function + return ValueIterator(nullptr,true); +} - // next iteration - zend_hash_move_forward(arr); - } - } +/** + * 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 +{ + // check type + if (isArray()) return ValueIterator(Z_ARRVAL_P(_val), false); - // done - return result; + // get access to the hast table + if (isObject()) return ValueIterator(Z_OBJ_HT_P(_val)->get_properties(_val), false); + + // invalid + return ValueIterator(nullptr, false); } /** diff --git a/src/valueiterator.cpp b/src/valueiterator.cpp new file mode 100644 index 0000000..a40ff81 --- /dev/null +++ b/src/valueiterator.cpp @@ -0,0 +1,135 @@ +/** + * ValueIterator.cpp + * + * Implementation of the value iterator + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Constructor + * @param hashtable The hashtable to iterate over + * @param first Should it start at the first position? + */ +ValueIterator::ValueIterator(HashTable *hashtable, bool first) : _table(hashtable) +{ + // reset the hash pointer to the internal position + if (hashtable && first) + { + // move to first position + zend_hash_internal_pointer_reset_ex(_table, &_position); + + // read current data + read(); + } + else + { + // start with invalid data + invalidate(); + } +} + +/** + * Increment position + * @return ValueIterator + */ +ValueIterator &ValueIterator::operator++() +{ + // leap out if already on an invalid pos (behind the last pos) + if (!_position) return *this; + + // move the iterator forward + if (zend_hash_move_forward_ex(_table, &_position) == SUCCESS) + { + // read current key and value + return read(); + } + else + { + // invalidate current position + return invalidate(); + } +} + +/** + * Decrement position + * @return ValueIterator + */ +ValueIterator &ValueIterator::operator--() +{ + // leap out if we're not even iterating over a hash table + if (!_table) return *this; + + // if position is invalid, it is one position behind the last position + if (!_position) + { + // move to last position + zend_hash_internal_pointer_end_ex(_table, &_position); + } + else if (zend_hash_move_backwards_ex(_table, &_position) == FAILURE) + { + // invalidate current position + return invalidate(); + } + + // read current key and value + return read(); +} + +/** + * Read current key and value + * @return ValueIterator + */ +ValueIterator &ValueIterator::read() +{ + // zval to read the current key in + Value key; + + // read in the current key + zend_hash_get_current_key_zval_ex(_table, key._val, &_position); + + // if the key is set to NULL, it means that the object is not at a valid position + if (key.isNull()) return invalidate(); + + // iterator is at a valid position, go fetch the data + // this is the variable we need for fetching the data + zval **value; + + // retrieve data + zend_hash_get_current_data_ex(_table, (void **) &value, &_position); + + // we can now update the current data + _current = std::make_pair<Value,Value>(std::move(key), *value); + + // done + return *this; +} + +/** + * Invalidate the iterator + * @return ValueIterator + */ +ValueIterator &ValueIterator::invalidate() +{ + // forget current position + _position = nullptr; + + // make the data a pair of null ptrs + _current = std::make_pair<Value,Value>(nullptr,nullptr); + + // done + return *this; +} + +/** + * End namespace + */ +} + |