*/ private function config(): array { static $cache = null; if ($cache !== null) { return $cache; } if (class_exists('AKSRT_Settings')) { $s = AKSRT_Settings::get_all(); } else { $s = []; } $cache = [ 'connector' => trim((string) ($s['seo_connector'] ?? '-')) ?: '-', 'site_title' => trim((string) ($s['seo_title'] ?? '')), 'description' => trim((string) ($s['seo_description'] ?? ($s['site_description'] ?? ''))), 'keywords' => trim((string) ($s['seo_keywords'] ?? '')), 'canonical' => !isset($s['canonical_tag']) || !empty($s['canonical_tag']), 'og_enabled' => empty($s['disable_og_meta']), 'jsonld' => !isset($s['seo_jsonld']) || !empty($s['seo_jsonld']), 'jsonld_single' => !isset($s['seo_jsonld_single']) || !empty($s['seo_jsonld_single']), 'jsonld_page' => !isset($s['seo_jsonld_page']) || !empty($s['seo_jsonld_page']), 'default_image' => trim((string) ($s['seo_image_default'] ?? '')), 'always_default' => !empty($s['seo_image_always_default']), ]; return $cache; } /** * 解析当前请求的规范 SEO 负载。 * * @return array{title:string,description:string,keywords:string,canonical:string,image:string,og_type:string,object:?WP_Post} */ private function resolve_payload(): array { $cfg = $this->config(); $site_name = get_bloginfo('name'); $payload = [ 'title' => wp_get_document_title(), 'description' => $cfg['description'], 'keywords' => $cfg['keywords'], 'canonical' => home_url(isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : '/'), 'image' => $cfg['default_image'], 'og_type' => 'website', 'object' => null, ]; if (is_singular()) { $post = get_queried_object(); if ($post instanceof WP_Post) { $payload['object'] = $post; $payload['canonical'] = (string) get_permalink($post); $payload['og_type'] = in_array($post->post_type, ['post', 'aksrt_resource'], true) ? 'article' : 'website'; // 单篇 SEO 覆盖:per-post meta → 文章字段 → 站点默认。 $meta = $this->post_seo_meta($post->ID); $payload['title'] = $meta['title'] !== '' ? $meta['title'] : wp_strip_all_tags(get_the_title($post)); if ($meta['description'] !== '') { $payload['description'] = $meta['description']; } else { $excerpt = has_excerpt($post) ? wp_strip_all_tags(get_the_excerpt($post)) : wp_trim_words(wp_strip_all_tags((string) $post->post_content), 40, '…'); $payload['description'] = trim((string) $excerpt); } if ($meta['keywords'] !== '') { $payload['keywords'] = $meta['keywords']; } elseif ($payload['keywords'] === '') { $payload['keywords'] = $this->terms_to_keywords($post); } $payload['image'] = $this->resolve_image($post, $cfg); } } elseif (is_home() || is_front_page()) { if ($cfg['site_title'] !== '') { $payload['title'] = $cfg['site_title']; } $payload['canonical'] = home_url('/'); } elseif (is_category() || is_tag() || is_tax()) { $term = get_queried_object(); if ($term instanceof WP_Term) { $payload['canonical'] = (string) get_term_link($term); $term_desc = trim(wp_strip_all_tags((string) term_description($term))); if ($term_desc !== '') { $payload['description'] = $term_desc; } } } if ($cfg['always_default'] && $cfg['default_image'] !== '') { $payload['image'] = $cfg['default_image']; } /** * 允许扩展(如 AI SEO)在最终输出前调整 head 负载。 * * @param array $payload 规范 SEO 负载。 */ return apply_filters('aksrt_seo_head_payload', $payload); } /** * 读取单篇 SEO 覆盖(per-post meta)。 * * @param int $post_id Post ID. * @return array{title:string,description:string,keywords:string} */ private function post_seo_meta(int $post_id): array { $get = static function (string $key) use ($post_id): string { return trim((string) get_post_meta($post_id, $key, true)); }; return [ 'title' => $get('_aksrt_seo_title'), 'description' => $get('_aksrt_seo_description'), 'keywords' => $get('_aksrt_seo_keywords'), ]; } /** * 由分类/标签生成关键词回退。 */ private function terms_to_keywords(WP_Post $post): string { $names = []; foreach ((array) get_the_category($post->ID) as $cat) { if ($cat instanceof WP_Term) { $names[] = $cat->name; } } $tags = get_the_terms($post, 'post_tag'); if (is_array($tags)) { foreach ($tags as $tag) { $names[] = $tag->name; } } return implode(',', array_slice(array_values(array_unique(array_filter($names))), 0, 10)); } /** * 解析单篇缩略图:特色图 → 内容首图 → 站点默认 → logo。 */ private function resolve_image(WP_Post $post, array $cfg): string { if (has_post_thumbnail($post)) { $image = (string) get_the_post_thumbnail_url($post, 'large'); if ($image !== '') { return $image; } } if (function_exists('aksrt_get_first_content_image_url')) { $image = (string) aksrt_get_first_content_image_url($post->ID); if ($image !== '') { return $image; } } if ($cfg['default_image'] !== '') { return $cfg['default_image']; } if (class_exists('AKSRT_Settings')) { return (string) AKSRT_Settings::logo_url(); } return ''; } /** * dl token 页面 noindex。 */ public function render_robots(): void { if (function_exists('get_query_var') && get_query_var('aksrt_dl_token')) { echo '' . "\n"; } } /** * 输出 head(favicon + canonical + description/keywords + OG/Twitter + JSON-LD)。 */ public function render(): void { $cfg = $this->config(); $payload = $this->resolve_payload(); echo "\n\n"; // Favicon(无站点图标时回退)。 if (function_exists('aksrt_get_favicon_url')) { $favicon = (string) aksrt_get_favicon_url(); if ($favicon !== '' && !has_site_icon()) { printf('' . "\n", esc_url($favicon)); } } // Canonical。 if ($cfg['canonical'] && !is_404() && $payload['canonical'] !== '') { printf('' . "\n", esc_url($payload['canonical'])); } // Description / Keywords。 if ($payload['description'] !== '') { printf('' . "\n", esc_attr($payload['description'])); } if ($payload['keywords'] !== '') { printf('' . "\n", esc_attr($payload['keywords'])); } // OpenGraph + Twitter。 if ($cfg['og_enabled']) { $this->render_open_graph($payload); } // JSON-LD。 if ($cfg['jsonld']) { $this->render_jsonld($payload, $cfg); } echo "\n"; } /** * 输出 OpenGraph + Twitter Card。 */ private function render_open_graph(array $payload): void { $site_name = get_bloginfo('name'); $locale = str_replace('-', '_', get_locale()); $og = [ ['property' => 'og:type', 'content' => $payload['og_type']], ['property' => 'og:locale', 'content' => $locale], ['property' => 'og:site_name', 'content' => $site_name], ['property' => 'og:title', 'content' => $payload['title']], ['property' => 'og:url', 'content' => $payload['canonical']], ]; if ($payload['description'] !== '') { $og[] = ['property' => 'og:description', 'content' => $payload['description']]; } if ($payload['image'] !== '') { $og[] = ['property' => 'og:image', 'content' => $payload['image']]; } if ($payload['og_type'] === 'article' && $payload['object'] instanceof WP_Post) { $post = $payload['object']; $published = get_the_date('c', $post); $modified = get_the_modified_date('c', $post); $author = get_the_author_meta('display_name', (int) $post->post_author); if ($published) $og[] = ['property' => 'article:published_time', 'content' => $published]; if ($modified) $og[] = ['property' => 'article:modified_time', 'content' => $modified]; if ($author) $og[] = ['property' => 'article:author', 'content' => $author]; $cats = get_the_category($post->ID); if (!empty($cats)) { $og[] = ['property' => 'article:section', 'content' => $cats[0]->name]; } $tags = get_the_terms($post, 'post_tag'); if (is_array($tags)) { foreach ($tags as $tag) { $og[] = ['property' => 'article:tag', 'content' => $tag->name]; } } } foreach ($og as $tag) { printf('' . "\n", esc_attr($tag['property']), esc_attr($tag['content'])); } $twitter = [ ['name' => 'twitter:card', 'content' => $payload['image'] !== '' ? 'summary_large_image' : 'summary'], ['name' => 'twitter:title', 'content' => $payload['title']], ]; if ($payload['description'] !== '') { $twitter[] = ['name' => 'twitter:description', 'content' => $payload['description']]; } if ($payload['image'] !== '') { $twitter[] = ['name' => 'twitter:image', 'content' => $payload['image']]; } foreach ($twitter as $tag) { printf('' . "\n", esc_attr($tag['name']), esc_attr($tag['content'])); } } /** * 输出 JSON-LD 结构化数据。 */ private function render_jsonld(array $payload, array $cfg): void { $graph = []; // WebSite(全站)。 $graph[] = [ '@type' => 'WebSite', '@id' => home_url('/#website'), 'url' => home_url('/'), 'name' => get_bloginfo('name'), 'inLanguage' => get_locale(), ]; if (is_singular('post') && $cfg['jsonld_single'] && $payload['object'] instanceof WP_Post) { $post = $payload['object']; $node = [ '@type' => 'Article', 'headline' => $payload['title'], 'url' => $payload['canonical'], 'datePublished' => get_the_date('c', $post), 'dateModified' => get_the_modified_date('c', $post), 'author' => [ '@type' => 'Person', 'name' => get_the_author_meta('display_name', (int) $post->post_author), ], 'publisher' => [ '@type' => 'Organization', 'name' => get_bloginfo('name'), ], ]; if ($payload['description'] !== '') { $node['description'] = $payload['description']; } if ($payload['image'] !== '') { $node['image'] = $payload['image']; } $graph[] = $node; } elseif (is_page() && $cfg['jsonld_page'] && $payload['object'] instanceof WP_Post) { $graph[] = [ '@type' => 'WebPage', 'url' => $payload['canonical'], 'name' => $payload['title'], ]; } if (empty($graph)) { return; } $doc = [ '@context' => 'https://schema.org', '@graph' => $graph, ]; echo '' . "\n"; } }