diff --git a/DRAFT.php b/DRAFT.php
new file mode 100644
index 0000000..c7ad66f
--- /dev/null
+++ b/DRAFT.php
@@ -0,0 +1,789 @@
+<?php
+/*
+|--------------------------------------------------------------------------
+| PHP Data Structures - API Draft - v2.0
+|
+| Target PHP Version: 7.3.1
+|
+| https://pecl.php.net/package/ds
+|
+|--------------------------------------------------------------------------
+|
+| This file is will be used for planning the API of the next major version of
+| the data structures extension. Please feel free to suggest modifications.
+*/
+namespace Ds;
+
+use Countable;
+use LogicException;
+use Traversable;
+
+/******************************************************************************/
+/*                                EXCEPTIONS                                  */
+/******************************************************************************/
+
+/**
+ * A marker interface that is implemented by all exceptions within this library.
+ */
+interface Exception implements Throwable {}
+
+/**
+ * Should be thrown when an empty container is polled in an empty state.
+ */
+class EmptyContainerException extends RuntimeException implements Exception {}
+
+/**
+ * Should be thrown when a key is not supported.
+ */
+class InvalidKeyException extends LogicException implements Exception {}
+
+/**
+ * Should be thrown when an index or key is not within the given access bounds
+ * of a structure, such as attempting to access beyond the length.
+ */
+class InvalidOffsetException extends LogicException implements Exception {}
+
+/******************************************************************************/
+/*                                INTERFACES                                  */
+/******************************************************************************/
+
+/**
+ * Indicates that a structure can be cleared.
+ */
+interface Clearable
+{
+    function clear(): void;
+}
+
+/**
+ * Indicates that a structure can be sorted.
+ */
+interface Sortable
+{
+    /**
+     * Sorts the keys and/or values of $this structure.
+     *
+     * This interface does not specify whether sorting is done in-place or not.
+     *
+     * @return static A sorted version of $this structure,
+     *                which could be itself or a sorted copy.
+     */
+    function sort(callable $comparator = null): self;
+}
+
+/**
+ * Indicates that an object is designed to be equated using `==`.
+ * The operator will be overloaded for all classes that implement Equatable.
+ */
+interface Equatable
+{
+    /**
+     * @param static $other The other structure to compare with.
+     *
+     * @return bool Whether this structure equates to the given one.
+     */
+    function equals($other): bool;
+}
+
+/**
+ * Indicates that an object is designed to be used in hash-based structures.
+ */
+interface Hashable extends Equatable
+{
+    /**
+     * @return int|string A scalar value to be used when generating a hashcode.
+     */
+    function hashCode();
+}
+
+/**
+ * Indicates that a structure contains elements and is aware of the number of
+ * items contained at any point in time.
+ */
+interface Container extends \Countable
+{
+    /**
+     * @return int The number of items in this container.
+     */
+    function count(): int;
+
+    /**
+     * @return bool Whether this container is empty.
+     */
+    function isEmpty(): bool;
+}
+
+/**
+ * Indicates that a structure can be accessed using a zero-based integer index
+ * indicating the position of an element from the beginning of the structure.
+ */
+interface Sequence extends \Countable
+{
+    /**
+     * @param mixed $value The value to look for.
+     *
+     * @return mixed The value at the given offset.
+     *
+     * @throws InvalidOffsetException if the $offset is not within [0, size).
+     */
+    function get(int $offset);
+
+    /**
+     * @param mixed $value The value to search for.
+     *
+     * @return int The offset of the given value, or -1 if the value could not be found.
+     */
+    // function indexOf($value): int;
+
+    /**
+     * @param mixed $value The value to search for.
+     *
+     * @return bool TRUE if this sequence contains the given value, FALSE otherwise.
+     */
+    // function contains($value): bool;
+
+    /**
+     * @return mixed The first value in $this structure.
+     *
+     * @throws EmptyContainerException
+     */
+    function first();
+
+    /**
+     * @return mixed The last value in $this structure.
+     *
+     * @throws EmptyContainerException
+     */
+    function last();
+}
+
+/**
+ * A sequence which can be modified, either in-place or as a copy.
+ */
+interface MutableSequence extends Sequence
+{
+    /**
+     * Sets the value at the given offset.
+     *
+     * @param int   $offset The offset at which the value should be set.
+     * @param mixed $value  The value to set.
+     *
+     * @throws InvalidOffsetException if the index is not within [0, size)
+     */
+    function set(int $offset, $value): void;
+
+    /**
+     * Removes the value at the given offset, moving all successive
+     * values one position towards the front.
+     *
+     * @param int The offset to be removed.
+     *
+     * @throws InvalidOffsetException if the index is not within [0, size)
+     */
+    function unset(int $offset): void;
+
+    /**
+     * Moves all values between the given index and the end of the sequence
+     * towards the back, then inserts the given values into the gap.
+     *
+     * @param int   $offset    The offset to insert values at.
+     * @param mixed ...$values The value to insert at the specified offset.
+     *
+     * @throws InvalidOffsetException if the index is out of range [0, size]
+     */
+    function insert(int $offset, ...$values): void;
+}
+
+/**
+ * 
+ */
+interface Set
+{
+    /**
+     * @param mixed $value The value to search for.
+     *
+     * @return bool TRUE if this set contains the given value, FALSE otherwise.
+     */
+    function contains($value): bool;
+
+    /**
+     * @param self $other The other set to union with.
+     *
+     * @return static A set containing values in both $this and $other.
+     */
+    function or(Set $other): Set;
+
+    /**
+     * @param self $other The other set to xor with.
+     *
+     * @return static A set containing values in either $this or $other,
+     *                but not in both.
+     */
+    function xor(Set $other): Set;
+
+    /**
+     * @param self $other The other set to diff against.
+     *
+     * @return static A set containing values in $this but not in $other.
+     */
+    function not(Set $other): Set;
+
+    /**
+     * @param self $other The other set to intersect with.
+     *
+     * @return static A set containing values in $this that are also in $other.
+     */
+    function and(Set $other): Set;
+}
+
+/**
+ * A set which can be modified, either in-place or as a copy.
+ */
+interface MutableSet extends Set
+{
+    /**
+     * @param mixed ...$values The values to be added.
+     *
+     * Adds the given value to $this set if it is not already in $this set.
+     */
+    function add(...$values): void;
+
+    /**
+     * @param mixed $value The value to be removed.
+     *
+     * Removes a given value from $this set, or does nothing if that value could
+     * not be found. The caller can use `has` to determine membership before
+     * removal. This method therefore promises only that the given value is not
+     * a member of the set on return.
+     */
+    function remove($value): void;
+}
+
+/**
+ * Indicates that a structure is a set that is always sorted.
+ */
+interface SortedSet extends Set
+{
+}
+
+/**
+ * A set that allows duplicate values, and keeps a count of the number of times
+ * a value appears in the set.
+ */
+interface MultiSet extends Set
+{
+    /**
+     * @param mixed $value The value of which the frequency is to be inspected.
+     *
+     * @return int The number of instances of a given value that $this set
+     *             contains, which could be 0 if not in the set at all.
+     */
+    public function countOf($value): int {}
+
+    /**
+     * Adjusts the count of a value by a given count. If the resulting count
+     * becomes <= 0, the value will be removed. If a value is not already in
+     * the set, it will be added before the count is adjusted.
+     *
+     *   > 0: Add
+     *   < 0: Remove
+     *
+     * @param mixed $value The value to be adjusted.
+     * @param int   $count The amount by which the value should be adjusted.
+     *
+     * @return int The resulting frequency after the adjustment.
+     */
+    public function adjust($value, int $count): int {}
+
+    /**
+     * Returns an iterator of the values with the highest frequencies, where
+     * the key is the element and the value is the frequency.
+     *
+     * @return iterable<mixed, int> The values as keys and their frequency as values.
+     */
+    public function rank(): iterable {}
+
+    /**
+     * Creates an iterable where keys are produced multiple times based on their frequency.
+     *
+     * @return iterable<int, mixed> Keys as values, possibly multiple times.
+     */
+    public function enumerate(): iterable {}
+}
+
+/**
+ * A structure that associates one value with another and provides the ability
+ * to query or adjust associations efficiently.
+ *
+ * @todo Should Map extend Set (not mutable)?
+ *       What about the set operation methods then?
+ *
+ * @todo Should Map require some kind of merge, diff, and intersection support?
+ */
+interface Map
+{
+    /**
+     * @todo if NULL keys are not allowed, should we throw if $key is NULL?
+     *
+     * @param mixed $key     The key at which the value should be looked for.
+     * @param mixed $default The fallback value of no value exists at specified key.
+     *
+     * @return mixed The value associated with the $key, or $default if the key
+     *               could not be found and a $default was provided.
+     *
+     * @throws AccessException if the $key was not found with default given.
+     */
+    function get($key, $default = null);
+
+    /**
+     * @return Set All keys from the map added in traversal order.
+     *
+     * @todo Should this return an iterator?
+     */
+    function keys(): Set;
+
+    /**
+     * @return Set All keys from the map added in traversal order.
+     *
+     * @todo Should this return an iterator?
+     */
+    function values(): Sequence;
+}
+
+/**
+ * A map which can be modified, either in-place or as a copy.
+ */
+interface MutableMap extends Map
+{
+    /**
+     * Associates the $key with the $value, overriding any previous association.
+     *
+     * @param mixed $key   The key to set the value for.
+     * @param mixed $value The value to set.
+     *
+     * @throws InvalidKeyException if the map implementation does not support
+     *                             the given key, eg. NULL
+     */
+    function set($key, $value): void;
+
+    /**
+     * Removes the given $key from the map, and does nothing if they key could
+     * not be found. This emulates `unset`
+     *
+     * @param mixed $key The key at which the value should be unset.
+     */
+    function unset($key): void;
+}
+
+/**
+ * Indicates that a structure is a map that is always sorted by key.
+ */
+interface SortedMap extends Map
+{
+}
+
+/**
+ * Indicates that a structure can receive and produce values efficiently in a
+ * semantically defined order. Both operations should be O(1).
+ */
+interface Transferable
+{
+    /**
+     * Offers one or more values to this transferable.
+     *
+     * @param mixed ...$values The values that should be transferred.
+     */
+    function send(...$values): void;
+
+    /**
+     * Removes and returns the next value produced by this transferable.
+     *
+     * @return mixed The transferred value.
+     *
+     * @throws EmptyContainerException
+     */
+    function poll();
+}
+
+
+/******************************************************************************/
+/*                                 CLASSES                                    */
+/******************************************************************************/
+
+/**
+ *
+ */
+final class Buffer implements
+    ArrayAccess,
+    Equatable,  /* Same type, size and values at each offset. */
+    Clearable,
+    Sortable
+    {
+        public const TYPE_BOOL      = 0; // undefined => false
+        public const TYPE_INTEGER   = 1; // undefined => 0
+        public const TYPE_FLOAT     = 2; // undefined => 0.0
+        public const TYPE_STRING    = 3; // undefined => NULL
+        public const TYPE_ARRAY     = 4; // undefined => NULL
+        public const TYPE_OBJECT    = 5; // undefined => NULL
+
+        /**
+         * Allow buffers to be truncated to 0.
+         */
+        public const MIN_SIZE = 0;
+
+        /**
+         * We must be able to accept and return size as integer, so we can only
+         * support up to max int values. This is absolutely massive though so
+         * should be incredibly rare to approach.
+         *
+         * @todo Should we consider when USE_ZEND_ALLOC might be 0? Is this a
+         *       potential security or buffer overflow issue? I'm not sure...
+         */
+        public const MAX_SIZE = PHP_INT_MAX;
+
+        /**
+         * @param int $capacity Initial capacity of the structure.
+         * @param int $type     Value type of the buffer, which must be one of
+         *                      the Buffer::Type_* constants.
+         *
+         * @throws UnexpectedValueException if the type is not a valid constant.
+         */
+        public function __construct(int $size, int $type) {}
+
+        /**
+         * @return int The type of the values in this buffer.
+         */
+        public function type(): int {}
+
+        /**
+         * @return int The current size of this buffer.
+         */
+        public function size(): int {}
+
+        /**
+         * Re-sizes this buffer to a new size, which may truncate the current buffer.
+         * It is up to user to manage this case.
+         *
+         * @param int $size The new size to which this buffer should be resized.
+         *
+         * @throws RangeException if the size is not within the valid range:
+         *                        MIN_SIZE <= $size <= MAX_SIZE
+         */
+        public function resize(int $size): void {}
+
+        /**
+         * Returns the value at the given offset as a standard value of the type
+         * that this buffer stores. Type validation is done on write. If nothing
+         * has been stored at the given offset, this method will return an
+         * appropriate "null" value for the type (see notes on constants).
+         *
+         * @param mixed $offset The offset at which to look for a value.
+         *
+         * @return mixed The value at the specified offset.
+         *
+         * @throws InvalidOffsetException if the offset is not within [0, size)
+         */
+        public function offsetGet($offset) {}
+
+        /**
+         * Sets the value at the given offset after validating its value type.
+         *
+         * Note: NULL is not supported. Use `unset` to clear a value by offset.
+         *
+         * @param mixed $offset The offset at which to set the new value.
+         * @param mixed $value  The value to set at the specified offset.
+         *
+         * @throws InvalidOffsetException if the offset is not within [0, size)
+         */
+        public function offsetSet($offset, $value): void {}
+
+        /**
+         * Sets the value at the given offset to NULL.
+         *
+         * @param mixed $offset The offset at which to unset a value.
+         *
+         * @throws InvalidOffsetException if the offset is not within [0, size),
+         *                         unless called as part of a silent `unset`.
+         */
+        public function offsetUnset($offset): void {}
+
+        /**
+         * Returns whether there is a non-NULL value at the given offset. This
+         * method returns FALSE if the offset is not within [0, size).
+         *
+         * @param mixed $offset The offset at which to look for a value.
+         *
+         * @return bool Whether the specified offset contains a value or not.
+         */
+        public function offsetExists($offset): bool {}
+    }
+
+/**
+ * A fixed-size immutable sequence.
+ */
+final class Tuple implements
+    Traversable,
+    Equatable,
+    Container, /* \Countable */
+    Sequence,  /* \Countable */
+    Hashable   /* Equatable */
+    {
+        /**
+         * @param mixed[] $source The values used to fill this tuple.
+         */
+        public function __construct(iterable $source) {}
+    }
+
+/**
+ * List structure, dynamic size, contiguous, no ops at the front.
+ */
+final class Vector implements
+    ArrayAccess,
+    Traversable,
+    Equatable,      /* Same values in the same order. */
+    Clearable,
+    Container,      /* \Countable */
+    Sortable,
+    MutableSequence /* Sequence, \Countable */
+    {
+        /**
+         * Adds one or more values to the end of the vector.
+         *
+         * @param mixed ...$values The values to append to the vector.
+         */
+        public function push(...$values): void;
+
+        /**
+         * Removes and returns the value at the end of the vector.
+         *
+         * @return mixed The value at the end of the vector.
+         *
+         * @throws EmptyContainerException
+         */
+        public function pop();
+    }
+
+/**
+ * The Set equivalent of HashMap, which might not use a HashMap internally, but
+ * will always preserve insertion order.
+ */
+final class HashSet implements
+    ArrayAccess,
+    Traversable,
+    Transferable,
+    Equatable,    /* Same values in the same order. */
+    Clearable,
+    Container,    /* \Countable */
+    Sortable,
+    Sequence,     /* \Countable */
+    MutableSet    /* Set */
+    {}
+
+/**
+ * A structure that is always sorted and does not allow duplicate elements.
+ *
+ * This would be useful for building sorted datasets, or sets without hashing.
+ *
+ * See: https://github.com/php-ds/ext-ds/issues/121
+ */
+final class TreeSet implements
+    Traversable,
+    Equatable,  /* Same values in the same order. */
+    Clearable,
+    Container,  /* \Countable */
+    MutableSet, /* Set */
+    SortedSet   /* Set */
+    {
+        /**
+         * Creates a new tree set using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this tree set.
+         */
+        public function __construct(callable $comparator = null) {}
+    }
+
+/**
+ * A set that allows duplicate values, and keeps a count of the number of times
+ * a value appears in the set.
+ */
+final class HashMultiSet implements
+    ArrayAccess,
+    Traversable,
+    Transferable,
+    Equatable,    /* Same values and frequencies. */
+    Clearable,
+    Container,    /* \Countable */
+    MutableSet,   /* Set */
+    MultiSet      /* Set */
+    {}
+
+/**
+ * A set that allows duplicate values, and keeps a count of the number of times
+ * a value appears in the set.
+ */
+final class TreeMultiSet implements
+    ArrayAccess,
+    Traversable,
+    Equatable,    /* Same values and frequencies. */
+    Transferable,
+    Container,    /* \Countable */
+    Clearable,
+    MutableSet,   /* Set */
+    SortedSet,    /* Set */
+    MultiSet      /* Set */
+    {
+        /**
+         * Creates a new multiset using values from $iter.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this multi set.
+         */
+        public function __construct(callable $comparator = null) {}
+    }
+
+/**
+ * This structure is based on the same internal structure of the PHP array,
+ * but is not identical. We can use hashable objects as keys, and insertion
+ * order is still preserved.
+ */
+final class HashMap implements
+    ArrayAccess,
+    Traversable,
+    Equatable,  /* Same key => value associations, ordering not considered. */
+    Container,  /* \Countable */
+    Clearable,
+    Sortable,
+    MutableMap  /* Map */
+    {}
+
+/**
+ *
+ */
+final class TreeMap implements
+    ArrayAccess,
+    Traversable,
+    Equatable,  /* Same key => value associations. */
+    Container,  /* \Countable */
+    Clearable,
+    MutableMap, /* Map */
+    SortedMap   /* Map */
+    {}
+
+/**
+ * A basic first-in-last-out structure.
+ */
+final class Stack implements
+    Container,   /* \Countable */
+    Transferable
+    {
+        /**
+         * Adds a value to the top of the stack.
+         *
+         * @param mixed ...$values The values to be pushed onto the stack.
+         */
+        public function push(...$values): void {}
+
+        /**
+         * Removes and returns the value at the top of the stack.
+         *
+         * @return mixed The value at the top.
+         *
+         * @throws EmptyContainerException
+         */
+        public function pop() {}
+    }
+
+/**
+ * A basic first-in-first-out structure.
+ */
+final class Queue implements
+    Container,   /* \Countable */
+    Transferable
+    {
+        /**
+         * Adds a value to the queue.
+         *
+         * @param mixed ...$values The values to be pushed into the queue.
+         */
+        public function push(...$values): void {}
+
+        /**
+         * Removes and returns the next value in the queue.
+         *
+         * @return mixed The next value.
+         */
+        public function shift();
+    }
+
+/**
+ * Stable heap with an optional comparator, defaulting to minimum.
+ */
+final class Heap implements
+    Container,   /* \Countable */
+    Clearable,
+    Transferable
+    {
+        /**
+         * Creates a new heap using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
+         */
+        public function __construct(callable $comparator = null) {}
+
+        /**
+         * Adds a value to the heap.
+         *
+         * @param mixed ...$values The values to be pushed onto the heap.
+         */
+        public function push(...$values): void {}
+
+        /**
+         * Removes and returns the value at the top of the heap.
+         *
+         * @return mixed The value at the top.
+         *
+         * @throws EmptyContainerException
+         */
+        public function shift() {}
+    }
+
+/**
+ * A queue that yields values in order of priority, from high to low.
+ */
+final class PriorityQueue implements
+    Container,   /* \Countable */
+    Clearable,
+    Transferable
+    {
+        /**
+         * Creates a new priority queue using an optional comparator.
+         *
+         * @param (callable(mixed $a, mixed $b): int)|null $comparator The comparator used to sort this heap.
+         */
+        public function __construct(callable $comparator = null) {}
+
+        /**
+         * Adjusts the priority of a given value, setting it to the return value
+         * of the given mutator, then ensures that heap invariants are resolved.
+         *
+         * @param mixed           $value   The value of which the priority should be adjusted.
+         * @param callable(): int $mutator The mutator returning the new priority.
+         */
+        public function adjust($value, callable $mutator): void {}
+
+        /**
+         * Adds a value to the priority queue, using a given initial priority.
+         *
+         * @param mixed $value    The value to be pushed into the queue.
+         * @param mixed $priority The priority of the inserted value.
+         */
+        public function push($value, $priority): void {}
+
+        /**
+         * Removes and returns the value at the front of the priority queue.
+         *
+         * @return mixed The next value.
+         *
+         * @throws EmptyContainerException
+         */
+        public function shift() {}
+    }
diff --git a/src/common.h b/src/common.h
index 252ebfa..d61021d 100644
--- a/src/common.h
+++ b/src/common.h
@@ -42,6 +42,25 @@
 #define PHP_DS_ME(cls, name) \
     PHP_ME(cls, name, arginfo_##cls##_##name, ZEND_ACC_PUBLIC)
 
+/**
+ *
+ */
+#define PHP_DS_NO_ITERATOR_OR_RETURN(obj) do { \
+    if (UNEXPECTED(obj->iteratorCount)) { \
+        MODIFIED_DURING_ITERATION(); \
+    } \
+} while (0)
+
+/**
+ * Increases the object's iterator count by 1.
+ */
+#define PHP_DS_INCR_ITERATOR(obj) (obj)->iteratorCount++
+
+/**
+ * Decreases the object's iterator count by 1.
+ */
+#define PHP_DS_DECR_ITERATOR(obj) (obj)->iteratorCount--
+
 /**
  *
  */
