<?php
 include_once(LIB_PATH . 'exceptions' . DIRECTORY_SEPARATOR . 'TException.php');
 include_once(LIB_PATH . 'global.class.php');
 use TCMS\TException;
 class TCmsApplication {
 private function handlerAppError() {
 if (ENVIRONMENT == 'prod') {
 error_reporting(0);
 }
 set_error_handler(function ($errno, $errstr, $errfile, $errline) {
 include_once APP_PATH . 'bootstrap.php';
 $appBootstrap = new \AppBootstrap();
 $exception = new \TCMS\TException($errstr, $errno);
 $appBootstrap->error($exception);
 if (\Request::isAPI() && ENVIRONMENT == 'prod') {
 http_response_code($exception->getCode());
 exit($exception->getMessage());
 }
 else {
 $errorMessage = ($errstr . '<br><br><span style="display:none">' . $errno . '</span><code>File: ' . $errfile . ' <br>Line: ' . $errline . '</code>');
 include_once(LIB_PATH . 'exceptions' . DIRECTORY_SEPARATOR . 'error-view.php');
 }
 }
);
 set_exception_handler(function ($exception) {
 include_once APP_PATH . 'bootstrap.php';
 $appBootstrap = new \AppBootstrap();
 if ($exception instanceof \Exception) {
 $appBootstrap->error($exception);
 }
 else if (method_exists($exception, "getMessage")) {
 $appBootstrap->error($exception);
 }
 if (\Request::isAPI() && ENVIRONMENT == 'prod') {
 http_response_code($exception->getCode());
 exit($exception->getMessage());
 }
 else {
 $errorMessage = $exception;
 include_once(LIB_PATH . 'exceptions' . DIRECTORY_SEPARATOR . 'error-view.php');
 }
 }
);
 register_shutdown_function(function () {
 releaseApp();
 }
);
 }
 public function initAppConfig() {
 Config::loadConfig();
 if (!empty(\Config::$appConfig['timezone'])) {
 date_default_timezone_set(Config::getAppConfig('timezone'));
 }
 if (empty(\Config::$appConfig['areas'])) {
 throw new TException('Không tìm thấy cấu hình areas trong file config app.php', 501);
 }
 if (isset($_COOKIE['web_lang'])) {
 GlobalVars::$requestInfo['cur_lang'] = $_COOKIE['web_lang'];
 }
 else {
 GlobalVars::$requestInfo['cur_lang'] = Config::$appConfig['default_lang'];
 }
 }
 private function setLang() {
 if (isset($_GET['lang'])) {
 $fullUrl = $_SERVER["REQUEST_URI"];
 $arr = explode('?', $fullUrl);
 $url = $arr[0];
 $arr = explode('&', $arr[1]);
 $lang = "lang=" . $_GET['lang'];
 $isTheFist = true;
 foreach ($arr as $v) {
 if ($v == $lang) {
 continue;
 }
 if ($isTheFist) {
 $url .= '?' . $v;
 $isTheFist = false;
 }
 else {
 $url .= '&' . $v;
 }
 }
 setcookie("web_lang", $_GET['lang'], time() + (86400 * 30), "/");
 header("Location: " . $url);
 exit();
 }
 }
 private function loadStaticLib() {
 include_once(LIB_PATH . 'tcms' . DIRECTORY_SEPARATOR . 'Session.php');
 include_once(LIB_PATH . 'tcms' . DIRECTORY_SEPARATOR . 'Cache.php');
 \Session::init();
 }
 private function initHookExtension() {
 Config::$extensionConfig = include_once(CONFIG_PATH . GlobalVars::$_areaKey . '.extension.php');
 include_once(LIB_PATH . 'models' . DIRECTORY_SEPARATOR . 'Db.php');
 include_once(LIB_PATH . 'extensions' . DIRECTORY_SEPARATOR . 'TCmsExtension.php');
 include_once(LIB_PATH . 'models' . DIRECTORY_SEPARATOR . 'TCmsModel.php');
 __exec_hook_exte("startup", null);
 }
 private function loadModule() {
 if (defined('BIN_PATH')) {
 GlobalVars::$isPublish = true;
 Config::$moduleConfig = require_once(BIN_PATH . GlobalVars::$_areaKey . '-route-data.php');
 [$module, $layoutModule] = $this->findModulePublishByUrl();
 Config::$moduleConfig = null;
 }
 else {
 Config::$moduleConfig = require_once(CONFIG_PATH . GlobalVars::$_areaKey . '.module.php');
 [$module, $layoutModule, $notAllowUrl] = $this->findModuleByUrl();
 if (!$module && $notAllowUrl) {
 throw new TException('Method của ' . (GlobalVars::$requestInfo['area_url'] . GlobalVars::$requestInfo['url']) . ' là ' . (\Request::isPost() ? 'GET' : 'POST'), 405);
 }
 }
 if ($module != null) {
 GlobalVars::$requestInfo['module'] = $module->moduleName;
 GlobalVars::$requestInfo['private_module'] = !empty($module->isPrivate);
 __exec_hook_exte('beforeModuleLoad', $module);
 $module->loadModule($layoutModule);
 GlobalVars::$mViewClass = null;
 GlobalVars::$mRouteClass = null;
 GlobalVars::$extensionLoaded = null;
 GlobalVars::$__langFileLoaded = null;
 GlobalVars::$styleScriptAdded = null;
 GlobalVars::$overrideModels = null;
 }
 else {
 throw new TException('Không tìm thấy trang nào phù hợp với đường dẫn ' . GlobalVars::$requestInfo['area_url'] . GlobalVars::$requestInfo['url'], 404);
 }
 }
 private function findModulePublishByUrl() {
 include_once(LIB_PATH . 'modules' . DIRECTORY_SEPARATOR . 'TCmsModule.php');
 $_module = null;
 $_layoutModule = null;
 $isFound = false;
 foreach (Config::$moduleConfig as $moduleCfg) {
 GlobalVars::$routeList[$moduleCfg['url']] = true;
 if ($isFound) {
 continue;
 }
 if ($moduleCfg['type'] != \Request::$method) {
 continue;
 }
 if (_isMatcherRouteUrl($moduleCfg)) {
 if (!empty($moduleCfg['private'])) {
 $infoPath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . $moduleCfg['n'] . DIRECTORY_SEPARATOR . 'minfo.php';
 }
 else {
 $infoPath = MODULE_PATH . $moduleCfg['n'] . DIRECTORY_SEPARATOR . 'minfo.php';
 }
 if (!file_exists($infoPath)) {
 throw new TException("Không tìm thấy minfo file khi publish", 503);
 return;
 }
 $mInfo = include($infoPath);
 $moduleCfg['namespace'] = $mInfo['namespace'];
 $moduleCfg['private'] = $mInfo['private'];
 \GlobalVars::$requestInfo['route_url'] = $moduleCfg['url'];
 $module = $this->includeAndNewPublishModule($moduleCfg);
 if ($module === false) continue;
 $curRouteUrl = $moduleCfg['url'];
 $routes = $module->getRoutes();
 foreach ($routes as $r) {
 if ($r->type == \Request::$method && $r->url == $curRouteUrl) {
 \GlobalVars::$requestInfo['controller'] = $r->controller;
 \GlobalVars::$requestInfo['action'] = $r->action;
 \GlobalVars::$requestInfo['route_name'] = $r->name;
 break;
 }
 }
 if (empty(\GlobalVars::$requestInfo['controller'])) continue;
 $_module = $module;
 $_module->moduleName = $moduleCfg['n'];
 $_module->moduleNamespace = $moduleCfg['namespace'];
 if (empty($moduleCfg['private'])) {
 $_module->moduleAbsolutePath = MODULE_PATH . $_module->moduleName . DIRECTORY_SEPARATOR;
 }
 else {
 $_module->isPrivate = true;
 $_module->moduleAbsolutePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . $_module->moduleName . DIRECTORY_SEPARATOR;
 }
 $isFound = true;
 }
 }
 if ($_module) {
 AppPublish::$layoutModules = require_once(BIN_PATH . GlobalVars::$_areaKey . '-mlayout-data.php');
 $lastKey = array_key_last(AppPublish::$layoutModules);
 if (isset($lastKey)) {
 $moduleCfg = AppPublish::$layoutModules[$lastKey];
 $_layoutModule = $this->includeAndNewPublishModule($moduleCfg);
 $_layoutModule->moduleName = $moduleCfg['n'];
 $_layoutModule->moduleNamespace = $moduleCfg['namespace'];
 if (empty($moduleCfg['private'])) {
 $_layoutModule->moduleAbsolutePath = MODULE_PATH . $_layoutModule->moduleName . DIRECTORY_SEPARATOR;
 }
 else {
 $_layoutModule->isPrivate = true;
 $_layoutModule->moduleAbsolutePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . $_layoutModule->moduleName . DIRECTORY_SEPARATOR;
 }
 $layoutMenuData = include_once(BIN_PATH . GlobalVars::$_areaKey . '-layout-data.php');
 foreach ($layoutMenuData as $menuData) {
 $_layoutMenuItem = new LayoutMenuItem();
 $_layoutMenuItem->id = $menuData['id'];
 $_layoutMenuItem->url = url($menuData['url']);
 $_layoutMenuItem->text = $menuData['text'];
 $_layoutMenuItem->layoutMenuId = $menuData['layoutMenuId'];
 $_layoutMenuItem->icon = $menuData['icon'];
 $_layoutMenuItem->class = $menuData['class'];
 $_layoutMenuItem->displayAfter = $menuData['displayAfter'];
 $_layoutMenuItem->parentMenuId = $menuData['parentMenuId'];
 $_layoutMenuItem->subMenuTitle = $menuData['subMenuTitle'];
 if ($menuData['middleware']) {
 $_layoutMenuItem->middleware = $menuData['middleware'];
 }
 \LayoutData::$layoutMenuItem[$_layoutMenuItem->url] = $_layoutMenuItem;
 }
 }
 }
 return [$_module, $_layoutModule];
 }
 private function findModuleByUrl() {
 include_once(LIB_PATH . 'modules' . DIRECTORY_SEPARATOR . 'TCmsModule.php');
 $_module = null;
 $_layoutModule = null;
 $url = GlobalVars::$requestInfo['url'];
 $moduleList = Config::$moduleConfig;
 $notAllowUrl = null;
 foreach ($moduleList as $moduleName => $moduleConfig) {
 $module = $this->includeAndNewModule($moduleName, $moduleConfig);
 if ($module === false) {
 continue;
 }
 if (!$module->isLayout) {
 $routes = $module->getRoutes();
 foreach ($routes as $route) {
 GlobalVars::$routeList[$route->url] = true;
 }
 }
 if ($module->isLayout && Request::isAPI()) {
 $module->destroy();
 unset($module);
 $module = null;
 continue;
 }
 if (!$module->isLayout) {
 if (!devIsMatcherUrl($routes, $url)) {
 if (devIsMatcherUrl($routes, $url, true)) {
 $notAllowUrl = true;
 }
 $module->destroy();
 unset($module);
 $module = null;
 continue;
 }
 }
 if ($module->isLayout) {
 if ($_layoutModule != null) {
 $_layoutModule->destroy();
 unset($_layoutModule);
 $_layoutModule = null;
 }
 $_layoutModule = $this->releaseAndSetModule($module, $moduleName, $moduleConfig);
 }
 else {
 if ($_module != null) {
 $_module->destroy();
 unset($_module);
 $_module = null;
 }
 $_module = $this->releaseAndSetModule($module, $moduleName, $moduleConfig);
 \GlobalVars::$requestInfo['module_key'] = $moduleName;
 }
 }
 return [$_module, $_layoutModule, $notAllowUrl];
 }
 private function releaseAndSetModule($module, $moduleName, $moduleConfig) {
 $_src = $module;
 $_src->moduleName = $moduleName;
 $_src->moduleNamespace = $moduleConfig['namespace'];
 if (isset($moduleConfig['path'])) {
 $_src->moduleAbsolutePath = $moduleConfig['path'] . $moduleName . DIRECTORY_SEPARATOR;
 }
 else {
 if (empty($moduleConfig['private'])) {
 $_src->moduleAbsolutePath = MODULE_PATH . \GlobalVars::$_areaKey . DIRECTORY_SEPARATOR . $moduleName . DIRECTORY_SEPARATOR;
 }
 else {
 $_src->isPrivate = true;
 $_src->moduleAbsolutePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . \GlobalVars::$_areaKey . DIRECTORY_SEPARATOR . $moduleName . DIRECTORY_SEPARATOR;
 }
 }
 return $_src;
 }
 private function includeAndNewModule($moduleName, $moduleConfig) {
 if (isset($moduleConfig['path'])) {
 $moduleFilePath = $moduleConfig['path'] . $moduleName . DIRECTORY_SEPARATOR . $moduleName . '.php';
 }
 else {
 if (empty($moduleConfig['private'])) {
 $moduleFilePath = MODULE_PATH . \GlobalVars::$_areaKey . DIRECTORY_SEPARATOR . $moduleName . DIRECTORY_SEPARATOR . $moduleName . '.php';
 }
 else {
 $moduleFilePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . \GlobalVars::$_areaKey . DIRECTORY_SEPARATOR . $moduleName . DIRECTORY_SEPARATOR . $moduleName . '.php';
 }
 }
 if (!file_exists($moduleFilePath)) {
 return false;
 }
 include_once($moduleFilePath);
 \GlobalVars::$curModulePath = dirname($moduleFilePath) . DIRECTORY_SEPARATOR;
 $moduleCls = "\\" . $moduleConfig['namespace'];
 return new $moduleCls();
 }
 private function includeAndNewPublishModule($moduleConfig) {
 $moduleName = $moduleConfig['n'];
 if (empty($moduleConfig['private'])) {
 $moduleFilePath = MODULE_PATH . $moduleName . DIRECTORY_SEPARATOR . $moduleName . '.php';
 }
 else {
 $moduleFilePath = APP_PATH . 'modules' . DIRECTORY_SEPARATOR . $moduleName . DIRECTORY_SEPARATOR . $moduleName . '.php';
 }
 if (!file_exists($moduleFilePath)) {
 return false;
 }
 include_once($moduleFilePath);
 \GlobalVars::$curModulePath = dirname($moduleFilePath) . DIRECTORY_SEPARATOR;
 $moduleCls = "\\" . $moduleConfig['namespace'];
 return new $moduleCls();
 }
 private function initEnv() {
 if (file_exists(APP_PATH . 'env.php')) Config::$envConfig = require(APP_PATH . 'env.php');
 define('APP_VERSION', env('app_version', '1.0'));
 define("ENVIRONMENT", env('environment', 'dev'));
 }
 public function run() {
 $this->initEnv();
 $this->setLang();
 $this->handlerAppError();
 $this->initAppConfig();
 $this->loadStaticLib();
 $this->initRequestInfo();
 $this->initHookExtension();
 $this->loadModule();
 releaseApp();
 }
 private function removeEmptyFromUrl($url) {
 $arr = [];
 $urls = explode('/', $url);
 foreach ($urls as $u) {
 if ($u) $arr[] = $u;
 }
 return '/' . implode('/', $arr);
 }
 private function parseWebUrl() {
 $url = $_SERVER['REQUEST_URI'];
 $length = strlen(dirname($_SERVER['SCRIPT_NAME']));
 if ($length > 1) {
 GlobalVars::$requestInfo['base_url'] = substr($url, 0, $length);
 $url = $this->removeEmptyFromUrl(substr($url, $length));
 }
 else {
 $arrUrl = explode('/', $_SERVER['DOCUMENT_ROOT']);
 if (count($arrUrl) == 1) {
 $arrUrl = explode('\\', $_SERVER['DOCUMENT_ROOT']);
 }
 $url = $this->removeEmptyFromUrl($url);
 $arrUrl2 = explode('/', $url);
 if (count($arrUrl2) > 2) {
 $index = false;
 foreach ($arrUrl as $v) {
 if (!$index) {
 if ($v == $arrUrl2[1]) {
 $index = 2;
 }
 }
 else {
 if ($v == $arrUrl2[$index]) {
 $index++;
 continue;
 }
 else {
 break;
 }
 }
 }
 if ($index) {
 for ($i = 0;
 $i < $index;
 $i++) {
 unset($arrUrl2[$i]);
 }
 $url = "/" . implode("/", $arrUrl2);
 }
 }
 }
 $arrUrl = explode('?', $url);
 $arrUrl = explode('#', $arrUrl[0]);
 $url = str_replace("/index.php", "", $arrUrl[0]);
 if (!isset($url) || $url == '') {
 $url = '/';
 }
 if (!GlobalVars::$requestInfo['base_url']) {
 GlobalVars::$requestInfo['base_url'] = host_url();
 }
 GlobalVars::$requestInfo['url'] = $url;
 }
 private function getSubDomain() {
 $c = \Config::getAppConfig('subdomain_area');
 if ($c) {
 $host = $_SERVER['HTTP_HOST'];
 $arr = explode('.', $host);
 $isWWW = $arr[0] == "www";
 if ((!$isWWW && count($arr) > 2) || ($isWWW && count($arr) > 3)) {
 $area = $isWWW ? $arr[1] : $arr[0];
 $langs = \Config::getAppConfig('lang');
 if (array_key_exists($area, $langs)) {
 GlobalVars::$requestInfo['cur_lang'] = $area;
 return false;
 }
 return $area;
 }
 }
 return false;
 }
 private function getBaseUrlWithoutSubdomain() {
 $host = $_SERVER['HTTP_HOST'];
 $arr = explode('.', $host);
 $isWWW = $arr[0] == "www";
 if ($isWWW) {
 unset($arr[0]);
 unset($arr[1]);
 return implode(".", $arr);
 }
 else {
 unset($arr[0]);
 return implode(".", $arr);
 }
 }
 private function initRequestInfo() {
 $this->parseWebUrl();
 $areasList = Config::getAppConfig('areas');
 $subDomain = $this->getSubDomain();
 if ($subDomain) {
 if ($subDomain == 'm') {
 if (\Config::getAppConfig('detect_mobi_device')) {
 \GlobalVars::$mobileDevice = true;
 }
 }
 else {
 $subDomain = '/' . $subDomain;
 if (in_array($subDomain, $areasList)) {
 $area = array_search($subDomain, $areasList);
 GlobalVars::$_areaKey = $area;
 $h = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https://" : "http://");
 GlobalVars::$requestInfo['area_url'] = $h . $_SERVER['HTTP_HOST'];
 GlobalVars::$requestInfo['base_url'] = $h . $this->getBaseUrlWithoutSubdomain();
 GlobalVars::$requestInfo['subdomain'] = true;
 Request::parseRequestInfo();
 return;
 }
 }
 }
 foreach ($areasList as $key => $value) {
 if (strpos(GlobalVars::$requestInfo['url'], $value) === 0) {
 $u = substr(GlobalVars::$requestInfo['url'], strlen($value));
 if($u && $u[0] != '/') continue;
 GlobalVars::$_areaKey = $key;
 GlobalVars::$requestInfo['url'] = $u;
 if (strpos(GlobalVars::$requestInfo['url'], "/") !== 0) {
 GlobalVars::$requestInfo['url'] = '/' . GlobalVars::$requestInfo['url'];
 }
 if ($value != '/') GlobalVars::$requestInfo['area_url'] = GlobalVars::$requestInfo['base_url'] . $value;
 else GlobalVars::$requestInfo['area_url'] = GlobalVars::$requestInfo['base_url'];
 break;
 }
 }
 if (GlobalVars::$_areaKey == '') {
 throw new TException("Không tìm thấy đường dẫn area trên url " . GlobalVars::$requestInfo['url'], 404);
 }
 Request::parseRequestInfo();
 }
 }
 