%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/sale/lib/services/base/ |
| Current File : //home/bitrix/www/bitrix/modules/sale/lib/services/base/restclient.php |
<?php
namespace Bitrix\Sale\Services\Base;
use Bitrix\Main\Error;
use Bitrix\Main\Loader;
use Bitrix\Main\ModuleManager;
use Bitrix\Sale\Result;
use Bitrix\Main\Context;
use Bitrix\Main\Web\Json;
use Bitrix\Main\Config\Option;
use Bitrix\Main\Text\Encoding;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Localization\Loc;
use Bitrix\Sale\Location\Exception;
use Bitrix\Sale\ResultSerializable;
Loc::loadMessages(__FILE__);
class RestClient
{
const REST_URI = '/rest/';
const REGISTER_URI = '/oauth/register/';
const SCOPE = 'sale';
const SERVICE_ACCESS_OPTION = 'saleservices_access';
const ERROR_WRONG_INPUT = 1;
const ERROR_WRONG_LICENSE = 2;
const ERROR_SERVICE_UNAVAILABLE = 3;
const ERROR_NOTHING_FOUND = 4;
const UNSUCCESSFUL_CALL_OPTION = 'sale_hda_last_unsuccessful_call';
const UNSUCCESSFUL_CALL_TRYINGS = 3; //times
const UNSUCCESSFUL_CALL_WAIT_INTERVAL = 300; //sec
protected $httpTimeout = 10;
protected $accessSettings = null;
protected $serviceHost = 'https://saleservices.bitrix.info';
protected $version = 4;
/**
* Performs call to the REST method and returns decoded results of the call.
* define SALE_SRVS_RESTCLIENT_DISABLE_SRV_ALIVE_CHECK to disable server alive checking.
* @param string $methodName Name of the REST method.
* @param array $additionalParams Parameters, that should be passed to the method.
* @param bool $licenseCheck Should client send license key as a parameter of the http request.
* @param bool $clearAccessSettings Should client clear authorization before performing http request.
* @return Result $result
*/
protected function call($methodName, $additionalParams = null, $licenseCheck = false, $clearAccessSettings = false)
{
$result = new ResultSerializable();
if(!self::isServerAlive() && !defined('SALE_SRVS_RESTCLIENT_DISABLE_SRV_ALIVE_CHECK'))
{
$result->addError(
new Error(
Loc::getMessage('SALE_SRV_BASE_REST_CONNECT_ERROR').' '.$this->getServiceHost(),
self::ERROR_SERVICE_UNAVAILABLE
)
);
return $result;
}
if ($clearAccessSettings)
{
$this->clearAccessSettings();
$this->accessSettings = null;
}
if (is_null($this->accessSettings))
{
$this->accessSettings = $this->getAccessSettings();
}
if (!$this->accessSettings)
{
$result->addError(new Error(Loc::getMessage('SALE_SRV_BASE_REST_ACCESS_SETTINGS_ERROR')));
return $result;
}
if (!is_array($additionalParams))
$additionalParams = array();
else
$additionalParams = Encoding::convertEncodingArray($additionalParams, LANG_CHARSET, "utf-8");
$additionalParams['version'] = $this->version;
$additionalParams['client_id'] = $this->accessSettings['client_id'];
$additionalParams['client_secret'] = $this->accessSettings['client_secret'];
$additionalParams['lang'] = LANGUAGE_ID;
if ($licenseCheck)
{
$additionalParams = static::signLicenseRequest($additionalParams, static::getLicense());
}
$host = $this->getServiceHost();
$http = new HttpClient(array('socketTimeout' => $this->httpTimeout));
$postResult = @$http->post(
$host.static::REST_URI.$methodName,
$additionalParams
);
try
{
$answer = $this->prepareAnswer($postResult);
}
catch(\Exception $e)
{
$answer = false;
}
if (!is_array($answer))
{
$result->addError(new Error(Loc::getMessage('SALE_SRV_BASE_REST_ANSWER_ERROR').' '.$this->getServiceHost().'. (Status: "'.$http->getStatus().'", Result: "'.$postResult.'")', static::ERROR_SERVICE_UNAVAILABLE));
$this->setLastUnSuccessCallInfo();
return $result;
}
if(self::getLastUnSuccessCount() > 0)
$this->setLastUnSuccessCallInfo(true);
if (array_key_exists('error', $answer))
{
if ($answer['error'] === 'verification_needed')
{
if($licenseCheck)
{
$result->addError(new Error($answer['error'].". ".$answer['error_description'], self::ERROR_WRONG_LICENSE));
return $result;
}
else
{
return $this->call($methodName, $additionalParams, true);
}
}
else if (($answer['error'] === 'ACCESS_DENIED' || $answer['error'] === 'Invalid client' || $answer['error'] === 'NO_AUTH_FOUND')
&& !$clearAccessSettings)
{
return $this->call($methodName, $additionalParams, true, true);
}
$result->addError(new Error($answer['error'].". ".$answer['error_description']));
return $result;
}
if ($answer['result'] == false)
$result->addError(new Error('Nothing found', static::ERROR_NOTHING_FOUND));
if (is_array($answer['result']))
$result->addData($answer['result']);
return $result;
}
/**
* @return string Host.
* Define const SALE_SRVS_RESTCLIENT_SRV_HOST to change server host.
*/
public function getServiceHost()
{
if(!defined('SALE_SRVS_RESTCLIENT_SRV_HOST'))
$result = $this->serviceHost;
else
$result = SALE_SRVS_RESTCLIENT_SRV_HOST;
return $result;
}
/**
* Decodes answer of the method.
* @param string $result Json-encoded answer.
* @return array|bool|mixed|string Decoded answer.
*/
protected function prepareAnswer($result)
{
return Json::decode($result);
}
/**
* Registers client on the properties service.
* @return Result
*/
protected function register()
{
$result = new Result();
$httpClient = new HttpClient();
$queryParams = array(
"scope" => static::SCOPE,
"redirect_uri" => static::getRedirectUri(),
);
$queryParams = static::signLicenseRequest($queryParams, static::getLicense());
$host = $this->getServiceHost();
$postResult = $httpClient->post($host.static::REGISTER_URI, $queryParams);
if ($postResult === false)
{
$result->addError(new Error(implode("\n", $httpClient->getError()), static::ERROR_SERVICE_UNAVAILABLE));
return $result;
}
try
{
$jsonResult = Json::decode($postResult);
}
catch(Exception $e)
{
$result->addError(new Error($e->getMessage()));
return $result;
}
if($jsonResult["error"])
$result->addError(new Error($jsonResult["error"], static::ERROR_WRONG_LICENSE));
else
$result->addData($jsonResult);
return $result;
}
public static function signLicenseRequest(array $request, $licenseKey)
{
if(Loader::includeModule('bitrix24'))
{
$request['BX_TYPE'] = 'B24';
$request['BX_LICENCE'] = BX24_HOST_NAME;
$request['BX_HASH'] = \CBitrix24::RequestSign(md5(implode("|", $request)));
}
else
{
$request['BX_TYPE'] = ModuleManager::isModuleInstalled('intranet') ? 'CP' : 'BSM';
$request['BX_LICENCE'] = md5("BITRIX".$licenseKey."LICENCE");
$request['BX_HASH'] = md5(md5(implode("|", $request)).md5($licenseKey));
}
return $request;
}
/**
* Stores access credentials.
* @param array $params Access credentials.
* @return void
*/
protected static function setAccessSettings(array $params)
{
Option::set('sale', static::SERVICE_ACCESS_OPTION, serialize($params));
}
/**
* Reads and returns access credentials.
* @return array|false Access credentials or false in case of errors.
*/
protected function getAccessSettings()
{
$accessSettings = Option::get('sale', static::SERVICE_ACCESS_OPTION);
if($accessSettings != '')
{
$accessSettings = unserialize($accessSettings);
if($accessSettings)
return $accessSettings;
else
$this->clearAccessSettings();
}
/** @var Result $result */
$result = $this->register();
if($result->isSuccess())
{
$accessSettings = $result->getData();
$this->setAccessSettings($accessSettings);
return $accessSettings;
}
else
{
return array();
}
}
/**
* Drops current stored access credentials.
* @return void
*/
public function clearAccessSettings()
{
Option::set('sale', static::SERVICE_ACCESS_OPTION, null);
}
/**
* Internal method for usage in registration process.
* @return string URL of the host.
*/
protected static function getRedirectUri()
{
$request = Context::getCurrent()->getRequest();
$host = $request->getHttpHost();
$isHttps = $request->isHttps();
return ($isHttps ? 'https' : 'http').'://'.$host."/";
}
/**
* Returns md5 hash of the license key.
* @return string md5 hash of the license key.
*/
protected static function getLicenseHash()
{
return md5(static::getLicense());
}
protected static function getLicense()
{
return LICENSE_KEY;
}
protected static function getLastUnSuccessCallInfo()
{
$result = Option::get('sale', static::UNSUCCESSFUL_CALL_OPTION, "");
if(strlen($result) > 0)
$result = unserialize($result);
return is_array($result) ? $result : array();
}
/**
* @param bool|false $reset
*/
protected static function setLastUnSuccessCallInfo($reset = false)
{
static $alreadySetted = false;
if($alreadySetted && !$reset)
return;
$data = "";
if(!$reset)
{
$alreadySetted = true;
$last = static::getLastUnSuccessCallInfo();
$data = serialize(array(
'COUNT' => intval($last['COUNT']) > 0 ? intval($last['COUNT'])+1 : 1,
'TIMESTAMP' => time()
));
}
Option::set('sale', static::UNSUCCESSFUL_CALL_OPTION, $data);
}
/**
* Check if server is alive.
* @return bool
*/
public static function isServerAlive()
{
$last = static::getLastUnSuccessCallInfo();
if(empty($last))
return true;
if(time() - intval($last['TIMESTAMP']) >= self::UNSUCCESSFUL_CALL_WAIT_INTERVAL)
return true;
if(intval($last['COUNT']) <= self::UNSUCCESSFUL_CALL_TRYINGS)
return true;
return false;
}
/**
* @return int Counts
*/
protected function getLastUnSuccessCount()
{
$last = static::getLastUnSuccessCallInfo();
return intval($last['COUNT']) > 0 ? intval($last['COUNT']) : 0;
}
}