274 lines
12 KiB
PHP
274 lines
12 KiB
PHP
<?php
|
|
|
|
/*******************************************************************
|
|
|
|
Module : /Data/Cache.php
|
|
Created By : Siau Pheng ([email protected]).
|
|
Created Date : March 16th, 2008.
|
|
Last Modified : September 3rd, 2026.
|
|
|
|
(c) 3FONIA Software; WWW.3FONIA.COM.
|
|
|
|
*******************************************************************/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Webapp\Data;
|
|
|
|
use \flight\Engine;
|
|
use \flight\Session;
|
|
use \flight\database\SimplePdo;
|
|
use \FilesystemIterator;
|
|
use \MatthiasMullie\Minify;
|
|
use \SplFileInfo;
|
|
|
|
/**
|
|
* Class Cache
|
|
*
|
|
* Membuat Cache gabungan CSS dan JS sesuai dengan session.
|
|
*
|
|
* @license MIT, https://www.3fonia.com
|
|
* @copyright Copyright (c) 2008-2026, Siau Pheng <[email protected]>
|
|
*
|
|
* @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'][] = '<script nonce="'.$nonce.'" type="text/javascript">';
|
|
$acache['JSX'][] = 'FoniaJS.setConfig("'.base64_encode($_secret).'", "'.base64_encode($_suser).'");';
|
|
$acache['JSX'][] = '$(document).ready(function(){';
|
|
if ($this->is_login) $acache['JSX'][] = 'FoniaJS.loadMenu();';
|
|
$acache['JSX'][] = '});';
|
|
$acache['JSX'][] = '</script>';
|
|
|
|
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) ];
|
|
}
|
|
|
|
}
|
|
|
|
?>
|