summaryrefslogtreecommitdiff
path: root/php/src/IcePHP/Marshal.cpp
diff options
context:
space:
mode:
authorMark Spruiell <mes@zeroc.com>2006-11-09 18:38:47 +0000
committerMark Spruiell <mes@zeroc.com>2006-11-09 18:38:47 +0000
commit9fc8c2106fdb8e918eefd8614aef6dd77a9c29e3 (patch)
tree3942117e687cbe34477b9d2f48d82fe098cd972b /php/src/IcePHP/Marshal.cpp
parentclean up; align with C++; bug fix (diff)
downloadice-9fc8c2106fdb8e918eefd8614aef6dd77a9c29e3.tar.bz2
ice-9fc8c2106fdb8e918eefd8614aef6dd77a9c29e3.tar.xz
ice-9fc8c2106fdb8e918eefd8614aef6dd77a9c29e3.zip
Reorganizing source code. Changing build process to use Makefiles instead
of configure. Desupporting all platforms except Linux and Windows.
Diffstat (limited to 'php/src/IcePHP/Marshal.cpp')
-rw-r--r--php/src/IcePHP/Marshal.cpp2286
1 files changed, 2286 insertions, 0 deletions
diff --git a/php/src/IcePHP/Marshal.cpp b/php/src/IcePHP/Marshal.cpp
new file mode 100644
index 00000000000..68c6ddc3200
--- /dev/null
+++ b/php/src/IcePHP/Marshal.cpp
@@ -0,0 +1,2286 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2006 ZeroC, Inc. All rights reserved.
+//
+// This copy of Ice is licensed to you under the terms described in the
+// ICE_LICENSE file included in this distribution.
+//
+// **********************************************************************
+
+#include <Marshal.h>
+#include <Profile.h>
+#include <Proxy.h>
+#include <Util.h>
+
+#include <IceUtil/InputUtil.h>
+#include <IceUtil/ScopedArray.h>
+
+using namespace std;
+using namespace IcePHP;
+
+ZEND_EXTERN_MODULE_GLOBALS(ice)
+
+//
+// The marshaling implementation is fairly straightforward. The factory methods in the
+// Marshaler base class examine the given Slice type and create a Marshaler subclass
+// that is responsible for marshaling that type. Some caching is done for complex types
+// such as struct and class; the cached Marshaler instance is stored as a member of the
+// ice_class_entry struct. (Cached instances are destroyed by Slice_destroyClasses.)
+//
+// The implementation of Ice object marshaling is more complex. In order to interface
+// with the Ice stream interface, we need to supply Ice::Object instances, and we must
+// be able to properly handle object graphs and cycles. The solution is to wrap each
+// PHP object with a temporary Ice::Object, and maintain a table that associates each PHP
+// object to its wrapper, so that graphs work correctly.
+//
+// The ObjectMarshaler class doesn't actually marshal object instances. Rather, it
+// represents the top-level marshaler for a particular formal type (i.e., the declared
+// type of a data member or operation parameter). During marshaling, the ObjectMarshaler
+// validates the type of the object to ensure it is compatible with the formal type,
+// and then obtains or creates an ObjectWriter instance for the object. Since each PHP
+// object is represented by an unsigned integer handle, looking up the wrapper is simple.
+//
+// Once the writer is obtained, the marshaler gives it to the stream. Eventually, the
+// stream will invoke write on the writer, at which point the data members are
+// marshaled. For efficiency, each "slice" of the object's state is marshaled by an
+// ObjectSliceMarshaler, which is cached for future reuse.
+//
+// Note that a graph of PHP objects does not result in an equivalent graph of writers.
+// Links between objects exist only in the PHP object representation. Furthermore, the
+// lifetime of the writers is bound to the operation, not to the PHP objects. Writers
+// exist only as a bridge to the C++ marshaling facility.
+//
+// Unmarshaling of Ice objects works in a similar fashion. A default object factory
+// is installed in the communicator that is capable of instantiating any concrete
+// class type for which a definition is present, including the type "::Ice::Object".
+// It returns an instance of ObjectReader, a subclass of Ice::Object that overrides
+// the read method to unmarshal object state. The setValue method is eventually
+// called on the ObjectReader in order to transfer its object handle to a different
+// zval value.
+//
+
+namespace IcePHP
+{
+
+//
+// Marshaler subclass definitions.
+//
+class PrimitiveMarshaler : public Marshaler
+{
+public:
+ PrimitiveMarshaler(const Slice::BuiltinPtr&);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+ bool validate(zval* TSRMLS_DC);
+
+private:
+ Slice::BuiltinPtr _type;
+};
+typedef IceUtil::Handle<PrimitiveMarshaler> PrimitiveMarshalerPtr;
+
+class SequenceMarshaler : public Marshaler
+{
+public:
+ SequenceMarshaler(const Slice::SequencePtr& TSRMLS_DC);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ Slice::SequencePtr _type;
+ Slice::BuiltinPtr _builtin;
+ MarshalerPtr _elementMarshaler;
+};
+
+class ProxyMarshaler : public Marshaler
+{
+public:
+ ProxyMarshaler(const Slice::ProxyPtr&);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ Slice::ProxyPtr _type;
+};
+
+class MemberMarshaler : public Marshaler
+{
+public:
+ MemberMarshaler(const string&, const MarshalerPtr&);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ string _name;
+ MarshalerPtr _marshaler;
+};
+
+class StructMarshaler : public Marshaler
+{
+public:
+ StructMarshaler(const Slice::StructPtr& TSRMLS_DC);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ Slice::StructPtr _type;
+ zend_class_entry* _class;
+ vector<MarshalerPtr> _members;
+};
+
+class EnumMarshaler : public Marshaler
+{
+public:
+ EnumMarshaler(const Slice::EnumPtr& TSRMLS_DC);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ zend_class_entry* _class;
+ long _count;
+};
+
+class NativeDictionaryMarshaler : public Marshaler
+{
+public:
+ NativeDictionaryMarshaler(const Slice::TypePtr&, const Slice::TypePtr& TSRMLS_DC);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ Slice::Builtin::Kind _keyKind;
+ MarshalerPtr _keyMarshaler;
+ MarshalerPtr _valueMarshaler;
+};
+
+class ExceptionMarshaler : public Marshaler
+{
+public:
+ ExceptionMarshaler(const Slice::ExceptionPtr& TSRMLS_DC);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ Slice::ExceptionPtr _ex;
+ zend_class_entry* _class;
+};
+
+//
+// Special marshaler just for the Ice::Object slice.
+//
+class IceObjectSliceMarshaler : public Marshaler
+{
+public:
+ IceObjectSliceMarshaler(TSRMLS_D);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+};
+
+class ObjectSliceMarshaler : public Marshaler
+{
+public:
+ ObjectSliceMarshaler(const string&, const Slice::DataMemberList& TSRMLS_DC);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ string _scoped;
+ vector<MarshalerPtr> _members;
+};
+
+class ObjectWriter : public Ice::ObjectWriter
+{
+public:
+ ObjectWriter(zval*, const Slice::SyntaxTreeBasePtr&, ObjectMap& TSRMLS_DC);
+ ~ObjectWriter();
+
+ virtual void ice_preMarshal();
+
+ virtual void write(const Ice::OutputStreamPtr&) const;
+
+private:
+ zval* _value;
+ Slice::ClassDefPtr _type; // nil if type is ::Ice::Object
+ ObjectMap& _map;
+#ifdef ZTS
+ TSRMLS_D;
+#endif
+};
+
+class ReadObjectCallback : public Ice::ReadObjectCallback
+{
+public:
+
+ virtual void invoke(const ::Ice::ObjectPtr&);
+
+ zend_class_entry* ce; // The formal type
+ string scoped;
+ zval* zv; // The destination zval
+};
+typedef IceUtil::Handle<ReadObjectCallback> ReadObjectCallbackPtr;
+
+class ObjectReader : public Ice::ObjectReader
+{
+public:
+ ObjectReader(zval*, const Slice::ClassDefPtr& TSRMLS_DC);
+ ~ObjectReader();
+
+ virtual void ice_postUnmarshal();
+
+ virtual void read(const Ice::InputStreamPtr&, bool);
+
+ void setValue(zend_class_entry*, const string&, zval*);
+
+private:
+ zval* _value;
+ Slice::ClassDefPtr _type; // nil if type is ::Ice::Object
+#ifdef ZTS
+ TSRMLS_D;
+#endif
+ zend_class_entry* _class;
+};
+typedef IceUtil::Handle<ObjectReader> ObjectReaderPtr;
+
+class ObjectMarshaler : public Marshaler
+{
+public:
+ ObjectMarshaler(const Slice::ClassDefPtr& TSRMLS_DC);
+
+ virtual bool marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC);
+ virtual bool unmarshal(zval*, const Ice::InputStreamPtr& TSRMLS_DC);
+
+ virtual void destroy();
+
+private:
+ Slice::ClassDefPtr _def;
+ zend_class_entry* _class; // The static class type.
+ string _scoped;
+};
+
+} // End of namespace IcePHP
+
+//
+// Marshaler implementation.
+//
+IcePHP::Marshaler::Marshaler()
+{
+}
+
+IcePHP::Marshaler::~Marshaler()
+{
+}
+
+MarshalerPtr
+IcePHP::Marshaler::createMarshaler(const Slice::TypePtr& type TSRMLS_DC)
+{
+ Slice::BuiltinPtr builtin = Slice::BuiltinPtr::dynamicCast(type);
+ if(builtin)
+ {
+ switch(builtin->kind())
+ {
+ case Slice::Builtin::KindByte:
+ case Slice::Builtin::KindBool:
+ case Slice::Builtin::KindShort:
+ case Slice::Builtin::KindInt:
+ case Slice::Builtin::KindLong:
+ case Slice::Builtin::KindFloat:
+ case Slice::Builtin::KindDouble:
+ case Slice::Builtin::KindString:
+ return new PrimitiveMarshaler(builtin);
+
+ case Slice::Builtin::KindObject:
+ return new ObjectMarshaler(0 TSRMLS_CC);
+
+ case Slice::Builtin::KindObjectProxy:
+ return new ProxyMarshaler(0);
+
+ case Slice::Builtin::KindLocalObject:
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unexpected local type");
+ return 0;
+ }
+ }
+
+ Slice::SequencePtr seq = Slice::SequencePtr::dynamicCast(type);
+ if(seq)
+ {
+ return new SequenceMarshaler(seq TSRMLS_CC);
+ }
+
+ Slice::ProxyPtr proxy = Slice::ProxyPtr::dynamicCast(type);
+ if(proxy)
+ {
+ return new ProxyMarshaler(proxy);
+ }
+
+ Slice::StructPtr st = Slice::StructPtr::dynamicCast(type);
+ if(st)
+ {
+ //
+ // Check to see if a marshaler for this type has already been created. If not, create
+ // one and cache it in the marshaler map for future use.
+ //
+ string scoped = st->scoped();
+ MarshalerMap* marshalerMap = static_cast<MarshalerMap*>(ICE_G(marshalerMap));
+ MarshalerMap::iterator p = marshalerMap->find(scoped);
+ if(p != marshalerMap->end())
+ {
+ return p->second;
+ }
+ else
+ {
+ MarshalerPtr result = new StructMarshaler(st TSRMLS_CC);
+ marshalerMap->insert(pair<string, MarshalerPtr>(scoped, result));
+ return result;
+ }
+ }
+
+ Slice::EnumPtr en = Slice::EnumPtr::dynamicCast(type);
+ if(en)
+ {
+ return new EnumMarshaler(en TSRMLS_CC);
+ }
+
+ Slice::DictionaryPtr dict = Slice::DictionaryPtr::dynamicCast(type);
+ if(dict)
+ {
+ if(isNativeKey(dict->keyType()))
+ {
+ return new NativeDictionaryMarshaler(dict->keyType(), dict->valueType() TSRMLS_CC);
+ }
+ }
+
+ Slice::ClassDeclPtr cl = Slice::ClassDeclPtr::dynamicCast(type);
+ if(cl)
+ {
+ //
+ // Don't cache ObjectMarshaler - we cache ObjectSliceMarshaler instead.
+ //
+ Slice::ClassDefPtr def = cl->definition();
+ if(!def)
+ {
+ string scoped = cl->scoped();
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "cannot use Slice %s %s because it has not been defined",
+ cl->isInterface() ? "interface" : "class", scoped.c_str());
+ return 0;
+ }
+ return new ObjectMarshaler(def TSRMLS_CC);
+ }
+
+ return 0;
+}
+
+MarshalerPtr
+IcePHP::Marshaler::createMemberMarshaler(const string& name, const Slice::TypePtr& type TSRMLS_DC)
+{
+ MarshalerPtr result;
+ MarshalerPtr m = createMarshaler(type TSRMLS_CC);
+ if(m)
+ {
+ result = new MemberMarshaler(name, m);
+ }
+ return result;
+}
+
+MarshalerPtr
+IcePHP::Marshaler::createExceptionMarshaler(const Slice::ExceptionPtr& ex TSRMLS_DC)
+{
+ return new ExceptionMarshaler(ex TSRMLS_CC);
+}
+
+//
+// PrimitiveMarshaler implementation.
+//
+IcePHP::PrimitiveMarshaler::PrimitiveMarshaler(const Slice::BuiltinPtr& type) :
+ _type(type)
+{
+}
+
+bool
+IcePHP::PrimitiveMarshaler::validate(zval* zv TSRMLS_DC)
+{
+ switch(_type->kind())
+ {
+ case Slice::Builtin::KindBool:
+ {
+ if(Z_TYPE_P(zv) != IS_BOOL)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected boolean value but received %s", s.c_str());
+ return false;
+ }
+ break;
+ }
+ case Slice::Builtin::KindByte:
+ {
+ if(Z_TYPE_P(zv) != IS_LONG)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected byte value but received %s", s.c_str());
+ return false;
+ }
+ long val = Z_LVAL_P(zv);
+ if(val < 0 || val > 255)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "value %ld is out of range for a byte", val);
+ return false;
+ }
+ break;
+ }
+ case Slice::Builtin::KindShort:
+ {
+ if(Z_TYPE_P(zv) != IS_LONG)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected short value but received %s", s.c_str());
+ return false;
+ }
+ long val = Z_LVAL_P(zv);
+ if(val < SHRT_MIN || val > SHRT_MAX)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "value %ld is out of range for a short", val);
+ return false;
+ }
+ break;
+ }
+ case Slice::Builtin::KindInt:
+ {
+ if(Z_TYPE_P(zv) != IS_LONG)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected int value but received %s", s.c_str());
+ return false;
+ }
+ long val = Z_LVAL_P(zv);
+ if(val < INT_MIN || val > INT_MAX)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "value %ld is out of range for an int", val);
+ return false;
+ }
+ break;
+ }
+ case Slice::Builtin::KindLong:
+ {
+ //
+ // The platform's 'long' type may not be 64 bits, so we also accept
+ // a string argument for this type.
+ //
+ if(Z_TYPE_P(zv) != IS_LONG && Z_TYPE_P(zv) != IS_STRING)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected long value but received %s", s.c_str());
+ return false;
+ }
+ Ice::Long val;
+ if(Z_TYPE_P(zv) == IS_LONG)
+ {
+ val = Z_LVAL_P(zv);
+ }
+ else
+ {
+ string sval(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+ if(!IceUtil::stringToInt64(sval, val))
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "invalid long value `%s'", Z_STRVAL_P(zv));
+ return false;
+ }
+ }
+ break;
+ }
+ case Slice::Builtin::KindFloat:
+ {
+ if(Z_TYPE_P(zv) != IS_DOUBLE)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected float value but received %s", s.c_str());
+ return false;
+ }
+ break;
+ }
+ case Slice::Builtin::KindDouble:
+ {
+ if(Z_TYPE_P(zv) != IS_DOUBLE)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected double value but received %s", s.c_str());
+ return false;
+ }
+ break;
+ }
+ case Slice::Builtin::KindString:
+ {
+ if(Z_TYPE_P(zv) != IS_STRING && Z_TYPE_P(zv) != IS_NULL)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected string value but received %s", s.c_str());
+ return false;
+ }
+ break;
+ }
+
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+ return true;
+}
+
+bool
+IcePHP::PrimitiveMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& TSRMLS_DC)
+{
+ if(!validate(zv TSRMLS_CC))
+ {
+ return false;
+ }
+
+ switch(_type->kind())
+ {
+ case Slice::Builtin::KindBool:
+ {
+ assert(Z_TYPE_P(zv) == IS_BOOL);
+ os->writeBool(Z_BVAL_P(zv) ? true : false);
+ break;
+ }
+ case Slice::Builtin::KindByte:
+ {
+ assert(Z_TYPE_P(zv) == IS_LONG);
+ long val = Z_LVAL_P(zv);
+ assert(val >= 0 && val <= 255);
+ os->writeByte(static_cast<Ice::Byte>(val));
+ break;
+ }
+ case Slice::Builtin::KindShort:
+ {
+ assert(Z_TYPE_P(zv) == IS_LONG);
+ long val = Z_LVAL_P(zv);
+ assert(val >= SHRT_MIN && val <= SHRT_MAX);
+ os->writeShort(static_cast<Ice::Short>(val));
+ break;
+ }
+ case Slice::Builtin::KindInt:
+ {
+ assert(Z_TYPE_P(zv) == IS_LONG);
+ long val = Z_LVAL_P(zv);
+ assert(val >= INT_MIN && val <= INT_MAX);
+ os->writeInt(static_cast<Ice::Int>(val));
+ break;
+ }
+ case Slice::Builtin::KindLong:
+ {
+ //
+ // The platform's 'long' type may not be 64 bits, so we also accept
+ // a string argument for this type.
+ //
+ assert(Z_TYPE_P(zv) == IS_LONG || Z_TYPE_P(zv) == IS_STRING);
+ Ice::Long val;
+ if(Z_TYPE_P(zv) == IS_LONG)
+ {
+ val = Z_LVAL_P(zv);
+ }
+ else
+ {
+ string sval(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+ IceUtil::stringToInt64(sval, val);
+ }
+ os->writeLong(val);
+ break;
+ }
+ case Slice::Builtin::KindFloat:
+ {
+ assert(Z_TYPE_P(zv) == IS_DOUBLE);
+ double val = Z_DVAL_P(zv);
+ os->writeFloat(static_cast<Ice::Float>(val));
+ break;
+ }
+ case Slice::Builtin::KindDouble:
+ {
+ assert(Z_TYPE_P(zv) == IS_DOUBLE);
+ double val = Z_DVAL_P(zv);
+ os->writeDouble(val);
+ break;
+ }
+ case Slice::Builtin::KindString:
+ {
+ assert(Z_TYPE_P(zv) == IS_STRING || Z_TYPE_P(zv) == IS_NULL);
+ if(Z_TYPE_P(zv) == IS_STRING)
+ {
+ string val(Z_STRVAL_P(zv), Z_STRLEN_P(zv));
+ os->writeString(val);
+ }
+ else
+ {
+ os->writeString(string());
+ }
+ break;
+ }
+
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+ return true;
+}
+
+bool
+IcePHP::PrimitiveMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ switch(_type->kind())
+ {
+ case Slice::Builtin::KindBool:
+ {
+ bool val = is->readBool();
+ ZVAL_BOOL(zv, val ? 1 : 0);
+ break;
+ }
+ case Slice::Builtin::KindByte:
+ {
+ Ice::Byte val = is->readByte();
+ ZVAL_LONG(zv, val & 0xff);
+ break;
+ }
+ case Slice::Builtin::KindShort:
+ {
+ Ice::Short val = is->readShort();
+ ZVAL_LONG(zv, val);
+ break;
+ }
+ case Slice::Builtin::KindInt:
+ {
+ Ice::Int val = is->readInt();
+ ZVAL_LONG(zv, val);
+ break;
+ }
+ case Slice::Builtin::KindLong:
+ {
+ Ice::Long val = is->readLong();
+
+ //
+ // The platform's 'long' type may not be 64 bits, so we store 64-bit
+ // values as a string.
+ //
+ if(sizeof(Ice::Long) > sizeof(long) && (val < LONG_MIN || val > LONG_MAX))
+ {
+ char buf[64];
+#ifdef WIN32
+ sprintf(buf, "%I64d", val);
+#else
+ sprintf(buf, "%lld", val);
+#endif
+ ZVAL_STRING(zv, buf, 1);
+ }
+ else
+ {
+ ZVAL_LONG(zv, static_cast<long>(val));
+ }
+ break;
+ }
+ case Slice::Builtin::KindFloat:
+ {
+ Ice::Float val = is->readFloat();
+ ZVAL_DOUBLE(zv, val);
+ break;
+ }
+ case Slice::Builtin::KindDouble:
+ {
+ Ice::Double val = is->readDouble();
+ ZVAL_DOUBLE(zv, val);
+ break;
+ }
+ case Slice::Builtin::KindString:
+ {
+ string val = is->readString();
+ ZVAL_STRINGL(zv, const_cast<char*>(val.c_str()), val.length(), 1);
+ break;
+ }
+
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+
+ return true;
+}
+
+void
+IcePHP::PrimitiveMarshaler::destroy()
+{
+}
+
+//
+// SequenceMarshaler implementation.
+//
+IcePHP::SequenceMarshaler::SequenceMarshaler(const Slice::SequencePtr& type TSRMLS_DC) :
+ _type(type)
+{
+ Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(type);
+ if(b && b->kind() != Slice::Builtin::KindObject && b->kind() != Slice::Builtin::KindObjectProxy)
+ {
+ _builtin = b;
+ }
+ _elementMarshaler = createMarshaler(type->type() TSRMLS_CC);
+}
+
+bool
+IcePHP::SequenceMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& m TSRMLS_DC)
+{
+ if(Z_TYPE_P(zv) != IS_ARRAY)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected array value but received %s", s.c_str());
+ return false;
+ }
+
+ HashTable* arr = Z_ARRVAL_P(zv);
+
+ HashPosition pos;
+ zend_hash_internal_pointer_reset_ex(arr, &pos);
+ Ice::Int sz = static_cast<Ice::Int>(zend_hash_num_elements(arr));
+
+ if(_builtin)
+ {
+ PrimitiveMarshalerPtr pm = PrimitiveMarshalerPtr::dynamicCast(_elementMarshaler);
+ assert(pm);
+ switch(_builtin->kind())
+ {
+ case Slice::Builtin::KindBool:
+ {
+ Ice::BoolSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ seq[i++] = Z_BVAL_P(*val) ? true : false;
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeBoolSeq(seq);
+ break;
+ }
+ case Slice::Builtin::KindByte:
+ {
+ Ice::ByteSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ long l = Z_LVAL_P(*val);
+ assert(l >= 0 && l <= 255);
+ seq[i++] = static_cast<Ice::Byte>(l);
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeByteSeq(seq);
+ break;
+ }
+ case Slice::Builtin::KindShort:
+ {
+ Ice::ShortSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ long l = Z_LVAL_P(*val);
+ assert(l >= SHRT_MIN && l <= SHRT_MAX);
+ seq[i++] = static_cast<Ice::Short>(l);
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeShortSeq(seq);
+ break;
+ }
+ case Slice::Builtin::KindInt:
+ {
+ Ice::IntSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ long l = Z_LVAL_P(*val);
+ assert(l >= INT_MIN && l <= INT_MAX);
+ seq[i++] = static_cast<Ice::Int>(l);
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeIntSeq(seq);
+ break;
+ }
+ case Slice::Builtin::KindLong:
+ {
+ Ice::LongSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ //
+ // The platform's 'long' type may not be 64 bits, so we also accept
+ // a string argument for this type.
+ //
+ assert(Z_TYPE_P(*val) == IS_LONG || Z_TYPE_P(*val) == IS_STRING);
+ Ice::Long l;
+ if(Z_TYPE_P(*val) == IS_LONG)
+ {
+ l = Z_LVAL_P(*val);
+ }
+ else
+ {
+ string sval(Z_STRVAL_P(*val), Z_STRLEN_P(*val));
+ IceUtil::stringToInt64(sval, l);
+ }
+ seq[i++] = l;
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeLongSeq(seq);
+ break;
+ }
+ case Slice::Builtin::KindFloat:
+ {
+ Ice::FloatSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ double d = Z_DVAL_P(*val);
+ seq[i++] = static_cast<Ice::Float>(d);
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeFloatSeq(seq);
+ break;
+ }
+ case Slice::Builtin::KindDouble:
+ {
+ Ice::DoubleSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ double d = Z_DVAL_P(*val);
+ seq[i++] = d;
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeDoubleSeq(seq);
+ break;
+ }
+ case Slice::Builtin::KindString:
+ {
+ Ice::StringSeq seq(sz);
+ zval** val;
+ Ice::Int i = 0;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!pm->validate(*val TSRMLS_CC))
+ {
+ return false;
+ }
+ string s;
+ if(Z_TYPE_P(*val) == IS_STRING)
+ {
+ s = string(Z_STRVAL_P(*val), Z_STRLEN_P(*val));
+ }
+ else
+ {
+ assert(Z_TYPE_P(*val) == IS_NULL);
+ }
+ seq[i++] = s;
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ os->writeStringSeq(seq);
+ break;
+ }
+
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+ }
+ else
+ {
+ os->writeSize(sz);
+
+ zval** val;
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(!_elementMarshaler->marshal(*val, os, m TSRMLS_CC))
+ {
+ return false;
+ }
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ }
+
+ return true;
+}
+
+bool
+IcePHP::SequenceMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ array_init(zv);
+
+ if(_builtin)
+ {
+ switch(_builtin->kind())
+ {
+ case Slice::Builtin::KindBool:
+ {
+ pair<const bool*, const bool*> pr;
+ IceUtil::ScopedArray<bool> arr(is->readBoolSeq(pr));
+ Ice::Int i = 0;
+ for(const bool* p = pr.first; p != pr.second; ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ ZVAL_BOOL(val, *p ? 1 : 0);
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+ case Slice::Builtin::KindByte:
+ {
+ pair<const Ice::Byte*, const Ice::Byte*> pr;
+ is->readByteSeq(pr);
+ Ice::Int i = 0;
+ for(const Ice::Byte* p = pr.first; p != pr.second; ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ ZVAL_LONG(val, *p & 0xff);
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+ case Slice::Builtin::KindShort:
+ {
+ pair<const Ice::Short*, const Ice::Short*> pr;
+ IceUtil::ScopedArray<Ice::Short> arr(is->readShortSeq(pr));
+ Ice::Int i = 0;
+ for(const Ice::Short* p = pr.first; p != pr.second; ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ ZVAL_LONG(val, *p);
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+ case Slice::Builtin::KindInt:
+ {
+ pair<const Ice::Int*, const Ice::Int*> pr;
+ IceUtil::ScopedArray<Ice::Int> arr(is->readIntSeq(pr));
+ Ice::Int i = 0;
+ for(const Ice::Int* p = pr.first; p != pr.second; ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ ZVAL_LONG(val, *p);
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+ case Slice::Builtin::KindLong:
+ {
+ pair<const Ice::Long*, const Ice::Long*> pr;
+ IceUtil::ScopedArray<Ice::Long> arr(is->readLongSeq(pr));
+ Ice::Int i = 0;
+ for(const Ice::Long* p = pr.first; p != pr.second; ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ //
+ // The platform's 'long' type may not be 64 bits, so we store 64-bit
+ // values as a string.
+ //
+ if(sizeof(Ice::Long) > sizeof(long) && (*p < LONG_MIN || *p > LONG_MAX))
+ {
+ char buf[64];
+#ifdef WIN32
+ sprintf(buf, "%I64d", *p);
+#else
+ sprintf(buf, "%lld", *p);
+#endif
+ ZVAL_STRING(val, buf, 1);
+ }
+ else
+ {
+ ZVAL_LONG(val, static_cast<long>(*p));
+ }
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+ case Slice::Builtin::KindFloat:
+ {
+ pair<const Ice::Float*, const Ice::Float*> pr;
+ IceUtil::ScopedArray<Ice::Float> arr(is->readFloatSeq(pr));
+ Ice::Int i = 0;
+ for(const Ice::Float* p = pr.first; p != pr.second; ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ ZVAL_DOUBLE(zv, *p);
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+ case Slice::Builtin::KindDouble:
+ {
+ pair<const Ice::Double*, const Ice::Double*> pr;
+ IceUtil::ScopedArray<Ice::Double> arr(is->readDoubleSeq(pr));
+ Ice::Int i = 0;
+ for(const Ice::Double* p = pr.first; p != pr.second; ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ ZVAL_DOUBLE(zv, *p);
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+ case Slice::Builtin::KindString:
+ {
+ Ice::StringSeq seq = is->readStringSeq();
+ Ice::Int i = 0;
+ for(Ice::StringSeq::iterator p = seq.begin(); p != seq.end(); ++p, ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ ZVAL_STRINGL(val, const_cast<char*>(p->c_str()), p->length(), 1);
+ add_index_zval(zv, i, val);
+ }
+ break;
+ }
+
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+ }
+ else
+ {
+ Ice::Int sz = is->readSize();
+ for(Ice::Int i = 0; i < sz; ++i)
+ {
+ zval* val;
+ MAKE_STD_ZVAL(val);
+ if(!_elementMarshaler->unmarshal(val, is TSRMLS_CC))
+ {
+ return false;
+ }
+ add_index_zval(zv, i, val);
+ }
+ }
+
+ return true;
+}
+
+void
+IcePHP::SequenceMarshaler::destroy()
+{
+ _elementMarshaler->destroy();
+ _elementMarshaler = 0;
+}
+
+//
+// ProxyMarshaler implementation.
+//
+IcePHP::ProxyMarshaler::ProxyMarshaler(const Slice::ProxyPtr& type) :
+ _type(type)
+{
+}
+
+bool
+IcePHP::ProxyMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& TSRMLS_DC)
+{
+ if(Z_TYPE_P(zv) != IS_OBJECT && Z_TYPE_P(zv) != IS_NULL)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected proxy value but received %s", s.c_str());
+ return false;
+ }
+
+ Ice::ObjectPrx proxy;
+ Slice::ClassDefPtr def;
+ if(!ZVAL_IS_NULL(zv))
+ {
+ if(!fetchProxy(zv, proxy, def TSRMLS_CC))
+ {
+ return false;
+ }
+
+ if(_type)
+ {
+ string scoped = _type->_class()->scoped();
+ if(def)
+ {
+ if(!def->isA(scoped))
+ {
+ string s = def->scoped();
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected a proxy of type %s but received %s",
+ scoped.c_str(), s.c_str());
+ return false;
+ }
+ }
+ else
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected a proxy of type %s", scoped.c_str());
+ return false;
+ }
+ }
+ }
+ os->writeProxy(proxy);
+
+ return true;
+}
+
+bool
+IcePHP::ProxyMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ Ice::ObjectPrx proxy = is->readProxy();
+
+ if(!proxy)
+ {
+ ZVAL_NULL(zv);
+ return true;
+ }
+
+ //
+ // If _type is not a primitive proxy (i.e., Builtin::KindObjectProxy), then we
+ // want to associate our class with the proxy so that it is considered to be
+ // "narrowed".
+ //
+ Slice::ClassDefPtr def;
+ if(_type)
+ {
+ def = _type->_class()->definition();
+ }
+
+ if(!createProxy(zv, proxy, def TSRMLS_CC))
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void
+IcePHP::ProxyMarshaler::destroy()
+{
+}
+
+//
+// MemberMarshaler implementation.
+//
+IcePHP::MemberMarshaler::MemberMarshaler(const string& name, const MarshalerPtr& marshaler) :
+ _name(name), _marshaler(marshaler)
+{
+}
+
+bool
+IcePHP::MemberMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& m TSRMLS_DC)
+{
+ zval** val;
+ if(zend_hash_find(Z_OBJPROP_P(zv), const_cast<char*>(_name.c_str()), _name.length() + 1, (void**)&val) == FAILURE)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "member `%s' is not defined", _name.c_str());
+ return false;
+ }
+
+ return _marshaler->marshal(*val, os, m TSRMLS_CC);;
+}
+
+bool
+IcePHP::MemberMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ zval* val;
+ MAKE_STD_ZVAL(val);
+
+ if(!_marshaler->unmarshal(val, is TSRMLS_CC))
+ {
+ return false;
+ }
+
+ if(add_property_zval(zv, const_cast<char*>(_name.c_str()), val) == FAILURE)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to set member `%s'", _name.c_str());
+ return false;
+ }
+ zval_ptr_dtor(&val); // add_property_zval increments the refcount
+
+ return true;
+}
+
+void
+IcePHP::MemberMarshaler::destroy()
+{
+ _marshaler->destroy();
+ _marshaler = 0;
+}
+
+//
+// StructMarshaler implementation.
+//
+IcePHP::StructMarshaler::StructMarshaler(const Slice::StructPtr& type TSRMLS_DC) :
+ _type(type)
+{
+ _class = findClassScoped(type->scoped() TSRMLS_CC);
+ assert(_class);
+
+ Slice::DataMemberList members = type->dataMembers();
+ for(Slice::DataMemberList::iterator q = members.begin(); q != members.end(); ++q)
+ {
+ MarshalerPtr marshaler = createMemberMarshaler((*q)->name(), (*q)->type() TSRMLS_CC);
+ assert(marshaler);
+ _members.push_back(marshaler);
+ }
+}
+
+bool
+IcePHP::StructMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& m TSRMLS_DC)
+{
+ if(Z_TYPE_P(zv) != IS_OBJECT)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected struct value of type %s but received %s", _class->name,
+ s.c_str());
+ return false;
+ }
+
+ //
+ // Compare class entries.
+ //
+ zend_class_entry* ce = Z_OBJCE_P(zv);
+ if(ce != _class)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected struct value of type %s but received %s", _class->name,
+ ce->name);
+ return false;
+ }
+
+ for(vector<MarshalerPtr>::iterator p = _members.begin(); p != _members.end(); ++p)
+ {
+ if(!(*p)->marshal(zv, os, m TSRMLS_CC))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+IcePHP::StructMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ if(object_init_ex(zv, _class) != SUCCESS)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to initialize object of type %s", _class->name);
+ return false;
+ }
+
+ for(vector<MarshalerPtr>::iterator p = _members.begin(); p != _members.end(); ++p)
+ {
+ if(!(*p)->unmarshal(zv, is TSRMLS_CC))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+IcePHP::StructMarshaler::destroy()
+{
+ vector<MarshalerPtr> members = _members;
+ _members.clear();
+ for(vector<MarshalerPtr>::iterator p = members.begin(); p != members.end(); ++p)
+ {
+ (*p)->destroy();
+ }
+}
+
+//
+// EnumMarshaler implementation.
+//
+IcePHP::EnumMarshaler::EnumMarshaler(const Slice::EnumPtr& type TSRMLS_DC)
+{
+ _class = findClassScoped(type->scoped() TSRMLS_CC);
+ assert(_class);
+ _count = static_cast<long>(type->getEnumerators().size());
+}
+
+bool
+IcePHP::EnumMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& TSRMLS_DC)
+{
+ if(Z_TYPE_P(zv) != IS_LONG)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected long value for enum %s but received %s", _class->name,
+ s.c_str());
+ return false;
+ }
+
+ //
+ // Validate value.
+ //
+ long val = Z_LVAL_P(zv);
+ if(val < 0 || val >= _count)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "value %ld is out of range for enum %s", val, _class->name);
+ return false;
+ }
+
+ if(_count <= 127)
+ {
+ os->writeByte(static_cast<Ice::Byte>(val));
+ }
+ else if(_count <= 32767)
+ {
+ os->writeShort(static_cast<Ice::Short>(val));
+ }
+ else
+ {
+ os->writeInt(static_cast<Ice::Int>(val));
+ }
+
+ return true;
+}
+
+bool
+IcePHP::EnumMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ if(_count <= 127)
+ {
+ Ice::Byte val = is->readByte();
+ ZVAL_LONG(zv, val);
+ }
+ else if(_count <= 32767)
+ {
+ Ice::Short val = is->readShort();
+ ZVAL_LONG(zv, val);
+ }
+ else
+ {
+ Ice::Int val = is->readInt();
+ ZVAL_LONG(zv, val);
+ }
+
+ return true;
+}
+
+void
+IcePHP::EnumMarshaler::destroy()
+{
+}
+
+//
+// NativeDictionaryMarshaler implementation.
+//
+IcePHP::NativeDictionaryMarshaler::NativeDictionaryMarshaler(const Slice::TypePtr& keyType,
+ const Slice::TypePtr& valueType TSRMLS_DC)
+{
+ Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(keyType);
+ assert(b);
+ _keyKind = b->kind();
+ _keyMarshaler = createMarshaler(keyType TSRMLS_CC);
+ _valueMarshaler = createMarshaler(valueType TSRMLS_CC);
+}
+
+bool
+IcePHP::NativeDictionaryMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& m TSRMLS_DC)
+{
+ if(Z_TYPE_P(zv) != IS_ARRAY)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected array value but received %s", s.c_str());
+ return false;
+ }
+
+ HashTable* arr = Z_ARRVAL_P(zv);
+ HashPosition pos;
+ zval** val;
+
+ os->writeSize(zend_hash_num_elements(arr));
+
+ zend_hash_internal_pointer_reset_ex(arr, &pos);
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ //
+ // Get the key (which can be a long or a string).
+ //
+ char* keyStr;
+ uint keyLen;
+ ulong keyNum;
+ int keyType = zend_hash_get_current_key_ex(arr, &keyStr, &keyLen, &keyNum, 0, &pos);
+
+ //
+ // Store the key in a zval, so that we can reuse the PrimitiveMarshaler logic.
+ //
+ zval zkey;
+ if(keyType == HASH_KEY_IS_LONG)
+ {
+ ZVAL_LONG(&zkey, keyNum);
+ }
+ else
+ {
+ ZVAL_STRINGL(&zkey, keyStr, keyLen - 1, 1);
+ }
+
+ //
+ // Convert the zval to the key type required by Slice, if necessary.
+ //
+ switch(_keyKind)
+ {
+ case Slice::Builtin::KindBool:
+ {
+ convert_to_boolean(&zkey);
+ break;
+ }
+
+ case Slice::Builtin::KindByte:
+ case Slice::Builtin::KindShort:
+ case Slice::Builtin::KindInt:
+ case Slice::Builtin::KindLong:
+ {
+ if(keyType == HASH_KEY_IS_STRING)
+ {
+ convert_to_long(&zkey);
+ }
+ break;
+ }
+
+ case Slice::Builtin::KindString:
+ {
+ if(keyType == HASH_KEY_IS_LONG)
+ {
+ convert_to_string(&zkey);
+ }
+ break;
+ }
+
+ case Slice::Builtin::KindFloat:
+ case Slice::Builtin::KindDouble:
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+
+ //
+ // Marshal the key.
+ //
+ if(!_keyMarshaler->marshal(&zkey, os, m TSRMLS_CC))
+ {
+ zval_dtor(&zkey);
+ return false;
+ }
+
+ zval_dtor(&zkey);
+
+ //
+ // Marshal the value.
+ //
+ if(!_valueMarshaler->marshal(*val, os, m TSRMLS_CC))
+ {
+ return false;
+ }
+
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+
+ return true;
+}
+
+bool
+IcePHP::NativeDictionaryMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ array_init(zv);
+
+ Ice::Int sz = is->readSize();
+
+ for(Ice::Int i = 0; i < sz; ++i)
+ {
+ zval key;
+ zval* val;
+ INIT_ZVAL(key);
+ MAKE_STD_ZVAL(val);
+
+ if(!_keyMarshaler->unmarshal(&key, is TSRMLS_CC))
+ {
+ return false;
+ }
+ if(!_valueMarshaler->unmarshal(val, is TSRMLS_CC))
+ {
+ return false;
+ }
+
+ switch(Z_TYPE(key))
+ {
+ case IS_LONG:
+ add_index_zval(zv, Z_LVAL(key), val);
+ break;
+ case IS_BOOL:
+ add_index_zval(zv, Z_BVAL(key) ? 1 : 0, val);
+ break;
+ case IS_STRING:
+ add_assoc_zval_ex(zv, Z_STRVAL(key), Z_STRLEN(key) + 1, val);
+ break;
+ default:
+ assert(false);
+ return false;
+ }
+ zval_dtor(&key);
+ }
+
+ return true;
+}
+
+void
+IcePHP::NativeDictionaryMarshaler::destroy()
+{
+ _keyMarshaler->destroy();
+ _keyMarshaler = 0;
+ _valueMarshaler->destroy();
+ _valueMarshaler = 0;
+}
+
+//
+// ExceptionMarshaler implementation.
+//
+IcePHP::ExceptionMarshaler::ExceptionMarshaler(const Slice::ExceptionPtr& ex TSRMLS_DC) :
+ _ex(ex)
+{
+ _class = findClassScoped(ex->scoped() TSRMLS_CC);
+ assert(_class);
+}
+
+bool
+IcePHP::ExceptionMarshaler::marshal(zval*, const Ice::OutputStreamPtr&, ObjectMap& TSRMLS_DC)
+{
+ //
+ // We never need to marshal an exception.
+ //
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "exception marshaling is not supported");
+ return false;
+}
+
+bool
+IcePHP::ExceptionMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ if(object_init_ex(zv, _class) != SUCCESS)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to initialize exception %s", _class->name);
+ return false;
+ }
+
+ //
+ // NOTE: The type id for the first slice has already been read.
+ //
+
+ Slice::ExceptionPtr ex = _ex;
+ while(ex)
+ {
+ Slice::DataMemberList members = ex->dataMembers();
+ is->startSlice();
+ for(Slice::DataMemberList::iterator p = members.begin(); p != members.end(); ++p)
+ {
+ MarshalerPtr member = createMemberMarshaler((*p)->name(), (*p)->type() TSRMLS_CC);
+ if(!member->unmarshal(zv, is TSRMLS_CC))
+ {
+ return false;
+ }
+ }
+ is->endSlice();
+ ex = ex->base();
+ if(ex)
+ {
+ is->readString(); // Skip id.
+ }
+ }
+
+ return true;
+}
+
+void
+IcePHP::ExceptionMarshaler::destroy()
+{
+}
+
+//
+// IceObjectSliceMarshaler implementation.
+//
+IcePHP::IceObjectSliceMarshaler::IceObjectSliceMarshaler(TSRMLS_D)
+{
+}
+
+bool
+IcePHP::IceObjectSliceMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& TSRMLS_DC)
+{
+ assert(Z_TYPE_P(zv) == IS_OBJECT);
+
+ os->writeTypeId(Ice::Object::ice_staticId());
+ os->startSlice();
+ os->writeSize(0); // For compatibility with the old AFM.
+ os->endSlice();
+
+ return true;
+}
+
+bool
+IcePHP::IceObjectSliceMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ assert(Z_TYPE_P(zv) == IS_OBJECT);
+
+ //
+ // Do not read type id here - see ObjectReader::__read().
+ //
+ //is->readTypeId()
+
+ is->startSlice();
+
+ // For compatibility with the old AFM.
+ Ice::Int sz = is->readSize();
+ if(sz != 0)
+ {
+ throw Ice::MarshalException(__FILE__, __LINE__);
+ }
+
+ is->endSlice();
+
+ return true;
+}
+
+void
+IcePHP::IceObjectSliceMarshaler::destroy()
+{
+}
+
+//
+// ObjectSliceMarshaler implementation.
+//
+IcePHP::ObjectSliceMarshaler::ObjectSliceMarshaler(const string& scoped,
+ const Slice::DataMemberList& members TSRMLS_DC) :
+ _scoped(scoped)
+{
+ for(Slice::DataMemberList::const_iterator p = members.begin(); p != members.end(); ++p)
+ {
+ MarshalerPtr marshaler = createMemberMarshaler((*p)->name(), (*p)->type() TSRMLS_CC);
+ assert(marshaler);
+ _members.push_back(marshaler);
+ }
+}
+
+bool
+IcePHP::ObjectSliceMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& m TSRMLS_DC)
+{
+ assert(Z_TYPE_P(zv) == IS_OBJECT);
+
+ os->writeTypeId(_scoped);
+ os->startSlice();
+ for(vector<MarshalerPtr>::iterator p = _members.begin(); p != _members.end(); ++p)
+ {
+ if(!(*p)->marshal(zv, os, m TSRMLS_CC))
+ {
+ return false;
+ }
+ }
+ os->endSlice();
+
+ return true;
+}
+
+bool
+IcePHP::ObjectSliceMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ assert(Z_TYPE_P(zv) == IS_OBJECT);
+
+ //
+ // Do not read type id here - see ObjectReader::__read().
+ //
+ //is->readTypeId()
+
+ is->startSlice();
+ for(vector<MarshalerPtr>::iterator p = _members.begin(); p != _members.end(); ++p)
+ {
+ if(!(*p)->unmarshal(zv, is TSRMLS_CC))
+ {
+ return false;
+ }
+ }
+ is->endSlice();
+
+ return true;
+}
+
+void
+IcePHP::ObjectSliceMarshaler::destroy()
+{
+ vector<MarshalerPtr> members = _members;
+ _members.clear();
+ for(vector<MarshalerPtr>::iterator p = members.begin(); p != members.end(); ++p)
+ {
+ (*p)->destroy();
+ }
+}
+
+//
+// ObjectWriter implementation.
+//
+IcePHP::ObjectWriter::ObjectWriter(zval* value, const Slice::SyntaxTreeBasePtr& type, ObjectMap& m TSRMLS_DC) :
+ _value(value), _map(m)
+{
+#if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x530)
+// Strange Sun C++ 5.3 bug.
+ const IceUtil::HandleBase<Slice::SyntaxTreeBase>& hb = type;
+ _type = Slice::ClassDefPtr::dynamicCast(hb);
+#else
+ _type = Slice::ClassDefPtr::dynamicCast(type);
+#endif
+
+#ifdef ZTS
+ this->TSRMLS_C = TSRMLS_C;
+#endif
+
+ Z_OBJ_HT_P(_value)->add_ref(_value TSRMLS_CC);
+}
+
+IcePHP::ObjectWriter::~ObjectWriter()
+{
+ Z_OBJ_HT_P(_value)->del_ref(_value TSRMLS_CC);
+}
+
+void
+IcePHP::ObjectWriter::ice_preMarshal()
+{
+ zend_call_method_with_0_params(&_value, NULL, NULL, "ice_preMarshal", NULL);
+}
+
+void
+IcePHP::ObjectWriter::write(const Ice::OutputStreamPtr& os) const
+{
+ MarshalerMap* marshalerMap = static_cast<MarshalerMap*>(ICE_G(marshalerMap));
+ ObjectMap& objectMap = const_cast<ObjectMap&>(_map);
+ zval* value = const_cast<zval*>(_value);
+
+ Slice::ClassDefPtr def = _type;
+ while(true)
+ {
+ string scoped = def->scoped();
+ MarshalerPtr slice;
+ MarshalerMap::iterator p = marshalerMap->find(scoped);
+ if(p != marshalerMap->end())
+ {
+ slice = p->second;
+ }
+ else
+ {
+ slice = new ObjectSliceMarshaler(scoped, def->dataMembers() TSRMLS_CC);
+ marshalerMap->insert(pair<string, MarshalerPtr>(scoped, slice));
+ }
+
+ if(!slice->marshal(value, os, objectMap TSRMLS_CC))
+ {
+ throw AbortMarshaling();
+ }
+
+ Slice::ClassList bases = def->bases();
+ if(!bases.empty() && !bases.front()->isInterface())
+ {
+ def = bases.front();
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ //
+ // Marshal the Ice::Object slice.
+ //
+ MarshalerPtr slice;
+ MarshalerMap::iterator p = marshalerMap->find(Ice::Object::ice_staticId());
+ if(p != marshalerMap->end())
+ {
+ slice = p->second;
+ }
+ else
+ {
+ slice = new IceObjectSliceMarshaler(TSRMLS_C);
+ marshalerMap->insert(pair<string, MarshalerPtr>(Ice::Object::ice_staticId(), slice));
+ }
+
+ if(!slice->marshal(value, os, objectMap TSRMLS_CC))
+ {
+ throw AbortMarshaling();
+ }
+}
+
+//
+// ReadObjectCallback implementation.
+//
+void
+IcePHP::ReadObjectCallback::invoke(const Ice::ObjectPtr& v)
+{
+ ObjectReaderPtr p = ObjectReaderPtr::dynamicCast(v);
+ if(p)
+ {
+ p->setValue(ce, scoped, zv);
+ }
+ else
+ {
+ ZVAL_NULL(zv);
+ }
+}
+
+//
+// ObjectReader implementation.
+//
+IcePHP::ObjectReader::ObjectReader(zval* val, const Slice::ClassDefPtr& type TSRMLS_DC) :
+ _value(val), _type(type)
+{
+#ifdef ZTS
+ this->TSRMLS_C = TSRMLS_C;
+#endif
+
+ ZVAL_ADDREF(_value);
+
+ _class = Z_OBJCE_P(_value);
+}
+
+IcePHP::ObjectReader::~ObjectReader()
+{
+ zval_ptr_dtor(&_value);
+}
+
+void
+IcePHP::ObjectReader::ice_postUnmarshal()
+{
+ zend_call_method_with_0_params(&_value, NULL, NULL, "ice_postUnmarshal", NULL);
+}
+
+void
+IcePHP::ObjectReader::read(const Ice::InputStreamPtr& is, bool rid)
+{
+ MarshalerMap* marshalerMap = static_cast<MarshalerMap*>(ICE_G(marshalerMap));
+
+ //
+ // Unmarshal the slices of a user-defined class.
+ //
+ if(_type)
+ {
+ Slice::ClassDefPtr def = _type;
+ while(true)
+ {
+ string scoped;
+ if(rid)
+ {
+ scoped = is->readTypeId();
+ }
+ else
+ {
+ scoped = def->scoped();
+ }
+
+ MarshalerPtr slice;
+ MarshalerMap::iterator p = marshalerMap->find(scoped);
+ if(p != marshalerMap->end())
+ {
+ slice = p->second;
+ }
+ else
+ {
+ slice = new ObjectSliceMarshaler(scoped, def->dataMembers() TSRMLS_CC);
+ marshalerMap->insert(pair<string, MarshalerPtr>(scoped, slice));
+ }
+
+ if(!slice->unmarshal(_value, is TSRMLS_CC))
+ {
+ throw AbortMarshaling();
+ }
+
+ rid = true;
+
+ Slice::ClassList bases = def->bases();
+ if(!bases.empty() && !bases.front()->isInterface())
+ {
+ def = bases.front();
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ //
+ // Unmarshal the Ice::Object slice.
+ //
+ if(rid)
+ {
+ is->readTypeId();
+ }
+
+ MarshalerPtr slice;
+ MarshalerMap::iterator p = marshalerMap->find(Ice::Object::ice_staticId());
+ if(p != marshalerMap->end())
+ {
+ slice = p->second;
+ }
+ else
+ {
+ slice = new IceObjectSliceMarshaler(TSRMLS_C);
+ marshalerMap->insert(pair<string, MarshalerPtr>(Ice::Object::ice_staticId(), slice));
+ }
+
+ if(!slice->unmarshal(_value, is TSRMLS_CC))
+ {
+ throw AbortMarshaling();
+ }
+}
+
+void
+IcePHP::ObjectReader::setValue(zend_class_entry* ce, const string& scoped, zval* zv)
+{
+ //
+ // Compare the class entries. The argument "ce" represents the formal type.
+ //
+ if(!checkClass(_class, ce))
+ {
+ Ice::UnexpectedObjectException ex(__FILE__, __LINE__);
+ ex.type = _type ? _type->scoped() : "::Ice::Object";
+ ex.expectedType = scoped;
+ throw ex;
+ }
+
+ //
+ // Now both zvals have the same object handle (they point at the same object). We need to
+ // increment the object's reference count accordingly.
+ //
+ Z_TYPE_P(zv) = IS_OBJECT;
+ zv->value.obj = _value->value.obj;
+ Z_OBJ_HT_P(_value)->add_ref(_value TSRMLS_CC);
+}
+
+//
+// ObjectMarshaler implementation.
+//
+IcePHP::ObjectMarshaler::ObjectMarshaler(const Slice::ClassDefPtr& def TSRMLS_DC) :
+ _def(def)
+{
+ //
+ // Find the class entry for this type.
+ //
+ if(def)
+ {
+ _scoped = def->scoped();
+ _class = findClassScoped(_scoped TSRMLS_CC);
+ }
+ else
+ {
+ _scoped = "::Ice::Object";
+ _class = findClass("Ice_ObjectImpl" TSRMLS_CC);
+ }
+
+ assert(_class);
+}
+
+bool
+IcePHP::ObjectMarshaler::marshal(zval* zv, const Ice::OutputStreamPtr& os, ObjectMap& m TSRMLS_DC)
+{
+ if(Z_TYPE_P(zv) == IS_NULL)
+ {
+ os->writeObject(0);
+ return true;
+ }
+
+ if(Z_TYPE_P(zv) != IS_OBJECT)
+ {
+ string s = zendTypeToString(Z_TYPE_P(zv));
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected object value of type %s but received %s",
+ _class ? _class->name : "ice_object", s.c_str());
+ return false;
+ }
+
+ //
+ // Compare the class entries.
+ //
+ zend_class_entry* ce = Z_OBJCE_P(zv);
+ if(ce != _class)
+ {
+ //
+ // Check for inheritance.
+ //
+ zend_class_entry* parent = ce->parent;
+ while(parent && parent != _class)
+ {
+ parent = parent->parent;
+ }
+
+ if(parent == NULL)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "expected object value of type %s but received %s", _class->name,
+ ce->name);
+ return false;
+ }
+ }
+
+ //
+ // ObjectWriter is a subclass of Ice::Object that wraps a PHP object for marshaling. It is
+ // possible that this PHP object has already been marshaled, therefore we first must check
+ // the object map to see if this object is present. If so, we use the existing ObjectWriter,
+ // otherwise we create a new one. The key of the map is the object's handle.
+ //
+ Ice::ObjectPtr writer;
+
+ //
+ // Retrieve the ClassDef for the actual type. This may fail if the type is Ice::Object.
+ //
+ Profile* profile = static_cast<Profile*>(ICE_G(profile));
+ assert(profile);
+ Slice::ClassDefPtr def;
+ Profile::ClassMap::iterator p = profile->classes.find(ce->name);
+ if(p != profile->classes.end())
+ {
+ def = p->second;
+ }
+
+ ObjectMap::iterator q = m.find(Z_OBJ_HANDLE_P(zv));
+ if(q == m.end())
+ {
+ writer = new ObjectWriter(zv, def, m TSRMLS_CC);
+ m.insert(pair<unsigned int, Ice::ObjectPtr>(Z_OBJ_HANDLE_P(zv), writer));
+ }
+ else
+ {
+ writer = q->second;
+ }
+
+ //
+ // Give the writer to the stream. The stream will eventually call write() on it.
+ //
+ os->writeObject(writer);
+
+ return true;
+}
+
+bool
+IcePHP::ObjectMarshaler::unmarshal(zval* zv, const Ice::InputStreamPtr& is TSRMLS_DC)
+{
+ ReadObjectCallbackPtr cb = new ReadObjectCallback;
+ cb->ce = _class;
+ cb->scoped = _scoped;
+ cb->zv = zv;
+
+ //
+ // Invoke readObject(), passing our callback object. When the object is eventually unmarshaled,
+ // our callback will be invoked and we will assign a value to zv.
+ //
+ is->readObject(cb);
+
+ return true;
+}
+
+void
+IcePHP::ObjectMarshaler::destroy()
+{
+}
+
+//
+// PHPObjectFactory implementation.
+//
+IcePHP::PHPObjectFactory::PHPObjectFactory(TSRMLS_D)
+{
+#ifdef ZTS
+ this->TSRMLS_C = TSRMLS_C;
+#endif
+}
+
+Ice::ObjectPtr
+IcePHP::PHPObjectFactory::create(const string& scoped)
+{
+ Ice::ObjectPtr result;
+
+ Profile* profile = static_cast<Profile*>(ICE_G(profile));
+ assert(profile);
+
+ ObjectFactoryMap* ofm = static_cast<ObjectFactoryMap*>(ICE_G(objectFactoryMap));
+ assert(ofm);
+
+ //
+ // First check our map for a factory registered for this type.
+ //
+ ObjectFactoryMap::iterator p;
+ p = ofm->find(scoped);
+ if(p == ofm->end())
+ {
+ //
+ // Next, check for a default factory.
+ //
+ p = ofm->find("");
+ }
+
+ //
+ // If we found a factory, invoke create() on the object.
+ //
+ if(p != ofm->end())
+ {
+ zval* id;
+ MAKE_STD_ZVAL(id);
+ ZVAL_STRINGL(id, const_cast<char*>(scoped.c_str()), scoped.length(), 1);
+
+ zval* zresult = NULL;
+
+ zend_call_method_with_1_params(&p->second, NULL, NULL, "create", &zresult, id);
+
+ zval_ptr_dtor(&id);
+
+ AutoDestroy destroyResult(zresult);
+
+ //
+ // Bail out if an exception has been thrown.
+ //
+ if(EG(exception))
+ {
+ throw AbortMarshaling();
+ }
+
+ if(zresult)
+ {
+ //
+ // If the factory returned a non-null value, verify that it is an object, and that it
+ // inherits from Ice_ObjectImpl.
+ //
+ if(!ZVAL_IS_NULL(zresult))
+ {
+ if(Z_TYPE_P(zresult) != IS_OBJECT)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "object factory did not return an object");
+ throw AbortMarshaling();
+ }
+
+ zend_class_entry* ce = Z_OBJCE_P(zresult);
+ zend_class_entry* base = findClass("Ice_ObjectImpl" TSRMLS_CC);
+ if(!checkClass(ce, base))
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR,
+ "object returned by factory does not implement Ice_ObjectImpl");
+ throw AbortMarshaling();
+ }
+
+ //
+ // Attempt to find a class definition for the object.
+ //
+ Profile::ClassMap::iterator p;
+ while(ce != NULL && (p = profile->classes.find(ce->name)) == profile->classes.end())
+ {
+ ce = ce->parent;
+ }
+
+ Slice::ClassDefPtr def;
+ if(ce != NULL)
+ {
+ assert(p != profile->classes.end());
+ def = p->second;
+ }
+
+ return new ObjectReader(zresult, def TSRMLS_CC);
+ }
+ }
+ }
+
+ //
+ // Attempt to find a class entry for the given type id. If no class entry is
+ // found, or the class is abstract, then we return nil and the stream will skip
+ // the slice and try again.
+ //
+ zend_class_entry* cls = NULL;
+ Slice::ClassDefPtr def;
+ if(scoped == Ice::Object::ice_staticId())
+ {
+ cls = findClass("Ice_ObjectImpl" TSRMLS_CC);
+ }
+ else
+ {
+ cls = findClassScoped(scoped TSRMLS_CC);
+ }
+
+ //
+ // Instantiate the class if it's not abstract.
+ //
+ const int abstractFlags = ZEND_ACC_INTERFACE | ZEND_ACC_EXPLICIT_ABSTRACT_CLASS;
+ if(cls && (cls->ce_flags & abstractFlags) == 0)
+ {
+ Profile::ClassMap::iterator p = profile->classes.find(cls->name);
+ if(p != profile->classes.end())
+ {
+ def = p->second;
+ }
+ zval* obj;
+ MAKE_STD_ZVAL(obj);
+ object_init_ex(obj, cls);
+ result = new ObjectReader(obj, def TSRMLS_CC);
+ zval_ptr_dtor(&obj);
+ }
+
+ return result;
+}
+
+void
+IcePHP::PHPObjectFactory::destroy()
+{
+}