<div class="auto-hide-last-sibling-br paragraph-JOTKXA paragraph-element br-paragraph-space">今天折腾着给网站加个 RSS 功能,搜了一圈发现 Typecho 平台上常用的是 “CustomRSS” 插件。本以为安装后就能顺利抓取文章,结果打开 RSS 链接时直接弹出 404 错误,折腾半天总算找到症结所在 —— 原来是没开启伪静态导致的链接格式问题。</div>
<p> </p>
<div class="auto-hide-last-sibling-br paragraph-JOTKXA paragraph-element br-paragraph-space">先看具体差异:
插件生成的 rss.xml 里,文章地址是https://www.0731119.xyz/rchives/491/
而网站实际的正确文章地址是https://www.0731119.xyz/index.php/archives/491/。</div>
<p> </p>
<div class="auto-hide-last-sibling-br paragraph-JOTKXA paragraph-element br-paragraph-space">追根溯源,问题出在插件的 URL 生成逻辑上:
Plugin.php 文件里用了Typecho_Router::url()函数生成链接,但这个函数默认不会带上 “index.php” 前缀。对于没开启伪静态的网站来说,缺少这个前缀就会导致链接无效,最终触发 404 错误。</div>
<p> </p>
<div class="auto-hide-last-sibling-br paragraph-JOTKXA paragraph-element br-paragraph-space">(补充说明:如果后续开启伪静态,服务器会自动处理 “index.php” 的隐藏,此时插件生成的链接也能正常访问。但针对当前未开启伪静态的情况,修改插件的 URL 生成逻辑,让其强制包含 “index.php” 是更直接的解决办法~)</div>
<div class="auto-hide-last-sibling-br paragraph-JOTKXA paragraph-element br-paragraph-space"> </div>
<div class="auto-hide-last-sibling-br paragraph-JOTKXA paragraph-element br-paragraph-space">附修改完毕后的源码~</div>
<div class="auto-hide-last-sibling-br paragraph-JOTKXA paragraph-element br-paragraph-space"> </div>
<pre class="language-php"><?php
if (!defined('__TYPECHO_ROOT_DIR__')) exit;

/**

  • 生成站点的 rss.xml 文件,用于 rss 订阅优化。
    *
  • @package CustomRSS
  • @version 1.0.3
  • @author 寻鹤
  • @link https://bluehe.cn/
  • @license GPL-3.0-or-later
    */

