summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Examples/CppClassesInPhp/cppclassinphp.cpp19
-rw-r--r--Examples/CppClassesInPhp/cppclassinphp.php12
-rw-r--r--include/base.h115
-rw-r--r--include/classbase.h60
-rw-r--r--include/object.h27
-rw-r--r--include/value.h6
-rw-r--r--src/base.cpp81
-rw-r--r--src/classbase.cpp138
-rw-r--r--src/object.cpp30
-rw-r--r--src/value.cpp28
10 files changed, 318 insertions, 198 deletions
diff --git a/Examples/CppClassesInPhp/cppclassinphp.cpp b/Examples/CppClassesInPhp/cppclassinphp.cpp
index 20c04c4..4c3d59f 100644
--- a/Examples/CppClassesInPhp/cppclassinphp.cpp
+++ b/Examples/CppClassesInPhp/cppclassinphp.cpp
@@ -19,7 +19,12 @@ private:
public:
MyCustomClass()
{
- std::cout << "MyCustomClass::MyCustomClass" << std::endl;
+ std::cout << "MyCustomClass::MyCustomClass()" << std::endl;
+ }
+
+ MyCustomClass(int value) : _x(value)
+ {
+ std::cout << "MyCustomClass::MyCustomClass(" << value << ")" << std::endl;
}
MyCustomClass(const MyCustomClass &that)
@@ -52,7 +57,14 @@ public:
// check number of parameters
if (params.size() != 1) throw Php::Exception("Invalid number of parameters supplied");
- std::cout << "myMethod is called." << std::endl;
+ std::cout << "myMethod is called for object " << _x << std::endl;
+
+
+ return Php::Object("MyClass", new MyCustomClass(100));
+
+
+ return false;
+
// create a new PHP DateTime object representing the current time
Php::Object now("DateTime", "now");
@@ -67,6 +79,9 @@ public:
std::cout << "return " << params[0] << std::endl;
+
+
+
// return it
return obj;
diff --git a/Examples/CppClassesInPhp/cppclassinphp.php b/Examples/CppClassesInPhp/cppclassinphp.php
index 7fd4f64..1c60878 100644
--- a/Examples/CppClassesInPhp/cppclassinphp.php
+++ b/Examples/CppClassesInPhp/cppclassinphp.php
@@ -20,6 +20,18 @@ class TestClass
//create a MyCustomClass object, which is an object of a C++ class
$object1 = new MyClass();
+
+$object2 = $object1->myMethod(1);
+$object2->myMethod(1);
+
+echo("unset\n");
+
+unset($object1);
+
+echo("got here\n");
+
+return;
+
//echo("prop x: ".$object1->x."\n");
$object1->x = 10;
diff --git a/include/base.h b/include/base.h
index 46f5678..b027d5a 100644
--- a/include/base.h
+++ b/include/base.h
@@ -11,6 +11,11 @@
namespace Php {
/**
+ * Forward declarations
+ */
+class MixedObject;
+
+/**
* Class definition
*/
class Base
@@ -22,109 +27,121 @@ protected:
Base() {}
public:
-
- // @todo should we delete the copy and move operators because we do not
- // allow the CPP code to make copies of itself?
-
-
/**
* Virtual destructor
*/
virtual ~Base() {}
/**
- * Convert the object to a Php::Value object (how it is used externally)
- * @return Object
- */
-// Object value();
-
- /**
- * Convert the object to a Php::Value object (how it is used externally)
- * @return Object
- */
-// Object value() const;
-
- /**
* Get access to a property by name using the [] operator
* @param string
* @return HashMember
*/
-// HashMember<std::string> operator[](const char *name)
-// {
-// return value()[name];
-// }
+ HashMember<std::string> operator[](const char *name)
+ {
+ return Value(this)[name];
+ }
/**
* Alternative way to access a property using the [] operator
* @param string
* @return HashMember
*/
-// HashMember<std::string> operator[](const std::string &name)
-// {
-// return value()[name];
-// }
+ HashMember<std::string> operator[](const std::string &name)
+ {
+ return Value(this)[name];
+ }
/**
* Retrieve a property by name
* @param string
* @return HashMember
*/
-// HashMember<std::string> property(const char *name)
-// {
-// return value()[name];
-// }
+ HashMember<std::string> property(const char *name)
+ {
+ return Value(this)[name];
+ }
/**
* Retrieve a property by name
* @param string
* @return HashMember
*/
-// HashMember<std::string> property(const std::string &name)
-// {
-// return value()[name];
-// }
+ HashMember<std::string> property(const std::string &name)
+ {
+ return Value(this)[name];
+ }
/**
* Get access to a property by name using the [] operator
* @param string
* @return Value
*/
-// Value operator[](const char *name) const
-// {
-// return value()[name];
-// }
+ Value operator[](const char *name) const
+ {
+ return Value(this)[name];
+ }
/**
* Alternative way to access a property using the [] operator
* @param string
* @return Value
*/
-// Value operator[](const std::string &name) const
-// {
-// return value()[name];
-// }
+ Value operator[](const std::string &name) const
+ {
+ return Value(this)[name];
+ }
/**
* Retrieve a property by name
* @param string
* @return Value
*/
-// Value property(const char *name) const
-// {
-// return value()[name];
-// }
+ Value property(const char *name) const
+ {
+ return Value(this)[name];
+ }
/**
* Retrieve a property by name
* @param string
* @return Value
*/
-// Value property(const std::string &name) const
-// {
-// return value()[name];
-// }
+ Value property(const std::string &name) const
+ {
+ return Value(this)[name];
+ }
private:
+ /**
+ * Store the object in the zend object cache
+ * @param entry
+ * @return MixedObject
+ */
+ MixedObject *store(struct _zend_class_entry *entry);
+
+ /**
+ * Retrieve the handle
+ * @return int
+ */
+ int handle() const
+ {
+ return _handle;
+ }
+
+ /**
+ * The handle in the zend object cache
+ * @var int
+ */
+ int _handle = 0;
+
+ /**
+ * Friends that have access to the private members
+ */
+ friend class Value;
+ friend class Object;
+ friend class ClassBase;
+
};
diff --git a/include/classbase.h b/include/classbase.h
index a380840..8a691fa 100644
--- a/include/classbase.h
+++ b/include/classbase.h
@@ -15,6 +15,11 @@
*/
/**
+ * Forward declarations
+ */
+struct _zend_object_value;
+
+/**
* Set up namespace
*/
namespace Php {
@@ -82,45 +87,6 @@ public:
/**
* Construct a new instance of the object
- * @param value
- * @return Base
- */
- Base* construct(const struct _zend_object_value &value)
- {
- // construct the base
- auto *result = construct();
- if (!result) return nullptr;
-
- // assign the zend object to it
- // @todo fix this
-// result->assign(value);
-
- // 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;
@@ -144,6 +110,7 @@ public:
* @param ns Namespace name
*/
void initialize(const std::string &ns);
+
protected:
/**
@@ -232,6 +199,21 @@ private:
const struct _zend_function_entry *entries();
/**
+ * Static member functions to clone objects based on this class
+ * @param val The object to be cloned
+ * @return zend_object_value Object info
+ */
+ static struct _zend_object_value cloneObject(struct _zval_struct *val);
+
+ /**
+ * 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 struct _zend_object_value createObject(struct _zend_class_entry *entry);
+
+ /**
* Name of the class
* @var string
*/
diff --git a/include/object.h b/include/object.h
index b3ea5bc..ff2f7c1 100644
--- a/include/object.h
+++ b/include/object.h
@@ -49,6 +49,33 @@ public:
}
/**
+ * Constructor to create a new instance of a builtin class
+ *
+ * You can use this constructor if you have created an instance of your
+ * own class, but has not assigned it to a variable yet. This happens
+ * for example for classes that are not constructed from PHP userspace,
+ * but from your own functions:
+ *
+ * Php::Value yourFunction()
+ * {
+ * return Php::Object("MyClass", new MyClass());
+ * }
+ *
+ * When you construct objects like this, the __construct function is not
+ * going to be called. If you want to construct the object just as if it
+ * was constructed from PHP user space, do this:
+ *
+ * Php::Value yourFunction()
+ * {
+ * return Php::Object("MyClass");
+ * }
+ *
+ * @param name Name of the class to instantiate
+ * @param base Implementation of the class
+ */
+ Object(const char *name, Base *base);
+
+ /**
* Constructor to create a new instance
*
* This constructor comes in many different forms, to support all possible
diff --git a/include/value.h b/include/value.h
index 8af95a5..d0079dc 100644
--- a/include/value.h
+++ b/include/value.h
@@ -67,10 +67,10 @@ public:
Value(struct _zval_struct *zval, bool ref = false);
/**
- * Wrap around an object
- * @param value The object value
+ * Wrap around an object implemented by us
+ * @param object Object to be wrapped
*/
- Value(const struct _zend_object_value &value);
+ Value(Base *base);
/**
* Copy constructor
diff --git a/src/base.cpp b/src/base.cpp
new file mode 100644
index 0000000..a96a6ce
--- /dev/null
+++ b/src/base.cpp
@@ -0,0 +1,81 @@
+/**
+ * Base.cpp
+ *
+ * Implementation file for the base of all classes
+ *
+ * @copyright 2014 Copernica BV
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Function that is called to clean up space that is occupied by the object
+ * @param object The object to be deallocated
+ */
+static void deallocate_object(void *object TSRMLS_DC)
+{
+ // allocate memory for the object
+ MixedObject *obj = (MixedObject *)object;
+
+ // deallocate the cpp object
+ if (obj->cpp) delete obj->cpp;
+
+ // get rid of the object properties
+ // @todo if we enable the following two lines, segmentation
+ // faults and memory corruption occurs. however, the online
+ // documentation does it like this
+ //zend_hash_destroy(obj->php.properties);
+ //FREE_HASHTABLE(obj->php.properties);
+
+ // deallocate the entire object
+ efree(obj);
+}
+
+/**
+ * Store the object in the PHP object cache
+ * @param entry Class entry
+ * @return MixedObject
+ */
+MixedObject *Base::store(zend_class_entry *entry)
+{
+ // allocate memory for the object
+ MixedObject *result = (MixedObject *)emalloc(sizeof(MixedObject));
+
+ // store the new c++ object
+ result->cpp = this;
+
+ // 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(result->php.properties);
+
+ // initialize the hash table
+ zend_hash_init(result->php.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
+
+ // initialize the properties
+ zend_hash_copy(result->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
+#else
+ // version higher than 5.3 have an easier way to initialize
+ object_properties_init(&result->php, entry);
+#endif
+
+ // the destructor and clone handlers are set to NULL. I dont know why, but they do not
+ // seem to be necessary...
+ _handle = zend_objects_store_put(result, NULL, deallocate_object, NULL TSRMLS_CC);
+
+ // done
+ return result;
+}
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/src/classbase.cpp b/src/classbase.cpp
index debfa7d..f9b58d0 100644
--- a/src/classbase.cpp
+++ b/src/classbase.cpp
@@ -14,29 +14,6 @@
namespace Php {
/**
- * Function that is called to clean up space that is occupied by the object
- * @param object The object to be deallocated
- */
-static void deallocate_object(void *object TSRMLS_DC)
-{
- // allocate memory for the object
- MixedObject *obj = (MixedObject *)object;
-
- // deallocate the cpp object
- if (obj->cpp) delete obj->cpp;
-
- // get rid of the object properties
- // @todo if we enable the following two lines, segmentation
- // faults and memory corruption occurs. however, the online
- // documentation does it like this
- //zend_hash_destroy(obj->php.properties);
- //FREE_HASHTABLE(obj->php.properties);
-
- // deallocate the entire object
- efree(obj);
-}
-
-/**
* Retrieve our C++ implementation object
* @param entry
* @return ClassBase
@@ -61,94 +38,45 @@ static ClassBase *cpp_class(zend_class_entry *entry)
}
/**
- * 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 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(result->php.properties);
-
- // initialize the hash table
- zend_hash_init(result->php.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
-
- // initialize the properties
- zend_hash_copy(result->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval*));
-#else
- // 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)
+zend_object_value ClassBase::cloneObject(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);
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
// retrieve the old object, which we are going to copy
MixedObject *old_object = (MixedObject *)zend_object_store_get_object(val);
+
+ // create a new base c++ object
+ auto *cpp = meta->clone(old_object->cpp);
- // we need the C++ class meta-information object
- ClassBase *meta = cpp_class(entry);
-
+ // report error on failure
+ if (!cpp) throw Php::Exception(std::string("Unable to clone ") + entry->name);
+
// the thing we're going to return
zend_object_value result;
+ // set the handlers
+ result.handlers = zend_get_std_object_handlers();
+
+ // we need a special handler for cloning
+ result.handlers->clone_obj = &ClassBase::cloneObject;
+
// store the object
- store(new_object, &result);
+ MixedObject *new_object = cpp->store(entry);
+ // store the object in the object cache
+ result.handle = cpp->handle();
+
// 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);
-
// done
return result;
}
@@ -159,24 +87,32 @@ static zend_object_value clone_object(zval *val TSRMLS_DC)
* @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)
+zend_object_value ClassBase::createObject(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);
+ // create a new base C++ object
+ auto *cpp = meta->construct();
+
+ // report error on failure
+ if (!cpp) throw Php::Exception(std::string("Unable to instantiate ") + entry->name);
+
// the thing we're going to return
zend_object_value result;
- // store the object
- store(new_object, &result);
+ // set the handlers
+ result.handlers = zend_get_std_object_handlers();
+
+ // we need a special handler for cloning
+ result.handlers->clone_obj = ClassBase::cloneObject;
- // finally, construct the cpp object
- // @todo call this earlier because it could fail
- new_object->cpp = meta->construct(result);
+ // store the object
+ cpp->store(entry);
+ // store the object in the object cache
+ result.handle = cpp->handle();
+
// done
return result;
}
@@ -257,7 +193,7 @@ void ClassBase::initialize(const std::string &prefix)
INIT_CLASS_ENTRY_EX(entry, _name.c_str(), _name.size(), entries());
// we need a special constructor
- entry.create_object = create_object;
+ entry.create_object = &ClassBase::createObject;
// register the class
_entry = zend_register_internal_class(&entry TSRMLS_CC);
diff --git a/src/object.cpp b/src/object.cpp
index aa99c0d..f1bcdf8 100644
--- a/src/object.cpp
+++ b/src/object.cpp
@@ -12,6 +12,36 @@
namespace Php {
/**
+ * Constructor to create a new instance of a builtin class
+ *
+ * @param name Name of the class to instantiate
+ * @param base Implementation of the class
+ */
+Object::Object(const char *name, Base *base)
+{
+ // does the object already have a handle?
+ if (base->handle())
+ {
+ // the object is already instantiated, we can assign it the this object
+ operator=(Value(base));
+ }
+ else
+ {
+ // this is a brand new object that should be allocated, the C++ instance
+ // is already there (created by the extension) but it is not yet stored
+ // in PHP, find out the classname first
+ auto *entry = zend_fetch_class(name, strlen(name), 0);
+ if (!entry) throw Php::Exception(std::string("Unknown class name ") + name);
+
+ // store the object in the php object cache (this will give the object a handle)
+ base->store(entry);
+
+ // now we can store it
+ operator=(Value(base));
+ }
+}
+
+/**
* Internal method to instantiate an object
* @param name
*/
diff --git a/src/value.cpp b/src/value.cpp
index 13c4d88..b43d72f 100644
--- a/src/value.cpp
+++ b/src/value.cpp
@@ -171,14 +171,34 @@ Value::Value(struct _zval_struct *val, bool ref)
/**
* Wrap around an object
- * @param value The object value
+ * @param object
*/
-Value::Value(const struct _zend_object_value &value)
+Value::Value(Base *object)
{
- // make a normal zval
+ // there are two options: the object was constructed from user space,
+ // and is already linked to a handle, or it was constructed from C++
+ // space, and no handle does yet exist
+ int handle = object->handle();
+
+ // do we have a handle?
+ if (!handle) throw Php::Exception("Assigning an unassigned object to a variable");
+
+ // make a regular zval, and set it to an object
MAKE_STD_ZVAL(_val);
Z_TYPE_P(_val) = IS_OBJECT;
- Z_OBJVAL_P(_val) = value;
+ Z_OBJ_HANDLE_P(_val) = handle;
+
+ // we have to lookup the object in the object-table
+ zend_object_store_bucket *obj_bucket = &EG(objects_store).object_buckets[handle];
+
+ // this is copy-pasted from zend_objects.c - and it is necessary too!
+ if (!obj_bucket->bucket.obj.handlers) obj_bucket->bucket.obj.handlers = &std_object_handlers;
+
+ // store the handlers in the zval too (cast is necessary for php 5.3)
+ Z_OBJ_HT_P(_val) = (zend_object_handlers*)obj_bucket->bucket.obj.handlers;
+
+ // run the copy constructor
+ zval_copy_ctor(_val);
}
/**