summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartijn Otto <martijn.otto@copernica.com>2016-05-11 17:34:40 +0200
committerMartijn Otto <martijn.otto@copernica.com>2016-05-11 17:34:40 +0200
commit9f2e816c787c30ceeb139623c8dae594c4b4443d (patch)
treee434ebd2ff37c98bdc5934120b48cda1bf3899c2
parent570590058d16274005a4994fd8f1046d0eaf7f74 (diff)
Work in progress on PHP7 compatibility
-rw-r--r--include/parameters.h12
-rw-r--r--zend/callable.h46
-rw-r--r--zend/classimpl.cpp185
-rw-r--r--zend/classimpl.h121
-rw-r--r--zend/compileroptions.h16
-rw-r--r--zend/constantimpl.h82
-rw-r--r--zend/exception_handler.cpp16
-rw-r--r--zend/executestate.h25
-rw-r--r--zend/hashiterator.h91
-rw-r--r--zend/iteratorimpl.cpp20
-rw-r--r--zend/iteratorimpl.h30
-rw-r--r--zend/object.cpp5
-rw-r--r--zend/objectimpl.h123
-rw-r--r--zend/opcodes.h52
-rw-r--r--zend/origexception.h36
-rw-r--r--zend/parametersimpl.h29
-rw-r--r--zend/traverseiterator.h93
17 files changed, 386 insertions, 596 deletions
diff --git a/include/parameters.h b/include/parameters.h
index 3465863..b5731e5 100644
--- a/include/parameters.h
+++ b/include/parameters.h
@@ -42,9 +42,17 @@ protected:
public:
/**
- * Destructor
+ * Do _not_ add a virtual destructor here.
+ *
+ * We are extending a vector, which does not itself
+ * have a virtual destructor, so destructing through
+ * a pointer to this vector has no effect.
+ *
+ * By adding a virtual destructor we create a vtable,
+ * which makes the class bigger, causing slicing and
+ * then we are actually introducing the problem that
+ * we are trying to avoid!
*/
- virtual ~Parameters() {}
/**
* The object that is being called
diff --git a/zend/callable.h b/zend/callable.h
index bd74f27..272b22e 100644
--- a/zend/callable.h
+++ b/zend/callable.h
@@ -1,7 +1,7 @@
/**
* Callable.h
*
- * Object represents a callable function or method that is defined with the CPP
+ * Object represents a callable function or method that is defined with the CPP
* API.
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
@@ -13,7 +13,7 @@
*/
namespace Php {
-/**
+/**
* Class definition
*/
class Callable
@@ -29,22 +29,22 @@ public:
// construct vector for arguments
_argc = arguments.size();
_argv = new zend_arg_info[_argc+1];
-
+
// the first record is initialized with information about the function,
// so we skip that here
int i=1;
-
+
// loop through the arguments
for (auto it = arguments.begin(); it != arguments.end(); it++)
{
// increment counter with number of required parameters
if (it->required()) _required++;
-
+
// fill the arg info
fill(&_argv[i++], *it);
}
}
-
+
/**
* Copy constructor
* @param that
@@ -55,7 +55,7 @@ public:
_required(that._required),
_argc(that._argc),
_argv(nullptr) {}
-
+
/**
* Move constructor
* @param that
@@ -65,7 +65,7 @@ public:
_return(that._return),
_required(that._required),
_argc(that._argc),
- _argv(that._argv)
+ _argv(that._argv)
{
// invalidate other object
that._argv = nullptr;
@@ -78,14 +78,14 @@ public:
{
if (_argv) delete[] _argv;
}
-
+
/**
* Method that gets called every time the function is executed
* @param params The parameters that were passed
* @return Variable Return value
*/
virtual Value invoke(Parameters &params) = 0;
-
+
/**
* Fill a function entry
* @param entry Entry to be filled
@@ -134,7 +134,7 @@ protected:
* @var zend_arg_info[]
*/
zend_arg_info *_argv = nullptr;
-
+
/**
* Private helper method to fill an argument object
* @param info object from the zend engine
@@ -143,10 +143,11 @@ protected:
void fill(zend_arg_info *info, const Argument &arg) const
{
// fill members
- info->name = arg.name();
- info->name_len = ::strlen(arg.name());
+ info->name = zend_string_init(arg.name(), ::strlen(arg.name()), 1);
-#if PHP_VERSION_ID >= 50400
+ // are we filling an object
+ if (arg.type() == Type::Object) info->class_name = zend_string_init(arg.classname(), ::strlen(arg.classname()), 1);
+ else info->class_name = nullptr;
// since php 5.4 there is a type-hint, but we only support arrays, objects and callables
switch (arg.type()) {
@@ -155,29 +156,14 @@ protected:
case Type::Object: info->type_hint = IS_OBJECT; break;
default: info->type_hint = IS_NULL; break;
}
-
-# if PHP_VERSION_ID >= 50600
- // from PHP 5.6 and onwards, an is_variadic property can be set, this
+ // from PHP 5.6 and onwards, an is_variadic property can be set, this
// specifies whether this argument is the first argument that specifies
// the type for a variable length list of arguments. For now we only
// support methods and functions with a fixed number of arguments.
info->is_variadic = false;
-# endif
-
-#else
-
- // php 5.3 code
- info->array_type_hint = arg.type() == Type::Array;
- info->return_reference = false;
- info->required_num_args = 0; // @todo is this correct?
-
-#endif
-
// this parameter is a regular type
- info->class_name = arg.type() == Type::Object ? arg.classname() : nullptr;
- info->class_name_len = arg.type() == Type::Object && arg.classname() ? ::strlen(arg.classname()) : 0;
info->allow_null = arg.allowNull();
info->pass_by_reference = arg.byReference();
}
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index 5c339e9..84e23ef 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -21,10 +21,8 @@ ClassImpl::~ClassImpl()
// destruct the entries
if (_entries) delete[] _entries;
-#if PHP_VERSION_ID >= 50400
- // on newer php versions, we have allocated the command to hide a pointer in it
- if (_self) free(_self);
-#endif
+ // free the stored pointer
+ free(_self);
}
/**
@@ -38,8 +36,6 @@ ClassImpl::~ClassImpl()
*/
static ClassImpl *self(zend_class_entry *entry)
{
-#if PHP_VERSION_ID >= 50400
-
/**
* somebody could have extended this class from PHP userland, in which
* case trying to dereference the doc_comment would result in a disaster
@@ -70,25 +66,6 @@ static ClassImpl *self(zend_class_entry *entry)
// the first byte of the comment is an empty string (null character), but
// the next bytes contain a pointer to the ClassBase class
return *((ClassImpl **)(comment + 1));
-#else
-
- /**
- * This is likely not correct: If the class was extended using PHP-CPP
- * itself, we should retrieve the ClassImpl directly, however there is
- * no sane way to check this here, unlike in PHP 5.4.
- *
- * We therefore always go to the very base, of which we are sure that
- * we are the implementers. This way - at least - we don't cause any
- * segfaults.
- */
- while (entry->parent) entry = entry->parent;
-
- // on php 5.3 we store the pointer to impl after the name in the entry
- ClassImpl** impl = (ClassImpl**)(entry->name + 1 + entry->name_length);
-
- // return the actual implementation
- return *impl;
-#endif
}
/**
@@ -203,17 +180,14 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
/**
* Method that returns the function definition of the __call function
- * @param object_ptr
- * @param method_name
- * @param method_len
+ *
+ * @param object_ptr 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
*/
-#if PHP_VERSION_ID < 50399
-zend_function *ClassImpl::getMethod(zval **object_ptr, char *method_name, int method_len TSRMLS_DC)
-#else
-zend_function *ClassImpl::getMethod(zval **object_ptr, char *method_name, int method_len, const zend_literal *key TSRMLS_DC)
-#endif
+zend_function *ClassImpl::getMethod(zval **object_ptr, 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
@@ -224,11 +198,7 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, char *method_name, int me
// 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
-#if PHP_VERSION_ID < 50399
- auto *defaultFunction = std_object_handlers.get_method(object_ptr, method_name, method_len TSRMLS_CC);
-#else
- auto *defaultFunction = std_object_handlers.get_method(object_ptr, method_name, method_len, key TSRMLS_CC);
-#endif
+ auto *defaultFunction = std_object_handlers.get_method(object_ptr, method, key TSRMLS_CC);
// did the default implementation do anything?
if (defaultFunction) return defaultFunction;
@@ -254,7 +224,7 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, char *method_name, int me
function->required_num_args = 0;
function->scope = entry;
function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
- function->function_name = method_name;
+ function->function_name = method;
// store pointer to ourselves
data->self = self(entry);
@@ -266,21 +236,18 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, char *method_name, int me
/**
* Method that is called right before a static method call is attempted
- * @param entry
- * @param method
- * @param method_len
+ *
+ * @param entry The class entry to find the static function in
+ * @param method The method to get information about
+ * @param key ???
* @param tsrm_ls
* @return zend_function
*/
-zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, char* method, int method_len TSRMLS_DC)
+zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *method, const zval *key 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
-#if PHP_VERSION_ID < 50399
- auto *defaultFunction = zend_std_get_static_method(entry, method, method_len TSRMLS_CC);
-#else
- auto *defaultFunction = zend_std_get_static_method(entry, method, method_len, nullptr TSRMLS_CC);
-#endif
+ auto *defaultFunction = zend_std_get_static_method(entry, method, key TSRMLS_CC);
// did the default implementation do anything?
if (defaultFunction) return defaultFunction;
@@ -542,7 +509,7 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
* @param val The object to be cloned
* @return zend_obejct_value The object to be created
*/
-zend_object_value 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);
@@ -564,17 +531,8 @@ zend_object_value ClassImpl::cloneObject(zval *val TSRMLS_DC)
// an exception back to the Zend engine)
if (!cpp) zend_error(E_ERROR, "Unable to clone %s", entry->name);
- // the thing we're going to return
- zend_object_value result;
-
- // set the handlers
- result.handlers = impl->objectHandlers();
-
// store the object
- ObjectImpl *new_object = new ObjectImpl(entry, cpp, 1 TSRMLS_CC);
-
- // store the object in the object cache
- result.handle = new_object->handle();
+ 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)
@@ -584,7 +542,7 @@ zend_object_value ClassImpl::cloneObject(zval *val TSRMLS_DC)
if (!entry->clone) meta->callClone(cpp);
// done
- return result;
+ return new_object->object();
}
/**
@@ -856,18 +814,16 @@ zval *ClassImpl::toZval(Value &&value, int type)
/**
* Function that is called when a property is read
- * @param object
- * @param name
- * @param type
- * @param key
+ *
+ * @param object The object on which it is called
+ * @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 tsrm_ls
* @return val
*/
-#if PHP_VERSION_ID < 50399
-zval *ClassImpl::readProperty(zval *object, zval *name, int type TSRMLS_DC)
-#else
-zval *ClassImpl::readProperty(zval *object, zval *name, int type, const zend_literal *key TSRMLS_DC)
-#endif
+zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_slot, zval *rv TSRMLS_DC)
{
// what to do with the type?
//
@@ -924,11 +880,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, const zend_lit
if (!std_object_handlers.read_property) return nullptr;
// call default
-#if PHP_VERSION_ID < 50399
- return std_object_handlers.read_property(object, name, type TSRMLS_CC);
-#else
return std_object_handlers.read_property(object, name, type, key TSRMLS_CC);
-#endif
}
catch (Exception &exception)
{
@@ -950,15 +902,11 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, const zend_lit
* @param object The object on which it is called
* @param name The name of the property
* @param value The new value
- * @param key ???
+ * @param cache_slot The cache slot used
* @param tsrm_ls
* @return zval
*/
-#if PHP_VERSION_ID < 50399
-void ClassImpl::writeProperty(zval *object, zval *name, zval *value TSRMLS_DC)
-#else
-void ClassImpl::writeProperty(zval *object, zval *name, zval *value, const zend_literal *key TSRMLS_DC)
-#endif
+void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cache_slot TSRMLS_DC)
{
// retrieve the object and class
Base *base = ObjectImpl::find(object TSRMLS_CC)->object();
@@ -1001,11 +949,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, const zend_
if (!std_object_handlers.write_property) return;
// call the default
-#if PHP_VERSION_ID < 50399
- std_object_handlers.write_property(object, name, value TSRMLS_CC);
-#else
std_object_handlers.write_property(object, name, value, key TSRMLS_CC);
-#endif
}
catch (Exception &exception)
{
@@ -1031,15 +975,11 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, const zend_
* @param object The object on which it is called
* @param name The name of the property to check
* @param has_set_exists See above
- * @param key ???
+ * @param cache_slot The cache slot used
* @param tsrm_ls
* @return bool
*/
-#if PHP_VERSION_ID < 50399
-int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists TSRMLS_DC)
-#else
-int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, const zend_literal *key TSRMLS_DC)
-#endif
+int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **cache_slot TSRMLS_DC)
{
// the default implementation throws an exception, if we catch that
// we know for sure that the user has not overridden the __isset method
@@ -1082,11 +1022,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, const z
if (!std_object_handlers.has_property) return 0;
// call default
-#if PHP_VERSION_ID < 50399
- return std_object_handlers.has_property(object, name, has_set_exists TSRMLS_CC);
-#else
return std_object_handlers.has_property(object, name, has_set_exists, key TSRMLS_CC);
-#endif
}
catch (Exception &exception)
{
@@ -1106,14 +1042,10 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, const z
*
* @param object The object on which it is called
* @param member The member to remove
- * @param key
+ * @param cache_slot The cache slot used
* @param tsrm_ls
*/
-#if PHP_VERSION_ID < 50399
-void ClassImpl::unsetProperty(zval *object, zval *member TSRMLS_DC)
-#else
-void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *key TSRMLS_DC)
-#endif
+void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRMLS_DC)
{
// the default implementation throws an exception, if we catch that
// we know for sure that the user has not overridden the __unset method
@@ -1143,11 +1075,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *ke
if (!std_object_handlers.unset_property) return;
// call the default
-#if PHP_VERSION_ID < 50399
- std_object_handlers.unset_property(object, member TSRMLS_CC);
-#else
std_object_handlers.unset_property(object, member, key TSRMLS_CC);
-#endif
}
catch (Exception &exception)
{
@@ -1164,7 +1092,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *ke
* @param handle
* @param tsrm_ls
*/
-void ClassImpl::destructObject(zend_object *object, zend_object_handle handle TSRMLS_DC)
+void ClassImpl::destructObject(zend_object *object TSRMLS_DC)
{
// find object
ObjectImpl *obj = ObjectImpl::find(object);
@@ -1212,7 +1140,7 @@ void ClassImpl::freeObject(zend_object *object TSRMLS_DC)
* @param tsrm_ls
* @return zend_object_value The newly created object
*/
-zend_object_value 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);
@@ -1225,20 +1153,11 @@ zend_object_value ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
// the Zend engine)
if (!cpp) zend_error(E_ERROR, "Unable to instantiate %s", entry->name);
- // the thing we're going to return
- zend_object_value result;
-
- // set the handlers
- result.handlers = impl->objectHandlers();
-
// create the object in the zend engine
- ObjectImpl *object = new ObjectImpl(entry, cpp, 1 TSRMLS_CC);
+ auto *object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1 TSRMLS_CC);
- // store the object in the object cache
- result.handle = object->handle();
-
- // done
- return result;
+ // return the php object stored in the implementation
+ return object->object();
}
/**
@@ -1427,6 +1346,14 @@ 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;
@@ -1478,30 +1405,6 @@ 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;
-#if PHP_VERSION_ID >= 50400
-
- // allocate doc comment to contain an empty string + a hidden pointer
- _self = (char *)malloc(1 + sizeof(ClassImpl *));
-
- // empty string on first position
- _self[0] = '\0';
-
- // copy the 'this' pointer to the doc-comment
- memcpy(_self+1, &impl, sizeof(ClassImpl *));
-
- // set our comment in the actual class entry
- _entry->info.user.doc_comment = _self;
-
-#else
-
- // Reallocate some extra space in the name in the zend_class_entry so we can fit a pointer behind it
- _entry->name = (char *) realloc(_entry->name, _entry->name_length + 1 + sizeof(ClassImpl *));
-
- // Copy the pointer after it
- memcpy(_entry->name + _entry->name_length + 1, &impl, sizeof(ClassImpl *));
-
-#endif
-
// set access types flags for class
_entry->ce_flags = (int)_type;
diff --git a/zend/classimpl.h b/zend/classimpl.h
index 18802e3..47e7785 100644
--- a/zend/classimpl.h
+++ b/zend/classimpl.h
@@ -37,12 +37,12 @@ private:
*/
ClassType _type = ClassType::Regular;
- /**
+ /**
* The class entry
* @var zend_class_entry
*/
zend_class_entry *_entry = nullptr;
-
+
/**
* Pointer to the entries
* @var zend_function_entry[]
@@ -54,13 +54,13 @@ private:
* @var std::list
*/
std::list<std::shared_ptr<Method>> _methods;
-
+
/**
* All class members (class properties)
* @var std::list
*/
std::list<std::shared_ptr<Member>> _members;
-
+
/**
* Map of dynamically accessible properties
* @var std::map
@@ -72,19 +72,19 @@ private:
* @var std::list
*/
std::list<std::shared_ptr<ClassImpl>> _interfaces;
-
+
/**
* The parent/base class
* @var std::shared_ptr
*/
std::shared_ptr<ClassImpl> _parent;
-
+
/**
* The object handlers for instances of this class
* @var zend_object_handlers
*/
zend_object_handlers _handlers;
-
+
/**
* Are the handlers already initialized?
* @var bool
@@ -98,10 +98,10 @@ private:
char *_self = nullptr;
/**
- * Retrieve an array of zend_function_entry objects that hold the
+ * Retrieve an array of zend_function_entry objects that hold the
* properties for each method. This method is called at extension
* startup time to register all methods.
- *
+ *
* @param classname The class name
* @return zend_function_entry[]
*/
@@ -155,16 +155,16 @@ public:
/**
* Initialize the class, given its name
- *
+ *
* The module functions are registered on module startup, but classes are
* initialized afterwards. The Zend engine is a strange thing. Nevertheless,
* this means that this method is called after the module is already available.
* This function will inform the Zend engine about the existence of the
* class.
- *
- * @param base The extension C++ class
+ *
+ * @param base The extension C++ class
* @param ns Namespace name
- * @param tsrm_ls
+ * @param tsrm_ls
* @return zend_class_entry
*/
struct _zend_class_entry *initialize(ClassBase *base, const std::string &ns TSRMLS_DC);
@@ -174,11 +174,11 @@ public:
* @param entry Pointer to class information
* @param val The object to be cloned
* @param tsrm_ls
- * @return zend_object_value Object info
+ * @return zend_object Object info
*/
- static zend_object_value createObject(zend_class_entry *entry TSRMLS_DC);
- static zend_object_value cloneObject(zval *val TSRMLS_DC);
- static void destructObject(zend_object *object, unsigned int handle TSRMLS_DC);
+ static zend_object createObject(zend_class_entry *entry TSRMLS_DC);
+ static zend_object cloneObject(zval *val TSRMLS_DC);
+ static void destructObject(zend_object *object TSRMLS_DC);
static void freeObject(zend_object *object TSRMLS_DC);
/**
@@ -195,7 +195,7 @@ public:
/**
* Function that is used to count the number of elements in the object
- * If the user has implemented the Countable interface, this method will
+ * If the user has implemented the Countable interface, this method will
* call the count() method
* @param val
* @param count
@@ -230,7 +230,7 @@ public:
* @return zend_object_handlers
*/
static zend_object_handlers *objectHandlers(zend_class_entry *entry);
-
+
/**
* Function to create a new iterator to iterate over an object
* @param entry The class entry
@@ -243,85 +243,72 @@ public:
/**
* Function that is called when a property is being read
+ *
* @param object The object on which it is called
* @param offset The name of the property
* @param type The type of the variable???
- * @param key ???
+ * @param cache_slot The cache slot used
+ * @param rv The "return value" (for errors
* @param tsrm_ls
* @return zval
*/
-#if PHP_VERSION_ID >= 50400
- static zval *readProperty(zval *object, zval *name, int type, const zend_literal *key TSRMLS_DC);
-#else
- static zval *readProperty(zval *object, zval *name, int type TSRMLS_DC);
-#endif
+ static zval *readProperty(zval *object, zval *name, int type, void **cache_slot, zval *rv TSRMLS_DC);
/**
* Function that is called when a property is set / updated
+ *
* @param object The object on which it is called
* @param name The name of the property
* @param value The new value
- * @param key ???
+ * @param cache_slot The cache slot used
* @param tsrm_ls
* @return zval
*/
-#if PHP_VERSION_ID >= 50400
- static void writeProperty(zval *object, zval *name, zval *value, const zend_literal *key TSRMLS_DC);
-#else
- static void writeProperty(zval *object, zval *name, zval *value TSRMLS_DC);
-#endif
+ static void writeProperty(zval *object, zval *name, zval *value, void **cache_slot TSRMLS_DC);
/**
* Function that is called to check whether a certain property is set
+ *
* @param object The object on which it is called
* @param name The name of the property to check
* @param has_set_exists See above
+ * @param cache_slot The cache slot used
* @param tsrm_ls
* @return bool
*/
-#if PHP_VERSION_ID >= 50400
- static int hasProperty(zval *object, zval *name, int has_set_exists, const zend_literal *key TSRMLS_DC);
-#else
- static int hasProperty(zval *object, zval *name, int has_set_exists TSRMLS_DC);
-#endif
+ static int hasProperty(zval *object, zval *name, int has_set_exists, void **cache_slot TSRMLS_DC);
/**
* Function that is called when a property is removed from the project
+ *
* @param object The object on which it is called
* @param member The member to remove
+ * @param cache_slot The cache slot used
* @param tsrm_ls
*/
-#if PHP_VERSION_ID >= 50400
- static void unsetProperty(zval *object, zval *member, const zend_literal *key TSRMLS_DC);
-#else
- static void unsetProperty(zval *object, zval *member TSRMLS_DC);
-#endif
+ static void unsetProperty(zval *object, zval *member, void **cache_slot TSRMLS_DC);
/**
* Method that returns information about the function signature of a undefined method
- * @param object_ptr
- * @param method
- * @param method_len
- * @param key
+ *
+ * @param object_ptr 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
*/
-#if PHP_VERSION_ID >= 50400
- static zend_function *getMethod(zval **object_ptr, char *method, int method_len, const zend_literal *key TSRMLS_DC);
-#else
- static zend_function *getMethod(zval **object_ptr, char *method, int method_len TSRMLS_DC);
-#endif
+ static zend_function *getMethod(zval **object_ptr, zend_string *method, const zval *key TSRMLS_DC);
/**
* Method that returns information about the function signature of an undefined static method
- * @param object_ptr
- * @param method
- * @param method_len
- * @param key
+ *
+ * @param entry The class entry to find the static function in
+ * @param method The method that we want information about
+ * @param key ???
* @param tsrm_ls
* @return zend_function
*/
- static zend_function *getStaticMethod(zend_class_entry *entry, char* method, int method_len TSRMLS_DC);
+ static zend_function *getStaticMethod(zend_class_entry *entry, zend_string *method, const zval *key TSRMLS_DC);
/**
* Method that returns information about the __invoke() method
@@ -365,17 +352,17 @@ public:
*/
static int serialize(zval *object, unsigned char **buffer, unsigned int *buf_len, zend_serialize_data *data TSRMLS_DC);
static int unserialize(zval **object, zend_class_entry *entry, const unsigned char *buffer, unsigned int buf_len, zend_unserialize_data *data TSRMLS_DC);
-
+
/**
* Add a method to the class
- * zend_serialize_data
+ * zend_serialize_data
* The method will be accessible as one of the class methods in your PHP
* code. When the method is called, it will automatically be forwarded
* to the C++ implementation. The flags can be Php::Public, Php::Protected
* or Php::Private (using private methods can be useful if you for example
* want to make the __construct() function private). The access-modified
* flag can be bitwise combined with the flag Php::Final or Php::Abstract).
- *
+ *
* @param name Name of the method
* @param method The actual method
* @param flags Optional flags
@@ -392,11 +379,11 @@ public:
/**
* Add a static method to the class
- *
+ *
* Because a C++ static method is just a regular function, that happens to
* have access to the private variables of the class at compile time, you
* can register any function that matches one of the function signatures
- *
+ *
* @param name Name of the method
* @param method The actual method
* @param flags Optional flags
@@ -409,30 +396,30 @@ public:
/**
* Add an abstract method to the class
- *
+ *
* @param name Name of the method
* @param flags Optional flags (like public or protected)
* @param args Description of the supported arguments
*/
- void method(const char *name, int flags=0, const Arguments &args = {})
- {
+ void method(const char *name, int flags=0, const Arguments &args = {})
+ {
// the "MethodModifiers" holds all the valid modifiers for a method: Final + Public + Protected + Private.
// The "Static" and "Abstract" properties are also valid modifiers in this context (in fact, you would
// expect that we could even force adding "Abstract" here, because we're adding an abstract method -- but
- // in a PHP interface the "Abstract" modifier is not allowed - even though it is of course abstract.
+ // in a PHP interface the "Abstract" modifier is not allowed - even though it is of course abstract.
// So we only _allow_ abstract here, and expect the caller to _set_ it.
_methods.push_back(std::make_shared<Method>(name, (flags & (MethodModifiers | Static | Abstract)), args));
}
/**
* Add a property to the class
- *
+ *
* Every instance of this class will have this property. The property
* can be Php::Public, Php::Protected or Php::Private (altough setting
* private properties is odd as the implementation of the class is in CPP,
* so why use private properties while the whole implementation is already
* hidden)
- *
+ *
* @param name Name of the property
* @param value Actual property value
* @param flags Optional flags
@@ -459,7 +446,7 @@ public:
void property(const char *name, const getter_callback_1 &getter, const setter_callback_0 &setter) { _properties[name] = std::make_shared<Property>(getter,setter); }
void property(const char *name, const getter_callback_0 &getter, const setter_callback_1 &setter) { _properties[name] = std::make_shared<Property>(getter,setter); }
void property(const char *name, const getter_callback_1 &getter, const setter_callback_1 &setter) { _properties[name] = std::make_shared<Property>(getter,setter); }
-
+
/**
* Add an interface that is implemented
* @param interface The interface that is implemented
diff --git a/zend/compileroptions.h b/zend/compileroptions.h
index 74ba053..c56bf78 100644
--- a/zend/compileroptions.h
+++ b/zend/compileroptions.h
@@ -2,7 +2,7 @@
* CompilerOptions.h
*
* Helper class to temporarily set compiler options
- *
+ *
* When an object is destructed, it automatically restored the previous compiler settings
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
@@ -22,10 +22,10 @@ class CompilerOptions
private:
/**
* The original compiler options
- * @var int
+ * @var uint32_t
*/
- zend_uint _original;
-
+ uint32_t _original;
+
#ifdef ZTS
/**
* When in thread safety mode, we also keep track of the TSRM_LS var
@@ -39,20 +39,20 @@ public:
* Constructor
* @param options
*/
- CompilerOptions(zend_uint options TSRMLS_DC)
+ CompilerOptions(uint32_t options TSRMLS_DC)
{
// remember the old compiler options before we set temporary compile options
_original = CG(compiler_options);
-
+
// we're going to evaluate only once
CG(compiler_options) = options;
-
+
#ifdef ZTS
// copy tsrm_ls param
this->tsrm_ls = tsrm_ls;
#endif
}
-
+
/**
* Destructor
*/
diff --git a/zend/constantimpl.h b/zend/constantimpl.h
index 46a16d2..9de8a31 100644
--- a/zend/constantimpl.h
+++ b/zend/constantimpl.h
@@ -1,8 +1,8 @@
/**
* ConstantImpl.h
- *
+ *
* Implementation file for the constant class
- *
+ *
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2015 Copernica BV
*/
@@ -11,7 +11,7 @@
* Set up namespace
*/
namespace Php {
-
+
/**
* Class definition
*/
@@ -28,7 +28,7 @@ public:
// initialize the zval
ZVAL_NULL(&_constant.value);
}
-
+
/**
* Constructor
* @param name
@@ -39,7 +39,7 @@ public:
// initialize the zval
ZVAL_BOOL(&_constant.value, value);
}
-
+
/**
* Constructor
* @param name
@@ -72,7 +72,7 @@ public:
// initialize the zval
ZVAL_DOUBLE(&_constant.value, value);
}
-
+
/**
* Constructor
* @param name
@@ -82,9 +82,9 @@ public:
ConstantImpl(const char *name, const char *value, size_t len) : _name(name)
{
// initialize the zval
- ZVAL_STRINGL(&_constant.value, value, len, 0);
+ ZVAL_STRINGL(&_constant.value, value, len);
}
-
+
/**
* Constructor
* @param name
@@ -93,9 +93,9 @@ public:
ConstantImpl(const char *name, const char *value) : _name(name)
{
// initialize the zval
- ZVAL_STRINGL(&_constant.value, value, ::strlen(value), 0);
+ ZVAL_STRINGL(&_constant.value, value, ::strlen(value));
}
-
+
/**
* Constructor
* @param name
@@ -104,9 +104,9 @@ public:
ConstantImpl(const char *name, const std::string &value) : _name(name)
{
// initialize the zval
- ZVAL_STRINGL(&_constant.value, value.c_str(), value.size(), 0);
+ ZVAL_STRINGL(&_constant.value, value.c_str(), value.size());
}
-
+
/**
* Destructor
*/
@@ -121,85 +121,87 @@ public:
// check the zval type
switch (Z_TYPE(_constant.value)) {
- case IS_NULL:
+ case IS_NULL:
// set a null constant
clss.property(_name, nullptr, Php::Const);
break;
-
+
case IS_LONG:
// set a long constant (cast is necessary because php uses longs, which
// have a different size on different platforms)
clss.property(_name, (int64_t)Z_LVAL(_constant.value), Php::Const);
break;
-
+
case IS_DOUBLE:
// set a double constant
clss.property(_name, Z_DVAL(_constant.value), Php::Const);
break;
-
- case IS_BOOL:
- // set a boolean constant
- clss.property(_name, Z_BVAL(_constant.value), Php::Const);
+
+ case IS_FALSE:
+ // set boolean false
+ clss.property(_name, false, Php::Const);
break;
-
+
+ case IS_TRUE:
+ // set boolean true
+ clss.property(_name, true, Php::Const);
+
case IS_STRING:
// set a string constant
clss.property(_name, std::string(Z_STRVAL(_constant.value), Z_STRLEN(_constant.value)), Php::Const);
break;
-
+
default:
// this should not happen, the constant can only be constructed as one
// of the above types, so it should be impossible to end up here. But
// for completeness, we are going to make a copy of the zval, and convert
// that to a string
zval copy = _constant.value;
-
+
// run the copy constructor to make sure zval is correctly copied
zval_copy_ctor(&copy);
-
+
// convert the copy to a string
convert_to_string(&copy);
-
+
// set as string constant
clss.property(_name, std::string(Z_STRVAL(copy), Z_STRLEN(copy)), Php::Const);
break;
}
}
-
+
/**
* Initialize the constant
* @param prefix Namespace prefix
* @param module_number The module number
* @param tsrmls Optional parameter when running in multi-threading context
*/
- void initialize(const std::string &prefix, int module_number TSRMLS_DC)
+ void initialize(const std::string &prefix, int module_number TSRMLS_DC)
{
// is there a namespace name involved?
- if (prefix.size() > 0)
+ if (!prefix.empty())
{
// size of the name
auto namelen = ::strlen(_name);
-
- // include prefix in the full name (name_len should include '\0')
- _constant.name_len = prefix.size() + 1 + namelen + 1;
- _constant.name = (char *)emalloc(_constant.name_len);
+
+ // allocate memory for the full name
+ _constant.name = zend_string_alloc(prefix.size() + 1 + namelen, 1);
// copy the entire namespace name, separator and constant name
- ::strncpy(_constant.name, prefix.c_str(), prefix.size());
- ::strncpy(_constant.name + prefix.size(), "\\", 1);
- ::strncpy(_constant.name + prefix.size() + 1, _name, namelen + 1);
+ ::strncpy(ZSTR_VAL(_constant.name), prefix.c_str(), prefix.size());
+ ::strncpy(ZSTR_VAL(_constant.name) + prefix.size(), "\\", 1);
+ ::strncpy(ZSTR_VAL(_constant.name) + prefix.size() + 1, _name, namelen + 1);
}
else
{
- // no namespace, we simply copy the name (name_len should include '\0')
- _constant.name_len = ::strlen(_name) + 1;
- _constant.name = zend_strndup(_name, _constant.name_len - 1);
+ // no namespace, we simply copy the name
+ _constant.name = zend_string_init(_name, ::strlen(_name), 1);
}
-
+
// set all the other constant properties
_constant.flags = CONST_CS | CONST_PERSISTENT;
_constant.module_number = module_number;
-
+
// register the zval
zend_register_constant(&_constant TSRMLS_CC);
}
@@ -217,7 +219,7 @@ private:
*/
zend_constant _constant;
};
-
+
/**
* End of namespace
*/
diff --git a/zend/exception_handler.cpp b/zend/exception_handler.cpp
index 75faef9..66034e2 100644
--- a/zend/exception_handler.cpp
+++ b/zend/exception_handler.cpp
@@ -31,16 +31,13 @@ Value set_exception_handler(const std::function<Value(Parameters &params)> &hand
Value output;
// turn our user_exception_handler into a Value so we can return the original one later on
- if (EG(user_exception_handler)) output = EG(user_exception_handler);
+ if (!Z_ISNULL(EG(user_exception_handler))) output = &EG(user_exception_handler);
// detach so we have the zval
auto value = functor.detach(true);
- // allocate the user_exception_handler
- ALLOC_ZVAL(EG(user_exception_handler));
-
// copy our zval into the user_exception_handler
- MAKE_COPY_ZVAL(&value, EG(user_exception_handler));
+ ZVAL_COPY(value, &EG(user_exception_handler));
// return the original handler
return output;
@@ -61,16 +58,13 @@ Value set_error_handler(const std::function<Value(Parameters &params)> &handler,
Value output;
// turn our user_error_handler into a Value if we have one, just so we can return it later on
- if (EG(user_error_handler)) output = EG(user_error_handler);
+ if (!Z_ISNULL(EG(user_error_handler))) output = &EG(user_error_handler);
// detach so we have the zval
auto value = functor.detach(true);
- // alocate the user_error_handler
- ALLOC_ZVAL(EG(user_error_handler));
-
// copy our zval into the user_error_handler
- MAKE_COPY_ZVAL(&value, EG(user_error_handler));
+ ZVAL_COPY(value, &EG(user_error_handler));
EG(user_error_handler_error_reporting) = (int) error;
// return the original handler
@@ -98,7 +92,7 @@ Value error_reporting(Error error)
if (size < 0) return false;
// alter the ini on the fly
- zend_alter_ini_entry("error_reporting", sizeof("error_reporting"), str, size, ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME);
+ zend_alter_ini_entry(zend_string_init("error_reporting", sizeof("error_reporting"), 1), zend_string_init(str, size, 1), ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME);
// return the output
return output;
diff --git a/zend/executestate.h b/zend/executestate.h
index d71a252..edd7d5e 100644
--- a/zend/executestate.h
+++ b/zend/executestate.h
@@ -16,7 +16,7 @@ namespace Php {
/**
* Helper class to store and restore the current opcode state
- *
+ *
* When we're going to execute a set of instructions, we need to store the
* current state of the Zend engine. After the instructions have been processed,
* we can switch back to the original instructions
@@ -29,10 +29,9 @@ private:
* @var mixed
*/
zend_op_array *_active_op_array;
- zval **_return_value_ptr_ptr;
- zend_op **_opline_ptr;
- int _interactive;
-
+ zval *_return_value;
+ const zend_op *_opline;
+
/**
* The new value for 'no-extensions'
* @var int
@@ -46,7 +45,7 @@ private:
*/
void ***tsrm_ls;
#endif
-
+
public:
/**
* No trivial constructor
@@ -60,10 +59,9 @@ public:
ExecuteState(int no_extensions TSRMLS_DC)
{
// store all the original stuff
- _active_op_array = EG(active_op_array);
- _return_value_ptr_ptr = EG(return_value_ptr_ptr);
- _opline_ptr = EG(opline_ptr);
- _interactive = CG(interactive);
+ _active_op_array = CG(active_op_array);
+ _return_value = EG(current_execute_data)->return_value;
+ _opline = EG(current_execute_data)->opline;
_no_extensions = no_extensions;
#ifdef ZTS
@@ -78,11 +76,10 @@ public:
virtual ~ExecuteState()
{
// restore all settings
- CG(interactive) = _interactive;
EG(no_extensions) = _no_extensions;
- EG(opline_ptr) = _opline_ptr;
- EG(active_op_array) = _active_op_array;
- EG(return_value_ptr_ptr) = _return_value_ptr_ptr;
+ EG(current_execute_data)->opline = _opline;
+ CG(active_op_array) = _active_op_array;
+ EG(current_execute_data)->return_value = _return_value;
}
};
diff --git a/zend/hashiterator.h b/zend/hashiterator.h
index 36c3d6c..357ffa6 100644
--- a/zend/hashiterator.h
+++ b/zend/hashiterator.h
@@ -32,14 +32,17 @@ public:
HashIterator(HashTable *hashtable, bool first, bool is_array = false) : _table(hashtable), _is_array(is_array)
{
// reset the hash pointer to the internal position
- if (hashtable && first)
+ if (hashtable && first)
{
+ // we should be valid (this is undone later if necessary)
+ _valid = true;
+
// move to first position
zend_hash_internal_pointer_reset_ex(_table, &_position);
-
+
// read current data
if (read()) return;
-
+
// data was private, move on
increment();
}
@@ -49,7 +52,7 @@ public:
invalidate();
}
}
-
+
/**
* Copy constructor
* @param that
@@ -61,12 +64,12 @@ public:
// read current position
read();
}
-
+
/**
* Destructor
*/
- virtual ~HashIterator() {}
-
+ virtual ~HashIterator() = default;
+
/**
* Clone the object
* @param tsrm_ls
@@ -84,15 +87,18 @@ public:
*/
virtual bool increment() override
{
+ // leap out if we're not even iterating over a hash table
+ if (!_table) return false;
+
// leap out if already on an invalid pos (behind the last pos)
- if (!_position) return false;
-
+ if (!_valid) return false;
+
// move the iterator forward
if (zend_hash_move_forward_ex(_table, &_position) == SUCCESS)
{
// read current key and value
if (read()) return true;
-
+
// data was private or invalid, move further
return increment();
}
@@ -102,7 +108,7 @@ public:
return invalidate();
}
}
-
+
/**
* Decrement position (pre-decrement)
* @return bool
@@ -111,9 +117,9 @@ public:
{
// leap out if we're not even iterating over a hash table
if (!_table) return false;
-
+
// if position is invalid, it is one position behind the last position
- if (!_position)
+ if (!_valid)
{
// move to last position
zend_hash_internal_pointer_end_ex(_table, &_position);
@@ -126,7 +132,7 @@ public:
// read current key and value
if (read()) return true;
-
+
// data was private, move on
return decrement();
}
@@ -140,9 +146,9 @@ public:
{
// this always is a hash iterator
HashIterator *other = (HashIterator *)that;
-
- // compare the positions
- return _position == other->_position;
+
+ // compare the tables and positions
+ return _table == other->_table && _position == other->_position;
}
/**
@@ -156,6 +162,12 @@ public:
private:
/**
+ * Are we at a possibly valid position?
+ * @var bool
+ */
+ bool _valid = false;
+
+ /**
* The hash table over which is being iterated
* @var HashTable
*/
@@ -165,7 +177,7 @@ private:
* The position in the hash table
* @var HashPosition
*/
- Bucket *_position = nullptr;
+ HashPosition _position;
/**
* Is a hash interator in array
@@ -188,44 +200,17 @@ private:
// zval to read the current key in
Value key;
-#if PHP_VERSION_ID >= 50500
-
// read in the current key
zend_hash_get_current_key_zval_ex(_table, key._val, &_position);
-
+
// if the key is set to NULL, it means that the object is not at a valid position
if (key.isNull()) return invalidate();
-
-#else
-
- // php 5.3 and php 5.4 need a different implementation because the function
- // zend_hash_get_current_key_zval_ex is missing in php 5.3, declare variables
- // we need for storing the key in
- char *string_key;
- unsigned int str_len;
- unsigned long num_key;
-
- // get the current key
- int type = zend_hash_get_current_key_ex(_table, &string_key, &str_len, &num_key, 0, &_position);
-
- // if key is not found, the iterator is at an invalid position
- if (type == HASH_KEY_NON_EXISTANT) return invalidate();
-
- // numeric keys are the easiest ones
- if (type == HASH_KEY_IS_LONG) key = (int64_t)num_key;
- else key = std::string(string_key, str_len - 1);
-
-#endif
// iterator is at a valid position, go fetch the data
- // this is the variable we need for fetching the data
- zval **value;
-
- // retrieve data
- zend_hash_get_current_data_ex(_table, (void **) &value, &_position);
-
+ auto *value = zend_hash_get_current_data_ex(_table, &_position);
+
// we can now update the current data
- _current = std::make_pair<Value,Value>(std::move(key), *value);
+ _current = std::make_pair<Value,Value>(std::move(key), value);
// if the key is private (it starts with a null character) we should return
// false to report that the object is not in a completely valid state
@@ -238,12 +223,12 @@ private:
*/
bool invalidate()
{
- // forget current position
- _position = nullptr;
-
+ // no longer valid
+ _valid = false;
+
// make the data a pair of null ptrs
_current = std::make_pair<Value,Value>(nullptr,nullptr);
-
+
// done
return false;
}
diff --git a/zend/iteratorimpl.cpp b/zend/iteratorimpl.cpp
index 49526b1..9d57df7 100644
--- a/zend/iteratorimpl.cpp
+++ b/zend/iteratorimpl.cpp
@@ -20,7 +20,7 @@ namespace Php {
*/
static IteratorImpl *self(zend_object_iterator *iter)
{
- return (IteratorImpl *)iter->data;
+ return (IteratorImpl *)Z_PTR(iter->data);
}
/**
@@ -100,14 +100,14 @@ int IteratorImpl::key(zend_object_iterator *iter, char **str_key, uint *str_key_
{
// retrieve the key
Value retval(self(iter)->key());
-
+
// is this a numeric string?
if (retval.isString())
{
// copy the key and the from the value
*str_key = estrndup(retval.rawValue(), retval.size());
*str_key_len = retval.size() + 1;
-
+
// done
return HASH_KEY_IS_STRING;
}
@@ -115,7 +115,7 @@ int IteratorImpl::key(zend_object_iterator *iter, char **str_key, uint *str_key_
{
// convert to a numeric
*int_key = retval.numericValue();
-
+
// done
return HASH_KEY_IS_LONG;
}
@@ -151,13 +151,13 @@ zend_object_iterator_funcs *IteratorImpl::functions()
{
// static variable with all functions
static zend_object_iterator_funcs funcs;
-
+
// static variable that knows if the funcs are already initialized
static bool initialized = false;
-
+
// no need to set anything if already initialized
if (initialized) return &funcs;
-
+
// set the members
funcs.dtor = &IteratorImpl::destructor;
funcs.valid = &IteratorImpl::valid;
@@ -165,13 +165,13 @@ zend_object_iterator_funcs *IteratorImpl::functions()
funcs.get_current_key = &IteratorImpl::key;
funcs.move_forward = &IteratorImpl::next;
funcs.rewind = &IteratorImpl::rewind;
-
+
// invalidate is not yet supported
funcs.invalidate_current = nullptr;
-
+
// remember that functions are initialized
initialized = true;
-
+
// done
return &funcs;
}
diff --git a/zend/iteratorimpl.h b/zend/iteratorimpl.h
index 0a815e2..5286309 100644
--- a/zend/iteratorimpl.h
+++ b/zend/iteratorimpl.h
@@ -3,9 +3,9 @@
*
* Base class for iterators. Extension writers that want to create traversable
* classes, should override the Php::Traversable base class. This base class
- * forces you to implement a getIterator() method that returns an instance of
+ * forces you to implement a getIterator() method that returns an instance of
* a Php::Iterator class.
- *
+ *
* In this file you find the signature of the Php::Iterator class. It mostly has
* pure virtual methods, which means that you should create a derived class
* that implements all these methods.
@@ -32,9 +32,9 @@ private:
std::unique_ptr<Iterator> _iterator;
/**
- * The current() method that is called by the Zend engine wants a
- * pointer-to-pointer-to-a-zval. Because of this, we have to keep the
- * current value in memory after the current() method returns because
+ * The current() method that is called by the Zend engine wants a
+ * pointer-to-pointer-to-a-zval. Because of this, we have to keep the
+ * current value in memory after the current() method returns because
* the pointer would otherwise fall out of scope. This is (once again)
* odd behavior of the Zend engine, but we'll have to live with that
* @var Value
@@ -61,7 +61,7 @@ private:
{
return _iterator->valid();
}
-
+
/**
* The value at the current position
* @return Value
@@ -70,7 +70,7 @@ private:
{
return _iterator->current();
}
-
+
/**
* The key at the current position
* @return Value
@@ -79,7 +79,7 @@ private:
{
return _iterator->key();
}
-
+
/**
* Move to the next position
*/
@@ -87,7 +87,7 @@ private:
{
return _iterator->next();
}
-
+
/**
* Rewind the iterator to the front position
*/
@@ -95,7 +95,7 @@ private:
{
return _iterator->rewind();
}
-
+
/**
* Iterator destructor method
* @param iter
@@ -161,14 +161,16 @@ public:
* Constructor
* @param iterator The iterator that is implemented by the extension
*/
- IteratorImpl(Iterator *iterator) : _iterator(iterator)
+ IteratorImpl(Iterator *iterator) : _iterator(iterator)
{
+ // wrap it in a zval
+ ZVAL_PTR(&_impl.data, this);
+
// initialize impl object
- _impl.data = this;
_impl.index = 0;
_impl.funcs = functions();
}
-
+
/**
* Destructor
*/
@@ -183,7 +185,7 @@ public:
return &_impl;
}
};
-
+
/**
* End namespace
*/
diff --git a/zend/object.cpp b/zend/object.cpp
index 7da8339..0f6307e 100644
--- a/zend/object.cpp
+++ b/zend/object.cpp
@@ -76,13 +76,10 @@ Object::Object(zend_class_entry *entry, Base *base) : Value()
// member in the base object), this is a self-destructing object that
// will be destructed when the last reference to it has been removed,
// we already set the reference to zero
- new ObjectImpl(entry, base, 0 TSRMLS_CC);
+ new ObjectImpl(entry, base, ClassImpl::objectHandlers(entry) 0 TSRMLS_CC);
// now we can store it
operator=(Value(base));
-
- // install the object handlers
- Z_OBJVAL_P(_val).handlers = ClassImpl::objectHandlers(entry);
}
}
diff --git a/zend/objectimpl.h b/zend/objectimpl.h
index 110491b..b014bb1 100644
--- a/zend/objectimpl.h
+++ b/zend/objectimpl.h
@@ -27,32 +27,25 @@ private:
struct MixedObject
{
/**
- * The actual object is the first member, so that casting
- * the MixedObject to a zend_object will also result in a valid pointer
- * @var zend_object
- */
- zend_object php;
-
- /**
* Pointer to ourselves
* @var ObjectImpl
*/
ObjectImpl *self;
+ /**
+ * The actual object MUST be the last member, because PHP uses hackish
+ * tricks for optimization (we allocate more memory than sizeof(MixedObject))
+ * @var zend_object
+ */
+ zend_object php;
} *_mixed;
/**
* Pointer to the C++ implementation
- * @var Base
+ * @var std::unique_ptr<Base>
*/
- Base *_object;
-
- /**
- * The object handle in the Zend engine
- * @var int
- */
- int _handle;
+ std::unique_ptr<Base> _object;
public:
/**
@@ -61,65 +54,30 @@ public:
* This will create a new object in the Zend engine.
*
* @param entry Zend class entry
+ * @param handler Zend object handlers
* @param base C++ object that already exists
* @param refcount The initial refcount for the object
* @param tsrm_ls Optional threading data
*/
- ObjectImpl(zend_class_entry *entry, Base *base, int refcount TSRMLS_DC)
+ ObjectImpl(zend_class_entry *entry, Base *base, zend_object_handlers *handlers, int refcount TSRMLS_DC) :
+ _object(base)
{
// allocate a mixed object (for some reason this does not have to be deallocated)
- _mixed = (MixedObject *)emalloc(sizeof(MixedObject));
+ _mixed = (MixedObject *)ecalloc(1, sizeof(MixedObject) + zend_object_properties_size(entry));
// copy properties to the mixed object
_mixed->php.ce = entry;
_mixed->self = this;
- // store the c++ object
- _object = base;
-
- // initialize the object
- zend_object_std_init(&_mixed->php, entry TSRMLS_CC);
-
-#if PHP_VERSION_ID < 50399
-
- // tmp variable
- zval *tmp;
-
- // initialize the properties, php 5.3 way
- zend_hash_copy(_mixed->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_property_ctor, &tmp, sizeof(zval*));
-
-#else
-
- // version higher than 5.3 have an easier way to initialize
+ // initialize the object and its properties
+ zend_object_std_init (&_mixed->php, entry TSRMLS_CC);
object_properties_init(&_mixed->php, entry);
-#endif
-
-#ifdef ZTS
-
- // when in thread safety mode, the destruct method and free method have
- // an extra parameter holding thread information
- using DestructType = void(*)(zend_object*,unsigned int,void***);
- using FreeType = void(*)(zend_object*,void***);
-
-#else
-
- // not in thread mode: no special parameter for the tsrm_ls variable
- using DestructType = void(*)(zend_object*, unsigned int);
- using FreeType = void(*)(zend_object*);
-
-#endif
-
- // store the two destruct methods in temporary vars
- DestructType destructMethod = &ClassImpl::destructObject;
- FreeType freeMethod = &ClassImpl::freeObject;
-
- // 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(php(), (zend_objects_store_dtor_t)destructMethod, (zend_objects_free_object_storage_t)freeMethod, NULL TSRMLS_CC);
+ // install the handlers
+ _mixed->php.handlers = handlers;
// set the initial refcount (if it is different than one, because one is the default)
- if (refcount != 1) EG(objects_store).object_buckets[_handle].bucket.obj.refcount = refcount;
+ if (refcount != 1) php()->gc.refcount = refcount;
// the object may remember that we are its implementation object
base->_impl = this;
@@ -128,11 +86,7 @@ public:
/**
* Destructor
*/
- virtual ~ObjectImpl()
- {
- // deallocate the cpp object
- if (_object) delete _object;
- }
+ virtual ~ObjectImpl() = default;
/**
* Destruct the object
@@ -140,14 +94,24 @@ public:
*/
void destruct(TSRMLS_D)
{
- // pass on to the default destructor
- zend_objects_free_object_storage(php() TSRMLS_CC);
-
// destruct the object
delete this;
}
/**
+ * The offset between the zend_object and the ObjectImpl
+ * in bytes. This can be used to find the other when only
+ * a pointer to one is available.
+ *
+ * @return The offset in bytes
+ */
+ static constexpr size_t offset()
+ {
+ // calculate the offset in bytes
+ return offsetof(MixedObject, php);
+ }
+
+ /**
* Find the object based on a zval
* @param val Zval object
* @param tsrm_ls Optional pointer to thread info
@@ -155,11 +119,8 @@ public:
*/
static ObjectImpl *find(zval *val TSRMLS_DC)
{
- // retrieve the old object, which we are going to copy
- MixedObject *object = (MixedObject *)zend_object_store_get_object(val TSRMLS_CC);
-
- // done
- return object->self;
+ // retrieve the zend_object from the zval and use it to find the ObjectImpl
+ return find(Z_OBJ_P(val));
}
/**
@@ -169,8 +130,11 @@ public:
*/
static ObjectImpl *find(const zend_object *object)
{
- // retrieve the old object, which we are going to copy
- const MixedObject *mixed = (const MixedObject *)object;
+ // the zend_object is the last pointer in the struct so we have to subtract the
+ // correct number of bytes from the pointer to get at the address at which the
+ // actual ObjectImpl starts. to be able to actually perform this pointer arithmetic
+ // we must first cast the pointer to a char (void pointer arithmetic is not allowed!)
+ auto *mixed = (const MixedObject*)((char*)object - offset());
// done
return mixed->self;
@@ -182,7 +146,7 @@ public:
*/
Base *object() const
{
- return _object;
+ return _object.get();
}
/**
@@ -193,15 +157,6 @@ public:
{
return &_mixed->php;
}
-
- /**
- * Retrieve the handle object
- * @return int
- */
- int handle() const
- {
- return _handle;
- }
};
/**
diff --git a/zend/opcodes.h b/zend/opcodes.h
index 4e78083..1901f1f 100644
--- a/zend/opcodes.h
+++ b/zend/opcodes.h
@@ -29,14 +29,14 @@ public:
* Constructor
* @param opcodes
*/
- Opcodes(struct _zend_op_array *opcodes TSRMLS_DC) : _opcodes(opcodes)
+ Opcodes(struct _zend_op_array *opcodes TSRMLS_DC) : _opcodes(opcodes)
{
#ifdef ZTS
// copy tsrm_ls param
this->tsrm_ls = tsrm_ls;
#endif
}
-
+
/**
* Destructor
*/
@@ -44,12 +44,12 @@ public:
{
// leap out if opcodes were not valid
if (!_opcodes) return;
-
+
// clean up opcodes
destroy_op_array(_opcodes TSRMLS_CC);
efree(_opcodes);
}
-
+
/**
* Are the opcodes valid?
* @return bool
@@ -58,7 +58,7 @@ public:
{
return _opcodes != nullptr;
}
-
+
/**
* Execute the opcodes
* @return Value
@@ -69,41 +69,39 @@ public:
if (!_opcodes) return nullptr;
// pointer that is going to hold the return value of the script
- zval *retval_ptr = nullptr;
-
+ zval retval;
+
+ // initialize to null
+ ZVAL_NULL(&retval);
+
// the zend engine is probably already busy processing opcodes, so we store
// the current execute state before we're going to switch the runtime to
// our own set of opcodes
ExecuteState execState(0 TSRMLS_CC);
-
- // old execute state has been saved (and will automatically be restured when
+
+ // old execute state has been saved (and will automatically be restored when
// the oldstate is destructed), so we can now safely overwrite all the settings
- EG(return_value_ptr_ptr) = &retval_ptr;
- EG(active_op_array) = _opcodes;
+ CG(active_op_array) = _opcodes;
EG(no_extensions) = 1;
- if (!EG(active_symbol_table)) zend_rebuild_symbol_table(TSRMLS_C);
- CG(interactive) = 0;
-
+ if (!EG(current_execute_data)->symbol_table) zend_rebuild_symbol_table(TSRMLS_C);
+
// the current exception
- zval* oldException = EG(exception);
+ auto *oldException = EG(exception);
// execute the code
- zend_execute(_opcodes TSRMLS_CC);
+ zend_execute(_opcodes, &retval TSRMLS_CC);
- // was an exception thrown inside the eval()'ed code? In that case we
+ // was an exception thrown inside the eval()'ed code? In that case we
// throw a C++ new exception to give the C++ code the chance to catch it
- if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC);
+ // todo: OrigException with constructor for zend_object
+ // if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC);
// we're ready if there is no return value
- if (!retval_ptr) return nullptr;
-
+ if (ZVAL_IS_NULL(&retval)) return nullptr;
+
// wrap the return value
- Value result(retval_ptr);
-
- // destruct the zval (this function will decrement the reference counter,
- // and only destruct if there are no other references left)
- zval_ptr_dtor(&retval_ptr);
-
+ Value result(&retval);
+
// copy the pointer into a value object, and return that
return result;
}
@@ -124,7 +122,7 @@ private:
#endif
};
-
+
/**
* End of namespace
*/
diff --git a/zend/origexception.h b/zend/origexception.h
index 55f89cf..f997608 100644
--- a/zend/origexception.h
+++ b/zend/origexception.h
@@ -21,12 +21,12 @@ class OrigException : public Value, public Exception
private:
/**
* Is this a an exception that was caught by extension C++ code.
- *
+ *
* When the object is initially created, we assume that it will be caught
* by C++ code. If it later turns out that the PHP-CPP can catch this
* exception after the extension C++ code ran, the variable is set back
* to false.
- *
+ *
* @var bool
*/
bool _handled = true;
@@ -38,40 +38,40 @@ private:
*/
TSRMLS_D;
#endif
-
+
public:
/**
* Constructor
* @param val
*/
- OrigException(zval *val TSRMLS_DC) :
- Value(val), Exception("OrigException")
+ OrigException(zval *val TSRMLS_DC) :
+ Value(val), Exception("OrigException")
{
#ifdef ZTS
// copy tsrm_ls
this->TSRMLS_C = TSRMLS_C;
#endif
}
-
+
/**
* Copy constructor
* @param exception
*/
- OrigException(const OrigException &exception) :
- Value(exception), Exception("OrigException"), _handled(exception._handled)
+ OrigException(const OrigException &exception) :
+ Value(exception), Exception("OrigException"), _handled(exception._handled)
{
#ifdef ZTS
// copy tsrm_ls
TSRMLS_C = exception.TSRMLS_C;
#endif
}
-
+
/**
* Move constructor
* @param exception
*/
OrigException(OrigException &&exception) :
- Value(std::move(exception)), Exception("OrigException"), _handled(exception._handled)
+ Value(std::move(exception)), Exception("OrigException"), _handled(exception._handled)
{
// set other exception to handled so that it wont do anything on destruction
exception._handled = true;
@@ -81,7 +81,7 @@ public:
TSRMLS_C = exception.TSRMLS_C;
#endif
}
-
+
/**
* Destructor
*/
@@ -90,11 +90,11 @@ public:
// if the exception was not handled by C++ code, we're not going to do anything
// and the exception stays active
if (!_handled) return;
-
+
// the exception was handled, so we should clean it up
zend_clear_exception(TSRMLS_C);
}
-
+
/**
* This is _not_ a native exception, it was thrown by a PHP script
* @return bool
@@ -103,7 +103,7 @@ public:
{
return false;
}
-
+
/**
* Reactivate the exception
*/
@@ -127,15 +127,15 @@ inline void process(Exception &exception TSRMLS_DC)
// the exception is native, call the zend throw method
zend_throw_exception(zend_exception_get_default(TSRMLS_C), (char *)exception.what(), 0 TSRMLS_CC);
}
-
+
// or does it have its own report function?
else if (!exception.report())
{
- // this is not a native exception, so it was originally thrown by a
- // php script, and then not caught by the c++ of the extension, we are
+ // this is not a native exception, so it was originally thrown by a
+ // php script, and then not caught by the c++ of the extension, we are
// going to tell to the exception that it is still active
OrigException &orig = static_cast<OrigException&>(exception);
-
+
// reactive the exception
orig.reactivate();
}
diff --git a/zend/parametersimpl.h b/zend/parametersimpl.h
index 2841c75..178f1be 100644
--- a/zend/parametersimpl.h
+++ b/zend/parametersimpl.h
@@ -2,7 +2,7 @@
* ParametersImpl.h
*
* Extended parameters class that can be instantiated
- *
+ *
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2013 Copernica BV
*/
@@ -28,22 +28,33 @@ public:
{
// reserve plenty of space
reserve(argc);
-
+
+ // array to store all the arguments in
+ zval arguments[argc];
+
+ // retrieve the arguments
+ zend_get_parameters_array_ex(argc, arguments);
+
// loop through the arguments
for (int i=0; i<argc; i++)
{
- // get the argument
- zval **arg = (zval **) (zend_vm_stack_top(TSRMLS_C) - 1 - (argc-i));
-
// append value
- emplace_back(*arg);
+ emplace_back(&arguments[i]);
}
}
-
+
/**
- * Destructor
+ * Do _not_ add a virtual destructor here.
+ *
+ * We are extending a vector, which does not itself
+ * have a virtual destructor, so destructing through
+ * a pointer to this vector has no effect.
+ *
+ * By adding a virtual destructor we create a vtable,
+ * which makes the class bigger, causing slicing and
+ * then we are actually introducing the problem that
+ * we are trying to avoid!
*/
- virtual ~ParametersImpl() {}
};
/**
diff --git a/zend/traverseiterator.h b/zend/traverseiterator.h
index a65f909..27c4832 100644
--- a/zend/traverseiterator.h
+++ b/zend/traverseiterator.h
@@ -30,20 +30,20 @@ public:
{
// leap out if this iterator starts at the end
if (!begin) return;
-
+
// we need the class entry
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
-
+ auto *entry = Z_OBJCE_P(object);
+
// create the iterator
_iter = entry->get_iterator(entry, object, false TSRMLS_CC);
-
+
// rewind the iterator
_iter->funcs->rewind(_iter TSRMLS_CC);
-
+
// read the first key/value pair
read(TSRMLS_C);
}
-
+
/**
* Copy constructor
* @param that
@@ -54,7 +54,7 @@ public:
// @todo this is a broken implementation, the copy is at the start
// position, while we'd like to be at the same position
}
-
+
/**
* Destructor
*/
@@ -62,10 +62,10 @@ public:
{
// do nothing if iterator is already invalid
if (!_iter) return;
-
+
// we need the tsrm pointer
TSRMLS_FETCH();
-
+
// call the iterator destructor
if (_iter) _iter->funcs->dtor(_iter TSRMLS_CC);
}
@@ -79,7 +79,7 @@ public:
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
-
+
// construct iterator
return new TraverseIterator(*this TSRMLS_CC);
}
@@ -96,17 +96,17 @@ public:
// we need the tsrm_ls variable
TSRMLS_FETCH();
-
+
// movw it forward
_iter->funcs->move_forward(_iter TSRMLS_CC);
-
+
// and read current data
read(TSRMLS_C);
-
+
// done
return true;
}
-
+
/**
* Decrement position (pre-decrement)
* @return bool
@@ -115,7 +115,7 @@ public:
{
// not possible with PHP iterators
throw Exception("Impossible to iterate backwards");
-
+
// unreachable
return false;
}
@@ -129,13 +129,13 @@ public:
{
// of course if the objects are identical
if (this == that) return true;
-
+
// cast to traverse-iterator
TraverseIterator *other = (TraverseIterator *)that;
-
+
// if both objects are in an invalid state we consider them to be identical
if (!_iter && !other->_iter) return true;
-
+
// although the iterators could be at the same pos, for simplicity
// we consider them different here
return false;
@@ -156,7 +156,7 @@ private:
* @var _val
*/
zval *_object = nullptr;
-
+
/**
* The iterator from Zend
* @var zend_object_iterator
@@ -183,60 +183,25 @@ private:
// is the iterator at a valid position?
if (_iter->funcs->valid(_iter TSRMLS_CC) == FAILURE) return invalidate(TSRMLS_C);
-#if PHP_VERSION_ID >= 50500
-
// create a value object
Value val;
-
+
// call the function to get the key
_iter->funcs->get_current_key(_iter, val._val TSRMLS_CC);
-
+
// store the key
_data.first = val;
-#else
-
- // variable we need for fetching the key, and that will be assigned by
- // the PHP engine (this is php 5.3 code)
- char *str_key; unsigned int str_key_len; unsigned long int_key;
-
- // php 5.4 or php 5.3 code, fetch the current key
- int type = _iter->funcs->get_current_key(_iter, &str_key, &str_key_len, &int_key TSRMLS_CC);
-
- // what sort of key do we have?
- if (type == HASH_KEY_IS_LONG)
- {
- // we have an int key
- _data.first = (int64_t)int_key;
- }
- else
- {
- // we have a string key that is already allocated
- _data.first = str_key;
-
- // deallocate the data
- efree(str_key);
- }
-
-#endif
-
- // now we're going to fetch the value, for this we need a strange zval
- // it is strange that this is a pointer-to-pointer, but that is how
- // the Zend engine implements this. It is going to be filled with
- // a pointer to a memory address that is guaranteed to hold a valid
- // zval.
- zval **zval;
-
// get the current value
- _iter->funcs->get_current_data(_iter, &zval TSRMLS_CC);
-
+ auto *zval = _iter->funcs->get_current_data(_iter);
+
// wrap the zval in a value object
- _data.second = Value(*zval);
-
+ _data.second = Value(zval);
+
// done
return true;
}
-
+
/**
* Invalidate the object
* @param tsrm_ls
@@ -246,13 +211,13 @@ private:
{
// skip if already invalid
if (!_iter) return false;
-
+
// reset the iterator
_iter->funcs->dtor(_iter TSRMLS_CC);
-
+
// set back to null
_iter = nullptr;
-
+
// done
return false;
}