<commit_before><?php
/**
 * Spiral Framework.
 *
 * @license   MIT
 * @author    <NAME> (Wolfy-J)
 */
namespace Spiral\Core;

use Interop\Container\ContainerInterface as InteropContainer;
use Spiral\Core\Exceptions\Container\ArgumentException;
use Spiral\Core\Exceptions\ControllerException;
use Spiral\Core\HMVC\ControllerInterface;
use Spiral\Core\Traits\SaturateTrait;
use Spiral\Debug\Traits\BenchmarkTrait;

/**
 * Basic application controller class. Implements method injections and simplified access to
 * container bindings.
 *
 * @todo Potentially move resolver to callAction method? Maybe not.
 */
abstract class Controller extends Service implements ControllerInterface
{
    /**
     * To benchmark action execution time.
     */
    use BenchmarkTrait, SaturateTrait;

    /**
     * Action method prefix value.
     *
     * @var string
     */
    const ACTION_PREFIX = '';

    /**
     * Action method postfix value.
     *
     * @var string
     */
    const ACTION_POSTFIX = 'Action';

    /**
     * Default action to run.
     *
     * @var string
     */
    protected $defaultAction = 'index';

    /**
     * Needed to resolve method arguments.
     *
     * @invisible
     * @var ResolverInterface
     */
    protected $resolver = null;

    /**
     * @param InteropContainer  $container
     * @param ResolverInterface $resolver Required to resolve method injections.
     */
    public function __construct(
        InteropContainer $container = null,
        ResolverInterface $resolver = null
    ) {
        parent::__construct($container);

        $this->resolver = $this->saturate($resolver, ResolverInterface::class);
    }

    /**
     * {@inheritdoc}
     */
    public function callAction($action = '', array $parameters = [])
    {
        //Action should include prefix and be always specified
        $action = static::ACTION_PREFIX
            . (!empty($action) ? $action : $this->defaultAction)
            . static::ACTION_POSTFIX;

        if (!method_exists($this, $action)) {
            throw new ControllerException(
                "No such action '{$action}'.", ControllerException::BAD_ACTION
            );
        }

        $reflection = new \ReflectionMethod($this, $action);

        if (!$this->isExecutable($reflection)) {
            //Need different exception code here
            throw new ControllerException(
                "Action '{$action}' can not be executed.",
                ControllerException::BAD_ACTION
            );
        }

        //Needed to be called via reflection
        $reflection->setAccessible(true);

        //Executing our action
        return $this->executeAction(
            $reflection,
            $this->resolveArguments($reflection, $parameters),
            $parameters
        );
    }

    /**
     * @param \ReflectionMethod $method
     * @param array             $arguments
     * @param array             $parameters
     * @return mixed
     */
    protected function executeAction(\ReflectionMethod $method, array $arguments, array $parameters)
    {
        $benchmark = $this->benchmark($method->getName());

        try {
            //Targeted controller method got called.
            return $method->invokeArgs($this, $arguments);
        } finally {
            $this->benchmark($benchmark);
        }
    }

    /**
     * Check if method is callable.
     *
     * @param \ReflectionMethod $method
     * @return bool
     */
    protected function isExecutable(\ReflectionMethod $method)
    {
        if ($method->isStatic() || !$method->isUserDefined()) {
            return false;
        }

        //Place to implement custom logic
        return true;
    }

    /**
     * Resolve controller method arguments.
     *
     * @param \ReflectionMethod $method
     * @param array             $parameters
     * @return array
     */
    protected function resolveArguments(\ReflectionMethod $method, array $parameters)
    {
        try {
            //Getting set of arguments should be sent to requested method
            return $this->resolver->resolveArguments($method, $parameters);
        } catch (ArgumentException $exception) {
            throw new ControllerException(
                "Missing/invalid parameter '{$exception->getParameter()->name}'.",
                ControllerException::BAD_ARGUMENT
            );
        }
    }
}<commit_msg>minor interface corrections<commit_after><?php
/**
 * Spiral Framework.
 *
 * @license   MIT
 * @author    <NAME> (Wolfy-J)
 */
