Продолжая разбор ядра CMS OpenCart, а, по-моему, правильнее было бы сказать "фреймворка" OpenCart, опишу, в данной статье, класс Action. Данный класс используется при вызове нужных методов всех контроллеров в OpenCart.

Файл с данным классом подключается одним из первых на этапе загрузки приложения в файле system\startup.php:
require_once(modification(DIR_SYSTEM . 'engine/action.php'));<span class="redactor-invisible-space"></span>

и используется при вызове стандартных событий OpenCart, подключаемых в файле system\framework.php строкой
if ($config->has('action_event')) {
    foreach ($config->get('action_event') as $key => $value) {
        $event->register($key, new Action($value));
    }
}
далее в этом же файле, для выполнения стандартных действий из папки controller\startup:
if ($config->has('action_pre_action')) {
    foreach ($config->get('action_pre_action') as $value) {
        $controller->addPreAction(new Action($value));
    }
}
а так же для выполнения всех других контроллеров(методов) , например действий согласно разбора текущего URL.

Основные задачи, выполняемые объектом класса Action:
  • разбор переданного методу-конструктору параметра $route. Определение названия и пути к нужному контроллеру и метода, который необходимо вызвать;
  • выполнение данного метода контроллера (используется класс Reflection API) с возвратом полученного результата.

Далее код данного класса с подробнейшим описанием (файл system\engine\action.php)

<?php
//DIR_APPLICATION - константа для указания директории с папкой контроллеров из файла config.php
define('DIR_APPLICATION', ''); 

class Action {    
    private $id;
    private $route;
    private $method = 'index';
    
    public function __construct($route) {
        /* Разбирает $route типа common/fun/get на элементы массива по разделителю "/"
        [0] => common
        [1] => fun 
        [2] => get
         */
        $parts = explode('/', preg_replace('/[^a-zA-Z0-9_\/]/', '', (string)$route));

        //Цикл выполняется пока $parts =  true или выполняется оператор break;
        while ($parts) {
            /*
             * Формирует строку элементов массива добавляя спереди и после символы
             * результат: ./controllers/common/fun/get.php
             */
            $file = DIR_APPLICATION . 'controller/' . implode('/', $parts) . '.php';

            if (is_file($file)) {
                //Если файл найден - формирует строку типа common/fun и сохраняет 
                $this->route = implode('/', $parts);
                break;
            } else {
                /*
                 * Если файл не найден - значит последний элемент массива - название метода
                 * Сохраняем последний элемент и удаляем его из массива $parts
                 */
                $this->method = array_pop($parts);
            }
        }
    }
    
    public function getId() {
        return $this->id;
    }
    /*
     * $registry - в Opencart используется для передачи в конструктор создаваемого 
     *  объекта (контроллера) - объекта-регистратора, для доступа через него к методам 
     *  других объектов
     * $args - аргументы для вызываемого метода
     */
    public function execute($registry=null, array $args = array()) {
        // Не разрешаем вызов магических методов
        if (substr($this->method, 0, 2) == '__') {
            return new \Exception('Ошибка: Запрещены вызовы к магическим методам!');
        }

        //Формируем путь к файлу
        $file = DIR_APPLICATION . 'controller/' . $this->route . '.php';    
        //Формирует название класса контроллера. Получается Controllercommonfun
        $class = 'Controller' . preg_replace('/[^a-zA-Z0-9]/', '', $this->route);

        if (is_file($file)) {
            include_once($file); //подключаем файл контроллера    
            $controller = new $class($registry); //создаем экземпляр класса контроллера
        } else {
            return new \Exception('Ошибка: Невозможно вызвать ' . $this->route . '/' . $this->method . '!');
        }
        //Создаем экземпляр класса Reflection API для получения информации о интересующем классе
        $reflection = new ReflectionClass($class);

        /*
         * Проверяем задан ли указанный метод, а так же проверяем, чтобы у этого метода кол-во обязательных аргументов
         * не превысило те, которые были переданы
         */
        if ($reflection->hasMethod($this->method) && $reflection->getMethod($this->method)->getNumberOfRequiredParameters() <= count($args)) {
            //Вызываем  метод $this->method объекта $controller с массивом аргументов $args
            return call_user_func_array(array($controller, $this->method), $args);
        } else {
            return new \Exception('Ошибка: Невозможно вызвать ' . $this->route . '/' . $this->method . '!');
        }
    }
}

Для демонстрации работы объекта класса Action, я создал парочку действий. Аналогично тому, что происходит в OpenCart, они передают в метод-конструктор при создании объекта параметр состоящий из
[путь к файлу контроллера] [название класса контроллера] [метод который нужно выполнить]



Стоит отметить, что в OpenCart, большинство классов контроллеров состоит из единственного метода "index". Поэтому предусмотрена возможность не указывать метод, если он является методом "по-умолчанию". Это продемонстрировано в первом примере.
Во-втором примере вызываем определенный метод нужного контроллера с передачей ему массива аргументов.
Результат вызовов будет отображен на экран.

Если вы захотите протестировать данный класс, можно скачать архив с файлами этих классов.

/*
 * 1. Простой вызов действия. Выполнится метод по-умолчанию - index
 */
$action = new Action('common/fun');
$output = $action->execute();
echo $output . '<br>'; //Выполнен метод index


/*
 * 2. Если нужен не метод по-умолчанию - указываем явно
 */
$action = new Action('common/fun/get');
/*
 * В массиве первым элементом можно передать аргумент для метода конструктора создаваемого экземпляра
 * класса контроллера
 * Вторым элементом передаются аргументы для метода (в массиве)
 */
$output = $action->execute(null, [10]);
echo $output; //Выполнен метод get. Значение 10

Файл controller\common\fun.php с примитивным классом контроллера для тестирования
<?php
/*
 * Для динамического формирования имени в методе execute() класса Action,
 * Имя должно состоять из Controller[путь к файлу][название]
 */
class ControllerCommonFun {
    function index(){
        return 'Выполнен метод index';
    }
    function get($date){
        return "Выполнен метод get. Значение $date";
    }
}