<?php

/*
 * @name        JMY CMS
 * @link        https://jmy.su/
 * @copyright   Copyright (C) 2012-2025 JMY LTD
 * @license     LICENSE.txt (see attached file)
 * @version     VERSION.txt (see attached file)
 * @author      Komarov Ivan
 * @edited      Orlov Alexsey
 */

if (!defined('ACCESS')) {
    header('Location: /');
    exit;
}

class dateBase extends controlDB
{
    var $prefix = '';
    private $dir = 'tmp/mysql';
    private $index_file = 'index';
    private $index = array('drop' => array());
    private $cached = array();
    private $pointer = array();
    private $hashDrop = array();
    private $flush_by = array('INSERT', 'UPDATE', 'DELETE', 'REPLACE', 'ALTER');
    private $real_time_tables = array('online', 'comments', 'plugins', 'logs', 'reffers');
    private $real_time_func = array('NOW', 'UNIX_TIMESTAMP');
    private $no_cache_timeout = 300;

    public function __construct()
    {
        global $config;
        require_once ROOT . 'etc/db.config.php';
        parent::connect($dbhost, $dbuser, $dbpass, $dbname);
        define("DB_PREFIX", $prefix);
        define("USER_PREFIX", $user_prefix);
        define("USER_DB", $user_db);
        define("CHARSET", $config['charset']);
        if (!is_dir(ROOT . 'tmp/mysql/')) {
            mkdir(ROOT . 'tmp/mysql/', 0777);
            @chmod_R(ROOT . 'tmp/mysql/', 0777);
        }
        $this->dir = ROOT . 'tmp/mysql/';
        if (file_exists($this->dir . $this->index_file)) include_once $this->dir . $this->index_file;
        unset($dbhost, $dbuser, $dbpass, $prefix, $user_prefix, $user_db, $config['charset']);
    }

    function __destruct()
    {
        if (sizeof($this->index['drop']) > 0) {
            $this->index['drop'] = array_unique($this->index['drop']);
            foreach ($this->index['drop'] as $hash) {
                unset($this->index['lifetime'][$hash]);
                if (file_exists($this->dir . $hash)) @unlink($this->dir . $hash);
            }
            $this->index['drop'] = array();
        }
        $index = '<?php $this->index=' . $this->arr2str($this->index) . '; ?>';
        $this->write_file($this->index_file, $index);
    }

    function arr2str(&$arr, $depth = 0)
    {
        $ret = array();
        if (is_array($arr) && sizeof($arr) > 0) {
            foreach ($arr as $key => $value) {
                $key = str_replace("'", "\\'", $key);
                if (is_array($value)) {
                    $ret[] = "'{$key}'=>" . $this->arr2str($value, $depth + 1);
                } elseif (is_int($value)) {
                    $ret[] = "'{$key}'=>{$value}";
                } else {
                    if (is_string($value)) {
                        $value = str_replace("'", '"', $value);
                    }
                    $ret[] = "'{$key}'=>'" . strval($value) . "'";
                }
            }
        }
        return 'array(' . implode(',', $ret) . ')';
    }

    private function write_file($filename, &$content)
    {
        ignore_user_abort(1);
        $lockfile = $this->dir . $filename . '.lock';
        if (file_exists($lockfile) && time() - filemtime($lockfile) > 5) {
            @unlink($lockfile);
        }
        $lock_ex = @fopen($lockfile, 'x');
        for ($i = 0; $lock_ex === false && $i < 20; $i++) {
            clearstatcache();
            usleep(rand(5, 15));
            $lock_ex = @fopen($lockfile, 'x');
        }
        $success = false;
        if ($lock_ex !== false) {
            $fp = @fopen($this->dir . $filename, 'wb');
            if (@fwrite($fp, $content)) {
                $success = true;
            }
            @fclose($fp);
            fclose($lock_ex);
            @unlink($lockfile);
        }
        ignore_user_abort(0);
        return $success;
    }

