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

209
README.md
View File

@@ -1,3 +1,208 @@
# htaccess-manager # HTACCESS Manager
一个专业的WordPress插件用于智能管理.htaccess文件中的重定向规则。支持HTTPS强制重定向、WWW与根域名跳转配置以及二级域名站点支持。 一个专业的WordPress插件用于智能管理.htaccess文件中的重定向规则。支持HTTPS强制重定向、WWW与根域名跳转配置以及二级域名站点支持。
## 功能特点
### 🔒 强制HTTPS重定向
- 自动将所有HTTP请求重定向到HTTPS
- 确保网站安全连接
### 🌐 灵活的域名跳转配置
- **WWW跳转到根域名**`www.example.com``example.com`
- **根域名跳转到WWW**`example.com``www.example.com`
- **自由选择跳转方向**
### 🏷️ 二级域名支持
- 专门针对二级域名站点的优化配置
- 启用后只强制HTTPS不处理WWW跳转
### ⚡ 智能规则管理
- 自动生成优化的.htaccess规则
- 不干扰其他插件的现有规则
- 安全的规则添加和移除机制
### 🛡️ 安全可靠
- Must-use插件支持确保高可靠性
- 精确的规则匹配,避免误删其他配置
- 完整的卸载清理机制
## 安装方法
### 方法一:手动安装
1. 下载插件ZIP文件
2. 在WordPress后台进入【插件】→【安装插件】→【上传插件】
3. 选择ZIP文件并上传
4. 激活插件
### 方法二FTP安装
1. 解压插件文件到 `wp-content/plugins/htaccess-manager/`
2. 在WordPress后台【插件】页面激活"HTACCESS Manager"
## 使用方法
### 基本配置
1. 进入【设置】→【HTACCESS 管理】
2. 根据您的需求配置选项:
- **是否为二级域名**:如果使用类似 `sub.domain.com` 的地址请启用
- **重定向类型**选择WWW与根域名的跳转方向
- **自动更新**:启用后设置更改时自动更新.htaccess文件
### 配置示例
#### 场景1主域名站点WWW跳转到根域名
```
www.example.com → example.com
http → https
```
**配置:**
- 是否为二级域名:❌ 否
- 重定向类型:✅ WWW跳转到根域名
#### 场景2主域名站点根域名跳转到WWW
```
example.com → www.example.com
http → https
```
**配置:**
- 是否为二级域名:❌ 否
- 重定向类型:✅ 根域名跳转到WWW
#### 场景3二级域名站点
```
sub.example.com → https://sub.example.com
不处理WWW跳转
```
**配置:**
- 是否为二级域名:✅ 是
- 重定向类型:自动禁用
### 手动操作
- **复制规则**:手动复制生成的规则到.htaccess文件
- **手动更新**:强制更新.htaccess文件
- **移除规则**:安全移除所有本插件添加的规则
## 生成的规则示例
### WWW跳转到根域名 + HTTPS
```apache
# BEGIN HTACCESS Manager
RewriteEngine On
# 同时处理 HTTPS 和 WWW 重定向
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+)$ [NC]
RewriteRule ^ https://%1%{REQUEST_URI} [L,R=301]
# END HTACCESS Manager
```
### 二级域名 + 只强制HTTPS
```apache
# BEGIN HTACCESS Manager
RewriteEngine On
# 强制HTTPS重定向二级域名
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# END HTACCESS Manager
```
## 兼容性
### 测试环境
- ✅ WordPress 5.0+
- ✅ PHP 7.4+
- ✅ Apache 2.4+
- ✅ LiteSpeed服务器
- ✅ 多站点网络
### 插件兼容
- ✅ 与大多数缓存插件兼容
- ✅ 与安全插件兼容
- ✅ 不影响其他.htaccess规则生成插件
## 故障排除
### 常见问题
**Q: 插件激活后网站出现重定向循环?**
A: 检查服务器是否已配置SSL证书或尝试清除浏览器缓存。
**Q: .htaccess文件无法写入**
A: 确保.htaccess文件权限设置为644或使用手动复制功能。
**Q: 规则没有生效?**
A: 确认服务器支持mod_rewrite模块检查Apache配置。
**Q: 如何恢复默认设置?**
A: 停用并重新激活插件,或使用"移除规则"功能。
### 日志检查
如果遇到问题,请检查:
1. WordPress调试日志
2. 服务器错误日志
3. .htaccess语法验证
## 开发者信息
### 文件结构
```
htaccess-manager/
├── htaccess-manager.php # 主插件文件
├── readme.md # 说明文档
└── (自动生成)
└── mu-plugins/
└── htaccess-helper.php # Must-use插件
```
### 钩子函数
插件提供以下WordPress钩子
- `htaccess_manager_before_update` - 规则更新前
- `htaccess_manager_after_update` - 规则更新后
### 自定义开发
如需扩展功能,可参考插件中的规则生成逻辑:
```php
$manager = new HTACCESS_Manager();
$rules = $manager->generate_htaccess_rules();
```
## 版本历史
### v1.0.2 (当前版本)
- 增强规则移除安全性
- 改进空行处理
- 优化MU插件逻辑
### v1.0.1
- 修复规则冲突问题
- 添加内容预览功能
- 改进用户界面
### v1.0.0
- 初始发布
- 基础重定向功能
- Must-use插件支持
## 技术支持
如果您遇到问题或需要帮助,请:
1. 查看本README文档
2. 检查WordPress错误日志
3. 联系服务器管理员确认Apache配置
## 许可证
GPL v2或更高版本
## 贡献
欢迎提交Issue和Pull Request来帮助改进这个插件。
---
**注意**:在使用本插件前,请务必备份您的.htaccess文件和数据库。

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();