Merge pull request #3 from aschamberger/master

Basic support for MTOM/SwA, WS-Adressing and WS-Security
This commit is contained in:
Christian Kerl 2012-01-08 11:15:28 -08:00
commit 23ba025ffe
43 changed files with 3821 additions and 302 deletions

4
.gitignore vendored
View File

@ -1 +1,5 @@
vendor vendor
/phpunit.xml
.buildpath
.project
.settings

View File

@ -0,0 +1,313 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
/**
* cURL wrapper class for doing HTTP requests that uses the soap class options.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class Curl
{
/**
* HTTP User Agent.
*
* @var string
*/
const USER_AGENT = 'PHP-SOAP/\BeSimple\SoapClient';
/**
* Curl resource.
*
* @var resource
*/
private $ch;
/**
* Maximum number of location headers to follow.
*
* @var int
*/
private $followLocationMaxRedirects;
/**
* Request response data.
*
* @var string
*/
private $response;
/**
* Constructor.
*
* @param array $options Options array from SoapClient constructor
* @param int $followLocationMaxRedirects Redirection limit for Location header
*/
public function __construct(array $options = array(), $followLocationMaxRedirects = 10)
{
// set the default HTTP user agent
if (!isset($options['user_agent'])) {
$options['user_agent'] = self::USER_AGENT;
}
$this->followLocationMaxRedirects = $followLocationMaxRedirects;
// make http request
$this->ch = curl_init();
$curlOptions = array(
CURLOPT_ENCODING => '',
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_FAILONERROR => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_HEADER => true,
CURLOPT_USERAGENT => $options['user_agent'],
CURLINFO_HEADER_OUT => true,
);
curl_setopt_array($this->ch, $curlOptions);
if (isset($options['compression']) && !($options['compression'] & SOAP_COMPRESSION_ACCEPT)) {
curl_setopt($this->ch, CURLOPT_ENCODING, 'identity');
}
if (isset($options['connection_timeout'])) {
curl_setopt($this->ch, CURLOPT_CONNECTTIMEOUT, $options['connection_timeout']);
}
if (isset($options['proxy_host'])) {
$port = isset($options['proxy_port']) ? $options['proxy_port'] : 8080;
curl_setopt($this->ch, CURLOPT_PROXY, $options['proxy_host'] . ':' . $port);
}
if (isset($options['proxy_user'])) {
curl_setopt($this->ch, CURLOPT_PROXYUSERPWD, $options['proxy_user'] . ':' . $options['proxy_password']);
}
if (isset($options['login'])) {
curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
curl_setopt($this->ch, CURLOPT_USERPWD, $options['login'].':'.$options['password']);
}
if (isset($options['local_cert'])) {
curl_setopt($this->ch, CURLOPT_SSLCERT, $options['local_cert']);
curl_setopt($this->ch, CURLOPT_SSLCERTPASSWD, $options['passphrase']);
}
}
/**
* Destructor.
*/
public function __destruct()
{
curl_close($this->ch);
}
/**
* Execute HTTP request.
* Returns true if request was successfull.
*
* @param string $location HTTP location
* @param string $request Request body
* @param array $requestHeaders Request header strings
*
* @return bool
*/
public function exec($location, $request = null, $requestHeaders = array())
{
curl_setopt($this->ch, CURLOPT_URL, $location);
if (!is_null($request)) {
curl_setopt($this->ch, CURLOPT_POST, true);
curl_setopt($this->ch, CURLOPT_POSTFIELDS, $request);
}
if (count($requestHeaders) > 0) {
curl_setopt($this->ch, CURLOPT_HTTPHEADER, $requestHeaders);
}
$this->response = $this->execManualRedirect($this->followLocationMaxRedirects);
return ($this->response === false) ? false : true;
}
/**
* Custom curl_exec wrapper that allows to follow redirects when specific
* http response code is set. SOAP only allows 307.
*
* @param int $redirects Current redirection count
*
* @return mixed
*/
private function execManualRedirect($redirects = 0)
{
if ($redirects > $this->followLocationMaxRedirects) {
// TODO Redirection limit reached, aborting
return false;
}
curl_setopt($this->ch, CURLOPT_HEADER, true);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($this->ch);
$httpResponseCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
if ($httpResponseCode == 307) {
$headerSize = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE);
$header = substr($response, 0, $headerSize);
$matches = array();
preg_match('/Location:(.*?)\n/', $header, $matches);
$url = trim(array_pop($matches));
// @parse_url to suppress E_WARNING for invalid urls
if (($url = @parse_url($url)) !== false) {
$lastUrl = parse_url(curl_getinfo($this->ch, CURLINFO_EFFECTIVE_URL));
if (!isset($url['scheme'])) {
$url['scheme'] = $lastUrl['scheme'];
}
if (!isset($url['host'])) {
$url['host'] = $lastUrl['host'];
}
if (!isset($url['path'])) {
$url['path'] = $lastUrl['path'];
}
$newUrl = $url['scheme'] . '://' . $url['host'] . $url['path'] . ($url['query'] ? '?' . $url['query'] : '');
curl_setopt($this->ch, CURLOPT_URL, $newUrl);
return $this->execManualRedirect($redirects++);
}
}
return $response;
}
/**
* Error code mapping from cURL error codes to PHP ext/soap error messages
* (where applicable)
*
* http://curl.haxx.se/libcurl/c/libcurl-errors.html
*
* @return array(int=>string)
*/
protected function getErrorCodeMapping()
{
return array(
1 => 'Unknown protocol. Only http and https are allowed.', //CURLE_UNSUPPORTED_PROTOCOL
3 => 'Unable to parse URL', //CURLE_URL_MALFORMAT
5 => 'Could not connect to host', //CURLE_COULDNT_RESOLVE_PROXY
6 => 'Could not connect to host', //CURLE_COULDNT_RESOLVE_HOST
7 => 'Could not connect to host', //CURLE_COULDNT_CONNECT
9 => 'Could not connect to host', //CURLE_REMOTE_ACCESS_DENIED
28 => 'Failed Sending HTTP SOAP request', //CURLE_OPERATION_TIMEDOUT
35 => 'Could not connect to host', //CURLE_SSL_CONNECT_ERROR
41 => 'Can\'t uncompress compressed response', //CURLE_FUNCTION_NOT_FOUND
51 => 'Could not connect to host', //CURLE_PEER_FAILED_VERIFICATION
52 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_GOT_NOTHING
53 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_NOTFOUND
54 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_SETFAILED
55 => 'Failed Sending HTTP SOAP request', //CURLE_SEND_ERROR
56 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_RECV_ERROR
58 => 'Could not connect to host', //CURLE_SSL_CERTPROBLEM
59 => 'Could not connect to host', //CURLE_SSL_CIPHER
60 => 'Could not connect to host', //CURLE_SSL_CACERT
61 => 'Unknown Content-Encoding', //CURLE_BAD_CONTENT_ENCODING
65 => 'Failed Sending HTTP SOAP request', //CURLE_SEND_FAIL_REWIND
66 => 'SSL support is not available in this build', //CURLE_SSL_ENGINE_INITFAILED
67 => 'Could not connect to host', //CURLE_LOGIN_DENIED
77 => 'Could not connect to host', //CURLE_SSL_CACERT_BADFILE
80 => 'Error Fetching http body, No Content-Length, connection closed or chunked data', //CURLE_SSL_SHUTDOWN_FAILED
);
}
/**
* Gets the curl error message.
*
* @return string
*/
public function getErrorMessage()
{
$errorCodeMapping = $this->getErrorCodeMapping();
$errorNumber = curl_errno($this->ch);
if (isset($errorCodeMapping[$errorNumber])) {
return $errorCodeMapping[$errorNumber];
}
return curl_error($this->ch);
}
/**
* Gets the request headers as a string.
*
* @return string
*/
public function getRequestHeaders()
{
return curl_getinfo($this->ch, CURLINFO_HEADER_OUT);
}
/**
* Gets the whole response (including headers) as a string.
*
* @return string
*/
public function getResponse()
{
return $this->response;
}
/**
* Gets the response body as a string.
*
* @return string
*/
public function getResponseBody()
{
$headerSize = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE);
return substr($this->response, $headerSize);
}
/**
* Gets the response content type.
*
* @return string
*/
public function getResponseContentType()
{
return curl_getinfo($this->ch, CURLINFO_CONTENT_TYPE);
}
/**
* Gets the response headers as a string.
*
* @return string
*/
public function getResponseHeaders()
{
$headerSize = curl_getinfo($this->ch, CURLINFO_HEADER_SIZE);
return substr($this->response, 0, $headerSize);
}
/**
* Gets the response http status code.
*
* @return string
*/
public function getResponseStatusCode()
{
return curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
}
/**
* Gets the response http status message.
*
* @return string
*/
public function getResponseStatusMessage()
{
preg_match('/HTTP\/(1\.[0-1]+) ([0-9]{3}) (.*)/', $this->response, $matches);
return trim(array_pop($matches));
}
}

View File

@ -0,0 +1,138 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\Mime\MultiPart as MimeMultiPart;
use BeSimple\SoapCommon\Mime\Parser as MimeParser;
use BeSimple\SoapCommon\Mime\Part as MimePart;
use BeSimple\SoapCommon\SoapRequest;
use BeSimple\SoapCommon\SoapRequestFilter;
use BeSimple\SoapCommon\SoapResponse;
use BeSimple\SoapCommon\SoapResponseFilter;
/**
* MIME filter.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class MimeFilter implements SoapRequestFilter, SoapResponseFilter
{
/**
* Attachment type.
*
* @var int Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM
*/
protected $attachmentType = Helper::ATTACHMENTS_TYPE_SWA;
/**
* Constructor.
*
* @param int $attachmentType Helper::ATTACHMENTS_TYPE_SWA | Helper::ATTACHMENTS_TYPE_MTOM
*/
public function __construct($attachmentType)
{
$this->attachmentType = $attachmentType;
}
/**
* Reset all properties to default values.
*/
public function resetFilter()
{
$this->attachmentType = Helper::ATTACHMENTS_TYPE_SWA;
}
/**
* Modify the given request XML.
*
* @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
*
* @return void
*/
public function filterRequest(SoapRequest $request)
{
// get attachments from request object
$attachmentsToSend = $request->getAttachments();
// build mime message if we have attachments
if (count($attachmentsToSend) > 0) {
$multipart = new MimeMultiPart();
$soapPart = new MimePart($request->getContent(), 'text/xml', 'utf-8', MimePart::ENCODING_EIGHT_BIT);
$soapVersion = $request->getVersion();
// change content type headers for MTOM with SOAP 1.1
if ($soapVersion == SOAP_1_1 && $this->attachmentType & Helper::ATTACHMENTS_TYPE_MTOM) {
$multipart->setHeader('Content-Type', 'type', 'application/xop+xml');
$multipart->setHeader('Content-Type', 'start-info', 'text/xml');
$soapPart->setHeader('Content-Type', 'application/xop+xml');
$soapPart->setHeader('Content-Type', 'type', 'text/xml');
}
// change content type headers for SOAP 1.2
elseif ($soapVersion == SOAP_1_2) {
$multipart->setHeader('Content-Type', 'type', 'application/soap+xml');
$soapPart->setHeader('Content-Type', 'application/soap+xml');
}
$multipart->addPart($soapPart, true);
foreach ($attachmentsToSend as $cid => $attachment) {
$multipart->addPart($attachment, false);
}
$request->setContent($multipart->getMimeMessage());
// TODO
$headers = $multipart->getHeadersForHttp();
list($name, $contentType) = explode(': ', $headers[0]);
$request->setContentType($contentType);
}
}
/**
* Modify the given response XML.
*
* @param \BeSimple\SoapCommon\SoapResponse $response SOAP response
*
* @return void
*/
public function filterResponse(SoapResponse $response)
{
// array to store attachments
$attachmentsRecieved = array();
// check content type if it is a multipart mime message
$responseContentType = $response->getContentType();
if (false !== stripos($responseContentType, 'multipart/related')) {
// parse mime message
$headers = array(
'Content-Type' => trim($responseContentType),
);
$multipart = MimeParser::parseMimeMessage($response->getContent(), $headers);
// get soap payload and update SoapResponse object
$soapPart = $multipart->getPart();
// convert href -> myhref for external references as PHP throws exception in this case
// http://svn.php.net/viewvc/php/php-src/branches/PHP_5_4/ext/soap/php_encoding.c?view=markup#l3436
$content = preg_replace('/href=(?!#)/', 'myhref=', $soapPart->getContent());
$response->setContent($content);
$response->setContentType($soapPart->getHeader('Content-Type'));
// store attachments
$attachments = $multipart->getParts(false);
foreach ($attachments as $cid => $attachment) {
$attachmentsRecieved[$cid] = $attachment;
}
}
// add attachments to response object
if (count($attachmentsRecieved) > 0) {
$response->setAttachments($attachmentsRecieved);
}
}
}

View File

