urfaclient на php

Вопросы по сопутствующим продуктам производства партнеров
Ответить
Kayfolom
Сообщения: 746
Зарегистрирован: Вс фев 12, 2006 17:15

Сообщение Kayfolom »

neiks писал(а):Хорошая идея, я даже готов материально немножко помочь.
На 5 странице этой ветки автор опубликовал реквизиты для помощи, я уже отметился, рекомендую всем морально и материально поддержать автора :)

South
Сообщения: 21
Зарегистрирован: Чт фев 12, 2009 14:12
Откуда: С-Петербург
Контактная информация:

Сообщение South »

Мы от себя тоже поблагодарили :-)

dwemer
Сообщения: 276
Зарегистрирован: Чт янв 25, 2007 05:59

Сообщение dwemer »

киньте, пожалуйста, сборку с реализованными функциями на dwemer yandex ru ?
спасибо

hcube
Сообщения: 14
Зарегистрирован: Вт фев 10, 2009 16:33

Сообщение hcube »

Ага, кажется нашел багу в либе. Или в самом URFA. Но скорее в либе.

Выглядит оно следующим образом - вызываю функцию 3018, возвращается код 0. Затем вызываю функцию 3019, возвращается код 3018 - т.е. оно читает из сокета ответ на _предыдущий_ запрос.

Пока что пофиксил ТРЕМЯ последовательными вызовами функции - в третий раз оно проходит. Но вообще надо это посмотреть более внимательно ;-). Да - и хорошо бы сделать опцию отключения диагностических сообщений, типа noecho() и echoon(), которые включают-выключают сообщения о ошибках ;-).
Последний раз редактировалось hcube Пт фев 13, 2009 11:05, всего редактировалось 1 раз.

Аватара пользователя
ds
Сообщения: 380
Зарегистрирован: Пн сен 18, 2006 14:06

Сообщение ds »

hcube писал(а):Ага, кажется нашел багу в либе. Или в самом URFA. Но скорее в либе.

Выглядит оно следующим образом - вызываю функцию 3018, возвращается код 0. Затем вызываю функцию 3019, возвращается код 3018 - т.е. оно читает из сокета ответ на _прыдыдущий_ запрос.

Пока что пофиксил ТРЕМЯ последовательными вызовами функции - третий раз оно проходит, но вообще надо это посмотреть более внимательно ;-).
Выложи код обоих функций, или мне на почту.

hcube
Сообщения: 14
Зарегистрирован: Вт фев 10, 2009 16:33

Сообщение hcube »

Код: Выделить всё