namespace Spiral\Core;

use Interop\Container\ContainerInterface as InteropContainer;
use Spiral\Core\Exceptions\Container\ArgumentException;
use Spiral\Core\Exceptions\ControllerException;
use Spiral\Core\HMVC\ControllerInterface;
use Spiral\Core\Traits\SaturateTrait;
use Spiral\Debug\Traits\BenchmarkTrait;

/**
 * Basic application controller class. Implements method injections and simplified access to
 * container bindings.
 *
 * @todo Potentially move resolver to callAction method? Maybe not.
 */
abstract class Controller extends Service implements ControllerInterface
{
    /**
     * To benchmark action execution time.
     */
    use BenchmarkTrait, SaturateTrait;

    /**
     * Action method prefix value.
     *
     * @var string
     */
    const ACTION_PREFIX = '';

    /**
     * Action method postfix value.
     *
     * @var string
     */
    const ACTION_POSTFIX = 'Action';

    /**
     * Needed to resolve method arguments.
     *
     * @invisible
     * @var ResolverInterface
     */
    private $resolver = null;

    /**
     * Default action to run.
     *
     * @var string
     */
    protected $defaultAction = 'index';

    /**
     * @param InteropContainer  $container
     * @param ResolverInterface $resolver Required to resolve method injections.
     */
    public function __construct(
        InteropContainer $container = null,
        ResolverInterface $resolver = null
    ) {
        parent::__construct($container);

        $this->resolver = $this->saturate($resolver, ResolverInterface::class);
    }

    /**
     * {@inheritdoc}
     */
    public function callAction($action = '', array $parameters = [])
    {
        //Action should include prefix and be always specified
        $action = static::ACTION_PREFIX
            . (!empty($action) ? $action : $this->defaultAction)
            . static::ACTION_POSTFIX;

        if (!method_exists($this, $action)) {
            throw new ControllerException(
                "No such action '{$action}'.", ControllerException::BAD_ACTION
            );
        }

        $reflection = new \ReflectionMethod($this, $action);

        if (!$this->isExecutable($reflection)) {
            //Need different exception code here
            throw new ControllerException(
                "Action '{$action}' can not be executed.",
                ControllerException::BAD_ACTION
            );
        }

        //Needed to be called via reflection
        $reflection->setAccessible(true);

        //Executing our action
        return $this->executeAction(
            $reflection,
            $this->resolveArguments($reflection, $parameters),
            $parameters
        );
    }

    /**
     * @param \ReflectionMethod $method
     * @param array             $arguments
     * @param array             $parameters
     * @return mixed
     */
    protected function executeAction(\ReflectionMethod $method, array $arguments, array $parameters)
    {
        $benchmark = $this->benchmark($method->getName());

        try {
            //Targeted controller method got called.
            return $method->invokeArgs($this, $arguments);
        } finally {
            $this->benchmark($benchmark);
        }
    }

    /**
     * Check if method is callable.
     *
     * @param \ReflectionMethod $method
     * @return bool
     */
    protected function isExecutable(\ReflectionMethod $method)
    {
        if ($method->isStatic() || !$method->isUserDefined()) {
            return false;
        }

        //Place to implement custom logic
        return true;
    }

    /**
     * Resolve controller method arguments.
     *
     * @param \ReflectionMethod $method
     * @param array             $parameters
     * @return array
     */
    private function resolveArguments(\ReflectionMethod $method, array $parameters)
    {
        try {
            //Getting set of arguments should be sent to requested method
            return $this->resolver->resolveArguments($method, $parameters);
        } catch (ArgumentException $exception) {
            throw new ControllerException(
                "Missing/invalid parameter '{$exception->getParameter()->name}'.",
                ControllerException::BAD_ARGUMENT
            );
        }
    }
}
{'fraction_non_alphanumeric': 0.1080773606370876, 'fraction_numerical': 0.0, 'mean_word_length': 2.1229129662522204, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 39, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6867397', 'n_tokens_mistral': 2278, 'n_tokens_neox': 2205, 'n_words': 582}