diff options
author | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2015-01-12 18:40:12 +0100 |
---|---|---|
committer | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2015-01-12 18:40:12 +0100 |
commit | a3007b9915a0ca3eec024b714cecc609e6356e17 (patch) | |
tree | e643f7ae433dbf12e6e1828950d694b855e4ec0a | |
parent | 74388e2735e806837cd31052c513451ec3942c0a (diff) |
fixed compiling in ZTS environments (reported in issue #57)
-rw-r--r-- | include/opcodes.h | 69 | ||||
-rw-r--r-- | include/script.h | 19 | ||||
-rw-r--r-- | phpcpp.h | 1 | ||||
-rw-r--r-- | zend/compileroptions.h | 15 | ||||
-rw-r--r-- | zend/file.cpp | 16 | ||||
-rw-r--r-- | zend/includes.h | 3 | ||||
-rw-r--r-- | zend/opcodes.cpp | 131 | ||||
-rw-r--r-- | zend/opcodes.h | 132 | ||||
-rw-r--r-- | zend/script.cpp | 49 |
9 files changed, 212 insertions, 223 deletions
diff --git a/include/opcodes.h b/include/opcodes.h deleted file mode 100644 index 5a40d05..0000000 --- a/include/opcodes.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Opcodes.h - * - * Class represents a set of opcodes of a PHP script that can be executed. This - * is an internal file that you normally do not have to instantiate yourself. - * Better use the Php::Script of Php::File classes. - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2014 Copernica BV - */ - -/** - * Forward declarations - */ -struct _zend_op_array; - -/** - * Namespace - */ -namespace Php { - -/** - * Class definition - */ -class Opcodes -{ -public: - /** - * Constructor - * @param opcodes - */ - Opcodes(struct _zend_op_array *opcodes) : _opcodes(opcodes) - { - // no other initialisation is necessary - } - - /** - * Destructor - */ - virtual ~Opcodes(); - - /** - * Are the opcodes valid? - * @return bool - */ - bool valid() const - { - return _opcodes != nullptr; - } - - /** - * Execute the opcodes - * @return Value - */ - Value execute() const; - -private: - /** - * The opcodes - * @var zend_op_array - */ - struct _zend_op_array *_opcodes; -}; - -/** - * End of namespace - */ -} - diff --git a/include/script.h b/include/script.h index a9c2921..af68a59 100644 --- a/include/script.h +++ b/include/script.h @@ -21,6 +21,11 @@ struct _zend_op_array; namespace Php { /** + * Forward declarations + */ +class Opcodes; + +/** * Class definition */ class Script @@ -62,33 +67,27 @@ public: /** * Destructor */ - virtual ~Script() {} + virtual ~Script(); /** * Is the script a valid PHP script without syntax errors? * @return bool */ - bool valid() const - { - return _opcodes.valid(); - } + bool valid() const; /** * Execute the script * The return value of the script is returned * @return Value */ - Value execute() const - { - return _opcodes.execute(); - } + Value execute() const; private: /** * The opcodes * @var Opcodes */ - Opcodes _opcodes; + Opcodes *_opcodes; /** * Helper function to compile the source code @@ -61,7 +61,6 @@ #include <phpcpp/namespace.h> #include <phpcpp/extension.h> #include <phpcpp/call.h> -#include <phpcpp/opcodes.h> #include <phpcpp/script.h> #include <phpcpp/file.h> diff --git a/zend/compileroptions.h b/zend/compileroptions.h index 798cb59..74ba053 100644 --- a/zend/compileroptions.h +++ b/zend/compileroptions.h @@ -25,19 +25,32 @@ private: * @var int */ zend_uint _original; + +#ifdef ZTS + /** + * When in thread safety mode, we also keep track of the TSRM_LS var + * @var void*** + */ + void ***tsrm_ls; +#endif public: /** * Constructor * @param options */ - CompilerOptions(zend_uint options) + CompilerOptions(zend_uint options TSRMLS_DC) { // remember the old compiler options before we set temporary compile options _original = CG(compiler_options); // we're going to evaluate only once CG(compiler_options) = options; + +#ifdef ZTS + // copy tsrm_ls param + this->tsrm_ls = tsrm_ls; +#endif } /** diff --git a/zend/file.cpp b/zend/file.cpp index 43c2d7a..2b7e77f 100644 --- a/zend/file.cpp +++ b/zend/file.cpp @@ -69,6 +69,9 @@ bool File::compile() // we are going to open the file zend_file_handle fileHandle; + // we need the tsrm_ls variable (@todo would it be better if this was a member?) + TSRMLS_FETCH(); + // open the file if (zend_stream_open(_path, &fileHandle TSRMLS_CC) == FAILURE) return false; @@ -76,13 +79,10 @@ bool File::compile() if (!fileHandle.opened_path) fileHandle.opened_path = estrdup(_path); // we need temporary compiler options - CompilerOptions options(ZEND_COMPILE_DEFAULT); + CompilerOptions options(ZEND_COMPILE_DEFAULT TSRMLS_CC); - // we need the tsrm_ls variable - TSRMLS_FETCH(); - // create the opcodes - _opcodes = new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE TSRMLS_CC)); + _opcodes = new Opcodes(zend_compile_file(&fileHandle, ZEND_INCLUDE TSRMLS_CC) TSRMLS_CC); // close the file handle zend_destroy_file_handle(&fileHandle TSRMLS_CC); @@ -130,6 +130,9 @@ Value File::execute() // try compiling the file if (!compile()) return nullptr; + // we need the tsrm_ls variable (@todo would it be better if this was a member?) + TSRMLS_FETCH(); + // add the entry to the list of included files zend_hash_add_empty_element(&EG(included_files), _path, ::strlen(_path) + 1); @@ -145,6 +148,9 @@ Value File::once() { // skip if the path is invalid if (!_path) return nullptr; + + // we need the tsrm_ls variable (@todo would it be better if this was a member?) + TSRMLS_FETCH(); // check if this file was already included if (zend_hash_exists(&EG(included_files), _path, ::strlen(_path) + 1)) return nullptr; diff --git a/zend/includes.h b/zend/includes.h index d6f2062..0b6a138 100644 --- a/zend/includes.h +++ b/zend/includes.h @@ -80,7 +80,6 @@ #include "../include/namespace.h" #include "../include/extension.h" #include "../include/call.h" -#include "../include/opcodes.h" #include "../include/script.h" #include "../include/file.h" @@ -117,6 +116,8 @@ #include "parametersimpl.h" #include "extensionimpl.h" #include "compileroptions.h" +#include "executestate.h" +#include "opcodes.h" #ifndef ZVAL_COPY_VALUE #define ZVAL_COPY_VALUE(z, v) \ diff --git a/zend/opcodes.cpp b/zend/opcodes.cpp deleted file mode 100644 index 9e4525a..0000000 --- a/zend/opcodes.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Opcodes.cpp - * - * Implementation file for the opcodes class - * - * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> - * @copyright 2014 Copernica BV - */ - -/** - * Dependencies - */ -#include "includes.h" - -/** - * Set up namespace - */ -namespace Php { - -/** - * Helper class to store and restore the current opcode state - * - * When we're going to execute a set of instructions, we need to store the - * current state of the Zend engine. After the instructions have been processed, - * we can switch back to the original instructions - */ -class ExecuteState -{ -private: - /** - * All the original settings - */ - zend_op_array *_active_op_array; - zval **_return_value_ptr_ptr; - zend_op **_opline_ptr; - int _interactive; - -public: - /** - * Constructor - */ - ExecuteState() - { - // store all the original stuff - _active_op_array = EG(active_op_array); - _return_value_ptr_ptr = EG(return_value_ptr_ptr); - _opline_ptr = EG(opline_ptr); - _interactive = CG(interactive); - } - - /** - * Destructor - */ - virtual ~ExecuteState() - { - // restore all settings - CG(interactive) = _interactive; - EG(no_extensions) = 0; - EG(opline_ptr) = _opline_ptr; - EG(active_op_array) = _active_op_array; - EG(return_value_ptr_ptr) = _return_value_ptr_ptr; - } -}; - -/** - * Destructor - */ -Opcodes::~Opcodes() -{ - // leap out if opcodes were not valid - if (!_opcodes) return; - - // clean up opcodes - destroy_op_array(_opcodes TSRMLS_CC); - efree(_opcodes); -} - -/** - * Execute the opcodes - * @return Value - */ -Value Opcodes::execute() const -{ - // if the script could not be compiled, we return null - if (!_opcodes) return nullptr; - - // pointer that is going to hold the return value of the script - zval *retval_ptr = nullptr; - - // the zend engine is probably already busy processing opcodes, so we store - // the current execute state before we're going to switch the runtime to - // our own set of opcodes - ExecuteState oldstate; - - // old execute state has been saved (and will automatically be restured when - // the oldstate is destructed), so we can now safely overwrite all the settings - EG(return_value_ptr_ptr) = &retval_ptr; - EG(active_op_array) = _opcodes; - EG(no_extensions) = 1; - if (!EG(active_symbol_table)) zend_rebuild_symbol_table(TSRMLS_C); - CG(interactive) = 0; - - // the current exception - zval* oldException = EG(exception); - - // execute the code - zend_execute(_opcodes TSRMLS_CC); - - // was an exception thrown inside the eval()'ed code? In that case we - // throw a C++ new exception to give the C++ code the chance to catch it - if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC); - - // we're ready if there is no return value - if (!retval_ptr) return nullptr; - - // wrap the return value - Value result(retval_ptr); - - // destruct the zval (this function will decrement the reference counter, - // and only destruct if there are no other references left) - zval_ptr_dtor(&retval_ptr); - - // copy the pointer into a value object, and return that - return result; -} - -/** - * End of namespace - */ -} - diff --git a/zend/opcodes.h b/zend/opcodes.h new file mode 100644 index 0000000..7e6d1ec --- /dev/null +++ b/zend/opcodes.h @@ -0,0 +1,132 @@ +/** + * Opcodes.h + * + * Class represents a set of opcodes of a PHP script that can be executed. This + * is an internal file that you normally do not have to instantiate yourself. + * Better use the Php::Script of Php::File classes. + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ + +/** + * Forward declarations + */ +struct _zend_op_array; + +/** + * Namespace + */ +namespace Php { + +/** + * Class definition + */ +class Opcodes +{ +public: + /** + * Constructor + * @param opcodes + */ + Opcodes(struct _zend_op_array *opcodes TSRMLS_DC) : _opcodes(opcodes) + { +#ifdef ZTS + // copy tsrm_ls param + this->tsrm_ls = tsrm_ls; +#endif + } + + /** + * Destructor + */ + virtual ~Opcodes() + { + // leap out if opcodes were not valid + if (!_opcodes) return; + + // clean up opcodes + destroy_op_array(_opcodes TSRMLS_CC); + efree(_opcodes); + } + + /** + * Are the opcodes valid? + * @return bool + */ + bool valid() const + { + return _opcodes != nullptr; + } + + /** + * Execute the opcodes + * @return Value + */ + Value execute() const + { + // if the script could not be compiled, we return null + if (!_opcodes) return nullptr; + + // pointer that is going to hold the return value of the script + zval *retval_ptr = nullptr; + + // the zend engine is probably already busy processing opcodes, so we store + // the current execute state before we're going to switch the runtime to + // our own set of opcodes + ExecuteState oldstate(TSRMLS_C); + + // old execute state has been saved (and will automatically be restured when + // the oldstate is destructed), so we can now safely overwrite all the settings + EG(return_value_ptr_ptr) = &retval_ptr; + EG(active_op_array) = _opcodes; + EG(no_extensions) = 1; + if (!EG(active_symbol_table)) zend_rebuild_symbol_table(TSRMLS_C); + CG(interactive) = 0; + + // the current exception + zval* oldException = EG(exception); + + // execute the code + zend_execute(_opcodes TSRMLS_CC); + + // was an exception thrown inside the eval()'ed code? In that case we + // throw a C++ new exception to give the C++ code the chance to catch it + if (oldException != EG(exception) && EG(exception)) throw OrigException(EG(exception) TSRMLS_CC); + + // we're ready if there is no return value + if (!retval_ptr) return nullptr; + + // wrap the return value + Value result(retval_ptr); + + // destruct the zval (this function will decrement the reference counter, + // and only destruct if there are no other references left) + zval_ptr_dtor(&retval_ptr); + + // copy the pointer into a value object, and return that + return result; + } + +private: + /** + * The opcodes + * @var zend_op_array + */ + struct _zend_op_array *_opcodes; + +#ifdef ZTS + /** + * When in thread safety mode, we also keep track of the TSRM_LS var + * @var void*** + */ + void ***tsrm_ls; +#endif + +}; + +/** + * End of namespace + */ +} + diff --git a/zend/script.cpp b/zend/script.cpp index 58ee376..a1b5b04 100644 --- a/zend/script.cpp +++ b/zend/script.cpp @@ -32,13 +32,14 @@ zend_op_array *Script::compile(const char *name, const char *phpcode, size_t siz // found there is full of zval manipulation, for which we can use the much // simpler Php::Value object Php::Value source(phpcode, size); - - // remember the old compiler options, and set new compiler options - CompilerOptions options(ZEND_COMPILE_DEFAULT_FOR_EVAL); - + // we need the tsrm_ls variable + // @todo it would be better if this was passed as param to the compile() method TSRMLS_FETCH(); + // remember the old compiler options, and set new compiler options + CompilerOptions options(ZEND_COMPILE_DEFAULT_FOR_EVAL TSRMLS_CC); + // compile the string return zend_compile_string(source._val, (char *)name TSRMLS_CC); } @@ -49,10 +50,48 @@ zend_op_array *Script::compile(const char *name, const char *phpcode, size_t siz * @param script actual PHP code * @param size length of the string */ -Script::Script(const char *name, const char *phpcode, size_t size) : _opcodes(compile(name, phpcode, size)) +Script::Script(const char *name, const char *phpcode, size_t size) { + // we need the tsrm_ls variable + TSRMLS_FETCH(); + + // construct opcodes + _opcodes = new Opcodes(compile(name, phpcode, size) TSRMLS_CC); } + +/** + * Destructor + */ +Script::~Script() +{ + // remove opcodes + delete _opcodes; +} + +/** + * Is the script a valid PHP script without syntax errors? + * @return bool + */ +bool Script::valid() const +{ + // check opcodes + return _opcodes && _opcodes->valid(); +} + +/** + * Execute the script + * The return value of the script is returned + * @return Value + */ +Value Script::execute() const +{ + // pass on to opcodes + if (!_opcodes) return nullptr; + // execute opcodes + return _opcodes->execute(); +} + /** * End of namespace */ |