Your IP : 18.117.76.198


Current Path : /home/bitrix/ext_www/crm.klimatlend.ua/bitrix/modules/transformer/lib/
Upload File :
Current File : /home/bitrix/ext_www/crm.klimatlend.ua/bitrix/modules/transformer/lib/command.php

<?php

namespace Bitrix\Transformer;

use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Result;
use Bitrix\Main\Error;
use Bitrix\Main\Type\DateTime;
use Bitrix\Transformer\Entity\CommandTable;
use Bitrix\Main\ArgumentNullException;
use Bitrix\Main\ArgumentTypeException;
use Bitrix\Main\InvalidOperationException;
use Bitrix\Main\ArgumentOutOfRangeException;
use Bitrix\Main\Loader;

/**
 * Class Command
 * @package Bitrix\Transformer
 */
class Command
{
	const STATUS_CREATE = 100;
	const STATUS_SEND = 200;
	const STATUS_UPLOAD = 300;
	const STATUS_SUCCESS = 400;
	const STATUS_ERROR = 1000;

	const ERROR_CONNECTION = 50;
	const ERROR_CONNECTION_COUNT = 51;
	const ERROR_CONNECTION_RESPONSE = 60;
	const ERROR_CONTROLLER_DOWNLOAD_STATUS = 100;
	const ERROR_CONTROLLER_DOWNLOAD_TYPE = 101;
	const ERROR_CONTROLLER_DOWNLOAD_SIZE = 102;
	const ERROR_CONTROLLER_BANNED = 103;
	const ERROR_CONTROLLER_QUEUE_CANCELED_BY_EVENT = 150;
	const ERROR_CONTROLLER_QUEUE_ADD_FAIL = 151;
	const ERROR_CONTROLLER_STATUS_AFTER_DOWNLOAD = 200;
	const ERROR_CONTROLLER_DOWNLOAD = 201;
	const ERROR_CONTROLLER_AFTER_DOWNLOAD_SIZE = 202;
	const ERROR_CONTROLLER_UPLOAD = 203;
	const ERROR_CONTROLLER_TRANSFORMATION = 300;
	const ERROR_CONTROLLER_TRANSFORMATION_COMMAND = 301;
	const ERROR_CONTROLLER_COMMAND_NOT_FOUND = 302;
	const ERROR_CONTROLLER_COMMAND_ERROR = 303;
	const ERROR_CONTROLLER_UNKNOWN_ERROR = 250;
	const ERROR_CALLBACK = 400;

	protected $command;
	protected $params;
	protected $status;
	protected $module;
	protected $callback;
	protected $guid;
	protected $id;
	protected $file;
	protected $time;
	protected $error;
	protected $errorCode;

	/**
	 * Command constructor.
	 * @param string $command Class name of the controller.
	 * @param array $params Params to be passed.
	 * @param string|array $module Module name (one or array) to be included before callback.
	 * @param string|array $callback Callback (one or array) to be called with results.
	 * @param int $status Current status.
	 * @param string $id Primary key.
	 * @param string $guid Unique key of the command.
	 * @throws ArgumentNullException
	 */
	public function __construct($command, $params, $module, $callback, $status = self::STATUS_CREATE, $id = '', $guid = '', $time = null, $error = '', $errorCode = 0)
	{
		if(empty($command))
		{
			throw new ArgumentNullException('command');
		}
		if(empty($module))
		{
			throw new ArgumentNullException('module');
		}
		if(empty($callback))
		{
			throw new ArgumentNullException('callback');
		}
		$this->command = $command;
		$this->params = $params;
		$this->module = $module;
		$this->callback = $callback;
		$this->status = intval($status);
		$this->id = $id;
		$this->guid = $guid;
		$this->time = $time;
		$this->error = $error;
		$this->errorCode = $errorCode;
		if(isset($params['file']))
		{
			$this->file = $params['file'];
		}
	}

	/**
	 * @return string
	 */
	protected static function generateGuid()
	{
		return randString(10) . uniqid();
	}

