MediaWiki

从一开始,MediaWiki就是专门为维基百科服务的软件。开发者一直致力于使其能更方便的被第三方用户使用,但在其发展过程中,是维基百科的影响力和偏好塑造了MediaWiki的体系结构。

维基百科是世界上排名前十的网站之一,目前,它每月有4亿的绝对造访人次,并且每秒有超过10万次的点击。维基百科并不是通过做广告来获得资金,它完全由非营利组织————维基媒体基金会支持,而维基媒体基金会主要依靠捐赠作为资金来源。这意味着MediaWiki不能只是运行一个世界前十的网站,而且必须在极其有限的预算下完成。为了达到这些要求,MediaWiki在性能,缓存和优化方面有着严重偏向。维基百科无法启用的昂贵功能可以通过配置的改变来恢复或废除,在功能和性能间有着一个无休止的平衡。

维基百科对MediWiki架构的影响不只局限于功能方面。与一般的内容管理系统不同,MediaWiki从一开始就有一个特别的目标:支持一个可以在开放平台上自由地创建和管理可重用知识的社区。这意味着,MediaWiki并不包含像“发布工作流程或访问控制列表”这样许多公司内容管理系统共有的特征,而是给许多处理垃圾邮件和恶意破坏的工具。

所以,从一开始,维基百科参与者的不断发展的社区的需求和行动就在影响着MediWiki,当然,反之亦然。MediaWiki的架构已经多次应社区的需求而改变,比如维基共享资源的建立,或者是标记修改的特征。开发者对主要架构的更改都是因为维基百科对于MediaWiki的需求。

MediaWiki从一开始就是开源软件,因此有了一个坚实的外部用户基础。第三方复用者们知道,只要维基百科这样知名度高的网站使用MediaWiki,这个软件就会始终坚持并不断发展。MediaWiki以前一直专注于为维基百科的网站服务,不过现在开发者也倾注了许多努力在使它更通用,更符合第三方用户的需求。例如,当安装的所有事情都要通过命令行来完成,并且软件包含了关于维基包含的硬编码路径时,MediaWiki正制作一个完善的网络安装来使安装过程不再那么痛苦。

不过,MediaWiki仍然是为维基百科服务的软件,这从它的发展历史和架构就可以看出。

这一章的安排如下:

* 历史回顾:给出一个对MediaWiki历史简短的回顾,或者再说说它的史前史和创作的环境。
* MediaWiki的代码库和实践:解释了选择PHP的原因,安全保护代码的重要和实现,以及如何处理一般配置
* 数据库和文本储存:讨论了分布式数据储存系统,以及它的结构是如何发展来适应数据的增长
* 请求、缓存和交付:通过MediaWiki的组件来追寻一个网站请求的执行,本节包含了对不同的缓存层和资产交付系统的描述
* 语言:详述了普遍的国际化和定位系统的重要性和实施过程
* 用户:介绍用户在软件中的表示,及用户权限是如何工作的
* 内容:详述了内容是如何结构化,格式化及被处理成最终的HTML。还有一部分专注于MediaWiki如何处理媒体文件
* MediaWiki的定制和延伸:说明了JavaScript, CSS,扩展和皮肤是如何用于定制一个维基,以及他们如何修改它的外观和行为。还有一段介绍了这个软件可用计算机处理的web接口

12.1 历史概述

阶段1:UseModWiki

维基百科在2001年一月推出。在那时,它主要是个为了提高Nupedia内容产量的尝试,而Nupedia是一个Jimmy Wales创建的内容开放,由同行评议的百科全书。正因为它是一个尝试,维基百科一开始以UseModWiki为引擎,UseModWiki是一个由Perl语言编写的GPL维基引擎,采用了骆驼拼词法并且存储所有的页面在单独的文本文件,不包含改动的历史记录。

人们很快发现,骆驼拼词法并不适合给百科全书的文章命名。在2001年一月下旬,UseModWiki的开发者和维基百科的参与者Clifford Adams给UseModWiki添加了一个新的特征:自由链接,即:链接页面用一种特殊的语法能力(双括号),而不是自动用骆驼拼词法链接。几周后,维基百科更新到新版本的UseModWiki,能够支持自由链接。

尽管这个初始阶段与MediaWiki本身无关,但它提供的一些环境和表现,使得维基百科在MediaWiki建立之前就开始加强软件中的这部分功能。UseModWiki也影响了MediaWiki的部分功能,比如:它的标记语言。这个 https://nostalgia.wikipedia.org/wiki/HomePage |怀旧版的维基百科 包含了对于2001年十二月的维基百科数据库的完整拷贝,那时维基百科还在用UseModWiki。

阶段2:PHP脚本

在2001年,维基百科还不是世界前十的网站。这只是一个在网络的阴暗角落的不起眼的项目,对于大多数搜索引擎是未知的,并只放在一个服务器上。不过,那时性能就已经是一个问题,因为UseModWiki将它的内容存储到一个平面文件数据库。那个时候,维基百科的人很担心自己被纽约时报、 Slashdot和Wired的文章淹没。

所以在2001年夏季,维基百科的参与者Magnus Manske(在那时还是个大学生)开始在他的空闲时间去完成一个维基百科的专用引擎。他打算使用的数据库驱动的应用程序提高维基百科的性能,并且开发一些一般的维基引擎无法实现的维基百科的特定功能。用PHP语言写并且支持MySQL,这个新的引擎可以被简单地称为”PHP脚本”、”PHP维基”、”维基百科软件”或”二期”。

