summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md12
-rw-r--r--documentation/calling-functions-and-methods.html2
-rw-r--r--documentation/exceptions.html9
-rw-r--r--documentation/magic-methods-and-interfaces.html21
-rw-r--r--include/class.h14
-rw-r--r--include/classbase.h19
-rw-r--r--include/interface.h14
-rw-r--r--include/iterator.h140
-rw-r--r--include/traversable.h8
-rw-r--r--include/value.h1
-rw-r--r--phpcpp.h2
-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
16 files changed, 511 insertions, 73 deletions
diff --git a/README.md b/README.md
index 24f81ec..601487a 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@ PHP-CPP
The PHP-CPP library is a C++ library for developing PHP extensions. It offers a collection
of well documented and easy-to-use classes that can be used and extended to build native
-extensions for PHP.
+extensions for PHP. The full documentation can be found on http://www.php-cpp.com.
Unlike regular PHP extensions - which are really hard to implement and require a deep
knowledge of the Zend engine and pointer manipulation - extensions built with PHP-CPP
@@ -81,11 +81,5 @@ function get_complex_array()
}
```
-However, this library is currently a work in progress, and it is an open
-source project. We are looking for people who'd like to contribute to it.
-
-PHP-CPP is an initiative from Copernica BV.
-
-For more information, contact me at [emiel.bruijntjes@copernica.com](mailto:emiel.bruijntjes@copernica.com?Subject=PHP-CPP).
-
-Emiel Bruijntjes (1 September 2013)
+More information and more examples are available on the official website:
+http://www.php-cpp.com. \ No newline at end of file
diff --git a/documentation/calling-functions-and-methods.html b/documentation/calling-functions-and-methods.html
index 8e2e7ba..d3bc3e2 100644
--- a/documentation/calling-functions-and-methods.html
+++ b/documentation/calling-functions-and-methods.html
@@ -114,7 +114,7 @@ extern "C" {
is used in the example to call the PHP method DateTime::format().
</p>
<p>
- In PHP scripts you can create an array with two members: and object and
+ In PHP scripts you can create an array with two members: an object and
the name of a method. This array can then be used as if it was a regular
function. You can do similar things in C++, as we showed in the example with the
"time_format" variable.
diff --git a/documentation/exceptions.html b/documentation/exceptions.html
index f8af4d0..56e7a31 100644
--- a/documentation/exceptions.html
+++ b/documentation/exceptions.html
@@ -12,6 +12,11 @@
<p>
<pre class="language-c++"><code>#include &lt;phpcpp.h&gt;
+/**
+ * Simple function that takes two numeric parameters,
+ * and that divides them. Division by zero is of course
+ * not permitted - it will throw an exception then
+ */
Php::Value myDiv(Php::Parameters &params)
{
// division by zero is not permitted, throw an exception when this happens
@@ -75,7 +80,7 @@ exception caught
</p>
<h2>Catching exceptions in C++</h2>
<p>
- And this works the other way around too. If your extensions calls a PHP
+ And this works the other way around too. If your extension calls a PHP
function, and that PHP function happens to throw an exception, you can catch it
just as if it was a normal C++ exception.
</p>
@@ -111,7 +116,7 @@ extern "C" {
be used just like a normal PHP $variable is used, and you can thus store
integers, strings, objects, arrays, et cetera in it. But this also means
that you can use it to store functions - because PHP variables can
- be used to store function too! And that's exactly what we're doing here.
+ be used to store functions too! And that's exactly what we're doing here.
</p>
<p>
The callMe() function from this example extension receives one single
diff --git a/documentation/magic-methods-and-interfaces.html b/documentation/magic-methods-and-interfaces.html
index 93c76a6..6ece6a5 100644
--- a/documentation/magic-methods-and-interfaces.html
+++ b/documentation/magic-methods-and-interfaces.html
@@ -159,11 +159,11 @@ echo(count($counter)."\n");
<p>
A PHP object can be turned into a variable that behaves like an array by
implementing the Php::ArrayAccess interface. When you do this, objects
- can be accessed by with the array access operator ($object["property"]).
+ can be accessed with array access operators ($object["property"]).
</p>
<p>
In the following example we use the Php::Countable and the Php::ArrayAccess
- interfaces to create a class that acts like an associative array than can
+ interfaces to create an associative array class than can
be used for storing strings (remember: this is just an example, PHP already
has support for associative arrays, so it is debatable how useful the
example is).
@@ -278,10 +278,12 @@ extern "C" {
to be forwarded to a regular C++ std::map object.
</p>
<p>
- The Map object from the example does not have any regular methods at all.
- It only implements the Php::Countable interface and Php::ArrayAccess interface,
- so it is perfectly usable to store and retrieve properties, but it does not
- have any methods. The following script shows how to use it.
+ Inside the get_module() function, the Map is registered and added to the
+ extension. But unlike many other examples, none of the class methods are
+ exported to PHP. It only implements the Php::Countable interface and
+ Php::ArrayAccess interface, so it is perfectly usable to store and retrieve
+ properties, but from a PHP script it does not have any callable methods.
+ The following script shows how to use it.
</p>
<p>
<pre class="language-php"><code>
@@ -292,7 +294,7 @@ $map = new Map();
// store some values
$map["a"] = 1234;
$map["b"] = "xyz";
-$map["c"] = new stdClass();
+$map["c"] = 0;
// show the values
echo($map["a"]."\n");
@@ -301,6 +303,11 @@ echo($map["c"]."\n");
// access a value that does not exist
echo($map["d"]."\n");
+
?&gt;
</pre></code>
</p>
+<p>
+ The output speaks for itself. The map has three members, "1234" (a string
+ variable), "xyz" and "0".
+</p>
diff --git a/include/class.h b/include/class.h
index e9a7179..6bcefda 100644
--- a/include/class.h
+++ b/include/class.h
@@ -154,7 +154,7 @@ private:
* Construct a new instance of the object
* @return Base
*/
- virtual Base* construct() override
+ virtual Base* construct() const override
{
// construct an instance
return new T();
@@ -165,7 +165,7 @@ private:
* @param orig
* @return Base
*/
- virtual Base *clone(Base *orig) override
+ virtual Base *clone(Base *orig) const override
{
// cast to the original object
T *t = (T *)orig;
@@ -175,6 +175,16 @@ private:
}
/**
+ * Is this class traversable?
+ * @return bool
+ */
+ virtual bool traversable() const override
+ {
+ // check if the templated class overrides from the base
+ return std::is_base_of<Traversable,T>::value;
+ }
+
+ /**
* Namespaces have access to the private base class
*/
friend class Namespace;
diff --git a/include/classbase.h b/include/classbase.h
index 81454ba..1be2538 100644
--- a/include/classbase.h
+++ b/include/classbase.h
@@ -97,14 +97,20 @@ public:
* Construct a new instance of the object
* @return Base
*/
- virtual Base* construct() = 0;
+ virtual Base* construct() const = 0;
/**
* Create a clone of an object
* @param orig
* @return Base
*/
- virtual Base *clone(Base *orig) = 0;
+ virtual Base *clone(Base *orig) const = 0;
+
+ /**
+ * Is this a traversable class?
+ * @return bool
+ */
+ virtual bool traversable() const = 0;
/**
* Initialize the class, given its name
@@ -283,6 +289,15 @@ private:
static struct _zend_object_handlers *objectHandlers();
/**
+ * 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
+ */
+ static struct _zend_object_iterator *getIterator(struct _zend_class_entry *entry, struct _zval_struct *object, int by_ref);
+
+ /**
* Name of the class
* @var string
*/
diff --git a/include/interface.h b/include/interface.h
index b3031e9..bdff75d 100644
--- a/include/interface.h
+++ b/include/interface.h
@@ -43,7 +43,7 @@ private:
* Construct a new instance of the object
* @return Base
*/
- virtual Base* construct() override
+ virtual Base* construct() const override
{
// this does not occur for interfaces
return nullptr;
@@ -54,11 +54,21 @@ private:
* @param orig
* @return Base
*/
- virtual Base* clone(Base *orig) override
+ virtual Base* clone(Base *orig) const override
{
// this does not occur for interfaces
return nullptr;
}
+
+ /**
+ * Is this a traversable interface?
+ * @return bool
+ */
+ virtual bool traversable() const override
+ {
+ // interfaces are never traversed
+ return false;
+ }
/**
* Namespaces have access to the private base class
diff --git a/include/iterator.h b/include/iterator.h
new file mode 100644
index 0000000..3c8f870
--- /dev/null
+++ b/include/iterator.h
@@ -0,0 +1,140 @@
+/**
+ * Iterator.h
+ *
+ * Base class for iterators. Extension writers that want to create traversable
+ * classes, should override this class and implement all pure virtual methods
+ * in it.
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Forward declarations
+ */
+struct _zend_object_iterator_funcs;
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class Iterator
+{
+public:
+ /**
+ * Constructor
+ * @param base Class over which the iterator is iterating
+ */
+ Iterator(Base *base) : _object(base) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~Iterator() {}
+
+ /**
+ * Is the iterator on a valid position
+ * @return bool
+ */
+ virtual bool valid() = 0;
+
+ /**
+ * The value at the current position
+ * @return Value
+ */
+ virtual Value current() = 0;
+
+ /**
+ * The key at the current position
+ * @return Value
+ */
+ virtual Value key() = 0;
+
+ /**
+ * Move to the next position
+ */
+ virtual void next() = 0;
+
+ /**
+ * Rewind the iterator to the front position
+ */
+ virtual void rewind() = 0;
+
+private:
+ /**
+ * During the lifetime of the iterator, the object over which
+ * it iterates is keps as a private variable. This ensures that
+ * this object is not destructed as long as the iterator exists
+ * @var Value
+ */
+ Value _object;
+
+ /**
+ * Internal method that returns the implementation object
+ * @return zend_object_iterator
+ */
+ struct _zend_object_iterator *implementation();
+
+ /**
+ * Iterator destructor method
+ * @param iter
+ */
+ static void destructor(struct _zend_object_iterator *iter);
+
+ /**
+ * Iterator valid function
+ * Returns FAILURE or SUCCESS
+ * @param iter
+ * @return int
+ */
+ static int valid(struct _zend_object_iterator *iter);
+
+ /**
+ * Fetch the current item
+ * @param iter
+ * @param data
+ */
+ static void current(struct _zend_object_iterator *iter, struct _zval_struct ***data);
+
+ /**
+ * 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 data
+ */
+ static void key(struct _zend_object_iterator *iter, struct _zval_struct *data);
+
+ /**
+ * Step forwards to the next element
+ * @param iter
+ */
+ static void next(struct _zend_object_iterator *iter);
+
+ /**
+ * Rewind the iterator back to the start
+ * @param iter
+ */
+ static void rewind(struct _zend_object_iterator *iter);
+
+ /**
+ * Get access to all iterator functions
+ * @return zend_object_iterator_funcs
+ */
+ static struct _zend_object_iterator_funcs *functions();
+
+ /**
+ * Classbase is a friend
+ */
+ friend class ClassBase;
+};
+
+/**
+ * End namespace
+ */
+}
diff --git a/include/traversable.h b/include/traversable.h
index 57cc9bc..f81a5b4 100644
--- a/include/traversable.h
+++ b/include/traversable.h
@@ -19,8 +19,12 @@ namespace Php {
*/
class Traversable
{
-
-
+public:
+ /**
+ * Retrieve an instance of the iterator
+ * @return Iterator
+ */
+ virtual Iterator *getIterator() = 0;
};
/**
diff --git a/include/value.h b/include/value.h
index a1dd593..2ffbd7c 100644
--- a/include/value.h
+++ b/include/value.h
@@ -862,6 +862,7 @@ protected:
friend class Globals;
friend class Member;
friend class ClassBase;
+ friend class Iterator;
};
/**
diff --git a/phpcpp.h b/phpcpp.h
index 207d329..15a5edd 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -42,6 +42,8 @@
#include <phpcpp/base.h>
#include <phpcpp/countable.h>
#include <phpcpp/arrayaccess.h>
+#include <phpcpp/iterator.h>
+#include <phpcpp/traversable.h>
#include <phpcpp/classtype.h>
#include <phpcpp/classbase.h>
#include <phpcpp/class.h>
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