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/components/wpjam-cdn.php
<?php
/*
Name: CDN 加速
URI: https://mp.weixin.qq.com/s/bie4JkmExgULgvEgx-AjUw
Description: CDN 加速使用云存储对博客的静态资源进行 CDN 加速。
Version: 2.0
*/
class WPJAM_CDN extends WPJAM_Option_Model{
	public static function get_sections(){
		$cdn_fields	= WPJAM_CDN_Type::get_setting_fields(['type'=>'select', 'name'=>'cdn_name', 'title'=>'云存储'])+[
			'host'		=> ['title'=>'CDN 域名',	'show_if'=>['cdn_name', '!=', ''],	'type'=>'url',	'description'=>'设置为在CDN云存储绑定的域名。'],
			'disabled'	=> ['title'=>'切换回本站','show_if'=>['cdn_name', '=', ''],	'label'=>'如使用 CDN 之后切换回使用本站图片,请勾选该选项,并将原 CDN 域名填回「本地设置」的「额外域名」中。'],
			'image'		=> ['title'=>'图片处理',	'show_if'=>['cdn_name', 'IN', ['aliyun_oss', 'volc_imagex', 'qcloud_cos', 'qiniu']],	'class'=>'switch',	'value'=>1,	'label'=>'开启云存储图片处理功能,使用云存储进行裁图、添加水印等操作。<br />&emsp;<strong>*</strong> 注意:开启之后,文章和媒体库中的所有图片都会镜像到云存储。'],
		];

		$local_fields	= [
			'local'		=> ['title'=>'本地域名',	'fields'=>[
				'local'		=> ['description'=>'并将该域名填入<strong>云存储的镜像源</strong>', 'type'=>'url',	'value'=>home_url()],
				'no_http'	=> ['label'=>'将无<code>http://</code>或<code>https://</code>的静态资源也进行镜像处理'],
			]],
			'exts'		=> ['title'=>'扩展名',	'fields'=>[
				'img_exts'	=> ['label'=>'支持所有图片扩展名'],
				'exts'		=> ['type'=>'mu-text',	'button_text'=>'添加扩展名',	'direction'=>'row',	'sortable'=>false,	'description'=>'镜像到云存储的静态文件扩展名'],
			]],
			'dirs'		=> ['title'=>'目录',		'type'=>'mu-text',	'direction'=>'row',	'sortable'=>false,	'style'=>'width:120px;', 'value'=>['wp-content','wp-includes'],	'description'=>'镜像到云存储的静态文件所在目录'],
			'locals'	=> ['title'=>'额外域名',	'type'=>'mu-text',	'item_type'=>'url'],
		];

		$sections	= [
			'cdn'	=> ['title'=>'云存储设置',	'fields'=>$cdn_fields],
			'local'	=> ['title'=>'本地设置',		'fields'=>$local_fields],
		];

		if(is_network_admin()){
			return wpjam_except($sections, 'local.fields.local');
		}

		if(!wpjam_basic_get_setting('upload_external_images')){
			if(!is_multisite() && $GLOBALS['wp_rewrite']->using_mod_rewrite_permalinks() && extension_loaded('gd')){
				$remote_summary	= '*自动将外部图片镜像到云存储需要博客支持固定链接和服务器支持GD库(不支持gif图片)';

				$remote_fields['remote']	= ['title'=>'外部图片',	'options'=>[''=>'关闭外部图片镜像到云存储', '1'=>'自动将外部图片镜像到云存储(不推荐)']];
			}else{
				$remote_fields['external']	= ['title'=>'外部图片',	'type'=>'view',	'value'=>'请先到「文章设置」中开启「支持在文章列表页上传外部图片」'];
			}
		}else{
			$remote_fields['external']		= ['title'=>'外部图片',	'type'=>'view',	'value'=>'已在「文章设置」中开启「支持在文章列表页上传外部图片」'];
		}

		$remote_fields['exceptions']	= ['title'=>'例外',	'type'=>'textarea',	'class'=>'',	'description'=>'如果外部图片的链接中包含以上字符串或域名,就不会被保存并镜像到云存储。'];

		$wm_fields		= ['title'=>'水印设置',	'show_if'=>['cdn_name', '!=', 'volc_imagex'],	'fields'=>[
			'view'		=> ['type'=>'view',		'title'=>'使用说明:',	'value'=>'请使用云存储域名下的图片,水印设置仅应用于文章内容中的图片'],
			'watermark'	=> ['type'=>'image',	'title'=>'水印图片:'],
			'dissolve'	=> ['type'=>'number',	'title'=>'透明度:',	'class'=>'small-text',	'description'=>'1-100,默认100(不透明)', 'min'=>0, 'max'=>100],
			'gravity'	=> ['type'=>'select',	'title'=>'水印位置:',	'options'=>[
				'SouthEast'	=> '右下角',
				'SouthWest'	=> '左下角',
				'NorthEast'	=> '右上角',
				'NorthWest'	=> '左上角',
				'Center'	=> '正中间',
				'West'		=> '左中间',
				'East'		=> '右中间',
				'North'		=> '上中间',
				'South'		=> '下中间'
			]],
			'distance'	=> ['type'=>'size',	'title'=>'水印边距:',	'fields'=>['width'=>['value'=>10], 'height'=>['value'=>10]]],
			'wm_size'	=> ['type'=>'size',	'title'=>'最小尺寸:',	'description'=>'小于该尺寸的图片都不会加上水印',	'show_if'=>['cdn_name', 'IN', ['aliyun_oss', 'qcloud_cos']]]
		]];

		$image_fields	= [
			'thumb_set'	=> ['title'=>'缩图设置',	'fields'=>[
				'no_subsizes'	=> ['value'=>1,	'label'=>'使用云存储的缩图功能,本地不再生成各种尺寸的缩略图。'],
				'thumbnail'		=> ['value'=>1,	'label'=>'使用云存储缩图功能对文章中的图片进行最佳尺寸显示处理。', 'fields'=>['max_width'=>['type'=>'number', 'value'=>($GLOBALS['content_width'] ?? 0), 'before'=>'文章中图片最大宽度:', 'class'=>'small-text', 'after'=>'px。']]]
			]],
			'webp'		=> ['title'=>'WebP 格式',	'label'=>'将图片转换成 WebP 格式。',	'show_if'=>['cdn_name', 'IN', ['volc_imagex', 'aliyun_oss', 'qcloud_cos']]],
			'image_set'	=> ['title'=>'格式质量',	'show_if'=>['cdn_name', '!=', 'volc_imagex'],	'fields'=>[
				'interlace'		=> ['label'=>'JPEG格式图片渐进显示。'],
				'quality'		=> ['type'=>'number',	'before'=>'图片质量:',	'class'=>'small-text',	'mim'=>0,	'max'=>100]
			]],
			'wm_set'	=> $wm_fields,
			'volc_imagex_template'	=> ['title'=>'火山引擎图片处理模板',	'show_if'=>['cdn_name', 'volc_imagex']]
		];

		return $sections+[
			'image'		=> ['title'=>'图片设置',	'fields'=>$image_fields,	'show_if'=>['image', 1]],
			'remote'	=> ['title'=>'外部图片',	'fields'=>$remote_fields,	'show_if'=>['cdn_name', '!=', ''],	'summary'=>$remote_summary ?? ''],
		];
	}