    function query($query)
    {
        global $core;
        $hash = md5(preg_replace('~([0-9]{10})~', '', $query));
        $is_realtime = preg_match('~([0-9]{10})~', $query);
        $is_cached = false;
        $do = trim(strtoupper(mb_substr($query, 0, strpos($query, ' '))));
        if (!isset($this->cached[$hash])) {
            if ($do == 'SELECT' or $do == 'DO') {
                preg_match_all('~' . $this->prefix . '_([^\\s`]+)~i', $query, $match);
                $tables =& $match[1];
                unset($match);
                if (sizeof($tables) > 0 && sizeof(array_intersect($tables, $this->real_time_tables)) == 0) {
                    if (isset($this->index['links']) or in_array($hash, $this->index['drop']) or !file_exists($this->dir . $hash)) {
                        foreach ($tables as $table) {
                            $this->index['links'][$table][$hash] = array_diff($tables, array($table));
                        }
                        $this->index['times'][$hash] = time();
                    } else {
                        $modified =& $this->index['times'][$hash];
                        if ($is_realtime or preg_match('~(' . implode('|', $this->real_time_func) . ')~i', $query)) {
                            if ($this->no_cache_timeout && time() - $modified < $this->no_cache_timeout) {
                                $is_cached = true;
                            } else {
                                $this->index['times'][$hash] = time();
                            }
                        } else {
                            $is_cached = true;
                        }
                    }
                    if ($is_cached) {
                        $this->readcache($hash);
                    } else {
                        $mysql = parent::doQuery($query, false, $this->prefix);
                        if ($mysql) {
                            while ($row = parent::getRow($mysql)) {
                                $this->cached[$hash]['data'][] = $row;
                            }
                            $this->cached[$hash]['sizeof'] = parent::numRows($mysql);
                            if (!$this->cached[$hash]['sizeof']) {
                                $this->cached[$hash]['sizeof'] = 0;
                            }
                            $this->pointer[$hash] = 0;
                            $this->iQuery_id = $hash;
                            $this->write_cache($hash);
                        }
                    }
                }
                return parent::doQuery($query, false, $this->prefix);
            }
            if (in_array($do, $this->flush_by)) {
                if (preg_match('~' . $this->prefix . '_([^\\s`]+)~i', $query, $match)) {
                    $table =& $match[1];
                    unset($match);
                    if (isset($this->index['links'][$table])) {
                        foreach ($this->index['links'][$table] as $h => $link) {
                            if (!isset($this->hashDrop[$h])) {
                                foreach ($link as $tbl) {
                                    unset($this->index['links'][$tbl][$h]);
                                }
                                $this->index['drop'][] = $h;
                                $this->hashDrop[$h] = true;
                            }
                        }
                    }
                }
            }
        } else {
            return parent::doQuery($query, false, $this->prefix);
        }
        if (isset($core->auth->isAdmin) && $core->auth->isAdmin) $hash = parent::doQuery($query, false, $this->prefix);
        return $hash;
    }

    private function readcache($hash)
    {
        include_once $this->dir . $hash;
        $this->pointer[$hash] = 0;
        $this->iQuery_id = $hash;
    }

    private function write_cache($hash)
    {
        $data = "<?php \$this->cached['{$hash}']=" . $this->arr2str($this->cached[$hash]) . '; ?>';
        $this->write_file($hash, $data);
    }

    function freeResult($resource)
    {
        return parent::freeResult($resource);
    }

    function &getRow($query_id)
    {
        $ret = false;
        if (!empty($query_id) && is_string($query_id) && isset($this->cached[$query_id])) {
            if ($this->pointer[$query_id] < $this->cached[$query_id]['sizeof']) {
                $ret = $this->cached[$query_id]['data'][$this->pointer[$query_id]];
                $this->pointer[$query_id]++;
            }
        } else {
            $ret = parent::getRow($query_id);
        }
        return $ret;
    }

    function fetchRow($query_id)
    {
        $ret =& $this->fetch_assoc($query_id);
        return $ret ? array_values($ret) : $ret;
    }

    function &fetch_assoc($query_id)
    {
        $ret = false;
        if (!empty($query_id) && is_string($query_id) && isset($this->cached[$query_id])) {
            if ($this->pointer[$query_id] < $this->cached[$query_id]['sizeof']) {
                $ret = $this->cached[$query_id]['data'][$this->pointer[$query_id]];
                $this->pointer[$query_id]++;
            }
        } else {
            $ret = parent::getRow($query_id);
        }
        return $ret;
    }

    function numRows($query_id)
    {
        $ret = false;
        if (!empty($query_id) && is_string($query_id) && isset($this->cached[$query_id])) {
            $ret = $this->cached[$query_id]['sizeof'];
        } else {
            $ret = parent::numRows($query_id);
        }
        return $ret;
    }
}

$db = new dateBase();