From 0375c634c61278ad8756a6658f273d63d06445e8 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 18 Oct 2016 00:05:05 +0200 Subject: [PATCH 001/101] improved makefile --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index 930e2f76..e4ce5648 100644 --- a/Makefile +++ b/Makefile @@ -157,6 +157,11 @@ PHP_SHARED_OBJECTS = $(PHP_SOURCES:%.cpp=shared/%.o) COMMON_STATIC_OBJECTS = $(COMMON_SOURCES:%.cpp=static/%.o) PHP_STATIC_OBJECTS = $(PHP_SOURCES:%.cpp=static/%.o) +# +# Dependencies +# + +DEPENDENCIES = $(wildcard shared/common/*.d) $(wildcard shared/zend/*.d) $(wildcard static/common/*.d) $(wildcard static/zend/*.d) # # End of the variables section. Here starts the list of instructions and @@ -167,6 +172,8 @@ all: COMPILER_FLAGS += -g all: LINKER_FLAGS += -g all: phpcpp +-include ${DEPENDENCIES} + release: COMPILER_FLAGS += -O2 release: LINKER_FLAGS += -O2 release: phpcpp From 1630d9639f5eb3092c4fe363ed504196da89f4ce Mon Sep 17 00:00:00 2001 From: Roland Eischer Date: Fri, 14 Apr 2017 10:46:22 +0200 Subject: [PATCH 002/101] Fixed VLA definition because not every compiler supports that --- zend/parametersimpl.h | 2 +- zend/value.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/zend/parametersimpl.h b/zend/parametersimpl.h index fa58d9fa..bf100c09 100644 --- a/zend/parametersimpl.h +++ b/zend/parametersimpl.h @@ -29,7 +29,7 @@ class ParametersImpl : public Parameters reserve(argc); // array to store all the arguments in - zval arguments[argc]; + zval* arguments = static_cast(alloca(argc * sizeof(zval))); // retrieve the arguments zend_get_parameters_array_ex(argc, arguments); diff --git a/zend/value.cpp b/zend/value.cpp index a50d4991..2043fb2b 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -884,7 +884,7 @@ Value Value::call(const char *name) Value Value::exec(int argc, Value *argv) const { // array of zvals to execute - zval params[argc]; + zval* params = static_cast(alloca(argc * sizeof(zval))); // convert all the values for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; } @@ -906,7 +906,7 @@ Value Value::exec(const char *name, int argc, Value *argv) const Value method(name); // array of zvals to execute - zval params[argc]; + zval* params = static_cast(alloca(argc * sizeof(zval))); // convert all the values for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; } @@ -928,7 +928,7 @@ Value Value::exec(const char *name, int argc, Value *argv) Value method(name); // array of zvals to execute - zval params[argc]; + zval* params = static_cast(alloca(argc * sizeof(zval))); // convert all the values for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; } From 7c6d21ecae96178575b75f862d473151be1e58ee Mon Sep 17 00:00:00 2001 From: Ben Wiersma Date: Wed, 29 Aug 2018 10:33:26 +0200 Subject: [PATCH 003/101] If our zend value is a reference, pass through the reference to retrieve the object class entry --- include/value.h | 1 + zend/value.cpp | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/include/value.h b/include/value.h index daab0d45..2fd83456 100644 --- a/include/value.h +++ b/include/value.h @@ -398,6 +398,7 @@ class PHPCPP_EXPORT Value : private HashParent bool isBool() const; bool isString() const; bool isFloat() const; + bool isReference() const; bool isObject() const; bool isArray() const; bool isScalar() const { return isNull() || isNumeric() || isBool() || isString() || isFloat(); } diff --git a/zend/value.cpp b/zend/value.cpp index a50d4991..bee1a97e 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1048,6 +1048,16 @@ bool Value::isFloat() const return ((Type) Z_TYPE_P(_val.dereference())) == Type::Float; } +/** + * Are we a reference? + * @return bool + */ +bool Value::isReference() const +{ + // check our pointer if we are a reference + return Z_ISREF_P(_val); +} + /** * Are we an object? This will also check if we're a reference to an object * @return bool @@ -1130,12 +1140,20 @@ bool Value::isCallable() const */ zend_class_entry *Value::classEntry(bool allowString) const { + // are we a reference + if (isReference()) + { + // we go through the reference to retrieve the object + return Z_OBJCE_P(Z_REFVAL_P(_val)); + } + // is this an object - if (isObject()) + else if (isObject()) { // class entry can be easily found return Z_OBJCE_P(_val); } + else { // the value is not an object, is this allowed? @@ -1777,11 +1795,14 @@ HashMember Value::operator[](const char *key) */ Base *Value::implementation() const { + // are we a reference + if (isReference()) return ObjectImpl::find(Z_REFVAL_P(_val))->object(); + // must be an object - if (!isObject()) return nullptr; + if (isObject()) return ObjectImpl::find(_val)->object(); // retrieve the mixed object that contains the base - return ObjectImpl::find(_val)->object(); + return nullptr; } /** From da2054cb7636983762cc4a67b3e82f91123bef3d Mon Sep 17 00:00:00 2001 From: Rafal Goslawski Date: Wed, 29 Aug 2018 11:19:18 +0200 Subject: [PATCH 004/101] Update Makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8071966d..c934944c 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.1 -VERSION = 2.1.0 +VERSION = 2.1.1 # From 095fcc041fd630f90668c3ffffeb0fe4c1cfa894 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Wed, 29 Aug 2018 15:44:19 +0200 Subject: [PATCH 005/101] Add PHP 7.3 to see compilation errors --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2eb2e2f8..ad85d30f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ php: - 7.0 - 7.1 - 7.2 + - 7.3 # - nightly # doesn't work yet on PHP 7.3! not building. @@ -40,4 +41,4 @@ before_install: script: # simply make the project with the set compiler - - make -j4 COMPILER=$COMPILER && sudo make install \ No newline at end of file + - make -j4 COMPILER=$COMPILER && sudo make install From 2b077c89d84b2e5b5f363b4d3caaea9a31bca5bb Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Wed, 29 Aug 2018 18:59:43 +0200 Subject: [PATCH 006/101] Enably nightly PHP build check --- .travis.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad85d30f..0c2e4509 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,7 @@ php: - 7.0 - 7.1 - 7.2 - - 7.3 -# - nightly # doesn't work yet on PHP 7.3! not building. + - nightly # doesn't work yet on PHP 7.3! not building. # setting the env is the easiest, because it will mix with all the separate php versions (travis does this) From 46a35828a6e83582904b76cf6a01206657de0245 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Thu, 30 Aug 2018 10:25:27 +0200 Subject: [PATCH 007/101] fix where info->type was not correctly assigned, resulting in garbage being dereferenced (php7.2). fix compilation error for php7.3 regarding the way constants are implemented in zend. --- .gitignore | 2 ++ zend/callable.cpp | 30 +++++++++++----------- zend/callable.h | 61 ++++++++++++++++++++++++++++----------------- zend/constantimpl.h | 6 +++++ 4 files changed, 61 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 44e22f90..fe945915 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ *.txt *.a.* *.so.* +shared/ +static/ \ No newline at end of file diff --git a/zend/callable.cpp b/zend/callable.cpp index 7248203b..455e3234 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -98,6 +98,10 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int #if PHP_VERSION_ID < 70200 _argv[_argc + 1].class_name = reinterpret_cast(this); #else + // @todo this is broken. the zend engine, from 7.2 onwards copies over + // the struct and slices of the last element, because the num_args + // is incorrect in their view. another place to put this may be + // hiding it behind the fname _argv[_argc + 1].type = reinterpret_cast(this); #endif @@ -123,33 +127,29 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int */ void Callable::initialize(zend_internal_function_info *info, const char *classname) const { + // initialize all common elements + info->required_num_args = _required; + info->return_reference = false; + info->_is_variadic = false; + + // the structure has been slightly altered since php7.2 #if PHP_VERSION_ID < 70200 // store the classname info->class_name = classname; // number of required arguments, and the expected return type - info->required_num_args = _required; info->type_hint = (unsigned char)_return; - // we do not support return-by-reference - 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 info->allow_null = false; - info->_is_variadic = false; #else - info->required_num_args = _required; - info->return_reference = false; - info->_is_variadic = false; - - if (_return == Type::Object) { - info->type = reinterpret_cast(classname); - } - else { - info->type = ZEND_TYPE_ENCODE(static_cast(_return), 0); - } + // if we can return a class, we can simply provide the classname as const char * + if (_return == Type::Object) info->type = reinterpret_cast(classname); + + // otherwise, we have to encode the type + else info->type = ZEND_TYPE_ENCODE(static_cast(_return), 0); #endif } diff --git a/zend/callable.h b/zend/callable.h index a5ae285f..1448075e 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -39,7 +39,7 @@ class Callable { // the first record is initialized with information about the function, // so we skip that here - int i=1; + int i = 1; // loop through the arguments for (auto &argument : arguments) @@ -51,12 +51,15 @@ class Callable fill(&_argv[i++], argument); } - // initialize the extra argument + // initialize all elements to null + _argv[i].name = nullptr; + _argv[i].type = 0; + _argv[i].is_variadic = false; + _argv[i].pass_by_reference = false; + + // initialize the extra argument prior to 7.2 #if PHP_VERSION_ID < 70200 _argv[i].class_name = nullptr; - _argv[i].name = nullptr; -#else - _argv[i].type = 0; #endif } @@ -176,6 +179,7 @@ class Callable if (arg.type() == Type::Object) info->class_name = arg.classname(); else info->class_name = nullptr; + // whether or not we allow null info->allow_null = arg.allowNull(); #endif @@ -205,26 +209,36 @@ class Callable case Type::Float: info->type = ZEND_TYPE_ENCODE(IS_DOUBLE, arg.allowNull()); break; // floating-point values welcome too case Type::String: info->type = ZEND_TYPE_ENCODE(IS_STRING, arg.allowNull()); break; // accept strings, should auto-cast objects with __toString as well case Type::Array: info->type = ZEND_TYPE_ENCODE(IS_ARRAY, arg.allowNull()); break; // array of anything (individual members cannot be restricted) - case Type::Object: // must be an object of the given classname - if (arg.classname()) { - if (!arg.allowNull()) { - info->type = reinterpret_cast(arg.classname()); - } - else { - /// FIXME this leaks, although this happens only once during startup / MINIT - /// Probably not critical - - const char* orig = arg.classname(); - std::size_t len = std::strlen(orig); - char* newname = new char[len + 2]; // trailing NUL and heading ? - newname[0] = '?'; - std::memcpy(newname + 1, orig, len + 1 /* Trailing NUL */); - } - } - else { - info->type = ZEND_TYPE_ENCODE(IS_OBJECT, arg.allowNull()); + case Type::Object: // if there is a classname and the argument is not nullable, it's simply the classname + if (arg.classname() && !arg.allowNull()) info->type = reinterpret_cast(arg.classname()); + + // otherwise, we need to prepend the character '?' + else if (arg.classname() && arg.allowNull()) + { + // @todo the code below leaks memory; the string should be + // held at another level. however, this only happens on + // initialization and therefore is non-critical. still, + // it should be fixed. + + // find the length of the classname + size_t length = std::strlen(arg.classname()); + + // create a copy of the string, plus the ? and a null character. + char *name = new char[length + 2]; + + // set the ? + name[0] = '?'; + + // copy over the string and the terminating null character + memcpy(name + 1, arg.classname(), length + 1); + + // assign the string + info->type = reinterpret_cast(name); } + // we simply require the type to be any object + else info->type = ZEND_TYPE_ENCODE(IS_OBJECT, arg.allowNull()); + break; case Type::Callable: info->type = ZEND_TYPE_ENCODE(IS_CALLABLE, arg.allowNull()); break; // anything that can be invoked @@ -238,6 +252,7 @@ class Callable // support methods and functions with a fixed number of arguments. info->is_variadic = false; + // whether or not to pass the argument by reference info->pass_by_reference = arg.byReference(); } diff --git a/zend/constantimpl.h b/zend/constantimpl.h index dcc316cf..58ce214d 100644 --- a/zend/constantimpl.h +++ b/zend/constantimpl.h @@ -197,9 +197,15 @@ class ConstantImpl _constant.name = zend_string_init(_name, ::strlen(_name), 1); } + // before 7.3 constants could simply be set +#if PHP_VERSION_ID < 70300 // set all the other constant properties _constant.flags = CONST_CS | CONST_PERSISTENT; _constant.module_number = module_number; +#else + // from 7.3 onwards there is a macro for setting the constant flags and module number + ZEND_CONSTANT_SET_FLAGS(c, CONST_CS | CONST_PERSISTENT, module_number); +#endif // register the zval zend_register_constant(&_constant); From 982f3e8698a76cf24b5045c068d471a68578a58f Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Thu, 30 Aug 2018 11:01:07 +0200 Subject: [PATCH 008/101] info->type is >php7.2 only --- zend/callable.h | 3 ++- zend/constantfuncs.cpp | 8 ++++++-- zend/constantimpl.h | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/zend/callable.h b/zend/callable.h index 1448075e..307f9f12 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -53,13 +53,14 @@ class Callable // initialize all elements to null _argv[i].name = nullptr; - _argv[i].type = 0; _argv[i].is_variadic = false; _argv[i].pass_by_reference = false; // initialize the extra argument prior to 7.2 #if PHP_VERSION_ID < 70200 _argv[i].class_name = nullptr; +#else + _argv[i].type = 0; #endif } diff --git a/zend/constantfuncs.cpp b/zend/constantfuncs.cpp index 1e898085..b6ae6461 100644 --- a/zend/constantfuncs.cpp +++ b/zend/constantfuncs.cpp @@ -90,12 +90,16 @@ bool define(const char *name, size_t size, const Value &value) zval_copy_ctor(&constant.value); } + // before 7.3 constants could simply be set +#if PHP_VERSION_ID < 70300 // 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; +#else + // from 7.3 onwards there is a macro for setting the constant flags and module number + ZEND_CONSTANT_SET_FLAGS(constant, CONST_CS, PHP_USER_CONSTANT); +#endif // register the constant return zend_register_constant(&constant) == SUCCESS; diff --git a/zend/constantimpl.h b/zend/constantimpl.h index 58ce214d..7fbdb5e9 100644 --- a/zend/constantimpl.h +++ b/zend/constantimpl.h @@ -204,7 +204,7 @@ class ConstantImpl _constant.module_number = module_number; #else // from 7.3 onwards there is a macro for setting the constant flags and module number - ZEND_CONSTANT_SET_FLAGS(c, CONST_CS | CONST_PERSISTENT, module_number); + ZEND_CONSTANT_SET_FLAGS(_constant, CONST_CS | CONST_PERSISTENT, module_number); #endif // register the zval From c6454f05448754321d280c32d469573db56c6380 Mon Sep 17 00:00:00 2001 From: Ben Wiersma Date: Thu, 30 Aug 2018 11:22:33 +0200 Subject: [PATCH 009/101] Alway dereference our value, if it is not a reference we get the original back again --- zend/value.cpp | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/zend/value.cpp b/zend/value.cpp index bee1a97e..661439a0 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1140,18 +1140,12 @@ bool Value::isCallable() const */ zend_class_entry *Value::classEntry(bool allowString) const { - // are we a reference - if (isReference()) - { - // we go through the reference to retrieve the object - return Z_OBJCE_P(Z_REFVAL_P(_val)); - } - // is this an object - else if (isObject()) + if (isObject()) { - // class entry can be easily found - return Z_OBJCE_P(_val); + // class entry can be easily found, we try to dereference here if our + // value is a reference to an object + return Z_OBJCE_P(_val.dereference()); } else @@ -1795,11 +1789,9 @@ HashMember Value::operator[](const char *key) */ Base *Value::implementation() const { - // are we a reference - if (isReference()) return ObjectImpl::find(Z_REFVAL_P(_val))->object(); - - // must be an object - if (isObject()) return ObjectImpl::find(_val)->object(); + // must be an object, we try to dereference because we might be a reference + // to an object + if (isObject()) return ObjectImpl::find(_val.dereference())->object(); // retrieve the mixed object that contains the base return nullptr; From 1bc77a91b80707eed4e59f2c9fe8b6c4a2c4a815 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Thu, 30 Aug 2018 11:51:00 +0200 Subject: [PATCH 010/101] constants were not right after all, and the iterator structure is slightly different for 7.3. --- zend/classimpl.cpp | 10 ++++++++++ zend/constantfuncs.cpp | 2 +- zend/constantimpl.h | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 48543b97..4b6fbe7c 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1354,7 +1354,17 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref { // install iterator functions entry.get_iterator = &ClassImpl::getIterator; + + // prior to 7.3, the iterator functions were statically allocated. +#if PHP_VERSION_ID < 70300 entry.iterator_funcs.funcs = IteratorImpl::functions(); +#else + // from 7.3 and up, we have to allocate it ourself + entry.iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs)); + + // and we finally include the pointer to the functions in the newly allocated structure + entry.iterator_funcs_ptr.funcs = IteratorImp::functions(); +#endif } // for serializable classes, we install callbacks for serializing and unserializing diff --git a/zend/constantfuncs.cpp b/zend/constantfuncs.cpp index b6ae6461..7b980941 100644 --- a/zend/constantfuncs.cpp +++ b/zend/constantfuncs.cpp @@ -98,7 +98,7 @@ bool define(const char *name, size_t size, const Value &value) constant.module_number = PHP_USER_CONSTANT; #else // from 7.3 onwards there is a macro for setting the constant flags and module number - ZEND_CONSTANT_SET_FLAGS(constant, CONST_CS, PHP_USER_CONSTANT); + ZEND_CONSTANT_SET_FLAGS(&constant, CONST_CS, PHP_USER_CONSTANT); #endif // register the constant diff --git a/zend/constantimpl.h b/zend/constantimpl.h index 7fbdb5e9..63289a82 100644 --- a/zend/constantimpl.h +++ b/zend/constantimpl.h @@ -204,7 +204,7 @@ class ConstantImpl _constant.module_number = module_number; #else // from 7.3 onwards there is a macro for setting the constant flags and module number - ZEND_CONSTANT_SET_FLAGS(_constant, CONST_CS | CONST_PERSISTENT, module_number); + ZEND_CONSTANT_SET_FLAGS(&_constant, CONST_CS | CONST_PERSISTENT, module_number); #endif // register the zval From d3a2d120a407096f7add6c3933b8076dc7b37194 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Thu, 30 Aug 2018 12:03:05 +0200 Subject: [PATCH 011/101] iterator_funcs is now of pointer type --- zend/classimpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 4b6fbe7c..12de34cd 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1363,7 +1363,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref entry.iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs)); // and we finally include the pointer to the functions in the newly allocated structure - entry.iterator_funcs_ptr.funcs = IteratorImp::functions(); + entry.iterator_funcs_ptr->funcs = IteratorImp::functions(); #endif } From f359c800028011affce541026703d3c18eeabced Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Thu, 30 Aug 2018 12:44:21 +0200 Subject: [PATCH 012/101] nightly stuff will be in a different branch for now --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 0c2e4509..f5ec7319 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ php: - 7.0 - 7.1 - 7.2 - - nightly # doesn't work yet on PHP 7.3! not building. +# - nightly # doesn't work yet on PHP 7.3! not building. # setting the env is the easiest, because it will mix with all the separate php versions (travis does this) From d8351b1a5e0f2f21f265007bbe0ae5aee7672331 Mon Sep 17 00:00:00 2001 From: Thierry Lamarre Date: Fri, 31 Aug 2018 11:12:32 +0200 Subject: [PATCH 013/101] fix build on OSX (#391) Should fix issue #367 --- Makefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c934944c..eff5725d 100644 --- a/Makefile +++ b/Makefile @@ -117,8 +117,16 @@ PHP_COMPILER_FLAGS = ${COMPILER_FLAGS} `${PHP_CONFIG} --includes` # LINKER_FLAGS = -shared +ifeq ($(UNAME), Darwin) + LINKER_FLAGS += -undefined dynamic_lookup +endif PHP_LINKER_FLAGS = `${PHP_CONFIG} --ldflags` ${LINKER_FLAGS} +ifeq ($(UNAME), Darwin) + LINKER_SONAME_OPTION = -install_name +else + LINKER_SONAME_OPTION = -soname +endif # # Command to remove files, copy files, link files and create directories. @@ -176,7 +184,7 @@ phpcpp: ${PHP_SHARED_LIBRARY} ${PHP_STATIC_LIBRARY} @echo "Build complete." ${PHP_SHARED_LIBRARY}: shared_directories ${COMMON_SHARED_OBJECTS} ${PHP_SHARED_OBJECTS} - ${LINKER} ${PHP_LINKER_FLAGS} -Wl,-soname,libphpcpp.so.$(SONAME) -o $@ ${COMMON_SHARED_OBJECTS} ${PHP_SHARED_OBJECTS} + ${LINKER} ${PHP_LINKER_FLAGS} -Wl,${LINKER_SONAME_OPTION},libphpcpp.so.$(SONAME) -o $@ ${COMMON_SHARED_OBJECTS} ${PHP_SHARED_OBJECTS} ${PHP_STATIC_LIBRARY}: static_directories ${COMMON_STATIC_OBJECTS} ${PHP_STATIC_OBJECTS} ${ARCHIVER} $@ ${COMMON_STATIC_OBJECTS} ${PHP_STATIC_OBJECTS} From a6b41241c8c73fb48efb8b96bd4d03b2503436cd Mon Sep 17 00:00:00 2001 From: Nathanael Noblet Date: Fri, 31 Aug 2018 04:21:34 -0600 Subject: [PATCH 014/101] Php73 Support (#390) * Added PHP7.3 support --- zend/classimpl.cpp | 7 ++----- zend/constantfuncs.cpp | 4 +++- zend/constantimpl.h | 2 +- zend/objectimpl.h | 7 +++++-- zend/value.cpp | 12 ++++++++++-- 5 files changed, 21 insertions(+), 11 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 12de34cd..c4a793ba 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1359,11 +1359,8 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref #if PHP_VERSION_ID < 70300 entry.iterator_funcs.funcs = IteratorImpl::functions(); #else - // from 7.3 and up, we have to allocate it ourself - entry.iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs)); - - // and we finally include the pointer to the functions in the newly allocated structure - entry.iterator_funcs_ptr->funcs = IteratorImp::functions(); + // from 7.3 and up, we may have to allocate it ourself + //entry.iterator_funcs_ptr = calloc(1, sizeof(zend_class_iterator_funcs)); #endif } diff --git a/zend/constantfuncs.cpp b/zend/constantfuncs.cpp index 7b980941..09e1be25 100644 --- a/zend/constantfuncs.cpp +++ b/zend/constantfuncs.cpp @@ -97,7 +97,9 @@ bool define(const char *name, size_t size, const Value &value) constant.flags = CONST_CS; constant.module_number = PHP_USER_CONSTANT; #else - // from 7.3 onwards there is a macro for setting the constant flags and module number + // constants are case sensitive (but not persistent, because this is a user + // space constant!) + // as module number we use a fake module number ZEND_CONSTANT_SET_FLAGS(&constant, CONST_CS, PHP_USER_CONSTANT); #endif diff --git a/zend/constantimpl.h b/zend/constantimpl.h index 63289a82..e58fd48d 100644 --- a/zend/constantimpl.h +++ b/zend/constantimpl.h @@ -206,7 +206,7 @@ class ConstantImpl // from 7.3 onwards there is a macro for setting the constant flags and module number ZEND_CONSTANT_SET_FLAGS(&_constant, CONST_CS | CONST_PERSISTENT, module_number); #endif - + // register the zval zend_register_constant(&_constant); } diff --git a/zend/objectimpl.h b/zend/objectimpl.h index 531f3344..f902b307 100644 --- a/zend/objectimpl.h +++ b/zend/objectimpl.h @@ -74,10 +74,13 @@ class ObjectImpl // install the handlers _mixed->php.handlers = handlers; - +#if PHP_VERSION_ID < 70300 // set the initial refcount (if it is different than one, because one is the default) if (refcount != 1) GC_REFCOUNT(php()) = refcount; - +#else + // set the initial refcount (if it is different than one, because one is the default) + if (refcount != 1) GC_SET_REFCOUNT(php(),refcount); +#endif // the object may remember that we are its implementation object base->_impl = this; } diff --git a/zend/value.cpp b/zend/value.cpp index 661439a0..6572dbee 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -158,9 +158,13 @@ Value::Value(struct _zval_struct *val, bool ref) // retrieve the reference zend_reference *ref = Z_REF_P(val); +#if PHP_VERSION_ID < 70300 // increment refcount ++GC_REFCOUNT(ref); - +#else + // increment refcount + GC_ADDREF(ref); +#endif // store the reference in our value ZVAL_REF(_val, ref); } @@ -237,9 +241,13 @@ Value Value::makeReference() // retriece the reference zend_reference *ref = Z_REF_P(from); +#if PHP_VERSION_ID < 70300 // increment reference count GC_REFCOUNT(ref)++; - +#else + // increment reference count + GC_ADDREF(ref); +#endif // copy the reference ZVAL_REF(to, ref); From da41a3ce2cfd1a2b119126c6efc8400d83194bc4 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Fri, 31 Aug 2018 12:22:26 +0200 Subject: [PATCH 015/101] enable nightly again, should work now --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f5ec7319..0c2e4509 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ php: - 7.0 - 7.1 - 7.2 -# - nightly # doesn't work yet on PHP 7.3! not building. + - nightly # doesn't work yet on PHP 7.3! not building. # setting the env is the easiest, because it will mix with all the separate php versions (travis does this) From 3a7a81f27223d4b0ffafcbfb992e65e94b4f8e12 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Fri, 31 Aug 2018 13:26:15 +0200 Subject: [PATCH 016/101] I allowed nightly failures, to fix the badge (hopefully) --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 0c2e4509..9b937ff7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,6 +25,10 @@ env: - COMPILER_PKG=clang++-5 COMPILER=clang++-5.0 - COMPILER_PKG=clang++-6 COMPILER=clang++-6.0 +matrix: + allow_failures: + - php: nightly + before_install: # install access to all gcc compilers - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test From efed3ae6dc94ee5fb5349c69adb1a5c6b9041cb0 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 28 Jan 2019 18:37:02 +0100 Subject: [PATCH 017/101] fixed some valgrind complaints about uninitialized memory --- zend/callable.cpp | 12 ++++----- zend/callable.h | 36 +++----------------------- zend/classimpl.cpp | 63 ++++++++++++++++++++++++++-------------------- 3 files changed, 45 insertions(+), 66 deletions(-) diff --git a/zend/callable.cpp b/zend/callable.cpp index 455e3234..3b637e30 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -4,7 +4,7 @@ * Implementation for the function class * * @author Emiel Bruijntjes - * @copyright 2013 Copernica BV + * @copyright 2013 - 2019 Copernica BV */ #include "includes.h" @@ -145,11 +145,11 @@ void Callable::initialize(zend_internal_function_info *info, const char *classna // them to false info->allow_null = false; #else - // if we can return a class, we can simply provide the classname as const char * - if (_return == Type::Object) info->type = reinterpret_cast(classname); - - // otherwise, we have to encode the type - else info->type = ZEND_TYPE_ENCODE(static_cast(_return), 0); + // the properties that are available on php 7.2 and higher + info->required_num_args = _required; + info->return_reference = false; + info->_is_variadic = false; + info->type = ZEND_TYPE_ENCODE((int)_return, true); #endif } diff --git a/zend/callable.h b/zend/callable.h index 307f9f12..14bda2fe 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -5,7 +5,7 @@ * API. * * @author Emiel Bruijntjes - * @copyright 2013 Copernica BV + * @copyright 2013 - 2019 Copernica BV */ /** @@ -112,7 +112,6 @@ class Callable /** * Fill a function entry * @param entry Entry to be filled - * @param ns Active namespace * @param classname Optional class name * @param flags Access flags */ @@ -121,7 +120,6 @@ class Callable /** * Fill function info * @param info Info object to be filled - * @param ns Active namespace * @param classname Optional class name */ void initialize(zend_internal_function_info *info, const char *classname = nullptr) const; @@ -211,37 +209,9 @@ class Callable case Type::String: info->type = ZEND_TYPE_ENCODE(IS_STRING, arg.allowNull()); break; // accept strings, should auto-cast objects with __toString as well case Type::Array: info->type = ZEND_TYPE_ENCODE(IS_ARRAY, arg.allowNull()); break; // array of anything (individual members cannot be restricted) case Type::Object: // if there is a classname and the argument is not nullable, it's simply the classname - if (arg.classname() && !arg.allowNull()) info->type = reinterpret_cast(arg.classname()); - - // otherwise, we need to prepend the character '?' - else if (arg.classname() && arg.allowNull()) - { - // @todo the code below leaks memory; the string should be - // held at another level. however, this only happens on - // initialization and therefore is non-critical. still, - // it should be fixed. - - // find the length of the classname - size_t length = std::strlen(arg.classname()); - - // create a copy of the string, plus the ? and a null character. - char *name = new char[length + 2]; - - // set the ? - name[0] = '?'; - - // copy over the string and the terminating null character - memcpy(name + 1, arg.classname(), length + 1); - - // assign the string - info->type = reinterpret_cast(name); - } - - // we simply require the type to be any object - else info->type = ZEND_TYPE_ENCODE(IS_OBJECT, arg.allowNull()); - + if (!arg.classname()) info->type = ZEND_TYPE_ENCODE(IS_OBJECT, arg.allowNull()); + else info->type = ZEND_TYPE_ENCODE_CLASS(arg.classname(), arg.allowNull()); break; - case Type::Callable: info->type = ZEND_TYPE_ENCODE(IS_CALLABLE, arg.allowNull()); break; // anything that can be invoked default: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // if not specified we allow anything #endif diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index c4a793ba..a4211328 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -4,7 +4,7 @@ * Implementation file for the ClassImpl class * * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV + * @copyright 2014 - 2019 Copernica BV */ #include "includes.h" #include @@ -92,7 +92,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) { // retrieve the originally called (and by us allocated) function object auto *data = (CallData *)execute_data->func; - zend_internal_function *func = &data->func; + auto *func = &data->func; // retrieve the function name const char *name = ZSTR_VAL(func->function_name); @@ -211,16 +211,19 @@ zend_function *ClassImpl::getMethod(zend_object **object, zend_string *method, c auto *data = (CallData *)emalloc(sizeof(CallData)); auto *function = &data->func; - // we're going to set all properties + // set all properties function->type = ZEND_INTERNAL_FUNCTION; - function->module = nullptr; - function->handler = &ClassImpl::callMethod; - function->arg_info = nullptr; - function->num_args = 0; + function->arg_flags[0] = 0; + function->arg_flags[1] = 0; + function->arg_flags[2] = 0; + function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; + function->function_name = method; + function->scope = entry; + function->prototype = nullptr; + function->num_args = 0; function->required_num_args = 0; - function->scope = entry; - function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; - function->function_name = method; + function->arg_info = nullptr; + function->handler = &ClassImpl::callMethod; // store pointer to ourselves data->self = self(entry); @@ -252,16 +255,19 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string * auto *data = (CallData *)emalloc(sizeof(CallData)); auto *function = &data->func; - // we're going to set all properties + // set all properties for the function function->type = ZEND_INTERNAL_FUNCTION; - function->module = nullptr; - function->handler = ClassImpl::callMethod; - function->arg_info = nullptr; - function->num_args = 0; + function->arg_flags[0] = 0; + function->arg_flags[1] = 0; + function->arg_flags[2] = 0; + function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; + function->function_name = nullptr; + function->scope = nullptr; + function->prototype = nullptr; + function->num_args = 0; function->required_num_args = 0; - function->scope = nullptr; - function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; - function->function_name = method; + function->arg_info = nullptr; + function->handler = &ClassImpl::callMethod; // store pointer to ourselves data->self = self(entry); @@ -293,16 +299,19 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct auto *data = (CallData *)emalloc(sizeof(CallData)); auto *function = &data->func; - // we're going to set all properties + // we're going to set all properties of the zend_internal_function struct function->type = ZEND_INTERNAL_FUNCTION; - function->module = nullptr; - function->handler = &ClassImpl::callInvoke; - function->arg_info = nullptr; - function->num_args = 0; + function->arg_flags[0] = 0; + function->arg_flags[1] = 0; + function->arg_flags[2] = 0; + function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; + function->function_name = nullptr; + function->scope = *entry_ptr; + function->prototype = nullptr; + function->num_args = 0; function->required_num_args = 0; - function->scope = *entry_ptr; - function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; - function->function_name = nullptr; + function->arg_info = nullptr; + function->handler = &ClassImpl::callInvoke; // store pointer to ourselves (note that the entry_ptr is useless // inside this function as it is always uninitialized for some reason) @@ -1310,7 +1319,7 @@ const struct _zend_function_entry *ClassImpl::entries() zend_function_entry *last = &_entries[i]; // all should be set to zero - memset(last, 0, sizeof(*last)); + memset(last, 0, sizeof(zend_function_entry)); // done return _entries; From e91de64e684b453fdf7567998494b601735da76c Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 28 Jan 2019 21:35:33 +0100 Subject: [PATCH 018/101] fixed the __invoke() and __call() methods: the return value of the C++ function was not passed to php space --- zend/classimpl.cpp | 23 +++++++++++------------ zend/classimpl.h | 10 +++++----- zend/constantimpl.h | 4 ++-- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index a4211328..f6986033 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -106,9 +106,6 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) // the function could throw an exception try { - // wrap the return value - Value result(return_value, true); - // construct parameters ParametersImpl params(getThis(), ZEND_NUM_ARGS()); @@ -116,8 +113,10 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) Base *base = params.object(); // is this a static, or a non-static call? - if (base) result = meta->callCall(base, name, params); - else result = meta->callCallStatic(name, params); + Php::Value result = base ? meta->callCall(base, name, params) : meta->callCallStatic(name, params); + + // return a full copy of the zval, and do not destruct it + RETVAL_ZVAL(result._val, 1, 0); } catch (const NotImplemented &exception) { @@ -151,9 +150,6 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS) // the function could throw an exception try { - // wrap the return value - Value result(return_value, true); - // construct parameters ParametersImpl params(getThis(), ZEND_NUM_ARGS()); @@ -161,7 +157,10 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS) Base *base = params.object(); // call the actual __invoke method on the base object - result = meta->callInvoke(base, params); + auto result = meta->callInvoke(base, params); + + // return a full copy of the zval, and do not destruct it + RETVAL_ZVAL(result._val, 1, 0); } catch (const NotImplemented &exception) { @@ -212,7 +211,7 @@ zend_function *ClassImpl::getMethod(zend_object **object, zend_string *method, c auto *function = &data->func; // set all properties - function->type = ZEND_INTERNAL_FUNCTION; + function->type = ZEND_INTERNAL_FUNCTION; function->arg_flags[0] = 0; function->arg_flags[1] = 0; function->arg_flags[2] = 0; @@ -256,7 +255,7 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string * auto *function = &data->func; // set all properties for the function - function->type = ZEND_INTERNAL_FUNCTION; + function->type = ZEND_INTERNAL_FUNCTION; function->arg_flags[0] = 0; function->arg_flags[1] = 0; function->arg_flags[2] = 0; @@ -300,7 +299,7 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct auto *function = &data->func; // we're going to set all properties of the zend_internal_function struct - function->type = ZEND_INTERNAL_FUNCTION; + function->type = ZEND_INTERNAL_FUNCTION; function->arg_flags[0] = 0; function->arg_flags[1] = 0; function->arg_flags[2] = 0; diff --git a/zend/classimpl.h b/zend/classimpl.h index 41613772..e9c4ef89 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -5,7 +5,7 @@ * exist for a class. * * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV + * @copyright 2014 - 2019 Copernica BV */ /** @@ -303,10 +303,10 @@ class ClassImpl /** * Method that returns information about the __invoke() method - * @param object - * @param entry - * @param func - * @param object_ptr + * @param object The object on which the method is called + * @param entry Class entry holding class properties + * @param func Function to be filled + * @param object_ptr To be filled with the object on which the method is to be called * @return int */ static int getClosure(zval *object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr); diff --git a/zend/constantimpl.h b/zend/constantimpl.h index e58fd48d..5d666105 100644 --- a/zend/constantimpl.h +++ b/zend/constantimpl.h @@ -4,7 +4,7 @@ * Implementation file for the constant class * * @author Emiel Bruijntjes - * @copyright 2015 Copernica BV + * @copyright 2015 - 2019 Copernica BV */ /** @@ -188,7 +188,7 @@ class ConstantImpl // copy the entire namespace name, separator and constant name ::strncpy(ZSTR_VAL(_constant.name), prefix.c_str(), prefix.size()); - ::strncpy(ZSTR_VAL(_constant.name) + prefix.size(), "\\", 1); + ::strcpy (ZSTR_VAL(_constant.name) + prefix.size(), "\\"); ::strncpy(ZSTR_VAL(_constant.name) + prefix.size() + 1, _name, namelen + 1); } else From 94fd45d970f18d56ce740e5d7d1143e58cc2de61 Mon Sep 17 00:00:00 2001 From: Rafal Goslawski Date: Thu, 28 Feb 2019 17:45:43 +0100 Subject: [PATCH 019/101] Update version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c5d86926..8ef87939 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.1 -VERSION = 2.1.1 +VERSION = 2.1.3 # From c7d1ff7930db49205bb639aed7cbe23693beac60 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 3 Mar 2019 16:56:50 +0100 Subject: [PATCH 020/101] added simple setup to access streams (and access the filedescriptor) --- include/stream.h | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ include/value.h | 3 +- phpcpp.h | 3 +- zend/includes.h | 3 +- zend/stream.cpp | 74 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 include/stream.h create mode 100644 zend/stream.cpp diff --git a/include/stream.h b/include/stream.h new file mode 100644 index 00000000..a2a84c46 --- /dev/null +++ b/include/stream.h @@ -0,0 +1,89 @@ +/** + * Stream.h + * + * PHP internally uses a "stream" concept. This is a complicated + * abstraction layer for sockets and filedescriptors and other sources + * from which data can be read and/or to which data can be sent. + * + * This Php::Stream class can be used to wrap around a value (if that + * value contains a stream) to access stream-specific properties. This + * class currently has a very limited API, but more features can be + * added if needed. + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Include guard + */ +#pragma once + +/** + * Forward declaration + */ +struct _php_stream; + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Class definition + */ +class Stream +{ +private: + /** + * The wrapped value that is suppoed to hold a stream + * @var Value + */ + Value _value; + + /** + * Object holding the stream data + * @var struct _php_stream + */ + struct _php_stream *_stream; + +public: + /** + * Constructor + * @param value value to be wrapped + * @throws std::runtime_error + */ + Stream(const Value &value); + + /** + * No copying + * @param that + */ + Stream(const Stream &that) = delete; + + /** + * Destructor + */ + virtual ~Stream(); + + /** + * Size of the read-buffer (number of bytes that have already been read + * from the underlying filesystem, but that are not yet passed to php space) + * @return size_t + */ + size_t readbuffer() const; + + /** + * Get access to the internal filedescriptor + * Note that not every stream is always associated with a filedescriptor, + * in which case this function returns -1 + * @return int + */ + int fd() const; +}; + +/** + * End of namespace + */ +} + diff --git a/include/value.h b/include/value.h index 2fd83456..030c4f10 100644 --- a/include/value.h +++ b/include/value.h @@ -15,7 +15,7 @@ * this class. * * @author Emiel Bruijntjes - * @copyright 2013 Copernica BV + * @copyright 2013 - 2019 Copernica BV */ /** @@ -1217,6 +1217,7 @@ class PHPCPP_EXPORT Value : private HashParent friend class ZendCallable; friend class Script; friend class ConstantImpl; + friend class Stream; /** * Friend functions which have to access that zval directly diff --git a/phpcpp.h b/phpcpp.h index 6f33dbc3..aff88ab0 100644 --- a/phpcpp.h +++ b/phpcpp.h @@ -3,7 +3,7 @@ * * Library to build PHP extensions with CPP * - * @copyright 2013 CopernicA BV + * @copyright 2013 - 2019 Copernica BV * @author Emiel Bruijntjes */ @@ -72,5 +72,6 @@ #include #include #include +#include #endif /* phpcpp.h */ diff --git a/zend/includes.h b/zend/includes.h index 121c0a20..bd0e5f78 100644 --- a/zend/includes.h +++ b/zend/includes.h @@ -4,7 +4,7 @@ * Startup include file to compile the phpcpp library * * @author Emiel Bruijntjes - * @copyright 2013 Copernica BV + * @copyright 2013 - 2019 Copernica BV */ /** @@ -104,6 +104,7 @@ #include "../include/script.h" #include "../include/file.h" #include "../include/function.h" +#include "../include/stream.h" /** * Common header files for internal use only diff --git a/zend/stream.cpp b/zend/stream.cpp new file mode 100644 index 00000000..aaff6fce --- /dev/null +++ b/zend/stream.cpp @@ -0,0 +1,74 @@ +/** + * Stream.cpp + * + * Implementation file for the stream class. + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Dependencies + */ +#include "includes.h" + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Constructor + * @param value value to be wrapped + * @throws std::runtime_error + */ +Stream::Stream(const Value &value) : _value(value) +{ + // get the stream object + php_stream_from_zval_no_verify(_stream, _value._val); + + // was this indeed a stream? + if (_stream == nullptr) throw std::runtime_error("variable does not hold a stream"); +} + +/** + * Destructor + */ +Stream::~Stream() {} + +/** + * Size of the read-buffer (number of bytes that have already been read + * from the underlying filesystem, but that are not yet passed to php space) + * @return size_t + */ +size_t Stream::readbuffer() const +{ + // do the math + return _stream->writepos - _stream->readpos; +} + +/** + * Get access to the internal filedescriptor + * Note that not every stream is always associated with a filedescriptor, + * in which case this function returns -1 + * @return int + */ +int Stream::fd() const +{ + // the return value + int retval = -1; + + // cast the stream to a filedescriptor, the cast-internal parameter is supposed to be to get + // rid of warnings about buffered data (this code was copied from streamfuncs.c). We use + // "FOR_SELECT" casting, as that is the most simple operations (without "FOR_SELECT" it flushes too) + auto result = php_stream_cast(_stream, PHP_STREAM_AS_FD_FOR_SELECT | PHP_STREAM_CAST_INTERNAL, (void **)&retval, 0); + + // on failure we return -1 + return result == SUCCESS ? retval : -1; +} + +/** + * End of namespace + */ +} + From 3c0fd3845f1ea40ef2dc7f2fb0cd1b32f7c73fe8 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 3 Mar 2019 19:55:51 +0100 Subject: [PATCH 021/101] make stream class externally available --- include/stream.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/stream.h b/include/stream.h index a2a84c46..2ab3a896 100644 --- a/include/stream.h +++ b/include/stream.h @@ -32,7 +32,7 @@ namespace Php { /** * Class definition */ -class Stream +class PHPCPP_EXPORT Stream { private: /** From 7b975bd1df65174d79241e555ef82c7f9c3538a2 Mon Sep 17 00:00:00 2001 From: Rafal Goslawski Date: Tue, 5 Mar 2019 12:53:30 +0100 Subject: [PATCH 022/101] Bump version to 2.1.4 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 8ef87939..be810e6b 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.1 -VERSION = 2.1.3 +VERSION = 2.1.4 # From 6b65489f79d1bd04db3f1f3d1c38523fa9563797 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Thu, 21 Mar 2019 08:03:56 +0100 Subject: [PATCH 023/101] renamed exception to throwable --- include/{exception.h => throwable.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename include/{exception.h => throwable.h} (100%) diff --git a/include/exception.h b/include/throwable.h similarity index 100% rename from include/exception.h rename to include/throwable.h From 1355617d74d533a3e557790292c8aed03a41f329 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Thu, 21 Mar 2019 14:21:49 +0100 Subject: [PATCH 024/101] - Enumeration Php::Error has been renamed to Php::ErrorType to make room for the new Php::Error class - Added new class Php::Error that can be used for throwing errors (PHP7 normally throws errors instead of reporting fatal errors, which is what PHP5 did) - Php::Exception is now only used for exceptions and no longer for errors (so extensions can be written to only catch exceptions, and not the errors) - Removed support for Exception::file() and Exception::line() - A couple of functions that used to report fatal errors, now throw an Php::Error object instead --- include/array.h | 12 +-- include/call.h | 2 +- include/errors.h | 2 +- include/fatalerror.h | 63 ------------ include/object.h | 8 +- include/throwable.h | 131 +++++++----------------- include/value.h | 2 +- include/zendcallable.h | 9 +- phpcpp.h | 3 +- zend/callable.cpp | 6 +- zend/classimpl.cpp | 109 ++++++++++---------- zend/exception_handler.cpp | 4 +- zend/fatalerror.cpp | 31 ------ zend/includes.h | 6 +- zend/object.cpp | 16 +-- zend/opcodes.h | 8 +- zend/origexception.h | 199 ------------------------------------- zend/value.cpp | 54 +++++----- zend/zendcallable.cpp | 11 +- 19 files changed, 158 insertions(+), 518 deletions(-) delete mode 100644 include/fatalerror.h delete mode 100644 zend/fatalerror.cpp delete mode 100644 zend/origexception.h diff --git a/include/array.h b/include/array.h index 925e9027..8321bc6f 100644 --- a/include/array.h +++ b/include/array.h @@ -5,7 +5,7 @@ * certain property always is an array * * @author Emiel Bruijntjes - * @copyright 2013, 2014 Copernica BV + * @copyright 2013 - 2019 Copernica BV */ /** @@ -31,7 +31,7 @@ class PHPCPP_EXPORT Array : public Value Array(const Value &value) : Value(value) { // type must be valid - if (value.type() != Type::Array) throw FatalError("Assigning a non-array to an array variable"); + if (value.type() != Type::Array) throw Error("Assigning a non-array to an array variable"); } /** @@ -41,7 +41,7 @@ class PHPCPP_EXPORT Array : public Value Array(Value &&value) : Value(std::move(value)) { // type must be valid - if (value.type() != Type::Array) throw FatalError("Moving a non-array to an array variable"); + if (value.type() != Type::Array) throw Error("Moving a non-array to an array variable"); } /** @@ -82,7 +82,7 @@ class PHPCPP_EXPORT Array : public Value virtual Value &setType(Type type) & override { // throw exception if things are going wrong - if (type != Type::Array) throw FatalError("Changing type of a fixed array variable"); + if (type != Type::Array) throw Error("Changing type of a fixed array variable"); // call base return Value::setType(Type::Array); @@ -99,7 +99,7 @@ class PHPCPP_EXPORT Array : public Value if (this == &value) return *this; // type must be valid - if (value.type() != Type::Array) throw FatalError("Assigning a non-array to a fixed array variable"); + if (value.type() != Type::Array) throw Error("Assigning a non-array to a fixed array variable"); // call base Value::operator=(value); @@ -119,7 +119,7 @@ class PHPCPP_EXPORT Array : public Value if (this == &value) return *this; // type must be valid - if (value.type() != Type::Array) throw FatalError("Moving a non-array to a fixed array variable"); + if (value.type() != Type::Array) throw Error("Moving a non-array to a fixed array variable"); // call base Value::operator=(std::move(value)); diff --git a/include/call.h b/include/call.h index 69c6e68a..3a3242b0 100644 --- a/include/call.h +++ b/include/call.h @@ -47,7 +47,7 @@ static inline Value require(const std::string &filename) { return requ extern PHPCPP_EXPORT Value require_once(const char *filename); static inline Value require_once(const std::string &filename) { return require_once(filename.c_str()); } extern PHPCPP_EXPORT Value set_exception_handler(const std::function &handler); -extern PHPCPP_EXPORT Value set_error_handler(const std::function &handler, Error error = Error::All); +extern PHPCPP_EXPORT Value set_error_handler(const std::function &handler, ErrorType error = ErrorType::All); extern PHPCPP_EXPORT Value error_reporting(Error error); extern PHPCPP_EXPORT const char *sapi_name(); diff --git a/include/errors.h b/include/errors.h index bc2922c4..1eaf4925 100644 --- a/include/errors.h +++ b/include/errors.h @@ -15,7 +15,7 @@ namespace Php { /** * Supported types of errors, this is mostly a copy from Zend/zend_errors.h */ -enum class Error : int { +enum class ErrorType : int { Error = (1 << 0L), Warning = (1 << 1L), Parse = (1 << 2L), diff --git a/include/fatalerror.h b/include/fatalerror.h deleted file mode 100644 index d1e1f647..00000000 --- a/include/fatalerror.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * FatalError.h - * - * - * Normally, fatal errors are reported with a call to zend_error(). - * - * However, this will trigger a longjmp(), which will cause objects - * constructed in the extension not to be destructed. We use therefore - * this FatalError class, which is a normal exception that _does_ - * cause objects to be destructed. - * - * When it is caught, right before control is handed back to the Zend - * engine, it will turn the exception into a zend_error() call and - * thus a longjmp. - * - * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV - */ - -/** - * Set up namespace - */ -namespace Php { - -/** - * Class definition - */ -class PHPCPP_EXPORT FatalError : public Exception -{ -public: - /** - * Constructor - * @param message - */ - FatalError(const std::string &message) : Exception(message) {} - - /** - * Destructor - */ - virtual ~FatalError() = default; - - /** - * Is this a native exception (one that was thrown from C++ code) - * @return bool - */ - virtual bool native() const override - { - // although it is native, we return 0 because it should not persist - // as exception, but it should live on as zend_error() in stead - return false; - } - - /** - * Report this error as a fatal error - * @return bool - */ - virtual bool report() const override; -}; - -/** - * End of namespace - */ -} diff --git a/include/object.h b/include/object.h index 56d7e834..0114af93 100644 --- a/include/object.h +++ b/include/object.h @@ -5,7 +5,7 @@ * Php::Base objects into regular Php::Value instances * * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV + * @copyright 2014 - 2019 Copernica BV */ /** @@ -109,7 +109,7 @@ class PHPCPP_EXPORT Object : public Value virtual Value &setType(Type type) & override { // throw exception if things are going wrong - if (type != Type::Object) throw FatalError("Changing type of a fixed object variable"); + if (type != Type::Object) throw Error("Changing type of a fixed object variable"); // call base return Value::setType(type); @@ -126,7 +126,7 @@ class PHPCPP_EXPORT Object : public Value if (this == &value) return *this; // type must be valid - if (value.type() != Type::Object) throw FatalError("Assigning a non-object to an object variable"); + if (value.type() != Type::Object) throw Error("Assigning a non-object to an object variable"); // call base Value::operator=(value); @@ -146,7 +146,7 @@ class PHPCPP_EXPORT Object : public Value if (this == &value) return *this; // type must be valid - if (value.type() != Type::Object) throw FatalError("Moving a non-object to an object variable"); + if (value.type() != Type::Object) throw Error("Moving a non-object to an object variable"); // call base Value::operator=(std::move(value)); diff --git a/include/throwable.h b/include/throwable.h index 1c2ef0ab..8b9adafa 100644 --- a/include/throwable.h +++ b/include/throwable.h @@ -1,13 +1,22 @@ /** - * Exception.h - * Implementation of Php Exceptions. + * Throwable.h + * + * Base class for exceptions and errors, where an error is a runtime + * problem with the program (like file-does-not-exist), and an error + * is a more fatal programming problem (like call-to-private-method). * * @author Jasper van Eck - * @copyright 2013, 2014 Copernica BV + * @author Emiel Bruijntjes + * @copyright 2013 - 2019 Copernica BV */ #include #include +/** + * Forward declarations + */ +struct _zend_object; + /** * Set up namespace */ @@ -16,114 +25,48 @@ namespace Php { /** * Class definition */ -class PHPCPP_EXPORT Exception : public std::exception +class PHPCPP_EXPORT Throwable : public std::runtime_error { protected: /** - * The exception message - * @var char* - */ - std::string _message; - -public: - /** - * Constructor - * - * @param message The exception message + * The exception code + * @var long int */ - Exception(std::string message) : _message(std::move(message)) {} - - /** - * Destructor - */ - virtual ~Exception() = default; - - /** - * Overridden what method - * @return const char * - */ - virtual const char *what() const _NOEXCEPT override - { - return _message.c_str(); - } - - /** - * Returns the message of the exception. - * @return &string - */ - const std::string &message() const throw() - { - return _message; - } + long int _code = -1; + +protected: /** - * Returns the exception code - * - * @note This only works if the exception was originally - * thrown in PHP userland. If the native() member - * function returns true, this function will not - * be able to correctly provide the filename. - * - * @return The exception code + * Protected constructor - only derived classes can instantiate + * @param message The exception message */ - virtual long int code() const _NOEXCEPT - { - return -1; - } - + Throwable(const std::string &message) : std::runtime_error(message) {} + /** - * Retrieve the filename the exception was thrown in - * - * @note This only works if the exception was originally - * thrown in PHP userland. If the native() member - * function returns true, this function will not - * be able to correctly provide the filename. - * - * @return The filename the exception was thrown in + * Another protected constructor + * @param object */ - virtual const std::string& file() const _NOEXCEPT - { - // we don't know the file the exception is from - static std::string file{ "" }; - - // return the missing filename - return file; - } + Throwable(struct _zend_object *object); +public: /** - * Retrieve the line at which the exception was thrown - * - * @note This only works if the exception was originally - * thrown in PHP userland. If the native() member - * function returns true, this function will not - * be able to correctly provide the line number. - * - * @return The line number the exception was thrown at + * Destructor */ - virtual long int line() const _NOEXCEPT - { - // we don't know the file the exception is from - return -1; - } - + virtual ~Throwable() = default; + /** - * Is this a native exception (one that was thrown from C++ code) - * @return bool + * Rethrow the exception / make sure that it ends up in PHP space */ - virtual bool native() const - { - // yes, it is native - return true; - } - + virtual void rethrow() = 0; + /** - * Report this error as a fatal error - * @return bool + * Returns the exception code + * @return The exception code */ - virtual bool report() const + long int code() const _NOEXCEPT { - // this is not done here - return false; + // expose the code + return _code; } }; diff --git a/include/value.h b/include/value.h index 030c4f10..8fe47c56 100644 --- a/include/value.h +++ b/include/value.h @@ -1223,7 +1223,7 @@ class PHPCPP_EXPORT Value : private HashParent * Friend functions which have to access that zval directly */ friend Value set_exception_handler(const std::function &handler); - friend Value set_error_handler(const std::function &handler, Error error); + friend Value set_error_handler(const std::function &handler, ErrorType error); }; /** diff --git a/include/zendcallable.h b/include/zendcallable.h index 4cb2acc8..192e0e41 100644 --- a/include/zendcallable.h +++ b/include/zendcallable.h @@ -5,7 +5,7 @@ * within PHP * * @author Martijn Otto - * @copyright 2016 Copernica B.V. + * @copyright 2016 - 2019 Copernica B.V. */ /** @@ -58,11 +58,10 @@ class PHPCPP_EXPORT ZendCallable static Parameters parameters(struct _zend_execute_data *execute_data); /** - * Handle exceptions - * - * @param exception The exception to handle + * Handle throwables + * @param throwable The object to handle */ - static void handle(Exception &exception); + static void handle(Throwable &throwable); /** * Yield (return) the given value diff --git a/phpcpp.h b/phpcpp.h index aff88ab0..7350ca76 100644 --- a/phpcpp.h +++ b/phpcpp.h @@ -35,8 +35,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/zend/callable.cpp b/zend/callable.cpp index 3b637e30..b8a4b6be 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -65,10 +65,10 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) // return a full copy of the zval, and do not destruct it RETVAL_ZVAL(result._val, 1, 0); } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception - process(exception); + // an exception was not caught by the extension, let it bubble up + throwable.rethrow(); } } } diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index f6986033..3847bd93 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -123,10 +123,10 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) // because of the two-step nature, we are going to report the error ourselves zend_error(E_ERROR, "Undefined method %s", name); } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); } } @@ -167,10 +167,10 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS) // because of the two-step nature, we are going to report the error ourselves zend_error(E_ERROR, "Function name must be a string"); } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); } } @@ -430,11 +430,10 @@ int ClassImpl::compare(zval *val1, zval *val2) // call default return std_object_handlers.compare_objects(val1, val2); } - catch (Exception &exception) + catch (Throwable &throwable) { - // a Php::Exception was thrown by the extension __compare function, - // pass this on to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // what shall we return here... return 1; @@ -492,10 +491,10 @@ int ClassImpl::cast(zval *val, zval *retval, int type) // call default return std_object_handlers.cast_object(val, retval, type); } - catch (Exception &exception) + catch (Throwable &throwable) { - // pass on the exception to php userspace - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // done return FAILURE; @@ -571,10 +570,10 @@ int ClassImpl::countElements(zval *object, zend_long *count) // done return SUCCESS; } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // unreachable return FAILURE; @@ -634,10 +633,10 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv) // ArrayAccess is implemented, call function return toZval(arrayaccess->offsetGet(offset), type, rv); } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception (send it to user space) - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // unreachable return Value(nullptr).detach(false); @@ -678,10 +677,10 @@ void ClassImpl::writeDimension(zval *object, zval *offset, zval *value) // set the value arrayaccess->offsetSet(offset, value); } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception (send it to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); } } else @@ -726,10 +725,10 @@ int ClassImpl::hasDimension(zval *object, zval *member, int check_empty) // the user wants to know if the property is empty return empty(arrayaccess->offsetGet(member)); } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception (send it to user space) - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // unreachable return false; @@ -768,10 +767,10 @@ void ClassImpl::unsetDimension(zval *object, zval *member) // remove the member arrayaccess->offsetUnset(member); } - catch (Exception &exception) + catch (Throwable &throwable) { - // process the exception (send it to user space) - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); } } else @@ -894,13 +893,12 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s // call default return std_object_handlers.read_property(object, name, type, cache_slot, rv); } - catch (Exception &exception) + catch (Throwable &throwable) { - // user threw an exception in its magic method - // implementation, send it to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); - // unreachable + // unreachable (or is it?) return Value(nullptr).detach(false); } } @@ -962,11 +960,10 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach // call the default std_object_handlers.write_property(object, name, value, cache_slot); } - catch (Exception &exception) + catch (Throwable &throwable) { - // user threw an exception in its magic method - // implementation, send it to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); } } @@ -1034,11 +1031,10 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void ** // call default return std_object_handlers.has_property(object, name, has_set_exists, cache_slot); } - catch (Exception &exception) + catch (Throwable &throwable) { - // user threw an exception in its magic method - // implementation, send it to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // unreachable return false; @@ -1086,11 +1082,10 @@ void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot) // call the default std_object_handlers.unset_property(object, member, cache_slot); } - catch (Exception &exception) + catch (Throwable &throwable) { - // user threw an exception in its magic method - // implementation, send it to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); } } @@ -1118,11 +1113,10 @@ void ClassImpl::destructObject(zend_object *object) // fallback on the default destructor call zend_objects_destroy_object(object); } - catch (Exception &exception) + catch (Throwable &throwable) { - // a regular Php::Exception was thrown by the extension, pass it on - // to PHP user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); } } @@ -1199,10 +1193,10 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje // done return wrapper->implementation(); } - catch (Exception &exception) + catch (Throwable &throwable) { - // user threw an exception in its method, send it to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // unreachable return nullptr; @@ -1234,11 +1228,10 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, *buffer = (unsigned char*)estrndup(value.c_str(), value.size()); *buf_len = value.size(); } - catch (Exception &exception) + catch (Throwable &throwable) { - // user threw an exception in its method - // implementation, send it to user space - process(exception); + // object was not caught by the extension, let it end up in user space + throwable.rethrow(); // unreachable return FAILURE; @@ -1270,12 +1263,12 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned // call the unserialize method on it serializable->unserialize((const char *)buffer, buf_len); } - catch (Exception &exception) + catch (Throwable &throwable) { // user threw an exception in its method // implementation, send it to user space - //process(exception); php_error_docref(NULL, E_NOTICE, "Error while unserializing"); + //throwable.rethrow(); // unreachable return FAILURE; diff --git a/zend/exception_handler.cpp b/zend/exception_handler.cpp index 79a5079e..4e52fbe3 100644 --- a/zend/exception_handler.cpp +++ b/zend/exception_handler.cpp @@ -44,7 +44,7 @@ Value set_exception_handler(const std::function &hand /** * Set a std::function as a php error handler */ -Value set_error_handler(const std::function &handler, Error error) +Value set_error_handler(const std::function &handler, ErrorType error) { // create the functor which wraps our callback Function functor(handler); @@ -69,7 +69,7 @@ Value set_error_handler(const std::function &handler, /** * Modify the error reporting level, will return the old error reporting level. */ -Value error_reporting(Error error) +Value error_reporting(ErrorType error) { // store the old error reporting value Value output(EG(error_reporting)); diff --git a/zend/fatalerror.cpp b/zend/fatalerror.cpp deleted file mode 100644 index e57be737..00000000 --- a/zend/fatalerror.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/** - * FatalError.cpp - * - * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV - */ -#include "includes.h" - -/** - * Set up namespace - */ -namespace Php { - -/** - * Report this error as a fatal error - * @return bool - */ -bool FatalError::report() const -{ - // report the error - zend_error(E_ERROR, "%s", what()); - - // return true: it was reported - return true; -} - -/** - * End of namespace - */ -} - diff --git a/zend/includes.h b/zend/includes.h index bd0e5f78..aa41f6e8 100644 --- a/zend/includes.h +++ b/zend/includes.h @@ -67,8 +67,9 @@ #include "../include/version.h" #include "../include/inivalue.h" #include "../include/ini.h" +#include "../include/throwable.h" #include "../include/exception.h" -#include "../include/fatalerror.h" +#include "../include/error.h" #include "../include/streams.h" #include "../include/type.h" #include "../include/errors.h" @@ -126,7 +127,6 @@ #include "stringmember.h" #include "floatmember.h" #include "arithmetic.h" -#include "origexception.h" #include "notimplemented.h" #include "property.h" #include "valueiteratorimpl.h" @@ -140,6 +140,8 @@ #include "extensionimpl.h" #include "compileroptions.h" #include "executestate.h" +#include "rethrowable.h" +#include "state.h" #include "opcodes.h" #include "functor.h" #include "constantimpl.h" diff --git a/zend/object.cpp b/zend/object.cpp index 11a0dd8d..9e656c1a 100644 --- a/zend/object.cpp +++ b/zend/object.cpp @@ -2,7 +2,7 @@ * Object.cpp * * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV + * @copyright 2014 - 2019 Copernica BV */ #include "includes.h" #include "string.h" @@ -30,12 +30,9 @@ Object::Object(const char *name, Base *base) : Value() { // this is a brand new object that should be allocated, the C++ instance // is already there (created by the extension) but it is not yet stored - // in PHP, find out the classname first (we use the FatalError class - // 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). + // in PHP, find out the classname first auto *entry = zend_fetch_class(String{ name }, ZEND_FETCH_CLASS_SILENT); - if (!entry) throw FatalError(std::string("Unknown class name ") + name); + if (!entry) throw Error(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 @@ -106,12 +103,9 @@ Object::Object(const Value &value) : Value() */ bool Object::instantiate(const char *name) { - // convert the name into a class_entry (we use the FatalError class - // 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). + // convert the name into a class_entry auto *entry = zend_fetch_class(String{ name }, ZEND_FETCH_CLASS_SILENT); - if (!entry) throw FatalError(std::string("Unknown class name ") + name); + if (!entry) throw Error(std::string("Unknown class name ") + name); // initiate the zval (which was already allocated in the base constructor) object_init_ex(_val, entry); diff --git a/zend/opcodes.h b/zend/opcodes.h index 8e811baa..6bcea12d 100644 --- a/zend/opcodes.h +++ b/zend/opcodes.h @@ -6,7 +6,7 @@ * Better use the Php::Script of Php::File classes. * * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV + * @copyright 2014 - 2019 Copernica BV */ /** @@ -81,15 +81,15 @@ class Opcodes EG(no_extensions) = 1; if (!EG(current_execute_data)->symbol_table) zend_rebuild_symbol_table(); - // the current exception - auto *oldException = EG(exception); + // the current exception state + State state; // execute the code zend_execute(_opcodes, &retval); // was an exception thrown inside the eval()'ed code? In that case we // throw a C++ new exception to give the C++ code the chance to catch it - if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception)); + state.rethrow(); // we're ready if there is no return value if (ZVAL_IS_NULL(&retval)) return nullptr; diff --git a/zend/origexception.h b/zend/origexception.h deleted file mode 100644 index 048520c2..00000000 --- a/zend/origexception.h +++ /dev/null @@ -1,199 +0,0 @@ -/** - * OrigException.h - * - * Class that wraps around an exception that was thrown by PHP code, - * and that could - but not necessarily has to - be caught by C++ - * - * @author Emiel Bruijntjes - * @copyright 2013 Copernica BV - */ - -/** - * Set up namespace - */ -namespace Php { - -/** - * Class definition - */ -class OrigException : public Exception -{ -private: - /** - * Is this a an exception that was caught by extension C++ code. - * - * When the object is initially created, we assume that it will be caught - * by C++ code. If it later turns out that the PHP-CPP can catch this - * exception after the extension C++ code ran, the variable is set back - * to false. - * - * @var bool - */ - bool _handled = true; - - /** - * The PHP exception code - * @var long int - */ - long int _code; - - /** - * PHP source file - * @var std::string - */ - std::string _file; - - /** - * PHP source line - * @var long int - */ - long int _line; - -public: - /** - * Constructor - * @param object The object that was thrown - */ - OrigException(zend_object *object) : Exception(std::string{ ZSTR_VAL(object->ce->name), ZSTR_LEN(object->ce->name) }) - { - // the result value from zend and the object zval - zval result, properties; - - // retrieve the object properties - ZVAL_OBJ(&properties, object); - - // retrieve the message, filename, error code and line number - auto message = zval_get_string(zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("message"), 1, &result)); - auto file = zval_get_string(zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("file" ), 1, &result)); - auto code = zval_get_long (zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("code" ), 1, &result)); - auto line = zval_get_long (zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("line" ), 1, &result)); - - // store the message, code, filename and line number - _message.assign(ZSTR_VAL(message), ZSTR_LEN(message)); - _code = code; - _file.assign(ZSTR_VAL(file), ZSTR_LEN(file)); - _line = line; - - // clean up message and file strings - zend_string_release(message); - zend_string_release(file); - } - - /** - * Copy constructor - * @param exception - */ - OrigException(const OrigException &exception) : - Exception("OrigException"), _handled(exception._handled) {} - - /** - * Move constructor - * @param exception - */ - OrigException(OrigException &&exception) : - Exception("OrigException"), _handled(exception._handled) - { - // set other exception to handled so that it wont do anything on destruction - exception._handled = true; - } - - /** - * Destructor - */ - virtual ~OrigException() throw() - { - // if the exception was not handled by C++ code, we're not going to do anything - // and the exception stays active - if (!_handled) return; - - // the exception was handled, so we should clean it up - zend_clear_exception(); - } - - /** - * This is _not_ a native exception, it was thrown by a PHP script - * @return bool - */ - virtual bool native() const override - { - return false; - } - - /** - * Reactivate the exception - */ - void reactivate() - { - // it was not handled by extension C++ code - _handled = false; - } - - /** - * Returns the exception code - * - * @note This only works if the exception was originally - * thrown in PHP userland. If the native() member - * function returns true, this function will not - * be able to correctly provide the filename. - * - * @return The exception code - */ - virtual long int code() const _NOEXCEPT override - { - // return the stored code - return _code; - } - - /** - * Retrieve the filename the exception was thrown in - * - * @return The filename the exception was thrown in - */ - virtual const std::string& file() const _NOEXCEPT override - { - // return the stored filename - return _file; - } - - /** - * Retrieve the line at which the exception was thrown - * - * @return The line number the exception was thrown at - */ - virtual long int line() const _NOEXCEPT override - { - // return the stored line number - return _line; - } -}; - -/** - * Global function to process an exception - * @param exception - */ -static inline void process(Exception &exception) -{ - // is this a native exception? - if (exception.native()) - { - // the exception is native, call the zend throw method - zend_throw_exception(zend_exception_get_default(), (char *)exception.what(), 0); - } - - // or does it have its own report function? - else if (!exception.report()) - { - // this is not a native exception, so it was originally thrown by a - // php script, and then not caught by the c++ of the extension, we are - // going to tell to the exception that it is still active - OrigException &orig = static_cast(exception); - - // reactive the exception - orig.reactivate(); - } -} - -/** - * End of namespace - */ -} diff --git a/zend/value.cpp b/zend/value.cpp index 6572dbee..ebc1468c 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -22,7 +22,7 @@ * * * @author Emiel Bruijntjes - * @copyright 2013, 2014 Copernica BV + * @copyright 2013 - 2019 Copernica BV */ #include "includes.h" #include "string.h" @@ -184,7 +184,7 @@ Value::Value(const Base *object) auto *impl = object->implementation(); // do we have a handle? - if (!impl) throw FatalError("Assigning an unassigned object to a variable"); + if (!impl) throw Error("Assigning an unassigned object to a variable"); // set it to an object and increase refcount ZVAL_OBJ(_val, impl->php()); @@ -761,15 +761,16 @@ static Value do_exec(const zval *object, zval *method, int argc, zval *argv) // the return zval zval retval; - // the current exception - zend_object *oldException = EG(exception); - + // remember current state of the PHP engine + State state; + // 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, argv, 1, nullptr) != SUCCESS) { // throw an exception, the function does not exist + // @todo this is not an exception but an error throw Exception("Invalid call to "+Value(method).stringValue()); // unreachable, but let's return at least something to prevent compiler warnings @@ -779,12 +780,16 @@ static Value do_exec(const zval *object, zval *method, int argc, zval *argv) { // 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)); + // @todo remove this + //if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception)); + + // rethrow the exception to the extension + state.rethrow(); // leap out if nothing was returned if (Z_ISUNDEF(retval)) return nullptr; - // wrap the retval in a value + // wrap the retval in a val Php::Value result(&retval); // destruct the retval (this just decrements the refcounter, which is ok, because @@ -1105,26 +1110,23 @@ Value &Value::setType(Type type) & // if this is not a reference variable, we should detach it to implement copy on write 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 + // run the conversion, when it fails we throw a fatal error that ends up in PHP space switch (type) { - 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 cannot be handled by the PHP-CPP library" }; break; - case Type::Constant: throw FatalError{ "Constant types cannot be assigned to a PHP-CPP library variable" }; break; - case Type::ConstantAST: throw FatalError{ "Constant types cannot be assigned to a PHP-CPP library variable" }; break; - case Type::Callable: throw FatalError{ "Callable types cannot 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; + case Type::Undefined: throw Error{ "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 Error{ "Resource types cannot be handled by the PHP-CPP library" }; break; + case Type::Constant: throw Error{ "Constant types cannot be assigned to a PHP-CPP library variable" }; break; + case Type::ConstantAST: throw Error{ "Constant types cannot be assigned to a PHP-CPP library variable" }; break; + case Type::Callable: throw Error{ "Callable types cannot be assigned to a PHP-CPP library variable" }; break; + case Type::Reference: throw Error{ "Reference types cannot be assigned to a PHP-CPP library variable" }; break; } // done diff --git a/zend/zendcallable.cpp b/zend/zendcallable.cpp index e2096255..c76a2600 100644 --- a/zend/zendcallable.cpp +++ b/zend/zendcallable.cpp @@ -70,14 +70,13 @@ Parameters ZendCallable::parameters(struct _zend_execute_data *execute_data) } /** - * Handle exceptions - * - * @param exception The exception to handle + * Handle throwables + * @param throwable The object to handle */ -void ZendCallable::handle(Exception &exception) +void ZendCallable::handle(Throwable &throwable) { - // pass it on to the exception handler - process(exception); + // pass to user space + throwable.rethrow(); } /** From eac457cee281a55c3f203577c05650caef1b02a4 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Thu, 21 Mar 2019 14:22:28 +0100 Subject: [PATCH 025/101] added a couple of files that were missing from the previos commit --- include/error.h | 59 ++++++++++++++++++ include/exception.h | 55 +++++++++++++++++ zend/error.cpp | 33 ++++++++++ zend/exception.cpp | 33 ++++++++++ zend/rethrowable.h | 145 ++++++++++++++++++++++++++++++++++++++++++++ zend/state.h | 106 ++++++++++++++++++++++++++++++++ zend/throwable.cpp | 65 ++++++++++++++++++++ 7 files changed, 496 insertions(+) create mode 100644 include/error.h create mode 100644 include/exception.h create mode 100644 zend/error.cpp create mode 100644 zend/exception.cpp create mode 100644 zend/rethrowable.h create mode 100644 zend/state.h create mode 100644 zend/throwable.cpp diff --git a/include/error.h b/include/error.h new file mode 100644 index 00000000..e762a699 --- /dev/null +++ b/include/error.h @@ -0,0 +1,59 @@ +/** + * Error.h + * + * Class for fatal programming errors. You should normally not catch + * these exceptions, but let them bubble up to crash your script, but + * if you insist, you can. + * + * In your C++ extension you can both throw and catch instances of these + * class. If you throw an exception, it will bubble up to PHP space, + * where the PHP script can then catch it. + * + * Error thrown from PHP space can also be caught in C++ code. + * If you put your call to a PHP user space function inside a try-catch + * block, you can catch the exceptions as Php::Error objects. + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Class definition + */ +class PHPCPP_EXPORT Error : public Throwable +{ +protected: + /** + * Internal constructor to wrap around an exception object + * @param object + */ + Error(struct _zend_object *object) : Throwable(object) {} + + /** + * Rethrow the exception / make sure that it ends up in PHP space + */ + virtual void rethrow() override; + +public: + /** + * Public constructor + * @param message + */ + Error(const std::string &message) : Throwable(message) {} + + /** + * Destructor + */ + virtual ~Error() = default; +}; + +/** + * End of namespace + */ +} + diff --git a/include/exception.h b/include/exception.h new file mode 100644 index 00000000..4a08be72 --- /dev/null +++ b/include/exception.h @@ -0,0 +1,55 @@ +/** + * Exception.h + * + * Class for runtime errors. In your C++ extension you can both throw + * and catch instances of these class. If you throw an exception, it + * will bubble up to PHP space, where the PHP script can then catch it. + * + * Exceptions called from PHP space can also be caught in C++ code. + * If you put your call to a PHP user space function inside a try-catch + * block, you can catch the exceptions as Php::Exception objects. + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Class definition + */ +class PHPCPP_EXPORT Exception : public Throwable +{ +protected: + /** + * Internal constructor to wrap around an exception object + * @param object + */ + Exception(struct _zend_object *object) : Throwable(object) {} + + /** + * Rethrow the exception / make sure that it ends up in PHP space + */ + virtual void rethrow() override; + +public: + /** + * Public constructor + * @param message + */ + Exception(const std::string &message) : Throwable(message) {} + + /** + * Destructor + */ + virtual ~Exception() = default; +}; + +/** + * End of namespace + */ +} + diff --git a/zend/error.cpp b/zend/error.cpp new file mode 100644 index 00000000..136d7f74 --- /dev/null +++ b/zend/error.cpp @@ -0,0 +1,33 @@ +/** + * Error.cpp + * + * Implementation file for the Error class + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Dependencies + */ +#include "includes.h" + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Rethrow the exception / make sure that it ends up in PHP space + */ +void Error::rethrow() +{ + // add the exception to userspace + zend_throw_exception(zend_ce_error, what(), _code); +} + +/** + * End of namespace + */ +} + diff --git a/zend/exception.cpp b/zend/exception.cpp new file mode 100644 index 00000000..924d56bd --- /dev/null +++ b/zend/exception.cpp @@ -0,0 +1,33 @@ +/** + * Exception.cpp + * + * Implementation file for the Exception class + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Dependencies + */ +#include "includes.h" + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Rethrow the exception / make sure that it ends up in PHP space + */ +void Exception::rethrow() +{ + // add the exception to userspace + zend_throw_exception(zend_ce_exception, what(), _code); +} + +/** + * End of namespace + */ +} + diff --git a/zend/rethrowable.h b/zend/rethrowable.h new file mode 100644 index 00000000..286be535 --- /dev/null +++ b/zend/rethrowable.h @@ -0,0 +1,145 @@ +/** + * Rethrowable.h + * + * Class that we use to wrap user space exceptions in, so that we can + * monitor whether they were handled by the extension or not. + * + * How does this work? When the C++ extension makes a call to PHP + * user-space, it is possible that this call throws an error or an + * exception: + * + * // make a call to the user-space function + * Php::call("my_userspace_function"); + * + * There are two options: the extension then catches this exception, or + * the exception is not caught by the extension. If the exception is + * handled by the extension we should tell the zend-engine that the + * exception was handled, otherwise we tell nothing to the zend-engine + * (which causes the exception to bubble up). + * + * This Rethrowable class is used to wrap the exceptions. It is supposed + * to end up in the extension. If the extension catches it, it is nicely + * destructed. But if it is not caught by the extension, it will end up + * in PHP-CPP code, where we can tell that it should not nicely destruct, + * but end up in PHP space anyway. + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Base class for WrappedException and WrappedError + */ +class Rethrowable +{ +protected: + /** + * Should the object be rethrown? + * We assume this to be false, if the object ends up in PHP-CPP space + * again (and the exception did not catch it) we reset this property. + * @var bool + */ + bool _rethrow = false; + +protected: + /** + * Protected constructor (only ThrowableException and ThrowableError should be extended) + */ + Rethrowable() {} + +public: + /** + * Copy constructor + * @param that + */ + Rethrowable(const Rethrowable &that) : _rethrow(that._rethrow) {} + + /** + * Move constructor + * @param that + */ + Rethrowable(Rethrowable &&that) : _rethrow(that._rethrow) + { + // reset other object + that._rethrow = false; + } + + /** + * Destructor + */ + virtual ~Rethrowable() + { + // if we have to rethrow, we keep it on the stack + if (_rethrow) return; + + // tell the zend engine to remove it from the stack + zend_clear_exception(); + } +}; + +/** + * Wrapper around the Php::Exception class + */ +class RethrowableException : public Exception, public Rethrowable +{ +public: + /** + * Constructor + * @param object + */ + RethrowableException(zend_object *object) : Exception(object) {} + + /** + * Destructor + */ + virtual ~RethrowableException() = default; + + /** + * Rethrow the exception + */ + virtual void rethrow() override + { + // update member + _rethrow = true; + } +}; + +/** + * Wrapper around the Php::Error class + */ +class RethrowableError : public Error, public Rethrowable +{ +public: + /** + * Constructor + * @param object + */ + RethrowableError(zend_object *object) : Error(object) {} + + /** + * Destructor + */ + virtual ~RethrowableError() = default; + + /** + * Rethrow the exception + */ + virtual void rethrow() override + { + // update member + _rethrow = true; + } +}; + +/** + * End of namespace + */ +} + + + diff --git a/zend/state.h b/zend/state.h new file mode 100644 index 00000000..36223f5f --- /dev/null +++ b/zend/state.h @@ -0,0 +1,106 @@ +/** + * State.h + * + * Class that is used to store the state of current callstack and + * whether an exception is now active. We use this before we make + * a call to PHP userspace to see if the state has changed afterwards, + * which means that an exception has bubbled up that should be handled + * first. + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Class definition + */ +class State +{ +private: + /** + * The current exception that was active when state was registered + * @var zend_object + */ + zend_object *_exception; + + /** + * Helper method to check if a zend-class is an instance of a certain class + * @param entry the class entry to check + * @param required the required class entry + * @return bool + */ + static bool instanceof(const zend_class_entry *entry, const zend_class_entry *required) + { + // check the class name + if (entry == required) return true; + + // is there a base class? + if (entry->parent == nullptr) return false; + + // check the parent + return instanceof(entry->parent, required); + } + + /** + * Helper method to check if a zend-object is an instance of a certain class + * @param object the object to check + * @param required required class entry + * @return bool + */ + static bool instanceof(const zend_object *object, zend_class_entry *required) + { + return instanceof(object->ce, required); + } + + +public: + /** + * Constructor + */ + State() : _exception(EG(exception)) {} + + /** + * Destructor + */ + virtual ~State() = default; + + /** + * Rethrow the exception so that it ends up in the extension + * + * When this method returns, it means that no exception occured, + * and the call to PHP space was a success. Otherwise it throws + * an exception. Note that this is not exactly in line with our + * coding convention that says that methods are not supposed to + * throw exceptions. + * + * @throw Throwable + */ + void rethrow() + { + // is an exception now active + zend_object *current = EG(exception); + + // if no exception is active + if (current == nullptr) return; + + // or if the exception did not change + if (current == _exception) return; + + // an exception occured, this can be a PHP error or a PHP exception + if (instanceof(current, zend_ce_error)) throw RethrowableError(current); + + // otherwise we wrap the exception + throw RethrowableException(current); + } +}; + +/** + * End of namespace + */ +} + diff --git a/zend/throwable.cpp b/zend/throwable.cpp new file mode 100644 index 00000000..c398d568 --- /dev/null +++ b/zend/throwable.cpp @@ -0,0 +1,65 @@ +/** + * Throwable.cpp + * + * Implementation file for the Throwable class + * + * @author Emiel Bruijntjes + * @copyright 2019 Copernica BV + */ + +/** + * Dependencies + */ +#include "includes.h" + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Helper function converts an object to a string + * @return std::string + */ +static std::string convert(zend_object *object) +{ + // the result value from zend and the object zval + zval tmp, properties; + + // retrieve the object properties + ZVAL_OBJ(&properties, object); + + // retrieve the message, filename, error code and line number + auto message = zval_get_string(zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("message"), 1, &tmp)); + + // copy message to a string + std::string result(ZSTR_VAL(message), ZSTR_LEN(message)); + + // clean up message string + zend_string_release(message); + + // done + return result; +} + +/** + * Another protected constructor + * @param object + */ +Throwable::Throwable(zend_object *object) : std::runtime_error(convert(object)) +{ + // the result value from zend and the object zval + zval result, properties; + + // retrieve the object properties + ZVAL_OBJ(&properties, object); + + // retrieve the error code + _code = zval_get_long(zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("code"), 1, &result)); +} + +/** + * End of namespace + */ +} + From 623e2565aa02f8c5c6d0194c47234f23cfb607eb Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Thu, 21 Mar 2019 17:04:28 +0100 Subject: [PATCH 026/101] - fixed exception handling for class methods and functions (uncaught Error objects caused a full crash, now they cause a fatal error) - when calling an invalid function we no longer throw an exception, but an error --- include/zendcallable.h | 52 +++++++++++++++++++++--------------------- zend/callable.cpp | 2 +- zend/value.cpp | 11 +++------ 3 files changed, 30 insertions(+), 35 deletions(-) diff --git a/include/zendcallable.h b/include/zendcallable.h index 192e0e41..f6ddbc86 100644 --- a/include/zendcallable.h +++ b/include/zendcallable.h @@ -86,14 +86,14 @@ class PHPCPP_EXPORT ZendCallable { // cast the base to the correct object and invoke the member (static_cast(instance(execute_data))->*callback)(); - + // there is no return value, so we just return null yield(return_value, nullptr); } - catch (Exception &exception) + catch (Throwable &throwable) { - // handle the exception - handle(exception); + // handle the throwable + handle(throwable); } } @@ -115,10 +115,10 @@ class PHPCPP_EXPORT ZendCallable // there is no return value, so we just return null yield(return_value, nullptr); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -140,10 +140,10 @@ class PHPCPP_EXPORT ZendCallable // store the return value in the return_value yield(return_value, result); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -165,10 +165,10 @@ class PHPCPP_EXPORT ZendCallable // store the return value in the return_value yield(return_value, result); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -196,10 +196,10 @@ class PHPCPP_EXPORT ZendCallable // there is no return value, so we just reutrn null yield(return_value, nullptr); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -227,10 +227,10 @@ class PHPCPP_EXPORT ZendCallable // there is no return value, so we just return null yield(return_value, nullptr); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -258,10 +258,10 @@ class PHPCPP_EXPORT ZendCallable // store the return value in the return_value yield(return_value, result); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -289,10 +289,10 @@ class PHPCPP_EXPORT ZendCallable // store the return value in the return_value yield(return_value, result); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -314,10 +314,10 @@ class PHPCPP_EXPORT ZendCallable // there is no return value, so we just return null yield(return_value, nullptr); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -339,10 +339,10 @@ class PHPCPP_EXPORT ZendCallable // store the return value in the return_value yield(return_value, result); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -370,10 +370,10 @@ class PHPCPP_EXPORT ZendCallable // there is no return value, so we just return null yield(return_value, nullptr); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } @@ -401,10 +401,10 @@ class PHPCPP_EXPORT ZendCallable // store the return value in the return_value yield(return_value, result); } - catch (Exception &exception) + catch (Throwable &throwable) { // handle the exception - handle(exception); + handle(throwable); } } }; diff --git a/zend/callable.cpp b/zend/callable.cpp index b8a4b6be..98c98140 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -61,7 +61,7 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) { // get the result Value result(callable->invoke(params)); - + // return a full copy of the zval, and do not destruct it RETVAL_ZVAL(result._val, 1, 0); } diff --git a/zend/value.cpp b/zend/value.cpp index ebc1468c..5f43dcc1 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -770,20 +770,15 @@ static Value do_exec(const zval *object, zval *method, int argc, zval *argv) if (call_user_function_ex(CG(function_table), (zval*) object, method, &retval, argc, argv, 1, nullptr) != SUCCESS) { // throw an exception, the function does not exist - // @todo this is not an exception but an error - throw Exception("Invalid call to "+Value(method).stringValue()); + throw Error("Invalid call to "+Value(method).stringValue()); // unreachable, but let's return at least something to prevent compiler warnings return nullptr; } else { - // 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 - // @todo remove this - //if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception)); - - // rethrow the exception to the extension + // the state object checks if a new exception is added to the stack, which means + // that an exception or error occured during the call to php space state.rethrow(); // leap out if nothing was returned From e68910e7a7605dc1d604e6caa2115978a850ffd4 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 22 Mar 2019 07:37:56 +0100 Subject: [PATCH 027/101] renamed ErrorType to Message --- include/call.h | 2 +- include/{errors.h => message.h} | 8 ++++---- include/value.h | 2 +- phpcpp.h | 2 +- zend/exception_handler.cpp | 8 ++++---- zend/includes.h | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) rename include/{errors.h => message.h} (87%) diff --git a/include/call.h b/include/call.h index 3a3242b0..16a200c7 100644 --- a/include/call.h +++ b/include/call.h @@ -47,7 +47,7 @@ static inline Value require(const std::string &filename) { return requ extern PHPCPP_EXPORT Value require_once(const char *filename); static inline Value require_once(const std::string &filename) { return require_once(filename.c_str()); } extern PHPCPP_EXPORT Value set_exception_handler(const std::function &handler); -extern PHPCPP_EXPORT Value set_error_handler(const std::function &handler, ErrorType error = ErrorType::All); +extern PHPCPP_EXPORT Value set_error_handler(const std::function &handler, Message message = Message::All); extern PHPCPP_EXPORT Value error_reporting(Error error); extern PHPCPP_EXPORT const char *sapi_name(); diff --git a/include/errors.h b/include/message.h similarity index 87% rename from include/errors.h rename to include/message.h index 1eaf4925..54d92a58 100644 --- a/include/errors.h +++ b/include/message.h @@ -1,10 +1,10 @@ /** - * Errors.h + * Message.h * - * In this file an enumeration type is defined with all error flags. + * In this file an enumeration type is defined with all error-message flags. * * @author Toon Schoenmakers - * @copyright 2015 Copernica BV + * @copyright 2015 - 2019 Copernica BV */ /** @@ -15,7 +15,7 @@ namespace Php { /** * Supported types of errors, this is mostly a copy from Zend/zend_errors.h */ -enum class ErrorType : int { +enum class Message : int { Error = (1 << 0L), Warning = (1 << 1L), Parse = (1 << 2L), diff --git a/include/value.h b/include/value.h index 8fe47c56..45f7464e 100644 --- a/include/value.h +++ b/include/value.h @@ -1223,7 +1223,7 @@ class PHPCPP_EXPORT Value : private HashParent * Friend functions which have to access that zval directly */ friend Value set_exception_handler(const std::function &handler); - friend Value set_error_handler(const std::function &handler, ErrorType error); + friend Value set_error_handler(const std::function &handler, Message message); }; /** diff --git a/phpcpp.h b/phpcpp.h index 7350ca76..1b0da150 100644 --- a/phpcpp.h +++ b/phpcpp.h @@ -39,7 +39,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/zend/exception_handler.cpp b/zend/exception_handler.cpp index 4e52fbe3..19191df6 100644 --- a/zend/exception_handler.cpp +++ b/zend/exception_handler.cpp @@ -44,7 +44,7 @@ Value set_exception_handler(const std::function &hand /** * Set a std::function as a php error handler */ -Value set_error_handler(const std::function &handler, ErrorType error) +Value set_error_handler(const std::function &handler, Message message) { // create the functor which wraps our callback Function functor(handler); @@ -60,7 +60,7 @@ Value set_error_handler(const std::function &handler, // copy our zval into the user_error_handler ZVAL_COPY_VALUE(&EG(user_error_handler), value); - EG(user_error_handler_error_reporting) = (int) error; + EG(user_error_handler_error_reporting) = (int) message; // return the original handler return output; @@ -69,7 +69,7 @@ Value set_error_handler(const std::function &handler, /** * Modify the error reporting level, will return the old error reporting level. */ -Value error_reporting(ErrorType error) +Value error_reporting(Message message) { // store the old error reporting value Value output(EG(error_reporting)); @@ -78,7 +78,7 @@ Value error_reporting(ErrorType error) char str[21]; // write the level into this buffer - int size = sprintf(str, "%d", (int) error); + int size = sprintf(str, "%d", (int) message); // if we failed for some reason we bail out if (size < 0) return false; diff --git a/zend/includes.h b/zend/includes.h index aa41f6e8..fd914611 100644 --- a/zend/includes.h +++ b/zend/includes.h @@ -72,7 +72,7 @@ #include "../include/error.h" #include "../include/streams.h" #include "../include/type.h" -#include "../include/errors.h" +#include "../include/message.h" #include "../include/hashparent.h" #include "../include/value.h" #include "../include/valueiterator.h" From b50838c0bc5a3eae8b65f7d969172def30fd2ea9 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 22 Mar 2019 07:53:56 +0100 Subject: [PATCH 028/101] fixed forgotten rename from Error to Message --- include/call.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/call.h b/include/call.h index 16a200c7..6e0229fd 100644 --- a/include/call.h +++ b/include/call.h @@ -48,7 +48,7 @@ extern PHPCPP_EXPORT Value require_once(const char *filename); static inline Value require_once(const std::string &filename) { return require_once(filename.c_str()); } extern PHPCPP_EXPORT Value set_exception_handler(const std::function &handler); extern PHPCPP_EXPORT Value set_error_handler(const std::function &handler, Message message = Message::All); -extern PHPCPP_EXPORT Value error_reporting(Error error); +extern PHPCPP_EXPORT Value error_reporting(Message message); extern PHPCPP_EXPORT const char *sapi_name(); /** From dfe4a94ee0dd1b240c6db3b324436c2138f7307c Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 22 Mar 2019 15:39:07 +0100 Subject: [PATCH 029/101] closures now get an empty string as name because exception handling functions sometimes need access to the name --- zend/classimpl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 3847bd93..d7b9ba02 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -304,7 +304,7 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct function->arg_flags[1] = 0; function->arg_flags[2] = 0; function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; - function->function_name = nullptr; + function->function_name = zend_empty_string; // should not be null, as this is free'ed by zend when doing exception handling function->scope = *entry_ptr; function->prototype = nullptr; function->num_args = 0; From 5a268f5104385389d9e3c90bcbaaa9564a2adced Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 26 Mar 2019 09:50:08 +0100 Subject: [PATCH 030/101] fixed issue when dealing with optional object parameters --- include/argument.h | 31 +++++++++++++++++++++++++++---- zend/callable.h | 2 +- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/include/argument.h b/include/argument.h index f7fb7822..95d9bf71 100644 --- a/include/argument.h +++ b/include/argument.h @@ -8,7 +8,7 @@ * classes instead. * * @author Emiel Bruijntjes - * @copyright 2013, 2014 Copernica BV + * @copyright 2013 - 2019 Copernica BV */ /** @@ -25,7 +25,11 @@ class PHPCPP_EXPORT Argument /** * Destructor */ - virtual ~Argument() = default; + virtual ~Argument() + { + // deallocate data if needed (we should not do this, memory buffer must be preserved for the duration of the program) + // if (_nullable) delete[] _classname; + } protected: /** @@ -47,9 +51,19 @@ class PHPCPP_EXPORT Argument * @param byref Is this a reference argument? */ Argument(const char *name, const char *classname, bool nullable = true, bool required = true, bool byref = false) : - _name(name), _type(Type::Object), _classname(classname), _nullable(nullable), _required(required), _byReference(byref) {} + _name(name), _type(Type::Object), _classname(classname), _nullable(nullable), _required(required), _byReference(byref) + { + // for nullable classes, zend uses a "?name" encoding, so we need some extra space + if (!_nullable) return; + + // allocate extra space + _classname = new char[::strlen(classname) + 2]; + + // copy the data + strcpy((char *)_classname, "?"); + strcpy((char *)_classname + 1, classname); + } -public: /** * Is this a required argument? * @return bool @@ -83,6 +97,15 @@ class PHPCPP_EXPORT Argument * @return const char * */ const char *classname() const + { + return _nullable ? _classname + 1 : _classname; + } + + /** + * The internal classname, with the encoding whether it is nullable + * @return const char * + */ + const char *encoded() const { return _classname; } diff --git a/zend/callable.h b/zend/callable.h index 14bda2fe..95f8e4ed 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -210,7 +210,7 @@ class Callable case Type::Array: info->type = ZEND_TYPE_ENCODE(IS_ARRAY, arg.allowNull()); break; // array of anything (individual members cannot be restricted) case Type::Object: // if there is a classname and the argument is not nullable, it's simply the classname if (!arg.classname()) info->type = ZEND_TYPE_ENCODE(IS_OBJECT, arg.allowNull()); - else info->type = ZEND_TYPE_ENCODE_CLASS(arg.classname(), arg.allowNull()); + else info->type = (zend_type)arg.encoded(); break; case Type::Callable: info->type = ZEND_TYPE_ENCODE(IS_CALLABLE, arg.allowNull()); break; // anything that can be invoked default: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // if not specified we allow anything From ff20f0dbf56b3ffe23fbc6493dba87d755b34d29 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 26 Mar 2019 09:53:24 +0100 Subject: [PATCH 031/101] {auto} removed too much code --- include/argument.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/argument.h b/include/argument.h index 95d9bf71..c312cfa0 100644 --- a/include/argument.h +++ b/include/argument.h @@ -64,6 +64,7 @@ class PHPCPP_EXPORT Argument strcpy((char *)_classname + 1, classname); } +public: /** * Is this a required argument? * @return bool From 9930b431e5a29fafcc564cf4419c89e922444e93 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Fri, 29 Mar 2019 13:53:04 +0100 Subject: [PATCH 032/101] add php 7.3 in the build matrix --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9b937ff7..af5a58f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,8 @@ php: - 7.0 - 7.1 - 7.2 - - nightly # doesn't work yet on PHP 7.3! not building. + - 7.3 + - nightly # doesn't work yet on PHP 7.4! not building. # setting the env is the easiest, because it will mix with all the separate php versions (travis does this) From d4ef49aac412be3d3844ba338767d91ff54229de Mon Sep 17 00:00:00 2001 From: Rafal Goslawski Date: Fri, 5 Apr 2019 12:43:10 +0200 Subject: [PATCH 033/101] bump version to 2.2.0 --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index be810e6b..cbd04ce4 100644 --- a/Makefile +++ b/Makefile @@ -49,8 +49,8 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # Otherwise only release verions changes. (version is MAJOR.MINOR.RELEASE) # -SONAME = 2.1 -VERSION = 2.1.4 +SONAME = 2.2 +VERSION = 2.2.0 # From 2373ef4b1f52dff8d863f691b93306eba9cd04cd Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 19 Apr 2019 17:34:15 +0200 Subject: [PATCH 034/101] Fixed "$object instanceof Traversable" and "$object instanceof Serializable" when the Php::Serializable ad Php::Traversable interfaces are implemented in C++ space --- zend/classimpl.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index d7b9ba02..2228eb81 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1406,6 +1406,10 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // otherwise report an error else std::cerr << "Derived class " << name() << " is initialized before base class " << interface->name() << ": interface is ignored" << std::endl; } + + // we may have to expose the Traversable or Serializable interfaces + if (_base->traversable()) zend_class_implements(_entry, 1, zend_ce_traversable); + if (_base->serializable()) zend_class_implements(_entry, 1, zend_ce_serializable); // this pointer has to be copied to temporary pointer, as &this causes compiler error ClassImpl *impl = this; From 6444ea28cafbcb6a8a188ce409e34a0686855049 Mon Sep 17 00:00:00 2001 From: Eden Reich Date: Mon, 9 Sep 2019 19:37:10 +0200 Subject: [PATCH 035/101] commentout missing files and rename errors.h to error.h --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d05d87fb..be97069f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -122,7 +122,7 @@ SET(PHPCPP_ZEND_SOURCES zend/exists.cpp zend/extension.cpp zend/extensionimpl.cpp - zend/fatalerror.cpp + # zend/fatalerror.cpp zend/file.cpp zend/function.cpp zend/functor.cpp @@ -175,7 +175,7 @@ SET(PHPCPP_ZEND_HEADERS zend/numericmember.h zend/objectimpl.h zend/opcodes.h - zend/origexception.h + # zend/origexception.h zend/parametersimpl.h zend/property.h zend/string.h @@ -200,10 +200,10 @@ SET(PHPCPP_HEADERS_INCLUDE include/constant.h include/countable.h include/deprecated.h - include/errors.h + include/error.h include/exception.h include/extension.h - include/fatalerror.h + # include/fatalerror.h include/file.h include/function.h include/global.h From da21e3a0bc5afdfeea1cbfe44b16472fc69e135b Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Thu, 12 Sep 2019 09:59:21 -0600 Subject: [PATCH 036/101] Add support for php7.4 --- Makefile | 4 ++-- common/modifiers.cpp | 11 +++++++++++ zend/classimpl.cpp | 34 +++++++++++++++++++++++++++++----- zend/classimpl.h | 10 ++++++++-- zend/value.cpp | 5 ++++- 5 files changed, 54 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index cbd04ce4..6ecab3fd 100644 --- a/Makefile +++ b/Makefile @@ -210,10 +210,10 @@ clean: find -name *.o | xargs ${RM} ${COMMON_SHARED_OBJECTS}: - ${COMPILER} ${COMPILER_FLAGS} ${SHARED_COMPILER_FLAGS} -o $@ ${@:shared/%.o=%.cpp} + ${COMPILER} ${PHP_COMPILER_FLAGS} ${SHARED_COMPILER_FLAGS} -o $@ ${@:shared/%.o=%.cpp} ${COMMON_STATIC_OBJECTS}: - ${COMPILER} ${COMPILER_FLAGS} ${STATIC_COMPILER_FLAGS} -o $@ ${@:static/%.o=%.cpp} + ${COMPILER} ${PHP_COMPILER_FLAGS} ${STATIC_COMPILER_FLAGS} -o $@ ${@:static/%.o=%.cpp} ${PHP_SHARED_OBJECTS}: ${COMPILER} ${PHP_COMPILER_FLAGS} ${SHARED_COMPILER_FLAGS} -o $@ ${@:shared/%.o=%.cpp} diff --git a/common/modifiers.cpp b/common/modifiers.cpp index 95b60c3c..c40066c5 100644 --- a/common/modifiers.cpp +++ b/common/modifiers.cpp @@ -10,6 +10,7 @@ * @copyright 2014 Copernica BV */ #include "includes.h" +#include /** * Set up namespace @@ -19,6 +20,15 @@ namespace Php { /** * The modifiers are constants */ +#if PHP_VERSION_ID >= 70400 +const int Static = 0x10; +const int Abstract = 0x40; +const int Final = 0x20; +const int Public = 0x01; +const int Protected = 0x02; +const int Private = 0x04; +const int Const = 0; +#else const int Static = 0x01; const int Abstract = 0x02; const int Final = 0x04; @@ -26,6 +36,7 @@ const int Public = 0x100; const int Protected = 0x200; const int Private = 0x400; const int Const = 0; +#endif /** * Modifiers that are supported for methods and properties diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 2228eb81..6ff96c02 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -527,7 +527,7 @@ zend_object *ClassImpl::cloneObject(zval *val) // a copy constructor). Because this function is directly called from the // Zend engine, we can call zend_error() (which does a longjmp()) to throw // an exception back to the Zend engine) - if (!cpp) zend_error(E_ERROR, "Unable to clone %s", entry->name); + if (!cpp) zend_error(E_ERROR, "Unable to clone %s", entry->name->val); // store the object auto *new_object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1); @@ -915,7 +915,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s * @param cache_slot The cache slot used * @return zval */ -void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cache_slot) +PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cache_slot) { // retrieve the object and class Base *base = ObjectImpl::find(object)->object(); @@ -946,7 +946,13 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach else { // check if it could be set - if (iter->second->set(base, value)) return; + if (iter->second->set(base, value)) { +#if PHP_VERSION_ID >= 70400 + return value; +#else + return; +#endif + } // read-only property zend_error(E_ERROR, "Unable to write to read-only property %s", (const char *)key); @@ -955,16 +961,30 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cach catch (const NotImplemented &exception) { // __set() function was not overridden by user, check if there is a default - if (!std_object_handlers.write_property) return; + if (!std_object_handlers.write_property) { +#if PHP_VERSION_ID >= 70400 + return value; +#else + return; +#endif + } // call the default std_object_handlers.write_property(object, name, value, cache_slot); +#if PHP_VERSION_ID >= 70400 + return value; +#else + return; +#endif } catch (Throwable &throwable) { // object was not caught by the extension, let it end up in user space throwable.rethrow(); } +#if PHP_VERSION_ID >= 70400 + return value; +#endif } /** @@ -1150,7 +1170,7 @@ zend_object *ClassImpl::createObject(zend_class_entry *entry) // report error on failure, because this function is called directly from the // Zend engine, we can call zend_error() here (which does a longjmp() back to // the Zend engine) - if (!cpp) zend_error(E_ERROR, "Unable to instantiate %s", entry->name); + if (!cpp) zend_error(E_ERROR, "Unable to instantiate %s", entry->name->val); // create the object in the zend engine auto *object = new ObjectImpl(entry, cpp, impl->objectHandlers(), 1); @@ -1428,7 +1448,11 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref _entry->info.user.doc_comment = _self; // set access types flags for class +#if PHP_VERSION_ID >= 70400 + _entry->ce_flags |= (int)_type; +#else _entry->ce_flags = (int)_type; +#endif // declare all member variables for (auto &member : _members) member->initialize(_entry); diff --git a/zend/classimpl.h b/zend/classimpl.h index e9c4ef89..225c3ad7 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -13,6 +13,12 @@ */ namespace Php { +#if PHP_VERSION_ID >= 70400 +# define PHP_WRITE_PROP_HANDLER_TYPE zval * +#else +# define PHP_WRITE_PROP_HANDLER_TYPE void +#endif + /** * Class definition */ @@ -257,9 +263,9 @@ class ClassImpl * @param name The name of the property * @param value The new value * @param cache_slot The cache slot used - * @return zval + * @return zval* */ - static void writeProperty(zval *object, zval *name, zval *value, void **cache_slot); + static PHP_WRITE_PROP_HANDLER_TYPE writeProperty(zval *object, zval *name, zval *value, void **cache_slot); /** * Function that is called to check whether a certain property is set diff --git a/zend/value.cpp b/zend/value.cpp index 5f43dcc1..6ded9556 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1499,9 +1499,12 @@ bool Value::contains(const char *key, int size) const } else if (isObject()) { +#if PHP_VERSION_ID >= 70400 // retrieve the object pointer and check whether the property we are trying to retrieve + if (zend_check_property_access(Z_OBJ_P(_val), String(key, size), 0) == FAILURE) return false; +#else if (zend_check_property_access(Z_OBJ_P(_val), String(key, size)) == FAILURE) return false; - +#endif // check if the 'has_property' method is available for this object auto *has_property = Z_OBJ_HT_P(_val)->has_property; From 473c1b50597988762ad5f3f3c6004e9cf1550c47 Mon Sep 17 00:00:00 2001 From: Ruhollah DamavandiKamali Date: Mon, 28 Sep 2020 12:19:18 +0330 Subject: [PATCH 037/101] The function call was deprecated Update README.md, Fixed. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 007de074..6171477f 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Php::Value my_plus(Php::Parameters ¶ms) The method call to export the above C++ function: ```c -extension.add("my_plus", my_plus, { +extension.add("my_plus", { Php::ByVal("a", Php::numericType), Php::ByVal("b", Php::numericType) }); From 3db7eed7a9173dd158c477ad2eea0cea47c6c3d1 Mon Sep 17 00:00:00 2001 From: Raoul Wols Date: Wed, 4 Aug 2021 10:35:08 +0200 Subject: [PATCH 038/101] Improve performance of destructing objects We used to throw a NotImplemented exception, but this causes the c++ runtime to allocate on the heap. We can call zend_objects_destroy_object directly in the Php::Base::__destroy method instead. If someone overrides this method, then zend_objects_destroy_object will not be called, which is the behavior we had before as well. --- zend/base.cpp | 5 ++--- zend/classimpl.cpp | 4 +++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/zend/base.cpp b/zend/base.cpp index 4cb3cd85..ed6fe3bc 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -17,9 +17,8 @@ namespace Php { */ void Base::__destruct() const { - // throw exception, so that the PHP-CPP library will check if the user - // somehow registered an explicit __destruct method - throw NotImplemented(); + // destroy the object by default + zend_objects_destroy_object(_impl->php()); } /** diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 6ff96c02..def3e706 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1130,7 +1130,9 @@ void ClassImpl::destructObject(zend_object *object) } catch (const NotImplemented &exception) { - // fallback on the default destructor call + // fallback on the default destructor call in case a derived object + // of Base throws this. The default implementation will call this + // function in any case. zend_objects_destroy_object(object); } catch (Throwable &throwable) From f5e38d75fd2a9005aee19459751dab03eb76cab8 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 19 Oct 2021 09:09:32 +0200 Subject: [PATCH 039/101] prepare for upcoming release 2.3.0 --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6ecab3fd..1b7f69ad 100644 --- a/Makefile +++ b/Makefile @@ -49,8 +49,8 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # Otherwise only release verions changes. (version is MAJOR.MINOR.RELEASE) # -SONAME = 2.2 -VERSION = 2.2.0 +SONAME = 2.3 +VERSION = 2.3.0 # From 86a93189ec4740efb6c7661dead0e86dac9cfe0d Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 10 Jun 2022 13:24:59 +0200 Subject: [PATCH 040/101] fixed issue for php 7.4 and higher when interfaces inherited other interfaces (php 7.4 turned the "interface_gets_implemented" and "create_object" methods of the zend_class_entry struct into a union, which caused an issue when the create_object property was set for interfaces) --- zend/classimpl.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index def3e706..a02f6137 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1366,8 +1366,10 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // initialize the class entry INIT_CLASS_ENTRY_EX(entry, _name.c_str(), _name.size(), entries()); - // we need a special constructor - entry.create_object = &ClassImpl::createObject; + // we need a special constructor, but only for real classes, not for interfaces. + // (in fact: from php 7.4 onwards the create_object method is part of union + // together with the interface_gets_implemented method, which causes a crash) + if (_type != ClassType::Interface) entry.create_object = &ClassImpl::createObject; // register function that is called for static method calls entry.get_static_method = &ClassImpl::getStaticMethod; @@ -1419,7 +1421,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref _entry = zend_register_internal_class(&entry); } - // register the classes + // register the interfaces for (auto &interface : _interfaces) { // register this interface From ea3e14b457b8fdff41588a01ca5d3820b51f5e97 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 10 Jun 2022 13:26:24 +0200 Subject: [PATCH 041/101] better comment --- zend/classimpl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index a02f6137..863e817d 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1368,7 +1368,8 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // we need a special constructor, but only for real classes, not for interfaces. // (in fact: from php 7.4 onwards the create_object method is part of union - // together with the interface_gets_implemented method, which causes a crash) + // together with the interface_gets_implemented method, which causes a crash + // when the create_object property is set for an interface) if (_type != ClassType::Interface) entry.create_object = &ClassImpl::createObject; // register function that is called for static method calls From 84b262e6673fc04e3da59825dee47606ba3e2e7a Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 10 Jun 2022 14:42:46 +0200 Subject: [PATCH 042/101] fixed version number in makefile --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 1b7f69ad..35edfb3d 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.3 -VERSION = 2.3.0 +VERSION = 2.3.2 # From 59e6bdcf9613323bbd2a6c1fba29fc830a4b0960 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 13 Jun 2022 16:00:05 +0200 Subject: [PATCH 043/101] fixed 7.4/7.2 incompatibility because the class constants were modified between these versions --- include/classtype.h | 21 +++++---------------- include/interface.h | 6 +++--- zend/classtype.h | 34 ++++++++++++++++++++++++++++++++++ zend/includes.h | 3 ++- zend/interface.cpp | 30 ++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 zend/classtype.h create mode 100644 zend/interface.cpp diff --git a/include/classtype.h b/include/classtype.h index f6e2ff60..8e9fcf3b 100644 --- a/include/classtype.h +++ b/include/classtype.h @@ -4,7 +4,7 @@ * Internal class types enumeration. * * @author Emiel Bruijntjes - * @copyright 2014 Copernica BV + * @copyright 2014 - 2022 Copernica BV */ /** @@ -13,22 +13,11 @@ namespace Php { /** - * Enumeration definition. - * - * The PHP-CPP library tries to hide the Zend engine internals completely from - * the user. Therefore, it does not include any of the Zend header files, nor - * can it refer to the constants defined in the Zend header files. The - * following constants have been copied from Zend. If the Zend engine ever - * changes (which we do not expect) we should also copy the constant values - * used here. - * + * Enumeration declaration + * The definition of the variables can be found in the Zend directory, + * so that we can use the macro's from the PHP header files. */ -enum class ClassType { - Regular = 0x00, - Abstract = 0x20, - Final = 0x04, - Interface = 0x40, -}; +enum class ClassType; /** * End namespace diff --git a/include/interface.h b/include/interface.h index a93167f7..330b38fd 100644 --- a/include/interface.h +++ b/include/interface.h @@ -1,7 +1,7 @@ /** * Interface.h * - * @copyright 2014 Copernica BV + * @copyright 2014 - 2022 Copernica BV * @author Emiel Bruijntjes */ @@ -20,12 +20,12 @@ class PHPCPP_EXPORT Interface : private ClassBase * Constructor * @param name */ - Interface(const char *name) : ClassBase(name, ClassType::Interface) {} + Interface(const char *name); /** * Destructor */ - virtual ~Interface() {} + virtual ~Interface() = default; /** * Add a - of course abstract - method to the interface diff --git a/zend/classtype.h b/zend/classtype.h new file mode 100644 index 00000000..5f6fbacf --- /dev/null +++ b/zend/classtype.h @@ -0,0 +1,34 @@ +/** + * @file classtype.h + * + * Internal class types enumeration. + * + * @author Emiel Bruijntjes + * @copyright 2014 - 2022 Copernica BV + */ + +/** + * Include guard + */ +#pragma once + +/** + * Set up namespace + */ +namespace Php { + +/** + * Enumeration definition. + * This is different for different PHP versions + */ +enum class ClassType { + Regular = 0x00, + Interface = ZEND_ACC_INTERFACE, + Abstract = ZEND_ACC_ABSTRACT, + Final = ZEND_ACC_FINAL, +}; + +/** + * End namespace + */ +} diff --git a/zend/includes.h b/zend/includes.h index fd914611..3ae0cc56 100644 --- a/zend/includes.h +++ b/zend/includes.h @@ -4,7 +4,7 @@ * Startup include file to compile the phpcpp library * * @author Emiel Bruijntjes - * @copyright 2013 - 2019 Copernica BV + * @copyright 2013 - 2022 Copernica BV */ /** @@ -117,6 +117,7 @@ * Specific zend implementation files for internal use only */ #include "init.h" +#include "classtype.h" #include "callable.h" #include "nativefunction.h" #include "method.h" diff --git a/zend/interface.cpp b/zend/interface.cpp new file mode 100644 index 00000000..5c06c5e6 --- /dev/null +++ b/zend/interface.cpp @@ -0,0 +1,30 @@ +/** + * Interface.cpp + * + * Implementation file for the Interface class + * + * @author Emiel Bruijntjes + * @copyright 2022 Copernica BV + */ + +/** + * Dependencies + */ +#include "includes.h" + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Constructor + * @param name + */ +Interface::Interface(const char *name) : ClassBase(name, ClassType::Interface) {} + +/** + * End of namespace + */ +} + From 088e402e2584d24b4ede53705501810a5806f602 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 14 Jun 2022 11:31:47 +0200 Subject: [PATCH 044/101] prepare makefile for next version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 35edfb3d..4dba971a 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.3 -VERSION = 2.3.2 +VERSION = 2.3.3 # From c253505760f62d04bbc593bdb95c22aae57da610 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 14 Jun 2022 11:48:58 +0200 Subject: [PATCH 045/101] moved setting the ce_flags a bit up so that we no longer need php7.2 / php7.4 difference --- zend/classimpl.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 863e817d..c250fd5e 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1366,6 +1366,9 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // initialize the class entry INIT_CLASS_ENTRY_EX(entry, _name.c_str(), _name.size(), entries()); + // set access types flags for class + _entry->ce_flags |= (int)_type; + // we need a special constructor, but only for real classes, not for interfaces. // (in fact: from php 7.4 onwards the create_object method is part of union // together with the interface_gets_implemented method, which causes a crash @@ -1452,13 +1455,6 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // install the doc_comment _entry->info.user.doc_comment = _self; - // set access types flags for class -#if PHP_VERSION_ID >= 70400 - _entry->ce_flags |= (int)_type; -#else - _entry->ce_flags = (int)_type; -#endif - // declare all member variables for (auto &member : _members) member->initialize(_entry); From 7a2212b2102a6497faa27d093facb2cfe264bed6 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 14 Jun 2022 13:49:54 +0200 Subject: [PATCH 046/101] fixed another mess up with the class access modifier --- Makefile | 2 +- zend/classimpl.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 4dba971a..258fcb0d 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.3 -VERSION = 2.3.3 +VERSION = 2.3.4 # diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index c250fd5e..061eed69 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1366,9 +1366,6 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // initialize the class entry INIT_CLASS_ENTRY_EX(entry, _name.c_str(), _name.size(), entries()); - // set access types flags for class - _entry->ce_flags |= (int)_type; - // we need a special constructor, but only for real classes, not for interfaces. // (in fact: from php 7.4 onwards the create_object method is part of union // together with the interface_gets_implemented method, which causes a crash @@ -1424,7 +1421,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // register the class _entry = zend_register_internal_class(&entry); } - + // register the interfaces for (auto &interface : _interfaces) { @@ -1439,6 +1436,9 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref if (_base->traversable()) zend_class_implements(_entry, 1, zend_ce_traversable); if (_base->serializable()) zend_class_implements(_entry, 1, zend_ce_serializable); + // instal the right modifier (to make the class an interface, abstract class, etc) + _entry->ce_flags |= uint64_t(_type); + // this pointer has to be copied to temporary pointer, as &this causes compiler error ClassImpl *impl = this; From f3759b7f3a8dbb7d28444586e9bdfc8c326681a3 Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Thu, 23 Jun 2022 10:54:48 +0200 Subject: [PATCH 047/101] added public name property for callable, and included checks for when serialize functions need to be added --- zend/callable.h | 8 ++++++++ zend/classimpl.cpp | 11 +++++++++++ 2 files changed, 19 insertions(+) diff --git a/zend/callable.h b/zend/callable.h index 95f8e4ed..7b55a283 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -124,6 +124,14 @@ class Callable */ void initialize(zend_internal_function_info *info, const char *classname = nullptr) const; + /** + * Name of the function + * @return const std::string& + */ + const std::string &name() const + { + return _name; + } protected: diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 061eed69..7517caa0 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -8,6 +8,7 @@ */ #include "includes.h" #include +#include /** * Set up namespace @@ -1313,6 +1314,16 @@ const struct _zend_function_entry *ClassImpl::entries() // already initialized? if (_entries) return _entries; + // if the class is serializable + if (_base->serializable()) + { + // we first check if the class already has a registered serialize method + auto result = std::find_if(_methods.begin(), _methods.end(), [](std::shared_ptr method){ return method->name() == "serialize"; }); + if (result == _methods.end()) { /* we need to insert the serialize method ourselves */ } + result = std::find_if(_methods.begin(), _methods.end(), [](std::shared_ptr method){ return method->name() == "unserialize"; }); + if (result == _methods.end()) { /* we need to insert the unserialize method ourselves */ } + } + // allocate memory for the functions _entries = new zend_function_entry[_methods.size() + 1]; From e7d8c1c05ad1450851151b014f13a325782df0f5 Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Thu, 23 Jun 2022 11:10:46 +0200 Subject: [PATCH 048/101] made getter oneliner --- zend/callable.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/zend/callable.h b/zend/callable.h index 7b55a283..1334d78a 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -128,10 +128,7 @@ class Callable * Name of the function * @return const std::string& */ - const std::string &name() const - { - return _name; - } + const std::string &name() const { return _name; } protected: From 1c37c4dbca8c203c48bb44adf96d7ac63f68d5c9 Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Fri, 24 Jun 2022 10:52:42 +0200 Subject: [PATCH 049/101] serialize and unserialize methods are now added to the class --- zend/classimpl.cpp | 25 ++++++++++++++++++++----- zend/classimpl.h | 7 +++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 7517caa0..fe4994aa 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1301,6 +1301,19 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned return SUCCESS; } +/** + * Helper method to check if a function is registered for this instance + * @param name name of the function to check for + * @return bool Wether the function exists or not + */ +bool ClassImpl::hasMethod(const char* name) const +{ + // find the method + auto result = std::find_if(_methods.begin(), _methods.end(), [name](std::shared_ptr method){ return method->name() == name; }); + // return wether its found or not + return result != _methods.end(); +} + /** * Retrieve an array of zend_function_entry objects that hold the * properties for each method. This method is called at extension @@ -1317,11 +1330,13 @@ const struct _zend_function_entry *ClassImpl::entries() // if the class is serializable if (_base->serializable()) { - // we first check if the class already has a registered serialize method - auto result = std::find_if(_methods.begin(), _methods.end(), [](std::shared_ptr method){ return method->name() == "serialize"; }); - if (result == _methods.end()) { /* we need to insert the serialize method ourselves */ } - result = std::find_if(_methods.begin(), _methods.end(), [](std::shared_ptr method){ return method->name() == "unserialize"; }); - if (result == _methods.end()) { /* we need to insert the unserialize method ourselves */ } + // i register the methods as abstract, though when they are called it seems to call the methods set in + // ClassImpl::Initialize below, so this works to bypass the 7.4 requirement that the functions are defined. + + // add the serialize method if the class does not have one defined yet + if (!hasMethod("serialize")) method("serialize"); + // add the unserialize method if the class does not have one defined yet + if (!hasMethod("unserialize")) method("unserialize", 0, {Php::ByVal("serialized")}); } // allocate memory for the functions diff --git a/zend/classimpl.h b/zend/classimpl.h index 225c3ad7..625e1910 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -112,6 +112,13 @@ class ClassImpl */ const zend_function_entry *entries(); + /** + * Helper method to check if a function is registered for this instance + * @param name name of the function to check for + * @return bool Wether the function exists or not + */ + bool hasMethod(const char* name) const; + /** * Helper method to turn a property into a zval * From 8a8bb9ac6f587da76099bb28454832e4123a6b52 Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Fri, 24 Jun 2022 10:56:39 +0200 Subject: [PATCH 050/101] removed comments --- zend/classimpl.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index fe4994aa..edf599a2 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1330,9 +1330,6 @@ const struct _zend_function_entry *ClassImpl::entries() // if the class is serializable if (_base->serializable()) { - // i register the methods as abstract, though when they are called it seems to call the methods set in - // ClassImpl::Initialize below, so this works to bypass the 7.4 requirement that the functions are defined. - // add the serialize method if the class does not have one defined yet if (!hasMethod("serialize")) method("serialize"); // add the unserialize method if the class does not have one defined yet From 8984634eb383d416ce94f924946999fac1f8d8a1 Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Wed, 29 Jun 2022 14:05:15 +0200 Subject: [PATCH 051/101] fixed issue: classes that extend from Serializable incorrectly reported that they were abstract because of missing serialize() and unserialize() methods --- .gitignore | 3 ++- include/base.h | 18 +++++++++++++++++- zend/base.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++- zend/classimpl.cpp | 25 +++++++++++++++++++----- 4 files changed, 85 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index fe945915..64fc9284 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ *.a.* *.so.* shared/ -static/ \ No newline at end of file +static/ +.vscode/ \ No newline at end of file diff --git a/include/base.h b/include/base.h index 023d7119..e6d8b879 100644 --- a/include/base.h +++ b/include/base.h @@ -2,7 +2,7 @@ * Base class for defining your own objects * * @author Emiel Bruijntjes - * @copyright 2013 Copernica BV + * @copyright 2013 - 2022 Copernica BV */ /** @@ -261,6 +261,22 @@ class PHPCPP_EXPORT Base */ int __compare(const Base &base) const; + /** + * Method that is called when an explicit call to $object->serialize() is made + * Note that a call to serialize($object) does not end up in this function, but + * is handled by the user-space implementation of Serializable::serialize()). + * @return Php::Value + */ + Php::Value __serialize(); + + /** + * Method that is called when an explicit call to $object->unserialize() is made + * Note that a call to unserialize($string) does not end up in this function, but + * is handled by the user-space implementation of Serializable::unserialize()). + * @param params The passed parameters + */ + void __unserialize(Php::Parameters ¶ms); + private: /** diff --git a/zend/base.cpp b/zend/base.cpp index ed6fe3bc..9f000ed8 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -3,7 +3,7 @@ * * Implementation file for the base of all classes * - * @copyright 2014 Copernica BV + * @copyright 2014 - 2022 Copernica BV */ #include "includes.h" @@ -217,6 +217,51 @@ int Base::__compare(const Base &that) const return 1; } +/** + * Method that is called when an explicit call to $object->serialize() is made + * Note that a call to serialize($object) does not end up in this function, but + * is handled by the user-space implementation of Serializable::serialize()). + * @return Php::Value + */ +Php::Value Base::__serialize() +{ + // 'this' refers to a Php::Base class, but we expect that is also implements the Serializable + // interface (otherwise we would never have registered the __serialize function as a callback) + auto *serializable = dynamic_cast(this); + + // this one should not fail + if (serializable == nullptr) return ""; + + // pass the call to the interface + return serializable->serialize(); +} + +/** + * Method that is called when an explicit call to $object->unserialize() is made + * Note that a call to unserialize($string) does not end up in this function, but + * is handled by the user-space implementation of Serializable::unserialize()). + * @param params The passed parameters + */ +void Base::__unserialize(Php::Parameters ¶ms) +{ + // 'this' refers to a Php::Base class, but we expect that is also implements the Serializable + // interface (otherwise we would never have registered the __serialize function as a callback) + auto *serializable = dynamic_cast(this); + + // this one should not fail + if (serializable == nullptr) return; + + // the passed in parameter + Php::Value param = params[0]; + + // make sure the parameter is indeed a string + param.setType(Type::String); + + // pass the call to the interface + serializable->unserialize(param.rawValue(), param.size()); +} + + /** * End namespace */ diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index edf599a2..dcf91154 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1327,17 +1327,19 @@ const struct _zend_function_entry *ClassImpl::entries() // already initialized? if (_entries) return _entries; - // if the class is serializable + // the number of entries that need to be allocated + size_t entrycount = _methods.size(); + + // if the class is serializable, we might need some extra methods if (_base->serializable()) { // add the serialize method if the class does not have one defined yet - if (!hasMethod("serialize")) method("serialize"); - // add the unserialize method if the class does not have one defined yet - if (!hasMethod("unserialize")) method("unserialize", 0, {Php::ByVal("serialized")}); + if (!hasMethod("serialize")) entrycount += 1; + if (!hasMethod("unserialize")) entrycount += 1; } // allocate memory for the functions - _entries = new zend_function_entry[_methods.size() + 1]; + _entries = new zend_function_entry[entrycount + 1]; // keep iterator counter int i = 0; @@ -1352,6 +1354,19 @@ const struct _zend_function_entry *ClassImpl::entries() method->initialize(entry, _name); } + // if the class is serializable, we might need some extra methods + if (_base->serializable()) + { + // the method objectneed to stay in scope for the lifetime of the script (because the register a pointer + // to an internal string buffer) -- so we create them as static variables + static Method serialize("serialize", &Base::__serialize, 0, {}); + static Method unserialize("unserialize", &Base::__unserialize, 0, { ByVal("input", Type::Undefined, true) }); + + // register the serialize and unserialize method in case this was not yet done in PHP user space + if (!hasMethod("serialize")) serialize.initialize(&_entries[i++], _name); + if (!hasMethod("unserialize")) unserialize.initialize(&_entries[i++], _name); + } + // last entry should be set to all zeros zend_function_entry *last = &_entries[i]; From 6291595a2f7b3679390f62d7f092e75f2aa3f08e Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Wed, 29 Jun 2022 17:44:13 +0200 Subject: [PATCH 052/101] prepare for upcoming release --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 258fcb0d..debc48f7 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.3 -VERSION = 2.3.4 +VERSION = 2.3.5 # From 1d328a8432c8aef49513b331e4b5a265e89dfd9f Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Thu, 7 Jul 2022 17:05:21 +0200 Subject: [PATCH 053/101] added count method when not defined by user --- include/base.h | 8 ++++++++ include/class.h | 10 ++++++++++ include/classbase.h | 1 + zend/base.cpp | 18 ++++++++++++++++++ zend/classimpl.cpp | 13 +++++++++++++ 5 files changed, 50 insertions(+) diff --git a/include/base.h b/include/base.h index e6d8b879..eb1a7129 100644 --- a/include/base.h +++ b/include/base.h @@ -277,6 +277,14 @@ class PHPCPP_EXPORT Base */ void __unserialize(Php::Parameters ¶ms); + /** + * Method that is called when an explicit call to $object->count() is made + * Note that a call to count($string) does not end up in this function, but + * is handled by the user-space implementation of Countable::count()). + * @param params The passed parameters + */ + Php::Value __count(Php::Parameters ¶ms); + private: /** diff --git a/include/class.h b/include/class.h index 14cbd95b..e18220ad 100644 --- a/include/class.h +++ b/include/class.h @@ -405,6 +405,16 @@ class PHPCPP_EXPORT Class : private ClassBase return std::is_base_of::value; } + /** + * Is this a countable class? + * @return bool + */ + virtual bool countable() const override + { + // check if the templated class overrides from the Countable class + return std::is_base_of::value; + } + /** * Call the __clone method * @param base diff --git a/include/classbase.h b/include/classbase.h index c650b5a7..3e010719 100644 --- a/include/classbase.h +++ b/include/classbase.h @@ -124,6 +124,7 @@ class PHPCPP_EXPORT ClassBase */ virtual bool traversable() const { return false; } virtual bool serializable() const { return false; } + virtual bool countable() const { return false; } virtual bool clonable() const { return false; } /** diff --git a/zend/base.cpp b/zend/base.cpp index 9f000ed8..515dde97 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -261,6 +261,24 @@ void Base::__unserialize(Php::Parameters ¶ms) serializable->unserialize(param.rawValue(), param.size()); } +/** + * Method that is called when an explicit call to $object->count() is made + * Note that a call to unserialize($string) does not end up in this function, but + * is handled by the user-space implementation of Serializable::count()). + * @param params The passed parameters + */ +Php::Value Base::__count(Php::Parameters ¶ms) +{ + // 'this' refers to a Php::Base class, but we expect that is also implements the Countable + // interface (otherwise we would never have registered the __count function as a callback) + auto *countable = dynamic_cast(this); + + // this one should not fail + if (countable == nullptr) return -1; + + // pass the call to the interface + return countable->count(); +} /** * End namespace diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index dcf91154..bca997c8 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1330,6 +1330,9 @@ const struct _zend_function_entry *ClassImpl::entries() // the number of entries that need to be allocated size_t entrycount = _methods.size(); + // if the class is countable, we might need some extra methods + if (_base->countable() && !hasMethod("count")) entrycount += 1; + // if the class is serializable, we might need some extra methods if (_base->serializable()) { @@ -1354,6 +1357,16 @@ const struct _zend_function_entry *ClassImpl::entries() method->initialize(entry, _name); } + if (_base->countable()) + { + // the method objectneed to stay in scope for the lifetime of the script (because the register a pointer + // to an internal string buffer) -- so we create them as static variables + static Method count("count", &Base::__count, 0, {}); + + // register the serialize and unserialize method in case this was not yet done in PHP user space + if (!hasMethod("count")) count.initialize(&_entries[i++], _name); + } + // if the class is serializable, we might need some extra methods if (_base->serializable()) { From 3709bacb435cd7ded67043ee8811f79a4213668a Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Thu, 7 Jul 2022 17:21:04 +0200 Subject: [PATCH 054/101] added comments --- zend/classimpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index bca997c8..c78ab216 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1357,6 +1357,7 @@ const struct _zend_function_entry *ClassImpl::entries() method->initialize(entry, _name); } + // if the class is countable, we might need to add some extra methods if (_base->countable()) { // the method objectneed to stay in scope for the lifetime of the script (because the register a pointer From 6d0ee21f9b39c6eed9c1d6f783aaea903e8ac469 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 8 Jul 2022 08:46:23 +0200 Subject: [PATCH 055/101] when countable is not implemented, it is probably better to return 0 --- zend/base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zend/base.cpp b/zend/base.cpp index 515dde97..c0d9e5af 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -274,7 +274,7 @@ Php::Value Base::__count(Php::Parameters ¶ms) auto *countable = dynamic_cast(this); // this one should not fail - if (countable == nullptr) return -1; + if (countable == nullptr) return 0; // pass the call to the interface return countable->count(); From ce83ef5d00ce098833088a74830a0159e6b862fa Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Wed, 29 Jun 2022 17:44:13 +0200 Subject: [PATCH 056/101] prepare for upcoming release --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 258fcb0d..debc48f7 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.3 -VERSION = 2.3.4 +VERSION = 2.3.5 # From 4b917cedef3d80c833e7d2d4fc51d1b45faf4054 Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Thu, 7 Jul 2022 17:05:21 +0200 Subject: [PATCH 057/101] added count method when not defined by user --- include/base.h | 8 ++++++++ include/class.h | 10 ++++++++++ include/classbase.h | 1 + zend/base.cpp | 18 ++++++++++++++++++ zend/classimpl.cpp | 13 +++++++++++++ 5 files changed, 50 insertions(+) diff --git a/include/base.h b/include/base.h index e6d8b879..eb1a7129 100644 --- a/include/base.h +++ b/include/base.h @@ -277,6 +277,14 @@ class PHPCPP_EXPORT Base */ void __unserialize(Php::Parameters ¶ms); + /** + * Method that is called when an explicit call to $object->count() is made + * Note that a call to count($string) does not end up in this function, but + * is handled by the user-space implementation of Countable::count()). + * @param params The passed parameters + */ + Php::Value __count(Php::Parameters ¶ms); + private: /** diff --git a/include/class.h b/include/class.h index 14cbd95b..e18220ad 100644 --- a/include/class.h +++ b/include/class.h @@ -405,6 +405,16 @@ class PHPCPP_EXPORT Class : private ClassBase return std::is_base_of::value; } + /** + * Is this a countable class? + * @return bool + */ + virtual bool countable() const override + { + // check if the templated class overrides from the Countable class + return std::is_base_of::value; + } + /** * Call the __clone method * @param base diff --git a/include/classbase.h b/include/classbase.h index c650b5a7..3e010719 100644 --- a/include/classbase.h +++ b/include/classbase.h @@ -124,6 +124,7 @@ class PHPCPP_EXPORT ClassBase */ virtual bool traversable() const { return false; } virtual bool serializable() const { return false; } + virtual bool countable() const { return false; } virtual bool clonable() const { return false; } /** diff --git a/zend/base.cpp b/zend/base.cpp index 9f000ed8..515dde97 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -261,6 +261,24 @@ void Base::__unserialize(Php::Parameters ¶ms) serializable->unserialize(param.rawValue(), param.size()); } +/** + * Method that is called when an explicit call to $object->count() is made + * Note that a call to unserialize($string) does not end up in this function, but + * is handled by the user-space implementation of Serializable::count()). + * @param params The passed parameters + */ +Php::Value Base::__count(Php::Parameters ¶ms) +{ + // 'this' refers to a Php::Base class, but we expect that is also implements the Countable + // interface (otherwise we would never have registered the __count function as a callback) + auto *countable = dynamic_cast(this); + + // this one should not fail + if (countable == nullptr) return -1; + + // pass the call to the interface + return countable->count(); +} /** * End namespace diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index dcf91154..bca997c8 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1330,6 +1330,9 @@ const struct _zend_function_entry *ClassImpl::entries() // the number of entries that need to be allocated size_t entrycount = _methods.size(); + // if the class is countable, we might need some extra methods + if (_base->countable() && !hasMethod("count")) entrycount += 1; + // if the class is serializable, we might need some extra methods if (_base->serializable()) { @@ -1354,6 +1357,16 @@ const struct _zend_function_entry *ClassImpl::entries() method->initialize(entry, _name); } + if (_base->countable()) + { + // the method objectneed to stay in scope for the lifetime of the script (because the register a pointer + // to an internal string buffer) -- so we create them as static variables + static Method count("count", &Base::__count, 0, {}); + + // register the serialize and unserialize method in case this was not yet done in PHP user space + if (!hasMethod("count")) count.initialize(&_entries[i++], _name); + } + // if the class is serializable, we might need some extra methods if (_base->serializable()) { From bf4c9b3617be69a7cf73080f9e61322964f9ca06 Mon Sep 17 00:00:00 2001 From: Bram van den Brink Date: Thu, 7 Jul 2022 17:21:04 +0200 Subject: [PATCH 058/101] added comments --- zend/classimpl.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index bca997c8..c78ab216 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1357,6 +1357,7 @@ const struct _zend_function_entry *ClassImpl::entries() method->initialize(entry, _name); } + // if the class is countable, we might need to add some extra methods if (_base->countable()) { // the method objectneed to stay in scope for the lifetime of the script (because the register a pointer From bb245b15ea917e5e86baa9d0ff595b39f18d3975 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 8 Jul 2022 08:46:23 +0200 Subject: [PATCH 059/101] when countable is not implemented, it is probably better to return 0 --- zend/base.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zend/base.cpp b/zend/base.cpp index 515dde97..c0d9e5af 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -274,7 +274,7 @@ Php::Value Base::__count(Php::Parameters ¶ms) auto *countable = dynamic_cast(this); // this one should not fail - if (countable == nullptr) return -1; + if (countable == nullptr) return 0; // pass the call to the interface return countable->count(); From 4494f1a3d827c17f6a8433a67de27ba9e23d4e2f Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Tue, 5 Jan 2021 08:46:53 -0700 Subject: [PATCH 060/101] PHP-8.0 support --- .travis.yml | 4 ++- zend/callable.cpp | 20 ++++++++++-- zend/callable.h | 28 ++++++++++++++-- zend/classimpl.cpp | 74 +++++++++++++++++++++++++++++++++--------- zend/classimpl.h | 36 ++++++++++++-------- zend/extensionimpl.cpp | 22 ++++++++----- zend/extensionimpl.h | 20 ++++++++---- zend/script.cpp | 4 +++ zend/throwable.cpp | 10 +++++- zend/value.cpp | 32 +++++++++++++++++- 10 files changed, 197 insertions(+), 53 deletions(-) diff --git a/.travis.yml b/.travis.yml index af5a58f8..fc8b2ef0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,9 @@ php: - 7.1 - 7.2 - 7.3 - - nightly # doesn't work yet on PHP 7.4! not building. + - 7.4 + - 8.0 + - nightly # setting the env is the easiest, because it will mix with all the separate php versions (travis does this) diff --git a/zend/callable.cpp b/zend/callable.cpp index 98c98140..388c65b8 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -36,9 +36,12 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) #else // Sanity check assert(info[argc].type != 0 && info[argc].name == nullptr); - // the callable we are retrieving +#if PHP_VERSION_ID < 80000 Callable *callable = reinterpret_cast(info[argc].type); +#else + Callable *callable = reinterpret_cast(info[argc].type.ptr); +#endif #endif // check if sufficient parameters were passed (for some reason this check @@ -99,10 +102,10 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int _argv[_argc + 1].class_name = reinterpret_cast(this); #else // @todo this is broken. the zend engine, from 7.2 onwards copies over - // the struct and slices of the last element, because the num_args + // the struct and slices off the last element, because the num_args // is incorrect in their view. another place to put this may be // hiding it behind the fname - _argv[_argc + 1].type = reinterpret_cast(this); + _argv[_argc + 1].type = ZEND_TYPE_INIT_PTR(this, IS_PTR, true, 0); #endif // we use our own invoke method, which does a lookup @@ -129,8 +132,10 @@ void Callable::initialize(zend_internal_function_info *info, const char *classna { // initialize all common elements info->required_num_args = _required; +#if PHP_VERSION_ID < 80000 info->return_reference = false; info->_is_variadic = false; +#endif // the structure has been slightly altered since php7.2 #if PHP_VERSION_ID < 70200 @@ -147,9 +152,18 @@ void Callable::initialize(zend_internal_function_info *info, const char *classna #else // the properties that are available on php 7.2 and higher info->required_num_args = _required; +#if PHP_VERSION_ID < 80000 info->return_reference = false; info->_is_variadic = false; info->type = ZEND_TYPE_ENCODE((int)_return, true); +#else + if ((int)_return) { + info->type = (zend_type)ZEND_TYPE_INIT_CODE((int)_return, true, 0); + } else { + info->type = (zend_type)ZEND_TYPE_INIT_NONE(0); + } + +#endif #endif } diff --git a/zend/callable.h b/zend/callable.h index 1334d78a..7402210e 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -53,13 +53,14 @@ class Callable // initialize all elements to null _argv[i].name = nullptr; +#if PHP_VERSION_ID < 80000 _argv[i].is_variadic = false; _argv[i].pass_by_reference = false; - +#endif // initialize the extra argument prior to 7.2 #if PHP_VERSION_ID < 70200 _argv[i].class_name = nullptr; -#else +#elif PHP_VERSION_ID < 80000 _argv[i].type = 0; #endif } @@ -203,6 +204,26 @@ class Callable case Type::Object: info->type_hint = IS_OBJECT; break; // must be an object of the given classname case Type::Callable: info->type_hint = IS_CALLABLE; break; // anything that can be invoked default: info->type_hint = IS_UNDEF; break; // if not specified we allow anything +#elif PHP_VERSION_ID >= 80000 + case Type::Undefined: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_UNDEF, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // undefined means we'll accept any type + case Type::Null: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_UNDEF, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // this is likely an error, what good would accepting NULL be? accept anything + case Type::False: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // accept true as well ;) + case Type::True: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // accept false as well + case Type::Bool: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // any bool will do, true, false, the options are limitless + case Type::Numeric: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // accept integers here + case Type::Float: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_DOUBLE, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // floating-point values welcome too + case Type::String: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_STRING, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // accept strings, should auto-cast objects with __toString as well + case Type::Array: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_ARRAY, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // array of anything (individual members cannot be restricted) + case Type::Object: + if (arg.classname()) { + info->type = (zend_type) ZEND_TYPE_INIT_CLASS(arg.encoded(), arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); + break; + } + info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_OBJECT, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); + break; + case Type::Callable: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_CALLABLE, arg.allowNull(), 0); break; // anything that can be invoke + + default: info->type = ZEND_TYPE_INIT_CODE(IS_UNDEF, 0, _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // if not specified we allow anything #else case Type::Undefined: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // undefined means we'll accept any type case Type::Null: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // this is likely an error, what good would accepting NULL be? accept anything @@ -221,7 +242,7 @@ class Callable default: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // if not specified we allow anything #endif } - +#if PHP_VERSION_ID < 80000 // from PHP 5.6 and onwards, an is_variadic property can be set, this // specifies whether this argument is the first argument that specifies // the type for a variable length list of arguments. For now we only @@ -230,6 +251,7 @@ class Callable // whether or not to pass the argument by reference info->pass_by_reference = arg.byReference(); +#endif } /** diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index c78ab216..b55367ef 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -285,7 +285,11 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string * * @param object_ptr * @return int */ -int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr) +#if PHP_VERSION_ID < 80000 +int ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr) +#else +int ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr, zend_bool check_only) +#endif { // 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 @@ -315,7 +319,11 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct // store pointer to ourselves (note that the entry_ptr is useless // inside this function as it is always uninitialized for some reason) +#if PHP_VERSION_ID < 80000 data->self = self(Z_OBJCE_P(object)); +#else + data->self = self(object->ce); +#endif // assign this dynamically allocated variable to the func parameter // the cast is ok, because zend_internal_function is a member of the @@ -324,7 +332,11 @@ 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) +#if PHP_VERSION_ID < 80000 *object_ptr = Z_OBJ_P(object); +#else + *object_ptr = object; +#endif // done return SUCCESS; @@ -373,7 +385,11 @@ zend_object_handlers *ClassImpl::objectHandlers() _handlers.cast_object = &ClassImpl::cast; // method to compare two objects +#if PHP_VERSION_ID < 80000 _handlers.compare_objects = &ClassImpl::compare; +#else + _handlers.compare = &ClassImpl::compare; +#endif // set the offset between our class implementation and // the zend_object member in the allocated structure @@ -426,10 +442,17 @@ int ClassImpl::compare(zval *val1, zval *val2) catch (const NotImplemented &exception) { // it was not implemented, do we have a default? +#if PHP_VERSION_ID < 80000 if (!std_object_handlers.compare_objects) return 1; // call default return std_object_handlers.compare_objects(val1, val2); +#else + if (!std_object_handlers.compare) return 1; + + // call default + return std_object_handlers.compare(val1, val2); +#endif } catch (Throwable &throwable) { @@ -448,14 +471,17 @@ int ClassImpl::compare(zval *val1, zval *val2) * @param type * @return int */ -int ClassImpl::cast(zval *val, zval *retval, int type) +int ClassImpl::cast(ZEND_OBJECT_OR_ZVAL val, zval *retval, int type) { // get the base c++ object Base *object = ObjectImpl::find(val)->object(); // retrieve the class entry linked to this object +#if PHP_VERSION_ID < 80000 auto *entry = Z_OBJCE_P(val); - +#else + auto *entry = val->ce; +#endif // we need the C++ class meta-information object ClassBase *meta = self(entry)->_base; @@ -508,11 +534,14 @@ int ClassImpl::cast(zval *val, zval *retval, int type) * @param val The object to be cloned * @return zend_object The object to be created */ -zend_object *ClassImpl::cloneObject(zval *val) +zend_object *ClassImpl::cloneObject(ZEND_OBJECT_OR_ZVAL val) { // retrieve the class entry linked to this object +#if PHP_VERSION_ID < 80000 auto *entry = Z_OBJCE_P(val); - +#else + auto *entry = val->ce; +#endif // we need the C++ class meta-information object ClassImpl *impl = self(entry); ClassBase *meta = impl->_base; @@ -554,7 +583,7 @@ zend_object *ClassImpl::cloneObject(zval *val) * @param count * @return int */ -int ClassImpl::countElements(zval *object, zend_long *count) +int ClassImpl::countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count) { // does it implement the countable interface? Countable *countable = dynamic_cast(ObjectImpl::find(object)->object()); @@ -602,7 +631,7 @@ int ClassImpl::countElements(zval *object, zend_long *count) * @param rv Pointer to where to store the data * @return zval */ -zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv) +zval *ClassImpl::readDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset, int type, zval *rv) { // what to do with the type? // @@ -664,7 +693,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type, zval *rv) * @param value The new value * @return zval */ -void ClassImpl::writeDimension(zval *object, zval *offset, zval *value) +void ClassImpl::writeDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset, zval *value) { // does it implement the arrayaccess interface? ArrayAccess *arrayaccess = dynamic_cast(ObjectImpl::find(object)->object()); @@ -705,7 +734,7 @@ void ClassImpl::writeDimension(zval *object, zval *offset, zval *value) * @param check_empty Was this an isset() call, or an empty() call? * @return bool */ -int ClassImpl::hasDimension(zval *object, zval *member, int check_empty) +int ClassImpl::hasDimension(ZEND_OBJECT_OR_ZVAL object, zval *member, int check_empty) { // does it implement the arrayaccess interface? ArrayAccess *arrayaccess = dynamic_cast(ObjectImpl::find(object)->object()); @@ -754,7 +783,7 @@ int ClassImpl::hasDimension(zval *object, zval *member, int check_empty) * @param object The object on which it is called * @param member The member to remove */ -void ClassImpl::unsetDimension(zval *object, zval *member) +void ClassImpl::unsetDimension(ZEND_OBJECT_OR_ZVAL object, zval *member) { // does it implement the arrayaccess interface? ArrayAccess *arrayaccess = dynamic_cast(ObjectImpl::find(object)->object()); @@ -835,7 +864,7 @@ zval *ClassImpl::toZval(Value &&value, int type, zval *rv) * @param rv Pointer to where to store the data * @return val */ -zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_slot, zval *rv) +zval *ClassImpl::readProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, int type, void **cache_slot, zval *rv) { // what to do with the type? // @@ -858,7 +887,11 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s Base *base = ObjectImpl::find(object)->object(); // retrieve the class entry linked to this object +#if PHP_VERSION_ID < 80000 auto *entry = Z_OBJCE_P(object); +#else + auto *entry = object->ce; +#endif // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -916,13 +949,17 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, void **cache_s * @param cache_slot The cache slot used * @return zval */ -PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(zval *object, zval *name, zval *value, void **cache_slot) +PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, zval *value, void **cache_slot) { // retrieve the object and class Base *base = ObjectImpl::find(object)->object(); // retrieve the class entry linked to this object +#if PHP_VERSION_ID < 80000 auto *entry = Z_OBJCE_P(object); +#else + auto *entry = object->ce; +#endif // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -1007,7 +1044,7 @@ PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(zval *object, zval *name, z * @param cache_slot The cache slot used * @return bool */ -int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void **cache_slot) +int ClassImpl::hasProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, int has_set_exists, void **cache_slot) { // the default implementation throws an exception, if we catch that // we know for sure that the user has not overridden the __isset method @@ -1017,7 +1054,11 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void ** Base *base = ObjectImpl::find(object)->object(); // retrieve the class entry linked to this object +#if PHP_VERSION_ID < 80000 auto *entry = Z_OBJCE_P(object); +#else + auto *entry = object->ce; +#endif // we need the C++ class meta-information object ClassImpl *impl = self(entry); @@ -1071,15 +1112,18 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, void ** * @param member The member to remove * @param cache_slot The cache slot used */ -void ClassImpl::unsetProperty(zval *object, zval *member, void **cache_slot) +void ClassImpl::unsetProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL member, void **cache_slot) { // the default implementation throws an exception, if we catch that // we know for sure that the user has not overridden the __unset method try { // retrieve the class entry linked to this object +#if PHP_VERSION_ID < 80000 auto *entry = Z_OBJCE_P(object); - +#else + auto *entry = object->ce; +#endif // we need the C++ class meta-information object ClassImpl *impl = self(entry); diff --git a/zend/classimpl.h b/zend/classimpl.h index 625e1910..939f8150 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -18,7 +18,13 @@ namespace Php { #else # define PHP_WRITE_PROP_HANDLER_TYPE void #endif - +#if PHP_VERSION_ID < 80000 +#define ZEND_OBJECT_OR_ZVAL zval * +#define ZEND_STRING_OR_ZVAL zval * +#else +#define ZEND_OBJECT_OR_ZVAL zend_object * +#define ZEND_STRING_OR_ZVAL zend_string * +#endif /** * Class definition */ @@ -189,7 +195,7 @@ class ClassImpl * @return zend_object Object info */ static zend_object *createObject(zend_class_entry *entry); - static zend_object *cloneObject(zval *val); + static zend_object *cloneObject(ZEND_OBJECT_OR_ZVAL val); static void destructObject(zend_object *object); static void freeObject(zend_object *object); @@ -212,7 +218,7 @@ class ClassImpl * @param count * @return int */ - static int countElements(zval *object, zend_long *count); + static int countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count); /** * Function that is called when the object is used as an array in PHP @@ -224,10 +230,10 @@ class ClassImpl * @param check_empty ???? * @return zval */ - static zval *readDimension(zval *object, zval *offset, int type, zval *rv); - static void writeDimension(zval *object, zval *offset, zval *value); - static int hasDimension(zval *object, zval *offset, int check_empty); - static void unsetDimension(zval *object, zval *offset); + static zval *readDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset, int type, zval *rv); + static void writeDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset, zval *value); + static int hasDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset, int check_empty); + static void unsetDimension(ZEND_OBJECT_OR_ZVAL object, zval *offset); /** * Retrieve pointer to our own object handlers @@ -261,7 +267,7 @@ class ClassImpl * @param rv Pointer to where to store the data * @return zval */ - static zval *readProperty(zval *object, zval *name, int type, void **cache_slot, zval *rv); + static zval *readProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, int type, void **cache_slot, zval *rv); /** * Function that is called when a property is set / updated @@ -272,7 +278,7 @@ class ClassImpl * @param cache_slot The cache slot used * @return zval* */ - static PHP_WRITE_PROP_HANDLER_TYPE writeProperty(zval *object, zval *name, zval *value, void **cache_slot); + static PHP_WRITE_PROP_HANDLER_TYPE writeProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, zval *value, void **cache_slot); /** * Function that is called to check whether a certain property is set @@ -283,7 +289,7 @@ class ClassImpl * @param cache_slot The cache slot used * @return bool */ - static int hasProperty(zval *object, zval *name, int has_set_exists, void **cache_slot); + static int hasProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL name, int has_set_exists, void **cache_slot); /** * Function that is called when a property is removed from the project @@ -292,7 +298,7 @@ class ClassImpl * @param member The member to remove * @param cache_slot The cache slot used */ - static void unsetProperty(zval *object, zval *member, void **cache_slot); + static void unsetProperty(ZEND_OBJECT_OR_ZVAL object, ZEND_STRING_OR_ZVAL member, void **cache_slot); /** * Method that returns information about the function signature of a undefined method @@ -322,7 +328,11 @@ class ClassImpl * @param object_ptr To be filled with the object on which the method is to be called * @return int */ - static int getClosure(zval *object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr); +#if PHP_VERSION_ID < 80000 + static int getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr); +#else + static int getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr, zend_bool check_only); +#endif /** * Function to cast the object to a different type @@ -331,7 +341,7 @@ class ClassImpl * @param type * @return int */ - static int cast(zval *object, zval *retval, int type); + static int cast(ZEND_OBJECT_OR_ZVAL object, zval *retval, int type); /** * Function to compare two objects diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp index 8f9bf80c..455a7424 100644 --- a/zend/extensionimpl.cpp +++ b/zend/extensionimpl.cpp @@ -107,7 +107,11 @@ static ExtensionImpl *find(int number) * @param number Module number * @return int 0 on success */ +#if PHP_VERSION_ID < 80000 int ExtensionImpl::processStartup(int type, int module_number) +#else +zend_result ExtensionImpl::processStartup(int type, int module_number) +#endif { // initialize and allocate the "global" variables ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL); @@ -116,7 +120,7 @@ int ExtensionImpl::processStartup(int type, int module_number) auto *extension = find(module_number); // initialize the extension - return BOOL2SUCCESS(extension->initialize(module_number)); + return extension->initialize(module_number) ? SUCCESS : FAILURE; } /** @@ -125,7 +129,7 @@ int ExtensionImpl::processStartup(int type, int module_number) * @param number Module number * @return int */ -int ExtensionImpl::processShutdown(int type, int module_number) +ZEND_RESULT_OR_INT ExtensionImpl::processShutdown(int type, int module_number) { // get the extension auto *extension = find(module_number); @@ -134,7 +138,7 @@ int ExtensionImpl::processShutdown(int type, int module_number) number2extension.erase(module_number); // done - return BOOL2SUCCESS(extension->shutdown(module_number)); + return extension->shutdown(module_number) ? SUCCESS:FAILURE; } /** @@ -143,7 +147,7 @@ int ExtensionImpl::processShutdown(int type, int module_number) * @param number Module number * @return int 0 on success */ -int ExtensionImpl::processRequest(int type, int module_number) +ZEND_RESULT_OR_INT ExtensionImpl::processRequest(int type, int module_number) { // get the extension auto *extension = find(module_number); @@ -152,7 +156,7 @@ int ExtensionImpl::processRequest(int type, int module_number) if (extension->_onRequest) extension->_onRequest(); // done - return BOOL2SUCCESS(true); + return SUCCESS; } /** @@ -161,7 +165,7 @@ int ExtensionImpl::processRequest(int type, int module_number) * @param number Module number * @return int 0 on success */ -int ExtensionImpl::processIdle(int type, int module_number) +ZEND_RESULT_OR_INT ExtensionImpl::processIdle(int type, int module_number) { // get the extension auto *extension = find(module_number); @@ -170,7 +174,7 @@ int ExtensionImpl::processIdle(int type, int module_number) if (extension->_onIdle) extension->_onIdle(); // done - return BOOL2SUCCESS(true); + return SUCCESS; } /** @@ -180,7 +184,7 @@ int ExtensionImpl::processIdle(int type, int module_number) * @param number Module number * @return int 0 on success */ -int ExtensionImpl::processMismatch(int type, int module_number) +ZEND_RESULT_OR_INT ExtensionImpl::processMismatch(int type, int module_number) { // get the extension auto *extension = find(module_number); @@ -189,7 +193,7 @@ int ExtensionImpl::processMismatch(int type, int module_number) warning << "Version mismatch between PHP-CPP and extension " << extension->name() << " " << extension->version() << " (recompile needed?)" << std::endl; // done - return BOOL2SUCCESS(true); + return SUCCESS; } /** diff --git a/zend/extensionimpl.h b/zend/extensionimpl.h index 2451b142..d65b6015 100644 --- a/zend/extensionimpl.h +++ b/zend/extensionimpl.h @@ -12,6 +12,12 @@ */ namespace Php { +#if PHP_VERSION_ID < 80000 +#define ZEND_RESULT_OR_INT int +#else +#define ZEND_RESULT_OR_INT zend_result +#endif + /** * Class definition */ @@ -157,23 +163,23 @@ class ExtensionImpl : public ExtensionBase * @param number Module number * @return int 0 on success */ - static int processStartup(int type, int module_number); - + static ZEND_RESULT_OR_INT processStartup(int type, int module_number); + /** * Function that is called when the extension is about to be stopped * @param type Module type * @param number Module number * @return int */ - static int processShutdown(int type, int module_number); - + static ZEND_RESULT_OR_INT processShutdown(int type, int module_number); + /** * Function that is called when a request starts * @param type Module type * @param number Module number * @return int 0 on success */ - static int processRequest(int type, int module_number); + static ZEND_RESULT_OR_INT processRequest(int type, int module_number); /** * Function that is called when a request is ended @@ -181,7 +187,7 @@ class ExtensionImpl : public ExtensionBase * @param number Module number * @return int 0 on success */ - static int processIdle(int type, int module_number); + static ZEND_RESULT_OR_INT processIdle(int type, int module_number); /** * Function that is called when the PHP engine initializes with a different PHP-CPP @@ -190,7 +196,7 @@ class ExtensionImpl : public ExtensionBase * @param number Module number * @return int 0 on success */ - static int processMismatch(int type, int module_number); + static ZEND_RESULT_OR_INT processMismatch(int type, int module_number); }; /** diff --git a/zend/script.cpp b/zend/script.cpp index fa046b30..2f197d55 100644 --- a/zend/script.cpp +++ b/zend/script.cpp @@ -37,7 +37,11 @@ zend_op_array *Script::compile(const char *name, const char *phpcode, size_t siz CompilerOptions options(ZEND_COMPILE_DEFAULT_FOR_EVAL); // compile the string +#if PHP_VERSION_ID < 80000 return zend_compile_string(source._val, (char *)name); +#else + return zend_compile_string(zval_get_string(source._val), (char*)name); +#endif } /** diff --git a/zend/throwable.cpp b/zend/throwable.cpp index c398d568..cc40658b 100644 --- a/zend/throwable.cpp +++ b/zend/throwable.cpp @@ -30,8 +30,12 @@ static std::string convert(zend_object *object) ZVAL_OBJ(&properties, object); // retrieve the message, filename, error code and line number +#if PHP_VERSION_ID < 80000 auto message = zval_get_string(zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("message"), 1, &tmp)); - +#else + auto message = zval_get_string(zend_read_property(Z_OBJCE(properties), object, ZEND_STRL("message"), 1, &tmp)); +#endif + // copy message to a string std::string result(ZSTR_VAL(message), ZSTR_LEN(message)); @@ -55,7 +59,11 @@ Throwable::Throwable(zend_object *object) : std::runtime_error(convert(object)) ZVAL_OBJ(&properties, object); // retrieve the error code +#if PHP_VERSION_ID < 80000 _code = zval_get_long(zend_read_property(Z_OBJCE(properties), &properties, ZEND_STRL("code"), 1, &result)); +#else + _code = zval_get_long(zend_read_property(Z_OBJCE(properties), object, ZEND_STRL("code"), 1, &result)); +#endif } /** diff --git a/zend/value.cpp b/zend/value.cpp index 6ded9556..891495e2 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -392,12 +392,14 @@ Value& Value::operator=(struct _zval_struct* value) // If the destination is refcounted if (Z_REFCOUNTED_P(to)) { +#if PHP_VERSION_ID < 80000 // objects can have their own assignment handler if (Z_TYPE_P(to) == IS_OBJECT && Z_OBJ_HANDLER_P(to, set)) { Z_OBJ_HANDLER_P(to, set)(to, value); return *this; } +#endif // If to and from are the same, there is nothing left to do if (to == value) return *this; @@ -767,7 +769,11 @@ static Value do_exec(const zval *object, zval *method, int argc, zval *argv) // 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 PHP_VERSION_ID < 80000 if (call_user_function_ex(CG(function_table), (zval*) object, method, &retval, argc, argv, 1, nullptr) != SUCCESS) +#else + if (call_user_function(CG(function_table), (zval*) object, method, &retval, argc, argv) != SUCCESS) +#endif { // throw an exception, the function does not exist throw Error("Invalid call to "+Value(method).stringValue()); @@ -1353,9 +1359,13 @@ int Value::size() const // create a variable to hold the result zend_long result; - +#if PHP_VERSION_ID < 80000 // call the function return Z_OBJ_HT_P(_val)->count_elements(_val, &result) == SUCCESS ? result : 0; +#else + zend_object *zobj = Z_OBJ_P(_val); + return Z_OBJ_HT_P(_val)->count_elements(zobj, &result) == SUCCESS ? result : 0; +#endif } // not an array, return string size if this is a string @@ -1511,12 +1521,22 @@ bool Value::contains(const char *key, int size) const // leap out if no 'has_property' function is not set (which should normally not occur) if (!has_property) return false; +#if PHP_VERSION_ID < 80000 // the property must be a zval, turn the key into a value Value property(key, size); // 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) return has_property(_val, property._val, 0, nullptr); +#else + int retval = 0; + zend_string *tmp_str; + tmp_str = zend_string_init(key, size, 0); + retval = has_property(Z_OBJ_P(_val), tmp_str, 0, nullptr); + zend_string_release(tmp_str); + return retval; +#endif + } else { @@ -1587,8 +1607,13 @@ Value Value::get(const char *key, int size) const zend_class_entry* scope = EG(fake_scope) ? EG(fake_scope) : zend_get_executed_scope(); #endif // read the property +#if PHP_VERSION_ID < 80000 zval *property = zend_read_property(scope, _val, key, size, 0, &rv); +#else + zend_object *zobj = Z_OBJ_P(_val); + zval *property = zend_read_property(scope, zobj, key, size, 0, &rv); +#endif // wrap in value return Value(property); } @@ -1661,7 +1686,12 @@ void Value::setRaw(const char *key, int size, const Value &value) #else zend_class_entry* scope = EG(fake_scope) ? EG(fake_scope) : zend_get_executed_scope(); #endif +#if PHP_VERSION_ID < 80000 zend_update_property(scope, _val, key, size, value._val); +#else + zend_update_property(scope, Z_OBJ_P(_val), key, size, value._val); +#endif + } else { From a5d901489e852d610042f933030ea3f37dc74c3a Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Tue, 22 Feb 2022 21:50:16 -0700 Subject: [PATCH 061/101] PHP 8.1 support --- include/file.h | 9 +++++++++ zend/file.cpp | 23 ++++++++++++++++++++--- zend/value.cpp | 14 +++++++------- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/include/file.h b/include/file.h index 4fab1c59..42d30fa5 100644 --- a/include/file.h +++ b/include/file.h @@ -42,6 +42,15 @@ class PHPCPP_EXPORT File */ File(const char *name) : File(name, ::strlen(name)) {} + /** + * Alternative constructor with zend_string filename + * and size of the string + * + * @param name the filename + * @param size size of the filename + */ + File(const _zend_string *name, size_t size); + /** * Alternative constructor with a string object * @param name the filename diff --git a/zend/file.cpp b/zend/file.cpp index fa0d0127..4655e799 100644 --- a/zend/file.cpp +++ b/zend/file.cpp @@ -27,9 +27,20 @@ namespace Php { * @param size length of the filename */ File::File(const char *name, size_t size) + { + zend_string *tmp_str = zend_string_init(name, size, 0); + _path = zend_resolve_path(tmp_str); +} + +File::File(const _zend_string *name, size_t size) { - // resolve the path - _path = zend_resolve_path(name, size); + /** + * Assign `const` zend_string as a non-const variable to resolve typing error. + * error: invalid conversion from ‘const zend_string*’ {aka ‘const _zend_string*’} to ‘zend_string*’ {aka ‘_zend_string*’} + * zend_resolve_path now only accepts the filename argument. + */ + zend_string *tmp_str = zend_string_init_fast(ZSTR_VAL(name), ZSTR_LEN(name)); + _path = zend_resolve_path(tmp_str); } /** @@ -56,8 +67,14 @@ bool File::compile() // we are going to open the file zend_file_handle fileHandle; + /** + * zend_stream_open now only accepts the fileHandle object + * Filename must now be set through the object path. + */ + fileHandle.filename = _path; + // open the file - if (zend_stream_open(ZSTR_VAL(_path), &fileHandle) == FAILURE) return false; + if (zend_stream_open(&fileHandle) == 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); diff --git a/zend/value.cpp b/zend/value.cpp index 891495e2..1f40ecff 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1109,7 +1109,7 @@ 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_NOREF(_val); // run the conversion, when it fails we throw a fatal error that ends up in PHP space switch (type) { @@ -1629,7 +1629,7 @@ 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_NOREF(_val); // add the value (this will decrement refcount on any current variable) add_index_zval(_val, index, value._val); @@ -1678,7 +1678,7 @@ 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_NOREF(_val); // update the property #if PHP_VERSION_ID < 70100 @@ -1696,7 +1696,7 @@ void Value::setRaw(const char *key, int size, const Value &value) 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_NOREF(_val); // add the value (this will reduce the refcount of the current value) add_assoc_zval_ex(_val, key, size, value._val); @@ -1742,7 +1742,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_NOREF(_val); // remove the index zend_hash_index_del(Z_ARRVAL_P(_val.dereference()), index); @@ -1759,7 +1759,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_NOREF(_val); // in the zend header files, unsetting properties is redirected to setting it to null... add_property_null_ex(_val, key, size); @@ -1767,7 +1767,7 @@ 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_NOREF(_val); // remove the index zend_hash_del(Z_ARRVAL_P(_val.dereference()), String(key, size)); From 1806c8e4de5db5c94b1a665bf3641649091ea3b7 Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Mon, 2 Jan 2023 10:04:51 -0700 Subject: [PATCH 062/101] PHP-8.2 support taken from https://github.com/baronbiosys/PHP-CPP/pull/1/commits/58c1d78c62b431acc6caea845ff00a8efeb22746 --- zend/callable.cpp | 2 +- zend/classimpl.cpp | 10 ++++++++++ zend/classimpl.h | 13 +++++++++++-- zend/script.cpp | 2 ++ zend/value.cpp | 4 ++++ 5 files changed, 28 insertions(+), 3 deletions(-) diff --git a/zend/callable.cpp b/zend/callable.cpp index 388c65b8..b8982a2c 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -35,7 +35,7 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) Callable *callable = reinterpret_cast(info[argc].class_name); #else // Sanity check - assert(info[argc].type != 0 && info[argc].name == nullptr); + assert(ZEND_TYPE_IS_SET(info[argc].type) && info[argc].name == nullptr); // the callable we are retrieving #if PHP_VERSION_ID < 80000 Callable *callable = reinterpret_cast(info[argc].type); diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index b55367ef..f0767720 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -287,6 +287,8 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string * */ #if PHP_VERSION_ID < 80000 int ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr) +#elif PHP_VERSION_ID > 80200 +zend_result ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr, zend_bool check_only) #else int ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr, zend_bool check_only) #endif @@ -471,7 +473,11 @@ int ClassImpl::compare(zval *val1, zval *val2) * @param type * @return int */ +#if PHP_VERSION_ID >= 80200 +zend_result ClassImpl::cast(ZEND_OBJECT_OR_ZVAL val, zval *retval, int type) +#else int ClassImpl::cast(ZEND_OBJECT_OR_ZVAL val, zval *retval, int type) +#endif { // get the base c++ object Base *object = ObjectImpl::find(val)->object(); @@ -583,7 +589,11 @@ zend_object *ClassImpl::cloneObject(ZEND_OBJECT_OR_ZVAL val) * @param count * @return int */ +#if PHP_VERSION_ID >= 80200 +zend_result ClassImpl::countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count) +#else int ClassImpl::countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count) +#endif { // does it implement the countable interface? Countable *countable = dynamic_cast(ObjectImpl::find(object)->object()); diff --git a/zend/classimpl.h b/zend/classimpl.h index 939f8150..8bc99a8e 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -218,8 +218,11 @@ class ClassImpl * @param count * @return int */ +#if PHP_VERSION_ID >= 80200 + static zend_result countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count); +#else static int countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count); - +#endif /** * Function that is called when the object is used as an array in PHP * @param object The object on which it is called @@ -330,8 +333,10 @@ class ClassImpl */ #if PHP_VERSION_ID < 80000 static int getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr); -#else +#elif PHP_VERSION_ID < 80200 static int getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr, zend_bool check_only); +#else + static zend_result getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry, zend_function **func, zend_object **object_ptr, zend_bool check_only); #endif /** @@ -341,7 +346,11 @@ class ClassImpl * @param type * @return int */ +#if PHP_VERSION_ID >= 80200 + static zend_result cast(ZEND_OBJECT_OR_ZVAL object, zval *retval, int type); +#else static int cast(ZEND_OBJECT_OR_ZVAL object, zval *retval, int type); +#endif /** * Function to compare two objects diff --git a/zend/script.cpp b/zend/script.cpp index 2f197d55..31b76d37 100644 --- a/zend/script.cpp +++ b/zend/script.cpp @@ -39,6 +39,8 @@ zend_op_array *Script::compile(const char *name, const char *phpcode, size_t siz // compile the string #if PHP_VERSION_ID < 80000 return zend_compile_string(source._val, (char *)name); +#elif PHP_VERSION_ID >= 80200 + return zend_compile_string(zval_get_string(source._val), (char*)name, ZEND_COMPILE_POSITION_AT_OPEN_TAG); #else return zend_compile_string(zval_get_string(source._val), (char*)name); #endif diff --git a/zend/value.cpp b/zend/value.cpp index 1f40ecff..e3a7149c 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -851,7 +851,11 @@ bool Value::isCallable(const char *name) if (!(func->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE)) return true; // check the result ("Returns true to the fake Closure's __invoke") +#if PHP_VERSION_ID < 80200 bool result = func->common.scope == zend_ce_closure && zend_string_equals_literal(methodname.value(), ZEND_INVOKE_FUNC_NAME); +#else + bool result = func->common.scope == zend_ce_closure && zend_string_equals_cstr(methodname.value(), ZEND_INVOKE_FUNC_NAME, ::strlen(ZEND_INVOKE_FUNC_NAME)); +#endif // free resources (still don't get this code, copied from zend_builtin_functions.c) zend_string_release(func->common.function_name); From 27ad89e05bdd28a028863d9f193942d87ac29cda Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Nov 2023 10:46:57 +0100 Subject: [PATCH 063/101] some fixes because it no longer worked for php 7 --- zend/callable.cpp | 6 ++++++ zend/file.cpp | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/zend/callable.cpp b/zend/callable.cpp index b8982a2c..ebe144bf 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -100,6 +100,12 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int // install ourselves in the extra argument #if PHP_VERSION_ID < 70200 _argv[_argc + 1].class_name = reinterpret_cast(this); +#elif PHP_VERSION_ID < 80000 + // @todo this is broken. the zend engine, from 7.2 onwards copies over + // the struct and slices off the last element, because the num_args + // is incorrect in their view. another place to put this may be + // hiding it behind the fname + _argv[_argc + 1].type = reinterpret_cast(this); #else // @todo this is broken. the zend engine, from 7.2 onwards copies over // the struct and slices off the last element, because the num_args diff --git a/zend/file.cpp b/zend/file.cpp index 4655e799..de798a95 100644 --- a/zend/file.cpp +++ b/zend/file.cpp @@ -27,11 +27,21 @@ namespace Php { * @param size length of the filename */ File::File(const char *name, size_t size) - { +{ +#if PHP_VERSION_ID < 80000 + // resolve the path + _path = zend_resolve_path(name, size); +#else + // first convert the path, then read it zend_string *tmp_str = zend_string_init(name, size, 0); _path = zend_resolve_path(tmp_str); +#endif } +/** + * Constructor based on zend_string + */ +#if PHP_VERSION_ID >= 80000 File::File(const _zend_string *name, size_t size) { /** @@ -42,6 +52,7 @@ File::File(const _zend_string *name, size_t size) zend_string *tmp_str = zend_string_init_fast(ZSTR_VAL(name), ZSTR_LEN(name)); _path = zend_resolve_path(tmp_str); } +#endif /** * Destructor @@ -67,6 +78,7 @@ bool File::compile() // we are going to open the file zend_file_handle fileHandle; +#if PHP_VERSION_ID > 80000 /** * zend_stream_open now only accepts the fileHandle object * Filename must now be set through the object path. @@ -75,6 +87,10 @@ bool File::compile() // open the file if (zend_stream_open(&fileHandle) == FAILURE) return false; +#else + // open the file + if (zend_stream_open(ZSTR_VAL(_path), &fileHandle) == FAILURE) return false; +#endif // 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); From b028a268b4447b351a622231ccd217f3a65ebe9e Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Nov 2023 12:29:46 +0100 Subject: [PATCH 064/101] restored if_not_ref calls --- zend/value.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zend/value.cpp b/zend/value.cpp index e3a7149c..18b352b6 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1113,7 +1113,7 @@ 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_NOREF(_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // run the conversion, when it fails we throw a fatal error that ends up in PHP space switch (type) { @@ -1633,7 +1633,7 @@ 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_NOREF(_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); @@ -1682,7 +1682,7 @@ 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_NOREF(_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // update the property #if PHP_VERSION_ID < 70100 @@ -1700,7 +1700,7 @@ void Value::setRaw(const char *key, int size, const Value &value) else { // if this is not a reference variable, we should detach it to implement copy on write - SEPARATE_ZVAL_NOREF(_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, value._val); @@ -1763,7 +1763,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_NOREF(_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // in the zend header files, unsetting properties is redirected to setting it to null... add_property_null_ex(_val, key, size); @@ -1771,7 +1771,7 @@ 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_NOREF(_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // remove the index zend_hash_del(Z_ARRVAL_P(_val.dereference()), String(key, size)); From e368916b6105bdd59e76394f543ea67a60e57e1f Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Nov 2023 12:48:45 +0100 Subject: [PATCH 065/101] workaround to fixed removed SEPARATE_ZVAL_IF_NOT_REF method --- zend/macros.h | 26 ++++++++++++++++++++++++++ zend/value.cpp | 1 + 2 files changed, 27 insertions(+) create mode 100644 zend/macros.h diff --git a/zend/macros.h b/zend/macros.h new file mode 100644 index 00000000..e2513371 --- /dev/null +++ b/zend/macros.h @@ -0,0 +1,26 @@ +/** + * Macros.h + * + * Some macros have changed or have been removed between PHP versions. + * In this file we write custom versions of such macros to overcome that + * + * @author Emiel Bruijntjes + * @copyright 2023 Copernica BV + */ + +/** + * Include guard + */ +#pragma once + +/** + * The "SEPARATE_ZVAL_IF_NOT_REF()" macro has been removed + */ +#ifndef SEPARATE_ZVAL_IF_NOT_REF +#define SEPARATE_ZVAL_IF_NOT_REF(zv) do { \ + zval *_zv = (zv); \ + if (Z_TYPE_P(_zv) == IS_ARRAY) { \ + SEPARATE_ARRAY(_zv); \ + } \ + } while(0) +#endif diff --git a/zend/value.cpp b/zend/value.cpp index 18b352b6..65db5883 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -27,6 +27,7 @@ #include "includes.h" #include "string.h" #include "lowercase.h" +#include "macros.h" /** * Set up namespace From 5279a97506501df71bafba383cece33301431bad Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Nov 2023 15:05:44 +0100 Subject: [PATCH 066/101] fixed memory leak in Php::File (string was not deallocated) --- include/file.h | 15 ++++++--------- zend/file.cpp | 24 +++++------------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/include/file.h b/include/file.h index 42d30fa5..f88e61e7 100644 --- a/include/file.h +++ b/include/file.h @@ -42,15 +42,6 @@ class PHPCPP_EXPORT File */ File(const char *name) : File(name, ::strlen(name)) {} - /** - * Alternative constructor with zend_string filename - * and size of the string - * - * @param name the filename - * @param size size of the filename - */ - File(const _zend_string *name, size_t size); - /** * Alternative constructor with a string object * @param name the filename @@ -93,6 +84,12 @@ class PHPCPP_EXPORT File Value execute(); private: + /** + * The original path + * @var struct _zend_string* + */ + struct _zend_string *_original = nullptr; + /** * The full resolved path name * @var struct _zend_string* diff --git a/zend/file.cpp b/zend/file.cpp index de798a95..75741261 100644 --- a/zend/file.cpp +++ b/zend/file.cpp @@ -26,34 +26,17 @@ namespace Php { * @param name the filename * @param size length of the filename */ -File::File(const char *name, size_t size) +File::File(const char *name, size_t size) : _original(zend_string_init(name, size, 0)) { #if PHP_VERSION_ID < 80000 // resolve the path _path = zend_resolve_path(name, size); #else // first convert the path, then read it - zend_string *tmp_str = zend_string_init(name, size, 0); - _path = zend_resolve_path(tmp_str); + _path = zend_resolve_path(_original); #endif } -/** - * Constructor based on zend_string - */ -#if PHP_VERSION_ID >= 80000 -File::File(const _zend_string *name, size_t size) -{ - /** - * Assign `const` zend_string as a non-const variable to resolve typing error. - * error: invalid conversion from ‘const zend_string*’ {aka ‘const _zend_string*’} to ‘zend_string*’ {aka ‘_zend_string*’} - * zend_resolve_path now only accepts the filename argument. - */ - zend_string *tmp_str = zend_string_init_fast(ZSTR_VAL(name), ZSTR_LEN(name)); - _path = zend_resolve_path(tmp_str); -} -#endif - /** * Destructor */ @@ -61,6 +44,9 @@ File::~File() { // clean up path name if (_path) zend_string_release(_path); + + // clean up original path + if (_original) zend_string_release(_original); } /** From 595a99101008a98bf0c237803386330ddce7f76d Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Nov 2023 15:10:57 +0100 Subject: [PATCH 067/101] another no_ref call --- zend/value.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zend/value.cpp b/zend/value.cpp index 65db5883..421ef8f5 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1747,7 +1747,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_NOREF(_val); + SEPARATE_ZVAL_IF_NOT_REF(_val); // remove the index zend_hash_index_del(Z_ARRVAL_P(_val.dereference()), index); From 3820faba7054dff72f809ccebfc0ed626ebb1146 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Nov 2023 18:19:31 +0100 Subject: [PATCH 068/101] prepare for upcoming 2.4.0 release --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index debc48f7..9ea93eaf 100644 --- a/Makefile +++ b/Makefile @@ -49,8 +49,8 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # Otherwise only release verions changes. (version is MAJOR.MINOR.RELEASE) # -SONAME = 2.3 -VERSION = 2.3.5 +SONAME = 2.4 +VERSION = 2.4.0 # From c2b36b9843b4b98f67342f6c0a956d1217bcb948 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 7 Nov 2023 09:13:13 +0100 Subject: [PATCH 069/101] fix dependency (stdexcept.h was not correctly included) --- include/throwable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/throwable.h b/include/throwable.h index 8b9adafa..8878013b 100644 --- a/include/throwable.h +++ b/include/throwable.h @@ -7,9 +7,9 @@ * * @author Jasper van Eck * @author Emiel Bruijntjes - * @copyright 2013 - 2019 Copernica BV + * @copyright 2013 - 2023 Copernica BV */ -#include +#include #include /** From 3d791e05c2655facc556d89565ead4f71c4c6217 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 7 Nov 2023 09:30:16 +0100 Subject: [PATCH 070/101] for php 8 implementing just Traverable leads to an error, hence we implement IteratorAggregate --- zend/classimpl.cpp | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index f0767720..55795a37 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1538,9 +1538,26 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref else std::cerr << "Derived class " << name() << " is initialized before base class " << interface->name() << ": interface is ignored" << std::endl; } - // we may have to expose the Traversable or Serializable interfaces - if (_base->traversable()) zend_class_implements(_entry, 1, zend_ce_traversable); - if (_base->serializable()) zend_class_implements(_entry, 1, zend_ce_serializable); + // we may have to expose the Traversable + if (_base->traversable()) + { +#if PHP_VERSION_ID < 80000 + // up to php 7.x we can implement just the Traversable method (strictly speaking, it does not seem + // to be a user-space concern whether a class is traversable because of "iterator" or "iteratoraggregate" + zend_class_implements(_entry, 1, zend_ce_traversable); +#else + // from php 8.0 an error occurs if a class just implements traversable, without being an explicit + // subclass of Iterator or IteratorAggregate -- so we implement the iteraroraggregate instead + zend_class_implements(_entry, 1, zend_ce_aggregate); +#endif + } + + // check if the Serializable interface + if (_base->serializable()) + { + // add the Seriablable interface + zend_class_implements(_entry, 1, zend_ce_serializable); + } // instal the right modifier (to make the class an interface, abstract class, etc) _entry->ce_flags |= uint64_t(_type); From 413eed136d89655b1c55a8526d71e3cc0d172b6a Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 7 Nov 2023 22:27:49 +0100 Subject: [PATCH 071/101] update Makefile to use relative paths for the phpcpp.so symlink --- Makefile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 9ea93eaf..4e6035c2 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ else endif INSTALL_HEADERS = ${INSTALL_PREFIX}/include -INSTALL_LIB = ${INSTALL_PREFIX}/lib +INSTALL_LIB = ${INSTALL_PREFIX}/lib # @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.0 +VERSION = 2.4.1 # @@ -231,11 +231,11 @@ install: ${CP} include/*.h ${INSTALL_HEADERS}/phpcpp if [ -e ${PHP_SHARED_LIBRARY} ]; then \ ${CP} ${PHP_SHARED_LIBRARY} ${INSTALL_LIB}/; \ - ${LN} ${INSTALL_LIB}/${PHP_SHARED_LIBRARY} ${INSTALL_LIB}/libphpcpp.so.$(SONAME); \ - ${LN} ${INSTALL_LIB}/${PHP_SHARED_LIBRARY} ${INSTALL_LIB}/libphpcpp.so; \ + ${LN} ${PHP_SHARED_LIBRARY} ${INSTALL_LIB}/libphpcpp.so.$(SONAME); \ + ${LN} ${PHP_SHARED_LIBRARY} ${INSTALL_LIB}/libphpcpp.so; \ fi if [ -e ${PHP_STATIC_LIBRARY} ]; then ${CP} ${PHP_STATIC_LIBRARY} ${INSTALL_LIB}/; \ - ${LN} ${INSTALL_LIB}/${PHP_STATIC_LIBRARY} ${INSTALL_LIB}/libphpcpp.a; \ + ${LN} ${PHP_STATIC_LIBRARY} ${INSTALL_LIB}/libphpcpp.a; \ fi if `which ldconfig`; then \ ldconfig; \ From 09d4ce990fa4feef06ea2adf92d3782a4795727a Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Thu, 16 Nov 2023 09:04:41 +0100 Subject: [PATCH 072/101] Fixed registering of parameters of unspecified type for PHP 8 and up --- zend/callable.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zend/callable.h b/zend/callable.h index 7402210e..f83a6341 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -205,8 +205,8 @@ class Callable case Type::Callable: info->type_hint = IS_CALLABLE; break; // anything that can be invoked default: info->type_hint = IS_UNDEF; break; // if not specified we allow anything #elif PHP_VERSION_ID >= 80000 - case Type::Undefined: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_UNDEF, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // undefined means we'll accept any type - case Type::Null: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_UNDEF, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // this is likely an error, what good would accepting NULL be? accept anything + case Type::Undefined: info->type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS((unsigned int)arg.byReference(), 0, 0)); break; // undefined means we'll accept any type + case Type::Null: info->type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS((unsigned int)arg.byReference(), 0, 0)); break; // this is likely an error, what good would accepting NULL be? accept anything case Type::False: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // accept true as well ;) case Type::True: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // accept false as well case Type::Bool: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // any bool will do, true, false, the options are limitless From 72e19606544042a9fe1702fdebf228fbbfc306e7 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Thu, 16 Nov 2023 09:30:36 +0100 Subject: [PATCH 073/101] update makefile to prepare for upcoming version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4e6035c2..07665a9b 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.1 +VERSION = 2.4.2 # From b1b74321ee7bda3dc486cbd2ecaf66b418b6bf60 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Sun, 19 Nov 2023 20:08:48 -0500 Subject: [PATCH 074/101] reorder PHP_VERSION_ID conditionals for consistency --- common/modifiers.cpp | 18 ++++++------- zend/callable.cpp | 8 +++--- zend/callable.h | 34 ++++++++++++------------- zend/classimpl.cpp | 60 +++++++++++++++++++++++--------------------- zend/classimpl.h | 26 +++++++++---------- zend/file.cpp | 14 +++++------ zend/script.cpp | 10 ++++---- zend/value.cpp | 14 +++++------ 8 files changed, 93 insertions(+), 91 deletions(-) diff --git a/common/modifiers.cpp b/common/modifiers.cpp index c40066c5..be3c11fe 100644 --- a/common/modifiers.cpp +++ b/common/modifiers.cpp @@ -20,15 +20,7 @@ namespace Php { /** * The modifiers are constants */ -#if PHP_VERSION_ID >= 70400 -const int Static = 0x10; -const int Abstract = 0x40; -const int Final = 0x20; -const int Public = 0x01; -const int Protected = 0x02; -const int Private = 0x04; -const int Const = 0; -#else +#if PHP_VERSION_ID < 70400 const int Static = 0x01; const int Abstract = 0x02; const int Final = 0x04; @@ -36,6 +28,14 @@ const int Public = 0x100; const int Protected = 0x200; const int Private = 0x400; const int Const = 0; +#else +const int Static = 0x10; +const int Abstract = 0x40; +const int Final = 0x20; +const int Public = 0x01; +const int Protected = 0x02; +const int Private = 0x04; +const int Const = 0; #endif /** diff --git a/zend/callable.cpp b/zend/callable.cpp index ebe144bf..20134b2f 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -37,11 +37,11 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) // Sanity check assert(ZEND_TYPE_IS_SET(info[argc].type) && info[argc].name == nullptr); // the callable we are retrieving -#if PHP_VERSION_ID < 80000 +# if PHP_VERSION_ID < 80000 Callable *callable = reinterpret_cast(info[argc].type); -#else +# else Callable *callable = reinterpret_cast(info[argc].type.ptr); -#endif +# endif #endif // check if sufficient parameters were passed (for some reason this check @@ -64,7 +64,7 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) { // get the result Value result(callable->invoke(params)); - + // return a full copy of the zval, and do not destruct it RETVAL_ZVAL(result._val, 1, 0); } diff --git a/zend/callable.h b/zend/callable.h index f83a6341..89561d5f 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -204,7 +204,23 @@ class Callable case Type::Object: info->type_hint = IS_OBJECT; break; // must be an object of the given classname case Type::Callable: info->type_hint = IS_CALLABLE; break; // anything that can be invoked default: info->type_hint = IS_UNDEF; break; // if not specified we allow anything -#elif PHP_VERSION_ID >= 80000 +#elif PHP_VERSION_ID < 80000 + case Type::Undefined: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // undefined means we'll accept any type + case Type::Null: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // this is likely an error, what good would accepting NULL be? accept anything + case Type::False: info->type = ZEND_TYPE_ENCODE(_IS_BOOL, arg.allowNull()); break; // accept true as well ;) + case Type::True: info->type = ZEND_TYPE_ENCODE(_IS_BOOL, arg.allowNull()); break; // accept false as well + case Type::Bool: info->type = ZEND_TYPE_ENCODE(_IS_BOOL, arg.allowNull()); break; // any bool will do, true, false, the options are limitless + case Type::Numeric: info->type = ZEND_TYPE_ENCODE(IS_LONG, arg.allowNull()); break; // accept integers here + case Type::Float: info->type = ZEND_TYPE_ENCODE(IS_DOUBLE, arg.allowNull()); break; // floating-point values welcome too + case Type::String: info->type = ZEND_TYPE_ENCODE(IS_STRING, arg.allowNull()); break; // accept strings, should auto-cast objects with __toString as well + case Type::Array: info->type = ZEND_TYPE_ENCODE(IS_ARRAY, arg.allowNull()); break; // array of anything (individual members cannot be restricted) + case Type::Object: // if there is a classname and the argument is not nullable, it's simply the classname + if (!arg.classname()) info->type = ZEND_TYPE_ENCODE(IS_OBJECT, arg.allowNull()); + else info->type = (zend_type)arg.encoded(); + break; + case Type::Callable: info->type = ZEND_TYPE_ENCODE(IS_CALLABLE, arg.allowNull()); break; // anything that can be invoked + default: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // if not specified we allow anything +#else case Type::Undefined: info->type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS((unsigned int)arg.byReference(), 0, 0)); break; // undefined means we'll accept any type case Type::Null: info->type = (zend_type) ZEND_TYPE_INIT_NONE(_ZEND_ARG_INFO_FLAGS((unsigned int)arg.byReference(), 0, 0)); break; // this is likely an error, what good would accepting NULL be? accept anything case Type::False: info->type = (zend_type) ZEND_TYPE_INIT_CODE(_IS_BOOL, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // accept true as well ;) @@ -224,22 +240,6 @@ class Callable case Type::Callable: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_CALLABLE, arg.allowNull(), 0); break; // anything that can be invoke default: info->type = ZEND_TYPE_INIT_CODE(IS_UNDEF, 0, _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // if not specified we allow anything -#else - case Type::Undefined: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // undefined means we'll accept any type - case Type::Null: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // this is likely an error, what good would accepting NULL be? accept anything - case Type::False: info->type = ZEND_TYPE_ENCODE(_IS_BOOL, arg.allowNull()); break; // accept true as well ;) - case Type::True: info->type = ZEND_TYPE_ENCODE(_IS_BOOL, arg.allowNull()); break; // accept false as well - case Type::Bool: info->type = ZEND_TYPE_ENCODE(_IS_BOOL, arg.allowNull()); break; // any bool will do, true, false, the options are limitless - case Type::Numeric: info->type = ZEND_TYPE_ENCODE(IS_LONG, arg.allowNull()); break; // accept integers here - case Type::Float: info->type = ZEND_TYPE_ENCODE(IS_DOUBLE, arg.allowNull()); break; // floating-point values welcome too - case Type::String: info->type = ZEND_TYPE_ENCODE(IS_STRING, arg.allowNull()); break; // accept strings, should auto-cast objects with __toString as well - case Type::Array: info->type = ZEND_TYPE_ENCODE(IS_ARRAY, arg.allowNull()); break; // array of anything (individual members cannot be restricted) - case Type::Object: // if there is a classname and the argument is not nullable, it's simply the classname - if (!arg.classname()) info->type = ZEND_TYPE_ENCODE(IS_OBJECT, arg.allowNull()); - else info->type = (zend_type)arg.encoded(); - break; - case Type::Callable: info->type = ZEND_TYPE_ENCODE(IS_CALLABLE, arg.allowNull()); break; // anything that can be invoked - default: info->type = ZEND_TYPE_ENCODE(IS_UNDEF, arg.allowNull()); break; // if not specified we allow anything #endif } #if PHP_VERSION_ID < 80000 diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 55795a37..d5fd4ac8 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -287,10 +287,10 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string * */ #if PHP_VERSION_ID < 80000 int ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr) -#elif PHP_VERSION_ID > 80200 -zend_result ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr, zend_bool check_only) -#else +#elif PHP_VERSION_ID < 80200 int ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr, zend_bool check_only) +#else +zend_result ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry **entry_ptr, zend_function **func, zend_object **object_ptr, zend_bool check_only) #endif { // it is really unbelievable how the Zend engine manages to implement every feature @@ -473,10 +473,10 @@ int ClassImpl::compare(zval *val1, zval *val2) * @param type * @return int */ -#if PHP_VERSION_ID >= 80200 -zend_result ClassImpl::cast(ZEND_OBJECT_OR_ZVAL val, zval *retval, int type) -#else +#if PHP_VERSION_ID < 80200 int ClassImpl::cast(ZEND_OBJECT_OR_ZVAL val, zval *retval, int type) +#else +zend_result ClassImpl::cast(ZEND_OBJECT_OR_ZVAL val, zval *retval, int type) #endif { // get the base c++ object @@ -589,10 +589,10 @@ zend_object *ClassImpl::cloneObject(ZEND_OBJECT_OR_ZVAL val) * @param count * @return int */ -#if PHP_VERSION_ID >= 80200 -zend_result ClassImpl::countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count) -#else +#if PHP_VERSION_ID < 80200 int ClassImpl::countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count) +#else +zend_result ClassImpl::countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count) #endif { // does it implement the countable interface? @@ -995,12 +995,12 @@ PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(ZEND_OBJECT_OR_ZVAL object, { // check if it could be set if (iter->second->set(base, value)) { -#if PHP_VERSION_ID >= 70400 - return value; -#else +#if PHP_VERSION_ID < 70400 return; +#else + return value; #endif - } + } // read-only property zend_error(E_ERROR, "Unable to write to read-only property %s", (const char *)key); @@ -1010,19 +1010,19 @@ PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(ZEND_OBJECT_OR_ZVAL object, { // __set() function was not overridden by user, check if there is a default if (!std_object_handlers.write_property) { -#if PHP_VERSION_ID >= 70400 - return value; +#if PHP_VERSION_ID < 70400 + return; #else - return; + return value; #endif } // call the default std_object_handlers.write_property(object, name, value, cache_slot); -#if PHP_VERSION_ID >= 70400 - return value; +#if PHP_VERSION_ID < 70400 + return; #else - return; + return value; #endif } catch (Throwable &throwable) @@ -1030,7 +1030,9 @@ PHP_WRITE_PROP_HANDLER_TYPE ClassImpl::writeProperty(ZEND_OBJECT_OR_ZVAL object, // object was not caught by the extension, let it end up in user space throwable.rethrow(); } -#if PHP_VERSION_ID >= 70400 +#if PHP_VERSION_ID < 70400 + return; +#else return value; #endif } @@ -1252,7 +1254,7 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje // retrieve the traversable object Traversable *traversable = dynamic_cast(ObjectImpl::find(object)->object()); - + // use might throw an exception in the getIterator() function try { @@ -1263,10 +1265,10 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje // the iteraters itself, we can no longer let c++ allocate the buffer + object // directly, so we first allocate the buffer, which is going to be cleaned up by php) auto *buffer = emalloc(sizeof(IteratorImpl)); - + // and then we use placement-new to allocate the implementation auto *wrapper = new(buffer)IteratorImpl(object, userspace); - + // done return wrapper->implementation(); } @@ -1473,7 +1475,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref INIT_CLASS_ENTRY_EX(entry, _name.c_str(), _name.size(), entries()); // we need a special constructor, but only for real classes, not for interfaces. - // (in fact: from php 7.4 onwards the create_object method is part of union + // (in fact: from php 7.4 onwards the create_object method is part of union // together with the interface_gets_implemented method, which causes a crash // when the create_object property is set for an interface) if (_type != ClassType::Interface) entry.create_object = &ClassImpl::createObject; @@ -1482,7 +1484,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref entry.get_static_method = &ClassImpl::getStaticMethod; // for traversable classes we install a special method to get the iterator - if (_base->traversable()) + if (_base->traversable()) { // install iterator functions entry.get_iterator = &ClassImpl::getIterator; @@ -1527,7 +1529,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // register the class _entry = zend_register_internal_class(&entry); } - + // register the interfaces for (auto &interface : _interfaces) { @@ -1537,9 +1539,9 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref // otherwise report an error else std::cerr << "Derived class " << name() << " is initialized before base class " << interface->name() << ": interface is ignored" << std::endl; } - + // we may have to expose the Traversable - if (_base->traversable()) + if (_base->traversable()) { #if PHP_VERSION_ID < 80000 // up to php 7.x we can implement just the Traversable method (strictly speaking, it does not seem @@ -1551,7 +1553,7 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref zend_class_implements(_entry, 1, zend_ce_aggregate); #endif } - + // check if the Serializable interface if (_base->serializable()) { diff --git a/zend/classimpl.h b/zend/classimpl.h index 8bc99a8e..37f6ffcb 100644 --- a/zend/classimpl.h +++ b/zend/classimpl.h @@ -13,17 +13,17 @@ */ namespace Php { -#if PHP_VERSION_ID >= 70400 -# define PHP_WRITE_PROP_HANDLER_TYPE zval * +#if PHP_VERSION_ID < 70400 +# define PHP_WRITE_PROP_HANDLER_TYPE void #else -# define PHP_WRITE_PROP_HANDLER_TYPE void +# define PHP_WRITE_PROP_HANDLER_TYPE zval * #endif #if PHP_VERSION_ID < 80000 -#define ZEND_OBJECT_OR_ZVAL zval * -#define ZEND_STRING_OR_ZVAL zval * +# define ZEND_OBJECT_OR_ZVAL zval * +# define ZEND_STRING_OR_ZVAL zval * #else -#define ZEND_OBJECT_OR_ZVAL zend_object * -#define ZEND_STRING_OR_ZVAL zend_string * +# define ZEND_OBJECT_OR_ZVAL zend_object * +# define ZEND_STRING_OR_ZVAL zend_string * #endif /** * Class definition @@ -218,10 +218,10 @@ class ClassImpl * @param count * @return int */ -#if PHP_VERSION_ID >= 80200 - static zend_result countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count); -#else +#if PHP_VERSION_ID < 80200 static int countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count); +#else + static zend_result countElements(ZEND_OBJECT_OR_ZVAL object, zend_long *count); #endif /** * Function that is called when the object is used as an array in PHP @@ -346,10 +346,10 @@ class ClassImpl * @param type * @return int */ -#if PHP_VERSION_ID >= 80200 - static zend_result cast(ZEND_OBJECT_OR_ZVAL object, zval *retval, int type); -#else +#if PHP_VERSION_ID < 80200 static int cast(ZEND_OBJECT_OR_ZVAL object, zval *retval, int type); +#else + static zend_result cast(ZEND_OBJECT_OR_ZVAL object, zval *retval, int type); #endif /** diff --git a/zend/file.cpp b/zend/file.cpp index 75741261..db364b87 100644 --- a/zend/file.cpp +++ b/zend/file.cpp @@ -28,9 +28,9 @@ namespace Php { */ File::File(const char *name, size_t size) : _original(zend_string_init(name, size, 0)) { -#if PHP_VERSION_ID < 80000 +#if PHP_VERSION_ID < 80100 // resolve the path - _path = zend_resolve_path(name, size); + _path = zend_resolve_path(name, size); #else // first convert the path, then read it _path = zend_resolve_path(_original); @@ -44,7 +44,7 @@ File::~File() { // clean up path name if (_path) zend_string_release(_path); - + // clean up original path if (_original) zend_string_release(_original); } @@ -64,7 +64,10 @@ bool File::compile() // we are going to open the file zend_file_handle fileHandle; -#if PHP_VERSION_ID > 80000 +#if PHP_VERSION_ID < 80100 + // open the file + if (zend_stream_open(ZSTR_VAL(_path), &fileHandle) == FAILURE) return false; +#else /** * zend_stream_open now only accepts the fileHandle object * Filename must now be set through the object path. @@ -73,9 +76,6 @@ bool File::compile() // open the file if (zend_stream_open(&fileHandle) == FAILURE) return false; -#else - // open the file - if (zend_stream_open(ZSTR_VAL(_path), &fileHandle) == FAILURE) return false; #endif // make sure the path name is stored in the handle (@todo: is this necessary? do we need the copy?) diff --git a/zend/script.cpp b/zend/script.cpp index 31b76d37..768f5295 100644 --- a/zend/script.cpp +++ b/zend/script.cpp @@ -35,14 +35,14 @@ zend_op_array *Script::compile(const char *name, const char *phpcode, size_t siz // remember the old compiler options, and set new compiler options CompilerOptions options(ZEND_COMPILE_DEFAULT_FOR_EVAL); - + // compile the string #if PHP_VERSION_ID < 80000 return zend_compile_string(source._val, (char *)name); -#elif PHP_VERSION_ID >= 80200 - return zend_compile_string(zval_get_string(source._val), (char*)name, ZEND_COMPILE_POSITION_AT_OPEN_TAG); -#else +#elif PHP_VERSION_ID < 80200 return zend_compile_string(zval_get_string(source._val), (char*)name); +#else + return zend_compile_string(zval_get_string(source._val), (char*)name, ZEND_COMPILE_POSITION_AT_OPEN_TAG); #endif } @@ -86,7 +86,7 @@ Value Script::execute() const { // pass on to opcodes if (!_opcodes) return nullptr; - + // execute opcodes return _opcodes->execute(); } diff --git a/zend/value.cpp b/zend/value.cpp index 421ef8f5..bfaf8b57 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -163,7 +163,7 @@ Value::Value(struct _zval_struct *val, bool ref) // increment refcount ++GC_REFCOUNT(ref); #else - // increment refcount + // increment refcount GC_ADDREF(ref); #endif // store the reference in our value @@ -315,7 +315,7 @@ Php::Zval Value::detach(bool keeprefcount) void Value::invalidate() { // do nothing if object is already undefined - if (Z_TYPE_P(_val) == IS_UNDEF) return; + if (Z_TYPE_P(_val) == IS_UNDEF) return; // call destructor zval_ptr_dtor(_val); @@ -766,7 +766,7 @@ static Value do_exec(const zval *object, zval *method, int argc, zval *argv) // remember current state of the PHP engine State state; - + // 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.. @@ -1514,11 +1514,11 @@ bool Value::contains(const char *key, int size) const } else if (isObject()) { -#if PHP_VERSION_ID >= 70400 // retrieve the object pointer and check whether the property we are trying to retrieve - if (zend_check_property_access(Z_OBJ_P(_val), String(key, size), 0) == FAILURE) return false; -#else +#if PHP_VERSION_ID < 70400 if (zend_check_property_access(Z_OBJ_P(_val), String(key, size)) == FAILURE) return false; +#else + if (zend_check_property_access(Z_OBJ_P(_val), String(key, size), 0) == FAILURE) return false; #endif // check if the 'has_property' method is available for this object auto *has_property = Z_OBJ_HT_P(_val)->has_property; @@ -1615,7 +1615,7 @@ Value Value::get(const char *key, int size) const #if PHP_VERSION_ID < 80000 zval *property = zend_read_property(scope, _val, key, size, 0, &rv); #else - zend_object *zobj = Z_OBJ_P(_val); + zend_object *zobj = Z_OBJ_P(_val); zval *property = zend_read_property(scope, zobj, key, size, 0, &rv); #endif From 2ddc54a7faa2f17dc81c5262533a3cd27154e6fb Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Mon, 20 Nov 2023 03:10:34 +0000 Subject: [PATCH 075/101] silence warning that `result` may be uninitialized --- zend/value.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zend/value.cpp b/zend/value.cpp index 421ef8f5..934d8598 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -292,7 +292,7 @@ Value::~Value() Php::Zval Value::detach(bool keeprefcount) { // the return value - Php::Zval result; + Php::Zval result {}; // copy the value ZVAL_COPY_VALUE(result, _val); From f2faf7ea3266be1b7b0f51bb51cdad8564950994 Mon Sep 17 00:00:00 2001 From: Anthon Pang Date: Tue, 21 Nov 2023 20:49:19 +0000 Subject: [PATCH 076/101] zend_empty_string was added in php 7.2 --- zend/classimpl.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index d5fd4ac8..85dcf6ff 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -311,6 +311,11 @@ zend_result ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry * function->arg_flags[1] = 0; function->arg_flags[2] = 0; function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER; +#if PHP_VERSION_ID < 70200 + zend_string *zend_empty_string = zend_string_alloc(sizeof("")-1, 1); + ZSTR_VAL(zend_empty_string)[0] = '\0'; + ZSTR_LEN(zend_empty_string) = 0; +#endif function->function_name = zend_empty_string; // should not be null, as this is free'ed by zend when doing exception handling function->scope = *entry_ptr; function->prototype = nullptr; From cfc3460c851a68ac7da13f59074d894739414c66 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 24 Nov 2023 07:33:23 +0100 Subject: [PATCH 077/101] we now use the same objects for the shared and static object files (because they both need the -fpic flag anyway) (resolves #378) --- .gitignore | 3 +-- Makefile | 49 +++++++++++++++++-------------------------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/.gitignore b/.gitignore index 64fc9284..9339f573 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,5 @@ *.txt *.a.* *.so.* -shared/ -static/ +build/ .vscode/ \ No newline at end of file diff --git a/Makefile b/Makefile index 07665a9b..e6a27c46 100644 --- a/Makefile +++ b/Makefile @@ -101,10 +101,7 @@ endif # you want to leave that flag out on production servers). # -COMPILER_FLAGS = -Wall -c -std=c++11 -fvisibility=hidden -DBUILDING_PHPCPP -Wno-write-strings -MD -SHARED_COMPILER_FLAGS = -fpic -STATIC_COMPILER_FLAGS = -PHP_COMPILER_FLAGS = ${COMPILER_FLAGS} `${PHP_CONFIG} --includes` +COMPILER_FLAGS = -Wall -c -std=c++11 -fvisibility=hidden -DBUILDING_PHPCPP -Wno-write-strings -MD -fpic `${PHP_CONFIG} --includes` # # Linker flags @@ -160,16 +157,14 @@ PHP_SOURCES = $(wildcard zend/*.cpp) # library. We also use a Makefile function here that takes all source files. # -COMMON_SHARED_OBJECTS = $(COMMON_SOURCES:%.cpp=shared/%.o) -PHP_SHARED_OBJECTS = $(PHP_SOURCES:%.cpp=shared/%.o) -COMMON_STATIC_OBJECTS = $(COMMON_SOURCES:%.cpp=static/%.o) -PHP_STATIC_OBJECTS = $(PHP_SOURCES:%.cpp=static/%.o) +COMMON_OBJECTS = $(COMMON_SOURCES:%.cpp=build/%.o) +PHP_OBJECTS = $(PHP_SOURCES:%.cpp=build/%.o) # # Dependencies # -DEPENDENCIES = $(wildcard shared/common/*.d) $(wildcard shared/zend/*.d) $(wildcard static/common/*.d) $(wildcard static/zend/*.d) +DEPENDENCIES = $(wildcard build/common/*.d) $(wildcard build/zend/*.d) # # End of the variables section. Here starts the list of instructions and @@ -190,36 +185,26 @@ phpcpp: ${PHP_SHARED_LIBRARY} ${PHP_STATIC_LIBRARY} @echo @echo "Build complete." -${PHP_SHARED_LIBRARY}: shared_directories ${COMMON_SHARED_OBJECTS} ${PHP_SHARED_OBJECTS} - ${LINKER} ${PHP_LINKER_FLAGS} -Wl,${LINKER_SONAME_OPTION},libphpcpp.so.$(SONAME) -o $@ ${COMMON_SHARED_OBJECTS} ${PHP_SHARED_OBJECTS} +${PHP_SHARED_LIBRARY}: build_directories ${COMMON_OBJECTS} ${PHP_OBJECTS} + ${LINKER} ${PHP_LINKER_FLAGS} -Wl,${LINKER_SONAME_OPTION},libphpcpp.so.$(SONAME) -o $@ ${COMMON_OBJECTS} ${PHP_OBJECTS} -${PHP_STATIC_LIBRARY}: static_directories ${COMMON_STATIC_OBJECTS} ${PHP_STATIC_OBJECTS} - ${ARCHIVER} $@ ${COMMON_STATIC_OBJECTS} ${PHP_STATIC_OBJECTS} +${PHP_STATIC_LIBRARY}: build_directories ${COMMON_OBJECTS} ${PHP_OBJECTS} + ${ARCHIVER} $@ ${COMMON_OBJECTS} ${PHP_OBJECTS} -shared_directories: - ${MKDIR} shared/common - ${MKDIR} shared/zend - -static_directories: - ${MKDIR} static/common - ${MKDIR} static/zend +build_directories: + ${MKDIR} build/common + ${MKDIR} build/zend clean: - ${RM} shared ${PHP_SHARED_LIBRARY} - ${RM} static ${PHP_STATIC_LIBRARY} + ${RM} build ${PHP_SHARED_LIBRARY} ${PHP_STATIC_LIBRARY} find -name *.o | xargs ${RM} + find -name *.d | xargs ${RM} -${COMMON_SHARED_OBJECTS}: - ${COMPILER} ${PHP_COMPILER_FLAGS} ${SHARED_COMPILER_FLAGS} -o $@ ${@:shared/%.o=%.cpp} - -${COMMON_STATIC_OBJECTS}: - ${COMPILER} ${PHP_COMPILER_FLAGS} ${STATIC_COMPILER_FLAGS} -o $@ ${@:static/%.o=%.cpp} - -${PHP_SHARED_OBJECTS}: - ${COMPILER} ${PHP_COMPILER_FLAGS} ${SHARED_COMPILER_FLAGS} -o $@ ${@:shared/%.o=%.cpp} +${COMMON_OBJECTS}: + ${COMPILER} ${COMPILER_FLAGS} -o $@ ${@:build/%.o=%.cpp} -${PHP_STATIC_OBJECTS}: - ${COMPILER} ${PHP_COMPILER_FLAGS} ${STATIC_COMPILER_FLAGS} -o $@ ${@:static/%.o=%.cpp} +${PHP_OBJECTS}: + ${COMPILER} ${COMPILER_FLAGS} -o $@ ${@:build/%.o=%.cpp} # The if statements below must be seen as single line by make From 5890384df26c9480f9376b2a9aa8c9b8f95c4351 Mon Sep 17 00:00:00 2001 From: Tomas Ebenlendr Date: Tue, 16 Apr 2024 09:27:40 +0200 Subject: [PATCH 078/101] Disable automatic conversions from void * to bool. It leads to various bugs, e.g., when "struct * _zend_string" is passed where zval was in php < 8.0. --- include/class.h | 1 + include/classbase.h | 1 + include/constant.h | 1 + include/hashmember.h | 10 ++++++++++ include/ini.h | 1 + include/value.h | 16 ++++++++++++++++ 6 files changed, 30 insertions(+) diff --git a/include/class.h b/include/class.h index e18220ad..d3f33673 100644 --- a/include/class.h +++ b/include/class.h @@ -227,6 +227,7 @@ class PHPCPP_EXPORT Class : private ClassBase Class &property(const char *name, const char *value, int flags = Public) { ClassBase::property(name, value, flags); return *this; } Class &property(const char *name, const std::string &value, int flags = Public) { ClassBase::property(name, value, flags); return *this; } Class &property(const char *name, bool value, int flags = Public) { ClassBase::property(name, value, flags); return *this; } + Class &property(const char *name, const void *value, int flags = Public) = delete; Class &property(const char *name, double value, int flags = Public) { ClassBase::property(name, value, flags); return *this; } /** diff --git a/include/classbase.h b/include/classbase.h index 3e010719..dfa2f38b 100644 --- a/include/classbase.h +++ b/include/classbase.h @@ -273,6 +273,7 @@ class PHPCPP_EXPORT ClassBase void property(const char *name, int32_t value, int flags = Php::Public); void property(const char *name, int64_t value, int flags = Php::Public); void property(const char *name, bool value, int flags = Php::Public); + void property(const char *name, const void *value, int flags = Php::Public) = delete; void property(const char *name, char value, int flags = Php::Public); void property(const char *name, const std::string &value, int flags = Php::Public); void property(const char *name, const char *value, int flags = Php::Public); diff --git a/include/constant.h b/include/constant.h index bb3a2d89..f90966a2 100644 --- a/include/constant.h +++ b/include/constant.h @@ -44,6 +44,7 @@ class PHPCPP_EXPORT Constant * @param value Constant's value */ Constant(const char *name, bool value); + Constant(const char *name, const void *value) = delete; /** * Constructor to create a constant for a 32-bit integer diff --git a/include/hashmember.h b/include/hashmember.h index 378ee905..6e8d458d 100644 --- a/include/hashmember.h +++ b/include/hashmember.h @@ -191,6 +191,7 @@ class PHPCPP_EXPORT HashMember : private HashParent HashMember &operator+=(int32_t value) { return operator=(this->value() + value); } HashMember &operator+=(int64_t value) { return operator=(this->value() + value); } HashMember &operator+=(bool value) { return operator=(this->value() + value); } + HashMember &operator+=(const void *value) = delete; HashMember &operator+=(char value) { return operator=(this->value() + value); } HashMember &operator+=(const std::string &value) { return operator=(this->value() + value); } HashMember &operator+=(const char *value) { return operator=(this->value() + value); } @@ -206,6 +207,7 @@ class PHPCPP_EXPORT HashMember : private HashParent HashMember &operator-=(int32_t value) { return operator=(this->value() - value); } HashMember &operator-=(int64_t value) { return operator=(this->value() - value); } HashMember &operator-=(bool value) { return operator=(this->value() - value); } + HashMember &operator-=(const void *value) = delete; HashMember &operator-=(char value) { return operator=(this->value() - value); } HashMember &operator-=(const std::string &value) { return operator=(this->value() - value); } HashMember &operator-=(const char *value) { return operator=(this->value() - value); } @@ -221,6 +223,7 @@ class PHPCPP_EXPORT HashMember : private HashParent HashMember &operator*=(int32_t value) { return operator=(this->value() * value); } HashMember &operator*=(int64_t value) { return operator=(this->value() * value); } HashMember &operator*=(bool value) { return operator=(this->value() * value); } + HashMember &operator*=(const void *value) = delete; HashMember &operator*=(char value) { return operator=(this->value() * value); } HashMember &operator*=(const std::string &value) { return operator=(this->value() * value); } HashMember &operator*=(const char *value) { return operator=(this->value() * value); } @@ -236,6 +239,7 @@ class PHPCPP_EXPORT HashMember : private HashParent HashMember &operator/=(int32_t value) { return operator=(this->value() / value); } HashMember &operator/=(int64_t value) { return operator=(this->value() / value); } HashMember &operator/=(bool value) { return operator=(this->value() / value); } + HashMember &operator/=(const void *value) = delete; HashMember &operator/=(char value) { return operator=(this->value() / value); } HashMember &operator/=(const std::string &value) { return operator=(this->value() / value); } HashMember &operator/=(const char *value) { return operator=(this->value() / value); } @@ -251,6 +255,7 @@ class PHPCPP_EXPORT HashMember : private HashParent HashMember &operator%=(int32_t value) { return operator=(this->value() % value); } HashMember &operator%=(int64_t value) { return operator=(this->value() % value); } HashMember &operator%=(bool value) { return operator=(this->value() % value); } + HashMember &operator%=(const void *value) = delete; HashMember &operator%=(char value) { return operator=(this->value() % value); } HashMember &operator%=(const std::string &value) { return operator=(this->value() % value); } HashMember &operator%=(const char *value) { return operator=(this->value() % value); } @@ -266,6 +271,7 @@ class PHPCPP_EXPORT HashMember : private HashParent Value operator+(int32_t value) { return this->value() + value; } Value operator+(int64_t value) { return this->value() + value; } Value operator+(bool value) { return this->value() + value; } + Value operator+(const void *value) = delete; Value operator+(char value) { return this->value() + value; } Value operator+(const std::string &value) { return this->value() + value; } Value operator+(const char *value) { return this->value() + value; } @@ -281,6 +287,7 @@ class PHPCPP_EXPORT HashMember : private HashParent Value operator-(int32_t value) { return this->value() - value; } Value operator-(int64_t value) { return this->value() - value; } Value operator-(bool value) { return this->value() - value; } + Value operator-(const void *value) = delete; Value operator-(char value) { return this->value() - value; } Value operator-(const std::string &value) { return this->value() - value; } Value operator-(const char *value) { return this->value() - value; } @@ -296,6 +303,7 @@ class PHPCPP_EXPORT HashMember : private HashParent Value operator*(int32_t value) { return this->value() * value; } Value operator*(int64_t value) { return this->value() * value; } Value operator*(bool value) { return this->value() * value; } + Value operator*(const void *value) = delete; Value operator*(char value) { return this->value() * value; } Value operator*(const std::string &value) { return this->value() * value; } Value operator*(const char *value) { return this->value() * value; } @@ -311,6 +319,7 @@ class PHPCPP_EXPORT HashMember : private HashParent Value operator/(int32_t value) { return this->value() / value; } Value operator/(int64_t value) { return this->value() / value; } Value operator/(bool value) { return this->value() / value; } + Value operator/(const void *value) = delete; Value operator/(char value) { return this->value() / value; } Value operator/(const std::string &value) { return this->value() / value; } Value operator/(const char *value) { return this->value() / value; } @@ -326,6 +335,7 @@ class PHPCPP_EXPORT HashMember : private HashParent Value operator%(int32_t value) { return this->value() % value; } Value operator%(int64_t value) { return this->value() % value; } Value operator%(bool value) { return this->value() % value; } + Value operator%(const void *value) = delete; Value operator%(char value) { return this->value() % value; } Value operator%(const std::string &value) { return this->value() % value; } Value operator%(const char *value) { return this->value() % value; } diff --git a/include/ini.h b/include/ini.h index a1899a4c..03e6d264 100644 --- a/include/ini.h +++ b/include/ini.h @@ -61,6 +61,7 @@ class PHPCPP_EXPORT Ini */ Ini(const char *name, bool value, const Place place = Place::All) : _name(name), _value(bool2str(value)), _place(place) {} + Ini(const char *name, const void *value, const Place place = Place::All) = delete; /** * Constructors for integer values diff --git a/include/value.h b/include/value.h index 45f7464e..084102c1 100644 --- a/include/value.h +++ b/include/value.h @@ -56,6 +56,7 @@ class PHPCPP_EXPORT Value : private HashParent Value(int32_t value); Value(int64_t value); Value(bool value); + Value(const void *value) = delete; Value(char value); Value(const std::string &value); Value(const char *value, int size = -1); @@ -174,6 +175,7 @@ class PHPCPP_EXPORT Value : private HashParent Value &operator=(int32_t value); Value &operator=(int64_t value); Value &operator=(bool value); + Value &operator=(const void *value) = delete; Value &operator=(char value); Value &operator=(const std::string &value); Value &operator=(const char *value); @@ -191,6 +193,7 @@ class PHPCPP_EXPORT Value : private HashParent Value &operator+=(int32_t value); Value &operator+=(int64_t value); Value &operator+=(bool value); + Value &operator+=(const void *value) = delete; Value &operator+=(char value); Value &operator+=(const std::string &value); Value &operator+=(const char *value); @@ -206,6 +209,7 @@ class PHPCPP_EXPORT Value : private HashParent Value &operator-=(int32_t value); Value &operator-=(int64_t value); Value &operator-=(bool value); + Value &operator-=(const void *value) = delete; Value &operator-=(char value); Value &operator-=(const std::string &value); Value &operator-=(const char *value); @@ -221,6 +225,7 @@ class PHPCPP_EXPORT Value : private HashParent Value &operator*=(int32_t value); Value &operator*=(int64_t value); Value &operator*=(bool value); + Value &operator*=(const void *value) = delete; Value &operator*=(char value); Value &operator*=(const std::string &value); Value &operator*=(const char *value); @@ -236,6 +241,7 @@ class PHPCPP_EXPORT Value : private HashParent Value &operator/=(int32_t value); Value &operator/=(int64_t value); Value &operator/=(bool value); + Value &operator/=(const void *value) = delete; Value &operator/=(char value); Value &operator/=(const std::string &value); Value &operator/=(const char *value); @@ -251,6 +257,7 @@ class PHPCPP_EXPORT Value : private HashParent Value &operator%=(int32_t value); Value &operator%=(int64_t value); Value &operator%=(bool value); + Value &operator%=(const void *value) = delete; Value &operator%=(char value); Value &operator%=(const std::string &value); Value &operator%=(const char *value); @@ -266,6 +273,7 @@ class PHPCPP_EXPORT Value : private HashParent Value operator+(int32_t value); Value operator+(int64_t value); Value operator+(bool value); + Value operator+(const void *value) = delete; Value operator+(char value); Value operator+(const std::string &value); Value operator+(const char *value); @@ -281,6 +289,7 @@ class PHPCPP_EXPORT Value : private HashParent Value operator-(int32_t value); Value operator-(int64_t value); Value operator-(bool value); + Value operator-(const void *value) = delete; Value operator-(char value); Value operator-(const std::string &value); Value operator-(const char *value); @@ -296,6 +305,7 @@ class PHPCPP_EXPORT Value : private HashParent Value operator*(int32_t value); Value operator*(int64_t value); Value operator*(bool value); + Value operator*(const void *value) = delete; Value operator*(char value); Value operator*(const std::string &value); Value operator*(const char *value); @@ -311,6 +321,7 @@ class PHPCPP_EXPORT Value : private HashParent Value operator/(int32_t value); Value operator/(int64_t value); Value operator/(bool value); + Value operator/(const void *value) = delete; Value operator/(char value); Value operator/(const std::string &value); Value operator/(const char *value); @@ -326,6 +337,7 @@ class PHPCPP_EXPORT Value : private HashParent Value operator%(int32_t value); Value operator%(int64_t value); Value operator%(bool value); + Value operator%(const void *value) = delete; Value operator%(char value); Value operator%(const std::string &value); Value operator%(const char *value); @@ -333,6 +345,10 @@ class PHPCPP_EXPORT Value : private HashParent /** * Comparison operators for hardcoded strings + * + * Note that this works only for string values, + * other values segfaults! + * * @param value */ bool operator==(const char *value) const { return ::strcmp(rawValue(), value) == 0; } From 9950a0b1c53ba167d09081307170194999daf9c8 Mon Sep 17 00:00:00 2001 From: Tomas Ebenlendr Date: Tue, 16 Apr 2024 09:29:46 +0200 Subject: [PATCH 079/101] Php::Value constructor from _zend_string Previous commit exposes mishandling of _zend_string function arguments by PHP-CPP to compiler. This commit fixes that problem. --- include/value.h | 1 + zend/value.cpp | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/include/value.h b/include/value.h index 084102c1..996eb48f 100644 --- a/include/value.h +++ b/include/value.h @@ -60,6 +60,7 @@ class PHPCPP_EXPORT Value : private HashParent Value(char value); Value(const std::string &value); Value(const char *value, int size = -1); + Value(struct _zend_string *value); Value(double value); Value(const IniValue &value); diff --git a/zend/value.cpp b/zend/value.cpp index 6f53af42..aa05efcb 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -128,6 +128,25 @@ Value::Value(const char *value, int size) } } +/** + * Constructor based on zend_string + * @param value + */ +Value::Value(struct _zend_string *value) +{ + // is there a value? + if (value) + { + // create a string zval + ZVAL_STRINGL(_val, value->val, value->len); + } + else + { + // store null + ZVAL_NULL(_val); + } +} + /** * Constructor based on decimal value * @param value From f9fe7a9c639858a2a6ffe6d0790ad2374b818ca1 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sat, 20 Apr 2024 10:30:29 +0200 Subject: [PATCH 080/101] New feature: it now is also possible to call the $object->getIterator() method explicitly when a C++ class implements the Traversable interface + this fixes the issue that in PHP 8 environments an error was reported that all Traversable objects could not be instantiated because they were abstract --- .gitignore | 3 ++- include/base.h | 10 +++++++++- zend/base.cpp | 16 +++++++++++++++- zend/classimpl.cpp | 20 +++++++++++++++++++- 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 9339f573..50001ca1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.d *.o *.a *.so @@ -5,4 +6,4 @@ *.a.* *.so.* build/ -.vscode/ \ No newline at end of file +.vscode/ diff --git a/include/base.h b/include/base.h index eb1a7129..5d2762c4 100644 --- a/include/base.h +++ b/include/base.h @@ -2,7 +2,7 @@ * Base class for defining your own objects * * @author Emiel Bruijntjes - * @copyright 2013 - 2022 Copernica BV + * @copyright 2013 - 2024 Copernica BV */ /** @@ -285,6 +285,14 @@ class PHPCPP_EXPORT Base */ Php::Value __count(Php::Parameters ¶ms); + /** + * Method that is called when an explicit call to $object->getIterator() is + * made, AND that is called when running on PHP 8 and higher (because somehow + * the get_iterator function is skipped by PHP 8 for non-internal classes)? + * @return Php::Value + */ + Php::Value __getIterator(); + private: /** diff --git a/zend/base.cpp b/zend/base.cpp index c0d9e5af..8199f527 100644 --- a/zend/base.cpp +++ b/zend/base.cpp @@ -3,7 +3,7 @@ * * Implementation file for the base of all classes * - * @copyright 2014 - 2022 Copernica BV + * @copyright 2014 - 2024 Copernica BV */ #include "includes.h" @@ -280,6 +280,20 @@ Php::Value Base::__count(Php::Parameters ¶ms) return countable->count(); } +/** + * Method that is called when an explicit call to $object->getIterator() is + * made, AND that is called when running on PHP 8 and higher (because somehow + * the get_iterator function is skipped by PHP 8 for non-internal classes)? + * @return Php::Value + */ +Php::Value Base::__getIterator() +{ + // because the object is already implicitly iterable because deep inside PHP knows that + // the "Traversable" method is implemented, we can simply return ourselves (which will + // end up that the code in ClassImpl::getIterator() will be called) + return this; +} + /** * End namespace */ diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 85dcf6ff..2bcc37c1 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1401,6 +1401,13 @@ const struct _zend_function_entry *ClassImpl::entries() if (!hasMethod("serialize")) entrycount += 1; if (!hasMethod("unserialize")) entrycount += 1; } + + // if the class is iterable, we might need some extra methods + if (_base->traversable()) + { + // add the getIterator method if the class does not have one defined yet + if (!hasMethod("getIterator")) entrycount += 1; + } // allocate memory for the functions _entries = new zend_function_entry[entrycount + 1]; @@ -1432,7 +1439,7 @@ const struct _zend_function_entry *ClassImpl::entries() // if the class is serializable, we might need some extra methods if (_base->serializable()) { - // the method objectneed to stay in scope for the lifetime of the script (because the register a pointer + // the method object need to stay in scope for the lifetime of the script (because the register a pointer // to an internal string buffer) -- so we create them as static variables static Method serialize("serialize", &Base::__serialize, 0, {}); static Method unserialize("unserialize", &Base::__unserialize, 0, { ByVal("input", Type::Undefined, true) }); @@ -1441,6 +1448,17 @@ const struct _zend_function_entry *ClassImpl::entries() if (!hasMethod("serialize")) serialize.initialize(&_entries[i++], _name); if (!hasMethod("unserialize")) unserialize.initialize(&_entries[i++], _name); } + + // if the class is traverable, we might need extra methods too (especially on php 8.1, maybe also 8.0?) + if (_base->traversable()) + { + // the method object need to stay in scope for the lifetime of the script (because the register a pointer + // to an internal string buffer) -- so we create them as static variables + static Method getIterator("getIterator", &Base::__getIterator, 0, {}); + + // register the serialize and unserialize method in case this was not yet done in PHP user space + if (!hasMethod("getIterator")) getIterator.initialize(&_entries[i++], _name); + } // last entry should be set to all zeros zend_function_entry *last = &_entries[i]; From 3cee025c6708aa465109b3df4a9332ad9f4ef2e9 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sat, 20 Apr 2024 11:02:38 +0200 Subject: [PATCH 081/101] Php::Value objects could crash if comparing non-string values with strings, fixes #524 --- include/value.h | 19 +++++++++++++------ zend/value.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/include/value.h b/include/value.h index 45f7464e..64653560 100644 --- a/include/value.h +++ b/include/value.h @@ -335,12 +335,12 @@ class PHPCPP_EXPORT Value : private HashParent * Comparison operators for hardcoded strings * @param value */ - bool operator==(const char *value) const { return ::strcmp(rawValue(), value) == 0; } - bool operator!=(const char *value) const { return ::strcmp(rawValue(), value) != 0; } - bool operator<=(const char *value) const { return ::strcmp(rawValue(), value) <= 0; } - bool operator>=(const char *value) const { return ::strcmp(rawValue(), value) >= 0; } - bool operator< (const char *value) const { return ::strcmp(rawValue(), value) < 0; } - bool operator> (const char *value) const { return ::strcmp(rawValue(), value) > 0; } + bool operator==(const char *value) const { return strcmp(value) == 0; } + bool operator!=(const char *value) const { return strcmp(value) != 0; } + bool operator<=(const char *value) const { return strcmp(value) <= 0; } + bool operator>=(const char *value) const { return strcmp(value) >= 0; } + bool operator< (const char *value) const { return strcmp(value) < 0; } + bool operator> (const char *value) const { return strcmp(value) > 0; } /** * Comparison operators for hardcoded Value @@ -424,6 +424,13 @@ class PHPCPP_EXPORT Value : private HashParent */ const char *rawValue() const; + /** + * Helper function for string comparison + * @param value + * @return int + */ + int strcmp(const char *value) const; + /** * Retrieve the value as number * diff --git a/zend/value.cpp b/zend/value.cpp index 6f53af42..e93826a1 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -1334,6 +1334,33 @@ const char *Value::rawValue() const return nullptr; } +/** + * Helper function for string comparison + * @param value + * @return int + */ +int Value::strcmp(const char *value) const +{ + // we need the string representation + zend_string *s = zval_get_string(_val); + + // remember size of the two strings + size_t valuelen = ::strlen(value); + size_t slen = ZSTR_LEN(s); + + // get the result for comparing the initial, overlapping, bytes + auto result = strncmp(ZSTR_VAL(s), value, std::min(valuelen, slen)); + + // we no longer need the string + zend_string_release(s); + + // if there are differences, we can expose thosw + if (result != 0) return result; + + // the shorter string comes first + return slen - valuelen; +} + /** * Retrieve the value as decimal * @return double From 10123915a8ef329662c814687cab4007d134328c Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sat, 20 Apr 2024 15:24:06 +0200 Subject: [PATCH 082/101] update version number --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e6a27c46..7f8f5ec6 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.2 +VERSION = 2.4.3 # From 42c4581963628602de37a144a263d9c001c0a33b Mon Sep 17 00:00:00 2001 From: "Nathanael d. Noblet" Date: Fri, 26 Apr 2024 07:33:55 -0600 Subject: [PATCH 083/101] Patch makefiles to determine install locations automatically --- Examples/CallPhpFunctions/Makefile | 14 ++++++++------ Examples/ConstStaticProp/cpp/Makefile | 13 ++++++++----- Examples/CppClassesInPhp/Makefile | 2 +- Examples/DlUnrestricted/Makefile | 2 +- Examples/EmptyExtension/Makefile | 2 +- Examples/Exceptions/ExceptionCatch/Makefile | 13 ++++++++----- Examples/Exceptions/ExceptionThrow/Makefile | 13 ++++++++----- Examples/Extension/Makefile | 13 ++++++++----- Examples/FunctionNoParameters/Makefile | 13 ++++++++----- Examples/FunctionReturnValue/Makefile | 13 ++++++++----- Examples/FunctionVoid/Makefile | 13 ++++++++----- Examples/FunctionWithParameters/Makefile | 13 ++++++++----- Examples/Globals/Makefile | 13 ++++++++----- Examples/ReturnObject/Makefile | 2 +- Examples/simple/Makefile | 17 +++++++++-------- 15 files changed, 93 insertions(+), 63 deletions(-) diff --git a/Examples/CallPhpFunctions/Makefile b/Examples/CallPhpFunctions/Makefile index cd9ee434..9d675b8a 100644 --- a/Examples/CallPhpFunctions/Makefile +++ b/Examples/CallPhpFunctions/Makefile @@ -1,11 +1,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 - -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +26,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/ConstStaticProp/cpp/Makefile b/Examples/ConstStaticProp/cpp/Makefile index 7e951b4a..44789408 100644 --- a/Examples/ConstStaticProp/cpp/Makefile +++ b/Examples/ConstStaticProp/cpp/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} \ No newline at end of file diff --git a/Examples/CppClassesInPhp/Makefile b/Examples/CppClassesInPhp/Makefile index 581a50ee..36b7091c 100644 --- a/Examples/CppClassesInPhp/Makefile +++ b/Examples/CppClassesInPhp/Makefile @@ -29,7 +29,7 @@ NAME = cppclassinphp # one for each extension. Use this variable to specify this directory. # -INI_DIR = /etc/php5/conf.d +INI_DIR = $(shell php-config --ini-dir) # diff --git a/Examples/DlUnrestricted/Makefile b/Examples/DlUnrestricted/Makefile index 86cddfdf..36ace0b5 100644 --- a/Examples/DlUnrestricted/Makefile +++ b/Examples/DlUnrestricted/Makefile @@ -29,7 +29,7 @@ NAME = dlunrestricted # one for each extension. Use this variable to specify this directory. # -INI_DIR = /etc/php5/mods-available +INI_DIR = $(shell php-config --ini-dir) # diff --git a/Examples/EmptyExtension/Makefile b/Examples/EmptyExtension/Makefile index deb78f26..d5525910 100644 --- a/Examples/EmptyExtension/Makefile +++ b/Examples/EmptyExtension/Makefile @@ -29,7 +29,7 @@ NAME = yourextension # one for each extension. Use this variable to specify this directory. # -INI_DIR = /etc/php5/mods-available/ +INI_DIR = $(shell php-config --ini-dir) # diff --git a/Examples/Exceptions/ExceptionCatch/Makefile b/Examples/Exceptions/ExceptionCatch/Makefile index 83537dd2..e380c64c 100644 --- a/Examples/Exceptions/ExceptionCatch/Makefile +++ b/Examples/Exceptions/ExceptionCatch/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/Exceptions/ExceptionThrow/Makefile b/Examples/Exceptions/ExceptionThrow/Makefile index c8571ca3..8e8f7ba1 100644 --- a/Examples/Exceptions/ExceptionThrow/Makefile +++ b/Examples/Exceptions/ExceptionThrow/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/Extension/Makefile b/Examples/Extension/Makefile index a55b6c1d..dae0ef7b 100644 --- a/Examples/Extension/Makefile +++ b/Examples/Extension/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/FunctionNoParameters/Makefile b/Examples/FunctionNoParameters/Makefile index ccd4ac22..792784c5 100644 --- a/Examples/FunctionNoParameters/Makefile +++ b/Examples/FunctionNoParameters/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} \ No newline at end of file diff --git a/Examples/FunctionReturnValue/Makefile b/Examples/FunctionReturnValue/Makefile index 0a2e1c30..cccbbdaf 100644 --- a/Examples/FunctionReturnValue/Makefile +++ b/Examples/FunctionReturnValue/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/FunctionVoid/Makefile b/Examples/FunctionVoid/Makefile index fbf4bdd5..1fb4ac19 100644 --- a/Examples/FunctionVoid/Makefile +++ b/Examples/FunctionVoid/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/FunctionWithParameters/Makefile b/Examples/FunctionWithParameters/Makefile index bbebb43d..8ed62bae 100644 --- a/Examples/FunctionWithParameters/Makefile +++ b/Examples/FunctionWithParameters/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -#Edit these lines to correspond with your own directories +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/Globals/Makefile b/Examples/Globals/Makefile index fec79f70..8872ed87 100644 --- a/Examples/Globals/Makefile +++ b/Examples/Globals/Makefile @@ -2,10 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 -PREFIX = /usr -PHP_CONFIG = php-config +PHP_CONFIG = $(shell which php-config) LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) -PHP_CONFIG_DIR = /etc/php5/cli/conf.d +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -shared -O2 @@ -28,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/ReturnObject/Makefile b/Examples/ReturnObject/Makefile index 526e4bc2..b154d389 100644 --- a/Examples/ReturnObject/Makefile +++ b/Examples/ReturnObject/Makefile @@ -29,7 +29,7 @@ NAME = returnobject # one for each extension. Use this variable to specify this directory. # -INI_DIR = /etc/php5/mods-available/ +INI_DIR = $(shell php-config --ini-dir) # diff --git a/Examples/simple/Makefile b/Examples/simple/Makefile index a85c7f8d..8e38e21e 100644 --- a/Examples/simple/Makefile +++ b/Examples/simple/Makefile @@ -2,12 +2,9 @@ CPP = g++ RM = rm -f CPP_FLAGS = -Wall -c -I. -I/home/work/include/ -O2 -std=c++11 -PREFIX = /home/work/ - -#Edit these lines to correspond with your own directories -LIBRARY_DIR = ${PREFIX}/local/php/lib/php/extensions/no-debug-non-zts-20090626/ - -PHP_CONFIG_DIR = /home/work/local/php/lib/ +PHP_CONFIG = $(shell which php-config) +LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) LD = g++ LD_FLAGS = -Wall -Wl,-rpath,/home/work/lib -shared -O2 -L/home/work/lib @@ -30,5 +27,9 @@ ${OBJECTS}: ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} install: - cp -f ${RESULT} ${LIBRARY_DIR} - cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR} + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} From 1e01b0c46e25d33d3280c16baecf433ddbfc8f26 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Thu, 16 May 2024 10:25:28 +0200 Subject: [PATCH 084/101] Removed exception for php 7 --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 6171477f..35fb0501 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,6 @@ The PHP-CPP library is a C++ library for developing PHP extensions. It offers a of well documented and easy-to-use classes that can be used and extended to build native extensions for PHP. The full documentation can be found on http://www.php-cpp.com. -**Watch out: PHP 7 only!** -This library has been updated to work with PHP versions 7.0 and up. If you wish to create -extensions for older PHP versions, use the [PHP-CPP-LEGACY](https://github.com/CopernicaMarketingSoftware/PHP-CPP-LEGACY) -library instead. The PHP-CPP and PHP-CPP-LEGACY library have (almost) identical API's, -so you can easily port extensions for PHP 5.* to PHP 7 and the other way around. - ABOUT ===== From dcf92fa333f14d4a80388cdb7644b846978a0aa7 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 13 Oct 2024 17:19:57 +0200 Subject: [PATCH 085/101] fixed memory corruption issue when making calls from an extension back to user space php functions --- include/value.h | 9 ++-- zend/execarguments.h | 102 +++++++++++++++++++++++++++++++++++++++++++ zend/value.cpp | 41 +++++++++-------- 3 files changed, 130 insertions(+), 22 deletions(-) create mode 100644 zend/execarguments.h diff --git a/include/value.h b/include/value.h index 8a56f87b..c9f48f1c 100644 --- a/include/value.h +++ b/include/value.h @@ -15,7 +15,7 @@ * this class. * * @author Emiel Bruijntjes - * @copyright 2013 - 2019 Copernica BV + * @copyright 2013 - 2024 Copernica BV */ /** @@ -1133,7 +1133,7 @@ class PHPCPP_EXPORT Value : private HashParent * @param argv The parameters * @return Value */ - Value exec(int argc, Value *argv) const; + Value exec(int argc, Value argv[]) const; /** * Call method with a number of parameters @@ -1142,8 +1142,8 @@ class PHPCPP_EXPORT Value : private HashParent * @param argv The parameters * @return Value */ - Value exec(const char *name, int argc, Value *argv) const; - Value exec(const char *name, int argc, Value *argv); + 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 @@ -1242,6 +1242,7 @@ class PHPCPP_EXPORT Value : private HashParent friend class Script; friend class ConstantImpl; friend class Stream; + friend class ExecArguments; /** * Friend functions which have to access that zval directly diff --git a/zend/execarguments.h b/zend/execarguments.h new file mode 100644 index 00000000..da5461f5 --- /dev/null +++ b/zend/execarguments.h @@ -0,0 +1,102 @@ +/** + * ExecArguments.h + * + * Helper class that we use to turn an array of Value objects into an + * array of zval parameters + * + * @author Emiel Bruijntjes + * @copyright 2024 Copernica BV + */ + +/** + * Include guard + */ +#pragma once + +/** + * Begin of namespace + */ +namespace Php { + +/** + * Class definition + */ +class ExecArguments +{ +private: + /** + * Short-array-optimization (most exec calls do not have more than 10 parameters) + * @var zval[] + */ + zval _preallocated[10]; + + /** + * The actual arguments + * @var zval[] + */ + zval *_argv; + + /** + * The number of arguments + * @var size_t + */ + size_t _argc; + +public: + /** + * Default constructor + */ + ExecArguments() : _argv(_preallocated), _argc(0) {} + + /** + * Constructor + * @param argc + * @param argv + */ + ExecArguments(size_t argc, Value argv[]) : _argv(_preallocated), _argc(argc) + { + // if there are too many arguments, we allocate them right away + if (_argc > 10) _argv = (zval *)malloc(sizeof(zval) * _argc); + + // convert Value objects to zval array with references + for (size_t i = 0; i < argc; ++i) + { + // make sure the original variable is a reference so that our copy points to the same data + // @todo not sure if this is needed, do we need to turn the parameters into references to allow for pass-by-reference parameters? + //if (!Z_ISREF_P(argv[i]._val)) ZVAL_MAKE_REF(argv[i]._val); + + // copy the zval + ZVAL_COPY(&_argv[i], argv[i]._val); + } + } + + /** + * No copying (we could implement this later, but for now this is not needed) + * @param that + */ + ExecArguments(const ExecArguments &that) = delete; + + /** + * Destructor + */ + virtual ~ExecArguments() + { + // destruct all zval objects + for (size_t i = 0; i < _argc; ++i) zval_ptr_dtor(&_argv[i]); + + // deallocate memory + if (_argv != _preallocated) free(_argv); + } + + /** + * Convert to a argv[] + * @return zval[] + */ + zval *argv() { return _argv; } + int argc() { return _argc; } +}; + +/** + * End of namespace + */ +} diff --git a/zend/value.cpp b/zend/value.cpp index 45d65623..0c4f1757 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -22,12 +22,13 @@ * * * @author Emiel Bruijntjes - * @copyright 2013 - 2019 Copernica BV + * @copyright 2019 - 2024 Copernica BV */ #include "includes.h" #include "string.h" #include "lowercase.h" #include "macros.h" +#include "execarguments.h" /** * Set up namespace @@ -822,6 +823,19 @@ static Value do_exec(const zval *object, zval *method, int argc, zval *argv) } } +/** + * 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 The parameters + * @return Value + */ +static Value do_exec(const zval *object, zval *method, ExecArguments &args) +{ + // pass on + return do_exec(object, method, args.argc(), args.argv()); +} + /** * Call the function in PHP * We have ten variants of this function, depending on the number of parameters @@ -919,16 +933,13 @@ Value Value::call(const char *name) * @param argv The parameters * @return Value */ -Value Value::exec(int argc, Value *argv) const +Value Value::exec(int argc, Value argv[]) const { // array of zvals to execute - zval* params = static_cast(alloca(argc * sizeof(zval))); - - // convert all the values - for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; } - + ExecArguments args(argc, argv); + // call helper function - return do_exec(nullptr, _val, argc, params); + return do_exec(nullptr, _val, args); } /** @@ -944,13 +955,10 @@ Value Value::exec(const char *name, int argc, Value *argv) const Value method(name); // array of zvals to execute - zval* params = static_cast(alloca(argc * sizeof(zval))); - - // convert all the values - for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; } + ExecArguments args(argc, argv); // call helper function - return do_exec(_val, method._val, argc, params); + return do_exec(_val, method._val, args); } /** @@ -966,13 +974,10 @@ Value Value::exec(const char *name, int argc, Value *argv) Value method(name); // array of zvals to execute - zval* params = static_cast(alloca(argc * sizeof(zval))); - - // convert all the values - for(int i = 0; i < argc; i++) { params[i] = *argv[i]._val; } + ExecArguments args(argc, argv); // call helper function - return do_exec(_val, method._val, argc, params); + return do_exec(_val, method._val, args); } /** From 2c0a23180c28c574174dbd9b96d4361c53354264 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 13 Oct 2024 22:26:58 +0200 Subject: [PATCH 086/101] fix double free call, which caused memory-corruption --- zend/classimpl.cpp | 7 +++++-- zend/value.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 2bcc37c1..0541ec24 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -4,7 +4,7 @@ * Implementation file for the ClassImpl class * * @author Emiel Bruijntjes - * @copyright 2014 - 2019 Copernica BV + * @copyright 2014 - 2024 Copernica BV */ #include "includes.h" #include @@ -207,7 +207,10 @@ zend_function *ClassImpl::getMethod(zend_object **object, zend_string *method, c // had an implementation here that used a static variable, and that worked too, // but we'll follow thread safe implementation of the Zend engine here, although // it is strange to allocate and free memory in one and the same method call (free() - // call happens in call_method()) + // call happens in call_method()) (2024-10-13 extra info: the method_exists() + // function and our own Value::isCallable() method expect this to be emalloc()- + // allocated buffer, because they both call zend_free_trampoline() (which is + // effectively an efree() call) on the returned function-structure) auto *data = (CallData *)emalloc(sizeof(CallData)); auto *function = &data->func; diff --git a/zend/value.cpp b/zend/value.cpp index 0c4f1757..0ff917cc 100644 --- a/zend/value.cpp +++ b/zend/value.cpp @@ -891,8 +891,11 @@ bool Value::isCallable(const char *name) bool result = func->common.scope == zend_ce_closure && zend_string_equals_cstr(methodname.value(), ZEND_INVOKE_FUNC_NAME, ::strlen(ZEND_INVOKE_FUNC_NAME)); #endif - // free resources (still don't get this code, copied from zend_builtin_functions.c) - zend_string_release(func->common.function_name); + // in method_exists(), there is also a zend_string_release() call here, but I dont think we + // need it here, because the methodname is already cleanup by the destructor of the LowerCase class + //zend_string_release(func->common.function_name); + + // free resources, just like method_exists() does zend_free_trampoline(func); // done From 63f425808485648207d4270b30849aa73afedfe7 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 13 Oct 2024 22:37:22 +0200 Subject: [PATCH 087/101] update version number --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 7f8f5ec6..450b0663 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.3 +VERSION = 2.4.4 # From 1d878ff67c0b566def8be43afd554a45528e3aa2 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 13 Oct 2024 22:53:15 +0200 Subject: [PATCH 088/101] up version number one more --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 450b0663..d50e5de9 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.4 +VERSION = 2.4.5 # From 009580dcccaebed3ddec07e0001b302221c5f97f Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 20 Oct 2024 12:06:04 +0200 Subject: [PATCH 089/101] fixed the Php::include_once() call for PHP 8.1 --- Examples/IncludeOnce/30-includeonce.ini | 4 ++ Examples/IncludeOnce/Makefile | 34 +++++++++++++++++ Examples/IncludeOnce/class.php | 21 +++++++++++ Examples/IncludeOnce/includeonce.cpp | 50 +++++++++++++++++++++++++ Examples/IncludeOnce/includeonce.php | 18 +++++++++ zend/file.cpp | 34 +++++++++-------- 6 files changed, 146 insertions(+), 15 deletions(-) create mode 100644 Examples/IncludeOnce/30-includeonce.ini create mode 100644 Examples/IncludeOnce/Makefile create mode 100644 Examples/IncludeOnce/class.php create mode 100644 Examples/IncludeOnce/includeonce.cpp create mode 100644 Examples/IncludeOnce/includeonce.php diff --git a/Examples/IncludeOnce/30-includeonce.ini b/Examples/IncludeOnce/30-includeonce.ini new file mode 100644 index 00000000..6cfdbd38 --- /dev/null +++ b/Examples/IncludeOnce/30-includeonce.ini @@ -0,0 +1,4 @@ +; configuration for phpcpp module +; priority=30 +extension=includeonce.so + diff --git a/Examples/IncludeOnce/Makefile b/Examples/IncludeOnce/Makefile new file mode 100644 index 00000000..e42b8496 --- /dev/null +++ b/Examples/IncludeOnce/Makefile @@ -0,0 +1,34 @@ +CPP = g++ +RM = rm -f +CPP_FLAGS = -Wall -c -I. -O2 -std=c++11 +PHP_CONFIG = $(shell which php-config) +LIBRARY_DIR = $(shell ${PHP_CONFIG} --extension-dir) +PHP_CONFIG_DIR = $(shell ${PHP_CONFIG} --ini-dir) + +LD = g++ +LD_FLAGS = -Wall -shared -O2 +RESULT = includeonce.so + +PHPINIFILE = 30-includeonce.ini + +SOURCES = $(wildcard *.cpp) +OBJECTS = $(SOURCES:%.cpp=%.o) + +all: ${OBJECTS} ${RESULT} + +${RESULT}: ${OBJECTS} + ${LD} ${LD_FLAGS} -o $@ ${OBJECTS} -lphpcpp + +clean: + ${RM} *.obj *~* ${OBJECTS} ${RESULT} + +${OBJECTS}: + ${CPP} ${CPP_FLAGS} -fpic -o $@ ${@:%.o=%.cpp} + +install: + cp -f ${RESULT} ${LIBRARY_DIR}/ + cp -f ${PHPINIFILE} ${PHP_CONFIG_DIR}/ + +uninstall: + rm ${LIBRARY_DIR}/${RESULT} + rm ${PHP_CONFIG_DIR}/${PHPINIFILE} diff --git a/Examples/IncludeOnce/class.php b/Examples/IncludeOnce/class.php new file mode 100644 index 00000000..81a68325 --- /dev/null +++ b/Examples/IncludeOnce/class.php @@ -0,0 +1,21 @@ + + * @copyright 2024 Copernica BV + */ + +/** + * Include ourselves (should do nothing) + */ +include_once(__FILE__); +my_include_once(__FILE__); + +/** + * Class definition + */ +class SillyClass {} diff --git a/Examples/IncludeOnce/includeonce.cpp b/Examples/IncludeOnce/includeonce.cpp new file mode 100644 index 00000000..243abedf --- /dev/null +++ b/Examples/IncludeOnce/includeonce.cpp @@ -0,0 +1,50 @@ +/** + * callphpfunction.cpp + * @author Jasper van Eck + * + * An example file to show the working of a php function call in C++. + */ + +/** + * Libraries used. + */ +#include +#include + +/** + * Namespace to use + */ +using namespace std; + +/** + * Function to test the Php::include_once() call + * @param ¶ms + */ +void my_include_once(Php::Parameters ¶ms) +{ + // the string + Php::Value path = params[0]; + + // do the call + Php::include_once(path.stringValue()); +} + + +// Symbols are exported according to the "C" language +extern "C" +{ + // export the "get_module" function that will be called by the Zend engine + PHPCPP_EXPORT void *get_module() + { + // create extension + static Php::Extension extension("include_once","1.0"); + + // add function to extension + extension.add("my_include_once", { + Php::ByVal("path", Php::Type::String), + }); + + // return the extension module + return extension.module(); + } +} diff --git a/Examples/IncludeOnce/includeonce.php b/Examples/IncludeOnce/includeonce.php new file mode 100644 index 00000000..4478f745 --- /dev/null +++ b/Examples/IncludeOnce/includeonce.php @@ -0,0 +1,18 @@ +valid(); // we are going to open the file - zend_file_handle fileHandle; + zend_file_handle filehandle; #if PHP_VERSION_ID < 80100 + // open the file if (zend_stream_open(ZSTR_VAL(_path), &fileHandle) == FAILURE) return false; + #else - /** - * zend_stream_open now only accepts the fileHandle object - * Filename must now be set through the object path. - */ - fileHandle.filename = _path; - // open the file - if (zend_stream_open(&fileHandle) == FAILURE) return false; + // since php 8 (or 8.1? - this has not been checked), zend_stream_open just takes the file-handle, and we must associate it first with a filename + zend_stream_init_filename_ex(&filehandle, _path); + + // the stream if supposed to be open by now + if (zend_stream_open(&filehandle) == FAILURE) return false; + #endif - // 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 (@todo: is this necessary? do we need the copy, + // this was copied from zend_execute.c, maybe deals with case when opened_path is not set for + // special types of files that are correctly opened, but that do not expose path-info, while this info + // is still needed by the subsequent zend_compile_file() call for error messages?) + if (!filehandle.opened_path) filehandle.opened_path = zend_string_copy(_path); // we need temporary compiler options CompilerOptions options(ZEND_COMPILE_DEFAULT); // create the opcodes - _opcodes.reset(new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE))); + _opcodes.reset(new Opcodes(zend_compile_file(&filehandle, ZEND_INCLUDE))); // close the file handle - zend_destroy_file_handle(&fileHandle); + zend_destroy_file_handle(&filehandle); // done return _opcodes->valid(); @@ -133,9 +137,6 @@ Value File::execute() // try compiling the file if (!compile()) return nullptr; - // add the entry to the list of included files - zend_hash_add_empty_element(&EG(included_files), _path); - // execute the opcodes return _opcodes->execute(); } @@ -152,6 +153,9 @@ Value File::once() // check if this file was already included if (zend_hash_exists(&EG(included_files), _path)) return nullptr; + // add the entry to the list of included files + zend_hash_add_empty_element(&EG(included_files), _path); + // execute the file return execute(); } From 52895f528bb5a4320cf3a78325028ba924817886 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Sun, 20 Oct 2024 12:08:19 +0200 Subject: [PATCH 090/101] new version number --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d50e5de9..4845793e 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.5 +VERSION = 2.4.6 # From 910f7b5f91bbf626044511fa58c0f8b0789823db Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 21 Oct 2024 11:26:54 +0200 Subject: [PATCH 091/101] fixed compile error on older php versions (lower than 8) --- Makefile | 2 +- zend/file.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 4845793e..2b9b4809 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.6 +VERSION = 2.4.7 # diff --git a/zend/file.cpp b/zend/file.cpp index 35e086e0..3d7d181a 100644 --- a/zend/file.cpp +++ b/zend/file.cpp @@ -67,7 +67,7 @@ bool File::compile() #if PHP_VERSION_ID < 80100 // open the file - if (zend_stream_open(ZSTR_VAL(_path), &fileHandle) == FAILURE) return false; + if (zend_stream_open(ZSTR_VAL(_path), &filehandle) == FAILURE) return false; #else From bd2710574363d638905cd2b737752ea065c38531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrija=20Vu=C4=8Dini=C4=87?= Date: Thu, 21 Nov 2024 11:17:59 +0100 Subject: [PATCH 092/101] PHP-CPP: PHP 8.4 support module.h - function signature changed https://github.com/php/php-src/blob/91f0b3bc0416539f64c44d453100274ccae942b3/Zend/zend_API.c#L2582 classimpl.cpp - doc_comment moved out of info.user https://github.com/php/php-src/blob/91f0b3bc0416539f64c44d453100274ccae942b3/Zend/zend.h#L224 iteratorimpl.(cpp|h) - valid now returns zend_result https://github.com/php/php-src/blob/91f0b3bc0416539f64c44d453100274ccae942b3/Zend/zend_iterators.h#L39 --- zend/classimpl.cpp | 12 ++++++++++++ zend/iteratorimpl.cpp | 4 ++++ zend/iteratorimpl.h | 4 ++++ zend/module.h | 6 ++++++ 4 files changed, 26 insertions(+) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 0541ec24..218426be 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -56,14 +56,22 @@ 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. */ +#if PHP_VERSION_ID < 80400 while (entry->parent && (entry->info.user.doc_comment == nullptr || ZSTR_LEN(entry->info.user.doc_comment) > 0)) +#else + while (entry->parent && (entry->doc_comment == nullptr || ZSTR_LEN(entry->doc_comment) > 0)) +#endif { // 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) +#if PHP_VERSION_ID < 80400 const char *comment = ZSTR_VAL(entry->info.user.doc_comment); +#else + const char *comment = ZSTR_VAL(entry->doc_comment); +#endif // the first byte of the comment is an empty string (null character), but // the next bytes contain a pointer to the ClassBase class @@ -1604,7 +1612,11 @@ zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &pref std::memcpy(ZSTR_VAL(_self) + 1, &impl, sizeof(impl)); // install the doc_comment +#if PHP_VERSION_ID < 80400 _entry->info.user.doc_comment = _self; +#else + _entry->doc_comment = _self; +#endif // declare all member variables for (auto &member : _members) member->initialize(_entry); diff --git a/zend/iteratorimpl.cpp b/zend/iteratorimpl.cpp index bac23c93..b9806511 100644 --- a/zend/iteratorimpl.cpp +++ b/zend/iteratorimpl.cpp @@ -68,7 +68,11 @@ void IteratorImpl::destructor(zend_object_iterator *iter) * @param iter * @return int */ +#if PHP_VERSION_ID < 80400 int IteratorImpl::valid(zend_object_iterator *iter) +#else +zend_result IteratorImpl::valid(zend_object_iterator *iter) +#endif { // check if valid return self(iter)->valid() ? SUCCESS : FAILURE; diff --git a/zend/iteratorimpl.h b/zend/iteratorimpl.h index 84c707a1..fedddab2 100644 --- a/zend/iteratorimpl.h +++ b/zend/iteratorimpl.h @@ -121,7 +121,11 @@ class IteratorImpl * @param iter * @return int */ +#if PHP_VERSION_ID < 80400 static int valid(zend_object_iterator *iter); +#else + static zend_result valid(zend_object_iterator *iter); +#endif /** * Fetch the current item diff --git a/zend/module.h b/zend/module.h index 7902f8e9..34d4aacf 100644 --- a/zend/module.h +++ b/zend/module.h @@ -179,17 +179,23 @@ class Module // this is not possible if the module is invalid in the first place if (!valid()) return false; +#if PHP_VERSION_ID < 80400 // the Zend engine sets a number of properties in the entry class, we do that here too // note that it would be better to call zend_next_free_module() to find the next module // number, but some users complain that this function is not always available _entry->type = MODULE_TEMPORARY; _entry->module_number = zend_hash_num_elements(&module_registry) + 1; +#endif _entry->handle = _handle; // @todo does loading an extension even work in a multi-threading setup? // register the module, this apparently returns a copied entry pointer +#if PHP_VERSION_ID < 80400 auto *entry = zend_register_module_ex(_entry); +#else + auto *entry = zend_register_module_ex(_entry, MODULE_TEMPORARY); +#endif // forget the entry, so that a new call to start() will fail too _entry = nullptr; From 5c9a31a18ca4affb1bb165e0375add22c7d9e5e8 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Tue, 25 Feb 2025 12:06:05 +0100 Subject: [PATCH 093/101] fixed error_reporting() call (a later call from php userspace after calling the c++ error_reporting() function caused a crash) --- Makefile | 2 +- zend/exception_handler.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 2b9b4809..b788325b 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.7 +VERSION = 2.4.8 # diff --git a/zend/exception_handler.cpp b/zend/exception_handler.cpp index 19191df6..cfdfdb50 100644 --- a/zend/exception_handler.cpp +++ b/zend/exception_handler.cpp @@ -87,7 +87,7 @@ Value error_reporting(Message message) static String entry{ "error_reporting" }; // alter the ini on the fly - zend_alter_ini_entry(entry, String(str, size), ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME); + zend_alter_ini_entry_chars(entry, str, size, ZEND_INI_USER, ZEND_INI_STAGE_RUNTIME); // return the output return output; From 46d2b80681c4448feb3e7a2c5460dd787931e2d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrija=20Vu=C4=8Dini=C4=87?= Date: Fri, 28 Mar 2025 11:30:48 +0100 Subject: [PATCH 094/101] fix double-free segfaults Props @vnsavage --- zend/callable.cpp | 4 ++++ zend/classimpl.cpp | 3 +++ 2 files changed, 7 insertions(+) diff --git a/zend/callable.cpp b/zend/callable.cpp index 20134b2f..a1a684e0 100644 --- a/zend/callable.cpp +++ b/zend/callable.cpp @@ -124,6 +124,10 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int entry->arg_info = _argv.get(); entry->num_args = _argc; entry->flags = flags; +#if PHP_VERSION_ID >= 80400 + entry->frameless_function_infos = nullptr; + entry->doc_comment = nullptr; +#endif // we should fill the first argument as well initialize((zend_internal_function_info*)_argv.get(), classname); diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 218426be..eafd0a5d 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -23,8 +23,11 @@ ClassImpl::~ClassImpl() // destruct the entries delete[] _entries; + // PHP 8.4 frees doc_comment if not null, so skip. +#if PHP_VERSION_ID < 80400 // free the stored pointer if (_self) zend_string_release(_self); +#endif } /** From 9bab49ea227d71b2eba2ad06851ff9e58df6fdf5 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 19 May 2025 08:06:35 +0200 Subject: [PATCH 095/101] fix startup error on php 8.3 when using named parameters --- zend/callable.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/zend/callable.h b/zend/callable.h index 89561d5f..ded12898 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -232,7 +232,14 @@ class Callable case Type::Array: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_ARRAY, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // array of anything (individual members cannot be restricted) case Type::Object: if (arg.classname()) { +#if PHP_VERSION_ID < 83000 + // up to to 8.3 the type-names were normally given as "const char *" info->type = (zend_type) ZEND_TYPE_INIT_CLASS(arg.encoded(), arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); +#else + // since 8.3 a zend_string* is required, or a compile time "literal string" -- we fake the system by calling "ZEND_TYPE_INIT_CLASS_CONST" + // to pretend that the name is not a zend_string* but a compile-time "const char *" (in reality it is a const char * stored in a std::string) + info->type = (zend_type) ZEND_TYPE_INIT_CLASS_CONST(arg.encoded(), arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); +#endif break; } info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_OBJECT, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); From 2706d5664de7a6bb5f53db3c22cddd50de918914 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 19 May 2025 08:08:16 +0200 Subject: [PATCH 096/101] increment version number --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index b788325b..69699bae 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.8 +VERSION = 2.4.9 # From ad90b5a16e108f16a51afac3a4d92f6d72173db8 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 19 May 2025 09:16:47 +0200 Subject: [PATCH 097/101] fixed version check --- Makefile | 2 +- zend/callable.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 69699bae..e268d909 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.9 +VERSION = 2.4.10 # diff --git a/zend/callable.h b/zend/callable.h index ded12898..8e46fd53 100644 --- a/zend/callable.h +++ b/zend/callable.h @@ -232,7 +232,7 @@ class Callable case Type::Array: info->type = (zend_type) ZEND_TYPE_INIT_CODE(IS_ARRAY, arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); break; // array of anything (individual members cannot be restricted) case Type::Object: if (arg.classname()) { -#if PHP_VERSION_ID < 83000 +#if PHP_VERSION_ID < 80300 // up to to 8.3 the type-names were normally given as "const char *" info->type = (zend_type) ZEND_TYPE_INIT_CLASS(arg.encoded(), arg.allowNull(), _ZEND_ARG_INFO_FLAGS(arg.byReference(), 0, 0)); #else From 77fea0ea08bd85b56a636d45445f2cb85ad56e18 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Wed, 9 Jul 2025 21:18:04 +0200 Subject: [PATCH 098/101] Fixed potention crashes when __invoke() was called on PHP 8.3 and up, because the zend_internal_function structure was not correctly initialized --- Makefile | 2 +- zend/classimpl.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e268d909..06fcd6bc 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.10 +VERSION = 2.4.11 # diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index eafd0a5d..22b08063 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -225,6 +225,9 @@ zend_function *ClassImpl::getMethod(zend_object **object, zend_string *method, c auto *data = (CallData *)emalloc(sizeof(CallData)); auto *function = &data->func; + // reset everything to zero (in case future PHP versions add more fields) + memset(function, 0, sizeof(*function)); + // set all properties function->type = ZEND_INTERNAL_FUNCTION; function->arg_flags[0] = 0; @@ -269,6 +272,9 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, zend_string * auto *data = (CallData *)emalloc(sizeof(CallData)); auto *function = &data->func; + // reset everything to zero (in case future PHP versions add more fields) + memset(function, 0, sizeof(*function)); + // set all properties for the function function->type = ZEND_INTERNAL_FUNCTION; function->arg_flags[0] = 0; @@ -319,6 +325,9 @@ zend_result ClassImpl::getClosure(ZEND_OBJECT_OR_ZVAL object, zend_class_entry * auto *data = (CallData *)emalloc(sizeof(CallData)); auto *function = &data->func; + // reset everything to zero (in case future PHP versions add more fields) + memset(function, 0, sizeof(*function)); + // we're going to set all properties of the zend_internal_function struct function->type = ZEND_INTERNAL_FUNCTION; function->arg_flags[0] = 0; From 262b6077f16b8048a4b8a1b818761110b1170bf3 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Oct 2025 16:17:34 +0200 Subject: [PATCH 099/101] when calling serialize, we now check whether the derived class has overridden the method --- zend/classimpl.cpp | 100 +++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 26 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 22b08063..887bd8ef 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1320,20 +1320,53 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje */ int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data) { - // get the serializable object - Serializable *serializable = dynamic_cast(ObjectImpl::find(object)->object()); - // user may throw an exception in the serialize() function try { - // call the serialize method on the object - auto value = serializable->serialize(); - - // allocate the buffer, and copy the data into it (the zend engine will - // (hopefully) clean up the data for us - the default serialize method does - // it like this too) - *buffer = (unsigned char*)estrndup(value.c_str(), value.size()); - *buf_len = value.size(); + // get the base object + Base *base = dynamic_cast(ObjectImpl::find(object)->object()); + + // get the class-entry + zend_class_entry *ce = Z_OBJCE_P(object); + + // we are going to check if the serialize method was overridden in user-space + zend_function *func = (zend_function *)zend_hash_str_find_ptr(&ce->function_table, "serialize", sizeof("serialize")-1); + + // do we have a user-space alternative? + if (func && func->type == ZEND_USER_FUNCTION) + { + // construct object to make the call + Php::Object self(ce, base); + + // get the serialized string + auto value = self.call("serialize"); + + // make sure the returned value is indeed a string + value.setType(Type::String); + + // allocate the buffer, and copy the data into it (the zend engine will + // (hopefully) clean up the data for us - the default serialize method does + // it like this too) + *buffer = (unsigned char*)estrndup(value.rawValue(), value.size()); + *buf_len = value.size(); + } + else + { + // get the base object + Serializable *serializable = dynamic_cast(base); + + // call the serialize method on the object + auto value = serializable->serialize(); + + // allocate the buffer, and copy the data into it (the zend engine will + // (hopefully) clean up the data for us - the default serialize method does + // it like this too) + *buffer = (unsigned char*)estrndup(value.c_str(), value.size()); + *buf_len = value.size(); + } + + // done + return SUCCESS; } catch (Throwable &throwable) { @@ -1343,9 +1376,6 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, // unreachable return FAILURE; } - - // done - return SUCCESS; } /** @@ -1358,17 +1388,38 @@ int ClassImpl::serialize(zval *object, unsigned char **buffer, size_t *buf_len, */ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned char *buffer, size_t buf_len, zend_unserialize_data *data) { - // create the PHP object - object_init_ex(object, entry); - - // turn this into a serializale - Serializable *serializable = dynamic_cast(ObjectImpl::find(object)->object()); - - // user may throw an exception in the serialize() function + // user may throw an exception in the unserialize() function try { - // call the unserialize method on it - serializable->unserialize((const char *)buffer, buf_len); + // create the PHP object + object_init_ex(object, entry); + + // get the base object + Base *base = dynamic_cast(ObjectImpl::find(object)->object()); + + // we are going to check if the serialize method was overridden in user-space + zend_function *func = (zend_function *)zend_hash_str_find_ptr(&entry->function_table, "unserialize", sizeof("unserialize")-1); + + // do we have a user-space alternative? + if (func && func->type == ZEND_USER_FUNCTION) + { + // construct object to make the call + Php::Object self(entry, base); + + // makek the unserialize call + self.call("unserialize", Php::Value((const char *)buffer, buf_len)); + } + else + { + // turn this into a serializale + Serializable *serializable = dynamic_cast(base); + + // call the unserialize method on it + serializable->unserialize((const char *)buffer, buf_len); + } + + // done + return SUCCESS; } catch (Throwable &throwable) { @@ -1380,9 +1431,6 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned // unreachable return FAILURE; } - - // done - return SUCCESS; } /** From f50252814230cce65ae099203bf97c1c3068d848 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Oct 2025 16:20:56 +0200 Subject: [PATCH 100/101] small typos --- zend/classimpl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp index 887bd8ef..baa6f0de 100644 --- a/zend/classimpl.cpp +++ b/zend/classimpl.cpp @@ -1397,7 +1397,7 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned // get the base object Base *base = dynamic_cast(ObjectImpl::find(object)->object()); - // we are going to check if the serialize method was overridden in user-space + // we are going to check if the unserialize method was overridden in user-space zend_function *func = (zend_function *)zend_hash_str_find_ptr(&entry->function_table, "unserialize", sizeof("unserialize")-1); // do we have a user-space alternative? @@ -1406,7 +1406,7 @@ int ClassImpl::unserialize(zval *object, zend_class_entry *entry, const unsigned // construct object to make the call Php::Object self(entry, base); - // makek the unserialize call + // make the unserialize call self.call("unserialize", Php::Value((const char *)buffer, buf_len)); } else From c083e812b740931bf1e72b1aeec1d27257a71ec0 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Mon, 6 Oct 2025 19:36:31 +0200 Subject: [PATCH 101/101] prepare for upcoming release --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 06fcd6bc..0c5d67ed 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,7 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # SONAME = 2.4 -VERSION = 2.4.11 +VERSION = 2.4.12 #