WP-B2增加文章随机API封面

最近写的文章几乎都是纯文字的(其实是懒得发图)导致页面不太美观,我就在想可不可以为文章增加一个随机图片呢,想着以为很简单就搞定了,实际操作起来出现了很多问题,不过最终还是成功解决了。

一、创建图片API接口

首先要先做一个图片API接口,这个接口要满足支持本地服务器的图片,也要支持外链图片。并且增加一个随机机制,使其在被打开页面的时候,多篇文章的封面图片不会相同,刷新页面也可以随机变化,还需要增加一个缓存机制,缓存图片并尽量减少请求次数。

API文件结构:

/文件/img/ 目录下:
├─ api.php           # API核心(网址/api.php即可使用,从config.php读取所有配置)
├─ config.php           # 主配置(保留外部链接配置入口,关联独立文件)
├─ external-links-list.php  # 单独管理外部链接(仅放具体URL,可随时修改)
└─ local-images/        # 本地图片文件夹

1.首先创建一个api.php的文件并放入以下代码

这是用来访问api的入口

[content_hide]

<?php
/**
 * 随机图片API(带缓存优化版)
 * 特点:1. 服务器缓存可用图片列表 2. 允许浏览器/CDN缓存API响应 3. 平衡缓存效率与随机性
 */

// 1. 优化的缓存控制(允许浏览器/CDN缓存,设置合理有效期)
// public: 允许中间缓存(如CDN)缓存
// max-age=300: 缓存5分钟(可根据需求调整,建议300-3600秒)
// must-revalidate: 缓存过期后必须重新验证,避免使用 stale 数据
header("Cache-Control: public, max-age=300, must-revalidate");
// 兼容HTTP/1.0(Pragma在HTTP/1.1中被Cache-Control替代,仅作兼容)
header("Pragma: cache");
// Vary: 告诉CDN/代理根据Accept-Encoding区分缓存(不同压缩方式的响应分开存)
header("Vary: Accept-Encoding");
// 安全头:防止浏览器嗅探错误的MIME类型
header("X-Content-Type-Options: nosniff");

// 2. 加载配置
require_once 'config.php';

// 3. 服务器端缓存可用图片列表(逻辑不变,减少IO/外部请求)
$cache_key = 'random_image_list_cache';
$cache_expire = 300; // 图片列表缓存5分钟(与API响应缓存时长保持一致,可独立调整)
$all_images = [];

// 尝试从缓存读取图片列表
if (function_exists('get_transient')) {
    // WordPress环境:用transient缓存
    $cached_list = get_transient($cache_key);
    if ($cached_list !== false && is_array($cached_list)) {
        $all_images = $cached_list;
    }
} else {
    // 非WP环境:用文件缓存
    $cache_file = __DIR__ . '/image_list_cache.txt';
    if (file_exists($cache_file) && time() - filemtime($cache_file) < $cache_expire) {
        $cached_content = file_get_contents($cache_file);
        $all_images = json_decode($cached_content, true);
    }
}

// 缓存不存在/过期:重新收集图片列表并缓存
if (empty($all_images)) {
    // 处理本地图片
    if (!empty($local['folder']) && !empty($local['url_prefix']) && is_dir($local['folder'])) {
        $allowed_exts = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp'];
        $dir = opendir($local['folder']);
        while (($file = readdir($dir)) !== false) {
            if ($file == '.' || $file == '..') continue;
            $file_path = $local['folder'] . $file;
            if (is_file($file_path)) {
                $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));
                if (in_array($ext, $allowed_exts)) {
                    $url_prefix = rtrim($local['url_prefix'], '/') . '/';
                    $local_url = $url_prefix . rawurlencode($file);
                    $all_images[] = $local_url;
                }
            }
        }
        closedir($dir);
    }

    // 处理外部链接
    if (!empty($external) && is_array($external)) {
        foreach ($external as $link) {
            $link = trim($link);
            if (strpos($link, 'http://') === 0 || strpos($link, 'https://') === 0) {
                $all_images[] = $link;
            }
        }
    }

    // 缓存新列表(非空时)
    if (!empty($all_images)) {
        if (function_exists('set_transient')) {
            set_transient($cache_key, $all_images, $cache_expire);
        } else {
            $cache_file = __DIR__ . '/image_list_cache.txt';
            file_put_contents($cache_file, json_encode($all_images));
        }
    }
}

