302 lines
6.8 KiB
PHP
302 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
* Bitrix Framework
|
|
* @package bitrix
|
|
* @subpackage main
|
|
* @copyright 2001-2022 Bitrix
|
|
*/
|
|
namespace Bitrix\Main;
|
|
|
|
use Bitrix\Main\Config\Configuration;
|
|
use Bitrix\Main\Engine\AutoWire;
|
|
use Bitrix\Main\Engine\Controller;
|
|
use Bitrix\Main\Engine\ControllerBuilder;
|
|
use Bitrix\Main\Engine\CurrentUser;
|
|
use Bitrix\Main\Engine\Response\AjaxJson;
|
|
use Bitrix\Main\Engine\Router;
|
|
use Bitrix\Main\Engine\JsonPayload;
|
|
use Bitrix\Main\Security\Sign\BadSignatureException;
|
|
use Bitrix\Main\UI\PageNavigation;
|
|
|
|
/**
|
|
* Http application extends application. Contains http specific methods.
|
|
*/
|
|
class HttpApplication extends Application
|
|
{
|
|
public const EXCEPTION_UNKNOWN_CONTROLLER = 221001;
|
|
|
|
/**
|
|
* Initializes context of the current request.
|
|
*
|
|
* @param array $params Request parameters
|
|
*/
|
|
protected function initializeContext(array $params)
|
|
{
|
|
$context = new HttpContext($this);
|
|
|
|
$server = new Server($params['server']);
|
|
|
|
$request = new HttpRequest(
|
|
$server,
|
|
$params['get'],
|
|
$params['post'],
|
|
$params['files'],
|
|
$params['cookie']
|
|
);
|
|
|
|
$response = new HttpResponse();
|
|
|
|
$context->initialize($request, $response, $server, ['env' => $params['env']]);
|
|
|
|
$this->setContext($context);
|
|
}
|
|
|
|
public function createExceptionHandlerOutput()
|
|
{
|
|
return new Diag\HttpExceptionHandlerOutput();
|
|
}
|
|
|
|
/**
|
|
* Starts request execution. Should be called after initialize.
|
|
*/
|
|
public function start()
|
|
{
|
|
$this->context->getRequest()->decodeJson();
|
|
}
|
|
|
|
/**
|
|
* Finishes request execution.
|
|
*/
|
|
public function finish()
|
|
{
|
|
}
|
|
|
|
private function getSourceParametersList()
|
|
{
|
|
if (!$this->context->getServer()->get('HTTP_BX_AJAX_QB'))
|
|
{
|
|
return array(
|
|
$this->context->getRequest()->getPostList(),
|
|
$this->context->getRequest()->getQueryList(),
|
|
);
|
|
}
|
|
|
|
return array(
|
|
Web\Json::decode($this->context->getRequest()->getPost('bx_data'))
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Runs controller and its action and sends response to the output.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function run()
|
|
{
|
|
try
|
|
{
|
|
$router = new Router($this->context->getRequest());
|
|
|
|
/** @var Controller $controller */
|
|
/** @var string $actionName */
|
|
[$controller, $actionName] = $router->getControllerAndAction();
|
|
if (!$controller)
|
|
{
|
|
throw new SystemException('Could not find controller for the request', self::EXCEPTION_UNKNOWN_CONTROLLER);
|
|
}
|
|
|
|
$this->runController($controller, $actionName);
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
$errorCollection = new ErrorCollection();
|
|
|
|
$this->processRunError($e, $errorCollection);
|
|
$this->finalizeControllerResult($controller ?? null, null, $errorCollection);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param Controller|string $controller
|
|
* @param string $action
|
|
* @return void
|
|
*/
|
|
final public function runController($controller, $action): void
|
|
{
|
|
$result = null;
|
|
$errorCollection = new ErrorCollection();
|
|
|
|
try
|
|
{
|
|
if (is_string($controller))
|
|
{
|
|
$controller = ControllerBuilder::build($controller, [
|
|
'scope' => Controller::SCOPE_AJAX,
|
|
'currentUser' => CurrentUser::get(),
|
|
]);
|
|
}
|
|
|
|
$this->registerAutoWirings();
|
|
|
|
$result = $controller->run($action, $this->getSourceParametersList());
|
|
$errorCollection->add($controller->getErrors());
|
|
}
|
|
catch (\Throwable $e)
|
|
{
|
|
$this->processRunError($e, $errorCollection);
|
|
}
|
|
finally
|
|
{
|
|
$this->finalizeControllerResult($controller, $result, $errorCollection);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param Controller|null $controller
|
|
* @param HttpResponse|null $result
|
|
* @param ErrorCollection $errorCollection
|
|
*/
|
|
private function finalizeControllerResult($controller, $result, ErrorCollection $errorCollection): void
|
|
{
|
|
$response = $this->buildResponse($result, $errorCollection);
|
|
$response = $this->context->getResponse()->copyHeadersTo($response);
|
|
|
|
if ($controller)
|
|
{
|
|
$controller->finalizeResponse($response);
|
|
}
|
|
|
|
$this->context->setResponse($response);
|
|
|
|
//todo exit code in Response?
|
|
$this->end(0, $response);
|
|
}
|
|
|
|
private function shouldWriteToLogException(\Throwable $e): bool
|
|
{
|
|
if ($e instanceof AutoWire\BinderArgumentException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ($e instanceof BadSignatureException)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$unnecessaryCodes = [
|
|
self::EXCEPTION_UNKNOWN_CONTROLLER,
|
|
Router::EXCEPTION_INVALID_COMPONENT_INTERFACE,
|
|
Router::EXCEPTION_INVALID_COMPONENT,
|
|
Router::EXCEPTION_INVALID_AJAX_MODE,
|
|
Router::EXCEPTION_NO_MODULE,
|
|
Router::EXCEPTION_INVALID_MODULE_NAME,
|
|
Router::EXCEPTION_INVALID_COMPONENT_NAME,
|
|
Router::EXCEPTION_NO_COMPONENT,
|
|
Router::EXCEPTION_NO_COMPONENT_AJAX_CLASS,
|
|
];
|
|
|
|
if ($e instanceof SystemException)
|
|
{
|
|
if (\in_array($e->getCode(), $unnecessaryCodes, true))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ($this->isDebugMode() && ($e->getCode() === Router::EXCEPTION_NO_CONFIGURATION))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function isDebugMode(): bool
|
|
{
|
|
$exceptionHandling = Configuration::getValue('exception_handling');
|
|
|
|
return !empty($exceptionHandling['debug']);
|
|
}
|
|
|
|
private function processRunError(\Throwable $e, ErrorCollection $errorCollection): void
|
|
{
|
|
$errorCollection[] = new Error($e->getMessage(), $e->getCode());
|
|
|
|
$debugMode = $this->isDebugMode();
|
|
if ($debugMode)
|
|
{
|
|
$errorCollection[] = new Error(Diag\ExceptionHandlerFormatter::format($e));
|
|
if ($e->getPrevious())
|
|
{
|
|
$errorCollection[] = new Error(Diag\ExceptionHandlerFormatter::format($e->getPrevious()));
|
|
}
|
|
}
|
|
|
|
if ($debugMode || $this->shouldWriteToLogException($e))
|
|
{
|
|
$this->getExceptionHandler()->writeToLog($e);
|
|
}
|
|
}
|
|
|
|
private function registerAutoWirings()
|
|
{
|
|
AutoWire\Binder::registerGlobalAutoWiredParameter(new AutoWire\Parameter(
|
|
PageNavigation::class,
|
|
static function() {
|
|
$pageNavigation = new PageNavigation('nav');
|
|
$pageNavigation
|
|
->setPageSizes(range(1, 50))
|
|
->initFromUri()
|
|
;
|
|
|
|
return $pageNavigation;
|
|
}
|
|
));
|
|
|
|
AutoWire\Binder::registerGlobalAutoWiredParameter(new AutoWire\Parameter(
|
|
JsonPayload::class,
|
|
static function() {
|
|
return new JsonPayload();
|
|
}
|
|
));
|
|
|
|
AutoWire\Binder::registerGlobalAutoWiredParameter(new AutoWire\Parameter(
|
|
CurrentUser::class,
|
|
static function() {
|
|
return CurrentUser::get();
|
|
}
|
|
));
|
|
}
|
|
|
|
/**
|
|
* Builds a response by result's action.
|
|
* If an action returns non subclass of HttpResponse then the method tries to create Response\StandardJson.
|
|
*
|
|
* @param mixed $actionResult
|
|
* @param ErrorCollection $errorCollection
|
|
*
|
|
* @return HttpResponse
|
|
*/
|
|
private function buildResponse($actionResult, ErrorCollection $errorCollection)
|
|
{
|
|
if ($actionResult instanceof HttpResponse)
|
|
{
|
|
return $actionResult;
|
|
}
|
|
|
|
if (!$errorCollection->isEmpty())
|
|
{
|
|
//todo There is opportunity to create DenyError() and recognize AjaxJson::STATUS_DENIED by this error.
|
|
|
|
return new AjaxJson(
|
|
$actionResult,
|
|
AjaxJson::STATUS_ERROR,
|
|
$errorCollection
|
|
);
|
|
}
|
|
|
|
return new AjaxJson($actionResult);
|
|
}
|
|
}
|