* * @package Webapp\Data */ final class Cache { private string $cachedir; private string $cssdir; private string $localedir; private string $jsdir; private string $jsapp; private string $rootdir; private array $ini; private array $web_app; private array $web_user; private bool $is_login; private bool $IS_DEV = false; protected Engine $app; protected SimplePdo $db; protected array $asset = []; /** * @param Engine $app */ public function __construct(Engine $app) { $this->app = $app; $this->db = $app->db(); $this->web_app = $app->session()->get('WEB_APP') ?? []; $this->web_user = $app->session()->get('WEB_USER') ?? []; $this->is_login = (count($this->web_user) > 0); $rootdir = (defined('PROJECT_ROOT')) ? PROJECT_ROOT : $app->get('app.project_root'); $this->cachedir = $rootdir . '/temp/cache/'; $this->localedir = $rootdir . '/app/Scripts/locale/'; $this->jsapp = $rootdir . '/app/Scripts/'; $this->cssdir = realpath(__DIR__ . '/../../assets/css') . '/'; $this->jsdir = realpath(__DIR__ . '/../../assets/js') . '/'; $this->rootdir = $rootdir . '/'; if (!is_dir($this->cachedir)) mkdir($this->cachedir, 0755, true); if (!file_exists($this->cachedir . "cache.ini")) { $this->ini = [ 'login' => [ 'css' => "", 'js' => "", 'app' => "", ], 'index' => [ 'css' => "", 'js' => "", 'app' => "", ], 'display' => [ 'css' => "", 'js' => "", 'app' => "", ], ]; $this->save_ini(); } else $this->ini = parse_ini_file($this->cachedir . "cache.ini", true); } public function set_dev(bool $is_dev = false): void { $this->IS_DEV = $is_dev; } public function set_asset(array $asset = []): void { $this->asset = $asset; } private function gen_file(string $mode = "index", string $type = "css"): array { $itime = 0; $afile = []; if ($type === "css") { $_afile = array_unique($this->asset['css'][$mode] ?? []); foreach ($_afile as $__key => $val) { if (!file_exists($this->cssdir . $val)) continue; $_file = new SplFileInfo($this->cssdir . $val); $afile[] = $_file->getRealPath(); $itime += $_file->getMTime(); } if (is_dir($this->rootdir . 'app/Styles/')) { $iterator = new FilesystemIterator($this->rootdir . 'app/Styles/', FilesystemIterator::SKIP_DOTS); foreach ($iterator as $_file) { if ($_file->isFile()) { $afile[] = $_file->getRealPath(); $itime += $_file->getMTime(); } } } } else if ($type === "js") { $_afile = array_unique($this->asset['js'][$mode] ?? []); foreach ($_afile as $__key => $val) { if (!file_exists($this->jsdir . $val)) continue; $_file = new SplFileInfo($this->jsdir . $val); $afile[] = $_file->getRealPath(); $itime += $_file->getMTime(); } // add locale file $ljs = ($this->web_user['LOCALE'] ?? $this->web_app['DEF_LOCALE']).'.js'; if (is_dir($this->localedir)) { $iterator = new \FilesystemIterator($this->localedir, \FilesystemIterator::SKIP_DOTS); foreach ($iterator as $_file) { if ($_file->isFile() && str_ends_with($_file->getFilename(), $ljs)) { $afile[] = $_file->getRealPath(); $itime += $_file->getMTime(); } } } } else if ($type === "app") { if (is_dir($this->jsapp)) { if (file_exists($this->jsapp . $mode . '.js')) { $_file = new SplFileInfo($this->jsapp . $mode . '.js'); $afile[] = $_file->getRealPath(); $itime += $_file->getMTime(); } if ($mode === 'index') { $_ajs = ['system/profile', 'system/chpasswd']; $amenu = array_values($this->web_user['Merge_Menu'] ?? []); $_ops = $this->db->fetchColumn("SELECT Link FROM app_menu WHERE Kode IN (?) AND Link<>'' ORDER BY Kode", [$amenu]); if ($_ops) $_ajs = array_merge($_ajs, $_ops); $dirIterator = new \RecursiveDirectoryIterator($this->jsapp, \RecursiveDirectoryIterator::SKIP_DOTS); $dirFilter = new \RecursiveCallbackFilterIterator($dirIterator, fn($curr, $key, $iter) => !($curr->isDir() && in_array($curr->getFilename(), ['locale']))); $iterator = new \RecursiveIteratorIterator($dirFilter); foreach ($iterator as $_file) { if ($_file->isFile() && !in_array($_file->getFilename(), ['login.js','index.js','display.js']) && array_any($_ajs, fn($js) => str_ends_with(str_replace(['_','\\'],['','/'],$_file->getRealPath()), $js.'.js'))) { $afile[] = $_file->getRealPath(); $itime += $_file->getMTime(); } } } } } return [ $afile, $itime ]; } private function create_cache(string $type = "css", array $afile = [], string $cachefile = ""): void { $contents = ''; $smini = null; $bpublic = ($this->app->request()->ip === '127.0.0.1') ? false : true; if ($bpublic === true) { if ($type === "css") { $smini = new Minify\CSS; } else { $smini = new Minify\JS; } } foreach ($afile as $nfile) { if (file_exists($nfile)) { if ($bpublic === true) { $smini->add($nfile); } else { $contents .= "\n". file_get_contents($nfile); } } } if ($bpublic) $contents = $smini->minify(); file_put_contents($this->cachedir . $cachefile, $contents); } private function save_ini(): void { $content = ""; unset ($this->ini['index']['app']); foreach ($this->ini as $key => $elem) { $content .= ($content === ""?"" : "\n"). "[".$key."]\n"; foreach ($elem as $key2 => $elem2) { if ($key) if ($elem2 === "") $content .= $key2." = \n"; else $content .= $key2." = \"".$elem2."\"\n"; } } file_put_contents($this->cachedir . "cache.ini", $content); } public function Cache(string $mode = "index"): array { if ($mode === 'index') { $this->ini['index']['app'] = (string) $this->db->fetchField("SELECT JS FROM app_operator_profile WHERE UserID=?", [$this->web_user['UserID']]) ?? ''; $this->web_user['Merge_Menu'] = array_filter(array_unique(explode(",", $this->web_user['Menu_Grup'] .",". $this->web_user['Menu']))); } $is_modif = false; foreach (['css','js','app'] as $type) { $is_index_app = ($mode === 'index' && $type === 'app'); if (empty($this->ini[$mode][$type]) || !file_exists($this->cachedir . $this->ini[$mode][$type]) || ($this->IS_DEV)) { [ $afile, $lastmod ] = $this->gen_file($mode, $type); $cachefile = md5($mode . '-' . $type . '-' . $lastmod) . ($type === 'css' ? '.css' : '.js'); if ($is_index_app) $cachefile = md5('index-app-' . (string) $this->web_user['UserID'] . $lastmod) . '.js'; $this->create_cache($type, $afile, $cachefile); $this->ini[$mode][$type] = $cachefile; if ($is_index_app) $this->db->runQuery("INSERT INTO app_operator_profile (UserID,JS) VALUES (?, ?) ON DUPLICATE KEY UPDATE JS=VALUES(JS)", [$this->web_user['UserID'], $cachefile]); $is_modif = true; } } $acache = [ 'CSS' => [], 'JS' => [], 'JSX' => [] ]; foreach ($this->ini[$mode] as $_type => $_file) { if (file_exists($this->cachedir . $_file)) { if ($_type === 'css') { $acache['CSS'][] = 'cache/' . $_file; } else $acache['JS'][] = 'cache/' . $_file; } } if ($is_modif === true) $this->save_ini(); $nonce = $this->app->get('csp_nonce'); $_secret = $this->web_app['APP_CODE'] .";". $this->web_app['APP_SID'] .";". $this->app->request()->getVar('HTTP_HOST') .";". "http://".$this->app->request()->getVar('HTTP_HOST') .";". isMobile() .";". ($this->web_app['AUTH_LEVEL']?$this->web_app['AUTH_LEVEL']:0); $_suser = ($this->is_login) ? $this->web_user['Username'] .";". $this->web_user['UserID'] .";". $this->web_user['Grup_MenuID'] .";". $this->web_user['Level'] : "nobody;999;999;9"; $acache['JSX'][] = ''; return $acache; } public function Make_Cache(array $amode = ['login', 'index', 'display']): array { $is_modif = false; foreach ($amode as $mode) { foreach (['css','js','app'] as $type) { if ($mode === 'index' && $type === 'app') continue; if (empty($this->ini[$mode][$type]) || !file_exists($this->cachedir . $this->ini[$mode][$type])) { [ $afile, $lastmod ] = $this->gen_file($mode, $type); $cachefile = md5($mode . '-' . $type . '-' . $lastmod) . ($type === 'css' ? '.css' : '.js'); $this->create_cache($type, $afile, $cachefile); $this->ini[$mode][$type] = $cachefile; $is_modif = true; } } } if ($is_modif === true) $this->save_ini(); if (in_array('index', $amode)) { $agrup = $this->db->fetchAll('SELECT Grup_MenuID,DECODE(Menu,?) Menu FROM app_menu_grup', [$this->web_app['PARENT_CODE']]); $auser = $this->db->fetchAll("SELECT A.*,IFNULL(B.JS,'') JS FROM (SELECT UserID,Grup_MenuID,DECODE(Menu,?) Menu FROM app_operator) A LEFT JOIN app_operator_profile B ON (A.UserID=B.UserID)", [$this->web_app['PARENT_CODE']]); foreach ($auser as $user) { if (empty($user['JS']) || !file_exists($this->cachedir . $user['JS'])) { $this->web_user['Merge_Menu'] = array_filter(array_unique(explode(",", $user['Menu'] .",". ($agrup[$user['Grup_MenuID']]['Menu'] ?? '')))); [ $afile, $lastmod ] = $this->gen_file('index', 'app'); $cachefile = md5('index-app-' . (string) $user['UserID'] . $lastmod) . '.js'; $this->create_cache('app', $afile, $cachefile); $this->db->runQuery("INSERT INTO app_operator_profile (UserID,JS) VALUES (?, ?) ON DUPLICATE KEY UPDATE JS=VALUES(JS)", [$user['UserID'], $cachefile]); } } } return [ 'status' => 1, 'user_count' => count($auser) ]; } } ?>