summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2015-01-15 16:34:14 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2015-01-15 16:34:14 +0100
commitd87b3ca8f1dbcb395f2dfd6540483da7a0e5e15c (patch)
tree6dd3179162d5cecc474fc9028039bd757f6df04b
parentd8fe9239959dfeae11daa5de70126e30119d3b75 (diff)
Added the Php::Function class. This is an extension to the Php::Value class that can be used if you want to assign a std::function object to a Value. This is useful if you want to pass a C++ lambda to a PHP userspace function
-rw-r--r--include/class.h6
-rw-r--r--include/function.h73
-rw-r--r--include/namespace.h6
-rw-r--r--include/object.h16
-rw-r--r--phpcpp.h1
-rw-r--r--zend/classimpl.cpp6
-rw-r--r--zend/classimpl.h14
-rw-r--r--zend/extensionimpl.cpp2
-rw-r--r--zend/function.cpp49
-rw-r--r--zend/functor.h61
-rw-r--r--zend/includes.h4
-rw-r--r--zend/namespace.cpp12
-rw-r--r--zend/nativefunction.h (renamed from zend/function.h)20
-rw-r--r--zend/object.cpp35
14 files changed, 277 insertions, 28 deletions
diff --git a/include/class.h b/include/class.h
index 9257213..e11074c 100644
--- a/include/class.h
+++ b/include/class.h
@@ -555,10 +555,12 @@ private:
}
/**
- * Namespaces have access to the private base class, so that the classes
- * can be registered.
+ * Namespaces and the function have access to the private base class,
+ * so that the classes can be registered (the function object needs
+ * this to register the Functor class).
*/
friend class Namespace;
+ friend class Function;
/**
* All Php::Class<AnyThing> also need access to the base class to
diff --git a/include/function.h b/include/function.h
new file mode 100644
index 0000000..7073f84
--- /dev/null
+++ b/include/function.h
@@ -0,0 +1,73 @@
+/**
+ * Function.h
+ *
+ * Small extension to the Value class that allows a value to be
+ * constructed with a std::function.
+ *
+ * If you want to assign a std::function to a value, the following
+ * piece of code won't work:
+ *
+ * Php::Value value([]() { .... });
+ *
+ * Because the passed in function would match with many of the possible
+ * Value constructors. For that reason we have created a small and
+ * simple Function class that can be used instead:
+ *
+ * Php::Function valu([]() { .... });
+ *
+ * A Php::Function is an extended Php::Value object, so can be used
+ * in place of Php::Values all the time. The only difference is that
+ * it has a different constructor
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2015 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class Function : public Value
+{
+public:
+ /**
+ * Constructor to wrap a function that takes parameters
+ * @param function The C++ function to be wrapped
+ */
+ Function(const std::function<Value(Parameters&)> &function);
+
+ /**
+ * Constructor to wrap a function that does not accept parameters
+ * @param function The C++ function to be wrapped
+ */
+ Function(const std::function<Value()> &function) : Function([function](Parameters &params) -> Value {
+
+ // call original function, forget about the parameters
+ return function();
+
+ }) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~Function() {}
+
+private:
+ /**
+ * Retrieve the class entry of the _functor class
+ * @return _zend_class_entry
+ */
+ static struct _zend_class_entry *entry();
+
+
+};
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/include/namespace.h b/include/namespace.h
index e713850..84f0740 100644
--- a/include/namespace.h
+++ b/include/namespace.h
@@ -16,7 +16,7 @@ namespace Php {
/**
* Forward declaration
*/
-class Function;
+class NativeFunction;
/**
* Class definition
@@ -34,7 +34,7 @@ protected:
* Functions defined in the namespace
* @var list
*/
- std::list<std::shared_ptr<Function>> _functions;
+ std::list<std::shared_ptr<NativeFunction>> _functions;
/**
* Classes defined in the namespace
@@ -220,7 +220,7 @@ public:
*
* @param callback
*/
- void functions(const std::function<void(const std::string &ns, Function &func)> &callback);
+ void functions(const std::function<void(const std::string &ns, NativeFunction &func)> &callback);
/**
* Apply a callback to each registered class
diff --git a/include/object.h b/include/object.h
index 0cdeb5c..301fa57 100644
--- a/include/object.h
+++ b/include/object.h
@@ -54,9 +54,23 @@ public:
* }
*
* @param name Name of the class to instantiate
- * @param base Implementation of the class
+ * @param base C++ object to wrap
*/
Object(const char *name, Base *base);
+
+ /**
+ * If you already have the zend_class_entry, you can also pass the
+ * class_entry structure instead of the class name. This constructor
+ * works exactly like the Object(name, base) constructor mentioned
+ * above.
+ *
+ * Note that if you normally use PHP-CPP, you do not have the class_entry,
+ * so you probably need to pass the name anyway
+ *
+ * @param entry The PHP class entry
+ * @param base C++ object to wrap
+ */
+ Object(struct _zend_class_entry *entry, Base *base);
/**
* Wrap around an object implemented by us
diff --git a/phpcpp.h b/phpcpp.h
index 48db1c7..231e112 100644
--- a/phpcpp.h
+++ b/phpcpp.h
@@ -63,6 +63,7 @@
#include <phpcpp/call.h>
#include <phpcpp/script.h>
#include <phpcpp/file.h>
+#include <phpcpp/function.h>
/**
* Macro to export a function
diff --git a/zend/classimpl.cpp b/zend/classimpl.cpp
index e42605e..fa1aa60 100644
--- a/zend/classimpl.cpp
+++ b/zend/classimpl.cpp
@@ -1344,8 +1344,9 @@ const struct _zend_function_entry *ClassImpl::entries()
* @param base the c++ class object created in the extension
* @param prefix namespace prefix
* @param tsrm_ls
+ * @return zend_class_entry
*/
-void ClassImpl::initialize(ClassBase *base, const std::string &prefix TSRMLS_DC)
+zend_class_entry *ClassImpl::initialize(ClassBase *base, const std::string &prefix TSRMLS_DC)
{
// store base pointer
_base = base;
@@ -1442,6 +1443,9 @@ void ClassImpl::initialize(ClassBase *base, const std::string &prefix TSRMLS_DC)
// declare all member variables
for (auto &member : _members) member->initialize(_entry TSRMLS_CC);
+
+ // done
+ return _entry;
}
/**
diff --git a/zend/classimpl.h b/zend/classimpl.h
index 26cf030..32f0e1f 100644
--- a/zend/classimpl.h
+++ b/zend/classimpl.h
@@ -140,6 +140,15 @@ public:
}
/**
+ * The class-entry
+ * @return zend_class_entry
+ */
+ struct _zend_class_entry *entry() const
+ {
+ return _entry;
+ }
+
+ /**
* Initialize the class, given its name
*
* The module functions are registered on module startup, but classes are
@@ -150,9 +159,10 @@ public:
*
* @param base The extension C++ class
* @param ns Namespace name
- * @param tsrm_ls
+ * @param tsrm_ls
+ * @return zend_class_entry
*/
- void initialize(ClassBase *base, const std::string &ns TSRMLS_DC);
+ struct _zend_class_entry *initialize(ClassBase *base, const std::string &ns TSRMLS_DC);
/**
* Static member functions to create or clone objects based on this class
diff --git a/zend/extensionimpl.cpp b/zend/extensionimpl.cpp
index f073acf..262ecdb 100644
--- a/zend/extensionimpl.cpp
+++ b/zend/extensionimpl.cpp
@@ -298,7 +298,7 @@ zend_module_entry *ExtensionImpl::module()
int i = 0;
// apply a function to each function
- _data->functions([&i, entries](const std::string &prefix, Function &function) {
+ _data->functions([&i, entries](const std::string &prefix, NativeFunction &function) {
// initialize the function
function.initialize(prefix, &entries[i]);
diff --git a/zend/function.cpp b/zend/function.cpp
new file mode 100644
index 0000000..789175e
--- /dev/null
+++ b/zend/function.cpp
@@ -0,0 +1,49 @@
+/**
+ * Function.cpp
+ *
+ * Implementation file for the Function class
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2015 Copernica BV
+ */
+
+/**
+ * Dependencies
+ */
+#include "includes.h"
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Function returns a pointer to the class-entry of the Functor class
+ * @return zend_class_entry
+ */
+zend_class_entry *Function::entry()
+{
+ // and the actual class entry
+ static zend_class_entry *entry = nullptr;
+
+ // is the class entry already valid?
+ if (entry) return entry;
+
+ // construct functor object
+ static std::unique_ptr<ClassBase> functor(new Class<Functor>("Functor"));
+
+ // initialize the functor class
+ return entry = functor->implementation()->initialize(functor.get(), "" TSRMLS_CC);
+}
+
+/**
+ * Constructor
+ * @param function The function to be wrapped
+ */
+Function::Function(const std::function<Php::Value(Php::Parameters&)> &function) : Value(Object(entry(), new Functor(function))) {}
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/zend/functor.h b/zend/functor.h
new file mode 100644
index 0000000..7d5bef5
--- /dev/null
+++ b/zend/functor.h
@@ -0,0 +1,61 @@
+/**
+ * Functor.h
+ *
+ * We want to be able to wrap a std::function in an object and pass
+ * that to PHP. The normal "Closure" class from the Zend engine
+ * would be very suitable for that. However, the Zend engine does
+ * not really allow us to add a secret pointer to such closure object.
+ *
+ * Therefore, we create our own Closure class, this time using PHP-CPP
+ * code, to wrap a std::function.
+ *
+ * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
+ * @copyright 2015 Copernica BV
+ */
+
+/**
+ * Set up namespace
+ */
+namespace Php {
+
+/**
+ * Class definition
+ */
+class Functor : public Base
+{
+public:
+ /**
+ * Constructor
+ * @param function The function to wrap
+ */
+ Functor(const std::function<Value(Parameters &params)> &function) : _function(function) {}
+
+ /**
+ * Destructor
+ */
+ virtual ~Functor() {}
+
+ /**
+ * Invoke the functor
+ * @param params
+ * @return Value
+ */
+ Value __invoke(Parameters &params) const
+ {
+ // pass on to the function
+ return _function(params);
+ }
+
+private:
+ /**
+ * The std::function that is wrapped in PHP code
+ * @var std::function
+ */
+ const std::function<Value(Parameters &params)> _function;
+};
+
+/**
+ * End of namespace
+ */
+}
+
diff --git a/zend/includes.h b/zend/includes.h
index 1552778..83b1086 100644
--- a/zend/includes.h
+++ b/zend/includes.h
@@ -82,6 +82,7 @@
#include "../include/call.h"
#include "../include/script.h"
#include "../include/file.h"
+#include "../include/function.h"
/**
* Common header files for internal use only
@@ -94,7 +95,7 @@
*/
#include "init.h"
#include "callable.h"
-#include "function.h"
+#include "nativefunction.h"
#include "method.h"
#include "member.h"
#include "nullmember.h"
@@ -118,6 +119,7 @@
#include "compileroptions.h"
#include "executestate.h"
#include "opcodes.h"
+#include "functor.h"
#ifndef ZVAL_COPY_VALUE
#define ZVAL_COPY_VALUE(z, v) \
diff --git a/zend/namespace.cpp b/zend/namespace.cpp
index 9274bf0..893f2f0 100644
--- a/zend/namespace.cpp
+++ b/zend/namespace.cpp
@@ -26,7 +26,7 @@ Namespace &Namespace::add(const char *name, const native_callback_0 &function, c
if (locked()) return *this;
// add a function
- _functions.push_back(std::make_shared<Function>(name, function, arguments));
+ _functions.push_back(std::make_shared<NativeFunction>(name, function, arguments));
// allow chaining
return *this;
@@ -45,7 +45,7 @@ Namespace &Namespace::add(const char *name, const native_callback_1 &function, c
if (locked()) return *this;
// add a function
- _functions.push_back(std::make_shared<Function>(name, function, arguments));
+ _functions.push_back(std::make_shared<NativeFunction>(name, function, arguments));
// allow chaining
return *this;
@@ -64,7 +64,7 @@ Namespace &Namespace::add(const char *name, const native_callback_2 &function, c
if (locked()) return *this;
// add a function
- _functions.push_back(std::make_shared<Function>(name, function, arguments));
+ _functions.push_back(std::make_shared<NativeFunction>(name, function, arguments));
// allow chaining
return *this;
@@ -83,7 +83,7 @@ Namespace &Namespace::add(const char *name, const native_callback_3 &function, c
if (locked()) return *this;
// add a function
- _functions.push_back(std::make_shared<Function>(name, function, arguments));
+ _functions.push_back(std::make_shared<NativeFunction>(name, function, arguments));
// allow chaining
return *this;
@@ -97,13 +97,13 @@ Namespace &Namespace::add(const char *name, const native_callback_3 &function, c
*
* @param callback
*/
-void Namespace::functions(const std::function<void(const std::string &ns, Function &func)> &callback)
+void Namespace::functions(const std::function<void(const std::string &ns, NativeFunction &func)> &callback)
{
// loop through the functions, and apply the callback
for (auto &function : _functions) callback(_name, *function);
// loop through the other namespaces
- for (auto &ns : _namespaces) ns->functions([this, callback](const std::string &ns, Function &func) {
+ for (auto &ns : _namespaces) ns->functions([this, callback](const std::string &ns, NativeFunction &func) {
// if this is the root namespace, we don't have to change the prefix
if (_name.size() == 0) return callback(ns, func);
diff --git a/zend/function.h b/zend/nativefunction.h
index 4c34ac7..073e69b 100644
--- a/zend/function.h
+++ b/zend/nativefunction.h
@@ -1,11 +1,11 @@
/**
- * Function.h
+ * NativeFunction.h
*
* The Function class is an extension of the Callable class that
* forwards the function call directly to a native function in C/C++
*
* @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com>
- * @copyright 2013 Copernica BV
+ * @copyright 2013-2015 Copernica BV
*/
/**
@@ -16,7 +16,7 @@ namespace Php {
/**
* Class definition
*/
-class Function : public Callable
+class NativeFunction : public Callable
{
public:
/**
@@ -24,27 +24,27 @@ public:
* @param name Function name
* @param function The native C function
*/
- Function(const char *name, const native_callback_0 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(0) { _function.f0 = function; }
- Function(const char *name, const native_callback_1 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(1) { _function.f1 = function; }
- Function(const char *name, const native_callback_2 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(2) { _function.f2 = function; }
- Function(const char *name, const native_callback_3 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(3) { _function.f3 = function; }
+ NativeFunction(const char *name, const native_callback_0 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(0) { _function.f0 = function; }
+ NativeFunction(const char *name, const native_callback_1 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(1) { _function.f1 = function; }
+ NativeFunction(const char *name, const native_callback_2 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(2) { _function.f2 = function; }
+ NativeFunction(const char *name, const native_callback_3 &function, const Arguments &arguments = {}) : Callable(name, arguments), _type(3) { _function.f3 = function; }
/**
* Copy constructor
* @param that
*/
- Function(const Function &that) : Callable(that), _function(that._function), _type(that._type) {}
+ NativeFunction(const NativeFunction &that) : Callable(that), _function(that._function), _type(that._type) {}
/**
* Move constructor
* @param that
*/
- Function(Function &&that) : Callable(std::move(that)), _function(that._function), _type(that._type) {}
+ NativeFunction(NativeFunction &&that) : Callable(std::move(that)), _function(that._function), _type(that._type) {}
/**
* Destructor
*/
- virtual ~Function() {}
+ virtual ~NativeFunction() {}
/**
* Method that gets called every time the function is executed
diff --git a/zend/object.cpp b/zend/object.cpp
index 22431fe..7da8339 100644
--- a/zend/object.cpp
+++ b/zend/object.cpp
@@ -15,7 +15,7 @@ namespace Php {
* Constructor to create a new instance of a builtin class
*
* @param name Name of the class to instantiate
- * @param base Implementation of the class
+ * @param base The C++ object to wrap
*/
Object::Object(const char *name, Base *base) : Value()
{
@@ -54,6 +54,39 @@ Object::Object(const char *name, Base *base) : Value()
}
/**
+ * Constructor in case the class entry is already known
+ *
+ * @param entry Class entry
+ * @param base The C++ object to wrap
+ */
+Object::Object(zend_class_entry *entry, Base *base) : Value()
+{
+ // does the object already have a handle?
+ if (base->implementation())
+ {
+ // the object is already instantiated, we can assign it to this object
+ operator=(Value(base));
+ }
+ else
+ {
+ // we need the tsrm_ls variable
+ TSRMLS_FETCH();
+
+ // construct an implementation (this will also set the implementation
+ // member in the base object), this is a self-destructing object that
+ // will be destructed when the last reference to it has been removed,
+ // we already set the reference to zero
+ new ObjectImpl(entry, base, 0 TSRMLS_CC);
+
+ // now we can store it
+ operator=(Value(base));
+
+ // install the object handlers
+ Z_OBJVAL_P(_val).handlers = ClassImpl::objectHandlers(entry);
+ }
+}
+
+/**
* Copy constructor is valid if the passed in object is also an object,
* or when it is a string holding a classname
* @param that An other object