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-user.php
<?php
class WPJAM_User{
	use WPJAM_Instance_Trait;

	protected $id;

	protected function __construct($id){
		$this->id	= (int)$id;
	}

	public function __get($key){
		if(in_array($key, ['id', 'user_id'])){
			return $this->id;
		}elseif(in_array($key, ['user', 'data'])){
			return get_userdata($this->id);
		}elseif($key == 'role'){
			return reset($this->data->roles);
		}else{
			return $this->user->$key ?? $this->meta_get($key);
		}
	}

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

	public function value_callback($field){
		if($field == 'role'){
			return reset($this->user->roles);
		}

		return $this->user->$field ?? $this->meta_get($field);
	}

	public function parse_for_json($size=96){
		return apply_filters('wpjam_user_json', [
			'id'			=> $this->id,
			'nickname'		=> $this->nickname,
			'name'			=> $this->display_name,
			'display_name'	=> $this->display_name,
			'avatar'		=> get_avatar_url($this->user, $size),
		], $this->id);
	}

	public function meta_get($key){
		return wpjam_get_metadata('user', $this->id, $key, null);
	}

	public function meta_exists($key){
		return metadata_exists('user', $this->id, $key);
	}

	public function meta_input(...$args){
		if($args){
			return wpjam_update_metadata('user', $this->id, ...$args);
		}
	}

	public function update_avatarurl($avatarurl){
		if($this->avatarurl != $avatarurl){
			$this->meta_input('avatarurl', $avatarurl);
		}

		return true;
	}

	public function update_nickname($nickname){
		if($this->nickname != $nickname){
			self::update($this->id, ['nickname'=>$nickname, 'display_name'=>$nickname]);
		}

		return true;
	}

	public function add_role($role, $blog_id=0){
		return wpjam_call_for_blog($blog_id, function($role){
			$wp_error	= false;

			if($this->roles){
				if(!in_array($role, $this->roles)){
					$wp_error	= new WP_Error('error', '你已有权限,如果需要更改权限,请联系管理员直接修改。');
				}
			}else{
				$this->user->add_role($role);
			}

			return $wp_error ?: $this->user;
		}, $role);
	}

	public function login(){
		wp_set_auth_cookie($this->id, true, is_ssl());
		wp_set_current_user($this->id);
		do_action('wp_login', $this->user_login, $this->user);
	}

	public function get_openid($name, $appid=''){
		return self::get_signup($name, $appid)->get_openid($this->id);
	}

	public function update_openid($name, $appid, $openid){
		return self::get_signup($name, $appid)->update_openid($this->id, $openid);
	}

	public function delete_openid($name, $appid=''){
		return self::get_signup($name, $appid)->delete_openid($this->id);
	}

	public function bind($name, $appid, $openid){
		return self::get_signup($name, $appid)->bind($openid, $this->id);
	}

	public function unbind($name, $appid=''){
		return self::get_signup($name, $appid)->unbind($this->id);
	}

	public static function get_instance($id, $wp_error=false){
		$user	= self::validate($id);

		if(is_wp_error($user)){
			return $wp_error ? $user : null;
		}

		return self::instance($user->ID, fn($id)=> new self($id));
	}

	public static function validate($user_id){
		$user	= $user_id ? self::get_user($user_id) : null;

		if(!$user || !($user instanceof WP_User)){
			return new WP_Error('invalid_user');
		}

		return $user;
	}

	public static function update_caches($user_ids){
		$user_ids	= array_filter(wp_parse_id_list($user_ids));

		if(!$user_ids){
			return [];
		}

		cache_users($user_ids);

		return array_map('get_userdata', $user_ids);
	}

	public static function get_by_ids($user_ids){
		return self::update_caches($user_ids);
	}

