HEX
Server: nginx/1.22.1
System: Linux VM-16-9-centos 3.10.0-1160.99.1.el7.x86_64 #1 SMP Wed Sep 13 14:19:20 UTC 2023 x86_64
User: www (1001)
PHP: 7.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/softfox.com.cn/wp-content/plugins/wpjam-basic/includes/class-wpjam-util.php
<?php
class WPJAM_Attr extends WPJAM_Args{
	public function __set($key, $value){
		if($key == 'attr'){
			$this->args	= $value;
		}else{
			parent::__set($key, $value);
		}
	}

	public function __toString(){
		return (string)$this->render();
	}

	public function jsonSerialize(){
		return $this->render();
	}

	public function render(){
		$args	= $this->get_args();
		$data	= array_wrap(array_pull($args, 'data'), []);
		$attr	= ['data'=>$data];
		$class	= array_wrap(array_pull($args, 'class'));
		$class	= array_filter($class);
		$style	= (array)array_pull($args, 'style', []);

		foreach(['readonly', 'disabled'] as $key){
			if($this->$key){
				$class[]	= $key;
			}
		}

		if($class){
			$attr['class']	= implode(' ', array_unique($class));
		}

		if($style){
			foreach($style as $k => &$v){
				$v	= $v ? ((is_numeric($k) ? '' : $k.':').rtrim($v, ';')) : '';
			}

			$attr['style']	= implode(' ', array_unique(array_filter($style)));
		}

		if(isset($this->value)){
			$attr['value']	= $this->value;
		}

		foreach($args as $key => $value){
			if(str_ends_with($key, '_callback') || str_starts_with($key, '_') || is_blank($value)){
				continue;
			}

			if(str_starts_with($key, 'data-')){
				$key	= wpjam_remove_prefix($key, 'data-');

				$attr['data'][$key]	= $value;
			}elseif($this->is_bool($key)){
				$attr[$key]	= $key;
			}elseif(is_scalar($value)){
				$attr[$key]	= $value;
			}else{
				trigger_error($key.' '.var_export($value, true).var_export($this, true));
			}
		}

		$items	= [];

		foreach($attr as $key => $value){
			if($key == 'data'){
				$items[]	= self::create($value, 'data');
			}else{
				$items[]	= $key.'="'.esc_attr($value).'"';
			}
		}

		return $items ? ' '.implode(' ', array_filter($items)) : '';
	}

	public static function parse_attr($attr){
		$parsed	= [];

		foreach($attr as $key => $value){
			if(is_numeric($key)){
				$key	= $value = strtolower(trim($value));

				if(!self::is_bool($key)){
					continue;
				}
			}else{
				$key	= strtolower(trim($key));

				if(self::is_bool($key)){
					if(!$value){
						continue;
					}

					$value	= $key;
				}
			}

			$parsed[$key]	= $value;
		}

		return $parsed;
	}

	public static function create($attr, $type=''){
		if($type == 'data'){
			return new WPJAM_Data_Attr($attr);
		}

		return new WPJAM_Attr($attr);
	}

