51 private Action $currentAction;
52 private array $eventHandlersIds = [
57 private $configurationOfActions =
null;
59 private string $scope;
67 private $sourceParametersList;
68 private $unsignedParameters;
86 $this->scope = self::SCOPE_AJAX;
90 $this->converter = Converter::toJson();
103 public function forward($controller,
string $actionName, array $parameters =
null)
105 if (is_string($controller))
107 $controller =
new $controller;
115 $controller->setScope($this->
getScope());
116 $controller->setCurrentUser($this->
getCurrentUser() ?? CurrentUser::get());
118 $currentAction = $this->getCurrentAction();
122 $result = $controller->run(
127 $this->attachFilters($currentAction);
128 $this->
addErrors($controller->getErrors());
140 $this->buildConfigurationOfActions();
148 return $this->configurationOfActions;
162 private function getCurrentAction():
Action
164 return $this->currentAction;
167 private function setCurrentAction(Action $currentAction): self
169 $this->currentAction = $currentAction;
177 $firstLetter = mb_substr(basename($this->
getFilePath()), 0, 1);
179 return $firstLetter !== mb_strtolower($firstLetter);
184 if (!$this->filePath)
186 $reflector = new \ReflectionClass($this);
187 $this->filePath = preg_replace(
'#[\\\/]+#',
'/', $reflector->getFileName());
190 return $this->filePath;
203 final public function getActionUri(
string $actionName, array $params = [],
bool $absolute =
false):
Uri
205 if (strpos($this->
getFilePath(),
'/components/') ===
false)
218 return $this->unsignedParameters;
225 $signedParameters = $source[
'signedParameters'] ??
null;
226 if (is_string($signedParameters))
252 if (isset($source[
'c']) && is_string($source[
'c']))
266 return $this->currentUser;
274 $this->currentUser = $currentUser;
286 return $this->converter->process($data);
296 $lengthSuffix = mb_strlen(self::METHOD_ACTION_SUFFIX);
298 $class = new \ReflectionClass($this);
299 foreach ($class->getMethods(\ReflectionMethod::IS_PUBLIC) as $method)
301 $probablySuffix = mb_substr($method->getName(), -$lengthSuffix);
302 if ($probablySuffix === self::METHOD_ACTION_SUFFIX)
304 $actions[] = mb_strtolower(mb_substr($method->getName(), 0, -$lengthSuffix));
308 return array_unique($actions);
343 private function buildConfigurationOfActions(): void
345 $this->configurationOfActions = $this->configurator->getConfigurationByController($this);
353 return $this->request;
371 $this->scope = $scope;
381 return $this->sourceParametersList;
391 $this->sourceParametersList = $sourceParametersList;
403 final public function run($actionName, array $sourceParametersList)
414 $action = $this->
create($actionName);
417 throw new SystemException(
"Could not create action by name {$actionName}");
419 $this->setCurrentAction($action);
421 $this->attachFilters($action);
431 $result = $action->runWithSourceParametersList();
435 $this->errorCollection->add($action->getErrors());
441 if ($probablyResult !==
null)
443 $result = $probablyResult;
446 catch (\Throwable $e)
449 $this->processExceptionInDebug($e);
467 $exceptionHandler->writeToLog($e);
470 private function processExceptionInDebug(\Throwable $e)
472 if ($this->shouldWriteToLogException($e))
474 $this->writeToLogException($e);
477 $exceptionHandling = Configuration::getValue(
'exception_handling');
478 if (!empty($exceptionHandling[
'debug']))
481 if ($e->getPrevious())
488 private function shouldWriteToLogException(\Throwable $e): bool
490 if ($e instanceof BinderArgumentException)
495 if ($e instanceof SystemException && ($e->getCode() === self::EXCEPTION_UNKNOWN_ACTION))
505 return static::class .
'::' . $eventName;
546 return $this->request->isPost();
551 \CUtil::jSPostUnescape();
569 static::getFullEventName(static::EVENT_ON_BEFORE_ACTION),
572 'controller' => $this,
578 foreach ($event->getResults() as $eventResult)
580 if ($eventResult->getType() != EventResult::SUCCESS)
582 $handler = $eventResult->getHandler();
585 $this->errorCollection->add($handler->getErrors());
624 static::getFullEventName(static::EVENT_ON_AFTER_ACTION),
628 'controller' => $this,
635 return $event->getParameter(
'result');
640 return $action . self::METHOD_ACTION_SUFFIX;
648 if (method_exists($this, $methodName))
650 $method = new \ReflectionMethod($this, $methodName);
651 if ($method->isPublic() && mb_strtolower($method->getName()) === mb_strtolower($methodName))
658 if (!$config && ($this instanceof Contract\FallbackActionInterface))
665 "Could not find description of {$actionName} in {$this::className()}",
666 self::EXCEPTION_UNKNOWN_ACTION
678 if (isset($config[
'callable']))
680 $callable = $config[
'callable'];
681 if (!is_callable($callable))
689 if (empty($config[
'class']))
692 "Could not find class in description of {$actionName} in {$this::className()} to create instance",
693 self::EXCEPTION_UNKNOWN_ACTION
698 return new $config[
'class']($actionName, $this, $config);
705 $action = $this->
create($actionName);
715 return isset($action);
754 if ($config ===
null)
759 if (!isset($config[
'prefilters']))
761 $config[
'prefilters'] = $this->configurator->wrapFiltersClosure(
765 if (!isset($config[
'postfilters']))
767 $config[
'postfilters'] = $this->configurator->wrapFiltersClosure(
772 $hasPostMethod = $hasCsrfCheck =
false;
773 foreach ($config[
'prefilters'] as $filter)
775 if ($filter instanceof ActionFilter\HttpMethod && $filter->containsPostMethod())
777 $hasPostMethod =
true;
779 if ($filter instanceof ActionFilter\Csrf)
781 $hasCsrfCheck =
true;
785 if ($hasPostMethod && !$hasCsrfCheck && $this->request->isPost())
790 if (!empty($config[
'-prefilters']))
792 $config[
'prefilters'] = $this->
removeFilters($config[
'prefilters'], $config[
'-prefilters']);
795 if (!empty($config[
'-postfilters']))
797 $config[
'postfilters'] = $this->
removeFilters($config[
'postfilters'], $config[
'-postfilters']);
800 if (!empty($config[
'+prefilters']))
802 $config[
'prefilters'] = $this->
appendFilters($config[
'prefilters'], $config[
'+prefilters']);
805 if (!empty($config[
'+postfilters']))
807 $config[
'postfilters'] = $this->
appendFilters($config[
'postfilters'], $config[
'+postfilters']);
813 final protected function appendFilters(array $filters, array $filtersToAppend): array
815 return array_merge($filters, $filtersToAppend);
818 final protected function removeFilters(array $filters, array $filtersToRemove): array
820 $cleanedFilters = [];
821 foreach ($filters as $filter)
824 foreach ($filtersToRemove as $filterToRemove)
826 if (is_a($filter, $filterToRemove))
835 $cleanedFilters[] = $filter;
839 return $cleanedFilters;
842 final protected function attachFilters(
Action $action): void
844 $modifiedConfig = $this->buildFilters(
845 $this->getActionConfig($action->
getName())
849 foreach ($modifiedConfig[
'prefilters'] as $filter)
852 if (!in_array($this->getScope(), $filter->listAllowedScopes(),
true))
857 $filter->bindAction($action);
859 $this->eventHandlersIds[
'prefilters'][] = $eventManager->addEventHandler(
861 static::getFullEventName(static::EVENT_ON_BEFORE_ACTION),
862 [$filter,
'onBeforeAction']
866 foreach ($modifiedConfig[
'postfilters'] as $filter)
869 if (!in_array($this->getScope(), $filter->listAllowedScopes(),
true))
875 $filter->bindAction($action);
877 $this->eventHandlersIds[
'postfilters'][] = $eventManager->addEventHandler(
879 static::getFullEventName(static::EVENT_ON_AFTER_ACTION),
880 [$filter,
'onAfterAction']
894 foreach ($this->eventHandlersIds[
'prefilters'] as $handlerId)
896 $eventManager->removeEventHandler(
898 static::getFullEventName(static::EVENT_ON_BEFORE_ACTION),
903 $this->eventHandlersIds[
'prefilters'] = [];
909 foreach ($this->eventHandlersIds[
'postfilters'] as $handlerId)
911 $eventManager->removeEventHandler(
913 static::getFullEventName(static::EVENT_ON_AFTER_ACTION),
918 $this->eventHandlersIds[
'postfilters'] = [];
923 $listOfActions = array_change_key_case($this->configurationOfActions, CASE_LOWER);
924 $actionName = mb_strtolower($actionName);
926 if (!isset($listOfActions[$actionName]))
931 return $listOfActions[$actionName];
936 $this->configurationOfActions[$actionName] = $config;
947 elseif ($throwable instanceof \Exception)
951 elseif ($throwable instanceof \
Error)
976 $currentControllerErrors = $this->
getErrors();
980 foreach ($errors as $error)
982 if (in_array($error, $currentControllerErrors,
true))
1000 return new Error($e->getMessage(), self::ERROR_REQUIRED_PARAMETER);
1003 return new Error($e->getMessage(), $e->getCode());
1053 $this->errorCollection[] = $error;
1066 $this->errorCollection->add($errors);
1077 return $this->errorCollection->toArray();
1087 return $this->errorCollection->getErrorByCode($code);
static unsignParameters($componentName, $signedParameters)
static format($exception, $htmlMode=false, $level=0)
buildFilters(array $config=null)
runProcessingThrowable(\Throwable $throwable)
runProcessingIfUserNotAuthorized()
shouldDecodePostData(Action $action)
processAfterAction(Action $action, $result)
buildActionInstance($actionName, array $config)
const EVENT_ON_BEFORE_ACTION
convertKeysToCamelCase($data)
const ERROR_REQUIRED_PARAMETER
processUnsignedParameters()
setSourceParametersList($sourceParametersList)
const ERROR_UNKNOWN_ACTION
runProcessingException(\Exception $e)
getPrimaryAutoWiredParameter()
appendFilters(array $filters, array $filtersToAppend)
const EXCEPTION_UNKNOWN_ACTION
detachFilters(Action $action)
__construct(Request $request=null)
processBeforeAction(Action $action)
getActionConfig($actionName)
finalizeResponse(Response $response)
triggerOnBeforeAction(Action $action)
runProcessingBinderThrowable(BinderArgumentException $e)
runProcessingIfInvalidCsrfToken()
generateActionMethodName($action)
const EVENT_ON_AFTER_ACTION
buildErrorFromPhpError(\Error $error)
getSourceParametersList()
detachPreFilters(Action $action)
forward($controller, string $actionName, array $parameters=null)
runProcessingError(\Error $error)
static getFullEventName($eventName)
getActionUri(string $actionName, array $params=[], bool $absolute=false)
getDefaultAutoWiredParameters()
run($actionName, array $sourceParametersList)
getConfigurationOfActions()
setActionConfig($actionName, array $config=null)
buildErrorFromException(\Exception $e)
triggerOnAfterAction(Action $action, $result)
existsAction($actionName)
setCurrentUser(CurrentUser $currentUser)
detachPostFilters(Action $action)
removeFilters(array $filters, array $filtersToRemove)
Configurator $configurator
writeToLogException(\Throwable $e)