summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-13 15:48:29 +0100
committerEmiel Bruijntjes <emiel.bruijntjes@copernica.com>2014-03-13 15:48:29 +0100
commit1a8c587f3a67db2e5c38cc525b29800e86f27936 (patch)
tree38b0a17b046d2cfdd76a5e03bfb97fddfbcaceed
parente45c7c392add8fdcd0d8fe06bbb790cea2f865f0 (diff)
magic methods no longer are virtual, so that more signatures are acceptable. added support for __callStatic()
-rw-r--r--include/base.h24
-rw-r--r--include/callstatic.h64
-rw-r--r--include/class.h228
-rw-r--r--include/classbase.h121
-rw-r--r--include/interface.h53
-rw-r--r--src/base.cpp24
-rw-r--r--src/classbase.cpp182
7 files changed, 543 insertions, 153 deletions
diff --git a/include/base.h b/include/base.h
index d0e7e71..6cf75df 100644
--- a/include/base.h
+++ b/include/base.h
@@ -121,7 +121,7 @@ public:
* @param key
* @return bool
*/
- virtual bool __isset(const Php::Value &key);
+ bool __isset(const Php::Value &key) const;
/**
* Overridable method that is called to set a new property
@@ -132,7 +132,7 @@ public:
* @param key
* @param value
*/
- virtual void __set(const Php::Value &key, const Php::Value &value);
+ void __set(const Php::Value &key, const Php::Value &value) const;
/**
* Retrieve a property
@@ -143,7 +143,7 @@ public:
* @param key
* @return value
*/
- virtual Php::Value __get(const Php::Value &key);
+ Php::Value __get(const Php::Value &key) const;
/**
* Remove a member
@@ -153,7 +153,7 @@ public:
*
* @param key
*/
- virtual void __unset(const Php::Value &key);
+ void __unset(const Php::Value &key) const;
/**
* Call a method
@@ -166,7 +166,7 @@ public:
* @param params The parameters that were passed to the function
* @return Value The return value
*/
- virtual Value __call(const char *method, Parameters &params);
+ Php::Value __call(const char *method, Php::Parameters &params) const;
/**
* Call the class as if it was a function
@@ -177,7 +177,7 @@ public:
* @param params The parameters that were passed to the function
* @return Value The return value
*/
- virtual Value __invoke(Parameters &params);
+ Php::Value __invoke(Php::Parameters &params) const;
/**
* Cast the object to a string
@@ -187,7 +187,7 @@ public:
*
* @return Value The object as a string
*/
- virtual Value __toString();
+ Php::Value __toString() const;
/**
* Cast the object to an integer
@@ -197,7 +197,7 @@ public:
*
* @return int Integer value
*/
- virtual long __toInteger();
+ Php::Value __toInteger() const;
/**
* Cast the object to a float
@@ -207,7 +207,7 @@ public:
*
* @return double Floating point value
*/
- virtual double __toFloat();
+ Php::Value __toFloat() const;
/**
* Cast the object to a boolean
@@ -217,17 +217,17 @@ public:
*
* @return bool
*/
- virtual bool __toBool();
+ Value __toBool() const;
/**
- * Comparison operator
+ * Compare the object with a different object
*
* Check how a different object compares to this object
*
* @param that Object to compare with
* @return int
*/
- bool operator<(const Base &that) const;
+ int __compare(const Base &base) const;
private:
diff --git a/include/callstatic.h b/include/callstatic.h
new file mode 100644
index 0000000..3979ef3
--- /dev/null
+++ b/include/callstatic.h
@@ -0,0 +1,64 @@
+/**
+ * CallStatic.h
+ *
+ * Class that performs a lot of C++11 magic to find out if the __callStatic()
+ * method was implemented by the user, and if it was, calls it
+ *
+ */
+
+namespace Php {
+
+/**
+ * SFINAE test to check if the __callStatic method is defined
+ *
+ * This type trait checks if the __callStatic method is defined in class T
+ *
+ * @see http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
+ */
+template <typename T>
+class HasCallStatic
+{
+ typedef char one;
+ typedef long two;
+
+ template <typename C> static one test( decltype(&C::__callStatic) ) ;
+ template <typename C> static two test(...);
+
+public:
+ static const bool value = sizeof(test<T>(0)) == sizeof(char);
+};
+
+/**
+ * Function that only exists if the class T has a __callStatic method
+ * @param name Name of the function
+ * @param params Parameters passed to the function
+ * @return Value
+ */
+template<typename T>
+typename std::enable_if<HasCallStatic<T>::value, Value >::type
+callStatic(const char *name, Parameters &params)
+{
+ // call the __callStatic() function
+ return T::__callStatic(name, params);
+}
+
+/**
+ * Function that only exists if the class T does not have a __callStatic method
+ * @param name Name of the function
+ * @param params Parameters passed to the function
+ * @return Value
+ */
+template<typename T>
+typename std::enable_if<!HasCallStatic<T>::value >::type
+callStatic(const char *name, Parameters &params)
+{
+ std::cout << "has NO call static" << std::endl;
+
+ return nullptr;
+}
+
+/**
+ * End namespace
+ */
+}
+
diff --git a/include/class.h b/include/class.h
index bb05fdd..6fd938c 100644
--- a/include/class.h
+++ b/include/class.h
@@ -25,6 +25,23 @@ extern struct _zend_class_entry *zend_ce_arrayaccess;
//extern struct _zend_class_entry *zend_ce_serializable;
/**
+ * SFINAE test to check if the __callStatic method is defined
+ * @see http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
+ */
+template <typename T>
+class HasCallStatic
+{
+ typedef char one;
+ typedef long two;
+
+ template <typename C> static one test( decltype(&C::__callStatic) ) ;
+ template <typename C> static two test(...);
+
+public:
+ static const bool value = sizeof(test<T>(0)) == sizeof(char);
+};
+
+/**
* Set up namespace
*/
namespace Php {
@@ -217,8 +234,217 @@ private:
// check if the templated class overrides from the Serializable class
return std::is_base_of<Serializable,T>::value;
}
+
+ /**
+ * Call a method
+ * @param base Object to call on
+ * @param name Name of the method
+ * @param params
+ * @return Value
+ */
+ virtual Value call(Base *base, const char *name, Parameters &params) const override
+ {
+ // cast to the user object
+ T *object = (T *)base;
+
+ // call the method on the base object
+ return base->__call(name, params);
+ }
+
+ /**
+ * SFINAE test to check if the __callStatic method is defined
+ *
+ * This type trait checks if the __callStatic method is defined in class T
+ *
+ * @see http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence
+ */
+ template <typename X>
+ class HasCallStatic
+ {
+ typedef char one;
+ typedef long two;
+
+ template <typename C> static one test( decltype(&C::__callStatic) ) ;
+ template <typename C> static two test(...);
+
+ public:
+ static const bool value = sizeof(test<X>(0)) == sizeof(char);
+ };
+
+ /**
+ * Function that only exists if the class T has a __callStatic method
+ * @param name Name of the function
+ * @param params Parameters passed to the function
+ * @return Value
+ */
+ template<typename X>
+ typename std::enable_if<HasCallStatic<X>::value, Value >::type
+ static maybeCallStatic(const char *name, Parameters &params)
+ {
+ // call the __callStatic() function
+ return X::__callStatic(name, params);
+ }
+
+ /**
+ * Function that only exists if the class T does not have a __callStatic method
+ * @param name Name of the function
+ * @param params Parameters passed to the function
+ * @return Value
+ */
+ template<typename X>
+ typename std::enable_if<!HasCallStatic<X>::value, Value >::type
+ static maybeCallStatic(const char *name, Parameters &params)
+ {
+ // this is not implemented
+ notImplemented();
+
+ // unreachable
+ return nullptr;
+ }
+
+ /**
+ * Call a the __callStatic() function
+ * @param name Name of the function
+ * @param params Parameters passed to the function
+ * @return Value
+ */
+ virtual Value callStatic(const char *name, Parameters &params) const override
+ {
+ return maybeCallStatic<T>(name, params);
+ }
+
+ /**
+ * Call the __invoke() method
+ * @param base Object to call it on
+ * @param params Parameters to pass
+ * @return Value
+ */
+ virtual Value invoke(Base *object, Parameters &params) const override
+ {
+ // cast to actual object
+ T *obj = (T *)object;
+
+ // pass on
+ return obj->__invoke(params);
+ }
+
+ /**
+ * Cast to string function
+ * @param base
+ * @return Value
+ */
+ virtual Value toString(Base *base) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ return obj->__toString().setType(Type::String);
+ }
+
+ /**
+ * Cast to integer function
+ * @param base
+ * @return Value
+ */
+ virtual Value toInteger(Base *base) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ return obj->__toInteger().setType(Type::Numeric);
+ }
+
+ /**
+ * Cast to float function
+ * @param base
+ * @return Value
+ */
+ virtual Value toFloat(Base *base) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ return obj->__toFloat().setType(Type::Float);
+ }
+
+ /**
+ * Cast to bool function
+ * @param base
+ * @return Value
+ */
+ virtual Value toBool(Base *base) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ return obj->__toBool().setType(Type::Bool);
+ }
+
+ /**
+ * Function to retrieve a property
+ * @param base
+ * @param name
+ * @param value
+ * @return Value
+ */
+ virtual Value get(Base *base, const Value &name) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ return obj->__get(name);
+ }
/**
+ * Function to set/overwrite a property
+ * @param base
+ * @param name
+ * @param value
+ */
+ virtual void set(Base *base, const Value &name, const Value &value) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ obj->__set(name, value);
+ }
+
+ /**
+ * Function to remove a property
+ * @param base
+ * @param name
+ */
+ virtual void unset(Base *base, const Value &name) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ obj->__unset(name);
+ }
+
+ /**
+ * Function to check if a property is set
+ * @param base
+ * @param name
+ * @return bool
+ */
+ virtual bool callIsset(Base *base, const Value &name) const override
+ {
+ // cast to actual object
+ T *obj = (T *)base;
+
+ // pass on
+ return obj->__isset(name);
+ }
+
+ /**
* Compare two objects
* @param object1
* @param object2
@@ -237,7 +463,7 @@ private:
// they must be identical
return 0;
}
-
+
/**
* Namespaces have access to the private base class
*/
diff --git a/include/classbase.h b/include/classbase.h
index b4312dd..d19a1b2 100644
--- a/include/classbase.h
+++ b/include/classbase.h
@@ -107,17 +107,32 @@ public:
virtual ~ClassBase();
/**
- * Construct a new instance of the object
- * @return Base
+ * Initialize the class, given its name
+ *
+ * The module functions are registered on module startup, but classes are
+ * initialized afterwards. The Zend engine is a strange thing. Nevertheless,
+ * this means that this method is called after the module is already available.
+ * This function will inform the Zend engine about the existence of the
+ * class.
+ *
+ * @param ns Namespace name
*/
- virtual Base* construct() const = 0;
-
+ void initialize(const std::string &ns);
+
+protected:
/**
- * Create a clone of an object
- * @param orig
+ * Construct a new instance of the object, or to clone the object
* @return Base
*/
- virtual Base *clone(Base *orig) const = 0;
+ virtual Base* construct() const { return nullptr; }
+ virtual Base *clone(Base *orig) const { return nullptr; }
+
+ /**
+ * Methods to check if a certain interface is overridden
+ * @return bool
+ */
+ virtual bool traversable() const { return false; }
+ virtual bool serializable() const { return false; }
/**
* Compare two objects
@@ -125,36 +140,51 @@ public:
* @param object2
* @return int
*/
- virtual int compare(Base *object1, Base *object2) const = 0;
-
+ virtual int callCompare(Base *object1, Base *object2) const { return 1; }
+
/**
- * Is this a traversable class?
- * @return bool
+ * Call the __call(), __invoke() or __callStatic() method
+ * @param base Object to call on
+ * @param name Name of the method
+ * @param params Parameters to pass to the method
+ * @return Value
*/
- virtual bool traversable() const = 0;
-
+ virtual Value callCall(Base *base, const char *name, Parameters &params) const { return nullptr; }
+ virtual Value callInvoke(Base *base, Parameters &params) const { return nullptr; }
+ virtual Value callCallStatic(const char *name, Parameters &params) const { return nullptr; }
+
/**
- * Is this a serializable class?
- * @return bool
+ * Casting functions
+ * @param base
+ * @return Value
*/
- virtual bool serializable() const = 0;
+ virtual Value callToString(Base *base) const { return Value(Type::String); }
+ virtual Value callToInteger(Base *base) const { return Value(Type::Numeric); }
+ virtual Value callToFloat(Base *base) const { return Value(Type::Float); }
+ virtual Value callToBool(Base *base) const { return Value(Type::Bool); }
/**
- * Initialize the class, given its name
- *
- * The module functions are registered on module startup, but classes are
- * initialized afterwards. The Zend engine is a strange thing. Nevertheless,
- * this means that this method is called after the module is already available.
- * This function will inform the Zend engine about the existence of the
- * class.
- *
- * @param ns Namespace name
+ * Function to get and set properties
+ * @param base
+ * @param name
+ * @param value
+ * @return Value
*/
- void initialize(const std::string &ns);
+ virtual Value callGet(Base *base, const Value &name) const { return nullptr; }
+ virtual void callSet(Base *base, const Value &name, const Value &value) const {}
+ virtual void callUnset(Base *base, const Value &name) const {}
+ virtual bool callIsset(Base *base, const Value &name) const { return false; }
+
protected:
/**
+ * Function that can be called by a derived method when a certain function
+ * is not implemented
+ */
+ static void notImplemented();
+
+ /**
* Add a method to the class
*
* The method will be accessible as one of the class methods in your PHP
@@ -268,19 +298,24 @@ private:
const struct _zend_function_entry *entries();
/**
- * Static member functions to clone objects based on this class
+ * Static member functions to create or clone objects based on this class
+ * @param entry Pointer to class information
* @param val The object to be cloned
* @return zend_object_value Object info
*/
+ static struct _zend_object_value createObject(struct _zend_class_entry *entry);
static struct _zend_object_value cloneObject(struct _zval_struct *val);
/**
- * Function that is called when an instance of the class needs to be created.
- * This function will create the C++ class, and the PHP object
- * @param entry Pointer to the class information
- * @return zend_object_value The newly created object
+ * Static member function that get called when a method or object is called
+ * @param ht ??
+ * @param return_value Zval holding the variable to store the return value in
+ * @param return_value_ptr Pointer to the same zval
+ * @param this_ptr Object being called
+ * @param return_value_used Is the return value used or not?
*/
- static struct _zend_object_value createObject(struct _zend_class_entry *entry);
+ static void callMethod(int ht, struct _zval_struct *return_value, struct _zval_struct **return_value_ptr, struct _zval_struct *this_ptr, int return_value_used);
+ static void callInvoke(int ht, struct _zval_struct *return_value, struct _zval_struct **return_value_ptr, struct _zval_struct *this_ptr, int return_value_used);
/**
* Function that is used to count the number of elements in the object
@@ -382,29 +417,25 @@ private:
static void unsetProperty(struct _zval_struct *object, struct _zval_struct *member);
/**
- * Method that is called when a undefined method is invoked
+ * Method that returns information about the function signature of a undefined method
+ * @param object_ptr
* @param method
- * @param ht
- * @param return_value
- * @param return_value_ptr
- * @param this_ptr
- * @param return_value_used
- * @param tsrm_ls
- * @return integer
+ * @param method_len
+ * @param key
+ * @return zend_function
*/
- static int callMethod(const char *method, int ht, struct _zval_struct *return_value, struct _zval_struct **return_value_ptr, struct _zval_struct *this_ptr, int return_value_used);
- static int callMethod(char *method, int ht, struct _zval_struct *return_value, struct _zval_struct **return_value_ptr, struct _zval_struct *this_ptr, int return_value_used);
+ static union _zend_function *getMethod(struct _zval_struct **object_ptr, char *method, int method_len, const struct _zend_literal *key);
+ static union _zend_function *getMethod(struct _zval_struct **object_ptr, char *method, int method_len);
/**
- * Method that returns information about the function signature of a undefined method
+ * Method that returns information about the function signature of an undefined static method
* @param object_ptr
* @param method
* @param method_len
* @param key
* @return zend_function
*/
- static union _zend_function *getMethod(struct _zval_struct **object_ptr, char *method, int method_len, const struct _zend_literal *key);
- static union _zend_function *getMethod(struct _zval_struct **object_ptr, char *method, int method_len);
+ static union _zend_function *getStaticMethod(struct _zend_class_entry *entry, char* method, int method_len);
/**
* Method that returns information about the __invoke() method
diff --git a/include/interface.h b/include/interface.h
index f7ef387..b0469f4 100644
--- a/include/interface.h
+++ b/include/interface.h
@@ -40,59 +40,6 @@ public:
private:
/**
- * Construct a new instance of the object
- * @return Base
- */
- virtual Base* construct() const override
- {
- // this does not occur for interfaces
- return nullptr;
- }
-
- /**
- * Construct a clone of the object
- * @param orig
- * @return Base
- */
- virtual Base* clone(Base *orig) const override
- {
- // this does not occur for interfaces
- return nullptr;
- }
-
- /**
- * Is this a traversable interface?
- * @return bool
- */
- virtual bool traversable() const override
- {
- // interfaces are never traversed
- return false;
- }
-
- /**
- * Compare two objects
- * @param object1
- * @param object2
- * @return int
- */
- virtual int compare(Base *object1, Base *object2) const override
- {
- // this is never called for interfaces
- return 0;
- }
-
- /**
- * Is this a serializable class?
- * @return bool
- */
- virtual bool serializable() const override
- {
- // not called for interfaces
- return false;
- }
-
- /**
* Namespaces have access to the private base class
*/
friend class Namespace;
diff --git a/src/base.cpp b/src/base.cpp
index 516663d..2c46147 100644
--- a/src/base.cpp
+++ b/src/base.cpp
@@ -86,7 +86,7 @@ MixedObject *Base::store(zend_class_entry *entry)
* @param key
* @return bool
*/
-bool Base::__isset(const Php::Value &key)
+bool Base::__isset(const Php::Value &key) const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the unset function can be called
@@ -102,7 +102,7 @@ bool Base::__isset(const Php::Value &key)
* @param key
* @param value
*/
-void Base::__set(const Php::Value &key, const Php::Value &value)
+void Base::__set(const Php::Value &key, const Php::Value &value) const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the unset function can be called
@@ -118,7 +118,7 @@ void Base::__set(const Php::Value &key, const Php::Value &value)
* @param key
* @return value
*/
-Php::Value Base::__get(const Php::Value &key)
+Php::Value Base::__get(const Php::Value &key) const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -136,7 +136,7 @@ Php::Value Base::__get(const Php::Value &key)
*
* @param key
*/
-void Base::__unset(const Php::Value &key)
+void Base::__unset(const Php::Value &key) const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -154,7 +154,7 @@ void Base::__unset(const Php::Value &key)
* @param params The parameters that were passed to the function
* @return Value The return value
*/
-Value Base::__call(const char *method, Parameters &params)
+Value Base::__call(const char *method, Parameters &params) const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -173,7 +173,7 @@ Value Base::__call(const char *method, Parameters &params)
* @param params The parameters that were passed to the function
* @return Value The return value
*/
-Value Base::__invoke(Parameters &params)
+Value Base::__invoke(Parameters &params) const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -191,7 +191,7 @@ Value Base::__invoke(Parameters &params)
*
* @return Value The object as a string
*/
-Value Base::__toString()
+Value Base::__toString() const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -209,7 +209,7 @@ Value Base::__toString()
*
* @return int Integer value
*/
-long Base::__toInteger()
+Value Base::__toInteger() const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -227,7 +227,7 @@ long Base::__toInteger()
*
* @return double Floating point value
*/
-double Base::__toFloat()
+Value Base::__toFloat() const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -245,7 +245,7 @@ double Base::__toFloat()
*
* @return bool
*/
-bool Base::__toBool()
+Value Base::__toBool() const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
@@ -256,14 +256,14 @@ bool Base::__toBool()
}
/**
- * Comparison operator
+ * Compare the object with a different object
*
* Check how a different object compares to this object
*
* @param that Object to compare with
* @return int
*/
-bool Base::operator<(const Base &that) const
+int Base::__compare(const Base &that) const
{
// throw an exception that will be caught in the ClassBase class,
// so that the default implementation of the function can be called
diff --git a/src/classbase.cpp b/src/classbase.cpp
index ac3a0f1..348aaf3 100644
--- a/src/classbase.cpp
+++ b/src/classbase.cpp
@@ -52,22 +52,39 @@ static Base *cpp_object(const zval *val)
}
/**
+ * Extended zend_internal_function structure that we use to store an
+ * instance of the ClassBase object. We need this for static method calls
+ */
+struct CallData
+{
+ // the internal function is the first member, so
+ // that it is possible to cast an instance of this
+ // struct to a zend_internal_function
+ zend_internal_function func;
+
+ // and a pointer to the ClassBase object
+ ClassBase *self;
+};
+
+/**
* Handler function that runs the __call function
* @param ... All normal parameters for function calls
*/
-static void call_method(INTERNAL_FUNCTION_PARAMETERS)
+void ClassBase::callMethod(INTERNAL_FUNCTION_PARAMETERS)
{
// retrieve the originally called (and by us allocated) function object
// (this was copied from the zend engine source code, code looks way too
// ugly to be made by me)
- zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function;
+ CallData *data = (CallData *)EG(current_execute_data)->function_state.function;
+ zend_internal_function *func = &data->func;
// retrieve the function name
const char *name = func->function_name;
+ ClassBase *meta = data->self;
- // the function structure was allocated by ourselves in the get_method function,
- // we no longer need it now
- efree(func);
+ // the data structure was allocated by ourselves in the getMethod or
+ // getStaticMethod functions, we no longer need it now
+ efree(data);
// the function could throw an exception
try
@@ -80,9 +97,10 @@ static void call_method(INTERNAL_FUNCTION_PARAMETERS)
// retrieve the base object
Base *base = params.object();
-
- // call the actual __call method on the base object
- result = base->__call(name, params);
+
+ // is this a static, or a non-static call?
+ if (base) result = meta->callCall(base, name, params);
+ else result = meta->callCallStatic(name, params);
}
catch (const NotImplemented &exception)
{
@@ -100,15 +118,19 @@ static void call_method(INTERNAL_FUNCTION_PARAMETERS)
* Handler function that runs the __invoke function
* @param ... All normal parameters for function calls
*/
-static void call_invoke(INTERNAL_FUNCTION_PARAMETERS)
+void ClassBase::callInvoke(INTERNAL_FUNCTION_PARAMETERS)
{
// retrieve the originally called (and by us allocated) function object
// (this was copied from the zend engine source code, code looks way too
// ugly to be made by me)
- zend_internal_function *func = (zend_internal_function *)EG(current_execute_data)->function_state.function;
+ CallData *data = (CallData *)EG(current_execute_data)->function_state.function;
- // we no longer need it (question: why have we allocated this on the heap???)
- efree(func);
+ // get self reference
+ ClassBase *meta = data->self;
+
+ // the data structure was allocated by ourselves in the getMethod or
+ // getStaticMethod functions, we no longer need it now
+ efree(data);
// the function could throw an exception
try
@@ -123,7 +145,7 @@ static void call_invoke(INTERNAL_FUNCTION_PARAMETERS)
Base *base = params.object();
// call the actual __invoke method on the base object
- result = base->__invoke(params);
+ result = meta->callInvoke(base, params);
}
catch (const NotImplemented &exception)
{
@@ -178,21 +200,68 @@ zend_function *ClassBase::getMethod(zval **object_ptr, char *method_name, int me
// but we'll follow thread safe implementation of the Zend engine here, although
// it is strange to allocate and free memory in one and the same method call (free()
// call happens in call_method())
- auto *function = (zend_internal_function *)emalloc(sizeof(zend_internal_function));
+ auto *data = (CallData *)emalloc(sizeof(CallData));
+ auto *function = &data->func;
// we're going to set all properties
function->type = ZEND_INTERNAL_FUNCTION;
function->module = nullptr;
- function->handler = call_method;
+ function->handler = &ClassBase::callMethod;
function->arg_info = nullptr;
function->num_args = 0;
function->scope = entry;
function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
function->function_name = method_name;
+ // store pointer to ourselves
+ data->self = cpp_class(entry);
+
// done (cast to zend_function* is allowed, because a zend_function is a union
// that has one member being a zend_internal_function)
- return (zend_function *)function;
+ return (zend_function *)data;
+}
+
+/**
+ * Method that is called right before a static method call is attempted
+ * @param entry
+ * @param method
+ * @param method_len
+ * @return zend_function
+ */
+zend_function *ClassBase::getStaticMethod(zend_class_entry *entry, char* method, int method_len)
+{
+ // first we'll check if the default handler does not have an implementation,
+ // in that case the method is probably already implemented as a regular method
+#if PHP_VERSION_ID < 50399
+ auto *defaultFunction = zend_std_get_static_method(entry, method, method_len);
+#else
+ auto *defaultFunction = zend_std_get_static_method(entry, method, method_len, nullptr);
+#endif
+
+ // did the default implementation do anything?
+ if (defaultFunction) return defaultFunction;
+
+ // just like we did in getMethod() (see comment there) we are going to dynamically
+ // allocate data holding information about the function
+ auto *data = (CallData *)emalloc(sizeof(CallData));
+ auto *function = &data->func;
+
+ // we're going to set all properties
+ function->type = ZEND_INTERNAL_FUNCTION;
+ function->module = nullptr;
+ function->handler = ClassBase::callMethod;
+ function->arg_info = nullptr;
+ function->num_args = 0;
+ function->scope = nullptr;
+ function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
+ function->function_name = method;
+
+ // store pointer to ourselves
+ data->self = cpp_class(entry);
+
+ // done (cast to zend_function* is allowed, because a zend_function is a union
+ // that has one member being a zend_internal_function)
+ return (zend_function *)data;
}
/**
@@ -217,22 +286,26 @@ int ClassBase::getClosure(zval *object, zend_class_entry **entry_ptr, zend_funct
// just like we did for getMethod(), we're going to dynamically allocate memory
// with all information about the function
- auto *function = (zend_internal_function *)emalloc(sizeof(zend_internal_function));
+ auto *data = (CallData *)emalloc(sizeof(CallData));
+ auto *function = &data->func;
// we're going to set all properties
function->type = ZEND_INTERNAL_FUNCTION;
function->module = nullptr;
- function->handler = call_invoke;
+ function->handler = &ClassBase::callInvoke;
function->arg_info = nullptr;
function->num_args = 0;
function->scope = entry;
function->fn_flags = ZEND_ACC_CALL_VIA_HANDLER;
function->function_name = nullptr;
+ // store pointer to ourselves
+ data->self = cpp_class(entry);
+
// assign this dynamically allocated variable to the func parameter
// the case is ok, because zend_internal_function is a member of the
// zend_function union
- *func = (zend_function *)function;
+ *func = (zend_function *)data;
// the object_ptr should be filled with the object on which the method is
// called (otherwise the Zend engine tries to call the method statically)
@@ -320,7 +393,7 @@ int ClassBase::compare(zval *object1, zval *object2)
Base *base2 = cpp_object(object2);
// run the compare method
- return meta->compare(base1, base2);
+ return meta->callCompare(base1, base2);
}
catch (const NotImplemented &exception)
{
@@ -353,6 +426,12 @@ int ClassBase::cast(zval *object, zval *retval, int type)
// get the base object
Base *base = cpp_object(object);
+ // retrieve the class entry linked to this object
+ auto *entry = zend_get_class_entry(object);
+
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
+
// retval it not yet initialized --- and again feelings of disbelief,
// frustration, wonder and anger come up when you see that there are not two
// functions in the Zend engine that have a comparable API
@@ -370,11 +449,11 @@ int ClassBase::cast(zval *object, zval *retval, int type)
// check type
switch ((Type)type) {
- case Type::Numeric: result = Value(base->__toInteger()).detach(); break;
- case Type::Float: result = Value(base->__toFloat()).detach(); break;
- case Type::Bool: result = Value(base->__toBool()).detach(); break;
- case Type::String: result = base->__toString().setType(Type::String).detach(); break;
- default: throw NotImplemented(); break;
+ case Type::Numeric: result = meta->callToInteger(base).detach(); break;
+ case Type::Float: result = meta->callToFloat(base).detach(); break;
+ case Type::Bool: result = meta->callToBool(base).detach(); break;
+ case Type::String: result = meta->callToString(base).detach(); break;
+ default: throw NotImplemented(); break;
}
// @todo do we turn into endless conversion if the __toString object returns 'this' ??
@@ -733,12 +812,21 @@ zval *ClassBase::readProperty(zval *object, zval *name, int type, const struct _
// from PHP. If someone wants to get a reference to such an internal variable,
// that is in most cases simply impossible.
+ // retrieve the object and class
+ Base *base = cpp_object(object);
+
+ // retrieve the class entry linked to this object
+ auto *entry = zend_get_class_entry(object);
+
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
+
// the default implementation throws an exception, so by catching
// the exception we know if the object was implemented by the user or not
try
{
// retrieve value
- Value value = cpp_object(object)->__get(name);
+ Value value = meta->callGet(base, name);
// because we do not want the value object to destruct the zval when
// it falls out of scope, we detach the zval from it, if this is a regular
@@ -796,12 +884,21 @@ void ClassBase::writeProperty(zval *object, zval *name, zval *value)
void ClassBase::writeProperty(zval *object, zval *name, zval *value, const struct _zend_literal *key)
#endif
{
+ // retrieve the object and class
+ Base *base = cpp_object(object);
+
+ // retrieve the class entry linked to this object
+ auto *entry = zend_get_class_entry(object);
+
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
+
// the default implementation throws an exception, if we catch that
// we know for sure that the user has not overridden the __set method
try
{
// call the default
- cpp_object(object)->__set(name, value);
+ meta->callSet(base, name, value);
}
catch (const NotImplemented &exception)
{
@@ -855,14 +952,20 @@ int ClassBase::hasProperty(zval *object, zval *name, int has_set_exists, const s
// get the cpp object
Base *base = cpp_object(object);
+ // retrieve the class entry linked to this object
+ auto *entry = zend_get_class_entry(object);
+
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
+
// call the C++ object
- if (!base->__isset(name)) return false;
+ if (!meta->callIsset(base, name)) return false;
// property exists, but what does the user want to know
if (has_set_exists == 2) return true;
// we have to retrieve the property
- Value value = base->__get(name);
+ Value value = meta->callGet(base, name);
// should we check on NULL?
switch (has_set_exists) {
@@ -912,8 +1015,14 @@ void ClassBase::unsetProperty(zval *object, zval *member, const struct _zend_lit
// we know for sure that the user has not overridden the __unset method
try
{
+ // retrieve the class entry linked to this object
+ auto *entry = zend_get_class_entry(object);
+
+ // we need the C++ class meta-information object
+ ClassBase *meta = cpp_class(entry);
+
// call the __unset method
- cpp_object(object)->__unset(member);
+ meta->callUnset(cpp_object(object), member);
}
catch (const NotImplemented &exception)
{
@@ -1130,6 +1239,9 @@ void ClassBase::initialize(const std::string &prefix)
// we need a special constructor
entry.create_object = &ClassBase::createObject;
+ // register function that is called for static method calls
+ entry.get_static_method = &ClassBase::getStaticMethod;
+
// for traversable classes we install a special method to get the iterator
if (traversable()) entry.get_iterator = &ClassBase::getIterator;
@@ -1183,6 +1295,16 @@ void ClassBase::initialize(const std::string &prefix)
}
/**
+ * Function that can be called by a derived method when a certain function
+ * is not implemented
+ */
+void ClassBase::notImplemented()
+{
+ // throw an exception
+ throw NotImplemented();
+}
+
+/**
* Add a method to the class
* @param name Name of the method
* @param method The actual method