summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-04 15:11:46 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-04 15:11:46 +0100
commit200952ad4004f6ee5527598622505adbe84df8af (patch)
treea4fb92a86730c771e36abf421835ab01861ef41c
parent59cfe935248918c1151b300eb19496b76ed579a9 (diff)
implemented cloning of objects
-rw-r--r--Examples/CppClassesInPhp/cppclassinphp.cpp9
-rw-r--r--Examples/CppClassesInPhp/cppclassinphp.php18
-rw-r--r--include/class.h14
-rw-r--r--include/classbase.h27
-rw-r--r--include/interface.h11
-rw-r--r--src/classbase.cpp156
6 files changed, 190 insertions, 45 deletions
diff --git a/Examples/CppClassesInPhp/cppclassinphp.cpp b/Examples/CppClassesInPhp/cppclassinphp.cpp
index 95f909b..20c04c4 100644
--- a/Examples/CppClassesInPhp/cppclassinphp.cpp
+++ b/Examples/CppClassesInPhp/cppclassinphp.cpp
@@ -14,14 +14,17 @@ using namespace std;
class MyCustomClass : public Php::Base // , public Php::Countable
{
private:
- int _x;
+ int _x = 3;
public:
MyCustomClass()
{
- _x = 3;
std::cout << "MyCustomClass::MyCustomClass" << std::endl;
- std::cout << _x << std::endl;
+ }
+
+ MyCustomClass(const MyCustomClass &that)
+ {
+ std::cout << "MyCustomClass::MyCustomClass copy constructor" << std::endl;
}
virtual ~MyCustomClass()
diff --git a/Examples/CppClassesInPhp/cppclassinphp.php b/Examples/CppClassesInPhp/cppclassinphp.php
index c969a4c..7fd4f64 100644
--- a/Examples/CppClassesInPhp/cppclassinphp.php
+++ b/Examples/CppClassesInPhp/cppclassinphp.php
@@ -18,7 +18,23 @@ class TestClass
}
//create a MyCustomClass object, which is an object of a C++ class
-$object = new MyClass();
+$object1 = new MyClass();
+
+//echo("prop x: ".$object1->x."\n");
+
+$object1->x = 10;
+$object1->y = 20;
+
+echo("prop x: ".$object1->x."\n");
+echo("prop y: ".$object1->y."\n");
+
+$object2 = clone $object1;
+
+echo("prop x: ".$object2->x."\n");
+echo("prop y: ".$object2->y."\n");
+
+
+return;
// run a function of the class
$obj = $object->myMethod("MyClass");
diff --git a/include/class.h b/include/class.h
index 8bfcad4..7d82441 100644
--- a/include/class.h
+++ b/include/class.h
@@ -139,6 +139,20 @@ private:
}
/**
+ * Construct a clone
+ * @param orig
+ * @return Base
+ */
+ virtual Base *clone(Base *orig) override
+ {
+ // cast to the original object
+ T *t = (T *)orig;
+
+ // construct a new base by calling the copy constructor
+ return new T(*t);
+ }
+
+ /**
* Namespaces have access to the private base class
*/
friend class Namespace;
diff --git a/include/classbase.h b/include/classbase.h
index e23c579..a380840 100644
--- a/include/classbase.h
+++ b/include/classbase.h
@@ -98,12 +98,39 @@ public:
// done
return result;
}
+
+ /**
+ * Construct a clone of the object
+ * @param orig
+ * @param value
+ * @return Base
+ */
+ Base* clone(Base *orig, const struct _zend_object_value &value)
+ {
+ // construct the base
+ auto *result = clone(orig);
+ if (!result) return nullptr;
+
+ // assign the zend object to it
+ // @todo fix this
+// result->assign(value);
+
+ // done
+ return result;
+ }
/**
* Construct a new instance of the object
* @return Base
*/
virtual Base* construct() = 0;
+
+ /**
+ * Create a clone of an object
+ * @param orig
+ * @return Base
+ */
+ virtual Base *clone(Base *orig) = 0;
/**
* Initialize the class, given its name
diff --git a/include/interface.h b/include/interface.h
index f276d6f..b3031e9 100644
--- a/include/interface.h
+++ b/include/interface.h
@@ -50,6 +50,17 @@ private:
}
/**
+ * Construct a clone of the object
+ * @param orig
+ * @return Base
+ */
+ virtual Base* clone(Base *orig) override
+ {
+ // this does not occur for interfaces
+ return nullptr;
+ }
+
+ /**
* Namespaces have access to the private base class
*/
friend class Namespace;
diff --git a/src/classbase.cpp b/src/classbase.cpp
index af707c4..debfa7d 100644
--- a/src/classbase.cpp
+++ b/src/classbase.cpp
@@ -37,71 +37,145 @@ static void deallocate_object(void *object TSRMLS_DC)
}
/**
- * Function that is called to create space for a cloned object
- * @param object The object to be cloned
- * @param clone The address that should become the clone
- */
-static void clone_object(void *object, void **clone TSRMLS_DC)
-{
- // @todo implementation
-}
-
-/**
- * Function that is called when an instance of the class needs to be created.
- * This function will create the C++ class, and the PHP object
- * @param type Pointer to the class information
- * @return zend_object_value The newly created object
+ * Retrieve our C++ implementation object
+ * @param entry
+ * @return ClassBase
*/
-static zend_object_value create_object(zend_class_entry *type TSRMLS_DC)
+static ClassBase *cpp_class(zend_class_entry *entry)
{
- // allocate memory for the object
- MixedObject *object = (MixedObject *)emalloc(sizeof(MixedObject));
+ // we need the base class (in user space the class may have been overridden,
+ // but we are not interested in these user space classes)
+ while (entry->parent) entry = entry->parent;
- // find base object (because the class may have been extended in user space)
- zend_class_entry *base = type;
- while (base->parent) base = base->parent;
-
- // retrieve the classinfo object
#if PHP_VERSION_ID >= 50400
- const char *comment = base->info.user.doc_comment;
+ // retrieve the comment (it has a pointer hidden in it to the ClassBase object)
+ const char *comment = entry->info.user.doc_comment;
#else
- const char *comment = base->doc_comment;
+ // retrieve the comment php5.3 style (it has a pointer hidden in it to the ClassBase object)
+ const char *comment = entry->doc_comment;
#endif
// the first byte of the comment is an empty string (null character), but
- // the next bytes contain a pointer to the ClassInfo class
- ClassBase *info = *((ClassBase **)(comment + 1));
+ // the next bytes contain a pointer to the ClassBase class
+ return *((ClassBase **)(comment + 1));
+}
+
+/**
+ * Create an object based on a certain class entry
+ * @param entry
+ * @return MixedObject
+ */
+static MixedObject *allocate_object(zend_class_entry *entry)
+{
+ // allocate memory for the object
+ MixedObject *result = (MixedObject *)emalloc(sizeof(MixedObject));
- // store the class
- object->php.ce = type;
+ // store the class entry in the newly created object
+ result->php.ce = entry;
+#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(object->php.properties);
+ ALLOC_HASHTABLE(result->php.properties);
// initialize the hash table
- zend_hash_init(object->php.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+ zend_hash_init(result->php.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
-
// initialize the properties
-#if PHP_VERSION_ID < 50399
- zend_hash_copy(object->php.properties, &(type->default_properties),
- (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
+ zend_hash_copy(result->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
#else
- object_properties_init(&(object->php), type);
+ // version higher than 5.3 have an easier way to initialize
+ object_properties_init(&result->php, entry);
#endif
+
+ // done
+ return result;
+}
+
+/**
+ * Forward declaration
+ */
+static zend_object_value clone_object(zval *val TSRMLS_DC);
+
+/**
+ * Store the mixed object in the PHP object cache
+ * @param object The object to store
+ * @param value The zend_object_value to initialize
+ */
+static void store(MixedObject *object, zend_object_value *value)
+{
+ // set the handlers
+ value->handlers = zend_get_std_object_handlers();
+
+ // we need a special handler for cloning
+ value->handlers->clone_obj = clone_object;
+
+ // the destructor and clone handlers are set to NULL. I dont know why, but they do not
+ // seem to be necessary...
+ value->handle = zend_objects_store_put(object, NULL, deallocate_object, NULL TSRMLS_CC);
+}
+
+/**
+ * Function that is called to create space for a cloned object
+ * @param val The object to be cloned
+ * @return zend_obejct_value The object to be created
+ */
+static zend_object_value clone_object(zval *val TSRMLS_DC)
+{
+ // @todo refactoring because there is a lot of double code here
+
+ // retrieve the class entry linked to this object
+ auto *entry = zend_get_class_entry(val);
+
+ // allocate memory for the new object
+ MixedObject *new_object = allocate_object(entry);
+ // retrieve the old object, which we are going to copy
+ MixedObject *old_object = (MixedObject *)zend_object_store_get_object(val);
+
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
+
// the thing we're going to return
zend_object_value result;
+
+ // store the object
+ store(new_object, &result);
+
+ // clone the members
+ zend_objects_clone_members(&new_object->php, result, &old_object->php, Z_OBJ_HANDLE_P(val));
+
+ // finally, construct the cpp object
+ // @todo call this earlier, because it could fail
+ new_object->cpp = meta->clone(old_object->cpp, result);
- // use default object handlers
- result.handlers = zend_get_std_object_handlers();
+ // done
+ return result;
+}
+
+/**
+ * Function that is called when an instance of the class needs to be created.
+ * This function will create the C++ class, and the PHP object
+ * @param entry Pointer to the class information
+ * @return zend_object_value The newly created object
+ */
+static zend_object_value create_object(zend_class_entry *entry TSRMLS_DC)
+{
+ // allocate memory for the object
+ MixedObject *new_object = allocate_object(entry);
+
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
+
+ // the thing we're going to return
+ zend_object_value result;
- // put the object in the storage, and assign a method for deallocating and cloning
- result.handle = zend_objects_store_put(object, NULL, deallocate_object, clone_object TSRMLS_CC);
+ // store the object
+ store(new_object, &result);
// finally, construct the cpp object
- object->cpp = info->construct(result);
+ // @todo call this earlier because it could fail
+ new_object->cpp = meta->construct(result);
// done
return result;
@@ -115,7 +189,7 @@ ClassBase::~ClassBase()
// destruct the entries
if (_entries) delete[] _entries;
- // php 5.3 deallocated the doc_comment by iself
+ // php 5.3 deallocates the doc_comment by iself
#if PHP_VERSION_ID >= 50400
if (_comment) free(_comment);
#endif