diff options
-rw-r--r-- | documentation/magic-methods.html | 4 | ||||
-rw-r--r-- | include/exception.h | 10 | ||||
-rw-r--r-- | include/value.h | 11 | ||||
-rw-r--r-- | src/callable.cpp | 13 | ||||
-rw-r--r-- | src/classbase.cpp | 177 | ||||
-rw-r--r-- | src/exception.cpp | 32 | ||||
-rw-r--r-- | src/origexception.cpp | 44 | ||||
-rw-r--r-- | src/origexception.h | 27 |
8 files changed, 216 insertions, 102 deletions
diff --git a/documentation/magic-methods.html b/documentation/magic-methods.html index 87c6cc2..c7902ea 100644 --- a/documentation/magic-methods.html +++ b/documentation/magic-methods.html @@ -68,7 +68,7 @@ public: if (name == "email") return _email; // property not supported, fall back on default - Php::Base::__get(name); + return Php::Base::__get(name); } /** @@ -82,7 +82,7 @@ public: if (name == "name") { // store member - _name = value; + _name = value.stringValue(); } // we check emails for validity diff --git a/include/exception.h b/include/exception.h index 5bcc26a..8475925 100644 --- a/include/exception.h +++ b/include/exception.h @@ -54,6 +54,16 @@ public: { return _message; } + + /** + * Process the exception + * + * This method is called only from withing the PHP-CPP library, + * and will turn the exception into a PHP exception + * + * @internal + */ + virtual void process(); }; /** diff --git a/include/value.h b/include/value.h index 2ffbd7c..f2850c9 100644 --- a/include/value.h +++ b/include/value.h @@ -308,6 +308,17 @@ public: Value operator%(double value); /** + * Comparison operators for hardcoded strings + * @param value + */ + bool operator==(const char *value) const { return stringValue() == value; } + bool operator!=(const char *value) const { return stringValue() != value; } + bool operator<=(const char *value) const { return stringValue() <= value; } + bool operator>=(const char *value) const { return stringValue() >= value; } + bool operator< (const char *value) const { return stringValue() < value; } + bool operator> (const char *value) const { return stringValue() > value; } + + /** * Comparison operators * @param value */ diff --git a/src/callable.cpp b/src/callable.cpp index 66333d5..bf83e10 100644 --- a/src/callable.cpp +++ b/src/callable.cpp @@ -43,17 +43,10 @@ static void invoke_callable(INTERNAL_FUNCTION_PARAMETERS) // get the result result = callable->invoke(params); } - catch (Php::OrigException &exception) + catch (Exception &exception) { - // we caught an exception that was original thrown by PHP code, and not - // processed by C++ code, this means that we're going to restore this - // exception so that it can be further handled by PHP - exception.restore(); - } - catch (Php::Exception &exception) - { - // an exception originally thrown by C++ should be passed on to PHP - zend_throw_exception(zend_exception_get_default(), (char*)exception.message().c_str(), 0 TSRMLS_CC); + // process the exception + exception.process(); } } diff --git a/src/classbase.cpp b/src/classbase.cpp index 8a2861b..e244bf4 100644 --- a/src/classbase.cpp +++ b/src/classbase.cpp @@ -153,11 +153,23 @@ int ClassBase::countElements(zval *object, long *count TSRMLS_DC) // if it does not implement the Countable interface, we rely on the default implementation if (countable) { - // call the count function - *count = countable->count(); - - // done - return SUCCESS; + // the user function may throw an exception that needs to be processed + try + { + // call the count function + *count = countable->count(); + + // done + return SUCCESS; + } + catch (Exception &exception) + { + // process the exception + exception.process(); + + // unreachable + return FAILURE; + } } else { @@ -206,23 +218,35 @@ zval *ClassBase::readDimension(zval *object, zval *offset, int type) // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) { - // ArrayAccess is implemented, call function - Value value = arrayaccess->offsetGet(offset); - - // because we do not want the value object to destruct the zval when - // it falls out of scope, we detach the zval from it, if this is a regular - // read operation we can do this right away - if (type == 0) return value.detach(); - - // this is a more complicated read operation, the scripts wants to get - // deeper access to the returned value. This, however, is only possible - // if the value has more than once reference (if it has a refcount of one, - // the value object that we have here is the only instance of the zval, - // and it is simply impossible to return a reference or so - if (value.refcount() <= 1) return value.detach(); - - // we're dealing with an editable zval, return a reference variable - return Value(value.detach(), true).detach(); + // the C++ code may throw an exception + try + { + // ArrayAccess is implemented, call function + Value value = arrayaccess->offsetGet(offset); + + // because we do not want the value object to destruct the zval when + // it falls out of scope, we detach the zval from it, if this is a regular + // read operation we can do this right away + if (type == 0) return value.detach(); + + // this is a more complicated read operation, the scripts wants to get + // deeper access to the returned value. This, however, is only possible + // if the value has more than once reference (if it has a refcount of one, + // the value object that we have here is the only instance of the zval, + // and it is simply impossible to return a reference or so + if (value.refcount() <= 1) return value.detach(); + + // we're dealing with an editable zval, return a reference variable + return Value(value.detach(), true).detach(); + } + catch (Exception &exception) + { + // process the exception (send it to user space) + exception.process(); + + // unreachable + return Value(nullptr).detach(); + } } else { @@ -253,8 +277,17 @@ void ClassBase::writeDimension(zval *object, zval *offset, zval *value) // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) { - // set the value - arrayaccess->offsetSet(offset, value); + // method may throw an exception + try + { + // set the value + arrayaccess->offsetSet(offset, value); + } + catch (Exception &exception) + { + // process the exception (send it to user space + exception.process(); + } } else { @@ -285,15 +318,27 @@ int ClassBase::hasDimension(zval *object, zval *member, int check_empty) // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) { - // check if the member exists - if (!arrayaccess->offsetExists(member)) return false; - - // we know for certain that the offset exists, but should we check - // more, like whether the value is empty or not? - if (!check_empty) return true; - - // it should not be empty - return !arrayaccess->offsetGet(member).isEmpty(); + // user implemented callbacks could throw an exception + try + { + // check if the member exists + if (!arrayaccess->offsetExists(member)) return false; + + // we know for certain that the offset exists, but should we check + // more, like whether the value is empty or not? + if (!check_empty) return true; + + // it should not be empty + return !arrayaccess->offsetGet(member).isEmpty(); + } + catch (Exception &exception) + { + // process the exception (send it to user space) + exception.process(); + + // unreachable + return false; + } } else { @@ -322,8 +367,17 @@ void ClassBase::unsetDimension(zval *object, zval *member) // if it does not implement the ArrayAccess interface, we rely on the default implementation if (arrayaccess) { - // remove the member - arrayaccess->offsetUnset(member); + // user implemented code could throw an exception + try + { + // remove the member + arrayaccess->offsetUnset(member); + } + catch (Exception &exception) + { + // process the exception (send it to user space) + exception.process(); + } } else { @@ -391,6 +445,15 @@ zval *ClassBase::readProperty(zval *object, zval *name, int type) // call default return std_object_handlers.read_property(object, name, type); } + catch (Exception &exception) + { + // user threw an exception in its magic method + // implementation, send it to user space + exception.process(); + + // unreachable + return Value(nullptr).detach(); + } } /** @@ -421,6 +484,12 @@ void ClassBase::writeProperty(zval *object, zval *name, zval *value) // call the default std_object_handlers.write_property(object, name, value); } + catch (Exception &exception) + { + // user threw an exception in its magic method + // implementation, send it to user space + exception.process(); + } } /** @@ -473,6 +542,15 @@ int ClassBase::hasProperty(zval *object, zval *name, int has_set_exists) // call default return std_object_handlers.has_property(object, name, has_set_exists); } + catch (Exception &exception) + { + // user threw an exception in its magic method + // implementation, send it to user space + exception.process(); + + // unreachable + return false; + } } /** @@ -500,6 +578,12 @@ void ClassBase::unsetProperty(zval *object, zval *member) // call the default std_object_handlers.unset_property(object, member); } + catch (Exception &exception) + { + // user threw an exception in its magic method + // implementation, send it to user space + exception.process(); + } } /** @@ -550,11 +634,24 @@ zend_object_iterator *ClassBase::getIterator(zend_class_entry *entry, zval *obje // retrieve the traversable object Traversable *traversable = dynamic_cast<Traversable*>(cpp_object(object)); - // create an iterator - auto *iterator = traversable->getIterator(); - - // return the implementation - return iterator->implementation(); + // user may throw an exception in the getIterator() function + try + { + // create an iterator + auto *iterator = traversable->getIterator(); + + // return the implementation + return iterator->implementation(); + } + catch (Exception &exception) + { + // user threw an exception in its method + // implementation, send it to user space + exception.process(); + + // unreachable + return nullptr; + } } /** diff --git a/src/exception.cpp b/src/exception.cpp new file mode 100644 index 0000000..83c1d12 --- /dev/null +++ b/src/exception.cpp @@ -0,0 +1,32 @@ +/** + * 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 withing the PHP-CPP library, + * and will turn the exception into a PHP exception + */ +void Exception::process() +{ + // an exception originally thrown by C++ should be passed on to PHP + zend_throw_exception(zend_exception_get_default(), (char*)message().c_str(), 0 TSRMLS_CC); +} + +/** + * End namespace + */ +} + diff --git a/src/origexception.cpp b/src/origexception.cpp index e24131a..e3634e4 100644 --- a/src/origexception.cpp +++ b/src/origexception.cpp @@ -14,37 +14,6 @@ namespace Php { /** - * Constructor - * @param zval - */ -OrigException::OrigException(struct _zval_struct *zval) : - Value(zval), - Exception("OrigException"), - _restored(false) -{ - // save the exception - //zend_exception_save(); -} - -/** - * Copy constructor - * @param exception - */ -OrigException::OrigException(const OrigException &exception) : - Value(exception), - Exception("OrigException"), - _restored(exception._restored) {} - -/** - * Move constructor - * @param exception - */ -OrigException::OrigException(OrigException &&exception) : - Value(std::move(exception)), - Exception("OrigException"), - _restored(exception._restored) {} - -/** * Destructor */ OrigException::~OrigException() noexcept @@ -57,19 +26,6 @@ OrigException::~OrigException() noexcept } /** - * Restore the exception - * @internal - */ -void OrigException::restore() -{ - // restore the exception - //zend_exception_restore(); - - // mark exception as restored - _restored = true; -} - -/** * End of namespace */ } diff --git a/src/origexception.h b/src/origexception.h index ca59e7f..c1b2871 100644 --- a/src/origexception.h +++ b/src/origexception.h @@ -23,26 +23,34 @@ private: * Has the exception been restored by the C++ code so that it can be dealt by PHP? * @var boolean */ - bool _restored; + bool _restored = false; public: /** * Constructor * @param zval */ - OrigException(struct _zval_struct *zval); + OrigException(struct _zval_struct *zval) : + Value(zval), Exception("OrigException") {} /** * Copy constructor * @param exception */ - OrigException(const OrigException &exception); + OrigException(const OrigException &exception) : + Value(exception), Exception("OrigException"), _restored(exception._restored) {} /** * Move constructor * @param exception */ - OrigException(OrigException &&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 @@ -50,10 +58,17 @@ public: virtual ~OrigException() throw(); /** - * Restore the exception - because the exception was not handled by C++ code + * Process the exception + * + * This will restore the exception so that it can be further processed + * in PHP code * @internal */ - void restore(); + virtual void process() override + { + // mark exception as restored + _restored = true; + } }; /** |