. * */ namespace Vvveb\System\Core; use function Vvveb\__; use Vvveb\System\Component\Component; use Vvveb\System\Event; use Vvveb\System\PageCache; use Vvveb\System\Routes; use Vvveb\System\Session; #[\AllowDynamicProperties] class FrontController { /** * Standard Controller constructor. */ static private $moduleName; static private $actionName; static private $app = 'app'; static public $rewrite; static private $status = 200; /** * Returns current controller name. * * @return string */ static function getModuleName() { return self :: $moduleName; } static function app($app = null) { if ($app) { self :: $app = $app; } return self :: $app; } /** * Returns current controller name. * * @return string */ static function getActionName() { return self :: $actionName; } /** * Returns current status. * * @return string */ static function getStatus() { return self :: $status; } static function setStatus($status) { return self :: $status = $status; } static private function callAction($controller, $action = 'index') { if ($statusCode !== 200) { header(' ', true, $statusCode); } if (include_once DIR_APP . DS . 'controller' . DS . "$controller.php") { $controller = 'Vvveb\Controller\\' . $controller; self :: $moduleName = $moduleName = $controller; $controller = 'Vvveb\Controller\\' . $controller; } } static function notFound($service = false, $message = false, $statusCode = 404) { self :: $status = $statusCode; //@header(' ', true, $statusCode); /* $view = View :: getInstance(); //$view = new View(); $view->setTheme(); if (is_string($message)) { $message = ['message' => $message]; } $view->set($message); PageCache::getInstance()->cleanUp(); self :: redirect('Error' . $statusCode, 'index'); die(0);*/ if (include_once DIR_APP . DS . 'controller' . DS . "error$statusCode.php") { $controller = 'Vvveb\Controller\Error' . $statusCode; self :: $moduleName = $moduleName = 'error' . $statusCode; $controller = 'Vvveb\Controller\Error' . $statusCode; //header("HTTP/1.1 $statusCode" /* . substr(str_replace(($message ?? ''), "\n", ' '), 100)*/); //die($statusCode); http_response_code($statusCode); } else { //header(' ', true, $statusCode); http_response_code($statusCode); die("Http error $statusCode"); } //$view = View :: getInstance(); $response = Response::getInstance(); if ($service) { $view = View::getInstance(); } else { $view = new View(); } $view->setTheme(); if (is_string($message)) { $message = ['message' => $message]; } $view->set($message); $view->template(self :: $moduleName . '.html'); //default html $controller = new $controller(); $controller->response = $response; $controller->view = $view; $template = call_user_func([$controller, 'index']); if ($template === false) { $view->template(false); } else { if (is_array($template)) { return $response->output($template); //echo json_encode($template); } else { if ($template) { $view->template($template); } } } /* if ($service === true) { $component = Component :: getInstance(); } */ //header(' ', true, $statusCode); PageCache::getInstance()->cleanUp(); $view->setType($response->getType()); $view->render(); die(); //return $response->output(); //self :: closeConnections(); //$view->render($service, true, $service); } static function closeConnections() { if (defined('DB_ENGINE') && ($instance = \Vvveb\System\Db::getInstance())) { $instance->close(); } } /** * Inject dependencies. * * @param string $controller */ static function di(&$controller) { $controller->request = Request::getInstance(); $controller->response = Response::getInstance(); $controller->view = View::getInstance(); $controller->session = Session::getInstance(); } /** * Initializes the controller class and calls the action. * * @param string $controllerClass * @param string $actionName * @param string $file */ static function call($controllerClass, $actionName, $file = false) { if ((! @include_once(DIR_APP . DS . 'controller' . DS . 'base.php')) || (! file_exists($file) || ! @include_once($file))) { $message = [ 'message' => __('Controller file not found!'), 'file' => $file, ]; return self :: notFound(false, $message); } // We check if the controller's class really exists $controller = false; if (class_exists($controllerClass , false)) {// if the controller does not exist route to controller main $controller = new $controllerClass(); if (! $controller || ! method_exists($controller , $actionName) || $actionName == 'init') { $message = [ 'message' => __('Method does not exist!'), 'file' => "$controllerClass :: $actionName", ]; return self :: notFound(false, $message); } } else { $message = [ 'message' => __('Controller does not exist!'), 'file' => $controllerClass, ]; return self :: notFound(false, $message); } self :: di($controller); if (method_exists($controller, 'init')) { $return = $controller->init(); if ($return) { $actionName = $return; if (! method_exists($controller , $actionName)) { $message = [ 'message' => __('Method does not exist!'), 'file' => "$controllerClass :: $actionName", ]; return self :: notFound(false, $message); } } } $response = Response::getInstance(); $template = str_replace('/', DS, strtolower(self :: $moduleName)); $theme = $controller->view->getTheme(); $path = DIR_THEME . $theme . DS; $isPlugin = strncmp($template, 'plugins/', 8) === 0; if ($actionName && $actionName != 'index') { if ($isPlugin) { $t = str_replace('plugins' . DS, '', $template); $p = strpos($t, DS); $pluginName = substr($t, 0, $p); $nameSpace = substr($t, $p + 1); $html = DIR_PLUGINS . $pluginName . DS . 'public' . DS . APP . DS . $nameSpace . DS . strtolower($actionName) . '.html'; } else { $html = $path . $template . DS . strtolower($actionName) . '.html'; } if (is_file($html)) { $template .= DS . strtolower($actionName); } } $controller->view->template($template . '.html'); //default html that can be overwritten //list($template) = Event :: trigger($controllerClass, "$actionName:before", $template); //$controller->view->template($template . '.html'); //default html $template = call_user_func([$controller, $actionName]); $responseType = $response->getType(); $controller->view->setType($responseType); if ($template === false) { $controller->view->template(false); } else { if (is_array($template)) { $response->output($template); //echo json_encode($template); } else { if ($template) { $controller->view->template($template); } } } //list($responseType) = Event :: trigger($controllerClass, "$actionName:after", $response->getType('json')); $response->setStatus(self :: $status); $return = $response->output(); self :: closeConnections(); return $return; } /** * Redirect or direct to a action or default controller action and parameters * it has the ability to http redirect to the specified action * internally used to direct to action. * * @param string $moduleName * @param string $actionName * @param array $parameters * @param bool $httpRedirect * @return bool */ static function redirect($moduleName , $actionName = 'index') { self :: $moduleName = $moduleName; self :: $actionName = $actionName; if (is_dir(DIR_APP . DS . 'controller' . DS . strtolower($moduleName))) { self :: $moduleName = $moduleName .= '/Index'; } $dir = strtolower(str_replace('/', DS, $moduleName)); $className = \Vvveb\dashesToCamelCase(str_replace(['/', DS], '\\', $moduleName)); $controllerClass = 'Vvveb\Controller\\' . $className; //change file paths for plugins if (strpos($moduleName, 'Plugins/') === 0) { $dir = str_replace('plugins' . DS, '', $dir); $p = strpos($dir, DS); $pluginName = $dir; $nameSpace = 'index'; if ($p !== false) { $pluginName = substr($dir, 0, $p); $nameSpace = substr($dir, $p + 1); } //$className = str_replace('Plugins\\', '', $className); $file = DIR_PLUGINS . $pluginName . DS . APP . DS . 'controller' . DS . "$nameSpace.php"; $pluginName = \Vvveb\dashesToCamelCase($pluginName); //insert Controller namespace $className = str_replace('Plugins\\' . $pluginName, $pluginName . '\Controller', $className); $controllerClass = 'Vvveb\Plugins\\' . $className; } else { $file = DIR_APP . 'controller' . DS . $dir . '.php'; } return self :: call($controllerClass, $actionName, $file); } static public function getRoute() { return $_GET['route'] ?? ''; } static public function getModule() { return $_GET['module'] ?? ''; } static public function dispatch() { $module = $_GET['module'] ?? $_POST['module'] ?? null; $action = $_GET['action'] ?? $_POST['action'] ?? null; $_REQUEST = array_merge($_GET, $_REQUEST); //subdirectory support /* $subdir = \Vvveb\pregMatch('@(.+)/index.php@',$_SERVER['SCRIPT_NAME'], 1); $subdir = str_replace(['/public', '/admin'], '', $subdir); define('V_SUBDIR_INSTALL', $subdir); */ //remove GET parameters to allow correct matching, $uri = $_SERVER['REQUEST_URI'] ?? ''; if (V_SUBDIR_INSTALL) { $uri = substr($uri, strlen(V_SUBDIR_INSTALL)); //$uri = str_replace(V_SUBDIR_INSTALL, '', $uri); } $uri = preg_replace('/\?.*$/', '', $uri); $route = false; if (! $module && (APP != 'admin' && APP != 'install' && (Routes::init(APP) && $route = Routes::match($uri)))) { $_GET = array_merge($route, $_GET); } else { $module = $module ?? ((APP == 'install' || APP == 'admin') ? 'index' : 'error404'); if (! $module && APP == 'app') { self :: $status = 404; } } if ($route) { if (preg_match('@(^.+?)/(\w+$)@', $_GET['module'], $routeMatch)) { $module = $routeMatch[1]; $action = $action ?? $routeMatch[2]; } else { $module = trim($_GET['module'], '/'); } } if (empty($action)) { $action = 'index'; } //santize inputs if (($module && ! preg_match('@^[a-zA-Z_/0-9\-]{4,70}$@', $module)) || ($action && ! preg_match('@^[a-zA-Z_/0-9\-]{3,70}$@', $action))) { return self::notFound(false, ['message' => 'Invalid request!'], 500); } $path = $module; $module = ucfirst($module); $path = ''; array_map(function ($value) use (&$path) { if ($path) { $path .= '/'; } $path .= ucfirst($value); }, explode('/', $module)); return self :: redirect($path, $action); } }