summaryrefslogtreecommitdiff
path: root/php/src/php5/Operation.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'php/src/php5/Operation.cpp')
-rw-r--r--php/src/php5/Operation.cpp908
1 files changed, 908 insertions, 0 deletions
diff --git a/php/src/php5/Operation.cpp b/php/src/php5/Operation.cpp
new file mode 100644
index 00000000000..f7f44b493f6
--- /dev/null
+++ b/php/src/php5/Operation.cpp
@@ -0,0 +1,908 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 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 <Operation.h>
+#include <Communicator.h>
+#include <Proxy.h>
+#include <Types.h>
+#include <Util.h>
+#include <Slice/PHPUtil.h>
+
+using namespace std;
+using namespace IcePHP;
+using namespace Slice::PHP;
+
+extern "C"
+{
+ZEND_FUNCTION(IcePHP_Operation_call);
+}
+
+namespace IcePHP
+{
+
+class ParamInfo : public IceUtil::Shared
+{
+public:
+
+ TypeInfoPtr type;
+ bool optional;
+ int tag;
+ int pos;
+};
+typedef IceUtil::Handle<ParamInfo> ParamInfoPtr;
+typedef list<ParamInfoPtr> ParamInfoList;
+
+//
+// Receives an out parameter or return value.
+//
+class ResultCallback : public UnmarshalCallback
+{
+public:
+
+ ResultCallback();
+ ~ResultCallback();
+
+ virtual void unmarshaled(zval*, zval*, void* TSRMLS_DC);
+
+ void unset(TSRMLS_D);
+
+ zval* zv;
+};
+typedef IceUtil::Handle<ResultCallback> ResultCallbackPtr;
+typedef vector<ResultCallbackPtr> ResultCallbackList;
+
+//
+// Encapsulates attributes of an operation.
+//
+class OperationI : public Operation
+{
+public:
+
+ OperationI(const char*, Ice::OperationMode, Ice::OperationMode, Ice::FormatType, zval*, zval*, zval*, zval*
+ TSRMLS_DC);
+ ~OperationI();
+
+ virtual zend_function* function();
+
+ string name; // On-the-wire name.
+ Ice::OperationMode mode;
+ Ice::OperationMode sendMode;
+ Ice::FormatType format;
+ ParamInfoList inParams;
+ ParamInfoList optionalInParams;
+ ParamInfoList outParams;
+ ParamInfoList optionalOutParams;
+ ParamInfoPtr returnType;
+ ExceptionInfoList exceptions;
+ bool sendsClasses;
+ bool returnsClasses;
+ int numParams;
+
+private:
+
+ zend_internal_function* _zendFunction;
+
+ static void convertParams(zval*, ParamInfoList&, bool& TSRMLS_DC);
+ static ParamInfoPtr convertParam(zval*, int TSRMLS_DC);
+ static void getArgInfo(zend_arg_info&, const ParamInfoPtr&, bool);
+};
+typedef IceUtil::Handle<OperationI> OperationIPtr;
+
+//
+// The base class for client-side invocations.
+//
+class Invocation : virtual public IceUtil::Shared
+{
+public:
+
+ Invocation(const Ice::ObjectPrx&, const CommunicatorInfoPtr& TSRMLS_DC);
+
+ virtual void invoke(INTERNAL_FUNCTION_PARAMETERS) = 0;
+
+protected:
+
+ Ice::ObjectPrx _prx;
+ CommunicatorInfoPtr _communicator;
+#ifdef ZTS
+ TSRMLS_D;
+#endif
+};
+typedef IceUtil::Handle<Invocation> InvocationPtr;
+
+//
+// TypedInvocation uses the information in the given operation to validate, marshal, and unmarshal
+// parameters and exceptions.
+//
+class TypedInvocation : virtual public Invocation
+{
+public:
+
+ TypedInvocation(const Ice::ObjectPrx&, const CommunicatorInfoPtr&, const OperationIPtr& TSRMLS_DC);
+
+protected:
+
+ OperationIPtr _op;
+
+ bool prepareRequest(int, zval**, Ice::OutputStream*, pair<const Ice::Byte*, const Ice::Byte*>& TSRMLS_DC);
+ void unmarshalResults(int, zval**, zval*, const pair<const Ice::Byte*, const Ice::Byte*>& TSRMLS_DC);
+ zval* unmarshalException(const pair<const Ice::Byte*, const Ice::Byte*>& TSRMLS_DC);
+ bool validateException(const ExceptionInfoPtr& TSRMLS_DC) const;
+ void checkTwowayOnly(const Ice::ObjectPrx&) const;
+};
+
+//
+// A synchronous typed invocation.
+//
+class SyncTypedInvocation : virtual public TypedInvocation
+{
+public:
+
+ SyncTypedInvocation(const Ice::ObjectPrx&, const CommunicatorInfoPtr&, const OperationIPtr& TSRMLS_DC);
+
+ virtual void invoke(INTERNAL_FUNCTION_PARAMETERS);
+};
+
+class UserExceptionFactory : public Ice::UserExceptionFactory
+{
+public:
+
+ UserExceptionFactory(const CommunicatorInfoPtr& communicator TSRMLS_DC) :
+ _communicator(communicator)
+ {
+#ifdef ZTS
+ this->TSRMLS_C = TSRMLS_C;
+#endif
+ }
+
+ virtual void createAndThrow(const string& id)
+ {
+ ExceptionInfoPtr info = getExceptionInfo(id TSRMLS_CC);
+ if(info)
+ {
+ throw ExceptionReader(_communicator, info TSRMLS_CC);
+ }
+ }
+
+private:
+
+ const CommunicatorInfoPtr _communicator;
+#if ZTS
+ TSRMLS_D;
+#endif
+};
+
+}
+
+//
+// ResultCallback implementation.
+//
+IcePHP::ResultCallback::ResultCallback() :
+ zv(0)
+{
+}
+
+IcePHP::ResultCallback::~ResultCallback()
+{
+ if(zv)
+ {
+ zval_ptr_dtor(&zv);
+ }
+}
+
+void
+IcePHP::ResultCallback::unmarshaled(zval* val, zval*, void* TSRMLS_DC)
+{
+ //
+ // Keep a reference to the unmarshaled value.
+ //
+ zv = val;
+ Z_ADDREF_P(zv);
+}
+
+void
+IcePHP::ResultCallback::unset(TSRMLS_D)
+{
+ MAKE_STD_ZVAL(zv);
+ assignUnset(zv TSRMLS_CC);
+}
+
+//
+// OperationI implementation.
+//
+IcePHP::OperationI::OperationI(const char* n, Ice::OperationMode m, Ice::OperationMode sm, Ice::FormatType f, zval* in,
+ zval* out, zval* ret, zval* ex TSRMLS_DC) :
+ name(n), mode(m), sendMode(sm), format(f), _zendFunction(0)
+{
+ //
+ // inParams
+ //
+ sendsClasses = false;
+ if(in)
+ {
+ convertParams(in, inParams, sendsClasses TSRMLS_CC);
+ }
+
+ //
+ // outParams
+ //
+ returnsClasses = false;
+ if(out)
+ {
+ convertParams(out, outParams, returnsClasses TSRMLS_CC);
+ }
+
+ //
+ // returnType
+ //
+ if(ret)
+ {
+ returnType = convertParam(ret, 0 TSRMLS_CC);
+ if(!returnsClasses)
+ {
+ returnsClasses = returnType->type->usesClasses();
+ }
+ }
+
+ numParams = static_cast<int>(inParams.size() + outParams.size());
+
+ class SortFn
+ {
+ public:
+ static bool compare(const ParamInfoPtr& lhs, const ParamInfoPtr& rhs)
+ {
+ return lhs->tag < rhs->tag;
+ }
+
+ static bool isRequired(const ParamInfoPtr& i)
+ {
+ return !i->optional;
+ }
+ };
+
+ //
+ // The inParams list represents the parameters in the order of declaration.
+ // We also need a sorted list of optional parameters.
+ //
+ ParamInfoList l = inParams;
+ copy(l.begin(), remove_if(l.begin(), l.end(), SortFn::isRequired), back_inserter(optionalInParams));
+ optionalInParams.sort(SortFn::compare);
+
+ //
+ // The outParams list represents the parameters in the order of declaration.
+ // We also need a sorted list of optional parameters. If the return value is
+ // optional, we must include it in this list.
+ //
+ l = outParams;
+ copy(l.begin(), remove_if(l.begin(), l.end(), SortFn::isRequired), back_inserter(optionalOutParams));
+ if(returnType && returnType->optional)
+ {
+ optionalOutParams.push_back(returnType);
+ }
+ optionalOutParams.sort(SortFn::compare);
+
+ //
+ // exceptions
+ //
+ if(ex)
+ {
+ HashTable* arr = Z_ARRVAL_P(ex);
+ HashPosition pos;
+ zend_hash_internal_pointer_reset_ex(arr, &pos);
+ void* data;
+ while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE)
+ {
+ zval** val = reinterpret_cast<zval**>(data);
+ ExceptionInfoPtr i = Wrapper<ExceptionInfoPtr>::value(*val TSRMLS_CC);
+ exceptions.push_back(i);
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+ }
+}
+
+IcePHP::OperationI::~OperationI()
+{
+ if(_zendFunction)
+ {
+ delete []_zendFunction->arg_info;
+ efree(const_cast<char*>(_zendFunction->function_name));
+ efree(_zendFunction);
+ }
+}
+
+zend_function*
+IcePHP::OperationI::function()
+{
+ if(!_zendFunction)
+ {
+ //
+ // Create an array that indicates how arguments are passed to the operation.
+ //
+ zend_arg_info* argInfo = new zend_arg_info[numParams];
+
+ int i = 0;
+ ParamInfoList::const_iterator p;
+ for(p = inParams.begin(); p != inParams.end(); ++p, ++i)
+ {
+ getArgInfo(argInfo[i], *p, false);
+#if PHP_VERSION_ID < 50400
+ argInfo[i].required_num_args = static_cast<zend_uint>(numParams);
+#endif
+ }
+ for(p = outParams.begin(); p != outParams.end(); ++p, ++i)
+ {
+ getArgInfo(argInfo[i], *p, true);
+#if PHP_VERSION_ID < 50400
+ argInfo[i].required_num_args = static_cast<zend_uint>(numParams);
+#endif
+ }
+
+ string fixed = fixIdent(name);
+ _zendFunction = static_cast<zend_internal_function*>(emalloc(sizeof(zend_internal_function)));
+ _zendFunction->type = ZEND_INTERNAL_FUNCTION;
+ _zendFunction->function_name = estrndup(STRCAST(fixed.c_str()), static_cast<zend_uint>(fixed.length()));
+ _zendFunction->scope = proxyClassEntry;
+ _zendFunction->fn_flags = ZEND_ACC_PUBLIC;
+ _zendFunction->prototype = 0;
+ _zendFunction->num_args = static_cast<zend_uint>(numParams);
+ _zendFunction->arg_info = argInfo;
+ _zendFunction->required_num_args = _zendFunction->num_args;
+#if PHP_VERSION_ID < 50400
+ _zendFunction->pass_rest_by_reference = 0;
+ _zendFunction->return_reference = 0;
+#endif
+ _zendFunction->handler = ZEND_FN(IcePHP_Operation_call);
+ }
+
+ return reinterpret_cast<zend_function*>(_zendFunction);
+}
+
+void
+IcePHP::OperationI::convertParams(zval* p, ParamInfoList& params, bool& usesClasses TSRMLS_DC)
+{
+ assert(Z_TYPE_P(p) == IS_ARRAY);
+ HashTable* arr = Z_ARRVAL_P(p);
+ HashPosition pos;
+ zend_hash_internal_pointer_reset_ex(arr, &pos);
+ void* data;
+ int i = 0;
+ while(zend_hash_get_current_data_ex(arr, &data, &pos) != FAILURE)
+ {
+ zval** val = reinterpret_cast<zval**>(data);
+ ParamInfoPtr param = convertParam(*val, i TSRMLS_CC);
+ params.push_back(param);
+ if(!param->optional && !usesClasses)
+ {
+ usesClasses = param->type->usesClasses();
+ }
+ zend_hash_move_forward_ex(arr, &pos);
+ ++i;
+ }
+}
+
+ParamInfoPtr
+IcePHP::OperationI::convertParam(zval* p, int pos TSRMLS_DC)
+{
+ assert(Z_TYPE_P(p) == IS_ARRAY);
+ HashTable* arr = Z_ARRVAL_P(p);
+ assert(zend_hash_num_elements(arr) == 3);
+
+ ParamInfoPtr param = new ParamInfo;
+ zval** m;
+
+ zend_hash_index_find(arr, 0, reinterpret_cast<void**>(&m));
+ param->type = Wrapper<TypeInfoPtr>::value(*m TSRMLS_CC);
+ zend_hash_index_find(arr, 1, reinterpret_cast<void**>(&m));
+ assert(Z_TYPE_PP(m) == IS_BOOL);
+ param->optional = Z_BVAL_PP(m) ? true : false;
+ zend_hash_index_find(arr, 2, reinterpret_cast<void**>(&m));
+ assert(Z_TYPE_PP(m) == IS_LONG);
+ param->tag = Z_LVAL_PP(m);
+ param->pos = pos;
+
+ return param;
+}
+
+void
+IcePHP::OperationI::getArgInfo(zend_arg_info& arg, const ParamInfoPtr& info, bool out)
+{
+ arg.name = 0;
+ arg.class_name = 0;
+ arg.allow_null = 1;
+
+ if(!info->optional)
+ {
+ const bool isArray = SequenceInfoPtr::dynamicCast(info->type) || DictionaryInfoPtr::dynamicCast(info->type);
+
+#if PHP_VERSION_ID < 50400
+ arg.array_type_hint = isArray ? 1 : 0;
+ arg.return_reference = 0;
+#else
+ arg.type_hint = isArray ? IS_ARRAY : 0;
+#endif
+ }
+ else
+ {
+#if PHP_VERSION_ID < 50400
+ arg.array_type_hint = 0;
+ arg.return_reference = 0;
+#else
+ arg.type_hint = 0;
+#endif
+ }
+
+ arg.pass_by_reference = out ? 1 : 0;
+}
+
+//
+// Invocation
+//
+IcePHP::Invocation::Invocation(const Ice::ObjectPrx& prx, const CommunicatorInfoPtr& communicator TSRMLS_DC) :
+ _prx(prx), _communicator(communicator)
+{
+#ifdef ZTS
+ this->TSRMLS_C = TSRMLS_C;
+#endif
+}
+
+//
+// TypedInvocation
+//
+IcePHP::TypedInvocation::TypedInvocation(const Ice::ObjectPrx& prx, const CommunicatorInfoPtr& communicator,
+ const OperationIPtr& op TSRMLS_DC) :
+ Invocation(prx, communicator TSRMLS_CC), _op(op)
+{
+}
+
+bool
+IcePHP::TypedInvocation::prepareRequest(int argc, zval** args, Ice::OutputStream* os,
+ pair<const Ice::Byte*, const Ice::Byte*>& params TSRMLS_DC)
+{
+ //
+ // Verify that the expected number of arguments are supplied. The context argument is optional.
+ //
+ if(argc != _op->numParams && argc != _op->numParams + 1)
+ {
+ runtimeError("incorrect number of parameters (%d)" TSRMLS_CC, argc);
+ return false;
+ }
+
+ //
+ // The operation's configuration (zend_function) forces out parameters
+ // to be passed by reference.
+ //
+ for(int i = static_cast<int>(_op->inParams.size()); i < _op->numParams; ++i)
+ {
+ assert(PZVAL_IS_REF(args[i]));
+ }
+
+ if(!_op->inParams.empty())
+ {
+ try
+ {
+ //
+ // Marshal the in parameters.
+ //
+ os->startEncapsulation(_prx->ice_getEncodingVersion(), _op->format);
+
+ ObjectMap objectMap;
+ ParamInfoList::iterator p;
+
+ //
+ // Validate the supplied arguments.
+ //
+ for(p = _op->inParams.begin(); p != _op->inParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ zval* arg = args[info->pos];
+ if((!info->optional || !isUnset(arg TSRMLS_CC)) && !info->type->validate(arg TSRMLS_CC))
+ {
+ invalidArgument("invalid value for argument %d in operation `%s'" TSRMLS_CC, info->pos + 1,
+ _op->name.c_str());
+ return false;
+ }
+ }
+
+ //
+ // Marshal the required parameters.
+ //
+ for(p = _op->inParams.begin(); p != _op->inParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ if(!info->optional)
+ {
+ zval* arg = args[info->pos];
+ info->type->marshal(arg, os, &objectMap, false TSRMLS_CC);
+ }
+ }
+
+ //
+ // Marshal the optional parameters.
+ //
+ for(p = _op->optionalInParams.begin(); p != _op->optionalInParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ zval* arg = args[info->pos];
+ if(!isUnset(arg TSRMLS_CC) && os->writeOptional(info->tag, info->type->optionalFormat()))
+ {
+ info->type->marshal(arg, os, &objectMap, true TSRMLS_CC);
+ }
+ }
+
+ if(_op->sendsClasses)
+ {
+ os->writePendingObjects();
+ }
+
+ os->endEncapsulation();
+ params = os->finished();
+ }
+ catch(const AbortMarshaling&)
+ {
+ return false;
+ }
+ catch(const Ice::Exception& ex)
+ {
+ throwException(ex TSRMLS_CC);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+IcePHP::TypedInvocation::unmarshalResults(int argc, zval** args, zval* ret,
+ const pair<const Ice::Byte*, const Ice::Byte*>& bytes TSRMLS_DC)
+{
+ Ice::InputStream is(_communicator->getCommunicator(), bytes);
+
+ //
+ // Store a pointer to a local StreamUtil object as the stream's closure.
+ // This is necessary to support object unmarshaling (see ObjectReader).
+ //
+ StreamUtil util;
+ assert(!is.getClosure());
+ is.setClosure(&util);
+
+ is.startEncapsulation();
+
+ ParamInfoList::iterator p;
+
+ //
+ // These callbacks collect references to the unmarshaled values. We copy them into
+ // the argument list *after* any pending objects have been unmarshaled.
+ //
+ ResultCallbackList outParamCallbacks;
+ ResultCallbackPtr retCallback;
+
+ outParamCallbacks.resize(_op->outParams.size());
+
+ //
+ // Unmarshal the required out parameters.
+ //
+ for(p = _op->outParams.begin(); p != _op->outParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ if(!info->optional)
+ {
+ ResultCallbackPtr cb = new ResultCallback;
+ outParamCallbacks[info->pos] = cb;
+ info->type->unmarshal(&is, cb, _communicator, 0, 0, false TSRMLS_CC);
+ }
+ }
+
+ //
+ // Unmarshal the required return value, if any.
+ //
+ if(_op->returnType && !_op->returnType->optional)
+ {
+ retCallback = new ResultCallback;
+ _op->returnType->type->unmarshal(&is, retCallback, _communicator, 0, 0, false TSRMLS_CC);
+ }
+
+ //
+ // Unmarshal the optional results. This includes an optional return value.
+ //
+ for(p = _op->optionalOutParams.begin(); p != _op->optionalOutParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+
+ ResultCallbackPtr cb = new ResultCallback;
+ if(_op->returnType && info->tag == _op->returnType->tag)
+ {
+ retCallback = cb;
+ }
+ else
+ {
+ outParamCallbacks[info->pos] = cb;
+ }
+
+ if(is.readOptional(info->tag, info->type->optionalFormat()))
+ {
+ info->type->unmarshal(&is, cb, _communicator, 0, 0, true TSRMLS_CC);
+ }
+ else
+ {
+ cb->unset(TSRMLS_C);
+ }
+ }
+
+ if(_op->returnsClasses)
+ {
+ is.readPendingObjects();
+ }
+
+ is.endEncapsulation();
+
+ util.updateSlicedData(TSRMLS_C);
+
+ int i = static_cast<int>(_op->inParams.size());
+ for(ResultCallbackList::iterator q = outParamCallbacks.begin(); q != outParamCallbacks.end(); ++q, ++i)
+ {
+ //
+ // We must explicitly destroy the existing contents of all zvals passed
+ // as out parameters, otherwise leaks occur.
+ //
+ zval* val = (*q)->zv;
+ zval_dtor(args[i]);
+ args[i]->value = val->value;
+ Z_TYPE_P(args[i]) = Z_TYPE_P(val);
+ zval_copy_ctor(args[i]);
+ }
+
+ if(_op->returnType)
+ {
+ ret->value = retCallback->zv->value;
+ Z_TYPE_P(ret) = Z_TYPE_P(retCallback->zv);
+ zval_copy_ctor(ret);
+ }
+}
+
+zval*
+IcePHP::TypedInvocation::unmarshalException(const pair<const Ice::Byte*, const Ice::Byte*>& bytes TSRMLS_DC)
+{
+ Ice::InputStream is(_communicator->getCommunicator(), bytes);
+
+ //
+ // Store a pointer to a local StreamUtil object as the stream's closure.
+ // This is necessary to support object unmarshaling (see ObjectReader).
+ //
+ StreamUtil util;
+ assert(!is.getClosure());
+ is.setClosure(&util);
+
+ is.startEncapsulation();
+
+ try
+ {
+ Ice::UserExceptionFactoryPtr factory = new UserExceptionFactory(_communicator TSRMLS_CC);
+ is.throwException(factory);
+ }
+ catch(const ExceptionReader& r)
+ {
+ is.endEncapsulation();
+
+ zval* ex = r.getException();
+ ExceptionInfoPtr info = r.getInfo();
+
+ if(validateException(info TSRMLS_CC))
+ {
+ util.updateSlicedData(TSRMLS_C);
+
+ Ice::SlicedDataPtr slicedData = r.getSlicedData();
+ if(slicedData)
+ {
+ StreamUtil::setSlicedDataMember(ex, slicedData TSRMLS_CC);
+ }
+
+ return ex;
+ }
+ else
+ {
+ zval_ptr_dtor(&ex);
+ Ice::UnknownUserException uue(__FILE__, __LINE__,
+ "operation raised undeclared exception `" + info->id + "'");
+ return convertException(uue TSRMLS_CC);
+ }
+ }
+
+ //
+ // Getting here should be impossible: we can get here only if the
+ // sender has marshaled a sequence of type IDs, none of which we
+ // have a factory for. This means that sender and receiver disagree
+ // about the Slice definitions they use.
+ //
+ Ice::UnknownUserException uue(__FILE__, __LINE__, "unknown exception");
+ return convertException(uue TSRMLS_CC);
+}
+
+bool
+IcePHP::TypedInvocation::validateException(const ExceptionInfoPtr& info TSRMLS_DC) const
+{
+ for(ExceptionInfoList::const_iterator p = _op->exceptions.begin(); p != _op->exceptions.end(); ++p)
+ {
+ if(info->isA((*p)->id))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+IcePHP::TypedInvocation::checkTwowayOnly(const Ice::ObjectPrx& proxy) const
+{
+ if((_op->returnType || !_op->outParams.empty()) && !proxy->ice_isTwoway())
+ {
+ Ice::TwowayOnlyException ex(__FILE__, __LINE__);
+ ex.operation = _op->name;
+ throw ex;
+ }
+}
+
+//
+// SyncTypedInvocation
+//
+IcePHP::SyncTypedInvocation::SyncTypedInvocation(const Ice::ObjectPrx& prx, const CommunicatorInfoPtr& communicator,
+ const OperationIPtr& op TSRMLS_DC) :
+ Invocation(prx, communicator TSRMLS_CC), TypedInvocation(prx, communicator, op TSRMLS_CC)
+{
+}
+
+void
+IcePHP::SyncTypedInvocation::invoke(INTERNAL_FUNCTION_PARAMETERS)
+{
+ //
+ // Retrieve the arguments.
+ //
+ zval*** args = static_cast<zval***>(emalloc(ZEND_NUM_ARGS() * sizeof(zval**)));
+ AutoEfree autoArgs(args); // Call efree on return
+ if(zend_get_parameters_array_ex(ZEND_NUM_ARGS(), args) == FAILURE)
+ {
+ runtimeError("unable to get arguments" TSRMLS_CC);
+ return;
+ }
+
+ Ice::OutputStream os(_prx->ice_getCommunicator());
+ pair<const Ice::Byte*, const Ice::Byte*> params;
+ if(!prepareRequest(ZEND_NUM_ARGS(), *args, &os, params TSRMLS_CC))
+ {
+ return;
+ }
+
+ bool hasCtx = false;
+ Ice::Context ctx;
+ if(ZEND_NUM_ARGS() == _op->numParams + 1)
+ {
+ if(!extractStringMap(*args[ZEND_NUM_ARGS() - 1], ctx TSRMLS_CC))
+ {
+ return;
+ }
+ hasCtx = true;
+ }
+
+ try
+ {
+ checkTwowayOnly(_prx);
+
+ //
+ // Invoke the operation.
+ //
+ vector<Ice::Byte> result;
+ bool status;
+ {
+ if(hasCtx)
+ {
+ status = _prx->ice_invoke(_op->name, _op->sendMode, params, result, ctx);
+ }
+ else
+ {
+ status = _prx->ice_invoke(_op->name, _op->sendMode, params, result);
+ }
+ }
+
+ //
+ // Process the reply.
+ //
+ if(_prx->ice_isTwoway())
+ {
+ if(!status)
+ {
+ //
+ // Unmarshal a user exception.
+ //
+ pair<const Ice::Byte*, const Ice::Byte*> rb(0, 0);
+ if(!result.empty())
+ {
+ rb.first = &result[0];
+ rb.second = &result[0] + result.size();
+ }
+
+ zval* ex = unmarshalException(rb TSRMLS_CC);
+ if(ex)
+ {
+ zend_throw_exception_object(ex TSRMLS_CC);
+ }
+ }
+ else if(!_op->outParams.empty() || _op->returnType)
+ {
+ //
+ // Unmarshal the results.
+ //
+ pair<const Ice::Byte*, const Ice::Byte*> rb(0, 0);
+ if(!result.empty())
+ {
+ rb.first = &result[0];
+ rb.second = &result[0] + result.size();
+ }
+ unmarshalResults(ZEND_NUM_ARGS(), *args, return_value, rb TSRMLS_CC);
+ }
+ }
+ }
+ catch(const AbortMarshaling&)
+ {
+ }
+ catch(const Ice::Exception& ex)
+ {
+ throwException(ex TSRMLS_CC);
+ }
+}
+
+ZEND_FUNCTION(IcePHP_defineOperation)
+{
+ zval* cls;
+ char* name;
+ int nameLen;
+ long mode;
+ long sendMode;
+ long format;
+ zval* inParams;
+ zval* outParams;
+ zval* returnType;
+ zval* exceptions;
+
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("osllla!a!a!a!"), &cls, &name, &nameLen,
+ &mode, &sendMode, &format, &inParams, &outParams, &returnType, &exceptions) == FAILURE)
+ {
+ return;
+ }
+
+ TypeInfoPtr type = Wrapper<TypeInfoPtr>::value(cls TSRMLS_CC);
+ ClassInfoPtr c = ClassInfoPtr::dynamicCast(type);
+ assert(c);
+
+ OperationIPtr op = new OperationI(name, static_cast<Ice::OperationMode>(mode),
+ static_cast<Ice::OperationMode>(sendMode), static_cast<Ice::FormatType>(format),
+ inParams, outParams, returnType, exceptions TSRMLS_CC);
+
+ c->addOperation(name, op);
+}
+
+ZEND_FUNCTION(IcePHP_Operation_call)
+{
+ Ice::ObjectPrx proxy;
+ ClassInfoPtr cls;
+ CommunicatorInfoPtr comm;
+#ifndef NDEBUG
+ bool b =
+#endif
+ fetchProxy(getThis(), proxy, cls, comm TSRMLS_CC);
+ assert(b);
+ assert(proxy);
+ assert(cls);
+
+ OperationPtr op = cls->getOperation(get_active_function_name(TSRMLS_C));
+ assert(op); // handleGetMethod should have already verified the operation's existence.
+ OperationIPtr opi = OperationIPtr::dynamicCast(op);
+ assert(opi);
+
+ InvocationPtr inv = new SyncTypedInvocation(proxy, comm, opi TSRMLS_CC);
+ inv->invoke(INTERNAL_FUNCTION_PARAM_PASSTHRU);
+}