	public static function get_user($user){
		if($user && is_numeric($user)){	// 不存在情况下的缓存优化
			$user_id	= $user;
			$found		= false;
			$cache		= wp_cache_get($user_id, 'users', false, $found);

			if($found){
				return $cache ? get_userdata($user_id) : $cache;
			}else{
				$user	= get_userdata($user_id);

				if(!$user){	// 防止重复 SQL 查询。
					wp_cache_add($user_id, false, 'users', 10);
				}
			}
		}

		return $user;
	}

	public static function get_authors($args=[]){
		return get_users(array_merge($args, ['capability'=>'edit_posts']));
	}

	public static function get_path($args, $item=[]){
		$id	= is_array($args) ? (int)wpjam_pull($args, 'author') : $args;

		if(!$id){
			return new WP_Error('invalid_author', ['作者']);
		}

		return $item['platform'] == 'template' ? get_author_posts_url($id) : str_replace('%author%', $id, $item['path']);
	}

	public static function get_path_fields(){
		return ['author' => ['type'=>'select', 'options'=>fn()=> wp_list_pluck(self::get_authors(), 'display_name', 'ID')]];
	}

	public static function options_callback($field){
		return wp_list_pluck(self::get_authors(), 'display_name', 'ID');
	}

	public static function get($id){
		$user	= get_userdata($id);

		return $user ? $user->to_array() : [];
	}

	public static function insert($data){
		return wp_insert_user(wp_slash($data));
	}

	public static function update($user_id, $data){
		$data['ID'] = $user_id;

		return wp_update_user(wp_slash($data));
	}

	public static function create($args){
		return wpjam_call_for_blog(array_get($args, 'blog_id'), function($args){
			$args	+= [
				'user_pass'		=> wp_generate_password(12, false),
				'user_login'	=> '',
				'user_email'	=> '',
				'nickname'		=> '',
				// 'avatarurl'		=> '',
			];

			if(!wpjam_pull($args, 'users_can_register', get_option('users_can_register'))){
				return new WP_Error('registration_closed', '用户注册关闭,请联系管理员手动添加!');
			}

			if(empty($args['user_email'])){
				return new WP_Error('empty_user_email', '用户的邮箱不能为空。');
			}

			$args['user_login']	= preg_replace('/\s+/', '', sanitize_user($args['user_login'], true));

			if($args['user_login']){
				$lock_key	= $args['user_login'].'_register_lock';
				$result		= wp_cache_add($lock_key, true, 'users', 5);

				if($result === false){
					return new WP_Error('error', '该用户名正在注册中,请稍后再试!');
				}
			}

			$data	= wpjam_slice($args, ['user_login', 'user_pass', 'user_email', 'role']);

			if($args['nickname']){
				$data['nickname']	= $data['display_name']	= $args['nickname'];
			}

			$meta_input	= wpjam_pull($data, 'meta_input');
			$user_id	= self::insert($data);

			wp_cache_delete($lock_key, 'users');

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

			if($meta_input){
				wpjam_update_metadata('user', $user_id, $meta);
			}

			return self::get_instance($user_id);
		}, $args);
	}

	public static function query_items($args){
		if(wpjam_pull($args, 'data_type')){
			return get_users(array_merge($args, ['search'=> !empty($args['search']) ? '*'.$args['search'].'*' : '']));
		}
	}

	public static function filter_fields($fields, $id){
		if($id && !is_array($id)){
			$object	= self::get_instance($id);
			$fields	= array_merge(['name'=>['title'=>'用户', 'type'=>'view', 'value'=>$object->display_name]], $fields);
		}

		return $fields;
	}

	public static function signup($name, $appid, $openid, $args){
		return self::get_signup($name, $appid)->signup($openid);
	}

	protected static function get_signup($name, $appid=''){
		trigger_error('get_signup');	// delete 2024-06-30
		return wpjam_get_user_signup_object($name, $appid);
	}

	public static function get_meta($user_id, ...$args){
		_deprecated_function(__METHOD__, 'WPJAM Basic 6.0', 'wpjam_get_metadata');		// delete 2024-06-30
		return wpjam_get_metadata('user', $user_id, ...$args);
	}

