HEX
Server: nginx/1.16.1
System: Linux VM-0-14-centos 4.18.0-348.7.1.el8_5.x86_64 #1 SMP Wed Dec 22 13:25:12 UTC 2021 x86_64
User: www (1000)
PHP: 8.3.31
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: /www/wwwroot/www.sceybwg.com/wp-content/plugins/wpjam-basic/public/wpjam-utils.php
<?php
if(!function_exists('base64_urlencode')){
	function base64_urlencode($str){
		return rtrim(strtr(base64_encode($str), '+/', '-_'), '=');
	}
}

if(!function_exists('base64_urldecode')){
	function base64_urldecode($str){
		return base64_decode(str_pad(strtr($str, '-_', '+/'), strlen($str) % 4, '='));
	}
}

// JWT
function wpjam_generate_jwt($payload, $header=[]){
	$header	+= ['typ'=>'JWT'];	// 'alg'=>'HS256'
	$jwt	= is_array($payload) ? implode('.', array_map(fn($v)=> base64_urlencode(wpjam_json_encode($v)), [$header, $payload])) : '';

	return $jwt ? $jwt.'.'.wpjam_generate_signature('hmac-sha256', $jwt) : false;
}

function wpjam_verify_jwt($token){
	$token	= explode('.', $token);

	if(count($token) == 3 && hash_equals(wpjam_generate_signature('hmac-sha256', $token[0].'.'.$token[1]), $token[2])){
		[$header, $payload]	= array_map(fn($v)=> wpjam_json_decode(base64_urldecode($v)), array_slice($token, 0, 2));

		//iat 签发时间不能大于当前时间
		//nbf 时间之前不接收处理该Token
		//exp 过期时间不能小于当前时间
		if(!array_any(['iat'=>'>', 'nbf'=>'>', 'exp'=>'<'], fn($v, $k)=> isset($payload[$k]) && wpjam_compare($payload[$k], $v, time()))){
			return $payload;
		}
	}

	return false;
}

function wpjam_get_jwt($key='access_token', $required=false){
	$header	= $_SERVER['HTTP_AUTHORIZATION'] ?? '';

	return try_remove_prefix($header, 'Bearer') ? trim($header) : wpjam_get_parameter($key, ['required'=>$required]);
}

// Crypt
function wpjam_encrypt($text, $args, $de=false){
	$args	+= ['method'=>'', 'key'=>'', 'options'=>'', 'iv'=>''];
	$text	= $de ? openssl_decrypt($text, $args['method'], $args['key'], $args['options'], $args['iv']) : $text;
	$cb		= 'wpjam_'.($de ? 'un' : '').'pad';
	$types	= ['weixin', 'pkcs7'];
	$types	= $de ? array_reverse($types) : $types;

	foreach($types as $type){
		if($type == 'pkcs7'){
			if(($args['options'] ?? '') == OPENSSL_ZERO_PADDING && !empty($args['block_size'])){
				$text	= $cb($text, $type, $args['block_size']);
			}
		}elseif($type == 'weixin'){
			if(($args['pad'] ?? '') == 'weixin' && !empty($args['appid'])){
				$text	= $cb($text, $type, trim($args['appid']));
			}
		}
	}

	return $de ? $text : openssl_encrypt($text, $args['method'], $args['key'], $args['options'], $args['iv']);
}

function wpjam_decrypt($text, $args){
	return wpjam_encrypt($text, $args, true);
}

function wpjam_pad($text, $type, ...$args){
	if($type == 'pkcs7'){
		$pad	= $args[0] - (strlen($text) % $args[0]);
		$text	.= str_repeat(chr($pad), $pad);
	}elseif($type == 'weixin'){
		$text	= wp_generate_password(16, false).pack("N", strlen($text)).$text.$args[0];
	}

	return $text;
}

function wpjam_unpad($text, $type, ...$args){
	if($type == 'pkcs7'){
		$pad	= ord(substr($text, -1));
		$text	= ($pad > 0 && $pad < $args[0]) ? substr($text, 0, -1 * $pad) : $text;
	}elseif($type == 'weixin'){
		$text	= substr($text, 16);
		$length	= (unpack("N", substr($text, 0, 4)))[1];

		if(trim(substr($text, $length + 4)) != trim($args[0])){
			return new WP_Error('invalid_appid', 'Appid 校验「'.substr($text, $length + 4).'」「'.$args[0].'」错误');
		}

		$text	= substr($text, 4, $length);
	}

	return $text;
}

function wpjam_generate_signature($algo='sha1', ...$args){
	if($algo == 'sha1'){
		return sha1(implode(wpjam_sort($args, SORT_STRING)));
	}elseif($algo == 'hmac-sha256'){
		return base64_urlencode(hash_hmac('sha256', $args[0], wp_salt(), true));
	}
}

// JSON 
function wpjam_json_encode($data){
	return wp_json_encode($data, JSON_UNESCAPED_UNICODE);
}

function wpjam_json_decode($json, $assoc=true){
	$json	= wpjam_strip_control_chars($json);
	$result	= $json ? json_decode($json, $assoc) : new WP_Error('empty_json', 'JSON 内容不能为空!');
	$result	??= str_contains($json, '\\') ? json_decode(stripslashes($json), $assoc) : $result;

	if(is_null($result)){
		$code	= json_last_error();
		$msg	= json_last_error_msg();

		trigger_error('json_decode_error['.$code.']:'.$msg."\n".var_export($json, true));

		return new WP_Error('json_decode_error', $msg);
	}

	return $result;
}

function wpjam_send_json($data=[], $code=null){
	if($data === true || $data === []){
		$data	= ['errcode'=>0];
	}elseif($data === false || is_null($data)){
		$data	= ['errcode'=>'-1', 'errmsg'=>'error'];
	}elseif(is_wp_error($data)){
		$data	= wpjam_error($data);
	}elseif(wpjam_is_assoc_array($data)){
		$data	+= ['errcode'=>0];
		$args	= $data['errmsg'] ?? [];
		$data	= array_merge($data, $data['errcode'] && (!$args || is_array($args)) ? wpjam_error($data['errcode'], $args) : []);
	}

	$data	= wpjam_json_encode($data);
	$jsonp	= wp_is_jsonp_request();

	if(!headers_sent()){
		isset($code) && status_header($code);

		wpjam_doing_debug() || @header('Content-Type: application/'.($jsonp ? 'javascript' : 'json').'; charset='.get_option('blog_charset'));
	}

	echo $jsonp ? '/**/'.$_GET['_jsonp'].'('.$data.')' : $data; exit;
}

// Compress
function wpjam_compress($data, $base64=true, $level=6){
	$text	= gzcompress(wpjam_json_encode($data), $level);

	return $base64 ? base64_encode($text) : $text;
}

function wpjam_uncompress($text, $base64=true){
	return wpjam_json_decode(gzuncompress($base64 ? base64_decode($text) : $text));
}

// User agent
function wpjam_get_user_agent(){
	return $_SERVER['HTTP_USER_AGENT'] ?? '';
}

function wpjam_get_ip(){
	return $_SERVER['REMOTE_ADDR'] ?? '';
}