PHP脚本在2001年八月被提供,于九月在SourceForge上共享,测试工作一直到2001年年末完成。随着通信量的增加,维基百科一直遭受着性能问题,英文维基百科最终在2002年一月将UseModeWiki换成了PHP脚本。同样在2001年建立的其他语言的版本则是慢慢地优化,尽管其中有些直到2004年还在使用UseModWiki。

PHP软件采用的是MySQL数据库,并且为之后的MediaWiki奠定了坚实的基础。它所提供的许多关键的功能直到今天还在使用,比如:组织内容的命名空间(包括讨论页)、皮肤和特殊的页面(包括维修报告、一个工作列表和用户的监视列表)。

阶段3:MediaWiki

尽管PHP脚本和数据库后端带来了很大的进步,但是增长的通信量、昂贵的功能和有限的硬件等仍然给维基百科带来了性能问题。在2002年,Lee Daniel Crocker重写了代码,将新的软件称为”三期”。因为网站经常遇到问题,Lee认为已经没有时间坐下来好好地设计和开发一个解决方案了,所以他只是对现有架构进行了重组以实现更好的性能,还攻击了所有的代码,并且增加了分析功能来跟踪慢函数。

三期软件保持着相同的基本接口,外观和表现尽可能设计的类似于阶段二,也加入了一些新的功能,比如:一个新的文件上传系统、内容变话的并排区分和Interwiki链接(一种链接到互联网上其他Wiki网站的简单方法)。

其他功能于2002年后被添加,比如:新的维护特殊页面,和”双击以编辑”选项。而性能问题很快又暴露了。例如,在2002年十一月,管理员就不得不停用了”访问量”和”站点”统计功能,这些造成了每一次访问有两次数据库的读写。他们也偶尔会将网站切换到只读模式来保证给读者的服务,并且因为表锁定问题,会在访问的高峰期禁用昂贵的维护页面。

在2003年初,他软件开发者们讨论了在这些性能问题变得无法控制之前,他们是应该完全重新设计一个软件,还是继续调整和完善已有的代码。最终他们还是选择了后者,主要是因为大部分开发者对于已有的代码还是比较满意的,并且有足够的信心,可以通过进一步的改进来使引擎跟上网站的发展。

在2003年六月,管理员添加了第二个服务器,第一个数据库服务器和网站服务器分开了(新的机器同样也是非英语网站的Web服务器)。这两家服务器之间的负载平衡将在今年晚一年中设定。管理员也启用了新的页面缓存系统,即用文件系统进行缓存渲染,为匿名用户提供准备输出页面。

2003年六月时,Jimmy Wales也创建了非盈利的维基媒体基金会来支持维基百科,并且管理其基础设施和日常运作。在七月,”维基百科软件”被正式命名为”MediaWiki”,这是根据维基媒体基金会的名字”WikiMedia”而来。当时这被认为是一个巧妙的双关语,混淆了用户和开发者。

还有些新的功能与七月被添加,如:自动生成目录和编辑页面部分的能力,这些直到今天都还在使用。”MediaWiki”于2003年八月发不了第一个版本,这表明了一个稳定的软件架构有着漫长的发展史。

12.2 MediaWiki的代码库和实践

PHP

PHP于2001年被选作为维基百科”二期”软件框架的语言。在那之后,维基百科开始有机地成长,并且还在不断发展。MediaWiki的大多数开发者都是志愿者,都是与空闲的时间进行开发工作,很少有人是从一开始就参与了软件的开发。在现在来看,软件的一些决策似乎是错的,并且还有不少遗漏。不过我们不应该去指责那些创始人,指责他们没有完成一些目前来看是至关重要的东西,因为最初的代码是如此之少,并且用来发展的时间又如此之短。

例如,MediaWiki用的是无前缀的类名,那么当PHP核心和PECL(PHP扩展模块)开发者添加新类时就可能引起冲突。MediaWiki的’’Namespace’’类必须更名为’’MWNamespace’’来与PHP 5.3兼容。坚持给所以类加前缀(例如’’MW’’)可以使类更容易嵌套进MediaWiki里的其他程序中。

使用PHP对于程序性能来说并不是最好的选择,其他的一些动态语言有着更大的优势。比如用Java的话性能会更好,并且可以简化后端维护任务的执行。不过在另一方面,PHP更受欢迎,使用PHP有利于招收开发者。

即使MediaWiki中还遗留着许多很”丑”的代码,不过这些年已经有了许多主要的改进,并且在其发展过程中许多新的元素被引入到MediaWiki里。他们包括’’Parser’’、’’SpecialPage’’和’’Database’’类,’’Image ‘’类和’’FileRepo ‘’类层次结构,资源加载器,还有’’Action’’层次结构。MediaWiki一开始没有这些东西,但是这些支持的都是一开始便存在的功能。开发者们主要致力于功能的发展,而架构则是保留了下来,只是后来因为不完善的架构有着明显的成本增加。

安全

