文章目录[隐藏]
手把手教学:为WordPress集成智能化的网站死链检测与自动修复工具
引言:为什么WordPress网站需要死链检测与修复工具
在当今互联网时代,网站链接的完整性和可用性直接影响用户体验和搜索引擎排名。根据权威统计,一个拥有超过1000个页面的网站中,平均有5%-10%的链接可能失效或成为"死链"。对于使用WordPress构建的网站而言,随着内容不断积累和更新,死链问题会日益严重。
死链不仅导致访客无法访问目标内容,还会损害网站的专业形象,更重要的是,搜索引擎会将死链数量作为网站质量评估的指标之一,直接影响SEO排名。传统的手动检测方法耗时耗力,且难以实时发现新出现的死链问题。因此,为WordPress集成智能化的死链检测与自动修复工具显得尤为重要。
本文将详细介绍如何通过WordPress代码二次开发,实现一个功能完善的死链检测与自动修复系统,让您的网站始终保持链接健康状态。
第一章:准备工作与环境配置
1.1 开发环境要求
在开始开发之前,我们需要确保具备以下环境条件:
- WordPress 5.0及以上版本
- PHP 7.2及以上版本(推荐PHP 7.4+)
- MySQL 5.6及以上版本
- 服务器支持cURL扩展
- 适当的服务器内存和CPU资源(检测过程可能消耗资源)
1.2 创建插件基础结构
首先,我们需要创建一个独立的WordPress插件来承载我们的死链检测功能:
- 在WordPress的
wp-content/plugins/目录下创建新文件夹smart-link-checker - 在该文件夹中创建主插件文件
smart-link-checker.php - 添加插件基本信息:
<?php
/**
* Plugin Name: Smart Link Checker & Fixer
* Plugin URI: https://yourwebsite.com/smart-link-checker
* Description: 智能化的网站死链检测与自动修复工具
* Version: 1.0.0
* Author: Your Name
* License: GPL v2 or later
* Text Domain: smart-link-checker
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('SLC_VERSION', '1.0.0');
define('SLC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('SLC_PLUGIN_URL', plugin_dir_url(__FILE__));
define('SLC_MAX_LINKS_PER_RUN', 100); // 每次运行检测的最大链接数
define('SLC_REQUEST_TIMEOUT', 15); // 请求超时时间(秒)
1.3 创建数据库表结构
我们需要创建数据库表来存储链接检测结果和历史记录:
// 在插件激活时创建数据库表
register_activation_hook(__FILE__, 'slc_create_database_tables');
function slc_create_database_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'slc_links';
$history_table = $wpdb->prefix . 'slc_link_history';
// 主链接表
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
url varchar(1000) NOT NULL,
source_id bigint(20) NOT NULL,
source_type varchar(50) NOT NULL,
last_checked datetime DEFAULT NULL,
status_code int(4) DEFAULT NULL,
status varchar(50) DEFAULT 'pending',
redirect_to varchar(1000) DEFAULT NULL,
error_message text,
check_count int(11) DEFAULT 0,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY url_index (url(191)),
KEY status_index (status),
KEY source_index (source_type, source_id)
) $charset_collate;";
// 历史记录表
$sql2 = "CREATE TABLE IF NOT EXISTS $history_table (
id bigint(20) NOT NULL AUTO_INCREMENT,
link_id bigint(20) NOT NULL,
status_code int(4) DEFAULT NULL,
response_time float DEFAULT NULL,
checked_at datetime DEFAULT CURRENT_TIMESTAMP,
notes text,
PRIMARY KEY (id),
KEY link_id_index (link_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
dbDelta($sql2);
// 添加默认选项
add_option('slc_settings', array(
'check_frequency' => 'daily',
'auto_fix_redirects' => true,
'send_notifications' => true,
'notification_email' => get_option('admin_email'),
'excluded_domains' => array(),
'max_redirects' => 3
));
}
第二章:核心功能模块开发
2.1 链接提取器模块
首先,我们需要从WordPress内容中提取所有链接:
class SLC_Link_Extractor {
/**
* 从所有文章和页面中提取链接
*/
public static function extract_all_links() {
global $wpdb;
$links = array();
// 从文章内容中提取链接
$posts = $wpdb->get_results("SELECT ID, post_content FROM {$wpdb->posts} WHERE post_status = 'publish' AND post_type IN ('post', 'page')");
foreach ($posts as $post) {
$post_links = self::extract_from_content($post->post_content);
foreach ($post_links as $link) {
$links[] = array(
'url' => $link,
'source_id' => $post->ID,
'source_type' => 'post'
);
}
}
// 从评论中提取链接
$comments = $wpdb->get_results("SELECT comment_ID, comment_content FROM {$wpdb->comments} WHERE comment_approved = '1'");
foreach ($comments as $comment) {
$comment_links = self::extract_from_content($comment->comment_content);
foreach ($comment_links as $link) {
$links[] = array(
'url' => $link,
'source_id' => $comment->comment_ID,
'source_type' => 'comment'
);
}
}
return $links;
}
/**
* 从文本内容中提取URL
*/
private static function extract_from_content($content) {
$pattern = '/https?://[^s'"<>]+/i';
preg_match_all($pattern, $content, $matches);
$urls = array();
if (!empty($matches[0])) {
foreach ($matches[0] as $url) {
// 清理URL
$url = rtrim($url, '.,;:!?');
$url = html_entity_decode($url);
// 排除站内链接(可选)
if (!self::is_internal_link($url)) {
$urls[] = $url;
}
}
}
return array_unique($urls);
}
/**
* 判断是否为站内链接
*/
private static function is_internal_link($url) {
$site_url = site_url();
$parsed_url = parse_url($url);
$parsed_site = parse_url($site_url);
if (!isset($parsed_url['host'])) {
return false;
}
return $parsed_url['host'] === $parsed_site['host'];
}
}
2.2 链接检测器模块
接下来,我们创建链接检测的核心功能:
class SLC_Link_Checker {
private $timeout;
private $user_agent;
public function __construct() {
$this->timeout = SLC_REQUEST_TIMEOUT;
$this->user_agent = 'Mozilla/5.0 (compatible; SmartLinkChecker/1.0; +' . site_url() . ')';
}
/**
* 检测单个链接的状态
*/
public function check_single_link($url) {
$start_time = microtime(true);
// 初始化cURL
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => $this->user_agent,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true, // 只获取头部信息,提高速度
));
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$total_time = curl_getinfo($ch, CURLINFO_TOTAL_TIME);
$effective_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
$redirect_count = curl_getinfo($ch, CURLINFO_REDIRECT_COUNT);
$error = curl_error($ch);
curl_close($ch);
$response_time = microtime(true) - $start_time;
// 分析结果
$result = array(
'url' => $url,
'http_code' => $http_code,
'response_time' => $response_time,
'effective_url' => $effective_url,
'redirect_count' => $redirect_count,
'error' => $error,
'status' => $this->determine_status($http_code, $error)
);
return $result;
}
/**
* 根据HTTP状态码确定链接状态
*/
private function determine_status($http_code, $error) {
if (!empty($error)) {
return 'error';
}
if ($http_code >= 200 && $http_code < 300) {
return 'working';
} elseif ($http_code >= 300 && $http_code < 400) {
return 'redirect';
} elseif ($http_code == 404) {
return 'broken';
} elseif ($http_code >= 400 && $http_code < 500) {
return 'client_error';
} elseif ($http_code >= 500) {
return 'server_error';
} else {
return 'unknown';
}
}
/**
* 批量检测链接
*/
public function check_batch_links($links, $batch_size = 10) {
$results = array();
$batches = array_chunk($links, $batch_size);
foreach ($batches as $batch) {
$batch_results = $this->check_batch_concurrently($batch);
$results = array_merge($results, $batch_results);
// 避免对目标服务器造成过大压力
sleep(1);
}
return $results;
}
/**
* 并发检测(使用curl_multi)
*/
private function check_batch_concurrently($links) {
$mh = curl_multi_init();
$channels = array();
foreach ($links as $i => $link) {
$ch = curl_init();
curl_setopt_array($ch, array(
CURLOPT_URL => $link['url'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_TIMEOUT => $this->timeout,
CURLOPT_CONNECTTIMEOUT => 5,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_USERAGENT => $this->user_agent,
CURLOPT_HEADER => true,
CURLOPT_NOBODY => true,
));
curl_multi_add_handle($mh, $ch);
$channels[$i] = array(
'channel' => $ch,
'link' => $link
);
}
// 执行并发请求
$active = null;
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
// 收集结果
$results = array();
foreach ($channels as $i => $channel) {
$ch = $channel['channel'];
$link = $channel['link'];
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$effective_url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
$error = curl_error($ch);
$results[] = array_merge($link, array(
'http_code' => $http_code,
'effective_url' => $effective_url,
'error' => $error,
'status' => $this->determine_status($http_code, $error)
));
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
return $results;
}
}
2.3 智能修复模块
检测到死链后,我们需要尝试自动修复:
class SLC_Link_Fixer {
/**
* 尝试自动修复死链
*/
public static function try_auto_fix($link_data) {
$url = $link_data['url'];
$source_id = $link_data['source_id'];
$source_type = $link_data['source_type'];
// 检查是否为404错误
if ($link_data['http_code'] != 404) {
return false;
}
// 尝试常见的修复策略
$fixed_url = self::try_fix_strategies($url);
if ($fixed_url && $fixed_url !== $url) {
// 验证修复后的链接是否有效
$checker = new SLC_Link_Checker();
$check_result = $checker->check_single_link($fixed_url);
if ($check_result['status'] === 'working') {
// 更新数据库中的链接
self::update_link_in_database($link_data['id'], $fixed_url, $check_result['http_code']);
// 更新内容中的链接
self::update_link_in_content($source_id, $source_type, $url, $fixed_url);
return array(
'original_url' => $url,
'fixed_url' => $fixed_url,
'http_code' => $check_result['http_code'],
'success' => true
);
}
}
return false;
}
/**
* 尝试多种修复策略
*/
private static function try_fix_strategies($url) {
$strategies = array(
'remove_www' => function($url) {
return preg_replace('/^(https?://)www./i', '$1', $url);
},
'add_www' => function($url) {
$parsed = parse_url($url);
if (!preg_match('/^www./i', $parsed['host'])) {
$parsed['host'] = 'www.' . $parsed['host'];
return self::build_url($parsed);
}
return $url;
},
'force_https' => function($url) {
return preg_replace('/^http:/i', 'https:', $url);
},
'force_http' => function($url) {
return preg_replace('/^https:/i', 'http:', $url);
},
'remove_trailing_slash' => function($url) {
return rtrim($url, '/');
},
'add_trailing_slash' => function($url) {
$parsed = parse_url($url);
if (!isset($parsed['path']) || substr($parsed['path'], -1) !== '/') {
$parsed['path'] = ($parsed['path'] ?? '') . '/';
return self::build_url($parsed);
}
return $url;
}
);
foreach ($strategies as $strategy) {
$fixed_url = $strategy($url);
if ($fixed_url !== $url) {
// 简单验证URL格式
if (filter_var($fixed_url, FILTER_VALIDATE_URL)) {
return $fixed_url;
}
}
}
return false;
}
/**
* 从解析的URL部分重建完整URL
*/
private static function build_url($parts) {
$scheme = isset($parts['scheme']) ? $parts['scheme'] . '://' : '';
$host = $parts['host'] ?? '';
$port = isset($parts['port']) ? ':' . $parts['port'] : '';
$path = $parts['path'] ?? '';
$query = isset($parts['query']) ? '?' . $parts['query'] : '';
$fragment = isset($parts['fragment']) ? '#' . $parts['fragment'] : '';
return $scheme . $host . $port . $path . $query . $fragment;
}
/**
* 更新数据库中的链接记录
*/
private static function update_link_in_database($link_id, $new_url, $http_code) {
global $wpdb;
$table_name = $wpdb->prefix . 'slc_links';
$wpdb->update(
$table_name,
array(
'url' => $new_url,
'status_code' => $http_code,
'status' => 'fixed',
'last_checked' => current_time('mysql')
),
array('id' => $link_id),
array('%s', '%d', '%s', '%s'),
array('%d')
);
// 记录修复历史
$history_table = $wpdb->prefix . 'slc_link_history';
$wpdb->insert(
$history_table,
array(
'link_id' => $link_id,
'status_code' => $http_code,
'checked_at' => current_time('mysql'),
'notes' => '自动修复: ' . $new_url
)
);
}
/**
* 更新内容中的链接
*/
private static function update_link_in_content($source_id, $source_type, $old_url, $new_url) {
if ($source_type === 'post') {
$post = get_post($source_id);
if ($post) {
$new_content = str_replace($old_url, $new_url, $post->post_content);
wp_update_post(array(
'ID' => $source_id,
'post_content' => $new_content
));
}
} elseif ($source_type === 'comment') {
$comment = get_comment($source_id);
if ($comment) {
$new_content = str_replace($old_url, $new_url, $comment->comment_content);
wp_update_comment(array(
'comment_ID' => $source_id,
'comment_content' => $new_content
));
}
}
}
/**
* 查找可能的替代链接(通过搜索引擎API)
*/
public static function find_alternative_url($url, $title = '') {
// 这里可以集成搜索引擎API来查找替代链接
// 由于API需要密钥,这里仅提供框架代码
$parsed = parse_url($url);
$domain = $parsed['host'] ?? '';
$path = $parsed['path'] ?? '';
// 尝试通过Wayback Machine查找存档
$wayback_url = "https://archive.org/wayback/available?url=" . urlencode($url);
$response = wp_remote_get($wayback_url, array('timeout' => 10));
if (!is_wp_error($response)) {
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['archived_snapshots']['closest']['url'])) {
return $data['archived_snapshots']['closest']['url'];
}
}
return false;
}
}
## 第三章:后台管理与用户界面
### 3.1 创建管理菜单和页面
class SLC_Admin {
public function __construct() {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
add_action('wp_ajax_slc_start_scan', array($this, 'ajax_start_scan'));
add_action('wp_ajax_slc_get_stats', array($this, 'ajax_get_stats'));
add_action('wp_ajax_slc_fix_link', array($this, 'ajax_fix_link'));
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
add_menu_page(
'智能死链检测',
'死链检测',
'manage_options',
'smart-link-checker',
array($this, 'render_main_page'),
'dashicons-admin-links',
30
);
add_submenu_page(
'smart-link-checker',
'检测设置',
'设置',
'manage_options',
'slc-settings',
array($this, 'render_settings_page')
);
add_submenu_page(
'smart-link-checker',
'检测报告',
'报告',
'manage_options',
'slc-reports',
array($this, 'render_reports_page')
);
}
/**
* 渲染主页面
*/
public function render_main_page() {
?>
<div class="wrap slc-admin">
<h1>智能死链检测与修复</h1>
<div class="slc-dashboard">
<div class="slc-stats-cards">
<div class="card">
<h3>总链接数</h3>
<p class="stat-number" id="total-links">0</p>
</div>
<div class="card">
<h3>正常链接</h3>
<p class="stat-number" id="working-links">0</p>
</div>
<div class="card">
<h3>死链数</h3>
<p class="stat-number" id="broken-links">0</p>
</div>
<div class="card">
<h3>已修复</h3>
<p class="stat-number" id="fixed-links">0</p>
</div>
</div>
<div class="slc-controls">
<button id="start-scan" class="button button-primary button-large">
<span class="dashicons dashicons-search"></span>
开始检测
</button>
<button id="stop-scan" class="button button-secondary button-large" disabled>
<span class="dashicons dashicons-controls-pause"></span>
停止检测
</button>
<button id="export-report" class="button button-secondary">
<span class="dashicons dashicons-download"></span>
导出报告
</button>
</div>
<div class="slc-progress-container" style="display: none;">
<h3>检测进度</h3>
<div class="progress-bar">
<div class="progress-fill" id="scan-progress" style="width: 0%"></div>
</div>
<p class="progress-text" id="progress-text">准备开始...</p>
</div>
<div class="slc-results">
<h2>检测结果</h2>
<div class="tablenav top">
<div class="alignleft actions">
<select id="status-filter">
<option value="all">所有状态</option>
<option value="broken">死链</option>
<option value="working">正常</option>
<option value="redirect">重定向</option>
<option value="error">错误</option>
</select>
<button id="apply-filter" class="button">筛选</button>
</div>
<div class="tablenav-pages">
<span class="displaying-num" id="displaying-num">0个项目</span>
<div class="pagination">
<button class="button" id="prev-page" disabled>上一页</button>
<span id="current-page">1</span> / <span id="total-pages">1</span>
<button class="button" id="next-page" disabled>下一页</button>
</div>
</div>
</div>
<table class="wp-list-table widefat fixed striped" id="links-table">
<thead>
<tr>
<th width="5%">ID</th>
<th width="30%">URL</th>
<th width="10%">状态码</th>
<th width="15%">状态</th>
<th width="20%">来源</th>
<th width="15%">最后检测</th>
<th width="15%">操作</th>
</tr>
</thead>
<tbody id="links-tbody">
<!-- 动态加载数据 -->
</tbody>
</table>
</div>
</div>
</div>
<?php
}
/**
* 渲染设置页面
*/
public function render_settings_page() {
$settings = get_option('slc_settings', array());
?>
<div class="wrap">
<h1>死链检测设置</h1>
<form method="post" action="options.php">
<?php settings_fields('slc_settings_group'); ?>
<table class="form-table">
<tr>
<th scope="row">检测频率</th>
<td>
<select name="slc_settings[check_frequency]">
<option value="hourly" <?php selected($settings['check_frequency'], 'hourly'); ?>>每小时</option>
<option value="twicedaily" <?php selected($settings['check_frequency'], 'twicedaily'); ?>>每天两次</option>
<option value="daily" <?php selected($settings['check_frequency'], 'daily'); ?>>每天</option>
<option value="weekly" <?php selected($settings['check_frequency'], 'weekly'); ?>>每周</option>
<option value="monthly" <?php selected($settings['check_frequency'], 'monthly'); ?>>每月</option>
</select>
<p class="description">自动检测死链的频率</p>
</td>
</tr>
<tr>
<th scope="row">自动修复</th>
<td>
<label>
<input type="checkbox" name="slc_settings[auto_fix_redirects]" value="1"
<?php checked($settings['auto_fix_redirects'], true); ?>>
自动尝试修复重定向和简单错误
</label>
</td>
</tr>
<tr>
<th scope="row">邮件通知</th>
<td>
<label>
<input type="checkbox" name="slc_settings[send_notifications]" value="1"
<?php checked($settings['send_notifications'], true); ?>>
检测到死链时发送邮件通知
</label>
<p class="description">
<input type="email" name="slc_settings[notification_email]"
value="<?php echo esc_attr($settings['notification_email']); ?>"
placeholder="通知邮箱地址">
</p>
</td>
</tr>
<tr>
<th scope="row">排除域名</th>
<td>
<textarea name="slc_settings[excluded_domains]" rows="5" cols="50"
placeholder="每行一个域名,例如:example.com"><?php
echo esc_textarea(implode("n", $settings['excluded_domains'] ?? array()));
?></textarea>
<p class="description">不检测这些域名的链接</p>
</td>
</tr>
<tr>
<th scope="row">最大重定向次数</th>
<td>
<input type="number" name="slc_settings[max_redirects]"
value="<?php echo esc_attr($settings['max_redirects']); ?>" min="1" max="10">
<p class="description">检测时允许的最大重定向次数</p>
</td>
</tr>
<tr>
<th scope="row">请求超时时间</th>
<td>
<input type="number" name="slc_settings[request_timeout]"
value="<?php echo esc_attr($settings['request_timeout'] ?? SLC_REQUEST_TIMEOUT); ?>" min="5" max="60">
<span>秒</span>
<p class="description">检测单个链接的最大等待时间</p>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
/**
* 注册设置
*/
public function register_settings() {
register_setting('slc_settings_group', 'slc_settings', array(
'sanitize_callback' => array($this, 'sanitize_settings')
));
}
/**
* 清理设置数据
*/
public function sanitize_settings($input) {
$sanitized = array();
$sanitized['check_frequency'] = sanitize_text_field($input['check_frequency']);
$sanitized['auto_fix_redirects'] = isset($input['auto_fix_redirects']);
$sanitized['send_notifications'] = isset($input['send_notifications']);
$sanitized['notification_email'] = sanitize_email($input['notification_email']);
// 处理排除域名
$excluded_domains = explode("n", $input['excluded_domains']);
$sanitized['excluded_domains'] = array_map('trim', $excluded_domains);
$sanitized['excluded_domains'] = array_filter($sanitized['excluded_domains']);
$sanitized['max_redirects'] = absint($input['max_redirects']);
if ($sanitized['max_redirects'] < 1 || $sanitized['max_redirects'] > 10) {
$sanitized['max_redirects'] = 3;
}
$sanitized['request_timeout'] = absint($input['request_timeout']);
if ($sanitized['request_timeout'] < 5 || $sanitized['request_timeout'] > 60) {
$sanitized['request_timeout'] = SLC_REQUEST_TIMEOUT;
}
return $sanitized;
}
}
### 3.2 AJAX处理与前端交互
// 继续SLC_Admin类中的方法
/**
* AJAX开始扫描
*/
public function ajax_start_scan() {
check_ajax_referer('slc_ajax_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_die('权限不足');
}
// 获取所有链接
$links = SLC_Link_Extractor::extract_all_links();
// 保存到数据库
$this->save_links_to_db($links);
// 开始后台处理
wp_schedule_single_event(time() + 1, 'slc_process_batch');
wp_send_json_success(array(
'message' => '扫描已开始',
'total_links' => count($links)
));
}
/**
* 保存链接到数据库
*/
private function save_links_to_db($links) {
global $wpdb;
$table_name = $wpdb->prefix . 'slc_links';
// 清空旧数据
$wpdb->query("TRUNCATE TABLE $table_name");
// 批量插入新数据
$values = array();
$placeholders = array();
foreach ($links as $link) {
array_push(
$values,
$link['url'],
$link['source_id'],
$link['source_type']
);
$placeholders[] = "(%s, %d, %s, 'pending')";
}
if (!empty($values)) {
$query = "INSERT INTO $table_name (url, source_id, source_type, status) VALUES ";
$query .= implode(', ', $placeholders);
$wpdb->query($wpdb->prepare($query, $values));
}
}
/**
* AJAX获取统计信息
*/
public function ajax_get_stats() {
global $wpdb;
$table_name = $wpdb->prefix . 'slc_links';
$stats = array(
'total' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name"),
'working' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'working'"),
'broken' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'broken'"),
'fixed' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'fixed'"),
'pending' => $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE status = 'pending'")
);
wp_send_json_success($stats);
}
/**
* AJAX修复链接
*/
public function ajax_fix_link() {
check_ajax_referer('slc_ajax_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_die('权限不足');
}
$link_id = intval($_POST['link_id']);
$fix_type = sanitize_text_field($_POST['fix_type']);
global $wpdb;
$table_name = $wpdb->prefix . 'slc_links';
$link = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE id = %d", $link_id
));
if (!$link) {
wp_send_json_error('链接不存在');
}
$result = false;
switch ($fix_type) {
case 'auto':
$result = SLC_Link_Fixer::try_auto_fix((array)$link);
break;
case 'manual':
$new_url = sanitize_text_field($_POST['new_url']);
if ($new_url && filter_var($new_url, FILTER_VALIDATE_URL)) {
$result = SLC_Link_Fixer::update_link_in_content(
$link->source_id,
$link->source_type,
$link->url,
$new_url
);
}
break;
case 'remove':
// 从内容中移除链接
$result = SLC_Link_Fixer::update_link_in_content(
$link->source_id,
$link->source_type,
$link->url,
''
);
break;
}
if ($result) {
wp_send_json_success(array(
'message' => '修复成功',
'link_id' => $link_id
));
} else {
wp_send_json_error('修复失败');
}
}
/**
* 加载管理脚本和样式
*/
public function enqueue_admin_scripts($hook) {