	public static function is_bool($attr){
		return in_array($attr, ['allowfullscreen', 'allowpaymentrequest', 'allowusermedia', 'async', 'autofocus', 'autoplay', 'checked', 'controls', 'default', 'defer', 'disabled', 'download', 'formnovalidate', 'hidden', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nomodule', 'novalidate', 'open', 'playsinline', 'readonly', 'required', 'reversed', 'selected', 'typemustmatch']);
	}
}

class WPJAM_Data_Attr extends WPJAM_Attr{
	public function render(){
		$items	= [];

		foreach($this as $key => $value){
			if(isset($value) && $value !== false){
				if(is_scalar($value)){
					$value	= esc_attr($value);
				}else{
					if($key == 'data'){
						$value	= http_build_query($value);
					}else{
						if($key == 'show_if'){
							$value	= array_except($value, '_parsed');
						}

						$value	= wpjam_json_encode($value);
					}
				}

				$items[]	= 'data-'.$key.'=\''.$value.'\'';
			}
		}

		return $items ? implode(' ', $items) : '';
	}
}

class WPJAM_Tag extends WPJAM_Attr{
	protected $tag		= '';
	protected $text		= '';
	protected $_before	= [];
	protected $_after	= [];
	protected $_prepend	= [];
	protected $_append	= [];

	public function __construct($tag='', $attr=[], $text=''){
		$this->tag	= $tag;
		$this->attr	= ($attr && wp_is_numeric_array($attr)) ? ['class'=>$attr] : $attr;

		if($text && is_array($text)){
			$this->text(...$text);
		}else{
			$this->text	= (string)$text;
		}
	}

	public function __call($method, $args){
		if(in_array($method, ['text', 'tag', 'before', 'after', 'prepend', 'append'])){
			if($args){
				if(count($args) > 1){
					if(!is_array($args[1])){
						$attr	= $args[2] ?? [];

						$args[0]	= new self($args[1], $attr, $args[0]);
					}else{
						$args[0]	= new self(...$args);
					}
				}else{
					if(!$args[0] && !in_array($method, ['text', 'tag'])){
						return $this;
					}
				}

				if($method == 'text'){
					$this->text	= $args[0];
				}elseif($method == 'tag'){
					$this->tag	= $args[0];
				}elseif(in_array($method, ['before', 'prepend'])){
					array_unshift($this->{'_'.$method}, $args[0]);
				}elseif(in_array($method, ['after', 'append'])){
					array_push($this->{'_'.$method}, $args[0]);
				}

				return $this;
			}else{
				if($method == 'text'){
					return $this->text;
				}elseif($method == 'tag'){
					return $this->tag;
				}else{
					return $this->{'_'.$method};
				}
			}
		}elseif(in_array($method, ['insert_before', 'insert_after', 'append_to', 'prepend_to'])){
			$method	= str_replace(['insert_', '_to'], '', $method);

			return call_user_func([$args[0], $method], $this);
		}elseif(in_array($method, ['render_before', 'render_after', 'render_append', 'render_prepend'])){
			$key	= str_replace('render', '', $method);

			return $this->$key ? implode('', $this->$key) : '';
		}elseif($method == 'get_children'){
			$children	= $this->_prepend;

			if($this->text && $this->text instanceof self){
				$children[]	= $this->text;
			}

			return array_merge($children, $this->_append);
		}
	}

	public function render(){
		if($this->tag == 'a' && is_null($this->href)){
			$this->href	= 'javascript:;';
		}

		$single	= $this->is_single($this->tag);
		$result	= $this->render_before();

		if($this->tag){
			$result	.= '<'.$this->tag.parent::render();
			$result	.= $single ? ' />' : '>';
		}

		if(!$single){
			$result	.= $this->render_prepend();
			$result	.= $this->text;
			$result	.= $this->render_append();

			if($this->tag){
				$result	.= '</'.$this->tag.'>';
			}
		}

		return $result	.= $this->render_after();
	}

	public function wrap($tag, $attr=[]){
		if($tag){
			$this->text	= $this->render();
			$this->tag	= $tag;

			if($attr && (!is_array($attr) || wp_is_numeric_array($attr))){
				$this->attr	= ['class'=>$attr];
			}else{
				$this->attr	= $attr;
			}

			return $this->flush();
		}

		return $this;
	}

	public function flush(){
		$this->_before	= $this->_after = $this->_prepend = $this->_append = [];

		return $this;
	}

	public static function is_single($tag){
		return in_array($tag, ['area', 'base', 'basefont', 'br', 'col', 'command', 'embed', 'frame', 'hr', 'img', 'input', 'isindex', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
	}
}

class WPJAM_Array extends WPJAM_Args{
	public function __construct($array=[]){
		parent::__construct(array_wrap($array, []));
	}

	public function exists($key){
		return $this->get($key) !== null;
	}

	public function get($key, $default=null){
		return $this->get_arg($key, $default);
	}

	public function set($key, $value){
		return $this->update_arg($key, $value);
	}

	public function add(...$args){
		if(count($args) >= 2){
			if(!$this->exists($args[0])){
				return $this->set(...$args);
			}
		}elseif($args){
			$this->args[]	= $value;
		}

		return $this;
	}

	public function delete($key){
		return $this->delete_arg($key);
	}

	public function merge($data){
		$this->args	= merge_deep($this->get_args(), $data);

		return $this;
	}

	public function filter($callback){
		$this->args	= filter_deep($this->get_args(), $callback);

		return $this;
	}
}

class WPJAM_Compare extends WPJAM_Singleton{
	public function parse_show_if($args){
		$this->args	= $args;

		if(!$this->_parsed && (isset($this->compare) || !isset($this->query_arg))){
			$this->_parsed	= true;
			$this->value	= $this->parse_value($this->value, $compare);
			$this->compare	= $compare;

			if(is_array($this->value)){
				$this->value	= array_map('strval', $this->value);	// JS Array.indexof is strict
			}
		}

		return $this->to_array();
	}

	public function compare($value, $args){
		$this->args	= $args;

		$strict	= (bool)$this->strict;
		$value2	= $this->value;
		$key	= $this->key;

		if($key){
			$item	= $value;

			if(is_object($item)){
				$value	= $item->{$key} ?? null;
			}elseif(is_array($item)){
				$value	= $item[$key] ?? null;
			}else{
				$value	= null;
			}

			if($this->callable && is_callable($value)){
				return call_user_func($value, $value2, $item);
			}elseif(is_array($value)){
				$this->swap	= true;
			}
		}

		if(is_null($this->compare) && isset($this->if_null) && is_null($value)){
			return $this->if_null;
		}

		if($this->swap){
			[$value, $value2]	= [$value2, $value];
		}

		$value2	= $this->parse_value($value2, $compare, $value);

		if($compare == '='){
			if($strict){
				return $value === $value2;
			}else{
				return $value == $value2;
			}
		}elseif($compare == '!='){
			if($strict){
				return $value !== $value2;
			}else{
				return $value != $value2;
			}
		}elseif($compare == '>'){
			return $value > $value2;
		}elseif($compare == '>='){
			return $value >= $value2;
		}elseif($compare == '<'){
			return $value < $value2;
		}elseif($compare == '<='){
			return $value <= $value2;
		}elseif($compare == 'IN'){
			if(is_array($value)){
				foreach($value as $v){
					if(!in_array($v, $value2, $strict)){
						return false;
					}
				}

				return true;
			}else{
				return in_array($value, $value2, $strict);
			}
		}elseif($compare == 'NOT IN'){
			if(is_array($value)){
				foreach($value as $v){
					if(in_array($v, $value2, $strict)){
						return false;
					}
				}

				return true;
			}else{
				return !in_array($value, $value2, $strict);
			}
		}elseif($compare == 'BETWEEN'){
			return $value > $value2[0] && $value < $value2[1];
		}elseif($compare == 'NOT BETWEEN'){
			return $value < $value2[0] || $value > $value2[1];
		}

		return false;
	}

	protected function parse_value($value, &$compare=null, $value1=null){
		$compare	= $this->compare;
		$compare	= $compare ? strtoupper($compare) : (is_array($value) ? 'IN' : '=');

		if(in_array($compare, ['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'])){
			$value	= wp_parse_list($value);

			if(!is_array($value1) && count($value) == 1){
				$value		= current($value);
				$compare	= in_array($compare, ['IN', 'BETWEEN']) ? '=' : '!=';
			}
		}else{
			if(is_string($value)){
				$value	= trim($value);
			}
		}

		return $value;
	}

	public function match($item, $args=[], $operator='AND'){
		$operator	= strtoupper($operator);

		if(!in_array($operator, ['AND', 'OR', 'NOT'], true)){
			return false;
		}

		foreach($args as $key => $value){
			if(is_closure($value) || (is_array($value) && is_callable($value))){
				trigger_error('123');	// 2023 11 01 delete
				if(is_object($item) && is_closure($value)){
					$value	= $value->bindTo($item);
					$cb		= [$key];
				}else{
					$cb		= [$key, $item];
				}

				$match	= call_user_func($value, ...$cb);
			}else{
				$value	= wpjam_is_assoc_array($value) ? $value : ['value'=>$value];
				$value	= wp_parse_args($value, ['key'=>$key]);
				$match	= $this->compare($item, $value);
			}

			if($match){
				if('OR' === $operator){
					return true;
				}elseif('NOT' === $operator){
					return false;
				}
			}else{
				if('AND' === $operator){
					return false;
				}
			}
		}

		if('AND' === $operator || 'NOT' === $operator){
			return true;
		}elseif('OR' === $operator){
			return false;
		}
	}
}

class WPJAM_User_Agent extends WPJAM_Args{
	private $user_agent;

	public function __construct($user_agent=null){
		$this->user_agent	= $user_agent ?? wpjam_get_user_agent();
	}

	public function get_parsed(){
		if(!$this->args){
			$this->parse_os();
			$this->parse_browser();
			$this->parse_app();
		}

		return wp_array_slice_assoc($this, ['os', 'device', 'app', 'browser', 'os_version', 'browser_version', 'app_version']);
	}

	public function parse_os(){
		$this->os			= 'unknown';
		$this->device		= '';
		$this->os_version	= 0;

		foreach([
			['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'],
		] as $rule){
			if(stripos($this->user_agent, $rule[0])){
				$this->os	= $rule[1];

				if(isset($rule[2])){
					$this->device	= $rule[2];
				}

				break;
			}
		}

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

					if(strpos($matches[2],';')!==false){
						$this->device	= substr($matches[2], strpos($matches[2],';')+1, strlen($matches[2])-strpos($matches[2],';'));
					}else{
						$this->device	= $matches[2];
					}

					$this->device	= trim($this->device);
					// $build	= trim($matches[3]);
				}
			}
		}
	}

	public function parse_browser(){
		$this->browser 			= '';
		$this->browser_version	= 0;

		foreach([
			['lynx',	'/lynx\/([\d\.]+)/i'],
			['safari',	'/version\/([\d\.]+).*safari/i'],
			['edge',	'/edge\/([\d\.]+)/i'],
			['chrome',	'/chrome\/([\d\.]+)/i'],
			['firefox',	'/firefox\/([\d\.]+)/i'],
			['opera', 	'/(?:opera|opr).([\d\.]+)/i'],
			['ie',		'/msie ([\d\.]+)/i'],
			['ie',		'/rv:([\d.]+) like Gecko/i'],
			// ['Gecko',	'gecko'],

		] as $rule){
			if(preg_match($rule[1], $this->user_agent, $matches)){
				$this->browser 	= $rule[0];
				$this->browser_version	= (float)(trim($matches[1]));

				break;
			}
		}
	}

	public function parse_app(){
		$referer	= $_SERVER['HTTP_REFERER'] ?? '';
		$this->app 		= '';
		$this->app_version	= 0;

		if(strpos($this->user_agent, 'MicroMessenger') !== false){
			if(strpos($referer, 'https://servicewechat.com') !== false){
				$this->app	= 'weapp';
			}else{
				$this->app	= 'weixin';
			}

			if(preg_match('/MicroMessenger\/(.*?)\s/', $this->user_agent, $matches)){
				$this->app_version = (float)$matches[1];
			}

			// if(preg_match('/NetType\/(.*?)\s/', $this->user_agent, $matches)){
			// 	$net_type = $matches[1];
			// }
		}elseif(strpos($this->user_agent, 'ToutiaoMicroApp') !== false 
			|| strpos($referer, 'https://tmaservice.developer.toutiao.com') !== false
		){
			$this->app	= 'bytedance';
		}
	}
}

class WPJAM_File{
	protected $value;
	protected $type;

