From e7faad695a7dae3f6ca07f3746d4577c2e8745d6 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 21 Mar 2014 09:44:08 +0100 Subject: implementation of traverseiterator (untested) --- include/value.h | 7 ++ src/includes.h | 1 + src/traverseiterator.h | 239 +++++++++++++++++++++++++++++++++++++++++++++++++ src/value.cpp | 46 +++++++--- 4 files changed, 280 insertions(+), 13 deletions(-) create mode 100644 src/traverseiterator.h 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 + * @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 ¤t() 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 _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,22 +1543,49 @@ std::map 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()); } +/** + * 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 @@ -1566,14 +1593,7 @@ ValueIterator Value::begin() const */ 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); } /** -- cgit v1.2.3