文章目录[隐藏]
手把手教程:为WordPress实现基于地理围栏的网站内容区域化展示工具
引言:地理围栏技术与网站内容区域化的价值
在当今全球化的互联网环境中,网站访问者可能来自世界各地。然而,并非所有内容都适合向所有地区的用户展示。基于地理围栏的内容区域化技术能够根据用户的地理位置,智能展示相关性强、合规性高的内容,这已成为现代网站开发的重要趋势。
地理围栏(Geo-fencing)是一种基于位置的服务技术,通过GPS、RFID、Wi-Fi或蜂窝数据等定位方式,在现实世界中创建一个虚拟的边界。当设备进入或离开这个边界时,可以触发预设的操作或通知。在网站开发中,这一技术可以用于根据用户的地理位置展示不同的内容、广告或功能。
对于WordPress网站而言,实现基于地理围栏的内容区域化展示具有多重价值:
- 提升用户体验:向用户展示与其地理位置相关的内容,如本地新闻、活动、产品等
- 合规性管理:根据不同地区的法律法规展示合规内容
- 营销精准化:针对不同地区实施差异化的营销策略
- 内容优化:根据地区特点优化内容展示,提高转化率
本教程将详细介绍如何通过WordPress代码二次开发,实现一个基于地理围栏的网站内容区域化展示工具,让您的网站具备智能化的区域内容展示能力。
第一部分:准备工作与环境配置
1.1 理解WordPress开发基础
在开始开发之前,我们需要确保具备以下基础知识:
- WordPress主题和插件的基本结构
- PHP编程基础
- JavaScript/jQuery基础
- WordPress钩子(Hooks)和过滤器(Filters)的使用
- 基本的HTML/CSS知识
1.2 开发环境搭建
- 本地开发环境:建议使用XAMPP、MAMP或Local by Flywheel等工具搭建本地WordPress开发环境
- 代码编辑器:推荐使用VS Code、PHPStorm或Sublime Text
- 版本控制:使用Git进行代码版本管理
- 测试工具:浏览器开发者工具、Postman等API测试工具
1.3 创建自定义插件
我们将创建一个独立的WordPress插件来实现地理围栏功能,这样可以确保功能的独立性和可移植性。
在WordPress的wp-content/plugins/目录下创建新文件夹geo-fencing-content,并在其中创建主插件文件:
<?php
/**
* Plugin Name: 地理围栏内容区域化工具
* Plugin URI: https://yourwebsite.com/
* Description: 基于用户地理位置实现网站内容区域化展示的WordPress插件
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
* Text Domain: geo-fencing-content
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('GFC_VERSION', '1.0.0');
define('GFC_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('GFC_PLUGIN_URL', plugin_dir_url(__FILE__));
// 初始化插件
require_once GFC_PLUGIN_DIR . 'includes/class-geo-fencing-core.php';
function gfc_init() {
$plugin = new Geo_Fencing_Core();
$plugin->run();
}
add_action('plugins_loaded', 'gfc_init');
第二部分:地理定位技术与实现方案
2.1 地理位置获取方案比较
实现地理围栏功能的第一步是获取用户的准确地理位置。以下是几种常用的技术方案:
- HTML5 Geolocation API:浏览器原生支持,精度较高,但需要用户授权
- IP地址定位:通过用户IP地址推断地理位置,无需用户授权但精度有限
- 第三方定位服务:如MaxMind、IPinfo等专业服务,精度和可靠性较高
2.2 实现IP地址定位功能
我们将首先实现基于IP地址的地理定位功能,作为基础方案。创建includes/class-geo-location.php文件:
<?php
class Geo_Location {
private $api_key;
private $cache_time;
public function __construct() {
// 可以从插件设置中获取API密钥
$this->api_key = get_option('gfc_ipapi_key', '');
$this->cache_time = 24 * 60 * 60; // 缓存24小时
}
/**
* 获取用户IP地址
*/
public function get_user_ip() {
$ip_keys = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR');
foreach ($ip_keys as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (explode(',', $_SERVER[$key]) as $ip) {
$ip = trim($ip);
// 验证IP地址格式
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
return $ip;
}
}
}
}
return $_SERVER['REMOTE_ADDR'];
}
/**
* 通过IP地址获取地理位置信息
*/
public function get_location_by_ip($ip = '') {
if (empty($ip)) {
$ip = $this->get_user_ip();
}
// 检查缓存
$cache_key = 'gfc_location_' . md5($ip);
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
// 使用ip-api.com免费服务(限制:45次/分钟)
$url = "http://ip-api.com/json/{$ip}";
if (!empty($this->api_key)) {
// 如果有付费API密钥,可以使用更专业的服务
// $url = "https://api.ipgeolocation.io/ipgeo?apiKey={$this->api_key}&ip={$ip}";
}
$response = wp_remote_get($url, array('timeout' => 5));
if (is_wp_error($response)) {
// 如果API请求失败,使用备用方法
return $this->get_fallback_location();
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
// 标准化返回数据
$location_data = array(
'country' => isset($data['country']) ? $data['country'] : '',
'country_code' => isset($data['countryCode']) ? $data['countryCode'] : '',
'region' => isset($data['region']) ? $data['region'] : '',
'region_name' => isset($data['regionName']) ? $data['regionName'] : '',
'city' => isset($data['city']) ? $data['city'] : '',
'zip' => isset($data['zip']) ? $data['zip'] : '',
'lat' => isset($data['lat']) ? $data['lat'] : 0,
'lon' => isset($data['lon']) ? $data['lon'] : 0,
'timezone' => isset($data['timezone']) ? $data['timezone'] : '',
'isp' => isset($data['isp']) ? $data['isp'] : '',
'ip' => $ip
);
// 缓存结果
set_transient($cache_key, $location_data, $this->cache_time);
return $location_data;
}
/**
* 备用定位方法
*/
private function get_fallback_location() {
// 这里可以添加备用定位逻辑
// 例如使用其他免费API或默认位置
return array(
'country' => '未知',
'country_code' => 'UN',
'region' => '',
'city' => '',
'lat' => 0,
'lon' => 0,
'ip' => $this->get_user_ip()
);
}
/**
* 计算两点之间的距离(用于地理围栏判断)
*/
public function calculate_distance($lat1, $lon1, $lat2, $lon2, $unit = 'km') {
$theta = $lon1 - $lon2;
$dist = sin(deg2rad($lat1)) * sin(deg2rad($lat2)) +
cos(deg2rad($lat1)) * cos(deg2rad($lat2)) * cos(deg2rad($theta));
$dist = acos($dist);
$dist = rad2deg($dist);
$miles = $dist * 60 * 1.1515;
switch ($unit) {
case 'km':
return $miles * 1.609344;
case 'm':
return $miles * 1.609344 * 1000;
case 'mile':
default:
return $miles;
}
}
}
第三部分:地理围栏系统设计与实现
3.1 地理围栏数据结构设计
我们需要设计一个灵活的地理围栏系统,支持多种形状的围栏(圆形、多边形等)。首先在数据库中创建存储地理围栏的表。
创建数据库表的代码可以放在插件激活钩子中。在class-geo-fencing-core.php中添加:
class Geo_Fencing_Core {
public function __construct() {
// 构造函数
}
public function run() {
// 注册激活钩子
register_activation_hook(__FILE__, array($this, 'activate_plugin'));
// 注册其他钩子和过滤器
$this->register_hooks();
}
public function activate_plugin() {
// 创建数据库表
$this->create_database_tables();
// 设置默认选项
$this->set_default_options();
}
private function create_database_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'geo_fences';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
fence_type varchar(20) NOT NULL DEFAULT 'circle',
coordinates text NOT NULL,
radius float DEFAULT 0,
content_rule text,
priority int(11) DEFAULT 0,
is_active tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY is_active (is_active),
KEY priority (priority)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
// 创建内容关联表
$relation_table = $wpdb->prefix . 'geo_fence_content';
$sql2 = "CREATE TABLE IF NOT EXISTS $relation_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
fence_id mediumint(9) NOT NULL,
content_type varchar(50) NOT NULL,
content_id bigint(20) NOT NULL,
action_type varchar(50) NOT NULL DEFAULT 'show',
conditions text,
PRIMARY KEY (id),
KEY fence_id (fence_id),
KEY content_type (content_type, content_id)
) $charset_collate;";
dbDelta($sql2);
}
}
3.2 地理围栏管理界面
我们需要创建一个管理界面,让网站管理员可以添加、编辑和删除地理围栏。创建admin/class-geo-fencing-admin.php:
class Geo_Fencing_Admin {
private $plugin_name;
private $version;
public function __construct($plugin_name, $version) {
$this->plugin_name = $plugin_name;
$this->version = $version;
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}
public function add_admin_menu() {
add_menu_page(
'地理围栏管理',
'地理围栏',
'manage_options',
'geo-fencing',
array($this, 'display_admin_page'),
'dashicons-location-alt',
30
);
add_submenu_page(
'geo-fencing',
'地理围栏列表',
'围栏列表',
'manage_options',
'geo-fencing',
array($this, 'display_admin_page')
);
add_submenu_page(
'geo-fencing',
'添加新围栏',
'添加围栏',
'manage_options',
'geo-fencing-add',
array($this, 'display_add_fence_page')
);
add_submenu_page(
'geo-fencing',
'地理围栏设置',
'设置',
'manage_options',
'geo-fencing-settings',
array($this, 'display_settings_page')
);
}
public function display_admin_page() {
include GFC_PLUGIN_DIR . 'admin/views/fence-list.php';
}
public function display_add_fence_page() {
include GFC_PLUGIN_DIR . 'admin/views/fence-edit.php';
}
public function display_settings_page() {
include GFC_PLUGIN_DIR . 'admin/views/settings.php';
}
public function enqueue_admin_scripts($hook) {
if (strpos($hook, 'geo-fencing') === false) {
return;
}
// 加载Leaflet地图库
wp_enqueue_style('leaflet-css', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.css');
wp_enqueue_script('leaflet-js', 'https://unpkg.com/leaflet@1.7.1/dist/leaflet.js', array(), '1.7.1', true);
// 加载插件自定义脚本
wp_enqueue_script(
$this->plugin_name . '-admin',
GFC_PLUGIN_URL . 'admin/js/admin.js',
array('jquery', 'leaflet-js'),
$this->version,
true
);
wp_enqueue_style(
$this->plugin_name . '-admin',
GFC_PLUGIN_URL . 'admin/css/admin.css',
array(),
$this->version
);
// 传递数据到JavaScript
wp_localize_script($this->plugin_name . '-admin', 'gfc_admin_data', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('gfc_admin_nonce'),
'default_lat' => get_option('gfc_default_lat', 39.9042),
'default_lng' => get_option('gfc_default_lng', 116.4074)
));
}
}
3.3 地理围栏编辑器实现
创建admin/views/fence-edit.php文件,实现围栏编辑界面:
<div class="wrap">
<h1><?php echo isset($_GET['action']) && $_GET['action'] == 'edit' ? '编辑地理围栏' : '添加新地理围栏'; ?></h1>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>" id="gfc-fence-form">
<input type="hidden" name="action" value="gfc_save_fence">
<?php wp_nonce_field('gfc_save_fence_action', 'gfc_fence_nonce'); ?>
<?php if (isset($_GET['id'])): ?>
<input type="hidden" name="fence_id" value="<?php echo intval($_GET['id']); ?>">
<?php endif; ?>
<table class="form-table">
<tr>
<th scope="row"><label for="fence_name">围栏名称</label></th>
<td>
<input type="text" name="fence_name" id="fence_name" class="regular-text" required>
<p class="description">用于识别此地理围栏的名称</p>
</td>
</tr>
<tr>
<th scope="row"><label for="fence_type">围栏类型</label></th>
<td>
<select name="fence_type" id="fence_type">
<option value="circle">圆形围栏</option>
<option value="polygon">多边形围栏</option>
<option value="rectangle">矩形围栏</option>
</select>
</td>
</tr>
<tr id="circle_fields">
<th scope="row"><label for="center_point">中心点坐标</label></th>
<td>
<div class="coordinates-input">
<input type="text" name="center_lat" id="center_lat" placeholder="纬度" class="small-text">
<input type="text" name="center_lng" id="center_lng" placeholder="经度" class="small-text">
<button type="button" class="button" id="get_current_location">获取当前位置</button>
</div>
<p class="description">格式:纬度,经度(例如:39.9042,116.4074)</p>
</td>
</tr>
<tr id="circle_radius_field">
<th scope="row"><label for="radius">半径</label></th>
<td>
<input type="number" name="radius" id="radius" min="0.1" step="0.1" class="small-text">
<select name="radius_unit">
<option value="km">公里</option>
<option value="m">米</option>
<option value="mile">英里</option>
</select>
</td>
</tr>
<tr id="polygon_fields" style="display:none;">
<tr id="polygon_fields" style="display:none;">
<th scope="row"><label for="polygon_coords">多边形坐标</label></th>
<td>
<textarea name="polygon_coords" id="polygon_coords" rows="5" cols="50" placeholder="格式:纬度,经度 例如: 39.9042,116.4074 39.9142,116.4174 39.8942,116.4274"></textarea>
<p class="description">每行输入一个坐标点,格式:纬度,经度</p>
</td>
</tr>
<tr>
<th scope="row"><label for="fence_priority">优先级</label></th>
<td>
<input type="number" name="fence_priority" id="fence_priority" value="0" min="0" class="small-text">
<p class="description">数值越大优先级越高,当用户位于多个围栏重叠区域时生效</p>
</td>
</tr>
<tr>
<th scope="row"><label for="is_active">状态</label></th>
<td>
<label>
<input type="checkbox" name="is_active" id="is_active" value="1" checked> 启用此围栏
</label>
</td>
</tr>
</table>
<div class="map-container">
<h3>地图预览</h3>
<div id="fence_map" style="height: 400px; width: 100%;"></div>
<p class="description">在地图上点击可以设置围栏中心点或添加多边形顶点</p>
</div>
<h2>内容规则设置</h2>
<div id="content_rules">
<div class="content-rule">
<h4>规则 #1</h4>
<table class="form-table">
<tr>
<th scope="row"><label>内容类型</label></th>
<td>
<select name="content_type[]" class="content-type-select">
<option value="post">文章</option>
<option value="page">页面</option>
<option value="category">分类目录</option>
<option value="custom">自定义内容</option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label>内容选择</label></th>
<td>
<select name="content_id[]" class="content-id-select" style="width: 300px;">
<option value="">请选择内容</option>
</select>
</td>
</tr>
<tr>
<th scope="row"><label>操作类型</label></th>
<td>
<select name="action_type[]">
<option value="show">显示内容</option>
<option value="hide">隐藏内容</option>
<option value="replace">替换内容</option>
<option value="redirect">重定向</option>
</select>
</td>
</tr>
<tr class="replace-content" style="display:none;">
<th scope="row"><label>替换内容</label></th>
<td>
<textarea name="replace_content[]" rows="3" cols="50" placeholder="输入替换内容或短代码"></textarea>
</td>
</tr>
<tr class="redirect-url" style="display:none;">
<th scope="row"><label>重定向URL</label></th>
<td>
<input type="url" name="redirect_url[]" class="regular-text" placeholder="https://">
</td>
</tr>
</table>
<hr>
</div>
</div>
<button type="button" class="button" id="add_content_rule">添加新规则</button>
<p class="submit">
<input type="submit" name="submit" id="submit" class="button button-primary" value="保存围栏">
</p>
</form>
</div>
<script>
jQuery(document).ready(function($) {
// 初始化地图
var map = L.map('fence_map').setView([39.9042, 116.4074], 10);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
// 地图点击事件
var marker;
var polygonPoints = [];
var polygonLayer;
map.on('click', function(e) {
var lat = e.latlng.lat;
var lng = e.latlng.lng;
$('#center_lat').val(lat.toFixed(6));
$('#center_lng').val(lng.toFixed(6));
// 更新地图标记
if (marker) {
map.removeLayer(marker);
}
marker = L.marker([lat, lng]).addTo(map);
// 如果是多边形模式,添加顶点
if ($('#fence_type').val() === 'polygon') {
polygonPoints.push([lat, lng]);
updatePolygonPreview();
}
});
// 围栏类型切换
$('#fence_type').on('change', function() {
var type = $(this).val();
if (type === 'circle') {
$('#circle_fields, #circle_radius_field').show();
$('#polygon_fields').hide();
} else if (type === 'polygon') {
$('#circle_fields, #circle_radius_field').hide();
$('#polygon_fields').show();
polygonPoints = [];
if (polygonLayer) {
map.removeLayer(polygonLayer);
}
}
});
// 更新多边形预览
function updatePolygonPreview() {
if (polygonLayer) {
map.removeLayer(polygonLayer);
}
if (polygonPoints.length >= 3) {
polygonLayer = L.polygon(polygonPoints, {color: 'blue'}).addTo(map);
map.fitBounds(polygonLayer.getBounds());
// 更新坐标文本
var coordsText = polygonPoints.map(function(point) {
return point[0].toFixed(6) + ',' + point[1].toFixed(6);
}).join('n');
$('#polygon_coords').val(coordsText);
}
}
// 获取当前位置
$('#get_current_location').on('click', function() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
var lat = position.coords.latitude;
var lng = position.coords.longitude;
$('#center_lat').val(lat.toFixed(6));
$('#center_lng').val(lng.toFixed(6));
// 更新地图
if (marker) {
map.removeLayer(marker);
}
marker = L.marker([lat, lng]).addTo(map);
map.setView([lat, lng], 13);
});
} else {
alert('您的浏览器不支持地理位置功能');
}
});
// 操作类型切换
$(document).on('change', 'select[name="action_type[]"]', function() {
var actionType = $(this).val();
var ruleDiv = $(this).closest('.content-rule');
ruleDiv.find('.replace-content, .redirect-url').hide();
if (actionType === 'replace') {
ruleDiv.find('.replace-content').show();
} else if (actionType === 'redirect') {
ruleDiv.find('.redirect-url').show();
}
});
// 添加新规则
$('#add_content_rule').on('click', function() {
var ruleCount = $('#content_rules .content-rule').length + 1;
var newRule = $('#content_rules .content-rule:first').clone();
newRule.find('h4').text('规则 #' + ruleCount);
newRule.find('input, textarea, select').val('');
newRule.find('.replace-content, .redirect-url').hide();
$('#content_rules').append(newRule);
});
});
</script>
## 第四部分:核心功能实现与集成
### 4.1 地理围栏检测算法
创建`includes/class-geo-fence-detector.php`,实现围栏检测逻辑:
<?php
class Geo_Fence_Detector {
private $location_service;
public function __construct($location_service) {
$this->location_service = $location_service;
}
/**
* 检测用户是否在指定围栏内
*/
public function is_user_in_fence($fence_data, $user_location = null) {
if (empty($user_location)) {
$user_location = $this->location_service->get_location_by_ip();
}
if (empty($user_location['lat']) || empty($user_location['lon'])) {
return false;
}
$user_lat = floatval($user_location['lat']);
$user_lng = floatval($user_location['lon']);
switch ($fence_data['fence_type']) {
case 'circle':
return $this->check_circle_fence($fence_data, $user_lat, $user_lng);
case 'polygon':
return $this->check_polygon_fence($fence_data, $user_lat, $user_lng);
case 'rectangle':
return $this->check_rectangle_fence($fence_data, $user_lat, $user_lng);
default:
return false;
}
}
/**
* 检测圆形围栏
*/
private function check_circle_fence($fence_data, $user_lat, $user_lng) {
$center_coords = explode(',', $fence_data['coordinates']);
if (count($center_coords) !== 2) {
return false;
}
$center_lat = floatval(trim($center_coords[0]));
$center_lng = floatval(trim($center_coords[1]));
$radius = floatval($fence_data['radius']);
// 计算距离
$distance = $this->location_service->calculate_distance(
$center_lat, $center_lng,
$user_lat, $user_lng,
'km'
);
return $distance <= $radius;
}
/**
* 检测多边形围栏(使用射线法)
*/
private function check_polygon_fence($fence_data, $user_lat, $user_lng) {
$coordinates = $this->parse_polygon_coordinates($fence_data['coordinates']);
if (count($coordinates) < 3) {
return false;
}
$inside = false;
$n = count($coordinates);
for ($i = 0, $j = $n - 1; $i < $n; $j = $i++) {
$xi = $coordinates[$i][0];
$yi = $coordinates[$i][1];
$xj = $coordinates[$j][0];
$yj = $coordinates[$j][1];
$intersect = (($yi > $user_lng) != ($yj > $user_lng))
&& ($user_lat < ($xj - $xi) * ($user_lng - $yi) / ($yj - $yi) + $xi);
if ($intersect) {
$inside = !$inside;
}
}
return $inside;
}
/**
* 解析多边形坐标
*/
private function parse_polygon_coordinates($coords_string) {
$coordinates = array();
$lines = explode("n", $coords_string);
foreach ($lines as $line) {
$line = trim($line);
if (empty($line)) continue;
$parts = explode(',', $line);
if (count($parts) === 2) {
$lat = floatval(trim($parts[0]));
$lng = floatval(trim($parts[1]));
$coordinates[] = array($lat, $lng);
}
}
return $coordinates;
}
/**
* 检测矩形围栏
*/
private function check_rectangle_fence($fence_data, $user_lat, $user_lng) {
$bounds = explode('|', $fence_data['coordinates']);
if (count($bounds) !== 4) {
return false;
}
$min_lat = floatval($bounds[0]);
$max_lat = floatval($bounds[1]);
$min_lng = floatval($bounds[2]);
$max_lng = floatval($bounds[3]);
return ($user_lat >= $min_lat && $user_lat <= $max_lat &&
$user_lng >= $min_lng && $user_lng <= $max_lng);
}
/**
* 获取用户所在的所有围栏
*/
public function get_user_fences($user_location = null) {
global $wpdb;
$table_name = $wpdb->prefix . 'geo_fences';
// 获取所有启用的围栏
$fences = $wpdb->get_results(
"SELECT * FROM $table_name WHERE is_active = 1 ORDER BY priority DESC",
ARRAY_A
);
$user_fences = array();
foreach ($fences as $fence) {
if ($this->is_user_in_fence($fence, $user_location)) {
$user_fences[] = $fence;
}
}
return $user_fences;
}
/**
* 获取适用于用户的内容规则
*/
public function get_content_rules_for_user() {
$user_fences = $this->get_user_fences();
$all_rules = array();
foreach ($user_fences as $fence) {
$fence_rules = $this->get_fence_content_rules($fence['id']);
$all_rules = array_merge($all_rules, $fence_rules);
}
// 按优先级排序
usort($all_rules, function($a, $b) {
return $b['priority'] - $a['priority'];
});
return $all_rules;
}
/**
* 获取围栏的内容规则
*/
private function get_fence_content_rules($fence_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'geo_fence_content';
return $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM $table_name WHERE fence_id = %d",
$fence_id
),
ARRAY_A
);
}
}
### 4.2 WordPress内容过滤与替换
创建`includes/class-content-filter.php`,实现内容过滤功能:
<?php
class Geo_Content_Filter {
private $fence_detector;
private $user_rules;
public function __construct($fence_detector) {
$this->fence_detector = $fence_detector;
$this->user_rules = null;
}
public function init() {
// 只在非管理页面应用规则
if (!is_admin()) {
// 文章内容过滤
add_filter('the_content', array($this, 'filter_post_content'), 10, 1);
// 小工具过滤
add_filter('widget_display_callback', array($this, 'filter_widget'), 10, 3);
// 菜单过滤
add_filter('wp_nav_menu_objects', array($this, 'filter_menu_items'), 10, 2);
// 重定向处理
add_action('template_redirect', array($this, 'handle_redirects'));
// 短代码支持
add_shortcode('geo_content', array($this, 'geo_content_shortcode'));
}
}
/**
* 获取用户规则(懒加载)
*/
private function get_user_rules() {
if ($this->user_rules === null) {
$this->user_rules = $this->fence_detector->get_content_rules_for_user();
}
return $this->user_rules;
}
/**
* 过滤文章内容
*/
public function filter_post_content($content) {
global $post;
if (empty($post)) {
return $content;
}
$rules = $this->get_user_rules();
foreach ($rules as $rule) {
if ($this->rule_applies_to_content($rule, $post)) {
$content = $this->apply_rule_to_content($rule, $content, $post);
}
}
return $content;
}
/**
* 检查规则是否适用于当前内容
*/
private function rule_applies_to_content($rule, $post) {
switch ($rule['content_type']) {
case 'post':
return ($post->post_type == 'post' && $rule['content_id'] == $post->ID);
case 'page':
return ($post->post_type == 'page' && $rule['content_id'] == $post->ID);
case 'category':
return has_category($rule['content_id'], $post);
case 'custom':
// 自定义规则,可以扩展
return $this->check_custom_rule($rule, $post);
default:
return false;
}
}
/**
* 应用规则到内容
*/
private function apply_rule_to_content($rule, $content, $post) {
switch ($rule['action_type']) {
case 'hide':
return ''; // 完全隐藏内容
case 'replace':
return $this->get_replacement_content($rule);
case 'show':
// 默认就是
