diff options
author | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-15 01:32:22 +0100 |
---|---|---|
committer | Emiel Bruijntjes <emiel.bruijntjes@copernica.com> | 2014-03-15 01:32:22 +0100 |
commit | 3300d64c4dd61e71b62d5b55b1f16e34d6335680 (patch) | |
tree | c2bd999ba1a2a1320acba20fb99acf0defb9651e /src | |
parent | 681d1e6aa735568a492140d2307a89063e7aadb9 (diff) | |
parent | cda98dc0dea7144941a46ff20598f3c095d7a6d0 (diff) |
implemented __clone method
Diffstat (limited to 'src')
-rw-r--r-- | src/arithmetic.h | 14 | ||||
-rw-r--r-- | src/base.cpp | 35 | ||||
-rw-r--r-- | src/classbase.cpp | 242 | ||||
-rw-r--r-- | src/extension.cpp | 2 | ||||
-rw-r--r-- | src/includes.h | 4 | ||||
-rw-r--r-- | src/numericmember.h (renamed from src/longmember.h) | 10 | ||||
-rw-r--r-- | src/property.h | 160 | ||||
-rw-r--r-- | src/super.cpp | 59 | ||||
-rw-r--r-- | src/value.cpp | 66 |
9 files changed, 511 insertions, 81 deletions
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<double>()(_value->floatValue(), value)); // apply to natural numbers - return Value(F<long>()(_value->numericValue(), value)); + return Value(F<int64_t>()(_value->numericValue(), value)); } /** @@ -69,7 +69,7 @@ public: if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value)); // apply to natural numbers - return Value(F<long>()(_value->numericValue(), value)); + return Value(F<int64_t>()(_value->numericValue(), value)); } /** @@ -83,7 +83,7 @@ public: if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value)); // apply to natural numbers - return Value(F<long>()(_value->numericValue(), value)); + return Value(F<int64_t>()(_value->numericValue(), value)); } /** @@ -97,7 +97,7 @@ public: if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), value?1:0)); // apply to natural numbers - return Value(F<long>()(_value->numericValue(), value?1:0)); + return Value(F<int64_t>()(_value->numericValue(), value?1:0)); } /** @@ -114,7 +114,7 @@ public: if (_value->isFloat()) return Value(F<double>()(_value->floatValue(), v)); // apply to natural numbers - return Value(F<long>()(_value->numericValue(), v)); + return Value(F<int64_t>()(_value->numericValue(), v)); } /** @@ -174,7 +174,7 @@ public: if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value)); // do a numeric operation - return _value->operator=(F<long>()(_value->numericValue(), value)); + return _value->operator=(F<int64_t>()(_value->numericValue(), value)); } /** @@ -188,7 +188,7 @@ public: if (_value->isFloat()) return _value->operator=(F<double>()(_value->floatValue(), value)); // do a numeric operation - return _value->operator=(F<long>()(_value->numericValue(), value)); + return _value->operator=(F<int64_t>()(_value->numericValue(), value)); } /** diff --git a/src/base.cpp b/src/base.cpp index 2c46147..f677bcf 100644 --- a/src/base.cpp +++ b/src/base.cpp @@ -13,29 +13,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 * @return MixedObject @@ -71,13 +48,23 @@ 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 * * The default implementation does nothing, and the script will fall back diff --git a/src/classbase.cpp b/src/classbase.cpp index ccfb9da..0de62dd 100644 --- a/src/classbase.cpp +++ b/src/classbase.cpp @@ -649,22 +649,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) { @@ -817,6 +802,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 * @param name @@ -860,23 +869,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) { @@ -932,8 +941,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) { @@ -992,15 +1019,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) { @@ -1055,9 +1088,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) { @@ -1080,6 +1122,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 * @param entry Pointer to the class information @@ -1509,7 +1598,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<LongMember>(name, value, flags)); + _members.push_back(std::make_shared<NumericMember>(name, value, flags)); } /** @@ -1521,7 +1610,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<LongMember>(name, value, flags)); + _members.push_back(std::make_shared<NumericMember>(name, value, flags)); } /** @@ -1533,7 +1622,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<LongMember>(name, value, flags)); + _members.push_back(std::make_shared<NumericMember>(name, value, flags)); } /** @@ -1597,6 +1686,77 @@ void ClassBase::property(const char *name, double value, int 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<Property>(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<Property>(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<Property>(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<Property>(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_0 &getter, const setter_callback_1 &setter) +{ + // add property + _properties[name] = std::make_shared<Property>(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<Property>(getter,setter); +} + + +/** * End namespace */ } 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..0c00df3 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" @@ -82,13 +83,14 @@ #include "method.h" #include "member.h" #include "nullmember.h" -#include "longmember.h" +#include "numericmember.h" #include "boolmember.h" #include "stringmember.h" #include "floatmember.h" #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/longmember.h b/src/numericmember.h index 09d3d8a..99889c6 100644 --- a/src/longmember.h +++ b/src/numericmember.h @@ -1,7 +1,7 @@ /** - * LongMember.h + * NumericMember.h * - * Implementation for a property that is initially set to a long value + * Implementation for a property that is initially set to a numeric value * * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> * @copyright 2013, 2014 Copernica BV @@ -15,7 +15,7 @@ namespace Php { /** * Class definition */ -class LongMember : public Member +class NumericMember : public Member { private: /** @@ -31,12 +31,12 @@ public: * @param value * @param flags */ - LongMember(const char *name, long value, int flags) : Member(name, flags), _value(value) {} + NumericMember(const char *name, long value, int flags) : Member(name, flags), _value(value) {} /** * Destructor */ - virtual ~LongMember() {} + virtual ~NumericMember() {} /** * Declare class constant diff --git a/src/property.h b/src/property.h new file mode 100644 index 0000000..f0fd46f --- /dev/null +++ b/src/property.h @@ -0,0 +1,160 @@ +/** + * Property.h + * + * Internal class for properties that are defined with a getter and setter method + * + * @author Emiel Bruijntjes <emiel.bruijntjes@copernica.com> + * @copyright 2014 Copernica BV + */ + +/** + * Set up namespace + */ +namespace Php { + +/** + * Class definition + */ +class Property +{ +private: + /** + * The getter + * @var getter_callback + */ + union { + getter_callback_0 g0; + getter_callback_1 g1; + } _getter; + + /** + * The setter + * @var setter_callback + */ + 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: + /** + * Constructor + * @param getter + * @param 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), _gtype(that._gtype), _stype(that._stype) {} + + /** + * Destructor + */ + virtual ~Property() {} + + /** + * Get the property + * @param base Object to call it on + * @return Value + */ + Value get(Base *base) + { + if (_gtype == 0) return (base->*_getter.g0)(); + else return (base->*_getter.g1)(); + } + + /** + * Set the property + * @param base Object to call it on + * @param value New value + * @return bool + */ + bool set(Base *base, const Value &value) + { + switch (_stype) { + case 0: (base->*_setter.s0)(value); return true; + case 1: (base->*_setter.s1)(value); return true; + default: return false; + } + } +}; + +/** + * End of 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 <emiel.bruijntjes@copernica.com> + */ +#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 + */ +} + diff --git a/src/value.cpp b/src/value.cpp index a78c2d4..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) @@ -170,6 +170,21 @@ Value::Value(struct _zval_struct *val, bool ref) } /** + * 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; @@ -316,6 +334,50 @@ zval *Value::detach() } /** + * 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 */ @@ -1353,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); |