	public static function update_meta($user_id, ...$args){
		_deprecated_function(__METHOD__, 'WPJAM Basic 6.0', 'wpjam_update_metadata');		// delete 2024-06-30
		return wpjam_update_metadata('user', $user_id, ...$args);
	}

	public static function update_metas($user_id, $data, $meta_keys=[]){
		_deprecated_function(__METHOD__, 'WPJAM Basic 6.0', 'wpjam_update_metadata');		// delete 2024-06-30
		return wpjam_update_metadata('user', $user_id, $data, $meta_keys);
	}
}

class WPJAM_Bind extends WPJAM_Register{
	public function __construct($type, $appid, $args=[]){
		parent::__construct($type.':'.$appid, array_merge($args, [
			'type'		=> $type,
			'appid'		=> $appid,
			'bind_key'	=> wpjam_join('_', [$type, $appid])
		]));
	}

	public function get_appid(){
		return $this->appid;
	}

	public function get_domain(){
		return $this->domain ?: $this->appid.'.'.$this->type;
	}

	protected function get_object($meta_type, $object_id){
		$callback	= 'wpjam_get_'.$meta_type.'_object';

		if(is_callable($callback)){
			return $callback($object_id);
		}
	}

	public function get_openid($meta_type, $object_id){
		return get_metadata($meta_type, $object_id, $this->bind_key, true);
	}

	public function update_openid($meta_type, $object_id, $openid){
		return update_metadata($meta_type, $object_id, $this->bind_key, $openid);
	}

	public function delete_openid($meta_type, $object_id){
		return delete_metadata($meta_type, $object_id, $this->bind_key);
	}

	public function bind_openid($meta_type, $object_id, $openid){
		wpjam_register_error_setting('is_bound', '已绑定其他账号,请先解绑再试!');

		$current	= $this->get_openid($meta_type, $object_id);

		if($current && $current != $openid){
			return new WP_Error('is_bound');
		}

		$exists	= $this->get_by_openid($meta_type, $openid);

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

		if($exists && $exists->id != $object_id){
			return new WP_Error('is_bound');
		}

		$this->update_value($openid, $meta_type.'_id', $object_id);

		return $current ? true : $this->update_openid($meta_type, $object_id, $openid);
	}

	public function unbind_openid($meta_type, $object_id){
		$openid	= $this->get_openid($meta_type, $object_id);
		$openid	= $openid ?: $this->get_openid_by($meta_type.'_id', $object_id);

		if($openid){
			$this->delete_openid($meta_type, $object_id);
			$this->update_value($openid, $meta_type.'_id', 0);
		}

		return $openid;
	}

	public function get_by_openid($meta_type, $openid){
		if(!$this->get_user($openid)){
			return new WP_Error('invalid_openid');
		}

		$object_id	= $this->get_value($openid, $meta_type.'_id');
		$object		= $this->get_object($meta_type, $object_id);

		if(!$object){
			$meta_data	= wpjam_get_by_meta($meta_type, $this->bind_key, $openid);

			if($meta_data){
				$object_id	= current($meta_data)[$meta_type.'_id'];
				$object		= $this->get_object($meta_type, $object_id);
			}
		}

		if(!$object && $meta_type == 'user'){
			$user_id	= username_exists($openid);
			$object		= $user_id ? wpjam_get_user_object($user_id) : null;
		}

		return $object;
	}

	public function bind_by_openid($meta_type, $openid, $object_id){
		return $this->bind_openid($meta_type, $object_id, $openid);
	}

	public function unbind_by_openid($meta_type, $openid){
		$object_id	= $this->get_value($openid, $meta_type.'_id');

		if($object_id){
			$this->delete_openid($meta_type, $object_id);
			$this->update_value($openid, $meta_type.'_id', 0);
		}
	}

