ICE 3.4.2
[php5-ice-freebsdport.git] / php / src / IcePHP / Communicator.cpp
blobfced1eb538eabdafd514fc7a3997de341ac1edf8
1 // **********************************************************************
2 //
3 // Copyright (c) 2003-2011 ZeroC, Inc. All rights reserved.
4 //
5 // This copy of Ice is licensed to you under the terms described in the
6 // ICE_LICENSE file included in this distribution.
7 //
8 // **********************************************************************
10 #include <Communicator.h>
11 #include <Logger.h>
12 #include <Properties.h>
13 #include <Proxy.h>
14 #include <Util.h>
15 #include <IceUtil/Options.h>
16 #include <IceUtil/MutexPtrLock.h>
17 #include <IceUtil/StringUtil.h>
18 #include <IceUtil/Timer.h>
20 using namespace std;
21 using namespace IcePHP;
23 ZEND_EXTERN_MODULE_GLOBALS(ice)
26 // Class entries represent the PHP class implementations we have registered.
28 namespace IcePHP
31 zend_class_entry* communicatorClassEntry = 0;
34 // An active communicator is in use by at least one request and may have
35 // registered so that it remains active after a request completes. The
36 // communicator is destroyed when there are no more references to this
37 // object.
39 class ActiveCommunicator : public IceUtil::Shared
41 public:
43 ActiveCommunicator(const Ice::CommunicatorPtr& c);
44 ~ActiveCommunicator();
46 const Ice::CommunicatorPtr communicator;
47 vector<string> ids;
48 int expires;
49 IceUtil::Time lastAccess;
51 typedef IceUtil::Handle<ActiveCommunicator> ActiveCommunicatorPtr;
53 typedef std::map<std::string, zval*> ObjectFactoryMap;
55 class CommunicatorInfoI : public CommunicatorInfo
57 public:
59 CommunicatorInfoI(const ActiveCommunicatorPtr&, zval*);
61 virtual void getZval(zval* TSRMLS_DC);
62 virtual void addRef(TSRMLS_D);
63 virtual void decRef(TSRMLS_D);
65 virtual Ice::CommunicatorPtr getCommunicator() const;
67 bool addObjectFactory(const std::string&, zval* TSRMLS_DC);
68 bool findObjectFactory(const std::string&, zval* TSRMLS_DC);
69 void destroyObjectFactories(TSRMLS_D);
71 const ActiveCommunicatorPtr ac;
72 const zval zv;
73 ObjectFactoryMap objectFactories;
75 typedef IceUtil::Handle<CommunicatorInfoI> CommunicatorInfoIPtr;
78 // Each PHP request has its own set of object factories. More precisely, there is
79 // an object factory map for each communicator that is created by a PHP request.
80 // The factory class defined below delegates the create/destroy methods to PHP
81 // objects supplied by the application. An instance of this class is installed
82 // as the communicator's default object factory, and the class holds a reference
83 // to its communicator. When create is invoked, the class resolves the appropriate
84 // PHP object as follows:
86 // * Using its communicator reference as the key, look up the corresponding
87 // CommunicatorInfoI object in the request-specific communicator map.
89 // * In the object factory map held by the CommunicatorInfoI object, look for a
90 // PHP factory object using the same algorithm as the Ice core.
92 class ObjectFactoryI : public Ice::ObjectFactory
94 public:
96 ObjectFactoryI(const Ice::CommunicatorPtr&);
98 virtual Ice::ObjectPtr create(const std::string&);
99 virtual void destroy();
101 private:
103 Ice::CommunicatorPtr _communicator;
106 class ReaperTask : public IceUtil::TimerTask
108 public:
110 virtual void runTimerTask();
115 namespace
118 // Communicator support.
120 zend_object_handlers _handlers;
123 // The profile map holds Properties objects corresponding to the "default" profile
124 // (defined via the ice.config & ice.options settings in php.ini) as well as named
125 // profiles defined in an external file.
127 typedef map<string, Ice::PropertiesPtr> ProfileMap;
128 ProfileMap _profiles;
129 const string _defaultProfileName = "";
132 // This map represents communicators that have been registered so that they can be used
133 // by multiple PHP requests.
135 typedef map<string, ActiveCommunicatorPtr> RegisteredCommunicatorMap;
136 RegisteredCommunicatorMap _registeredCommunicators;
137 IceUtil::Mutex* _registeredCommunicatorsMutex = 0;
139 IceUtil::TimerPtr _timer;
142 // This map is stored in the "global" variables for each PHP request and holds
143 // the communicators that have been created (or registered communicators that have
144 // been used) by the request.
146 typedef map<Ice::CommunicatorPtr, CommunicatorInfoIPtr> CommunicatorMap;
148 class Init
150 public:
152 Init()
154 _registeredCommunicatorsMutex = new IceUtil::Mutex();
157 ~Init()
159 delete _registeredCommunicatorsMutex;
160 _registeredCommunicatorsMutex = 0;
164 Init init;
167 extern "C"
169 static zend_object_value handleAlloc(zend_class_entry* TSRMLS_DC);
170 static void handleFreeStorage(void* TSRMLS_DC);
171 static zend_object_value handleClone(zval* TSRMLS_DC);
174 ZEND_METHOD(Ice_Communicator, __construct)
176 runtimeError("communicators cannot be instantiated directly" TSRMLS_CC);
179 ZEND_METHOD(Ice_Communicator, destroy)
181 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
182 assert(_this);
185 // Remove all registrations.
188 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
189 for(vector<string>::iterator p = _this->ac->ids.begin(); p != _this->ac->ids.end(); ++p)
191 _registeredCommunicators.erase(*p);
193 _this->ac->ids.clear();
197 // We need to destroy any object factories installed by this request.
199 _this->destroyObjectFactories(TSRMLS_C);
201 Ice::CommunicatorPtr c = _this->getCommunicator();
202 assert(c);
203 CommunicatorMap* m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap));
204 assert(m);
205 assert(m->find(c) != m->end());
206 m->erase(c);
210 c->destroy();
212 catch(const IceUtil::Exception& ex)
214 throwException(ex TSRMLS_CC);
215 RETURN_NULL();
219 ZEND_METHOD(Ice_Communicator, stringToProxy)
221 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
222 assert(_this);
224 char* str;
225 int strLen;
226 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("s"), &str, &strLen) != SUCCESS)
228 RETURN_NULL();
230 string s(str, strLen);
234 Ice::ObjectPrx prx = _this->getCommunicator()->stringToProxy(s);
235 if(!createProxy(return_value, prx, _this TSRMLS_CC))
237 RETURN_NULL();
240 catch(const IceUtil::Exception& ex)
242 throwException(ex TSRMLS_CC);
243 RETURN_NULL();
247 ZEND_METHOD(Ice_Communicator, proxyToString)
249 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
250 assert(_this);
252 zval* zv;
253 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("O!"), &zv, proxyClassEntry) != SUCCESS)
255 RETURN_NULL();
260 string str;
261 if(zv)
263 Ice::ObjectPrx prx;
264 ClassInfoPtr info;
265 if(!fetchProxy(zv, prx, info TSRMLS_CC))
267 RETURN_NULL();
269 assert(prx);
270 str = prx->ice_toString();
272 RETURN_STRINGL(STRCAST(str.c_str()), str.length(), 1);
274 catch(const IceUtil::Exception& ex)
276 throwException(ex TSRMLS_CC);
277 RETURN_NULL();
281 ZEND_METHOD(Ice_Communicator, propertyToProxy)
283 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
284 assert(_this);
286 char* str;
287 int strLen;
288 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("s"), &str, &strLen) != SUCCESS)
290 RETURN_NULL();
292 string s(str, strLen);
296 Ice::ObjectPrx prx = _this->getCommunicator()->propertyToProxy(s);
297 if(!createProxy(return_value, prx, _this TSRMLS_CC))
299 RETURN_NULL();
302 catch(const IceUtil::Exception& ex)
304 throwException(ex TSRMLS_CC);
305 RETURN_NULL();
309 ZEND_METHOD(Ice_Communicator, proxyToProperty)
311 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
312 assert(_this);
314 zval* zv;
315 char* str;
316 int strLen;
317 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("O!s"), &zv, proxyClassEntry, &str, &strLen)
318 != SUCCESS)
320 RETURN_NULL();
323 string prefix(str, strLen);
327 if(zv)
329 Ice::ObjectPrx prx;
330 ClassInfoPtr info;
331 if(!fetchProxy(zv, prx, info TSRMLS_CC))
333 RETURN_NULL();
335 assert(prx);
337 Ice::PropertyDict val = _this->getCommunicator()->proxyToProperty(prx, prefix);
338 if(!createStringMap(return_value, val TSRMLS_CC))
340 RETURN_NULL();
343 else
345 array_init(return_value);
348 catch(const IceUtil::Exception& ex)
350 throwException(ex TSRMLS_CC);
351 RETURN_NULL();
355 ZEND_METHOD(Ice_Communicator, stringToIdentity)
357 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
358 assert(_this);
360 char* str;
361 int strLen;
362 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("s"), &str, &strLen) != SUCCESS)
364 RETURN_NULL();
366 string s(str, strLen);
370 Ice::Identity id = _this->getCommunicator()->stringToIdentity(s);
371 if(!createIdentity(return_value, id TSRMLS_CC))
373 RETURN_NULL();
376 catch(const IceUtil::Exception& ex)
378 throwException(ex TSRMLS_CC);
379 RETURN_NULL();
383 ZEND_METHOD(Ice_Communicator, identityToString)
385 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
386 assert(_this);
388 zend_class_entry* identityClass = idToClass("::Ice::Identity" TSRMLS_CC);
389 assert(identityClass);
391 zval* zv;
392 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("O"), &zv, identityClass) != SUCCESS)
394 RETURN_NULL();
396 Ice::Identity id;
397 if(!extractIdentity(zv, id TSRMLS_CC))
399 RETURN_NULL();
404 string str = _this->getCommunicator()->identityToString(id);
405 RETURN_STRINGL(STRCAST(str.c_str()), str.length(), 1);
407 catch(const IceUtil::Exception& ex)
409 throwException(ex TSRMLS_CC);
410 RETURN_NULL();
414 ZEND_METHOD(Ice_Communicator, addObjectFactory)
416 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
417 assert(_this);
419 zend_class_entry* factoryClass = idToClass("Ice::ObjectFactory" TSRMLS_CC);
420 assert(factoryClass);
422 zval* factory;
423 char* id;
424 int idLen;
425 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("Os!"), &factory, factoryClass, &id,
426 &idLen TSRMLS_CC) != SUCCESS)
428 RETURN_NULL();
431 string type;
432 if(id)
434 type = string(id, idLen);
437 if(!_this->addObjectFactory(type, factory TSRMLS_CC))
439 RETURN_NULL();
443 ZEND_METHOD(Ice_Communicator, findObjectFactory)
445 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
446 assert(_this);
448 char* id;
449 int idLen;
450 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("s!"), &id, &idLen TSRMLS_CC) != SUCCESS)
452 RETURN_NULL();
455 string type;
456 if(id)
458 type = string(id, idLen);
461 if(!_this->findObjectFactory(type, return_value TSRMLS_CC))
463 RETURN_NULL();
467 ZEND_METHOD(Ice_Communicator, getImplicitContext)
469 runtimeError("not implemented" TSRMLS_CC);
472 ZEND_METHOD(Ice_Communicator, getProperties)
474 if(ZEND_NUM_ARGS() > 0)
476 WRONG_PARAM_COUNT;
479 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
480 assert(_this);
484 Ice::PropertiesPtr props = _this->getCommunicator()->getProperties();
485 if(!createProperties(return_value, props TSRMLS_CC))
487 RETURN_NULL();
490 catch(const IceUtil::Exception& ex)
492 throwException(ex TSRMLS_CC);
493 RETURN_NULL();
497 ZEND_METHOD(Ice_Communicator, getLogger)
499 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
500 assert(_this);
504 Ice::LoggerPtr logger = _this->getCommunicator()->getLogger();
505 if(!createLogger(return_value, logger TSRMLS_CC))
507 RETURN_NULL();
510 catch(const IceUtil::Exception& ex)
512 throwException(ex TSRMLS_CC);
513 RETURN_NULL();
517 ZEND_METHOD(Ice_Communicator, getDefaultRouter)
519 if(ZEND_NUM_ARGS() > 0)
521 WRONG_PARAM_COUNT;
524 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
525 assert(_this);
529 Ice::RouterPrx router = _this->getCommunicator()->getDefaultRouter();
530 if(router)
532 ClassInfoPtr info = getClassInfoById("::Ice::Router" TSRMLS_CC);
533 if(!info)
535 runtimeError("no definition for Ice::Router" TSRMLS_CC);
536 RETURN_NULL();
538 if(!createProxy(return_value, router, info, _this TSRMLS_CC))
540 RETURN_NULL();
543 else
545 RETURN_NULL();
548 catch(const IceUtil::Exception& ex)
550 throwException(ex TSRMLS_CC);
551 RETURN_NULL();
555 ZEND_METHOD(Ice_Communicator, setDefaultRouter)
557 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
558 assert(_this);
560 zval* zv;
561 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("O!"), &zv, proxyClassEntry TSRMLS_CC) !=
562 SUCCESS)
564 RETURN_NULL();
567 Ice::ObjectPrx proxy;
568 ClassInfoPtr info;
569 if(zv && !fetchProxy(zv, proxy, info TSRMLS_CC))
571 RETURN_NULL();
576 Ice::RouterPrx router;
577 if(proxy)
579 if(!info || !info->isA("::Ice::Router"))
581 invalidArgument("setDefaultRouter requires a proxy narrowed to Ice::Router" TSRMLS_CC);
582 RETURN_NULL();
584 router = Ice::RouterPrx::uncheckedCast(proxy);
586 _this->getCommunicator()->setDefaultRouter(router);
588 catch(const IceUtil::Exception& ex)
590 throwException(ex TSRMLS_CC);
591 RETURN_NULL();
595 ZEND_METHOD(Ice_Communicator, getDefaultLocator)
597 if(ZEND_NUM_ARGS() > 0)
599 WRONG_PARAM_COUNT;
602 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
603 assert(_this);
607 Ice::LocatorPrx locator = _this->getCommunicator()->getDefaultLocator();
608 if(locator)
610 ClassInfoPtr info = getClassInfoById("::Ice::Locator" TSRMLS_CC);
611 if(!info)
613 runtimeError("no definition for Ice::Locator" TSRMLS_CC);
614 RETURN_NULL();
616 if(!createProxy(return_value, locator, info, _this TSRMLS_CC))
618 RETURN_NULL();
621 else
623 RETURN_NULL();
626 catch(const IceUtil::Exception& ex)
628 throwException(ex TSRMLS_CC);
629 RETURN_NULL();
633 ZEND_METHOD(Ice_Communicator, setDefaultLocator)
635 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
636 assert(_this);
638 zval* zv;
639 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("O!"), &zv, proxyClassEntry TSRMLS_CC) !=
640 SUCCESS)
642 RETURN_NULL();
645 Ice::ObjectPrx proxy;
646 ClassInfoPtr info;
647 if(zv && !fetchProxy(zv, proxy, info TSRMLS_CC))
649 RETURN_NULL();
654 Ice::LocatorPrx locator;
655 if(proxy)
657 if(!info || !info->isA("::Ice::Locator"))
659 invalidArgument("setDefaultLocator requires a proxy narrowed to Ice::Locator" TSRMLS_CC);
660 RETURN_NULL();
662 locator = Ice::LocatorPrx::uncheckedCast(proxy);
664 _this->getCommunicator()->setDefaultLocator(locator);
666 catch(const IceUtil::Exception& ex)
668 throwException(ex TSRMLS_CC);
669 RETURN_NULL();
673 ZEND_METHOD(Ice_Communicator, flushBatchRequests)
675 CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
676 assert(_this);
678 if(ZEND_NUM_ARGS() != 8)
680 WRONG_PARAM_COUNT;
685 _this->getCommunicator()->flushBatchRequests();
687 catch(const IceUtil::Exception& ex)
689 throwException(ex TSRMLS_CC);
690 RETURN_NULL();
694 #ifdef _WIN32
695 extern "C"
696 #endif
697 static zend_object_value
698 handleAlloc(zend_class_entry* ce TSRMLS_DC)
700 zend_object_value result;
702 Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::create(ce TSRMLS_CC);
703 assert(obj);
705 result.handle = zend_objects_store_put(obj, 0, (zend_objects_free_object_storage_t)handleFreeStorage, 0 TSRMLS_CC);
706 result.handlers = &_handlers;
708 return result;
711 #ifdef _WIN32
712 extern "C"
713 #endif
714 static void
715 handleFreeStorage(void* p TSRMLS_DC)
717 Wrapper<CommunicatorInfoIPtr>* obj = static_cast<Wrapper<CommunicatorInfoIPtr>*>(p);
718 delete obj->ptr;
719 zend_objects_free_object_storage(static_cast<zend_object*>(p) TSRMLS_CC);
722 #ifdef _WIN32
723 extern "C"
724 #endif
725 static zend_object_value
726 handleClone(zval* zv TSRMLS_DC)
728 php_error_docref(0 TSRMLS_CC, E_ERROR, "communicators cannot be cloned");
729 return zend_object_value();
732 static CommunicatorInfoIPtr
733 createCommunicator(zval* zv, const ActiveCommunicatorPtr& ac TSRMLS_DC)
737 if(object_init_ex(zv, communicatorClassEntry) != SUCCESS)
739 runtimeError("unable to initialize communicator object" TSRMLS_CC);
740 return 0;
743 Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::extract(zv TSRMLS_CC);
744 assert(!obj->ptr);
746 CommunicatorInfoIPtr info = new CommunicatorInfoI(ac, zv);
747 obj->ptr = new CommunicatorInfoIPtr(info);
749 CommunicatorMap* m;
750 if(ICE_G(communicatorMap))
752 m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap));
754 else
756 m = new CommunicatorMap;
757 ICE_G(communicatorMap) = m;
759 m->insert(CommunicatorMap::value_type(ac->communicator, info));
761 return info;
763 catch(const IceUtil::Exception& ex)
765 throwException(ex TSRMLS_CC);
766 return 0;
770 static CommunicatorInfoIPtr
771 initializeCommunicator(zval* zv, Ice::StringSeq& args, bool hasArgs, const Ice::InitializationData& initData TSRMLS_DC)
775 Ice::CommunicatorPtr c;
776 if(hasArgs)
778 c = Ice::initialize(args, initData);
780 else
782 c = Ice::initialize(initData);
785 ActiveCommunicatorPtr ac = new ActiveCommunicator(c);
788 // Install a default object factory that delegates to PHP factories.
790 c->addObjectFactory(new ObjectFactoryI(c), "");
792 CommunicatorInfoIPtr info = createCommunicator(zv, ac TSRMLS_CC);
793 if(!info)
797 c->destroy();
799 catch(...)
804 return info;
806 catch(const IceUtil::Exception& ex)
808 throwException(ex TSRMLS_CC);
809 return 0;
813 ZEND_FUNCTION(Ice_initialize)
815 if(ZEND_NUM_ARGS() > 2)
817 runtimeError("too many arguments" TSRMLS_CC);
818 RETURN_NULL();
821 zend_class_entry* initClass = idToClass("::Ice::InitializationData" TSRMLS_CC);
822 assert(initClass);
825 // Retrieve the arguments.
827 zval*** args = static_cast<zval***>(emalloc(ZEND_NUM_ARGS() * sizeof(zval**)));
828 AutoEfree autoArgs(args); // Call efree on return
829 if(zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE)
831 runtimeError("unable to get arguments" TSRMLS_CC);
832 RETURN_NULL();
835 Ice::StringSeq seq;
836 Ice::InitializationData initData;
837 zval* zvinit = 0;
840 // Accept the following invocations:
842 // initialize(array, InitializationData)
843 // initialize(array)
844 // initialize(InitializationData)
845 // initialize()
847 bool hasArgs = false;
848 if(ZEND_NUM_ARGS())
850 if(Z_TYPE_PP(args[0]) == IS_ARRAY)
852 if(!extractStringArray(*args[0], seq TSRMLS_CC))
854 RETURN_NULL();
856 hasArgs = true;
857 if(ZEND_NUM_ARGS() > 1)
859 if(Z_TYPE_PP(args[1]) != IS_OBJECT || Z_OBJCE_PP(args[1]) != initClass)
861 string s = zendTypeToString(Z_TYPE_PP(args[1]));
862 invalidArgument("expected InitializationData object but received %s" TSRMLS_CC, s.c_str());
863 RETURN_NULL();
865 zvinit = *args[1];
868 else if(Z_TYPE_PP(args[0]) == IS_OBJECT && Z_OBJCE_PP(args[0]) == initClass)
870 if(ZEND_NUM_ARGS() > 1)
872 runtimeError("too many arguments" TSRMLS_CC);
873 RETURN_NULL();
875 zvinit = *args[0];
877 else
879 string s = zendTypeToString(Z_TYPE_PP(args[0]));
880 invalidArgument("unexpected argument type %s" TSRMLS_CC, s.c_str());
881 RETURN_NULL();
885 if(zvinit)
887 void* data;
888 string member;
890 member = "properties";
891 if(zend_hash_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size() + 1, &data) == SUCCESS)
893 zval** val = reinterpret_cast<zval**>(data);
894 if(!fetchProperties(*val, initData.properties TSRMLS_CC))
896 RETURN_NULL();
900 member = "logger";
901 if(zend_hash_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size() + 1, &data) == SUCCESS)
903 zval** val = reinterpret_cast<zval**>(data);
904 if(!fetchLogger(*val, initData.logger TSRMLS_CC))
906 RETURN_NULL();
911 CommunicatorInfoIPtr info = initializeCommunicator(return_value, seq, hasArgs, initData TSRMLS_CC);
912 if(!info)
914 RETURN_NULL();
918 ZEND_FUNCTION(Ice_register)
920 zval* comm;
921 char* s;
922 int sLen;
923 long expires = 0;
924 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("Os|l"), &comm, communicatorClassEntry, &s,
925 &sLen, &expires TSRMLS_CC) != SUCCESS)
927 RETURN_NULL();
930 string id(s, sLen);
931 if(id.empty())
933 invalidArgument("communicator id cannot be empty" TSRMLS_CC);
934 RETURN_NULL();
937 CommunicatorInfoIPtr info = Wrapper<CommunicatorInfoIPtr>::value(comm TSRMLS_CC);
938 assert(info);
940 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
942 RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
943 if(p != _registeredCommunicators.end())
945 if(p->second->communicator != info->getCommunicator())
948 // A different communicator is already registered with that ID.
950 RETURN_FALSE;
953 else
955 info->ac->ids.push_back(id);
956 _registeredCommunicators[id] = info->ac;
959 if(expires > 0)
962 // Update the expiration time. If a communicator is registered with multiple IDs, we
963 // always use the most recent expiration setting.
965 info->ac->expires = static_cast<int>(expires);
966 info->ac->lastAccess = IceUtil::Time::now();
969 // Start the timer if necessary. Reap expired communicators every five minutes.
971 if(!_timer)
973 _timer = new IceUtil::Timer;
974 _timer->scheduleRepeated(new ReaperTask, IceUtil::Time::seconds(5 * 60));
978 RETURN_TRUE;
981 ZEND_FUNCTION(Ice_unregister)
983 char* s;
984 int sLen;
985 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("s"), &s, &sLen TSRMLS_CC) != SUCCESS)
987 RETURN_NULL();
990 string id(s, sLen);
992 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
994 RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
995 if(p == _registeredCommunicators.end())
998 // No communicator registered with that ID.
1000 RETURN_FALSE;
1004 // Remove the ID from the ActiveCommunicator's list of registered IDs.
1006 ActiveCommunicatorPtr ac = p->second;
1007 vector<string>::iterator q = find(ac->ids.begin(), ac->ids.end(), id);
1008 assert(q != ac->ids.end());
1009 ac->ids.erase(q);
1011 _registeredCommunicators.erase(p);
1013 RETURN_TRUE;
1016 ZEND_FUNCTION(Ice_find)
1018 char* s;
1019 int sLen;
1020 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("s"), &s, &sLen TSRMLS_CC) != SUCCESS)
1022 RETURN_NULL();
1025 string id(s, sLen);
1027 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
1029 RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
1030 if(p == _registeredCommunicators.end())
1033 // No communicator registered with that ID.
1035 RETURN_NULL();
1038 if(p->second->expires > 0)
1040 p->second->lastAccess = IceUtil::Time::now();
1044 // Check if this communicator has already been obtained by the current request.
1045 // If so, we can return the existing PHP object that corresponds to the communicator.
1047 CommunicatorMap* m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap));
1048 if(m)
1050 CommunicatorMap::iterator q = m->find(p->second->communicator);
1051 if(q != m->end())
1053 q->second->getZval(return_value TSRMLS_CC);
1054 return;
1058 if(!createCommunicator(return_value, p->second TSRMLS_CC))
1060 RETURN_NULL();
1064 ZEND_FUNCTION(Ice_getProperties)
1066 char* s = 0;
1067 int sLen;
1068 if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("|s"), &s, &sLen TSRMLS_CC) != SUCCESS)
1070 RETURN_NULL();
1073 string name;
1074 if(s)
1076 name = string(s, sLen);
1079 ProfileMap::iterator p = _profiles.find(name);
1080 if(p == _profiles.end())
1082 RETURN_NULL();
1085 Ice::PropertiesPtr clone = p->second->clone();
1086 if(!createProperties(return_value, clone TSRMLS_CC))
1088 RETURN_NULL();
1093 // Predefined methods for Communicator.
1095 static function_entry _interfaceMethods[] =
1097 {0, 0, 0}
1099 static function_entry _classMethods[] =
1101 ZEND_ME(Ice_Communicator, __construct, NULL, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR)
1102 ZEND_ME(Ice_Communicator, destroy, NULL, ZEND_ACC_PUBLIC)
1103 ZEND_ME(Ice_Communicator, stringToProxy, NULL, ZEND_ACC_PUBLIC)
1104 ZEND_ME(Ice_Communicator, proxyToString, NULL, ZEND_ACC_PUBLIC)
1105 ZEND_ME(Ice_Communicator, propertyToProxy, NULL, ZEND_ACC_PUBLIC)
1106 ZEND_ME(Ice_Communicator, proxyToProperty, NULL, ZEND_ACC_PUBLIC)
1107 ZEND_ME(Ice_Communicator, stringToIdentity, NULL, ZEND_ACC_PUBLIC)
1108 ZEND_ME(Ice_Communicator, identityToString, NULL, ZEND_ACC_PUBLIC)
1109 ZEND_ME(Ice_Communicator, addObjectFactory, NULL, ZEND_ACC_PUBLIC)
1110 ZEND_ME(Ice_Communicator, findObjectFactory, NULL, ZEND_ACC_PUBLIC)
1111 ZEND_ME(Ice_Communicator, getImplicitContext, NULL, ZEND_ACC_PUBLIC)
1112 ZEND_ME(Ice_Communicator, getProperties, NULL, ZEND_ACC_PUBLIC)
1113 ZEND_ME(Ice_Communicator, getLogger, NULL, ZEND_ACC_PUBLIC)
1114 ZEND_ME(Ice_Communicator, getDefaultRouter, NULL, ZEND_ACC_PUBLIC)
1115 ZEND_ME(Ice_Communicator, setDefaultRouter, NULL, ZEND_ACC_PUBLIC)
1116 ZEND_ME(Ice_Communicator, getDefaultLocator, NULL, ZEND_ACC_PUBLIC)
1117 ZEND_ME(Ice_Communicator, setDefaultLocator, NULL, ZEND_ACC_PUBLIC)
1118 ZEND_ME(Ice_Communicator, flushBatchRequests, NULL, ZEND_ACC_PUBLIC)
1119 {0, 0, 0}
1122 static bool
1123 createProfile(const string& name, const string& config, const string& options TSRMLS_DC)
1125 ProfileMap::iterator p = _profiles.find(name);
1126 if(p != _profiles.end())
1128 php_error_docref(0 TSRMLS_CC, E_WARNING, "duplicate Ice profile `%s'", name.c_str());
1129 return false;
1132 Ice::PropertiesPtr properties = Ice::createProperties();
1134 if(!config.empty())
1138 properties->load(config);
1140 catch(const IceUtil::Exception& ex)
1142 ostringstream ostr;
1143 ex.ice_print(ostr);
1144 php_error_docref(0 TSRMLS_CC, E_WARNING, "unable to load Ice configuration file %s:\n%s", config.c_str(),
1145 ostr.str().c_str());
1146 return false;
1150 if(!options.empty())
1152 vector<string> args;
1155 args = IceUtilInternal::Options::split(options);
1157 catch(const IceUtil::Exception& ex)
1159 ostringstream ostr;
1160 ex.ice_print(ostr);
1161 string msg = ostr.str();
1162 php_error_docref(0 TSRMLS_CC, E_WARNING, "error occurred while parsing the options `%s':\n%s",
1163 options.c_str(), msg.c_str());
1164 return false;
1167 properties->parseCommandLineOptions("", args);
1170 _profiles[name] = properties;
1171 return true;
1174 static bool
1175 parseProfiles(const string& file TSRMLS_DC)
1178 // The Zend engine doesn't export a function for loading an INI file, so we
1179 // have to do it ourselves. The format is:
1181 // [profile-name]
1182 // ice.config = config-file
1183 // ice.options = args
1185 ifstream in(file.c_str());
1186 if(!in)
1188 php_error_docref(0 TSRMLS_CC, E_WARNING, "unable to open Ice profiles in %s", file.c_str());
1189 return false;
1192 string name, config, options;
1193 char line[1024];
1194 while(in.getline(line, 1024))
1196 const string delim = " \t\r\n";
1197 string s = line;
1199 string::size_type idx = s.find(';');
1200 if(idx != string::npos)
1202 s.erase(idx);
1205 idx = s.find_last_not_of(delim);
1206 if(idx != string::npos && idx + 1 < s.length())
1208 s.erase(idx + 1);
1211 string::size_type beg = s.find_first_not_of(delim);
1212 if(beg == string::npos)
1214 continue;
1217 if(s[beg] == '[')
1219 beg++;
1220 string::size_type end = s.find_first_of(" \t]", beg);
1221 if(end == string::npos || s[s.length() - 1] != ']')
1223 php_error_docref(0 TSRMLS_CC, E_WARNING, "invalid profile section in file %s:\n%s\n", file.c_str(),
1224 line);
1225 return false;
1228 if(!name.empty())
1230 createProfile(name, config, options TSRMLS_CC);
1231 config.clear();
1232 options.clear();
1235 name = s.substr(beg, end - beg);
1237 else
1239 string::size_type end = s.find_first_of(delim + "=", beg);
1240 assert(end != string::npos);
1242 string key = s.substr(beg, end - beg);
1244 end = s.find('=', end);
1245 if(end == string::npos)
1247 php_error_docref(0 TSRMLS_CC, E_WARNING, "invalid profile entry in file %s:\n%s\n", file.c_str(), line);
1248 return false;
1250 ++end;
1252 string value;
1253 beg = s.find_first_not_of(delim, end);
1254 if(beg != string::npos)
1256 end = s.length();
1257 value = s.substr(beg, end - beg);
1260 // Check for quotes and remove them if present
1262 string::size_type qpos = IceUtilInternal::checkQuote(value);
1263 if(qpos != string::npos)
1265 value = value.substr(1, qpos - 1);
1269 if(key == "config" || key == "ice.config")
1271 config = value;
1273 else if(key == "options" || key == "ice.options")
1275 options = value;
1277 else
1279 php_error_docref(0 TSRMLS_CC, E_WARNING, "unknown profile entry in file %s:\n%s\n", file.c_str(), line);
1282 if(name.empty())
1284 php_error_docref(0 TSRMLS_CC, E_WARNING, "no section for profile entry in file %s:\n%s\n", file.c_str(),
1285 line);
1286 return false;
1291 if(!name.empty())
1293 if(!createProfile(name, config, options TSRMLS_CC))
1295 return false;
1299 return true;
1302 bool
1303 IcePHP::communicatorInit(TSRMLS_D)
1306 // We register an interface and a class that implements the interface. This allows
1307 // applications to safely include the Slice-generated code for the type.
1311 // Register the Communicator interface.
1313 zend_class_entry ce;
1314 #ifdef ICEPHP_USE_NAMESPACES
1315 INIT_NS_CLASS_ENTRY(ce, STRCAST("Ice"), STRCAST("Communicator"), _interfaceMethods);
1316 #else
1317 INIT_CLASS_ENTRY(ce, "Ice_Communicator", _interfaceMethods);
1318 #endif
1319 zend_class_entry* interface = zend_register_internal_interface(&ce TSRMLS_CC);
1322 // Register the Communicator class.
1324 INIT_CLASS_ENTRY(ce, "IcePHP_Communicator", _classMethods);
1325 ce.create_object = handleAlloc;
1326 communicatorClassEntry = zend_register_internal_class(&ce TSRMLS_CC);
1327 memcpy(&_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1328 _handlers.clone_obj = handleClone;
1329 zend_class_implements(communicatorClassEntry TSRMLS_CC, 1, interface);
1332 // Create the profiles from configuration settings.
1334 const char* empty = "";
1335 const char* config = INI_STR("ice.config");
1336 if(!config)
1338 config = empty;
1340 const char* options = INI_STR("ice.options");
1341 if(!options)
1343 options = empty;
1345 if(!createProfile(_defaultProfileName, config, options TSRMLS_CC))
1347 return false;
1350 const char* profiles = INI_STR("ice.profiles");
1351 if(!profiles)
1353 profiles = empty;
1355 if(strlen(profiles) > 0)
1357 if(!parseProfiles(profiles TSRMLS_CC))
1359 return false;
1362 if(INI_BOOL(const_cast<char*>("ice.hide_profiles")))
1364 memset(const_cast<char*>(profiles), '*', strlen(profiles));
1366 // For some reason the code below does not work as expected. It causes a call
1367 // to ini_get_all() to segfault.
1370 if(zend_alter_ini_entry("ice.profiles", sizeof("ice.profiles"), "<hidden>", sizeof("<hidden>") - 1,
1371 PHP_INI_ALL, PHP_INI_STAGE_STARTUP) == FAILURE)
1373 return false;
1379 return true;
1382 bool
1383 IcePHP::communicatorShutdown(TSRMLS_D)
1385 _profiles.clear();
1387 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
1389 if(_timer)
1391 _timer->destroy();
1392 _timer = 0;
1396 // Clearing the map releases the last remaining reference counts of the ActiveCommunicator
1397 // objects. The ActiveCommunicator destructor destroys its communicator.
1399 _registeredCommunicators.clear();
1401 return true;
1404 bool
1405 IcePHP::communicatorRequestInit(TSRMLS_D)
1407 ICE_G(communicatorMap) = 0;
1409 return true;
1412 bool
1413 IcePHP::communicatorRequestShutdown(TSRMLS_D)
1415 if(ICE_G(communicatorMap))
1417 CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
1418 for(CommunicatorMap::iterator p = m->begin(); p != m->end(); ++p)
1420 CommunicatorInfoIPtr info = p->second;
1423 // We need to destroy any object factories installed during this request.
1425 info->destroyObjectFactories(TSRMLS_C);
1429 // Deleting the map decrements the reference count of its ActiveCommunicator
1430 // values. If there are no other references to an ActiveCommunicator, its
1431 // destructor destroys the communicator.
1433 delete m;
1436 return true;
1439 IcePHP::ActiveCommunicator::ActiveCommunicator(const Ice::CommunicatorPtr& c) :
1440 communicator(c), expires(0)
1444 IcePHP::ActiveCommunicator::~ActiveCommunicator()
1447 // There are no more references to this communicator, so we can safely destroy it now.
1451 communicator->destroy();
1453 catch(...)
1458 IcePHP::CommunicatorInfoI::CommunicatorInfoI(const ActiveCommunicatorPtr& c, zval* z) :
1459 ac(c),
1460 zv(*z) // This is legal - it simply copies the object's handle.
1464 void
1465 IcePHP::CommunicatorInfoI::getZval(zval* z TSRMLS_DC)
1467 Z_TYPE_P(z) = IS_OBJECT;
1468 z->value.obj = zv.value.obj;
1469 addRef(TSRMLS_C);
1472 void
1473 IcePHP::CommunicatorInfoI::addRef(TSRMLS_D)
1475 zval* p = const_cast<zval*>(&zv);
1476 Z_OBJ_HT_P(p)->add_ref(p TSRMLS_CC);
1479 void
1480 IcePHP::CommunicatorInfoI::decRef(TSRMLS_D)
1482 zval* p = const_cast<zval*>(&zv);
1483 Z_OBJ_HT(zv)->del_ref(p TSRMLS_CC);
1486 Ice::CommunicatorPtr
1487 IcePHP::CommunicatorInfoI::getCommunicator() const
1489 return ac->communicator;
1492 bool
1493 IcePHP::CommunicatorInfoI::addObjectFactory(const string& id, zval* factory TSRMLS_DC)
1495 ObjectFactoryMap::iterator p = objectFactories.find(id);
1496 if(p != objectFactories.end())
1498 Ice::AlreadyRegisteredException ex(__FILE__, __LINE__);
1499 ex.kindOfObject = "object factory";
1500 ex.id = id;
1501 throwException(ex TSRMLS_CC);
1502 return false;
1505 objectFactories.insert(ObjectFactoryMap::value_type(id, factory));
1506 Z_ADDREF_P(factory);
1508 return true;
1511 bool
1512 IcePHP::CommunicatorInfoI::findObjectFactory(const string& id, zval* zv TSRMLS_DC)
1514 ObjectFactoryMap::iterator p = objectFactories.find(id);
1515 if(p != objectFactories.end())
1517 *zv = *p->second; // This is legal - it simply copies the object's handle.
1518 INIT_PZVAL(zv);
1519 zval_copy_ctor(zv);
1520 return true;
1523 return false;
1526 void
1527 IcePHP::CommunicatorInfoI::destroyObjectFactories(TSRMLS_D)
1529 for(ObjectFactoryMap::iterator p = objectFactories.begin(); p != objectFactories.end(); ++p)
1532 // Invoke the destroy method on each registered PHP factory.
1534 invokeMethod(p->second, "destroy" TSRMLS_CC);
1535 zend_clear_exception(TSRMLS_C);
1536 zval_ptr_dtor(&p->second);
1540 IcePHP::ObjectFactoryI::ObjectFactoryI(const Ice::CommunicatorPtr& communicator) :
1541 _communicator(communicator)
1545 Ice::ObjectPtr
1546 IcePHP::ObjectFactoryI::create(const string& id)
1549 // Get the TSRM id for the current request.
1551 TSRMLS_FETCH();
1553 CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
1554 assert(m);
1555 CommunicatorMap::iterator p = m->find(_communicator);
1556 assert(p != m->end());
1558 CommunicatorInfoIPtr info = p->second;
1560 zval* factory = 0;
1563 // Check if the application has registered a factory for this id.
1565 ObjectFactoryMap::iterator q = info->objectFactories.find(id);
1566 if(q == info->objectFactories.end())
1568 q = info->objectFactories.find(""); // Look for a default factory.
1570 if(q != info->objectFactories.end())
1572 factory = q->second;
1576 // Get the type information.
1578 ClassInfoPtr cls = getClassInfoById(id TSRMLS_CC);
1579 if(!cls)
1581 return 0;
1584 if(factory)
1586 zval* arg;
1587 MAKE_STD_ZVAL(arg);
1588 ZVAL_STRINGL(arg, STRCAST(id.c_str()), id.length(), 1);
1590 zval* obj = 0;
1592 zend_try
1594 const char* func = "create";
1595 zend_call_method(&factory, 0, 0, const_cast<char*>(func), strlen(func), &obj, 1, arg, 0 TSRMLS_CC);
1597 zend_catch
1599 obj = 0;
1601 zend_end_try();
1603 zval_ptr_dtor(&arg);
1606 // Bail out if an exception has already been thrown.
1608 if(!obj || EG(exception))
1610 throw AbortMarshaling();
1613 AutoDestroy destroy(obj);
1615 if(Z_TYPE_P(obj) == IS_NULL)
1617 return 0;
1620 return new ObjectReader(obj, cls, info TSRMLS_CC);
1624 // If the requested type is an abstract class, then we give up.
1626 if(cls->isAbstract)
1628 return 0;
1632 // Instantiate the object.
1634 zval* obj;
1635 MAKE_STD_ZVAL(obj);
1636 AutoDestroy destroy(obj);
1638 if(object_init_ex(obj, cls->zce) != SUCCESS)
1640 throw AbortMarshaling();
1643 if(!invokeMethod(obj, ZEND_CONSTRUCTOR_FUNC_NAME TSRMLS_CC))
1645 throw AbortMarshaling();
1648 return new ObjectReader(obj, cls, info TSRMLS_CC);
1651 void
1652 IcePHP::ObjectFactoryI::destroy()
1654 _communicator = 0;
1657 void
1658 IcePHP::ReaperTask::runTimerTask()
1660 IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
1662 IceUtil::Time now = IceUtil::Time::now();
1663 RegisteredCommunicatorMap::iterator p = _registeredCommunicators.begin();
1664 while(p != _registeredCommunicators.end())
1666 if(p->second->lastAccess + IceUtil::Time::seconds(p->second->expires * 60) <= now)
1670 p->second->communicator->destroy();
1672 catch(...)
1675 _registeredCommunicators.erase(p++);
1677 else
1679 ++p;