文章目录[隐藏]
WordPress REST API 开发教程:集成第三方数据服务,实现常用互联网小工具功能
引言:WordPress REST API 的强大潜力
WordPress作为全球最流行的内容管理系统,早已超越了简单的博客平台定位。随着REST API的全面集成,WordPress已经演变为一个功能强大的应用程序框架。本教程将深入探讨如何利用WordPress REST API进行二次开发,集成第三方数据服务,实现各种实用的互联网小工具功能。
通过本教程,您将学习到如何将WordPress转变为一个灵活的开发平台,创建诸如天气预报显示、实时汇率转换、社交媒体聚合、新闻资讯展示等实用功能。这些技能不仅适用于个人网站定制,也为企业级应用开发提供了坚实基础。
第一章:WordPress REST API 基础与配置
1.1 REST API 核心概念
WordPress REST API提供了一套标准的HTTP端点,允许开发者以JSON格式与WordPress数据进行交互。这种架构使得前端与后端分离成为可能,也为集成外部服务提供了便利。
REST API的核心优势包括:
- 标准化数据交换格式(JSON)
- 无状态通信,易于扩展
- 支持多种HTTP方法(GET、POST、PUT、DELETE)
- 完善的认证和权限控制
1.2 环境配置与基础测试
在开始开发前,确保您的WordPress版本在4.7以上,这是REST API完全集成的最低要求。您可以通过以下步骤验证REST API是否正常工作:
- 访问
https://您的网站域名/wp-json/,应该能看到API的索引页面 - 安装并激活"WP REST API Controller"等插件,方便管理API端点
- 使用Postman或Insomnia等API测试工具进行基础调用测试
1.3 自定义端点基础结构
创建自定义REST API端点是扩展WordPress功能的关键。以下是一个基础的自定义端点示例:
// 在主题的functions.php或自定义插件中添加
add_action('rest_api_init', function () {
// 注册自定义路由
register_rest_route('custom/v1', '/test', array(
'methods' => 'GET',
'callback' => 'custom_api_test',
'permission_callback' => '__return_true'
));
});
function custom_api_test($request) {
return new WP_REST_Response(array(
'status' => 'success',
'message' => '自定义API端点工作正常',
'timestamp' => current_time('timestamp')
), 200);
}
第二章:第三方数据服务集成策略
2.1 选择合适的数据服务API
在集成第三方服务前,需要考虑以下因素:
- API的稳定性和可靠性
- 请求频率限制和定价策略
- 数据格式的兼容性
- 文档完整性和技术支持
常用免费数据服务包括:
- 天气数据:OpenWeatherMap,WeatherAPI
- 金融数据:ExchangeRate-API,Alpha Vantage
- 新闻资讯:NewsAPI,Currents API
- 地理位置:IP-API,OpenCage Geocoder
2.2 API密钥管理与安全
安全存储和管理API密钥至关重要。推荐做法:
// 使用WordPress选项API存储API密钥
class API_Key_Manager {
private $api_keys;
public function __construct() {
$this->api_keys = get_option('third_party_api_keys', array());
}
public function get_key($service_name) {
if (isset($this->api_keys[$service_name])) {
return $this->api_keys[$service_name];
}
return false;
}
public function update_key($service_name, $key) {
$this->api_keys[$service_name] = $key;
update_option('third_party_api_keys', $this->api_keys);
}
public function create_settings_page() {
// 创建管理页面供用户输入API密钥
add_options_page(
'API密钥管理',
'API密钥',
'manage_options',
'api-key-settings',
array($this, 'render_settings_page')
);
}
public function render_settings_page() {
// 设置页面HTML
?>
<div class="wrap">
<h1>第三方API密钥管理</h1>
<form method="post" action="options.php">
<?php settings_fields('api_keys_group'); ?>
<table class="form-table">
<tr>
<th scope="row">天气API密钥</th>
<td>
<input type="text" name="weather_api_key"
value="<?php echo esc_attr($this->get_key('weather')); ?>"
class="regular-text">
</td>
</tr>
<!-- 更多API密钥字段 -->
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
}
2.3 数据缓存与性能优化
频繁调用第三方API会影响网站性能,合理的缓存策略必不可少:
class API_Cache_Manager {
private $cache_duration;
public function __construct($duration = 3600) {
$this->cache_duration = $duration;
}
public function get_cached_data($key) {
$cached = get_transient($key);
if ($cached !== false) {
return $cached;
}
return false;
}
public function set_cached_data($key, $data, $duration = null) {
$duration = $duration ?: $this->cache_duration;
set_transient($key, $data, $duration);
}
public function fetch_with_cache($cache_key, $callback, $force_refresh = false) {
// 强制刷新时跳过缓存
if ($force_refresh) {
delete_transient($cache_key);
}
// 尝试从缓存获取
$cached_data = $this->get_cached_data($cache_key);
if ($cached_data !== false) {
return $cached_data;
}
// 缓存未命中,执行回调获取数据
$fresh_data = call_user_func($callback);
// 存储到缓存
if ($fresh_data && !is_wp_error($fresh_data)) {
$this->set_cached_data($cache_key, $fresh_data);
}
return $fresh_data;
}
}
第三章:实用小工具开发实战
3.1 实时天气显示小工具
创建一个显示实时天气的REST API端点和小工具:
// 天气API端点
add_action('rest_api_init', function () {
register_rest_route('tools/v1', '/weather', array(
'methods' => 'GET',
'callback' => 'get_weather_data',
'permission_callback' => '__return_true',
'args' => array(
'city' => array(
'required' => false,
'default' => 'Beijing',
'validate_callback' => function($param) {
return is_string($param) && !empty(trim($param));
}
),
'units' => array(
'required' => false,
'default' => 'metric',
'enum' => array('metric', 'imperial')
)
)
));
});
function get_weather_data($request) {
$city = sanitize_text_field($request->get_param('city'));
$units = $request->get_param('units');
$cache_manager = new API_Cache_Manager(1800); // 30分钟缓存
$cache_key = 'weather_data_' . md5($city . $units);
$weather_data = $cache_manager->fetch_with_cache($cache_key, function() use ($city, $units) {
$api_manager = new API_Key_Manager();
$api_key = $api_manager->get_key('openweathermap');
if (!$api_key) {
return new WP_Error('no_api_key', '天气API密钥未配置', array('status' => 400));
}
$api_url = sprintf(
'https://api.openweathermap.org/data/2.5/weather?q=%s&units=%s&appid=%s',
urlencode($city),
$units,
$api_key
);
$response = wp_remote_get($api_url, array('timeout' => 15));
if (is_wp_error($response)) {
return $response;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['cod']) && $data['cod'] !== 200) {
return new WP_Error('api_error', $data['message'] ?? '天气数据获取失败', array('status' => $data['cod']));
}
// 格式化响应数据
return array(
'city' => $data['name'],
'country' => $data['sys']['country'] ?? '',
'temperature' => round($data['main']['temp']),
'feels_like' => round($data['main']['feels_like']),
'humidity' => $data['main']['humidity'],
'pressure' => $data['main']['pressure'],
'description' => $data['weather'][0]['description'],
'icon' => $data['weather'][0]['icon'],
'wind_speed' => $data['wind']['speed'],
'wind_direction' => $data['wind']['deg'] ?? 0,
'sunrise' => date('H:i', $data['sys']['sunrise']),
'sunset' => date('H:i', $data['sys']['sunset']),
'timestamp' => current_time('mysql')
);
});
return new WP_REST_Response($weather_data, 200);
}
// 天气小工具前端组件
class Weather_Widget extends WP_Widget {
public function __construct() {
parent::__construct(
'weather_widget',
'实时天气小工具',
array('description' => '显示指定城市的实时天气信息')
);
}
public function widget($args, $instance) {
echo $args['before_widget'];
$city = !empty($instance['city']) ? $instance['city'] : '北京';
$units = !empty($instance['units']) ? $instance['units'] : 'metric';
?>
<div class="weather-widget" data-city="<?php echo esc_attr($city); ?>"
data-units="<?php echo esc_attr($units); ?>">
<h3 class="weather-title"><?php echo esc_html($city); ?>天气</h3>
<div class="weather-content">
<div class="weather-loading">加载中...</div>
</div>
</div>
<script>
jQuery(document).ready(function($) {
function loadWeather() {
var widget = $('.weather-widget');
var city = widget.data('city');
var units = widget.data('units');
$.getJSON('/wp-json/tools/v1/weather?city=' + encodeURIComponent(city) + '&units=' + units,
function(data) {
if (data && !data.code) {
var html = '<div class="weather-info">';
html += '<div class="weather-temp">' + data.temperature + '°' + (units === 'metric' ? 'C' : 'F') + '</div>';
html += '<div class="weather-desc">' + data.description + '</div>';
html += '<div class="weather-details">';
html += '<span>湿度: ' + data.humidity + '%</span>';
html += '<span>风速: ' + data.wind_speed + ' m/s</span>';
html += '</div>';
html += '</div>';
widget.find('.weather-content').html(html);
} else {
widget.find('.weather-content').html('<p>天气数据暂时不可用</p>');
}
}
).fail(function() {
$('.weather-content').html('<p>天气数据加载失败</p>');
});
}
loadWeather();
// 每30分钟刷新一次
setInterval(loadWeather, 30 * 60 * 1000);
});
</script>
<?php
echo $args['after_widget'];
}
public function form($instance) {
$city = !empty($instance['city']) ? $instance['city'] : '';
$units = !empty($instance['units']) ? $instance['units'] : 'metric';
?>
<p>
<label for="<?php echo $this->get_field_id('city'); ?>">城市名称:</label>
<input class="widefat" id="<?php echo $this->get_field_id('city'); ?>"
name="<?php echo $this->get_field_name('city'); ?>"
type="text" value="<?php echo esc_attr($city); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('units'); ?>">温度单位:</label>
<select class="widefat" id="<?php echo $this->get_field_id('units'); ?>"
name="<?php echo $this->get_field_name('units'); ?>">
<option value="metric" <?php selected($units, 'metric'); ?>>摄氏度 (°C)</option>
<option value="imperial" <?php selected($units, 'imperial'); ?>>华氏度 (°F)</option>
</select>
</p>
<?php
}
public function update($new_instance, $old_instance) {
$instance = array();
$instance['city'] = !empty($new_instance['city']) ? sanitize_text_field($new_instance['city']) : '';
$instance['units'] = !empty($new_instance['units']) ? sanitize_text_field($new_instance['units']) : 'metric';
return $instance;
}
}
// 注册小工具
add_action('widgets_init', function() {
register_widget('Weather_Widget');
});
3.2 实时汇率转换工具
创建一个实用的汇率转换API和小工具:
// 汇率API端点
add_action('rest_api_init', function () {
register_rest_route('tools/v1', '/exchange-rate', array(
'methods' => 'GET',
'callback' => 'get_exchange_rate',
'permission_callback' => '__return_true',
'args' => array(
'from' => array(
'required' => true,
'validate_callback' => function($param) {
return strlen($param) === 3 && ctype_alpha($param);
}
),
'to' => array(
'required' => true,
'validate_callback' function($param) {
return strlen($param) === 3 && ctype_alpha($param);
}
),
'amount' => array(
'required' => false,
'default' => 1,
'validate_callback' => function($param) {
return is_numeric($param) && $param >= 0;
},
'sanitize_callback' => 'floatval'
)
)
));
});
function get_exchange_rate($request) {
$from = strtoupper(sanitize_text_field($request->get_param('from')));
$to = strtoupper(sanitize_text_field($request->get_param('to')));
$amount = floatval($request->get_param('amount'));
$cache_manager = new API_Cache_Manager(3600); // 1小时缓存
$cache_key = 'exchange_rate_' . $from . '_' . $to;
$rate_data = $cache_manager->fetch_with_cache($cache_key, function() use ($from, $to) {
// 使用ExchangeRate-API
$api_manager = new API_Key_Manager();
$api_key = $api_manager->get_key('exchangerate');
if (!$api_key) {
// 如果没有API密钥,使用免费替代方案
return get_exchange_rate_fallback($from, $to);
}
$api_url = sprintf(
'https://v6.exchangerate-api.com/v6/%s/pair/%s/%s',
$api_key,
$from,
$to
);
$response = wp_remote_get($api_url, array('timeout' => 15));
if (is_wp_error($response)) {
return get_exchange_rate_fallback($from, $to);
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if ($data['result'] === 'success') {
return array(
'from' => $from,
'to' => $to,
'rate' => $data['conversion_rate'],
'last_updated' => current_time('mysql'),
'source' => 'exchangerate-api'
);
}
return get_exchange_rate_fallback($from, $to);
});
if (is_wp_error($rate_data)) {
return $rate_data;
}
$converted_amount = $amount * $rate_data['rate'];
$response_data = array(
'from_currency' => $from,
'to_currency' => $to,
'amount' => $amount,
'exchange_rate' => $rate_data['rate'],
'converted_amount' => round($converted_amount, 4),
'last_updated' => $rate_data['last_updated'],
'source' => $rate_data['source']
);
return new WP_REST_Response($response_data, 200);
}
function get_exchange_rate_fallback($from, $to) {
// 使用免费API作为备选方案
$api_url = sprintf(
'https://api.exchangerate.host/latest?base=%s&symbols=%s',
$from,
$to
);
$response = wp_remote_get($api_url, array('timeout' => 15));
if (is_wp_error($response)) {
return new WP_Error('api_error', '无法获取汇率数据', array('status' => 500));
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['rates'][$to])) {
return array(
'from' => $from,
'to' => $to,
'rate' => $data['rates'][$to],
'last_updated' => $data['date'] . ' 00:00:00',
'source' => 'exchangerate.host'
);
}
return new WP_Error('no_rate', '未找到指定的汇率数据', array('status' => 404));
}
// 汇率转换小工具前端
class Currency_Converter_Widget extends WP_Widget {
private $popular_currencies = array(
'USD' => '美元',
'EUR' => '欧元',
'GBP' => '英镑',
'JPY' => '日元',
'CNY' => '人民币',
'CAD' => '加元',
'AUD' => '澳元',
'CHF' => '瑞士法郎'
);
public function __construct() {
parent::__construct(
'currency_converter_widget',
'汇率转换工具',
array('description' => '实时货币汇率转换计算器')
);
}
public function widget($args, $instance) {
echo $args['before_widget'];
$title = !empty($instance['title']) ? $instance['title'] : '汇率转换';
$default_from = !empty($instance['default_from']) ? $instance['default_from'] : 'USD';
$default_to = !empty($instance['default_to']) ? $instance['default_to'] : 'CNY';
?>
<div class="currency-converter-widget">
<h3 class="widget-title"><?php echo esc_html($title); ?></h3>
<div class="converter-form">
<div class="input-group">
<input type="number" class="amount-input"
placeholder="金额" value="1" min="0" step="0.01">
<select class="currency-select from-currency">
<?php foreach ($this->popular_currencies as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"
<?php selected($code, $default_from); ?>>
<?php echo esc_html("$code - $name"); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="swap-button-container">
<button type="button" class="swap-currencies">
<span class="dashicons dashicons-arrow-down-alt"></span>
<span class="dashicons dashicons-arrow-up-alt"></span>
</button>
</div>
<div class="input-group">
<input type="text" class="result-output" readonly
placeholder="转换结果">
<select class="currency-select to-currency">
<?php foreach ($this->popular_currencies as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"
<?php selected($code, $default_to); ?>>
<?php echo esc_html("$code - $name"); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="converter-info">
<p class="exchange-rate-info">汇率: <span class="rate-value">加载中...</span></p>
<p class="last-updated">最后更新: <span class="update-time">-</span></p>
</div>
<button type="button" class="convert-button button button-primary">
立即转换
</button>
</div>
</div>
<script>
jQuery(document).ready(function($) {
var converter = $('.currency-converter-widget');
var rateInfo = converter.find('.rate-value');
var updateTime = converter.find('.update-time');
var resultOutput = converter.find('.result-output');
function getExchangeRate(from, to, amount) {
$.getJSON('/wp-json/tools/v1/exchange-rate?from=' + from + '&to=' + to + '&amount=' + amount,
function(response) {
if (response && !response.code) {
resultOutput.val(response.converted_amount.toFixed(2));
rateInfo.text('1 ' + response.from_currency + ' = ' +
response.exchange_rate.toFixed(4) + ' ' + response.to_currency);
updateTime.text(response.last_updated);
} else {
resultOutput.val('转换失败');
rateInfo.text('数据获取失败');
}
}
).fail(function() {
resultOutput.val('服务暂时不可用');
rateInfo.text('网络错误');
});
}
// 初始加载
var fromCurrency = converter.find('.from-currency').val();
var toCurrency = converter.find('.to-currency').val();
var amount = converter.find('.amount-input').val();
getExchangeRate(fromCurrency, toCurrency, amount);
// 转换按钮点击事件
converter.find('.convert-button').on('click', function() {
fromCurrency = converter.find('.from-currency').val();
toCurrency = converter.find('.to-currency').val();
amount = converter.find('.amount-input').val();
if (!amount || amount <= 0) {
alert('请输入有效的金额');
return;
}
getExchangeRate(fromCurrency, toCurrency, amount);
});
// 货币交换按钮
converter.find('.swap-currencies').on('click', function() {
var fromVal = converter.find('.from-currency').val();
var toVal = converter.find('.to-currency').val();
converter.find('.from-currency').val(toVal);
converter.find('.to-currency').val(fromVal);
// 重新获取汇率
amount = converter.find('.amount-input').val();
getExchangeRate(toVal, fromVal, amount);
});
// 输入框实时转换(防抖处理)
var convertTimeout;
converter.find('.amount-input').on('input', function() {
clearTimeout(convertTimeout);
convertTimeout = setTimeout(function() {
amount = converter.find('.amount-input').val();
if (amount && amount > 0) {
fromCurrency = converter.find('.from-currency').val();
toCurrency = converter.find('.to-currency').val();
getExchangeRate(fromCurrency, toCurrency, amount);
}
}, 500);
});
// 货币选择变化时更新
converter.find('.currency-select').on('change', function() {
amount = converter.find('.amount-input').val();
if (amount && amount > 0) {
fromCurrency = converter.find('.from-currency').val();
toCurrency = converter.find('.to-currency').val();
getExchangeRate(fromCurrency, toCurrency, amount);
}
});
});
</script>
<style>
.currency-converter-widget {
padding: 15px;
background: #f8f9fa;
border-radius: 8px;
border: 1px solid #dee2e6;
}
.converter-form .input-group {
display: flex;
margin-bottom: 10px;
}
.amount-input, .result-output, .currency-select {
padding: 10px;
border: 1px solid #ced4da;
border-radius: 4px;
}
.amount-input {
flex: 1;
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.result-output {
flex: 1;
background: #fff;
border-right: none;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.currency-select {
width: 150px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.swap-button-container {
text-align: center;
margin: 10px 0;
}
.swap-currencies {
background: #6c757d;
color: white;
border: none;
border-radius: 50%;
width: 40px;
height: 40px;
cursor: pointer;
display: inline-flex;
align-items: center;
justify-content: center;
}
.swap-currencies:hover {
background: #5a6268;
}
.converter-info {
margin: 15px 0;
font-size: 0.9em;
color: #666;
}
.convert-button {
width: 100%;
padding: 12px;
font-size: 16px;
}
</style>
<?php
echo $args['after_widget'];
}
public function form($instance) {
$title = !empty($instance['title']) ? $instance['title'] : '汇率转换';
$default_from = !empty($instance['default_from']) ? $instance['default_from'] : 'USD';
$default_to = !empty($instance['default_to']) ? $instance['default_to'] : 'CNY';
?>
<p>
<label for="<?php echo $this->get_field_id('title'); ?>">小工具标题:</label>
<input class="widefat" id="<?php echo $this->get_field_id('title'); ?>"
name="<?php echo $this->get_field_name('title'); ?>"
type="text" value="<?php echo esc_attr($title); ?>">
</p>
<p>
<label for="<?php echo $this->get_field_id('default_from'); ?>">默认源货币:</label>
<select class="widefat" id="<?php echo $this->get_field_id('default_from'); ?>"
name="<?php echo $this->get_field_name('default_from'); ?>">
<?php foreach ($this->popular_currencies as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"
<?php selected($code, $default_from); ?>>
<?php echo esc_html("$code - $name"); ?>
</option>
<?php endforeach; ?>
</select>
</p>
<p>
<label for="<?php echo $this->get_field_id('default_to'); ?>">默认目标货币:</label>
<select class="widefat" id="<?php echo $this->get_field_id('default_to'); ?>"
name="<?php echo $this->get_field_name('default_to'); ?>">
<?php foreach ($this->popular_currencies as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"
<?php selected($code, $default_to); ?>>
<?php echo esc_html("$code - $name"); ?>
</option>
<?php endforeach; ?>
</select>
</p>
<?php
}
public function update($new_instance, $old_instance) {
$instance = array();
$instance['title'] = !empty($new_instance['title']) ? sanitize_text_field($new_instance['title']) : '';
$instance['default_from'] = !empty($new_instance['default_from']) ? sanitize_text_field($new_instance['default_from']) : 'USD';
$instance['default_to'] = !empty($new_instance['default_to']) ? sanitize_text_field($new_instance['default_to']) : 'CNY';
return $instance;
}
}
add_action('widgets_init', function() {
register_widget('Currency_Converter_Widget');
});
第四章:社交媒体聚合与展示
4.1 Twitter/X 最新推文聚合
创建一个聚合社交媒体内容的API端点:
// Twitter API集成(使用Twitter API v2)
add_action('rest_api_init', function () {
register_rest_route('social/v1', '/twitter/timeline', array(
'methods' => 'GET',
'callback' => 'get_twitter_timeline',
'permission_callback' => '__return_true',
'args' => array(
'username' => array(
'required' => false,
'default' => '',
'sanitize_callback' => 'sanitize_text_field'
),
'count' => array(
'required' => false,
'default' => 5,
'validate_callback' => function($param) {
return is_numeric($param) && $param > 0 && $param <= 20;
}
)
)
));
});
function get_twitter_timeline($request) {
$username = $request->get_param('username');
$count = intval($request->get_param('count'));
if (empty($username)) {
$api_manager = new API_Key_Manager();
$default_account = $api_manager->get_key('twitter_default_account');
if ($default_account) {
$username = $default_account;
} else {
return new WP_Error('no_username', '未指定Twitter用户名', array('status' => 400));
}
}
$cache_manager = new API_Cache_Manager(300); // 5分钟缓存
$cache_key = 'twitter_timeline_' . md5($username . $count);
$tweets = $cache_manager->fetch_with_cache($cache_key, function() use ($username, $count) {
$api_manager = new API_Key_Manager();
$bearer_token = $api_manager->get_key('twitter_bearer_token');
if (!$bearer_token) {
// 如果没有正式API密钥,使用备用方案
return get_twitter_timeline_fallback($username, $count);
}
// 首先获取用户ID
$user_url = sprintf(
'https://api.twitter.com/2/users/by/username/%s',
urlencode($username)
);
$user_response = wp_remote_get($user_url, array(
'timeout' => 15,
'headers' => array(
'Authorization' => 'Bearer ' . $bearer_token
)
));
if (is_wp_error($user_response)) {
return get_twitter_timeline_fallback($username, $count);
}
$user_data = json_decode(wp_remote_retrieve_body($user_response), true);
if (!isset($user_data['data']['id'])) {
return new WP_Error('user_not_found', '未找到指定的Twitter用户', array('status' => 404));
}
$user_id = $user_data['data']['id'];
// 获取用户时间线
$timeline_url = sprintf(
'https://api.twitter.com/2/users/%s/tweets?max_results=%d&tweet.fields=created_at,public_metrics,entities',
$user_id,
$count
);
$timeline_response = wp_remote_get($timeline_url, array(
'timeout' => 15,
'headers' => array(
'Authorization' => 'Bearer ' . $bearer_token
)
));
if (is_wp_error($timeline_response)) {
return get_twitter_timeline_fallback($username, $count);
}
$timeline_data = json_decode(wp_remote_retrieve_body($timeline_response), true);
if (!isset($timeline_data['data'])) {
return array();
}
$formatted_tweets = array();
foreach ($timeline_data['data'] as $tweet) {
$formatted_tweets[] = array(
'id' => $tweet['id'],
'text' => format_tweet_text($tweet['text'], $tweet['entities'] ?? array()),
'created_at' => $tweet['created_at'],
'timestamp' => strtotime($tweet['created_at']),
'likes' => $tweet['public_metrics']['like_count'] ?? 0,
'retweets' => $tweet['public_metrics']['retweet_count'] ?? 0,
'replies' => $tweet['public_metrics']['reply_count'] ?? 0,
'url' => sprintf('https://twitter.com/%s/status/%s', $username, $tweet['id'])
);
}
return $formatted_tweets;
});
if (is_wp_error($tweets)) {
return $tweets;
}
return new WP_REST_Response(array(
'username' => $username,
'tweets' => $tweets,
'count' => count($tweets),
'retrieved_at' => current_time('mysql')
), 200);
}
function format_tweet_text($text, $entities) {
// 处理链接
if (isset($entities['urls'])) {
foreach ($entities['urls'] as $url) {
$text = str_replace(
$url['url'],
sprintf('<a href="%s" target="_blank" rel="noopener">%s</a>',
esc_url($url['expanded_url']),
esc_html($url['display_url'])),
$text
);
}
}
// 处理提及
if (isset($entities['mentions'])) {
foreach ($entities['mentions'] as $mention) {
$text = str_replace(
'@' . $mention['username'],
sprintf('<a href="https://twitter.com/%s" target="_blank" rel="noopener">@%s</a>',
esc_attr($mention['username']),
esc_html($mention['username'])),
$text
);
}
}
// 处理标签
if (isset($entities['hashtags'])) {
foreach ($entities['hashtags'] as $hashtag) {
$text = str_replace(
'#' . $hashtag['tag'],
sprintf('<a href="https://twitter.com/hashtag/%s" target="_blank" rel="noopener">#%s</a>',
esc_attr($hashtag['tag']),
esc_html($hashtag['tag'])),
$text
);
}
}
return $text;
}
