diff options
author | Mark Spruiell <mes@zeroc.com> | 2009-06-23 17:47:58 -0700 |
---|---|---|
committer | Mark Spruiell <mes@zeroc.com> | 2009-06-23 17:47:58 -0700 |
commit | a2e794a1366fcd71f04496c206869ffa158547ac (patch) | |
tree | 4c9d6554f3dc658aeaa76892aeecfc3d7eb56b61 /php/src/IcePHP/Communicator.cpp | |
parent | Add x64 configurations to C++ projects (diff) | |
download | ice-a2e794a1366fcd71f04496c206869ffa158547ac.tar.bz2 ice-a2e794a1366fcd71f04496c206869ffa158547ac.tar.xz ice-a2e794a1366fcd71f04496c206869ffa158547ac.zip |
PHP changes:
- static translation
- more traditional language mapping (multiple communicators, etc.)
- support for registered (persistent) communicators
- support for PHP namespaces (PHP 5.3 or later)
Diffstat (limited to 'php/src/IcePHP/Communicator.cpp')
-rw-r--r-- | php/src/IcePHP/Communicator.cpp | 1651 |
1 files changed, 1252 insertions, 399 deletions
diff --git a/php/src/IcePHP/Communicator.cpp b/php/src/IcePHP/Communicator.cpp index 40a315a3f84..ca48dc52565 100644 --- a/php/src/IcePHP/Communicator.cpp +++ b/php/src/IcePHP/Communicator.cpp @@ -7,11 +7,14 @@ // // ********************************************************************** -#include <IceUtil/DisableWarnings.h> #include <Communicator.h> +#include <Logger.h> +#include <Properties.h> #include <Proxy.h> -#include <Marshal.h> #include <Util.h> +#include <IceUtil/Options.h> +#include <IceUtil/StaticMutex.h> +#include <IceUtil/Timer.h> using namespace std; using namespace IcePHP; @@ -21,704 +24,1554 @@ ZEND_EXTERN_MODULE_GLOBALS(ice) // // Class entries represent the PHP class implementations we have registered. // -static zend_class_entry* _communicatorClassEntry; +namespace IcePHP +{ + +zend_class_entry* communicatorClassEntry = 0; // -// Ice::Communicator support. +// An active communicator is in use by at least one request and may have +// registered so that it remains active after a request completes. The +// communicator is destroyed when there are no more references to this +// object. // -static zend_object_handlers _handlers; +class ActiveCommunicator : public IceUtil::Shared +{ +public: -extern "C" + ActiveCommunicator(const Ice::CommunicatorPtr& c); + ~ActiveCommunicator(); + + const Ice::CommunicatorPtr communicator; + vector<string> ids; + int expires; + IceUtil::Time lastAccess; +}; +typedef IceUtil::Handle<ActiveCommunicator> ActiveCommunicatorPtr; + +typedef std::map<std::string, zval*> ObjectFactoryMap; + +class CommunicatorInfoI : public CommunicatorInfo { -static zend_object_value handleAlloc(zend_class_entry* TSRMLS_DC); -static zend_object_value handleClone(zval* TSRMLS_DC); -static void handleFreeStorage(void* TSRMLS_DC); +public: -static union _zend_function* handleGetMethod(zval**, char*, int TSRMLS_DC); -} + CommunicatorInfoI(const ActiveCommunicatorPtr&, zval*); + + virtual void getZval(zval* TSRMLS_DC); + virtual void addRef(TSRMLS_D); + virtual void decRef(TSRMLS_D); + + virtual Ice::CommunicatorPtr getCommunicator() const; -static void initCommunicator(ice_object* TSRMLS_DC); + bool addObjectFactory(const std::string&, zval* TSRMLS_DC); + bool findObjectFactory(const std::string&, zval* TSRMLS_DC); + void destroyObjectFactories(TSRMLS_D); + + const ActiveCommunicatorPtr ac; + const zval zv; + ObjectFactoryMap objectFactories; +}; +typedef IceUtil::Handle<CommunicatorInfoI> CommunicatorInfoIPtr; // -// Function entries for Ice::Communicator methods. +// Each PHP request has its own set of object factories. More precisely, there is +// an object factory map for each communicator that is created by a PHP request. +// The factory class defined below delegates the create/destroy methods to PHP +// objects supplied by the application. An instance of this class is installed +// as the communicator's default object factory, and the class holds a reference +// to its communicator. When create is invoked, the class resolves the appropriate +// PHP object as follows: // -static function_entry _methods[] = -{ - {"__construct", PHP_FN(Ice_Communicator___construct), 0}, - {"getProperty", PHP_FN(Ice_Communicator_getProperty), 0}, - {"setProperty", PHP_FN(Ice_Communicator_setProperty), 0}, - {"stringToProxy", PHP_FN(Ice_Communicator_stringToProxy), 0}, - {"proxyToString", PHP_FN(Ice_Communicator_proxyToString), 0}, - {"propertyToProxy", PHP_FN(Ice_Communicator_propertyToProxy), 0}, - {"stringToIdentity", PHP_FN(Ice_Communicator_stringToIdentity), 0}, - {"identityToString", PHP_FN(Ice_Communicator_identityToString), 0}, - {"addObjectFactory", PHP_FN(Ice_Communicator_addObjectFactory), 0}, - {"findObjectFactory", PHP_FN(Ice_Communicator_findObjectFactory), 0}, - {"flushBatchRequests", PHP_FN(Ice_Communicator_flushBatchRequests), 0}, - {0, 0, 0} +// * Using its communicator reference as the key, look up the corresponding +// CommunicatorInfoI object in the request-specific communicator map. +// +// * In the object factory map held by the CommunicatorInfoI object, look for a +// PHP factory object using the same algorithm as the Ice core. +// +class ObjectFactoryI : public Ice::ObjectFactory +{ +public: + + ObjectFactoryI(const Ice::CommunicatorPtr&); + + virtual Ice::ObjectPtr create(const std::string&); + virtual void destroy(); + +private: + + Ice::CommunicatorPtr _communicator; }; -bool -IcePHP::communicatorInit(TSRMLS_D) +class ReaperTask : public IceUtil::TimerTask { - // - // Register the Ice_Communicator class. - // - zend_class_entry ce; - INIT_CLASS_ENTRY(ce, "Ice_Communicator", _methods); - ce.create_object = handleAlloc; - _communicatorClassEntry = zend_register_internal_class(&ce TSRMLS_CC); - memcpy(&_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); - _handlers.clone_obj = handleClone; - _handlers.get_method = handleGetMethod; +public: + + virtual void runTimerTask(); +}; - return true; } -bool -IcePHP::createCommunicator(TSRMLS_D) +// +// Communicator support. +// +static zend_object_handlers _handlers; + +// +// The profile map holds Properties objects corresponding to the "default" profile +// (defined via the ice.config & ice.options settings in php.ini) as well as named +// profiles defined in an external file. +// +typedef map<string, Ice::PropertiesPtr> ProfileMap; +static ProfileMap _profiles; +static const string _defaultProfileName = ""; + +// +// This map represents communicators that have been registered so that they can be used +// by multiple PHP requests. +// +typedef map<string, ActiveCommunicatorPtr> RegisteredCommunicatorMap; +static RegisteredCommunicatorMap _registeredCommunicators; +static IceUtil::StaticMutex _registeredCommunicatorsMutex = ICE_STATIC_MUTEX_INITIALIZER; + +static IceUtil::TimerPtr _timer; + +// +// This map is stored in the "global" variables for each PHP request and holds +// the communicators that have been created (or registered communicators that have +// been used) by the request. +// +typedef map<Ice::CommunicatorPtr, CommunicatorInfoIPtr> CommunicatorMap; + +extern "C" { - zval* global; - MAKE_STD_ZVAL(global); +static zend_object_value handleAlloc(zend_class_entry* TSRMLS_DC); +static void handleFreeStorage(void* TSRMLS_DC); +static zend_object_value handleClone(zval* TSRMLS_DC); +} + +ZEND_METHOD(Ice_Communicator, __construct) +{ + runtimeError("communicators cannot be instantiated directly" TSRMLS_CC); +} + +ZEND_METHOD(Ice_Communicator, destroy) +{ + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); // - // Create the global variable for the communicator, but delay creation of the communicator - // itself until it is first used (see handleGetMethod). + // Remove all registrations. // - if(object_init_ex(global, _communicatorClassEntry) != SUCCESS) { - php_error_docref(0 TSRMLS_CC, E_ERROR, "unable to create object for communicator"); - return false; + IceUtil::StaticMutex::Lock sync(_registeredCommunicatorsMutex); + for(vector<string>::iterator p = _this->ac->ids.begin(); p != _this->ac->ids.end(); ++p) + { + _registeredCommunicators.erase(*p); + } + _this->ac->ids.clear(); } // - // Register the global variable "ICE" to hold the communicator. + // We need to destroy any object factories installed by this request. // - ICE_G(communicator) = global; - ZEND_SET_GLOBAL_VAR("ICE", global); + _this->destroyObjectFactories(TSRMLS_C); - return true; + Ice::CommunicatorPtr c = _this->getCommunicator(); + assert(c); + CommunicatorMap* m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap)); + assert(m); + assert(m->find(c) != m->end()); + m->erase(c); + + try + { + c->destroy(); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + RETURN_NULL(); + } } -Ice::CommunicatorPtr -IcePHP::getCommunicator(TSRMLS_D) +ZEND_METHOD(Ice_Communicator, stringToProxy) { - Ice::CommunicatorPtr result; + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); - void* data; - if(zend_hash_find(&EG(symbol_table), "ICE", sizeof("ICE"), &data) == SUCCESS) + char* str; + int strLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &strLen) != SUCCESS) { - zval** zv = reinterpret_cast<zval**>(data); - ice_object* obj = getObject(*zv TSRMLS_CC); - assert(obj); + RETURN_NULL(); + } + string s(str, strLen); - // - // Initialize the communicator if necessary. - // - if(!obj->ptr) + try + { + Ice::ObjectPrx prx = _this->getCommunicator()->stringToProxy(s); + if(!createProxy(return_value, prx, _this TSRMLS_CC)) { - try - { - initCommunicator(obj TSRMLS_CC); - } - catch(const IceUtil::Exception& ex) - { - ostringstream ostr; - ex.ice_print(ostr); - php_error_docref(0 TSRMLS_CC, E_ERROR, "unable to initialize communicator:\n%s", ostr.str().c_str()); - return 0; - } + RETURN_NULL(); } - - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); - result = *_this; } - - return result; -} - -zval* -IcePHP::getCommunicatorZval(TSRMLS_D) -{ - void* data = 0; - zend_hash_find(&EG(symbol_table), "ICE", sizeof("ICE"), &data); - assert(data); - zval **zv = reinterpret_cast<zval**>(data); - return *zv; + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + RETURN_NULL(); + } } -ZEND_FUNCTION(Ice_Communicator___construct) +ZEND_METHOD(Ice_Communicator, proxyToString) { - php_error_docref(0 TSRMLS_CC, E_ERROR, "Ice_Communicator cannot be instantiated, use the global variable $ICE"); -} + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); -ZEND_FUNCTION(Ice_Communicator_getProperty) -{ - if(ZEND_NUM_ARGS() < 1 || ZEND_NUM_ARGS() > 2) + zval* zv; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &zv, proxyClassEntry) != SUCCESS) { - WRONG_PARAM_COUNT; + RETURN_NULL(); } - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + try { - return; + string str; + if(zv) + { + Ice::ObjectPrx prx; + ClassInfoPtr info; + if(!fetchProxy(zv, prx, info TSRMLS_CC)) + { + RETURN_NULL(); + } + assert(prx); + str = prx->ice_toString(); + } + RETURN_STRINGL(STRCAST(str.c_str()), str.length(), 1); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + RETURN_NULL(); } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); +} - char *name; - int nameLen; - char *def = 0; - int defLen = 0; +ZEND_METHOD(Ice_Communicator, propertyToProxy) +{ + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &name, &nameLen, &def, &defLen) == FAILURE) + char* str; + int strLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &strLen) != SUCCESS) { RETURN_NULL(); } + string s(str, strLen); try { - string val = (*_this)->getProperties()->getProperty(name); - if(val.empty() && def) - { - RETURN_STRING(def, 1); - } - else + Ice::ObjectPrx prx = _this->getCommunicator()->propertyToProxy(s); + if(!createProxy(return_value, prx, _this TSRMLS_CC)) { - RETURN_STRING(const_cast<char*>(val.c_str()), 1); + RETURN_NULL(); } } catch(const IceUtil::Exception& ex) { throwException(ex TSRMLS_CC); - RETURN_EMPTY_STRING(); + RETURN_NULL(); } } -ZEND_FUNCTION(Ice_Communicator_setProperty) +ZEND_METHOD(Ice_Communicator, stringToIdentity) { - if(ZEND_NUM_ARGS() != 2) + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); + + char* str; + int strLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &strLen) != SUCCESS) { - WRONG_PARAM_COUNT; + RETURN_NULL(); } + string s(str, strLen); - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + try { - return; + Ice::Identity id = _this->getCommunicator()->stringToIdentity(s); + if(!createIdentity(return_value, id TSRMLS_CC)) + { + RETURN_NULL(); + } } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + RETURN_NULL(); + } +} + +ZEND_METHOD(Ice_Communicator, identityToString) +{ + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); - char *prop; - int propLen; - char *val; - int valLen; + zend_class_entry* identityClass = idToClass("::Ice::Identity" TSRMLS_CC); + assert(identityClass); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &prop, &propLen, &val, &valLen) == FAILURE) + zval* zv; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zv, identityClass) != SUCCESS) + { + RETURN_NULL(); + } + Ice::Identity id; + if(!extractIdentity(zv, id TSRMLS_CC)) { RETURN_NULL(); } try { - (*_this)->getProperties()->setProperty(prop, val); + string str = _this->getCommunicator()->identityToString(id); + RETURN_STRINGL(STRCAST(str.c_str()), str.length(), 1); } catch(const IceUtil::Exception& ex) { throwException(ex TSRMLS_CC); + RETURN_NULL(); } - RETURN_EMPTY_STRING(); } -ZEND_FUNCTION(Ice_Communicator_stringToProxy) +ZEND_METHOD(Ice_Communicator, addObjectFactory) { - if(ZEND_NUM_ARGS() != 1) + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); + + zend_class_entry* factoryClass = idToClass("Ice::ObjectFactory" TSRMLS_CC); + assert(factoryClass); + + zval* factory; + char* id; + int idLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os!", &factory, factoryClass, &id, &idLen TSRMLS_CC) != + SUCCESS) { - WRONG_PARAM_COUNT; + RETURN_NULL(); } - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + string type; + if(id) { - return; + type = string(id, idLen); } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); - - char *str; - int len; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) + if(!_this->addObjectFactory(type, factory TSRMLS_CC)) { RETURN_NULL(); } +} - Ice::ObjectPrx proxy; - try +ZEND_METHOD(Ice_Communicator, findObjectFactory) +{ + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); + + char* id; + int idLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s!", &id, &idLen TSRMLS_CC) != SUCCESS) { - proxy = (*_this)->stringToProxy(str); + RETURN_NULL(); } - catch(const IceUtil::Exception& ex) + + string type; + if(id) { - throwException(ex TSRMLS_CC); - RETURN_NULL(); + type = string(id, idLen); } - if(!createProxy(return_value, proxy TSRMLS_CC)) + if(!_this->findObjectFactory(type, return_value TSRMLS_CC)) { RETURN_NULL(); } } -ZEND_FUNCTION(Ice_Communicator_proxyToString) +ZEND_METHOD(Ice_Communicator, getImplicitContext) { - if(ZEND_NUM_ARGS() != 1) - { - WRONG_PARAM_COUNT; - } + runtimeError("not implemented" TSRMLS_CC); +} - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) +ZEND_METHOD(Ice_Communicator, getProperties) +{ + if(ZEND_NUM_ARGS() > 0) { - return; + WRONG_PARAM_COUNT; } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); - zval* zprx; + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &zprx, proxyClassEntry) == FAILURE) + try { - RETURN_EMPTY_STRING(); + Ice::PropertiesPtr props = _this->getCommunicator()->getProperties(); + if(!createProperties(return_value, props TSRMLS_CC)) + { + RETURN_NULL(); + } } - - Ice::ObjectPrx proxy; - Slice::ClassDefPtr def; - if(!zprx || !fetchProxy(zprx, proxy, def TSRMLS_CC)) + catch(const IceUtil::Exception& ex) { - RETURN_EMPTY_STRING(); + throwException(ex TSRMLS_CC); + RETURN_NULL(); } +} + +ZEND_METHOD(Ice_Communicator, getLogger) +{ + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); try { - string result = (*_this)->proxyToString(proxy); - RETURN_STRING(const_cast<char*>(result.c_str()), 1); + Ice::LoggerPtr logger = _this->getCommunicator()->getLogger(); + if(!createLogger(return_value, logger TSRMLS_CC)) + { + RETURN_NULL(); + } } catch(const IceUtil::Exception& ex) { throwException(ex TSRMLS_CC); - RETURN_EMPTY_STRING(); + RETURN_NULL(); } } -ZEND_FUNCTION(Ice_Communicator_propertyToProxy) +ZEND_METHOD(Ice_Communicator, getDefaultRouter) { - if(ZEND_NUM_ARGS() != 1) + if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); + + try { - return; + Ice::RouterPrx router = _this->getCommunicator()->getDefaultRouter(); + if(router) + { + ClassInfoPtr info = getClassInfoById("::Ice::Router" TSRMLS_CC); + if(!info) + { + runtimeError("no definition for Ice::Router" TSRMLS_CC); + RETURN_NULL(); + } + if(!createProxy(return_value, router, info, _this TSRMLS_CC)) + { + RETURN_NULL(); + } + } + else + { + RETURN_NULL(); + } } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + RETURN_NULL(); + } +} - char *str; - int len; +ZEND_METHOD(Ice_Communicator, setDefaultRouter) +{ + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) + zval* zv; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &zv, proxyClassEntry TSRMLS_CC) != SUCCESS) { RETURN_NULL(); } Ice::ObjectPrx proxy; + ClassInfoPtr info; + if(zv && !fetchProxy(zv, proxy, info TSRMLS_CC)) + { + RETURN_NULL(); + } + try { - proxy = (*_this)->propertyToProxy(str); + Ice::RouterPrx router; + if(proxy) + { + if(!info || !info->isA("::Ice::Router")) + { + invalidArgument("setDefaultRouter requires a proxy narrowed to Ice::Router" TSRMLS_CC); + RETURN_NULL(); + } + router = Ice::RouterPrx::uncheckedCast(proxy); + } + _this->getCommunicator()->setDefaultRouter(router); } catch(const IceUtil::Exception& ex) { throwException(ex TSRMLS_CC); RETURN_NULL(); } - - if(!createProxy(return_value, proxy TSRMLS_CC)) - { - RETURN_NULL(); - } } -ZEND_FUNCTION(Ice_Communicator_identityToString) +ZEND_METHOD(Ice_Communicator, getDefaultLocator) { - if(ZEND_NUM_ARGS() != 1) + if(ZEND_NUM_ARGS() > 0) { WRONG_PARAM_COUNT; } - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); + + try + { + Ice::LocatorPrx locator = _this->getCommunicator()->getDefaultLocator(); + if(locator) + { + ClassInfoPtr info = getClassInfoById("::Ice::Locator" TSRMLS_CC); + if(!info) + { + runtimeError("no definition for Ice::Locator" TSRMLS_CC); + RETURN_NULL(); + } + if(!createProxy(return_value, locator, info, _this TSRMLS_CC)) + { + RETURN_NULL(); + } + } + else + { + RETURN_NULL(); + } + } + catch(const IceUtil::Exception& ex) { - return; + throwException(ex TSRMLS_CC); + RETURN_NULL(); } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); +} - zend_class_entry* cls = findClass("Ice_Identity" TSRMLS_CC); - assert(cls); +ZEND_METHOD(Ice_Communicator, setDefaultLocator) +{ + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); - zval *zid; + zval* zv; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &zv, proxyClassEntry TSRMLS_CC) != SUCCESS) + { + RETURN_NULL(); + } - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zid, cls) == FAILURE) + Ice::ObjectPrx proxy; + ClassInfoPtr info; + if(zv && !fetchProxy(zv, proxy, info TSRMLS_CC)) { RETURN_NULL(); } - Ice::Identity id; - if(extractIdentity(zid, id TSRMLS_CC)) + try { - string s = (*_this)->identityToString(id); - RETURN_STRINGL(const_cast<char*>(s.c_str()), s.length(), 1); + Ice::LocatorPrx locator; + if(proxy) + { + if(!info || !info->isA("::Ice::Locator")) + { + invalidArgument("setDefaultRouter requires a proxy narrowed to Ice::Locator" TSRMLS_CC); + RETURN_NULL(); + } + locator = Ice::LocatorPrx::uncheckedCast(proxy); + } + _this->getCommunicator()->setDefaultLocator(locator); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + RETURN_NULL(); } } -ZEND_FUNCTION(Ice_Communicator_stringToIdentity) +ZEND_METHOD(Ice_Communicator, flushBatchRequests) { - if(ZEND_NUM_ARGS() != 1) + CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC); + assert(_this); + + if(ZEND_NUM_ARGS() != 8) { WRONG_PARAM_COUNT; } - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + try { - return; + _this->getCommunicator()->flushBatchRequests(); } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); - - char* str; - int len; - - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) + catch(const IceUtil::Exception& ex) { + throwException(ex TSRMLS_CC); RETURN_NULL(); } +} + +#ifdef _WIN32 +extern "C" +#endif +static zend_object_value +handleAlloc(zend_class_entry* ce TSRMLS_DC) +{ + zend_object_value result; + + Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::create(ce TSRMLS_CC); + assert(obj); + + result.handle = zend_objects_store_put(obj, 0, (zend_objects_free_object_storage_t)handleFreeStorage, 0 TSRMLS_CC); + result.handlers = &_handlers; + return result; +} + +#ifdef _WIN32 +extern "C" +#endif +static void +handleFreeStorage(void* p TSRMLS_DC) +{ + Wrapper<CommunicatorInfoIPtr>* obj = static_cast<Wrapper<CommunicatorInfoIPtr>*>(p); + delete obj->ptr; + zend_objects_free_object_storage(static_cast<zend_object*>(p) TSRMLS_CC); +} + +#ifdef _WIN32 +extern "C" +#endif +static zend_object_value +handleClone(zval* zv TSRMLS_DC) +{ + php_error_docref(0 TSRMLS_CC, E_ERROR, "communicators cannot be cloned"); + return zend_object_value(); +} + +static CommunicatorInfoIPtr +createCommunicator(zval* zv, const ActiveCommunicatorPtr& ac TSRMLS_DC) +{ try { - Ice::Identity id = (*_this)->stringToIdentity(str); - createIdentity(return_value, id TSRMLS_CC); + if(object_init_ex(zv, communicatorClassEntry) != SUCCESS) + { + runtimeError("unable to initialize communicator object" TSRMLS_CC); + return 0; + } + + Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::extract(zv TSRMLS_CC); + assert(!obj->ptr); + + CommunicatorInfoIPtr info = new CommunicatorInfoI(ac, zv); + obj->ptr = new CommunicatorInfoIPtr(info); + + CommunicatorMap* m; + if(ICE_G(communicatorMap)) + { + m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap)); + } + else + { + m = new CommunicatorMap; + ICE_G(communicatorMap) = m; + } + m->insert(CommunicatorMap::value_type(ac->communicator, info)); + + return info; } catch(const IceUtil::Exception& ex) { throwException(ex TSRMLS_CC); + return 0; } } -ZEND_FUNCTION(Ice_Communicator_addObjectFactory) +static CommunicatorInfoIPtr +initializeCommunicator(zval* zv, Ice::StringSeq& args, const Ice::InitializationData& initData TSRMLS_DC) { - if(ZEND_NUM_ARGS() != 2) + try { - WRONG_PARAM_COUNT; + Ice::CommunicatorPtr c = Ice::initialize(args, initData); + ActiveCommunicatorPtr ac = new ActiveCommunicator(c); + + // + // Install a default object factory that delegates to PHP factories. + // + c->addObjectFactory(new ObjectFactoryI(c), ""); + + CommunicatorInfoIPtr info = createCommunicator(zv, ac TSRMLS_CC); + if(!info) + { + try + { + c->destroy(); + } + catch(...) + { + } + } + + return info; } + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + return 0; + } +} - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) +ZEND_FUNCTION(Ice_initialize) +{ + if(ZEND_NUM_ARGS() > 2) { - return; + runtimeError("too many arguments" TSRMLS_CC); + RETURN_NULL(); } - assert(obj->ptr); - zval* zfactory; - char* id; - int len; + zend_class_entry* initClass = idToClass("::Ice::InitializationData" TSRMLS_CC); + assert(initClass); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "os", &zfactory, &id, &len) == FAILURE) + // + // Retrieve the arguments. + // + zval*** args = static_cast<zval***>(emalloc(ZEND_NUM_ARGS() * sizeof(zval**))); + AutoEfree autoArgs(args); // Call efree on return + if(zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE) { - return; + runtimeError("unable to get arguments" TSRMLS_CC); + RETURN_NULL(); } + Ice::StringSeq seq; + Ice::InitializationData initData; + zval* zvinit = 0; + // - // Verify that the object implements Ice_ObjectFactory. + // Accept the following invocations: // - // TODO: When zend_check_class is changed to also check interfaces, we can remove this code and - // pass the class entry for Ice_ObjectFactory to zend_parse_parameters instead. + // initialize(array, InitializationData) + // initialize(array) + // initialize(InitializationData) + // initialize() // - zend_class_entry* ce = Z_OBJCE_P(zfactory); - zend_class_entry* base = findClass("Ice_ObjectFactory" TSRMLS_CC); - assert(base); - if(!checkClass(ce, base)) + if(ZEND_NUM_ARGS()) { - php_error_docref(0 TSRMLS_CC, E_ERROR, "object does not implement Ice_ObjectFactory"); - return; + if(Z_TYPE_PP(args[0]) == IS_ARRAY) + { + if(!extractStringArray(*args[0], seq TSRMLS_CC)) + { + RETURN_NULL(); + } + if(ZEND_NUM_ARGS() > 1) + { + if(Z_TYPE_PP(args[1]) != IS_OBJECT || Z_OBJCE_PP(args[1]) != initClass) + { + string s = zendTypeToString(Z_TYPE_PP(args[1])); + invalidArgument("expected InitializationData object but received %s" TSRMLS_CC, s.c_str()); + RETURN_NULL(); + } + zvinit = *args[1]; + } + } + else if(Z_TYPE_PP(args[0]) == IS_OBJECT && Z_OBJCE_PP(args[0]) == initClass) + { + if(ZEND_NUM_ARGS() > 1) + { + runtimeError("too many arguments" TSRMLS_CC); + RETURN_NULL(); + } + zvinit = *args[0]; + } + else + { + string s = zendTypeToString(Z_TYPE_PP(args[0])); + invalidArgument("unexpected argument type %s" TSRMLS_CC, s.c_str()); + RETURN_NULL(); + } } - ObjectFactoryMap* ofm = static_cast<ObjectFactoryMap*>(ICE_G(objectFactoryMap)); - ObjectFactoryMap::iterator p = ofm->find(id); - if(p != ofm->end()) + if(zvinit) { - Ice::AlreadyRegisteredException ex(__FILE__, __LINE__); - ex.kindOfObject = "object factory"; - ex.id = id; - throwException(ex TSRMLS_CC); - return; - } + void* data; + string member; - // - // Create a new zval with the same object handle as the factory. - // - zval* zv; - MAKE_STD_ZVAL(zv); - Z_TYPE_P(zv) = IS_OBJECT; - zv->value.obj = zfactory->value.obj; + member = "properties"; + if(zend_hash_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size() + 1, &data) == SUCCESS) + { + zval** val = reinterpret_cast<zval**>(data); + if(!fetchProperties(*val, initData.properties TSRMLS_CC)) + { + RETURN_NULL(); + } + } - // - // Increment the factory's reference count. - // - Z_OBJ_HT_P(zv)->add_ref(zv TSRMLS_CC); + member = "logger"; + if(zend_hash_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size() + 1, &data) == SUCCESS) + { + zval** val = reinterpret_cast<zval**>(data); + if(!fetchLogger(*val, initData.logger TSRMLS_CC)) + { + RETURN_NULL(); + } + } + } - // - // Update the factory map. - // - ofm->insert(ObjectFactoryMap::value_type(id, zv)); + CommunicatorInfoIPtr info = initializeCommunicator(return_value, seq, initData TSRMLS_CC); + if(!info) + { + RETURN_NULL(); + } } -ZEND_FUNCTION(Ice_Communicator_findObjectFactory) +ZEND_FUNCTION(Ice_register) { - if(ZEND_NUM_ARGS() != 1) + zval* comm; + char* s; + int sLen; + long expires = 0; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os|l", &comm, communicatorClassEntry, &s, &sLen, &expires + TSRMLS_CC) != SUCCESS) { - WRONG_PARAM_COUNT; + RETURN_NULL(); } - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + string id(s, sLen); + if(id.empty()) { + invalidArgument("communicator id cannot be empty" TSRMLS_CC); RETURN_NULL(); } - assert(obj->ptr); - char* id; - int len; + CommunicatorInfoIPtr info = Wrapper<CommunicatorInfoIPtr>::value(comm TSRMLS_CC); + assert(info); + + IceUtil::StaticMutex::Lock sync(_registeredCommunicatorsMutex); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, &len) == FAILURE) + RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id); + if(p != _registeredCommunicators.end()) { - RETURN_NULL(); + if(p->second->communicator != info->getCommunicator()) + { + // + // A different communicator is already registered with that ID. + // + RETURN_FALSE; + } } - - ObjectFactoryMap* ofm = static_cast<ObjectFactoryMap*>(ICE_G(objectFactoryMap)); - ObjectFactoryMap::iterator p = ofm->find(id); - if(p == ofm->end()) + else { - RETURN_NULL(); + info->ac->ids.push_back(id); + _registeredCommunicators[id] = info->ac; } - // - // Set the zval with the same object handle as the factory. - // - Z_TYPE_P(return_value) = IS_OBJECT; - return_value->value.obj = p->second->value.obj; + if(expires > 0) + { + // + // Update the expiration time. If a communicator is registered with multiple IDs, we + // always use the most recent expiration setting. + // + info->ac->expires = static_cast<int>(expires); + info->ac->lastAccess = IceUtil::Time::now(); - // - // Increment the factory's reference count. - // - Z_OBJ_HT_P(p->second)->add_ref(p->second TSRMLS_CC); + // + // Start the timer if necessary. Reap expired communicators every five minutes. + // + if(!_timer) + { + _timer = new IceUtil::Timer; + _timer->scheduleRepeated(new ReaperTask, IceUtil::Time::seconds(5 * 60)); + } + } + + RETURN_TRUE; } -ZEND_FUNCTION(Ice_Communicator_flushBatchRequests) +ZEND_FUNCTION(Ice_unregister) { - if(ZEND_NUM_ARGS() != 0) - { - WRONG_PARAM_COUNT; - } - - ice_object* obj = getObject(getThis() TSRMLS_CC); - if(!obj) + char* s; + int sLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s, &sLen TSRMLS_CC) != SUCCESS) { RETURN_NULL(); } - assert(obj->ptr); - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); - try - { - (*_this)->flushBatchRequests();; - } - catch(const IceUtil::Exception& ex) + string id(s, sLen); + + IceUtil::StaticMutex::Lock sync(_registeredCommunicatorsMutex); + + RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id); + if(p == _registeredCommunicators.end()) { - throwException(ex TSRMLS_CC); + // + // No communicator registered with that ID. + // + RETURN_FALSE; } + + // + // Remove the ID from the ActiveCommunicator's list of registered IDs. + // + ActiveCommunicatorPtr ac = p->second; + vector<string>::iterator q = find(ac->ids.begin(), ac->ids.end(), id); + assert(q != ac->ids.end()); + ac->ids.erase(q); + + _registeredCommunicators.erase(p); + + RETURN_TRUE; } -ZEND_FUNCTION(Ice_stringToIdentity) +ZEND_FUNCTION(Ice_find) { - if(ZEND_NUM_ARGS() != 1) + char* s; + int sLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &s, &sLen TSRMLS_CC) != SUCCESS) { - WRONG_PARAM_COUNT; + RETURN_NULL(); } - char* str; - int len; + string id(s, sLen); + + IceUtil::StaticMutex::Lock sync(_registeredCommunicatorsMutex); - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) + RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id); + if(p == _registeredCommunicators.end()) { + // + // No communicator registered with that ID. + // RETURN_NULL(); } - try + if(p->second->expires > 0) { - Ice::CommunicatorPtr communicator = getCommunicator(TSRMLS_C); - Ice::Identity id = communicator->stringToIdentity(str); - createIdentity(return_value, id TSRMLS_CC); + p->second->lastAccess = IceUtil::Time::now(); } - catch(const IceUtil::Exception& ex) + + // + // Check if this communicator has already been obtained by the current request. + // If so, we can return the existing PHP object that corresponds to the communicator. + // + CommunicatorMap* m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap)); + if(m) { - throwException(ex TSRMLS_CC); + CommunicatorMap::iterator q = m->find(p->second->communicator); + if(q != m->end()) + { + q->second->getZval(return_value TSRMLS_CC); + return; + } + } + + if(!createCommunicator(return_value, p->second TSRMLS_CC)) + { + RETURN_NULL(); } } -ZEND_FUNCTION(Ice_identityToString) +ZEND_FUNCTION(Ice_getProperties) { - if(ZEND_NUM_ARGS() != 1) + char* s = 0; + int sLen; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &s, &sLen TSRMLS_CC) != SUCCESS) { - WRONG_PARAM_COUNT; + RETURN_NULL(); } - zend_class_entry* cls = findClass("Ice_Identity" TSRMLS_CC); - assert(cls); - - zval *zid; + string name; + if(s) + { + name = string(s, sLen); + } - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zid, cls) == FAILURE) + ProfileMap::iterator p = _profiles.find(name); + if(p == _profiles.end()) { RETURN_NULL(); } - Ice::Identity id; - if(extractIdentity(zid, id TSRMLS_CC)) + Ice::PropertiesPtr clone = p->second->clone(); + if(!createProperties(return_value, clone TSRMLS_CC)) { - Ice::CommunicatorPtr communicator = getCommunicator(TSRMLS_C); - string s = communicator->identityToString(id); - RETURN_STRINGL(const_cast<char*>(s.c_str()), s.length(), 1); + RETURN_NULL(); } } -#ifdef _WIN32 -extern "C" -#endif -static zend_object_value -handleAlloc(zend_class_entry* ce TSRMLS_DC) +// +// Predefined methods for Communicator. +// +static function_entry _interfaceMethods[] = { - zend_object_value result; - - ice_object* obj = newObject(ce TSRMLS_CC); - assert(obj); + {0, 0, 0} +}; +static function_entry _classMethods[] = +{ + ZEND_ME(Ice_Communicator, __construct, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR) + ZEND_ME(Ice_Communicator, destroy, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, stringToProxy, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, proxyToString, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, propertyToProxy, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, stringToIdentity, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, identityToString, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, addObjectFactory, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, findObjectFactory, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, getImplicitContext, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, getProperties, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, getLogger, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, getDefaultRouter, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, setDefaultRouter, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, getDefaultLocator, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, setDefaultLocator, NULL, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Communicator, flushBatchRequests, NULL, ZEND_ACC_PUBLIC) + {0, 0, 0} +}; - result.handle = zend_objects_store_put(obj, 0, (zend_objects_free_object_storage_t)handleFreeStorage, 0 TSRMLS_CC); - result.handlers = &_handlers; +static bool +createProfile(const string& name, const string& config, const string& options TSRMLS_DC) +{ + ProfileMap::iterator p = _profiles.find(name); + if(p != _profiles.end()) + { + php_error_docref(0 TSRMLS_CC, E_WARNING, "duplicate Ice profile `%s'", name.c_str()); + return false; + } - return result; -} + Ice::PropertiesPtr properties = Ice::createProperties(); -#ifdef _WIN32 -extern "C" -#endif -static zend_object_value -handleClone(zval* zv TSRMLS_DC) -{ - zend_object_value result; - memset(&result, 0, sizeof(zend_object_value)); - php_error_docref(0 TSRMLS_CC, E_ERROR, "__clone is not supported for Ice_Communicator"); - return result; -} + if(!config.empty()) + { + try + { + properties->load(config); + } + catch(const IceUtil::Exception& ex) + { + ostringstream ostr; + ex.ice_print(ostr); + php_error_docref(0 TSRMLS_CC, E_WARNING, "unable to load Ice configuration file %s:\n%s", config.c_str(), + ostr.str().c_str()); + return false; + } + } -#ifdef _WIN32 -extern "C" -#endif -static void -handleFreeStorage(void* p TSRMLS_DC) -{ - ice_object* obj = static_cast<ice_object*>(p); - if(obj->ptr) + if(!options.empty()) { - Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); + vector<string> args; try { - (*_this)->destroy(); + args = IceUtilInternal::Options::split(options); } catch(const IceUtil::Exception& ex) { ostringstream ostr; ex.ice_print(ostr); - php_error_docref(0 TSRMLS_CC, E_ERROR, "unable to destroy communicator:\n%s", ostr.str().c_str()); + string msg = ostr.str(); + php_error_docref(0 TSRMLS_CC, E_WARNING, "error occurred while parsing the options `%s':\n%s", + options.c_str(), msg.c_str()); + return false; } - delete _this; + + properties->parseCommandLineOptions("", args); } - zend_objects_free_object_storage(reinterpret_cast<zend_object*>(p) TSRMLS_CC); + _profiles[name] = properties; + return true; } -#ifdef _WIN32 -extern "C" -#endif -static union _zend_function* -handleGetMethod(zval** zv, char* method, int len TSRMLS_DC) +static bool +parseProfiles(const string& file TSRMLS_DC) { // - // Delegate to the standard implementation of get_method. We're simply using this hook - // as a convenient way of implementing lazy initialization of the communicator. + // The Zend engine doesn't export a function for loading an INI file, so we + // have to do it ourselves. The format is: + // + // [profile-name] + // ice.config = config-file + // ice.options = args // - zend_function* result = zend_get_std_object_handlers()->get_method(zv, method, len TSRMLS_CC); - if(result) + ifstream in(file.c_str()); + if(!in) { - ice_object* obj = static_cast<ice_object*>(zend_object_store_get_object(*zv TSRMLS_CC)); - if(!obj->ptr) + php_error_docref(0 TSRMLS_CC, E_WARNING, "unable to open Ice profiles in %s", file.c_str()); + return false; + } + + string name, config, options; + char line[1024]; + while(in.getline(line, 1024)) + { + const string delim = " \t\r\n"; + string s = line; + + string::size_type idx = s.find(';'); + if(idx != string::npos) + { + s.erase(idx); + } + + idx = s.find_last_not_of(delim); + if(idx != string::npos && idx + 1 < s.length()) + { + s.erase(idx + 1); + } + + string::size_type beg = s.find_first_not_of(delim); + if(beg == string::npos) { - if(!ICE_G(profile)) + continue; + } + + if(s[beg] == '[') + { + beg++; + string::size_type end = s.find_first_of(" \t]", beg); + if(end == string::npos || s[s.length() - 1] != ']') { - php_error_docref(0 TSRMLS_CC, E_ERROR, "$ICE used before a profile was loaded"); - return 0; + php_error_docref(0 TSRMLS_CC, E_WARNING, "invalid profile section in file %s:\n%s\n", file.c_str(), + line); + return false; } - try + if(!name.empty()) { - initCommunicator(obj TSRMLS_CC); + createProfile(name, config, options TSRMLS_CC); + config.clear(); + options.clear(); } - catch(const IceUtil::Exception& ex) + + name = s.substr(beg, end - beg); + } + else + { + string::size_type end = s.find_first_of(delim + "=", beg); + assert(end != string::npos); + + string key = s.substr(beg, end - beg); + + end = s.find('=', end); + if(end == string::npos) + { + php_error_docref(0 TSRMLS_CC, E_WARNING, "invalid profile entry in file %s:\n%s\n", file.c_str(), line); + return false; + } + ++end; + + string value; + beg = s.find_first_not_of(delim, end); + if(beg != string::npos) + { + end = s.length(); + value = s.substr(beg, end - beg); + } + + if(key == "config" || key == "ice.config") + { + config = value; + } + else if(key == "options" || key == "ice.options") + { + options = value; + } + else + { + php_error_docref(0 TSRMLS_CC, E_WARNING, "unknown profile entry in file %s:\n%s\n", file.c_str(), line); + } + + if(name.empty()) { - ostringstream ostr; - ex.ice_print(ostr); - php_error_docref(0 TSRMLS_CC, E_ERROR, "unable to initialize communicator:\n%s", ostr.str().c_str()); - return 0; + php_error_docref(0 TSRMLS_CC, E_WARNING, "no section for profile entry in file %s:\n%s\n", file.c_str(), + line); + return false; } } } - return result; + if(!name.empty()) + { + if(!createProfile(name, config, options TSRMLS_CC)) + { + return false; + } + } + + return true; } -// -// Initialize a communicator instance and store it in the given object. Can raise exceptions. -// -static void -initCommunicator(ice_object* obj TSRMLS_DC) +bool +IcePHP::communicatorInit(TSRMLS_D) { - assert(!obj->ptr); + // + // We register an interface and a class that implements the interface. This allows + // applications to safely include the Slice-generated code for the type. + // + + // + // Register the Communicator interface. + // + zend_class_entry ce; +#ifdef ICEPHP_USE_NAMESPACES + INIT_NS_CLASS_ENTRY(ce, STRCAST("Ice"), STRCAST("Communicator"), _interfaceMethods); +#else + INIT_CLASS_ENTRY(ce, "Ice_Communicator", _interfaceMethods); +#endif + zend_class_entry* interface = zend_register_internal_interface(&ce TSRMLS_CC); - Ice::PropertiesPtr* properties = static_cast<Ice::PropertiesPtr*>(ICE_G(properties)); + // + // Register the Communicator class. + // + INIT_CLASS_ENTRY(ce, "IcePHP_Communicator", _classMethods); + ce.create_object = handleAlloc; + communicatorClassEntry = zend_register_internal_class(&ce TSRMLS_CC); + memcpy(&_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + _handlers.clone_obj = handleClone; + zend_class_implements(communicatorClassEntry TSRMLS_CC, 1, interface); - Ice::InitializationData initData; - initData.properties = *properties; - Ice::CommunicatorPtr communicator = Ice::initialize(initData); - obj->ptr = new Ice::CommunicatorPtr(communicator); + // + // Create the profiles from configuration settings. + // + const char* config = INI_STR("ice.config"); + const char* options = INI_STR("ice.options"); + if(!createProfile(_defaultProfileName, config, options TSRMLS_CC)) + { + return false; + } + + const char* profiles = INI_STR("ice.profiles"); + if(strlen(profiles) > 0) + { + if(!parseProfiles(profiles TSRMLS_CC)) + { + return false; + } + + if(INI_BOOL("ice.hide_profiles")) + { + memset(const_cast<char*>(profiles), '*', strlen(profiles)); + // + // For some reason the code below does not work as expected. It causes a call + // to ini_get_all() to segfault. + // + /* + if(zend_alter_ini_entry("ice.profiles", sizeof("ice.profiles"), "<hidden>", sizeof("<hidden>") - 1, + PHP_INI_ALL, PHP_INI_STAGE_STARTUP) == FAILURE) + { + return false; + } + */ + } + } + + return true; +} + +bool +IcePHP::communicatorShutdown(TSRMLS_D) +{ + _profiles.clear(); + + IceUtil::StaticMutex::Lock sync(_registeredCommunicatorsMutex); + + if(_timer) + { + _timer->destroy(); + _timer = 0; + } + + // + // Clearing the map releases the last remaining reference counts of the ActiveCommunicator + // objects. The ActiveCommunicator destructor destroys its communicator. + // + _registeredCommunicators.clear(); + + return true; +} + +bool +IcePHP::communicatorRequestInit(TSRMLS_D) +{ + ICE_G(communicatorMap) = 0; + + return true; +} + +bool +IcePHP::communicatorRequestShutdown(TSRMLS_D) +{ + if(ICE_G(communicatorMap)) + { + CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap)); + for(CommunicatorMap::iterator p = m->begin(); p != m->end(); ++p) + { + CommunicatorInfoIPtr info = p->second; + + // + // We need to destroy any object factories installed during this request. + // + info->destroyObjectFactories(TSRMLS_C); + } + + // + // Deleting the map decrements the reference count of its ActiveCommunicator + // values. If there are no other references to an ActiveCommunicator, its + // destructor destroys the communicator. + // + delete m; + } + + return true; +} + +IcePHP::ActiveCommunicator::ActiveCommunicator(const Ice::CommunicatorPtr& c) : + communicator(c), expires(0) +{ +} +IcePHP::ActiveCommunicator::~ActiveCommunicator() +{ // - // Register our default object factory with the communicator. + // There are no more references to this communicator, so we can safely destroy it now. // - Ice::ObjectFactoryPtr factory = new PHPObjectFactory(TSRMLS_C); - communicator->addObjectFactory(factory, ""); + try + { + communicator->destroy(); + } + catch(...) + { + } +} + +IcePHP::CommunicatorInfoI::CommunicatorInfoI(const ActiveCommunicatorPtr& c, zval* z) : + ac(c), + zv(*z) // This is legal - it simply copies the object's handle. +{ +} + +void +IcePHP::CommunicatorInfoI::getZval(zval* z TSRMLS_DC) +{ + Z_TYPE_P(z) = IS_OBJECT; + z->value.obj = zv.value.obj; + addRef(TSRMLS_C); +} + +void +IcePHP::CommunicatorInfoI::addRef(TSRMLS_D) +{ + zval* p = const_cast<zval*>(&zv); + Z_OBJ_HT_P(p)->add_ref(p TSRMLS_CC); +} + +void +IcePHP::CommunicatorInfoI::decRef(TSRMLS_D) +{ + zval* p = const_cast<zval*>(&zv); + Z_OBJ_HT(zv)->del_ref(p TSRMLS_CC); +} + +Ice::CommunicatorPtr +IcePHP::CommunicatorInfoI::getCommunicator() const +{ + return ac->communicator; +} + +bool +IcePHP::CommunicatorInfoI::addObjectFactory(const string& id, zval* factory TSRMLS_DC) +{ + ObjectFactoryMap::iterator p = objectFactories.find(id); + if(p != objectFactories.end()) + { + Ice::AlreadyRegisteredException ex(__FILE__, __LINE__); + ex.kindOfObject = "object factory"; + ex.id = id; + throwException(ex TSRMLS_CC); + return false; + } + + objectFactories.insert(ObjectFactoryMap::value_type(id, factory)); + Z_ADDREF_P(factory); + + return true; +} + +bool +IcePHP::CommunicatorInfoI::findObjectFactory(const string& id, zval* zv TSRMLS_DC) +{ + ObjectFactoryMap::iterator p = objectFactories.find(id); + if(p != objectFactories.end()) + { + *zv = *p->second; // This is legal - it simply copies the object's handle. + INIT_PZVAL(zv); + zval_copy_ctor(zv); + return true; + } + + return false; +} + +void +IcePHP::CommunicatorInfoI::destroyObjectFactories(TSRMLS_D) +{ + for(ObjectFactoryMap::iterator p = objectFactories.begin(); p != objectFactories.end(); ++p) + { + // + // Invoke the destroy method on each registered PHP factory. + // + invokeMethod(p->second, "destroy" TSRMLS_CC); + zend_clear_exception(TSRMLS_C); + zval_ptr_dtor(&p->second); + } +} + +IcePHP::ObjectFactoryI::ObjectFactoryI(const Ice::CommunicatorPtr& communicator) : + _communicator(communicator) +{ +} + +Ice::ObjectPtr +IcePHP::ObjectFactoryI::create(const string& id) +{ + // + // Get the TSRM id for the current request. + // + TSRMLS_FETCH(); + + CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap)); + assert(m); + CommunicatorMap::iterator p = m->find(_communicator); + assert(p != m->end()); + + CommunicatorInfoIPtr info = p->second; + + zval* factory = 0; + + // + // Check if the application has registered a factory for this id. + // + ObjectFactoryMap::iterator q = info->objectFactories.find(id); + if(q == info->objectFactories.end()) + { + q = info->objectFactories.find(""); // Look for a default factory. + } + if(q != info->objectFactories.end()) + { + factory = q->second; + } + + // + // Get the type information. + // + ClassInfoPtr cls = getClassInfoById(id TSRMLS_CC); + if(!cls) + { + return 0; + } + + if(factory) + { + zval* arg; + MAKE_STD_ZVAL(arg); + ZVAL_STRINGL(arg, STRCAST(id.c_str()), id.length(), 1); + + zval* obj = 0; + + zend_try + { + zend_call_method_with_1_params(&factory, 0, 0, "create", &obj, arg); + } + zend_catch + { + obj = 0; + } + zend_end_try(); + + zval_ptr_dtor(&arg); + + // + // Bail out if an exception has already been thrown. + // + if(!obj || EG(exception)) + { + throw AbortMarshaling(); + } + + AutoDestroy destroy(obj); + + if(Z_TYPE_P(obj) == IS_NULL) + { + return 0; + } + + return new ObjectReader(obj, cls, info TSRMLS_CC); + } + + // + // If the requested type is an abstract class, then we give up. + // + if(cls->isAbstract) + { + return 0; + } + + // + // Instantiate the object. + // + zval* obj; + MAKE_STD_ZVAL(obj); + AutoDestroy destroy(obj); + + if(object_init_ex(obj, cls->zce) != SUCCESS) + { + throw AbortMarshaling(); + } + + if(!invokeMethod(obj, ZEND_CONSTRUCTOR_FUNC_NAME TSRMLS_CC)) + { + throw AbortMarshaling(); + } + + return new ObjectReader(obj, cls, info TSRMLS_CC); +} + +void +IcePHP::ObjectFactoryI::destroy() +{ + _communicator = 0; +} + +void +IcePHP::ReaperTask::runTimerTask() +{ + IceUtil::StaticMutex::Lock sync(_registeredCommunicatorsMutex); + + IceUtil::Time now = IceUtil::Time::now(); + RegisteredCommunicatorMap::iterator p = _registeredCommunicators.begin(); + while(p != _registeredCommunicators.end()) + { + if(p->second->lastAccess + IceUtil::Time::seconds(p->second->expires * 60) <= now) + { + try + { + p->second->communicator->destroy(); + } + catch(...) + { + } + _registeredCommunicators.erase(p++); + } + else + { + ++p; + } + } } |