VvebOIDC/system/core/frontcontroller.php

439 lines
12 KiB
PHP

<?php
/**
* Vvveb
*
* Copyright (C) 2022 Ziadin Givan
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
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);
}
}