<?php
/*
Plugin Name: Static JSON Cacher
Plugin URI: https://example.com/
Description: Generates static JSON files for posts, category indexes, and a global search index, then uploads them to a remote server via FTP. This minimizes direct database queries and improves performance.
Version: 1.3
Author: Your Name
Author URI: https://example.com/
License: GPL2
*/
// Exit if accessed directly.
if (!defined(‘ABSPATH’)) {
exit;
}
/———————————————– | Configuration Constants ———————————————–/
// FTP Settings
if (!defined(‘SJC_FTP_SERVER’)) {
define(‘SJC_FTP_SERVER’, ‘ftp.gb.stackcp.com’);
}
if (!defined(‘SJC_FTP_USER’)) {
define(‘SJC_FTP_USER’, ‘[email protected]’);
}
if (!defined(‘SJC_FTP_PASS’)) {
define(‘SJC_FTP_PASS’, ‘8efg87uxwx’);
}
// Remote base directory where JSON files will be stored (e.g., “/public_html/static/”)
if (!defined(‘SJC_REMOTE_BASE’)) {
define(‘SJC_REMOTE_BASE’, ‘/public_html/static/’); // Absolute path on FTP server
}
// Remote URL base (used on the front end to fetch JSON files)
if (!defined(‘SJC_REMOTE_URL_BASE’)) {
define(‘SJC_REMOTE_URL_BASE’, ‘https://img.newsletter.tf/cdn/’);
}
/———————————————– | Helper: Ensure Remote Directory Exists ———————————————–/
function sjc_ensure_remote_directory($conn, $remote_path) {
$remote_path = trim($remote_path, ‘/’);
$parts = array_filter(explode(‘/’, $remote_path));
ftp_chdir($conn, ‘/’); // Start at root
foreach ($parts as $part) {
error_log("Static JSON Cacher: Checking directory: $part");
if (!@ftp_chdir($conn, $part)) {
if (ftp_mkdir($conn, $part)) {
error_log("Static JSON Cacher: Created directory: $part");
ftp_chdir($conn, $part);
} else {
error_log("Static JSON Cacher: FAILED to create: $part");
return false;
}
}
}
return true;
}
/———————————————– | Helper: Upload JSON File via FTP ———————————————–/
function sjc_upload_json_to_remote($local_file, $remote_path) {
$conn = ftp_connect(SJC_FTP_SERVER);
if (!$conn) {
error_log(‘Static JSON Cacher: FTP connection failed.’);
return false;
}
if (!ftp_login($conn, SJC_FTP_USER, SJC_FTP_PASS)) {
error_log(‘Static JSON Cacher: FTP login failed.’);
ftp_close($conn);
return false;
}
ftp_pasv($conn, true);
// Log the full remote path
error_log("Static JSON Cacher: Full remote path: $remote_path");
// Ensure directory exists
$remote_dir = dirname($remote_path);
if (!sjc_ensure_remote_directory($conn, $remote_dir)) {
error_log("Static JSON Cacher: Directory creation failed for $remote_dir");
ftp_close($conn);
return false;
}
// Upload the file
$upload = ftp_put($conn, $remote_path, $local_file, FTP_BINARY);
if (!$upload) {
error_log("Static JSON Cacher: FTP upload failed for $remote_path");
$error = error_get_last();
error_log("Static JSON Cacher: PHP error: " . $error['message']);
}
ftp_close($conn);
return $upload;
}
/———————————————– | Generate/Update JSON for a Single Post ———————————————–/
function sjc_update_post_json($post_id) {
// Skip revisions or non-published posts.
if (wp_is_post_revision($post_id) || get_post_status($post_id) != ‘publish’) {
return;
}
$slug = get_post_field('post_name', $post_id);
$post_data = [
'id' => $post_id,
'title' => get_the_title($post_id),
'excerpt' => get_the_excerpt($post_id),
'content' => apply_filters('the_content', get_post_field('post_content', $post_id)),
'image' => get_the_post_thumbnail_url($post_id),
'date' => get_the_date('Y-m-d H:i:s', $post_id),
'slug' => $slug,
'categories' => wp_get_post_categories($post_id, ['fields' => 'slugs'])
];
// Generate JSON data.
$json_data = json_encode($post_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$temp_file = wp_tempnam($slug . '.json');
file_put_contents($temp_file, $json_data);
error_log("Static JSON Cacher: Local JSON created at: $temp_file");
// Define remote path for the post JSON.
$remote_path = SJC_REMOTE_BASE . 'posts/post-' . $slug . '.json';
sjc_upload_json_to_remote($temp_file, $remote_path);
// Remove temporary file.
unlink($temp_file);
// Update category JSON for each category this post belongs to.
if (!empty($post_data['categories'])) {
foreach ($post_data['categories'] as $cat_slug) {
sjc_generate_category_json($cat_slug);
}
}
// Update the global search index.
sjc_generate_search_index();
}
add_action(‘save_post’, ‘sjc_update_post_json’);
/———————————————– | Generate/Update JSON for a Category ———————————————–/
function sjc_generate_category_json($category_slug) {
$args = [
‘category_name’ => $category_slug,
‘posts_per_page’ => 20,
‘post_status’ => ‘publish’
];
$query = new WP_Query($args);
$posts = [];
while ($query->have_posts()) {
$query->the_post();
$posts[] = [
'id' => get_the_ID(),
'title' => get_the_title(),
'excerpt' => get_the_excerpt(),
'image' => get_the_post_thumbnail_url(),
'date' => get_the_date('Y-m-d H:i:s'),
'slug' => get_post_field('post_name')
];
}
wp_reset_postdata();
// Generate JSON data.
$json_data = json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$temp_file = wp_tempnam('category-' . $category_slug . '.json');
file_put_contents($temp_file, $json_data);
error_log("Static JSON Cacher: Local JSON created at: $temp_file");
// Define remote path for the category JSON.
$remote_path = SJC_REMOTE_BASE . 'categories/category-' . $category_slug . '.json';
sjc_upload_json_to_remote($temp_file, $remote_path);
// Remove temporary file.
unlink($temp_file);
}
/———————————————– | Generate/Update Global Search Index JSON ———————————————–/
function sjc_generate_search_index() {
$args = [
‘posts_per_page’ => -1,
‘post_status’ => ‘publish’
];
$query = new WP_Query($args);
$posts = [];
while ($query->have_posts()) {
$query->the_post();
$posts[] = [
'id' => get_the_ID(),
'title' => get_the_title(),
'excerpt' => get_the_excerpt(),
'content' => wp_strip_all_tags(get_the_content()),
'image' => get_the_post_thumbnail_url(),
'slug' => get_post_field('post_name')
];
}
wp_reset_postdata();
// Generate JSON data.
$json_data = json_encode($posts, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$temp_file = wp_tempnam('search-index.json');
file_put_contents($temp_file, $json_data);
error_log("Static JSON Cacher: Local JSON created at: $temp_file");
// Define remote path for the search index JSON.
$remote_path = SJC_REMOTE_BASE . 'search-index.json';
sjc_upload_json_to_remote($temp_file, $remote_path);
// Remove temporary file.
unlink($temp_file);
}
/———————————————– | Serve Post Content from CDN (No Theme Changes) ———————————————–/
function sjc_replace_post_content_with_cdn($content) {
// Only affect single post pages
if (!is_single()) {
return $content;
}
$post_slug = get_post_field('post_name', get_the_ID());
$cdn_url = SJC_REMOTE_URL_BASE . 'posts/post-' . $post_slug . '.json';
// Fetch JSON from CDN with caching
$transient_key = 'sjc_cdn_post_' . $post_slug;
$post_data = get_transient($transient_key);
if (false === $post_data) {
$response = wp_remote_get($cdn_url);
if (!is_wp_error($response)) {
$post_data = json_decode($response['body'], true);
set_transient($transient_key, $post_data, HOUR_IN_SECONDS); // Cache for 1 hour
}
}
if (isset($post_data['content'])) {
return $post_data['content'];
}
// Fallback to original content if CDN fails
return $content;
}
add_filter(‘the_content’, ‘sjc_replace_post_content_with_cdn’);
/———————————————– | Replace Post Title with CDN Data ———————————————–/
function sjc_replace_post_title_with_cdn($title, $id = null) {
if (!is_single() || !$id) {
return $title;
}
$post_slug = get_post_field('post_name', $id);
$cdn_url = SJC_REMOTE_URL_BASE . 'posts/post-' . $post_slug . '.json';
// Fetch JSON from CDN with caching
$transient_key = 'sjc_cdn_post_' . $post_slug;
$post_data = get_transient($transient_key);
if (false === $post_data) {
$response = wp_remote_get($cdn_url);
if (!is_wp_error($response)) {
$post_data = json_decode($response['body'], true);
set_transient($transient_key, $post_data, HOUR_IN_SECONDS); // Cache for 1 hour
}
}
if (isset($post_data['title'])) {
return $post_data['title'];
}
return $title;
}
add_filter(‘the_title’, ‘sjc_replace_post_title_with_cdn’, 10, 2);
/———————————————– | Replace Excerpt with CDN Data ———————————————–/
function sjc_replace_post_excerpt_with_cdn($excerpt) {
if (!is_single()) {
return $excerpt;
}
$post_slug = get_post_field('post_name', get_the_ID());
$cdn_url = SJC_REMOTE_URL_BASE . 'posts/post-' . $post_slug . '.json';
// Fetch JSON from CDN with caching
$transient_key = 'sjc_cdn_post_' . $post_slug;
$post_data = get_transient($transient_key);
if (false === $post_data) {
$response = wp_remote_get($cdn_url);
if (!is_wp_error($response)) {
$post_data = json_decode($response['body'], true);
set_transient($transient_key, $post_data, HOUR_IN_SECONDS); // Cache for 1 hour
}
}
if (isset($post_data['excerpt'])) {
return $post_data['excerpt'];
}
return $excerpt;
}
add_filter(‘get_the_excerpt’, ‘sjc_replace_post_excerpt_with_cdn’);
/———————————————– | Replace Featured Image with CDN Data ———————————————–/
function sjc_replace_post_thumbnail_with_cdn($html, $post_id, $post_thumbnail_id, $size, $attr) {
$post_slug = get_post_field(‘post_name’, $post_id);
$cdn_url = SJC_REMOTE_URL_BASE . ‘posts/post-‘ . $post_slug . ‘.json’;
// Fetch JSON from CDN with caching
$transient_key = 'sjc_cdn_post_' . $post_slug;
$post_data = get_transient($transient_key);
if (false === $post_data) {
$response = wp_remote_get($cdn_url);
if (!is_wp_error($response)) {
$post_data = json_decode($response['body'], true);
set_transient($transient_key, $post_data, HOUR_IN_SECONDS); // Cache for 1 hour
}
}
if (isset($post_data['image'])) {
return '<img src="' . esc_url($post_data['image']) . '" alt="' . esc_attr(get_the_title()) . '">';
}
return $html;
}
add_filter(‘post_thumbnail_html’, ‘sjc_replace_post_thumbnail_with_cdn’, 10, 5);
/———————————————– | Admin Notice (Optional) ———————————————–/
function sjc_admin_notice() {
?>
Static JSON Cacher is active. It will generate static JSON files for your posts, categories, and search index and upload them to your remote server via FTP.
<?php
}
add_action(‘admin_notices’, ‘sjc_admin_notice’);