diff --git a/.gitignore b/.gitignore index fe945915..64fc9284 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ *.a.* *.so.* shared/ -static/ \ No newline at end of file +static/ +.vscode/ \ No newline at end of file diff --git a/include/base.h b/include/base.h index 023d7119..e6d8b879 100644 --- a/include/base.h +++ b/include/base.h @@ -2,7 +2,7 @@ * Base class for defining your own objects * * @author Emiel Bruijntjes - * @copyright 2013 Copernica BV + * @copyright 2013 - 2022 Copernica BV */ /** @@ -261,6 +261,22 @@ class PHPCPP_EXPORT Base */ int __compare(const Base &base) const; + /** + * Method that is called when an explicit call to $object->serialize() is made + * Note that a call to serialize($object) does not end up in this function, but + * is handled by the user-space implementation of Serializable::serialize()). + * @return Php::Value + */ + Php::Value __serialize(); + + /** + * Method that is called when an explicit call to $object->unserialize() is made + * Note that a call to unserialize($string) does not end up in this function, but + * is handled by the user-space implementation of Serializable::unserialize()). + * @param params The passed parameters + */ + void __unserialize(Php::Parameters ¶ms); + private: /** diff --git a/zend/base.cpp b/zend/base.cpp index ed6fe3bc..9f000ed8 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -3,7 +3,7 @@ * * Implementation file for the base of all classes * - * @copyright 2014 Copernica BV + * @copyright 2014 - 2022 Copernica BV */ #include "includes.h" @@ -217,6 +217,51 @@ int Base::__compare(const Base &that) const return 1; } +/** + * Method that is called when an explicit call to $object->serialize() is made + * Note that a call to serialize($object) does not end up in this function, but + * is handled by the user-space implementation of Serializable::serialize()). + * @return Php::Value + */ +Php::Value Base::__serialize() +{ + // 'this' refers to a Php::Base class, but we expect that is also implements the Serializable + // interface (otherwise we would never have registered the __serialize function as a callback) + auto *serializable = dynamic_cast(this); + + // this one should not fail + if (serializable == nullptr) return ""; + + // pass the call to the interface + return serializable->serialize(); +} + +/** + * Method that is called when an explicit call to $object->unserialize() is made + * Note that a call to unserialize($string) does not end up in this function, but + * is handled by the user-space implementation of Serializable::unserialize()). + * @param params The passed parameters + */ +void Base::__unserialize(Php::Parameters ¶ms) +{ + // 'this' refers to a Php::Base class, but we expect that is also implements the Serializable + // interface (otherwise we would never have registered the __serialize function as a callback) + auto *serializable = dynamic_cast(this); + + // this one should not fail + if (serializable == nullptr) return; + + // the passed in parameter + Php::Value param = params[0]; + + // make sure the parameter is indeed a string + param.setType(Type::String); + + // pass the call to the interface + serializable->unserialize(param.rawValue(), param.size()); +} + + /** * End namespace */ diff --git a/zend/callable.h b/zend/callable.h index 95f8e4ed..1334d78a 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -124,6 +124,11 @@ class Callable */ void initialize(zend_internal_function_info *info, const char *classname = nullptr) const; + /** + * Name of the function + * @return const std::string& + */ + const std::string &name() const { return _name; } protected: diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 061eed69..dcf91154 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -8,6 +8,7 @@ */ #include "includes.h" #include +#include /** * Set up namespace @@ -1300,6 +1301,19 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned return SUCCESS; } +/** + * Helper method to check if a function is registered for this instance + * @param name name of the function to check for + * @return bool Wether the function exists or not + */ +bool ClassImpl::hasMethod(const char* name) const +{ + // find the method + auto result = std::find_if(_methods.begin(), _methods.end(), [name](std::shared_ptr method){ return method->name() == name; }); + // return wether its found or not + return result != _methods.end(); +} + /** * Retrieve an array of zend_function_entry objects that hold the * properties for each method. This method is called at extension @@ -1313,8 +1327,19 @@ const struct _zend_function_entry *ClassImpl::entries() // already initialized? if (_entries) return _entries; + // the number of entries that need to be allocated + size_t entrycount = _methods.size(); + + // if the class is serializable, we might need some extra methods + if (_base->serializable()) + { + // add the serialize method if the class does not have one defined yet + if (!hasMethod("serialize")) entrycount += 1; + if (!hasMethod("unserialize")) entrycount += 1; + } + // allocate memory for the functions - _entries = new zend_function_entry[_methods.size() + 1]; + _entries = new zend_function_entry[entrycount + 1]; // keep iterator counter int i = 0; @@ -1329,6 +1354,19 @@ const struct _zend_function_entry *ClassImpl::entries() method->initialize(entry, _name); } + // if the class is serializable, we might need some extra methods + if (_base->serializable()) + { + // the method objectneed to stay in scope for the lifetime of the script (because the register a pointer + // to an internal string buffer) -- so we create them as static variables + static Method serialize("serialize", &Base::__serialize, 0, {}); + static Method unserialize("unserialize", &Base::__unserialize, 0, { ByVal("input", Type::Undefined, true) }); + + // register the serialize and unserialize method in case this was not yet done in PHP user space + if (!hasMethod("serialize")) serialize.initialize(&_entries[i++], _name); + if (!hasMethod("unserialize")) unserialize.initialize(&_entries[i++], _name); + } + // last entry should be set to all zeros zend_function_entry *last = &_entries[i]; diff --git a/zend/classimpl.h b/zend/classimpl.h index 225c3ad7..625e1910 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -112,6 +112,13 @@ class ClassImpl */ const zend_function_entry *entries(); + /** + * Helper method to check if a function is registered for this instance + * @param name name of the function to check for + * @return bool Wether the function exists or not + */ + bool hasMethod(const char* name) const; + /** * Helper method to turn a property into a zval *