HEX
Server: nginx/1.18.0
System: Linux iZj6c1ieg2jrpk1z5tzi19Z 6.3.9-1.el7.elrepo.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Jun 21 22:18:40 EDT 2023 x86_64
User: www (1001)
PHP: 8.2.4
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.cytocare.cn/wp-content/plugins/wpjam-basic/includes/class-wpjam-args.php
<?php
trait WPJAM_Call_Trait{
	protected static $_closures	= [];

	protected function bind_if_closure($closure){
		return is_closure($closure) ? $closure->bindTo($this, get_called_class()) : $closure;
	}

	public function ob_get($method, ...$args){
		return wpjam_ob_get_contents([$this, $method], ...$args);
	}

	public function try($method, ...$args){
		return wpjam_try([$this, $method], ...$args);
	}

	public function chain($value){
		return new WPJAM_Chainable($this, $value);
	}

	protected static function get_called(){
		return strtolower(get_called_class());
	}

	public static function dynamic_method($action, $method, ...$args){
		if(!$method){
			return;
		}

		$name	= self::get_called();

		if($action == 'add'){
			if(is_closure($args[0])){
				self::$_closures[$name][$method]	= $args[0];
			}
		}elseif($action == 'remove'){
			unset(self::$_closures[$name][$method]);
		}elseif($action == 'get'){
			$closure	= self::$_closures[$name][$method] ?? null;

			return $closure ?: (($parent = get_parent_class($name)) ? $parent::dynamic_method('get', $method) : null);
		}
	}

	public static function add_dynamic_method($method, $closure){
		self::dynamic_method('add', $method, $closure);
	}

	public static function remove_dynamic_method($method){
		self::dynamic_method('remove', $method);
	}

	protected function call_dynamic_method($method, ...$args){
		$closure	= is_closure($method) ? $method : self::dynamic_method('get', $method);

		return $closure ? $this->bind_if_closure($closure)(...$args) : null;
	}
}

trait WPJAM_Items_Trait{
	public function get_items($field=''){
		return $this->{$field ?: '_items'} ?: [];
	}

	public function update_items($items, $field=''){
		$this->{$field ?: '_items'}	= $items;

		return $this;
	}

	public function item_exists($key, $field=''){
		return $this->handle_item('exists', $key, null, $field);
	}

	public function get_item($key, $field=''){
		return $this->handle_item('get', $key, null, $field);
	}

	public function pull_item($key, $field=''){
		try{
			return $this->get_item($key, $field);
		}finally{
			$this->delete_item($key, $field);
		}
	}

	public function get_item_arg($key, $arg, $field=''){
		return ($item = $this->get_item($key, $field)) ? wpjam_get($item, $arg) : null;
	}

	public function has_item($item, $field=''){
		return $this->handle_item('has', null, $item, $field);
	}

	public function add_item($key, ...$args){
		if(!$args || !$this->is_keyable($key)){
			$item	= $key;
			$key	= null;
		}else{
			$item	= array_shift($args);
		}

		$cb		= ($args && is_closure($args[0])) ? array_shift($args) : '';
		$field	= array_shift($args) ?: '';

		return $this->handle_item('add', $key, $item, $field, $cb);
	}

	public function is_keyable($key){
		return is_int($key) || is_string($key) || is_null($key);
	}

	public function remove_item($item, $field=''){
		return $this->handle_item('remove', null, $item, $field);
	}

	public function edit_item($key, $item, $field=''){
		return $this->handle_item('edit', $key, $item, $field);
	}

	public function replace_item($key, $item, $field=''){
		return $this->handle_item('replace', $key, $item, $field);
	}

	public function set_item($key, $item, $field=''){
		return $this->handle_item('set', $key, $item, $field);
	}

	public function delete_item($key, $field=''){
		$result	= $this->handle_item('delete', $key, null, $field);

		if(!is_wp_error($result)){
			$this->after_delete_item($key, $field);
		}

		return $result;
	}

	public function del_item($key, $field=''){
		return $this->delete_item($key, $field);
	}

	public function move_item($orders, $field=''){
		if(wpjam_is_assoc_array($orders)){
			[$orders, $field]	= array_values(wpjam_pull($orders, ['item', '_field']));
		}

		$items	= $this->get_items($field);
		$items	= array_merge(wpjam_pull($items, $orders), $items);

		return $this->update_items($items, $field);
	}

