<?php
/**
 *   ################################################################################
 *   ##############################  Our PHP Library   ##############################
 *   ##### Here we collect frequently used methods across our PHP applications. #####
 *   ################################################################################
 *
 *   ### Example usage: ###
 *         $helpers = new \Puvox\php_library();
 *                      // everyday methods
 *                 ->random_string();
 *                 ->random_number();
 *                 ->get_visitor_ip();
 *                 ->redirect('https://example.com');
 *                 ->stringify(['key'=>'value']);
 *                      // various methods accepting objects in addition to array 
 *                 ->array_merge($arg1, $arg2);
 *                 ->child_value($obj_or_array, $key, $default_value);
 *                 		// various simplified file operations
 *                 ->file->read('/path/to/file.ext');
 *                 ->file->write('/path/to/file.ext', 'content...');
 *                 ->file->delete_directory('/path/to/dir');
 *                 		// caching (using simple local file caching)
 *                 ->cache->file->set('myKey', 'myValue');
 *                 ->cache->file->get('myKey');
 *                 		// caching (using memcache)
 *                 ->cache->memcache->set('myKey', 'myValue'); 
 *                 ->cache->memcache->get('myKey');
 *                    ...
 *
 * 	     // Note, if you use this library file in official plugins (hosted on services like wp.org or etc), remove all codes between regions titled "AUTOGENERATED_METHODS" in this file, because those codes is not accepted by revision system. 
*/


namespace Puvox;