	public function __construct($value='', $type=''){
		$this->value	= $value;
		$this->type		= $type ?: (is_numeric($value) ? 'id' : 'url');
	}

	public function __get($key){
		if($key == $this->type){
			return $this->value;
		}elseif($this->type == 'id'){
			return wpjam_get_attachment_value($this->value, $key);
		}elseif(in_array($this->type, ['url', 'file', 'path'])){
			return self::convert($this->value, $this->type, $key);
		}
	}

	public function __isset($key){
		return $this->$key !== null;
	}

	public static function convert($value, $from='path', $to='file'){
		$dir	= wp_get_upload_dir();

		if($from == 'path'){
			$path	= $value;
		}else{
			if($from == 'url'){
				$value	= parse_url($value, PHP_URL_PATH);
				$base	= parse_url($dir['baseurl'], PHP_URL_PATH);
			}elseif($from == 'file'){
				$base	= $dir['basedir'];
			}

			if(!str_starts_with($value, $base)){
				return null;
			}

			$path	= wpjam_remove_prefix($value, $base);
		}

		if($to == 'path'){
			return $path;
		}elseif($to == 'file'){
			return $dir['basedir'].$path;
		}elseif($to == 'url'){
			return $dir['baseurl'].$path;
		}elseif($to == 'size'){
			$file	= $dir['basedir'].$path;
			$size	= file_exists($file) ? wp_getimagesize($file) : [];

			if($size){
				return ['width'=>$size[0], 'height'=>$size[1]];
			}
		}

		$id		= self::get_id_by_meta($path);

		return $id ? wpjam_get_attachment_value($id, $to) : null;
	}