	protected function handle_item($action, $key, $item, $field='', $cb=false){
		$items	= $this->get_items($field);

		if($action == 'get'){
			return $items[$key] ?? null;
		}elseif($action == 'exists'){
			return wpjam_exists($items, $key);
		}elseif($action == 'has'){
			return in_array($item, $items);
		}

		$result	= $this->validate_item($item, $key, $action, $field);

		if(is_wp_error($result)){
			return $result;
		}

		$invalid	= fn($msg)=> new WP_Error('invalid_item_key', $msg);

		if(isset($item)){
			$item	= $this->sanitize_item($item, $key, $action, $field);
			$index	= $cb ? array_find_index($items, $cb) : null;
		}

		if(isset($key)){
			if($this->item_exists($key, $field)){
				if($action == 'add'){
					return $invalid('「'.$key.'」已存在,无法添加');
				}
			}else{
				if(in_array($action, ['edit', 'replace'])){
					return $invalid('「'.$key.'」不存在,无法编辑');
				}elseif($action == 'delete'){
					return $invalid('「'.$key.'」不存在,无法删除');
				}
			}

			if(isset($item)){
				if(is_numeric($index)){
					$items	= wpjam_add_at($items, $index, $key, $item);
				}else{
					$items[$key]	= $item;
				}
			}else{
				unset($items[$key]);
			}
		}else{
			if($action == 'add'){
				if(is_numeric($index)){
					array_splice($items, $index, 0, [$item]);
				}else{
					array_push($items, $item);
				}
			}elseif($action == 'remove'){
				$items	= array_diff($items, [$item]);
			}else{
				return $invalid('key不能为空');
			}
		}

		return $this->update_items($items, $field);
	}

	protected function validate_item($item, $key, $action='', $field=''){
		return true;
	}

	protected function sanitize_item($item, $key, $action='', $field=''){
		return $item;
	}

	protected function after_delete_item($key, $field=''){
		return true;
	}

	public static function item_list_callback($id, $data, $action){
		$i 		= wpjam_get_data_parameter('i');
		$args	= $action == 'del_item' ? [$i] : [$i, $data];
		$args[]	= wpjam_get_data_parameter('_field');

		return wpjam_try([get_called_class(), $action], $id, ...$args);
	}

	public static function item_data_callback($id){
		return wpjam_try([get_called_class(), 'get_item'], $id, ...array_values(wpjam_get_data_parameter(['i', '_field'])));
	}

	public static function get_item_actions(){
		$args	= [
			'callback'		=> [static::class, 'item_list_callback'],
			'data_callback'	=> [static::class, 'item_data_callback'],
			'value_callback'=> fn()=> '',
			'row_action'	=> false,
		];

		return [
			'add_item'	=>['page_title'=>'新增项目',	'title'=>'新增',	'dismiss'=>true]+array_merge($args, ['data_callback'=> fn()=> []]),
			'edit_item'	=>['page_title'=>'修改项目',	'dashicon'=>'edit']+$args,
			'del_item'	=>['page_title'=>'删除项目',	'dashicon'=>'no-alt',	'class'=>'del-icon',	'direct'=>true,	'confirm'=>true]+$args,
			'move_item'	=>['page_title'=>'移动项目',	'dashicon'=>'move',		'class'=>'move-item',	'direct'=>true]+wpjam_except($args, 'callback'),
		];
	}
}

class WPJAM_Args implements ArrayAccess, IteratorAggregate, JsonSerializable{
	use WPJAM_Call_Trait;

	protected $args;

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

	public function __get($key){
		$args	= $this->get_args();

		return wpjam_exists($args, $key) ? $args[$key] : ($key == 'args' ? $args : null);
	}

	public function __set($key, $value){
		$this->filter_args();

		$this->args[$key]	= $value;
	}

	public function __isset($key){
		if(wpjam_exists($this->get_args(), $key)){
			return true;
		}

		return $this->$key !== null;
	}

	public function __unset($key){
		$this->filter_args();

		unset($this->args[$key]);
	}

	#[ReturnTypeWillChange]
	public function offsetGet($key){
		return $this->get_args()[$key] ?? null;
	}

	#[ReturnTypeWillChange]
	public function offsetSet($key, $value){
		$this->filter_args();

		if(is_null($key)){
			$this->args[]		= $value;
		}else{
			$this->args[$key]	= $value;
		}
	}

	#[ReturnTypeWillChange]
	public function offsetExists($key){
		return wpjam_exists($this->get_args(), $key);
	}

	#[ReturnTypeWillChange]
	public function offsetUnset($key){
		$this->filter_args();

		unset($this->args[$key]);
	}

	#[ReturnTypeWillChange]
	public function getIterator(){
		return new ArrayIterator($this->get_args());
	}

	#[ReturnTypeWillChange]
	public function jsonSerialize(){
		return $this->get_args();
	}

	protected function filter_args(){
		if(!$this->args && !is_array($this->args)){
			$this->args = [];
		}

		return $this->args;
	}

	public function get_args(){
		return $this->filter_args();
	}

	public function set_args($args){
		$this->args	= $args;

		return $this;
	}

	public function update_args($args, $replace=true){
		$this->args	= ($replace ? 'array_replace' : 'wp_parse_args')($this->get_args(), $args);

		return $this;
	}

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

	public function update_arg($key, $value=null){
		$this->args	= wpjam_set($this->get_args(), $key, $value);

		return $this;
	}

	public function delete_arg($key){
		$this->args	= wpjam_except($this->get_args(), $key);

		return $this;
	}

	public function pull($key, ...$args){
		$this->filter_args();

		return wpjam_pull($this->args, $key, ...$args);
	}

	public function to_array(){
		return $this->get_args();
	}

	public function sandbox($callback, ...$args){
		try{
			$archive	= $this->get_args();

			return $this->bind_if_closure($callback)(...$args);
		}finally{
			$this->args	= $archive;
		}
	}