	/**
	 * Check current status of the command and send it through $http.
	 *
	 * @param Http $http Class to send command.
	 * @return Result
	 * @throws ArgumentNullException
	 * @throws ArgumentTypeException
	 * @throws InvalidOperationException
	 */
	public function send(Http $http)
	{
		if($this->status != self::STATUS_CREATE)
		{
			throw new InvalidOperationException('command should be in status '.self::getStatusText(self::STATUS_CREATE));
		}
		if(empty($this->guid))
		{
			throw new InvalidOperationException('command should be saved before send');
		}
		$result = new Result();
		$response = $http->query($this->command, $this->guid, $this->params);
		if($response && $response['success'] !== false)
		{
			$this->updateStatus(self::STATUS_SEND);
			$result->setData(['commandId' => $this->id]);
		}
		else
		{
			$result = $this->processError($response['result']['code'], $response['result']['msg']);
		}
		return $result;
	}

	/**
	 * Include modules and call all the callbacks. Return true on success of all callbacks.
	 * If at least one of callbacks returned false, this method return false.
	 *
	 * @param array $result Result from the controller.
	 * @return bool
	 */
	public function callback($result = array())
	{
		if(!is_array($this->module))
		{
			$this->module = array($this->module);
		}

		if(!is_array($this->callback))
		{
			$this->callback = array($this->callback);
		}

		foreach($this->module as $module)
		{
			if(!\Bitrix\Main\Loader::includeModule($module))
			{
				Log::write('callback cant load module '.$module);
				return false;
			}
		}
		$count = count($this->callback);
		$success = 0;
		$result['command'] = $this;
		foreach($this->callback as $callback)
		{
			if(is_a($callback, 'Bitrix\Transformer\InterfaceCallback', true))
			{
				try
				{
					/* @var $callback InterfaceCallback*/
					$resultCallback = $callback::call($this->status, $this->command, $this->params, $result);
				}
				catch(\Exception $exception)
				{
					$resultCallback = $exception->getMessage();
				}
				catch (\Error $error)
				{
					$resultCallback = $error->getMessage();
				}
				if($resultCallback === true)
				{
					$success++;
				}
				else
				{
					Log::write('Error doing callback. Result: '.$resultCallback);
				}
			}
			else
			{
				Log::write($callback.' does not implements Bitrix\Transformer\InterfaceCallback');
			}
		}
		return ($count == $success);
	}

	/**
	 * Update status of command and save it in DB.
	 *
	 * @param int $status CommandTable_STATUS.
	 * @param string $error Error to save to DB.
	 * @param int $errorCode
	 * @return \Bitrix\Main\Entity\UpdateResult
	 * @throws ArgumentOutOfRangeException
	 * @throws InvalidOperationException
	 * @throws \Bitrix\Main\ObjectException
	 */
	public function updateStatus($status, $error = '', $errorCode = 0)
	{
		$status = intval($status);
		if(!self::getStatusText($status))
		{
			throw new ArgumentOutOfRangeException('status');
		}
		if($this->status >= $status)
		{
			throw new InvalidOperationException('new status should be greater than current');
		}
		if(!$this->id)
		{
			throw new InvalidOperationException('command should be saved before update');
		}
		Log::write('updateStatus in '.$this->guid.' from '.$this->status.' to '.$status);
		$this->status = $status;
		$data = array('STATUS' => $status, 'UPDATE_TIME' => new DateTime());
		if(!empty($error))
		{
			$data['ERROR'] = $error;
		}
		if(!empty($errorCode))
		{
			$errorCode = intval($errorCode);
			$data['ERROR_CODE'] = $errorCode;
		}
		return CommandTable::update($this->id, $data);
	}

	/**
	 * Get current status of the command.
	 *
	 * @return int
	 */
	public function getStatus()
	{
		return $this->status;
	}