@@ -222,6 +241,10 @@ int name##_unserialize(                 \
     zend_ce_error, \
     "Immutable objects may not be changed")
 
+#define MODIFIED_DURING_ITERATION() ds_throw_exception( \
+    spl_ce_LogicException, \
+    "Modification during iteration is not supported")
+
 /**
  *
  */
diff --git a/src/php/classes/php_map_ce.c b/src/php/classes/php_map_ce.c
index ec9efd6..86deb72 100644
--- a/src/php/classes/php_map_ce.c
+++ b/src/php/classes/php_map_ce.c
@@ -48,12 +48,14 @@ METHOD(capacity)
 METHOD(put)
 {
     PARSE_ZVAL_ZVAL(key, value);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_MAP_OBJ());
     ds_map_put(THIS_DS_MAP(), key, value);
 }
 
 METHOD(putAll)
 {
     PARSE_ZVAL(values);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_MAP_OBJ());
     ds_map_put_all(THIS_DS_MAP(), values);
 }
 
@@ -96,6 +98,7 @@ METHOD(diff)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_MAP_OBJ());
     ds_map_clear(THIS_DS_MAP());
 }
 
diff --git a/src/php/classes/php_priority_queue_ce.c b/src/php/classes/php_priority_queue_ce.c
index 22da404..edd50f8 100644
--- a/src/php/classes/php_priority_queue_ce.c
+++ b/src/php/classes/php_priority_queue_ce.c
@@ -40,12 +40,14 @@ METHOD(copy)
 METHOD(push)
 {
     PARSE_ZVAL_ZVAL(value, priority);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_PRIORITY_QUEUE_OBJ());
     ds_priority_queue_push(THIS_DS_PRIORITY_QUEUE(), value, priority);
 }
 
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_PRIORITY_QUEUE_OBJ());
     ds_priority_queue_pop(THIS_DS_PRIORITY_QUEUE(), return_value);
 }
 