	public static function get_id_by_meta($value, $key='_wp_attached_file'){
		if($key == '_wp_attached_file'){
			$value	= ltrim($value, '/');
		}

		$meta	= wpjam_get_by_meta('post', $key, $value);

		if($meta){
			$id	= current($meta)['post_id'];

			if(get_post_type($id) == 'attachment'){
				return $id;
			}
		}

		return '';
	}

	public static function add_to_media($file, $post_id=0){
		if(is_array($file)){
			$upload	= $file;
			$file	= $upload['file'] ?? '';
			$url	= $upload['url'] ?? '';
			$type	= $upload['type'] ?? '';
		}else{
			$url	= $type = '';
		}

		if(!$file){
			return;
		}

		$path	= self::convert($file, 'file', 'path');
		$id		= self::get_id_by_meta($path);

		if($id){
			return $id;
		}

		$url	= $url ?: self::convert($file, 'file', 'url');
		$type	= $type ?: mime_content_type($file);

		if(!$url){
			return;
		}

		require_once ABSPATH.'wp-admin/includes/image.php';

		$title		= preg_replace('/\.[^.]+$/', '', wp_basename($file));
		$content	= '';
		$image_meta	= wp_read_image_metadata($file);

		if($image_meta ) {
			if(trim($image_meta['title']) && ! is_numeric(sanitize_title($image_meta['title']))){
				$title	= $image_meta['title'];
			}

			if(trim($image_meta['caption'])){
				$content	= $image_meta['caption'];
			}
		}

		$id	= wp_insert_attachment([
			'post_title'		=> $title,
			'post_content'		=> $content,
			'post_parent'		=> $post_id,
			'post_mime_type'	=> $type,
			'guid'				=> $url,
		], $file, $post_id, true);

		if(!is_wp_error($id)){
			wp_update_attachment_metadata($id, wp_generate_attachment_metadata($id, $file));
		}

		return $id;
	}

