From c9274ab3c422390998e628820afc6a27c12a1a57 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 09:39:14 +0100 Subject: introduced super globals Php::POST, Php::GET, et cetera, fixed setting array members, introduced Value::attach() method --- include/array.h | 7 +----- include/globals.h | 2 +- include/hashmember.h | 30 ++++++++++++------------- include/value.h | 19 ++++++++++++++++ phpcpp.h | 1 + src/extension.cpp | 2 +- src/includes.h | 1 + src/value.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 101 insertions(+), 23 deletions(-) diff --git a/include/array.h b/include/array.h index dcd5e5f..26fec24 100644 --- a/include/array.h +++ b/include/array.h @@ -12,7 +12,7 @@ * Set up namespace */ namespace Php { - + /** * Class definition */ @@ -123,11 +123,6 @@ public: } }; -/** - * Define for arrays and objects - */ -using Array = Array; - /** * End of namespace */ diff --git a/include/globals.h b/include/globals.h index 0278c88..b996131 100644 --- a/include/globals.h +++ b/include/globals.h @@ -68,7 +68,7 @@ public: }; /** - * We always have one instance + * We always have one instance of the GLOBALS instance * @var Globals */ extern Globals &GLOBALS; diff --git a/include/hashmember.h b/include/hashmember.h index 65ca23d..41c40d3 100644 --- a/include/hashmember.h +++ b/include/hashmember.h @@ -43,7 +43,7 @@ public: HashMember &operator=(const Value &value) { // set property in parent array - _base.set(_index, value); + _base->set(_index, value); // if there is a parent, it should sets its value too if (_parent) _parent->operator=(_base); @@ -58,7 +58,7 @@ public: */ Value value() const { - return _base.get(_index); + return _base->get(_index); } /** @@ -67,7 +67,7 @@ public: */ operator Value () const { - return _base.get(_index); + return _base->get(_index); } /** @@ -76,7 +76,7 @@ public: */ operator int16_t () const { - return _base.get(_index).numericValue(); + return _base->get(_index).numericValue(); } /** @@ -85,7 +85,7 @@ public: */ operator int32_t () const { - return _base.get(_index).numericValue(); + return _base->get(_index).numericValue(); } /** @@ -94,7 +94,7 @@ public: */ operator int64_t () const { - return _base.get(_index).numericValue(); + return _base->get(_index).numericValue(); } /** @@ -103,7 +103,7 @@ public: */ operator bool () const { - return _base.get(_index).boolValue(); + return _base->get(_index).boolValue(); } /** @@ -112,7 +112,7 @@ public: */ operator std::string () const { - return _base.get(_index).stringValue(); + return _base->get(_index).stringValue(); } /** @@ -121,7 +121,7 @@ public: */ operator const char * () const { - return _base.get(_index).rawValue(); + return _base->get(_index).rawValue(); } /** @@ -130,7 +130,7 @@ public: */ operator double () const { - return _base.get(_index).decimalValue(); + return _base->get(_index).decimalValue(); } /** @@ -141,7 +141,7 @@ public: */ HashMember operator[](int index) { - return _base.get(_index)[index].add(this); + return _base->get(_index)[index].add(this); } /** @@ -152,7 +152,7 @@ public: */ HashMember operator[](const std::string &key) { - return _base.get(_index)[key].add(this); + return _base->get(_index)[key].add(this); } /** @@ -163,7 +163,7 @@ public: */ HashMember operator[](const char *key) { - return _base.get(_index)[key].add(this); + return _base->get(_index)[key].add(this); } /** @@ -359,7 +359,7 @@ private: * @param base Base value * @param index Index in the array */ - HashMember(const Value *base, Type index) : _base(*base), _index(index) {} + HashMember(Value *base, Type index) : _base(base), _index(index) {} // @todo add move constructor @@ -390,7 +390,7 @@ private: * Base value * @var Value */ - Value _base; + Value *_base; /** * Parent member (in case of nested members) diff --git a/include/value.h b/include/value.h index f2850c9..9c48f1f 100644 --- a/include/value.h +++ b/include/value.h @@ -111,6 +111,12 @@ public: */ Value(struct _zval_struct *zval, bool ref = false); + /** + * Wrap around a hash table + * @param ht Hashtable to wrap + */ + Value(struct _hashtable *ht); + /** * Wrap around an object implemented by us * @param object Object to be wrapped @@ -845,6 +851,18 @@ protected: */ struct _zval_struct *detach(); + /** + * Attach a different zval + * + * This will first detach the current zval, and link the Value object to + * a different zval. Versions exist to attach to a zval and to an entire + * hash table + * + * @param val + */ + void attach(struct _zval_struct *val); + void attach(struct _hashtable *hashtable); + /** * Set a certain property without running any checks (you must already know * for sure that this is an array, and that the index is not yet in use) @@ -874,6 +892,7 @@ protected: friend class Member; friend class ClassBase; friend class Iterator; + friend class Extension; }; /** diff --git a/phpcpp.h b/phpcpp.h index 599c2cb..f58eb8d 100644 --- a/phpcpp.h +++ b/phpcpp.h @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/src/extension.cpp b/src/extension.cpp index 3f29d9c..4c6a46f 100644 --- a/src/extension.cpp +++ b/src/extension.cpp @@ -117,7 +117,7 @@ int Extension::onStartup(int type, int module_number) // is the callback registered? if (extension->_onStartup) extension->_onStartup(); - + // done return BOOL2SUCCESS(true); } diff --git a/src/includes.h b/src/includes.h index 50d4144..b2cb353 100644 --- a/src/includes.h +++ b/src/includes.h @@ -55,6 +55,7 @@ #include "../include/byval.h" #include "../include/byref.h" #include "../include/global.h" +#include "../include/super.h" #include "../include/hashmember.h" #include "../include/parameters.h" #include "../include/modifiers.h" diff --git a/src/value.cpp b/src/value.cpp index a78c2d4..29b6dcd 100644 --- a/src/value.cpp +++ b/src/value.cpp @@ -169,6 +169,21 @@ Value::Value(struct _zval_struct *val, bool ref) Z_SET_ISREF_P(_val); } +/** + * Wrap around a hash table + * @param ht Hashtable to wrap + */ +Value::Value(HashTable *ht) +{ + // construct a zval + MAKE_STD_ZVAL(_val); + Z_ARRVAL_P(_val) = ht; + Z_TYPE_P(_val) = IS_ARRAY; + + // add a reference + Z_ADDREF_P(_val); +} + /** * Wrap around an object * @param object @@ -302,6 +317,9 @@ Value::~Value() */ zval *Value::detach() { + // leap out if already detached + if (!_val) return nullptr; + // copy return value zval *result = _val; @@ -315,6 +333,50 @@ zval *Value::detach() return result; } +/** + * Attach a different zval + * + * This will first detach the current zval, and link the Value object to + * a different zval. + * + * @param val + */ +void Value::attach(struct _zval_struct *val) +{ + // detach first + if (_val) detach(); + + // store the zval + _val = val; + + // add one more reference + Z_ADDREF_P(_val); +} + +/** + * Attach a different zval + * + * This will first detach the current zval, and link the Value object to + * a new zval + * + * @param hashtable + */ +void Value::attach(struct _hashtable *hashtable) +{ + // detach first + if (_val) detach(); + + // construct a new zval + MAKE_STD_ZVAL(_val); + + // store pointer to the hashtable, and mark the zval as an array + Z_ARRVAL_P(_val) = hashtable; + Z_TYPE_P(_val) = IS_ARRAY; + + // add a reference + Z_ADDREF_P(_val); +} + /** * Retrieve the refcount * @return int -- cgit v1.2.3 From 159781ee8257329ca9c40306f7495a8c2f31f710 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 09:47:54 +0100 Subject: update documentation, added super.h and super.cpp files that I forgot in previous commit --- documentation/variables.html | 22 +++++++++++-- include/super.h | 76 ++++++++++++++++++++++++++++++++++++++++++++ src/super.cpp | 59 ++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 include/super.h create mode 100644 src/super.cpp diff --git a/documentation/variables.html b/documentation/variables.html index 6b63598..b3f4330 100644 --- a/documentation/variables.html +++ b/documentation/variables.html @@ -286,7 +286,7 @@ std::cout << array("Y-m-d H:i:s") << std::endl;

Global variables

To read or update global PHP variables, you can use the Php::GLOBALS - variable. This variable works more or less the same as the $_GLOBALS + variable. This variable works more or less the same as the $GLOBALS variable in a PHP script.

@@ -305,7 +305,25 @@ std::cout << Php::GLOBALS["b"] << std::endl;

- Unlike PHP scripts, that handles only single pageviews, an extension is + Next to the $GLOBALS variable, PHP allows you to access variables using + the $_GET, $_POST, $_COOKIE, $_FILES, $_SERVER, $_REQUEST and $_ENV variables. + In your C++ extension you can do something similar with the global variables + Php::GET, Php::POST, Php::COOKIE, Php::FILES, Php::SERVER, Php::REQUEST and + Php::ENV. These are global, read-only, objects with an overloaded operator[] + method. You can thus access them as if it were associative arrays. +

+

+


+// retrieve the value of a request variable
+int age = Php::REQUEST["name"];
+
+// or retrieve the value of a server variable
+std::string referer = Php::SERVER["HTTP_REFERER"];
+
+

+

Be careful with global C++ variables

+

+ Unlike PHP scripts, that only handle single pageviews, an extension is used to handle multiple pageviews after each other. This means that when you use global C++ (!) variables in your extension, that these variables are not set back to their initial value in between the pageviews. The diff --git a/include/super.h b/include/super.h new file mode 100644 index 0000000..73e3761 --- /dev/null +++ b/include/super.h @@ -0,0 +1,76 @@ +/** + * Super.h + * + * The Super class is used to implement one of the super variables $_POST, + * $_GET, $_SERVER, et cetera + * + * @copyright 2014 Copernica BV + * @author Emiel Bruijntjes + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class Super +{ +public: + /** + * Constructor + * + * Extension writers do not have to access the super-globals themselves. + * They are always accessible via Php::POST, Php::GET, et cetera. + * + * @param index number + */ + Super(int index) : _index(index) {} + + /** + * Destructor + */ + virtual ~Super() {} + + /** + * Array access operator + * This can be used for accessing associative arrays + * @param key + * @return Value + */ + Value operator[](const std::string &key) const; + + /** + * Array access operator + * This can be used for accessing associative arrays + * @param key + * @return Value + */ + Value operator[](const char *key) const; + +private: + /** + * Index number + * @var int + */ + int _index; +}; + +/** + * A number of super-globals are always accessible + */ +extern Super POST; +extern Super GET; +extern Super COOKIE; +extern Super SERVER; +extern Super ENV; +extern Super FILES; +extern Super REQUEST; + +/** + * End namespace + */ +} + diff --git a/src/super.cpp b/src/super.cpp new file mode 100644 index 0000000..6b02916 --- /dev/null +++ b/src/super.cpp @@ -0,0 +1,59 @@ +/** + * Super.cpp + * + * @copyright 2014 Copernica BV + * @author Emiel Bruijntjes + */ +#include "includes.h" + +/** + * Set up namespace + */ +namespace Php { + +/** + * A number of super-globals are always accessible + */ +Super POST (TRACK_VARS_POST); +Super GET (TRACK_VARS_GET); +Super COOKIE (TRACK_VARS_COOKIE); +Super SERVER (TRACK_VARS_SERVER); +Super ENV (TRACK_VARS_ENV); +Super FILES (TRACK_VARS_FILES); +Super REQUEST (TRACK_VARS_REQUEST); + +/** + * Array access operator + * This can be used for accessing associative arrays + * @param key + * @return Value + */ +Value Super::operator[](const std::string &key) const +{ + // create a value object that wraps around the actual zval + Value value(PG(http_globals)[_index]); + + // pass on the call + return value[key]; +} + +/** + * Array access operator + * This can be used for accessing associative arrays + * @param key + * @return Value + */ +Value Super::operator[](const char *key) const +{ + // create a value object that wraps around the actual zval + Value value(PG(http_globals)[_index]); + + // pass on the call + return value[key]; +} + +/** + * End namespace + */ +} + -- cgit v1.2.3 From 4c55148476952276ece19f5b975ca0a0233dee4c Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 10:32:32 +0100 Subject: implemented __destruct magic method --- include/base.h | 5 +++++ include/class.h | 15 ++++++++++++++- include/classbase.h | 13 +++++++++++++ src/base.cpp | 35 +++++++++++------------------------ src/classbase.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 25 deletions(-) diff --git a/include/base.h b/include/base.h index 6cf75df..2d5b815 100644 --- a/include/base.h +++ b/include/base.h @@ -111,6 +111,11 @@ public: { return Value(this)[name]; } + + /** + * Overridable method that is called right before an object is destructed + */ + void __destruct() const; /** * Overridable method that is called to check if a property is set diff --git a/include/class.h b/include/class.h index 9fa14a7..31e29a8 100644 --- a/include/class.h +++ b/include/class.h @@ -247,7 +247,7 @@ private: T *object = (T *)base; // call the method on the base object - return base->__call(name, params); + return object->__call(name, params); } /** @@ -301,6 +301,19 @@ private: return nullptr; } + /** + * Call the __destruct method + * @param object + */ + virtual Value callDestruct(Base *base) const override + { + // cast to actual object + T *obj = (T *)base; + + // pass on + return obj->__destruct(); + } + /** * Call a the __callStatic() function * @param name Name of the function diff --git a/include/classbase.h b/include/classbase.h index 0ea6ea4..24e8f5a 100644 --- a/include/classbase.h +++ b/include/classbase.h @@ -149,6 +149,11 @@ protected: */ virtual int callCompare(Base *object1, Base *object2) const { return 1; } + /** + * Call the __destruct method + */ + virtual Value callDestruct(Base *base) const { return nullptr; } + /** * Call the __call(), __invoke() or __callStatic() method * @param base Object to call on @@ -283,6 +288,8 @@ private: */ static struct _zend_object_value createObject(struct _zend_class_entry *entry); static struct _zend_object_value cloneObject(struct _zval_struct *val); + static void destructObject(struct _zend_object *object, unsigned int handle); + static void freeObject(struct _zend_object *object); /** * Static member function that get called when a method or object is called @@ -495,6 +502,12 @@ private: * @var std::list */ std::list> _members; + + /** + * Base object has access to the members + * This is needed by the Base::store() method + */ + friend class Base; }; /** diff --git a/src/base.cpp b/src/base.cpp index 2c46147..f677bcf 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -12,29 +12,6 @@ */ namespace Php { -/** - * Function that is called to clean up space that is occupied by the object - * @param object The object to be deallocated - */ -static void deallocate_object(void *object TSRMLS_DC) -{ - // allocate memory for the object - MixedObject *obj = (MixedObject *)object; - - // deallocate the cpp object - if (obj->cpp) delete obj->cpp; - - // get rid of the object properties - // @todo if we enable the following two lines, segmentation - // faults and memory corruption occurs. however, the online - // documentation does it like this - //zend_hash_destroy(obj->php.properties); - //FREE_HASHTABLE(obj->php.properties); - - // deallocate the entire object - efree(obj); -} - /** * Store the object in the PHP object cache * @param entry Class entry @@ -71,12 +48,22 @@ MixedObject *Base::store(zend_class_entry *entry) // the destructor and clone handlers are set to NULL. I dont know why, but they do not // seem to be necessary... - _handle = zend_objects_store_put(result, NULL, deallocate_object, NULL TSRMLS_CC); + _handle = zend_objects_store_put(result, (zend_objects_store_dtor_t)ClassBase::destructObject, (zend_objects_free_object_storage_t)ClassBase::freeObject, NULL TSRMLS_CC); // done return result; } +/** + * Overridable method that is called right before an object is destructed + */ +void Base::__destruct() const +{ + // throw exception, so that the PHP-CPP library will check if the user + // somehow registered an explicit __destruct method + throw NotImplemented(); +} + /** * Overridable method that is called to check if a property is set * diff --git a/src/classbase.cpp b/src/classbase.cpp index ec316cd..2fae658 100644 --- a/src/classbase.cpp +++ b/src/classbase.cpp @@ -1076,6 +1076,53 @@ void ClassBase::unsetProperty(zval *object, zval *member, const struct _zend_lit } } +/** + * Function that is called when an object is about to be destructed + * This will call the magic __destruct method + */ +void ClassBase::destructObject(zend_object *object, zend_object_handle handle) +{ + // allocate memory for the object + MixedObject *obj = (MixedObject *)object; + + // get meta info + ClassBase *meta = cpp_class(object->ce); + + // prevent exceptions + try + { + // call the destruct function + if (obj->cpp) meta->callDestruct(obj->cpp); + } + catch (const NotImplemented &exception) + { + // fallback on the default destructor call + zend_objects_destroy_object(object, handle); + } + catch (Exception &exception) + { + // a regular Php::Exception was thrown by the extension, pass it on + // to PHP user space + exception.process(); + } +} + +/** + * Function that is called to clean up space that is occupied by the object + * @param object The object to be deallocated + */ +void ClassBase::freeObject(zend_object *object) +{ + // allocate memory for the object + MixedObject *obj = (MixedObject *)object; + + // deallocate the cpp object + if (obj->cpp) delete obj->cpp; + + // pass on to the default destructor + zend_objects_free_object_storage(object); +} + /** * 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 -- cgit v1.2.3 From 91e1175a467cb9e2f90e7421a1398430d075f776 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 11:14:58 +0100 Subject: removed return value for __destruct() method, improved documentation --- documentation/magic-methods.html | 75 ++++++++++++++++++++++++++++++++++++---- include/class.h | 4 +-- include/classbase.h | 2 +- 3 files changed, 72 insertions(+), 9 deletions(-) diff --git a/documentation/magic-methods.html b/documentation/magic-methods.html index 8a1fbfa..2b40273 100644 --- a/documentation/magic-methods.html +++ b/documentation/magic-methods.html @@ -10,13 +10,21 @@ in your class, and if they do, they will be compiled into your extension and called when they are accessed from PHP.

