文章目录[隐藏]
WordPress REST API 开发教程:集成第三方数据服务实现实用小工具
引言:WordPress REST API 的强大潜力
WordPress REST API 是现代WordPress开发的核心组件之一,它提供了一种标准化的方式与WordPress进行数据交互。通过REST API,开发者可以创建、读取、更新和删除WordPress内容,更重要的是,它允许我们将WordPress转变为一个功能强大的应用程序平台。
本教程将深入探讨如何利用WordPress REST API集成第三方数据服务,通过代码二次开发实现各种实用的互联网小工具。我们将从基础概念开始,逐步构建完整的解决方案,每个代码示例都包含详细注释,确保您能够完全理解并应用这些技术。
第一部分:WordPress REST API 基础与环境配置
1.1 REST API 基础概念
WordPress REST API 遵循RESTful架构原则,使用HTTP方法(GET、POST、PUT、DELETE)来操作资源。每个WordPress内容类型(如文章、页面、用户等)都有对应的API端点。
<?php
/**
* WordPress REST API 基础示例
* 演示如何注册自定义API端点
*/
// 确保在WordPress环境中运行
if (!defined('ABSPATH')) {
exit;
}
/**
* 初始化REST API相关功能
*/
function my_custom_api_init() {
// 注册自定义命名空间
register_rest_route('myplugin/v1', '/test', array(
'methods' => 'GET',
'callback' => 'my_custom_api_test',
'permission_callback' => '__return_true' // 允许公开访问
));
}
add_action('rest_api_init', 'my_custom_api_init');
/**
* 自定义API端点回调函数
*
* @param WP_REST_Request $request 请求对象
* @return WP_REST_Response 响应对象
*/
function my_custom_api_test($request) {
// 准备响应数据
$data = array(
'status' => 'success',
'message' => 'Hello from WordPress REST API!',
'timestamp' => current_time('timestamp'),
'site_name' => get_bloginfo('name')
);
// 创建并返回响应
return new WP_REST_Response($data, 200);
}
/**
* 添加自定义API字段到文章端点
*/
function my_custom_api_register_fields() {
register_rest_field('post', 'custom_field', array(
'get_callback' => 'my_custom_api_get_field',
'update_callback' => null,
'schema' => array(
'description' => '自定义字段示例',
'type' => 'string',
'context' => array('view', 'edit')
)
));
}
add_action('rest_api_init', 'my_custom_api_register_fields');
/**
* 获取自定义字段值的回调函数
*/
function my_custom_api_get_field($object, $field_name, $request) {
return get_post_meta($object['id'], $field_name, true);
}
?>
1.2 开发环境配置
在开始开发前,需要确保您的WordPress环境已正确配置:
- 启用永久链接:REST API需要干净的URL结构
- 调试模式:在wp-config.php中启用调试功能
- 安装必要插件:如"WP REST API Controller"用于管理API端点
- 使用开发者工具:推荐Postman或Insomnia测试API
第二部分:集成第三方天气数据服务
2.1 选择天气API服务
我们将使用OpenWeatherMap API作为示例,它提供免费的天气数据服务。首先需要注册获取API密钥。
<?php
/**
* WordPress天气小工具
* 集成OpenWeatherMap API
*/
class Weather_Widget_Tool {
private $api_key;
private $cache_time;
/**
* 构造函数
*
* @param string $api_key OpenWeatherMap API密钥
* @param int $cache_time 缓存时间(秒)
*/
public function __construct($api_key = '', $cache_time = 1800) {
$this->api_key = $api_key;
$this->cache_time = $cache_time;
// 初始化钩子
$this->init_hooks();
}
/**
* 初始化WordPress钩子
*/
private function init_hooks() {
// 注册REST API端点
add_action('rest_api_init', array($this, 'register_weather_endpoints'));
// 注册短代码
add_shortcode('weather_widget', array($this, 'weather_shortcode'));
// 注册小工具
add_action('widgets_init', function() {
register_widget('Weather_Widget_Class');
});
}
/**
* 注册天气相关的REST API端点
*/
public function register_weather_endpoints() {
// 获取当前天气
register_rest_route('weather/v1', '/current/(?P<city>[a-zA-Z0-9s,]+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_current_weather'),
'args' => array(
'city' => array(
'required' => true,
'validate_callback' => function($param) {
return !empty($param);
}
),
'units' => array(
'required' => false,
'default' => 'metric',
'enum' => array('metric', 'imperial')
)
),
'permission_callback' => '__return_true'
));
// 获取天气预报
register_rest_route('weather/v1', '/forecast/(?P<city>[a-zA-Z0-9s,]+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_weather_forecast'),
'permission_callback' => '__return_true'
));
}
/**
* 获取当前天气数据
*
* @param WP_REST_Request $request 请求对象
* @return WP_REST_Response|WP_Error 响应数据或错误
*/
public function get_current_weather($request) {
$city = sanitize_text_field($request['city']);
$units = sanitize_text_field($request->get_param('units') ?: 'metric');
// 检查缓存
$cache_key = 'weather_current_' . md5($city . $units);
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return new WP_REST_Response($cached_data, 200);
}
// 构建API请求URL
$api_url = add_query_arg(array(
'q' => $city,
'appid' => $this->api_key,
'units' => $units,
'lang' => get_locale()
), 'https://api.openweathermap.org/data/2.5/weather');
// 发送API请求
$response = wp_remote_get($api_url, array(
'timeout' => 15
));
// 检查请求是否成功
if (is_wp_error($response)) {
return new WP_Error('api_error',
__('无法连接到天气服务', 'textdomain'),
array('status' => 500));
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
// 检查API响应
if (isset($data['cod']) && $data['cod'] != 200) {
return new WP_Error('weather_error',
$data['message'] ?? __('获取天气数据失败', 'textdomain'),
array('status' => $data['cod']));
}
// 格式化响应数据
$formatted_data = array(
'city' => $data['name'] ?? $city,
'country' => $data['sys']['country'] ?? '',
'temperature' => round($data['main']['temp']),
'feels_like' => round($data['main']['feels_like']),
'humidity' => $data['main']['humidity'],
'pressure' => $data['main']['pressure'],
'weather' => array(
'main' => $data['weather'][0]['main'],
'description' => $data['weather'][0]['description'],
'icon' => $this->get_weather_icon_url($data['weather'][0]['icon'])
),
'wind' => array(
'speed' => $data['wind']['speed'],
'deg' => $data['wind']['deg'] ?? 0
),
'units' => $units,
'timestamp' => time(),
'sunrise' => $data['sys']['sunrise'],
'sunset' => $data['sys']['sunset']
);
// 缓存数据
set_transient($cache_key, $formatted_data, $this->cache_time);
return new WP_REST_Response($formatted_data, 200);
}
/**
* 获取天气图标URL
*
* @param string $icon_code 图标代码
* @return string 图标URL
*/
private function get_weather_icon_url($icon_code) {
return sprintf('https://openweathermap.org/img/wn/%s@2x.png', $icon_code);
}
/**
* 天气小工具短代码
*
* @param array $atts 短代码属性
* @return string HTML输出
*/
public function weather_shortcode($atts) {
$atts = shortcode_atts(array(
'city' => 'Beijing',
'units' => 'metric',
'show_icon' => true,
'show_details' => false
), $atts, 'weather_widget');
// 获取天气数据
$request = new WP_REST_Request('GET', '/weather/v1/current/' . urlencode($atts['city']));
$request->set_param('units', $atts['units']);
$response = rest_do_request($request);
if ($response->is_error()) {
return '<div class="weather-error">' .
__('无法加载天气数据', 'textdomain') .
'</div>';
}
$weather_data = $response->get_data();
// 构建HTML输出
ob_start();
?>
<div class="weather-widget" data-city="<?php echo esc_attr($atts['city']); ?>">
<div class="weather-header">
<h3><?php echo esc_html($weather_data['city']); ?></h3>
<?php if ($atts['show_icon']): ?>
<img src="<?php echo esc_url($weather_data['weather']['icon']); ?>"
alt="<?php echo esc_attr($weather_data['weather']['description']); ?>"
class="weather-icon">
<?php endif; ?>
</div>
<div class="weather-main">
<div class="temperature">
<?php echo esc_html($weather_data['temperature']); ?>°
<?php echo $atts['units'] == 'metric' ? 'C' : 'F'; ?>
</div>
<div class="description">
<?php echo esc_html($weather_data['weather']['description']); ?>
</div>
</div>
<?php if ($atts['show_details']): ?>
<div class="weather-details">
<div class="detail-item">
<span class="label"><?php _e('体感温度', 'textdomain'); ?>:</span>
<span class="value"><?php echo esc_html($weather_data['feels_like']); ?>°</span>
</div>
<div class="detail-item">
<span class="label"><?php _e('湿度', 'textdomain'); ?>:</span>
<span class="value"><?php echo esc_html($weather_data['humidity']); ?>%</span>
</div>
<div class="detail-item">
<span class="label"><?php _e('风速', 'textdomain'); ?>:</span>
<span class="value"><?php echo esc_html($weather_data['wind']['speed']); ?> m/s</span>
</div>
</div>
<?php endif; ?>
</div>
<style>
.weather-widget {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
max-width: 300px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.weather-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.weather-header h3 {
margin: 0;
font-size: 1.5em;
}
.weather-icon {
width: 60px;
height: 60px;
}
.weather-main {
text-align: center;
margin-bottom: 15px;
}
.temperature {
font-size: 3em;
font-weight: bold;
line-height: 1;
}
.description {
font-size: 1.2em;
text-transform: capitalize;
}
.weather-details {
border-top: 1px solid rgba(255,255,255,0.2);
padding-top: 15px;
}
.detail-item {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 0.9em;
}
.weather-error {
padding: 10px;
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
border-radius: 4px;
}
</style>
<?php
return ob_get_clean();
}
}
/**
* 天气小工具类
*/
class Weather_Widget_Class extends WP_Widget {
public function __construct() {
parent::__construct(
'weather_widget',
__('天气小工具', 'textdomain'),
array('description' => __('显示当前天气信息', 'textdomain'))
);
}
public function widget($args, $instance) {
echo $args['before_widget'];
if (!empty($instance['title'])) {
echo $args['before_title'] .
apply_filters('widget_title', $instance['title']) .
$args['after_title'];
}
// 使用短代码显示天气
echo do_shortcode('[weather_widget city="' .
esc_attr($instance['city']) .
'" units="' .
esc_attr($instance['units']) .
'" show_details="' .
($instance['show_details'] ? 'true' : 'false') .
'"]');
echo $args['after_widget'];
}
public function form($instance) {
$title = !empty($instance['title']) ? $instance['title'] : '';
$city = !empty($instance['city']) ? $instance['city'] : 'Beijing';
$units = !empty($instance['units']) ? $instance['units'] : 'metric';
$show_details = !empty($instance['show_details']) ? $instance['show_details'] : false;
?>
<p>
<label for="<?php echo esc_attr($this->get_field_id('title')); ?>">
<?php _e('标题:', 'textdomain'); ?>
</label>
<input class="widefat"
id="<?php echo esc_attr($this->get_field_id('title')); ?>"
name="<?php echo esc_attr($this->get_field_name('title')); ?>"
type="text"
value="<?php echo esc_attr($title); ?>">
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('city')); ?>">
<?php _e('城市:', 'textdomain'); ?>
</label>
<input class="widefat"
id="<?php echo esc_attr($this->get_field_id('city')); ?>"
name="<?php echo esc_attr($this->get_field_name('city')); ?>"
type="text"
value="<?php echo esc_attr($city); ?>">
</p>
<p>
<label for="<?php echo esc_attr($this->get_field_id('units')); ?>">
<?php _e('单位:', 'textdomain'); ?>
</label>
<select class="widefat"
id="<?php echo esc_attr($this->get_field_id('units')); ?>"
name="<?php echo esc_attr($this->get_field_name('units')); ?>">
<option value="metric" <?php selected($units, 'metric'); ?>>
<?php _e('公制 (°C)', 'textdomain'); ?>
</option>
<option value="imperial" <?php selected($units, 'imperial'); ?>>
<?php _e('英制 (°F)', 'textdomain'); ?>
</option>
</select>
</p>
<p>
<input type="checkbox"
id="<?php echo esc_attr($this->get_field_id('show_details')); ?>"
name="<?php echo esc_attr($this->get_field_name('show_details')); ?>"
value="1" <?php checked($show_details, 1); ?>>
<label for="<?php echo esc_attr($this->get_field_id('show_details')); ?>">
<?php _e('显示详细天气信息', 'textdomain'); ?>
</label>
</p>
<?php
}
public function update($new_instance, $old_instance) {
$instance = array();
$instance['title'] = (!empty($new_instance['title'])) ?
strip_tags($new_instance['title']) : '';
$instance['city'] = (!empty($new_instance['city'])) ?
city']) : 'Beijing';
$instance['units'] = (!empty($new_instance['units'])) ?
sanitize_text_field($new_instance['units']) : 'metric';
$instance['show_details'] = (!empty($new_instance['show_details'])) ? 1 : 0;
return $instance;
}
}
// 初始化天气小工具
$weather_api_key = 'your_openweathermap_api_key_here'; // 请替换为您的API密钥
new Weather_Widget_Tool($weather_api_key);
?>
### 2.2 天气预报功能扩展
<?php
/**
- 天气预报功能扩展
- 提供5天天气预报数据
*/
class Weather_Forecast_Extension {
private $api_key;
public function __construct($api_key) {
$this->api_key = $api_key;
$this->init();
}
private function init() {
// 注册天气预报短代码
add_shortcode('weather_forecast', array($this, 'forecast_shortcode'));
// 添加AJAX处理
add_action('wp_ajax_get_weather_forecast', array($this, 'ajax_get_forecast'));
add_action('wp_ajax_nopriv_get_weather_forecast', array($this, 'ajax_get_forecast'));
// 注册前端脚本
add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
}
/**
* 获取天气预报数据
*/
public function get_weather_forecast($request) {
$city = sanitize_text_field($request['city']);
$units = sanitize_text_field($request->get_param('units') ?: 'metric');
$cache_key = 'weather_forecast_' . md5($city . $units);
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return new WP_REST_Response($cached_data, 200);
}
$api_url = add_query_arg(array(
'q' => $city,
'appid' => $this->api_key,
'units' => $units,
'cnt' => 40 // 5天数据(每3小时一次)
), 'https://api.openweathermap.org/data/2.5/forecast');
$response = wp_remote_get($api_url, array('timeout' => 15));
if (is_wp_error($response)) {
return new WP_Error('api_error',
__('无法获取天气预报数据', 'textdomain'),
array('status' => 500));
}
$data = json_decode(wp_remote_retrieve_body($response), true);
if ($data['cod'] != '200') {
return new WP_Error('forecast_error',
$data['message'] ?? __('获取预报数据失败', 'textdomain'),
array('status' => $data['cod']));
}
// 按天分组预报数据
$forecast_by_day = array();
foreach ($data['list'] as $forecast) {
$date = date('Y-m-d', $forecast['dt']);
if (!isset($forecast_by_day[$date])) {
$forecast_by_day[$date] = array(
'date' => $date,
'day_name' => date_i18n('l', $forecast['dt']),
'forecasts' => array(),
'temp_min' => PHP_INT_MAX,
'temp_max' => PHP_INT_MIN,
'icons' => array()
);
}
$forecast_by_day[$date]['forecasts'][] = array(
'time' => date('H:i', $forecast['dt']),
'temp' => round($forecast['main']['temp']),
'feels_like' => round($forecast['main']['feels_like']),
'humidity' => $forecast['main']['humidity'],
'weather' => array(
'main' => $forecast['weather'][0]['main'],
'description' => $forecast['weather'][0]['description'],
'icon' => $this->get_weather_icon_url($forecast['weather'][0]['icon'])
),
'wind_speed' => $forecast['wind']['speed']
);
// 更新当天温度范围
$forecast_by_day[$date]['temp_min'] = min(
$forecast_by_day[$date]['temp_min'],
round($forecast['main']['temp_min'])
);
$forecast_by_day[$date]['temp_max'] = max(
$forecast_by_day[$date]['temp_max'],
round($forecast['main']['temp_max'])
);
// 收集天气图标
$forecast_by_day[$date]['icons'][] = $forecast['weather'][0]['icon'];
}
// 确定每天的主要天气图标
foreach ($forecast_by_day as &$day) {
$icon_counts = array_count_values($day['icons']);
arsort($icon_counts);
$day['main_icon'] = $this->get_weather_icon_url(key($icon_counts));
}
$formatted_data = array(
'city' => $data['city']['name'],
'country' => $data['city']['country'],
'forecast_days' => array_values($forecast_by_day),
'units' => $units,
'updated_at' => current_time('mysql')
);
set_transient($cache_key, $formatted_data, 3600); // 缓存1小时
return new WP_REST_Response($formatted_data, 200);
}
private function get_weather_icon_url($icon_code) {
return sprintf('https://openweathermap.org/img/wn/%s@2x.png', $icon_code);
}
/**
* 天气预报短代码
*/
public function forecast_shortcode($atts) {
$atts = shortcode_atts(array(
'city' => 'Beijing',
'days' => 5,
'units' => 'metric',
'layout' => 'horizontal' // horizontal 或 vertical
), $atts, 'weather_forecast');
ob_start();
?>
<div class="weather-forecast-widget"
data-city="<?php echo esc_attr($atts['city']); ?>"
data-days="<?php echo esc_attr($atts['days']); ?>"
data-units="<?php echo esc_attr($atts['units']); ?>"
data-layout="<?php echo esc_attr($atts['layout']); ?>">
<div class="forecast-loading">
<?php _e('加载天气预报...', 'textdomain'); ?>
</div>
<div class="forecast-content" style="display: none;"></div>
</div>
<?php
return ob_get_clean();
}
/**
* 注册前端脚本
*/
public function enqueue_scripts() {
wp_register_script('weather-forecast',
plugins_url('js/weather-forecast.js', __FILE__),
array('jquery'),
'1.0.0',
true);
wp_localize_script('weather-forecast', 'weatherForecast', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('weather_forecast_nonce')
));
wp_enqueue_script('weather-forecast');
wp_enqueue_style('weather-forecast-style',
plugins_url('css/weather-forecast.css', __FILE__));
}
/**
* AJAX获取天气预报
*/
public function ajax_get_forecast() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'weather_forecast_nonce')) {
wp_die('Security check failed');
}
$city = sanitize_text_field($_POST['city']);
$units = sanitize_text_field($_POST['units']);
// 构建REST请求
$request = new WP_REST_Request('GET', '/weather/v1/forecast/' . urlencode($city));
$request->set_param('units', $units);
$response = rest_do_request($request);
if ($response->is_error()) {
wp_send_json_error($response->as_error()->get_error_message());
}
wp_send_json_success($response->get_data());
}
}
// 初始化天气预报扩展
$forecast_extension = new Weather_Forecast_Extension($weather_api_key);
?>
## 第三部分:集成汇率转换工具
### 3.1 汇率API集成
<?php
/**
- 汇率转换工具
- 使用免费汇率API
*/
class Currency_Converter_Tool {
private $api_base = 'https://api.exchangerate-api.com/v4/latest/';
private $cache_time = 3600; // 1小时缓存
public function __construct() {
$this->init_hooks();
}
private function init_hooks() {
// 注册REST API端点
add_action('rest_api_init', array($this, 'register_currency_endpoints'));
// 注册短代码
add_shortcode('currency_converter', array($this, 'converter_shortcode'));
// 注册小工具
add_action('widgets_init', function() {
register_widget('Currency_Converter_Widget');
});
}
/**
* 注册汇率API端点
*/
public function register_currency_endpoints() {
// 获取汇率
register_rest_route('currency/v1', '/rates/(?P<base>[A-Z]{3})', array(
'methods' => 'GET',
'callback' => array($this, 'get_exchange_rates'),
'args' => array(
'base' => array(
'required' => true,
'validate_callback' => function($param) {
return preg_match('/^[A-Z]{3}$/', $param);
}
)
),
'permission_callback' => '__return_true'
));
// 货币转换
register_rest_route('currency/v1', '/convert', array(
'methods' => 'GET',
'callback' => array($this, 'convert_currency'),
'args' => array(
'amount' => array(
'required' => true,
'validate_callback' => function($param) {
return is_numeric($param) && $param > 0;
}
),
'from' => array(
'required' => true,
'validate_callback' => function($param) {
return preg_match('/^[A-Z]{3}$/', $param);
}
),
'to' => array(
'required' => true,
'validate_callback' => function($param) {
return preg_match('/^[A-Z]{3}$/', $param);
}
)
),
'permission_callback' => '__return_true'
));
// 获取支持的货币列表
register_rest_route('currency/v1', '/currencies', array(
'methods' => 'GET',
'callback' => array($this, 'get_currency_list'),
'permission_callback' => '__return_true'
));
}
/**
* 获取汇率数据
*/
public function get_exchange_rates($request) {
$base_currency = strtoupper($request['base']);
$cache_key = 'exchange_rates_' . $base_currency;
$cached_data = get_transient($cache_key);
if ($cached_data !== false) {
return new WP_REST_Response($cached_data, 200);
}
$api_url = $this->api_base . $base_currency;
$response = wp_remote_get($api_url, array('timeout' => 15));
if (is_wp_error($response)) {
// 备用API
$api_url = "https://api.exchangerate.host/latest?base={$base_currency}";
$response = wp_remote_get($api_url, array('timeout' => 15));
if (is_wp_error($response)) {
return new WP_Error('api_error',
__('无法获取汇率数据', 'textdomain'),
array('status' => 500));
}
}
$data = json_decode(wp_remote_retrieve_body($response), true);
if (empty($data['rates'])) {
return new WP_Error('data_error',
__('无效的汇率数据', 'textdomain'),
array('status' => 500));
}
$formatted_data = array(
'base' => $base_currency,
'rates' => $data['rates'],
'date' => $data['date'] ?? current_time('Y-m-d'),
'timestamp' => time()
);
set_transient($cache_key, $formatted_data, $this->cache_time);
return new WP_REST_Response($formatted_data, 200);
}
/**
* 货币转换
*/
public function convert_currency($request) {
$amount = floatval($request['amount']);
$from_currency = strtoupper($request['from']);
$to_currency = strtoupper($request['to']);
// 获取汇率
$rates_request = new WP_REST_Request('GET', '/currency/v1/rates/' . $from_currency);
$rates_response = rest_do_request($rates_request);
if ($rates_response->is_error()) {
return $rates_response->as_error();
}
$rates_data = $rates_response->get_data();
if (!isset($rates_data['rates'][$to_currency])) {
return new WP_Error('currency_error',
__('不支持的目标货币', 'textdomain'),
array('status' => 400));
}
$rate = $rates_data['rates'][$to_currency];
$converted_amount = $amount * $rate;
$result = array(
'amount' => $amount,
'from' => $from_currency,
'to' => $to_currency,
'rate' => $rate,
'converted' => round($converted_amount, 4),
'timestamp' => time(),
'date' => $rates_data['date']
);
return new WP_REST_Response($result, 200);
}
/**
* 获取货币列表
*/
public function get_currency_list() {
$currencies = array(
'USD' => __('美元', 'textdomain'),
'EUR' => __('欧元', 'textdomain'),
'GBP' => __('英镑', 'textdomain'),
'JPY' => __('日元', 'textdomain'),
'CNY' => __('人民币', 'textdomain'),
'CAD' => __('加元', 'textdomain'),
'AUD' => __('澳元', 'textdomain'),
'CHF' => __('瑞士法郎', 'textdomain'),
'HKD' => __('港币', 'textdomain'),
'SGD' => __('新加坡元', 'textdomain'),
'KRW' => __('韩元', 'textdomain'),
'INR' => __('印度卢比', 'textdomain'),
'RUB' => __('俄罗斯卢布', 'textdomain'),
'BRL' => __('巴西雷亚尔', 'textdomain'),
'MXN' => __('墨西哥比索', 'textdomain')
);
return new WP_REST_Response($currencies, 200);
}
/**
* 汇率转换器短代码
*/
public function converter_shortcode($atts) {
$atts = shortcode_atts(array(
'default_from' => 'USD',
'default_to' => 'CNY',
'show_historical' => false,
'theme' => 'light'
), $atts, 'currency_converter');
// 获取货币列表
$request = new WP_REST_Request('GET', '/currency/v1/currencies');
$response = rest_do_request($request);
$currencies = $response->is_error() ? array() : $response->get_data();
ob_start();
?>
<div class="currency-converter-widget theme-<?php echo esc_attr($atts['theme']); ?>">
<div class="converter-header">
<h3><?php _e('汇率转换器', 'textdomain'); ?></h3>
<div class="last-updated">
<?php _e('实时汇率', 'textdomain'); ?>
</div>
</div>
<div class="converter-form">
<div class="input-group">
<div class="input-wrapper">
<input type="number"
class="amount-input"
value="1"
min="0"
step="0.01"
placeholder="<?php esc_attr_e('金额', 'textdomain'); ?>">
<select class="currency-select from-currency">
<?php foreach ($currencies as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"
<?php selected($code, $atts['default_from']); ?>>
<?php echo esc_html("$code - $name"); ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="swap-button">
<button type="button" class="swap-currencies">
<span class="swap-icon">⇄</span>
</button>
</div>
<div class="input-wrapper">
<input type="text"
class="result-output"
readonly
placeholder="<?php esc_attr_e('转换结果', 'textdomain'); ?>">
<select class="currency-select to-currency">
<?php foreach ($currencies as $code => $name): ?>
<option value="<?php echo esc_attr($code); ?>"
<?php selected($code, $atts['default_to']);