	public function get_by_user_email($meta_type, $email){
		if($email && str_ends_with($email, '@'.$this->get_domain())){
			$openid	= substr($email, 0, 0-strlen('@'.$this->get_domain()));

			return $this->get_value($openid, $meta_type.'_id');
		}
	}

	protected function get_value($openid, $key){
		$user	= $this->get_user($openid);

		if($user && !is_wp_error($user)){
			return $user[$key] ?? null;
		}
	}

	protected function update_value($openid, $key, $value){
		$prev	= $this->get_value($openid, $key);

		return ($prev != $value) ? $this->update_user($openid, [$key=>$value]) : true;
	}

	public function get_user_email($openid){
		return $openid.'@'.$this->get_domain();
	}

	public function get_avatarurl($openid){
		return $this->get_value($openid, 'avatarurl');
	}

	public function get_nickname($openid){
		return $this->get_value($openid, 'nickname');
	}

	public function get_unionid($openid){
		return $this->get_value($openid, 'unionid');
	}

	public function get_phone_data($openid){
		$phone			= $this->get_value($openid, 'phone') ?: 0;
		$country_code	= $this->get_value($openid, 'country_code') ?: 86;

		return $phone ? ['phone'=>$phone, 'country_code'=>$country_code] : [];
	}

	public function get_openid_by($key, $value){
		return null;
	}

	public function get_user($openid){
		return ['openid'=>$openid];
	}

	public function update_user($openid, $user){
		return true;
	}

	public static function create($type, $appid, $args){
		if(is_array($args)){
			$object	= new static($type, $appid, $args);
		}else{
			$model	= $args;
			$object	= new $model($appid, []);
		}

		return self::register($object);
	}

	// compact
	protected function get_bind($openid, $bind, $unionid=false){
		return $this->get_value($openid, $bind);
	}

	public function get_email($openid){
		return $this->get_user_email($openid);
	}
}

class WPJAM_Qrcode_Bind extends WPJAM_Bind{
	public function verify_qrcode($scene, $code, $output=''){
		$qrcode	= $scene ? $this->cache_get($scene.'_scene') : null;

		if(!$qrcode){
			return new WP_Error('invalid_qrcode');
		}

		if(!$code || empty($qrcode['openid']) || $code != $qrcode['code']){
			return new WP_Error('invalid_code');
		}

		$this->cache_delete($scene.'_scene');

		return $output == 'openid' ? $qrcode['openid'] : $qrcode;
	}

	public function scan_qrcode($openid, $scene){
		$qrcode	= $scene ? $this->cache_get($scene.'_scene') : null;

		if(!$qrcode || (!empty($qrcode['openid']) && $qrcode['openid'] != $openid)){
			return new WP_Error('invalid_qrcode');
		}

		$this->cache_delete($qrcode['key'].'_qrcode');

		if(!empty($qrcode['id']) && !empty($qrcode['bind_callback']) && is_callable($qrcode['bind_callback'])){
			return $qrcode['bind_callback']($openid, $qrcode['id']);
		}else{
			$this->cache_set($scene.'_scene', array_merge($qrcode, ['openid'=>$openid]), 1200);

			return $qrcode['code'];
		}
	}

	public function create_qrcode($key, $args=[]){
		return [];
	}
}

class WPJAM_User_Signup extends WPJAM_Register{
	public function __construct($name, $args=[]){
		if(is_array($args)){
			if(empty($args['type'])){
				$args['type']	= $name;
			}

			parent::__construct($name, $args);
		}
	}

	public function __call($method, $args){
		$object	= wpjam_get_bind_object($this->type, $this->appid);
		$args	= (str_ends_with($method, '_openid') || $method == 'get_by_user_email') ? ['user', ...$args] : $args;

		return $object->$method(...$args);
	}

	public function _compact($openid){	// 兼容代码
		if($this->name == 'weixin'){
			return $this->verify_code($openid['code']);
		}elseif($this->name == 'phone'){
			$result	= wpjam_verify_sms($openid['phone'], $openid['code']);

			return is_wp_error($result) ? $result : $openid['phone'];
		}
	}