@@ -76,6 +78,7 @@ METHOD(count)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_PRIORITY_QUEUE_OBJ());
     ds_priority_queue_clear(THIS_DS_PRIORITY_QUEUE());
 }
 
diff --git a/src/php/classes/php_queue_ce.c b/src/php/classes/php_queue_ce.c
index d69988a..653b034 100644
--- a/src/php/classes/php_queue_ce.c
+++ b/src/php/classes/php_queue_ce.c
@@ -38,12 +38,14 @@ METHOD(capacity)
 METHOD(push)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_QUEUE_OBJ());
     ds_queue_push(THIS_DS_QUEUE(), argc, argv);
 }
 
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_QUEUE_OBJ());
     ds_queue_pop_throw(THIS_DS_QUEUE(), return_value);
 }
 
@@ -68,6 +70,7 @@ METHOD(count)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_QUEUE_OBJ());
     ds_queue_clear(THIS_DS_QUEUE());
 }
 
diff --git a/src/php/classes/php_set_ce.c b/src/php/classes/php_set_ce.c
index d961837..099f4b4 100644
--- a/src/php/classes/php_set_ce.c
+++ b/src/php/classes/php_set_ce.c
@@ -49,12 +49,14 @@ METHOD(capacity)
 METHOD(add)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
     ds_set_add_va(THIS_DS_SET(), argc, argv);
 }
 
 METHOD(remove)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
     ds_set_remove_va(THIS_DS_SET(), argc, argv);
 }
 
