summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-10 14:10:16 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-10 14:10:16 +0100
commit81861258bef606bcdf14d170fb73cd06e25e5ebe (patch)
tree7b9027d8d461046ee982e059da08d54f4664f4ee
parentdd3422f6ed454d06e6091ad5023da7fc294595b8 (diff)
deal with magic methods and magic interfaces that throw exceptions
-rw-r--r--documentation/magic-methods.html4
-rw-r--r--include/exception.h10
-rw-r--r--include/value.h11
-rw-r--r--src/callable.cpp13
-rw-r--r--src/classbase.cpp177
-rw-r--r--src/exception.cpp32
-rw-r--r--src/origexception.cpp44
-rw-r--r--src/origexception.h27
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;
+ }
};
/**