+

Compile time detection

- The signature of the methods is flexible. Most of the methods accept - Php::Value objects and also return Php::Value objects, but if your own - magic methods have a slightly different signature (they return an integer - for example) it will be picked up by the compiler too because the Php::Value - has many implicit constructors to convert other types into Php::Value - objects. + Although you may think that the magic methods are virtual methods that + are overridden from the Php::Base class, they are not. The methods are + detected by the C++ compiler at compile time - and are very normal methods + that just happen to have a certain name. +

+

+ Because of the compile time detection, the signature of the methods is + somewhat flexible. The return values of many magic methods are assigned to + Php::Value objects, which means that as long as you make sure + that your magic method returns a type that is assignable to a Php::Value, + you can use it in your class. Your __toString() method may thus return a + char*, a std::string, Php::Value (and even an integer!), because all these + types can be assigned to a Php::Value.

The nice thing about magic methods implemented with PHP-CPP is that they @@ -25,6 +33,61 @@ can not be called explicitly from PHP scripts - but they do get called when a property is accessed.

+

Constructors

+

+ We start with an exception to the rule. Normally, you do not have to register + magic methods to make them work. When you add a magic method like + __toString() or __get() to your class, it will automatically be + called when an object is casted to a string or a property is accessed. There + is no need to enable the magic method explicitly in the get_module() startup + function. +

