summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-21 09:44:08 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-21 09:44:08 +0100
commite7faad695a7dae3f6ca07f3746d4577c2e8745d6 (patch)
tree27f3d3d61a2381c05daac0ae19400db9e1a04c31
parent64bbfe2cb99b9214aa78089ad8a14c9c73ed6a16 (diff)
implementation of traverseiterator (untested)
-rw-r--r--include/value.h7
-rw-r--r--src/includes.h1
-rw-r--r--src/traverseiterator.h239
-rw-r--r--src/value.cpp46
4 files changed, 280 insertions, 13 deletions
diff --git a/include/value.h b/include/value.h
index d49ce4a..63914a9 100644
--- a/include/value.h
+++ b/include/value.h
@@ -933,6 +933,13 @@ protected:
* @return Value The value that was set
*/
const Value &setRaw(const char *key, int size, const Value &value);
+
+ /**
+ * Internal helper method to create an iterator
+ * @param begin Should the iterator start at the begin?
+ * @return iterator
+ */
+ iterator createIterator(bool begin) const;
/**
* The Globals and Member classes can access the zval directly
diff --git a/src/includes.h b/src/includes.h
index 1862037..cf7dff3 100644
--- a/src/includes.h
+++ b/src/includes.h
@@ -95,6 +95,7 @@
#include "iteratorimpl.h"
#include "hashiterator.h"
#include "invaliditerator.h"
+#include "traverseiterator.h"
#ifndef ZVAL_COPY_VALUE
#define ZVAL_COPY_VALUE(z, v) \
diff --git a/src/traverseiterator.h b/src/traverseiterator.h
new file mode 100644
index 0000000..1b12650
--- /dev/null
+++ b/src/traverseiterator.h
@@ -0,0 +1,239 @@
+/**
+ * TraverseIterator.h
+ *
+ * When an object from PHP userspace implements its own iterator methods,
+ * and the Php::Value object is used to iterate over its properties, this
+ * TraversableIterator class is used to iterate over the properties
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class TraverseIterator : public IteratorImpl
+{
+public:
+ /**
+ * Constructor
+ * @param object
+ * @param begin
+ */
+ TraverseIterator(zval *object, bool begin) : _object(object)
+ {
+ // leap out if this iterator starts at the end
+ if (!begin) return;
+
+ // we need the class entry
+ auto *entry = zend_get_class_entry(object);
+
+ // create the iterator
+ _iter = entry->get_iterator(entry, object, false);
+
+ // rewind the iterator
+ _iter->funcs->rewind(_iter);
+
+ // is the iterator at a valid position?
+ if (_iter->funcs->valid(_iter)) return;
+
+ // reset the iterator
+ _iter->funcs->dtor(_iter);
+
+ // set back to null
+ _iter = nullptr;
+ }
+
+ /**
+ * Copy constructor
+ * @param that
+ */
+ TraverseIterator(const TraverseIterator &that) : TraverseIterator(that._object, that._iter != nullptr)
+ {
+ // @todo this is a broken implementation, the copy is at the start
+ // position, while we'd like to be at the same position
+ }
+
+ /**
+ * Destructor
+ */
+ virtual ~TraverseIterator()
+ {
+ // call the iterator destructor
+ if (_iter) _iter->funcs->dtor(_iter);
+ }
+
+ /**
+ * Clone the object
+ * @return IteratorImpl*
+ */
+ virtual IteratorImpl *clone() override
+ {
+ return new TraverseIterator(*this);
+ }
+
+ /**
+ * Increment position (pre-increment)
+ * @return bool
+ */
+ virtual bool increment() override
+ {
+ // do we still have an iterator?
+ if (_iter) return false;
+
+ // movw it forward
+ _iter->funcs->move_forward(_iter);
+
+ // and read current data
+ read();
+ }
+
+ /**
+ * Decrement position (pre-decrement)
+ * @return bool
+ */
+ virtual bool decrement() override
+ {
+ // not possible with PHP iterators
+ }
+
+ /**
+ * Compare with other iterator
+ * @param that
+ * @return bool
+ */
+ virtual bool equals(const IteratorImpl *that) const override
+ {
+ // of course if the objects are identical
+ if (this == that) return true;
+
+ // cast to traverse-iterator
+ TraverseIterator *other = (TraverseIterator *)that;
+
+ // if both objects are in an invalid state we consider them to be identical
+ if (!_iter && !other->_iter) return true;
+
+ // although the iterators could be at the same pos, for simplicity
+ // we consider them different here
+ return false;
+ }
+
+ /**
+ * Derefecence, this returns a std::pair with the current key and value
+ * @return std::pair
+ */
+ virtual const std::pair<Value,Value> &current() const
+ {
+ return _data;
+ }
+
+private:
+ /**
+ * The object that is iterated over
+ * @var _val
+ */
+ zval *_object = nullptr;
+
+ /**
+ * The iterator from Zend
+ * @var zend_object_iterator
+ */
+ struct _zend_object_iterator *_iter = nullptr;
+
+ /**
+ * Current data
+ * @var pair
+ */
+ std::pair<Value,Value> _data;
+
+
+ /**
+ * Read current data
+ * @return bool
+ */
+ bool read()
+ {
+ // not possible when no iterator exists
+ if (!_iter) return false;
+
+ // is the iterator at a valid position?
+ if (_iter->funcs->valid(_iter))
+ {
+ // we need to read the current key and value
+ // @todo implementation for php 5.3 and php 5.4 and higher
+
+#if PHP_VERSION_ID >= 50400
+
+ // create a value object
+ Value val;
+
+ // call the function to get the key
+ _iter->funcs->get_current_key(_iter, _val.
+
+#else
+
+ // variable we need for fetching the key, and that will be assigned by
+ // the PHP engine
+ char *str_key; unsigned int str_key_len; unsigned long int_key;
+
+ // php 5.3 code, fetch the current key
+ int type = _iter->funcs->get_current_key(_iter, &str_key, &str_key_len, &int_key);
+
+ // what sort of key do we have?
+ if (type == HASH_KEY_IS_LONG)
+ {
+ // we have an int key
+ _data.first = (int64_t)int_key;
+ }
+ else
+ {
+ // we have a string key that is already allocated
+ _data.first = str_key;
+
+ // deallocate the data
+ efree(str_key);
+ }
+
+#endif
+
+ // now we're going to fetch the value, for this we need a strange zval
+ // it is strange that this is a pointer-to-pointer, but that is how
+ // the Zend engine implements this. It is going to be filled with
+ // a pointer to a memory address that is guaranteed to hold a valid
+ // zval.
+ zval **zval;
+
+ // get the current value
+ _iter->funcs->get_current_data(_iter, &zval);
+
+ // wrap the zval in a value object
+ _data.second = Value(*zval);
+
+ // done
+ return true;
+ }
+ else
+ {
+ // reset the iterator
+ _iter->funcs->dtor(_iter);
+
+ // set back to null
+ _iter = nullptr;
+
+ // done
+ return false;
+ }
+ }
+
+};
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/src/value.cpp b/src/value.cpp
index 96f7321..6f91f44 100644
--- a/src/value.cpp
+++ b/src/value.cpp
@@ -1543,17 +1543,34 @@ std::map<std::string,Php::Value> Value::mapValue() const
}
/**
- * Return an iterator for iterating over the values
- * This is only meaningful for Value objects that hold an array or an object
+ * Internal helper method to retrieve an iterator
+ * @param begin Should the iterator start at the begin
* @return iterator
*/
-ValueIterator Value::begin() const
+ValueIterator Value::createIterator(bool begin) const
{
// check type
- if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), true));
+ if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), begin));
// get access to the hast table
- if (isObject()) return ValueIterator(new HashIterator(Z_OBJ_HT_P(_val)->get_properties(_val), true));
+ if (isObject())
+ {
+ // is a special iterator method defined in the class entry?
+ auto *entry = zend_get_class_entry(_val);
+
+ // 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));
+ }
+ else
+ {
+ // construct a regular iterator
+ return ValueIterator(new HashIterator(Z_OBJ_HT_P(_val)->get_properties(_val), begin));
+ }
+ }
// invalid
return ValueIterator(new InvalidIterator());
@@ -1564,16 +1581,19 @@ ValueIterator Value::begin() const
* 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
{
- // check type
- if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), false));
-
- // get access to the hast table
- if (isObject()) return ValueIterator(new HashIterator(Z_OBJ_HT_P(_val)->get_properties(_val), false));
-
- // invalid
- return ValueIterator(new InvalidIterator());
+ return createIterator(false);
}
/**