summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-16 14:55:51 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-16 14:55:51 +0100
commitec6d84016bec41b05e275641f990831572171969 (patch)
treeff76497cf1a000d96892dd9b2ed2ab6144a3bf89
parenta83e9b3af70f35400ec5d62a4b400d55b05c3492 (diff)
added Value::begin() and Value::end() methods to make it possible to iterate over a value
-rw-r--r--include/value.h21
-rw-r--r--include/valueiterator.h129
-rw-r--r--phpcpp.h1
-rw-r--r--src/includes.h1
-rw-r--r--src/value.cpp134
-rw-r--r--src/valueiterator.cpp135
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
+ */
+}
+
diff --git a/phpcpp.h b/phpcpp.h
index f58eb8d..b9f10df 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -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
+ */
+}
+