1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <Util.h>
6 #include <Ice/UUID.h>
7 #include <Slice/PHPUtil.h>
8 #include <algorithm>
9 #include <ctype.h>
10 
11 using namespace std;
12 using namespace IcePHP;
13 using namespace Slice::PHP;
14 
15 namespace
16 {
17 
18 bool
getMember(zval * zv,const string & name,zval * member,int type,bool required)19 getMember(zval* zv, const string& name, zval* member, int type, bool required)
20 {
21     zval* val = zend_hash_str_find(Z_OBJPROP_P(zv), STRCAST(name.c_str()), name.size());
22     if(!val)
23     {
24         if(required)
25         {
26             invalidArgument("object does not contain member `%s'", name.c_str());
27             return false;
28         }
29     }
30 
31     if(val)
32     {
33         assert(Z_TYPE_P(val) == IS_INDIRECT);
34         val = Z_INDIRECT_P(val);
35         if(Z_TYPE_P(val) != type)
36         {
37             string expected = zendTypeToString(type);
38             string actual = zendTypeToString(Z_TYPE_P(val));
39             invalidArgument("expected value of type %s for member `%s' but received %s", expected.c_str(),
40                             name.c_str(), actual.c_str());
41             return false;
42         }
43         ZVAL_COPY_VALUE(member, val);
44     }
45     return true;
46 }
47 
48 void
setStringMember(zval * obj,const string & name,const string & val)49 setStringMember(zval* obj, const string& name, const string& val)
50 {
51     zend_class_entry* cls = Z_OBJCE_P(obj);
52     assert(cls);
53     zend_update_property_stringl(cls,
54                                  obj,
55                                  const_cast<char*>(name.c_str()),
56                                  static_cast<int>(name.size()),
57                                  const_cast<char*>(val.c_str()),
58                                  static_cast<int>(val.size())
59                                 );
60 }
61 
62 template<typename T>
63 bool
getVersion(zval * zv,T & v,const char * type)64 getVersion(zval* zv, T& v, const char* type)
65 {
66     if(Z_TYPE_P(zv) != IS_OBJECT)
67     {
68         invalidArgument("value does not contain an object");
69         return false;
70     }
71 
72     zend_class_entry* cls = idToClass(type);
73     assert(cls);
74 
75     zend_class_entry* ce = Z_OBJCE_P(zv);
76     if(ce != cls)
77     {
78         invalidArgument("expected an instance of %s", ce->name->val);
79         return false;
80     }
81 
82     zval majorVal;
83     if(!getMember(zv, "major", &majorVal, IS_LONG, true))
84     {
85         return false;
86     }
87 
88     zval minorVal;
89     if(!getMember(zv, "minor", &minorVal, IS_LONG, true))
90     {
91         return false;
92     }
93 
94     long m;
95     m = static_cast<long>(Z_LVAL_P(&majorVal));
96     if(m < 0 || m > 255)
97     {
98         invalidArgument("version major must be a value between 0 and 255");
99         return false;
100     }
101     v.major = static_cast<Ice::Byte>(m);
102 
103     m = static_cast<long>(Z_LVAL_P(&minorVal));
104     if(m < 0 || m > 255)
105     {
106         invalidArgument("version minor must be a value between 0 and 255");
107         return false;
108     }
109     v.minor = static_cast<Ice::Byte>(m);
110 
111     return true;
112 }
113 
114 template<typename T>
115 bool
createVersion(zval * zv,const T & version,const char * type)116 createVersion(zval* zv, const T& version, const char* type)
117 {
118     zend_class_entry* cls = idToClass(type);
119     assert(cls);
120 
121     if(object_init_ex(zv, cls) != SUCCESS)
122     {
123         runtimeError("unable to initialize %s", cls->name->val);
124         return false;
125     }
126 
127     zend_update_property_long(cls, zv, const_cast<char*>("major"), sizeof("major") - 1, version.major);
128     zend_update_property_long(cls, zv, const_cast<char*>("minor"), sizeof("minor") - 1, version.minor);
129 
130     return true;
131 }
132 
133 template<typename T>
134 bool
versionToString(zval * zv,zval * s,const char * type)135 versionToString(zval* zv, zval* s, const char* type)
136 {
137     T v;
138     if(!getVersion<T>(zv, v, type))
139     {
140         return false;
141     }
142 
143     try
144     {
145         string str = IceInternal::versionToString<T>(v);
146         ZVAL_STRINGL(s, STRCAST(str.c_str()), static_cast<int>(str.length()));
147     }
148     catch(const IceUtil::Exception& ex)
149     {
150         throwException(ex);
151         return false;
152     }
153 
154     return true;
155 }
156 
157 template<typename T>
158 bool
stringToVersion(const string & s,zval * zv,const char * type)159 stringToVersion(const string& s, zval* zv, const char* type)
160 {
161     try
162     {
163         T v = IceInternal::stringToVersion<T>(s);
164         return createVersion<T>(zv, v, type);
165     }
166     catch(const IceUtil::Exception& ex)
167     {
168         throwException(ex);
169     }
170 
171     return false;
172 }
173 
174 char Ice_ProtocolVersion[] = "::Ice::ProtocolVersion";
175 char Ice_EncodingVersion[] = "::Ice::EncodingVersion";
176 
177 }
178 
179 void*
extractWrapper(zval * zv)180 IcePHP::extractWrapper(zval* zv)
181 {
182     if(!zv)
183     {
184         runtimeError("method %s() must be invoked on an object", get_active_function_name());
185         return 0;
186     }
187 
188     zend_object* obj = Z_OBJ_P(zv);
189     if(!obj)
190     {
191         runtimeError("no object found in %s()", get_active_function_name());
192         return 0;
193     }
194 
195     return obj;
196 }
197 
198 zend_class_entry*
idToClass(const string & id)199 IcePHP::idToClass(const string& id)
200 {
201 #ifdef ICEPHP_USE_NAMESPACES
202     string cls = scopedToName(id, true);
203 #else
204     string cls = scopedToName(id, false);
205 #endif
206 
207     return nameToClass(cls);
208 }
209 
210 zend_class_entry*
nameToClass(const string & name)211 IcePHP::nameToClass(const string& name)
212 {
213     zend_class_entry* result;
214     zend_string* s = zend_string_init(STRCAST(name.c_str()), static_cast<int>(name.length()), 0);
215     result = zend_lookup_class(s);
216     zend_string_release(s);
217     return result;
218 }
219 
220 bool
createIdentity(zval * zv,const Ice::Identity & id)221 IcePHP::createIdentity(zval* zv, const Ice::Identity& id)
222 {
223     zend_class_entry* cls = idToClass("::Ice::Identity");
224     assert(cls);
225 
226     if(object_init_ex(zv, cls) != SUCCESS)
227     {
228         runtimeError("unable to initialize Ice::Identity");
229         return false;
230     }
231 
232     setStringMember(zv, "name", id.name);
233     setStringMember(zv, "category", id.category);
234 
235     return true;
236 }
237 
238 bool
extractIdentity(zval * zv,Ice::Identity & id)239 IcePHP::extractIdentity(zval* zv, Ice::Identity& id)
240 {
241     if(Z_TYPE_P(zv) != IS_OBJECT)
242     {
243         invalidArgument("value does not contain an object");
244         return false;
245     }
246 
247     zend_class_entry* cls = idToClass("::Ice::Identity");
248     assert(cls);
249 
250     zend_class_entry* ce = Z_OBJCE_P(zv);
251     if(ce != cls)
252     {
253         invalidArgument("expected an identity but received %s", ce->name->val);
254         return false;
255     }
256 
257     //
258     // Category is optional, but name is required.
259     //
260     zval categoryVal;
261     ZVAL_UNDEF(&categoryVal);
262     zval nameVal;
263     ZVAL_UNDEF(&nameVal);
264 
265     bool catOk = getMember(zv, "category", &categoryVal, IS_STRING, false);
266     bool nameOk = getMember(zv, "name", &nameVal, IS_STRING, true);
267 
268     if(!catOk || !nameOk)
269     {
270         return false;
271     }
272 
273     id.name = Z_STRVAL_P(&nameVal);
274 
275     if(!Z_ISUNDEF(categoryVal))
276     {
277         id.category = Z_STRVAL_P(&categoryVal);
278     }
279     else
280     {
281         id.category = "";
282     }
283 
284     return true;
285 }
286 
287 bool
createStringMap(zval * zv,const map<string,string> & ctx)288 IcePHP::createStringMap(zval* zv, const map<string, string>& ctx)
289 {
290     array_init(zv);
291 
292     for(map<string, string>::const_iterator p = ctx.begin(); p != ctx.end(); ++p)
293     {
294         if(add_assoc_stringl_ex(zv,
295                                 const_cast<char*>(p->first.c_str()),
296                                 static_cast<uint>(p->first.length()),
297                                 const_cast<char*>(p->second.c_str()),
298                                 static_cast<uint>(p->second.length())) == FAILURE)
299         {
300             return false;
301         }
302     }
303 
304     return true;
305 }
306 
307 bool
extractStringMap(zval * zv,map<string,string> & ctx)308 IcePHP::extractStringMap(zval* zv, map<string, string>& ctx)
309 {
310     if(Z_TYPE_P(zv) != IS_ARRAY)
311     {
312         string s = zendTypeToString(Z_TYPE_P(zv));
313         invalidArgument("expected an associative array but received %s", s.c_str());
314         return false;
315     }
316 
317     HashTable* arr = Z_ARRVAL_P(zv);
318     zend_ulong num_key;
319     zend_string* key;
320     zval* val;
321     ZEND_HASH_FOREACH_KEY_VAL(arr, num_key, key, val)
322     {
323         if(!key)
324         {
325             invalidArgument("array key must be a string");
326             return false;
327         }
328 
329         if(Z_TYPE_P(val) != IS_STRING)
330         {
331             invalidArgument("array value must be a string");
332             return false;
333         }
334 
335         ctx[key->val] = Z_STRVAL_P(val);
336         (void)num_key; // Avoids error from older versions of GCC about unused variable num_key.
337     }
338     ZEND_HASH_FOREACH_END();
339 
340     return true;
341 }
342 
343 bool
createStringArray(zval * zv,const Ice::StringSeq & seq)344 IcePHP::createStringArray(zval* zv, const Ice::StringSeq& seq)
345 {
346     array_init(zv);
347     for(Ice::StringSeq::const_iterator p = seq.begin(); p != seq.end(); ++p)
348     {
349         if(add_next_index_stringl(zv, STRCAST(p->c_str()), static_cast<uint>(p->length())) == FAILURE)
350         {
351             return false;
352         }
353     }
354 
355     return true;
356 }
357 
358 bool
extractStringArray(zval * zv,Ice::StringSeq & seq)359 IcePHP::extractStringArray(zval* zv, Ice::StringSeq& seq)
360 {
361     if(Z_TYPE_P(zv) != IS_ARRAY)
362     {
363         string s = zendTypeToString(Z_TYPE_P(zv));
364         invalidArgument("expected an array of strings but received %s", s.c_str());
365         return false;
366     }
367 
368     HashTable* arr = Z_ARRVAL_P(zv);
369     zval* val;
370     ZEND_HASH_FOREACH_VAL(arr, val)
371     {
372         if(Z_TYPE_P(val) != IS_STRING)
373         {
374             invalidArgument("array element must be a string");
375             return false;
376         }
377 
378         string s(Z_STRVAL_P(val), Z_STRLEN_P(val));
379         seq.push_back(s);
380     }
381     ZEND_HASH_FOREACH_END();
382 
383     return true;
384 }
385 
386 bool
createProtocolVersion(zval * zv,const Ice::ProtocolVersion & v)387 IcePHP::createProtocolVersion(zval* zv, const Ice::ProtocolVersion& v)
388 {
389     return createVersion<Ice::ProtocolVersion>(zv, v, Ice_ProtocolVersion);
390 }
391 
392 bool
createEncodingVersion(zval * zv,const Ice::EncodingVersion & v)393 IcePHP::createEncodingVersion(zval* zv, const Ice::EncodingVersion& v)
394 {
395     return createVersion<Ice::EncodingVersion>(zv, v, Ice_EncodingVersion);
396 }
397 
398 bool
extractEncodingVersion(zval * zv,Ice::EncodingVersion & v)399 IcePHP::extractEncodingVersion(zval* zv, Ice::EncodingVersion& v)
400 {
401     return getVersion<Ice::EncodingVersion>(zv, v, Ice_EncodingVersion);
402 }
403 
404 static bool
convertLocalException(const Ice::LocalException & ex,zval * zex)405 convertLocalException(const Ice::LocalException& ex, zval* zex)
406 {
407     zend_class_entry* cls = Z_OBJCE_P(zex);
408     assert(cls);
409 
410     //
411     // Transfer data members from Ice exception to PHP object.
412     //
413     try
414     {
415         ex.ice_throw();
416     }
417     catch(const Ice::InitializationException& e)
418     {
419         setStringMember(zex, "reason", e.reason);
420     }
421     catch(const Ice::PluginInitializationException& e)
422     {
423         setStringMember(zex, "reason", e.reason);
424     }
425     catch(const Ice::AlreadyRegisteredException& e)
426     {
427         setStringMember(zex, "kindOfObject", e.kindOfObject);
428         setStringMember(zex, "id", e.id);
429     }
430     catch(const Ice::NotRegisteredException& e)
431     {
432         setStringMember(zex, "kindOfObject", e.kindOfObject);
433         setStringMember(zex, "id", e.id);
434     }
435     catch(const Ice::TwowayOnlyException& e)
436     {
437         setStringMember(zex, "operation", e.operation);
438     }
439     catch(const Ice::UnknownException& e)
440     {
441         setStringMember(zex, "unknown", e.unknown);
442     }
443     catch(const Ice::ObjectAdapterDeactivatedException& e)
444     {
445         setStringMember(zex, "name", e.name);
446     }
447     catch(const Ice::ObjectAdapterIdInUseException& e)
448     {
449         setStringMember(zex, "id", e.id);
450     }
451     catch(const Ice::NoEndpointException& e)
452     {
453         setStringMember(zex, "proxy", e.proxy);
454     }
455     catch(const Ice::EndpointParseException& e)
456     {
457         setStringMember(zex, "str", e.str);
458     }
459     catch(const Ice::IdentityParseException& e)
460     {
461         setStringMember(zex, "str", e.str);
462     }
463     catch(const Ice::ProxyParseException& e)
464     {
465         setStringMember(zex, "str", e.str);
466     }
467     catch(const Ice::IllegalIdentityException& e)
468     {
469         zval id;
470         if(!createIdentity(&id, e.id))
471         {
472             zval_ptr_dtor(&id);
473             return false;
474         }
475         zend_update_property(cls, zex, const_cast<char*>("id"), sizeof("id") - 1, &id);
476         zval_ptr_dtor(&id);
477     }
478     catch(const Ice::RequestFailedException& e)
479     {
480         zval id;
481         if(!createIdentity(&id, e.id))
482         {
483             zval_ptr_dtor(&id);
484             return false;
485         }
486         zend_update_property(cls, zex, const_cast<char*>("id"), sizeof("id") - 1, &id);
487         zval_ptr_dtor(&id);
488         setStringMember(zex, "facet", e.facet);
489         setStringMember(zex, "operation", e.operation);
490     }
491     catch(const Ice::FileException& e)
492     {
493         zend_update_property_long(cls, zex, const_cast<char*>("error"), sizeof("error") - 1, e.error);
494         setStringMember(zex, "path", e.path);
495     }
496     catch(const Ice::SyscallException& e) // This must appear after all subclasses of SyscallException.
497     {
498         zend_update_property_long(cls, zex, const_cast<char*>("error"), sizeof("error") - 1, e.error);
499     }
500     catch(const Ice::DNSException& e)
501     {
502         zend_update_property_long(cls, zex, const_cast<char*>("error"), sizeof("error") - 1, e.error);
503         setStringMember(zex, "host", e.host);
504     }
505     catch(const Ice::UnsupportedProtocolException& e)
506     {
507         zval v;
508         if(!createProtocolVersion(&v, e.bad))
509         {
510             zval_ptr_dtor(&v);
511             return false;
512         }
513         zend_update_property(cls, zex, const_cast<char*>("bad"), sizeof("bad") - 1, &v);
514         zval_ptr_dtor(&v);
515 
516         if(!createProtocolVersion(&v, e.supported))
517         {
518             zval_ptr_dtor(&v);
519             return false;
520         }
521         zend_update_property(cls, zex, const_cast<char*>("supported"), sizeof("supported") - 1, &v);
522         zval_ptr_dtor(&v);
523     }
524     catch(const Ice::UnsupportedEncodingException& e)
525     {
526         zval v;
527         if(!createEncodingVersion(&v, e.bad))
528         {
529             zval_ptr_dtor(&v);
530             return false;
531         }
532         zend_update_property(cls, zex, const_cast<char*>("bad"), sizeof("bad") - 1, &v);
533         zval_ptr_dtor(&v);
534 
535         if(!createEncodingVersion(&v, e.supported))
536         {
537             zval_ptr_dtor(&v);
538             return false;
539         }
540         zend_update_property(cls, zex, const_cast<char*>("supported"), sizeof("supported") - 1, &v);
541         zval_ptr_dtor(&v);
542     }
543     catch(const Ice::NoValueFactoryException& e)
544     {
545         setStringMember(zex, "reason", e.reason);
546         setStringMember(zex, "type", e.type);
547     }
548     catch(const Ice::UnexpectedObjectException& e)
549     {
550         setStringMember(zex, "reason", e.reason);
551         setStringMember(zex, "type", e.type);
552         setStringMember(zex, "expectedType", e.expectedType);
553     }
554     catch(const Ice::ProtocolException& e) // This must appear after all subclasses of ProtocolException.
555     {
556         setStringMember(zex, "reason", e.reason);
557     }
558     catch(const Ice::FeatureNotSupportedException& e)
559     {
560         setStringMember(zex, "unsupportedFeature", e.unsupportedFeature);
561     }
562     catch(const Ice::SecurityException& e)
563     {
564         setStringMember(zex, "reason", e.reason);
565     }
566     catch(const Ice::ConnectionManuallyClosedException& e)
567     {
568         add_property_bool(zex, "graceful", e.graceful ? 1 : 0);
569     }
570     catch(const Ice::LocalException&)
571     {
572         //
573         // Nothing to do.
574         //
575     }
576 
577     return true;
578 }
579 
580 void
convertException(zval * zex,const Ice::Exception & ex)581 IcePHP::convertException(zval* zex, const Ice::Exception& ex)
582 {
583     ZVAL_UNDEF(zex);
584 
585     ostringstream ostr;
586     ostr << ex;
587     string str = ostr.str();
588 
589     try
590     {
591         ex.ice_throw();
592     }
593     catch(const Ice::LocalException& e)
594     {
595         zend_class_entry* cls = idToClass(e.ice_id());
596         if(cls)
597         {
598             if(object_init_ex(zex, cls) != SUCCESS)
599             {
600                 runtimeError("unable to create exception %s", cls->name->val);
601                 return;
602             }
603             if(!convertLocalException(e, zex))
604             {
605                 return;
606             }
607         }
608         else
609         {
610             cls = idToClass("Ice::UnknownLocalException");
611             assert(cls);
612             if(object_init_ex(zex, cls) != SUCCESS)
613             {
614                 runtimeError("unable to create exception %s", cls->name->val);
615                 return;
616             }
617             setStringMember(zex, "unknown", str);
618         }
619     }
620     catch(const Ice::UserException&)
621     {
622         zend_class_entry* cls = idToClass("Ice::UnknownUserException");
623         assert(cls);
624         if(object_init_ex(zex, cls) != SUCCESS)
625         {
626             runtimeError("unable to create exception %s", cls->name->val);
627             return;
628         }
629         setStringMember(zex, "unknown", str);
630     }
631     catch(const Ice::Exception&)
632     {
633         zend_class_entry* cls = idToClass("Ice::UnknownException");
634         assert(cls);
635         if(object_init_ex(zex, cls) != SUCCESS)
636         {
637             runtimeError("unable to create exception %s", cls->name->val);
638             return;
639         }
640         setStringMember(zex, "unknown", str);
641     }
642 
643     return;
644 }
645 
646 void
throwException(const Ice::Exception & ex)647 IcePHP::throwException(const Ice::Exception& ex)
648 {
649     zval zex;
650     convertException(&zex, ex);
651     if(!Z_ISUNDEF(zex))
652     {
653         zend_throw_exception_object(&zex);
654     }
655 }
656 
657 std::string
zendTypeToString(int type)658 IcePHP::zendTypeToString(int type)
659 {
660     string result;
661 
662     switch(type)
663     {
664     case IS_NULL:
665         result = "null";
666         break;
667 
668     case IS_LONG:
669         result = "long";
670         break;
671 
672     case IS_DOUBLE:
673         result = "double";
674         break;
675 
676     case IS_STRING:
677         result = "string";
678         break;
679 
680     case IS_ARRAY:
681         result = "array";
682         break;
683 
684     case IS_OBJECT:
685         result = "object";
686         break;
687 
688     case IS_TRUE:
689     case IS_FALSE:
690         result = "bool";
691         break;
692 
693     default:
694         result = "unknown";
695         break;
696     }
697 
698     return result;
699 }
700 
701 static void
throwError(const string & name,const string & msg)702 throwError(const string& name, const string& msg)
703 {
704     if(EG(exception))
705     {
706         return;
707     }
708     zval ex;
709     // AutoDestroy destroy(&ex);
710 
711     zend_class_entry* cls;
712     {
713         zend_class_entry* p;
714         zend_string* s = zend_string_init(STRCAST(name.c_str()), static_cast<int>(name.size()), 0);
715         p = zend_lookup_class(s);
716         zend_string_release(s);
717         assert(p);
718         cls = p;
719     }
720     if(object_init_ex(&ex, cls) == FAILURE)
721     {
722         assert(false);
723     }
724 
725     //
726     // Invoke constructor.
727     //
728     if(!invokeMethod(&ex, ZEND_CONSTRUCTOR_FUNC_NAME, msg))
729     {
730         assert(false);
731     }
732 
733     zend_throw_exception_object(&ex);
734     // destroy.release();
735 }
736 
737 void
runtimeError(const char * fmt,...)738 IcePHP::runtimeError(const char* fmt, ...)
739 {
740     va_list args;
741     char msg[1024];
742 
743     va_start(args, fmt);
744 
745 #if defined(_MSC_VER)
746     vsprintf_s(msg, fmt, args);
747 #else
748     vsprintf(msg, fmt, args);
749 #endif
750 
751     va_end(args);
752 
753     throwError("RuntimeException", msg);
754 }
755 
756 void
invalidArgument(const char * fmt,...)757 IcePHP::invalidArgument(const char* fmt, ...)
758 {
759     va_list args;
760     char msg[1024];
761 
762     va_start(args, fmt);
763 
764 #if defined(_MSC_VER)
765     vsprintf_s(msg, fmt, args);
766 #else
767     vsprintf(msg, fmt, args);
768 #endif
769 
770     va_end(args);
771 
772     throwError("InvalidArgumentException", msg);
773 }
774 
775 static bool
invokeMethodHelper(zval * obj,const string & name,zval * param)776 invokeMethodHelper(zval* obj, const string& name, zval* param)
777 {
778     assert(zend_hash_str_exists(&Z_OBJCE_P(obj)->function_table, STRCAST(name.c_str()), name.size()));
779     zval ret, method;
780     ZVAL_STRING(&method, STRCAST(name.c_str()));
781     uint32_t numParams = param ? 1 : 0;
782     // zval** params = param ? &param : 0;
783     int status = 0;
784     zend_try
785     {
786         status = call_user_function(0, obj, &method, &ret, numParams, param);
787     }
788     zend_catch
789     {
790         status = FAILURE;
791     }
792     zend_end_try();
793     zval_dtor(&method);
794     zval_dtor(&ret);
795     if(status == FAILURE || EG(exception))
796     {
797         return false;
798     }
799     return true;
800 }
801 
802 bool
invokeMethod(zval * obj,const string & name)803 IcePHP::invokeMethod(zval* obj, const string& name)
804 {
805     return invokeMethodHelper(obj, name, 0);
806 }
807 
808 bool
invokeMethod(zval * obj,const string & name,const string & arg)809 IcePHP::invokeMethod(zval* obj, const string& name, const string& arg)
810 {
811     zval param;
812     ZVAL_STRINGL(&param, STRCAST(arg.c_str()), static_cast<int>(arg.size()));
813     return invokeMethodHelper(obj, name, &param);
814 }
815 
816 bool
checkClass(zend_class_entry * ce,zend_class_entry * base)817 IcePHP::checkClass(zend_class_entry* ce, zend_class_entry* base)
818 {
819     while(ce)
820     {
821         if(ce == base)
822         {
823             return true;
824         }
825 
826         for(zend_ulong i = 0; i < ce->num_interfaces; ++i)
827         {
828             if(checkClass(ce->interfaces[i], base))
829             {
830                 return true;
831             }
832         }
833 
834         ce = ce->parent;
835     }
836 
837     return false;
838 }
839 
ZEND_FUNCTION(Ice_stringVersion)840 ZEND_FUNCTION(Ice_stringVersion)
841 {
842     if(ZEND_NUM_ARGS() > 0)
843     {
844         WRONG_PARAM_COUNT;
845     }
846 
847     RETURN_STRINGL(STRCAST(ICE_STRING_VERSION), static_cast<int>(strlen(ICE_STRING_VERSION)));
848 }
849 
ZEND_FUNCTION(Ice_intVersion)850 ZEND_FUNCTION(Ice_intVersion)
851 {
852     if(ZEND_NUM_ARGS() > 0)
853     {
854         WRONG_PARAM_COUNT;
855     }
856 
857     RETURN_LONG(ICE_INT_VERSION);
858 }
859 
ZEND_FUNCTION(Ice_generateUUID)860 ZEND_FUNCTION(Ice_generateUUID)
861 {
862     if(ZEND_NUM_ARGS() > 0)
863     {
864         WRONG_PARAM_COUNT;
865     }
866 
867     string uuid = Ice::generateUUID();
868     RETURN_STRINGL(STRCAST(uuid.c_str()), static_cast<int>(uuid.size()));
869 }
870 
ZEND_FUNCTION(Ice_currentProtocol)871 ZEND_FUNCTION(Ice_currentProtocol)
872 {
873     if(ZEND_NUM_ARGS() > 0)
874     {
875         WRONG_PARAM_COUNT;
876     }
877 
878     if(!createProtocolVersion(return_value, Ice::currentProtocol))
879     {
880         RETURN_NULL();
881     }
882 }
883 
ZEND_FUNCTION(Ice_currentProtocolEncoding)884 ZEND_FUNCTION(Ice_currentProtocolEncoding)
885 {
886     if(ZEND_NUM_ARGS() > 0)
887     {
888         WRONG_PARAM_COUNT;
889     }
890 
891     if(!createEncodingVersion(return_value, Ice::currentProtocolEncoding))
892     {
893         RETURN_NULL();
894     }
895 }
896 
ZEND_FUNCTION(Ice_currentEncoding)897 ZEND_FUNCTION(Ice_currentEncoding)
898 {
899     if(ZEND_NUM_ARGS() > 0)
900     {
901         WRONG_PARAM_COUNT;
902     }
903 
904     if(!createEncodingVersion(return_value, Ice::currentEncoding))
905     {
906         RETURN_NULL();
907     }
908 }
909 
ZEND_FUNCTION(Ice_protocolVersionToString)910 ZEND_FUNCTION(Ice_protocolVersionToString)
911 {
912     zend_class_entry* versionClass = idToClass(Ice_ProtocolVersion);
913     assert(versionClass);
914 
915     zval zv;
916     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O"), &zv, versionClass) != SUCCESS)
917     {
918         RETURN_NULL();
919     }
920 
921     if(!versionToString<Ice::ProtocolVersion>(&zv, return_value, Ice_ProtocolVersion))
922     {
923         RETURN_NULL();
924     }
925 }
926 
ZEND_FUNCTION(Ice_stringToProtocolVersion)927 ZEND_FUNCTION(Ice_stringToProtocolVersion)
928 {
929     char* str;
930     size_t strLen;
931     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS)
932     {
933         RETURN_NULL();
934     }
935     string s(str, strLen);
936 
937     if(!stringToVersion<Ice::ProtocolVersion>(s, return_value, Ice_ProtocolVersion))
938     {
939         RETURN_NULL();
940     }
941 }
942 
ZEND_FUNCTION(Ice_encodingVersionToString)943 ZEND_FUNCTION(Ice_encodingVersionToString)
944 {
945     zend_class_entry* versionClass = idToClass(Ice_EncodingVersion);
946     assert(versionClass);
947 
948     zval* zv;
949     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("O"), &zv, versionClass) != SUCCESS)
950     {
951         RETURN_NULL();
952     }
953 
954     if(!versionToString<Ice::EncodingVersion>(zv, return_value, Ice_EncodingVersion))
955     {
956         RETURN_NULL();
957     }
958 }
959 
ZEND_FUNCTION(Ice_stringToEncodingVersion)960 ZEND_FUNCTION(Ice_stringToEncodingVersion)
961 {
962     char* str;
963     size_t strLen;
964     if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("s"), &str, &strLen) != SUCCESS)
965     {
966         RETURN_NULL();
967     }
968     string s(str, strLen);
969 
970     if(!stringToVersion<Ice::EncodingVersion>(s, return_value, Ice_EncodingVersion))
971     {
972         RETURN_NULL();
973     }
974 }
975