	protected function parse_method($name, $type=null){
		if(!$type || $type != 'property'){
			$model	= (!$type || $type == 'model') ? $this->model : $type;

			if($model && method_exists($model, $name)){
				return [$model, $name];
			}
		}

		if(!$type || $type == 'property'){
			if($this->$name && is_callable($this->$name)){
				return $this->bind_if_closure($this->$name);
			}
		}
	}

	public function call_method($method, ...$args){
		$called	= $this->parse_method($method);

		if($called){
			return $called(...$args);
		}

		if(str_starts_with($method, 'filter_')){
			return array_shift($args);
		}
	}

	public function try_method($method, ...$args){
		return wpjam_throw_if_error($this->call_method($method, ...$args));
	}

	protected function call_property($property, ...$args){
		$called	= $this->parse_method($property, 'property');

		return $called ? $called(...$args) : null;
	}

	protected function call_model($method, ...$args){
		$called	= $this->parse_method($method, 'model');

		return $called ? $called(...$args) : null;
	}

	protected function error($code, $msg){
		return new WP_Error($code, $msg);
	}
}

class WPJAM_Register extends WPJAM_Args{
	use WPJAM_Items_Trait;

	protected $name;
	protected $_group;
	protected $_filtered;

	public function __construct($name, $args=[], $group=''){
		$this->name		= $name;
		$this->args		= $args;
		$this->_group	= self::parse_group($group);

		if($this->is_active() || !empty($args['active'])){
			$this->args	= $this->preprocess_args($args);
		}

		$this->args	= array_merge($this->args, ['name'=>$name]);
	}

	protected function preprocess_args($args){
		$group	= $this->_group;
		$config	= $group->get_config('model') ?? true;
		$model	= $config ? wpjam_get($args, 'model') : null;

		if($model || !empty($args['hooks']) || !empty($args['init'])){
			$file	= wpjam_pull($args, 'file');

			if($file && is_file($file)){
				include_once $file;
			}
		}

		if($model && is_subclass_of($model, 'WPJAM_Register')){
			trigger_error('「'.(is_object($model) ? get_class($model) : $model).'」是 WPJAM_Register 子类');
		}

		if($model){
			if($config === 'object'){
				if(!is_object($model)){
					if(class_exists($model, true)){
						$model = $args['model']	= new $model(array_merge($args, ['object'=>$this]));
					}else{
						trigger_error('model 无效');
					}
				}
			}else{
				$group->handle_model('add', $model, $this);
			}

			foreach([
				['hooks', 'add_hooks', true],
				['init', 'init', $group->get_config('init')],
			] as [$key, $method, $default]){
				if(($args[$key] ?? $default) === true){
					$args[$key]	= $this->parse_method($method, $model);
				} 
			}
		}

		wpjam_hooks(wpjam_pull($args, 'hooks'));
		wpjam_init(wpjam_pull($args, 'init'));

		return $args;
	}

	protected function filter_args(){
		if(!$this->_filtered){
			$this->_filtered	= true;

			$class		= self::get_called();
			$filter		= $class == 'wpjam_register' ? 'wpjam_'.$this->_group->name : $class;
			$this->args	= apply_filters($filter.'_args', $this->args, $this->name);
		}

		return $this->args;
	}

	public function get_arg($key, $default=null, $do_callback=true){
		$value	= parent::get_arg($key);

		if(is_null($value)){
			if($this->model && $key && is_string($key) && !str_contains($key, '.')){
				$value	= $this->parse_method('get_'.$key, 'model');
			}
		}elseif(is_callable($value)){
			$value	= $this->bind_if_closure($value);
		}

		if($do_callback && is_callable($value)){
			return $value($this->name);
		}

		return $value ?? $default;
	}

	public function get_parent(){
		return $this->sub_name ? self::get($this->name) : null;
	}

	public function is_sub(){
		return (bool)$this->sub_name;
	}

	public function get_sub($name){
		return $this->get_item($name, 'subs');
	}

	public function get_subs(){
		return $this->get_items('subs');
	}

	public function register_sub($name, $args){
		$sub	= new static($this->name, array_merge($args, ['sub_name'=>$name]));

		$this->add_item($name, $sub, 'subs');

		return self::register($this->name.':'.$name, $sub);
	}

	public function unregister_sub($name){
		$this->delete_item($name, 'subs');

		return self::unregister($this->name.':'.$name);
	}

	public function is_active(){
		return true;
	}

	public static function validate_name($name){
		$prefix	= self::class.'的注册 name';

		if(empty($name)){
			trigger_error($prefix.' 为空');
			return;
		}elseif(is_numeric($name)){
			trigger_error($prefix.'「'.$name.'」'.'为纯数字');
			return;
		}elseif(!is_string($name)){
			trigger_error($prefix.'「'.var_export($name, true).'」不为字符串');
			return;
		}

		return true;
	}

	protected static function get_defaults(){
		return [];
	}