@@ -121,6 +123,7 @@ METHOD(union)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
     ds_set_clear(THIS_DS_SET());
 }
 
@@ -144,6 +147,8 @@ METHOD(isEmpty)
 
 METHOD(sort)
 {
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_SET_OBJ());
+
     if (ZEND_NUM_ARGS()) {
         PARSE_COMPARE_CALLABLE();
         ds_set_sort_callback(THIS_DS_SET());
diff --git a/src/php/classes/php_stack_ce.c b/src/php/classes/php_stack_ce.c
index b44905b..8b4d45a 100644
--- a/src/php/classes/php_stack_ce.c
+++ b/src/php/classes/php_stack_ce.c
@@ -39,12 +39,14 @@ METHOD(capacity)
 METHOD(push)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_STACK_OBJ());
     ds_stack_push_va(THIS_DS_STACK(), argc, argv);
 }
 
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_STACK_OBJ());
     ds_stack_pop_throw(THIS_DS_STACK(), return_value);
 }
 
@@ -69,6 +71,7 @@ METHOD(copy)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_STACK_OBJ());
     ds_stack_clear(THIS_DS_STACK());
 }
 
diff --git a/src/php/classes/php_vector_ce.c b/src/php/classes/php_vector_ce.c
index d590215..27af77f 100644
--- a/src/php/classes/php_vector_ce.c
+++ b/src/php/classes/php_vector_ce.c
@@ -45,6 +45,7 @@ METHOD(capacity)
 METHOD(clear)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_clear(THIS_DS_VECTOR());
 }
 
@@ -97,6 +98,7 @@ METHOD(get)
 METHOD(insert)
 {
     PARSE_LONG_AND_VARIADIC_ZVAL(index);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_insert_va(THIS_DS_VECTOR(), index, argc, argv);
 }
 
@@ -143,21 +145,17 @@ METHOD(merge)
 METHOD(pop)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_pop_throw(THIS_DS_VECTOR(), return_value);
 }
 
 METHOD(push)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_push_va(THIS_DS_VECTOR(), argc, argv);
 }
 
-METHOD(push_one)
-{
-    PARSE_ZVAL(value);
-    ds_vector_push(THIS_DS_VECTOR(), value);
-}
-
 METHOD(reduce)
 {
     PARSE_CALLABLE_AND_OPTIONAL_ZVAL(initial);
@@ -167,6 +165,7 @@ METHOD(reduce)
 METHOD(remove)
 {
     PARSE_LONG(index);
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_remove(THIS_DS_VECTOR(), index, return_value);
 }
 
@@ -197,6 +196,7 @@ METHOD(set)
 METHOD(shift)
 {
     PARSE_NONE;
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_shift_throw(THIS_DS_VECTOR(), return_value);
 }
 
@@ -254,6 +254,7 @@ METHOD(toArray)
 METHOD(unshift)
 {
     PARSE_VARIADIC_ZVAL();
+    PHP_DS_NO_ITERATOR_OR_RETURN(THIS_DS_VECTOR_OBJ());
     ds_vector_unshift_va(THIS_DS_VECTOR(), argc, argv);
 }
 
diff --git a/src/php/handlers/php_deque_handlers.c b/src/php/handlers/php_deque_handlers.c
index 2cb85be..c2124dc 100644
--- a/src/php/handlers/php_deque_handlers.c
+++ b/src/php/handlers/php_deque_handlers.c
@@ -44,6 +44,7 @@ static void php_ds_deque_write_dimension(zval *obj, zval *offset, zval *value)
     ds_deque_t *deque = Z_DS_DEQUE_P(obj);
 
     if (offset == NULL) { /* $v[] = ... */
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_DEQUE_OBJ_P(obj));
         ds_deque_push(deque, value);
 
     } else {
@@ -72,6 +73,8 @@ static void php_ds_deque_unset_dimension(zval *obj, zval *offset)
 {
     ZVAL_DEREF(offset);
 
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_DEQUE_OBJ_P(obj));
+
     if (Z_TYPE_P(offset) != IS_LONG) {
         return;
 
diff --git a/src/php/handlers/php_map_handlers.c b/src/php/handlers/php_map_handlers.c
index ef44e4d..c93af9b 100644
--- a/src/php/handlers/php_map_handlers.c
+++ b/src/php/handlers/php_map_handlers.c
@@ -49,6 +49,7 @@ static void php_ds_map_write_dimension(zval *obj, zval *offset, zval *value)
 
     ZVAL_DEREF(offset);
 
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_MAP_OBJ_P(obj));
     ds_htable_put(map->table, offset, value);
 }
 
@@ -67,6 +68,8 @@ static void php_ds_map_unset_dimension(zval *obj, zval *offset)
 
     ZVAL_DEREF(offset);
 