class CustomRSS_Plugin implements Typecho_Plugin_Interface
{

public static function activate()
{
    Typecho_Plugin::factory('Widget_Archive')-&gt;footer = array('CustomRSS_Plugin', 'generateRSS');
}

public static function deactivate()
{
    @unlink(__TYPECHO_ROOT_DIR__ . '/rss.xml');
}

public static function config(Typecho_Widget_Helper_Form $form)
{
    $numOfPosts = new Typecho_Widget_Helper_Form_Element_Text('numOfPosts', NULL, '10', _t('文章数量'), _t('设定生成 RSS 文件时包含的文章数量'));
    $form-&gt;addInput($numOfPosts-&gt;addRule('isInteger', _t('文章数量必须是整数')));
}

public static function personalConfig(Typecho_Widget_Helper_Form $form) {}

public static function generateRSS()
{
    $db = Typecho_Db::get();
    $options = Typecho_Widget::widget('Widget_Options');
    $numOfPosts = $options-&gt;plugin('CustomRSS')-&gt;numOfPosts;

    $rows = $db-&gt;fetchAll($db-&gt;select()
        -&gt;from('table.contents')
        -&gt;where('type = ?', 'post')
        -&gt;where('status = ?', 'publish')
        -&gt;order('created', Typecho_Db::SORT_DESC)
        -&gt;limit($numOfPosts));

    require_once 'Parsedown.php';
    $Parsedown = new Parsedown();

    $rssFeed = '&lt;?xml version="1.0" encoding="UTF-8" ?&gt;' . PHP_EOL;
    $rssFeed .= '&lt;rss version="2.0"' . PHP_EOL;
    $rssFeed .= 'xmlns:content="http://purl.org/rss/1.0/modules/content/"' . PHP_EOL;
    $rssFeed .= 'xmlns:dc="http://purl.org/dc/elements/1.1/"' . PHP_EOL;
    $rssFeed .= 'xmlns:atom="http://www.w3.org/2005/Atom"' . PHP_EOL;
    $rssFeed .= 'xmlns:slash="http://purl.org/rss/1.0/modules/slash/"&gt;' . PHP_EOL;
    $rssFeed .= '&lt;channel&gt;' . PHP_EOL;
    $rssFeed .= '&lt;title&gt;' . htmlspecialchars($options-&gt;title) . '&lt;/title&gt;' . PHP_EOL;
    $rssFeed .= '&lt;link&gt;' . htmlspecialchars($options-&gt;siteUrl) . '&lt;/link&gt;' . PHP_EOL;
    $rssFeed .= '&lt;description&gt;' . htmlspecialchars($options-&gt;description) . '&lt;/description&gt;' . PHP_EOL;
    $rssFeed .= '&lt;atom:link href="' . htmlspecialchars($options-&gt;siteUrl . 'rss.xml') . '" rel="self" type="application/rss+xml" /&gt;' . PHP_EOL;
    $rssFeed .= '&lt;lastBuildDate&gt;' . date(DATE_RSS) . '&lt;/lastBuildDate&gt;' . PHP_EOL;

    foreach ($rows as $row) {
        // 生成带有index.php前缀的URL
        $permalink = Typecho_Router::url('post', $row, $options-&gt;siteUrl);
        // 确保URL包含index.php
        if (strpos($permalink, 'index.php') === false) {
            $path = parse_url($permalink, PHP_URL_PATH);
            $permalink = str_replace($path, '/index.php' . $path, $permalink);
        }

        $author = $db-&gt;fetchRow($db-&gt;select('screenName')
            -&gt;from('table.users')
            -&gt;where('uid = ?', $row['authorId']));

        $rssFeed .= '&lt;item&gt;' . PHP_EOL;
        $rssFeed .= '&lt;title&gt;' . htmlspecialchars($row['title']) . '&lt;/title&gt;' . PHP_EOL;
        $rssFeed .= '&lt;link&gt;' . htmlspecialchars($permalink) . '&lt;/link&gt;' . PHP_EOL;
        $rssFeed .= '&lt;guid isPermaLink="false"&gt;' . htmlspecialchars($permalink) . '&lt;/guid&gt;' . PHP_EOL;
        $rssFeed .= '&lt;pubDate&gt;' . date(DATE_RSS, $row['created']) . '&lt;/pubDate&gt;' . PHP_EOL;
        $rssFeed .= '&lt;dc:creator&gt;' . htmlspecialchars($author['screenName']) . '&lt;/dc:creator&gt;' . PHP_EOL;

        $categories = $db-&gt;fetchAll($db-&gt;select()
            -&gt;from('table.metas')
            -&gt;join('table.relationships', 'table.metas.mid = table.relationships.mid')
            -&gt;where('table.relationships.cid = ?', $row['cid'])
            -&gt;where('table.metas.type = ?', 'category'));
        if (!empty($categories)) {
            foreach ($categories as $category) {
                $rssFeed .= '&lt;category&gt;&lt;![CDATA[' . $category['name'] . ']]&gt;&lt;/category&gt;' . PHP_EOL;
            }
        }

        $tags = $db-&gt;fetchAll($db-&gt;select()
            -&gt;from('table.metas')
            -&gt;join('table.relationships', 'table.metas.mid = table.relationships.mid')
            -&gt;where('table.relationships.cid = ?', $row['cid'])
            -&gt;where('table.metas.type = ?', 'tag'));
        if (!empty($tags)) {
            foreach ($tags as $tag) {
                $rssFeed .= '&lt;category&gt;&lt;![CDATA[' . $tag['name'] . ']]&gt;&lt;/category&gt;' . PHP_EOL;
            }
        }

        $customDesc = $db-&gt;fetchRow($db-&gt;select('str_value')
            -&gt;from('table.fields')
            -&gt;where('cid = ?', $row['cid'])
            -&gt;where('name = ?', 'postjj'));
        if (!empty($customDesc['str_value'])) {
            $rssFeed .= '&lt;description&gt;&lt;![CDATA[' . htmlspecialchars($customDesc['str_value']) . ']]&gt;&lt;/description&gt;' . PHP_EOL;
        } else {
            if (!empty($row['text'])) {
                $htmlContent = $Parsedown-&gt;text($row['text']);
                $excerpt = mb_substr(strip_tags($htmlContent), 0, 128, 'UTF-8') . '...';
                $rssFeed .= '&lt;description&gt;&lt;![CDATA[' . $excerpt . ']]&gt;&lt;/description&gt;' . PHP_EOL;
            }
        }

        if (!empty($row['text'])) {
            // 处理 Markdown 内容,去除 &lt;!--markdown--&gt; 注释,确保解析
            $row['text'] = preg_replace('/&lt;!--markdown--&gt;/i', '', $row['text']);
            $htmlContent = $Parsedown-&gt;text($row['text']);
            $rssFeed .= '&lt;content:encoded&gt;&lt;![CDATA[' . PHP_EOL . $htmlContent . PHP_EOL . ']]&gt;&lt;/content:encoded&gt;' . PHP_EOL;
        }

        $commentsNum = $db-&gt;fetchObject($db-&gt;select(array('COUNT(*)' =&gt; 'num'))
            -&gt;from('table.comments')
            -&gt;where('cid = ?', $row['cid'])
            -&gt;where('status = ?', 'approved'))-&gt;num;

        $commentsLink = $permalink . '#comments';
        $rssFeed .= '&lt;slash:comments&gt;' . htmlspecialchars($commentsNum) . '&lt;/slash:comments&gt;' . PHP_EOL;
        $rssFeed .= '&lt;comments&gt;' . htmlspecialchars($commentsLink) . '&lt;/comments&gt;' . PHP_EOL;

        $banner = $db-&gt;fetchRow($db-&gt;select('str_value')
            -&gt;from('table.fields')
            -&gt;where('cid = ?', $row['cid'])
            -&gt;where('name = ?', 'banner'));
        if (!empty($banner['str_value'])) {
            $rssFeed .= '&lt;enclosure url="' . htmlspecialchars($banner['str_value']) . '" length="0" type="image/jpeg" /&gt;' . PHP_EOL;
        }

        $rssFeed .= '&lt;/item&gt;' . PHP_EOL;
    }

    $rssFeed .= '&lt;/channel&gt;' . PHP_EOL;
    $rssFeed .= '&lt;/rss&gt;' . PHP_EOL;

    file_put_contents(__TYPECHO_ROOT_DIR__ . '/rss.xml', $rssFeed);
}

}
</pre>
<p> </p>
<p> </p>

已有 2 条评论

  1. 嘿嘿我说呢为啥中午抓到的链接404了
    话说中间的/index.php没法隐藏吗?确实有点不好看哩

    1. @LiuShen

      开伪静态就可以了,但是今天想琢磨一下再出开的情况下如何修改插件实现。

发表评论