MediaWiki | CSS 页面解析 filepath:// 为文件实际路径

近期接触了很多维基农场(Wiki farm,提供多个 Wiki 托管的服务器),大多数农场使用的是修改过的 MediaWiki 实例,其中发现有不少农场(例如 wiki.ggWeird Gloop 等)为 css 页面提供了编写 filepath:// ,解析后指向文件实际链接(通常是图片)的功能。这种做法更像是一种自定义的伪协议(Pseudo-protocol),避免了硬编码 URL 带来的维护噩梦,非常值得借鉴。

什么是 filepath

标准 MediaWiki

MediaWiki 本身其实有 filepath 功能,但是和上述所说的 filepath 不甚相同。标准的 MediaWiki 中,filepath 是一个解析函数(Parser Function),写法通常是 {{filepath:文件名}}。经过服务器解析后,会直接返回该文件在服务器上的绝对 URL 地址。大部分情况下,filepath 主要用于 Wikitext 页面(比如模板当中),在需要直接引用图片链接(而非渲染图片)时使用,但无法直接在 CSS 或 JavaScript 文件中使用。

动态解析拦截器

而维基农场中的 filepath://,则是一种专门为 CSS 设计的资源路由协议。这种 filepath:// 并非简单的字符串替换,而是在 MediaWiki 处理静态资源(ResourceLoader)的管线中,插入了一个动态解析拦截器。以 Weird Gloop 为例,这个拦截器发生在 remapOne 阶段,也就是 ResourceLoader 将 CSS 发送给客户端之前。解析后,浏览器接收到的 CSS 将会是经过计算的实际 URL(如 https://cdn.example.com/images/thumb/a/ab/Wiki.png/300px-Wiki.png)。

如何启用 filepath

如果想在自建的 MediaWiki 环境或农场中实现这个功能,核心在于修改 CSS 解析工具类。下面以 Weird Gloop 的方案(修改 CSSMin.php )为例说明。

详细说明

定位到并打开文件 /vendor/wikimedia/minify/src/CSSMin.php,定位到 remapOne,在 remapOne 方法开始处插入以下内容。

$parsedUrl = parse_url( $url );
if ( is_array( $parsedUrl ) && isset( $parsedUrl['scheme'] ) && $parsedUrl['scheme'] == 'filepath' ) {
    if ( isset( $parsedUrl['host'] ) ) {
        // 提取文件名(即 host 部分,如 Wiki.png)并解析参数(如 ?width=200)
        $name = rawurldecode( $parsedUrl['host'] );
        $width = -1;
        if ( isset( $parsedUrl['query'] ) ) {
            parse_str( $parsedUrl['query'], $opts );
            $width = (int) ( $opts['width'] ?? $opts['w'] ?? -1 );
        }

        // 调用 MediaWiki 核心服务,获取文件对象;如果有宽度要求,生成并指向缩略图
        $fileObj = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $name );

        if ( $fileObj ) {
            $resultUrl = $fileObj->getUrl();
            
            // 
            if ( $width != -1 ) {
                $mto = $fileObj->transform( [ 'width' => $width ] );
                if ( $mto && !$mto->isError() ) {
                    $resultUrl = $mto->getUrl();
                }
            }
            return $resultUrl; // 返回解析后的真实链接
        }
    }
}

修改后的完整代码

定位到并打开文件 /vendor/wikimedia/minify/src/CSSMin.php,定位到 remapOne,将整个函数替换为以下代码(已知适用于 MediaWiki 1.43,其他版本未测试)。

public static function remapOne( $file, $query, $local, $remote, $embed ) {
    // The full URL possibly with query, as passed to the 'url()' value in CSS
    $url = $file . $query;

    // WGL start - Implement filepath: URL scheme for long-term cached image usage in CSS.
    // i.e. filepath://Wiki.png?width=30
    $parsedUrl = parse_url( $url );
    if ( is_array( $parsedUrl ) && isset( $parsedUrl['scheme'] ) && $parsedUrl['scheme'] == 'filepath' ) {
        if ( isset( $parsedUrl['host'] ) ) {
            $name = rawurldecode( $parsedUrl['host'] );

            $width = -1;
            if ( isset( $parsedUrl['query'] ) ) {
                parse_str( $parsedUrl['query'], $opts );
                $width = (int) ( $opts['width'] ?? $opts['w'] ?? -1 );
            }

            // Handles non-existing files by returning the non-cached path where the file would exist.
            $fileObj = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()->newFile( $name );

            // Handle bad file name.
            if ( !$fileObj ) {
                return '';
            }

            // Non-thumbnail URL.
            $resultUrl = $fileObj->getUrl();
            
            // Ensure we have a valid URL
            if ( !$resultUrl ) {
                return '';
            }

            // If a width is requested.
            if ( $width != -1 ) {
                $mto = $fileObj->transform( [ 'width' => $width ] );
                // ... and we can
                if ( $mto && !$mto->isError() ) {
                    // ... change the URL to point to a thumbnail.
                    $thumbUrl = $mto->getUrl();
                    if ( $thumbUrl ) {
                        $resultUrl = $thumbUrl;
                    }
                }
            }

            return $resultUrl;
        }
    }
    // WGL end.

    // Expand local URLs with absolute paths to a full URL (possibly protocol-relative).
    if ( self::isLocalUrl( $url ) ) {
        return self::resolveUrl( $remote, $url );
    }

    // Pass through fully-qualified and protocol-relative URLs and data URIs, as well as local URLs if
    // we can't expand them.
    // Also skips anchors or the rare `behavior` property specifying application's default behavior
    if (
        self::isRemoteUrl( $url ) ||
        ( $url[0] ?? '' ) === '#'
    ) {
        return $url;
    }

    // The $remote must have a trailing slash beyond this point for correct path resolution.
    if ( ( $remote[-1] ?? '' ) !== '/' ) {
        $remote .= '/';
    }

    if ( $local === false ) {
        // CSS specifies a path that is neither a local file path, nor a local URL.
        // It is probably already a fully-qualitied URL or data URI, but try to expand
        // it just in case.
        $url = self::resolveUrl( $remote, $url );
    } else {
        // We drop the query part here and instead make the path relative to $remote
        $url = self::resolveUrl( $remote, $file );
        // Path to the actual file on the filesystem
        $localFile = "{$local}/{$file}";
        if ( file_exists( $localFile ) ) {
            if ( $embed ) {
                $data = self::encodeImageAsDataURI( $localFile );
                if ( $data !== false ) {
                    return $data;
                }
            }
            // Add version parameter as the first five hex digits
            // of the MD5 hash of the file's contents.
            $url .= '?' . substr( md5_file( $localFile ), 0, 5 );
        }
        // If any of these conditions failed (file missing, we don't want to embed it
        // or it's not embeddable), return the URL (possibly with ?timestamp part)
    }
    return $url;
}

需要注意的是,remapOne 会在每次 ResourceLoader 生成缓存时运行。获取文件对象涉及数据库查询,建议开启对象缓存(如 Redis 或 Memcached),否则数据库庞大时会显著拖慢 CSS 的编译速度。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