+

+ The only exception to this rule is the __construct() method. This method + does have to be explicitly registered. There are a number of reasons for this. + For a start, the __construct() method does not have a fixed signature, and + by explicitly adding it to the extension, you can also exactly specify what + parameters it accepts, and whether the __construct() method should be + private or protected (if you want to create classes that can not be + instantiated from PHP). +

+

+ The other reason why you have to explicitly register the __construct() method, + is that, unlike other magic methods, the magic __construct method must + be visible from PHP. Inside constructors of derived classes, it often is + necessary to make a call to parent::__construct(). By registering the + __construct() method in the get_module() function you make the function + visible from PHP, which makes constructs like this possible. +

+

+ We have a special article about + constructors and destructors with multiple examples how to register + the __construct() method. +

+

Clone and destruct

+

+ The magic __clone() method is very similar to the __construct() method. It + is the first method to be called on a new object after it is copy + constructed. The __destruct() is the method that gets called right before + the object gets destructed (and the C++ destructor runs). +

+

+ The __clone() and __destruct() methods are regular magic methods. You do + not have to register them to make them active. The PHP-CPP library calls + them automatically if they are available. +

+

+ In normal circumstances you probably do not need these methods very often. + The C++ copy constructor and the C++ destructor can be used too. The only + difference is that the magic methods are called on objects that are in a + fully initialized state, while the C++ copy constructor and C++ destructor + work on objects that are being initialized, or that are + being destructed. +

+

+ The article about mentioned above + as more details and examples. +

Pseudo properties

