Your IP : 18.223.158.193


Current Path : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/modules/rest/lib/engine/
Upload File :
Current File : /var/www/www-root/data/www/info.monolith-realty.ru/bitrix/modules/rest/lib/engine/restmanager.php

<?php

namespace Bitrix\Rest\Engine;

use Bitrix\Main\Config\Configuration;
use Bitrix\Main\Context;
use Bitrix\Main\Engine;
use Bitrix\Main\Engine\AutoWire;
use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Engine\Resolver;
use Bitrix\Main\Errorable;
use Bitrix\Main\Error;
use Bitrix\Main\ErrorCollection;
use Bitrix\Main\HttpResponse;
use Bitrix\Main\Type\Contract;
use Bitrix\Main\Type\Date;
use Bitrix\Main\Type\DateTime;
use Bitrix\Main\UI\PageNavigation;
use Bitrix\Main\Web\Uri;
use Bitrix\Rest\Engine\ScopeManager;
use Bitrix\Rest\RestException;

class RestManager extends \IRestService
{
	public const DONT_CALCULATE_COUNT = -1;

	/** @var \CRestServer */
	protected $restServer;
	/** @var PageNavigation */
	private $pageNavigation;
	/** @var bool */
	private $calculateTotalCount = true;

	public static function onFindMethodDescription($potentialAction)
	{
		$restManager = new static();
		$potentialActionData = ScopeManager::getInstance()->getMethodInfo($potentialAction);

		$request = new \Bitrix\Main\HttpRequest(
			Context::getCurrent()->getServer(),
			['action' => $potentialActionData['method']],
			[], [], []
		);

		$router = new Engine\Router($request);
		$controllersConfig = Configuration::getInstance($router->getModule());
		if (empty($controllersConfig['controllers']['restIntegration']['enabled']))
		{
			return false;
		}

		/** @var Controller $controller */
		list($controller) = $router->getControllerAndAction();
		if (!$controller || $controller instanceof Engine\DefaultController)
		{
			return false;
		}

		return [
			'scope' => static::getModuleScopeAlias($potentialActionData['scope']),
			'callback' => [
				$restManager, 'processMethodRequest'
			]
		];
	}

	public static function getModuleScopeAlias($moduleId)
	{
		if($moduleId === 'tasks')
		{
			return 'task';
		}

		return $moduleId;
	}

	private static function getAlternativeScope($scope): ?array
	{
		if ($scope === \Bitrix\Rest\Api\User::SCOPE_USER)
		{
			return [
				\Bitrix\Rest\Api\User::SCOPE_USER_BRIEF,
				\Bitrix\Rest\Api\User::SCOPE_USER_BASIC,
			];
		}

		return null;
	}

	public static function fillAlternativeScope($scope, $scopeList)
	{
		if (!in_array($scope, $scopeList, true))
		{
			$altScopeList = static::getAlternativeScope($scope);
			if (is_array($altScopeList))
			{
				$hasScope = array_intersect($scopeList, $altScopeList);
				if (count($hasScope) > 0)
				{
					$scopeList[] = $scope;
				}
			}
		}

		return $scopeList;
	}

	/**
	 * Processes method to services.
	 *
	 * @param array $params Input parameters ($_GET, $_POST).
	 * @param     string $start Start position.
	 * @param \CRestServer $restServer REST server.
	 *
	 * @return array
	 * @throws RestException
	 * @throws \Bitrix\Main\SystemException
	 */
	public function processMethodRequest(array $params, $start, \CRestServer $restServer)
	{
		$start = intval($start);
		$this->initialize($restServer, $start);

		$errorCollection = new ErrorCollection();
		$method = $restServer->getMethod();
		$methodData = ScopeManager::getInstance()->getMethodInfo($method);

		$request = new \Bitrix\Main\HttpRequest(
			Context::getCurrent()->getServer(),
			['action' => $methodData['method']],
			[], [], []
		);
		$router = new Engine\Router($request);

		/** @var Controller $controller */
		[$controller, $action] = Resolver::getControllerAndAction(
			$router->getVendor(),
			$router->getModule(),
			$router->getAction(),
			Controller::SCOPE_REST
		);
		if (!$controller)
		{
			throw new RestException("Unknown {$method}. There is not controller in module {$router->getModule()}");
		}

		$this->calculateTotalCount = true;
		if ((int)$start === self::DONT_CALCULATE_COUNT)
		{
			$this->calculateTotalCount = false;
		}

		$autoWirings = $this->getAutoWirings();

		$this->registerAutoWirings($autoWirings);
		$result = $controller->run($action, [$params, ['__restServer' => $restServer, '__calculateTotalCount' => $this->calculateTotalCount]]);
		$this->unRegisterAutoWirings($autoWirings);

		if ($result instanceof Engine\Response\File)
		{
			return $result->send();
		}

		if ($result instanceof HttpResponse)
		{
			if ($result instanceof Errorable)
			{
				$errorCollection->add($result->getErrors());
			}

			$result = $result->getContent();
		}

		if ($result instanceof RestException)
		{
			throw $result;
		}

		if ($result === null)
		{
			$errorCollection->add($controller->getErrors());
			if (!$errorCollection->isEmpty())
			{
				throw $this->createExceptionFromErrors($errorCollection->toArray());
			}
		}

		return $this->processData($result);
	}

