. */ if(basename($_SERVER['SCRIPT_FILENAME'])==basename(__FILE__)) exit; // To use PhpWsdlClient, you need to load it seperatly from PhpWsdl. Or you // just load this class and let it autoload PhpWsdl require_once(dirname(__FILE__).'/class.phpwsdl.php'); /** * A SOAP client class for PhpWsdl * * @author Andreas Zimmermann, wan24.de * @version 2.2 */ class PhpWsdlClient{ /** * The version number * * @var string */ public static $VERSION='2.2'; /** * Parse a method * 1: Type * 2: Method * 3: Parameters * * @var string */ public static $methodRx='/^([^\s]+)\s+([^\(]+)\(([^\)]+)?\).*$/'; /** * Parse a type * 1: Type * 2: Name * * @var string */ public static $typeRx='/^([^\s]+)\s([^\[|\{|\s]+)[\[|\{|\s].*$/s'; /** * Parse an element * 1: Type * 2: Name * * @var string */ public static $elementRx='/^\s*([^\s]+)\s+([^\s|;]+);.*$/'; /** * The WSDL URI * * @var string */ public $WsdlUri; /** * The PHP SoapClient object * * @var SoapClient */ public $Client=null; /** * SOAP call options * * @var array */ public $Options; /** * SOAP call request headers * * @var array */ public $RequestHeaders; /** * PHP SoapClient options array * * @var array */ public $ClientOptions; /** * The webservice name * * @var string */ public $ServiceName=null; /** * The SOAP endpoint * * @var string */ public $EndPoint=null; /** * The SOAP namespace * * @var string */ public $NameSpace=null; /** * A PhpWsdl object * * @var PhpWsdl */ public $Server=null; /** * The WSDL * * @var string */ public $WSDL=null; /** * A list of warnings * * @var string[] */ public $Warnings=Array(); /** * The http Auth username for fetching the WSDL (and sending SOAP requests, if PhpWsdlClient->UseSoapHttpAuth is TRUE) * If you need a different login for SOAP requests, use the PhpWsdlClient->ClientOptions array fro the SOAP request * authentification values (login/password) * * @var string */ public $HttpUser=null; /** * The http Auth password * * @var string */ public $HttpPassword=null; /** * Use http Auth for SOAP requests, too? If FALSE, the login will only be used to fetch the WSDL from the given URI * * @var boolean */ public $UseSoapHttpAuth=true; /** * Enable debugging of the RAW request/response? * * @var boolean */ public $Debugging=false; /** * Constructor * * @param string $wsdlUri The WSDL URI * @param array $options SOAP call options (default: NULL) * @param array $requestHeaders SOAP call request headers (default: NULL) */ public function PhpWsdlClient($wsdlUri,$options=null,$requestHeaders=null,$clientOptions=Array()){ self::__construct($wsdlUri, $options, $requestHeaders, $clientOptions); } public function __construct($wsdlUri,$options=null,$requestHeaders=null,$clientOptions=Array()){ PhpWsdl::Debug('New PhpWsdlClient '.$wsdlUri); $this->WsdlUri=$wsdlUri; $this->Options=$options; $this->RequestHeaders=$requestHeaders; $this->ClientOptions=array_merge( Array( 'soap_version' => SOAP_1_1|SOAP_1_2, 'encoding' => 'UTF-8', 'compression' => SOAP_COMPRESSION_ACCEPT|SOAP_COMPRESSION_GZIP|9, 'connection_timeout'=>5 ), $clientOptions ); $this->GetWsdlFromCache(); } /** * Fetch the WSDL (supports http Auth if CURL is enabled) * * @param string $wsdlUri The WSDL URI or NULL to use the PhpWsdlClient->WaslUri property (default: NULL) * @param array $options The CURL options (default: array) * @return string The WSDL XML */ public function FetchWsdl($wsdlUri=null,$options=Array()){ if(is_null($wsdlUri)) $wsdlUri=$this->WsdlUri; PhpWsdl::Debug('Fetch WSDL from '.$wsdlUri); if(!is_file($wsdlUri)&&function_exists('curl_init')&&!is_null($this->HttpUser)){ // Fetch with http Auth (credits to faebu :) PhpWsdl::Debug('Try CURL for http Auth'); $ch=curl_init(); $credit=($this->HttpUser.':'.$this->HttpPassword); curl_setopt_array($ch,array_merge( Array( CURLOPT_URL => $wsdlUri, CURLOPT_HTTPAUTH => CURLAUTH_BASIC, CURLOPT_USERPWD => $credit, CURLOPT_TIMEOUT => 10, CURLOPT_RETURNTRANSFER => 1 ), $options )); $wsdl=curl_exec($ch); if($wsdl===false){ PhpWsdl::Debug('Could not fetch WSDL with CURL: '.curl_error($ch)); @curl_close($ch); }else{ curl_close($ch); return $wsdl; } } // Fetch without authentification return file_get_contents($wsdlUri); } /** * Do a SOAP request * * @param string $method The method name * @param array $param The method parameters * @param array $options The call options (default: array) * @param array $requestHeaders The request headers (default: array) * @return mixed The server response */ public function DoRequest($method,$param,$options=Array(),$requestHeaders=Array()){ PhpWsdl::Debug('Sending request '.$method); $options=array_merge($this->Options,$options); $requestHeaders=array_merge($this->RequestHeaders,$requestHeaders); $client=$this->GetClient(); $res=$client->__soapCall($method,$param,$options,$requestHeaders); if(is_soap_fault($res)) PhpWsdl::Debug('SOAP error #'.$res->faultcode.': '.$res->faultstring); if($this->Debugging){ PhpWsdl::Debug('Parameters: '.print_r($param,true)); PhpWsdl::Debug('Options: '.print_r($options,true)); PhpWsdl::Debug('Headers: '.print_r($requestHeaders,true)); PhpWsdl::Debug('Result: '.print_r($res,true)); PhpWsdl::Debug('Request: '.$client->__getLastRequest()); PhpWsdl::Debug('Request headers: '.$client->__getLastRequestHeaders()); PhpWsdl::Debug('Response: '.$client->__getLastResponse()); PhpWsdl::Debug('Response headers: '.$client->__getLastResponseHeaders()); } return $res; } /** * Get the PHP SoapClient object * * @param string $uri The WSDL URI or NULL to use the PhpWsdlClient->WsdlUri property (default: NULL) * @param array $options The additional PHP SoapClient options (default: array) * @return SoapClient The PHP SoapClient object */ public function GetClient($uri=null,$options=Array()){ if(is_null($this->Client)){ if(is_null($uri)) $uri=$this->WsdlUri; PhpWsdl::Debug('Create a PHP SoapClient object in PhpWsdlClient from '.$uri); if(!is_null(PhpWsdl::$CacheFolder)){ $wsdlFile=$this->GetCacheFileName($uri); if(!is_null($this->GetWsdlFromCache($wsdlFile))){ PhpWsdl::Debug('Use cached WSDL from '.$wsdlFile); $uri=$wsdlFile; }else{ $wsdl=$this->FetchWsdl($uri); if($wsdl!==false) if($this->WriteWsdlToCache($wsdl,$uri,$wsdlFile,true)){ PhpWsdl::Debug('Using cached WSDL from '.$wsdlFile); $uri=$wsdlFile; } } } if( !is_null($this->HttpUser)&& !isset($this->ClientOptions['login'])&& !isset($options['login'])&& $this->UseSoapHttpAuth ){ PhpWsdl::Debug('Using http Auth options'); $options['login']=$this->HttpUser; $options['password']=$this->HttpPassword; } if($this->Debugging){ PhpWsdl::Debug('Debugging enabled'); $options['trace']=true; } $this->Client=new SoapClient($uri,array_merge( $this->ClientOptions, $options )); } return $this->Client; } /** * Create/Fill a PhpWsdl object from WSDL * * @param PhpWsdl An existing PhpWsdl object (default: NULL) * @return PhpWsdl The PhpWsdl object */ public function CreateServerFromWsdl($soap=null){ PhpWsdl::Debug('Create/Fill a PhpWsdl object from WSDL'); if(is_null($soap)){ if(!is_null($this->Server)){ PhpWsdl::Debug('Return existing object'); return $this->Server; } $soap=PhpWsdl::CreateInstance(); }else{ PhpWsdl::Debug('Use existing object'); } if(!is_null($soap->GetWsdlFromCache())){ PhpWsdl::Debug('Server created from cached values'); if(is_null($this->Server)) $this->Server=$soap; return $soap; } // Configuration $soap->WsdlUri=$this->WsdlUri; $this->ParseWsdl(); if(!is_null($this->ServiceName)) $soap->Name=$this->ServiceName; if(!is_null($this->NameSpace)) $soap->NameSpace=$this->NameSpace; $client=$this->GetClient(); // Methods $fnc=$client->__getFunctions(); $i=-1; $len=sizeof($fnc); while(++$i<$len){ $f=$fnc[$i]; list( $type, $method, $temp )=explode("\t",preg_replace(self::$methodRx,"$1\t$2\t$3",$f)); PhpWsdl::Debug('Found method #'.$i.' '.$method); if(!is_null($soap->GetMethod($method))){ $this->Warn('WARNING: Double method detected!'); continue; } $m=new PhpWsdlMethod($method); $temp=explode(' ',$temp); $pLen=sizeof($temp); for($j=0;$j<$pLen-1;$j++){ list( $t, $n )=Array( $temp[$j], $temp[$j+1] ); PhpWsdl::Debug('Found parameter #'.$j.' '.$n.' type of '.$t); $m->Param[]=new PhpWsdlParam(substr($n,1),$t); } if($type!='void') $m->Return=new PhpWsdlParam('return',$type); $soap->Methods[]=$m; } // Types $typ=$client->__getTypes(); $i=-1; $len=sizeof($typ); while(++$i<$len){ $t=$typ[$i]; list( $type, $name )=explode("\t",preg_replace(self::$typeRx,"$1\t$2",$t)); PhpWsdl::Debug('Found type #'.$i.' '.$name.' type of '.$type); if(!is_null($soap->GetType($name))){ $this->Warn('WARNING: Double type detected!'); continue; } $arr=strpos($t,'[]')>-1; if($arr){ PhpWsdl::Debug('Array type'); $y=new PhpWsdlComplex($name); $y->Type=$type; $y->IsArray=true; $soap->Types[]=$y; }else if($type=='struct'){ PhpWsdl::Debug('Complex type'); $el=Array(); $temp=explode("\n",$t); $j=0; $eLen=sizeof($temp)-1; while(++$j<$eLen){ list( $p, $n )=explode("\t",preg_replace(self::$elementRx,"$1\t$2",$temp[$j]),2); PhpWsdl::Debug('Found element #'.$j.' '.$n.' type of '.$p); $el[]=new PhpWsdlElement($n,$p); } $y=new PhpWsdlComplex($name,$el); $y->IsArray=false; $soap->Types[]=$y; }else{ $this->Warn('WARNING: Could not create type '.$t); } } if(is_null($this->Server)) $this->Server=$soap; return $soap; } /** * Parse the WSDL for webservice name, namespace and endpoint URI */ public function ParseWsdl(){ if(!is_null($this->ServiceName)&&!is_null($this->EndPoint)&&!is_null($this->NameSpace)) return; PhpWsdl::Debug('Parse WSDL'); $wsdl=$this->WSDL; $writeCache=false; if(is_null($wsdl)){ $wsdl=$this->FetchWsdl(); if($wsdl===false) throw(new Exception('Could not fetch WSDL')); $writeCache=true; } $xml=new DOMDocument(); if(!$xml->loadXML($wsdl)) throw(new Exception('Invalid WSDL')); $x=new DOMXPath($xml); // Namespace $temp=$x->query("/*[local-name()='definitions']/@targetNamespace"); if($temp->length>0){ $temp=$temp->item(0); PhpWsdl::Debug('Namespace '.$temp->value); $this->NameSpace=$temp->value; }else{ $this->Warn('WARNING: No namespace found'); } // Webservice name $temp=$x->query("/*[local-name()='definitions']/*[local-name()='service']/@name"); if($temp->length>0){ $temp=$temp->item(0); PhpWsdl::Debug('Name '.$temp->value); $this->ServiceName=$temp->value; }else{ $this->Warn('WARNING: No name found'); } // Endpoint URI $temp=$x->query("/*[local-name()='definitions']/*[local-name()='service']/*[local-name()='port']/*[local-name()='address']/@location"); if($temp->length>0){ $temp=$temp->item(0); PhpWsdl::Debug('Endpoint '.$temp->value); $this->EndPoint=$temp->value; }else{ $this->Warn('WARNING: No endpoint found'); } // Caching $this->WriteWsdlToCache($wsdl,null,null,$writeCache); } /** * Create a PHP SOAP client for this webservice * * @param string $filename Name of the file to save the PHP code (default: NULL) * @param array $options The options array for the PhpWsdl->OutputPhp method (default: array) * @return string The UTF-8 encoded PHP code of the SOAP client */ public function CreatePhpSoapClient($filename=null,$options=Array()){ $php=$this->CreateServerFromWsdl()->OutputPhp(false,false,$options); if(!is_null($filename)) if(file_put_contents($filename,$php)===false) PhpWsdl::Debug('Could not write file '.$filename); return $php; } /** * Add a warning message * * @param string $str The message */ private function Warn($str){ $this->Warnings[]=$str; PhpWsdl::Debug($str); } /** * Get the cache filename * * @param string $wsdluri The WSDL URI or NULL to use the PhpWsdlAjax->WsdlUri property (default: NULL) * @return string The cache filename or NULL, if caching is disabled */ public function GetCacheFileName($wsdluri=null){ $data=Array( 'client' => $this, 'wsdluri' => $wsdluri, 'filename' => (is_null(PhpWsdl::$CacheFolder))?null:PhpWsdl::$CacheFolder.'/client-'.sha1((is_null($wsdluri))?$this->WsdlUri:$wsdluri).'.wsdl' ); PhpWsdl::CallHook( 'ClientCacheFileNameHook', $data ); return $data['filename']; } /** * Determine if the cache file exists * * @param string $file The WSDL cache filename or NULL to use the default (default: NULL) * @return boolean Are the cache files present? */ public function CacheFileExists($file=null){ if(is_null($file)) $file=$this->GetCacheFileName(); PhpWsdl::Debug('Check cache file exists '.$file); return file_exists($file)&&file_exists($file.'.cache'); } /** * Determine if the existing cache files are still valid * * @param string $file The WSDL cache filename or NULL to use the default (default: NULL) * @return boolean Valid? */ public function IsCacheValid($file=null){ PhpWsdl::Debug('Check cache valid'); if(is_null($file)) $file=$this->GetCacheFileName(); if(!$this->CacheFileExists($file)) return false; return PhpWsdl::$CacheTime<0||time()-file_get_contents($file.'.cache')<=PhpWsdl::$CacheTime; } /** * Get the WSDL from the cache * * @param string $file The WSDL cache filename or NULL to use the default (default: NULL) * @param boolean $force Force this even if the cache is timed out? (default: FALSE) * @param boolean $nounserialize Don't unserialize the PhpWsdl* objects? (default: FALSE) * @return string The cached WSDL */ public function GetWsdlFromCache($file=null,$force=false,$nounserialize=false){ PhpWsdl::Debug('Get WSDL from cache'); if(!is_null($this->WSDL)) return $this->WSDL; if(is_null($file)) $file=$this->GetCacheFileName(); if(!$force){ if(!$this->IsCacheValid($file)) return null; }else if(!$this->CacheFileExists($file)){ return null; } $this->WSDL=file_get_contents($file); if(!$nounserialize){ PhpWsdl::Debug('Unserialize data'); $data=unserialize(file_get_contents($file.'.obj')); $this->ServiceName=$data['servicename']; $this->EndPoint=$data['endpoint']; $this->NameSpace=$data['namespace']; PhpWsdl::CallHook( 'ClientReadCacheHook', Array( 'client' => $this, 'data' => &$data ) ); if($data['version']!=self::$VERSION){ PhpWsdl::Debug('Could not use cache from version '.$data['version']); $this->ServiceName=null; $this->EndPoint=null; $this->NameSpace=null; $this->WSDL=null; return null; } } return $this->WSDL; } /** * Write WSDL to cache * * @param string $wsdl The UTF-8 encoded WSDL string (default: NULL) * @param string $wsdluri The SOAP WSDL URI or NULL to use the default (default: NULL) * @param string $file The target filename or NULL to use the default (default: NULL) * @param boolean $force Force refresh (default: FALSE) * @return boolean Succeed? */ public function WriteWsdlToCache($wsdl=null,$wsdluri=null,$file=null,$force=false){ PhpWsdl::Debug('Write WSDL to the cache'); if(is_null($wsdluri)) $wsdluri=$this->WsdlUri; if($wsdluri==$this->WsdlUri&&!is_null($wsdl)) $this->WSDL=$wsdl; if(is_null($wsdl)){ if(is_null($this->WSDL)){ PhpWsdl::Debug('No WSDL'); return false;// WSDL not defined } $wsdl=$this->WSDL; } if(is_null($file)){ $file=$this->GetCacheFileName($wsdluri); if(is_null($file)){ PhpWsdl::Debug('No cache file'); return false;// No cache file } } $temp=substr($file,0,1); if($temp!='/'&&$temp!='.'){ if(is_null(PhpWsdl::$CacheFolder)){ PhpWsdl::Debug('No cache folder'); return false;// No cache folder } $file=PhpWsdl::$CacheFolder.'/'.$file; } if(!$force) if($this->IsCacheValid($file)){ PhpWsdl::Debug('Cache is still valid'); return true;// Existing cache is still valid } PhpWsdl::Debug('Write to '.$file); if(file_put_contents($file,$wsdl)===false){ PhpWsdl::Debug('Could not write to cache'); return false;// Error writing to cache } if(file_put_contents($file.'.cache',time())===false){ PhpWsdl::Debug('Could not write cache time file'); return false;// Error writing to cache } $data=Array( 'version' => self::$VERSION, 'servicename' => $this->ServiceName, 'endpoint' => $this->EndPoint, 'namespace' => $this->NameSpace ); PhpWsdl::CallHook( 'ClientWriteCacheHook', Array( 'client' => $this, 'data' => &$data ) ); if(file_put_contents($file.'.obj',serialize($data))===false){ PhpWsdl::Debug('Could not write serialized cache'); return false; } return true; } /** * SOAP method call proxy method * * @param string $method The method name * @param array $param The method parameters * @return mixed The server response */ public function __call($method,$param){ return $this->DoRequest($method,$param); } }