	public function signup($openid, $args=null){
		if(is_array($openid)){
			$openid	= $this->_compact($openid);

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

		$user	= $this->get_by_openid($openid);

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

		$args	= $args ?? [];
		$args	= apply_filters('wpjam_user_signup_args', $args, $this->type, $this->appid, $openid);

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

		if(!$user){
			$is_create	= true;

			$args['user_login']	= $openid;
			$args['user_email']	= $this->get_user_email($openid);
			$args['nickname']	= $this->get_nickname($openid);

			$user	= WPJAM_User::create($args);

			if(is_wp_error($user)){
				return $user;
			}
		}else{
			$is_create	= false;
		}

		if(!$is_create && !empty($args['role'])){
			$blog_id	= $args['blog_id'] ?? 0;
			$result		= $user->add_role($args['role'], $blog_id);

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

		$this->bind($openid, $user->id);

		$user->login();

		do_action('wpjam_user_signuped', $user->data, $args);

		return $user;
	}

	public function bind($openid, $user_id=null){
		if(is_array($openid)){
			$openid	= $this->_compact($openid);

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

		$user_id	= $user_id ?? get_current_user_id();
		$result		= $this->bind_openid($user_id, $openid);

		if($result && !is_wp_error($result)){
			$avatarurl	= $this->get_avatarurl($openid);
			$nickname	= $this->get_nickname($openid);
			$user		= wpjam_get_user_object($user_id);

			if($avatarurl){
				$user->update_avatarurl($avatarurl);
			}

			if($nickname && (!$user->nickname || $user->nickname == $openid)){
				$user->update_nickname($nickname);
			}
		}

		return $result;
	}

	public function unbind($user_id=null){
		$user_id	= $user_id ?? get_current_user_id();

		return $this->unbind_openid($user_id);
	}

	public function get_fields($action='login', $for=''){
		return [];
	}

	public function get_attr($action='login', $for=''){
		$fields	= $this->get_fields($action, $for);

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

		$attr	= [];

		if($action == 'bind'){
			if($this->get_openid(get_current_user_id())){
				$fields['action']['value']	= 'unbind';
				$attr['submit_text']		= '解除绑定';
			}else{
				$attr['submit_text']		= '立刻绑定';
			}
		}

		if($for != 'admin'){
			$attr	= array_merge($attr, wpjam_get_ajax_data_attr($this->name.'-'.$action)->to_array());
			$fields	= wpjam_fields($fields)->render(['wrap_tag'=>'p']);
		}

		return $attr+['fields'=>$fields];
	}

	public function ajax_response($data){
		$action	= wpjam_pull($data, 'action');
		$method	= $action == 'login' ? 'signup' : $action;
		$args	= $method == 'unbind' ? [] : [$data];
		$result = wpjam_catch([$this, $method], ...$args);

		return is_wp_error($result) ? $result : true;
	}

	// public function register_bind_user_action(){
	// 	wpjam_register_list_table_action('bind_user', [
	// 		'title'			=> '绑定用户',
	// 		'capability'	=> is_multisite() ? 'manage_sites' : 'manage_options',
	// 		'callback'		=> [$this, 'bind_user_callback'],
	// 		'fields'		=> [
	// 			'nickname'	=> ['title'=>'用户',		'type'=>'view'],
	// 			'user_id'	=> ['title'=>'用户ID',	'type'=>'text',	'class'=>'all-options',	'description'=>'请输入 WordPress 的用户']
	// 		]
	// 	]);
	// }

	// public function bind_user_callback($openid, $data){
	// 	$user_id	= $data['user_id'] ?? 0;

	// 	if($user_id){
	// 		if(get_userdata($user_id)){
	// 			return $this->bind($openid, $user_id);
	// 		}else{
	// 			return new WP_Error('invalid_user');
	// 		}
	// 	}else{
	// 		return $this->unbind_by_openid($openid);
	// 	}
	// }

	public function registered($count){
		foreach(['login', 'bind'] as $action){
			wpjam_register_ajax($this->name.'-'.$action, [
				'nopriv'	=> true,
				'callback'	=> [$this, 'ajax_response']
			]);

			wpjam_register_ajax('get-'.$this->name.'-'.$action, [
				'nopriv'	=> true,
				'verify'	=> false,
				'callback'	=> fn()=> $this->get_attr($action)
			]);
		}
	}

	public static function create($name, $args){
		$model	= wpjam_pull($args, 'model');
		$type	= array_get($args, 'type') ?: $name;
		$appid	= array_get($args, 'appid');

		if(!wpjam_get_bind_object($type, $appid) || !$model){
			return null;
		}

		if(is_object($model)){	// 兼容
			$model	= get_class($model);
		}

		$args['type']	= $type;

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

class WPJAM_User_Qrcode_Signup extends WPJAM_User_Signup{
	public function signup($data, $args=null){
		if(is_array($data)){
			$scene	= $data['scene'] ?? '';
			$code	= $data['code'] ?? '';
			$user	= apply_filters('wpjam_user_signup', null, 'qrcode', $scene, $code);

			if(!$user){
				$args	= $args ?? (array_get($data, 'args') ?: []);
				$openid	= $this->verify_qrcode($scene, $code, 'openid');
				$user	= is_wp_error($openid) ? $openid : parent::signup($openid, $args);
			}

			if(is_wp_error($user)){
				do_action('wpjam_user_signup_failed', 'qrcode', $scene, $user);
			}

			return $user;
		}

		return parent::signup($data, $args);
	}

	public function bind($data, $user_id=null){
		if(is_array($data)){
			$scene	= $data['scene'] ?? '';
			$code	= $data['code'] ?? '';
			$openid	= $this->verify_qrcode($scene, $code, 'openid');

			if(is_wp_error($openid)){
				return $openid;
			}
		}else{
			$openid	= $data;
		}

		return parent::bind($openid, $user_id);
	}

	public function qrcode_signup($scene, $code, $args=[]){
		return $this->signup(compact('scene', 'code'), $args);
	}

	public function get_fields($action='login', $for='admin'){
		if($action == 'bind'){
			$user_id	= get_current_user_id();
			$openid		= $this->get_openid($user_id);
		}else{
			$openid		= null;
		}

		if($openid){
			$view	= '';

			if($avatar = $this->get_avatarurl($openid)){
				$view	.= '<img src="'.str_replace('/132', '/0', $avatar).'" width="272" />'."<br />";
			}

			if($nickname = $this->get_nickname($openid)){
				$view	.= '<strong>'.$nickname.'</strong>';
			}

			$view	= $view ?: $openid;

			return [
				'view'		=> ['type'=>'view',		'title'=>'绑定的微信账号',	'value'=>$view],
				'action'	=> ['type'=>'hidden',	'value'=>'unbind'],
			];
		}else{
			if($action == 'bind'){
				$qrcode	= $this->create_qrcode(md5('bind_'.$user_id), ['id'=>$user_id]);
				$title	= '微信扫码,一键绑定';
			}else{
				$qrcode	= $this->create_qrcode(wp_generate_password(32, false, false));
				$title	= '微信扫码,一键登录';
			}

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

			$img	= array_get($qrcode, 'qrcode_url') ?: array_get($qrcode, 'qrcode');

			return [
				'qrcode'	=> ['type'=>'view',		'title'=>$title,	'value'=>'<img src="'.$img.'" width="272" />'],
				'code'		=> ['type'=>'number',	'title'=>'验证码',	'class'=>'input',	'required', 'size'=>20],
				'scene'		=> ['type'=>'hidden',	'value'=>$qrcode['scene']],
				'action'	=> ['type'=>'hidden',	'value'=>$action],
			];
		}
	}
}