From 262b6077f16b8048a4b8a1b818761110b1170bf3 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Oct 2025 16:17:34 +0200 Subject: [PATCH 1/2] when calling serialize, we now check whether the derived class has overridden the method --- zend/classimpl.cpp | 100 +++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 22b08063..887bd8ef 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1320,20 +1320,53 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje */ int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data) { - // get the serializable object - Serializable *serializable = dynamic_cast(ObjectImpl::find(object)->object()); - // user may throw an exception in the serialize() function try { - // call the serialize method on the object - auto value = serializable->serialize(); - - // allocate the buffer, and copy the data into it (the zend engine will - // (hopefully) clean up the data for us - the default serialize method does - // it like this too) - *buffer = (unsigned char*)estrndup(value.c_str(), value.size()); - *buf_len = value.size(); + // get the base object + Base *base = dynamic_cast(ObjectImpl::find(object)->object()); + + // get the class-entry + zend_class_entry *ce = Z_OBJCE_P(object); + + // we are going to check if the serialize method was overridden in user-space + zend_function *func = (zend_function *)zend_hash_str_find_ptr(&ce->function_table, "serialize", sizeof("serialize")-1); + + // do we have a user-space alternative? + if (func && func->type == ZEND_USER_FUNCTION) + { + // construct object to make the call + Php::Object self(ce, base); + + // get the serialized string + auto value = self.call("serialize"); + + // make sure the returned value is indeed a string + value.setType(Type::String); + + // allocate the buffer, and copy the data into it (the zend engine will + // (hopefully) clean up the data for us - the default serialize method does + // it like this too) + *buffer = (unsigned char*)estrndup(value.rawValue(), value.size()); + *buf_len = value.size(); + } + else + { + // get the base object + Serializable *serializable = dynamic_cast(base); + + // call the serialize method on the object + auto value = serializable->serialize(); + + // allocate the buffer, and copy the data into it (the zend engine will + // (hopefully) clean up the data for us - the default serialize method does + // it like this too) + *buffer = (unsigned char*)estrndup(value.c_str(), value.size()); + *buf_len = value.size(); + } + + // done + return SUCCESS; } catch (Throwable &throwable) { @@ -1343,9 +1376,6 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, // unreachable return FAILURE; } - - // done - return SUCCESS; } /** @@ -1358,17 +1388,38 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, */ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned char *buffer, size_t buf_len, zend_unserialize_data *data) { - // create the PHP object - object_init_ex(object, entry); - - // turn this into a serializale - Serializable *serializable = dynamic_cast(ObjectImpl::find(object)->object()); - - // user may throw an exception in the serialize() function + // user may throw an exception in the unserialize() function try { - // call the unserialize method on it - serializable->unserialize((const char *)buffer, buf_len); + // create the PHP object + object_init_ex(object, entry); + + // get the base object + Base *base = dynamic_cast(ObjectImpl::find(object)->object()); + + // we are going to check if the serialize method was overridden in user-space + zend_function *func = (zend_function *)zend_hash_str_find_ptr(&entry->function_table, "unserialize", sizeof("unserialize")-1); + + // do we have a user-space alternative? + if (func && func->type == ZEND_USER_FUNCTION) + { + // construct object to make the call + Php::Object self(entry, base); + + // makek the unserialize call + self.call("unserialize", Php::Value((const char *)buffer, buf_len)); + } + else + { + // turn this into a serializale + Serializable *serializable = dynamic_cast(base); + + // call the unserialize method on it + serializable->unserialize((const char *)buffer, buf_len); + } + + // done + return SUCCESS; } catch (Throwable &throwable) { @@ -1380,9 +1431,6 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned // unreachable return FAILURE; } - - // done - return SUCCESS; } /** From f50252814230cce65ae099203bf97c1c3068d848 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Oct 2025 16:20:56 +0200 Subject: [PATCH 2/2] small typos --- zend/classimpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 887bd8ef..baa6f0de 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1397,7 +1397,7 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned // get the base object Base *base = dynamic_cast(ObjectImpl::find(object)->object()); - // we are going to check if the serialize method was overridden in user-space + // we are going to check if the unserialize method was overridden in user-space zend_function *func = (zend_function *)zend_hash_str_find_ptr(&entry->function_table, "unserialize", sizeof("unserialize")-1); // do we have a user-space alternative? @@ -1406,7 +1406,7 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned // construct object to make the call Php::Object self(entry, base); - // makek the unserialize call + // make the unserialize call self.call("unserialize", Php::Value((const char *)buffer, buf_len)); } else