	public static function parse_args($name, $media=true, $post_id=0){
		if(is_array($name)){
			$args	= wp_parse_args($name, [
				'name'		=> '',
				'media'		=> false,
				'post_id'	=> 0,
			]);
		}else{
			$args	= [
				'name'		=> $args,
				'media'		=> $media,
				'post_id'	=> $post_id,
			];
		}

		if(empty($args['field'])){
			$args['field']	= $args['media'] ? 'id' : 'file';
		}

		return $args;
	}
}

class WPJAM_Image extends WPJAM_File{
	public function is_valid(){
		if($this->type == 'url'){
			$ext_types	= wp_get_ext_types();
			$img_exts	= $ext_types['image'];
			$img_parts	= explode('?', $this->value);
			$img_url	= wpjam_remove_postfix($img_parts[0], '#');

			return preg_match('/\.('.implode('|', $img_exts).')$/i', $img_url);
		}elseif($this->type == 'file'){
			return !empty($this->size);
		}elseif($this->type == 'id'){
			return wp_attachment_is_image($this->value);
		}
	}

	public function get_size(){
		$size	= $this->size ?: [];
		$size	= apply_filters('wpjam_image_size', $size, $this->value, $this->type);

		if($size){
			$size = array_map('intval', $size);

			$size['orientation']	= $size['height'] > $size['width'] ? 'portrait' : 'landscape';
		}

		return $size;
	}

	public function get_thumbnail(...$args){
		if($this->url){
			$img_url	= wpjam_zh_urlencode($this->url);	// 中文名
			$img_url	= remove_query_arg(['orientation', 'width', 'height'], $img_url);

			return apply_filters('wpjam_thumbnail', $img_url, self::parse_thumbnail_args(...$args));
		}

		return '';
	}

	public function parse_query(){
		$query	= parse_url($this->url, PHP_URL_QUERY);
		$query	= wp_parse_args($query);

		foreach(['width', 'height'] as $key){
			if(isset($query[$key])){
				$query[$key]	= (int)$query[$key];
			}
		}

		return $query;
	}

	public static function parse_thumbnail_args(...$args){
		$args_num	= count($args);

		if($args_num == 0){
			// 1. 无参数
			return [];
		}elseif($args_num == 1){
			// 2. ['width'=>100, 'height'=>100]	// 这个为最标准版本
			// 3. [100,100]
			// 4. 100x100
			// 5. 100

			return self::parse_size($args[0]);
		}else{
			if(is_numeric($args[0])){
				// 6. 100, 100, $crop=1

				return [
					'width'		=> $args[0] ?? 0,
					'height'	=> $args[1] ?? 0,
					'crop'		=> $args[2] ?? 1
				];
			}else{
				// 7.【100,100], $crop=1

				return array_merge(self::parse_size($args[0]), ['crop'=>$args[1] ?? 1]);
			}
		}
	}

