|null slug => version from manifest.json when cached */ function chinaready_wp_cdn_manifest_versions(string $type): ?array { static $manifest = null; if ($manifest === null) { $response = wp_remote_get(chinaready_wp_cdn_origin() . '/manifest.json', ['timeout' => 5]); if (is_wp_error($response)) { $manifest = false; return null; } $body = wp_remote_retrieve_body($response); $decoded = json_decode($body, true); $manifest = is_array($decoded) ? $decoded : false; } if ($manifest === false || !is_array($manifest)) { return null; } if ($type === 'plugin') { $items = $manifest['plugins'] ?? []; } elseif ($type === 'theme') { $items = $manifest['themes'] ?? []; } else { return null; } $map = []; foreach ($items as $item) { if (!is_array($item) || empty($item['slug']) || empty($item['version'])) { continue; } $map[$item['slug']] = $item['version']; } return $map; } /** * @return string|null Installed plugin version for slug */ function chinaready_wp_cdn_installed_plugin_version(string $slug): ?string { if (!function_exists('get_plugins')) { require_once ABSPATH . 'wp-admin/includes/plugin.php'; } foreach (get_plugins() as $file => $data) { if (strpos($file, $slug . '/') === 0) { return $data['Version'] ?? null; } } return null; } /** * @return string|null Installed theme version for slug */ function chinaready_wp_cdn_installed_theme_version(string $slug): ?string { $theme = wp_get_theme($slug); if (!$theme->exists()) { return null; } return $theme->get('Version') ?: null; } /** * Rewrite a local WordPress asset URL to the CDN when version matches snapshot. */ function chinaready_wp_cdn_rewrite_src(string $src): string { if ($src === '' || strpos($src, 'wp-includes') === false && strpos($src, 'wp-content') === false) { return $src; } $parts = wp_parse_url($src); if (!is_array($parts) || empty($parts['path'])) { return $src; } $path = $parts['path']; $query = []; if (!empty($parts['query'])) { parse_str($parts['query'], $query); } $origin = chinaready_wp_cdn_origin(); if (preg_match('#/wp-includes/(.+)$#', $path, $matches)) { $core_version = get_bloginfo('version'); if (!$core_version) { return $src; } return $origin . '/wp/' . rawurlencode($core_version) . '/wp-includes/' . $matches[1]; } if (preg_match('#/wp-content/plugins/([^/]+)/(.+)$#', $path, $matches)) { $slug = $matches[1]; $rest = $matches[2]; $version = $query['ver'] ?? chinaready_wp_cdn_installed_plugin_version($slug); if (!$version) { return $src; } $manifest = chinaready_wp_cdn_manifest_versions('plugin'); if ($manifest !== null && (!isset($manifest[$slug]) || $manifest[$slug] !== $version)) { return $src; } return $origin . '/wp-content/plugins/' . rawurlencode($slug) . '@' . rawurlencode($version) . '/' . $rest; } if (preg_match('#/wp-content/themes/([^/]+)/(.+)$#', $path, $matches)) { $slug = $matches[1]; $rest = $matches[2]; $version = $query['ver'] ?? chinaready_wp_cdn_installed_theme_version($slug); if (!$version) { return $src; } $manifest = chinaready_wp_cdn_manifest_versions('theme'); if ($manifest !== null && (!isset($manifest[$slug]) || $manifest[$slug] !== $version)) { return $src; } return $origin . '/wp-content/themes/' . rawurlencode($slug) . '@' . rawurlencode($version) . '/' . $rest; } return $src; } add_filter('script_loader_src', 'chinaready_wp_cdn_rewrite_src', 20); add_filter('style_loader_src', 'chinaready_wp_cdn_rewrite_src', 20);