summaryrefslogtreecommitdiff
path: root/php/src/php7/Communicator.cpp
diff options
context:
space:
mode:
authorJoe George <joe@zeroc.com>2016-02-12 11:10:28 -0500
committerJoe George <joe@zeroc.com>2016-02-12 11:10:28 -0500
commit70fcaab8e9566edd7d749b1f6e3147e0efdbe94d (patch)
tree6f043cce81cf206a9ac7f1ff2fc4349a80aa23c3 /php/src/php7/Communicator.cpp
parentminor fixes to stream classes; restoring test/Ice/stream (diff)
downloadice-70fcaab8e9566edd7d749b1f6e3147e0efdbe94d.tar.bz2
ice-70fcaab8e9566edd7d749b1f6e3147e0efdbe94d.tar.xz
ice-70fcaab8e9566edd7d749b1f6e3147e0efdbe94d.zip
ICE-6973 - Add support for PHP 7
Diffstat (limited to 'php/src/php7/Communicator.cpp')
-rw-r--r--php/src/php7/Communicator.cpp2112
1 files changed, 2112 insertions, 0 deletions
diff --git a/php/src/php7/Communicator.cpp b/php/src/php7/Communicator.cpp
new file mode 100644
index 00000000000..cfe9f2c00ff
--- /dev/null
+++ b/php/src/php7/Communicator.cpp
@@ -0,0 +1,2112 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2015 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 <Communicator.h>
+#include <Logger.h>
+#include <Properties.h>
+#include <Proxy.h>
+#include <Types.h>
+#include <Util.h>
+#include <IceUtil/DisableWarnings.h>
+#include <IceUtil/Options.h>
+#include <IceUtil/MutexPtrLock.h>
+#include <IceUtil/StringUtil.h>
+#include <IceUtil/Timer.h>
+
+using namespace std;
+using namespace IcePHP;
+
+ZEND_EXTERN_MODULE_GLOBALS(ice)
+
+//
+// Class entries represent the PHP class implementations we have registered.
+//
+namespace IcePHP
+{
+
+zend_class_entry* communicatorClassEntry = 0;
+zend_class_entry* valueFactoryManagerClassEntry = 0;
+
+//
+// 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.
+//
+class ActiveCommunicator : public IceUtil::Shared
+{
+public:
+
+ ActiveCommunicator(const Ice::CommunicatorPtr& c);
+ ~ActiveCommunicator();
+
+ const Ice::CommunicatorPtr communicator;
+ vector<string> ids;
+ int expires;
+ IceUtil::Time lastAccess;
+};
+typedef IceUtil::Handle<ActiveCommunicator> ActiveCommunicatorPtr;
+
+class FactoryWrapper;
+typedef IceUtil::Handle<FactoryWrapper> FactoryWrapperPtr;
+
+class DefaultValueFactory;
+typedef IceUtil::Handle<DefaultValueFactory> DefaultValueFactoryPtr;
+
+//
+// CommunicatorInfoI encapsulates communicator-related information that
+// is specific to a PHP "request". In other words, multiple PHP requests
+// might share the same communicator instance but still need separate
+// workspaces. For example, we don't want the value factories installed
+// by one request to influence the behavior of another request.
+//
+class CommunicatorInfoI : public CommunicatorInfo
+{
+public:
+
+ CommunicatorInfoI(const ActiveCommunicatorPtr&, zval*);
+
+ virtual void getZval(zval*);
+ virtual void addRef(void);
+ virtual void decRef(void);
+
+ virtual Ice::CommunicatorPtr getCommunicator() const;
+
+ bool addFactory(zval*, const string&, bool);
+ FactoryWrapperPtr findFactory(const string&) const;
+ Ice::ValueFactoryPtr defaultFactory() const { return _defaultFactory; }
+ void destroyFactories(void);
+
+ const ActiveCommunicatorPtr ac;
+ zval zv;
+
+private:
+
+ typedef map<string, FactoryWrapperPtr> FactoryMap;
+
+ FactoryMap _factories;
+ DefaultValueFactoryPtr _defaultFactory;
+};
+typedef IceUtil::Handle<CommunicatorInfoI> CommunicatorInfoIPtr;
+
+//
+// Wraps a PHP object/value factory.
+//
+class FactoryWrapper : public Ice::ValueFactory
+{
+public:
+
+ FactoryWrapper(zval*, bool, const CommunicatorInfoIPtr&);
+
+ virtual Ice::ValuePtr create(const string&);
+
+ void getZval(zval*);
+
+ bool isObjectFactory() const;
+
+ void destroy(void);
+
+protected:
+
+ zval _factory;
+ bool _isObjectFactory;
+ CommunicatorInfoIPtr _info;
+};
+
+//
+// Implements the default value factory behavior.
+//
+class DefaultValueFactory : public Ice::ValueFactory
+{
+public:
+
+ DefaultValueFactory(const CommunicatorInfoIPtr&);
+
+ virtual Ice::ValuePtr create(const string&);
+
+ void setDelegate(const FactoryWrapperPtr& d) { _delegate = d; }
+ FactoryWrapperPtr getDelegate() const { return _delegate; }
+
+ void destroy(void);
+
+private:
+
+ FactoryWrapperPtr _delegate;
+ CommunicatorInfoIPtr _info;
+};
+
+//
+// Each PHP request has its own set of value factories. More precisely, there is
+// a value factory map for each communicator that is created by a PHP request.
+// (see CommunicatorInfoI).
+//
+// We define a custom value factory manager implementation that delegates to
+// to PHP objects supplied by the application.
+//
+// An instance of this class is installed as the communicator's value factory
+// manager, and the class holds a reference to its communicator. When find() is
+// invoked, the class resolves the appropriate factory as follows:
+//
+// * Using its communicator reference as the key, look up the corresponding
+// CommunicatorInfoI object in the request-specific communicator map.
+//
+// * If the type-id is empty, return the default factory. This factory will
+// either delegate to an application-supplied default factory (if present) or
+// default-construct an instance of a concrete Slice class type.
+//
+// * For non-empty type-ids, return a wrapper around the application-supplied
+// factory, if any.
+//
+class ValueFactoryManager : public Ice::ValueFactoryManager
+{
+public:
+
+ virtual void add(const Ice::ValueFactoryPtr&, const string&);
+ virtual Ice::ValueFactoryPtr find(const string&) const;
+
+ void setCommunicator(const Ice::CommunicatorPtr& c) { _communicator = c; }
+ Ice::CommunicatorPtr getCommunicator() const { return _communicator; }
+
+ void getZval(zval*);
+
+ void destroy();
+
+private:
+
+ Ice::CommunicatorPtr _communicator;
+};
+typedef IceUtil::Handle<ValueFactoryManager> ValueFactoryManagerPtr;
+
+class ReaperTask : public IceUtil::TimerTask
+{
+public:
+
+ virtual void runTimerTask();
+};
+
+}
+
+namespace
+{
+//
+// Communicator support.
+//
+zend_object_handlers _handlers;
+
+//
+// ValueFactoryManager support.
+//
+zend_object_handlers _vfmHandlers;
+
+//
+// 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;
+ProfileMap _profiles;
+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;
+RegisteredCommunicatorMap _registeredCommunicators;
+IceUtil::Mutex* _registeredCommunicatorsMutex = 0;
+
+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;
+
+class Init
+{
+public:
+
+ Init()
+ {
+ _registeredCommunicatorsMutex = new IceUtil::Mutex();
+ }
+
+ ~Init()
+ {
+ delete _registeredCommunicatorsMutex;
+ _registeredCommunicatorsMutex = 0;
+ }
+};
+
+Init init;
+}
+
+extern "C"
+{
+static zend_object* handleAlloc(zend_class_entry*);
+static void handleFreeStorage(zend_object*);
+static zend_object* handleClone(zval*);
+
+static zend_object* handleVfmAlloc(zend_class_entry*);
+static void handleVfmFreeStorage(zend_object*);
+static zend_object* handleVfmClone(zval*);
+}
+
+ZEND_METHOD(Ice_Communicator, __construct)
+{
+ runtimeError("communicators cannot be instantiated directly");
+}
+
+ZEND_METHOD(Ice_Communicator, destroy)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ //
+ // Remove all registrations.
+ //
+ {
+ IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
+ for(vector<string>::iterator p = _this->ac->ids.begin(); p != _this->ac->ids.end(); ++p)
+ {
+ _registeredCommunicators.erase(*p);
+ }
+ _this->ac->ids.clear();
+ }
+
+ //
+ // We need to destroy any object|value factories installed by this request.
+ //
+ _this->destroyFactories();
+
+ 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);
+
+ ValueFactoryManagerPtr vfm = ValueFactoryManagerPtr::dynamicCast(c->getValueFactoryManager());
+ assert(vfm);
+ vfm->destroy();
+
+ try
+ {
+ c->destroy();
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, stringToProxy)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ 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);
+
+ try
+ {
+ Ice::ObjectPrx prx = _this->getCommunicator()->stringToProxy(s);
+ if(!createProxy(return_value, prx, _this))
+ {
+ RETURN_NULL();
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, proxyToString)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ zval* zv;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!"), &zv, proxyClassEntry) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ try
+ {
+ string str;
+ if(zv)
+ {
+ Ice::ObjectPrx prx;
+ ClassInfoPtr info;
+ if(!fetchProxy(zv, prx, info))
+ {
+ RETURN_NULL();
+ }
+ assert(prx);
+ str = prx->ice_toString();
+ }
+ RETURN_STRINGL(STRCAST(str.c_str()), static_cast<int>(str.length()));
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, propertyToProxy)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ 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);
+
+ try
+ {
+ Ice::ObjectPrx prx = _this->getCommunicator()->propertyToProxy(s);
+ if(!createProxy(return_value, prx, _this))
+ {
+ RETURN_NULL();
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, proxyToProperty)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ zval* zv;
+ char* str;
+ size_t strLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!s"), &zv, proxyClassEntry, &str, &strLen)
+ != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string prefix(str, strLen);
+
+ try
+ {
+ if(zv)
+ {
+ Ice::ObjectPrx prx;
+ ClassInfoPtr info;
+ if(!fetchProxy(zv, prx, info))
+ {
+ RETURN_NULL();
+ }
+ assert(prx);
+
+ Ice::PropertyDict val = _this->getCommunicator()->proxyToProperty(prx, prefix);
+ if(!createStringMap(return_value, val))
+ {
+ RETURN_NULL();
+ }
+ }
+ else
+ {
+ array_init(return_value);
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, stringToIdentity)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ 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);
+
+ try
+ {
+ Ice::Identity id = _this->getCommunicator()->stringToIdentity(s);
+ if(!createIdentity(return_value, id))
+ {
+ RETURN_NULL();
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, identityToString)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ zend_class_entry* identityClass = idToClass("::Ice::Identity");
+ assert(identityClass);
+
+ zval* zv;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O"), &zv, identityClass) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+ Ice::Identity id;
+ if(!extractIdentity(zv, id))
+ {
+ RETURN_NULL();
+ }
+
+ try
+ {
+ string str = _this->getCommunicator()->identityToString(id);
+ RETURN_STRINGL(STRCAST(str.c_str()), static_cast<int>(str.length()));
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, addObjectFactory)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ zend_class_entry* factoryClass = idToClass("Ice::ObjectFactory");
+ assert(factoryClass);
+
+ zval* factory;
+ char* id;
+ size_t idLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("Os!"), &factory, factoryClass, &id,
+ &idLen) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string type;
+ if(id)
+ {
+ type = string(id, idLen);
+ }
+
+ if(!_this->addFactory(factory, type, true))
+ {
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, findObjectFactory)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ char* id;
+ size_t idLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s!"), &id, &idLen) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string type;
+ if(id)
+ {
+ type = string(id, idLen);
+ }
+
+ FactoryWrapperPtr w = _this->findFactory(type);
+ if(w && w->isObjectFactory())
+ {
+ w->getZval(return_value);
+ }
+ else
+ {
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, getValueFactoryManager)
+{
+ if(ZEND_NUM_ARGS() > 0)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ try
+ {
+ ValueFactoryManagerPtr vfm =
+ ValueFactoryManagerPtr::dynamicCast(_this->getCommunicator()->getValueFactoryManager());
+ assert(vfm);
+ if(object_init_ex(return_value, valueFactoryManagerClassEntry) != SUCCESS)
+ {
+ runtimeError("unable to initialize properties object");
+ RETURN_NULL();
+ }
+
+ Wrapper<ValueFactoryManagerPtr>* obj = Wrapper<ValueFactoryManagerPtr>::extract(return_value);
+ assert(!obj->ptr);
+ obj->ptr = new ValueFactoryManagerPtr(vfm);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, getImplicitContext)
+{
+ runtimeError("not implemented");
+}
+
+ZEND_METHOD(Ice_Communicator, getProperties)
+{
+ if(ZEND_NUM_ARGS() > 0)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ try
+ {
+ Ice::PropertiesPtr props = _this->getCommunicator()->getProperties();
+ if(!createProperties(return_value, props))
+ {
+ RETURN_NULL();
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, getLogger)
+{
+ if(ZEND_NUM_ARGS() > 0)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ try
+ {
+ Ice::LoggerPtr logger = _this->getCommunicator()->getLogger();
+ if(!createLogger(return_value, logger))
+ {
+ RETURN_NULL();
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, getDefaultRouter)
+{
+ if(ZEND_NUM_ARGS() > 0)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ try
+ {
+ Ice::RouterPrx router = _this->getCommunicator()->getDefaultRouter();
+ if(router)
+ {
+ ClassInfoPtr info = getClassInfoById("::Ice::Router");
+ if(!info)
+ {
+ runtimeError("no definition for Ice::Router");
+ RETURN_NULL();
+ }
+ if(!createProxy(return_value, router, info, _this))
+ {
+ RETURN_NULL();
+ }
+ }
+ else
+ {
+ RETURN_NULL();
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, setDefaultRouter)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ zval* zv;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!"), &zv, proxyClassEntry) !=
+ SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ Ice::ObjectPrx proxy;
+ ClassInfoPtr info;
+ if(zv && !fetchProxy(zv, proxy, info))
+ {
+ RETURN_NULL();
+ }
+
+ try
+ {
+ Ice::RouterPrx router;
+ if(proxy)
+ {
+ if(!info || !info->isA("::Ice::Router"))
+ {
+ invalidArgument("setDefaultRouter requires a proxy narrowed to Ice::Router");
+ RETURN_NULL();
+ }
+ router = Ice::RouterPrx::uncheckedCast(proxy);
+ }
+ _this->getCommunicator()->setDefaultRouter(router);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, getDefaultLocator)
+{
+ if(ZEND_NUM_ARGS() > 0)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ try
+ {
+ Ice::LocatorPrx locator = _this->getCommunicator()->getDefaultLocator();
+ if(locator)
+ {
+ ClassInfoPtr info = getClassInfoById("::Ice::Locator");
+ if(!info)
+ {
+ runtimeError("no definition for Ice::Locator");
+ RETURN_NULL();
+ }
+ if(!createProxy(return_value, locator, info, _this))
+ {
+ RETURN_NULL();
+ }
+ }
+ else
+ {
+ RETURN_NULL();
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, setDefaultLocator)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ zval* zv;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!"), &zv, proxyClassEntry) !=
+ SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ Ice::ObjectPrx proxy;
+ ClassInfoPtr info;
+ if(zv && !fetchProxy(zv, proxy, info))
+ {
+ RETURN_NULL();
+ }
+
+ try
+ {
+ Ice::LocatorPrx locator;
+ if(proxy)
+ {
+ if(!info || !info->isA("::Ice::Locator"))
+ {
+ invalidArgument("setDefaultLocator requires a proxy narrowed to Ice::Locator");
+ RETURN_NULL();
+ }
+ locator = Ice::LocatorPrx::uncheckedCast(proxy);
+ }
+ _this->getCommunicator()->setDefaultLocator(locator);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_Communicator, flushBatchRequests)
+{
+ CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
+ assert(_this);
+
+ if(ZEND_NUM_ARGS() != 8)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ try
+ {
+ _this->getCommunicator()->flushBatchRequests();
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_ValueFactoryManager, __construct)
+{
+ runtimeError("value factory managers cannot be instantiated directly");
+}
+
+ZEND_METHOD(Ice_ValueFactoryManager, add)
+{
+ ValueFactoryManagerPtr _this = Wrapper<ValueFactoryManagerPtr>::value(getThis());
+ assert(_this);
+
+ zend_class_entry* factoryClass = idToClass("Ice::ValueFactory");
+ assert(factoryClass);
+
+ zval* factory;
+ char* id;
+ size_t idLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("Os!"), &factory, factoryClass, &id,
+ &idLen) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string type;
+ if(id)
+ {
+ type = string(id, idLen);
+ }
+
+ CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
+ assert(m);
+ CommunicatorMap::iterator p = m->find(_this->getCommunicator());
+ assert(p != m->end());
+
+ CommunicatorInfoIPtr info = p->second;
+
+ if(!info->addFactory(factory, type, false))
+ {
+ RETURN_NULL();
+ }
+}
+
+ZEND_METHOD(Ice_ValueFactoryManager, find)
+{
+ ValueFactoryManagerPtr _this = Wrapper<ValueFactoryManagerPtr>::value(getThis());
+ assert(_this);
+
+ char* id;
+ size_t idLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s!"), &id, &idLen) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string type;
+ if(id)
+ {
+ type = string(id, idLen);
+ }
+
+ CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
+ assert(m);
+ CommunicatorMap::iterator p = m->find(_this->getCommunicator());
+ assert(p != m->end());
+
+ CommunicatorInfoIPtr info = p->second;
+
+ FactoryWrapperPtr w = info->findFactory(type);
+ if(w)
+ {
+ w->getZval(return_value);
+ }
+ else
+ {
+ RETURN_NULL();
+ }
+}
+
+#ifdef _WIN32
+extern "C"
+#endif
+static zend_object*
+handleAlloc(zend_class_entry* ce)
+{
+ Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::create(ce);
+ assert(obj);
+
+ obj->zobj.handlers = &_handlers;
+
+ return &obj->zobj;
+}
+
+#ifdef _WIN32
+extern "C"
+#endif
+static void
+handleFreeStorage(zend_object* object)
+{
+ Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::fetch(object);
+ assert(obj);
+
+ delete obj->ptr;
+ zend_object_std_dtor(object);
+}
+
+#ifdef _WIN32
+extern "C"
+#endif
+static zend_object*
+handleClone(zval* zv)
+{
+ php_error_docref(0, E_ERROR, "communicators cannot be cloned");
+ return 0;
+}
+
+#ifdef _WIN32
+extern "C"
+#endif
+static zend_object*
+handleVfmAlloc(zend_class_entry* ce)
+{
+ Wrapper<ValueFactoryManagerPtr>* obj = Wrapper<ValueFactoryManagerPtr>::create(ce);
+ assert(obj);
+
+ obj->zobj.handlers = &_vfmHandlers;
+
+ return &obj->zobj;
+}
+
+#ifdef _WIN32
+extern "C"
+#endif
+static void
+handleVfmFreeStorage(zend_object* object)
+{
+ Wrapper<ValueFactoryManagerPtr>* obj = Wrapper<ValueFactoryManagerPtr>::fetch(object);
+ assert(obj);
+
+ delete obj->ptr;
+ zend_object_std_dtor(object);
+}
+
+#ifdef _WIN32
+extern "C"
+#endif
+static zend_object*
+handleVfmClone(zval* zv)
+{
+ php_error_docref(0, E_ERROR, "value factory managers cannot be cloned");
+ return 0;
+}
+
+static CommunicatorInfoIPtr
+createCommunicator(zval* zv, const ActiveCommunicatorPtr& ac)
+{
+ try
+ {
+ if(object_init_ex(zv, communicatorClassEntry) != SUCCESS)
+ {
+ runtimeError("unable to initialize communicator object");
+ return 0;
+ }
+
+ Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::extract(zv);
+ 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);
+ return 0;
+ }
+}
+
+static CommunicatorInfoIPtr
+initializeCommunicator(zval* zv, Ice::StringSeq& args, bool hasArgs, const Ice::InitializationData& initData)
+{
+ try
+ {
+ Ice::CommunicatorPtr c;
+ if(hasArgs)
+ {
+ c = Ice::initialize(args, initData);
+ }
+ else
+ {
+ c = Ice::initialize(initData);
+ }
+ ActiveCommunicatorPtr ac = new ActiveCommunicator(c);
+
+ ValueFactoryManagerPtr vfm = ValueFactoryManagerPtr::dynamicCast(c->getValueFactoryManager());
+ assert(vfm);
+ vfm->setCommunicator(c);
+
+ CommunicatorInfoIPtr info = createCommunicator(zv, ac);
+ if(!info)
+ {
+ try
+ {
+ c->destroy();
+ }
+ catch(...)
+ {
+ }
+
+ vfm->destroy();
+ }
+
+ return info;
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ throwException(ex);
+ return 0;
+ }
+}
+
+ZEND_FUNCTION(Ice_initialize)
+{
+ if(ZEND_NUM_ARGS() > 2)
+ {
+ runtimeError("too many arguments");
+ RETURN_NULL();
+ }
+
+ zend_class_entry* initClass = idToClass("::Ice::InitializationData");
+ assert(initClass);
+
+ //
+ // 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)
+ {
+ runtimeError("unable to get arguments");
+ RETURN_NULL();
+ }
+
+ Ice::StringSeq seq;
+ Ice::InitializationData initData;
+ zval* zvargs = 0;
+ zval* zvinit = 0;
+
+ //
+ // Accept the following invocations:
+ //
+ // initialize(array, InitializationData)
+ // initialize(array)
+ // initialize(InitializationData)
+ // initialize()
+ //
+ bool hasArgs = false;
+ if(ZEND_NUM_ARGS())
+ {
+ if(Z_TYPE(args[0]) == IS_ARRAY)
+ {
+ if(!extractStringArray(&args[0], seq))
+ {
+ RETURN_NULL();
+ }
+ zvargs = &args[0];
+ hasArgs = true;
+ if(ZEND_NUM_ARGS() > 1)
+ {
+ if(Z_TYPE(args[1]) != IS_OBJECT || Z_OBJCE(args[1]) != initClass)
+ {
+ string s = zendTypeToString(Z_TYPE(args[1]));
+ invalidArgument("expected InitializationData object but received %s", s.c_str());
+ RETURN_NULL();
+ }
+ zvinit = &args[1];
+ }
+ }
+ else if(Z_TYPE(args[0]) == IS_OBJECT && Z_OBJCE(args[0]) == initClass)
+ {
+ if(ZEND_NUM_ARGS() > 1)
+ {
+ runtimeError("too many arguments");
+ RETURN_NULL();
+ }
+ zvinit = &args[0];
+ }
+ else
+ {
+ string s = zendTypeToString(Z_TYPE(args[0]));
+ invalidArgument("unexpected argument type %s", s.c_str());
+ RETURN_NULL();
+ }
+ }
+
+ if(zvinit)
+ {
+ zval* data;
+ string member;
+
+ member = "properties";
+ {
+ if((data = zend_hash_str_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size())))
+ {
+ assert(Z_TYPE_P(data) == IS_INDIRECT);
+ if(!fetchProperties(Z_INDIRECT_P(data), initData.properties))
+ {
+ RETURN_NULL();
+ }
+ }
+ }
+
+ member = "logger";
+ {
+ if((data = zend_hash_str_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size())))
+ {
+ assert(Z_TYPE_P(data) == IS_INDIRECT);
+ if(!fetchLogger(Z_INDIRECT_P(data), initData.logger))
+ {
+ RETURN_NULL();
+ }
+ }
+ }
+ }
+
+ initData.compactIdResolver = new IdResolver();
+ initData.valueFactoryManager = new ValueFactoryManager;
+
+ CommunicatorInfoIPtr info = initializeCommunicator(return_value, seq, hasArgs, initData);
+ if(!info)
+ {
+ RETURN_NULL();
+ }
+
+ if(zvargs && Z_ISREF_P(zvargs))
+ {
+ zval_dtor(zvargs);
+ if(!createStringArray(zvargs, seq))
+ {
+ RETURN_NULL();
+ }
+ }
+}
+
+ZEND_FUNCTION(Ice_register)
+{
+ zval* comm;
+ char* s;
+ size_t sLen;
+ long expires = 0;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("Os|l"), &comm, communicatorClassEntry, &s,
+ &sLen, &expires) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string id(s, sLen);
+ if(id.empty())
+ {
+ invalidArgument("communicator id cannot be empty");
+ RETURN_NULL();
+ }
+
+ CommunicatorInfoIPtr info = Wrapper<CommunicatorInfoIPtr>::value(comm);
+ assert(info);
+
+ IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
+
+ RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
+ if(p != _registeredCommunicators.end())
+ {
+ if(p->second->communicator != info->getCommunicator())
+ {
+ //
+ // A different communicator is already registered with that ID.
+ //
+ RETURN_FALSE;
+ }
+ }
+ else
+ {
+ info->ac->ids.push_back(id);
+ _registeredCommunicators[id] = info->ac;
+ }
+
+ 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();
+
+ //
+ // 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_unregister)
+{
+ char* s;
+ size_t sLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &s, &sLen) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string id(s, sLen);
+
+ IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
+
+ RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
+ if(p == _registeredCommunicators.end())
+ {
+ //
+ // 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_find)
+{
+ char* s;
+ size_t sLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &s, &sLen) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string id(s, sLen);
+
+ IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
+
+ RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
+ if(p == _registeredCommunicators.end())
+ {
+ //
+ // No communicator registered with that ID.
+ //
+ RETURN_NULL();
+ }
+
+ if(p->second->expires > 0)
+ {
+ p->second->lastAccess = IceUtil::Time::now();
+ }
+
+ //
+ // 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)
+ {
+ CommunicatorMap::iterator q = m->find(p->second->communicator);
+ if(q != m->end())
+ {
+ q->second->getZval(return_value);
+ return;
+ }
+ }
+
+ if(!createCommunicator(return_value, p->second))
+ {
+ RETURN_NULL();
+ }
+}
+
+ZEND_FUNCTION(Ice_getProperties)
+{
+ char* s = 0;
+ size_t sLen;
+ if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("|s"), &s, &sLen) != SUCCESS)
+ {
+ RETURN_NULL();
+ }
+
+ string name;
+ if(s)
+ {
+ name = string(s, sLen);
+ }
+
+ ProfileMap::iterator p = _profiles.find(name);
+ if(p == _profiles.end())
+ {
+ RETURN_NULL();
+ }
+
+ Ice::PropertiesPtr clone = p->second->clone();
+ if(!createProperties(return_value, clone))
+ {
+ RETURN_NULL();
+ }
+}
+
+//
+// Necessary to suppress warnings from zend_function_entry in php-5.2
+// and INI_STR macro.
+//
+#ifdef __GNUC__
+# pragma GCC diagnostic ignored "-Wwrite-strings"
+#endif
+
+//
+// Predefined methods for Communicator.
+//
+static zend_function_entry _interfaceMethods[] =
+{
+ {0, 0, 0}
+};
+static zend_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, proxyToProperty, 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, getValueFactoryManager, 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}
+};
+
+//
+// Predefined methods for ValueFactoryManager.
+//
+static zend_function_entry _vfmInterfaceMethods[] =
+{
+ {0, 0, 0}
+};
+static zend_function_entry _vfmClassMethods[] =
+{
+ ZEND_ME(Ice_ValueFactoryManager, __construct, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR)
+ ZEND_ME(Ice_ValueFactoryManager, add, NULL, ZEND_ACC_PUBLIC)
+ ZEND_ME(Ice_ValueFactoryManager, find, NULL, ZEND_ACC_PUBLIC)
+ {0, 0, 0}
+};
+
+static bool
+createProfile(const string& name, const string& config, const string& options)
+{
+ ProfileMap::iterator p = _profiles.find(name);
+ if(p != _profiles.end())
+ {
+ php_error_docref(0, E_WARNING, "duplicate Ice profile `%s'", name.c_str());
+ return false;
+ }
+
+ Ice::PropertiesPtr properties = Ice::createProperties();
+
+ if(!config.empty())
+ {
+ try
+ {
+ properties->load(config);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ ostringstream ostr;
+ ex.ice_print(ostr);
+ php_error_docref(0, E_WARNING, "unable to load Ice configuration file %s:\n%s", config.c_str(),
+ ostr.str().c_str());
+ return false;
+ }
+ }
+
+ if(!options.empty())
+ {
+ vector<string> args;
+ try
+ {
+ args = IceUtilInternal::Options::split(options);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ ostringstream ostr;
+ ex.ice_print(ostr);
+ string msg = ostr.str();
+ php_error_docref(0, E_WARNING, "error occurred while parsing the options `%s':\n%s",
+ options.c_str(), msg.c_str());
+ return false;
+ }
+
+ properties->parseCommandLineOptions("", args);
+ }
+
+ _profiles[name] = properties;
+ return true;
+}
+
+static bool
+parseProfiles(const string& file)
+{
+ //
+ // 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
+ //
+ ifstream in(file.c_str());
+ if(!in)
+ {
+ php_error_docref(0, 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)
+ {
+ 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, E_WARNING, "invalid profile section in file %s:\n%s\n", file.c_str(),
+ line);
+ return false;
+ }
+
+ if(!name.empty())
+ {
+ createProfile(name, config, options);
+ config.clear();
+ options.clear();
+ }
+
+ 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, 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);
+
+ //
+ // Check for quotes and remove them if present
+ //
+ string::size_type qpos = IceUtilInternal::checkQuote(value);
+ if(qpos != string::npos)
+ {
+ value = value.substr(1, qpos - 1);
+ }
+ }
+
+ if(key == "config" || key == "ice.config")
+ {
+ config = value;
+ }
+ else if(key == "options" || key == "ice.options")
+ {
+ options = value;
+ }
+ else
+ {
+ php_error_docref(0, E_WARNING, "unknown profile entry in file %s:\n%s\n", file.c_str(), line);
+ }
+
+ if(name.empty())
+ {
+ php_error_docref(0, E_WARNING, "no section for profile entry in file %s:\n%s\n", file.c_str(),
+ line);
+ return false;
+ }
+ }
+ }
+
+ if(!name.empty())
+ {
+ if(!createProfile(name, config, options))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+IcePHP::communicatorInit(void)
+{
+ //
+ // 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, "Ice", "Communicator", _interfaceMethods);
+#else
+ INIT_CLASS_ENTRY(ce, "Ice_Communicator", _interfaceMethods);
+#endif
+ zend_class_entry* interface = zend_register_internal_interface(&ce);
+
+ //
+ // Register the Communicator class.
+ //
+ INIT_CLASS_ENTRY(ce, "IcePHP_Communicator", _classMethods);
+ ce.create_object = handleAlloc;
+ communicatorClassEntry = zend_register_internal_class(&ce);
+ memcpy(&_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ _handlers.clone_obj = handleClone;
+ _handlers.free_obj = handleFreeStorage;
+ _handlers.offset = XtOffsetOf(Wrapper<CommunicatorInfoIPtr>, zobj);
+ zend_class_implements(communicatorClassEntry, 1, interface);
+
+ //
+ // Register the ValueFactoryManager interface.
+ //
+#ifdef ICEPHP_USE_NAMESPACES
+ INIT_NS_CLASS_ENTRY(ce, "Ice", "ValueFactoryManager", _vfmInterfaceMethods);
+#else
+ INIT_CLASS_ENTRY(ce, "Ice_ValueFactoryManager", _vfmInterfaceMethods);
+#endif
+ zend_class_entry* vfmInterface = zend_register_internal_interface(&ce);
+
+ //
+ // Register the ValueFactoryManager class.
+ //
+ INIT_CLASS_ENTRY(ce, "IcePHP_ValueFactoryManager", _vfmClassMethods);
+ ce.create_object = handleVfmAlloc;
+ valueFactoryManagerClassEntry = zend_register_internal_class(&ce);
+ memcpy(&_vfmHandlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
+ _vfmHandlers.clone_obj = handleVfmClone;
+ _vfmHandlers.free_obj = handleVfmFreeStorage;
+ _vfmHandlers.offset = XtOffsetOf(Wrapper<ValueFactoryManagerPtr>, zobj);
+ zend_class_implements(valueFactoryManagerClassEntry, 1, vfmInterface);
+
+ //
+ // Create the profiles from configuration settings.
+ //
+ const char* empty = "";
+ const char* config = INI_STR("ice.config"); // Needs to be a string literal!
+ if(!config)
+ {
+ config = empty;
+ }
+ const char* options = INI_STR("ice.options"); // Needs to be a string literal!
+ if(!options)
+ {
+ options = empty;
+ }
+ if(!createProfile(_defaultProfileName, config, options))
+ {
+ return false;
+ }
+
+ const char* profiles = INI_STR("ice.profiles"); // Needs to be a string literal!
+ if(!profiles)
+ {
+ profiles = empty;
+ }
+ if(strlen(profiles) > 0)
+ {
+ if(!parseProfiles(profiles))
+ {
+ return false;
+ }
+
+ if(INI_BOOL(const_cast<char*>("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(void)
+{
+ _profiles.clear();
+
+ IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_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(void)
+{
+ ICE_G(communicatorMap) = 0;
+
+ return true;
+}
+
+bool
+IcePHP::communicatorRequestShutdown(void)
+{
+ 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|value factories installed during this request.
+ //
+ info->destroyFactories();
+ }
+
+ //
+ // 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()
+{
+ //
+ // There are no more references to this communicator, so we can safely destroy it now.
+ //
+ try
+ {
+ communicator->destroy();
+ }
+ catch(...)
+ {
+ }
+}
+
+IcePHP::FactoryWrapper::FactoryWrapper(zval* factory, bool isObjectFactory, const CommunicatorInfoIPtr& info) :
+ _isObjectFactory(isObjectFactory),
+ _info(info)
+{
+ ZVAL_COPY(&_factory, factory);
+}
+
+Ice::ValuePtr
+IcePHP::FactoryWrapper::create(const string& id)
+{
+ //
+ // Get the TSRM id for the current request.
+ //
+
+ //
+ // Get the type information.
+ //
+ ClassInfoPtr cls;
+ if(id == Ice::Object::ice_staticId())
+ {
+ //
+ // When the ID is that of Ice::Object, it indicates that the stream has not
+ // found a factory and is providing us an opportunity to preserve the object.
+ //
+ cls = getClassInfoById("::Ice::UnknownSlicedObject");
+ }
+ else
+ {
+ cls = getClassInfoById(id);
+ }
+
+ if(!cls)
+ {
+ return 0;
+ }
+
+ zval arg;
+ ZVAL_STRINGL(&arg, STRCAST(id.c_str()), static_cast<int>(id.length()));
+
+ zval obj;
+ ZVAL_UNDEF(&obj);
+
+ zend_try
+ {
+ assert(Z_TYPE(_factory) == IS_OBJECT);
+ zend_call_method(&_factory, 0, 0, const_cast<char*>("create"), sizeof("create") - 1, &obj, 1, &arg, 0);
+ }
+ zend_catch
+ {
+ // obj;
+ }
+ zend_end_try();
+
+ //
+ // Bail out if an exception has already been thrown.
+ //
+ if(Z_ISUNDEF(obj) || EG(exception))
+ {
+ throw AbortMarshaling();
+ }
+
+ AutoDestroy destroy(&obj);
+
+ if(Z_TYPE(obj) == IS_NULL)
+ {
+ return 0;
+ }
+
+ return new ObjectReader(&obj, cls, _info);
+}
+
+void
+IcePHP::FactoryWrapper::getZval(zval* factory)
+{
+ ZVAL_DUP(factory, &_factory);
+}
+
+bool
+IcePHP::FactoryWrapper::isObjectFactory() const
+{
+ return _isObjectFactory;
+}
+
+void
+IcePHP::FactoryWrapper::destroy(void)
+{
+ if(_isObjectFactory)
+ {
+ //
+ // Invoke the destroy method on the PHP factory.
+ //
+ invokeMethod(&_factory, "destroy");
+ zend_clear_exception();
+ }
+ zval_ptr_dtor(&_factory);
+ _info = 0;
+}
+
+IcePHP::DefaultValueFactory::DefaultValueFactory(const CommunicatorInfoIPtr& info) :
+ _info(info)
+{
+}
+
+Ice::ValuePtr
+IcePHP::DefaultValueFactory::create(const string& id)
+{
+ //
+ // Get the TSRM id for the current request.
+ //
+
+ if(_delegate)
+ {
+ Ice::ValuePtr v = _delegate->create(id);
+ if(v)
+ {
+ return v;
+ }
+ }
+
+ //
+ // Get the type information.
+ //
+ ClassInfoPtr cls;
+ if(id == Ice::Object::ice_staticId())
+ {
+ //
+ // When the ID is that of Ice::Object, it indicates that the stream has not
+ // found a factory and is providing us an opportunity to preserve the object.
+ //
+ cls = getClassInfoById("::Ice::UnknownSlicedObject");
+ }
+ else
+ {
+ cls = getClassInfoById(id);
+ }
+
+ if(!cls)
+ {
+ return 0;
+ }
+
+ //
+ // If the requested type is an abstract class, then we give up.
+ //
+ if(cls->isAbstract)
+ {
+ return 0;
+ }
+
+ //
+ // Instantiate the object.
+ //
+ zval obj;
+
+ if(object_init_ex(&obj, const_cast<zend_class_entry*>(cls->zce)) != SUCCESS)
+ {
+ throw AbortMarshaling();
+ }
+
+ if(!invokeMethod(&obj, ZEND_CONSTRUCTOR_FUNC_NAME))
+ {
+ throw AbortMarshaling();
+ }
+
+ return new ObjectReader(&obj, cls, _info);
+}
+
+void
+IcePHP::DefaultValueFactory::destroy(void)
+{
+ if(_delegate)
+ {
+ _delegate->destroy();
+ _delegate = 0;
+ }
+ _info = 0;
+}
+
+IcePHP::CommunicatorInfoI::CommunicatorInfoI(const ActiveCommunicatorPtr& c, zval* z) :
+ ac(c),
+ _defaultFactory(new DefaultValueFactory(this))
+{
+ ZVAL_COPY_VALUE(&zv, z);
+}
+
+void
+IcePHP::CommunicatorInfoI::getZval(zval* z)
+{
+ ZVAL_COPY_VALUE(z, &zv);
+ addRef();
+}
+
+void
+IcePHP::CommunicatorInfoI::addRef(void)
+{
+ Z_ADDREF_P(&zv);
+}
+
+void
+IcePHP::CommunicatorInfoI::decRef(void)
+{
+ Z_DELREF_P(&zv);
+}
+
+Ice::CommunicatorPtr
+IcePHP::CommunicatorInfoI::getCommunicator() const
+{
+ return ac->communicator;
+}
+
+bool
+IcePHP::CommunicatorInfoI::addFactory(zval* factory, const string& id, bool isObjectFactory)
+{
+ if(id.empty())
+ {
+ if(_defaultFactory->getDelegate())
+ {
+ Ice::AlreadyRegisteredException ex(__FILE__, __LINE__);
+ ex.kindOfObject = "value factory";
+ ex.id = id;
+ throwException(ex);
+ return false;
+ }
+
+ _defaultFactory->setDelegate(new FactoryWrapper(factory, isObjectFactory, this));
+ }
+ else
+ {
+ FactoryMap::iterator p = _factories.find(id);
+ if(p != _factories.end())
+ {
+ Ice::AlreadyRegisteredException ex(__FILE__, __LINE__);
+ ex.kindOfObject = "value factory";
+ ex.id = id;
+ throwException(ex);
+ return false;
+ }
+
+ _factories.insert(FactoryMap::value_type(id, new FactoryWrapper(factory, isObjectFactory, this)));
+ }
+
+ return true;
+}
+
+FactoryWrapperPtr
+IcePHP::CommunicatorInfoI::findFactory(const string& id) const
+{
+ if(id.empty())
+ {
+ return _defaultFactory->getDelegate();
+ }
+ else
+ {
+ FactoryMap::const_iterator p = _factories.find(id);
+ if(p != _factories.end())
+ {
+ assert(p->second);
+ return p->second;
+ }
+ }
+
+ return 0;
+}
+
+void
+IcePHP::CommunicatorInfoI::destroyFactories(void)
+{
+ for(FactoryMap::iterator p = _factories.begin(); p != _factories.end(); ++p)
+ {
+ p->second->destroy();
+ }
+ _factories.clear();
+ _defaultFactory->destroy();
+}
+
+void
+IcePHP::ValueFactoryManager::add(const Ice::ValueFactoryPtr&, const string&)
+{
+ //
+ // We don't support factories registered in C++.
+ //
+ throw Ice::FeatureNotSupportedException(__FILE__, __LINE__, "C++ value factory");
+}
+
+Ice::ValueFactoryPtr
+IcePHP::ValueFactoryManager::find(const string& id) const
+{
+ //
+ // Get the TSRM id for the current request.
+ //
+
+ CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
+ assert(m);
+ CommunicatorMap::iterator p = m->find(_communicator);
+ assert(p != m->end());
+
+ CommunicatorInfoIPtr info = p->second;
+
+ if(id.empty())
+ {
+ return info->defaultFactory();
+ }
+ else
+ {
+ return info->findFactory(id);
+ }
+}
+
+void
+IcePHP::ValueFactoryManager::destroy()
+{
+ _communicator = 0;
+}
+
+void
+IcePHP::ReaperTask::runTimerTask()
+{
+ IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_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;
+ }
+ }
+}