Подключение к web-сервисам NAV из PHP
Необходимые знания/навыки
Пожалуйста, вначале прочтите эту статью – чтобы получить разъяснение по поводу изменений сервера приложений для работы с 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.
*/
«Моя» версия
Я слегка изменил код Томаса (в основном убрал отладочные сообщения и т.п.)
Кроме того, я поменял способ указания логина и пароля в коде (константа в скрипте):
Обертка (wrapper) для Stream теперь выглядит так:
{
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, определенную выше, и выглядит так:
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->__last_request_headers)."\n";
}
}
Вставка этого кода в скрипт PHP позволит вам подключиться к веб-сервисам NAV и получить список фирм, доступных на сервере приложений:
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 к странице клиента:
echo "<br>URL of Customer page: $pageURL<br><br>";
А затем я могу создать SOAP-клиента для страницы Customer (по-английски, для лучшего понимания – прим. переводчика. В терминологии – и там, и там – клиент):
$page = new NTLMSoapClient($pageURL);
Используя этот код, я читаю данные по клиенту 10000 и вывожу его имя:
$result = $page->Read($params);
$customer = $result->Customer;
echo "Name of Customer 10000:".$customer->Name."<br><br>";
Далее, давайте наложим фильтр и считаем данные по всем клиентам из Англии, с кодом склада RED или BLUE:
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-протокола. И обратно, возвращаемое значение может быть массивом, а может и нет.
</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.