KStats/PHP/KStats.php

303 lines
12 KiB
PHP
Raw Permalink Normal View History

<?php
session_start();
class KStats{
private static $url = null;
private static $default_settings = [
"engine" => "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();
}
};