// 检查可用图片
if (empty($all_images)) {
    die("没有可用图片");
}

// 4. 随机选择图片(缓存有效期内,相同客户端会看到相同图片,符合缓存逻辑)
$total = count($all_images);
// 用微秒时间戳+客户端IP片段作为种子,确保不同客户端缓存的图片不同
$seed = (int)(microtime(true) * 1000000) + crc32($_SERVER['REMOTE_ADDR'] ?? 'unknown');
mt_srand($seed); 
$random_index = mt_rand(0, $total - 1);

// 5. 跳转到随机图片
$random_image = $all_images[$random_index];
header("Location: {$random_image}", true, 302);
exit;

[/content_hide]

2.创建文件config.php并放入以下代码

这个文件是api的配置用来管理本地图片和外部的链接(需要更改url_prefix里的url)

[content_hide]

<?php
/**
 * API主配置文件(保留外部链接配置入口,具体链接在external-links-list.php中)
 */

// 1. 本地图片配置(原有逻辑不变)
$local = [
    'folder' => __DIR__ . '/local-images/', // 本地图片服务器绝对路径
    'url_prefix' => 'https://ceshi1.tkzyz.com/wp-content/themes/b2child/img/local-images/' // 本地图片访问URL前缀
];

// 2. 外部链接配置(保留入口,具体链接从独立文件引入)
$external = []; // 初始化外部链接数组(API会读取这个数组)
$external_config_file = __DIR__ . '/external-links-list.php'; // 指向单独的链接文件

// 引入独立的外部链接列表(核心:关联external-links-list.php)
if (file_exists($external_config_file)) {
    require_once $external_config_file;
    // 将独立文件中的$external_links赋值给$external(确保API能识别)
    if (!empty($external_links) && is_array($external_links)) {
        $external = $external_links;
    }
} else {
    // 容错提示:若独立文件不存在,给出明确指引(避免API报错)
    die("外部链接配置文件缺失:请在 " . __DIR__ . " 目录下创建 external-links-list.php 文件,并添加外部图片链接");
}

[/content_hide]

3.创建文件external-links-list.php并放入以下代码

这个文件是用来管理增加或删除外部链接的图片

[content_hide]

<?php
/**
 * 外部图片链接列表(单独维护,无需改动其他文件)
 * 规则:1. 每个链接占一行 2. 必须以http/https开头 3. 空行和#注释行会自动过滤
 */

// 定义外部链接数组(变量名需与config.php中一致,确保能被读取)
$external_links = [
    // 已有的外部链接
    'https://random-picture.aneu.cn/acg-webp/0072vf1pgy1foxkfrmrlgj31hc0u0wt1.webp',
    'https://random-picture.aneu.cn/acg-webp/0072vf1pgy1foxlhvnh9qj31hc0u0tmu.webp',
    
    // 新增外部链接直接在这里加(示例)
//    'https://picsum.photos/id/100/800/600',
//    'https://picsum.photos/id/200/800/600',
    
    // # 这是注释行,不会被读取
    // '错误链接(会被过滤)' // 没有http开头,自动排除
];

// 自动过滤无效链接(容错:避免手动添加错误导致API失效)
$external_links = array_filter($external_links, function($link) {
    $link = trim($link);
    // 只保留以http/https开头的有效链接
    return strpos($link, 'http://') === 0 || strpos($link, 'https://') === 0;
});

[/content_hide]

4.创建文件夹local-images

这个文件夹是放入本地图片文件的,放入即可增加,删除即可减少

二、修改主题文件

目录/wp-content/themes/b2child