	public static function parse_size($size, $ratio=1){
		if(is_array($size)){
			if(!wp_is_numeric_array($size)){
				$size['width']	= !empty($size['width']) ? ((int)$size['width'])*$ratio : 0;
				$size['height']	= !empty($size['height']) ? ((int)$size['height'])*$ratio : 0;
				$size['crop']	= $size['crop'] ?? ($size['width'] && $size['height']);

				return $size;
			}else{
				$width	= $size[0] ?? 0;
				$height	= $size[1] ?? 0;
			}
		}else{
			$size	= str_replace(['*','X'], 'x', $size);

			if(strpos($size, 'x') !== false){
				$size	= explode('x', $size);
				$width	= $size[0];
				$height	= $size[1];
				$crop	= true;
			}elseif(is_numeric($size)){
				$width	= $size;
				$height	= 0;
			}elseif($size == 'thumb' || $size == 'thumbnail'){
				$width	= get_option('thumbnail_size_w') ?: 100;
				$height = get_option('thumbnail_size_h') ?: 100;
				$crop	= get_option('thumbnail_crop');
			}elseif($size == 'medium'){
				$width	= get_option('medium_size_w') ?: 300;
				$height	= get_option('medium_size_h') ?: 300;
				$crop	= false;
			}else{
				if($size == 'medium_large'){
					$width	= get_option('medium_large_size_w');
					$height	= get_option('medium_large_size_h');
					$crop	= false;
				}elseif($size == 'large'){
					$width	= get_option('large_size_w') ?: 1024;
					$height	= get_option('large_size_h') ?: 1024;
					$crop	= false;
				}else{
					$_sizes = wp_get_additional_image_sizes();

					if(isset($_sizes[$size])){
						$width	= $_sizes[$size]['width'];
						$height	= $_sizes[$size]['height'];
						$crop	= $_sizes[$size]['crop'];
					}else{
						$width	= $height = 0;
					}
				}

				if($width && !empty($GLOBALS['content_width'])){
					$max_width	= $GLOBALS['content_width'] * $ratio;
					$width		= min($max_width, $width);
				}
			}
		}

		return [
			'crop'		=> $crop ?? ($width && $height),
			'width'		=> (int)$width * $ratio,
			'height'	=> (int)$height * $ratio
		];
	}

	public static function constrain_size($size, $max_width, $max_height){
		$size	= self::parse_size($size);

		if($size['width'] && $size['height']){
			list($size['width'], $size['height'])	= wp_constrain_dimensions($size['width'], $size['height'], $max_width, $max_height);
		}elseif($size['width']){
			$size['width']	= $size['width'] < $max_width ? $size['width'] : $max_width;
		}else{
			$size['height']	= $size['height'] < $max_height ? $size['height'] : $max_height;
		}

		return $size;
	}
}

class WPJAM_Bit{
	protected $value	= 0;

	public function __construct($value=0){
		$this->value	= (int)$value;
	}

	public function __get($name){
		return $name == 'value' ? $this->value : null;
	}

	public function has($bit){
		$bit	= (int)$bit;

		return ($this->value & $bit) == $bit;
	}

	public function add($bit){
		$this->value = $this->value | (int)$bit;

		return $this;
	}

	public function remove($bit){
		$this->value = $this->value & (~(int)$bit);

		return $this;
	}
}

class WPJAM_Crypt extends WPJAM_Args{
	public function __construct($args=[]){
		$this->args	= wp_parse_args($args, [
			'method'		=> 'aes-256-cbc',
			'key'			=> '',
			'iv'			=> '',
			'options'		=> OPENSSL_ZERO_PADDING,
			'block_size'	=> 32,	// 注意 PHP 默认 aes cbc 算法的 block size 都是 16 位
		]);
	}

	public function encrypt($text){
		if($this->options == OPENSSL_ZERO_PADDING && $this->block_size){
			$text	= $this->pkcs7_pad($text, $this->block_size);	//使用自定义的填充方式对明文进行补位填充
		}

		return openssl_encrypt($text, $this->method, $this->key, $this->options, $this->iv);
	}

	public function decrypt($encrypted_text){
		try{
			$text	= openssl_decrypt($encrypted_text, $this->method, $this->key, $this->options, $this->iv);
		}catch(Exception $e){
			return new WP_Error('decrypt_aes_failed', 'aes 解密失败');
		}

		if($this->options == OPENSSL_ZERO_PADDING && $this->block_size){
			$text	= $this->pkcs7_unpad($text, $this->block_size);	//去除补位字符
		}

		return $text;
	}

	public static function pkcs7_pad($text, $block_size=32){	//对需要加密的明文进行填充 pkcs#7 补位
		//计算需要填充的位数
		$amount_to_pad	= $block_size - (strlen($text) % $block_size);
		$amount_to_pad	= $amount_to_pad ?: $block_size;

		//获得补位所用的字符
		return $text.str_repeat(chr($amount_to_pad), $amount_to_pad);
	}