@ -12,9 +12,322 @@
namespace BeSimple\SoapClient; namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\SoapKernel;
use BeSimple\SoapCommon\Converter\MtomTypeConverter;
use BeSimple\SoapCommon\Converter\SwaTypeConverter;
/** /**
* @author Francis Besset <francis.besset@gmail.com> * Extended SoapClient that uses a a cURL wrapper for all underlying HTTP
* requests in order to use proper authentication for all requests. This also
* adds NTLM support. A custom WSDL downloader resolves remote xsd:includes and
* allows caching of all remote referenced items.
*
* @author Andreas Schamberger <mail@andreass.net>
*/ */
class SoapClient extends \SoapClient class SoapClient extends \SoapClient
{ {
/**
* Soap version.
*
* @var int
*/
protected $soapVersion = SOAP_1_1;
/**
* Tracing enabled?
*
* @var boolean
*/
protected $tracingEnabled = false;
/**
* cURL instance.
*
* @var \BeSimple\SoapClient\Curl
*/
protected $curl = null;
/**
* Last request headers.
*
* @var string
*/
private $lastRequestHeaders = '';
/**
* Last request.
*
* @var string
*/
private $lastRequest = '';
/**
* Last response headers.
*
* @var string
*/
private $lastResponseHeaders = '';
/**
* Last response.
*
* @var string
*/
private $lastResponse = '';
/**
* Last response.
*
* @var \BeSimple\SoapCommon\SoapKernel
*/
protected $soapKernel = null;
/**
* Constructor.
*
* @param string $wsdl WSDL file
* @param array(string=>mixed) $options Options array
*/
public function __construct($wsdl, array $options = array())
{
// tracing enabled: store last request/response header and body
if (isset($options['trace']) && $options['trace'] === true) {
$this->tracingEnabled = true;
}
// store SOAP version
if (isset($options['soap_version'])) {
$this->soapVersion = $options['soap_version'];
}
$this->curl = new Curl($options);
$wsdlFile = $this->loadWsdl($wsdl, $options);
// TODO $wsdlHandler = new WsdlHandler($wsdlFile, $this->soapVersion);
$this->soapKernel = new SoapKernel();
// set up type converter and mime filter
$this->configureMime($options);
// we want the exceptions option to be set
$options['exceptions'] = true;
// disable obsolete trace option for native SoapClient as we need to do our own tracing anyways
$options['trace'] = false;
// disable WSDL caching as we handle WSDL caching for remote URLs ourself
$options['cache_wsdl'] = WSDL_CACHE_NONE;
parent::__construct($wsdlFile, $options);
}
/**
* Perform HTTP request with cURL.
*
* @param SoapRequest $soapRequest SoapRequest object
*
* @return SoapResponse
*/
private function __doHttpRequest(SoapRequest $soapRequest)
{
// HTTP headers
$headers = array(
'Content-Type:' . $soapRequest->getContentType(),
'SOAPAction: "' . $soapRequest->getAction() . '"',
);
// execute HTTP request with cURL
$responseSuccessfull = $this->curl->exec(
$soapRequest->getLocation(),
$soapRequest->getContent(),
$headers
);
// tracing enabled: store last request header and body
if ($this->tracingEnabled === true) {
$this->lastRequestHeaders = $this->curl->getRequestHeaders();
$this->lastRequest = $soapRequest->getContent();
}
// in case of an error while making the http request throw a soapFault
if ($responseSuccessfull === false) {
// get error message from curl
$faultstring = $this->curl->getErrorMessage();
throw new \SoapFault('HTTP', $faultstring);
}
// tracing enabled: store last response header and body
if ($this->tracingEnabled === true) {
$this->lastResponseHeaders = $this->curl->getResponseHeaders();
$this->lastResponse = $this->curl->getResponseBody();
}
// wrap response data in SoapResponse object
$soapResponse = SoapResponse::create(
$this->curl->getResponseBody(),
$soapRequest->getLocation(),
$soapRequest->getAction(),
$soapRequest->getVersion(),
$this->curl->getResponseContentType()
);
return $soapResponse;
}
/**
* Custom request method to be able to modify the SOAP messages.
* $oneWay parameter is not used at the moment.
*
* @param string $request Request string
* @param string $location Location
* @param string $action SOAP action
* @param int $version SOAP version
* @param int $oneWay 0|1
*
* @return string
*/
public function __doRequest($request, $location, $action, $version, $oneWay = 0)
{
// wrap request data in SoapRequest object
$soapRequest = SoapRequest::create($request, $location, $action, $version);
// do actual SOAP request
$soapResponse = $this->__doRequest2($soapRequest);
// return SOAP response to ext/soap
return $soapResponse->getContent();
}
/**
* Runs the currently registered request filters on the request, performs
* the HTTP request and runs the response filters.
*
* @param SoapRequest $soapRequest SOAP request object
*
* @return SoapResponse
*/
protected function __doRequest2(SoapRequest $soapRequest)
{
// run SoapKernel on SoapRequest
$this->soapKernel->filterRequest($soapRequest);
// perform HTTP request with cURL
$soapResponse = $this->__doHttpRequest($soapRequest);
// run SoapKernel on SoapResponse
$this->soapKernel->filterResponse($soapResponse);
return $soapResponse;
}
/**
* Get last request HTTP headers.
*
* @return string
*/
public function __getLastRequestHeaders()
{
return $this->lastRequestHeaders;
}
/**
* Get last request HTTP body.
*
* @return string
*/
public function __getLastRequest()
{
return $this->lastRequest;
}
/**
* Get last response HTTP headers.
*
* @return string
*/
public function __getLastResponseHeaders()
{
return $this->lastResponseHeaders;
}
/**
* Get last response HTTP body.
*
* @return string
*/
public function __getLastResponse()
{
return $this->lastResponse;
}
/**
* Get SoapKernel instance.
*
* @return \BeSimple\SoapCommon\SoapKernel
*/
public function getSoapKernel()
{
return $this->soapKernel;
}
/**
* Configure filter and type converter for SwA/MTOM.
*
* @param array &$options SOAP constructor options array.
*
* @return void
*/
private function configureMime(array &$options)
{
if (isset($options['attachment_type']) && Helper::ATTACHMENTS_TYPE_BASE64 !== $options['attachment_type']) {
// register mime filter in SoapKernel
$mimeFilter = new MimeFilter($options['attachment_type']);
$this->soapKernel->registerFilter($mimeFilter);
// configure type converter
if (Helper::ATTACHMENTS_TYPE_SWA === $options['attachment_type']) {
$converter = new SwaTypeConverter();
$converter->setKernel($this->soapKernel);
} elseif (Helper::ATTACHMENTS_TYPE_MTOM === $options['attachment_type']) {
$converter = new MtomTypeConverter();
$converter->setKernel($this->soapKernel);
}
// configure typemap
if (!isset($options['typemap'])) {
$options['typemap'] = array();
}
$soapKernel = $this->soapKernel;
$options['typemap'][] = array(
'type_name' => $converter->getTypeName(),
'type_ns' => $converter->getTypeNamespace(),
'from_xml' => function($input) use ($converter) {
return $converter->convertXmlToPhp($input);
},
'to_xml' => function($input) use ($converter) {
return $converter->convertPhpToXml($input);
},
);
}
}
/**
* Downloads WSDL files with cURL. Uses all SoapClient options for
* authentication. Uses the WSDL_CACHE_* constants and the 'soap.wsdl_*'
* ini settings. Does only file caching as SoapClient only supports a file
* name parameter.
*
* @param string $wsdl WSDL file
* @param array(string=>mixed) $options Options array
*
* @return string
*/
private function loadWsdl($wsdl, array $options)
{
// option to resolve wsdl/xsd includes
$resolveRemoteIncludes = true;
if (isset($options['resolve_wsdl_remote_includes'])) {
$resolveRemoteIncludes = $options['resolve_wsdl_remote_includes'];
}
// option to enable cache
$wsdlCache = WSDL_CACHE_DISK;
if (isset($options['cache_wsdl'])) {
$wsdlCache = $options['cache_wsdl'];
}
$wsdlDownloader = new WsdlDownloader($this->curl, $resolveRemoteIncludes, $wsdlCache);
try {
$cacheFileName = $wsdlDownloader->download($wsdl);
} catch (\RuntimeException $e) {
throw new \SoapFault('WSDL', "SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl . "' : failed to load external entity \"" . $wsdl . "\"");
}
return $cacheFileName;
}
} }

View File

@ -15,25 +15,35 @@ namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\AbstractSoapBuilder; use BeSimple\SoapCommon\AbstractSoapBuilder;
/** /**
* Fluent interface builder for SoapClient instance.
*
* @author Francis Besset <francis.besset@gmail.com> * @author Francis Besset <francis.besset@gmail.com>
* @author Christian Kerl <christian-kerl@web.de> * @author Christian Kerl <christian-kerl@web.de>
*/ */
class SoapClientBuilder extends AbstractSoapBuilder class SoapClientBuilder extends AbstractSoapBuilder
{ {
/**
* Authentication options.
*
* @var array(string=>mixed)
*/
protected $soapOptionAuthentication = array(); protected $soapOptionAuthentication = array();
/** /**
* @return SoapClientBuilder * Create new instance with default options.
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/ */
static public function createWithDefaults() public static function createWithDefaults()
{ {
return parent::createWithDefaults() return parent::createWithDefaults()
->withUserAgent('BeSimpleSoap') ->withUserAgent('BeSimpleSoap');
;
} }
/** /**
* @return SoapClient * Finally returns a SoapClient instance.
*
* @return \BeSimple\SoapClient\SoapClient
*/ */
public function build() public function build()
{ {
@ -42,13 +52,22 @@ class SoapClientBuilder extends AbstractSoapBuilder
return new SoapClient($this->wsdl, $this->getSoapOptions()); return new SoapClient($this->wsdl, $this->getSoapOptions());
} }
/**
* Get final array of SOAP options.
*
* @return array(string=>mixed)
*/
public function getSoapOptions() public function getSoapOptions()
{ {
return parent::getSoapOptions() + $this->soapOptionAuthentication; return parent::getSoapOptions() + $this->soapOptionAuthentication;
} }
/** /**
* @return SoapClientBuilder * Configure option 'trace'.
*
* @param boolean $trace Enable/Disable
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/ */
public function withTrace($trace = true) public function withTrace($trace = true)
{ {
@ -58,7 +77,11 @@ class SoapClientBuilder extends AbstractSoapBuilder
} }
/** /**
* @return SoapClientBuilder * Configure option 'exceptions'.
*
* @param boolean $exceptions Enable/Disable
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/ */
public function withExceptions($exceptions = true) public function withExceptions($exceptions = true)
{ {
@ -68,7 +91,11 @@ class SoapClientBuilder extends AbstractSoapBuilder
} }
/** /**
* @return SoapClientBuilder * Configure option 'user_agent'.
*
* @param string $userAgent User agent string
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/ */
public function withUserAgent($userAgent) public function withUserAgent($userAgent)
{ {
@ -77,18 +104,37 @@ class SoapClientBuilder extends AbstractSoapBuilder
return $this; return $this;
} }
/**
* Enable gzip compression.
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withCompressionGzip() public function withCompressionGzip()
{ {
$this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP; $this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_GZIP;
}
public function withCompressionDeflate() return $this;
{
$this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE;
} }
/** /**
* @return SoapClientBuilder * Enable deflate compression.
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withCompressionDeflate()
{
$this->soapOptions['compression'] = SOAP_COMPRESSION_ACCEPT | SOAP_COMPRESSION_DEFLATE;
return $this;
}
/**
* Configure basic authentication
*
* @param string $username Username
* @param string $password Password
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/ */
public function withBasicAuthentication($username, $password) public function withBasicAuthentication($username, $password)
{ {
@ -102,7 +148,12 @@ class SoapClientBuilder extends AbstractSoapBuilder
} }
/** /**
* @return SoapClientBuilder * Configure digest authentication.
*
* @param string $certificate Certificate
* @param string $passphrase Passphrase
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/ */
public function withDigestAuthentication($certificate, $passphrase = null) public function withDigestAuthentication($certificate, $passphrase = null)
{ {
@ -118,6 +169,16 @@ class SoapClientBuilder extends AbstractSoapBuilder
return $this; return $this;
} }
/**
* Configure proxy.
*
* @param string $host Host
* @param int $port Port
* @param string $username Username
* @param string $password Password
*
* @return \BeSimple\SoapClient\SoapClientBuilder
*/
public function withProxy($host, $port, $username = null, $password = null) public function withProxy($host, $port, $username = null, $password = null)
{ {
$this->soapOptions['proxy_host'] = $host; $this->soapOptions['proxy_host'] = $host;
@ -131,6 +192,9 @@ class SoapClientBuilder extends AbstractSoapBuilder
return $this; return $this;
} }
/**
* Validate options.
*/
protected function validateOptions() protected function validateOptions()
{ {
$this->validateWsdl(); $this->validateWsdl();

View File

@ -12,186 +12,37 @@
namespace BeSimple\SoapClient; namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
use BeSimple\SoapCommon\SoapMessage;
/** /**
* @author Francis Besset <francis.besset@gmail.com> * SoapRequest class for SoapClient. Provides factory function for request object.
*
* @author Andreas Schamberger <mail@andreass.net>
*/ */
class SoapRequest class SoapRequest extends CommonSoapRequest
{ {
protected $function;
protected $arguments;
protected $options;
protected $headers;
public function __construct($function = null, array $arguments = array(), array $options = array(), array $headers = array())
{
$this->function = $function;
$this->arguments = $arguments;
$this->options = $options;
$this->setHeaders($headers);
}
/** /**
* @return string The function name * Factory function for SoapRequest.
*/
public function getFunction()
{
return $this->function;
}
/**
* @param string The function name
* *
* @return SoapRequest * @param string $content Content
*/ * @param string $location Location
public function setFunction($function) * @param string $action SOAP action
{ * @param string $version SOAP version
$this->function = $function;
return $this;
}
/**
* @return array An array with all arguments
*/
public function getArguments()
{
return $this->arguments;
}
/**
* @param string The name of the argument
* @param mixed The default value returned if the argument is not exists
* *
* @return mixed * @return BeSimple\SoapClient\SoapRequest
*/ */
public function getArgument($name, $default = null) public static function create($content, $location, $action, $version)
{ {
return $this->hasArgument($name) ? $this->arguments[$name] : $default; $request = new SoapRequest();
// $content is if unmodified from SoapClient not a php string type!
$request->setContent((string) $content);
$request->setLocation($location);
$request->setAction($action);
$request->setVersion($version);
$contentType = SoapMessage::getContentTypeForVersion($version);
$request->setContentType($contentType);
return $request;
} }
/**
* @param string The name of the argument
*
* @return boolean
*/
public function hasArgument($name)
{
return isset($this->arguments[$name]);
}
/**
* @param array An array with arguments
*
* @return SoapRequest
*/
public function setArguments(array $arguments)
{
$this->arguments = $arguments;
return $this;
}
/**
* @param string The name of argument
* @param mixed The value of argument
*
* @return SoapRequest
*/
public function addArgument($name, $value)
{
$this->arguments[$name] = $value;
return $this;
}
/**
* @return array An array with all options
*/
public function getOptions()
{
return $this->options;
}
/**
* @param string The name of the option
* @param mixed The default value returned if the option is not exists
*
* @return mixed
*/
public function getOption($name, $default = null)
{
return $this->hasOption($name) ? $this->options[$name] : $default;
}
/**
* @param string The name of the option
*
* @return boolean
*/
public function hasOption($name)
{
return isset($this->options[$name]);
}
/**
* @param array An array with options
*
* @return SoapRequest
*/
public function setOptions(array $options)
{
$this->options = $options;
return $this;
}
/**
* @return array|null
*/
public function getHeaders()
{
return empty($this->headers) ? null : $this->headers;
}
/**
* @param array $headers
*
* @return SoapRequest
*/
public function setHeaders(array $headers)
{
$this->headers = array();
foreach ($headers as $header) {
$this->addHeader($header);
}
return $this;
}
/**
* @param \SoapHeader $header
*
* @return SoapRequest
*/
public function addHeader(\SoapHeader $header)
{
$this->headers[] = $header;
return $this;
}
/**
* @param string The name of option
* @param mixed The value of option
*
* @return SoapRequest
*/
public function addOption($name, $value)
{
$this->options[$name] = $value;
return $this;
}
} }

View File

@ -0,0 +1,47 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
use BeSimple\SoapCommon\SoapMessage;
/**
* SoapResponse class for SoapClient. Provides factory function for response object.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class SoapResponse extends CommonSoapResponse
{
/**
* Factory function for SoapResponse.
*
* @param string $content Content
* @param string $location Location
* @param string $action SOAP action
* @param string $version SOAP version
* @param string $contentType Content type header
*
* @return BeSimple\SoapClient\SoapResponse
*/
public static function create($content, $location, $action, $version, $contentType)
{
$response = new SoapResponse();
$response->setContent($content);
$response->setLocation($location);
$response->setAction($action);
$response->setVersion($version);
$response->setContentType($contentType);
return $response;
}
}

View File

@ -0,0 +1,347 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\FilterHelper;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
use BeSimple\SoapCommon\SoapRequestFilter;
use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
use BeSimple\SoapCommon\SoapResponseFilter;
/**
* This plugin implements a subset of the following standards:
* * Web Services Addressing 1.0 - Core
* http://www.w3.org/TR/2006/REC-ws-addr-core
* * Web Services Addressing 1.0 - SOAP Binding
* http://www.w3.org/TR/ws-addr-soap
*
* Per default this plugin uses the SoapClient's $action and $location values
* for wsa:Action and wsa:To. Therefore the only REQUIRED property 'wsa:Action'
* is always set automatically.
*
* Limitation: wsa:From, wsa:FaultTo and wsa:ReplyTo only support the
* wsa:Address element of the endpoint reference at the moment.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class WsAddressingFilter implements SoapRequestFilter, SoapResponseFilter
{
/**
* (2.1) Endpoint reference (EPR) anonymous default address.
*
* Some endpoints cannot be located with a meaningful IRI; this URI is used
* to allow such endpoints to send and receive messages. The precise meaning
* of this URI is defined by the binding of Addressing to a specific
* protocol and/or the context in which the EPR is used.
*
* @see http://www.w3.org/TR/2006/REC-ws-addr-core-20060509/#predefaddr
*/
const ENDPOINT_REFERENCE_ANONYMOUS = 'http://www.w3.org/2005/08/addressing/anonymous';
/**
* (2.1) Endpoint reference (EPR) address for discarting messages.
*
* Messages sent to EPRs whose [address] is this value MUST be discarded
* (i.e. not sent). This URI is typically used in EPRs that designate a
* reply or fault endpoint (see section 3.1 Abstract Property Definitions)
* to indicate that no reply or fault message should be sent.
*
* @see http://www.w3.org/TR/2006/REC-ws-addr-core-20060509/#predefaddr
*/
const ENDPOINT_REFERENCE_NONE = 'http://www.w3.org/2005/08/addressing/none';
/**
* (3.1) Predefined value for reply.
*
* Indicates that this is a reply to the message identified by the [message id] IRI.
*
* see http://www.w3.org/TR/2006/REC-ws-addr-core-20060509/#predefrels
*/
const RELATIONSHIP_TYPE_REPLY = 'http://www.w3.org/2005/08/addressing/reply';
/**
* FaultTo.
*
* @var string
*/
protected $faultTo;
/**
* From.
*
* @var string
*/
protected $from;
/**
* MessageId.
*
* @var string
*/
protected $messageId;
/**
* List of reference parameters associated with this soap message.
*
* @var unknown_type
*/
protected $referenceParametersSet = array();
/**
* List of reference parameters recieved with this soap message.
*
* @var unknown_type
*/
protected $referenceParametersRecieved = array();
/**
* RelatesTo.
*
* @var string
*/
protected $relatesTo;
/**
* RelatesTo@RelationshipType.
*
* @var string
*/
protected $relatesToRelationshipType;
/**
* ReplyTo.
*
* @var string
*/
protected $replyTo;
/**
* Add additional reference parameters
*
* @param string $ns Namespace URI
* @param string $pfx Namespace prefix
* @param string $parameter Parameter name
* @param string $value Parameter value
*
* @return void
*/
public function addReferenceParameter($ns, $pfx, $parameter, $value)
{
$this->referenceParametersSet[] = array(
'ns' => $ns,
'pfx' => $pfx,
'parameter' => $parameter,
'value' => $value,
);
}
/**
* Get additional reference parameters.
*
* @param string $ns Namespace URI
* @param string $parameter Parameter name
*
* @return string|null
*/
public function getReferenceParameter($ns, $parameter)
{
if (isset($this->referenceParametersRecieved[$ns][$parameter])) {
return $this->referenceParametersRecieved[$ns][$parameter];
}
return null;
}
/**
* Reset all properties to default values.
*/
public function resetFilter()
{
$this->faultTo = null;
$this->from = null;
$this->messageId = null;
$this->referenceParametersRecieved = array();
$this->referenceParametersSet = array();
$this->relatesTo = null;
$this->relatesToRelationshipType = null;
$this->replyTo = null;
}
/**
* Set FaultTo address of type xs:anyURI.
*
* @param string $faultTo xs:anyURI
*
* @return void
*/
public function setFaultTo($faultTo)
{
$this->faultTo = $faultTo;
}
/**
* Set From address of type xs:anyURI.
*
* @param string $from xs:anyURI
*
* @return void
*/
public function setFrom($from)
{
$this->from = $from;
}
/**
* Set MessageId of type xs:anyURI.
* Default: UUID v4 e.g. 'uuid:550e8400-e29b-11d4-a716-446655440000'
*
* @param string $messageId xs:anyURI
*
* @return void
*/
public function setMessageId($messageId = null)
{
if (null === $messageId) {
$messageId = 'uuid:' . Helper::generateUUID();
}
$this->messageId = $messageId;
}
/**
* Set RelatesTo of type xs:anyURI with the optional relationType
* (of type xs:anyURI).
*
* @param string $relatesTo xs:anyURI
* @param string $relationType xs:anyURI
*
* @return void
*/
public function setRelatesTo($relatesTo, $relationType = null)
{
$this->relatesTo = $relatesTo;
if (null !== $relationType && $relationType != self::RELATIONSHIP_TYPE_REPLY) {
$this->relatesToRelationshipType = $relationType;
}
}
/**
* Set ReplyTo address of type xs:anyURI
* Default: self::ENDPOINT_REFERENCE_ANONYMOUS
*
* @param string $replyTo xs:anyURI
*
* @return void
*/
public function setReplyTo($replyTo = null)
{
if (null === $replyTo) {
$replyTo = self::ENDPOINT_REFERENCE_ANONYMOUS;
}
$this->replyTo = $replyTo;
}
/**
* Modify the given request XML.
*
* @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
*
* @return void
*/
public function filterRequest(CommonSoapRequest $request)
{
// get \DOMDocument from SOAP request
$dom = $request->getContentDocument();
// create FilterHelper
$filterHelper = new FilterHelper($dom);
// add the neccessary namespaces
$filterHelper->addNamespace(Helper::PFX_WSA, Helper::NS_WSA);
$action = $filterHelper->createElement(Helper::NS_WSA, 'Action', $request->getAction());
$filterHelper->addHeaderElement($action);
$to = $filterHelper->createElement(Helper::NS_WSA, 'To', $request->getLocation());
$filterHelper->addHeaderElement($to);
if (null !== $this->faultTo) {
$faultTo = $filterHelper->createElement(Helper::NS_WSA, 'FaultTo');
$filterHelper->addHeaderElement($faultTo);
$address = $filterHelper->createElement(Helper::NS_WSA, 'Address', $this->faultTo);
$faultTo->appendChild($address);
}
if (null !== $this->from) {
$from = $filterHelper->createElement(Helper::NS_WSA, 'From');
$filterHelper->addHeaderElement($from);
$address = $filterHelper->createElement(Helper::NS_WSA, 'Address', $this->from);
$from->appendChild($address);
}
if (null !== $this->messageId) {
$messageId = $filterHelper->createElement(Helper::NS_WSA, 'MessageID', $this->messageId);
$filterHelper->addHeaderElement($messageId);
}
if (null !== $this->relatesTo) {
$relatesTo = $filterHelper->createElement(Helper::NS_WSA, 'RelatesTo', $this->relatesTo);
if (null !== $this->relatesToRelationshipType) {
$filterHelper->setAttribute($relatesTo, Helper::NS_WSA, 'RelationshipType', $this->relatesToRelationshipType);
}
$filterHelper->addHeaderElement($relatesTo);
}
if (null !== $this->replyTo) {
$replyTo = $filterHelper->createElement(Helper::NS_WSA, 'ReplyTo');
$filterHelper->addHeaderElement($replyTo);
$address = $filterHelper->createElement(Helper::NS_WSA, 'Address', $this->replyTo);
$replyTo->appendChild($address);
}
foreach ($this->referenceParametersSet as $rp) {
$filterHelper->addNamespace($rp['pfx'], $rp['ns']);
$parameter = $filterHelper->createElement($rp['ns'], $rp['parameter'], $rp['value']);
$filterHelper->setAttribute($parameter, Helper::NS_WSA, 'IsReferenceParameter', 'true');
$filterHelper->addHeaderElement($parameter);
}
}
/**
* Modify the given response XML.
*
* @param \BeSimple\SoapCommon\SoapResponse $response SOAP response
*
* @return void
*/
public function filterResponse(CommonSoapResponse $response)
{
// get \DOMDocument from SOAP response
$dom = $response->getContentDocument();
$this->referenceParametersRecieved = array();
$referenceParameters = $dom->getElementsByTagNameNS(Helper::NS_WSA, 'ReferenceParameters')->item(0);
if (null !== $referenceParameters) {
foreach ($referenceParameters->childNodes as $childNode) {
if (!isset($this->referenceParametersRecieved[$childNode->namespaceURI])) {
$this->referenceParametersRecieved[$childNode->namespaceURI] = array();
}
$this->referenceParametersRecieved[$childNode->namespaceURI][$childNode->localName] = $childNode->nodeValue;
}
}
}
}

View File

@ -0,0 +1,581 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use ass\XmlSecurity\DSig as XmlSecurityDSig;
use ass\XmlSecurity\Enc as XmlSecurityEnc;
use ass\XmlSecurity\Key as XmlSecurityKey;
use BeSimple\SoapCommon\FilterHelper;
use BeSimple\SoapCommon\Helper;
use BeSimple\SoapCommon\SoapRequest as CommonSoapRequest;
use BeSimple\SoapCommon\SoapRequestFilter;
use BeSimple\SoapCommon\SoapResponse as CommonSoapResponse;
use BeSimple\SoapCommon\SoapResponseFilter;
use BeSimple\SoapCommon\WsSecurityKey;
/**
* This plugin implements a subset of the following standards:
* * Web Services Security: SOAP Message Security 1.0 (WS-Security 2004)
* http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0.pdf
* * Web Services Security UsernameToken Profile 1.0
* http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf
* * Web Services Security X.509 Certificate Token Profile
* http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0.pdf
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class WsSecurityFilter implements SoapRequestFilter, SoapResponseFilter
{
/*
* The date format to be used with {@link \DateTime}
*/
const DATETIME_FORMAT = 'Y-m-d\TH:i:s.000\Z';
/**
* (UT 3.1) Password type: plain text.
*/
const PASSWORD_TYPE_TEXT = 0;
/**
* (UT 3.1) Password type: digest.
*/
const PASSWORD_TYPE_DIGEST = 1;
/**
* (X509 3.2.1) Reference to a Subject Key Identifier
*/
const TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER = 0;
/**
* (X509 3.2.1) Reference to a Security Token
*/
const TOKEN_REFERENCE_SECURITY_TOKEN = 1;
/**
* (SMS_1.1 7.3) Key Identifiers
*/
const TOKEN_REFERENCE_THUMBPRINT_SHA1 = 2;
/**
* Actor.
*
* @var string
*/
protected $actor;
/**
* (SMS 10) Add security timestamp.
*
* @var boolean
*/
protected $addTimestamp;
/**
* Encrypt the signature?
*
* @var boolean
*/
protected $encryptSignature;
/**
* (SMS 10) Security timestamp expires time in seconds.
*
* @var int
*/
protected $expires;
/**
* (UT 3.1) Password.
*
* @var string
*/
protected $password;
/**
* (UT 3.1) Password type: text or digest.
*
* @var int
*/
protected $passwordType;
/**
* Sign all headers.
*
* @var boolean
*/
protected $signAllHeaders;
/**
* (X509 3.2) Token reference type for encryption.
*
* @var int
*/
protected $tokenReferenceEncryption = null;
/**
* (X509 3.2) Token reference type for signature.
*
* @var int
*/
protected $tokenReferenceSignature = null;
/**
* Service WsSecurityKey.
*
* @var \BeSimple\SoapCommon\WsSecurityKey
*/
protected $serviceSecurityKey;
/**
* (UT 3.1) Username.
*
* @var string
*/
protected $username;
/**
* User WsSecurityKey.
*
* @var \BeSimple\SoapCommon\WsSecurityKey
*/
protected $userSecurityKey;
/**
* Constructor.
*
* @param boolean $addTimestamp (SMS 10) Add security timestamp.
* @param int $expires (SMS 10) Security timestamp expires time in seconds.
* @param string $actor SOAP actor
*/
public function __construct($addTimestamp = true, $expires = 300, $actor = null)
{
$this->addTimestamp = $addTimestamp;
$this->expires = $expires;
$this->actor = $actor;
}
/**
* Add user data.
*
* @param string $username Username
* @param string $password Password
* @param int $passwordType self::PASSWORD_TYPE_DIGEST | self::PASSWORD_TYPE_TEXT
*
* @return void
*/
public function addUserData($username, $password = null, $passwordType = self::PASSWORD_TYPE_DIGEST)
{
$this->username = $username;
$this->password = $password;
$this->passwordType = $passwordType;
}
/**
* Reset all properties to default values.
*/
public function resetFilter()
{
$this->actor = null;
$this->addTimestamp = null;
$this->encryptSignature = null;
$this->expires = null;
$this->password = null;
$this->passwordType = null;
$this->serviceSecurityKey = null;
$this->signAllHeaders = null;
$this->tokenReferenceEncryption = null;
$this->tokenReferenceSignature = null;
$this->username = null;
$this->userSecurityKey = null;
}
/**
* Get service security key.
*
* @param \BeSimple\SoapCommon\WsSecurityKey $serviceSecurityKey Service security key
*
* @return void
*/
public function setServiceSecurityKeyObject(WsSecurityKey $serviceSecurityKey)
{
$this->serviceSecurityKey = $serviceSecurityKey;
}
/**
* Get user security key.
*
* @param \BeSimple\SoapCommon\WsSecurityKey $userSecurityKey User security key
*
* @return void
*/
public function setUserSecurityKeyObject(WsSecurityKey $userSecurityKey)
{
$this->userSecurityKey = $userSecurityKey;
}
/**
* Set security options.
*
* @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1
* @param boolean $encryptSignature Encrypt signature
*
* @return void
*/
public function setSecurityOptionsEncryption($tokenReference, $encryptSignature = false)
{
$this->tokenReferenceEncryption = $tokenReference;
$this->encryptSignature = $encryptSignature;
}
/**
* Set security options.
*
* @param int $tokenReference self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | self::TOKEN_REFERENCE_SECURITY_TOKEN | self::TOKEN_REFERENCE_THUMBPRINT_SHA1
* @param boolean $signAllHeaders Sign all headers?
*
* @return void
*/
public function setSecurityOptionsSignature($tokenReference, $signAllHeaders = false)
{
$this->tokenReferenceSignature = $tokenReference;
$this->signAllHeaders = $signAllHeaders;
}
/**
* Modify the given request XML.
*
* @param \BeSimple\SoapCommon\SoapRequest $request SOAP request
*
* @return void
*/
public function filterRequest(CommonSoapRequest $request)
{
// get \DOMDocument from SOAP request
$dom = $request->getContentDocument();
// create FilterHelper
$filterHelper = new FilterHelper($dom);
// add the neccessary namespaces
$filterHelper->addNamespace(Helper::PFX_WSS, Helper::NS_WSS);
$filterHelper->addNamespace(Helper::PFX_WSU, Helper::NS_WSU);
$filterHelper->registerNamespace(XmlSecurityDSig::PFX_XMLDSIG, XmlSecurityDSig::NS_XMLDSIG);
// init timestamp
$dt = new \DateTime('now', new \DateTimeZone('UTC'));
$createdTimestamp = $dt->format(self::DATETIME_FORMAT);
// create security header
$security = $filterHelper->createElement(Helper::NS_WSS, 'Security');
$filterHelper->addHeaderElement($security, true, $this->actor, $request->getVersion());
if (true === $this->addTimestamp || null !== $this->expires) {
$timestamp = $filterHelper->createElement(Helper::NS_WSU, 'Timestamp');
$created = $filterHelper->createElement(Helper::NS_WSU, 'Created', $createdTimestamp);
$timestamp->appendChild($created);
if (null !== $this->expires) {
$dt->modify('+' . $this->expires . ' seconds');
$expiresTimestamp = $dt->format(self::DATETIME_FORMAT);
$expires = $filterHelper->createElement(Helper::NS_WSU, 'Expires', $expiresTimestamp);
$timestamp->appendChild($expires);
}
$security->appendChild($timestamp);
}
if (null !== $this->username) {
$usernameToken = $filterHelper->createElement(Helper::NS_WSS, 'UsernameToken');
$security->appendChild($usernameToken);
$username = $filterHelper->createElement(Helper::NS_WSS, 'Username', $this->username);
$usernameToken->appendChild($username);
if (null !== $this->password
&& (null === $this->userSecurityKey
|| (null !== $this->userSecurityKey && !$this->userSecurityKey->hasPrivateKey()))) {
if (self::PASSWORD_TYPE_DIGEST === $this->passwordType) {
$nonce = mt_rand();
$password = base64_encode(sha1($nonce . $createdTimestamp . $this->password, true));
$passwordType = Helper::NAME_WSS_UTP . '#PasswordDigest';
} else {
$password = $this->password;
$passwordType = Helper::NAME_WSS_UTP . '#PasswordText';
}
$password = $filterHelper->createElement(Helper::NS_WSS, 'Password', $password);
$filterHelper->setAttribute($password, null, 'Type', $passwordType);
$usernameToken->appendChild($password);
if (self::PASSWORD_TYPE_DIGEST === $this->passwordType) {
$nonce = $filterHelper->createElement(Helper::NS_WSS, 'Nonce', base64_encode($nonce));
$usernameToken->appendChild($nonce);
$created = $filterHelper->createElement(Helper::NS_WSU, 'Created', $createdTimestamp);
$usernameToken->appendChild($created);
}
}
}
if (null !== $this->userSecurityKey && $this->userSecurityKey->hasKeys()) {
$guid = 'CertId-' . Helper::generateUUID();
// add token references
$keyInfo = null;
if (null !== $this->tokenReferenceSignature) {
$keyInfo = $this->createKeyInfo($filterHelper, $this->tokenReferenceSignature, $guid, $this->userSecurityKey->getPublicKey());
}
$nodes = $this->createNodeListForSigning($dom, $security);
$signature = XmlSecurityDSig::createSignature($this->userSecurityKey->getPrivateKey(), XmlSecurityDSig::EXC_C14N, $security, null, $keyInfo);
$options = array(
'id_ns_prefix' => Helper::PFX_WSU,
'id_prefix_ns' => Helper::NS_WSU,
);
foreach ($nodes as $node) {
XmlSecurityDSig::addNodeToSignature($signature, $node, XmlSecurityDSig::SHA1, XmlSecurityDSig::EXC_C14N, $options);
}
XmlSecurityDSig::signDocument($signature, $this->userSecurityKey->getPrivateKey(), XmlSecurityDSig::EXC_C14N);
$publicCertificate = $this->userSecurityKey->getPublicKey()->getX509Certificate(true);
$binarySecurityToken = $filterHelper->createElement(Helper::NS_WSS, 'BinarySecurityToken', $publicCertificate);
$filterHelper->setAttribute($binarySecurityToken, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary');
$filterHelper->setAttribute($binarySecurityToken, null, 'ValueType', Helper::NAME_WSS_X509 . '#X509v3');
$filterHelper->setAttribute($binarySecurityToken, Helper::NS_WSU, 'Id', $guid);
$security->insertBefore($binarySecurityToken, $signature);
// encrypt soap document
if (null !== $this->serviceSecurityKey && $this->serviceSecurityKey->hasKeys()) {
$guid = 'EncKey-' . Helper::generateUUID();
// add token references
$keyInfo = null;
if (null !== $this->tokenReferenceEncryption) {
$keyInfo = $this->createKeyInfo($filterHelper, $this->tokenReferenceEncryption, $guid, $this->serviceSecurityKey->getPublicKey());
}
$encryptedKey = XmlSecurityEnc::createEncryptedKey($guid, $this->serviceSecurityKey->getPrivateKey(), $this->serviceSecurityKey->getPublicKey(), $security, $signature, $keyInfo);
$referenceList = XmlSecurityEnc::createReferenceList($encryptedKey);
// token reference to encrypted key
$keyInfo = $this->createKeyInfo($filterHelper, self::TOKEN_REFERENCE_SECURITY_TOKEN, $guid);
$nodes = $this->createNodeListForEncryption($dom, $security);
foreach ($nodes as $node) {
$type = XmlSecurityEnc::ELEMENT;
if ($node->localName == 'Body') {
$type = XmlSecurityEnc::CONTENT;
}
XmlSecurityEnc::encryptNode($node, $type, $this->serviceSecurityKey->getPrivateKey(), $referenceList, $keyInfo);
}
}
}
}
/**
* Modify the given request XML.
*
* @param \BeSimple\SoapCommon\SoapResponse $response SOAP response
*
* @return void
*/
public function filterResponse(CommonSoapResponse $response)
{
// get \DOMDocument from SOAP response
$dom = $response->getContentDocument();
// locate security header
$security = $dom->getElementsByTagNameNS(Helper::NS_WSS, 'Security')->item(0);
if (null !== $security) {
// add SecurityTokenReference resolver for KeyInfo
if (null !== $this->serviceSecurityKey) {
$keyResolver = array($this, 'keyInfoSecurityTokenReferenceResolver');
XmlSecurityDSig::addKeyInfoResolver(Helper::NS_WSS, 'SecurityTokenReference', $keyResolver);
}
// do we have a reference list in header
$referenceList = XmlSecurityEnc::locateReferenceList($security);
// get a list of encrypted nodes
$encryptedNodes = XmlSecurityEnc::locateEncryptedData($dom, $referenceList);
// decrypt them
if (null !== $encryptedNodes) {
foreach ($encryptedNodes as $encryptedNode) {
XmlSecurityEnc::decryptNode($encryptedNode);
}
}
// locate signature node
$signature = XmlSecurityDSig::locateSignature($security);
if (null !== $signature) {
// verify references
$options = array(
'id_ns_prefix' => Helper::PFX_WSU,
'id_prefix_ns' => Helper::NS_WSU,
);
if (XmlSecurityDSig::verifyReferences($signature, $options) !== true) {
throw new \SoapFault('wsse:FailedCheck', 'The signature or decryption was invalid');
}
// verify signature
if (XmlSecurityDSig::verifyDocumentSignature($signature) !== true) {
throw new \SoapFault('wsse:FailedCheck', 'The signature or decryption was invalid');
}
}
}
}
/**
* Adds the configured KeyInfo to the parentNode.
*
* @param FilterHelper $filterHelper Filter helper object
* @param int $tokenReference Token reference type
* @param string $guid Unique ID
* @param \ass\XmlSecurity\Key $xmlSecurityKey XML security key
*
* @return \DOMElement
*/
protected function createKeyInfo(FilterHelper $filterHelper, $tokenReference, $guid, XmlSecurityKey $xmlSecurityKey = null)
{
$keyInfo = $filterHelper->createElement(XmlSecurityDSig::NS_XMLDSIG, 'KeyInfo');
$securityTokenReference = $filterHelper->createElement(Helper::NS_WSS, 'SecurityTokenReference');
$keyInfo->appendChild($securityTokenReference);
// security token
if (self::TOKEN_REFERENCE_SECURITY_TOKEN === $tokenReference) {
$reference = $filterHelper->createElement(Helper::NS_WSS, 'Reference');
$filterHelper->setAttribute($reference, null, 'URI', '#' . $guid);
if (null !== $xmlSecurityKey) {
$filterHelper->setAttribute($reference, null, 'ValueType', Helper::NAME_WSS_X509 . '#X509v3');
}
$securityTokenReference->appendChild($reference);
// subject key identifier
} elseif (self::TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER === $tokenReference && null !== $xmlSecurityKey) {
$keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier');
$filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary');
$filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_X509 . '#509SubjectKeyIdentifier');
$securityTokenReference->appendChild($keyIdentifier);
$certificate = $xmlSecurityKey->getX509SubjectKeyIdentifier();
$dataNode = new \DOMText($certificate);
$keyIdentifier->appendChild($dataNode);
// thumbprint sha1
} elseif (self::TOKEN_REFERENCE_THUMBPRINT_SHA1 === $tokenReference && null !== $xmlSecurityKey) {
$keyIdentifier = $filterHelper->createElement(Helper::NS_WSS, 'KeyIdentifier');
$filterHelper->setAttribute($keyIdentifier, null, 'EncodingType', Helper::NAME_WSS_SMS . '#Base64Binary');
$filterHelper->setAttribute($keyIdentifier, null, 'ValueType', Helper::NAME_WSS_SMS_1_1 . '#ThumbprintSHA1');
$securityTokenReference->appendChild($keyIdentifier);
$thumbprintSha1 = base64_encode(sha1(base64_decode($xmlSecurityKey->getX509Certificate(true)), true));
$dataNode = new \DOMText($thumbprintSha1);
$keyIdentifier->appendChild($dataNode);
}
return $keyInfo;
}
/**
* Create a list of \DOMNodes that should be encrypted.
*
* @param \DOMDocument $dom DOMDocument to query
* @param \DOMElement $security Security element
*
* @return \DOMNodeList
*/
protected function createNodeListForEncryption(\DOMDocument $dom, \DOMElement $security)
{
$xpath = new \DOMXPath($dom);
$xpath->registerNamespace('SOAP-ENV', $dom->documentElement->namespaceURI);
$xpath->registerNamespace('ds', XmlSecurityDSig::NS_XMLDSIG);
if ($this->encryptSignature === true) {
$query = '//ds:Signature | //SOAP-ENV:Body';
} else {
$query = '//SOAP-ENV:Body';
}
return $xpath->query($query);
}
/**
* Create a list of \DOMNodes that should be signed.
*
* @param \DOMDocument $dom DOMDocument to query
* @param \DOMElement $security Security element
*
* @return array(\DOMNode)
*/
protected function createNodeListForSigning(\DOMDocument $dom, \DOMElement $security)
{
$nodes = array();
$body = $dom->getElementsByTagNameNS($dom->documentElement->namespaceURI, 'Body')->item(0);
if (null !== $body) {
$nodes[] = $body;
}
foreach ($security->childNodes as $node) {
if (XML_ELEMENT_NODE === $node->nodeType) {
$nodes[] = $node;
}
}
if ($this->signAllHeaders) {
foreach ($security->parentNode->childNodes as $node) {
if (XML_ELEMENT_NODE === $node->nodeType &&
Helper::NS_WSS !== $node->namespaceURI) {
$nodes[] = $node;
}
}
}
return $nodes;
}
/**
* Gets the referenced node for the given URI.
*
* @param \DOMElement $node Node
* @param string $uri URI
*
* @return \DOMElement
*/
protected function getReferenceNodeForUri(\DOMElement $node, $uri)
{
$url = parse_url($uri);
$referenceId = $url['fragment'];
$query = '//*[@'.Helper::PFX_WSU.':Id="'.$referenceId.'" or @Id="'.$referenceId.'"]';
$xpath = new \DOMXPath($node->ownerDocument);
$xpath->registerNamespace(Helper::PFX_WSU, Helper::NS_WSU);
return $xpath->query($query)->item(0);
}
/**
* Tries to resolve a key from the given \DOMElement.
*
* @param \DOMElement $node Node where to resolve the key
* @param string $algorithm XML security key algorithm
*
* @return \ass\XmlSecurity\Key|null
*/
public function keyInfoSecurityTokenReferenceResolver(\DOMElement $node, $algorithm)
{
foreach ($node->childNodes as $key) {
if (Helper::NS_WSS === $key->namespaceURI) {
switch ($key->localName) {
case 'KeyIdentifier':
return $this->serviceSecurityKey->getPublicKey();
case 'Reference':
$uri = $key->getAttribute('URI');
$referencedNode = $this->getReferenceNodeForUri($node, $uri);
if (XmlSecurityEnc::NS_XMLENC === $referencedNode->namespaceURI
&& 'EncryptedKey' == $referencedNode->localName) {
$key = XmlSecurityEnc::decryptEncryptedKey($referencedNode, $this->userSecurityKey->getPrivateKey());
return XmlSecurityKey::factory($algorithm, $key, XmlSecurityKey::TYPE_PRIVATE);
} else {
//$valueType = $key->getAttribute('ValueType');
return $this->serviceSecurityKey->getPublicKey();
}
}
}
}
return null;
}
}

View File

@ -0,0 +1,259 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapCommon\Helper;
/**
* Downloads WSDL files with cURL. Uses the WSDL_CACHE_* constants and the
* 'soap.wsdl_*' ini settings. Does only file caching as SoapClient only
* supports a file name parameter. The class also resolves remote XML schema
* includes.
*
* @author Andreas Schamberger <mail@andreass.net>
*/
class WsdlDownloader
{
/**
* Cache enabled.
*
* @var bool
*/
protected $cacheEnabled;
/**
* Cache dir.
*
* @var string
*/
protected $cacheDir;
/**
* Cache TTL.
*
* @var int
*/
protected $cacheTtl;
/**
* cURL instance for downloads.
*
* @var unknown_type
*/
protected $curl;
/**
* Resolve WSDl/XSD includes.
*
* @var boolean
*/
protected $resolveRemoteIncludes = true;
/**
* Constructor.
*
* @param \BeSimple\SoapClient\Curl $curl Curl instance
* @param boolean $resolveRemoteIncludes WSDL/XSD include enabled?
* @param boolean $cacheWsdl Cache constant
*/
public function __construct(Curl $curl, $resolveRemoteIncludes = true, $cacheWsdl = WSDL_CACHE_DISK)
{
$this->curl = $curl;
$this->resolveRemoteIncludes = $resolveRemoteIncludes;
// get current WSDL caching config
$this->cacheEnabled = (bool) ini_get('soap.wsdl_cache_enabled');
if ($this->cacheEnabled === true
&& $cacheWsdl === WSDL_CACHE_NONE) {
$this->cacheEnabled = false;
}
$this->cacheDir = ini_get('soap.wsdl_cache_dir');
if (!is_dir($this->cacheDir)) {
$this->cacheDir = sys_get_temp_dir();
}
$this->cacheDir = rtrim($this->cacheDir, '/\\');
$this->cacheTtl = ini_get('soap.wsdl_cache_ttl');
}
/**
* Download given WSDL file and return name of cache file.
*
* @param string $wsdl WSDL file URL/path
*
* @return string
*/
public function download($wsdl)
{
// download and cache remote WSDL files or local ones where we want to
// resolve remote XSD includes
$isRemoteFile = $this->isRemoteFile($wsdl);
if ($isRemoteFile === true || $this->resolveRemoteIncludes === true) {
$cacheFile = $this->cacheDir . DIRECTORY_SEPARATOR . 'wsdl_' . md5($wsdl) . '.cache';
if ($this->cacheEnabled === false
|| !file_exists($cacheFile)
|| (filemtime($cacheFile) + $this->cacheTtl) < time()) {
if ($isRemoteFile === true) {
// execute request
$responseSuccessfull = $this->curl->exec($wsdl);
// get content
if ($responseSuccessfull === true) {
$response = $this->curl->getResponseBody();
if ($this->resolveRemoteIncludes === true) {
$this->resolveRemoteIncludes($response, $cacheFile, $wsdl);
} else {
file_put_contents($cacheFile, $response);
}
} else {
throw new \ErrorException("SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl ."'");
}
} elseif (file_exists($wsdl)) {
$response = file_get_contents($wsdl);
$this->resolveRemoteIncludes($response, $cacheFile);
} else {
throw new \ErrorException("SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl ."'");
}
}
return $cacheFile;
} elseif (file_exists($wsdl)) {
return realpath($wsdl);
} else {
throw new \ErrorException("SOAP-ERROR: Parsing WSDL: Couldn't load from '" . $wsdl ."'");
}
}
/**
* Do we have a remote file?
*
* @param string $file File URL/path
*
* @return boolean
*/
private function isRemoteFile($file)
{
$isRemoteFile = false;
// @parse_url to suppress E_WARNING for invalid urls
if (($url = @parse_url($file)) !== false) {
if (isset($url['scheme']) && substr($url['scheme'], 0, 4) == 'http') {
$isRemoteFile = true;
}
}
return $isRemoteFile;
}
/**
* Resolves remote WSDL/XSD includes within the WSDL files.
*
* @param string $xml XML file
* @param string $cacheFile Cache file name
* @param boolean $parentFile Parent file name
*
* @return void
*/
private function resolveRemoteIncludes($xml, $cacheFile, $parentFile = null)
{
$doc = new \DOMDocument();
$doc->loadXML($xml);
$xpath = new \DOMXPath($doc);
$xpath->registerNamespace(Helper::PFX_XML_SCHEMA, Helper::NS_XML_SCHEMA);
$xpath->registerNamespace(Helper::PFX_WSDL, Helper::NS_WSDL);
// WSDL include/import
$query = './/' . Helper::PFX_WSDL . ':include | .//' . Helper::PFX_WSDL . ':import';
$nodes = $xpath->query($query);
if ($nodes->length > 0) {
foreach ($nodes as $node) {
$location = $node->getAttribute('location');
if ($this->isRemoteFile($location)) {
$location = $this->download($location);
$node->setAttribute('location', $location);
} elseif (!is_null($parentFile)) {
$location = $this->resolveRelativePathInUrl($parentFile, $location);
$location = $this->download($location);
$node->setAttribute('location', $location);
}
}
}
// XML schema include/import
$query = './/' . Helper::PFX_XML_SCHEMA . ':include | .//' . Helper::PFX_XML_SCHEMA . ':import';
$nodes = $xpath->query($query);
if ($nodes->length > 0) {
foreach ($nodes as $node) {
$schemaLocation = $node->getAttribute('schemaLocation');
if ($this->isRemoteFile($schemaLocation)) {
$schemaLocation = $this->download($schemaLocation);
$node->setAttribute('schemaLocation', $schemaLocation);
} elseif (!is_null($parentFile)) {
$schemaLocation = $this->resolveRelativePathInUrl($parentFile, $schemaLocation);
$schemaLocation = $this->download($schemaLocation);
$node->setAttribute('schemaLocation', $schemaLocation);
}
}
}
$doc->save($cacheFile);
}
/**
* Resolves the relative path to base into an absolute.
*
* @param string $base Base path
* @param string $relative Relative path
*
* @return string
*/
private function resolveRelativePathInUrl($base, $relative)
{
$urlParts = parse_url($base);
// combine base path with relative path
if (isset($urlParts['path']) && strpos($relative, '/') === 0) {
// $relative is absolute path from domain (starts with /)
$path = $relative;
} elseif (isset($urlParts['path']) && strrpos($urlParts['path'], '/') === (strlen($urlParts['path']) )) {
// base path is directory
$path = $urlParts['path'] . $relative;
} elseif (isset($urlParts['path'])) {
// strip filename from base path
$path = substr($urlParts['path'], 0, strrpos($urlParts['path'], '/')) . '/' . $relative;
} else {
// no base path
$path = '/' . $relative;
}
// foo/./bar ==> foo/bar
$path = preg_replace('~/\./~', '/', $path);
// remove double slashes
$path = preg_replace('~/+~', '/', $path);
// split path by '/'
$parts = explode('/', $path);
// resolve /../
foreach ($parts as $key => $part) {
if ($part == "..") {
$keyToDelete = $key-1;
while ($keyToDelete > 0) {
if (isset($parts[$keyToDelete])) {
unset($parts[$keyToDelete]);
break;
} else {
$keyToDelete--;
}
}
unset($parts[$key]);
}
}
$hostname = $urlParts['scheme'] . '://' . $urlParts['host'];
if (isset($urlParts['port'])) {
$hostname .= ':' . $urlParts['port'];
}
return $hostname . implode('/', $parts);
}
}

View File

@ -0,0 +1,47 @@
<?php
use BeSimple\SoapCommon\Helper as BeSimpleSoapHelper;
use BeSimple\SoapClient\SoapClient as BeSimpleSoapClient;
require '../bootstrap.php';
echo '<pre>';
$options = array(
'soap_version' => SOAP_1_1,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_MTOM,
'cache_wsdl' => WSDL_CACHE_NONE,
);
/*
* Deploy "axis_services/sample-mtom.aar" to Apache Axis2 to get this
* example to work.
*
* Apache Axis2 MTOM example.
*
*/
$sc = new BeSimpleSoapClient('MTOM.wsdl', $options);
//var_dump($sc->__getFunctions());
//var_dump($sc->__getTypes());
try {
$attachment = new stdClass();
$attachment->fileName = 'test123.txt';
$attachment->binaryData = 'This is a test.';
var_dump($sc->attachment($attachment));
} catch (Exception $e) {
var_dump($e);
}
// var_dump(
// $sc->__getLastRequestHeaders(),
// $sc->__getLastRequest(),
// $sc->__getLastResponseHeaders(),
// $sc->__getLastResponse()
// );

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<definitions targetNamespace="http://ws.apache.org/axis2/mtomsample/" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://ws.apache.org/axis2/mtomsample/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/">
<types>
<xsd:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://ws.apache.org/axis2/mtomsample/" xmlns="http://schemas.xmlsoap.org/wsdl/">
<xsd:import namespace="http://www.w3.org/2005/05/xmlmime"/>
<xsd:complexType name="AttachmentType">
<xsd:sequence>
<xsd:element minOccurs="0" name="fileName" type="xsd:string"/>
<xsd:element minOccurs="0" name="binaryData" type="xmime:base64Binary"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="AttachmentRequest" type="tns:AttachmentType"/>
<xsd:element name="AttachmentResponse" type="xsd:string"/>
</xsd:schema>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xmime="http://www.w3.org/2005/05/xmlmime" attributeFormDefault="unqualified" elementFormDefault="unqualified" targetNamespace="http://www.w3.org/2005/05/xmlmime">
<xs:attribute name="contentType">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:minLength value="3"/>
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="expectedContentTypes" type="xs:string"/>
<xs:complexType name="base64Binary">
<xs:simpleContent>
<xs:extension base="xs:base64Binary">
<xs:attribute ref="xmime:contentType"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="hexBinary">
<xs:simpleContent>
<xs:extension base="xs:hexBinary">
<xs:attribute ref="xmime:contentType"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:schema>
</types>
<message name="AttachmentResponse">
<part name="part1" element="tns:AttachmentResponse">
</part>
</message>
<message name="AttachmentRequest">
<part name="part1" element="tns:AttachmentRequest">
</part>
</message>
<portType name="MTOMServicePortType">
<operation name="attachment">
<input message="tns:AttachmentRequest" wsaw:Action="attachment">
</input>
<output message="tns:AttachmentResponse" wsaw:Action="http://schemas.xmlsoap.org/wsdl/MTOMServicePortType/AttachmentResponse">
</output>
</operation>
</portType>
<binding name="MTOMServiceSOAP11Binding" type="tns:MTOMServicePortType">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="attachment">
<soap:operation soapAction="attachment" style="document"/>
<input>
<soap:body use="literal"/>
</input>
<output>
<soap:body use="literal"/>
</output>
</operation>
</binding>
<binding name="MTOMServiceSOAP12Binding" type="tns:MTOMServicePortType">
<soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="attachment">
<soap12:operation soapAction="attachment" style="document"/>
<input>
<soap12:body use="literal"/>
</input>
<output>
<soap12:body use="literal"/>
</output>
</operation>
</binding>
<service name="MTOMSample">
<port name="MTOMSampleSOAP12port_http" binding="tns:MTOMServiceSOAP12Binding">
<soap12:address location="http://192.168.0.104:8080/axis2/services/MTOMSample.MTOMSampleSOAP12port_http/"/>
</port>
<port name="MTOMSampleSOAP11port_http" binding="tns:MTOMServiceSOAP11Binding">
<soap:address location="http://192.168.0.104:8080/axis2/services/MTOMSample.MTOMSampleSOAP11port_http/"/>
</port>
</service>
</definitions>

86
tests/AxisInterop/SwA.php Normal file
View File

@ -0,0 +1,86 @@
<?php
use BeSimple\SoapCommon\Helper as BeSimpleSoapHelper;
use BeSimple\SoapClient\SoapClient as BeSimpleSoapClient;
require '../bootstrap.php';
echo '<pre>';
$options = array(
'soap_version' => SOAP_1_1,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
'attachment_type' => BeSimpleSoapHelper::ATTACHMENTS_TYPE_SWA,
'cache_wsdl' => WSDL_CACHE_NONE,
);
/*
* Deploy "axis_services/besimple-swa.aar" to Apache Axis2 to get this
* example to work.
*
* Run ant to rebuild aar.
*
* Example based on:
* http://axis.apache.org/axis2/java/core/docs/mtom-guide.html#a3
* http://wso2.org/library/1675
*
* Doesn't work directly with ?wsdl served by Apache Axis!
*
*/
$sc = new BeSimpleSoapClient('SwA.wsdl', $options);
//var_dump($sc->__getFunctions());
//var_dump($sc->__getTypes());
try {
$file = new stdClass();
$file->name = 'upload.txt';
$file->data = 'This is a test text!';
$result = $sc->uploadFile($file);
var_dump(
$result->return
);
$file = new stdClass();
$file->name = 'upload.txt';
$result = $sc->downloadFile($file);
var_dump(
$result->data
);
$file = new stdClass();
$file->name = 'image.jpg'; // source: http://www.freeimageslive.com/galleries/light/pics/swirl3768.jpg
$file->data = file_get_contents('image.jpg');
$result = $sc->uploadFile($file);
var_dump(
$result->return
);
$crc32 = crc32($file->data);
$file = new stdClass();
$file->name = 'image.jpg';
$result = $sc->downloadFile($file);
file_put_contents('image2.jpg', $result->data);
var_dump(
crc32($result->data) === $crc32
);
} catch (Exception $e) {
var_dump($e);
}
// var_dump(
// $sc->__getLastRequestHeaders(),
// $sc->__getLastRequest(),
// $sc->__getLastResponseHeaders(),
// $sc->__getLastResponse()
// );

162
tests/AxisInterop/SwA.wsdl Normal file
View File

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:ns1="http://org.apache.axis2/xsd" xmlns:ns="http://service.besimple" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" targetNamespace="http://service.besimple">
<wsdl:documentation>BeSimpleSwaService</wsdl:documentation>
<wsdl:types>
<xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://service.besimple">
<xs:complexType name="Exception">
<xs:sequence>
<xs:element minOccurs="0" name="Exception" nillable="true" type="xs:anyType"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Exception">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="Exception" nillable="true" type="ns:Exception"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="uploadFile">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="data" nillable="true" type="xs:base64Binary"/>
<xs:element minOccurs="0" name="name" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="uploadFileResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="return" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="downloadFile">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="name" nillable="true" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="downloadFileResponse">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="data" nillable="true" type="xs:base64Binary"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>
<wsdl:message name="downloadFileRequest">
<wsdl:part name="parameters" element="ns:downloadFile"/>
</wsdl:message>
<wsdl:message name="downloadFileResponse">
<wsdl:part name="parameters" element="ns:downloadFileResponse"/>
</wsdl:message>
<wsdl:message name="Exception">
<wsdl:part name="parameters" element="ns:Exception"/>
</wsdl:message>
<wsdl:message name="uploadFileRequest">
<wsdl:part name="parameters" element="ns:uploadFile"/>
</wsdl:message>
<wsdl:message name="uploadFileResponse">
<wsdl:part name="parameters" element="ns:uploadFileResponse"/>
</wsdl:message>
<wsdl:portType name="BeSimpleSwaServicePortType">
<wsdl:operation name="downloadFile">
<wsdl:input message="ns:downloadFileRequest" wsaw:Action="urn:downloadFile"/>
<wsdl:output message="ns:downloadFileResponse" wsaw:Action="urn:downloadFileResponse"/>
<wsdl:fault message="ns:Exception" name="Exception" wsaw:Action="urn:downloadFileException"/>
</wsdl:operation>
<wsdl:operation name="uploadFile">
<wsdl:input message="ns:uploadFileRequest" wsaw:Action="urn:uploadFile"/>
<wsdl:output message="ns:uploadFileResponse" wsaw:Action="urn:uploadFileResponse"/>
<wsdl:fault message="ns:Exception" name="Exception" wsaw:Action="urn:uploadFileException"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="BeSimpleSwaServiceSoap11Binding" type="ns:BeSimpleSwaServicePortType">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="downloadFile">
<soap:operation soapAction="urn:downloadFile" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap:fault use="literal" name="Exception"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="uploadFile">
<soap:operation soapAction="urn:uploadFile" style="document"/>
<wsdl:input>
<soap:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap:fault use="literal" name="Exception"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="BeSimpleSwaServiceSoap12Binding" type="ns:BeSimpleSwaServicePortType">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
<wsdl:operation name="downloadFile">
<soap12:operation soapAction="urn:downloadFile" style="document"/>
<wsdl:input>
<soap12:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap12:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap12:fault use="literal" name="Exception"/>
</wsdl:fault>
</wsdl:operation>
<wsdl:operation name="uploadFile">
<soap12:operation soapAction="urn:uploadFile" style="document"/>
<wsdl:input>
<soap12:body use="literal"/>
</wsdl:input>
<wsdl:output>
<soap12:body use="literal"/>
</wsdl:output>
<wsdl:fault name="Exception">
<soap12:fault use="literal" name="Exception"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
<wsdl:binding name="BeSimpleSwaServiceHttpBinding" type="ns:BeSimpleSwaServicePortType">
<http:binding verb="POST"/>
<wsdl:operation name="downloadFile">
<http:operation location="BeSimpleSwaService/downloadFile"/>
<wsdl:input>
<mime:content type="text/xml" part="downloadFile"/>
</wsdl:input>
<wsdl:output>
<mime:content type="text/xml" part="downloadFile"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="uploadFile">
<http:operation location="BeSimpleSwaService/uploadFile"/>
<wsdl:input>
<mime:content type="text/xml" part="uploadFile"/>
</wsdl:input>
<wsdl:output>
<mime:content type="text/xml" part="uploadFile"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="BeSimpleSwaService">
<wsdl:port name="BeSimpleSwaServiceHttpSoap11Endpoint" binding="ns:BeSimpleSwaServiceSoap11Binding">
<soap:address location="http://localhost:8080/axis2/services/BeSimpleSwaService.BeSimpleSwaServiceHttpSoap11Endpoint/"/>
</wsdl:port>
<wsdl:port name="BeSimpleSwaServiceHttpSoap12Endpoint" binding="ns:BeSimpleSwaServiceSoap12Binding">
<soap12:address location="http://localhost:8080/axis2/services/BeSimpleSwaService.BeSimpleSwaServiceHttpSoap12Endpoint/"/>
</wsdl:port>
<wsdl:port name="BeSimpleSwaServiceHttpEndpoint" binding="ns:BeSimpleSwaServiceHttpBinding">
<http:address location="http://localhost:8080/axis2/services/BeSimpleSwaService.BeSimpleSwaServiceHttpEndpoint/"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@ -0,0 +1,38 @@
<project name="BeSimpleSwaService" default="generate.service">
<property environment="env" />
<property name="axis2.home" value="C:/axis2" />
<property name="axis2.repo" value="${axis2.home}/repository" />
<property name="build.dir" value="build" />
<property name="filename" value="besimple-swa.aar" />
<path id="axis.classpath">
<fileset dir="${axis2.home}/lib">
<include name="*.jar" />
</fileset>
</path>
<target name="generate.service" depends="compile">
<jar destfile="${build.dir}/${filename}">
<fileset dir="resources/">
<include name="META-INF/services.xml" />
</fileset>
<fileset dir="${build.dir}/classes">
<include name="besimple/service/**/*.class" />
</fileset>
</jar>
<copy file="${build.dir}/${filename}" tofile="../axis_services/${filename}" overwrite="true" />
<copy file="${build.dir}/${filename}" tofile="${axis2.repo}/services/${filename}" overwrite="true" />
<antcall target="clean" />
</target>
<target name="compile">
<mkdir dir="${build.dir}/classes" />
<javac debug="on" srcdir="src" destdir="${build.dir}/classes">
<classpath refid="axis.classpath" />
</javac>
</target>
<target name="clean">
<delete dir="${build.dir}" />
</target>
</project>

View File

@ -0,0 +1,15 @@
<serviceGroup>
<service name="BeSimpleSwaService">
<description>BeSimple test service for SwA.</description>
<parameter name="enableSwA">true</parameter>
<parameter name="ServiceClass" locked="false">besimple.service.BeSimpleSwaService</parameter>
<operation name="uploadFile">
<actionMapping>urn:uploadFile</actionMapping>
<messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
</operation>
<operation name="downloadFile">
<actionMapping>urn:downloadFile</actionMapping>
<messageReceiver class="org.apache.axis2.receivers.RawXMLINOutMessageReceiver"/>
</operation>
</service>
</serviceGroup>

View File

@ -0,0 +1,78 @@
package besimple.service;
import java.io.File;
import java.io.FileOutputStream;
import javax.xml.namespace.QName;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import org.apache.axiom.attachments.Attachments;
import org.apache.axiom.om.OMAbstractFactory;
import org.apache.axiom.om.OMAttribute;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMFactory;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.OperationContext;
import org.apache.axis2.wsdl.WSDLConstants;
public class BeSimpleSwaService {
String namespace = "http://service.besimple";
public OMElement uploadFile(OMElement element) throws Exception {
OMElement dataElement = (OMElement)element.getFirstChildWithName(new QName(namespace, "data"));
OMAttribute hrefAttribute = dataElement.getAttribute(new QName("href"));
String contentID = hrefAttribute.getAttributeValue();
contentID = contentID.trim();
if (contentID.substring(0, 3).equalsIgnoreCase("cid")) {
contentID = contentID.substring(4);
}
OMElement nameElement = (OMElement)element.getFirstChildWithName(new QName(namespace, "name"));
String name = nameElement.getText();
MessageContext msgCtx = MessageContext.getCurrentMessageContext();
Attachments attachment = msgCtx.getAttachmentMap();
DataHandler dataHandler = attachment.getDataHandler(contentID);
File file = new File(name);
FileOutputStream fileOutputStream = new FileOutputStream(file);
dataHandler.writeTo(fileOutputStream);
fileOutputStream.flush();
fileOutputStream.close();
OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace omNs = factory.createOMNamespace(namespace, "swa");
OMElement wrapperElement = factory.createOMElement("uploadFileResponse", omNs);
OMElement returnElement = factory.createOMElement("return", omNs, wrapperElement);
returnElement.setText("File saved succesfully.");
return wrapperElement;
}
public OMElement downloadFile(OMElement element) throws Exception {
OMElement nameElement = (OMElement)element.getFirstChildWithName(new QName(namespace, "name"));
String name = nameElement.getText();
MessageContext msgCtxIn = MessageContext.getCurrentMessageContext();
OperationContext operationContext = msgCtxIn.getOperationContext();
MessageContext msgCtxOut = operationContext.getMessageContext(WSDLConstants.MESSAGE_LABEL_OUT_VALUE);
FileDataSource fileDataSource = new FileDataSource(name);
DataHandler dataHandler = new DataHandler(fileDataSource);
String contentID = "cid:" + msgCtxOut.addAttachment(dataHandler);
OMFactory factory = OMAbstractFactory.getOMFactory();
OMNamespace omNs = factory.createOMNamespace(namespace, "swa");
OMElement wrapperElement = factory.createOMElement("downloadFileResponse", omNs);
OMElement dataElement = factory.createOMElement("data", omNs, wrapperElement);
dataElement.addAttribute("href", contentID, null);
return wrapperElement;
}
}

View File

@ -0,0 +1,75 @@
<?php
use BeSimple\SoapClient\SoapClient as BeSimpleSoapClient;
use BeSimple\SoapClient\WsAddressingFilter as BeSimpleWsAddressingFilter;
require '../bootstrap.php';
echo '<pre>';
$options = array(
'soap_version' => SOAP_1_2,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
);
/*
* Deploy "axis_services/version2.aar" to Apache Axis2 to get this example to
* work.
*
* To rebuild the "axis_services/version2.aar" the following steps need to be
* done to build a working Apache Axis2 version service with SOAP session
* enabled.
*
* 1) Go to $AXIS_HOME/samples/version and edit the following files:
*
* resources/META-INF/services.xml:
* <service name="Version2" scope="soapsession">
* ...
* </service>
*
* build.xml:
* replace version.aar with version2.aar
*
* 2) Run ant build.xml in "$AXIS_HOME/samples/version"
*
*/
$sc = new BeSimpleSoapClient('http://localhost:8080/axis2/services/Version2?wsdl', $options);
$soapKernel = $sc->getSoapKernel();
$wsaFilter = new BeSimpleWsAddressingFilter();
$soapKernel->registerFilter($wsaFilter);
//var_dump($sc->__getFunctions());
//var_dump($sc->__getTypes());
try {
$wsaFilter->setReplyTo(BeSimpleWsAddressingFilter::ENDPOINT_REFERENCE_ANONYMOUS);
$wsaFilter->setMessageId();
var_dump($sc->getVersion());
$soapSessionId1 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId');
echo 'ID1: ' .$soapSessionId1 . PHP_EOL;
$wsaFilter->addReferenceParameter('http://ws.apache.org/namespaces/axis2', 'axis2', 'ServiceGroupId', $soapSessionId1);
var_dump($sc->getVersion());
$soapSessionId2 = $wsaFilter->getReferenceParameter('http://ws.apache.org/namespaces/axis2', 'ServiceGroupId');
echo 'ID2: ' . $soapSessionId2 . PHP_EOL;
if ($soapSessionId1 == $soapSessionId2) {
echo PHP_EOL;
echo 'SOAP session worked :)';
}
} catch (Exception $e) {
var_dump($e);
}
// var_dump(
// $sc->__getLastRequestHeaders(),
// $sc->__getLastRequest(),
// $sc->__getLastResponseHeaders(),
// $sc->__getLastResponse()
// );

View File

@ -0,0 +1,116 @@
<?php
use ass\XmlSecurity\Key as XmlSecurityKey;
use BeSimple\SoapClient\SoapClient as BeSimpleSoapClient;
use BeSimple\SoapClient\WsSecurityFilter as BeSimpleWsSecurityFilter;
use BeSimple\SoapCommon\WsSecurityKey as BeSimpleWsSecurityKey;
require '../bootstrap.php';
echo '<pre>';
$options = array(
'soap_version' => SOAP_1_2,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
);
/*
* Deploy "axis_services/library-signencr.aar" to Apache Axis2 to get this
* example to work.
*
* Links:
* http://www.dcc.uchile.cl/~pcamacho/tutorial/web/xmlsec/xmlsec.html
* http://www.aleksey.com/xmlsec/xmldsig-verifier.html
*
* Using code from axis example:
* http://www.ibm.com/developerworks/java/library/j-jws5/index.html
*
* Download key tool to export private key
* http://couchpotato.net/pkeytool/
*
* keytool -export -alias serverkey -keystore server.keystore -storepass nosecret -file servercert.cer
* openssl x509 -out servercert.pem -outform pem -in servercert.pem -inform der
*
* keytool -export -alias clientkey -keystore client.keystore -storepass nosecret -file clientcert.cer
* openssl x509 -out clientcert.pem -outform pem -in clientcert.pem -inform der
* java -jar pkeytool.jar -exportkey -keystore client.keystore -storepass nosecret -keypass clientpass -rfc -alias clientkey -file clientkey.pem
*
* C:\Program Files\Java\jre6\bin\keytool -export -alias serverkey -keystore server.keystore -storepass nosecret -file servercert.cer
* C:\xampp\apache\bin\openssl x509 -out servercert.pem -outform pem -in servercert.cer -inform der
*
* C:\Program Files\Java\jre6\bin\keytool -export -alias clientkey -keystore client.keystore -storepass nosecret -file clientcert.cer
* C:\xampp\apache\bin\openssl x509 -out clientcert.pem -outform pem -in clientcert.cer -inform der
* java -jar C:\axis2\pkeytool\pkeytool.jar -exportkey -keystore client.keystore -storepass nosecret -keypass clientpass -rfc -alias clientkey -file clientkey.pem
*
* build.properties:
* server-policy=hash-policy-server.xml
*
* allows both text and digest!
*/
class getBook {}
class getBookResponse {}
class getBooksByType {}
class getBooksByTypeResponse {}
class addBook {}
class addBookResponse {}
class BookInformation {}
$options['classmap'] = array(
'getBook' => 'getBook',
'getBookResponse' => 'getBookResponse',
'getBooksByType' => 'getBooksByType',
'getBooksByTypeResponse' => 'getBooksByTypeResponse',
'addBook' => 'addBook',
'addBookResponse' => 'addBookResponse',
'BookInformation' => 'BookInformation',
);
$sc = new BeSimpleSoapClient('WsSecuritySigEnc.wsdl', $options);
$wssFilter = new BeSimpleWsSecurityFilter();
// user key for signature and encryption
$securityKeyUser = new BeSimpleWsSecurityKey();
$securityKeyUser->addPrivateKey(XmlSecurityKey::RSA_SHA1, 'clientkey.pem', true);
$securityKeyUser->addPublicKey(XmlSecurityKey::RSA_SHA1, 'clientcert.pem', true);
$wssFilter->setUserSecurityKeyObject($securityKeyUser);
// service key for encryption
$securityKeyService = new BeSimpleWsSecurityKey();
$securityKeyService->addPrivateKey(XmlSecurityKey::TRIPLEDES_CBC);
$securityKeyService->addPublicKey(XmlSecurityKey::RSA_1_5, 'servercert.pem', true);
$wssFilter->setServiceSecurityKeyObject($securityKeyService);
// TOKEN_REFERENCE_SUBJECT_KEY_IDENTIFIER | TOKEN_REFERENCE_SECURITY_TOKEN | TOKEN_REFERENCE_THUMBPRINT_SHA1
$wssFilter->setSecurityOptionsSignature(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_SECURITY_TOKEN);
$wssFilter->setSecurityOptionsEncryption(BeSimpleWsSecurityFilter::TOKEN_REFERENCE_THUMBPRINT_SHA1);
$soapKernel = $sc->getSoapKernel();
$soapKernel->registerFilter($wssFilter);
//var_dump($sc->__getFunctions());
//var_dump($sc->__getTypes());
try {
$gb = new getBook();
$gb->isbn = '0061020052';
var_dump($sc->getBook($gb));
$ab = new addBook();
$ab->isbn = '0445203498';
$ab->title = 'The Dragon Never Sleeps';
$ab->author = 'Cook, Glen';
$ab->type = 'scifi';
var_dump($sc->addBook($ab));
// getBooksByType("scifi");
} catch (Exception $e) {
var_dump($e);
}
//var_dump(
// $sc->__getLastRequestHeaders(),
// $sc->__getLastRequest(),
// $sc->__getLastResponseHeaders(),
// $sc->__getLastResponse()
//);

View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
xmlns:wns="http://ws.sosnoski.com/library/wsdl"
xmlns:tns="http://ws.sosnoski.com/library/types"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:types>
<schema elementFormDefault="qualified"
targetNamespace="http://ws.sosnoski.com/library/wsdl"
xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://ws.sosnoski.com/library/types"/>
<element name="getBook">
<complexType>
<sequence>
<element name="isbn" type="string"/>
</sequence>
</complexType>
</element>
<element name="getBookResponse">
<complexType>
<sequence>
<element name="getBookReturn" minOccurs="0" type="tns:BookInformation"/>
</sequence>
</complexType>
</element>
<element name="getBooksByType">
<complexType>
<sequence>
<element name="type" type="string"/>
</sequence>
</complexType>
</element>
<element name="getBooksByTypeResponse">
<complexType>
<sequence>
<element name="getBooksByTypeReturn" minOccurs="0" maxOccurs="unbounded" type="tns:BookInformation"/>
</sequence>
</complexType>
</element>
<element name="addBook">
<complexType>
<sequence>
<element name="type" type="string"/>
<element name="isbn" type="string"/>
<element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
<element name="title" type="string"/>
</sequence>
</complexType>
</element>
<element name="addBookResponse">
<complexType>
<sequence>
<element name="addBookReturn" type="boolean"/>
</sequence>
</complexType>
</element>
</schema>
<schema elementFormDefault="qualified"
targetNamespace="http://ws.sosnoski.com/library/types"
xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="BookInformation">
<sequence>
<element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
<element name="title" type="string"/>
</sequence>
<attribute name="type" use="required" type="string"/>
<attribute name="isbn" use="required" type="string"/>
</complexType>
</schema>
</wsdl:types>
<wsdl:message name="getBookRequest">
<wsdl:part element="wns:getBook" name="parameters"/>
</wsdl:message>
<wsdl:message name="getBookResponse">
<wsdl:part element="wns:getBookResponse" name="parameters"/>
</wsdl:message>
<wsdl:message name="getBooksByTypeRequest">
<wsdl:part element="wns:getBooksByType" name="parameters"/>
</wsdl:message>
<wsdl:message name="getBooksByTypeResponse">
<wsdl:part element="wns:getBooksByTypeResponse" name="parameters"/>
</wsdl:message>
<wsdl:message name="addBookRequest">
<wsdl:part element="wns:addBook" name="parameters"/>
</wsdl:message>
<wsdl:message name="addBookResponse">
<wsdl:part element="wns:addBookResponse" name="parameters"/>
</wsdl:message>
<wsdl:portType name="Library">
<wsdl:operation name="getBook">
<wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
<wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
</wsdl:operation>
<wsdl:operation name="getBooksByType">
<wsdl:input message="wns:getBooksByTypeRequest" name="getBooksByTypeRequest"/>
<wsdl:output message="wns:getBooksByTypeResponse" name="getBooksByTypeResponse"/>
</wsdl:operation>
<wsdl:operation name="addBook">
<wsdl:input message="wns:addBookRequest" name="addBookRequest"/>
<wsdl:output message="wns:addBookResponse" name="addBookResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="LibrarySoapBinding" type="wns:Library">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getBook">
<wsdlsoap:operation soapAction="urn:getBook"/>
<wsdl:input name="getBookRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getBookResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getBooksByType">
<wsdlsoap:operation soapAction="urn:getBooksByType"/>
<wsdl:input name="getBooksByTypeRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getBooksByTypeResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="addBook">
<wsdlsoap:operation soapAction="urn:addBook"/>
<wsdl:input name="addBookRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="addBookResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="library-signencr">
<wsdl:port binding="wns:LibrarySoapBinding" name="library">
<wsdlsoap:address location="http://localhost:8080/axis2/services/library-signencr"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

View File

@ -0,0 +1,81 @@
<?php
use BeSimple\SoapClient\SoapClient as BeSimpleSoapClient;
use BeSimple\SoapClient\WsSecurityFilter as BeSimpleWsSecurityFilter;
require '../bootstrap.php';
echo '<pre>';
$options = array(
'soap_version' => SOAP_1_2,
'features' => SOAP_SINGLE_ELEMENT_ARRAYS, // make sure that result is array for size=1
'trace' => true, // enables use of the methods SoapClient->__getLastRequest, SoapClient->__getLastRequestHeaders, SoapClient->__getLastResponse and SoapClient->__getLastResponseHeaders
);
/*
* Deploy "axis_services/library-username-digest.aar" to Apache Axis2 to get
* this example to work.
*
* Using code from axis example:
* http://www.ibm.com/developerworks/java/library/j-jws4/index.html
*
* build.properties:
* server-policy=hash-policy-server.xml
*
* allows both text and digest!
*/
class getBook {}
class getBookResponse {}
class getBooksByType {}
class getBooksByTypeResponse {}
class addBook {}
class addBookResponse {}
class BookInformation {}
$options['classmap'] = array(
'getBook' => 'getBook',
'getBookResponse' => 'getBookResponse',
'getBooksByType' => 'getBooksByType',
'getBooksByTypeResponse' => 'getBooksByTypeResponse',
'addBook' => 'addBook',
'addBookResponse' => 'addBookResponse',
'BookInformation' => 'BookInformation',
);
$sc = new BeSimpleSoapClient('WsSecurityUserPass.wsdl', $options);
$wssFilter = new BeSimpleWsSecurityFilter(true, 600);
$wssFilter->addUserData('libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_TEXT);
//$wssFilter->addUserData( 'libuser', 'books', BeSimpleWsSecurityFilter::PASSWORD_TYPE_DIGEST );
$soapKernel = $sc->getSoapKernel();
$soapKernel->registerFilter($wssFilter);
//var_dump($sc->__getFunctions());
//var_dump($sc->__getTypes());
try {
$gb = new getBook();
$gb->isbn = '0061020052';
var_dump($sc->getBook($gb));
$ab = new addBook();
$ab->isbn = '0445203498';
$ab->title = 'The Dragon Never Sleeps';
$ab->author = 'Cook, Glen';
$ab->type = 'scifi';
var_dump($sc->addBook($ab));
// getBooksByType("scifi");
} catch (Exception $e) {
var_dump($e);
}
//var_dump(
// $sc->__getLastRequestHeaders(),
// $sc->__getLastRequest(),
// $sc->__getLastResponseHeaders(),
// $sc->__getLastResponse()
//);

View File

@ -0,0 +1,184 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
xmlns:wns="http://ws.sosnoski.com/library/wsdl"
xmlns:tns="http://ws.sosnoski.com/library/types"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
<wsdl:types>
<schema elementFormDefault="qualified"
targetNamespace="http://ws.sosnoski.com/library/wsdl"
xmlns="http://www.w3.org/2001/XMLSchema">
<import namespace="http://ws.sosnoski.com/library/types"/>
<element name="getBook">
<complexType>
<sequence>
<element name="isbn" type="string"/>
</sequence>
</complexType>
</element>
<element name="getBookResponse">
<complexType>
<sequence>
<element name="getBookReturn" minOccurs="0" type="tns:BookInformation"/>
</sequence>
</complexType>
</element>
<element name="getBooksByType">
<complexType>
<sequence>
<element name="type" type="string"/>
</sequence>
</complexType>
</element>
<element name="getBooksByTypeResponse">
<complexType>
<sequence>
<element name="getBooksByTypeReturn" minOccurs="0" maxOccurs="unbounded" type="tns:BookInformation"/>
</sequence>
</complexType>
</element>
<element name="addBook">
<complexType>
<sequence>
<element name="type" type="string"/>
<element name="isbn" type="string"/>
<element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
<element name="title" type="string"/>
</sequence>
</complexType>
</element>
<element name="addBookResponse">
<complexType>
<sequence>
<element name="addBookReturn" type="boolean"/>
</sequence>
</complexType>
</element>
</schema>
<schema elementFormDefault="qualified"
targetNamespace="http://ws.sosnoski.com/library/types"
xmlns="http://www.w3.org/2001/XMLSchema">
<complexType name="BookInformation">
<sequence>
<element name="author" minOccurs="0" maxOccurs="unbounded" type="string"/>
<element name="title" type="string"/>
</sequence>
<attribute name="type" use="required" type="string"/>
<attribute name="isbn" use="required" type="string"/>
</complexType>
</schema>
</wsdl:types>
<wsdl:message name="getBookRequest">
<wsdl:part element="wns:getBook" name="parameters"/>
</wsdl:message>
<wsdl:message name="getBookResponse">
<wsdl:part element="wns:getBookResponse" name="parameters"/>
</wsdl:message>
<wsdl:message name="getBooksByTypeRequest">
<wsdl:part element="wns:getBooksByType" name="parameters"/>
</wsdl:message>
<wsdl:message name="getBooksByTypeResponse">
<wsdl:part element="wns:getBooksByTypeResponse" name="parameters"/>
</wsdl:message>
<wsdl:message name="addBookRequest">
<wsdl:part element="wns:addBook" name="parameters"/>
</wsdl:message>
<wsdl:message name="addBookResponse">
<wsdl:part element="wns:addBookResponse" name="parameters"/>
</wsdl:message>
<wsdl:portType name="Library">
<wsdl:operation name="getBook">
<wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
<wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
</wsdl:operation>
<wsdl:operation name="getBooksByType">
<wsdl:input message="wns:getBooksByTypeRequest" name="getBooksByTypeRequest"/>
<wsdl:output message="wns:getBooksByTypeResponse" name="getBooksByTypeResponse"/>
</wsdl:operation>
<wsdl:operation name="addBook">
<wsdl:input message="wns:addBookRequest" name="addBookRequest"/>
<wsdl:output message="wns:addBookResponse" name="addBookResponse"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="LibrarySoapBinding" type="wns:Library">
<wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="getBook">
<wsdlsoap:operation soapAction="urn:getBook"/>
<wsdl:input name="getBookRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getBookResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="getBooksByType">
<wsdlsoap:operation soapAction="urn:getBooksByType"/>
<wsdl:input name="getBooksByTypeRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="getBooksByTypeResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
<wsdl:operation name="addBook">
<wsdlsoap:operation soapAction="urn:addBook"/>
<wsdl:input name="addBookRequest">
<wsdlsoap:body use="literal"/>
</wsdl:input>
<wsdl:output name="addBookResponse">
<wsdlsoap:body use="literal"/>
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="library-username">
<wsdl:port binding="wns:LibrarySoapBinding" name="library">
<wsdlsoap:address location="http://localhost:8080/axis2/services/library-username"/>
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICoDCCAgkCBEnhw2IwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNVBAYTAk5aMRMw
EQYDVQQIEwpXZWxsaW5ndG9uMRowGAYDVQQHExFQYXJhcGFyYXVtdSBCZWFjaDEq
MCgGA1UEChMhU29zbm9za2kgU29mdHdhcmUgQXNzb2NpYXRlcyBMdGQuMRAwDgYD
VQQLEwdVbmtub3duMRgwFgYDVQQDEw9EZW5uaXMgU29zbm9za2kwHhcNMDkwNDEy
MTAzMzA2WhcNMzYwODI3MTAzMzA2WjCBljELMAkGA1UEBhMCTloxEzARBgNVBAgT
CldlbGxpbmd0b24xGjAYBgNVBAcTEVBhcmFwYXJhdW11IEJlYWNoMSowKAYDVQQK
EyFTb3Nub3NraSBTb2Z0d2FyZSBBc3NvY2lhdGVzIEx0ZC4xEDAOBgNVBAsTB1Vu
a25vd24xGDAWBgNVBAMTD0Rlbm5pcyBTb3Nub3NraTCBnzANBgkqhkiG9w0BAQEF
AAOBjQAwgYkCgYEAhOVyNK8xyxtb4DnKtU6mF9KoiFqCk7eKoLE26+9h410CtTkx
zWAfgnR+8i+LPbdsPY+yXAo6NYpCCKolXfDLe+AG2GwnMZGrIl6+BLF3hqTmIXBF
TLGUmC7A7uBTivaWgdH1w3hb33rASoVU67BVtQ3QQi99juZX4vU9o9pScocCAwEA
ATANBgkqhkiG9w0BAQUFAAOBgQBMNPo1KAGbz8Jl6HGbtAcetieSJ3bEAXmv1tcj
ysBS67AXzdu1Ac+onHh2EpzBM7kuGbw+trU+AhulooPpewIQRApXP1F0KHRDcbqW
jwvknS6HnomN9572giLGKn2601bHiRUj35hiA8aLmMUBppIRPFFAoQ0QUBCPx+m8
/0n33w==
-----END CERTIFICATE-----

View File

@ -0,0 +1,14 @@
-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAITlcjSvMcsbW+A5yrVOphfSqIha
gpO3iqCxNuvvYeNdArU5Mc1gH4J0fvIviz23bD2PslwKOjWKQgiqJV3wy3vgBthsJzGRqyJevgSx
d4ak5iFwRUyxlJguwO7gU4r2loHR9cN4W996wEqFVOuwVbUN0EIvfY7mV+L1PaPaUnKHAgMBAAEC
gYAZ6UqtLwN8YGc3fs0hMKZ9upsViuAuwPiMgED/G3twgzAF+ZLWQkmie+hMfCyf6eV200+pVm0n
Bz/8xH/oowxpX0Kk3szoB4vFghjU84GKUcrbhu/NRIm7l3drnfbzqhQkHDCx6n1CotI4Gs49cDWu
4uEAuxJkEIVY553unZjZgQJBAOJVIallNKmD0iQlvtWRmRzpmYDjt9vhNY6WBTIOx6SDn9SRaoSA
fkipQ2HXo04r78TQ674+zfZ1lRTkFG7px6ECQQCWUPHp3pSZOM1oGzJrNvNaw+MizZAZjq34npHm
9GRquFLG7BlCaI9QNGE7pN2ryYsYCRUMaM2e4GR0tUXxVGknAkAgrxqFU9AfCqI2Bh1gyf3KZxF7
w2axofwR8ygc6nV6FGfoUneHWubhp0/LuVAj4cRmL6Vbe8ZSaPh2Y9lviuMBAkEAicP8Q+1E4j1m
PPEYP51oYprANOiUFmhnWEL00+jPk+QFsd03tV6hYs/vAbwzkjuwqMHCMdJoCiH8z95IEUvc5wJA
MvLOuZdu4dmhOXg/YKsbMSPjFNEVskLQNSXqw6O2wIrpPg1NQvBBAOTbiuZj3vind4VPos1wc4vB
QocvdUC6dA==
-----END PRIVATE KEY-----

BIN
tests/AxisInterop/image.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICoDCCAgkCBEnhwzMwDQYJKoZIhvcNAQEFBQAwgZYxCzAJBgNVBAYTAk5aMRMw
EQYDVQQIEwpXZWxsaW5ndG9uMRowGAYDVQQHExFQYXJhcGFyYXVtdSBCZWFjaDEq
MCgGA1UEChMhU29zbm9za2kgU29mdHdhcmUgQXNzb2NpYXRlcyBMdGQuMRAwDgYD
VQQLEwdVbmtub3duMRgwFgYDVQQDEw9EZW5uaXMgU29zbm9za2kwHhcNMDkwNDEy
MTAzMjE5WhcNMzYwODI3MTAzMjE5WjCBljELMAkGA1UEBhMCTloxEzARBgNVBAgT
CldlbGxpbmd0b24xGjAYBgNVBAcTEVBhcmFwYXJhdW11IEJlYWNoMSowKAYDVQQK
EyFTb3Nub3NraSBTb2Z0d2FyZSBBc3NvY2lhdGVzIEx0ZC4xEDAOBgNVBAsTB1Vu
a25vd24xGDAWBgNVBAMTD0Rlbm5pcyBTb3Nub3NraTCBnzANBgkqhkiG9w0BAQEF
AAOBjQAwgYkCgYEA1H3mjQCF9uce2jmm/Yq9kE4ytfvkp4c8G90cDfJXJvOiGQds
p2vDZXKuCkHQ7vsBBXPNTt8J/d8ZbEwyuB9Ccz5pJqi6Ig6Y2/mEsPthDyh5SrJV
yQ/wxUGwmfSuwdrIMnplMTq+OR9BOfT3CvjSvuy9d6BQNo4wOMkDvmZTtI8CAwEA
ATANBgkqhkiG9w0BAQUFAAOBgQCqv4475QaqlKcN2QCZJbLVKZEX+76XLQurGkgf
2fCgesRHjfUfOHyTTlhWQdEKTcBB2XviUyyW6I//fmKfXUIiQqvgh4LHdXRPEXDf
Y9nr89MjyQpDlnl6AlrvSej30a9iwVRUeVk4d6gxWHMRonKBFgh+TGexxUXHtPkf
B1Pdtg==
-----END CERTIFICATE-----

View File

@ -0,0 +1,180 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapClient\Curl;
/**
* @author Andreas Schamberger <mail@andreass.net>
*/
class CurlTest extends \PHPUnit_Framework_TestCase
{
protected $webserverProcessId;
protected function startPhpWebserver()
{
if ('Windows' == substr(php_uname('s'), 0, 7 )) {
$powershellCommand = "\$app = start-process php.exe -ArgumentList '-S localhost:8000 -t ".__DIR__.DIRECTORY_SEPARATOR."Fixtures' -WindowStyle 'Hidden' -passthru; Echo \$app.Id;";
$shellCommand = 'powershell -command "& {'.$powershellCommand.'}"';
} else {
$shellCommand = "nohup php -S localhost:8000 -t ".__DIR__.DIRECTORY_SEPARATOR."Fixtures &";
}
$output = array();
exec($shellCommand, $output);
$this->webserverProcessId = $output[0]; // pid is in first element
}
protected function stopPhpWebserver()
{
if (!is_null($this->webserverProcessId)) {
if ('Windows' == substr(php_uname('s'), 0, 7 )) {
exec('TASKKILL /F /PID ' . $this->webserverProcessId);
} else {
exec('kill ' . $this->webserverProcessId);
}
$this->webserverProcessId = null;
}
}
public function testExec()
{
$this->startPhpWebserver();
$curl = new Curl();
$this->assertTrue($curl->exec('http://localhost:8000/curl.txt'));
$this->assertTrue($curl->exec('http://localhost:8000/404.txt'));
$this->stopPhpWebserver();
}
public function testGetErrorMessage()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://unknown/curl.txt');
$this->assertEquals('Could not connect to host', $curl->getErrorMessage());
$curl->exec('xyz://localhost:8000/@404.txt');
$this->assertEquals('Unknown protocol. Only http and https are allowed.', $curl->getErrorMessage());
$curl->exec('');
$this->assertEquals('Unable to parse URL', $curl->getErrorMessage());
$this->stopPhpWebserver();
}
public function testGetRequestHeaders()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://localhost:8000/curl.txt');
$this->assertEquals(136, strlen($curl->getRequestHeaders()));
$curl->exec('http://localhost:8000/404.txt');
$this->assertEquals(135, strlen($curl->getRequestHeaders()));
$this->stopPhpWebserver();
}
public function testGetResponse()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://localhost:8000/curl.txt');
$this->assertEquals(150, strlen($curl->getResponse()));
$curl->exec('http://localhost:8000/404.txt');
$this->assertEquals(1282, strlen($curl->getResponse()));
$this->stopPhpWebserver();
}
public function testGetResponseBody()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://localhost:8000/curl.txt');
$this->assertEquals('This is a testfile for cURL.', $curl->getResponseBody());
$this->stopPhpWebserver();
}
public function testGetResponseContentType()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://localhost:8000/curl.txt');
$this->assertEquals('text/plain; charset=UTF-8', $curl->getResponseContentType());
$curl->exec('http://localhost:8000/404.txt');
$this->assertEquals('text/html; charset=UTF-8', $curl->getResponseContentType());
$this->stopPhpWebserver();
}
public function testGetResponseHeaders()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://localhost:8000/curl.txt');
$this->assertEquals(122, strlen($curl->getResponseHeaders()));
$curl->exec('http://localhost:8000/404.txt');
$this->assertEquals(130, strlen($curl->getResponseHeaders()));
$this->stopPhpWebserver();
}
public function testGetResponseStatusCode()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://localhost:8000/curl.txt');
$this->assertEquals(200, $curl->getResponseStatusCode());
$curl->exec('http://localhost:8000/404.txt');
$this->assertEquals(404, $curl->getResponseStatusCode());
$this->stopPhpWebserver();
}
public function testGetResponseStatusMessage()
{
$this->startPhpWebserver();
$curl = new Curl();
$curl->exec('http://localhost:8000/curl.txt');
$this->assertEquals('OK', $curl->getResponseStatusMessage());
$curl->exec('http://localhost:8000/404.txt');
$this->assertEquals('Not Found', $curl->getResponseStatusMessage());
$this->stopPhpWebserver();
}
}