文件functions.php(最好修改子主题的文件防止以后升级主题要重新修改)

将以下代码放入文件functions.php的底部(本文件的其他位置也可以,记得注释防止忘记)

[content_hide]

//自动外部API封面
// 未设置封面时自动调用API图片
// B2主题默认封面替换(平衡多样性与请求量)
function b2_replace_default_thumbnail_with_random_api() {
    if (is_admin() || wp_doing_ajax() || wp_doing_cron()) {
        return;
    }

    $original_default_img = 'https://ceshi1.tkzyz.com/wp-content/themes/b2/Assets/fontend/images/default-img.jpg';
    $api_base = 'https://ceshi1.tkzyz.com/wp-content/themes/b2child/img/api.php';

    if (!is_singular() && !is_archive() && !is_home()) {
        return;
    }

    ob_start(function($buffer) use ($original_default_img, $api_base) {
        $pattern = '/' . preg_quote($original_default_img, '/') . '/';
        
        // 1. 时间分段(保留:15分钟更新一次,减少请求)
        $time_segment = floor(time() / 900); // 15分钟
        
        // 2. 核心:为每个文章生成唯一标识(确保不同文章有不同图片)
        // 单篇文章页:用文章ID作为唯一标识
        // 列表页(首页/分类页):从HTML中提取文章ID(B2主题会在封面容器添加data-id)
        $post_unique_ids = [];
        if (preg_match_all('/data-post-id="(\d+)"/', $buffer, $matches)) {
            $post_unique_ids = $matches[1]; // 提取页面中所有文章的ID
        }
        $current_post_id = is_singular() ? get_queried_object_id() : '';

        // 3. 替换逻辑:结合文章ID+位置,确保每个封面唯一
        $replaced_buffer = preg_replace_callback($pattern, function($matches) use (
            $api_base, $time_segment, $post_unique_ids, $current_post_id
        ) {
            // 为当前替换的封面匹配对应的文章ID(列表页用提取的ID,单页用当前文章ID)
            static $id_index = 0;
            $post_id = '';
            if (is_singular() && $current_post_id) {
                $post_id = $current_post_id; // 单篇文章页:固定用当前文章ID
            } elseif (!empty($post_unique_ids[$id_index])) {
                $post_id = $post_unique_ids[$id_index]; // 列表页:按顺序匹配文章ID
                $id_index++;
            } else {
                $post_id = uniqid(); // 极端情况:无ID时用唯一随机值
            }

            // 生成唯一标识:文章ID+时间分段+位置(确保不同文章/位置不同)
            static $position = 0;
            $position++;
            $unique_identifier = md5($post_id . $time_segment . $position);
            $random_param = 'rand=' . substr($unique_identifier, 0, 12); // 更长的参数,减少重复

            // 缓存键:基于文章ID+时间+位置(确保每个文章封面独立缓存)
            $cache_key = 'b2_thumb_unique_' . $post_id . '_' . $time_segment . '_' . $position;
            $cached_url = '';
            if (function_exists('get_transient') && function_exists('set_transient')) {
                $cached_url = get_transient($cache_key);
                if (!$cached_url) {
                    $cached_url = $api_base . '?' . $random_param;
                    set_transient($cache_key, $cached_url, 900); // 15分钟缓存
                }
            } else {
                $cached_url = $api_base . '?' . $random_param;
            }

            // 保留浏览器缓存优化,同时确保每个封面URL唯一
            return $cached_url 
                . '" loading="lazy" '
                . 'onerror="this.onerror=null; this.src=\'\'; this.style.display=\'none\'; this.alt=\'封面加载失败\''
                . '; this.setAttribute(\'cache-control\', \'max-age=86400\')';
        }, $buffer);

        return $replaced_buffer;
    });
}

add_action('template_redirect', 'b2_replace_default_thumbnail_with_random_api', 20);

[/content_hide]

完成以上操作即可实现不设置封面的情况下默认封面变为调用随机API封面

 

 

THE END
喜欢就支持一下吧