因为MediaWiki是像维基百科这样高知名度网站的平台,核心开发者和代码评审员执行了严格的安全规则(见 https://www.mediawiki.org/wiki/Security_for_developers |安全开发详细指南 )。为了更容易的编写安全代码,MediaWiki给开发者提供HTML输出和数据库查询的包装来处理泄漏问题。为了审查用户的输入,开发者用采用了’’WebRequest ‘’类,它可以分析由URL和传输表传输的数据,移除”魔术引号”和斜线条,标识非法字符并且规范Unicode序列。跨站请求的伪造是通过使用令牌来避免,而跨站脚本攻击是通过验证输入和逃避输出来防止,这些经常是使用了PHP的’’htmlspecialchars()’’函数。MediaWiki也通过给XHTML清除提供(和使用)’’Sanitizer ‘’类和数据库函数类防止SQL注入攻击。

配置

MediaWiki提供了数百的配置设置,作为PHP中的全局变量。默认值设置在’’DefaultSettings.php’’ 中,并且系统管理员可以通过修改文件’’LocalSettings.php’’来更改它们。

MediaWiki以前过分依赖于全局变量,包括配置和上下文处理。过多的全局变量给PHP的’’register_globals’’函数(这个函数MediaWiki在1.2版本之后就不再使用)带来了严重影响。这个系统也限制了配置的潜在抽象,使其更难优化启动过程。此外,配置的命名空间与用于注册和对象上下文的变量共享,导致潜在的冲突。从用户角度来看,配置的全局变量使MediaWiki更难以配置和维护。MediaWiki的开发一直是一个将环境从全局变量移入对象的过程。将环境存入对象中可以使这些对象被更灵活地运用。

12.3 数据库和文本储存

从二期软件开始,MediaWiki就开始使用关系数据库作为后台。MediaWiki默认的(支持性最好的)数据库管理系统是MySQL,MySQL同时也被所有维基媒体基金会的网站使用,也有其他的数据库管理系统(比如PostgreSQL、Oracle和SQLite)由社区支持来实现。系统管理员可以在安装MediaWiki时选择一个数据库,并且MediaWiki提供了一个数据库的抽象和一个查询的抽象层来为开发者简化数据库接口。

Figure 12.1: 数据库设计

目前的设计啊包含了许多的表。许多适合维基百科的内容有关(例如:’’page’’、 ‘’revision’’、 ‘’category’’和’’ recentchanges’’)。其他的表则包含了关于用户(‘’user’’、 ‘’ user_groups’’)、媒体文件(‘’image’’、 ‘’ filearchive’’)、缓存(‘’objectcache’’、 ‘’ l10n_cache’’、 ‘’querycache’’)和内部工具(‘’job’’用于工作队列)等的数据,正如图12.2所示(完整的数据库布局文档可以在维基百科上找到)。因为用SQL查询大量的数据是非常昂贵的,尤其在维基百科上,所以MediaWiki广泛地使用了索引和汇总表。没有索引的查询是非常困难的。

这些年,数据库的模式有了许多的变换,其中最著名的则是MediaWiki1.5版里的文本存储的解耦和修订追踪。

Figure 12.2: Main content tables in MediaWiki 1.4 and 1.5

在1.4架构中,内容被存储在两个重要的表里,’’cur ‘’(包含当前版本页面的文本和元数据)和’’old’’(包含之前的版本),被删除的页面保存在’’archive’’中。当页面做出了修改,当前版本的被复制到’’old’’表中,而新的修改被保存在’’cur ‘’中。当一个页面被重命名,’’old’’表中所有旧版元数据中的页面标题必须被更新,这是个耗时很长的操作。当一个页面被删时,那么在删除前,它在’’cur ‘’表和’’old’’表中的所有记录都必须复制到’’archive’’表中;这意味着要移动所有版本的文本,这会非常耗时。

在1.5版架构中,元数据和文本被分隔开:’’cur ‘’和’’old’’表被’’page ‘’(页面元数据)、’’revision’’(所有版本的元数据,无论旧版还是新版)和’’text ‘’(所有版本的文本,旧、新或被删除的)替代。现在,当出现了修改,元数据不需要再被复制到别的表上,插入新的记录并且更新’’page_latest ‘’的指针就足够了。还要,元数据不再包含页面
标题,只有它的ID;这样的话,当页面被改名时就不需要去修改所以版本数据。

‘’revision’’表存储着每次修改的元数据,但不包含文本;相反,它们是有一个文本的ID指向包含了完整文本的’’text ‘’表。当一个页面被删除时,页面所有版本的文本仍然保留,并且不需要被移动到别的表去。’’text ‘’表是由ID到文本块的映射组成,’’flags ‘’域则表示这个文本块是否被压缩(为了节约空间)以及这个文本块是否只是一个指向外部文本存储的指针。维基百科的网站使用一个MySQL的外部存储集来存储诸多版本的数据块。第一个版本的数据是完整存储的,之后的版本则只记录与之前版本的不同,然后在将数据压缩。因为各版本都是按页面分组的,它们都是很相似的,所以差别相对较小并且压缩的效果也很好。维基百科的压缩比在百分之98左右。

在硬件方面,MediaWiki已经内置了对负载平衡的支持,这最早在2004年的MediaWiki1.2(那时维基百科有了第二台服务器,是当时的一件大事)中被添加。负载平衡器(MediaWiki
的PHP代码决定连接到哪个服务器)现在是维基百科基础设施的一个重要组成部分,这也解释了它在代码的一些算法中的影响。在MediaWiki的配置中,有一个主数据库服务器和任意数量的从数据库服务器,系统管理员可以给每个服务器指定权值。负载均衡器会将所有的写操作分给主服务器,然后根据权值分配读操作。它也保持对每个从服务器反应延迟的监控。如果从服务器的响应延迟超过了30秒,它不会再接受这个服务器的中断请求。如果所有的从服务器的延迟都超过了30秒,MediaWiki会自动将自己置为只读模式。

MediaWiki的”时间表保护器”可以确保响应延迟不会使用户看到声称动作还没有完成的页面:例如,如果一个用户重命名页面,另一个用户仍可以看到旧的名字,但是改名的那个人一直看到的都是新名字,因为那是他改的名字。这是通过当用户的读请求导致了写请求时,在用户的会话中存储主服务器的位置来完成的。下次用户进行读请求时,负载均衡器会从话中读取位置,然后选择一个已经获取响应位置的从服务器来为这个请求服务。如果没有可用的,那么它会一直等待。它可能会有其他的用户,虽然动作还没有发生,但是时间表使每个用户的请求保持一致。

12.4 请求、缓存和交付

网页请求的执行流程

‘’index.php’’是MediaWiki的主要入口点,并且处理大部分应用服务器加工的请求(即:请求不是由缓存基础设施来服务;参见下文)。从’’index.php’’开始执行的代码依次进行下列操作,进行安全检查、从’’includes/DefaultSettings.php’’加载默认配置、通过’’includes/DefaultSettings.php’’推断配置,然后进行’’LocalSettings.php’’中网站配置。接下来,实例化一个’’MediaWiki’’对象(‘’$mediawiki’’)并且根据请求的标题及动作参数创建一个’’Title’’对象(‘’$wgTitle’’)。

‘’index.php’’可以接收URL请求中的许多行动参数;默认的动作是’’view’’,可以定期查看文章内容。例如,请求https://en.wikipedia.org/w/index.php?title=Apple&action=view 显示了英文维基百科中“苹果”这篇文章的内容(网页请求通常有URL重写来修饰,比如https://en.wikipedia.org/wiki/Apple )。其他的一些常见的行为,包括’’edit’’(打开一个文章来编辑)、’’submit’’(预览并保存一份文章)、’’history’’(展示一篇文章的历史)和’’watch’’(在用户的浏览列表添加一篇文章)。管理的行为包
括’’delete’’(删除一篇文章)和’’protect’’(阻止对文章的修改)。

‘’MediaWiki::performRequest()’’被调用来处理大部分URL请求。它可以检查标题的好坏、阅读的限制、本地维基内部链接的重定向,和重定向循环,并且确定该请求是通过一个正常的页面还是一个特殊的页面。

正常页面的请求都交给’’initializearticle() MediaWiki::’’来给页面创建一个’’Article’’对象(‘’$wgArticle’’),之后交给’’MediaWiki::performAction()’’来进行标准行动。一旦动作完成了,’’MediaWiki::finalCleanup()’’进行提交数据库事务、输出HTML和通过作业队列进行延期的更新等操作,以完成请求。’’MediaWiki::restInPeace()’’提交延迟的更新并适当地结束任务。

如果请求的页面是一个特殊页面(即不是一个普通的维基内容页面,而是一种特殊的与软件相关的网页,比如’’Statistics’’),’’SpecialPageFactory::executePath’’代替’’initializeArticle()’’被调用,之后相应的PHP脚本开始执行。特殊页面可以做许多神奇的事情,并且每一个都有特定的目的,而这些通常都独立于它的标题和内容。特殊页面还包含各种类型的报告(最近的变化、日志、未分类的页面),和维基管理工具(用户模块、用户权限更改)等。他们的执行工作流程取决于他们的功能。

许多函数包含调试代码,这样可以使它按工作流程调试。调试时可以分别通过调用’’wfProfileIn’’和’’wfProfileOut’’函数来启动和停止调试一个函数,这两个函数以被调试的函数的名字为参数。在维基百科网站,调试是按请求的百分比来实行,这样可以保持性能。MediaWiki发送UDP包给产生调试数据的中央服务器。

缓存

由于MediaWiki在维基百科网站上起到一个中心的作用,所以它主要在性能方面有加强,不过,它也是一个更大的操作生态系统的一部分,而这个系统已经影响到了它的架构。维基百科的缓存基础设施(层次结构)在MediaWiki上强加了限制;对于这个问题,开发者们并没有尝试去对维基百科的缓存基础设施进行更广泛的优化,而是去使MediaWiki更加灵活,这样它可以不影响性能和缓存的需求地在基础设施上运行。例如,在默认情况下,MediaWiki将用户的IP显示在界面的右上角来作为一个提醒,这样当他们没有登录时,软件就会知道。’’$wgShowIPinHeader’’配置变量允许系统管理员禁用此项功能,因此页面内容可以独立于用户;所有的匿名用户访问的网页都是同样的版本。

缓存的第一层(用于维基百科网站)包含反向缓存代理服务器(Squids),它可以在大部分请求到MediaWiki应用服务器前进行拦截和服务。Squids含有整个渲染页面的静态版本,帮助不登陆网站的用户进行简单的读取。MediaWiki一开始就支持Squids和Varnish,并完善了该缓存层,比如:当网页改变时,通知他们从缓存层上清理网页。

当MediaWiki根据多个对象渲染和组合页面时,缓存的第二层开始运用,这些对象可以存在缓存上来减少之后的调用。这些对象包括网页的界面(补充工具栏,菜单,用户界面文本)和用维基语法对内容的解析。内存对象的缓存自1.1版本的MediaWiki就有了,并且对于避免很长的重新分析和复杂的界面很重要。

登录会话的数据也可以被存储在缓存中,它使得会话可以透明地在众多前端web服务器上负载均衡地执行(维基很大程度上依赖于负载均衡,通过使用LVS和PyBal)。
自从1.16版本后,MediaWiki为本地化的用户界面文本使用一个专用的对象缓存;这是在注意到,存储在缓存集群上的对象中有一大部分是部分为用户语言的界面信息,之后添加的。该系统是基于快速获取个人信息从常数数据库(CDB),例如,键值对文件。在经典案例中,CDBS大大减少了内存开销和启动时间;他们也用于维基内部链接的缓存。

最后一个缓存层是PHP操作码缓存,通常能够加快PHP应用程序。编译是一个漫长的过程;为了避免每次都要将PHP脚本编译为操作码,一个PHP加速器可以用来存储编译码和执行直接编译。MediaWiki使用了许多加速器,比如:APC、PHP加速器和eAccelerator。

因为维基百科的偏向,MediaWiki在它的完整性、多层次和分布式缓存基础设施方面进行了优化。尽管如此,它一开始也有对于较小网站的可选的设置。例如,它提供了一个可选的简单的文件缓存系统,来存储完全渲染页面的输出,就像Squid那样。另外,MediaWiki的抽象类缓存层使它能在许多地方存储缓存对象,包括:文件系统、数据库和操作码缓存器。

资源加载器

与许多的Web应用程序一样,MediaWiki的接口在这些年来变得更有互动性和反应力,这些都是通过JavaScript的使用。在可用性方面的努力是从2008年开始,同时还有对媒体处理的改进(例如:在线编辑视频文件),这些都是专注于对前端性能的改进。

为了优化JavaScript和CSS内容的交付,资源加载器模块在JS和CSS的交付方面有所优化。这个工作自2009年开始,2011年完成,并且自版本1.17后成为了MediaWiki的核心功能。资源加载器会加载所需的JS和CSS的内容,从而在某些特征不用时,减少加载和解析的时间,就像使用旧的浏览器。它还可以缩减代码、通过给资源分组来减少请求,还有嵌入图像作为数据的标识符(想了解资源加载器的更多内容,请查看官方文档)。

12.5 语言

语境和理论

有效的贡献和传播免费自由的知识的一个核心部分是提供尽可能多的语言。维基百科有超过280种语言,并且百科全书中的英文文章只占了不到百分之二十。由于维基百科和它的姊妹网站有这么多语言存在,重要的不只是给读者提供母语的内容,而是提供本地化的接口、有效的输入和转换工具,这样就有更多的参与者可以贡献内容了。

为此,本地化与国际化是MediaWiki的核心组成部分。国际化的系统是普遍的,影响了软件的许多部分;它也使最灵活和功能丰富的一个(这里有一个对于国际化和本土化的MediaWiki的 https://www.mediawiki.org/wiki/Localisation |详尽指南 )。翻译便利通常是为了开发的便利,但这被认为是一个可以接受的成本。

MediaWiki是目前定位在超过350种语言,包括非拉丁和从右到左(RTL)的语言,这些都有不同程度的完成。界面和内容可以存在不同的语言,有混合性。

内容语言

MediaWiki最初使用的每种语言的编码,这导致很多问题,比如:国外的脚本不可以被使用在网页标题。于是,UTF-8被采用了。随着MediaWiki1.5中主要数据库设计的改变,对UTF-8之外的字符集的支持于2005年废止了,内容必须是UTF-8编码。

编者键盘上没有的字符可以通过MediaWiki的编辑工具自定义地插入,接口信息会出现在编辑窗口下面;JavaScript版本的会自动插入被点击的字符。WikiEditor是MediaWiki的延
伸,发展为可用性工作的一部分,将特殊的字符放入工具栏。Narayam是另一个延伸,提供额外的输入方法和对非ASCII编码的映射这一关键功能。

接口语言

自从三期软件刚建立开始,接口信息就被存储在PHP关键值对数组。每条信息都是由唯一的钥匙(key)确定的,不过有着许多不同的值(value)。钥匙是由开发者决定的,他们被鼓
励使用前缀扩展,比如:对于UploadWizard扩展的钥匙会以’’mwe-upwiz-‘’开头,这里的’’mwe’’表示MediaWiki的扩展。

MediaWiki信息可以嵌入软件提供的参数,这往往会影响消息的语法。为了支持几乎任何语言,MediaWiki的定位系统进行了改进,并且随着时间不断复杂化,来容纳语言的特点和例外,这经常被认为是说英语的人做出的怪事。

例如,形容词在英语中是不变的单词,但是像法语这类语言就要求形容词与名词保持一致。如果用户在他们的首选项中指定了他们的性别,那么’’和’’

定位信息

本地化界面信息页面存储在’’MessagesXx.php’’文件,其中Xx语言的ISO-639编码(例如:’’MessagesFr.php’’表示法国);默认的信息都是英文的,并且存储在’’MessagesEn.php’’。MediaWiki的扩展使用了类似的系统,或者将所有本地信息放入了`.i18n.php’’文件。随着转换,消息文件还包括语言相关的信息,如日期格式。

起作用的转换过去是通过给’’MessagesXx.php’’文件提供PHP补丁来完成的。在2003十二月,MediaWiki 1.1引入了的“数据库信息”,是含有接口消息的MediaWiki命名空间上的维基页面的一个子集。页面’’MediaWiki:的内容是一个关键信息的文本,并且优先于它在PHP文件上的值。本地化版本的信息存储在''MediaWiki:<Message-key>/<language-code>,比如:’’MediaWiki:Rollbacklink/de’’。

此功能允许用户转换(和定制)本地维基的接口信息,但这个过程不会更新国际化的文件。在2006年,Niklas Laxstr?m创建了一个特别的,被黑客严重攻击过的MediaWiki网站(现在托管在http://translatewiki.net ),在这译者可以仅仅通过编辑一个维基页面来更改所以语言的接口信息。之后’’MessagesXx.php’’文件再MediaWiki的代码仓库进行更新,在那里他们可以在那里他们可以自动读取任何维基,并利用LocalisationUpdate扩展更新。在维基网站,数据库的信息现在只用于用户化,并没有任何更多的定位。MediaWiki扩展
和相关程序,如机器人,也定位在translatewiki.net。

为了帮助翻译人员理解接口信息的语境和意义,给每一个信息都提供记录是MediaWiki的一个不错的实践。这些记录是被存储在特殊的消息文件中,用的是’’qqq’’语言的代码,这种语言并不是任何一种真实存在的语言。之后每条消息的翻译记录会在translatewiki.net.上的翻译界面显示。另一个有用的工具是’’qqx’’语言,每当使用’’&uselang’’参数显示一个维基界面时(例如:https://en.wikipedia.org/wiki/Special:RecentChanges?uselang=qqx ),MediaWiki会将信息的key而不是value显示在界面上,这对于识别信息是否被修改过很有用。

注册用户可以在他们的首选项中设置自己的界面语言,以覆盖该网站的默认界面语言。MediaWiki也支持回退语言:如果信息无法以所选择的语言显示,它将在尽可能接近的语言显示,而不一定是英语。例如,布列塔尼回退的语言是法语。

12.6 用户

用户在代码中使用的用户类的实例表示,它封装了用户所有的特定的设置(用户ID、名称、权限,密码,电子邮件地址等等)。客户类使用访问器来访问这些字段,他们的工作主要是确定用户是否登录、请求的选项是否与cookies符合,以及是否需要数据查询操作。一般页面的大部分设置都记录在cookie中,以减少对数据库的使用。

MediaWiki提供了一个非常细致的权限系统,每一个可能的操作都要用户权限。例如,进行”回滚”操作(即:快速回滚最后编辑页面的用户的编辑)时,用户需要回滚权限,包括在MediaWiki的’’sysop’’的用户集中。但它也可以被添加到其他用户组,或有一个专门的用户组提供本许可(英文维基百科,就有这样的’’Rollbackers’’组)。用户权限的定制是通过修改’’LocalSettings.php’’中的’’$wgGroupPermissions’’数组,例如:’’$wgGroupPermissions[‘user’][‘movefile’] = true;’’允许所有注册用户重命名文件。一个用户可以属于几个组,并继承与他们每个人相关的最高权利。

然而,MediaWiki的用户权限系统是根据维基百科的想法设计的;一个网站的内容是对所有人开放,而且对于用户只有一定的行动限制。MediaWiki缺少一个统一的、普适的权限的概念;它不提供传统的CMS功能,比如:按主题或内容类型来限制阅读和确定写权限。MediaWiki的一些扩展在一定程度上提供了这样的功能。

12.7 内容

内容结构

命名空间的概念被使用在维基百科的UseModWiki时代,讨论页的标题是“<项目名称> /Talk”。命名空间一开始就被正式引入了Magnus Manske的第一个“PHP脚本”。这些年来虽然有了许多改动,但仍使用同样的函数来区分不同种类的内容。他们由一个网页标题分离出来的前缀加一个冒号组成(例如:’’Talk:’’、’’File:’’、’’Template:’’);主内容的命名空间没有前缀。维基百科用户很快就采纳了他们,他们为社区提供了不同的空间来发展。命名空间已被证明是MediaWiki的重要特征,作为他们创建一个Wiki社区、促进社区发展,并且建立元层次的讨论、、门户网站、用户配置文件,等等的必要前提。

MediaWiki主要内容的命名空间的默认配置是平的(没有子页面),因为这就是维基百科的运行方式,启用子页面是无意义的。不过他们会在其他命名空间上启用(例如:在’’User’’上人们可以做打草稿等工作)并且陈列一些琐碎的内容。

命名空间通过类型的不同对内容进行区分;在同一个命名空间中,页面可以按标题类别进行分组,伪分层组织方案在是MediaWiki1.3被引入。

内容处理:MediaWiki标记语言和语法分析器

用户生成的页面并不是以HTML存储在MediaWiki上,而是用一种MediaWiki的特殊标记语言,这种语言有时被称作” wikitext”。 它允许用户去更改格式(如粗体,斜体使用引号),添加链接(用方括号),包括模板,插入上下文相关的内容(如日期或签名),并使其他特殊的事情达到一个令人难以置信的数量。( https://www.mediawiki.org/wiki/Markup_spec |详尽文档 )。

要显示一个页面,相关内容需要被分析,组装所有外部或动态的部分,并转换为适当的HTML。解析器是MediaWiki的重要组成部分,这使得它很难被改变或改善。由于全球数以亿计的Wiki页面需要解析器按它固有的方式持续输出HTML,所以它必须保持非常稳定。

MediaWiki一开始并没有使用标记语言,它是随着” UseModWiki”开始使用的,之后演变和进化成了一种需求。在缺少正常的规则的情况下,MediaWiki的标记语言已经成为了一个复杂而特殊的语言,基本上之和解析器兼容,它不能被表示为一个正式的语法。当前解析器的规范被戏称为“任何解析器从wikitext吐出来的,都要用几百个测试用例去检验”。

有许多人尝试去完成其他的解析器,但至今没有成功。在2004年,Jens Frank写了一个分解器来解析wikitext,并且被维基百科采用了,但仅使用了三天就不得不被禁用,原因是在PHP数组的内存分配上性能很差。从那时起,大多数的分析是通过一大堆的正则表达式,和许多的辅助函数完成的。wiki标记,和解析器需要支持的所有特殊情况,也都变得更为复杂,使得未来的尝试更加困难。

一个显著的改进是Tim Starling的预处理器重写在MediaWiki 1.12,其主要目的是通过复杂的模板提高页面上的分析性能。预处理器将wikitext转换为一个XML DOM树来表现文档(模板调用,解析器的功能,标签挂钩,章节标题,和一些其他的结构)的部分内容,但是可以跳过”枯枝”,比如在扩展模板中,没有事例跟随的’’#switch’’还有未使用过的默认参数,解析器接着遍历DOM结构并将其内容转换为HTML。

最近已经有一些对可视化编辑器的改善,使分析过程有所改进(或是使它更快),所以对于解析器上MediaWiki标记和最终HTML代码之间中间层的改进已重新开始。(参见下文的”未来”)。

特殊词和模板

MediaWiki提供了”特殊词”来修改网页的一般行为或者将动态内容包含进去。他们由开关’’NOTOC‘’(隐藏内容的自动表)或’’NOINDEX‘’(告诉搜索引擎不索引页面),变量’’,结构体’’![](GENDER:}}’’,’’![](PLURAL:}}’’及’’![](GRAMMAR:}}’’组成,被用于本地化的用户界面,是解析器的功能。

最常见的包括MediaWiki其他页面内容的方式是使用模板的。模板常被用来包括在不同的页面上的相同的内容,例如,维基百科文章中的导航面板或维护横幅;这种创建部分页面布局并在成千上万的文章重用他们,还可以正常地维护好他们的能力,对像维基百科这样的网站有着巨大的作用。

然而,用户也以一个完全不同的目的使用(和滥用)模板。MediaWiki 1.3使得模板带参数来改变输出;添加默认参数的能力(于MediaWiki 1.6被引入)以PHP实现了函数式程序设计语言的建设,这最终成为性能方面最昂贵的特征之一。

Tim Starling之后开发了额外的解析器函数(ParserFunctions扩展)来作为应对用户疯狂使用模板的情况的临时措施。这套功能包括逻辑结构’’#if’’和’’#switch’’,和其他功能如’’#expr’’(评价的数学表达式)和’’#time’’(时间格式)。

很快,维基百科用户开始创建更复杂的模板,使用新的功能,这大大降低了使用许多模板的页面上的解析性能。新的处理器被引入MediaWiki 1.12(一个主要的结构变化)来部分解决这个问题。近日,MediaWiki的开发者们已经讨论了是否使用实际的脚本语言,或许是Lua,来提高性能。

媒体文件

用户通过’’Special:Upload’’页面来上传文件,管理员可以通过额外的名单来配置允许被上传的文件类型。文件一旦上传,便存储在文件系统上的一个文件夹内,并列在一个专用的缩略的目录中。

因为维基的教育使命,MediaWiki支持可能在其他Web应用程序或CMS上罕见的文件类型,如SVG矢量图像和多页的PDF文件和DjVus。它们呈现为PNG文件,并可显示缩略图和内联,同样也可以作为其他更常见的图像文件,如GIF,JPG和PNG图片。

当一个文件被上传,它会产生一个包含上传者信息的’’File:’’页面,这是自由文本,通常包括版权信息(作者、许可证)和件的内容的描述或分类文(名称、位置、日期、类别等)。而私人维基可能根本不在乎这个信息,在媒体库像维基共享资源中,组织收集和保证共享这些文件的合法性才是关键。这些元数据被认为应该被存储在一个可查询的结构像一个数据库表,事实上也是如此。这将大大方便搜索,并且也可由第三方使用,比如,通过API使用。

大部分维基百科的网站仍允许本地文件上传到每个维基,但社区在试着将得到许可,免费的媒体文件存储到维基百科的免费媒体库(Wikimedia Commons)中。任何维基百科的网站可以像播放本地媒体文件一样播放媒体库中的文件。这种惯例可以避免每个使用文件的网站都要传一份文件。

MediaWiki从一开始就支持国外媒体库,即:通过API和’’ForeignAPIRepo’’系统访问在别的维基上的媒体文件的能力,而且有了成果。.16以后的版本,任何页面的网站可以很容易地通过’’InstantCommons’’特征使用维基共享资源。使用外来存储库时,缩略图存储在本地以节省带宽。然而,这是不可能从另一个维基上传文件到外国媒体库。

12.8 MediaWiki的定制和延伸

等级

MediaWiki的架构提供了不同的方式来定制和扩展软件。这可以通过不同级别的权限做到:

  • 系统管理员可以安装扩展和皮肤,并配置维基独立的辅助程序(例如,图像缩略图和TeX渲染)和全局设置(详见”配置”)。

  • 维基站长(有时也被称为“管理员”)可以编辑用于所有维基网站的小配件,JavaScript和CSS的设置。

  • 每个注册的用户都可以通过自己选择参数或制作mod来定制自己的设置和界面。

如果机器的API被启用,外部程序就可以通过它与 MediaWiki沟通,主要可以使用户能够访问任何功能和数据。

JavaScript和CSS

MediaWiki可以通过自定义的维基页面阅读并使用适用于所有网页和皮肤的JavaScript和CSS:这些页面在’’MediaWiki:’’的命名空间,因此只能由站长编辑;例如,’’MediaWiki:Common.js’’中的JavaScript的模组适用于所有皮肤,’’MediaWiki: Common.css’’中的CSS模组也适用于所有皮肤,但’’MediaWiki: Vector.css’’只适用于用户的载体皮肤。

用户可以通过用户页面的子页面来做同样类型的修改,不过只适用于他们自己的界面(例如:JavaScript的’’User:/common.js’’适用于所有皮肤,CSS的’’User:/common.css’’适用于所有皮肤,CSS的模组’’User:/vector.css’’只适用于载体皮肤)。

如果安装扩展的小部件,站长也可以编辑小部件,例如:JavaScript代码片段,用户可以自己选择提供的功能的开关。以后会尽可能让这些小部件通过维基来传播,这样可以避免重复。

这套工具产生了巨大的影响,大大提高了MediaWiki的软件开发的民主化。个人用户有权为自己添加功能;高权限用户可以非正式地,或通过全局配置的管理员控制系统来与他人分享。这个框架更适用于小的、独立的模组,并且通过一个较低的门槛而不是代码复杂的模组来进入扩展和hook。

扩展和皮肤

当JavaScript和CSS的模组不够时,页面提供了一个hook系统,来让第三方开发者运行自定义的PHP代码,来代替用于特殊事件的MediaWiki代码。(MediaWiki hooks参见https://www.mediawiki.org/wiki/Manual:Hooks )。MediaWiki的延伸通过hook来添加到代码里。

在MediaWiki开始使用hook之前,添加定制的PHP代码意味着修改核心代码,这是既不容易,也不推荐的。第一个hook由Evan Prodromou提出并于2004添加;这些年来已经有许多新的hook因为需要被添加。使用hook甚至可以扩展MediaWiki标记的作用。

扩展系统还并不完善,扩展的注册是在运行的代码的基础上实现的,而不是缓存的数据,这限制了代码的抽象和优化,并降低了性能。但总的来说,扩展的框架现在是一个相当灵活的基础设施,可以使专用代码更加模块化,在扩展时保持核心软件,并使第三方用户创建自定义功能上更容易定制。

相反的是,在不重新构建基础的情况下,给MediaWiki写一个新皮肤是非常困难的。在MediaWiki中,皮肤都是延伸自’’Skin’’类的PHP类,他们通过收集需要的数据来实现HTML。已经有很长寿命的“MonoBook“皮肤很难被定制,因为它包含很多浏览器特定的、支持旧的浏览器CSS ,编辑模板和CSS需要根据所有浏览器和平台的变化来进行很多后续的变化。

API

除了’’index.php’’,其他主要的进入MediaWiki的点就是’’api.php’’了,这是用来访问它的机器可读的网页查询API(应用编程接口)。

维基百科用户最初创建的“机器人”,是通过抓取屏幕中的HTML内容来工作的。这个方法很不可靠,出了许多问题。为了改善这种情况,开发商推出的一种只读接口(位于’’query.php’’),然后进化成一个完整的读写机API,这个API提供了直接的、高层次的对包含在MediaWiki数据库数据的访问( https://www.mediawiki.org/wiki/API:Main_page |详尽文档 )。

客户端程序可以使用该API进行登录,获取数据,并进行更改。这个API支持简单的基于网络的JavaScript客户端和最终用户程序。几乎所有可以通过Web界面来完成的事情,基本上都可以通过这个接口来完成。客户几乎所有可以通过Web界面来完成的事情,基本上都可以通过这个接口来完成。客户端库是通过多种语言实现API的,包括Python和.NET。

12.9未来

当初那个由PHP开发者于夏天单独完成的工程,已经成为了MediaWiki这个成熟的、稳定的维基引擎,甚至仅通过很小的基础设施就能支撑维基百科这个全球前十的网站。这个框架的实现,是通过一个优秀的开发团队,不断地进行性能优化和架构修改来完成的。

随着网络技术的发展,和维基百科的增长,MediaWiki的架构需要不断的改进和并添加新的功能。例如,不断发展的可视化编辑工程,促使了解析器的发展,以及维基标记语言、DOM、HTML之间的转换。

MediaWiki是一种用于不同目的的工具。例如,对于维基媒体项目,它被用来完成创建和管理一个百科全书(维基百科),支持一个庞大的媒体库(维基共享资源),将扫描转换为文本等工作。在其他情况下,MediaWiki是作为一个集团的CMS,或作为一个数据存储库,有时会与语义框架结合。这些一开始并不在计划内的用途,可能会促使软件框架不断地进行内部调整。因此,MediaWiki框架是非常活跃的,就想它所支持的有许多用户的巨大的社区。

12.10扩展阅读

  • 1、 https://www.mediawiki.org/wiki/MediaWiki |MediaWiki的文档和支持
  • 2、 https://phabricator.wikimedia.org/diffusion/ |自动生成的页面文件
  • 3、MySQL用户大会上,维基百科的Domas Mituzas的发言, https://dom.as/talks/ |site internals, configuration, code examples and management issues

    12.11鸣谢

    本章是协作创作。Guillaume Paumier通过组织MediaWiki的用户和代码开发者所给材料,完成了本章大部分内容。Sumana Harihareswara协调了界面和输入采集阶段。特别感谢Antoine Musso, Brion Vibber, Chad Horohoe, Tim Starling, Roan Kattouw, Sam Reed, Siebrand Mazeland, Erik M?ller, Magnus Manske, Rob Lanphier, Amir Aharoni, Federico Leva, Graham Pearce等人提供材料和审核内容。