	public static function get_setting($name='', ...$args){
		if(in_array($name, ['dx', 'dy'])){
			$name	= 'distance.'.['dx'=>'width', 'dy'=>'height'][$name];
		}elseif(in_array($name, ['wm_width', 'wm_height'])){
			$name	= 'wm_size.'.substr($name, 3);
		}

		$value	= parent::get_setting($name, ...$args);

		if(in_array($name, ['exts', 'dirs'])){
			$value	= $value ? array_filter(array_map('trim', $value)) : [];

			if($name == 'exts'){
				$value	= is_login() ? array_diff($value, ['js','css']) : $value;
				$value	= parent::get_setting('img_exts') ? array_unique(array_merge($value, wp_get_ext_types()['image'])) : $value;
			}
		}elseif($name == 'watermark'){
			$value	= $value ? (explode('?', $value))[0] : '';
		}

		return $value;
	}

	public static function get_sizes($id, $meta){
		foreach(wp_get_registered_image_subsizes() as $name => $size){
			$downsize	= self::downsize($id, $size, $meta);

			if($downsize && !empty($downsize[3])){
				$arr	= explode('?', $downsize[0]);

				$sizes[$name]	= [
					'file'			=> wp_basename($arr[0]).(isset($arr[1]) ? '?'.$arr[1] : ''),
					'url'			=> $downsize[0],
					'width'			=> $downsize[1],
					'height'		=> $downsize[2],
					'orientation'	=> $downsize[2] > $downsize[1] ? 'portrait' : 'landscape',
				];
			}
		}

		return $sizes ?? [];
	}

	public static function is($url){
		return apply_filters('wpjam_is_cdn_url', str_starts_with($url, CDN_HOST), $url);
	}

