WikiDebug
This extension contains a small set of tools that I find useful when debugging my extensions. I have not prepared a downloadable version of the file yet, but the full source code is listed towards the bottom of this page.
Currently the extension contains two tools: 'describe_table' and 'show_source'.
Contents
describe_table
This extension adds a new tag that allows you to get a live description of a DB table.
Syntax
<describe_table table="{TableName}" showindexes="{bool}"></describe_table>
- {TableName} is the name of a table in the wiki's database. Do not include the prefix specified by $wgPrefix.
- showindexes is an optional argument, which defaults to true if omitted. Set to false to disable index display.
As a security measure this extension will only allow display of tables added to the $wgWikiDebug_ViewableTables array. Any other tables are blocked.
For example, to view the table 'page', you would need to add the following to LocalSettings.php:
$wgWikiDebug_ViewableTables[] = "page";
Example 1: Description with indexes
Providing the table 'page' is in the viewable tables array, then you can show it's description by using the following syntax:
<describe_table table="page"></describe_table>
Live Table Definition: page
CREATE TABLE IF NOT EXISTS `page` ( `page_id` int(8) unsigned NOT NULL AUTO_INCREMENT, `page_namespace` int(11) NOT NULL DEFAULT '0', `page_title` varchar(255) NOT NULL DEFAULT '', `page_restrictions` tinyblob NOT NULL, `page_counter` bigint(20) unsigned NOT NULL DEFAULT '0', `page_is_redirect` tinyint(1) unsigned NOT NULL DEFAULT '0', `page_is_new` tinyint(1) unsigned NOT NULL DEFAULT '0', `page_random` double unsigned NOT NULL DEFAULT '0', `page_touched` varchar(14) NOT NULL DEFAULT '', `page_latest` int(8) unsigned NOT NULL DEFAULT '0', `page_len` int(8) unsigned NOT NULL DEFAULT '0', `page_content_model` varbinary(32) DEFAULT NULL, `page_links_updated` varbinary(14) DEFAULT NULL, `page_lang` varbinary(35) DEFAULT NULL, PRIMARY KEY (`page_id`), UNIQUE KEY `name_title` (`page_namespace`,`page_title`), KEY `page_random` (`page_random`), KEY `page_len` (`page_len`), KEY `page_redirect_namespace_len` (`page_is_redirect`,`page_namespace`,`page_len`) ) ENGINE=MyISAM;
Example 2: Description without indexes
<describe_table table="page" showindexes="false"></describe_table>
Live Table Definition: page
CREATE TABLE IF NOT EXISTS `page` ( `page_id` int(8) unsigned NOT NULL AUTO_INCREMENT, `page_namespace` int(11) NOT NULL DEFAULT '0', `page_title` varchar(255) NOT NULL DEFAULT '', `page_restrictions` tinyblob NOT NULL, `page_counter` bigint(20) unsigned NOT NULL DEFAULT '0', `page_is_redirect` tinyint(1) unsigned NOT NULL DEFAULT '0', `page_is_new` tinyint(1) unsigned NOT NULL DEFAULT '0', `page_random` double unsigned NOT NULL DEFAULT '0', `page_touched` varchar(14) NOT NULL DEFAULT '', `page_latest` int(8) unsigned NOT NULL DEFAULT '0', `page_len` int(8) unsigned NOT NULL DEFAULT '0', `page_content_model` varbinary(32) DEFAULT NULL, `page_links_updated` varbinary(14) DEFAULT NULL, `page_lang` varbinary(35) DEFAULT NULL, PRIMARY KEY (`page_id`), UNIQUE KEY `name_title` (`page_namespace`,`page_title`), KEY `page_random` (`page_random`), KEY `page_len` (`page_len`), KEY `page_redirect_namespace_len` (`page_is_redirect`,`page_namespace`,`page_len`) ) ENGINE=MyISAM;
Example 3: Non-viewable table
If we try and look at a table that is not listed in the $wgWikiDebug_ViewableTables array we get the following:
Live Table Definition: user
Table cannot be viewed. It either does not exist, or is protected against viewing.
show_source
This extension adds a new tag that allows you to display the contents of a php file within the page. For security reasons it only allows the display of files that are added to an array of allowed files. If an extension that adds a hook for the 'php' tag has been installed (e.g. the GeSHi Syntax Highlighter) then this will be used to wrap the output, otherwise 'pre' tags will be used.
Note: The use of 'php' tags is currently disabled, as it trips up if the code contains a closing tag, as in the example below.
Syntax
<show_source file="{FileName}"></show_source>
- {FileName} is the name of a file residing on the server.
As a security measure this extension will only allow display of tables added to the $wgWikiDebug_ViewableFiles array. Any other files are blocked.
For example, to view the file containing this extension, you would need to add the following to LocalSettings.php:
$wgWikiDebug_ViewableFiles[] = "WikiDebug.php";
Note that paths are relative to the current include path, which includes the extensions directory on this wiki, but which may not on yours.
Example 1: WikiDebug.php
Here is the current (live) contents of WikiDebug.php - the file that contains this extension.
<show_source file="WikiDebug.php"></show_source>
Live sourcecode viewer: WikiDebug.php
Last modified: 2024-06-11 04:19:38
<?php if (!defined('MEDIAWIKI')) die("MediaWiki extensions cannot be run directly."); /** * An extension to implement various useful debugging tools. * * @author Mark Clements <mclements at kennel17 dot co dot uk> * @copyright Copyright © 2006-2024, Mark Clements * @license http://creativecommons.org/licenses/by-sa/2.5/ cc-by-sa 2.5 or later * @version $Rev: 2439 $ */ // The $wgVersion global variable was deprecated in MW 1.35, in favour of a new // constant. All code should use the constant rather than the variable, but for // backwards compatibility we create the constant if it is not already defined. // Note that the constant was back-ported to other release branches, but was not // present in the *.0 release, so they do not affect the minimum version requirements // for removing this shim. // @back-compat MW < 1.35 if (!defined("MW_VERSION")) define("MW_VERSION", $wgVersion); // Setup version number $pMCExt_Version = '$Rev: 2439 $'; $pMCExt_Version = substr($pMCExt_Version, 6, -2); // Setup extension credits $wgExtensionCredits['other'][] = array( 'name' => 'WikiDebug', 'version' => "r" . $pMCExt_Version, 'author' => 'Mark Clements', 'description' => "A set of debugging tools for extension developers", 'url' => 'http://www.kennel17.co.uk/testwiki/Debugging%20tools', ); // Tidy up unset($pMCExt_Version); if (!isset($wgWikiDebug_ViewableTables)) $wgWikiDebug_ViewableTables = array(); if (!isset($wgWikiDebug_ViewableFiles)) $wgWikiDebug_ViewableFiles = array(); $pWikiDebug_Messages = array( // Messages for wfWikiDebug_DescribeTable() 'debug_table_header' => "Live Table Definition: $1", 'debug_no_table' => "No table specified.", 'debug_table_not_found' => "Table cannot be viewed. It either does not " . "exist, or is protected against viewing.", 'debug_no_fields' => "No fields found.", 'debug_index_header' => "Indexes", 'debug_no_indexes' => "No indexes defined.", 'debug_error_reading_tabledef' => "There was an error reading the table " . "definition!", // Messages for wfWikiDebug_ShowSource() and wfWikiDebug_ShowRevision() 'debug_file_header' => "Live source code viewer: $1", 'debug_last_modified' => "Last modified: $1", 'debug_no_file' => "No file specified.", 'debug_file_blocked' => "File cannot be viewed. It either does not exist, " . "or is protected against viewing.", 'debug_file_blocked_short' => "File not found", 'debug_file_unreadable' => "File cannot be read.", 'debug_no_version' => "version unknown", 'debug_version' => "rev \$1", ); // DB_PRIMARY was introduced in MW 1.36, at which point DB_MASTER was made into an // alias. // To maintain support for older versions of MediaWiki, if DB_PRIMARY is not defined, // then we define it here. // @back-compat MW < 1.36 if (!defined("DB_PRIMARY")) { define("DB_PRIMARY", DB_MASTER); } // Parser hooks. // The ParserFirstCallInit hook was added in MediaWiki 1.12. // For older versions, of MediaWiki, we manually call the hook function as part of // the main extension registration function, as this hook is ignored. $wgHooks['ParserFirstCallInit'][] = "wfWikiDebug_RegisterParserHooks"; function wfWikiDebug_RegisterParserHooks(&$Parser) { // Specific tags to be parsed $Parser->setHook( "describe_table", "wfWikiDebug_DescribeTable" ); $Parser->setHook( "show_source", "wfWikiDebug_ShowSource" ); $Parser->setHook( "show_revision", "wfWikiDebug_ShowRevision" ); // We also accept <show_version> as an alias for <show_revision>, for // backwards-compatibility reasons. $Parser->setHook( "show_version", "wfWikiDebug_ShowRevision" ); return true; } $wgExtensionFunctions[] = "wfWikiDebug"; function wfWikiDebug() { global $wgMessageCache, $wgParser, $wgHooks; global $pWikiDebug_Messages, $wgWikiDebug_ViewableFiles; // Define the extension's messages. // If the old-style $wgMessageCache object is available then use this, as it // gives a better result (the messages are defined, and are editable via // Special:AllMessages). // If the $wgMessageCache object is not defined, we instead use the // MessagesPreLoad hook, to populate the message keys with our strings. // This is less good as (a) the messages won't show up for editing on // Special:AllPages; and (b) I suspect it is detrimental to performance. // However, it does mean that our messages will appear correctly in the // interface, and if you manually create the appropriate page in the // MediaWiki namespace, then the local version will be correctly used. // TODO: Is there a better way of handling it that resolves the above issues? // The LocalisationCacheRecache hook looked promising, but doesn't seem // to work well enough for us (it seems not to run, presumably because // a recache needs to be triggered somehow). if (is_object($wgMessageCache)) { $wgMessageCache->addMessages($pWikiDebug_Messages); } else { $wgHooks['MessagesPreLoad'][] = "wfWikiDebug_PreloadMessage"; } // If this is MediaWiki < 1.12, then the ParserFirstCallInit hook does not exist, // therefore we manually call our hook registration function with the global // Parser object. // @back-compat MW < 1.12 if (version_compare(MW_VERSION, '1.12', '<')) { wfWikiDebug_RegisterParserHooks($wgParser); wfWikiDebug_SetupParserFunctions($wgParser); } // Tidy all paths in the list of viewable files, to ensure that directory // separators are treated in a system-agnostic fashion. foreach ($wgWikiDebug_ViewableFiles as $Key => $Path) $wgWikiDebug_ViewableFiles[$Key] = pwfWikiDebug_TidyPath($Path); } function wfWikiDebug_DescribeTable($Input, $Args, $Parser = null) { global $wgParser; global $wgWikiDebug_ViewableTables; // The $Parser argument was added in MediaWiki 1.5.8. Therefore, to keep // compatibility with older versions, we need to default the function argument // to null and default to the global $wgParser argument if it is not set. // @back-compat MW < 1.5.8 if (!isset($Parser)) $Parser = $wgParser; WikiDebug_DisableCache($Parser); if (isset($Args['table'])) $TableName = $Args['table']; else $TableName = ""; $Output = "<h3>" . WikiDebug_Msg('debug_table_header', $TableName) . "</h3>\n"; if ($TableName == "") $Output .= WikiDebug_Msg('debug_no_table'); elseif (!in_array($TableName, $wgWikiDebug_ViewableTables)) $Output .= WikiDebug_Msg('debug_table_not_found'); else { $DB = wfGetDB(DB_PRIMARY); if ($DB->tableExists($TableName)) { // Get the requested display mode. $Display = "sql"; if (isset($Args['display'])) $Display = strtolower(trim($Args['display'])); // Output the data according to the display mode. switch ($Display) { case "tables": case "table": // Get field data from table $objResult = $DB->query("DESCRIBE " . $DB->tableName($TableName) . ";"); $Fields = array(); while ( $row = $objResult->fetchObject() ) $Fields[] = $row; $objResult->free(); // If required, get index data from table $ShowIndexes = true; if (isset($Args['showindexes'])) { switch (strtolower(trim($Args['showindexes']))) { case "0": case "false": $ShowIndexes = false; } } $Indexes = array(); if ($ShowIndexes) { $objResult = $DB->query("SHOW INDEX FROM " . $DB->tableName($Args['table']) . ";"); while ( $row = $objResult->fetchObject() ) $Indexes[] = $row; $objResult->free(); } if (count($Fields) == 0) $Output .= WikiDebug_Msg('debug_no_fields'); else { $Output .= '<table border="1" cellpadding="5" ' . 'cellspacing="2"><tr>'; foreach ($Fields[0] as $RowHeader => $Row) $Output .= '<th>' . $RowHeader . '</th>'; foreach ($Fields as $Row) { $Output .= '<tr>'; foreach ($Row as $RowValue) { if ($RowValue === null || $RowValue == "") $RowValue = " "; $Output .= '<td>' . $RowValue . '</td>'; } $Output .= '</tr>'; } $Output .= '</table>'; if ($ShowIndexes) { $Output .= "<h3>" . WikiDebug_Msg('debug_index_header') . "</h3>\n"; if (count($Indexes) > 0) { $Output .= '<table border="1" cellpadding="5" ' . 'cellspacing="2"><tr>'; foreach ($Indexes[0] as $RowHeader => $Row) { if ($RowHeader != "Table") $Output .= '<th>' . $RowHeader . '</th>'; } foreach ($Indexes as $Row) { $Output .= '<tr>'; foreach ($Row as $RowHeader => $RowValue) { if ($RowHeader != "Table") { if ($RowValue === null || $RowValue == "") { $RowValue = " "; } $Output .= '<td>' . $RowValue . '</td>'; } } $Output .= '</tr>'; } $Output .= '</table>'; } else { $Output .= " " . WikiDebug_Msg('debug_no_indexes'); } } } break; case "sql": default: // Get field data from table $objResult = $DB->query("SHOW CREATE TABLE " . $DB->tableName($TableName) . ";"); if ($objResult !== false && $row = $objResult->fetchObject()) { $SQL = ""; foreach ($row as $key => $val) { if ($key = "Create Table") { $SQL = htmlspecialchars($val, ENT_COMPAT, "UTF-8"); } } $Removables = array( "/character set .* /Ui", "/collate .* /Ui", "/ ?DEFAULT CHARSET=.*( |$)/Ui", "/ ?COLLATE=.*( |$)/Ui", "/ ?AUTO_INCREMENT\s*=\s*([0-9])+/", ); $SQL = preg_replace($Removables, "", $SQL); $Search = array( "/^CREATE TABLE " . $DB->tableName($TableName) . "/i", "/TYPE=(.*)( |$)/Ui", "/^/m", // Space at the start of the line, to // ensure <pre> block works properly. "/([^;])$/", ); $Replace = array( "CREATE TABLE IF NOT EXISTS `" . $TableName . "`", "ENGINE=$1$2", " ", "$1;", ); $SQL = preg_replace($Search, $Replace, $SQL); $Output .= $SQL . "\n"; } else $Output .= WikiDebug_Msg('debug_error_reading_tabledef'); if ($objResult !== false) $objResult->free(); } } else $Output .= WikiDebug_Msg('debug_table_not_found'); } $Output = '<div style="border: 1px solid #AAAAAA; ' . 'background-color: #EEEEEE; padding: 1em;">' . $Output . '</div>'; return $Output; } function wfWikiDebug_ShowSource($Input, $Args, $Parser = null) { global $wgOut, $wgParser; // The $Parser argument was added in MediaWiki 1.5.8. Therefore, to keep // compatibility with older versions, we need to default the function argument // to null and default to the global $wgParser argument if it is not set. // @back-compat MW < 1.5.8 if (!isset($Parser)) $Parser = $wgParser; WikiDebug_DisableCache($Parser); $AllowPHPOutput = false; $OutputIsAlreadyEscaped = false; if (isset($Args['file'])) $FileName = $Args['file']; else $FileName = ""; $Header = WikiDebug_Msg('debug_file_header', $FileName); if ($FileName == "") { $Output = WikiDebug_Msg('debug_no_file'); $OutputIsAlreadyEscaped = true; } else { $FilePath = pwfWikiDebug_GetRealPath($FileName); if (!$FilePath) { $Output = WikiDebug_Msg('debug_file_blocked'); $OutputIsAlreadyEscaped = true; } elseif (($File = @fopen($FilePath, "r", true)) === false) { $Output = WikiDebug_Msg('debug_file_unreadable'); $OutputIsAlreadyEscaped = true; } else { // Get last modified date $ModDate = date("Y-m-d h:i:s", filemtime($FilePath)); $Header .= "<br><span style=\"font-size: 80%; " . "font-weight: normal;\">" . WikiDebug_Msg('debug_last_modified', $ModDate) . "</span>"; // Get length of file. fseek($File, 0, SEEK_END); $Length = ftell($File); fseek($File, 0, SEEK_SET); // Get contents of file. $Output = fread($File, $Length); // We don't care about read errors and we don't report them. Therefore, // convert any error results to an empty string. if (!is_string($Output)) $Output = ""; // If it was a PHP file and was successfully read, then enable the // PHP syntax-highlighter (if installed). // We detect this by seeing if the file contains the opening PHP tag. if (strpos($Output, "<?") !== false) $AllowPHPOutput = true; // Convert file to UTF-8, if we detect that it is in a different // encoding. // This can only be done if the multi-byte extension is enabled. If not, // we currently just output the file in its raw format, which may result // in encoding errors. // TODO: Is there a better way of handling situations where we can't // detect the encoding (e.g. error message)? Are there alternative // detection methods (e.g. built into MediaWiki)? if (function_exists("mb_detect_encoding")) { // The supported encodings are UTF-8 and CP1252. // The ordering is important - if these are the other way round then // CP1252 will always be detected as it covers all code points. // Note that CP1252 is a superset of ISO-8859-1, so this will also // correctly detect ISO-8859-1 encoding. // TODO: Are there other encodings that should be included in this // list? $SupportedEncodings = 'UTF-8, CP1252'; // Detect the encoding. Note that, because CP1252 is a // single-byte encoding with no invalid values, we won't ever fail // to detect something. $Encoding = mb_detect_encoding($Output, $SupportedEncodings, true); // Perform the conversion. // TODO: Is it safe to assume our output is always UTF-8? $Output = iconv($Encoding, "UTF-8", $Output); } } } $Header = "<h3>" . $Header . "</h3>\n"; if ($AllowPHPOutput && isset($Parser->mTagHooks['php'])) { // This can easily use more than the standard available memory, so remove // limit. $MemLimit = ini_get('memory_limit'); ini_set('memory_limit', -1); $Output = $Header . call_user_func($Parser->mTagHooks['php'], $Output, array(), $Parser); ini_set('memory_limit', $MemLimit); } else { if (!$OutputIsAlreadyEscaped) $Output = htmlspecialchars($Output, ENT_COMPAT, "UTF-8"); $Output = "<pre>" . $Header . $Output . "</pre>"; } return $Output; } function wfWikiDebug_ShowRevision($Input, $Args, $Parser = null) { global $wgOut, $wgParser; // The $Parser argument was added in MediaWiki 1.5.8. Therefore, to keep // compatibility with older versions, we need to default the function argument // to null and default to the global $wgParser argument if it is not set. // @back-compat MW < 1.5.8 if (!isset($Parser)) $Parser = $wgParser; WikiDebug_DisableCache($Parser); if (isset($Args['file'])) $FileName = $Args['file']; else $FileName = ""; if ($FileName == "") $Output = WikiDebug_Msg('debug_no_file'); else { $FilePath = pwfWikiDebug_GetRealPath($FileName); if (!$FilePath) $Output = WikiDebug_Msg('debug_file_blocked'); elseif (($File = @fopen($FilePath, "r", true)) === false) $Output = WikiDebug_Msg('debug_file_unreadable'); else { // Get length of file. fseek($File, 0, SEEK_END); $Length = ftell($File); fseek($File, 0, SEEK_SET); // Get contents of file. $Contents = fread($File, $Length); // We don't care about read errors and we don't report them. Therefore, // convert any error results to an empty string. if (!is_string($Contents)) $Contents = ""; $VersionRegex = '/(?:\$Rev:|\$LastChangedRevision:|\$Revision:|) ' . '?([0-9]+) ?\$/Ui'; $Result = preg_match($VersionRegex, $Contents, $matches); if (count($matches) > 0) $Output = WikiDebug_Msg('debug_version', $matches[1]); else $Output = WikiDebug_Msg('debug_no_version'); } } return "<i>(" . $Output . ")</i>"; } /////////////////////////// PARSER FUNCTIONS /////////////////////////// // MW 1.7 and above, only. // SETUP FUNCTIONS // The ParserFirstCallInit hook was added in MediaWiki 1.12. // For older versions, of MediaWiki, we manually call the hook function as part of // the main extension registration function, as this hook is ignored. $wgHooks['ParserFirstCallInit'][] = 'wfWikiDebug_SetupParserFunctions'; $wgHooks['LanguageGetMagic'][] = 'wfWikiDebug_SetupLanguageMagic'; function wfWikiDebug_SetupParserFunctions(&$Parser) { $Parser->setFunctionHook('IfExtensionPresent', 'wfWikiDebug_IfExtensionPresent'); return true; } function wfWikiDebug_SetupLanguageMagic(&$MagicWords) { // Add the magic word. // Set first array element to 0 to indicate non-case-sensitivity. // Additional elements are synonyms. $MagicWords['IfExtensionPresent'] = array(1, 'IfExtensionPresent'); // Return true, so other parser hooks execute properly. return true; } // INDIVIDUAL PARSER FUNCTIONS function wfWikiDebug_IfExtensionPresent($Parser, $Param1 = "", $Param2 = "", $Param3 = "") { global $wgExtensionCredits; $Matched = false; foreach ($wgExtensionCredits as $Type => $Extensions) { foreach ($Extensions as $Extension) { if (isset($Extension['name']) && $Extension['name'] == $Param1) { $Matched = true; break 2; } } } if ($Matched) return $Param2; else return $Param3; } /////////////////////////// SUPPORT FUNCTIONS /////////////////////////// // pwfWikiDebug_GetRealPath() // Takes a $FileName argument, and checks that (a) the file exists in the // include paths and (b) it is in the list of viewable files. // If it fails either of these tests, the function returns false, indicating that // the file cannot be viewed (there is deliberately no distinction between the // different reasons why it may not be viewable). // If it succeeds, and the file exists and is on the white list, then the real // path to the file is returned. // The function does not check whether the file is readable by the web user. function pwfWikiDebug_GetRealPath($FileName) { $FileName = pwfWikiDebug_TidyPath($FileName); // First, check that the file exists in the include path. We do this first // as it will allow us to use realpath() in the CanViewFile() function if we // need to (currently it isn't used, but at one point it was thought to be // required and I'm not sure that I won't change my mind again in the future). $FilePath = pwfWikiDebug_file_exists_incpath($FileName); if (!$FilePath) return false; // If file exists, check it is viewable. if (!pwfWikiDebug_CanViewFile($FileName)) return false; return $FilePath; } // pwfWikiDebug_file_exists_incpath() // For relative paths, checks if the file exists in the include path and returns // the full path of where it was found if it does, or false if not. // For absolute paths, it doesn't check the include path - it just returns the // supplied $File path if it exists, or false if not. // Function was modified from file_exists_incpath() - credits follow: /** * Check if a file exists in the include path * * @version 1.2.1 * @author Aidan Lister <aidan@php.net> * @link http://aidanlister.com/repos/v/function.file_exists_incpath.php * @param string $File Name of the file to look for * @return mixed The full path if file exists, FALSE if it does not */ function pwfWikiDebug_file_exists_incpath($File) { // If this is an absolute path, then we don't need to resolve it in terms // of the include path - just check whether the file exists. if (in_array(substr($File, 0, 1), array("\\", "/"))) { if (file_exists($File)) return $File; } // Otherwise it is a relative path, so look for it in each of the include // directories, selecting the first directory in which the file is found. else { $arrPaths = explode(PATH_SEPARATOR, ini_get('include_path')); foreach ($arrPaths as $Path) { // Skip over any blank paths. if ($Path != "") { // Formulate the absolute path, and if the file exists there, // return it. $FullPath = $Path . "/" . $File; if (file_exists($FullPath)) return $FullPath; } } } // If the file was not found in the path, return false. return false; } // pwfWikiDebug_CanViewFile() // Returns true if the named file is viewable according to the settings in // $wgWikiDebug_ViewableFiles. This is an array of files/directories. If the // entry is a directory, all of it's contents, including sub-directories, are // viewable. function pwfWikiDebug_CanViewFile($FileName) { global $wgWikiDebug_ViewableFiles; // If the supplied filename is in the list of viewable files, then return // true. if (in_array($FileName, $wgWikiDebug_ViewableFiles)) return true; // Otherwise, if there are any directories in the list of viewable files, // return true if this file is within one of those directories. foreach ($wgWikiDebug_ViewableFiles as $ViewableFile) { // Ensure the path ends in a directory separator. This doesn't cause // any problems if the file is not a directory, as we separately test // that the full path exists. if (substr($ViewableFile, -1) != "/") $ViewableFile .= "/"; // If the path of the $ViewableFile (including trailing backslash) matches // the start of the file we are checking permission for, then the file is // inside the viewable directory, so we return true. // TODO: We could be a bit smarter about matching files which are // directories (i.e. allowing them to be specified with or without // a trailing slash) but this isn't required at the moment. It needs // fixing at the point where we have any tags that can operate on // directories (e.g. <file_list> to provide a directory listing, or // <zip> tag to zip up the contents of a directory). // (Actually, as far as I can tell this is only a problem if the // ViewableFiles array contains entries with trailing slashes, as the // other way round will still result in a match - and this can be // fixed in wfWikiDebug if necessary. Some more thought required, // though). if (strpos($FileName, $ViewableFile) === 0) return true; } return false; } // pwfWikiDebug_TidyPath() // Converts Windows-like back-slash characters to Unix-like forward-slashes. // Forward slashes work on both systems, so are always safe to use, and this // conversion ensures that any file paths are treated in a system-agnostic way. function pwfWikiDebug_TidyPath($Path) { return str_replace("\\", "/", $Path); } function wfWikiDebug_PreloadMessage($Title, &$Message) { global $pWikiDebug_Messages; // As message names are page titles, MW normalises them to have an upper-case // first character. // We therefore need to force them back to lower-case in order to check against // our array. $Title = strtolower($Title); if (isset($pWikiDebug_Messages[$Title])) { $Message = $pWikiDebug_Messages[$Title]; } return true; } // WikiDebug_Msg() // Returns the text of the specified system message, using any additional arguments // for variable replacement. // Functionally equivalent to wfMsg(), which is the original way of handling this // in MediaWiki. However, this was deprecated in favour of wfMessage() and the new // Message class in MW 1.17 and removed altogether in MW 1.27. // This wrapper function calls wfMessage() if available, otherwise wfMsg(), and // in both cases returns the expanded string (not a Message object). As well as // ensuring compatibility across MediaWiki versions, using this function simplifies // our code as it doesn't need to explicitly call text() to get the output string. // Note that the only difference between this and wfMsg() is that it benefits from // some bug fixes that were applied to the wfMessage() implementation, namely that // variable substitution now happens before template expansion, so constructs such // as {{PLURAL:$1|egg|eggs}} now work as expected. function WikiDebug_Msg($Key) { $Args = func_get_args(); if (function_exists("wfMessage")) { $objMessage = call_user_func_array("wfMessage", $Args); return $objMessage->text(); } else { return call_user_func_array("wfMsg", $Args); } } // WikiDebug_DisableCache() // Prevents the results of the current parse from being cached. This makes most // sense in the context of parsing a full page, but may make sense in other parsing // contexts, too. // Primarily provided for backwards-compatibility reasons, but may still be useful // when older MW versions no longer need to be supported, for clarity and brevity. function WikiDebug_DisableCache($objParser) { // Prior to MW 1.28, you simply called disableCache() on the Parser instance in // order to disable the cache. In MW 1.28 this was deprecated and it was // ultimately removed in MW 1.35. // Note that we need a version check here as this can't be feature-detected. All // relevant functions have existed since at least MW 1.17 and we don't want to // call disableCache() after deprecation, as it will emit notices. // TODO: Although the deprecation was made in MW 1.28, it is possible that the // new technique worked correctly in older MW versions. It may therefore // be possible to turn this into a feature-detection check after all. // @back-compat MW < 1.28 if (version_compare(MW_VERSION, "<", "1.28")) { $objParser->disableCache(); } // In more recent MediaWiki versions, you need to first retrieve the ParserOutput // object from the Parser and then set the cache expiry to zero seconds. // TODO: Although the deprecation was made in MW 1.28, it is possible that the // new technique worked correctly in older MW versions. It may therefore // be possible to turn this into a feature-detection check after all. else { $objParserOutput = $objParser->getOutput(); $objParserOutput->updateCacheExpiry(0); } }
Example 2: Non-viewable file
Here is an attempt to view LocalSettings.php. Because this file is not in the array of allowed files, you are unable to see it's contents:
<show_source file="LocalSettings.php"></show_source>
Live sourcecode viewer: LocalSettings.php
File cannot be viewed. It either does not exist, or is protected against viewing.