	protected static function parse_group($name=''){
		$called	= get_called_class();
		$group	= WPJAM_Register_Group::get_instance(strtolower($name ?: $called));

		if(!$group->called){
			$group->called		= $called;
			$group->defaults	= $called::get_defaults();

			if($route = $group->get_config('route')){
				wpjam_register_route($route, ['model'=>$called]);
			}
		}

		return $group;
	}

	public static function call_group($method, ...$args){
		[$method, $group]	= str_contains($method, '_by_') ? explode('_by_', $method) : [$method, ''];

		return [self::parse_group($group), $method](...$args);
	}

	public static function register($name, $args=[]){
		return self::call_group('add_object', $name, $args);
	}

	public static function re_register($name, $args, $merge=true){
		self::unregister($name);

		return self::register($name, $args);
	}

	public static function unregister($name){
		self::call_group('remove_object', $name);
	}

	public static function get_registereds($args=[], $output='objects', $operator='and'){
		$objects	= self::call_group('get_objects', $args, $operator);

		return $output == 'names' ? array_keys($objects) : $objects;
	}

	public static function get_by(...$args){
		$args	= $args ? (is_array($args[0]) ? $args[0] : [$args[0]=> $args[1]]) : [];

		return self::get_registereds($args);
	}

	public static function get($name, $by='', $top=''){
		return self::call_group('get_object', $name, $by, $top);
	}

	public static function exists($name){
		return (bool)self::get($name);
	}

	public static function get_setting_fields($args=[]){
		return self::call_group('get_fields', $args);
	}

	public static function get_active($key=null){
		return self::call_group('get_active', $key);
	}

	public static function call_active($method, ...$args){
		return self::call_group('call_active', $method, ...$args);
	}

	public static function by_active(...$args){
		$name	= current_filter();
		$method = (did_action($name) ? 'on_' : 'filter_').substr($name, str_starts_with($name, 'wpjam_') ? 6 : 0);

		return self::call_active($method, ...$args);
	}
}

class WPJAM_Register_Group extends WPJAM_Args{
	use WPJAM_Items_Trait;

	public function get_objects($args=[], $operator='AND'){
		if($defaults = $this->pull('defaults')){
			wpjam_map($defaults, [$this, 'add_object']);
		}

		return $args ? wpjam_filter($this->get_items(), $args, $operator) : $this->get_items();
	}

	public function get_object($name, $by='', $top=''){
		if(!$name){
			return;
		}

		if($by == 'model'){
			if($name && strcasecmp($name, $top) !== 0){
				return $this->handle_model('get', $name) ?: $this->get_object(get_parent_class($name), $by, $top);
			}
		}else{
			$object	= $this->get_item($name);

			if(!$object && $this->defaults){
				$args	= $this->pull_item($name, 'defaults');
				$object	= isset($args) ? $this->add_object($name, $args) : null;
			}

			return $object;
		}
	}

	public function add_object($name, $object){
		$class	= $this->called;
		$count	= count($this->get_items());

		if(is_object($name)){
			$object	= $name;
			$name	= $object->name ?? null;
		}elseif(is_array($name)){
			[$object, $name]	= [$name, $object];

			$name	= wpjam_pull($object, 'name') ?: ($name ?: '__'.$count);
		}

		if(!$class::validate_name($name)){
			return;
		}

		if($this->get_item($name)){
			trigger_error($this->name.'「'.$name.'」已经注册。');
		}

		if(is_array($object)){
			if(!empty($object['admin']) && !is_admin()){
				return;
			}

			$object	= new $class($name, $object);
		}

		if($orderby = $this->get_config('orderby')){
			$by		= $orderby === true ? 'order' : $orderby;
			$order	= $this->get_config('order') ?? 'DESC';
			$score	= wpjam_get($object, $by, 10);
			$comp	= ($order == 'DESC' ? '>' : '<');
			$args[]	= fn($v)=> wpjam_compare($score, $comp, wpjam_get($v, $by, 10));
		}else{
			$args	= [];
		}

		$this->add_item($name, $object, ...$args);

		if(method_exists($object, 'registered')){
			$object->registered($count+1);
		}

		return $object;
	}

	public function remove_object($name){
		$object	= $this->get_item($name);

		if($object){
			$this->handle_model('delete', $object->model);
			$this->delete_item($name);
		}
	}

	public function handle_model($action, $model, $object=null){
		$model	= ($model && is_string($model)) ? strtolower($model) : null;

		if($model){
			return $this->handle_item($action, $model, $object, 'models');
		}
	}

	public function get_config($key){
		if($this->called == 'WPJAM_Register'){
			return;
		}

		if(is_null($this->config)){
			$ref	= new ReflectionClass($this->called);

			if(method_exists($ref, 'getAttributes')){
				$args	= $ref->getAttributes('config');
				$args	= $args ? $args[0]->getArguments() : [];
				$args	= $args ? (is_array($args[0]) ? $args[0] : $args) : [];
			}else{
				$args	= preg_match_all('/@config\s+([^\r\n]*)/', $ref->getDocComment(), $matches) ? wp_parse_list($matches[1][0]) : [];
			}

			$this->config = wpjam_array($args, fn($k, $v)=> is_numeric($k) ? (str_contains($v, '=') ? explode('=', $v) : [$v, true]) : [$k, $v]);
		}

		return $this->config[$key] ?? null;
	}