	public static function is_external($url, $scene){
		if(self::is($url)){
			return false;
		}

		$exceptions	= $scene == 'fetch' ? self::get_setting('exceptions') : [];
		$exceptions	= $exceptions ? array_filter(explode("\n", $exceptions)) : [];

		return $exceptions ? array_all($exceptions, fn($v)=> strpos($url, trim($v)) === false) : true;
	}

	public static function downsize($id, $size, $meta=null){
		$url	= wp_get_attachment_url($id);
		$meta	??= wp_get_attachment_metadata($id);

		if(is_array($meta) && isset($meta['width'], $meta['height'])){
			$ratio	= 2;
			$size	= wpjam_parse_size($size, $ratio);

			if($size['crop']){
				[$width, $height]	= [min($size['width'], $meta['width']), min($size['height'], $meta['height'])];
			}else{
				[$width, $height]	= wp_constrain_dimensions($meta['width'], $meta['height'], $size['width'], $size['height']);
			}

			if($width < $meta['width'] || $height <  $meta['height']){
				return [wpjam_get_thumbnail($url, compact('width', 'height')), (int)($width/$ratio), (int)($height/$ratio), true];
			}else{
				return [wpjam_get_thumbnail($url), $width, $height, false];
			}
		}

		return [];
	}

	public static function scheme_replace($url){
		$http	= str_starts_with($url, 'http://');

		return ($http ? 'https://' : 'http://').substr($url, ($http ? 7 : 8));
	}

	public static function replace($str, $to_cdn=true, $html=false){
		$to	= $to_cdn ? CDN_HOST : LOCAL_HOST;

		if(!$html && str_starts_with($str, $to)){
			return $str;
		}

		$locals	= [self::scheme_replace(LOCAL_HOST), ...array_map('untrailingslashit', self::get_setting('locals') ?: [])];
		$locals	= $to_cdn ? [...$locals, self::scheme_replace(CDN_HOST), LOCAL_HOST] : $locals;
		$locals	= array_unique(apply_filters('wpjam_cdn_local_hosts', $locals));

		if($html){
			return strtr($str, array_fill_keys($locals, $to));
		}

		$local	= array_find($locals, fn($v)=> str_starts_with($str, $v));

		return $local ? $to.substr($str, strlen($local)) : $str;
	}

	public static function filter_html($html){
		$local	= preg_quote(LOCAL_HOST).(self::get_setting('no_http') ? '|'.preg_quote(str_replace(['http://', 'https://'], '//', LOCAL_HOST)) : '');
		$exts	= self::get_setting('exts');
		$dirs	= self::get_setting('dirs');
		$dirs	= $dirs ? '('.implode('|', array_map(fn($dir)=> preg_quote(trim($dir, '/')), $dirs)).')\/' : '';
		$regex	= '#('.$local.')\/('.$dirs.'[^\s\?\\\'\"\;\>\<]{1,}\.('.implode('|', $exts).')'.'[\"\\\'\)\s\]\?]{1})#';

		return wpjam_replace($regex, CDN_HOST.'/$2', self::replace($html, false, true));
	}

	public static function resize($proc, $size, $max){
		if($max && $size['width'] > $max){
			$size	= ['width'=>$max, 'height'=>(int)($max/$size['width']*$size['height'])];

			array_walk($size, fn($v, $k)=> $v ? $proc->set_attribute($k, $v) : null);
		}

		return $size;
	}

	public static function filter_content_img_tag($img_tag, $context, $id){
		$proc	= $context == 'the_content' ? wpjam_html_tag_processor($img_tag) : '';
		$src	= $proc ? $proc->get_attribute('src') : '';
	
		if(!$src || wpjam_is_external_url($src)){
			return $img_tag;
		}

		$attr	= ['width', 'height'];
		$valid	= fn($size)=> array_all($size, fn($v)=> is_numeric($v));
		$size	= wpjam_fill($attr, fn($k)=> $proc->get_attribute($k) ?: 0);
		$max	= (int)self::get_setting('max_width', ($GLOBALS['content_width'] ?? 0));
		$meta	= $id ? wp_get_attachment_metadata($id) : null;

		if($meta && is_array($meta) && !array_filter($size)){
			$name	= $proc->get_attribute('data-size');
			$size	= self::resize($proc, wpjam_slice((($name && $name != 'full') ? ($meta['sizes'][$name] ?? $meta) : $meta), $attr), $max);
		}else{
			if($max){
				if($valid($size) && $size['width'] > $max){
					$size	= self::resize($proc, $size, $max);
				}elseif(!array_filter($size)){
					$size['width']	= $max;
				}
			}
		}

		if($meta && is_array($meta) && $valid($size)){
			$s2	= wpjam_fill($attr, fn($k)=> $size[$k]*2 >= $meta[$k]);

			if($s2['width'] && $s2['height']){
				unset($size['width'], $size['height']);
			}elseif($s2['width'] && !$size['height']){
				unset($size['width']);
			}elseif($s2['height'] && !$size['width']){
				unset($size['height']);
			}
		}

		$proc->set_attribute('src', wpjam_get_thumbnail($src, wpjam_parse_size($size+['content'=>true], 2)));

		return $proc->get_updated_html();
	}

