文章目录[隐藏]
WordPress插件开发教程:集成实时股票行情与财经数据工具
引言:为什么需要自定义财经数据插件
在当今数字化时代,财经数据的实时获取和展示对于许多网站来说至关重要。无论是金融博客、投资社区还是企业官网,实时股票行情和财经数据都能显著提升网站的专业性和用户粘性。虽然市场上有一些现成的财经插件,但它们往往功能单一、定制性差,或者存在样式冲突、加载缓慢等问题。
通过自主开发WordPress插件,我们可以完全掌控数据展示方式、更新频率和用户界面,同时确保与网站主题完美兼容。本教程将引导您从零开始创建一个功能全面的实时股票行情与财经数据工具,并探讨如何通过WordPress代码二次开发实现常用互联网小工具功能。
第一章:WordPress插件开发基础
1.1 插件开发环境搭建
在开始开发之前,我们需要准备合适的开发环境:
- 本地开发环境:推荐使用XAMPP、MAMP或Local by Flywheel搭建本地WordPress环境
- 代码编辑器:Visual Studio Code、PHPStorm或Sublime Text
- 浏览器开发者工具:用于调试前端代码
- 版本控制系统:Git用于代码版本管理
1.2 创建插件基本结构
每个WordPress插件都需要一个主文件,通常以插件名称命名。让我们创建插件的初始结构:
wp-content/plugins/stock-finance-tool/
│
├── stock-finance-tool.php # 主插件文件
├── includes/ # 包含核心功能文件
│ ├── class-stock-api.php # 股票API处理类
│ ├── class-data-cache.php # 数据缓存类
│ └── class-shortcodes.php # 短代码处理类
├── admin/ # 后台管理文件
│ ├── css/ # 后台样式
│ ├── js/ # 后台脚本
│ └── class-admin-settings.php # 设置页面类
├── public/ # 前端文件
│ ├── css/ # 前端样式
│ ├── js/ # 前端脚本
│ └── templates/ # 前端模板
├── assets/ # 静态资源
│ └── images/ # 图片资源
└── uninstall.php # 插件卸载脚本
1.3 插件主文件配置
首先,我们需要在插件主文件中添加标准的WordPress插件头部信息:
<?php
/**
* Plugin Name: 实时股票行情与财经数据工具
* Plugin URI: https://yourwebsite.com/stock-plugin
* Description: 在WordPress网站中集成实时股票行情、财经数据和小工具功能
* Version: 1.0.0
* Author: 您的名称
* Author URI: https://yourwebsite.com
* License: GPL v2 or later
* Text Domain: stock-finance-tool
* Domain Path: /languages
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('SFT_VERSION', '1.0.0');
define('SFT_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('SFT_PLUGIN_URL', plugin_dir_url(__FILE__));
define('SFT_PLUGIN_BASENAME', plugin_basename(__FILE__));
// 插件初始化
add_action('plugins_loaded', 'sft_init_plugin');
function sft_init_plugin() {
// 加载文本域用于国际化
load_plugin_textdomain('stock-finance-tool', false, dirname(SFT_PLUGIN_BASENAME) . '/languages');
// 检查依赖
if (!function_exists('curl_init')) {
add_action('admin_notices', function() {
echo '<div class="notice notice-error"><p>';
echo esc_html__('实时股票行情插件需要cURL扩展支持,请联系您的主机提供商启用cURL。', 'stock-finance-tool');
echo '</p></div>';
});
return;
}
// 引入核心类文件
require_once SFT_PLUGIN_DIR . 'includes/class-stock-api.php';
require_once SFT_PLUGIN_DIR . 'includes/class-data-cache.php';
require_once SFT_PLUGIN_DIR . 'includes/class-shortcodes.php';
// 初始化核心类
$stock_api = new SFT_Stock_API();
$data_cache = new SFT_Data_Cache();
$shortcodes = new SFT_Shortcodes();
// 根据环境加载管理或前端功能
if (is_admin()) {
require_once SFT_PLUGIN_DIR . 'admin/class-admin-settings.php';
new SFT_Admin_Settings();
} else {
// 前端初始化
add_action('wp_enqueue_scripts', 'sft_enqueue_frontend_assets');
}
}
// 注册激活和停用钩子
register_activation_hook(__FILE__, 'sft_activate_plugin');
register_deactivation_hook(__FILE__, 'sft_deactivate_plugin');
function sft_activate_plugin() {
// 创建必要的数据库表
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'sft_stock_data';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
symbol varchar(20) NOT NULL,
data longtext NOT NULL,
last_updated datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY symbol (symbol)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
// 设置默认选项
add_option('sft_api_provider', 'alpha_vantage');
add_option('sft_cache_duration', 300);
add_option('sft_default_symbols', 'AAPL,MSFT,GOOGL,AMZN,TSLA');
add_option('sft_currency', 'USD');
}
function sft_deactivate_plugin() {
// 清理定时任务
wp_clear_scheduled_hook('sft_daily_data_update');
}
// 前端资源加载
function sft_enqueue_frontend_assets() {
wp_enqueue_style(
'sft-frontend-style',
SFT_PLUGIN_URL . 'public/css/frontend.css',
array(),
SFT_VERSION
);
wp_enqueue_script(
'sft-frontend-script',
SFT_PLUGIN_URL . 'public/js/frontend.js',
array('jquery'),
SFT_VERSION,
true
);
// 本地化脚本,传递数据到JavaScript
wp_localize_script('sft-frontend-script', 'sft_ajax_object', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('sft_nonce')
));
}
第二章:集成股票行情API
2.1 选择合适的财经数据API
市场上有多种财经数据API可供选择,每种都有其优缺点:
- Alpha Vantage:免费层提供较全面的数据,但调用频率有限制
- Yahoo Finance (非官方API):免费但稳定性存疑
- Twelve Data:提供免费和付费方案,数据质量较好
- Finnhub:免费层提供实时数据,但功能有限
- 国内选择:新浪财经、腾讯财经等提供免费接口
本教程将以Alpha Vantage为例,但代码设计将支持多种API提供商。
2.2 创建API处理类
<?php
// includes/class-stock-api.php
class SFT_Stock_API {
private $api_key;
private $api_provider;
private $cache_duration;
public function __construct() {
$this->api_key = get_option('sft_api_key', '');
$this->api_provider = get_option('sft_api_provider', 'alpha_vantage');
$this->cache_duration = get_option('sft_cache_duration', 300);
// 注册AJAX处理
add_action('wp_ajax_sft_get_stock_data', array($this, 'ajax_get_stock_data'));
add_action('wp_ajax_nopriv_sft_get_stock_data', array($this, 'ajax_get_stock_data'));
}
/**
* 获取股票数据
*/
public function get_stock_data($symbol, $force_refresh = false) {
// 检查缓存
if (!$force_refresh) {
$cached_data = $this->get_cached_data($symbol);
if ($cached_data !== false) {
return $cached_data;
}
}
// 根据选择的API提供商调用相应方法
switch ($this->api_provider) {
case 'alpha_vantage':
$data = $this->get_alpha_vantage_data($symbol);
break;
case 'twelve_data':
$data = $this->get_twelve_data_data($symbol);
break;
case 'yahoo':
$data = $this->get_yahoo_data($symbol);
break;
default:
$data = array('error' => '不支持的API提供商');
}
// 缓存数据
if (!isset($data['error'])) {
$this->cache_data($symbol, $data);
}
return $data;
}
/**
* Alpha Vantage API调用
*/
private function get_alpha_vantage_data($symbol) {
if (empty($this->api_key)) {
return array('error' => 'API密钥未配置');
}
$url = add_query_arg(array(
'function' => 'GLOBAL_QUOTE',
'symbol' => $symbol,
'apikey' => $this->api_key
), 'https://www.alphavantage.co/query');
$response = wp_remote_get($url, array(
'timeout' => 15,
'user-agent' => 'WordPress Stock Finance Tool/' . SFT_VERSION
));
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['Global Quote']) && !empty($data['Global Quote'])) {
$quote = $data['Global Quote'];
return array(
'symbol' => $symbol,
'price' => $quote['05. price'],
'change' => $quote['09. change'],
'change_percent' => $quote['10. change percent'],
'volume' => $quote['06. volume'],
'latest_trading_day' => $quote['07. latest trading day'],
'previous_close' => $quote['08. previous close'],
'open' => $quote['02. open'],
'high' => $quote['03. high'],
'low' => $quote['04. low']
);
} elseif (isset($data['Error Message'])) {
return array('error' => $data['Error Message']);
} else {
return array('error' => '无法解析API响应');
}
}
/**
* 获取缓存数据
*/
private function get_cached_data($symbol) {
global $wpdb;
$table_name = $wpdb->prefix . 'sft_stock_data';
$result = $wpdb->get_row($wpdb->prepare(
"SELECT data, last_updated FROM $table_name WHERE symbol = %s",
$symbol
));
if ($result) {
$last_updated = strtotime($result->last_updated);
$current_time = current_time('timestamp');
// 检查缓存是否过期
if (($current_time - $last_updated) < $this->cache_duration) {
return unserialize($result->data);
}
}
return false;
}
/**
* 缓存数据
*/
private function cache_data($symbol, $data) {
global $wpdb;
$table_name = $wpdb->prefix . 'sft_stock_data';
$wpdb->replace(
$table_name,
array(
'symbol' => $symbol,
'data' => serialize($data),
'last_updated' => current_time('mysql')
),
array('%s', '%s', '%s')
);
}
/**
* AJAX获取股票数据
*/
public function ajax_get_stock_data() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'sft_nonce')) {
wp_die('安全验证失败');
}
$symbol = sanitize_text_field($_POST['symbol']);
$data = $this->get_stock_data($symbol);
wp_send_json($data);
}
/**
* 批量获取多个股票数据
*/
public function get_multiple_stocks($symbols) {
$results = array();
$symbol_array = explode(',', $symbols);
foreach ($symbol_array as $symbol) {
$symbol = trim($symbol);
if (!empty($symbol)) {
$results[$symbol] = $this->get_stock_data($symbol);
}
}
return $results;
}
/**
* 获取历史数据
*/
public function get_historical_data($symbol, $interval = 'daily', $output_size = 'compact') {
if (empty($this->api_key)) {
return array('error' => 'API密钥未配置');
}
$url = add_query_arg(array(
'function' => 'TIME_SERIES_' . strtoupper($interval),
'symbol' => $symbol,
'outputsize' => $output_size,
'apikey' => $this->api_key
), 'https://www.alphavantage.co/query');
$response = wp_remote_get($url);
if (is_wp_error($response)) {
return array('error' => $response->get_error_message());
}
$body = wp_remote_retrieve_body($response);
return json_decode($body, true);
}
}
第三章:创建数据缓存系统
3.1 设计高效缓存机制
为了提高插件性能并减少API调用次数,我们需要实现一个高效的缓存系统:
<?php
// includes/class-data-cache.php
class SFT_Data_Cache {
private $cache_group = 'sft_stock_data';
public function __construct() {
// 注册清理过期缓存的定时任务
add_action('sft_clean_expired_cache', array($this, 'clean_expired_cache'));
if (!wp_next_scheduled('sft_clean_expired_cache')) {
wp_schedule_event(time(), 'daily', 'sft_clean_expired_cache');
}
}
/**
* 设置缓存
*/
public function set($key, $data, $expiration = 300) {
$cache_data = array(
'data' => $data,
'expires' => time() + $expiration
);
return wp_cache_set($key, $cache_data, $this->cache_group, $expiration);
}
/**
* 获取缓存
*/
public function get($key) {
$cached = wp_cache_get($key, $this->cache_group);
if ($cached === false) {
return false;
}
// 检查是否过期
if (isset($cached['expires']) && $cached['expires'] < time()) {
wp_cache_delete($key, $this->cache_group);
return false;
}
return $cached['data'];
}
/**
* 删除缓存
*/
public function delete($key) {
return wp_cache_delete($key, $this->cache_group);
}
/**
* 清理所有插件缓存
*/
public function flush() {
return wp_cache_flush();
}
/**
* 清理过期缓存
*/
public function clean_expired_cache() {
global $wpdb;
// 清理数据库缓存
$table_name = $wpdb->prefix . 'sft_stock_data';
$expiration_time = date('Y-m-d H:i:s', time() - 86400); // 24小时前的数据
$wpdb->query($wpdb->prepare(
"DELETE FROM $table_name WHERE last_updated < %s",
$expiration_time
));
// 清理对象缓存中的旧数据
// 注意:wp_cache_flush()会清理所有缓存,可能影响其他插件
// 在生产环境中需要更精细的缓存管理
}
/**
* 获取缓存统计信息
*/
public function get_stats() {
global $wpdb;
$table_name = $wpdb->prefix . 'sft_stock_data';
$total = $wpdb->get_var("SELECT COUNT(*) FROM $table_name");
$oldest = $wpdb->get_var("SELECT MIN(last_updated) FROM $table_name");
$newest = $wpdb->get_var("SELECT MAX(last_updated) FROM $table_name");
return array(
'total_cached' => $total,
'oldest_cache' => $oldest,
'newest_cache' => $newest
);
}
}
第四章:实现短代码功能
4.1 创建基本短代码
短代码是WordPress中非常强大的功能,允许用户在文章和页面中轻松插入动态内容:
<?php
// includes/class-shortcodes.php
class SFT_Shortcodes {
private $api;
public function __construct() {
$this->api = new SFT_Stock_API();
4.1 创建基本短代码(续)
// 注册短代码
add_shortcode('stock_quote', array($this, 'stock_quote_shortcode'));
add_shortcode('stock_ticker', array($this, 'stock_ticker_shortcode'));
add_shortcode('financial_data', array($this, 'financial_data_shortcode'));
add_shortcode('currency_converter', array($this, 'currency_converter_shortcode'));
add_shortcode('crypto_price', array($this, 'crypto_price_shortcode'));
// 注册Gutenberg块
add_action('init', array($this, 'register_gutenberg_blocks'));
}
/**
* 股票报价短代码
*/
public function stock_quote_shortcode($atts) {
$atts = shortcode_atts(array(
'symbol' => 'AAPL',
'show_change' => 'true',
'show_volume' => 'false',
'refresh' => '60',
'style' => 'default',
'currency' => 'USD'
), $atts, 'stock_quote');
// 获取股票数据
$data = $this->api->get_stock_data($atts['symbol']);
if (isset($data['error'])) {
return '<div class="sft-error">错误: ' . esc_html($data['error']) . '</div>';
}
// 生成唯一ID用于JavaScript
$widget_id = 'sft-stock-' . uniqid();
// 格式化输出
ob_start();
?>
<div id="<?php echo esc_attr($widget_id); ?>" class="sft-stock-quote sft-style-<?php echo esc_attr($atts['style']); ?>"
data-symbol="<?php echo esc_attr($atts['symbol']); ?>"
data-refresh="<?php echo esc_attr($atts['refresh']); ?>">
<div class="sft-stock-header">
<h4 class="sft-stock-symbol"><?php echo esc_html($atts['symbol']); ?></h4>
<span class="sft-stock-price"><?php echo esc_html($this->format_currency($data['price'], $atts['currency'])); ?></span>
</div>
<?php if ($atts['show_change'] === 'true') : ?>
<div class="sft-stock-change <?php echo (floatval($data['change']) >= 0) ? 'sft-positive' : 'sft-negative'; ?>">
<span class="sft-change-amount"><?php echo esc_html($data['change']); ?></span>
<span class="sft-change-percent">(<?php echo esc_html($data['change_percent']); ?>)</span>
</div>
<?php endif; ?>
<?php if ($atts['show_volume'] === 'true') : ?>
<div class="sft-stock-volume">
成交量: <?php echo esc_html($this->format_number($data['volume'])); ?>
</div>
<?php endif; ?>
<div class="sft-stock-footer">
<span class="sft-updated-time">更新于: <?php echo esc_html(date('H:i:s')); ?></span>
</div>
</div>
<script>
jQuery(document).ready(function($) {
// 自动刷新功能
var refreshInterval = <?php echo intval($atts['refresh']) * 1000; ?>;
if (refreshInterval > 0) {
setInterval(function() {
$.ajax({
url: sft_ajax_object.ajax_url,
type: 'POST',
data: {
action: 'sft_get_stock_data',
symbol: '<?php echo esc_js($atts['symbol']); ?>',
nonce: sft_ajax_object.nonce
},
success: function(response) {
if (!response.error) {
$('#<?php echo esc_js($widget_id); ?> .sft-stock-price').text(response.price);
$('#<?php echo esc_js($widget_id); ?> .sft-change-amount').text(response.change);
$('#<?php echo esc_js($widget_id); ?> .sft-change-percent').text('(' + response.change_percent + ')');
$('#<?php echo esc_js($widget_id); ?> .sft-stock-volume').text('成交量: ' + response.volume);
$('#<?php echo esc_js($widget_id); ?> .sft-updated-time').text('更新于: ' + new Date().toLocaleTimeString());
// 更新颜色
var changeClass = parseFloat(response.change) >= 0 ? 'sft-positive' : 'sft-negative';
$('#<?php echo esc_js($widget_id); ?> .sft-stock-change')
.removeClass('sft-positive sft-negative')
.addClass(changeClass);
}
}
});
}, refreshInterval);
}
});
</script>
<?php
return ob_get_clean();
}
/**
* 股票行情滚动条短代码
*/
public function stock_ticker_shortcode($atts) {
$atts = shortcode_atts(array(
'symbols' => 'AAPL,MSFT,GOOGL,AMZN,TSLA',
'speed' => '50',
'direction' => 'left',
'separator' => '|',
'show_change' => 'true'
), $atts, 'stock_ticker');
$symbols = array_map('trim', explode(',', $atts['symbols']));
$data = $this->api->get_multiple_stocks($atts['symbols']);
ob_start();
?>
<div class="sft-stock-ticker"
data-speed="<?php echo esc_attr($atts['speed']); ?>"
data-direction="<?php echo esc_attr($atts['direction']); ?>">
<div class="sft-ticker-container">
<div class="sft-ticker-content">
<?php foreach ($symbols as $symbol) :
if (isset($data[$symbol]) && !isset($data[$symbol]['error'])) :
$stock = $data[$symbol];
?>
<div class="sft-ticker-item">
<span class="sft-ticker-symbol"><?php echo esc_html($symbol); ?></span>
<span class="sft-ticker-price"><?php echo esc_html($stock['price']); ?></span>
<?php if ($atts['show_change'] === 'true') : ?>
<span class="sft-ticker-change <?php echo (floatval($stock['change']) >= 0) ? 'sft-positive' : 'sft-negative'; ?>">
<?php echo esc_html($stock['change']); ?> (<?php echo esc_html($stock['change_percent']); ?>)
</span>
<?php endif; ?>
<span class="sft-ticker-separator"><?php echo esc_html($atts['separator']); ?></span>
</div>
<?php endif; endforeach; ?>
</div>
</div>
</div>
<style>
.sft-stock-ticker {
overflow: hidden;
white-space: nowrap;
background: #f8f9fa;
padding: 10px;
border-radius: 4px;
}
.sft-ticker-container {
display: inline-block;
animation: sft-ticker-<?php echo esc_attr($atts['direction']); ?> <?php echo esc_attr(100000/$atts['speed']); ?>s linear infinite;
}
@keyframes sft-ticker-left {
0% { transform: translateX(100%); }
100% { transform: translateX(-100%); }
}
@keyframes sft-ticker-right {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
</style>
<?php
return ob_get_clean();
}
/**
* 货币转换器短代码
*/
public function currency_converter_shortcode($atts) {
$atts = shortcode_atts(array(
'from' => 'USD',
'to' => 'EUR',
'amount' => '1',
'show_form' => 'true',
'update_frequency' => '3600'
), $atts, 'currency_converter');
$conversion_rate = $this->get_currency_rate($atts['from'], $atts['to']);
$converted_amount = floatval($atts['amount']) * $conversion_rate;
ob_start();
?>
<div class="sft-currency-converter" data-from="<?php echo esc_attr($atts['from']); ?>"
data-to="<?php echo esc_attr($atts['to']); ?>">
<div class="sft-conversion-result">
<p>
<?php echo esc_html($atts['amount']); ?> <?php echo esc_html($atts['from']); ?> =
<strong><?php echo number_format($converted_amount, 4); ?></strong> <?php echo esc_html($atts['to']); ?>
</p>
<p class="sft-rate-info">
汇率: 1 <?php echo esc_html($atts['from']); ?> = <?php echo number_format($conversion_rate, 6); ?> <?php echo esc_html($atts['to']); ?>
</p>
</div>
<?php if ($atts['show_form'] === 'true') : ?>
<div class="sft-converter-form">
<div class="sft-form-row">
<input type="number" class="sft-amount" value="<?php echo esc_attr($atts['amount']); ?>"
step="0.01" min="0" placeholder="金额">
<select class="sft-from-currency">
<?php echo $this->get_currency_options($atts['from']); ?>
</select>
<span class="sft-convert-arrow">→</span>
<select class="sft-to-currency">
<?php echo $this->get_currency_options($atts['to']); ?>
</select>
<button type="button" class="sft-convert-button">转换</button>
</div>
<div class="sft-form-result" style="display:none;">
<p class="sft-result-text"></p>
</div>
</div>
<script>
jQuery(document).ready(function($) {
$('.sft-convert-button').on('click', function() {
var $converter = $(this).closest('.sft-currency-converter');
var amount = $converter.find('.sft-amount').val();
var from = $converter.find('.sft-from-currency').val();
var to = $converter.find('.sft-to-currency').val();
$.ajax({
url: sft_ajax_object.ajax_url,
type: 'POST',
data: {
action: 'sft_convert_currency',
amount: amount,
from: from,
to: to,
nonce: sft_ajax_object.nonce
},
success: function(response) {
if (response.success) {
$converter.find('.sft-result-text').html(
amount + ' ' + from + ' = <strong>' + response.result + '</strong> ' + to
);
$converter.find('.sft-form-result').show();
}
}
});
});
});
</script>
<?php endif; ?>
</div>
<?php
return ob_get_clean();
}
/**
* 获取货币汇率
*/
private function get_currency_rate($from, $to) {
$cache_key = 'currency_rate_' . $from . '_' . $to;
$cached_rate = get_transient($cache_key);
if ($cached_rate !== false) {
return $cached_rate;
}
// 使用免费汇率API
$api_url = "https://api.exchangerate-api.com/v4/latest/{$from}";
$response = wp_remote_get($api_url);
if (!is_wp_error($response)) {
$data = json_decode(wp_remote_retrieve_body($response), true);
if (isset($data['rates'][$to])) {
$rate = $data['rates'][$to];
set_transient($cache_key, $rate, HOUR_IN_SECONDS);
return $rate;
}
}
// 备用汇率(如果API失败)
$fallback_rates = array(
'USD_EUR' => 0.85,
'EUR_USD' => 1.18,
'USD_GBP' => 0.73,
'GBP_USD' => 1.37,
'USD_JPY' => 110.5,
'JPY_USD' => 0.0091
);
$key = $from . '_' . $to;
return isset($fallback_rates[$key]) ? $fallback_rates[$key] : 1.0;
}
/**
* 获取货币选项
*/
private function get_currency_options($selected) {
$currencies = array(
'USD' => '美元',
'EUR' => '欧元',
'GBP' => '英镑',
'JPY' => '日元',
'CNY' => '人民币',
'CAD' => '加元',
'AUD' => '澳元',
'CHF' => '瑞士法郎',
'HKD' => '港币'
);
$options = '';
foreach ($currencies as $code => $name) {
$selected_attr = ($code === $selected) ? ' selected' : '';
$options .= "<option value="{$code}"{$selected_attr}>{$name} ({$code})</option>";
}
return $options;
}
/**
* 格式化货币
*/
private function format_currency($amount, $currency) {
$formats = array(
'USD' => '$%s',
'EUR' => '€%s',
'GBP' => '£%s',
'JPY' => '¥%s',
'CNY' => '¥%s'
);
$format = isset($formats[$currency]) ? $formats[$currency] : '%s ' . $currency;
return sprintf($format, number_format(floatval($amount), 2));
}
/**
* 格式化数字(千位分隔符)
*/
private function format_number($number) {
return number_format(floatval($number));
}
/**
* 注册Gutenberg块
*/
public function register_gutenberg_blocks() {
// 检查Gutenberg是否可用
if (!function_exists('register_block_type')) {
return;
}
// 注册股票报价块
register_block_type('sft/stock-quote', array(
'editor_script' => 'sft-gutenberg-blocks',
'render_callback' => array($this, 'render_stock_quote_block'),
'attributes' => array(
'symbol' => array(
'type' => 'string',
'default' => 'AAPL'
),
'showChange' => array(
'type' => 'boolean',
'default' => true
),
'refreshInterval' => array(
'type' => 'number',
'default' => 60
)
)
));
}
/**
* 渲染Gutenberg块
*/
public function render_stock_quote_block($attributes) {
return $this->stock_quote_shortcode(array(
'symbol' => $attributes['symbol'],
'show_change' => $attributes['showChange'] ? 'true' : 'false',
'refresh' => $attributes['refreshInterval']
));
}
}
第五章:创建管理后台界面
5.1 设置页面开发
<?php
// admin/class-admin-settings.php
class SFT_Admin_Settings {
private $options_page;
public function __construct() {
add_action('admin_menu', array($this, 'add_admin_menu'));
add_action('admin_init', array($this, 'register_settings'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets'));
// 添加插件设置链接
add_filter('plugin_action_links_' . SFT_PLUGIN_BASENAME, array($this, 'add_settings_link'));
}
/**
* 添加管理菜单
*/
public function add_admin_menu() {
$this->options_page = add_menu_page(
'股票财经工具设置',
'股票财经工具',
'manage_options',
'sft-settings',
array($this, 'render_settings_page'),
'dashicons-chart-line',
80
);
// 添加子菜单
add_submenu_page(
'sft-settings',
'API设置',
'API设置',
'manage_options',
'sft-settings',
array($this, 'render_settings_page')
);
add_submenu_page(
'sft-settings',
'小工具管理',
'小工具管理',
'manage_options',
'sft-widgets',
array($this, 'render_widgets_page')
);
add_submenu_page(
'sft-settings',
'数据缓存',
'数据缓存',
'manage_options',
'sft-cache',
array($this, 'render_cache_page')
);
add_submenu_page(
'sft-settings',
'使用说明',
'使用说明',
'manage_options',
'sft-documentation',
array($this, 'render_documentation_page')
);
}
/**
* 渲染设置页面
*/
public function render_settings_page() {
if (!current_user_can('manage_options')) {