function wpjam_parse_user_agent($user_agent=null, $referer=null){
	$user_agent	??= $_SERVER['HTTP_USER_AGENT'] ?? '';
	$referer	??= $_SERVER['HTTP_REFERER'] ?? '';
	$os			= 'unknown';
	$device		= $browser = $app = '';
	$os_version	= $browser_version = $app_version = 0;

	if($rule	= array_find([
		['iPhone',			'iOS',	'iPhone'],
		['iPad',			'iOS',	'iPad'],
		['iPod',			'iOS',	'iPod'],
		['Android',			'Android'],
		['Windows NT',		'Windows'],
		['Macintosh',		'Macintosh'],
		['Windows Phone',	'Windows Phone'],
		['BlackBerry',		'BlackBerry'],
		['BB10',			'BlackBerry'],
		['Symbian',			'Symbian'],
	], fn($rule)=> stripos($user_agent, $rule[0]))){
		[$os, $device]	= array_pad($rule, 2, '');
	}

	if($os == 'iOS'){
		$os_version	= preg_match('/OS (.*?) like Mac OS X[\)]{1}/i', $user_agent, $matches) ? (float)(trim(str_replace('_', '.', $matches[1]))) : 0;
	}elseif($os == 'Android'){
		if(preg_match('/Android ([0-9\.]{1,}?); (.*?) Build\/(.*?)[\)\s;]{1}/i', $user_agent, $matches) && !empty($matches[1]) && !empty($matches[2])){
			$os_version	= trim($matches[1]);
			$device		= trim($matches[2]);
			$device		= str_contains($device, ';') ? explode(';', $device)[1] : $device;
		}
	}

	if($rule	= array_find([
		['lynx',	'lynx'],
		['safari',	'safari',	'/version\/([\d\.]+).*safari/i'],
		['edge',	'edge',		'/edge\/([\d\.]+)/i'],
		['chrome',	'chrome',	'/chrome\/([\d\.]+)/i'],
		['firefox',	'firefox',	'/firefox\/([\d\.]+)/i'],
		['opera',	'opera',	'/(?:opera).([\d\.]+)/i'],
		['opr/',	'opera',	'/(?:opr).([\d\.]+)/i'],
		['msie',	'ie'],
		['trident',	'ie'],
		['gecko',	'gecko'],
		['nav',		'nav']
	], fn($rule)=> stripos($user_agent, $rule[0]))){
		$browser			= $rule[1];
		$browser_version	= !empty($rule[2]) && preg_match($rule[2], $user_agent, $matches) ? (float)(trim($matches[1])) : 0;
	}

	if(strpos($user_agent, 'MicroMessenger') !== false){
		$app			= str_contains($referer, 'https://servicewechat.com') ? 'weapp' : 'weixin';
		$app_version	= preg_match('/MicroMessenger\/(.*?)\s/', $user_agent, $matches) ? (float)$matches[1] : 0;
	}

	return compact('os', 'device', 'app', 'browser', 'os_version', 'browser_version', 'app_version');
}

function wpjam_parse_ip($ip=''){
	$ip	= $ip ?: ($_SERVER['REMOTE_ADDR'] ?? '');

	if($ip == 'unknown' || !$ip){
		return false;
	}

	$default	= ['ip'=>$ip]+array_fill_keys(['country', 'region', 'city'], '');

	if(!file_exists(WP_CONTENT_DIR.'/uploads/17monipdb.dat')){
		return $default;
	}

	$nip	= gethostbyname($ip);
	$ipdot	= explode('.', $nip);

	if($ipdot[0] < 0 || $ipdot[0] > 255 || count($ipdot) !== 4){
		return $default;
	}

	static $cache	= [];

	if(!$cache){
		$fp		= fopen(WP_CONTENT_DIR.'/uploads/17monipdb.dat', 'rb');
		$offset	= unpack('Nlen', fread($fp, 4));
		$index	= fread($fp, $offset['len'] - 4);
		$cache	= ['fp'=>$fp, 'offset'=>$offset, 'index'=>$index];

		register_shutdown_function(fn()=> fclose($fp));
	}

	$fp		= $cache['fp'];
	$offset	= $cache['offset'];
	$index	= $cache['index'];
	$nip2	= pack('N', ip2long($nip));
	$start	= (int)$ipdot[0]*4;
	$start	= unpack('Vlen', $index[$start].$index[$start+1].$index[$start+2].$index[$start+3]);

	for($start = $start['len']*8+1024; $start < $offset['len']-1024-4; $start+=8){
		if($index[$start].$index[$start+1].$index[$start+2].$index[$start+3] >= $nip2){
			$index_offset = unpack('Vlen', $index[$start+4].$index[$start+5].$index[$start+6]."\x0");
			$index_length = unpack('Clen', $index[$start+7]);

			fseek($fp, $offset['len']+$index_offset['len']-1024);

			$data	= explode("\t", fread($fp, $index_length['len']));
			$data	= array_slice(array_pad($data, 3, ''), 0, 3);

			return ['ip'=>$ip]+array_combine(['country', 'region', 'city'], $data);
		}
	}

	return $default;
}

function wpjam_ua($name=''){
	return $name ? wpjam_get(wpjam_ua(), $name) : (wpjam('user_agent') ?: wpjam('user_agent', wpjam_parse_user_agent()));
}

function wpjam_current_supports($feature){
	if($feature == 'webp'){
		return wpjam_ua('browser') == 'chrome' || wpjam_ua('os') == 'Android' || (wpjam_ua('os') == 'iOS' && version_compare(wpjam_ua('os_version'), 14) >= 0);
	}
}

function wpjam_get_device(){
	return wpjam_ua('device');
}

function wpjam_get_os(){
	return wpjam_ua('os');
}

function wpjam_get_app(){
	return wpjam_ua('app');
}

function wpjam_get_browser(){
	return wpjam_ua('browser');
}

function wpjam_get_version($key){
	return wpjam_ua($key.'_version');
}

function is_ipad(){
	return wpjam_get_device() == 'iPad';
}

function is_iphone(){
	return wpjam_get_device() == 'iPhone';
}

function is_ios(){
	return wpjam_get_os() == 'iOS';
}

function is_macintosh(){
	return wpjam_get_os() == 'Macintosh';
}

function is_android(){
	return wpjam_get_os() == 'Android';
}

function is_weixin(){
	return isset($_GET['weixin_appid']) ? true : wpjam_get_app() == 'weixin';
}

function is_weapp(){
	return isset($_GET['appid']) ? true : wpjam_get_app() == 'weapp';
}

function is_bytedance(){
	return isset($_GET['bytedance_appid']) ? true : wpjam_get_app() == 'bytedance';
}