+
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_MAP_OBJ_P(obj));
     ds_htable_remove(map->table, offset, NULL);
 }
 
diff --git a/src/php/handlers/php_queue_handlers.c b/src/php/handlers/php_queue_handlers.c
index 4210338..63d1d52 100644
--- a/src/php/handlers/php_queue_handlers.c
+++ b/src/php/handlers/php_queue_handlers.c
@@ -11,6 +11,7 @@ static void php_ds_queue_write_dimension(zval *obj, zval *offset, zval *value)
     ds_queue_t *queue = Z_DS_QUEUE_P(obj);
 
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_QUEUE_OBJ_P(obj));
         ds_queue_push_one(queue, value);
         return;
     }
diff --git a/src/php/handlers/php_set_handlers.c b/src/php/handlers/php_set_handlers.c
index dcd57f5..6e398a0 100644
--- a/src/php/handlers/php_set_handlers.c
+++ b/src/php/handlers/php_set_handlers.c
@@ -31,6 +31,7 @@ static zval *php_ds_set_read_dimension(zval *obj, zval *offset, int type, zval *
 static void php_ds_set_write_dimension(zval *obj, zval *offset, zval *value)
 {
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_SET_OBJ_P(obj));
         ds_set_add(Z_DS_SET_P(obj), value);
         return;
     }
@@ -87,7 +88,7 @@ void php_ds_register_set_handlers()
 {
     memcpy(&php_ds_set_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 
-    php_ds_set_handlers.offset = 0; // XtOffsetOf(php_ds_set_t, std);
+    php_ds_set_handlers.offset = 0;
 
     php_ds_set_handlers.cast_object     = php_ds_default_cast_object;
     php_ds_set_handlers.clone_obj       = php_ds_set_clone_obj;
diff --git a/src/php/handlers/php_stack_handlers.c b/src/php/handlers/php_stack_handlers.c
index c06fc0f..b50c447 100644
--- a/src/php/handlers/php_stack_handlers.c
+++ b/src/php/handlers/php_stack_handlers.c
@@ -10,6 +10,7 @@ static void php_ds_stack_write_dimension(zval *obj, zval *offset, zval *value)
     ds_stack_t *stack = Z_DS_STACK_P(obj);
 
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_STACK_OBJ_P(obj));
         ds_stack_push(stack, value);
         return;
     }
diff --git a/src/php/handlers/php_vector_handlers.c b/src/php/handlers/php_vector_handlers.c
index 5817feb..03fe45e 100644
--- a/src/php/handlers/php_vector_handlers.c
+++ b/src/php/handlers/php_vector_handlers.c
@@ -45,6 +45,7 @@ static void php_ds_vector_write_dimension(zval *obj, zval *offset, zval *value)
 
     /* $v[] = ... */
     if (offset == NULL) {
+        PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_VECTOR_OBJ_P(obj));
         ds_vector_push(vector, value);
 
     } else {
@@ -74,6 +75,8 @@ static void php_ds_vector_unset_dimension(zval *obj, zval *offset)
     zend_long index;
     ds_vector_t *vector = Z_DS_VECTOR_P(obj);
 
+    PHP_DS_NO_ITERATOR_OR_RETURN(Z_DS_VECTOR_OBJ_P(obj));
+
     ZVAL_DEREF(offset);
 
     if (Z_TYPE_P(offset) == IS_LONG) {
diff --git a/src/php/iterators/php_htable_iterator.c b/src/php/iterators/php_htable_iterator.c
index 74e7dbf..a94e486 100644
--- a/src/php/iterators/php_htable_iterator.c
+++ b/src/php/iterators/php_htable_iterator.c
@@ -24,6 +24,10 @@ static void php_ds_htable_iterator_dtor(zend_object_iterator *iter)
 
     OBJ_RELEASE(iterator->obj);
     DTOR_AND_UNDEF(&iterator->intern.data);
+
+    if (iterator->dtor) {
+        iterator->dtor((zend_object_iterator *) iterator);
+    }
 }
 
 static int php_ds_htable_iterator_valid(zend_object_iterator *iter)
@@ -160,7 +164,9 @@ static zend_object_iterator *php_ds_htable_create_htable_iterator(
     zval *obj,
     ds_htable_t *table,
     zend_object_iterator_funcs *funcs,
-    int by_ref
+    int by_ref,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ) {
     ds_htable_iterator_t *iterator;
 
@@ -178,6 +184,8 @@ static zend_object_iterator *php_ds_htable_create_htable_iterator(
     iterator->intern.funcs  = funcs;
     iterator->table         = table;
     iterator->obj           = Z_OBJ_P(obj);
+    iterator->ctor          = ctor;
+    iterator->dtor          = dtor;
 
     // Add a reference to the object so that it doesn't get collected when
     // the iterated object is implict, eg. foreach ($obj->getInstance() as $value){ ... }
@@ -187,6 +195,10 @@ static zend_object_iterator *php_ds_htable_create_htable_iterator(
     ++GC_REFCOUNT(iterator->obj);
 #endif
 
+    if (iterator->ctor) {
+        iterator->ctor((zend_object_iterator *) iterator);
+    }
+
     return (zend_object_iterator *) iterator;
 }
 
@@ -194,38 +206,46 @@ zend_object_iterator *php_ds_htable_get_value_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_value_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_value_iterator_funcs, by_ref, ctor, dtor);
 }
 
 zend_object_iterator *php_ds_htable_get_key_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_key_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_key_iterator_funcs, by_ref, ctor, dtor);
 }
 
 zend_object_iterator *php_ds_htable_get_pair_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_pair_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_pair_iterator_funcs, by_ref, ctor, dtor);
 }
 
 zend_object_iterator *php_ds_htable_get_assoc_iterator_ex(
     zend_class_entry *ce,
     zval *obj,
     int by_ref,
-    ds_htable_t *table
+    ds_htable_t *table,
+    ds_htable_iterator_ctor ctor,
+    ds_htable_iterator_dtor dtor
 ){
     return php_ds_htable_create_htable_iterator(
-        obj, table, &php_ds_htable_get_assoc_iterator_funcs, by_ref);
+        obj, table, &php_ds_htable_get_assoc_iterator_funcs, by_ref, ctor, dtor);
 }
diff --git a/src/php/iterators/php_htable_iterator.h b/src/php/iterators/php_htable_iterator.h
index cb2eec0..d9205d0 100644
--- a/src/php/iterators/php_htable_iterator.h
+++ b/src/php/iterators/php_htable_iterator.h
@@ -4,19 +4,24 @@
 #include "php.h"
 #include "../../ds/ds_htable.h"
 
+typedef void (*ds_htable_iterator_ctor)(zend_object_iterator *iterator);
+typedef void (*ds_htable_iterator_dtor)(zend_object_iterator *iterator);
+
 typedef struct ds_htable_iterator {
 
     zend_object_iterator     intern;
     uint32_t                 position;
     ds_htable_bucket_t      *bucket;
     ds_htable_t             *table;
-    zend_object             *obj;
+    zend_object             *obj; // TODO this already exists on intern?
+    ds_htable_iterator_ctor  ctor;
+    ds_htable_iterator_dtor  dtor;
 
 } ds_htable_iterator_t;
 