if (!class_exists('\\Puvox\\library')) {
	
#[\AllowDynamicProperties]
class library
{
	public function constant($name) { 
		return defined($name) ? constant($name) : null;
	}

	public function property($propertyName) {
		return property_exists($this, $propertyName) ? $this->{$propertyName} : null;
	}

	#region debug
	public function dump($obj){
		$content = $this->stringify($obj); //print_r($obj, true);
		$out = '<pre>'; 
		$out .= htmlentities( $this->br2nl( $content )) ;  
		try{ 	
			$trace = debug_backtrace();
			if ( isset($trace[1]) )
				$out .= ($this->is_cli() ? ' [' : '<span style="font-size:0.6em; margin:1px; padding:1px; background:pink;">') .$this->array_value( $trace[1],'file','').':'.$this->array_value( $trace[1],'line',''). ($this->is_cli() ? '] ':'</span>'); 
		}
		catch(\Exception $e){} 
		$out .= '</pre>'; 
		exit ($out);
	} 

	public function var_dump($obj){
		ini_set("xdebug.var_display_max_children", '-1');
		ini_set("xdebug.var_display_max_data", '10000');
		ini_set("xdebug.var_display_max_depth", '-1');
		echo '<pre>';
		var_dump($obj);
		echo '</pre>';
	}

	public function ExceptionMessage($ex, $extended=true){ 	return "Exception Message: {$ex->getMessage()} \r\n[{$ex->getTraceAsString()}] \r\n";	}  //[{$ex->getFile()}::{$ex->getLine()}]

	public function stringify($data, $pretty=false) { 
		if( $this->is_simple_type($data) ) {
			if ($pretty && $this->is_json($array_or_txt)) {
				return json_encode(json_decode($array_or_txt), JSON_PRETTY_PRINT);
			}
			return (!is_bool($data) ? $data : ($data? 'true':'false'));
		}
		else{
			if (is_a($data, 'Exception')){  
				return print_r($data, true);  //exception (https://pastebin_com/P73cgSkq) are only handled well by this
			}
			else{
				return ( $pretty ? json_encode($data, JSON_PRETTY_PRINT) : json_encode($data));  
			}
		}
	}
	public static function uniqueId($args, $addition=''){ return md5(json_encode($args)."_$addition"); }
	#endregion

	public static function sleep($milliseconds){ 
		if ( self::swoole_inside_coroutine() ) 
			\Swoole\Coroutine\System::sleep($milliseconds / 1000); 
		else {
			usleep($milliseconds*1000);
		}
	}

	public function force_redirect_to_https(){
		if(!$this->is_https) { header("Location: https://" . $this->domainReal . $_SERVER["REQUEST_URI"], true, 301); exit; }
	}

	public static function current_url_contains($phrase, $case_sens=true){
		return self::contains($_SERVER['REQUEST_URI'], $phrase, $case_sens);
	}

	public function get_visitor_ip() {
		$proxy_headers = array("CLIENT_IP", "FORWARDED", "FORWARDED_FOR", "FORWARDED_FOR_IP", "HTTP_CLIENT_IP", "HTTP_FORWARDED", "HTTP_FORWARDED_FOR", "HTTP_FORWARDED_FOR_IP", "HTTP_PC_REMOTE_ADDR", "HTTP_PROXY_CONNECTION", "HTTP_VIA", "HTTP_X_FORWARDED", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_FOR_IP", "HTTP_X_IMFORWARDS", "HTTP_XROXY_CONNECTION", "VIA", "X_FORWARDED", "X_FORWARDED_FOR");
		foreach($proxy_headers as $proxy_header) {
			if (isset($_SERVER[$proxy_header])) {
				if(preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $_SERVER[$proxy_header])) {
					return $_SERVER[$proxy_header];
				}
				else if (stristr(",", $_SERVER[$proxy_header]) !== FALSE) {
					$proxy_header_temp = trim(array_shift(explode(",", $_SERVER[$proxy_header])));
					if (($pos_temp = stripos($proxy_header_temp, ":")) !== FALSE) {$proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp); }
					if (preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $proxy_header_temp)) { return $proxy_header_temp; }
				}
			}
		}
		return ( !empty($_SERVER["REMOTE_ADDR"]) ? $_SERVER["REMOTE_ADDR"] : '__UNDEFINED_REMOTE_ADDR__');
	}

	public function mail_scrambler($email) {
		return str_replace('@', '&#64;', $email);
	}

	public function expire_headers()
	{
		ini_set('session.cookie_httponly', 1);		
			//always display as new
		header("Cache-Control: no-cache, must-revalidate, max-age=0");
			//expired in past
		header("Expires: ".			date	('D, d M Y H:i:s', time() - 86400 *2) . " GMT");
		header("Vary: Accept-Encoding");
		header("Last-Modified: ".	gmdate	("D, d M Y H:i:s", time() - 86400 *2) . " GMT"); 
	}

	public function change_max_upload_post()
	{
		if (property_exists($this,'upload_max_limit')) 
		{
			$this->upload_max_limit = max($this->upload_max_limit, ini_get('post_max_size'));
			ini_set('post_max_size', $this->upload_max_limit.'M'); ini_set('upload_max_filesize', upload_max_limit.'M');   ini_set('upload_max_size', upload_max_limit.'M');
		}		
	}


	#region url/path helpers
	public function br2nl($content) { return preg_replace('/\<br(\s*)?\/?\>/i', "\n", $content); }

	public function strip_tags_and_attrs($html_str, $tags=null){
		$xml = new DOMDocument();
		libxml_use_internal_errors(true);
		$allowed_tags = is_null($tags) ? ["html", "body", "b", "br", "em", "hr", "i", "li", "ol", "p", "s", "span", "table", "tr", "td", "u", "ul"] :'';
		$allowed_attrs = ["class", "id", "style"];
		if (!strlen($html_str)){return false;}
		if ($xml->loadHTML($html_str, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD)){
			foreach ($xml->getElementsByTagName("*") as $tag){
				if (!in_array($tag->tagName, $allowed_tags)){
					$tag->parentNode->removeChild($tag);
				}else{
					foreach ($tag->attributes as $attr){
						if (!in_array($attr->nodeName, $allowed_attrs)){
							$tag->removeAttribute($attr->nodeName);
						}
					}
				}
			}
		}
		return $xml->saveHTML();
	}

	// url things
	public function add_http_www($url){
		if(strpos($url, '://') !== false){
			return preg_replace('/^(http(s|)|):\/\/(www.|)/i', 'https://www.', $url);
		}
		else{
			return 'https://www.'.$url;
		}
	}
	public function get_domain($url=null){
		if (is_null($url))
			$url = $this->currentURL;
		return preg_replace('/http(s|):\/\/(www.|)(.*?)(\/.*|$)/i', '$3', $url);
	}
	public function get_domain_from_url($url){
		return $this->get_domain($url);
	}
	public function get_domain_without_http($url=null){
		return $this->remove_http_www($this->get_domain($url));
	}
	public function remove_www($url) 	{ 
		return str_replace( '://www.', '://', $url ); 
	}
	public function remove_http_www($url){
		$url = preg_replace('/(http(s|):\/\/|:\/\/)(www.|)/i', '',  $url);
		$url = $this->remove_if_starts_with($url, 'www.');
		return $url;
	}
	public function remove_domain_from_url($url){
		return $this->remove_domain($url);
	}
	public function remove_domain($url){
		$url = preg_replace('/(http(s|):\/\/|:\/\/)(www.|)(.*?)(\/|$)/i', '/',  $url);
		return $url;
	}

	public static function slash_trailing ($path) {
		return self::slash_untrailing($path) . '/';
	}

	public static function slash_untrailing ($path) {
		return rtrim($path, '/\\' );
	}

	public static function slash_normalize_url($url, $add_trailish_slash=true) {
		if ($add_trailish_slash) {
			$url = self::slash_trailing($url);
		}
		// remove double slashes (except ://)
		$url = preg_replace('/([^:])(\/{2,})/', '$1/', $url);
		// remove accidental backslashes
		$url = str_replace(['\\/', '\\'], '/', $url);
		return $url;
	}

	public static function slash_normalize_path($path, $add_trailish_slash=true) {
		if ($add_trailish_slash) {
			$path = self::slash_trailing($path);
		}
		// remove double slashes
		$path = preg_replace('/(\/{2,}|\\{2,})/', DIRECTORY_SEPARATOR, $path);
		// remove accidental remnant back/forward slashes
		$path = str_replace(['\\/', '\\', '/'], DIRECTORY_SEPARATOR, $path);
		return $path;
	}
 
	public function slash_replace_direction($path_or_url, $direction = 'forward|backward|OS'){
		$chosen_side = $direction === 'forward' ? '/' : ($direction == 'backward' ? '\\' : DIRECTORY_SEPARATOR);
		return $this->slash_normalize_path ( str_replace(['\\','/'], $chosen_side, $path_or_url) );
	}


	public function urlify($path){
		return str_replace( '\\', "/", $path); 
	}
	public function convert_urls_in_text($text) {
		return preg_replace('@([^\"\']https?://([-\w\.]+)+(:\d+)?(/([\w/_\.%-=#][^<]*(\?\S+)?)?)?)@', '<a href="$1">$1</a>', $text);
	}
	public function restricted_directory_requested($url=false, $dieORreturn=true ){ if (!$url) {$url=$_SERVER['REQUEST_URI'];}
		$url =stripslashes($url);
		if ( stristr($url,'\\')  ||   substr($url, 0, 2)=='..' || stristr($url,'../')  ||  stristr($url,'/..')  ||  stristr($url,'?')  ||  stristr($url,'*')  ||  stristr($url,'.php')	){
			if ($dieORreturn) {die("incorrect path requested.. error4292");} 	else{ return true;}
		}
	}
	public function stripslashes_from_strings_only( $value ) {
		return is_string( $value ) ? stripslashes( $value ) : $value;	
	}
	public function stripslashes_deep($value)
	{
		$value = is_array($value) ? array_map('stripslashes_deep', $value) : stripslashes($value);
		return $value;
	}
	
	public function stripslashes_deep2($value){ 
		return $this->array_map_deep([$this,'stripslashes_from_strings_only'] , $value ); 
	}

	// minified version of https://www.php.net/manual/en/function.realpath.php#123783
	public function realpath($path) {
        if(DIRECTORY_SEPARATOR !== '/') {
            $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
        }
        $search = explode('/', $path);
        $search = array_filter($search, function($part) {
            return $part !== '.';
        });
        $append = array();
        $match = false;
        while(count($search) > 0) {
            $match = realpath(implode('/', $search));
            if($match !== false) {
                break;
            }
            array_unshift($append, array_pop($search));
        };
        if($match === false) {
            $match = getcwd();
        }
        if(count($append) > 0) {
            $match .= DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, $append);
        }
        return $match;
    }

	// minified version of https://www.php.net/manual/en/function.realpath.php#124254
	public static function realpath2($path){
        $path = mb_ereg_replace('\\\\|/', DIRECTORY_SEPARATOR, $path, 'msr');
        $startWithSeparator = $path[0] === DIRECTORY_SEPARATOR;
        preg_match('/^[a-z]:/', $path, $matches);
        $startWithLetterDir = isset($matches[0]) ? $matches[0] : false;
        $subPaths = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'mb_strlen');
        $absolutes = [];
        foreach ($subPaths as $subPath) {
            if ('.' === $subPath) { continue;  }
            if ('..' === $subPath && !$startWithSeparator && !$startWithLetterDir && empty(array_filter($absolutes, function ($value) { return !('..' === $value); })) ) { $absolutes[] = $subPath;  continue; }
            if ('..' === $subPath) { array_pop($absolutes); continue; }
            $absolutes[] = $subPath;
        }
        return (($startWithSeparator ? DIRECTORY_SEPARATOR : $startWithLetterDir) ? $startWithLetterDir.DIRECTORY_SEPARATOR : '' ).implode(DIRECTORY_SEPARATOR, $absolutes);
	}
	#endregion

 
	public function string_to_truefalse($string) { return ( $string ==='true' ? true : ($string ==='false' ? false : $string)); }
	public function truefalse_to_string($string) { return ( $string === true ? 'true' : ($string ===false ? 'false' : $string)); }
	public function bool_to_sign($bool_or_string) { return ( $bool_or_string===true ||  $bool_or_string==="true" ? 1 : ( $bool_or_string===false || $bool_or_string==="false" ? -1 : 0) ); }


	#region string manipulations 2
	public function random_string($length = 11) {
		return substr(str_shuffle(str_repeat($x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ceil($length/strlen($x)) )),1, $length);    //random_stringg($length= 15){ return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, $length);}
	} 
	// i.e. 5m, 1H, 2H, 1D, 240M, etc...
	public function stockTF_to_seconds($string, $minuteSymbol="m", $monthSymbol="M"){
		$res=$string;
		$arr=['s'=>1, 'S'=>1, $minuteSymbol=>1*60, 'h'=>60*60, 'H'=>60*60, 'd'=>24*60*60, 'D'=>24*60*60, 'w'=>7*24*60*60, 'W'=>7*24*60*60, $monthSymbol=>31*24*60*60, 'y'=>365*24*60*60, 'Y'=>365*24*60*60];
		foreach ($arr as $key=>$val) { if (empty($key)) continue; if (strpos($string, $key)!==false) { $res = str_ireplace($key, '', $string) * $val; break; }  }
		return $res;
	}
	
	public function str_replace_last($search, $replace, $subject)
	{
		$pos = strrpos($subject, $search);
		if($pos !== false) $subject = substr_replace($subject, $replace, $pos, strlen($search)); 
		return $subject;
	}
	
	public function str_replace_first($from, $to, $content, $type="plain"){
		if($type=="plain"){
			$pos = strpos($content, $from);
			if ($pos !== false) {
				$content = substr_replace($content, $to, $pos, strlen($from));
			}
			return $content;
		}
		elseif($type=="regex"){
			$from = '/'.preg_quote($from, '/').'/';
			return preg_replace($from, $to, $content, 1);
		}
	}
	
	public static function toString($inp){ return self::to_string($inp); }
	public static function to_string($inp){ return $inp.""; }
	
	public function value_to_string( $value ){
		return is_bool($value) ? ($value ? 'true' : 'false' ) : strip_tags(  $value ) ;
	}
	public function string_to_value( $value ){
		return is_bool($value) ? $value : ( !is_string($value) ?  $value : ( $value =='true' ? true : (  $value =='false' ? false : $value) ) );
	}
	public static function contains_numeric($str){
		$str = self::to_string($str);
		for($i=0; $i<=9; $i++) {
			if (strpos($str, self::to_string($i) )!==false){
				return true;
			}
		}
		return false;
	}
	#endregion

	#region date and time
	public function microtime_float()
	{
		list($usec, $sec) = explode(" ", microtime());
		return ((float)$usec + (float)$sec);
	}
	public function microtime()	{ return $this->microtime_float();	}

	//  if ( is_admin() && file_exists($lib_start=__DIR__."/$name") && !defined("_puvox_machine_") ) { rename($lib_start, $lib_final); } require_once($lib_final);
	public static function seconds(){ return time(); }
	public static function milliseconds(){ return round(microtime(true)*1000); }
	public static function time_ms(){ return self::milliseconds(); }
	
	public static function yyyymmdd($time){
		return strtotime(date('Y-m-d', $time));
	}
	public static function current_datetime($time='', $MS=false){
		$format = 'Y-m-d H:i:s'.($MS?'.v':'') ;
		return ( !empty($time) ? gmdate($format, $time) : gmdate($format) );
	}
	public static function time_to_date($time, $ms=000){
		return date("Y-m-d H:i:s.$ms", $time);
	}
	public static function time_to_date_TZ($time){
		$time_rounded = floor($time);
		// if milliseconds
		if ($time_rounded>1000000000000){
			$ms = $time_rounded % 1000;
			$timeS= floor($time_rounded/1000);
			$dat = date("Y-m-d\TH:i:s.$ms\Z", $timeS);
		}
		else{
			$dat = date("Y-m-d\TH:i:s.000\Z", $time_rounded);
		}
		return $dat;
	}
	public function is_weekend($time){
		return date('N',$time) > 5;
	}
	#endregion



	public static function safemode_basedir_set(){
		return ( ini_get('open_basedir') || ini_get('safe_mode') ) ;
	}
	public static function header($type){
		switch ($type){
			case "json" : header('Content-Type: application/json;  charset=utf-8'); break;
			case "text" : header('Content-Type: text/plain;  charset=utf-8'); break;
			case "js"   : header('Content-Type: application/javascript;  charset=utf-8'); break;
		}
	}

	public function file_get_contents($path, $defaultValue=null )
	{
		$path = self::realpath($path);  
		if (!file_exists($path)){
			if (method_exists($this,'localdata_set')){
				$this->localdata_set($path, $defaultValue);
			}
			return $defaultValue;
		} 
		else {
			// is_readable( $path ) will not work here, because of LOCK-waiting
			$wait_for_lock_seconds= 0.2;
			if($fp = fopen($path,'r')) {
				$startTime = microtime(true);
				do{
					$canRead = flock($fp, LOCK_SH);
					if(!$canRead) {
						self::sleep( 0.01 );   // Releases CPU for others
					}
					// If the lock is not acquired and the timeout has not expired, continue to acquire the lock
				} while((!$canRead) && ((microtime(true) - $startTime) < $wait_for_lock_seconds ));
				if($canRead) {
					//file_get_contents($path)
					$contents=file_get_contents($path); // fread($fp, filesize($path));
					flock($fp,LOCK_UN);
					fclose($fp);
				}
				else{
					fclose($fp);
					throw new \Exception("Could not get read-access to file $path");
				}
				return $contents;
			}
			else{
				throw new \Exception("Could not open access to file $path");
			}
		}
	}
	public function file_get_contents_json($path, $defaultValue=null, $isAssoc = true){
		$content = $this->file_get_contents($path, $defaultValue);
		return json_decode($content, $isAssoc);
	}


	public static function exitPlain($content, $encode=false){
		self::header('text');
		if ($encode) $content = json_encode($content);
		print($content); exit;
	}

	public static function exitJson($content){
		self::header('json');
		if (is_array($content)) $content = json_encode($content);
		exit($content);
	}

	public function try_increase_exec_time($seconds, $memory=null){
		if( ! $this-> safemode_basedir_set() ) {
			if(!is_null($memory)) $this->try_increase_memory($memory);
			return ini_set('max_execution_time', $seconds); //stackoverflow.com/questions/8914257
		}
		return false;
	}

	public static function set_memory_limit($new_limit = 'mbs'){
		if( ! self::safemode_basedir_set() ) {
			$limitBytes = $new_limit * 1048576;
			$currentLimit = trim(ini_get('memory_limit'));
			$lastChar = strtolower($currentLimit[strlen((int) $currentLimit)-1]);
			switch($lastChar) {
				case 'g': $currentLimit *= 1024;
				case 'm': $currentLimit *= 1024;
				case 'k': $currentLimit *= 1024;
			}
			if ($currentLimit < $limitBytes)
				return ini_set('memory_limit', $new_limit . 'M');
		}
		return false;
	}

	// find defined bits/flags/enums
	public static function binding_flags($flag)
	{
		$setBits = array();
		for ($i = 1; $i <= 32; $i++) {
			if ($flag & (1 << $i)) {
				$setBits[] = (1 << $i);
			}
		}
	
		// Sort array to order the bits
		sort($setBits);
	
		return $setBits;
	}
	// find if bit/flag/enum is defined
	public static function flag_exists($existing, $target){
		return in_array($target,self::binding_flags($existing));//$target & (1 << $target);
	}

	public function message_stop_malicious_attempt(){
		return 'Not allowed. Try again.';//'Well... I know that these words won\'t change you, but I\'ll do it again: Developers try to create a balance & harmony in internet, and some people like you try to steal things from other people. Even if you can it, please don\'t do that.';
	}

	public function link($link, $text){ return $this->href_url($link, $text);  }
	public function href_url($link, $text){ return '<a href="'.$link.'" target="_blank">'.$text.'</a>'; }
	public function href($link, $text){ return $this->href_url($link, $text); }

    // ##### CUSTOM OPTIONS #####
	public function optsNameX1(){ return 'all_options_'.$this->module_NAMESPACE; }
	public function get_option($name, $defaultValue=null){ 
		if($this->isWP)
		{
			$opts = get_site_option($this->optsNameX1(),[]);
		}
		else{
			$opts = $this->get_option_json($name,$defaultValue); 
		}
		return is_null($name) ? $opts : (array_key_exists($name, $opts) ? $opts[$name] : '');
	}
	public function update_option($name, $value, $autoload=null){  
		$opts = $this->get_option($this->optsNameX1(),[]);
		if( !is_null($name) ){
			$opts[$name]= $value;
		}
		else{
			$opts = $value;
		}
		return ($this->isWP) ? update_site_option($this->optsNameX1(), $opts, $autoload) :  $this->update_option_json($name,$value,$autoload);  
	}

	public function add_my_site_options($array)
	{ 
		$this->extra_options_enabled=true;
		$this->_my_site_options=$this->get_option(null);
		$final=[];
		foreach($array as $key=>$value){
			$final[$key]=array_key_exists($key, $this->_my_site_options) ? $this->_my_site_options[$key] : $value;
		}
		if($this->_my_site_options!=$final) { $this->update_my_site_options($final); }
	}
	public function get_my_site_option($name=null, $default=null, $force_update=false)
	{
		$this->_my_site_options=$this->get_option($name);
		if ($name!=null)
		{
			if (! array_key_exists($name, $this->_my_site_options) || $force_update){
				$this->_my_site_options[$name]=$default;
				$this->update_my_site_options();
			}
			return $this->_my_site_options[$name];
		}
		return $this->_my_site_options;
	}
	public function update_my_site_options($array=null)
	{
		$this->update_option(null,  ( $array ?: $this->_my_site_options) );
	}



	#region ########################### SQLITE ###########################
	//  $db = new \PDO('sqlite:'.$db_path);  $db ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
	public function sqlite_db_init($db_path="/example/my.db"){
		try {
			$db = new \SQLite3($db_path);
		} catch(\Exception $ex) { 
			die('Error: '.$ex->getMessage());
		}
		return $db ;
	} 
	public function sqlite_create_table_PDO($db){
		$db->exec( "CREATE TABLE IF NOT EXISTS myValuesTable (
			id INTEGER PRIMARY KEY, 
			title TEXT, 
			text TEXT, 
			status TEXT, 
			time INTEGER)"
		);
	} 
	public function sqlite_insert_PDO($title, $text, $status, $time) {
        $sql = 'INSERT INTO myValuesTable(title,text,status,time) VALUES(:title,:text,:status,:time)';
        $stmt = $this->pdo->prepare($sql);
        $stmt->execute([
            ':title'	=> $title,
            ':text'		=> $text,
            ':status'	=> $status,
            ':time' 	=> $time,
        ]);
        return $this->pdo->lastInsertId();
    } 
	public function sqlitePDO_update($id, $title, $text, $status, $time) {
        // SQL statement to update status of a task to completed
        $sql = "UPDATE myValuesTable SET "
				. "title  = :title, "
                . "text   = :text, "
                . "status = :status, "
                . "time   = :time "
                . "WHERE id = :id";

        $stmt = $this->pdo->prepare($sql);
        return $stmt->execute([
            ':title'  => $title,
            ':text'   => $text,
            ':status' => $status,
            ':time'   => $time,
            ':id'     => $id,
        ]);
    }

	public function sqlitePDO_fetchAll($db, $tablename) {
		$which = '*'; //'project_id, project_name';//
        $stmt = $db->query('SELECT '.$which .' FROM '.$tablename);
        $res = [];
        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
            $res[] = $row;
        }
        return $res;
    }
	
    public function sqlitePDO_Command($db_name="db_all.db") { 
        $this->pdo	= $this->sqlite_db_init($db_name);
        $statement->execute(); 
        $res = $statement->fetchAll(PDO::FETCH_ASSOC);
    }
	
	public function sqlite_create_table_TRANSLATIONS($db)
	{
		$sql =
		'CREATE TABLE IF NOT EXISTS translations (	
			ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
			program_name		VARCHAR(150),
			string				TEXT 	NOT NULL,
			lang				TEXT 	NOT NULL,
			value				TEXT 	NOT NULL,
			time				INT,
			suggestion			TEXT
		);';
		return $db->query($sql);     //possibles: VARCHAR(50)  ,  PRIMARY KEY (`ID`), UNIQUE KEY `ID` (`ID`) 	) AUTO_INCREMENT=1;'; 
	}	
	
	public function sqlite_insert_TRANSLATION($db, $string, $lang, $value, $time, $program_name, $suggestion) { 
		$statement = $db->prepare('INSERT INTO translations ( string, lang, value, time, program_name, suggestion ) VALUES (:string, :lang, :value, :time, :program_name,  :suggestion );');
		return $statement->execute([':string'=>$string, ':suggestion'=>$suggestion]);
	}
			
	public function sqlite_string_exists($string, $lang=false, $program_name=false) 
	{
		$statement = $this->db->prepare('SELECT * from translations where string= :string'. ( $lang? ' and lang = :lang' : '') .' LIMIT 1' ); // . ( $program_name? ' and program_name = :program_name' : '') 
		$statement->bindValue(':string',	$string);
			if ($lang)			
		$statement->bindValue(':lang',	$lang); 
		$res = $statement->execute();  
		return !empty($res->fetchArray(SQLITE3_ASSOC));
	}
	
	public function sqlite_get()
	{
		$statement = $this->db->prepare('SELECT * from translations where string= :string and lang= :lang ');  //. ( $program_name? ' program_name = :program_name' : '') 
		$statement->bindParam(':string',$string);
		$statement->bindParam(':lang',	$lang);
		$ret = $statement->execute(); 
		$res = $ret->fetchArray(SQLITE3_ASSOC);
		if(!empty($res)){
			$this->found=true;
			$return= $res['value'];
		}
	}
		//$current_day_logname = 'log_'.date('Y-m-d'). ($new_file ? time().$new_file : '') . '.txt'; 
		//$this->helpers->filecreate($current_day_logname,$data, FILE_APPEND); 
	#endregion ########################### END SQLITE ###########################
	
	

	#region DOM PARSER
	public function new_dom_document($content)
	{
		$dom = new \DOMDocument('1.0', 'UTF-8');
		$internalErrors = libxml_use_internal_errors(true);	//disable
		$dom->loadHTML( $content);
		libxml_use_internal_errors($internalErrors);		//restore
		$finder = new \DOMXpath( $dom );
		$nodes= $finder->query( "//*" );
		foreach ($nodes as $node) {
			if ($node->hasAttributes())
			{
				$error = $node->ownerDocument->saveHTML($node); break;
			}
		}
	}
	public function domDocument_load($content)
	{
		if (!property_exists($this,'tempDom')) $this->tempDom = new \DOMDocument('1.0', 'UTF-8');
		$internalErrors = libxml_use_internal_errors(true);	//disable
		$this->tempDom->loadHTML( $content);
		libxml_use_internal_errors($internalErrors);		//restore
		return $this->tempDom;
	}
	public function domDocument_remove($el)
	{
		$el->parentNode->removeChild($el);
	}
	public function domDocument_body($dom)
	{ 
		$body = $dom->getElementsByTagName('body');
		if ( $body && 0<$body->length ) {
			$body = $body->item(0);
			return $dom->savehtml($body);
		}
		return "-1";
	}
	public function domDocument_getElementById($dom, $idName, $showError=false) {
		try{
			return $dom->getElementById($idName)->nodeValue;
		}
		catch(\Exception $ex){
			return ($showError ? "DomError:".$ex->getMessage() : null);
		}
	}
	public function domDocument_getElementsByClassName($dom, $ClassName, $tagName=null) {
		$Elements = $tagName ? $dom->getElementsByTagName($tagName) : $dom->getElementsByTagName("*");
		$Matched = array();
		for($i=0;$i<$Elements->length;$i++) {
			if($Elements->item($i)->attributes->getNamedItem('class')){
				if($Elements->item($i)->attributes->getNamedItem('class')->nodeValue == $ClassName) {
					$Matched[]=$Elements->item($i);
				}
			}
		}
		return $Matched;
	}
	public function domDocument_getElementsByClass_2(&$parentNode, $tagName, $className) {
		$nodes=array();
		$childNodeList = $parentNode->getElementsByTagName($tagName);
		for ($i = 0; $i < $childNodeList->length; $i++) {
			$temp = $childNodeList->item($i);
			if (stripos($temp->getAttribute('class'), $className) !== false) {
				$nodes[]=$temp;
			}
		}

		return $nodes;
	}
	public function get_dom_element_data($html_data, $tag_type='id', $tag_key='')
	{
        $dom = new \DOMDocument('1.0', 'UTF-8');;  
        $dom->loadHTML($html_data, LIBXML_HTML_NOIMPLIED | LIBXML_DTDVALID | LIBXML_NOENT | LIBXML_NOERROR );   // https://www.php.net/manual/en/libxml.constants.php#constant.libxml-html-noimplied
        $dom->preserveWhiteSpace = false; //disact whitespace
        if( $tag_type=='id'){
			$json_obj = $dom->getElementById($tag_key); 
			$data = $json_obj->nodeValue;
		}
        try{
			return json_decode($data);
		}
		catch(\Exception $ex){
			return $data;
		}
	}
	#endregion ======================== domparser ========================



	public function include_dir($dir){ 
		foreach(glob($dir ."/*.php$") as $file) include_once($file);
	}

	public function file_url($file){ 
		return $this->moduleURL."/$file?vers_=".$this->filedate($this->moduleDIR. "/$file");
	}

    public function checked_children($array, $keyname, $post_array=null)
    {
		$ref_Array= !is_null($post_array) ? $post_array : $array;
        foreach ($array as $key=>$name)
        {
            $array[$key][$keyname]= isset($ref_Array[$key][$keyname]);
        }
        return $array;
    }
	// ================================================



	//only open and close the same-origin creator of session  (argument can be TRUE/FALSE or STRING too
	public function session_state ($arg) { 
		if($arg===true)	{	if(session_status() == PHP_SESSION_NONE)	{ $GLOBALS['my_session_pp']='sess'.rand(1,99999999); session_start();  return $GLOBALS['my_session_pp']; }   	}     
		else			{	if(session_status() == PHP_SESSION_ACTIVE)	{ if(!$arg || $arg==$GLOBALS['my_session_pp']) session_write_close();  }   	}  
	}
	public function set_session_var ($name,$value) {
		$id= $this->session_state(true);
		$_SESSION[$name] = $value;
		$this->session_state($id);
	}
	
	public function start_session_if_was_not_started(){
		if(session_status() == PHP_SESSION_NONE)  { $this->session_being_opened = true; session_start();  }
	}
	public function end_session_if_was_started( $method=2){
		if(session_status() != PHP_SESSION_NONE && property_exists($this,"session_being_opened") )  {
			unset($this->session_being_opened);
			if($method==1) session_destroy();
			elseif($method==2) session_write_close();
			elseif($method==3) session_abort();
			elseif($method==4) session_unset();
		}
	}









	// ########################################################### //
	// ###################### ARRAY OPERATIONS ################### //
	// ########################################################### //
	// NOTE: Generaly, creating new variable has much better memory performance, then "unset"

	// ======== manual  ========
	//convert unsorted array (i.e. [ 'first'=>["a","b","c"], 'second'=>[1,2,3] , ] ) to associative   [ "a"=>1, "b"=2 ]
	public function array_to_associative($array) {
		
	}
	public function string_to_array($string, $divider='|', $allow_empty=false){ $res=( array_map('trim', !empty($string)?explode($divider, $string):[] ) ); return $allow_empty? $res : array_filter($res); }
	public function array_to_string($array)	{ return implode(",",  array_map('trim',array_filter($array)) ); }
	public function arrayPhpToJs($array)	{ return '["'. implode('","', (!empty($array[0]) && is_array($array[0]) ? $this->arrayPhpToJs($array) : $array) ) .'"]'; }

	public function object_to_array($data) //faster than json_encode & decode : https://stackoverflow.com/a/4345578/2377343
	{
		if (is_array($data) || is_object($data))
		{
			$result = [];
			foreach ($data as $key => $value)
			{
				$result[$key] = (is_array($data) || is_object($data)) ? $this->object_to_array($value) : $value;
			}
			return $result;
		}
		return $data;
	}
	public static function array_to_object($data) { 
		if (is_object($data))
		{
			foreach ($data as $key=>$value)
			{
				if(is_array($value) || is_object($value))
					$data->$key = self::array_to_object($value);
			}
			return $data;
		}
		else{
			$result = new \stdClass();
			foreach ($data as $key => $value)
			{
				$result->$key = (is_array($value) || is_object($value)) ? self::array_to_object($value) : $value;
			}
			return $result;
		}
	}

	
	public static function array_add_prefix_to_keys($array, $prefix){
		$new_array =[];
		foreach ($array as $k => $v) {
			$new_array[$prefix.$k] = $v;
		}
		return $new_array;
	}
	public static function add_prefix_to_array_keys($array, $prefix){
		return self::array_add_prefix_to_keys($array, $prefix);
	}
	public static function array_add_prefix_to_values($array, $prefix){
		$new_array =[];
		foreach ($array as $k => $v) {
			$new_array[$k] = $prefix.$v;
		}
		return $new_array;
	}

	public static function array_shuffle($list) {
		if (!is_array($list)) return $list;
		$keys = array_keys($list);
		shuffle($keys);
		$random = [];
		foreach ($keys as $key)
			$random[$key] = $list[$key];
		return $random;
	}
	

	public function array_map_recursive( $func, $value) {  //removed callable $func
		if (is_array($func)){
			$array = $value;
			foreach($func as $each_func){
				$array = $this->array_map_recursive($each_func,$array);
			}
			return $array;
		}
		else{
			return filter_var($value, \FILTER_CALLBACK, ['options' => $func]);
		}
	}
	
	public function array_map_deep( $callback , $value) 
	{
		if ( is_array( $value ) ) {
			foreach ( $value as $index => $item ) {
					$value[ $index ] = $this->array_map_deep($callback,  $item );
			}
		} elseif ( is_object( $value ) ) {
			$object_vars = get_object_vars( $value );
			foreach ( $object_vars as $property_name => $property_value ) {
					$value->$property_name = $this->array_map_deep( $callback, $property_value );
			}
		} else {
			$value = call_user_func( $callback, $value );
		}
		return $value;
	}


	public static function count($arr_or_object)
	{
		if(is_object($arr_or_object)) return count(get_object_vars( $arr_or_object ));
		else return count($arr_or_object);
	}
	public static function child_value($array, $key, $default=''){
		if (is_object($array)) {
			return (property_exists($array, $key) ? $array->$key : $default);
		}
		else{
			return (array_key_exists($key, $array) ? $array[$key] : $default);
		}
	}
	public static function array_value_safe($array, $key, $default=''){
		return self::child_value($array ?:[], $key, $default);
	}
	public static function array_value($array, $key, $default=''){
		return self::array_value_safe ($array, $key, $default);
	}
	public function array_value_sub($array, $key1, $key2, $default=''){
		if (is_object($array)) {
			return ( !property_exists($array, $key1) || !property_exists($array->$key1, $key2) ) ? $default : $array->$key1->$key2;
		}
		else{
			return ( !array_key_exists($key1,$array) || !array_key_exists($key2, $array[$key1]) ) ? $default : $array[$key1][$key2];
		}
	}

	public static function in_array($needle, $haystack, $case_sensitive=true) {
		return ( $case_sensitive ? in_array($needle, self::array_values($haystack)) : in_array(strtolower($needle), array_map('strtolower', $haystack)) );
	}

	public static function array_values($array_or_object){
		if (is_object($array_or_object)){
			$arr = [];
			foreach($array_or_object as $key=>$val){
				$arr[] = $val;
			}
			return $arr;
		}
		else{
			return array_values($array_or_object);
		}
	}

	public static function arrayize($val) {
		return self::is_array($val) ? $val : [$val];
	}
	// i.e. array_child (['a'=>['b'=>'c']], ['a','b']) will return 'c'
	
	public function array_child($arrayOrObject, $childrenKeys){
		if (is_string($childrenKeys)) {
			return $this->array_value($arrayOrObject, $childrenKeys);
		}
		else if (is_array($childrenKeys)){
			if (count($childrenKeys)==0) {
				return $arrayOrObject;
			} else {
				$targetValue = $arrayOrObject;
				foreach($childrenKeys as $childKey){
					$targetValue = $this->array_value($targetValue, $childKey, null);
					if ($targetValue===null) {
						return null;
					}
				}
				return $targetValue;
			};
		} else {
			throw new \Exception("Invalid childrenKeys, should be either string or array");
		}
	}



	// ##########################
	public function arrayIndexBy($array, $targetKey){
		return array_makeKeyedBySubkey($array, $targetKey);
	}
	public function array_makeKeyedBySubkey($array, $targetKey){  //this is 2x faster than below
		return array_column($array, null, $targetKey);
	}
	// strict defines, whether throw exceptions if targetkey not found
	public function array_makeKeyedBySubkey_manual($array, $targetKey, $strict=true){
		$new_array=[];
		foreach($array as $key=>$subArray)
		{
			if ($strict){
				$name = $this->is_array($subArray) ? $subArray[$targetKey] : $subArray->{$targetKey};
			}
			else{
				if ( is_array($subArray) && array_key_exists($targetKey,$subArray) )
					$name = $subArray[$targetKey];
				else if ( is_object($subArray) && property_exists($subArray, $targetKey) )
					$name = $subArray->{$targetKey};
				else
					$name = $key;
			}
			$new_array[$name] = $subArray;
		}
		return $new_array;
	}
	public function array_makeKeyedByValue($array){
		$new_array=[];
		foreach($array as $value)
		{
			$new_array[$value] = $value;
		}
		return $new_array;
	}

	//insert sub-child item in array-children with the child's keyname
	public function array_insertChildSameKey(&$array, $targetKey){
		$new_array=[];
		foreach($array as $key=>$subArray)
		{
			$subArray[$targetKey] =$key;
			$new_array[$key] = $subArray;
		}
		$array = $new_array;
		return $array;
	}
	public function array_parentize($array, $targetSubKey_1, $targetSubKey_2=''){
		$new_array=[];
		foreach($array as $key=>$subArray)
		{
			$parent1_key_will_be = $this->array_value($subArray,$targetSubKey_1); 
			$parent2_key_will_be = empty($targetSubKey_2) ? $key : $this->array_value($subArray, $targetSubKey_2); 
			$new_array[$parent1_key_will_be][$parent2_key_will_be] = $subArray;
		}
		return $new_array;
	}	


	public function array_sub_with_keyvalue($array, $keyName, $shouldBeEqualTo, $resortKeysFromZero=false, $case_sensitive=true)
	{ 
		$newArray = [];
		foreach($array as $key=>$subArray) {
			if (is_array($subArray))
			{
				$add=false;
				if ( array_key_exists($keyName, $subArray) )
				{
					$v_1 = $subArray[$keyName];
					$v_2 = $shouldBeEqualTo;
					if( is_numeric($v_1) && is_numeric($v_2) )
					{
						$v_1 =self::number_format($v_1);
						$v_2 =self::number_format($v_2);
					}
					if( $v_1 === $v_2){
						$add=true;
					}
				}
				if ($add){
					if ($resortKeysFromZero)
						$newArray[] = $subArray;
					else
						$newArray[$key] = $subArray;
				}
			}
			elseif ( is_object($subArray) ) {
				$add=false;
				if ( property_exists($subArray,$keyName) )
				{
					$v_1 = $subArray->$keyName;
					$v_2 = $shouldBeEqualTo;
					if( is_numeric($v_1) && is_numeric($v_2) )
					{
						$v_1 =self::number_format($v_1);
						$v_2 =self::number_format($v_2);
					}
					if( $v_1 === $v_2){
						$add=true;
					}
				}
				if ($add){
					if ($resortKeysFromZero)
						$newArray[] = $subArray;
					else
						$newArray[$key] = $subArray;
				}
			}
		}
		return $newArray;
	}	
	public function array_sub_without_keyvalue($array, $keyName, $shouldBeEqualTo, $resortKeysFromZero=false, $case_sensitive=true)
	{ 
		$newArray = [];
		foreach($array as $key=>$subArray) {
			if (is_array($subArray))
			{
				$add=true;
				if ( array_key_exists($keyName, $subArray) )
				{
					$v_1 = $subArray[$keyName];
					$v_2 = $shouldBeEqualTo;
					if( is_numeric($v_1) && is_numeric($v_2) )
					{
						$v_1 =(float)($v_1);
						$v_2 =(float)($v_2);
					}
					if( $v_1 === $v_2){
						$add=false;
					}
				}
				if ($add){
					if ($resortKeysFromZero)
						$newArray[] = $subArray;
					else
						$newArray[$key] = $subArray;
				}
			}
			elseif ( is_object($subArray) ) {
				$add=true;
				if ( property_exists($subArray,$keyName) )
				{
					$v_1 = $subArray->$keyName;
					$v_2 = $shouldBeEqualTo;
					if( is_numeric($v_1) && is_numeric($v_2) )
					{
						$v_1 =(float)($v_1);
						$v_2 =(float)($v_2);
					}
					if( $v_1 === $v_2){
						$add=false;
					}
				}
				if ($add){
					if ($resortKeysFromZero)
						$newArray[] = $subArray;
					else
						$newArray[$key] = $subArray;
				}
			}
		}
		return $newArray;
	}
	
	
	// CONTAIN //
	public function array_sub_with_keyvalue_contain($array, $keyName, $shoulContain, $resortKeysFromZero=false, $case_sensitive=true)
	{ 
	   //todo
	}
	//TODO
	public function array_sub_without_keyvalue_contain($array, $keyName, $shouldNotContain, $resortKeysFromZero=false, $case_sensitive=true)
	{ 
		foreach($array as $key=>$subArray) {
			if ( array_key_exists($keyName, $subArray) && $this->contains($subArray[$keyName],$shouldNotContain,$case_sensitive) ){
				unset($array[$key]);
			}
		}
		return $array;
	}
	//TODO
	public function array_sub_sub_without_keyvalue_contain($array, $childKey, $keyName, $shouldNotContain, $resortKeysFromZero=false, $case_sensitive=true)
	{ 
		foreach($array as $key_1=>$subArray_1) {
			foreach($subArray_1[$childKey] as $key_2=>$subArray_2) {
				if ( array_key_exists($keyName, $subArray_2) && $this->contains($subArray_2[$keyName],$shouldNotContain,$case_sensitive) ){
					unset($array[$key_1][$childKey][$key_2]);
				}
			}
		}
		return $array;
	}
	// ### CONTAIN ### //

	//TODO
	public function array_sub_replace_keyname($array, $keyName, $replaceWithKeyname)
	{ 
		//$newArray = [];
		//foreach($array as $key=>$subArray) {
		//	if ( $keyName===$key ){
		//		$newArray[$replaceWithKeyname] = $subArray;
		//	}
		//	else{
		//		$newArray[$key] = $subArray;
		//	}
		//}
		//return $newArray;
	}

	public function array_sub_replace_value($array, $keyName, $replaceWhatValue, $replaceWithValue)
	{ 
		$newArray = [];
		foreach($array as $key=>$subArray) {
			if ( array_key_exists($keyName, $subArray) && $subArray[$keyName] === $replaceWhatValue )
				$subArray[$keyName]=$replaceWithValue;
			$newArray[$key] = $subArray;
		}
		return $newArray;
	}

	public function array_subarray_value_above($array, $keyName, $value, $allow_null=false)
	{ 
		$newArray = [];
		foreach($array as $key=>$subArray) {
			//if array
			if ( is_array($subArray) && array_key_exists($keyName, $subArray) && ($subArray[$keyName] >= $value || ($allow_null && is_null($subArray[$keyName]) ) ) ) 
				$newArray[$key] = $subArray;
			//if object
			if ( is_object($subArray) && property_exists($subArray, $keyName) && ($subArray->$keyName >= $value || ($allow_null && is_null($subArray->$keyName) ) ) ) 
				$newArray[$key] = $subArray;
		}
		return $newArray;
	}



	public function array_only_keys_that_contain($array, $string_or_array, $case_sens=true, $position='any'){
		$new=[];
		if (!empty($string_or_array))
		{
			$contained_strs = $this->arrayize($string_or_array);
			foreach($array as $key=>$block)  {
				if ( $this->contains_AgainstArray($key, $contained_strs, $case_sens,$position) ){
					$new[$key] = $block;
				}
			}
		}
		return $new;
	}
	public function array_only_keys_that_not_contain($array, $string_or_array, $case_sens=true, $position='any'){
		$new=[];
		if (!empty($string_or_array))
		{
			$contained_strs = $this->arrayize($string_or_array);
			foreach($array as $key=>$block)  {
				if ( ! $this->contains_AgainstArray($key, $contained_strs, $case_sens,$position) ){
					$new[$key] = $block;
				}
			}
		}
		return $new;
	}

	public function array_only_keys_that_contain_not_contain($array, $INCLUDE_string_or_array=[], $INCLUDE_case_sens=true, $INCLUDE_position='any', $EXCLUDE_string_or_array=[], $EXCLUDE_case_sens=true, $EXCLUDE_position='any'){
		$INCLUDEDs = is_array($INCLUDE_string_or_array) ? $INCLUDE_string_or_array : [$INCLUDE_string_or_array];
		$EXCLUDEDs = is_array($EXCLUDE_string_or_array) ? $EXCLUDE_string_or_array : [$EXCLUDE_string_or_array];
		$isInlcude= !empty($INCLUDEDs);
		$isExclude= !empty($EXCLUDEDs);

		$isObject = is_object($array);
		$new_array=$isObject ? (object) [] : [];
		foreach( $array as $key=>$block)  {
			$set=true;
			if ( $set && ($isInlcude && ! $this->contains_AgainstArray($key, $INCLUDEDs, $INCLUDE_case_sens, $INCLUDE_position) )){
				$set=false;
			}
			if ( $set && ($isExclude && $this->contains_AgainstArray($key, $EXCLUDEDs, $EXCLUDE_case_sens, $EXCLUDE_position) )){
				$set=false;
			}
			if($set){
				if($isObject) $new_array->$key = $block;
				else          $new_array[$key] = $block;
			}
		} 
		return $array;
	}
	public function array_only_values_that_contain($array, $string_or_array, $case_sens=true, $position='any'){
		$new_array=[];
		if (!empty($string_or_array))
		{
			if( $this->is_array($string_or_array) )
			{
				foreach($array as $key=>$block)  {
					if ( $this->contains_AgainstArray($block, $string_or_array, $case_sens, $position) ){
						$new_array[$key]=$block;
					}
				}
			}
			else{
				foreach($array as $key=>$block)  {
					if ( $this->contains($block, $string_or_array, $case_sens, $position) ){
						$new_array[$key]=$block;
					}
				}
			}
		}
		return $new_array;
	}
	public function array_only_values_that_not_contain($array, $string_or_array, $case_sens=true, $position='any'){
		$new_array=[];
		if (!empty($string_or_array))
		{
			if( $this->is_array($string_or_array) )
			{
				foreach($array as $key=>$block)  {
					if ( !$this->contains_AgainstArray($block, $string_or_array, $case_sens, $position) ){
						$new_array[$key]=$block;
					}
				}
			}
			else{
				foreach($array as $key=>$block)  {
					if ( !$this->contains($block, $string_or_array, $case_sens, $position) ){
						$new_array[$key]=$block;
					}
				}
			}
		}
		return $new_array;
	}


	public static function is_array($data){
		return is_array($data) || is_object($data);
	}

	public function array_key_rename($array, $oldKeyToBeRenamed, $newKeyToBeUsed){
		if (array_key_exists($oldKeyToBeRenamed,$array)) 
		{
			$array[$newKeyToBeUsed]=$array[$oldKeyToBeRenamed];
			unset($array[$oldKeyToBeRenamed]);
		}
		return $array;
	}
	
	public function array_key_rename_recursive($array, $keyToRemove, $keyToAdd){
		$array = $this->array_key_rename($array, $keyToRemove, $keyToAdd);
		$new_array =[];
		if (is_array($array))
		{
			foreach($array as $key=>$value)
			{
				$new_array[$key]= !is_array($value) ? $value : $this->array_key_rename_recursive($value, $keyToRemove, $keyToAdd);
			}
		}
		else{
			$new_array = $array;
		}
		return $new_array;
	}
	
	public function array_change_key_case(&$arr, $case = CASE_LOWER)
	{
		$arr = array_change_key_case($arr, $case);
		$arr = array_map(function(&$item) use($case) {
			if(is_array($item))
				$this->array_change_key_case($item, $case);
			return $item;
		}, $arr );
	}
	//another : https://stackoverflow.com/a/49993735/2377343
	public function array_change_key_case_recursive(&$arr, $case = CASE_LOWER)
	{
		return array_map(function($item) use($case) {
			if(is_array($item))
				$item = $this->array_change_key_case_recursive($item, $case);
			return $item;
		}, array_change_key_case($arr, $case));
	}
	
    public static function has_child_with_key_value ($element, $targetKey, $targetValue) {
        $keys = self::keys ($element);
        for ($i = 0; $i < count($keys); $i++) {
            $currentKey = $keys[$i];
            $childMember = $element[$currentKey];
            $value = self::array_value ($childMember, $targetKey, null);
            if ($value === $target_keytargetValue) {
                return true;
            }
        }
        return false;
    }

	public function insert_value_at_position($arr, $insertedArray, $position) {
		$i = 0;
		$new_array=[];
		foreach ($arr as $key => $value) {
			if ($i == $position) {
				foreach ($insertedArray as $ikey => $ivalue) {
					$new_array[$ikey] = $ivalue;
				}
			}
			$new_array[$key] = $value;
			$i++;
		}
		return $new_array;
	}

	public function array_has_subchild_with_value($array,$subchild_key, $subchild_value, $strict=true){
		$found = false;
		foreach($array as $key=>$child){
			$val =  $this->array_value($child, $subchild_key);
			if     ( $strict && $val===$subchild_value)
				$found=true;
			elseif (!$strict && $val==$subchild_value)
				$found=true;
		}
		return $found;
	} 

	// supports asterisks i.e. *->keyname  ( https://pastebin_com/BcC8ztDp )
	public function array_with_keys($array_or_object, $keyToRemain)
	{
		$asterisk       = '*'; //should always have child ->
		$child_divisor  = '->';
		$keyToRemain_ALL= is_array($keyToRemain)?$keyToRemain:[$keyToRemain];

		if (is_array($array_or_object))
		{
			$new_arr 	  = [];
			//normal
			$keysToRemain_NORMAL = array_filter($this->array_only_values_that_not_contain($keyToRemain_ALL, $child_divisor) );
			$keysToRemain_REGEX  = array_filter($this->array_only_values_that_contain($keyToRemain_ALL, $child_divisor) );
			foreach( $array_or_object as $key_1=>$val_1 )
			{
				if ( in_array($key_1, $keysToRemain_NORMAL) ){ 
					$new_arr[$key_1]=$val_1;
				}
			}
			foreach( $array_or_object as $key_1=>$val_1 )
			{
				if(array_key_exists($key_1, $new_arr) )
					continue;
				foreach( $keysToRemain_REGEX as $eachRegex )
				{
					$parts = explode($child_divisor, $eachRegex, 2 );
					$first = $parts[0];
					$second= $parts[1];
					if( $first===$asterisk || $first===$key_1){
						if(is_array($array_or_object[$key_1])){
							$sVals=  $this->array_with_keys($array_or_object[$key_1],$second);
							if (empty($new_arr[$key_1])) $new_arr[$key_1]=[];
							$new_arr[$key_1]= array_merge_recursive($new_arr[$key_1],$sVals); 
						}
						elseif( $first===$key_1 ){
						//	$new_arr[$key_1]=$array_or_object[$key_1];
						}
					}
				}	
			}
			return $new_arr;
		}
		elseif(is_object($array_or_object ))
		{ 
			foreach($array_or_object as $key=>$value) {
				if( !in_array($key,$keyToRemain_ALL) ){
					unset($array_or_object->$key);
				}
			}
			return $array_or_object;
		}
		else{
			return $array_or_object;
		}
	} //old: https://pastebin_com/Tbm8sEGH 

	
	// supports asterisks i.e. *->keyname  ( https://pastebin_com/BcC8ztDp )
	public function array_unset_keys($array_or_object, $keysToUnset)
	{ 
		$asterisk       = '*'; //should always have child ->
		$child_divisor  = '->';
		$keysToUnset_ALL= is_array($keysToUnset)?$keysToUnset:[$keysToUnset];
		
		$is_arr=is_array($array_or_object);
		$is_obj=is_object($array_or_object);
		if ($is_arr || $is_obj)
		{
			$new_arr      = [];
			$keysToUnset_NORMAL = array_filter($this->array_only_values_that_not_contain($keysToUnset_ALL, $child_divisor) );
			$keysToUnset_REGEX  = array_filter($this->array_only_values_that_contain($keysToUnset_ALL, $child_divisor) );

			foreach( $array_or_object as $key_1=>$val_1 )
			{
				if ( $is_arr && !in_array($key_1, $keysToUnset_NORMAL)){
					$new_arr[$key_1]=$val_1;
				}
				if( $is_obj && in_array($key_1, $keysToUnset_NORMAL)){
					unset($array_or_object->$key_1);
				}
			}

			foreach( ($is_arr ? $new_arr : $array_or_object) as $key_1=>$val_1 )
			{
				foreach( $keysToUnset_REGEX as $eachRegex )
				{
					$parts = explode($child_divisor, $eachRegex, 2 );
					$first = $parts[0];
					$second= $parts[1];
					if( $first===$asterisk || $first===$key_1 ){
						if( 
							($is_arr && !is_object($new_arr[$key_1]) && !is_array($new_arr[$key_1])) 
								|| 
							($is_obj && !is_object($array_or_object->$key_1) &&  !is_array($array_or_object->$key_1)  ) 
						)
						{
							//$new_arr[$key_1]=$array_or_object[$key_1];
						}
						else{
							if ( $is_arr )
								$new_arr[$key_1]=$this->array_unset_keys($new_arr[$key_1],$second);
							if ( $is_obj )
								$array_or_object->$key_1=$this->array_unset_keys($array_or_object->$key_1,$second); 
						}
					}
				}	
			}
			if($is_arr) return $new_arr;
			if($is_obj) return $array_or_object;
		}
	}
	///////////////

	public static function array_part($array, $amount, $from="start|end")
	{
		return self::array_trim($array, $amount, $from);
	}
	public static function array_trim($array, $amount, $from="start|end")
	{
		$count = count($array);
		return $count<=$amount ? $array : array_slice($array, $from="start" ? 0 : $count-$amount, $amount);
	}

	//add only in case the array didnt containted it already
	public function add_in_array_if_not_already_added($my_arrayy,$target_value){
		if (array_search($target_value, $my_arrayy) !== true) {	$my_arrayy[] = $target_value;}			return $my_arrayy;
	}

	//remove item from array by value
	public function unset_by_value(&$my_arrayy, $target_value, $reindex=false){
		$new=[];
		if (!empty($my_arrayy) && is_array($my_arrayy) ) {
			foreach ($my_arrayy as $key => $value){  
				if ($value != $target_value) { 
					if(!$reindex)
						$new[$key] = $my_arrayy[$key]; 
					else
						$new[] = $my_arrayy[$key]; 
				}   
			}
		}
		$my_arrayy = $new;
		return $my_arrayy;
	}

	public function array_is_associative(array $array) {
		return count(array_filter(array_keys($array), 'is_string')) > 0;
	}
	
	public function is_associative(array $arr){
		if (array() === $arr) return false;
		return array_keys($arr) !== range(0, count($arr) - 1);
	}
	
	public function array_get_by_subkey($array, $subkey, $subvalue){
		foreach ($array as $each){
			//if()
		}
		return [];
	}

	public function next_key_in_array($target_keyname, $array){
		$keys = array_keys($array);
		$index_of_target_keyname = array_search($target_keyname,  $keys , true);
		return (count($array) > $index_of_target_keyname+1 ) ? $keys[$index_of_target_keyname+1]  :  $keys[0];
	}

	public function next_value_in_array($target_value, $array, $by_key=false){
		$keys = array_keys($array);
		$target_keyname = $by_key ? $target_value : array_search($target_value,  $array, true );
		$index_of_target_keyname = array_search($target_keyname,  $keys, true );
		return (count($array) > $index_of_target_keyname+1 ) ? $array[ $keys[$index_of_target_keyname+1] ]  :  $array[  $keys[0]  ];
	}

	public function array_search($targetValue, $array){
		foreach($array as $key=>$value){
			if ($value===$targetValue)
				return $key;
		}
		return false;
	}

	public function string_contains_array_value_any($string, $array, $case_sensitive=false){
		$found='';
		$string = $case_sensitive ? $string : strtolower($string);
		$array = $case_sensitive ? $array : array_map('strtolower', $array);
		foreach($array as $each)
		{
			if (stripos($string,$each)!==false){
				$found=$each; break;
			}
		}
		return $found;
	}
	public function string_contains_array_value_all($string, $array, $case_sensitive=false){
		$result=true;
		$string = $case_sensitive ? $string : strtolower($string);
		$array  = $case_sensitive ? $array  : array_map('strtolower', $array);
		foreach($array as $each)
		{
			if (stripos($string,$each)===false){
				$result=false; break;
			}
		}
		return $result;
	}
	public function array_values_contain_string($array, $string, $case_sensitive=false){
		$found=false;
		$string = $case_sensitive ? $string : strtolower($string);
		foreach($array as $each)
		{
			$each = $case_sensitive ? $each : strtolower($each);
			if (stripos($each,$string)!==false){
				$found=true; break;
			}
		}
		return $found;
	}

	public static function keys($array_or_object){
		if(is_array($array_or_object))
			return array_keys($array_or_object);
		//elseif (is_object($arrayOrObject))
			return array_keys(get_object_vars($array_or_object));
		//return [];
	}
	public static function key_at_index($array_or_object, $position){
		return self::keys($array_or_object)[$position];
	}
	public static function value_at_index($array_or_object, $position){
		$target_key = self::key_at_index($array_or_object, $position);
		if ($target_key === $position) {
			return $array_or_object[$position];
		}
		else {
			return self::array_value($array_or_object, $target_key, null);
		}
	}
	public function index_of_key($array, $key){
		return array_search($key, array_keys($array));
	}
	public function index_of_value($array, $value){
		return array_search($value, $array);
	}
	public function array_get_part($array, $from_start=0, $from_end=0){
		$i=0;
		$new_arr=[];
		$count = count($array);
		foreach($array as $key=>$value)
		{
			$i++;
			if ($i<= $from_start ) $new_arr[$key]=$value;
			if ($i> $count-$from_end ) $new_arr[$key]=$value;
		}
		return $new_arr;
	}

	public static function value_at_last_index( $array )
	{
		$keys = self::keys($array);
		$key_last = $keys[ count($keys)-1];
		return self::array_value($array, $key_last, null);
	}

	public function array_sort_by_key($array, $key, $remove_current= false){
		$remaining =  array_splice ($array, $this->index_of_key($array, $key));
		if($remove_current){
			$array[$key]= $remaining[$key];
			unset($remaining[$key] );
		}
		return array_merge($remaining, $array);
	}
	
	// todo
	public function array_sort_by_key2($array, $key, $direction=SORT_ASC){
		$is_obj= is_object($array);
		if ($is_obj)
			$array = (array) $array;
		$columns = array_column($array, $key);
        if ( array_multisort($columns, $direction, $array) )
			return ($is_obj ? (object) $array : $array);
		else
			throw new \Exception("Multisort failed");
	}

	//in multi dimensional array
	public function array_find_by_key_value($array, $key, $value, $defaultValue = []){
		foreach($array as $subArray){
			if (is_array($subArray)) {
				if (array_key_exists($key, $subArray) && $subArray[$key] === $value){
					return $subArray;
				}
			} else {
				if (property_exists($subArray, $key) && $subArray->{$key} === $value) {
					return $subArray;
				}
			}
		}
		return $defaultValue; //new \stdClass()
	}

	// array_diff ( array $array , array ...$arrays ): Compares array against one or more other arrays and returns the values in array that are not present in any of the other arrays.
	//response only gives the items, that are in ARRAY_1, and doesn't matter if there are more things in ARRAY_2. See sample: pastebin_com/AUx4238H
	public function array_diff_assoc_recursive( $array_source, $array_toward)
	{
		$difference = [];
		if (empty($array_toward))
			return $array_source;
			
		foreach($array_source as $key => $value_1){
			// if target array does not have this key
			if ( 
				(is_array($array_source) && !array_key_exists($key, $array_toward) )
				||
				(is_object($array_source) && !property_exists($array_toward, $key) )
			)
			{
				$difference[$key] = $value_1;
			}
			else {
				$value_2 = is_array($array_toward) ? $array_toward[$key] :  $array_toward->$key;
				// if member is simple-type
				if( !is_array($value_1) && !is_object($value_1) &&$value_1!=$value_2 ){
					$difference[$key] = $value_1; 
				}
				// if member is array/object
				elseif ( is_array($value_1) || is_object($value_1) ) {
					if ( !is_array($value_2) && !is_object($value_2) ) {
						$difference[$key] = $value_1;
					}
					else{
						$newDiff = $this->array_diff_assoc_recursive($value_1, $value_2 );
						if( !empty($newDiff) )
						{
							$difference[$key] = $newDiff;
						}
					}
				}
			}
		}
		return $difference;
	}
	public function array_diff_recursive( $array_source, $array_compared_to )
	{
		return [];
	}
	public function array_difference( $array_source, $array_compared_to )
	{
		$diff = [
			'first'=> $this->array_diff_assoc_recursive($array_source, $array_compared_to),
			'second'=> $this->array_diff_assoc_recursive($array_compared_to, $array_source),
		];
		
		$diff['added'] = [];
		$diff['changed'] = [];
		$diff['removed'] = [];
		foreach ($diff['first'] as $keyNm => $block) {
			if ( !array_key_exists($keyNm, $diff['second']) ) 
				$diff['added'][$keyNm]=$block; 
			else 
				$diff['changed'][$keyNm]=$block; 
		}
		foreach ($diff['second'] as $keyNm => $block) {
			if ( !array_key_exists($keyNm, $diff['first']) ) 
				$diff['removed'][$keyNm]=$block; 
		}
		return $diff;
	}
	
	public function array_diff_key_full( $array_source, $array_toward )
	{
		$diff = [
			'added'=> array_diff_key($array_source, $array_toward),
			'removed'=> array_diff_key($array_toward, $array_source),
		];
		return $diff;
	}
	
	


	public function array_difference_full( $array_source, $array_toward )
	{
		$difference = [ 'new'=>[], 'old'=>[], 'different'=>[] ];
		if (empty($array_toward)){
			$difference['old'] = $array_source;
		}
		else{
			foreach($array_toward as $key => $value){
				// if target array does not have this key, then tell that this key is new
				if ( !array_key_exists($key, $array_source) )
				{
					$difference['new'][$key] = $value;
				}
			}

			foreach($array_source as $key => $value){
				// if target array does not have this key, then tell that this key is new
				if ( !array_key_exists($key, $array_toward) )
				{
					$difference['old'][$key] = $value;
				}
				else {
					$value_2 = $array_toward[$key];
					// if member is simple-type
					if( !is_array($value) )
					{
						if ( $value_2 != $value ) 
						{
							$difference['different'][$key] = $value;
						}
					}
					// if member is array/object
					else { 
						// if array
						if ( is_array($value) ) 
						{
							if (!is_array($value_2) ) {
								$difference['different'][$key] = $value;
							}
							else{
								$newDiff = $this->array_difference_full($value, $value_2);
								if( !empty($newDiff) )
								{
									$difference['different'][$key] = $newDiff;
								}
							}
						}
					}
				}
			}
		}
		return $difference;
	}

	public static function array_keys_diff($ar1, $ar2){ return self::array_diff_keys($ar1, $ar2); }
	public static function array_diff_keys($ar1, $ar2){
		$k1=array_keys($ar1);  $k2=array_keys($ar2);
		return [ array_diff($k1, $k2), array_diff($k2, $k1) ];
	}

	public function array_intersect_assoc_recursive(&$arr1, &$arr2) {
		if (!is_array($arr1) || !is_array($arr2)) {
	//      return $arr1 == $arr2; // Original line
			return (string) $arr1 == (string) $arr2;
		}
		$commonkeys = array_intersect(array_keys($arr1), array_keys($arr2));
		$ret = array();
		foreach ($commonkeys as $key) {
			$var= $this->array_intersect_assoc_recursive($arr1[$key], $arr2[$key]);
			$ret[$key] = & $var;
		}
		return $ret;
	}
	
	public function array_emptify_keys($array, $insert_key_in_body='')
	{
		$new=[];
        foreach($array as $key1=>$block) {
			$new[]=$block;
		}
		return $new;
	}
	public function array_emptify_keys_sub($array, $insert_key_in_body='')
	{
		$new=[];
        foreach($array as $key1=>$block1) {
			foreach($block1 as $key2=>$block2) {
				if(!empty($insert_key_in_body))
				{
					if(is_object($block2))
						$block2->$insert_key_in_body = $key2;
					else
						$block2[$insert_key_in_body] = $key2;
				}
				$new[$key1][]=$block2;
			}
		}
		return $new;
	}
	 

	public function array_merge($obj_or_array1, $obj_or_array2)
	{
		$is_obj = is_object($obj_or_array1);
		if ($is_obj){
			return (object) array_merge((array) $obj_or_array1, (array) $obj_or_array2);
		}
		else{
			return array_merge($obj_or_array1, $obj_or_array2);
		}
	}
    public function array_merge_sub($array, $unique=false) {
        $final_arr=[];
        foreach($array as $block) $final_arr=array_merge($final_arr, $block);
        return ($unique ? array_unique($final_arr) : $final_arr);
    }
    public function array_merge_sub_sub($array, $which_key, $unique=false) {
        $final_arr=[];
        foreach($array as $block) 
			$final_arr=array_merge($final_arr, $this->array_value($block,$which_key) );
        return ($unique ? array_unique($final_arr) : $final_arr);
    }
	public function array_merge_recursive_distinct ( array $array1, array $array2 )
	{
	  $merged = $array1;
	  foreach ( $array2 as $key => &$value )
	  {
		if ( is_array ( $value ) && isset ( $merged [$key] ) && is_array ( $merged [$key] ) )
		{
		  $merged[$key] = $this->array_merge_recursive_distinct ( $merged [$key], $value );
		}
		else
		{
		  $merged[$key] = $value;
		}
	  }
	  return $merged;
	}

	public function recursive_for_array_value($array,$function_name=false){ 
		//on first run, we define the desired function name to be executed on values
		if ($function_name) { $GLOBALS['current_func_name']= $function_name; } else {$function_name=$GLOBALS['current_func_name'];}
		//now, if it's array, then recurse, otherwise execute function
		return is_array($array) ? array_map('recursive_for_array_value', $array) : $function_name($array); 
	}

	public function key_after_key($keyname, $array, $increment){
		$keys = array_keys($array);
		$current_key_index = array_search($keyname, $keys);
		return $keys[array_search($keyname,$keys)+$increment];
	}

	public function array_set_keys_from_child($array, $keyName){
		$new=[];
		foreach($array as $key=>$value) { if (isset($value[$keyName])) $new[$value[$keyName]]=$value;   }
		return $new; 
	}
	public function array_column_with_keys($array,$keyName){ 
		$new=[]; foreach($array as $key=>$value) { if (isset($value[$keyName])) $new[$key]=$value[$keyName];   }
		return $new;  
	}

	public function array_only_with_key($array, $keyName, $target_level){
		$new	= []; 
		$old	= $array; 
		$value_0= $array;
		$cur_level = 0;
		if (is_array($value_0) && $target_level>=$cur_level)
		foreach($value_0 as $key_1=>$value_1) { 
			$cur_level = 1;
			if ($cur_level == $target_level && $key_1 == $keyName)
			{
				$new[$key_1] = $value_1;
				unset($old[$key_1]);
			}
			else{
				if (is_array($value_1) && $target_level>=$cur_level)
				foreach($value_1 as $key_2=>$value_2) { 
					$cur_level = 2;
					if ($cur_level == $target_level && $key_2 == $keyName)
					{
						$new[$key_1][$key_2] = $value_2;
						unset($old[$key_1][$key_2]);
					}
					else{
						if (is_array($value_2) && $target_level>=$cur_level)
						foreach($value_2 as $key_3=>$value_3) { 
							$cur_level = 3;
							if ($cur_level == $target_level && $key_3 == $keyName)
							{
								$new[$key_1][$key_2][$key_3] = $value_3;
								unset($old[$key_1][$key_2][$key_3]);
							}
							else{
								if (is_array($value_3) && $target_level>=$cur_level)
								foreach($value_3 as $key_4=>$value_4) { 
									$cur_level = 4;
									if ($cur_level == $target_level && $key_4 == $keyName)
									{
										$new[$key_1][$key_2][$key_3][$key_4] = $value_4;
										unset($old[$key_1][$key_2][$key_3][$key_4]);
									}
									else{
										if (is_array($value_4) && $target_level>=$cur_level)
										foreach($value_4 as $key_5=>$value_5) { 
											$cur_level = 5;
											if ($cur_level == $target_level && $key_5 == $keyName)
											{
												$new[$key_1][$key_2][$key_3][$key_4][$key_5] = $value_5;
												unset($old[$key_1][$key_2][$key_3][$key_4][$key_5]);
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
		return ['with'=>$new, 'without'=>$old];  
	}
	public function array_column_with_key_2($array,$keyName){
		return array_filter(array_combine(array_keys($array), array_column($array, $keyName)));
	}







	// #################### conversions ##########################

	public function php_to_js_array($array){
		return '["'. implode('","', $array ) .'"]';
	}
	public static function xml_to_array($xml_string, $replace_colons=false, $opts=0 ){
		return json_decode(self::xml_to_json($xml_string, $replace_colons, $opts), true);
		// try
		// {
		// 	$xml = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA);
		// 	return json_decode( json_encode($xml), TRUE);
		// } catch (Exception $ex) {
		// 	return ['xmlerror'=>$ex];
		// }
	}
	public static function xml_to_json($xml_string, $replace_colons=false, $opts=0 ){
		if ($opts===0) 
			$opts = LIBXML_NOCDATA | LIBXML_COMPACT | LIBXML_NOENT ; //https://www.php.net/manual/en/libxml.constants.php : LIBXML_NOCDATA | LIBXML_COMPACT  | LIBXML_DTDLOAD |   
		if ($replace_colons)
			$xml_string = self::xml_tag_replace_colons($xml_string);
		$xml = simplexml_load_string($xml_string, null, $opts);  
		return json_encode($xml);
	}
	public function xml_to_array_by_key($content, $keyName, $removeIfKeyIsOnlyChild=false)
	{
		try
		{
			$xml = simplexml_load_string($content, "SimpleXMLElement", LIBXML_NOCDATA );
			$array= json_decode( json_encode($xml), TRUE);
			return $this->xml_set_child($array, $keyName, $removeIfKeyIsOnlyChild);
		} catch (Exception $ex) {
			return ['xmlerror'=>$ex];
		}
	}
	public function xml_set_child($array, $keyName, $removeIfKeyIsOnlyChild=false)
	{
		$new_array= [];
		foreach ($array as $key_1=>$value_1)
		{
			if (is_array($value_1) && isset($value_1[0]))
			{
				$sub_arr=[];
				foreach ($value_1 as $idx=>$value_2)
				{
					$keyValue = $value_2['@attributes'][$keyName];
					if( $removeIfKeyIsOnlyChild && count($value_2)==1 && count($value_2['@attributes'])==1 ) 
						$new_array[$key_1][$keyValue] = null;
					else
						$new_array[$key_1][$keyValue] = $this->xml_set_child($value_2, $keyName, $removeIfKeyIsOnlyChild);
				}
			}
			else{
				$new_array[$key_1]=$value_1;
			}
		}
		return $new_array;
	}


	public static function xml_tag_replace_colons($xmlData){
		return preg_replace('~(</?|\s)([a-z0-9_]+):~is', '$1$2_', $xmlData);
	}
	
	public function array_to_xml_output($array) {
		$xml_data = new \SimpleXMLElement('<?xml version="1.0"?><xml_data></xml_data>');
		$this->array_to_xml($array, $xml_data);
		//$result = $xml_data->asXML('/file/path/name.xml');
		return $xml_data->asXML();
	}

	public function array_to_xml( $data, &$xml_data ) {
		foreach( $data as $key => $value ) {
			if( is_numeric($key) ){	$key = 'item'.$key; } //dealing with <0/>..<nuemric/> issues
			if( is_array($value) ) { $subnode = $xml_data->addChild($key);	array_to_xml($value, $subnode);	} 
			else {	$xml_data->addChild("$key",htmlspecialchars("$value"));	}
		}
	}
	//##############  ARRAYs #################

	

	public function unquote($txt){
		return str_replace('"', "", str_replace("'", "", $txt) );
	} 

	public function get_extension($fileUrl)
	{
		$array=explode('.', basename(parse_url($fileUrl)['path']));
		return $array[count($array)-1]; 
	}
	public function filename($fileUrl)
	{
		$array=explode('.', basename(parse_url($fileUrl)['path']));
		$min = min(count($array), 2);
		return basename($array[$min-2]); 
	}

	public function files_list_in_dir($path, $only_files = false) {
		$all_list = new \RecursiveIteratorIterator(
				new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS),
				( $only_files ? \RecursiveIteratorIterator::LEAVES_ONLY : \RecursiveIteratorIterator::SELF_FIRST )
		);
		$files = [];
		foreach ($all_list as $file)
			$files[] = $file->getPathname();

		return $files;
	}

	public function set_property($obj, $property, $value) {
		$reflection = new \ReflectionClass($obj);
		$property = $reflection->getProperty($property);
		$property->setAccessible(true);
		return $property->setValue($obj, $value);
	}

	public function is_localhost($domain=''){
		return in_array( (!empty($domain) ? $domain : $this->domainCurrentWithoutPort), ['localhost','127.0.0.1','::1']); 
	}

	public function is_json($string){
		return (is_string($string) && is_array(json_decode($string, true)));
	}

	public function arrayed_json($answer){
		$result = [];
		if(!$this->is_json($answer)){
			$result['error'] = $answer;
		}
		else{
			$result = json_decode($answer, true);
		}
		return $result;
	}

	public function arrayed_answer($answer){
		$result = [];
		if(!$this->is_json($answer)){
			$result['error'] = $answer;
		}
		else{
			$result = json_decode($answer, true);
		}
		return $result;
	}

	public function maybe_json($string){
		$firstLetter = substr($string, 0, 1); 
		$maybe_json = $firstLetter==='{' || $firstLetter==='[';
		return $maybe_json;
	}

	//from Wordpress codex
	public static function is_serialized( $data, $strict = true ) { if ( ! is_string( $data ) ) { return false; } $data = trim( $data ); if ( 'N;' === $data ) { return true; } if ( strlen( $data ) < 4 ) { return false; } if ( ':' !== $data[1] ) { return false; } if ( $strict ) { $lastc = substr( $data, -1 ); if ( ';' !== $lastc && '}' !== $lastc ) { return false; } } else { $semicolon = strpos( $data, ';' ); $brace     = strpos( $data, '}' ); if ( false === $semicolon && false === $brace ) { return false; } if ( false !== $semicolon && $semicolon < 3 ) { return false; } if ( false !== $brace && $brace < 4 ) { return false; } } $token = $data[0]; switch ( $token ) { case 's': if ( $strict ) { if ( '"' !== substr( $data, -2, 1 ) ) { return false; } } elseif ( false === strpos( $data, '"' ) ) { return false; } case 'a': case 'O': return (bool) preg_match( "/^{$token}:[0-9]+:/s", $data ); case 'b': case 'i': case 'd': $end = $strict ? '$' : ''; return (bool) preg_match( "/^{$token}:[0-9.E+-]+;$end/", $data ); } return false; }

	//https://www.php.net/manual/en/errorfunc.constants.php
	public function errors_exception(){
		//set_error_handler("warning_handler", E_WARNING);
		set_error_handler(function($errno, $errstr, $errfile, $errline) {
			// error was suppressed with the @-operator
			if (0 === error_reporting()) {
				return false;
			}
			
			throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
		});
	}


	public function disable_header_cache($hard=false, $file=false){
		// TODO - handle _POST
		header("Expires: Mon, 4 Jan 1999 12:00:00 GMT");        // Expired already 
		header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");     
		header("Cache-Control: no-cache, must-revalidate");      // good for HTTP/1.1 
		header("Pragma: no-cache"); 
		if($hard){
			if(!isset($_GET['rand']))
				$this->php_redirect( $this->AddStringToUrl($_SERVER['REQUEST_URI'], 'rand='.rand(1,9999999) )   );
		}
		ini_set("opcache.enable", 0); 
		if($file){
			opcache_invalidate($file);
		}
	}


		
	public function my_mail($a=null,$b=null,$c=null,$d=null,$e=null){ return (!$this->definedTRUE("MAILS_DISABLED") ? mail($a,$b,$c,$d,$e) : "MAILS_NOT_ENABLED__error99234"); }

	public function get_yout_Vid_Aud_array($ID,$TITL)	{return yout_DownUrls($ID, $TITL);}

	public function default_mail_headers($from=false){ return $headers='MIME-Version: 1.0' . "\r\n" . 'Content-type: text/html; charset=UTF-8' . "\r\n" . 'From: mesg@' .$_SERVER['HTTP_HOST'] ."\r\n".'Reply-To: mesg@'.$_SERVER['HTTP_HOST'] . "\r\n" . "X-Mailer: PHP/" . phpversion(); }	
		


	//use whenever you want to show something on the first happening
	// first_cookie_message('ini_get_noexits','<script>alert("ini_get doesnt work on server. i will hide forever now");</script>')
	public function first_cookie_message($identifier, $message){
		$cName=filter_var($identifier, FILTER_SANITIZE_STRING);
		if (!isset($_COOKIE[$cName])){
			setcookie($cName,'okk',time()+99999999, $this->constant('homeFOLD','/'));
			die($message);
		}
	}

	public function cookie_set($name){ if (empty($_COOKIE[$name])) { return false;} else { return true;} }
	public function cookie_setOnceExecution($name){ if (empty($_COOKIE[$name])) { setcookie($name, time(), time()+ 999999,  $this->constant('homeFOLD','/') ); return true; } return false; }
	public function cookie_not_set($name){ cookie_setOnceExecution($name); }

	public function set_cookie($name, $val, $time_length = 86400, $path=false, $domain=false, $httponly=true, $only_on_secure_https = false){
		$site_urls = parse_url( (function_exists('home_url') ? home_url() : $_SERVER['SERVER_NAME']) );
		$real_domain = $site_urls["host"];
		$path = $path ? $path : ( (!empty($this) && property_exists($this,'homeFOLDER') ) ?  $this->homeFOLDER : '/');
		$domain = $domain ? $domain : ((substr($real_domain, 0, 4) == "www.") ? substr($real_domain, 4) : $real_domain);
		setcookie ( $name , $val , time()+$time_length, $path = $path, $domain = $domain,  $only_on_secure_https,  $httponly  );
	}
	public function setcookie_secure($name, $val, $time_length = 86400, $httponly=true, $homeurl=false){
		$real_domain = $homeur ?: $_SERVER['HTTP_HOST'];
		$domain = (substr($real_domain, 0, 4) == "www.") ? substr($real_domain, 4) : $real_domain;
		$path = $path ?: ( (!empty($this) && property_exists('pathAfterDomain', $this) ) ?  $this->pathAfterDomain : '/');
		setcookie ( $name , $val , time()+$time_length, $path, $domain = $domain ,  $only_on_https = FALSE,  $httponly  );
	}

	public function page_load_limited_for_seconds($seconds = 3, $cookiename = 'pageloader_limiter'){
		if (isset($_COOKIE[$cookiename])) {
			
		}
	}
	public function site_slug() { return str_replace(array('.','/',':'),'_', $this->domain  ); }

	public function site_visitor_default_cookiee() {return 'default_visitr_'.site_slug(); }

	public function set_cookie_for_visitors(){ setcookie(site_visitor_default_cookiee(), time()+1000, time()+1000, $this->constant('homeFOLD','/'));  }
	//      SetCookieForVisitors();

	public function die_if_not_this_site_youtube(){if (!isset($_COOKIE[site_visitor_default_cookiee()])) {  die('noauth_6453'); } }

	public $share_urls =
	[
		'facebook'	=>'https://www.facebook.com/sharer/sharer.php?u=', 
		'twitter'	=>'https://twitter.com/share?url='
	];

	public function validate_mail( $mail ){  
		return !filter_var( $mail, FILTER_VALIDATE_EMAIL );
	}

	// only for explicit temp use
	public function password_site($password, $hint="Type password")
	{
		$rnd_ext = 'pss_'.str_replace('.','_', $this->domain);
		if ( $password == $this->Post('passwk') ) { setcookie($rnd_ext, $password,  time()+1111111,  $this->homeFOLD); header("location:".$_SERVER['REQUEST_URI']);exit; } 
		elseif (!isset($_COOKIE[$rnd_ext]) || $_COOKIE[$rnd_ext]!=$password ){ echo '<div style="display:flex; justify-content:center;"><form action="" method="post">  <b>'. $this->sanitize_text($hint).'</b>:<input name="passwk" value="">  <input type="submit" value="Enter"></form></dvi>';exit;}
	}	

	public function get_filename_($url){ return basename(parse_url($url)['path']); }

	public function scriptt($name, $with_css=false)	{ 
		return  ( (!empty($GLOBALS['already_loaded_'.$name])) ? '<!-- already outputed "'.$name.'" -->' :  $GLOBALS['already_loaded_'.$name]='<script type="text/javascript" src="'. $GLOBALS['odd']['scripts'][$name]['js'].'"></script>')  
			.  
		( !$with_css ? '' : '<link rel="stylesheet" href="'.  $GLOBALS['odd']['scripts'][$name]['css'].'"> '   );
	}

	public function scriptss(){
		foreach(func_get_args() as $key=>$value){ echo (!is_array($value) ? scriptt($value) : scriptt($value[0], $value[1]) ); }
	}


	public function translate__MONTH($text,$target_lang=''){   global $odd;	//switch ($text) { case 'January':	return TRANSLL('monthh1',$target_lang);
		if( !empty($odd['months_langs'][$target_lang]) && array_key_exists($text, $odd['months_langs'][$target_lang]))	{  
			$text = $odd['months_langs'][$target_lang][$text];
			if (mb_detect_encoding($text) =='UTF-8') {$text= mb_substr ($text,0,3,'utf-8') ; }  
		} 
		else{
			$text = TRANSLL($text,$target_lang);
		}
		return $text;
	}

    protected function parseJson($content)
    {
        $content = json_decode($content, true);

        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new UnexpectedValueException(sprintf(
                "Failed to parse JSON response: %s",
                json_last_error_msg()
            ));
        }

        return $content;
    }

	public function translate__DAY($text,$target_lang='') {	
		if (in_array($text, array('Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sunday')) ) {
			return TRANSLL($text,$target_lang); 
		} return $text;
	}

	// language specifics
	public function GEO_to_ENG($input){  return strtr($input, array(
		"�?"=>"a",	"ბ"=>"b",	"გ"=>"g",	"დ"=>"d",	"ე"=>"e",	"ვ"=>"v",	"ზ"=>"z",	"თ"=>"T",	"ი"=>"i",
		"კ"=>"k",	"ლ"=>"l",	"მ"=>"m",	"ნ"=>"n",	"�?"=>"o",	"პ"=>"p",	"ჟ"=>"J",	"რ"=>"r",	"ს"=>"s",
		"ტ"=>"t",	"უ"=>"u",	"ფ"=>"f",	"ქ"=>"q",	"ღ"=>"R",	"ყ"=>"y",	"შ"=>"S",	"ჩ"=>"C",	"ც"=>"c",
		"ძ"=>"Z",	"წ"=>"w",	"ჭ"=>"W",	"ხ"=>"x",	"ჯ"=>"j",	"ჰ"=>"h"    	));
	}
	public function ENG_to_GEO($input) { return strtr($input, array(
		'a'=>'�?',	'b'=>'ბ',	'g'=>'გ',	'd'=>'დ',	'e'=>'ე',	'v'=>'ვ',	'z'=>'ზ',	'T'=>'თ',	'i'=>'ი',
		'k'=>'კ',	'l'=>'ლ',	'm'=>'მ',	'n'=>'ნ',	'o'=>'�?',	'p'=>'პ',	'J'=>'ჟ',	'r'=>'რ',	's'=>'ს',
		't'=>'ტ',	'u'=>'უ',	'f'=>'ფ',	'q'=>'ქ',	'R'=>'ღ',	'y'=>'ყ',	'S'=>'შ',	'C'=>'ჩ',	'c'=>'ც',
		'Z'=>'ძ',	'w'=>'წ',	'W'=>'ჭ',	'x'=>'ხ',	'j'=>'ჯ',	'h'=>'ჰ'		));
	}

	//UPPERCASE CHARS sometimes MESS-UP several FUNCTION's USAGE. So, sometimes we need lowercased words
	public function GEO_to_ENG__LowerCased($m) { return strtolower(strtr($m, array( 
		"�?"=>"a",	"ბ"=>"b",	"გ"=>"g",	"დ"=>"d",	"ე"=>"e",	"ვ"=>"v",	"ზ"=>"z",	"თ"=>"t",	"ი"=>"i",
		"კ"=>"k",	"ლ"=>"l",	"მ"=>"m",	"ნ"=>"n",	"�?"=>"o",	"პ"=>"p",	"ჟ"=>"dj",	"რ"=>"r",	"ს"=>"s",
		"ტ"=>"t",	"უ"=>"u",	"ფ"=>"f",	"ქ"=>"q",	"ღ"=>"gh",	"ყ"=>"y",	"შ"=>"sh",	"ჩ"=>"ch",	"ც"=>"c",
		"ძ"=>"dz",	"წ"=>"w",	"ჭ"=>"tch",	"ხ"=>"x",	"ჯ"=>"j",	"ჰ"=>"h"    	)));
	}

	public function Rus_To_Eng__LowerCased($input){  return strtr($input, array(
		"а"=>"a","�?"=>"a",		"б"=>"b","Б"=>"b",		"в"=>"v","В"=>"v",		"г"=>"g","Г"=>"g",		"д"=>"d","Д"=>"d",
		"е"=>"e","Е"=>"e",		"ё"=>"yo","�?"=>"yo",	"ж"=>"zh","Ж"=>"zh",	"з"=>"z","З"=>"z",		"и"=>"i","И"=>"i",
		"й"=>"j","Й"=>"j",		"к"=>"k","К"=>"k",		"л"=>"l","Л"=>"l",		"м"=>"m","М"=>"m",		"н"=>"n","�?"=>"n",
		"о"=>"o","О"=>"o",		"п"=>"p","П"=>"p",		"р"=>"r","Р"=>"r",		"�?"=>"s","С"=>"s",		"т"=>"t","Т"=>"t",
		"у"=>"u","У"=>"u",		"ф"=>"f","Ф"=>"f",		"х"=>"kh","Х"=>"kh",	"ц"=>"ts","Ц"=>"ts",	"ч"=>"ch","Ч"=>"ch",
		"ш"=>"sh","Ш"=>"sh",	"щ"=>"sch","Щ"=>"sch",	"ъ"=>"","Ъ"=>"",		"ы"=>"y","Ы"=>"y", 		"ь"=>"","Ь"=>"",
		"�?"=>"e","Э"=>"e",		"ю"=>"yu","Ю"=>"yu",	"�?"=>"ya","Я"=>"ya",    ));
	}
	public function ic1251_to_utf8($s){
		$s= str_replace('С?',$a1='fgr43443443',$s);
		$s= str_replace('Р?',$a2='tg5gh45h3hg3',$s);
		$s= str_replace('пїЅпїЅ?',$a3='fgr35gh35hg3gdfw',$s);
		$s= str_replace('СЊС?',$a4='XXX83rhf423888df8d23d1',$s);
		$s= str_replace('бѓ?',$a5='XXX83rhf423888df8d23d2',$s);
		$s= mb_convert_encoding($s, "windows-1251", "utf-8");
		$s= str_replace($a5,'ი',$s);
		$s= str_replace($a3,'ი',$s);
		$s= str_replace($a1,'ш',$s);
		$s= str_replace($a2,'И',$s);
		$s= str_replace($a4,'шь',$s);
		return $s;
	}

	public function INCORRECT_GEO_to_ENG($input){  return strtr($input, array(
		"áƒ�?"=>"a", "áƒ‘"=>"b", "áƒ’"=>"g",  "áƒ“"=>"d",  "áƒ�?"=>"e",  "áƒ•"=>"v",  "áƒ–"=>"z",  "áƒ—"=>"T",  "áƒ˜"=>"i",  "áƒ™"=>"k", "áƒš"=>"l",  "áƒ›"=>"m",  "áƒœ"=>"n",  "áƒ�?"=>"o", "áƒž"=>"p",  "áƒŸ"=>"J",  "áƒ "=>"r",  "áƒ¡"=>"s",    "áƒ¢"=>"t",  "áƒ£"=>"u",  "áƒ¤"=>"f",  "áƒ¥"=>"q",  "áƒ¦"=>"R",  "áƒ§"=>"y",  "áƒ¨"=>"S",  "áƒ©"=>"C",  "áƒª"=>"c",  "áƒ«"=>"Z",  "áƒ¬"=>"w",  "áƒ­"=>"W",  "áƒ®"=>"x",  "áƒ¯"=>"j",  "áƒ°"=>"h"   ));
	}
	
	// # language specifics
	
	
	public function validate_post_id($id)	 { if (!is_numeric($id) || strlen($id)>7) 								{die("incorrrrrect_postid error81"); }}
	public function validate_simple_word_of_s_GET($text){if (preg_match('/[\<\>\'\=\$\"\?\(\{]/si',$text))			{die("incorrrrrect error86");}}
	//
	// Validation
	public function validate_url($url)	{ return filter_var($url, FILTER_VALIDATE_URL) !== false && (preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i",$url)); }  
	public function validate_email($email)	{
		if(empty($email))  return false;
		$regex = '/'. ($name='([a-z0-9_.-]+)').  ($at='@').  ($sub_domain='([a-z0-9.-]+){2,255}') . ($period='.').  ($ext='([a-z]+){2,10}'). '/i';
		return empty(preg_replace($regex, '', $email) );
	}
	// ########## Sanitization ##########
	// https://php.net/manual/en/filter.filters.sanitize.php
	
	public static function sanitize($key){ return preg_replace( '/[^a-zA-Z0-9_\-]/', "_", trim($key) ); }
	public static function sanitize_key($key, $use_dash=false ){ return preg_replace( '/[^a-z0-9_\-]/', ($use_dash===true ? "_": (is_string($use_dash) ? $use_dash: "") ), strtolower(trim($key) )); }  //same as wp
	public static function sanitize_key_($key){ return self::sanitize_key_dashed($key); }
	public static function sanitize_key_dashed($str){ return self::str_replace_recursive('__', '_',  self::sanitize($str, true) ); }
	public static function sanitize_text($str,$use_dash=false) { return preg_replace("/[^a-zA-Z0-9\!\@\#\$\%\^\&\*\(\)\-\_\+\=\,\.\/\?\;\[\]\{\}\|\s]+/", ($use_dash ? "_":""), trim($str)); }	 //  \= \/ 
	//Try this to remove everything except a-z, A-Z and 0-9, -, _, .
	public static function sanitize_nonoword($text)		{ return preg_replace('/\W/si','',$text); }   
	public static function sanitize_alhpabet($key){ return preg_replace( '/[^a-zA-Z]/',"", $key); }

	public static function sanitize_text_entities($str,$use_dash=false){ return self::sanitize_text(htmlentities($str,$use_dash)); }	
	public static function sanitize_text_filter($string) { return filter_var($string,FILTER_SANITIZE_STRING);}
			// other versions
			// [^a-zA-Z0-9\-\_\.]
			// return strtr($input, [ " "=>"-",	"."=>"--",	":"=>"--",	","=>"-",	"/"=>"-",	";"=>"--",	"—"=>"",	"–"=>"-" ]);
			// str_replace(array(' ','-',',','.','/','\\','|','!','@','#','$','%','^','&','*','(',')'),'_',   strip_tags( trim($str) ));
			// preg_replace('/[^\w\d_\-]/', '',  filter_var($input,	FILTER_SANITIZE_STRING)	);
	public static function sanitize_digits($string){ return filter_var($string,FILTER_SANITIZE_NUMBER_INT);}
	public static function sanitize_url($string) { return filter_var($string,FILTER_SANITIZE_URL);}
	public static function sanitize_symbol($str)	{ return str_replace(array('/','\\','|','!','*'), '_',   strip_tags( strtoupper(trim($str) )) ) ; } 
	public static function sanitize_url_dots($url)	{ return self::slash_normalize_url(str_replace('/..','', str_replace('\\','', $url) ) ); }

	public static function sanitize_unicode($text, $replace_with=''){ 
		$x= preg_replace('/[\x00-\x1F\x7F-\xFF]/', $replace_with, $text); 
		return preg_replace('/['.$replace_with.']+/', $replace_with, $x);
	}

	public static function sanitize_text_field($text)
	{
		if(function_exists('sanitize_text_field'))
			return sanitize_text_field($text);
		else
			return self::sanitize_text($text);
	}
	public static function sanitize_text_field_recursive($data)
	{
		if ( empty($data) ) {
			return $data;
		}
		if ( is_array( $data ) ) {
			foreach ( $data as $key => $value ) {
				if ( is_array( $value ) ) {
					$value = self::sanitize_text_field_recursive($value);
				} else {
				   $value = stripslashes(sanitize_text_field($value));
				}
				$data[$key] = $value;
			}
			return $data;
		}
		return sanitize_text_field($data);
	}

	public static function sanitize_comma_array($string, $type="key")
	{
		$values = explode(',', self::sanitize_text_field($string));
		$sanitized_values = $values;
		$sanitized_values = array_map('sanitize_key', $sanitized_values);
		$sanitized_values = array_map('trim', $sanitized_values);
		$sanitized_text = implode(',', $sanitized_values);
		return $sanitized_text;
	}
	public static function remove_new_lines($text)
	{
		return str_replace(["\r","\n"],'', $text);
	}
	//
	public static function remove_whitespaces($input, $oneSpace=true){ 
		$what = $oneSpace ? ' ':'';
		$input= str_replace("   ",		$what,$input );
		$input= str_replace("  ",		$what,$input );
		$input= str_replace("\t\t",		$what,$input );
		$input= str_replace("\t",		$what,$input );
		$input= str_replace("\r\n\r\n",	$what,$input );
		$input= str_replace("\r\n ",	$what,$input );
		if (!$oneSpace){
			$input= str_replace(" ",	$what,$input );
		}
		return $input;
	}
	
	public static function str_replace_recursive($value, $replace, $string) {
		$string = str_replace($value, $replace, $string);
		if (strpos($string, $value)!==false) {
			$string= self::str_replace_recursive($value, $replace, $string);
		}
		return $string;
	}

	// remove dots (.) and "after plus part" (+xxxx) from gmail address
	public function sanitize_gmail($user_mail)
	{
		$sanitized_email = preg_replace_callback( '/(.*)\@/si', 
			function($matches){return str_replace('.','',$matches[0]); },  
			preg_replace( '/\+.*\@/s', '@', $user_mail )
		);
		return $sanitized_email;
	}

	public function sanitize_utf8_filenamee($input){
		$filename_sanitized = $this->GEO_to_ENG__LowerCased($input);
		$filename_sanitized = $this->Rus_To_Eng__LowerCased($filename_sanitized);
		$filename_sanitized = str_replace(' ','-',$filename_sanitized);
		$filename_sanitized = utf8_encode($filename_sanitized);
		return $filename_sanitized;
	}
	public function remove_html_parts($content)
	{
		$content = preg_replace('/(.*)\<body\>/si','',$content);
		$content = preg_replace('/<script(.*?)script\>/si','',$content);
		$content = preg_replace('/<iframe(.*?)iframe\>/si','',$content);
		$content = preg_replace('/\<\/body(.*)/si','',$content);
		return trim($content);
	}
	
	public function ensure($value, $array){
		if (in_array($value, $array))
			return $value;
		else {
			$msg =  "Provided value was not in array. Value: " . $this->var_dump($value) . "\r\n<br/>Array:".$this->var_dump($array) ;
			throw new \Exception( $msg );
		}
	}
	
	
	public function decode_encoded_utf8($string){
		return preg_replace_callback('#\\\\u([0-9a-f]{4})#ism', function($matches) { return mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UCS-2BE"); }, $string);
	}

	// directory correction
	public function directory_canonicalize3($address)
	{
		$address = explode('/', $address);
		$keys = array_keys($address, '..');

		foreach($keys AS $keypos => $key)
		{
			array_splice($address, $key - ($keypos * 2 + 1), 2);
		}

		$address = implode('/', $address);
		$address = str_replace('./', '', $address);

		return $address;
	}

	public function directory_canonicalize2($address)
	{
		$address =preg_replace_callback(
			'/(.*?|)\/(.*?)(\/..*?)\b/i',  
			function ($matches){
				if(!empty($matches[3])){
					return ($matches[3]);
				}
				return $matches[0];
			},
			$address
		);
		return $address;
	}

	// copied from: https://developer.wordpress.org/reference/functions/map_deep/
	public static function map_deep( $value, $callback ) {
		if ( is_array( $value ) ) {
			foreach ( $value as $index => $item ) {
				$value[ $index ] = self::map_deep( $item, $callback );
			}
		} elseif ( is_object( $value ) ) {
			$object_vars = get_object_vars( $value );
			foreach ( $object_vars as $property_name => $property_value ) {
				$value->$property_name = self::map_deep( $property_value, $callback );
			}
		} else {
			$value = call_user_func( $callback, $value );
		}
		return $value;
	}

	public function remove_query_from_url($url, $which_argument=false){ 
		return preg_replace( '/'.  (  $which_argument ? '(\&|)'.$which_argument.'(\=(.*?)((?=&(?!amp\;))|$)|(.*?)\b)' : '(\?.*)').'/i' , '', $url);  
	}
	
	public function add_to_query($url, $key, $value){
		$pair = urlencode($key).'='.urlencode($value);
		return ($this->contains($url,'?') ? $url."&$pair" :  $url."?$pair");
	}

	// copied from: https://developer.wordpress.org/reference/functions/add_query_arg/
	public static function add_query_arg( ...$args ) {
		 if(is_array($args[0])){if(count($args)<2||false===$args[1]){$uri=$_SERVER['REQUEST_URI'];}else{$uri=$args[1];}}else{if(count($args)<3||false===$args[2]){$uri=$_SERVER['REQUEST_URI'];}else{$uri=$args[2];}}$frag=strstr($uri,'#');if($frag){$uri=substr($uri,0,-strlen($frag));}else{$frag='';}if(0===stripos($uri,'http://')){$protocol='http://';$uri=substr($uri,7);}elseif(0===stripos($uri,'https://')){$protocol='https://';$uri=substr($uri,8);}else{$protocol='';}if(strpos($uri,'?')!==false){list($base,$query)=explode('?',$uri,2);$base.='?';}elseif($protocol||strpos($uri,'=')===false){$base=$uri.'?';$query='';}else{$base='';$query=$uri;}wp_parse_str($query,$qs);$qs=self::map_deep($qs, 'urlencode');if(is_array($args[0])){foreach($args[0]as $k=>$v){$qs[$k]=$v;}}else{$qs[$args[0]]=$args[1];}foreach($qs as $k=>$v){if(false===$v){unset($qs[$k]);}}$ret=build_query($qs);$ret=trim($ret,'?');$ret=preg_replace('#=(&|$)#','$1',$ret);$ret=$protocol.$base.$ret.$frag;$ret=rtrim($ret,'?');$ret=str_replace('?#','#',$ret);return $ret; 
	}
	public static function remove_query_arg( $keyOrArray, $query = false ) {
		if ( is_array( $key ) ) { // Removing multiple keys.
			foreach ( $key as $k ) {
				$query = self::add_query_arg( $k, false, $query );
			}
			return $query;
		}
		return self::add_query_arg( $key, false, $query );
	}

	public function remove_parameter_from_url($full_url, $param_name){
		return $final = preg_replace('/(\&|\?)'.$param_name.'(\=(.*?(&|#)|.*)|)/i', (!empty('$4') ? '$4' : ''), $full_url);
	}

	public function get_query_from_url($url){ 
		$query= $this->array_value(parse_url($url), 'query','');
		parse_str($query, $output);
		return $output;
	}
	
	public function checked_if_value($array, $key){
		return ( $this->array_value($array, $key)  ? ' checked="checked"' : '');
    }
	public static function if_checked($value){
		return ( $value ? ' checked="checked"' : '');
    }

	public static function is_color_string($color){
		return preg_match('/^#([a-f0-9]{3}){1,2}$/i', $color);
	}
	// ##################################

	
	// Output decimals better, i.e.  $x= 0.000021;  or  $x= 123424235.325434645
	// method 1
	public static function remove_zero_from_end($input){
		return floatval($input);//

		// Method 2 : 
		// while( ($last=substr($input,-1))=="0" || $last=="." )
		// 	$input= substr($input,0,-1);
		// return $input;

		// Method 3 :
		// return strpos($nbr,'.')!==false ? rtrim(rtrim($nbr,'0'),'.') : $nbr;
	}
	public function add_zero_in_front($num, $maxLength){
		$finalNum = $num;
		while( strlen($finalNum)<$maxLength ){
			$finalNum = "0".$finalNum;
		}
		return $finalNum;
	}
	public function trim_zero_dot($input){
		$sanitized=rtrim( $input, "0");
		if(substr($sanitized, -1) =="."){
			$sanitized=substr($sanitized,0, -1);
		}
		return $sanitized;
	}

	public function double_normal($input, $round_to=15, $use_sprintf=true){ 
		return (!is_float($input) && !is_numeric($input) ? $input : (float) $this->trim_zero_dot( $use_sprintf ? sprintf("%.{$round_to}f", $input) : self::number_format($input, $round_to) ) );	
	}
	public static function number_format($input, $decimals=15, $method=1){ 
		if ($method===1)
			$ret = number_format($input, $decimals, null, ''); 
		else 
			$ret =sprintf("%.{$decimals}f", $input); 
		return self::remove_zero_from_end($ret);
	}
	// method 2
	public function decimal_outputer($input, $length=5, $only_dot=false){  
		$timeParts = explode('.', $input);
		if(count($timeParts)<=1) return $input;
		return ($only_dot ? '' : $timeParts[0] . '.') . substr($timeParts[1], 0, $length); //sprintf('%.10F',$input); 
	}
	//
	public function double_normal_array($array){
		return $this->array_map_deep([$this,'double_normal'], $array);
	}


	//WP immitations
	public function add_filterX($a=null,$b=null,$c=null,$d=null)	{if(function_exists('add_filter')) 		return add_filter($a,$b,$c,$d);  	}
	public function add_actionX($a=null,$b=null,$c=null,$d=null)	{if(function_exists('add_action')) 		return add_action($a,$b,$c,$d);  	}
	public function add_shortcodeX($a=null,$b=null,$c=null,$d=null)	{if(function_exists('add_shortcode'))	return add_shortcode($a,$b,$c,$d);  }
	
	public function cut__my($text, $chars, $points = "...") {  $text = strip_tags($text);	if( strlen($text) <= $chars) { return $text;} else { return mb_strimwidth($text,0,$chars, $points,'utf-8'); } }
	public function trim_string($text, $chars, $points = "...") {  if( strlen($text) <= $chars) { return $text;} else { return mb_strimwidth($text,0, $chars, $points,'utf-8'); } }

	public function truncate($string, $width, $unicode = true){
		if (mb_str_word_count($string) > $width) {
			$string= preg_replace('/((\w+\W*|| [\p{L}]+\W*){'.($width-1).'}(\w+))(.*)/', '${1}', $string);
		}
		return $string;
	}
	
	
	public function customm_word_length_sentence($got_content,$words_length,$StripOrNot=true, $preserved=''){
		$got_content = trim($got_content); 			//https://php.net/manual/en/function.trim.php
		//$got_content = strip_shortcodes($got_content); //https://stackoverflow.com/a/20403438/2165415
		$got_content = str_replace(']]>', ']]>', $got_content);
		$got_content= str_replace("\n",' ',$got_content);
		$got_content= str_replace("\r",' ',$got_content);
		$got_content = !$StripOrNot ? $got_content : strip_tags($got_content,$preserved) ;
		$words = explode(' ', $got_content, $words_length + 1);
		if(count($words) > $words_length) :
			array_pop($words);
			array_push($words, '…');
			$got_content = implode(' ', $words);
		endif;
		return $got_content;	
	}

	public function get_first_words($sentence , $desired_words_amount=5){
		$all_words = explode(' ', $sentence);  $words_amount = count($all_words);  $words_index_amount=$words_amount-1;
		$out = '';
		if ($words_amount > $desired_words_amount) {
			for($i = 0; $i< $desired_words_amount; $i++) {
				if(array_key_exists( $i,$all_words)){
					$out = $out.' '.$all_words[$i];
				}
			}
		}
		else {$out = $sentence;  }
		return strip_tags($out);
	}

	public function get_last_words($sentence , $desired_words_amount=5){
		$all_words = explode(' ', $sentence);  $words_amount = count($all_words);  $words_index_amount=$words_amount-1;
		$out = '';
		if ($words_amount > $desired_words_amount) {
			for($i = 0; $i< $desired_words_amount; $i++) {
				if(array_key_exists( ($words_index_amount-$i),$all_words)){
					$out = $all_words[($words_index_amount-$i)].' '.$out;
				}
			}
		}
		else {$out = $sentence;  }
		return strip_tags($out);
	}

	public function unicode_words_count($string) {	preg_match_all('/[\pL\pN\pPd]+/u', $string, $matches);	return count($matches[0]);}
	public function text_splitt($msg, $word_numbs) {
		$msg = preg_replace('/[\r\n]+/', ' ', $msg);
		$chunks = wordwrap($msg, $word_numbs*20 , '\n', true);
		return explode('\n', $chunks);
	}
	
	public function trim_to_charlength($text, $charlength) {
		$charlength++;
		if ( mb_strlen( $text ) > $charlength ) {
			$subex = mb_substr( $text, 0, $charlength - 5 );
			$exwords = explode( ' ', $subex );
			$excut = - ( mb_strlen( $exwords[ count( $exwords ) - 1 ] ) );
			if ( $excut < 0 ) {
				$text = mb_substr( $subex, 0, $excut );
			} else {
				$text = $subex;
			}
			$text .= '...';
		}
		return $text;
	}

	
	// ### substr shorthands:
	public function remove_chars_from_start_end($word, $removeFromStart, $removeFromEnd) {
		return substr($word, $removeFromStart, -$removeFromEnd);
	}
	public static function chars_from_start($word, $amount)
	{
		return substr($word, 0, $amount);
	}
	public function chars_from_end($word, $amount)
	{
		return substr($word, -$amount);
	}
	public function remove_if_starts_with($haystack, $needle) { return (!$this->starts_with($haystack, $needle) ? $haystack : substr($haystack, strlen($needle) ) ); }
	public function remove_if_ends_with($haystack, $needle) { return (!$this->ends_with($haystack, $needle) ? $haystack : substr($haystack, 0, -1 * strlen($needle) ) ); }

	// makes a string from an assiciative array
	public function implode_assoc($glue,$arr) 
	{ 
		$keys=array_keys($arr); 
		$values=array_values($arr);
		return(implode($glue,$keys).$glue.implode($glue,$values)); 
	}

	public function url_correction_for_html_output($content){ 
		return preg_replace_callback( 
			'/\<(img|link|iframe|frame|frameset|script|embed|video|audio)([^>]*)/si', 
			function($matches) { return '<'.$matches[1].preg_replace('/=(\"|\')(http(s|):)/si','=$1', $matches[2]);	}, 
			$content
		);
	}
	
	public function array_max_pair($array){
		$peakVal = -999999999999;
		$peakPair = [];
		foreach($array as $key=>$value){
			if ($value>=$peakVal){
				$peakVal=$value;
				$peakPair=[$key,$value];
			}
		}
		return $peakPair;
	}
	public function array_min_pair($array){
		$peakVal = +999999999999;
		$peakPair=[];
		foreach($array as $key=>$value){
			if ($value<=$peakVal){
				$peakVal=$value;
				$peakPair=[$key,$value];
			}
		}
		return $peakPair;
	}
	
	public function equals_string($content, $target){ 
		return strtolower($content)===strtolower($target);
	}

	public static function contains($content, $needle, $case_sens=true, $position='any'){ 
		if ($position==='start'){
			return self::starts_with($content, $needle, $case_sens);
		}
		elseif ($position==='end'){
			return self::ends_with($content, $needle, $case_sens);
		}
		else{
			return ($case_sens ? strpos($content, $needle)!==false : stripos($content, $needle)) !== false;
		}
	}

	public function contains_from_array($content, $needles_array, $case_sens= true, $position='any'){   
		foreach($needles_array as $needle){
			if ($this->contains($content, $needle, $case_sens, $position) ){
				return true;
			}
		}
		return false;
	}

	// https://stackoverflow.com/a/860509/2377343
	public static function starts_with($haystack, $needle, $case_sens=true) {
		return $needle === "" || 
		( 
			( $case_sens && strpos($haystack, $needle, 0) === 0 )
				||
    		( !$case_sens && stripos($haystack, $needle, 0) === 0 )
		)
		; 
	}
	public static function starts_with_from_array($haystack, $needles_array, $case_sens=true) { 
		foreach($needles_array as $needle){
			if (self::starts_with($haystack, $needle, $case_sens))
				return true;
		}
		return false;
	}
	public static function ends_with($haystack, $needle, $case_sens=true) { 
		$expectedPosition = strlen($haystack) - strlen($needle);
		if ($case_sens)
			return strrpos($haystack, $needle, 0) === $expectedPosition;
		return strripos($haystack, $needle, 0) === $expectedPosition;
	}
	public static function ends_with_from_array($haystack, $needles_array, $case_sens=true) { 
		foreach($needles_array as $needle){
			if (self::ends_with($haystack, $needle,$case_sens))
				return true;
		}
		return false;
	}


	public function die_if_not_this_site_visitor(){ //if half day passed
		if (empty($_COOKIE['ytdow___']) || $_COOKIE['ytdow___'] > time()*3 + 43200 ) {die('incorrect_download_<b>123</b>.<script type="text/javascript">top.window.location = "http://'.$_SERVER['HTTP_HOST'].'";</script>');}
	}

	public function js_redirect($url=false, $echo=true){
		$str = '<script>location.href = "'. ( $url ?: $_SERVER['REQUEST_URI'] ) .'"; document.body.style.opacity=0; </script>';
		if($echo) { exit($str); }  else { return $str; }
	}

	public function php_redirect($url=false, $code=302){
		//avoid redirection from customizer: if (!empty($_COOKIE['MLSS_cstRedirect']) || defined('MLSS_cstRedirect')) {return;}
		header("Cache-Control: no-store, no-cache, must-revalidate"); header("Expires: Thu, 01 Jan 1970 00:00:00 GMT");   
		header("location: ". ( $url ?: $_SERVER['REQUEST_URI'] ), true, $code); exit;
	}
	public function redirect($url=false, $code=302){
		return $this->php_redirect($url,$code);
	}
	public function js_redirect_message($message, $url=false){
		echo '<script>alert(\''. $this::sanitize_text($message).'\');</script>';
		$this->js_redirect($url);
	}
				
	public function get_output(callable $funct, $clear=true){  
		ob_start();
		$res = call_user_func($funct);
		if ($clear)
		{
			$cont= ob_get_clean(); 
			ob_flush(); 
		}
		else{
			$cont= ob_get_contents(); 
		}
		// $cont= ob_get_contents();
		//ob_get_clean();
		return $cont; 
	}	
	//output js header 
	public function get_js_header_output(){  

        header("Pragma: public");
        header("Cache-Control: public, maxage=".$expires);
		header("Content-type: application/javascript;  charset=utf-8");
        header('Expires: ' . gmdate('D, d M Y H:i:s', time()+$expires) . ' GMT');
	}
	public static function swap_pair($pairname, $divisor='/'){
		$arr= explode($divisor, $pairname);
		return (count($arr)<=0 ? $pairname : $arr[1].$divisor.$arr[0]);
	}
	
	



	#region ### TELEGRAM FUNCTIONS ###
	public function telegram_message($text, $chat_id, $bot_key, $extra_opts = []){
		$is_repeated_call = array_key_exists('is_repeated_call', $extra_opts);
		if (!array_key_exists('parse_mode', $extra_opts)){
			$extra_opts['parse_mode'] = 'html';
		}
		if (!array_key_exists('disable_web_page_preview', $extra_opts)){
			$extra_opts['disable_web_page_preview'] = true;
		}
		// whether it's without `-100` prefix
		$chat_id = strval($chat_id);
		if (!$this->starts_with($chat_id, '-100')) $chat_id = '-100' . $chat_id;
		$text = $this->br2nl($text);
		$text = strip_tags($text,'<b><strong><i><em><u><ins><s><strike><del><a><code><pre>'); // allowed: https://core.telegram.org/bots/api#html-style
		$text = substr($text,0,4095); //max telegram message length 4096
		$requestOpts = array_merge(['chat_id'=>$chat_id, 'text'=>$text], $extra_opts);
		unset($requestOpts['is_repeated_call']);
		$responseText = $this->get_remote_data( ['url'=>'https://api.telegram.org/bot'.$bot_key.'/sendMessage', 'post'=> $requestOpts]);  // pastebin_com/u0J1Cph3  //'sendMessage?'.http_build_query($opts, '');
		try {
			$responseJson = json_decode($responseText);
			// if it was successfull
			if ($responseJson->ok)
			{
				return $responseJson;
			}
			// for some reason, if still unsupported format submitted, resubmit the plain format
			//i.e. {"ok":false,"error_code":400,"description":"Bad Request: can't parse entities: Unsupported start tag \"br/\" at byte offset 43"}
			else{
				if (stripos($responseJson->description, 'Bad Request: can\'t parse entities') !==false){
					if (! $is_repeated_call){
						$text = "[SecondSend with stipped tags] \r\n". strip_tags($text) ;
						$extra_opts['is_repeated_call'] = true;
						return $this->telegram_message($text, $chat_id, $bot_key, $extra_opts);
					}
				}
				return $responseJson;
			}
		} catch (Exception $ex) {
			return ['ok'=>false, 'description'=> $ex->getMessage() . ':::' . $responseText];
		}
	}

	public $telegram_interval_ms = 50; // telegram seems to accept around 30 times per second, so we'd better wait around that milliseconds
	private $telegram_last_time=0;

	public function telegram_message_cached($array, $botid){
		$curMS  = $this->timeMS();
		$goneMS = $curMS - $this->telegram_last_time;
		if ( $goneMS < $this->telegram_interval_ms ){
			$this->sleep( $this->telegram_interval_ms - $goneMS);
		}
		$this->telegram_last_time = $curMS;

		$key = $this->cache_key_create(array_merge($array, [$botid]));
		if ( ! $this->is_cached_id('function__telegram_message_cached', $key) ){
			$res= $this->telegram_message($array, $botid);
			$ok='true';
		}
		else {
			$res= (object)( ["ok"=>true, "success"=>false, 'reason'=>"$key was cached", 'content'=> json_encode($array) ] );
			$ok='false';
		}
		if(is_callable([$this,'notifications_db_entry'])) 
			$this->notifications_db_entry($key, $array['chat_id'], $this->stringify($res), time(), $ok );
		return $res;
	}

	public function telegram_message_cached_with_channel($array, $botid){
		$answer = $this->telegram_message_cached ( $array, $botid );
		$this->telegram_channel_name_save($answer);
		return $answer;
	}

	public function telegram_channel_name_save($response){
		$existing = $this->telegram_channel_name_get();
        $res = $response; //already decoded
		if ( $this->array_value($res,'ok') )
		{
			// check to ensure (because cached ids dont have result)
			if ( $this->array_value($res,'result') )
			{
				$id    = $res->result->chat->id;
				$title = $res->result->chat->title;
				$type  = $res->result->chat->type;  //group or channel
				$existing[$id] = (object)['title'=>$title, 'type'=>$type];
				update_option('telegram_channel_names_temp', $existing);
			}
		}
	} 
	public function telegram_channel_name_get($id=''){
		$channelsArray = get_option('telegram_channel_names_temp',[]);
		return !empty($id) ? $this->array_value($channelsArray, $id) : $channelsArray;
	}
	#endregion


	// ################
	// https://github.com/ttodua/useful-php-scripts/blob/master/get-remote-url-content-data.php 
	public static function get_remote_data($url, $post_params=null, $request_options=null)	
	{
		return self::fetch($url, $post_params, $request_options);
	}
	public static function fetch($url, $post_params=null, $request_options=null)	
	{
		$func = $post_params ? "wp_remote_post" : "wp_remote_get";
		$is_wp = (function_exists($func));
		$request_options = !empty($request_options)? $request_options : [];

		if (!$is_wp)
		{
			return self::get_remote_data_callback($url, $post_params, $request_options);
		}
		else
		{ 
			if($func=="wp_remote_get") {
				$out= wp_remote_get($url, $request_options );
			}
			if($func=="wp_remote_post") {
				$post_array = (is_array($post_params)) ? $post_params : (parse_str($post_params , $new) ? $new : $new );		
				$args['body']=$post_array;
				$args= array_merge($args, $request_options);
				$out= wp_remote_post($url, $args );
				//$out= call_user_func($func, $url, $args );
			}
			return wp_remote_retrieve_body($out); //same as $out['body'] 
		}
		return "empty_data. Create your own remote function";
	}

	public function get_remote_data_array($arr, $force_curl=false, $repeated_call=false)
	{
		if (is_array($arr))
		{
			$url 			= $arr['url'];
			$post_params	= $this->array_value($arr,'post',    null);
			$request_options= $this->array_value($arr,'options', []);	if ($request_options==null) $request_options=[];
			$should_be_json = $this->array_value($arr,'json',    false);
			$retry			= $this->array_value($arr,'retry',   true);
		}
		else{
			$url 			= $arr;
			$post_params	= null;
			$request_options=[];
			$should_be_json = true;
			$retry			= true;
		}

        $request_options = array_merge_recursive($request_options, ['headers'=>['Cache-Control'=>'no-cache']] );

		$data = $this->get_remote_data($url, $post_params, $request_options, $force_curl) ;
		if (empty($data))
		{
			$res= (object)['error'=>'empty data', 'response'=>''];
		}
		else{
			if ($should_be_json)
			{
				$dataTemp = $this->JsonData($data);
				if (is_null($dataTemp))
					$res= (object)['error'=>'not json', 'response'=>($data) ];
				else
					$res= (object)['error'=>false, 'response'=>json_decode($data) ];
				/*
					try { 
						$res= (object)['error'=>false, 'response'=>json_decode($data) ];
					}
					catch(\Exception $ex){
						$res= (object)['error'=>'not json', 'response'=>($data) ];
					}
				*/
			}
			else{
				$res= (object)['error'=>false, 'response'=>$data ];
			}
		}
		// if still error, and retry allowed
		if ($res->error && $retry && !$repeated_call)
		{
			$this->sleep(100);
			$res = $this->get_remote_data_array($arr, $force_curl, $repeated_call=true);
		}
		return $res;
	}

	 


    //i.e. set_cookies_from_url("http://example.com/?username=user&auth=key');
    public function set_cookies_from_url($url)
    {
        $d=$this->get_remote_data($url, false, ["curl_opts"=>["CURLOPT_HEADERFUNCTION"=>
            ( function ($ch, $headerLine) {
                if (preg_match('/^Set-Cookie:\s*([^;]*)/mi', $headerLine, $cookieArr) == 1)
                {
                    $cookie = $cookieArr[1];
                    $cookie_vars = explode('=', $cookie, 2);
                    $this->example_cookies[$cookie_vars[0]] = $cookie_vars[1];
                }
                return strlen($headerLine); // Needed by curl
                }
            )
            ]]
        );
        foreach($this->example_cookies as $key=>$name)
        {
            $this->set_cookie($key,$name, 86000, '/target_dir/');
        }
        $this->set_cookie("sample_confirm","1");
    }
   
	// ----
	public function get_client_ip() {
		$proxy_headers = array("CLIENT_IP", "FORWARDED", "FORWARDED_FOR", "FORWARDED_FOR_IP", "HTTP_CLIENT_IP", "HTTP_FORWARDED", "HTTP_FORWARDED_FOR", "HTTP_FORWARDED_FOR_IP", "HTTP_PC_REMOTE_ADDR", "HTTP_PROXY_CONNECTION", "HTTP_VIA", "HTTP_X_FORWARDED", "HTTP_X_FORWARDED_FOR", "HTTP_X_FORWARDED_FOR_IP", "HTTP_X_IMFORWARDS", "HTTP_XROXY_CONNECTION", "VIA", "X_FORWARDED", "X_FORWARDED_FOR");
		foreach($proxy_headers as $proxy_header) {
			if (isset($_SERVER[$proxy_header])) {
				if(preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $_SERVER[$proxy_header])) {
					return $_SERVER[$proxy_header];
				}
				else if (stristr(",", $_SERVER[$proxy_header]) !== FALSE) {
					$proxy_header_temp = trim(array_shift(explode(",", $_SERVER[$proxy_header])));
					if (($pos_temp = stripos($proxy_header_temp, ":")) !== FALSE) {$proxy_header_temp = substr($proxy_header_temp, 0, $pos_temp); }
					if (preg_match("/^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/", $proxy_header_temp)) { return $proxy_header_temp; }
				}
			}
		}
		return $_SERVER["REMOTE_ADDR"];
	}

	
	// $ipinfo = json_decode(get_ip_info($_SERVER['REMOTE_ADDR']), true);
	// if($ipinfo['country_name'] != 'Georgia'){
	public function get_ip_info($ip, $type=1, $api=""){
		$info="";
		if($type==1){
			$info = $this->get_remote_data('https://geoip-db.com/json/'.$ip);	
			//"country_code":"GE", "country_name":"Georgia", "city":"null", "postal":null, "latitude":42, "longitude":43.5, "IPv4":"xxx.xxx.xxx.xxx", "state":"null"
		}
		elseif($type==2){
			// PLEASE DONT USE THIS API
			$info_initial = $this->get_remote_data('https://geoipify.whoisxmlapi.com/api/v1?apiKey='.$api.'&ipAddress='.$ip);	
			// {"ip":"xxx.xxx.xxx.xxx","location":{"country":"AU","region":"Victoria","city":"Research","lat":-37.7,"lng":145.1833,"postalCode":"3095","timezone":"Australia\/Melbourne"}}
			$decoded = json_decode($info_initial, true);
			$loc =$decoded['location'] ;
			unset($decoded['location']) ;
			$ipinfo_new = array_merge( $decoded,$loc );
			return  $ipinfo_new;
		}
		return $info;
	}

	public function output_js_headers()
	{
		session_cache_limiter('none');
		// https://stackoverflow.com/a/1385982/2377343
											$year=60*60*24*365;//year
		//Caching with "CACHE CONTROL"
			header('Cache-control: max-age='.$year .', public');
		//Caching with "EXPIRES"  (no need of EXPIRES when CACHE-CONTROL enabled)
			//header('Expires: '.gmdate(DATE_RFC1123,time()+$year));
		//To get best cacheability, send Last-Modified header and ...
			header('Last-Modified: '.gmdate(DATE_RFC1123,filemtime(__file__)));  //i.e.  1467220550 [it's 30 june,2016]
		//reply using: status 304 (with empty body) if browser sends If-Modified-Since header.... This is cheating a bit (doesn't verify the date), but remove if you dont want to be cached forever:
			// if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {  header('HTTP/1.1 304 Not Modified');   die();	}
		header("Content-type: application/javascript;  charset=utf-8");
	}

		
	public function input_fields_from_array($value, $keyname='', $replace_spaces=false){	//$keyname= (strpos($keyname,'[') === false) ? '['.$keyname.']' : $keyname;
		echo '<div class="array_fields1"><style>.array_fields1 textarea{max-height:200px!important;  border-radius: 5px; width:100%; color:#53ae14; border: 2px solid black; margin:0 0 0 0px; height:50px; }  .def_textareaa{height:70px;} .high_textarea{height:130px;} .new_block{MARGIN:0 0 0 50px; border:2px solid; border-width:0 0 0 2px;} .txtar{padding:0 0 0 25px;}  .new_block .keyname{color:rgb(248, 48, 83);} </style>';
		$this->input_fields_from_array_recursive($value, $keyname, $replace_spaces);
		echo '</div>';
	}
	public function input_fields_from_array_recursive($value, $keyname='', $replace_spaces=false){		
		if (!is_array($value)){
			$height=30; $lines=explode("\r\n",$value); 
				foreach($lines as $eachLINE){
					$height= $height+ceil(mb_strlen($eachLINE)/100) * 30; 
				}
				// replace multiple whitespaces with single
				$value =   !$replace_spaces ? $value : preg_replace('!\s+!', ' ', str_replace("\t",' ', $value));
			echo 
			'<div class="each_ln">
				<div class="keyname">'.$keyname.'</div>
				<div class="txtar"><textarea class="" style="height:'. $height.'px;" name="'.$keyname.'">'. htmlentities($value).'</textarea></div>
			</div>';
		}
		else{
			echo '<div class="new_array_title">'.$keyname.'</div>';
			foreach ($value  as $keyname1=>$value1){
				echo '<div class="new_block">';
				$this->input_fields_from_array_RECURSIVE($value1, $keyname.'['.$keyname1.']',  $replace_spaces);
				echo '</div>';
			}
		}
	}

	public function random_color($alpha='FF') {
		return '#' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT).$alpha;
	}

	public function content_height($content, $lineHeight=30){
		$lines=explode("\n", $content); 
		$height = $lineHeight;
		foreach($lines as $eachLINE){
			$height = $height+ceil(mb_strlen($eachLINE)/100) * $lineHeight; 
		} 
		return $height;
	}
	
	
	public function js_autosize_textarea($classname=null)
	{ ?><script>
		function autoSizeTextareas(className)
		{
			let tx = document.querySelectorAll(className);
			for (let i = 0; i < tx.length; i++) {
				tx[i].setAttribute('style', 'height:' + (tx[i].scrollHeight) + 'px;overflow-y:hidden;');
				var oninput = function () {
				  this.style.height = 'auto';
				  this.style.height = (this.scrollHeight) + 'px';
				};
				tx[i].addEventListener("input", oninput, false);
			}
		}
		</script> <?php
		if ($classname) { ?><script>document.addEventListener('readystatechange', event => {
			if (event.target.readyState === "interactive") {  
				autoSizeTextareas('<?php echo $classname;?>');
			}
		});</script><?php }
	}

	public function dropdown_from_array($array, $name, $selected){
		$out =
		'<select name="'.$name.'">';
			foreach($array as $each) $out .= 
		'<option value="'.$each.'"'. ( $each==$selected ?' selected ':'') . ">$each</option>";
		$out .= '</select>';
		return '<div>'.$out.'</div>';
	}

	public function loader($type="")
	{
		$circlecolor="#ffffff"; 
		$head = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(241, 242, 243); display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid">'; //< ?xml version="1.0" encoding="utf-8"? >
		
		if ($type=='infinity')
			$out = $head.'<circle cx="50" cy="50" fill="none" stroke="#292664" stroke-width="15" r="36" stroke-dasharray="169.64600329384882 58.548667764616276" transform="rotate(338.174 50 50)"> <animateTransform attributeName="transform" type="rotate" repeatCount="indefinite" dur="0.8s" values="0 50 50;360 50 50" keyTimes="0;1"></animateTransform></circle>';
		elseif ($type=='eclipse')
			$out = $head.'<path d="M10 50A40 40 0 0 0 90 50A40 49.2 0 0 1 10 50" fill="#1d3f72" stroke="none" transform="rotate(235.214 50 54.6)"><animateTransform attributeName="transform" type="rotate" dur="1s" repeatCount="indefinite" keyTimes="0;1" values="0 50 54.6;360 50 54.6"></animateTransform></path>';
		elseif ($type=='normal')
			$out = $head.'<path fill="none" stroke="#1d3f72" stroke-width="8" stroke-dasharray="42.76482137044271 42.76482137044271" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40 C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke-linecap="round" style="transform:scale(0.8);transform-origin:50px 50px"><animate attributeName="stroke-dashoffset" repeatCount="indefinite" dur="1s" keyTimes="0;1" values="0;256.58892822265625"></animate></path>';
		else //dots
			$out = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; display: block; shape-rendering: auto;" width="200px" height="200px" viewBox="0 0 130 130" preserveAspectRatio="xMidYMid">            <g><circle cx="16" cy="64" r="16" fill="'.$circlecolor.'" fill-opacity="1"/><circle cx="16" cy="64" r="16" fill="'.$circlecolor.'" fill-opacity="0.67" transform="rotate(45,64,64)"/><circle cx="16" cy="64" r="16" fill="#ffffff" fill-opacity="0.42" transform="rotate(90,64,64)"/><circle cx="16" cy="64" r="16" fill="'.$circlecolor.'" fill-opacity="0.2" transform="rotate(135,64,64)"/><circle cx="16" cy="64" r="16" fill="'.$circlecolor.'" fill-opacity="0.12" transform="rotate(180,64,64)"/><circle cx="16" cy="64" r="16" fill="'.$circlecolor.'" fill-opacity="0.12" transform="rotate(225,64,64)"/><circle cx="16" cy="64" r="16" fill="'.$circlecolor.'" fill-opacity="0.12" transform="rotate(270,64,64)"/><circle cx="16" cy="64" r="16" fill="'.$circlecolor.'" fill-opacity="0.12" transform="rotate(315,64,64)"/><animateTransform attributeName="transform" type="rotate" values="0 64 64;315 64 64;270 64 64;225 64 64;180 64 64;135 64 64;90 64 64;45 64 64" calcMode="discrete" dur="720ms" repeatCount="indefinite"></animateTransform></g></svg>';
		$out = $out.'<!-- generated by https://loading.io/ --></svg>'; 
		return $out;
	}


	public function get_user_browser(){ 
		if (empty($_SERVER['HTTP_USER_AGENT'])) $_SERVER['HTTP_USER_AGENT']="unknown";
		$b = $_SERVER['HTTP_USER_AGENT']; $final =array();

		//(START FROM MOBILE check!!!!)
		if(
			preg_match('/android.+mobile|Windows Mobile|Nokia|avantgo|Mozilla(.*?)(Android|Mobile|Blackberry|Symbian)|OperaMini|Opera Mini|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|ap|od)|iris|kindle|lge |maemo|meego.+mobile|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i',$b)
			||
			preg_match('/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i',substr($b,0,4))
			)									{	$final['brwsr'] = "Mobilee";	}
		//if typical browsers
		elseif(preg_match('/Firefox/i',$b))		{	$final['brwsr'] = "Firefox";	}
		elseif(preg_match('/Safari/i',$b))		{	$final['brwsr'] = "Safari";	}
		elseif(preg_match('/Chrome/i',$b))		{	$final['brwsr'] = "Chrome";	}
		elseif(preg_match('/Flock/i',$b))		{	$final['brwsr'] = "Flock";		}
		elseif(preg_match('/Opera/i',$b))		{	$final['brwsr'] = "Opera";		}
				elseif(preg_match('/MSIE 6/i',$b))				{$final['brwsr'] = "MSIE 6";	}
				elseif(preg_match('/MSIE 7/i',$b))				{$final['brwsr'] = "MSIE 7";	}
				elseif(preg_match('/MSIE 8/i',$b))				{$final['brwsr'] = "MSIE 8";	}
				elseif(preg_match('/MSIE 9/i',$b))				{$final['brwsr'] = "MSIE 9";	}
				elseif(preg_match('/MSIE 10/i',$b))				{$final['brwsr'] = "MSIE 10";	}
				elseif(preg_match('/Trident\/7.0; rv:11.0/',$b)){$final['brwsr'] = "MSIE 11";	}
				else											{$final['brwsr'] = "UNKNOWNNN";	}
		//===========================================================================================================
		$final['full_brwsr_namee']	 = $b;
		//other parameters
		return $final;
	}

	public function get_user_OS() { 
		if (empty($_SERVER['HTTP_USER_AGENT'])) $_SERVER['HTTP_USER_AGENT']="unknown";
		$user_agent=$_SERVER['HTTP_USER_AGENT']; $final =array(); $final['os_namee']="_Unknown_OS_";  $final['os_typee']="_Unknown_OS_";
		$os_array=array(
			'MOUSED'	=> array(
				'/windows nt 10.0/i'=>'Windows 10', '/windows nt 6.3/i'=>'Windows 8.1', '/windows nt 6.2/i'=>'Windows 8', '/windows nt 6.1/i'=>'Windows 7',	'/windows nt 6.0/i'=>'Windows Vista','/windows nt 5.2/i'=>'Windows Server 2003/XP x64', '/windows nt 5.1/i'=>'Windows XP', '/windows xp/i'=>'Windows XP','/windows nt 5.0/i'=>'Windows 2000','/windows me/i'=>'Windows ME','/win98/i'=>'Windows 98','/win95/i'=>'Windows 95','/win16/i'=>'Windows 3.11',
				'/macintosh|mac os x/i' =>'Mac OS X','/mac_powerpc/i'=>'Mac OS 9', '/linux/i'=>'Linux','/ubuntu/i'=>'Ubuntu',
								),
			'NOMOUSED'	=> array(
				'/iphone/i'=>'iPhone','/ipod/i'=>'iPod','/ipad/i'=>'iPad','/android/i'=>'Android','/blackberry/i'=>'BlackBerry', '/webos/i'=>'Mobile'
								)
		); 
		foreach($os_array as $namee=>$valuee) { foreach ($valuee as $regex => $value1) {	if(preg_match($regex, $user_agent)){$final['os_namee']=$value1;  $final['os_typee'] = $namee;}		} }
		return $final;
	}

	// https://stackoverflow.com/a/31476046/2377343 
	public function get_url_parts($url,$part){	 $x='';
		$pURL = parse_url($url);	$pthURL = pathinfo($url);		
		//for example: https://example.com/myfolder/sympony.mp3?aa=1&bb=2?cc=#gggg
		if		($part=='scheme'){ 	$x = !empty($pURL['scheme'])	?	$pURL['scheme']				:'';}	//  http
		elseif	($part=='hostname'){ 	$x = !empty($pURL['host'])		?	$pURL['host']				:'';}   //  example.com
		elseif	($part=='query'){ 		$x = !empty($pURL['query'])		?	$pURL['query']				:'';}   //  aa=1&bb=2?cc=
		elseif	($part=='hash'){ 		$x = !empty($pURL['fragment'])	?	$pURL['fragment']			:'';}   //  gggg
		elseif	($part=='file'){ 		$x = !empty($pURL['path'])		?	$pURL['path']				:'';}   //  /myfolder/sympony.mp3
		elseif	($part=='filename'){ 	$x = !empty($pURL['path'])		?	basename($pURL['path'])		:'';}   //  sympony.mp3
		elseif	($part=='extension'){	$x = !empty($pURL['path'])		?	pathinfo($pURL['path'], PATHINFO_EXTENSION) :'';}   //  mp3
		elseif	($part=='folder'){ 		$x = !empty($pURL['path'])		?	dirname($pURL['path'])		:'';}   //  /myfolder
		elseif	($part=='dirname'){ 	$x = !empty($pthURL['dirname'])	?	$pthURL['dirname']			:'';}   //  https://example.com/myfolder
		elseif	($part=='afterfolder'){	$x = !empty($pthURL['basename'])?	$pthURL['basename']			:'';}   //  sympony.mp3?aa=1&bb=2?cc=#ggg
		
		return $x;
	}

	public function urlencodeall($x) {
		$out = '';
		for ($i = 0; isset($x[$i]); $i++) {
			$c = $x[$i];
			if (!ctype_alnum($c)) $c = '%' . sprintf('%02X', ord($c));
			$out .= $c;
		}
		return $out;
	}

	// measure / timer for a function
	public function function_benchmark($callback, $cycles_amount=1, $hint=''){
		$before = microtime(true);
		$val=[];
		for ($i=0 ; $i<$cycles_amount; $i++) {
			$val[]=call_user_func($callback);
		}
		$after = microtime(true);
		echo "Time needed to execute $cycles_amount cycles:". self::number_format($after-$before, 5) . " sec\n<br/>";
		return $val;
	}

	public function json_encode_unicode($data){ return json_encode($data, JSON_UNESCAPED_UNICODE); }

	public function utf8_declarationn() { return '<meta http-equiv="content-type" content="text/html; charset=UTF-8">'; }
	public function utf8_declarationn_auto() { return '<meta http-equiv="content-type" content="'.get_bloginfo('html_type').'; charset='.get_bloginfo('charset').'">'; }


	public function default_html_declaration($lng = 'en'){
		return '<!DOCTYPE html> <html id="pagehtml" class="lang lang_'.$lng.'" xmlns:fb="https://www.facebook.com/2008/fbml" xmlns:og="https://opengraphprotocol.org/schema/" xmlns="https://www.w3.org/1999/xhtml" lang="'.$lng.'" xml:lang="'.$lng.'" >';
	}

	public function default_rss_head_tags(){ 
	?> 	<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="<?php bloginfo('rss2_url'); ?>" />
		<link rel="alternate" type="application/atom+xml" title="Atom 0.3" href="<?php bloginfo('atom_url'); ?>" />
		<link rel="alternate" type="text/xml" title="RSS .92" href="<?php bloginfo('rss_url'); ?>" />
		<link rel="pingback" href="<?php bloginfo('pingback_url'); ?>" /> <?php	
	}

	//add_actionX('wp_head','check_if_js_cookies_enabled');
	public function check_if_enabled_js(){	$out = 
		'<noscript>
			<div style="text-align:center; position:absolute;background-color:red;">Enable Javascript in your Browser to avoid BROWSER problems!</div>
		</noscript>';
		return	$out;
	}				
	public function check_if_enabled_cookies(){ $out1 = 
			'<script>
			function check_if_cookies_are_enabled(){ 
				var temp_cooK_name="__verify=1"; var dattee = new Date();dattee.setTime(dattee.getTime()+(30*1000));
				document.cookie = temp_cooK_name + ";expires=" + dattee.toUTCString();
				var supportsCOOCKIES = document.cookie.length >= 1 && document.cookie.indexOf(temp_cooK_name) > -1;
				if (supportsCOOCKIES) {document.write(\'<div style="text-align:center; position:absolute;background-color:red;">Enable cookies in your<br/> browser to avoid <br/>browser problems!</div>\');}
			}
			check_if_cookies_are_enabled();
			</script>';
		return $out1;
	}			
	
	public function old_browser_message($first=null, $incompatible_browsers=array('MSIE') ){
		global $odd;
		if (in_array($this->platforms()['brwsr'], $incompatible_browsers) ) { echo '<div style="padding:20px;text-align:center;position:fixed; top:0px;left:0px; z-idnex:99; background:red;color:black; ">Your have an INCOMPATIBLE BROWSER! Please, use any modern browser (<b><a href="https://www.firefox.com">Firefox</a>, <a href="https://www.opera.com">Opera</a>, <a href="https://www.apple.com/safari/">Safari</a> , <a href="https://www.chrome.com">Chrome</a></b>..) to view site normally. </div>'; }
	}	


	public function facebook_rescarpe_url($url){  $x= $this->get_remote_data('https://graph.facebook.com/','id='.urlencode($url).'&scrape=true'); }




	// ==================== text to image==============
	// # Usage #
	//text_to_image_my( 
	//	$text='Helloooo World!' , 
	//	$separate_line_after_chars=40,   $font='./Arial%20Unicode.ttf',    $size=24,   $rotate=0,   $padding=0,   $transparent=true,  $color=['r'=>0,'g'=>0,'b'=>0],   $bg_color=['r'=>255,'h'=>255,'b'=>255] 
	//);
	
	public function text_to_image($text, $separate_line_after_chars=40,  $font='./Arial%20Unicode.ttf', 
		$size=24,$rotate=0,$padding=2,$transparent=true, $color=array('r'=>0,'g'=>0,'b'=>0), $bg_color=array('r'=>255,'g'=>255,'b'=>255) ){
		$amount_of_lines= ceil(strlen($text)/$separate_line_after_chars)+substr_count($text, '\n')+1;
		$all_lines=explode("\n", $text);  $amount_of_lines = count($all_lines);    $text_final='';
		foreach($all_lines as $key=>$value){ 
			while( mb_strlen($value,'utf-8')>$separate_line_after_chars){	
				$text_final .= mb_substr($value, 0, $separate_line_after_chars, 'utf-8')."\n";
				$value = mb_substr($value, $separate_line_after_chars, null, 'utf-8');
			}  
			$text_final .= mb_substr($value, 0, $separate_line_after_chars, 'utf-8') . ( $amount_of_lines-1 == $key ? "" : "\n");
		}

		Header("Content-type: image/png");
		$width=$height=$offset_x=$offset_y = 0;
		// you can use: if (!file_exists($font))  filecreat('https://github.com/edx/edx-certificates/raw/master/template_data/fonts/Arial%20Unicode.ttf', $font);
														// get the font height.
														$bounds = ImageTTFBBox($size, $rotate, $font, "W");
														if ($rotate < 0)		{$font_height = abs($bounds[7]-$bounds[1]);	} 
														elseif ($rotate > 0)	{$font_height = abs($bounds[1]-$bounds[7]);	} 
														else { $font_height = abs($bounds[7]-$bounds[1]);}
				
		// determine bounding box.
		$bounds = ImageTTFBBox($size, $rotate, $font, $text_final);
		if ($rotate < 0){		$width = abs($bounds[4]-$bounds[0]);					$height = abs($bounds[3]-$bounds[7]);
								$offset_y = $font_height;								$offset_x = 0;
		} 
		elseif ($rotate > 0) {	$width = abs($bounds[2]-$bounds[6]);					$height = abs($bounds[1]-$bounds[5]);
								$offset_y = abs($bounds[7]-$bounds[5])+$font_height;	$offset_x = abs($bounds[0]-$bounds[6]);
		} 
		else{					$width = abs($bounds[4]-$bounds[6]);					$height = abs($bounds[7]-$bounds[1]);
								$offset_y = $font_height;								$offset_x = 0;
		}
		$height = $height +  $font_height*($amount_of_lines+1);
		$image = imagecreate($width+($padding*2)+1,$height+($padding*2)+1);
		
		$background = ImageColorAllocate($image, $bg_color['r'], $bg_color['g'], $bg_color['b']);
		$foreground = ImageColorAllocate($image, $color['r'], $color['g'], $color['b']);

		if ($transparent) ImageColorTransparent($image, $background);
		ImageInterlace($image, true);
	  // render the image
		ImageTTFText($image, $size, $rotate, $offset_x+$padding, $offset_y+$padding, $foreground, $font, $text_final);
		imagealphablending($image, true);
		imagesavealpha($image, true);
	  // output PNG object.
		imagePNG($image);
	}
	
	public function text_to_image2($your_text="heloooo", $width=250, $height=80)
	{
		$IMG = imagecreate( $width, $height );
		$background = imagecolorallocate($IMG, 0,0,255);
		$text_color = imagecolorallocate($IMG, 255,255,0); 
		$line_color = imagecolorallocate($IMG, 128,255,0);
		imagestring( $IMG, 10, 1, 25, $your_text,  $text_color );
		imagesetthickness ( $IMG, 5 );
		//imageline( $IMG, 30, 45, 165, 45, $line_color );
		header( "Content-type: image/png" );
		imagepng($IMG);
		imagecolordeallocate($IMG, $line_color );
		imagecolordeallocate($IMG, $text_color );
		imagecolordeallocate($IMG, $background );
		imagedestroy($IMG); 
		exit;   
	}

	// https://mekshq.com/how-to-convert-hexadecimal-color-code-to-rgb-or-rgba-using-php/
	public function hex2rgba($color, $opacity = false) {
		$default = 'rgb(0,0,0)';
		//Return default if no color provided
		if(empty($color))
			  return $default; 
		//Sanitize $color if "#" is provided 
		if ($color[0] == '#' ) {
			$color = substr( $color, 1 );
		}
		//Check if color has 6 or 3 characters and get values
		if (strlen($color) == 6) {
				$hex = array( $color[0] . $color[1], $color[2] . $color[3], $color[4] . $color[5] );
		} elseif ( strlen( $color ) == 3 ) {
				$hex = array( $color[0] . $color[0], $color[1] . $color[1], $color[2] . $color[2] );
		} else {
				return $default;
		}
		//Convert hexadec to rgb
		$rgb =  array_map('hexdec', $hex);
		//Check if opacity is set(rgba or rgb)
		if($opacity){
			if(abs($opacity) > 1)
				throw new \Exception("Opacity cant be more than 1");
			$opacity = self::number_format((float)$opacity, 2); 
			$output = 'rgba('.implode(",",$rgb).','.$opacity.')';
		} else {
			$output = 'rgb('.implode(",",$rgb).')';
		} 
		return $output;
	}

	// https://stackoverflow.com/a/5925612/2377343  |  https://stackoverflow.com/questions/15852122/
	public function hex_color($name){
		$arr=['aliceblue'=>'F0F8FF', 'antiquewhite'=>'FAEBD7', 'aqua'=>'00FFFF', 'aquamarine'=>'7FFFD4', 'azure'=>'F0FFFF', 'beige'=>'F5F5DC', 'bisque'=>'FFE4C4', 'black'=>'000000', 'blanchedalmond '=>'FFEBCD', 'blue'=>'0000FF', 'blueviolet'=>'8A2BE2', 'brown'=>'A52A2A', 'burlywood'=>'DEB887', 'cadetblue'=>'5F9EA0', 'chartreuse'=>'7FFF00', 'chocolate'=>'D2691E', 'coral'=>'FF7F50', 'cornflowerblue'=>'6495ED', 'cornsilk'=>'FFF8DC', 'crimson'=>'DC143C', 'cyan'=>'00FFFF', 'darkblue'=>'00008B', 'darkcyan'=>'008B8B', 'darkgoldenrod'=>'B8860B', 'darkgray'=>'A9A9A9', 'darkgreen'=>'006400', 'darkgrey'=>'A9A9A9', 'darkkhaki'=>'BDB76B', 'darkmagenta'=>'8B008B', 'darkolivegreen'=>'556B2F', 'darkorange'=>'FF8C00', 'darkorchid'=>'9932CC', 'darkred'=>'8B0000', 'darksalmon'=>'E9967A', 'darkseagreen'=>'8FBC8F', 'darkslateblue'=>'483D8B', 'darkslategray'=>'2F4F4F', 'darkslategrey'=>'2F4F4F', 'darkturquoise'=>'00CED1', 'darkviolet'=>'9400D3', 'deeppink'=>'FF1493', 'deepskyblue'=>'00BFFF', 'dimgray'=>'696969', 'dimgrey'=>'696969', 'dodgerblue'=>'1E90FF', 'firebrick'=>'B22222', 'floralwhite'=>'FFFAF0', 'forestgreen'=>'228B22', 'fuchsia'=>'FF00FF', 'gainsboro'=>'DCDCDC', 'ghostwhite'=>'F8F8FF', 'gold'=>'FFD700', 'goldenrod'=>'DAA520', 'gray'=>'808080', 'green'=>'008000', 'greenyellow'=>'ADFF2F', 'grey'=>'808080', 'honeydew'=>'F0FFF0', 'hotpink'=>'FF69B4', 'indianred'=>'CD5C5C', 'indigo'=>'4B0082', 'ivory'=>'FFFFF0', 'khaki'=>'F0E68C', 'lavender'=>'E6E6FA', 'lavenderblush'=>'FFF0F5', 'lawngreen'=>'7CFC00', 'lemonchiffon'=>'FFFACD', 'lightblue'=>'ADD8E6', 'lightcoral'=>'F08080', 'lightcyan'=>'E0FFFF', 'lightgoldenrodyellow'=>'FAFAD2', 'lightgray'=>'D3D3D3', 'lightgreen'=>'90EE90', 'lightgrey'=>'D3D3D3', 'lightpink'=>'FFB6C1', 'lightsalmon'=>'FFA07A', 'lightseagreen'=>'20B2AA', 'lightskyblue'=>'87CEFA', 'lightslategray'=>'778899', 'lightslategrey'=>'778899', 'lightsteelblue'=>'B0C4DE', 'lightyellow'=>'FFFFE0', 'lime'=>'00FF00', 'limegreen'=>'32CD32', 'linen'=>'FAF0E6', 'magenta'=>'FF00FF', 'maroon'=>'800000', 'mediumaquamarine'=>'66CDAA', 'mediumblue'=>'0000CD', 'mediumorchid'=>'BA55D3', 'mediumpurple'=>'9370D0', 'mediumseagreen'=>'3CB371', 'mediumslateblue'=>'7B68EE', 'mediumspringgreen'=>'00FA9A', 'mediumturquoise'=>'48D1CC', 'mediumvioletred'=>'C71585', 'midnightblue'=>'191970', 'mintcream'=>'F5FFFA', 'mistyrose'=>'FFE4E1', 'moccasin'=>'FFE4B5', 'navajowhite'=>'FFDEAD', 'navy'=>'000080', 'oldlace'=>'FDF5E6', 'olive'=>'808000', 'olivedrab'=>'6B8E23', 'orange'=>'FFA500', 'orangered'=>'FF4500', 'orchid'=>'DA70D6', 'palegoldenrod'=>'EEE8AA', 'palegreen'=>'98FB98', 'paleturquoise'=>'AFEEEE', 'palevioletred'=>'DB7093', 'papayawhip'=>'FFEFD5', 'peachpuff'=>'FFDAB9', 'peru'=>'CD853F', 'pink'=>'FFC0CB', 'plum'=>'DDA0DD', 'powderblue'=>'B0E0E6', 'purple'=>'800080', 'red'=>'FF0000', 'rosybrown'=>'BC8F8F', 'royalblue'=>'4169E1', 'saddlebrown'=>'8B4513', 'salmon'=>'FA8072', 'sandybrown'=>'F4A460', 'seagreen'=>'2E8B57', 'seashell'=>'FFF5EE', 'sienna'=>'A0522D', 'silver'=>'C0C0C0', 'skyblue'=>'87CEEB', 'slateblue'=>'6A5ACD', 'slategray'=>'708090', 'slategrey'=>'708090', 'snow'=>'FFFAFA', 'springgreen'=>'00FF7F', 'steelblue'=>'4682B4', 'tan'=>'D2B48C', 'teal'=>'008080', 'thistle'=>'D8BFD8', 'tomato'=>'FF6347', 'turquoise'=>'40E0D0', 'violet'=>'EE82EE', 'wheat'=>'F5DEB3', 'white'=>'FFFFFF', 'whitesmoke'=>'F5F5F5', 'yellow'=>'FFFF00', 'yellowgreen'=>'9ACD32'];
		return '#'.trim($this->array_value($arr, $name, 'FFFFFFFF'));
	}
	
	//addTextOnImage( ['text'=>'hello',  'input'=>'img.png', 'echo'=>false, 'method'=>'gd|imagick', 'fontsize'=>9, 'angle'=>-15, 'x'=>11, 'y'=>14, 'color'=>'#e7e7e7', 'opacity'=>0.5, 'stroke'=>['#e7e7e7',$width=4,$alpha=0.5], 'spaces'=>3]); //also, font
	public function add_text_on_image($opts=[])
	{
		//v_dump(glob("C:\Windows\Fonts\*"));
		//v_dump($Imagick->queryFonts("*"));
		$text 		= $opts['text'];
		$imagePath	= $opts['input'];
		$fontsize	= $opts['fontsize'];
		list($width, $height, $type, $attr) =getimagesize($imagePath); 
		$x_position = $this->array_value($opts,'x',0);
		$y_position = $this->array_value($opts,'y',0);
		if (strpos($x_position,'%')!==false) $x_position = $width * str_replace('%','',$x_position)/100;
		if (strpos($y_position,'%')!==false) $y_position = $height * str_replace('%','',$y_position)/100;
		
		if( $this->array_value($opts, 'text_repeat') === true)
		{
			$final_text="";
			$multiplier=4; //lets say 3 for assurance
			$spaces_between = $this->array_value($opts, 'spaces',5);
			$repeated_per_width = ($width / (strlen($text) * $fontsize)) * $multiplier;
			$repeated_per_height= ($height / ($fontsize)) * $multiplier;
			
			for ($i=0; $i<$repeated_per_height; $i++)
			{
				$t= "";
				for ($j=0; $j<$repeated_per_width; $j++)
				{
					$t .= $text . str_repeat(" ", $spaces_between );
				}
				$final_text .=$t. "\r\n";
			}
			$text = $final_text;
		}

		if ($this->array_value($opts, 'method') ==='gd') 
		{
			// FETCH IMAGE & WRITE TEXT
			$im = imagecreatefrompng($imagePath); 
			//imagecolorclosest  imagecolorallocate
			$red = imagecolorclosest($im, 0xFF, 0x00, 0x00);		
			$black = imagecolorclosest($im, 0x00, 0x00, 0x00);		
			$white = imagecolorclosest($im, 255, 255, 255);
			// imagecolorallocate(imagecreatetruecolor(111, 111), 2, 2, 2)
			//$color = $red;//$red;

			imagefttext($im, $fontsize=$opts['fontsize'], $angle=$opts['angle'], $x_pos=$x_position, $y_pos=$y_position, $color=$opts['color'], $font=$opts['font'], $text);
			imagealphablending($im, false);
			imagesavealpha($im, true);
			if ($resize=false)
			{
				$percent=0.5;
				$new_width = $width * $percent;
				$new_height = $height * $percent;
				$image_p = imagecreatetruecolor($new_width, $new_height);
				imagecopyresampled($im, $im, 0, 0, 0, 0, $new_width, $new_height, $width, $height);
			}

			// Output and free memory
			header('Content-type: image/png');
			imagepng($im);
			imagedestroy($im);
		}
		else
		{
			// https://mlocati.github.io/articles/php-windows-imagick.html
			// https://www.php.net/manual/en/book.imagick.php
			$Imagick = new \Imagick();
			$Imagick->readImage($imagePath);
			$Imagick->setImageFormat( $format = $this->array_value($opts,'format','png') );
			//$Imagick->setCompressionQuality ( 0 );
			
			$ImagickDraw = new \ImagickDraw();
			$ImagickDraw->setFontSize( $fontsize );
			$ImagickDraw->setTextAntialias ( true );
			if (array_key_exists('font',$opts) )
				$ImagickDraw->setFont( $font=$opts['font'] );
			
			if ( array_key_exists('stroke', $opts))
			{
				$ImagickDraw->setStrokeColor($opts['stroke'][0]);
				$ImagickDraw->setStrokeWidth($opts['stroke'][1]);
				$ImagickDraw->setStrokeOpacity($opts['stroke'][2]);
			}
			$ImagickDraw->setFillColor($color=$opts['color']);
			$ImagickDraw->setFillOpacity($opacity=$opts['opacity']);
			
			//$ImagickDraw->setGravity( Imagick::GRAVITY_CENTER );
			$Imagick->annotateImage( $ImagickDraw, $x_pos=$x_position, $y_pos=$y_position, $angle=$opts['angle'], $text);

			if ($opts['echo'])
			{
				header( "Content-Type: image/{$Imagick->getImageFormat()}" );
				echo $Imagick->getImageBlob();
			}
			else{
				$Imagick->writeImage($imagePath);
			}
		}
	}
	
	public function output_image($file=''){ 
		if ( !in_array( $this->get_extension($file), ['jpg','jpeg','png','bmp','gif']) ) exit ('');
		header("Content-type: image/png");  die( $this->file_get_contents($file)  ); 
	}

	//not_founded_images_redirections (when on FTP, the file is not found, then automatically, the site is loaded.. so, in this case, use our function.
	public function not_found_images_redirect() {
		if (in_array( $this->get_url_parts($this->currentURL,'extension'), ['png','jpg','jpeg','gif','bmp','svg']))		{  
			echo '<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg viewBox="0 85 80 120" xmlns="http://www.w3.org/2000/svg"><style></style><text x="0" y="100" class="small">Image error</text></svg>'; exit;
		}
	}

	public function resize_image($imagePath, $width, $height=0, $auto_proportion=true, $filter=false, $blur=1)
	{
		$Imagick = new \Imagick();
		$Imagick->readImage($imagePath);
		//$Imagick->setImageFormat( $format = $this->array_value($opts,'format','png') );
		$filter= !$filter ? \Imagick::FILTER_LANCZOS : $filter;  //FILTER_LANCZOS
		$Imagick->resizeImage($width, $height, $filter, $blur, $auto_proportion );
		$Imagick->writeImage($imagePath);
	}

	//======helper function==========
	//if(!function_exists('mb_substr_replace')){
	  function mb_substr_replace($string, $replacement, $start, $length = null, $encoding = "UTF-8") {
		if (extension_loaded('mbstring') === true){
			$string_length = (is_null($encoding) === true) ? mb_strlen($string) : mb_strlen($string, $encoding);
			if ($start < 0) { $start = max(0, $string_length + $start); }
			else if ($start > $string_length) {$start = $string_length; }
			if ($length < 0){ $length = max(0, $string_length - $start + $length);  }
			else if ((is_null($length) === true) || ($length > $string_length)) { $length = $string_length; }
			if (($start + $length) > $string_length){$length = $string_length - $start;} 
			if (is_null($encoding) === true) {  return mb_substr($string, 0, $start) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length); }
			return mb_substr($string, 0, $start, $encoding) . $replacement . mb_substr($string, $start + $length, $string_length - $start - $length, $encoding);
		}
		return (is_null($length) === true) ? substr_replace($string, $replacement, $start) : substr_replace($string, $replacement, $start, $length);
	  }
	//}
	//if(!function_exists('mb_str_word_count')){
		function mb_str_word_count($string, $format = 0, $charlist = '[]') {
			$string=trim($string);
			if(empty($string)){$words = array();}    else {$words = preg_split('~[^\p{L}\p{N}\']+~u',$string);}
			switch ($format) {   case 0: return count($words); break;       case 1:      case 2: return $words; break;          default: return $words; break;    }
		}
	//}

	public function header_mail($from=false, $host= false){ 
		$from = $from ? $from : "contact"; 
		$host = $host ? $host : $_SERVER['HTTP_HOST'];//$_SERVER['SERVER_ADDR']; 
		return array('From: '.$from.'@'.$host . "\r\n" .  'Reply-To: '.$from.'@'.$host . "\r\n" .  'X-Mailer: PHP/' . phpversion());
	}

	public function value_or_input_field($namee){
		if (!empty($GLOBALS['editing_inputs'])){
			
		}
		else{
			
		}
	}
	
	public function ksort_recursive(&$array) {
	   foreach ($array as &$value) {
		  if (is_array($value)) $this->ksort_recursive($value);
	   }
	   return ksort($array);
	}

	public function die_if_array_key($array, $key){ if (array_key_exists($key, $array)) exit($array[$key]); }

	public function chars_array_($alhpanumeric=true){  return  ( $alhpanumeric ?
			array('0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z')
			:
			array('!','$','+','<','[',']','%',',','.','=','&','-','<','>','|', '"', '\'', '\\', '~','(','/',')','!',' ',"\r","\n", '*', '{','}','?','`','@',':',';','^')
		);
	}

	public function preg_quote_fast($text){
		$specs =array('/', '.','\\','+','*','?','[','^',']','$','(',')','{','}','=','!','<','>','|',':','-');
		$new_array_for_strtr = array();
		foreach($specs as $each){
			$new_array_for_strtr[$each] = '\\'.$each;
		}
		$text = strtr( $text, $new_array_for_strtr);
		return $text;
	}

	public function checkboxes($checkbox_name,$current_value, $unchecked_value,$checked_value){
		$out = '<input type="hidden" name="'.$checkbox_name.'" value="'.$unchecked_value.'" /><input class="chbkx" type="checkbox"  name="'.$checkbox_name.'" value="'.$checked_value.'" '. ($current_value==$checked_value ? 'checked="checked"': '') .' />'; return $out;
	}

	public function js_library($url_or_Tag=true, $defaultPath=""){
		if( empty($defaultPath) && function_exists('home_url') )
			$defaultPath = plugin_dir_url($this->plugin_entryfile);
		$url = $defaultPath . '/libray_standard.js';
		return $url_or_Tag ? $url : '<script src="'.$url.'"></script>';
	}

	public function my_translate_month_inside($string = '27/January/2015'){
		foreach($GLOBALS['odd']['months_arr'] as $each){
			if(strpos($string,$each)!==false) { 
				$string = str_replace($each,translate__MONTH($each), $string);
			}
		}
		return $string;
	}

	public function my_utf8_decode($textt){
		$var = $textt;	$var = iconv("UTF-8","ISO-8859-1//IGNORE",$var);	$var = iconv("ISO-8859-1","UTF-8",$var); $var = str_replace(' ','',$var);
		return $var;
	}

	// ============================================= YOUTUBE DOWNLOAD FUNCTIONS ====================================================
	// https://pastebin_com/bFePMkfy
	
	//	https://img.youtube.com/vi/XXXXXXXXX/0.jpg (a bit larger)   // 1,2,3
	//  https://img.youtube.com/vi/xxxxxxxxx/mqdefault.jpg
	//  https://img.youtube.com/vi/xxxxxxxxx/hqdefault.jpg
	//  https://img.youtube.com/vi/xxxxxxxxx/maxresdefault.jpg
	public function get_youtube_thumbnail($id,$quality='maxres'){return 'https://i.ytimg.com/vi/'.$id.'/'.$quality.'.jpg';}  

	//to check if variable are normal
	public function get_youtube_id_from_url($url) {
		preg_match('/(http(s|):|)\/\/(www\.|)youtu(be\.com|\.be)\/(embed\/|watch.*?v=|)([a-z_A-Z0-9\-]{11})/i', $url, $results); 
		return (isset($results[6]) ? $results[6] : false);
	}
	public function get_youtube_id_from_contents($url){ 
		if (stripos($url,'youtu.be/')!==false)			{preg_match('/(https:|http:|)(\/\/www\.|\/\/|)(.*?)\/(.{11})/si', $url, $final_ID); $x= !empty($final_ID[4]) ? $final_ID[4] : '';}
		elseif  (stripos($url,'youtube.com/')!==false)	{preg_match('/(https:|http:|)(\/\/www\.|\/\/|)(.*?)\/(embed\/|watch.*?v=|)([a-z_A-Z0-9\-]{11})/si', $url, $IDD);$x= !empty($IDD[5]) ? $IDD[5] : ''; }
		return (!empty($x) ? $x : '');
	}

	public function validate_youtube_id($id){ if (strlen($id)!=11 || preg_match('/[\<\>\'\=\$\"\?\(\{]/si',$text)) {die("incorrrrrect_ID_ error79");	 }}
	//#################################
		
	// force ssl	
	public function redirect_to_https(){
		if(empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === "off")
		{
			$redirect = $this::sanitize_url('https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
			header('HTTP/1.1 301 Moved Permanently');
			header('Location: ' . $redirect);
			exit();
		}
	}

	public function redirect_to_nonwww($https=true){
		if( stripos($_SERVER['HTTP_HOST'],'www.') !== false ) {
			$redirect =  ($https ? 'https' : 'http') . '://' . str_replace('www.','', $_SERVER['HTTP_HOST']) . $_SERVER['REQUEST_URI'];
			header('HTTP/1.1 301 Moved Permanently');
			header('Location: ' . $redirect);
			exit();
		}
	}
	
	public function serialized_fixer($serialized_string){
		// securities
		if (empty($serialized_string)) 						return '';
		if ( !preg_match('/^[aOs]:/', $serialized_string) )	return $serialized_string;
		if ( @unserialize($serialized_string) !== false ) 	return $serialized_string;
		
		return
		preg_replace_callback(
			'/s\:(\d+)\:\"(.*?)\";/s', 
			function ($matches){	return 's:'.strlen($matches[2]).':"'.$matches[2].'";';	},
			$serialized_string )
		;
	}

	public function img_urlencode($imgUrl){
		return str_replace('/'.basename($imgUrl) ,  '/'.str_replace('+','%20',basename($imgUrl)),       $imgUrl);
	}
	public function img_urlencode2($imgUrl){
		preg_match('/(.*)\/(.*)/si',$imgUrl, $n);	$x = (!empty($n[1]) && !empty($n[2])) ? $n[1].'/'.str_replace('+','%20',urlencode($n[2])) : "error_29858";  return $x;
	}

	// i.e. get_remote_data(' tinyurl.com/api-create.php?url='.$url); 
	public function get_short_link($url) { return $url; }

	public function allowed_extensions_of_url( $url ) {
		$ext = array( 'jpeg', 'jpg', 'gif', 'png' );
		$info = (array) pathinfo( parse_url( $url, PHP_URL_PATH ) );
		return isset( $info['extension'] ) && in_array( strtolower( $info['extension'] ), $ext, TRUE );
	}

	public function m1($tag=""){ $this->var_dump("\r\n<br/>* [MemoryUsage]A$tag :". $this->memory_usage()); }
	public function memory_usage(){ return memory_get_usage()/pow(1024,2); }
	public function gc_enable(){ return gc_enable(); }
	public function gc_clean() { return gc_collect_cycles(); }

	
	// create: https://vectr.com/new      https://vectorpaint.yaks.co.nz/     
	// convert : https://hnet.com/png-to-svg/  ( https://image.online-convert.com/convert-to-svg | https://convertio.co/ )
	// view: https://www.rapidtables.com/web/tools/svg-viewer-editor.html
	public function images($which, $type="png", $url_or_tag=true)
	{
		$url=[];
		switch ($which)
		{
			 //see visually: https://i.imgur.com/MNxlU7s.png
			case "overlay-pro"		: $url['svg'] = '<svg height="15pt" preserveAspectRatio="xMidYMid meet" viewBox="0 0 14 15" width="14pt" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(.1 0 0 -.1 0 15)"><path d="m20 125c-13-14-21-27-18-30 2-3 17 9 33 25 16 17 24 30 19 30-6 0-21-11-34-25z"/><path d="m53 91c-73-80-67-94 7-17 33 35 60 66 60 69 0 16-18 2-67-52z"/><path d="m85 50c-27-28-45-50-39-50 13 0 99 88 93 95-3 2-27-18-54-45z"/><path d="m125 10c-3-5-1-10 4-10 6 0 11 5 11 10 0 6-2 10-4 10-3 0-8-4-11-10z"/></g></svg>';  break; 
			 //see visually: https://i.imgur.com/6oHljXM.png
			case "questionMark-1"	: $url['svg'] = '<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="64px" height="64px" viewBox="0 0 640 640" preserveAspectRatio="xMidYMid meet"> <g id="layer101" fill="#2a589e" stroke="none"> <path d="M234 616 c-124 -40 -224 -175 -224 -301 0 -106 83 -231 185 -279 76 -37 184 -37 260 0 110 52 191 182 183 294 -8 111 -83 222 -182 270 -59 28 -163 36 -222 16z"/> </g> <g id="layer102" fill="#6a88b6" stroke="none"> <path d="M270 470 l0 -40 45 0 45 0 0 40 0 40 -45 0 -45 0 0 -40z"/> <path d="M36 389 c-20 -103 3 -203 65 -273 119 -135 329 -135 449 1 22 25 40 50 40 55 0 6 -20 -12 -45 -40 -43 -48 -115 -93 -146 -91 -8 0 -2 5 14 11 31 11 34 20 12 38 -8 7 -12 16 -10 21 3 4 -10 2 -29 -6 -20 -8 -52 -14 -73 -14 l-38 2 45 6 c84 13 130 54 130 114 0 12 -23 51 -50 86 -28 35 -50 72 -50 82 0 15 -7 19 -35 19 -31 0 -35 -3 -35 -26 0 -31 14 -60 47 -95 54 -59 17 -125 -58 -102 l-34 10 47 -1 c55 -1 67 7 49 36 -7 11 -8 17 -2 13 6 -3 11 -2 11 2 0 5 -23 16 -50 26 -70 25 -158 79 -199 121 -19 20 -38 36 -42 36 -3 0 -9 -14 -13 -31z"/> </g> <g id="layer103" fill="#e5ebf3" stroke="none"> <path d="M280 470 c0 -39 1 -40 35 -40 34 0 35 1 35 40 0 39 -1 40 -35 40 -34 0 -35 -1 -35 -40z"/> <path d="M280 372 c0 -19 51 -105 69 -116 15 -9 14 -59 -1 -74 -14 -14 -77 -16 -111 -3 -23 9 -25 7 -29 -20 -3 -16 0 -33 5 -36 5 -3 40 -8 77 -11 148 -10 196 76 105 188 -25 30 -45 63 -45 72 0 14 -8 18 -35 18 -27 0 -35 -4 -35 -18z"/> </g> </svg>'; break;
			 //see visually: https://i.imgur.com/73R7eLv.png
			case "questionMark-2"	: $url['svg'] = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:se="http://svg-edit.googlecode.com" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="220.36365738125096" height="219.96970986050064" style=""><rect id="backgroundrect" width="100%" height="100%" x="0" y="0" fill="#FFFFFF" stroke="none" class="" style=""/>  <g class="currentLayer" style=""><title>Layer 1</title><path fill="#4a90d6" fill-opacity="1" stroke="#ebeb1a" stroke-opacity="1" stroke-width="0" stroke-dasharray="none" stroke-linejoin="round" stroke-linecap="butt" stroke-dashoffset="" fill-rule="nonzero" opacity="1" marker-start="" marker-mid="" marker-end="" d="M0,110 C0,49.22651933701658 49.226519337016555,0 110,0 C170.77348066298345,0 220,49.22651933701658 220,110 C220,170.77348066298345 170.77348066298345,220 110,220 C49.226519337016555,220 0,170.77348066298345 0,110 z" id="svg_1" class="" filter=""/><foreignObject fill="#4a90d6" stroke="#222222" stroke-width="2" stroke-linejoin="round" stroke-dashoffset="" fill-rule="nonzero" font-size="156" font-family="Arial, Helvetica, sans-serif" letter-spacing="0" word-spacing="0" marker-start="" marker-mid="" marker-end="" id="svg_9" x="23.115700873212546" y="29.279336103256995" width="224.30167929102208" height="168.0040174784078" style="color: rgb(36, 36, 36);" class="" transform="rotate(0.08801647275686264 832.5809326171628,364.08416748049103) "><p style="border: none;outline: none;font-size: inherit;line-height: 1em;padding:0;margin:0;" xmlns="http://www.w3.org/1999/xhtml"><p xmlns="http://www.w3.org/1999/xhtml" style="border: none;outline: none;font-size: inherit;line-height: 1em;padding:0;margin:0;"></p><p xmlns="http://www.w3.org/1999/xhtml" style="border: none;outline: none;font-size: inherit;line-height: 1em;padding:0;margin:0;"></p><p xmlns="http://www.w3.org/1999/xhtml" style="border: none;outline: none;font-size: inherit;line-height: 1em;padding:0;margin:0;"> ?</p><p style="border: none;outline: none;font-size: inherit;line-height: 1em;padding:0;margin:0;"></p><p style="border: none;outline: none;font-size: inherit;line-height: 1em;padding:0;margin:0;"></p></p></foreignObject></g><defs><marker id="DotS" refX="0" refY="0" orient="auto" inkscape:stockid="DotS" overflow="visible"> <path transform="scale(.2) translate(7.4 1)" d="M-2.5-1c0 2.76-2.24 5-5 5s-5-2.24-5-5 2.24-5 5-5 5 2.24 5 5z" fill-rule="evenodd" stroke="#000" stroke-width="1pt" style="fill: rgb(235, 235, 26); stroke: rgb(235, 235, 26); stroke-dasharray: none;"/></marker></defs></svg>'; break;
			 //see visually: https://i.imgur.com/mx70WNM.png
			case "rating-transparent" : $url['svg'] = '<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="544px" height="128px" viewBox="0 0 5440 1280" preserveAspectRatio="xMidYMid meet"><g id="layer101" fill="#ffb900" stroke="none"><path d="M335 1134 c-51 -14 -56 -69 -25 -256 l21 -119 -91 -100 c-72 -79 -90 -106 -91 -132 -3 -60 3 -102 16 -115 7 -8 69 -21 149 -31 74 -10 139 -21 143 -25 5 -4 40 -69 78 -144 l70 -137 75 0 75 0 70 137 c39 75 73 140 78 144 4 4 68 15 142 25 127 16 143 16 270 0 74 -10 138 -21 142 -25 5 -4 40 -69 78 -144 l70 -137 75 0 75 0 70 137 c39 75 73 140 78 144 4 4 68 15 142 25 127 16 143 16 270 0 74 -10 138 -21 142 -25 5 -4 40 -69 78 -144 l70 -137 75 0 75 0 70 137 c39 75 73 140 78 144 4 4 68 15 142 25 127 16 143 16 270 0 74 -10 138 -21 142 -25 5 -4 40 -69 78 -144 l70 -137 75 0 75 0 70 137 c39 75 73 140 78 144 4 4 68 15 142 25 127 16 143 16 270 0 74 -10 138 -21 142 -25 5 -4 40 -69 78 -144 l70 -137 75 0 75 0 70 137 c39 75 73 140 78 144 4 4 69 15 143 25 80 10 142 23 149 31 7 7 14 29 16 48 5 89 3 95 -91 199 -49 56 -90 102 -90 103 0 2 9 55 20 118 23 135 25 209 6 235 -11 15 -27 19 -74 20 -54 0 -73 -6 -181 -60 l-121 -59 -121 59 c-108 54 -127 60 -181 60 -47 -1 -63 -5 -74 -20 -19 -26 -17 -97 6 -237 l21 -119 -72 -80 c-40 -43 -75 -79 -79 -79 -7 0 -130 134 -143 156 -3 6 2 62 13 125 23 135 25 208 6 234 -11 15 -27 19 -74 20 -54 0 -73 -6 -181 -60 l-121 -59 -121 59 c-108 54 -127 60 -181 60 -47 -1 -63 -5 -74 -20 -19 -26 -17 -97 6 -237 l21 -119 -72 -80 c-40 -43 -75 -79 -79 -79 -7 0 -130 134 -143 156 -3 6 2 62 13 125 23 135 25 208 6 234 -11 15 -27 19 -74 20 -54 0 -73 -6 -181 -60 l-121 -59 -121 59 c-108 54 -127 60 -181 60 -47 -1 -63 -5 -74 -20 -19 -26 -17 -97 6 -237 l21 -119 -72 -80 c-40 -43 -75 -79 -79 -79 -7 0 -130 134 -143 156 -3 6 2 62 13 125 23 135 25 208 6 234 -11 15 -27 19 -74 20 -54 0 -73 -6 -181 -60 l-121 -59 -121 59 c-108 54 -127 60 -181 60 -47 -1 -63 -5 -74 -20 -19 -26 -17 -97 6 -237 l21 -119 -72 -80 c-40 -43 -75 -79 -79 -79 -7 0 -130 134 -143 156 -3 6 2 62 13 125 23 135 25 208 6 234 -11 15 -27 19 -74 20 -54 0 -73 -6 -181 -60 l-121 -59 -117 57 c-113 57 -177 74 -228 61z"/></g></svg>'; break;
			//see visually: https://upload.wikimedia.org/wikipedia/commons/b/b5/PayPal.svg
			case "paypal" : $url['svg'] = '<svg xmlns="http://www.w3.org/2000/svg" width="248" height="66" viewBox="0 0 124 33"><path fill="#253B80" d="M46.211 6.749h-6.839a.95.95 0 0 0-.939.802l-2.766 17.537a.57.57 0 0 0 .564.658h3.265a.95.95 0 0 0 .939-.803l.746-4.73a.95.95 0 0 1 .938-.803h2.165c4.505 0 7.105-2.18 7.784-6.5.306-1.89.013-3.375-.872-4.415-.972-1.142-2.696-1.746-4.985-1.746zM47 13.154c-.374 2.454-2.249 2.454-4.062 2.454h-1.032l.724-4.583a.57.57 0 0 1 .563-.481h.473c1.235 0 2.4 0 3.002.704.359.42.469 1.044.332 1.906zM66.654 13.075h-3.275a.57.57 0 0 0-.563.481l-.145.916-.229-.332c-.709-1.029-2.29-1.373-3.868-1.373-3.619 0-6.71 2.741-7.312 6.586-.313 1.918.132 3.752 1.22 5.031.998 1.176 2.426 1.666 4.125 1.666 2.916 0 4.533-1.875 4.533-1.875l-.146.91a.57.57 0 0 0 .562.66h2.95a.95.95 0 0 0 .939-.803l1.77-11.209a.568.568 0 0 0-.561-.658zm-4.565 6.374c-.316 1.871-1.801 3.127-3.695 3.127-.951 0-1.711-.305-2.199-.883-.484-.574-.668-1.391-.514-2.301.295-1.855 1.805-3.152 3.67-3.152.93 0 1.686.309 2.184.892.499.589.697 1.411.554 2.317zM84.096 13.075h-3.291a.954.954 0 0 0-.787.417l-4.539 6.686-1.924-6.425a.953.953 0 0 0-.912-.678h-3.234a.57.57 0 0 0-.541.754l3.625 10.638-3.408 4.811a.57.57 0 0 0 .465.9h3.287a.949.949 0 0 0 .781-.408l10.946-15.8a.57.57 0 0 0-.468-.895z"/><path fill="#179BD7" d="M94.992 6.749h-6.84a.95.95 0 0 0-.938.802l-2.766 17.537a.569.569 0 0 0 .562.658h3.51a.665.665 0 0 0 .656-.562l.785-4.971a.95.95 0 0 1 .938-.803h2.164c4.506 0 7.105-2.18 7.785-6.5.307-1.89.012-3.375-.873-4.415-.971-1.142-2.694-1.746-4.983-1.746zm.789 6.405c-.373 2.454-2.248 2.454-4.062 2.454h-1.031l.725-4.583a.568.568 0 0 1 .562-.481h.473c1.234 0 2.4 0 3.002.704.359.42.468 1.044.331 1.906zM115.434 13.075h-3.273a.567.567 0 0 0-.562.481l-.145.916-.23-.332c-.709-1.029-2.289-1.373-3.867-1.373-3.619 0-6.709 2.741-7.311 6.586-.312 1.918.131 3.752 1.219 5.031 1 1.176 2.426 1.666 4.125 1.666 2.916 0 4.533-1.875 4.533-1.875l-.146.91a.57.57 0 0 0 .564.66h2.949a.95.95 0 0 0 .938-.803l1.771-11.209a.571.571 0 0 0-.565-.658zm-4.565 6.374c-.314 1.871-1.801 3.127-3.695 3.127-.949 0-1.711-.305-2.199-.883-.484-.574-.666-1.391-.514-2.301.297-1.855 1.805-3.152 3.67-3.152.93 0 1.686.309 2.184.892.501.589.699 1.411.554 2.317zM119.295 7.23l-2.807 17.858a.569.569 0 0 0 .562.658h2.822c.469 0 .867-.34.939-.803l2.768-17.536a.57.57 0 0 0-.562-.659h-3.16a.571.571 0 0 0-.562.482z"/><path fill="#253B80" d="M7.266 29.154l.523-3.322-1.165-.027H1.061L4.927 1.292a.316.316 0 0 1 .314-.268h9.38c3.114 0 5.263.648 6.385 1.927.526.6.861 1.227 1.023 1.917.17.724.173 1.589.007 2.644l-.012.077v.676l.526.298a3.69 3.69 0 0 1 1.065.812c.45.513.741 1.165.864 1.938.127.795.085 1.741-.123 2.812-.24 1.232-.628 2.305-1.152 3.183a6.547 6.547 0 0 1-1.825 2c-.696.494-1.523.869-2.458 1.109-.906.236-1.939.355-3.072.355h-.73c-.522 0-1.029.188-1.427.525a2.21 2.21 0 0 0-.744 1.328l-.055.299-.924 5.855-.042.215c-.011.068-.03.102-.058.125a.155.155 0 0 1-.096.035H7.266z"/><path fill="#179BD7" d="M23.048 7.667c-.028.179-.06.362-.096.55-1.237 6.351-5.469 8.545-10.874 8.545H9.326c-.661 0-1.218.48-1.321 1.132L6.596 26.83l-.399 2.533a.704.704 0 0 0 .695.814h4.881c.578 0 1.069-.42 1.16-.99l.048-.248.919-5.832.059-.32c.09-.572.582-.992 1.16-.992h.73c4.729 0 8.431-1.92 9.513-7.476.452-2.321.218-4.259-.978-5.622a4.667 4.667 0 0 0-1.336-1.03z"/><path fill="#222D65" d="M21.754 7.151a9.757 9.757 0 0 0-1.203-.267 15.284 15.284 0 0 0-2.426-.177h-7.352a1.172 1.172 0 0 0-1.159.992L8.05 17.605l-.045.289a1.336 1.336 0 0 1 1.321-1.132h2.752c5.405 0 9.637-2.195 10.874-8.545.037-.188.068-.371.096-.55a6.594 6.594 0 0 0-1.017-.429 9.045 9.045 0 0 0-.277-.087z"/><path fill="#253B80" d="M9.614 7.699a1.169 1.169 0 0 1 1.159-.991h7.352c.871 0 1.684.057 2.426.177a9.757 9.757 0 0 1 1.481.353c.365.121.704.264 1.017.429.368-2.347-.003-3.945-1.272-5.392C20.378.682 17.853 0 14.622 0h-9.38c-.66 0-1.223.48-1.325 1.133L.01 25.898a.806.806 0 0 0 .795.932h5.791l1.454-9.225 1.564-9.906z"/></svg>'; break;
		}
		return $url[$type];
	}
	public function encode_svg($content){ return str_replace(['<','>', '#', '"'], ['%3C','%3E', '%23', '\''], $content); }
	public function image_svg($which){ return 'data:image/svg+xml;charset=UTF-8,'. $this->encode_svg( $this->images($which, 'svg') ); }
	
	public function question_mark($text, $dialog=0, $question_mark="") { 
		$mouseover='';
		$content = '';
		if($dialog==0){
			$content = $text;
		}
		else if($dialog==1){
			$content = '';
			$mouseover = ' onmouseover="jQuery(\'#\'+this.parentNode.id).tooltip({ items:this,   content:\''.$text.'\', show: { effect: \'blind\', duration: 800 } 	}).tooltip(\'open\');"'; 	
		}
		else if($dialog==2){
			$content = '';
			$mouseover = ' onmouseover="jQuery(\'<div>'.$text.'</div>\').dialog({   modal:true,   width:600 });"';
		}
		if (empty($question_mark)) $question_mark=$this->image_svg('questionMark-1');
		return '<span id="xx"><img src="'. $question_mark .'" class="question_mark" style="cursor:crosshair; width:20px;" alt="'.$content.'" title="'.$content.'" '.$mouseover.' /></span>';
	}

	public function between($a,$b,$c){
		return ($a<$b && $b<$c);
	}
	public function inside($a,$b,$c){
		return ($this->between($a,$b,$c) || $this->between($c,$b,$a) );
	}
	

	public static $nodepath='';
	public static function node_exec($filepath){
		//process.stdout.write("hii");
		return empty(self::$nodepath) ? 'Node path not set' : self::cmd(self::$nodepath .' '.$filepath);
	}


	#region     ############### ASYNC FUNCTIONS ############### //
	// https://www.reddit.com/r/PHP/comments/no7abs/

	public $async_methods_available = ['reactphp', 'amphp', 'amphp_parallel', 'spatie','parallel','fibers', 'swoole', 'exec', 'proc-open', 'popen', 'pcntl_fork', '_no_async_' ];
	// to execute non-blocking async callback-function |  args:   [$func,$args]  where $args is array 
	public function async_function($callback, $which_method='spatie'){
		$method = strtolower($which_method);
		if ( !in_array( $method, $this->async_methods_available) ) 
			throw new \Exception( "$method not supported, use from: ". implode(' | ',$this->async_methods_available) );
		
		$func_name = 'async_functions_helper_'.$method;
		call_user_func([$this,$func_name],  [ [$callback, $arg1=[]] ] );
	}

	// to execute blocking/non-blocking call of multiple gourp for async callback-functions
	// #######    EXAMPLE   #######
	//
	//   $func = function($param1=null, $param2=null, ... ){
	//	    echo "\nParams are : ". print_r(func_get_args(),true);
	//   };
	//
    //   $arr = [ [$func], [$func, "hi"], [$func, "good", "bye"] ] ;
	//   $helpers->async_functions($arr,'parallel');

	public function async_functions($callbacksArray, $which_method='spatie', $blocking=true, $exception_handler=null){
		$method = strtolower($which_method);
		if ( !in_array( $method, $this->async_methods_available) ) 
			throw new \Exception( "$method not supported, use from: ". implode(' | ',$this->async_methods_available) );

		$example = ': CALLBACK & ARGUMENT pair, like [$callback,$arg1,...] or [$callback], where $args behaves like as in call_user_func_array';
		if ( !is_array($callbacksArray) )
			throw new \Exception("async_functions was passed incorrect argument. Should be array of $example"); 
		
		$callbacksArray_NEW =[]; // just add arguments
	
		foreach($callbacksArray as $eachPair){
			if (is_callable($eachPair))
			{
				$callbacksArray_NEW[] = [$eachPair, []];
			}
			elseif (is_array($eachPair))
			{
				$callback = $eachPair[0];
				array_shift($eachPair); //unset first one, as all other ones are arguments passed to call_user_func
				$args     = empty($eachPair)? [] : $eachPair;
				$callbacksArray_NEW[] = [$callback, $args];
			}
			else{
				throw new \Exception("async_functions was passed incorrect array of callback. Each array child should be $example");
			}
		}
		
		//re-sort
		$func_name = 'async_functions_helper_'.$method;

		call_user_func([$this, $func_name], $callbacksArray_NEW, $exception_handler);
		return;

		if ($blocking)
		{
			
		}
		else{
			//foreach($callbacksArray_NEW as $callbackAndArgPair){
			//	$this->async_function($callbackAndArgPair, $which_method);
			//}
		}
	}


	// ##### PARALLEL (requires extension) #####
    public static function async_functions_helper_parallel($callbacksArray, $exception_handler=null)
    {
		foreach( $callbacksArray as $callback_arg_pair){
			//\parallel\run($callback = $callback_arg_pair[0], $args = $callback_arg_pair[1] ); 
			//$r1 = new \parallel\Runtime();$r1->run
			self::async_function_parallel($callback = $callback_arg_pair[0], $args = $callback_arg_pair[1]);
		}
    } 
    public static function async_function_parallel($callback, $args=[])
    {
		\parallel\run($callback, $args); 
    } 


	// ##### SPATIE #####  [ https://github.com/spatie/async/issues/120 ]
    public static function async_functions_helper_spatie($callbacksArray, $exception_handler=null)
    {
		if ( ! self::spatieSupported()  ){
			$msg = "Spaties needed extensions are not enabled: pcntl & posix";
			//if ( !$this->is_localhost()) throw new \Exception($msg); else   
			self::var_dump($msg . " ; However, continuing execution in synchronous mode");
		}
		$pool = \Spatie\Async\Pool::create();
		foreach( $callbacksArray as $callback_arg_pair){
			$pool->add( function() use($callback_arg_pair){  
				call_user_func_array($callback = $callback_arg_pair[0], $args = $callback_arg_pair[1]);
			} )->then(function ($output) {
				// Handle success
			})->catch(function (\Spatie\Async\Pool\Throwable $exception) {
				var_dump($exception);
			});
		}
		$pool->wait(); 
    }  public static function spatieSupported(){ return \Spatie\Async\Pool::isSupported(); } 


	
	// ##### SWOOLE (requires extension) #####
	// use advanced usage: https://www.swoole.co.uk/docs/modules/swoole-coroutine-run
    public static function async_functions_helper_swoole($callbacksArray, $exception_handler=null)
    {
		// needs to be checked ( as: https://www.swoole.co.uk/docs/modules/swoole-coroutine-run )
		if (self::swoole_installed() && function_exists('\\Swoole\\Coroutine\\run') )
		{
			if ( !self::swoole_inside_coroutine() ) 
			{
				\Swoole\Coroutine\run(function() use ($callbacksArray,$exception_handler)  {
					self::async_functions_helper_swooleGoTrigger($callbacksArray,$exception_handler);
				});
			}
			else{ 
				self::async_functions_helper_swooleGoTrigger($callbacksArray,$exception_handler);
			} 
		}
		else{
			var_dump("Swoole not installed, running plain function");
			self::async_functions_helper__no_async_($callbacksArray);
		}
    }  
    private static function async_functions_helper_swooleGoTrigger($callbacksArray, $exception_handler=null)
	{
		//$wg = new \Swoole\Coroutine\WaitGroup();
		foreach( $callbacksArray as $callback_arg_pair){
			\go(function() use ($callback_arg_pair, $exception_handler) {
				try{
					//$wg->add(1); 
					call_user_func_array($callback = $callback_arg_pair[0], $args = $callback_arg_pair[1]);
					//$wg->done();
				}
				catch(\Exception $ex){
					if (is_callable($exception_handler))
						call_user_func($exception_handler, new \Exception($ex->getMessage()) );
					else 
						trigger_error($ex->getMessage());
				}
			});
		}
		//$wg->wait();
	}  
	public static function swoole_inside_coroutine(){ return (self::swoole_installed() && \Swoole\Coroutine::getCid()!=-1 && \Swoole\Coroutine::getPcid()!==false ); } // https://cloud.tencent.com/developer/article/1771756
	public static function swoole_inside_coroutine_run(){ return (self::swoole_installed() && \Swoole\Coroutine::getPcid()==-1); } 
	public static function swoole_inside_coroutine_go(){ return (self::swoole_installed() && is_numeric(\Swoole\Coroutine::getPcid()) && \Swoole\Coroutine::getPcid()>0); } 

	public static function swoole_installed(){ return class_exists('\\Swoole\\Coroutine'); }
	public static function helper_SwooleToggle($enable_or_disable ){
		// SWOOLE_HOOK_ALL;  // https://www.geeksforgeeks.org/php-bitwise-operators/ ::: swoole_hook_all is: 2147479551  | SWOOLE_HOOK_CURL : 2048  | SWOOLE_HOOK_NATIVE_CURL : 4096    \Swoole\Runtime::getHookFlags(); // $currentHooks ^ SWOOLE_HOOK_CURL  ^ SWOOLE_HOOK_NATIVE_CURL; <-- cannot enable both  |  \Swoole\Runtime::setHookFlags($final); // https://github.com/swoole/swoole-src/issues/4280
		if (!self::swoole_inside_coroutine())
			return;
		if($enable_or_disable)
			\Swoole\Runtime::setHookFlags(SWOOLE_HOOK_ALL);
		else
			\Swoole\Runtime::setHookFlags(0);
	}

	// ##### AMPHP #####
	public static function async_functions_helper_reactphp($callbacksArray, $exception_handler=null){
		$loop = \React\EventLoop\Factory::create();
		$promise = new \React\Promise\Promise(function($resolve){
		});
		foreach($callbacksArray as $callback_arg_pair) { 
			$promise->then(function($v) use ($loop, $callback_arg_pair, $exception_handler) {
				$loop->run( function() use($callback_arg_pair, $exception_handler){
					try{
						call_user_func_array($callback = $callback_arg_pair[0], $args = $callback_arg_pair[1]);
					}
					catch(\Exception $ex){
						if (is_callable($exception_handler))
							call_user_func($exception_handler,$ex);
						else 
							trigger_error($ex->getMessage());
					}
				}  );
			});
		}
		$loop->run();		
	}


	// ##### AMPHP #####
	public static function async_functions_helper_amphp($callbacksArray, $exception_handler=null){

		$promises = [];
		\Amp\Loop::run(function () use($callbacksArray) { 
			foreach($callbacksArray as $eachCallbackAndArg) { 
				\Amp\Loop::defer($eachCallbackAndArg[0]); 
			}
		});

		return;
		//...
			$promises[] = \Amp\call( function() use ($eachCallbackAndArg) {
				return call_user_func($eachCallbackAndArg[0],$eachCallbackAndArg[1]);
			} );  
		//\Amp\Loop::run();  \Amp\asyncCall
		\Amp\Promise\wait(\Amp\Promise\all($promises)); 

		return;
		// ..
			\Amp\asyncCall( function() use ($eachCallbackAndArg) {
				return call_user_func($eachCallbackAndArg[0],$eachCallbackAndArg[1]);
			} ); 
		\Amp\Loop::run();
	}

	// https://github.com/amphp/parallel-functions  && https://github.com/amphp/parallel-functions/issues/28
	public static function async_functions_helper_amphp_parallel($callbacksArray, $exception_handler=null){
		foreach($callbacksArray as $callback_arg_pair) {
			//$pool = new \Amp\Parallel\Worker\DefaultPool();
			$callbacks[] = call_user_func_array(\Amp\ParallelFunctions\parallel($callback_arg_pair[0]), $callback_arg_pair[1]); //, $pool
		}
		$result = \Amp\Promise\wait(\Amp\Promise\all($callbacks));
	}



	// ##### EXEC #####
	// https://stackoverflow.com/questions/49592786/asynchronous-call-to-shell-exec-php
	// https://stackoverflow.com/questions/222414/asynchronous-shell-exec-in-php
	// https://stackoverflow.com/questions/45953/php-execute-a-background-process#45966
	// https://stackoverflow.com/questions/2212635/best-way-to-manage-long-running-php-script !!
	public static function async_functions_helper_exec($command = null, $with_php=false){
		// moved to separate file, due to WP restrictions
		return self::async_functions_helper_exec2($command, $with_php);
	}

	// no async, just default fallback
    public static function async_functions_helper__no_async_($callbacksArray, $exception_handler=null)
    {		
		self::call_user_funcs($callbacksArray);
    } 
    public static function call_user_funcs($callbacksArray)
    {
		foreach($callbacksArray as $callbackAndArgsPair){
			call_user_func_array($callbackAndArgsPair[0], $callbackAndArgsPair[1]);
		}
    }
	#endregion

	
	// get-timezones : pastebin_com/4tXjgY7B

	public function add_prefix_to_object_keys($object, $prefix){
		$new_object = new stdClass();
		foreach ($object as $k => $v) { 
			$new_object->{$prefix . $k} = $v;
		}
		return $new_object;
	}

	public function convert_timeframe_into_seconds($input="4h", $minute_symbol="m", $month_symbol="M")
	{
		$array=['s'=>1,'S'=>1,     'm'=>60,    'h'=>3600,'H'=>3600,     'd'=>86400,'D'=>86400,     'w'=>604800,'W'=>604800,     'M'=>2678400];//31days
		foreach($array as $key=>$value)
		{
			if ( strpos($input,$key)!==false ) { $input=str_replace($key,'', $input); $input=$input*$value; }
		}
		return $input;
	}
	
	public function die_pretty($html){
		$html = 
		'<div style="padding: 50px; margin:100px auto; width:50%; text-align:center; line-height: 1.4; display:flex; justify-content:center; flex-direction:column; font-family: cursive; font-size: 1.7em; box-shadow:0px 0px 10px gray; border-radius: 10px;">'.
			'<div><h3>'.$html.'</h3></div>'.
		'</div>';
		exit ($html);
	}

	// custom always-loaded scripts 
	// my_script_url("css|js",  "public|admin")
	public function my_script_url($type="js|css", $kind="public|admin", $with_tag=false) 
	{
		if ($type=='js'){
			return ($with_tag? '<script type="text/javascript" src="':'') . $this->js_library() .'&vers='.$this->changeable_JS_CSS_version . ($with_tag? '"></script>':'');
		}
		elseif ($type=='css'){
			return ($with_tag? '<link rel="stylesheet" href="':'') . $this->baseScriptsUrl.'style-'.$kind.'.css?vers='.$this->changeable_JS_CSS_version. ($with_tag? '"	type="text/css" media="all" />':'');
		}
	}
	public function my_loader_css_js($css=true, $js=true)
	{  	
		$admin = function_exists('is_admin') ? is_admin() : false;
		if ($css)	echo $this->my_script_url('css', ( $admin ? 'admin':'public'), true);
		if ($js) 	echo $this->my_script_url('js',  '', true);
	}
	
	public function my_loader_css_js_trigger()
	{  	
		$screen= is_admin()? 'admin' : 'public';
		$this->my_loader_css_js($css=$this->load_styles['css'][$screen], $js=$css=$this->load_styles['js'][$screen]);
	}

	// ================================== STYLES ================================== //
	
	private $all_enqueue_scripts=[];
	public function init_loadscripts($override_array)
	{
	  $initial_scripts=
	  [
		//
		'my_javascript'	=> ['screen'=>['admin'=>0, 'public'=>0], 'urls'=>[
			'js' => $this->my_script_url('js','')
		]],
		'my_style_public'=> ['screen'=>['admin'=>0, 'public'=>0], 'urls'=>[
			'css' => $this->my_script_url('css','public')
		]],
		'my_style_admin'=> ['screen'=>['admin'=>0, 'public'=>0], 'urls'=>[
			'css' => $this->my_script_url('css','admin')
		]]
	  ];

	  $this->load_scripts_override = [];
	  if ( method_exists($this, 'define_load_links') ) $this->define_load_links();
	  $initial_scripts = array_merge($initial_scripts, $this->load_scripts_override);

	  $this->all_enqueue_scripts = array_replace_recursive($initial_scripts, $override_array);
	}
	
	
	public function my_styles_hook($pure_php=false) {
		$front_or_back = function_exists('is_admin') && is_admin() ? 'admin' : 'public';
		$current_screen = $front_or_back=='public' ? 'wp' : 'admin';  // gets: admin or public
		foreach ($this->all_enqueue_scripts as $name=>$block)
		{
			if($block['screen'][$front_or_back])
			{
				if (!empty($block['urls']))
				foreach ($block['urls'] as $JS_or_CSS=>$url)
				{
					$type_ = ($JS_or_CSS=="js") ? 'script' : ($JS_or_CSS=="css" ? 'style' : $JS_or_CSS);
					if ($pure_php===true) 
					{
						if ($type_=='style')
							echo '<link rel="stylesheet" href="'. $this::sanitize_url($url).'" type="text/css" media="all" />';
						else {
							echo '<script src="'.$this::sanitize_url($url).'"></script>';
						}
					}
					else
					{
						$this->register_stylescript($current_screen, $type_, $name, $url);
					}
				}
			}
		}
	}

	//example testmode : pastebin_com/bUncPcFD
	
	public function filedate($file){
		return date("Y-M-D--H-i-s", filemtime($file) ); 
	}
  
	//if ( !$this->above_version('5.4') ) { echo("php_version is ". PHP_VERSION ." (quite old). HIGHLY recomended to update to higher version, or this program might not funciton normally ". __FILE__ ); }
	public function above_version($version= "5.4"){
		return version_compare(phpversion(), $version, '>=');
	}

	public function noindex_meta_tag() { return '<meta name="robots" content="noindex, nofollow">'; }

    public function randomId($divider="_"){ return "K".time() . $divider . rand(1,9).rand(0,9999999999999); }

	// #####################################
	//check either GET or argv (note, in argv, the parameters should be quoted, like: 
	//       * * * * *  /usr/bin/php -q /var/www/example/wp-cron.php "cron_cycle=3&param=1"
	public static function argvs($key='', $default='')
	{
		global $argv;
		$array = [];
		if (!empty($argv[1])) {
			parse_str($argv[1], $array);
		}
		return $array;
	}
	public static function argv($key='', $default='')
	{
		global $argv;
		$array = [];
		if (!empty($argv[1])) {
			parse_str($argv[1], $array);
		}
		return empty($key) ? $array : ( array_key_exists($key, $array) ? $array[$key] : $default);
	}
	public function get($key='', $default='')
	{
		return empty($key) ? $_GET : ( array_key_exists($key, $_GET) ? $_GET[$key] : $default);
	}
	// THIS IS ALWAYS SANITIZED IN IMPLEMENTATIONS
	public function post($key='', $parsePhpInput=true){
		if (!empty($_POST)) {
			$array = $_POST;
		} else {
			$input = file_get_contents('php://input');
			if (!empty($input)) {
				if (!$parsePhpInput) return $input;
				$array= json_decode($input, true);
				if (is_null($array))
				{
					return $input;
				}
			} else{
				$array = [];
			}
		}
		return (!empty($key) ? $this->array_value($array,$key) : $array);
	}
	public function inputData(){
		return $this->POST();
	}
	
	public function argv_or_get($key='', $default=null)
	{
		global $argv;
		$array = [];
		if (!empty($argv[1])) {
			parse_str($argv[1], $array);
		}
		else {
			$array=$this->Get();
		}
		return !empty($key) ? $this->array_value($array,$key,$default) : $array;
	}
	public function argv_is_set($key)
	{
		global $argv;
		$array = [];
		if (!empty($argv[1])) {
			parse_str($argv[1], $array);
		}
		return array_key_exists($key,$array);
	}
	public function argv_or_get_is_set($key)
	{
		global $argv;
		$array = [];
		if (!empty($argv[1])) {
			parse_str($argv[1], $array);
		}
		else {
			$array=$this->Get();
		}
		return array_key_exists($key,$array);
	}

	public static function argv_called_file()
	{
		global $argv;
		return !empty($argv[0]) ? $argv[0] : '';
	}

	public static function is_cron(){ return self::is_cli(); }
	public static function is_cli(){
		try{
			if( defined('STDIN') ) return true;  
			if( empty($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['HTTP_USER_AGENT']) && count($_SERVER['argv']) > 0)  return true; 
			$sapi_type = php_sapi_name();
			return (substr($sapi_type, 0, 3) == 'cli' || empty($_SERVER['REMOTE_ADDR']));
		}
		catch(\Exception $ex){
			
		}
		return false;
	}
	

	public static function array_to_argv($array=null)
	{
		$str = http_build_query($array);
		return $str;
	}

	//convert command line to array like _GET
	public function argv_to_array($argv_=null,$index=1)
	{
		$array=[];
		if (!empty($argv_[$index])) parse_str($argv_[$index], $array);	
		return $array;
	}


	public static function php_path_current(){ return PHP_BINARY; }
	
	public static function command_current($addToParams=''){
		return self::php_path_current().' "'.self::ArgvFile().'" "'.self::array_to_argv(self::Argv()) .$addToParams.'"';
	}

	public static function is_simple_type($var){
		return ( is_string($var) || is_numeric($var) || is_bool($var) );
	}


	public function argvs_get_post($argv_,$index=1)
	{
		//$array=[];
		//if (!empty($argv_[$index])) parse_str($argv_[$index], $array);	
		return $array;
	}
	
	public function serialize_argv($argvs)
	{
		if(empty($argvs) || !is_array($argvs)) return $argvs;
	
		$new_ar=[];
		foreach($argvs as $key=>$value)
		{
			if(stripos($value,'=')===false)
			{
				$new_ar[$key] = $value;
			}
			else{
				parse_str($argvs[$key], $params);
				$key1=array_keys($params)[0];
				if(!empty($argvs) && is_array($params))
					$new_ar[$key1] =  $params[$key1];
			}
		}
		return $new_ar;
	}
	
	// #####################################

	public function array_fields($array, $parent="plugin_slug[sample][sub]", $pairs=false)
	{ 
		echo '<div class="inpHolder">';

		echo '<div class="inputsBlock">';
		if (is_array($array) && !empty($array)) 
		{
			foreach ($array as $optName=>$value)
			{
				echo $this->field_out_helper1($parent, $optName, $value, $pairs) ;
			}
		}

		$sample_field = $this->field_out_helper1($parent, "", "", $pairs);
		//echo $sample_field;
		echo '</div>';
		?>
				<?php $unique = $this->sanitize_nonoword($parent); ?>
		<a class="button" href="#" onclick="return <?php echo $unique;?>_addNewArrayField_k(this);" class="addNewArrayInput"><?php _e('Add New');?></a>
		<script>
		function <?php echo $unique;?>_addNewArrayField_k(el)
		{ 
			var targetEl = el.parentNode.parentNode.parentNode.getElementsByClassName("eachInputBlock")[0];
			var rand=  Math.random().toString(36).substring(2); 
			var newElString = targetEl.outerHTML.replace( /(inputKey_[\w]*)/g, "inputKey_"+rand).replace(/value="(.*?)"/g, 'value=""');
			targetEl.parentNode.insertAdjacentHTML("beforeend", newElString);
			return false;
		}
		</script>
		<?php
		echo '</div>';
	}

	public function field_out_helper1($parent, $optName, $value, $pairs)
	{
		$output='<div class="eachInputBlock">';
		$rand= "inputKey_".rand(1,999999)."_".rand(1,999999)."_".rand(1,999999);  
		if (!$pairs) { 
			$key = (!empty($optName) ? $optName : $rand);
			$output .= '<input name="'.$parent.'['.$key.']"  class="eachInput each_'.$key.' regular-text" type="text" value="'.$value.'"  placeholder="" />';
		} else {
			$output .= '<input name="'.$parent.'['.$rand.'][name]"  class="eachInput each_'.$rand.' medium-text _key" type="text" value="'. (!empty($optName) ? $optName : "").'"  placeholder="name" />';
			$output .= '<input name="'.$parent.'['.$rand.'][value]"  class="eachInput each_'.$rand.' medium-text _value" type="text" value="'.$value.'"  placeholder="value" />';
		}
		$output .='</div>';
		return $output;
	}

	public function array_sanitize_text_field($ar)
	{
		$new=[];
		foreach($ar as $key=>$val)
		{
			$new[ $this->sanitize_text_field($val["name"]) ] = $this->sanitize_text_field($val["value"]);
		}
		return $new;
	}


	public function get_fb_name_regex($fb_url){
		preg_match('/'.preg_quote('^(?:https?://)?(?:www.|m.|touch.)?(?:facebook.com|fb(?:.me|.com))/(?!$)(?:(?:\w)#!/)?(?:pages/)?(?:[\w-]/)?(?:/)?(?:profile.php?id=)?([^/?\s])(?:/|&|?)?.*$/'), $fb_url, $n);
		return $n[1];
	}
	
	// shapeSpace
	public function allowed_html_tags() { 

		$allowed_tags = [
			'a' => [
				'class'=>[], 'href'=>[], 'rel'=>[], 'title'=>[],
			],
			'abbr' => [
				'title' => [],
			],
			'b' => [],
			'blockquote' => [
				'cite'  => [],
			],
			'cite' => [
				'title' => [],
			],
			'code' => [],
			'del' => [
				'datetime'=>[], 'title'=>[],
			],
			'dd' => [],
			'div' => [
				'class'=>[], 'title'=>[], 'style'=>[],
			],
			'dl' => [],
			'dt' => [],
			'em' => [],
			'h1' => [],
			'h2' => [],
			'h3' => [],
			'h4' => [],
			'h5' => [],
			'h6' => [],
			'i' => [],
			'img' => [
				'alt'=>[], 'class'=>[], 'height'=>[], 'src'=>[], 'width'=>[],
			],
			'li' => [
				'class'=>[],
			],
			'ol' => [
				'class'=>[],
			],
			'p' => [
				'class'=>[],
			],
			'q' => [
				'cite'=>[],'title'=>[],
			],
			'span' => [
				'class'=>[], 'title'=>[], 'style'=>[],
			],
			'strike' => [],
			'strong' => [],
			'ul' => [
				'class' => [],
			]
		];
		
		return $allowed_tags;
	}	
	
	public function display_errors()
	{
		ini_set('display_errors', 1);
		ini_set('display_startup_errors', 1);
		error_reporting( E_ALL );
	}
	
	public function log_errors($path=false, $callback=null)
	{
		//htaccess:
		//	php_flag log_errors on
		//	php_value error_log /home/FTP_username/public_html/error_log.txt
		ini_set("log_errors", 1);
		ini_set("error_log", $path ? $path : $_SERVER['DOCUMENT_ROOT']."/zzz___php-my-errors_".$this->my_site_variables__secret('rand_name', random_string(11)).".log");
		//error_log( "Hello, errors!" );	
		if (!is_null($callback)) set_error_handler($callback);
	}
	 
	public function javascript_headers()
	{
		session_cache_limiter('none');
		// http://stackoverflow.com/a/1385982/2377343
		//Caching with "CACHE CONTROL"
		header('Cache-control: max-age='.($year=60*60*24*365) .', public');
		//Caching with "EXPIRES"  (no need of EXPIRES when CACHE-CONTROL enabled)
		//header('Expires: '.gmdate(DATE_RFC1123,time()+$year));
		//To get best cacheability, send Last-Modified header and ...
		header('Last-Modified: '.gmdate(DATE_RFC1123,filemtime(__file__)));  //i.e.  1467220550 [it's 30 june,2016]
		//reply using: status 304 (with empty body) if browser sends If-Modified-Since header.... This is cheating a bit (doesn't verify the date), but remove if you dont want to be cached forever:
		// if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {  header('HTTP/1.1 304 Not Modified');   die();	}
		header("Content-type: application/javascript;  charset=utf-8");
	}
	
	public function set_log_dir($dir)
	{
		$this->logDir = $dir;
		$this->mkdir($this->logDir);
	}

	public function form_from_array($value, $fullKeyName='', $replace_spaces=false, $url='')
	{
		?>
		<form action="<?php echo $this::sanitize_url($url);?>" method="POST">
		<?php $this->input_fields_from_array_recursive2($value, $fullKeyName, $replace_spaces); ?>
		<input type="submit"></form>
		<?php 
	}

	public $arFieldRecRastKey="";
	public $arFieldAutoresize=true;
	public function input_fields_from_array_recursive2($value, $fullKeyName='', $replace_spaces=false, $cycle=0){
		$fullKeyName = $this::sanitize_text($fullKeyName);
		$current_key = preg_replace('/(.*)\[(.*?)\]/','$2', $fullKeyName);  //i.e. cc from: aa[bb][cc]
		$addButtonKeyName = "_ADD_BUTTON_";

		if ($cycle==0)
		{
			if ($this->arFieldAutoresize)
				$this->js_autosize_textarea('.valueTxtArea');
			?>
			<style>
			@media screen and (min-width: 1200px) { 
				#container-rebox { width: 700px; }
			}
			.ArrayFieldsContainer{box-shadow:0px 0px 5px black; font-size:0.8em; } 

			.ArrayFieldsContainer .keyBlock{transition:0.1s all; padding:1px 0px 0px 3px; zmargin:2px 0px 0px 10px; height: 100%; } 
			.ArrayFieldsContainer > .keyBlock { height: auto; first_block_needs_autoheigh:okk; }
			.ArrayFieldsContainer .keyBlock:hover{box-shadow:0 0 0 1px black; border:2px black;} 
			.ArrayFieldsContainer .new_block{ position:relative; border:1px black dashed; margin: 4px; transition:1s all; opacity:1; } 
			.ArrayFieldsContainer .removing{ height:0px!important; opacity:0!important; } 
			
			.ArrayFieldsContainer .each_arr, .ArrayFieldsContainer .each_ln { display:flex; padding:2px 2px 0 5px; zmargin:2px 2px 0 5px; flex-direction:column; } 

			.ArrayFieldsContainer .valueTxtArea{ margin:0 0 0 5px; float:right;     padding: 0px; }  

			.ArrayFieldsContainer .emptyTxtarea[style*="height:2"],.ArrayFieldsContainer .emptyTxtarea[style*="height:3"]{ background:#ff7e7e; _after_change_that_height_changes:"";} 
			.ArrayFieldsContainer .parentKey{ opacity:0.6; font-size:0.7em; } 
			.ArrayFieldsContainer .keyname{ min-width: 10px; } 
			.ArrayFieldsContainer .plus_minus_sign{ font-weight:bold; font-size:1em; } 
			.ArrayFieldsContainer .plus_minus_sign.sign_minus{ color:red; } 
			.ArrayFieldsContainer .plus_minus_sign.sign_plus{ color:green; font-size:2em; } 
			.ArrayFieldsContainer .addhrefBLock { display:block; text-align:center; } 
 
			.ArrayFieldsContainer .ancestor_array .keyBlock { display:flex; flex-direction:row; padding:10px 10px 0 10px;} 
			.ArrayFieldsContainer .ancestor_array .each_ln {display:flex; flex-direction: column;} 
			.ArrayFieldsContainer textarea { overflow-y: scroll;} 
			</style>
			<script>	
			var parentPredix= "parent_of_";
			function addNewBlock(evt)
			{
				evt.preventDefault();
				let targ=evt.target; 
				let parent=targ.parentNode.parentNode;
				let samplEl= $(parent).find('[class*="'+parentPredix+'"]').first().clone(); 
				let newKey= prompt("Input new key name");
				if (typeof PuvoxAddNewBlock_Filter_Func6273 === "function")
					newKey = PuvoxAddNewBlock_Filter_Func6273(newKey);
				if(!newKey) return;

				let oldKey= samplEl.data("key"); 
				samplEl.data("key", newKey);
				samplEl.find("textarea").text("");
				
				let parentkey= samplEl.data("parent"); 
				let newFullKey=parentkey +"["+newKey+"]"; 
				let newCont = samplEl.html().replace( new RegExp("(\\[|\>)"+oldKey+"\\b", "g"), '$1'+newKey );
				samplEl.html(newCont); 
				$(targ).closest(".new_block").find(".children_cont").first().append(samplEl);
				//targ.insertAdjacentHTML("beforebegin", newCont);
			}
			function removeCurrentBlock(evt, keynm)
			{
				evt.preventDefault();
				if (confirm("U sure to delete this block?"))
				{
					let targ	= evt.target; 
					let parent  = $(targ).closest(".new_block");
					parent.css("height", parent[0].offsetHeight +"px");
					window.setTimeout( function(){ parent.addClass("removing"); }, 10 );
					window.setTimeout( function(){ parent.remove(); }, 1000 );
				}
			}
			</script>
			<?php
			
			//initial
			echo '<div class="ArrayFieldsContainer">'; 
		}

		$keyName_BOLDED = preg_replace('/(.*)\[(.*?)\]/','<span class="parentKey">$1</span>[<b>$2</b>]', $fullKeyName);
		$is_parent_array = !is_array($value) || !array_key_exists('languages', $value) ;

		echo '<div class="keyBlock keyname_'.$fullKeyName.' '.($is_parent_array ? "regular_array" : "ancestor_array").'" style="background:'.$this->random_color('30').'; ">';
		$style = ' style=""';

		if ( !is_array($value) ){
			echo '
			<div class="each_ln" '.$style.'>
				<div class="keyname">'. $keyName_BOLDED .'</div>
				<div class="txtar"><textarea class="valueTxtArea '. ( trim($value) =='' ? 'emptyTxtarea' :'') .' key_'.$current_key.'"  name="'.$fullKeyName.'" placeholder="">'. trim($value) .'</textarea></div>
			</div>';
		}
		else{
			echo  '
			<div class="each_arr" '.$style.'> 
				<div class="keyname">'. $keyName_BOLDED .'</div>
				<div class="children_cont">';
			$addButtonOnKey=false;
			foreach ($value as $keyname1=>$value1){
				if ($keyname1==$addButtonKeyName) {
					continue;
				}
				$keyname1 = $this::sanitize_text($keyname1);
				$newKeyNm = $fullKeyName.'['.$keyname1.']';
				echo '<div class="new_block parent_of_" data-parent="'.$newKeyNm.'" data-key="'.$keyname1.'">';

				// if array contains empty_placeholder, then it was meant for Adding/Removing for keys
				if (array_key_exists($addButtonKeyName, $value))
				{
					$addButtonOnKey=true;
					echo '<a href="#" onclick="removeCurrentBlock(event,\''.$newKeyNm.'\')"><span class="plus_minus_sign sign_minus">-delete</span></a>';
				}
				
				$this->arFieldRecRastKey=$keyname1;
				$this->input_fields_from_array_recursive2($value1, $newKeyNm,  $replace_spaces, $cycle+1);
				echo '</div>';
			}
			echo '</div>';

			if ($addButtonOnKey)
			{
				echo '<a class="addhrefBLock" href="#" onclick="addNewBlock(event)"><span class="plus_minus_sign sign_plus">+ADD</span></a>';
			}
			echo '
			</div>';	
		}
		echo '</div>';		
		if ($cycle==0)
		{
			echo '</div>'; 
		}
	}
	 

	public function move_folder_contents($from, $to)
	{
		foreach( glob($from ."/*") as $each)
		{
			$target=$to."/".basename($each);
			if(is_dir($target)) {
				//$this->rmdir_recursive($target);
			}
			elseif(is_file($target)) 
			{
				@unlink($target);
				//rename($each, $target);
			}
		}
	}

	public function js_debugmode($name='debugmode')
	{
		if ( ! defined('PUVOX_js_debugmode') )
		{
			define('PUVOX_js_debugmode', true);
			$this->debugmode_script = '<script>debug_mode_ ="true";</script>';
			add_action('wp_head',	function(){ echo $this->debugmode_script; }, 1); 
			add_action('admin_head',function(){ echo $this->debugmode_script; }, 1);
		}
	}
	

	// REGEXES
	public function preg_replace_if_inside($str, $phrase, $start, $end)
	{ 
		return preg_replace("/(?<=". preg_quote($start).")(.*?|)". $phrase ."(.*?|)(?=".preg_quote($end).")/si", '', $str);
	}
		
	public function split_by_X_not_inside_YZ($str, $by, $y, $z) //y&z: not inside
	{ 
		return preg_split( '/'.$by.'+(?=(?:(?:[^'.$y.']*"){2})*[^'.$z.']*$)/si', $str );
	}

	public function get_from_X_till_Y_not_inside($content, $from, $till, $array, $regex_index=0, $remove_outside=true) 
	{
		$from_ = $from; //this.escapeRegExp(from);
		preg_match( '/'. $from_ ."(.*)/si", $content, $matches );
		$result = "";
		if ($matches != null )
		{
			$regex_index_final = $regex_index+1;
			$foundPart= $matches[$regex_index_final];
			$splits = $this->split_by_X_not_inside($foundPart, $till, $array);
			$result = trim($splits[0]);
		}
		return $result;
	}
	// ############### REGEXES ###############
	
	 
	
	public function get_current_buffer_clean($func_name=false){  ob_start(); if ($func_name) {$args=func_get_args(); call_user_func_array($func_name, $args);}  $cont= ob_get_clean(); ob_flush(); return $cont; }
	
	// youtube old funcs - get_video_info: pastebin_com/HWeaHFVJ

	// php memory ram shared caching: pastebin_com/CpkkA43x
	

	
	
	
	// ### ERRORS TABLE ### //
	public function sqlite_error_db_init($db_dir=false, $db_name=false, $PLA=[], $TELEGRAM=[])
	{
		$sqlite_db_path= $db_dir . $db_name;
		$db = $this->sqlite_db_init( $sqlite_db_path );
		$this->SQLITE_ERRORS_DB_INSTANCE = $db;
	}
	public function sqlite_error_table_create($db, $tablename)
	{
		$db->exec( "CREATE TABLE IF NOT EXISTS {$tablename} (
			id INTEGER PRIMARY KEY, 
			title TEXT, 
			text TEXT, 
			status TEXT,
			url TEXT, 
			time INTEGER)"
		);
	}
	public function sqlite_error_log($db, $tablename, $title, $text, $status=null)
	{
		$db = $db ?: $this->SQLITE_ERRORS_DB_INSTANCE;
		$this->sqlite_error_table_create($db, $tablename);
        $sql = 'INSERT INTO '.$tablename.'(title,text,status,url,time) VALUES( "'.($title ?: '').'", "'.($text ?: '').'", "'.($status ?: '').'", "'.$this->requestURL.'" , "'. time().'")';
        //if ( ! $this->helpers->is_localhost ) $this->helpers->telegramMsg( $this->tg_errorbot['key'], $this->tg_errorbot['chat'], $title .'::::'. $text);
		return $db->exec($sql);
    }
	public function slog($title, $text, $db=null, $tablename='all_errors')
	{
		return $this->sqlite_error_log($db, $tablename, $title, $text);
    }
	



	// ##################### //
 


	
	#region PHP to PHP/C# encryption/description :
	// https://puvox.software/blog/two-way-encryption-decryption-between-php-and-c-sharp/
	//public static class EncryptDecrypt {
		public static function encrypt($plaintext, $password, $method= 'aes-256-cbc'){
			self::helper__encrypt_decrypt_stream($password);
			return base64_encode(openssl_encrypt($plaintext, $method, self::password_shuffled, OPENSSL_RAW_DATA, self::iv));
		}

		public static function decrypt($encrypted, $password, $method= 'aes-256-cbc'){
			self::helper__encrypt_decrypt_stream($password);
			return openssl_decrypt(base64_decode($encrypted), $method, self::password_shuffled, OPENSSL_RAW_DATA, self::iv);
		}
		
		public static function helper__encrypt_decrypt_stream($password, $method= 'aes-256-cbc'){
			// Must be exact 32 chars (256 bit)
			self::$password_shuffled = substr(hash('sha256', $password, true), 0, 32);		
			// IV must be exact 16 chars (128 bit)
			self::$iv = chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0);
		}
	//}
	#endregion 




	
	// --- LOCAL OPTIONS APPROACH ---
	public function site_options_path(){
		return $this->baseDIR.'/_site_options.json';
	}
	public function get_site_options_all(){
		if (property_exists($this,'JSON_CACHED_ALL_OPTIONS'))
		{
			return $this->JSON_CACHED_ALL_OPTIONS;
		}
		$path = $this->site_options_path();
		if (!file_exists($path)){
			$this->localdata_set($path,'{"all_options":{}}');
		}
		$this->JSON_CACHED_ALL_OPTIONS = json_decode( $this->file_get_contents($path), []);
		return $this->JSON_CACHED_ALL_OPTIONS;
	}
	public function save_site_options_all($opts){
		$path = $this->site_options_path();
		$this->localdata_set($path, $opts);
	}

	public function update_option_json($option_name, $value){
		$path = $this->site_options_path();
		$opts=  $this->get_site_options_all();
		if (!is_array($option_name)){
			$opts['all_options'][$option_name] = $value;
		}
		else{ 
			$c = count($option_name);
			$o = $option_name;
			if ($c==2) $opts[ $o[0] ][ $o[1] ] = $value;
			if ($c==3) $opts[ $o[0] ][ $o[1] ][ $o[2] ] = $value;
			if ($c==4) $opts[ $o[0] ][ $o[1] ][ $o[2] ][ $o[3] ] = $value;
			if ($c==5) $opts[ $o[0] ][ $o[1] ][ $o[2] ][ $o[3] ][ $o[4] ] = $value;
			if ($c==6) $opts[ $o[0] ][ $o[1] ][ $o[2] ][ $o[3] ][ $o[4] ][ $o[5] ] = $value;
			if ($c==7) $opts[ $o[0] ][ $o[1] ][ $o[2] ][ $o[3] ][ $o[4] ][ $o[5] ][ $o[6] ] = $value;
			if ($c==8) $opts[ $o[0] ][ $o[1] ][ $o[2] ][ $o[3] ][ $o[4] ][ $o[5] ][ $o[6] ][ $o[7] ] = $value;
		}
		$this->JSON_CACHED_ALL_OPTIONS = $opts;
		try { $this->localdata_set($path, json_encode($opts)); } catch (\Exception $e){ die($e->getMessage()); }
		return true;
	}


	public function get_option_json($option_name, $default_value=''){
		$opts = $this->get_site_options_all() ;
		if (!is_array($option_name)){
			return ( array_key_exists($option_name, $opts['all_options']) ? $opts['all_options'][$option_name] : $default_value );
		}
		else{
			$new_val = $opts;
			foreach($option_name as $val)
			{
				$new_val = $new_val[$val];
			}
			return $new_val;
		}
	}
	public function set_default_options( $array, $key_name ){
		$opts = $this->get_site_options_all();
		if (!array_key_exists($key_name, $opts)){
			$opts[$key_name]=[];
		}
		foreach($array as $key=>$value){
			if ( !array_key_exists($key, $opts[$key_name]) ){
				$opts[$key_name][$key] = $value;
				$this->update_option_json([$key_name, $key],  $value);
			}
		}
		return $opts[$key_name];
	}

	public function get_child($array, $name)
	{
		if (!is_array($name))
			return $array[$name];
		else{
			$new_val = $array;
			foreach($name as $val)
			{
				$new_val = $new_val[$val];
			}
			return $new_val;
		}
	}
	//




	// algo-related functions
	public function highest($values, $length)
	{
		$amount= count($values);
		$last= max(array_keys($values));//might be empty initial X keys, so dont use "count"
		$index = $last;
		$highest= $values[$last];
		if ($length >= 1){ 
			for ($i=0; $i<min($amount, $length); $i++){ 
				$idx= $last-$i;
				if (array_key_exists($idx, $values)){
					if ( $values[$idx] > $highest){ 
						$highest = $values[$idx];
						$index=$idx;
					}
				}
			}
		}
		return ["value"=>$highest, "index"=>$index];
	}
	
	public function lowest($values, $length)
	{
		$amount= count($values);
		$last= max(array_keys($values));//might be empty initial X keys, so dont use "count"
		$index = $last;
		$lowest= $values[$last];
		if ($length >= 1){
			for ($i=0; $i<min($amount, $length); $i++){ 
				$idx= $last-$i;
				if (array_key_exists($idx, $values)){
					if ( $values[$idx] < $lowest){ 
						$lowest = $values[$idx];
						$index=$idx;
					}
				}
			}
		}
		return ["value"=>$lowest, "index"=>$index];
	}
	
	public function zip_folder ($input_folder, $output_zip_file) {
		$zipClass = new ZipArchive();
		if($input_folder !== false && $output_zip_file !== false)
		{
			$res = $zipClass->open($output_zip_file, \ZipArchive::CREATE);
			if($res === TRUE)   {
				// Add a Dir with Files and Subdirs to the archive
				$foldername = basename($input_folder);
				$zipClass->addEmptyDir($foldername);
				$foldername .= '/';         $input_folder .= '/';
				// Read all Files in Dir
				$dir = opendir ($input_folder);
				while ($file = readdir($dir))    {
					if ($file == '.' || $file == '..') continue;
					// Rekursiv, If dir: GoodZipArchive::addDir(), else ::File();
					$do = (filetype( $input_folder . $file) == 'dir') ? 'addDir' : 'addFile';
					$zipClass->$do($input_folder . $file, $foldername . $file);
				}
				$zipClass->close(); 
			}
			else   { exit ('Could not create a zip archive, migth be write permissions or other reason. Contact admin.'); }
		}
	} 



	#region ================ INIT ================
	public function init_defaults()
	{
		try{
			//some of this can be overwriten by init_module
			$this->ip				= $this->get_visitor_ip();
			$this->isMobile			= false;
			$this->isWP				= defined("ABSPATH");
			$this->is_cli			= $this->is_cli();
			$this->is_development 	= defined("_puvox_machine_") ;			// set only in devmachine (in "my_superglobals.php" and in "EnvVariables")
			//only web parts
			$this->is_https			= $this->is_cli ? false 	: ((!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS']!=='off') || (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT']==443) );
			$this->https			= $this->is_cli ? 'https://': ( $this->is_https ? 'https://' : 'http://');
			$this->domainCurrent	= $this->is_cli ? '' 		: (!empty($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');
			$this->domain			= $this->is_cli ? '' 		: $this->https . $this->domainCurrent;
			$this->requestURL		= $this->is_cli ? '' 		: $this->array_value($_SERVER,'REQUEST_URI'); $this->requestURI=$this->requestURL;
			$this->currentURL		= $this->is_cli ? '' 		: $this->domain.$this->requestURL; 
			$this->domainCurrentWithoutPort=$this->is_cli ? ''  : $this->array_value( parse_url($this->currentURL),'host');
			$this->is_localhost     = $this->is_cli ? false 	: $this->is_localhost();
			// others 
			$this->empty_image		= "data:image/svg+xml;utf8,&lt;svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1 1'&gt;&lt;/svg&gt;";
			$this->extra_options_enabled=false;
		}
		catch(\Exception $ex){
			$this->var_dump($ex);
		}
	}

	// only when called explicitly, i.e. from plugin or module
	public function init_module($args=[])
	{
		// Because this is a class, we don't use "__FILE__" & "__DIR__" here, but "Reflection" to refer to caller file ####
		$isClass=(array_key_exists('class',$args) && !empty($args['class']));
		$reflection				= $isClass ? (new \ReflectionClass( $args['class'] )) : null; 
		$this->module_NAMESPACE	= $isClass ? $reflection->getNamespaceName() : (array_key_exists('NAMESPACE',$args) ? $args['NAMESPACE'] : "EXAMPLE"); 	// get parent's namespace name
		$this->moduleFILE		= $isClass ? $reflection->getFileName() : (array_key_exists('FILE',$args) ? $args['FILE'] : __FILE__); 		// set plugin's main file path
		$this->moduleDIR		= (array_key_exists('DIR',$args) ? $args['DIR'] : dirname($this->moduleFILE) ).DIRECTORY_SEPARATOR ;  	// set plugin's dir	path
		$this->prefix			= strtolower( preg_replace('![^A-Z]+!', '', $this->module_NAMESPACE) );// get prefix from current namespace initials of UpperCase characters (i.e. MyPluginNamespace-->MPN)
		$this->prefix_			= $this->prefix .'_';
		
		//$backtrace = debug_backtrace(); 	$this->_index_file_		= $backtrace[0]['file'];		$this->_index_dir_		= dirname($this->_index_file_);   
		// if this class is used just as a helper php library
		if (!$this->isWP || array_key_exists('homeFOLDER', $args))
		{
			$this->homeFOLDER 		= $args['homeFOLDER'];
			$this->homeURL 			= $this->domain.$this->homeFOLDER;
			$this->doc_root_real 	= $this->slash_replace_direction(str_replace( $this->homeFOLDER,'',  $this->slash_replace_direction($this->moduleDIR, 'forward') ), 'forward'); // even for symlinked;
			$this->moduleURL		= str_replace($this->doc_root_real,'',  $this->slash_replace_direction($this->moduleDIR, 'forward')) ;
		}
		// else, if this class is used as plugin class (used mostly by Puvox.Software)
		else
		{
			$this->wpURL 			= network_home_url('/');						// WP installation home 
			$this->wpFOLDER 		= network_home_url('/', 'relative');			// WP folder 
			$this->homeURL			= home_url('/');								// current sub/site home url
			$this->homeFOLDER		= home_url('/', 'relative');					// current sub/site home folder
			$this->moduleURL		= plugin_dir_url($this->moduleFILE);			//
			$this->plugin_entryfile	= defined( $this->module_NAMESPACE.'\\PLUGIN_ENTRY_FILE') ? constant($this->module_NAMESPACE.'\\PLUGIN_ENTRY_FILE') : $this->moduleFILE;
		} 
		$this->httpsReal		= preg_replace('/(http(s|):\/\/)(.*)/i', '$1', $this->homeURL);
		$this->domainReal		= $this->get_domain_from_url($this->homeURL);  $this->domainNaked=$this->domainReal;
		$this->domain			= $this->httpsReal.$this->domainReal;
		$this->domain_schemeless= '//'.$this->domainReal;
		$this->site_slug			= str_ireplace('.','_',   $this->domainReal);
		$this->urlAfterHome		= substr($this->requestURL, strlen($this->homeFOLDER) );
		$this->pathAfterHome	= parse_url($this->urlAfterHome, PHP_URL_PATH);
		$this->homeUrlStripped	= $this->remove_http_www($this->homeURL); 

		$this->baseFILE			= $this->moduleFILE;								//
		$this->baseDIR			= $this->moduleDIR.'/';								//
		$this->baseURL			= property_exists($this, 'baseURL') ? $this->baseURL : $this->moduleURL; //( stripos(__FILE__, 'wp-content'.DIRECTORY_SEPARATOR.'themes') !== false ? themeURL ... 
		$this->baseScriptsFolder= property_exists($this, 'baseScriptsFolder') ? $this->baseScriptsFolder : '';
		$this->baseScriptsDir	= $this->baseDIR . $this->baseScriptsFolder; 
		$this->baseScriptsUrl	= $this->baseURL . $this->baseScriptsFolder; 
		$this->changeable_JS_CSS_version = ( file_exists($file = $this->baseScriptsDir.'/style-public.css') ? 'date_'.filemtime($file) : $this->sanitize_key($this->domainReal).date('m') );
		
		// others
		$this->is_development 	= defined("_puvox_machine_") ;			// set only in devmachine (in "my_superglobals.php" and in "EnvVariables")
		if ($this->is_development)
		{
			$this->display_errors();
			if (!property_exists($this,'triggered_dev_shutdown_hook'))
			{
				$this->triggered_dev_shutdown_hook=true;
				register_shutdown_function( function(){ if (substr(ob_get_contents(), -7)=='</html>') echo('<div data-debug-memory-limit="'. ini_get('memory_limit').'" data-debug-WP_MEMORY_LIMIT="'. (defined('WP_MEMORY_LIMIT') ? WP_MEMORY_LIMIT : '').'"></div>');} ); 
				$this->START_TIME1 = microtime(true);
				register_shutdown_function( function(){ if (substr(ob_get_contents(), -7)=='</html>') echo('<div data-debug-time-load="'. (microtime(true)-$this->START_TIME1).'"></div>');} );
			}
		} 
	}

	
	private $privateAppName__ = null;
	public function set_app_name ($name){ $this->privateAppName__ = $name; }

	public function get_app_name() { 
		if (!$this->privateAppName__){
			throw new \Exception ('Before you start using caching functions, please at first define your appplication\'s name(identifier) at first with .->set_app_name ("whatever_my_app_name"), so it will get its own cache-storage identifier');
		}
		return $this->privateAppName__; 
	}
	
	public function __construct()
	{
		$this->file = new \Puvox\tempclass_file($this);
		$this->cache = new \Puvox\tempclass_cache($this);
		$this->init_defaults();
	}
	#endregion



	// ##############################################################
	// ##################### CUSTOM FUNCTIONS #######################
	// ##############################################################
	// DO_NOT_OT_REMOVE_THIS_LINE___
	// ##############################################################
	// ################# END - CUSTOM FUNCTIONS #####################
	// ##############################################################

} // class



	
#[\AllowDynamicProperties]
class tempclass_file {

	public function __construct($parent){
		$this->parent = $parent;
	}

	public function temp_dir() {
		return $this->parent::slash_trailing (sys_get_temp_dir());
	}

	/*
	static function _getTempDir()
    {
        $tmp_locations = array('/tmp', '/var/tmp', 'c:\WUTemp', 'c:\temp', 'c:\windows\temp', 'c:\winnt\temp');
        $tmp = ini_get('upload_tmp_dir');
        if (!strlen($tmp)) {
            $tmp = getenv('TMPDIR');
        }
        while (!strlen($tmp) && count($tmp_locations)) {
            $tmp_check = array_shift($tmp_locations);
            if (@is_dir($tmp_check)) {
                $tmp = $tmp_check;
            }
        }
        return strlen($tmp) ? $tmp : false;
    }
	*/

	public function exists ($filePath) {
		return file_exists($filePath) || is_dir($filePath);
	}

	public function mtime ($filePath) {
		if ($this->exists($filePath)) {
			return filemtime ($filePath)*1000;
		} else {
			return null;
		}
	}

	public function unlink ($filePath) {
		return unlink($filePath);
	}

	//public function mkdir($dest, $permissions=0755, $create=true){ return $this->mkdir_recursive($dest, $permissions, $create); }
	//public function mkdir_recursive(
	public function create_directory($dirPath, $permissions=0755, $create=true) {
		if(!is_dir($dirPath)){
			//at first, recursively create parent directory if doesn't exist
			$parent = dirname($dirPath);
			if( $parent && !is_dir($parent) ){ $this->create_directory($parent, $permissions, $create); }
			else {
				if ( is_writable( $parent ) ){
					return mkdir($dirPath, $permissions, $create); 
				}
				else{
					var_dump("No permission to create directory: $parent");
					return false;
				}
			}
		}
		return true;
	}

	//rmdir rmdir_recursive
	public function delete_directory($dirPath) {
		if(!empty($dirPath) && is_dir($dirPath) ){
			$dir  = new \RecursiveDirectoryIterator($dirPath, \RecursiveDirectoryIterator::SKIP_DOTS); //upper dirs not included,otherwise DISASTER HAPPENS :)
			$files = new \RecursiveIteratorIterator($dir, \RecursiveIteratorIterator::CHILD_FIRST);
			foreach ($files as $path) $path->isDir() && !$path->isLink() ? $this->delete_directory($path->getPathname()) : unlink($path->getPathname()); //{if (is_file($path)) {unlink($path);} else {$empty_dirs[] = $path;} } if (!empty($empty_dirs)) {foreach ($empty_dirs as $eachDir) {rmdir($eachDir);}} 
			rmdir($dirPath);
			return true;
		}
		return true;
		//include_once(ABSPATH.'/wp-admin/includes/class-wp-filesystem-base.php');
		//\WP_Filesystem_Base::rmdir($fullPath, true);
	}

	public function read($filePath, $defaultContent = ''){
		if (!$this->exists($filePath)){
			return $defaultContent;
		}
		return $this->parent->file_get_contents($filePath);
	}

	public function write($filePath, $content){
		$dir = dirname($filePath);
		$this->create_directory($dir);
		return $this->parent->file_put_contents($filePath, $content);
	}
	
	public function get_files_list_from_dir ($dir) {
		$filesList = [];
		// todo
		return $filesList;
	}

    /* 
	public function emptyDir($dirPath){
		return array_map( 'unlink', array_filter((array) glob("$dirPath/*") ) );
	}
	public function copy_recursive($source, $dest, $permissions = 0755){
		if (is_link($source))	{ return symlink(readlink($source), $dest); }
		elseif (is_file($source))	{ 
			if(!file_exists(dirname($dest))){$this->create_directory(dirname($dest), $permissions, true); }
			if(!copy($source, $dest)) {echo "not copied ($source ---> $dest )";} return true; 
		}
		elseif (is_dir($source))	{ 
			$this->create_directory($dest, $permissions, true); 
			foreach (glob($source.'/*') as $each){	$basen= basename($each);
				if ($basen != '.' && $basen != '..') { $this->copy_recursive("$each", "$dest/$basen", $permissions);	}
			}
		}
	}
	*/

} // templcass_file


#[\AllowDynamicProperties]
class tempclass_cache {
	
	public function __construct($parent){
		$this->parent = $parent;
		$this->file = new \Puvox\tempclass_cache_file($this->parent);
	}

} // templcass_cache

#[\AllowDynamicProperties]
class tempclass_cache_file {

	public function __construct($parent){
		$this->parent = $parent;
	}

	public $customCacheDir = null;

	public function get_dir() {
		if (!$this->customCacheDir) { 
			$this->customCacheDir = $this->parent->file->tempDir();
		}
		$finaldir = $this->parent::slash_trailing ($this->customCacheDir . '_cache_' . $this->parent->get_app_name());
		return $finaldir; 
	}

	public function set_dir($dir, $auto_clear_seconds=null){ 
		if($dir) $this->customCacheDir = $dir;
		$res = $this->parent->file->create_directory($this->customCacheDir);
		if( !is_null($auto_clear_seconds))
		{
			throw new \Exception("Not implemented yet! 345346");
			//$this->clearCacheDir($auto_clear_seconds); 
		}
		return $res;
	}

	public function file_path($uniqFileName){
		$uniqFileName = is_string($uniqFileName) || is_numeric($uniqFileName) ? $uniqFileName : json_encode($uniqFileName);
		$uniqFileName = $this->parent::sanitize_key_dashed($this->parent::chars_from_start($uniqFileName, 15)) . "_" . md5($uniqFileName);
		$filePath= $this->get_dir() . $uniqFileName . "_tmp";
		return $filePath;
	}
	//
	public function get($uniqFileName, $defaultContent='', $expire_seconds=8640000, $decode = true)
	{
		$filePath = $this->file_path($uniqFileName);
		if ( $this->parent->file->exists($filePath) ){
			if ($this->parent->file->mtime($filePath) + $expire_seconds*1000 < time()*1000){
				$this->parent->file->unlink($filePath);
				return $defaultContent;
			}
			else{	
				$cont = $this->parent->file->read($filePath, null);
				// if specifically array, then on empty, reckon as array
				if ($cont===null)
				{
					return $defaultContent;
				}
				if ($decode){
					try{
						return json_decode($cont, true);
					}
					catch(\Exception $e){
						return $cont;
					}
				}
				else{
					return $cont;
				}
			}
		}
		else {
			return $defaultContent;
		}
	}

	public function set($uniqFileName, $content)
	{
		$filePath= $this->file_path($uniqFileName);
		$contentFinal = is_string($content) ? $content : ((is_array($content) || parent.isObject($content)) ? json_encode($content) : $content);
		return $this->parent->file->write($filePath, $contentFinal);
	}


	//
	// public function writeFileAppendJson(filePath, jsonContent, callback){
	// 	try{
	// 		var callback = callback || function(){};
	// 		var self = this;
	// 		puvox_library.modules('fs').readFile(filePath, 'utf8', function(err,data) {
	// 			let json = {};
	// 			if (typeof data !="undefined" && data!=''){
	// 				json=JSON.parse(data);
	// 			}
	// 			let jsonNew = self.jsonConcat(json, jsonContent);
	// 			let content = JSON.stringify(jsonNew);
	// 			puvox_library.modules('fs').writeFile(filePath, content, 'utf8', function(callback_) {
	// 			}); 
	// 		});
	// 	}
	// 	catch(e){
	// 		console.log("writeFileAppendJson", e); 
	// 	}
	// },
	public $containerDefaultPrefix= "_cached_ids_";
	public $tempIds =[];
	public function id_for_content($slugOrContent){
		return md5($this->parent->is_simple_type($slugOrContent) ? $slugOrContent : json_encode($slugOrContent)); 
	}

	public function exists_id($containerSlug, $id){
		return array_key_exists($id, $this->get_ids($containerSlug));
	}

	public function get_ids($containerSlug) {
		if (! array_key_exists($containerSlug, $this->tempIds)) {
			$content = $this->parent->file->read($this->get_dir() . $this->containerDefaultPrefix . $containerSlug, '{}');
			$this->tempIds[$containerSlug] = json_decode($content, true);
		}
		return $this->tempIds[$containerSlug];
	}

	public function set_ids($containerSlug, $idsDict) {
		$this->tempIds[$containerSlug] = $idsDict;
		return $this->parent->file->write($this->get_dir() . $this->containerDefaultPrefix . $containerSlug, json_encode($this->tempIds[$containerSlug]));
	}

	public function add_id($containerSlug, $id){
		$ids = $this->get_ids($containerSlug);
		$ids[$id] = 1;
		$this->set_ids($containerSlug, $ids);
	}

	public function add_id_if_not_exists($containerSlug, $id){
		if (! $this->exists_id($containerSlug, $id)){
			$this->add_id($containerSlug, $id);
			return true;
		}
		return false;
	}

	/*
	#region ################  CACHE ###############
	public function cache_get($key, $default=null){
		return call_user_func([$this, "cache_get_{$this->CACHE_CHOSEN_PROGRAM}"], $key, $default);
	}
	public function cache_set($key, $data, $seconds = 8640000){
		return call_user_func([$this, "cache_set_{$this->CACHE_CHOSEN_PROGRAM}"], $key, $data, $seconds);
	}
	public function cache_append($key, $data, $seconds = 8640000){
		$existing_arr = call_user_func([$this, "cache_get_{$this->CACHE_CHOSEN_PROGRAM}"], $key, []);
		$new  = $this->is_array($existing_arr) ? $this->array_merge($existing_arr,$data) : $existing_arr. $data;
		$final_value  = ($this->is_simple_type($new) ? $new : serialize($new));
		return call_user_func([$this, "cache_set_{$this->CACHE_CHOSEN_PROGRAM}"], $key, $final_value, $seconds);
	}







			
			#region ### phpRedis (better than "pRedis" ) [edit: 02mxypZ1 ] ###
			public $redis_host_params = [
				'host' => '127.0.0.1',
				'port' => 6379,
				'connectTimeout' => 2.5,
				'db_index' => 0,
				'auth' => ['', ''],
				'ssl' => ['verify_peer' => false],
			];
			public $redis_keys_prefix='';
			public $redis_instance = null; 
			public $redis_default_key_pre = ':';
			public function cache_get_redis($key, $default=null){
				$this->helper_cache_redis_init_check($this->redis_host_params, true);
				$key = $this->redis_keys_prefix . $key; 
				$redis = $this->helper_redis_getInstance();
				$val = $redis->get($key);
				$this->helper_redis_IfSwooleCloseNeeded($redis);
				if (!$val)
					return $default;
				return (self::is_serialized($val) ? unserialize($val) : $val);
			}
			public function cache_set_redis($key, $data, $seconds = 8640000){
				$this->helper_cache_redis_init_check($this->redis_host_params, true);
				$key = $this->redis_keys_prefix . $key;
				$redis =$this->helper_redis_getInstance();
				$result = $redis->set($key, ($this->is_simple_type($data) ? $data : serialize($data)), $seconds );
				$this->helper_redis_IfSwooleCloseNeeded($redis);
				return $result;
			} 
			// public function cache_append_redis($key, $data, $seconds = 8640000){
			// 	$this->helper_cache_redis_init($this->redis_host);
			// 	$existing_arr = $this->cache_get_redis($key,[]);
			// 	$new  = $this->is_array($existing_arr) ? $this->array_merge($existing_arr,$data) : $existing_arr. $data;
			// 	$final_value  = ($this->is_simple_type($new) ? $new : serialize($new));
			// 	return $this->cache_set_redis($key, $final_value, $seconds);
			// }
			public function cache_clear_redis(){
				$this->helper_cache_redis_init_check($this->redis_host_params, true);
				$this->helper_redis_getInstance()->flushAll();
			}
			// helpers, with added swoole support
			private $redis_pool=null;   private $redis_start_inited=false;
			public function helper_cache_redis_init($host_params, $use_params=false){
				try{ 
					if (self::swoole_inside_coroutine()){
						if ( is_null($this->redis_pool) ){
							$authString = !empty($host_params['auth'][0]) ? $host_params['auth'][0].':'.$host_params['auth'][1] : '';
							$newR = (new \Swoole\Database\RedisConfig)->withHost($host_params['host'])->withPort($host_params['port'])->withAuth($authString)->withDbIndex($host_params['db_index'])->withTimeout($host_params['connectTimeout']);
							$this->redis_pool = new \Swoole\Database\RedisPool( $newR );
							$this->redis_instance= $this->helper_redis_getInstance(); 
						}
					}
					else {
						if ( is_null($this->redis_instance) ){
							$this->redis_instance= new \Redis(); 
							$this->redis_instance->connect( $host_params['host'], $host_params['port']); 
							if ($use_params)
							{
								// SERIALIZER_NONE | SERIALIZER_PHP | SERIALIZER_IGBINARY | SERIALIZER_MSGPACK );
								$this->redis_instance->setOption(\Redis::OPT_SERIALIZER, \Redis::SERIALIZER_PHP);
								$this->redis_instance->setOption(\Redis::OPT_PREFIX, __NAMESPACE__. $this->redis_default_key_pre);	// use custom prefix on all keys
							}
						} 
					}
					if ($use_params)
						$this->redis_keys_prefix = $this->slug .'_';
				}
				catch(\Exception $ex){
					if (get_class($ex)==="RedisException"){
						if (!$this->redis_start_inited){
							$this->redis_start_inited=true;
							if(method_exists($this,'helper_redis_try_os_start')) $this->helper_redis_try_os_start();
							sleep(1);
							$this->helper_cache_redis_init($host_params);
						}
					}
				}
			} 
			public function helper_cache_redis_init_check($host_params, $use_params=false){
				if(is_null($this->redis_instance))
					$this->helper_cache_redis_init($host_params, $use_params);
			}

			public function helper_redis_getInstance()
			{
				return (self::swoole_inside_coroutine() ? $this->redis_pool->get() : $this->redis_instance);
			} 
			public function helper_redis_getAllKeys($redis_instance)
			{
				$keys=[];
				$it = NULL;
				do {
					$arr_keys = $redis_instance->scan($it);
					if ($arr_keys !== FALSE) {
						foreach($arr_keys as $str_key) {
							$keys[]= $str_key;
						}
					}
				} while ($it > 0);
				return $keys;
			} 
					
			public function helper_redis_IfSwooleCloseNeeded($redis_instance)
			{
				if ( self::swoole_inside_coroutine() ){
					$this->redis_pool->put($redis_instance);
				}
			}
			#endregion 

	// ##### memcached: todo #####
	//
	//
	//
	//
	//
	//

	// ##### apcu: todo ##### (examples: https://pastebin_com/0Q5y28fA)
	//
	//
	//
	//
	//
	//


	// ####################### OBJECTS ####################### //
	// https://medium.com/@dylanwenzlau/500x-faster-caching-than-redis-memcache-apc-in-php-hhvm-dcd26e8447ad
	// sample ---> https://pastebin_com/0eUvyXaD

	public $cache_object_method='serialize'; //serialize | memcached | apcu
	public function cache_get_object($uniqFileName, $default='', $expire_seconds=86400 )
	{
		if ($this->cache_object_method=='apcu')
		{
		}
		else{
			$data = $this->cache_get_file($uniqFileName, $default, $expire_seconds, $decode=false);
			return unserialize( $data );
		}
	}
	public function cache_set_object($uniqFileName, $content, $throw_exception=true)
	{
		$res=false;
		if ($this->cache_object_method=='apcu')
		{
			
		}
		else{
			$res = $this->cache_set_file($uniqFileName, serialize($content), false);
		}
		if(!$res && $throw_exception){
			throw new \Exception('Was unable to set APCU cache for '.$uniqFileName);
		}
		return $res;
	}



	// ### CACHE DIRS ### 
	private $cacheDirectory = __DIR__.'/_cache/';  //sys_get_temp_dir()
	public function cache_dir_set($dir=null, $auto_clear_seconds=null){ 
		if($dir)  $this->cacheDirectory = $dir;
		$res = $this->mkdir($this->cacheDirectory);
		if( !is_null($auto_clear_seconds))
		{
			$this->clearCacheDir($auto_clear_seconds); 
		}
		return $res;
	}
	public function cache_dir_get($create=true){ 
		$dir = $this->cacheDirectory;
		if($create && !is_dir($dir)){ $this->mkdir($dir); }
		return $dir; 
	}	 

	// ### CACHE FILES ###
	public function cache_file_location($uniqFileName){
		$uniqFileName = is_string($uniqFileName) || is_numeric($uniqFileName) ? $uniqFileName : json_encode($uniqFileName);
		$uniqFileName = self::sanitize( substr($uniqFileName, 0, 10)) . "_" . md5($uniqFileName);
		$filePath= $this->cache_dir_get() . $uniqFileName ."_tmp"; //"/". 
		return $this->realpath($filePath);
	}
	public function cache_get_file($uniqFileName, $default='', $expire_seconds=8640000)
	{
		$filePath= $this->cache_file_location($uniqFileName);
		if ( strlen($filePath) < 3) return "too tiny filename";

		if ( file_exists($filePath) ){
			if (filemtime($filePath)+$expire_seconds<time() ){
				unlink($filePath);
				return $default;
			}
			else{	
				return $this->file_get_contents($filePath);
			}
		}
		else {
			return $default;
		}
	}
	public function cache_set_file($uniqFileName, $content, $encode=true)
	{
		$filePath= $this->cache_file_location($uniqFileName);
		$contentFinal = ($encode && (is_array($content) || is_object($content)) ) ? json_encode($content): $content;
		return $this->localdata_set($filePath, $contentFinal);
	}
	
	public function cache_append_array_file($uniqKeyFileName, $data)
	{    
		$existing  = $this->cache_get_file($uniqKeyFileName,[]);
		$newData   = is_array($data) ? $data : [$data];
		$finalData = array_merge_recursive($existing,$newData);
		return $this->cache_set_file($uniqKeyFileName, $finalData);
	}

	public function cacheDirUrl(){ return basename($this->cache_dir_get()); }
	public function backupFileIntoCache($filename, $data){
		$filename = $this->cache_dir_get() .'/'. $filename . date('Y-m-d H-i-s') . "_".md5($data);
		$this->localdata_set($filename, is_array($data) ? json_encode($data) : $data );
	}

	// ####################### IDs ####################### //
	public $cached_IDS_type = 'file'; // file, wp, db, object (using redis or whatever, according to $CACHE_CHOSEN_PROGRAM) //$this->isWP;
	public function cache_key_create($text){return md5( is_array($text) ? json_encode($text) : $text );	}
 
	// ########
	private function cache_ids_parentname($containerHint){ return "_px_cached_ids_".$containerHint;} 
	private function get_cached_ids_array($containerHint){
		$ContainerName = $this->cache_ids_parentname($containerHint);
		if ($this->cached_IDS_type=='file'){
			$filePath =$this->cache_dir_get() . $ContainerName;
			if ( empty($this->temp_cacheIdsArray) || empty($this->temp_cacheIdsArray[$filePath]) ) {
				$cont = $this->file_get_contents( $filePath );
				if ( empty($cont) ) {
					$this->temp_cacheIdsArray[$filePath] = [];
				}
				else {
					$this->temp_cacheIdsArray[$filePath] = json_decode($cont,true);
					//if error happened
					if (is_null($this->temp_cacheIdsArray[$filePath])){
						//if contains broken array due to rare overwrite problem, i.e. ["id_1"],"id2","id3"]
						if ($this->contains($cont, $delimiter = '"')){
							$arrs=$this->string_to_array($cont, $delimiter);
							$this->temp_cacheIdsArray[$filePath]=$arrs;
						}
					}
				}
			}
			$existing_ids = $this->temp_cacheIdsArray[$filePath];
		}
		elseif ($this->cached_IDS_type=='wp'){
			$existing_ids = get_option( $ContainerName, [] );
		}
		elseif ($this->cached_IDS_type=='object'){
			$existing_ids = $this->cache_get( $ContainerName, [] );
		}
		return $existing_ids;
	}
	private function set_cached_ids_array($containerHint, $existing_ids){
		$ContainerName = $this->cache_ids_parentname($containerHint);
		if ($this->cached_IDS_type=='file'){
			$filePath = $this->cache_dir_get() . $ContainerName;
			$this->temp_cacheIdsArray[$filePath] = $existing_ids;
			$this->localdata_set( $filePath, json_encode($existing_ids) );
		}
		elseif ($this->cached_IDS_type=='wp'){
			update_option( $ContainerName, $existing_ids );
		}
		elseif ($this->cached_IDS_type=='redis'){
			$this->cache_set( $ContainerName, $existing_ids);
		}
	}

	private function add_cached_id($containerHint, $cache_id){
		$ContainerName = $this->cache_ids_parentname($containerHint);
		//to ensure to preserve any overwrites happened within last few milliseconds
		$latest_current_ids = $this->get_cached_ids_array($containerHint); //
		//$recently_added_ids = array_diff($latest_current_ids, $added_ids);
		//$up_to_date_IDS = array_merge($existing_ids, $recently_added_ids); 
		$latest_current_ids[]=$cache_id;
		$this->set_cached_ids_array($containerHint, $latest_current_ids);
	}

	public function is_cached_id($cache_parent_key, $item_key_or_params){  
		$key = is_array($item_key_or_params) ? json_encode($item_key_or_params) : $item_key_or_params;
		$key = strlen($key) <=35 ? $key : md5($key); //if same length as md5, then prefer original readable key
		if( in_array($key, $this->get_cached_ids_array($cache_parent_key) ) )
		{
			return true;
		}
		else{
			$this->add_cached_id($cache_parent_key, $key);
			return false;
		}
	}
	public function clearCacheIdsOnCall($param_name){ 
		if (isset($_ GET[$param_name]))
		{
			$this->clearCacheIds('todo');
		}
	}
	public function clearCacheIds($key_name){ 
		$key_fullname = $this->cache_ids_parentname($key_name);
		if ($this->cached_IDS_type=='local'){
			$this->rmdir($this->cache_dir_get());
			$this->mkdir($this->cache_dir_get());
		}
		elseif ($this->cached_IDS_type=='wp'){
			update_option( $this->cache_ids_parentname($key_name), []);
		}
		elseif ($this->cached_IDS_type=='object'){
			$this->cache_set( $key_fullname, []);
		}
		else {
		}
	}
	public function clearCacheDir($seconds=86400){
		$timerFile= $this->cache_dir_get().'/_cleanTime.blobz';
		if (file_exists($timerFile) && filemtime($timerFile)<time()-$seconds){
			array_map( 'unlink', array_filter((array) glob( $this->cache_dir_get()."*") ) );
			$this->localdata_set($timerFile, time());
		}
		else{
			$this->localdata_set($timerFile, time());
		}
	} 
	
	public function cacheDirLink(){
		return $this->baseURL .'/'. $this->cacheDirUrl() .'/';
	}


	// usage:  cachedFunction( [$xyzClass,'methodName'], $params, $cache_seconds=60*60*24, "mySitePeopleAges" )
	public function cachedFunctionCall($callbackFunction, $params=[], $seconds=86400, $UniqCacheName='', $force_on_empty=true){
		$fileName = $this->funcStringName($callbackFunction, $params, $seconds, $UniqCacheName);
		$cache_file = $this->cache_dir_get() .'_'. $fileName ;
		$call = false;
		//if ( $this->isWpCache() )
		//{
		//}
		
		if ( $seconds<=0 || $this->forceNewCache || !file_exists($cache_file) || time() - filemtime($cache_file) > $seconds )  
		{
			$call=true;
		}
		else{
			$cont = $this->file_get_contents($cache_file);
			if ($cont=="" && $force_on_empty){
				$call=true;
			}
			else{
				$response = $cont;
			}
		}
		//
		if($call){
			$response = call_user_func_array($callbackFunction, $params);
			$this->localdata_set($cache_file, is_array($response) || is_object($response) ? json_encode($response) : $response );
		}

		try{
			return is_array($response) || is_object($response) || ! is_string($response) ? $response : json_decode($response);
		}
		catch(\Exception $e)
		{
			return $response;
		}
	}
	
	public function funcStringName($callbackFunction, $params, $seconds, $fixedName='')
	{ 
		if (!empty($fixedName))  
			return $fixedName; 
		$funcSlug = is_array($callbackFunction) && is_object($callbackFunction[0]) ? get_class($callbackFunction[0])."_".$callbackFunction[1] : (is_string($callbackFunction) ? $callbackFunction : md5(json_encode($callbackFunction)));
		$funcAliasString= md5( basename( $funcSlug ."_". md5(json_encode($params)) . "_". $seconds ) );
		return $funcAliasString; 
	}

	public function transientFunction($callbackFunction, $params=[], $seconds=86400, $transientName=''){
		$transientName = $this->funcStringName($callbackFunction, $params, $seconds, $transientName) ;
		if( ($value = get_transient($transientName))===false ) { 
			$value = call_user_func_array($callbackFunction, $params);
			set_transient($transientName, $value, $seconds);
		}
		return $value;
	}
	*/
	

} // tempclass_cache_file


 

#region TRANSLATIONS
#[\AllowDynamicProperties]
class tempclass_translations {
	#region TRANSLATIONS DB create
	/*
		$transl = new TranslationsDB("db_all");   
		$transl->set("programName",		$req['program']); 
		$transl->set("lang",			$req['lang']);
		// for useing automatizations:
		$transl->set("gtranslateApiKey",		"A1KQX8Iza...."); 
		$transl->set("correctKey",		"nmsE3hig..."); 
		$transl->set("developerKey",	'adGFfger..' ) ; 
			
	*/
	public static function create_connection_($db_path="/example/my.db_or_sqlite3", $pdo=true){
		try {
			if ( $pdo )
			{
				$db = new PDO('sqlite:'.$db_path);
				$db ->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);	// Set errormode to exceptions
			}
			else{
				$db = new SQLite3($db_path);
			}
		} catch(Exception $e) {
			die('Error: '.$e->getMessage());
		}
		return $db ;
	}

	//db->exec($command);
	//db->prepare($command='INSERT INTO translations ( string, lang, value ) VALUES (:string, :lang, :value);');
	
	private	$db						= null;
	public $lang					= "";
	public $found					= false;
	// user opts
	public $programName				= "example";
	public $gtranslateApiKey		= "";
	public $isDeveloper				= false; 
	public $helpers;
	
	public function __construct_TranslationsDB($db_name="db_translations.db", $helpers=null) { 
		$this->db		= generic_funcs::create_connection_($db_name);
		$this->create_translations_table();
		$this->helpers = $helpers;
	}

	public function set($name, $value) {
		$this->$name=$value;
	}
	
	public function create_translations_table() {
		$sql =
		'CREATE TABLE IF NOT EXISTS translations (	
			ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
			program_name		VARCHAR(150),
			string				TEXT 	NOT NULL,
			lang				TEXT 	NOT NULL,
			value				TEXT 	NOT NULL,
			time				INT,
			suggestion			TEXT
		);';
		return $this->db->query($sql);     //possibles: VARCHAR(50)  ,  PRIMARY KEY (`ID`), UNIQUE KEY `ID` (`ID`) 	) AUTO_INCREMENT=1;'; 
	}


	public function insert_translation($string, $lang, $value, $time, $program_name, $suggestion) { 
		$statement = $this->db->prepare('INSERT INTO translations ( string, lang, value, time, program_name, suggestion ) VALUES (:string, :lang, :value, :time, :program_name,  :suggestion );');
		$statement->bindValue(':string',		$string);
		$statement->bindValue(':lang',			$lang);
		$statement->bindValue(':value',			$value);
		$statement->bindValue(':time',			$time);
		$statement->bindValue(':program_name',	$program_name);
		$statement->bindValue(':suggestion',	$suggestion);
		return $statement->execute();
	}

	public function string_exists($string, $lang=false, $program_name=false) 
	{
		$statement = $this->db->prepare('SELECT * from translations where string= :string'. ( $lang? ' and lang = :lang' : '') .' LIMIT 1' ); // . ( $program_name? ' and program_name = :program_name' : '') 
		$statement->bindValue(':string',	$string);
			if ($lang)			
		$statement->bindValue(':lang',	$lang); 
		$res = $statement->execute();  
		return !empty($res->fetchArray(SQLITE3_ASSOC));
	}

	// for single translations
	public function get_translation($string, $lang=false) 
	{
		$lang =  $lang ?: $this->lang;
		
		try {
			// if bulk translation
			if(is_array($string))
			{
				$lang =  $lang ?: $this->lang;
				$array=[];
				// bulk translate with Google, if this language doesnt exist
				if ( !$this->string_exists($string[0], $lang) &&  (  $this->string_exists($string[0])  || $this->isDeveloper )  )
				{
					$this->gtranslate($string, $lang);
				}
				foreach ($string as $each){
					$array[$each]= $this->get_translation($each, $lang);
				}
				return $array;
			}
			//if single translation
			else
			{
				$return= $string;
				$statement = $this->db->prepare('SELECT * from translations where string= :string and lang= :lang ');  //. ( $program_name? ' program_name = :program_name' : '') 
				$statement->bindParam(':string',$string);
				$statement->bindParam(':lang',	$lang);
				$ret = $statement->execute(); 
				$res = $ret->fetchArray(SQLITE3_ASSOC);
				if(!empty($res)){
					$this->found=true;
					$return= $res['value'];
				}
				else{
					$this->found=false;
					if ( $this->string_exists($string) || $this->isDeveloper ){
						$res= $this->gtranslate($string, $lang);
						if (array_key_exists($string, $res))
						{
							$value = $res[$string];
							$return= $value;
						}
					}
				}
			}
		}
		catch (Exception $e){
			$return=$string;
		}
		return $return;
	}
	 
	public function gtranslate($q, $target, $sourceEng=false, $gtranslateApiKey=false)
	{
		$q_string =     !is_array($q)      ?      'q='.urlencode($q)  	  :     'q='.implode("&q=", array_map('urlencode',$q) ); 
		$res=$this->curl("https://www.googleapis.com/language/translate/v2?" .$q_string , ['target'=>$target, 'source'=>($source ?: "en"), 'key'=>($gtranslateApiKey ?: $this->gtranslateApiKey)]  );
		$array= json_decode($res, true);
		$transl_result = $array['data']['translations'];

		// format data correctly 
		$assoc=[];
		if(is_array($q)){
			if (count($q) != count($transl_result))
			{
				exit("error in translation pairs: ". print_r($q,true) . PHP_EOL . print_r($transl_result,true) );
			}
			for($i=0; $i < count($q); $i++)
			{
				$assoc[$q[$i]] = $value = $transl_result[$i]['translatedText']; 
				$this->insert_translation($q[$i], $target, $value, time(), $this->programName, "");
			}
			return $assoc;
		}
		else{
			$assoc[$q] =  $value =  $transl_result[0]['translatedText'];
			$this->insert_translation($q, $target, $value, time(), $this->programName, "");
			return $assoc;
		}
	}
}
#endregion tempclass_translations

}// if-class