View File

@ -0,0 +1 @@
This is a testfile for cURL.

View File

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

View File

@ -0,0 +1,15 @@
<?xml version="1.0"?>
<wsdl:types xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://test.sample">
<xs:element name="note">
<xs:complexType>
<xs:sequence>
<xs:element name="to" type="xs:string"/>
<xs:element name="from" type="xs:string"/>
<xs:element name="heading" type="xs:string"/>
<xs:element name="body" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
</wsdl:types>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<wsdl:documentation>wsdlincludetest</wsdl:documentation>
<wsdl:include location="http://localhost:8000/wsdl_include.wsdl"/>
</wsdl:definitions>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<wsdl:documentation>wsdlincludetest</wsdl:documentation>
<wsdl:include location="../wsdl_include.wsdl"/>
</wsdl:definitions>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<wsdl:documentation>xsdinctest</wsdl:documentation>
<wsdl:types>
<xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://test.sample">
<xs:include schemaLocation="http://localhost:8000/type_include.xsd"/>
</xs:schema>
</wsdl:types>
</wsdl:definitions>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<wsdl:documentation>xsdinctest</wsdl:documentation>
<wsdl:types>
<xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://test.sample">
<xs:include schemaLocation="../type_include.xsd"/>
</xs:schema>
</wsdl:types>
</wsdl:definitions>