function urfa_call($code)
{
    global $socket;
    $packet = new Packet();
    $packet->clean();
    $packet->code=201;
    $packet->AttrSetInt($code,3);
    $packet->write($socket);
    if (!feof($socket))
    {
	    // for&#40;$i=0;$i<1000000;$i++&#41;; // задержка на чтение из сокета.
	    $packet->clean&#40;&#41;;
	    $packet->read&#40;$socket&#41;;
           echo "read &#58; ".$packet->code." -> &#91;".dechex&#40;$packet->AttrGetInt&#40;3&#41;&#41;."&#93; vs &#91;".dechex&#40;$code&#41;."&#93;";
	    switch &#40;$packet->code&#41;
        &#123;
	        case 200&#58;
		        if &#40;$packet->AttrGetInt&#40;3&#41;==$code&#41;
		            return TRUE;
		        else
		            return FALSE;
	    &#125;
    &#125;
    unset&#40;$packet&#41;;
&#125;
Вот функция работы с сокетом.
А вот что происходит при последовательных вызовах

Unlink tariff : 4
read : 200 -> [0] vs [3019]
Error calling function rpcf_unlink_user_tariff
Link tariff : 4
read : 200 -> [3019] vs [3018]
Error calling function rpcf_link_user_tariff
read : 200 -> [0] vs [3018]
Error calling function rpcf_link_user_tariff
read : 200 -> [3018] vs [3018]

У меня есть вот какая мысль - вместо однократного чтения, сделать чтение сокета _пока_ не будет прочитано нужное значение, либо не исчерпан счетчик чтений. У меня такое ощущение, что в PHP может дурить функция работы с сокетами - которая feof($socket)) - т.е. она может выдать, что сокет непуст, когда реально данные туда еще не положились. Особенно в случае 4-байтных значений.

Код: Выделить всё

function rpcf_link_user_tariff&#40;$user_id,$account_id=0,$tariff_current,$tariff_next=tariff_current,$discount_period_id,$tariff_link_id=0&#41; &#123; //0x3018
	$ret=array&#40;&#41;;
	if &#40;!urfa_call&#40;0x3018&#41;&#41; &#123;
		print "Error calling function ". __FUNCTION__ ."\n";
		return FALSE;
	&#125;
	$packet = new Packet&#40;&#41;;
	$packet->DataSetInt&#40;$user_id&#41;;
	$packet->DataSetInt&#40;$account_id&#41;;
	$packet->DataSetInt&#40;$tariff_current&#41;;
	$packet->DataSetInt&#40;$tariff_next&#41;;
	$packet->DataSetInt&#40;$discount_period_id&#41;;
	$packet->DataSetInt&#40;$tariff_link_id&#41;;
	urfa_send_param&#40;$packet&#41;;
	if &#40;$x = urfa_get_data&#40;&#41;&#41; &#123;
		$ret&#91;'tariff_link_id'&#93;=$x->DataGetInt&#40;&#41;;
		urfa_get_data&#40;&#41;;
	&#125;
	return $ret;
&#125;

function rpcf_unlink_user_tariff&#40;$user_id,$account_id=0,$tariff_link_id=0&#41; 
&#123; //0x3019 
    $ret=array&#40;&#41;; 
    if &#40;!urfa_call&#40;0x3019&#41;&#41; &#123; 
          print "Error calling function ". __FUNCTION__ ."\n"; 
          return FALSE; 
    &#125; 
    $packet = new Packet&#40;&#41;; 
    $packet->DataSetInt&#40;$user_id&#41;; 
    $packet->DataSetInt&#40;$account_id&#41;; 
    $packet->DataSetInt&#40;$tariff_link_id&#41;; 
    urfa_send_param&#40;$packet&#41;; 
    return $ret; 
&#125;
Сами функции вот, но дело не в функциях как таковых, на мой взгляд, а именно в корректной работе с сокетом.

Я думаю, что лучше вместо feof() использовать вот этот конструкт, и сравнение производить только тогда, когда приняты _два_ байта и они реально есть в буфере.

array stream_get_meta_data ( resource $stream )

Проверил... не, не то. Оно проверяет байты уже в PHP буфере, и просто зависает, если сделать ожидание наличия двух байтов. Так что возвращаясь к плану А - можно сделать просто ожидание нужного значения.

hcube
Сообщения: 14
Зарегистрирован: Вт фев 10, 2009 16:33

Сообщение hcube »

Модифицировал функцию вот таким образом :

Код: Выделить всё

function urfa_call&#40;$code&#41;
&#123;
    global $socket;
    $packet = new Packet&#40;&#41;;
    $packet->clean&#40;&#41;;
    $packet->code=201;
    $packet->AttrSetInt&#40;$code,3&#41;;
    $packet->write&#40;$socket&#41;;

    if &#40;!feof&#40;$socket&#41;&#41;
    &#123;
	    $packet->clean&#40;&#41;;
	    $packet->read&#40;$socket&#41;;
           echo "\nread &#58; ".$packet->code." -> &#91;".dechex&#40;$packet->AttrGetInt&#40;3&#41;&#41;."&#93; vs &#91;".dechex&#40;$code&#41;."&#93;";

	    while&#40;$packet->AttrGetInt&#40;3&#41; != $code&#41;
			&#123;
	    		$packet->read&#40;$socket&#41;;
           		echo "\nread &#58; ".$packet->code." -> &#91;".dechex&#40;$packet->AttrGetInt&#40;3&#41;&#41;."&#93; vs &#91;".dechex&#40;$code&#41;."&#93;";
			&#125;

	    switch &#40;$packet->code&#41;
           &#123;
	        case 200&#58;
		        if &#40;$packet->AttrGetInt&#40;3&#41;==$code&#41;
		            return TRUE;
		        else
		            return FALSE;
	    &#125;
    &#125;
    unset&#40;$packet&#41;;
&#125;
Со второго раза оно получает правильное значение и дальше вроде нормально пашет. А иногда - с первого раза получает. Но это конечно не дело ;-)

