1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <Communicator.h>
6 #include <Logger.h>
7 #include <Properties.h>
8 #include <Proxy.h>
9 #include <Types.h>
10 #include <Util.h>
11 #include <IceUtil/DisableWarnings.h>
12 #include <IceUtil/Options.h>
13 #include <IceUtil/MutexPtrLock.h>
14 #include <IceUtil/StringUtil.h>
15 #include <IceUtil/Timer.h>
16 #include <fstream>
17 
18 #ifdef getcwd
19 #  undef getcwd
20 #endif
21 #include <IceUtil/FileUtil.h>
22 
23 using namespace std;
24 using namespace IcePHP;
25 
26 ZEND_EXTERN_MODULE_GLOBALS(ice)
27 
28 //
29 // Class entries represent the PHP class implementations we have registered.
30 //
31 namespace IcePHP
32 {
33 
34 zend_class_entry* communicatorClassEntry = 0;
35 zend_class_entry* valueFactoryManagerClassEntry = 0;
36 
37 //
38 // An active communicator is in use by at least one request and may have
39 // registered so that it remains active after a request completes. The
40 // communicator is destroyed when there are no more references to this
41 // object.
42 //
43 class ActiveCommunicator : public IceUtil::Shared
44 {
45 public:
46 
47     ActiveCommunicator(const Ice::CommunicatorPtr& c);
48     ~ActiveCommunicator();
49 
50     const Ice::CommunicatorPtr communicator;
51     vector<string> ids;
52     int expires;
53     IceUtil::Time lastAccess;
54 };
55 typedef IceUtil::Handle<ActiveCommunicator> ActiveCommunicatorPtr;
56 
57 class FactoryWrapper;
58 typedef IceUtil::Handle<FactoryWrapper> FactoryWrapperPtr;
59 
60 class DefaultValueFactory;
61 typedef IceUtil::Handle<DefaultValueFactory> DefaultValueFactoryPtr;
62 
63 //
64 // CommunicatorInfoI encapsulates communicator-related information that
65 // is specific to a PHP "request". In other words, multiple PHP requests
66 // might share the same communicator instance but still need separate
67 // workspaces. For example, we don't want the value factories installed
68 // by one request to influence the behavior of another request.
69 //
70 class CommunicatorInfoI : public CommunicatorInfo
71 {
72 public:
73 
74     CommunicatorInfoI(const ActiveCommunicatorPtr&, zval*);
75 
76     virtual void getZval(zval*);
77     virtual void addRef(void);
78     virtual void decRef(void);
79 
80     virtual Ice::CommunicatorPtr getCommunicator() const;
81 
82     bool addFactory(zval*, const string&, bool);
83     FactoryWrapperPtr findFactory(const string&) const;
defaultFactory() const84     Ice::ValueFactoryPtr defaultFactory() const { return _defaultFactory; }
85     void destroyFactories(void);
86 
87     const ActiveCommunicatorPtr ac;
88     zval zv;
89 
90 private:
91 
92     typedef map<string, FactoryWrapperPtr> FactoryMap;
93 
94     FactoryMap _factories;
95     DefaultValueFactoryPtr _defaultFactory;
96 };
97 typedef IceUtil::Handle<CommunicatorInfoI> CommunicatorInfoIPtr;
98 
99 //
100 // Wraps a PHP object/value factory.
101 //
102 class FactoryWrapper : public Ice::ValueFactory
103 {
104 public:
105 
106     FactoryWrapper(zval*, bool, const CommunicatorInfoIPtr&);
107 
108     virtual Ice::ValuePtr create(const string&);
109 
110     void getZval(zval*);
111 
112     bool isObjectFactory() const;
113 
114     void destroy(void);
115 
116 protected:
117 
118     zval _factory;
119     bool _isObjectFactory;
120     CommunicatorInfoIPtr _info;
121 };
122 
123 //
124 // Implements the default value factory behavior.
125 //
126 class DefaultValueFactory : public Ice::ValueFactory
127 {
128 public:
129 
130     DefaultValueFactory(const CommunicatorInfoIPtr&);
131 
132     virtual Ice::ValuePtr create(const string&);
133 
setDelegate(const FactoryWrapperPtr & d)134     void setDelegate(const FactoryWrapperPtr& d) { _delegate = d; }
getDelegate() const135     FactoryWrapperPtr getDelegate() const { return _delegate; }
136 
137     void destroy(void);
138 
139 private:
140 
141     FactoryWrapperPtr _delegate;
142     CommunicatorInfoIPtr _info;
143 };
144 
145 //
146 // Each PHP request has its own set of value factories. More precisely, there is
147 // a value factory map for each communicator that is created by a PHP request.
148 // (see CommunicatorInfoI).
149 //
150 // We define a custom value factory manager implementation that delegates to
151 // to PHP objects supplied by the application.
152 //
153 // An instance of this class is installed as the communicator's value factory
154 // manager, and the class holds a reference to its communicator. When find() is
155 // invoked, the class resolves the appropriate factory as follows:
156 //
157 //  * Using its communicator reference as the key, look up the corresponding
158 //    CommunicatorInfoI object in the request-specific communicator map.
159 //
160 //  * If the type-id is empty, return the default factory. This factory will
161 //    either delegate to an application-supplied default factory (if present) or
162 //    default-construct an instance of a concrete Slice class type.
163 //
164 //  * For non-empty type-ids, return a wrapper around the application-supplied
165 //    factory, if any.
166 //
167 class ValueFactoryManager : public Ice::ValueFactoryManager
168 {
169 public:
170 
171     virtual void add(const Ice::ValueFactoryPtr&, const string&);
172     virtual Ice::ValueFactoryPtr find(const string&) const ICE_NOEXCEPT;
173 
setCommunicator(const Ice::CommunicatorPtr & c)174     void setCommunicator(const Ice::CommunicatorPtr& c) { _communicator = c; }
getCommunicator() const175     Ice::CommunicatorPtr getCommunicator() const { return _communicator; }
176 
177     void getZval(zval*);
178 
179     void destroy();
180 
181 private:
182 
183     Ice::CommunicatorPtr _communicator;
184 };
185 typedef IceUtil::Handle<ValueFactoryManager> ValueFactoryManagerPtr;
186 
187 class ReaperTask : public IceUtil::TimerTask
188 {
189 public:
190 
191     virtual void runTimerTask();
192 };
193 
194 }
195 
196 namespace
197 {
198 //
199 // Communicator support.
200 //
201 zend_object_handlers _handlers;
202 
203 //
204 // ValueFactoryManager support.
205 //
206 zend_object_handlers _vfmHandlers;
207 
208 //
209 // The profile map holds Properties objects corresponding to the "default" profile
210 // (defined via the ice.config & ice.options settings in php.ini) as well as named
211 // profiles defined in an external file.
212 //
213 typedef map<string, Ice::PropertiesPtr> ProfileMap;
214 ProfileMap _profiles;
215 const string _defaultProfileName = "";
216 
217 //
218 // This map represents communicators that have been registered so that they can be used
219 // by multiple PHP requests.
220 //
221 typedef map<string, ActiveCommunicatorPtr> RegisteredCommunicatorMap;
222 RegisteredCommunicatorMap _registeredCommunicators;
223 IceUtil::Mutex* _registeredCommunicatorsMutex = 0;
224 
225 IceUtil::TimerPtr _timer;
226 
227 //
228 // This map is stored in the "global" variables for each PHP request and holds
229 // the communicators that have been created (or registered communicators that have
230 // been used) by the request.
231 //
232 typedef map<Ice::CommunicatorPtr, CommunicatorInfoIPtr> CommunicatorMap;
233 
234 class Init
235 {
236 public:
237 
Init()238     Init()
239     {
240         _registeredCommunicatorsMutex = new IceUtil::Mutex();
241     }
242 
~Init()243     ~Init()
244     {
245         delete _registeredCommunicatorsMutex;
246         _registeredCommunicatorsMutex = 0;
247     }
248 };
249 
250 Init init;
251 }
252 
253 extern "C"
254 {
255 static zend_object* handleAlloc(zend_class_entry*);
256 static void handleFreeStorage(zend_object*);
257 static zend_object* handleClone(zval*);
258 
259 static zend_object* handleVfmAlloc(zend_class_entry*);
260 static void handleVfmFreeStorage(zend_object*);
261 static zend_object* handleVfmClone(zval*);
262 }
263 
ZEND_METHOD(Ice_Communicator,__construct)264 ZEND_METHOD(Ice_Communicator, __construct)
265 {
266     runtimeError("communicators cannot be instantiated directly");
267 }
268 
ZEND_METHOD(Ice_Communicator,shutdown)269 ZEND_METHOD(Ice_Communicator, shutdown)
270 {
271     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
272     assert(_this);
273 
274     try
275     {
276         _this->getCommunicator()->shutdown();
277     }
278     catch(const IceUtil::Exception& ex)
279     {
280         throwException(ex TSRMLS_CC);
281     }
282 }
283 
ZEND_METHOD(Ice_Communicator,isShutdown)284 ZEND_METHOD(Ice_Communicator, isShutdown)
285 {
286     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
287     assert(_this);
288 
289     try
290     {
291         RETURN_BOOL(_this->getCommunicator()->isShutdown() ? 1 : 0);
292     }
293     catch(const IceUtil::Exception& ex)
294     {
295         throwException(ex TSRMLS_CC);
296         RETURN_FALSE;
297     }
298 }
299 
ZEND_METHOD(Ice_Communicator,waitForShutdown)300 ZEND_METHOD(Ice_Communicator, waitForShutdown)
301 {
302     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
303     assert(_this);
304 
305     try
306     {
307         _this->getCommunicator()->waitForShutdown();
308     }
309     catch(const IceUtil::Exception& ex)
310     {
311         throwException(ex TSRMLS_CC);
312     }
313 }
314 
ZEND_METHOD(Ice_Communicator,destroy)315 ZEND_METHOD(Ice_Communicator, destroy)
316 {
317     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
318     assert(_this);
319 
320     Ice::CommunicatorPtr c = _this->getCommunicator();
321     assert(c);
322     CommunicatorMap* m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap));
323     assert(m);
324     if(m->find(c) != m->end())
325     {
326         m->erase(c);
327 
328         //
329         // Remove all registrations.
330         //
331         {
332             IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
333             for(vector<string>::iterator p = _this->ac->ids.begin(); p != _this->ac->ids.end(); ++p)
334             {
335                 _registeredCommunicators.erase(*p);
336             }
337             _this->ac->ids.clear();
338         }
339 
340         //
341         // We need to destroy any object|value factories installed by this request.
342         //
343         _this->destroyFactories();
344 
345         ValueFactoryManagerPtr vfm = ValueFactoryManagerPtr::dynamicCast(c->getValueFactoryManager());
346         assert(vfm);
347         vfm->destroy();
348 
349         c->destroy();
350     }
351 }
352 
ZEND_METHOD(Ice_Communicator,stringToProxy)353 ZEND_METHOD(Ice_Communicator, stringToProxy)
354 {
355     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
356     assert(_this);
357 
358     char* str;
359     size_t strLen;
360     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS)
361     {
362         RETURN_NULL();
363     }
364     string s(str, strLen);
365 
366     try
367     {
368         Ice::ObjectPrx prx = _this->getCommunicator()->stringToProxy(s);
369         if(!createProxy(return_value, prx, _this))
370         {
371             RETURN_NULL();
372         }
373     }
374     catch(const IceUtil::Exception& ex)
375     {
376         throwException(ex);
377         RETURN_NULL();
378     }
379 }
380 
ZEND_METHOD(Ice_Communicator,proxyToString)381 ZEND_METHOD(Ice_Communicator, proxyToString)
382 {
383     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
384     assert(_this);
385 
386     zval* zv;
387     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!"), &zv, proxyClassEntry) != SUCCESS)
388     {
389         RETURN_NULL();
390     }
391 
392     try
393     {
394         string str;
395         if(zv)
396         {
397             Ice::ObjectPrx prx;
398             ProxyInfoPtr info;
399             if(!fetchProxy(zv, prx, info))
400             {
401                 RETURN_NULL();
402             }
403             assert(prx);
404             str = prx->ice_toString();
405         }
406         RETURN_STRINGL(STRCAST(str.c_str()), static_cast<int>(str.length()));
407     }
408     catch(const IceUtil::Exception& ex)
409     {
410         throwException(ex);
411         RETURN_NULL();
412     }
413 }
414 
ZEND_METHOD(Ice_Communicator,propertyToProxy)415 ZEND_METHOD(Ice_Communicator, propertyToProxy)
416 {
417     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
418     assert(_this);
419 
420     char* str;
421     size_t strLen;
422     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS)
423     {
424         RETURN_NULL();
425     }
426     string s(str, strLen);
427 
428     try
429     {
430         Ice::ObjectPrx prx = _this->getCommunicator()->propertyToProxy(s);
431         if(!createProxy(return_value, prx, _this))
432         {
433             RETURN_NULL();
434         }
435     }
436     catch(const IceUtil::Exception& ex)
437     {
438         throwException(ex);
439         RETURN_NULL();
440     }
441 }
442 
ZEND_METHOD(Ice_Communicator,proxyToProperty)443 ZEND_METHOD(Ice_Communicator, proxyToProperty)
444 {
445     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
446     assert(_this);
447 
448     zval* zv;
449     char* str;
450     size_t strLen;
451     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!s"), &zv, proxyClassEntry, &str, &strLen)
452         != SUCCESS)
453     {
454         RETURN_NULL();
455     }
456 
457     string prefix(str, strLen);
458 
459     try
460     {
461         if(zv)
462         {
463             Ice::ObjectPrx prx;
464             ProxyInfoPtr info;
465             if(!fetchProxy(zv, prx, info))
466             {
467                 RETURN_NULL();
468             }
469             assert(prx);
470 
471             Ice::PropertyDict val = _this->getCommunicator()->proxyToProperty(prx, prefix);
472             if(!createStringMap(return_value, val))
473             {
474                 RETURN_NULL();
475             }
476         }
477         else
478         {
479             array_init(return_value);
480         }
481     }
482     catch(const IceUtil::Exception& ex)
483     {
484         throwException(ex);
485         RETURN_NULL();
486     }
487 }
488 
ZEND_METHOD(Ice_Communicator,stringToIdentity)489 ZEND_METHOD(Ice_Communicator, stringToIdentity)
490 {
491     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
492     assert(_this);
493 
494     char* str;
495     size_t strLen;
496     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS)
497     {
498         RETURN_NULL();
499     }
500     string s(str, strLen);
501 
502     try
503     {
504         Ice::Identity id = _this->getCommunicator()->stringToIdentity(s);
505         if(!createIdentity(return_value, id))
506         {
507             RETURN_NULL();
508         }
509     }
510     catch(const IceUtil::Exception& ex)
511     {
512         throwException(ex);
513         RETURN_NULL();
514     }
515 }
516 
ZEND_METHOD(Ice_Communicator,identityToString)517 ZEND_METHOD(Ice_Communicator, identityToString)
518 {
519     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
520     assert(_this);
521 
522     zend_class_entry* identityClass = idToClass("::Ice::Identity");
523     assert(identityClass);
524 
525     zval* zv;
526     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O"), &zv, identityClass) != SUCCESS)
527     {
528         RETURN_NULL();
529     }
530     Ice::Identity id;
531     if(!extractIdentity(zv, id))
532     {
533         RETURN_NULL();
534     }
535 
536     try
537     {
538         string str = _this->getCommunicator()->identityToString(id);
539         RETURN_STRINGL(STRCAST(str.c_str()), static_cast<int>(str.length()));
540     }
541     catch(const IceUtil::Exception& ex)
542     {
543         throwException(ex);
544         RETURN_NULL();
545     }
546 }
547 
ZEND_METHOD(Ice_Communicator,addObjectFactory)548 ZEND_METHOD(Ice_Communicator, addObjectFactory)
549 {
550     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
551     assert(_this);
552 
553     zend_class_entry* factoryClass = idToClass("Ice::ObjectFactory");
554     assert(factoryClass);
555 
556     zval* factory;
557     char* id;
558     size_t idLen;
559     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("Os!"), &factory, factoryClass, &id,
560                              &idLen) != SUCCESS)
561     {
562         RETURN_NULL();
563     }
564 
565     string type;
566     if(id)
567     {
568         type = string(id, idLen);
569     }
570 
571     if(!_this->addFactory(factory, type, true))
572     {
573         RETURN_NULL();
574     }
575 }
576 
ZEND_METHOD(Ice_Communicator,findObjectFactory)577 ZEND_METHOD(Ice_Communicator, findObjectFactory)
578 {
579     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
580     assert(_this);
581 
582     char* id;
583     size_t idLen;
584     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s!"), &id, &idLen) != SUCCESS)
585     {
586         RETURN_NULL();
587     }
588 
589     string type;
590     if(id)
591     {
592         type = string(id, idLen);
593     }
594 
595     FactoryWrapperPtr w = _this->findFactory(type);
596     if(w && w->isObjectFactory())
597     {
598         w->getZval(return_value);
599     }
600     else
601     {
602         RETURN_NULL();
603     }
604 }
605 
ZEND_METHOD(Ice_Communicator,getValueFactoryManager)606 ZEND_METHOD(Ice_Communicator, getValueFactoryManager)
607 {
608     if(ZEND_NUM_ARGS() > 0)
609     {
610         WRONG_PARAM_COUNT;
611     }
612 
613     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
614     assert(_this);
615 
616     try
617     {
618         ValueFactoryManagerPtr vfm =
619             ValueFactoryManagerPtr::dynamicCast(_this->getCommunicator()->getValueFactoryManager());
620         assert(vfm);
621         if(object_init_ex(return_value, valueFactoryManagerClassEntry) != SUCCESS)
622         {
623             runtimeError("unable to initialize properties object");
624             RETURN_NULL();
625         }
626 
627         Wrapper<ValueFactoryManagerPtr>* obj = Wrapper<ValueFactoryManagerPtr>::extract(return_value);
628         assert(!obj->ptr);
629         obj->ptr = new ValueFactoryManagerPtr(vfm);
630     }
631     catch(const IceUtil::Exception& ex)
632     {
633         throwException(ex);
634         RETURN_NULL();
635     }
636 }
637 
ZEND_METHOD(Ice_Communicator,getImplicitContext)638 ZEND_METHOD(Ice_Communicator, getImplicitContext)
639 {
640     runtimeError("not implemented");
641 }
642 
ZEND_METHOD(Ice_Communicator,getProperties)643 ZEND_METHOD(Ice_Communicator, getProperties)
644 {
645     if(ZEND_NUM_ARGS() > 0)
646     {
647         WRONG_PARAM_COUNT;
648     }
649 
650     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
651     assert(_this);
652 
653     try
654     {
655         Ice::PropertiesPtr props = _this->getCommunicator()->getProperties();
656         if(!createProperties(return_value, props))
657         {
658             RETURN_NULL();
659         }
660     }
661     catch(const IceUtil::Exception& ex)
662     {
663         throwException(ex);
664         RETURN_NULL();
665     }
666 }
667 
ZEND_METHOD(Ice_Communicator,getLogger)668 ZEND_METHOD(Ice_Communicator, getLogger)
669 {
670     if(ZEND_NUM_ARGS() > 0)
671     {
672         WRONG_PARAM_COUNT;
673     }
674 
675     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
676     assert(_this);
677 
678     try
679     {
680         Ice::LoggerPtr logger = _this->getCommunicator()->getLogger();
681         if(!createLogger(return_value, logger))
682         {
683             RETURN_NULL();
684         }
685     }
686     catch(const IceUtil::Exception& ex)
687     {
688         throwException(ex);
689         RETURN_NULL();
690     }
691 }
692 
ZEND_METHOD(Ice_Communicator,getDefaultRouter)693 ZEND_METHOD(Ice_Communicator, getDefaultRouter)
694 {
695     if(ZEND_NUM_ARGS() > 0)
696     {
697         WRONG_PARAM_COUNT;
698     }
699 
700     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
701     assert(_this);
702 
703     try
704     {
705         Ice::RouterPrx router = _this->getCommunicator()->getDefaultRouter();
706         if(router)
707         {
708             ProxyInfoPtr info = getProxyInfo("::Ice::Router");
709             if(!info)
710             {
711                 runtimeError("no definition for Ice::Router");
712                 RETURN_NULL();
713             }
714             if(!createProxy(return_value, router, info, _this))
715             {
716                 RETURN_NULL();
717             }
718         }
719         else
720         {
721             RETURN_NULL();
722         }
723     }
724     catch(const IceUtil::Exception& ex)
725     {
726         throwException(ex);
727         RETURN_NULL();
728     }
729 }
730 
ZEND_METHOD(Ice_Communicator,setDefaultRouter)731 ZEND_METHOD(Ice_Communicator, setDefaultRouter)
732 {
733     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
734     assert(_this);
735 
736     zval* zv;
737     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!"), &zv, proxyClassEntry) !=
738         SUCCESS)
739     {
740         RETURN_NULL();
741     }
742 
743     Ice::ObjectPrx proxy;
744     ProxyInfoPtr info;
745     if(zv && !fetchProxy(zv, proxy, info))
746     {
747         RETURN_NULL();
748     }
749 
750     try
751     {
752         Ice::RouterPrx router;
753         if(proxy)
754         {
755             if(!info || !info->isA("::Ice::Router"))
756             {
757                 invalidArgument("setDefaultRouter requires a proxy narrowed to Ice::Router");
758                 RETURN_NULL();
759             }
760             router = Ice::RouterPrx::uncheckedCast(proxy);
761         }
762         _this->getCommunicator()->setDefaultRouter(router);
763     }
764     catch(const IceUtil::Exception& ex)
765     {
766         throwException(ex);
767         RETURN_NULL();
768     }
769 }
770 
ZEND_METHOD(Ice_Communicator,getDefaultLocator)771 ZEND_METHOD(Ice_Communicator, getDefaultLocator)
772 {
773     if(ZEND_NUM_ARGS() > 0)
774     {
775         WRONG_PARAM_COUNT;
776     }
777 
778     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
779     assert(_this);
780 
781     try
782     {
783         Ice::LocatorPrx locator = _this->getCommunicator()->getDefaultLocator();
784         if(locator)
785         {
786             ProxyInfoPtr info = getProxyInfo("::Ice::Locator");
787             if(!info)
788             {
789                 runtimeError("no definition for Ice::Locator");
790                 RETURN_NULL();
791             }
792             if(!createProxy(return_value, locator, info, _this))
793             {
794                 RETURN_NULL();
795             }
796         }
797         else
798         {
799             RETURN_NULL();
800         }
801     }
802     catch(const IceUtil::Exception& ex)
803     {
804         throwException(ex);
805         RETURN_NULL();
806     }
807 }
808 
ZEND_METHOD(Ice_Communicator,setDefaultLocator)809 ZEND_METHOD(Ice_Communicator, setDefaultLocator)
810 {
811     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis());
812     assert(_this);
813 
814     zval* zv;
815     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O!"), &zv, proxyClassEntry) !=
816         SUCCESS)
817     {
818         RETURN_NULL();
819     }
820 
821     Ice::ObjectPrx proxy;
822     ProxyInfoPtr info;
823     if(zv && !fetchProxy(zv, proxy, info))
824     {
825         RETURN_NULL();
826     }
827 
828     try
829     {
830         Ice::LocatorPrx locator;
831         if(proxy)
832         {
833             if(!info || !info->isA("::Ice::Locator"))
834             {
835                 invalidArgument("setDefaultLocator requires a proxy narrowed to Ice::Locator");
836                 RETURN_NULL();
837             }
838             locator = Ice::LocatorPrx::uncheckedCast(proxy);
839         }
840         _this->getCommunicator()->setDefaultLocator(locator);
841     }
842     catch(const IceUtil::Exception& ex)
843     {
844         throwException(ex);
845         RETURN_NULL();
846     }
847 }
848 
ZEND_METHOD(Ice_Communicator,flushBatchRequests)849 ZEND_METHOD(Ice_Communicator, flushBatchRequests)
850 {
851     zval* compress;
852     if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("z"), &compress TSRMLS_CC) != SUCCESS)
853     {
854         RETURN_NULL();
855     }
856 
857     if(Z_TYPE_P(compress) != IS_LONG)
858     {
859         invalidArgument("value for 'compress' argument must be an enumerator of CompressBatch" TSRMLS_CC);
860         RETURN_NULL();
861     }
862     Ice::CompressBatch cb = static_cast<Ice::CompressBatch>(Z_LVAL_P(compress));
863 
864     CommunicatorInfoIPtr _this = Wrapper<CommunicatorInfoIPtr>::value(getThis() TSRMLS_CC);
865     assert(_this);
866 
867     try
868     {
869         _this->getCommunicator()->flushBatchRequests(cb);
870     }
871     catch(const IceUtil::Exception& ex)
872     {
873         throwException(ex TSRMLS_CC);
874         RETURN_NULL();
875     }
876 }
877 
ZEND_METHOD(Ice_ValueFactoryManager,__construct)878 ZEND_METHOD(Ice_ValueFactoryManager, __construct)
879 {
880     runtimeError("value factory managers cannot be instantiated directly");
881 }
882 
ZEND_METHOD(Ice_ValueFactoryManager,add)883 ZEND_METHOD(Ice_ValueFactoryManager, add)
884 {
885     ValueFactoryManagerPtr _this = Wrapper<ValueFactoryManagerPtr>::value(getThis());
886     assert(_this);
887 
888     zend_class_entry* factoryClass = idToClass("Ice::ValueFactory");
889     assert(factoryClass);
890 
891     zval* factory;
892     char* id;
893     size_t idLen;
894     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("Os!"), &factory, factoryClass, &id,
895                              &idLen) != SUCCESS)
896     {
897         RETURN_NULL();
898     }
899 
900     string type;
901     if(id)
902     {
903         type = string(id, idLen);
904     }
905 
906     CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
907     assert(m);
908     CommunicatorMap::iterator p = m->find(_this->getCommunicator());
909     assert(p != m->end());
910 
911     CommunicatorInfoIPtr info = p->second;
912 
913     if(!info->addFactory(factory, type, false))
914     {
915         RETURN_NULL();
916     }
917 }
918 
ZEND_METHOD(Ice_ValueFactoryManager,find)919 ZEND_METHOD(Ice_ValueFactoryManager, find)
920 {
921     ValueFactoryManagerPtr _this = Wrapper<ValueFactoryManagerPtr>::value(getThis());
922     assert(_this);
923 
924     char* id;
925     size_t idLen;
926     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s!"), &id, &idLen) != SUCCESS)
927     {
928         RETURN_NULL();
929     }
930 
931     string type;
932     if(id)
933     {
934         type = string(id, idLen);
935     }
936 
937     CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
938     assert(m);
939     CommunicatorMap::iterator p = m->find(_this->getCommunicator());
940     assert(p != m->end());
941 
942     CommunicatorInfoIPtr info = p->second;
943 
944     FactoryWrapperPtr w = info->findFactory(type);
945     if(w)
946     {
947         w->getZval(return_value);
948     }
949     else
950     {
951         RETURN_NULL();
952     }
953 }
954 
955 #ifdef _WIN32
956 extern "C"
957 #endif
958 static zend_object*
handleAlloc(zend_class_entry * ce)959 handleAlloc(zend_class_entry* ce)
960 {
961     Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::create(ce);
962     assert(obj);
963 
964     obj->zobj.handlers = &_handlers;
965 
966     return &obj->zobj;
967 }
968 
969 #ifdef _WIN32
970 extern "C"
971 #endif
972 static void
handleFreeStorage(zend_object * object)973 handleFreeStorage(zend_object* object)
974 {
975     Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::fetch(object);
976     assert(obj);
977 
978     delete obj->ptr;
979     zend_object_std_dtor(object);
980 }
981 
982 #ifdef _WIN32
983 extern "C"
984 #endif
985 static zend_object*
handleClone(zval * zv)986 handleClone(zval* zv)
987 {
988     php_error_docref(0, E_ERROR, "communicators cannot be cloned");
989     return 0;
990 }
991 
992 #ifdef _WIN32
993 extern "C"
994 #endif
995 static zend_object*
handleVfmAlloc(zend_class_entry * ce)996 handleVfmAlloc(zend_class_entry* ce)
997 {
998     Wrapper<ValueFactoryManagerPtr>* obj = Wrapper<ValueFactoryManagerPtr>::create(ce);
999     assert(obj);
1000 
1001     obj->zobj.handlers = &_vfmHandlers;
1002 
1003     return &obj->zobj;
1004 }
1005 
1006 #ifdef _WIN32
1007 extern "C"
1008 #endif
1009 static void
handleVfmFreeStorage(zend_object * object)1010 handleVfmFreeStorage(zend_object* object)
1011 {
1012     Wrapper<ValueFactoryManagerPtr>* obj = Wrapper<ValueFactoryManagerPtr>::fetch(object);
1013     assert(obj);
1014 
1015     delete obj->ptr;
1016     zend_object_std_dtor(object);
1017 }
1018 
1019 #ifdef _WIN32
1020 extern "C"
1021 #endif
1022 static zend_object*
handleVfmClone(zval * zv)1023 handleVfmClone(zval* zv)
1024 {
1025     php_error_docref(0, E_ERROR, "value factory managers cannot be cloned");
1026     return 0;
1027 }
1028 
1029 static CommunicatorInfoIPtr
createCommunicator(zval * zv,const ActiveCommunicatorPtr & ac)1030 createCommunicator(zval* zv, const ActiveCommunicatorPtr& ac)
1031 {
1032     try
1033     {
1034         if(object_init_ex(zv, communicatorClassEntry) != SUCCESS)
1035         {
1036             runtimeError("unable to initialize communicator object");
1037             return 0;
1038         }
1039 
1040         Wrapper<CommunicatorInfoIPtr>* obj = Wrapper<CommunicatorInfoIPtr>::extract(zv);
1041         assert(!obj->ptr);
1042 
1043         CommunicatorInfoIPtr info = new CommunicatorInfoI(ac, zv);
1044         obj->ptr = new CommunicatorInfoIPtr(info);
1045 
1046         CommunicatorMap* m;
1047         if(ICE_G(communicatorMap))
1048         {
1049             m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap));
1050         }
1051         else
1052         {
1053             m = new CommunicatorMap;
1054             ICE_G(communicatorMap) = m;
1055         }
1056         m->insert(CommunicatorMap::value_type(ac->communicator, info));
1057 
1058         return info;
1059     }
1060     catch(const IceUtil::Exception& ex)
1061     {
1062         throwException(ex);
1063         return 0;
1064     }
1065 }
1066 
1067 static CommunicatorInfoIPtr
initializeCommunicator(zval * zv,Ice::StringSeq & args,bool hasArgs,const Ice::InitializationData & initData)1068 initializeCommunicator(zval* zv, Ice::StringSeq& args, bool hasArgs, const Ice::InitializationData& initData)
1069 {
1070     try
1071     {
1072         Ice::CommunicatorPtr c;
1073         if(hasArgs)
1074         {
1075             c = Ice::initialize(args, initData);
1076         }
1077         else
1078         {
1079             c = Ice::initialize(initData);
1080         }
1081         ActiveCommunicatorPtr ac = new ActiveCommunicator(c);
1082 
1083         ValueFactoryManagerPtr vfm = ValueFactoryManagerPtr::dynamicCast(c->getValueFactoryManager());
1084         assert(vfm);
1085         vfm->setCommunicator(c);
1086 
1087         CommunicatorInfoIPtr info = createCommunicator(zv, ac);
1088         if(!info)
1089         {
1090             try
1091             {
1092                 c->destroy();
1093             }
1094             catch(...)
1095             {
1096             }
1097 
1098             vfm->destroy();
1099         }
1100 
1101         return info;
1102     }
1103     catch(const IceUtil::Exception& ex)
1104     {
1105         throwException(ex);
1106         return 0;
1107     }
1108 }
1109 
ZEND_FUNCTION(Ice_initialize)1110 ZEND_FUNCTION(Ice_initialize)
1111 {
1112     if(ZEND_NUM_ARGS() > 2)
1113     {
1114         runtimeError("too many arguments");
1115         RETURN_NULL();
1116     }
1117 
1118     zend_class_entry* initClass = idToClass("::Ice::InitializationData");
1119     assert(initClass);
1120 
1121     //
1122     // Retrieve the arguments.
1123     //
1124 
1125     zval* args = static_cast<zval*>(emalloc(ZEND_NUM_ARGS() * sizeof(zval)));
1126     AutoEfree autoArgs(args); // Call efree on return
1127     if(zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE)
1128     {
1129         runtimeError("unable to get arguments");
1130         RETURN_NULL();
1131     }
1132 
1133     Ice::StringSeq seq;
1134     Ice::InitializationData initData;
1135     zval* zvargs = 0;
1136     zval* zvinit = 0;
1137 
1138     //
1139     // The argument options are:
1140     //
1141     // initialize()
1142     // initialize(args)
1143     // initialize(initData)
1144     // initialize(args, initData)
1145     // initialize(initData, args)
1146     //
1147 
1148     if(ZEND_NUM_ARGS() > 2)
1149     {
1150         runtimeError("too many arguments to initialize");
1151         RETURN_NULL();
1152     }
1153 
1154     if(ZEND_NUM_ARGS() > 0)
1155     {
1156         zval* arg = &args[0];
1157         while(Z_TYPE_P(arg) == IS_REFERENCE)
1158         {
1159             arg = Z_REFVAL_P(arg);
1160         }
1161 
1162         if(Z_TYPE_P(arg) == IS_ARRAY)
1163         {
1164             zvargs = arg;
1165         }
1166         else if(Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == initClass)
1167         {
1168             zvinit = arg;
1169         }
1170         else
1171         {
1172             invalidArgument("initialize expects an argument list, an InitializationData object, or both");
1173             RETURN_NULL();
1174         }
1175     }
1176 
1177     if(ZEND_NUM_ARGS() > 1)
1178     {
1179         zval* arg = &args[1];
1180         while(Z_TYPE_P(arg) == IS_REFERENCE)
1181         {
1182             arg = Z_REFVAL_P(arg);
1183         }
1184 
1185         if(Z_TYPE_P(arg) == IS_ARRAY)
1186         {
1187             if(zvargs)
1188             {
1189                 invalidArgument("unexpected array argument to initialize");
1190                 RETURN_NULL();
1191             }
1192             zvargs = arg;
1193         }
1194         else if(Z_TYPE_P(arg) == IS_OBJECT && Z_OBJCE_P(arg) == initClass)
1195         {
1196             if(zvinit)
1197             {
1198                 invalidArgument("unexpected InitializationData argument to initialize");
1199                 RETURN_NULL();
1200             }
1201             zvinit = arg;
1202         }
1203         else
1204         {
1205             invalidArgument("initialize expects an argument list, an InitializationData object, or both");
1206             RETURN_NULL();
1207         }
1208     }
1209 
1210     if(zvargs && !extractStringArray(zvargs, seq))
1211     {
1212         RETURN_NULL();
1213     }
1214 
1215     if(zvinit)
1216     {
1217         zval* data;
1218         string member;
1219 
1220         member = "properties";
1221         {
1222             if((data = zend_hash_str_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size())) != 0)
1223             {
1224                 assert(Z_TYPE_P(data) == IS_INDIRECT);
1225                 if(!fetchProperties(Z_INDIRECT_P(data), initData.properties))
1226                 {
1227                     RETURN_NULL();
1228                 }
1229             }
1230         }
1231 
1232         member = "logger";
1233         {
1234             if((data = zend_hash_str_find(Z_OBJPROP_P(zvinit), STRCAST(member.c_str()), member.size())) != 0)
1235             {
1236                 assert(Z_TYPE_P(data) == IS_INDIRECT);
1237                 if(!fetchLogger(Z_INDIRECT_P(data), initData.logger))
1238                 {
1239                     RETURN_NULL();
1240                 }
1241             }
1242         }
1243     }
1244 
1245     initData.compactIdResolver = new IdResolver();
1246     initData.valueFactoryManager = new ValueFactoryManager;
1247 
1248     CommunicatorInfoIPtr info = initializeCommunicator(return_value, seq, zvargs != 0, initData);
1249     if(!info)
1250     {
1251         RETURN_NULL();
1252     }
1253 
1254     //
1255     // Replace the existing argument array with the filtered set.
1256     //
1257     if(zvargs)
1258     {
1259         zval_dtor(zvargs);
1260         if(!createStringArray(zvargs, seq))
1261         {
1262             RETURN_NULL();
1263         }
1264     }
1265 }
1266 
ZEND_FUNCTION(Ice_register)1267 ZEND_FUNCTION(Ice_register)
1268 {
1269     zval* comm;
1270     char* s;
1271     size_t sLen;
1272     zend_long expires = 0;
1273     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("Os|l"), &comm, communicatorClassEntry, &s,
1274                              &sLen, &expires) != SUCCESS)
1275     {
1276         RETURN_NULL();
1277     }
1278 
1279     string id(s, sLen);
1280     if(id.empty())
1281     {
1282         invalidArgument("communicator id cannot be empty");
1283         RETURN_NULL();
1284     }
1285 
1286     CommunicatorInfoIPtr info = Wrapper<CommunicatorInfoIPtr>::value(comm);
1287     assert(info);
1288 
1289     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
1290 
1291     RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
1292     if(p != _registeredCommunicators.end())
1293     {
1294         if(p->second->communicator != info->getCommunicator())
1295         {
1296             //
1297             // A different communicator is already registered with that ID.
1298             //
1299             RETURN_FALSE;
1300         }
1301     }
1302     else
1303     {
1304         info->ac->ids.push_back(id);
1305         _registeredCommunicators[id] = info->ac;
1306     }
1307 
1308     if(expires > 0)
1309     {
1310         //
1311         // Update the expiration time. If a communicator is registered with multiple IDs, we
1312         // always use the most recent expiration setting.
1313         //
1314         info->ac->expires = static_cast<int>(expires);
1315         info->ac->lastAccess = IceUtil::Time::now();
1316 
1317         //
1318         // Start the timer if necessary. Reap expired communicators every five minutes.
1319         //
1320         if(!_timer)
1321         {
1322             _timer = new IceUtil::Timer;
1323             _timer->scheduleRepeated(new ReaperTask, IceUtil::Time::seconds(5 * 60));
1324         }
1325     }
1326 
1327     RETURN_TRUE;
1328 }
1329 
ZEND_FUNCTION(Ice_unregister)1330 ZEND_FUNCTION(Ice_unregister)
1331 {
1332     char* s;
1333     size_t sLen;
1334     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &s, &sLen) != SUCCESS)
1335     {
1336         RETURN_NULL();
1337     }
1338 
1339     string id(s, sLen);
1340 
1341     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
1342 
1343     RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
1344     if(p == _registeredCommunicators.end())
1345     {
1346         //
1347         // No communicator registered with that ID.
1348         //
1349         RETURN_FALSE;
1350     }
1351 
1352     //
1353     // Remove the ID from the ActiveCommunicator's list of registered IDs.
1354     //
1355     ActiveCommunicatorPtr ac = p->second;
1356     vector<string>::iterator q = find(ac->ids.begin(), ac->ids.end(), id);
1357     assert(q != ac->ids.end());
1358     ac->ids.erase(q);
1359 
1360     _registeredCommunicators.erase(p);
1361 
1362     RETURN_TRUE;
1363 }
1364 
ZEND_FUNCTION(Ice_find)1365 ZEND_FUNCTION(Ice_find)
1366 {
1367     char* s;
1368     size_t sLen;
1369     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &s, &sLen) != SUCCESS)
1370     {
1371         RETURN_NULL();
1372     }
1373 
1374     string id(s, sLen);
1375 
1376     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
1377 
1378     RegisteredCommunicatorMap::iterator p = _registeredCommunicators.find(id);
1379     if(p == _registeredCommunicators.end())
1380     {
1381         //
1382         // No communicator registered with that ID.
1383         //
1384         RETURN_NULL();
1385     }
1386 
1387     if(p->second->expires > 0)
1388     {
1389         p->second->lastAccess = IceUtil::Time::now();
1390     }
1391 
1392     //
1393     // Check if this communicator has already been obtained by the current request.
1394     // If so, we can return the existing PHP object that corresponds to the communicator.
1395     //
1396     CommunicatorMap* m = reinterpret_cast<CommunicatorMap*>(ICE_G(communicatorMap));
1397     if(m)
1398     {
1399         CommunicatorMap::iterator q = m->find(p->second->communicator);
1400         if(q != m->end())
1401         {
1402             q->second->getZval(return_value);
1403             return;
1404         }
1405     }
1406 
1407     if(!createCommunicator(return_value, p->second))
1408     {
1409         RETURN_NULL();
1410     }
1411 }
1412 
ZEND_FUNCTION(Ice_getProperties)1413 ZEND_FUNCTION(Ice_getProperties)
1414 {
1415     char* s = 0;
1416     size_t sLen;
1417     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("|s"), &s, &sLen) != SUCCESS)
1418     {
1419         RETURN_NULL();
1420     }
1421 
1422     string name;
1423     if(s)
1424     {
1425         name = string(s, sLen);
1426     }
1427 
1428     ProfileMap::iterator p = _profiles.find(name);
1429     if(p == _profiles.end())
1430     {
1431         RETURN_NULL();
1432     }
1433 
1434     Ice::PropertiesPtr clone = p->second->clone();
1435     if(!createProperties(return_value, clone))
1436     {
1437         RETURN_NULL();
1438     }
1439 }
1440 
ZEND_FUNCTION(Ice_identityToString)1441 ZEND_FUNCTION(Ice_identityToString)
1442 {
1443     zend_class_entry* identityClass = idToClass("::Ice::Identity");
1444     assert(identityClass);
1445 
1446     zval* zv;
1447     zend_long mode = 0;
1448 
1449     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O|l"), &zv, identityClass, &mode) != SUCCESS)
1450     {
1451         RETURN_NULL();
1452     }
1453     Ice::Identity id;
1454     if(!extractIdentity(zv, id))
1455     {
1456         RETURN_NULL();
1457     }
1458 
1459     try
1460     {
1461         string str = Ice::identityToString(id, static_cast<Ice::ToStringMode>(mode));
1462         RETURN_STRINGL(STRCAST(str.c_str()), static_cast<int>(str.length()));
1463     }
1464     catch(const IceUtil::Exception& ex)
1465     {
1466         throwException(ex);
1467         RETURN_NULL();
1468     }
1469 }
1470 
ZEND_FUNCTION(Ice_stringToIdentity)1471 ZEND_FUNCTION(Ice_stringToIdentity)
1472 {
1473     char* str;
1474     size_t strLen;
1475     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS)
1476     {
1477         RETURN_NULL();
1478     }
1479     string s(str, strLen);
1480 
1481     try
1482     {
1483         Ice::Identity id = Ice::stringToIdentity(s);
1484         if(!createIdentity(return_value, id))
1485         {
1486             RETURN_NULL();
1487         }
1488     }
1489     catch(const IceUtil::Exception& ex)
1490     {
1491         throwException(ex);
1492         RETURN_NULL();
1493     }
1494 }
1495 
1496 //
1497 // Necessary to suppress warnings from zend_function_entry in php-5.2
1498 // and INI_STR macro.
1499 //
1500 #ifdef __GNUC__
1501 #   pragma GCC diagnostic ignored "-Wwrite-strings"
1502 #endif
1503 
1504 //
1505 // Predefined methods for Communicator.
1506 //
1507 static zend_function_entry _interfaceMethods[] =
1508 {
1509     {0, 0, 0}
1510 };
1511 static zend_function_entry _classMethods[] =
1512 {
1513     ZEND_ME(Ice_Communicator, __construct, ICE_NULLPTR, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR)
1514     ZEND_ME(Ice_Communicator, shutdown, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1515     ZEND_ME(Ice_Communicator, isShutdown, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1516     ZEND_ME(Ice_Communicator, waitForShutdown, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1517     ZEND_ME(Ice_Communicator, destroy, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1518     ZEND_ME(Ice_Communicator, stringToProxy, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1519     ZEND_ME(Ice_Communicator, proxyToString, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1520     ZEND_ME(Ice_Communicator, propertyToProxy, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1521     ZEND_ME(Ice_Communicator, proxyToProperty, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1522     ZEND_ME(Ice_Communicator, stringToIdentity, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1523     ZEND_ME(Ice_Communicator, identityToString, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1524     ZEND_ME(Ice_Communicator, addObjectFactory, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1525     ZEND_ME(Ice_Communicator, findObjectFactory, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1526     ZEND_ME(Ice_Communicator, getValueFactoryManager, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1527     ZEND_ME(Ice_Communicator, getImplicitContext, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1528     ZEND_ME(Ice_Communicator, getProperties, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1529     ZEND_ME(Ice_Communicator, getLogger, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1530     ZEND_ME(Ice_Communicator, getDefaultRouter, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1531     ZEND_ME(Ice_Communicator, setDefaultRouter, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1532     ZEND_ME(Ice_Communicator, getDefaultLocator, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1533     ZEND_ME(Ice_Communicator, setDefaultLocator, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1534     ZEND_ME(Ice_Communicator, flushBatchRequests, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1535     {0, 0, 0}
1536 };
1537 
1538 //
1539 // Predefined methods for ValueFactoryManager.
1540 //
1541 static zend_function_entry _vfmInterfaceMethods[] =
1542 {
1543     {0, 0, 0}
1544 };
1545 static zend_function_entry _vfmClassMethods[] =
1546 {
1547     ZEND_ME(Ice_ValueFactoryManager, __construct, ICE_NULLPTR, ZEND_ACC_PRIVATE|ZEND_ACC_CTOR)
1548     ZEND_ME(Ice_ValueFactoryManager, add, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1549     ZEND_ME(Ice_ValueFactoryManager, find, ICE_NULLPTR, ZEND_ACC_PUBLIC)
1550     {0, 0, 0}
1551 };
1552 
1553 static bool
createProfile(const string & name,const string & config,const string & options)1554 createProfile(const string& name, const string& config, const string& options)
1555 {
1556     ProfileMap::iterator p = _profiles.find(name);
1557     if(p != _profiles.end())
1558     {
1559         php_error_docref(0, E_WARNING, "duplicate Ice profile `%s'", name.c_str());
1560         return false;
1561     }
1562 
1563     Ice::PropertiesPtr properties = Ice::createProperties();
1564 
1565     if(!config.empty())
1566     {
1567         try
1568         {
1569             properties->load(config);
1570         }
1571         catch(const IceUtil::Exception& ex)
1572         {
1573             ostringstream ostr;
1574             ex.ice_print(ostr);
1575             php_error_docref(0, E_WARNING, "unable to load Ice configuration file %s:\n%s", config.c_str(),
1576                              ostr.str().c_str());
1577             return false;
1578         }
1579     }
1580 
1581     if(!options.empty())
1582     {
1583         vector<string> args;
1584         try
1585         {
1586             args = IceUtilInternal::Options::split(options);
1587         }
1588         catch(const IceUtil::Exception& ex)
1589         {
1590             ostringstream ostr;
1591             ex.ice_print(ostr);
1592             string msg = ostr.str();
1593             php_error_docref(0, E_WARNING, "error occurred while parsing the options `%s':\n%s",
1594                              options.c_str(), msg.c_str());
1595             return false;
1596         }
1597 
1598         properties->parseCommandLineOptions("", args);
1599     }
1600 
1601     _profiles[name] = properties;
1602     return true;
1603 }
1604 
1605 static bool
parseProfiles(const string & file)1606 parseProfiles(const string& file)
1607 {
1608     //
1609     // The Zend engine doesn't export a function for loading an INI file, so we
1610     // have to do it ourselves. The format is:
1611     //
1612     // [profile-name]
1613     // ice.config = config-file
1614     // ice.options = args
1615     //
1616     ifstream in(IceUtilInternal::streamFilename(file).c_str());
1617     if(!in)
1618     {
1619         php_error_docref(0, E_WARNING, "unable to open Ice profiles in %s", file.c_str());
1620         return false;
1621     }
1622 
1623     string name, config, options;
1624     char line[1024];
1625     while(in.getline(line, 1024))
1626     {
1627         const string delim = " \t\r\n";
1628         string s = line;
1629 
1630         string::size_type idx = s.find(';');
1631         if(idx != string::npos)
1632         {
1633             s.erase(idx);
1634         }
1635 
1636         idx = s.find_last_not_of(delim);
1637         if(idx != string::npos && idx + 1 < s.length())
1638         {
1639             s.erase(idx + 1);
1640         }
1641 
1642         string::size_type beg = s.find_first_not_of(delim);
1643         if(beg == string::npos)
1644         {
1645             continue;
1646         }
1647 
1648         if(s[beg] == '[')
1649         {
1650             beg++;
1651             string::size_type end = s.find_first_of(" \t]", beg);
1652             if(end == string::npos || s[s.length() - 1] != ']')
1653             {
1654                 php_error_docref(0, E_WARNING, "invalid profile section in file %s:\n%s\n", file.c_str(),
1655                                  line);
1656                 return false;
1657             }
1658 
1659             if(!name.empty())
1660             {
1661                 createProfile(name, config, options);
1662                 config.clear();
1663                 options.clear();
1664             }
1665 
1666             name = s.substr(beg, end - beg);
1667         }
1668         else
1669         {
1670             string::size_type end = s.find_first_of(delim + "=", beg);
1671             assert(end != string::npos);
1672 
1673             string key = s.substr(beg, end - beg);
1674 
1675             end = s.find('=', end);
1676             if(end == string::npos)
1677             {
1678                 php_error_docref(0, E_WARNING, "invalid profile entry in file %s:\n%s\n", file.c_str(), line);
1679                 return false;
1680             }
1681             ++end;
1682 
1683             string value;
1684             beg = s.find_first_not_of(delim, end);
1685             if(beg != string::npos)
1686             {
1687                 end = s.length();
1688                 value = s.substr(beg, end - beg);
1689 
1690                 //
1691                 // Check for quotes and remove them if present
1692                 //
1693                 string::size_type qpos = IceUtilInternal::checkQuote(value);
1694                 if(qpos != string::npos)
1695                 {
1696                     value = value.substr(1, qpos - 1);
1697                 }
1698             }
1699 
1700             if(key == "config" || key == "ice.config")
1701             {
1702                 config = value;
1703             }
1704             else if(key == "options" || key == "ice.options")
1705             {
1706                 options = value;
1707             }
1708             else
1709             {
1710                 php_error_docref(0, E_WARNING, "unknown profile entry in file %s:\n%s\n", file.c_str(), line);
1711             }
1712 
1713             if(name.empty())
1714             {
1715                 php_error_docref(0, E_WARNING, "no section for profile entry in file %s:\n%s\n", file.c_str(),
1716                                  line);
1717                 return false;
1718             }
1719         }
1720     }
1721 
1722     if(!name.empty())
1723     {
1724         if(!createProfile(name, config, options))
1725         {
1726             return false;
1727         }
1728     }
1729 
1730     return true;
1731 }
1732 
1733 bool
communicatorInit(void)1734 IcePHP::communicatorInit(void)
1735 {
1736     //
1737     // We register an interface and a class that implements the interface. This allows
1738     // applications to safely include the Slice-generated code for the type.
1739     //
1740 
1741     //
1742     // Register the Communicator interface.
1743     //
1744     zend_class_entry ce;
1745 #ifdef ICEPHP_USE_NAMESPACES
1746     INIT_NS_CLASS_ENTRY(ce, "Ice", "Communicator", _interfaceMethods);
1747 #else
1748     INIT_CLASS_ENTRY(ce, "Ice_Communicator", _interfaceMethods);
1749 #endif
1750     zend_class_entry* interface = zend_register_internal_interface(&ce);
1751 
1752     //
1753     // Register the Communicator class.
1754     //
1755     INIT_CLASS_ENTRY(ce, "IcePHP_Communicator", _classMethods);
1756     ce.create_object = handleAlloc;
1757     communicatorClassEntry = zend_register_internal_class(&ce);
1758     memcpy(&_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1759     _handlers.clone_obj = handleClone;
1760     _handlers.free_obj = handleFreeStorage;
1761     _handlers.offset = XtOffsetOf(Wrapper<CommunicatorInfoIPtr>, zobj);
1762     zend_class_implements(communicatorClassEntry, 1, interface);
1763 
1764     //
1765     // Register the ValueFactoryManager interface.
1766     //
1767 #ifdef ICEPHP_USE_NAMESPACES
1768     INIT_NS_CLASS_ENTRY(ce, "Ice", "ValueFactoryManager", _vfmInterfaceMethods);
1769 #else
1770     INIT_CLASS_ENTRY(ce, "Ice_ValueFactoryManager", _vfmInterfaceMethods);
1771 #endif
1772     zend_class_entry* vfmInterface = zend_register_internal_interface(&ce);
1773 
1774     //
1775     // Register the ValueFactoryManager class.
1776     //
1777     INIT_CLASS_ENTRY(ce, "IcePHP_ValueFactoryManager", _vfmClassMethods);
1778     ce.create_object = handleVfmAlloc;
1779     valueFactoryManagerClassEntry = zend_register_internal_class(&ce);
1780     memcpy(&_vfmHandlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
1781     _vfmHandlers.clone_obj = handleVfmClone;
1782     _vfmHandlers.free_obj = handleVfmFreeStorage;
1783     _vfmHandlers.offset   = XtOffsetOf(Wrapper<ValueFactoryManagerPtr>, zobj);
1784     zend_class_implements(valueFactoryManagerClassEntry, 1, vfmInterface);
1785 
1786     //
1787     // Create the profiles from configuration settings.
1788     //
1789     const char* empty = "";
1790     const char* config = INI_STR("ice.config"); // Needs to be a string literal!
1791     if(!config)
1792     {
1793         config = empty;
1794     }
1795     const char* options = INI_STR("ice.options"); // Needs to be a string literal!
1796     if(!options)
1797     {
1798         options = empty;
1799     }
1800     if(!createProfile(_defaultProfileName, config, options))
1801     {
1802         return false;
1803     }
1804 
1805     const char* profiles = INI_STR("ice.profiles"); // Needs to be a string literal!
1806     if(!profiles)
1807     {
1808         profiles = empty;
1809     }
1810     if(strlen(profiles) > 0)
1811     {
1812         if(!parseProfiles(profiles))
1813         {
1814             return false;
1815         }
1816 
1817         if(INI_BOOL(const_cast<char*>("ice.hide_profiles")))
1818         {
1819             memset(const_cast<char*>(profiles), '*', strlen(profiles));
1820             //
1821             // For some reason the code below does not work as expected. It causes a call
1822             // to ini_get_all() to segfault.
1823             //
1824             /*
1825             if(zend_alter_ini_entry("ice.profiles", sizeof("ice.profiles"), "<hidden>", sizeof("<hidden>") - 1,
1826                                     PHP_INI_ALL, PHP_INI_STAGE_STARTUP) == FAILURE)
1827             {
1828                 return false;
1829             }
1830             */
1831         }
1832     }
1833 
1834     return true;
1835 }
1836 
1837 bool
communicatorShutdown(void)1838 IcePHP::communicatorShutdown(void)
1839 {
1840     _profiles.clear();
1841 
1842     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
1843 
1844     if(_timer)
1845     {
1846         _timer->destroy();
1847         _timer = 0;
1848     }
1849 
1850     //
1851     // Clearing the map releases the last remaining reference counts of the ActiveCommunicator
1852     // objects. The ActiveCommunicator destructor destroys its communicator.
1853     //
1854     _registeredCommunicators.clear();
1855 
1856     return true;
1857 }
1858 
1859 bool
communicatorRequestInit(void)1860 IcePHP::communicatorRequestInit(void)
1861 {
1862     ICE_G(communicatorMap) = 0;
1863 
1864     return true;
1865 }
1866 
1867 bool
communicatorRequestShutdown(void)1868 IcePHP::communicatorRequestShutdown(void)
1869 {
1870     if(ICE_G(communicatorMap))
1871     {
1872         CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
1873         for(CommunicatorMap::iterator p = m->begin(); p != m->end(); ++p)
1874         {
1875             CommunicatorInfoIPtr info = p->second;
1876 
1877             //
1878             // We need to destroy any object|value factories installed during this request.
1879             //
1880             info->destroyFactories();
1881         }
1882 
1883         //
1884         // Deleting the map decrements the reference count of its ActiveCommunicator
1885         // values. If there are no other references to an ActiveCommunicator, its
1886         // destructor destroys the communicator.
1887         //
1888         delete m;
1889     }
1890 
1891     return true;
1892 }
1893 
ActiveCommunicator(const Ice::CommunicatorPtr & c)1894 IcePHP::ActiveCommunicator::ActiveCommunicator(const Ice::CommunicatorPtr& c) :
1895     communicator(c), expires(0)
1896 {
1897 }
1898 
~ActiveCommunicator()1899 IcePHP::ActiveCommunicator::~ActiveCommunicator()
1900 {
1901     //
1902     // There are no more references to this communicator, so we can safely destroy it now.
1903     //
1904     try
1905     {
1906         communicator->destroy();
1907     }
1908     catch(...)
1909     {
1910     }
1911 }
1912 
FactoryWrapper(zval * factory,bool isObjectFactory,const CommunicatorInfoIPtr & info)1913 IcePHP::FactoryWrapper::FactoryWrapper(zval* factory, bool isObjectFactory, const CommunicatorInfoIPtr& info) :
1914     _isObjectFactory(isObjectFactory),
1915     _info(info)
1916 {
1917     ZVAL_COPY(&_factory, factory);
1918 }
1919 
1920 Ice::ValuePtr
create(const string & id)1921 IcePHP::FactoryWrapper::create(const string& id)
1922 {
1923     //
1924     // Get the TSRM id for the current request.
1925     //
1926 
1927     //
1928     // Get the type information.
1929     //
1930     ClassInfoPtr cls;
1931     if(id == Ice::Object::ice_staticId())
1932     {
1933         //
1934         // When the ID is that of Ice::Object, it indicates that the stream has not
1935         // found a factory and is providing us an opportunity to preserve the object.
1936         //
1937         cls = getClassInfoById("::Ice::UnknownSlicedValue");
1938     }
1939     else
1940     {
1941         cls = getClassInfoById(id);
1942     }
1943 
1944     if(!cls)
1945     {
1946         return 0;
1947     }
1948 
1949     zval arg;
1950     ZVAL_STRINGL(&arg, STRCAST(id.c_str()), static_cast<int>(id.length()));
1951 
1952     zval obj;
1953     ZVAL_UNDEF(&obj);
1954 
1955     zend_try
1956     {
1957         assert(Z_TYPE(_factory) == IS_OBJECT);
1958         zend_call_method(&_factory, 0, 0, const_cast<char*>("create"), sizeof("create") - 1, &obj, 1, &arg, 0);
1959     }
1960     zend_catch
1961     {
1962         // obj;
1963     }
1964     zend_end_try();
1965 
1966     //
1967     // Bail out if an exception has already been thrown.
1968     //
1969     if(Z_ISUNDEF(obj) || EG(exception))
1970     {
1971         throw AbortMarshaling();
1972     }
1973 
1974     AutoDestroy destroy(&obj);
1975 
1976     if(Z_TYPE(obj) == IS_NULL)
1977     {
1978         return 0;
1979     }
1980 
1981     return new ObjectReader(&obj, cls, _info);
1982 }
1983 
1984 void
getZval(zval * factory)1985 IcePHP::FactoryWrapper::getZval(zval* factory)
1986 {
1987     ZVAL_DUP(factory, &_factory);
1988 }
1989 
1990 bool
isObjectFactory() const1991 IcePHP::FactoryWrapper::isObjectFactory() const
1992 {
1993     return _isObjectFactory;
1994 }
1995 
1996 void
destroy(void)1997 IcePHP::FactoryWrapper::destroy(void)
1998 {
1999     if(_isObjectFactory)
2000     {
2001         //
2002         // Invoke the destroy method on the PHP factory.
2003         //
2004         invokeMethod(&_factory, "destroy");
2005         zend_clear_exception();
2006     }
2007     zval_ptr_dtor(&_factory);
2008     _info = 0;
2009 }
2010 
DefaultValueFactory(const CommunicatorInfoIPtr & info)2011 IcePHP::DefaultValueFactory::DefaultValueFactory(const CommunicatorInfoIPtr& info) :
2012     _info(info)
2013 {
2014 }
2015 
2016 Ice::ValuePtr
create(const string & id)2017 IcePHP::DefaultValueFactory::create(const string& id)
2018 {
2019     //
2020     // Get the TSRM id for the current request.
2021     //
2022     if(_delegate)
2023     {
2024         Ice::ValuePtr v = _delegate->create(id);
2025         if(v)
2026         {
2027             return v;
2028         }
2029     }
2030 
2031     //
2032     // Get the type information.
2033     //
2034     ClassInfoPtr cls;
2035     if(id == Ice::Object::ice_staticId())
2036     {
2037         //
2038         // When the ID is that of Ice::Object, it indicates that the stream has not
2039         // found a factory and is providing us an opportunity to preserve the object.
2040         //
2041         cls = getClassInfoById("::Ice::UnknownSlicedValue");
2042     }
2043     else
2044     {
2045         cls = getClassInfoById(id);
2046     }
2047 
2048     if(!cls)
2049     {
2050         return 0;
2051     }
2052 
2053     //
2054     // Instantiate the object.
2055     //
2056     zval obj;
2057 
2058     if(object_init_ex(&obj, const_cast<zend_class_entry*>(cls->zce)) != SUCCESS)
2059     {
2060         throw AbortMarshaling();
2061     }
2062 
2063     if(!invokeMethod(&obj, ZEND_CONSTRUCTOR_FUNC_NAME))
2064     {
2065         throw AbortMarshaling();
2066     }
2067 
2068     return new ObjectReader(&obj, cls, _info);
2069 }
2070 
2071 void
destroy(void)2072 IcePHP::DefaultValueFactory::destroy(void)
2073 {
2074     if(_delegate)
2075     {
2076         _delegate->destroy();
2077         _delegate = 0;
2078     }
2079     _info = 0;
2080 }
2081 
CommunicatorInfoI(const ActiveCommunicatorPtr & c,zval * z)2082 IcePHP::CommunicatorInfoI::CommunicatorInfoI(const ActiveCommunicatorPtr& c, zval* z) :
2083     ac(c),
2084     _defaultFactory(new DefaultValueFactory(this))
2085 {
2086     ZVAL_COPY_VALUE(&zv, z);
2087 }
2088 
2089 void
getZval(zval * z)2090 IcePHP::CommunicatorInfoI::getZval(zval* z)
2091 {
2092     ZVAL_COPY_VALUE(z, &zv);
2093     addRef();
2094 }
2095 
2096 void
addRef(void)2097 IcePHP::CommunicatorInfoI::addRef(void)
2098 {
2099     Z_ADDREF_P(&zv);
2100 }
2101 
2102 void
decRef(void)2103 IcePHP::CommunicatorInfoI::decRef(void)
2104 {
2105     Z_DELREF_P(&zv);
2106 }
2107 
2108 Ice::CommunicatorPtr
getCommunicator() const2109 IcePHP::CommunicatorInfoI::getCommunicator() const
2110 {
2111     return ac->communicator;
2112 }
2113 
2114 bool
addFactory(zval * factory,const string & id,bool isObjectFactory)2115 IcePHP::CommunicatorInfoI::addFactory(zval* factory, const string& id, bool isObjectFactory)
2116 {
2117     if(id.empty())
2118     {
2119         if(_defaultFactory->getDelegate())
2120         {
2121             Ice::AlreadyRegisteredException ex(__FILE__, __LINE__);
2122             ex.kindOfObject = "value factory";
2123             ex.id = id;
2124             throwException(ex);
2125             return false;
2126         }
2127 
2128         _defaultFactory->setDelegate(new FactoryWrapper(factory, isObjectFactory, this));
2129     }
2130     else
2131     {
2132         FactoryMap::iterator p = _factories.find(id);
2133         if(p != _factories.end())
2134         {
2135             Ice::AlreadyRegisteredException ex(__FILE__, __LINE__);
2136             ex.kindOfObject = "value factory";
2137             ex.id = id;
2138             throwException(ex);
2139             return false;
2140         }
2141         _factories.insert(FactoryMap::value_type(id, new FactoryWrapper(factory, isObjectFactory, this)));
2142     }
2143 
2144     return true;
2145 }
2146 
2147 FactoryWrapperPtr
findFactory(const string & id) const2148 IcePHP::CommunicatorInfoI::findFactory(const string& id) const
2149 {
2150     if(id.empty())
2151     {
2152         return _defaultFactory->getDelegate();
2153     }
2154     else
2155     {
2156         FactoryMap::const_iterator p = _factories.find(id);
2157         if(p != _factories.end())
2158         {
2159             assert(p->second);
2160             return p->second;
2161         }
2162     }
2163     return 0;
2164 }
2165 
2166 void
destroyFactories(void)2167 IcePHP::CommunicatorInfoI::destroyFactories(void)
2168 {
2169     for(FactoryMap::iterator p = _factories.begin(); p != _factories.end(); ++p)
2170     {
2171         p->second->destroy();
2172     }
2173     _factories.clear();
2174     _defaultFactory->destroy();
2175 }
2176 
2177 void
add(const Ice::ValueFactoryPtr &,const string &)2178 IcePHP::ValueFactoryManager::add(const Ice::ValueFactoryPtr&, const string&)
2179 {
2180     //
2181     // We don't support factories registered in C++.
2182     //
2183     throw Ice::FeatureNotSupportedException(__FILE__, __LINE__, "C++ value factory");
2184 }
2185 
2186 Ice::ValueFactoryPtr
find(const string & id) const2187 IcePHP::ValueFactoryManager::find(const string& id) const ICE_NOEXCEPT
2188 {
2189     //
2190     // Get the TSRM id for the current request.
2191     //
2192 
2193     CommunicatorMap* m = static_cast<CommunicatorMap*>(ICE_G(communicatorMap));
2194     assert(m);
2195     CommunicatorMap::iterator p = m->find(_communicator);
2196     assert(p != m->end());
2197 
2198     CommunicatorInfoIPtr info = p->second;
2199 
2200     if(id.empty())
2201     {
2202         return info->defaultFactory();
2203     }
2204     else
2205     {
2206         return info->findFactory(id);
2207     }
2208 }
2209 
2210 void
destroy()2211 IcePHP::ValueFactoryManager::destroy()
2212 {
2213     _communicator = 0;
2214 }
2215 
2216 void
runTimerTask()2217 IcePHP::ReaperTask::runTimerTask()
2218 {
2219     IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(_registeredCommunicatorsMutex);
2220 
2221     IceUtil::Time now = IceUtil::Time::now();
2222     RegisteredCommunicatorMap::iterator p = _registeredCommunicators.begin();
2223     while(p != _registeredCommunicators.end())
2224     {
2225         if(p->second->lastAccess + IceUtil::Time::seconds(p->second->expires * 60) <= now)
2226         {
2227             try
2228             {
2229                 p->second->communicator->destroy();
2230             }
2231             catch(...)
2232             {
2233             }
2234             _registeredCommunicators.erase(p++);
2235         }
2236         else
2237         {
2238             ++p;
2239         }
2240     }
2241 }
2242