summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Makefile131
-rw-r--r--README.md2
-rw-r--r--documentation/ten-reasons-for-using-php-cpp.html2
-rw-r--r--include/array.h4
-rw-r--r--include/base.h8
-rw-r--r--include/byref.h10
-rw-r--r--include/byval.h10
-rw-r--r--include/call.h18
-rw-r--r--include/class.h2
-rw-r--r--include/classbase.h2
-rw-r--r--include/exception.h4
-rw-r--r--include/file.h106
-rw-r--r--include/global.h4
-rw-r--r--include/hashmember.h2
-rw-r--r--include/hiddenpointer.h2
-rw-r--r--include/ini.h4
-rw-r--r--include/object.h53
-rw-r--r--include/opcodes.h69
-rw-r--r--include/script.h107
-rw-r--r--include/type.h2
-rw-r--r--include/value.h202
-rw-r--r--phpcpp.h6
-rw-r--r--tests/cpp/include/doubl2str.h98
-rw-r--r--zend/callable.cpp12
-rw-r--r--zend/callable.h2
-rw-r--r--zend/classimpl.cpp69
-rw-r--r--zend/classimpl.h6
-rw-r--r--zend/compileroptions.h57
-rw-r--r--zend/eval.cpp145
-rw-r--r--zend/exists.cpp77
-rw-r--r--zend/file.cpp160
-rw-r--r--zend/hashiterator.h16
-rw-r--r--zend/includes.h5
-rw-r--r--zend/object.cpp44
-rw-r--r--zend/objectimpl.h18
-rw-r--r--zend/opcodes.cpp131
-rw-r--r--zend/parametersimpl.h2
-rw-r--r--zend/script.cpp60
-rw-r--r--zend/super.cpp4
-rw-r--r--zend/value.cpp574
41 files changed, 1504 insertions, 729 deletions
diff --git a/.gitignore b/.gitignore
index 5aa80fe..8e44606 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
*.o
+*.a
*.so
*.txt
-create_config
-/include/config.h
diff --git a/Makefile b/Makefile
index 9b991f2..9000d6d 100644
--- a/Makefile
+++ b/Makefile
@@ -17,7 +17,7 @@
# installed in the default directory, you can change that here.
#
-PHP_CONFIG = php-config
+PHP_CONFIG = php-config
#
@@ -29,7 +29,7 @@ PHP_CONFIG = php-config
# Usually /usr/bin/php
#
-PHP_BIN = $(shell ${PHP_CONFIG} --php-binary)
+PHP_BIN = $(shell ${PHP_CONFIG} --php-binary)
#
@@ -42,9 +42,9 @@ PHP_BIN = $(shell ${PHP_CONFIG} --php-binary)
# and /usr/local/lib. You can of course change it to whatever suits you best
#
-INSTALL_PREFIX = /usr
-INSTALL_HEADERS = ${INSTALL_PREFIX}/include
-INSTALL_LIB = ${INSTALL_PREFIX}/lib
+INSTALL_PREFIX = /usr
+INSTALL_HEADERS = ${INSTALL_PREFIX}/include
+INSTALL_LIB = ${INSTALL_PREFIX}/lib
#
@@ -55,8 +55,10 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib
# you can change that here.
#
-PHP_LIBRARY = libphpcpp.so
-HHVM_LIBRARY = libhhvmcpp.so
+PHP_SHARED_LIBRARY = libphpcpp.so
+PHP_STATIC_LIBRARY = libphpcpp.a
+HHVM_SHARED_LIBRARY = libhhvmcpp.so
+HHVM_STATIC_LIBRARY = libhhvmcpp.a
#
@@ -69,9 +71,19 @@ HHVM_LIBRARY = libhhvmcpp.so
# library file. By default, g++ (the GNU C++ compiler) is used for both.
#
-COMPILER = g++
-LINKER = g++
+ifdef CXX
+ COMPILER = ${CXX}
+ LINKER = ${CXX}
+else
+ COMPILER = g++
+ LINKER = g++
+endif
+ifdef AR
+ ARCHIVER = ${AR} rcs
+else
+ ARCHIVER = ar rcs
+endif
#
# Compiler flags
@@ -85,9 +97,11 @@ LINKER = g++
# you want to leave that flag out on production servers).
#
-COMPILER_FLAGS = -Wall -c -g -std=c++11 -fpic
-PHP_COMPILER_FLAGS = ${COMPILER_FLAGS} `php-config --includes`
-HHVM_COMPILER_FLAGS = ${COMPILER_FLAGS}
+COMPILER_FLAGS = -Wall -c -g -std=c++11
+SHARED_COMPILER_FLAGS = -fpic
+STATIC_COMPILER_FLAGS =
+PHP_COMPILER_FLAGS = ${COMPILER_FLAGS} `${PHP_CONFIG} --includes`
+HHVM_COMPILER_FLAGS = ${COMPILER_FLAGS}
#
# Linker flags
@@ -99,9 +113,9 @@ HHVM_COMPILER_FLAGS = ${COMPILER_FLAGS}
# to the linker flags
#
-LINKER_FLAGS = -shared
-PHP_LINKER_FLAGS = ${LINKER_FLAGS} `php-config --ldflags`
-HHVM_LINKER_FLAGS = ${LINKER_FLAGS}
+LINKER_FLAGS = -shared
+PHP_LINKER_FLAGS = ${LINKER_FLAGS} `${PHP_CONFIG} --ldflags`
+HHVM_LINKER_FLAGS = ${LINKER_FLAGS}
#
@@ -111,9 +125,9 @@ HHVM_LINKER_FLAGS = ${LINKER_FLAGS}
# So you can probably leave this as it is
#
-RM = rm -f
-CP = cp -f
-MKDIR = mkdir -p
+RM = rm -fr
+CP = cp -f
+MKDIR = mkdir -p
#
@@ -124,9 +138,9 @@ MKDIR = mkdir -p
# probably necessary here
#
-COMMON_SOURCES = $(wildcard common/*.cpp)
-PHP_SOURCES = $(wildcard zend/*.cpp)
-HHVM_SOURCES = $(wildcard hhvm/*.cpp)
+COMMON_SOURCES = $(wildcard common/*.cpp)
+PHP_SOURCES = $(wildcard zend/*.cpp)
+HHVM_SOURCES = $(wildcard hhvm/*.cpp)
#
# The object files
@@ -137,9 +151,12 @@ HHVM_SOURCES = $(wildcard hhvm/*.cpp)
# takes all source files.
#
-COMMON_OBJECTS = $(COMMON_SOURCES:%.cpp=%.o)
-PHP_OBJECTS = $(PHP_SOURCES:%.cpp=%.o)
-HHVM_OBJECTS = $(HHVM_SOURCES:%.cpp=%.o)
+COMMON_SHARED_OBJECTS = $(COMMON_SOURCES:%.cpp=shared/%.o)
+PHP_SHARED_OBJECTS = $(PHP_SOURCES:%.cpp=shared/%.o)
+HHVM_SHARED_OBJECTS = $(HHVM_SOURCES:%.cpp=shared/%.o)
+COMMON_STATIC_OBJECTS = $(COMMON_SOURCES:%.cpp=static/%.o)
+PHP_STATIC_OBJECTS = $(PHP_SOURCES:%.cpp=static/%.o)
+HHVM_STATIC_OBJECTS = $(HHVM_SOURCES:%.cpp=static/%.o)
#
@@ -147,39 +164,71 @@ HHVM_OBJECTS = $(HHVM_SOURCES:%.cpp=%.o)
# dependencies that are used by the compiler.
#
-all: ${PHP_LIBRARY}
+all: phpcpp
-phpcpp: ${PHP_LIBRARY}
-
-hhvmcpp: ${HHVM_LIBRARY}
+phpcpp: ${PHP_SHARED_LIBRARY} ${PHP_STATIC_LIBRARY}
+ @echo
+ @echo "Build complete."
+ @echo "Don't forget to run 'make test'."
-${PHP_LIBRARY}: ${COMMON_OBJECTS} ${PHP_OBJECTS}
- ${LINKER} ${PHP_LINKER_FLAGS} -o $@ ${COMMON_OBJECTS} ${PHP_OBJECTS}
+hhvmcpp: ${HHVM_SHARED_LIBRARY} ${PHP_STATIC_LIBRARY}
@echo
@echo "Build complete."
@echo "Don't forget to run 'make test'."
-${HHVM_LIBRARY}: ${COMMON_OBJECTS} ${HHVM_OBJECTS}
- ${LINKER} ${HHVM_LINKER_FLAGS} -o $@ ${COMMON_OBJECTS} ${HHVM_OBJECTS}
+${PHP_SHARED_LIBRARY}: shared_directories ${COMMON_SHARED_OBJECTS} ${PHP_SHARED_OBJECTS}
+ ${LINKER} ${PHP_LINKER_FLAGS} -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}
+
+${HHVM_SHARED_LIBRARY}: shared_directories ${COMMON_SHARED_OBJECTS} ${HHVM_SHARED_OBJECTS}
+ ${LINKER} ${HHVM_LINKER_FLAGS} -o $@ ${COMMON_SHARED_OBJECTS} ${HHVM_SHARED_OBJECTS}
+
+${HHVM_STATIC_LIBRARY}: static_directories ${COMMON_STATIC_OBJECTS} ${HHVM_STATIC_OBJECTS}
+ ${ARCHIVER} $@ ${COMMON_STATIC_OBJECTS} ${HHVM_STATIC_OBJECTS}
+
+shared_directories:
+ ${MKDIR} shared/common
+ ${MKDIR} shared/zend
+ ${MKDIR} shared/hhvm
+
+static_directories:
+ ${MKDIR} static/common
+ ${MKDIR} static/zend
+ ${MKDIR} static/hhvm
clean:
- ${RM} ${COMMON_OBJECTS} ${PHP_OBJECTS} ${HHVM_OBJECTS} ${PHP_LIBRARY} ${HHVM_LIBRARY}
+ ${RM} shared ${PHP_SHARED_LIBRARY} ${HHVM_SHARED_LIBRARY}
+ ${RM} static ${PHP_STATIC_LIBRARY} ${HHVM_STATIC_LIBRARY}
+ find -name *.o | xargs ${RM}
+
+${COMMON_SHARED_OBJECTS}:
+ ${COMPILER} ${COMPILER_FLAGS} ${SHARED_COMPILER_FLAGS} -o $@ ${@:shared/%.o=%.cpp}
+
+${COMMON_STATIC_OBJECTS}:
+ ${COMPILER} ${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 $@ ${@:%.o=%.cpp}
+${PHP_STATIC_OBJECTS}:
+ ${COMPILER} ${PHP_COMPILER_FLAGS} ${STATIC_COMPILER_FLAGS} -o $@ ${@:static/%.o=%.cpp}
-${PHP_OBJECTS}:
- ${COMPILER} ${PHP_COMPILER_FLAGS} -o $@ ${@:%.o=%.cpp}
+${HHVM_SHARED_OBJECTS}:
+ ${COMPILER} ${HHVM_COMPILER_FLAGS} ${SHARED_COMPILER_FLAGS} -o $@ ${@:shared/%.o=%.cpp}
-${HHVM_OBJECTS}:
- ${COMPILER} ${HHVM_COMPILER_FLAGS} -o $@ ${@:%.o=%.cpp}
+${HHVM_STATIC_OBJECTS}:
+ ${COMPILER} ${HHVM_COMPILER_FLAGS} ${STATIC_COMPILER_FLAGS} -o $@ ${@:static/%.o=%.cpp}
install:
${MKDIR} ${INSTALL_HEADERS}/phpcpp
${CP} phpcpp.h ${INSTALL_HEADERS}
${CP} include/*.h ${INSTALL_HEADERS}/phpcpp
- if [ -e ${PHP_LIBRARY} ]; then ${CP} ${PHP_LIBRARY} ${INSTALL_LIB}; fi
- if [ -e ${HHVM_LIBRARY} ]; then ${CP} ${HHVM_LIBRARY} ${INSTALL_LIB}; fi
+ if [ -e ${PHP_SHARED_LIBRARY} ]; then ${CP} ${PHP_SHARED_LIBRARY} ${INSTALL_LIB}; fi
+ if [ -e ${PHP_STATIC_LIBRARY} ]; then ${CP} ${PHP_STATIC_LIBRARY} ${INSTALL_LIB}; fi
+ if [ -e ${HHVM_SHARED_LIBRARY} ]; then ${CP} ${HHVM_SHARED_LIBRARY} ${INSTALL_LIB}; fi
+ if [ -e ${HHVM_STATIC_LIBRARY} ]; then ${CP} ${HHVM_STATIC_LIBRARY} ${INSTALL_LIB}; fi
test:
mkdir -p ./tests/include/zts/phpcpp
diff --git a/README.md b/README.md
index 17d1b62..488aad4 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ C++, and the PHP-CPP library uses all the power offered by C++11 to convert the
values from your functions to/and from PHP:
```c
-Php::Value hello_word()
+Php::Value hello_world()
{
return "hello world!";
}
diff --git a/documentation/ten-reasons-for-using-php-cpp.html b/documentation/ten-reasons-for-using-php-cpp.html
index 484f3fb..7d830d4 100644
--- a/documentation/ten-reasons-for-using-php-cpp.html
+++ b/documentation/ten-reasons-for-using-php-cpp.html
@@ -71,7 +71,7 @@
C++ is a proven language with a more than 40 year long history. C++ has an
official open standard and is controlled by a C++ standards committee with
members that have proven track records. Compilers have been developed by companies
- like Microsoft, IBM, Intel, Apple and there there are several open source
+ like Microsoft, IBM, Intel, Apple and there are several open source
compilers available (GNU, CLANG), so you can always switch to a faster or
more stable alternative. The compiler vendors are constantly motivated to
be better than their competitors and bring out new versions of their compilers
diff --git a/include/array.h b/include/array.h
index 4d6b6b1..d238862 100644
--- a/include/array.h
+++ b/include/array.h
@@ -38,7 +38,7 @@ public:
* Move constructor from a value object
* @param value
*/
- Array(Value &&value) : Value(std::move(value))
+ Array(Value &&value) noexcept : Value(std::move(value))
{
// type must be valid
if (value.type() != Type::Array) throw FatalError("Moving a non-array to an array variable");
@@ -107,7 +107,7 @@ public:
* @param value
* @return Array
*/
- Array &operator=(Value &&value)
+ Array &operator=(Value &&value) noexcept
{
// skip self assignment
if (this == &value) return *this;
diff --git a/include/base.h b/include/base.h
index 87c7483..f5ebed1 100644
--- a/include/base.h
+++ b/include/base.h
@@ -87,7 +87,7 @@ public:
*/
Value operator[](const char *name) const
{
- return Value(this)[name];
+ return Value(this).get(name);
}
/**
@@ -97,7 +97,7 @@ public:
*/
Value operator[](const std::string &name) const
{
- return Value(this)[name];
+ return Value(this).get(name);
}
/**
@@ -107,7 +107,7 @@ public:
*/
Value property(const char *name) const
{
- return Value(this)[name];
+ return Value(this).get(name);
}
/**
@@ -117,7 +117,7 @@ public:
*/
Value property(const std::string &name) const
{
- return Value(this)[name];
+ return Value(this).get(name);
}
/**
diff --git a/include/byref.h b/include/byref.h
index e828660..ff60981 100644
--- a/include/byref.h
+++ b/include/byref.h
@@ -24,8 +24,8 @@ public:
* @param type Argument type
* @param required Is this argument required?
*/
- ByRef(const char *name, Type type, bool required = true) : Argument(name, type, required, true) {}
-
+ ByRef(const char *name, Type type = Type::Null, bool required = true) : Argument(name, type, required, true) {}
+
/**
* Constructor
* @param name Name of the argument
@@ -34,7 +34,7 @@ public:
* @param required Is this argument required?
*/
ByRef(const char *name, const char *classname, bool nullable = false, bool required = true) : Argument(name, classname, nullable, required, true) {}
-
+
/**
* Copy constructor
* @param argument
@@ -45,8 +45,8 @@ public:
* Move constructor
* @param argument
*/
- ByRef(ByRef &&argument) : Argument(argument) {}
-
+ ByRef(ByRef &&argument) noexcept : Argument(argument) {}
+
/**
* Destructor
*/
diff --git a/include/byval.h b/include/byval.h
index 40a458c..3606df7 100644
--- a/include/byval.h
+++ b/include/byval.h
@@ -24,8 +24,8 @@ public:
* @param type Argument type
* @param required Is this argument required?
*/
- ByVal(const char *name, Type type, bool required = true) : Argument(name, type, required, false) {}
-
+ ByVal(const char *name, Type type = Type::Null, bool required = true) : Argument(name, type, required, false) {}
+
/**
* Constructor
* @param name Name of the argument
@@ -34,7 +34,7 @@ public:
* @param required Is this argument required?
*/
ByVal(const char *name, const char *classname, bool nullable = false, bool required = true) : Argument(name, classname, nullable, required, false) {}
-
+
/**
* Copy constructor
* @param argument
@@ -45,8 +45,8 @@ public:
* Move constructor
* @param argument
*/
- ByVal(ByVal &&argument) : Argument(argument) {}
-
+ ByVal(ByVal &&argument) noexcept : Argument(argument) {}
+
/**
* Destructor
*/
diff --git a/include/call.h b/include/call.h
index 16de1fe..279cbac 100644
--- a/include/call.h
+++ b/include/call.h
@@ -13,6 +13,24 @@
namespace Php {
/**
+ * List of functions that are available for use in PHP
+ */
+extern bool class_exists(const char *classname, size_t size, bool autoload = true);
+inline bool class_exists(const char *classname, bool autoload = true) { return class_exists(classname, strlen(classname), autoload); }
+inline bool class_exists(const std::string &classname, bool autoload = true) { return class_exists(classname.c_str(), classname.size(), autoload); }
+extern Value eval(const std::string &phpCode);
+extern Value include(const std::string &filename);
+extern Value include_once(const std::string &filename);
+inline bool is_a(const Value &obj, const char *classname, size_t size, bool allow_string = false) { return obj.instanceOf(classname, size, allow_string); }
+inline bool is_a(const Value &obj, const char *classname, bool allow_string = false) { return is_a(obj, classname, strlen(classname), allow_string); }
+inline bool is_a(const Value &obj, const std::string &classname, bool allow_string = false) { return is_a(obj, classname.c_str(), classname.size(), allow_string); }
+inline bool is_subclass_of(const Value &obj, const char *classname, size_t size, bool allow_string = true) { return obj.derivedFrom(classname, size, allow_string); }
+inline bool is_subclass_of(const Value &obj, const char *classname, bool allow_string = true) { return is_subclass_of(obj, classname, strlen(classname), allow_string); }
+inline bool is_subclass_of(const Value &obj, const std::string &classname, bool allow_string = true) { return is_subclass_of(obj, classname.c_str(), classname.size(), allow_string); }
+extern Value require(const std::string &filename);
+extern Value require_once(const std::string &filename);
+
+/**
* Call a function in PHP
* @param name Name of the function to call
* @param params Variable number of parameters
diff --git a/include/class.h b/include/class.h
index cffc7bd..9257213 100644
--- a/include/class.h
+++ b/include/class.h
@@ -48,7 +48,7 @@ public:
* Move constructor
* @param that
*/
- Class(Class<T> &&that) : ClassBase(std::move(that)) {}
+ Class(Class<T> &&that) noexcept : ClassBase(std::move(that)) {}
/**
* Destructor
diff --git a/include/classbase.h b/include/classbase.h
index 9f0bac3..1129298 100644
--- a/include/classbase.h
+++ b/include/classbase.h
@@ -85,7 +85,7 @@ public:
* Move constructor
* @param that
*/
- ClassBase(ClassBase &&that) : _impl(std::move(that._impl)) {}
+ ClassBase(ClassBase &&that) noexcept : _impl(std::move(that._impl)) {}
/**
* Destructor
diff --git a/include/exception.h b/include/exception.h
index aff0afc..94aaa85 100644
--- a/include/exception.h
+++ b/include/exception.h
@@ -52,7 +52,11 @@ public:
* Overridden what method
* @return const char *
*/
+#ifdef _NOEXCEPT
+ virtual const char *what() const _NOEXCEPT override
+#else
virtual const char *what() const noexcept override
+#endif
{
return _message.c_str();
}
diff --git a/include/file.h b/include/file.h
new file mode 100644
index 0000000..7029bb6
--- /dev/null
+++ b/include/file.h
@@ -0,0 +1,106 @@
+/**
+ * File.h
+ *
+ * Extended script, a PHP source file name can be passed to a Php::File object
+ * to have it evaluated.
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class File
+{
+public:
+ /**
+ * Constructor
+ *
+ * The constructor receives a filename as parameter. It uses the normal
+ * PHP include path resolve algorithms to find the location of the file.
+ *
+ * @param name the filename
+ * @param size size of the filename
+ */
+ File(const char *name, size_t size);
+
+ /**
+ * Alternative constructor with just a filename
+ *
+ * @param name the filename
+ */
+ File(const char *name) : File(name, ::strlen(name)) {}
+
+ /**
+ * Alternative constructor with a string object
+ * @param name the filename
+ */
+ File(const std::string &name) : File(name.c_str(), name.size()) {}
+
+ /**
+ * Alternative constructor with a Value object
+ * @param name the filename
+ */
+ File(const Value &value) : File(value.stringValue()) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~File();
+
+ /**
+ * Does the file exist?
+ * @return boolean
+ */
+ bool exists();
+
+ /**
+ * Is this a valid file?
+ * @return boolean
+ */
+ bool valid();
+
+ /**
+ * Execute the file once (do nothing if the file already was executed)
+ * @return Php::Value
+ */
+ Value once();
+
+ /**
+ * Execute the file
+ * @return Php::Value
+ */
+ Value execute();
+
+private:
+ /**
+ * The full resolved path name
+ * @var const char *
+ */
+ char *_path = nullptr;
+
+ /**
+ * The opcodes of this file
+ * @var Opcodes
+ */
+ Opcodes *_opcodes = nullptr;
+
+ /**
+ * Compile the file
+ * @return bool
+ */
+ bool compile();
+
+};
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/include/global.h b/include/global.h
index ae54212..4593e5d 100644
--- a/include/global.h
+++ b/include/global.h
@@ -34,7 +34,7 @@ public:
* Move constructor
* @param global
*/
- Global(Global &&global) : Value(std::move(global)), _name(std::move(global._name)), _exists(global._exists) {}
+ Global(Global &&global) noexcept : Value(std::move(global)), _name(std::move(global._name)), _exists(global._exists) {}
/**
* Destructor
@@ -70,7 +70,7 @@ public:
* @return Global
*/
/*
- Global &operator=(Global &&global)
+ Global &operator=(Global &&global) noexcept
{
// skip self assignment
if (&global == this) return *this;
diff --git a/include/hashmember.h b/include/hashmember.h
index 6853228..b5b6c17 100644
--- a/include/hashmember.h
+++ b/include/hashmember.h
@@ -579,7 +579,7 @@ protected:
* Move constructor
* @param value Other element
*/
-// HashMember(HashMember<Type> &&member) :
+// HashMember(HashMember<Type> &&member) noexcept :
// _parent(std::move(member._parent)), _index(std::move(member._index)) {}
private:
diff --git a/include/hiddenpointer.h b/include/hiddenpointer.h
index dc981c0..5465ebb 100644
--- a/include/hiddenpointer.h
+++ b/include/hiddenpointer.h
@@ -84,7 +84,7 @@ public:
* Move constructor
* @param that
*/
- HiddenPointer(HiddenPointer<Type> &&that) : _allocated(that._allocated), _buffer(that._buffer)
+ HiddenPointer(HiddenPointer<Type> &&that) noexcept : _allocated(that._allocated), _buffer(that._buffer)
{
// the other object is no longer allocated
that._allocated = false;
diff --git a/include/ini.h b/include/ini.h
index 1d881e9..69137a2 100644
--- a/include/ini.h
+++ b/include/ini.h
@@ -125,7 +125,11 @@ private:
* @param value
* @return string
*/
+#ifdef _MSC_VER
+ static const char* bool2str(const bool value)
+#else
static constexpr const char* bool2str(const bool value)
+#endif
{
// cast to a string
return ( static_cast<bool>(value) ? "On" : "Off");
diff --git a/include/object.h b/include/object.h
index 2e6628a..2e92f6a 100644
--- a/include/object.h
+++ b/include/object.h
@@ -25,28 +25,11 @@ public:
Object() : Value(Type::Object) {}
/**
- * Move constructor is passed to the parent
- * @param value
- */
- Object(Value &&value) : Value(std::move(value))
- {
- // throw exception in case of problems
- if (value.type() != Type::Object) throw FatalError("Constructing an object variable by moving a non object");
- }
-
- /**
* Copy constructor is valid if the passed in object is also an object,
* or when it is a string holding a classname
* @param that An other object
*/
- Object(const Value &value) : Value()
- {
- // string types are instantiated
- if (value.isString()) instantiate(value);
-
- // otherwise copy the other object
- else operator=(value);
- }
+ Object(const Value &value);
/**
* Constructor to create a new instance of a builtin class
@@ -88,28 +71,10 @@ public:
* number of parameters that are passed to the constructor
*
* @param name Name of the class to instantiate
- * @param arg0 Optional argument 1
- * @param arg1 Optional argument 2
- * @param arg2 Optional argument 3
- * @param arg3 Optional argument 4
- * @param arg4 Optional argument 5
- * @param arg5 Optional argument 6
- * @param arg6 Optional argument 7
- * @param arg7 Optional argument 8
- * @param arg8 Optional argument 9
- * @param arg9 Optional argument 10
+ * @param args Optional arguments
*/
- Object(const char *name) { instantiate(name); call("__construct"); }
- Object(const char *name, Value p0) { instantiate(name); call("__construct", p0); }
- Object(const char *name, Value p0, Value p1) { instantiate(name); call("__construct", p0, p1); }
- Object(const char *name, Value p0, Value p1, Value p2) { instantiate(name); call("__construct", p0, p1, p2); }
- Object(const char *name, Value p0, Value p1, Value p2, Value p3) { instantiate(name); call("__construct", p0, p1, p2, p3); }
- Object(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4) { instantiate(name); call("__construct", p0, p1, p2, p3, p4); }
- Object(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5) { instantiate(name); call("__construct", p0, p1, p2, p3, p4, p5); }
- Object(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6) { instantiate(name); call("__construct", p0, p1, p2, p3, p4, p5, p6); }
- Object(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7) { instantiate(name); call("__construct", p0, p1, p2, p3, p4, p5, p6, p7); }
- Object(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8) { instantiate(name); call("__construct", p0, p1, p2, p3, p4, p5, p6, p7, p8); }
- Object(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8, Value p9) { instantiate(name); call("__construct", p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); }
+ template <typename ...Args>
+ Object(const char *name, Args&&... args) : Value() { if (instantiate(name)) call("__construct", std::forward<Value>(args)...); }
/**
* Destructor
@@ -154,7 +119,7 @@ public:
* @param value
* @return ForcedValue
*/
- Object &operator=(Value &&value)
+ Object &operator=(Value &&value) noexcept
{
// skip self assignment
if (this == &value) return *this;
@@ -172,10 +137,14 @@ public:
private:
/**
* Helper method to instantiate an object
+ *
+ * This method returns true if there is a __construct() function, and
+ * false otherwise
+ *
* @param name Class name
+ * @return bool
*/
- void instantiate(const char *name);
-
+ bool instantiate(const char *name);
};
/**
diff --git a/include/opcodes.h b/include/opcodes.h
new file mode 100644
index 0000000..5a40d05
--- /dev/null
+++ b/include/opcodes.h
@@ -0,0 +1,69 @@
+/**
+ * Opcodes.h
+ *
+ * Class represents a set of opcodes of a PHP script that can be executed. This
+ * is an internal file that you normally do not have to instantiate yourself.
+ * Better use the Php::Script of Php::File classes.
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Forward declarations
+ */
+struct _zend_op_array;
+
+/**
+ * Namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class Opcodes
+{
+public:
+ /**
+ * Constructor
+ * @param opcodes
+ */
+ Opcodes(struct _zend_op_array *opcodes) : _opcodes(opcodes)
+ {
+ // no other initialisation is necessary
+ }
+
+ /**
+ * Destructor
+ */
+ virtual ~Opcodes();
+
+ /**
+ * Are the opcodes valid?
+ * @return bool
+ */
+ bool valid() const
+ {
+ return _opcodes != nullptr;
+ }
+
+ /**
+ * Execute the opcodes
+ * @return Value
+ */
+ Value execute() const;
+
+private:
+ /**
+ * The opcodes
+ * @var zend_op_array
+ */
+ struct _zend_op_array *_opcodes;
+};
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/include/script.h b/include/script.h
new file mode 100644
index 0000000..a9c2921
--- /dev/null
+++ b/include/script.h
@@ -0,0 +1,107 @@
+/**
+ * Script.h
+ *
+ * Class that can be used to evaluate a PHP script in the current PHP context.
+ *
+ * The difference between directly calling eval() is that the script object
+ * will first evaluate the string, and then it can be executed multiple times.
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Forward declarations
+ */
+struct _zend_op_array;
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class Script
+{
+public:
+ /**
+ * Constructor
+ *
+ * The constructor will not throw any exceptions, even when invalid
+ * PHP code is passed to it that can not be evaluated. You should call
+ * the valid() to find out if the script was valid (could be parsed).
+ *
+ * @param name Name of the PHP script
+ * @param source PHP source code to be evaluated
+ * @param size Length of the source code
+ */
+ Script(const char *name, const char *source, size_t size) noexcept;
+
+ /**
+ * Alternative constructor without a size
+ * @param name Name of the PHP script
+ * @param source PHP source code to be evaluated
+ */
+ Script(const char *name, const char *source) noexcept : Script(name, source, ::strlen(source)) {}
+
+ /**
+ * Alternative constructor without a name
+ * @param source PHP source code to be evaluated
+ * @param size Length of the source code
+ */
+ Script(const char *source, size_t size) noexcept : Script("Unknown", source, size) {}
+
+ /**
+ * Alternative constructor without a name and without a size
+ * @param source PHP source code to be evaluated
+ */
+ Script(const char *source) noexcept : Script("Unknown", source, ::strlen(source)) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~Script() {}
+
+ /**
+ * Is the script a valid PHP script without syntax errors?
+ * @return bool
+ */
+ bool valid() const
+ {
+ return _opcodes.valid();
+ }
+
+ /**
+ * Execute the script
+ * The return value of the script is returned
+ * @return Value
+ */
+ Value execute() const
+ {
+ return _opcodes.execute();
+ }
+
+private:
+ /**
+ * The opcodes
+ * @var Opcodes
+ */
+ Opcodes _opcodes;
+
+ /**
+ * Helper function to compile the source code
+ * @param name name of the script
+ * @param script actual PHP code
+ * @param size length of the string
+ * @return opcodes
+ */
+ static struct _zend_op_array *compile(const char *name, const char *phpcode, size_t size);
+
+};
+
+/**
+ * End of namespace
+ */
+}
diff --git a/include/type.h b/include/type.h
index bec0fd5..d248455 100644
--- a/include/type.h
+++ b/include/type.h
@@ -18,7 +18,7 @@ namespace Php {
* The values are the same as the ones used internally in Zend
*/
enum class Type : unsigned char {
- Null = 0,
+ Null = 0, // Null will allow any type
Numeric = 1,
Float = 2,
Bool = 3,
diff --git a/include/value.h b/include/value.h
index f72304c..363779d 100644
--- a/include/value.h
+++ b/include/value.h
@@ -100,7 +100,7 @@ public:
* @param value
*/
template <typename T>
- Value(const std::map<std::string,T> &value)
+ Value(const std::map<std::string,T> &value) : Value(Type::Array)
{
// set all elements
for (auto &iter : value) setRaw(iter.first.c_str(), iter.first.size(), iter.second);
@@ -110,7 +110,6 @@ public:
* Wrap object around zval
* @param zval Zval to wrap
* @param ref Force this to be a reference
- * @param tsrm_ls Optional pointer to thread safe data
*/
Value(struct _zval_struct *zval, bool ref=false);
@@ -130,7 +129,7 @@ public:
* Move constructor
* @param value
*/
- Value(Value &&that);
+ Value(Value &&that) noexcept;
/**
* Destructor
@@ -142,7 +141,7 @@ public:
* @param value
* @return Value
*/
- Value &operator=(Value &&value);
+ Value &operator=(Value &&value) noexcept;
/**
* Assignment operator for various types
@@ -406,7 +405,7 @@ public:
char *reserve(size_t size);
/**
- * Get access to the raw buffer for read operationrs.
+ * Get access to the raw buffer for read operations.
* @return const char *
*/
const char *rawValue() const;
@@ -452,35 +451,64 @@ public:
template <typename T>
std::vector<T> vectorValue() const
{
-
-
// only works for arrays, other types return an empty vector
if (!isArray()) return std::vector<T>();
// allocate a result
std::vector<T> result;
-
+
// reserve enough space
size_t count = size();
result.reserve(count);
-
+
// and fill the result vector
- for (size_t i = 0; i<count; i++)
+ for (size_t i = 0; i<count; i++)
{
// check if the index exists
if (!contains(i)) continue;
-
- // get the value object
- Value value(get(i));
-
- // add it to the vector
- result.push_back(value);
+
+ // get the value and add it to the vector
+ result.push_back(get(i));
}
-
+
// done
return result;
}
-
+
+ /**
+ * Convert the object to a set
+ *
+ * This only works for regular arrays that are indexed by a number, start
+ * with position 0 and have no empty spaces.
+ *
+ * return std::vector
+ */
+ template <typename T>
+ std::set<T> setValue() const
+ {
+ // only works for arrays, other types return an empty set
+ if (!isArray()) return std::set<T>();
+
+ // allocate a result
+ std::set<T> result;
+
+ // how many elements are we inserting
+ size_t count = size();
+
+ // and fill the result set
+ for (size_t i = 0; i<count; i++)
+ {
+ // check if the index exists
+ if (!contains(i)) continue;
+
+ // get the value and add it to the vector
+ result.insert(get(i));
+ }
+
+ // done
+ return result;
+ }
+
/**
* Convert the object to a map with string index and Php::Value value
* @return std::map
@@ -497,12 +525,20 @@ public:
// must be an array or an object, otherwise the map is empty
if (!isArray() && !isObject()) return std::map<std::string,T>();
- // get the original map value
- std::map<std::string,Php::Value> map(mapValue());
-
// result variable
std::map<std::string,T> result;
+ // iterate over the values
+ iterate([&result](const Value &key, const Value &value) {
+
+ // first convert the value to the appropriate type (otherwise
+ // compiler errors occur)
+ T val = value;
+
+ // add the value to the array
+ result[key] = val;
+ });
+
// done
return result;
}
@@ -618,7 +654,7 @@ public:
/**
* Cast to a number
- * @return uint64_t
+ * @return int64_t
*/
operator int64_t () const
{
@@ -642,7 +678,7 @@ public:
{
return stringValue();
}
-
+
/**
* Cast to byte array
* @return const char *
@@ -672,6 +708,16 @@ public:
}
/**
+ * Convert the object to a set
+ * @return std::set
+ */
+ template <typename T>
+ operator std::set<T>() const
+ {
+ return setValue<T>();
+ }
+
+ /**
* Convert the object to a map with string index and Php::Value value
* @return std::map
*/
@@ -898,24 +944,32 @@ public:
return get(key.stringValue());
}
-
/**
* Call the function in PHP
- * We have ten variants of this function, depending on the number of parameters
* This call operator is only useful when the variable represents a callable
* @return Value
*/
Value operator()() const;
- Value operator()(Value p0) const;
- Value operator()(Value p0, Value p1) const;
- Value operator()(Value p0, Value p1, Value p2) const;
- Value operator()(Value p0, Value p1, Value p2, Value p3) const;
- Value operator()(Value p0, Value p1, Value p2, Value p3, Value p4) const;
- Value operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5) const;
- Value operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6) const;
- Value operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7) const;
- Value operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8) const;
- Value operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8, Value p9) const;
+
+ /**
+ * Call the function - if the variable holds a callable thing
+ * @param args Optional arguments
+ * @return Value
+ */
+ template <typename ...Args>
+ Value operator()(Args&&... args) const
+ {
+ // store arguments
+ Value vargs[] = { static_cast<Value>(args)... };
+ //Value vargs[] = { std::forward<Value>(args)... };
+
+ // array of parameters
+ _zval_struct **params[sizeof...(Args)];
+ for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;}
+
+ // call the function
+ return exec(sizeof...(Args), params);
+ }
/**
* Call a method
@@ -925,16 +979,27 @@ public:
* @return Value
*/
Value call(const char *name);
- Value call(const char *name, Value p0);
- Value call(const char *name, Value p0, Value p1);
- Value call(const char *name, Value p0, Value p1, Value p2);
- Value call(const char *name, Value p0, Value p1, Value p2, Value p3);
- Value call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4);
- Value call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5);
- Value call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6);
- Value call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7);
- Value call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8);
- Value call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8, Value p9);
+
+ /**
+ *
+ * Call the method - if the variable holds an object with the given method
+ * @param name name of the method to call
+ * @param p0 The first parameter
+ * @return Value
+ */
+ template <typename ...Args>
+ Value call(const char *name, Args&&... args)
+ {
+ // store arguments
+ Value vargs[] = { static_cast<Value>(args)... };
+
+ // array of parameters
+ _zval_struct **params[sizeof...(Args)];
+ for(unsigned i=0; i < sizeof...(Args); i++) {params[i] = &vargs[i]._val;}
+
+ // call the function
+ return exec(name, sizeof...(Args), params);
+ }
/**
* Retrieve the original implementation
@@ -964,9 +1029,46 @@ public:
// try casting it
return dynamic_cast<T*>(base);
}
+
+ /**
+ * Check whether this object is an instance of a certain class
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
+ * @return bool
+ */
+ bool instanceOf(const char *classname, size_t size, bool allowString = false) const;
+ bool instanceOf(const char *classname, bool allowString = false) const { return instanceOf(classname, strlen(classname), allowString); }
+ bool instanceOf(const std::string &classname, bool allowString = false) const { return instanceOf(classname.c_str(), classname.size(), allowString); }
+
+ /**
+ * Check whether this object is derived from a certain class.
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
+ * @return bool
+ */
+ bool derivedFrom(const char *classname, size_t size, bool allowString = false) const;
+ bool derivedFrom(const char *classname, bool allowString = false) const { return derivedFrom(classname, strlen(classname), allowString); }
+ bool derivedFrom(const std::string &classname, bool allowString = false) const { return derivedFrom(classname.c_str(), classname.size(), allowString); }
+
private:
/**
+ * Iterate over key value pairs
+ * @param callback
+ */
+ void iterate(const std::function<void(const Php::Value &,const Php::Value &)> &callback) const;
+
+ /**
* Call function with a number of parameters
* @param argc Number of parameters
* @param argv The parameters
@@ -987,7 +1089,7 @@ private:
* Refcount - the number of references to the value
* @return int
*/
- int refcount();
+ int refcount() const;
protected:
/**
@@ -1049,6 +1151,13 @@ protected:
iterator createIterator(bool begin) const;
/**
+ * Retrieve the class entry
+ * @param allowString Allow the 'this' object to be a string
+ * @return zend_class_entry
+ */
+ struct _zend_class_entry *classEntry(bool allowString = true) const;
+
+ /**
* The Globals and Member classes can access the zval directly
*/
friend class Globals;
@@ -1061,6 +1170,7 @@ protected:
friend class HashMember<int>;
friend class HashMember<std::string>;
friend class Callable;
+ friend class Script;
};
/**
diff --git a/phpcpp.h b/phpcpp.h
index c3f3365..368018e 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -2,7 +2,7 @@
* phpcpp.h
*
* Library to build PHP extensions with CPP
- *
+ *
* @copyright 2013 CopernicA BV
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
*/
@@ -22,6 +22,7 @@
#include <exception>
#include <map>
#include <set>
+#include <functional>
/**
* Include all headers files that are related to this library
@@ -60,6 +61,9 @@
#include <phpcpp/namespace.h>
#include <phpcpp/extension.h>
#include <phpcpp/call.h>
+#include <phpcpp/opcodes.h>
+#include <phpcpp/script.h>
+#include <phpcpp/file.h>
/**
* Macro to export a function
diff --git a/tests/cpp/include/doubl2str.h b/tests/cpp/include/doubl2str.h
index 1a5e3fc..66f2b86 100644
--- a/tests/cpp/include/doubl2str.h
+++ b/tests/cpp/include/doubl2str.h
@@ -4,99 +4,13 @@
*
*/
-
-
-char num2char(unsigned int num)
+#include <sstream>
+#include <iomanip>
+std::string double2str(long double d)
{
- switch(num)
- {
- case 0:
- return '0';
- case 1:
- return '1';
- case 2:
- return '2';
- case 3:
- return '3';
- case 4:
- return '4';
- case 5:
- return '5';
- case 6:
- return '6';
- case 7:
- return '7';
- case 8:
- return '8';
- case 9:
- return '9';
- }
-
- //return '\0';
- return '-';
-}
-
-std::string double2str(long double D)
-{
- int sign = (D > 0) ? 1 : (D*=-1.0, -1);
- unsigned long long int Ceil = D;
-
- // if D is ceil
- if(Ceil == D)
- {
- return std::to_string( (long long)(D*sign) );
- }
-
- // size of result buffer
- const int bs = 32;
-
- // size of temporary buffer
- const int pw = 16;
- // Temporary buffer
- char buf[pw];
- // Result buffer
- char rez[bs];
-
- int i, size = 0;
- // set sign
- if(sign < 0) rez[size++] = '-';
-
- // set ceil
- std::string sceil = std::to_string(Ceil);
- const char * bceil = sceil.c_str();
- int sceillen = sceil.size();
- for(i = 0; i < sceillen; i++)
- {
- rez[size++] = bceil[i];
- }
-
- // set point
- rez[size++] = '.';
-
- unsigned long long int I = D * 10000000000000000; // D * 10**pw
- // .14159265359 -> 14159265359000000
- I -= Ceil * 10000000000000000;
-
- // Remove the tail of zeros
- // 14159265359000000 -> 14159265359
- while(0 == I % 10) I /= 10;
-
- int ind = 0;
- while(I > 0)
- {
- buf[ind++] = num2char(I%10);
- I = (I - I%10) / 10;
- }
-
- // set fraction part
- for(i = 0; i < ind; i++)
- {
- rez[size] = buf[ind-i-1];
- size++;
- }
-
- return std::string(rez, size);
- //rez[size] = '\0';
+ std::ostringstream strs;
+ strs << std::setprecision(16) << d;
+ return strs.str();
}
diff --git a/zend/callable.cpp b/zend/callable.cpp
index a85ab72..f0dc3e8 100644
--- a/zend/callable.cpp
+++ b/zend/callable.cpp
@@ -51,14 +51,14 @@ void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS)
{
// get the result
Value result(callable->invoke(params));
-
- // detach the zval (we don't want it to be destructed)
- zval *val = result.detach();
-
+
+ // we're ready if the return value is not even used
+ if (!return_value_used) return;
+
// @todo php 5.6 has a RETVAL_ZVAL_FAST macro that can be used instead (and is faster)
-
+
// return a full copy of the zval, and do not destruct it
- RETVAL_ZVAL(val, 1, 0);
+ RETVAL_ZVAL(result._val, 1, 0);
}
catch (Exception &exception)
{
diff --git a/zend/callable.h b/zend/callable.h
index 9958a2a..bd74f27 100644
--- a/zend/callable.h
+++ b/zend/callable.h
@@ -144,7 +144,7 @@ protected:
{
// fill members
info->name = arg.name();
- info->name_len = ::strlen(arg.name());
+ info->name_len = ::strlen(arg.name());
#if PHP_VERSION_ID >= 50400
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index b2acc24..e42605e 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -20,11 +20,6 @@ ClassImpl::~ClassImpl()
{
// destruct the entries
if (_entries) delete[] _entries;
-
- // php 5.3 deallocates the doc_comment by iself
-#if PHP_VERSION_ID >= 50400
- if (_comment) free(_comment);
-#endif
}
/**
@@ -41,18 +36,21 @@ static ClassImpl *self(zend_class_entry *entry)
// we need the base class (in user space the class may have been overridden,
// but we are not interested in these user space classes)
while (entry->parent) entry = entry->parent;
-
+
#if PHP_VERSION_ID >= 50400
// retrieve the comment (it has a pointer hidden in it to the ClassBase object)
const char *comment = entry->info.user.doc_comment;
-#else
- // retrieve the comment php5.3 style (it has a pointer hidden in it to the ClassBase object)
- const char *comment = 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
return *((ClassImpl **)(comment + 1));
+#else
+ // on php 5.3 we store the pointer to impl after the name in the entry
+ ClassImpl** impl = (ClassImpl**)(entry->name + 1 + entry->name_length);
+
+ // return the actual implementation
+ return *impl;
+#endif
}
/**
@@ -536,7 +534,7 @@ zend_object_value ClassImpl::cloneObject(zval *val TSRMLS_DC)
result.handlers = impl->objectHandlers();
// store the object
- ObjectImpl *new_object = new ObjectImpl(entry, cpp TSRMLS_CC);
+ ObjectImpl *new_object = new ObjectImpl(entry, cpp, 1 TSRMLS_CC);
// store the object in the object cache
result.handle = new_object->handle();
@@ -1197,7 +1195,7 @@ zend_object_value ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
result.handlers = impl->objectHandlers();
// create the object in the zend engine
- ObjectImpl *object = new ObjectImpl(entry, cpp TSRMLS_CC);
+ ObjectImpl *object = new ObjectImpl(entry, cpp, 1 TSRMLS_CC);
// store the object in the object cache
result.handle = object->handle();
@@ -1411,34 +1409,37 @@ void ClassImpl::initialize(ClassBase *base, const std::string &prefix TSRMLS_DC)
// otherwise report an error
else std::cerr << "Derived class " << name() << " is initialized before base class " << interface->name() << ": interface is ignored" << std::endl;
}
-
+
+ // this pointer has to be copied to temporary pointer, as &this causes compiler error
+ ClassImpl *impl = this;
+
+#if PHP_VERSION_ID >= 50400
+
// allocate doc comment to contain an empty string + a hidden pointer
- if (!_comment)
- {
- // allocate now
- _comment = (char *)malloc(1 + sizeof(ClassBase *));
-
- // empty string on first position
- _comment[0] = '\0';
-
- // this pointer has to be copied to temporary pointer, as &this causes compiler error
- ClassImpl *impl = this;
-
- // copy the 'this' pointer to the doc-comment
- memcpy(_comment+1, &impl, sizeof(ClassImpl *));
- }
-
- // store pointer to the class in the unused doc_comment member
-#if PHP_VERSION_ID >= 50400
+ char *_comment = (char *)malloc(1 + sizeof(ClassImpl *));
+
+ // empty string on first position
+ _comment[0] = '\0';
+
+ // copy the 'this' pointer to the doc-comment
+ memcpy(_comment+1, &impl, sizeof(ClassImpl *));
+
+ // set our comment in the actual class entry
_entry->info.user.doc_comment = _comment;
+
#else
- // and store the wrapper inside the comment
- _entry->doc_comment = _comment;
+
+ // Reallocate some extra space in the name in the zend_class_entry so we can fit a pointer behind it
+ _entry->name = (char *) realloc(_entry->name, _entry->name_length + 1 + sizeof(ClassImpl *));
+
+ // Copy the pointer after it
+ memcpy(_entry->name + _entry->name_length + 1, &impl, sizeof(ClassImpl *));
+
#endif
// set access types flags for class
_entry->ce_flags = (int)_type;
-
+
// declare all member variables
for (auto &member : _members) member->initialize(_entry TSRMLS_CC);
}
diff --git a/zend/classimpl.h b/zend/classimpl.h
index bd631b8..26cf030 100644
--- a/zend/classimpl.h
+++ b/zend/classimpl.h
@@ -32,12 +32,6 @@ private:
std::string _name;
/**
- * The comment for reflexion, with a stored pointer to ourselves
- * @var char*
- */
- char *_comment = nullptr;
-
- /**
* The class type (this can be values like Php::Abstract and Php::Final)
* @var ClassType
*/
diff --git a/zend/compileroptions.h b/zend/compileroptions.h
new file mode 100644
index 0000000..798cb59
--- /dev/null
+++ b/zend/compileroptions.h
@@ -0,0 +1,57 @@
+/**
+ * CompilerOptions.h
+ *
+ * Helper class to temporarily set compiler options
+ *
+ * When an object is destructed, it automatically restored the previous compiler settings
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Open PHP namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class CompilerOptions
+{
+private:
+ /**
+ * The original compiler options
+ * @var int
+ */
+ zend_uint _original;
+
+public:
+ /**
+ * Constructor
+ * @param options
+ */
+ CompilerOptions(zend_uint options)
+ {
+ // remember the old compiler options before we set temporary compile options
+ _original = CG(compiler_options);
+
+ // we're going to evaluate only once
+ CG(compiler_options) = options;
+ }
+
+ /**
+ * Destructor
+ */
+ virtual ~CompilerOptions()
+ {
+ // restore original options
+ CG(compiler_options) = _original;
+ }
+};
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/zend/eval.cpp b/zend/eval.cpp
new file mode 100644
index 0000000..674d39d
--- /dev/null
+++ b/zend/eval.cpp
@@ -0,0 +1,145 @@
+/**
+ * Eval.cpp
+ *
+ * This file holds the implementation for the Php::eval() function
+ *
+ * @author andot <https://github.com/andot>
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * Open PHP namespace
+ */
+namespace Php {
+
+/**
+ * Evaluate a PHP string
+ * @param phpCode The PHP code to evaluate
+ * @return Value The result of the evaluation
+ */
+Value eval(const std::string &phpCode)
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // the current exception
+ zval* oldException = EG(exception);
+
+ // the return va
+ zval *retval = nullptr;
+ MAKE_STD_ZVAL(retval);
+
+ // evaluate the string
+ if (zend_eval_stringl_ex((char *)phpCode.c_str(), (int32_t)phpCode.length(), retval, (char *)"", 1 TSRMLS_CC) != SUCCESS)
+ {
+ // Do we want to throw an exception here? The original author of this code
+ // did, but there are some reasons not to:
+ //
+ // 1. the PHP eval() function also does not throw exceptions.
+ //
+ // 2. the zend_eval_string() function already triggers a
+ // 'PHP parse error' when an error occurs, which also has
+ // to be handled. If we also throw an exception here, the
+ // user will have to write two error checks: for the error
+ // and the exception.
+ //
+ // if we _do_ want to throw an exception, we will first have to
+ // prevent the original zend_error to occur, and then turn it
+ // into an exception. An exception would be nicer from a C++
+ // point of view, but because of the extra complexity, we do not
+ // this for now.
+ return nullptr;
+ }
+ else
+ {
+ // was an exception thrown inside the eval()'ed code? In that case we
+ // throw a C++ new exception to give the C++ code the chance to catch it
+ if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC);
+
+ // wrap the return value in a value object
+ Value result(retval);
+
+ // the retval should now have two references: the value object and the
+ // retval itselves, so we can remove one of it (the zval_ptr_dtor only
+ // decrements refcount and won't destruct anything because there still
+ // is one reference left inside the Value object)
+ zval_ptr_dtor(&retval);
+
+ // done
+ return result;
+ }
+}
+
+/**
+ * Include a file
+ * @param filename
+ * @return Value
+ */
+Value include(const std::string &filename)
+{
+ // we can simply execute a file
+ return File(filename).execute();
+}
+
+/**
+ * Include a file only once
+ * @param filename
+ * @return Value
+ */
+Value include_once(const std::string &filename)
+{
+ // we can simply execute a file
+ return File(filename).execute();
+}
+
+/**
+ * Require a file
+ * This causes a fatal error if the file does not exist
+ * @param filename
+ * @return Value
+ */
+Value require(const std::string &filename)
+{
+ // create the file
+ File file(filename);
+
+ // execute if it exists
+ if (file.exists()) return file.execute();
+
+ // trigger fatal error
+ error << filename << " does not exist" << std::flush;
+
+ // unreachable
+ return nullptr;
+}
+
+/**
+ * Require a file only once
+ * This causes a fatal error if the file does not exist
+ * @param filename
+ * @return Value
+ */
+Value require_once(const std::string &filename)
+{
+ // create the file
+ File file(filename);
+
+ // execute if it exists
+ if (file.exists()) return file.once();
+
+ // trigger fatal error
+ error << filename << " does not exist" << std::flush;
+
+ // unreachable
+ return nullptr;
+}
+
+
+/**
+ * End of namespace
+ */
+}
diff --git a/zend/exists.cpp b/zend/exists.cpp
new file mode 100644
index 0000000..1177e47
--- /dev/null
+++ b/zend/exists.cpp
@@ -0,0 +1,77 @@
+/**
+ * Exists.cpp
+ *
+ * This file holds the implementation of all *_exists() functions,
+ * like class_exists(), et cetera
+ *
+ * @author andot <https://github.com/andot>
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * On php 5.3 ZEND_ACC_TRAIT isn't defined, so we simply define it to 0
+ * so all operations with it are basically no-ops. Currently unconfirmed
+ * if this actually works correctly on php 5.3, but it at least compiles.
+ */
+#ifndef ZEND_ACC_TRAIT
+#define ZEND_ACC_TRAIT 0
+#endif
+
+/**
+ * Open the PHP namespace
+ */
+namespace Php {
+
+/**
+ * Check whether a class with a certain name exists
+ * @param classname
+ * @param len
+ * @param autoload
+ * @return bool
+ */
+bool class_exists(const char *classname, size_t len, bool autoload)
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // we're going to load a class-entry
+ zend_class_entry **ce;
+
+ // should we autoload the class?
+ if (autoload)
+ {
+ // no auto-load
+ if (SUCCESS != zend_lookup_class(classname, len, &ce TSRMLS_CC)) return false;
+
+ // the found "class" could also be an interface or trait, which we do no want
+ return ((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0;
+ }
+ else
+ {
+ // starting slashes can be ignored
+ if (len > 0 && classname[0] == '\\') { classname++; len--; }
+
+ // all classes are in lowercase in the hash, so we make
+ // a temporary buffer for storing the lowercase class name
+ // (is this smart? memory allocation is expensive!)
+ std::unique_ptr<char[]> lc_name(new char[len + 1]);
+
+ // copy the name to lowercase, but ignore the starting slash (if there is one)
+ zend_str_tolower_copy(lc_name.get(), classname, len);
+
+ // see if there is a class with this name
+ if (SUCCESS != zend_hash_find(EG(class_table), lc_name.get(), len + 1, (void **) &ce)) return false;
+
+ // the found "class" could also be an interface or trait, which we do no want
+ return !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
+ }
+}
+
+/**
+ * End of namespace
+ */
+}
diff --git a/zend/file.cpp b/zend/file.cpp
new file mode 100644
index 0000000..43c2d7a
--- /dev/null
+++ b/zend/file.cpp
@@ -0,0 +1,160 @@
+/**
+ * File.cpp
+ *
+ * Implementation file for the File class
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * Namespace
+ */
+namespace Php {
+
+/**
+ * Constructor
+ *
+ * The constructor receives a filename as parameter. It uses the normal
+ * PHP include path resolve algorithms to find the location of the file.
+ *
+ * @param name the filename
+ * @param size length of the filename
+ */
+File::File(const char *name, size_t size)
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // resolve the path
+ _path = zend_resolve_path(name, size TSRMLS_CC);
+
+ // the resolve-path function sometimes returns the original pointer, we
+ // do not want that because we may have to store the pathname in this object
+ if (_path != name) return;
+
+ // make a full copy of the pathname
+ _path = estrndup(name, size);
+}
+
+/**
+ * Destructor
+ */
+File::~File()
+{
+ // clean up path name
+ if (_path) efree(_path);
+
+ // clean up opcodes
+ if (_opcodes) delete _opcodes;
+}
+
+/**
+ * Compile the file
+ * @return bool
+ */
+bool File::compile()
+{
+ // never works if the path is invalid
+ if (!_path) return false;
+
+ // is the file already compiled?
+ if (_opcodes) return _opcodes->valid();
+
+ // we are going to open the file
+ zend_file_handle fileHandle;
+
+ // open the file
+ if (zend_stream_open(_path, &fileHandle TSRMLS_CC) == FAILURE) return false;
+
+ // make sure the path name is stored in the handle
+ if (!fileHandle.opened_path) fileHandle.opened_path = estrdup(_path);
+
+ // we need temporary compiler options
+ CompilerOptions options(ZEND_COMPILE_DEFAULT);
+
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // create the opcodes
+ _opcodes = new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE TSRMLS_CC));
+
+ // close the file handle
+ zend_destroy_file_handle(&fileHandle TSRMLS_CC);
+
+ // done
+ return _opcodes->valid();
+}
+
+/**
+ * Does the file exist?
+ * @return boolean
+ */
+bool File::exists()
+{
+ // it is of course not valid if the path could not be resolved
+ if (!_path) return false;
+
+ // if we have valid opcodes, we're sure that it exists
+ if (_opcodes && _opcodes->valid()) return true;
+
+ // retrieve stats
+ struct stat buf;
+ return stat(_path, &buf) == 0;
+}
+
+/**
+ * Is this a valid file?
+ * @return boolean
+ */
+bool File::valid()
+{
+ // check if file is compilable
+ return compile();
+}
+
+/**
+ * Execute the file
+ * @return Value
+ */
+Value File::execute()
+{
+ // do we already have the opcodes?
+ if (_opcodes) return _opcodes->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, ::strlen(_path) + 1);
+
+ // execute the opcodes
+ return _opcodes->execute();
+}
+
+/**
+ * Execute a file only once
+ * @return Value
+ */
+Value File::once()
+{
+ // skip if the path is invalid
+ if (!_path) return nullptr;
+
+ // check if this file was already included
+ if (zend_hash_exists(&EG(included_files), _path, ::strlen(_path) + 1)) return nullptr;
+
+ // execute the file
+ return execute();
+}
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/zend/hashiterator.h b/zend/hashiterator.h
index b1f409f..36c3d6c 100644
--- a/zend/hashiterator.h
+++ b/zend/hashiterator.h
@@ -29,7 +29,7 @@ public:
* @param first Should it start on the first position?
* @param tsrm_ls
*/
- HashIterator(HashTable *hashtable, bool first) : _table(hashtable)
+ HashIterator(HashTable *hashtable, bool first, bool is_array = false) : _table(hashtable), _is_array(is_array)
{
// reset the hash pointer to the internal position
if (hashtable && first)
@@ -56,7 +56,7 @@ public:
* @param tsrm_ls
*/
HashIterator(const HashIterator &that TSRMLS_DC) :
- _table(that._table), _position(that._position)
+ _table(that._table), _position(that._position), _is_array(that._is_array)
{
// read current position
read();
@@ -166,7 +166,13 @@ private:
* @var HashPosition
*/
Bucket *_position = nullptr;
-
+
+ /**
+ * Is a hash interator in array
+ * @var bool
+ */
+ bool _is_array = false;
+
/**
* The current key and value
* @var std::pair
@@ -207,7 +213,7 @@ private:
// numeric keys are the easiest ones
if (type == HASH_KEY_IS_LONG) key = (int64_t)num_key;
- else key = string_key;
+ else key = std::string(string_key, str_len - 1);
#endif
@@ -223,7 +229,7 @@ private:
// if the key is private (it starts with a null character) we should return
// false to report that the object is not in a completely valid state
- return !_current.first.isString() || _current.first.rawValue()[0];
+ return _is_array || !_current.first.isString() || _current.first.rawValue()[0];
}
/**
diff --git a/zend/includes.h b/zend/includes.h
index 63b435e..d6f2062 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -20,6 +20,7 @@
#include <list>
#include <exception>
#include <type_traits>
+#include <functional>
// for debug
#include <iostream>
@@ -79,6 +80,9 @@
#include "../include/namespace.h"
#include "../include/extension.h"
#include "../include/call.h"
+#include "../include/opcodes.h"
+#include "../include/script.h"
+#include "../include/file.h"
/**
* Common header files for internal use only
@@ -112,6 +116,7 @@
#include "objectimpl.h"
#include "parametersimpl.h"
#include "extensionimpl.h"
+#include "compileroptions.h"
#ifndef ZVAL_COPY_VALUE
#define ZVAL_COPY_VALUE(z, v) \
diff --git a/zend/object.cpp b/zend/object.cpp
index dc548cc..22431fe 100644
--- a/zend/object.cpp
+++ b/zend/object.cpp
@@ -17,7 +17,7 @@ namespace Php {
* @param name Name of the class to instantiate
* @param base Implementation of the class
*/
-Object::Object(const char *name, Base *base)
+Object::Object(const char *name, Base *base) : Value()
{
// does the object already have a handle?
if (base->implementation())
@@ -29,7 +29,7 @@ Object::Object(const char *name, Base *base)
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
-
+
// 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
@@ -38,24 +38,48 @@ Object::Object(const char *name, Base *base)
// by the extension).
auto *entry = zend_fetch_class(name, ::strlen(name), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
if (!entry) throw FatalError(std::string("Unknown class name ") + name);
-
+
// construct an implementation (this will also set the implementation
- // member in the base object)
- new ObjectImpl(entry, base TSRMLS_CC);
-
+ // member in the base object), this is a self-destructing object that
+ // will be destructed when the last reference to it has been removed,
+ // we already set the reference to zero
+ new ObjectImpl(entry, base, 0 TSRMLS_CC);
+
// now we can store it
operator=(Value(base));
-
+
// install the object handlers
Z_OBJVAL_P(_val).handlers = ClassImpl::objectHandlers(entry);
}
}
/**
+ * Copy constructor is valid if the passed in object is also an object,
+ * or when it is a string holding a classname
+ * @param that An other object
+ */
+Object::Object(const Value &value) : Value()
+{
+ // when a string is passed in, we are going to make a new instance of the
+ // passed in string
+ if (value.isString())
+ {
+ // instantiate the object
+ if (instantiate(value)) call("__construct");
+ }
+ else
+ {
+ // this simply copies the other object
+ operator=(value);
+ }
+}
+
+/**
* Internal method to instantiate an object
- * @param name
+ * @param name Name of the class to instantiate
+ * @return bool True if there is a __construct function
*/
-void Object::instantiate(const char *name)
+bool Object::instantiate(const char *name)
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
@@ -79,6 +103,8 @@ void Object::instantiate(const char *name)
// @todo is this a memory leak? the base class first initializes a stdClass,
// and then we overwrite it with a specific class
+ // return whether there is a __construct function
+ return zend_hash_exists(&entry->function_table, "__construct", 12);
}
/**
diff --git a/zend/objectimpl.h b/zend/objectimpl.h
index 7f16320..4d20f3c 100644
--- a/zend/objectimpl.h
+++ b/zend/objectimpl.h
@@ -62,9 +62,10 @@ public:
*
* @param entry Zend class entry
* @param base C++ object that already exists
+ * @param refcount The initial refcount for the object
* @param tsrm_ls Optional threading data
*/
- ObjectImpl(zend_class_entry *entry, Base *base TSRMLS_DC)
+ ObjectImpl(zend_class_entry *entry, Base *base, int refcount TSRMLS_DC)
{
// allocate a mixed object (for some reason this does not have to be deallocated)
_mixed = (MixedObject *)emalloc(sizeof(MixedObject));
@@ -98,25 +99,28 @@ public:
// when in thread safety mode, the destruct method and free method have
// an extra parameter holding thread information
- using DestructType = void(zend_object*,unsigned int,void***);
- using FreeType = void(zend_object*,void***);
+ using DestructType = void(*)(zend_object*,unsigned int,void***);
+ using FreeType = void(*)(zend_object*,void***);
#else
// not in thread mode: no special parameter for the tsrm_ls variable
- using DestructType = void(zend_object*,unsigned int);
- using FreeType = void(zend_object*);
+ using DestructType = void(*)(zend_object*, unsigned int);
+ using FreeType = void(*)(zend_object*);
#endif
// store the two destruct methods in temporary vars
- DestructType *destructMethod = &ClassImpl::destructObject;
- FreeType *freeMethod = &ClassImpl::freeObject;
+ DestructType destructMethod = &ClassImpl::destructObject;
+ FreeType freeMethod = &ClassImpl::freeObject;
// the destructor and clone handlers are set to NULL. I dont know why, but they do not
// seem to be necessary...
_handle = zend_objects_store_put(php(), (zend_objects_store_dtor_t)destructMethod, (zend_objects_free_object_storage_t)freeMethod, NULL TSRMLS_CC);
+ // set the initial refcount (if it is different than one, because one is the default)
+ if (refcount != 1) EG(objects_store).object_buckets[_handle].bucket.obj.refcount = refcount;
+
// the object may remember that we are its implementation object
base->_impl = this;
}
diff --git a/zend/opcodes.cpp b/zend/opcodes.cpp
new file mode 100644
index 0000000..9e4525a
--- /dev/null
+++ b/zend/opcodes.cpp
@@ -0,0 +1,131 @@
+/**
+ * Opcodes.cpp
+ *
+ * Implementation file for the opcodes class
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Helper class to store and restore the current opcode state
+ *
+ * When we're going to execute a set of instructions, we need to store the
+ * current state of the Zend engine. After the instructions have been processed,
+ * we can switch back to the original instructions
+ */
+class ExecuteState
+{
+private:
+ /**
+ * All the original settings
+ */
+ zend_op_array *_active_op_array;
+ zval **_return_value_ptr_ptr;
+ zend_op **_opline_ptr;
+ int _interactive;
+
+public:
+ /**
+ * Constructor
+ */
+ ExecuteState()
+ {
+ // store all the original stuff
+ _active_op_array = EG(active_op_array);
+ _return_value_ptr_ptr = EG(return_value_ptr_ptr);
+ _opline_ptr = EG(opline_ptr);
+ _interactive = CG(interactive);
+ }
+
+ /**
+ * Destructor
+ */
+ virtual ~ExecuteState()
+ {
+ // restore all settings
+ CG(interactive) = _interactive;
+ EG(no_extensions) = 0;
+ EG(opline_ptr) = _opline_ptr;
+ EG(active_op_array) = _active_op_array;
+ EG(return_value_ptr_ptr) = _return_value_ptr_ptr;
+ }
+};
+
+/**
+ * Destructor
+ */
+Opcodes::~Opcodes()
+{
+ // leap out if opcodes were not valid
+ if (!_opcodes) return;
+
+ // clean up opcodes
+ destroy_op_array(_opcodes TSRMLS_CC);
+ efree(_opcodes);
+}
+
+/**
+ * Execute the opcodes
+ * @return Value
+ */
+Value Opcodes::execute() const
+{
+ // if the script could not be compiled, we return null
+ if (!_opcodes) return nullptr;
+
+ // pointer that is going to hold the return value of the script
+ zval *retval_ptr = nullptr;
+
+ // the zend engine is probably already busy processing opcodes, so we store
+ // the current execute state before we're going to switch the runtime to
+ // our own set of opcodes
+ ExecuteState oldstate;
+
+ // old execute state has been saved (and will automatically be restured when
+ // the oldstate is destructed), so we can now safely overwrite all the settings
+ EG(return_value_ptr_ptr) = &retval_ptr;
+ EG(active_op_array) = _opcodes;
+ EG(no_extensions) = 1;
+ if (!EG(active_symbol_table)) zend_rebuild_symbol_table(TSRMLS_C);
+ CG(interactive) = 0;
+
+ // the current exception
+ zval* oldException = EG(exception);
+
+ // execute the code
+ zend_execute(_opcodes TSRMLS_CC);
+
+ // was an exception thrown inside the eval()'ed code? In that case we
+ // throw a C++ new exception to give the C++ code the chance to catch it
+ if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC);
+
+ // we're ready if there is no return value
+ if (!retval_ptr) return nullptr;
+
+ // wrap the return value
+ Value result(retval_ptr);
+
+ // destruct the zval (this function will decrement the reference counter,
+ // and only destruct if there are no other references left)
+ zval_ptr_dtor(&retval_ptr);
+
+ // copy the pointer into a value object, and return that
+ return result;
+}
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/zend/parametersimpl.h b/zend/parametersimpl.h
index fd14238..2841c75 100644
--- a/zend/parametersimpl.h
+++ b/zend/parametersimpl.h
@@ -36,7 +36,7 @@ public:
zval **arg = (zval **) (zend_vm_stack_top(TSRMLS_C) - 1 - (argc-i));
// append value
- push_back(Value(*arg));
+ emplace_back(*arg);
}
}
diff --git a/zend/script.cpp b/zend/script.cpp
new file mode 100644
index 0000000..58ee376
--- /dev/null
+++ b/zend/script.cpp
@@ -0,0 +1,60 @@
+/**
+ * Script.cpp
+ *
+ * Implementation file for the script class
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * Open PHP namespace
+ */
+namespace Php {
+
+/**
+ * Helper function to compile the source code
+ * @param name name of the script
+ * @param script actual PHP code
+ * @param size length of the string
+ * @return opcodes
+ */
+zend_op_array *Script::compile(const char *name, const char *phpcode, size_t size)
+{
+ // Sadly, there is not a simple Zend function to compile a string into opcodes,
+ // so we basically copy the code that we found in zend_execute_API.c inside
+ // the zend_eval_stringl() function into this file here. However, the code
+ // found there is full of zval manipulation, for which we can use the much
+ // simpler Php::Value object
+ Php::Value source(phpcode, size);
+
+ // remember the old compiler options, and set new compiler options
+ CompilerOptions options(ZEND_COMPILE_DEFAULT_FOR_EVAL);
+
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // compile the string
+ return zend_compile_string(source._val, (char *)name TSRMLS_CC);
+}
+
+/**
+ * Constructor
+ * @param name name of the script
+ * @param script actual PHP code
+ * @param size length of the string
+ */
+Script::Script(const char *name, const char *phpcode, size_t size) : _opcodes(compile(name, phpcode, size))
+{
+}
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/zend/super.cpp b/zend/super.cpp
index c25efeb..a2fa0a9 100644
--- a/zend/super.cpp
+++ b/zend/super.cpp
@@ -40,7 +40,7 @@ Value Super::operator[](const std::string &key)
Value value(PG(http_globals)[_index]);
// pass on the call
- return value[key];
+ return value.get(key);
}
/**
@@ -61,7 +61,7 @@ Value Super::operator[](const char *key)
Value value(PG(http_globals)[_index]);
// pass on the call
- return value[key];
+ return value.get(key);
}
/**
diff --git a/zend/value.cpp b/zend/value.cpp
index 6890ffd..4a59d97 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -124,10 +124,10 @@ Value::Value(const std::string &value)
*/
Value::Value(const char *value, int size)
{
- // allocate the zval
+ // allocate the zval
MAKE_STD_ZVAL(_val);
-
- // do we have a valid value?
+
+ // is there a value?
if (value)
{
// create a string zval
@@ -157,10 +157,8 @@ Value::Value(double value)
* @param ref Force this to be a reference
*/
Value::Value(struct _zval_struct *val, bool ref)
+: _val(val)
{
- // just copy the zval into this object
- _val = val;
-
// if the variable is not already a reference, and it has more than one
// variable pointing to it, we should seperate it so that any changes
// we're going to make will not change the other variable
@@ -169,13 +167,13 @@ Value::Value(struct _zval_struct *val, bool ref)
// separate the zval
SEPARATE_ZVAL_IF_NOT_REF(&_val);
}
-
+
// we see ourselves as reference too
Z_ADDREF_P(_val);
-
+
// we're ready if we do not have to force it as a reference
if (!ref || Z_ISREF_P(_val)) return;
-
+
// make this a reference
Z_SET_ISREF_P(_val);
}
@@ -188,7 +186,9 @@ Value::Value(const Base *object)
{
// there are two options: the object was constructed from user space,
// and is already linked to a handle, or it was constructed from C++
- // space, and no handle does yet exist, find the implementation object
+ // space, and no handle does yet exist. But if it was constructed from
+ // C++ space and not yet wrapped, this Value constructor should not be
+ // called directly, but first via the derived Php::Object class.
auto *impl = object->implementation();
// do we have a handle?
@@ -205,14 +205,14 @@ Value::Value(const Base *object)
// we have to lookup the object in the object-table
zend_object_store_bucket *obj_bucket = &EG(objects_store).object_buckets[impl->handle()];
+ // there is one more reference to the object
+ obj_bucket->bucket.obj.refcount += 1;
+
// this is copy-pasted from zend_objects.c - and it is necessary too!
if (!obj_bucket->bucket.obj.handlers) obj_bucket->bucket.obj.handlers = &std_object_handlers;
// store the handlers in the zval too (cast is necessary for php 5.3)
Z_OBJ_HT_P(_val) = (zend_object_handlers*)obj_bucket->bucket.obj.handlers;
-
- // run the copy constructor
- zval_copy_ctor(_val);
}
/**
@@ -286,7 +286,7 @@ Value::Value(const Value &that)
* Move constructor
* @param value
*/
-Value::Value(Value &&that) : _val(that._val)
+Value::Value(Value &&that) noexcept: _val(that._val)
{
// clear the other object
that._val = nullptr;
@@ -387,7 +387,7 @@ void Value::attach(struct _hashtable *hashtable)
* Retrieve the refcount
* @return int
*/
-int Value::refcount()
+int Value::refcount() const
{
return Z_REFCOUNT_P(_val);
}
@@ -397,7 +397,7 @@ int Value::refcount()
* @param value
* @return Value
*/
-Value &Value::operator=(Value &&value)
+Value &Value::operator=(Value &&value) noexcept
{
// skip self assignment
if (this == &value) return *this;
@@ -854,191 +854,6 @@ Value Value::operator()() const
}
/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @return Value
- */
-Value Value::operator()(Value p0) const
-{
- // array of parameters
- zval **params[1] = { &p0._val };
-
- // call the function
- return exec(1, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1) const
-{
- // array of parameters
- zval **params[2] = { &p0._val, &p1._val };
-
- // call the function
- return exec(2, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2) const
-{
- // array of parameters
- zval **params[3] = { &p0._val, &p1._val, &p2._val };
-
- // call the function
- return exec(3, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2, Value p3) const
-{
- // array of parameters
- zval **params[4] = { &p0._val, &p1._val, &p2._val, &p3._val };
-
- // call the function
- return exec(4, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4) const
-{
- // array of parameters
- zval **params[5] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val };
-
- // call the function
- return exec(5, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5) const
-{
- // array of parameters
- zval **params[6] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val };
-
- // call the function
- return exec(6, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6) const
-{
- // array of parameters
- zval **params[7] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val };
-
- // call the function
- return exec(7, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @param p7 The eighth parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7) const
-{
- // array of parameters
- zval **params[8] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val };
-
- // call the function
- return exec(8, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @param p7 The eighth parameter
- * @param p8 The ninth parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8) const
-{
- // array of parameters
- zval **params[9] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val };
-
- // call the function
- return exec(9, params);
-}
-
-/**
- * Call the function - if the variable holds a callable thing
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @param p7 The eighth parameter
- * @param p8 The ninth parameter
- * @param p9 The tenth parameter
- * @return Value
- */
-Value Value::operator()(Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8, Value p9) const
-{
- // array of parameters
- zval **params[10] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val, &p9._val };
-
- // call the function
- return exec(10, params);
-}
-
-/**
* Call the method - if the variable holds an object with the given method
* @param name name of the method to call
* @return Value
@@ -1050,201 +865,6 @@ Value Value::call(const char *name)
}
/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0)
-{
- // array of parameters
- zval **params[] = { &p0._val };
-
- // call with zero parameters
- return exec(name, 1, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val };
-
- // call with zero parameters
- return exec(name, 2, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val };
-
- // call with zero parameters
- return exec(name, 3, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val };
-
- // call with zero parameters
- return exec(name, 4, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val };
-
- // call with zero parameters
- return exec(name, 5, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val };
-
- // call with zero parameters
- return exec(name, 6, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val };
-
- // call with zero parameters
- return exec(name, 7, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @param p7 The eighth parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val };
-
- // call with zero parameters
- return exec(name, 8, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @param p7 The eighth parameter
- * @param p8 The ninth parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val };
-
- // call with zero parameters
- return exec(name, 9, params);
-}
-
-/**
- * Call the method - if the variable holds an object with the given method
- * @param name name of the method to call
- * @param p0 The first parameter
- * @param p1 The second parameter
- * @param p2 The third parameter
- * @param p3 The fourth parameter
- * @param p4 The fifth parameter
- * @param p5 The sixth parameter
- * @param p6 The seventh parameter
- * @param p7 The eighth parameter
- * @param p8 The ninth parameter
- * @param p9 The tenth parameter
- * @return Value
- */
-Value Value::call(const char *name, Value p0, Value p1, Value p2, Value p3, Value p4, Value p5, Value p6, Value p7, Value p8, Value p9)
-{
- // array of parameters
- zval **params[] = { &p0._val, &p1._val, &p2._val, &p3._val, &p4._val, &p5._val, &p6._val, &p7._val, &p8._val, &p9._val };
-
- // call with zero parameters
- return exec(name, 10, params);
-}
-
-/**
* Helper function that runs the actual call
* @param object The object to call it on
* @param method The function or method to call
@@ -1409,6 +1029,116 @@ bool Value::isCallable() const
}
/**
+ * Retrieve the class entry
+ * @param allowString
+ * @return zend_class_entry
+ */
+zend_class_entry *Value::classEntry(bool allowString) const
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // is this an object
+ if (isObject())
+ {
+ // should have a class entry
+ if (!HAS_CLASS_ENTRY(*_val)) return nullptr;
+
+ // class entry can be easily found
+ return Z_OBJCE_P(_val);
+ }
+ else
+ {
+ // the value is not an object, is this allowed?
+ if (!allowString || !isString()) return nullptr;
+
+ // temporary variable
+ zend_class_entry **ce;
+
+ // find the class entry
+ if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &ce TSRMLS_CC) == FAILURE) return nullptr;
+
+ // found the entry
+ return *ce;
+ }
+}
+
+/**
+ * Check whether this object is an instance of a certain class
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
+ * @return bool
+ */
+bool Value::instanceOf(const char *classname, size_t size, bool allowString) const
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // the class-entry of 'this'
+ zend_class_entry *this_ce = classEntry(allowString);
+ if (!this_ce) return false;
+
+ // class entry of the parameter
+ zend_class_entry **ce;
+
+ // now we can look up the actual class
+ // the signature of zend_lookup_class_ex is slightly different since 5.4
+ // TODO The signature of this changed once again as of 5.6!
+#if PHP_VERSION_ID >= 50400
+ if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
+#else
+ if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false;
+#endif
+
+ // check if this is a subclass
+ return instanceof_function(this_ce, *ce TSRMLS_CC);
+}
+
+/**
+ * Check whether this object is derived from a certain class
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
+ * @return bool
+ */
+bool Value::derivedFrom(const char *classname, size_t size, bool allowString) const
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // the class-entry of 'this'
+ zend_class_entry *this_ce = classEntry(allowString);
+ if (!this_ce) return false;
+
+ // class entry of the parameter
+ zend_class_entry **ce;
+
+ // now we can look up the actual class
+ // the signature of zend_lookup_class_ex is slightly different since 5.4
+ // TODO The signature of this changed once again as of 5.6!
+#if PHP_VERSION_ID >= 50400
+ if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
+#else
+ if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false;
+#endif
+
+ // should not be identical, it must be a real derived object
+ if (this_ce == *ce) return false;
+
+ // check if this is a subclass
+ return instanceof_function(this_ce, *ce TSRMLS_CC);
+}
+
+/**
* Make a clone of the type
* @return Value
*/
@@ -1416,18 +1146,24 @@ Value Value::clone() const
{
// the zval that will hold the copy
zval *copy;
-
+
// allocate memory
ALLOC_ZVAL(copy);
-
+
// copy the data
INIT_PZVAL_COPY(copy, _val);
-
+
// run the copy constructor to ensure that everything gets copied
zval_copy_ctor(copy);
-
+
+ // wrap it using the Value(zval*) constructor, this will +1 the refcount!!!!
+ Value output(copy);
+
+ // -1 the refcount to avoid future leaks
+ Z_DELREF_P(copy);
+
// done
- return Value(copy);
+ return output;
}
/**
@@ -1610,10 +1346,10 @@ std::map<std::string,Php::Value> Value::mapValue() const
{
// result variable
std::map<std::string,Php::Value> result;
-
+
// iterate over the object
- for (auto &iter : *this) result[iter.first.rawValue()] = iter.second;
-
+ for (auto &iter : *this) result[iter.first.stringValue()] = iter.second;
+
// done
return result;
}
@@ -1626,9 +1362,9 @@ std::map<std::string,Php::Value> Value::mapValue() const
ValueIterator Value::createIterator(bool begin) const
{
// check type
- if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), begin));
+ if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), begin, true));
- // get access to the hast table
+ // get access to the hash table
if (isObject())
{
// we need the TSRMLS_CC variable
@@ -1647,7 +1383,7 @@ ValueIterator Value::createIterator(bool begin) const
else
{
// construct a regular iterator
- return ValueIterator(new HashIterator(Z_OBJ_HT_P(_val)->get_properties(_val TSRMLS_CC), begin));
+ return ValueIterator(new HashIterator(Z_OBJPROP_P(_val), begin));
}
}
@@ -1676,6 +1412,20 @@ ValueIterator Value::end() const
}
/**
+ * Iterate over key value pairs
+ * @param callback
+ */
+void Value::iterate(const std::function<void(const Php::Value &,const Php::Value &)> &callback) const
+{
+ // iterate over the object
+ for (const auto &iter : *this)
+ {
+ // call the callback
+ callback(iter.first, iter.second);
+ }
+}
+
+/**
* Does the array contain a certain index?
* @param index
* @return bool
@@ -1761,7 +1511,7 @@ Value Value::get(int index) const
*/
Value Value::get(const char *key, int size) const
{
- // must be an array
+ // must be an array or object
if (!isArray() && !isObject()) return Value();
// calculate size
@@ -1781,6 +1531,9 @@ Value Value::get(const char *key, int size) const
}
else
{
+ // key should not start with a null byte
+ if (size > 0 && key[0] == 0) return Value();
+
// we need the tsrm_ls variable
TSRMLS_FETCH();
@@ -1847,6 +1600,9 @@ void Value::set(int index, const Value &value)
*/
void Value::setRaw(const char *key, int size, const Value &value)
{
+ // does not work for empty keys
+ if (!key || (size > 0 && key[0] == 0)) return;
+
// is this an object?
if (isObject())
{
@@ -1858,7 +1614,7 @@ void Value::setRaw(const char *key, int size, const Value &value)
// retrieve the class entry
auto *entry = zend_get_class_entry(_val TSRMLS_CC);
-
+
// update the property (cast necessary for php 5.3)
zend_update_property(entry, _val, (char *)key, size, value._val TSRMLS_CC);
}