	public function get_active($key=null){
		$objects	= array_filter($this->get_objects(), fn($object)=> $object->active ?? $object->is_active());

		return $key ? array_filter(array_map(fn($object)=> $object->get_arg($key), $objects), fn($v)=> !is_null($v)) : $objects;
	}

	public function call_active($method, ...$args){
		$type	= array_find(['filter', 'get'], fn($type)=> str_starts_with($method, $type.'_'));

		foreach($this->get_active() as $object){
			$result	= $object->call_method($method, ...$args);	// 不能调用对象本身的方法,会死循环

			if(is_wp_error($result)){
				return $result;
			}

			if($type == 'filter'){
				$args[0]	= $result;
			}elseif($type == 'get'){
				if($result && is_array($result)){
					$return	= array_merge(($return ?? []), $result);
				}
			}
		}

		if($type == 'filter'){
			return $args[0];
		}elseif($type == 'get'){
			return $return ?? [];
		}
	}

	public function get_fields($args=[]){
		$title_field	= wpjam_pull($args, 'title_field') ?: 'title';
		$name_field		= wpjam_pull($args, 'name_field') ?: 'name';
		$objects		= $this->get_objects(wpjam_pull($args, 'filter_args'));

		if(wpjam_get($args, 'type') == 'select'){
			return [wpjam_pull($args, 'name')=> $args+[
				'show_option_none'	=> __('&mdash; Select &mdash;'),
				'options'			=> wpjam_array($objects, fn($k, $v)=> [$v->$name_field, array_filter([
					'title'			=> $v->$title_field,
					'description'	=> $v->description,
					'fields'		=> $v->get_arg('fields')
				])])
			]];
		}

		return wpjam_array($objects, fn($k, $v)=> isset($v->active) ? null : [$v->$name_field, ($v->field ?: [])+['label'=>$v->$title_field]]);
	}

	protected static $_groups	= [];

	public static function __callStatic($method, $args){
		foreach(self::$_groups as $group){
			if($method == 'register_json'){
				if($group->get_config($method)){
					$group->call_active($method, $args[0]);
				}
			}elseif(in_array($method, ['add_menu_page', 'add_admin_load'])){
				$key	= substr($method, 4);

				if($group->get_config($key)){
					array_map('wpjam_'.$method, $group->get_active($key));
				}
			}
		}
	}

	public static function get_instance($name){
		if(!self::$_groups){
			add_action('wpjam_api',	[self::class, 'register_json']);

			if(is_admin()){
				add_action('wpjam_admin_init',	[self::class, 'add_menu_page']);
				add_action('wpjam_admin_init',	[self::class, 'add_admin_load']);
			}
		}

		return self::$_groups[$name]	??= new self(['name'=>$name]);
	}
}

class WPJAM_AJAX extends WPJAM_Register{
	public function registered($count){
		$this->add_action();

		if($this->nopriv){
			$this->add_action('nopriv_');
		}

		if($count == 1 && !is_admin()){
			wpjam_script('wpjam-ajax', [
				'for'		=> 'wp, login',
				'src'		=> wpjam_url(dirname(__DIR__).'/static/ajax.js'),
				'deps'		=> ['jquery'],
				'data'		=> 'var ajaxurl	= "'.admin_url('admin-ajax.php').'";',
				'position'	=> 'before',
				'priority'	=> 1
			]);

			if(!is_login()){
				add_filter('script_loader_tag', fn($tag, $handle)=> $handle == 'wpjam-ajax' && current_theme_supports('script', $handle) ? '' : $tag, 10, 2);
			}
		}
	}

	public function add_action($part=''){
		add_action('wp_ajax_'.$part.$this->name, [$this, 'callback']);
	}

	public function callback(){
		add_filter('wp_die_ajax_handler', fn()=> ['WPJAM_Error', 'wp_die_handler']);

		if(!$this->callback || !is_callable($this->callback)){
			wp_die('invalid_callback');
		}

		$data	= wpjam_get_data_parameter();
		$data	= array_merge($data, wpjam_except(wpjam_get_post_parameter(), ['action', 'defaults', 'data', '_ajax_nonce']));
		$result	= wpjam_catch([wpjam_fields($this->fields), 'validate'], $data, 'parameter');

		if(is_wp_error($result)){
			wpjam_send_json($result);
		}

		$data	= array_merge($data, $result);

		if($this->verify !== false && !check_ajax_referer($this->get_nonce_action($data), false, false)){
			wp_die('invalid_nonce');
		}

		$result	= wpjam_catch($this->callback, $data, $this->name);
		$result	= $result === true ? [] : $result;

		wpjam_send_json($result);
	}

	public function get_attr($data=[], $return=null){
		$attr	= ['action'=>$this->name, 'data'=>$data];
		$attr	= array_merge($attr, $this->verify !== false ? ['nonce'=> wp_create_nonce($this->get_nonce_action($data))] : []);

		return $return ? $attr : wpjam_attr($attr, 'data');
	}