Аватара пользователя
ds
Сообщения: 380
Зарегистрирован: Пн сен 18, 2006 14:06

Сообщение ds »

AttrGetInt здесь должен быть только один вызов, а то индекс сбивается. Попробуй так если хочешь посмотреть что за код возвращается через переменную $ca здесь.

Код: Выделить всё

function urfa_call&#40;$code&#41;
&#123;
    global $socket;
    $packet = new Packet&#40;&#41;;
    $packet->clean&#40;&#41;;
    $packet->code=201;
    $packet->AttrSetInt&#40;$code,3&#41;;
    $packet->write&#40;$socket&#41;;
    if &#40;!feof&#40;$socket&#41;&#41;
    &#123;
       // for&#40;$i=0;$i<1000000;$i++&#41;; // задержка на чтение из сокета.
       $packet->clean&#40;&#41;;
       $packet->read&#40;$socket&#41;;
       $ca = $packet->AttrGetInt&#40;3&#41;;
       echo "read &#58; ".$packet->code." -> &#91;".dechex&#40;$ca&#41;."&#93; vs &#91;".dechex&#40;$code&#41;."&#93;";
// echo "read &#58; ".$packet->code." -> &#91;".dechex&#40;$packet->AttrGetInt&#40;3&#41;&#41;."&#93; vs &#91;".dechex&#40;$code&#41;."&#93;";
       switch &#40;$packet->code&#41;
        &#123;
           case 200&#58;
              if &#40;$ca==$code&#41;
                  return TRUE;
              else
                  return FALSE;
       &#125;
    &#125;
    unset&#40;$packet&#41;;
&#125;

Kayfolom
Сообщения: 746
Зарегистрирован: Вс фев 12, 2006 17:15

Сообщение Kayfolom »

Добавил в http://wiki.flintnet.ru/ юзерские функции и все функции которые нашел в этой теме.

hcube
Сообщения: 14
Зарегистрирован: Вт фев 10, 2009 16:33

Сообщение hcube »

Не-а. В том-то и дело, что проходит только _второе_ чтение из сокета. По первому читается или ноль, или предыдущий результат, а вот по второму - корректный код, и именно это чтение как раз синхронизирует обмен с URFA по фазе. Не знаю, почему - по идее должен был бы код читаться сразу. Ан нет. Может быть дело в том, что URFA сервис на загруженном серваке чуть подтормаживает после посылки запроса - незаметно для глаза, но достаточно для PHP скрипта.

И - AttrGetInt получает данные из УЖЕ прочитанного пакета. Сам факт чтения происходит по операции $packet->read($socket), а после нее уже можно как угодно из этого пакета читать числа.

Код: Выделить всё

function AttrGetInt&#40;$code&#41;
    &#123;
        if &#40;isset&#40;$this->attr&#91;$code&#93;&#91;'data'&#93;&#41;&#41;
        &#123;
            $x = unpack&#40;"N",$this->attr&#91;$code&#93;&#91;'data'&#93;&#41;;
	        return $x&#91;1&#93;;
	    &#125;
        else
            return FALSE;
    &#125;
Операция unpack() работает с уже полученными данными, не с сокетом. Поэтому ее можно запросто вызывать повторно.

Аватара пользователя
ds
Сообщения: 380
Зарегистрирован: Пн сен 18, 2006 14:06

Сообщение ds »

Это некорректно составлены функции.
Не работает потому что не хватает вызовов $packet->urfa_get_data()


Поэтому и переписал urfa_get_data() чтобы одним вызовом все из сокета забиралось.

Попробуй так:

Код: Выделить всё

