diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/base.cpp | 5 | ||||
-rw-r--r-- | src/classbase.cpp | 28 | ||||
-rw-r--r-- | src/includes.h | 2 | ||||
-rw-r--r-- | src/iterator.cpp | 211 | ||||
-rw-r--r-- | src/value.cpp | 96 |
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 |