所有分析说明采用文字使用浅红色、小四号楷体。
Index.php
//mediawiki程序入口
This is the main web entry point for MediaWiki.
现在开始看程序的第一句代码,判断PHP版本是否是5.3.2及以上,如果不是就在页面报错提示。
- if ( !function_exists( 'version_compare' ) || version_compare( PHP_VERSION, '5.3.2' ) < 0 ) {
- // We need to use dirname( __FILE__ ) here cause __DIR__ is PHP5.3+
- require dirname( __FILE__ ) . '/includes/PHPVersionError.php';
- wfPHPVersionError( 'index.php' );
- }
接下来是比较关键的代码了,引入一个PHP文件 Webstart.php。
- require __DIR__ . '/includes/WebStart.php';
Webstart.php
* This does the initial set up for a web request.
* It does some security checks, starts the profiler and loads the
* configuration, and optionally loads Setup.php depending on whether
* MW_NO_SETUP is defined.
* Setup.php (if loaded) then sets up GlobalFunctions, the AutoLoader,
* and the configuration globals (though not $wgTitle).
WebStart.php的文件注解部分如上,大概说此文件执行的操作是为一个Web请求进行初始化设置:进行安全检查、调试开启、装载配置文件里的全局变量及常量。最后如果没有安装过mediawiki则调用setup.php执行安装mediawiki操作。这个文件中调用了Defines.php(常量)、LocalSettings.php(配置文件,全局变量),另外还在这里根据配置开启字符缓冲区,回调方法是OutputHandler.php的wfOutputHandler方法。
- if ( ini_get( 'register_globals' ) ) {
- die( 'MediaWiki does not support installations where register_globals is enabled. '
- . 'Please see mediawiki.org '
- . 'for help on how to disable it.' );
- }
如果PHP配置项register_globals是打开状态(ON),则mediawiki无法运行。
# bug 15461: Make IE8 turn off content sniffing. Everybody else should ignore this
# We're adding it here so that it's *always* set, even for alternate entry
# points and when $wgOut gets disabled or overridden.
- header( 'X-Content-Type-Options: nosniff' );
针对IE8进行关闭内容嗅探,大家应该应该忽略这个
- $wgRequestTime = microtime( true );
函数返回当前 Unix 时间戳和微秒数。
- unset( $IP );
注销定义$IP的变量
- define( 'MEDIAWIKI', true );
定义一个常量mediawiki
# Full path to working directory.
# Makes it possible to for example to have effective exclude path in apc.
# __DIR__ breaks symlinked includes, but realpath() returns false
# if we don't have permissions on parent directories.
- $IP = getenv( 'MW_INSTALL_PATH' );
- if ( $IP === false ) {
- $IP = realpath( '.' ) ?: dirname( __DIR__ );
- }
通过获取PHP的环境变量,获取安装的安装目录。
# Load the profiler
- require_once "$IP/includes/profiler/Profiler.php";
- $wgRUstart = wfGetRusage() ?: array();
...
# Start the profiler
//StartProfiler.php文件里只调用了ProfilerStub.php。根据上下文来看ProfilerStub.php里定义的两个主要的函数wfProfileIn()、wfProfileOut()应该是做Debug用的。
- $wgProfiler = array();
- if ( file_exists( "$IP/StartProfiler.php" ) ) {
- require "$IP/StartProfiler.php";
- }
- ...
- if ( !defined( 'MW_NO_SETUP' ) ) {
- require_once "$IP/includes/Setup.php";
- }
require_once了一大堆文件:Profiler.php(分析其,主要用于DEBUG调试使用)、AutoLoader.php(类管理器,类似JAVA中SPRING的IOC容器)、Defines.php、StartProfiler.php、DefaultSettings.php、autoload.php、NoLocalSettings.php、OutputHandler.php、Setup.php……
接下来到了程序业务处理入口:
- $mediaWiki = new MediaWiki();
- $mediaWiki->run();
Mediawiki.php
mediawiki.php里定义了MediaWiki类。其中包括很多的wiki对象的方法。接着为$mediaWiki对象开辟内存空间。
- public function __construct( IContextSource $context = null ) {
- if ( !$context ) {
- $context = RequestContext::getMain();
- }
- $this->context = $context;
- $this->config = $context->getConfig();
- }
通过构造方法,获取request请求对象、配置信息。
- public function run() {
- try {
- //请求中如果包含延迟请求,和系统最后一次操作时间对比。如果最后一次操作时间大于请求最大延迟,则提示超时。
- $this->checkMaxLag();
- try {
- //关键方法,主要做业务流转相关操作。
- $this->main();
- } catch ( ErrorPageError $e ) {
- // Bug 62091: while exceptions are convenient to bubble up GUI errors,
- // they are not internal application faults. As with normal requests, this
- // should commit, print the output, do deferred updates, jobs, and profiling.
- wfGetLBFactory()->commitMasterChanges();
- $e->report(); // display the GUI error
- }
- if ( function_exists( 'fastcgi_finish_request' ) ) {
- fastcgi_finish_request();
- }
- $this->triggerJobs();
- $this->restInPeace();
- } catch ( Exception $e ) {
- MWExceptionHandler::handle( $e );
- }
- }
- 现在进入关键方法main()方法
- // Send Ajax requests to the Ajax dispatcher.
- if ( $this->config->get( 'UseAjax' ) && $request->getVal( 'action', 'view' ) == 'ajax' ) {
- // Set a dummy title, because $wgTitle == null might break things
- $title = Title::makeTitle( NS_MAIN, 'AJAX' );
- $this->context->setTitle( $title );
- $wgTitle = $title;
- $dispatcher = new AjaxDispatcher( $this->config );
- $dispatcher->performAction( $this->context->getUser() );
- wfProfileOut( __METHOD__ );
- return;
- }
判断是否启用AJAX请求,并且请求中$action值为ajax,则将Ajax请求发送到Ajax dispather处理器。
If the user has forceHTTPS set to true, or if the user
// is in a group requiring HTTPS, or if they have the HTTPS
// preference set, redirect them to HTTPS.
// Note: Do this after $wgTitle is setup, otherwise the hooks run from
// isLoggedIn() will do all sorts of weird stuff.
- if (
- $request->getProtocol() == 'http' &&
- (
- ...
- wfProfileOut( __METHOD__ );
- return;
- }
- }
如果forcehttps设置为true,并且使用https访问,进行重定项处理
- if ( $this->config->get( 'UseFileCache' ) && $title->getNamespace() >= 0 ) {
- wfProfileIn( 'main-try-filecache' );
- ...
- wfProfileOut( 'main-try-filecache' );
- }
判断配置是否开启文件缓存功能,并且命名空间大于等于1的情况,使用文件缓存机制相关功能。
命名空间值 |
命名空间值含义 |
-1 |
Special: |
0 |
Template: |
1 |
Talk: |
2 |
User: |
3 |
User_talk: |
4 |
Test: |
5 |
Test_talk: |
6 |
Image: |
7 |
Image_talk: |
8 |
MediaWiki: |
9 |
MediaWiki_talk: |
10 |
Template: |
11 |
Template_talk: |
12 |
Help: |
13 |
Help_talk: |
14 |
Category: |
15 |
Category_talk: |
16 |
ONLINEPAY |
// Actually do the work of the request and build up any output
- $this->performRequest();
处理请求的工作和建立输出。在此方法会生程一个输出对象$output,此对象有相应方法可以设置不同的输出结果。
- wfProfileIn( __METHOD__ );
方法第一句,发现mediawiki中基本方法入口都要这么一句,他的后面出现wfProfileOut( __METHOD__ )跟踪发现为启动DEBUG模式后,进行相应数据的打印。开启打印方法LocationSettings.php里设置$wgDebugLogFile=d:\a.txt值。注意:wfProfileIn和wfProfileOut需要成对出现,否则会出错。而且调试信息的输出顺序是:先输出已经匹配好了的一对wfProfileIn和wfProfileOut的调试信息,也即遇到wfProfileOut时才输出该段调试信息,而不是wfProfileIn。。
- if ( $request->getVal( 'printable' ) === 'yes' ) {
- $output->setPrintable();
- }
判断请求是否有打印请求。如果有就在输出对象中进行标注。
- $unused = null; // To pass it by reference
- wfRunHooks( 'BeforeInitialize', array( &$title, &$unused, &$output, &$user, $request, $this ) );
通过请求对象,进行初始化之前的检查工作。这个属于系统钩子程序,应该需要插件进行实现BeforeInitialize方法,我全文搜索没有此方法的具体实用。
// Check user's permissions to read this page.
// We have to check here to catch special pages etc.
// We will check again in Article::view().
- $permErrors = $title->isSpecial( 'RunJobs' )
- ? array() // relies on HMAC key signature alone
- : $title->getUserPermissionsErrors( 'read', $user );
- if ( count( $permErrors ) ) {
根据title进行判断用户是否有次页面的访问read权限。如果权限不足构造项页面进行返回。
// Either all DB and deferred updates should happen or none.
// The later should not be cancelled due to client disconnect.
- ignore_user_abort( true );
PHP提供的函数,如果设置为 true,则忽略与用户的断开。PHP 不会检测到用户是否已断开连接,直到尝试向客户机发送信息为止。
// Now commit any transactions, so that unreported errors after
// output() don't roll back the whole DB transaction
- wfGetLBFactory()->commitMasterChanges();
事物提交,存在错误进行回滚。
// Output everything!
- $this->context->getOutput()->output();
页面输出到前台页面,在此句之前所有数据不携带样式。词句代码执行会按返回数据类型进行添加不同的skin。
- wfProfileOut( __METHOD__ );