	protected function get_nonce_action($data){
		$data	= array_filter(wp_array_slice_assoc($data, ($this->nonce_keys ?: [])));

		return $this->name.($data ? ':'.implode(':', $data) : '');
	}
}

class WPJAM_Verify_TXT extends WPJAM_Register{
	public function get_fields(){
		return [
			'name'	=>['title'=>'文件名称',	'type'=>'text',	'required', 'value'=>$this->get_data('name'),	'class'=>'all-options'],
			'value'	=>['title'=>'文件内容',	'type'=>'text',	'required', 'value'=>$this->get_data('value')]
		];
	}

	public function get_data($key=''){
		$data	= wpjam_get_setting('wpjam_verify_txts', $this->name) ?: [];

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

	public function set_data($data){
		return wpjam_update_setting('wpjam_verify_txts', $this->name, $data) || true;
	}

	public static function __callStatic($method, $args){	// 放弃
		$name	= $args[0];

		if($object = self::get($name)){
			if(in_array($method, ['get_name', 'get_value'])){
				return $object->get_data(str_replace('get_', '', $method));
			}elseif($method == 'set' || $method == 'set_value'){
				return $object->set_data(['name'=>$args[1], 'value'=>$args[2]]);
			}
		}
	}

	public static function filter_root_rewrite_rules($root_rewrite){
		if(empty($GLOBALS['wp_rewrite']->root)){
			$home_path	= parse_url(home_url());

			if(empty($home_path['path']) || '/' == $home_path['path']){
				$root_rewrite	= array_merge(['([^/]+)\.txt?$'=>'index.php?module=txt&action=$matches[1]'], $root_rewrite);
			}
		}

		return $root_rewrite;
	}

	public static function get_rewrite_rule(){
		add_filter('root_rewrite_rules',	[self::class, 'filter_root_rewrite_rules']);
	}

	public static function redirect($action){
		$txts	= wpjam_get_option('wpjam_verify_txts');
		$txt	= $txts ? array_find($txts, fn($v)=> $v['name'] == str_replace('.txt', '', $action).'.txt') : '';

		if($txt){
			header('Content-Type: text/plain');
			echo $txt['value'];

			exit;
		}
	}
}

class WPJAM_Data_Processor extends WPJAM_Args{
	private $data	= [];

	public function get_fields($type=''){
		if($type){
			return $this->$type ? array_intersect_key($this->fields, $this->$type) : [];
		}

		return $this->fields;
	}

	public function validate(){
		$error	= array_find($this->formulas, fn($v)=> is_wp_error($v));

		return $error ?: true;
	}

	public function process($items, $args=[]){
		$sums		= empty($args['sum']) ? [] : array_fill_keys($this->sum_fields, 0);
		$calc		= $args['calc'] ?? true;
		$orderby	= $args['orderby'] ?? '';

		foreach($items as &$item){
			$item	= $calc ? $this->calc($item) : $item;

			if($orderby){
				$item[$orderby]	??= 0;
			}

			foreach($sums as $k => &$v){
				if(isset($item[$k]) && is_numeric($item[$k])){
					$v	+= $item[$k];
				}
			}
		}

		if($orderby){
			$items	= wpjam_sort($items, [$orderby => $args['order'] ?? '']);
		}

		if($sums){
			$items	= wpjam_add_at($items, 0, null, $this->calc($sums, true)+(is_array($args['sum']) ? $args['sum'] : []));
		}

		return $items;
	}

	public function calc($item, $sum=false){
		$formulas	= $sum ? $this->sum_formulas : $this->formulas;

		if(!$item || !is_array($item) || !$formulas){
			return $item;
		}
 
		$item	= wpjam_except($item, array_keys($formulas));

		foreach($formulas as $key => $formula){
			if(!is_array($formula)){
				$item[$key]	= is_wp_error($formula) ? '!公式错误' : $formula;
			}elseif(!isset($item[$key])){
				$item[$key]	= $this->do_calc($item, $key);
			}
		}

		return $item;
	}

	public function sum($items, $calc=false){
		if(($this->sumable || $calc) && $items){
			return $this->calc($this->process($items, ['sum'=>true, 'calc'=>$calc])[0], true);
		}
	}

	public function accumulate($item=null, $group=''){
		if($item){
			$item	= $this->calc($item);
			$value	= $group ? ($item[$group] ?? '') : '__';

			$this->data[$group][$value]	??= array_merge($item, array_fill_keys($this->sum_fields, 0));

			foreach($this->sum_fields as $k){
				if(isset($item[$k]) && is_numeric($item[$k])){
					$this->data[$group][$value][$k]	+= $item[$k];
				}
			}

			return $this->data[$group];
		}

		$items	= wpjam_pull($this->data, $group) ?: [];
		$items	= array_map(fn($item)=> $this->calc($item, true), $items);

		return $group ? $items : ($items['__'] ?? []);
	}