	public static function pkcs7_unpad($text, $block_size){	//对解密后的明文进行补位删除
		$pad	= ord(substr($text, -1));

		if($pad < 1 || $pad > $block_size){
			$pad	= 0;
		}

		return substr($text, 0, (strlen($text) - $pad));
	}

	public static function weixin_pad($text, $appid){
		$random = self::generate_random_string(16);		//获得16位随机字符串,填充到明文之前
		return $random.pack("N", strlen($text)).$text.$appid;
	}

	public static function weixin_unpad($text, &$appid){	//去除16位随机字符串,网络字节序和AppId
		$text		= substr($text, 16, strlen($text));
		$len_list	= unpack("N", substr($text, 0, 4));
		$text_len	= $len_list[1];
		$appid		= substr($text, $text_len + 4);

		return substr($text, 4, $text_len);
	}

	public static function sha1(...$args){
		sort($args, SORT_STRING);

		return sha1(implode($args));
	}

	public static function generate_weixin_signature($token, &$timestamp='', &$nonce='', $encrypt_msg=''){
		$timestamp	= $timestamp ?: time();
		$nonce		= $nonce ?: self::generate_random_string(8);
		return self::sha1($encrypt_msg, $token, $timestamp, $nonce);
	}

	public static function generate_random_string($length){
		$alphabet	= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
		$max		= strlen($alphabet);
		$token		= '';

		for($i = 0; $i < $length; $i++){
			$token	.= $alphabet[self::crypto_rand_secure(0, $max - 1)];
		}

		return $token;
	}

	private static function crypto_rand_secure($min, $max){
		$range	= $max - $min;

		if($range < 1){
			return $min;
		}

		$log	= ceil(log($range, 2));
		$bytes	= (int)($log / 8) + 1;		// length in bytes
		$bits	= (int)$log + 1;			// length in bits
		$filter	= (int)(1 << $bits) - 1;	// set all lower bits to 1

		do {
			$rnd	= hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
			$rnd	= $rnd & $filter;	// discard irrelevant bits
		}while($rnd > $range);

		return $min + $rnd;
	}
}

class WPJAM_Updater extends WPJAM_Args{
	public function get_data($file){
		$key		= 'update_'.$this->plural.':'.$this->hostname;
		$response	= get_transient($key);

		if($response === false){
			$response	= wpjam_remote_request($this->update_url);	// https://api.wordpress.org/plugins/update-check/1.1/

			if(!is_wp_error($response)){
				if(isset($response['template']['table'])){
					$response	= $response['template']['table'];
				}else{
					$response	= $response[$this->plural];
				}

				set_transient($key, $response, MINUTE_IN_SECONDS);
			}else{
				$response	= false;
			}
		}

		if($response){
			if(isset($response['fields']) && isset($response['content'])){
				$fields	= array_column($response['fields'], 'index', 'title');
				$index	= $fields[$this->label];

				foreach($response['content'] as $item){
					if($item['i'.$index] == $file){
						$data	= [];

						foreach($fields as $name => $index){
							$data[$name]	= $item['i'.$index] ?? '';
						}

						return [
							$this->type		=> $file,
							'url'			=> $data['更新地址'],
							'package'		=> $data['下载地址'],
							'icons'			=> [],
							'banners'		=> [],
							'banners_rtl'	=> [],
							'new_version'	=> $data['版本'],
							'requires_php'	=> $data['PHP最低版本'],
							'requires'		=> $data['最低要求版本'],
							'tested'		=> $data['最新测试版本'],
						];
					}
				}
			}else{
				return $response[$file] ?? [];
			}
		}
	}

	public function filter_update($update, $data, $file, $locales){
		$new_data	= $this->get_data($file);

		if($new_data){
			return wp_parse_args($new_data, [
				'id'		=> $data['UpdateURI'], 
				'version'	=> $data['Version'],
			]);
		}

		return $update;
	}

	public static function create($type, $hostname, $update_url){
		if(in_array($type, ['plugin', 'theme'])){
			$plural	= $type.'s';
			$object	= new self([
				'type'			=> $type,
				'plural'		=> $plural,
				'label'			=> $type == 'plugin' ? '插件' : '主题',
				'hostname'		=> $hostname,
				'update_url'	=> $update_url
			]);

			add_filter('update_'.$plural.'_'.$hostname, [$object, 'filter_update'], 10, 4);
		}
	}
}

class WPJAM_Cache extends WPJAM_Args{
	use WPJAM_Instance_Trait;

