diff options
author | xue <> | 2007-01-01 17:09:55 +0000 |
---|---|---|
committer | xue <> | 2007-01-01 17:09:55 +0000 |
commit | fac69c59fab801bdc793cd500492f23d69427c33 (patch) | |
tree | c5462df88304cd7433e355815eec302187c15e62 | |
parent | 279aa3bb883563d26ce316563ee3ef083f9905da (diff) |
added TSoapService and TSoapServer.
-rw-r--r-- | HISTORY | 1 | ||||
-rw-r--r-- | framework/3rdParty/WsdlGen/Wsdl.php | 102 | ||||
-rw-r--r-- | framework/3rdParty/WsdlGen/WsdlGenerator.php | 97 | ||||
-rw-r--r-- | framework/Exceptions/messages.txt | 9 | ||||
-rw-r--r-- | framework/Web/Services/TSoapService.php | 593 |
5 files changed, 620 insertions, 182 deletions
@@ -12,6 +12,7 @@ NEW: SQLMap (Wei) NEW: TOutputCache (Qiang) NEW: TQueue (Qiang) NEW: TSessionPageStatePersister (Qiang) +NEW: TSoapService, TSoapServer (Knut, Qiang) NEW: TFeedService, TRssFeedDocument (Knut, Qiang) NEW: TJsonService NEW: TCacheDependency, TFileCacheDependency, TDirectoryCacheDependency (Qiang) diff --git a/framework/3rdParty/WsdlGen/Wsdl.php b/framework/3rdParty/WsdlGen/Wsdl.php index dcccc5cd..55694438 100644 --- a/framework/3rdParty/WsdlGen/Wsdl.php +++ b/framework/3rdParty/WsdlGen/Wsdl.php @@ -16,7 +16,7 @@ */ /** - * Contains the dom object used to build up the wsdl. The + * Contains the dom object used to build up the wsdl. The * operations generated by the generator are stored in here until the getWsdl() * method is called which builds and returns the generated XML string. * @author Marcus Nyeholt <tanus@users.sourceforge.net> @@ -29,51 +29,51 @@ class Wsdl * @var string */ private $serviceName; - + /** * The URI to find the service at. If empty, the current * uri will be used (minus any query string) */ private $serviceUri; - + /** * The complex types declarations * @var ArrayObject */ private $types; - - + + /** * A collection of SOAP operations - * @var ArrayObject + * @var array */ - private $operations; - + private $operations=array(); + /** * Wsdl DOMDocument that's generated. */ private $wsdl = null; - + /** * The definitions created for the WSDL */ private $definitions = null; - + /** * The target namespace variable? */ private $targetNamespace =''; - + /** * The binding style (default at the moment) */ private $bindingStyle = 'rpc'; - + /** * The binding uri */ private $bindingTransport = 'http://schemas.xmlsoap.org/soap/http'; - + /** * Creates a new Wsdl thing * @param string $name the name of the service. @@ -87,13 +87,13 @@ class Wsdl $this->types = new ArrayObject(); $this->targetNamespace = 'urn:'.$name.'wsdl'; } - + public function getWsdl() { $this->buildWsdl(); return $this->wsdl; } - + /** * Generates the WSDL file into the $this->wsdl variable */ @@ -105,37 +105,37 @@ class Wsdl xmlns:tns="'.$this->targetNamespace.'" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" - xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/"></definitions>'; + xmlns:soap-enc="http://schemas.xmlsoap.org/soap/encoding/"></definitions>'; $dom = DOMDocument::loadXml($xml); $this->definitions = $dom->documentElement; - + $this->addTypes($dom); - + $this->addMessages($dom); $this->addPortTypes($dom); $this->addBindings($dom); $this->addService($dom); - + $this->wsdl = $dom->saveXML(); } - + /** * Adds complexType definitions to the document * @param DomDocument $dom The document to add to */ public function addTypes(DomDocument $dom) { - if (!count($this->types)) return; - $types = $dom->createElement('types'); + if (!count($this->types)) return; + $types = $dom->createElement('types'); $schema = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:schema'); $schema->setAttribute('targetNamespace', $this->targetNamespace); - foreach($this->types as $type => $elements) + foreach($this->types as $type => $elements) { $complexType = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:complexType'); - $complexType->setAttribute('name', $type); + $complexType->setAttribute('name', $type); if(substr($type, strlen($type) - 5, 5) == 'Array') // if it's an array - { + { $complexContent = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:complexContent'); $restriction = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:restriction'); $restriction->setAttribute('base', 'SOAP-ENC:Array'); @@ -144,27 +144,27 @@ class Wsdl $attribute->setAttribute('arrayType', 'tns:' . substr($type, 0, strlen($type) - 5) . '[]'); $restriction->appendChild($attribute); $complexContent->appendChild($restriction); - $complexType->appendChild($complexContent); - } - else + $complexType->appendChild($complexContent); + } + else { $all = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:all'); - foreach($elements as $elem) + foreach($elements as $elem) { $e = $dom->createElementNS('http://www.w3.org/2001/XMLSchema', 'xsd:element'); - $e->setAttribute('name', $elem['name']); + $e->setAttribute('name', $elem['name']); $e->setAttribute('type', $elem['type']); - $all->appendChild($e); + $all->appendChild($e); } $complexType->appendChild($all); - } + } $schema->appendChild($complexType); - $types->appendChild($schema); + $types->appendChild($schema); } - + $this->definitions->appendChild($types); } - + /** * Add messages for the service * @param DomDocument $dom The document to add to @@ -175,7 +175,7 @@ class Wsdl $operation->setMessageElements($this->definitions, $dom); } } - + /** * Add the port types for the service * @param DomDocument $dom The document to add to @@ -184,14 +184,14 @@ class Wsdl { $portType = $dom->createElement('portType'); $portType->setAttribute('name', $this->serviceName.'PortType'); - + $this->definitions->appendChild($portType); foreach ($this->operations as $operation) { $portOperation = $operation->getPortOperation($dom); $portType->appendChild($portOperation); } } - + /** * Add the bindings for the service * @param DomDocument $dom The document to add to @@ -201,20 +201,20 @@ class Wsdl $binding = $dom->createElement('binding'); $binding->setAttribute('name', $this->serviceName.'Binding'); $binding->setAttribute('type', 'tns:'.$this->serviceName.'PortType'); - + $soapBinding = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/soap/', 'soap:binding'); $soapBinding->setAttribute('style', $this->bindingStyle); $soapBinding->setAttribute('transport', $this->bindingTransport); $binding->appendChild($soapBinding); - + $this->definitions->appendChild($binding); - + foreach ($this->operations as $operation) { $bindingOperation = $operation->getBindingOperation($dom, $this->targetNamespace, $this->bindingStyle); $binding->appendChild($bindingOperation); } } - + /** * Add the service definition * @param DomDocument $dom The document to add to @@ -223,20 +223,20 @@ class Wsdl { $service = $dom->createElement('service'); $service->setAttribute('name', $this->serviceName.'Service'); - + $port = $dom->createElement('port'); $port->setAttribute('name', $this->serviceName.'Port'); $port->setAttribute('binding', 'tns:'.$this->serviceName.'Binding'); - + $soapAddress = $dom->createElementNS('http://schemas.xmlsoap.org/wsdl/soap/', 'soap:address'); $soapAddress->setAttribute('location', $this->serviceUri); $port->appendChild($soapAddress); - + $service->appendChild($port); - - $this->definitions->appendChild($service); + + $this->definitions->appendChild($service); } - + /** * Adds an operation to have port types and bindings output * @param WsdlOperation $operation The operation to add @@ -245,15 +245,15 @@ class Wsdl { $this->operations[] = $operation; } - + /** * Adds complexTypes to the wsdl * @param string $type Name of the type - * @param Array $elements Elements of the type (each one is an associative array('name','type')) + * @param Array $elements Elements of the type (each one is an associative array('name','type')) */ public function addComplexType($type, $elements) { - $this->types[$type] = $elements; + $this->types[$type] = $elements; } } ?>
\ No newline at end of file diff --git a/framework/3rdParty/WsdlGen/WsdlGenerator.php b/framework/3rdParty/WsdlGen/WsdlGenerator.php index 3d788769..b7ab544d 100644 --- a/framework/3rdParty/WsdlGen/WsdlGenerator.php +++ b/framework/3rdParty/WsdlGen/WsdlGenerator.php @@ -20,7 +20,7 @@ require_once(dirname(__FILE__).'/WsdlMessage.php'); require_once(dirname(__FILE__).'/WsdlOperation.php'); /** - * Generator for the wsdl. + * Generator for the wsdl. * Special thanks to Cristian Losada for implementing the Complex Types section of the WSDL. * @author Marcus Nyeholt <tanus@users.sourceforge.net> * @author Cristian Losada <cristian@teaxul.com> @@ -33,37 +33,37 @@ class WsdlGenerator * var WsdlGenerator */ private static $instance; - + /** * The name of this service (the classname) * @var string */ private $serviceName = ''; - + /** * The complex types to use in the wsdl * @var Array */ private $types = array(); - + /** * The operations available in this wsdl * @var ArrayObject */ private $operations; - + /** * The wsdl object. * @var object */ private $wsdlDocument; - + /** * The actual wsdl string * @var string */ private $wsdl = ''; - + /** * The singleton instance for the generator */ @@ -74,7 +74,7 @@ class WsdlGenerator } return self::$instance; } - + /** * Get the Wsdl generated * @return string The Wsdl for this wsdl @@ -83,10 +83,10 @@ class WsdlGenerator { return $this->wsdl; } - + /** * Generates WSDL for a passed in class, and saves it in the current object. The - * WSDL can then be retrieved by calling + * WSDL can then be retrieved by calling * @param string $className The name of the class to generate for * @param string $serviceUri The URI of the service that handles this WSDL * @return void @@ -94,24 +94,24 @@ class WsdlGenerator public function generateWsdl($className, $serviceUri='') { $this->wsdlDocument = new Wsdl($className, $serviceUri); - - $classReflect = new ReflectionClass($className); + + $classReflect = new ReflectionClass($className); $methods = $classReflect->getMethods(); - + foreach ($methods as $method) { // Only process public methods if ($method->isPublic()) { $this->processMethod($method); } } - + foreach($this->types as $type => $elements) { $this->wsdlDocument->addComplexType($type, $elements); } - + $this->wsdl = $this->wsdlDocument->getWsdl(); } - + /** * Static method that generates and outputs the generated wsdl * @param string $className The name of the class to export @@ -120,13 +120,13 @@ class WsdlGenerator public static function generate($className, $serviceUri='') { $generator = WsdlGenerator::getInstance(); - $generator->generateWsdl($className, $serviceUri); + $generator->generateWsdl($className, $serviceUri); //header('Content-type: text/xml'); return $generator->getWsdl(); //exit(); - + } - + /** * Process a method found in the passed in class. * @param ReflectionMethod $method The method to process @@ -134,7 +134,6 @@ class WsdlGenerator protected function processMethod(ReflectionMethod $method) { $comment = $method->getDocComment(); - if (strpos($comment, '@soapmethod') === false) { return; } @@ -147,25 +146,25 @@ class WsdlGenerator $comment = str_replace("\r", "", $comment); $comment = preg_replace("/([\\t])+/", "\t", $comment); $commentLines = explode("\n", $comment); - + $methodDoc = ''; $params = array(); $return = array(); $gotDesc = false; $gotParams = false; - + foreach ($commentLines as $line) { if ($line == '') continue; if ($line{0} == '@') { $gotDesc = true; if (preg_match('/^@param\s+([\w\[\]()]+)\s+\$([\w()]+)\s*(.*)/i', $line, $match)) { $param = array(); - $param['type'] = $this->convertType($match[1]); + $param['type'] = $this->convertType($match[1]); $param['name'] = $match[2]; - $param['desc'] = $match[3]; + $param['desc'] = $match[3]; $params[] = $param; } - else if (preg_match('/^@return\s+([\w\[\]()]+)\s*(.*)/i', $line, $match)) { + else if (preg_match('/^@return\s+([\w\[\]()]+)\s*(.*)/i', $line, $match)) { $gotParams = true; $return['type'] = $this->convertType($match[1]); $return['desc'] = $match[2]; @@ -185,19 +184,19 @@ class WsdlGenerator } } } - + $methodName = $method->getName(); $operation = new WsdlOperation($methodName, $methodDoc); - + $operation->setInputMessage(new WsdlMessage($methodName.'Request', $params)); $operation->setOutputMessage(new WsdlMessage($methodName.'Response', array($return))); - + $this->wsdlDocument->addOperation($operation); - + } - + /** - * Converts from a PHP type into a WSDL type. This is borrowed from + * Converts from a PHP type into a WSDL type. This is borrowed from * Cerebral Cortex (let me know and I'll remove asap). * * TODO: date and dateTime @@ -234,55 +233,55 @@ class WsdlGenerator break; case 'void': return ''; - default: + default: if(strpos($type, '[]')) // if it is an array - { + { $className = substr($type, 0, strlen($type) - 2); $type = $className . 'Array'; $this->types[$type] = ''; $this->convertType($className); - } - else + } + else { - if(!isset($this->types[$type])) + if(!isset($this->types[$type])) $this->extractClassProperties($type); - } - return 'tns:' . $type; + } + return 'tns:' . $type; } } - + /** * Extract the type and the name of all properties of the $className class and saves it in the $types array * This method extract properties from PHPDoc formatted comments for variables. Unfortunately the reflectionproperty * class doesn't have a getDocComment method to extract comments about it, so we have to extract the information * about the variables manually. Thanks heaps to Cristian Losada for implementing this. - * @param string $className The name of the class + * @param string $className The name of the class */ - private function extractClassProperties($className) + private function extractClassProperties($className) { // Lets get the class' file so we can read the full contents of it. $classReflect = new ReflectionClass($className); - + if (!file_exists($classReflect->getFileName())) { throw new Exception('Could not find class file for '.$className); } - + $file = file_get_contents($classReflect->getFileName()); if ($file == '') { throw new Exception("File {$classReflect->getFileName()} could not be opened"); } - + $pos = strpos($file, 'class ' . $className); $pos = strpos($file, '@var', $pos) + 5; - for($t = 0; $pos > 5; $t++) - { + for($t = 0; $pos > 5; $t++) + { $type = $this->convertType(substr($file, $pos, strpos($file, ' ', $pos) - $pos)); $pos = strpos($file, '$', $pos) + 1; - $name = substr($file, $pos, strpos($file, ';', $pos) - $pos); - $this->types[$className][$t] = array('type' => $type, 'name' => $name); + $name = substr($file, $pos, strpos($file, ';', $pos) - $pos); + $this->types[$className][$t] = array('type' => $type, 'name' => $name); $pos = strpos($file, '@var', $pos) + 5; } } - + } ?>
\ No newline at end of file diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index d176b5f7..28b0b1e7 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -365,4 +365,11 @@ dbdatareader_rewind_invalid = TDbDataReader is a forward-only stream. It can dbtransaction_transaction_inactive = TDbTransaction is inactive.
directorycachedependency_directory_invalid = TDirectoryCacheDependency.Directory {0} does not refer to a valid directory.
-cachedependencylist_cachedependency_required = Only objects implementing ICacheDependency can be added into TCacheDependencyList.
\ No newline at end of file +cachedependencylist_cachedependency_required = Only objects implementing ICacheDependency can be added into TCacheDependencyList.
+
+soapservice_configfile_invalid = TSoapService.ConfigFile '{0}' does not exist. Note, it has to be specified in a namespace format and the file extension must be '.xml'.
+soapservice_request_invalid = SOAP server '{0}' not found.
+soapservice_serverid_required = <soap> element must have 'id' attribute.
+soapservice_serverid_duplicated = SOAP server ID '{0}' is duplicated.
+soapserver_id_invalid = Invalid SOAP server ID '{0}'. It should not end with '.wsdl'.
+soapserver_version_invalid = Invalid SOAP version '{0}'. It must be either '1.1' or '1.2'.
\ No newline at end of file diff --git a/framework/Web/Services/TSoapService.php b/framework/Web/Services/TSoapService.php index a2db2e27..b1b04998 100644 --- a/framework/Web/Services/TSoapService.php +++ b/framework/Web/Services/TSoapService.php @@ -1,101 +1,532 @@ <?php /** - * TSoapService class file + * TSoapService and TSoapServer class file * * @author Knut Urdalen <knut.urdalen@gmail.com> - * @link http://www.pradosoft.com + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.pradosoft.com/ * @copyright Copyright © 2006 PradoSoft * @license http://www.pradosoft.com/license/ + * @version $Id: $ * @package System.Web.Services */ -//Prado::using('System.Web.Services.TWebService'); - -require_once dirname(__FILE__).'/../../3rdParty/WsdlGen/WsdlGenerator.php'; - /** * TSoapService class * - * TSoapService provides + * TSoapService processes SOAP requests for a PRADO application. + * TSoapService requires PHP SOAP extension to be loaded. + * + * TSoapService manages a set of SOAP providers. Each SOAP provider + * is a class that implements a set of SOAP methods which are exposed + * to SOAP clients for remote invocation. TSoapService generates WSDL + * automatically for the SOAP providers by default. + * + * To use TSoapService, configure it in the application specification like following: + * <code> + * <services> + * <service id="soap" class="System.Web.Services.TSoapService"> + * <soap id="stockquote" provider="MyStockQuote" /> + * </service> + * </services> + * </code> + * + * The above example specifies a single SOAP provider named "stockquote" + * whose class is "MyStockQuote". A SOAP client can then obtain the WSDL for + * this provider via the following URL: + * <code> + * http://hostname/path/to/index.php?soap=stockquote.wsdl + * </code> + * + * The WSDL for the provider class "MyStockQuote" is generated based on special + * comment tags in the class. In particular, if a class method's comment + * contains the keyword "@soapmethod", it is considered to be a SOAP method + * and will be exposed to SOAP clients. For example, + * <code> + * class MyStockQuote { + * / ** + * * @param string $symbol the stock symbol + * * @return float the stock price + * * @soapmethod + * * / + * public function getQuote($symbol) {...} + * } + * </code> + * + * With the above SOAP provider, a typical SOAP client may call the method "getQuote" + * remotely like the following: + * <code> + * $client=new SoapClient("http://hostname/path/to/index.php?soap=stockquote.wsdl"); + * echo $client->getQuote("ibm"); + * </code> + * + * Each <soap> element in the application specification actually configures + * the properties of a SOAP server which defaults to {@link TSoapServer}. + * Therefore, any writable property of {@link TSoapServer} may appear as an attribute + * in the <soap> element. For example, the "provider" attribute refers to + * the {@link TSoapServer::setProvider Provider} property of {@link TSoapServer}. + * The following configuration specifies that the SOAP server is persistent within + * the user session (that means a MyStockQuote object will be stored in session) + * <code> + * <services> + * <service id="soap" class="System.Web.Services.TSoapService"> + * <soap id="stockquote" provider="MyStockQuote" SessionPersistent="true" /> + * </service> + * </services> + * </code> + * + * You may also use your own SOAP server class by specifying the "class" attribute of <soap>. * * @author Knut Urdalen <knut.urdalen@gmail.com> + * @author Qiang Xue <qiang.xue@gmail.com> + * @package System.Web.Services + * @since 3.1 + */ +class TSoapService extends TService +{ + const DEFAULT_SOAP_SERVER='TSoapServer'; + const CONFIG_FILE_EXT='.xml'; + private $_servers=array(); + private $_configFile=null; + private $_wsdlRequest=false; + private $_serverID=null; + + /** + * Constructor. + * Sets default service ID to 'soap'. + */ + public function __construct() + { + $this->setID('soap'); + } + + /** + * Initializes this module. + * This method is required by the IModule interface. + * @param TXmlElement configuration for this module, can be null + * @throws TConfigurationException if {@link getConfigFile ConfigFile} is invalid. + */ + public function init($config) + { + if($this->_configFile!==null) + { + if(is_file($this->_configFile)) + { + $dom=new TXmlDocument; + $dom->loadFromFile($this->_configFile); + $this->loadConfig($dom); + } + else + throw new TConfigurationException('soapservice_configfile_invalid',$this->_configFile); + } + $this->loadConfig($config); + + $this->resolveRequest(); + } + + /** + * Resolves the request parameter. + * It identifies the server ID and whether the request is for WSDL. + * @throws THttpException if the server ID cannot be found + * @see getServerID + * @see getIsWsdlRequest + */ + protected function resolveRequest() + { + $serverID=$this->getRequest()->getServiceParameter(); + if(($pos=strrpos($serverID,'.wsdl'))===strlen($serverID)-5) + { + $serverID=substr($serverID,0,$pos); + $this->_wsdlRequest=true; + } + else + $this->_wsdlRequest=false; + $this->_serverID=$serverID; + if(!isset($this->_servers[$serverID])) + throw new THttpException(400,'soapservice_request_invalid',$serverID); + } + + /** + * Loads configuration from an XML element + * @param TXmlElement configuration node + * @throws TConfigurationException if soap server id is not specified or duplicated + */ + private function loadConfig($xml) + { + foreach($xml->getElementsByTagName('soap') as $serverXML) + { + $properties=$serverXML->getAttributes(); + if(($id=$properties->remove('id'))===null) + throw new TConfigurationException('soapservice_serverid_required'); + if(isset($this->_servers[$id])) + throw new TConfigurationException('soapservice_serverid_duplicated',$id); + $this->_servers[$id]=$properties; + } + } + + /** + * @return string external configuration file. Defaults to null. + */ + public function getConfigFile() + { + return $this->_configFile; + } + + /** + * @param string external configuration file in namespace format. The file + * must be suffixed with '.xml'. + * @throws TInvalidDataValueException if the file is invalid. + */ + public function setConfigFile($value) + { + if(($this->_configFile=Prado::getPathOfNamespace($value,self::CONFIG_FILE_EXT))===null) + throw new TConfigurationException('soapservice_configfile_invalid',$value); + } + + /** + * Constructs a URL with specified page path and GET parameters. + * @param string soap server ID + * @param array list of GET parameters, null if no GET parameters required + * @param boolean whether to encode the ampersand in URL, defaults to true. + * @param boolean whether to encode the GET parameters (their names and values), defaults to true. + * @return string URL for the page and GET parameters + */ + public function constructUrl($serverID,$getParams=null,$encodeAmpersand=true,$encodeGetItems=true) + { + return $this->getRequest()->constructUrl($this->getID(),$serverID,$getParams,$encodeAmpersand,$encodeGetItems); + } + + /** + * @return boolean whether this is a request for WSDL + */ + public function getIsWsdlRequest() + { + return $this->_wsdlRequest; + } + + /** + * @return string the SOAP server ID + */ + public function getServerID() + { + return $this->_serverID; + } + + /** + * Creates the requested SOAP server. + * The SOAP server is initialized with the property values specified + * in the configuration. + * @return TSoapServer the SOAP server instance + */ + protected function createServer() + { + $properties=$this->_servers[$this->_serverID]; + if(($serverClass=$properties->remove('class'))===null) + $serverClass=self::DEFAULT_SOAP_SERVER; + Prado::using($serverClass); + $className=($pos=strrpos($serverClass,'.'))!==false?substr($serverClass,$pos+1):$serverClass; + if($className!==self::DEFAULT_SOAP_SERVER && !is_subclass_of($className,self::DEFAULT_SOAP_SERVER)) + throw new TConfigurationException('soapservice_server_invalid',$serverClass); + $server=new $className; + $server->setID($this->_serverID); + foreach($properties as $name=>$value) + $server->setSubproperty($name,$value); + return $server; + } + + /** + * Runs the service. + * If the service parameter ends with '.wsdl', it will serve a WSDL file for + * the specified soap server. + * Otherwise, it will handle the soap request using the specified server. + */ + public function run() + { + Prado::trace("Running SOAP service",'System.Web.Services.TSoapService'); + $server=$this->createServer(); + if($this->getIsWsdlRequest()) + { + // server WSDL file + Prado::trace("Generating WSDL",'System.Web.Services.TSoapService'); + $this->getResponse()->setContentType('text/xml'); + $this->getResponse()->write($server->getWsdl()); + } + else + { + // provide SOAP service + Prado::trace("Handling SOAP request",'System.Web.Services.TSoapService'); + $server->run(); + } + } +} + + +/** + * TSoapServer class. + * + * TSoapServer is a wrapper of the PHP SoapServer class. + * It associates a SOAP provider class to the SoapServer object. + * It also manages the URI for the SOAP service and WSDL. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: $ * @package System.Web.Services * @since 3.1 */ -class TSoapService extends TService { - - private $_class; - - private $_server; // reference to the SOAP server - - /** - * Constructor. - * Sets default service ID to 'soap'. - */ - public function __construct() { - $this->setID('soap'); - } - - /** - * Initializes the service. - * This method is required by IService interface and is invoked by application. - * @param TXmlElement service configuration - */ - public function init($config) { - // nothing to do here - } - - /** - * Runs the service. - * - * This will serve a WSDL-file of the Soap server if 'wsdl' is provided as a key in - * the URL, else if will serve the Soap server. - */ - public function run() { - Prado::trace("Running SOAP service",'System.Web.Services.TSoapService'); - - $this->setSoapServer($this->getRequest()->getServiceParameter()); - Prado::using($this->getSoapServer()); // Load class - - // TODO: Fix protocol and host - $uri = 'http://'.$_SERVER['HTTP_HOST'].$this->getRequest()->getRequestUri(); - - //print_r($this->getRequest()); - if($this->getRequest()->itemAt('wsdl') !== null) { // Show WSDL-file - // TODO: Check WSDL cache - // Solution: Use Application Mode 'Debug' = no caching, 'Performance' = use cachez - $uri = str_replace('&wsdl', '', $uri); // throw away the 'wsdl' key (this is a bit dirty) - $uri = str_replace('wsdl', '', $uri); // throw away the 'wsdl' key (this is a bit dirty) - $wsdl = WsdlGenerator::generate($this->_class, $uri); - $this->getResponse()->setContentType('text/xml'); - $this->getResponse()->write($wsdl); - } else { // Provide service - // TODO: use TSoapServer - $this->_server = new SoapServer($uri.'&wsdl'); - $this->_server->setClass($this->getSoapServer()); - $this->_server->handle(); - } - } - - /** - * @return TSoapServer - */ - public function getSoapServer() { - return $this->_class; - } - - /** - * @param TSoapServer $class - */ - public function setSoapServer($class) { - // TODO: ensure $class instanceof TSoapServer - $this->_class = $class; - } - - public function getPersistence() { - - } - +class TSoapServer extends TApplicationComponent +{ + const WSDL_CACHE_PREFIX='wsdl.'; + + private $_id; + private $_provider; + + private $_version=''; + private $_actor=''; + private $_encoding=''; + private $_uri=''; + private $_classMap; + private $_persistent=false; + private $_wsdlUri=''; + + /** + * Constructor. + * It creates the classmap object. + */ + public function __construct() + { + $this->_classMap=new TAttributeCollection; + } + + /** + * @return string the ID of the SOAP server + */ + public function getID() + { + return $this->_id; + } + + /** + * @param string the ID of the SOAP server + * @throws TInvalidDataValueException if the ID ends with '.wsdl'. + */ + public function setID($id) + { + if(strrpos($this->_id,'.wsdl')===strlen($this->_id)-5) + throw new TInvalidDataValueException('soapserver_id_invalid',$id); + $this->_id=$id; + } + + /** + * Handles the SOAP request. + */ + public function run() + { + if(($provider=$this->getProvider())!==null) + { + Prado::using($provider); + $providerClass=($pos=strrpos($provider,'.'))!==false?substr($provider,$pos+1):$provider; + $server=$this->createServer(); + $server->setClass($providerClass); + if($this->_persistent) + $server->setPersistence(SOAP_PERSISTENCE_SESSION); + } + else + $server=$this->createServer(); + $server->handle(); + } + + /** + * Creates the SoapServer instance. + */ + protected function createServer() + { + return new SoapServer($this->getWsdlUri(),$this->getOptions()); + } + + /** + * @return array options for creating SoapServer instance + */ + protected function getOptions() + { + $options=array(); + if($this->_version==='1.1') + $options['soap_version']=SOAP_1_1; + else if($this->_version==='1.2') + $options['soap_version']=SOAP_1_2; + if(!empty($this->_actor)) + $options['actor']=$this->_actor; + if(!empty($this->_encoding)) + $options['encoding']=$this->_encoding; + if(!empty($this->_uri)) + $options['uri']=$this->_uri; + if($this->_classMap->getCount()>0) + $options['classmap']=$this->_classMap->toArray(); + return $options; + } + + /** + * Returns the WSDL content of the SOAP server. + * If {@link getWsdlUri WsdlUri} is set, its content will be returned. + * If not, the {@link setProvider Provider} class will be investigated + * and the WSDL will be automatically genearted. + * @return string the WSDL content of the SOAP server + */ + public function getWsdl() + { + if($this->_wsdlUri==='') + { + $provider=$this->getProvider(); + $providerClass=($pos=strrpos($provider,'.'))!==false?substr($provider,$pos+1):$provider; + Prado::using($provider); + if($this->getApplication()->getMode()===TApplicationMode::Performance && ($cache=$this->getApplication()->getCache())!==null) + { + $wsdl=$cache->get(self::WSDL_CACHE_PREFIX.$providerClass); + if(is_string($wsdl)) + return $wsdl; + Prado::using('System.3rdParty.WsdlGen.WsdlGenerator'); + $wsdl=WsdlGenerator::generate($providerClass, $this->getUri()); + $cache->set(self::WSDL_CACHE_PREFIX.$providerClass,$wsdl); + return $wsdl; + } + else + { + Prado::using('System.3rdParty.WsdlGen.WsdlGenerator'); + return WsdlGenerator::generate($providerClass, $this->getUri()); + } + } + else + return file_get_contents($this->_wsdlUri); + } + + /** + * @return string the URI for WSDL + */ + public function getWsdlUri() + { + if($this->_wsdlUri==='') + return $this->getRequest()->getBaseUrl().$this->getService()->constructUrl($this->getID().'.wsdl',false); + else + return $this->_wsdlUri; + } + + /** + * @param string the URI for WSDL + */ + public function setWsdlUri($value) + { + $this->_wsdlUri=$value; + } + + /** + * @return string the URI for the SOAP service + */ + public function getUri() + { + if($this->_uri==='') + return $this->getRequest()->getBaseUrl().$this->getService()->constructUrl($this->getID(),false); + else + return $this->_uri; + } + + /** + * @param string the URI for the SOAP service + */ + public function setUri($uri) + { + $this->_uri=$uri; + } + + /** + * @return string the SOAP provider class (in namespace format) + */ + public function getProvider() + { + return $this->_provider; + } + + /** + * @param string the SOAP provider class (in namespace format) + */ + public function setProvider($provider) + { + $this->_provider=$provider; + } + + /** + * @return string SOAP version, defaults to empty (meaning not set). + */ + public function getVersion() + { + return $this->_version; + } + + /** + * @param string SOAP version, either '1.1' or '1.2' + * @throws TInvalidDataValueException if neither '1.1' nor '1.2' + */ + public function setVersion($value) + { + if($value==='1.1' || $value==='1.2' || $value==='') + $this->_version=$value; + else + throw new TInvalidDataValueException('soapserver_version_invalid',$value); + } + + /** + * @return string actor of the SOAP service + */ + public function getActor() + { + return $this->_actor; + } + + /** + * @param string actor of the SOAP service + */ + public function setActor($value) + { + $this->_actor=$value; + } + + /** + * @return string encoding of the SOAP service + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * @param string encoding of the SOAP service + */ + public function setEncoding($value) + { + $this->_encoding=$value; + } + + /** + * @return boolean whether the SOAP service is persistent within session. Defaults to false. + */ + public function getSessionPersistent() + { + return $this->_persistent; + } + + /** + * @param boolean whether the SOAP service is persistent within session. + */ + public function setSessionPersistent($value) + { + $this->_persistent=TPropertyValue::ensureBoolean($value); + } + + /** + * @return TAttributeCollection the class map for the SOAP service + */ + public function getClassMap() + { + return $this->_classMap; + } } ?>
\ No newline at end of file |