	protected function do_calc(&$item, $key){
		$if_error	= $this->if_errors[$key] ?? null;
		$formula	= $this->formulas[$key];

		if(is_array($formula[0])){
			$f	= array_find($formula, fn($f)=> wpjam_if($item, $f));

			if(!$f){
				return '';
			}

			$formula	= $f['formula'];
		}

		foreach($formula as &$t){
			if(str_starts_with($t, '$')){
				$k	= substr($t, 1);

				if(!isset($item[$k]) && isset($this->formulas[$k])){
					$item[$k]	= $this->do_calc($item, $k);
				}

				if(isset($item[$k]) && is_numeric(trim($item[$k]))){
					$t	= $item[$k];
					$t	= (is_numeric($t) && $t < 0) ? '('.$t.')' : $t;
				}else{
					$t	= $this->if_errors[$k] ?? null;

					if(!isset($t)){
						return $if_error ?? '!无法计算';
					}
				}

				if(isset($p) && $p == '/' && !(float)$t){
					return $if_error ?? '!除零错误';
				}
			}

			$p	= $t;
		}

		return eval('return '.implode('', $formula).';');
	}

	protected static function parse_formula($formula, $vars=[], $title=''){
		if(is_array($formula)){
			return array_map(fn($f)=> array_merge($f, ['formula'=>self::parse_formula($f['formula'], $vars, $title)]), $formula);
		}

		$raw		= $formula;
		$formula	= preg_replace('@\s@', '', $formula);
		$signs		= ['+', '-', '*', '/', '(', ')', ',', '\'', '.', '%'];
		$pattern	= '/([\\'.implode('\\', $signs).'])/';
		$formula	= preg_split($pattern, $formula, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
		$methods	= ['abs', 'ceil', 'pow', 'sqrt', 'pi', 'max', 'min', 'fmod', 'round'];

		foreach($formula as $token){
			if(!is_numeric($token) && !str_starts_with($token, '$') && !in_array($token, $signs) && !in_array(strtolower($token), $methods)){
				return new WP_Error('invalid_formula', $title.'公式「'.$raw.'」错误,无效的「'.$token.'」');
			}

			if(str_starts_with($token, '$') && !in_array(substr($token, 1), $vars)){
				return new WP_Error('invalid_formula', $title.'公式「'.$raw.'」错误,「'.$token.'」未定义');
			}
		}

		return $formula;
	}

	public static function create($fields, $by='fields'){
		$args	= ['fields'=>$fields];
		$keys	= array_keys($fields);

		foreach($fields as $key => $field){
			if(!empty($field['sumable'])){
				$args['sumable'][$key]	= $field['sumable'];
			}

			if(!empty($field['formula'])){
				$args['formulas'][$key]	= self::parse_formula($field['formula'], $keys, '字段'.wpjam_get($field, 'title').'「'.$key.'」');
			}

			if(isset($field['if_error']) && ($field['if_error'] || is_numeric($field['if_error']))){
				$args['if_errors'][$key]	= $field['if_error'];
			}
		}

		if(!empty($args['sumable'])){
			if(!empty($args['formulas'])){
				$args['sum_formulas']	= array_intersect_key($args['formulas'], array_filter($args['sumable'], fn($v)=> $v == 2));
			}

			$args['sum_fields']	= array_keys(array_filter($args['sumable'], fn($v)=> $v == 1));
		}

		return new self($args);
	}
}

class WPJAM_Updater extends WPJAM_Args{
	public function get_data($file){	// https://api.wordpress.org/plugins/update-check/1.1/
		$type		= $this->type;
		$plural		= $type.'s';
		$response	= wpjam_transient('update_'.$plural.':'.$this->hostname, fn()=> wpjam_remote_request($this->url), MINUTE_IN_SECONDS);

		if(is_wp_error($response)){
			return false;
		}

		$response	= $response['template']['table'] ?? $response[$plural];

		if(isset($response['fields']) && isset($response['content'])){
			$fields	= array_column($response['fields'], 'index', 'title');
			$label	= $type == 'plugin' ? '插件' : '主题';
			$item	= array_find($response['content'], fn($item)=> $item['i'.$fields[$label]] == $file);
			$data	= $item ? array_map(fn($index)=> $item['i'.$index] ?? '', $fields) : [];

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

		return $response[$file] ?? [];
	}

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

		return $new_data ? $new_data+['id'=>$data['UpdateURI'], 'version'=>$data['Version']] : $update;
	}

	public function filter_pre_set_site_transient($updates){
		if(isset($updates->no_update) || isset($updates->response)){
			$file	= 'wpjam-basic/wpjam-basic.php';
			$update	= $this->get_data($file);

			if($update){
				$plugin	= get_plugin_data(WP_PLUGIN_DIR.'/'.$file);
				$key 	= version_compare($update['new_version'], $plugin['Version'], '>') ? 'response' : 'no_update';

				$updates->$key[$file]	= (object)(isset($updates->$key[$file]) ? array_merge((array)$updates->$key[$file], $update) : $update);
			}
		}

		return $updates;
	}

