From 927e54ecde25ed7ba718697631dc8ffd208e361a Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Tue, 6 May 2014 09:33:22 +0200
Subject: [PATCH 01/41] Value objects constructed with (const char *)NULL
caused a crash, this has been fixed so they hold a PHP NULL value
---
zend/value.cpp | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/zend/value.cpp b/zend/value.cpp
index 90402344..7fc45d65 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -124,9 +124,20 @@ Value::Value(const std::string &value)
*/
Value::Value(const char *value, int size)
{
- // create a string zval
+ // allocate the zval
MAKE_STD_ZVAL(_val);
- ZVAL_STRINGL(_val, value, size < 0 ? ::strlen(value) : size, 1);
+
+ // do we have a valid value?
+ if (value)
+ {
+ // create a string zval
+ ZVAL_STRINGL(_val, value, size < 0 ? ::strlen(value) : size, 1);
+ }
+ else
+ {
+ // store null
+ ZVAL_NULL(_val);
+ }
}
/**
From f57607d2d58f6e7689a3550c84ba68ce42c6a7b3 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Tue, 6 May 2014 09:34:12 +0200
Subject: [PATCH 02/41] When "apache reload" is called, the PHP-CPP library
made the entire Apache process crash. This has been fixed
---
include/extension.h | 16 ++++++++++++++++
include/namespace.h | 37 +++++++++++++++++++++++++++++++++++++
zend/extension.cpp | 12 ++++++++++++
zend/extensionimpl.cpp | 12 +++++++++++-
zend/extensionimpl.h | 23 ++++++++++++++++++-----
zend/namespace.cpp | 12 ++++++++++++
6 files changed, 106 insertions(+), 6 deletions(-)
diff --git a/include/extension.h b/include/extension.h
index f03c8fb7..562fcebd 100644
--- a/include/extension.h
+++ b/include/extension.h
@@ -111,6 +111,9 @@ class Extension : public Namespace
*/
Extension &add(Ini &&ini)
{
+ // skip when locked
+ if (locked()) return *this;
+
// and add it to the list of classes
_ini_entries.emplace_back(new Ini(std::move(ini)));
@@ -125,6 +128,9 @@ class Extension : public Namespace
*/
Extension &add(const Ini &ini)
{
+ // skip when locked
+ if (locked()) return *this;
+
// and add it to the list of classes
_ini_entries.emplace_back(new Ini(ini));
@@ -180,6 +186,16 @@ class Extension : public Namespace
{
return module();
}
+
+protected:
+ /**
+ * Is the extension object in a locked state? This happens after the
+ * get_module() function was called for the first time ("apache reload"
+ * forces a new call to get_module())
+ *
+ * @return bool
+ */
+ virtual bool locked() const override;
private:
/**
diff --git a/include/namespace.h b/include/namespace.h
index 828c12c6..e7138503 100644
--- a/include/namespace.h
+++ b/include/namespace.h
@@ -48,6 +48,25 @@ class Namespace
*/
std::list> _namespaces;
+ /**
+ * Is the object locked?
+ *
+ * After the object is locked, no more elements can be added to it.
+ * This happens after the call to get_module - it the no longer makes
+ * sense to add more objects. When 'apache reload' is executed, the
+ * get_module() function is called for a second (or third, or fourth)
+ * time, but the classes, functions and namespaces will then not be
+ * filled.
+ *
+ * @var bool
+ */
+ virtual bool locked() const
+ {
+ // by default, namespaces are not locked (only derived extension
+ // objects can end up in a locked state
+ return false;
+ }
+
public:
/**
@@ -81,6 +100,9 @@ class Namespace
template
Namespace &add(Class &&type)
{
+ // skip when locked
+ if (locked()) return *this;
+
// make a copy of the object, and add it to the list of classes
_classes.push_back(std::unique_ptr(new Class(std::move(type))));
@@ -96,6 +118,9 @@ class Namespace
template
Namespace &add(const Class &type)
{
+ // skip when locked
+ if (locked()) return *this;
+
// and add it to the list of classes
_classes.push_back(std::unique_ptr(new Class(type)));
@@ -110,6 +135,9 @@ class Namespace
*/
Namespace &add(Interface &&interface)
{
+ // skip when locked
+ if (locked()) return *this;
+
// make a copy and add it to the list of classes
_classes.push_back(std::unique_ptr(new Interface(std::move(interface))));
@@ -124,6 +152,9 @@ class Namespace
*/
Namespace &add(const Interface &interface)
{
+ // skip when locked
+ if (locked()) return *this;
+
// make a copy and add it to the list of classes
_classes.push_back(std::unique_ptr(new Interface(interface)));
@@ -138,6 +169,9 @@ class Namespace
*/
Namespace &add(Namespace &&ns)
{
+ // skip when locked
+ if (locked()) return *this;
+
// add it to the list of namespaces
_namespaces.push_back(std::unique_ptr(new Namespace(std::move(ns))));
@@ -152,6 +186,9 @@ class Namespace
*/
Namespace &add(const Namespace &ns)
{
+ // skip when locked
+ if (locked()) return *this;
+
// make a copy and add it to the list of namespaces
_namespaces.push_back(std::unique_ptr(new Namespace(ns)));
diff --git a/zend/extension.cpp b/zend/extension.cpp
index 9685b32c..1545f893 100644
--- a/zend/extension.cpp
+++ b/zend/extension.cpp
@@ -96,6 +96,18 @@ void *Extension::module()
return _impl->module();
}
+/**
+ * Is the extension object in a locked state? This happens after the
+ * get_module() function was called for the first time ("apache reload"
+ * forces a new call to get_module())
+ *
+ * @return bool
+ */
+bool Extension::locked() const
+{
+ return _impl->locked();
+}
+
/**
* End of namespace
*/
diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp
index ea0c0493..f073acf2 100644
--- a/zend/extensionimpl.cpp
+++ b/zend/extensionimpl.cpp
@@ -142,6 +142,10 @@ int ExtensionImpl::processStartup(int type, int module_number TSRMLS_DC)
// initialize the extension
extension->initialize(TSRMLS_C);
+ // remember that we're initialized (when you use "apache reload" it is
+ // possible that the processStartup() method is called more than once)
+ extension->_locked = true;
+
// is the callback registered?
if (extension->_onStartup) extension->_onStartup();
@@ -164,6 +168,12 @@ int ExtensionImpl::processShutdown(int type, int module_number TSRMLS_DC)
// unregister the ini entries
zend_unregister_ini_entries(module_number TSRMLS_CC);
+ // destruct the ini entries
+ if (extension->_ini) delete[] extension->_ini;
+
+ // forget the ini entries
+ extension->_ini = nullptr;
+
// is the callback registered?
if (extension->_onShutdown) extension->_onShutdown();
@@ -272,7 +282,7 @@ ExtensionImpl::~ExtensionImpl()
*/
zend_module_entry *ExtensionImpl::module()
{
- // check if functions we're already defined
+ // check if functions were already defined
if (_entry.functions) return &_entry;
// the number of functions
diff --git a/zend/extensionimpl.h b/zend/extensionimpl.h
index ba3e6c8e..0e9a289c 100644
--- a/zend/extensionimpl.h
+++ b/zend/extensionimpl.h
@@ -20,15 +20,17 @@ 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;
+ /**
+ * Is the object locked? This prevents crashes for 'apache reload'
+ * because we then do not have to re-initialize the entire php engine
+ * @var bool
+ */
+ bool _locked = false;
+
/**
* The .ini entries
*
@@ -56,6 +58,17 @@ class ExtensionImpl : public ExtensionBase
*/
virtual ~ExtensionImpl();
+ /**
+ * Is the object locked (true) or is it still possible to add more functions,
+ * classes and other elements to it?
+ * @return bool
+ */
+ bool locked()
+ {
+ // return member
+ return _locked;
+ }
+
/**
* Retrieve the module entry
*
diff --git a/zend/namespace.cpp b/zend/namespace.cpp
index 2b4b62af..9274bf05 100644
--- a/zend/namespace.cpp
+++ b/zend/namespace.cpp
@@ -22,6 +22,9 @@ namespace Php {
*/
Namespace &Namespace::add(const char *name, const native_callback_0 &function, const Arguments &arguments)
{
+ // skip when locked
+ if (locked()) return *this;
+
// add a function
_functions.push_back(std::make_shared(name, function, arguments));
@@ -38,6 +41,9 @@ Namespace &Namespace::add(const char *name, const native_callback_0 &function, c
*/
Namespace &Namespace::add(const char *name, const native_callback_1 &function, const Arguments &arguments)
{
+ // skip when locked
+ if (locked()) return *this;
+
// add a function
_functions.push_back(std::make_shared(name, function, arguments));
@@ -54,6 +60,9 @@ Namespace &Namespace::add(const char *name, const native_callback_1 &function, c
*/
Namespace &Namespace::add(const char *name, const native_callback_2 &function, const Arguments &arguments)
{
+ // skip when locked
+ if (locked()) return *this;
+
// add a function
_functions.push_back(std::make_shared(name, function, arguments));
@@ -70,6 +79,9 @@ Namespace &Namespace::add(const char *name, const native_callback_2 &function, c
*/
Namespace &Namespace::add(const char *name, const native_callback_3 &function, const Arguments &arguments)
{
+ // skip when locked
+ if (locked()) return *this;
+
// add a function
_functions.push_back(std::make_shared(name, function, arguments));
From 9497b86d1a5a31684f8fcce9c6db3d66b5a79260 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Tue, 6 May 2014 10:03:42 +0200
Subject: [PATCH 03/41] update documentation about extension callbacks
---
documentation/extension-callbacks.html | 35 ++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/documentation/extension-callbacks.html b/documentation/extension-callbacks.html
index ae266dec..4821f9bf 100644
--- a/documentation/extension-callbacks.html
+++ b/documentation/extension-callbacks.html
@@ -130,6 +130,41 @@ Extension callbacks
right before PHP shuts down. If there is anything to clean up, you can install
such a callback and run the cleanup code from it.
+Forked engines (like Apache)
+
+ If you run PHP on a pre-fork web server (like Apache), your extension is
+ loaded and initialized before the various worker threads are
+ forked off. The consequence of this is that the get_module() function
+ and your optional onStartup() callback function are called by the parent
+ process, and all other callbacks and the actual page handling by the
+ child processes. A call to getpid() (or other functions to retrieve
+ information about the current process) will therefore return something
+ else inside the onStartup callback, as it does in any of the other
+ extension functions.
+
+
+ You may have to be careful because of this. It is better not to
+ do things in the startup functions that may not work when the process
+ is forked into different child processes (like opening file descriptors).
+ Something else to keep in mind is that the startup function is only called
+ by the parent processes when Apache starts up (or reloaded, see later),
+ while the shutdown function is called by every child process
+ that gracefully exits. The onShutdown is thus not only called when the
+ Apache process is stopped, but also when one of the worker processes
+ exits because it no longer is necessary, or because it is replaced by
+ a fresh and new worker.
+
+
+ The get_module() function - as you've seen countles times before - is
+ called when your extension is initially loaded. But not only then. When
+ apache is reloaded (for example by giving the command line instruction
+ "apachectl reload"), your get_module() gets called for a second time,
+ and the callback that you registered with Extension::onStartup() too.
+ This is normally not a problem, because the static extension object is
+ in a locked state after the first get_module() call, and the functions
+ and classes that you try to add to the extension object in the second
+ invokation of get_module() are simply ignored.
+
Watch out for multi-threading
If your extension runs on a multi-threaded PHP installation, you need to take
From 9502a731c09593db0d3b7ad3a1d3ee90b8b44901 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Tue, 6 May 2014 10:06:20 +0200
Subject: [PATCH 04/41] tiny changes in documentation about extension callbacks
---
documentation/extension-callbacks.html | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/documentation/extension-callbacks.html b/documentation/extension-callbacks.html
index 4821f9bf..b8c26866 100644
--- a/documentation/extension-callbacks.html
+++ b/documentation/extension-callbacks.html
@@ -151,7 +151,7 @@ Forked engines (like Apache)
while the shutdown function is called by every child process
that gracefully exits. The onShutdown is thus not only called when the
Apache process is stopped, but also when one of the worker processes
- exits because it no longer is necessary, or because it is replaced by
+ exits because it no longer is needed, or because it is replaced by
a fresh and new worker.
@@ -159,7 +159,8 @@
Forked engines (like Apache)
called when your extension is initially loaded. But not only then. When
apache is reloaded (for example by giving the command line instruction
"apachectl reload"), your get_module() gets called for a second time,
- and the callback that you registered with Extension::onStartup() too.
+ and the callback that you registered with Extension::onStartup() is
+ called again too.
This is normally not a problem, because the static extension object is
in a locked state after the first get_module() call, and the functions
and classes that you try to add to the extension object in the second
From 5ba897e11fc7a4648a9323ed2bb74c1ac6aa57d8 Mon Sep 17 00:00:00 2001
From: valmat
Date: Sun, 8 Jun 2014 22:45:27 +0600
Subject: [PATCH 05/41] Comparison operators for hardcoded Value
---
include/value.h | 11 +++++++
zend/value.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 91 insertions(+)
diff --git a/include/value.h b/include/value.h
index 63e8e6f9..c478eb26 100644
--- a/include/value.h
+++ b/include/value.h
@@ -321,6 +321,17 @@ class Value : private HashParent
bool operator< (const char *value) const { return ::strcmp(rawValue(), value) < 0; }
bool operator> (const char *value) const { return ::strcmp(rawValue(), value) > 0; }
+ /**
+ * Comparison operators for hardcoded Value
+ * @param value
+ */
+ bool operator==(const Value &value) const;
+ bool operator!=(const Value &value) const { return !operator==(value); }
+ bool operator< (const Value &value) const;
+ bool operator> (const Value &value) const;
+ bool operator<=(const Value &value) const { return !operator>(value); }
+ bool operator>=(const Value &value) const { return !operator<(value); }
+
/**
* Comparison operators
* @param value
diff --git a/zend/value.cpp b/zend/value.cpp
index 7fc45d65..477017a5 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1311,6 +1311,86 @@ Value Value::exec(const char *name, int argc, struct _zval_struct ***params)
return do_exec(&_val, method._val, argc, params);
}
+
+/**
+ * Comparison operators for hardcoded Value
+ * @param value
+ */
+bool Value::operator==(const Value &value) const
+{
+ auto tp = type();
+ if(tp != value.type())
+ return false;
+
+ switch (tp) {
+ case Type::Null: return true;
+ case Type::Numeric: return (_val->value.lval == value._val->value.lval);
+ case Type::Float: return (_val->value.dval == value._val->value.dval);
+ case Type::Bool: return (_val->value.lval == value._val->value.lval);
+ case Type::String: return (_val->value.str.len && value._val->value.str.len) && (::strcmp(_val->value.str.val, value._val->value.str.val) == 0);
+ // @todo
+ case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
+ // @todo
+ case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
+ case Type::Resource: return (_val->value.lval == value._val->value.lval);
+ case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
+
+ }
+ return false;
+}
+bool Value::operator< (const Value &value) const
+{
+ auto tp = type();
+ if(tp != value.type()){
+ // @todo maybe this can be improved
+ throw Php::Exception("It is not possible to compare different types");
+ return false;
+ }
+ switch (tp) {
+ case Type::Null: return false;
+ case Type::Numeric: return (_val->value.lval < value._val->value.lval);
+ case Type::Float: return (_val->value.dval < value._val->value.dval);
+ case Type::Bool: return (_val->value.lval < value._val->value.lval);
+ case Type::String: return (::strcmp(_val->value.str.val, value._val->value.str.val) < 0);
+ // @todo
+ case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
+ // @todo
+ case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
+ case Type::Resource: throw Php::Exception("Resource types is not comparable"); break;
+ case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
+ }
+ return true;
+}
+bool Value::operator> (const Value &value) const
+{
+ auto tp = type();
+ if(tp != value.type()){
+ // @todo maybe this can be improved
+ throw Php::Exception("It is not possible to compare different types");
+ return false;
+ }
+ switch (tp) {
+ case Type::Null: return false;
+ case Type::Numeric: return (_val->value.lval > value._val->value.lval);
+ case Type::Float: return (_val->value.dval > value._val->value.dval);
+ case Type::Bool: return (_val->value.lval > value._val->value.lval);
+ case Type::String: return (::strcmp(_val->value.str.val, value._val->value.str.val) > 0);
+ // @todo
+ case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
+ // @todo
+ case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
+ case Type::Resource: throw Php::Exception("Resource types is not comparable"); break;
+ case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
+ }
+ return true;
+}
+
/**
* The type of object
* @return Type
From b4936cbc31c20c390d50333da5c83ff31795acf7 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Wed, 11 Jun 2014 10:45:01 +0200
Subject: [PATCH 06/41] when an object was created using Php::Object("MyClass",
new MyClass()), the object handlers were not installed, which caused the
magic methods not to be functional (issue #94)
---
include/classbase.h | 4 ++--
zend/classimpl.cpp | 56 ++++++++++++++++++++++++---------------------
zend/classimpl.h | 19 +++++++++++++++
zend/object.cpp | 5 +++-
zend/objectimpl.h | 2 +-
5 files changed, 56 insertions(+), 30 deletions(-)
diff --git a/include/classbase.h b/include/classbase.h
index a736c4e0..9f0bac3c 100644
--- a/include/classbase.h
+++ b/include/classbase.h
@@ -2,9 +2,9 @@
* ClassBase.h
*
* This is the base class of the "Class" class. This is an internal class that
- * is used by the PHP-CPP library. But because the constructor is protected,
+ * is used by the PHP-CPP library. Because the constructor is protected,
* you can not create any instances if this class yourself (and you are not
- * supposed to do that either.
+ * supposed to do that either).
*
* Further more, because this base class is a 'private' base of Class, all
* features of it are normally also inaccessible.
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index d63956a8..953738a9 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -330,52 +330,56 @@ int ClassImpl::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
*/
zend_object_handlers *ClassImpl::objectHandlers()
{
- // keep static structure
- static zend_object_handlers handlers;
-
- // is the object already initialized?
- static bool initialized = false;
-
// already initialized?
- if (initialized) return &handlers;
+ if (_initialized) return &_handlers;
// initialize the handlers
- memcpy(&handlers, &std_object_handlers, sizeof(zend_object_handlers));
+ memcpy(&_handlers, &std_object_handlers, sizeof(zend_object_handlers));
// install custom clone function
- if (!_base->clonable()) handlers.clone_obj = nullptr;
- else handlers.clone_obj = &ClassImpl::cloneObject;
+ if (!_base->clonable()) _handlers.clone_obj = nullptr;
+ else _handlers.clone_obj = &ClassImpl::cloneObject;
// functions for the Countable interface
- handlers.count_elements = &ClassImpl::countElements;
+ _handlers.count_elements = &ClassImpl::countElements;
// functions for the ArrayAccess interface
- handlers.write_dimension = &ClassImpl::writeDimension;
- handlers.read_dimension = &ClassImpl::readDimension;
- handlers.has_dimension = &ClassImpl::hasDimension;
- handlers.unset_dimension = &ClassImpl::unsetDimension;
+ _handlers.write_dimension = &ClassImpl::writeDimension;
+ _handlers.read_dimension = &ClassImpl::readDimension;
+ _handlers.has_dimension = &ClassImpl::hasDimension;
+ _handlers.unset_dimension = &ClassImpl::unsetDimension;
// functions for the magic properties handlers (__get, __set, __isset and __unset)
- handlers.write_property = &ClassImpl::writeProperty;
- handlers.read_property = &ClassImpl::readProperty;
- handlers.has_property = &ClassImpl::hasProperty;
- handlers.unset_property = &ClassImpl::unsetProperty;
+ _handlers.write_property = &ClassImpl::writeProperty;
+ _handlers.read_property = &ClassImpl::readProperty;
+ _handlers.has_property = &ClassImpl::hasProperty;
+ _handlers.unset_property = &ClassImpl::unsetProperty;
// when a method is called (__call and __invoke)
- handlers.get_method = &ClassImpl::getMethod;
- handlers.get_closure = &ClassImpl::getClosure;
+ _handlers.get_method = &ClassImpl::getMethod;
+ _handlers.get_closure = &ClassImpl::getClosure;
// handler to cast to a different type
- handlers.cast_object = &ClassImpl::cast;
+ _handlers.cast_object = &ClassImpl::cast;
// method to compare two objects
- handlers.compare_objects = &ClassImpl::compare;
+ _handlers.compare_objects = &ClassImpl::compare;
// remember that object is now initialized
- initialized = true;
+ _initialized = true;
// done
- return &handlers;
+ return &_handlers;
+}
+
+/**
+ * Alternative way to retrieve object handlers, given a class entry
+ * @param entry
+ * @return zend_object_handlers
+ */
+zend_object_handlers *ClassImpl::objectHandlers(zend_class_entry *entry)
+{
+ return self(entry)->objectHandlers();
}
/**
@@ -465,7 +469,7 @@ int ClassImpl::cast(zval *val, zval *retval, int type TSRMLS_DC)
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;
+ default: throw NotImplemented(); break;
}
// @todo do we turn into endless conversion if the __toString object returns 'this' ??
diff --git a/zend/classimpl.h b/zend/classimpl.h
index ec28322f..bd631b8b 100644
--- a/zend/classimpl.h
+++ b/zend/classimpl.h
@@ -84,6 +84,18 @@ class ClassImpl
* @var std::shared_ptr
*/
std::shared_ptr _parent;
+
+ /**
+ * The object handlers for instances of this class
+ * @var zend_object_handlers
+ */
+ zend_object_handlers _handlers;
+
+ /**
+ * Are the handlers already initialized?
+ * @var bool
+ */
+ bool _initialized = false;
/**
@@ -203,6 +215,13 @@ class ClassImpl
*/
zend_object_handlers *objectHandlers();
+ /**
+ * Alternative way to retrieve object handlers, given a class entry
+ * @param entry
+ * @return zend_object_handlers
+ */
+ static zend_object_handlers *objectHandlers(zend_class_entry *entry);
+
/**
* Function to create a new iterator to iterate over an object
* @param entry The class entry
diff --git a/zend/object.cpp b/zend/object.cpp
index 08553b5d..940b1436 100644
--- a/zend/object.cpp
+++ b/zend/object.cpp
@@ -22,7 +22,7 @@ Object::Object(const char *name, Base *base)
// does the object already have a handle?
if (base->implementation())
{
- // the object is already instantiated, we can assign it the this object
+ // the object is already instantiated, we can assign it to this object
operator=(Value(base));
}
else
@@ -42,6 +42,9 @@ Object::Object(const char *name, Base *base)
// now we can store it
operator=(Value(base));
+
+ // install the object handlers
+ Z_OBJVAL_P(_val).handlers = ClassImpl::objectHandlers(entry);
}
}
diff --git a/zend/objectimpl.h b/zend/objectimpl.h
index f66a6ab8..7f16320a 100644
--- a/zend/objectimpl.h
+++ b/zend/objectimpl.h
@@ -66,7 +66,7 @@ class ObjectImpl
*/
ObjectImpl(zend_class_entry *entry, Base *base TSRMLS_DC)
{
- // allocate a mixed object (for some reason this does not have to deallocated)
+ // allocate a mixed object (for some reason this does not have to be deallocated)
_mixed = (MixedObject *)emalloc(sizeof(MixedObject));
// copy properties to the mixed object
From b17b09c3b482f51a842f5272e88a7ed2820a1925 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Wed, 11 Jun 2014 11:10:31 +0200
Subject: [PATCH 07/41] issue #93: fix crash when moving to null Php::Value
object
---
zend/value.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/zend/value.cpp b/zend/value.cpp
index 7fc45d65..7592f575 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -403,7 +403,7 @@ Value &Value::operator=(Value &&value)
if (this == &value) return *this;
// is the object a reference?
- if (Z_ISREF_P(_val))
+ if (_val && Z_ISREF_P(_val))
{
// @todo difference if the other object is a reference or not?
@@ -449,7 +449,7 @@ Value &Value::operator=(Value &&value)
{
// destruct the zval (this function will decrement the reference counter,
// and only destruct if there are no other references left)
- zval_ptr_dtor(&_val);
+ if (_val) zval_ptr_dtor(&_val);
// just copy the zval completely
_val = value._val;
From a3aea78834102b7d0f206c73463de69050f15076 Mon Sep 17 00:00:00 2001
From: valmat
Date: Wed, 11 Jun 2014 16:36:01 +0600
Subject: [PATCH 08/41] Corrected in accordance with Proposition
https://github.com/CopernicaMarketingSoftware/PHP-CPP/pull/95#issuecomment-45716890
---
zend/value.cpp | 49 +++++++++++++++++++++----------------------------
1 file changed, 21 insertions(+), 28 deletions(-)
diff --git a/zend/value.cpp b/zend/value.cpp
index 477017a5..74c84e58 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1318,21 +1318,20 @@ Value Value::exec(const char *name, int argc, struct _zval_struct ***params)
*/
bool Value::operator==(const Value &value) const
{
- auto tp = type();
- if(tp != value.type())
- return false;
+ Type tp = type();
+ const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
switch (tp) {
case Type::Null: return true;
- case Type::Numeric: return (_val->value.lval == value._val->value.lval);
- case Type::Float: return (_val->value.dval == value._val->value.dval);
- case Type::Bool: return (_val->value.lval == value._val->value.lval);
- case Type::String: return (_val->value.str.len && value._val->value.str.len) && (::strcmp(_val->value.str.val, value._val->value.str.val) == 0);
+ case Type::Numeric: return (_val->value.lval == thatval->value.lval);
+ case Type::Float: return (_val->value.dval == thatval->value.dval);
+ case Type::Bool: return (_val->value.lval == thatval->value.lval);
+ case Type::String: return (_val->value.str.len && thatval->value.str.len) && (::strcmp(_val->value.str.val, thatval->value.str.val) == 0);
// @todo
case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
// @todo
case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
- case Type::Resource: return (_val->value.lval == value._val->value.lval);
+ case Type::Resource: return (_val->value.lval == thatval->value.lval);
case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
@@ -1342,18 +1341,15 @@ bool Value::operator==(const Value &value) const
}
bool Value::operator< (const Value &value) const
{
- auto tp = type();
- if(tp != value.type()){
- // @todo maybe this can be improved
- throw Php::Exception("It is not possible to compare different types");
- return false;
- }
+ Type tp = type();
+ const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
+
switch (tp) {
case Type::Null: return false;
- case Type::Numeric: return (_val->value.lval < value._val->value.lval);
- case Type::Float: return (_val->value.dval < value._val->value.dval);
- case Type::Bool: return (_val->value.lval < value._val->value.lval);
- case Type::String: return (::strcmp(_val->value.str.val, value._val->value.str.val) < 0);
+ case Type::Numeric: return (_val->value.lval < thatval->value.lval);
+ case Type::Float: return (_val->value.dval < thatval->value.dval);
+ case Type::Bool: return (_val->value.lval < thatval->value.lval);
+ case Type::String: return (::strcmp(_val->value.str.val, thatval->value.str.val) < 0);
// @todo
case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
// @todo
@@ -1367,18 +1363,15 @@ bool Value::operator< (const Value &value) const
}
bool Value::operator> (const Value &value) const
{
- auto tp = type();
- if(tp != value.type()){
- // @todo maybe this can be improved
- throw Php::Exception("It is not possible to compare different types");
- return false;
- }
+ Type tp = type();
+ const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
+
switch (tp) {
case Type::Null: return false;
- case Type::Numeric: return (_val->value.lval > value._val->value.lval);
- case Type::Float: return (_val->value.dval > value._val->value.dval);
- case Type::Bool: return (_val->value.lval > value._val->value.lval);
- case Type::String: return (::strcmp(_val->value.str.val, value._val->value.str.val) > 0);
+ case Type::Numeric: return (_val->value.lval > thatval->value.lval);
+ case Type::Float: return (_val->value.dval > thatval->value.dval);
+ case Type::Bool: return (_val->value.lval > thatval->value.lval);
+ case Type::String: return (::strcmp(_val->value.str.val, thatval->value.str.val) > 0);
// @todo
case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
// @todo
From e350c8f1b33d7c6c605f5450e8cf901cc3e5e1c4 Mon Sep 17 00:00:00 2001
From: valmat
Date: Wed, 11 Jun 2014 18:34:13 +0600
Subject: [PATCH 09/41] Value::operator==
---
include/value.h | 8 ++-
zend/value.cpp | 151 ++++++++++++++++++++++++++++++++++--------------
2 files changed, 115 insertions(+), 44 deletions(-)
diff --git a/include/value.h b/include/value.h
index c478eb26..307702ef 100644
--- a/include/value.h
+++ b/include/value.h
@@ -321,6 +321,12 @@ class Value : private HashParent
bool operator< (const char *value) const { return ::strcmp(rawValue(), value) < 0; }
bool operator> (const char *value) const { return ::strcmp(rawValue(), value) > 0; }
+ /**
+ * Helper method to prepare for comparison
+ * @param value
+ */
+ bool comparePrepare(const Value &value) const;
+
/**
* Comparison operators for hardcoded Value
* @param value
@@ -328,7 +334,7 @@ class Value : private HashParent
bool operator==(const Value &value) const;
bool operator!=(const Value &value) const { return !operator==(value); }
bool operator< (const Value &value) const;
- bool operator> (const Value &value) const;
+ bool operator> (const Value &value) const { return value.operator<(*this); }
bool operator<=(const Value &value) const { return !operator>(value); }
bool operator>=(const Value &value) const { return !operator<(value); }
diff --git a/zend/value.cpp b/zend/value.cpp
index 74c84e58..e1f66838 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1311,36 +1311,127 @@ Value Value::exec(const char *name, int argc, struct _zval_struct ***params)
return do_exec(&_val, method._val, argc, params);
}
+/**
+ * Helper method to prepare for comparison
+ * @param value
+ */
+bool Value::comparePrepare(const Value &value) const
+{
+ Type thisTp = type(), thatTp = value.type();
+ if(thisTp == Type::Resource || thatTp == Type::Resource)
+ {
+ throw Php::Exception("Resource types is not comparable");
+ return false;
+ }
+ if(thisTp == Type::Constant || thatTp == Type::Constant || thisTp == Type::ConstantArray || thatTp == Type::ConstantArray)
+ {
+ throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable");
+ return false;
+ }
+ if(thisTp == Type::Callable || thatTp == Type::Callable)
+ {
+ throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable");
+ return false;
+ }
+ return true;
+}
/**
- * Comparison operators for hardcoded Value
+ * Comparison operators== for hardcoded Value
* @param value
*/
bool Value::operator==(const Value &value) const
{
- Type tp = type();
- const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
+ if(!comparePrepare(value)) return false;
- switch (tp) {
- case Type::Null: return true;
- case Type::Numeric: return (_val->value.lval == thatval->value.lval);
- case Type::Float: return (_val->value.dval == thatval->value.dval);
- case Type::Bool: return (_val->value.lval == thatval->value.lval);
- case Type::String: return (_val->value.str.len && thatval->value.str.len) && (::strcmp(_val->value.str.val, thatval->value.str.val) == 0);
- // @todo
- case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
- // @todo
- case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
- case Type::Resource: return (_val->value.lval == thatval->value.lval);
- case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
+ Type thisTp = type(), thatTp = value.type();
+ //Type tp = type();
+ //const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
+
+ if( (thatTp == Type::Array && thisTp != Type::Array) || (thisTp == Type::Array && thatTp != Type::Array) )
+ {
+ return false;
+ }
+ if(thatTp == Type::Array && thisTp == Type::Array)
+ {
+ if(size() != value.size()) return false;
+ auto thisIt = begin(), thatIt = value.begin(), thisEnd = end();;
+ while(thisIt != thisEnd)
+ {
+ if(*thisIt != *thatIt) return false;
+ ++thisIt;
+ ++thatIt;
+ }
+ return true;
}
+
+ if(thatTp == Type::Null)
+ {
+ return (thisTp == Type::Null) ||
+ (
+ (thisTp == Type::Numeric || thisTp == Type::Bool) &&
+ _val->value.lval == 0
+ ) ||
+ ( (thisTp == Type::String) && _val->value.str.len == 0);
+ }
+ if(thisTp == Type::Null)
+ {
+ return (
+ (thatTp == Type::Numeric || thatTp == Type::Bool) &&
+ value._val->value.lval == 0
+ ) ||
+ ( (thatTp == Type::String) && value._val->value.str.len == 0);
+ }
+
+ if(thatTp == Type::Float)
+ {
+ return clone(Type::Float) == value._val->value.dval;
+ }
+ if(thisTp == Type::Float)
+ {
+ return value.clone(Type::Float) == _val->value.dval;
+ }
+
+ if(thatTp == Type::String)
+ {
+ return clone(Type::String) == value.rawValue();
+ }
+ if(thisTp == Type::String)
+ {
+ return value.clone(Type::String) == rawValue();
+ }
+
+ if(thatTp == Type::Numeric || thatTp == Type::Bool)
+ {
+ return clone(thatTp) == value._val->value.lval;
+ }
+ if(thisTp == Type::String)
+ {
+ return value.clone(thisTp) == _val->value.lval;
+ }
+
+
+ if( (thatTp == Type::Object && thisTp != Type::Object) || (thisTp == Type::Object && thatTp != Type::Object) )
+ {
+ return false;
+ }
+ if(thatTp == Type::Object && thisTp == Type::Object)
+ {
+ return !ClassImpl::compare(_val, value._val TSRMLS_CC);
+ }
+
return false;
}
+
+/**
+ * Comparison operators< for hardcoded Value
+ * @param value
+ */
bool Value::operator< (const Value &value) const
{
+ if(!comparePrepare(value)) return false;
+
Type tp = type();
const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
@@ -1354,32 +1445,6 @@ bool Value::operator< (const Value &value) const
case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
// @todo
case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
- case Type::Resource: throw Php::Exception("Resource types is not comparable"); break;
- case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
- }
- return true;
-}
-bool Value::operator> (const Value &value) const
-{
- Type tp = type();
- const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
-
- switch (tp) {
- case Type::Null: return false;
- case Type::Numeric: return (_val->value.lval > thatval->value.lval);
- case Type::Float: return (_val->value.dval > thatval->value.dval);
- case Type::Bool: return (_val->value.lval > thatval->value.lval);
- case Type::String: return (::strcmp(_val->value.str.val, thatval->value.str.val) > 0);
- // @todo
- case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
- // @todo
- case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
- case Type::Resource: throw Php::Exception("Resource types is not comparable"); break;
- case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
}
return true;
}
From 4f3769855dd65a8ef0b647c7e98860b8aa747c26 Mon Sep 17 00:00:00 2001
From: valmat
Date: Wed, 11 Jun 2014 19:34:40 +0600
Subject: [PATCH 10/41] done
---
include/value.h | 6 -
tests/cpp/h/variables.h | 1 +
tests/cpp/include/variables/028-029-compare.h | 194 ++++++++++++++++++
tests/cpp/main.cpp | 3 +
tests/php/phpt/variables/028-compare1.phpt | 53 +++++
tests/php/phpt/variables/029-compare2.phpt | 74 +++++++
zend/value.cpp | 128 +-----------
7 files changed, 335 insertions(+), 124 deletions(-)
create mode 100644 tests/cpp/include/variables/028-029-compare.h
create mode 100644 tests/php/phpt/variables/028-compare1.phpt
create mode 100644 tests/php/phpt/variables/029-compare2.phpt
diff --git a/include/value.h b/include/value.h
index 307702ef..e8337a03 100644
--- a/include/value.h
+++ b/include/value.h
@@ -321,12 +321,6 @@ class Value : private HashParent
bool operator< (const char *value) const { return ::strcmp(rawValue(), value) < 0; }
bool operator> (const char *value) const { return ::strcmp(rawValue(), value) > 0; }
- /**
- * Helper method to prepare for comparison
- * @param value
- */
- bool comparePrepare(const Value &value) const;
-
/**
* Comparison operators for hardcoded Value
* @param value
diff --git a/tests/cpp/h/variables.h b/tests/cpp/h/variables.h
index 8a074cdf..0b684ddc 100644
--- a/tests/cpp/h/variables.h
+++ b/tests/cpp/h/variables.h
@@ -27,6 +27,7 @@
#include "../include/variables/025-post-raw1.h"
#include "../include/variables/026-post-raw2.h"
#include "../include/variables/027-env.h"
+#include "../include/variables/028-029-compare.h"
//#include "../include/variables/.h"
diff --git a/tests/cpp/include/variables/028-029-compare.h b/tests/cpp/include/variables/028-029-compare.h
new file mode 100644
index 00000000..bd95298d
--- /dev/null
+++ b/tests/cpp/include/variables/028-029-compare.h
@@ -0,0 +1,194 @@
+/**
+ *
+ * Test variables
+ * phptname.phpt
+ *
+ */
+
+
+
+
+/**
+ * Set up namespace
+ */
+namespace TestVariables {
+
+ /*
+ * Test bool Value::operator==(const Value &value) const
+ */
+ void test_compare1()
+ {
+ Php::Value v1(5), v2(5.0), v3("5"), v4("5.0");
+
+ Php::out << "true:" << std::endl;
+ Php::out << (v1 == v2) << std::endl;
+ Php::out << (v1 == v3) << std::endl;
+ Php::out << (v1 == v4) << std::endl;
+ Php::out << (v2 == v1) << std::endl;
+ Php::out << (v2 == v3) << std::endl;
+ Php::out << (v2 == v4) << std::endl;
+ Php::out << (v3 == v1) << std::endl;
+ Php::out << (v3 == v2) << std::endl;
+ Php::out << (v3 == v4) << std::endl;
+ Php::out << (v4 == v1) << std::endl;
+ Php::out << (v4 == v2) << std::endl;
+ Php::out << (v4 == v3) << std::endl;
+
+ Php::Value v5(6), v6(6.0), v7("6"), v8("6.0");
+
+ Php::out << "false:" << std::endl;
+ Php::out << (v1 == v5) << std::endl;
+ Php::out << (v1 == v6) << std::endl;
+ Php::out << (v1 == v7) << std::endl;
+ Php::out << (v1 == v8) << std::endl;
+
+ Php::out << (v2 == v5) << std::endl;
+ Php::out << (v2 == v6) << std::endl;
+ Php::out << (v2 == v7) << std::endl;
+ Php::out << (v2 == v8) << std::endl;
+
+ Php::out << (v3 == v5) << std::endl;
+ Php::out << (v3 == v6) << std::endl;
+ Php::out << (v3 == v7) << std::endl;
+ Php::out << (v3 == v8) << std::endl;
+
+ Php::out << (v4 == v5) << std::endl;
+ Php::out << (v4 == v6) << std::endl;
+ Php::out << (v4 == v7) << std::endl;
+ Php::out << (v4 == v8) << std::endl;
+
+ Php::Value v9, v10, v11, v12;
+ v9[0] = 5;
+ v9[1] = 6;
+
+ v10[0] = 5;
+ v10[1] = "Hello!";
+
+ v11[0] = 5;
+ v11[1] = 6;
+
+ v12[0] = 5;
+
+ Php::out << "Compare array:" << std::endl;
+ Php::out << (v1 == v9) << std::endl;
+ Php::out << (v5 == v9) << std::endl;
+ Php::out << (v9 == v10) << std::endl;
+ Php::out << (v11 == v9) << std::endl;
+ Php::out << (v12 == v9) << std::endl;
+
+ Php::Value v13 = false, v14, v15 = 0;
+ Php::out << "Compare NULL:" << std::endl;
+ Php::out << (v1 == v13) << std::endl;
+ Php::out << (v1 == v14) << std::endl;
+ Php::out << (v1 == v15) << std::endl;
+
+ Php::out << (v13 == v14) << std::endl;
+ Php::out << (v13 == v15) << std::endl;
+ Php::out << (v14 == v15) << std::endl;
+ }
+
+ /*
+ * Test bool Value::operator< (const Value &value) const
+ */
+ void test_compare2()
+ {
+ Php::Value v1(5), v2(5.0), v3("5"), v4("5.0");
+
+ Php::out << "false:" << std::endl;
+ Php::out << (v1 < v2) << std::endl;
+ Php::out << (v1 < v3) << std::endl;
+ Php::out << (v1 < v4) << std::endl;
+ Php::out << (v2 < v1) << std::endl;
+ Php::out << (v2 < v3) << std::endl;
+ Php::out << (v2 < v4) << std::endl;
+ Php::out << (v3 < v1) << std::endl;
+ Php::out << (v3 < v2) << std::endl;
+ Php::out << (v3 < v4) << std::endl;
+ Php::out << (v4 < v1) << std::endl;
+ Php::out << (v4 < v2) << std::endl;
+ Php::out << (v4 < v3) << std::endl;
+
+ Php::Value v5(6), v6(6.0), v7("6"), v8("6.0");
+
+ Php::out << "true:" << std::endl;
+ Php::out << (v1 < v5) << std::endl;
+ Php::out << (v1 < v6) << std::endl;
+ Php::out << (v1 < v7) << std::endl;
+ Php::out << (v1 < v8) << std::endl;
+
+ Php::out << (v2 < v5) << std::endl;
+ Php::out << (v2 < v6) << std::endl;
+ Php::out << (v2 < v7) << std::endl;
+ Php::out << (v2 < v8) << std::endl;
+
+ Php::out << (v3 < v5) << std::endl;
+ Php::out << (v3 < v6) << std::endl;
+ Php::out << (v3 < v7) << std::endl;
+ Php::out << (v3 < v8) << std::endl;
+
+ Php::out << (v4 < v5) << std::endl;
+ Php::out << (v4 < v6) << std::endl;
+ Php::out << (v4 < v7) << std::endl;
+ Php::out << (v4 < v8) << std::endl;
+
+ Php::out << "false:" << std::endl;
+ Php::out << (v1 > v5) << std::endl;
+ Php::out << (v1 > v6) << std::endl;
+ Php::out << (v1 > v7) << std::endl;
+ Php::out << (v1 > v8) << std::endl;
+
+ Php::out << (v2 > v5) << std::endl;
+ Php::out << (v2 > v6) << std::endl;
+ Php::out << (v2 > v7) << std::endl;
+ Php::out << (v2 > v8) << std::endl;
+
+ Php::out << (v3 > v5) << std::endl;
+ Php::out << (v3 > v6) << std::endl;
+ Php::out << (v3 > v7) << std::endl;
+ Php::out << (v3 > v8) << std::endl;
+
+ Php::out << (v4 > v5) << std::endl;
+ Php::out << (v4 > v6) << std::endl;
+ Php::out << (v4 > v7) << std::endl;
+ Php::out << (v4 > v8) << std::endl;
+
+ Php::Value v9, v10, v11, v12;
+ v9[0] = 5;
+ v9[1] = 6;
+
+ v10[0] = 5;
+ v10[1] = "Hello!";
+
+ v11[0] = 5;
+ v11[1] = 6;
+
+ v12[0] = 5;
+
+ Php::out << "Compare array:" << std::endl;
+ Php::out << (v1 < v9) << std::endl;
+ Php::out << (v5 < v9) << std::endl;
+ Php::out << (v9 < v10) << std::endl;
+ Php::out << (v9 > v10) << std::endl;
+ Php::out << (v11 < v9) << std::endl;
+ Php::out << (v12 < v9) << std::endl;
+
+ Php::Value v13 = false, v14, v15 = 0;
+ Php::out << "Compare NULL:" << std::endl;
+ Php::out << (v1 < v13) << std::endl;
+ Php::out << (v1 < v14) << std::endl;
+ Php::out << (v1 < v15) << std::endl;
+
+ Php::out << (v1 > v13) << std::endl;
+ Php::out << (v1 > v14) << std::endl;
+ Php::out << (v1 > v15) << std::endl;
+
+ Php::out << (v13 < v14) << std::endl;
+ Php::out << (v13 < v15) << std::endl;
+ Php::out << (v14 < v15) << std::endl;
+ }
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/tests/cpp/main.cpp b/tests/cpp/main.cpp
index 37ba5edd..47cd90d5 100644
--- a/tests/cpp/main.cpp
+++ b/tests/cpp/main.cpp
@@ -125,6 +125,9 @@ extern "C"
extension.add("TestVariables\\post_raw1", TestVariables::post_raw1);
extension.add("TestVariables\\post_raw2", TestVariables::post_raw2);
extension.add("TestVariables\\test_env", TestVariables::test_env);
+ extension.add("TestVariables\\test_compare1", TestVariables::test_compare1);
+ extension.add("TestVariables\\test_compare2", TestVariables::test_compare2);
+
diff --git a/tests/php/phpt/variables/028-compare1.phpt b/tests/php/phpt/variables/028-compare1.phpt
new file mode 100644
index 00000000..2885fe8f
--- /dev/null
+++ b/tests/php/phpt/variables/028-compare1.phpt
@@ -0,0 +1,53 @@
+--TEST--
+Test bool Value::operator==(const Value &value) const
+--SKIPIF--
+
+--FILEEOF--
+
+--FILEEOF--
+value.lval == 0
- ) ||
- ( (thisTp == Type::String) && _val->value.str.len == 0);
- }
- if(thisTp == Type::Null)
- {
- return (
- (thatTp == Type::Numeric || thatTp == Type::Bool) &&
- value._val->value.lval == 0
- ) ||
- ( (thatTp == Type::String) && value._val->value.str.len == 0);
- }
-
- if(thatTp == Type::Float)
- {
- return clone(Type::Float) == value._val->value.dval;
- }
- if(thisTp == Type::Float)
- {
- return value.clone(Type::Float) == _val->value.dval;
- }
-
- if(thatTp == Type::String)
- {
- return clone(Type::String) == value.rawValue();
- }
- if(thisTp == Type::String)
- {
- return value.clone(Type::String) == rawValue();
- }
-
- if(thatTp == Type::Numeric || thatTp == Type::Bool)
- {
- return clone(thatTp) == value._val->value.lval;
- }
- if(thisTp == Type::String)
- {
- return value.clone(thisTp) == _val->value.lval;
- }
-
-
- if( (thatTp == Type::Object && thisTp != Type::Object) || (thisTp == Type::Object && thatTp != Type::Object) )
+ zval result;
+ if(SUCCESS != compare_function(&result, _val, value._val TSRMLS_CC) )
{
+ throw Php::Exception("Not comparable");
return false;
}
- if(thatTp == Type::Object && thisTp == Type::Object)
- {
- return !ClassImpl::compare(_val, value._val TSRMLS_CC);
- }
-
- return false;
+ return (0 == result.value.lval);
}
/**
@@ -1430,23 +1332,13 @@ bool Value::operator==(const Value &value) const
*/
bool Value::operator< (const Value &value) const
{
- if(!comparePrepare(value)) return false;
-
- Type tp = type();
- const _zval_struct *thatval = (tp == value.type()) ? value._val : value.clone(tp)._val;
-
- switch (tp) {
- case Type::Null: return false;
- case Type::Numeric: return (_val->value.lval < thatval->value.lval);
- case Type::Float: return (_val->value.dval < thatval->value.dval);
- case Type::Bool: return (_val->value.lval < thatval->value.lval);
- case Type::String: return (::strcmp(_val->value.str.val, thatval->value.str.val) < 0);
- // @todo
- case Type::Array: throw Php::Exception("TODO implement comparison arrays"); break;
- // @todo
- case Type::Object: throw Php::Exception("TODO implement comparison arrays"); break;
+ zval result;
+ if(SUCCESS != compare_function(&result, _val, value._val TSRMLS_CC) )
+ {
+ throw Php::Exception("Not comparable");
+ return false;
}
- return true;
+ return (-1 == result.value.lval);
}
/**
From 7b64409dfb1e399c7c980fd3227d5c0432c39e6c Mon Sep 17 00:00:00 2001
From: valmat
Date: Thu, 19 Jun 2014 00:03:46 +0600
Subject: [PATCH 11/41] implementation issue #97
---
include/class.h | 30 ++++++++++++++++++++++++++++--
1 file changed, 28 insertions(+), 2 deletions(-)
diff --git a/include/class.h b/include/class.h
index e0316df9..cffc7bd3 100644
--- a/include/class.h
+++ b/include/class.h
@@ -201,6 +201,32 @@ class Class : private ClassBase
Class &extends(const Class &base) { ClassBase::extends(base); return *this; }
private:
+ /**
+ * Method to create the object if it is default constructable
+ * @param orig
+ * @return Base*
+ */
+ template
+ typename std::enable_if::value, Base*>::type
+ static maybeConstruct()
+ {
+ // create a new instance
+ return new X();
+ }
+
+ /**
+ * Method to create the object if it is not default constructable
+ * @param orig
+ * @return Base*
+ */
+ template
+ typename std::enable_if::value, Base*>::type
+ static maybeConstruct()
+ {
+ // create empty instance
+ return nullptr;
+ }
+
/**
* Construct a new instance of the object
* @return Base
@@ -208,7 +234,7 @@ class Class : private ClassBase
virtual Base* construct() const override
{
// construct an instance
- return new T();
+ return maybeConstruct();
}
/**
@@ -225,7 +251,7 @@ class Class : private ClassBase
}
/**
- * Method to clone the object if it is copy constructable
+ * Method to clone the object if it is not copy constructable
* @param orig
* @return Base*
*/
From c6c68cbc60711b43e8a570d708db3768240fcc5a Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Thu, 19 Jun 2014 13:48:59 +0200
Subject: [PATCH 12/41] errors are no longer thrown as exceptions, but are php
fatal errors, so that they more closely match the zend error reporting system
---
include/fatalerror.h | 66 ++++++++++++++++++++++++++++++++++++++++++++
zend/fatalerror.cpp | 31 +++++++++++++++++++++
2 files changed, 97 insertions(+)
create mode 100644 include/fatalerror.h
create mode 100644 zend/fatalerror.cpp
diff --git a/include/fatalerror.h b/include/fatalerror.h
new file mode 100644
index 00000000..b178f250
--- /dev/null
+++ b/include/fatalerror.h
@@ -0,0 +1,66 @@
+/**
+ * FatalError.h
+ *
+ *
+ * Normally, fatal errors are reported with a call to zend_error().
+ *
+ * However, this will trigger a longjmp(), which will cause objects
+ * constructed in the extension not to be destructed. We use therefore
+ * this FatalError class, which is a normally exception that _does_
+ * cause objects to be destructed.
+ *
+ * When it is caught, right before control is handed back to the Zend
+ * engine, it will turn the exception into a zend_error() call and
+ * thus a longjmp.
+ *
+ * @author Emiel Bruijntjes
+ * @copyright 2014 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class FatalError : public Exception
+{
+public:
+ /**
+ * Constructor
+ * @param message
+ */
+ FatalError(const std::string &message) : Exception(message) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~FatalError() throw()
+ {
+ }
+
+ /**
+ * Is this a native exception (one that was thrown from C++ code)
+ * @return bool
+ */
+ virtual bool native() const
+ {
+ // although it is native, we return 0 because it should not persist
+ // as exception, but it should live on as zend_error() in stead
+ return false;
+ }
+
+ /**
+ * Report this error as a fatal error
+ * @return bool
+ */
+ virtual bool report() const override;
+};
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/zend/fatalerror.cpp b/zend/fatalerror.cpp
new file mode 100644
index 00000000..e57be737
--- /dev/null
+++ b/zend/fatalerror.cpp
@@ -0,0 +1,31 @@
+/**
+ * FatalError.cpp
+ *
+ * @author Emiel Bruijntjes
+ * @copyright 2014 Copernica BV
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Report this error as a fatal error
+ * @return bool
+ */
+bool FatalError::report() const
+{
+ // report the error
+ zend_error(E_ERROR, "%s", what());
+
+ // return true: it was reported
+ return true;
+}
+
+/**
+ * End of namespace
+ */
+}
+
From ca60a32c601fe9b0236d3d4717c7b94368a3c172 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Thu, 19 Jun 2014 13:49:07 +0200
Subject: [PATCH 13/41] errors are no longer thrown as exceptions, but are php
fatal errors, so that they more closely match the zend error reporting system
---
include/array.h | 10 ++++-----
include/exception.h | 10 +++++++++
include/object.h | 8 +++----
phpcpp.h | 1 +
zend/classimpl.cpp | 18 ++++++++++------
zend/includes.h | 1 +
zend/object.cpp | 18 ++++++++++------
zend/origexception.h | 6 ++++--
zend/streambuf.cpp | 3 +--
zend/value.cpp | 50 +++++++++++++++++++++++++++-----------------
10 files changed, 81 insertions(+), 44 deletions(-)
diff --git a/include/array.h b/include/array.h
index 0b6ceb9b..4d6b6b18 100644
--- a/include/array.h
+++ b/include/array.h
@@ -31,7 +31,7 @@ class Array : public Value
Array(const Value &value) : Value(value)
{
// type must be valid
- if (value.type() != Type::Array) throw Php::Exception("Assigning a non-array to an array variable");
+ if (value.type() != Type::Array) throw FatalError("Assigning a non-array to an array variable");
}
/**
@@ -41,7 +41,7 @@ class Array : public Value
Array(Value &&value) : Value(std::move(value))
{
// type must be valid
- if (value.type() != Type::Array) throw Php::Exception("Moving a non-array to an array variable");
+ if (value.type() != Type::Array) throw FatalError("Moving a non-array to an array variable");
}
/**
@@ -76,7 +76,7 @@ class Array : public Value
virtual Value &setType(Type type) override
{
// throw exception if things are going wrong
- if (type != Type::Array) throw Php::Exception("Changing type of a fixed array variable");
+ if (type != Type::Array) throw FatalError("Changing type of a fixed array variable");
// call base
return Value::setType(Type::Array);
@@ -93,7 +93,7 @@ class Array : public Value
if (this == &value) return *this;
// type must be valid
- if (value.type() != Type::Array) throw Php::Exception("Assigning a non-array to a fixed array variable");
+ if (value.type() != Type::Array) throw FatalError("Assigning a non-array to a fixed array variable");
// call base
Value::operator=(value);
@@ -113,7 +113,7 @@ class Array : public Value
if (this == &value) return *this;
// type must be valid
- if (value.type() != Type::Array) throw Php::Exception("Moving a non-array to a fixed array variable");
+ if (value.type() != Type::Array) throw FatalError("Moving a non-array to a fixed array variable");
// call base
Value::operator=(std::move(value));
diff --git a/include/exception.h b/include/exception.h
index 671df9e5..aff0afc3 100644
--- a/include/exception.h
+++ b/include/exception.h
@@ -75,6 +75,16 @@ class Exception : public std::exception
// yes, it is native
return true;
}
+
+ /**
+ * Report this error as a fatal error
+ * @return bool
+ */
+ virtual bool report() const
+ {
+ // this is not done here
+ return false;
+ }
};
/**
diff --git a/include/object.h b/include/object.h
index 97c9482b..2e6628a9 100644
--- a/include/object.h
+++ b/include/object.h
@@ -31,7 +31,7 @@ class Object : public Value
Object(Value &&value) : Value(std::move(value))
{
// throw exception in case of problems
- if (value.type() != Type::Object) throw Php::Exception("Constructing an object variable by moving a non object");
+ if (value.type() != Type::Object) throw FatalError("Constructing an object variable by moving a non object");
}
/**
@@ -123,7 +123,7 @@ class Object : public Value
virtual Value &setType(Type type) override
{
// throw exception if things are going wrong
- if (type != Type::Object) throw Php::Exception("Changing type of a fixed object variable");
+ if (type != Type::Object) throw FatalError("Changing type of a fixed object variable");
// call base
return Value::setType(type);
@@ -140,7 +140,7 @@ class Object : public Value
if (this == &value) return *this;
// type must be valid
- if (value.type() != Type::Object) throw Php::Exception("Assigning a non-object to an object variable");
+ if (value.type() != Type::Object) throw FatalError("Assigning a non-object to an object variable");
// call base
Value::operator=(value);
@@ -160,7 +160,7 @@ class Object : public Value
if (this == &value) return *this;
// type must be valid
- if (value.type() != Type::Object) throw Php::Exception("Moving a non-object to an object variable");
+ if (value.type() != Type::Object) throw FatalError("Moving a non-object to an object variable");
// call base
Value::operator=(std::move(value));
diff --git a/phpcpp.h b/phpcpp.h
index 3f94cefa..c3f33659 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -29,6 +29,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index 953738a9..ca75c5e8 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -524,8 +524,10 @@ zend_object_value ClassImpl::cloneObject(zval *val TSRMLS_DC)
// 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
- // a copy constructor)
- if (!cpp) throw Php::Exception(std::string("Unable to clone ") + entry->name);
+ // a copy constructor). Because this function is directly called from the
+ // Zend engine, we can call zend_error() (which does a longjmp()) to throw
+ // an exception back to the Zend engine)
+ if (!cpp) zend_error(E_ERROR, "Unable to clone %s", entry->name);
// the thing we're going to return
zend_object_value result;
@@ -1183,8 +1185,10 @@ zend_object_value ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
// create a new base C++ object
auto *cpp = impl->_base->construct();
- // report error on failure
- if (!cpp) throw Php::Exception(std::string("Unable to instantiate ") + entry->name);
+ // report error on failure, because this function is called directly from the
+ // Zend engine, we can call zend_error() here (which does a longjmp() back to
+ // the Zend engine)
+ if (!cpp) zend_error(E_ERROR, "Unable to instantiate %s", entry->name);
// the thing we're going to return
zend_object_value result;
@@ -1212,8 +1216,10 @@ zend_object_value ClassImpl::createObject(zend_class_entry *entry TSRMLS_DC)
*/
zend_object_iterator *ClassImpl::getIterator(zend_class_entry *entry, zval *object, int by_ref TSRMLS_DC)
{
- // by-ref is not possible (copied from SPL)
- if (by_ref) throw Php::Exception("Foreach by ref is not possible");
+ // by-ref is not possible (copied from SPL), this function is called directly
+ // from the Zend engine, so we can use zend_error() to longjmp() back to the
+ // Zend engine)
+ if (by_ref) zend_error(E_ERROR, "Foreach by ref is not possible");
// retrieve the traversable object
Traversable *traversable = dynamic_cast(ObjectImpl::find(object TSRMLS_CC)->object());
diff --git a/zend/includes.h b/zend/includes.h
index cabb0969..63b435eb 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -48,6 +48,7 @@
#include "../include/inivalue.h"
#include "../include/ini.h"
#include "../include/exception.h"
+#include "../include/fatalerror.h"
#include "../include/streams.h"
#include "../include/type.h"
#include "../include/hashparent.h"
diff --git a/zend/object.cpp b/zend/object.cpp
index 940b1436..dc548cc1 100644
--- a/zend/object.cpp
+++ b/zend/object.cpp
@@ -32,9 +32,12 @@ Object::Object(const char *name, Base *base)
// this is a brand new object that should be allocated, the C++ instance
// is already there (created by the extension) but it is not yet stored
- // in PHP, find out the classname first
- auto *entry = zend_fetch_class(name, ::strlen(name), 0 TSRMLS_CC);
- if (!entry) throw Php::Exception(std::string("Unknown class name ") + name);
+ // in PHP, find out the classname first (we use the FatalError class
+ // here because this function is called from C++ context, and zend_error()
+ // would cause a longjmp() which does not clean up C++ objects created
+ // by the extension).
+ auto *entry = zend_fetch_class(name, ::strlen(name), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
+ if (!entry) throw FatalError(std::string("Unknown class name ") + name);
// construct an implementation (this will also set the implementation
// member in the base object)
@@ -57,9 +60,12 @@ void Object::instantiate(const char *name)
// we need the tsrm_ls variable
TSRMLS_FETCH();
- // convert the name into a class_entry
- auto *entry = zend_fetch_class(name, ::strlen(name), 0 TSRMLS_CC);
- if (!entry) throw Php::Exception(std::string("Unknown class name ") + name);
+ // convert the name into a class_entry (we use the FatalError class
+ // here because this function is called from C++ context, and zend_error()
+ // would cause a longjmp() which does not clean up C++ objects created
+ // by the extension).
+ auto *entry = zend_fetch_class(name, ::strlen(name), ZEND_FETCH_CLASS_SILENT TSRMLS_CC);
+ if (!entry) throw FatalError(std::string("Unknown class name ") + name);
// initiate the zval (which was already allocated in the base constructor)
object_init_ex(_val, entry);
diff --git a/zend/origexception.h b/zend/origexception.h
index 775e4129..55f89cf2 100644
--- a/zend/origexception.h
+++ b/zend/origexception.h
@@ -127,10 +127,12 @@ inline void process(Exception &exception TSRMLS_DC)
// 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
+
+ // or does it have its own report function?
+ else if (!exception.report())
{
// 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
+ // php script, and then not caught by the c++ of the extension, we are
// going to tell to the exception that it is still active
OrigException &orig = static_cast(exception);
diff --git a/zend/streambuf.cpp b/zend/streambuf.cpp
index 86e5f034..11d928d1 100644
--- a/zend/streambuf.cpp
+++ b/zend/streambuf.cpp
@@ -33,7 +33,6 @@ int StreamBuf::sync()
// not null terminated and (2) it could contain % signs and allow all
// sorts of buffer overflows.
zend_error(_error, "%.*s", (int)size, pbase());
-
}
else
{
@@ -52,4 +51,4 @@ int StreamBuf::sync()
* End namespace
*/
}
-
\ No newline at end of file
+
diff --git a/zend/value.cpp b/zend/value.cpp
index 6458bd5c..9a1a5dbb 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -192,7 +192,7 @@ Value::Value(const Base *object)
auto *impl = object->implementation();
// do we have a handle?
- if (!impl) throw Php::Exception("Assigning an unassigned object to a variable");
+ if (!impl) throw FatalError("Assigning an unassigned object to a variable");
// make a regular zval, and set it to an object
MAKE_STD_ZVAL(_val);
@@ -1317,28 +1317,37 @@ Value Value::exec(const char *name, int argc, struct _zval_struct ***params)
*/
bool Value::operator==(const Value &value) const
{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // zval that will hold the result of the comparison
zval result;
- if(SUCCESS != compare_function(&result, _val, value._val TSRMLS_CC) )
- {
- throw Php::Exception("Not comparable");
- return false;
- }
- return (0 == result.value.lval);
+
+ // run the comparison
+ if (SUCCESS != compare_function(&result, _val, value._val TSRMLS_CC)) return false;
+
+ // convert to boolean
+ return result.value.lval == 0;
}
/**
* Comparison operators< for hardcoded Value
* @param value
+ * @return bool
*/
-bool Value::operator< (const Value &value) const
+bool Value::operator<(const Value &value) const
{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // zval that will hold the result of the comparison
zval result;
- if(SUCCESS != compare_function(&result, _val, value._val TSRMLS_CC) )
- {
- throw Php::Exception("Not comparable");
- return false;
- }
- return (-1 == result.value.lval);
+
+ // run the comparison
+ if (SUCCESS != compare_function(&result, _val, value._val TSRMLS_CC)) return false;
+
+ // convert to boolean
+ return result.value.lval < 0;
}
/**
@@ -1364,7 +1373,10 @@ Value &Value::setType(Type type)
// if this is not a reference variable, we should detach it to implement copy on write
SEPARATE_ZVAL_IF_NOT_REF(&_val);
- // run the conversion
+ // run the conversion, when it fails we throw a fatal error which will
+ // in the end result in a zend_error() call. This FatalError class is necessary
+ // because a direct call to zend_error() will do a longjmp() which may not
+ // clean up the C++ objects created by the extension
switch (type) {
case Type::Null: convert_to_null(_val); break;
case Type::Numeric: convert_to_long(_val); break;
@@ -1373,10 +1385,10 @@ Value &Value::setType(Type type)
case Type::Array: convert_to_array(_val); break;
case Type::Object: convert_to_object(_val); break;
case Type::String: convert_to_string(_val); break;
- case Type::Resource: throw Php::Exception("Resource types can not be handled by the PHP-CPP library"); break;
- case Type::Constant: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::ConstantArray: throw Php::Exception("Constant types can not be assigned to a PHP-CPP library variable"); break;
- case Type::Callable: throw Php::Exception("Callable types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::Resource: throw FatalError("Resource types can not be handled by the PHP-CPP library"); break;
+ case Type::Constant: throw FatalError("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::ConstantArray: throw FatalError("Constant types can not be assigned to a PHP-CPP library variable"); break;
+ case Type::Callable: throw FatalError("Callable types can not be assigned to a PHP-CPP library variable"); break;
}
// done
From 80e9566d52a682b011e38083fbe18f233298f03e Mon Sep 17 00:00:00 2001
From: valmat
Date: Mon, 23 Jun 2014 20:16:58 +0600
Subject: [PATCH 14/41] Fixed issue #100
---
zend/classimpl.cpp | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index ca75c5e8..23c39cfb 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -1382,14 +1382,25 @@ void ClassImpl::initialize(ClassBase *base, const std::string &prefix TSRMLS_DC)
if (_parent)
{
// check if the base class was already defined
- if (_parent->_entry) entry.parent = _parent->_entry;
-
+ if (_parent->_entry)
+ {
+ // register the class
+ _entry = zend_register_internal_class_ex(&entry, _parent->_entry, const_cast(_parent->name().c_str()) TSRMLS_CC);
+ }
+
// otherwise an error is reported
- else std::cerr << "Derived class " << name() << " is initialized before base class " << _parent->name() << ": base class is ignored" << std::endl;
+ else
+ {
+ std::cerr << "Derived class " << name() << " is initialized before base class " << _parent->name() << ": base class is ignored" << std::endl;
+ // register the class
+ _entry = zend_register_internal_class(&entry TSRMLS_CC);
+ }
+ }
+ else
+ {
+ // register the class
+ _entry = zend_register_internal_class(&entry TSRMLS_CC);
}
-
- // register the class
- _entry = zend_register_internal_class(&entry TSRMLS_CC);
// register the classes
for (auto &interface : _interfaces)
From d0dbed9ed21123455adcd0277991036ad9bdbb06 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Tue, 8 Jul 2014 15:03:43 +0200
Subject: [PATCH 15/41] in the documentation and source code comments, the old
method name Value::resize() was used, while the actual name is reserve(),
solves issue #102
---
documentation/variables.html | 6 +++---
include/value.h | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/documentation/variables.html b/documentation/variables.html
index 98a6d492..dfc5b18e 100644
--- a/documentation/variables.html
+++ b/documentation/variables.html
@@ -223,10 +223,10 @@ Strings
// result variable
Php::Value result;
- // resize the buffer to 4096 bytes, the resize() method resizes
+ // resize the buffer to 4096 bytes, the reserve() method resizes
// the internal buffer to the appropriate size, and returns a pointer
// to the buffer
- char *buffer = result.resize(4096);
+ char *buffer = result.reserve(4096);
// read in the bytes directly into the just allocated buffer
ssize_t bytes = read(fd, buffer, 4096);
@@ -235,7 +235,7 @@ Strings
// resize the buffer to the actual number of bytes in it (this
// is necessary, otherwise the PHP strlen() returns 4096 even
// when less bytes were available
- result.resize(bytes);
+ result.reserve(bytes);
// return the result
return result;
diff --git a/include/value.h b/include/value.h
index e8337a03..f72304cf 100644
--- a/include/value.h
+++ b/include/value.h
@@ -387,7 +387,7 @@ class Value : private HashParent
* variables - other variables return nullptr.
*
* If you are going to write to the buffer, make sure that you first call
- * the resize() method to ensure that the buffer is big enough.
+ * the reserve() method to ensure that the buffer is big enough.
*
* @return char *
*/
@@ -396,7 +396,7 @@ class Value : private HashParent
/**
* Resize buffer space. If you want to write directly to the buffer (which
* is returned by the buffer() method), you should first reserve enough
- * space in it. This can be done with this resize() method. This will also
+ * space in it. This can be done with this reserve() method. This will also
* turn the Value object into a string (if it was not already a string).
* The writable buffer is returned.
*
From 28578382589dab25ea5fbd35b7754687706abc07 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Tue, 8 Jul 2014 15:15:00 +0200
Subject: [PATCH 16/41] changed comments to match coding style
---
zend/classimpl.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index 23c39cfb..b2acc24a 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -1387,12 +1387,12 @@ void ClassImpl::initialize(ClassBase *base, const std::string &prefix TSRMLS_DC)
// register the class
_entry = zend_register_internal_class_ex(&entry, _parent->_entry, const_cast(_parent->name().c_str()) TSRMLS_CC);
}
-
- // otherwise an error is reported
else
{
+ // report an error - the extension programmer probably made an error
std::cerr << "Derived class " << name() << " is initialized before base class " << _parent->name() << ": base class is ignored" << std::endl;
- // register the class
+
+ // register the class, but without the base class
_entry = zend_register_internal_class(&entry TSRMLS_CC);
}
}
From d24ce36eaa07e9e924e7084fafa4f504cde39c2f Mon Sep 17 00:00:00 2001
From: andot
Date: Thu, 10 Jul 2014 12:45:04 +0800
Subject: [PATCH 17/41] Implementation issue #98
---
include/fastcall.h | 13 +++++++++++++
phpcpp.h | 3 ++-
zend/fastcall.cpp | 40 ++++++++++++++++++++++++++++++++++++++++
zend/includes.h | 1 +
4 files changed, 56 insertions(+), 1 deletion(-)
create mode 100644 include/fastcall.h
create mode 100644 zend/fastcall.cpp
diff --git a/include/fastcall.h b/include/fastcall.h
new file mode 100644
index 00000000..29f274db
--- /dev/null
+++ b/include/fastcall.h
@@ -0,0 +1,13 @@
+/**
+ * fastcall.h
+ *
+ * This file holds some PHP functions implementation in C directly.
+ *
+ */
+
+namespace Php {
+
+ Value eval(const std::string &phpCode);
+
+}
+
diff --git a/phpcpp.h b/phpcpp.h
index c3f33659..8be1a251 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -2,7 +2,7 @@
* phpcpp.h
*
* Library to build PHP extensions with CPP
- *
+ *
* @copyright 2013 CopernicA BV
* @author Emiel Bruijntjes
*/
@@ -60,6 +60,7 @@
#include
#include
#include
+#include
/**
* Macro to export a function
diff --git a/zend/fastcall.cpp b/zend/fastcall.cpp
new file mode 100644
index 00000000..3ecd5987
--- /dev/null
+++ b/zend/fastcall.cpp
@@ -0,0 +1,40 @@
+/**
+ * fastcall.cpp
+ *
+ * This file holds some PHP functions implementation in C directly.
+ *
+ */
+
+#include "includes.h"
+
+namespace Php {
+
+ Value eval(const std::string &phpCode) {
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // the current exception
+ zval* oldException = EG(exception);
+
+ // the return zval
+ zval* retval = nullptr;
+ if (zend_eval_stringl_ex((char *)phpCode.c_str(), (int32_t)phpCode.length(), retval, (char *)"", 1 TSRMLS_CC) != SUCCESS)
+ {
+ // throw an exception, php couldn't evaluate code
+ throw Exception("PHP couldn't evaluate: " + phpCode);
+
+ // unreachable, but let's return at least something to prevent compiler warnings
+ return nullptr;
+ }
+ else
+ {
+ // 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) TSRMLS_CC);
+
+ // no (additional) exception was thrown
+ return retval ? Value(retval) : nullptr;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/zend/includes.h b/zend/includes.h
index 63b435eb..8af557cc 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -79,6 +79,7 @@
#include "../include/namespace.h"
#include "../include/extension.h"
#include "../include/call.h"
+#include "../include/fastcall.h"
/**
* Common header files for internal use only
From 894ef44bacd1f44f6f7f3c74284af94ddda89f7e Mon Sep 17 00:00:00 2001
From: andot
Date: Thu, 10 Jul 2014 12:54:31 +0800
Subject: [PATCH 18/41] Add Php::class_exists implementation.
---
include/fastcall.h | 13 +++++++++++++
phpcpp.h | 3 ++-
zend/fastcall.cpp | 42 ++++++++++++++++++++++++++++++++++++++++++
zend/includes.h | 1 +
4 files changed, 58 insertions(+), 1 deletion(-)
create mode 100644 include/fastcall.h
create mode 100644 zend/fastcall.cpp
diff --git a/include/fastcall.h b/include/fastcall.h
new file mode 100644
index 00000000..a5a8e1c4
--- /dev/null
+++ b/include/fastcall.h
@@ -0,0 +1,13 @@
+/**
+ * fastcall.h
+ *
+ * This file holds some PHP functions implementation in C directly.
+ *
+ */
+
+namespace Php {
+
+ bool class_exists(const std::string &classname, bool autoload = true);
+
+}
+
diff --git a/phpcpp.h b/phpcpp.h
index c3f33659..8be1a251 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -2,7 +2,7 @@
* phpcpp.h
*
* Library to build PHP extensions with CPP
- *
+ *
* @copyright 2013 CopernicA BV
* @author Emiel Bruijntjes
*/
@@ -60,6 +60,7 @@
#include
#include
#include
+#include
/**
* Macro to export a function
diff --git a/zend/fastcall.cpp b/zend/fastcall.cpp
new file mode 100644
index 00000000..e198e699
--- /dev/null
+++ b/zend/fastcall.cpp
@@ -0,0 +1,42 @@
+/**
+ * fastcall.cpp
+ *
+ * This file holds some PHP functions implementation in C directly.
+ *
+ */
+
+#include "includes.h"
+
+namespace Php {
+
+ bool class_exists(const std::string &classname, bool autoload) {
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ zend_class_entry **ce;
+ int found;
+ const char * str = classname.c_str();
+ int32_t len = (int32_t)classname.length();
+
+
+ if (autoload) {
+ char lc_name[len + 1];
+ zend_str_tolower_copy(lc_name, str, len);
+
+ char *name = lc_name;
+ if (lc_name[0] == '\\') {
+ name = &lc_name[1];
+ --len;
+ }
+
+ found = zend_hash_find(EG(class_table), name, len + 1, (void **) &ce);
+ return (found == SUCCESS && !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS));
+ }
+
+ if (zend_lookup_class(str, len, &ce TSRMLS_CC) == SUCCESS) {
+ return (((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0);
+ }
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/zend/includes.h b/zend/includes.h
index 63b435eb..8af557cc 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -79,6 +79,7 @@
#include "../include/namespace.h"
#include "../include/extension.h"
#include "../include/call.h"
+#include "../include/fastcall.h"
/**
* Common header files for internal use only
From b6cc95e9a96edf3a1733c4f562d42eed79dd8946 Mon Sep 17 00:00:00 2001
From: andot
Date: Thu, 10 Jul 2014 13:06:22 +0800
Subject: [PATCH 19/41] Add Php::is_a implementation.
Add is, isSubClassOf for Php::Value.
Add Php::is_a implementation.
Add Php::is_subclass_of implementation.
---
include/fastcall.h | 18 ++++++++++++++++++
include/value.h | 21 +++++++++++++++++++++
phpcpp.h | 3 ++-
zend/includes.h | 1 +
zend/value.cpp | 38 ++++++++++++++++++++++++++++++++++++++
5 files changed, 80 insertions(+), 1 deletion(-)
create mode 100644 include/fastcall.h
diff --git a/include/fastcall.h b/include/fastcall.h
new file mode 100644
index 00000000..778a9b2c
--- /dev/null
+++ b/include/fastcall.h
@@ -0,0 +1,18 @@
+/**
+ * fastcall.h
+ *
+ * This file holds some PHP functions implementation in C directly.
+ *
+ */
+
+namespace Php {
+
+ inline bool is_a(const Value &obj, const std::string classname, bool allow_string = false) {
+ return obj.is(classname, allow_string);
+ }
+ inline bool is_subclass_of(const Value &obj, const std::string classname, bool allow_string = true) {
+ return obj.isSubClassOf(classname, allow_string);
+ }
+
+}
+
diff --git a/include/value.h b/include/value.h
index f72304cf..b3c288c9 100644
--- a/include/value.h
+++ b/include/value.h
@@ -965,7 +965,28 @@ class Value : private HashParent
return dynamic_cast(base);
}
+ /**
+ * Checks if this object is of the class or has the class as one of its parents
+ * @param classname
+ * @param allow_string
+ * @return bool
+ */
+ inline bool is(const std::string &classname, bool allow_string=false) const {
+ return isImpl(classname, allow_string, false);
+ }
+
+ /**
+ * Checks if this object has the class as one of its parents
+ * @param classname
+ * @return bool
+ */
+ inline bool isSubClassOf(const std::string &classname, bool allow_string=true) const {
+ return isImpl(classname, allow_string, true);
+ }
+
private:
+
+ bool isImpl(const std::string &classname, bool allow_string, bool only_subclass) const;
/**
* Call function with a number of parameters
* @param argc Number of parameters
diff --git a/phpcpp.h b/phpcpp.h
index c3f33659..8be1a251 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -2,7 +2,7 @@
* phpcpp.h
*
* Library to build PHP extensions with CPP
- *
+ *
* @copyright 2013 CopernicA BV
* @author Emiel Bruijntjes
*/
@@ -60,6 +60,7 @@
#include
#include
#include
+#include
/**
* Macro to export a function
diff --git a/zend/includes.h b/zend/includes.h
index 63b435eb..8af557cc 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -79,6 +79,7 @@
#include "../include/namespace.h"
#include "../include/extension.h"
#include "../include/call.h"
+#include "../include/fastcall.h"
/**
* Common header files for internal use only
diff --git a/zend/value.cpp b/zend/value.cpp
index 9a1a5dbb..38a0425f 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1408,6 +1408,44 @@ bool Value::isCallable() const
return zend_is_callable(_val, 0, NULL TSRMLS_CC);
}
+bool Value::isImpl(const std::string &classname, bool allow_string, bool only_subclass) const {
+ /*
+ * allow_string - is default is false, isSubclassOf is true.
+ * if it's allowed, the the autoloader will be called if the class does not exist.
+ * default behaviour is different, as 'is' used to be used to test mixed return
+ * values and there is no easy way to deprecate this.
+ */
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ zend_class_entry *instance_ce;
+ zend_class_entry **ce;
+
+ if (allow_string && isString()) {
+ zend_class_entry **the_ce;
+ if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &the_ce TSRMLS_CC) == FAILURE) {
+ return false;
+ }
+ instance_ce = *the_ce;
+ }
+ else if (isObject() && HAS_CLASS_ENTRY(*_val)) {
+ instance_ce = Z_OBJCE_P(_val);
+ }
+ else {
+ return false;
+ }
+
+ if (zend_lookup_class_ex(classname.c_str(), (int32_t)classname.length(), NULL, 0, &ce TSRMLS_CC) == FAILURE) {
+ return false;
+ }
+
+ if (only_subclass && instance_ce == *ce) {
+ return false;
+ }
+
+ return instanceof_function(instance_ce, *ce TSRMLS_CC);
+}
+
/**
* Make a clone of the type
* @return Value
From 6dba54f21a1972d0558299c865c03dcb3371d0b9 Mon Sep 17 00:00:00 2001
From: andot
Date: Thu, 10 Jul 2014 13:44:38 +0800
Subject: [PATCH 20/41] Fixed issue #107
---
include/value.h | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/value.h b/include/value.h
index f72304cf..8eac3945 100644
--- a/include/value.h
+++ b/include/value.h
@@ -100,7 +100,7 @@ class Value : private HashParent
* @param value
*/
template
- Value(const std::map &value)
+ Value(const std::map &value) : Value(Type::Array)
{
// set all elements
for (auto &iter : value) setRaw(iter.first.c_str(), iter.first.size(), iter.second);
@@ -502,6 +502,8 @@ class Value : private HashParent
// result variable
std::map result;
+
+ for (auto &iter : map) result[iter.first] = iter.second;
// done
return result;
From 15e6ddb931324bae6866e35898f41e066312a46b Mon Sep 17 00:00:00 2001
From: andot
Date: Thu, 10 Jul 2014 14:08:34 +0800
Subject: [PATCH 21/41] Fixed Object Constructor.
The old copy constructor of Php::Object forgot the call("__construct")
---
include/object.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/include/object.h b/include/object.h
index 2e6628a9..47935d70 100644
--- a/include/object.h
+++ b/include/object.h
@@ -42,8 +42,10 @@ class Object : public Value
Object(const Value &value) : Value()
{
// string types are instantiated
- if (value.isString()) instantiate(value);
-
+ if (value.isString()) {
+ instantiate(value);
+ call("__construct");
+ }
// otherwise copy the other object
else operator=(value);
}
From bf88eac6d32771891c8335e56f0d3fde6d53edd8 Mon Sep 17 00:00:00 2001
From: andot
Date: Fri, 11 Jul 2014 16:59:58 +0800
Subject: [PATCH 22/41] Fixed a bug of HashIterator
The old implementation of HashIterator can't support `"\0"` prefix key.
I think the array and the object is different. Maybe the user didn't
want to get the private property of an object. but in an array, `"\0"`
prefix key doesn't mean private key. so we should return `"\0"` prefix
key when it is an array.
---
zend/hashiterator.h | 16 +++++++++++-----
zend/value.cpp | 4 ++--
2 files changed, 13 insertions(+), 7 deletions(-)
diff --git a/zend/hashiterator.h b/zend/hashiterator.h
index b1f409f6..36c3d6cf 100644
--- a/zend/hashiterator.h
+++ b/zend/hashiterator.h
@@ -29,7 +29,7 @@ class HashIterator : public ValueIteratorImpl
* @param first Should it start on the first position?
* @param tsrm_ls
*/
- HashIterator(HashTable *hashtable, bool first) : _table(hashtable)
+ HashIterator(HashTable *hashtable, bool first, bool is_array = false) : _table(hashtable), _is_array(is_array)
{
// reset the hash pointer to the internal position
if (hashtable && first)
@@ -56,7 +56,7 @@ class HashIterator : public ValueIteratorImpl
* @param tsrm_ls
*/
HashIterator(const HashIterator &that TSRMLS_DC) :
- _table(that._table), _position(that._position)
+ _table(that._table), _position(that._position), _is_array(that._is_array)
{
// read current position
read();
@@ -166,7 +166,13 @@ class HashIterator : public ValueIteratorImpl
* @var HashPosition
*/
Bucket *_position = nullptr;
-
+
+ /**
+ * Is a hash interator in array
+ * @var bool
+ */
+ bool _is_array = false;
+
/**
* The current key and value
* @var std::pair
@@ -207,7 +213,7 @@ class HashIterator : public ValueIteratorImpl
// numeric keys are the easiest ones
if (type == HASH_KEY_IS_LONG) key = (int64_t)num_key;
- else key = string_key;
+ else key = std::string(string_key, str_len - 1);
#endif
@@ -223,7 +229,7 @@ class HashIterator : public ValueIteratorImpl
// if the key is private (it starts with a null character) we should return
// false to report that the object is not in a completely valid state
- return !_current.first.isString() || _current.first.rawValue()[0];
+ return _is_array || !_current.first.isString() || _current.first.rawValue()[0];
}
/**
diff --git a/zend/value.cpp b/zend/value.cpp
index 9a1a5dbb..352e90d3 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1626,7 +1626,7 @@ std::map Value::mapValue() const
ValueIterator Value::createIterator(bool begin) const
{
// check type
- if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), begin));
+ if (isArray()) return ValueIterator(new HashIterator(Z_ARRVAL_P(_val), begin, true));
// get access to the hast table
if (isObject())
@@ -1647,7 +1647,7 @@ ValueIterator Value::createIterator(bool begin) const
else
{
// construct a regular iterator
- return ValueIterator(new HashIterator(Z_OBJ_HT_P(_val)->get_properties(_val TSRMLS_CC), begin));
+ return ValueIterator(new HashIterator(Z_OBJPROP_P(_val), begin));
}
}
From 78c5a1463d51d8a1e92545d4dfda18845e1f23c4 Mon Sep 17 00:00:00 2001
From: andot
Date: Mon, 14 Jul 2014 21:40:39 +0800
Subject: [PATCH 23/41] Change C99 VLA C++ dynamic array
---
zend/fastcall.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/zend/fastcall.cpp b/zend/fastcall.cpp
index e198e699..359c3ada 100644
--- a/zend/fastcall.cpp
+++ b/zend/fastcall.cpp
@@ -20,7 +20,7 @@ namespace Php {
if (autoload) {
- char lc_name[len + 1];
+ char *lc_name = new char[len + 1];
zend_str_tolower_copy(lc_name, str, len);
char *name = lc_name;
@@ -30,6 +30,7 @@ namespace Php {
}
found = zend_hash_find(EG(class_table), name, len + 1, (void **) &ce);
+ delete [] lc_name;
return (found == SUCCESS && !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS));
}
From 3f1b209984d8932a667681278c6610772079b12b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ad=C3=A1n=20Lobato?=
Date: Mon, 21 Jul 2014 11:28:35 +0200
Subject: [PATCH 24/41] Fix typo
---
documentation/ten-reasons-for-using-php-cpp.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/documentation/ten-reasons-for-using-php-cpp.html b/documentation/ten-reasons-for-using-php-cpp.html
index 484f3fbd..7d830d4b 100644
--- a/documentation/ten-reasons-for-using-php-cpp.html
+++ b/documentation/ten-reasons-for-using-php-cpp.html
@@ -71,7 +71,7 @@ 7. Proven technology
C++ is a proven language with a more than 40 year long history. C++ has an
official open standard and is controlled by a C++ standards committee with
members that have proven track records. Compilers have been developed by companies
- like Microsoft, IBM, Intel, Apple and there there are several open source
+ like Microsoft, IBM, Intel, Apple and there are several open source
compilers available (GNU, CLANG), so you can always switch to a faster or
more stable alternative. The compiler vendors are constantly motivated to
be better than their competitors and bring out new versions of their compilers
From 340b05438b13fbe7cd937835e1abe935967330d3 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 13:39:16 +0200
Subject: [PATCH 25/41] renamed fastcall.cpp to eval.cpp, and moved the
Php::eval() definition to the call.h header file
---
include/call.h | 5 +++++
include/fastcall.h | 13 -----------
phpcpp.h | 1 -
zend/eval.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++
zend/fastcall.cpp | 40 ---------------------------------
zend/includes.h | 1 -
6 files changed, 61 insertions(+), 55 deletions(-)
delete mode 100644 include/fastcall.h
create mode 100644 zend/eval.cpp
delete mode 100644 zend/fastcall.cpp
diff --git a/include/call.h b/include/call.h
index 16de1fef..05c59556 100644
--- a/include/call.h
+++ b/include/call.h
@@ -12,6 +12,11 @@
*/
namespace Php {
+/**
+ * List of functions that are available for use in PHP
+ */
+Value eval(const std::string &phpCode);
+
/**
* Call a function in PHP
* @param name Name of the function to call
diff --git a/include/fastcall.h b/include/fastcall.h
deleted file mode 100644
index 29f274db..00000000
--- a/include/fastcall.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * fastcall.h
- *
- * This file holds some PHP functions implementation in C directly.
- *
- */
-
-namespace Php {
-
- Value eval(const std::string &phpCode);
-
-}
-
diff --git a/phpcpp.h b/phpcpp.h
index 8be1a251..baa91cca 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -60,7 +60,6 @@
#include
#include
#include
-#include
/**
* Macro to export a function
diff --git a/zend/eval.cpp b/zend/eval.cpp
new file mode 100644
index 00000000..e0baaed6
--- /dev/null
+++ b/zend/eval.cpp
@@ -0,0 +1,56 @@
+/**
+ * Eval.cpp
+ *
+ * This file holds the implementation for the Php::eval() function
+ *
+ * @author andot
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * Open PHP namespace
+ */
+namespace Php {
+
+/**
+ * Evaluate a PHP string
+ * @param phpCode The PHP code to evaluate
+ * @return Value The result of the evaluation
+ */
+Value eval(const std::string &phpCode)
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // the current exception
+ zval* oldException = EG(exception);
+
+ // the return zval
+ zval* retval = nullptr;
+ if (zend_eval_stringl_ex((char *)phpCode.c_str(), (int32_t)phpCode.length(), retval, (char *)"", 1 TSRMLS_CC) != SUCCESS)
+ {
+ // throw an exception, php couldn't evaluate code
+ throw Exception("PHP eval error");
+
+ // unreachable, but let's return at least something to prevent compiler warnings
+ return nullptr;
+ }
+ else
+ {
+ // 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) TSRMLS_CC);
+
+ // no (additional) exception was thrown
+ return retval ? Value(retval) : nullptr;
+ }
+}
+
+/**
+ * End of namespace
+ */
+}
diff --git a/zend/fastcall.cpp b/zend/fastcall.cpp
deleted file mode 100644
index 3ecd5987..00000000
--- a/zend/fastcall.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * fastcall.cpp
- *
- * This file holds some PHP functions implementation in C directly.
- *
- */
-
-#include "includes.h"
-
-namespace Php {
-
- Value eval(const std::string &phpCode) {
- // we need the tsrm_ls variable
- TSRMLS_FETCH();
-
- // the current exception
- zval* oldException = EG(exception);
-
- // the return zval
- zval* retval = nullptr;
- if (zend_eval_stringl_ex((char *)phpCode.c_str(), (int32_t)phpCode.length(), retval, (char *)"", 1 TSRMLS_CC) != SUCCESS)
- {
- // throw an exception, php couldn't evaluate code
- throw Exception("PHP couldn't evaluate: " + phpCode);
-
- // unreachable, but let's return at least something to prevent compiler warnings
- return nullptr;
- }
- else
- {
- // 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) TSRMLS_CC);
-
- // no (additional) exception was thrown
- return retval ? Value(retval) : nullptr;
- }
- }
-
-}
\ No newline at end of file
diff --git a/zend/includes.h b/zend/includes.h
index 8af557cc..63b435eb 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -79,7 +79,6 @@
#include "../include/namespace.h"
#include "../include/extension.h"
#include "../include/call.h"
-#include "../include/fastcall.h"
/**
* Common header files for internal use only
From 513db71261813c2785e4292cfe05bc1d5b814f4f Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 13:40:11 +0200
Subject: [PATCH 26/41] minor change so that phpcpp.h does not have any changes
in this project
---
phpcpp.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/phpcpp.h b/phpcpp.h
index baa91cca..c3f33659 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -2,7 +2,7 @@
* phpcpp.h
*
* Library to build PHP extensions with CPP
- *
+ *
* @copyright 2013 CopernicA BV
* @author Emiel Bruijntjes
*/
From 8801ed40905040115d8af6031c4f3c48d1f67e18 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 14:36:29 +0200
Subject: [PATCH 27/41] no more exceptions for wrong eval()'ed code
---
zend/eval.cpp | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/zend/eval.cpp b/zend/eval.cpp
index e0baaed6..4632fff5 100644
--- a/zend/eval.cpp
+++ b/zend/eval.cpp
@@ -33,16 +33,28 @@ Value eval(const std::string &phpCode)
zval* retval = nullptr;
if (zend_eval_stringl_ex((char *)phpCode.c_str(), (int32_t)phpCode.length(), retval, (char *)"", 1 TSRMLS_CC) != SUCCESS)
{
- // throw an exception, php couldn't evaluate code
- throw Exception("PHP eval error");
-
- // unreachable, but let's return at least something to prevent compiler warnings
+ // Do we want to throw an exception here? The original author
+ // did, but there are some reasons not to:
+ //
+ // 1. the PHP eval() function also does not throw exceptions.
+ //
+ // 2. the zend_eval_string() function already triggers a
+ // 'PHP parse error' when an error occurs, which also has
+ // to be handled. If we also throw an exception here, the
+ // user will have to write two error checks: for the error
+ // and the exception.
+ //
+ // if we _do_ want to throw an exception, we will first have to
+ // prevent the original zend_error to occur, and then turn it
+ // into an exception. An exception would be nicer from a C++
+ // point of view, but because of the extra complexity, we do not
+ // this for now.
return nullptr;
}
else
{
- // was an exception thrown inside the function? In that case we throw a C++ new exception
- // to give the C++ code the chance to catch it
+ // 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);
// no (additional) exception was thrown
From fec2fca2df568aa059e93d59bcfb63daec686583 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 15:17:41 +0200
Subject: [PATCH 28/41] modifed class_exists function to work with a char*
without having to convert it into a std::string
---
include/call.h | 3 ++
include/fastcall.h | 13 ---------
phpcpp.h | 1 -
zend/exists.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++
zend/fastcall.cpp | 43 -----------------------------
zend/includes.h | 1 -
6 files changed, 71 insertions(+), 58 deletions(-)
delete mode 100644 include/fastcall.h
create mode 100644 zend/exists.cpp
delete mode 100644 zend/fastcall.cpp
diff --git a/include/call.h b/include/call.h
index 05c59556..80ef6c97 100644
--- a/include/call.h
+++ b/include/call.h
@@ -15,6 +15,9 @@ namespace Php {
/**
* List of functions that are available for use in PHP
*/
+bool class_exists(const char *classname, size_t size, bool autoload = true);
+bool class_exists(const char *classname, bool autoload = true) { return class_exists(classname, strlen(classname), autoload); }
+bool class_exists(const std::string &classname, bool autoload = true) { return class_exists(classname.c_str(), classname.size(), autoload); }
Value eval(const std::string &phpCode);
/**
diff --git a/include/fastcall.h b/include/fastcall.h
deleted file mode 100644
index a5a8e1c4..00000000
--- a/include/fastcall.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * fastcall.h
- *
- * This file holds some PHP functions implementation in C directly.
- *
- */
-
-namespace Php {
-
- bool class_exists(const std::string &classname, bool autoload = true);
-
-}
-
diff --git a/phpcpp.h b/phpcpp.h
index 8be1a251..baa91cca 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -60,7 +60,6 @@
#include
#include
#include
-#include
/**
* Macro to export a function
diff --git a/zend/exists.cpp b/zend/exists.cpp
new file mode 100644
index 00000000..6bab8ba9
--- /dev/null
+++ b/zend/exists.cpp
@@ -0,0 +1,68 @@
+/**
+ * Exists.cpp
+ *
+ * This file holds the implementation of all *_exists() functions,
+ * like class_exists(), et cetera
+ *
+ * @author andot
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * Open the PHP namespace
+ */
+namespace Php {
+
+/**
+ * Check whether a class with a certain name exists
+ * @param classname
+ * @param len
+ * @param autoload
+ * @return bool
+ */
+bool class_exists(const char *classname, size_t len, bool autoload)
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // we're going to load a class-entry
+ zend_class_entry **ce;
+
+ // should we autoload the class?
+ if (autoload)
+ {
+ // starting slashes can be ignored
+ if (len > 0 && classname[0] == '\\') { classname++; len--; }
+
+ // all classes are in lowercase in the hash, so we make
+ // a temporary buffer for storing the lowercase class name
+ // (is this smart? memory allocation is expensive!)
+ std::unique_ptr lc_name(new char[len + 1]);
+
+ // copy the name to lowercase, but ignore the starting slash (if there is one)
+ zend_str_tolower_copy(lc_name, classname, len);
+
+ // see if there is a class with this name
+ if (SUCCESS != zend_hash_find(EG(class_table), name, len + 1, (void **) &ce)) return false;
+
+ // the found "class" could also be an interface or trait, which we do no want
+ return !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
+ }
+ else
+ {
+ // no auto-load
+ if (SUCCESS != zend_lookup_class(str, len, &ce TSRMLS_CC) == SUCCESS) return false;
+
+ // the found "class" could also be an interface or trait, which we do no want
+ return ((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0;
+ }
+}
+
+/**
+ * End of namespace
+ */
+}
diff --git a/zend/fastcall.cpp b/zend/fastcall.cpp
deleted file mode 100644
index 359c3ada..00000000
--- a/zend/fastcall.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- * fastcall.cpp
- *
- * This file holds some PHP functions implementation in C directly.
- *
- */
-
-#include "includes.h"
-
-namespace Php {
-
- bool class_exists(const std::string &classname, bool autoload) {
- // we need the tsrm_ls variable
- TSRMLS_FETCH();
-
- zend_class_entry **ce;
- int found;
- const char * str = classname.c_str();
- int32_t len = (int32_t)classname.length();
-
-
- if (autoload) {
- char *lc_name = new char[len + 1];
- zend_str_tolower_copy(lc_name, str, len);
-
- char *name = lc_name;
- if (lc_name[0] == '\\') {
- name = &lc_name[1];
- --len;
- }
-
- found = zend_hash_find(EG(class_table), name, len + 1, (void **) &ce);
- delete [] lc_name;
- return (found == SUCCESS && !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS));
- }
-
- if (zend_lookup_class(str, len, &ce TSRMLS_CC) == SUCCESS) {
- return (((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0);
- }
- return false;
- }
-
-}
\ No newline at end of file
diff --git a/zend/includes.h b/zend/includes.h
index 8af557cc..63b435eb 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -79,7 +79,6 @@
#include "../include/namespace.h"
#include "../include/extension.h"
#include "../include/call.h"
-#include "../include/fastcall.h"
/**
* Common header files for internal use only
From fe19521099b620869c5dbbe64b2ddf9aeddcbe51 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 15:18:22 +0200
Subject: [PATCH 29/41] removed space from docblock
---
phpcpp.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/phpcpp.h b/phpcpp.h
index baa91cca..c3f33659 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -2,7 +2,7 @@
* phpcpp.h
*
* Library to build PHP extensions with CPP
- *
+ *
* @copyright 2013 CopernicA BV
* @author Emiel Bruijntjes
*/
From 8e089ea9174ca133f938bdb16ef14e7a1027ccaf Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 15:21:33 +0200
Subject: [PATCH 30/41] compile issues
---
include/call.h | 8 ++++----
zend/exists.cpp | 6 +++---
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/include/call.h b/include/call.h
index 80ef6c97..e1bb12d3 100644
--- a/include/call.h
+++ b/include/call.h
@@ -15,10 +15,10 @@ namespace Php {
/**
* List of functions that are available for use in PHP
*/
-bool class_exists(const char *classname, size_t size, bool autoload = true);
-bool class_exists(const char *classname, bool autoload = true) { return class_exists(classname, strlen(classname), autoload); }
-bool class_exists(const std::string &classname, bool autoload = true) { return class_exists(classname.c_str(), classname.size(), autoload); }
-Value eval(const std::string &phpCode);
+extern bool class_exists(const char *classname, size_t size, bool autoload = true);
+inline bool class_exists(const char *classname, bool autoload = true) { return class_exists(classname, strlen(classname), autoload); }
+inline bool class_exists(const std::string &classname, bool autoload = true) { return class_exists(classname.c_str(), classname.size(), autoload); }
+extern Value eval(const std::string &phpCode);
/**
* Call a function in PHP
diff --git a/zend/exists.cpp b/zend/exists.cpp
index 6bab8ba9..b5bc64f9 100644
--- a/zend/exists.cpp
+++ b/zend/exists.cpp
@@ -44,10 +44,10 @@ bool class_exists(const char *classname, size_t len, bool autoload)
std::unique_ptr lc_name(new char[len + 1]);
// copy the name to lowercase, but ignore the starting slash (if there is one)
- zend_str_tolower_copy(lc_name, classname, len);
+ zend_str_tolower_copy(lc_name.get(), classname, len);
// see if there is a class with this name
- if (SUCCESS != zend_hash_find(EG(class_table), name, len + 1, (void **) &ce)) return false;
+ if (SUCCESS != zend_hash_find(EG(class_table), lc_name.get(), len + 1, (void **) &ce)) return false;
// the found "class" could also be an interface or trait, which we do no want
return !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
@@ -55,7 +55,7 @@ bool class_exists(const char *classname, size_t len, bool autoload)
else
{
// no auto-load
- if (SUCCESS != zend_lookup_class(str, len, &ce TSRMLS_CC) == SUCCESS) return false;
+ if (SUCCESS != zend_lookup_class(classname, len, &ce TSRMLS_CC)) return false;
// the found "class" could also be an interface or trait, which we do no want
return ((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0;
From 4104abb21a40fa0057c488199769b24519aa1da7 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 15:31:23 +0200
Subject: [PATCH 31/41] autoload check had to be exactly the other way around
---
zend/exists.cpp | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/zend/exists.cpp b/zend/exists.cpp
index b5bc64f9..3add227a 100644
--- a/zend/exists.cpp
+++ b/zend/exists.cpp
@@ -34,6 +34,14 @@ bool class_exists(const char *classname, size_t len, bool autoload)
// should we autoload the class?
if (autoload)
+ {
+ // no auto-load
+ if (SUCCESS != zend_lookup_class(classname, len, &ce TSRMLS_CC)) return false;
+
+ // the found "class" could also be an interface or trait, which we do no want
+ return ((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0;
+ }
+ else
{
// starting slashes can be ignored
if (len > 0 && classname[0] == '\\') { classname++; len--; }
@@ -52,14 +60,6 @@ bool class_exists(const char *classname, size_t len, bool autoload)
// the found "class" could also be an interface or trait, which we do no want
return !(((*ce)->ce_flags & (ZEND_ACC_INTERFACE | ZEND_ACC_TRAIT)) > ZEND_ACC_EXPLICIT_ABSTRACT_CLASS);
}
- else
- {
- // no auto-load
- if (SUCCESS != zend_lookup_class(classname, len, &ce TSRMLS_CC)) return false;
-
- // the found "class" could also be an interface or trait, which we do no want
- return ((*ce)->ce_flags & (ZEND_ACC_INTERFACE | (ZEND_ACC_TRAIT - ZEND_ACC_EXPLICIT_ABSTRACT_CLASS))) == 0;
- }
}
/**
From 3f1d5ca94f721cf7feff0fc9cb05a5c1b6d39873 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 17:00:10 +0200
Subject: [PATCH 32/41] added Value::subclassOf(), and implemented Php::is_a()
and Php::is_subclass_of()
---
include/call.h | 7 +++
include/fastcall.h | 18 -------
include/value.h | 45 +++++++++++------
zend/value.cpp | 119 ++++++++++++++++++++++++++++++++++-----------
4 files changed, 129 insertions(+), 60 deletions(-)
delete mode 100644 include/fastcall.h
diff --git a/include/call.h b/include/call.h
index e1bb12d3..dff815c1 100644
--- a/include/call.h
+++ b/include/call.h
@@ -19,6 +19,13 @@ extern bool class_exists(const char *classname, size_t size, bool autoload = tr
inline bool class_exists(const char *classname, bool autoload = true) { return class_exists(classname, strlen(classname), autoload); }
inline bool class_exists(const std::string &classname, bool autoload = true) { return class_exists(classname.c_str(), classname.size(), autoload); }
extern Value eval(const std::string &phpCode);
+inline bool is_a(const Value &obj, const char *classname, size_t size, bool allow_string = false) { return obj.instanceOf(classname, size, allow_string); }
+inline bool is_a(const Value &obj, const char *classname, bool allow_string = false) { return is_a(obj, classname, strlen(classname), allow_string); }
+inline bool is_a(const Value &obj, const std::string &classname, bool allow_string = false) { return is_a(obj, classname.c_str(), classname.size(), allow_string); }
+inline bool is_subclass_of(const Value &obj, const char *classname, size_t size, bool allow_string = true) { return obj.subclassOf(classname, size, allow_string); }
+inline bool is_subclass_of(const Value &obj, const char *classname, bool allow_string = true) { return is_subclass_of(obj, classname, strlen(classname), allow_string); }
+inline bool is_subclass_of(const Value &obj, const std::string &classname, bool allow_string = true) { return is_subclass_of(obj, classname.c_str(), classname.size(), allow_string); }
+
/**
* Call a function in PHP
diff --git a/include/fastcall.h b/include/fastcall.h
deleted file mode 100644
index 778a9b2c..00000000
--- a/include/fastcall.h
+++ /dev/null
@@ -1,18 +0,0 @@
-/**
- * fastcall.h
- *
- * This file holds some PHP functions implementation in C directly.
- *
- */
-
-namespace Php {
-
- inline bool is_a(const Value &obj, const std::string classname, bool allow_string = false) {
- return obj.is(classname, allow_string);
- }
- inline bool is_subclass_of(const Value &obj, const std::string classname, bool allow_string = true) {
- return obj.isSubClassOf(classname, allow_string);
- }
-
-}
-
diff --git a/include/value.h b/include/value.h
index b3c288c9..e6b8d69f 100644
--- a/include/value.h
+++ b/include/value.h
@@ -964,29 +964,39 @@ class Value : private HashParent
// try casting it
return dynamic_cast(base);
}
-
+
/**
- * Checks if this object is of the class or has the class as one of its parents
- * @param classname
- * @param allow_string
+ * Check whether this object is an instance of a certain class
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
* @return bool
*/
- inline bool is(const std::string &classname, bool allow_string=false) const {
- return isImpl(classname, allow_string, false);
- }
+ bool instanceOf(const char *classname, size_t size, bool allowString = false) const;
+ bool instanceOf(const char *classname, bool allowString = false) const { return instanceOf(classname, strlen(classname), allowString); }
+ bool instanceOf(const std::string &classname, bool allowString = false) const { return instanceOf(classname.c_str(), classname.size(), allowString); }
/**
- * Checks if this object has the class as one of its parents
- * @param classname
+ * Check whether this object is derived from a certain class.
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
* @return bool
*/
- inline bool isSubClassOf(const std::string &classname, bool allow_string=true) const {
- return isImpl(classname, allow_string, true);
- }
+ bool subclassOf(const char *classname, size_t size, bool allowString = false) const;
+ bool subclassOf(const char *classname, bool allowString = false) const { return subclassOf(classname, strlen(classname), allowString); }
+ bool subclassOf(const std::string &classname, bool allowString = false) const { return subclassOf(classname.c_str(), classname.size(), allowString); }
-private:
- bool isImpl(const std::string &classname, bool allow_string, bool only_subclass) const;
+private:
/**
* Call function with a number of parameters
* @param argc Number of parameters
@@ -1069,6 +1079,13 @@ class Value : private HashParent
*/
iterator createIterator(bool begin) const;
+ /**
+ * Retrieve the class entry
+ * @param allowString Allow the 'this' object to be a string
+ * @return zend_class_entry
+ */
+ struct _zend_class_entry *classEntry(bool allowString = true) const;
+
/**
* The Globals and Member classes can access the zval directly
*/
diff --git a/zend/value.cpp b/zend/value.cpp
index 9b409f78..9258dbd8 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1408,42 +1408,105 @@ bool Value::isCallable() const
return zend_is_callable(_val, 0, NULL TSRMLS_CC);
}
-bool Value::isImpl(const std::string &classname, bool allow_string, bool only_subclass) const {
- /*
- * allow_string - is default is false, isSubclassOf is true.
- * if it's allowed, the the autoloader will be called if the class does not exist.
- * default behaviour is different, as 'is' used to be used to test mixed return
- * values and there is no easy way to deprecate this.
- */
+/**
+ * Retrieve the class entry
+ * @param allowString
+ * @return zend_class_entry
+ */
+zend_class_entry Value::classEntry(bool allowString) const
+{
// we need the tsrm_ls variable
TSRMLS_FETCH();
- zend_class_entry *instance_ce;
- zend_class_entry **ce;
-
- if (allow_string && isString()) {
- zend_class_entry **the_ce;
- if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &the_ce TSRMLS_CC) == FAILURE) {
- return false;
- }
- instance_ce = *the_ce;
- }
- else if (isObject() && HAS_CLASS_ENTRY(*_val)) {
- instance_ce = Z_OBJCE_P(_val);
+ // the class-entry of 'this'
+ zend_class_entry *this_entry;
+
+ // is this an object
+ if (isObject())
+ {
+ // should have a class entry
+ if (!HAS_CLASS_ENTRY(*_val)) return nullptr;
+
+ // class entry can be easily found
+ return Z_OBJCE_P(_val);
}
- else {
- return false;
+ else
+ {
+ // the value is not an object, is this allowed?
+ if (!allowString || !isString()) return nullptr;
+
+ // temporary variable
+ zend_class_entry **ce;
+
+ // find the class entry
+ if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &ce TSRMLS_CC) == FAILURE) return return;
+
+ // found the entry
+ return *ce;
}
+}
- if (zend_lookup_class_ex(classname.c_str(), (int32_t)classname.length(), NULL, 0, &ce TSRMLS_CC) == FAILURE) {
- return false;
- }
+/**
+ * Check whether this object is an instance of a certain class
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
+ * @return bool
+ */
+bool Value::instanceOf(const char *classname, size_t size, bool allowString) const
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
- if (only_subclass && instance_ce == *ce) {
- return false;
- }
+ // the class-entry of 'this'
+ zend_class_entry *this_ce = classEntry(allowString);
+ if (!this_ce) return false;
+
+ // class entry of the parameter
+ zend_class_entry **ce;
+
+ // now we can look up the actual class
+ if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
+
+ // check if this is a subclass
+ return instanceof_function(this_ce, *ce TSRMLS_CC);
+}
+
+/**
+ * Check whether this object is derived from a certain class
+ *
+ * If you set the parameter 'allowString' to true, and the Value object
+ * holds a string, the string will be treated as class name.
+ *
+ * @param classname The class of which this should be an instance
+ * @param size Length of the classname string
+ * @param allowString Is it allowed for 'this' to be a string
+ * @return bool
+ */
+bool Value::subclassOf(const char *classname, size_t size, bool allowString) const
+{
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
- return instanceof_function(instance_ce, *ce TSRMLS_CC);
+ // the class-entry of 'this'
+ zend_class_entry *this_ce = classEntry(allowString);
+ if (!this_ce) return false;
+
+ // class entry of the parameter
+ zend_class_entry **ce;
+
+ // now we can look up the actual class
+ if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
+
+ // should not be identical, it must be a real derived object
+ if (this_ce == *ce) return false;
+
+ // check if this is a subclass
+ return instanceof_function(this_ce, *ce TSRMLS_CC);
}
/**
From fc24b07c6572c018a7311a2e6a4ee6da79661bcc Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 17:01:16 +0200
Subject: [PATCH 33/41] removed includes for fastcall.h
---
phpcpp.h | 1 -
zend/includes.h | 1 -
2 files changed, 2 deletions(-)
diff --git a/phpcpp.h b/phpcpp.h
index 8be1a251..baa91cca 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -60,7 +60,6 @@
#include
#include
#include
-#include
/**
* Macro to export a function
diff --git a/zend/includes.h b/zend/includes.h
index 8af557cc..63b435eb 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -79,7 +79,6 @@
#include "../include/namespace.h"
#include "../include/extension.h"
#include "../include/call.h"
-#include "../include/fastcall.h"
/**
* Common header files for internal use only
From 5e8c9017107dd48150790ac7027d9cdc67d8b09c Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sat, 26 Jul 2014 17:02:59 +0200
Subject: [PATCH 34/41] fixed compile issues
---
zend/value.cpp | 7 ++-----
1 file changed, 2 insertions(+), 5 deletions(-)
diff --git a/zend/value.cpp b/zend/value.cpp
index 9258dbd8..d48e08b3 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1413,14 +1413,11 @@ bool Value::isCallable() const
* @param allowString
* @return zend_class_entry
*/
-zend_class_entry Value::classEntry(bool allowString) const
+zend_class_entry *Value::classEntry(bool allowString) const
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
- // the class-entry of 'this'
- zend_class_entry *this_entry;
-
// is this an object
if (isObject())
{
@@ -1439,7 +1436,7 @@ zend_class_entry Value::classEntry(bool allowString) const
zend_class_entry **ce;
// find the class entry
- if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &ce TSRMLS_CC) == FAILURE) return return;
+ if (zend_lookup_class(Z_STRVAL_P(_val), Z_STRLEN_P(_val), &ce TSRMLS_CC) == FAILURE) return nullptr;
// found the entry
return *ce;
From 5cf1b5d53db6ddeb4b8d371baef0d9c37c0fa231 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sun, 27 Jul 2014 16:30:17 +0200
Subject: [PATCH 35/41] renamed subclassOf() to derivedFrom() (added to pull
request #110)
---
include/call.h | 2 +-
include/value.h | 6 +++---
zend/value.cpp | 2 +-
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/include/call.h b/include/call.h
index dff815c1..1a0c59ff 100644
--- a/include/call.h
+++ b/include/call.h
@@ -22,7 +22,7 @@ extern Value eval(const std::string &phpCode);
inline bool is_a(const Value &obj, const char *classname, size_t size, bool allow_string = false) { return obj.instanceOf(classname, size, allow_string); }
inline bool is_a(const Value &obj, const char *classname, bool allow_string = false) { return is_a(obj, classname, strlen(classname), allow_string); }
inline bool is_a(const Value &obj, const std::string &classname, bool allow_string = false) { return is_a(obj, classname.c_str(), classname.size(), allow_string); }
-inline bool is_subclass_of(const Value &obj, const char *classname, size_t size, bool allow_string = true) { return obj.subclassOf(classname, size, allow_string); }
+inline bool is_subclass_of(const Value &obj, const char *classname, size_t size, bool allow_string = true) { return obj.derivedFrom(classname, size, allow_string); }
inline bool is_subclass_of(const Value &obj, const char *classname, bool allow_string = true) { return is_subclass_of(obj, classname, strlen(classname), allow_string); }
inline bool is_subclass_of(const Value &obj, const std::string &classname, bool allow_string = true) { return is_subclass_of(obj, classname.c_str(), classname.size(), allow_string); }
diff --git a/include/value.h b/include/value.h
index e6b8d69f..853be966 100644
--- a/include/value.h
+++ b/include/value.h
@@ -991,9 +991,9 @@ class Value : private HashParent
* @param allowString Is it allowed for 'this' to be a string
* @return bool
*/
- bool subclassOf(const char *classname, size_t size, bool allowString = false) const;
- bool subclassOf(const char *classname, bool allowString = false) const { return subclassOf(classname, strlen(classname), allowString); }
- bool subclassOf(const std::string &classname, bool allowString = false) const { return subclassOf(classname.c_str(), classname.size(), allowString); }
+ bool derivedFrom(const char *classname, size_t size, bool allowString = false) const;
+ bool derivedFrom(const char *classname, bool allowString = false) const { return derivedFrom(classname, strlen(classname), allowString); }
+ bool derivedFrom(const std::string &classname, bool allowString = false) const { return derivedFrom(classname.c_str(), classname.size(), allowString); }
private:
diff --git a/zend/value.cpp b/zend/value.cpp
index d48e08b3..61efe2fe 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1484,7 +1484,7 @@ bool Value::instanceOf(const char *classname, size_t size, bool allowString) con
* @param allowString Is it allowed for 'this' to be a string
* @return bool
*/
-bool Value::subclassOf(const char *classname, size_t size, bool allowString) const
+bool Value::derivedFrom(const char *classname, size_t size, bool allowString) const
{
// we need the tsrm_ls variable
TSRMLS_FETCH();
From e46c2ec8eb8a8dc6e3750b8e3fdb764956b18e42 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sun, 27 Jul 2014 16:36:08 +0200
Subject: [PATCH 36/41] added comment
---
include/value.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/include/value.h b/include/value.h
index fe28e6d5..b0b5ed20 100644
--- a/include/value.h
+++ b/include/value.h
@@ -503,6 +503,7 @@ class Value : private HashParent
// result variable
std::map result;
+ // loop through the original map, and copy everything to the result
for (auto &iter : map) result[iter.first] = iter.second;
// done
From 23579ad5a15377a4832163290b00ab23f504ba14 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes
Date: Sun, 27 Jul 2014 16:48:31 +0200
Subject: [PATCH 37/41] update coding style
---
include/object.h | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/include/object.h b/include/object.h
index 47935d70..6350259e 100644
--- a/include/object.h
+++ b/include/object.h
@@ -41,13 +41,21 @@ class Object : public Value
*/
Object(const Value &value) : Value()
{
- // string types are instantiated
- if (value.isString()) {
+ // when a string is passed in, we are going to make a new instance of the
+ // passed in string
+ if (value.isString())
+ {
+ // instantiate the object
instantiate(value);
+
+ // and call the __construct method
call("__construct");
}
- // otherwise copy the other object
- else operator=(value);
+ else
+ {
+ // this simply copies the other object
+ operator=(value);
+ }
}
/**
From eac464b8b4428864817edbedf1fb79f6dac0dd3b Mon Sep 17 00:00:00 2001
From: Toon Schoenmakers
Date: Fri, 15 Aug 2014 17:17:05 +0200
Subject: [PATCH 38/41] Properly use numeric keys in case we're requesting a
mapValue
---
zend/value.cpp | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/zend/value.cpp b/zend/value.cpp
index 61efe2fe..3f38498d 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1710,7 +1710,12 @@ std::map Value::mapValue() const
std::map result;
// iterate over the object
- for (auto &iter : *this) result[iter.first.rawValue()] = iter.second;
+ for (auto &iter : *this)
+ {
+ auto &key = iter.first;
+ if (key.isNumeric()) result[std::to_string(key.numericValue())] = iter.second;
+ else result[iter.first.rawValue()] = iter.second;
+ }
// done
return result;
From cc106471bee34549d0db18a6d3797e791853fc6c Mon Sep 17 00:00:00 2001
From: Toon Schoenmakers
Date: Tue, 19 Aug 2014 11:07:37 +0200
Subject: [PATCH 39/41] Use stringValue() rather than checking if it's a
numeric value to then use std::to_string()
---
zend/value.cpp | 11 +++--------
1 file changed, 3 insertions(+), 8 deletions(-)
diff --git a/zend/value.cpp b/zend/value.cpp
index 3f38498d..50c4b649 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1708,15 +1708,10 @@ std::map Value::mapValue() const
{
// result variable
std::map result;
-
+
// iterate over the object
- for (auto &iter : *this)
- {
- auto &key = iter.first;
- if (key.isNumeric()) result[std::to_string(key.numericValue())] = iter.second;
- else result[iter.first.rawValue()] = iter.second;
- }
-
+ for (auto &iter : *this) result[iter.first.stringValue()] = iter.second;
+
// done
return result;
}
From 36ab68bd25aaedc81fdf6745faf5f3a14474c53f Mon Sep 17 00:00:00 2001
From: Toon Schoenmakers
Date: Tue, 19 Aug 2014 14:20:11 +0200
Subject: [PATCH 40/41] Fixed the compile issues against php 5.3
---
zend/exists.cpp | 9 +++++++++
zend/value.cpp | 34 +++++++++++++++++++++++-----------
2 files changed, 32 insertions(+), 11 deletions(-)
diff --git a/zend/exists.cpp b/zend/exists.cpp
index 3add227a..1177e47b 100644
--- a/zend/exists.cpp
+++ b/zend/exists.cpp
@@ -12,6 +12,15 @@
*/
#include "includes.h"
+/**
+ * On php 5.3 ZEND_ACC_TRAIT isn't defined, so we simply define it to 0
+ * so all operations with it are basically no-ops. Currently unconfirmed
+ * if this actually works correctly on php 5.3, but it at least compiles.
+ */
+#ifndef ZEND_ACC_TRAIT
+#define ZEND_ACC_TRAIT 0
+#endif
+
/**
* Open the PHP namespace
*/
diff --git a/zend/value.cpp b/zend/value.cpp
index 50c4b649..69f7e2c5 100644
--- a/zend/value.cpp
+++ b/zend/value.cpp
@@ -1445,10 +1445,10 @@ zend_class_entry *Value::classEntry(bool allowString) const
/**
* Check whether this object is an instance of a certain class
- *
+ *
* If you set the parameter 'allowString' to true, and the Value object
* holds a string, the string will be treated as class name.
- *
+ *
* @param classname The class of which this should be an instance
* @param size Length of the classname string
* @param allowString Is it allowed for 'this' to be a string
@@ -1462,23 +1462,29 @@ bool Value::instanceOf(const char *classname, size_t size, bool allowString) con
// the class-entry of 'this'
zend_class_entry *this_ce = classEntry(allowString);
if (!this_ce) return false;
-
+
// class entry of the parameter
zend_class_entry **ce;
-
+
// now we can look up the actual class
+ // the signature of zend_lookup_class_ex is slightly different since 5.4
+ // TODO The signature of this changed once again as of 5.6!
+#if PHP_VERSION_ID >= 50400
if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
-
+#else
+ if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false;
+#endif
+
// check if this is a subclass
return instanceof_function(this_ce, *ce TSRMLS_CC);
}
/**
* Check whether this object is derived from a certain class
- *
+ *
* If you set the parameter 'allowString' to true, and the Value object
* holds a string, the string will be treated as class name.
- *
+ *
* @param classname The class of which this should be an instance
* @param size Length of the classname string
* @param allowString Is it allowed for 'this' to be a string
@@ -1492,16 +1498,22 @@ bool Value::derivedFrom(const char *classname, size_t size, bool allowString) co
// the class-entry of 'this'
zend_class_entry *this_ce = classEntry(allowString);
if (!this_ce) return false;
-
+
// class entry of the parameter
zend_class_entry **ce;
-
+
// now we can look up the actual class
+ // the signature of zend_lookup_class_ex is slightly different since 5.4
+ // TODO The signature of this changed once again as of 5.6!
+#if PHP_VERSION_ID >= 50400
if (zend_lookup_class_ex(classname, size, NULL, 0, &ce TSRMLS_CC) == FAILURE) return false;
-
+#else
+ if (zend_lookup_class_ex(classname, size, 0, &ce TSRMLS_CC) == FAILURE) return false;
+#endif
+
// should not be identical, it must be a real derived object
if (this_ce == *ce) return false;
-
+
// check if this is a subclass
return instanceof_function(this_ce, *ce TSRMLS_CC);
}
From 97bc6757346d394a4b7d5898983be298e0b0ea98 Mon Sep 17 00:00:00 2001
From: Toon Schoenmakers
Date: Fri, 22 Aug 2014 15:23:05 +0200
Subject: [PATCH 41/41] Store the impl pointer for ClassImpl after the name in
the zend_class_entry on php5.3
Turns out the apache reload issue from f57607d2d58f6e7689a3550c84ba68ce42c6a7b3 was
never actually fixed. This commit however does finally fix it. The previously comment
trick however is still used with php 5.4 and php 5.5 as this 'new' trick doesn't work
with these versions of php as char* name in the zend_class_entry is a const char* and
is no longer internally copied and all (meaning we can't realloc it).
---
zend/classimpl.cpp | 65 +++++++++++++++++++++++-----------------------
zend/classimpl.h | 6 -----
2 files changed, 33 insertions(+), 38 deletions(-)
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index b2acc24a..9c28a0a3 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -20,11 +20,6 @@ ClassImpl::~ClassImpl()
{
// destruct the entries
if (_entries) delete[] _entries;
-
- // php 5.3 deallocates the doc_comment by iself
-#if PHP_VERSION_ID >= 50400
- if (_comment) free(_comment);
-#endif
}
/**
@@ -41,18 +36,21 @@ static ClassImpl *self(zend_class_entry *entry)
// we need the base class (in user space the class may have been overridden,
// but we are not interested in these user space classes)
while (entry->parent) entry = entry->parent;
-
+
#if PHP_VERSION_ID >= 50400
// retrieve the comment (it has a pointer hidden in it to the ClassBase object)
const char *comment = entry->info.user.doc_comment;
-#else
- // retrieve the comment php5.3 style (it has a pointer hidden in it to the ClassBase object)
- const char *comment = entry->doc_comment;
-#endif
-
+
// the first byte of the comment is an empty string (null character), but
// the next bytes contain a pointer to the ClassBase class
return *((ClassImpl **)(comment + 1));
+#else
+ // on php 5.3 we store the pointer to impl after the name in the entry
+ ClassImpl** impl = (ClassImpl**)(entry->name + 1 + entry->name_length);
+
+ // return the actual implementation
+ return *impl;
+#endif
}
/**
@@ -1411,34 +1409,37 @@ void ClassImpl::initialize(ClassBase *base, const std::string &prefix TSRMLS_DC)
// otherwise report an error
else std::cerr << "Derived class " << name() << " is initialized before base class " << interface->name() << ": interface is ignored" << std::endl;
}
-
+
+ // this pointer has to be copied to temporary pointer, as &this causes compiler error
+ ClassImpl *impl = this;
+
+#if PHP_VERSION_ID >= 50400
+
// allocate doc comment to contain an empty string + a hidden pointer
- if (!_comment)
- {
- // allocate now
- _comment = (char *)malloc(1 + sizeof(ClassBase *));
-
- // empty string on first position
- _comment[0] = '\0';
-
- // this pointer has to be copied to temporary pointer, as &this causes compiler error
- ClassImpl *impl = this;
-
- // copy the 'this' pointer to the doc-comment
- memcpy(_comment+1, &impl, sizeof(ClassImpl *));
- }
-
- // store pointer to the class in the unused doc_comment member
-#if PHP_VERSION_ID >= 50400
+ char *_comment = (char *)malloc(1 + sizeof(ClassImpl *));
+
+ // empty string on first position
+ _comment[0] = '\0';
+
+ // copy the 'this' pointer to the doc-comment
+ memcpy(_comment+1, &impl, sizeof(ClassImpl *));
+
+ // set our comment in the actual class entry
_entry->info.user.doc_comment = _comment;
+
#else
- // and store the wrapper inside the comment
- _entry->doc_comment = _comment;
+
+ // Reallocate some extra space in the name in the zend_class_entry so we can fit a pointer behind it
+ _entry->name = (char *) realloc(_entry->name, _entry->name_length + 1 + sizeof(ClassImpl *));
+
+ // Copy the pointer after it
+ memcpy(_entry->name + _entry->name_length + 1, &impl, sizeof(ClassImpl *));
+
#endif
// set access types flags for class
_entry->ce_flags = (int)_type;
-
+
// declare all member variables
for (auto &member : _members) member->initialize(_entry TSRMLS_CC);
}
diff --git a/zend/classimpl.h b/zend/classimpl.h
index bd631b8b..26cf0307 100644
--- a/zend/classimpl.h
+++ b/zend/classimpl.h
@@ -31,12 +31,6 @@ class ClassImpl
*/
std::string _name;
- /**
- * The comment for reflexion, with a stored pointer to ourselves
- * @var char*
- */
- char *_comment = nullptr;
-
/**
* The class type (this can be values like Php::Abstract and Php::Final)
* @var ClassType