"mysql", "database" => "KStats", "host" => "localhost", "port" => 3306, "charset" => "utf8", "user" => "root", "password" => "", "connection_string" => "{engine}:dbname={database};host={host};port={port};charset={charset}", "allow_all" => false, "data_key" => "kstats_data", "ip_keys_ordered" => ["HTTP_CF_CONNECTING_IP", "HTTP_X_FORWARDED_FOR", "HTTP_X_REAL_IP", "HTTP_CLIENT_IP", "REMOTE_ADDR"] ]; private $input = []; private $connection = null; private $token = null; private $type = null; private $method = null; private $mode = null; private $id = null; private $query = null; private $session = null; private $request_data = null; public static function is_dictionary($value){ return is_array($value) && array_values($value) != $value; } public static function is_secure(){ return (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on") || $_SERVER["SERVER_PORT"] == 443; } private static function get_url(){ return self::$url ? self::$url : (self::$url = ( isset($_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : "http" . (self::is_secure() ? "s" : "") . "://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"] )); } public function default_value($default = null, $nulls = null){ return $default !== null || (is_bool($nulls) ? $nulls : $this->settings("nulls", null, false, false)) ? $default : $this->settings(["default_value", "default"], null, null, true); } private function settings($names = null, $inputs = null, $default = null, $nulls = null){ if(!$names) return $this->default_value($default, $nulls); !is_bool($nulls) && ($nulls = $this->settings("nulls", null, false, false)); !is_array($names) && ($names = [$names]); foreach(array_merge(is_array($inputs) ? (self::is_dictionary($inputs) ? [$input] : $input) : [], [$this->input, self::$default_settings]) as $input) if(self::is_dictionary($input)) foreach($names as $name) if($name && isset($input[$name]) && ($nulls || $input[$name] !== null)) return $input[$name]; return $this->default_value($default, $nulls); } public function get_ip(){ foreach($this->settings("ip_keys_ordered") as $key){ if(!empty($_SERVER[$key])) return explode(",", $_SERVER[$key])[0]; if($ips = getenv($key)) return explode(",", $ips)[0]; }; return null; } private function get_token(){ return preg_replace('/^\/api\/([^\/]+)(\/.*)?$/', "$1", $_SERVER["REQUEST_URI"]); } public static function string_variables($string, $variables = null, $default = null){ if(!is_array($variables)) $variables = []; elseif(self::is_dictionary($variables)) $variables = [$variables]; return preg_replace_callback('/\{([^\{\}]+)\}/', function($values) use($variables){ foreach($variables as $set) if(isset($set[$values[1]])) return $set[$values[1]]; return $values[0]; }, $string); } private function query($query, $variables){ $used = []; $results = [ "tables" => [], "variables" => [] ]; if(!is_array($variables)) $variables = []; elseif(self::is_dictionary($variables)) $variables = [$variables]; preg_replace_callback('/\@([a-zA-Z0-9_]+)/', function($values) use(&$used){ !in_array($values[1], $used) && ($used[] = $values[1]); }, is_array($variables) ? ($query = preg_replace_callback('/\{([^\{\}]+)\}/', function($values) use($variables){ foreach($variables as $set) if(isset($set[$values[1]])) return ( $set[$values[1]] === null ? "null" : ( is_bool($set[$values[1]]) ? ($set[$values[1]] ? "true" : "false") : ( is_string($set[$values[1]]) ? "'" . preg_replace('/([\\\\\'])/', "\\\\$1", $set[$values[1]]) . "'" : ( $set[$values[1]] )))); return "null"; }, $query)) : $query); if(!empty($used)){ $subquery = ""; foreach($used as $key) $subquery .= ($subquery ? "," : "") . " @" . $key . " as '" . $key . "'"; $query .= (preg_match('/;$/', $query) ? "" : ";") . "select" . $subquery . ";"; } if(!$this->connection){ $this->connection = new \PDO(self::string_variables($this->settings(["connection_string", "string_connection"]), [ "engine" => $this->settings(["connection_engine", "engine"]), "database" => $this->settings(["connection_database", "database"]), "host" => $this->settings(["connection_host", "host"]), "port" => $this->settings(["connection_port", "port"]), "charset" => $this->settings(["connection_charset", "charset"]) ]), $this->settings(["connection_user", "user"]), $this->settings(["connection_password", "password"])); $this->connection->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->connection->beginTransaction(); }; $this->query = $query; ($statement = $this->connection->prepare($query))->execute(); do{ try{ $table = []; foreach($statement->rowCount() == 0 ? [] : $statement->fetchAll(\PDO::FETCH_ASSOC) as $new_row){ $row = []; foreach($new_row as $key => $value){ if($value && is_string($value) && in_array($value[0], ["[", "{"])){ try{ $row[$key] = json_decode(utf8_encode($value), true); }catch(\Exception $exception){} !$row[$key] && ($row[$key] = utf8_encode($value)); }else $row[$key] = $value; }; $table[] = $row; }; $results["tables"][] = $table; }catch(\Exception $exception){}; }while($statement->nextRowset()); if(preg_match('/(,\s+?|\()\@/', $query)){ $l = count($results["tables"]) - 1; foreach($results["tables"][$l][0] as $key => $value) $results["variables"][$key] = $value; unset($results["tables"][$l]); }; $this->connection->commit(); return $results; } private function save(){ $results = null; if(!in_array($this->type, ["js", "ecma"]) || $this->settings("allow_all")){ // isset($_SERVER["HTTP_REFERER"]) && $results = $this->query("call register({session}, {from}, {token}, {ip}, {url}, @error, @current_session, @id);", [ "session" => $this->session ? $this->session : null, "from" => $this->id ? $this->id : null, "token" => $this->get_token(), "ip" => $this->get_ip(), "url" => $this->request_data && isset($this->request_data["url"]) ? $this->request_data["url"] : self::get_url() ]); $results && ($_SESSION["kstats_id"] = $results["variables"]["current_session"]); }; return $results; } private function print($data){ switch($this->type){ case "js": header("content-type: text/javascript"); echo file_get_contents(__DIR__ . "/../Public/js/KStats.js"); break; case "ecma": header("content-type: text/javascript"); echo file_get_contents(__DIR__ . "/../Public/ecma/KStats.ecma.js"); break; case "image": case "img": header("content-type: image/png"); echo file_get_contents(__DIR__ . "/../Public/images/min.png"); break; case "css": header("content-type: text/css"); echo "kstats-tag-link::after{content : '" . $data . "';}"; break; case "json": header("content-type: application/json"); echo json_encode([ "ok" => true, "code" => 200, "data" => $data ]); break; case "test": header("content-type: text/javascript"); echo "console.log(" . json_encode([ "session" => $_SESSION ]) . ");console.log(document.cookie.split(';'));"; break; default: header("content-type: text/plain"); echo ""; break; }; exit(0); } public function __construct($input = null){ is_array($input) && ($this->input = $input); if(preg_match('/^\/api\/([^\/]+)\/([^\/]+)\/([^\/]+)\/([^\/\?]+)(\/([^\?]+))?/', $_SERVER["REQUEST_URI"], $matches)){ $data_key = $this->settings("data_key"); $this->token = $matches[1]; $this->session = intval($matches[2]); $this->type = $matches[3]; foreach([$_POST, $_GET, $_COOKIE] as $set) if(isset($set[$data_key])){ $this->request_data = json_decode(base64_decode(urldecode($set[$data_key])), true); break; }; // print_r(["request_data", $this->request_data, "get" => $_GET]); switch($this->method = $matches[4]){ case "set": isset($matches[6]) && $matches[6] && ($this->id = intval($matches[6])); $response = $this->save(); $this->print(array_merge(is_array($response) ? $response : [], [ "url" => self::get_url(), "query" => $this->query ])); }; }; $this->print(false); } public function close(){ if($this->connection){ try{ $this->connection->commit(); }catch(\Exception $exception){ try{ $this->connection->rollback(); }catch(\Exception $subexception){}; }; }; $this->connection = null; } public function __destruct(){ $this->close(); } };