function rpcf_link_user_tariff&#40;$user_id,$account_id=0,$tariff_current,$tariff_next=tariff_current,$discount_period_id,$tariff_link_id=0&#41; &#123; //0x3018
   $ret=array&#40;&#41;;
   if &#40;!urfa_call&#40;0x3018&#41;&#41; &#123;
      print "Error calling function ". __FUNCTION__ ."\n";
      return FALSE;
   &#125;
   $packet = new Packet&#40;&#41;;
   $packet->DataSetInt&#40;$user_id&#41;;
   $packet->DataSetInt&#40;$account_id&#41;;
   $packet->DataSetInt&#40;$tariff_current&#41;;
   $packet->DataSetInt&#40;$tariff_next&#41;;
   $packet->DataSetInt&#40;$discount_period_id&#41;;
   $packet->DataSetInt&#40;$tariff_link_id&#41;;
   urfa_send_param&#40;$packet&#41;;
   if &#40;$x = urfa_get_data&#40;&#41;&#41; &#123;
      $ret&#91;'tariff_link_id'&#93;=$x->DataGetInt&#40;&#41;;
   &#125;
   urfa_get_data&#40;&#41;; // Для старой версии urfa_get_data
   return $ret;
&#125;

function rpcf_unlink_user_tariff&#40;$user_id,$account_id=0,$tariff_link_id=0&#41;
&#123; //0x3019
    $ret=array&#40;&#41;;
    if &#40;!urfa_call&#40;0x3019&#41;&#41; &#123;
          print "Error calling function ". __FUNCTION__ ."\n";
          return FALSE;
    &#125;
    $packet = new Packet&#40;&#41;;
    $packet->DataSetInt&#40;$user_id&#41;;
    $packet->DataSetInt&#40;$account_id&#41;;
    $packet->DataSetInt&#40;$tariff_link_id&#41;;
    urfa_send_param&#40;$packet&#41;;
    urfa_get_data&#40;&#41;; // Для старой версии urfa_get_data
    return $ret;
&#125;

Kayfolom
Сообщения: 746
Зарегистрирован: Вс фев 12, 2006 17:15

Сообщение Kayfolom »

Намучался с перехватом ексепшинов (не хотят они нормально работать, хоть ты тресни), сделал небольшую модификацию.

В URFAClient_Connection.php ввел переменную $error='' , сделал обработку ошибок коннекта и логина.

Код: Выделить всё

class URFAClient_Connection
&#123;
	private $socket = null;
	public $error = ''; // Для возврата ошибки
	
	function __construct&#40;$address, $port, $login, $pass, $ssl = true&#41;
	&#123;
		if &#40;$address && $port && $login&#41; &#123;
			if &#40;!$this->open&#40;$address, $port&#41;&#41; &#123;
			    $this->error = 'connect error';
			    return false;
 			&#125;
			if &#40;!$this->login&#40;$login, $pass, $ssl&#41;&#41; &#123;
			    $this->error = 'login error';
 			&#125;
		&#125;
	&#125;
	
	function open&#40;$address, $port&#41;
	&#123;
	    $context = stream_context_create&#40;array&#40;'ssl' => array&#40;'ciphers'=> "ADH-RC4-MD5",&#41;&#41;&#41;;
	    $address = gethostbyname&#40;$address&#41;;
	    $this->socket = stream_socket_client&#40;"tcp&#58;//$address&#58;$port", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context&#41;;
	    return !empty&#40;$this->socket&#41;; // Для возврата ошибки
	&#125;
....
В URFAClient.php :

Код: Выделить всё

abstract class URFAClient
&#123;
	/**
	 * @var URFAClient_Connection
	 */
	protected $connection = null;
	protected $address;
	protected $port;

	public function __construct&#40;$login, $pass, $address = "127.0.0.1", $port = "11758", $ssl = false&#41;
	&#123;
		$this->address = $address;
		$this->port = $port;
		$this->connection = new URFAClient_Connection&#40;$address, $port, $login, $pass, $ssl&#41;;
		$this->error = $this->connection->error;
	&#125;
&#125;
Теперь перехватывает ошибки коннекта и логина на 100% :

