// ********************************************************************** // // Copyright (c) 2003-2018 ZeroC, Inc. All rights reserved. // // This copy of Ice is licensed to you under the terms described in the // ICE_LICENSE file included in this distribution. // // ********************************************************************** #include #include #include #include #include #if defined(__GNUC__) && !defined(__clang__) // False warning with older GCC # pragma GCC diagnostic ignored "-Wclobbered" #endif using namespace std; using namespace IcePHP; using namespace Slice::PHP; namespace { bool getMember(zval* zv, const string& name, zval** member, int type, bool required TSRMLS_DC) { *member = 0; void* data = 0; if(zend_hash_find(Z_OBJPROP_P(zv), STRCAST(name.c_str()), name.size() + 1, &data) == FAILURE) { if(required) { invalidArgument("object does not contain member `%s'" TSRMLS_CC, name.c_str()); return false; } } if(data) { zval** val = reinterpret_cast(data); if(Z_TYPE_PP(val) != type) { string expected = zendTypeToString(type); string actual = zendTypeToString(Z_TYPE_PP(val)); invalidArgument("expected value of type %s for member `%s' but received %s" TSRMLS_CC, expected.c_str(), name.c_str(), actual.c_str()); return false; } *member = *val; } return true; } void setStringMember(zval* obj, const string& name, const string& val TSRMLS_DC) { zend_class_entry* cls = Z_OBJCE_P(obj); assert(cls); zend_update_property_stringl(cls, obj, const_cast(name.c_str()), static_cast(name.size()), const_cast(val.c_str()), static_cast(val.size()) TSRMLS_CC); } template bool getVersion(zval* zv, T& v, const char* type TSRMLS_DC) { if(Z_TYPE_P(zv) != IS_OBJECT) { invalidArgument("value does not contain an object" TSRMLS_CC); return false; } zend_class_entry* cls = idToClass(type TSRMLS_CC); assert(cls); zend_class_entry* ce = Z_OBJCE_P(zv); if(ce != cls) { invalidArgument("expected an instance of %s" TSRMLS_CC, ce->name); return false; } zval* majorVal; if(!getMember(zv, "major", &majorVal, IS_LONG, true TSRMLS_CC)) { return false; } zval* minorVal; if(!getMember(zv, "minor", &minorVal, IS_LONG, true TSRMLS_CC)) { return false; } long m; m = Z_LVAL_P(majorVal); if(m < 0 || m > 255) { invalidArgument("version major must be a value between 0 and 255" TSRMLS_CC); return false; } v.major = static_cast(m); m = Z_LVAL_P(minorVal); if(m < 0 || m > 255) { invalidArgument("version minor must be a value between 0 and 255" TSRMLS_CC); return false; } v.minor = static_cast(m); return true; } template bool createVersion(zval* zv, const T& version, const char* type TSRMLS_DC) { zend_class_entry* cls = idToClass(type TSRMLS_CC); assert(cls); if(object_init_ex(zv, cls) != SUCCESS) { runtimeError("unable to initialize %s" TSRMLS_CC, cls->name); return false; } zend_update_property_long(cls, zv, const_cast("major"), sizeof("major") - 1, version.major TSRMLS_CC); zend_update_property_long(cls, zv, const_cast("minor"), sizeof("minor") - 1, version.minor TSRMLS_CC); return true; } template bool versionToString(zval* zv, zval* s, const char* type TSRMLS_DC) { T v; if(!getVersion(zv, v, type TSRMLS_CC)) { return false; } try { string str = IceInternal::versionToString(v); ZVAL_STRINGL(s, STRCAST(str.c_str()), static_cast(str.length()), 1); } catch(const IceUtil::Exception& ex) { throwException(ex TSRMLS_CC); return false; } return true; } template bool stringToVersion(const string& s, zval* zv, const char* type TSRMLS_DC) { try { T v = IceInternal::stringToVersion(s); return createVersion(zv, v, type TSRMLS_CC); } catch(const IceUtil::Exception& ex) { throwException(ex TSRMLS_CC); } return false; } char Ice_ProtocolVersion[] = "::Ice::ProtocolVersion"; char Ice_EncodingVersion[] = "::Ice::EncodingVersion"; } #if PHP_VERSION_ID < 50400 #ifdef _WIN32 extern "C" #endif static void dtor_wrapper(void* p) { zval_ptr_dtor(static_cast(p)); } #endif void* IcePHP::createWrapper(zend_class_entry* ce, size_t sz TSRMLS_DC) { zend_object* obj; obj = static_cast(emalloc(sz)); zend_object_std_init(obj, ce TSRMLS_CC); #if PHP_VERSION_ID < 50400 zval* tmp; obj->properties = static_cast(emalloc(sizeof(HashTable))); zend_hash_init(obj->properties, 0, 0, dtor_wrapper, 0); zend_hash_copy(obj->properties, &ce->default_properties, (copy_ctor_func_t)zval_add_ref, &tmp, sizeof(zval*)); #else object_properties_init(obj, ce); #endif return obj; } void* IcePHP::extractWrapper(zval* zv TSRMLS_DC) { if(!zv) { runtimeError("method %s() must be invoked on an object" TSRMLS_CC, get_active_function_name(TSRMLS_C)); return 0; } zend_object* obj = static_cast(zend_object_store_get_object(zv TSRMLS_CC)); if(!obj) { runtimeError("no object found in %s()" TSRMLS_CC, get_active_function_name(TSRMLS_C)); return 0; } return obj; } zend_class_entry* IcePHP::idToClass(const string& id TSRMLS_DC) { #ifdef ICEPHP_USE_NAMESPACES string cls = scopedToName(id, true); #else string cls = scopedToName(id, false); #endif return nameToClass(cls TSRMLS_CC); } zend_class_entry* IcePHP::nameToClass(const string& name TSRMLS_DC) { zend_class_entry** result; if(zend_lookup_class(STRCAST(name.c_str()), static_cast(name.length()), &result TSRMLS_CC) == FAILURE) { return 0; } return *result; } bool IcePHP::createIdentity(zval* zv, const Ice::Identity& id TSRMLS_DC) { zend_class_entry* cls = idToClass("::Ice::Identity" TSRMLS_CC); assert(cls); if(object_init_ex(zv, cls) != SUCCESS) { runtimeError("unable to initialize Ice::Identity" TSRMLS_CC); return false; } setStringMember(zv, "name", id.name TSRMLS_CC); setStringMember(zv, "category", id.category TSRMLS_CC); return true; } bool IcePHP::extractIdentity(zval* zv, Ice::Identity& id TSRMLS_DC) { if(Z_TYPE_P(zv) != IS_OBJECT) { invalidArgument("value does not contain an object" TSRMLS_CC); return false; } zend_class_entry* cls = idToClass("::Ice::Identity" TSRMLS_CC); assert(cls); zend_class_entry* ce = Z_OBJCE_P(zv); if(ce != cls) { invalidArgument("expected an identity but received %s" TSRMLS_CC, ce->name); return false; } // // Category is optional, but name is required. // zval* categoryVal; zval* nameVal; if(!getMember(zv, "category", &categoryVal, IS_STRING, false TSRMLS_CC) || !getMember(zv, "name", &nameVal, IS_STRING, true TSRMLS_CC)) { return false; } id.name = Z_STRVAL_P(nameVal); if(categoryVal) { id.category = Z_STRVAL_P(categoryVal); } else { id.category = ""; } return true; } bool IcePHP::createStringMap(zval* zv, const map& ctx TSRMLS_DC) { array_init(zv); for(map::const_iterator p = ctx.begin(); p != ctx.end(); ++p) { if(add_assoc_stringl_ex(zv, const_cast(p->first.c_str()), p->first.length() + 1, const_cast(p->second.c_str()), static_cast(p->second.length()), 1) == FAILURE) { return false; } } return true; } bool IcePHP::extractStringMap(zval* zv, map& ctx TSRMLS_DC) { if(Z_TYPE_P(zv) != IS_ARRAY) { string s = zendTypeToString(Z_TYPE_P(zv)); invalidArgument("expected an associative array but received %s" TSRMLS_CC, s.c_str()); return false; } HashTable* arr = Z_ARRVAL_P(zv); 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 = reinterpret_cast(data); // // Get the key (which can be a long or a string). // char* keyStr; uint keyLen; ulong keyNum; int keyType = zend_hash_get_current_key_ex(arr, &keyStr, &keyLen, &keyNum, 0, &pos); // // Store the key in a zval, so that we can reuse the PrimitiveMarshaler logic. // if(keyType != HASH_KEY_IS_STRING) { invalidArgument("array key must be a string" TSRMLS_CC); return false; } if(Z_TYPE_PP(val) != IS_STRING) { invalidArgument("array value must be a string" TSRMLS_CC); return false; } ctx[keyStr] = Z_STRVAL_PP(val); zend_hash_move_forward_ex(arr, &pos); } return true; } bool IcePHP::createStringArray(zval* zv, const Ice::StringSeq& seq TSRMLS_DC) { array_init(zv); for(Ice::StringSeq::const_iterator p = seq.begin(); p != seq.end(); ++p) { if(add_next_index_stringl(zv, STRCAST(p->c_str()), static_cast(p->length()), 1) == FAILURE) { return false; } } return true; } bool IcePHP::extractStringArray(zval* zv, Ice::StringSeq& seq TSRMLS_DC) { if(Z_TYPE_P(zv) != IS_ARRAY) { string s = zendTypeToString(Z_TYPE_P(zv)); invalidArgument("expected an array of strings but received %s" TSRMLS_CC, s.c_str()); return false; } HashTable* arr = Z_ARRVAL_P(zv); 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 = reinterpret_cast(data); if(Z_TYPE_PP(val) != IS_STRING) { invalidArgument("array element must be a string" TSRMLS_CC); return false; } string s(Z_STRVAL_PP(val), Z_STRLEN_PP(val)); seq.push_back(s); zend_hash_move_forward_ex(arr, &pos); } return true; } bool IcePHP::createProtocolVersion(zval* zv, const Ice::ProtocolVersion& v TSRMLS_DC) { return createVersion(zv, v, Ice_ProtocolVersion TSRMLS_CC); } bool IcePHP::createEncodingVersion(zval* zv, const Ice::EncodingVersion& v TSRMLS_DC) { return createVersion(zv, v, Ice_EncodingVersion TSRMLS_CC); } bool IcePHP::extractEncodingVersion(zval* zv, Ice::EncodingVersion& v TSRMLS_DC) { return getVersion(zv, v, Ice_EncodingVersion TSRMLS_CC); } static bool convertLocalException(const Ice::LocalException& ex, zval* zex TSRMLS_DC) { zend_class_entry* cls = Z_OBJCE_P(zex); assert(cls); // // Transfer data members from Ice exception to PHP object. // try { ex.ice_throw(); } catch(const Ice::InitializationException& e) { setStringMember(zex, "reason", e.reason TSRMLS_CC); } catch(const Ice::PluginInitializationException& e) { setStringMember(zex, "reason", e.reason TSRMLS_CC); } catch(const Ice::AlreadyRegisteredException& e) { setStringMember(zex, "kindOfObject", e.kindOfObject TSRMLS_CC); setStringMember(zex, "id", e.id TSRMLS_CC); } catch(const Ice::NotRegisteredException& e) { setStringMember(zex, "kindOfObject", e.kindOfObject TSRMLS_CC); setStringMember(zex, "id", e.id TSRMLS_CC); } catch(const Ice::TwowayOnlyException& e) { setStringMember(zex, "operation", e.operation TSRMLS_CC); } catch(const Ice::UnknownException& e) { setStringMember(zex, "unknown", e.unknown TSRMLS_CC); } catch(const Ice::ObjectAdapterDeactivatedException& e) { setStringMember(zex, "name", e.name TSRMLS_CC); } catch(const Ice::ObjectAdapterIdInUseException& e) { setStringMember(zex, "id", e.id TSRMLS_CC); } catch(const Ice::NoEndpointException& e) { setStringMember(zex, "proxy", e.proxy TSRMLS_CC); } catch(const Ice::EndpointParseException& e) { setStringMember(zex, "str", e.str TSRMLS_CC); } catch(const Ice::IdentityParseException& e) { setStringMember(zex, "str", e.str TSRMLS_CC); } catch(const Ice::ProxyParseException& e) { setStringMember(zex, "str", e.str TSRMLS_CC); } catch(const Ice::IllegalIdentityException& e) { zval* id; MAKE_STD_ZVAL(id); if(!createIdentity(id, e.id TSRMLS_CC)) { zval_ptr_dtor(&id); return false; } zend_update_property(cls, zex, const_cast("id"), sizeof("id") - 1, id TSRMLS_CC); zval_ptr_dtor(&id); } catch(const Ice::RequestFailedException& e) { zval* id; MAKE_STD_ZVAL(id); if(!createIdentity(id, e.id TSRMLS_CC)) { zval_ptr_dtor(&id); return false; } zend_update_property(cls, zex, const_cast("id"), sizeof("id") - 1, id TSRMLS_CC); zval_ptr_dtor(&id); setStringMember(zex, "facet", e.facet TSRMLS_CC); setStringMember(zex, "operation", e.operation TSRMLS_CC); } catch(const Ice::FileException& e) { zend_update_property_long(cls, zex, const_cast("error"), sizeof("error") - 1, e.error TSRMLS_CC); setStringMember(zex, "path", e.path TSRMLS_CC); } catch(const Ice::SyscallException& e) // This must appear after all subclasses of SyscallException. { zend_update_property_long(cls, zex, const_cast("error"), sizeof("error") - 1, e.error TSRMLS_CC); } catch(const Ice::DNSException& e) { zend_update_property_long(cls, zex, const_cast("error"), sizeof("error") - 1, e.error TSRMLS_CC); setStringMember(zex, "host", e.host TSRMLS_CC); } catch(const Ice::UnsupportedProtocolException& e) { zval* v; MAKE_STD_ZVAL(v); if(!createProtocolVersion(v, e.bad TSRMLS_CC)) { zval_ptr_dtor(&v); return false; } zend_update_property(cls, zex, const_cast("bad"), sizeof("bad") - 1, v TSRMLS_CC); zval_ptr_dtor(&v); MAKE_STD_ZVAL(v); if(!createProtocolVersion(v, e.supported TSRMLS_CC)) { zval_ptr_dtor(&v); return false; } zend_update_property(cls, zex, const_cast("supported"), sizeof("supported") - 1, v TSRMLS_CC); zval_ptr_dtor(&v); } catch(const Ice::UnsupportedEncodingException& e) { zval* v; MAKE_STD_ZVAL(v); if(!createEncodingVersion(v, e.bad TSRMLS_CC)) { zval_ptr_dtor(&v); return false; } zend_update_property(cls, zex, const_cast("bad"), sizeof("bad") - 1, v TSRMLS_CC); zval_ptr_dtor(&v); MAKE_STD_ZVAL(v); if(!createEncodingVersion(v, e.supported TSRMLS_CC)) { zval_ptr_dtor(&v); return false; } zend_update_property(cls, zex, const_cast("supported"), sizeof("supported") - 1, v TSRMLS_CC); zval_ptr_dtor(&v); } catch(const Ice::NoValueFactoryException& e) { setStringMember(zex, "reason", e.reason TSRMLS_CC); setStringMember(zex, "type", e.type TSRMLS_CC); } catch(const Ice::UnexpectedObjectException& e) { setStringMember(zex, "reason", e.reason TSRMLS_CC); setStringMember(zex, "type", e.type TSRMLS_CC); setStringMember(zex, "expectedType", e.expectedType TSRMLS_CC); } catch(const Ice::ProtocolException& e) // This must appear after all subclasses of ProtocolException. { setStringMember(zex, "reason", e.reason TSRMLS_CC); } catch(const Ice::FeatureNotSupportedException& e) { setStringMember(zex, "unsupportedFeature", e.unsupportedFeature TSRMLS_CC); } catch(const Ice::SecurityException& e) { setStringMember(zex, "reason", e.reason TSRMLS_CC); } catch(const Ice::ConnectionManuallyClosedException& e) { add_property_bool(zex, "graceful", e.graceful ? 1 : 0); } catch(const Ice::LocalException&) { // // Nothing to do. // } return true; } zval* IcePHP::convertException(const Ice::Exception& ex TSRMLS_DC) { zval* zex; MAKE_STD_ZVAL(zex); AutoDestroy destroy(zex); ostringstream ostr; ostr << ex; string str = ostr.str(); try { ex.ice_throw(); } catch(const Ice::LocalException& e) { zend_class_entry* cls = idToClass(e.ice_id() TSRMLS_CC); if(cls) { if(object_init_ex(zex, cls) != SUCCESS) { runtimeError("unable to create exception %s" TSRMLS_CC, cls->name); return 0; } if(!convertLocalException(e, zex TSRMLS_CC)) { return 0; } } else { cls = idToClass("Ice::UnknownLocalException" TSRMLS_CC); assert(cls); if(object_init_ex(zex, cls) != SUCCESS) { runtimeError("unable to create exception %s" TSRMLS_CC, cls->name); return 0; } setStringMember(zex, "unknown", str TSRMLS_CC); } } catch(const Ice::UserException&) { zend_class_entry* cls = idToClass("Ice::UnknownUserException" TSRMLS_CC); assert(cls); if(object_init_ex(zex, cls) != SUCCESS) { runtimeError("unable to create exception %s" TSRMLS_CC, cls->name); return 0; } setStringMember(zex, "unknown", str TSRMLS_CC); } catch(const Ice::Exception&) { zend_class_entry* cls = idToClass("Ice::UnknownException" TSRMLS_CC); assert(cls); if(object_init_ex(zex, cls) != SUCCESS) { runtimeError("unable to create exception %s" TSRMLS_CC, cls->name); return 0; } setStringMember(zex, "unknown", str TSRMLS_CC); } return destroy.release(); } void IcePHP::throwException(const Ice::Exception& ex TSRMLS_DC) { zval* zex = convertException(ex TSRMLS_CC); if(zex) { zend_throw_exception_object(zex TSRMLS_CC); } } std::string IcePHP::zendTypeToString(int type) { string result; switch(type) { case IS_NULL: result = "null"; break; case IS_LONG: result = "long"; break; case IS_DOUBLE: result = "double"; break; case IS_STRING: result = "string"; break; case IS_ARRAY: result = "array"; break; case IS_OBJECT: result = "object"; break; case IS_BOOL: result = "bool"; break; default: result = "unknown"; break; } return result; } static void throwError(const string& name, const string& msg TSRMLS_DC) { zval* ex; MAKE_STD_ZVAL(ex); AutoDestroy destroy(ex); zend_class_entry* cls; { zend_class_entry** p; if(zend_lookup_class(STRCAST(name.c_str()), static_cast(name.size()), &p TSRMLS_CC) == FAILURE) { assert(false); } cls = *p; } if(object_init_ex(ex, cls) == FAILURE) { assert(false); } // // Invoke constructor. // if(!invokeMethod(ex, ZEND_CONSTRUCTOR_FUNC_NAME, msg TSRMLS_CC)) { assert(false); } zend_throw_exception_object(ex TSRMLS_CC); destroy.release(); } void IcePHP::runtimeError(const char* fmt TSRMLS_DC, ...) { va_list args; char msg[1024]; #if ZTS va_start(args, TSRMLS_C); #else va_start(args, fmt); #endif #if defined(_MSC_VER) vsprintf_s(msg, fmt, args); #else vsprintf(msg, fmt, args); #endif va_end(args); throwError("RuntimeException", msg TSRMLS_CC); } void IcePHP::invalidArgument(const char* fmt TSRMLS_DC, ...) { va_list args; char msg[1024]; #if ZTS va_start(args, TSRMLS_C); #else va_start(args, fmt); #endif #if defined(_MSC_VER) vsprintf_s(msg, fmt, args); #else vsprintf(msg, fmt, args); #endif va_end(args); throwError("InvalidArgumentException", msg TSRMLS_CC); } static bool invokeMethodHelper(zval* obj, const string& name, zval* param TSRMLS_DC) { assert(zend_hash_exists(&Z_OBJCE_P(obj)->function_table, STRCAST(name.c_str()), name.size() + 1)); zval ret, method; INIT_ZVAL(ret); INIT_ZVAL(method); ZVAL_STRING(&method, STRCAST(name.c_str()), 1); zend_uint numParams = param ? 1 : 0; zval** params = param ? ¶m : 0; int status = 0; zend_try { status = call_user_function(0, &obj, &method, &ret, numParams, params TSRMLS_CC); } zend_catch { status = FAILURE; } zend_end_try(); zval_dtor(&method); zval_dtor(&ret); if(status == FAILURE || EG(exception)) { return false; } return true; } bool IcePHP::invokeMethod(zval* obj, const string& name TSRMLS_DC) { return invokeMethodHelper(obj, name, 0 TSRMLS_CC); } bool IcePHP::invokeMethod(zval* obj, const string& name, const string& arg TSRMLS_DC) { zval* param; MAKE_STD_ZVAL(param); ZVAL_STRINGL(param, STRCAST(arg.c_str()), static_cast(arg.size()), 1); AutoDestroy destroy(param); return invokeMethodHelper(obj, name, param TSRMLS_CC); } bool IcePHP::checkClass(zend_class_entry* ce, zend_class_entry* base) { while(ce) { if(ce == base) { return true; } for(zend_uint i = 0; i < ce->num_interfaces; ++i) { if(checkClass(ce->interfaces[i], base)) { return true; } } ce = ce->parent; } return false; } ZEND_FUNCTION(Ice_stringVersion) { if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } RETURN_STRINGL(STRCAST(ICE_STRING_VERSION), static_cast(strlen(ICE_STRING_VERSION)), 1); } ZEND_FUNCTION(Ice_intVersion) { if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } RETURN_LONG(ICE_INT_VERSION); } ZEND_FUNCTION(Ice_generateUUID) { if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } string uuid = Ice::generateUUID(); RETURN_STRINGL(STRCAST(uuid.c_str()), static_cast(uuid.size()), 1); } ZEND_FUNCTION(Ice_currentProtocol) { if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } if(!createProtocolVersion(return_value, Ice::currentProtocol TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(Ice_currentProtocolEncoding) { if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } if(!createEncodingVersion(return_value, Ice::currentProtocolEncoding TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(Ice_currentEncoding) { if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } if(!createEncodingVersion(return_value, Ice::currentEncoding TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(Ice_protocolVersionToString) { zend_class_entry* versionClass = idToClass(Ice_ProtocolVersion TSRMLS_CC); assert(versionClass); zval* zv; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("O"), &zv, versionClass) != SUCCESS) { RETURN_NULL(); } if(!versionToString(zv, return_value, Ice_ProtocolVersion TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(Ice_stringToProtocolVersion) { char* str; int strLen; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("s"), &str, &strLen) != SUCCESS) { RETURN_NULL(); } string s(str, strLen); if(!stringToVersion(s, return_value, Ice_ProtocolVersion TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(Ice_encodingVersionToString) { zend_class_entry* versionClass = idToClass(Ice_EncodingVersion TSRMLS_CC); assert(versionClass); zval* zv; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("O"), &zv, versionClass) != SUCCESS) { RETURN_NULL(); } if(!versionToString(zv, return_value, Ice_EncodingVersion TSRMLS_CC)) { RETURN_NULL(); } } ZEND_FUNCTION(Ice_stringToEncodingVersion) { char* str; int strLen; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast("s"), &str, &strLen) != SUCCESS) { RETURN_NULL(); } string s(str, strLen); if(!stringToVersion(s, return_value, Ice_EncodingVersion TSRMLS_CC)) { RETURN_NULL(); } }