diff options
author | valmat <ufabiz@gmail.com> | 2014-04-09 11:00:05 +0600 |
---|---|---|
committer | valmat <ufabiz@gmail.com> | 2014-04-09 11:00:05 +0600 |
commit | 6c7c846edd5b74450b76532da33c25e6cc6a10a4 (patch) | |
tree | 51b0e0be5c43ddba6ca9351026fc94bf8ae7bc07 | |
parent | 08ed8866a5bba0b23a8d5587116a968512df2568 (diff) | |
parent | 33760c3efba4207eac826ff080b5f9b9672fc60e (diff) |
Merge branch 'master' into ini-master
Conflicts:
include/namespace.h
zend/extensionimpl.cpp
-rw-r--r-- | Makefile | 71 | ||||
-rw-r--r-- | common/extensionbase.h | 138 | ||||
-rw-r--r-- | common/includes.h | 23 | ||||
-rw-r--r-- | common/modifiers.cpp (renamed from src/modifiers.cpp) | 0 | ||||
-rw-r--r-- | common/streambuf.cpp (renamed from src/streambuf.cpp) | 33 | ||||
-rw-r--r-- | common/streambuf.h (renamed from src/streambuf.h) | 0 | ||||
-rw-r--r-- | config/config.cpp | 28 | ||||
-rw-r--r-- | documentation/ten-reasons-for-using-php-cpp.html | 4 | ||||
-rw-r--r-- | hhvm/extension.cpp | 103 | ||||
-rw-r--r-- | hhvm/extensionimpl.h | 55 | ||||
-rw-r--r-- | hhvm/includes.h | 55 | ||||
-rw-r--r-- | hhvm/streambuf.cpp | 59 | ||||
-rw-r--r-- | hhvm/streams.cpp | 41 | ||||
-rw-r--r-- | include/argument.h | 111 | ||||
-rw-r--r-- | include/array.h | 4 | ||||
-rw-r--r-- | include/base.h | 42 | ||||
-rw-r--r-- | include/class.h | 5 | ||||
-rw-r--r-- | include/exception.h | 37 | ||||
-rw-r--r-- | include/extension.h | 129 | ||||
-rw-r--r-- | include/iterator.h | 102 | ||||
-rw-r--r-- | include/namespace.h | 178 | ||||
-rw-r--r-- | include/object.h | 2 | ||||
-rw-r--r-- | include/parameters.h | 29 | ||||
-rw-r--r-- | include/value.h | 3 | ||||
-rw-r--r-- | include/valueiterator.h | 12 | ||||
-rw-r--r-- | phpcpp.h | 26 | ||||
-rw-r--r-- | src/argument.cpp | 171 | ||||
-rw-r--r-- | src/exception.cpp | 34 | ||||
-rw-r--r-- | src/mixedobject.h | 39 | ||||
-rw-r--r-- | src/origexception.cpp | 35 | ||||
-rw-r--r-- | src/origexception.h | 79 | ||||
-rw-r--r-- | src/parameters.cpp | 49 | ||||
-rw-r--r-- | tests/php/phpt/variables/021-HashMember-3.phpt | 1 | ||||
-rw-r--r-- | zend/arithmetic.h (renamed from src/arithmetic.h) | 0 | ||||
-rw-r--r-- | zend/base.cpp (renamed from src/base.cpp) | 110 | ||||
-rw-r--r-- | zend/boolmember.h (renamed from src/boolmember.h) | 0 | ||||
-rw-r--r-- | zend/callable.cpp (renamed from src/callable.cpp) | 21 | ||||
-rw-r--r-- | zend/callable.h (renamed from src/callable.h) | 75 | ||||
-rw-r--r-- | zend/classbase.cpp (renamed from src/classbase.cpp) | 0 | ||||
-rw-r--r-- | zend/classimpl.cpp (renamed from src/classimpl.cpp) | 194 | ||||
-rw-r--r-- | zend/classimpl.h (renamed from src/classimpl.h) | 7 | ||||
-rw-r--r-- | zend/extension.cpp | 103 | ||||
-rw-r--r-- | zend/extensionimpl.cpp (renamed from src/extension.cpp) | 142 | ||||
-rw-r--r-- | zend/extensionimpl.h | 120 | ||||
-rw-r--r-- | zend/floatmember.h (renamed from src/floatmember.h) | 0 | ||||
-rw-r--r-- | zend/function.h (renamed from src/function.h) | 2 | ||||
-rw-r--r-- | zend/global.cpp (renamed from src/global.cpp) | 0 | ||||
-rw-r--r-- | zend/globals.cpp (renamed from src/globals.cpp) | 0 | ||||
-rw-r--r-- | zend/hashiterator.h (renamed from src/hashiterator.h) | 8 | ||||
-rw-r--r-- | zend/hashmember.cpp (renamed from src/hashmember.cpp) | 0 | ||||
-rw-r--r-- | zend/includes.h (renamed from src/includes.h) | 18 | ||||
-rw-r--r-- | zend/init.h (renamed from include/init.h) | 0 | ||||
-rw-r--r-- | zend/invaliditerator.h (renamed from src/invaliditerator.h) | 8 | ||||
-rw-r--r-- | zend/iteratorimpl.cpp (renamed from src/iterator.cpp) | 93 | ||||
-rw-r--r-- | zend/iteratorimpl.h | 190 | ||||
-rw-r--r-- | zend/member.h (renamed from src/member.h) | 0 | ||||
-rw-r--r-- | zend/members.cpp (renamed from src/members.cpp) | 0 | ||||
-rw-r--r-- | zend/method.h (renamed from src/method.h) | 0 | ||||
-rw-r--r-- | zend/namespace.cpp (renamed from src/namespace.cpp) | 79 | ||||
-rw-r--r-- | zend/notimplemented.h (renamed from src/notimplemented.h) | 0 | ||||
-rw-r--r-- | zend/nullmember.h (renamed from src/nullmember.h) | 0 | ||||
-rw-r--r-- | zend/numericmember.h (renamed from src/numericmember.h) | 0 | ||||
-rw-r--r-- | zend/object.cpp (renamed from src/object.cpp) | 7 | ||||
-rw-r--r-- | zend/objectimpl.h | 207 | ||||
-rw-r--r-- | zend/origexception.h | 145 | ||||
-rw-r--r-- | zend/parametersimpl.h | 53 | ||||
-rw-r--r-- | zend/property.h (renamed from src/property.h) | 0 | ||||
-rw-r--r-- | zend/streambuf.cpp | 55 | ||||
-rw-r--r-- | zend/streams.cpp (renamed from src/streams.cpp) | 0 | ||||
-rw-r--r-- | zend/stringmember.h (renamed from src/stringmember.h) | 0 | ||||
-rw-r--r-- | zend/super.cpp (renamed from src/super.cpp) | 0 | ||||
-rw-r--r-- | zend/traverseiterator.h (renamed from src/traverseiterator.h) | 8 | ||||
-rw-r--r-- | zend/value.cpp (renamed from src/value.cpp) | 18 | ||||
-rw-r--r-- | zend/valueiterator.cpp (renamed from src/valueiterator.cpp) | 0 | ||||
-rw-r--r-- | zend/valueiteratorimpl.h (renamed from src/iteratorimpl.h) | 18 |
75 files changed, 2046 insertions, 1333 deletions
@@ -55,7 +55,8 @@ INSTALL_LIB = ${INSTALL_PREFIX}/lib # you can change that here. # -LIBRARY = libphpcpp.so +PHP_LIBRARY = libphpcpp.so +HHVM_LIBRARY = libhhvmcpp.so # @@ -84,7 +85,9 @@ LINKER = g++ # you want to leave that flag out on production servers). # -COMPILER_FLAGS = -Wall -c `php-config --includes` -g -std=c++11 -fpic -o +COMPILER_FLAGS = -Wall -c -g -std=c++11 -fpic +PHP_COMPILER_FLAGS = ${COMPILER_FLAGS} `php-config --includes` +HHVM_COMPILER_FLAGS = ${COMPILER_FLAGS} # # Linker flags @@ -96,7 +99,9 @@ COMPILER_FLAGS = -Wall -c `php-config --includes` -g -std=c++11 -fpic -o # to the linker flags # -LINKER_FLAGS = -shared `php-config --ldflags` +LINKER_FLAGS = -shared +PHP_LINKER_FLAGS = ${LINKER_FLAGS} `php-config --ldflags` +HHVM_LINKER_FLAGS = ${LINKER_FLAGS} # @@ -115,33 +120,26 @@ MKDIR = mkdir -p # The source files # # For this we use a special Makefile function that automatically scans the -# src/ directory for all *.cpp files. No changes are probably necessary here +# common/, zend/ and hhvm/ directories for all *.cpp files. No changes are +# probably necessary here # -LIBRARY_SOURCES = $(wildcard src/*.cpp) - +COMMON_SOURCES = $(wildcard common/*.cpp) +PHP_SOURCES = $(wildcard zend/*.cpp) +HHVM_SOURCES = $(wildcard hhvm/*.cpp) # # The object files # # The intermediate object files are generated by the compiler right before -# the linker turns all these object files into the libphpcpp.so shared library. -# We also use a Makefile function here that takes all source files. +# the linker turns all these object files into the libphpcpp.so and +# libhhvmcpp.so shared libraries. We also use a Makefile function here that +# takes all source files. # -LIBRARY_OBJECTS = $(LIBRARY_SOURCES:%.cpp=%.o) - - -# -# Configuration program -# -# During installation, a configuration utility will be installed. It is -# compiled with the following instructions -# - -CONFIG_UTILITY = ./create_config -CONFIG_SOURCES = $(wildcard config/*.cpp) -CONFIG_FLAGS = `php-config --includes` -o +COMMON_OBJECTS = $(COMMON_SOURCES:%.cpp=%.o) +PHP_OBJECTS = $(PHP_SOURCES:%.cpp=%.o) +HHVM_OBJECTS = $(HHVM_SOURCES:%.cpp=%.o) # @@ -149,29 +147,38 @@ CONFIG_FLAGS = `php-config --includes` -o # dependencies that are used by the compiler. # -all: ${LIBRARY_OBJECTS} ${LIBRARY} ${CONFIG_UTILITY} +all: ${PHP_LIBRARY} + +phpcpp: ${PHP_LIBRARY} + +hhvmcpp: ${HHVM_LIBRARY} -${LIBRARY}: ${LIBRARY_OBJECTS} - ${LINKER} ${LINKER_FLAGS} -o $@ ${LIBRARY_OBJECTS} +${PHP_LIBRARY}: ${COMMON_OBJECTS} ${PHP_OBJECTS} + ${LINKER} ${PHP_LINKER_FLAGS} -o $@ ${COMMON_OBJECTS} ${PHP_OBJECTS} -${CONFIG_UTILITY}: - ${COMPILER} ${CONFIG_FLAGS} $@ ${CONFIG_SOURCES} +${HHVM_LIBRARY}: ${COMMON_OBJECTS} ${HHVM_OBJECTS} + ${LINKER} ${HHVM_LINKER_FLAGS} -o $@ ${COMMON_OBJECTS} ${HHVM_OBJECTS} clean: - ${RM} ${LIBRARY_OBJECTS} ${LIBRARY} ${CONFIG_UTILITY} + ${RM} ${COMMON_OBJECTS} ${PHP_OBJECTS} ${HHVM_OBJECTS} ${PHP_LIBRARY} ${HHVM_LIBRARY} + +${COMMON_OBJECTS}: + ${COMPILER} ${COMPILER_FLAGS} -o $@ ${@:%.o=%.cpp} + +${PHP_OBJECTS}: + ${COMPILER} ${PHP_COMPILER_FLAGS} -o $@ ${@:%.o=%.cpp} -${LIBRARY_OBJECTS}: - ${COMPILER} ${COMPILER_FLAGS} $@ ${@:%.o=%.cpp} +${HHVM_OBJECTS}: + ${COMPILER} ${HHVM_COMPILER_FLAGS} -o $@ ${@:%.o=%.cpp} install: ${MKDIR} ${INSTALL_HEADERS}/phpcpp ${CP} phpcpp.h ${INSTALL_HEADERS} ${CP} include/*.h ${INSTALL_HEADERS}/phpcpp - ${CP} ${LIBRARY} ${INSTALL_LIB} - ${CONFIG_UTILITY} > ${INSTALL_HEADERS}/phpcpp/config.h + if [ -e ${PHP_LIBRARY} ]; then ${CP} ${PHP_LIBRARY} ${INSTALL_LIB}; fi + if [ -e ${HHVM_LIBRARY} ]; then ${CP} ${HHVM_LIBRARY} ${INSTALL_LIB}; fi test: mkdir -p ./tests/include/zts/phpcpp - ${CONFIG_UTILITY} > ./tests/include/zts/phpcpp/config.h cd tests && ./test.sh -p "${PHP_BIN}" diff --git a/common/extensionbase.h b/common/extensionbase.h new file mode 100644 index 0000000..bf7f128 --- /dev/null +++ b/common/extensionbase.h @@ -0,0 +1,138 @@ +/** + * ExtensionBase.h + * + * Base class for ExtensionImpl objects. Common code used by both the Zend + * and HHVM engine. + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013, 2014 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class ExtensionBase +{ +protected: + /** + * Pointer to the extension object that is filled by the extension programmer + * @var Extension + */ + Extension *_data; + + /** + * Callback that is called after the engine is initialized and before the + * pageviews are going to be handled + * @var Callback + */ + Callback _onStartup; + + /** + * Callback that is called in front of each request + * @var Callback + */ + Callback _onRequest; + + /** + * Callback that is called right after each request + * @var Callback + */ + Callback _onIdle; + + /** + * Callback that is called right before the engine is closing down + * @var Callback + */ + Callback _onShutdown; + +public: + /** + * Constructor + * @param data Extension object created by the extension programmer + */ + ExtensionBase(Extension *data) : _data(data) {} + + /** + * No copy'ing and no moving + */ + ExtensionBase(const ExtensionImpl &extension) = delete; + ExtensionBase(ExtensionImpl &&extension) = delete; + + /** + * Destructor + */ + virtual ~ExtensionBase() {} + + /** + * Register a function to be called when the PHP engine is ready + * + * The callback will be called after all extensions are loaded, and all + * functions and classes are available, but before the first pageview/request + * is handled. You can register this callback if you want to be notified + * when the engine is ready, for example to initialize certain things. + * + * @param callback + */ + void onStartup(const Callback &callback) + { + // copy callback + _onStartup = callback; + } + + /** + * Register a function to be called when the PHP engine is going to stop + * + * The callback will be called right before the process is going to stop. + * You can register a function if you want to clean up certain things. + * + * @param callback + */ + void onShutdown(const Callback &callback) + { + // copy callback + _onShutdown = callback; + } + + /** + * Register a callback that is called at the beginning of each pageview/request + * + * You can register a callback if you want to initialize certain things + * at the beginning of each request. Remember that the extension can handle + * multiple requests after each other, and you may want to set back certain + * global variables to their initial variables in front of each request + * + * @param callback + */ + void onRequest(const Callback &callback) + { + // copy callback + _onRequest = callback; + } + + /** + * Register a callback that is called to cleanup things after a pageview/request + * + * The callback will be called after _each_ request, so that you can clean up + * certain things and make your extension ready to handle the next request. + * This method is called onIdle because the extension is idle in between + * requests. + * + * @param callback + */ + void onIdle(const Callback &callback) + { + // copy callback + _onIdle = callback; + } +}; + +/** + * End of namespace + */ +} + diff --git a/common/includes.h b/common/includes.h new file mode 100644 index 0000000..db29f10 --- /dev/null +++ b/common/includes.h @@ -0,0 +1,23 @@ +/** + * Includes.h + * + * All includes for compiling the common module files of PHP-CPP library + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ + +/** + * Standard C and C++ libraries + */ +#include <sstream> + +/** + * Public include files + */ +#include "../include/modifiers.h" + +/** + * Generic implementation header files + */ +#include "streambuf.h" diff --git a/src/modifiers.cpp b/common/modifiers.cpp index 3720730..3720730 100644 --- a/src/modifiers.cpp +++ b/common/modifiers.cpp diff --git a/src/streambuf.cpp b/common/streambuf.cpp index e258b4e..2d87f9a 100644 --- a/src/streambuf.cpp +++ b/common/streambuf.cpp @@ -54,39 +54,6 @@ int StreamBuf::overflow(int c) } /** - * Called when the internal buffer should be synchronized - * @return int - */ -int StreamBuf::sync() -{ - // current buffer size - size_t size = pptr() - pbase(); - - // is this the error stream or the regular output stream? - if (_error) - { - // write to error (the zend_error() method is a varargs function, - // which means that we have to include a printf() like format as first - // parameter. We can not specify pbase() directly, because (1) it is - // not null terminated and (2) it could contain % signs and allow all - // sorts of buffer overflows. - zend_error(_error, "%.*s", (int)size, pbase()); - - } - else - { - // write to zend - zend_write(pbase(), size); - } - - // reset the buffer - pbump(-size); - - // done - return 0; -} - -/** * End namespace */ } diff --git a/src/streambuf.h b/common/streambuf.h index ed1506a..ed1506a 100644 --- a/src/streambuf.h +++ b/common/streambuf.h diff --git a/config/config.cpp b/config/config.cpp deleted file mode 100644 index 2fcd5ad..0000000 --- a/config/config.cpp +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Config.cpp - * - * Simple programs that creates the config file for PHP-CPP. PHP-CPP needs - * a different config file when it is installed on a system with multi-threaded - * PHP, and on a system with single threaded PHP. - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2014 Copernica BV - */ -#include <iostream> -#include <php_config.h> - -/** - * Main procedure - * @return int - */ -int main() -{ -#ifdef ZTS - // also define ZTS in the config file - std::cout << "#define ZTS" << std::endl; -#endif - - // done - return 0; -} - diff --git a/documentation/ten-reasons-for-using-php-cpp.html b/documentation/ten-reasons-for-using-php-cpp.html index 7597fb2..d4a202b 100644 --- a/documentation/ten-reasons-for-using-php-cpp.html +++ b/documentation/ten-reasons-for-using-php-cpp.html @@ -1,6 +1,6 @@ <h1>Ten reasons for using PHP-CPP</h1> <p> - There are many reasons for using PHP-CPP. Let's name a view. + There are many reasons for using PHP-CPP. Let's name a few. </p> <h4>1. It's fast</h4> <p> @@ -41,7 +41,7 @@ <p> Many programmers find it a matter of honor to make code that can only be understood by themselves. We do not agree. The PHP-CPP library is - fully documented (the documentation can be found on <a href="documentation"> + fully documented (the documentation can be found on <a href="http://www.php-cpp.com/documentation"> www.php-cpp.com/documentation</a>), and the source code is full with comments and explanations. </p> diff --git a/hhvm/extension.cpp b/hhvm/extension.cpp new file mode 100644 index 0000000..9685b32 --- /dev/null +++ b/hhvm/extension.cpp @@ -0,0 +1,103 @@ +/** + * Extension.cpp + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013, 2014 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Constructor that defines a number of functions right away + * @param name Extension name + * @param version Extension version string + */ +Extension::Extension(const char *name, const char *version) : + Namespace(""), _impl(new ExtensionImpl(this, name, version)) {} + +/** + * Destructor + */ +Extension::~Extension() +{ + // get rid of the implementation object + delete _impl; +} + +/** + * Register a function to be called when the PHP engine is ready + * @param callback + * @return Extension + */ +Extension &Extension::onStartup(const Callback &callback) +{ + // pass on to the implementation + _impl->onStartup(callback); + + // allow chaining + return *this; +} + +/** + * Register a function to be called when the PHP engine is going to stop + * @param callback + * @return Extension + */ +Extension &Extension::onShutdown(const Callback &callback) +{ + // pass on to the implementation + _impl->onShutdown(callback); + + // allow chaining + return *this; +} + +/** + * Register a callback that is called at the beginning of each pageview/request + * @param callback + */ +Extension &Extension::onRequest(const Callback &callback) +{ + // pass on to the implementation + _impl->onRequest(callback); + + // allow chaining + return *this; +} + +/** + * Register a callback that is called to cleanup things after a pageview/request + * @param callback + */ +Extension &Extension::onIdle(const Callback &callback) +{ + // pass on to the implementation + _impl->onIdle(callback); + + // allow chaining + return *this; +} + +/** + * Retrieve the module pointer + * + * This is the memory address that should be exported by the get_module() + * function. + * + * @return void* + */ +void *Extension::module() +{ + // pass on to the implementation + return _impl->module(); +} + +/** + * End of namespace + */ +} + diff --git a/hhvm/extensionimpl.h b/hhvm/extensionimpl.h new file mode 100644 index 0000000..deb80a6 --- /dev/null +++ b/hhvm/extensionimpl.h @@ -0,0 +1,55 @@ +/** + * ExtensionImpl.h + * + * Implementation of the extension object for the HHVM engine + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ + +/** + * Namespace + */ +namespace Php { + +/** + * Class definition + */ +class ExtensionImpl : public ExtensionBase +{ +private: + /** + * Pointer to the extension object that is filled by the extension programmer + * @var Extension + */ + Extension *_data; + +public: + /** + * Constructor + * @param data Pointer to the extension object created by the extension programmer + * @param name Name of the extension + * @param version Version identifier of the extension + */ + ExtensionImpl(Extension *data, const char *name, const char *version) : ExtensionBase(data) {} + + /** + * Destructor + */ + virtual ~ExtensionImpl() {} + + /** + * Pointer to the module that is loaded by HHVM + * @return void* + */ + void *module() + { + return nullptr; + } +}; + +/** + * End of namespace + */ +} + diff --git a/hhvm/includes.h b/hhvm/includes.h new file mode 100644 index 0000000..0e587db --- /dev/null +++ b/hhvm/includes.h @@ -0,0 +1,55 @@ +/** + * Includes.h + * + * All includes for compiling the HHVM implementation of PHP-CPP + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ + +/** + * Standard C and C++ libraries + */ +#include <functional> +#include <list> +#include <memory> +#include <vector> +#include <map> +#include <string.h> +#include <iostream> + +/** + * HHVM includes + */ + + +/** + * Public include files + */ +#include "../include/type.h" +#include "../include/hashparent.h" +#include "../include/value.h" +#include "../include/parameters.h" +#include "../include/classtype.h" +#include "../include/argument.h" +#include "../include/modifiers.h" +#include "../include/classbase.h" +#include "../include/interface.h" +#include "../include/iterator.h" +#include "../include/traversable.h" +#include "../include/serializable.h" +#include "../include/class.h" +#include "../include/namespace.h" +#include "../include/extension.h" + +/** + * Generic implementation header files + */ +#include "../common/extensionbase.h" +#include "../common/streambuf.h" + +/** + * Specific HHVM header files for the implementation only + */ +#include "extensionimpl.h" + diff --git a/hhvm/streambuf.cpp b/hhvm/streambuf.cpp new file mode 100644 index 0000000..805fc0f --- /dev/null +++ b/hhvm/streambuf.cpp @@ -0,0 +1,59 @@ +/** + * StreamBuf.cpp + * + * Implementation file for the StreamBuf class + * + * @see http://www.mr-edd.co.uk/blog/beginners_guide_streambuf + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Called when the internal buffer should be synchronized + * @return int + */ +int StreamBuf::sync() +{ + // current buffer size + size_t size = pptr() - pbase(); + + // is this the error stream or the regular output stream? + if (_error) + { + // write to error (the zend_error() method is a varargs function, + // which means that we have to include a printf() like format as first + // parameter. We can not specify pbase() directly, because (1) it is + // not null terminated and (2) it could contain % signs and allow all + // sorts of buffer overflows. + + // @todo hhvm implementation + +// zend_error(_error, "%.*s", (int)size, pbase()); + + } + else + { + // @todo hhvm implementation + + // write to zend +// zend_write(pbase(), size); + } + + // reset the buffer + pbump(-size); + + // done + return 0; +} + +/** + * End namespace + */ +} diff --git a/hhvm/streams.cpp b/hhvm/streams.cpp new file mode 100644 index 0000000..de327e2 --- /dev/null +++ b/hhvm/streams.cpp @@ -0,0 +1,41 @@ +/** + * Streams.cpp + * + * Implementation of the streams + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Some static buffers for writing data + * @var StreamBuf + */ +// @todo find the right constants +static StreamBuf bufOut (0); +static StreamBuf bufError (0); //E_ERROR); +static StreamBuf bufWarning (0); //E_WARNING); +static StreamBuf bufNotice (0); //E_NOTICE); +static StreamBuf bufDeprecated (0); //E_DEPRECATED); + +/** + * Create the actual steams + * @var std::ostream + */ +std::ostream out (&bufOut); +std::ostream error (&bufError); +std::ostream warning (&bufWarning); +std::ostream notice (&bufNotice); +std::ostream deprecated (&bufDeprecated); + +/** + * End namespace + */ +} + diff --git a/include/argument.h b/include/argument.h index 68ac7df..81b1bf4 100644 --- a/include/argument.h +++ b/include/argument.h @@ -8,15 +8,10 @@ * classes instead. * * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2013 Copernica BV + * @copyright 2013, 2014 Copernica BV */ /** - * Forward declaration - */ -struct _zend_arg_info; - -/** * Set up namespace */ namespace Php { @@ -28,21 +23,9 @@ class Argument { public: /** - * Copy constructor - * @param argument - */ - Argument(const Argument &argument); - - /** - * Move constructor - * @param argument - */ - Argument(Argument &&argument); - - /** * Destructor */ - virtual ~Argument(); + virtual ~Argument() {} protected: /** @@ -52,7 +35,8 @@ protected: * @param required Is this argument required? * @param byref Is this a reference argument */ - Argument(const char *name, Type type, bool required = true, bool byref = false); + Argument(const char *name, Type type, bool required = true, bool byref = false) : + _name(name), _type(type), _required(required), _byReference(byref) {} /** * Constructor @@ -62,17 +46,11 @@ protected: * @param required Is this argument required? * @param byref Is this a reference argument? */ - Argument(const char *name, const char *classname, bool nullable = true, bool required = true, bool byref = false); + 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) {} public: /** - * Fill an arg_info structure with data - * @param info - * @internal - */ - void fill(struct _zend_arg_info *info) const; - - /** * Is this a required argument? * @return bool * @internal @@ -81,19 +59,88 @@ public: { return _required; } - + + /** + * Name of the argument + * @return std::string + */ + const std::string &name() const + { + return _name; + } + + /** + * Type-hint for the argument + * @return Type + */ + Type type() const + { + return _type; + } + + /** + * If the type is a class, the name of the class + * @return std::string + */ + const std::string &classname() const + { + return _classname; + } + + /** + * Is it allowed to pass parameter with a null value? + * @return bool + */ + bool allowNull() const + { + return _nullable; + } + + /** + * Is this a parameter-by-reference? + * @return bool + */ + bool byReference() const + { + return _byReference; + } + private: /** - * The argument info - * @var zend_arg_info + * Name of the argument + * @var std::string */ - struct _zend_arg_info *_info; + std::string _name; /** + * Type of argument + * @var Type + */ + Type _type; + + /** + * Classname, if this is a parameter that is supposed to be an instance of a class + * @var std::string + */ + std::string _classname; + + /** + * May the parameter be null? + * @var bool + */ + bool _nullable; + + /** * Is this a required argument * @var bool */ bool _required; + + /** + * Is this a 'by-reference' parameter? + * @var bool + */ + bool _byReference; }; /** diff --git a/include/array.h b/include/array.h index 26fec24..0b6ceb9 100644 --- a/include/array.h +++ b/include/array.h @@ -31,7 +31,7 @@ public: Array(const Value &value) : Value(value) { // type must be valid - if (value.type() != Type::Array) throw Php::Exception("Assiging a non-array to an array variable"); + if (value.type() != Type::Array) throw Php::Exception("Assigning a non-array to an array variable"); } /** @@ -93,7 +93,7 @@ public: if (this == &value) return *this; // type must be valid - if (value.type() != Type::Array) throw Php::Exception("Assiging a non-array to a fixed array variable"); + if (value.type() != Type::Array) throw Php::Exception("Assigning a non-array to a fixed array variable"); // call base Value::operator=(value); diff --git a/include/base.h b/include/base.h index 8b3e561..87c7483 100644 --- a/include/base.h +++ b/include/base.h @@ -13,13 +13,21 @@ namespace Php { /** * Forward declarations */ -struct MixedObject; +class ObjectImpl; + /** * Class definition */ class Base { +private: + /** + * Object handle in the PHP engine + * @var ObjectImpl + */ + ObjectImpl *_impl = nullptr; + protected: /** * Constructor @@ -241,41 +249,25 @@ public: */ int __compare(const Base &base) const; - -private: - /** - * Store the object in the zend object cache - * @param entry - * @param tsrm_ls - * @return MixedObject - */ - MixedObject *store(struct _zend_class_entry *entry TSRMLS_DC); +private: /** - * Retrieve the handle - * @return int + * Get access to the implementation object + * @return ObjectImpl */ - int handle() const + const ObjectImpl *implementation() const { - return _handle; + return _impl; } - - /** - * The handle in the zend object cache - * @var int - */ - int _handle = 0; /** - * Friends that have access to the private members + * Classes that have direct access to private date */ - friend class Value; + friend class ObjectImpl; friend class Object; - friend class ClassImpl; - + friend class Value; }; - /** * End of namespace */ diff --git a/include/class.h b/include/class.h index 7b9a928..e0316df 100644 --- a/include/class.h +++ b/include/class.h @@ -16,11 +16,6 @@ */ /** - * Zend/SPL interfaces that we support - */ -extern struct _zend_class_entry *zend_ce_arrayaccess; - -/** * Set up namespace */ namespace Php { diff --git a/include/exception.h b/include/exception.h index dd38035..671df9e 100644 --- a/include/exception.h +++ b/include/exception.h @@ -30,42 +30,51 @@ private: */ int _code; + /** + * Has this exception been processed by native C++ code? + * @var bool + */ + bool _processed = false; + public: /** * Constructor * @param &string */ - Exception(const std::string &message, int code = 0) : std::exception(), _message(message), _code(code) - { - } + Exception(const std::string &message, int code = 0) : std::exception(), _message(message), _code(code) {} /** * Destructor */ - virtual ~Exception() throw() + virtual ~Exception() throw() {} + + /** + * 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 */ - std::string &message() throw() + const std::string &message() const throw() { return _message; } /** - * Process the exception - * - * This method is called only from within the PHP-CPP library, - * and will turn the exception into a PHP exception - * - * @param tsrm_ls - * - * @internal + * Is this a native exception (one that was thrown from C++ code) + * @return bool */ - virtual void process(TSRMLS_D); + virtual bool native() const + { + // yes, it is native + return true; + } }; /** diff --git a/include/extension.h b/include/extension.h index 6d1fa0c..fcc0f72 100644 --- a/include/extension.h +++ b/include/extension.h @@ -6,7 +6,7 @@ * apache process starts - and will be used for all subsequent requests that * are handled by Apache. * - * For some environments (for example CLI scripts and FastCGI calls) only one + * For some environments (for example CLI scripts and CGI calls) only one * request is handled by an extension instance, but for others (when PHP runs * as module in a webserver) many requests are handled by the same extension * instance. @@ -16,11 +16,6 @@ */ /** - * Structures referenced in this class - */ -struct _zend_module_entry; - -/** * Set up namespace */ namespace Php { @@ -28,7 +23,7 @@ namespace Php { /** * Forward declaration */ -class Extension; +class ExtensionImpl; /** * Signature of a callback @@ -46,7 +41,7 @@ public: * @param name Extension name * @param version Extension version string */ - Extension(const char *name = NULL, const char *version = NULL); + Extension(const char *name, const char *version = "1.0"); /** * No copy'ing and no moving @@ -67,13 +62,10 @@ public: * is handled. You can register this callback if you want to be notified * when the engine is ready, for example to initialize certain things. * - * @param callback + * @param callback Function to be called + * @return Extension Same object to allow chaining */ - void onStartup(const Callback &callback) - { - // copy callback - _onStartup = callback; - } + Extension &onStartup(const Callback &callback); /** * Register a function to be called when the PHP engine is going to stop @@ -81,13 +73,10 @@ public: * The callback will be called right before the process is going to stop. * You can register a function if you want to clean up certain things. * - * @param callback + * @param callback Function to be called + * @return Extension Same object to allow chaining */ - void onShutdown(const Callback &callback) - { - // copy callback - _onShutdown = callback; - } + Extension &onShutdown(const Callback &callback); /** * Register a callback that is called at the beginning of each pageview/request @@ -97,13 +86,10 @@ public: * multiple requests after each other, and you may want to set back certain * global variables to their initial variables in front of each request * - * @param callback + * @param callback Function to be called + * @return Extension Same object to allow chaining */ - void onRequest(const Callback &callback) - { - // copy callback - _onRequest = callback; - } + Extension &onRequest(const Callback &callback); /** * Register a callback that is called to cleanup things after a pageview/request @@ -113,105 +99,38 @@ public: * This method is called onIdle because the extension is idle in between * requests. * - * @param callback + * @param callback Function to be called + * @return Extension Same object to allow chaining */ - void onIdle(const Callback &callback) - { - // copy callback - _onIdle = callback; - } + Extension &onIdle(const Callback &callback); /** - * Retrieve the module entry + * Retrieve the module pointer * * This is the memory address that should be exported by the get_module() * function. * - * @return _zend_module_entry + * @return void* */ - _zend_module_entry *module(); + void *module(); /** * Cast to a module entry - * @return _zend_module_entry* + * + * @return void* */ - operator _zend_module_entry * () + operator void * () { return module(); } private: /** - * The information that is passed to the Zend engine - * - * Although it would be slightly faster to not make this a pointer, this - * would require that client code also includes the PHP header files, which - * we try to prevent with the PHP-CPP library, so we allocate it dynamically. + * The implementation object * - * @var zend_module_entry - */ - _zend_module_entry *_entry; - - /** - * Callback that is called after the engine is initialized and before the - * pageviews are going to be handled - * @var Callback - */ - Callback _onStartup; - - /** - * Callback that is called in front of each request - * @var Callback - */ - Callback _onRequest; - - /** - * Callback that is called right after each request - * @var Callback - */ - Callback _onIdle; - - /** - * Callback that is called right before the engine is closing down - * @var Callback - */ - Callback _onShutdown; - - /** - * Function that is called when the extension initializes - * @param type Module type - * @param number Module number - * @param tsrm_ls - * @return int 0 on success - */ - static int onStartup(int type, int module_number TSRMLS_DC); - - /** - * Function that is called when the extension is about to be stopped - * @param type Module type - * @param number Module number - * @param tsrm_ls - * @return int - */ - static int onShutdown(int type, int module_number TSRMLS_DC); - - /** - * Function that is called when a request starts - * @param type Module type - * @param number Module number - * @param tsrm_ls - * @return int 0 on success - */ - static int onRequest(int type, int module_number TSRMLS_DC); - - /** - * Function that is called when a request is ended - * @param type Module type - * @param number Module number - * @param tsrm_ls - * @return int 0 on success + * @var ExtensionImpl */ - static int onIdle(int type, int module_number TSRMLS_DC); + ExtensionImpl *_impl; }; /** diff --git a/include/iterator.h b/include/iterator.h index 13b30fc..331c8b3 100644 --- a/include/iterator.h +++ b/include/iterator.h @@ -2,19 +2,19 @@ * Iterator.h * * Base class for iterators. Extension writers that want to create traversable - * classes, should override this class and implement all pure virtual methods - * in it. + * classes, should override the Php::Traversable base class. This base class + * forces you to implement a getIterator() method that returns an instance of + * a Php::Iterator class. + * + * In this file you find the signature of the Php::Iterator class. It mostly has + * pure virtual methods, which means that you should create a derived class + * that implements all these methods. * * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> * @copyright 2014 Copernica BV */ /** - * Forward declarations - */ -struct _zend_object_iterator_funcs; - -/** * Set up namespace */ namespace Php { @@ -64,7 +64,7 @@ public: */ virtual void rewind() = 0; -private: +protected: /** * During the lifetime of the iterator, the object over which * it iterates is keps as a private variable. This ensures that @@ -73,92 +73,6 @@ private: */ Value _object; - /** - * The current() method that is called by the Zend engine wants a - * pointer-to-pointer-to-a-zval. Because of this, we have to keep the - * current value in memory after the current() method returns because - * the pointer would otherwise fall out of scope. This is (once again) - * odd behavior of the Zend engine, but we'll have to live with that - * @var Value - */ - Value _current; - - /** - * Internal method that returns the implementation object - * @return zend_object_iterator - */ - struct _zend_object_iterator *implementation(); - - /** - * Iterator destructor method - * @param iter - * @param tsrm_ls - */ - static void destructor(struct _zend_object_iterator *iter TSRMLS_DC); - - /** - * Iterator valid function - * Returns FAILURE or SUCCESS - * @param iter - * @param tsrm_ls - * @return int - */ - static int valid(struct _zend_object_iterator *iter TSRMLS_DC); - - /** - * Fetch the current item - * @param iter - * @param data - * @param tsrm_ls - */ - static void current(struct _zend_object_iterator *iter, struct _zval_struct ***data TSRMLS_DC); - - /** - * Fetch the key for the current element (optional, may be NULL). The key - * should be written into the provided zval* using the ZVAL_* macros. If - * this handler is not provided auto-incrementing integer keys will be - * used. - * @param iter - * @param data - * @param tsrm_ls - */ - static void key(struct _zend_object_iterator *iter, struct _zval_struct *data TSRMLS_DC); - - /** - * Function to retrieve the current key, php 5.3 style - * @param iter - * @param str_key - * @param str_key_len - * @param int_key - * @param tsrm_ls - * @return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG - */ - static int key(struct _zend_object_iterator *iter, char **str_key, unsigned int *str_key_len, unsigned long *int_key TSRMLS_DC); - - /** - * Step forwards to the next element - * @param iter - * @param tsrm_ls - */ - static void next(struct _zend_object_iterator *iter TSRMLS_DC); - - /** - * Rewind the iterator back to the start - * @param iter - * @param tsrm_ls - */ - static void rewind(struct _zend_object_iterator *iter TSRMLS_DC); - - /** - * Get access to all iterator functions - * @return zend_object_iterator_funcs - */ - static struct _zend_object_iterator_funcs *functions(); - - /** - * Classbase is a friend - */ - friend class ClassImpl; }; /** diff --git a/include/namespace.h b/include/namespace.h index 5830339..25d4e5e 100644 --- a/include/namespace.h +++ b/include/namespace.h @@ -23,32 +23,43 @@ class Function; */ class Namespace { -public: +protected: /** - * Constructor - * @param name Name of the namespace + * Name of the namespace + * @var string */ - Namespace(const char *name) : _name(name) {} + std::string _name; + + /** + * Functions defined in the namespace + * @var list + */ + std::list<std::shared_ptr<Function>> _functions; + + /** + * Classes defined in the namespace + * @var list + */ + std::list<std::shared_ptr<ClassBase>> _classes; /** - * Copy constructor - * @param ns Namespace to copy + * Namespaces defined inside the namespace + * @var list */ - Namespace(const Namespace &ns) : - _name(ns._name), - _functions(ns._functions), - _classes(ns._classes), - _namespaces(ns._namespaces) {} - + std::list<std::shared_ptr<Namespace>> _namespaces; + /** - * Move constructor - * @param ns + * Ini entry defined by the extension + * @var list */ - Namespace(Namespace &&ns) : - _name(std::move(ns._name)), - _functions(std::move(ns._functions)), - _classes(std::move(ns._classes)), - _namespaces(std::move(ns._namespaces)) {} + std::list<std::shared_ptr<Ini>> _ini_entries; + +public: + /** + * Constructor + * @param name Name of the namespace + */ + Namespace(const char *name) : _name(name) {} /** * Destructor @@ -56,7 +67,7 @@ public: virtual ~Namespace() {} /** - * Add a native function directly to the extension + * Add a native function directly to the namespace * @param name Name of the function * @param function The function to add * @param arguments Optional argument specification @@ -68,106 +79,91 @@ public: Namespace &add(const char *name, const native_callback_3 &function, const Arguments &arguments = {}); /** - * Add a native class to the extension by moving it + * Add a native class to the namespace by moving it * @param type The class implementation * @return Namespace Same object to allow chaining */ template<typename T> Namespace &add(Class<T> &&type) { - // make a copy of the object - auto *copy = new Class<T>(std::move(type)); - - // and add it to the list of classes - _classes.push_back(std::unique_ptr<ClassBase>(copy)); + // make a copy of the object, and add it to the list of classes + _classes.push_back(std::unique_ptr<ClassBase>(new Class<T>(std::move(type)))); // allow chaining return *this; } /** - * Add a native class to the extension by copying it + * Add a native class to the namespace by copying it * @param type The class implementation - * @param Namespace Same object to allow chaining + * @return Namespace Same object to allow chaining */ template<typename T> Namespace &add(const Class<T> &type) { - // make a copy of the object - auto *copy = new Class<T>(std::move(type)); - // and add it to the list of classes - _classes.push_back(std::unique_ptr<ClassBase>(copy)); + _classes.push_back(std::unique_ptr<ClassBase>(new Class<T>(type))); // allow chaining return *this; } /** - * Add an interface to the extension by moving it + * Add an interface to the namespace by moving it * @param interface The interface properties + * @return Namespace Same object to allow chaining */ Namespace &add(Interface &&interface) { - // make a copy of the object - auto *copy = new Interface(std::move(interface)); - - // and add it to the list of classes - _classes.push_back(std::unique_ptr<ClassBase>(copy)); + // make a copy and add it to the list of classes + _classes.push_back(std::unique_ptr<ClassBase>(new Interface(std::move(interface)))); // allow chaining return *this; } /** - * Add an interface to the extension by copying it + * Add an interface to the namespace by copying it * @param interface The interface properties + * @return Namespace Same object to allow chaining */ Namespace &add(const Interface &interface) { - // make a copy of the object - auto *copy = new Interface(interface); - - // and add it to the list of classes - _classes.push_back(std::unique_ptr<ClassBase>(copy)); + // make a copy and add it to the list of classes + _classes.push_back(std::unique_ptr<ClassBase>(new Interface(interface))); // allow chaining return *this; } /** - * Add a namespace to the extension by moving it + * Add a namespace to the namespace by moving it * @param ns The namespace + * @return Namespace Same object to allow chaining */ Namespace &add(Namespace &&ns) { - // make a copy of the object - auto *copy = new Namespace(std::move(ns)); - // add it to the list of namespaces - _namespaces.push_back(std::unique_ptr<Namespace>(copy)); + _namespaces.push_back(std::unique_ptr<Namespace>(new Namespace(std::move(ns)))); // allow chaining return *this; } /** - * Add a namespace to the extension by copying it + * Add a namespace to the namespace by copying it * @param ns The namespace + * @return Namespace Same object to allow chaining */ Namespace &add(const Namespace &ns) { - // make a copy of the object - auto *copy = new Namespace(std::move(ns)); - - // add it to the list of namespaces - _namespaces.push_back(std::unique_ptr<Namespace>(copy)); + // make a copy and add it to the list of namespaces + _namespaces.push_back(std::unique_ptr<Namespace>(new Namespace(ns))); // allow chaining return *this; } - /** * Add a ini entry to the extension by moving it * @param ini The class implementation @@ -175,11 +171,8 @@ public: */ Namespace &add(Ini &&ini) { - // make a copy of the object - auto *copy = new Ini(std::move(ini)); - // and add it to the list of classes - _ini_entries.push_back(std::unique_ptr<Ini>(copy)); + _ini_entries.push_back(std::unique_ptr<Ini>(new Ini(std::move(ini)))); // allow chaining return *this; @@ -192,48 +185,13 @@ public: */ Namespace &add(const Ini &ini) { - // make a copy of the object - auto *copy = new Ini(std::move(ini)); - // and add it to the list of classes - _ini_entries.push_back(std::unique_ptr<Ini>(copy)); + _ini_entries.push_back(std::unique_ptr<Ini>(new Ini(ini))); // allow chaining return *this; } - -protected: - /** - * Name of the namespace - * @var string - */ - std::string _name; - - /** - * Functions defined by the extension - * @var list - */ - std::list<std::shared_ptr<Function>> _functions; - - /** - * Classes defined by the extension - * @var list - */ - std::list<std::shared_ptr<ClassBase>> _classes; - - /** - * Namespaces defined by the extension - * @var list - */ - std::list<std::shared_ptr<Namespace>> _namespaces; - - /** - * Ini entry defined by the extension - * @var list - */ - std::list<std::shared_ptr<Ini>> _ini_entries; - /** * The total number of functions * @return size_t @@ -249,21 +207,27 @@ protected: // done return result; } - + /** - * Initialize all functions in this namespace - * @param ns Namespace prefix - * @param entries The array to be filled - * @return int Number of functions that were initialized + * Apply a callback to each registered function + * + * The callback will be called with the name of the namespace, and + * a reference to the registered function. + * + * @param callback */ - size_t initialize(const std::string &ns, struct _zend_function_entry entries[]); - + void apply(const std::function<void(const std::string &ns, Function &func)> &callback); + /** - * Initialize the namespace after it was registered - * @param parent Parent namespace - * @param tsrm_ls + * Apply a callback to each registered class + * + * The callback will be called with the name of the namespace, and + * a reference to the registered class. + * + * @param callback */ - void initialize(const std::string &parent TSRMLS_DC); + void apply(const std::function<void(const std::string &ns, ClassBase &clss)> &callback); + }; /** diff --git a/include/object.h b/include/object.h index 1445943..97c9482 100644 --- a/include/object.h +++ b/include/object.h @@ -140,7 +140,7 @@ public: if (this == &value) return *this; // type must be valid - if (value.type() != Type::Object) throw Php::Exception("Assiging a non-object to an object variable"); + if (value.type() != Type::Object) throw Php::Exception("Assigning a non-object to an object variable"); // call base Value::operator=(value); diff --git a/include/parameters.h b/include/parameters.h index fa53004..b464260 100644 --- a/include/parameters.h +++ b/include/parameters.h @@ -22,15 +22,25 @@ class Base; */ class Parameters : public std::vector<Value> { -public: +private: /** - * Constructor - * @param this_ptr Optional this_ptr - * @param argc Number of arguments - * @param tsrm_ls + * The base object + * @var Base */ - Parameters(struct _zval_struct *this_ptr, int argc TSRMLS_DC); + Base *_object = nullptr; +protected: + /** + * Protected constructor + * + * The constructor is protected because extension programmers are not + * supposed to instantiate parameters objects themselves + * + * @param object The 'this' object + */ + Parameters(Base *object) : _object(object) {} + +public: /** * Destructor */ @@ -44,13 +54,6 @@ public: { return _object; } - -private: - /** - * The base object - * @var Base - */ - Base *_object = nullptr; }; /** diff --git a/include/value.h b/include/value.h index e7fa825..95985d7 100644 --- a/include/value.h +++ b/include/value.h @@ -943,12 +943,13 @@ protected: friend class Globals; friend class Member; friend class ClassImpl; - friend class Iterator; + friend class IteratorImpl; friend class Extension; friend class HashIterator; friend class TraverseIterator; friend class HashMember<int>; friend class HashMember<std::string>; + friend class Callable; }; /** diff --git a/include/valueiterator.h b/include/valueiterator.h index fd8118d..95cdac6 100644 --- a/include/valueiterator.h +++ b/include/valueiterator.h @@ -12,12 +12,6 @@ */ /** - * Forward declaration - */ -struct _hashtable; -struct bucket; - -/** * Set up namespace */ namespace Php { @@ -25,7 +19,7 @@ namespace Php { /** * Forward declarations */ -class IteratorImpl; +class ValueIteratorImpl; /** * Class definition @@ -37,7 +31,7 @@ public: * Constructor * @param impl Implementation iterator */ - ValueIterator(IteratorImpl *impl) : _impl(impl) {} + ValueIterator(ValueIteratorImpl *impl) : _impl(impl) {} /** * Copy constructor @@ -125,7 +119,7 @@ private: * Pointer to the actual implementation * @var std::unique_ptr */ - IteratorImpl *_impl; + ValueIteratorImpl *_impl; }; @@ -23,32 +23,6 @@ #include <map> /** - * Include PHP config - */ -#include <phpcpp/config.h> - -/** - * Is ZTS enabled? - */ -#ifdef ZTS - - // enable TSRM -# define TSRMLS_C tsrm_ls -# define TSRMLS_CC ,tsrm_ls -# define TSRMLS_D void ***tsrm_ls -# define TSRMLS_DC ,void ***tsrm_ls - -#else - - // disable TSRM -# define TSRMLS_C -# define TSRMLS_CC -# define TSRMLS_D -# define TSRMLS_DC - -#endif - -/** * Include all headers files that are related to this library */ #include <phpcpp/ini.h> diff --git a/src/argument.cpp b/src/argument.cpp deleted file mode 100644 index a2aa8d5..0000000 --- a/src/argument.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Argument.cpp - * - * Implementation for the Argument class - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2013 Copernica BV - */ -#include "includes.h" - -/** - * Set up namespace - */ -namespace Php { - -/** - * Constructor - * @param name Name of the argument - * @param type Argument type - * @param required Is this argument required? - * @param byref Is this a reference argument - */ -Argument::Argument(const char *name, Type type, bool required, bool byref) -{ - // construct object - _info = new zend_arg_info; - - // fill members - _info->name = name; - _info->name_len = strlen(name); - -#if PHP_VERSION_ID >= 50400 - - // since php 5.4 there is a type-hint - _info->type_hint = (unsigned char)(type == Type::Array || type == Type::Callable ? type : Type::Null); - -# if PHP_VERSION_ID >= 50600 - - // from PHP 5.6 and onwards, an is_variadic property can be set, this - // specifies whether this argument is the first argument that specifies - // the type for a variable length list of arguments. For now we only - // support methods and functions with a fixed number of arguments. - _info->is_variadic = false; - -# endif - -#else - - // php 5.3 code - _info->array_type_hint = type == Type::Array; - _info->return_reference = false; - _info->required_num_args = 0; // @todo is this correct? - -#endif - - // this parameter is a regular type - _info->class_name = NULL; - _info->class_name_len = 0; - _info->allow_null = false; - _info->pass_by_reference = byref; - - // store if required - _required = required; -} - -/** - * Constructor - * @param name Name of the argument - * @param classname Name of the class - * @param nullable Can it be null? - * @param required Is this argument required? - * @param byref Is this a reference argument? - */ -Argument::Argument(const char *name, const char *classname, bool nullable, bool required, bool byref) -{ - // construct object - _info = new zend_arg_info; - - // fill members - _info->name = name; - _info->name_len = strlen(name); - -#if PHP_VERSION_ID >= 50400 - - // since php 5.4 there is a type hint - _info->type_hint = (unsigned char)Type::Object; - -# if PHP_VERSION_ID >= 50600 - - // from PHP 5.6 and onwards, an is_variadic property can be set, this - // specifies whether this argument is the first argument that specifies - // the type for a variable length list of arguments. For now we only - // support methods and functions with a fixed number of arguments. - _info->is_variadic = false; - -# endif - -#else - - // php 5.3 code - _info->array_type_hint = false; - _info->return_reference = false; - _info->required_num_args = 0; // @todo is this correct? - -#endif - - // the parameter is a class - _info->class_name = classname; - _info->class_name_len = strlen(classname); - _info->allow_null = nullable; - _info->pass_by_reference = byref; - - // store if required - _required = required; -} - -/** - * Copy constructor - * @param argument - */ -Argument::Argument(const Argument &argument) -{ - // construct object - _info = new zend_arg_info; - - // fill members - *_info = *argument._info; - - // store if required - _required = argument._required; -} - -/** - * Move constructor - * @param argument - */ -Argument::Argument(Argument &&argument) -{ - // copy memory pointer - _info = argument._info; - - // forget in other object - argument._info = nullptr; - - // store if required - _required = argument._required; -} - -/** - * Destructor - */ -Argument::~Argument() -{ - if (_info) delete _info; -} - -/** - * Fill an arg_info structure with data - * @param info - * @internal - */ -void Argument::fill(struct _zend_arg_info *info) const -{ - // copy all data - *info = *_info; -} - -/** - * End of namespace - */ -} diff --git a/src/exception.cpp b/src/exception.cpp deleted file mode 100644 index 810a73c..0000000 --- a/src/exception.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Exception.cpp - * - * Implementation for the exception class - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2014 Copernica BV - */ -#include "includes.h" - -/** - * Set up namespace - */ -namespace Php { - -/** - * Process the exception - * - * This method is called only from within the PHP-CPP library, - * and will turn the exception into a PHP exception - * - * @param tsrm_ls - */ -void Exception::process(TSRMLS_D) -{ - // an exception originally thrown by C++ should be passed on to PHP - zend_throw_exception(zend_exception_get_default(TSRMLS_C), (char*)message().c_str(), 0 TSRMLS_CC); -} - -/** - * End namespace - */ -} - diff --git a/src/mixedobject.h b/src/mixedobject.h deleted file mode 100644 index 4db74da..0000000 --- a/src/mixedobject.h +++ /dev/null @@ -1,39 +0,0 @@ -/** - * MixedObject.h - * - * Structure that combines a Zend object with an object in C++ - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2013 Copernica BV - */ - -/** - * Set up namespace - */ -namespace Php { - -/** - * Structure that combines a C++ object with a zend object - */ -struct MixedObject -{ - /** - * The actual object is the first member, so that casting - * the MixedObject to a zend_object will also result in a valid pointer - * @var zend_object - */ - zend_object php; - - /** - * Pointer to the C++ implementation - * @var Base - */ - Base *cpp; -}; - -/** - * End of namespace - */ -} - - diff --git a/src/origexception.cpp b/src/origexception.cpp deleted file mode 100644 index f64d696..0000000 --- a/src/origexception.cpp +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Implementation of the exception that was originally thrown by PHP - * code or the zend engine, and that could or could not be picked - * up by C++ code - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2013 Copernica BV - */ -#include "includes.h" - -/** - * Set up namespace - */ -namespace Php { - -/** - * Destructor - */ -OrigException::~OrigException() noexcept -{ - // skip if the exception was restored - if (_restored) return; - - // we need the tsrm_ls var - TSRMLS_FETCH(); - - // clean up the exception, because it was handled in C++ code - zend_clear_exception(TSRMLS_C); -} - -/** - * End of namespace - */ -} - diff --git a/src/origexception.h b/src/origexception.h deleted file mode 100644 index 5cbb0e1..0000000 --- a/src/origexception.h +++ /dev/null @@ -1,79 +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 <emiel.bruijntjes@copernica.com> - * @copyright 2013 Copernica BV - */ - -/** - * Set up namespace - */ -namespace Php { - -/** - * Class definition - */ -class OrigException : public Value, public Exception -{ -private: - /** - * Has the exception been restored by the C++ code so that it can be dealt by PHP? - * @var boolean - */ - bool _restored = false; - -public: - /** - * Constructor - * @param zval - */ - OrigException(struct _zval_struct *zval) : - Value(zval), Exception("OrigException") {} - - /** - * Copy constructor - * @param exception - */ - OrigException(const OrigException &exception) : - Value(exception), Exception("OrigException"), _restored(exception._restored) {} - - /** - * Move constructor - * @param exception - */ - OrigException(OrigException &&exception) : - Value(std::move(exception)), Exception("OrigException"), _restored(exception._restored) - { - // set other exception to restored so that it wont - // do anything on destruction - exception._restored = true; - } - - /** - * Destructor - */ - virtual ~OrigException() throw(); - - /** - * Process the exception - * - * This will restore the exception so that it can be further processed - * in PHP code - * - * @param tsrm_ls - * @internal - */ - virtual void process(TSRMLS_D) override - { - // mark exception as restored - _restored = true; - } -}; - -/** - * End of namespace - */ -} diff --git a/src/parameters.cpp b/src/parameters.cpp deleted file mode 100644 index c4ed9c7..0000000 --- a/src/parameters.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Parameters.cpp - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2013 Copernica BV - */ -#include "includes.h" - -/** - * Set up namespace - */ -namespace Php { - -/** - * Parameters - * @param this_ptr Pointer to the object - * @param argc Number of arguments - * @param tsrm_ls - */ -Parameters::Parameters(zval *this_ptr, int argc TSRMLS_DC) -{ - // reserve plenty of space - reserve(argc); - - // loop through the arguments - for (int i=0; i<argc; i++) - { - // get the argument - zval **arg = (zval **) (zend_vm_stack_top(TSRMLS_C) - 1 - (argc-i)); - - // append value - push_back(Value(*arg)); - } - - // skip if there is no this_ptr - if (!this_ptr) return; - - // get the mixed object - MixedObject *obj = (MixedObject *)zend_object_store_get_object(this_ptr TSRMLS_CC); - - // store the CPP object - _object = obj->cpp; -} - -/** - * End of namespace - */ -} - diff --git a/tests/php/phpt/variables/021-HashMember-3.phpt b/tests/php/phpt/variables/021-HashMember-3.phpt index c6c517f..20f43ba 100644 --- a/tests/php/phpt/variables/021-HashMember-3.phpt +++ b/tests/php/phpt/variables/021-HashMember-3.phpt @@ -22,6 +22,5 @@ array ( 'key1' => array ( 'key2' => 'val1-2', - 'key3' => 'val1-3', ), )
\ No newline at end of file diff --git a/src/arithmetic.h b/zend/arithmetic.h index 20e346e..20e346e 100644 --- a/src/arithmetic.h +++ b/zend/arithmetic.h diff --git a/src/base.cpp b/zend/base.cpp index a828083..5d15011 100644 --- a/src/base.cpp +++ b/zend/base.cpp @@ -18,61 +18,61 @@ namespace Php { * @param tsrm_ls * @return MixedObject */ -MixedObject *Base::store(zend_class_entry *entry TSRMLS_DC) -{ - // allocate memory for the object - MixedObject *result = (MixedObject *)emalloc(sizeof(MixedObject)); - - // store the new c++ object - result->cpp = this; - - // store the class entry in the newly created object - result->php.ce = entry; - - // initialize the object - zend_object_std_init(&result->php, entry TSRMLS_CC); - -#if PHP_VERSION_ID < 50399 - - // tmp variable - zval *tmp; - - // initialize the properties, php 5.3 way - zend_hash_copy(result->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_property_ctor, &tmp, sizeof(zval*)); - -#else - - // version higher than 5.3 have an easier way to initialize - object_properties_init(&result->php, entry); - -#endif - -#ifdef ZTS - - // when in thread safety mode, the destruct method and free method have - // an extra parameter holding thread information - using DestructType = void(zend_object*,unsigned int,void***); - using FreeType = void(zend_object*,void***); - -#else - - // not in thread mode: no special parameter for the tsrm_ls variable - using DestructType = void(zend_object*,unsigned int); - using FreeType = void(zend_object*); - -#endif - - // store the two destruct methods in temporary vars - DestructType *destructMethod = &ClassImpl::destructObject; - FreeType *freeMethod = &ClassImpl::freeObject; - - // the destructor and clone handlers are set to NULL. I dont know why, but they do not - // seem to be necessary... - _handle = zend_objects_store_put(result, (zend_objects_store_dtor_t)destructMethod, (zend_objects_free_object_storage_t)freeMethod, NULL TSRMLS_CC); - - // done - return result; -} +//MixedObject *Base::store(zend_class_entry *entry TSRMLS_DC) +//{ +// // allocate memory for the object +// MixedObject *result = (MixedObject *)emalloc(sizeof(MixedObject)); +// +// // store the new c++ object +// result->cpp = this; +// +// // store the class entry in the newly created object +// result->php.ce = entry; +// +// // initialize the object +// zend_object_std_init(&result->php, entry TSRMLS_CC); +// +//#if PHP_VERSION_ID < 50399 +// +// // tmp variable +// zval *tmp; +// +// // initialize the properties, php 5.3 way +// zend_hash_copy(result->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_property_ctor, &tmp, sizeof(zval*)); +// +//#else +// +// // version higher than 5.3 have an easier way to initialize +// object_properties_init(&result->php, entry); +// +//#endif +// +//#ifdef ZTS +// +// // when in thread safety mode, the destruct method and free method have +// // an extra parameter holding thread information +// using DestructType = void(zend_object*,unsigned int,void***); +// using FreeType = void(zend_object*,void***); +// +//#else +// +// // not in thread mode: no special parameter for the tsrm_ls variable +// using DestructType = void(zend_object*,unsigned int); +// using FreeType = void(zend_object*); +// +//#endif +// +// // store the two destruct methods in temporary vars +// DestructType *destructMethod = &ClassImpl::destructObject; +// FreeType *freeMethod = &ClassImpl::freeObject; +// +// // the destructor and clone handlers are set to NULL. I dont know why, but they do not +// // seem to be necessary... +// _handle = zend_objects_store_put(result, (zend_objects_store_dtor_t)destructMethod, (zend_objects_free_object_storage_t)freeMethod, NULL TSRMLS_CC); +// +// // done +// return result; +//} /** * Overridable method that is called right before an object is destructed diff --git a/src/boolmember.h b/zend/boolmember.h index 5b5d43d..5b5d43d 100644 --- a/src/boolmember.h +++ b/zend/boolmember.h diff --git a/src/callable.cpp b/zend/callable.cpp index 5e37df7..96f5f90 100644 --- a/src/callable.cpp +++ b/zend/callable.cpp @@ -23,7 +23,7 @@ namespace Php { * @param tsrm_ls * @return integer */ -static void invoke_callable(INTERNAL_FUNCTION_PARAMETERS) +void Callable::invoke(INTERNAL_FUNCTION_PARAMETERS) { // find the function name const char *name = get_active_function_name(TSRMLS_C); @@ -31,22 +31,27 @@ static void invoke_callable(INTERNAL_FUNCTION_PARAMETERS) // uncover the hidden pointer inside the function name Callable *callable = HiddenPointer<Callable>(name); - // wrap the return value - Value result(return_value, true); - // construct parameters - Parameters params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); + ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); // the function could throw an exception try { // get the result - result = callable->invoke(params); + Value result(callable->invoke(params)); + + // detach the zval (we don't want it to be destructed) + zval *val = result.detach(); + + // @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); } catch (Exception &exception) { // process the exception - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } @@ -64,7 +69,7 @@ void Callable::initialize(zend_function_entry *entry, const char *classname, int { // fill the members of the entity, and hide a pointer to the current object in the name entry->fname = (const char *)_ptr; - entry->handler = invoke_callable; + entry->handler = &Callable::invoke; entry->arg_info = _argv; entry->num_args = _argc; entry->flags = flags; diff --git a/src/callable.h b/zend/callable.h index f123c8b..67863a9 100644 --- a/src/callable.h +++ b/zend/callable.h @@ -9,12 +9,6 @@ */ /** - * Forward definitions - */ -struct _zend_function_entry; -struct _zend_arg_info; - -/** * Set up namespace */ namespace Php { @@ -43,11 +37,11 @@ public: // loop through the arguments for (auto it = arguments.begin(); it != arguments.end(); it++) { - // increment required + // increment counter with number of required parameters if (it->required()) _required++; // fill the arg info - it->fill(&_argv[i++]); + fill(&_argv[i], *it); } } @@ -99,7 +93,7 @@ public: * @param classname Optional class name * @param flags Access flags */ - void initialize(struct _zend_function_entry *entry, const char *classname = nullptr, int flags = 0) const; + void initialize(zend_function_entry *entry, const char *classname = nullptr, int flags = 0) const; /** * Fill function info @@ -107,7 +101,7 @@ public: * @param ns Active namespace * @param classname Optional class name */ - void initialize(struct _zend_arg_info *info, const char *classname = nullptr) const; + void initialize(zend_arg_info *info, const char *classname = nullptr) const; protected: @@ -139,7 +133,66 @@ protected: * The arguments * @var zend_arg_info[] */ - struct _zend_arg_info *_argv = nullptr; + zend_arg_info *_argv = nullptr; + + /** + * Private helper method to fill an argument object + * @param info object from the zend engine + * @param arg original object + */ + void fill(zend_arg_info *info, const Argument &arg) const + { + // fill members + info->name = arg.name().c_str(); + info->name_len = arg.name().size(); + +#if PHP_VERSION_ID >= 50400 + + // since php 5.4 there is a type-hint, but we only support arrays, objects and callables + switch (arg.type()) { + case Type::Array: info->type_hint = IS_ARRAY; break; + case Type::Callable: info->type_hint = IS_CALLABLE; break; + case Type::Object: info->type_hint = IS_OBJECT; break; + default: info->type_hint = IS_NULL; break; + } + +# if PHP_VERSION_ID >= 50600 + + // from PHP 5.6 and onwards, an is_variadic property can be set, this + // specifies whether this argument is the first argument that specifies + // the type for a variable length list of arguments. For now we only + // support methods and functions with a fixed number of arguments. + info->is_variadic = false; + +# endif + +#else + + // php 5.3 code + info->array_type_hint = arg.type() == Type::Array; + info->return_reference = false; + info->required_num_args = 0; // @todo is this correct? + +#endif + + // this parameter is a regular type + info->class_name = arg.type() == Type::Object ? arg.classname().c_str() : nullptr; + info->class_name_len = arg.type() == Type::Object ? arg.classname().size() : 0; + info->allow_null = arg.allowNull(); + info->pass_by_reference = arg.byReference(); + } + + /** + * Function that is called by the Zend engine every time that a function gets called + * @param ht + * @param return_value + * @param return_value_ptr + * @param this_ptr + * @param return_value_used + * @param tsrm_ls + * @return integer + */ + static void invoke(INTERNAL_FUNCTION_PARAMETERS); }; diff --git a/src/classbase.cpp b/zend/classbase.cpp index 18d81d5..18d81d5 100644 --- a/src/classbase.cpp +++ b/zend/classbase.cpp diff --git a/src/classimpl.cpp b/zend/classimpl.cpp index 3999cf3..d63956a 100644 --- a/src/classimpl.cpp +++ b/zend/classimpl.cpp @@ -28,11 +28,15 @@ ClassImpl::~ClassImpl() } /** + * @todo refactor so that methods become simpler + */ + +/** * Retrieve our C++ implementation object * @param entry * @return ClassImpl */ -static ClassImpl *cpp_impl(zend_class_entry *entry) +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) @@ -52,34 +56,6 @@ static ClassImpl *cpp_impl(zend_class_entry *entry) } /** - * Retrieve the extension's class object - * @param entry - * @return ClassBase - */ -ClassBase *ClassImpl::base(zend_class_entry *entry) -{ - // first the implementation class - ClassImpl *impl = cpp_impl(entry); - - // and now the base - return impl->_base; -} - -/** - * Retrieve the CPP object - * @param val - * @return Base - */ -static Base *cpp_object(const zval *val TSRMLS_DC) -{ - // retrieve the old object, which we are going to copy - MixedObject *object = (MixedObject *)zend_object_store_get_object(val TSRMLS_CC); - - // return the cpp object - return object->cpp; -} - -/** * Extended zend_internal_function structure that we use to store an * instance of the ClassBase object. We need this for static method calls */ @@ -121,7 +97,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) Value result(return_value, true); // construct parameters - Parameters params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); + ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); // retrieve the base object Base *base = params.object(); @@ -138,7 +114,7 @@ void ClassImpl::callMethod(INTERNAL_FUNCTION_PARAMETERS) catch (Exception &exception) { // process the exception - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } @@ -167,7 +143,7 @@ void ClassImpl::callInvoke(INTERNAL_FUNCTION_PARAMETERS) Value result(return_value, true); // construct parameters - Parameters params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); + ParametersImpl params(this_ptr, ZEND_NUM_ARGS() TSRMLS_CC); // retrieve the base object Base *base = params.object(); @@ -179,12 +155,11 @@ 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) { // process the exception - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } @@ -244,7 +219,7 @@ zend_function *ClassImpl::getMethod(zval **object_ptr, char *method_name, int me function->function_name = method_name; // store pointer to ourselves - data->self = cpp_impl(entry); + data->self = self(entry); // done (cast to zend_function* is allowed, because a zend_function is a union // that has one member being a zend_internal_function) @@ -289,7 +264,7 @@ zend_function *ClassImpl::getStaticMethod(zend_class_entry *entry, char* method, function->function_name = method; // store pointer to ourselves - data->self = cpp_impl(entry); + data->self = self(entry); // done (cast to zend_function* is allowed, because a zend_function is a union // that has one member being a zend_internal_function) @@ -334,7 +309,7 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct function->function_name = nullptr; // store pointer to ourselves - data->self = cpp_impl(entry); + data->self = self(entry); // assign this dynamically allocated variable to the func parameter // the case is ok, because zend_internal_function is a member of the @@ -405,31 +380,31 @@ zend_object_handlers *ClassImpl::objectHandlers() /** * Function to compare two objects - * @param object1 - * @param object2 + * @param val1 + * @param val2 * @param tsrm_ls * @return int */ -int ClassImpl::compare(zval *object1, zval *object2 TSRMLS_DC) +int ClassImpl::compare(zval *val1, zval *val2 TSRMLS_DC) { // prevent exceptions try { // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(object1 TSRMLS_CC); + auto *entry = zend_get_class_entry(val1 TSRMLS_CC); // other object must be of the same type - if (entry != zend_get_class_entry(object2 TSRMLS_CC)) throw NotImplemented(); + if (entry != zend_get_class_entry(val2 TSRMLS_CC)) throw NotImplemented(); // we need the C++ class meta-information object - ClassBase *meta = base(entry); + ClassBase *meta = self(entry)->_base; // get the base objects - Base *base1 = cpp_object(object1 TSRMLS_CC); - Base *base2 = cpp_object(object2 TSRMLS_CC); + Base *object1 = ObjectImpl::find(val1 TSRMLS_CC)->object(); + Base *object2 = ObjectImpl::find(val2 TSRMLS_CC)->object(); // run the compare method - return meta->callCompare(base1, base2); + return meta->callCompare(object1, object2); } catch (const NotImplemented &exception) { @@ -437,13 +412,13 @@ int ClassImpl::compare(zval *object1, zval *object2 TSRMLS_DC) if (!std_object_handlers.compare_objects) return 1; // call default - return std_object_handlers.compare_objects(object1, object2 TSRMLS_CC); + return std_object_handlers.compare_objects(val1, val2 TSRMLS_CC); } catch (Exception &exception) { // a Php::Exception was thrown by the extension __compare function, // pass this on to user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // what shall we return here... return 1; @@ -452,22 +427,22 @@ int ClassImpl::compare(zval *object1, zval *object2 TSRMLS_DC) /** * Function to cast the object to a different type - * @param object + * @param val * @param retval * @param type * @param tsrm_ls * @return int */ -int ClassImpl::cast(zval *object, zval *retval, int type TSRMLS_DC) +int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC) { - // get the base object - Base *base = cpp_object(object TSRMLS_CC); + // get the base c++ object + Base *object = ObjectImpl::find(val TSRMLS_CC)->object(); // retrieve the class entry linked to this object - auto *entry = zend_get_class_entry(object TSRMLS_CC); + auto *entry = zend_get_class_entry(val TSRMLS_CC); // we need the C++ class meta-information object - ClassBase *meta = ClassImpl::base(entry); + ClassBase *meta = self(entry)->_base; // retval it not yet initialized --- and again feelings of disbelief, // frustration, wonder and anger come up when you see that there are not two @@ -486,18 +461,18 @@ int ClassImpl::cast(zval *object, zval *retval, int type TSRMLS_DC) // check type switch ((Type)type) { - case Type::Numeric: result = meta->callToInteger(base).detach(); break; - case Type::Float: result = meta->callToFloat(base).detach(); break; - case Type::Bool: result = meta->callToBool(base).detach(); break; - case Type::String: result = meta->callToString(base).detach(); break; + case Type::Numeric: result = meta->callToInteger(object).detach(); break; + case Type::Float: result = meta->callToFloat(object).detach(); break; + case Type::Bool: result = meta->callToBool(object).detach(); break; + case Type::String: result = meta->callToString(object).detach(); break; default: throw NotImplemented(); break; } // @todo do we turn into endless conversion if the __toString object returns 'this' ?? // (and if it does: who cares? If the extension programmer is stupid, why do we have to suffer?) - // is the object overwritten? - if (object == retval) zval_dtor(object); + // is the original parameter overwritten? + if (val == retval) zval_dtor(val); // overwrite the result ZVAL_ZVAL(retval, result, 1, 1); @@ -511,12 +486,12 @@ int ClassImpl::cast(zval *object, zval *retval, int type TSRMLS_DC) if (!std_object_handlers.cast_object) return FAILURE; // call default - return std_object_handlers.cast_object(object, retval, type TSRMLS_CC); + return std_object_handlers.cast_object(val, retval, type TSRMLS_CC); } catch (Exception &exception) { // pass on the exception to php userspace - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // done return FAILURE; @@ -534,14 +509,14 @@ zend_object_value ClassImpl::cloneObject(zval *val TSRMLS_DC) auto *entry = zend_get_class_entry(val TSRMLS_CC); // we need the C++ class meta-information object - ClassImpl *impl = cpp_impl(entry); + ClassImpl *impl = self(entry); ClassBase *meta = impl->_base; // retrieve the old object, which we are going to copy - MixedObject *old_object = (MixedObject *)zend_object_store_get_object(val TSRMLS_CC); + ObjectImpl *old_object = ObjectImpl::find(val TSRMLS_CC); // create a new base c++ object - auto *cpp = meta->clone(old_object->cpp); + auto *cpp = meta->clone(old_object->object()); // report error on failure (this does not occur because the cloneObject() // method is only installed as handler when we have seen that there is indeed @@ -555,14 +530,14 @@ zend_object_value ClassImpl::cloneObject(zval *val TSRMLS_DC) result.handlers = impl->objectHandlers(); // store the object - MixedObject *new_object = cpp->store(entry TSRMLS_CC); + ObjectImpl *new_object = new ObjectImpl(entry, cpp TSRMLS_CC); // store the object in the object cache - result.handle = cpp->handle(); + result.handle = new_object->handle(); // clone the members (this will also call the __clone() function if the user // had registered that as a visible method) - zend_objects_clone_members(&new_object->php, result, &old_object->php, Z_OBJ_HANDLE_P(val) TSRMLS_CC); + zend_objects_clone_members(new_object->php(), result, old_object->php(), Z_OBJ_HANDLE_P(val) TSRMLS_CC); // was a custom clone method installed? If not we call the magic c++ __clone method if (!entry->clone) meta->callClone(cpp); @@ -584,7 +559,7 @@ zend_object_value ClassImpl::cloneObject(zval *val TSRMLS_DC) int ClassImpl::countElements(zval *object, long *count TSRMLS_DC) { // does it implement the countable interface? - Countable *countable = dynamic_cast<Countable*>(cpp_object(object TSRMLS_CC)); + Countable *countable = dynamic_cast<Countable*>(ObjectImpl::find(object TSRMLS_CC)->object()); // if it does not implement the Countable interface, we rely on the default implementation if (countable) @@ -601,7 +576,7 @@ int ClassImpl::countElements(zval *object, long *count TSRMLS_DC) catch (Exception &exception) { // process the exception - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // unreachable return FAILURE; @@ -650,7 +625,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC) // does it implement the arrayaccess interface? - ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(cpp_object(object TSRMLS_CC)); + ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(ObjectImpl::find(object TSRMLS_CC)->object()); // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) @@ -664,7 +639,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC) catch (Exception &exception) { // process the exception (send it to user space) - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // unreachable return Value(nullptr).detach(); @@ -695,7 +670,7 @@ zval *ClassImpl::readDimension(zval *object, zval *offset, int type TSRMLS_DC) void ClassImpl::writeDimension(zval *object, zval *offset, zval *value TSRMLS_DC) { // does it implement the arrayaccess interface? - ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(cpp_object(object TSRMLS_CC)); + ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(ObjectImpl::find(object TSRMLS_CC)->object()); // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) @@ -709,7 +684,7 @@ void ClassImpl::writeDimension(zval *object, zval *offset, zval *value TSRMLS_DC catch (Exception &exception) { // process the exception (send it to user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } else @@ -737,7 +712,7 @@ void ClassImpl::writeDimension(zval *object, zval *offset, zval *value TSRMLS_DC int ClassImpl::hasDimension(zval *object, zval *member, int check_empty TSRMLS_DC) { // does it implement the arrayaccess interface? - ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(cpp_object(object TSRMLS_CC)); + ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(ObjectImpl::find(object TSRMLS_CC)->object()); // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) @@ -758,7 +733,7 @@ int ClassImpl::hasDimension(zval *object, zval *member, int check_empty TSRMLS_D catch (Exception &exception) { // process the exception (send it to user space) - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // unreachable return false; @@ -787,7 +762,7 @@ int ClassImpl::hasDimension(zval *object, zval *member, int check_empty TSRMLS_D void ClassImpl::unsetDimension(zval *object, zval *member TSRMLS_DC) { // does it implement the arrayaccess interface? - ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(cpp_object(object TSRMLS_CC)); + ArrayAccess *arrayaccess = dynamic_cast<ArrayAccess*>(ObjectImpl::find(object TSRMLS_CC)->object()); // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) @@ -801,7 +776,7 @@ void ClassImpl::unsetDimension(zval *object, zval *member TSRMLS_DC) catch (Exception &exception) { // process the exception (send it to user space) - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } else @@ -871,13 +846,13 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, const zend_lit // that is in most cases simply impossible. // retrieve the object and class - Base *base = cpp_object(object TSRMLS_CC); + Base *base = ObjectImpl::find(object TSRMLS_CC)->object(); // retrieve the class entry linked to this object auto *entry = zend_get_class_entry(object TSRMLS_CC); // we need the C++ class meta-information object - ClassImpl *impl = cpp_impl(entry); + ClassImpl *impl = self(entry); ClassBase *meta = impl->_base; // the default implementation throws an exception, so by catching @@ -918,7 +893,7 @@ zval *ClassImpl::readProperty(zval *object, zval *name, int type, const zend_lit { // user threw an exception in its magic method // implementation, send it to user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // unreachable return Value(nullptr).detach(); @@ -945,13 +920,13 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, const zend_ #endif { // retrieve the object and class - Base *base = cpp_object(object TSRMLS_CC); + Base *base = ObjectImpl::find(object TSRMLS_CC)->object(); // retrieve the class entry linked to this object auto *entry = zend_get_class_entry(object TSRMLS_CC); // we need the C++ class meta-information object - ClassImpl *impl = cpp_impl(entry); + ClassImpl *impl = self(entry); ClassBase *meta = impl->_base; // the default implementation throws an exception, if we catch that @@ -995,7 +970,7 @@ void ClassImpl::writeProperty(zval *object, zval *name, zval *value, const zend_ { // user threw an exception in its magic method // implementation, send it to user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } @@ -1030,13 +1005,13 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, const z try { // get the cpp object - Base *base = cpp_object(object TSRMLS_CC); + Base *base = ObjectImpl::find(object TSRMLS_CC)->object(); // retrieve the class entry linked to this object auto *entry = zend_get_class_entry(object TSRMLS_CC); // we need the C++ class meta-information object - ClassImpl *impl = cpp_impl(entry); + ClassImpl *impl = self(entry); ClassBase *meta = impl->_base; // convert the name to a Value object @@ -1076,7 +1051,7 @@ int ClassImpl::hasProperty(zval *object, zval *name, int has_set_exists, const z { // user threw an exception in its magic method // implementation, send it to user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // unreachable return false; @@ -1107,7 +1082,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *ke auto *entry = zend_get_class_entry(object TSRMLS_CC); // we need the C++ class meta-information object - ClassImpl *impl = cpp_impl(entry); + ClassImpl *impl = self(entry); // property name Value name(member); @@ -1116,7 +1091,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *ke auto iter = impl->_properties.find(name); // if the property does not exist, we forward to the __unset - if (iter == impl->_properties.end()) impl->_base->callUnset(cpp_object(object TSRMLS_CC), member); + if (iter == impl->_properties.end()) impl->_base->callUnset(ObjectImpl::find(object TSRMLS_CC)->object(), member); // callback properties cannot be unset zend_error(E_ERROR, "Property %s can not be unset", (const char *)name); @@ -1137,7 +1112,7 @@ void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *ke { // user threw an exception in its magic method // implementation, send it to user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } @@ -1150,17 +1125,17 @@ void ClassImpl::unsetProperty(zval *object, zval *member, const zend_literal *ke */ void ClassImpl::destructObject(zend_object *object, zend_object_handle handle TSRMLS_DC) { - // allocate memory for the object - MixedObject *obj = (MixedObject *)object; + // find object + ObjectImpl *obj = ObjectImpl::find(object); // get meta info - ClassImpl *impl = cpp_impl(object->ce); + ClassImpl *impl = self(object->ce); // prevent exceptions try { // call the destruct function - if (obj->cpp) impl->_base->callDestruct(obj->cpp); + if (obj->object()) impl->_base->callDestruct(obj->object()); } catch (const NotImplemented &exception) { @@ -1171,7 +1146,7 @@ void ClassImpl::destructObject(zend_object *object, zend_object_handle handle TS { // a regular Php::Exception was thrown by the extension, pass it on // to PHP user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); } } @@ -1183,13 +1158,10 @@ void ClassImpl::destructObject(zend_object *object, zend_object_handle handle TS void ClassImpl::freeObject(zend_object *object TSRMLS_DC) { // allocate memory for the object - MixedObject *obj = (MixedObject *)object; - - // deallocate the cpp object - if (obj->cpp) delete obj->cpp; + ObjectImpl *obj = ObjectImpl::find(object); - // pass on to the default destructor - zend_objects_free_object_storage(object TSRMLS_CC); + // no longer need it + obj->destruct(TSRMLS_C); } /** @@ -1202,7 +1174,7 @@ void ClassImpl::freeObject(zend_object *object TSRMLS_DC) zend_object_value ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC) { // we need the C++ class meta-information object - ClassImpl *impl = cpp_impl(entry); + ClassImpl *impl = self(entry); // create a new base C++ object auto *cpp = impl->_base->construct(); @@ -1216,11 +1188,11 @@ zend_object_value ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC) // set the handlers result.handlers = impl->objectHandlers(); - // store the object - cpp->store(entry TSRMLS_CC); - + // create the object in the zend engine + ObjectImpl *object = new ObjectImpl(entry, cpp TSRMLS_CC); + // store the object in the object cache - result.handle = cpp->handle(); + result.handle = object->handle(); // done return result; @@ -1240,13 +1212,13 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje if (by_ref) throw Php::Exception("Foreach by ref is not possible"); // retrieve the traversable object - Traversable *traversable = dynamic_cast<Traversable*>(cpp_object(object TSRMLS_CC)); + Traversable *traversable = dynamic_cast<Traversable*>(ObjectImpl::find(object TSRMLS_CC)->object()); // user may throw an exception in the getIterator() function try { // create an iterator - auto *iterator = traversable->getIterator(); + auto *iterator = new IteratorImpl(traversable->getIterator()); // return the implementation return iterator->implementation(); @@ -1255,7 +1227,7 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje { // user threw an exception in its method // implementation, send it to user space - exception.process(TSRMLS_C); + process(exception TSRMLS_CC); // unreachable return nullptr; @@ -1274,7 +1246,7 @@ zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *obje int ClassImpl::serialize(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC) { // get the serializable object - Serializable *serializable = dynamic_cast<Serializable*>(cpp_object(object TSRMLS_CC)); + Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(object TSRMLS_CC)->object()); // call the serialize method on the object auto value = serializable->serialize(); @@ -1304,7 +1276,7 @@ int ClassImpl::unserialize(zval **object, zend_class_entry *entry, const unsigne object_init_ex(*object, entry); // turn this into a serializale - Serializable *serializable = dynamic_cast<Serializable*>(cpp_object(*object TSRMLS_CC)); + Serializable *serializable = dynamic_cast<Serializable*>(ObjectImpl::find(*object TSRMLS_CC)->object()); // call the unserialize method on it serializable->unserialize((const char *)buffer, buf_len); diff --git a/src/classimpl.h b/zend/classimpl.h index cf28199..febdbca 100644 --- a/src/classimpl.h +++ b/zend/classimpl.h @@ -134,13 +134,6 @@ public: } /** - * Retrieve the extension's class object - * @param entry - * @return ClassBase - */ - static ClassBase *base(zend_class_entry *entry); - - /** * Initialize the class, given its name * * The module functions are registered on module startup, but classes are diff --git a/zend/extension.cpp b/zend/extension.cpp new file mode 100644 index 0000000..9685b32 --- /dev/null +++ b/zend/extension.cpp @@ -0,0 +1,103 @@ +/** + * Extension.cpp + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013, 2014 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Constructor that defines a number of functions right away + * @param name Extension name + * @param version Extension version string + */ +Extension::Extension(const char *name, const char *version) : + Namespace(""), _impl(new ExtensionImpl(this, name, version)) {} + +/** + * Destructor + */ +Extension::~Extension() +{ + // get rid of the implementation object + delete _impl; +} + +/** + * Register a function to be called when the PHP engine is ready + * @param callback + * @return Extension + */ +Extension &Extension::onStartup(const Callback &callback) +{ + // pass on to the implementation + _impl->onStartup(callback); + + // allow chaining + return *this; +} + +/** + * Register a function to be called when the PHP engine is going to stop + * @param callback + * @return Extension + */ +Extension &Extension::onShutdown(const Callback &callback) +{ + // pass on to the implementation + _impl->onShutdown(callback); + + // allow chaining + return *this; +} + +/** + * Register a callback that is called at the beginning of each pageview/request + * @param callback + */ +Extension &Extension::onRequest(const Callback &callback) +{ + // pass on to the implementation + _impl->onRequest(callback); + + // allow chaining + return *this; +} + +/** + * Register a callback that is called to cleanup things after a pageview/request + * @param callback + */ +Extension &Extension::onIdle(const Callback &callback) +{ + // pass on to the implementation + _impl->onIdle(callback); + + // allow chaining + return *this; +} + +/** + * Retrieve the module pointer + * + * This is the memory address that should be exported by the get_module() + * function. + * + * @return void* + */ +void *Extension::module() +{ + // pass on to the implementation + return _impl->module(); +} + +/** + * End of namespace + */ +} + diff --git a/src/extension.cpp b/zend/extensionimpl.cpp index 2782f3f..3534cdb 100644 --- a/src/extension.cpp +++ b/zend/extensionimpl.cpp @@ -51,8 +51,8 @@ static void init_globals(zend_phpcpp_globals *globals) {} * * @var map */ -static std::map<std::string,Extension*> name2extension; -static std::map<int,Extension*> number2extension; +static std::map<std::string,ExtensionImpl*> name2extension; +static std::map<int,ExtensionImpl*> number2extension; /** * Handler function that is used in combination with zend_hash_apply() @@ -61,9 +61,9 @@ static std::map<int,Extension*> number2extension; * an extension number. We loop through the list of all registered modules, and * for each module we check if we know the extension based on the name * - * @param _zend_module_entry + * @param zend_module_entry */ -static int match_module(_zend_module_entry *entry) +static int match_module(zend_module_entry *entry) { // check if there is an extension with this name auto iter = name2extension.find(entry->name); @@ -82,7 +82,7 @@ static int match_module(_zend_module_entry *entry) * @param tsrm_ls * @return Extension* */ -static Extension *find(int number TSRMLS_DC) +static ExtensionImpl *find(int number TSRMLS_DC) { // do we already have an extension with this number? auto iter = number2extension.find(number); @@ -106,14 +106,14 @@ static Extension *find(int number TSRMLS_DC) * @param tsrm_ls * @return int 0 on success */ -int Extension::onStartup(int type, int module_number TSRMLS_DC) +int ExtensionImpl::processStartup(int type, int module_number TSRMLS_DC) { // initialize and allocate the "global" variables ZEND_INIT_MODULE_GLOBALS(phpcpp, init_globals, NULL); // get the extension - Extension *extension = find(module_number TSRMLS_CC); + auto *extension = find(module_number TSRMLS_CC); // array contains ini settings static zend_ini_entry *ini_entries = new zend_ini_entry[ extension->_ini_entries.size()+1 ]; @@ -129,8 +129,8 @@ int Extension::onStartup(int type, int module_number TSRMLS_DC) // register REGISTER_INI_ENTRIES(); - // initialize namespace - extension->initialize("" TSRMLS_CC); + // initialize the extension + extension->initialize(TSRMLS_C); // is the callback registered? if (extension->_onStartup) extension->_onStartup(); @@ -146,10 +146,10 @@ int Extension::onStartup(int type, int module_number TSRMLS_DC) * @param tsrm_ls * @return int */ -int Extension::onShutdown(int type, int module_number TSRMLS_DC) +int ExtensionImpl::processShutdown(int type, int module_number TSRMLS_DC) { // get the extension - Extension *extension = find(module_number TSRMLS_CC); + auto *extension = find(module_number TSRMLS_CC); UNREGISTER_INI_ENTRIES(); @@ -171,10 +171,10 @@ int Extension::onShutdown(int type, int module_number TSRMLS_DC) * @param tsrm_ls * @return int 0 on success */ -int Extension::onRequest(int type, int module_number TSRMLS_DC) +int ExtensionImpl::processRequest(int type, int module_number TSRMLS_DC) { // get the extension - Extension *extension = find(module_number TSRMLS_CC); + auto *extension = find(module_number TSRMLS_CC); // is the callback registered? if (extension->_onRequest) extension->_onRequest(); @@ -190,10 +190,10 @@ int Extension::onRequest(int type, int module_number TSRMLS_DC) * @param tsrm_ls * @return int 0 on success */ -int Extension::onIdle(int type, int module_number TSRMLS_DC) +int ExtensionImpl::processIdle(int type, int module_number TSRMLS_DC) { // get the extension - Extension *extension = find(module_number TSRMLS_CC); + auto *extension = find(module_number TSRMLS_CC); // is the callback registered? if (extension->_onIdle) extension->_onIdle(); @@ -204,52 +204,45 @@ int Extension::onIdle(int type, int module_number TSRMLS_DC) /** * Constructor + * @param data Pointer to the extension object created by the extension programmer * @param name Name of the extension * @param version Version number - * @param start Request start callback - * @param stop Request stop callback */ -Extension::Extension(const char *name, const char *version) : Namespace("") +ExtensionImpl::ExtensionImpl(Extension *data, const char *name, const char *version) : ExtensionBase(data) { // keep extension pointer based on the name name2extension[name] = this; - // allocate memory (we allocate this on the heap so that the size of the - // entry does not have to be defined in the .h file. We pay a performance - // price for this, but we pay this price becuase the design goal of the - // PHP-C++ library is to have an interface that is as simple as possible - _entry = new _zend_module_entry; - // assign all members (apart from the globals) - _entry->size = sizeof(zend_module_entry); // size of the data - _entry->zend_api = ZEND_MODULE_API_NO; // api number - _entry->zend_debug = ZEND_DEBUG; // debug mode enabled? - _entry->zts = USING_ZTS; // is thread safety enabled? - _entry->ini_entry = NULL; // the php.ini record, will be filled by Zend engine - _entry->deps = NULL; // dependencies on other modules - _entry->name = name; // extension name - _entry->functions = NULL; // functions supported by this module (none for now) - _entry->module_startup_func = &Extension::onStartup; // startup function for the whole extension - _entry->module_shutdown_func = &Extension::onShutdown; // shutdown function for the whole extension - _entry->request_startup_func = &Extension::onRequest; // startup function per request - _entry->request_shutdown_func = &Extension::onIdle; // shutdown function per request - _entry->info_func = NULL; // information for retrieving info - _entry->version = version; // version string - _entry->globals_size = 0; // size of the global variables - _entry->globals_ctor = NULL; // constructor for global variables - _entry->globals_dtor = NULL; // destructor for global variables - _entry->post_deactivate_func = NULL; // unknown function - _entry->module_started = 0; // module is not yet started - _entry->type = 0; // temporary or persistent module, will be filled by Zend engine - _entry->handle = NULL; // dlopen() handle, will be filled by Zend engine - _entry->module_number = 0; // module number will be filled in by Zend engine - _entry->build_id = (char *)ZEND_MODULE_BUILD_ID; // check if extension and zend engine are compatible + _entry.size = sizeof(zend_module_entry); // size of the data + _entry.zend_api = ZEND_MODULE_API_NO; // api number + _entry.zend_debug = ZEND_DEBUG; // debug mode enabled? + _entry.zts = USING_ZTS; // is thread safety enabled? + _entry.ini_entry = NULL; // the php.ini record, will be filled by Zend engine + _entry.deps = NULL; // dependencies on other modules + _entry.name = name; // extension name + _entry.functions = NULL; // functions supported by this module (none for now) + _entry.module_startup_func = &ExtensionImpl::processStartup; // startup function for the whole extension + _entry.module_shutdown_func = &ExtensionImpl::processShutdown; // shutdown function for the whole extension + _entry.request_startup_func = &ExtensionImpl::processRequest; // startup function per request + _entry.request_shutdown_func = &ExtensionImpl::processIdle; // shutdown function per request + _entry.info_func = NULL; // information for retrieving info + _entry.version = version; // version string + _entry.globals_size = 0; // size of the global variables + _entry.globals_ctor = NULL; // constructor for global variables + _entry.globals_dtor = NULL; // destructor for global variables + _entry.post_deactivate_func = NULL; // unknown function + _entry.module_started = 0; // module is not yet started + _entry.type = 0; // temporary or persistent module, will be filled by Zend engine + _entry.handle = NULL; // dlopen() handle, will be filled by Zend engine + _entry.module_number = 0; // module number will be filled in by Zend engine + _entry.build_id = (char *)ZEND_MODULE_BUILD_ID; // check if extension and zend engine are compatible // things that only need to be initialized #ifdef ZTS - _entry->globals_id_ptr = NULL; + _entry.globals_id_ptr = NULL; #else - _entry->globals_ptr = NULL; + _entry.globals_ptr = NULL; #endif } @@ -257,29 +250,42 @@ Extension::Extension(const char *name, const char *version) : Namespace("") /** * Destructor */ -Extension::~Extension() +ExtensionImpl::~ExtensionImpl() { // deallocate functions - if (_entry->functions) delete[] _entry->functions; - - // deallocate entry - delete _entry; + if (_entry.functions) delete[] _entry.functions; } /** * Retrieve the module entry * @return zend_module_entry */ -zend_module_entry *Extension::module() +zend_module_entry *ExtensionImpl::module() { // check if functions we're already defined - if (_entry->functions || _functions.size() == 0) return _entry; + if (_entry.functions) return &_entry; + + // the number of functions + int count = _data->functions(); + + // skip if there are no functions + if (count == 0) return &_entry; // allocate memory for the functions - zend_function_entry *entries = new zend_function_entry[functions() + 1]; + zend_function_entry *entries = new zend_function_entry[count + 1]; + + // index being processed + int i = 0; - // initialize the entries - int count = Namespace::initialize("", entries); + // apply a function to each function + _data->apply([&i, entries](const std::string &prefix, Function &function) { + + // initialize the function + function.initialize(prefix, &entries[i]); + + // move on to the next iteration + i++; + }); // last entry should be set to all zeros zend_function_entry *last = &entries[count]; @@ -288,10 +294,24 @@ zend_module_entry *Extension::module() memset(last, 0, sizeof(zend_function_entry)); // store functions in entry object - _entry->functions = entries; + _entry.functions = entries; // return the entry - return _entry; + return &_entry; +} + +/** + * Initialize the extension after it was started + * @param tsrm_ls + */ +void ExtensionImpl::initialize(TSRMLS_D) +{ + // we need to register each class, find out all classes + _data->apply([TSRMLS_C](const std::string &prefix, ClassBase &c) { + + // forward to implementation class + c.implementation()->initialize(&c, prefix TSRMLS_CC); + }); } /** diff --git a/zend/extensionimpl.h b/zend/extensionimpl.h new file mode 100644 index 0000000..e58ce66 --- /dev/null +++ b/zend/extensionimpl.h @@ -0,0 +1,120 @@ +/** + * ExtensionImpl.h + * + * Extension implementation for the Zend engine. + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013, 2014 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class ExtensionImpl : public ExtensionBase +{ +protected: + /** + * The information that is passed to the Zend engine + * + * Although it would be slightly faster to not make this a pointer, this + * would require that client code also includes the PHP header files, which + * we try to prevent with the PHP-CPP library, so we allocate it dynamically. + * + * @var zend_module_entry + */ + zend_module_entry _entry; + +public: + /** + * Constructor + * @param data Extension object created by the extension programmer + * @param name Name of the extension + * @param version Version number + */ + ExtensionImpl(Extension *data, const char *name, const char *version); + + /** + * No copy'ing and no moving + */ + ExtensionImpl(const ExtensionImpl &extension) = delete; + ExtensionImpl(ExtensionImpl &&extension) = delete; + + /** + * Destructor + */ + virtual ~ExtensionImpl(); + + /** + * Retrieve the module entry + * + * This is the memory address that should be exported by the get_module() + * function. + * + * @return _zend_module_entry + */ + zend_module_entry *module(); + + /** + * Cast to a module entry + * @return _zend_module_entry* + */ + operator zend_module_entry * () + { + return module(); + } + +private: + /** + * Initialize the namespace after it was registered + * @param tsrm_ls + */ + void initialize(TSRMLS_D); + + /** + * Function that is called when the extension initializes + * @param type Module type + * @param number Module number + * @param tsrm_ls + * @return int 0 on success + */ + static int processStartup(int type, int module_number TSRMLS_DC); + + /** + * Function that is called when the extension is about to be stopped + * @param type Module type + * @param number Module number + * @param tsrm_ls + * @return int + */ + static int processShutdown(int type, int module_number TSRMLS_DC); + + /** + * Function that is called when a request starts + * @param type Module type + * @param number Module number + * @param tsrm_ls + * @return int 0 on success + */ + static int processRequest(int type, int module_number TSRMLS_DC); + + /** + * Function that is called when a request is ended + * @param type Module type + * @param number Module number + * @param tsrm_ls + * @return int 0 on success + */ + static int processIdle(int type, int module_number TSRMLS_DC); +}; + +/** + * End of namespace + */ +} + + diff --git a/src/floatmember.h b/zend/floatmember.h index 9b5d4f2..9b5d4f2 100644 --- a/src/floatmember.h +++ b/zend/floatmember.h diff --git a/src/function.h b/zend/function.h index fd60944..4c34ac7 100644 --- a/src/function.h +++ b/zend/function.h @@ -67,7 +67,7 @@ public: * @param prefix Active namespace prefix * @param entry Entry to be filled */ - void initialize(const std::string &prefix, struct _zend_function_entry *entry) + void initialize(const std::string &prefix, zend_function_entry *entry) { // if there is a namespace prefix, we should adjust the name if (prefix.size()) _ptr = HiddenPointer<Callable>(this, prefix+"\\"+(const char *)_ptr); diff --git a/src/global.cpp b/zend/global.cpp index 7eea8e7..7eea8e7 100644 --- a/src/global.cpp +++ b/zend/global.cpp diff --git a/src/globals.cpp b/zend/globals.cpp index 8132ef3..8132ef3 100644 --- a/src/globals.cpp +++ b/zend/globals.cpp diff --git a/src/hashiterator.h b/zend/hashiterator.h index d68ad4e..b1f409f 100644 --- a/src/hashiterator.h +++ b/zend/hashiterator.h @@ -20,7 +20,7 @@ namespace Php { /** * Class definition */ -class HashIterator : public IteratorImpl +class HashIterator : public ValueIteratorImpl { public: /** @@ -70,9 +70,9 @@ public: /** * Clone the object * @param tsrm_ls - * @return IteratorImpl + * @return ValueIteratorImpl */ - virtual IteratorImpl *clone() + virtual ValueIteratorImpl *clone() { // create a new instance return new HashIterator(*this); @@ -136,7 +136,7 @@ public: * @param that * @return bool */ - virtual bool equals(const IteratorImpl *that) const override + virtual bool equals(const ValueIteratorImpl *that) const override { // this always is a hash iterator HashIterator *other = (HashIterator *)that; diff --git a/src/hashmember.cpp b/zend/hashmember.cpp index f6f8483..f6f8483 100644 --- a/src/hashmember.cpp +++ b/zend/hashmember.cpp diff --git a/src/includes.h b/zend/includes.h index 45cbd2d..e7dece9 100644 --- a/src/includes.h +++ b/zend/includes.h @@ -76,12 +76,17 @@ #include "../include/namespace.h" #include "../include/extension.h" #include "../include/call.h" -#include "../include/init.h" /** - * Interface files for internal use only + * Common header files for internal use only */ -#include "mixedobject.h" +#include "../common/extensionbase.h" +#include "../common/streambuf.h" + +/** + * Specific zend implementation files for internal use only + */ +#include "init.h" #include "callable.h" #include "function.h" #include "method.h" @@ -95,12 +100,15 @@ #include "origexception.h" #include "notimplemented.h" #include "property.h" -#include "iteratorimpl.h" +#include "valueiteratorimpl.h" #include "hashiterator.h" #include "invaliditerator.h" #include "traverseiterator.h" -#include "streambuf.h" +#include "iteratorimpl.h" #include "classimpl.h" +#include "objectimpl.h" +#include "parametersimpl.h" +#include "extensionimpl.h" #ifndef ZVAL_COPY_VALUE #define ZVAL_COPY_VALUE(z, v) \ diff --git a/include/init.h b/zend/init.h index 8f914f5..8f914f5 100644 --- a/include/init.h +++ b/zend/init.h diff --git a/src/invaliditerator.h b/zend/invaliditerator.h index 7531d7d..388eca8 100644 --- a/src/invaliditerator.h +++ b/zend/invaliditerator.h @@ -16,15 +16,15 @@ namespace Php { /** * Class definition */ -class InvalidIterator : public IteratorImpl +class InvalidIterator : public ValueIteratorImpl { public: /** * Clone the object * @param tsrm_ls - * @return IteratorImpl + * @return ValueIteratorImpl */ - virtual IteratorImpl *clone() + virtual ValueIteratorImpl *clone() { // create a new instance return new InvalidIterator(*this); @@ -54,7 +54,7 @@ public: * @param that * @return bool */ - virtual bool equals(const IteratorImpl *that) const override + virtual bool equals(const ValueIteratorImpl *that) const override { // the other iterator is also an invalid-iterator, and all invalid // iterators are equal diff --git a/src/iterator.cpp b/zend/iteratorimpl.cpp index 0362fb1..2750ddb 100644 --- a/src/iterator.cpp +++ b/zend/iteratorimpl.cpp @@ -14,20 +14,24 @@ namespace Php { /** + * Helper method to get access to ourselves + * @param iter + * @return IteratorImpl + */ +static IteratorImpl *self(zend_object_iterator *iter) +{ + return (IteratorImpl *)iter->data; +} + +/** * Iterator destructor method * @param iter * @param tsrm_ls */ -void Iterator::destructor(zend_object_iterator *iter TSRMLS_DC) +void IteratorImpl::destructor(zend_object_iterator *iter TSRMLS_DC) { - // get the actual iterator - Iterator *iterator = (Iterator *)iter->data; - - // delete the iterator - delete iterator; - - // free memory for the meta object - efree(iter); + // delete the object + delete self(iter); } /** @@ -37,13 +41,10 @@ void Iterator::destructor(zend_object_iterator *iter TSRMLS_DC) * @param tsrm_ls * @return int */ -int Iterator::valid(zend_object_iterator *iter TSRMLS_DC) +int IteratorImpl::valid(zend_object_iterator *iter TSRMLS_DC) { - // get the actual iterator - Iterator *iterator = (Iterator *)iter->data; - // check if valid - return iterator->valid() ? SUCCESS : FAILURE; + return self(iter)->valid() ? SUCCESS : FAILURE; } /** @@ -52,10 +53,10 @@ int Iterator::valid(zend_object_iterator *iter TSRMLS_DC) * @param data * @param tsrm_ls */ -void Iterator::current(zend_object_iterator *iter, zval ***data TSRMLS_DC) +void IteratorImpl::current(zend_object_iterator *iter, zval ***data TSRMLS_DC) { // get the actual iterator - Iterator *iterator = (Iterator *)iter->data; + IteratorImpl *iterator = self(iter); // retrieve the value (and store it in a member so that it is not // destructed when the function returns) @@ -74,13 +75,10 @@ void Iterator::current(zend_object_iterator *iter, zval ***data TSRMLS_DC) * @param key * @param tsrm_ls */ -void Iterator::key(zend_object_iterator *iter, zval *key TSRMLS_DC) +void IteratorImpl::key(zend_object_iterator *iter, zval *key TSRMLS_DC) { - // get the actual iterator - Iterator *iterator = (Iterator *)iter->data; - // retrieve the key - Value retval(iterator->key()); + Value retval(self(iter)->key()); // detach the underlying zval zval *val = retval.detach(); @@ -98,13 +96,10 @@ void Iterator::key(zend_object_iterator *iter, zval *key TSRMLS_DC) * @param tsrm_ls * @return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG */ -int Iterator::key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) +int IteratorImpl::key(zend_object_iterator *iter, char **str_key, uint *str_key_len, ulong *int_key TSRMLS_DC) { - // get the actual iterator - Iterator *iterator = (Iterator *)iter->data; - // retrieve the key - Value retval(iterator->key()); + Value retval(self(iter)->key()); // is this a numeric string? if (retval.isString()) @@ -131,13 +126,10 @@ int Iterator::key(zend_object_iterator *iter, char **str_key, uint *str_key_len, * @param iter * @param tsrm_ls */ -void Iterator::next(zend_object_iterator *iter TSRMLS_DC) +void IteratorImpl::next(zend_object_iterator *iter TSRMLS_DC) { - // get the actual iterator - Iterator *iterator = (Iterator *)iter->data; - // call the next method - iterator->next(); + self(iter)->next(); } /** @@ -145,20 +137,17 @@ void Iterator::next(zend_object_iterator *iter TSRMLS_DC) * @param iter * @param tsrm_ls */ -void Iterator::rewind(zend_object_iterator *iter TSRMLS_DC) +void IteratorImpl::rewind(zend_object_iterator *iter TSRMLS_DC) { - // get the actual iterator - Iterator *iterator = (Iterator *)iter->data; - // call the rewind method - iterator->rewind(); + self(iter)->rewind(); } /** * Get access to all iterator functions * @return zend_object_iterator_funcs */ -zend_object_iterator_funcs *Iterator::functions() +zend_object_iterator_funcs *IteratorImpl::functions() { // static variable with all functions static zend_object_iterator_funcs funcs; @@ -170,12 +159,12 @@ zend_object_iterator_funcs *Iterator::functions() if (initialized) return &funcs; // set the members - funcs.dtor = &Iterator::destructor; - funcs.valid = &Iterator::valid; - funcs.get_current_data = &Iterator::current; - funcs.get_current_key = &Iterator::key; - funcs.move_forward = &Iterator::next; - funcs.rewind = &Iterator::rewind; + funcs.dtor = &IteratorImpl::destructor; + funcs.valid = &IteratorImpl::valid; + funcs.get_current_data = &IteratorImpl::current; + funcs.get_current_key = &IteratorImpl::key; + funcs.move_forward = &IteratorImpl::next; + funcs.rewind = &IteratorImpl::rewind; // invalidate is not yet supported funcs.invalidate_current = nullptr; @@ -188,24 +177,6 @@ zend_object_iterator_funcs *Iterator::functions() } /** - * Internal method that returns the implementation object - * @return zend_object_iterator - */ -struct _zend_object_iterator *Iterator::implementation() -{ - // create an iterator - zend_object_iterator *iterator = (zend_object_iterator *)emalloc(sizeof(zend_object_iterator)); - - // initialize all properties - iterator->data = this; - iterator->index = 0; - iterator->funcs = functions(); - - // done - return iterator; -} - -/** * End namespace */ } diff --git a/zend/iteratorimpl.h b/zend/iteratorimpl.h new file mode 100644 index 0000000..0a815e2 --- /dev/null +++ b/zend/iteratorimpl.h @@ -0,0 +1,190 @@ +/** + * Iterator.h + * + * Base class for iterators. Extension writers that want to create traversable + * classes, should override the Php::Traversable base class. This base class + * forces you to implement a getIterator() method that returns an instance of + * a Php::Iterator class. + * + * In this file you find the signature of the Php::Iterator class. It mostly has + * pure virtual methods, which means that you should create a derived class + * that implements all these methods. + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class IteratorImpl +{ +private: + /** + * Unique pointer to the iterator that is returned by the extension + * @var std::unique_ptr + */ + std::unique_ptr<Iterator> _iterator; + + /** + * The current() method that is called by the Zend engine wants a + * pointer-to-pointer-to-a-zval. Because of this, we have to keep the + * current value in memory after the current() method returns because + * the pointer would otherwise fall out of scope. This is (once again) + * odd behavior of the Zend engine, but we'll have to live with that + * @var Value + */ + Value _current; + + /** + * The object iterator as is needed by the Zend engine + * @var zend_object_iterator + */ + zend_object_iterator _impl; + + /** + * Get access to all iterator functions + * @return zend_object_iterator_funcs + */ + static zend_object_iterator_funcs *functions(); + + /** + * Is the iterator on a valid position + * @return bool + */ + bool valid() + { + return _iterator->valid(); + } + + /** + * The value at the current position + * @return Value + */ + Value current() + { + return _iterator->current(); + } + + /** + * The key at the current position + * @return Value + */ + Value key() + { + return _iterator->key(); + } + + /** + * Move to the next position + */ + void next() + { + return _iterator->next(); + } + + /** + * Rewind the iterator to the front position + */ + void rewind() + { + return _iterator->rewind(); + } + + /** + * Iterator destructor method + * @param iter + * @param tsrm_ls + */ + static void destructor(zend_object_iterator *iter TSRMLS_DC); + + /** + * Iterator valid function + * Returns FAILURE or SUCCESS + * @param iter + * @param tsrm_ls + * @return int + */ + static int valid(zend_object_iterator *iter TSRMLS_DC); + + /** + * Fetch the current item + * @param iter + * @param data + * @param tsrm_ls + */ + static void current(zend_object_iterator *iter, zval ***data TSRMLS_DC); + + /** + * Fetch the key for the current element (optional, may be NULL). The key + * should be written into the provided zval* using the ZVAL_* macros. If + * this handler is not provided auto-incrementing integer keys will be + * used. + * @param iter + * @param data + * @param tsrm_ls + */ + static void key(zend_object_iterator *iter, zval *data TSRMLS_DC); + + /** + * Function to retrieve the current key, php 5.3 style + * @param iter + * @param str_key + * @param str_key_len + * @param int_key + * @param tsrm_ls + * @return HASH_KEY_IS_STRING or HASH_KEY_IS_LONG + */ + static int key(zend_object_iterator *iter, char **str_key, unsigned int *str_key_len, unsigned long *int_key TSRMLS_DC); + + /** + * Step forwards to the next element + * @param iter + * @param tsrm_ls + */ + static void next(zend_object_iterator *iter TSRMLS_DC); + + /** + * Rewind the iterator back to the start + * @param iter + * @param tsrm_ls + */ + static void rewind(zend_object_iterator *iter TSRMLS_DC); + +public: + /** + * Constructor + * @param iterator The iterator that is implemented by the extension + */ + IteratorImpl(Iterator *iterator) : _iterator(iterator) + { + // initialize impl object + _impl.data = this; + _impl.index = 0; + _impl.funcs = functions(); + } + + /** + * Destructor + */ + virtual ~IteratorImpl() {} + + /** + * Internal method that returns the implementation object + * @return zend_object_iterator + */ + zend_object_iterator *implementation() + { + return &_impl; + } +}; + +/** + * End namespace + */ +} diff --git a/src/member.h b/zend/member.h index 7aa01d8..7aa01d8 100644 --- a/src/member.h +++ b/zend/member.h diff --git a/src/members.cpp b/zend/members.cpp index 1965807..1965807 100644 --- a/src/members.cpp +++ b/zend/members.cpp diff --git a/src/method.h b/zend/method.h index dd18a9a..dd18a9a 100644 --- a/src/method.h +++ b/zend/method.h diff --git a/src/namespace.cpp b/zend/namespace.cpp index d462684..bea31a1 100644 --- a/src/namespace.cpp +++ b/zend/namespace.cpp @@ -78,58 +78,55 @@ Namespace &Namespace::add(const char *name, const native_callback_3 &function, c } /** - * Initialize all functions in this namespace - * @param parent Namespace prefix of the parent - * @param entries The array to be filled - * @return int Number of functions that were initialized + * Apply a callback to each registered function + * + * The callback will be called with the name of the namespace, and + * a reference to the registered function. + * + * @param callback */ -size_t Namespace::initialize(const std::string &parent, struct _zend_function_entry entries[]) +void Namespace::apply(const std::function<void(const std::string &ns, Function &func)> &callback) { - // keep iterator counter - int count = 0; - - // the namespace to use - std::string prefix = parent.size() ? parent + "\\" + _name : _name; - - // loop through the functions - for (auto &function : _functions) - { - // retrieve entry - zend_function_entry *entry = &entries[count++]; - - // let the function fill the entry - function->initialize(prefix, entry); - } + // loop through the functions, and apply the callback + for (auto &function : _functions) callback(_name, *function); - // loop through the namespace - for (auto &ns : _namespaces) - { - // let the namespace initialize - count += ns->initialize(prefix, &entries[count]); - } - - // done - return count; + // loop through the other namespaces + for (auto &ns : _namespaces) ns->apply([this, callback](const std::string &ns, Function &func) { + + // if this is the root namespace, we don't have to change the prefix + if (_name.size() == 0) return callback(ns, func); + + // construct a new prefix + // @todo this could be slightly inefficient + return callback(_name + "\\" + ns, func); + }); } /** - * Initialize the namespace after it was registered - * @param parent Parent namespace - * @param tsrm_ls + * Apply a callback to each registered class + * + * The callback will be called with the name of the namespace, and + * a reference to the registered class. + * + * @param callback */ -void Namespace::initialize(const std::string &parent TSRMLS_DC) +void Namespace::apply(const std::function<void(const std::string &ns, ClassBase &clss)> &callback) { - // the namespace to use - std::string prefix = parent.size() ? parent + "\\" + _name : _name; + // loop through the classes, and apply the callback + for (auto &c : _classes) callback(_name, *c); - // loop through the classes in this namespace - for (auto &c : _classes) c->implementation()->initialize(c.get(), prefix TSRMLS_CC); - - // and loop through the other namespaces - for (auto &n : _namespaces) n->initialize(prefix TSRMLS_CC); + // loop through the other namespaces + for (auto &ns : _namespaces) ns->apply([this, callback](const std::string &ns, ClassBase &clss) { + + // if this is the root namespace, we don't have to change the prefix + if (_name.size() == 0) return callback(ns, clss); + + // construct a new prefix + // @todo this could be slightly inefficient + return callback(_name + "\\" + ns, clss); + }); } - /** * End namespace */ diff --git a/src/notimplemented.h b/zend/notimplemented.h index ee99254..ee99254 100644 --- a/src/notimplemented.h +++ b/zend/notimplemented.h diff --git a/src/nullmember.h b/zend/nullmember.h index e035897..e035897 100644 --- a/src/nullmember.h +++ b/zend/nullmember.h diff --git a/src/numericmember.h b/zend/numericmember.h index 0a040d3..0a040d3 100644 --- a/src/numericmember.h +++ b/zend/numericmember.h diff --git a/src/object.cpp b/zend/object.cpp index 05449bc..d7fc158 100644 --- a/src/object.cpp +++ b/zend/object.cpp @@ -20,7 +20,7 @@ namespace Php { Object::Object(const char *name, Base *base) { // does the object already have a handle? - if (base->handle()) + if (base->implementation()) { // the object is already instantiated, we can assign it the this object operator=(Value(base)); @@ -36,8 +36,9 @@ Object::Object(const char *name, Base *base) auto *entry = zend_fetch_class(name, strlen(name), 0 TSRMLS_CC); if (!entry) throw Php::Exception(std::string("Unknown class name ") + name); - // store the object in the php object cache (this will give the object a handle) - base->store(entry TSRMLS_CC); + // construct an implementation (this will also set the implementation + // member in the base object) + new ObjectImpl(entry, base TSRMLS_CC); // now we can store it operator=(Value(base)); diff --git a/zend/objectimpl.h b/zend/objectimpl.h new file mode 100644 index 0000000..f66a6ab --- /dev/null +++ b/zend/objectimpl.h @@ -0,0 +1,207 @@ +/** + * ObjectImpl.h + * + * Implementation class for Base objects that allow the objects to be stored + * in the Zend engine + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class ObjectImpl +{ +private: + /** + * Structure with a first element which is a zend_object, so that + * it can be casted to a zend_object + * @var MixedObject + */ + struct MixedObject + { + /** + * The actual object is the first member, so that casting + * the MixedObject to a zend_object will also result in a valid pointer + * @var zend_object + */ + zend_object php; + + /** + * Pointer to ourselves + * @var ObjectImpl + */ + ObjectImpl *self; + + + } *_mixed; + + /** + * Pointer to the C++ implementation + * @var Base + */ + Base *_object; + + /** + * The object handle in the Zend engine + * @var int + */ + int _handle; + +public: + /** + * Constructor + * + * This will create a new object in the Zend engine. + * + * @param entry Zend class entry + * @param base C++ object that already exists + * @param tsrm_ls Optional threading data + */ + ObjectImpl(zend_class_entry *entry, Base *base TSRMLS_DC) + { + // allocate a mixed object (for some reason this does not have to deallocated) + _mixed = (MixedObject *)emalloc(sizeof(MixedObject)); + + // copy properties to the mixed object + _mixed->php.ce = entry; + _mixed->self = this; + + // store the c++ object + _object = base; + + // initialize the object + zend_object_std_init(&_mixed->php, entry TSRMLS_CC); + +#if PHP_VERSION_ID < 50399 + + // tmp variable + zval *tmp; + + // initialize the properties, php 5.3 way + zend_hash_copy(_mixed->php.properties, &entry->default_properties, (copy_ctor_func_t) zval_property_ctor, &tmp, sizeof(zval*)); + +#else + + // version higher than 5.3 have an easier way to initialize + object_properties_init(&_mixed->php, entry); + +#endif + +#ifdef ZTS + + // when in thread safety mode, the destruct method and free method have + // an extra parameter holding thread information + using DestructType = void(zend_object*,unsigned int,void***); + using FreeType = void(zend_object*,void***); + +#else + + // not in thread mode: no special parameter for the tsrm_ls variable + using DestructType = void(zend_object*,unsigned int); + using FreeType = void(zend_object*); + +#endif + + // store the two destruct methods in temporary vars + DestructType *destructMethod = &ClassImpl::destructObject; + FreeType *freeMethod = &ClassImpl::freeObject; + + // the destructor and clone handlers are set to NULL. I dont know why, but they do not + // seem to be necessary... + _handle = zend_objects_store_put(php(), (zend_objects_store_dtor_t)destructMethod, (zend_objects_free_object_storage_t)freeMethod, NULL TSRMLS_CC); + + // the object may remember that we are its implementation object + base->_impl = this; + } + + /** + * Destructor + */ + virtual ~ObjectImpl() + { + // deallocate the cpp object + if (_object) delete _object; + } + + /** + * Destruct the object + * @param tsrm_ls + */ + void destruct(TSRMLS_D) + { + // pass on to the default destructor + zend_objects_free_object_storage(php() TSRMLS_CC); + + // destruct the object + delete this; + } + + /** + * Find the object based on a zval + * @param val Zval object + * @param tsrm_ls Optional pointer to thread info + * @return ObjectImpl + */ + static ObjectImpl *find(zval *val TSRMLS_DC) + { + // retrieve the old object, which we are going to copy + MixedObject *object = (MixedObject *)zend_object_store_get_object(val TSRMLS_CC); + + // done + return object->self; + } + + /** + * Find the object based on a zend_object + * @param object Zend object pointer + * @return ObjectImpl + */ + static ObjectImpl *find(const zend_object *object) + { + // retrieve the old object, which we are going to copy + const MixedObject *mixed = (MixedObject *)object; + + // done + return mixed->self; + } + + /** + * Retrieve the base class of the original C++ object + * @return Base + */ + Base *object() + { + return _object; + } + + /** + * Pointer to the PHP object + * @return zend_object + */ + zend_object *php() + { + return &_mixed->php; + } + + /** + * Retrieve the handle object + * @return int + */ + int handle() const + { + return _handle; + } +}; + +/** + * End of namespace + */ +} + diff --git a/zend/origexception.h b/zend/origexception.h new file mode 100644 index 0000000..775e412 --- /dev/null +++ b/zend/origexception.h @@ -0,0 +1,145 @@ +/** + * 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 <emiel.bruijntjes@copernica.com> + * @copyright 2013 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class OrigException : public Value, public Exception +{ +private: + /** + * Is this a an exception that was caught by extension C++ code. + * + * When the object is initially created, we assume that it will be caught + * by C++ code. If it later turns out that the PHP-CPP can catch this + * exception after the extension C++ code ran, the variable is set back + * to false. + * + * @var bool + */ + bool _handled = true; + +#ifdef ZTS + /** + * When we run in multi-thread mode, we store the thread handle + * @var void*** + */ + TSRMLS_D; +#endif + +public: + /** + * Constructor + * @param val + */ + OrigException(zval *val TSRMLS_DC) : + Value(val), Exception("OrigException") + { +#ifdef ZTS + // copy tsrm_ls + this->TSRMLS_C = TSRMLS_C; +#endif + } + + /** + * Copy constructor + * @param exception + */ + OrigException(const OrigException &exception) : + Value(exception), Exception("OrigException"), _handled(exception._handled) + { +#ifdef ZTS + // copy tsrm_ls + TSRMLS_C = exception.TSRMLS_C; +#endif + } + + /** + * Move constructor + * @param exception + */ + OrigException(OrigException &&exception) : + Value(std::move(exception)), Exception("OrigException"), _handled(exception._handled) + { + // set other exception to handled so that it wont do anything on destruction + exception._handled = true; + +#ifdef ZTS + // copy tsrm_ls + TSRMLS_C = exception.TSRMLS_C; +#endif + } + + /** + * 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(TSRMLS_C); + } + + /** + * 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; + } +}; + +/** + * Global function to process an exception + * @param exception + * @param tsrm_ls + */ +inline void process(Exception &exception TSRMLS_DC) +{ + // is this a native exception? + if (exception.native()) + { + // the exception is native, call the zend throw method + zend_throw_exception(zend_exception_get_default(TSRMLS_C), (char *)exception.what(), 0 TSRMLS_CC); + } + else + { + // this is not a native exception, so it was originally thrown by a + // php script, and then not caught by the c++ of the extensiont, we are + // going to tell to the exception that it is still active + OrigException &orig = static_cast<OrigException&>(exception); + + // reactive the exception + orig.reactivate(); + } +} + +/** + * End of namespace + */ +} diff --git a/zend/parametersimpl.h b/zend/parametersimpl.h new file mode 100644 index 0000000..fd14238 --- /dev/null +++ b/zend/parametersimpl.h @@ -0,0 +1,53 @@ +/** + * ParametersImpl.h + * + * Extended parameters class that can be instantiated + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2013 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class ParametersImpl : public Parameters +{ +public: + /** + * Constructor + * @param this_ptr Pointer to the object + * @param argc Number of arguments + * @param tsrm_ls + */ + ParametersImpl(zval *this_ptr, int argc TSRMLS_DC) : Parameters(this_ptr ? ObjectImpl::find(this_ptr TSRMLS_CC)->object() : nullptr) + { + // reserve plenty of space + reserve(argc); + + // loop through the arguments + for (int i=0; i<argc; i++) + { + // get the argument + zval **arg = (zval **) (zend_vm_stack_top(TSRMLS_C) - 1 - (argc-i)); + + // append value + push_back(Value(*arg)); + } + } + + /** + * Destructor + */ + virtual ~ParametersImpl() {} +}; + +/** + * End of namespace + */ +} + diff --git a/src/property.h b/zend/property.h index f0fd46f..f0fd46f 100644 --- a/src/property.h +++ b/zend/property.h diff --git a/zend/streambuf.cpp b/zend/streambuf.cpp new file mode 100644 index 0000000..86e5f03 --- /dev/null +++ b/zend/streambuf.cpp @@ -0,0 +1,55 @@ +/** + * StreamBuf.cpp + * + * Implementation file for the StreamBuf class + * + * @see http://www.mr-edd.co.uk/blog/beginners_guide_streambuf + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * Called when the internal buffer should be synchronized + * @return int + */ +int StreamBuf::sync() +{ + // current buffer size + size_t size = pptr() - pbase(); + + // is this the error stream or the regular output stream? + if (_error) + { + // write to error (the zend_error() method is a varargs function, + // which means that we have to include a printf() like format as first + // parameter. We can not specify pbase() directly, because (1) it is + // not null terminated and (2) it could contain % signs and allow all + // sorts of buffer overflows. + zend_error(_error, "%.*s", (int)size, pbase()); + + } + else + { + // write to zend + zend_write(pbase(), size); + } + + // reset the buffer + pbump(-size); + + // done + return 0; +} + +/** + * End namespace + */ +} +
\ No newline at end of file diff --git a/src/streams.cpp b/zend/streams.cpp index ba6d916..ba6d916 100644 --- a/src/streams.cpp +++ b/zend/streams.cpp diff --git a/src/stringmember.h b/zend/stringmember.h index e58cd3d..e58cd3d 100644 --- a/src/stringmember.h +++ b/zend/stringmember.h diff --git a/src/super.cpp b/zend/super.cpp index 506d4a5..506d4a5 100644 --- a/src/super.cpp +++ b/zend/super.cpp diff --git a/src/traverseiterator.h b/zend/traverseiterator.h index 84c50f0..16f1ce7 100644 --- a/src/traverseiterator.h +++ b/zend/traverseiterator.h @@ -17,7 +17,7 @@ namespace Php { /** * Class definition */ -class TraverseIterator : public IteratorImpl +class TraverseIterator : public ValueIteratorImpl { public: /** @@ -73,9 +73,9 @@ public: /** * Clone the object * @param tsrm_ls - * @return IteratorImpl* + * @return ValueIteratorImpl* */ - virtual IteratorImpl *clone() override + virtual ValueIteratorImpl *clone() override { // we need the tsrm_ls variable TSRMLS_FETCH(); @@ -119,7 +119,7 @@ public: * @param that * @return bool */ - virtual bool equals(const IteratorImpl *that) const override + virtual bool equals(const ValueIteratorImpl *that) const override { // of course if the objects are identical if (this == that) return true; diff --git a/src/value.cpp b/zend/value.cpp index 6684bd0..2ca1585 100644 --- a/src/value.cpp +++ b/zend/value.cpp @@ -177,22 +177,22 @@ 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 - int handle = object->handle(); + // space, and no handle does yet exist, find the implementation object + auto *impl = object->implementation(); // do we have a handle? - if (!handle) throw Php::Exception("Assigning an unassigned object to a variable"); + if (!impl) throw Php::Exception("Assigning an unassigned object to a variable"); // make a regular zval, and set it to an object MAKE_STD_ZVAL(_val); Z_TYPE_P(_val) = IS_OBJECT; - Z_OBJ_HANDLE_P(_val) = handle; + Z_OBJ_HANDLE_P(_val) = impl->handle(); // we need the tsrm_ls variable TSRMLS_FETCH(); // we have to lookup the object in the object-table - zend_object_store_bucket *obj_bucket = &EG(objects_store).object_buckets[handle]; + zend_object_store_bucket *obj_bucket = &EG(objects_store).object_buckets[impl->handle()]; // 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; @@ -1257,7 +1257,7 @@ static Value do_exec(zval **object, zval *method, int argc, zval ***params) { // was an exception thrown inside the function? In that case we throw a C++ new exception // to give the C++ code the chance to catch it - if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception)); + if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC); // no (additional) exception was thrown return retval ? Value(retval) : nullptr; @@ -1890,11 +1890,7 @@ Base *Value::implementation() const TSRMLS_FETCH(); // retrieve the mixed object that contains the base - MixedObject *object = (MixedObject *)zend_object_store_get_object(_val TSRMLS_CC); - if (!object) return nullptr; - - // retrieve the associated C++ class - return object->cpp; + return ObjectImpl::find(_val TSRMLS_CC)->object(); } /** diff --git a/src/valueiterator.cpp b/zend/valueiterator.cpp index 65c687c..65c687c 100644 --- a/src/valueiterator.cpp +++ b/zend/valueiterator.cpp diff --git a/src/iteratorimpl.h b/zend/valueiteratorimpl.h index babf9f0..82c888d 100644 --- a/src/iteratorimpl.h +++ b/zend/valueiteratorimpl.h @@ -1,9 +1,9 @@ /** - * IteratorImpl.h + * ValueIteratorImpl.h * - * Interface that describes what an implementation of an iterator should + * Interface that describes what an implementation of a value iterator should * look like. This is an internal class that extension developers do not - * need. + * need. It is used internally inside the ValueIterator class. * * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> * @copyright 2014 Copernica BV @@ -17,25 +17,25 @@ namespace Php { /** * Class definition */ -class IteratorImpl +class ValueIteratorImpl { public: /** * Constructor */ - IteratorImpl() {} + ValueIteratorImpl() {} /** * Destructor */ - virtual ~IteratorImpl() {} + virtual ~ValueIteratorImpl() {} /** * Clone the object * @param tsrm_ls - * @return IteratorImpl* + * @return ValueIteratorImpl* */ - virtual IteratorImpl *clone() = 0; + virtual ValueIteratorImpl *clone() = 0; /** * Increment position (pre-increment) @@ -55,7 +55,7 @@ public: * @param that * @return bool */ - virtual bool equals(const IteratorImpl *that) const = 0; + virtual bool equals(const ValueIteratorImpl *that) const = 0; /** * Derefecence, this returns a std::pair with the current key and value |