// File
function wpjam_import($file, $columns=[]){
	$dir	= wp_get_upload_dir()['basedir'];
	$file	= !$file || str_starts_with($file, $dir) ? $file : $dir.$file;

	if(!$file || !file_exists($file)){
		return new WP_Error('file_not_exists', '文件不存在');
	}

	$ext	= wpjam_at($file, '.', -1);

	if($ext == 'csv'){
		if(($handle = fopen($file, 'r')) !== false){
			while(($row = fgetcsv($handle)) !== false){
				if(!array_filter($row)){
					continue;
				}

				if(($encoding	??= mb_detect_encoding(implode('', $row), mb_list_encodings(), true)) != 'UTF-8'){
					$row	= array_map(fn($v) => mb_convert_encoding($v, 'UTF-8', 'GBK'), $row);
				}

				if(isset($map)){
					$data[]	= array_map(fn($i)=> preg_replace('/="([^"]*)"/', '$1', $row[$i]), $map);
				}else{
					if($columns){
						$row	= array_map(fn($v)=> trim(trim($v), "\xEF\xBB\xBF"), $row);
						$columns= array_flip(array_map('trim', $columns));
						$map	= wpjam_array($row, fn($k, $v)=> isset($columns[$v]) ? [$columns[$v], $k] : (in_array($v, $columns) ? [$v, $k] : null));
					}else{
						$map	= array_flip(array_map(fn($v)=> trim(trim($v), "\xEF\xBB\xBF"), $row));
					}
				}
			}

			fclose($handle);
		}
	}else{
		$data	= file_get_contents($file);
		$data	= ($ext == 'txt' && is_serialized($data)) ? maybe_unserialize($data) : $data;
	}

	unlink($file);

	return $data ?? [];
}

function wpjam_export($file, $data, $columns=[]){
	header('Content-Disposition: attachment;filename='.$file);
	header('Pragma: no-cache');
	header('Expires: 0');

	$handle	= fopen('php://output', 'w');
	$ext	= wpjam_at($file, '.', -1);

	if($ext == 'csv'){
		header('Content-Type: text/csv');

		fwrite($handle, chr(0xEF).chr(0xBB).chr(0xBF));

		if($columns){
			fputcsv($handle, $columns);
			array_walk($data, fn($item)=> fputcsv($handle, wpjam_map($columns, fn($v, $k)=> $item[$k] ?? '')));
		}else{
			array_walk($data, fn($item)=> fputcsv($handle, $item));
		}
	}elseif($ext == 'txt'){
		header('Content-Type: text/plain');

		fputs($handle, is_scalar($data) ? $data : maybe_serialize($data));
	}

	fclose($handle);

	exit;
}

function wpjam_between($value, $min, $max=null){
	return $value >= $min && $value <= ($max ?? $min);
}

function wpjam_get_operator(...$args){
	$data = [
		'not'		=> '!=',
		'lt'		=> '<',
		'lte'		=> '<=',
		'gt'		=> '>',
		'gte'		=> '>=',
		'in'		=> 'IN',
		'not_in'	=> 'NOT IN',
		'like'		=> 'LIKE',
		'not_like'	=> 'NOT LIKE',
	];

	return $args ? ($data[$args[0]] ?? '') : $data;
}

// $value, $args
// $value, $value2
// $value, $compare, $value2, $strict=false
function wpjam_compare($value, $compare, ...$args){
	if(wpjam_is_assoc_array($compare)){
		return wpjam_match($value, $compare);
	}

	$is_value	= is_array($compare) || !$args;
	$value2		= $is_value ? $compare : array_shift($args);
	$compare	= $is_value ? '' : $compare;
	$strict		= in_array($compare, ['!==', '===']) ? true : (bool)array_shift($args);
	$compare	= $compare ? strtoupper(['!=='=>'!=', '==='=>'=', '=='=>'='][$compare] ?? $compare) : (is_array($value2) ? 'IN' : '=');
	$antonyms	= ['!='=>'=', '<='=>'>', '>='=>'<', 'NOT IN'=>'IN', 'NOT BETWEEN'=>'BETWEEN'];

	if(isset($antonyms[$compare])){
		return !wpjam_compare($value, $antonyms[$compare], $value2, $strict);
	}

	if(!in_array($compare, $antonyms)){
		return false;
	}

	$value2	= in_array($compare, ['IN', 'BETWEEN']) ? wp_parse_list($value2) : (is_string($value2) ? trim($value2) : $value2);
	[$a, $b]= [$value, $value2];

	switch($compare){
		case '=': return $strict ? $a === $b : $a == $b;
		case '>': return $a > $b;
		case '<': return $a < $b;
		case 'IN': return is_array($a) ? array_all($a , fn($v)=> in_array($v, $b, $strict)) : in_array($a, $b, $strict);
		case 'IBETWEENN': return wpjam_between($a, ...$b);
	}
}

function wpjam_calc(...$args){
	if(wpjam_is_assoc_array($args[0])){
		$item		= $args[0];
		$formulas	= $args[1];
		$if_errors	= $args[2] ?? [];
		$item		= array_diff_key($item, $formulas);

		foreach($formulas as $key => $formula){
			foreach($formula as &$t){
				if(str_starts_with($t, '$')){
					$k	= substr($t, 1);
					$v	= $item[$k] ?? null;
					$r	= isset($v) ? wpjam_format($v, '-,', false) : false;
					$t	= $r === false ? ($if_errors[$k] ?? null) : $r;

					if(!isset($t)){
						$item[$key]	= $if_errors[$key] ?? (isset($v) ? '!!无法计算' : '!无法计算'); break;
					}
				}
			}

			try{
				$item[$key]	= wpjam_calc($formula);
			}catch(throwable $e){
				$item[$key]	= $if_errors[$key] ?? ($e instanceof DivisionByZeroError ? '!除零错误' : '!'.$e->getMessage());
			}
		}

		return $item;
	}

	$exp	= $args[0];
	$item	= $args[1] ?? [];
	$ops	= ['+'=>'bcadd', '-'=>'bcsub', '*'=>'bcmul', '/'=>'bcdiv', '%'=>'bcmod', '**'=>'bcpow'];
	$throw	= fn($msg)=> wpjam_throw('invalid_calc', '计算错误:'.$msg);
	$calc	= [];

	foreach(is_array($exp) ? $exp : wpjam_formula(...$args) as $t){
		if(is_numeric($t) || $t === '|'){
			$calc[]	= $t;
		}elseif(try_remove_prefix($t, '$')){
			$calc[]	= $item[$t] ?? 0;
		}elseif(in_array($t, ['sin', 'cos', 'abs', 'max', 'min', 'sqrt', 'pow', 'round', 'floor', 'ceil', 'fmod'])){
			$_args	= array_slice(array_splice($calc, array_last(array_keys($calc, '|'))), 1);
			$calc[]	= $_args ? $t(...$_args) : $throw('函数「'.$t.'」无有效参数');
		}else{
			$a	= array_pop($calc);
			$b	= array_pop($calc);

			if(in_array($t, ['/', '%']) && in_array((string)$a, ['0', '0.0', ''])){
				throw new DivisionByZeroError('Division by zero');
			}
			
			$calc[]	= isset($ops[$t]) ? $ops[$t]((string)$b, (string)$a, 6) : wpjam_compare($b, $t, $a);
		}
	}

	return count($calc) === 1 ? $calc[0] : $throw('计算栈剩余元素数还有'.count($calc).'个');
}

