summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/base.cpp5
-rw-r--r--src/classbase.cpp28
-rw-r--r--src/includes.h2
-rw-r--r--src/iterator.cpp211
-rw-r--r--src/value.cpp96
5 files changed, 296 insertions, 46 deletions
diff --git a/src/base.cpp b/src/base.cpp
index a96a6ce..305821f 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -50,8 +50,10 @@ MixedObject *Base::store(zend_class_entry *entry)
// store the class entry in the newly created object
result->php.ce = entry;
+
+ // @todo is this really necessary - and when do we destruct this data?
+ // (if we remove this code, everything breaks down in a for ($object as $k => $v) loop)
-#if PHP_VERSION_ID < 50399
// the original create_object fills the initial object with the default properties,
// we're going to do exactly the same. start with setting up a hashtable for the props
ALLOC_HASHTABLE(result->php.properties);
@@ -59,6 +61,7 @@ MixedObject *Base::store(zend_class_entry *entry)
// initialize the hash table
zend_hash_init(result->php.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+#if PHP_VERSION_ID < 50399
// initialize the properties
zend_hash_copy(result->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
#else
diff --git a/src/classbase.cpp b/src/classbase.cpp
index fe08bd1..8926b3e 100644
--- a/src/classbase.cpp
+++ b/src/classbase.cpp
@@ -359,6 +359,30 @@ zend_object_value ClassBase::createObject(zend_class_entry *entry TSRMLS_DC)
}
/**
+ * Function to create a new iterator to iterate over an object
+ * @param entry The class entry
+ * @param object The object to iterate over
+ * @param by_ref ?????
+ * @return zend_object_iterator* Pointer to the iterator
+ */
+zend_object_iterator *ClassBase::getIterator(zend_class_entry *entry, zval *object, int by_ref)
+{
+ std::cout << "call to getIterator" << std::endl;
+
+ // by-ref is not possible (copied from SPL)
+ if (by_ref) throw Php::Exception("Foreach by ref is not possible");
+
+ // retrieve the traversable object
+ Traversable *traversable = dynamic_cast<Traversable*>(cpp_object(object));
+
+ // create an iterator
+ auto *iterator = traversable->getIterator();
+
+ // return the implementation
+ return iterator->implementation();
+}
+
+/**
* Destructor
*/
ClassBase::~ClassBase()
@@ -436,6 +460,10 @@ void ClassBase::initialize(const std::string &prefix)
// we need a special constructor
entry.create_object = &ClassBase::createObject;
+ // and a special function for retrieving the iterator (but only if this is
+ // a traversable class)
+ if (traversable()) entry.get_iterator = &ClassBase::getIterator;
+
// register the class
_entry = zend_register_internal_class(&entry TSRMLS_CC);
diff --git a/src/includes.h b/src/includes.h
index 67a7749..24b4837 100644
--- a/src/includes.h
+++ b/src/includes.h
@@ -60,6 +60,8 @@
#include "../include/base.h"
#include "../include/countable.h"
#include "../include/arrayaccess.h"
+#include "../include/iterator.h"
+#include "../include/traversable.h"
#include "../include/classtype.h"
#include "../include/classbase.h"
#include "../include/class.h"
diff --git a/src/iterator.cpp b/src/iterator.cpp
new file mode 100644
index 0000000..c0e9c3d
--- /dev/null
+++ b/src/iterator.cpp
@@ -0,0 +1,211 @@
+/**
+ * IteratorImpl.cpp
+ *
+ * Implementation file of the IteratorImpl class
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Iterator destructor method
+ * @param iter
+ */
+void Iterator::destructor(zend_object_iterator *iter)
+{
+ std::cout << "destruct iterator" << std::endl;
+
+ // get the actual iterator
+ Iterator *iterator = (Iterator *)iter->data;
+
+ // delete the iterator
+ delete iterator;
+
+ // free memory for the meta object
+ efree(iter);
+}
+
+/**
+ * Iterator valid function
+ * Returns FAILURE or SUCCESS
+ * @param iter
+ * @return int
+ */
+int Iterator::valid(zend_object_iterator *iter)
+{
+ std::cout << "Iterator::valid" << std::endl;
+
+ // get the actual iterator
+ Iterator *iterator = (Iterator *)iter->data;
+
+ // check if valid
+ return iterator->valid() ? SUCCESS : FAILURE;
+}
+
+/**
+ * Fetch the current item
+ * @param iter
+ * @param data
+ */
+void Iterator::current(zend_object_iterator *iter, zval ***data)
+{
+ std::cout << "get current value " << std::endl;
+
+ // get the actual iterator
+ Iterator *iterator = (Iterator *)iter->data;
+
+ // retrieve the value
+ Value value(iterator->current());
+
+ std::cout << "detach value " << value << std::endl;
+
+ zval *val = value.detach();
+
+ // copy the value
+ *data = &val;
+}
+
+/**
+ * Fetch the key for the current element (optional, may be NULL). The key
+ * should be written into the provided zval* using the ZVAL_* macros. If
+ * this handler is not provided auto-incrementing integer keys will be
+ * used.
+ * @param iter
+ * @param retval
+ */
+void Iterator::key(zend_object_iterator *iter, zval *retval)
+{
+ // get the actual iterator
+ Iterator *iterator = (Iterator *)iter->data;
+
+ // wrap data into a result object
+// Value result(data);
+
+// ZVAL_LONG(data, 123);
+
+// return;
+// std::cout << "retrieve key " << result.refcount() << std::endl;
+
+ // retrieve the key as key
+ Value keyValue = iterator->key();
+
+ std::cout << "got key " << keyValue << " " << keyValue.refcount() << std::endl;
+
+// zval *zval = key.detach();
+
+
+ std::cout << "ret key " << retval << " " << Z_REFCOUNT_P(retval) << std::endl;
+
+ ZVAL_LONG(retval, rand());
+
+// ZVAL_ZVAL(data, zval, 1, 1);
+
+ return;
+
+ // copy the key into the other zval, but we use a string or numeric for
+ // this operation, because we have looked at the implementation of Value
+ // and assigning a full value to the result variable will cause the zval
+ // to be destructed and re-allocated (which we do not need)
+// if (key.isString()) ZVAL_STRING(data, key.stringValue();
+// else ZVAL_LONG(data, key.numericValue());
+//
+// std::cout << "key is copied" << std::endl;
+//
+// // detach from result
+// result.detach();
+//
+ std::cout << "detached" << std::endl;
+}
+
+/**
+ * Step forwards to the next element
+ * @param iter
+ */
+void Iterator::next(zend_object_iterator *iter)
+{
+ std::cout << "Iterator::next" << std::endl;
+
+ // get the actual iterator
+ Iterator *iterator = (Iterator *)iter->data;
+
+ // call the next method
+ iterator->next();
+}
+
+/**
+ * Rewind the iterator back to the start
+ * @param iter
+ */
+void Iterator::rewind(zend_object_iterator *iter)
+{
+ std::cout << "Iterator::rewind" << std::endl;
+
+ // get the actual iterator
+ Iterator *iterator = (Iterator *)iter->data;
+
+ // call the rewind method
+ iterator->rewind();
+}
+
+/**
+ * Get access to all iterator functions
+ * @return zend_object_iterator_funcs
+ */
+zend_object_iterator_funcs *Iterator::functions()
+{
+ // static variable with all functions
+ static zend_object_iterator_funcs funcs;
+
+ // static variable that knows if the funcs are already initialized
+ static bool initialized = false;
+
+ // no need to set anything if already initialized
+ if (initialized) return &funcs;
+
+ // set the members
+ funcs.dtor = &Iterator::destructor;
+ funcs.valid = &Iterator::valid;
+ funcs.get_current_data = &Iterator::current;
+ funcs.get_current_key = &Iterator::key;
+ funcs.move_forward = &Iterator::next;
+ funcs.rewind = &Iterator::rewind;
+
+ // invalidate is not yet supported
+ funcs.invalidate_current = nullptr;
+
+ // remember that functions are initialized
+ initialized = true;
+
+ // done
+ return &funcs;
+}
+
+/**
+ * Internal method that returns the implementation object
+ * @return zend_object_iterator
+ */
+struct _zend_object_iterator *Iterator::implementation()
+{
+ // create an iterator
+ zend_object_iterator *iterator = (zend_object_iterator *)emalloc(sizeof(zend_object_iterator));
+
+ // initialize all properties
+ iterator->data = this;
+ iterator->index = 0;
+ iterator->funcs = functions();
+
+ // done
+ return iterator;
+}
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/src/value.cpp b/src/value.cpp
index 752d77e..f8ee3e9 100644
--- a/src/value.cpp
+++ b/src/value.cpp
@@ -1467,67 +1467,73 @@ int Value::size() const
*/
std::map<std::string,Php::Value> Value::mapValue() const
{
- // loop through the zval key/value pairs, and return a map
// result variable
std::map<std::string,Php::Value> result;
// check type
- if (isArray() || isObject())
+ if (isArray())
{
- zval **value;
- char *key;
- unsigned long ind;
+ // get access to the hast table
+ HashTable *arr = Z_ARRVAL_P(_val);
- // get access to the internal hash table of _val
- // see Zend/zend_API.h 723: HASH_OF(_val)
- HashTable *arr = isArray() ? Z_ARRVAL_P(_val) : Z_OBJ_HT_P(_val)->get_properties((_val) TSRMLS_CC);
+ // reset iterator to beginning of the hash table
+ zend_hash_internal_pointer_reset(arr);
+ // pointer that will be set to hash key and index
+ char *key;
+ unsigned long ind;
- // similarly php: reset($array):
- // The definition of this and the following functions can be found in Zend/zend_hash.h 174
- // Maybe make it optional?
- // If the following line to remove, then repeated calling the Value::mapValue() will return an empty map
- zend_hash_internal_pointer_reset(arr);
+ // key type
+ int hash_key_type;
- if (isArray())
+ // loop through the recors
+ while ((hash_key_type = zend_hash_get_current_key(arr, &key, &ind, 0)) != HASH_KEY_NON_EXISTENT)
{
- unsigned int hash_key_type;
- while( (hash_key_type = zend_hash_get_current_key(arr, &key, &ind, 0)) != HASH_KEY_NON_EXISTENT )
- {
- zend_hash_get_current_data(arr, (void **) &value);
+ // required variable
+ zval **value;
+
+ // retrieve data
+ zend_hash_get_current_data(arr, (void **) &value);
- if(HASH_KEY_IS_LONG == hash_key_type)
- {
- result[std::to_string(ind)] = Value(*value);
- }
- else // hash_key_type == HASH_KEY_IS_STRING
- {
- result[key] = Value(*value);
- }
-
- // next iteration
- zend_hash_move_forward(arr);
- }
+ // check the type of key
+ if (HASH_KEY_IS_LONG != hash_key_type) result[key] = Value(*value);
+ else result[std::to_string(ind)] = Value(*value);
+
+ // next iteration
+ zend_hash_move_forward(arr);
}
- else
+ }
+ 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);
+
+ // pointer that will be set to hash key and index
+ char *key;
+ unsigned long ind;
+
+ // loop through the records
+ while( zend_hash_get_current_key(arr, &key, &ind, 0) != HASH_KEY_NON_EXISTENT )
{
- // For obtaining a hashtable of the object meets function void rebuild_object_properties(zend_object *zobj)
- // Zend/zend_object_handlers.c 66
- // hashtable of object's properties always has string (no integer) keys
- while( zend_hash_get_current_key(arr, &key, &ind, 0) != HASH_KEY_NON_EXISTENT )
+ // if property is accessible (i.e. propertie access type is public. See rebuild_object_properties )
+ if('\0' != *key)
{
- // if propertie is accessible (i.e. propertie access type is public. See rebuild_object_properties )
- if('\0' != *key)
- {
- zend_hash_get_current_data(arr, (void **) &value);
- result[key] = Value(*value);
- }
-
- // next iteration
- zend_hash_move_forward(arr);
+ // required variable
+ zval **value;
+
+ // retrieve property
+ zend_hash_get_current_data(arr, (void **) &value);
+
+ // append to mape
+ result[key] = Value(*value);
}
- }
+ // next iteration
+ zend_hash_move_forward(arr);
+ }
}
// done