Update
This commit is contained in:
@@ -219,12 +219,10 @@ class Binder
|
||||
catch (\TypeError $exception)
|
||||
{
|
||||
throw $exception;
|
||||
// $this->processException($exception);
|
||||
}
|
||||
catch (\ErrorException $exception)
|
||||
{
|
||||
throw $exception;
|
||||
// $this->processException($exception);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -17,6 +17,10 @@ use Bitrix\Main\Errorable;
|
||||
use Bitrix\Main\ArgumentNullException;
|
||||
use Bitrix\Main\ArgumentTypeException;
|
||||
use Bitrix\Main\Context;
|
||||
use Bitrix\Main\Engine\Response\Render\Component;
|
||||
use Bitrix\Main\Engine\Response\Render\Extension;
|
||||
use Bitrix\Main\Engine\Response\Render\View;
|
||||
use Bitrix\Main\Engine\View\ViewPathResolver;
|
||||
use Bitrix\Main\Event;
|
||||
use Bitrix\Main\EventManager;
|
||||
use Bitrix\Main\EventResult;
|
||||
@@ -108,7 +112,7 @@ class Controller implements Errorable, Controllerable
|
||||
}
|
||||
|
||||
/** @see \Bitrix\Main\Engine\ControllerBuilder::build */
|
||||
//propbably should refactor with ControllerBuilder::build
|
||||
//probably should refactor with ControllerBuilder::build
|
||||
|
||||
// override parameters
|
||||
$controller->request = $this->getRequest();
|
||||
@@ -420,11 +424,12 @@ class Controller implements Errorable, Controllerable
|
||||
|
||||
$this->attachFilters($action);
|
||||
|
||||
|
||||
if ($this->prepareParams() &&
|
||||
$this->processBeforeAction($action) === true &&
|
||||
$this->triggerOnBeforeAction($action) === true)
|
||||
{
|
||||
$result = $action->runWithSourceParametersList();
|
||||
$result = $this->getActionResponse($action);
|
||||
|
||||
if ($action instanceof Errorable)
|
||||
{
|
||||
@@ -457,13 +462,18 @@ class Controller implements Errorable, Controllerable
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function writeToLogException(\Throwable $e)
|
||||
protected function getActionResponse(Action $action)
|
||||
{
|
||||
return $action->runWithSourceParametersList();
|
||||
}
|
||||
|
||||
protected function writeToLogException(\Throwable $e): void
|
||||
{
|
||||
$exceptionHandler = Application::getInstance()->getExceptionHandler();
|
||||
$exceptionHandler->writeToLog($e);
|
||||
}
|
||||
|
||||
private function processExceptionInDebug(\Throwable $e)
|
||||
private function processExceptionInDebug(\Throwable $e): void
|
||||
{
|
||||
if ($this->shouldWriteToLogException($e))
|
||||
{
|
||||
@@ -627,6 +637,7 @@ class Controller implements Errorable, Controllerable
|
||||
protected function create($actionName)
|
||||
{
|
||||
$config = $this->getActionConfig($actionName);
|
||||
|
||||
$methodName = $this->generateActionMethodName($actionName);
|
||||
|
||||
if (method_exists($this, $methodName))
|
||||
@@ -646,7 +657,7 @@ class Controller implements Errorable, Controllerable
|
||||
if (!$config)
|
||||
{
|
||||
throw new SystemException(
|
||||
"Could not find description of {$actionName} in {$this::className()}",
|
||||
"Could not find description of $actionName in {$this::className()}",
|
||||
self::EXCEPTION_UNKNOWN_ACTION
|
||||
);
|
||||
}
|
||||
@@ -1085,6 +1096,11 @@ class Controller implements Errorable, Controllerable
|
||||
return $this->errorCollection->toArray();
|
||||
}
|
||||
|
||||
public function hasErrors(): bool
|
||||
{
|
||||
return !$this->errorCollection->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Getting once error with the necessary code.
|
||||
* @param string $code Code of error.
|
||||
@@ -1094,4 +1110,115 @@ class Controller implements Errorable, Controllerable
|
||||
{
|
||||
return $this->errorCollection->getErrorByCode($code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns response with component content inside site template.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
public function fooAction()
|
||||
{
|
||||
return $this->renderComponent('bitrix:component.name', 'template-name', [
|
||||
'param' => 'value',
|
||||
]);
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* @see \Bitrix\Main\Engine\Response\Render\Component
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $template
|
||||
* @param array $params
|
||||
* @param bool $withSiteTemplate
|
||||
*
|
||||
* @return Component
|
||||
*/
|
||||
final protected function renderComponent(string $name, string $template = '', array $params = [], bool $withSiteTemplate = true): Component
|
||||
{
|
||||
return new Component(
|
||||
$name,
|
||||
$template,
|
||||
$params,
|
||||
$withSiteTemplate,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render file content.
|
||||
*
|
||||
* Example:
|
||||
* ```php
|
||||
public function fooAction()
|
||||
{
|
||||
return $this->renderView('testfile');
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Equivalent to:
|
||||
* ```php
|
||||
public function fooAction()
|
||||
{
|
||||
return $this->renderView('/local/modules/my.module/views/testfile.php');
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Example with params:
|
||||
* ```php
|
||||
public function fooAction()
|
||||
{
|
||||
return $this->renderView('testfile', [
|
||||
'id' => 123,
|
||||
]);
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* Render only file content, without site template:
|
||||
* ```php
|
||||
public function fooAction()
|
||||
{
|
||||
return $this->renderView('/local/modules/my.module/views/testfile.php', withSiteTemplate: false);
|
||||
}
|
||||
* ```
|
||||
*
|
||||
* @see \Bitrix\Main\Engine\Response\Render\View
|
||||
*
|
||||
* @param string $viewPath
|
||||
* @param array $params
|
||||
* @param bool $withSiteTemplate
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
final protected function renderView(string $viewPath, array $params = [], bool $withSiteTemplate = true): View
|
||||
{
|
||||
$resolver = new ViewPathResolver(
|
||||
$viewPath,
|
||||
'renderView',
|
||||
);
|
||||
|
||||
return new View(
|
||||
$resolver->resolve(),
|
||||
$params,
|
||||
$withSiteTemplate
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render extension. It's not SSR!
|
||||
*
|
||||
* @see \Bitrix\Main\Engine\Response\Render\Extension
|
||||
*
|
||||
* @param string $extension
|
||||
* @param array $params
|
||||
* @param bool $withSiteTemplate
|
||||
*
|
||||
* @return Extension
|
||||
*/
|
||||
final protected function renderExtension(string $extension, array $params = [], bool $withSiteTemplate = true): Extension
|
||||
{
|
||||
return new Extension(
|
||||
$extension,
|
||||
$params,
|
||||
$withSiteTemplate,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine;
|
||||
|
||||
|
||||
@@ -12,6 +13,7 @@ final class ControllerBuilder
|
||||
{
|
||||
$scope = $options['scope'] ?? Controller::SCOPE_AJAX;
|
||||
$currentUser = $options['currentUser'] ?? CurrentUser::get();
|
||||
$request = $options['request'] ?? null;
|
||||
|
||||
$reflectionClass = new \ReflectionClass($controllerClass);
|
||||
if ($reflectionClass->isAbstract())
|
||||
@@ -27,7 +29,7 @@ final class ControllerBuilder
|
||||
/** @var Controller $controller */
|
||||
/** @see \Bitrix\Main\Engine\Controller::__construct */
|
||||
/** @see \Bitrix\Main\Engine\Controller::forward */
|
||||
$controller = $reflectionClass->newInstance();
|
||||
$controller = $reflectionClass->newInstance($request);
|
||||
$controller->setScope($scope);
|
||||
$controller->setCurrentUser($currentUser);
|
||||
|
||||
@@ -38,4 +40,4 @@ final class ControllerBuilder
|
||||
throw new ObjectException("Unable to construct controller {{$controllerClass}}.", $exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
62
core/bitrix/modules/main/lib/engine/response/render/base.php
Normal file
62
core/bitrix/modules/main/lib/engine/response/render/base.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\Response\Render;
|
||||
|
||||
use Bitrix\Main\Application;
|
||||
use Bitrix\Main\HttpResponse;
|
||||
use CMain;
|
||||
|
||||
abstract class Base extends HttpResponse
|
||||
{
|
||||
abstract protected function renderContent(): void;
|
||||
|
||||
protected function __construct(
|
||||
bool $withSiteTemplate,
|
||||
)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->fillContent($withSiteTemplate);
|
||||
}
|
||||
|
||||
final protected function fillContent(bool $withSiteTemplate): void
|
||||
{
|
||||
global $APPLICATION;
|
||||
|
||||
/**
|
||||
* @var CMain $APPLICATION
|
||||
*/
|
||||
|
||||
$APPLICATION->RestartBuffer();
|
||||
|
||||
if ($withSiteTemplate)
|
||||
{
|
||||
$this->includeHeader();
|
||||
}
|
||||
else
|
||||
{
|
||||
$APPLICATION->ShowAjaxHead();
|
||||
}
|
||||
|
||||
$this->renderContent();
|
||||
|
||||
if ($withSiteTemplate)
|
||||
{
|
||||
$this->includeFooter();
|
||||
}
|
||||
|
||||
$content = $APPLICATION->EndBufferContentMan();
|
||||
|
||||
$this->setContent($content);
|
||||
}
|
||||
|
||||
final protected function includeHeader(): void
|
||||
{
|
||||
require Application::getDocumentRoot() . '/bitrix/modules/main/include/prolog_after.php';
|
||||
}
|
||||
|
||||
final protected function includeFooter(): void
|
||||
{
|
||||
require Application::getDocumentRoot() . '/bitrix/modules/main/include/epilog_before.php';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\Response\Render;
|
||||
|
||||
use CMain;
|
||||
|
||||
/**
|
||||
* Response with component on site template.
|
||||
*
|
||||
* @see \Bitrix\Main\Engine\Response\Component for AJAX responses.
|
||||
*/
|
||||
final class Component extends Base
|
||||
{
|
||||
public function __construct(
|
||||
private string $name,
|
||||
private string $template,
|
||||
private array $params = [],
|
||||
bool $withSiteTemplate = true,
|
||||
)
|
||||
{
|
||||
parent::__construct($withSiteTemplate);
|
||||
}
|
||||
|
||||
protected function renderContent(): void
|
||||
{
|
||||
global $APPLICATION;
|
||||
|
||||
/**
|
||||
* @var CMain $APPLICATION
|
||||
*/
|
||||
|
||||
$APPLICATION->IncludeComponent(
|
||||
$this->name,
|
||||
$this->template,
|
||||
$this->params
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\Response\Render\Exception;
|
||||
|
||||
final class InvalidConfigExtensionException extends RenderException
|
||||
{
|
||||
public function __construct(string $extension, string $reason)
|
||||
{
|
||||
parent::__construct(
|
||||
"Invalid config format for extension `{$extension}`: {$reason}",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\Response\Render\Exception;
|
||||
|
||||
final class NotFoundPathToViewException extends RenderException
|
||||
{
|
||||
/**
|
||||
* @param string $pathOnDocumentRoot
|
||||
*/
|
||||
public function __construct(string $pathOnDocumentRoot)
|
||||
{
|
||||
parent::__construct(
|
||||
"Path to view `$pathOnDocumentRoot` not found on document root.",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\Response\Render\Exception;
|
||||
|
||||
use Bitrix\Main\SystemException;
|
||||
|
||||
abstract class RenderException extends SystemException
|
||||
{}
|
||||
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\Response\Render;
|
||||
|
||||
use Bitrix\Main\Engine\Response\Render\Exception\InvalidConfigExtensionException;
|
||||
use Bitrix\Main\Web\Json;
|
||||
use CUtil;
|
||||
|
||||
final class Extension extends Base
|
||||
{
|
||||
public function __construct(
|
||||
private string $extension,
|
||||
private array $params = [],
|
||||
bool $withSiteTemplate = true,
|
||||
)
|
||||
{
|
||||
parent::__construct($withSiteTemplate);
|
||||
}
|
||||
|
||||
protected function renderContent(): void
|
||||
{
|
||||
$html = null;
|
||||
$controllerEntryPoint = $this->getControllerEntryPoint();
|
||||
|
||||
if ($controllerEntryPoint)
|
||||
{
|
||||
$containerId = uniqid('render_container_');
|
||||
$selector = CUtil::JSEscape('#' . $containerId);
|
||||
|
||||
$jsonParams = Json::encode($this->params);
|
||||
|
||||
$html = join('', [
|
||||
"<div id='{$containerId}'></div>",
|
||||
"<script>BX.ready(() => { {$controllerEntryPoint}('{$selector}', {$jsonParams}) });</script>",
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidConfigExtensionException(
|
||||
$this->extension,
|
||||
'`controllerEntrypoint` is not defined in extension config',
|
||||
);
|
||||
}
|
||||
|
||||
\Bitrix\Main\UI\Extension::load($this->extension);
|
||||
|
||||
echo $html;
|
||||
}
|
||||
|
||||
private function getControllerEntryPoint(): ?string
|
||||
{
|
||||
$config = \Bitrix\Main\UI\Extension::getConfig($this->extension);
|
||||
if (isset($config['controllerEntrypoint']))
|
||||
{
|
||||
if (!is_string($config['controllerEntrypoint']))
|
||||
{
|
||||
throw new InvalidConfigExtensionException(
|
||||
$this->extension,
|
||||
'`controllerEntrypoint` in config must be a string with JS function name',
|
||||
);
|
||||
}
|
||||
|
||||
return $config['controllerEntrypoint'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
37
core/bitrix/modules/main/lib/engine/response/render/view.php
Normal file
37
core/bitrix/modules/main/lib/engine/response/render/view.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\Response\Render;
|
||||
|
||||
use CMain;
|
||||
|
||||
/**
|
||||
* Response with static view content.
|
||||
*
|
||||
* @see \Bitrix\Main\Routing\Controllers\PublicPageController for using static page on router.
|
||||
*/
|
||||
final class View extends Base
|
||||
{
|
||||
public function __construct(
|
||||
private string $pathOnDocumentRoot,
|
||||
private array $params = [],
|
||||
bool $withSiteTemplate = true,
|
||||
)
|
||||
{
|
||||
parent::__construct($withSiteTemplate);
|
||||
}
|
||||
|
||||
protected function renderContent(): void
|
||||
{
|
||||
global $APPLICATION;
|
||||
|
||||
/**
|
||||
* @var CMain $APPLICATION
|
||||
*/
|
||||
|
||||
$APPLICATION->IncludeFile(
|
||||
$this->pathOnDocumentRoot,
|
||||
$this->params,
|
||||
[],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\View\Exception;
|
||||
|
||||
use Bitrix\Main\SystemException;
|
||||
|
||||
final class FileNotExistsException extends SystemException
|
||||
{
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(
|
||||
"File `{$path}` not exists"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\View\Exception;
|
||||
|
||||
use Bitrix\Main\SystemException;
|
||||
|
||||
final class InvalidPathOnViewException extends SystemException
|
||||
{
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(
|
||||
"Invalid path to view file: {$path}"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\View\Exception;
|
||||
|
||||
use Bitrix\Main\SystemException;
|
||||
|
||||
final class PathOutsideDocumentRootException extends SystemException
|
||||
{
|
||||
public function __construct(string $path)
|
||||
{
|
||||
parent::__construct(
|
||||
"Path `{$path}` is outside the document root"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
122
core/bitrix/modules/main/lib/engine/view/viewpathresolver.php
Normal file
122
core/bitrix/modules/main/lib/engine/view/viewpathresolver.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Bitrix\Main\Engine\View;
|
||||
|
||||
use Bitrix\Main\Diag\Helper;
|
||||
use Bitrix\Main\Engine\View\Exception\FileNotExistsException;
|
||||
use Bitrix\Main\Engine\View\Exception\InvalidPathOnViewException;
|
||||
use Bitrix\Main\Engine\View\Exception\PathOutsideDocumentRootException;
|
||||
use Bitrix\Main\IO\Path;
|
||||
use Bitrix\Main\Loader;
|
||||
use Bitrix\Main\SystemException;
|
||||
|
||||
final class ViewPathResolver
|
||||
{
|
||||
private string $documentRoot;
|
||||
|
||||
public function __construct(
|
||||
private string $viewPath,
|
||||
private string $renderFunctionName,
|
||||
)
|
||||
{
|
||||
$this->documentRoot = Loader::getDocumentRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets path on document root to view file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function resolve(): string
|
||||
{
|
||||
$path = $this->getAbsolutePath();
|
||||
if (!empty($path))
|
||||
{
|
||||
$path = Path::normalize($path);
|
||||
}
|
||||
|
||||
if (is_null($path))
|
||||
{
|
||||
throw new InvalidPathOnViewException($this->viewPath);
|
||||
}
|
||||
elseif (!file_exists($path))
|
||||
{
|
||||
throw new FileNotExistsException($path);
|
||||
}
|
||||
elseif (!str_starts_with($path, $this->documentRoot))
|
||||
{
|
||||
throw new PathOutsideDocumentRootException($this->viewPath);
|
||||
}
|
||||
|
||||
$pathOnDocumentRoot = substr(
|
||||
$path,
|
||||
strlen($this->documentRoot)
|
||||
);
|
||||
|
||||
return $pathOnDocumentRoot;
|
||||
}
|
||||
|
||||
private function getAbsolutePath(): ?string
|
||||
{
|
||||
if (str_starts_with($this->viewPath, '/'))
|
||||
{
|
||||
return $this->documentRoot . $this->viewPath;
|
||||
}
|
||||
|
||||
return $this->getPathToViewOnModule();
|
||||
}
|
||||
|
||||
private function getPathToViewOnModule(): ?string
|
||||
{
|
||||
$moduleId = $this->getModuleId();
|
||||
$postfix = '';
|
||||
|
||||
$extension = pathinfo($this->viewPath, PATHINFO_EXTENSION);
|
||||
if ($extension === '')
|
||||
{
|
||||
$postfix = '.php';
|
||||
}
|
||||
elseif ($extension !== 'php')
|
||||
{
|
||||
throw new SystemException('Invalid extension for view file: ' . $extension);
|
||||
}
|
||||
|
||||
$absolutePath = Loader::getLocal(
|
||||
'modules/' . $moduleId . '/views/' . $this->viewPath . $postfix,
|
||||
);
|
||||
if ($absolutePath !== false)
|
||||
{
|
||||
return $absolutePath;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private function getModuleId(): string
|
||||
{
|
||||
$trace = Helper::getBackTrace();
|
||||
foreach ($trace as $item)
|
||||
{
|
||||
if ($item['function'] === $this->renderFunctionName)
|
||||
{
|
||||
$path = Path::normalize($item['file']);
|
||||
$parts = explode('/', $path);
|
||||
$prevPart = null;
|
||||
foreach ($parts as $part)
|
||||
{
|
||||
if ($part === 'lib')
|
||||
{
|
||||
if (isset($prevPart))
|
||||
{
|
||||
return $prevPart;
|
||||
}
|
||||
}
|
||||
|
||||
$prevPart = $part;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new SystemException('Cannot parse module id from path');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user