-zend_object_iterator *php_ds_htable_get_value_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
-zend_object_iterator *php_ds_htable_get_key_iterator_ex  (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
-zend_object_iterator *php_ds_htable_get_pair_iterator_ex (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
-zend_object_iterator *php_ds_htable_get_assoc_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h);
+zend_object_iterator *php_ds_htable_get_value_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
+zend_object_iterator *php_ds_htable_get_key_iterator_ex  (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
+zend_object_iterator *php_ds_htable_get_pair_iterator_ex (zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
+zend_object_iterator *php_ds_htable_get_assoc_iterator_ex(zend_class_entry *ce, zval *obj, int by_ref, ds_htable_t *h, ds_htable_iterator_ctor ctor, ds_htable_iterator_dtor dtor);
 
 #endif
diff --git a/src/php/iterators/php_map_iterator.c b/src/php/iterators/php_map_iterator.c
index e32ad92..41fead3 100644
--- a/src/php/iterators/php_map_iterator.c
+++ b/src/php/iterators/php_map_iterator.c
@@ -7,8 +7,18 @@
 #include "php_map_iterator.h"
 #include "php_htable_iterator.h"
 
+static void php_ds_map_iterator_ctor(zend_object_iterator *iterator)
+{
+    PHP_DS_INCR_ITERATOR((php_ds_map_t *) &iterator->std);
+}
+
+static void php_ds_map_iterator_dtor(zend_object_iterator *iterator)
+{
+    PHP_DS_DECR_ITERATOR((php_ds_map_t *) &iterator->std);
+}
+
 zend_object_iterator *php_ds_map_get_iterator(zend_class_entry *ce, zval *obj, int by_ref)
 {
     ds_htable_t *table = Z_DS_MAP_P(obj)->table;
-    return php_ds_htable_get_assoc_iterator_ex(ce, obj, by_ref, table);
+    return php_ds_htable_get_assoc_iterator_ex(ce, obj, by_ref, table, php_ds_map_iterator_ctor, php_ds_map_iterator_dtor);
 }
diff --git a/src/php/iterators/php_set_iterator.c b/src/php/iterators/php_set_iterator.c
index 170c9fe..cf0ee85 100644
--- a/src/php/iterators/php_set_iterator.c
+++ b/src/php/iterators/php_set_iterator.c
@@ -7,8 +7,18 @@
 #include "php_set_iterator.h"
 #include "php_htable_iterator.h"
 
+static void php_ds_set_iterator_ctor(zend_object_iterator *iterator)
+{
+    PHP_DS_INCR_ITERATOR((php_ds_set_t *) (&iterator->std));
+}
+
+static void php_ds_set_iterator_dtor(zend_object_iterator *iterator)
+{
+    PHP_DS_DECR_ITERATOR((php_ds_set_t *) (&iterator->std));
+}
+
 zend_object_iterator *php_ds_set_get_iterator(zend_class_entry *ce, zval *obj, int by_ref)
 {
     ds_htable_t *table = (Z_DS_SET_P(obj))->table;
-    return php_ds_htable_get_key_iterator_ex(ce, obj, by_ref, table);
+    return php_ds_htable_get_key_iterator_ex(ce, obj, by_ref, table, php_ds_set_iterator_ctor, php_ds_set_iterator_dtor);
 }
diff --git a/src/php/iterators/php_stack_iterator.c b/src/php/iterators/php_stack_iterator.c
index 999a429..2e3d79d 100644
--- a/src/php/iterators/php_stack_iterator.c
+++ b/src/php/iterators/php_stack_iterator.c
@@ -4,7 +4,7 @@
 
 static void php_ds_stack_iterator_dtor(zend_object_iterator *iter)
 {
-
+    PHP_DS_DECR_ITERATOR((php_ds_stack_t *) (&iter->std));
 }
 
 static int php_ds_stack_iterator_valid(zend_object_iterator *iter)
@@ -63,6 +63,7 @@ zend_object_iterator *php_ds_stack_get_iterator(zend_class_entry *ce, zval *obje
         return NULL;
     }
 
+
     iterator = ecalloc(1, sizeof(php_ds_stack_iterator_t));
     zend_iterator_init((zend_object_iterator*) iterator);
 
@@ -70,5 +71,7 @@ zend_object_iterator *php_ds_stack_get_iterator(zend_class_entry *ce, zval *obje
     iterator->stack        = Z_DS_STACK_P(object);
     iterator->position     = 0;
 
+    PHP_DS_INCR_ITERATOR(Z_DS_STACK_OBJ_P(object));
+
     return (zend_object_iterator *) iterator;
 }
diff --git a/src/php/iterators/php_vector_iterator.c b/src/php/iterators/php_vector_iterator.c
index 222fdf6..ebd7af0 100644
--- a/src/php/iterators/php_vector_iterator.c
+++ b/src/php/iterators/php_vector_iterator.c
@@ -8,6 +8,8 @@ static void php_ds_vector_iterator_dtor(zend_object_iterator *iter)
 {
     php_ds_vector_iterator_t *iterator = (php_ds_vector_iterator_t *) iter;
 
+    PHP_DS_DECR_ITERATOR((php_ds_vector_t *) iterator->object);
+
     OBJ_RELEASE(iterator->object);
 }
 
@@ -53,6 +55,7 @@ static zend_object_iterator *php_ds_vector_create_iterator(zval *obj, int by_ref
 {
     php_ds_vector_iterator_t *iterator;
 
+// TODO: if we disable modification during iteration, could be reconsider this?
     if (by_ref) {
         ITERATION_BY_REF_NOT_SUPPORTED();
         return NULL;
@@ -67,6 +70,8 @@ static zend_object_iterator *php_ds_vector_create_iterator(zval *obj, int by_ref
     iterator->object        = Z_OBJ_P(obj);
     iterator->position      = 0;
 
+    PHP_DS_INCR_ITERATOR((php_ds_vector_t *) iterator->object);
+
     // Add a reference to the object so that it doesn't get collected when
     // the iterated object is implict, eg. foreach ($obj->getInstance() as $value){ ... }
 #if PHP_VERSION_ID >= 70300
diff --git a/src/php/iterators/php_vector_iterator.h b/src/php/iterators/php_vector_iterator.h
index 380cc20..c3dffcb 100644
--- a/src/php/iterators/php_vector_iterator.h
+++ b/src/php/iterators/php_vector_iterator.h
@@ -6,8 +6,8 @@
 
 typedef struct php_ds_vector_iterator {
     zend_object_iterator     intern;
-    zend_object             *object;
-    ds_vector_t             *vector;
+    zend_object             *object; // TODO we can cast intern's std to php_ds_vector_t because offset is zero.
+    ds_vector_t             *vector; // TODO isn't this on the object above?
     zend_long                position;
 } php_ds_vector_iterator_t;
 
diff --git a/src/php/objects/php_deque.c b/src/php/objects/php_deque.c
index e8633bf..019ad46 100644
--- a/src/php/objects/php_deque.c
+++ b/src/php/objects/php_deque.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_deque_create_object_ex(ds_deque_t *deque)
 {
     php_ds_deque_t *obj = ecalloc(1, sizeof(php_ds_deque_t));
+
     zend_object_std_init(&obj->std, php_ds_deque_ce);
-    obj->std.handlers = &php_deque_handlers;
-    obj->deque = deque;
+
+    obj->std.handlers  = &php_deque_handlers;
+    obj->deque         = deque;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_deque.h b/src/php/objects/php_deque.h
index 5c95cfe..6034f52 100644
--- a/src/php/objects/php_deque.h
+++ b/src/php/objects/php_deque.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_deque.h"
 
-#define Z_DS_DEQUE(z)   ((php_ds_deque_t*) Z_OBJ(z))->deque
+#define Z_DS_DEQUE_OBJ(z)   ((php_ds_deque_t*) (Z_OBJ(z)))
+#define Z_DS_DEQUE_OBJ_P(z) Z_DS_DEQUE_OBJ(*z)
+
+#define Z_DS_DEQUE(z)   ((Z_DS_DEQUE_OBJ(z))->deque)
 #define Z_DS_DEQUE_P(z) Z_DS_DEQUE(*z)
-#define THIS_DS_DEQUE() Z_DS_DEQUE_P(getThis())
+
+#define THIS_DS_DEQUE()     Z_DS_DEQUE_P(getThis())
+#define THIS_DS_DEQUE_OBJ() Z_DS_DEQUE_OBJ_P(getThis())
 
 #define ZVAL_DS_DEQUE(z, d)  ZVAL_OBJ(z, php_ds_deque_create_object_ex(d))
 
@@ -27,6 +32,7 @@ do {                                        \
 typedef struct php_ds_deque {
     zend_object  std;
     ds_deque_t  *deque;
+    zend_ulong   iteratorCount;
 } php_ds_deque_t;
 
 /**
diff --git a/src/php/objects/php_map.c b/src/php/objects/php_map.c
index 820f452..bdf19ab 100644
--- a/src/php/objects/php_map.c
+++ b/src/php/objects/php_map.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_map_create_object_ex(ds_map_t *map)
 {
     php_ds_map_t *obj = ecalloc(1, sizeof(php_ds_map_t));
+
     zend_object_std_init(&obj->std, php_ds_map_ce);
-    obj->std.handlers = &php_map_handlers;
-    obj->map = map;
+
+    obj->std.handlers  = &php_map_handlers;
+    obj->map           = map;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_map.h b/src/php/objects/php_map.h
index 8d5ef0a..47218ae 100644
--- a/src/php/objects/php_map.h
+++ b/src/php/objects/php_map.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_map.h"
 
-#define Z_DS_MAP(z)   (((php_ds_map_t*)(Z_OBJ(z)))->map)
+#define Z_DS_MAP_OBJ(z)   ((php_ds_map_t*)(Z_OBJ(z)))
+#define Z_DS_MAP_OBJ_P(z) Z_DS_MAP_OBJ(*z)
+
+#define Z_DS_MAP(z)   ((Z_DS_MAP_OBJ(z))->map)
 #define Z_DS_MAP_P(z) Z_DS_MAP(*z)
-#define THIS_DS_MAP() Z_DS_MAP_P(getThis())
+
+#define THIS_DS_MAP()     Z_DS_MAP_P(getThis())
+#define THIS_DS_MAP_OBJ() Z_DS_MAP_OBJ_P(getThis())
 
 #define ZVAL_DS_MAP(z, map) ZVAL_OBJ(z, php_ds_map_create_object_ex(map))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct _php_ds_map_t {
     zend_object  std;
     ds_map_t    *map;
+    zend_ulong   iteratorCount;
 } php_ds_map_t;
 
 zend_object *php_ds_map_create_object_ex(ds_map_t *map);
diff --git a/src/php/objects/php_priority_queue.c b/src/php/objects/php_priority_queue.c
index 7fa50c0..4e90b6d 100644
--- a/src/php/objects/php_priority_queue.c
+++ b/src/php/objects/php_priority_queue.c
@@ -7,12 +7,14 @@
 zend_object *php_ds_priority_queue_create_object_ex(ds_priority_queue_t *queue)
 {
     php_ds_priority_queue_t *obj = ecalloc(1, sizeof(php_ds_priority_queue_t));
+
     zend_object_std_init(&obj->std, php_ds_priority_queue_ce);
-    obj->std.handlers = &php_priority_queue_handlers;
 
-    obj->queue   = queue;
-    obj->gc_data = NULL;
-    obj->gc_size = 0;
+    obj->std.handlers  = &php_priority_queue_handlers;
+    obj->queue         = queue;
+    obj->gc_data       = NULL;
+    obj->gc_size       = 0;
+    obj->iteratorCount = 0;
 
     return &obj->std;
 }
diff --git a/src/php/objects/php_priority_queue.h b/src/php/objects/php_priority_queue.h
index f7a38dc..8e71711 100644
--- a/src/php/objects/php_priority_queue.h
+++ b/src/php/objects/php_priority_queue.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_priority_queue.h"
 
-#define Z_DS_PRIORITY_QUEUE(z)   (((php_ds_priority_queue_t*)(Z_OBJ(z)))->queue)
+#define Z_DS_PRIORITY_QUEUE_OBJ(z)   ((php_ds_priority_queue_t*) (Z_OBJ(z)))
+#define Z_DS_PRIORITY_QUEUE_OBJ_P(z) Z_DS_PRIORITY_QUEUE_OBJ(*z)
+
+#define Z_DS_PRIORITY_QUEUE(z)   ((Z_DS_PRIORITY_QUEUE_OBJ(z))->queue)
 #define Z_DS_PRIORITY_QUEUE_P(z) Z_DS_PRIORITY_QUEUE(*z)
+
 #define THIS_DS_PRIORITY_QUEUE() Z_DS_PRIORITY_QUEUE_P(getThis())
+#define THIS_DS_PRIORITY_QUEUE_OBJ() Z_DS_PRIORITY_QUEUE_OBJ_P(getThis())
 
 #define ZVAL_DS_PRIORITY_QUEUE(z, queue) \
     ZVAL_OBJ(z, (php_ds_priority_queue_create_object_ex(queue)))
@@ -24,6 +29,7 @@ do {                                                    \
 typedef struct _php_ds_priority_queue_t {
     zend_object          std;
     ds_priority_queue_t *queue;
+    zend_ulong           iteratorCount;
     zval                *gc_data;
     int                  gc_size;
 
diff --git a/src/php/objects/php_queue.c b/src/php/objects/php_queue.c
index 7de656d..dcf4231 100644
--- a/src/php/objects/php_queue.c
+++ b/src/php/objects/php_queue.c
@@ -7,9 +7,12 @@
 zend_object *php_ds_queue_create_object_ex(ds_queue_t *queue)
 {
     php_ds_queue_t *obj = ecalloc(1, sizeof(php_ds_queue_t));
+
     zend_object_std_init(&obj->std, php_ds_queue_ce);
-    obj->std.handlers = &php_queue_handlers;
-    obj->queue = queue;
+
+    obj->std.handlers  = &php_queue_handlers;
+    obj->queue         = queue;
+    obj->iteratorCount = 0;
 
     return &obj->std;
 }
diff --git a/src/php/objects/php_queue.h b/src/php/objects/php_queue.h
index dc76385..a6506c2 100644
--- a/src/php/objects/php_queue.h
+++ b/src/php/objects/php_queue.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_queue.h"
 
-#define Z_DS_QUEUE(z)   (((php_ds_queue_t*)(Z_OBJ(z)))->queue)
+#define Z_DS_QUEUE_OBJ(z)   ((php_ds_queue_t*) (Z_OBJ(z)))
+#define Z_DS_QUEUE_OBJ_P(z) Z_DS_QUEUE_OBJ(*z)
+
+#define Z_DS_QUEUE(z)   ((Z_DS_QUEUE_OBJ(z))->queue)
 #define Z_DS_QUEUE_P(z) Z_DS_QUEUE(*z)
+
 #define THIS_DS_QUEUE() Z_DS_QUEUE_P(getThis())
+#define THIS_DS_QUEUE_OBJ() Z_DS_QUEUE_OBJ_P(getThis())
 
 #define ZVAL_DS_QUEUE(z, queue) \
     ZVAL_OBJ(z, php_ds_queue_create_object_ex(queue))
@@ -24,6 +29,7 @@ do {                                        \
 typedef struct _php_ds_queue_t {
     zend_object    std;
     ds_queue_t    *queue;
+    zend_ulong     iteratorCount;
 } php_ds_queue_t;
 
 zend_object *php_ds_queue_create_object_ex(ds_queue_t *queue);
diff --git a/src/php/objects/php_set.c b/src/php/objects/php_set.c
index 70d82c0..765335a 100644
--- a/src/php/objects/php_set.c
+++ b/src/php/objects/php_set.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_set_create_object_ex(ds_set_t *set)
 {
     php_ds_set_t *obj = ecalloc(1, sizeof(php_ds_set_t));
+
     zend_object_std_init(&obj->std, php_ds_set_ce);
-    obj->std.handlers = &php_ds_set_handlers;
-    obj->set = set;
+
+    obj->std.handlers  = &php_ds_set_handlers;
+    obj->set           = set;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_set.h b/src/php/objects/php_set.h
index 8f94b33..ceaac38 100644
--- a/src/php/objects/php_set.h
+++ b/src/php/objects/php_set.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_set.h"
 
-#define Z_DS_SET(z)   (((php_ds_set_t*)(Z_OBJ(z)))->set)
+#define Z_DS_SET_OBJ(z)   ((php_ds_set_t*) (Z_OBJ(z)))
+#define Z_DS_SET_OBJ_P(z) Z_DS_SET_OBJ(*z)
+
+#define Z_DS_SET(z)   ((Z_DS_SET_OBJ(z))->set)
 #define Z_DS_SET_P(z) Z_DS_SET(*z)
+
 #define THIS_DS_SET() Z_DS_SET_P(getThis())
+#define THIS_DS_SET_OBJ() Z_DS_SET_OBJ_P(getThis())
 
 #define ZVAL_DS_SET(z, set) ZVAL_OBJ(z, php_ds_set_create_object_ex(set))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct _php_ds_set_t {
     zend_object    std;
     ds_set_t      *set;
+    zend_ulong     iteratorCount;
 } php_ds_set_t;
 
 zend_object *php_ds_set_create_object_ex(ds_set_t *set);
diff --git a/src/php/objects/php_stack.c b/src/php/objects/php_stack.c
index 8b2ac61..0bd97b4 100644
--- a/src/php/objects/php_stack.c
+++ b/src/php/objects/php_stack.c
@@ -7,9 +7,13 @@
 zend_object *php_ds_stack_create_object_ex(ds_stack_t *stack)
 {
     php_ds_stack_t *obj = ecalloc(1, sizeof(php_ds_stack_t));
+
     zend_object_std_init(&obj->std, php_ds_stack_ce);
-    obj->std.handlers = &php_ds_stack_handlers;
-    obj->stack = stack;
+
+    obj->std.handlers  = &php_ds_stack_handlers;
+    obj->stack         = stack;
+    obj->iteratorCount = 0;
+
     return &obj->std;
 }
 
diff --git a/src/php/objects/php_stack.h b/src/php/objects/php_stack.h
index c34934b..cb4285d 100644
--- a/src/php/objects/php_stack.h
+++ b/src/php/objects/php_stack.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_stack.h"
 
-#define Z_DS_STACK(z)   (((php_ds_stack_t*)(Z_OBJ(z)))->stack)
+#define Z_DS_STACK_OBJ(z)   ((php_ds_stack_t*) (Z_OBJ(z)))
+#define Z_DS_STACK_OBJ_P(z) Z_DS_STACK_OBJ(*z)
+
+#define Z_DS_STACK(z)   ((Z_DS_STACK_OBJ(z))->stack)
 #define Z_DS_STACK_P(z) Z_DS_STACK(*z)
+
 #define THIS_DS_STACK() Z_DS_STACK_P(getThis())
+#define THIS_DS_STACK_OBJ() Z_DS_STACK_OBJ_P(getThis())
 
 #define ZVAL_DS_STACK(z, s)  ZVAL_OBJ(z, php_ds_stack_create_object_ex(s))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct _php_ds_stack_t {
     zend_object      std;
     ds_stack_t      *stack;
+    zend_ulong       iteratorCount;
 } php_ds_stack_t;
 
 zend_object *php_ds_stack_create_object_ex(ds_stack_t *stack);
diff --git a/src/php/objects/php_vector.c b/src/php/objects/php_vector.c
index f415c43..d1ce490 100644
--- a/src/php/objects/php_vector.c
+++ b/src/php/objects/php_vector.c
@@ -7,9 +7,12 @@
 zend_object *php_ds_vector_create_object_ex(ds_vector_t *vector)
 {
     php_ds_vector_t *obj = ecalloc(1, sizeof(php_ds_vector_t));
+
     zend_object_std_init(&obj->std, php_ds_vector_ce);
-    obj->std.handlers = &php_vector_handlers;
-    obj->vector = vector;
+
+    obj->std.handlers  = &php_vector_handlers;
+    obj->vector        = vector;
+    obj->iteratorCount = 0;
 
     return &obj->std;
 }
diff --git a/src/php/objects/php_vector.h b/src/php/objects/php_vector.h
index 63b4b7f..9d28706 100644
--- a/src/php/objects/php_vector.h
+++ b/src/php/objects/php_vector.h
@@ -3,9 +3,14 @@
 
 #include "../../ds/ds_vector.h"
 
-#define Z_DS_VECTOR(z)   (((php_ds_vector_t*)(Z_OBJ(z)))->vector)
+#define Z_DS_VECTOR_OBJ(z)   ((php_ds_vector_t*) (Z_OBJ(z)))
+#define Z_DS_VECTOR_OBJ_P(z) Z_DS_VECTOR_OBJ(*z)
+
+#define Z_DS_VECTOR(z)   ((Z_DS_VECTOR_OBJ(z))->vector)
 #define Z_DS_VECTOR_P(z) Z_DS_VECTOR(*z)
+
 #define THIS_DS_VECTOR() Z_DS_VECTOR_P(getThis())
+#define THIS_DS_VECTOR_OBJ() Z_DS_VECTOR_OBJ_P(getThis())
 
 #define ZVAL_DS_VECTOR(z, v) ZVAL_OBJ(z, php_ds_vector_create_object_ex(v))
 
@@ -23,6 +28,7 @@ do {                                        \
 typedef struct php_ds_vector {
     zend_object      std;
     ds_vector_t     *vector;
+    zend_ulong       iteratorCount;
 } php_ds_vector_t;
 
 zend_object *php_ds_vector_create_object_ex(ds_vector_t *vector);
diff --git a/watch.sh b/watch.sh
new file mode 100755
index 0000000..fb0c5b7
--- /dev/null
+++ b/watch.sh
@@ -0,0 +1,36 @@
+#! /bin/bash
+FILE_PATTERN="./**/*.h ./**/*.c *.m4"
+
+# Wait for file changes.
+function wait_for_changes {
+    inotifywait -r -e modify,move,create,delete $FILE_PATTERN
+}
+
+function clean {
+    make clean
+    phpize --clean
+}
+
+function init {
+    phpize
+    ./configure
+}
+
+function build {
+    clear
+    make install -j
+}
+
+function test {
+    php test.php
+}
+
+# Setup and intial run.
+clean
+init
+build
+
+while wait_for_changes; do
+    build
+    test
+done
