Подключение к web-сервисам NAV из PHP

04.01.2011 | Автор: | Рубрика: Разработчикам

Необходимые знания/навыки

Пожалуйста, вначале прочтите эту статью – чтобы получить разъяснение по поводу изменений сервера приложений для работы с NTML-аутентификацией, а также краткого описания сценария, которую я буду реализовывать в PHP.

Ну еще, конечно, надо немного знать PHP :-)

Версии

В примере я использую PHP версии 5.2.11, которую скачал с http://www.php.net, но в принципе должно работать с любой версией и выше.

Чтобы все заработало, убедитесь что установлены и включены (enabled) в php.ini расширения SOAP и CURL.

PHP не поддерживает ни NTLM, ни SPNEGO – протоколы авторизации, так что это надо будет сделать вручную. Звучит, прямо скажем, пугающе – тут и какие-то обходы заборов, тут и экспертный уровень владения предметом. К счастью, таких экспертов полно в интернете, и я нашел статью Томаса Рабуа (Thomas Rabaix), который объясняет, что именно надо сделать, как и почему. Обратите внимание, что в примере используется NTLM-аутентификация, и для работы вам придется немного изменить Web Service Listener.

Лицензия на код

Код можно использовать как вашей душе угодно, требуется только включить в комментарии объявление про авторские права Томаса:

/*
* Copyright (c) 2008 Invest-In-France Agency http://www.invest-in-france.org
*
* Author : Thomas Rabaix
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED «AS IS» AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

«Моя» версия

Я слегка изменил код Томаса (в основном убрал отладочные сообщения и т.п.)

Кроме того, я поменял способ указания логина и пароля в коде (константа в скрипте):

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

Обертка (wrapper) для Stream теперь выглядит так:

class NTLMStream
{
private $path;
private $mode;
private $options;
private $opened_path;
private $buffer;
private $pos;
/**
* Open the stream
*
* @param unknown_type $path
* @param unknown_type $mode
* @param unknown_type $options
* @param unknown_type $opened_path
* @return unknown
*/

public function stream_open($path, $mode, $options, $opened_path) {
$this->path = $path;
$this->mode = $mode;
$this->options = $options;
$this->opened_path = $opened_path;
$this->createBuffer($path);
return true;
}
/**
* Close the stream
*
*/

public function stream_close() {
curl_close($this->ch);
}
/**
* Read the stream
*
* @param int $count number of bytes to read
* @return content from pos to count
*/

public function stream_read($count) {
if(strlen($this->buffer) == 0) {
return false;
}
$read = substr($this->buffer,$this->pos, $count);
$this->pos += $count;
return $read;
}
/**
* write the stream
*
* @param int $count number of bytes to read
* @return content from pos to count
*/

public function stream_write($data) {
if(strlen($this->buffer) == 0) {
return false;
}
return true;
}
/**
*
* @return true if eof else false
*/

public function stream_eof() {
return ($this->pos > strlen($this->buffer));
}
/**
* @return int the position of the current read pointer
*/

public function stream_tell() {
return $this->pos;
}
/**
* Flush stream data
*/

public function stream_flush() {
$this->buffer = null;
$this->pos = null;
}
/**
* Stat the file, return only the size of the buffer
*
* @return array stat information
*/

public function stream_stat() {
$this->createBuffer($this->path);
$stat = array(
'size' => strlen($this->buffer),
);
return $stat;
}
/**
* Stat the url, return only the size of the buffer
*
* @return array stat information
*/

public function url_stat($path, $flags) {
$this->createBuffer($path);
$stat = array(
'size' => strlen($this->buffer),
);
return $stat;
}
/**
* Create the buffer by requesting the url through cURL
*
* @param unknown_type $path
*/