	/**
	 * Write error message to log, update status of the command, call callback with error status.
	 *
	 * @param int $errorCode
	 * @param string $message
	 * @return Result
	 * @throws \Bitrix\Main\ObjectException
	 */
	protected function processError($errorCode = 0, $message = '')
	{
		if(!$errorCode)
		{
			$errorCode = Command::ERROR_CONTROLLER_UNKNOWN_ERROR;
		}
		if(!$message && $errorCode > 0)
		{
			$message = $this->getErrorMessages()[$errorCode];
		}
		if(!$message)
		{
			$message = $this->getErrorMessages()[Command::ERROR_CONTROLLER_UNKNOWN_ERROR];
		}
		$result = new Result();
		$result->addError(new Error($message, $errorCode));
		Log::write($message);
		if($this->id > 0)
		{
			$this->updateStatus(self::STATUS_ERROR, $message, $errorCode);
		}
		if(!empty($this->callback))
		{
			$this->callback();
		}
		return $result;
	}

	/**
	 * Get text description of the status.
	 *
	 * @param int $status Code of the status.
	 * @return int|string - status description.
	 */
	public static function getStatusText($status)
	{
		$statusList = array(
			self::STATUS_CREATE => 'create',
			self::STATUS_SEND => 'send',
			self::STATUS_UPLOAD => 'upload',
			self::STATUS_SUCCESS => 'success',
			self::STATUS_ERROR => 'error'
		);
		if(isset($statusList[$status]))
		{
			return $statusList[$status];
		}

		return false;
	}

	/**
	 * Save command in DB.
	 *
	 * @return \Bitrix\Main\Entity\AddResult
	 * @throws InvalidOperationException
	 */
	public function save()
	{
		if($this->id > 0)
		{
			throw new InvalidOperationException('command should not be saved before save');
		}
		$this->guid = self::generateGuid();
		$time = new DateTime();
		$time->setTime($time->format('H'), $time->format('i'), $time->format('s'));
		$this->time = $time;
		$commandItem = array(
			'GUID' => $this->guid,
			'STATUS' => $this->status,
			'COMMAND' => $this->command,
			'MODULE' => base64_encode(serialize($this->module)),
			'CALLBACK' => base64_encode(serialize($this->callback)),
			'PARAMS' => base64_encode(serialize($this->params)),
			'FILE' => $this->file,
			'UPDATE_TIME' => $this->time,
		);
		$addResult = CommandTable::add($commandItem);
		if($addResult->isSuccess())
		{
			$this->id = $addResult->getId();
		}
		return $addResult;
	}

	/**
	 * Get command from DB on $guid.
	 *
	 * @param string $guid Unique key to get Command from DB.
	 * @return Command|bool
	 * @throws ArgumentNullException
	 */
	public static function getByGuid($guid)
	{
		if(empty($guid))
		{
			throw new ArgumentNullException('guid');
		}
		$commandItem = CommandTable::getRow(array('filter' => array('=GUID' => $guid), 'order' => array('ID' => 'desc')));
		if($commandItem && $commandItem['ID'] > 0)
		{
			return self::initFromArray($commandItem);
		}
		return false;
	}

	/**
	 * Get last command from DB on $file.
	 *
	 * @param string $file Path to the file command had been created with.
	 * @return Command|bool
	 * @throws ArgumentNullException
	 */
	public static function getByFile($file)
	{
		if(empty($file))
		{
			throw new ArgumentNullException('file');
		}
		$commandItem = CommandTable::getRow(array('filter' => array('=FILE' => $file), 'order' => array('ID' => 'desc')));
		if($commandItem && $commandItem['ID'] > 0)
		{
			return self::initFromArray($commandItem);
		}
		return false;
	}

	/**
	 * Create new object from array.
	 *
	 * @param array $commandItem
	 * @return Command
	 */
	protected static function initFromArray($commandItem)
	{
		$commandItem['CALLBACK'] = unserialize(base64_decode($commandItem['CALLBACK']));
		$commandItem['MODULE'] = unserialize(base64_decode($commandItem['MODULE']));
		$commandItem['PARAMS'] = unserialize(base64_decode($commandItem['PARAMS']));
		$commandItem['ID'] = intval($commandItem['ID']);
		return new self($commandItem['COMMAND'], $commandItem['PARAMS'], $commandItem['MODULE'], $commandItem['CALLBACK'], $commandItem['STATUS'], $commandItem['ID'], $commandItem['GUID'], $commandItem['UPDATE_TIME'], $commandItem['ERROR'], $commandItem['ERROR_CODE']);
	}

