1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <Types.h>
6 #include <Proxy.h>
7 #include <Util.h>
8 #include <IceUtil/InputUtil.h>
9 #include <IceUtil/OutputUtil.h>
10 #include <IceUtil/ScopedArray.h>
11 #include <Slice/PHPUtil.h>
12 #include <Ice/SlicedData.h>
13 
14 #include <limits>
15 
16 using namespace std;
17 using namespace IcePHP;
18 using namespace IceUtil;
19 using namespace IceUtilInternal;
20 
21 ZEND_EXTERN_MODULE_GLOBALS(ice)
22 
23 //
24 // Class entries represent the PHP class implementations we have registered.
25 //
26 namespace IcePHP
27 {
28 zend_class_entry* typeInfoClassEntry = 0;
29 zend_class_entry* exceptionInfoClassEntry = 0;
30 }
31 
32 static zend_object_handlers _typeInfoHandlers;
33 static zend_object_handlers _exceptionInfoHandlers;
34 
35 static string _unsetGUID = "710A52F2-A014-4CB2-AF40-348D48DBCDDD";
36 
37 extern "C"
38 {
39 static zend_object* handleTypeInfoAlloc(zend_class_entry*);
40 static void handleTypeInfoFreeStorage(zend_object*);
41 
42 static zend_object* handleExceptionInfoAlloc(zend_class_entry*);
43 static void handleExceptionInfoFreeStorage(zend_object*);
44 }
45 
46 typedef map<string, ProxyInfoPtr> ProxyInfoMap;
47 typedef map<string, ClassInfoPtr> ClassInfoMap;
48 typedef map<Ice::Int, ClassInfoPtr> CompactIdMap;
49 typedef map<string, ExceptionInfoPtr> ExceptionInfoMap;
50 
51 //
52 // addProxyInfo()
53 //
54 static void
addProxyInfo(const ProxyInfoPtr & p)55 addProxyInfo(const ProxyInfoPtr& p)
56 {
57     ProxyInfoMap* m;
58     if(ICE_G(proxyInfoMap))
59     {
60         m = reinterpret_cast<ProxyInfoMap*>(ICE_G(proxyInfoMap));
61     }
62     else
63     {
64         m = new ProxyInfoMap;
65         ICE_G(proxyInfoMap) = m;
66     }
67     m->insert(ProxyInfoMap::value_type(p->id, p));
68 }
69 
70 //
71 // getProxyInfo()
72 //
73 IcePHP::ProxyInfoPtr
getProxyInfo(const string & id)74 IcePHP::getProxyInfo(const string& id)
75 {
76     if(ICE_G(proxyInfoMap))
77     {
78         ProxyInfoMap* m = reinterpret_cast<ProxyInfoMap*>(ICE_G(proxyInfoMap));
79         ProxyInfoMap::iterator p = m->find(id);
80         if(p != m->end())
81         {
82             return p->second;
83         }
84     }
85     return 0;
86 }
87 
88 //
89 // addClassInfoById()
90 //
91 static void
addClassInfoById(const ClassInfoPtr & p)92 addClassInfoById(const ClassInfoPtr& p)
93 {
94     assert(!getClassInfoById(p->id));
95 
96     ClassInfoMap* m = reinterpret_cast<ClassInfoMap*>(ICE_G(idToClassInfoMap));
97     if(!m)
98     {
99         m = new ClassInfoMap;
100         ICE_G(idToClassInfoMap) = m;
101     }
102     m->insert(ClassInfoMap::value_type(p->id, p));
103 }
104 
105 //
106 // addClassInfoByName()
107 //
108 static void
addClassInfoByName(const ClassInfoPtr & p)109 addClassInfoByName(const ClassInfoPtr& p)
110 {
111     assert(!getClassInfoByName(p->name));
112 #ifdef ICEPHP_USE_NAMESPACES
113     assert(p->name[0] == '\\');
114 #endif
115 
116     ClassInfoMap* m = reinterpret_cast<ClassInfoMap*>(ICE_G(nameToClassInfoMap));
117     if(!m)
118     {
119         m = new ClassInfoMap;
120         ICE_G(nameToClassInfoMap) = m;
121     }
122     m->insert(ClassInfoMap::value_type(p->name, p));
123 }
124 
125 static ClassInfoPtr
getClassInfoByClass(zend_class_entry * cls,zend_class_entry * formal)126 getClassInfoByClass(zend_class_entry* cls, zend_class_entry* formal)
127 {
128     //
129     // See if there's a match in our class name => ClassInfo map.
130     //
131     ClassInfoPtr info = getClassInfoByName(cls->name->val);
132 
133     //
134     // Check the base class, assuming it's compatible with our formal type (if any).
135     //
136     if(!info && cls->parent && (!formal || checkClass(cls->parent, formal)))
137     {
138         info = getClassInfoByClass(cls->parent, formal);
139     }
140 
141     //
142     // Check interfaces.
143     //
144     if(!info)
145     {
146         for(zend_ulong i = 0; i < cls->num_interfaces && !info; ++i)
147         {
148             if(!formal || checkClass(cls->interfaces[i], formal))
149             {
150                 info = getClassInfoByClass(cls->interfaces[i], formal);
151             }
152         }
153     }
154 
155     return info;
156 }
157 
158 //
159 // getClassInfoById()
160 //
161 IcePHP::ClassInfoPtr
getClassInfoById(const string & id)162 IcePHP::getClassInfoById(const string& id)
163 {
164     if(ICE_G(idToClassInfoMap))
165     {
166         ClassInfoMap* m = reinterpret_cast<ClassInfoMap*>(ICE_G(idToClassInfoMap));
167         ClassInfoMap::iterator p = m->find(id);
168         if(p != m->end())
169         {
170             return p->second;
171         }
172     }
173     return 0;
174 }
175 
176 //
177 // getClassInfoByName()
178 //
179 IcePHP::ClassInfoPtr
getClassInfoByName(const string & name)180 IcePHP::getClassInfoByName(const string& name)
181 {
182     if(ICE_G(nameToClassInfoMap))
183     {
184         string s = name;
185 
186 #ifdef ICEPHP_USE_NAMESPACES
187         //
188         // PHP's class definition (zend_class_entry) does not use a leading backslash
189         // in the class name.
190         //
191         if(s[0] != '\\')
192         {
193             s.insert(0, "\\");
194         }
195 #endif
196 
197         ClassInfoMap* m = reinterpret_cast<ClassInfoMap*>(ICE_G(nameToClassInfoMap));
198         ClassInfoMap::iterator p = m->find(s);
199         if(p != m->end())
200         {
201             return p->second;
202         }
203     }
204     return 0;
205 }
206 
207 //
208 // getExceptionInfo()
209 //
210 IcePHP::ExceptionInfoPtr
getExceptionInfo(const string & id)211 IcePHP::getExceptionInfo(const string& id)
212 {
213     if(ICE_G(exceptionInfoMap))
214     {
215         ExceptionInfoMap* m = reinterpret_cast<ExceptionInfoMap*>(ICE_G(exceptionInfoMap));
216         ExceptionInfoMap::iterator p = m->find(id);
217         if(p != m->end())
218         {
219             return p->second;
220         }
221     }
222     return 0;
223 }
224 
225 //
226 // StreamUtil implementation
227 //
228 zend_class_entry* IcePHP::StreamUtil::_slicedDataType = 0;
229 zend_class_entry* IcePHP::StreamUtil::_sliceInfoType = 0;
230 
~StreamUtil()231 IcePHP::StreamUtil::~StreamUtil()
232 {
233     //
234     // Make sure we break any cycles among the ObjectReaders in preserved slices.
235     //
236     for(set<ObjectReaderPtr>::iterator p = _readers.begin(); p != _readers.end(); ++p)
237     {
238         Ice::SlicedDataPtr slicedData = (*p)->getSlicedData();
239         for(Ice::SliceInfoSeq::const_iterator q = slicedData->slices.begin(); q != slicedData->slices.end(); ++q)
240         {
241             //
242             // Don't just call (*q)->instances.clear(), as releasing references
243             // to the instances could have unexpected side effects. We exchange
244             // the vector into a temporary and then let the temporary fall out
245             // of scope.
246             //
247             vector<Ice::ObjectPtr> tmp;
248             tmp.swap((*q)->instances);
249         }
250     }
251 }
252 
253 void
add(const ReadObjectCallbackPtr & callback)254 IcePHP::StreamUtil::add(const ReadObjectCallbackPtr& callback)
255 {
256     _callbacks.push_back(callback);
257 }
258 
259 void
add(const ObjectReaderPtr & reader)260 IcePHP::StreamUtil::add(const ObjectReaderPtr& reader)
261 {
262     assert(reader->getSlicedData());
263     _readers.insert(reader);
264 }
265 
266 void
updateSlicedData(void)267 IcePHP::StreamUtil::updateSlicedData(void)
268 {
269     for(set<ObjectReaderPtr>::iterator p = _readers.begin(); p != _readers.end(); ++p)
270     {
271         setSlicedDataMember((*p)->getObject(), (*p)->getSlicedData());
272     }
273 }
274 
275 void
setSlicedDataMember(zval * obj,const Ice::SlicedDataPtr & slicedData)276 IcePHP::StreamUtil::setSlicedDataMember(zval* obj, const Ice::SlicedDataPtr& slicedData)
277 {
278     //
279     // Create a PHP equivalent of the SlicedData object.
280     //
281 
282     assert(slicedData);
283 
284     if(!_slicedDataType)
285     {
286         _slicedDataType = idToClass("::Ice::SlicedData");
287         assert(_slicedDataType);
288     }
289     if(!_sliceInfoType)
290     {
291         _sliceInfoType = idToClass("::Ice::SliceInfo");
292         assert(_sliceInfoType);
293     }
294 
295     zval sd;
296     AutoDestroy sdDestroyer(&sd);
297 
298     if(object_init_ex(&sd, _slicedDataType) != SUCCESS)
299     {
300         throw AbortMarshaling();
301     }
302 
303     zval slices;
304     array_init(&slices);
305 #ifdef HT_ALLOW_COW_VIOLATION
306     HT_ALLOW_COW_VIOLATION(Z_ARRVAL(slices)); // Allow circular references.
307 #endif
308     AutoDestroy slicesDestroyer(&slices);
309 
310     if(add_property_zval(&sd, STRCAST("slices"), &slices) != SUCCESS)
311     {
312         throw AbortMarshaling();
313     }
314 
315     //
316     // Translate each SliceInfo object into its PHP equivalent.
317     //
318     for(vector<Ice::SliceInfoPtr>::const_iterator p = slicedData->slices.begin(); p != slicedData->slices.end(); ++p)
319     {
320         zval slice;
321         AutoDestroy sliceDestroyer(&slice);
322 
323         if(object_init_ex(&slice, _sliceInfoType) != SUCCESS)
324         {
325             throw AbortMarshaling();
326         }
327 
328         add_next_index_zval(&slices, &slice); // Steals a reference.
329         Z_ADDREF_P(&slice);
330 
331         //
332         // typeId
333         //
334         zval typeId;
335         AutoDestroy typeIdDestroyer(&typeId);
336         ZVAL_STRINGL(&typeId, STRCAST((*p)->typeId.c_str()), static_cast<int>((*p)->typeId.size()));
337         if(add_property_zval(&slice, STRCAST("typeId"), &typeId) != SUCCESS)
338         {
339             throw AbortMarshaling();
340         }
341 
342         //
343         // compactId
344         //
345         zval compactId;
346         AutoDestroy compactIdDestroyer(&compactId);
347         ZVAL_LONG(&compactId, (*p)->compactId);
348         if(add_property_zval(&slice, STRCAST("compactId"), &compactId) != SUCCESS)
349         {
350             throw AbortMarshaling();
351         }
352 
353         //
354         // bytes
355         //
356         zval bytes;
357         array_init(&bytes);
358         AutoDestroy bytesDestroyer(&bytes);
359         for(vector<Ice::Byte>::const_iterator q = (*p)->bytes.begin(); q != (*p)->bytes.end(); ++q)
360         {
361             add_next_index_long(&bytes, *q & 0xff);
362         }
363         if(add_property_zval(&slice, STRCAST("bytes"), &bytes) != SUCCESS)
364         {
365             throw AbortMarshaling();
366         }
367 
368         //
369         // instances
370         //
371         zval instances;
372         array_init(&instances);
373 #ifdef HT_ALLOW_COW_VIOLATION
374         HT_ALLOW_COW_VIOLATION(Z_ARRVAL(instances)); // Allow circular references.
375 #endif
376         AutoDestroy instancesDestroyer(&instances);
377         if(add_property_zval(&slice, STRCAST("instances"), &instances) != SUCCESS)
378         {
379             throw AbortMarshaling();
380         }
381 
382         for(vector<Ice::ObjectPtr>::const_iterator q = (*p)->instances.begin(); q != (*p)->instances.end(); ++q)
383         {
384             //
385             // Each element in the instances list is an instance of ObjectReader that wraps a PHP object.
386             //
387             assert(*q);
388             ObjectReaderPtr r = ObjectReaderPtr::dynamicCast(*q);
389             assert(r);
390             zval* o = r->getObject();
391             assert(Z_TYPE_P(o) == IS_OBJECT); // Should be non-nil.
392             add_next_index_zval(&instances, o); // Steals a reference.
393             Z_ADDREF_P(o);
394         }
395 
396         //
397         // hasOptionalMembers
398         //
399         zval hasOptionalMembers;
400         AutoDestroy hasOptionalMembersDestroyer(&hasOptionalMembers);
401         ZVAL_BOOL(&hasOptionalMembers, (*p)->hasOptionalMembers ? 1 : 0);
402         if(add_property_zval(&slice, STRCAST("hasOptionalMembers"), &hasOptionalMembers) != SUCCESS)
403         {
404             throw AbortMarshaling();
405         }
406 
407         //
408         // isLastSlice
409         //
410         zval isLastSlice;
411         AutoDestroy isLastSliceDestroyer(&isLastSlice);
412         ZVAL_BOOL(&isLastSlice, (*p)->isLastSlice ? 1 : 0);
413         if(add_property_zval(&slice, STRCAST("isLastSlice"), &isLastSlice) != SUCCESS)
414         {
415             throw AbortMarshaling();
416         }
417     }
418 
419     if(add_property_zval(obj, STRCAST("_ice_slicedData"), &sd) != SUCCESS)
420     {
421         throw AbortMarshaling();
422     }
423 }
424 
425 //
426 // Instances of preserved class and exception types may have a data member
427 // named _ice_slicedData which is an instance of the PHP class Ice_SlicedData.
428 //
429 Ice::SlicedDataPtr
getSlicedDataMember(zval * obj,ObjectMap * objectMap)430 IcePHP::StreamUtil::getSlicedDataMember(zval* obj, ObjectMap* objectMap)
431 {
432     Ice::SlicedDataPtr slicedData;
433 
434     string name = "_ice_slicedData";
435     zval* sd = zend_hash_str_find(Z_OBJPROP_P(obj), STRCAST(name.c_str()), name.size());
436     if(sd)
437     {
438         if(Z_TYPE_P(sd) != IS_NULL)
439         {
440             //
441             // The "slices" member is an array of Ice_SliceInfo objects.
442             //
443 
444             zval* sl = zend_hash_str_find(Z_OBJPROP_P(sd), STRCAST("slices"), sizeof("slices") - 1);
445             assert(sl);
446             assert(Z_TYPE_P(sl) == IS_INDIRECT);
447             sl = Z_INDIRECT_P(sl);
448             assert(Z_TYPE_P(sl) == IS_ARRAY);
449 
450             Ice::SliceInfoSeq slices;
451 
452             HashTable* arr = Z_ARRVAL_P(sl);
453             assert(arr);
454 
455             zval* s;
456 
457             ZEND_HASH_FOREACH_VAL(arr, s)
458             {
459                 assert(Z_OBJCE_P(s) == _sliceInfoType);
460 
461                 Ice::SliceInfoPtr info = new Ice::SliceInfo;
462 
463                 zval* typeId = zend_hash_str_find(Z_OBJPROP_P(s), STRCAST("typeId"), sizeof("typeId") - 1);
464                 assert(Z_TYPE_P(typeId) == IS_INDIRECT);
465                 typeId = Z_INDIRECT_P(typeId);
466                 assert(typeId && Z_TYPE_P(typeId) == IS_STRING);
467                 info->typeId = string(Z_STRVAL_P(typeId), Z_STRLEN_P(typeId));
468 
469                 zval* compactId = zend_hash_str_find(Z_OBJPROP_P(s), STRCAST("compactId"), sizeof("compactId") - 1);
470                 assert(Z_TYPE_P(compactId) == IS_INDIRECT);
471                 compactId = Z_INDIRECT_P(compactId);
472                 assert(compactId && Z_TYPE_P(compactId) == IS_LONG);
473                 info->compactId = static_cast<long>(Z_LVAL_P(compactId));
474 
475                 zval* bytes = zend_hash_str_find(Z_OBJPROP_P(s), STRCAST("bytes"), sizeof("bytes") - 1);
476                 assert(Z_TYPE_P(bytes) == IS_INDIRECT);
477                 bytes = Z_INDIRECT_P(bytes);
478                 assert(bytes && Z_TYPE_P(bytes) == IS_ARRAY);
479                 HashTable* barr = Z_ARRVAL_P(bytes);
480                 zval* e;
481                 info->bytes.resize(zend_hash_num_elements(barr));
482 
483 #if defined(__clang__)
484 #   pragma clang diagnostic push
485 #   pragma clang diagnostic ignored "-Wshadow"
486 #endif
487                 vector<Ice::Byte>::size_type i = 0;
488                 ZEND_HASH_FOREACH_VAL(barr, e)
489                 {
490                     long l = static_cast<long>(Z_LVAL_P(e));
491                     assert(l >= 0 && l <= 255);
492                     info->bytes[i++] = static_cast<Ice::Byte>(l);
493                 }
494                 ZEND_HASH_FOREACH_END();
495 #if defined(__clang__)
496 #   pragma clang diagnostic pop
497 #endif
498 
499                 zval* instances = zend_hash_str_find(Z_OBJPROP_P(s), STRCAST("instances"), sizeof("instances") - 1);
500                 assert(Z_TYPE_P(instances) == IS_INDIRECT);
501                 instances = Z_INDIRECT_P(instances);
502                 assert(instances && Z_TYPE_P(instances) == IS_ARRAY);
503                 HashTable* oarr = Z_ARRVAL_P(instances);
504                 zval* o;
505 
506 #if defined(__clang__)
507 #   pragma clang diagnostic push
508 #   pragma clang diagnostic ignored "-Wshadow"
509 #endif
510                 ZEND_HASH_FOREACH_VAL(oarr, o)
511                 {
512                     assert(Z_TYPE_P(o) == IS_OBJECT);
513 
514                     Ice::ObjectPtr writer;
515 
516                     ObjectMap::iterator i = objectMap->find(Z_OBJ_HANDLE_P(o));
517                     if(i == objectMap->end())
518                     {
519                         writer = new ObjectWriter(o, objectMap, 0);
520                         objectMap->insert(ObjectMap::value_type(Z_OBJ_HANDLE_P(o), writer));
521                     }
522                     else
523                     {
524                         writer = i->second;
525                     }
526 
527                     info->instances.push_back(writer);
528                 }
529                 ZEND_HASH_FOREACH_END();
530 #if defined(__clang__)
531 #   pragma clang diagnostic pop
532 #endif
533 
534                 zval* hasOptionalMembers =
535                     zend_hash_str_find(Z_OBJPROP_P(s), STRCAST("hasOptionalMembers"), sizeof("hasOptionalMembers") - 1);
536                 assert(Z_TYPE_P(hasOptionalMembers) == IS_INDIRECT);
537                 hasOptionalMembers = Z_INDIRECT_P(hasOptionalMembers);
538                 assert(hasOptionalMembers &&
539                     (Z_TYPE_P(hasOptionalMembers) == IS_TRUE || Z_TYPE_P(hasOptionalMembers) == IS_FALSE));
540                 info->hasOptionalMembers = Z_TYPE_P(hasOptionalMembers) == IS_TRUE;
541 
542                 zval* isLastSlice =
543                     zend_hash_str_find(Z_OBJPROP_P(s), STRCAST("isLastSlice"), sizeof("isLastSlice") - 1);
544                 assert(Z_TYPE_P(isLastSlice) == IS_INDIRECT);
545                 isLastSlice = Z_INDIRECT_P(isLastSlice);
546                 assert(isLastSlice && (Z_TYPE_P(isLastSlice) == IS_TRUE || Z_TYPE_P(isLastSlice) == IS_FALSE));
547                 info->isLastSlice = Z_TYPE_P(isLastSlice) == IS_TRUE;
548 
549                 slices.push_back(info);
550             }
551             ZEND_HASH_FOREACH_END();
552 
553             slicedData = new Ice::SlicedData(slices);
554         }
555     }
556 
557     return slicedData;
558 }
559 
560 //
561 // UnmarshalCallback implementation.
562 //
~UnmarshalCallback()563 IcePHP::UnmarshalCallback::~UnmarshalCallback()
564 {
565 }
566 
567 //
568 // TypeInfo implementation.
569 //
TypeInfo()570 IcePHP::TypeInfo::TypeInfo()
571 {
572 }
573 
574 bool
usesClasses() const575 IcePHP::TypeInfo::usesClasses() const
576 {
577     return false;
578 }
579 
580 void
unmarshaled(zval *,zval *,void *)581 IcePHP::TypeInfo::unmarshaled(zval*, zval*, void*)
582 {
583     assert(false);
584 }
585 
586 void
destroy()587 IcePHP::TypeInfo::destroy()
588 {
589 }
590 
591 //
592 // PrimitiveInfo implementation.
593 //
594 string
getId() const595 IcePHP::PrimitiveInfo::getId() const
596 {
597     switch(kind)
598     {
599     case KindBool:
600         return "bool";
601     case KindByte:
602         return "byte";
603     case KindShort:
604         return "short";
605     case KindInt:
606         return "int";
607     case KindLong:
608         return "long";
609     case KindFloat:
610         return "float";
611     case KindDouble:
612         return "double";
613     case KindString:
614         return "string";
615     }
616     assert(false);
617     return string();
618 }
619 
620 bool
validate(zval * zv,bool throwException)621 IcePHP::PrimitiveInfo::validate(zval* zv, bool throwException)
622 {
623     switch(kind)
624     {
625     case PrimitiveInfo::KindBool:
626     {
627         if(!(Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE))
628         {
629             string s = zendTypeToString(Z_TYPE_P(zv));
630             if(throwException)
631             {
632                 invalidArgument("expected boolean value but received %s", s.c_str());
633             }
634             return false;
635         }
636         break;
637     }
638     case PrimitiveInfo::KindByte:
639     {
640         if(Z_TYPE_P(zv) != IS_LONG)
641         {
642             string s = zendTypeToString(Z_TYPE_P(zv));
643             if(throwException)
644             {
645                 invalidArgument("expected byte value but received %s", s.c_str());
646             }
647             return false;
648         }
649         long val = static_cast<long>(Z_LVAL_P(zv));
650         if(val < 0 || val > 255)
651         {
652             if(throwException)
653             {
654                 invalidArgument("value %ld is out of range for a byte", val);
655             }
656             return false;
657         }
658         break;
659     }
660     case PrimitiveInfo::KindShort:
661     {
662         if(Z_TYPE_P(zv) != IS_LONG)
663         {
664             string s = zendTypeToString(Z_TYPE_P(zv));
665             if(throwException)
666             {
667                 invalidArgument("expected short value but received %s", s.c_str());
668             }
669             return false;
670         }
671         zend_long val = Z_LVAL_P(zv);
672         if(val < SHRT_MIN || val > SHRT_MAX)
673         {
674             if(throwException)
675             {
676                 invalidArgument("value %ld is out of range for a short", val);
677             }
678             return false;
679         }
680         break;
681     }
682     case PrimitiveInfo::KindInt:
683     {
684         if(Z_TYPE_P(zv) != IS_LONG)
685         {
686             string s = zendTypeToString(Z_TYPE_P(zv));
687             if(throwException)
688             {
689                 invalidArgument("expected int value but received %s", s.c_str());
690             }
691             return false;
692         }
693         zend_long val = Z_LVAL_P(zv);
694         if(val < INT_MIN || val > INT_MAX)
695         {
696             if(throwException)
697             {
698                 invalidArgument("value %ld is out of range for an int", val);
699             }
700             return false;
701         }
702         break;
703     }
704     case PrimitiveInfo::KindLong:
705     {
706         //
707         // The platform's 'long' type may not be 64 bits, so we also accept
708         // a string argument for this type.
709         //
710         if(Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_STRING)
711         {
712             string s = zendTypeToString(Z_TYPE_P(zv));
713             if(throwException)
714             {
715                 invalidArgument("expected long value but received %s", s.c_str());
716             }
717             return false;
718         }
719 
720         if(Z_TYPE_P(zv) != IS_LONG)
721         {
722             Ice::Long val;
723             string sval(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
724             if(!IceUtilInternal::stringToInt64(sval, val))
725             {
726                 if(throwException)
727                 {
728                     invalidArgument("invalid long value `%s'", Z_STRVAL_P(zv));
729                 }
730                 return false;
731             }
732         }
733         break;
734     }
735     case PrimitiveInfo::KindFloat:
736     {
737         if(Z_TYPE_P(zv) != IS_DOUBLE && Z_TYPE_P(zv) != IS_LONG)
738         {
739             string s = zendTypeToString(Z_TYPE_P(zv));
740             if(throwException)
741             {
742                 invalidArgument("expected float value but received %s", s.c_str());
743             }
744             return false;
745         }
746         if(Z_TYPE_P(zv) == IS_DOUBLE)
747         {
748             double val = Z_DVAL_P(zv);
749             return (val <= numeric_limits<float>::max() && val >= -numeric_limits<float>::max()) || !isfinite(val);
750         }
751         break;
752     }
753     case PrimitiveInfo::KindDouble:
754     {
755         if(Z_TYPE_P(zv) != IS_DOUBLE && Z_TYPE_P(zv) != IS_LONG)
756         {
757             string s = zendTypeToString(Z_TYPE_P(zv));
758             if(throwException)
759             {
760                 invalidArgument("expected double value but received %s", s.c_str());
761             }
762             return false;
763         }
764         break;
765     }
766     case PrimitiveInfo::KindString:
767     {
768         if(Z_TYPE_P(zv) != IS_STRING && Z_TYPE_P(zv) != IS_NULL)
769         {
770             string s = zendTypeToString(Z_TYPE_P(zv));
771             if(throwException)
772             {
773                 invalidArgument("expected string value but received %s", s.c_str());
774             }
775             return false;
776         }
777         break;
778     }
779     }
780 
781     return true;
782 }
783 
784 bool
variableLength() const785 IcePHP::PrimitiveInfo::variableLength() const
786 {
787     return kind == KindString;
788 }
789 
790 int
wireSize() const791 IcePHP::PrimitiveInfo::wireSize() const
792 {
793     switch(kind)
794     {
795     case KindBool:
796     case KindByte:
797         return 1;
798     case KindShort:
799         return 2;
800     case KindInt:
801         return 4;
802     case KindLong:
803         return 8;
804     case KindFloat:
805         return 4;
806     case KindDouble:
807         return 8;
808     case KindString:
809         return 1;
810     }
811     assert(false);
812     return 0;
813 }
814 
815 Ice::OptionalFormat
optionalFormat() const816 IcePHP::PrimitiveInfo::optionalFormat() const
817 {
818     switch(kind)
819     {
820     case KindBool:
821     case KindByte:
822         return Ice::OptionalFormatF1;
823     case KindShort:
824         return Ice::OptionalFormatF2;
825     case KindInt:
826         return Ice::OptionalFormatF4;
827     case KindLong:
828         return Ice::OptionalFormatF8;
829     case KindFloat:
830         return Ice::OptionalFormatF4;
831     case KindDouble:
832         return Ice::OptionalFormatF8;
833     case KindString:
834         return Ice::OptionalFormatVSize;
835     }
836 
837     assert(false);
838     return Ice::OptionalFormatF1;
839 }
840 
841 void
marshal(zval * zv,Ice::OutputStream * os,ObjectMap *,bool)842 IcePHP::PrimitiveInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap*, bool)
843 {
844     switch(kind)
845     {
846     case PrimitiveInfo::KindBool:
847     {
848         assert(Z_TYPE_P(zv) == IS_TRUE || Z_TYPE_P(zv) == IS_FALSE);
849         os->write(Z_TYPE_P(zv) == IS_TRUE);
850         break;
851     }
852     case PrimitiveInfo::KindByte:
853     {
854         assert(Z_TYPE_P(zv) == IS_LONG);
855         long val = static_cast<long>(Z_LVAL_P(zv));
856         assert(val >= 0 && val <= 255); // validate() should have caught this.
857         os->write(static_cast<Ice::Byte>(val));
858         break;
859     }
860     case PrimitiveInfo::KindShort:
861     {
862         assert(Z_TYPE_P(zv) == IS_LONG);
863         long val = static_cast<long>(Z_LVAL_P(zv));
864         assert(val >= SHRT_MIN && val <= SHRT_MAX); // validate() should have caught this.
865         os->write(static_cast<Ice::Short>(val));
866         break;
867     }
868     case PrimitiveInfo::KindInt:
869     {
870         assert(Z_TYPE_P(zv) == IS_LONG);
871         long val = static_cast<long>(Z_LVAL_P(zv));
872         assert(val >= INT_MIN && val <= INT_MAX); // validate() should have caught this.
873         os->write(static_cast<Ice::Int>(val));
874         break;
875     }
876     case PrimitiveInfo::KindLong:
877     {
878         //
879         // The platform's 'long' type may not be 64 bits, so we also accept
880         // a string argument for this type.
881         //
882         assert(Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_STRING); // validate() should have caught this.
883         Ice::Long val;
884         if(Z_TYPE_P(zv) == IS_LONG)
885         {
886             val = static_cast<long>(Z_LVAL_P(zv));
887         }
888         else
889         {
890             string sval(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
891             IceUtilInternal::stringToInt64(sval, val);
892         }
893         os->write(val);
894         break;
895     }
896     case PrimitiveInfo::KindFloat:
897     {
898         Ice::Double val = 0;
899         if(Z_TYPE_P(zv) == IS_DOUBLE)
900         {
901             val = Z_DVAL_P(zv);
902         }
903         else if(Z_TYPE_P(zv) == IS_LONG)
904         {
905             val = static_cast<double>(Z_LVAL_P(zv));
906         }
907         else
908         {
909             assert(false); // validate() should have caught this.
910         }
911         os->write(static_cast<Ice::Float>(val));
912         break;
913     }
914     case PrimitiveInfo::KindDouble:
915     {
916         Ice::Double val = 0;
917         if(Z_TYPE_P(zv) == IS_DOUBLE)
918         {
919             val = Z_DVAL_P(zv);
920         }
921         else if(Z_TYPE_P(zv) == IS_LONG)
922         {
923             val = static_cast<double>(Z_LVAL_P(zv));
924         }
925         else
926         {
927             assert(false); // validate() should have caught this.
928         }
929         os->write(val);
930         break;
931     }
932     case PrimitiveInfo::KindString:
933     {
934         assert(Z_TYPE_P(zv) == IS_STRING || Z_TYPE_P(zv) == IS_NULL); // validate() should have caught this.
935         if(Z_TYPE_P(zv) == IS_STRING)
936         {
937             string val(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
938             os->write(val);
939         }
940         else
941         {
942             os->write(string());
943         }
944         break;
945     }
946     }
947 }
948 
949 void
unmarshal(Ice::InputStream * is,const UnmarshalCallbackPtr & cb,const CommunicatorInfoPtr &,zval * target,void * closure,bool)950 IcePHP::PrimitiveInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb,
951                                  const CommunicatorInfoPtr&, zval* target, void* closure, bool)
952 {
953     zval zv;
954     AutoDestroy destroy(&zv);
955 
956     switch(kind)
957     {
958     case PrimitiveInfo::KindBool:
959     {
960         bool val;
961         is->read(val);
962         ZVAL_BOOL(&zv, val ? 1 : 0);
963         break;
964     }
965     case PrimitiveInfo::KindByte:
966     {
967         Ice::Byte val;
968         is->read(val);
969         ZVAL_LONG(&zv, val & 0xff);
970         break;
971     }
972     case PrimitiveInfo::KindShort:
973     {
974         Ice::Short val;
975         is->read(val);
976         ZVAL_LONG(&zv, val);
977         break;
978     }
979     case PrimitiveInfo::KindInt:
980     {
981         Ice::Int val;
982         is->read(val);
983         ZVAL_LONG(&zv, val);
984         break;
985     }
986     case PrimitiveInfo::KindLong:
987     {
988         Ice::Long val;
989         is->read(val);
990 
991         //
992         // The platform's 'long' type may not be 64 bits, so we store 64-bit
993         // values as a string.
994         //
995         if(sizeof(Ice::Long) > sizeof(long) && (val < LONG_MIN || val > LONG_MAX))
996         {
997             string str = IceUtilInternal::int64ToString(val);
998             ZVAL_STRINGL(&zv, STRCAST(str.c_str()), static_cast<int>(str.length()));
999         }
1000         else
1001         {
1002             ZVAL_LONG(&zv, static_cast<long>(val));
1003         }
1004         break;
1005     }
1006     case PrimitiveInfo::KindFloat:
1007     {
1008         Ice::Float val;
1009         is->read(val);
1010         ZVAL_DOUBLE(&zv, val);
1011         break;
1012     }
1013     case PrimitiveInfo::KindDouble:
1014     {
1015         Ice::Double val;
1016         is->read(val);
1017         ZVAL_DOUBLE(&zv, val);
1018         break;
1019     }
1020     case PrimitiveInfo::KindString:
1021     {
1022         string val;
1023         is->read(val);
1024         ZVAL_STRINGL(&zv, STRCAST(val.c_str()), static_cast<int>(val.length()));
1025         break;
1026     }
1027     }
1028     cb->unmarshaled(&zv, target, closure);
1029 }
1030 
1031 void
print(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory *)1032 IcePHP::PrimitiveInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory*)
1033 {
1034     if(!validate(zv, false))
1035     {
1036         out << "<invalid value - expected " << getId() << ">";
1037         return;
1038     }
1039     zval tmp;
1040     ZVAL_STR(&tmp, zval_get_string(zv));
1041     out << Z_STRVAL(tmp);
1042 }
1043 
1044 //
1045 // EnumInfo implementation.
1046 //
EnumInfo(const string & ident,zval * en)1047 IcePHP::EnumInfo::EnumInfo(const string& ident, zval* en) :
1048     id(ident),
1049     maxValue(0)
1050 {
1051     HashTable* arr = Z_ARRVAL_P(en);
1052     HashPosition pos;
1053     zval* val;
1054 
1055     zend_hash_internal_pointer_reset_ex(arr, &pos);
1056     while((val = zend_hash_get_current_data_ex(arr, &pos)) != 0)
1057     {
1058         assert(Z_TYPE_P(val) == IS_STRING);
1059         string name = Z_STRVAL_P(val);
1060         zend_hash_move_forward_ex(arr, &pos);
1061 
1062         val = zend_hash_get_current_data_ex(arr, &pos);
1063         assert(Z_TYPE_P(val) == IS_LONG);
1064         Ice::Int value = static_cast<Ice::Int>(Z_LVAL_P(val));
1065         zend_hash_move_forward_ex(arr, &pos);
1066 
1067         if(value > maxValue)
1068         {
1069             const_cast<int&>(maxValue) = value;
1070         }
1071 
1072         const_cast<map<Ice::Int, string>&>(enumerators)[value] = name;
1073     }
1074 }
1075 
1076 string
getId() const1077 IcePHP::EnumInfo::getId() const
1078 {
1079     return id;
1080 }
1081 
1082 bool
validate(zval * zv,bool)1083 IcePHP::EnumInfo::validate(zval* zv, bool)
1084 {
1085     if(Z_TYPE_P(zv) == IS_LONG)
1086     {
1087         const Ice::Int l = static_cast<Ice::Int>(Z_LVAL_P(zv));
1088         return l >= 0 && enumerators.find(l) != enumerators.end();
1089     }
1090     return false;
1091 }
1092 
1093 bool
variableLength() const1094 IcePHP::EnumInfo::variableLength() const
1095 {
1096     return true;
1097 }
1098 
1099 int
wireSize() const1100 IcePHP::EnumInfo::wireSize() const
1101 {
1102     return 1;
1103 }
1104 
1105 Ice::OptionalFormat
optionalFormat() const1106 IcePHP::EnumInfo::optionalFormat() const
1107 {
1108     return Ice::OptionalFormatSize;
1109 }
1110 
1111 void
marshal(zval * zv,Ice::OutputStream * os,ObjectMap *,bool)1112 IcePHP::EnumInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap*, bool)
1113 {
1114     assert(Z_TYPE_P(zv) == IS_LONG); // validate() should have caught this.
1115     const Ice::Int val = static_cast<Ice::Int>(Z_LVAL_P(zv));
1116     assert(val >= 0 && enumerators.find(val) != enumerators.end()); // validate() should have caught this.
1117 
1118     os->writeEnum(val, maxValue);
1119 }
1120 
1121 void
unmarshal(Ice::InputStream * is,const UnmarshalCallbackPtr & cb,const CommunicatorInfoPtr &,zval * target,void * closure,bool)1122 IcePHP::EnumInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb,
1123                             const CommunicatorInfoPtr&, zval* target, void* closure, bool)
1124 {
1125     zval zv;
1126     AutoDestroy destroy(&zv);
1127 
1128     const Ice::Int val = is->readEnum(maxValue);
1129 
1130     if(enumerators.find(val) == enumerators.end())
1131     {
1132         invalidArgument("enumerator %d is out of range for enum %s", val, id.c_str());
1133         throw AbortMarshaling();
1134     }
1135 
1136     ZVAL_LONG(&zv, val);
1137     cb->unmarshaled(&zv, target, closure);
1138 }
1139 
1140 void
print(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory *)1141 IcePHP::EnumInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory*)
1142 {
1143     if(!validate(zv, false))
1144     {
1145         out << "<invalid value - expected " << id << ">";
1146         return;
1147     }
1148     const Ice::Int val = static_cast<Ice::Int>(Z_LVAL_P(zv));
1149     map<Ice::Int, string>::const_iterator p = enumerators.find(val);
1150     assert(p != enumerators.end());
1151     out << p->second;
1152 }
1153 
1154 //
1155 // DataMember implementation.
1156 //
1157 void
unmarshaled(zval * zv,zval * target,void *)1158 IcePHP::DataMember::unmarshaled(zval* zv, zval* target, void*)
1159 {
1160     setMember(target, zv);
1161 }
1162 
1163 void
setMember(zval * target,zval * zv)1164 IcePHP::DataMember::setMember(zval* target, zval* zv)
1165 {
1166     assert(Z_TYPE_P(target) == IS_OBJECT);
1167 
1168     zend_update_property(Z_OBJCE_P(target), target, STRCAST(name.c_str()), strlen(name.c_str()), zv);
1169 }
1170 
1171 static void
convertDataMembers(zval * zv,DataMemberList & reqMembers,DataMemberList & optMembers,bool allowOptional)1172 convertDataMembers(zval* zv, DataMemberList& reqMembers, DataMemberList& optMembers, bool allowOptional)
1173 {
1174     list<DataMemberPtr> optList;
1175 
1176     assert(Z_TYPE_P(zv) == IS_ARRAY);
1177     HashTable* membersArray = Z_ARRVAL_P(zv);
1178     zval* arr;
1179 
1180     ZEND_HASH_FOREACH_VAL(membersArray, arr)
1181     {
1182         DataMemberPtr m = new DataMember();
1183         zval* elem;
1184 
1185         assert(Z_TYPE_P(arr) == IS_ARRAY);
1186         HashTable* member = Z_ARRVAL_P(arr);
1187         assert(zend_hash_num_elements(member) == static_cast<uint32_t>(allowOptional ? 4 : 2));
1188 
1189         elem = zend_hash_index_find(member, 0);
1190         assert(Z_TYPE_P(elem) == IS_STRING);
1191         m->name = Z_STRVAL_P(elem);
1192 
1193         elem = zend_hash_index_find(member, 1);
1194         assert(Z_TYPE_P(elem) == IS_OBJECT);
1195         m->type = Wrapper<TypeInfoPtr>::value(elem);
1196 
1197         if(allowOptional)
1198         {
1199             elem = zend_hash_index_find(member, 2);
1200             assert(Z_TYPE_P(elem) == IS_TRUE || Z_TYPE_P(elem) == IS_FALSE);
1201             m->optional = Z_TYPE_P(elem) == IS_TRUE;
1202 
1203             elem = zend_hash_index_find(member, 3);
1204             assert(Z_TYPE_P(elem) == IS_LONG);
1205             m->tag = static_cast<int>(Z_LVAL_P(elem));
1206         }
1207         else
1208         {
1209             m->optional = false;
1210             m->tag = 0;
1211         }
1212 
1213         if(m->optional)
1214         {
1215             optList.push_back(m);
1216         }
1217         else
1218         {
1219             reqMembers.push_back(m);
1220         }
1221     }
1222     ZEND_HASH_FOREACH_END();
1223 
1224     if(allowOptional)
1225     {
1226         class SortFn
1227         {
1228         public:
1229             static bool compare(const DataMemberPtr& lhs, const DataMemberPtr& rhs)
1230             {
1231                 return lhs->tag < rhs->tag;
1232             }
1233         };
1234 
1235         optList.sort(SortFn::compare);
1236         copy(optList.begin(), optList.end(), back_inserter(optMembers));
1237     }
1238 }
1239 
1240 //
1241 // StructInfo implementation.
1242 //
StructInfo(const string & ident,const string & n,zval * m)1243 IcePHP::StructInfo::StructInfo(const string& ident, const string& n, zval* m) :
1244     id(ident), name(n)
1245 {
1246     // Set to undefined
1247     ZVAL_UNDEF(&_nullMarshalValue);
1248 
1249     DataMemberList opt;
1250     convertDataMembers(m, const_cast<DataMemberList&>(members), opt, false);
1251     assert(opt.empty());
1252     const_cast<zend_class_entry*&>(zce) = nameToClass(name);
1253     assert(zce);
1254 
1255     _variableLength = false;
1256     _wireSize = 0;
1257     for(DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p)
1258     {
1259         if(!_variableLength && (*p)->type->variableLength())
1260         {
1261             _variableLength = true;
1262         }
1263         _wireSize += (*p)->type->wireSize();
1264     }
1265 }
1266 
1267 string
getId() const1268 IcePHP::StructInfo::getId() const
1269 {
1270     return id;
1271 }
1272 
1273 bool
validate(zval * zv,bool throwException)1274 IcePHP::StructInfo::validate(zval* zv, bool throwException)
1275 {
1276     if(Z_TYPE_P(zv) == IS_NULL)
1277     {
1278         return true;
1279     }
1280     else if(Z_TYPE_P(zv) != IS_OBJECT)
1281     {
1282         if(throwException)
1283         {
1284             string s = zendTypeToString(Z_TYPE_P(zv));
1285             invalidArgument("expected struct value of type %s but received %s", zce->name->val, s.c_str());
1286         }
1287         return false;
1288     }
1289 
1290     //
1291     // Compare class entries.
1292     //
1293     zend_class_entry* ce = Z_OBJCE_P(zv);
1294     if(ce != zce)
1295     {
1296         invalidArgument("expected struct value of type %s but received %s", zce->name->val, ce->name->val);
1297         return false;
1298     }
1299 
1300     return true;
1301 }
1302 
1303 bool
variableLength() const1304 IcePHP::StructInfo::variableLength() const
1305 {
1306     return _variableLength;
1307 }
1308 
1309 int
wireSize() const1310 IcePHP::StructInfo::wireSize() const
1311 {
1312     return _wireSize;
1313 }
1314 
1315 Ice::OptionalFormat
optionalFormat() const1316 IcePHP::StructInfo::optionalFormat() const
1317 {
1318     return _variableLength ? Ice::OptionalFormatFSize : Ice::OptionalFormatVSize;
1319 }
1320 
1321 bool
usesClasses() const1322 IcePHP::StructInfo::usesClasses() const
1323 {
1324     for(DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p)
1325     {
1326         if((*p)->type->usesClasses())
1327         {
1328             return true;
1329         }
1330     }
1331 
1332     return false;
1333 }
1334 
1335 void
marshal(zval * zv,Ice::OutputStream * os,ObjectMap * objectMap,bool optional)1336 IcePHP::StructInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool optional)
1337 {
1338     assert(Z_TYPE_P(zv) == IS_NULL || (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == zce));
1339 
1340     if(Z_TYPE_P(zv) == IS_NULL)
1341     {
1342         if(Z_ISUNDEF(_nullMarshalValue))
1343         {
1344             if(object_init_ex(&_nullMarshalValue, const_cast<zend_class_entry*>(zce)) != SUCCESS)
1345             {
1346                 runtimeError("unable to initialize object of type %s", zce->name->val);
1347                 throw AbortMarshaling();
1348             }
1349 
1350             if(!invokeMethod(&_nullMarshalValue, ZEND_CONSTRUCTOR_FUNC_NAME))
1351             {
1352                 assert(false);
1353             }
1354         }
1355         assert(!Z_ISUNDEF(_nullMarshalValue));
1356         ZVAL_COPY_VALUE(zv, &_nullMarshalValue);
1357     }
1358 
1359     Ice::OutputStream::size_type sizePos = 0;
1360     if(optional)
1361     {
1362         if(_variableLength)
1363         {
1364             sizePos = os->startSize();
1365         }
1366         else
1367         {
1368             os->writeSize(_wireSize);
1369         }
1370     }
1371 
1372     for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q)
1373     {
1374         DataMemberPtr member = *q;
1375 
1376         zval* val = zend_hash_str_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size());
1377         if(!val)
1378         {
1379             runtimeError("member `%s' of %s is not defined", member->name.c_str(), id.c_str());
1380             throw AbortMarshaling();
1381         }
1382 
1383         if(Z_TYPE_P(val) == IS_INDIRECT)
1384         {
1385             val = Z_INDIRECT_P(val);
1386         }
1387 
1388         if(!member->type->validate(val, false))
1389         {
1390             invalidArgument("invalid value for %s member `%s'", id.c_str(), member->name.c_str());
1391             throw AbortMarshaling();
1392         }
1393 
1394         member->type->marshal(val, os, objectMap, false);
1395     }
1396 
1397     if(optional && _variableLength)
1398     {
1399         os->endSize(sizePos);
1400     }
1401 }
1402 
1403 void
unmarshal(Ice::InputStream * is,const UnmarshalCallbackPtr & cb,const CommunicatorInfoPtr & comm,zval * target,void * closure,bool optional)1404 IcePHP::StructInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb,
1405                               const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional)
1406 {
1407     zval zv;
1408     AutoDestroy destroy(&zv);
1409     if(object_init_ex(&zv, const_cast<zend_class_entry*>(zce)) != SUCCESS)
1410     {
1411         runtimeError("unable to initialize object of type %s", zce->name->val);
1412         throw AbortMarshaling();
1413     }
1414 
1415     if(optional)
1416     {
1417         if(_variableLength)
1418         {
1419             is->skip(4);
1420         }
1421         else
1422         {
1423             is->skipSize();
1424         }
1425     }
1426 
1427     for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q)
1428     {
1429         DataMemberPtr member = *q;
1430         member->type->unmarshal(is, member, comm, &zv, 0, false);
1431     }
1432 
1433     cb->unmarshaled(&zv, target, closure);
1434 }
1435 
1436 void
print(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory * history)1437 IcePHP::StructInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history)
1438 {
1439     if(!validate(zv, false))
1440     {
1441         out << "<invalid value - expected " << id << ">";
1442         return;
1443     }
1444 
1445     if(Z_TYPE_P(zv) == IS_NULL)
1446     {
1447         out << "<nil>";
1448     }
1449     else
1450     {
1451         out.sb();
1452         for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q)
1453         {
1454             DataMemberPtr member = *q;
1455 
1456             out << nl << member->name << " = ";
1457             zval* val = zend_hash_str_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size());
1458             assert(Z_TYPE_P(val) == IS_INDIRECT);
1459             val = Z_INDIRECT_P(val);
1460             if(val)
1461             {
1462                 member->type->print(val, out, history);
1463             }
1464             else
1465             {
1466                 out << "<not defined>";
1467             }
1468         }
1469         out.eb();
1470     }
1471 }
1472 
1473 void
destroy()1474 IcePHP::StructInfo::destroy()
1475 {
1476     for(DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p)
1477     {
1478         (*p)->type->destroy();
1479     }
1480     const_cast<DataMemberList&>(members).clear();
1481     if(!Z_ISUNDEF(_nullMarshalValue))
1482     {
1483         zval_ptr_dtor(&_nullMarshalValue);
1484     }
1485 }
1486 
1487 //
1488 // SequenceInfo implementation.
1489 //
SequenceInfo(const string & ident,zval * e)1490 IcePHP::SequenceInfo::SequenceInfo(const string& ident, zval* e) :
1491     id(ident)
1492 {
1493     const_cast<TypeInfoPtr&>(elementType) = Wrapper<TypeInfoPtr>::value(e);
1494 }
1495 
1496 string
getId() const1497 IcePHP::SequenceInfo::getId() const
1498 {
1499     return id;
1500 }
1501 
1502 bool
validate(zval * zv,bool)1503 IcePHP::SequenceInfo::validate(zval* zv, bool)
1504 {
1505     return Z_TYPE_P(zv) == IS_NULL || Z_TYPE_P(zv) == IS_ARRAY;
1506 }
1507 
1508 bool
variableLength() const1509 IcePHP::SequenceInfo::variableLength() const
1510 {
1511     return true;
1512 }
1513 
1514 int
wireSize() const1515 IcePHP::SequenceInfo::wireSize() const
1516 {
1517     return 1;
1518 }
1519 
1520 Ice::OptionalFormat
optionalFormat() const1521 IcePHP::SequenceInfo::optionalFormat() const
1522 {
1523     return elementType->variableLength() ? Ice::OptionalFormatFSize : Ice::OptionalFormatVSize;
1524 }
1525 
1526 bool
usesClasses() const1527 IcePHP::SequenceInfo::usesClasses() const
1528 {
1529     return elementType->usesClasses();
1530 }
1531 
1532 void
marshal(zval * zv,Ice::OutputStream * os,ObjectMap * objectMap,bool optional)1533 IcePHP::SequenceInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool optional)
1534 {
1535     Ice::Int sz = 0;
1536     HashTable* arr = 0;
1537 
1538     if(Z_TYPE_P(zv) != IS_NULL)
1539     {
1540         assert(Z_TYPE_P(zv) == IS_ARRAY); // validate() should have caught this.
1541         arr = Z_ARRVAL_P(zv);
1542         sz = static_cast<Ice::Int>(zend_hash_num_elements(arr));
1543     }
1544 
1545     Ice::OutputStream::size_type sizePos = 0;
1546     if(optional)
1547     {
1548         if(elementType->variableLength())
1549         {
1550             sizePos = os->startSize();
1551         }
1552         else if(elementType->wireSize() > 1)
1553         {
1554             os->writeSize(sz == 0 ? 1 : sz * elementType->wireSize() + (sz > 254 ? 5 : 1));
1555         }
1556     }
1557 
1558     if(sz == 0)
1559     {
1560         os->writeSize(0);
1561     }
1562     else
1563     {
1564         PrimitiveInfoPtr pi = PrimitiveInfoPtr::dynamicCast(elementType);
1565         if(pi)
1566         {
1567             marshalPrimitiveSequence(pi, zv, os);
1568             return;
1569         }
1570 
1571         os->writeSize(sz);
1572 
1573         zval* val;
1574         ZEND_HASH_FOREACH_VAL(arr, val)
1575         {
1576             if(!elementType->validate(val, false))
1577             {
1578                 invalidArgument("invalid value for sequence element `%s'", id.c_str());
1579                 throw AbortMarshaling();
1580             }
1581             elementType->marshal(val, os, objectMap, false);
1582         }
1583         ZEND_HASH_FOREACH_END();
1584     }
1585 
1586     if(optional && elementType->variableLength())
1587     {
1588         os->endSize(sizePos);
1589     }
1590 }
1591 
1592 void
unmarshal(Ice::InputStream * is,const UnmarshalCallbackPtr & cb,const CommunicatorInfoPtr & comm,zval * target,void * closure,bool optional)1593 IcePHP::SequenceInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb,
1594                                 const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional)
1595 {
1596     if(optional)
1597     {
1598         if(elementType->variableLength())
1599         {
1600             is->skip(4);
1601         }
1602         else if(elementType->wireSize() > 1)
1603         {
1604             is->skipSize();
1605         }
1606     }
1607 
1608     PrimitiveInfoPtr pi = PrimitiveInfoPtr::dynamicCast(elementType);
1609     if(pi)
1610     {
1611         unmarshalPrimitiveSequence(pi, is, cb, target, closure);
1612         return;
1613     }
1614 
1615     zval zv;
1616     array_init(&zv);
1617 #ifdef HT_ALLOW_COW_VIOLATION
1618     HT_ALLOW_COW_VIOLATION(Z_ARRVAL(zv)); // Allow circular references.
1619 #endif
1620     AutoDestroy destroy(&zv);
1621 
1622     Ice::Int sz = is->readSize();
1623     for(Ice::Int i = 0; i < sz; ++i)
1624     {
1625 #ifdef _MSC_VER
1626 #    pragma warning(disable:4311)
1627 #    pragma warning(disable:4312)
1628 #endif
1629         void* cl = reinterpret_cast<void*>(i);
1630 #ifdef _MSC_VER
1631 #    pragma warning(default:4311)
1632 #    pragma warning(default:4312)
1633 #endif
1634         //
1635         // Add a temporary null value so that the foreach order is the
1636         // same as the index order.
1637         //
1638         add_index_null(&zv, i);
1639         elementType->unmarshal(is, this, comm, &zv, cl, false);
1640     }
1641 
1642     cb->unmarshaled(&zv, target, closure);
1643 }
1644 
1645 void
print(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory * history)1646 IcePHP::SequenceInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history)
1647 {
1648     if(!validate(zv, false))
1649     {
1650         out << "<invalid value - expected " << id << ">";
1651         return;
1652     }
1653 
1654     if(Z_TYPE_P(zv) == IS_NULL)
1655     {
1656         out << "{}";
1657     }
1658     else
1659     {
1660         assert(Z_TYPE_P(zv) == IS_ARRAY);
1661 
1662         HashTable* arr = Z_ARRVAL_P(zv);
1663 
1664         out.sb();
1665 
1666         int i = 0;
1667         zval* val;
1668         ZEND_HASH_FOREACH_VAL(arr, val)
1669         {
1670             out << nl << '[' << i << "] = ";
1671             elementType->print(val, out, history);
1672             ++i;
1673         }
1674         ZEND_HASH_FOREACH_END();
1675 
1676         out.eb();
1677     }
1678 }
1679 
1680 void
unmarshaled(zval * zv,zval * target,void * closure)1681 IcePHP::SequenceInfo::unmarshaled(zval* zv, zval* target, void* closure)
1682 {
1683     assert(Z_TYPE_P(target) != IS_REFERENCE);
1684     assert(Z_TYPE_P(target) == IS_ARRAY);
1685 
1686 #ifdef _MSC_VER
1687 #    pragma warning(disable:4302)
1688 #    pragma warning(disable:4311)
1689 #endif
1690     long i = reinterpret_cast<long>(closure);
1691 #ifdef _MSC_VER
1692 #    pragma warning(default:4302)
1693 #    pragma warning(disable:4311)
1694 #endif
1695     add_index_zval(target, i, zv);
1696     if(Z_REFCOUNTED_P(zv))
1697     {
1698         Z_ADDREF_P(zv);
1699     }
1700 }
1701 
1702 void
destroy()1703 IcePHP::SequenceInfo::destroy()
1704 {
1705     if(elementType)
1706     {
1707         elementType->destroy();
1708         const_cast<TypeInfoPtr&>(elementType) = 0;
1709     }
1710 }
1711 
1712 void
marshalPrimitiveSequence(const PrimitiveInfoPtr & pi,zval * zv,Ice::OutputStream * os)1713 IcePHP::SequenceInfo::marshalPrimitiveSequence(const PrimitiveInfoPtr& pi, zval* zv, Ice::OutputStream* os)
1714 {
1715     HashTable* arr = Z_ARRVAL_P(zv);
1716 
1717     Ice::Int sz = static_cast<Ice::Int>(zend_hash_num_elements(arr));
1718     assert(sz > 0);
1719 
1720     switch(pi->kind)
1721     {
1722     case PrimitiveInfo::KindBool:
1723     {
1724         Ice::BoolSeq seq(sz);
1725         Ice::Int i = 0;
1726         zval* val;
1727         ZEND_HASH_FOREACH_VAL(arr, val)
1728         {
1729             if(!pi->validate(val, true))
1730             {
1731                 throw AbortMarshaling();
1732             }
1733             seq[i++] = Z_TYPE_P(val) == IS_TRUE;
1734         }
1735         ZEND_HASH_FOREACH_END();
1736         os->write(seq);
1737         break;
1738     }
1739     case PrimitiveInfo::KindByte:
1740     {
1741         Ice::ByteSeq seq(sz);
1742         Ice::Int i = 0;
1743         zval* val;
1744         ZEND_HASH_FOREACH_VAL(arr, val)
1745         {
1746             if(!pi->validate(val, true))
1747             {
1748                 throw AbortMarshaling();
1749             }
1750             long l = static_cast<long>(Z_LVAL_P(val));
1751             assert(l >= 0 && l <= 255);
1752             seq[i++] = static_cast<Ice::Byte>(l);
1753         }
1754         ZEND_HASH_FOREACH_END();
1755 
1756         os->write(&seq[0], &seq[0] + seq.size());
1757         break;
1758     }
1759     case PrimitiveInfo::KindShort:
1760     {
1761         Ice::ShortSeq seq(sz);
1762         Ice::Int i = 0;
1763         zval* val;
1764         ZEND_HASH_FOREACH_VAL(arr, val)
1765         {
1766             if(!pi->validate(val, true))
1767             {
1768                 throw AbortMarshaling();
1769             }
1770             long l = static_cast<long>(Z_LVAL_P(val));
1771             assert(l >= SHRT_MIN && l <= SHRT_MAX);
1772             seq[i++] = static_cast<Ice::Short>(l);
1773         }
1774         ZEND_HASH_FOREACH_END();
1775 
1776         os->write(&seq[0], &seq[0] + seq.size());
1777         break;
1778     }
1779     case PrimitiveInfo::KindInt:
1780     {
1781         Ice::IntSeq seq(sz);
1782         Ice::Int i = 0;
1783         zval* val;
1784         ZEND_HASH_FOREACH_VAL(arr, val)
1785         {
1786             if(!pi->validate(val, true))
1787             {
1788                 throw AbortMarshaling();
1789             }
1790             long l = static_cast<long>(Z_LVAL_P(val));
1791             assert(l >= INT_MIN && l <= INT_MAX);
1792             seq[i++] = static_cast<Ice::Int>(l);
1793         }
1794         ZEND_HASH_FOREACH_END();
1795 
1796         os->write(&seq[0], &seq[0] + seq.size());
1797         break;
1798     }
1799     case PrimitiveInfo::KindLong:
1800     {
1801         Ice::LongSeq seq(sz);
1802         Ice::Int i = 0;
1803         zval* val;
1804         ZEND_HASH_FOREACH_VAL(arr, val)
1805         {
1806             if(!pi->validate(val, true))
1807             {
1808                 throw AbortMarshaling();
1809             }
1810             //
1811             // The platform's 'long' type may not be 64 bits, so we also accept
1812             // a string argument for this type.
1813             //
1814             assert(Z_TYPE_P(val) == IS_LONG || Z_TYPE_P(val) == IS_STRING);
1815             Ice::Long l;
1816             if(Z_TYPE_P(val) == IS_LONG)
1817             {
1818                 l = static_cast<long>(Z_LVAL_P(val));
1819             }
1820             else
1821             {
1822                 string sval(Z_STRVAL_P(val), Z_STRLEN_P(val));
1823                 IceUtilInternal::stringToInt64(sval, l);
1824             }
1825             seq[i++] = l;
1826         }
1827         ZEND_HASH_FOREACH_END();
1828 
1829         os->write(&seq[0], &seq[0] + seq.size());
1830         break;
1831     }
1832     case PrimitiveInfo::KindFloat:
1833     {
1834         Ice::FloatSeq seq(sz);
1835         Ice::Int i = 0;
1836         zval* val;
1837         ZEND_HASH_FOREACH_VAL(arr, val)
1838         {
1839             if(!pi->validate(val, true))
1840             {
1841                 throw AbortMarshaling();
1842             }
1843             double d = 0;
1844             if(Z_TYPE_P(val) == IS_DOUBLE)
1845             {
1846                 d = Z_DVAL_P(val);
1847             }
1848             else if(Z_TYPE_P(val) == IS_LONG)
1849             {
1850                 d = static_cast<double>(Z_LVAL_P(val));
1851             }
1852             else
1853             {
1854                 assert(false); // validate() should have caught this.
1855             }
1856             seq[i++] = static_cast<Ice::Float>(d);
1857         }
1858         ZEND_HASH_FOREACH_END();
1859 
1860         os->write(&seq[0], &seq[0] + seq.size());
1861         break;
1862     }
1863     case PrimitiveInfo::KindDouble:
1864     {
1865         Ice::DoubleSeq seq(sz);
1866         Ice::Int i = 0;
1867         zval* val;
1868         ZEND_HASH_FOREACH_VAL(arr, val)
1869         {
1870             if(!pi->validate(val, true))
1871             {
1872                 throw AbortMarshaling();
1873             }
1874             double d = 0;
1875             if(Z_TYPE_P(val) == IS_DOUBLE)
1876             {
1877                 d = Z_DVAL_P(val);
1878             }
1879             else if(Z_TYPE_P(val) == IS_LONG)
1880             {
1881                 d = static_cast<double>(Z_LVAL_P(val));
1882             }
1883             else
1884             {
1885                 assert(false); // validate() should have caught this.
1886             }
1887             seq[i++] = d;
1888         }
1889         ZEND_HASH_FOREACH_END();
1890 
1891         os->write(&seq[0], &seq[0] + seq.size());
1892         break;
1893     }
1894     case PrimitiveInfo::KindString:
1895     {
1896         Ice::StringSeq seq(sz);
1897         Ice::Int i = 0;
1898         zval* val;
1899         ZEND_HASH_FOREACH_VAL(arr, val)
1900         {
1901             if(!pi->validate(val, true))
1902             {
1903                 throw AbortMarshaling();
1904             }
1905             string s;
1906             if(Z_TYPE_P(val) == IS_STRING)
1907             {
1908                 s = string(Z_STRVAL_P(val), Z_STRLEN_P(val));
1909             }
1910             else
1911             {
1912                 assert(Z_TYPE_P(val) == IS_NULL);
1913             }
1914             seq[i++] = s;
1915         }
1916         ZEND_HASH_FOREACH_END();
1917 
1918         os->write(seq);
1919         break;
1920     }
1921     }
1922 }
1923 
1924 void
unmarshalPrimitiveSequence(const PrimitiveInfoPtr & pi,Ice::InputStream * is,const UnmarshalCallbackPtr & cb,zval * target,void * closure)1925 IcePHP::SequenceInfo::unmarshalPrimitiveSequence(const PrimitiveInfoPtr& pi, Ice::InputStream* is,
1926                                                  const UnmarshalCallbackPtr& cb, zval* target, void* closure)
1927 {
1928     zval zv;
1929     array_init(&zv);
1930     AutoDestroy destroy(&zv);
1931 
1932     switch(pi->kind)
1933     {
1934     case PrimitiveInfo::KindBool:
1935     {
1936         pair<const bool*, const bool*> pr;
1937         IceUtil::ScopedArray<bool> arr;
1938         is->read(pr, arr);
1939         for(const bool* p = pr.first; p != pr.second; ++p)
1940         {
1941             add_next_index_bool(&zv, *p ? 1 : 0);
1942         }
1943         break;
1944     }
1945     case PrimitiveInfo::KindByte:
1946     {
1947         pair<const Ice::Byte*, const Ice::Byte*> pr;
1948         is->read(pr);
1949         for(const Ice::Byte* p = pr.first; p != pr.second; ++p)
1950         {
1951             add_next_index_long(&zv, *p & 0xff);
1952         }
1953         break;
1954     }
1955     case PrimitiveInfo::KindShort:
1956     {
1957         pair<const Ice::Short*, const Ice::Short*> pr;
1958         IceUtil::ScopedArray<Ice::Short> arr;
1959         is->read(pr, arr);
1960         for(const Ice::Short* p = pr.first; p != pr.second; ++p)
1961         {
1962             add_next_index_long(&zv, *p);
1963         }
1964         break;
1965     }
1966     case PrimitiveInfo::KindInt:
1967     {
1968         pair<const Ice::Int*, const Ice::Int*> pr;
1969         IceUtil::ScopedArray<Ice::Int> arr;
1970         is->read(pr, arr);
1971         for(const Ice::Int* p = pr.first; p != pr.second; ++p)
1972         {
1973             add_next_index_long(&zv, *p);
1974         }
1975         break;
1976     }
1977     case PrimitiveInfo::KindLong:
1978     {
1979         pair<const Ice::Long*, const Ice::Long*> pr;
1980         IceUtil::ScopedArray<Ice::Long> arr;
1981         is->read(pr, arr);
1982         Ice::Int i = 0;
1983         for(const Ice::Long* p = pr.first; p != pr.second; ++p, ++i)
1984         {
1985             zval val;
1986             //
1987             // The platform's 'long' type may not be 64 bits, so we store 64-bit
1988             // values as a string.
1989             //
1990             if(sizeof(Ice::Long) > sizeof(long) && (*p < LONG_MIN || *p > LONG_MAX))
1991             {
1992                 string str = IceUtilInternal::int64ToString(*p);
1993                 ZVAL_STRINGL(&val, STRCAST(str.c_str()), static_cast<int>(str.length()));
1994             }
1995             else
1996             {
1997                 ZVAL_LONG(&val, static_cast<long>(*p));
1998             }
1999             add_index_zval(&zv, i, &val);
2000         }
2001         break;
2002     }
2003     case PrimitiveInfo::KindFloat:
2004     {
2005         pair<const Ice::Float*, const Ice::Float*> pr;
2006         IceUtil::ScopedArray<Ice::Float> arr;
2007         is->read(pr, arr);
2008         Ice::Int i = 0;
2009         for(const Ice::Float* p = pr.first; p != pr.second; ++p, ++i)
2010         {
2011             zval val;
2012             ZVAL_DOUBLE(&val, *p);
2013             add_index_zval(&zv, i, &val);
2014         }
2015         break;
2016     }
2017     case PrimitiveInfo::KindDouble:
2018     {
2019         pair<const Ice::Double*, const Ice::Double*> pr;
2020         IceUtil::ScopedArray<Ice::Double> arr;
2021         is->read(pr, arr);
2022         Ice::Int i = 0;
2023         for(const Ice::Double* p = pr.first; p != pr.second; ++p, ++i)
2024         {
2025             zval val;
2026             ZVAL_DOUBLE(&val, *p);
2027             add_index_zval(&zv, i, &val);
2028         }
2029         break;
2030     }
2031     case PrimitiveInfo::KindString:
2032     {
2033         Ice::StringSeq seq;
2034         is->read(seq, true);
2035         Ice::Int i = 0;
2036         for(Ice::StringSeq::iterator p = seq.begin(); p != seq.end(); ++p, ++i)
2037         {
2038             zval val;
2039             ZVAL_STRINGL(&val, STRCAST(p->c_str()), static_cast<int>(p->length()));
2040             add_index_zval(&zv, i, &val);
2041         }
2042         break;
2043     }
2044     }
2045 
2046     cb->unmarshaled(&zv, target, closure);
2047 }
2048 
2049 //
2050 // DictionaryInfo implementation.
2051 //
DictionaryInfo(const string & ident,zval * k,zval * v)2052 IcePHP::DictionaryInfo::DictionaryInfo(const string& ident, zval* k, zval* v) :
2053     id(ident)
2054 {
2055     const_cast<TypeInfoPtr&>(keyType) = Wrapper<TypeInfoPtr>::value(k);
2056     const_cast<TypeInfoPtr&>(valueType) = Wrapper<TypeInfoPtr>::value(v);
2057 
2058     _variableLength = keyType->variableLength() || valueType->variableLength();
2059     _wireSize = keyType->wireSize() + valueType->wireSize();
2060 }
2061 
2062 string
getId() const2063 IcePHP::DictionaryInfo::getId() const
2064 {
2065     return id;
2066 }
2067 
2068 bool
validate(zval * zv,bool)2069 IcePHP::DictionaryInfo::validate(zval* zv, bool)
2070 {
2071     return Z_TYPE_P(zv) == IS_NULL || Z_TYPE_P(zv) == IS_ARRAY;
2072 }
2073 
2074 bool
variableLength() const2075 IcePHP::DictionaryInfo::variableLength() const
2076 {
2077     return true;
2078 }
2079 
2080 int
wireSize() const2081 IcePHP::DictionaryInfo::wireSize() const
2082 {
2083     return 1;
2084 }
2085 
2086 Ice::OptionalFormat
optionalFormat() const2087 IcePHP::DictionaryInfo::optionalFormat() const
2088 {
2089     return _variableLength ? Ice::OptionalFormatFSize : Ice::OptionalFormatVSize;
2090 }
2091 
2092 bool
usesClasses() const2093 IcePHP::DictionaryInfo::usesClasses() const
2094 {
2095     return valueType->usesClasses();
2096 }
2097 
2098 void
marshal(zval * zv,Ice::OutputStream * os,ObjectMap * objectMap,bool optional)2099 IcePHP::DictionaryInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool optional)
2100 {
2101     Ice::Int sz = 0;
2102     HashTable* arr = 0;
2103 
2104     if(Z_TYPE_P(zv) != IS_NULL)
2105     {
2106         assert(Z_TYPE_P(zv) == IS_ARRAY); // validate() should have caught this.
2107         arr = Z_ARRVAL_P(zv);
2108         sz = static_cast<Ice::Int>(zend_hash_num_elements(arr));
2109     }
2110 
2111     Ice::OutputStream::size_type sizePos = 0;
2112     if(optional)
2113     {
2114         if(_variableLength)
2115         {
2116             sizePos = os->startSize();
2117         }
2118         else
2119         {
2120             os->writeSize(sz == 0 ? 1 : sz * _wireSize + (sz > 254 ? 5 : 1));
2121         }
2122     }
2123 
2124     PrimitiveInfoPtr piKey = PrimitiveInfoPtr::dynamicCast(keyType);
2125     EnumInfoPtr enKey = EnumInfoPtr::dynamicCast(keyType);
2126     if(!enKey && (!piKey || piKey->kind == PrimitiveInfo::KindFloat || piKey->kind == PrimitiveInfo::KindDouble))
2127     {
2128         invalidArgument("dictionary type `%s' cannot be marshaled", id.c_str());
2129         throw AbortMarshaling();
2130     }
2131 
2132     os->writeSize(sz);
2133 
2134     if(sz > 0)
2135     {
2136         zend_long num_key;
2137         zend_string* key;
2138         zval* val;
2139 
2140         ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, key, val)
2141         {
2142             //
2143             // Store the key (which can be a long or a string) in a zval so that we can reuse the marshaling logic.
2144             //
2145             zval zkey;
2146             AutoDestroy destroy(&zkey);
2147 
2148             if(key)
2149             {
2150                 ZVAL_STRINGL(&zkey, key->val, key->len);
2151             }
2152             else
2153             {
2154                 ZVAL_LONG(&zkey, num_key);
2155             }
2156 
2157             //
2158             // Convert the zval to the required type, if necessary.
2159             //
2160             if(piKey)
2161             {
2162                 switch(piKey->kind)
2163                 {
2164                 case PrimitiveInfo::KindBool:
2165                 {
2166                     convert_to_boolean(&zkey);
2167                     break;
2168                 }
2169 
2170                 case PrimitiveInfo::KindByte:
2171                 case PrimitiveInfo::KindShort:
2172                 case PrimitiveInfo::KindInt:
2173                 case PrimitiveInfo::KindLong:
2174                 {
2175                     if(key) // HASH_KEY_IS_STRING
2176                     {
2177                         convert_to_long(&zkey);
2178                     }
2179                     break;
2180                 }
2181 
2182                 case PrimitiveInfo::KindString:
2183                 {
2184                     if(!key) // HASH_KEY_IS_LONG
2185                     {
2186                         convert_to_string(&zkey);
2187                     }
2188                     break;
2189                 }
2190 
2191                 case PrimitiveInfo::KindFloat:
2192                 case PrimitiveInfo::KindDouble:
2193                     assert(false);
2194                 }
2195             }
2196             else
2197             {
2198                 if(key) // HASH_KEY_IS_STRING
2199                 {
2200                     convert_to_long(&zkey);
2201                 }
2202             }
2203 
2204             //
2205             // Marshal the key.
2206             //
2207             if(!keyType->validate(&zkey, false))
2208             {
2209                 invalidArgument("invalid key in `%s' element", id.c_str());
2210                 throw AbortMarshaling();
2211             }
2212             keyType->marshal(&zkey, os, objectMap, false);
2213 
2214             //
2215             // Marshal the value.
2216             //
2217             if(!valueType->validate(val, false))
2218             {
2219                 invalidArgument("invalid value in `%s' element", id.c_str());
2220                 throw AbortMarshaling();
2221             }
2222             valueType->marshal(val, os, objectMap, false);
2223         }
2224         ZEND_HASH_FOREACH_END();
2225     }
2226 
2227     if(optional && _variableLength)
2228     {
2229         os->endSize(sizePos);
2230     }
2231 }
2232 
2233 void
unmarshal(Ice::InputStream * is,const UnmarshalCallbackPtr & cb,const CommunicatorInfoPtr & comm,zval * target,void * closure,bool optional)2234 IcePHP::DictionaryInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb,
2235                                   const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional)
2236 {
2237     if(optional)
2238     {
2239         if(_variableLength)
2240         {
2241             is->skip(4);
2242         }
2243         else
2244         {
2245             is->skipSize();
2246         }
2247     }
2248 
2249     PrimitiveInfoPtr piKey = PrimitiveInfoPtr::dynamicCast(keyType);
2250     EnumInfoPtr enKey = EnumInfoPtr::dynamicCast(keyType);
2251     if(!enKey && (!piKey || piKey->kind == PrimitiveInfo::KindFloat || piKey->kind == PrimitiveInfo::KindDouble))
2252     {
2253         invalidArgument("dictionary type `%s' cannot be unmarshaled", id.c_str());
2254         throw AbortMarshaling();
2255     }
2256 
2257     zval zv;
2258     array_init(&zv);
2259 #ifdef HT_ALLOW_COW_VIOLATION
2260     HT_ALLOW_COW_VIOLATION(Z_ARRVAL(zv)); // Allow circular references.
2261 #endif
2262     AutoDestroy destroy(&zv);
2263 
2264     Ice::Int sz = is->readSize();
2265     for(Ice::Int i = 0; i < sz; ++i)
2266     {
2267         //
2268         // A dictionary key cannot be a class (or contain one), so the key must be
2269         // available immediately.
2270         //
2271         KeyCallbackPtr keyCB = new KeyCallback;
2272         keyType->unmarshal(is, keyCB, comm, 0, 0, false);
2273         assert(Z_TYPE(keyCB->key) != IS_UNDEF);
2274 
2275         //
2276         // Allocate a callback that holds a reference to the key.
2277         //
2278         ValueCallbackPtr valueCB = new ValueCallback(&keyCB->key);
2279 
2280         //
2281         // Pass the key to the callback.
2282         //
2283         valueType->unmarshal(is, valueCB, comm, &zv, 0, false);
2284     }
2285 
2286     cb->unmarshaled(&zv, target, closure);
2287 }
2288 
2289 void
print(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory * history)2290 IcePHP::DictionaryInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history)
2291 {
2292     if(!validate(zv, false))
2293     {
2294         out << "<invalid value - expected " << id << ">";
2295         return;
2296     }
2297 
2298     if(Z_TYPE_P(zv) == IS_NULL)
2299     {
2300         out << "{}";
2301     }
2302     else
2303     {
2304         HashTable* arr = Z_ARRVAL_P(zv);
2305         zval* val;
2306         zend_long num_key;
2307         zend_string* key;
2308         bool first = true;
2309 
2310         out.sb();
2311 
2312         ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, key, val)
2313         {
2314             if(first)
2315             {
2316                 first = false;
2317             }
2318             else
2319             {
2320                 out << nl;
2321             }
2322             out << nl << "key = ";
2323 
2324             if(key)  // HASH_KEY_IS_STRING
2325             {
2326                 out << key->val;
2327             }
2328             else // HASH_KEY_IS_LONG
2329             {
2330                 out << num_key;
2331             }
2332             out << nl << "value = ";
2333             valueType->print(val, out, history);
2334         }
2335         ZEND_HASH_FOREACH_END();
2336 
2337         out.eb();
2338     }
2339 }
2340 
KeyCallback()2341 IcePHP::DictionaryInfo::KeyCallback::KeyCallback()
2342 {
2343     ZVAL_UNDEF(&key);
2344 }
2345 
~KeyCallback()2346 IcePHP::DictionaryInfo::KeyCallback::~KeyCallback()
2347 {
2348     zval_ptr_dtor(&key);
2349 }
2350 
2351 void
unmarshaled(zval * zv,zval *,void *)2352 IcePHP::DictionaryInfo::KeyCallback::unmarshaled(zval* zv, zval*, void*)
2353 {
2354     zval_ptr_dtor(&key);
2355     ZVAL_COPY(&key, zv);
2356 }
2357 
ValueCallback(zval * k)2358 IcePHP::DictionaryInfo::ValueCallback::ValueCallback(zval* k)
2359 {
2360     ZVAL_COPY_VALUE(&key, k);
2361 }
2362 
~ValueCallback()2363 IcePHP::DictionaryInfo::ValueCallback::~ValueCallback()
2364 {
2365 }
2366 
2367 void
unmarshaled(zval * zv,zval * target,void *)2368 IcePHP::DictionaryInfo::ValueCallback::unmarshaled(zval* zv, zval* target, void*)
2369 {
2370     assert(Z_TYPE_P(target) == IS_ARRAY);
2371 
2372     switch(Z_TYPE(key))
2373     {
2374     case IS_LONG:
2375         add_index_zval(target, Z_LVAL(key), zv);
2376         break;
2377     case IS_TRUE:
2378         add_index_zval(target, 1, zv);
2379         break;
2380     case IS_FALSE:
2381         add_index_zval(target, 0, zv);
2382         break;
2383     case IS_STRING:
2384         add_assoc_zval_ex(target, Z_STRVAL(key), Z_STRLEN(key), zv);
2385         break;
2386     default:
2387         assert(false);
2388         return;
2389     }
2390 
2391     Z_TRY_ADDREF_P(zv);
2392 }
2393 
2394 void
destroy()2395 IcePHP::DictionaryInfo::destroy()
2396 {
2397     if(keyType)
2398     {
2399         keyType->destroy();
2400         keyType = 0;
2401     }
2402     if(valueType)
2403     {
2404         valueType->destroy();
2405         valueType = 0;
2406     }
2407 }
2408 
2409 //
2410 // ClassInfo implementation.
2411 //
ClassInfo(const string & ident)2412 IcePHP::ClassInfo::ClassInfo(const string& ident) :
2413     id(ident), compactId(-1), preserve(false), interface(false), zce(0), defined(false)
2414 {
2415 }
2416 
2417 void
define(const string & n,Ice::Int compact,bool pres,bool intf,zval * b,zval * m)2418 IcePHP::ClassInfo::define(const string& n, Ice::Int compact, bool pres, bool intf, zval* b, zval* m)
2419 {
2420     const_cast<string&>(name) = n;
2421     const_cast<Ice::Int&>(compactId) = static_cast<Ice::Int>(compact);
2422     const_cast<bool&>(preserve) = pres;
2423     const_cast<bool&>(interface) = intf;
2424 
2425     if(b)
2426     {
2427         TypeInfoPtr p = Wrapper<TypeInfoPtr>::value(b);
2428         const_cast<ClassInfoPtr&>(base) = ClassInfoPtr::dynamicCast(p);
2429         assert(base);
2430     }
2431 
2432     if(m)
2433     {
2434         convertDataMembers(m, const_cast<DataMemberList&>(members), const_cast<DataMemberList&>(optionalMembers),
2435                            true);
2436     }
2437 
2438     const_cast<bool&>(defined) = true;
2439 #ifdef ICEPHP_USE_NAMESPACES
2440     const string valueClass = "Ice\\Value";
2441 #else
2442     const string valueClass = "Ice_Value";
2443 #endif
2444     const_cast<zend_class_entry*&>(zce) = nameToClass(interface ? valueClass : name);
2445     assert(zce || id == "::Ice::LocalObject" || interface); // LocalObject and interfaces does not have a native PHP equivalent.
2446 }
2447 
2448 string
getId() const2449 IcePHP::ClassInfo::getId() const
2450 {
2451     return id;
2452 }
2453 
2454 bool
validate(zval * val,bool)2455 IcePHP::ClassInfo::validate(zval* val, bool)
2456 {
2457     if(Z_TYPE_P(val) == IS_OBJECT)
2458     {
2459         return checkClass(Z_OBJCE_P(val), const_cast<zend_class_entry*>(zce));
2460     }
2461     return Z_TYPE_P(val) == IS_NULL;
2462 }
2463 
2464 bool
variableLength() const2465 IcePHP::ClassInfo::variableLength() const
2466 {
2467     return true;
2468 }
2469 
2470 int
wireSize() const2471 IcePHP::ClassInfo::wireSize() const
2472 {
2473     return 1;
2474 }
2475 
2476 Ice::OptionalFormat
optionalFormat() const2477 IcePHP::ClassInfo::optionalFormat() const
2478 {
2479     return Ice::OptionalFormatClass;
2480 }
2481 
2482 bool
usesClasses() const2483 IcePHP::ClassInfo::usesClasses() const
2484 {
2485     return true;
2486 }
2487 
2488 void
marshal(zval * zv,Ice::OutputStream * os,ObjectMap * objectMap,bool)2489 IcePHP::ClassInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap* objectMap, bool)
2490 {
2491     if(!defined)
2492     {
2493         runtimeError("class or interface %s is declared but not defined", id.c_str());
2494         throw AbortMarshaling();
2495     }
2496 
2497     if(Z_TYPE_P(zv) == IS_NULL)
2498     {
2499         Ice::ObjectPtr nil;
2500         os->write(nil);
2501         return;
2502     }
2503 
2504     assert(Z_TYPE_P(zv) == IS_OBJECT); // validate() should have caught this.
2505     assert(checkClass(Z_OBJCE_P(zv), const_cast<zend_class_entry*>(zce))); // validate() should have caught this.
2506 
2507     //
2508     // Ice::ObjectWriter is a subclass of Ice::Object that wraps a PHP object for marshaling.
2509     // It is possible that this PHP object has already been marshaled, therefore we first must
2510     // check the object map to see if this object is present. If so, we use the existing ObjectWriter,
2511     // otherwise we create a new one. The key of the map is the object's handle.
2512     //
2513     Ice::ObjectPtr writer;
2514     assert(objectMap);
2515     ObjectMap::iterator q = objectMap->find(Z_OBJ_HANDLE_P(zv));
2516     if(q == objectMap->end())
2517     {
2518         writer = new ObjectWriter(zv, objectMap, this);
2519         objectMap->insert(ObjectMap::value_type(Z_OBJ_HANDLE_P(zv), writer));
2520     }
2521     else
2522     {
2523         writer = q->second;
2524     }
2525 
2526     //
2527     // Give the writer to the stream. The stream will eventually call write() on it.
2528     //
2529     os->write(writer);
2530 }
2531 
2532 namespace
2533 {
2534 
2535 void
patchObject(void * addr,const Ice::ObjectPtr & v)2536 patchObject(void* addr, const Ice::ObjectPtr& v)
2537 {
2538     ReadObjectCallback* cb = static_cast<ReadObjectCallback*>(addr);
2539     assert(cb);
2540     cb->invoke(v);
2541 }
2542 
2543 }
2544 
2545 void
unmarshal(Ice::InputStream * is,const UnmarshalCallbackPtr & cb,const CommunicatorInfoPtr & comm,zval * target,void * closure,bool)2546 IcePHP::ClassInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb,
2547                              const CommunicatorInfoPtr& comm, zval* target, void* closure, bool)
2548 {
2549     if(!defined)
2550     {
2551         runtimeError("class or interface %s is declared but not defined", id.c_str());
2552         throw AbortMarshaling();
2553     }
2554 
2555     //
2556     // This callback is notified when the Slice value is actually read. The StreamUtil object
2557     // attached to the stream keeps a reference to the callback object to ensure it lives
2558     // long enough.
2559     //
2560     ReadObjectCallbackPtr rocb = new ReadObjectCallback(this, cb, target, closure);
2561     StreamUtil* util = reinterpret_cast<StreamUtil*>(is->getClosure());
2562     assert(util);
2563     util->add(rocb);
2564     is->read(patchObject, rocb.get());
2565 }
2566 
2567 void
print(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory * history)2568 IcePHP::ClassInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history)
2569 {
2570     if(!validate(zv, false))
2571     {
2572         out << "<invalid value - expected " << id << ">";
2573         return;
2574     }
2575 
2576     if(Z_TYPE_P(zv) == IS_NULL)
2577     {
2578         out << "<nil>";
2579     }
2580     else
2581     {
2582         map<unsigned int, int>::iterator q = history->objects.find(Z_OBJ_HANDLE_P(zv));
2583         if(q != history->objects.end())
2584         {
2585             out << "<object #" << q->second << ">";
2586         }
2587         else
2588         {
2589             out << "object #" << history->index << " (" << id << ')';
2590             history->objects.insert(map<unsigned int, int>::value_type(Z_OBJ_HANDLE_P(zv), history->index));
2591             ++history->index;
2592             out.sb();
2593             printMembers(zv, out, history);
2594             out.eb();
2595         }
2596     }
2597 }
2598 
2599 void
destroy()2600 IcePHP::ClassInfo::destroy()
2601 {
2602     const_cast<ClassInfoPtr&>(base) = 0;
2603     if(!members.empty())
2604     {
2605         DataMemberList ml = members;
2606         const_cast<DataMemberList&>(members).clear();
2607         for(DataMemberList::iterator p = ml.begin(); p != ml.end(); ++p)
2608         {
2609             (*p)->type->destroy();
2610         }
2611     }
2612 }
2613 
2614 void
printMembers(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory * history)2615 IcePHP::ClassInfo::printMembers(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history)
2616 {
2617     if(base)
2618     {
2619         base->printMembers(zv, out, history);
2620     }
2621 
2622     DataMemberList::const_iterator q;
2623 
2624     for(q = members.begin(); q != members.end(); ++q)
2625     {
2626         DataMemberPtr member = *q;
2627 
2628         out << nl << member->name << " = ";
2629         zval* val = zend_hash_str_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size());
2630         assert(Z_TYPE_P(val) == IS_INDIRECT);
2631         val = Z_INDIRECT_P(val);
2632         if(val)
2633         {
2634             member->type->print(val, out, history);
2635         }
2636         else
2637         {
2638             out << "<not defined>";
2639         }
2640     }
2641 
2642     for(q = optionalMembers.begin(); q != optionalMembers.end(); ++q)
2643     {
2644         DataMemberPtr member = *q;
2645 
2646         out << nl << member->name << " = ";
2647         zval* val = zend_hash_str_find(Z_OBJPROP_P(zv), STRCAST(member->name.c_str()), member->name.size());
2648         assert(Z_TYPE_P(val) == IS_INDIRECT);
2649         val = Z_INDIRECT_P(val);
2650         if(val)
2651         {
2652             if(isUnset(val))
2653             {
2654                 out << "<unset>";
2655             }
2656             else
2657             {
2658                 member->type->print(val, out, history);
2659             }
2660         }
2661         else
2662         {
2663             out << "<not defined>";
2664         }
2665     }
2666 }
2667 
2668 bool
isA(const string & typeId) const2669 IcePHP::ClassInfo::isA(const string& typeId) const
2670 {
2671     if(id == typeId)
2672     {
2673         return true;
2674     }
2675 
2676     return base && base->isA(typeId);
2677 }
2678 
2679 //
2680 // ProxyInfo implementation.
2681 //
ProxyInfo(const string & ident)2682 IcePHP::ProxyInfo::ProxyInfo(const string& ident) :
2683     id(ident), defined(false)
2684 {
2685 }
2686 
2687 void
define(zval * b,zval * i)2688 IcePHP::ProxyInfo::define(zval* b, zval* i)
2689 {
2690     if(b)
2691     {
2692         TypeInfoPtr p = Wrapper<TypeInfoPtr>::value(b);
2693         const_cast<ProxyInfoPtr&>(base) = ProxyInfoPtr::dynamicCast(p);
2694         assert(base);
2695     }
2696 
2697     if(i)
2698     {
2699         HashTable* interfacesArray = Z_ARRVAL_P(i);
2700         zval* interfaceType;
2701 
2702         ZEND_HASH_FOREACH_VAL(interfacesArray, interfaceType)
2703         {
2704             TypeInfoPtr t = Wrapper<TypeInfoPtr>::value(interfaceType);
2705             ProxyInfoPtr c = ProxyInfoPtr::dynamicCast(t);
2706             assert(c);
2707             const_cast<ProxyInfoList&>(interfaces).push_back(c);
2708         }
2709         ZEND_HASH_FOREACH_END();
2710     }
2711 
2712     const_cast<bool&>(defined) = true;
2713 }
2714 
2715 string
getId() const2716 IcePHP::ProxyInfo::getId() const
2717 {
2718     return id;
2719 }
2720 
2721 bool
validate(zval * zv,bool throwException)2722 IcePHP::ProxyInfo::validate(zval* zv, bool throwException)
2723 {
2724     if(Z_TYPE_P(zv) != IS_NULL)
2725     {
2726         if(Z_TYPE_P(zv) != IS_OBJECT || (Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) != proxyClassEntry))
2727         {
2728             if(throwException)
2729             {
2730                 string s = zendTypeToString(Z_TYPE_P(zv));
2731                 invalidArgument("expected proxy value or null but received %s", s.c_str());
2732             }
2733             return false;
2734         }
2735     }
2736 
2737     return true;
2738 }
2739 
2740 bool
variableLength() const2741 IcePHP::ProxyInfo::variableLength() const
2742 {
2743     return true;
2744 }
2745 
2746 int
wireSize() const2747 IcePHP::ProxyInfo::wireSize() const
2748 {
2749     return 1;
2750 }
2751 
2752 Ice::OptionalFormat
optionalFormat() const2753 IcePHP::ProxyInfo::optionalFormat() const
2754 {
2755     return Ice::OptionalFormatFSize;
2756 }
2757 
2758 void
marshal(zval * zv,Ice::OutputStream * os,ObjectMap *,bool optional)2759 IcePHP::ProxyInfo::marshal(zval* zv, Ice::OutputStream* os, ObjectMap*, bool optional)
2760 {
2761     Ice::OutputStream::size_type sizePos = 0;
2762     if(optional)
2763     {
2764         sizePos = os->startSize();
2765     }
2766 
2767     if(Z_TYPE_P(zv) == IS_NULL)
2768     {
2769         os->write(Ice::ObjectPrx());
2770     }
2771     else
2772     {
2773         assert(Z_TYPE_P(zv) == IS_OBJECT && Z_OBJCE_P(zv) == proxyClassEntry); // validate() should have caught this.
2774         Ice::ObjectPrx proxy;
2775         ProxyInfoPtr info;
2776         if(!fetchProxy(zv, proxy, info))
2777         {
2778             throw AbortMarshaling();
2779         }
2780         if(!info->isA(id))
2781         {
2782             invalidArgument("proxy is not narrowed to %s", id.c_str());
2783             throw AbortMarshaling();
2784         }
2785         os->write(proxy);
2786     }
2787 
2788     if(optional)
2789     {
2790         os->endSize(sizePos);
2791     }
2792 }
2793 
2794 void
unmarshal(Ice::InputStream * is,const UnmarshalCallbackPtr & cb,const CommunicatorInfoPtr & comm,zval * target,void * closure,bool optional)2795 IcePHP::ProxyInfo::unmarshal(Ice::InputStream* is, const UnmarshalCallbackPtr& cb,
2796                              const CommunicatorInfoPtr& comm, zval* target, void* closure, bool optional)
2797 {
2798     zval zv;
2799     AutoDestroy destroy(&zv);
2800 
2801     if(optional)
2802     {
2803         is->skip(4);
2804     }
2805 
2806     Ice::ObjectPrx proxy;
2807     is->read(proxy);
2808 
2809     if(!proxy)
2810     {
2811         ZVAL_NULL(&zv);
2812         cb->unmarshaled(&zv, target, closure);
2813         return;
2814     }
2815 
2816     if(!defined)
2817     {
2818         runtimeError("proxy %s is declared but not defined" TSRMLS_CC, id.c_str());
2819         throw AbortMarshaling();
2820     }
2821 
2822     if(!createProxy(&zv, proxy, this, comm))
2823     {
2824         throw AbortMarshaling();
2825     }
2826     cb->unmarshaled(&zv, target, closure);
2827 }
2828 
2829 void
print(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory *)2830 IcePHP::ProxyInfo::print(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory*)
2831 {
2832     if(!validate(zv, false))
2833     {
2834         out << "<invalid value - expected " << id << ">";
2835         return;
2836     }
2837 
2838     if(Z_TYPE_P(zv) == IS_NULL)
2839     {
2840         out << "<nil>";
2841     }
2842     else
2843     {
2844         Ice::ObjectPrx proxy;
2845         ProxyInfoPtr info;
2846         if(!fetchProxy(zv, proxy, info))
2847         {
2848             return;
2849         }
2850         out << proxy->ice_toString();
2851     }
2852 }
2853 
2854 void
destroy()2855 IcePHP::ProxyInfo::destroy()
2856 {
2857     const_cast<ProxyInfoPtr&>(base) = 0;
2858     const_cast<ProxyInfoList&>(interfaces).clear();
2859 }
2860 
2861 bool
isA(const string & typeId) const2862 IcePHP::ProxyInfo::isA(const string& typeId) const
2863 {
2864     if(id == typeId)
2865     {
2866         return true;
2867     }
2868 
2869     if(base && base->isA(typeId))
2870     {
2871         return true;
2872     }
2873 
2874     for(ProxyInfoList::const_iterator p = interfaces.begin(); p != interfaces.end(); ++p)
2875     {
2876         if((*p)->isA(typeId))
2877         {
2878             return true;
2879         }
2880     }
2881 
2882     return false;
2883 }
2884 
2885 void
addOperation(const string & name,const OperationPtr & op)2886 IcePHP::ProxyInfo::addOperation(const string& name, const OperationPtr& op)
2887 {
2888     operations.insert(OperationMap::value_type(Slice::PHP::fixIdent(name), op));
2889 }
2890 
2891 IcePHP::OperationPtr
getOperation(const string & name) const2892 IcePHP::ProxyInfo::getOperation(const string& name) const
2893 {
2894     OperationPtr op;
2895     OperationMap::const_iterator p = operations.find(name);
2896     if(p != operations.end())
2897     {
2898         op = p->second;
2899     }
2900     if(!op && base)
2901     {
2902         op = base->getOperation(name);
2903     }
2904     if(!op && !interfaces.empty())
2905     {
2906         for(ProxyInfoList::const_iterator q = interfaces.begin(); q != interfaces.end() && !op; ++q)
2907         {
2908             op = (*q)->getOperation(name);
2909         }
2910     }
2911     return op;
2912 }
2913 
2914 //
2915 // ObjectWriter implementation.
2916 //
ObjectWriter(zval * object,ObjectMap * objectMap,const ClassInfoPtr & formal)2917 IcePHP::ObjectWriter::ObjectWriter(zval* object, ObjectMap* objectMap, const ClassInfoPtr& formal) :
2918     _map(objectMap), _formal(formal)
2919 {
2920     // Copy zval and increase ref count
2921     ZVAL_COPY(&_object, object);
2922     if(!_formal || !_formal->interface)
2923     {
2924         //
2925         // For non interface types we need to determine the most-derived Slice type supported by
2926         // this object. This is typically a Slice class, but it can also be an interface.
2927         //
2928         // The caller may have provided a ClassInfo representing the formal type, in
2929         // which case we ensure that the actual type is compatible with the formal type.
2930         //
2931         _info = getClassInfoByClass(Z_OBJCE_P(object), formal ? const_cast<zend_class_entry*>(formal->zce) : 0);
2932         assert(_info);
2933     }
2934 }
2935 
~ObjectWriter()2936 IcePHP::ObjectWriter::~ObjectWriter()
2937 {
2938     zval_ptr_dtor(&_object);
2939 }
2940 
2941 void
ice_preMarshal()2942 IcePHP::ObjectWriter::ice_preMarshal()
2943 {
2944     string name = "ice_premarshal"; // Must be lowercase.
2945     if(zend_hash_str_exists(&Z_OBJCE_P(&_object)->function_table, STRCAST(name.c_str()), static_cast<uint>(name.size())))
2946     {
2947         if(!invokeMethod(&_object, name))
2948         {
2949             throw AbortMarshaling();
2950         }
2951     }
2952 }
2953 
2954 void
_iceWrite(Ice::OutputStream * os) const2955 IcePHP::ObjectWriter::_iceWrite(Ice::OutputStream* os) const
2956 {
2957     Ice::SlicedDataPtr slicedData;
2958 
2959     if(_info && _info->preserve)
2960     {
2961         //
2962         // Retrieve the SlicedData object that we stored as a hidden member of the PHP object.
2963         //
2964         slicedData = StreamUtil::getSlicedDataMember(const_cast<zval*>(&_object), const_cast<ObjectMap*>(_map));
2965     }
2966 
2967     os->startValue(slicedData);
2968 
2969     if(_formal && _formal->interface)
2970     {
2971         //
2972         // For an interface by value we just marshal the Ice type id
2973         // of the object in its own slice.
2974         //
2975         zval ret;
2976         ZVAL_UNDEF(&ret);
2977 
2978         zend_try
2979         {
2980             assert(Z_TYPE(_object) == IS_OBJECT);
2981             zend_call_method(const_cast<zval*>(&_object), 0, 0, const_cast<char*>("ice_id"), sizeof("ice_id") - 1, &ret, 0, 0, 0);
2982         }
2983         zend_catch
2984         {
2985             // ret;
2986         }
2987         zend_end_try();
2988 
2989         //
2990         // Bail out if an exception has already been thrown.
2991         //
2992         if(Z_ISUNDEF(ret) || EG(exception))
2993         {
2994             throw AbortMarshaling();
2995         }
2996 
2997         AutoDestroy destroy(&ret);
2998 
2999         if(Z_TYPE(ret) != IS_STRING)
3000         {
3001             throw AbortMarshaling();
3002         }
3003 
3004         string id(Z_STRVAL(ret), Z_STRLEN(ret));
3005         os->startSlice(id, -1, true);
3006         os->endSlice();
3007     }
3008     else
3009     {
3010         if(_info->id != "::Ice::UnknownSlicedValue")
3011         {
3012             ClassInfoPtr info = _info;
3013             while(info && info->id != Ice::Object::ice_staticId())
3014             {
3015                 assert(info->base); // All classes have the Ice::Object base type.
3016                 const bool lastSlice = info->base->id == Ice::Object::ice_staticId();
3017                 os->startSlice(info->id, info->compactId, lastSlice);
3018 
3019                 writeMembers(os, info->members);
3020                 writeMembers(os, info->optionalMembers); // The optional members have already been sorted by tag.
3021 
3022                 os->endSlice();
3023 
3024                 info = info->base;
3025             }
3026         }
3027     }
3028     os->endValue();
3029 }
3030 
3031 void
_iceRead(Ice::InputStream *)3032 IcePHP::ObjectWriter::_iceRead(Ice::InputStream*)
3033 {
3034     assert(false);
3035 }
3036 
3037 void
writeMembers(Ice::OutputStream * os,const DataMemberList & members) const3038 IcePHP::ObjectWriter::writeMembers(Ice::OutputStream* os, const DataMemberList& members) const
3039 {
3040     for(DataMemberList::const_iterator q = members.begin(); q != members.end(); ++q)
3041     {
3042         DataMemberPtr member = *q;
3043         zval* val = zend_hash_str_find(Z_OBJPROP_P(const_cast<zval*>(&_object)),
3044                                        STRCAST(member->name.c_str()), static_cast<int>(member->name.size()));
3045 
3046         if(!val)
3047         {
3048             runtimeError("member `%s' of %s is not defined", member->name.c_str(), _info->id.c_str());
3049             throw AbortMarshaling();
3050         }
3051 
3052         assert(Z_TYPE_P(val) == IS_INDIRECT);
3053         val = Z_INDIRECT_P(val);
3054 
3055         if(Z_TYPE_P(val) == IS_REFERENCE)
3056         {
3057             val = Z_REFVAL_P(val);
3058         }
3059 
3060         if(member->optional && (isUnset(val) || !os->writeOptional(member->tag, member->type->optionalFormat())))
3061         {
3062             continue;
3063         }
3064 
3065         if(!member->type->validate(val, false))
3066         {
3067             invalidArgument("invalid value for %s member `%s'", _info->id.c_str(), member->name.c_str());
3068             throw AbortMarshaling();
3069         }
3070 
3071         member->type->marshal(val, os, _map, member->optional);
3072     }
3073 }
3074 
3075 //
3076 // ObjectReader implementation.
3077 //
ObjectReader(zval * object,const ClassInfoPtr & info,const CommunicatorInfoPtr & comm)3078 IcePHP::ObjectReader::ObjectReader(zval* object, const ClassInfoPtr& info, const CommunicatorInfoPtr& comm) :
3079     _info(info), _communicator(comm)
3080 {
3081     assert(Z_TYPE_P(object) == IS_OBJECT);
3082     ZVAL_DUP(&_object, object);
3083 }
3084 
~ObjectReader()3085 IcePHP::ObjectReader::~ObjectReader()
3086 {
3087     zval_ptr_dtor(&_object);
3088 }
3089 
3090 void
ice_postUnmarshal()3091 IcePHP::ObjectReader::ice_postUnmarshal()
3092 {
3093     string name = "ice_postunmarshal"; // Must be lowercase.
3094     if(zend_hash_str_exists(&Z_OBJCE(_object)->function_table,
3095                             STRCAST(name.c_str()), static_cast<int>(name.size())))
3096     {
3097         if(!invokeMethod(&_object, name))
3098         {
3099             throw AbortMarshaling();
3100         }
3101     }
3102 }
3103 
3104 void
_iceWrite(Ice::OutputStream *) const3105 IcePHP::ObjectReader::_iceWrite(Ice::OutputStream*) const
3106 {
3107     assert(false);
3108 }
3109 
3110 void
_iceRead(Ice::InputStream * is)3111 IcePHP::ObjectReader::_iceRead(Ice::InputStream* is)
3112 {
3113     is->startValue();
3114     const bool unknown = _info->id == "::Ice::UnknownSlicedValue";
3115 
3116     //
3117     // Unmarshal the slices of a user-defined class.
3118     //
3119     if(!unknown)
3120     {
3121         ClassInfoPtr info = _info;
3122 
3123         while(info && info->id != Ice::Object::ice_staticId())
3124         {
3125             is->startSlice();
3126 
3127             DataMemberList::const_iterator p;
3128 
3129             for(p = info->members.begin(); p != info->members.end(); ++p)
3130             {
3131                 DataMemberPtr member = *p;
3132                 member->type->unmarshal(is, member, _communicator, &_object, 0, false);
3133             }
3134 
3135             //
3136             // The optional members have already been sorted by tag.
3137             //
3138             for(p = info->optionalMembers.begin(); p != info->optionalMembers.end(); ++p)
3139             {
3140                 DataMemberPtr member = *p;
3141                 if(is->readOptional(member->tag, member->type->optionalFormat()))
3142                 {
3143                     member->type->unmarshal(is, member, _communicator, &_object, 0, true);
3144                 }
3145                 else
3146                 {
3147                     zval zv;
3148                     AutoDestroy destroy(&zv);
3149                     assignUnset(&zv);
3150                     member->setMember(&_object, &zv);
3151                 }
3152             }
3153 
3154             is->endSlice();
3155 
3156             info = info->base;
3157         }
3158     }
3159 
3160     _slicedData = is->endValue(_info->preserve);
3161 
3162     if(_slicedData)
3163     {
3164         StreamUtil* util = reinterpret_cast<StreamUtil*>(is->getClosure());
3165         assert(util);
3166         util->add(this);
3167 
3168         //
3169         // Define the "unknownTypeId" member for an instance of UnknownSlicedObject.
3170         //
3171         if(unknown)
3172         {
3173             assert(!_slicedData->slices.empty());
3174 
3175             const string typeId = _slicedData->slices[0]->typeId;
3176             zval zv;
3177             AutoDestroy typeIdDestroyer(&zv);
3178             ZVAL_STRINGL(&zv, STRCAST(typeId.c_str()), static_cast<int>(typeId.size()));
3179             add_property_zval(&_object, STRCAST("unknownTypeId"), &zv);
3180         }
3181     }
3182 }
3183 
3184 ClassInfoPtr
getInfo() const3185 IcePHP::ObjectReader::getInfo() const
3186 {
3187     return _info;
3188 }
3189 
3190 zval*
getObject() const3191 IcePHP::ObjectReader::getObject() const
3192 {
3193     return const_cast<zval*>(&_object);
3194 }
3195 
3196 Ice::SlicedDataPtr
getSlicedData() const3197 IcePHP::ObjectReader::getSlicedData() const
3198 {
3199     return _slicedData;
3200 }
3201 
3202 //
3203 // ReadObjectCallback implementation.
3204 //
ReadObjectCallback(const ClassInfoPtr & info,const UnmarshalCallbackPtr & cb,zval * target,void * closure)3205 IcePHP::ReadObjectCallback::ReadObjectCallback(const ClassInfoPtr& info, const UnmarshalCallbackPtr& cb,
3206                                                zval* target, void* closure) :
3207     _info(info), _cb(cb), _closure(closure)
3208 {
3209     ZVAL_NULL(&_target);
3210 
3211     if(target)
3212     {
3213         assert(Z_REFCOUNTED_P(target));
3214         ZVAL_COPY(&_target, target);
3215     }
3216 }
3217 
~ReadObjectCallback()3218 IcePHP::ReadObjectCallback::~ReadObjectCallback()
3219 {
3220     zval_ptr_dtor(&_target);
3221 }
3222 
3223 void
invoke(const Ice::ObjectPtr & p)3224 IcePHP::ReadObjectCallback::invoke(const Ice::ObjectPtr& p)
3225 {
3226     if(p)
3227     {
3228         ObjectReaderPtr reader = ObjectReaderPtr::dynamicCast(p);
3229         assert(reader);
3230 
3231         //
3232         // Verify that the unmarshaled object is compatible with the formal type.
3233         //
3234         zval* obj = reader->getObject();
3235         if(!_info->interface && !reader->getInfo()->isA(_info->id))
3236         {
3237             Ice::UnexpectedObjectException ex(__FILE__, __LINE__);
3238             ex.reason = "unmarshaled object is not an instance of " + _info->id;
3239             ex.type = reader->getInfo()->id;
3240             ex.expectedType = _info->id;
3241             throw ex;
3242         }
3243         _cb->unmarshaled(obj, &_target, _closure);
3244     }
3245     else
3246     {
3247         zval zv;
3248         AutoDestroy destroy(&zv);
3249         ZVAL_NULL(&zv);
3250         _cb->unmarshaled(&zv, &_target, _closure);
3251     }
3252 }
3253 
3254 //
3255 // ExceptionInfo implementation.
3256 //
3257 zval*
unmarshal(Ice::InputStream * is,const CommunicatorInfoPtr & comm)3258 IcePHP::ExceptionInfo::unmarshal(Ice::InputStream* is, const CommunicatorInfoPtr& comm)
3259 {
3260     zval* zv = static_cast<zval*>(emalloc(sizeof(zval)));
3261 
3262     if(object_init_ex(zv, zce) != SUCCESS)
3263     {
3264         runtimeError("unable to initialize object of type %s", zce->name->val);
3265         throw AbortMarshaling();
3266     }
3267 
3268     //
3269     // NOTE: The type id for the first slice has already been read.
3270     //
3271     ExceptionInfoPtr info = this;
3272     while(info)
3273     {
3274         is->startSlice();
3275 
3276         DataMemberList::iterator q;
3277 
3278         for(q = info->members.begin(); q != info->members.end(); ++q)
3279         {
3280             DataMemberPtr member = *q;
3281             member->type->unmarshal(is, member, comm, zv, 0, false);
3282         }
3283 
3284         //
3285         // The optional members have already been sorted by tag.
3286         //
3287         for(q = info->optionalMembers.begin(); q != info->optionalMembers.end(); ++q)
3288         {
3289             DataMemberPtr member = *q;
3290             if(is->readOptional(member->tag, member->type->optionalFormat()))
3291             {
3292                 member->type->unmarshal(is, member, comm, zv, 0, true);
3293             }
3294             else
3295             {
3296                 zval un;
3297                 AutoDestroy destroy(&un);
3298                 assignUnset(&un);
3299                 member->setMember(zv, &un);
3300             }
3301         }
3302 
3303         is->endSlice();
3304 
3305         info = info->base;
3306     }
3307 
3308     return zv;
3309 }
3310 
3311 void
print(zval * zv,IceUtilInternal::Output & out)3312 IcePHP::ExceptionInfo::print(zval* zv, IceUtilInternal::Output& out)
3313 {
3314     out << "exception " << id;
3315     out.sb();
3316 
3317     if(Z_TYPE_P(zv) != IS_OBJECT)
3318     {
3319         string s = zendTypeToString(Z_TYPE_P(zv));
3320         out << nl << "expected exception value of type " << zce->name->val << " but received " << s;
3321         out.eb();
3322         return;
3323     }
3324 
3325     //
3326     // Compare class entries.
3327     //
3328     zend_class_entry* ce = Z_OBJCE_P(zv);
3329     if(ce != zce)
3330     {
3331         out << nl << "expected exception value of type " << zce->name->val << " but received " << ce->name->val;
3332         out.eb();
3333         return;
3334     }
3335 
3336     PrintObjectHistory history;
3337     history.index = 0;
3338 
3339     printMembers(zv, out, &history);
3340     out.eb();
3341 }
3342 
3343 void
printMembers(zval * zv,IceUtilInternal::Output & out,PrintObjectHistory * history)3344 IcePHP::ExceptionInfo::printMembers(zval* zv, IceUtilInternal::Output& out, PrintObjectHistory* history)
3345 {
3346     if(base)
3347     {
3348         base->printMembers(zv, out, history);
3349     }
3350 
3351     DataMemberList::iterator q;
3352 
3353     for(q = members.begin(); q != members.end(); ++q)
3354     {
3355         DataMemberPtr member = *q;
3356 
3357         out << nl << member->name << " = ";
3358         zval* val =  zend_hash_str_find(Z_OBJPROP_P(zv),
3359                                      STRCAST(member->name.c_str()),
3360                                      static_cast<int>(member->name.size()));
3361 
3362         assert(Z_TYPE_P(val) == IS_INDIRECT);
3363         val = Z_INDIRECT_P(val);
3364 
3365         if(val)
3366         {
3367             member->type->print(val, out, history);
3368         }
3369         else
3370         {
3371             out << "<not defined>";
3372         }
3373     }
3374 
3375     for(q = optionalMembers.begin(); q != optionalMembers.end(); ++q)
3376     {
3377         DataMemberPtr member = *q;
3378 
3379         out << nl << member->name << " = ";
3380         zval* val = zend_hash_str_find(Z_OBJPROP_P(zv),
3381                                      STRCAST(member->name.c_str()),
3382                                      static_cast<int>(member->name.size()));
3383 
3384         assert(Z_TYPE_P(val) == IS_INDIRECT);
3385         val = Z_INDIRECT_P(val);
3386 
3387         if(val)
3388         {
3389             if(isUnset(val))
3390             {
3391                 out << "<unset>";
3392             }
3393             else
3394             {
3395                 member->type->print(val, out, history);
3396             }
3397         }
3398         else
3399         {
3400             out << "<not defined>";
3401         }
3402     }
3403 }
3404 
3405 bool
isA(const string & typeId) const3406 IcePHP::ExceptionInfo::isA(const string& typeId) const
3407 {
3408     if(id == typeId)
3409     {
3410         return true;
3411     }
3412 
3413     if(base && base->isA(typeId))
3414     {
3415         return true;
3416     }
3417 
3418     return false;
3419 }
3420 
3421 //
3422 // ExceptionReader implementation.
3423 //
ExceptionReader(const CommunicatorInfoPtr & communicatorInfo,const ExceptionInfoPtr & info)3424 IcePHP::ExceptionReader::ExceptionReader(const CommunicatorInfoPtr& communicatorInfo, const ExceptionInfoPtr& info
3425                                         ) :
3426     _communicatorInfo(communicatorInfo), _info(info)
3427 {
3428 }
3429 
~ExceptionReader()3430 IcePHP::ExceptionReader::~ExceptionReader()
3431     throw()
3432 {
3433 }
3434 
3435 string
ice_id() const3436 IcePHP::ExceptionReader::ice_id() const
3437 {
3438     return _info->id;
3439 }
3440 
3441 IcePHP::ExceptionReader*
ice_clone() const3442 IcePHP::ExceptionReader::ice_clone() const
3443 {
3444     assert(false);
3445     return 0;
3446 }
3447 
3448 void
ice_throw() const3449 IcePHP::ExceptionReader::ice_throw() const
3450 {
3451     throw *this;
3452 }
3453 
3454 void
_write(Ice::OutputStream *) const3455 IcePHP::ExceptionReader::_write(Ice::OutputStream*) const
3456 {
3457     assert(false);
3458 }
3459 
3460 void
_read(Ice::InputStream * is)3461 IcePHP::ExceptionReader::_read(Ice::InputStream* is)
3462 {
3463     is->startException();
3464 
3465     const_cast<zval*&>(_ex) = _info->unmarshal(is, _communicatorInfo);
3466 
3467     const_cast<Ice::SlicedDataPtr&>(_slicedData) = is->endException(_info->preserve);
3468 }
3469 
3470 bool
_usesClasses() const3471 IcePHP::ExceptionReader::_usesClasses() const
3472 {
3473     return _info->usesClasses;
3474 }
3475 
3476 ExceptionInfoPtr
getInfo() const3477 IcePHP::ExceptionReader::getInfo() const
3478 {
3479     return _info;
3480 }
3481 
3482 zval*
getException() const3483 IcePHP::ExceptionReader::getException() const
3484 {
3485     return _ex;
3486 }
3487 
3488 Ice::SlicedDataPtr
getSlicedData() const3489 IcePHP::ExceptionReader::getSlicedData() const
3490 {
3491     return _slicedData;
3492 }
3493 
3494 //
3495 // IdResolver
3496 //
IdResolver(void)3497 IcePHP::IdResolver::IdResolver(void)
3498 {
3499 }
3500 
3501 string
resolve(Ice::Int id) const3502 IcePHP::IdResolver::resolve(Ice::Int id) const
3503 {
3504     CompactIdMap* m = reinterpret_cast<CompactIdMap*>(ICE_G(compactIdToClassInfoMap));
3505     if(m)
3506     {
3507         CompactIdMap::iterator p = m->find(id);
3508         if(p != m->end())
3509         {
3510             return p->second->id;
3511         }
3512     }
3513     return string();
3514 }
3515 
3516 #ifdef _WIN32
3517 extern "C"
3518 #endif
3519 static zend_object*
handleTypeInfoAlloc(zend_class_entry * ce)3520 handleTypeInfoAlloc(zend_class_entry* ce)
3521 {
3522     Wrapper<TypeInfoPtr>* obj = Wrapper<TypeInfoPtr>::create(ce);
3523     assert(obj);
3524 
3525     obj->zobj.handlers = &_typeInfoHandlers;
3526 
3527     return &obj->zobj;
3528 }
3529 
3530 #ifdef _WIN32
3531 extern "C"
3532 #endif
3533 static void
handleTypeInfoFreeStorage(zend_object * object)3534 handleTypeInfoFreeStorage(zend_object* object)
3535 {
3536     Wrapper<TypeInfoPtr>* obj = Wrapper<TypeInfoPtr>::fetch(object);
3537     delete obj->ptr;
3538     zend_object_std_dtor(object);
3539 }
3540 
3541 static bool
createTypeInfo(zval * zv,const TypeInfoPtr & p)3542 createTypeInfo(zval* zv, const TypeInfoPtr& p)
3543 {
3544     assert(typeInfoClassEntry);
3545     if(object_init_ex(zv, typeInfoClassEntry) != SUCCESS)
3546     {
3547         runtimeError("unable to initialize type");
3548         return false;
3549     }
3550 
3551     Wrapper<TypeInfoPtr>* ze = Wrapper<TypeInfoPtr>::extract(zv);
3552     assert(!ze->ptr);
3553     ze->ptr = new TypeInfoPtr(p);
3554 
3555     return true;
3556 }
3557 
ZEND_FUNCTION(IcePHP_defineEnum)3558 ZEND_FUNCTION(IcePHP_defineEnum)
3559 {
3560     char* id;
3561     size_t idLen;
3562     zval* enumerators;
3563 
3564     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("sa"), &id, &idLen, &enumerators) == FAILURE)
3565     {
3566         return;
3567     }
3568 
3569     EnumInfoPtr type = new EnumInfo(id, enumerators);
3570 
3571     if(!createTypeInfo(return_value, type))
3572     {
3573         RETURN_NULL();
3574     }
3575 }
3576 
ZEND_FUNCTION(IcePHP_defineStruct)3577 ZEND_FUNCTION(IcePHP_defineStruct)
3578 {
3579     char* id;
3580     size_t idLen;
3581     char* name;
3582     size_t nameLen;
3583     zval* members;
3584 
3585     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("ssa"), &id, &idLen, &name, &nameLen,
3586                              &members) == FAILURE)
3587     {
3588         return;
3589     }
3590 
3591     StructInfoPtr type = new StructInfo(id, name, members);
3592 
3593     if(!createTypeInfo(return_value, type))
3594     {
3595         RETURN_NULL();
3596     }
3597 }
3598 
ZEND_FUNCTION(IcePHP_defineSequence)3599 ZEND_FUNCTION(IcePHP_defineSequence)
3600 {
3601     char* id;
3602     size_t idLen;
3603     zval* element;
3604 
3605     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("so"), &id, &idLen, &element) == FAILURE)
3606     {
3607         assert(false);
3608         return;
3609     }
3610 
3611     SequenceInfoPtr type = new SequenceInfo(id, element);
3612 
3613     if(!createTypeInfo(return_value, type))
3614     {
3615         RETURN_NULL();
3616     }
3617 }
3618 
ZEND_FUNCTION(IcePHP_defineDictionary)3619 ZEND_FUNCTION(IcePHP_defineDictionary)
3620 {
3621     char* id;
3622     size_t idLen;
3623     zval* key;
3624     zval* value;
3625 
3626     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("soo"), &id, &idLen, &key, &value) == FAILURE)
3627     {
3628         return;
3629     }
3630 
3631     DictionaryInfoPtr type = new DictionaryInfo(id, key, value);
3632 
3633     if(!createTypeInfo(return_value, type))
3634     {
3635         RETURN_NULL();
3636     }
3637 }
3638 
ZEND_FUNCTION(IcePHP_declareProxy)3639 ZEND_FUNCTION(IcePHP_declareProxy)
3640 {
3641     char* id;
3642     size_t idLen;
3643 
3644     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &id, &idLen) == FAILURE)
3645     {
3646         return;
3647     }
3648 
3649     ProxyInfoPtr type = getProxyInfo(id);
3650     if(!type)
3651     {
3652         type = new ProxyInfo(id);
3653         addProxyInfo(type);
3654     }
3655 
3656     if(!createTypeInfo(return_value, type))
3657     {
3658         RETURN_NULL();
3659     }
3660 }
3661 
ZEND_FUNCTION(IcePHP_defineProxy)3662 ZEND_FUNCTION(IcePHP_defineProxy)
3663 {
3664     char* id;
3665     size_t idLen;
3666     zval* base;
3667     zval* interfaces;
3668 
3669     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("so!a!"), &id, &idLen, &base, &interfaces) == FAILURE)
3670     {
3671         return;
3672     }
3673 
3674     ProxyInfoPtr type = getProxyInfo(id);
3675     if(!type)
3676     {
3677         type = new ProxyInfo(id);
3678         addProxyInfo(type);
3679     }
3680     type->define(base, interfaces);
3681 
3682     if(!createTypeInfo(return_value, type))
3683     {
3684         RETURN_NULL();
3685     }
3686 }
3687 
ZEND_FUNCTION(IcePHP_declareClass)3688 ZEND_FUNCTION(IcePHP_declareClass)
3689 {
3690     char* id;
3691     size_t idLen;
3692 
3693     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &id, &idLen) == FAILURE)
3694     {
3695         return;
3696     }
3697 
3698     ClassInfoPtr type = getClassInfoById(id);
3699     if(!type)
3700     {
3701         type = new ClassInfo(id);
3702         addClassInfoById(type);
3703     }
3704 
3705     if(!createTypeInfo(return_value, type))
3706     {
3707         RETURN_NULL();
3708     }
3709 }
3710 
ZEND_FUNCTION(IcePHP_defineClass)3711 ZEND_FUNCTION(IcePHP_defineClass)
3712 {
3713     char* id;
3714     size_t idLen;
3715     char* name;
3716     size_t nameLen;
3717     zend_long compactId;
3718     zend_bool preserve;
3719     zend_bool interface;
3720     zval* base;
3721     zval* members;
3722 
3723     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("sslbbo!a!"), &id, &idLen, &name, &nameLen,
3724                              &compactId, &preserve, &interface, &base, &members) == FAILURE)
3725     {
3726         return;
3727     }
3728 
3729     ClassInfoPtr type = getClassInfoById(id);
3730     if(!type)
3731     {
3732         type = new ClassInfo(id);
3733         addClassInfoById(type);
3734     }
3735 
3736     type->define(name, static_cast<Ice::Int>(compactId), preserve ? true : false, interface ? true : false, base, members);
3737 
3738     if(!interface)
3739     {
3740         addClassInfoByName(type);
3741     }
3742 
3743     if(type->compactId != -1)
3744     {
3745         CompactIdMap* m = reinterpret_cast<CompactIdMap*>(ICE_G(compactIdToClassInfoMap));
3746         if(!m)
3747         {
3748             m = new CompactIdMap;
3749             ICE_G(compactIdToClassInfoMap) = m;
3750         }
3751         m->insert(CompactIdMap::value_type(type->compactId, type));
3752     }
3753 
3754     if(!createTypeInfo(return_value, type))
3755     {
3756         RETURN_NULL();
3757     }
3758 }
3759 
3760 #ifdef _WIN32
3761 extern "C"
3762 #endif
3763 static zend_object*
handleExceptionInfoAlloc(zend_class_entry * ce)3764 handleExceptionInfoAlloc(zend_class_entry* ce)
3765 {
3766     Wrapper<ExceptionInfoPtr>* obj = Wrapper<ExceptionInfoPtr>::create(ce);
3767     assert(obj);
3768 
3769     obj->zobj.handlers = &_exceptionInfoHandlers;
3770 
3771     return &obj->zobj;
3772 }
3773 
3774 #ifdef _WIN32
3775 extern "C"
3776 #endif
3777 static void
handleExceptionInfoFreeStorage(zend_object * object)3778 handleExceptionInfoFreeStorage(zend_object* object)
3779 {
3780     Wrapper<ExceptionInfoPtr>* obj = Wrapper<ExceptionInfoPtr>::fetch(object);
3781     delete obj->ptr;
3782     zend_object_std_dtor(object);
3783 }
3784 
3785 static bool
createExceptionInfo(zval * zv,const ExceptionInfoPtr & p)3786 createExceptionInfo(zval* zv, const ExceptionInfoPtr& p)
3787 {
3788     if(object_init_ex(zv, exceptionInfoClassEntry) != SUCCESS)
3789     {
3790         runtimeError("unable to initialize exception info");
3791         return false;
3792     }
3793 
3794     Wrapper<ExceptionInfoPtr>* ze = Wrapper<ExceptionInfoPtr>::extract(zv);
3795     assert(!ze->ptr);
3796     ze->ptr = new ExceptionInfoPtr(p);
3797 
3798     return true;
3799 }
3800 
ZEND_FUNCTION(IcePHP_defineException)3801 ZEND_FUNCTION(IcePHP_defineException)
3802 {
3803     char* id;
3804     size_t idLen;
3805     char* name;
3806     size_t nameLen;
3807     zend_bool preserve;
3808     zval* base;
3809     zval* members;
3810 
3811     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("ssbo!a!"), &id, &idLen, &name, &nameLen,
3812                              &preserve, &base, &members) == FAILURE)
3813     {
3814         return;
3815     }
3816 
3817     ExceptionInfoPtr ex = new ExceptionInfo();
3818     ex->id = id;
3819     ex->name = name;
3820     ex->preserve = preserve ? true : false;
3821     if(base)
3822     {
3823         ex->base = Wrapper<ExceptionInfoPtr>::value(base);
3824     }
3825     if(members)
3826     {
3827         convertDataMembers(members, ex->members, ex->optionalMembers, true);
3828     }
3829 
3830     ex->usesClasses = false;
3831 
3832     //
3833     // Only examine the required members to see if any use classes.
3834     //
3835     for(DataMemberList::iterator p = ex->members.begin(); p != ex->members.end(); ++p)
3836     {
3837         if(!ex->usesClasses)
3838         {
3839             ex->usesClasses = (*p)->type->usesClasses();
3840         }
3841     }
3842 
3843     ex->zce = nameToClass(ex->name);
3844 
3845     assert(!getExceptionInfo(ex->id));
3846 
3847     ExceptionInfoMap* m;
3848     if(ICE_G(exceptionInfoMap))
3849     {
3850         m = reinterpret_cast<ExceptionInfoMap*>(ICE_G(exceptionInfoMap));
3851     }
3852     else
3853     {
3854         m = new ExceptionInfoMap;
3855         ICE_G(exceptionInfoMap) = m;
3856     }
3857     m->insert(ExceptionInfoMap::value_type(ex->id, ex));
3858 
3859     if(!createExceptionInfo(return_value, ex))
3860     {
3861         RETURN_NULL();
3862     }
3863 }
3864 
ZEND_FUNCTION(IcePHP_stringify)3865 ZEND_FUNCTION(IcePHP_stringify)
3866 {
3867     if(ZEND_NUM_ARGS() != 2)
3868     {
3869         WRONG_PARAM_COUNT;
3870     }
3871 
3872     zval* v;
3873     zval* t;
3874 
3875     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("zz"), &v, &t) == FAILURE)
3876     {
3877         return;
3878     }
3879 
3880     TypeInfoPtr type = Wrapper<TypeInfoPtr>::value(t);
3881     assert(type);
3882 
3883     ostringstream ostr;
3884     IceUtilInternal::Output out(ostr);
3885     PrintObjectHistory history;
3886     history.index = 0;
3887     type->print(v, out, &history);
3888 
3889     string str = ostr.str();
3890     RETURN_STRINGL(STRCAST(str.c_str()), static_cast<int>(str.length()));
3891 }
3892 
ZEND_FUNCTION(IcePHP_stringifyException)3893 ZEND_FUNCTION(IcePHP_stringifyException)
3894 {
3895     if(ZEND_NUM_ARGS() != 2)
3896     {
3897         WRONG_PARAM_COUNT;
3898     }
3899 
3900     zval* v;
3901     zval* t;
3902 
3903     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("oo"), &v, &t) == FAILURE)
3904     {
3905         return;
3906     }
3907 
3908     ExceptionInfoPtr ex = Wrapper<ExceptionInfoPtr>::value(t);
3909     assert(ex);
3910 
3911     ostringstream ostr;
3912     IceUtilInternal::Output out(ostr);
3913     ex->print(v, out);
3914 
3915     string str = ostr.str();
3916     RETURN_STRINGL(STRCAST(str.c_str()), static_cast<int>(str.length()));
3917 }
3918 
3919 //
3920 // Necessary to suppress warnings from zend_function_entry in php-5.2.
3921 //
3922 #if defined(__GNUC__)
3923 #  pragma GCC diagnostic ignored "-Wwrite-strings"
3924 #endif
3925 
3926 //
3927 // Predefined methods for IcePHP_TypeInfo.
3928 //
3929 static zend_function_entry _typeInfoMethods[] =
3930 {
3931     {0, 0, 0}
3932 };
3933 
3934 //
3935 // Predefined methods for IcePHP_ExceptionInfo.
3936 //
3937 static zend_function_entry _exceptionInfoMethods[] =
3938 {
3939     {0, 0, 0}
3940 };
3941 
3942 bool
isUnset(zval * zv)3943 IcePHP::isUnset(zval* zv)
3944 {
3945     if(Z_TYPE_P(zv) == IS_STRING)
3946     {
3947         return _unsetGUID == string(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
3948     }
3949     return false;
3950 }
3951 
3952 void
assignUnset(zval * zv)3953 IcePHP::assignUnset(zval* zv)
3954 {
3955     ZVAL_DUP(zv, ICE_G(unset));
3956 }
3957 
3958 bool
typesInit(INIT_FUNC_ARGS)3959 IcePHP::typesInit(INIT_FUNC_ARGS)
3960 {
3961     zend_class_entry ce;
3962 
3963     //
3964     // Register the IcePHP_TypeInfo class.
3965     //
3966     INIT_CLASS_ENTRY(ce, "IcePHP_TypeInfo", _typeInfoMethods);
3967     ce.create_object = handleTypeInfoAlloc;
3968     typeInfoClassEntry = zend_register_internal_class(&ce);
3969     memcpy(&_typeInfoHandlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
3970     _typeInfoHandlers.free_obj = handleTypeInfoFreeStorage;
3971     _typeInfoHandlers.offset = XtOffsetOf(Wrapper<TypeInfoPtr>, zobj);
3972 
3973     //
3974     // Register the IcePHP_ExceptionInfo class.
3975     //
3976     INIT_CLASS_ENTRY(ce, "IcePHP_ExceptionInfo", _exceptionInfoMethods);
3977     ce.create_object = handleExceptionInfoAlloc;
3978     exceptionInfoClassEntry = zend_register_internal_class(&ce);
3979     memcpy(&_exceptionInfoHandlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
3980     _exceptionInfoHandlers.free_obj = handleExceptionInfoFreeStorage;
3981     _exceptionInfoHandlers.offset = XtOffsetOf(Wrapper<ExceptionInfoPtr>, zobj);
3982 
3983 #ifdef ICEPHP_USE_NAMESPACES
3984     REGISTER_NS_STRING_CONSTANT("Ice", "None", const_cast<char*>(_unsetGUID.c_str()), CONST_CS|CONST_PERSISTENT);
3985 #else
3986     REGISTER_STRING_CONSTANT("Ice_Unset", const_cast<char*>(_unsetGUID.c_str()), CONST_CS|CONST_PERSISTENT);
3987 #endif
3988 
3989     return true;
3990 }
3991 
3992 //
3993 // enable warning again
3994 //
3995 #if defined(__GNUC__)
3996 #  pragma GCC diagnostic error "-Wwrite-strings"
3997 #endif
3998 
3999 bool
typesRequestInit(void)4000 IcePHP::typesRequestInit(void)
4001 {
4002     //
4003     // Create the global variables for the primitive types.
4004     //
4005     for(int i = static_cast<int>(PrimitiveInfo::KindBool); i <= static_cast<int>(PrimitiveInfo::KindString); ++i)
4006     {
4007         PrimitiveInfoPtr type = new PrimitiveInfo();
4008         type->kind = static_cast<PrimitiveInfo::Kind>(i);
4009 
4010         zval zv;
4011         if(!createTypeInfo(&zv, type))
4012         {
4013             zval_ptr_dtor(&zv);
4014             return false;
4015         }
4016         string name = "IcePHP__t_" + type->getId();
4017         zend_hash_str_update(&EG(symbol_table), const_cast<char*>(name.c_str()), name.size(), &zv);
4018     }
4019 
4020     ICE_G(idToClassInfoMap) = 0;
4021     ICE_G(nameToClassInfoMap) = 0;
4022     ICE_G(proxyInfoMap) = 0;
4023     ICE_G(exceptionInfoMap) = 0;
4024 
4025     zval* unset = static_cast<zval*>(emalloc(sizeof(zval)));
4026     ZVAL_STRINGL(unset, STRCAST(_unsetGUID.c_str()), static_cast<int>(_unsetGUID.length()));
4027     ICE_G(unset) = unset;
4028 
4029     return true;
4030 }
4031 
4032 bool
typesRequestShutdown(void)4033 IcePHP::typesRequestShutdown(void)
4034 {
4035     if(ICE_G(proxyInfoMap))
4036     {
4037         ProxyInfoMap* m = static_cast<ProxyInfoMap*>(ICE_G(proxyInfoMap));
4038         for(ProxyInfoMap::iterator p = m->begin(); p != m->end(); ++p)
4039         {
4040             p->second->destroy();
4041         }
4042         delete m;
4043     }
4044 
4045     if(ICE_G(idToClassInfoMap))
4046     {
4047         ClassInfoMap* m = static_cast<ClassInfoMap*>(ICE_G(idToClassInfoMap));
4048         for(ClassInfoMap::iterator p = m->begin(); p != m->end(); ++p)
4049         {
4050             p->second->destroy();
4051         }
4052         delete m;
4053     }
4054 
4055     if(ICE_G(nameToClassInfoMap))
4056     {
4057         ClassInfoMap* m = static_cast<ClassInfoMap*>(ICE_G(nameToClassInfoMap));
4058         delete m;
4059     }
4060 
4061     delete static_cast<ExceptionInfoMap*>(ICE_G(exceptionInfoMap));
4062 
4063     zval_dtor(ICE_G(unset));
4064     efree(ICE_G(unset));
4065     return true;
4066 }
4067