summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/base.h2
-rw-r--r--include/file.h13
-rw-r--r--include/global.h22
-rw-r--r--include/ini.h50
-rw-r--r--include/parameters.h12
-rw-r--r--include/type.h28
-rw-r--r--include/value.h43
-rw-r--r--zend/callable.cpp84
-rw-r--r--zend/callable.h97
-rw-r--r--zend/classimpl.cpp347
-rw-r--r--zend/classimpl.h149
-rw-r--r--zend/compileroptions.h16
-rw-r--r--zend/constantfuncs.cpp44
-rw-r--r--zend/constantimpl.h82
-rw-r--r--zend/exception_handler.cpp16
-rw-r--r--zend/executestate.h25
-rw-r--r--zend/exists.cpp39
-rw-r--r--zend/extensionimpl.cpp97
-rw-r--r--zend/extensionimpl.h40
-rw-r--r--zend/file.cpp59
-rw-r--r--zend/functor.cpp8
-rw-r--r--zend/global.cpp69
-rw-r--r--zend/globals.cpp24
-rw-r--r--zend/hashiterator.h91
-rw-r--r--zend/ini.cpp35
-rw-r--r--zend/iteratorimpl.cpp35
-rw-r--r--zend/iteratorimpl.h39
-rw-r--r--zend/nativefunction.h8
-rw-r--r--zend/object.cpp31
-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/super.cpp12
-rw-r--r--zend/traverseiterator.h93
-rw-r--r--zend/value.cpp514
36 files changed, 1097 insertions, 1367 deletions
diff --git a/include/base.h b/include/base.h
index 1a1643b..023d711 100644
--- a/include/base.h
+++ b/include/base.h
@@ -50,7 +50,7 @@ public:
/**
* Virtual destructor
*/
- virtual ~Base() {}
+ virtual ~Base() = default;
/**
* Get access to a property by name using the [] operator
diff --git a/include/file.h b/include/file.h
index fff3cd0..4fab1c5 100644
--- a/include/file.h
+++ b/include/file.h
@@ -9,6 +9,11 @@
*/
/**
+ * Forward declarations
+ */
+struct _zend_string;
+
+/**
* Set up namespace
*/
namespace Php {
@@ -81,15 +86,15 @@ public:
private:
/**
* The full resolved path name
- * @var const char *
+ * @var struct _zend_string*
*/
- char *_path = nullptr;
+ struct _zend_string *_path = nullptr;
/**
* The opcodes of this file
- * @var Opcodes
+ * @var std::unique_ptr<Opcodes>
*/
- Opcodes *_opcodes = nullptr;
+ std::unique_ptr<Opcodes> _opcodes;
/**
* Compile the file
diff --git a/include/global.h b/include/global.h
index 7a66997..605aeec 100644
--- a/include/global.h
+++ b/include/global.h
@@ -9,9 +9,10 @@
*/
/**
- * Forward definitions
+ * Forward declarations
*/
struct _zval_struct;
+struct _zend_string;
/**
* Namespace
@@ -34,12 +35,12 @@ public:
* Move constructor
* @param global
*/
- Global(Global &&global) _NOEXCEPT : Value(std::move(global)), _name(std::move(global._name)), _exists(global._exists) {}
+ Global(Global &&global) _NOEXCEPT;
/**
* Destructor
*/
- virtual ~Global() {}
+ virtual ~Global();
/**
* Assignment operator
@@ -143,35 +144,36 @@ protected:
private:
/**
* Constructor for non-existing var
- * @param name
+ *
+ * @param name Name for the variable that does not exist
*/
- Global(const char *name) : Value(), _name(name), _exists(false) {}
+ Global(const char *name);
/**
* Alternative constructor for non-existing var
* @param name
*/
- Global(const std::string &name) : Value(), _name(name), _exists(false) {}
+ Global(const std::string &name);
/**
* Constructor to wrap zval for existing global bar
* @param name
* @param val
*/
- Global(const char *name, struct _zval_struct *val) : Value(val, true), _name(name), _exists(true) {}
+ Global(const char *name, struct _zval_struct *val);
/**
* Alternative constructor to wrap zval
* @param name
* @param val
*/
- Global(const std::string &name, struct _zval_struct *val) : Value(val, true), _name(name), _exists(true) {}
+ Global(const std::string &name, struct _zval_struct *val);
/**
* Name of the variable
- * @var string
+ * @var struct _zend_string*
*/
- std::string _name;
+ struct _zend_string *_name;
/**
* Does it already exist?
diff --git a/include/ini.h b/include/ini.h
index 592fba3..a1899a4 100644
--- a/include/ini.h
+++ b/include/ini.h
@@ -47,68 +47,46 @@ public:
*
* @param name Name of the php.ini variable
* @param value Default value
- * @param orig Original value (if the user resets the variable, it is set back to this value)
* @param place Place where the ini setting can be changed
*/
- Ini(const char *name, const char *value, const char *orig, const Place place = Place::All) :
- _name(name), _value(value), _orig(orig), _place(place) {}
-
Ini(const char *name, const char *value, const Place place = Place::All) :
- _name(name), _value(value), _orig_empty(true), _place(place) {}
+ _name(name), _value(value), _place(place) {}
/**
* Constructors for bool values
*
* @param name Name of the php.ini variable
* @param value Default value
- * @param orig Original value (if the user resets the variable, it is set back to this value)
* @param place Place where the ini setting can be changed
*/
- Ini(const char *name,const bool value, const bool orig, const Place place = Place::All) :
- _name(name), _value(bool2str(value)), _orig(bool2str(orig)), _place(place) {}
-
- Ini(const char *name, const bool value, const Place place = Place::All) :
- _name(name), _value(bool2str(value)), _orig_empty(true), _place(place) {}
+ Ini(const char *name, bool value, const Place place = Place::All) :
+ _name(name), _value(bool2str(value)), _place(place) {}
/**
* Constructors for integer values
*
* @param name Name of the php.ini variable
* @param value Default value
- * @param orig Original value (if the user resets the variable, it is set back to this value)
* @param place Place where the ini setting can be changed
*/
- Ini(const char *name, const int16_t value, const int16_t orig, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {}
-
Ini(const char *name, const int16_t value, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {}
-
- Ini(const char *name, const int32_t value, const int32_t orig, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {}
+ _name(name), _value(std::to_string(value)), _place(place) {}
Ini(const char *name, const int32_t value, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {}
-
- Ini(const char *name, const int64_t value, const int64_t orig, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {}
+ _name(name), _value(std::to_string(value)), _place(place) {}
Ini(const char *name, const int64_t value, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {}
+ _name(name), _value(std::to_string(value)), _place(place) {}
/**
* Constructors for floating point values
*
* @param name Name of the php.ini variable
* @param value Default value
- * @param orig Original value (if the user resets the variable, it is set back to this value)
* @param place Place where the ini setting can be changed
*/
- Ini(const char *name, const double value, const double orig, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig(std::to_string(orig)), _place(place) {}
-
Ini(const char *name, const double value, const Place place = Place::All) :
- _name(name), _value(std::to_string(value)), _orig_empty(true), _place(place) {}
+ _name(name), _value(std::to_string(value)), _place(place) {}
/**
@@ -116,7 +94,7 @@ public:
* @param ini_entry
* @param module_number
*/
- void fill(struct _zend_ini_entry *ini_entry, int module_number);
+ void fill(struct _zend_ini_entry_def *ini_entry, int module_number);
private:
@@ -148,18 +126,6 @@ private:
std::string _value;
/**
- * ini entry original value
- * @var std::string
- */
- std::string _orig;
-
- /**
- * Is the orig value set or empty?
- * @var bool
- */
- bool _orig_empty = false;
-
- /**
* Place where the configuration can be changed
* @var Place
*/
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/include/type.h b/include/type.h
index 151919c..fbffb1f 100644
--- a/include/type.h
+++ b/include/type.h
@@ -18,17 +18,23 @@ namespace Php {
* The values are the same as the ones used internally in Zend
*/
enum class PHPCPP_EXPORT Type : unsigned char {
- Null = 0, // Null will allow any type
- Numeric = 1,
- Float = 2,
- Bool = 3,
- Array = 4,
- Object = 5,
- String = 6,
- Resource = 7,
- Constant = 8,
- ConstantArray = 9,
- Callable = 10
+ Undefined = 0, // Variable is not set
+ Null = 1, // Null will allow any type
+ False = 2, // Boolean false
+ True = 3, // Boolean true
+ Numeric = 4, // Integer type
+ Float = 5, // Floating point type
+ String = 6, // A string obviously
+ Array = 7, // An array of things
+ Object = 8, // An object
+ Resource = 9, // A resource
+ Reference = 10, // Reference to another value (can be any type!)
+ Constant = 11, // A constant value
+ ConstantAST = 12, // I think an Abstract Syntax tree, not quite sure
+
+ // "fake types", not quite sure what that means
+ Bool = 13, // You will never get this back as a type
+ Callable = 14, // I don't know why this is a "fake" type
};
/**
diff --git a/include/value.h b/include/value.h
index 8c10bd8..afd532d 100644
--- a/include/value.h
+++ b/include/value.h
@@ -382,7 +382,7 @@ public:
*/
bool isNull() const { return type() == Type::Null; }
bool isNumeric() const { return type() == Type::Numeric; }
- bool isBool() const { return type() == Type::Bool; }
+ bool isBool() const { return type() == Type::False || type() == Type::True; }
bool isString() const { return type() == Type::String; }
bool isFloat() const { return type() == Type::Float; }
bool isObject() const { return type() == Type::Object; }
@@ -403,18 +403,6 @@ public:
char *buffer() const;
/**
- * Resize buffer space. If you want to write directly to the buffer (which
- * is returned by the buffer() method), you should first reserve enough
- * space in it. This can be done with this reserve() method. This will also
- * turn the Value object into a string (if it was not already a string).
- * The writable buffer is returned.
- *
- * @param size
- * @return char*
- */
- char *reserve(size_t size);
-
- /**
* Get access to the raw buffer for read operations. Note that this
* only works for string variables - other variables return nullptr.
*
@@ -977,14 +965,9 @@ public:
{
// store arguments
Value vargs[] = { static_cast<Value>(args)... };
- //Value vargs[] = { std::forward<Value>(args)... };
-
- // array of parameters
- _zval_struct **params[sizeof...(Args)];
- for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;}
// call the function
- return exec(sizeof...(Args), params);
+ return exec(sizeof...(Args), vargs);
}
/**
@@ -1021,12 +1004,8 @@ public:
// store arguments
Value vargs[] = { static_cast<Value>(args)... };
- // array of parameters
- _zval_struct **params[sizeof...(Args)];
- for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;}
-
// call the function
- return exec(name, sizeof...(Args), params);
+ return exec(name, sizeof...(Args), vargs);
}
template <typename ...Args>
@@ -1035,12 +1014,8 @@ public:
// store arguments
Value vargs[] = { static_cast<Value>(args)... };
- // array of parameters
- _zval_struct **params[sizeof...(Args)];
- for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;}
-
// call the function
- return exec(name, sizeof...(Args), params);
+ return exec(name, sizeof...(Args), vargs);
}
/**
@@ -1116,7 +1091,7 @@ private:
* @param argv The parameters
* @return Value
*/
- Value exec(int argc, struct _zval_struct ***params) const;
+ Value exec(int argc, Value *argv) const;
/**
* Call method with a number of parameters
@@ -1125,8 +1100,8 @@ private:
* @param argv The parameters
* @return Value
*/
- Value exec(const char *name, int argc, struct _zval_struct ***params) const;
- Value exec(const char *name, int argc, struct _zval_struct ***params);
+ Value exec(const char *name, int argc, Value *argv) const;
+ Value exec(const char *name, int argc, Value *argv);
/**
* Refcount - the number of references to the value
@@ -1137,9 +1112,9 @@ private:
protected:
/**
* The wrapped zval
- * @var struct zval
+ * @var struct zval*
*/
- struct _zval_struct *_val;
+ struct _zval_struct *_val = nullptr;
/**
* Detach the zval
diff --git a/zend/callable.cpp b/zend/callable.cpp
index f0dc3e8..cf0dd64 100644
--- a/zend/callable.cpp
+++ b/zend/callable.cpp
@@ -14,6 +14,16 @@
namespace Php {
/**
+ * Map function names to their implementation
+ *
+ * This is braindead, there should be a way to get this information
+ * from the "This" zval in the execute_data, I just can't find it
+ * @todo Find a better way for this
+ * @var std::map<std::string, Callable*>
+ */
+static std::map<std::string, Callable*> callables;
+
+/**
* Function that is called by the Zend engine every time that a function gets called
* @param ht
* @param return_value
@@ -27,9 +37,9 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
{
// find the function name
const char *name = get_active_function_name(TSRMLS_C);
-
- // uncover the hidden pointer inside the function name
- Callable *callable = HiddenPointer<Callable>(name);
+
+ // retrieve the callable from the map
+ auto *callable = callables.find(name)->second;
// check if sufficient parameters were passed (for some reason this check
// is not done by Zend, so we do it here ourselves)
@@ -44,7 +54,7 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
else
{
// construct parameters
- ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);
+ ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC);
// the function could throw an exception
try
@@ -52,11 +62,6 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
// get the result
Value result(callable->invoke(params));
- // we're ready if the return value is not even used
- if (!return_value_used) return;
-
- // @todo php 5.6 has a RETVAL_ZVAL_FAST macro that can be used instead (and is faster)
-
// return a full copy of the zval, and do not destruct it
RETVAL_ZVAL(result._val, 1, 0);
}
@@ -70,25 +75,28 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
/**
* Fill a function entry
- *
- * This method is called when the extension is registering itself, when the
- * function or method introces himself
- *
+ *
+ * This method is called when the extension is registering itself, when the
+ * function or method introduces himself
+ *
* @param entry Entry to be filled
* @param classname Optional class name
* @param flags Is this a public property?
*/
void Callable::initialize(zend_function_entry *entry, const char *classname, int flags) const
{
+ // track the callable
+ callables[_name] = const_cast<Callable*>(this);
+
// fill the members of the entity, and hide a pointer to the current object in the name
- entry->fname = (const char *)_ptr;
+ entry->fname = _name.data();
entry->handler = &Callable::invoke;
- entry->arg_info = _argv;
+ entry->arg_info = _argv.get();
entry->num_args = _argc;
entry->flags = flags;
// we should fill the first argument as well
- initialize((zend_arg_info *)entry->arg_info, classname);
+ initialize((zend_internal_function_info*)_argv.get(), classname);
}
/**
@@ -96,51 +104,23 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int
* @param info Info to be filled
* @param classname Optional classname
*/
-void Callable::initialize(zend_arg_info *info, const char *classname) const
+void Callable::initialize(zend_internal_function_info *info, const char *classname) const
{
-#if PHP_VERSION_ID >= 50400
- // up until php 5.3, the first info object is filled with alternative information,
- // later it is casted to a zend_internal_function object
- auto *finfo = (zend_internal_function_info *)info;
-
- // fill in all the members, note that return reference is false by default,
- // because we do not support returning references in PHP-CPP, although Zend
- // engine allows it. Inside the name we hide a pointer to the current object
- finfo->_name = _ptr;
- finfo->_name_len = ::strlen(_ptr);
- finfo->_class_name = classname;
+ // store the classname
+ info->class_name = classname;
// number of required arguments, and the expected return type
- finfo->required_num_args = _required;
- finfo->_type_hint = (unsigned char)_return;
+ info->required_num_args = _required;
+ info->type_hint = (unsigned char)_return;
// we do not support return-by-reference
- finfo->return_reference = false;
-
-# if PHP_VERSION_ID >= 50600
+ info->return_reference = false;
+
// since php 5.6 there are _allow_null and _is_variadic properties. It's
// not exactly clear what they do (@todo find this out) so for now we set
// them to false
- finfo->_allow_null = false;
- finfo->_is_variadic = false;
-
-# else
- // passing by reference is not used (only for php 5.4 and php 5.5)
- finfo->pass_rest_by_reference = false;
-# endif
-
-#else
- // php 5.3 code
- info->name = nullptr;
- info->name_len = 0;
- info->class_name = nullptr;
- info->class_name_len = 0;
- info->array_type_hint = false;
info->allow_null = false;
- info->pass_by_reference = false;
- info->return_reference = false;
- info->required_num_args = _required;
-#endif
+ info->_is_variadic = false;
}
/**
diff --git a/zend/callable.h b/zend/callable.h
index bd74f27..f6a172f 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>
@@ -9,11 +9,16 @@
*/
/**
+ * Dependencies
+ */
+#include <cstring>
+
+/**
* Set up namespace
*/
namespace Php {
-/**
+/**
* Class definition
*/
class Callable
@@ -24,68 +29,62 @@ public:
* @param name Function or method name
* @param arguments Information about the arguments
*/
- Callable(const char *name, const Arguments &arguments = {}) : _ptr(this, name)
+ Callable(const char *name, const Arguments &arguments = {}) :
+ _name(name),
+ _argc(arguments.size()),
+ _argv(new zend_internal_arg_info[_argc + 1])
{
- // 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++)
+ for (auto &argument : arguments)
{
// increment counter with number of required parameters
- if (it->required()) _required++;
-
+ if (argument.required()) _required++;
+
// fill the arg info
- fill(&_argv[i++], *it);
+ fill(&_argv[i++], argument);
}
}
-
+
/**
* Copy constructor
* @param that
*/
Callable(const Callable &that) :
- _ptr(that._ptr),
+ _name(that._name),
_return(that._return),
_required(that._required),
_argc(that._argc),
_argv(nullptr) {}
-
+ // @todo: we have no arguments after copy? is this correct?
+ // we do have the argument count though...
+
/**
* Move constructor
* @param that
*/
Callable(Callable &&that) :
- _ptr(std::move(that._ptr)),
+ _name(std::move(that._name)),
_return(that._return),
_required(that._required),
_argc(that._argc),
- _argv(that._argv)
- {
- // invalidate other object
- that._argv = nullptr;
- }
+ _argv(std::move(that._argv)) {}
/**
* Destructor
*/
- virtual ~Callable()
- {
- if (_argv) delete[] _argv;
- }
-
+ virtual ~Callable() = default;
+
/**
* 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
@@ -101,27 +100,27 @@ public:
* @param ns Active namespace
* @param classname Optional class name
*/
- void initialize(zend_arg_info *info, const char *classname = nullptr) const;
+ void initialize(zend_internal_function_info *info, const char *classname = nullptr) const;
protected:
/**
- * Hidden pointer to the name and the function
- * @var HiddenPointer
+ * Name of the function
+ * @var std::string
*/
- HiddenPointer<Callable> _ptr;
+ std::string _name;
/**
* Suggestion for the return type
* @var Type
*/
- Type _return = Type::Null;
+ Type _return = Type::Undefined;
/**
* Required number of arguments
- * @var integer
+ * @var unsigned integer
*/
- int _required = 0;
+ unsigned int _required = 0;
/**
* Total number of arguments
@@ -131,22 +130,23 @@ protected:
/**
* The arguments
- * @var zend_arg_info[]
+ * @var std::unique_ptr<zend_internal_arg_info[]>
*/
- zend_arg_info *_argv = nullptr;
-
+ std::unique_ptr<zend_internal_arg_info[]> _argv;
+
/**
* Private helper method to fill an argument object
* @param info object from the zend engine
* @param arg original object
*/
- void fill(zend_arg_info *info, const Argument &arg) const
+ void fill(zend_internal_arg_info *info, const Argument &arg) const
{
// fill members
info->name = arg.name();
- info->name_len = ::strlen(arg.name());
-#if PHP_VERSION_ID >= 50400
+ // are we filling an object
+ if (arg.type() == Type::Object) info->class_name = arg.classname();
+ 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 +155,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..75191f5 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,12 +20,10 @@ namespace Php {
ClassImpl::~ClassImpl()
{
// destruct the entries
- if (_entries) delete[] _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
+ if (_self) zend_string_release(_self);
}
/**
@@ -38,8 +37,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
@@ -58,37 +55,18 @@ 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
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
}
/**
@@ -113,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
@@ -134,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();
@@ -162,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;
@@ -181,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();
@@ -203,17 +177,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 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(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
@@ -224,17 +195,13 @@ 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, 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
@@ -254,7 +221,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 +233,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 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, nullptr TSRMLS_CC);
// did the default implementation do anything?
if (defaultFunction) return defaultFunction;
@@ -318,7 +282,7 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, char* method,
* @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
@@ -327,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));
@@ -342,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
@@ -356,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;
@@ -397,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;
@@ -433,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;
@@ -481,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;
@@ -489,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
@@ -539,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_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);
+ auto *entry = Z_OBJCE_P(val);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -564,27 +537,18 @@ 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)
- 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 result;
+ return new_object->php();
}
/**
@@ -642,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?
//
@@ -675,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)
{
@@ -692,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);
}
}
@@ -832,42 +797,66 @@ 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;
}
/**
* 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 Pointer to where to store the data
* @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?
//
@@ -890,7 +879,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, const zend_lit
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);
@@ -910,12 +899,12 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, const zend_lit
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)
@@ -924,11 +913,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
+ return std_object_handlers.read_property(object, name, type, cache_slot, rv TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -950,21 +935,17 @@ 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();
// 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);
@@ -1001,11 +982,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
+ std_object_handlers.write_property(object, name, value, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1031,15 +1008,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
@@ -1049,7 +1022,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, const z
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);
@@ -1082,11 +1055,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
+ return std_object_handlers.has_property(object, name, has_set_exists, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1106,21 +1075,17 @@ 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
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);
@@ -1143,11 +1108,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
+ std_object_handlers.unset_property(object, member, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1161,10 +1122,9 @@ void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *ke
* 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, zend_object_handle handle TSRMLS_DC)
+void ClassImpl::destructObject(zend_object *object TSRMLS_DC)
{
// find object
ObjectImpl *obj = ObjectImpl::find(object);
@@ -1181,7 +1141,7 @@ void ClassImpl::destructObject(zend_object *object, zend_object_handle handle TS
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)
{
@@ -1212,7 +1172,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 +1185,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->php();
}
/**
@@ -1288,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());
@@ -1328,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
@@ -1390,7 +1341,7 @@ const struct _zend_function_entry *ClassImpl::entries()
zend_function_entry *last = &_entries[i];
// all should be set to zero
- memset(last, 0, sizeof(zend_function_entry));
+ memset(last, 0, sizeof(*last));
// done
return _entries;
@@ -1448,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
{
@@ -1478,29 +1429,15 @@ 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 *));
+ // allocate memory for the doc_comment (which we abuse for storing a pointer to ourselves)
+ _self = zend_string_alloc(sizeof(this), 1);
- // Copy the pointer after it
- memcpy(_entry->name + _entry->name_length + 1, &impl, sizeof(ClassImpl *));
+ // make the string appear empty
+ ZSTR_VAL(_self)[0] = '\0';
+ ZSTR_LEN(_self) = 0;
-#endif
+ // 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;
diff --git a/zend/classimpl.h b/zend/classimpl.h
index 18802e3..370fa81 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
@@ -93,27 +93,28 @@ private:
/**
* Memory allocated by this object to hide a pointer
- * @var char*
+ * @var zend_string*
*/
- char *_self = nullptr;
+ zend_string *_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[]
*/
const zend_function_entry *entries();
/**
* 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)
*/
- static zval *toZval(Value &&value, int type);
+ static zval *toZval(Value &&value, int type, zval *rv);
public:
/**
@@ -155,16 +156,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 +175,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);
/**
@@ -190,12 +191,12 @@ public:
* @param return_value_used Is the return value used or not?
* @param tsrm_ls
*/
- static void callMethod(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC);
- static void callInvoke(int ht, zval *return_value, zval **return_value_ptr, zval *this_ptr, int return_value_used TSRMLS_DC);
+ static void callMethod(zend_execute_data *execute_data, zval *return_value TSRMLS_DC);
+ static void callInvoke(zend_execute_data *execute_data, zval *return_value TSRMLS_DC);
/**
* 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
@@ -210,10 +211,11 @@ public:
* @param offset The name of the property
* @param value The new value
* @param type The type of the variable???
+ * @param rv Pointer to where to store the data
* @param check_empty ????
* @return zval
*/
- static zval *readDimension(zval *object, zval *offset, int type TSRMLS_DC);
+ static zval *readDimension(zval *object, zval *offset, int type, zval *rv TSRMLS_DC);
static void writeDimension(zval *object, zval *offset, zval *value TSRMLS_DC);
static int hasDimension(zval *object, zval *offset, int check_empty TSRMLS_DC);
static void unsetDimension(zval *object, zval *offset TSRMLS_DC);
@@ -230,7 +232,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 +245,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 Pointer to where to store the data
* @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 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(zend_object **object, 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 TSRMLS_DC);
/**
* Method that returns information about the __invoke() method
@@ -332,7 +321,7 @@ public:
* @param tsrm_ls
* @return int
*/
- static int getClosure(zval *object, zend_class_entry **entry, zend_function **func, zval **object_ptr TSRMLS_DC);
+ static int getClosure(zval *object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr TSRMLS_DC);
/**
* Function to cast the object to a different type
@@ -363,19 +352,19 @@ public:
* @param tsrm_ls
* @return int
*/
- 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);
-
+ static int serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data TSRMLS_DC);
+ static int unserialize(zval *object, zend_class_entry *entry, const unsigned char *buffer, size_t 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 +381,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 +398,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 +448,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/constantfuncs.cpp b/zend/constantfuncs.cpp
index 40ea20e..46886cf 100644
--- a/zend/constantfuncs.cpp
+++ b/zend/constantfuncs.cpp
@@ -39,13 +39,13 @@ Value constant(const char *constant, size_t size)
// we need the tsrm_ls variable
TSRMLS_FETCH();
- // the value that holds the result
- Value result;
-
// retrieve the constant
- if (!zend_get_constant(constant, size, result._val TSRMLS_CC)) return nullptr;
-
- // zval was correctly retrieved, wrap in value
+ auto *result = zend_get_constant(zend_string_init(constant, size, 1) TSRMLS_CC);
+
+ // did the constant exist?
+ if (!result) return nullptr;
+
+ // return the valid result
return result;
}
@@ -71,14 +71,13 @@ bool define(const char *name, size_t size, const Value &value)
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
-
+
// the constant structure from the zend engine
zend_constant constant;
-
- // copy the name (note that name_len also includes the end-of-string '\0' byte)
- constant.name = zend_strndup(name, size);
- constant.name_len = size + 1;
-
+
+ // copy the name
+ constant.name = zend_string_init(name, size, 1);
+
// only scalar values can be used for constants
if (value.isScalar())
{
@@ -90,19 +89,19 @@ bool define(const char *name, size_t size, const Value &value)
{
// we're going to convert the value object into a string, and use that
Value str = value.clone(Type::String);
-
+
// use the copied value
constant.value = *str._val;
zval_copy_ctor(&constant.value);
}
-
+
// constants are case sensitive (but not persistent, because this is a user
// space constant!)
constant.flags = CONST_CS;
// as module number we use a fake module number
constant.module_number = PHP_USER_CONSTANT;
-
+
// register the constant
return zend_register_constant(&constant TSRMLS_CC) == SUCCESS;
}
@@ -137,20 +136,21 @@ bool define(const std::string &name, const Value &value)
* @param size
* @return bool
*/
-bool defined(const char *name, size_t size)
+bool defined(const char *name, size_t size)
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
- // result variable
- zval c;
-
// retrieve the constant
- if (!zend_get_constant_ex(name, size, &c, NULL, ZEND_FETCH_CLASS_SILENT TSRMLS_CC)) return false;
+ auto *value = zend_get_constant_ex(zend_string_init(name, size, 1), nullptr, ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
+
+ // check if the value was found
+ if (!value) return false;
// constant exists, but the returned zval should first be destructed
- zval_dtor(&c);
-
+ // @todo: is this necessary in PHP 7?
+ zval_dtor(value);
+
// done
return true;
}
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/exists.cpp b/zend/exists.cpp
index 1177e47..21d3184 100644
--- a/zend/exists.cpp
+++ b/zend/exists.cpp
@@ -1,7 +1,7 @@
/**
* Exists.cpp
*
- * This file holds the implementation of all *_exists() functions,
+ * This file holds the implementation of all *_exists() functions,
* like class_exists(), et cetera
*
* @author andot <https://github.com/andot>
@@ -33,41 +33,42 @@ namespace Php {
* @param autoload
* @return bool
*/
-bool class_exists(const char *classname, size_t len, bool autoload)
+bool class_exists(const char *classname, size_t len, bool autoload)
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
- // we're going to load a class-entry
- zend_class_entry **ce;
-
// should we autoload the class?
- if (autoload)
+ if (autoload)
{
+ // retrieve class entry
+ auto *ce = zend_lookup_class(zend_string_init(classname, len, 1) TSRMLS_CC);
+
// no auto-load
- if (SUCCESS != zend_lookup_class(classname, len, &ce TSRMLS_CC)) return false;
+ if (!ce) return false;
// the found "class" could also be an interface or trait, which we do no want
- return ((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0;
+ return (ce->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0;
}
else
{
// starting slashes can be ignored
if (len > 0 && classname[0] == '\\') { classname++; len--; }
-
- // all classes are in lowercase in the hash, so we make
- // a temporary buffer for storing the lowercase class name
- // (is this smart? memory allocation is expensive!)
- std::unique_ptr<char[]> lc_name(new char[len + 1]);
-
+
+ // allocate a zend_string
+ auto *string = zend_string_alloc(len, 1);
+
// copy the name to lowercase, but ignore the starting slash (if there is one)
- zend_str_tolower_copy(lc_name.get(), classname, len);
+ zend_str_tolower_copy(ZSTR_VAL(string), classname, len);
// see if there is a class with this name
- if (SUCCESS != zend_hash_find(EG(class_table), lc_name.get(), len + 1, (void **) &ce)) return false;
-
- // the found "class" could also be an interface or trait, which we do no want
- return !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
+ auto *val = zend_hash_find(EG(class_table), string);
+
+ // check whether something was found
+ if (val == nullptr) return false;
+
+ // the "something" could also be an interface or trait, which we do no want
+ return !(((Z_CE_P(val))->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
}
}
diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp
index bacf80f..ef5d33d 100644
--- a/zend/extensionimpl.cpp
+++ b/zend/extensionimpl.cpp
@@ -41,14 +41,14 @@ static void init_globals(zend_phpcpp_globals *globals) {}
* variable. However, there does not seem to be a decent API call in Zend to
* get back the original module_entry linked to this number. So we have to
* look up entries in a hash table to find the right module entry. To make things
- * even worse, the records in this hash table are copies of the original
+ * even worse, the records in this hash table are copies of the original
* zend_module_entry structure, so we can also not hide the C++ extension
* object pointer in the entry that we created ourselves.
- *
+ *
* We have an ugly solution, we keep track of a map of all C++ extension names
* and their associated extension object, and a map of all module number and
* the linked extension object.
- *
+ *
* @var map
*/
static std::map<std::string,ExtensionImpl*> name2extension;
@@ -56,22 +56,25 @@ static std::map<int,ExtensionImpl*> number2extension;
/**
* Handler function that is used in combination with zend_hash_apply()
- *
+ *
* This function is called when we need to find an extension object based on
- * an extension number. We loop through the list of all registered modules, and
+ * an extension number. We loop through the list of all registered modules, and
* for each module we check if we know the extension based on the name
- *
+ *
* @param zend_module_entry
*/
-static int match_module(zend_module_entry *entry)
+static int match_module(zval *value TSRMLS_DC)
{
+ // retrieve the module entry from the zval
+ auto *entry = (zend_module_entry*)Z_PTR_P(value);
+
// check if there is an extension with this name
auto iter = name2extension.find(entry->name);
if (iter == name2extension.end()) return ZEND_HASH_APPLY_KEEP;
-
+
// we have the extension, store in combination with the number
number2extension[entry->module_number] = iter->second;
-
+
// done
return ZEND_HASH_APPLY_KEEP;
}
@@ -87,14 +90,14 @@ static ExtensionImpl *find(int number TSRMLS_DC)
// do we already have an extension with this number?
auto iter = number2extension.find(number);
if (iter != number2extension.end()) return iter->second;
-
+
// no, not yet, loop through all modules
- zend_hash_apply(&module_registry, (apply_func_t)match_module TSRMLS_CC);
-
+ zend_hash_apply(&module_registry, match_module TSRMLS_CC);
+
// find again
iter = number2extension.find(number);
if (iter == number2extension.end()) return nullptr;
-
+
// found!
return iter->second;
}
@@ -109,7 +112,7 @@ static ExtensionImpl *find(int number TSRMLS_DC)
int ExtensionImpl::processStartup(int type, int module_number TSRMLS_DC)
{
// initialize and allocate the "global" variables
- ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL);
+ ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL);
// get the extension
auto *extension = find(module_number TSRMLS_CC);
@@ -148,10 +151,10 @@ int ExtensionImpl::processRequest(int type, int module_number TSRMLS_DC)
{
// get the extension
auto *extension = find(module_number TSRMLS_CC);
-
+
// is the callback registered?
if (extension->_onRequest) extension->_onRequest();
-
+
// done
return BOOL2SUCCESS(true);
}
@@ -167,10 +170,10 @@ int ExtensionImpl::processIdle(int type, int module_number TSRMLS_DC)
{
// get the extension
auto *extension = find(module_number TSRMLS_CC);
-
+
// is the callback registered?
if (extension->_onIdle) extension->_onIdle();
-
+
// done
return BOOL2SUCCESS(true);
}
@@ -187,10 +190,10 @@ int ExtensionImpl::processMismatch(int type, int module_number TSRMLS_DC)
{
// get the extension
auto *extension = find(module_number TSRMLS_CC);
-
+
// report a warning
warning << "Version mismatch between PHP-CPP and extension " << extension->name() << " " << extension->version() << " (recompile needed?)" << std::endl;
-
+
// done
return BOOL2SUCCESS(true);
}
@@ -202,12 +205,12 @@ int ExtensionImpl::processMismatch(int type, int module_number TSRMLS_DC)
* @param version Version number
* @param apiversion API version number
*/
-ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion) :
+ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion) :
ExtensionBase(data)
{
// keep extension pointer based on the name
name2extension[name] = this;
-
+
// assign all members (apart from the globals)
_entry.size = sizeof(zend_module_entry); // size of the data
_entry.zend_api = ZEND_MODULE_API_NO; // api number
@@ -242,8 +245,8 @@ ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *vers
// everything is ok if the api versions match
if (apiversion == PHPCPP_API_VERSION) return;
-
- // mismatch between api versions, the extension is invalid, we use a
+
+ // mismatch between api versions, the extension is invalid, we use a
// different startup function to report to the user
_entry.module_startup_func = &ExtensionImpl::processMismatch;
@@ -260,12 +263,9 @@ ExtensionImpl::~ExtensionImpl()
{
// remove from the array
name2extension.erase(_entry.name);
-
- // deallocate the php.ini entries
- if (_ini) delete[] _ini;
-
+
// deallocate functions
- if (_entry.functions) delete[] _entry.functions;
+ delete[] _entry.functions;
}
/**
@@ -303,7 +303,7 @@ zend_module_entry *ExtensionImpl::module()
// the number of functions
int count = _data->functions();
-
+
// skip if there are no functions
if (count == 0) return &_entry;
@@ -315,10 +315,10 @@ zend_module_entry *ExtensionImpl::module()
// apply a function to each function
_data->functions([&i, entries](const std::string &prefix, NativeFunction &function) {
-
+
// initialize the function
function.initialize(prefix, &entries[i]);
-
+
// move on to the next iteration
i++;
});
@@ -345,40 +345,40 @@ zend_module_entry *ExtensionImpl::module()
bool ExtensionImpl::initialize(int module_number TSRMLS_DC)
{
// array contains ini settings
- _ini = new zend_ini_entry[_data->iniVariables()+1];
+ _ini.reset(new zend_ini_entry_def[_data->iniVariables()+1]);
// the entry that we're filling
int i = 0;
// Fill the php.ini entries
_data->iniVariables([this, &i, module_number](Ini &ini) {
-
+
// initialize the function
- zend_ini_entry *entry = &_ini[i];
-
+ zend_ini_entry_def *entry = &_ini[i];
+
// fill the property
ini.fill(entry, module_number);
-
+
// move on to the next iteration
- i++;
+ ++i;
});
// last entry should be set to all zero's
- memset(&_ini[i], 0, sizeof(zend_ini_entry));
+ memset(&_ini[i], 0, sizeof(_ini[i]));
// register ini entries in Zend core
- zend_register_ini_entries(_ini, module_number TSRMLS_CC);
+ zend_register_ini_entries(_ini.get(), module_number TSRMLS_CC);
// the constants are registered after the module is ready
_data->constants([module_number TSRMLS_CC](const std::string &prefix, Constant &c) {
-
+
// forward to implementation class
c.implementation()->initialize(prefix, module_number TSRMLS_CC);
});
-
+
// we also need to register each class, find out all classes
_data->classes([TSRMLS_C](const std::string &prefix, ClassBase &c) {
-
+
// forward to implementation class
c.implementation()->initialize(&c, prefix TSRMLS_CC);
});
@@ -386,10 +386,10 @@ bool ExtensionImpl::initialize(int module_number TSRMLS_DC)
// initialize the PhpCpp::Functor class
Functor::initialize(TSRMLS_C);
- // remember that we're initialized (when you use "apache reload" it is
+ // remember that we're initialized (when you use "apache reload" it is
// possible that the processStartup() method is called more than once)
_locked = true;
-
+
// is the callback registered?
if (_onStartup) _onStartup();
@@ -409,17 +409,14 @@ bool ExtensionImpl::shutdown(int module_number TSRMLS_DC)
zend_unregister_ini_entries(module_number TSRMLS_CC);
// destruct the ini entries
- if (_ini) delete[] _ini;
-
- // forget the ini entries
- _ini = nullptr;
+ _ini.reset();
// shutdown the functor class
Functor::shutdown(TSRMLS_C);
// is the callback registered?
if (_onShutdown) _onShutdown();
-
+
// done
return true;
}
diff --git a/zend/extensionimpl.h b/zend/extensionimpl.h
index 3a0fc63..aef4802 100644
--- a/zend/extensionimpl.h
+++ b/zend/extensionimpl.h
@@ -2,7 +2,7 @@
* ExtensionImpl.h
*
* Extension implementation for the Zend engine.
- *
+ *
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2013, 2014 Copernica BV
*/
@@ -23,21 +23,21 @@ protected:
* @var zend_module_entry
*/
zend_module_entry _entry;
-
+
/**
* Is the object locked? This prevents crashes for 'apache reload'
* because we then do not have to re-initialize the entire php engine
* @var bool
*/
bool _locked = false;
-
+
/**
* The .ini entries
- *
- * @var zend_ini_entry
+ *
+ * @var std::unique_ptr<zend_ini_entry_def[]>
*/
- zend_ini_entry *_ini = nullptr;
-
+ std::unique_ptr<zend_ini_entry_def[]> _ini = nullptr;
+
public:
/**
* Constructor
@@ -47,31 +47,31 @@ public:
* @param apiversion API version number
*/
ExtensionImpl(Extension *data, const char *name, const char *version, int apiversion);
-
+
/**
* No copy'ing and no moving
*/
ExtensionImpl(const ExtensionImpl &extension) = delete;
ExtensionImpl(ExtensionImpl &&extension) = delete;
-
+
/**
* Destructor
*/
virtual ~ExtensionImpl();
-
+
/**
* The extension name
* @return const char *
*/
const char *name() const;
-
+
/**
* The extension version
* @return const char *
*/
const char *version() const;
-
- /**
+
+ /**
* Is the object locked (true) or is it still possible to add more functions,
* classes and other elements to it?
* @return bool
@@ -81,17 +81,17 @@ public:
// return member
return _locked;
}
-
+
/**
* Retrieve the module entry
- *
+ *
* This is the memory address that should be exported by the get_module()
* function.
*
* @return _zend_module_entry
*/
zend_module_entry *module();
-
+
/**
* Cast to a module entry
* @return _zend_module_entry*
@@ -100,10 +100,10 @@ public:
{
return module();
}
-
+
private:
/**
- * Initialize the namespace after it was registered
+ * Initialize the extension after it was registered
* @param module_number
* @param tsrm_ls
* @return bool
@@ -126,7 +126,7 @@ private:
* @return int 0 on success
*/
static int processStartup(int type, int module_number TSRMLS_DC);
-
+
/**
* Function that is called when the extension is about to be stopped
* @param type Module type
@@ -135,7 +135,7 @@ private:
* @return int
*/
static int processShutdown(int type, int module_number TSRMLS_DC);
-
+
/**
* Function that is called when a request starts
* @param type Module type
diff --git a/zend/file.cpp b/zend/file.cpp
index 2b7e77f..77b96f5 100644
--- a/zend/file.cpp
+++ b/zend/file.cpp
@@ -19,10 +19,10 @@ namespace Php {
/**
* Constructor
- *
+ *
* The constructor receives a filename as parameter. It uses the normal
- * PHP include path resolve algorithms to find the location of the file.
- *
+ * PHP include path resolve algorithms to find the location of the file.
+ *
* @param name the filename
* @param size length of the filename
*/
@@ -30,16 +30,9 @@ File::File(const char *name, size_t size)
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
-
+
// resolve the path
_path = zend_resolve_path(name, size TSRMLS_CC);
-
- // the resolve-path function sometimes returns the original pointer, we
- // do not want that because we may have to store the pathname in this object
- if (_path != name) return;
-
- // make a full copy of the pathname
- _path = estrndup(name, size);
}
/**
@@ -48,10 +41,7 @@ File::File(const char *name, size_t size)
File::~File()
{
// clean up path name
- if (_path) efree(_path);
-
- // clean up opcodes
- if (_opcodes) delete _opcodes;
+ if (_path) zend_string_release(_path);
}
/**
@@ -62,10 +52,10 @@ bool File::compile()
{
// never works if the path is invalid
if (!_path) return false;
-
+
// is the file already compiled?
if (_opcodes) return _opcodes->valid();
-
+
// we are going to open the file
zend_file_handle fileHandle;
@@ -73,20 +63,20 @@ bool File::compile()
TSRMLS_FETCH();
// open the file
- if (zend_stream_open(_path, &fileHandle TSRMLS_CC) == FAILURE) return false;
+ if (zend_stream_open(ZSTR_VAL(_path), &fileHandle TSRMLS_CC) == FAILURE) return false;
+
+ // make sure the path name is stored in the handle (@todo: is this necessary? do we need the copy?)
+ if (!fileHandle.opened_path) fileHandle.opened_path = zend_string_copy(_path);
- // make sure the path name is stored in the handle
- if (!fileHandle.opened_path) fileHandle.opened_path = estrdup(_path);
-
// we need temporary compiler options
CompilerOptions options(ZEND_COMPILE_DEFAULT TSRMLS_CC);
-
+
// create the opcodes
- _opcodes = new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE TSRMLS_CC) TSRMLS_CC);
+ _opcodes.reset(new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE TSRMLS_CC) TSRMLS_CC));
// close the file handle
zend_destroy_file_handle(&fileHandle TSRMLS_CC);
-
+
// done
return _opcodes->valid();
}
@@ -99,13 +89,13 @@ bool File::exists()
{
// it is of course not valid if the path could not be resolved
if (!_path) return false;
-
+
// if we have valid opcodes, we're sure that it exists
if (_opcodes && _opcodes->valid()) return true;
-
+
// retrieve stats
struct stat buf;
- return stat(_path, &buf) == 0;
+ return stat(ZSTR_VAL(_path), &buf) == 0;
}
/**
@@ -122,7 +112,7 @@ bool File::valid()
* Execute the file
* @return Value
*/
-Value File::execute()
+Value File::execute()
{
// do we already have the opcodes?
if (_opcodes) return _opcodes->execute();
@@ -130,12 +120,9 @@ Value File::execute()
// try compiling the file
if (!compile()) return nullptr;
- // we need the tsrm_ls variable (@todo would it be better if this was a member?)
- TSRMLS_FETCH();
-
// add the entry to the list of included files
- zend_hash_add_empty_element(&EG(included_files), _path, ::strlen(_path) + 1);
-
+ zend_hash_add_empty_element(&EG(included_files), _path);
+
// execute the opcodes
return _opcodes->execute();
}
@@ -144,16 +131,16 @@ Value File::execute()
* Execute a file only once
* @return Value
*/
-Value File::once()
+Value File::once()
{
// skip if the path is invalid
if (!_path) return nullptr;
// we need the tsrm_ls variable (@todo would it be better if this was a member?)
TSRMLS_FETCH();
-
+
// check if this file was already included
- if (zend_hash_exists(&EG(included_files), _path, ::strlen(_path) + 1)) return nullptr;
+ if (zend_hash_exists(&EG(included_files), _path)) return nullptr;
// execute the file
return execute();
diff --git a/zend/functor.cpp b/zend/functor.cpp
index 7d0404b..866fcea 100644
--- a/zend/functor.cpp
+++ b/zend/functor.cpp
@@ -6,7 +6,7 @@
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
* @copyright 2015 Copernica BV
*/
-
+
/**
* Dependencies
*/
@@ -31,10 +31,10 @@ void Functor::initialize(TSRMLS_D)
{
// leap out if the class entry is already set
if (_entry) return;
-
+
// construct functor object
static std::unique_ptr<ClassBase> functor(new Class<Functor>("PhpCpp::Functor"));
-
+
// initialize the functor class
_entry = functor->implementation()->initialize(functor.get(), "" TSRMLS_CC);
}
@@ -48,7 +48,7 @@ void Functor::shutdown(TSRMLS_D)
// we forget the entry
_entry = nullptr;
}
-
+
/**
* End of namespace
*/
diff --git a/zend/global.cpp b/zend/global.cpp
index 7eea8e7..e7fef53 100644
--- a/zend/global.cpp
+++ b/zend/global.cpp
@@ -14,6 +14,67 @@
namespace Php {
/**
+ * Move constructor
+ * @param global
+ */
+Global::Global(Global &&global) _NOEXCEPT :
+ Value(std::move(global)),
+ _name(global._name),
+ _exists(global._exists)
+{
+ // remove from other global to avoid double free
+ global._name = nullptr;
+}
+
+/**
+ * Constructor for non-existing var
+ *
+ * @param name Name for the variable that does not exist
+ */
+Global::Global(const char *name) :
+ Value(),
+ _name(zend_string_init(name, ::strlen(name), 1)),
+ _exists(false) {}
+
+/**
+ * Alternative constructor for non-existing var
+ * @param name
+ */
+Global::Global(const std::string &name) :
+ Value(),
+ _name(zend_string_init(name.data(), name.size(), 1)),
+ _exists(false) {}
+
+/**
+ * Constructor to wrap zval for existing global bar
+ * @param name
+ * @param val
+ */
+Global::Global(const char *name, struct _zval_struct *val) :
+ Value(val, true),
+ _name(zend_string_init(name, ::strlen(name), 1)),
+ _exists(true) {}
+
+/**
+ * Alternative constructor to wrap zval
+ * @param name
+ * @param val
+ */
+Global::Global(const std::string &name, struct _zval_struct *val) :
+ Value(val, true),
+ _name(zend_string_init(name.data(), name.size(), 1)),
+ _exists(true) {}
+
+/**
+ * Destructor
+ */
+Global::~Global()
+{
+ // release the string
+ if (_name) zend_string_release(_name);
+}
+
+/**
* Function that is called when the value is updated
* @return Value
*/
@@ -21,19 +82,19 @@ Global &Global::update()
{
// skip if the variable already exists
if (_exists) return *this;
-
+
// we need the TSRMLS variable
TSRMLS_FETCH();
// add the variable to the globals
- zend_hash_add(EG(active_symbol_table), _name.c_str(), _name.size()+1, &_val, sizeof(zval*), NULL);
+ zend_hash_add(EG(current_execute_data)->symbol_table, _name, _val);
// add one extra reference because the variable now is a global var too
- Z_ADDREF_P(_val);
+ Z_TRY_ADDREF_P(_val);
// remember that the variable now exists
_exists = true;
-
+
// done
return *this;
}
diff --git a/zend/globals.cpp b/zend/globals.cpp
index 5299c90..ea4e65e 100644
--- a/zend/globals.cpp
+++ b/zend/globals.cpp
@@ -36,14 +36,14 @@ Globals &GLOBALS = Globals::instance();
*/
Global Globals::operator[](const char *name)
{
- // pointer to a zval
- zval **varvalue;
-
// we need the TSRMLS variable
TSRMLS_FETCH();
-
+
+ // retrieve the variable (if it exists)
+ auto *varvalue = zend_hash_find(&EG(symbol_table), zend_string_init(name, ::strlen(name), 0));
+
// check if the variable already exists
- if (zend_hash_find(&EG(symbol_table), name, ::strlen(name)+1, (void**)&varvalue) == FAILURE)
+ if (!varvalue)
{
// the variable does not already exist, return a global object
// that will automatically set the value when it is updated
@@ -53,7 +53,7 @@ Global Globals::operator[](const char *name)
{
// we are in the happy situation that the variable exists, we turn
// this value into a reference value, and return that
- return Global(name, *varvalue);
+ return Global(name, varvalue);
}
}
@@ -64,14 +64,14 @@ Global Globals::operator[](const char *name)
*/
Global Globals::operator[](const std::string &name)
{
- // pointer to a zval
- zval **varvalue;
-
// we need the TSRMLS variable
TSRMLS_FETCH();
-
+
+ // retrieve the variable (if it exists)
+ auto *varvalue = zend_hash_find(&EG(symbol_table), zend_string_init(name.data(), name.size(), 0));
+
// check if the variable already exists
- if (zend_hash_find(&EG(symbol_table), name.c_str(), name.size()+1, (void**)&varvalue) == FAILURE)
+ if (!varvalue)
{
// the variable does not already exist, return a global object
// that will automatically set the value when it is updated
@@ -81,7 +81,7 @@ Global Globals::operator[](const std::string &name)
{
// we are in the happy situation that the variable exists, we turn
// this value into a reference value, and return that
- return Global(name, *varvalue);
+ return Global(name, varvalue);
}
}
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/ini.cpp b/zend/ini.cpp
index 889e388..849f6c4 100644
--- a/zend/ini.cpp
+++ b/zend/ini.cpp
@@ -17,35 +17,20 @@ namespace Php {
* @param zend_ini_entry *ini_entry, int module_number
* @param int module_number
*/
-void Ini::fill(zend_ini_entry *ini_entry, int module_number)
+void Ini::fill(zend_ini_entry_def *ini_entry, int module_number)
{
- ini_entry->module_number = module_number;
- ini_entry->modifiable = static_cast<int>(this->_place);
- ini_entry->name = const_cast<char*>(this->_name.c_str());
- ini_entry->name_length = this->_name.size()+1;
- ini_entry->on_modify = OnUpdateString;
- ini_entry->mh_arg1 = nullptr;
+ ini_entry->modifiable = static_cast<int>(_place);
+ ini_entry->name = _name.data();
+ ini_entry->on_modify = OnUpdateString;
+ ini_entry->mh_arg1 = nullptr;
#ifdef ZTS
- ini_entry->mh_arg2 = (void *) &phpcpp_globals_id;
+ ini_entry->mh_arg2 = (void *) &phpcpp_globals_id;
#else
- ini_entry->mh_arg2 = (void *) &phpcpp_globals;
+ ini_entry->mh_arg2 = (void *) &phpcpp_globals;
#endif
- ini_entry->mh_arg3 = nullptr;
- ini_entry->value = const_cast<char*>(this->_value.c_str());
- ini_entry->value_length = this->_value.size();
- if( this->_orig_empty)
- {
- ini_entry->orig_value = nullptr;
- ini_entry->orig_value_length = 0;
- }
- else
- {
- ini_entry->orig_value = const_cast<char*>(this->_orig.c_str());
- ini_entry->orig_value_length = this->_orig.size();
- }
- ini_entry->orig_modifiable = 0;
- ini_entry->modified = 0;
- ini_entry->displayer = nullptr;
+ ini_entry->mh_arg3 = nullptr;
+ ini_entry->value = _value.data();
+ ini_entry->displayer = nullptr;
}
diff --git a/zend/iteratorimpl.cpp b/zend/iteratorimpl.cpp
index 49526b1..4861e9e 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);
}
/**
@@ -49,21 +49,22 @@ int IteratorImpl::valid(zend_object_iterator *iter TSRMLS_DC)
/**
* Fetch the current item
- * @param iter
- * @param data
- * @param tsrm_ls
+ *
+ * @param iter The iterator to retrieve the value from
+ * @param tsrm_ls Thread safety variable
+ * @return The current value of the iterator
*/
-void IteratorImpl::current(zend_object_iterator *iter, zval ***data TSRMLS_DC)
+zval *IteratorImpl::current(zend_object_iterator *iter TSRMLS_DC)
{
// get the actual iterator
- IteratorImpl *iterator = self(iter);
+ auto *iterator = self(iter);
// retrieve the value (and store it in a member so that it is not
// destructed when the function returns)
iterator->_current = iterator->current();
- // copy the value
- *data = &iterator->_current._val;
+ // return the value
+ return iterator->_current._val;
}
/**
@@ -100,14 +101,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 +116,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 +152,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 +166,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..2199e6f 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
@@ -114,11 +114,12 @@ private:
/**
* Fetch the current item
- * @param iter
- * @param data
- * @param tsrm_ls
+ *
+ * @param iter The iterator used to retrieve the value from
+ * @param tsrm_ls Thread safety variable
+ * @return The current value of the iterator
*/
- static void current(zend_object_iterator *iter, zval ***data TSRMLS_DC);
+ static zval *current(zend_object_iterator *iter TSRMLS_DC);
/**
* Fetch the key for the current element (optional, may be NULL). The key
@@ -161,14 +162,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 +186,7 @@ public:
return &_impl;
}
};
-
+
/**
* End namespace
*/
diff --git a/zend/nativefunction.h b/zend/nativefunction.h
index 073e69b..e661112 100644
--- a/zend/nativefunction.h
+++ b/zend/nativefunction.h
@@ -70,8 +70,8 @@ public:
void initialize(const std::string &prefix, zend_function_entry *entry)
{
// if there is a namespace prefix, we should adjust the name
- if (prefix.size()) _ptr = HiddenPointer<Callable>(this, prefix+"\\"+(const char *)_ptr);
-
+ if (!prefix.empty()) _name = prefix + '\\' + _name;
+
// call base initialize
Callable::initialize(entry);
}
@@ -79,7 +79,7 @@ public:
private:
/**
* Union of supported callbacks
- * One of the callbacks will be set
+ * One of the callbacks will be set
*/
union {
native_callback_0 f0;
@@ -87,7 +87,7 @@ private:
native_callback_2 f2;
native_callback_3 f3;
} _function;
-
+
/**
* The callback that is set
* @var integer
diff --git a/zend/object.cpp b/zend/object.cpp
index 7da8339..e2345c1 100644
--- a/zend/object.cpp
+++ b/zend/object.cpp
@@ -13,7 +13,7 @@ namespace Php {
/**
* Constructor to create a new instance of a builtin class
- *
+ *
* @param name Name of the class to instantiate
* @param base The C++ object to wrap
*/
@@ -36,26 +36,26 @@ Object::Object(const char *name, Base *base) : Value()
// here because this function is called from C++ context, and zend_error()
// would cause a longjmp() which does not clean up C++ objects created
// by the extension).
- auto *entry = zend_fetch_class(name, ::strlen(name), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
+ auto *entry = zend_fetch_class(zend_string_init(name, ::strlen(name), 0), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
if (!entry) throw FatalError(std::string("Unknown class name ") + name);
// construct an implementation (this will also set the implementation
// 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);
+ Z_OBJ_P(_val)->handlers = ClassImpl::objectHandlers(entry);
}
}
/**
* Constructor in case the class entry is already known
- *
+ *
* @param entry Class entry
* @param base The C++ object to wrap
*/
@@ -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);
}
}
@@ -95,12 +92,12 @@ Object::Object(const Value &value) : Value()
{
// when a string is passed in, we are going to make a new instance of the
// passed in string
- if (value.isString())
+ if (value.isString())
{
// instantiate the object
if (instantiate(value)) call("__construct");
}
- else
+ else
{
// this simply copies the other object
operator=(value);
@@ -121,23 +118,23 @@ bool Object::instantiate(const char *name)
// here because this function is called from C++ context, and zend_error()
// would cause a longjmp() which does not clean up C++ objects created
// by the extension).
- auto *entry = zend_fetch_class(name, ::strlen(name), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
+ auto *entry = zend_fetch_class(zend_string_init(name, ::strlen(name), 0), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
if (!entry) throw FatalError(std::string("Unknown class name ") + name);
// initiate the zval (which was already allocated in the base constructor)
object_init_ex(_val, entry);
-
+
// @todo should we call methods like allocating hashtables, copying and
// initializing properties, et cetera????? In all example you always
// see such complicated and next-to-impossible-to-understand
// sequences of functions being called, but this object_init_ex
// also seems to work...
-
- // @todo is this a memory leak? the base class first initializes a stdClass,
+
+ // @todo is this a memory leak? the base class first initializes a stdClass,
// and then we overwrite it with a specific class
-
+
// return whether there is a __construct function
- return zend_hash_exists(&entry->function_table, "__construct", 12);
+ return zend_hash_exists(&entry->function_table, zend_string_init("__construct", 12, 1));
}
/**
diff --git a/zend/objectimpl.h b/zend/objectimpl.h
index 110491b..ed48e26 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) GC_REFCOUNT(php()) = 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/super.cpp b/zend/super.cpp
index ea690fe..02a1e46 100644
--- a/zend/super.cpp
+++ b/zend/super.cpp
@@ -32,10 +32,16 @@ Value Super::value()
TSRMLS_FETCH();
// call zend_is_auto_global to ensure that the just-in-time globals are loaded
- if (_name) { zend_is_auto_global(_name, ::strlen(_name) TSRMLS_CC); _name = nullptr; }
-
+ if (_name) {
+ // make the variable an auto global
+ zend_is_auto_global(zend_string_init(_name, ::strlen(_name), 1) TSRMLS_CC);
+
+ // reset because we only need to do this once
+ _name = nullptr;
+ }
+
// create a value object that wraps around the actual zval
- return Value(PG(http_globals)[_index]);
+ return &PG(http_globals)[_index];
}
/**
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;
}
diff --git a/zend/value.cpp b/zend/value.cpp
index d22830a..9be4b24 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -34,31 +34,24 @@ namespace Php {
/**
* Constructor (value = NULL)
*/
-Value::Value()
+Value::Value() : _val(new zval)
{
// create a null zval
- MAKE_STD_ZVAL(_val);
ZVAL_NULL(_val);
}
/**
* Constructor for null ptr
*/
-Value::Value(std::nullptr_t value)
-{
- // create a null zval
- MAKE_STD_ZVAL(_val);
- ZVAL_NULL(_val);
-}
+Value::Value(std::nullptr_t value) : Value() {}
/**
* Constructor based on integer value
* @param value
*/
-Value::Value(int16_t value)
+Value::Value(int16_t value) : _val(new zval)
{
// create an integer zval
- MAKE_STD_ZVAL(_val);
ZVAL_LONG(_val, value);
}
@@ -66,10 +59,9 @@ Value::Value(int16_t value)
* Constructor based on integer value
* @param value
*/
-Value::Value(int32_t value)
+Value::Value(int32_t value) : _val(new zval)
{
// create an integer zval
- MAKE_STD_ZVAL(_val);
ZVAL_LONG(_val, value);
}
@@ -77,10 +69,9 @@ Value::Value(int32_t value)
* Constructor based on int64_t value
* @param value
*/
-Value::Value(int64_t value)
+Value::Value(int64_t value) : _val(new zval)
{
// create an integer zval
- MAKE_STD_ZVAL(_val);
ZVAL_LONG(_val, value);
}
@@ -88,10 +79,9 @@ Value::Value(int64_t value)
* Constructor based on boolean value
* @param value
*/
-Value::Value(bool value)
+Value::Value(bool value) : _val(new zval)
{
// create a boolean zval
- MAKE_STD_ZVAL(_val);
ZVAL_BOOL(_val, value);
}
@@ -99,22 +89,20 @@ Value::Value(bool value)
* Constructor based on single character
* @param value
*/
-Value::Value(char value)
+Value::Value(char value) : _val(new zval)
{
// create a string zval
- MAKE_STD_ZVAL(_val);
- ZVAL_STRINGL(_val, &value, 1, 1);
+ ZVAL_STRINGL(_val, &value, 1);
}
/**
* Constructor based on string value
* @param value
*/
-Value::Value(const std::string &value)
+Value::Value(const std::string &value) : _val(new zval)
{
// create a string zval
- MAKE_STD_ZVAL(_val);
- ZVAL_STRINGL(_val, value.c_str(), value.size(), 1);
+ ZVAL_STRINGL(_val, value.c_str(), value.size());
}
/**
@@ -122,16 +110,13 @@ Value::Value(const std::string &value)
* @param value
* @param size
*/
-Value::Value(const char *value, int size)
+Value::Value(const char *value, int size) : _val(new zval)
{
- // allocate the zval
- MAKE_STD_ZVAL(_val);
-
// is there a value?
if (value)
{
// create a string zval
- ZVAL_STRINGL(_val, value, size < 0 ? ::strlen(value) : size, 1);
+ ZVAL_STRINGL(_val, value, size < 0 ? ::strlen(value) : size);
}
else
{
@@ -144,10 +129,9 @@ Value::Value(const char *value, int size)
* Constructor based on decimal value
* @param value
*/
-Value::Value(double value)
+Value::Value(double value) : _val(new zval)
{
// create a double zval
- MAKE_STD_ZVAL(_val);
ZVAL_DOUBLE(_val, value);
}
@@ -156,15 +140,22 @@ Value::Value(double value)
* @param zval Value to wrap
* @param ref Force this to be a reference
*/
-Value::Value(struct _zval_struct *val, bool ref) : _val(val)
+Value::Value(struct _zval_struct *val, bool ref) : _val(new zval)
{
+ // copy the value over (and add reference if relevant)
+ ZVAL_COPY(_val, val);
+
+ // not refcounted? then there is nothing we can do here
+ // @todo: we should be able to force a reference somehow
+ if (!Z_REFCOUNTED_P(_val)) return;
+
// if the variable is not already a reference, and it has more than one
// variable pointing to it, we should seperate it so that any changes
// we're going to make will not change the other variable
if (ref && Z_REFCOUNT_P(_val) > 1)
{
// separate the zval
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
}
// we see ourselves as reference too
@@ -174,7 +165,7 @@ Value::Value(struct _zval_struct *val, bool ref) : _val(val)
if (!ref || Z_ISREF_P(_val)) return;
// make this a reference
- Z_SET_ISREF_P(_val);
+ ZVAL_MAKE_REF(_val);
}
/**
@@ -193,34 +184,19 @@ Value::Value(const Base *object)
// do we have a handle?
if (!impl) throw FatalError("Assigning an unassigned object to a variable");
- // make a regular zval, and set it to an object
- MAKE_STD_ZVAL(_val);
- Z_TYPE_P(_val) = IS_OBJECT;
- Z_OBJ_HANDLE_P(_val) = impl->handle();
-
- // we need the tsrm_ls variable
- TSRMLS_FETCH();
-
- // we have to lookup the object in the object-table
- zend_object_store_bucket *obj_bucket = &EG(objects_store).object_buckets[impl->handle()];
-
- // there is one more reference to the object
- obj_bucket->bucket.obj.refcount += 1;
+ // allocate variable and set it to an object
+ _val = new zval;
+ Z_TYPE_INFO_P(_val) = IS_OBJECT;
- // this is copy-pasted from zend_objects.c - and it is necessary too!
- if (!obj_bucket->bucket.obj.handlers) obj_bucket->bucket.obj.handlers = ClassImpl::objectHandlers(impl->php()->ce);
-
- // store the handlers in the zval too (cast is necessary for php 5.3)
- Z_OBJ_HT_P(_val) = (zend_object_handlers*)obj_bucket->bucket.obj.handlers;
+ // increase refcount
+ GC_REFCOUNT(impl->php())++;
}
/**
* Wrap around a php.ini value
* @param value
*/
-Value::Value(const IniValue &value) : Value((const char *)value)
-{
-}
+Value::Value(const IniValue &value) : Value((const char *)value) {}
/**
* Copy constructor
@@ -228,57 +204,22 @@ Value::Value(const IniValue &value) : Value((const char *)value)
*/
Value::Value(const Value &that)
{
- // is the other variable a reference?
- if (Z_ISREF_P(that._val))
+ // is the other variable a reference? or is this a simple scalar?
+ if (Z_ISREF_P(that._val) || !Z_REFCOUNTED_P(that._val))
{
// because this is supposed to be a COPY, we can not add ourselves
// to the variable but have to allocate a new variable
- ALLOC_ZVAL(_val);
- INIT_PZVAL_COPY(_val, that._val);
-
- // we have to call the copy constructor to copy the entire other zval
- zval_copy_ctor(_val);
+ _val = new zval;
+ ZVAL_COPY_VALUE(_val, that._val);
}
else
{
// simply use the same zval
_val = that._val;
- }
- // that zval has one more reference
- Z_ADDREF_P(_val);
-
-
-// Below the old implementation - I thought really hard about it and I though
-// it was a correct and very smart implementation. However, it does not work
-// when you swap two variables. I changed it to the implementation above, but
-// maybe that implementation introduces other bugs??? Let's keep the old
-// implementation for a while in this file, but commented out
-//
-// // how many references does the other object have?
-// if (Z_REFCOUNT_P(that._val) > 1 && !Z_ISREF_P(that._val))
-// {
-// // there are already multiple variables linked to this value, and it
-// // is not a reference. this implies that we can not turn this variable
-// // into a reference, otherwise strange things could happen, we're going
-// // to create a new zval
-// ALLOC_ZVAL(_val);
-// INIT_PZVAL_COPY(_val, that._val);
-// zval_copy_ctor(_val);
-// }
-// else
-// {
-// // simply use the same zval
-// _val = that._val;
-// }
-//
-// // the other object only has one variable using it, or it is already
-// // a variable by reference, we can safely add one more reference to it
-// // and make it a variable by reference if it was not already a ref
-// Z_ADDREF_P(_val);
-//
-// // make reference
-// Z_SET_ISREF_P(_val);
+ // and increment the reference count
+ Z_ADDREF_P(_val);
+ }
}
/**
@@ -299,14 +240,29 @@ Value::~Value()
// ignore if moved
if (!_val) return;
- // if there were two references or less, we're going to remove a reference
- // and only one reference will remain, the object will then impossible be
- // a reference
- if (Z_REFCOUNT_P(_val) <= 2) Z_UNSET_ISREF_P(_val);
+ // copy to local variable
+ auto val = _val;
- // destruct the zval (this function will decrement the reference counter,
- // and only destruct if there are no other references left)
- zval_ptr_dtor(&_val);
+ // reset local
+ _val = nullptr;
+
+ // are we not a refcounted variable?
+ if (!Z_REFCOUNTED_P(val))
+ {
+ // we can simply delete it
+ delete val;
+ }
+ else
+ {
+ // if there were two references or less, we're going to remove a reference
+ // and only one reference will remain, the object will then impossible be
+ // a reference
+ if (Z_REFCOUNT_P(val) <= 2) ZVAL_UNREF(val);
+
+ // destruct the zval (this function will decrement the reference counter,
+ // and only destruct if there are no other references left)
+ zval_ptr_dtor(val);
+ }
}
/**
@@ -317,7 +273,7 @@ Value::~Value()
* deallocate the zval structure. This is used for functions that have to
* return a zval pointer, that would otherwise be deallocated the moment
* the function returns.
- *
+ *
* @param keeprefcount
* @return zval
*/
@@ -331,12 +287,12 @@ zval *Value::detach(bool keeprefcount)
// reset internal object
_val = nullptr;
-
+
// we're ready if we should keep the refcounter
if (keeprefcount) return result;
// decrement reference counter
- Z_DELREF_P(result);
+ Z_TRY_DELREF_P(result);
// done
return result;
@@ -348,6 +304,10 @@ zval *Value::detach(bool keeprefcount)
*/
int Value::refcount() const
{
+ // are we not a refcounted variable?
+ if (!Z_REFCOUNTED_P(_val)) return 0;
+
+ // we are, retrieve the count
return Z_REFCOUNT_P(_val);
}
@@ -375,7 +335,7 @@ Value &Value::operator=(Value &&value) _NOEXCEPT
*_val = *value._val;
// restore reference and refcount setting
- Z_SET_ISREF_TO_P(_val, true);
+ ZVAL_MAKE_REF(_val);
Z_SET_REFCOUNT_P(_val, refcount);
// how many references did the old variable have?
@@ -398,7 +358,7 @@ Value &Value::operator=(Value &&value) _NOEXCEPT
// the last and only reference to the other object was
// removed, we no longer need it
- FREE_ZVAL(value._val);
+ delete value._val;
// the other object is no longer valid
value._val = nullptr;
@@ -408,7 +368,7 @@ Value &Value::operator=(Value &&value) _NOEXCEPT
{
// destruct the zval (this function will decrement the reference counter,
// and only destruct if there are no other references left)
- if (_val) zval_ptr_dtor(&_val);
+ if (_val) zval_ptr_dtor(_val);
// just copy the zval completely
_val = value._val;
@@ -447,20 +407,20 @@ Value &Value::operator=(const Value &value)
zval_copy_ctor(_val);
// restore refcount and reference setting
- Z_SET_ISREF_TO_P(_val, true);
+ ZVAL_MAKE_REF(_val);
Z_SET_REFCOUNT_P(_val, refcount);
}
else
{
// destruct the zval (this function will decrement the reference counter,
// and only destruct if there are no other references left)
- zval_ptr_dtor(&_val);
+ zval_ptr_dtor(_val);
// just copy the zval, and the refcounter
_val = value._val;
// and we have one more reference
- Z_ADDREF_P(_val);
+ Z_TRY_ADDREF_P(_val);
}
// update the object
@@ -476,7 +436,7 @@ Value &Value::operator=(const Value &value)
Value &Value::operator=(std::nullptr_t value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
@@ -496,7 +456,7 @@ Value &Value::operator=(std::nullptr_t value)
Value &Value::operator=(int16_t value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
@@ -516,7 +476,7 @@ Value &Value::operator=(int16_t value)
Value &Value::operator=(int32_t value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
@@ -536,7 +496,7 @@ Value &Value::operator=(int32_t value)
Value &Value::operator=(int64_t value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
@@ -556,7 +516,7 @@ Value &Value::operator=(int64_t value)
Value &Value::operator=(bool value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
@@ -576,13 +536,13 @@ Value &Value::operator=(bool value)
Value &Value::operator=(char value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
// set new value
- ZVAL_STRINGL(_val, &value, 1, 1);
+ ZVAL_STRINGL(_val, &value, 1);
// update the object
return *this;
@@ -596,13 +556,13 @@ Value &Value::operator=(char value)
Value &Value::operator=(const std::string &value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
// set new value
- ZVAL_STRINGL(_val, value.c_str(), value.size(), 1);
+ ZVAL_STRINGL(_val, value.c_str(), value.size());
// update the object
return *this;
@@ -616,13 +576,13 @@ Value &Value::operator=(const std::string &value)
Value &Value::operator=(const char *value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
// set new value
- ZVAL_STRING(_val, value, 1);
+ ZVAL_STRINGL(_val, value, ::strlen(value));
// update the object
return *this;
@@ -636,7 +596,7 @@ Value &Value::operator=(const char *value)
Value &Value::operator=(double value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// deallocate current zval (without cleaning the zval structure)
zval_dtor(_val);
@@ -844,6 +804,9 @@ Value Value::operator()() const
*/
bool Value::isCallable(const char *name)
{
+ // this only makes sense if we are an object
+ if (!isObject()) return false;
+
// wrap the name in a Php::Value object to get a zval
Value method(name);
@@ -851,7 +814,7 @@ bool Value::isCallable(const char *name)
TSRMLS_FETCH();
// ask zend nicely whether the function is callable
- return zend_is_callable_ex(method._val, _val, IS_CALLABLE_CHECK_NO_ACCESS, nullptr, nullptr, nullptr, nullptr TSRMLS_CC);
+ return zend_is_callable_ex(method._val, Z_OBJ_P(_val), IS_CALLABLE_CHECK_NO_ACCESS, nullptr, nullptr, nullptr TSRMLS_CC);
}
/**
@@ -880,25 +843,25 @@ Value Value::call(const char *name)
* Helper function that runs the actual call
* @param object The object to call it on
* @param method The function or method to call
- * @param args Number of arguments
- * @param params The parameters
+ * @param argc Number of arguments
+ * @param argv The parameters
* @return Value
*/
-static Value do_exec(zval *const *object, zval *method, int argc, zval ***params)
+static Value do_exec(const zval *object, zval *method, int argc, zval *argv)
{
// the return zval
- zval *retval = nullptr;
+ zval retval;
// we need the tsrm_ls variable
TSRMLS_FETCH();
// the current exception
- zval *oldException = EG(exception);
+ // zend_object *oldException = EG(exception);
// call the function
// we're casting the const away here, object is only const so we can call this method
// from const methods after all..
- if (call_user_function_ex(CG(function_table), (zval**) object, method, &retval, argc, params, 1, NULL TSRMLS_CC) != SUCCESS)
+ if (call_user_function_ex(CG(function_table), (zval*) object, method, &retval, argc, argv, 1, nullptr TSRMLS_CC) != SUCCESS)
{
// throw an exception, the function does not exist
throw Exception("Invalid call to "+Value(method).stringValue());
@@ -910,14 +873,15 @@ static Value do_exec(zval *const *object, zval *method, int argc, zval ***params
{
// was an exception thrown inside the function? 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: make OrigException except a zend_object instance
+ // if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC);
+
// leap out if nothing was returned
- if (!retval) return nullptr;
-
+ if (Z_ISUNDEF(retval)) return nullptr;
+
// wrap the retval in a value
- Php::Value result(retval);
-
+ Php::Value result(&retval);
+
// destruct the retval (this just decrements the refcounter, which is ok, because
// it is already wrapped in a Php::Value so still has 1 reference)
zval_ptr_dtor(&retval);
@@ -933,8 +897,14 @@ static Value do_exec(zval *const *object, zval *method, int argc, zval ***params
* @param argv The parameters
* @return Value
*/
-Value Value::exec(int argc, zval ***params) const
+Value Value::exec(int argc, Value *argv) const
{
+ // array of zvals to execute
+ zval params[argc];
+
+ // convert all the values
+ for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; }
+
// call helper function
return do_exec(nullptr, _val, argc, params);
}
@@ -946,13 +916,19 @@ Value Value::exec(int argc, zval ***params) const
* @param argv The parameters
* @return Value
*/
-Value Value::exec(const char *name, int argc, struct _zval_struct ***params) const
+Value Value::exec(const char *name, int argc, Value *argv) const
{
// wrap the name in a Php::Value object to get a zval
Value method(name);
+ // array of zvals to execute
+ zval params[argc];
+
+ // convert all the values
+ for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; }
+
// call helper function
- return do_exec(&_val, method._val, argc, params);
+ return do_exec(_val, method._val, argc, params);
}
/**
@@ -962,13 +938,19 @@ Value Value::exec(const char *name, int argc, struct _zval_struct ***params) con
* @param argv The parameters
* @return Value
*/
-Value Value::exec(const char *name, int argc, struct _zval_struct ***params)
+Value Value::exec(const char *name, int argc, Value *argv)
{
// wrap the name in a Php::Value object to get a zval
Value method(name);
+ // array of zvals to execute
+ zval params[argc];
+
+ // convert all the values
+ for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; }
+
// call helper function
- return do_exec(&_val, method._val, argc, params);
+ return do_exec(_val, method._val, argc, params);
}
/**
@@ -1031,24 +1013,29 @@ Value &Value::setType(Type type)
if (this->type() == type) return *this;
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// run the conversion, when it fails we throw a fatal error which will
// in the end result in a zend_error() call. This FatalError class is necessary
// because a direct call to zend_error() will do a longjmp() which may not
// clean up the C++ objects created by the extension
switch (type) {
- case Type::Null: convert_to_null(_val); break;
- case Type::Numeric: convert_to_long(_val); break;
- case Type::Float: convert_to_double(_val); break;
- case Type::Bool: convert_to_boolean(_val); break;
- case Type::Array: convert_to_array(_val); break;
- case Type::Object: convert_to_object(_val); break;
- case Type::String: convert_to_string(_val); break;
- case Type::Resource: throw FatalError("Resource types can not be handled by the PHP-CPP library"); break;
- case Type::Constant: throw FatalError("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::ConstantArray: throw FatalError("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::Callable: throw FatalError("Callable types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::Undefined: throw FatalError{ "Cannot make a variable undefined" }; break;
+ case Type::Null: convert_to_null(_val); break;
+ case Type::Numeric: convert_to_long(_val); break;
+ case Type::Float: convert_to_double(_val); break;
+ case Type::Bool: convert_to_boolean(_val); break;
+ case Type::False: convert_to_boolean(_val); ZVAL_FALSE(_val); break;
+ case Type::True: convert_to_boolean(_val); ZVAL_TRUE(_val); break;
+ case Type::Array: convert_to_array(_val); break;
+ case Type::Object: convert_to_object(_val); break;
+ case Type::String: convert_to_string(_val); break;
+ case Type::Resource: throw FatalError{ "Resource types can not be handled by the PHP-CPP library" }; break;
+ case Type::Constant: throw FatalError{ "Constant types can not be assigned to a PHP-CPP library variable" }; break;
+ case Type::ConstantAST: throw FatalError{ "Constant types can not be assigned to a PHP-CPP library variable" }; break;
+ case Type::Callable: throw FatalError{ "Callable types can not be assigned to a PHP-CPP library variable" }; break;
+ case Type::Reference: throw FatalError{ "Reference types cannot be assigned to a PHP-CPP library variable" }; break;
+
}
// done
@@ -1081,9 +1068,6 @@ zend_class_entry *Value::classEntry(bool allowString) const
// is this an object
if (isObject())
{
- // should have a class entry
- if (!HAS_CLASS_ENTRY(*_val)) return nullptr;
-
// class entry can be easily found
return Z_OBJCE_P(_val);
}
@@ -1092,14 +1076,8 @@ zend_class_entry *Value::classEntry(bool allowString) const
// the value is not an object, is this allowed?
if (!allowString || !isString()) return nullptr;
- // temporary variable
- zend_class_entry **ce;
-
// find the class entry
- if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &ce TSRMLS_CC) == FAILURE) return nullptr;
-
- // found the entry
- return *ce;
+ return zend_lookup_class(Z_STR_P(_val) TSRMLS_CC);
}
}
@@ -1123,20 +1101,14 @@ bool Value::instanceOf(const char *classname, size_t size, bool allowString) con
zend_class_entry *this_ce = classEntry(allowString);
if (!this_ce) return false;
- // class entry of the parameter
- zend_class_entry **ce;
-
// now we can look up the actual class
- // the signature of zend_lookup_class_ex is slightly different since 5.4
- // TODO The signature of this changed once again as of 5.6!
-#if PHP_VERSION_ID >= 50400
- if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
-#else
- if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false;
-#endif
+ auto *ce = zend_lookup_class_ex(zend_string_init(classname, size, 1), nullptr, 0 TSRMLS_CC);
+
+ // no such class, then we are not instanceof
+ if (!ce) return false;
// check if this is a subclass
- return instanceof_function(this_ce, *ce TSRMLS_CC);
+ return instanceof_function(this_ce, ce TSRMLS_CC);
}
/**
@@ -1159,23 +1131,17 @@ bool Value::derivedFrom(const char *classname, size_t size, bool allowString) co
zend_class_entry *this_ce = classEntry(allowString);
if (!this_ce) return false;
- // class entry of the parameter
- zend_class_entry **ce;
-
// now we can look up the actual class
- // the signature of zend_lookup_class_ex is slightly different since 5.4
- // TODO The signature of this changed once again as of 5.6!
-#if PHP_VERSION_ID >= 50400
- if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
-#else
- if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false;
-#endif
+ auto *ce = zend_lookup_class_ex(zend_string_init(classname, size, 1), nullptr, 0 TSRMLS_CC);
+
+ // unable to find the class entry?
+ if (!ce) return false;
// should not be identical, it must be a real derived object
- if (this_ce == *ce) return false;
+ if (this_ce == ce) return false;
// check if this is a subclass
- return instanceof_function(this_ce, *ce TSRMLS_CC);
+ return instanceof_function(this_ce, ce TSRMLS_CC);
}
/**
@@ -1184,23 +1150,11 @@ bool Value::derivedFrom(const char *classname, size_t size, bool allowString) co
*/
Value Value::clone() const
{
- // the zval that will hold the copy
- zval *copy;
-
- // allocate memory
- ALLOC_ZVAL(copy);
-
- // copy the data
- INIT_PZVAL_COPY(copy, _val);
-
- // run the copy constructor to ensure that everything gets copied
- zval_copy_ctor(copy);
+ // value to clone to
+ Value output;
- // wrap it using the Value(zval*) constructor, this will +1 the refcount!!!!
- Value output(copy);
-
- // -1 the refcount to avoid future leaks
- Z_DELREF_P(copy);
+ // copy the value over to the output
+ ZVAL_COPY_VALUE(output._val, _val);
// done
return output;
@@ -1213,11 +1167,14 @@ Value Value::clone() const
*/
Value Value::clone(Type type) const
{
- // regular clone if nothing changes
- if (this->type() == type) return clone();
+ // first create the clone
+ auto cloned = clone();
- // make a clone
- return clone().setType(type);
+ // should we change the type
+ if (this->type() != type) cloned.setType(type);
+
+ // return the finished clone
+ return cloned;
}
/**
@@ -1239,11 +1196,19 @@ int64_t Value::numericValue() const
*/
bool Value::boolValue() const
{
- // already a bool?
- if (isBool()) return Z_BVAL_P(_val);
-
- // make a clone
- return clone(Type::Bool).boolValue();
+ // what variable type do we hold?
+ switch (type())
+ {
+ case Type::Undefined: return false;
+ case Type::Null: return false;
+ case Type::False: return false;
+ case Type::True: return true;
+ case Type::Numeric: return numericValue();
+ case Type::Float: return floatValue();
+ case Type::String: return size();
+ case Type::Array: return size();
+ default: return clone(Type::Bool).boolValue();
+ }
}
/**
@@ -1252,10 +1217,19 @@ bool Value::boolValue() const
*/
std::string Value::stringValue() const
{
- // already a string?
- if (isString()) return std::string(Z_STRVAL_P(_val), Z_STRLEN_P(_val));
+ // what kind of casting do we need to do?
+ switch (type())
+ {
+ case Type::Null: return {};
+ case Type::False: return "0";
+ case Type::True: return "1";
+ case Type::Numeric: return std::to_string(numericValue());
+ case Type::Float: return std::to_string(floatValue());
+ case Type::String: return { Z_STRVAL_P(_val), Z_STRLEN_P(_val) };
+ default: break;
+ }
- // make a clone
+ // clone the value and convert it to a string
return clone(Type::String).stringValue();
}
@@ -1273,36 +1247,6 @@ char *Value::buffer() const
}
/**
- * Reserve enough space
- * @param size
- * @return char*
- */
-char *Value::reserve(size_t size)
-{
- // must be a string
- setType(Type::String);
-
- // is the current buffer too small?
- if (Z_STRLEN_P(_val) < (int)size)
- {
- // is there already a buffer?
- if (!Z_STRVAL_P(_val)) Z_STRVAL_P(_val) = (char *)emalloc(size+1);
-
- // reallocate an existing buffer
- else Z_STRVAL_P(_val) = (char *)erealloc(Z_STRVAL_P(_val), size+1);
-
- // last byte should be zero
- Z_STRVAL_P(_val)[size] = 0;
- }
-
- // store size
- Z_STRLEN_P(_val) = size;
-
- // done
- return Z_STRVAL_P(_val);
-}
-
-/**
* Get access to the raw buffer for read operations. Note that this
* only works for string variables - other variables return nullptr.
*
@@ -1413,7 +1357,7 @@ ValueIterator Value::createIterator(bool begin) const
TSRMLS_FETCH();
// is a special iterator method defined in the class entry?
- auto *entry = zend_get_class_entry(_val TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(_val);
// check if there is an iterator
if (entry->get_iterator)
@@ -1476,14 +1420,12 @@ bool Value::contains(int index) const
{
// if we're an object implementing ArrayAccess it makes sense for this method to work as well, so we call offsetExists
if (isObject() && instanceOf("ArrayAccess")) return call("offsetExists", index).boolValue();
+
// must be an array
else if (!isArray()) return false;
- // unused variable
- zval **result;
-
// check if this index is already in the array
- return zend_hash_index_find(Z_ARRVAL_P(_val), index, (void**)&result) != FAILURE;
+ return zend_hash_index_find(Z_ARRVAL_P(_val), index) != nullptr;
}
/**
@@ -1497,14 +1439,11 @@ bool Value::contains(const char *key, int size) const
// calculate size
if (size < 0) size = ::strlen(key);
- // unused variable
- zval **result;
-
// deal with arrays
if (isArray())
{
// check if index is already in the array
- return zend_hash_find(Z_ARRVAL_P(_val), key, size+1, (void **)&result) != FAILURE;
+ return zend_hash_find(Z_ARRVAL_P(_val), zend_string_init(key, size, 1)) != nullptr;
}
else if (isObject())
{
@@ -1513,7 +1452,7 @@ bool Value::contains(const char *key, int size) const
// retrieve the object pointer and check whether the property we are trying to retrieve
// is marked as private/protected (cast necessary for php 5.3)
- if (zend_check_property_access(zend_objects_get_address(_val TSRMLS_CC), const_cast<char *>(key), size TSRMLS_CC) == FAILURE) return false;
+ if (zend_check_property_access(Z_OBJ_P(_val), zend_string_init(key, size, 1) TSRMLS_CC) == FAILURE) return false;
// check if the 'has_property' method is available for this object
auto *has_property = Z_OBJ_HT_P(_val)->has_property;
@@ -1526,11 +1465,7 @@ bool Value::contains(const char *key, int size) const
// call the has_property() method (0 means: check whether property exists and is not NULL,
// this is not really what we want, but the closest to the possible values of that parameter)
-#if PHP_VERSION_ID >= 50400
return has_property(_val, property._val, 0, nullptr TSRMLS_CC);
-#else
- return has_property(_val, property._val, 0 TSRMLS_CC);
-#endif
}
else
{
@@ -1549,19 +1484,19 @@ Value Value::get(int index) const
// if we're an actual normal array we just use the zend_hash_index_find method
if (isArray())
{
- // zval to retrieve
- zval **result;
+ // retrieve the value
+ auto *result = zend_hash_index_find(Z_ARRVAL_P(_val), index);
- // check if index is in the array
- if (zend_hash_index_find(Z_ARRVAL_P(_val), index, (void **)&result) == FAILURE) return Value();
+ // did the offset exist?
+ if (!result) return Type::Undefined;
// wrap the value
- return Value(*result);
+ return result;
}
// if we're an object implementing ArrayAccess it makes sense for this method to work as well, so we call offsetGet
else if (isObject() && instanceOf("ArrayAccess")) return call("offsetGet", index);
// if we're neither we return an empty value
- else return Value();
+ else return Type::Undefined;
}
/**
@@ -1581,14 +1516,8 @@ Value Value::get(const char *key, int size) const
// are we in an object or an array?
if (isArray())
{
- // the result value
- zval **result;
-
- // check if this index is already in the array, otherwise we return NULL
- if (zend_hash_find(Z_ARRVAL_P(_val), key, size + 1, (void **)&result) == FAILURE) return Value();
-
- // wrap the value
- return Value(*result);
+ // find the result and wrap it in a value
+ return Value{ zend_hash_find(Z_ARRVAL_P(_val), zend_string_init(key, size, 1)) };
}
else
{
@@ -1598,8 +1527,11 @@ Value Value::get(const char *key, int size) const
// we need the tsrm_ls variable
TSRMLS_FETCH();
- // read the property (cast necessary for php 5.3)
- zval *property = zend_read_property(nullptr, _val, const_cast<char *>(key), size, 0 TSRMLS_CC);
+ // temporary value for holding any error
+ zval rv;
+
+ // read the property
+ zval *property = zend_read_property(nullptr, _val, key, size, 0, &rv TSRMLS_CC);
// wrap in value
return Value(property);
@@ -1616,13 +1548,13 @@ Value Value::get(const char *key, int size) const
void Value::setRaw(int index, const Value &value)
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// add the value (this will decrement refcount on any current variable)
add_index_zval(_val, index, value._val);
// the variable has one more reference (the array entry)
- Z_ADDREF_P(value._val);
+ Z_TRY_ADDREF_P(value._val);
}
/**
@@ -1634,13 +1566,13 @@ void Value::setRaw(int index, const Value &value)
void Value::set(int index, const Value &value)
{
// the current value
- zval **current;
+ zval *current;
// check if this index is already in the array, otherwise we return NULL
- if (isArray() && zend_hash_index_find(Z_ARRVAL_P(_val), index, (void **)&current) != FAILURE)
+ if (isArray() && (current = zend_hash_index_find(Z_ARRVAL_P(_val), index)))
{
// skip if nothing is going to change
- if (value._val == *current) return;
+ if (value._val == current) return;
}
// must be an array
@@ -1665,24 +1597,24 @@ void Value::setRaw(const char *key, int size, const Value &value)
if (isObject())
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// we need the tsrm_ls variable
TSRMLS_FETCH();
- // update the property (cast necessary for php 5.3)
- zend_update_property(nullptr, _val, const_cast<char *>(key), size, value._val TSRMLS_CC);
+ // update the property
+ zend_update_property(nullptr, _val, key, size, value._val TSRMLS_CC);
}
else
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// add the value (this will reduce the refcount of the current value)
add_assoc_zval_ex(_val, key, size+1, value._val);
// the variable has one more reference (the array entry)
- Z_ADDREF_P(value._val);
+ Z_TRY_ADDREF_P(value._val);
}
}
@@ -1696,13 +1628,13 @@ void Value::setRaw(const char *key, int size, const Value &value)
void Value::set(const char *key, int size, const Value &value)
{
// the current value
- zval **current;
+ zval *current;
// check if this index is already in the array, otherwise we return NULL
- if (isArray() && zend_hash_find(Z_ARRVAL_P(_val), key, size + 1, (void **)&current) != FAILURE)
+ if (isArray() && (current = zend_hash_find(Z_ARRVAL_P(_val), zend_string_init(key, size, 1))))
{
// skip if nothing is going to change
- if (value._val == *current) return;
+ if (value._val == current) return;
}
// this should be an object or an array
@@ -1722,7 +1654,7 @@ void Value::unset(int index)
if (!isArray()) return;
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// remove the index
zend_hash_index_del(Z_ARRVAL_P(_val), index);
@@ -1739,7 +1671,7 @@ void Value::unset(const char *key, int size)
if (isObject())
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// we need the tsrm_ls variable
TSRMLS_FETCH();
@@ -1750,10 +1682,10 @@ void Value::unset(const char *key, int size)
else if (isArray())
{
// if this is not a reference variable, we should detach it to implement copy on write
- SEPARATE_ZVAL_IF_NOT_REF(&_val);
+ SEPARATE_ZVAL_IF_NOT_REF(_val);
// remove the index
- zend_hash_del(Z_ARRVAL_P(_val), key, size + 1);
+ zend_hash_del(Z_ARRVAL_P(_val), zend_string_init(key, size, 1));
}
}