With the methods __get(), __set(), __unset() and __isset() you can define diff --git a/include/class.h b/include/class.h index 31e29a8..008ff8a 100644 --- a/include/class.h +++ b/include/class.h @@ -305,13 +305,13 @@ private: * Call the __destruct method * @param object */ - virtual Value callDestruct(Base *base) const override + virtual void callDestruct(Base *base) const override { // cast to actual object T *obj = (T *)base; // pass on - return obj->__destruct(); + obj->__destruct(); } /** diff --git a/include/classbase.h b/include/classbase.h index 24e8f5a..1979f94 100644 --- a/include/classbase.h +++ b/include/classbase.h @@ -152,7 +152,7 @@ protected: /** * Call the __destruct method */ - virtual Value callDestruct(Base *base) const { return nullptr; } + virtual void callDestruct(Base *base) const {} /** * Call the __call(), __invoke() or __callStatic() method -- cgit v1.2.3 From 1f1a0fa9349d37e623ae763b48c7ea21681cd45b Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 12:42:35 +0100 Subject: implementation of properties using callback methods --- documentation/magic-methods.html | 24 ++++---- include/class.h | 22 ++++++- include/classbase.h | 31 ++++++++++ src/classbase.cpp | 130 +++++++++++++++++++++++++++------------ src/includes.h | 1 + src/property.h | 82 ++++++++++++++++++++++++ 6 files changed, 239 insertions(+), 51 deletions(-) create mode 100644 src/property.h diff --git a/documentation/magic-methods.html b/documentation/magic-methods.html index 2b40273..9294a3e 100644 --- a/documentation/magic-methods.html +++ b/documentation/magic-methods.html @@ -12,8 +12,8 @@

Compile time detection

- Although you may think that the magic methods are virtual methods that - are overridden from the Php::Base class, they are not. The methods are + Although you may have expected that the magic methods are virtual methods in + the Php::Base class that can be overridden, they are not. The methods are detected by the C++ compiler at compile time - and are very normal methods that just happen to have a certain name.

@@ -46,18 +46,18 @@ The only exception to this rule is the __construct() method. This method does have to be explicitly registered. There are a number of reasons for this. For a start, the __construct() method does not have a fixed signature, and - by explicitly adding it to the extension, you can also exactly specify what - parameters it accepts, and whether the __construct() method should be + by explicitly adding it to the extension, you can also specify what + parameters it accepts, and whether the __construct() method should be public, private or protected (if you want to create classes that can not be instantiated from PHP).

The other reason why you have to explicitly register the __construct() method, - is that, unlike other magic methods, the magic __construct method must + is that, unlike other magic methods, the __construct method must be visible from PHP. Inside constructors of derived classes, it often is necessary to make a call to parent::__construct(). By registering the __construct() method in the get_module() function you make the function - visible from PHP, which makes constructs like this possible. + visible from PHP.

We have a special article about @@ -72,12 +72,12 @@ the object gets destructed (and the C++ destructor runs).

- The __clone() and __destruct() methods are regular magic methods. You do - not have to register them to make them active. The PHP-CPP library calls - them automatically if they are available. + The __clone() and __destruct() methods are regular magic methods and you + therefore do not have to register them to make them active. The PHP-CPP + library calls them automatically if they are available.

- In normal circumstances you probably do not need these methods very often. + In normal circumstances you probably have not need for these methods. The C++ copy constructor and the C++ destructor can be used too. The only difference is that the magic methods are called on objects that are in a fully initialized state, while the C++ copy constructor and C++ destructor @@ -85,8 +85,8 @@ being destructed.

- The article about mentioned above - as more details and examples. + The article about mentioned above about + constructors and destructors has more details and examples.

Pseudo properties

diff --git a/include/class.h b/include/class.h index 008ff8a..d867699 100644 --- a/include/class.h +++ b/include/class.h @@ -156,7 +156,27 @@ public: void property(const char *name, const std::string &value, int flags = Public) { ClassBase::property(name, value, flags); } void property(const char *name, bool value, int flags = Public) { ClassBase::property(name, value, flags); } void property(const char *name, double value, int flags = Public) { ClassBase::property(name, value, flags); } - + + /** + * Properties as methods + * + * This is a smarter way for adding properties to a class. You can define + * a property and a method that gets called every time the property is + * set or unset. + * + * If you do not set a setter method, your property will be read-only. + * + * @param name Name of the property + * @param getter The getter method + * @param setter The setter method + */ + void property(const char *name, Value (T::*getter)() ) { ClassBase::property(name, static_cast(getter), nullptr); } + void property(const char *name, Value (T::*getter)() const ) { ClassBase::property(name, static_cast(getter), nullptr); } + void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + private: /** * Construct a new instance of the object diff --git a/include/classbase.h b/include/classbase.h index 1979f94..3ae8568 100644 --- a/include/classbase.h +++ b/include/classbase.h @@ -51,11 +51,18 @@ typedef void (Base::*method_callback_5)(Parameters &) const; typedef Value (Base::*method_callback_6)() const; typedef Value (Base::*method_callback_7)(Parameters &) const; +/** + * Signatures for getters and setters + */ +typedef Value (Base::*getter_callback)(); +typedef void (Base::*setter_callback)(const Php::Value &value); + /** * Forward declarations */ class Method; class Member; +class Property; /** * Class definition @@ -87,6 +94,7 @@ public: _type(that._type), _methods(that._methods), _members(that._members), + _properties(that._properties), _entry(nullptr) {} /** @@ -98,6 +106,7 @@ public: _type(that._type), _methods(std::move(that._methods)), _members(std::move(that._members)), + _properties(std::move(that._properties)), _entry(that._entry) { // other entry are invalid now (not that it is used..., class objects are @@ -269,6 +278,14 @@ protected: void property(const char *name, const char *value, int flags = Php::Public); void property(const char *name, double value, int flags = Php::Public); + /** + * Set property with callbacks + * @param name Name of the property + * @param getter Getter method + * @param setter Setter method + */ + void property(const char *name, const getter_callback &getter, const setter_callback &setter); + private: /** * Retrieve an array of zend_function_entry objects that hold the @@ -280,6 +297,14 @@ private: */ const struct _zend_function_entry *entries(); + /** + * Helper method to turn a property into a zval + * @param value + * @param type + * @return Value + */ + static struct _zval_struct *toZval(Value &&value, int type); + /** * Static member functions to create or clone objects based on this class * @param entry Pointer to class information @@ -503,6 +528,12 @@ private: */ std::list> _members; + /** + * Map of dynamically accessible properties + * @var std::map + */ + std::map> _properties; + /** * Base object has access to the members * This is needed by the Base::store() method diff --git a/src/classbase.cpp b/src/classbase.cpp index 2fae658..1773890 100644 --- a/src/classbase.cpp +++ b/src/classbase.cpp @@ -646,22 +646,7 @@ zval *ClassBase::readDimension(zval *object, zval *offset, int type) try { // ArrayAccess is implemented, call function - Value value = arrayaccess->offsetGet(offset); - - // because we do not want the value object to destruct the zval when - // it falls out of scope, we detach the zval from it, if this is a regular - // read operation we can do this right away - if (type == 0) return value.detach(); - - // this is a more complicated read operation, the scripts wants to get - // deeper access to the returned value. This, however, is only possible - // if the value has more than once reference (if it has a refcount of one, - // the value object that we have here is the only instance of the zval, - // and it is simply impossible to return a reference or so - if (value.refcount() <= 1) return value.detach(); - - // we're dealing with an editable zval, return a reference variable - return Value(value.detach(), true).detach(); + return toZval(arrayaccess->offsetGet(offset), type); } catch (Exception &exception) { @@ -813,6 +798,30 @@ void ClassBase::unsetDimension(zval *object, zval *member) } } +/** + * Helper method to turn a property into a zval + * @param value + * @param type + * @return Value + */ +zval *ClassBase::toZval(Value &&value, int type) +{ + // because we do not want the value object to destruct the zval when + // it falls out of scope, we detach the zval from it, if this is a regular + // read operation we can do this right away + if (type == 0) return value.detach(); + + // this is a more complicated read operation, the scripts wants to get + // deeper access to the returned value. This, however, is only possible + // if the value has more than once reference (if it has a refcount of one, + // the value object that we have here is the only instance of the zval, + // and it is simply impossible to return a reference or so + if (value.refcount() <= 1) return value.detach(); + + // we're dealing with an editable zval, return a reference variable + return Value(value.detach(), true).detach(); +} + /** * Function that is called when a property is read * @param object @@ -857,23 +866,23 @@ zval *ClassBase::readProperty(zval *object, zval *name, int type, const struct _ // the exception we know if the object was implemented by the user or not try { - // retrieve value - 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 - // read operation we can do this right away - if (type == 0) return value.detach(); + // convert name to a Value object + Value key(name); - // this is a more complicated read operation, the scripts wants to get - // deeper access to the returned value. This, however, is only possible - // if the value has more than once reference (if it has a refcount of one, - // the value object that we have here is the only instance of the zval, - // and it is simply impossible to return a reference or so - if (value.refcount() <= 1) return value.detach(); + // is it a property with a callback? + auto iter = meta->_properties.find(key); - // we're dealing with an editable zval, return a reference variable - return Value(value.detach(), true).detach(); + // was it found? + if (iter == meta->_properties.end()) + { + // retrieve value from the __get method + return toZval(meta->callGet(base, key), type); + } + else + { + // get the value + return toZval(iter->second->get(base), type); + } } catch (const NotImplemented &exception) { @@ -929,8 +938,26 @@ void ClassBase::writeProperty(zval *object, zval *name, zval *value, const struc // we know for sure that the user has not overridden the __set method try { - // call the default - meta->callSet(base, name, value); + // wrap the name + Value key(name); + + // check if the property has a callback + auto iter = meta->_properties.find(key); + + // is it set? + if (iter == meta->_properties.end()) + { + // use the __set method + meta->callSet(base, key, value); + } + else + { + // check if it could be set + if (iter->second->set(base, value)) return; + + // read-only property + zend_error(E_ERROR, "Unable to write to read-only property %s", (const char *)key); + } } catch (const NotImplemented &exception) { @@ -989,15 +1016,21 @@ int ClassBase::hasProperty(zval *object, zval *name, int has_set_exists, const s // we need the C++ class meta-information object ClassBase *meta = cpp_class(entry); + + // convert the name to a Value object + Value key(name); + + // check if this is a callback property + if (meta->_properties.find(key) != meta->_properties.end()) return true; // call the C++ object - if (!meta->callIsset(base, name)) return false; + if (!meta->callIsset(base, key)) 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 = meta->callGet(base, name); + Value value = meta->callGet(base, key); // should we check on NULL? switch (has_set_exists) { @@ -1052,9 +1085,18 @@ void ClassBase::unsetProperty(zval *object, zval *member, const struct _zend_lit // we need the C++ class meta-information object ClassBase *meta = cpp_class(entry); - - // call the __unset method - meta->callUnset(cpp_object(object), member); + + // property name + Value name(member); + + // is this a callback property? + auto iter = meta->_properties.find(name); + + // if the property does not exist, we forward to the __unset + if (iter == meta->_properties.end()) meta->callUnset(cpp_object(object), member); + + // callback properties cannot be unset + zend_error(E_ERROR, "Property %s can not be unset", (const char *)name); } catch (const NotImplemented &exception) { @@ -1640,6 +1682,18 @@ void ClassBase::property(const char *name, double value, int flags) _members.push_back(std::make_shared(name, value, flags)); } +/** + * Set property with callbacks + * @param name Name of the property + * @param getter Getter method + * @param setter Setter method + */ +void ClassBase::property(const char *name, const getter_callback &getter, const setter_callback &setter) +{ + // add property + _properties[name] = std::make_shared(getter,setter); +} + /** * End namespace */ diff --git a/src/includes.h b/src/includes.h index b2cb353..721b29e 100644 --- a/src/includes.h +++ b/src/includes.h @@ -90,6 +90,7 @@ #include "arithmetic.h" #include "origexception.h" #include "notimplemented.h" +#include "property.h" #ifndef ZVAL_COPY_VALUE #define ZVAL_COPY_VALUE(z, v) \ diff --git a/src/property.h b/src/property.h new file mode 100644 index 0000000..589b58c --- /dev/null +++ b/src/property.h @@ -0,0 +1,82 @@ +/** + * Property.h + * + * Internal class for properties that are defined with a getter and setter method + * + * @author Emiel Bruijntjes + * @copyright 2014 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class Property +{ +private: + /** + * The getter + * @var getter_callback + */ + getter_callback _getter = nullptr; + + /** + * The setter + * @var setter_callback + */ + setter_callback _setter = nullptr; + +public: + /** + * Constructor + * @param getter + * @param setter + */ + Property(const getter_callback &getter, const setter_callback &setter) : + _getter(getter), _setter(setter) {} + + /** + * Copy constructor + * @param that + */ + Property(const Property &that) : + _getter(that._getter), _setter(that._setter) {} + + /** + * Destructor + */ + virtual ~Property(); + + /** + * Get the property + * @param base Object to call it on + * @return Value + */ + Value get(Base *base) + { + return (base->*_getter)(); + } + + /** + * Set the property + * @param base Object to call it on + * @param value New value + * @return bool + */ + bool set(Base *base, const Value &value) + { + if (!_setter) return false; + (base->*_setter)(value); + return false; + } +}; + +/** + * End of namespace + */ +} + -- cgit v1.2.3 From a525d95b26fa6e4afdca803a8f77579cd26cedc6 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 13:54:39 +0100 Subject: added extra property methods to allow both const and non-const setters and getters, updated documentation --- documentation/constructors-and-destructors.html | 8 +- documentation/properties.html | 272 +++++++++++++++++++++++- include/class.h | 12 +- include/classbase.h | 13 +- src/classbase.cpp | 61 +++++- src/property.h | 100 ++++++++- 6 files changed, 431 insertions(+), 35 deletions(-) diff --git a/documentation/constructors-and-destructors.html b/documentation/constructors-and-destructors.html index d7cc61b..399367a 100644 --- a/documentation/constructors-and-destructors.html +++ b/documentation/constructors-and-destructors.html @@ -135,10 +135,10 @@ extern "C" { // description of the class so that PHP knows which methods are accessible Php::Class<Counter> counter("Counter"); - counter.method("__construct", &Counter::__construct); - counter.method("increment", &Counter::increment); - counter.method("decrement", &Counter::decrement); - counter.method("value", &Counter::value); + counter.method("__construct", &Counter::__construct); + counter.method("increment", &Counter::increment); + counter.method("decrement", &Counter::decrement); + counter.method("value", &Counter::value); // add the class to the extension myExtension.add(std::move(counter)); diff --git a/documentation/properties.html b/documentation/properties.html index bdbdfa7..30368cc 100644 --- a/documentation/properties.html +++ b/documentation/properties.html @@ -2,22 +2,274 @@

When you define a class completely in PHP, you can add properties (member variables) to it. When you add member variables to a native C++ class however, - you better use regular C++ member variables for that, instead of PHP variables. + you better use regular native member variables for that, instead of PHP variables. Native variables have an immensely better performance than PHP variables, and it would be insane to store integers or strings in Php::Value objects if you can store them in int's and std::string objects as well.

+

Normal member variables

- To access these member variables you could create getX() and setX() - methods, or alternatively implement __get() and __set() methods if you - want to make your native member variables look like public or protected - properties. + It is difficult to imagine that someone in the world would like to create + a native class, with regular non-typed public PHP properties on it. However, + if you insist, you can use the PHP-CPP library for this. Let's take an example + class in PHP, and see what it will look like in C++.

- I can not imagine that there is anyone in the world who would like to create - a native class, with regular public PHP properties on it. But still, in this - article we explain how you can do that. +


+<?php
+/**
+ *  PHP example class
+ */
+class Example
+{
+    /**
+     *  Define a public property
+     */
+    public $property1;
+
+    /**
+     *  Constructor
+     */
+    public function __construct()
+    {
+        // initialize the property
+        $this->property1 = "xyz";
+    }
+    
+    /**
+     *  Example method
+     */
+    public function method()
+    {
+        // do something with the public property (like changing it)
+        $this->property = "abc";
+    }
+}
+
+// create an instance
+$example = new Example();
+
+// overwrite the public property
+$example->property1 = "new value";
+
+?>
+

- ... this article is not finished yet -

\ No newline at end of file + The above example creates a class with one public property. This property + can be accessed by the Example class, but because it is public, also by + everyone else, as is shown in the example. If you like such classes, you can + write something similar with PHP-CPP. +

+

+


+#include <phpcpp.h>
+
+/**
+ *  C++ Example class
+ */
+class Example : public Php::Base
+{
+public:
+    /**
+     *  c++ constructor
+     */
+    Example() {}
+    
+    /**
+     *  c++ destructor
+     */
+    virtual ~Example() {}
+    
+    /**
+     *  php "constructor"
+     *  @param  params
+     */
+    void __construct()
+    {
+        // get self reference as Php::Value object
+        Php::Value self(this);
+        
+        // initialize a public property
+        self["property1"] = "xyz";
+    }
+    
+    /**
+     *  Example method
+     */
+    void method()
+    {
+        // get self reference as Php::Value object
+        Php::Value self(this);
+
+        // overwrite the property
+        self["property1"] = "abc";
+    }
+};
+
+/**
+ *  Switch to C context so that the get_module() function can be
+ *  called by C programs (which the Zend engine is)
+ */
+extern "C" {
+    /**
+     *  Startup function for the extension
+     *  @return void*
+     */
+    PHPCPP_EXPORT void *get_module() {
+        // create static extension object
+        static Php::Extension myExtension("my_extension", "1.0");
+        
+        // description of the class so that PHP knows which methods are accessible
+        Php::Class<Example> example("Example");
+        
+        // register the methods
+        example.method("__construct", &Example::__construct);
+        example.method("method", &Example::method);
+        
+        // the Example class has one public property
+        example.property("property1", "xyz", Php::Public);
+        
+        // add the class to the extension
+        myExtension.add(std::move(example));
+        
+        // return the extension
+        return myExtension;
+    }
+}
+
+

+

+ The example code shows how you need to initialize the properties inside + the get_module() function. +

+

+ Instead of public properties, you can also define private or protected + properties, but even that is probably not what you want, as storing + data in native C++ variables is much faster. +

+

Smart properties

+

+ With the magic methods __get() and __set() you + can make more advanced properties that are directly mapped to your C++ + code, and that allows you to perform additional checks when a property + is overwritten, so that an object always remains in a valid state. +

+

+ On top of that, with the PHP-CPP library you can also assign getter and + setter methods to properties. Every time a property is accessed, your getter + or setter method will automatically be accessed. +

+

+


+#include <phpcpp.h>
+
+/**
+ *  C++ Example class
+ */
+class Example : public Php::Base
+{
+private:
+    /**
+     *  Example property 1
+     *  @var    int
+     */
+    int _value = 0;
+    
+    
+public:
+    /**
+     *  c++ constructor
+     */
+    Example() {}
+    
+    /**
+     *  c++ destructor
+     */
+    virtual ~Example() {}
+    
+    /**
+     *  Method to get access to the property
+     *  @return Php::Value
+     */
+    Php::Value getValue() const
+    {
+        return _value;
+    }
+    
+    /**
+     *  Method to overwrite the property
+     *  @param  value
+     */
+    void setValue(const Php::Value &value)
+    {
+        // overwrite property
+        _value = value;
+        
+        // sanity check: the value should never exceed 100
+        if (_value > 100) _value = 100;
+    }
+        
+    /**
+     *  Method to retrieve the double property value
+     *  @return Php::Value
+     */
+    Php::Value getDouble() const
+    {
+        return _value * 2;
+    }
+};
+
+/**
+ *  Switch to C context so that the get_module() function can be
+ *  called by C programs (which the Zend engine is)
+ */
+extern "C" {
+    /**
+     *  Startup function for the extension
+     *  @return void*
+     */
+    PHPCPP_EXPORT void *get_module() {
+        // create static extension object
+        static Php::Extension myExtension("my_extension", "1.0");
+        
+        // description of the class so that PHP knows which methods are accessible
+        Php::Class<Example> example("Example");
+        
+        // register the "value" property, with the methods to get and set it
+        example.property("value", &Example::getValue, &Example::setValue);
+        
+        // register a read-only "double" property, with a method to get it
+        example.property("double", &Example::getDouble);
+        
+        // add the class to the extension
+        myExtension.add(std::move(example));
+        
+        // return the extension
+        return myExtension;
+    }
+}
+
+

+

+ The following PHP script uses this. It created an example object, sets the + value property to 500 (which is not allowed, values higher than 100 are + rounded to 100), and then it reads out the double value. +

+

+


+<?php
+// create object
+$object = new Example();
+
+// set the value
+$object->value = 500;
+
+// show the double value
+echo($object->double."\n");
+
+// update the double value
+// (this will trigger an error, this is a read-only property)
+$object->double = 300;
+?>
+
diff --git a/include/class.h b/include/class.h index d867699..23c39e1 100644 --- a/include/class.h +++ b/include/class.h @@ -170,12 +170,12 @@ public: * @param getter The getter method * @param setter The setter method */ - void property(const char *name, Value (T::*getter)() ) { ClassBase::property(name, static_cast(getter), nullptr); } - void property(const char *name, Value (T::*getter)() const ) { ClassBase::property(name, static_cast(getter), nullptr); } - void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } - void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } - void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } - void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + void property(const char *name, Value (T::*getter)() ) { ClassBase::property(name, static_cast(getter)); } + void property(const char *name, Value (T::*getter)() const ) { ClassBase::property(name, static_cast(getter)); } + void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) ) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + void property(const char *name, Value (T::*getter)() , void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } + void property(const char *name, Value (T::*getter)() const, void (T::*setter)(const Value &value) const) { ClassBase::property(name, static_cast(getter), static_cast(setter)); } private: /** diff --git a/include/classbase.h b/include/classbase.h index 3ae8568..40f3fda 100644 --- a/include/classbase.h +++ b/include/classbase.h @@ -54,8 +54,10 @@ typedef Value (Base::*method_callback_7)(Parameters &) const; /** * Signatures for getters and setters */ -typedef Value (Base::*getter_callback)(); -typedef void (Base::*setter_callback)(const Php::Value &value); +typedef Value (Base::*getter_callback_0)(); +typedef Value (Base::*getter_callback_1)() const; +typedef void (Base::*setter_callback_0)(const Php::Value &value); +typedef void (Base::*setter_callback_1)(const Php::Value &value) const; /** * Forward declarations @@ -284,7 +286,12 @@ protected: * @param getter Getter method * @param setter Setter method */ - void property(const char *name, const getter_callback &getter, const setter_callback &setter); + void property(const char *name, const getter_callback_0 &getter); + void property(const char *name, const getter_callback_1 &getter); + void property(const char *name, const getter_callback_0 &getter, const setter_callback_0 &setter); + void property(const char *name, const getter_callback_1 &getter, const setter_callback_0 &setter); + void property(const char *name, const getter_callback_0 &getter, const setter_callback_1 &setter); + void property(const char *name, const getter_callback_1 &getter, const setter_callback_1 &setter); private: /** diff --git a/src/classbase.cpp b/src/classbase.cpp index 1773890..09850a7 100644 --- a/src/classbase.cpp +++ b/src/classbase.cpp @@ -1682,18 +1682,77 @@ void ClassBase::property(const char *name, double value, int flags) _members.push_back(std::make_shared(name, value, flags)); } +/** + * Set property with callbacks + * @param name Name of the property + * @param getter Getter method + */ +void ClassBase::property(const char *name, const getter_callback_0 &getter) +{ + // add property + _properties[name] = std::make_shared(getter); +} + +/** + * Set property with callbacks + * @param name Name of the property + * @param getter Getter method + */ +void ClassBase::property(const char *name, const getter_callback_1 &getter) +{ + // add property + _properties[name] = std::make_shared(getter); +} + +/** + * Set property with callbacks + * @param name Name of the property + * @param getter Getter method + * @param setter Setter method + */ +void ClassBase::property(const char *name, const getter_callback_0 &getter, const setter_callback_0 &setter) +{ + // add property + _properties[name] = std::make_shared(getter,setter); +} + +/** + * Set property with callbacks + * @param name Name of the property + * @param getter Getter method + * @param setter Setter method + */ +void ClassBase::property(const char *name, const getter_callback_1 &getter, const setter_callback_0 &setter) +{ + // add property + _properties[name] = std::make_shared(getter,setter); +} + /** * Set property with callbacks * @param name Name of the property * @param getter Getter method * @param setter Setter method */ -void ClassBase::property(const char *name, const getter_callback &getter, const setter_callback &setter) +void ClassBase::property(const char *name, const getter_callback_0 &getter, const setter_callback_1 &setter) { // add property _properties[name] = std::make_shared(getter,setter); } +/** + * Set property with callbacks + * @param name Name of the property + * @param getter Getter method + * @param setter Setter method + */ +void ClassBase::property(const char *name, const getter_callback_1 &getter, const setter_callback_1 &setter) +{ + // add property + _properties[name] = std::make_shared(getter,setter); +} + + /** * End namespace */ diff --git a/src/property.h b/src/property.h index 589b58c..f0fd46f 100644 --- a/src/property.h +++ b/src/property.h @@ -22,13 +22,31 @@ private: * The getter * @var getter_callback */ - getter_callback _getter = nullptr; + union { + getter_callback_0 g0; + getter_callback_1 g1; + } _getter; /** * The setter * @var setter_callback */ - setter_callback _setter = nullptr; + union { + setter_callback_0 s0; + setter_callback_1 s1; + } _setter; + + /** + * Type of getter + * @var char + */ + int _gtype = 0; + + /** + * Type of setter + * @var char + */ + int _stype = 100; public: /** @@ -36,20 +54,77 @@ public: * @param getter * @param setter */ - Property(const getter_callback &getter, const setter_callback &setter) : - _getter(getter), _setter(setter) {} - + Property(const getter_callback_0 &getter) : _gtype(0) + { + _getter.g0 = getter; + } + + /** + * Constructor + * @param getter + * @param setter + */ + Property(const getter_callback_1 &getter) : _gtype(1) + { + _getter.g1 = getter; + } + + + /** + * Constructor + * @param getter + * @param setter + */ + Property(const getter_callback_0 &getter, const setter_callback_0 &setter) : _gtype(0), _stype(0) + { + _getter.g0 = getter; + _setter.s0 = setter; + } + + /** + * Constructor + * @param getter + * @param setter + */ + Property(const getter_callback_1 &getter, const setter_callback_0 &setter) : _gtype(1), _stype(0) + { + _getter.g1 = getter; + _setter.s0 = setter; + } + + /** + * Constructor + * @param getter + * @param setter + */ + Property(const getter_callback_0 &getter, const setter_callback_1 &setter) : _gtype(0), _stype(1) + { + _getter.g0 = getter; + _setter.s1 = setter; + } + + /** + * Constructor + * @param getter + * @param setter + */ + Property(const getter_callback_1 &getter, const setter_callback_1 &setter) : _gtype(1), _stype(1) + { + _getter.g1 = getter; + _setter.s1 = setter; + } + /** * Copy constructor * @param that */ Property(const Property &that) : - _getter(that._getter), _setter(that._setter) {} + _getter(that._getter), _setter(that._setter), _gtype(that._gtype), _stype(that._stype) {} /** * Destructor */ - virtual ~Property(); + virtual ~Property() {} /** * Get the property @@ -58,7 +133,8 @@ public: */ Value get(Base *base) { - return (base->*_getter)(); + if (_gtype == 0) return (base->*_getter.g0)(); + else return (base->*_getter.g1)(); } /** @@ -69,9 +145,11 @@ public: */ bool set(Base *base, const Value &value) { - if (!_setter) return false; - (base->*_setter)(value); - return false; + switch (_stype) { + case 0: (base->*_setter.s0)(value); return true; + case 1: (base->*_setter.s1)(value); return true; + default: return false; + } } }; -- cgit v1.2.3 From bb0a1d1a861a71c91de417a4c53ceefd9d5afcb3 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 14:01:18 +0100 Subject: documentation typos --- documentation/properties.html | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/documentation/properties.html b/documentation/properties.html index 30368cc..50b0df3 100644 --- a/documentation/properties.html +++ b/documentation/properties.html @@ -12,11 +12,11 @@ It is difficult to imagine that someone in the world would like to create a native class, with regular non-typed public PHP properties on it. However, if you insist, you can use the PHP-CPP library for this. Let's take an example - class in PHP, and see what it will look like in C++. + class in PHP, and see what it would look like in C++.


-<?php
+<?php
 /**
  *  PHP example class
  */
@@ -57,7 +57,7 @@ $example->property1 = "new value";
 

The above example creates a class with one public property. This property - can be accessed by the Example class, but because it is public, also by + can be accessed by the Example class, and because it is public also by everyone else, as is shown in the example. If you like such classes, you can write something similar with PHP-CPP.

@@ -140,7 +140,7 @@ extern "C" {

- The example code shows how you need to initialize the properties inside + The example code shows how you initialize the properties inside the get_module() function.

@@ -151,14 +151,14 @@ extern "C" {

Smart properties

With the magic methods __get() and __set() you - can make more advanced properties that are directly mapped to your C++ - code, and that allows you to perform additional checks when a property + can make more advanced properties that are directly mapped to C++ + variables, and that allow you to perform additional checks when a property is overwritten, so that an object always remains in a valid state.

On top of that, with the PHP-CPP library you can also assign getter and setter methods to properties. Every time a property is accessed, your getter - or setter method will automatically be accessed. + or setter method will automatically be called.


@@ -171,7 +171,7 @@ class Example : public Php::Base
 {
 private:
     /**
-     *  Example property 1
+     *  Example property
      *  @var    int
      */
     int _value = 0;
@@ -258,7 +258,7 @@ extern "C" {
 


-<?php
+<?php
 // create object
 $object = new Example();
 
-- 
cgit v1.2.3


From 6c64773dc5edd4b56eabcfa76b66b53ab76ecdf6 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes 
Date: Fri, 14 Mar 2014 14:03:54 +0100
Subject: documentation typos

---
 documentation/properties.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/documentation/properties.html b/documentation/properties.html
index 50b0df3..f7f96c6 100644
--- a/documentation/properties.html
+++ b/documentation/properties.html
@@ -158,7 +158,7 @@ extern "C" {
 

On top of that, with the PHP-CPP library you can also assign getter and setter methods to properties. Every time a property is accessed, your getter - or setter method will automatically be called. + or setter method is automatically called.


-- 
cgit v1.2.3


From ab98c70a98e860f4cce0332d7164077cffa51b30 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes 
Date: Fri, 14 Mar 2014 15:02:40 +0100
Subject: Value::numericValue() now returns a int64_t, and no longer a long

---
 include/callstatic.h | 64 ---------------------------------------------------
 include/value.h      | 12 +++++++---
 src/arithmetic.h     | 14 +++++------
 src/classbase.cpp    |  6 ++---
 src/includes.h       |  2 +-
 src/longmember.h     | 65 ----------------------------------------------------
 src/numericmember.h  | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/value.cpp        |  4 ++--
 8 files changed, 87 insertions(+), 145 deletions(-)
 delete mode 100644 include/callstatic.h
 delete mode 100644 src/longmember.h
 create mode 100644 src/numericmember.h

diff --git a/include/callstatic.h b/include/callstatic.h
deleted file mode 100644
index 3979ef3..0000000
--- a/include/callstatic.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- *  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 
-class HasCallStatic
-{
-    typedef char one;
-    typedef long two;
-
-    template  static one test( decltype(&C::__callStatic) ) ;
-    template  static two test(...);
-
-public:
-    static const bool value = sizeof(test(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 std::enable_if::value, Value >::type
-callStatic(const char *name, Parameters ¶ms)
-{
-    // 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 std::enable_if::value >::type
-callStatic(const char *name, Parameters ¶ms)
-{
-    std::cout << "has NO call static" << std::endl;
-    
-    return nullptr;
-}
-
-/**
- *  End namespace
- */
-}
-
diff --git a/include/value.h b/include/value.h
index 9c48f1f..fce8750 100644
--- a/include/value.h
+++ b/include/value.h
@@ -381,9 +381,15 @@ public:
     
     /**
      *  Retrieve the value as number
-     *  @return long
-     */
-    long numericValue() const;
+     *
+     *  We force this to be a int64_t because we assume that most
+     *  servers run 64 bits nowadays, and because we use int32_t, int64_t
+     *  almost everywhere, instead of 'long' and on OSX neither of
+     *  these intxx_t types is defined as 'long'...
+     *
+     *  @return int64_t
+     */
+    int64_t numericValue() const;
     
     /**
      *  Retrieve the value as boolean
diff --git a/src/arithmetic.h b/src/arithmetic.h
index ae690bc..21343d1 100644
--- a/src/arithmetic.h
+++ b/src/arithmetic.h
@@ -55,7 +55,7 @@ public:
         if (_value->isFloat()) return Value(F()(_value->floatValue(), value));
         
         // apply to natural numbers
-        return Value(F()(_value->numericValue(), value));
+        return Value(F()(_value->numericValue(), value));
     }
     
     /**
@@ -69,7 +69,7 @@ public:
         if (_value->isFloat()) return Value(F()(_value->floatValue(), value));
         
         // apply to natural numbers
-        return Value(F()(_value->numericValue(), value));
+        return Value(F()(_value->numericValue(), value));
     }
 
     /**
@@ -83,7 +83,7 @@ public:
         if (_value->isFloat()) return Value(F()(_value->floatValue(), value));
         
         // apply to natural numbers
-        return Value(F()(_value->numericValue(), value));
+        return Value(F()(_value->numericValue(), value));
     }
         
     /**
@@ -97,7 +97,7 @@ public:
         if (_value->isFloat()) return Value(F()(_value->floatValue(), value?1:0));
         
         // apply to natural numbers
-        return Value(F()(_value->numericValue(), value?1:0));
+        return Value(F()(_value->numericValue(), value?1:0));
     }
     
     /**
@@ -114,7 +114,7 @@ public:
         if (_value->isFloat()) return Value(F()(_value->floatValue(), v));
         
         // apply to natural numbers
-        return Value(F()(_value->numericValue(), v));
+        return Value(F()(_value->numericValue(), v));
     }
     
     /**
@@ -174,7 +174,7 @@ public:
         if (_value->isFloat()) return _value->operator=(F()(_value->floatValue(), value));
         
         // do a numeric operation
-        return _value->operator=(F()(_value->numericValue(), value));
+        return _value->operator=(F()(_value->numericValue(), value));
     }
     
     /**
@@ -188,7 +188,7 @@ public:
         if (_value->isFloat()) return _value->operator=(F()(_value->floatValue(), value));
         
         // do a numeric operation
-        return _value->operator=(F()(_value->numericValue(), value));
+        return _value->operator=(F()(_value->numericValue(), value));
     }
 
     /**
diff --git a/src/classbase.cpp b/src/classbase.cpp
index 09850a7..ea24cfd 100644
--- a/src/classbase.cpp
+++ b/src/classbase.cpp
@@ -1595,7 +1595,7 @@ void ClassBase::property(const char *name, std::nullptr_t value, int flags)
 void ClassBase::property(const char *name, int16_t value, int flags)
 {
     // add property
-    _members.push_back(std::make_shared(name, value, flags));
+    _members.push_back(std::make_shared(name, value, flags));
 }
 
 /**
@@ -1607,7 +1607,7 @@ void ClassBase::property(const char *name, int16_t value, int flags)
 void ClassBase::property(const char *name, int32_t value, int flags)
 {
     // add property
-    _members.push_back(std::make_shared(name, value, flags));
+    _members.push_back(std::make_shared(name, value, flags));
 }
 
 /**
@@ -1619,7 +1619,7 @@ void ClassBase::property(const char *name, int32_t value, int flags)
 void ClassBase::property(const char *name, int64_t value, int flags)
 {
     // add property
-    _members.push_back(std::make_shared(name, value, flags));
+    _members.push_back(std::make_shared(name, value, flags));
 }
 
 /**
diff --git a/src/includes.h b/src/includes.h
index 721b29e..0c00df3 100644
--- a/src/includes.h
+++ b/src/includes.h
@@ -83,7 +83,7 @@
 #include "method.h"
 #include "member.h"
 #include "nullmember.h"
-#include "longmember.h"
+#include "numericmember.h"
 #include "boolmember.h"
 #include "stringmember.h"
 #include "floatmember.h"
diff --git a/src/longmember.h b/src/longmember.h
deleted file mode 100644
index 09d3d8a..0000000
--- a/src/longmember.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- *  LongMember.h
- *
- *  Implementation for a property that is initially set to a long value
- *
- *  @author Emiel Bruijntjes 
- *  @copyright 2013, 2014 Copernica BV
- */
-
-/**
- *  Set up namespace
- */
-namespace Php {
-
-/**
- *  Class definition
- */
-class LongMember : public Member
-{
-private:
-    /**
-     *  The value
-     *  @var long
-     */
-    long _value;
-    
-public:
-    /**
-     *  Constructor
-     *  @param  name
-     *  @param  value
-     *  @param  flags
-     */
-    LongMember(const char *name, long value, int flags) : Member(name, flags), _value(value) {}
-
-    /**
-     *  Destructor
-     */
-    virtual ~LongMember() {}
-
-    /**
-     *  Declare class constant
-     *  @param  entry       Class entry
-     */
-    virtual void constant(struct _zend_class_entry *entry) override
-    {
-        zend_declare_class_constant_long(entry, _name.c_str(), _name.size(), _value);
-    }
-
-    /**
-     *  Virtual method to declare the property
-     *  @param  entry       Class entry
-     */
-    virtual void declare(struct _zend_class_entry *entry) override
-    {
-        // char* cast is necessary for php 5.3
-        zend_declare_property_long(entry, (char *)_name.c_str(), _name.size(), _value, _flags);
-    }
-};
-
-/**
- *  End of namespace
- */
-}
-
diff --git a/src/numericmember.h b/src/numericmember.h
new file mode 100644
index 0000000..99889c6
--- /dev/null
+++ b/src/numericmember.h
@@ -0,0 +1,65 @@
+/**
+ *  NumericMember.h
+ *
+ *  Implementation for a property that is initially set to a numeric value
+ *
+ *  @author Emiel Bruijntjes 
+ *  @copyright 2013, 2014 Copernica BV
+ */
+
+/**
+ *  Set up namespace
+ */
+namespace Php {
+
+/**
+ *  Class definition
+ */
+class NumericMember : public Member
+{
+private:
+    /**
+     *  The value
+     *  @var long
+     */
+    long _value;
+    
+public:
+    /**
+     *  Constructor
+     *  @param  name
+     *  @param  value
+     *  @param  flags
+     */
+    NumericMember(const char *name, long value, int flags) : Member(name, flags), _value(value) {}
+
+    /**
+     *  Destructor
+     */
+    virtual ~NumericMember() {}
+
+    /**
+     *  Declare class constant
+     *  @param  entry       Class entry
+     */
+    virtual void constant(struct _zend_class_entry *entry) override
+    {
+        zend_declare_class_constant_long(entry, _name.c_str(), _name.size(), _value);
+    }
+
+    /**
+     *  Virtual method to declare the property
+     *  @param  entry       Class entry
+     */
+    virtual void declare(struct _zend_class_entry *entry) override
+    {
+        // char* cast is necessary for php 5.3
+        zend_declare_property_long(entry, (char *)_name.c_str(), _name.size(), _value, _flags);
+    }
+};
+
+/**
+ *  End of namespace
+ */
+}
+
diff --git a/src/value.cpp b/src/value.cpp
index 29b6dcd..9af2d14 100644
--- a/src/value.cpp
+++ b/src/value.cpp
@@ -74,7 +74,7 @@ Value::Value(int32_t value)
 }
 
 /**
- *  Constructor based on long value
+ *  Constructor based on int64_t value
  *  @param  value
  */
 Value::Value(int64_t value)
@@ -1415,7 +1415,7 @@ Value Value::clone(Type type) const
  *  Retrieve the value as integer
  *  @return long
  */
-long Value::numericValue() const
+int64_t Value::numericValue() const
 {
     // already a long?
     if (isNumeric()) return Z_LVAL_P(_val);
-- 
cgit v1.2.3


From 6bf3f222bbbd1adec7f85a954dc1aee146259894 Mon Sep 17 00:00:00 2001
From: Emiel Bruijntjes 
Date: Fri, 14 Mar 2014 16:58:17 +0100
Subject: fixed magic method documentation, added comment in Makefile

---
 Makefile                         |  5 ++++-
 documentation/magic-methods.html | 25 ++++++++++++++++---------
 2 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/Makefile b/Makefile
index 734590b..32fd0b0 100644
--- a/Makefile
+++ b/Makefile
@@ -80,8 +80,11 @@ COMPILER_FLAGS  =   -Wall -c -I. -I${PHP_DIR} -I${PHP_DIR}/main -I${PHP_DIR}/ext
 #   Just like the compiler, the linker can have flags too. The default flag
 #   is probably the only one you need.
 #
+#   Are you compiling on OSX? You may have to append the option "-undefined dynamic_lookup"
+#   to the linker flags
+#
 
-LINKER_FLAGS    =   -shared
+LINKER_FLAGS    =   -shared `php-config --ldflags`
 
 
 #
diff --git a/documentation/magic-methods.html b/documentation/magic-methods.html
index 9294a3e..6356d2a 100644
--- a/documentation/magic-methods.html
+++ b/documentation/magic-methods.html
@@ -35,8 +35,8 @@
 

Constructors

- We start with an exception to the rule. Normally, you do not have to register - magic methods to make them work. When you add a magic method like + Normally, magic methods do not have + to be registered to make them work. When you add a magic method like __toString() or __get() to your class, it will automatically be called when an object is casted to a string or a property is accessed. There is no need to enable the magic method explicitly in the get_module() startup @@ -67,17 +67,24 @@

Clone and destruct

The magic __clone() method is very similar to the __construct() method. It - is the first method to be called on a new object after it is copy - constructed. The __destruct() is the method that gets called right before - the object gets destructed (and the C++ destructor runs). + also is a method that is called directly after an object is constructed. + The difference is that __clone() is called after an object is copy + constructed (or cloned if you prefer the PHP idiom), while + __construct() is called right after the normal constructor.

- The __clone() and __destruct() methods are regular magic methods and you - therefore do not have to register them to make them active. The PHP-CPP - library calls them automatically if they are available. + The magic __destruct() method gets called right before the object gets + destructed (and right before the C++ destructor runs).

- In normal circumstances you probably have not need for these methods. + The __clone() and __destruct() methods are regular magic methods (unlike + __construct()) and you therefore do not have to register them to make them + active. If you add one of these two methods to your class, you will not have + to make any changes to the get_module() startup function. The PHP-CPP library + calls them automatically if they are available. +

+

+ In normal circumstances you probably have no need for these methods. The C++ copy constructor and the C++ destructor can be used too. The only difference is that the magic methods are called on objects that are in a fully initialized state, while the C++ copy constructor and C++ destructor -- cgit v1.2.3 From cda98dc0dea7144941a46ff20598f3c095d7a6d0 Mon Sep 17 00:00:00 2001 From: Emiel Bruijntjes Date: Fri, 14 Mar 2014 17:03:37 +0100 Subject: update documentation about installing on OSX --- documentation/install.html | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/documentation/install.html b/documentation/install.html index 7f7a2c3..d1d1cb0 100644 --- a/documentation/install.html +++ b/documentation/install.html @@ -5,17 +5,16 @@ system(s).

- Luckily, for most of us (those who use Linux environments), this will be - a piece of cake. If you're on a different platform however, you are left on - your own, because we (as in me, the PHP-CPP developer), only uses Linux - systems. There is however no reason why this library should not also work on - other platforms, because it only uses straight forward C++ code. Thus, if - you are on a different platform and have managed to compile the library on - it, please give us feedback so that we can update these installation - instructions and include other platforms as well. + Luckily, for most of us (those who use Linux or Apple environments), this + will be a piece of cake. If you're on a different platform however, you are + left on your own, because we (as in me, the PHP-CPP developer), only uses + Linux systems. There is however no reason why this library should not also + work on other platforms, because it only uses straight forward C++ code. + Thus, if you are on a different platform and have managed to compile the + library on it, please give us feedback so that we can update these + installation instructions and include other platforms as well.

-

Limitations

At this moment, PHP-CPP only supports single-threaded PHP installations. @@ -85,7 +84,20 @@

make

- The PHP-CPP library has now been built, and all that is left to do is + This will start the compiler and build the library. +

+ +

Compiling on OSX?

+

+ If you compile the software on OSX, you may run into linking and "unresolved + symbol" errors. In that case you will have to make a change to the Makefile. + Somewhere in this Makefile there is an option "LINKER_FLAGS". This option + should be extended, and the extra flag "-undefined dynamic_lookup" should + be added to it. +

+ +

+ After you ran 'make', and the PHP-CPP library was built, all that is left to do is install it on your system. You can use the "make install" command for it. This command should be executed as root, either by using "sudo", or by logging on as root first. @@ -94,6 +106,7 @@

sudo make install

- Congratulations! You are now the happy owner of a system with PHP-CPP installed - and nothing can stop you from building your first fast native PHP extension. + That was it! After these steps you are now the happy owner of a system with + PHP-CPP installed and nothing can stop you from building your first fast + native PHP extension.

-- cgit v1.2.3