	public static function filter_image_block($content, $parsed){
		$slug	= $parsed['attrs']['sizeSlug'] ?? '';
		$slug	= $slug == 'full' ? '' : $slug;
		$size	= wpjam_slice($parsed['attrs'], ['width', 'height']);

		if($slug || $size){
			$proc	= wpjam_html_tag_processor($content, 'img');

			if($slug){
				$proc->set_attribute('data-size', $slug);
			}

			if($size){
				array_walk($size, fn($v, $k)=> $proc->get_attribute($k) || $proc->set_attribute($k, str_ends_with($v, 'px') ? substr($v, 0, -2) : $v));
			}

			return (string)$proc;
		}

		return $content;
	}

	public static function filter_content($content){
		if(doing_filter('get_the_excerpt') || false === strpos($content, '<img')){
			return $content;
		}

		if(!wpjam_is_json_request()){
			$content	= self::replace($content, false, true);
		}

		if(self::get_setting('no_subsizes', 1)){
			add_filter('wp_img_tag_add_srcset_and_sizes_attr', fn()=> false);
			add_filter('wp_img_tag_add_width_and_height_attr', fn()=> false);
		}

		add_filter('wp_content_img_tag', [self::class, 'filter_content_img_tag'], 1, 3);

		return $content;
	}

	public static function on_plugins_loaded(){
		define('CDN_NAME',		self::get_setting('cdn_name'));
		define('CDN_HOST',		untrailingslashit(self::get_setting('host') ?: site_url()));
		define('LOCAL_HOST',	untrailingslashit(set_url_scheme(self::get_setting('local') ?: site_url())));

		if(CDN_NAME){
			do_action('wpjam_cdn_loaded');

			$exts	= self::get_setting('exts');

			if(!is_admin() && $exts){
				add_filter((wpjam_is_json_request() ? 'the_content' : 'wpjam_html'), [self::class, 'filter_html'], 5);
			}

			add_filter('wpjam_is_external_url', fn($status, $url, $scene)=> $status && self::is_external($url, $scene), 10, 3);
			add_filter('wp_resource_hints', fn($urls, $type)=> array_merge($urls, $type == 'dns-prefetch' ? [CDN_HOST] : []), 10, 2);

			if(self::get_setting('image', 1)){
				if(!self::get_setting('distance')){
					self::update_setting('distance', ['width'=>self::get_setting('dx', 10), 'height'=>self::get_setting('dy', 10)]);
				}

				if(!self::get_setting('wm_size')){
					self::update_setting('wm_size', ['width'=>self::get_setting('wm_width', 0), 'height'=>self::get_setting('wm_height', 0)]);
				}

				WPJAM_CDN_Type::load(CDN_NAME);

				if(self::get_setting('no_subsizes', 1)){
					add_filter('wp_calculate_image_srcset_meta',	fn()=> []);
					add_filter('embed_thumbnail_image_size',		fn()=> '160x120');
					add_filter('intermediate_image_sizes_advanced',	fn($sizes)=> isset($sizes['full']) ? ['full'=>$sizes['full']] : []);
					add_filter('wp_get_attachment_metadata',		fn($meta, $id)=> array_merge($meta, (wp_attachment_is_image($id) && is_array($meta) && empty($meta['sizes'])) ? ['sizes'=>self::get_sizes($id, $meta)] : []), 10, 2);
				}

				if(self::get_setting('thumbnail', 1)){
					add_filter('render_block_core/image',	[self::class, 'filter_image_block'], 5, 2);
					add_filter('the_content',				[self::class, 'filter_content'], 5);
				}

				if($exts){
					add_filter('wp_get_attachment_url',	fn($url)=> preg_match('/\.('.implode('|', $exts).')$/i', $url) ? self::replace($url) : $url);
				}

				add_filter('wpjam_thumbnail',	[self::class, 'replace'], 1, 1);
				add_filter('wp_mime_type_icon',	[self::class, 'replace']);
				// add_filter('upload_dir',		[self::class, 'filter_upload_dir']);
				add_filter('image_downsize',	fn($downsize, $id, $size)=> wp_attachment_is_image($id) ? self::downsize($id, $size) : $downsize, 10, 3);
			}

			if(!wpjam_basic_get_setting('upload_external_images')){
				if(self::get_setting('remote') === 'download'){
					if(is_admin()){
						WPJAM_Basic::update_setting('upload_external_images', 1);

						self::update_setting('remote', 0);
					}
				}elseif(self::get_setting('remote')){
					if(!is_multisite()){
						include dirname(__DIR__).'/cdn/remote.php';
					}
				}
			}
		}else{
			if(self::get_setting('disabled')){
				$replace	= fn($html)=> self::replace($html, false, true);

				if(!is_admin() && !wpjam_is_json_request()){
					add_filter('wpjam_html',	$replace, 9);
				}

				add_filter('the_content',		$replace, 5);
				add_filter('wpjam_thumbnail',	$replace, 9);
			}
		}
	}
}

