summaryrefslogtreecommitdiff
path: root/zend/classimpl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'zend/classimpl.cpp')
-rw-r--r--zend/classimpl.cpp206
1 files changed, 120 insertions, 86 deletions
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index 84e23ef..e4a620f 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -7,6 +7,7 @@
* @copyright 2014 Copernica BV
*/
#include "includes.h"
+#include <cstring>
/**
* Set up namespace
@@ -19,10 +20,10 @@ namespace Php {
ClassImpl::~ClassImpl()
{
// destruct the entries
- if (_entries) delete[] _entries;
+ delete[] _entries;
// free the stored pointer
- free(_self);
+ if (_self) zend_string_release(_self);
}
/**
@@ -54,14 +55,14 @@ static ClassImpl *self(zend_class_entry *entry)
* the string, in case PHP tries to read it) and after that the pointer
* and we leave the doc_comment_len at 0.
*/
- while (entry->parent && (entry->info.user.doc_comment == nullptr || entry->info.user.doc_comment_len > 0))
+ while (entry->parent && (entry->info.user.doc_comment == nullptr || ZSTR_LEN(entry->info.user.doc_comment) > 0))
{
// we did not create this class entry, but luckily we have a parent
entry = entry->parent;
}
// retrieve the comment (it has a pointer hidden in it to the ClassBase object)
- const char *comment = entry->info.user.doc_comment;
+ const char *comment = ZSTR_VAL(entry->info.user.doc_comment);
// the first byte of the comment is an empty string (null character), but
// the next bytes contain a pointer to the ClassBase class
@@ -90,13 +91,11 @@ struct CallData
void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS)
{
// retrieve the originally called (and by us allocated) function object
- // (this was copied from the zend engine source code, code looks way too
- // ugly to be made by me)
- CallData *data = (CallData *)EG(current_execute_data)->function_state.function;
+ auto *data = (CallData *)execute_data->func;
zend_internal_function *func = &data->func;
// retrieve the function name
- const char *name = func->function_name;
+ const char *name = ZSTR_VAL(func->function_name);
ClassBase *meta = data->self->_base;
// the data structure was allocated by ourselves in the getMethod or
@@ -111,7 +110,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS)
Value result(return_value, true);
// construct parameters
- ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);
+ ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC);
// retrieve the base object
Base *base = params.object();
@@ -139,9 +138,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS)
void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
{
// retrieve the originally called (and by us allocated) function object
- // (this was copied from the zend engine source code, code looks way too
- // ugly to be made by me)
- CallData *data = (CallData *)EG(current_execute_data)->function_state.function;
+ auto *data = (CallData *)execute_data->func;
// get self reference
ClassBase *meta = data->self->_base;
@@ -158,7 +155,7 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
Value result(return_value, true);
// construct parameters
- ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);
+ ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC);
// retrieve the base object
Base *base = params.object();
@@ -181,13 +178,13 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
/**
* Method that returns the function definition of the __call function
*
- * @param object_ptr Pointer to the object from which we want to retrieve the member function
+ * @param object Pointer to the object from which we want to retrieve the member function
* @param method The method that we want information about
* @param key ???
* @param tsrm_ls
* @return zend_function
*/
-zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, const zval *key TSRMLS_DC)
+zend_function *ClassImpl::getMethod(zend_object **object, zend_string *method, const zval *key TSRMLS_DC)
{
// something strange about the Zend engine (once more). The structure with
// object-handlers has a get_method and call_method member. When a function is
@@ -198,13 +195,13 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, cons
// first we'll check if the default handler does not have an implementation,
// in that case the method is probably already implemented as a regular method
- auto *defaultFunction = std_object_handlers.get_method(object_ptr, method, key TSRMLS_CC);
+ auto *defaultFunction = std_object_handlers.get_method(object, method, key TSRMLS_CC);
// did the default implementation do anything?
if (defaultFunction) return defaultFunction;
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(*object_ptr TSRMLS_CC);
+ auto *entry = (*object)->ce;
// this is peculiar behavior of the zend engine, we first are going to dynamically
// allocate memory holding all the properties of the __call method (we initially
@@ -243,11 +240,11 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, cons
* @param tsrm_ls
* @return zend_function
*/
-zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *method, const zval *key TSRMLS_DC)
+zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *method TSRMLS_DC)
{
// first we'll check if the default handler does not have an implementation,
// in that case the method is probably already implemented as a regular method
- auto *defaultFunction = zend_std_get_static_method(entry, method, key TSRMLS_CC);
+ auto *defaultFunction = zend_std_get_static_method(entry, method, nullptr TSRMLS_CC);
// did the default implementation do anything?
if (defaultFunction) return defaultFunction;
@@ -285,7 +282,7 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *
* @param tsrm_ls
* @return int
*/
-int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zval **object_ptr TSRMLS_DC)
+int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr TSRMLS_DC)
{
// it is really unbelievable how the Zend engine manages to implement every feature
// in a complete different manner. You would expect the __invoke() and the
@@ -294,9 +291,6 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
// to fill the function parameter with all information about the invoke()
// method that is going to get called
- // retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
-
// just like we did for getMethod(), we're going to dynamically allocate memory
// with all information about the function
auto *data = (CallData *)emalloc(sizeof(CallData));
@@ -309,12 +303,12 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
function->arg_info = nullptr;
function->num_args = 0;
function->required_num_args = 0;
- function->scope = entry;
+ function->scope = *entry_ptr;
function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
function->function_name = nullptr;
// store pointer to ourselves
- data->self = self(entry);
+ data->self = self(*entry_ptr);
// assign this dynamically allocated variable to the func parameter
// the cast is ok, because zend_internal_function is a member of the
@@ -323,7 +317,7 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
// the object_ptr should be filled with the object on which the method is
// called (otherwise the Zend engine tries to call the method statically)
- *object_ptr = object;
+ *object_ptr = Z_OBJ_P(object);
// done
return SUCCESS;
@@ -364,12 +358,20 @@ zend_object_handlers *ClassImpl::objectHandlers()
_handlers.get_method = &ClassImpl::getMethod;
_handlers.get_closure = &ClassImpl::getClosure;
+ // register destructor and deallocator
+ _handlers.dtor_obj = &ClassImpl::destructObject;
+ _handlers.free_obj = &ClassImpl::freeObject;
+
// handler to cast to a different type
_handlers.cast_object = &ClassImpl::cast;
// method to compare two objects
_handlers.compare_objects = &ClassImpl::compare;
+ // set the offset between our class implementation and
+ // the zend_object member in the allocated structure
+ _handlers.offset = ObjectImpl::offset();
+
// remember that object is now initialized
_initialized = true;
@@ -400,10 +402,10 @@ int ClassImpl::compare(zval *val1, zval *val2 TSRMLS_DC)
try
{
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(val1 TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(val1);
// other object must be of the same type
- if (entry != zend_get_class_entry(val2 TSRMLS_CC)) throw NotImplemented();
+ if (entry != Z_OBJCE_P(val2)) throw NotImplemented();
// we need the C++ class meta-information object
ClassBase *meta = self(entry)->_base;
@@ -448,7 +450,7 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
Base *object = ObjectImpl::find(val TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(val TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(val);
// we need the C++ class meta-information object
ClassBase *meta = self(entry)->_base;
@@ -456,7 +458,10 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
// retval is not yet initialized --- and again feelings of disbelief,
// frustration, wonder and anger come up when you see that there are not two
// functions in the Zend engine that have a comparable API
- INIT_PZVAL(retval);
+ //
+ // this function was removed, because it was supposedly no longer necessary
+ // can we get away with removing it here too?
+ // INIT_PZVAL(retval);
// when the magic function it not implemented, an exception will be thrown,
// and the extension may throw a Php::Exception
@@ -506,13 +511,14 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
/**
* 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
+ *
+ * @param val The object to be cloned
+ * @return zend_object The object to be created
*/
-zend_object ClassImpl::cloneObject(zval *val TSRMLS_DC)
+zend_object *ClassImpl::cloneObject(zval *val TSRMLS_DC)
{
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(val TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(val);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -532,17 +538,17 @@ zend_object ClassImpl::cloneObject(zval *val TSRMLS_DC)
if (!cpp) zend_error(E_ERROR, "Unable to clone %s", entry->name);
// store the object
- auto *new_object = new ObjectImpl(entry, cpp, impl->objectHandlers, 1 TSRMLS_CC);
+ auto *new_object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1 TSRMLS_CC);
// clone the members (this will also call the __clone() function if the user
// had registered that as a visible method)
- zend_objects_clone_members(new_object->php(), result, old_object->php(), Z_OBJ_HANDLE_P(val) TSRMLS_CC);
+ zend_objects_clone_members(new_object->php(), old_object->php() TSRMLS_CC);
// was a custom clone method installed? If not we call the magic c++ __clone method
if (!entry->clone) meta->callClone(cpp);
// done
- return new_object->object();
+ return new_object->php();
}
/**
@@ -600,10 +606,11 @@ int ClassImpl::countElements(zval *object, long *count TSRMLS_DC)
* @param object The object on which it is called
* @param offset The name of the property
* @param type The type of the variable???
+ * @param rv Pointer to where to store the data
* @param tsrm_ls
* @return zval
*/
-zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC)
+zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv TSRMLS_DC)
{
// what to do with the type?
//
@@ -633,7 +640,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC)
try
{
// ArrayAccess is implemented, call function
- return toZval(arrayaccess->offsetGet(offset), type);
+ return toZval(arrayaccess->offsetGet(offset), type, rv);
}
catch (Exception &exception)
{
@@ -650,7 +657,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC)
if (!std_object_handlers.read_dimension) return nullptr;
// call default
- return std_object_handlers.read_dimension(object, offset, type TSRMLS_CC);
+ return std_object_handlers.read_dimension(object, offset, type, rv TSRMLS_CC);
}
}
@@ -790,26 +797,52 @@ void ClassImpl::unsetDimension(zval *object, zval *member TSRMLS_DC)
/**
* Helper method to turn a property into a zval
- * @param value
- * @param type
- * @return Value
+ *
+ * @param value The value to convert to a zval
+ * @param type The type of operation (read or write)
+ * @param rv Pointer to where to store the data
+ * @return The result (same as the ptr input)
*/
-zval *ClassImpl::toZval(Value &&value, int type)
+zval *ClassImpl::toZval(Value &&value, int type, zval *rv)
{
- // because we do not want the value object to destruct the zval when
- // it falls out of scope, we detach the zval from it, if this is a regular
- // read operation we can do this right away
- if (type == 0) return value.detach(false);
-
- // this is a more complicated read operation, the scripts wants to get
- // deeper access to the returned value. This, however, is only possible
- // if the value has more than once reference (if it has a refcount of one,
- // the value object that we have here is the only instance of the zval,
- // and it is simply impossible to return a reference or so
- if (value.refcount() <= 1) return value.detach(false);
-
- // we're dealing with an editable zval, return a reference variable
- return Value(value.detach(false), true).detach(false);
+ // the result zval that needs to be copied over
+ zval *result = nullptr;
+
+ /**
+ * Because we do not want the value object to destruct the zval when
+ * it falls out of scope, we detach the zval from it, if this is a regular
+ * read operation we can do this right away.
+ *
+ * For write operations we need to check the refcount. If the refcount is
+ * only 1 (meaning the value object has the only reference) we cannot return
+ * a reference because there _is_ nothing to reference (the value will destruct)
+ */
+ if (type == 0 || value.refcount() <= 1)
+ {
+ // first retrieve the value so we can copy it
+ result = value.detach(false);
+ }
+ // this is an editable zval, return a reference to it
+ else
+ {
+ // we're dealing with an editable zval, retrieve a reference variable
+ result = Value(value.detach(false), true).detach(false);
+ }
+
+ // now copy the value over to the pointer
+ ZVAL_COPY_VALUE(rv, result);
+
+ // if the zval has a reference count we must decrease it
+ Z_TRY_DELREF_P(result);
+
+ // the pointer from the value may now be destroyed
+ // (it was allocated by the value and detached)
+ // we do not actually "destroy" the value here,
+ // even if the refcount reaches 0 here!
+ delete result;
+
+ // return the pointer to the value
+ return rv;
}
/**
@@ -819,7 +852,7 @@ zval *ClassImpl::toZval(Value &&value, int type)
* @param offset The name of the property
* @param type The type of the variable???
* @param cache_slot The cache slot used
- * @param rv The "return value" (for errors
+ * @param rv Pointer to where to store the data
* @param tsrm_ls
* @return val
*/
@@ -846,7 +879,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
Base *base = ObjectImpl::find(object TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -866,12 +899,12 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
if (iter == impl->_properties.end())
{
// retrieve value from the __get method
- return toZval(meta->callGet(base, key), type);
+ return toZval(meta->callGet(base, key), type, rv);
}
else
{
// get the value
- return toZval(iter->second->get(base), type);
+ return toZval(iter->second->get(base), type, rv);
}
}
catch (const NotImplemented &exception)
@@ -880,7 +913,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
if (!std_object_handlers.read_property) return nullptr;
// call default
- return std_object_handlers.read_property(object, name, type, key TSRMLS_CC);
+ return std_object_handlers.read_property(object, name, type, cache_slot, rv TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -912,7 +945,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach
Base *base = ObjectImpl::find(object TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -949,7 +982,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach
if (!std_object_handlers.write_property) return;
// call the default
- std_object_handlers.write_property(object, name, value, key TSRMLS_CC);
+ std_object_handlers.write_property(object, name, value, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -989,7 +1022,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **
Base *base = ObjectImpl::find(object TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -1022,7 +1055,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **
if (!std_object_handlers.has_property) return 0;
// call default
- return std_object_handlers.has_property(object, name, has_set_exists, key TSRMLS_CC);
+ return std_object_handlers.has_property(object, name, has_set_exists, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1052,7 +1085,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM
try
{
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -1075,7 +1108,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM
if (!std_object_handlers.unset_property) return;
// call the default
- std_object_handlers.unset_property(object, member, key TSRMLS_CC);
+ std_object_handlers.unset_property(object, member, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1089,7 +1122,6 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM
* Function that is called when an object is about to be destructed
* This will call the magic __destruct method
* @param object
- * @param handle
* @param tsrm_ls
*/
void ClassImpl::destructObject(zend_object *object TSRMLS_DC)
@@ -1109,7 +1141,7 @@ void ClassImpl::destructObject(zend_object *object TSRMLS_DC)
catch (const NotImplemented &exception)
{
// fallback on the default destructor call
- zend_objects_destroy_object(object, handle TSRMLS_CC);
+ zend_objects_destroy_object(object TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1140,7 +1172,7 @@ void ClassImpl::freeObject(zend_object *object TSRMLS_DC)
* @param tsrm_ls
* @return zend_object_value The newly created object
*/
-zend_object ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
+zend_object *ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
{
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -1157,7 +1189,7 @@ zend_object ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
auto *object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1 TSRMLS_CC);
// return the php object stored in the implementation
- return object->object();
+ return object->php();
}
/**
@@ -1207,7 +1239,7 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje
* @param tsrm_ls
* @return int
*/
-int ClassImpl::serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC)
+int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data TSRMLS_DC)
{
// get the serializable object
Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(object TSRMLS_CC)->object());
@@ -1247,13 +1279,13 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, zend_uint *buf_le
* @param tsrm_ls
* @return int
*/
-int ClassImpl::unserialize(zval **object, zend_class_entry *entry, const unsigned char *buffer, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC)
+int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned char *buffer, size_t buf_len, zend_unserialize_data *data TSRMLS_DC)
{
// create the PHP object
- object_init_ex(*object, entry);
+ object_init_ex(object, entry);
// turn this into a serializale
- Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(*object TSRMLS_CC)->object());
+ Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(object TSRMLS_CC)->object());
// user may throw an exception in the serialize() function
try
@@ -1346,14 +1378,6 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref
// we need a special constructor
entry.create_object = &ClassImpl::createObject;
- // register destructor and deallocator
- entry.dtor_obj = &ClassImpl::destructObject;
- entry.free_obj = &ClassImpl::freeObject;
-
- // set the offset for the zend_object, to allow PHP to
- // locate the original point of the allocated memory
- entry.offset = ObjectImpl::offset();
-
// register function that is called for static method calls
entry.get_static_method = &ClassImpl::getStaticMethod;
@@ -1375,7 +1399,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref
if (_parent->_entry)
{
// register the class
- _entry = zend_register_internal_class_ex(&entry, _parent->_entry, const_cast<char*>(_parent->name().c_str()) TSRMLS_CC);
+ _entry = zend_register_internal_class_ex(&entry, _parent->_entry TSRMLS_CC);
}
else
{
@@ -1405,6 +1429,16 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref
// this pointer has to be copied to temporary pointer, as &this causes compiler error
ClassImpl *impl = this;
+ // allocate memory for the doc_comment (which we abuse for storing a pointer to ourselves)
+ _self = zend_string_alloc(sizeof(this), 1);
+
+ // make the string appear empty
+ ZSTR_VAL(_self)[0] = '\0';
+ ZSTR_LEN(_self) = 0;
+
+ // copy over the 'this'-pointer after the null-character
+ std::memcpy(ZSTR_VAL(_self) + 1, &impl, sizeof(impl));
+
// set access types flags for class
_entry->ce_flags = (int)_type;