Calling WebServices using PHP

jwilderjwilder Member Posts: 263
edited 2011-07-08 in NAV Three Tier
We are attempting to call a function via webservices using PHP. If the Function in NAV has no parameters but is just returning something, it works.

As soon as there is a parameter the Web Services returns this error:
"Parameter passInHelloWorld in method HelloWorld in service TreeNoWebService is null! "

Here is the php code:
if($Client = new NTLMSoapClient($baseUrl))
{
$value = (string)"HelloWorldValue";
$reponse = @$Client->__call('HelloWorld', array ('passInHelloWorld'=>$value));

die(print_r($response, true));
}

Freddy has a post regarding php but it doesn't inlude a parameter, only a return (http://blogs.msdn.com/freddyk/archive/2 ... m-php.aspx).

Anyone have any thoughts?

Comments

  • IsakssonMiIsakssonMi Member Posts: 77
    Hello!

    To call a NAV Webservice you need to pass Credentials (.NET) ... I can't imagine that is possible by PHP, neither by SOAP (correct my if i'm wrong).

    A suggestion is to make an ASP.NET proxy that talks to NAV's webservices and then connect to the proxy from your PHP-code by SOAP. Some authentication in the SOAP-header wouldn't hurt (atleast :). Thinking of security.
  • davesdaves Member Posts: 49
    Liberty Grove Software has been accessing Web Services using php / browser applications since the first version of NAV 2009 (i.e. before SP1). What does the WSDL of the function you are calling look like?
    Dave Studebaker
    Co-Founder Liberty Grove Software
    Author: Programming Microsoft Dynamics NAV (V5.0), 2009, 2013
  • jwilderjwilder Member Posts: 263
    It is possible to pass credentials in PHP but you have to set a property that was added in NAV SPI in the web service config file called WebServicesUseNTLMAuthentication = true. (We have had to do this for Java as well). If you are using .Net then you can keep this property to false whihc is the default.

    Our PHP programmer has figured this out. I will post what he did when I can get my hands on it.
  • chrisgrill3chrisgrill3 Member Posts: 18
    Did you register the NTLMStream in place of standard http? I think that is what allows you to pass NTLM credentials. Followed Freddy K's blog and got that to work. Can't seem to get an XMLport working over SOAP though. It works using a .NET application in between.
  • chrisgrill3chrisgrill3 Member Posts: 18
    The SOAP request gets there and fires off the code unit, but the xmlport always fails. I tried to use GETLASTERRORTEXT but it returns nothing. The SOAP returned always has blank values in the xml, just the elements. Some of the lines are pasted from emacs and may have trailing slashes from emacs line breaks. Any thoughts would be greatly appreciated. Here is additional info:
    Here is my codeunit:
    WebOrderImport(VAR WebOrderImportXMLPort : XMLport "CK Web Order Import") ReturnText : Text[80]
    IF WebOrderImportXMLPort.IMPORT THEN BEGIN
    ReturnText:='Web Order imported.';
    COMMIT;
    WebOrdProcessingGCdu.ValidateWebOrders;
    WebOrdProcessingGCdu.MakeSalesOrders;
    END ELSE
    ReturnText:='Web Order Not Imported, Try Again';

    Here is the WSDL for the web service it exposes:
    - <definitions targetNamespace="urn:microsoft-dynamics-schemas/codeunit/WebOrderImportService" xmlns="http://schemas.xmlsoap.org/wsdl/&quot; xmlns:tns="urn:microsoft-dynamics-schemas/codeunit/WebOrderImportService">
    - <types>
    - <schema elementFormDefault="qualified" targetNamespace="urn:microsoft-dynamics-nav/xmlports/WebOrderImport" xmlns="http://www.w3.org/2001/XMLSchema&quot; xmlns:tns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport">
    - <complexType name="CustomField">
    - <sequence>
    <element minOccurs="0" maxOccurs="unbounded" name="CustomFieldName" type="string" />
    <element minOccurs="0" maxOccurs="unbounded" name="CustomFieldValue" type="string" />
    </sequence>
    </complexType>
    - <complexType name="CustomFields" mixed="true">
    - <sequence>
    <element minOccurs="0" maxOccurs="unbounded" name="CustomField" type="tns:CustomField" />
    </sequence>
    </complexType>
    - <complexType name="Item">
    - <sequence>
    <element minOccurs="0" maxOccurs="1" name="ItemCode" type="string" />
    <element minOccurs="0" maxOccurs="1" name="Variant" type="string" />
    <element minOccurs="0" maxOccurs="unbounded" name="Description" type="string" />
    <element minOccurs="0" maxOccurs="unbounded" name="Logo" type="string" />
    <element minOccurs="0" maxOccurs="1" name="Quantity" type="string" />
    <element minOccurs="0" maxOccurs="1" name="Price" type="string" />
    <element minOccurs="0" maxOccurs="unbounded" name="CustomFields" type="tns:CustomFields" />
    </sequence>
    </complexType>
    - <complexType name="Items" mixed="true">
    - <sequence>
    <element minOccurs="1" maxOccurs="unbounded" name="Item" type="tns:Item" />
    </sequence>
    </complexType>
    - <complexType name="order">
    - <sequence>
    <element minOccurs="1" maxOccurs="1" name="CustomerCode" type="string" />
    <element minOccurs="1" maxOccurs="1" name="ShipToContact" type="string" />
    <element minOccurs="1" maxOccurs="1" name="ShipToAddress" type="string" />
    <element minOccurs="1" maxOccurs="1" name="ShipToAddress2" type="string" />
    <element minOccurs="1" maxOccurs="1" name="ShipToCity" type="string" />
    <element minOccurs="1" maxOccurs="1" name="ShipToState" type="string" />
    <element minOccurs="1" maxOccurs="1" name="ShipToZIP" type="string" />
    <element minOccurs="0" maxOccurs="1" name="ShipToCountry" type="string" />
    <element minOccurs="1" maxOccurs="1" name="SalesRepCode" type="string" />
    <element minOccurs="1" maxOccurs="1" name="ShipViaCode" type="string" />
    <element minOccurs="1" maxOccurs="1" name="DeliveryDate" type="string" />
    <element minOccurs="1" maxOccurs="1" name="CustomerPONo" type="string" />
    <element minOccurs="1" maxOccurs="1" name="SeparateShipment" type="string" />
    <element minOccurs="1" maxOccurs="unbounded" name="Items" type="tns:Items" />
    </sequence>
    </complexType>
    <element name="order" type="tns:order" />
    </schema>
    - <schema elementFormDefault="qualified" targetNamespace="urn:microsoft-dynamics-schemas/codeunit/WebOrderImportService" xmlns="http://www.w3.org/2001/XMLSchema"&gt;
    - <element name="WebOrderImport">
    - <complexType>
    - <sequence>
    <element minOccurs="1" maxOccurs="1" name="webOrderImportXMLPort" type="q1:order" xmlns:q1="urn:microsoft-dynamics-nav/xmlports/WebOrderImport" />
    </sequence>
    </complexType>
    </element>
    - <element name="WebOrderImport_Result">
    - <complexType>
    - <sequence>
    <element minOccurs="1" maxOccurs="1" name="return_value" type="string" />
    <element minOccurs="1" maxOccurs="1" name="webOrderImportXMLPort" type="q2:order" xmlns:q2="urn:microsoft-dynamics-nav/xmlports/WebOrderImport" />
    </sequence>
    </complexType>
    </element>
    </schema>
    </types>
    - <message name="WebOrderImport">
    <part name="parameters" element="tns:WebOrderImport" />
    </message>
    - <message name="WebOrderImport_Result">
    <part name="parameters" element="tns:WebOrderImport_Result" />
    </message>
    - <portType name="WebOrderImportService_Port">
    - <operation name="WebOrderImport">
    <input name="WebOrderImport" message="tns:WebOrderImport" />
    <output name="WebOrderImport_Result" message="tns:WebOrderImport_Result" />
    </operation>
    </portType>
    - <binding name="WebOrderImportService_Binding" type="tns:WebOrderImportService_Port">
    <binding transport="http://schemas.xmlsoap.org/soap/http&quot; xmlns="http://schemas.xmlsoap.org/wsdl/soap/&quot; />
    - <operation name="WebOrderImport">
    <operation soapAction="urn:microsoft-dynamics-schemas/codeunit/WebOrderImportService:WebOrderImport" style="document" xmlns="http://schemas.xmlsoap.org/wsdl/soap/&quot; />
    - <input name="WebOrderImport">
    <body use="literal" xmlns="http://schemas.xmlsoap.org/wsdl/soap/&quot; />
    </input>
    - <output name="WebOrderImport_Result">
    <body use="literal" xmlns="http://schemas.xmlsoap.org/wsdl/soap/&quot; />
    </output>
    </operation>
    </binding>
    - <service name="WebOrderImportService">
    - <port name="WebOrderImportService_Port" binding="tns:WebOrderImportService_Binding">
    <address location="http://ck-test-1.ck1.dmn:7047/DynamicsNAV/WS/ReplaceWithAPercentEncodedCompanyName/Codeunit/WebOrderImportService&quot; xmlns="http://schemas.xmlsoap.org/wsdl/soap/&quot; />
    </port>
    </service>
    </definitions>
    Here is the SOAP I send
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/&quot; xmlns:ns1="urn:microsoft-dynamic\
    s-nav/xmlports/WebOrderImport" xmlns:ns2="urn:microsoft-dynamics-schemas/codeunit/WebOrderImportService"><SOAP-ENV:Body><ns2:WebOrderImport><ns2:webOrderImportXMLPort><ns1:order><ns1:CustomerCode>1111</ns1:CustomerCode><ns1:ShipToContact>Chris Grill</ns1:ShipToContact><ns1:ShipToAddress>153 Rosewood Ave</ns1:Sh\
    ipToAddress><ns1:ShipToAddress2></ns1:ShipToAddress2><ns1:ShipToCity>Wherever</ns1:ShipToCity><ns1:ShipToState>PA</ns1:ShipToState><ns1:ShipToZIP>55555</n\
    s1:ShipToZIP><ns1:ShipToCountry>United States</ns1:ShipToCountry><ns1:SalesRepCode>WEB</ns1:SalesRepCode><ns1:ShipViaCode>UPS Ground</ns1:ShipViaCode><ns1:D\
    eliveryDate>1969-12-31</ns1:DeliveryDate><ns1:CustomerPONo>TT3859</ns1:CustomerPONo><ns1:SeparateShipment>0</ns1:SeparateShipment><ns1:Items><ns1:Item><ns1\
    :ItemCode>4300</ns1:ItemCode><ns1:Variant>PB-KHA</ns1:Variant><ns1:Description>Visor</ns1:Description><ns1:Quantity>1</ns1:Quantity><ns1:Price>5.0000</ns1:P\
    rice></ns1:Item></ns1:Items></ns1:order></ns2:webOrderImportXMLPort></ns2:WebOrderImport></SOAP-ENV:Body></SOAP-ENV:Envelope>'

    SOAP returned
    <Soap:Envelope xmlns:Soap="http://schemas.xmlsoap.org/soap/envelope/"><Soap:Body><WebOrderImport_Re\
    sult xmlns="urn:microsoft-dynamics-schemas/codeunit/WebOrderImportService"><return_value>Web Order Not Imported, Try Again</return_value><webOrderImportXMLPort><CustomerCode xmlns="ur\
    n:microsoft-dynamics-nav/xmlports/WebOrderImport"/><ShipToContact xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport"/><ShipToAddress xmlns="urn:micr\
    osoft-dynamics-nav/xmlports/WebOrderImport"/><ShipToAddress2 xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport"/><ShipToCity xmlns="urn:microsoft-dy\
    namics-nav/xmlports/WebOrderImport"/><ShipToState xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport"/><ShipToZIP xmlns="urn:microsoft-dynamics-nav/x\
    mlports/WebOrderImport"/><ShipToCountry xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport"/><SalesRepCode xmlns="urn:microsoft-dynamics-nav/xmlports\
    /WebOrderImport"/><ShipViaCode xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport"/><DeliveryDate xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrder\
    Import"/><CustomerPONo xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport"/><SeparateShipment xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImpo\
    rt"/><Items xmlns="urn:microsoft-dynamics-nav/xmlports/WebOrderImport"><Item><ItemCode/><Variant/><Description/><Logo/><Quantity>0</Quantity><Price>0.00</Pr\
    ice><CustomFields/></Item></Items></webOrderImportXMLPort></WebOrderImport_Result></Soap:Body></Soap:Envelope>
  • davesdaves Member Posts: 49
    Based on what you posted, it looks like your connection (php to NAV web services) is working. That assumption is based on the apparent receipt back of your NAV error message. What appears to us (a couple of us read your last posting) is that you are attempting to pass data in the form of an XML data structure and, through an indirect reference in the function parameter list, invoke an XMLport to read the data. Is that correct (or at least close)?

    If so, you can't do that. What you could do is call a Codeunit function, pass in the location (e.g. a path string) of the XML data, then invoke the XMLport from the Codeunit function to go read the data from the passed path. There are other approaches that would also work. But we haven't thought of any way that you could utiilize the XMLPort directly as a function parameter. If we didn't interpret your code correctly, let us know.
    Dave Studebaker
    Co-Founder Liberty Grove Software
    Author: Programming Microsoft Dynamics NAV (V5.0), 2009, 2013
  • MarijnMarijn Member Posts: 69
    I am programming a PHP client application for NAV and do have a bit of hands-on experience.

    Assuming you are using the NTLMSoapClient and NTLMStream classes unaltered, and you have set the useNTLM property set to 'TRUE' in the config file in the NAV folder, then this code should work. It works for me. This example passes a lowercase string to a NAV Codeunit Webservice which returns the string in uppercase. Here's the PHP:

    define('USERPWD', 'localhost\Username:Password');
    $baseURL = 'http://localhost:7047/DynamicsNAV/WS/';
    $companyName = "CRONUS Nederland NV";
    stream_wrapper_unregister('http');
    stream_wrapper_register('http', 'App_NTLMStream') or die("Failed to register protocol");
    $pageURL = $baseURL.rawurlencode($companyName).'/Codeunit/Letters'; // 'Letters' is the name of the Codeunit..
    $client = new App_NTLMSoapClient($pageURL);
    $params = array('inputstring' => 'test');
    $result = $client->Capitalize($params);
    $string = $result->return_value;
    stream_wrapper_restore('http');
    echo $string;
    die();
    // output: "TEST"

    In my actual implementation I do have three classes to abstract the nitty-gritty away. An abstract class which is extended by either a page class to have a 1 size fits all solution for all Pages or a dedicated class which deals with the codeunits. In practice, to delete something it will look like this in case of a Page:

    $namespace = $this->_request->getParam('namespace'); // get the type from somewhere, like Customer, Order etc.
    $no = $this->_request->getParam('No'); // fetch the key (or composed key) from somewhere
    $pageModel = new App_Page($namespace); // instantiate the model
    $pageModel->deleteItem($no); // call a method

    Happy coding..
  • ebsoftebsoft Member Posts: 81
    Marijn,
    I tried your code and I get this error, maybe you can help me even if 5 years are gone..

    Fatal error: Uncaught SoapFault exception: [Client] Function ("capitalize") is not a valid method for this service in C:\wamp\www\nav\letters.php:15 Stack trace: #0 C:\wamp\www\nav\letters.php(15): SoapClient->__call('capitalize', Array) #1 C:\wamp\www\nav\letters.php(15): NTLMSoapClient->capitalize(Array) #2 {main} thrown in C:\wamp\www\nav\letters.php on line 15

    Thanks in advance.
    Regards,
    Federico

    MBS Specialist since NAV 2.0
    My experiences on Linkedin
  • ebsoftebsoft Member Posts: 81
    edited 2016-04-13
    my code:
    <?php

    define('USERPWD', 'domain\\user:pwd');

    require('soapclass.php');
    require('streamclass.php');

    $baseURL = 'http://EXP120.local:7047/DynamicsNAV90/WS/';
    $companyName = "CRONUS Italia S.p.A.";
    stream_wrapper_unregister('http');
    stream_wrapper_register('http', 'NTLMStream') or die("Failed to register protocol");
    $pageURL = 'http://EXP120.local:7047/DynamicsNAV90/WS/CRONUS Italia S.p.A./Codeunit/letters'; // 'Letters' is the name of the Codeunit..
    $client = new NTLMSoapClient($pageURL);
    $params = array();
    $params["param1"] = 'test';
    $result = $client->capitalize($params);
    $string = $result->return_value;
    stream_wrapper_restore('http');
    echo $string;
    die();
    ?>
    Regards,
    Federico

    MBS Specialist since NAV 2.0
    My experiences on Linkedin
  • ebsoftebsoft Member Posts: 81
    I found it... a ini_set was missing
    Regards,
    Federico

    MBS Specialist since NAV 2.0
    My experiences on Linkedin
Sign In or Register to comment.