diff options
Diffstat (limited to 'php/src/php7/Util.cpp')
-rw-r--r-- | php/src/php7/Util.cpp | 972 |
1 files changed, 972 insertions, 0 deletions
diff --git a/php/src/php7/Util.cpp b/php/src/php7/Util.cpp new file mode 100644 index 00000000000..193f613f303 --- /dev/null +++ b/php/src/php7/Util.cpp @@ -0,0 +1,972 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2016 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 <Util.h> +#include <IceUtil/UUID.h> +#include <Slice/PHPUtil.h> +#include <algorithm> +#include <ctype.h> + +using namespace std; +using namespace IcePHP; +using namespace Slice::PHP; + +namespace +{ + +bool +getMember(zval* zv, const string& name, zval* member, int type, bool required) +{ + zval* val = zend_hash_str_find(Z_OBJPROP_P(zv), STRCAST(name.c_str()), name.size()); + if(!val) + { + if(required) + { + invalidArgument("object does not contain member `%s'", name.c_str()); + return false; + } + } + + if(val) + { + assert(Z_TYPE_P(val) == IS_INDIRECT); + val = Z_INDIRECT_P(val); + if(Z_TYPE_P(val) != type) + { + string expected = zendTypeToString(type); + string actual = zendTypeToString(Z_TYPE_P(val)); + invalidArgument("expected value of type %s for member `%s' but received %s", expected.c_str(), + name.c_str(), actual.c_str()); + return false; + } + ZVAL_COPY_VALUE(member, val); + } + return true; +} + +void +setStringMember(zval* obj, const string& name, const string& val) +{ + zend_class_entry* cls = Z_OBJCE_P(obj); + assert(cls); + zend_update_property_stringl(cls, + obj, + const_cast<char*>(name.c_str()), + static_cast<int>(name.size()), + const_cast<char*>(val.c_str()), + static_cast<int>(val.size()) + ); +} + +template<typename T> +bool +getVersion(zval* zv, T& v, const char* type) +{ + if(Z_TYPE_P(zv) != IS_OBJECT) + { + invalidArgument("value does not contain an object"); + return false; + } + + zend_class_entry* cls = idToClass(type); + assert(cls); + + zend_class_entry* ce = Z_OBJCE_P(zv); + if(ce != cls) + { + invalidArgument("expected an instance of %s", ce->name); + return false; + } + + zval majorVal; + if(!getMember(zv, "major", &majorVal, IS_LONG, true)) + { + return false; + } + + zval minorVal; + if(!getMember(zv, "minor", &minorVal, IS_LONG, true)) + { + return false; + } + + long m; + m = static_cast<long>(Z_LVAL_P(&majorVal)); + if(m < 0 || m > 255) + { + invalidArgument("version major must be a value between 0 and 255"); + return false; + } + v.major = static_cast<Ice::Byte>(m); + + m = static_cast<long>(Z_LVAL_P(&minorVal)); + if(m < 0 || m > 255) + { + invalidArgument("version minor must be a value between 0 and 255"); + return false; + } + v.minor = static_cast<Ice::Byte>(m); + + return true; +} + +template<typename T> +bool +createVersion(zval* zv, const T& version, const char* type) +{ + zend_class_entry* cls = idToClass(type); + assert(cls); + + if(object_init_ex(zv, cls) != SUCCESS) + { + runtimeError("unable to initialize %s", cls->name); + return false; + } + + zend_update_property_long(cls, zv, const_cast<char*>("major"), sizeof("major") - 1, version.major); + zend_update_property_long(cls, zv, const_cast<char*>("minor"), sizeof("minor") - 1, version.minor); + + return true; +} + +template<typename T> +bool +versionToString(zval* zv, zval* s, const char* type) +{ + T v; + if(!getVersion<T>(zv, v, type)) + { + return false; + } + + try + { + string str = IceInternal::versionToString<T>(v); + ZVAL_STRINGL(s, STRCAST(str.c_str()), static_cast<int>(str.length())); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex); + return false; + } + + return true; +} + +template<typename T> +bool +stringToVersion(const string& s, zval* zv, const char* type) +{ + try + { + T v = IceInternal::stringToVersion<T>(s); + return createVersion<T>(zv, v, type); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex); + } + + return false; +} + +char Ice_ProtocolVersion[] = "::Ice::ProtocolVersion"; +char Ice_EncodingVersion[] = "::Ice::EncodingVersion"; + +} + +void* +IcePHP::extractWrapper(zval* zv) +{ + if(!zv) + { + runtimeError("method %s() must be invoked on an object", get_active_function_name()); + return 0; + } + + zend_object* obj = Z_OBJ_P(zv); + if(!obj) + { + runtimeError("no object found in %s()", get_active_function_name()); + return 0; + } + + return obj; +} + +zend_class_entry* +IcePHP::idToClass(const string& id) +{ +#ifdef ICEPHP_USE_NAMESPACES + string cls = scopedToName(id, true); +#else + string cls = scopedToName(id, false); +#endif + + return nameToClass(cls); +} + +zend_class_entry* +IcePHP::nameToClass(const string& name) +{ + zend_class_entry* result; + zend_string* s = zend_string_init(STRCAST(name.c_str()), static_cast<int>(name.length()), 0); + result = zend_lookup_class(s); + zend_string_release(s); + return result; +} + +bool +IcePHP::createIdentity(zval* zv, const Ice::Identity& id) +{ + zend_class_entry* cls = idToClass("::Ice::Identity"); + assert(cls); + + if(object_init_ex(zv, cls) != SUCCESS) + { + runtimeError("unable to initialize Ice::Identity"); + return false; + } + + setStringMember(zv, "name", id.name); + setStringMember(zv, "category", id.category); + + return true; +} + +bool +IcePHP::extractIdentity(zval* zv, Ice::Identity& id) +{ + if(Z_TYPE_P(zv) != IS_OBJECT) + { + invalidArgument("value does not contain an object"); + return false; + } + + zend_class_entry* cls = idToClass("::Ice::Identity"); + assert(cls); + + zend_class_entry* ce = Z_OBJCE_P(zv); + if(ce != cls) + { + invalidArgument("expected an identity but received %s", ce->name); + return false; + } + + // + // Category is optional, but name is required. + // + zval categoryVal; + ZVAL_UNDEF(&categoryVal); + zval nameVal; + + if(!getMember(zv, "category", &categoryVal, IS_STRING, false) || + !getMember(zv, "name", &nameVal, IS_STRING, true)) + { + return false; + } + + id.name = Z_STRVAL_P(&nameVal); + + if(!Z_ISUNDEF(categoryVal)) + { + id.category = Z_STRVAL_P(&categoryVal); + } + else + { + id.category = ""; + } + + return true; +} + +bool +IcePHP::createStringMap(zval* zv, const map<string, string>& ctx) +{ + array_init(zv); + + for(map<string, string>::const_iterator p = ctx.begin(); p != ctx.end(); ++p) + { + if(add_assoc_stringl_ex(zv, + const_cast<char*>(p->first.c_str()), + static_cast<uint>(p->first.length()), + const_cast<char*>(p->second.c_str()), + static_cast<uint>(p->second.length())) == FAILURE) + { + return false; + } + } + + return true; +} + +bool +IcePHP::extractStringMap(zval* zv, map<string, string>& ctx) +{ + if(Z_TYPE_P(zv) != IS_ARRAY) + { + string s = zendTypeToString(Z_TYPE_P(zv)); + invalidArgument("expected an associative array but received %s", s.c_str()); + return false; + } + + HashTable* arr = Z_ARRVAL_P(zv); + zend_ulong num_key; + zend_string* key; + zval* val; + ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, key, val) + { + if(!key) + { + invalidArgument("array key must be a string"); + return false; + } + + if(Z_TYPE_P(val) != IS_STRING) + { + invalidArgument("array value must be a string"); + return false; + } + + ctx[key->val] = Z_STRVAL_P(val); + (void)num_key; // Avoids error from older versions of GCC about unused variable num_key. + } + ZEND_HASH_FOREACH_END(); + + return true; +} + +bool +IcePHP::createStringArray(zval* zv, const Ice::StringSeq& seq) +{ + 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<uint>(p->length())) == FAILURE) + { + return false; + } + } + + return true; +} + +bool +IcePHP::extractStringArray(zval* zv, Ice::StringSeq& seq) +{ + if(Z_TYPE_P(zv) != IS_ARRAY) + { + string s = zendTypeToString(Z_TYPE_P(zv)); + invalidArgument("expected an array of strings but received %s", s.c_str()); + return false; + } + + HashTable* arr = Z_ARRVAL_P(zv); + zval* val; + ZEND_HASH_FOREACH_VAL(arr, val) + { + if(Z_TYPE_P(val) != IS_STRING) + { + invalidArgument("array element must be a string"); + return false; + } + + string s(Z_STRVAL_P(val), Z_STRLEN_P(val)); + seq.push_back(s); + } + ZEND_HASH_FOREACH_END(); + + return true; +} + +bool +IcePHP::createProtocolVersion(zval* zv, const Ice::ProtocolVersion& v) +{ + return createVersion<Ice::ProtocolVersion>(zv, v, Ice_ProtocolVersion); +} + +bool +IcePHP::createEncodingVersion(zval* zv, const Ice::EncodingVersion& v) +{ + return createVersion<Ice::EncodingVersion>(zv, v, Ice_EncodingVersion); +} + +bool +IcePHP::extractEncodingVersion(zval* zv, Ice::EncodingVersion& v) +{ + return getVersion<Ice::EncodingVersion>(zv, v, Ice_EncodingVersion); +} + +static bool +convertLocalException(const Ice::LocalException& ex, zval* zex) +{ + 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); + } + catch(const Ice::PluginInitializationException& e) + { + setStringMember(zex, "reason", e.reason); + } + catch(const Ice::AlreadyRegisteredException& e) + { + setStringMember(zex, "kindOfObject", e.kindOfObject); + setStringMember(zex, "id", e.id); + } + catch(const Ice::NotRegisteredException& e) + { + setStringMember(zex, "kindOfObject", e.kindOfObject); + setStringMember(zex, "id", e.id); + } + catch(const Ice::TwowayOnlyException& e) + { + setStringMember(zex, "operation", e.operation); + } + catch(const Ice::UnknownException& e) + { + setStringMember(zex, "unknown", e.unknown); + } + catch(const Ice::ObjectAdapterDeactivatedException& e) + { + setStringMember(zex, "name", e.name); + } + catch(const Ice::ObjectAdapterIdInUseException& e) + { + setStringMember(zex, "id", e.id); + } + catch(const Ice::NoEndpointException& e) + { + setStringMember(zex, "proxy", e.proxy); + } + catch(const Ice::EndpointParseException& e) + { + setStringMember(zex, "str", e.str); + } + catch(const Ice::IdentityParseException& e) + { + setStringMember(zex, "str", e.str); + } + catch(const Ice::ProxyParseException& e) + { + setStringMember(zex, "str", e.str); + } + catch(const Ice::IllegalIdentityException& e) + { + zval id; + if(!createIdentity(&id, e.id)) + { + zval_ptr_dtor(&id); + return false; + } + zend_update_property(cls, zex, const_cast<char*>("id"), sizeof("id") - 1, &id); + zval_ptr_dtor(&id); + } + catch(const Ice::RequestFailedException& e) + { + zval id; + if(!createIdentity(&id, e.id)) + { + zval_ptr_dtor(&id); + return false; + } + zend_update_property(cls, zex, const_cast<char*>("id"), sizeof("id") - 1, &id); + zval_ptr_dtor(&id); + setStringMember(zex, "facet", e.facet); + setStringMember(zex, "operation", e.operation); + } + catch(const Ice::FileException& e) + { + zend_update_property_long(cls, zex, const_cast<char*>("error"), sizeof("error") - 1, e.error); + setStringMember(zex, "path", e.path); + } + catch(const Ice::SyscallException& e) // This must appear after all subclasses of SyscallException. + { + zend_update_property_long(cls, zex, const_cast<char*>("error"), sizeof("error") - 1, e.error); + } + catch(const Ice::DNSException& e) + { + zend_update_property_long(cls, zex, const_cast<char*>("error"), sizeof("error") - 1, e.error); + setStringMember(zex, "host", e.host); + } + catch(const Ice::UnsupportedProtocolException& e) + { + zval v; + if(!createProtocolVersion(&v, e.bad)) + { + zval_ptr_dtor(&v); + return false; + } + zend_update_property(cls, zex, const_cast<char*>("bad"), sizeof("bad") - 1, &v); + zval_ptr_dtor(&v); + + if(!createProtocolVersion(&v, e.supported)) + { + zval_ptr_dtor(&v); + return false; + } + zend_update_property(cls, zex, const_cast<char*>("supported"), sizeof("supported") - 1, &v); + zval_ptr_dtor(&v); + } + catch(const Ice::UnsupportedEncodingException& e) + { + zval v; + if(!createEncodingVersion(&v, e.bad)) + { + zval_ptr_dtor(&v); + return false; + } + zend_update_property(cls, zex, const_cast<char*>("bad"), sizeof("bad") - 1, &v); + zval_ptr_dtor(&v); + + if(!createEncodingVersion(&v, e.supported)) + { + zval_ptr_dtor(&v); + return false; + } + zend_update_property(cls, zex, const_cast<char*>("supported"), sizeof("supported") - 1, &v); + zval_ptr_dtor(&v); + } + catch(const Ice::NoObjectFactoryException& e) + { + setStringMember(zex, "reason", e.reason); + setStringMember(zex, "type", e.type); + } + catch(const Ice::UnexpectedObjectException& e) + { + setStringMember(zex, "reason", e.reason); + setStringMember(zex, "type", e.type); + setStringMember(zex, "expectedType", e.expectedType); + } + catch(const Ice::ProtocolException& e) // This must appear after all subclasses of ProtocolException. + { + setStringMember(zex, "reason", e.reason); + } + catch(const Ice::FeatureNotSupportedException& e) + { + setStringMember(zex, "unsupportedFeature", e.unsupportedFeature); + } + catch(const Ice::SecurityException& e) + { + setStringMember(zex, "reason", e.reason); + } + catch(const Ice::LocalException&) + { + // + // Nothing to do. + // + } + + return true; +} + +void +IcePHP::convertException(zval* zex, const Ice::Exception& ex) +{ + ZVAL_UNDEF(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_name()); + if(cls) + { + if(object_init_ex(zex, cls) != SUCCESS) + { + runtimeError("unable to create exception %s", cls->name); + return; + } + if(!convertLocalException(e, zex)) + { + return; + } + } + else + { + cls = idToClass("Ice::UnknownLocalException"); + assert(cls); + if(object_init_ex(zex, cls) != SUCCESS) + { + runtimeError("unable to create exception %s", cls->name); + return; + } + setStringMember(zex, "unknown", str); + } + } + catch(const Ice::UserException&) + { + zend_class_entry* cls = idToClass("Ice::UnknownUserException"); + assert(cls); + if(object_init_ex(zex, cls) != SUCCESS) + { + runtimeError("unable to create exception %s", cls->name); + return; + } + setStringMember(zex, "unknown", str); + } + catch(const Ice::Exception&) + { + zend_class_entry* cls = idToClass("Ice::UnknownException"); + assert(cls); + if(object_init_ex(zex, cls) != SUCCESS) + { + runtimeError("unable to create exception %s", cls->name); + return; + } + setStringMember(zex, "unknown", str); + } + + return; +} + +void +IcePHP::throwException(const Ice::Exception& ex) +{ + zval zex; + convertException(&zex, ex); + if(!Z_ISUNDEF(zex)) + { + zend_throw_exception_object(&zex); + } +} + +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_TRUE: + case IS_FALSE: + result = "bool"; + break; + + default: + result = "unknown"; + break; + } + + return result; +} + +static void +throwError(const string& name, const string& msg) +{ + if(EG(exception)) + { + return; + } + zval ex; + // AutoDestroy destroy(&ex); + + zend_class_entry* cls; + { + zend_class_entry* p; + zend_string* s = zend_string_init(STRCAST(name.c_str()), static_cast<int>(name.size()), 0); + p = zend_lookup_class(s); + zend_string_release(s); + assert(p); + cls = p; + } + if(object_init_ex(&ex, cls) == FAILURE) + { + assert(false); + } + + // + // Invoke constructor. + // + if(!invokeMethod(&ex, ZEND_CONSTRUCTOR_FUNC_NAME, msg)) + { + assert(false); + } + + zend_throw_exception_object(&ex); + // destroy.release(); +} + +void +IcePHP::runtimeError(const char* fmt, ...) +{ + va_list args; + char msg[1024]; + + va_start(args, fmt); + +#if defined(_MSC_VER) + vsprintf_s(msg, fmt, args); +#else + vsprintf(msg, fmt, args); +#endif + + va_end(args); + + throwError("RuntimeException", msg); +} + +void +IcePHP::invalidArgument(const char* fmt, ...) +{ + va_list args; + char msg[1024]; + + va_start(args, fmt); + +#if defined(_MSC_VER) + vsprintf_s(msg, fmt, args); +#else + vsprintf(msg, fmt, args); +#endif + + va_end(args); + + throwError("InvalidArgumentException", msg); +} + +static bool +invokeMethodHelper(zval* obj, const string& name, zval* param) +{ + assert(zend_hash_str_exists(&Z_OBJCE_P(obj)->function_table, STRCAST(name.c_str()), name.size())); + zval ret, method; + ZVAL_STRING(&method, STRCAST(name.c_str())); + uint32_t numParams = param ? 1 : 0; + // zval** params = param ? ¶m : 0; + int status = 0; + zend_try + { + status = call_user_function(0, obj, &method, &ret, numParams, param); + } + 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) +{ + return invokeMethodHelper(obj, name, 0); +} + +bool +IcePHP::invokeMethod(zval* obj, const string& name, const string& arg) +{ + zval param; + ZVAL_STRINGL(¶m, STRCAST(arg.c_str()), static_cast<int>(arg.size())); + return invokeMethodHelper(obj, name, ¶m); +} + +bool +IcePHP::checkClass(zend_class_entry* ce, zend_class_entry* base) +{ + while(ce) + { + if(ce == base) + { + return true; + } + + for(zend_ulong 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<int>(strlen(ICE_STRING_VERSION))); +} + +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 = IceUtil::generateUUID(); + RETURN_STRINGL(STRCAST(uuid.c_str()), static_cast<int>(uuid.size())); +} + +ZEND_FUNCTION(Ice_currentProtocol) +{ + if(ZEND_NUM_ARGS() > 0) + { + WRONG_PARAM_COUNT; + } + + if(!createProtocolVersion(return_value, Ice::currentProtocol)) + { + RETURN_NULL(); + } +} + +ZEND_FUNCTION(Ice_currentProtocolEncoding) +{ + if(ZEND_NUM_ARGS() > 0) + { + WRONG_PARAM_COUNT; + } + + if(!createEncodingVersion(return_value, Ice::currentProtocolEncoding)) + { + RETURN_NULL(); + } +} + +ZEND_FUNCTION(Ice_currentEncoding) +{ + if(ZEND_NUM_ARGS() > 0) + { + WRONG_PARAM_COUNT; + } + + if(!createEncodingVersion(return_value, Ice::currentEncoding)) + { + RETURN_NULL(); + } +} + +ZEND_FUNCTION(Ice_protocolVersionToString) +{ + zend_class_entry* versionClass = idToClass(Ice_ProtocolVersion); + assert(versionClass); + + zval zv; + if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O"), &zv, versionClass) != SUCCESS) + { + RETURN_NULL(); + } + + if(!versionToString<Ice::ProtocolVersion>(&zv, return_value, Ice_ProtocolVersion)) + { + RETURN_NULL(); + } +} + +ZEND_FUNCTION(Ice_stringToProtocolVersion) +{ + char* str; + size_t strLen; + if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS) + { + RETURN_NULL(); + } + string s(str, strLen); + + if(!stringToVersion<Ice::ProtocolVersion>(s, return_value, Ice_ProtocolVersion)) + { + RETURN_NULL(); + } +} + +ZEND_FUNCTION(Ice_encodingVersionToString) +{ + zend_class_entry* versionClass = idToClass(Ice_EncodingVersion); + assert(versionClass); + + zval* zv; + if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O"), &zv, versionClass) != SUCCESS) + { + RETURN_NULL(); + } + + if(!versionToString<Ice::EncodingVersion>(zv, return_value, Ice_EncodingVersion)) + { + RETURN_NULL(); + } +} + +ZEND_FUNCTION(Ice_stringToEncodingVersion) +{ + char* str; + size_t strLen; + if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS) + { + RETURN_NULL(); + } + string s(str, strLen); + + if(!stringToVersion<Ice::EncodingVersion>(s, return_value, Ice_EncodingVersion)) + { + RETURN_NULL(); + } +} |