class WPJAM_CDN_Type extends WPJAM_Register{
	public static function load($name){
		$object	= self::get($name);
		$file	= $object ? ($object->file ?: dirname(__DIR__).'/cdn/'.$name.'.php') : '';

		if($file && file_exists($file)){
			$callback	= include $file;

			if($callback !== 1 && is_callable($callback)){
				add_filter('wpjam_thumbnail', $callback, 10, 2);
			}
		}
	}

	public static function get_defaults(){
		return [
			'aliyun_oss'	=> [
				'title'			=> '阿里云OSS',
				'description'	=> '请点击这里注册和申请<strong><a href="http://wpjam.com/go/aliyun/" target="_blank">阿里云</a></strong>可获得代金券,点击这里查看<strong><a href="https://blog.wpjam.com/m/aliyun-oss-cdn/" target="_blank">阿里云OSS详细使用指南</a></strong>。'
			],
			'qcloud_cos'	=> [
				'title'			=> '腾讯云COS',
				'description'	=> '请点击这里注册和申请<strong><a href="http://wpjam.com/go/qcloud/" target="_blank">腾讯云</a></strong>可获得优惠券,点击这里查看<strong><a href="https://blog.wpjam.com/m/qcloud-cos-cdn/" target="_blank">腾讯云COS详细使用指南</a></strong>。'
			],
			'volc_imagex'	=> [
				'title'			=> '火山引擎veImageX',
				'description'	=> '使用邀请码 <strong>CLEMNL</strong> 注册和申请<strong><a href="https://wpjam.com/go/volc-imagex" target="_blank">火山引擎</a></strong>,可以领取每月免费额度(10GB流量和10GB存储等),<br />以及HTTPS 访问免费和回源流量免费,点击这里查看<strong><a href="http://blog.wpjam.com/m/volc-veimagex/" target="_blank">火山引擎 veImageX 详细使用指南</a></strong>。'
			],
			'ucloud'		=> ['title'=>'UCloud'],
			'qiniu'			=> ['title'=>'七牛云存储'],
		];
	}
}

function wpjam_register_cdn($name, $args){
	return WPJAM_CDN_Type::register($name, $args);
}

function wpjam_unregister_cdn($name){
	return WPJAM_CDN_Type::unregister($name);
}

function wpjam_cdn_get_setting($name, ...$args){
	return WPJAM_CDN::get_setting($name, ...$args);
}

function wpjam_cdn_host_replace($html, $to_cdn=true){
	return WPJAM_CDN::replace($html, $to_cdn, true);
}

function wpjam_local_host_replace($html){
	return str_replace(CDN_HOST, LOCAL_HOST, $html);
}

function wpjam_is_cdn_url($url){
	return WPJAM_CDN::is($url);
}

function wpjam_restore_attachment_file($id){
	$file = get_attached_file($id, true);

	if($file && !file_exists($file)){
		$dir	= dirname($file);

		if(!is_dir($dir)){
			mkdir($dir, 0777, true);
		}

		$image	= WPJAM_CDN::replace(wp_get_attachment_url($id));
		$result	= wpjam_remote_request($image, ['stream'=>true, 'filename'=>$file]);

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

	return true;
}

wpjam_register_option('wpjam-cdn',	[
	'title'			=> 'CDN加速',
	'model'			=> 'WPJAM_CDN',
	'hooks'			=> ['plugins_loaded', ['WPJAM_CDN', 'on_plugins_loaded'], 99],
	'menu_page'		=> ['parent'=>'wpjam-basic', 'position'=>2, 'summary'=>__FILE__],
	'site_default'	=> true,
]);