function wpjam_formula(...$args){
	if(is_array($args[0])){
		$fields	= array_shift($args);

		if($args){
			$render	= fn($key)=> '字段'.($fields[$key]['title'] ?? '').'「'.$key.'」'.'公式「'.$fields[$key]['formula'].'」';
			$key	= $args[0];
			$parsed	= $args[1];
			$path	= $args[2] ?? [];

			if(isset($parsed[$key])){
				return $parsed;
			}

			if(in_array($key, $path)){
				wpjam_throw('invalid_formula', '公式嵌套:'.implode(' → ', wpjam_map(array_slice($path, array_search($key, $path)), fn($k)=> $render($k))));
			}

			$path[]		= $key;
			$formula	= wpjam_formula($fields[$key]['formula'], $fields, $render($key).'错误');
			$parsed 	= array_reduce($formula, fn($c, $t)=> try_remove_prefix($t, '$') && !empty($fields[$t]['formula']) ? wpjam_formula($fields, $t, $c, $path) : $c, $parsed);

			return $parsed+[$key => $formula];
		}

		return wpjam_reduce($fields, fn($c, $v, $k)=> empty($v['formula']) ? $c : wpjam_formula($fields, $k, $c), []);
	}

	$formula	= $args[0];
	$fields		= $args[1] ?? [];
	$error		= $args[2] ?? '';
	$throw		= fn($msg)=> wpjam_throw('invalid_formula', $error.':'.$msg);
	$functions	= ['sin', 'cos', 'abs', 'ceil', 'pow', 'sqrt', 'pi', 'max', 'min', 'fmod', 'round'];
	$precedence	= ['+'=>1, '-'=>1, '**'=>2, '*'=>2, '/'=>2, '%'=>2, '>='=>3, '<='=>3, '!='=>3, '=='=>3, '>'=>3, '<'=>3,];
	$signs		= implode('|', array_map(fn($v)=> preg_quote($v, '/'), [...array_keys($precedence), '(', ')', ',']));
	$formula	= preg_split('/\s*('.$signs.')\s*/', trim($formula), -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
	$output		= $stack = [];
	$pt			= null;

	foreach($formula as $i => $t){
		$nt	= $formula[$i+1] ?? null;

		if(is_numeric($t)){
			$output[]	= str_ends_with($t, '.') ? $throw('无效的数字「'.$t.'」') : (float)$t;
		}elseif(str_starts_with($t, '$')){
			$output[]	= isset($fields[substr($t, 1)]) ? $t : $throw('「'.$t.'」未定义');
		}elseif(in_array($t, $functions, true)){
			$stack[]	= $t;
			$output[]	= '|';
		}elseif($t === '('){
			$stack[]	= $t;
		}elseif($t === ')' || $t === ','){
			if(!in_array('(', $stack, true) || ($t === ',' && ($pt === '(' || !$nt || $nt === ','))){
				$throw(($t === ')' ? '未匹配' : '无效').'的「'.$t.'」');
			}

			while(array_last($stack) !== '('){
				$output[] = array_pop($stack);
			}

			if($t === ')'){
				array_pop($stack);

				in_array(array_last($stack), $functions) && array_push($output, array_pop($stack));
			}
		}elseif(isset($precedence[$t])){
			$r	= ((!$pt || in_array($pt, ['(', ','], true)) ? 1 : 0)+((!$nt || in_array($nt, [')', ','], true) || isset($precedence[$nt])) ? 2 : 0);

			if(in_array($t, ['+', '-'], true) && $r == 1){
				$output[]	= 0;
			}elseif($r){
				$throw('操作符「'.$t.'」缺少操作数');
			}

			while($stack && ($v = array_last($stack)) !== '('){
				if(in_array($v, $functions) || $precedence[$t] <= $precedence[$v]){
					$output[]	= array_pop($stack);
				}else{
					break;
				}
			}

			$stack[] = $t;
		}else{
			$throw('无效的符号「'.$t.'」');
		}

		$pt	= $t;
	}
	
	return array_merge($output, array_reverse(in_array('(', $stack, true) ? $throw('未匹配的「(」') : $stack));
}

function wpjam_format($value, $format, ...$args){
	if(is_array($value) && is_array($format)){
		return wpjam_reduce($format ?: [], fn($c, $v, $k)=> isset($c[$k]) ? wpjam_set($c, $k, wpjam_format($c[$k], ...$v)) : $c, $value);
	}

	if(is_numeric($value)){
		if($format == ','){
			return number_format(trim($value), (int)($args[0] ?? 2));
		}elseif($format == '%'){
			return round($value * 100, ($args[0] ?? 2) ?: 2).'%';
		}elseif(!$format && $args && is_numeric($args[0])){
			return round($value, $args[0]);
		}

		return $value / 1;
	}

	if(in_array($format, ['-,', '-%'])){
		if(is_string($value)){
			$value	= str_replace(',', '', trim($value));

			if($format == '-%' && try_remove_suffix($value, '%')){
				$value	= is_numeric($value) ? $value / 100 : $value;
			}
		}

		return is_numeric($value) ? $value / 1 : ($args ? $args[0] : $value);
	}

	return $value;
}

function wpjam_match($item, ...$args){
	if(!$args || is_null($args[0])){
		return true;
	}

	if(is_string($args[0])){
		$args	= wpjam_parse_show_if($args);
	}else{
		$op		= $args[1] ?? ((wp_is_numeric_array($args[0]) || !isset($args[0]['key'])) ? 'AND' : '');
		$args	= $args[0];
	
		if($op){
			trigger_error('matches');	// del 2026-03-31
			return wpjam_matches($item, $args, $op);
		}
	}

	$value		= wpjam_get($item, $args['key']);
	$value2		= $args['value'] ?? null;
	$compare	= $args['compare'] ?? null;

	if(is_null($compare)){
		if(!empty($args['callable']) && (is_closure($value) || (is_callable($value) && is_array($value)))){
			return $value($value2, $item);
		}

		if(isset($args['if_null']) && is_null($value)){
			return $args['if_null'];
		}
	}

	if(is_array($value) || !empty($args['swap'])){
		[$value, $value2]	= [$value2, $value];
	}

	return wpjam_compare($value, $compare, $value2, (bool)($args['strict'] ?? false));
}

function wpjam_matches($arr, $args, $op='AND'){
	$op	= strtoupper($op);

	if(!in_array($op, ['AND', 'ALL', 'OR', 'ANY', 'NOT'])){
		return false;
	}

	if(!is_callable($args)){
		return wpjam_matches($args, fn($v, $k)=> wpjam_match($arr, ...(wpjam_is_assoc_array($v) ? [$v+['key'=>$k]] : [$k, $v])), $op);
	}

	if(in_array($op, ['AND', 'ALL'], true)){
		return array_all($arr, $args);
	}elseif(in_array($op, ['OR', 'ANY'], true)){
		return array_any($arr, $args);
	}else{
		return !array_all($arr, $args);
	}
}

// Tap
function wpjam_tap($value, $cb=null){
	$cb && $cb($value);

	return $value;
}

// Array
function wpjam_is_assoc_array($arr){
	return is_array($arr) && !wp_is_numeric_array($arr);
}

function wpjam_array($arr=null, $cb=null, $skip_null=false){
	if(!$cb){
		if(is_object($arr)){
			if(method_exists($arr, 'to_array')){
				return $arr->to_array();
			}elseif($arr instanceof Traversable){
				return iterator_to_array($arr);
			}elseif($arr instanceof JsonSerializable){
				return is_array($data = $arr->jsonSerialize()) ? $data : [];
			}else{
				return [];
			}
		}

		return (array)$arr;	
	}

	foreach($arr as $k => $v){
		$r	= $cb($k, $v);

		if(is_scalar($r)){
			$k	= $r;
		}elseif(is_array($r) && count($r) >= 2 && !($skip_null && is_null($r[1]))){
			[$k, $v]	= $r;
		}else{
			continue;
		}

		if(is_null($k)){
			$data[]	= $v;
		}else{
			$data[$k]	= $v;
		}
	}

	return $data ?? [];
}

function wpjam_fill($keys, $cb){
	return wpjam_array($keys, fn($i, $k)=> [$k, $cb($k, $i)], true);
}

function wpjam_pick($arr, $args){
	return wpjam_array($args, fn($i, $k)=> [$k, wpjam_get($arr, $k)], true);
}

function wpjam_entries($items, $key=null, $value=null){
	$key	??= 0;
	$value	??= (int)($key === 0);

	return wpjam_array($items, fn($k, $v)=> [null, [$key=>$k, $value=>$v]]);
}

function wpjam_column($items, $key=null, $index=null){
	return wpjam_array($items, fn($k, $v)=> [
		is_null($index) || $index === false ? null : ($index === true ? $k : $v[$index]),
		is_array($key) ? wpjam_array($key, fn($i, $j)=> [wpjam_is_assoc_array($key) ? $i : $j, wpjam_get($v, $j)], true) : wpjam_get($v, $key)
	]);
}

function wpjam_map($arr, $cb, $args=[]){
	if(!$arr){
		return $arr;
	}

	$args	= (is_bool($args) || $args === 'deep' ? ['deep'=>(bool)$args] : (is_string($args) ?  ['mode'=>$args] : $args))+['deep'=>false];
	$mode	= in_array($args['mode'] ?? '', ['vk', 'kv', 'k', 'v']) ? $args['mode'] : 'vk';

	return wpjam_array($arr, fn($k, $v)=> [$k, ($args['deep'] && is_array($v)) ? wpjam_map($v, $cb, $args) : $cb(...array_map(fn($c) => $c === 'k' ? $k : $v, str_split($mode)))]);
}

function wpjam_reduce($arr, $cb, $carry=null, ...$args){
	$depth	= $args[1] ?? 0;
	$args	= $args && is_array($args[0]) ? $args[0] : ['key'=>$args[0] ?? ''];
	$key	= $args['key'] ?? '';
	$max	= $args['max_depth'] ?? 0;

	foreach(wpjam_array($arr) as $k => $v){
		$carry	= $cb($carry, $v, $k, $depth);

		if($key && (!$max || $max > $depth+1) && is_array($v)){
			$sub	= $key === true ? $v : wpjam_get($v, $key);
			$carry	= is_array($sub) ? wpjam_reduce($sub, $cb, $carry, $args, $depth+1) : $carry;
		}
	}

	return $carry;
}

function wpjam_nest($items, $options=[]){
	if(!$items){
		return [];
	}

	$object	= wpjam_args($options);
	$parse	= $object->parse = wpjam_bind(function($items, $depth=0, $parent=0){
		$fields	= ($this->fields ?: [])+['id'=>'id', 'name'=>'name', 'children'=>'children'];
		$max	= $this->max_depth;
		$cb		= $this->item_callback;
		$parsed	= [];

		foreach(wpjam_pull($items, $parent) ?: [] as $item){
			$item		= $cb ? $cb($item) : $item;
			$children	= (!$max || $max > $depth+1) ? ($this->parse)($items, $depth+1, wpjam_get($item, $fields['id'])) : [];

			if($this->format == 'flat'){
				$parsed[]	= wpjam_set($item, $fields['name'], str_repeat('&emsp;', $depth).wpjam_get($item, $fields['name']));
				$parsed		= array_merge($parsed, $children);
			}else{
				$parsed[]	= wpjam_set($item, $fields['children'], $children);
			}
		}

		return $parsed;
	}, $object);

	$group		= wpjam_group($items, $object->get_arg('fields[parent]') ?: 'parent');
	$group[0]	= $object->top ? [$object->top] : ($group[0] ?? wpjam_pull($group, array_key_first($group)));

	return $parse($group, 0, 0);
}

function wpjam_at($arr, $index, ...$args){
	if(is_string($arr)){
		[$sep, $index]	= is_int($index) ? [$args[0] ?? '', $index] : [$index, $args[0] ?? 0];

		$sep && ($arr = explode($sep, $arr));
	}

	if(is_array($arr) || is_string($arr)){
		$count	= is_array($arr) ? count($arr) : strlen($arr);
		$index	= $index >= 0 ? $index : $count + $index;

		if($index >= 0 && $index < $count){
			return is_string($arr) ? $arr[$index] : $arr[array_keys($arr)[$index]];
		}
	}
}

function wpjam_add_at($arr, $index, $key, ...$args){
	if(!$args && !is_array($key)){
		$args	= [$key];
		$key	= null;
	}

	if(is_null($key)){
		array_splice($arr, $index, 0, $args);

		return $arr;
	}

	return array_replace(array_slice($arr, 0, $index, true), (is_array($key) ? $key : [$key=>$args[0] ?? '']))+array_slice($arr, $index, null, true);
}

function wpjam_find($arr, $cb, ...$args){
	$output	= 'value';

	if($args){
		if(is_callable($args[0])){
			$output	= 'result';
			$mapper	= $args[0];
		}else{
			$output	= $args[0];
		}
	}

	$cb	= wpjam_is_assoc_array($cb) ? fn($v)=> wpjam_matches($v, $cb) : ($cb ?: fn()=> true);
	$cb	= $cb === true ? fn($v)=> $v : $cb;

	if(!$cb){
		return;
	}

	if($output == 'value'){
		return array_find($arr, $cb);
	}

	if($output == 'key'){
		return array_find_key($arr, $cb);
	}

	if($output == 'index'){
		return array_search(array_find_key($arr, $cb), array_keys($arr));
	}

	if($output == 'result'){
		foreach($arr as $k => $v){
			$v	= $mapper($v, $k);

			if($cb($v)){
				return $v;
			}
		}
	}
}

function wpjam_group($arr, $field){
	return wpjam_reduce($arr, fn($c, $v, $k)=> wpjam_set($c, [wpjam_get($v, $field), $k], $v), []);
}

function wpjam_pull(&$arr, $key, ...$args){
	$value	= (is_array($key) ? 'wpjam_pick' : 'wpjam_get')($arr, $key, ...$args);
	$arr	= wpjam_except($arr, $key);

	return $value;
}

function wpjam_assoc($arr){
	if(!wp_is_numeric_array($arr) && (!($sub = wpjam_filter($arr, fn($v, $k) => is_int($k))) || !array_is_list($sub))){
		return $arr;
	}

	return wpjam_array($arr, fn($k, $v)=> is_int($k) ? $v : $k);
}

function wpjam_except($arr, $key){
	if(is_object($arr)){
		foreach(is_array($key) ? $key : [$key] as $k){
			unset($arr->$k);
		}

		return $arr;
	}

	if(!is_array($arr)){
		trigger_error(var_export($arr, true).':'.var_export($key, true));
		return $arr;
	}

	if(is_array($key) || wpjam_exists($arr, $key)){
		return array_diff_key($arr, is_array($key) ? array_flip($key) : [$key=>'']);
	}

	$key	= wpjam_keys($key);
	$sub	= &$arr;

	while($key){
		$k	= array_shift($key);

		if(empty($key)){
			unset($sub[$k]);
		}elseif(wpjam_exists($sub, $k)){
			$sub = &$sub[$k];
		}else{
			break;
		}
	}

	return $arr;
}

function wpjam_merge($arr, $data){
	foreach($data as $k => $v){
		$arr[$k]	= ((wpjam_is_assoc_array($v) || $v === []) && isset($arr[$k]) && wpjam_is_assoc_array($arr[$k])) ? wpjam_merge($arr[$k], $v) : $v;
	}

	return $arr;
}

function wpjam_diff($arr, $data, $compare='value'){
	if($compare == 'value' && array_is_list($arr) && array_is_list($data)){
		return array_values(array_diff($arr, $data));
	}

	foreach($data as $k => $v){
		if(isset($arr[$k])){
			if(wpjam_is_assoc_array($v) && wpjam_is_assoc_array($arr[$k])){
				$arr[$k]	= wpjam_diff($arr[$k], $v, $compare);

				if(!$arr[$k]){
					unset($arr[$k]);
				}
			}else{
				if($compare == 'key' || $arr[$k] == $v){
					unset($arr[$k]);
				}
			}
		}
	}

	return $arr;
}

function wpjam_toggle($arr, $data){
	return array_merge(array_diff($arr, $data), array_diff($data, $arr));
}

function wpjam_filter($arr, $cb=null, ...$args){
	$list	= array_is_list($arr);

	if($cb){
		if(wpjam_is_assoc_array($cb)){
			$arr	= array_filter($arr, fn($v)=> wpjam_matches($v, $cb, ...$args)); 
		}elseif(wp_is_numeric_array($cb) && !is_callable($cb)){
			$arr	= array_intersect_key($arr, array_flip($cb));
		}elseif($cb === 'unique'){
			$arr	= array_unique($arr);
		}else{
			$cb		= $cb === 'isset' ? fn($v)=> !is_null($v) : $cb;
			$deep	= $args[0] ?? ($cb == 'isset');
			$arr	= $deep ? array_map(fn($v)=> is_array($v) ? wpjam_filter($v, $cb, true) : $v, $arr) : $arr;
			$arr	= array_filter($arr, $cb, ARRAY_FILTER_USE_BOTH);
		}
	}else{
		$arr	= array_filter($arr);
	}

	return $list ? array_values($arr) : $arr;
}

function wpjam_sort($arr, ...$args){
	if(count($arr) <= 1){
		return $arr;
	}

	if(!$args || is_int($args[0])){
		sort($arr, ...$args);

		return $arr;
	}

	if(in_array($args[0], ['', 'k', 'a', 'kr', 'ar', 'r'], true)){
		(array_shift($args).'sort')($arr, ...$args);

		return $arr;
	}

	$is_asc	= fn($v)=> is_int($v) ? $v === SORT_ASC : strtolower($v) === 'asc';

	if(wpjam_is_assoc_array($args[0])){
		$args	= wpjam_reduce($args[0], fn($carry, $order, $field)=>[
			...$carry,
			($column = array_column($arr, $field)),
			$is_asc($order) ? SORT_ASC : SORT_DESC,
			is_numeric(array_first($column)) ? SORT_NUMERIC : SORT_REGULAR
		], []);
	}elseif(is_callable($args[0]) || is_string($args[0])){
		$field	= $args[0];
		$order	= $args[1] ?? '';

		if(is_callable($field)){
			$column	= array_map($field, ($order === 'key' ? array_keys($arr) : $arr));
			$flag	= $args[2] ?? SORT_NUMERIC;
		}else{
			$default= $args[2] ?? 0;
			$column	= array_map(fn($item)=> wpjam_get($item, $field, $default), $arr);
			$flag	= is_numeric($default) ? SORT_NUMERIC : SORT_REGULAR;
		}

		$args	= [$column, ($is_asc($order) ? SORT_ASC : SORT_DESC), $flag];
	}

	array_push($args, range(1, count($arr)), SORT_ASC, SORT_NUMERIC);

	if(wp_is_numeric_array($arr)){
		$keys	= array_keys($arr);
		$args[]	= &$keys;
	}

	$args[] = &$arr;

	array_multisort(...$args);

	return isset($keys) ? array_combine($keys, $arr) : $arr;
}

function wpjam_exists($arr, $key){
	return is_array($arr) ? array_key_exists($key, $arr) : (is_object($arr) ? isset($arr->$key) : false);
}

function wpjam_keys($key, $type=''){
	if($type == '.'){
		return str_contains($key, '.') ? explode('.', $key) : [];
	}

	if($type == '[]'){
		$keys	= [];

		if(str_contains($key, '[') && !str_starts_with($key, '[') && str_ends_with($key, ']')){
			$parts	= preg_split('/(['.preg_quote('[]', '/').'])/', $key, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

			if(count($parts) % 3 != 1){
				return [];
			}

			$keys[]	= array_shift($parts);

			for($i = 0; $i < count($parts); $i += 3){
				if(in_array($parts[$i+1], ['[', ']'], true) || $parts[$i] !== '[' || $parts[$i+2] !== ']'){
					return [];
				}

				$keys[] = $parts[$i+1];
			}
		}

		return $keys;
	}

	return wpjam_find(['[]', '.'], true, fn($v)=> wpjam_keys($key, $v)) ?: [];
}

function wpjam_names($name){
	return is_array($name) ? array_shift($name).($name ? '['.implode('][', $name).']' : '') : (wpjam_keys($name, '[]') ?: [$name]);
}

function wpjam_get($arr, $key, $default=null){
	if(is_object($arr)){
		return $arr->$key ?? $default;
	}

	if(!is_array($arr)){
		trigger_error(var_export($arr, true));
		return $default;
	}

	if(!is_array($key)){
		if(isset($key) && wpjam_exists($arr, $key)){
			return $arr[$key];
		}

		if(is_null($key) || $key === '[]'){
			return $arr;
		}

		if(str_ends_with($key, '[]')){
			$value	= wpjam_get($arr, substr($key, 0, -2), $default);

			return is_object($value) ? [$value] : (array)$value;
		}

		$key	= wpjam_keys($key);
	}

	return _wp_array_get($arr, $key, $default);
}

function wpjam_set($arr, $key, ...$args){
	if(!$args && is_array($key)){
		return wpjam_reduce($key, fn($c, $v, $k)=> wpjam_set($c, $k, $v), $arr);
	}

	$value	= $args[0] ?? null;

	if(is_object($arr)){
		$arr->$key = $value;

		return $arr;
	}

	if(!is_array($arr)){
		return $arr;
	}

	if(!is_array($key)){
		if(isset($key) && wpjam_exists($arr, $key)){
			$arr[$key] = $value;

			return $arr;
		}

		$key	??= '[]';

		if(str_ends_with($key, '[]')){
			$items		= wpjam_get($arr, $key);
			$items[]	= $value;

			return $key === '[]' ? $items : wpjam_set($arr, substr($key, 0, -2), $items);
		}

		$key	= wpjam_keys($key) ?: [$key];
	}

	_wp_array_set($arr, $key, $value);

	return $arr;
}

function wpjam_some($arr, $cb){
	foreach($arr as $k => $v){
		if($cb($v, $k)){
			return true;
		}
	}

	return false;
}

function wpjam_every($arr, $cb){
	foreach($arr as $k => $v){
		if(!$cb($v, $k)){
			return false;
		}
	}

	return true;
}

function wpjam_lines($str, ...$args){
	[$sep, $cb]	= count($args) == 1 && is_closure($args[0]) ? ["\n", $args[0]] : ($args+["\n", null]);

	return array_reduce(explode($sep, $str ?: ''), fn($c, $v)=> ($v = $cb ? $cb(trim($v)) : trim($v)) ? [...$c, $v] : $c, []);
}

if(!function_exists('array_pull')){
	function array_pull(&$arr, $key, ...$args){
		return wpjam_pull($arr, $key, ...$args);
	}
}

if(!function_exists('array_except')){
	function array_except($array, ...$keys){
		return wpjam_except($array, (($keys && is_array($keys[0])) ? $keys[0] : $keys));
	}
}

if(!function_exists('array_find')){
	function array_find($arr, $cb){
		foreach($arr as $k => $v){
			if($cb($v, $k)){
				return $v;
			}
		}
	}
}

if(!function_exists('array_find_key')){
	function array_find_key($arr, $cb){
		foreach($arr as $k => $v){
			if($cb($v, $k)){
				return $k;
			}
		}
	}
}

if(!function_exists('array_first')){
	function array_first($array){
		return $array === [] ? null : $array[array_key_first($array)];  
	}
}

if(!function_exists('array_last')){
	function array_last($array){
		return $array === [] ? null : $array[array_key_last($array)];  
	}
}

function wpjam_move($arr, $id, $data){
	$arr	= array_values($arr);
	$index	= array_search($id, $arr);
	$arr	= wpjam_diff($arr, [$id]);

	$index === false && wpjam_throw('invalid_id', '无效的 ID');

	if(isset($data['pos'])){
		$index	= $data['pos'];
	}elseif(!empty($data['up'])){
		$index == 0 && wpjam_throw('invalid_position', '已经是第一个了,不可上移了!');

		$index--;
	}elseif(!empty($data['down'])){
		$index == count($arr) && wpjam_throw('invalid_position', '已经最后一个了,不可下移了!');

		$index++;
	}else{
		$k		= array_find(['next', 'prev'], fn($k)=> isset($data[$k]));
		$index	= ($k && isset($data[$k])) ? array_search($data[$k], $arr) : false;

		$index === false && wpjam_throw('invalid_position', '无效的移动位置');

		$index	+= $k == 'prev' ? 1 : 0;
	}

	return wpjam_add_at($arr, $index, null, $id);
}

// Bit
function wpjam_has_bit($value, $bit){
	return ((int)$value & (int)$bit) == $bit;
}

function wpjam_add_bit($value, $bit){
	return $value = (int)$value | (int)$bit;
}

function wpjam_remove_bit($value, $bit){
	return $value = (int)$value & (~(int)$bit);
}

// UUID
function wpjam_create_uuid(){
	$chars	= md5(uniqid(mt_rand(), true));

	return implode('-', array_map(fn($v)=> substr($chars, ...$v), [[0, 8], [8, 4], [12, 4], [16, 4], [20, 12]]));
}

// Str
if(!function_exists('try_remove_prefix')){
	function try_remove_prefix(&$str, $prefix){
		$res	= str_starts_with($str, $prefix);
		$str	= $res ? substr($str, strlen($prefix)) : $str;

		return $res;
	}
}

if(!function_exists('try_remove_suffix')){
	function try_remove_suffix(&$str, $suffix){
		$res	= str_ends_with($str, $suffix);
		$str	= $res ? substr($str, 0, -strlen($suffix)) : $str;

		return $res;
	}
}

if(!function_exists('explode_last')){
	function explode_last($sep, $str){
		return ($pos = strrpos($str, $sep)) === false ? [$str] : [substr($str, 0, $pos), substr($str, $pos + strlen($sep))];
	}
}

function wpjam_remove_prefix($str, $prefix){
	return [try_remove_prefix($str, $prefix), $str][1];
}

function wpjam_remove_suffix($str, $suffix){
	return [try_remove_suffix($str, $suffix), $str][1];
}

function wpjam_echo($str){
	echo $str;
}

function wpjam_join($sep, ...$args){
	return join($sep, array_filter(($args && is_array($args[0])) ? $args[0] : $args));
}

function wpjam_remove_pre_tab($str, $times=1){
	return preg_replace('/^\t{'.$times.'}/m', '', $str);
}

function wpjam_preg_replace($pattern, $replace, $subject, $limit=-1, &$count=null, $flags=0){
	$result	= is_closure($replace) ? preg_replace_callback($pattern, $replace, $subject, $limit, $count, $flags) : preg_replace($pattern, $replace, $subject, $limit, $count);

	if(is_null($result)){
		trigger_error(preg_last_error_msg());
		return $subject;
	}

	return $result;
}

function wpjam_serialize($data){
	return maybe_serialize(wpjam_sort(wpjam_map($data, fn($v)=> is_closure($v) ? spl_object_hash($v) : $v, true), 'k'));
}

function wpjam_unserialize($serialized, $cb=null){
	if($serialized){
		$result	= @unserialize($serialized);

		if(!$result){
			$fixed	= preg_replace_callback('!s:(\d+):"(.*?)";!', fn($m)=> 's:'.strlen($m[2]).':"'.$m[2].'";', $serialized);
			$result	= @unserialize($fixed);

			if($result && $cb){
				$cb($fixed);
			}
		}

		return $result;
	}
}

// 去掉非 utf8mb4 字符
function wpjam_strip_invalid_text($text){
	return $text ? iconv('UTF-8', 'UTF-8//IGNORE', $text) : '';
}

// 去掉 4字节 字符
function wpjam_strip_4_byte_chars($text){
	return $text ? preg_replace('/[\x{10000}-\x{10FFFF}]/u', '', $text) : '';
	// return preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $text);	// \xEF\xBF\xBD 常用来表示未知、未识别或不可表示的字符
}

// 移除 除了 line feeds 和 carriage returns 所有控制字符
function wpjam_strip_control_chars($text){
	return $text ? preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F]/u', '', $text) : '';
	// return preg_replace('/[\x00-\x09\x0B\x0C\x0E-\x1F\x80-\x9F]/u', '', $text);
}

//获取第一段
function wpjam_get_first_p($text){
	return $text ? (wpjam_lines(wp_strip_all_tags($text))[0] ?? '') : '';
}

function wpjam_unicode_decode($text){
	return wpjam_preg_replace('/\\\\u(?!00[0-7][0-9A-F])[0-9a-fA-F]{4}/i', fn($m)=> json_decode('"'.$m[0].'"') ?: $m[0], $text);
}

function wpjam_zh_urlencode($url){
	return $url ? wpjam_preg_replace('/[\x{4e00}-\x{9fa5}]+/u', fn($m)=> urlencode($m[0]), $url) : '';
}

// 检查非法字符
function wpjam_blacklist_check($text, $name='内容'){
	$pre	= $text ? apply_filters('wpjam_pre_blacklist_check', null, $text, $name) : false;
	$pre	= $pre ?? array_any(wpjam_lines(get_option('disallowed_keys')), fn($w)=> (trim($w) && preg_match("#".preg_quote(trim($w), '#')."#i", $text)));

	return $pre;
}

function wpjam_doing_debug(){
	return isset($_GET['debug']) && $_GET['debug'] ? sanitize_key($_GET['debug']) : isset($_GET['debug']);
}

function wpjam_expandable($str, $num=10, $name=null){
	if(is_a($str, 'WPJAM_Tag') || count(explode("\n", $str)) > $num){
		static $index = 0;

		$name	= 'expandable_'.($name ?? (++$index));

		return '<div class="expandable-container"><input type="checkbox" class="button" id="'.esc_attr($name).'" /><div class="inner">'.$str.'</div></div>';
	}

	return $str;
}

function wpjam_get_current_page_url(){
	return set_url_scheme('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
}

// Date
function wpjam_date($format, $ts=null){
	$ts	??= time();
	$dt	= $ts ? date_create('@'.$ts) : null;

	return $dt ? $dt->setTimezone(wp_timezone())->format($format) : '';
}

function wpjam_strtotime($str){
	$dt	= $str ? date_create($str, wp_timezone()) : null;

	return $dt ? $dt->getTimestamp() : 0;
}

function wpjam_human_time_diff($from, $to=0){
	return sprintf(__('%s '.(($to ?: time()) > $from ? 'ago' : 'from now'), 'wpjam'), human_time_diff($from, $to));
}

function wpjam_human_date_diff($from, $to=0){
	$zone	= wp_timezone();
	$to		= $to ? date_create($to, $zone) : current_datetime();
	$from	= date_create($from, $zone);
	$day	= [
		0	=> __('Today', 'wpjam'),
		-1	=> __('Yesterday', 'wpjam'),
		-2	=> __('The day before yesterday', 'wpjam'),
		1	=> __('Tomorrow', 'wpjam'),
		2	=> __('The day after tomorrow', 'wpjam')
	][(int)$to->diff($from)->format('%R%a')] ?? '';

	return $day ?: ($from->format('W') == $to->format('W') ? __($from->format('l'), 'wpjam') : $from->format(__('F, Y', 'wpjam')));
}

// Video
function wpjam_get_video_mp4($id_or_url){
	if(filter_var($id_or_url, FILTER_VALIDATE_URL)){
		if(preg_match('#http://www.miaopai.com/show/(.*?).htm#i',$id_or_url, $matches)){
			return 'http://gslb.miaopai.com/stream/'.esc_attr($matches[1]).'.mp4';
		}

		return ($id	= wpjam_get_qqv_id($id_or_url)) ? wpjam_get_qqv_mp4($id) : wpjam_zh_urlencode($id_or_url);
	}

	return wpjam_get_qqv_mp4($id_or_url);
}

function wpjam_get_qqv_mp4($vid, $cache=true){
	strlen($vid) > 20 && wpjam_throw('error', '无效的腾讯视频');

	if($cache){
		return wpjam_transient('qqv_mp4:'.$vid, fn()=> wpjam_get_qqv_mp4($vid, false), HOUR_IN_SECONDS*6);
	}

	$response	= wpjam_remote_request('http://vv.video.qq.com/getinfo?otype=json&platform=11001&vid='.$vid, ['timeout'=>4, 'throw'=>true]);
	$response	= trim(substr($response, strpos($response, '{')),';');
	$response	= wpjam_try('wpjam_json_decode', $response);

	empty($response['vl']) && wpjam_throw('error', '腾讯视频不存在或者为收费视频!');

	$u	= $response['vl']['vi'][0];

	return $u['ul']['ui'][0]['url'].$u['fn'].'?vkey='.$u['fvkey'];
}

function wpjam_get_qqv_id($id_or_url){
	if(filter_var($id_or_url, FILTER_VALIDATE_URL)){
		return wpjam_find(['#https://v.qq.com/x/page/(.*?).html#i', '#https://v.qq.com/x/cover/.*/(.*?).html#i'], true, fn($v)=> preg_match($v, $id_or_url, $m) ? $m[1] : '') ?: '';
	}

	return $id_or_url;
}

// 打印
function wpjam_print_r($value){
	$capability	= is_multisite() ? 'manage_site' : 'manage_options';

	if(current_user_can($capability)){
		echo '<pre>';
		print_r($value);
		echo '</pre>'."\n";
	}
}

function wpjam_var_dump($value){
	$capability	= is_multisite() ? 'manage_site' : 'manage_options';
	if(current_user_can($capability)){
		echo '<pre>';
		var_dump($value);
		echo '</pre>'."\n";
	}
}

function wpjam_is_mobile_number($number){
	return preg_match('/^0{0,1}(1[3,5,8][0-9]|14[5,7]|166|17[0,1,3,6,7,8]|19[8,9])[0-9]{8}$/', $number);
}

function wpjam_set_cookie($key, $value, $expire=DAY_IN_SECONDS){
	if(is_null($value)){
		unset($_COOKIE[$key]);
	}else{
		$_COOKIE[$key]	= $value;

		$expire	+= $expire < time() ? time() : 0;
	}

	setcookie($key, $value ?? '', $expire, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);

	COOKIEPATH != SITECOOKIEPATH && setcookie($key, $value ?? '', $expire, SITECOOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);
}

function wpjam_clear_cookie($key){
	wpjam_set_cookie($key, null, time()-YEAR_IN_SECONDS);
}

function wpjam_get_filter_name($name, $type){
	return (str_starts_with($name, 'wpjam') ? '' : 'wpjam_').str_replace('-', '_', $name).'_'.$type;
}

function wpjam_get_filesystem(){
	if(empty($GLOBALS['wp_filesystem'])){
		if(!function_exists('WP_Filesystem')){
			require_once(ABSPATH.'wp-admin/includes/file.php');
		}

		WP_Filesystem();
	}

	return $GLOBALS['wp_filesystem'];
}