// // Copyright (c) ZeroC, Inc. All rights reserved. // #include #include #include #include #include #include #include #include #include using namespace std; using namespace IcePHP; using namespace IceUtil; using namespace IceUtilInternal; ZEND_EXTERN_MODULE_GLOBALS(ice) // // Class entries represent the PHP class implementations we have registered. // namespace IcePHP { zend_class_entry* typeInfoClassEntry = 0; zend_class_entry* exceptionInfoClassEntry = 0; } static zend_object_handlers _typeInfoHandlers; static zend_object_handlers _exceptionInfoHandlers; static string _unsetGUID = "710A52F2-A014-4CB2-AF40-348D48DBCDDD"; extern "C" { static zend_object_value handleTypeInfoAlloc(zend_class_entry* TSRMLS_DC); static void handleTypeInfoFreeStorage(void* TSRMLS_DC); static zend_object_value handleExceptionInfoAlloc(zend_class_entry* TSRMLS_DC); static void handleExceptionInfoFreeStorage(void* TSRMLS_DC); } typedef map ProxyInfoMap; typedef map ClassInfoMap; typedef map CompactIdMap; typedef map ExceptionInfoMap; // // addProxyInfo() // static void addProxyInfo(const ProxyInfoPtr& p TSRMLS_DC) { ProxyInfoMap* m; if(ICE_G(proxyInfoMap)) { m = reinterpret_cast(ICE_G(proxyInfoMap)); } else { m = new ProxyInfoMap; ICE_G(proxyInfoMap) = m; } m->insert(ProxyInfoMap::value_type(p->id, p)); } // // getProxyInfo() // IcePHP::ProxyInfoPtr IcePHP::getProxyInfo(const string& id TSRMLS_DC) { if(ICE_G(proxyInfoMap)) { ProxyInfoMap* m = reinterpret_cast(ICE_G(proxyInfoMap)); ProxyInfoMap::iterator p = m->find(id); if(p != m->end()) { return p->second; } } return 0; } // // addClassInfoById() // static void addClassInfoById(const ClassInfoPtr& p TSRMLS_DC) { assert(!getClassInfoById(p->id TSRMLS_CC)); ClassInfoMap* m = reinterpret_cast(ICE_G(idToClassInfoMap)); if(!m) { m = new ClassInfoMap; ICE_G(idToClassInfoMap) = m; } m->insert(ClassInfoMap::value_type(p->id, p)); } // // addClassInfoByName() // static void addClassInfoByName(const ClassInfoPtr& p TSRMLS_DC) { assert(!getClassInfoByName(p->name TSRMLS_CC)); #ifdef ICEPHP_USE_NAMESPACES assert(p->name[0] == '\\'); #endif ClassInfoMap* m = reinterpret_cast(ICE_G(nameToClassInfoMap)); if(!m) { m = new ClassInfoMap; ICE_G(nameToClassInfoMap) = m; } m->insert(ClassInfoMap::value_type(p->name, p)); } static ClassInfoPtr getClassInfoByClass(zend_class_entry* cls, zend_class_entry* formal TSRMLS_DC) { // // See if there's a match in our class name => ClassInfo map. // ClassInfoPtr info = getClassInfoByName(cls->name TSRMLS_CC); // // Check the base class, assuming it's compatible with our formal type (if any). // if(!info && cls->parent && (!formal || checkClass(cls->parent, formal))) { info = getClassInfoByClass(cls->parent, formal TSRMLS_CC); } // // Check interfaces. // if(!info) { for(zend_uint i = 0; i < cls->num_interfaces && !info; ++i) { if(!formal || checkClass(cls->interfaces[i], formal)) { info = getClassInfoByClass(cls->interfaces[i], formal TSRMLS_CC); } } } return info; } // // getClassInfoById() // IcePHP::ClassInfoPtr IcePHP::getClassInfoById(const string& id TSRMLS_DC) { if(ICE_G(idToClassInfoMap)) { ClassInfoMap* m = reinterpret_cast(ICE_G(idToClassInfoMap)); ClassInfoMap::iterator p = m->find(id); if(p != m->end()) { return p->second; } } return 0; } // // getClassInfoByName() // IcePHP::ClassInfoPtr IcePHP::getClassInfoByName(const string& name TSRMLS_DC) { if(ICE_G(nameToClassInfoMap)) { string s = name; #ifdef ICEPHP_USE_NAMESPACES // // PHP's class definition (zend_class_entry) does not use a leading backslash // in the class name. // if(s[0] != '\\') { s.insert(0, "\\"); } #endif ClassInfoMap* m = reinterpret_cast(ICE_G(nameToClassInfoMap)); ClassInfoMap::iterator p = m->find(s); if(p != m->end()) { return p->second; } } return 0; } // // getExceptionInfo() // IcePHP::ExceptionInfoPtr IcePHP::getExceptionInfo(const string& id TSRMLS_DC) { if(ICE_G(exceptionInfoMap)) { ExceptionInfoMap* m = reinterpret_cast(ICE_G(exceptionInfoMap)); ExceptionInfoMap::iterator p = m->find(id); if(p != m->end()) { return p->second; } } return 0; } // // StreamUtil implementation // zend_class_entry* IcePHP::StreamUtil::_slicedDataType = 0; zend_class_entry* IcePHP::StreamUtil::_sliceInfoType = 0; IcePHP::StreamUtil::~StreamUtil() { // // Make sure we break any cycles among the ObjectReaders in preserved slices. // for(set::iterator p = _readers.begin(); p != _readers.end(); ++p) { Ice::SlicedDataPtr slicedData = (*p)->getSlicedData(); for(Ice::SliceInfoSeq::const_iterator q = slicedData->slices.begin(); q != slicedData->slices.end(); ++q) { // // Don't just call (*q)->instances.clear(), as releasing references // to the instances could have unexpected side effects. We exchange // the vector into a temporary and then let the temporary fall out // of scope. // vector tmp; tmp.swap((*q)->instances); } } } void IcePHP::StreamUtil::add(const ReadObjectCallbackPtr& callback) { _callbacks.push_back(callback); } void IcePHP::StreamUtil::add(const ObjectReaderPtr& reader) { assert(reader->getSlicedData()); _readers.insert(reader); } void IcePHP::StreamUtil::updateSlicedData(TSRMLS_D) { for(set::iterator p = _readers.begin(); p != _readers.end(); ++p) { setSlicedDataMember((*p)->getObject(), (*p)->getSlicedData() TSRMLS_CC); } } void IcePHP::StreamUtil::setSlicedDataMember(zval* obj, const Ice::SlicedDataPtr& slicedData TSRMLS_DC) { // // Create a PHP equivalent of the SlicedData object. // assert(slicedData); if(!_slicedDataType) { _slicedDataType = idToClass("::Ice::SlicedData" TSRMLS_CC); assert(_slicedDataType); } if(!_sliceInfoType) { _sliceInfoType = idToClass("::Ice::SliceInfo" TSRMLS_CC); assert(_sliceInfoType); } zval* sd; MAKE_STD_ZVAL(sd); AutoDestroy sdDestroyer(sd); if(object_init_ex(sd, _slicedDataType) != SUCCESS) { throw AbortMarshaling(); } zval* slices; MAKE_STD_ZVAL(slices); array_init(slices); AutoDestroy slicesDestroyer(slices); if(add_property_zval(sd, STRCAST("slices"), slices) != SUCCESS) { throw AbortMarshaling(); } // // Translate each SliceInfo object into its PHP equivalent. // for(vector::const_iterator p = slicedData->slices.begin(); p != slicedData->slices.end(); ++p) { zval* slice; MAKE_STD_ZVAL(slice); AutoDestroy sliceDestroyer(slice); if(object_init_ex(slice, _sliceInfoType) != SUCCESS) { throw AbortMarshaling(); } add_next_index_zval(slices, slice); // Steals a reference. Z_ADDREF_P(slice); // // typeId // zval* typeId; MAKE_STD_ZVAL(typeId); AutoDestroy typeIdDestroyer(typeId); ZVAL_STRINGL(typeId, STRCAST((*p)->typeId.c_str()), static_cast((*p)->typeId.size()), 1); if(add_property_zval(slice, STRCAST("typeId"), typeId) != SUCCESS) { throw AbortMarshaling(); } // // compactId // zval* compactId; MAKE_STD_ZVAL(compactId); AutoDestroy compactIdDestroyer(compactId); ZVAL_LONG(compactId, (*p)->compactId); if(add_property_zval(slice, STRCAST("compactId"), compactId) != SUCCESS) { throw AbortMarshaling(); } // // bytes // zval* bytes; MAKE_STD_ZVAL(bytes); array_init(bytes); AutoDestroy bytesDestroyer(bytes); for(vector::const_iterator q = (*p)->bytes.begin(); q != (*p)->bytes.end(); ++q) { add_next_index_long(bytes, *q & 0xff); } if(add_property_zval(slice, STRCAST("bytes"), bytes) != SUCCESS) { throw AbortMarshaling(); } // // instances // zval* instances; MAKE_STD_ZVAL(instances); array_init(instances); AutoDestroy instancesDestroyer(instances); if(add_property_zval(slice, STRCAST("instances"), instances) != SUCCESS) { throw AbortMarshaling(); } for(vector::const_iterator q = (*p)->instances.begin(); q != (*p)->instances.end(); ++q) { // // Each element in the instances list is an instance of ObjectReader that wraps a PHP object. // assert(*q); ObjectReaderPtr r = ObjectReaderPtr::dynamicCast(*q); assert(r); zval* o = r->getObject(); assert(Z_TYPE_P(o) == IS_OBJECT); // Should be non-nil. add_next_index_zval(instances, o); // Steals a reference. Z_ADDREF_P(o); } // // hasOptionalMembers // zval* hasOptionalMembers; MAKE_STD_ZVAL(hasOptionalMembers); AutoDestroy hasOptionalMembersDestroyer(hasOptionalMembers); ZVAL_BOOL(hasOptionalMembers, (*p)->hasOptionalMembers ? 1 : 0); if(add_property_zval(slice, STRCAST("hasOptionalMembers"), hasOptionalMembers) != SUCCESS) { throw AbortMarshaling(); } // // isLastSlice // zval* isLastSlice; MAKE_STD_ZVAL(isLastSlice); AutoDestroy isLastSliceDestroyer(isLastSlice); ZVAL_BOOL(isLastSlice, (*p)->isLastSlice ? 1 : 0); if(add_property_zval(slice, STRCAST("isLastSlice"), isLastSlice) != SUCCESS) { throw AbortMarshaling(); } } if(add_property_zval(obj, STRCAST("_ice_slicedData"), sd) != SUCCESS) { throw AbortMarshaling(); } } // // Instances of preserved class and exception types may have a data member // named _ice_slicedData which is an instance of the PHP class Ice_SlicedData. // Ice::SlicedDataPtr IcePHP::StreamUtil::getSlicedDataMember(zval* obj, ObjectMap* objectMap TSRMLS_DC) { Ice::SlicedDataPtr slicedData; string name = "_ice_slicedData"; void* data; if(zend_hash_find(Z_OBJPROP_P(obj), STRCAST(name.c_str()), name.size() + 1, &data) == SUCCESS) { zval* sd = *(reinterpret_cast(data)); if(Z_TYPE_P(sd) != IS_NULL) { #ifndef NDEBUG int status; #endif // // The "slices" member is an array of Ice_SliceInfo objects. // #ifndef NDEBUG status = #endif zend_hash_find(Z_OBJPROP_P(sd), STRCAST("slices"), sizeof("slices"), &data); assert(status == SUCCESS); zval* sl = *(reinterpret_cast(data)); assert(Z_TYPE_P(sl) == IS_ARRAY); Ice::SliceInfoSeq slices; HashTable* arr = Z_ARRVAL_P(sl); assert(arr); HashPosition pos; zend_hash_internal_pointer_reset_ex(arr, &pos); while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval* s = *(reinterpret_cast(data)); assert(Z_OBJCE_P(s) == _sliceInfoType); Ice::SliceInfoPtr info = new Ice::SliceInfo; #ifndef NDEBUG status = #endif zend_hash_find(Z_OBJPROP_P(s), STRCAST("typeId"), sizeof("typeId"), &data); assert(status == SUCCESS); zval* typeId = *(reinterpret_cast(data)); assert(Z_TYPE_P(typeId) == IS_STRING); info->typeId = string(Z_STRVAL_P(typeId), Z_STRLEN_P(typeId)); #ifndef NDEBUG status = #endif zend_hash_find(Z_OBJPROP_P(s), STRCAST("compactId"), sizeof("compactId"), &data); assert(status == SUCCESS); zval* compactId = *(reinterpret_cast(data)); assert(Z_TYPE_P(compactId) == IS_LONG); info->compactId = Z_LVAL_P(compactId); #ifndef NDEBUG status = #endif zend_hash_find(Z_OBJPROP_P(s), STRCAST("bytes"), sizeof("bytes"), &data); assert(status == SUCCESS); zval* bytes = *(reinterpret_cast(data)); assert(Z_TYPE_P(bytes) == IS_ARRAY); HashTable* barr = Z_ARRVAL_P(bytes); HashPosition bpos; zend_hash_internal_pointer_reset_ex(barr, &bpos); info->bytes.resize(zend_hash_num_elements(barr)); vector::size_type i = 0; while(zend_hash_get_current_data_ex(barr, &data, &bpos) != FAILURE) { zval* e = *(reinterpret_cast(data)); long l = Z_LVAL_P(e); assert(l >= 0 && l <= 255); info->bytes[i++] = static_cast(l); zend_hash_move_forward_ex(barr, &bpos); } #ifndef NDEBUG status = #endif zend_hash_find(Z_OBJPROP_P(s), STRCAST("instances"), sizeof("instances"), &data); assert(status == SUCCESS); zval* instances = *(reinterpret_cast(data)); assert(Z_TYPE_P(instances) == IS_ARRAY); HashTable* oarr = Z_ARRVAL_P(instances); HashPosition opos; zend_hash_internal_pointer_reset_ex(oarr, &opos); while(zend_hash_get_current_data_ex(oarr, &data, &opos) != FAILURE) { zval* o = *(reinterpret_cast(data)); assert(Z_TYPE_P(o) == IS_OBJECT); Ice::ObjectPtr writer; ObjectMap::iterator j = objectMap->find(Z_OBJ_HANDLE_P(o)); if(j == objectMap->end()) { writer = new ObjectWriter(o, objectMap, 0 TSRMLS_CC); objectMap->insert(ObjectMap::value_type(Z_OBJ_HANDLE_P(o), writer)); } else { writer = j->second; } info->instances.push_back(writer); zend_hash_move_forward_ex(oarr, &opos); } #ifndef NDEBUG status = #endif zend_hash_find(Z_OBJPROP_P(s), STRCAST("hasOptionalMembers"), sizeof("hasOptionalMembers"), &data); assert(status == SUCCESS); zval* hasOptionalMembers = *(reinterpret_cast(data)); assert(Z_TYPE_P(hasOptionalMembers) == IS_BOOL); info->hasOptionalMembers = Z_BVAL_P(hasOptionalMembers) ? true : false; #ifndef NDEBUG status = #endif zend_hash_find(Z_OBJPROP_P(s), STRCAST("isLastSlice"), sizeof("isLastSlice"), &data); assert(status == SUCCESS); zval* isLastSlice = *(reinterpret_cast(data)); assert(Z_TYPE_P(isLastSlice) == IS_BOOL); info->isLastSlice = Z_BVAL_P(isLastSlice) ? true : false; slices.push_back(info); zend_hash_move_forward_ex(arr, &pos); } slicedData = new Ice::SlicedData(slices); } } return slicedData; } // // UnmarshalCallback implementation. // IcePHP::UnmarshalCallback::~UnmarshalCallback() { } // // TypeInfo implementation. // IcePHP::TypeInfo::TypeInfo() { } bool IcePHP::TypeInfo::usesClasses() const { return false; } void IcePHP::TypeInfo::unmarshaled(zval*, zval*, void* TSRMLS_DC) { assert(false); } void IcePHP::TypeInfo::destroy() { } // // PrimitiveInfo implementation. // string IcePHP::PrimitiveInfo::getId() const { switch(kind) { case KindBool: return "bool"; case KindByte: return "byte"; case KindShort: return "short"; case KindInt: return "int"; case KindLong: return "long"; case KindFloat: return "float"; case KindDouble: return "double"; case KindString: return "string"; } assert(false); return string(); } bool IcePHP::PrimitiveInfo::validate(zval* zv, bool throwException TSRMLS_DC) { switch(kind) { case PrimitiveInfo::KindBool: { if(Z_TYPE_P(zv) != IS_BOOL) { if(throwException) { string s = zendTypeToString(Z_TYPE_P(zv)); invalidArgument("expected boolean value but received %s" TSRMLS_CC, s.c_str()); } return false; } break; } case PrimitiveInfo::KindByte: { if(Z_TYPE_P(zv) != IS_LONG) { string s = zendTypeToString(Z_TYPE_P(zv)); if(throwException) { invalidArgument("expected byte value but received %s" TSRMLS_CC, s.c_str()); } return false; } long val = Z_LVAL_P(zv); if(val < 0 || val > 255) { if(throwException) { invalidArgument("value %ld is out of range for a byte" TSRMLS_CC, val); } return false; } break; } case PrimitiveInfo::KindShort: { if(Z_TYPE_P(zv) != IS_LONG) { string s = zendTypeToString(Z_TYPE_P(zv)); if(throwException) { invalidArgument("expected short value but received %s" TSRMLS_CC, s.c_str()); } return false; } long val = Z_LVAL_P(zv); if(val < SHRT_MIN || val > SHRT_MAX) { if(throwException) { invalidArgument("value %ld is out of range for a short" TSRMLS_CC, val); } return false; } break; } case PrimitiveInfo::KindInt: { if(Z_TYPE_P(zv) != IS_LONG) { string s = zendTypeToString(Z_TYPE_P(zv)); if(throwException) { invalidArgument("expected int value but received %s" TSRMLS_CC, s.c_str()); } return false; } long val = Z_LVAL_P(zv); if(val < INT_MIN || val > INT_MAX) { if(throwException) { invalidArgument("value %ld is out of range for an int" TSRMLS_CC, val); } return false; } break; } case PrimitiveInfo::KindLong: { // // The platform's 'long' type may not be 64 bits, so we also accept // a string argument for this type. // if(Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_STRING) { string s = zendTypeToString(Z_TYPE_P(zv)); if(throwException) { invalidArgument("expected long value but received %s" TSRMLS_CC, s.c_str()); } return false; } Ice::Long val; if(Z_TYPE_P(zv) == IS_LONG) { val = Z_LVAL_P(zv); } else { string sval(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); if(!IceUtilInternal::stringToInt64(sval, val)) { if(throwException) { invalidArgument("invalid long value `%s'" TSRMLS_CC, Z_STRVAL_P(zv)); } return false; } } break; } case PrimitiveInfo::KindFloat: { if(Z_TYPE_P(zv) != IS_DOUBLE && Z_TYPE_P(zv) != IS_LONG) { string s = zendTypeToString(Z_TYPE_P(zv)); if(throwException) { invalidArgument("expected float value but received %s" TSRMLS_CC, s.c_str()); } return false; } if(Z_TYPE_P(zv) == IS_DOUBLE) { double val = Z_DVAL_P(zv); return (val <= numeric_limits::max() && val >= -numeric_limits::max()) || #if defined(_MSC_VER) && (_MSC_VER <= 1700) !_finite(val); #else !isfinite(val); #endif } break; } case PrimitiveInfo::KindDouble: { if(Z_TYPE_P(zv) != IS_DOUBLE && Z_TYPE_P(zv) != IS_LONG) { string s = zendTypeToString(Z_TYPE_P(zv)); if(throwException) { invalidArgument("expected double value but received %s" TSRMLS_CC, s.c_str()); } return false; } break; } case PrimitiveInfo::KindString: { if(Z_TYPE_P(zv) != IS_STRING && Z_TYPE_P(zv) != IS_NULL) { string s = zendTypeToString(Z_TYPE_P(zv)); if(throwException) { invalidArgument("expected string value but received %s" TSRMLS_CC, s.c_str()); } return false; } break; } } return true; } bool IcePHP::PrimitiveInfo::variableLength() const { return kind == KindString; } int IcePHP::PrimitiveInfo::wireSize() const { switch(kind) { case KindBool: case KindByte: return 1; case KindShort: return 2; case KindInt: return 4; case KindLong: return 8; case KindFloat: return 4; case KindDouble: return 8; case KindString: return 1; } assert(false); return 0; } Ice::OptionalFormat IcePHP::PrimitiveInfo::optionalFormat() const { switch(kind) { case KindBool: case KindByte: return Ice::OptionalFormatF1; case KindShort: return Ice::OptionalFormatF2; case KindInt: return Ice::OptionalFormatF4; case KindLong: return Ice::OptionalFormatF8; case KindFloat: return Ice::OptionalFormatF4; case KindDouble: return Ice::OptionalFormatF8; case KindString: return Ice::OptionalFormatVSize; } assert(false); return Ice::OptionalFormatF1; } void IcePHP::PrimitiveInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap*, bool TSRMLS_DC) { switch(kind) { case PrimitiveInfo::KindBool: { assert(Z_TYPE_P(zv) == IS_BOOL); os->write(Z_BVAL_P(zv) ? true : false); break; } case PrimitiveInfo::KindByte: { assert(Z_TYPE_P(zv) == IS_LONG); long val = Z_LVAL_P(zv); assert(val >= 0 && val <= 255); // validate() should have caught this. os->write(static_cast(val)); break; } case PrimitiveInfo::KindShort: { assert(Z_TYPE_P(zv) == IS_LONG); long val = Z_LVAL_P(zv); assert(val >= SHRT_MIN && val <= SHRT_MAX); // validate() should have caught this. os->write(static_cast(val)); break; } case PrimitiveInfo::KindInt: { assert(Z_TYPE_P(zv) == IS_LONG); long val = Z_LVAL_P(zv); assert(val >= INT_MIN && val <= INT_MAX); // validate() should have caught this. os->write(static_cast(val)); break; } case PrimitiveInfo::KindLong: { // // The platform's 'long' type may not be 64 bits, so we also accept // a string argument for this type. // assert(Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_STRING); // validate() should have caught this. Ice::Long val; if(Z_TYPE_P(zv) == IS_LONG) { val = Z_LVAL_P(zv); } else { string sval(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); IceUtilInternal::stringToInt64(sval, val); } os->write(val); break; } case PrimitiveInfo::KindFloat: { Ice::Double val = 0; if(Z_TYPE_P(zv) == IS_DOUBLE) { val = Z_DVAL_P(zv); } else if(Z_TYPE_P(zv) == IS_LONG) { val = static_cast(Z_LVAL_P(zv)); } else { assert(false); // validate() should have caught this. } os->write(static_cast(val)); break; } case PrimitiveInfo::KindDouble: { Ice::Double val = 0; if(Z_TYPE_P(zv) == IS_DOUBLE) { val = Z_DVAL_P(zv); } else if(Z_TYPE_P(zv) == IS_LONG) { val = static_cast(Z_LVAL_P(zv)); } else { assert(false); // validate() should have caught this. } os->write(val); break; } case PrimitiveInfo::KindString: { assert(Z_TYPE_P(zv) == IS_STRING || Z_TYPE_P(zv) == IS_NULL); // validate() should have caught this. if(Z_TYPE_P(zv) == IS_STRING) { string val(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); os->write(val); } else { os->write(string()); } break; } } } void IcePHP::PrimitiveInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb, const CommunicatorInfoPtr&, zval* target, void* closure, bool TSRMLS_DC) { zval* zv; MAKE_STD_ZVAL(zv); AutoDestroy destroy(zv); switch(kind) { case PrimitiveInfo::KindBool: { bool val; is->read(val); ZVAL_BOOL(zv, val ? 1 : 0); break; } case PrimitiveInfo::KindByte: { Ice::Byte val; is->read(val); ZVAL_LONG(zv, val & 0xff); break; } case PrimitiveInfo::KindShort: { Ice::Short val; is->read(val); ZVAL_LONG(zv, val); break; } case PrimitiveInfo::KindInt: { Ice::Int val; is->read(val); ZVAL_LONG(zv, val); break; } case PrimitiveInfo::KindLong: { Ice::Long val; is->read(val); // // The platform's 'long' type may not be 64 bits, so we store 64-bit // values as a string. // if(sizeof(Ice::Long) > sizeof(long) && (val < LONG_MIN || val > LONG_MAX)) { string str = IceUtilInternal::int64ToString(val); ZVAL_STRINGL(zv, STRCAST(str.c_str()), static_cast(str.length()), 1); } else { ZVAL_LONG(zv, static_cast(val)); } break; } case PrimitiveInfo::KindFloat: { Ice::Float val; is->read(val); ZVAL_DOUBLE(zv, val); break; } case PrimitiveInfo::KindDouble: { Ice::Double val; is->read(val); ZVAL_DOUBLE(zv, val); break; } case PrimitiveInfo::KindString: { string val; is->read(val); ZVAL_STRINGL(zv, STRCAST(val.c_str()), static_cast(val.length()), 1); break; } } cb->unmarshaled(zv, target, closure TSRMLS_CC); } void IcePHP::PrimitiveInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* TSRMLS_DC) { if(!validate(zv, false TSRMLS_CC)) { out << ""; return; } zval tmp = *zv; zval_copy_ctor(&tmp); INIT_PZVAL(&tmp); convert_to_string(&tmp); out << Z_STRVAL(tmp); zval_dtor(&tmp); } // // EnumInfo implementation. // IcePHP::EnumInfo::EnumInfo(const string& ident, zval* en TSRMLS_DC) : id(ident), maxValue(0) { HashTable* arr = Z_ARRVAL_P(en); void* data; HashPosition pos; zend_hash_internal_pointer_reset_ex(arr, &pos); while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val; val = reinterpret_cast(data); assert(Z_TYPE_PP(val) == IS_STRING); string name = Z_STRVAL_PP(val); zend_hash_move_forward_ex(arr, &pos); zend_hash_get_current_data_ex(arr, &data, &pos); val = reinterpret_cast(data); assert(Z_TYPE_PP(val) == IS_LONG); Ice::Int value = static_cast(Z_LVAL_PP(val)); zend_hash_move_forward_ex(arr, &pos); if(value > maxValue) { const_cast(maxValue) = value; } const_cast&>(enumerators)[value] = name; } } string IcePHP::EnumInfo::getId() const { return id; } bool IcePHP::EnumInfo::validate(zval* zv, bool TSRMLS_DC) { if(Z_TYPE_P(zv) == IS_LONG) { const Ice::Int l = static_cast(Z_LVAL_P(zv)); return l >= 0 && enumerators.find(l) != enumerators.end(); } return false; } bool IcePHP::EnumInfo::variableLength() const { return true; } int IcePHP::EnumInfo::wireSize() const { return 1; } Ice::OptionalFormat IcePHP::EnumInfo::optionalFormat() const { return Ice::OptionalFormatSize; } void IcePHP::EnumInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap*, bool TSRMLS_DC) { assert(Z_TYPE_P(zv) == IS_LONG); // validate() should have caught this. const Ice::Int val = static_cast(Z_LVAL_P(zv)); assert(val >= 0 && enumerators.find(val) != enumerators.end()); // validate() should have caught this. os->writeEnum(val, maxValue); } void IcePHP::EnumInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb, const CommunicatorInfoPtr&, zval* target, void* closure, bool TSRMLS_DC) { zval* zv; ALLOC_INIT_ZVAL(zv); AutoDestroy destroy(zv); const Ice::Int val = is->readEnum(maxValue); if(enumerators.find(val) == enumerators.end()) { invalidArgument("enumerator %d is out of range for enum %s" TSRMLS_CC, val, id.c_str()); throw AbortMarshaling(); } ZVAL_LONG(zv, val); cb->unmarshaled(zv, target, closure TSRMLS_CC); } void IcePHP::EnumInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* TSRMLS_DC) { if(!validate(zv, false TSRMLS_CC)) { out << ""; return; } const Ice::Int val = static_cast(Z_LVAL_P(zv)); map::const_iterator p = enumerators.find(val); assert(p != enumerators.end()); out << p->second; } // // DataMember implementation. // void IcePHP::DataMember::unmarshaled(zval* zv, zval* target, void* TSRMLS_DC) { setMember(target, zv TSRMLS_CC); } void IcePHP::DataMember::setMember(zval* target, zval* zv TSRMLS_DC) { assert(Z_TYPE_P(target) == IS_OBJECT); // // The add_property_zval function fails if the data member has protected visibility. // As a workaround, before calling the function we change the current scope to be that // of the object. // zend_class_entry *oldScope = EG(scope); EG(scope) = Z_OBJCE_P(target); // // add_property_zval increments the refcount of zv. // int status = add_property_zval(target, STRCAST(name.c_str()), zv); EG(scope) = oldScope; // Restore the previous scope. if(status == FAILURE) { runtimeError("unable to set member `%s'" TSRMLS_CC, name.c_str()); throw AbortMarshaling(); } } static void convertDataMembers(zval* zv, DataMemberList& reqMembers, DataMemberList& optMembers, bool allowOptional TSRMLS_DC) { list optList; assert(Z_TYPE_P(zv) == IS_ARRAY); HashTable* membersArray = Z_ARRVAL_P(zv); void* data; HashPosition pos; zend_hash_internal_pointer_reset_ex(membersArray, &pos); while(zend_hash_get_current_data_ex(membersArray, &data, &pos) != FAILURE) { zval** arr = reinterpret_cast(data); DataMemberPtr m = new DataMember(); zval** elem; assert(Z_TYPE_PP(arr) == IS_ARRAY); HashTable* member = Z_ARRVAL_PP(arr); assert(zend_hash_num_elements(member) == (allowOptional ? 4 : 2)); zend_hash_index_find(member, 0, reinterpret_cast(&elem)); assert(Z_TYPE_PP(elem) == IS_STRING); m->name = Z_STRVAL_PP(elem); zend_hash_index_find(member, 1, reinterpret_cast(&elem)); assert(Z_TYPE_PP(elem) == IS_OBJECT); m->type = Wrapper::value(*elem TSRMLS_CC); if(allowOptional) { zend_hash_index_find(member, 2, reinterpret_cast(&elem)); assert(Z_TYPE_PP(elem) == IS_BOOL); m->optional = Z_BVAL_PP(elem) ? true : false; zend_hash_index_find(member, 3, reinterpret_cast(&elem)); assert(Z_TYPE_PP(elem) == IS_LONG); m->tag = static_cast(Z_LVAL_PP(elem)); } else { m->optional = false; m->tag = 0; } if(m->optional) { optList.push_back(m); } else { reqMembers.push_back(m); } zend_hash_move_forward_ex(membersArray, &pos); } if(allowOptional) { class SortFn { public: static bool compare(const DataMemberPtr& lhs, const DataMemberPtr& rhs) { return lhs->tag < rhs->tag; } }; optList.sort(SortFn::compare); copy(optList.begin(), optList.end(), back_inserter(optMembers)); } } // // StructInfo implementation. // IcePHP::StructInfo::StructInfo(const string& ident, const string& n, zval* m TSRMLS_DC) : id(ident), name(n), _nullMarshalValue(0) { DataMemberList opt; convertDataMembers(m, const_cast(members), opt, false TSRMLS_CC); assert(opt.empty()); const_cast(zce) = nameToClass(name TSRMLS_CC); assert(zce); _variableLength = false; _wireSize = 0; for(DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p) { if(!_variableLength && (*p)->type->variableLength()) { _variableLength = true; } _wireSize += (*p)->type->wireSize(); } } string IcePHP::StructInfo::getId() const { return id; } bool IcePHP::StructInfo::validate(zval* zv, bool throwException TSRMLS_DC) { if(Z_TYPE_P(zv) == IS_NULL) { return true; } else if(Z_TYPE_P(zv) != IS_OBJECT) { if(throwException) { string s = zendTypeToString(Z_TYPE_P(zv)); invalidArgument("expected struct value of type %s but received %s" TSRMLS_CC, zce->name, s.c_str()); } return false; } // // Compare class entries. // zend_class_entry* ce = Z_OBJCE_P(zv); if(ce != zce) { if(throwException) { invalidArgument("expected struct value of type %s but received %s" TSRMLS_CC, zce->name, ce->name); } return false; } return true; } bool IcePHP::StructInfo::variableLength() const { return _variableLength; } int IcePHP::StructInfo::wireSize() const { return _wireSize; } Ice::OptionalFormat IcePHP::StructInfo::optionalFormat() const { return _variableLength ? Ice::OptionalFormatFSize : Ice::OptionalFormatVSize; } bool IcePHP::StructInfo::usesClasses() const { for(DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p) { if((*p)->type->usesClasses()) { return true; } } return false; } void IcePHP::StructInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool optional TSRMLS_DC) { assert(Z_TYPE_P(zv) == IS_NULL || (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zce)); if(Z_TYPE_P(zv) == IS_NULL) { if(_nullMarshalValue == 0) { MAKE_STD_ZVAL(_nullMarshalValue); if(object_init_ex(_nullMarshalValue, const_cast(zce)) != SUCCESS) { runtimeError("unable to initialize object of type %s" TSRMLS_CC, zce->name); throw AbortMarshaling(); } if(!invokeMethod(_nullMarshalValue, ZEND_CONSTRUCTOR_FUNC_NAME TSRMLS_CC)) { assert(false); } } zv = _nullMarshalValue; } Ice::OutputStream::size_type sizePos = 0; if(optional) { if(_variableLength) { sizePos = os->startSize(); } else { os->writeSize(_wireSize); } } for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) { DataMemberPtr member = *q; void* data; if(zend_hash_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size() + 1, &data) == FAILURE) { runtimeError("member `%s' of %s is not defined" TSRMLS_CC, member->name.c_str(), id.c_str()); throw AbortMarshaling(); } zval** val = reinterpret_cast(data); if(!member->type->validate(*val, false TSRMLS_CC)) { invalidArgument("invalid value for %s member `%s'" TSRMLS_CC, id.c_str(), member->name.c_str()); throw AbortMarshaling(); } member->type->marshal(*val, os, objectMap, false TSRMLS_CC); } if(optional && _variableLength) { os->endSize(sizePos); } } void IcePHP::StructInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb, const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional TSRMLS_DC) { zval* zv; MAKE_STD_ZVAL(zv); AutoDestroy destroy(zv); if(object_init_ex(zv, const_cast(zce)) != SUCCESS) { runtimeError("unable to initialize object of type %s" TSRMLS_CC, zce->name); throw AbortMarshaling(); } if(optional) { if(_variableLength) { is->skip(4); } else { is->skipSize(); } } for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) { DataMemberPtr member = *q; member->type->unmarshal(is, member, comm, zv, 0, false TSRMLS_CC); } cb->unmarshaled(zv, target, closure TSRMLS_CC); } void IcePHP::StructInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history TSRMLS_DC) { if(!validate(zv, false TSRMLS_CC)) { out << ""; return; } if(Z_TYPE_P(zv) == IS_NULL) { out << ""; } else { out.sb(); for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) { DataMemberPtr member = *q; out << nl << member->name << " = "; void* data; if(zend_hash_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size() + 1, &data) == SUCCESS) { zval** val = reinterpret_cast(data); member->type->print(*val, out, history TSRMLS_CC); } else { out << ""; } } out.eb(); } } void IcePHP::StructInfo::destroy() { for(DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p) { (*p)->type->destroy(); } const_cast(members).clear(); if(_nullMarshalValue != 0) { zval_ptr_dtor(&_nullMarshalValue); _nullMarshalValue = 0; } } // // SequenceInfo implementation. // IcePHP::SequenceInfo::SequenceInfo(const string& ident, zval* e TSRMLS_DC) : id(ident) { const_cast(elementType) = Wrapper::value(e TSRMLS_CC); } string IcePHP::SequenceInfo::getId() const { return id; } bool IcePHP::SequenceInfo::validate(zval* zv, bool TSRMLS_DC) { return Z_TYPE_P(zv) == IS_NULL || Z_TYPE_P(zv) == IS_ARRAY; } bool IcePHP::SequenceInfo::variableLength() const { return true; } int IcePHP::SequenceInfo::wireSize() const { return 1; } Ice::OptionalFormat IcePHP::SequenceInfo::optionalFormat() const { return elementType->variableLength() ? Ice::OptionalFormatFSize : Ice::OptionalFormatVSize; } bool IcePHP::SequenceInfo::usesClasses() const { return elementType->usesClasses(); } void IcePHP::SequenceInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool optional TSRMLS_DC) { Ice::Int sz = 0; HashTable* arr = 0; if(Z_TYPE_P(zv) != IS_NULL) { assert(Z_TYPE_P(zv) == IS_ARRAY); // validate() should have caught this. arr = Z_ARRVAL_P(zv); sz = static_cast(zend_hash_num_elements(arr)); } Ice::OutputStream::size_type sizePos = 0; if(optional) { if(elementType->variableLength()) { sizePos = os->startSize(); } else if(elementType->wireSize() > 1) { os->writeSize(sz == 0 ? 1 : sz * elementType->wireSize() + (sz > 254 ? 5 : 1)); } } if(sz == 0) { os->writeSize(0); } else { PrimitiveInfoPtr pi = PrimitiveInfoPtr::dynamicCast(elementType); if(pi) { marshalPrimitiveSequence(pi, zv, os TSRMLS_CC); return; } HashPosition pos; zend_hash_internal_pointer_reset_ex(arr, &pos); os->writeSize(sz); void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!elementType->validate(*val, false TSRMLS_CC)) { invalidArgument("invalid value for sequence element `%s'" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } elementType->marshal(*val, os, objectMap, false TSRMLS_CC); zend_hash_move_forward_ex(arr, &pos); } } if(optional && elementType->variableLength()) { os->endSize(sizePos); } } void IcePHP::SequenceInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb, const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional TSRMLS_DC) { if(optional) { if(elementType->variableLength()) { is->skip(4); } else if(elementType->wireSize() > 1) { is->skipSize(); } } PrimitiveInfoPtr pi = PrimitiveInfoPtr::dynamicCast(elementType); if(pi) { unmarshalPrimitiveSequence(pi, is, cb, target, closure TSRMLS_CC); return; } zval* zv; MAKE_STD_ZVAL(zv); array_init(zv); AutoDestroy destroy(zv); Ice::Int sz = is->readSize(); for(Ice::Int i = 0; i < sz; ++i) { // // Add a temporary null value so that the foreach order is the // same as the index order. // add_index_null(zv, i); void* cl = reinterpret_cast(i); elementType->unmarshal(is, this, comm, zv, cl, false TSRMLS_CC); } cb->unmarshaled(zv, target, closure TSRMLS_CC); } void IcePHP::SequenceInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history TSRMLS_DC) { if(!validate(zv, false TSRMLS_CC)) { out << ""; return; } if(Z_TYPE_P(zv) == IS_NULL) { out << "{}"; } else { assert(Z_TYPE_P(zv) == IS_ARRAY); HashTable* arr = Z_ARRVAL_P(zv); HashPosition pos; zend_hash_internal_pointer_reset_ex(arr, &pos); out.sb(); int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); out << nl << '[' << i << "] = "; elementType->print(*val, out, history TSRMLS_CC); zend_hash_move_forward_ex(arr, &pos); ++i; } out.eb(); } } void IcePHP::SequenceInfo::unmarshaled(zval* zv, zval* target, void* closure TSRMLS_DC) { assert(Z_TYPE_P(target) == IS_ARRAY); long i = reinterpret_cast(closure); add_index_zval(target, i, zv); Z_ADDREF_P(zv); } void IcePHP::SequenceInfo::destroy() { if(elementType) { elementType->destroy(); const_cast(elementType) = 0; } } void IcePHP::SequenceInfo::marshalPrimitiveSequence(const PrimitiveInfoPtr& pi, zval* zv, Ice::OutputStream* os TSRMLS_DC) { HashTable* arr = Z_ARRVAL_P(zv); HashPosition pos; zend_hash_internal_pointer_reset_ex(arr, &pos); Ice::Int sz = static_cast(zend_hash_num_elements(arr)); assert(sz > 0); switch(pi->kind) { case PrimitiveInfo::KindBool: { Ice::BoolSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } seq[i++] = Z_BVAL_P(*val) ? true : false; zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } case PrimitiveInfo::KindByte: { Ice::ByteSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } long l = Z_LVAL_P(*val); assert(l >= 0 && l <= 255); seq[i++] = static_cast(l); zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } case PrimitiveInfo::KindShort: { Ice::ShortSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } long l = Z_LVAL_P(*val); assert(l >= SHRT_MIN && l <= SHRT_MAX); seq[i++] = static_cast(l); zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } case PrimitiveInfo::KindInt: { Ice::IntSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } long l = Z_LVAL_P(*val); assert(l >= INT_MIN && l <= INT_MAX); seq[i++] = static_cast(l); zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } case PrimitiveInfo::KindLong: { Ice::LongSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } // // The platform's 'long' type may not be 64 bits, so we also accept // a string argument for this type. // assert(Z_TYPE_P(*val) == IS_LONG || Z_TYPE_P(*val) == IS_STRING); Ice::Long l; if(Z_TYPE_P(*val) == IS_LONG) { l = Z_LVAL_P(*val); } else { string sval(Z_STRVAL_P(*val), Z_STRLEN_P(*val)); IceUtilInternal::stringToInt64(sval, l); } seq[i++] = l; zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } case PrimitiveInfo::KindFloat: { Ice::FloatSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } double d = 0; if(Z_TYPE_P(*val) == IS_DOUBLE) { d = Z_DVAL_P(*val); } else if(Z_TYPE_P(*val) == IS_LONG) { d = static_cast(Z_LVAL_P(*val)); } else { assert(false); // validate() should have caught this. } seq[i++] = static_cast(d); zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } case PrimitiveInfo::KindDouble: { Ice::DoubleSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } double d = 0; if(Z_TYPE_P(*val) == IS_DOUBLE) { d = Z_DVAL_P(*val); } else if(Z_TYPE_P(*val) == IS_LONG) { d = static_cast(Z_LVAL_P(*val)); } else { assert(false); // validate() should have caught this. } seq[i++] = d; zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } case PrimitiveInfo::KindString: { Ice::StringSeq seq(sz); Ice::Int i = 0; void* data; while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); if(!pi->validate(*val, true TSRMLS_CC)) { throw AbortMarshaling(); } string s; if(Z_TYPE_P(*val) == IS_STRING) { s = string(Z_STRVAL_P(*val), Z_STRLEN_P(*val)); } else { assert(Z_TYPE_P(*val) == IS_NULL); } seq[i++] = s; zend_hash_move_forward_ex(arr, &pos); } os->write(seq); break; } } } void IcePHP::SequenceInfo::unmarshalPrimitiveSequence(const PrimitiveInfoPtr& pi, Ice::InputStream* is, const UnmarshalCallbackPtr& cb, zval* target, void* closure TSRMLS_DC) { zval* zv; MAKE_STD_ZVAL(zv); array_init(zv); AutoDestroy destroy(zv); switch(pi->kind) { case PrimitiveInfo::KindBool: { pair pr; IceUtil::ScopedArray arr; is->read(pr, arr); for(const bool* p = pr.first; p != pr.second; ++p) { add_next_index_bool(zv, *p ? 1 : 0); } break; } case PrimitiveInfo::KindByte: { pair pr; is->read(pr); for(const Ice::Byte* p = pr.first; p != pr.second; ++p) { add_next_index_long(zv, *p & 0xff); } break; } case PrimitiveInfo::KindShort: { pair pr; IceUtil::ScopedArray arr; is->read(pr, arr); for(const Ice::Short* p = pr.first; p != pr.second; ++p) { add_next_index_long(zv, *p); } break; } case PrimitiveInfo::KindInt: { pair pr; IceUtil::ScopedArray arr; is->read(pr, arr); for(const Ice::Int* p = pr.first; p != pr.second; ++p) { add_next_index_long(zv, *p); } break; } case PrimitiveInfo::KindLong: { pair pr; IceUtil::ScopedArray arr; is->read(pr, arr); Ice::Int i = 0; for(const Ice::Long* p = pr.first; p != pr.second; ++p, ++i) { zval* val; MAKE_STD_ZVAL(val); // // The platform's 'long' type may not be 64 bits, so we store 64-bit // values as a string. // if(sizeof(Ice::Long) > sizeof(long) && (*p < LONG_MIN || *p > LONG_MAX)) { string str = IceUtilInternal::int64ToString(*p); ZVAL_STRINGL(val, STRCAST(str.c_str()), static_cast(str.length()), 1); } else { ZVAL_LONG(val, static_cast(*p)); } add_index_zval(zv, i, val); } break; } case PrimitiveInfo::KindFloat: { pair pr; IceUtil::ScopedArray arr; is->read(pr, arr); Ice::Int i = 0; for(const Ice::Float* p = pr.first; p != pr.second; ++p, ++i) { zval* val; MAKE_STD_ZVAL(val); ZVAL_DOUBLE(val, *p); add_index_zval(zv, i, val); } break; } case PrimitiveInfo::KindDouble: { pair pr; IceUtil::ScopedArray arr; is->read(pr, arr); Ice::Int i = 0; for(const Ice::Double* p = pr.first; p != pr.second; ++p, ++i) { zval* val; MAKE_STD_ZVAL(val); ZVAL_DOUBLE(val, *p); add_index_zval(zv, i, val); } break; } case PrimitiveInfo::KindString: { Ice::StringSeq seq; is->read(seq, true); Ice::Int i = 0; for(Ice::StringSeq::iterator p = seq.begin(); p != seq.end(); ++p, ++i) { zval* val; MAKE_STD_ZVAL(val); ZVAL_STRINGL(val, STRCAST(p->c_str()), static_cast(p->length()), 1); add_index_zval(zv, i, val); } break; } } cb->unmarshaled(zv, target, closure TSRMLS_CC); } // // DictionaryInfo implementation. // IcePHP::DictionaryInfo::DictionaryInfo(const string& ident, zval* k, zval* v TSRMLS_DC) : id(ident) { const_cast(keyType) = Wrapper::value(k TSRMLS_CC); const_cast(valueType) = Wrapper::value(v TSRMLS_CC); _variableLength = keyType->variableLength() || valueType->variableLength(); _wireSize = keyType->wireSize() + valueType->wireSize(); } string IcePHP::DictionaryInfo::getId() const { return id; } bool IcePHP::DictionaryInfo::validate(zval* zv, bool TSRMLS_DC) { return Z_TYPE_P(zv) == IS_NULL || Z_TYPE_P(zv) == IS_ARRAY; } bool IcePHP::DictionaryInfo::variableLength() const { return true; } int IcePHP::DictionaryInfo::wireSize() const { return 1; } Ice::OptionalFormat IcePHP::DictionaryInfo::optionalFormat() const { return _variableLength ? Ice::OptionalFormatFSize : Ice::OptionalFormatVSize; } bool IcePHP::DictionaryInfo::usesClasses() const { return valueType->usesClasses(); } void IcePHP::DictionaryInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool optional TSRMLS_DC) { Ice::Int sz = 0; HashTable* arr = 0; if(Z_TYPE_P(zv) != IS_NULL) { assert(Z_TYPE_P(zv) == IS_ARRAY); // validate() should have caught this. arr = Z_ARRVAL_P(zv); sz = static_cast(zend_hash_num_elements(arr)); } Ice::OutputStream::size_type sizePos = 0; if(optional) { if(_variableLength) { sizePos = os->startSize(); } else { os->writeSize(sz == 0 ? 1 : sz * _wireSize + (sz > 254 ? 5 : 1)); } } PrimitiveInfoPtr piKey = PrimitiveInfoPtr::dynamicCast(keyType); EnumInfoPtr enKey = EnumInfoPtr::dynamicCast(keyType); if(!enKey && (!piKey || piKey->kind == PrimitiveInfo::KindFloat || piKey->kind == PrimitiveInfo::KindDouble)) { invalidArgument("dictionary type `%s' cannot be marshaled" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } os->writeSize(sz); if(sz > 0) { HashPosition pos; void* data; zend_hash_internal_pointer_reset_ex(arr, &pos); while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); // // Get the key (which can be a long or a string). // char* keyStr; uint keyLen; ulong keyNum; int hashKeyType = zend_hash_get_current_key_ex(arr, &keyStr, &keyLen, &keyNum, 0, &pos); // // Store the key in a zval so that we can reuse the marshaling logic. // zval* zkey; MAKE_STD_ZVAL(zkey); AutoDestroy destroy(zkey); if(hashKeyType == HASH_KEY_IS_LONG) { ZVAL_LONG(zkey, keyNum); } else { ZVAL_STRINGL(zkey, keyStr, keyLen - 1, 1); } // // Convert the zval to the required type, if necessary. // if(piKey) { switch(piKey->kind) { case PrimitiveInfo::KindBool: { convert_to_boolean(zkey); break; } case PrimitiveInfo::KindByte: case PrimitiveInfo::KindShort: case PrimitiveInfo::KindInt: case PrimitiveInfo::KindLong: { if(hashKeyType == HASH_KEY_IS_STRING) { convert_to_long(zkey); } break; } case PrimitiveInfo::KindString: { if(hashKeyType == HASH_KEY_IS_LONG) { convert_to_string(zkey); } break; } case PrimitiveInfo::KindFloat: case PrimitiveInfo::KindDouble: assert(false); } } else { if(hashKeyType == HASH_KEY_IS_STRING) { convert_to_long(zkey); } } // // Marshal the key. // if(!keyType->validate(zkey, false TSRMLS_CC)) { invalidArgument("invalid key in `%s' element" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } keyType->marshal(zkey, os, objectMap, false TSRMLS_CC); // // Marshal the value. // if(!valueType->validate(*val, false TSRMLS_CC)) { invalidArgument("invalid value in `%s' element" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } valueType->marshal(*val, os, objectMap, false TSRMLS_CC); zend_hash_move_forward_ex(arr, &pos); } } if(optional && _variableLength) { os->endSize(sizePos); } } void IcePHP::DictionaryInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb, const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional TSRMLS_DC) { if(optional) { if(_variableLength) { is->skip(4); } else { is->skipSize(); } } PrimitiveInfoPtr piKey = PrimitiveInfoPtr::dynamicCast(keyType); EnumInfoPtr enKey = EnumInfoPtr::dynamicCast(keyType); if(!enKey && (!piKey || piKey->kind == PrimitiveInfo::KindFloat || piKey->kind == PrimitiveInfo::KindDouble)) { invalidArgument("dictionary type `%s' cannot be unmarshaled" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } zval* zv; MAKE_STD_ZVAL(zv); array_init(zv); AutoDestroy destroy(zv); KeyCallbackPtr keyCB = new KeyCallback; Ice::Int sz = is->readSize(); for(Ice::Int i = 0; i < sz; ++i) { // // A dictionary key cannot be a class (or contain one), so the key must be // available immediately. // keyType->unmarshal(is, keyCB, comm, 0, 0, false TSRMLS_CC); assert(keyCB->key); // // Allocate a callback that holds a reference to the key. // ValueCallbackPtr valueCB = new ValueCallback(keyCB->key TSRMLS_CC); // // Pass the key to the callback. // valueType->unmarshal(is, valueCB, comm, zv, 0, false TSRMLS_CC); } cb->unmarshaled(zv, target, closure TSRMLS_CC); } void IcePHP::DictionaryInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history TSRMLS_DC) { if(!validate(zv, false TSRMLS_CC)) { out << ""; return; } if(Z_TYPE_P(zv) == IS_NULL) { out << "{}"; } else { HashTable* arr = Z_ARRVAL_P(zv); HashPosition pos; void* data; bool first = true; out.sb(); zend_hash_internal_pointer_reset_ex(arr, &pos); while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE) { zval** val = reinterpret_cast(data); // // Get the key (which can be a long or a string). // char* keyStr; uint keyLen; ulong keyNum; int hashKeyType = zend_hash_get_current_key_ex(arr, &keyStr, &keyLen, &keyNum, 0, &pos); if(first) { first = false; } else { out << nl; } out << nl << "key = "; if(hashKeyType == HASH_KEY_IS_LONG) { out << keyNum; } else { out << keyStr; } out << nl << "value = "; valueType->print(*val, out, history TSRMLS_CC); zend_hash_move_forward_ex(arr, &pos); } out.eb(); } } IcePHP::DictionaryInfo::KeyCallback::KeyCallback() : key(0) { } IcePHP::DictionaryInfo::KeyCallback::~KeyCallback() { if(key) { zval_ptr_dtor(&key); } } void IcePHP::DictionaryInfo::KeyCallback::unmarshaled(zval* zv, zval*, void* TSRMLS_DC) { if(key) { zval_ptr_dtor(&key); } key = zv; Z_ADDREF_P(key); } IcePHP::DictionaryInfo::ValueCallback::ValueCallback(zval* k TSRMLS_DC) : key(k) { Z_ADDREF_P(key); } IcePHP::DictionaryInfo::ValueCallback::~ValueCallback() { zval_ptr_dtor(&key); } void IcePHP::DictionaryInfo::ValueCallback::unmarshaled(zval* zv, zval* target, void* TSRMLS_DC) { assert(Z_TYPE_P(target) == IS_ARRAY); switch(Z_TYPE_P(key)) { case IS_LONG: add_index_zval(target, Z_LVAL_P(key), zv); break; case IS_BOOL: add_index_zval(target, Z_BVAL_P(key) ? 1 : 0, zv); break; case IS_STRING: add_assoc_zval_ex(target, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, zv); break; default: assert(false); return; } Z_ADDREF_P(zv); } void IcePHP::DictionaryInfo::destroy() { if(keyType) { keyType->destroy(); keyType = 0; } if(valueType) { valueType->destroy(); valueType = 0; } } // // ClassInfo implementation. // IcePHP::ClassInfo::ClassInfo(const string& ident TSRMLS_DC) : id(ident), compactId(-1), preserve(false), interface(false), zce(0), defined(false) { } void IcePHP::ClassInfo::define(const string& n, Ice::Int compact, bool pres, bool intf, zval* b, zval* m TSRMLS_DC) { const_cast(name) = n; const_cast(compactId) = static_cast(compact); const_cast(preserve) = pres; const_cast(interface) = intf; if(b) { TypeInfoPtr p = Wrapper::value(b TSRMLS_CC); const_cast(base) = ClassInfoPtr::dynamicCast(p); assert(base); } if(m) { convertDataMembers(m, const_cast(members), const_cast(optionalMembers), true TSRMLS_CC); } const_cast(defined) = true; #ifdef ICEPHP_USE_NAMESPACES const string valueClass = "Ice\\Value"; #else const string valueClass = "Ice_Value"; #endif const_cast(zce) = nameToClass(interface ? valueClass : name TSRMLS_CC); // LocalObject and interfaces does not have a native PHP equivalent. assert(zce || id == "::Ice::LocalObject" || interface); } string IcePHP::ClassInfo::getId() const { return id; } bool IcePHP::ClassInfo::validate(zval* val, bool TSRMLS_DC) { if(Z_TYPE_P(val) == IS_OBJECT) { return checkClass(Z_OBJCE_P(val), const_cast(zce)); } return Z_TYPE_P(val) == IS_NULL; } bool IcePHP::ClassInfo::variableLength() const { return true; } int IcePHP::ClassInfo::wireSize() const { return 1; } Ice::OptionalFormat IcePHP::ClassInfo::optionalFormat() const { return Ice::OptionalFormatClass; } bool IcePHP::ClassInfo::usesClasses() const { return true; } void IcePHP::ClassInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool TSRMLS_DC) { if(!defined) { runtimeError("class or interface %s is declared but not defined" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } if(Z_TYPE_P(zv) == IS_NULL) { Ice::ObjectPtr nil; os->write(nil); return; } assert(Z_TYPE_P(zv) == IS_OBJECT); // validate() should have caught this. assert(checkClass(Z_OBJCE_P(zv), const_cast(zce))); // validate() should have caught this. // // Ice::ObjectWriter is a subclass of Ice::Object that wraps a PHP object for marshaling. // It is possible that this PHP object has already been marshaled, therefore we first must // check the object map to see if this object is present. If so, we use the existing ObjectWriter, // otherwise we create a new one. The key of the map is the object's handle. // Ice::ObjectPtr writer; assert(objectMap); ObjectMap::iterator q = objectMap->find(Z_OBJ_HANDLE_P(zv)); if(q == objectMap->end()) { writer = new ObjectWriter(zv, objectMap, this TSRMLS_CC); objectMap->insert(ObjectMap::value_type(Z_OBJ_HANDLE_P(zv), writer)); } else { writer = q->second; } // // Give the writer to the stream. The stream will eventually call write() on it. // os->write(writer); } namespace { void patchObject(void* addr, const Ice::ObjectPtr& v) { ReadObjectCallback* cb = static_cast(addr); assert(cb); cb->invoke(v); } } void IcePHP::ClassInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb, const CommunicatorInfoPtr& comm, zval* target, void* closure, bool TSRMLS_DC) { if(!defined) { runtimeError("class or interface %s is declared but not defined" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } // // This callback is notified when the Slice value is actually read. The StreamUtil object // attached to the stream keeps a reference to the callback object to ensure it lives // long enough. // ReadObjectCallbackPtr rocb = new ReadObjectCallback(this, cb, target, closure TSRMLS_CC); StreamUtil* util = reinterpret_cast(is->getClosure()); assert(util); util->add(rocb); is->read(patchObject, rocb.get()); } void IcePHP::ClassInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history TSRMLS_DC) { if(!validate(zv, false TSRMLS_CC)) { out << ""; return; } if(Z_TYPE_P(zv) == IS_NULL) { out << ""; } else { map::iterator q = history->objects.find(Z_OBJ_HANDLE_P(zv)); if(q != history->objects.end()) { out << "second << ">"; } else { out << "object #" << history->index << " (" << id << ')'; history->objects.insert(map::value_type(Z_OBJ_HANDLE_P(zv), history->index)); ++history->index; out.sb(); printMembers(zv, out, history TSRMLS_CC); out.eb(); } } } void IcePHP::ClassInfo::destroy() { const_cast(base) = 0; if(!members.empty()) { DataMemberList ml = members; const_cast(members).clear(); for(DataMemberList::iterator p = ml.begin(); p != ml.end(); ++p) { (*p)->type->destroy(); } } } void IcePHP::ClassInfo::printMembers(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history TSRMLS_DC) { if(base) { base->printMembers(zv, out, history TSRMLS_CC); } DataMemberList::const_iterator q; for(q = members.begin(); q != members.end(); ++q) { DataMemberPtr member = *q; out << nl << member->name << " = "; void* data; if(zend_hash_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size() + 1, &data) == SUCCESS) { zval** val = reinterpret_cast(data); member->type->print(*val, out, history TSRMLS_CC); } else { out << ""; } } for(q = optionalMembers.begin(); q != optionalMembers.end(); ++q) { DataMemberPtr member = *q; out << nl << member->name << " = "; void* data; if(zend_hash_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size() + 1, &data) == SUCCESS) { zval** val = reinterpret_cast(data); if(isUnset(*val TSRMLS_CC)) { out << ""; } else { member->type->print(*val, out, history TSRMLS_CC); } } else { out << ""; } } } bool IcePHP::ClassInfo::isA(const string& typeId) const { if(id == typeId) { return true; } return base && base->isA(typeId); } // // ProxyInfo implementation. // IcePHP::ProxyInfo::ProxyInfo(const string& ident TSRMLS_DC) : id(ident), defined(false) { } void IcePHP::ProxyInfo::define(zval* b, zval* i TSRMLS_DC) { if(b) { TypeInfoPtr p = Wrapper::value(b TSRMLS_CC); const_cast(base) = ProxyInfoPtr::dynamicCast(p); assert(base); } if(i) { HashTable* interfacesArray = Z_ARRVAL_P(i); void* data; HashPosition pos; zend_hash_internal_pointer_reset_ex(interfacesArray, &pos); while(zend_hash_get_current_data_ex(interfacesArray, &data, &pos) != FAILURE) { zval** interfaceType = reinterpret_cast(data); TypeInfoPtr t = Wrapper::value(*interfaceType TSRMLS_CC); ProxyInfoPtr c = ProxyInfoPtr::dynamicCast(t); assert(c); const_cast(interfaces).push_back(c); zend_hash_move_forward_ex(interfacesArray, &pos); } } const_cast(defined) = true; } string IcePHP::ProxyInfo::getId() const { return id; } bool IcePHP::ProxyInfo::validate(zval* zv, bool throwException TSRMLS_DC) { if(Z_TYPE_P(zv) != IS_NULL) { if(Z_TYPE_P(zv) != IS_OBJECT || (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) != proxyClassEntry)) { if(throwException) { string s = zendTypeToString(Z_TYPE_P(zv)); invalidArgument("expected proxy value or null but received %s" TSRMLS_CC, s.c_str()); } return false; } } return true; } bool IcePHP::ProxyInfo::variableLength() const { return true; } int IcePHP::ProxyInfo::wireSize() const { return 1; } Ice::OptionalFormat IcePHP::ProxyInfo::optionalFormat() const { return Ice::OptionalFormatFSize; } void IcePHP::ProxyInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap*, bool optional TSRMLS_DC) { Ice::OutputStream::size_type sizePos = 0; if(optional) { sizePos = os->startSize(); } if(Z_TYPE_P(zv) == IS_NULL) { os->write(Ice::ObjectPrx()); } else { assert(Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == proxyClassEntry); // validate() should have caught this. Ice::ObjectPrx proxy; ProxyInfoPtr info; if(!fetchProxy(zv, proxy, info TSRMLS_CC)) { throw AbortMarshaling(); } if(!info->isA(id)) { invalidArgument("proxy is not narrowed to %s" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } os->write(proxy); } if(optional) { os->endSize(sizePos); } } void IcePHP::ProxyInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb, const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional TSRMLS_DC) { zval* zv; MAKE_STD_ZVAL(zv); AutoDestroy destroy(zv); if(optional) { is->skip(4); } Ice::ObjectPrx proxy; is->read(proxy); if(!proxy) { ZVAL_NULL(zv); cb->unmarshaled(zv, target, closure TSRMLS_CC); return; } if(!defined) { runtimeError("proxy %s is declared but not defined" TSRMLS_CC, id.c_str()); throw AbortMarshaling(); } if(!createProxy(zv, proxy, this, comm TSRMLS_CC)) { throw AbortMarshaling(); } cb->unmarshaled(zv, target, closure TSRMLS_CC); } void IcePHP::ProxyInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* TSRMLS_DC) { if(!validate(zv, false TSRMLS_CC)) { out << ""; return; } if(Z_TYPE_P(zv) == IS_NULL) { out << ""; } else { Ice::ObjectPrx proxy; ProxyInfoPtr info; if(!fetchProxy(zv, proxy, info TSRMLS_CC)) { return; } out << proxy->ice_toString(); } } void IcePHP::ProxyInfo::destroy() { const_cast(base) = 0; const_cast(interfaces).clear(); } bool IcePHP::ProxyInfo::isA(const string& typeId) const { if(id == typeId) { return true; } if(base && base->isA(typeId)) { return true; } for(ProxyInfoList::const_iterator p = interfaces.begin(); p != interfaces.end(); ++p) { if((*p)->isA(typeId)) { return true; } } return false; } void IcePHP::ProxyInfo::addOperation(const string& name, const OperationPtr& op) { operations.insert(OperationMap::value_type(Slice::PHP::fixIdent(name), op)); } IcePHP::OperationPtr IcePHP::ProxyInfo::getOperation(const string& name) const { OperationPtr op; OperationMap::const_iterator p = operations.find(name); if(p != operations.end()) { op = p->second; } if(!op && base) { op = base->getOperation(name); } if(!op && !interfaces.empty()) { for(ProxyInfoList::const_iterator q = interfaces.begin(); q != interfaces.end() && !op; ++q) { op = (*q)->getOperation(name); } } return op; } // // ObjectWriter implementation. // IcePHP::ObjectWriter::ObjectWriter(zval* object, ObjectMap* objectMap, const ClassInfoPtr& formal TSRMLS_DC) : _object(object), _map(objectMap), _formal(formal) { #ifdef ZTS this->TSRMLS_C = TSRMLS_C; #endif Z_ADDREF_P(_object); if(!_formal || !_formal->interface) { // // For no interface types We need to determine the most-derived Slice type supported by this object. // This is typically a Slice class, but it can also be an interface. // // The caller may have provided a ClassInfo representing the formal type, in // which case we ensure that the actual type is compatible with the formal type. // _info = getClassInfoByClass(Z_OBJCE_P(object), formal ? const_cast(formal->zce) : 0 TSRMLS_CC); assert(_info); } } IcePHP::ObjectWriter::~ObjectWriter() { zval_ptr_dtor(&_object); } void IcePHP::ObjectWriter::ice_preMarshal() { string name = "ice_premarshal"; // Must be lowercase. if(zend_hash_exists(&Z_OBJCE_P(_object)->function_table, STRCAST(name.c_str()), static_cast(name.size() + 1))) { if(!invokeMethod(_object, name TSRMLS_CC)) { throw AbortMarshaling(); } } } void IcePHP::ObjectWriter::_iceWrite(Ice::OutputStream* os) const { Ice::SlicedDataPtr slicedData; if(_info && _info->preserve) { // // Retrieve the SlicedData object that we stored as a hidden member of the PHP object. // slicedData = StreamUtil::getSlicedDataMember(_object, const_cast(_map) TSRMLS_CC); } os->startValue(slicedData); if(_formal && _formal->interface) { // // For an interface by value we just marshal the Ice type id // of the object in its own slice. // zval* ret = 0; zend_try { const char* func = "ice_id"; zval* obj = const_cast(_object); zend_call_method(&obj, 0, 0, const_cast(func), static_cast(strlen(func)), &ret, 0, 0, 0 TSRMLS_CC); } zend_catch { ret = 0; } zend_end_try(); // // Bail out if an exception has already been thrown. // if(!ret || EG(exception)) { throw AbortMarshaling(); } AutoDestroy destroy(ret); if(Z_TYPE_P(ret) != IS_STRING) { throw AbortMarshaling(); } string id(Z_STRVAL_P(ret), Z_STRLEN_P(ret)); os->startSlice(id, -1, true); os->endSlice(); } else { if(_info->id != "::Ice::UnknownSlicedValue") { ClassInfoPtr info = _info; while(info && info->id != Ice::Object::ice_staticId()) { assert(info->base); // All classes have the Ice::Object base type. const bool lastSlice = info->base->id == Ice::Object::ice_staticId(); os->startSlice(info->id, info->compactId, lastSlice); writeMembers(os, info->members); writeMembers(os, info->optionalMembers); // The optional members have already been sorted by tag. os->endSlice(); info = info->base; } } } os->endValue(); } void IcePHP::ObjectWriter::_iceRead(Ice::InputStream*) { assert(false); } void IcePHP::ObjectWriter::writeMembers(Ice::OutputStream* os, const DataMemberList& members) const { for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q) { DataMemberPtr member = *q; void* data; if(zend_hash_find(Z_OBJPROP_P(_object), STRCAST(member->name.c_str()), static_cast(member->name.size() + 1), &data) == FAILURE) { runtimeError("member `%s' of %s is not defined" TSRMLS_CC, member->name.c_str(), _info->id.c_str()); throw AbortMarshaling(); } zval** val = reinterpret_cast(data); if(member->optional && (isUnset(*val TSRMLS_CC) || !os->writeOptional(member->tag, member->type->optionalFormat()))) { continue; } if(!member->type->validate(*val, false TSRMLS_CC)) { invalidArgument("invalid value for %s member `%s'" TSRMLS_CC, _info->id.c_str(), member->name.c_str()); throw AbortMarshaling(); } member->type->marshal(*val, os, _map, member->optional TSRMLS_CC); } } // // ObjectReader implementation. // IcePHP::ObjectReader::ObjectReader(zval* object, const ClassInfoPtr& info, const CommunicatorInfoPtr& comm TSRMLS_DC) : _object(object), _info(info), _communicator(comm) { #ifdef ZTS this->TSRMLS_C = TSRMLS_C; #endif Z_ADDREF_P(_object); } IcePHP::ObjectReader::~ObjectReader() { zval_ptr_dtor(&_object); } void IcePHP::ObjectReader::ice_postUnmarshal() { string name = "ice_postunmarshal"; // Must be lowercase. if(zend_hash_exists(&Z_OBJCE_P(_object)->function_table, STRCAST(name.c_str()), static_cast(name.size() + 1))) { if(!invokeMethod(_object, name TSRMLS_CC)) { throw AbortMarshaling(); } } } void IcePHP::ObjectReader::_iceWrite(Ice::OutputStream*) const { assert(false); } void IcePHP::ObjectReader::_iceRead(Ice::InputStream* is) { is->startValue(); const bool unknown = _info->id == "::Ice::UnknownSlicedValue"; // // Unmarshal the slices of a user-defined class. // if(!unknown) { ClassInfoPtr info = _info; while(info && info->id != Ice::Object::ice_staticId()) { is->startSlice(); DataMemberList::const_iterator p; for(p = info->members.begin(); p != info->members.end(); ++p) { DataMemberPtr member = *p; member->type->unmarshal(is, member, _communicator, _object, 0, false TSRMLS_CC); } // // The optional members have already been sorted by tag. // for(p = info->optionalMembers.begin(); p != info->optionalMembers.end(); ++p) { DataMemberPtr member = *p; if(is->readOptional(member->tag, member->type->optionalFormat())) { member->type->unmarshal(is, member, _communicator, _object, 0, true TSRMLS_CC); } else { zval* zv; MAKE_STD_ZVAL(zv); AutoDestroy destroy(zv); assignUnset(zv TSRMLS_CC); member->setMember(_object, zv TSRMLS_CC); } } is->endSlice(); info = info->base; } } _slicedData = is->endValue(_info->preserve); if(_slicedData) { StreamUtil* util = reinterpret_cast(is->getClosure()); assert(util); util->add(this); // // Define the "unknownTypeId" member for an instance of UnknownSlicedObject. // if(unknown) { assert(!_slicedData->slices.empty()); const string typeId = _slicedData->slices[0]->typeId; zval* zv; MAKE_STD_ZVAL(zv); AutoDestroy typeIdDestroyer(zv); ZVAL_STRINGL(zv, STRCAST(typeId.c_str()), static_cast(typeId.size()), 1); add_property_zval(_object, STRCAST("unknownTypeId"), zv); } } } ClassInfoPtr IcePHP::ObjectReader::getInfo() const { return _info; } zval* IcePHP::ObjectReader::getObject() const { return _object; } Ice::SlicedDataPtr IcePHP::ObjectReader::getSlicedData() const { return _slicedData; } // // ReadObjectCallback implementation. // IcePHP::ReadObjectCallback::ReadObjectCallback(const ClassInfoPtr& info, const UnmarshalCallbackPtr& cb, zval* target, void* closure TSRMLS_DC) : _info(info), _cb(cb), _target(target), _closure(closure) { #ifdef ZTS this->TSRMLS_C = TSRMLS_C; #endif // // Keep a reference to the target to prevent it from being deallocated until the // Ice object is actually read. // if(_target) { Z_ADDREF_P(_target); } } IcePHP::ReadObjectCallback::~ReadObjectCallback() { if(_target) { zval_ptr_dtor(&_target); } } void IcePHP::ReadObjectCallback::invoke(const Ice::ObjectPtr& p) { if(p) { ObjectReaderPtr reader = ObjectReaderPtr::dynamicCast(p); assert(reader); // // Verify that the unmarshaled object is compatible with the formal type. // zval* obj = reader->getObject(); if(!_info->interface && !reader->getInfo()->isA(_info->id)) { Ice::UnexpectedObjectException ex(__FILE__, __LINE__); ex.reason = "unmarshaled object is not an instance of " + _info->id; ex.type = reader->getInfo()->id; ex.expectedType = _info->id; throw ex; } _cb->unmarshaled(obj, _target, _closure TSRMLS_CC); } else { zval* zv; MAKE_STD_ZVAL(zv); AutoDestroy destroy(zv); ZVAL_NULL(zv); _cb->unmarshaled(zv, _target, _closure TSRMLS_CC); } } // // ExceptionInfo implementation. // zval* IcePHP::ExceptionInfo::unmarshal(Ice::InputStream* is, const CommunicatorInfoPtr& comm TSRMLS_DC) { zval* zv; MAKE_STD_ZVAL(zv); AutoDestroy destroy(zv); if(object_init_ex(zv, zce) != SUCCESS) { runtimeError("unable to initialize object of type %s" TSRMLS_CC, zce->name); throw AbortMarshaling(); } // // NOTE: The type id for the first slice has already been read. // ExceptionInfoPtr info = this; while(info) { is->startSlice(); DataMemberList::iterator q; for(q = info->members.begin(); q != info->members.end(); ++q) { DataMemberPtr member = *q; member->type->unmarshal(is, member, comm, zv, 0, false TSRMLS_CC); } // // The optional members have already been sorted by tag. // for(q = info->optionalMembers.begin(); q != info->optionalMembers.end(); ++q) { DataMemberPtr member = *q; if(is->readOptional(member->tag, member->type->optionalFormat())) { member->type->unmarshal(is, member, comm, zv, 0, true TSRMLS_CC); } else { zval* un; MAKE_STD_ZVAL(un); AutoDestroy destroy2(un); assignUnset(un TSRMLS_CC); member->setMember(zv, un TSRMLS_CC); } } is->endSlice(); info = info->base; } return destroy.release(); } void IcePHP::ExceptionInfo::print(zval* zv, IceUtilInternal::Output& out TSRMLS_DC) { out << "exception " << id; out.sb(); if(Z_TYPE_P(zv) != IS_OBJECT) { string s = zendTypeToString(Z_TYPE_P(zv)); out << nl << "expected exception value of type " << zce->name << " but received " << s; out.eb(); return; } // // Compare class entries. // zend_class_entry* ce = Z_OBJCE_P(zv); if(ce != zce) { out << nl << "expected exception value of type " << zce->name << " but received " << ce->name; out.eb(); return; } PrintObjectHistory history; history.index = 0; out << "exception " << id; out.sb(); printMembers(zv, out, &history TSRMLS_CC); out.eb(); } void IcePHP::ExceptionInfo::printMembers(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history TSRMLS_DC) { if(base) { base->printMembers(zv, out, history TSRMLS_CC); } DataMemberList::iterator q; for(q = members.begin(); q != members.end(); ++q) { DataMemberPtr member = *q; out << nl << member->name << " = "; void* data; if(zend_hash_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), static_cast(member->name.size() + 1), &data) == SUCCESS) { zval** val = reinterpret_cast(data); member->type->print(*val, out, history TSRMLS_CC); } else { out << ""; } } for(q = optionalMembers.begin(); q != optionalMembers.end(); ++q) { DataMemberPtr member = *q; out << nl << member->name << " = "; void* data; if(zend_hash_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), static_cast(member->name.size() + 1), &data) == SUCCESS) { zval** val = reinterpret_cast(data); if(isUnset(*val TSRMLS_CC)) { out << ""; } else { member->type->print(*val, out, history TSRMLS_CC); } } else { out << ""; } } } bool IcePHP::ExceptionInfo::isA(const string& typeId) const { if(id == typeId) { return true; } if(base && base->isA(typeId)) { return true; } return false; } // // ExceptionReader implementation. // IcePHP::ExceptionReader::ExceptionReader(const CommunicatorInfoPtr& communicatorInfo, const ExceptionInfoPtr& info TSRMLS_DC) : _communicatorInfo(communicatorInfo), _info(info) { #ifdef ZTS this->TSRMLS_C = TSRMLS_C; #endif } IcePHP::ExceptionReader::~ExceptionReader() throw() { } string IcePHP::ExceptionReader::ice_id() const { return _info->id; } IcePHP::ExceptionReader* IcePHP::ExceptionReader::ice_clone() const { assert(false); return 0; } void IcePHP::ExceptionReader::ice_throw() const { throw *this; } void IcePHP::ExceptionReader::_write(Ice::OutputStream*) const { assert(false); } void IcePHP::ExceptionReader::_read(Ice::InputStream* is) { is->startException(); const_cast(_ex) = _info->unmarshal(is, _communicatorInfo TSRMLS_CC); const_cast(_slicedData) = is->endException(_info->preserve); } bool IcePHP::ExceptionReader::_usesClasses() const { return _info->usesClasses; } ExceptionInfoPtr IcePHP::ExceptionReader::getInfo() const { return _info; } zval* IcePHP::ExceptionReader::getException() const { return _ex; } Ice::SlicedDataPtr IcePHP::ExceptionReader::getSlicedData() const { return _slicedData; } // // IdResolver // IcePHP::IdResolver::IdResolver(TSRMLS_D) { #ifdef ZTS this->TSRMLS_C = TSRMLS_C; #endif } string IcePHP::IdResolver::resolve(Ice::Int id) const { CompactIdMap* m = reinterpret_cast(ICE_G(compactIdToClassInfoMap)); if(m) { CompactIdMap::iterator p = m->find(id); if(p != m->end()) { return p->second->id; } } return string(); } #ifdef _WIN32 extern "C" #endif static zend_object_value handleTypeInfoAlloc(zend_class_entry* ce TSRMLS_DC) { zend_object_value result; Wrapper* obj = Wrapper::create(ce TSRMLS_CC); assert(obj); result.handle = zend_objects_store_put(obj, 0, reinterpret_cast(handleTypeInfoFreeStorage), 0 TSRMLS_CC); result.handlers = &_typeInfoHandlers; return result; } #ifdef _WIN32 extern "C" #endif static void handleTypeInfoFreeStorage(void* p TSRMLS_DC) { Wrapper* obj = static_cast*>(p); delete obj->ptr; zend_object_std_dtor(static_cast(p) TSRMLS_CC); efree(p); } static bool createTypeInfo(zval* zv, const TypeInfoPtr& p TSRMLS_DC) { if(object_init_ex(zv, typeInfoClassEntry) != SUCCESS) { runtimeError("unable to initialize type" TSRMLS_CC); return false; } Wrapper* ze = static_cast*>(zend_object_store_get_object(zv TSRMLS_CC)); assert(!ze->ptr); ze->ptr = new TypeInfoPtr(p); return true; } ZEND_FUNCTION(IcePHP_defineEnum) { char* id; int idLen; zval* enumerators; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("sa"), &id, &idLen, &enumerators) == FAILURE) { return; } EnumInfoPtr type = new EnumInfo(id, enumerators TSRMLS_CC); if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_defineStruct) { char* id; int idLen; char* name; int nameLen; zval* members; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("ssa"), &id, &idLen, &name, &nameLen, &members) == FAILURE) { return; } StructInfoPtr type = new StructInfo(id, name, members TSRMLS_CC); if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_defineSequence) { char* id; int idLen; zval* element; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("so"), &id, &idLen, &element) == FAILURE) { return; } SequenceInfoPtr type = new SequenceInfo(id, element TSRMLS_CC); if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_defineDictionary) { char* id; int idLen; zval* key; zval* value; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("soo"), &id, &idLen, &key, &value) == FAILURE) { return; } DictionaryInfoPtr type = new DictionaryInfo(id, key, value TSRMLS_CC); if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_declareProxy) { char* id; int idLen; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("s"), &id, &idLen) == FAILURE) { return; } ProxyInfoPtr type = getProxyInfo(id TSRMLS_CC); if(!type) { type = new ProxyInfo(id TSRMLS_CC); addProxyInfo(type TSRMLS_CC); } if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_defineProxy) { char* id; size_t idLen; zval* base; zval* interfaces; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("so!a!"), &id, &idLen, &base, &interfaces) == FAILURE) { return; } ProxyInfoPtr type = getProxyInfo(id TSRMLS_CC); if(!type) { type = new ProxyInfo(id TSRMLS_CC); addProxyInfo(type TSRMLS_CC); } type->define(base, interfaces TSRMLS_CC); if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_declareClass) { char* id; int idLen; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("s"), &id, &idLen) == FAILURE) { return; } ClassInfoPtr type = getClassInfoById(id TSRMLS_CC); if(!type) { type = new ClassInfo(id TSRMLS_CC); addClassInfoById(type TSRMLS_CC); } if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_defineClass) { char* id; int idLen; char* name; int nameLen; long compactId; zend_bool preserve; zend_bool interface; zval* base; zval* members; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("sslbbo!a!"), &id, &idLen, &name, &nameLen, &compactId, &preserve, &interface, &base, &members) == FAILURE) { return; } ClassInfoPtr type = getClassInfoById(id TSRMLS_CC); if(!type) { type = new ClassInfo(id TSRMLS_CC); addClassInfoById(type TSRMLS_CC); } type->define(name, static_cast(compactId), preserve ? true : false, interface ? true : false, base, members TSRMLS_CC); if(!interface) { addClassInfoByName(type TSRMLS_CC); } if(type->compactId != -1) { CompactIdMap* m = reinterpret_cast(ICE_G(compactIdToClassInfoMap)); if(!m) { m = new CompactIdMap; ICE_G(compactIdToClassInfoMap) = m; } m->insert(CompactIdMap::value_type(type->compactId, type)); } if(!createTypeInfo(return_value, type TSRMLS_CC)) { RETURN_NULL(); } } #ifdef _WIN32 extern "C" #endif static zend_object_value handleExceptionInfoAlloc(zend_class_entry* ce TSRMLS_DC) { zend_object_value result; Wrapper* obj = Wrapper::create(ce TSRMLS_CC); assert(obj); result.handle = zend_objects_store_put(obj, 0, reinterpret_cast(handleExceptionInfoFreeStorage), 0 TSRMLS_CC); result.handlers = &_exceptionInfoHandlers; return result; } #ifdef _WIN32 extern "C" #endif static void handleExceptionInfoFreeStorage(void* p TSRMLS_DC) { Wrapper* obj = static_cast*>(p); delete obj->ptr; zend_object_std_dtor(static_cast(p) TSRMLS_CC); efree(p); } static bool createExceptionInfo(zval* zv, const ExceptionInfoPtr& p TSRMLS_DC) { if(object_init_ex(zv, exceptionInfoClassEntry) != SUCCESS) { runtimeError("unable to initialize exception info" TSRMLS_CC); return false; } Wrapper* ze = static_cast*>(zend_object_store_get_object(zv TSRMLS_CC)); assert(!ze->ptr); ze->ptr = new ExceptionInfoPtr(p); return true; } ZEND_FUNCTION(IcePHP_defineException) { char* id; int idLen; char* name; int nameLen; zend_bool preserve; zval* base; zval* members; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("ssbo!a!"), &id, &idLen, &name, &nameLen, &preserve, &base, &members) == FAILURE) { return; } ExceptionInfoPtr ex = new ExceptionInfo(); ex->id = id; ex->name = name; ex->preserve = preserve ? true : false; if(base) { ex->base = Wrapper::value(base TSRMLS_CC); } if(members) { convertDataMembers(members, ex->members, ex->optionalMembers, true TSRMLS_CC); } ex->usesClasses = false; // // Only examine the required members to see if any use classes. // for(DataMemberList::iterator p = ex->members.begin(); p != ex->members.end(); ++p) { if(!ex->usesClasses) { ex->usesClasses = (*p)->type->usesClasses(); } } ex->zce = nameToClass(ex->name TSRMLS_CC); assert(!getExceptionInfo(ex->id TSRMLS_CC)); ExceptionInfoMap* m; if(ICE_G(exceptionInfoMap)) { m = reinterpret_cast(ICE_G(exceptionInfoMap)); } else { m = new ExceptionInfoMap; ICE_G(exceptionInfoMap) = m; } m->insert(ExceptionInfoMap::value_type(ex->id, ex)); if(!createExceptionInfo(return_value, ex TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(IcePHP_stringify) { if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; } zval* v; zval* t; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("zz"), &v, &t) == FAILURE) { return; } TypeInfoPtr type = Wrapper::value(t TSRMLS_CC); assert(type); ostringstream ostr; IceUtilInternal::Output out(ostr); PrintObjectHistory history; history.index = 0; type->print(v, out, &history TSRMLS_CC); string str = ostr.str(); RETURN_STRINGL(STRCAST(str.c_str()), static_cast(str.length()), 1); } ZEND_FUNCTION(IcePHP_stringifyException) { if(ZEND_NUM_ARGS() != 2) { WRONG_PARAM_COUNT; } zval* v; zval* t; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("oo"), &v, &t) == FAILURE) { return; } ExceptionInfoPtr ex = Wrapper::value(t TSRMLS_CC); assert(ex); ostringstream ostr; IceUtilInternal::Output out(ostr); ex->print(v, out TSRMLS_CC); string str = ostr.str(); RETURN_STRINGL(STRCAST(str.c_str()), static_cast(str.length()), 1); } // // Necessary to suppress warnings from zend_function_entry in php-5.2. // #if defined(__GNUC__) # pragma GCC diagnostic ignored "-Wwrite-strings" #endif // // Predefined methods for IcePHP_TypeInfo. // static zend_function_entry _typeInfoMethods[] = { {0, 0, 0} }; // // Predefined methods for IcePHP_ExceptionInfo. // static zend_function_entry _exceptionInfoMethods[] = { {0, 0, 0} }; bool IcePHP::isUnset(zval* zv TSRMLS_DC) { if(Z_TYPE_P(zv) == IS_STRING) { return _unsetGUID == string(Z_STRVAL_P(zv), Z_STRLEN_P(zv)); } return false; } void IcePHP::assignUnset(zval* zv TSRMLS_DC) { *zv = *ICE_G(unset); zval_copy_ctor(zv); } bool IcePHP::typesInit(INIT_FUNC_ARGS) { zend_class_entry ce; // // Register the IcePHP_TypeInfo class. // INIT_CLASS_ENTRY(ce, "IcePHP_TypeInfo", _typeInfoMethods); ce.create_object = handleTypeInfoAlloc; typeInfoClassEntry = zend_register_internal_class(&ce TSRMLS_CC); memcpy(&_typeInfoHandlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); // // Register the IcePHP_ExceptionInfo class. // INIT_CLASS_ENTRY(ce, "IcePHP_ExceptionInfo", _exceptionInfoMethods); ce.create_object = handleExceptionInfoAlloc; exceptionInfoClassEntry = zend_register_internal_class(&ce TSRMLS_CC); memcpy(&_exceptionInfoHandlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); #ifdef ICEPHP_USE_NAMESPACES REGISTER_NS_STRING_CONSTANT("Ice", "None", const_cast(_unsetGUID.c_str()), CONST_CS|CONST_PERSISTENT); #else REGISTER_STRING_CONSTANT("Ice_Unset", const_cast(_unsetGUID.c_str()), CONST_CS|CONST_PERSISTENT); #endif return true; } // // enable warning again // #if defined(__GNUC__) # pragma GCC diagnostic error "-Wwrite-strings" #endif bool IcePHP::typesRequestInit(TSRMLS_D) { // // Create the global variables for the primitive types. // for(int i = static_cast(PrimitiveInfo::KindBool); i <= static_cast(PrimitiveInfo::KindString); ++i) { PrimitiveInfoPtr type = new PrimitiveInfo(); type->kind = static_cast(i); zval* zv; MAKE_STD_ZVAL(zv); if(!createTypeInfo(zv, type TSRMLS_CC)) { zval_ptr_dtor(&zv); return false; } string name = "IcePHP__t_" + type->getId(); ZEND_SET_SYMBOL(&EG(symbol_table), const_cast(name.c_str()), zv); } ICE_G(idToClassInfoMap) = 0; ICE_G(nameToClassInfoMap) = 0; ICE_G(proxyInfoMap) = 0; ICE_G(exceptionInfoMap) = 0; zval* unset; MAKE_STD_ZVAL(unset); ZVAL_STRINGL(unset, STRCAST(_unsetGUID.c_str()), static_cast(_unsetGUID.length()), 1); ICE_G(unset) = unset; return true; } bool IcePHP::typesRequestShutdown(TSRMLS_D) { if(ICE_G(proxyInfoMap)) { ProxyInfoMap* m = static_cast(ICE_G(proxyInfoMap)); for(ProxyInfoMap::iterator p = m->begin(); p != m->end(); ++p) { p->second->destroy(); } delete m; } if(ICE_G(idToClassInfoMap)) { ClassInfoMap* m = static_cast(ICE_G(idToClassInfoMap)); for(ClassInfoMap::iterator p = m->begin(); p != m->end(); ++p) { p->second->destroy(); } delete m; } if(ICE_G(nameToClassInfoMap)) { ClassInfoMap* m = static_cast(ICE_G(nameToClassInfoMap)); delete m; } delete static_cast(ICE_G(exceptionInfoMap)); zval_ptr_dtor(&ICE_G(unset)); return true; }