first commit

This commit is contained in:
LinRuiqi
2025-11-19 16:38:24 +08:00
parent 24d18849da
commit 9e7d1a591b
2 changed files with 656 additions and 2 deletions

449
htaccess-manager.php Normal file
View File

@@ -0,0 +1,449 @@
<?php
/**
* Plugin Name: HTACCESS Manager
* Plugin URI: https://lhcy.org
* Description: 根据需求生成.htaccess重定向规则
* Version: 1.0
* Author: 林海草原
* Text Domain: htaccess-manager
* License: Private
*
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
class HTACCESS_Manager {
private $options;
public function __construct() {
$this->options = get_option('htaccess_manager_options');
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'admin_init'));
add_action('admin_notices', array($this, 'admin_notices'));
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
register_uninstall_hook(__FILE__, array('HTACCESS_Manager', 'uninstall'));
}
public function activate() {
// 创建must-use插件目录
$mu_plugins_dir = WP_CONTENT_DIR . '/mu-plugins';
if (!file_exists($mu_plugins_dir)) {
wp_mkdir_p($mu_plugins_dir);
}
// 生成初始选项
if (false === $this->options) {
$default_options = array(
'redirect_type' => 'www_to_root',
'is_subdomain' => false,
'auto_update' => true
);
update_option('htaccess_manager_options', $default_options);
}
$this->update_htaccess();
$this->create_mu_plugin();
}
public function deactivate() {
// 从.htaccess中安全移除本插件的规则
$this->safe_remove_htaccess_rules();
// 清理mu-plugin
$mu_plugin_file = WP_CONTENT_DIR . '/mu-plugins/htaccess-helper.php';
if (file_exists($mu_plugin_file)) {
unlink($mu_plugin_file);
}
}
public static function uninstall() {
// 删除选项
delete_option('htaccess_manager_options');
// 从.htaccess中安全移除规则
self::safe_remove_htaccess_rules_static();
// 清理mu-plugin
$mu_plugin_file = WP_CONTENT_DIR . '/mu-plugins/htaccess-helper.php';
if (file_exists($mu_plugin_file)) {
unlink($mu_plugin_file);
}
}
public function add_admin_menu() {
add_options_page(
'HTACCESS 管理',
'HTACCESS 管理',
'manage_options',
'htaccess-manager',
array($this, 'admin_page')
);
}
public function admin_init() {
register_setting('htaccess_manager', 'htaccess_manager_options');
add_settings_section(
'htaccess_manager_section',
'重定向设置',
array($this, 'section_callback'),
'htaccess_manager'
);
add_settings_field(
'is_subdomain',
'是否为二级域名',
array($this, 'is_subdomain_callback'),
'htaccess_manager',
'htaccess_manager_section'
);
add_settings_field(
'redirect_type',
'重定向类型',
array($this, 'redirect_type_callback'),
'htaccess_manager',
'htaccess_manager_section'
);
add_settings_field(
'auto_update',
'自动更新',
array($this, 'auto_update_callback'),
'htaccess_manager',
'htaccess_manager_section'
);
}
public function section_callback() {
echo '<p>配置您的网站重定向规则。HTTPS重定向是强制的。</p>';
}
public function is_subdomain_callback() {
$value = isset($this->options['is_subdomain']) ? $this->options['is_subdomain'] : false;
?>
<label>
<input type="checkbox" name="htaccess_manager_options[is_subdomain]" value="1" <?php checked(1, $value); ?> />
我的网站使用二级域名sub.domain.com
</label>
<p class="description">如果启用将只强制HTTPS不处理www和根域名之间的跳转。</p>
<?php
}
public function redirect_type_callback() {
$value = isset($this->options['redirect_type']) ? $this->options['redirect_type'] : 'www_to_root';
?>
<fieldset>
<label>
<input type="radio" name="htaccess_manager_options[redirect_type]" value="www_to_root" <?php checked('www_to_root', $value); ?> <?php echo isset($this->options['is_subdomain']) && $this->options['is_subdomain'] ? 'disabled' : ''; ?> />
www跳转到根域名www.example.com → example.com
</label><br>
<label>
<input type="radio" name="htaccess_manager_options[redirect_type]" value="root_to_www" <?php checked('root_to_www', $value); ?> <?php echo isset($this->options['is_subdomain']) && $this->options['is_subdomain'] ? 'disabled' : ''; ?> />
根域名跳转到wwwexample.com → www.example.com
</label>
</fieldset>
<?php
}
public function auto_update_callback() {
$value = isset($this->options['auto_update']) ? $this->options['auto_update'] : true;
?>
<label>
<input type="checkbox" name="htaccess_manager_options[auto_update]" value="1" <?php checked(1, $value); ?> />
设置更改时自动更新.htaccess文件
</label>
<?php
}
public function admin_page() {
?>
<div class="wrap">
<h1>HTACCESS 管理</h1>
<form action="options.php" method="post">
<?php
settings_fields('htaccess_manager');
do_settings_sections('htaccess_manager');
submit_button('保存设置');
?>
</form>
<hr>
<div class="card">
<h2>当前生成的规则</h2>
<textarea readonly style="width: 100%; height: 200px; font-family: monospace; background: #f6f6f6;"><?php echo esc_textarea($this->generate_htaccess_rules()); ?></textarea>
<p>
<button type="button" class="button button-secondary" onclick="copyToClipboard(this)">复制规则</button>
<span id="copy-message" style="margin-left: 10px; color: green; display: none;">已复制到剪贴板!</span>
</p>
</div>
<div class="card">
<h2>手动更新</h2>
<p>如果自动更新失败,您可以手动复制上面的规则到您的.htaccess文件中。</p>
<form method="post">
<?php wp_nonce_field('manual_update', 'htaccess_nonce'); ?>
<input type="submit" name="manual_update" class="button button-primary" value="手动更新.htaccess文件">
<input type="submit" name="manual_remove" class="button button-secondary" value="从.htaccess中移除规则" onclick="return confirm('确定要从.htaccess文件中移除所有HTACCESS Manager规则吗')">
</form>
</div>
<div class="card">
<h2>当前.htaccess内容预览</h2>
<?php
$htaccess_file = ABSPATH . '.htaccess';
if (file_exists($htaccess_file)) {
$content = file_get_contents($htaccess_file);
echo '<textarea readonly style="width: 100%; height: 300px; font-family: monospace; background: #f0f0f0;">' . esc_textarea($content) . '</textarea>';
} else {
echo '<p>.htaccess文件不存在。</p>';
}
?>
</div>
</div>
<script>
function copyToClipboard(button) {
const textarea = document.querySelector('textarea');
textarea.select();
document.execCommand('copy');
const message = document.getElementById('copy-message');
message.style.display = 'inline';
setTimeout(() => {
message.style.display = 'none';
}, 2000);
}
// 动态禁用/启用重定向类型选项
document.querySelector('input[name="htaccess_manager_options[is_subdomain]"]').addEventListener('change', function() {
var radios = document.querySelectorAll('input[name="htaccess_manager_options[redirect_type]"]');
radios.forEach(function(radio) {
radio.disabled = this.checked;
}.bind(this));
});
</script>
<?php
// 处理手动更新
if (isset($_POST['manual_update']) && check_admin_referer('manual_update', 'htaccess_nonce')) {
$this->update_htaccess();
echo '<div class="notice notice-success is-dismissible"><p>.htaccess文件已更新</p></div>';
}
// 处理手动移除
if (isset($_POST['manual_remove']) && check_admin_referer('manual_update', 'htaccess_nonce')) {
$this->safe_remove_htaccess_rules();
echo '<div class="notice notice-success is-dismissible"><p>已从.htaccess文件中移除所有HTACCESS Manager规则</p></div>';
}
}
public function admin_notices() {
if (isset($_GET['settings-updated']) && $_GET['settings-updated']) {
if (isset($this->options['auto_update']) && $this->options['auto_update']) {
$this->update_htaccess();
echo '<div class="notice notice-success is-dismissible"><p>设置已保存并更新了.htaccess文件</p></div>';
} else {
echo '<div class="notice notice-success is-dismissible"><p>设置已保存!请手动更新.htaccess文件。</p></div>';
}
}
}
private function generate_htaccess_rules() {
$rules = array();
$rules[] = 'RewriteEngine On';
$rules[] = '';
if (isset($this->options['is_subdomain']) && $this->options['is_subdomain']) {
// 二级域名情况只强制HTTPS
$rules[] = '# 强制HTTPS重定向二级域名';
$rules[] = 'RewriteCond %{HTTPS} off';
$rules[] = 'RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]';
} else {
// 主域名情况处理www和HTTPS
$rules[] = '# 同时处理 HTTPS 和 WWW 重定向';
if (isset($this->options['redirect_type']) && $this->options['redirect_type'] === 'root_to_www') {
// 根域名跳转到www
$rules[] = 'RewriteCond %{HTTPS} off [OR]';
$rules[] = 'RewriteCond %{HTTP_HOST} ^' . preg_quote(parse_url(home_url(), PHP_URL_HOST), '.') . '$ [NC]';
$rules[] = 'RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]';
$rules[] = 'RewriteRule ^ https://www.%1%{REQUEST_URI} [L,R=301]';
} else {
// www跳转到根域名默认
$rules[] = 'RewriteCond %{HTTPS} off [OR]';
$rules[] = 'RewriteCond %{HTTP_HOST} ^www\. [NC]';
$rules[] = 'RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]';
$rules[] = 'RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]';
}
}
return implode("\n", $rules);
}
private function update_htaccess() {
$htaccess_file = ABSPATH . '.htaccess';
if (!file_exists($htaccess_file)) {
// 如果.htaccess文件不存在创建它
touch($htaccess_file);
}
if (is_writable($htaccess_file)) {
$new_rules = $this->generate_htaccess_rules();
// 读取现有内容
$content = file_get_contents($htaccess_file);
// 定义标记
$start_marker = '# BEGIN HTACCESS Manager';
$end_marker = '# END HTACCESS Manager';
// 检查是否已有我们的规则
if (strpos($content, $start_marker) !== false) {
// 替换现有规则
$pattern = '/' . preg_quote($start_marker, '/') . '.*?' . preg_quote($end_marker, '/') . '/s';
$replacement = $start_marker . "\n" . $new_rules . "\n" . $end_marker;
$content = preg_replace($pattern, $replacement, $content);
} else {
// 在文件开头添加新规则在WordPress规则之前
$wp_start_marker = '# BEGIN WordPress';
if (strpos($content, $wp_start_marker) !== false) {
$replacement = $start_marker . "\n" . $new_rules . "\n" . $end_marker . "\n\n" . $wp_start_marker;
$content = str_replace($wp_start_marker, $replacement, $content);
} else {
// 如果没有WordPress标记直接添加到文件开头
$content = $start_marker . "\n" . $new_rules . "\n" . $end_marker . "\n\n" . $content;
}
}
// 清理多余的空行
$content = preg_replace("/(\r\n|\r|\n){3,}/", "\n\n", $content);
file_put_contents($htaccess_file, $content);
return true;
}
return false;
}
private function safe_remove_htaccess_rules() {
$htaccess_file = ABSPATH . '.htaccess';
if (file_exists($htaccess_file) && is_writable($htaccess_file)) {
$content = file_get_contents($htaccess_file);
// 备份原始内容
$backup_content = $content;
// 只移除我们自己的规则标记和内容
$start_marker = '# BEGIN HTACCESS Manager';
$end_marker = '# END HTACCESS Manager';
$pattern = '/' . preg_quote($start_marker, '/') . '.*?' . preg_quote($end_marker, '/') . '\s*\n?/s';
$content = preg_replace($pattern, '', $content);
// 清理多余的空行
$content = preg_replace("/(\r\n|\r|\n){3,}/", "\n\n", $content);
$content = trim($content) . "\n";
// 只有在内容确实发生变化且不为空时才写入
if ($content !== $backup_content && !empty(trim($content))) {
file_put_contents($htaccess_file, $content);
}
return true;
}
return false;
}
public static function safe_remove_htaccess_rules_static() {
$htaccess_file = ABSPATH . '.htaccess';
if (file_exists($htaccess_file) && is_writable($htaccess_file)) {
$content = file_get_contents($htaccess_file);
// 备份原始内容
$backup_content = $content;
// 只移除我们自己的规则标记和内容
$start_marker = '# BEGIN HTACCESS Manager';
$end_marker = '# END HTACCESS Manager';
$pattern = '/' . preg_quote($start_marker, '/') . '.*?' . preg_quote($end_marker, '/') . '\s*\n?/s';
$content = preg_replace($pattern, '', $content);
// 清理多余的空行
$content = preg_replace("/(\r\n|\r|\n){3,}/", "\n\n", $content);
$content = trim($content) . "\n";
// 只有在内容确实发生变化且不为空时才写入
if ($content !== $backup_content && !empty(trim($content))) {
file_put_contents($htaccess_file, $content);
}
return true;
}
return false;
}
private function create_mu_plugin() {
$mu_plugin_content = '<?php
/**
* Must-use plugin for HTACCESS Manager
* Auto-generated file - DO NOT EDIT
*/
defined(\'ABSPATH\') or die();
class HTACCESS_Manager_Helper {
public static function safe_remove_rules() {
$htaccess_file = ABSPATH . \'.htaccess\';
if (file_exists($htaccess_file) && is_writable($htaccess_file)) {
$content = file_get_contents($htaccess_file);
$backup_content = $content;
// 只移除HTACCESS Manager的规则
$start_marker = \'# BEGIN HTACCESS Manager\';
$end_marker = \'# END HTACCESS Manager\';
$pattern = \'/\' . preg_quote($start_marker, \'/\') . \'.*?\' . preg_quote($end_marker, \'/\') . \'\\s*\\n?/s\';
$content = preg_replace($pattern, \'\', $content);
// 清理多余空行
$content = preg_replace("/(\\r\\n|\\r|\\n){3,}/", "\\n\\n", $content);
$content = trim($content) . "\\n";
// 只有在内容变化且不为空时才写入
if ($content !== $backup_content && !empty(trim($content))) {
file_put_contents($htaccess_file, $content);
}
}
}
}
// 在主插件被禁用时清理规则
add_action(\'deactivated_plugin\', function($plugin) {
if ($plugin === \'htaccess-manager/htaccess-manager.php\') {
HTACCESS_Manager_Helper::safe_remove_rules();
}
});
';
$mu_plugin_file = WP_CONTENT_DIR . '/mu-plugins/htaccess-helper.php';
file_put_contents($mu_plugin_file, $mu_plugin_content);
}
}
new HTACCESS_Manager();