View File

@ -1,113 +0,0 @@
<?php
/*
* This file is part of the BeSimpleSoapBundle.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\Tests\SoapClient;
use BeSimple\SoapClient\SoapRequest;
class SoapRequestTest extends \PHPUnit_Framework_TestCase
{
public function testSetFunction()
{
$soapRequest = new SoapRequest();
$soapRequest->setFunction('foo');
$this->assertEquals('foo', $soapRequest->getFunction());
}
public function testSetArguments()
{
$soapRequest = new SoapRequest();
$arguments = array(
'foo' => true,
'bar' => false,
);
$soapRequest->setArguments($arguments);
$this->assertEquals($arguments, $soapRequest->getArguments());
}
public function testGetArgument()
{
$soapRequest = new SoapRequest();
$this->assertSame(null, $soapRequest->getArgument('foo'));
$this->assertFalse($soapRequest->getArgument('foo', false));
$soapRequest->addArgument('foo', 'bar');
$this->assertEquals('bar', $soapRequest->getArgument('foo', false));
}
public function testSetOptions()
{
$soapRequest = new SoapRequest();
$options = array(
'uri' => 'foo',
'soapaction' => 'bar',
);
$soapRequest->setOptions($options);
$this->assertEquals($options, $soapRequest->getOptions());
}
public function testGetOption()
{
$soapRequest = new SoapRequest();
$this->assertSame(null, $soapRequest->getOption('soapaction'));
$this->assertFalse($soapRequest->getOption('soapaction', false));
$soapRequest->addOption('soapaction', 'foo');
$this->assertEquals('foo', $soapRequest->getOption('soapaction'));
}
public function testSetHeaders()
{
$soapRequest = new SoapRequest();
$this->assertEquals(null, $soapRequest->getHeaders());
$header1 = new \SoapHeader('foobar', 'foo', 'bar');
$header2 = new \SoapHeader('barfoo', 'bar', 'foo');
$soapRequest
->addHeader($header1)
->addHeader($header2)
;
$this->assertSame(array($header1, $header2), $soapRequest->getHeaders());
}
public function testConstruct()
{
$soapRequest = new SoapRequest();
$this->assertNull($soapRequest->getFunction());
$this->assertEquals(array(), $soapRequest->getArguments());
$this->assertEquals(array(), $soapRequest->getOptions());
$this->assertEquals(null, $soapRequest->getHeaders());
$arguments = array('bar' => 'foobar');
$options = array('soapaction' => 'foobar');
$headers = array(
new \SoapHeader('foobar', 'foo', 'bar'),
new \SoapHeader('barfoo', 'bar', 'foo'),
);
$soapRequest = new SoapRequest('foo', $arguments, $options, $headers);
$this->assertEquals('foo', $soapRequest->getFunction());
$this->assertEquals($arguments, $soapRequest->getArguments());
$this->assertEquals($options, $soapRequest->getOptions());
$this->assertSame($headers, $soapRequest->getHeaders());
}
}

View File

@ -0,0 +1,265 @@
<?php
/*
* This file is part of the BeSimpleSoapClient.
*
* (c) Christian Kerl <christian-kerl@web.de>
* (c) Francis Besset <francis.besset@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace BeSimple\SoapClient;
use BeSimple\SoapClient\WsdlDownloader;
use BeSimple\SoapClient\Curl;
/**
* @author Andreas Schamberger <mail@andreass.net>
*/
class WsdlDownloaderTest extends \PHPUnit_Framework_TestCase
{
protected $webserverProcessId;
protected function startPhpWebserver()
{
if ('Windows' == substr(php_uname('s'), 0, 7 )) {
$powershellCommand = "\$app = start-process php.exe -ArgumentList '-S localhost:8000 -t ".__DIR__.DIRECTORY_SEPARATOR."Fixtures' -WindowStyle 'Hidden' -passthru; Echo \$app.Id;";
$shellCommand = 'powershell -command "& {'.$powershellCommand.'}"';
} else {
$shellCommand = "nohup php -S localhost:8000 -t ".__DIR__.DIRECTORY_SEPARATOR."Fixtures &";
}
$output = array();
exec($shellCommand, $output);
$this->webserverProcessId = $output[0]; // pid is in first element
}
protected function stopPhpWebserver()
{
if (!is_null($this->webserverProcessId)) {
if ('Windows' == substr(php_uname('s'), 0, 7 )) {
exec('TASKKILL /F /PID ' . $this->webserverProcessId);
} else {
exec('kill ' . $this->webserverProcessId);
}
$this->webserverProcessId = null;
}
}
public function testDownload()
{
$this->startPhpWebserver();
$curl = new Curl();
$wd = new WsdlDownloader($curl);
$cacheDir = ini_get('soap.wsdl_cache_dir');
if (!is_dir($cacheDir)) {
$cacheDir = sys_get_temp_dir();
$cacheDirForRegExp = preg_quote( $cacheDir );
}
$tests = array(
'localWithAbsolutePath' => array(
'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_absolute.xml',
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
'localWithRelativePath' => array(
'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_relative.xml',
'assertRegExp' => '~.*\.\./type_include\.xsd.*~',
),
'remoteWithAbsolutePath' => array(
'source' => 'http://localhost:8000/xsdinclude/xsdinctest_absolute.xml',
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
'remoteWithAbsolutePath' => array(
'source' => 'http://localhost:8000/xsdinclude/xsdinctest_relative.xml',
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
);
foreach ($tests as $name => $values) {
$cacheFileName = $wd->download($values['source']);
$result = file_get_contents($cacheFileName);
$this->assertRegExp($values['assertRegExp'],$result,$name);
unlink($cacheFileName);
}
$this->stopPhpWebserver();
}
public function testIsRemoteFile()
{
$curl = new Curl();
$wd = new WsdlDownloader($curl);
$class = new \ReflectionClass($wd);
$method = $class->getMethod('isRemoteFile');
$method->setAccessible(true);
$this->assertTrue($method->invoke($wd, 'http://www.php.net/'));
$this->assertTrue($method->invoke($wd, 'http://localhost/'));
$this->assertTrue($method->invoke($wd, 'http://mylocaldomain/'));
$this->assertTrue($method->invoke($wd, 'http://www.php.net/dir/test.html'));
$this->assertTrue($method->invoke($wd, 'http://localhost/dir/test.html'));
$this->assertTrue($method->invoke($wd, 'http://mylocaldomain/dir/test.html'));
$this->assertTrue($method->invoke($wd, 'https://www.php.net/'));
$this->assertTrue($method->invoke($wd, 'https://localhost/'));
$this->assertTrue($method->invoke($wd, 'https://mylocaldomain/'));
$this->assertTrue($method->invoke($wd, 'https://www.php.net/dir/test.html'));
$this->assertTrue($method->invoke($wd, 'https://localhost/dir/test.html'));
$this->assertTrue($method->invoke($wd, 'https://mylocaldomain/dir/test.html'));
$this->assertFalse($method->invoke($wd, 'c:/dir/test.html'));
$this->assertFalse($method->invoke($wd, '/dir/test.html'));
$this->assertFalse($method->invoke($wd, '../dir/test.html'));
}
public function testResolveWsdlIncludes()
{
$this->startPhpWebserver();
$curl = new Curl();
$wd = new WsdlDownloader($curl);
$class = new \ReflectionClass($wd);
$method = $class->getMethod('resolveRemoteIncludes');
$method->setAccessible(true);
$cacheDir = ini_get('soap.wsdl_cache_dir');
if (!is_dir($cacheDir)) {
$cacheDir = sys_get_temp_dir();
$cacheDirForRegExp = preg_quote( $cacheDir );
}
$remoteUrlAbsolute = 'http://localhost:8000/wsdlinclude/wsdlinctest_absolute.xml';
$remoteUrlRelative = 'http://localhost:8000/wsdlinclude/wsdlinctest_relative.xml';
$tests = array(
'localWithAbsolutePath' => array(
'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/wsdlinclude/wsdlinctest_absolute.xml',
'cacheFile' => $cacheDir.'/cache_local_absolute.xml',
'remoteParentUrl' => null,
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
'localWithRelativePath' => array(
'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/wsdlinclude/wsdlinctest_relative.xml',
'cacheFile' => $cacheDir.'/cache_local_relative.xml',
'remoteParentUrl' => null,
'assertRegExp' => '~.*\.\./wsdl_include\.wsdl.*~',
),
'remoteWithAbsolutePath' => array(
'source' => $remoteUrlAbsolute,
'cacheFile' => $cacheDir.'/cache_remote_absolute.xml',
'remoteParentUrl' => $remoteUrlAbsolute,
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
'remoteWithAbsolutePath' => array(
'source' => $remoteUrlRelative,
'cacheFile' => $cacheDir.'/cache_remote_relative.xml',
'remoteParentUrl' => $remoteUrlRelative,
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
);
foreach ($tests as $name => $values) {
$wsdl = file_get_contents( $values['source'] );
$method->invoke($wd, $wsdl, $values['cacheFile'],$values['remoteParentUrl']);
$result = file_get_contents($values['cacheFile']);
$this->assertRegExp($values['assertRegExp'],$result,$name);
unlink($values['cacheFile']);
}
$this->stopPhpWebserver();
}
public function testResolveXsdIncludes()
{
$this->startPhpWebserver();
$curl = new Curl();
$wd = new WsdlDownloader($curl);
$class = new \ReflectionClass($wd);
$method = $class->getMethod('resolveRemoteIncludes');
$method->setAccessible(true);
$cacheDir = ini_get('soap.wsdl_cache_dir');
if (!is_dir($cacheDir)) {
$cacheDir = sys_get_temp_dir();
$cacheDirForRegExp = preg_quote( $cacheDir );
}
$remoteUrlAbsolute = 'http://localhost:8000/xsdinclude/xsdinctest_absolute.xml';
$remoteUrlRelative = 'http://localhost:8000/xsdinclude/xsdinctest_relative.xml';
$tests = array(
'localWithAbsolutePath' => array(
'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_absolute.xml',
'cacheFile' => $cacheDir.'/cache_local_absolute.xml',
'remoteParentUrl' => null,
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
'localWithRelativePath' => array(
'source' => __DIR__.DIRECTORY_SEPARATOR.'Fixtures/xsdinclude/xsdinctest_relative.xml',
'cacheFile' => $cacheDir.'/cache_local_relative.xml',
'remoteParentUrl' => null,
'assertRegExp' => '~.*\.\./type_include\.xsd.*~',
),
'remoteWithAbsolutePath' => array(
'source' => $remoteUrlAbsolute,
'cacheFile' => $cacheDir.'/cache_remote_absolute.xml',
'remoteParentUrl' => $remoteUrlAbsolute,
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
'remoteWithAbsolutePath' => array(
'source' => $remoteUrlRelative,
'cacheFile' => $cacheDir.'/cache_remote_relative.xml',
'remoteParentUrl' => $remoteUrlRelative,
'assertRegExp' => '~.*'.$cacheDirForRegExp.'\\\wsdl_.*\.cache.*~',
),
);
foreach ($tests as $name => $values) {
$wsdl = file_get_contents( $values['source'] );
$method->invoke($wd, $wsdl, $values['cacheFile'],$values['remoteParentUrl']);
$result = file_get_contents($values['cacheFile']);
$this->assertRegExp($values['assertRegExp'],$result,$name);
unlink($values['cacheFile']);
}
$this->stopPhpWebserver();
}
public function testResolveRelativePathInUrl()
{
$curl = new Curl();
$wd = new WsdlDownloader($curl);
$class = new \ReflectionClass($wd);
$method = $class->getMethod('resolveRelativePathInUrl');
$method->setAccessible(true);
$this->assertEquals('http://localhost:8080/test', $method->invoke($wd, 'http://localhost:8080/sub', '/test'));
$this->assertEquals('http://localhost:8080/test', $method->invoke($wd, 'http://localhost:8080/sub/', '/test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub', '/test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/', '/test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost', './test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/', './test'));
$this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub', './test'));
$this->assertEquals('http://localhost/sub/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/', './test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub', '../test'));
$this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/', '../test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub', '../../test'));
$this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/', '../../test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/sub', '../../../test'));
$this->assertEquals('http://localhost/sub/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/sub/', '../../../test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub', '../../../test'));
$this->assertEquals('http://localhost/test', $method->invoke($wd, 'http://localhost/sub/sub/sub/', '../../../test'));
}
}

View File

@ -16,6 +16,13 @@ spl_autoload_register(function($class) {
if (file_exists($path) && is_readable($path)) { if (file_exists($path) && is_readable($path)) {
require_once $path; require_once $path;
return true;
}
} elseif (0 === strpos($class, 'ass\XmlSecurity\\')) {
$path = __DIR__.'/../vendor/XmlSecurity/src/'.strtr($class, '\\', '/').'.php';
if (file_exists($path) && is_readable($path)) {
require_once $path;
return true; return true;
} }
} elseif (0 === strpos($class, 'BeSimple\SoapCommon\\')) { } elseif (0 === strpos($class, 'BeSimple\SoapCommon\\')) {

1
vendors.php Executable file → Normal file
View File

@ -25,6 +25,7 @@ if (!is_dir($vendorDir = dirname(__FILE__).'/vendor')) {
$deps = array( $deps = array(
array('besimple-soapcommon', 'http://github.com/BeSimple/BeSimpleSoapCommon.git', 'origin/HEAD'), array('besimple-soapcommon', 'http://github.com/BeSimple/BeSimpleSoapCommon.git', 'origin/HEAD'),
array('XmlSecurity', 'https://github.com/aschamberger/XmlSecurity.git', 'origin/HEAD'),
); );
foreach ($deps as $dep) { foreach ($deps as $dep) {