Код: Выделить всё

	$urfa_user5 = new URFAClient_User5&#40;$login, $password, "127.0.0.1", "11758",false&#41;;
	if &#40;$urfa_user5->error<>''&#41; &#123;
		exit&#40;$urfa_user5->error&#41;;
	&#125; 
В силу своих ограниченных знаний php возможно криво, но главное работает как часы.

i_destr
Сообщения: 12
Зарегистрирован: Ср сен 26, 2007 13:16

Сообщение i_destr »

Код: Выделить всё

	function rpcf_get_users_list&#40;$from,$to,$card_user=0&#41; &#123; //0x2001
		$ret=array&#40;&#41;;
		if &#40;!$this->connection->urfa_call&#40;0x2001&#41;&#41; &#123;
			print "Error calling function ". __FUNCTION__ ."\n";
			return FALSE;
		&#125;
		$packet = $this->connection->getPacket&#40;&#41;;
		$packet->DataSetInt&#40;$from&#41;;
		$packet->DataSetInt&#40;$to&#41;;
		$packet->DataSetInt&#40;$card_user&#41;;
		$this->connection->urfa_send_param&#40;$packet&#41;;
		if&#40;$x = $this->connection->urfa_get_data&#40;&#41;&#41;&#123;
			$count=$x->DataGetInt&#40;&#41;;
			$ret&#91;'count'&#93;=$count;
			for &#40;$i=0;$i<$count;$i++&#41; &#123;
				$users&#91;'user_id'&#93;=$x->DataGetInt&#40;&#41;;
				$users&#91;'login'&#93;=$x->DataGetString&#40;&#41;;
				$users&#91;'basic_account'&#93;=$x->DataGetInt&#40;&#41;;
				$users&#91;'full_name'&#93;=$x->DataGetString&#40;&#41;;
				$users&#91;'is_blocked'&#93;=$x->DataGetInt&#40;&#41;;
				$users&#91;'balance'&#93;=$x->DataGetDouble&#40;&#41;;
				$ip_adr_size=$x->DataGetInt&#40;&#41;;
				$users&#91;'ip_adr_size'&#93;=$ip_adr_size;
				$ipgroup=array&#40;&#41;;
				for &#40;$j=0;$j<$ip_adr_size;$j++&#41; &#123;
					$group_size=$x->DataGetInt&#40;&#41;;
					$ipgroup&#91;'group_size'&#93;=$group_size;
					$ips=array&#40;&#41;;
					for &#40;$k=0;$k<$group_size;$k++&#41; &#123;
						$ips&#91;'ip_address'&#93;=$x->DataGetIPAddress&#40;&#41;;
						$ips&#91;'mask'&#93;=$x->DataGetIPAddress&#40;&#41;;
						$ips&#91;'group_type'&#93;=$x->DataGetInt&#40;&#41;;
						$ipgroup&#91;'ips'&#93;&#91;&#93;=$ips;
					&#125;
					$users&#91;'ipgroup'&#93;=$ipgroup;
				&#125;
				$users&#91;'user_int_status'&#93;=$x->DataGetInt&#40;&#41;;
				$ret&#91;'users'&#93;&#91;&#93;=$users;
			&#125;
		&#125;
		return $ret;
	&#125;
добавлено в WIKI.
Если есть ошибки - просьба сообщить.

hcube
Сообщения: 14
Зарегистрирован: Вт фев 10, 2009 16:33

Сообщение hcube »

Не работает потому что не хватает вызовов $packet->urfa_get_data()


Ага. Первый вызов функции в скрипте тоже не работает потому что перед ним не было urfa_get_data()? ;-)[/quote]

Аватара пользователя
ds
Сообщения: 380
Зарегистрирован: Пн сен 18, 2006 14:06

Сообщение ds »

hcube писал(а):
Не работает потому что не хватает вызовов $packet->urfa_get_data()


Ага. Первый вызов функции в скрипте тоже не работает потому что перед ним не было urfa_get_data()? ;-)
[/quote]

А перед ним вызываются еще функции?

Ответить