private function createBuffer($path) {
if($this->buffer) {
return;
}
$this->ch = curl_init($path);
curl_setopt($this->ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($this->ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_setopt($this->ch, CURLOPT_USERPWD, USERPWD);
$this->buffer = curl_exec($this->ch);
$this->pos = 0;
}
}


Клиент NTLM SOAP использует константу USERPWD, определенную выше, и выглядит так:

class NTLMSoapClient extends SoapClient {
function __doRequest($request, $location, $action, $version) {
$headers = array(
'Method: POST',
'Connection: Keep-Alive',
'User-Agent: PHP-SOAP-CURL',
'Content-Type: text/xml; charset=utf-8',
'SOAPAction: "'.$action.'"',
);
$this->__last_request_headers = $headers;
$ch = curl_init($location);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POST, true );
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
curl_setopt($ch, CURLOPT_USERPWD, USERPWD);
$response = curl_exec($ch);
return $response;
} <span style="font-family: Times New Roman;">
</span>

function __getLastRequestHeaders() {
return implode("\n", $this-&gt;__last_request_headers)."\n";
}
}


Вставка этого кода в скрипт PHP позволит вам подключиться к веб-сервисам NAV и получить список фирм, доступных на сервере приложений:

// делаем unregister текущей обертки http

stream_wrapper_unregister('http');
// регистрируем новую обертку (нашу)
stream_wrapper_register('http', 'NTLMStream') or die("Failed to register protocol"); <span style="font-family: Times New Roman;">
</span>

// инициализация SOAP-клиента
$baseURL = 'http://localhost:7047/DynamicsNAV/WS/';
$client = new NTLMSoapClient($baseURL.'SystemService'); <span style="font-family: Times New Roman;">
</span>

// Найти первую фирму в списке фирм
$result = $client->Companies();
$companies = $result->return_value;
echo "Companies:<br>";
if (is_array($companies)) {
foreach($companies as $company) {
echo "$company<br>";
}
$cur = $companies[0];
}
else {
echo "$companies<br>";
$cur = $companies;
}


Обратите внимание, что возвращаемое значение – массив, если фирм несколько. Если же фирма одна – то возвращаемое значение – название фирмы. Я НЕ ЗНАЮ, почему так происходит и возможно ли написать код так, чтобы избежать этого.

Т.е., теперь у меня есть фирма, которую я буду использовать в $cur, и вот как я сделаю URL к странице клиента:

$pageURL = $baseURL.rawurlencode($cur).'/Page/Customer';
echo "<br>URL of Customer page: $pageURL<br><br>";

А затем я могу создать SOAP-клиента для страницы Customer (по-английски, для лучшего понимания – прим. переводчика. В терминологии – и там, и там – клиент):

// Initialize Page Soap Client
$page = new NTLMSoapClient($pageURL);

Используя этот код, я читаю данные по клиенту 10000 и вывожу его имя:

$params = array('No' => '10000');
$result = $page->Read($params);
$customer = $result->Customer;
echo "Name of Customer 10000:".$customer->Name."<br><br>";


Далее, давайте наложим фильтр и считаем данные по всем клиентам из Англии, с кодом склада RED или BLUE:

$params = array('filter' => array(
array('Field' => 'Location_Code',
'Criteria' => 'RED|BLUE'),
array('Field' => 'Country_Region_Code',
'Criteria' => 'GB')
),
'setSize' => 0);
$result = $page->ReadMultiple($params);
$customers = $result->ReadMultiple_Result->Customer;


Обратите внимание, что Bookmark – необязательный параметр, его можно не указывать.

А теперь выведем клиентов списком и вернем назад обработку http-протокола. И обратно, возвращаемое значение может быть массивом, а может и нет.

echo "Customers in GB served by RED or BLUE warehouse:<br>"; <span style="font-family: Times New Roman;">
</span>

if (is_array($customers)) {
foreach($customers as $cust) {
echo $cust->Name."<br>";
}
}
else {
echo $customers->Name."<br>";
} <span style="font-family: Times New Roman;">
</span>

// restore the original http protocole
stream_wrapper_restore('http');


Все вышенаписанное выведется при открытии скрипта в броузере (на моей машине с запущенной NAV 2009SP1 W1).

Надеюсь, статья поможет вам в работе.

Удачи!

Оригинал статьи доступен здесь: http://blogs.msdn.com/b/freddyk/archive/2010/01/19/connecting-to-nav-web-services-from-php.aspx

Автор:

В области Navision - с 2003 года. Профессиональные интересы: NAV, MS SQL, .NET, BPMN, IT-менеджмент. Предметная область: логистика, финансы, склады, 3PL.

Количество статей, опубликованных автором: 86.

Добавить комментарий