	public function __construct($group, $args=[]){
		$this->args		= $args;
		$this->group	= $group;

		if($this->pull('global')){
			wp_cache_add_global_groups($group);
		}
	}

	public function __call($method, $args){
		if(str_starts_with($method, 'cache_')){
			$method	= wpjam_remove_prefix($method, 'cache_');
		}

		$key	= array_pull($args, 0);
		$key	= $this->key($key);

		if(in_array($method, ['get', 'delete'])){
			return call_user_func('wp_cache_'.$method, $key, $this->group);
		}elseif(in_array($method, ['add', 'replace', 'set'])){
			$value	= $args[1];
			$time	= $args[2] ?? ($this->cache_time ?: DAY_IN_SECONDS);

			return call_user_func('wp_cache_'.$method, $key, $value, $this->group, $time);
		}elseif(str_ends_with($method, '_item') || $method == 'empty'){
			trigger_error('2023-03-01删除');
			$method			= wpjam_remove_postfix($method, '_item');
			$items_object	= new WPJAM_Cache_Items($key, ['group'=>$this]);

			return call_user_func_array([$items_object, $method], $args);
		}
	}

	protected function key($key){
		return implode(':', array_filter([$this->prefix, $key]));
	}

	public function get_with_cas($key, &$cas_token){
		return wp_cache_get_with_cas($this->key($key), $this->group, $cas_token);
	}

	public function cas($cas_token, $key, $value, $expire=null){
		$expire	= $expire ?? ($this->cache_time ?: DAY_IN_SECONDS);

		return wp_cache_cas($cas_token, $this->key($key), $value, $this->group, $expire);
	}

	public function cache_get_with_cas($key, &$cas_token){
		return $this->get_with_cas($key, $cas_token);
	}

	public function cache_cas($cas_token, $key, $value, $expire=null){
		return $this->cas($cas_token, $key, $value, $expire);
	}

	public static function get_instance($group, $args=[]){
		$prefix	= $args['prefix'] ?? '';
		$name	= $group.($prefix ? ':'.$prefix : '');
		$object	= self::instance_exists($name);

		if(!$object && !is_null($args)){
			$object	= self::add_instance($name, new self($group, $args));
		}

		return $object;
	}

	/* HTML 片段缓存
	Usage:

	if (!WPJAM_Cache::output('unique-key')) {
		functions_that_do_stuff_live();
		these_should_echo();
		WPJAM_Cache::store(3600);
	}
	*/
	public static function output($key) {
		$output	= get_transient($key);

		if(!empty($output)) {
			echo $output;
			return true;
		}else{
			ob_start();
			return false;
		}
	}

	public static function store($key, $cache_time='600') {
		$output = ob_get_flush();
		set_transient($key, $output, $cache_time);
		echo $output;
	}
}

class IP{
	private static $ip = null;
	private static $fp = null;
	private static $offset = null;
	private static $index = null;
	private static $cached = [];

	public static function find($ip){
		if (empty( $ip ) === true) {
			return 'N/A';
		}

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

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

		if (isset( self::$cached[$nip] ) === true) {
			return self::$cached[$nip];
		}

		if (self::$fp === null) {
			self::init();
		}

		$nip2 = pack('N', ip2long($nip));

		$tmp_offset	= (int) $ipdot[0] * 4;
		$start		= unpack('Vlen',
			self::$index[$tmp_offset].self::$index[$tmp_offset + 1].self::$index[$tmp_offset + 2].self::$index[$tmp_offset + 3]);

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

				break;
			}
		}

		if ($index_offset === null) {
			return 'N/A';
		}

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

		self::$cached[$nip] = explode("\t", fread(self::$fp, $index_length['len']));

		return self::$cached[$nip];
	}

	private static function init(){
		if(self::$fp === null){
			self::$ip = new self();

			self::$fp = fopen(WP_CONTENT_DIR.'/uploads/17monipdb.dat', 'rb');
			if (self::$fp === false) {
				throw new Exception('Invalid 17monipdb.dat file!');
			}

			self::$offset = unpack('Nlen', fread(self::$fp, 4));
			if (self::$offset['len'] < 4) {
				throw new Exception('Invalid 17monipdb.dat file!');
			}

			self::$index = fread(self::$fp, self::$offset['len'] - 4);
		}
	}

	public function __destruct(){
		if(self::$fp !== null){
			fclose(self::$fp);
		}
	}
}