diff options
-rw-r--r-- | php/demo/Ice/value/Client.php | 192 | ||||
-rw-r--r-- | php/demo/Ice/value/README | 8 | ||||
-rw-r--r-- | php/demo/Ice/value/Value.ice | 49 | ||||
-rw-r--r-- | php/demo/Ice/value/php.ini | 1 | ||||
-rw-r--r-- | php/src/ice/communicator.cpp | 170 | ||||
-rw-r--r-- | php/src/ice/ice_communicator.h | 14 | ||||
-rw-r--r-- | php/src/ice/ice_marshal.h | 9 | ||||
-rw-r--r-- | php/src/ice/ice_util.h | 18 | ||||
-rw-r--r-- | php/src/ice/marshal.cpp | 247 | ||||
-rw-r--r-- | php/src/ice/profile.cpp | 19 | ||||
-rw-r--r-- | php/src/ice/util.cpp | 106 |
11 files changed, 766 insertions, 67 deletions
diff --git a/php/demo/Ice/value/Client.php b/php/demo/Ice/value/Client.php new file mode 100644 index 00000000000..01fff908a79 --- /dev/null +++ b/php/demo/Ice/value/Client.php @@ -0,0 +1,192 @@ +<?php +Ice_loadProfile(); + +class PrinterI extends Printer +{ + function printBackwards() + { + echo strrev($this->message),"\n"; + } +} + +class DerivedPrinterI extends DerivedPrinter +{ + function printBackwards() + { + echo strrev($this->message),"\n"; + } + + function printUpperCase() + { + echo strtoupper($this->derivedMessage),"\n"; + } +} + +class ObjectFactory extends Ice_LocalObjectImpl implements Ice_ObjectFactory +{ + function create($type) + { + if($type == "::Printer") + { + return new PrinterI; + } + + if($type == "::DerivedPrinter") + { + return new DerivedPrinterI; + } + + assert(false); + return null; + } + + function destroy() + { + } +} + +try +{ + $base = $ICE->stringToProxy("initial:default -p 10000"); + $initial = $base->ice_checkedCast("::Initial"); + + echo "\n"; + echo "Let's first transfer a simple object, for a class without\n"; + echo "operations, and print its contents. No factory is required\n"; + echo "for this.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $simple = $initial->getSimple(); + echo "==> ",$simple->message,"\n"; + + echo "\n"; + echo "Ok, this worked. Now let's try to transfer an object for a class\n"; + echo "with operations as type ::Ice::Object. Because no factory is installed,\n"; + echo "the class will be sliced to ::Ice::Object.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $obj = $initial->getPrinterAsObject(); + echo "==> The type ID of the received object is \"",get_class($obj),"\"\n"; + assert(get_class($obj) == "ice_objectimpl"); + + echo "\n"; + echo "Yes, this worked. Now let's try to transfer an object for a class\n"; + echo "with operations as type ::Printer, without installing a factory first.\n"; + echo "This should give us a `no factory' exception.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $gotException = false; + try + { + $initial->getPrinter($printer, $printerProxy); + } + catch(Ice_NoObjectFactoryException $ex) + { + print_r($ex); + $gotException = true; + } + assert($gotException); + + echo "\n"; + echo "Yep, that's what we expected. Now let's try again, but with\n"; + echo "installing an appropriate factory first. If successful, we print\n"; + echo "the object's content.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $factory = new ObjectFactory; + $ICE->addObjectFactory($factory, "::Printer"); + + $initial->getPrinter($printer, $printerProxy); + echo "==> ",$printer->message,"\n"; + + echo "\n";; + echo "Cool, it worked! Let's try calling the printBackwards() method\n"; + echo "on the object we just received locally.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + echo "==> "; + $printer->printBackwards(); + + echo "\n"; + echo "Now we call the same method, but on the remote object. Watch the\n"; + echo "server's output.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $printerProxy->printBackwards(); + + echo "\n"; + echo "Next, we transfer a derived object from the server as a base\n"; + echo "object. Since we haven't yet installed a factory for the derived\n"; + echo "class, the derived class (::DerivedPrinter) is sliced\n"; + echo "to its base class (::Printer).\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $derivedAsBase = $initial->getDerivedPrinter(); + echo "==> The type ID of the received object is \"",get_class($derivedAsBase),"\"\n"; + assert($derivedAsBase instanceof Printer); + + echo "\n"; + echo "Now we install a factory for the derived class, and try again.\n"; + echo "Because we receive the derived object as a base object, we\n"; + echo "we need to do a dynamic_cast<> to get from the base to the derived object.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $ICE->addObjectFactory($factory, "::DerivedPrinter"); + + $derivedAsBase = $initial->getDerivedPrinter(); + assert($derivedAsBase instanceof DerivedPrinter); + $derived = $derivedAsBase; + echo "==> dynamic_cast<> to derived object succeeded\n"; + echo "==> The type ID of the received object is \"",get_class($derived),"\"\n"; + + echo "\n"; + echo "Let's print the message contained in the derived object, and\n"; + echo "call the operation printUppercase() on the derived object\n"; + echo "locally.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + echo "==> ",$derived->derivedMessage,"\n"; + echo "==> "; + $derived->printUppercase(); + + echo "\n"; + echo "Finally, we try the same again, but instead of returning the\n"; + echo "derived object, we throw an exception containing the derived\n"; + echo "object.\n"; + echo "[press enter]\n"; + fgets(STDIN); + + $gotException = false; + try + { + $initial->throwDerivedPrinter(); + } + catch(DerivedPrinterException $ex) + { + $derived = $ex->derived; + assert($derived != null); + $gotException = true; + } + assert($gotException); + + echo "==> ",$derived->derivedMessage,"\n"; + echo "==> "; + $derived->printUppercase(); + + echo "\n"; + echo "That's it for this demo. Have fun with Ice!\n"; +} +catch(Ice_LocalException $ex) +{ + print_r($ex); +} +?> diff --git a/php/demo/Ice/value/README b/php/demo/Ice/value/README new file mode 100644 index 00000000000..5113013ef8c --- /dev/null +++ b/php/demo/Ice/value/README @@ -0,0 +1,8 @@ +This is a PHP version of the Ice "value" demo client. First, start the +C++ or Java version of the value demo server on the same host. Then +run this client with the following command: + +$ php -c php.ini -f Client.php + +Note that this client requires user input, therefore you must run this +example using the CLI version of the PHP interpreter. diff --git a/php/demo/Ice/value/Value.ice b/php/demo/Ice/value/Value.ice new file mode 100644 index 00000000000..4a69d6b6a7d --- /dev/null +++ b/php/demo/Ice/value/Value.ice @@ -0,0 +1,49 @@ +// ********************************************************************** +// +// Copyright (c) 2003 +// ZeroC, Inc. +// Billerica, MA, USA +// +// All Rights Reserved. +// +// Ice is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License version 2 as published by +// the Free Software Foundation. +// +// ********************************************************************** + +#ifndef VALUE_ICE +#define VALUE_ICE + +class Simple +{ + string message; +}; + +class Printer +{ + string message; + void printBackwards(); +}; + +class DerivedPrinter extends Printer +{ + string derivedMessage; + void printUppercase(); +}; + +exception DerivedPrinterException +{ + DerivedPrinter derived; +}; + +class Initial +{ + Simple getSimple(); + Object getPrinterAsObject(); + void getPrinter(out Printer impl, out Printer* proxy); + Printer getDerivedPrinter(); + void throwDerivedPrinter() throws DerivedPrinterException; +}; + +#endif diff --git a/php/demo/Ice/value/php.ini b/php/demo/Ice/value/php.ini new file mode 100644 index 00000000000..04f27b5db6d --- /dev/null +++ b/php/demo/Ice/value/php.ini @@ -0,0 +1 @@ +ice.slice=Value.ice diff --git a/php/src/ice/communicator.cpp b/php/src/ice/communicator.cpp index d306ee93c1b..0788425bba4 100644 --- a/php/src/ice/communicator.cpp +++ b/php/src/ice/communicator.cpp @@ -51,9 +51,13 @@ static void initCommunicator(ice_object* TSRMLS_DC); // static function_entry _methods[] = { - {"__construct", PHP_FN(Ice_Communicator___construct), NULL}, - {"stringToProxy", PHP_FN(Ice_Communicator_stringToProxy), NULL}, - {"proxyToString", PHP_FN(Ice_Communicator_proxyToString), NULL}, + {"__construct", PHP_FN(Ice_Communicator___construct), NULL}, + {"stringToProxy", PHP_FN(Ice_Communicator_stringToProxy), NULL}, + {"proxyToString", PHP_FN(Ice_Communicator_proxyToString), NULL}, + {"addObjectFactory", PHP_FN(Ice_Communicator_addObjectFactory), NULL}, + {"removeObjectFactory", PHP_FN(Ice_Communicator_removeObjectFactory), NULL}, + {"findObjectFactory", PHP_FN(Ice_Communicator_findObjectFactory), NULL}, + {"flushBatchRequests", PHP_FN(Ice_Communicator_flushBatchRequests), NULL}, {NULL, NULL, NULL} }; @@ -151,6 +155,11 @@ ZEND_FUNCTION(Ice_Communicator___construct) ZEND_FUNCTION(Ice_Communicator_stringToProxy) { + if(ZEND_NUM_ARGS() != 1) + { + WRONG_PARAM_COUNT; + } + ice_object* obj = getObject(getThis() TSRMLS_CC); if(!obj) { @@ -159,11 +168,6 @@ ZEND_FUNCTION(Ice_Communicator_stringToProxy) assert(obj->ptr); Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); - if(ZEND_NUM_ARGS() != 1) - { - WRONG_PARAM_COUNT; - } - char *str; int len; @@ -191,6 +195,11 @@ ZEND_FUNCTION(Ice_Communicator_stringToProxy) ZEND_FUNCTION(Ice_Communicator_proxyToString) { + if(ZEND_NUM_ARGS() != 1) + { + WRONG_PARAM_COUNT; + } + ice_object* obj = getObject(getThis() TSRMLS_CC); if(!obj) { @@ -199,11 +208,6 @@ ZEND_FUNCTION(Ice_Communicator_proxyToString) assert(obj->ptr); Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); - if(ZEND_NUM_ARGS() != 1) - { - WRONG_PARAM_COUNT; - } - zval* zprx; if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O!", &zprx, proxyClassEntry) == FAILURE) @@ -230,6 +234,146 @@ ZEND_FUNCTION(Ice_Communicator_proxyToString) } } +ZEND_FUNCTION(Ice_Communicator_addObjectFactory) +{ + if(ZEND_NUM_ARGS() != 2) + { + WRONG_PARAM_COUNT; + } + + ice_object* obj = getObject(getThis() TSRMLS_CC); + if(!obj) + { + return; + } + assert(obj->ptr); + Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); + + zval* zfactory; + char* id; + int len; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "os", &zfactory, &id, &len) == FAILURE) + { + return; + } + + // + // Verify that the object implements Ice_ObjectFactory. + // + // TODO: When zend_check_class is changed to also check interfaces, we can remove this code and + // pass the class entry for Ice_ObjectFactory to zend_parse_parameters instead. + // + zend_class_entry* ce = Z_OBJCE_P(zfactory); + zend_class_entry* base = findClass("Ice_ObjectFactory" TSRMLS_CC); + assert(base); + if(!checkClass(ce, base)) + { + zend_error(E_ERROR, "%s(): object does not implement Ice_ObjectFactory", get_active_function_name(TSRMLS_C)); + return; + } + + // + // Retrieve the PHPObjectFactory. + // + Ice::ObjectFactoryPtr factory = (*_this)->findObjectFactory(""); + PHPObjectFactoryPtr phpFactory = PHPObjectFactoryPtr::dynamicCast(factory); + assert(phpFactory); + + phpFactory->addObjectFactory(zfactory, id TSRMLS_CC); +} + +ZEND_FUNCTION(Ice_Communicator_removeObjectFactory) +{ + if(ZEND_NUM_ARGS() != 1) + { + WRONG_PARAM_COUNT; + } + + ice_object* obj = getObject(getThis() TSRMLS_CC); + if(!obj) + { + RETURN_NULL(); + } + assert(obj->ptr); + Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); + + char* id; + int len; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, len) == FAILURE) + { + RETURN_NULL(); + } + + // + // Retrieve the PHPObjectFactory. + // + Ice::ObjectFactoryPtr factory = (*_this)->findObjectFactory(""); + PHPObjectFactoryPtr phpFactory = PHPObjectFactoryPtr::dynamicCast(factory); + assert(phpFactory); + + phpFactory->removeObjectFactory(id TSRMLS_CC); +} + +ZEND_FUNCTION(Ice_Communicator_findObjectFactory) +{ + if(ZEND_NUM_ARGS() != 1) + { + WRONG_PARAM_COUNT; + } + + ice_object* obj = getObject(getThis() TSRMLS_CC); + if(!obj) + { + RETURN_NULL(); + } + assert(obj->ptr); + Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); + + char* id; + int len; + + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &id, len) == FAILURE) + { + RETURN_NULL(); + } + + // + // Retrieve the PHPObjectFactory. + // + Ice::ObjectFactoryPtr factory = (*_this)->findObjectFactory(""); + PHPObjectFactoryPtr phpFactory = PHPObjectFactoryPtr::dynamicCast(factory); + assert(phpFactory); + + phpFactory->findObjectFactory(id, return_value TSRMLS_CC); +} + +ZEND_FUNCTION(Ice_Communicator_flushBatchRequests) +{ + if(ZEND_NUM_ARGS() != 0) + { + WRONG_PARAM_COUNT; + } + + ice_object* obj = getObject(getThis() TSRMLS_CC); + if(!obj) + { + return; + } + assert(obj->ptr); + Ice::CommunicatorPtr* _this = static_cast<Ice::CommunicatorPtr*>(obj->ptr); + + try + { + (*_this)->flushBatchRequests(); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + } +} + #ifdef WIN32 extern "C" #endif diff --git a/php/src/ice/ice_communicator.h b/php/src/ice/ice_communicator.h index b5d43f3f44a..131d2839ece 100644 --- a/php/src/ice/ice_communicator.h +++ b/php/src/ice/ice_communicator.h @@ -25,12 +25,20 @@ extern "C" ZEND_FUNCTION(Ice_Communicator___construct); ZEND_FUNCTION(Ice_Communicator_stringToProxy); ZEND_FUNCTION(Ice_Communicator_proxyToString); +ZEND_FUNCTION(Ice_Communicator_addObjectFactory); +ZEND_FUNCTION(Ice_Communicator_removeObjectFactory); +ZEND_FUNCTION(Ice_Communicator_findObjectFactory); +ZEND_FUNCTION(Ice_Communicator_flushBatchRequests); } #define ICE_PHP_COMMUNICATOR_FUNCTIONS \ - ZEND_FE(Ice_Communicator___construct, NULL) \ - ZEND_FE(Ice_Communicator_stringToProxy, NULL) \ - ZEND_FE(Ice_Communicator_proxyToString, NULL) + ZEND_FE(Ice_Communicator___construct, NULL) \ + ZEND_FE(Ice_Communicator_stringToProxy, NULL) \ + ZEND_FE(Ice_Communicator_proxyToString, NULL) \ + ZEND_FE(Ice_Communicator_addObjectFactory, NULL) \ + ZEND_FE(Ice_Communicator_removeObjectFactory, NULL) \ + ZEND_FE(Ice_Communicator_findObjectFactory, NULL) \ + ZEND_FE(Ice_Communicator_flushBatchRequests, NULL) namespace IcePHP { diff --git a/php/src/ice/ice_marshal.h b/php/src/ice/ice_marshal.h index 19239343658..d8ad51c02df 100644 --- a/php/src/ice/ice_marshal.h +++ b/php/src/ice/ice_marshal.h @@ -84,7 +84,8 @@ class AbortMarshaling // // PHPObjectFactory is an implementation of Ice::ObjectFactory that creates PHP objects. -// A single instance can be used for all types. +// It is also the registry for user-defined PHP factory implementations. A single instance +// can be used for all types. // class PHPObjectFactory : public Ice::ObjectFactory { @@ -94,11 +95,17 @@ public: virtual Ice::ObjectPtr create(const std::string&); virtual void destroy(); + void addObjectFactory(zval*, const std::string& TSRMLS_DC); + void removeObjectFactory(const std::string& TSRMLS_DC); + void findObjectFactory(const std::string&, zval* TSRMLS_DC); + private: #ifdef ZTS TSRMLS_D; #endif + std::map<std::string, zval*> _factories; }; +typedef IceUtil::Handle<PHPObjectFactory> PHPObjectFactoryPtr; } // End of namespace IcePHP diff --git a/php/src/ice/ice_util.h b/php/src/ice/ice_util.h index df642753610..28173d97185 100644 --- a/php/src/ice/ice_util.h +++ b/php/src/ice/ice_util.h @@ -111,6 +111,11 @@ bool isNativeKey(const Slice::TypePtr&); bool getContext(zval*, Ice::Context& TSRMLS_DC); // +// Determines whether a class (or interface) inherits from a base class (or interface). +// +bool checkClass(zend_class_entry*, zend_class_entry*); + +// // Exception-safe efree. // class AutoEfree @@ -123,6 +128,19 @@ private: void* _p; }; +// +// Exception-safe zval destroy. +// +class AutoDestroy +{ +public: + AutoDestroy(zval* zv) : _zv(zv) {} + ~AutoDestroy() { if(_zv) zval_ptr_dtor(&_zv); } + +private: + zval* _zv; +}; + } // End of namespace IcePHP #endif diff --git a/php/src/ice/marshal.cpp b/php/src/ice/marshal.cpp index 1d44b128495..6524294b4ce 100644 --- a/php/src/ice/marshal.cpp +++ b/php/src/ice/marshal.cpp @@ -250,7 +250,7 @@ private: class ObjectReader : public Ice::Object { public: - ObjectReader(zend_class_entry*, const Slice::ClassDefPtr& TSRMLS_DC); + ObjectReader(zval*, const Slice::ClassDefPtr& TSRMLS_DC); ~ObjectReader(); virtual void __write(::IceInternal::BasicStream*) const; @@ -259,12 +259,12 @@ public: void setValue(zend_class_entry*, zval*); private: - zend_class_entry* _class; + zval* _value; Slice::ClassDefPtr _type; // nil if type is ::Ice::Object #ifdef ZTS TSRMLS_D; #endif - zval* _value; + zend_class_entry* _class; }; typedef IceUtil::Handle<ObjectReader> ObjectReaderPtr; @@ -1521,28 +1521,21 @@ IcePHP::ObjectWriter::__read(IceInternal::BasicStream* is, bool rid) // // ObjectReader implementation. // -IcePHP::ObjectReader::ObjectReader(zend_class_entry* cls, const Slice::ClassDefPtr& type TSRMLS_DC) : - _class(cls), _type(type) +IcePHP::ObjectReader::ObjectReader(zval* val, const Slice::ClassDefPtr& type TSRMLS_DC) : + _value(val), _type(type) { #ifdef ZTS this->TSRMLS_C = TSRMLS_C; #endif - // - // Create a zval to hold the new object. - // - MAKE_STD_ZVAL(_value); + ZVAL_ADDREF(_value); - // - // Instantiate the new object. - // - object_init_ex(_value, _class); + _class = Z_OBJCE_P(_value); } IcePHP::ObjectReader::~ObjectReader() { - Z_OBJ_HT_P(_value)->del_ref(_value TSRMLS_CC); - efree(_value); + zval_ptr_dtor(&_value); } void @@ -1638,23 +1631,11 @@ IcePHP::ObjectReader::setValue(zend_class_entry* ce, zval* zv) // // Compare the class entries. The argument "ce" represents the formal type. // - if(_class != ce) + if(!checkClass(_class, ce)) { - // - // Check for inheritance. - // - zend_class_entry* c = _class->parent; - while(c && c != ce) - { - c = c->parent; - } - - if(c == NULL) - { - zend_error(E_ERROR, "%s(): expected object value of type %s but received %s", - get_active_function_name(TSRMLS_C), ce->name, _class->name); - return; - } + Ice::NoObjectFactoryException ex(__FILE__, __LINE__); + ex.type = ce->name; + throw ex; } // @@ -1833,6 +1814,96 @@ IcePHP::PHPObjectFactory::create(const string& scoped) { Ice::ObjectPtr result; + Profile* profile = static_cast<Profile*>(ICE_G(profile)); + assert(profile); + + // + // First check our map for a factory registered for this type. + // + map<string, zval*>::iterator p; + p = _factories.find(scoped); + if(p == _factories.end()) + { + // + // Next, check for a default factory. + // + p = _factories.find(""); + } + + // + // If we found a factory, invoke create() on the object. + // + if(p != _factories.end()) + { + zval** args[1]; + zval* id; + MAKE_STD_ZVAL(id); + ZVAL_STRINGL(id, const_cast<char*>(scoped.c_str()), scoped.length(), 1); + + zval* create; + MAKE_STD_ZVAL(create); + ZVAL_STRINGL(create, "create", sizeof("create") - 1, 1); + + args[0] = &id; + zval* zresult = NULL; + int status = call_user_function_ex(NULL, &p->second, create, &zresult, 1, args, 0, NULL TSRMLS_CC); + + zval_ptr_dtor(&create); + 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) + { + zend_error(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)) + { + zend_error(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 @@ -1850,14 +1921,16 @@ IcePHP::PHPObjectFactory::create(const string& scoped) } if(cls && (cls->ce_flags & ZEND_CE_ABSTRACT) == 0) { - Profile* profile = static_cast<Profile*>(ICE_G(profile)); - assert(profile); Profile::ClassMap::iterator p = profile->classes.find(cls->name); if(p != profile->classes.end()) { def = p->second; } - result = new ObjectReader(cls, def TSRMLS_CC); + zval* obj; + MAKE_STD_ZVAL(obj); + object_init_ex(obj, cls); + result = new ObjectReader(obj, def TSRMLS_CC); + zval_ptr_dtor(&obj); } return result; @@ -1866,6 +1939,112 @@ IcePHP::PHPObjectFactory::create(const string& scoped) void IcePHP::PHPObjectFactory::destroy() { + // + // Invoke destroy() on each registered factory. + // + for(map<string, zval*>::iterator p = _factories.begin(); p != _factories.end(); ++p) + { + zval* funcName; + MAKE_STD_ZVAL(funcName); + ZVAL_STRINGL(funcName, "destroy", sizeof("destroy") - 1, 1); + + zval* result = NULL; + int status = call_user_function_ex(NULL, &p->second, funcName, &result, 0, NULL, 0, NULL TSRMLS_CC); + + zval_ptr_dtor(&funcName); + if(result) + { + zval_ptr_dtor(&result); + } + + Z_OBJ_HT_P(p->second)->del_ref(p->second TSRMLS_CC); + zval_ptr_dtor(&p->second); + } + + _factories.clear(); +} + +void +IcePHP::PHPObjectFactory::addObjectFactory(zval* factory, const string& id TSRMLS_DC) +{ + map<string, zval*>::iterator p = _factories.find(id); + if(p != _factories.end()) + { + Ice::AlreadyRegisteredException ex(__FILE__, __LINE__); + ex.kindOfObject = "object factory"; + ex.id = id; + throwException(ex TSRMLS_CC); + return; + } + + // + // Create a new zval with the same object handle as the factory. + // + zval* zv; + MAKE_STD_ZVAL(zv); + Z_TYPE_P(zv) = IS_OBJECT; + zv->value.obj = factory->value.obj; + + // + // Increment the factory's reference count. + // + Z_OBJ_HT_P(factory)->add_ref(factory TSRMLS_CC); + + // + // Update the factory map. + // + _factories[id] = zv; +} + +void +IcePHP::PHPObjectFactory::removeObjectFactory(const string& id TSRMLS_DC) +{ + map<string, zval*>::iterator p = _factories.find(id); + if(p == _factories.end()) + { + Ice::NotRegisteredException ex(__FILE__, __LINE__); + ex.kindOfObject = "object factory"; + ex.id = id; + throwException(ex TSRMLS_CC); + return; + } + + // + // Decrement the factory's reference count. + // + Z_OBJ_HT_P(p->second)->del_ref(p->second TSRMLS_CC); + + // + // Destroy the zval. + // + zval_ptr_dtor(&p->second); + + // + // Update the factory map. + // + _factories.erase(p); +} + +void +IcePHP::PHPObjectFactory::findObjectFactory(const string& id, zval* factory TSRMLS_DC) +{ + map<string, zval*>::iterator p = _factories.find(id); + if(p == _factories.end()) + { + ZVAL_NULL(factory); + return; + } + + // + // Set the zval with the same object handle as the factory. + // + Z_TYPE_P(factory) = IS_OBJECT; + factory->value.obj = p->second->value.obj; + + // + // Increment the factory's reference count. + // + Z_OBJ_HT_P(p->second)->add_ref(p->second TSRMLS_CC); } // diff --git a/php/src/ice/profile.cpp b/php/src/ice/profile.cpp index e556cb3d269..695125f850e 100644 --- a/php/src/ice/profile.cpp +++ b/php/src/ice/profile.cpp @@ -119,6 +119,19 @@ static const char* _coreTypes = "{\n" "}\n" "\n" + "class Ice_ProtocolException extends Ice_LocalException\n" + "{\n" + "}\n" + "\n" + "class Ice_MarshalException extends Ice_ProtocolException\n" + "{\n" + "}\n" + "\n" + "class Ice_NoObjectFactoryException extends Ice_MarshalException\n" + "{\n" + " var $type;\n" + "}\n" + "\n" "abstract class Ice_UserException\n" "{\n" "}\n" @@ -139,6 +152,12 @@ static const char* _coreTypes = "{\n" " var $ice_facets = array();\n" "}\n" + "\n" + "interface Ice_ObjectFactory implements Ice_LocalObject\n" + "{\n" + " function create($id);\n" + " function destroy();\n" + "}\n" ; // diff --git a/php/src/ice/util.cpp b/php/src/ice/util.cpp index b24ac10626f..83002e49fde 100644 --- a/php/src/ice/util.cpp +++ b/php/src/ice/util.cpp @@ -376,15 +376,13 @@ IcePHP::throwException(const IceUtil::Exception& ex TSRMLS_DC) // EG(exception) = zex; } - catch(const Ice::LocalException& e) + catch(const Ice::NoObjectFactoryException& e) { - // - // All other local exceptions are raised as UnknownLocalException. - // - zend_class_entry* cls = findClass("Ice_UnknownLocalException" TSRMLS_CC); + string name = e.ice_name(); + zend_class_entry* cls = findClassScoped(name TSRMLS_CC); if(!cls) { - zend_error(E_ERROR, "unable to find class Ice_UnknownLocalException"); + zend_error(E_ERROR, "unable to find class %s", name.c_str()); return; } @@ -397,20 +395,72 @@ IcePHP::throwException(const IceUtil::Exception& ex TSRMLS_DC) } // - // Set the unknown member. + // Set the type member. // - zval* unknown; - MAKE_STD_ZVAL(unknown); - ostringstream ostr; - e.ice_print(ostr); - string str = ostr.str(); - ZVAL_STRINGL(unknown, const_cast<char*>(str.c_str()), str.length(), 1); - if(add_property_zval(zex, "unknown", unknown) == FAILURE) + zval* type; + MAKE_STD_ZVAL(type); + ZVAL_STRINGL(type, const_cast<char*>(e.type.c_str()), e.type.length(), 1); + if(add_property_zval(zex, "type", type) == FAILURE) { - zend_error(E_ERROR, "unable to set unknown member of %s", cls->name); + zend_error(E_ERROR, "unable to set type member of %s", cls->name); return; } - zval_ptr_dtor(&unknown); // add_property_zval increments the refcount + zval_ptr_dtor(&type); // add_property_zval increments the refcount + + // + // Throw the exception. + // + EG(exception) = zex; + } + catch(const Ice::LocalException& e) + { + zval* zex; + MAKE_STD_ZVAL(zex); + + // + // See if we have a PHP class for the exception, otherwise raise UnknownLocalException. + // + string name = e.ice_name(); + zend_class_entry* cls = findClassScoped(name TSRMLS_CC); + if(cls) + { + if(object_init_ex(zex, cls) != SUCCESS) + { + zend_error(E_ERROR, "unable to create exception %s", cls->name); + return; + } + } + else + { + cls = findClass("Ice_UnknownLocalException" TSRMLS_CC); + if(!cls) + { + zend_error(E_ERROR, "unable to find class Ice_UnknownLocalException"); + return; + } + + if(object_init_ex(zex, cls) != SUCCESS) + { + zend_error(E_ERROR, "unable to create exception %s", cls->name); + return; + } + + // + // Set the unknown member. + // + zval* unknown; + MAKE_STD_ZVAL(unknown); + ostringstream ostr; + e.ice_print(ostr); + string str = ostr.str(); + ZVAL_STRINGL(unknown, const_cast<char*>(str.c_str()), str.length(), 1); + if(add_property_zval(zex, "unknown", unknown) == FAILURE) + { + zend_error(E_ERROR, "unable to set unknown member of %s", cls->name); + return; + } + zval_ptr_dtor(&unknown); // add_property_zval increments the refcount + } // // Throw the exception. @@ -658,3 +708,27 @@ IcePHP::getContext(zval* zv, Ice::Context& ctx TSRMLS_DC) return true; } + +bool +IcePHP::checkClass(zend_class_entry* ce, zend_class_entry* base) +{ + while(ce != NULL) + { + if(ce == base) + { + return true; + } + + for(zend_uint i = 0; i < ce->num_interfaces; ++i) + { + if(checkClass(ce->interfaces[i], base)) + { + return true; + } + } + + ce = ce->parent; + } + + return false; +} |