	/**
	 * @param \CRestServer $restServer
	 * @param $start
	 */
	private function initialize(\CRestServer $restServer, int $start): void
	{
		$pageNavigation = new PageNavigation('nav');
		$pageNavigation->setPageSize(static::LIST_LIMIT);
		if ($start > 0)
		{
			$pageNavigation->setCurrentPage((int)($start / static::LIST_LIMIT) + 1);
		}

		$this->pageNavigation = $pageNavigation;
		$this->restServer = $restServer;
	}

	/**
	 * @param Engine\Response\DataType\Page $page
	 * @see \IRestService::setNavData
	 *
	 * @return array
	 */
	private function getNavigationData(Engine\Response\DataType\Page $page): array
	{
		if (!$this->calculateTotalCount)
		{
			return [];
		}

		$result = [];
		$offset = $this->pageNavigation->getOffset();
		$total = $page->getTotalCount();

		$currentPageSize = count($page->getItems());

		if ($offset + $currentPageSize < $total)
		{
			$result['next'] = $offset + $currentPageSize;
		}

		$result['total'] = $total;

		return $result;
	}


	private function processData($result)
	{
		if ($result instanceof DateTime)
		{
			return \CRestUtil::convertDateTime($result);
		}

		if ($result instanceof Date)
		{
			return \CRestUtil::convertDate($result);
		}

		if ($result instanceof Uri)
		{
			return $this->convertAjaxUriToRest($result);
		}

		if ($result instanceof Engine\Response\DataType\Page)
		{
			if (method_exists($result, 'getId'))
			{
				$data = [$result->getId() => $this->processData($result->getIterator())];
			}
			else
			{
				$data = $this->processData($result->getIterator());
			}

			return array_merge($data, $this->getNavigationData($result));
		}

		if ($result instanceof Contract\Arrayable)
		{
			$result = $result->toArray();
		}

		if (is_array($result))
		{
			foreach ($result as $key => $item)
			{
				if ($item instanceof Engine\Response\DataType\ContentUri)
				{
					$result[$key . "Machine"] = $this->processData($item);
					$result[$key] = $this->processData(new Uri($item));
				}
				else
				{
					$result[$key] = $this->processData($item);
				}

			}
		}
		elseif ($result instanceof \Traversable)
		{
			$newResult = [];
			foreach ($result as $key => $item)
			{
				$newResult[$key] = $this->processData($item);
			}

			$result = $newResult;
		}

		return $result;
	}

	private function convertAjaxUriToRest(Uri $uri)
	{
		if (!($uri instanceof Engine\Response\DataType\ContentUri))
		{
			return $uri->getUri();
		}

		$endPoint = Engine\UrlManager::getInstance()->getEndPoint(Engine\UrlManager::ABSOLUTE_URL);
		if ($uri->getPath() !== $endPoint->getPath())
		{
			return $uri->getUri();
		}

		if ($uri->getHost() && $uri->getHost() !== $endPoint->getHost())
		{
			return $uri->getUri();
		}

		parse_str($uri->getQuery(), $params);
		if (empty($params['action']))
		{
			return $uri->getUri();
		}

		return \CRestUtil::getSpecialUrl($params['action'], $params, $this->restServer);
	}

	private function getRestEndPoint()
	{
		return \Bitrix\Main\Config\Option::get('rest', 'rest_server_path', '/rest');
	}

	/**
	 * @param Error[] $errors
	 * @return RestException
	 */
	private function createExceptionFromErrors(array $errors)
	{
		if(!$errors)
		{
			return null;
		}

		$firstError = reset($errors);

		return new RestException($firstError->getMessage(), $firstError->getCode());
	}

	/**
	 * @param array $autoWirings
	 */
	private function registerAutoWirings(array $autoWirings): void
	{
		foreach ($autoWirings as $parameter)
		{
			AutoWire\Binder::registerGlobalAutoWiredParameter($parameter);
		}
	}

	/**
	 * @param array $autoWirings
	 */
	private function unRegisterAutoWirings(array $autoWirings): void
	{
		foreach ($autoWirings as $parameter)
		{
			AutoWire\Binder::unRegisterGlobalAutoWiredParameter($parameter);
		}
	}

	/**
	 * @return array
	 */
	private function getAutoWirings(): array
	{
		$buildRules = [
			'restServer' => [
				'class' => \CRestServer::class,
				'constructor' => function() {
					return $this->restServer;
				},
			],
			'pageNavigation' => [
				'class' => PageNavigation::class,
				'constructor' => function() {
					return $this->pageNavigation;
				},
			],
		];

		$autoWirings = [];
		foreach ($buildRules as $rule)
		{
			$autoWirings[] = new AutoWire\Parameter($rule['class'], $rule['constructor']);
		}

		return $autoWirings;
	}
}