summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartijn Otto <martijn.otto@copernica.com>2016-05-17 13:47:20 +0200
committerMartijn Otto <martijn.otto@copernica.com>2016-05-17 13:47:20 +0200
commitdd5f3a635053aa03417cdab1228e03c94b9c3136 (patch)
tree53178a459fcefdfa551eb501064e70453611788a
parent9f2e816c787c30ceeb139623c8dae594c4b4443d (diff)
Fixed final compilation issues
-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/type.h28
-rw-r--r--include/value.h41
-rw-r--r--zend/callable.cpp65
-rw-r--r--zend/callable.h40
-rw-r--r--zend/classimpl.cpp206
-rw-r--r--zend/classimpl.h39
-rw-r--r--zend/constantfuncs.cpp44
-rw-r--r--zend/exists.cpp39
-rw-r--r--zend/extensionimpl.cpp88
-rw-r--r--zend/extensionimpl.h38
-rw-r--r--zend/file.cpp59
-rw-r--r--zend/global.cpp69
-rw-r--r--zend/globals.cpp24
-rw-r--r--zend/ini.cpp35
-rw-r--r--zend/iteratorimpl.cpp15
-rw-r--r--zend/iteratorimpl.h9
-rw-r--r--zend/object.cpp28
-rw-r--r--zend/objectimpl.h2
-rw-r--r--zend/super.cpp12
-rw-r--r--zend/value.cpp460
24 files changed, 663 insertions, 765 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/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..99c8cc9 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,7 +1112,7 @@ private:
protected:
/**
* The wrapped zval
- * @var struct zval
+ * @var struct zval*
*/
struct _zval_struct *_val;
diff --git a/zend/callable.cpp b/zend/callable.cpp
index f0dc3e8..1231b55 100644
--- a/zend/callable.cpp
+++ b/zend/callable.cpp
@@ -27,7 +27,7 @@ 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);
@@ -44,7 +44,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 +52,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,10 +65,10 @@ 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?
@@ -83,12 +78,12 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int
// fill the members of the entity, and hide a pointer to the current object in the name
entry->fname = (const char *)_ptr;
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 +91,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 272b22e..65a971f 100644
--- a/zend/callable.h
+++ b/zend/callable.h
@@ -24,24 +24,23 @@ 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 = {}) :
+ _ptr(this, 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);
}
}
@@ -55,6 +54,8 @@ public:
_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
@@ -65,19 +66,12 @@ public:
_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
@@ -101,7 +95,7 @@ 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:
@@ -131,22 +125,22 @@ 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 = zend_string_init(arg.name(), ::strlen(arg.name()), 1);
+ info->name = arg.name();
// are we filling an object
- if (arg.type() == Type::Object) info->class_name = zend_string_init(arg.classname(), ::strlen(arg.classname()), 1);
+ 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
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index 84e23ef..e4a620f 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -7,6 +7,7 @@
* @copyright 2014 Copernica BV
*/
#include "includes.h"
+#include <cstring>
/**
* Set up namespace
@@ -19,10 +20,10 @@ namespace Php {
ClassImpl::~ClassImpl()
{
// destruct the entries
- if (_entries) delete[] _entries;
+ delete[] _entries;
// free the stored pointer
- free(_self);
+ if (_self) zend_string_release(_self);
}
/**
@@ -54,14 +55,14 @@ static ClassImpl *self(zend_class_entry *entry)
* the string, in case PHP tries to read it) and after that the pointer
* and we leave the doc_comment_len at 0.
*/
- while (entry->parent && (entry->info.user.doc_comment == nullptr || entry->info.user.doc_comment_len > 0))
+ while (entry->parent && (entry->info.user.doc_comment == nullptr || ZSTR_LEN(entry->info.user.doc_comment) > 0))
{
// we did not create this class entry, but luckily we have a parent
entry = entry->parent;
}
// retrieve the comment (it has a pointer hidden in it to the ClassBase object)
- const char *comment = entry->info.user.doc_comment;
+ const char *comment = ZSTR_VAL(entry->info.user.doc_comment);
// the first byte of the comment is an empty string (null character), but
// the next bytes contain a pointer to the ClassBase class
@@ -90,13 +91,11 @@ struct CallData
void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS)
{
// retrieve the originally called (and by us allocated) function object
- // (this was copied from the zend engine source code, code looks way too
- // ugly to be made by me)
- CallData *data = (CallData *)EG(current_execute_data)->function_state.function;
+ auto *data = (CallData *)execute_data->func;
zend_internal_function *func = &data->func;
// retrieve the function name
- const char *name = func->function_name;
+ const char *name = ZSTR_VAL(func->function_name);
ClassBase *meta = data->self->_base;
// the data structure was allocated by ourselves in the getMethod or
@@ -111,7 +110,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS)
Value result(return_value, true);
// construct parameters
- ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);
+ ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC);
// retrieve the base object
Base *base = params.object();
@@ -139,9 +138,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS)
void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
{
// retrieve the originally called (and by us allocated) function object
- // (this was copied from the zend engine source code, code looks way too
- // ugly to be made by me)
- CallData *data = (CallData *)EG(current_execute_data)->function_state.function;
+ auto *data = (CallData *)execute_data->func;
// get self reference
ClassBase *meta = data->self->_base;
@@ -158,7 +155,7 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
Value result(return_value, true);
// construct parameters
- ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC);
+ ParametersImpl params(getThis(), ZEND_NUM_ARGS() TSRMLS_CC);
// retrieve the base object
Base *base = params.object();
@@ -181,13 +178,13 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
/**
* Method that returns the function definition of the __call function
*
- * @param object_ptr Pointer to the object from which we want to retrieve the member function
+ * @param object Pointer to the object from which we want to retrieve the member function
* @param method The method that we want information about
* @param key ???
* @param tsrm_ls
* @return zend_function
*/
-zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, const zval *key TSRMLS_DC)
+zend_function *ClassImpl::getMethod(zend_object **object, zend_string *method, const zval *key TSRMLS_DC)
{
// something strange about the Zend engine (once more). The structure with
// object-handlers has a get_method and call_method member. When a function is
@@ -198,13 +195,13 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, cons
// first we'll check if the default handler does not have an implementation,
// in that case the method is probably already implemented as a regular method
- auto *defaultFunction = std_object_handlers.get_method(object_ptr, method, key TSRMLS_CC);
+ auto *defaultFunction = std_object_handlers.get_method(object, method, key TSRMLS_CC);
// did the default implementation do anything?
if (defaultFunction) return defaultFunction;
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(*object_ptr TSRMLS_CC);
+ auto *entry = (*object)->ce;
// this is peculiar behavior of the zend engine, we first are going to dynamically
// allocate memory holding all the properties of the __call method (we initially
@@ -243,11 +240,11 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, zend_string *method, cons
* @param tsrm_ls
* @return zend_function
*/
-zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *method, const zval *key TSRMLS_DC)
+zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *method TSRMLS_DC)
{
// first we'll check if the default handler does not have an implementation,
// in that case the method is probably already implemented as a regular method
- auto *defaultFunction = zend_std_get_static_method(entry, method, key TSRMLS_CC);
+ auto *defaultFunction = zend_std_get_static_method(entry, method, nullptr TSRMLS_CC);
// did the default implementation do anything?
if (defaultFunction) return defaultFunction;
@@ -285,7 +282,7 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string *
* @param tsrm_ls
* @return int
*/
-int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zval **object_ptr TSRMLS_DC)
+int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr TSRMLS_DC)
{
// it is really unbelievable how the Zend engine manages to implement every feature
// in a complete different manner. You would expect the __invoke() and the
@@ -294,9 +291,6 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
// to fill the function parameter with all information about the invoke()
// method that is going to get called
- // retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
-
// just like we did for getMethod(), we're going to dynamically allocate memory
// with all information about the function
auto *data = (CallData *)emalloc(sizeof(CallData));
@@ -309,12 +303,12 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
function->arg_info = nullptr;
function->num_args = 0;
function->required_num_args = 0;
- function->scope = entry;
+ function->scope = *entry_ptr;
function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
function->function_name = nullptr;
// store pointer to ourselves
- data->self = self(entry);
+ data->self = self(*entry_ptr);
// assign this dynamically allocated variable to the func parameter
// the cast is ok, because zend_internal_function is a member of the
@@ -323,7 +317,7 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
// the object_ptr should be filled with the object on which the method is
// called (otherwise the Zend engine tries to call the method statically)
- *object_ptr = object;
+ *object_ptr = Z_OBJ_P(object);
// done
return SUCCESS;
@@ -364,12 +358,20 @@ zend_object_handlers *ClassImpl::objectHandlers()
_handlers.get_method = &ClassImpl::getMethod;
_handlers.get_closure = &ClassImpl::getClosure;
+ // register destructor and deallocator
+ _handlers.dtor_obj = &ClassImpl::destructObject;
+ _handlers.free_obj = &ClassImpl::freeObject;
+
// handler to cast to a different type
_handlers.cast_object = &ClassImpl::cast;
// method to compare two objects
_handlers.compare_objects = &ClassImpl::compare;
+ // set the offset between our class implementation and
+ // the zend_object member in the allocated structure
+ _handlers.offset = ObjectImpl::offset();
+
// remember that object is now initialized
_initialized = true;
@@ -400,10 +402,10 @@ int ClassImpl::compare(zval *val1, zval *val2 TSRMLS_DC)
try
{
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(val1 TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(val1);
// other object must be of the same type
- if (entry != zend_get_class_entry(val2 TSRMLS_CC)) throw NotImplemented();
+ if (entry != Z_OBJCE_P(val2)) throw NotImplemented();
// we need the C++ class meta-information object
ClassBase *meta = self(entry)->_base;
@@ -448,7 +450,7 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
Base *object = ObjectImpl::find(val TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(val TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(val);
// we need the C++ class meta-information object
ClassBase *meta = self(entry)->_base;
@@ -456,7 +458,10 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
// retval is not yet initialized --- and again feelings of disbelief,
// frustration, wonder and anger come up when you see that there are not two
// functions in the Zend engine that have a comparable API
- INIT_PZVAL(retval);
+ //
+ // this function was removed, because it was supposedly no longer necessary
+ // can we get away with removing it here too?
+ // INIT_PZVAL(retval);
// when the magic function it not implemented, an exception will be thrown,
// and the extension may throw a Php::Exception
@@ -506,13 +511,14 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
/**
* Function that is called to create space for a cloned object
- * @param val The object to be cloned
- * @return zend_obejct_value The object to be created
+ *
+ * @param val The object to be cloned
+ * @return zend_object The object to be created
*/
-zend_object ClassImpl::cloneObject(zval *val TSRMLS_DC)
+zend_object *ClassImpl::cloneObject(zval *val TSRMLS_DC)
{
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(val TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(val);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -532,17 +538,17 @@ zend_object ClassImpl::cloneObject(zval *val TSRMLS_DC)
if (!cpp) zend_error(E_ERROR, "Unable to clone %s", entry->name);
// store the object
- auto *new_object = new ObjectImpl(entry, cpp, impl->objectHandlers, 1 TSRMLS_CC);
+ auto *new_object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1 TSRMLS_CC);
// clone the members (this will also call the __clone() function if the user
// had registered that as a visible method)
- zend_objects_clone_members(new_object->php(), result, old_object->php(), Z_OBJ_HANDLE_P(val) TSRMLS_CC);
+ zend_objects_clone_members(new_object->php(), old_object->php() TSRMLS_CC);
// was a custom clone method installed? If not we call the magic c++ __clone method
if (!entry->clone) meta->callClone(cpp);
// done
- return new_object->object();
+ return new_object->php();
}
/**
@@ -600,10 +606,11 @@ int ClassImpl::countElements(zval *object, long *count TSRMLS_DC)
* @param object The object on which it is called
* @param offset The name of the property
* @param type The type of the variable???
+ * @param rv Pointer to where to store the data
* @param tsrm_ls
* @return zval
*/
-zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC)
+zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv TSRMLS_DC)
{
// what to do with the type?
//
@@ -633,7 +640,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC)
try
{
// ArrayAccess is implemented, call function
- return toZval(arrayaccess->offsetGet(offset), type);
+ return toZval(arrayaccess->offsetGet(offset), type, rv);
}
catch (Exception &exception)
{
@@ -650,7 +657,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC)
if (!std_object_handlers.read_dimension) return nullptr;
// call default
- return std_object_handlers.read_dimension(object, offset, type TSRMLS_CC);
+ return std_object_handlers.read_dimension(object, offset, type, rv TSRMLS_CC);
}
}
@@ -790,26 +797,52 @@ void ClassImpl::unsetDimension(zval *object, zval *member TSRMLS_DC)
/**
* Helper method to turn a property into a zval
- * @param value
- * @param type
- * @return Value
+ *
+ * @param value The value to convert to a zval
+ * @param type The type of operation (read or write)
+ * @param rv Pointer to where to store the data
+ * @return The result (same as the ptr input)
*/
-zval *ClassImpl::toZval(Value &&value, int type)
+zval *ClassImpl::toZval(Value &&value, int type, zval *rv)
{
- // because we do not want the value object to destruct the zval when
- // it falls out of scope, we detach the zval from it, if this is a regular
- // read operation we can do this right away
- if (type == 0) return value.detach(false);
-
- // this is a more complicated read operation, the scripts wants to get
- // deeper access to the returned value. This, however, is only possible
- // if the value has more than once reference (if it has a refcount of one,
- // the value object that we have here is the only instance of the zval,
- // and it is simply impossible to return a reference or so
- if (value.refcount() <= 1) return value.detach(false);
-
- // we're dealing with an editable zval, return a reference variable
- return Value(value.detach(false), true).detach(false);
+ // the result zval that needs to be copied over
+ zval *result = nullptr;
+
+ /**
+ * Because we do not want the value object to destruct the zval when
+ * it falls out of scope, we detach the zval from it, if this is a regular
+ * read operation we can do this right away.
+ *
+ * For write operations we need to check the refcount. If the refcount is
+ * only 1 (meaning the value object has the only reference) we cannot return
+ * a reference because there _is_ nothing to reference (the value will destruct)
+ */
+ if (type == 0 || value.refcount() <= 1)
+ {
+ // first retrieve the value so we can copy it
+ result = value.detach(false);
+ }
+ // this is an editable zval, return a reference to it
+ else
+ {
+ // we're dealing with an editable zval, retrieve a reference variable
+ result = Value(value.detach(false), true).detach(false);
+ }
+
+ // now copy the value over to the pointer
+ ZVAL_COPY_VALUE(rv, result);
+
+ // if the zval has a reference count we must decrease it
+ Z_TRY_DELREF_P(result);
+
+ // the pointer from the value may now be destroyed
+ // (it was allocated by the value and detached)
+ // we do not actually "destroy" the value here,
+ // even if the refcount reaches 0 here!
+ delete result;
+
+ // return the pointer to the value
+ return rv;
}
/**
@@ -819,7 +852,7 @@ zval *ClassImpl::toZval(Value &&value, int type)
* @param offset The name of the property
* @param type The type of the variable???
* @param cache_slot The cache slot used
- * @param rv The "return value" (for errors
+ * @param rv Pointer to where to store the data
* @param tsrm_ls
* @return val
*/
@@ -846,7 +879,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
Base *base = ObjectImpl::find(object TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -866,12 +899,12 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
if (iter == impl->_properties.end())
{
// retrieve value from the __get method
- return toZval(meta->callGet(base, key), type);
+ return toZval(meta->callGet(base, key), type, rv);
}
else
{
// get the value
- return toZval(iter->second->get(base), type);
+ return toZval(iter->second->get(base), type, rv);
}
}
catch (const NotImplemented &exception)
@@ -880,7 +913,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s
if (!std_object_handlers.read_property) return nullptr;
// call default
- return std_object_handlers.read_property(object, name, type, key TSRMLS_CC);
+ return std_object_handlers.read_property(object, name, type, cache_slot, rv TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -912,7 +945,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach
Base *base = ObjectImpl::find(object TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -949,7 +982,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach
if (!std_object_handlers.write_property) return;
// call the default
- std_object_handlers.write_property(object, name, value, key TSRMLS_CC);
+ std_object_handlers.write_property(object, name, value, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -989,7 +1022,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **
Base *base = ObjectImpl::find(object TSRMLS_CC)->object();
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -1022,7 +1055,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **
if (!std_object_handlers.has_property) return 0;
// call default
- return std_object_handlers.has_property(object, name, has_set_exists, key TSRMLS_CC);
+ return std_object_handlers.has_property(object, name, has_set_exists, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1052,7 +1085,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM
try
{
// retrieve the class entry linked to this object
- auto *entry = zend_get_class_entry(object TSRMLS_CC);
+ auto *entry = Z_OBJCE_P(object);
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -1075,7 +1108,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM
if (!std_object_handlers.unset_property) return;
// call the default
- std_object_handlers.unset_property(object, member, key TSRMLS_CC);
+ std_object_handlers.unset_property(object, member, cache_slot TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1089,7 +1122,6 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot TSRM
* Function that is called when an object is about to be destructed
* This will call the magic __destruct method
* @param object
- * @param handle
* @param tsrm_ls
*/
void ClassImpl::destructObject(zend_object *object TSRMLS_DC)
@@ -1109,7 +1141,7 @@ void ClassImpl::destructObject(zend_object *object TSRMLS_DC)
catch (const NotImplemented &exception)
{
// fallback on the default destructor call
- zend_objects_destroy_object(object, handle TSRMLS_CC);
+ zend_objects_destroy_object(object TSRMLS_CC);
}
catch (Exception &exception)
{
@@ -1140,7 +1172,7 @@ void ClassImpl::freeObject(zend_object *object TSRMLS_DC)
* @param tsrm_ls
* @return zend_object_value The newly created object
*/
-zend_object ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
+zend_object *ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
{
// we need the C++ class meta-information object
ClassImpl *impl = self(entry);
@@ -1157,7 +1189,7 @@ zend_object ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
auto *object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1 TSRMLS_CC);
// return the php object stored in the implementation
- return object->object();
+ return object->php();
}
/**
@@ -1207,7 +1239,7 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje
* @param tsrm_ls
* @return int
*/
-int ClassImpl::serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC)
+int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data TSRMLS_DC)
{
// get the serializable object
Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(object TSRMLS_CC)->object());
@@ -1247,13 +1279,13 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, zend_uint *buf_le
* @param tsrm_ls
* @return int
*/
-int ClassImpl::unserialize(zval **object, zend_class_entry *entry, const unsigned char *buffer, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC)
+int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned char *buffer, size_t buf_len, zend_unserialize_data *data TSRMLS_DC)
{
// create the PHP object
- object_init_ex(*object, entry);
+ object_init_ex(object, entry);
// turn this into a serializale
- Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(*object TSRMLS_CC)->object());
+ Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(object TSRMLS_CC)->object());
// user may throw an exception in the serialize() function
try
@@ -1346,14 +1378,6 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref
// we need a special constructor
entry.create_object = &ClassImpl::createObject;
- // register destructor and deallocator
- entry.dtor_obj = &ClassImpl::destructObject;
- entry.free_obj = &ClassImpl::freeObject;
-
- // set the offset for the zend_object, to allow PHP to
- // locate the original point of the allocated memory
- entry.offset = ObjectImpl::offset();
-
// register function that is called for static method calls
entry.get_static_method = &ClassImpl::getStaticMethod;
@@ -1375,7 +1399,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref
if (_parent->_entry)
{
// register the class
- _entry = zend_register_internal_class_ex(&entry, _parent->_entry, const_cast<char*>(_parent->name().c_str()) TSRMLS_CC);
+ _entry = zend_register_internal_class_ex(&entry, _parent->_entry TSRMLS_CC);
}
else
{
@@ -1405,6 +1429,16 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref
// this pointer has to be copied to temporary pointer, as &this causes compiler error
ClassImpl *impl = this;
+ // allocate memory for the doc_comment (which we abuse for storing a pointer to ourselves)
+ _self = zend_string_alloc(sizeof(this), 1);
+
+ // make the string appear empty
+ ZSTR_VAL(_self)[0] = '\0';
+ ZSTR_LEN(_self) = 0;
+
+ // copy over the 'this'-pointer after the null-character
+ std::memcpy(ZSTR_VAL(_self) + 1, &impl, sizeof(impl));
+
// set access types flags for class
_entry->ce_flags = (int)_type;
diff --git a/zend/classimpl.h b/zend/classimpl.h
index 47e7785..1754755 100644
--- a/zend/classimpl.h
+++ b/zend/classimpl.h
@@ -93,9 +93,9 @@ 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
@@ -109,11 +109,13 @@ private:
/**
* 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:
/**
@@ -176,8 +178,8 @@ public:
* @param tsrm_ls
* @return zend_object Object info
*/
- static zend_object createObject(zend_class_entry *entry TSRMLS_DC);
- static zend_object cloneObject(zval *val 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,8 +192,8 @@ 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
@@ -210,10 +212,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);
@@ -248,7 +251,7 @@ public:
* @param offset The name of the property
* @param type The type of the variable???
* @param cache_slot The cache slot used
- * @param rv The "return value" (for errors
+ * @param rv Pointer to where to store the data
* @param tsrm_ls
* @return zval
*/
@@ -291,13 +294,13 @@ public:
/**
* Method that returns information about the function signature of a undefined method
*
- * @param object_ptr Pointer to the object from which we want to retrieve the member function
+ * @param object Pointer to the object from which we want to retrieve the member function
* @param method The method that we want information about
* @param key ???
* @param tsrm_ls
* @return zend_function
*/
- static zend_function *getMethod(zval **object_ptr, zend_string *method, const zval *key TSRMLS_DC);
+ 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
@@ -308,7 +311,7 @@ public:
* @param tsrm_ls
* @return zend_function
*/
- static zend_function *getStaticMethod(zend_class_entry *entry, zend_string *method, const zval *key TSRMLS_DC);
+ static zend_function *getStaticMethod(zend_class_entry *entry, zend_string *method TSRMLS_DC);
/**
* Method that returns information about the __invoke() method
@@ -319,7 +322,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
@@ -350,8 +353,8 @@ 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
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/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..5261b0d 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,11 +56,11 @@ 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)
@@ -68,10 +68,10 @@ static int match_module(zend_module_entry *entry)
// 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 +87,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);
-
+
// find again
iter = number2extension.find(number);
if (iter == number2extension.end()) return nullptr;
-
+
// found!
return iter->second;
}
@@ -109,7 +109,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 +148,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 +167,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 +187,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 +202,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 +242,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 +260,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 +300,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 +312,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 +342,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));
// 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 +383,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 +406,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..b933624 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,7 +100,7 @@ public:
{
return module();
}
-
+
private:
/**
* Initialize the namespace after it was registered
@@ -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/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/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 9d57df7..4861e9e 100644
--- a/zend/iteratorimpl.cpp
+++ b/zend/iteratorimpl.cpp
@@ -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;
}
/**
diff --git a/zend/iteratorimpl.h b/zend/iteratorimpl.h
index 5286309..2199e6f 100644
--- a/zend/iteratorimpl.h
+++ b/zend/iteratorimpl.h
@@ -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
diff --git a/zend/object.cpp b/zend/object.cpp
index 0f6307e..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,7 +76,7 @@ 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, ClassImpl::objectHandlers(entry) 0 TSRMLS_CC);
+ new ObjectImpl(entry, base, ClassImpl::objectHandlers(entry), 0 TSRMLS_CC);
// now we can store it
operator=(Value(base));
@@ -92,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);
@@ -118,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 b014bb1..ed48e26 100644
--- a/zend/objectimpl.h
+++ b/zend/objectimpl.h
@@ -77,7 +77,7 @@ public:
_mixed->php.handlers = handlers;
// set the initial refcount (if it is different than one, because one is the default)
- if (refcount != 1) php()->gc.refcount = refcount;
+ if (refcount != 1) GC_REFCOUNT(php()) = refcount;
// the object may remember that we are its implementation object
base->_impl = this;
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/value.cpp b/zend/value.cpp
index d22830a..40308f4 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);
}
@@ -158,13 +142,16 @@ Value::Value(double value)
*/
Value::Value(struct _zval_struct *val, bool ref) : _val(val)
{
+ // not refcounted? then there is nothing we can here
+ 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 +161,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 +180,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 +200,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_DUP(_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 +236,23 @@ 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);
+ // 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);
+ // 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 +263,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 +277,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 +294,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 +325,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 +348,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 +358,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 +397,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 +426,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 +446,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 +466,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 +486,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 +506,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 +526,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 +546,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 +566,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 +586,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 +794,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 +804,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 +833,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 +863,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 +887,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(unsigned i=0; i < argc; i++) { params[i] = *argv[i]._val; }
+
// call helper function
return do_exec(nullptr, _val, argc, params);
}
@@ -946,13 +906,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(unsigned 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 +928,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(unsigned 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 +1003,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 +1058,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 +1066,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 +1091,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 +1121,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);
}
/**
@@ -1185,16 +1141,10 @@ 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);
+ auto *copy = new zval;
// copy the data
- INIT_PZVAL_COPY(copy, _val);
-
- // run the copy constructor to ensure that everything gets copied
- zval_copy_ctor(copy);
+ ZVAL_DUP(copy, _val);
// wrap it using the Value(zval*) constructor, this will +1 the refcount!!!!
Value output(copy);
@@ -1239,11 +1189,13 @@ 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::True: return true;
+ case Type::False: return false;
+ default: return clone(Type::Bool).boolValue();
+ }
}
/**
@@ -1273,36 +1225,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 +1335,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 +1398,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 +1417,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 +1430,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 +1443,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 +1462,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 +1494,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 +1505,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 +1526,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 +1544,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 +1575,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 +1606,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 +1632,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 +1649,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 +1660,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));
}
}