I want to interact with SOAP (as a client) and am not able to get the right syntax for input parameters. I have a WSDL URL that I have tested it with SoapUI and it returns result properly. There are two functions defined in the WSDL, but I only need one ("FirstFunction" below). Here is the script I run to get information on the available functions and types:
$client = new SoapClient("http://example.com/webservices?wsdl");
var_dump($client->__getFunctions());
var_dump($client->__getTypes());
And here is the output it generates:
array(
[0] => "FirstFunction Function1(FirstFunction $parameters)",
[1] => "SecondFunction Function2(SecondFunction $parameters)",
);
struct Amount {
anyURI Identifier;
Information charge;
string referenceCode;
}
struct Information {
string description;
decimal amount;
string code;
}
According to above result I developed my client with nusoap and php as below:
class Information
{
public $description;
public $amount;
public $code;
}
class Amount {
public $Identifier;
public $charge;
public $referenceCode;
}
$charge = new Information();
$charge->description = "ROUTE=XXX|abc=".$code;
$charge->amount = "NULL";
$charge->code = $chargecode;
$params = new Amount();
$params->Identifier =$num;
$params->charge = $charge;
$params->referenceCode = $refcode;
$header = new SoapHeader('key', $key);
$client->__setSoapHeaders($header);
try
{
$res = $client->__call('charge',array('parametrs'=>$params));
print_r($res->return);
}
catch(PDOException $e)
{
print_r($e->getMessage());
}
I get the following error as result:
Uncaught SoapFault exception: [soapenv:Server] unknown
In my opinion the best way to achieve it is to use a WSDL to php generator such as the PackageGenerator project. It abstracts the whole process so you only deal with objects without really worrying about SOAP.
Related
I am trying to consume WCF service from PHP . I need to pass a header field in soap client of PHP in order to consume it. Soap header will be something like this:
<header>
<LisenseKey>Lisense key goes here</LisenseKey>
</header>
The namespace for LisenseKey element is "http://mylinsensekeynamespace".
The method I want to consume in WCF is as follows:
public string function GetMessage(string name)
{
return "Hello , "+name;
}
Before the service is configured to validate header, I was consuming the service from PHP as follows and it is working perfectly:
try{
$client = new SoapClient("http://localhost:8181/?wsdl");
$param = new stdClass();
$param->name = "My Name";
$webService = $client->GetMessage($param);
print_r($webService);
}
catch(Exception $e)
{
echo $e->getMessage();
}
When after the service is configured to validate license key in header, I am trying to consume it like this and it is not working yet:
try{
$client = new SoapClient("http://localhost:8181/?wsdl");
$actionHeader = new SoapHeader("http://mycustomheader",'LisenseKey',"lisense key",true);
$client->__setSoapHeaders($actionHeader);
$param = new stdClass();
$param->name = "My Name";
$webService = $client->GetMessage($param);
print_r($webService);
}
catch(Exception $e)
{
echo $e->getMessage();
}
I already tried in so many different ways from online articles. How can I consume it? WCF service is using BasicHttpBinding and SOAP version should be 1.1. How to pass the header information to consume the service?
Following is the .NET WCF service code that validate for LicenseKey soap header for every request.
public class MyServiceMessageInspector : System.ServiceModel.Dispatcher.IDispatchMessageInspector
{
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel,
System.ServiceModel.InstanceContext instanceContext)
{
if (request.Headers.FindHeader("LisenseKey", "") == -1)
{
throw new FaultException("Lisense Key Was Not Provided");
}
var lisenseKey = request.Headers.GetHeader<string>("LisenseKey", "http://mycustomheader.com");
if (string.IsNullOrEmpty(lisenseKey))
{
throw new FaultException("Lisnse key should not be empty");
}
if (lisenseKey != "12345x")
{
throw new FaultException("Lisense key is not valid");
}
return instanceContext;
}
public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
{
}
}
public class MyServiceMessageInspectorBehaviour : Attribute, System.ServiceModel.Description.IServiceBehavior
{
public void AddBindingParameters(System.ServiceModel.Description.ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase,
System.Collections.ObjectModel.Collection<System.ServiceModel.Description.ServiceEndpoint> endpoints,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(System.ServiceModel.Description.ServiceDescription serviceDescription,
System.ServiceModel.ServiceHostBase serviceHostBase)
{
foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (var endpointDispatcher in channelDispatcher.Endpoints)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MyServiceMessageInspector());
}
}
}
public void Validate(System.ServiceModel.Description.ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
{
}
}
Try calling the WCF service like this : $client->__soapCall("GetMessage", $param, NULL, $header);
I have created a Webservice from a BAPI in SAP to insert some AccountDocuments into SAP. The system in these cases needs a COMMIT-call after a successful insert call. Both of these functions must be called in "one context".
Now I'm facing the problem that I don't know how to do this in php or if there is any way to do this?
I have created the following example, but it doesn't work. The COMMIT function gets executed but it has no impact in SAP. I cannot see the data in the databases, although the first call returns "Data successfully booked". I know that you must confirm this with the COMMIT call in SAP. In SE37 there is a way to put 2 function calls into one Sequence. I'm searching the php-way to do this.
function insertAccntDoc($accntgl, $currAmount, $docHeader, $accntTax)
{
#Define Authentication
$SOAP_AUTH = array( 'login' => SAPUSER,
'password' => SAPPASSWORD);
$WSDL = "url_to_my_wsdl";
#Create Client Object, download and parse WSDL
$client = new SoapClient($WSDL, $SOAP_AUTH);
#Setup input parameters (SAP Likes to Capitalise the parameter names)
$params = array(
'AccountGl' => $accntgl,
'CurrencyAmount' => $currAmount,
'DocumentHeader' => $docHeader,
'AccountTax' => $accntTax
);
#Call Operation (Function). Catch and display any errors
try
{
$result = $client->AcctngDocumentPost($params);
$result = $client->BapiServiceTransactionCommit();
$result->Gebucht = 'Committed';
if(count($result->Return) > 1)
{
$client->BapiServiceTransactionRollback();
$result->Gebucht = 'Rollback';
}
else if($result->Return->item->Type == 'S')
{
try
{
$client->BapiServiceTransactionCommit();
$result->Gebucht = 'Committed';
}
catch(SoapFault $exception)
{
$client->BapiServiceTransactionRollback();
$result->Fehler = "***Caught Exception***<br>".$exception."<br>***END Exception***<br>";
$result->Gebucht = 'Fehler beim Committen';
}
}
}
catch (SoapFault $exception)
{
$client->BapiServiceTransactionRollback();
$result->Fehler = "***Caught Exception***<br>".$exception."<br>***END Exception***<br>";
$result->Gebucht = 'Fehler beim Anlegen';
}
#Output the results
$result->FlexRet = 'insertAccntDoc';
return $result;
}
Thanks!
This link gives details on how to use "stateful" web services. This is required to have a shared session.
http://scn.sap.com/thread/140909
I have php code that execute python cgi and I want to pass python trace (returned from cgi) as extra data to php exception how can I do this and how can I get that value from catch(Exception e) { (It should check if that extra value exesit or not).
I have code like this:
$response = json_decode(curl_exec($ch));
if (isset($response->error)) {
// how to send $response->trace with exception.
throw new Exception($response->error);
}
return $response->result;
and I use json-rpc library that should return that data to the user:
} catch (Exception $e) {
//catch all exeption from user code
$msg = $e->getMessage();
echo response(null, $id, array("code"=>200, "message"=>$msg));
}
Do I need to write new type of exception or can I do this with normal Exception? I would like to send everything that was thrown in "data" =>
You need to extend Exception class:
<?php
class ResponseException extends Exception
{
private $_data = '';
public function __construct($message, $data)
{
$this->_data = $data;
parent::__construct($message);
}
public function getData()
{
return $this->_data;
}
}
When throwing:
<?php
...
throw new ResponseException($response->error, $someData);
...
And when catching:
catch(ResponseException $e) {
...
$data = $e->getData();
...
}
Dynamic Property (not recommended)
Please note that this will cause deprecation error in PHP 8.2 and will stop working in PHP 9 according to one of the PHP RFC https://wiki.php.net/rfc/deprecate_dynamic_properties
As the OP asking about doing this task without extending Exception class, you can totally skip ResponseException class declaration. I really not recommend do it this way, unless you've got really strong reason (see this topic for more details: https://softwareengineering.stackexchange.com/questions/186439/is-declaring-fields-on-classes-actually-harmful-in-php)
In throwing section:
...
$e = new Exception('Exception message');
$e->data = $customData; // we're creating object property on the fly
throw $e;
...
and when catching:
catch(Exception $e) {
$data = $e->data; // Access data property
}
September 2018 edit:
As some of readers found this answer useful, I have added a link to another Stack Overflow question which explains the downsides of using dynamically declared properties.
Currently, your code converts the response text directly into an object without any intermediate step. Instead, you could always just keep the serialized (via JSON) text it and append it to the end of the Exception message.
$responseText = curl_exec($ch);
$response = json_decode($responseText);
if (isset($response->error)) {
throw new Exception('Error when fetching resource. Response:'.$responseText);
}
return $response->result;
Then you could just recover everything after "Response:" in your error log and optionally de-serialize it or just read it.
As an aside, I would also not count on the server sending JSON, you should verify that the response text was actually parseable as JSON and return a separate error for that if it isn't.
Presently, I am trying to interface with a SOAP-based camera system to tie into its action API so I can control when its lights come on programatically and so forth. However, when I use the code below, it's saying it cannot bind to the service and doesn't seem to be able to properly digest the WSDL file associated with the API, which can be found here:
http://www.axis.com/vapix/ws/action1/ActionService.wsdl
Is there something that's wrong with my code, or is this an issue with the WSDL file itself? Thank you very much in advance for the assistance! In advance, the error generating is the following, generated at the instantiation of the SoapClient object in the constructor:
SOAP-ERROR: Parsing WSDL: Couldn't bind to service
<?php
/**
* The purpose of this class is to act as a means to interface with a Vapix camera
* using SOAP requests so that events may be broadcast to it.
*/
$vapix = new Vapix("http://www.axis.com/vapix/ws/action1/ActionService.wsdl",
"<http://camera.address.edu>",
"<username>", "<password>");
if ($vapix)
{
echo "Connection to VAPIX successful!\n";
}
else
{
echo "Connection to VAPIX unsuccessful!\n";
}
/**
* The constructor takes in a WSDL address, the actual interfacing address of the
* server we are connecting to, a username, and a password, and establishes the
* SOAP client we need to interface with said address.
*
* #param $wsdl The WSDL specification for the service we are interacting with.
* #param $address The actual server address we are interfacing with.
* #param $username The username we need to access the server.
* #param $password The password we need to access the server.
*
* #return New Vapix object ready to interface with SOAP service.
*/
class Vapix
{
// the soap client variable we will be using to store our Vapix connection
private $soapClient;
public function __construct($wsdl, $address, $username, $password)
{
try
{
$soapClient = new SoapClient($wsdl, array("soap_version" => SOAP_1_2));
}
catch (SoapFault $fault)
{
echo "Error instantiating SOAP object!\n";
echo $fault->getMessage() . "\n";
}
// prepare SOAP headers
$sh_param = array(
"username" => $username,
"password" => $password
);
$headers = new SoapHeader($address, "UserCredentials", $sh_param);
// prepare SOAP client
$soapClient->__setSoapHeaders(array($headers));
}
/**
* This function is a generalized function used for calling a SOAP request to
* whatever service or server we are linked up to (in this case a VAPIX camera)
* so that other more specialized functions can derive from it. It will take in
* the name of the function, as well as a list of parameters.
*
* #param $funcName The name of the function we want to call.
* #param $parameters The parameters for the function we want to call.
*
* #return $info Returns info from the call if successful, NULL otherwise.
*/
public function callSoapFunction($funcName, $parameters)
{
try
{
$info = $soapClient->__call($funcName, array($parameters));
}
catch (SoapFault $fault)
{
print(
"alert('Sorry, blah returned the following ERROR: " . $fault->faultcode . "-" .
$fault->faultstring.". We will now take you back to our home page.');
window.location = 'main.php';"
);
return NULL;
}
if ($error == 0)
{
return $info;
}
}
}
?>
At least the provided WSDL-file has no <service>-area at the end of the wsdl-definition right after the <binding> block. But this missing <service>-block is needed as it contains concrete servicespecific informations (for instance its webservice URL/endpoint is listed there).
Hi everyone Im trying to consume a .NET with PHP using SoapClient but I got the following issue when my php client send the request, the .NET WS doesnt get the request xml on the right format heres my code i hope some one help me, thanks ind advice
class login {
public $User;
public $Password;
}
$logr = new login;
$logr->User = 'user';
$logr->Password = 'pass';
try {
$client = new soapclient ("http://..../Service.asmx?WSDL", array('classmap' => array('LoginRequest' => 'login'),));
print_r($logr);
$client -> Login ($logr);
}
catch (Exception $e) {
echo "Error!<br />";
echo $e -> getMessage ();
}
when i test my .net webserver on a .net application i send this, and it works well
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2011/XMLScheme">
<soap:Body>
<Login
xmlns="http://tempuri.org">
<LoginRequest>
<User>user</User>
<Password>pass</Password>
</LoginRequest>
</Login
</soap:Body>
</soap:Envelope>
but when i test it on php i get this, and this error Server was unable to process request. ---> Object reference not set to an instance of an object.
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope"
xmlns:ns1="http://tempuri.org"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<SOAP-ENV:Body>
<Login
xmlns="http://tempuri.com"
xso:type="ns1:LoginRequest">
<User>user</User>
<Password>pass</Password>
</Login>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
class LoginRequest {
public $User;
public $Password;
public function __construct($usr, $pwd) {
$this->User = $usr;
$this->Password = $pwd;
}
}
$login = new LoginRequest('user', 'password');
$client = new SoapClient('http://..../services.asmx?wsdl');
$client->login($login); // try 1
$client->login(array('LoginRequest' => $login)); //try 2
To use a .Net web service, using the NetBeans IDE, add a 'service' and point to the .net web service (make sure to put ?WSDL at the end), and then drag and drop from the 'service's ' toolbox to a php file and it writes the code for you :)
Works great too.
[I can't say what's wrong with your code though]
I recently ran into this issue as well when helping a customer consume our .Net web service so I thought I would share in case anyone else came across this in the future.
Thanks to adudly for providing guidance that helped shed light on the issue. You have to provide a name for the parameter in the array, and note that the name is case sensitive.
Working Code
try {
$wsdl_url = 'http://<mywebserver>/LeadWs.svc?wsdl';
$client = new SOAPClient($wsdl_url);
$params = array(
'lead' => ""
);
$return = $client->Insert2($params);
print_r($return);
} catch (Exception $e) {
echo "Exception occurred: " . $e;
}
My failed attempts used a capital 'L' for Lead. This is apparently the only thing in the WSDLs/XSDs that is lowercase by default. If you do a careful search through the WSDLs/XSDs you will see the exact names of any parameters your method expects. Once you get those right, SoapClient handles the rest of the XML encoding.
My final Code looked like this:
try {
$wsdl_url = 'http://<mywebserver>/LeadWs.svc?wsdl';
$client = new SOAPClient($wsdl_url);
$lead = new Lead(); // Could just be an array as well
// but I created a class to help the user
$lead->FirstName = "Tester";
$lead->LastName = "Test";
$lead->ZipCode = "00000";
$lead->NumberOfTVs = 2;
$params = array('lead' => $lead);
$return = $client->Insert2($params);
print_r($return);
} catch (Exception $e) {
echo "Exception occurred: " . $e;
}
Hope that helps someone in the future.
You can use SoapHeader and setSoapHeaders here