	public static function create($type, $hostname, $url){
		if(in_array($type, ['plugin', 'theme'])){
			$object	= new self([
				'type'		=> $type,
				'hostname'	=> $hostname,
				'url'		=> $url
			]);

			add_filter('update_'.$type.'s_'.$hostname, [$object, 'filter_update'], 10, 4);

			if($type == 'plugin' && $hostname == 'blog.wpjam.com'){
				add_filter('pre_set_site_transient_update_plugins', [$object, 'filter_pre_set_site_transient']);
			}
		}
	}
}

class WPJAM_Cache extends WPJAM_Args{
	public function __call($method, $args){
		$method	= substr($method, str_starts_with($method, 'cache_') ? 6 : 0);
		$gnd	= str_contains($method, 'get') || str_contains($method, 'delete');
		$cb		= $method == 'cas' ? [array_shift($args)] : [];
		$key	= array_shift($args);

		if(str_contains($method, '_multiple')){
			$cb[]	= $gnd ? array_map([$this, 'key'], $key) : wpjam_array($key, fn($k)=> $this->key($k));
		}else{
			$cb[]	= $this->key($key);

			if(!$gnd){
				$cb[]	= array_shift($args);
			}
		}

		$cb[]	= $this->group;

		if(!$gnd){
			$cb[]	= (int)(array_shift($args)) ?: ($this->time ?: DAY_IN_SECONDS);
		}else{
			if($args){
				$cb[]	= array_shift($args);
			}
		}

		$callback	= 'wp_cache_'.$method;
		$result		= $callback(...$cb);

		if($result && $method == 'get_multiple'){
			$result	= wpjam_array($key, fn($i, $k) => [$k, $result[$cb[0][$i]]]);
			$result	= array_filter($result, fn($v) => $v !== false);
		}

		return $result;
	}

	protected function key($key){
		return wpjam_join(':', $this->prefix, $key);
	}

	public function get_with_cas($key, &$token, ...$args){
		[$object, $token]	= is_object($token) ? [$token, null] : [null, $token];

		$key	= $this->key($key);
		$result	= wp_cache_get_with_cas($key, $this->group, $token);

		if($result === false && $args){
			$this->set($key, $args[0]);

			$result	= wp_cache_get_with_cas($key, $this->group, $token);
		}

		if($object){
			$object->cas_token	= $token;
		}

		return $result;
	}

	public function is_over($key, $max, $time){
		$times	= $this->get($key) ?: 0;

		if($times > $max){
			return true;
		}

		$this->set($key, $times+1, ($max == $times && $time > 60) ? $time : 60);

		return false;
	}

	public function generate($key){
		try{
			$this->failed_times($key);
			$this->interval($key);

			$code = rand(100000, 999999);

			$this->set($key.':code', $code, $this->cache_time);

			return $code;
		}catch(Exception $e){
			return wpjam_catch($e);
		}
	}

	public function verify($key, $code){
		try{
			$this->failed_times($key);

			if(!$code || (int)$code !== (int)$this->get($key.':code')){
				$this->failed_times($key, true);
			}

			return true;
		}catch(Exception $e){
			return wpjam_catch($e);
		}
	}

	protected function failed_times($key, $increment=false){
		if($this->failed_times){
			$times	= (int)$this->get($key.':failed_times');

			if($increment){
				$this->set($key.':failed_times', $times+1, $this->cache_time/2);

				wpjam_throw('invalid_code');
			}else{
				if($times > $this->failed_times){
					wpjam_throw('failed_times_exceeded', ['尝试的失败次数', '请15分钟后重试。']);
				}
			}
		}
	}

	protected function interval($key){
		if($this->interval){
			if($this->get($key.':time')){
				wpjam_throw('error', '验证码'.$this->interval.'分钟前已发送了。');
			}

			$this->set($key.':time', time(), $this->interval*60);
		}
	}

	public static function parse_group($group){
		return is_bool($group) ? ($group ? 'site-' : '').'transient' : $group;
	}

	public static function get_verification($args){
		[$name, $args]	= is_array($args) ? [wpjam_pull($args, 'group'), $args] : [$args, []];

		return self::get_instance([
			'group'		=> 'verification_code',
			'prefix'	=> $name ?: 'default',
			'global'	=> true,
		]+$args+[
			'failed_times'	=> 5,
			'interval'		=> 1,
			'cache_time'	=> MINUTE_IN_SECONDS*30
		]);
	}

	public static function get_instance($group, $args=[]){
		$args	= is_array($group) ? $group : ['group'=>$group]+$args;

		if(!empty($args['group'])){
			$name	= wpjam_join(':', $args['group'], ($args['prefix'] ?? ''));

			return wpjam_get_instance('cache', $name, fn()=> self::create($args));
		}
	}

	public static function create($args=[]){
		if(is_object($args)){
			if(!$args->cache_object){
				$group	= $args->cache_group;

				if($group){
					$group	= is_array($group) ? array_combine(['group', 'global'], $group) : ['group'=>$group];

					$args->cache_object	= self::create($group+['prefix'=>$args->cache_prefix, 'time'=>$args->cache_time]);
				}
			}

			return $args->cache_object;
		}else{
			if(!empty($args['group'])){
				if(wpjam_pull($args, 'global')){
					wp_cache_add_global_groups($args['group']);
				}

				return new self($args);
			}
		}
	}
}