	/**
	 * Get update time of the command.
	 *
	 * @return null|DateTime
	 */
	public function getTime()
	{
		return $this->time;
	}

	/**
	 * Get id of the command
	 *
	 * @return int
	 */
	public function getId()
	{
		return $this->id;
	}

	/**
	 * Adds new record to push&pull
	 */
	public function push()
	{
		if($this->id > 0 && Loader::includeModule('pull'))
		{
			\CPullWatch::AddToStack('TRANSFORMATIONCOMPLETE'.$this->id, [
				'module_id' => 'transformer',
				'command' => 'refreshPlayer',
				'params' => ['id' => $this->id],
			]);
		}
	}

	/**
	 * @return array
	 */
	public function getParams()
	{
		return $this->params;
	}

	/**
	 * @return Error|null
	 */
	public function getError()
	{
		if($this->status === static::STATUS_ERROR)
		{
			$message = null;
			if($this->errorCode)
			{
				$message = $this->getErrorMessages()[$this->errorCode];
			}
			if(!$message)
			{
				$message = $this->error;
			}
			if(!$message)
			{
				$message = $this->getErrorMessages()[static::ERROR_CONTROLLER_UNKNOWN_ERROR];
			}

			return new Error($message, $this->errorCode, ['originalMessage' => $this->error]);
		}

		return null;
	}

	/**
	 * @return array
	 */
	protected function getErrorMessages()
	{
		return [
			static::ERROR_CONNECTION => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_CONNECTION'),
			static::ERROR_CONNECTION_COUNT => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_CONNECTION_COUNT'),
			static::ERROR_CONNECTION_RESPONSE => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_CONNECTION_RESPONSE'),
			static::ERROR_CONTROLLER_DOWNLOAD_STATUS => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_DOWNLOAD_STATUS'),
			static::ERROR_CONTROLLER_DOWNLOAD_TYPE => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_DOWNLOAD_TYPE'),
			static::ERROR_CONTROLLER_DOWNLOAD_SIZE => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_DOWNLOAD_SIZE'),
			static::ERROR_CONTROLLER_BANNED => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_BANNED'),
			static::ERROR_CONTROLLER_QUEUE_CANCELED_BY_EVENT => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_QUEUE_CANCELED_BY_EVENT'),
			static::ERROR_CONTROLLER_QUEUE_ADD_FAIL => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_QUEUE_ADD_FAIL'),
			static::ERROR_CONTROLLER_STATUS_AFTER_DOWNLOAD => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_DOWNLOAD'),
			static::ERROR_CONTROLLER_DOWNLOAD => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_DOWNLOAD'),
			static::ERROR_CONTROLLER_AFTER_DOWNLOAD_SIZE => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_DOWNLOAD_SIZE'),
			static::ERROR_CONTROLLER_UPLOAD => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_UPLOAD'),
			static::ERROR_CONTROLLER_TRANSFORMATION => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_TRANSFORMATION'),
			static::ERROR_CONTROLLER_TRANSFORMATION_COMMAND => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_TRANSFORMATION'),
			static::ERROR_CONTROLLER_COMMAND_NOT_FOUND => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_COMMAND_NOT_FOUND'),
			static::ERROR_CONTROLLER_COMMAND_ERROR => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_COMMAND_ERROR'),
			static::ERROR_CONTROLLER_UNKNOWN_ERROR => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_UNKNOWN'),
			static::ERROR_CALLBACK => Loc::getMessage('TRANSFORMER_COMMAND_ERROR_CALLBACK'),
		];
	}
}