summaryrefslogtreecommitdiff
path: root/php/src/IcePHP/Profile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'php/src/IcePHP/Profile.cpp')
-rw-r--r--php/src/IcePHP/Profile.cpp1302
1 files changed, 1302 insertions, 0 deletions
diff --git a/php/src/IcePHP/Profile.cpp b/php/src/IcePHP/Profile.cpp
new file mode 100644
index 00000000000..e738527f900
--- /dev/null
+++ b/php/src/IcePHP/Profile.cpp
@@ -0,0 +1,1302 @@
+// **********************************************************************
+//
+// 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 <Profile.h>
+#include <Util.h>
+
+#include <Slice/Preprocessor.h>
+#include <IceUtil/Options.h>
+#include <fstream>
+
+using namespace std;
+using namespace IcePHP;
+
+ZEND_EXTERN_MODULE_GLOBALS(ice)
+
+//
+// The name we give to the default profile.
+//
+static const char* _defaultProfileName = "__default__";
+
+//
+// The table of profiles.
+//
+static map<string, Profile*> _profiles;
+
+namespace IcePHP
+{
+
+//
+// CodeVisitor descends the Slice parse tree and generates PHP code for certain Slice types.
+//
+class CodeVisitor : public Slice::ParserVisitor
+{
+public:
+ CodeVisitor(ostream&, Profile::ClassMap& TSRMLS_DC);
+
+ virtual void visitClassDecl(const Slice::ClassDeclPtr&);
+ virtual bool visitClassDefStart(const Slice::ClassDefPtr&);
+ virtual void visitClassDefEnd(const Slice::ClassDefPtr&);
+ virtual bool visitExceptionStart(const Slice::ExceptionPtr&);
+ virtual void visitExceptionEnd(const Slice::ExceptionPtr&);
+ virtual bool visitStructStart(const Slice::StructPtr&);
+ virtual void visitStructEnd(const Slice::StructPtr&);
+ virtual void visitOperation(const Slice::OperationPtr&);
+ virtual void visitDataMember(const Slice::DataMemberPtr&);
+ virtual void visitDictionary(const Slice::DictionaryPtr&);
+ virtual void visitEnum(const Slice::EnumPtr&);
+ virtual void visitConst(const Slice::ConstPtr&);
+
+private:
+ string getTypeHint(const Slice::TypePtr&);
+
+ ostream& _out;
+ Profile::ClassMap& _classes;
+#ifdef ZTS
+ TSRMLS_D;
+#endif
+};
+
+} // End of namespace IcePHP
+
+//
+// This PHP code defines the core types we need. We supply a few of the common
+// local exception subclasses; all other local exceptions are mapped to
+// UnknownLocalException. We don't define Ice::Identity here because it's
+// possible the user will have included its definition (see createProfile).
+//
+// NOTE: If a local exception is added or removed here, then changes are also
+// necessary to IcePHP::throwException.
+//
+static const char* _coreTypes =
+ "define(\"ICE_STRING_VERSION\", \"3.2.0\");\n"
+ "define(\"ICE_INT_VERSION\", 30200);\n"
+ "\n"
+ "abstract class Ice_Exception extends Exception\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Exception::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "abstract class Ice_LocalException extends Ice_Exception\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_Exception::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_TwowayOnlyException extends Ice_LocalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_LocalException::__construct($message);\n"
+ " }\n"
+ "\n"
+ " function __toString()\n"
+ " {\n"
+ " return $this->operation;\n"
+ " }\n"
+ "\n"
+ " public $operation;\n"
+ "}\n"
+ "\n"
+ "class Ice_UnknownException extends Ice_LocalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_LocalException::__construct($message);\n"
+ " }\n"
+ "\n"
+ " function __toString()\n"
+ " {\n"
+ " return $this->unknown;\n"
+ " }\n"
+ "\n"
+ " public $unknown;\n"
+ "}\n"
+ "\n"
+ "class Ice_UnknownLocalException extends Ice_UnknownException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_UnknownException::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_UnknownUserException extends Ice_UnknownException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_UnknownException::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_RequestFailedException extends Ice_LocalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_LocalException::__construct($message);\n"
+ " }\n"
+ "\n"
+ " function __toString()\n"
+ " {\n"
+ " return get_class($this) . \"\\n\" .\n"
+ " \" identity: \" . Ice_identityToString($this->id) . \"\\n\" .\n"
+ " \" facet: \" . $this->facet . \"\\n\" .\n"
+ " \" operation: \" . $this->operation;\n"
+ " }\n"
+ "\n"
+ " public $id;\n"
+ " public $facet;\n"
+ " public $operation;\n"
+ "}\n"
+ "\n"
+ "class Ice_ObjectNotExistException extends Ice_RequestFailedException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_RequestFailedException::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_FacetNotExistException extends Ice_RequestFailedException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_RequestFailedException::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_OperationNotExistException extends Ice_RequestFailedException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_RequestFailedException::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_ProtocolException extends Ice_LocalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_LocalException::__construct($message);\n"
+ " }\n"
+ "\n"
+ " function __toString()\n"
+ " {\n"
+ " return get_class($this) . \"\\n\" .\n"
+ " \" reason: \" . $this->reason;\n"
+ " }\n"
+ "\n"
+ " public $reason;\n"
+ "}\n"
+ "\n"
+ "class Ice_MarshalException extends Ice_ProtocolException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_ProtocolException::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_NoObjectFactoryException extends Ice_MarshalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_MarshalException::__construct($message);\n"
+ " }\n"
+ "\n"
+ " function __toString()\n"
+ " {\n"
+ " return get_class($this) . \"\\n\" .\n"
+ " \" reason: \" . $this->reason . \"\\n\" .\n"
+ " \" type: \" . $this->type;\n"
+ " }\n"
+ "\n"
+ " public $type;\n"
+ "}\n"
+ "\n"
+ "class Ice_UnexpectedObjectException extends Ice_MarshalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_MarshalException::__construct($message);\n"
+ " }\n"
+ "\n"
+ " function __toString()\n"
+ " {\n"
+ " return get_class($this) . \"\\n\" .\n"
+ " \" reason: \" . $this->reason . \"\\n\" .\n"
+ " \" type: \" . $this->type . \"\\n\" .\n"
+ " \" expectedType: \" . $this->expectedType;\n"
+ " }\n"
+ "\n"
+ " public $type;\n"
+ "}\n"
+ "\n"
+ "class Ice_ProfileAlreadyLoadedException extends Ice_LocalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_LocalException::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "class Ice_ProfileNotFoundException extends Ice_LocalException\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_LocalException::__construct($message);\n"
+ " }\n"
+ "\n"
+ " function __toString()\n"
+ " {\n"
+ " return get_class($this) . \"\\n\" .\n"
+ " \" name: \" . $this->name;\n"
+ " }\n"
+ "\n"
+ " public $name;\n"
+ "}\n"
+ "\n"
+ "abstract class Ice_UserException extends Ice_Exception\n"
+ "{\n"
+ " function __construct($message = '')\n"
+ " {\n"
+ " Ice_Exception::__construct($message);\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "interface Ice_LocalObject\n"
+ "{\n"
+ "}\n"
+ "\n"
+ "class Ice_LocalObjectImpl implements Ice_LocalObject\n"
+ "{\n"
+ "}\n"
+ "\n"
+ "interface Ice_Object\n"
+ "{\n"
+ " function ice_preMarshal();\n"
+ " function ice_postUnmarshal();\n"
+ "}\n"
+ "\n"
+ "abstract class Ice_ObjectImpl implements Ice_Object\n"
+ "{\n"
+ " function ice_preMarshal()\n"
+ " {\n"
+ " }\n"
+ "\n"
+ " function ice_postUnmarshal()\n"
+ " {\n"
+ " }\n"
+ "}\n"
+ "\n"
+ "interface Ice_ObjectFactory extends Ice_LocalObject\n"
+ "{\n"
+ " function create($id);\n"
+ " function destroy();\n"
+ "}\n"
+;
+
+//
+// Parse the Slice files that define the types and operations available to a PHP script.
+//
+static bool
+parseSlice(const string& argStr, Slice::UnitPtr& unit TSRMLS_DC)
+{
+ vector<string> args;
+ try
+ {
+ args = IceUtil::Options::split(argStr);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ ostringstream ostr;
+ ex.ice_print(ostr);
+ string msg = ostr.str();
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "error occurred while parsing Slice options in `%s':\n%s",
+ argStr.c_str(), msg.c_str());
+ return false;
+ }
+
+ IceUtil::Options opts;
+ opts.addOpt("D", "", IceUtil::Options::NeedArg, "", IceUtil::Options::Repeat);
+ opts.addOpt("U", "", IceUtil::Options::NeedArg, "", IceUtil::Options::Repeat);
+ opts.addOpt("I", "", IceUtil::Options::NeedArg, "", IceUtil::Options::Repeat);
+ opts.addOpt("d", "debug");
+ opts.addOpt("", "ice");
+ opts.addOpt("", "case-sensitive");
+
+ vector<string> files;
+ try
+ {
+ args.insert(args.begin(), ""); // dummy argv[0]
+ files = opts.parse(args);
+ if(files.empty() && !argStr.empty())
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "no Slice files specified in `%s'", argStr.c_str());
+ return false;
+ }
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ ostringstream ostr;
+ ex.ice_print(ostr);
+ string msg = ostr.str();
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "error occurred while parsing Slice options in `%s':\n%s",
+ argStr.c_str(), msg.c_str());
+ return false;
+ }
+
+ string cppArgs;
+ bool debug = false;
+ bool ice = true; // This must be true so that we can create Ice::Identity when necessary.
+ bool caseSensitive = false;
+ if(opts.isSet("D"))
+ {
+ vector<string> optargs = opts.argVec("D");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ cppArgs += " -D" + *i;
+ }
+ }
+ if(opts.isSet("U"))
+ {
+ vector<string> optargs = opts.argVec("U");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ cppArgs += " -U" + *i;
+ }
+ }
+ if(opts.isSet("I"))
+ {
+ vector<string> optargs = opts.argVec("I");
+ for(vector<string>::const_iterator i = optargs.begin(); i != optargs.end(); ++i)
+ {
+ cppArgs += " -I" + *i;
+ }
+ }
+ debug = opts.isSet("d") || opts.isSet("debug");
+ caseSensitive = opts.isSet("case-sensitive");
+
+ bool ignoreRedefs = false;
+ bool all = true;
+ unit = Slice::Unit::createUnit(ignoreRedefs, all, ice, caseSensitive);
+ bool status = true;
+
+ for(vector<string>::iterator p = files.begin(); p != files.end(); ++p)
+ {
+ Slice::Preprocessor icecpp("icecpp", *p, cppArgs);
+ FILE* cppHandle = icecpp.preprocess(false);
+
+ if(cppHandle == 0)
+ {
+ status = false;
+ break;
+ }
+
+ int parseStatus = unit->parse(cppHandle, debug);
+
+ if(!icecpp.close())
+ {
+ status = false;
+ break;
+ }
+
+ if(parseStatus == EXIT_FAILURE)
+ {
+ status = false;
+ break;
+ }
+ }
+
+ return status;
+}
+
+static bool
+createProfile(const string& name, const string& config, const string& options, const string& slice TSRMLS_DC)
+{
+ map<string, Profile*>::iterator p = _profiles.find(name);
+ if(p != _profiles.end())
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "profile `%s' already exists", name.c_str());
+ return false;
+ }
+
+ Ice::PropertiesPtr properties = Ice::createProperties();
+
+ if(!config.empty())
+ {
+ try
+ {
+ properties->load(config);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ ostringstream ostr;
+ ex.ice_print(ostr);
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to load Ice configuration file %s:\n%s", config.c_str(),
+ ostr.str().c_str());
+ return false;
+ }
+ }
+
+ if(!options.empty())
+ {
+ vector<string> args;
+ try
+ {
+ args = IceUtil::Options::split(options);
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ ostringstream ostr;
+ ex.ice_print(ostr);
+ string msg = ostr.str();
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "error occurred while parsing the options `%s':\n%s",
+ options.c_str(), msg.c_str());
+ return false;
+ }
+ properties->parseCommandLineOptions("", args);
+ }
+
+ Slice::UnitPtr unit;
+ if(!slice.empty())
+ {
+ if(!parseSlice(slice, unit TSRMLS_CC))
+ {
+ return false;
+ }
+ }
+ else
+ {
+ //
+ // We must be allowed to obtain builtin types, as well as create Ice::Identity if necessary.
+ //
+ unit = Slice::Unit::createUnit(false, false, true, false);
+ }
+
+ //
+ // Create the Slice definition for Ice::Identity if it doesn't exist. The PHP class will
+ // be created automatically by CodeVisitor.
+ //
+ string scoped = "::Ice::Identity";
+ Slice::TypeList l = unit->lookupTypeNoBuiltin(scoped, false);
+ if(l.empty())
+ {
+ Slice::ContainedList c = unit->lookupContained("Ice", false);
+ Slice::ModulePtr module;
+ if(c.empty())
+ {
+ module = unit->createModule("Ice");
+ }
+ else
+ {
+ module = Slice::ModulePtr::dynamicCast(c.front());
+ if(!module)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "the symbol `::Ice' is defined in Slice but is not a module");
+ return false;
+ }
+ }
+ Slice::StructPtr identity = module->createStruct("Identity", false);
+ Slice::TypePtr str = unit->builtin(Slice::Builtin::KindString);
+ identity->createDataMember("category", str);
+ identity->createDataMember("name", str);
+ }
+
+ //
+ // Create the Slice definition for Ice::EndpointSelectionType if it doesn't exist. The PHP class will
+ // be created automatically by CodeVisitor.
+ //
+ scoped = "::Ice::EndpointSelectionType";
+ l = unit->lookupTypeNoBuiltin(scoped, false);
+ if(l.empty())
+ {
+ Slice::ContainedList c = unit->lookupContained("Ice", false);
+ Slice::ModulePtr module;
+ if(c.empty())
+ {
+ module = unit->createModule("Ice");
+ }
+ else
+ {
+ module = Slice::ModulePtr::dynamicCast(c.front());
+ if(!module)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "the symbol `::Ice' is defined in Slice but is not a module");
+ return false;
+ }
+ }
+ Slice::EnumPtr en = module->createEnum("EndpointSelectionType", false);
+ Slice::EnumeratorList el;
+ el.push_back(module->createEnumerator("Random"));
+ el.push_back(module->createEnumerator("Ordered"));
+ en->setEnumerators(el);
+ }
+
+ //
+ // Descend the parse tree to create PHP code.
+ //
+ ostringstream out;
+ Profile::ClassMap classes;
+ CodeVisitor visitor(out, classes TSRMLS_CC);
+ unit->visit(&visitor, false);
+
+ Profile* profile = new Profile;
+ profile->name = name;
+ profile->unit = unit;
+ profile->code = out.str();
+ profile->classes = classes;
+ profile->properties = properties;
+
+ _profiles[name] = profile;
+
+ return true;
+}
+
+bool
+IcePHP::profileInit(TSRMLS_D)
+{
+ //
+ // The default profile is configured using ice.config, ice.options and ice.slice. Named profiles
+ // are contained in a separate INI file, whose name is defined by ice.profiles.
+ //
+ const char* config = INI_STR("ice.config");
+ const char* options = INI_STR("ice.options");
+ const char* profiles = INI_STR("ice.profiles");
+ const char* slice = INI_STR("ice.slice");
+
+ if(!createProfile(_defaultProfileName, config, options, slice TSRMLS_CC))
+ {
+ return false;
+ }
+
+ if(strlen(profiles) > 0)
+ {
+ //
+ // The Zend engine doesn't export a function for loading an INI file, so we
+ // have to do it ourselves. The format is:
+ //
+ // [profile-name]
+ // ice.config = config-file
+ // ice.options = args
+ // ice.slice = slice-args
+ //
+ ifstream in(profiles);
+ if(!in)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to open Ice profiles in %s", profiles);
+ return false;
+ }
+
+ string currentName, currentConfig, currentOptions, currentSlice;
+ char line[1024];
+ while(in.getline(line, 1024))
+ {
+ const string delim = " \t\r\n";
+ string s = line;
+
+ string::size_type idx = s.find(';');
+ if(idx != string::npos)
+ {
+ s.erase(idx);
+ }
+
+ idx = s.find_last_not_of(delim);
+ if(idx != string::npos && idx + 1 < s.length())
+ {
+ s.erase(idx + 1);
+ }
+
+ string::size_type beg = s.find_first_not_of(delim);
+ if(beg == string::npos)
+ {
+ continue;
+ }
+
+ if(s[beg] == '[')
+ {
+ beg++;
+ string::size_type end = s.find_first_of(" \t]", beg);
+ if(end == string::npos || s[s.length() - 1] != ']')
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "invalid profile section in file %s:\n%s\n", profiles,
+ line);
+ return false;
+ }
+
+ if(!currentName.empty())
+ {
+ if(!createProfile(currentName, currentConfig, currentOptions, currentSlice TSRMLS_CC))
+ {
+ return false;
+ }
+ currentConfig.clear();
+ currentOptions.clear();
+ currentSlice.clear();
+ }
+
+ currentName = s.substr(beg, end - beg);
+ }
+ else
+ {
+ string::size_type end = s.find_first_of(delim + "=", beg);
+ assert(end != string::npos);
+
+ string key = s.substr(beg, end - beg);
+
+ end = s.find('=', end);
+ if(end == string::npos)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "invalid profile entry in file %s:\n%s\n", profiles,
+ line);
+ return false;
+ }
+ ++end;
+
+ string value;
+ beg = s.find_first_not_of(delim, end);
+ if(beg != string::npos)
+ {
+ end = s.length();
+ value = s.substr(beg, end - beg);
+ }
+
+ if(key == "config" || key == "ice.config")
+ {
+ currentConfig = value;
+ }
+ else if(key == "options" || key == "ice.options")
+ {
+ currentOptions = value;
+ }
+ else if(key == "slice" || key == "ice.slice")
+ {
+ currentSlice = value;
+ }
+ else
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unknown profile entry in file %s:\n%s\n", profiles,
+ line);
+ return false;
+ }
+
+ if(currentName.empty())
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "no section for profile entry in file %s:\n%s\n",
+ profiles, line);
+ return false;
+ }
+ }
+ }
+
+ if(!currentName.empty() && !createProfile(currentName, currentConfig, currentOptions, currentSlice TSRMLS_CC))
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+IcePHP::profileShutdown(TSRMLS_D)
+{
+ for(map<string, Profile*>::iterator p = _profiles.begin(); p != _profiles.end(); ++p)
+ {
+ try
+ {
+ p->second->unit->destroy();
+ }
+ catch(const IceUtil::Exception& ex)
+ {
+ ostringstream ostr;
+ ex.ice_print(ostr);
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "error while destroying Slice parse tree:\n%s\n",
+ ostr.str().c_str());
+ }
+
+ delete p->second;
+ }
+
+ _profiles.clear();
+
+ return true;
+}
+
+static bool
+do_load(const string& name, const Ice::StringSeq& args TSRMLS_DC)
+{
+ Profile* profile = static_cast<Profile*>(ICE_G(profile));
+
+ if(profile)
+ {
+ //
+ // A profile has already been loaded; raise Ice_ProfileAlreadyLoadedException.
+ //
+ zend_class_entry* cls = findClass("Ice_ProfileAlreadyLoadedException" TSRMLS_CC);
+ assert(cls != NULL);
+
+ zval* zex;
+ MAKE_STD_ZVAL(zex);
+ if(object_init_ex(zex, cls) != SUCCESS)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to create exception %s", cls->name);
+ return false;
+ }
+
+ zend_throw_exception_object(zex TSRMLS_CC);
+ return false;
+ }
+
+ string profileName = name;
+ if(profileName.empty())
+ {
+ profileName = _defaultProfileName;
+ }
+
+ //
+ // Compile the core types if necessary. We do this now so that the exceptions
+ // are available.
+ //
+ if(findClass("Ice_Exception" TSRMLS_CC) == NULL)
+ {
+ if(zend_eval_string(const_cast<char*>(_coreTypes), NULL, "__core" TSRMLS_CC) == FAILURE)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to create core types:\n%s\n", _coreTypes);
+ return false;
+ }
+ }
+
+ map<string, Profile*>::iterator p = _profiles.find(profileName);
+ if(p == _profiles.end())
+ {
+ zend_class_entry* cls = findClass("Ice_ProfileNotFoundException" TSRMLS_CC);
+ assert(cls != NULL);
+
+ zval* zex;
+ MAKE_STD_ZVAL(zex);
+ if(object_init_ex(zex, cls) != SUCCESS)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to create exception %s", cls->name);
+ return false;
+ }
+
+ //
+ // Set the name member.
+ //
+ zend_update_property_string(cls, zex, "name", sizeof("name") - 1,
+ const_cast<char*>(profileName.c_str()) TSRMLS_CC);
+
+ zend_throw_exception_object(zex TSRMLS_CC);
+ return false;
+ }
+ profile = p->second;
+
+ //
+ // Compile the user-defined types.
+ //
+ if(zend_eval_string(const_cast<char*>(profile->code.c_str()), NULL, "__slice" TSRMLS_CC) == FAILURE)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "unable to create Slice types:\n%s\n", profile->code.c_str());
+ return false;
+ }
+
+ //
+ // Make a copy of the profile's properties, and include any command-line arguments.
+ //
+ Ice::PropertiesPtr properties = Ice::createProperties();
+ properties->parseCommandLineOptions("", profile->properties->getCommandLineOptions());
+ properties->parseCommandLineOptions("", args);
+ ICE_G(properties) = new Ice::PropertiesPtr(properties);
+
+ ICE_G(profile) = profile;
+ return true;
+}
+
+ZEND_FUNCTION(Ice_loadProfile)
+{
+ if(ZEND_NUM_ARGS() > 1)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ char* name = "";
+ int len;
+
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &name, &len) == FAILURE)
+ {
+ return;
+ }
+
+ Ice::StringSeq args;
+ do_load(name, args TSRMLS_CC);
+}
+
+ZEND_FUNCTION(Ice_loadProfileWithArgs)
+{
+ if(ZEND_NUM_ARGS() > 2)
+ {
+ WRONG_PARAM_COUNT;
+ }
+
+ zval* zv;
+ char* name = "";
+ int len;
+
+ if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a|s", &zv, &name, &len) == FAILURE)
+ {
+ return;
+ }
+
+ //
+ // Extract the command-line arguments from the array.
+ //
+ Ice::StringSeq args;
+ HashTable* arr = Z_ARRVAL_P(zv);
+ HashPosition pos;
+ zval** val;
+ zend_hash_internal_pointer_reset_ex(arr, &pos);
+ while(zend_hash_get_current_data_ex(arr, (void**)&val, &pos) != FAILURE)
+ {
+ if(Z_TYPE_PP(val) != IS_STRING)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "argument array must contain strings");
+ return;
+ }
+ args.push_back(Z_STRVAL_PP(val));
+ zend_hash_move_forward_ex(arr, &pos);
+ }
+
+ do_load(name, args TSRMLS_CC);
+}
+
+ZEND_FUNCTION(Ice_dumpProfile)
+{
+ Profile* profile = static_cast<Profile*>(ICE_G(profile));
+ Ice::PropertiesPtr* properties = static_cast<Ice::PropertiesPtr*>(ICE_G(properties));
+
+ if(!profile)
+ {
+ php_error_docref(NULL TSRMLS_CC, E_ERROR, "no profile has been loaded");
+ return;
+ }
+
+ ostringstream out;
+ out << "Ice profile: " << profile->name << endl;
+
+ Ice::PropertyDict props = (*properties)->getPropertiesForPrefix("");
+ if(!props.empty())
+ {
+ out << endl << "Ice configuration properties:" << endl << endl;
+ for(Ice::PropertyDict::iterator p = props.begin(); p != props.end(); ++p)
+ {
+ out << p->first << "=" << p->second << endl;
+ }
+ }
+ else
+ {
+ out << endl << "Ice configuration properties: <none>" << endl;
+ }
+
+ if(!profile->code.empty())
+ {
+ out << endl << "PHP code for Slice types:" << endl << endl;
+ out << profile->code;
+ }
+ else
+ {
+ out << endl << "PHP code for Slice types: <none>" << endl;
+ }
+
+ string s = out.str();
+ PUTS(s.c_str());
+}
+
+IcePHP::CodeVisitor::CodeVisitor(ostream& out, map<string, Slice::ClassDefPtr>& classes TSRMLS_DC) :
+ _out(out), _classes(classes)
+{
+#ifdef ZTS
+ this->TSRMLS_C = TSRMLS_C;
+#endif
+}
+
+void
+IcePHP::CodeVisitor::visitClassDecl(const Slice::ClassDeclPtr& p)
+{
+ Slice::ClassDefPtr def = p->definition();
+ if(!def)
+ {
+ string scoped = p->scoped();
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s %s declared but not defined",
+ p->isInterface() ? "interface" : "class", scoped.c_str());
+ }
+}
+
+bool
+IcePHP::CodeVisitor::visitClassDefStart(const Slice::ClassDefPtr& p)
+{
+ string flat = flatten(p->scoped());
+
+ _classes[flat] = p;
+
+ Slice::ClassList bases = p->bases();
+
+ if(p->isInterface())
+ {
+ _out << "interface " << flat << " extends ";
+ if(!bases.empty())
+ {
+ for(Slice::ClassList::iterator q = bases.begin(); q != bases.end(); ++q)
+ {
+ if(q != bases.begin())
+ {
+ _out << ",";
+ }
+ _out << flatten((*q)->scoped());
+ }
+ }
+ else if(p->isLocal())
+ {
+ _out << "Ice_LocalObject";
+ }
+ else
+ {
+ _out << "Ice_Object";
+ }
+ }
+ else
+ {
+ if(p->isAbstract())
+ {
+ _out << "abstract ";
+ }
+ _out << "class " << flat << " extends ";
+ if(!bases.empty() && !bases.front()->isInterface())
+ {
+ _out << flatten(bases.front()->scoped());
+ bases.pop_front();
+ }
+ else if(p->isLocal())
+ {
+ _out << "Ice_LocalObjectImpl";
+ }
+ else
+ {
+ _out << "Ice_ObjectImpl";
+ }
+ if(!bases.empty())
+ {
+ _out << " implements ";
+ for(Slice::ClassList::iterator q = bases.begin(); q != bases.end(); ++q)
+ {
+ if(q != bases.begin())
+ {
+ _out << ",";
+ }
+ _out << flatten((*q)->scoped());
+ }
+ }
+ }
+
+ _out << endl << '{' << endl;
+
+ return true;
+}
+
+void
+IcePHP::CodeVisitor::visitClassDefEnd(const Slice::ClassDefPtr& p)
+{
+ _out << '}' << endl;
+}
+
+bool
+IcePHP::CodeVisitor::visitExceptionStart(const Slice::ExceptionPtr& p)
+{
+ string flat = flatten(p->scoped());
+ Slice::ExceptionPtr base = p->base();
+
+ _out << "class " << flat << " extends ";
+ string baseName;
+ if(!base)
+ {
+ if(p->isLocal())
+ {
+ baseName = "Ice_LocalException";
+ }
+ else
+ {
+ baseName = "Ice_UserException";
+ }
+ }
+ else
+ {
+ baseName = flatten(base->scoped());
+ }
+
+ _out << baseName << endl << '{' << endl;
+
+ _out << "function __construct($message = '')" << endl;
+ _out << "{" << endl;
+ _out << " " << baseName << "::__construct($message);" << endl;
+ _out << "}" << endl;
+
+ return true;
+}
+
+void
+IcePHP::CodeVisitor::visitExceptionEnd(const Slice::ExceptionPtr& p)
+{
+ _out << '}' << endl;
+}
+
+bool
+IcePHP::CodeVisitor::visitStructStart(const Slice::StructPtr& p)
+{
+ string flat = flatten(p->scoped());
+
+ _out << "class " << flatten(p->scoped()) << endl;
+ _out << '{' << endl;
+
+ return true;
+}
+
+void
+IcePHP::CodeVisitor::visitStructEnd(const Slice::StructPtr& p)
+{
+ _out << '}' << endl;
+}
+
+void
+IcePHP::CodeVisitor::visitOperation(const Slice::OperationPtr& p)
+{
+ string name = fixIdent(p->name());
+
+ Slice::ParamDeclList params = p->parameters();
+
+ Slice::ClassDefPtr cl = Slice::ClassDefPtr::dynamicCast(p->container());
+ assert(cl);
+
+ if(!cl->isInterface())
+ {
+ _out << "abstract ";
+ }
+ _out << "function " << name << '(';
+ for(Slice::ParamDeclList::const_iterator q = params.begin(); q != params.end(); ++q)
+ {
+ Slice::ParamDeclPtr param = *q;
+ if(q != params.begin())
+ {
+ _out << ", ";
+ }
+ if(param->isOutParam())
+ {
+ _out << '&';
+ }
+ else
+ {
+ string hint = getTypeHint(param->type());
+ if(!hint.empty())
+ {
+ _out << hint << ' ';
+ }
+ }
+ _out << '$' << fixIdent(param->name());
+ }
+ _out << ");" << endl;
+}
+
+void
+IcePHP::CodeVisitor::visitDataMember(const Slice::DataMemberPtr& p)
+{
+ _out << "public $" << fixIdent(p->name()) << ';' << endl;
+}
+
+void
+IcePHP::CodeVisitor::visitDictionary(const Slice::DictionaryPtr& p)
+{
+ Slice::TypePtr keyType = p->keyType();
+ if(!isNativeKey(keyType))
+ {
+ //
+ // TODO: Generate class.
+ //
+ string scoped = p->scoped();
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "skipping dictionary %s - unsupported key type", scoped.c_str());
+ }
+}
+
+void
+IcePHP::CodeVisitor::visitEnum(const Slice::EnumPtr& p)
+{
+ string flat = flatten(p->scoped());
+
+ _out << "class " << flat << endl;
+ _out << '{' << endl;
+
+ //
+ // Create a class constant for each enumerator.
+ //
+ Slice::EnumeratorList l = p->getEnumerators();
+ Slice::EnumeratorList::const_iterator q;
+ long i;
+ for(q = l.begin(), i = 0; q != l.end(); ++q, ++i)
+ {
+ string name = fixIdent((*q)->name());
+ _out << "const " << fixIdent((*q)->name()) << " = " << i << ';' << endl;
+ }
+
+ _out << '}' << endl;
+}
+
+void
+IcePHP::CodeVisitor::visitConst(const Slice::ConstPtr& p)
+{
+ string flat = flatten(p->scoped());
+ Slice::TypePtr type = p->type();
+ string value = p->value();
+
+ _out << "define(\"" << flat << "\", ";
+
+ Slice::BuiltinPtr b = Slice::BuiltinPtr::dynamicCast(type);
+ if(b)
+ {
+ switch(b->kind())
+ {
+ case Slice::Builtin::KindBool:
+ case Slice::Builtin::KindByte:
+ case Slice::Builtin::KindShort:
+ case Slice::Builtin::KindInt:
+ case Slice::Builtin::KindFloat:
+ case Slice::Builtin::KindDouble:
+ _out << value;
+ break;
+
+ case Slice::Builtin::KindLong:
+ {
+ IceUtil::Int64 l;
+ IceUtil::stringToInt64(value, l);
+ //
+ // The platform's 'long' type may not be 64 bits, so we store 64-bit
+ // values as a string.
+ //
+ if(sizeof(IceUtil::Int64) > sizeof(long) && (l < LONG_MIN || l > LONG_MAX))
+ {
+ _out << "\"" << value << "\";";
+ }
+ else
+ {
+ _out << value;
+ }
+ break;
+ }
+
+ case Slice::Builtin::KindString:
+ {
+ //
+ // Expand strings into the basic source character set. We can't use isalpha() and the like
+ // here because they are sensitive to the current locale.
+ //
+ static const string basicSourceChars = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_{}[]#()<>%:;,?*+=/^&|~!=,\\' \t";
+ static const set<char> charSet(basicSourceChars.begin(), basicSourceChars.end());
+
+ _out << "\""; // Opening "
+
+ ios_base::fmtflags originalFlags = _out.flags(); // Save stream state
+ streamsize originalWidth = _out.width();
+ ostream::char_type originalFill = _out.fill();
+
+ for(string::const_iterator c = value.begin(); c != value.end(); ++c)
+ {
+ if(*c == '$')
+ {
+ _out << "\\$";
+ }
+ else if(*c == '"')
+ {
+ _out << "\\\"";
+ }
+ else if(charSet.find(*c) == charSet.end())
+ {
+ unsigned char uc = *c; // char may be signed, so make it positive
+ _out << "\\"; // Print as octal if not in basic source character set
+ _out.flags(ios_base::oct);
+ _out.width(3);
+ _out.fill('0');
+ _out << static_cast<unsigned>(uc);
+ }
+ else
+ {
+ _out << *c; // Print normally if in basic source character set
+ }
+ }
+
+ _out.fill(originalFill); // Restore stream state
+ _out.width(originalWidth);
+ _out.flags(originalFlags);
+
+ _out << "\""; // Closing "
+
+ break;
+ }
+
+ case Slice::Builtin::KindObject:
+ case Slice::Builtin::KindObjectProxy:
+ case Slice::Builtin::KindLocalObject:
+ assert(false);
+ }
+
+ _out << ");" << endl;
+ return;
+ }
+
+ Slice::EnumPtr en = Slice::EnumPtr::dynamicCast(type);
+ if(en)
+ {
+ string::size_type colon = value.rfind(':');
+ if(colon != string::npos)
+ {
+ value = value.substr(colon + 1);
+ }
+ Slice::EnumeratorList l = en->getEnumerators();
+ Slice::EnumeratorList::iterator q;
+ for(q = l.begin(); q != l.end(); ++q)
+ {
+ if((*q)->name() == value)
+ {
+ _out << flatten(en->scoped()) << "::" << fixIdent(value) << ");" << endl;
+ return;
+ }
+ }
+ assert(false); // No match found.
+ }
+}
+
+string
+IcePHP::CodeVisitor::getTypeHint(const Slice::TypePtr& type)
+{
+ //
+ // Currently, the Zend engine does not allow an argument with a type hint to have
+ // a value of null, therefore we can only use type hints for structs.
+ //
+ Slice::StructPtr st = Slice::StructPtr::dynamicCast(type);
+ if(st)
+ {
+ return flatten(st->scoped());
+ }
+
+ return string();
+}