feat: Mediawiki added, extensions added

This commit is contained in:
Sascha 2026-06-01 16:02:23 +02:00
parent f0d1fd3f58
commit c0fb067595
10902 changed files with 2773577 additions and 1 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
node_modules/ node_modules/
mediawiki/
composer.phar composer.phar
mediawiki/cache/ mediawiki/cache/
mediawiki/images/ mediawiki/images/

4
mediawiki/.dockerignore Normal file
View File

@ -0,0 +1,4 @@
# local build artifacts that must not be included
/composer.lock
/node_modules
/vendor

31
mediawiki/.editorconfig Normal file
View File

@ -0,0 +1,31 @@
root = true
[*]
indent_style = tab
indent_size = tab
tab_width = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
# Ensure text editors don't turn leading spaces into tabs,
# e.g. in multi-line bullet list items
[*.md]
indent_style = space
indent_size = 2
# Tabs may not be valid YAML
# @see https://yaml.org/spec/1.2/spec.html#id2777534
[*.{yml,yaml}]
indent_style = space
indent_size = 2
[.git/**]
indent_style = space
indent_size = 2
# Trailing whitespace is intended in parser test files
# T278066
[tests/parser/*.txt]
trim_trailing_whitespace = false

38
mediawiki/.eslintignore Normal file
View File

@ -0,0 +1,38 @@
# Upstream code
/resources/lib/
/resources/src/mediawiki.libs.jpegmeta/
# Skip functions
/resources/src/skip-*
# Build tooling
/docs/coverage/
/docs/html/
/docs/js/
/docs/latex/
/vendor/
/tests/coverage/
# Test data
/tests/phpunit/data/registration/duplicate_keys.json
/tests/phpunit/data/resourceloader/codex/
/tests/phpunit/data/resourceloader/codex-devmode/
/tests/phpunit/data/SelserContext/*.json
/tests/phpunit/unit/includes/Settings/Source/fixtures/bad.json
/tests/phpunit/**/*malformed*.json
/maintenance/benchmarks/data/
# Nested projects
/extensions/
/skins/
# Language files written automatically by TranslateWiki
/**/i18n/**/*.json
!/**/i18n/**/en.json
!/**/i18n/**/qqq.json
# Ignore directories where MW may write internal files (T291674)
# Ref $wgCacheDirectory (installer)
# Ref $wgUploadDirectory (run-time default)
/cache/
/images/

9
mediawiki/.eslintrc.json Normal file
View File

@ -0,0 +1,9 @@
{
"root": true,
"extends": [
"wikimedia/server"
],
"rules": {
"max-len": "warn"
}
}

58
mediawiki/.fresnel.yml Normal file
View File

@ -0,0 +1,58 @@
# This file declares MediaWiki scenarios for Fresnel, a web page performance
# monitoring tool. See <https://wikitech.wikimedia.org/wiki/Performance/Fresnel>.
warmup: true
runs: 7
scenarios:
Read a page:
# The only page that exists by default is the main page.
# But its actual name is configurable/unknown (T216791).
# Omit 'title' to let MediaWiki show the default (which is the main page),
# and a query string to prevent a normalization redirect.
url: "{MW_SERVER}{MW_SCRIPT_PATH}/index.php?noredirectplz"
viewport:
width: 1100
height: 700
reports:
- navtiming
- paint
- transfer
probes:
- screenshot
- trace
Load the editor:
url: "{MW_SERVER}{MW_SCRIPT_PATH}/index.php?action=edit"
viewport:
width: 1100
height: 700
reports:
- navtiming
- paint
- transfer
probes:
- screenshot
- trace
View history of a page:
url: "{MW_SERVER}{MW_SCRIPT_PATH}/index.php?action=history"
viewport:
width: 1100
height: 700
reports:
- navtiming
- paint
- transfer
probes:
- screenshot
- trace
View recent changes:
url: "{MW_SERVER}{MW_SCRIPT_PATH}/index.php?title=Special:RecentChanges"
viewport:
width: 1100
height: 700
reports:
- navtiming
- paint
- transfer
probes:
- screenshot
- trace

View File

@ -0,0 +1,3 @@
# mass code style changes - array syntax
0077c5da15aab081125ee1c72cc4d95225e4ff5f
110a5877e9e6ebe7a6ecd758f5812f32fc4ef57e

16
mediawiki/.gitattributes vendored Normal file
View File

@ -0,0 +1,16 @@
*.sh eol=lf
*.icc binary
*.webp binary
*.mp3 binary
*.map.json binary
*~ export-ignore
#*# export-ignore
.* export-ignore
*.htaccess -export-ignore
package.json export-ignore
package-lock.json export-ignore
Gruntfile.js export-ignore
Gemfile* export-ignore
vendor/pear/net_smtp/README.rst export-ignore
phpunit.xml.dist export-ignore
DEVELOPERS.md export-ignore

94
mediawiki/.gitignore vendored Normal file
View File

@ -0,0 +1,94 @@
# Repository management
.svn
# git-deploy status file:
/.deploy
# Editors
*.kate-swp
*~
\#*#
.#*
.*.swp
.project
cscope.files
cscope.out
*.orig
## NetBeans
nbproject*
project.index
## Sublime
sublime-*
sftp-config.json
## Visual Studio Code
*.vscode/
# MediaWiki install & usage
/cache
/logs
/docs/coverage
/docs/js
/docs/latex
/images/[0-9a-f]
/images/archive
/images/cache
/images/deleted
/images/lockdir
/images/temp
/images/thumb
## Extension:EasyTimeline
/images/timeline
## Extension:Score
/images/lilypond
## Extension:Phonos
/images/phonos-render
## Extension:TimedMediaHandler
/images/transcoded
/images/tmp
/maintenance/.mweval_history
/maintenance/.mwsql_history
/LocalSettings.php
/includes/PlatformSettings.php
# Building & testing
npm-debug.log
node_modules/
/resources/lib/.foreign
/tests/phpunit/phpunit.phar
.phpunit.result.cache
/tests/phpunit/.phpunit.result.cache
phpunit.xml
/tests/selenium/log
.eslintcache
.api-testing.config.json
.phan/local-config.php
# Composer
/composer.lock
/composer.local.json
/composer.phar
# Operating systems
## Mac OS X
.DS_Store
## Windows
Thumbs.db
# Misc
.buildpath
.classpath
.idea
*.iml
.metadata*
.settings
/favicon.ico
/static*
/tags
/.htaccess
/.htpasswd
*.rej
# Docker
docker-compose.override.yml
.env
/.stylelintcache

33
mediawiki/.gitmessage Normal file
View File

@ -0,0 +1,33 @@
component: Some awesome subject
Why:
- …
What:
- …
Bug: TXXXXX
#
# When in doubt, consult https://www.mediawiki.org/wiki/Gerrit/Commit_message_guidelines
#
# - Subject should be 50 characters or less
# - Separate body from subject
# - Use imperative mood, avoid passive voice
# - Wrap the body text at 72 chars
#
# Explain "why" and "what". If it does not make sense to include
# this in your commit message, then delete those sections.
#
# Don't forget the Bug: line
# Don't leave a blank line after the Bug: line
#
# If you are unsure about what to use for component:
# - "HookContainer" could work for changes to include/HookContainer
# - "phpunit" for changes to PHPUnit tests
# - "Authentication" for changes affecting multiple sections
# relating to user authentication
#
# Hint: you can enable this to be used as a template for git commit
# git config commit.template .gitmessage

152
mediawiki/.gitmodules vendored Normal file
View File

@ -0,0 +1,152 @@
[submodule "extensions/AbuseFilter"]
path = extensions/AbuseFilter
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/AbuseFilter
branch = REL1_43
[submodule "extensions/CategoryTree"]
path = extensions/CategoryTree
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/CategoryTree
branch = REL1_43
[submodule "extensions/Cite"]
path = extensions/Cite
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Cite
branch = REL1_43
[submodule "extensions/CiteThisPage"]
path = extensions/CiteThisPage
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/CiteThisPage
branch = REL1_43
[submodule "extensions/CodeEditor"]
path = extensions/CodeEditor
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/CodeEditor
branch = REL1_43
[submodule "extensions/ConfirmEdit"]
path = extensions/ConfirmEdit
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/ConfirmEdit
branch = REL1_43
[submodule "extensions/DiscussionTools"]
path = extensions/DiscussionTools
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/DiscussionTools
branch = REL1_43
[submodule "extensions/Echo"]
path = extensions/Echo
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Echo
branch = REL1_43
[submodule "extensions/Gadgets"]
path = extensions/Gadgets
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Gadgets
branch = REL1_43
[submodule "extensions/ImageMap"]
path = extensions/ImageMap
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/ImageMap
branch = REL1_43
[submodule "extensions/InputBox"]
path = extensions/InputBox
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/InputBox
branch = REL1_43
[submodule "extensions/Interwiki"]
path = extensions/Interwiki
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Interwiki
branch = REL1_43
[submodule "extensions/Linter"]
path = extensions/Linter
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Linter
branch = REL1_43
[submodule "extensions/LoginNotify"]
path = extensions/LoginNotify
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/LoginNotify
branch = REL1_43
[submodule "extensions/Math"]
path = extensions/Math
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Math
branch = REL1_43
[submodule "extensions/MultimediaViewer"]
path = extensions/MultimediaViewer
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/MultimediaViewer
branch = REL1_43
[submodule "extensions/Nuke"]
path = extensions/Nuke
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Nuke
branch = REL1_43
[submodule "extensions/OATHAuth"]
path = extensions/OATHAuth
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/OATHAuth
branch = REL1_43
[submodule "extensions/PageImages"]
path = extensions/PageImages
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/PageImages
branch = REL1_43
[submodule "extensions/ParserFunctions"]
path = extensions/ParserFunctions
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/ParserFunctions
branch = REL1_43
[submodule "extensions/PdfHandler"]
path = extensions/PdfHandler
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/PdfHandler
branch = REL1_43
[submodule "extensions/Poem"]
path = extensions/Poem
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Poem
branch = REL1_43
[submodule "extensions/ReplaceText"]
path = extensions/ReplaceText
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/ReplaceText
branch = REL1_43
[submodule "extensions/Scribunto"]
path = extensions/Scribunto
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Scribunto
branch = REL1_43
[submodule "extensions/SecureLinkFixer"]
path = extensions/SecureLinkFixer
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/SecureLinkFixer
branch = REL1_43
[submodule "extensions/SpamBlacklist"]
path = extensions/SpamBlacklist
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/SpamBlacklist
branch = REL1_43
[submodule "extensions/SyntaxHighlight_GeSHi"]
path = extensions/SyntaxHighlight_GeSHi
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/SyntaxHighlight_GeSHi
branch = REL1_43
[submodule "extensions/TemplateData"]
path = extensions/TemplateData
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/TemplateData
branch = REL1_43
[submodule "extensions/TextExtracts"]
path = extensions/TextExtracts
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/TextExtracts
branch = REL1_43
[submodule "extensions/Thanks"]
path = extensions/Thanks
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/Thanks
branch = REL1_43
[submodule "extensions/TitleBlacklist"]
path = extensions/TitleBlacklist
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/TitleBlacklist
branch = REL1_43
[submodule "extensions/VisualEditor"]
path = extensions/VisualEditor
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/VisualEditor
branch = REL1_43
[submodule "extensions/WikiEditor"]
path = extensions/WikiEditor
url = https://gerrit.wikimedia.org/r/mediawiki/extensions/WikiEditor
branch = REL1_43
[submodule "skins/MinervaNeue"]
path = skins/MinervaNeue
url = https://gerrit.wikimedia.org/r/mediawiki/skins/MinervaNeue
branch = REL1_43
[submodule "skins/MonoBook"]
path = skins/MonoBook
url = https://gerrit.wikimedia.org/r/mediawiki/skins/MonoBook
branch = REL1_43
[submodule "skins/Timeless"]
path = skins/Timeless
url = https://gerrit.wikimedia.org/r/mediawiki/skins/Timeless
branch = REL1_43
[submodule "skins/Vector"]
path = skins/Vector
url = https://gerrit.wikimedia.org/r/mediawiki/skins/Vector
branch = REL1_43
[submodule "vendor"]
path = vendor
url = https://gerrit.wikimedia.org/r/mediawiki/vendor
branch = REL1_43

6
mediawiki/.gitreview Normal file
View File

@ -0,0 +1,6 @@
[gerrit]
host=gerrit.wikimedia.org
port=29418
project=mediawiki/core.git
track=1
defaultrebase=0

588
mediawiki/.mailmap Normal file
View File

@ -0,0 +1,588 @@
# Map author and committer names and email addresses to canonical real names
# and email addresses.
#
# To update the CREDITS file, run maintenance/updateCredits.php
#
# Two types of entries are useful here. The first sets a canonical author
# name for a given email address:
#
# Canonical Author Name <author email>
#
# The second allows collecting alternate email addresses into a single
# canonical author name and email address:
#
# Canonical Author Name <author email> <alternate email>
#
# Mappings are only needed for authors who have used multiple author names
# and/or author emails for revisions over time. Author names beginning with
# "[BOT]" will be omitted from the CREDITS file.
#
# See also: https://git-scm.com/docs/git-shortlog#_mapping_authors
#
[BOT] Gerrit Code Review <gerrit@wikimedia.org>
[BOT] Gerrit Patch Uploader <gerritpatchuploader@gmail.com>
[BOT] jenkins-bot <jenkins-bot@gerrit.wikimedia.org>
[BOT] jenkins-bot <jenkins-bot@gerrit.wikimedia.org> <jenkins-bot@wikimedia.org>
[BOT] Translation updater bot <l10n-bot@translatewiki.net>
[BOT] libraryupgrader <tools.libraryupgrader@tools.wmflabs.org>
Aaron Schulz <aschulz@wikimedia.org>
Aaron Schulz <aschulz@wikimedia.org> <aaron@users.mediawiki.org>
Abijeet Patro <apatro@wikimedia.org>
Abijeet Patro <apatro@wikimedia.org> <abijeetpatro@gmail.com>
Adam Roses Wight <adam.wight@wikimedia.de>
Adam Roses Wight <adam.wight@wikimedia.de> <awight@wikimedia.org>
Adam Roses Wight <adam.wight@wikimedia.de> <spam@ludd.net>
addshore <addshorewiki@gmail.com>
addshore <addshorewiki@gmail.com> <adamshorland@gmail.com>
Aditya Sastry <ganeshaditya1@gmail.com>
Adrian Heine <adrian.heine@wikimedia.de>
Alangi Derick <alangiderick@gmail.com>
Alangi Derick <alangiderick@gmail.com> <alangiderick@gmail.com>
Alangi Derick <alangiderick@gmail.com> <xsavitar.wiki@aol.com>
Alex Paskulin <apaskulin@wikimedia.org>
Alex Z. <mrzmanwiki@gmail.com> <mrzman@users.mediawiki.org>
Aleksey Bekh-Ivanov <aleksey.bekh-ivanov@wikimedia.de>
Alexandre Emsenhuber <ialex.wiki@gmail.com>
Alexandre Emsenhuber <ialex.wiki@gmail.com> <ialex@users.mediawiki.org>
Alexandre Emsenhuber <ialex.wiki@gmail.com> <mediawiki@emsenhuber.ch>
Alexander Mashin <alex.mashin@gmail.com>
Alexander Monk <krenair@gmail.com>
Alexander Monk <krenair@gmail.com> <alex@wikimedia.org>
Alexander Monk <krenair@gmail.com> <krenair@wikimedia.org>
Alexander Vorwerk <zabe@avorwerk.net>
Alexander Vorwerk <zabe@avorwerk.net> <alec@vc-celle.de>
Alexander Vorwerk <zabe@avorwerk.net> <alexander.vorwerk@stud.uni-goettingen.de>
Alexia E. Smith <washuu@gmail.com>
alistair3149 <alistair31494322@gmail.com>
AlQaholic007 <soham.parekh1998@gmail.com>
Amir E. Aharoni <amir.aharoni@mail.huji.ac.il>
Amir E. Aharoni <amir.aharoni@mail.huji.ac.il> <amire80@users.mediawiki.org>
Amir Sarabadani <ladsgroup@gmail.com> <Ladsgroup@gmail.com>
Anders Wegge Jakobsen <awegge@gmail.com> <wegge@users.mediawiki.org>
Andre Engels <andreengels@gmail.com> <a_engels@users.mediawiki.org>
Andrew Garrett <agarrett@wikimedia.org>
Andrew Garrett <agarrett@wikimedia.org> <werdna@users.mediawiki.org>
Andrew Otto <aotto@wikimedia.org>
Andrew Otto <aotto@wikimedia.org> <acotto@gmail.com>
Angela Beesley Starling <wiki@nge.la> <angelab@users.mediawiki.org>
Antoine Musso <hashar@free.fr>
Antoine Musso <hashar@free.fr> <hashar@users.mediawiki.org>
Aran Dunkley <aran@organicdesign.co.nz> <nad@users.mediawiki.org>
Ariel Glenn <ariel@wikimedia.org> <ariel@users.mediawiki.org>
Ariel Glenn <ariel@wikimedia.org> <ariel@wikimedia.org>
Arlo Breault <abreault@wikimedia.org>
ArtBaltai <art.baltai@speedandfunction.com>
Arthur Richards <arichards@wikimedia.org>
Arthur Richards <arichards@wikimedia.org> <awjrichards@users.mediawiki.org>
Aryeh Gregor <ayg@aryeh.name>
Aryeh Gregor <ayg@aryeh.name> <simetrical@users.mediawiki.org>
Asher Feldman <afeldman@wikimedia.org>
Asher Feldman <afeldman@wikimedia.org> <asher@users.mediawiki.org>
aude <aude.wiki@gmail.com>
Audrey Tang <audreyt@audreyt.org>
Audrey Tang <audreyt@audreyt.org> <au@localhost>
ayush_garg <ayush.ce13@iitp.ac.in>
Bae Junehyeon <devunt@gmail.com>
Bahodir Mansurov <bmansurov@wikimedia.org>
Bartosz Dziewoński <dziewonski@fastmail.fm>
Bartosz Dziewoński <dziewonski@fastmail.fm> <matma.rex@gmail.com>
Bartosz Dziewoński <dziewonski@fastmail.fm> <bdziewonski@wikimedia.org>
Bartosz Dziewoński <dziewonski@fastmail.fm> <matmarex@wikimedia.org>
Ben Hartshorne <bhartshorne@wikimedia.org> <ben@users.mediawiki.org>
Bene <benestar.wikimedia@gmail.com>
Bene <benestar.wikimedia@gmail.com> <benestar.wikimedia@googlemail.com>
Benny Situ <bsitu@wikimedia.org>
Benny Situ <bsitu@wikimedia.org> <bsitu@users.mediawiki.org>
Bernard Wang <bwang@wikimedia.org>
Bertrand Grondin <bertrand.grondin@tiscali.fr> <grondin@users.mediawiki.org>
Bill Pirkle <bpirkle@wikimedia.org>
Bill Pirkle <bpirkle@wikimedia.org> <bpirkle@wikipedia.org>
Brad Jorsch <bjorsch@wikimedia.org>
Brad Jorsch <bjorsch@wikimedia.org> <anomie.wikipedia@gmail.com>
Brandon Harris <bharris@wikimedia.org> <bharris@users.mediawiki.org>
BrandonXLF <brandfowl@gmail.com>
Brent Garber <overlordq@gmail.com>
Brent Garber <overlordq@gmail.com> <wikipedia@thedarkcitadel.com>
Brent Garber <overlordq@gmail.com> <overlordq@users.mediawiki.org>
Brian Wolff <bawolff+wn@gmail.com>
Brian Wolff <bawolff+wn@gmail.com> <bawolff+svn@gmail.com>
Brian Wolff <bawolff+wn@gmail.com> <bawolff@users.mediawiki.org>
Brooke Vibber <bvibber@wikimedia.org>
Brooke Vibber <bvibber@wikimedia.org> <brion@wikimedia.org>
Brooke Vibber <bvibber@wikimedia.org> <brion@pobox.com>
Brooke Vibber <bvibber@wikimedia.org> <brion@users.mediawiki.org>
Bryan Davis <bd808@wikimedia.org>
Bryan Davis <bd808@wikimedia.org> <bd808@bd808.com>
Bryan Tong Minh <bryan.tongminh@gmail.com>
Bryan Tong Minh <bryan.tongminh@gmail.com> <btongminh@users.mediawiki.org>
C. Scott Ananian <cscott@cscott.net>
C. Scott Ananian <cscott@cscott.net> <cananian@wikimedia.org>
Cacycle <cacyclewp@gmail.com>
cenarium <cenarium.sysop@gmail.com>
Chad Horohoe <chadh@wikimedia.org>
Chad Horohoe <chadh@wikimedia.org> <demon@users.mediawiki.org>
Charles Melbye <charlie@yourwiki.net> <charlie@users.mediawiki.org>
Chiefwei <chiefwei1989@gmail.com>
Chris Koerner <ckoerner@wikimedia.org>
Chris McMahon <cmcmahon@wikimedia.org>
Chris Steipp <csteipp@wikimedia.org>
Christian Aistleitner <christian@quelltextlich.at>
Christian Aistleitner <christian@quelltextlich.at> <qchris@users.mediawiki.org>
Christian Williams <orbit@framezero.com>
Christian Williams <orbit@framezero.com> <christian@localhost>
Christian Williams <orbit@framezero.com> <christian@wikia-inc.com>
Christoph Jauera <christoph.jauera@wikimedia.de>
Christoph Jauera <christoph.jauera@wikimedia.de> <christoph.fischer@wikimedia.de>
Christopher Johnson <root@bugzilla.wmde.de>
church of emacs <churchofemacs@users.mediawiki.org>
Clara Andrew-Wani <candrewwani@gmail.com>
Clara Andrew-Wani <candrewwani@gmail.com> <candrewwani@gmail.com>
Cindy Cicalese <ccicalese@wikimedia.org>
Cindy Cicalese <ccicalese@wikimedia.org> <cicalese@mitre.org>
Cindy Cicalese <ccicalese@wikimedia.org> <cindom@gmail.com>
ckoerner <nobelx@gmail.com>
Conrad Irwin <conrad.irwin+wiki@gmail.com> <conrad@users.mediawiki.org>
Cyndy Simiyu <csimiyu@wikimedia.org>
Dan Duvall <dduvall@wikimedia.org>
dan-nl <d_entous@yahoo.com>
Daniel A. R. Werner <daniel.a.r.werner@gmail.com>
Daniel Cannon <cannon.danielc@gmail.com> <amidaniel@users.mediawiki.org>
Daniel Friesen <mediawiki@danielfriesen.name>
Daniel Friesen <mediawiki@danielfriesen.name> <daniel@nadir-seen-fire.com>
Daniel Friesen <mediawiki@danielfriesen.name> <dantman@users.mediawiki.org>
Daniel Friesen <mediawiki@danielfriesen.name> <pub-github@nadir-seen-fire.com>
Daniel Kinzler <dkinzler@wikimedia.org>
Daniel Kinzler <dkinzler@wikimedia.org> <daniel.kinzler@wikimedia.de>
Daniel Kinzler <dkinzler@wikimedia.org> <daniel@users.mediawiki.org>
Daniel Renfro <bluecurio@gmail.com> <drenfro@vistaprint.com>
Danny B. <Wikipedia.Danny.B@email.cz>
Danny B. <Wikipedia.Danny.B@email.cz> <danny.b@email.cz>
Danny B. <Wikipedia.Danny.B@email.cz> <danny_b@users.mediawiki.org>
Danny B. <Wikipedia.Danny.B@email.cz> <wikimedia.danny.b@email.cz>
Darian Anthony Patrick <dpatrick@wikimedia.org>
Darkdragon09 <ubuntu@ip-172-31-39-38.us-west-2.compute.internal>
DatGuy <datguysteam@gmail.com>
David Barratt <dbarratt@wikimedia.org>
David Causse <dcausse@wikimedia.org>
David Chan <david@sheetmusic.org.uk>
Dayllan Maza <dmaza@wikimedia.org>
Dayllan Maza <dmaza@wikimedia.org> <dayllan.maza@gmail.com>
Denny Vrandečić <dvrandecic@wikimedia.org>
Dereckson <dereckson@espace-win.org>
Derk-Jan Hartman <hartman.wiki@gmail.com>
Derk-Jan Hartman <hartman.wiki@gmail.com> <hartman@videolan.org>
Derk-Jan Hartman <hartman.wiki@gmail.com> <hartman@users.mediawiki.org>
Devi Krishnan <devikrishnan67@gmail.com>
Diederik van Liere <dvanliere@gmail.com> <diederik@users.mediawiki.org>
diesel kapasule <diesel.kapasule@speedandfunction.com>
Domas Mituzas <domas.mituzas@gmail.com> <midom@users.mediawiki.org>
Douglas Gardner <douglas@chippy.ch>
Doğu Abaris <ydogu@wikimedia.org.tr>
DPStokesNZ <duncan.stokes@gmail.com>
Dreamy Jazz <dreamyjazzwikipedia@gmail.com>
Dreamy Jazz <dreamyjazzwikipedia@gmail.com> <wbrown-ctr@wikimedia.org>
Dreamy Jazz <dreamyjazzwikipedia@gmail.com> <wbrown@wikimedia.org>
Dreamy Jazz <dreamyjazzwikipedia@gmail.com> <wpgbrown@wikimedia.org>
Dylsss <dylssswp@gmail.com>
Ebrahim Byagowi <ebrahim@gnu.org>
Ed Sanders <esanders@wikimedia.org>
Elliott Eggleston <eeggleston@wikimedia.org>
Elliott Eggleston <eeggleston@wikimedia.org> <ejegg@ejegg.com>
Emad Elwany <emadelwany@hotmail.com>
Emmanuel Engelhart <kelson@kiwix.org> <kelson42@users.mediawiki.org>
Emufarmers <emufarmers@gmail.com>
Emufarmers <emufarmers@gmail.com> <emufarmers@users.mediawiki.org>
Entlinkt <entlinkt@gmx-topmail.de>
Eranroz <eranroz89@gmail.com>
Erik Bernhardson <ebernhardson@wikimedia.org>
Erik Moeller <erik@wikimedia.org>
Erik Moeller <erik@wikimedia.org> <erik@users.mediawiki.org>
Erwin Dokter <erwin@darcoury.nl>
Evan McIntire <mcintire.evan@gmail.com>
Evan Prodromou <evanprodromou@users.mediawiki.org> <evan@users.mediawiki.org>
Federico Leva <federicoleva@tiscali.it>
Ferran Tufan <southparkfan223@hotmail.com>
Fenzik Joseph <fenzik@gmail.com> <fenzik@users.mediawiki.org>
Florian Schmidt <florian.schmidt.welzow@t-online.de>
Florian Schmidt <florian.schmidt.welzow@t-online.de> <florian.schmidt.stargatewissen@gmail.com>
fomafix <fomafix@googlemail.com>
Fran Rogers <fran@dumetella.net>
Fran Rogers <fran@dumetella.net> <krimpet@users.mediawiki.org>
Frances Goodwin <fgoodwin@wikimedia.org>
freakolowsky <freak@drajv.si>
Func <Funcer@outlook.com>
FunPika <funpikawiki@gmail.com>
Gabriel Wicke <gwicke@wikimedia.org>
Gabriel Wicke <gwicke@wikimedia.org> <gwicke@users.mediawiki.org>
Gabriel Wicke <gwicke@wikimedia.org> <wicke@wikidev.net>
gengh <ggalarzaheredero@wikimedia.org>
Geoffrey Mon <geofbot@gmail.com>
Geoffrey Trang <geoffreytrang@gmail.com>
Gergő Tisza <gtisza@wikimedia.org>
Gergő Tisza <gtisza@wikimedia.org> <tgr.huwiki@gmail.com>
Giftpflanze <gifti@tools.wmflabs.org>
Gilles Dubuc <gdubuc@wikimedia.org> <gilles@wikimedia.org>
Gilles Dubuc <gdubuc@wikimedia.org>
gladoscc <admin@glados.cc>
glaisher <glaisher.wiki@gmail.com>
Greg Sabino Mullane <greg@turnstep.com>
Greg Sabino Mullane <greg@turnstep.com> <greg@endpoint.com>
Greg Sabino Mullane <greg@turnstep.com> <greg@users.mediawiki.org>
Grunny <mwgrunny@gmail.com>
Guy Van den Broeck <guyvdb@gmail.com> <guyvdb@users.mediawiki.org>
HakanIST <ozdemir@gmail.com>
Happy-melon <happy-melon@live.com> <happy-melon@users.mediawiki.org>
Helder <he7d3r@gmail.com>
Helder <he7d3r@gmail.com> <helder.wiki@gmail.com>
Hoo man <hoo@online.de>
Huji <huji.huji@gmail.com>
Huji <huji.huji@gmail.com> <huji@users.mediawiki.org>
Ian Baker <ibaker@wikimedia.org> <raindrift@users.mediawiki.org>
Ilmari Karonen <nospam@vyznev.net> <vyznev@users.mediawiki.org>
Inez Korczyński <inez@wikia-inc.com>
Inez Korczyński <inez@wikia-inc.com> <inez@users.mediawiki.org>
isarra <s@zaori.org>
isarra <s@zaori.org> <zhorishna@gmail.com>
Ivan Lanin <ivanlanin@gmail.com> <ivanlanin@users.mediawiki.org>
Jack Phoenix <jack@countervandalism.net>
Jack Phoenix <jack@countervandalism.net> <ashley@users.mediawiki.org>
Jackmcbarn <jackmcbarn@gmail.com>
Jackmcbarn <jackmcbarn@gmail.com> <jackmcbarn@users.noreply.github.com>
jagori <jagori79@gmail.com>
James D. Forrester <jforrester@wikimedia.org>
Jan Drewniak <jdrewniak@wikimedia.org>
Jan Drewniak <jdrewniak@wikimedia.org> <jan.drewniak@gmail.com>
Jaime Crespo <jcrespo@wikimedia.org>
Jan Gerber <j@thing.net> <j@users.mediawiki.org>
Jan Luca Naumann <jan@jans-seite.de>
Jan Luca Naumann <jan@jans-seite.de> <jan@users.mediawiki.org>
Jan Paul Posma <jp.posma@gmail.com> <janpaul123@users.mediawiki.org>
Jan Zerebecki <jan.wikimedia@zerebecki.de>
Jared Flores <jaredflores2000@gmail.com>
Jaroslav Škarvada <jskarvad@redhat.com>
jarrettmunton <jmuntjmunt@gmail.com>
Jason Richey <jasonr@wikia.com>
Jason Richey <jasonr@wikia.com> <jasonr@users.mediawiki.org>
Jason Richey <jasonr@wikia.com> <urichj00@users.mediawiki.org>
Jeff Hall <jeffreyehall@gmail.com>
Jeff Hall <jeffreyehall@gmail.com> <jhall@wikimedia.org>
Jeff Hobson <jhobson@wikimedia.org>
Jeff Janes <jeff.janes@gmail.com>
Jeremy Baron <jeremy@tuxmachine.com>
Jeremy Postlethwaite <jpostlethwaite@wikimedia.org> <jpostlethwaite@users.mediawiki.org>
Jeroen De Dauw <jeroendedauw@gmail.com>
Jeroen De Dauw <jeroendedauw@gmail.com> <jeroendedauw@users.mediawiki.org>
Jesús Martínez Novo <martineznovo@gmail.com>
Jiabao <jiabao.foss@gmail.com>
Jimmy Collins <jimmy.collins@web.de> <collinj@users.mediawiki.org>
Joel Sahleen <jsahleen@wikimedia.org>
John Du Hart <john@compwhizii.net> <johnduhart@users.mediawiki.org>
John Erling Blad <john.blad@wikimedia.de>
Jon Harald Søby <jhsoby@gmail.com> <jhsoby@users.mediawiki.org>
Jon Harald Søby <jhsoby@gmail.com>
Jon Robson <jrobson@wikimedia.org>
Jon Robson <jrobson@wikimedia.org> <jdlrobson@gmail.com>
Jsn.sherman <jsherman@wikimedia.org>
Juliusz Gonera <jgonera@gmail.com>
Juliusz Gonera <jgonera@gmail.com> <jgonera@wikimedia.org>
Jure Kajzer <freak@drajv.si>
Jure Kajzer <freak@drajv.si> <freakolowsky@users.mediawiki.org>
Justin Du <justin.d128@gmail.com>
Kai Nissen <kai.nissen@wikimedia.de>
Karun Dambiec <karun.84@gmx.de>
Katie Filbert <aude.wiki@gmail.com>
Katie Filbert <aude.wiki@gmail.com> <aude@users.mediawiki.org>
Kevin Israel <pleasestand@live.com>
Klein Muçi <kleinmuci@gmail.com>
Kosta Harlan <kharlan@wikimedia.org>
Kosta Harlan <kharlan@wikimedia.org> <kosta@fastmail.com>
Kunal Grover <kunalgrover05@gmail.com>
Kunal Mehta <legoktm@debian.org>
Kunal Mehta <legoktm@debian.org> <legoktm@member.fsf.org>
Kunal Mehta <legoktm@debian.org> <legoktm.wikipedia@gmail.com>
Kunal Mehta <legoktm@debian.org> <legoktm@gmail.com>
Kwan Ting Chan <ktc@ktchan.info> <ktchan@users.mediawiki.org>
lekshmi <andnlnbn18@gmail.com>
Lectrician1 <jayandseth@gmail.com>
Leo Koppelkamm <diebuche@gmail.com> <diebuche@users.mediawiki.org>
Leon Liesener <leon.liesener@wikipedia.de>
Leon Weber <leon@vserver152.masterssystems.com> <leon@users.mediawiki.org>
Leonardo Gregianin <leogregianin@googlemail.com> <leogregianin@users.mediawiki.org>
Leons Petrazickis <leons.petrazickis.haveyouconsiderednotincludingthisphrase@gmail.com> <leonsp@users.mediawiki.org>
liangent <liangent@gmail.com>
Lisa Ridley <lhridley@gmail.com> <lhridley@users.mediawiki.org>
Ljudusika <plo2000@i.ua>
Lucas Werkmeister <lucas.werkmeister@wikimedia.de>
Luis Felipe Schenone <schenonef@gmail.com>
Luke Welling <lwelling@wikimedia.org>
Lupo <lupo.bugzilla@gmail.com>
lwatson <lwatson@wikimedia.org>
m4tx <m4tx@m4tx.pl>
Madman <madman.enwiki@gmail.com>
Magnus Manske <magnusmanske@googlemail.com> <magnusmanske@users.mediawiki.org>
Manuel Schneider <manuel.schneider@wikimedia.ch> <80686@users.mediawiki.org>
Marc-André Pelletier <marc@uberbox.org>
Marcin Cieślak <saper@saper.info>
Marcin Cieślak <saper@saper.info> <saper@users.mediawiki.org>
Marco Falke <maic23@live.de>
MarcoAurelio <strigiwm@gmail.com>
MarcoAurelio <strigiwm@gmail.com> <maurelio@tools.wmflabs.org>
Marielle Volz <marielle.volz@gmail.com>
Marius Hoch <hoo@online.de>
Mark Clements <mediawiki@kennel17.co.uk> <happydog@users.mediawiki.org>
Mark Hershberger <mah@everybody.org>
Mark Hershberger <mah@everybody.org> <mah@nichework.com>
Mark Hershberger <mah@everybody.org> <mah@users.mediawiki.org>
Mark Hershberger <mah@everybody.org> <mhershberger@wikimedia.org>
Mark Holmquist <mtraceur@member.fsf.org>
Mark Holmquist <mtraceur@member.fsf.org> <mholmquist@wikimedia.org>
Marko Obrovac <mobrovac@wikimedia.org>
Markus Glaser <glaser@hallowelt.biz>
Markus Glaser <glaser@hallowelt.biz> <mglaser@users.mediawiki.org>
Martin Urbanec <martin.urbanec@wikimedia.cz>
Martin Urbanec <martin.urbanec@wikimedia.cz> <murbanec@wikimedia.org>
Matt Johnston <mattj@emazestudios.com> <mattj@users.mediawiki.org>
Matthew Britton <hugglegurch@gmail.com> <gurch@users.mediawiki.org>
Matthew Bowker <matthewrbowker.bugs@gmail.com>
Matthew Flaschen <mflaschen@wikimedia.org>
Matthew Walker <mwalker@wikimedia.org>
MatthiasDD <Matthias_K2@gmx.de>
Matthias Mullie <git@mullie.eu>
Matthias Mullie <git@mullie.eu> <mmullie@wikimedia.org>
Matěj Grabovský <mgrabovsky@yahoo.com> <mgrabovsky@users.mediawiki.org>
Matěj Suchánek <matejsuchanek97@gmail.com>
Max Semenik <maxsem.wiki@gmail.com>
Max Semenik <maxsem.wiki@gmail.com> <maxsem@users.mediawiki.org>
Max Semenik <maxsem.wiki@gmail.com> <semenik@gmail.com>
mbsantos <msantos@wikimedia.org>
mgooley <g0013y@gmail.com>
Michael Dale <mdale@wikimedia.org> <dale@users.mediawiki.org>
Mikko Miettinen <miki@jedipedia.fi>
Mikko Miettinen <miki@jedipedia.fi> <97.mikko.miettinen@gmail.com>
mjbmr <mjbmri@gmail.com>
Mohamed Magdy <mohamedmk@gmail.com> <alnokta@users.mediawiki.org>
Moriel Schottlender <mschottlender@wikimedia.org>
Moriel Schottlender <mschottlender@wikimedia.org> <moriel@gmail.com>
Mormegil <mormegil@centrum.cz>
MrBlueSky <mrbluesky@wikipedia.be>
MrBlueSky <mrbluesky@wikipedia.be> <mrbluesky@localhost>
Mukunda Modell <mmodell@wikimedia.org>
MZMcBride <g@mzmcbride.com>
Máté Szabó <mszabo@fandom.com>
Máté Szabó <mszabo@fandom.com> <mszabo@wikia-inc.com>
Máté Szabó <mszabo@fandom.com> <tk.999.wikia@gmail.com>
nadeesha <nadeesha@calcey.com> <nadeesha@users.mediawiki.org>
Namit <namit.ohri@gmail.com>
Nathan Larson <nathanlarson3141@gmail.com>
Nathan Larson <nathanlarson3141@gmail.com> <tjlsangria@gmail.com>
Nathaniel Herman <redwwjd@yahoo.com> <pinky@users.mediawiki.org>
Neil Kandalgaonkar <neilk@wikimedia.org> <neilk@users.mediawiki.org>
Nemo bis <federicoleva@tiscali.it>
nephele <nephele@skyhighway.com> <nephele@users.mediawiki.org>
Nick Jenkins <nickpj@gmail.com> <nickj@users.mediawiki.org>
Niharika Kohli <nkohli@wikimedia.org>
Nik Everett <neverett@wikimedia.org>
Niklas Laxström <niklas.laxstrom@gmail.com>
Niklas Laxström <niklas.laxstrom@gmail.com> <nikerabbit@users.mediawiki.org>
Nimish Gautam <nimishg@wikimedia.org> <nimishg@users.mediawiki.org>
Novem Linguae <novemlinguae@gmail.com>
Nuria Ruiz <nuria@wikimedia.org>
Ori Livneh <ori@wikimedia.org>
Ori Livneh <ori@wikimedia.org> <ori.livneh@gmail.com>
Oskar Jauch <oskar.jauch@gmail.com>
Owen Davis <owen@wikia-inc.com>
Owen Davis <owen@wikia-inc.com> <owen@users.mediawiki.org>
paladox <thomasmulhall410@yahoo.com>
Patricio Molina <patriciomolina@gmail.com>
Patrick Reilly <preilly@wikimedia.org>
Patrick Reilly <preilly@wikimedia.org> <preilly@users.mediawiki.org>
Patrick Westerhoff <PatrickWesterhoff@gmail.com>
Paul Copperman <paul.copperman@gmail.com> <pcopp@users.mediawiki.org>
Petar Petković <ppetkovic@wikimedia.org>
Peter Coombe <pcoombe@wikimedia.org>
Peter Coti <petercoti@gmail.com>
Peter Potrowl <peter017@gmail.com> <peter17@users.mediawiki.org>
Petr Kadlec <mormegil@centrum.cz>
Philip Tzou <philip.npc@gmail.com> <philip@users.mediawiki.org>
physikerwelt (Moritz Schubotz) <wiki@physikerwelt.de>
Piotr Miazga <piotr@polishdeveloper.pl>
PiRSquared17 <pirsquared@tools.wmflabs.org>
Platonides <platonides@gmail.com> <platonides@users.mediawiki.org>
pppery <mapreader@olum.org>
PranavK <pranavmk98@gmail.com>
Prateek Saxena <psaxena@wikimedia.org>
Prateek Saxena <psaxena@wikimedia.org> <prtksxna@gmail.com>
Priyanka Dhanda <pdhanda@wikimedia.org> <pdhanda@users.mediawiki.org>
Purodha Blissenbach <purodha@blissenbach.org>
Purodha Blissenbach <purodha@blissenbach.org> <publi@web.de>
Purodha Blissenbach <purodha@blissenbach.org> <purodha@users.mediawiki.org>
Pwirth <wirth@hallowelt.biz>
Raimond Spekking <raimond.spekking@gmail.com>
Raimond Spekking <raimond.spekking@gmail.com> <raymond@users.mediawiki.org>
Remember the dot <rememberthedot@gmail.com> <rememberthedot@users.mediawiki.org>
Reza <reza.energy@gmail.com>
Ricordisamoa <ricordisamoa@openmailbox.org>
rillke <rillke@wikipedia.de>
rillke <rillke@wikipedia.de> <rainerrillke@hotmail.com>
River Tarnell <river@wikimedia.org> <kateturner@users.mediawiki.org>
River Tarnell <river@wikimedia.org> <river@users.mediawiki.org>
Roan Kattouw <roan.kattouw@gmail.com>
Roan Kattouw <roan.kattouw@gmail.com> <catrope@users.mediawiki.org>
Roan Kattouw <roan.kattouw@gmail.com> <roan@wikimedia.org>
Rob Church <robchur@gmail.com> <robchurch@users.mediawiki.org>
Rob Lanphier <robla@robla.net>
Rob Lanphier <robla@robla.net> <robla@users.mediawiki.org>
Rob Lanphier <robla@robla.net> <robla@wikimedia.org>
Rob Moen <rmoen@mediawiki.org>
Rob Moen <rmoen@mediawiki.org> <rmoen@users.mediawiki.org>
Rob Moen <rmoen@mediawiki.org> <rmoen@wikimedia.org>
Robert Hoenig <indielives010@gmail.com>
Robert Leverington <robert@rhl.me.uk> <roberthl@users.mediawiki.org>
Robert Rohde <rarohde@gmail.com> <rarohde@users.mediawiki.org>
Robert Stojnić <rainmansr@gmail.com> <rainman@users.mediawiki.org>
Robert Vogel <vogel@hallowelt.biz>
Robin Pepermans <robinp.1273@gmail.com>
Robin Pepermans <robinp.1273@gmail.com> <robin@users.mediawiki.org>
robinhood701 <robinhood70@live.ca>
Rohan <rohan1395@yahoo.com>
Rotem Liss <rotemliss@gmail.com> <rotem@users.mediawiki.org>
Rummana Yasmeen <ryasmeen@wikimedia.org>
Russ Nelson <russnelson@gmail.com> <nelson@users.mediawiki.org>
Ryan Kaldari <rkaldari@wikimedia.org>
Ryan Kaldari <rkaldari@wikimedia.org> <kaldari@gmail.com>
Ryan Kaldari <rkaldari@wikimedia.org> <kaldari@users.mediawiki.org>
Ryan Lane <rlane32@gmail.com>
Ryan Lane <rlane32@gmail.com> <laner@users.mediawiki.org>
Ryan Lane <rlane32@gmail.com> <rlane@wikimedia.org>
Ryan Schmidt <skizzerz@gmail.com>
Ryan Schmidt <skizzerz@gmail.com> <skizzerz@skizzerz.net>
Ryan Schmidt <skizzerz@gmail.com> <skizzerz@users.mediawiki.org>
S Page <spage@wikimedia.org>
SBassett <sbassett@wikimedia.org>
Sakretsu <sakretsu.wiki@gmail.com>
Sam Reed <reedy@wikimedia.org>
Sam Reed <reedy@wikimedia.org> <sam@reedyboy.net>
Sam Reed <reedy@wikimedia.org> <Reedy reedy@wikimedia.org>
Sam Reed <reedy@wikimedia.org> <reedy@formey.wikimedia.org>
Sam Reed <reedy@wikimedia.org> <reedy@users.mediawiki.org>
Sam Smith <git@samsmith.io>
Sammy Tarling <sam@theresnotime.co.uk>
Sammy Tarling <starling@wikimedia.org>
Sammy Tarling <starling@wikimedia.org> <theresnotime@wikimedia.org>
Sammy Tarling <starling@wikimedia.org> <starling-ctr@wikimedia.org>
Santhosh Thottingal <santhosh.thottingal@gmail.com>
Santhosh Thottingal <santhosh.thottingal@gmail.com> <santhosh@users.mediawiki.org>
Schnark (Michael M.) <listenleser@gmail.com>
Scimonster <tehalmightyscimonster@gmail.com>
Sean Colombo <sean.colombo@gmail.com> <sean_colombo@users.mediawiki.org>
Sean Pringle <springle@wikimedia.org>
Seb35 <seb35wikipedia@gmail.com>
Sergio Santoro <santoro.srg@gmail.com>
Shahyar <shahyar@gmail.com>
Shinjiman <shinjiman@gmail.com>
Shinjiman <shinjiman@gmail.com> <shinjiman@users.mediawiki.org>
shirayuki <shirayuking@gmail.com>
Siebrand Mazeland <s.mazeland@xs4all.nl>
Siebrand Mazeland <s.mazeland@xs4all.nl> <siebrand@kitano.nl>
Siebrand Mazeland <s.mazeland@xs4all.nl> <siebrand@users.mediawiki.org>
Siebrand Mazeland <s.mazeland@xs4all.nl> <siebrand@wikimedia.org>
Simone Cuomo <simone@thisdot.co>
sjoerddebruin <sjoerddebruin@me.com>
Smriti Singh <smritis.31095@gmail.com>
Sohom Datta <sohom.datta@learner.manipal.edu>
Sohom Datta <sohom.datta@learner.manipal.edu> <sohomdatta1+git@gmail.com>
Sorawee Porncharoenwase <nullzero.free@gmail.com>
SQL <sxwiki@gmail.com> <sql@users.mediawiki.org>
ST47 <en.wp.st47@gmail.com>
stang <pmx42@live.cn>
stang <pmx42@live.cn> <stang@toolforge.org>
Stanislav Malyshev <smalyshev@gmail.com>
Stephan Gambke <s7eph4n@gmail.com>
Stephane Bisson <sbisson@wikimedia.org>
Stephen Liang <github@stephenliang.pw>
Steve Sanbeg <ffnaort@jro.qr> <sanbeg@users.mediawiki.org>
Steven Roddis <StevenRoddis@users.noreply.github.com>
Steven Walling <swalling@wikimedia.org>
Stevie Beth Mhaol <sbmhaol@wikimedia.org>
Stevie Beth Mhaol <sbmhaol@wikimedia.org> <sshirley@wikimedia.org>
Subramanya Sastry <ssastry@wikimedia.org>
Sucheta Ghoshal <sghoshal@wikimedia.org>
Sumit Asthana <asthana.sumit23@gmail.com>
Taavi Väänänen <hi@taavi.wtf>
Taavi Väänänen <hi@taavi.wtf> <hi@tassu.me>
Taavi Väänänen <hi@taavi.wtf> <taavi@wikimedia.org>
tacsipacsi <tacsipacsi@jnet.hu>
TerraCodes <terracodes@tools.wmflabs.org>
Thalia Chan <thalia@cantorion.org>
Thalia Chan <thalia@cantorion.org> <thalia.e.chan@googlemail.com>
Thiemo Kreuz <thiemo.kreuz@wikimedia.de>
Thiemo Kreuz <thiemo.kreuz@wikimedia.de> <thiemo.maettig@wikimedia.de>
Thiemo Kreuz <thiemo.kreuz@wikimedia.de> <mr.heat@gmx.de>
This, that and the other <at.light@live.com.au>
tholam <t.lam@lamsinfosystem.com>
Thomas Bleher <ThomasBleher@gmx.de> <tbleher@users.mediawiki.org>
Thomas Chin <tchin@wikimedia.org>
Thomas Dalton <thomas.dalton@gmail.com> <tango@users.mediawiki.org>
Thomas Gries <mail@tgries.de> <wikinaut@users.mediawiki.org>
Tim Landscheidt <tim@tim-landscheidt.de>
Tim Laqua <t.laqua@gmail.com> <tlaqua@users.mediawiki.org>
Tim Starling <tstarling@wikimedia.org>
Tim Starling <tstarling@wikimedia.org> <tstarling@users.mediawiki.org>
Timo Tijhof <krinkle@fastmail.com>
Timo Tijhof <krinkle@fastmail.com> <krinkle@users.mediawiki.org>
Timo Tijhof <krinkle@fastmail.com> <timo@wikimedia.org>
Timo Tijhof <krinkle@fastmail.com> <ttijhof@wikimedia.org>
Timo Tijhof <krinkle@fastmail.com> <krinklemail@gmail.com>
Tina Johnson <tinajohnson.1234@gmail.com>
Tobi_406 <tobim406@gmail.com>
Tom Maaswinkel <tom.maaswinkel@12wiki.eu> <thedevilonline@users.mediawiki.org>
Tomasz Finc <tfinc@wikimedia.org> <tomasz@users.mediawiki.org>
Tomasz W. Kozlowski <tomasz@twkozlowski.com>
Tomasz W. Kozlowski <tomasz@twkozlowski.com> <tomasz@twkozlowski.net>
Tomasz W. Kozlowski <tomasz@twkozlowski.com> <twkozlowski@gmail.com>
Tony Thomas <01tonythomas@gmail.com>
Tpt <thomaspt@hotmail.fr>
Trevor Parscal <trevorparscal@gmail.com>
Trevor Parscal <trevorparscal@gmail.com> <tparscal@users.mediawiki.org>
Trevor Parscal <trevorparscal@gmail.com> <tparscal@wikimedia.org>
Trey Jones <tjones@wikimedia.org>
Tyler Cipriani <tcipriani@wikimedia.org>
Tyler Romeo <tylerromeo@gmail.com>
Umherirrender <umherirrender_de.wp@web.de>
Victor Barbu <victorbarbu08@gmail.com>
Victor Vasiliev <vasilvv@mit.edu>
Victor Vasiliev <vasilvv@mit.edu> <vasilievvv@users.mediawiki.org>
Victor Vasiliev <vasilvv@mit.edu> <vasilvv@gmail.com>
Vlad Shapik <vlad.shapik@speedandfunction.com>
Vikas S Yaligar <vikasyaligar.it@gmail.com>
Vinitha <vinithacse@gmail.com>
Vivek Ghaisas <v.a.ghaisas@gmail.com>
Volker E <volker.e@wikimedia.org>
Wendy Quarshie <wquarshie@wikimedia.org>
wctaiwan <wctaiwan@gmail.com>
withoutaname <drevitchi@gmail.com>
X! <soxred93@gmail.com> <soxred93@users.mediawiki.org>
Yaron Koren <yaron57@gmail.com>
Yaron Koren <yaron57@gmail.com> <yaron@users.mediawiki.org>
Yaroslav Melnychuk <yaroslavmelnuchuk@gmail.com>
Yongmin Hong <revi@pobox.com>
Yongmin Hong <revi@pobox.com> <revi@member.fsf.org>
Yongmin Hong <revi@pobox.com> <reviwiki@gmail.com>
Yuri Astrakhan <yurik@wikimedia.org>
Yuri Astrakhan <yurik@wikimedia.org> <yuriastrakhan@gmail.com>
Yuri Astrakhan <yurik@wikimedia.org> <yurik@users.mediawiki.org>
Yuriy Shnitkovskiy <bmp2558@gmail.com>
Yusuke Matsubara <whym@whym.org>
Yuvi Panda <yuvipanda@gmail.com>
Zak Greant <zak+mediawiki@fooassociates.com> <zak@users.mediawiki.org>
Zhengzhu Feng <zhengzhu@gmail.com>
Zhengzhu Feng <zhengzhu@gmail.com> <zhengzhu@users.mediawiki.org>
Zoranzoki21 <zorandori4444@gmail.com>
Zppix <support@zppixballee.com>
Ævar Arnfjörð Bjarmason <avarab@gmail.com> <avar@users.mediawiki.org>
Étienne Beaulé <beauleetienne0@gmail.com>
Željko Filipin <zeljko.filipin@gmail.com>
Željko Filipin <zeljko.filipin@gmail.com> <zfilipin@wikimedia.org>
星耀晨曦 <razesoldier@outlook.com>
星耀晨曦 <razesoldier@outlook.com> <liguangjie4399@hotmail.com>
沈澄心 <dringsim@qq.com>

182
mediawiki/.phan/config.php Normal file
View File

@ -0,0 +1,182 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
*/
$cfg = require __DIR__ . '/../vendor/mediawiki/mediawiki-phan-config/src/config.php';
// This value should match the PHP version specified in composer.json,
// PHPVersionCheck.php, and ScopeStructureTest.php
$cfg['minimum_target_php_version'] = '8.1.0';
$cfg['file_list'] = array_merge(
$cfg['file_list'],
class_exists( Socket::class ) ? [] : [ '.phan/stubs/Socket.php' ],
class_exists( AllowDynamicProperties::class ) ? [] : [ '.phan/stubs/AllowDynamicProperties.php' ],
[
// This makes constants and globals known to Phan before processing all other files.
// You can check the parser order with --dump-parsed-file-list
'includes/Defines.php',
// @todo This isn't working yet, see globals_type_map below
// 'includes/Setup.php',
'tests/phpunit/MediaWikiIntegrationTestCase.php',
'tests/phpunit/includes/TestUser.php',
]
);
$cfg['exclude_file_list'] = array_merge(
$cfg['exclude_file_list'],
[
// Avoid microsoft/tolerant-php-parser dependency
'maintenance/findDeprecated.php',
'maintenance/CodeCleanerGlobalsPass.php',
// Avoid nikic/php-parser dependency
'maintenance/shell.php',
]
);
$cfg['autoload_internal_extension_signatures'] = [
'excimer' => '.phan/internal_stubs/excimer.phan_php',
'imagick' => '.phan/internal_stubs/imagick.phan_php',
'memcached' => '.phan/internal_stubs/memcached.phan_php',
'pcntl' => '.phan/internal_stubs/pcntl.phan_php',
'pgsql' => '.phan/internal_stubs/pgsql.phan_php',
'redis' => '.phan/internal_stubs/redis.phan_php',
'sockets' => '.phan/internal_stubs/sockets.phan_php',
'tideways_xhprof' => '.phan/internal_stubs/tideways_xhprof.phan_php',
'wikidiff2' => '.phan/internal_stubs/wikidiff.php'
];
$cfg['directory_list'] = [
'includes/',
'languages/',
'maintenance/',
'mw-config/',
'resources/',
'tests/common/',
'tests/parser/',
'tests/phan',
'tests/phpunit/mocks/',
// Do NOT add .phan/stubs/ here: stubs are conditionally loaded in file_list
];
// Include only direct production dependencies in vendor/
// Omit dev dependencies and most indirect dependencies
$composerJson = json_decode(
file_get_contents( __DIR__ . '/../composer.json' ),
true
);
$directDeps = [];
foreach ( $composerJson['require'] as $dep => $version ) {
$parts = explode( '/', $dep );
if ( count( $parts ) === 2 ) {
$directDeps[] = $dep;
}
}
// This is a list of all composer packages that are referenced by core but
// are not listed as requirements in composer.json.
$indirectDeps = [
'composer/spdx-licenses',
'doctrine/dbal',
'doctrine/sql-formatter',
'guzzlehttp/psr7',
'pear/net_url2',
'pear/pear-core-minimal',
'phpunit/phpunit',
'psr/http-client',
'psr/http-factory',
'psr/http-message',
'seld/jsonlint',
'wikimedia/testing-access-wrapper',
'wikimedia/zest-css',
];
foreach ( [ ...$directDeps, ...$indirectDeps ] as $dep ) {
$cfg['directory_list'][] = "vendor/$dep";
}
$cfg['exclude_analysis_directory_list'] = [
'vendor/',
'.phan/',
'tests/phpunit/',
// The referenced classes are not available in vendor, only when
// included from composer.
'includes/composer/',
// Directly references classes that only exist in Translate extension
'maintenance/language/',
// External class
'includes/libs/objectcache/utils/MemcachedClient.php',
// File may be valid, but may contain numerous "errors" such as iterating over an
// empty array due to the version checking in T246594 not being currently used.
'includes/PHPVersionCheck.php',
];
// TODO: Ideally we'd disable this in core, given we don't need backwards compatibility here and aliases
// should not be used. However, that would have unwanted side effects such as being unable to test
// taint-check (T321806).
$cfg['enable_class_alias_support'] = true;
// Exclude Parsoid's src/DOM in favor of .phan/stubs/DomImpl.php
$cfg['exclude_file_list'] = array_merge(
$cfg['exclude_file_list'],
array_map( fn ( $f ) => "vendor/wikimedia/parsoid/src/DOM/{$f}.php", [
'Attr', 'CharacterData', 'Comment', 'Document', 'DocumentFragment',
'DocumentType', 'Element', 'Node', 'ProcessingInstruction', 'Text',
] )
);
$cfg['file_list'][] = '.phan/stubs/DomImpl.php';
$cfg['ignore_undeclared_variables_in_global_scope'] = true;
// @todo It'd be great if we could just make phan read these from config-schema.php, to avoid
// duplicating the types. config-schema.php has JSON types though, not PHP types.
// @todo As we are removing access to global variables from the code base,
// remove them from here as well, so phan complains when something tries to use them.
$cfg['globals_type_map'] = array_merge( $cfg['globals_type_map'], [
'IP' => 'string',
'wgTitle' => \MediaWiki\Title\Title::class,
'wgGalleryOptions' => 'array',
'wgDirectoryMode' => 'int',
'wgDummyLanguageCodes' => 'string[]',
'wgNamespaceProtection' => 'array<int,string|string[]>',
'wgNamespaceAliases' => 'array<string,int>',
'wgLockManagers' => 'array[]',
'wgForeignFileRepos' => 'array[]',
'wgDefaultUserOptions' => 'array',
'wgSkipSkins' => 'string[]',
'wgLogTypes' => 'string[]',
'wgLogNames' => 'array<string,string>',
'wgLogHeaders' => 'array<string,string>',
'wgLogActionsHandlers' => 'array<string,class-string>',
'wgPasswordPolicy' => 'array<string,array<string,string|array>>',
'wgVirtualRestConfig' => 'array<string,array>',
'wgLocalInterwikis' => 'string[]',
'wgDebugLogGroups' => 'string|false|array{destination:string,sample?:int,level:int}',
'wgCookiePrefix' => 'string|false',
'wgOut' => \MediaWiki\Output\OutputPage::class,
'wgExtraNamespaces' => 'string[]',
'wgRequest' => \MediaWiki\Request\WebRequest::class,
] );
// Include a local config file if it exists
if ( file_exists( __DIR__ . '/local-config.php' ) ) {
require __DIR__ . '/local-config.php';
}
return $cfg;

View File

@ -0,0 +1,5 @@
See <https://github.com/phan/phan/wiki/How-To-Use-Stubs#generating-stubs> for
how to generate internal stubs for phan.
The stubs should be generated using the PHP version that is our lowest
requirement.

View File

@ -0,0 +1,64 @@
<?php
// These stubs were generated by the phan stub generator.
// @phan-stub-for-extension excimer@1.1.1
namespace {
class ExcimerLog implements \Iterator, \Traversable, \Countable, \ArrayAccess {
// methods
final private function __construct() {}
public function formatCollapsed() {}
public function getSpeedscopeData() {}
public function aggregateByFunction() : array {}
public function getEventCount() {}
public function current() : mixed {}
public function key() : mixed {}
public function next() : void {}
public function rewind() : void {}
public function valid() : bool {}
public function count() : int {}
public function offsetExists($offset) : bool {}
public function offsetGet($offset) : mixed {}
public function offsetSet($offset, $value) : void {}
public function offsetUnset($offset) : void {}
}
class ExcimerLogEntry {
// methods
final private function __construct() {}
public function getTimestamp() {}
public function getEventCount() {}
public function getTrace() {}
}
class ExcimerProfiler {
// methods
public function setPeriod($period) {}
public function setEventType($event_type) {}
public function setMaxDepth($max_depth) {}
public function setFlushCallback($callback, $max_samples) {}
public function clearFlushCallback() {}
public function start() {}
public function stop() {}
public function getLog() : \ExcimerLog {}
public function flush() {}
}
class ExcimerTimer {
// methods
public function setEventType($event_type) {}
public function setInterval($interval) {}
public function setPeriod($period) {}
public function setCallback($callback) {}
public function start() {}
public function stop() {}
public function getTime() {}
}
function excimer_set_timeout($callback, $interval) {}
const EXCIMER_CPU = 1;
const EXCIMER_REAL = 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,180 @@
<?php
// These stubs were generated by the phan stub generator.
// @phan-stub-for-extension memcached@3.0.1
namespace {
class Memcached {
// constants
const LIBMEMCACHED_VERSION_HEX = 16777240;
const OPT_COMPRESSION = -1001;
const OPT_COMPRESSION_TYPE = -1004;
const OPT_PREFIX_KEY = -1002;
const OPT_SERIALIZER = -1003;
const OPT_USER_FLAGS = -1006;
const OPT_STORE_RETRY_COUNT = -1005;
const HAVE_IGBINARY = true;
const HAVE_JSON = true;
const HAVE_MSGPACK = true;
const HAVE_SESSION = true;
const HAVE_SASL = true;
const OPT_HASH = 2;
const HASH_DEFAULT = 0;
const HASH_MD5 = 1;
const HASH_CRC = 2;
const HASH_FNV1_64 = 3;
const HASH_FNV1A_64 = 4;
const HASH_FNV1_32 = 5;
const HASH_FNV1A_32 = 6;
const HASH_HSIEH = 7;
const HASH_MURMUR = 8;
const OPT_DISTRIBUTION = 9;
const DISTRIBUTION_MODULA = 0;
const DISTRIBUTION_CONSISTENT = 1;
const DISTRIBUTION_VIRTUAL_BUCKET = 6;
const OPT_LIBKETAMA_COMPATIBLE = 16;
const OPT_LIBKETAMA_HASH = 17;
const OPT_TCP_KEEPALIVE = 32;
const OPT_BUFFER_WRITES = 10;
const OPT_BINARY_PROTOCOL = 18;
const OPT_NO_BLOCK = 0;
const OPT_TCP_NODELAY = 1;
const OPT_SOCKET_SEND_SIZE = 4;
const OPT_SOCKET_RECV_SIZE = 5;
const OPT_CONNECT_TIMEOUT = 14;
const OPT_RETRY_TIMEOUT = 15;
const OPT_DEAD_TIMEOUT = 36;
const OPT_SEND_TIMEOUT = 19;
const OPT_RECV_TIMEOUT = 20;
const OPT_POLL_TIMEOUT = 8;
const OPT_CACHE_LOOKUPS = 6;
const OPT_SERVER_FAILURE_LIMIT = 21;
const OPT_AUTO_EJECT_HOSTS = 28;
const OPT_HASH_WITH_PREFIX_KEY = 25;
const OPT_NOREPLY = 26;
const OPT_SORT_HOSTS = 12;
const OPT_VERIFY_KEY = 13;
const OPT_USE_UDP = 27;
const OPT_NUMBER_OF_REPLICAS = 29;
const OPT_RANDOMIZE_REPLICA_READ = 30;
const OPT_REMOVE_FAILED_SERVERS = 35;
const OPT_SERVER_TIMEOUT_LIMIT = 37;
const RES_SUCCESS = 0;
const RES_FAILURE = 1;
const RES_HOST_LOOKUP_FAILURE = 2;
const RES_UNKNOWN_READ_FAILURE = 7;
const RES_PROTOCOL_ERROR = 8;
const RES_CLIENT_ERROR = 9;
const RES_SERVER_ERROR = 10;
const RES_WRITE_FAILURE = 5;
const RES_DATA_EXISTS = 12;
const RES_NOTSTORED = 14;
const RES_NOTFOUND = 16;
const RES_PARTIAL_READ = 18;
const RES_SOME_ERRORS = 19;
const RES_NO_SERVERS = 20;
const RES_END = 21;
const RES_ERRNO = 26;
const RES_BUFFERED = 32;
const RES_TIMEOUT = 31;
const RES_BAD_KEY_PROVIDED = 33;
const RES_STORED = 15;
const RES_DELETED = 22;
const RES_STAT = 24;
const RES_ITEM = 25;
const RES_NOT_SUPPORTED = 28;
const RES_FETCH_NOTFINISHED = 30;
const RES_SERVER_MARKED_DEAD = 35;
const RES_UNKNOWN_STAT_KEY = 36;
const RES_INVALID_HOST_PROTOCOL = 34;
const RES_MEMORY_ALLOCATION_FAILURE = 17;
const RES_CONNECTION_SOCKET_CREATE_FAILURE = 11;
const RES_E2BIG = 37;
const RES_KEY_TOO_BIG = 39;
const RES_SERVER_TEMPORARILY_DISABLED = 47;
const RES_SERVER_MEMORY_ALLOCATION_FAILURE = 48;
const RES_AUTH_PROBLEM = 40;
const RES_AUTH_FAILURE = 41;
const RES_AUTH_CONTINUE = 42;
const RES_PAYLOAD_FAILURE = -1001;
const SERIALIZER_PHP = 1;
const SERIALIZER_IGBINARY = 2;
const SERIALIZER_JSON = 3;
const SERIALIZER_JSON_ARRAY = 4;
const SERIALIZER_MSGPACK = 5;
const COMPRESSION_FASTLZ = 2;
const COMPRESSION_ZLIB = 1;
const GET_PRESERVE_ORDER = 1;
const GET_EXTENDED = 2;
const GET_ERROR_RETURN_VALUE = false;
// methods
public function __construct($persistent_id = null, $callback = null) {}
public function getResultCode() {}
public function getResultMessage() {}
public function get($key, $cache_cb = null, $get_flags = null) {}
public function getByKey($server_key, $key, $cache_cb = null, $get_flags = null) {}
public function getMulti(array $keys, $get_flags = null) {}
public function getMultiByKey($server_key, array $keys, $get_flags = null) {}
public function getDelayed(array $keys, $with_cas = null, $value_cb = null) {}
public function getDelayedByKey($server_key, array $keys, $with_cas = null, $value_cb = null) {}
public function fetch() {}
public function fetchAll() {}
public function set($key, $value, $expiration = null) {}
public function setByKey($server_key, $key, $value, $expiration = null) {}
public function touch($key, $expiration) {}
public function touchByKey($server_key, $key, $expiration) {}
public function setMulti(array $items, $expiration = null) {}
public function setMultiByKey($server_key, array $items, $expiration = null) {}
public function cas($cas_token, $key, $value, $expiration = null) {}
public function casByKey($cas_token, $server_key, $key, $value, $expiration = null) {}
public function add($key, $value, $expiration = null) {}
public function addByKey($server_key, $key, $value, $expiration = null) {}
public function append($key, $value, $expiration = null) {}
public function appendByKey($server_key, $key, $value, $expiration = null) {}
public function prepend($key, $value, $expiration = null) {}
public function prependByKey($server_key, $key, $value, $expiration = null) {}
public function replace($key, $value, $expiration = null) {}
public function replaceByKey($server_key, $key, $value, $expiration = null) {}
public function delete($key, $time = null) {}
public function deleteMulti($keys, $time = null) {}
public function deleteByKey($server_key, $key, $time = null) {}
public function deleteMultiByKey($server_key, $keys, $time = null) {}
public function increment($key, $offset = null, $initial_value = null, $expiry = null) {}
public function decrement($key, $offset = null, $initial_value = null, $expiry = null) {}
public function incrementByKey($server_key, $key, $offset = null, $initial_value = null, $expiry = null) {}
public function decrementByKey($server_key, $key, $offset = null, $initial_value = null, $expiry = null) {}
public function addServer($host, $port, $weight = null) {}
public function addServers(array $servers) {}
public function getServerList() {}
public function getServerByKey($server_key) {}
public function resetServerList() {}
public function quit() {}
public function flushBuffers() {}
public function getLastErrorMessage() {}
public function getLastErrorCode() {}
public function getLastErrorErrno() {}
public function getLastDisconnectedServer() {}
public function getStats($args) {}
public function getVersion() {}
public function getAllKeys() {}
public function flush($delay = null) {}
public function getOption($option) {}
public function setOption($option, $value) {}
public function setOptions($options) {}
public function setBucket($host_map, $forward_map, $replicas) {}
public function setSaslAuthData($username, $password) {}
public function isPersistent() {}
public function isPristine() {}
}
class MemcachedException extends \RuntimeException {
// properties
protected $message;
protected $code;
protected $file;
protected $line;
}
}

View File

@ -0,0 +1,139 @@
<?php
// These stubs were generated by the phan stub generator.
// @phan-stub-for-extension pcntl@7.0.33-0+deb9u3
namespace {
function pcntl_alarm($seconds) {}
function pcntl_errno() {}
function pcntl_exec($path, $args = null, $envs = null) {}
function pcntl_fork() {}
function pcntl_get_last_error() {}
function pcntl_getpriority($pid = null, $process_identifier = null) {}
function pcntl_setpriority($priority, $pid = null, $process_identifier = null) {}
function pcntl_signal($signo, $handler, $restart_syscalls = null) {}
function pcntl_signal_dispatch() {}
function pcntl_sigprocmask($how, $set, &$oldset = null) {}
function pcntl_sigtimedwait($set, &$info = null, $seconds = null, $nanoseconds = null) {}
function pcntl_sigwaitinfo($set, &$info = null) {}
function pcntl_strerror($errno) {}
function pcntl_wait(&$status, $options = null, &$rusage = null) {}
function pcntl_waitpid($pid, &$status, $options = null, &$rusage = null) {}
function pcntl_wexitstatus($status) {}
function pcntl_wifcontinued($status) {}
function pcntl_wifexited($status) {}
function pcntl_wifsignaled($status) {}
function pcntl_wifstopped($status) {}
function pcntl_wstopsig($status) {}
function pcntl_wtermsig($status) {}
const BUS_ADRALN = 1;
const BUS_ADRERR = 2;
const BUS_OBJERR = 3;
const CLD_CONTINUED = 6;
const CLD_DUMPED = 3;
const CLD_EXITED = 1;
const CLD_KILLED = 2;
const CLD_STOPPED = 5;
const CLD_TRAPPED = 4;
const FPE_FLTDIV = 3;
const FPE_FLTINV = 7;
const FPE_FLTOVF = 4;
const FPE_FLTRES = 6;
const FPE_FLTSUB = 8;
const FPE_FLTUND = 7;
const FPE_INTDIV = 1;
const FPE_INTOVF = 2;
const ILL_BADSTK = 8;
const ILL_COPROC = 7;
const ILL_ILLADR = 3;
const ILL_ILLOPC = 1;
const ILL_ILLOPN = 2;
const ILL_ILLTRP = 4;
const ILL_PRVOPC = 5;
const ILL_PRVREG = 6;
const PCNTL_E2BIG = 7;
const PCNTL_EACCES = 13;
const PCNTL_EAGAIN = 11;
const PCNTL_ECHILD = 10;
const PCNTL_EFAULT = 14;
const PCNTL_EINTR = 4;
const PCNTL_EINVAL = 22;
const PCNTL_EIO = 5;
const PCNTL_EISDIR = 21;
const PCNTL_ELIBBAD = 80;
const PCNTL_ELOOP = 40;
const PCNTL_EMFILE = 24;
const PCNTL_ENAMETOOLONG = 36;
const PCNTL_ENFILE = 23;
const PCNTL_ENOENT = 2;
const PCNTL_ENOEXEC = 8;
const PCNTL_ENOMEM = 12;
const PCNTL_ENOTDIR = 20;
const PCNTL_EPERM = 1;
const PCNTL_ESRCH = 3;
const PCNTL_ETXTBSY = 26;
const POLL_ERR = 4;
const POLL_HUP = 6;
const POLL_IN = 1;
const POLL_MSG = 3;
const POLL_OUT = 2;
const POLL_PRI = 5;
const PRIO_PGRP = 1;
const PRIO_PROCESS = 0;
const PRIO_USER = 2;
const SEGV_ACCERR = 2;
const SEGV_MAPERR = 1;
const SIGABRT = 6;
const SIGALRM = 14;
const SIGBABY = 31;
const SIGBUS = 7;
const SIGCHLD = 17;
const SIGCLD = 17;
const SIGCONT = 18;
const SIGFPE = 8;
const SIGHUP = 1;
const SIGILL = 4;
const SIGINT = 2;
const SIGIO = 29;
const SIGIOT = 6;
const SIGKILL = 9;
const SIGPIPE = 13;
const SIGPOLL = 29;
const SIGPROF = 27;
const SIGPWR = 30;
const SIGQUIT = 3;
const SIGSEGV = 11;
const SIGSTKFLT = 16;
const SIGSTOP = 19;
const SIGSYS = 31;
const SIGTERM = 15;
const SIGTRAP = 5;
const SIGTSTP = 20;
const SIGTTIN = 21;
const SIGTTOU = 22;
const SIGURG = 23;
const SIGUSR1 = 10;
const SIGUSR2 = 12;
const SIGVTALRM = 26;
const SIGWINCH = 28;
const SIGXCPU = 24;
const SIGXFSZ = 25;
const SIG_BLOCK = 0;
const SIG_DFL = 0;
const SIG_ERR = -1;
const SIG_IGN = 1;
const SIG_SETMASK = 2;
const SIG_UNBLOCK = 1;
const SI_ASYNCIO = -4;
const SI_KERNEL = 128;
const SI_MESGQ = -3;
const SI_QUEUE = -1;
const SI_SIGIO = -5;
const SI_TIMER = -2;
const SI_TKILL = -6;
const SI_USER = 0;
const TRAP_BRKPT = 1;
const TRAP_TRACE = 2;
const WCONTINUED = 8;
const WNOHANG = 1;
const WUNTRACED = 2;
}

View File

@ -0,0 +1,189 @@
<?php
// These stubs were generated by the phan stub generator.
// @phan-stub-for-extension pgsql@7.3.4
namespace {
function pg_affected_rows($result) {}
function pg_cancel_query($connection) {}
function pg_client_encoding($connection = null) {}
function pg_clientencoding($connection = null) {}
function pg_close($connection = null) {}
function pg_cmdtuples($result) {}
function pg_connect($connection_string, $connect_type = null, $host = null, $port = null, $options = null, $tty = null, $database = null) {}
function pg_connect_poll($connection = null) {}
function pg_connection_busy($connection) {}
function pg_connection_reset($connection) {}
function pg_connection_status($connection) {}
function pg_consume_input($connection) {}
function pg_convert($db, $table, $values, $options = null) {}
function pg_copy_from($connection, $table_name, $rows, $delimiter = null, $null_as = null) {}
function pg_copy_to($connection, $table_name, $delimiter = null, $null_as = null) {}
function pg_dbname($connection = null) {}
function pg_delete($db, $table, $ids, $options = null) {}
function pg_end_copy($connection = null) {}
function pg_errormessage($connection = null) {}
function pg_escape_bytea($connection = null, $data = null) {}
function pg_escape_identifier($connection = null, $data = null) {}
function pg_escape_literal($connection = null, $data = null) {}
function pg_escape_string($connection = null, $data = null) {}
function pg_exec($connection = null, $query = null) {}
function pg_execute($connection = null, $stmtname = null, $params = null) {}
function pg_fetch_all($result, $result_type = null) {}
function pg_fetch_all_columns($result, $column_number = null) {}
function pg_fetch_array($result, $row = null, $result_type = null) {}
function pg_fetch_assoc($result, $row = null) {}
function pg_fetch_object($result, $row = null, $class_name = null, $l = null, $ctor_params = null) {}
function pg_fetch_result($result, $row_number = null, $field_name = null) {}
function pg_fetch_row($result, $row = null, $result_type = null) {}
function pg_field_is_null($result, $row = null, $field_name_or_number = null) {}
function pg_field_name($result, $field_number) {}
function pg_field_num($result, $field_name) {}
function pg_field_prtlen($result, $row = null, $field_name_or_number = null) {}
function pg_field_size($result, $field_number) {}
function pg_field_table($result, $field_number, $oid_only = null) {}
function pg_field_type($result, $field_number) {}
function pg_field_type_oid($result, $field_number) {}
function pg_fieldisnull($result, $row = null, $field_name_or_number = null) {}
function pg_fieldname($result, $field_number) {}
function pg_fieldnum($result, $field_name) {}
function pg_fieldprtlen($result, $row = null, $field_name_or_number = null) {}
function pg_fieldsize($result, $field_number) {}
function pg_fieldtype($result, $field_number) {}
function pg_flush($connection) {}
function pg_free_result($result) {}
function pg_freeresult($result) {}
function pg_get_notify($connection = null, $e = null) {}
function pg_get_pid($connection = null) {}
function pg_get_result($connection) {}
function pg_getlastoid($result) {}
function pg_host($connection = null) {}
function pg_insert($db, $table, $values, $options = null) {}
function pg_last_error($connection = null) {}
function pg_last_notice($connection, $option = null) {}
function pg_last_oid($result) {}
function pg_lo_close($large_object) {}
function pg_lo_create($connection = null, $large_object_id = null) {}
function pg_lo_export($connection = null, $objoid = null, $filename = null) {}
function pg_lo_import($connection = null, $filename = null, $large_object_oid = null) {}
function pg_lo_open($connection = null, $large_object_oid = null, $mode = null) {}
function pg_lo_read($large_object, $len = null) {}
function pg_lo_read_all($large_object) {}
function pg_lo_seek($large_object, $offset, $whence = null) {}
function pg_lo_tell($large_object) {}
function pg_lo_truncate($large_object, $size = null) {}
function pg_lo_unlink($connection = null, $large_object_oid = null) {}
function pg_lo_write($large_object, $buf, $len = null) {}
function pg_loclose($large_object) {}
function pg_locreate($connection = null, $large_object_id = null) {}
function pg_loexport($connection = null, $objoid = null, $filename = null) {}
function pg_loimport($connection = null, $filename = null, $large_object_oid = null) {}
function pg_loopen($connection = null, $large_object_oid = null, $mode = null) {}
function pg_loread($large_object, $len = null) {}
function pg_loreadall($large_object) {}
function pg_lounlink($connection = null, $large_object_oid = null) {}
function pg_lowrite($large_object, $buf, $len = null) {}
function pg_meta_data($db, $table) {}
function pg_num_fields($result) {}
function pg_num_rows($result) {}
function pg_numfields($result) {}
function pg_numrows($result) {}
function pg_options($connection = null) {}
function pg_parameter_status($connection, $param_name = null) {}
function pg_pconnect($connection_string, $host = null, $port = null, $options = null, $tty = null, $database = null) {}
function pg_ping($connection = null) {}
function pg_port($connection = null) {}
function pg_prepare($connection = null, $stmtname = null, $query = null) {}
function pg_put_line($connection = null, $query = null) {}
function pg_query($connection = null, $query = null) {}
function pg_query_params($connection = null, $query = null, $params = null) {}
function pg_result($connection) {}
function pg_result_error($result) {}
function pg_result_error_field($result, $fieldcode) {}
function pg_result_seek($result, $offset) {}
function pg_result_status($result, $result_type = null) {}
function pg_select($db, $table, $ids, $options = null, $result_type = null) {}
function pg_send_execute($connection, $stmtname, $params) {}
function pg_send_prepare($connection, $stmtname, $query) {}
function pg_send_query($connection, $query) {}
function pg_send_query_params($connection, $query, $params) {}
function pg_set_client_encoding($connection = null, $encoding = null) {}
function pg_set_error_verbosity($connection = null, $verbosity = null) {}
function pg_setclientencoding($connection = null, $encoding = null) {}
function pg_socket($connection) {}
function pg_trace($filename, $mode = null, $connection = null) {}
function pg_transaction_status($connection) {}
function pg_tty($connection = null) {}
function pg_unescape_bytea($data) {}
function pg_untrace($connection = null) {}
function pg_update($db, $table, $fields, $ids, $options = null) {}
function pg_version($connection = null) {}
const PGSQL_ASSOC = 1;
const PGSQL_BAD_RESPONSE = 5;
const PGSQL_BOTH = 3;
const PGSQL_COMMAND_OK = 1;
const PGSQL_CONNECTION_AUTH_OK = 5;
const PGSQL_CONNECTION_AWAITING_RESPONSE = 4;
const PGSQL_CONNECTION_BAD = 1;
const PGSQL_CONNECTION_MADE = 3;
const PGSQL_CONNECTION_OK = 0;
const PGSQL_CONNECTION_SETENV = 6;
const PGSQL_CONNECTION_STARTED = 2;
const PGSQL_CONNECT_ASYNC = 4;
const PGSQL_CONNECT_FORCE_NEW = 2;
const PGSQL_CONV_FORCE_NULL = 4;
const PGSQL_CONV_IGNORE_DEFAULT = 2;
const PGSQL_CONV_IGNORE_NOT_NULL = 8;
const PGSQL_COPY_IN = 4;
const PGSQL_COPY_OUT = 3;
const PGSQL_DIAG_COLUMN_NAME = 99;
const PGSQL_DIAG_CONSTRAINT_NAME = 110;
const PGSQL_DIAG_CONTEXT = 87;
const PGSQL_DIAG_DATATYPE_NAME = 100;
const PGSQL_DIAG_INTERNAL_POSITION = 112;
const PGSQL_DIAG_INTERNAL_QUERY = 113;
const PGSQL_DIAG_MESSAGE_DETAIL = 68;
const PGSQL_DIAG_MESSAGE_HINT = 72;
const PGSQL_DIAG_MESSAGE_PRIMARY = 77;
const PGSQL_DIAG_SCHEMA_NAME = 115;
const PGSQL_DIAG_SEVERITY = 83;
const PGSQL_DIAG_SEVERITY_NONLOCALIZED = 86;
const PGSQL_DIAG_SOURCE_FILE = 70;
const PGSQL_DIAG_SOURCE_FUNCTION = 82;
const PGSQL_DIAG_SOURCE_LINE = 76;
const PGSQL_DIAG_SQLSTATE = 67;
const PGSQL_DIAG_STATEMENT_POSITION = 80;
const PGSQL_DIAG_TABLE_NAME = 116;
const PGSQL_DML_ASYNC = 1024;
const PGSQL_DML_ESCAPE = 4096;
const PGSQL_DML_EXEC = 512;
const PGSQL_DML_NO_CONV = 256;
const PGSQL_DML_STRING = 2048;
const PGSQL_EMPTY_QUERY = 0;
const PGSQL_ERRORS_DEFAULT = 1;
const PGSQL_ERRORS_TERSE = 0;
const PGSQL_ERRORS_VERBOSE = 2;
const PGSQL_FATAL_ERROR = 7;
const PGSQL_LIBPQ_VERSION = '9.6.9';
const PGSQL_LIBPQ_VERSION_STR = 'PostgreSQL 9.6.9 (win32)';
const PGSQL_NONFATAL_ERROR = 6;
const PGSQL_NOTICE_ALL = 2;
const PGSQL_NOTICE_CLEAR = 3;
const PGSQL_NOTICE_LAST = 1;
const PGSQL_NUM = 2;
const PGSQL_POLLING_ACTIVE = 4;
const PGSQL_POLLING_FAILED = 0;
const PGSQL_POLLING_OK = 3;
const PGSQL_POLLING_READING = 1;
const PGSQL_POLLING_WRITING = 2;
const PGSQL_SEEK_CUR = 1;
const PGSQL_SEEK_END = 2;
const PGSQL_SEEK_SET = 0;
const PGSQL_STATUS_LONG = 1;
const PGSQL_STATUS_STRING = 2;
const PGSQL_TRANSACTION_ACTIVE = 1;
const PGSQL_TRANSACTION_IDLE = 0;
const PGSQL_TRANSACTION_INERROR = 3;
const PGSQL_TRANSACTION_INTRANS = 2;
const PGSQL_TRANSACTION_UNKNOWN = 4;
const PGSQL_TUPLES_OK = 2;
}

View File

@ -0,0 +1,549 @@
<?php
// These stubs were generated by the phan stub generator.
// @phan-stub-for-extension redis@5.1.1
namespace {
class Redis {
// constants
const REDIS_NOT_FOUND = 0;
const REDIS_STRING = 1;
const REDIS_SET = 2;
const REDIS_LIST = 3;
const REDIS_ZSET = 4;
const REDIS_HASH = 5;
const REDIS_STREAM = 6;
const PIPELINE = 2;
const ATOMIC = 0;
const MULTI = 1;
const OPT_SERIALIZER = 1;
const OPT_PREFIX = 2;
const OPT_READ_TIMEOUT = 3;
const OPT_TCP_KEEPALIVE = 6;
const OPT_COMPRESSION = 7;
const OPT_REPLY_LITERAL = 8;
const OPT_COMPRESSION_LEVEL = 9;
const SERIALIZER_NONE = 0;
const SERIALIZER_PHP = 1;
const SERIALIZER_IGBINARY = 2;
const SERIALIZER_JSON = 4;
const COMPRESSION_NONE = 0;
const OPT_SCAN = 4;
const SCAN_RETRY = 1;
const SCAN_NORETRY = 0;
const AFTER = 'after';
const BEFORE = 'before';
// methods
public function __construct() {}
public function __destruct() {}
public function _prefix($key) {}
public function _serialize($value) {}
public function _unserialize($value) {}
public function append($key, $value) {}
public function auth($password) {}
public function bgSave() {}
public function bgrewriteaof() {}
public function bitcount($key) {}
public function bitop($operation, $ret_key, $key, ...$other_keys) {}
public function bitpos($key, $bit, $start = null, $end = null) {}
public function blPop($key, $timeout_or_key, ...$extra_args) {}
public function brPop($key, $timeout_or_key, ...$extra_args) {}
public function brpoplpush($src, $dst, $timeout) {}
public function bzPopMax($key, $timeout_or_key, ...$extra_args) {}
public function bzPopMin($key, $timeout_or_key, ...$extra_args) {}
public function clearLastError() {}
public function client($cmd, ...$args) {}
public function close() {}
public function command(...$args) {}
public function config($cmd, $key, $value = null) {}
public function connect($host, $port = null, $timeout = null, $retry_interval = null) {}
public function dbSize() {}
public function debug($key) {}
public function decr($key) {}
public function decrBy($key, $value) {}
public function del($key, ...$other_keys) {}
public function discard() {}
public function dump($key) {}
public function echo($msg) {}
public function eval($script, $args = null, $num_keys = null) {}
public function evalsha($script_sha, $args = null, $num_keys = null) {}
public function exec() {}
public function exists($key, ...$other_keys) {}
public function expire($key, $timeout) {}
public function expireAt($key, $timestamp) {}
public function flushAll($async = null) {}
public function flushDB($async = null) {}
public function geoadd($key, $lng, $lat, $member, ...$other_triples) {}
public function geodist($key, $src, $dst, $unit = null) {}
public function geohash($key, $member, ...$other_members) {}
public function geopos($key, $member, ...$other_members) {}
public function georadius($key, $lng, $lan, $radius, $unit, array $opts = null) {}
public function georadius_ro($key, $lng, $lan, $radius, $unit, array $opts = null) {}
public function georadiusbymember($key, $member, $radius, $unit, array $opts = null) {}
public function georadiusbymember_ro($key, $member, $radius, $unit, array $opts = null) {}
public function get($key) {}
public function getAuth() {}
public function getBit($key, $offset) {}
public function getDBNum() {}
public function getHost() {}
public function getLastError() {}
public function getMode() {}
public function getOption($option) {}
public function getPersistentID() {}
public function getPort() {}
public function getRange($key, $start, $end) {}
public function getReadTimeout() {}
public function getSet($key, $value) {}
public function getTimeout() {}
public function hDel($key, $member, ...$other_members) {}
public function hExists($key, $member) {}
public function hGet($key, $member) {}
public function hGetAll($key) {}
public function hIncrBy($key, $member, $value) {}
public function hIncrByFloat($key, $member, $value) {}
public function hKeys($key) {}
public function hLen($key) {}
public function hMget($key, array $keys) {}
public function hMset($key, array $pairs) {}
public function hSet($key, $member, $value) {}
public function hSetNx($key, $member, $value) {}
public function hStrLen($key, $member) {}
public function hVals($key) {}
public function hscan($str_key, &$i_iterator, $str_pattern = null, $i_count = null) {}
public function incr($key) {}
public function incrBy($key, $value) {}
public function incrByFloat($key, $value) {}
public function info($option = null) {}
public function isConnected() {}
public function keys($pattern) {}
public function lInsert($key, $position, $pivot, $value) {}
public function lLen($key) {}
public function lPop($key) {}
public function lPush($key, $value) {}
public function lPushx($key, $value) {}
public function lSet($key, $index, $value) {}
public function lastSave() {}
public function lindex($key, $index) {}
public function lrange($key, $start, $end) {}
public function lrem($key, $value, $count) {}
public function ltrim($key, $start, $stop) {}
public function mget(array $keys) {}
public function migrate($host, $port, $key, $db, $timeout, $copy = null, $replace = null) {}
public function move($key, $dbindex) {}
public function mset(array $pairs) {}
public function msetnx(array $pairs) {}
public function multi($mode = null) {}
public function object($field, $key) {}
public function pconnect($host, $port = null, $timeout = null) {}
public function persist($key) {}
public function pexpire($key, $timestamp) {}
public function pexpireAt($key, $timestamp) {}
public function pfadd($key, array $elements) {}
public function pfcount($key) {}
public function pfmerge($dstkey, array $keys) {}
public function ping() {}
public function pipeline() {}
public function psetex($key, $expire, $value) {}
public function psubscribe(array $patterns, $callback) {}
public function pttl($key) {}
public function publish($channel, $message) {}
public function pubsub($cmd, ...$args) {}
public function punsubscribe($pattern, ...$other_patterns) {}
public function rPop($key) {}
public function rPush($key, $value) {}
public function rPushx($key, $value) {}
public function randomKey() {}
public function rawcommand($cmd, ...$args) {}
public function rename($key, $newkey) {}
public function renameNx($key, $newkey) {}
public function restore($ttl, $key, $value) {}
public function role() {}
public function rpoplpush($src, $dst) {}
public function sAdd($key, $value) {}
public function sAddArray($key, array $options) {}
public function sDiff($key, ...$other_keys) {}
public function sDiffStore($dst, $key, ...$other_keys) {}
public function sInter($key, ...$other_keys) {}
public function sInterStore($dst, $key, ...$other_keys) {}
public function sMembers($key) {}
public function sMove($src, $dst, $value) {}
public function sPop($key) {}
public function sRandMember($key, $count = null) {}
public function sUnion($key, ...$other_keys) {}
public function sUnionStore($dst, $key, ...$other_keys) {}
public function save() {}
public function scan(&$i_iterator, $str_pattern = null, $i_count = null) {}
public function scard($key) {}
public function script($cmd, ...$args) {}
public function select($dbindex) {}
public function set($key, $value, $opts = null) {}
public function setBit($key, $offset, $value) {}
public function setOption($option, $value) {}
public function setRange($key, $offset, $value) {}
public function setex($key, $expire, $value) {}
public function setnx($key, $value) {}
public function sismember($key, $value) {}
public function slaveof($host = null, $port = null) {}
public function slowlog($arg, $option = null) {}
public function sort($key, array $options = null) {}
public function sortAsc($key, $pattern = null, $get = null, $start = null, $end = null, $getList = null) {}
public function sortAscAlpha($key, $pattern = null, $get = null, $start = null, $end = null, $getList = null) {}
public function sortDesc($key, $pattern = null, $get = null, $start = null, $end = null, $getList = null) {}
public function sortDescAlpha($key, $pattern = null, $get = null, $start = null, $end = null, $getList = null) {}
public function srem($key, $member, ...$other_members) {}
public function sscan($str_key, &$i_iterator, $str_pattern = null, $i_count = null) {}
public function strlen($key) {}
public function subscribe(array $channels, $callback) {}
public function swapdb($srcdb, $dstdb) {}
public function time() {}
public function ttl($key) {}
public function type($key) {}
public function unlink($key, ...$other_keys) {}
public function unsubscribe($channel, ...$other_channels) {}
public function unwatch() {}
public function wait($numslaves, $timeout) {}
public function watch($key, ...$other_keys) {}
public function xack($str_key, $str_group, array $arr_ids) {}
public function xadd($str_key, $str_id, array $arr_fields, $i_maxlen = null, $boo_approximate = null) {}
public function xclaim($str_key, $str_group, $str_consumer, $i_min_idle, array $arr_ids, array $arr_opts = null) {}
public function xdel($str_key, array $arr_ids) {}
public function xgroup($str_operation, $str_key = null, $str_arg1 = null, $str_arg2 = null, $str_arg3 = null) {}
public function xinfo($str_cmd, $str_key = null, $str_group = null) {}
public function xlen($key) {}
public function xpending($str_key, $str_group, $str_start = null, $str_end = null, $i_count = null, $str_consumer = null) {}
public function xrange($str_key, $str_start, $str_end, $i_count = null) {}
public function xread(array $arr_streams, $i_count = null, $i_block = null) {}
public function xreadgroup($str_group, $str_consumer, array $arr_streams, $i_count = null, $i_block = null) {}
public function xrevrange($str_key, $str_start, $str_end, $i_count = null) {}
public function xtrim($str_key, $i_maxlen, $boo_approximate = null) {}
public function zAdd($key, $score, $value) {}
public function zCard($key) {}
public function zCount($key, $min, $max) {}
public function zIncrBy($key, $value, $member) {}
public function zLexCount($key, $min, $max) {}
public function zPopMax($key) {}
public function zPopMin($key) {}
public function zRange($key, $start, $end, $scores = null) {}
public function zRangeByLex($key, $min, $max, $offset = null, $limit = null) {}
public function zRangeByScore($key, $start, $end, array $options = null) {}
public function zRank($key, $member) {}
public function zRem($key, $member, ...$other_members) {}
public function zRemRangeByLex($key, $min, $max) {}
public function zRemRangeByRank($key, $start, $end) {}
public function zRemRangeByScore($key, $min, $max) {}
public function zRevRange($key, $start, $end, $scores = null) {}
public function zRevRangeByLex($key, $min, $max, $offset = null, $limit = null) {}
public function zRevRangeByScore($key, $start, $end, array $options = null) {}
public function zRevRank($key, $member) {}
public function zScore($key, $member) {}
public function zinterstore($key, array $keys, ?array $weights = null, $aggregate = null) {}
public function zscan($str_key, &$i_iterator, $str_pattern = null, $i_count = null) {}
public function zunionstore($key, array $keys, ?array $weights = null, $aggregate = null) {}
public function delete($key, ...$other_keys) {}
public function evaluate($script, $args = null, $num_keys = null) {}
public function evaluateSha($script_sha, $args = null, $num_keys = null) {}
public function getKeys($pattern) {}
public function getMultiple(array $keys) {}
public function lGet($key, $index) {}
public function lGetRange($key, $start, $end) {}
public function lRemove($key, $value, $count) {}
public function lSize($key) {}
public function listTrim($key, $start, $stop) {}
public function open($host, $port = null, $timeout = null, $retry_interval = null) {}
public function popen($host, $port = null, $timeout = null) {}
public function renameKey($key, $newkey) {}
public function sContains($key, $value) {}
public function sGetMembers($key) {}
public function sRemove($key, $member, ...$other_members) {}
public function sSize($key) {}
public function sendEcho($msg) {}
public function setTimeout($key, $timeout) {}
public function substr($key, $start, $end) {}
public function zDelete($key, $member, ...$other_members) {}
public function zDeleteRangeByRank($key, $min, $max) {}
public function zDeleteRangeByScore($key, $min, $max) {}
public function zInter($key, array $keys, ?array $weights = null, $aggregate = null) {}
public function zRemove($key, $member, ...$other_members) {}
public function zRemoveRangeByScore($key, $min, $max) {}
public function zReverseRange($key, $start, $end, $scores = null) {}
public function zSize($key) {}
public function zUnion($key, array $keys, ?array $weights = null, $aggregate = null) {}
}
class RedisArray {
// methods
public function __call($function_name, $arguments) {}
public function __construct($name_or_hosts, array $options = null) {}
public function _continuum() {}
public function _distributor() {}
public function _function() {}
public function _hosts() {}
public function _instance($host) {}
public function _rehash($callable = null) {}
public function _target($key) {}
public function bgsave() {}
public function del($keys) {}
public function discard() {}
public function exec() {}
public function flushall($async = null) {}
public function flushdb($async = null) {}
public function getOption($opt) {}
public function info() {}
public function keys($pattern) {}
public function mget($keys) {}
public function mset($pairs) {}
public function multi($host, $mode = null) {}
public function ping() {}
public function save() {}
public function select($index) {}
public function setOption($opt, $value) {}
public function unlink() {}
public function unwatch() {}
public function delete($keys) {}
public function getMultiple($keys) {}
}
class RedisCluster {
// constants
const REDIS_NOT_FOUND = 0;
const REDIS_STRING = 1;
const REDIS_SET = 2;
const REDIS_LIST = 3;
const REDIS_ZSET = 4;
const REDIS_HASH = 5;
const REDIS_STREAM = 6;
const ATOMIC = 0;
const MULTI = 1;
const OPT_SERIALIZER = 1;
const OPT_PREFIX = 2;
const OPT_READ_TIMEOUT = 3;
const OPT_TCP_KEEPALIVE = 6;
const OPT_COMPRESSION = 7;
const OPT_REPLY_LITERAL = 8;
const OPT_COMPRESSION_LEVEL = 9;
const SERIALIZER_NONE = 0;
const SERIALIZER_PHP = 1;
const SERIALIZER_IGBINARY = 2;
const SERIALIZER_JSON = 4;
const COMPRESSION_NONE = 0;
const OPT_SCAN = 4;
const SCAN_RETRY = 1;
const SCAN_NORETRY = 0;
const OPT_SLAVE_FAILOVER = 5;
const FAILOVER_NONE = 0;
const FAILOVER_ERROR = 1;
const FAILOVER_DISTRIBUTE = 2;
const FAILOVER_DISTRIBUTE_SLAVES = 3;
const AFTER = 'after';
const BEFORE = 'before';
// methods
public function __construct($name, array $seeds = null, $timeout = null, $read_timeout = null, $persistent = null, $auth = null) {}
public function _masters() {}
public function _prefix($key) {}
public function _redir() {}
public function _serialize($value) {}
public function _unserialize($value) {}
public function append($key, $value) {}
public function bgrewriteaof($key_or_address) {}
public function bgsave($key_or_address) {}
public function bitcount($key) {}
public function bitop($operation, $ret_key, $key, ...$other_keys) {}
public function bitpos($key, $bit, $start = null, $end = null) {}
public function blpop($key, $timeout_or_key, ...$extra_args) {}
public function brpop($key, $timeout_or_key, ...$extra_args) {}
public function brpoplpush($src, $dst, $timeout) {}
public function clearlasterror() {}
public function bzpopmax($key, $timeout_or_key, ...$extra_args) {}
public function bzpopmin($key, $timeout_or_key, ...$extra_args) {}
public function client($key_or_address, $arg = null, ...$other_args) {}
public function close() {}
public function cluster($key_or_address, $arg = null, ...$other_args) {}
public function command(...$args) {}
public function config($key_or_address, $arg = null, ...$other_args) {}
public function dbsize($key_or_address) {}
public function decr($key) {}
public function decrby($key, $value) {}
public function del($key, ...$other_keys) {}
public function discard() {}
public function dump($key) {}
public function echo($msg) {}
public function eval($script, $args = null, $num_keys = null) {}
public function evalsha($script_sha, $args = null, $num_keys = null) {}
public function exec() {}
public function exists($key) {}
public function expire($key, $timeout) {}
public function expireat($key, $timestamp) {}
public function flushall($key_or_address, $async = null) {}
public function flushdb($key_or_address, $async = null) {}
public function geoadd($key, $lng, $lat, $member, ...$other_triples) {}
public function geodist($key, $src, $dst, $unit = null) {}
public function geohash($key, $member, ...$other_members) {}
public function geopos($key, $member, ...$other_members) {}
public function georadius($key, $lng, $lan, $radius, $unit, array $opts = null) {}
public function georadius_ro($key, $lng, $lan, $radius, $unit, array $opts = null) {}
public function georadiusbymember($key, $member, $radius, $unit, array $opts = null) {}
public function georadiusbymember_ro($key, $member, $radius, $unit, array $opts = null) {}
public function get($key) {}
public function getbit($key, $offset) {}
public function getlasterror() {}
public function getmode() {}
public function getoption($option) {}
public function getrange($key, $start, $end) {}
public function getset($key, $value) {}
public function hdel($key, $member, ...$other_members) {}
public function hexists($key, $member) {}
public function hget($key, $member) {}
public function hgetall($key) {}
public function hincrby($key, $member, $value) {}
public function hincrbyfloat($key, $member, $value) {}
public function hkeys($key) {}
public function hlen($key) {}
public function hmget($key, array $keys) {}
public function hmset($key, array $pairs) {}
public function hscan($str_key, &$i_iterator, $str_pattern = null, $i_count = null) {}
public function hset($key, $member, $value) {}
public function hsetnx($key, $member, $value) {}
public function hstrlen($key, $member) {}
public function hvals($key) {}
public function incr($key) {}
public function incrby($key, $value) {}
public function incrbyfloat($key, $value) {}
public function info($key_or_address, $option = null) {}
public function keys($pattern) {}
public function lastsave($key_or_address) {}
public function lget($key, $index) {}
public function lindex($key, $index) {}
public function linsert($key, $position, $pivot, $value) {}
public function llen($key) {}
public function lpop($key) {}
public function lpush($key, $value) {}
public function lpushx($key, $value) {}
public function lrange($key, $start, $end) {}
public function lrem($key, $value) {}
public function lset($key, $index, $value) {}
public function ltrim($key, $start, $stop) {}
public function mget(array $keys) {}
public function mset(array $pairs) {}
public function msetnx(array $pairs) {}
public function multi() {}
public function object($field, $key) {}
public function persist($key) {}
public function pexpire($key, $timestamp) {}
public function pexpireat($key, $timestamp) {}
public function pfadd($key, array $elements) {}
public function pfcount($key) {}
public function pfmerge($dstkey, array $keys) {}
public function ping($key_or_address) {}
public function psetex($key, $expire, $value) {}
public function psubscribe(array $patterns, $callback) {}
public function pttl($key) {}
public function publish($channel, $message) {}
public function pubsub($key_or_address, $arg = null, ...$other_args) {}
public function punsubscribe($pattern, ...$other_patterns) {}
public function randomkey($key_or_address) {}
public function rawcommand($cmd, ...$args) {}
public function rename($key, $newkey) {}
public function renamenx($key, $newkey) {}
public function restore($ttl, $key, $value) {}
public function role() {}
public function rpop($key) {}
public function rpoplpush($src, $dst) {}
public function rpush($key, $value) {}
public function rpushx($key, $value) {}
public function sadd($key, $value) {}
public function saddarray($key, array $options) {}
public function save($key_or_address) {}
public function scan(&$i_iterator, $str_node, $str_pattern = null, $i_count = null) {}
public function scard($key) {}
public function script($key_or_address, $arg = null, ...$other_args) {}
public function sdiff($key, ...$other_keys) {}
public function sdiffstore($dst, $key, ...$other_keys) {}
public function set($key, $value, $opts = null) {}
public function setbit($key, $offset, $value) {}
public function setex($key, $expire, $value) {}
public function setnx($key, $value) {}
public function setoption($option, $value) {}
public function setrange($key, $offset, $value) {}
public function sinter($key, ...$other_keys) {}
public function sinterstore($dst, $key, ...$other_keys) {}
public function sismember($key, $value) {}
public function slowlog($key_or_address, $arg = null, ...$other_args) {}
public function smembers($key) {}
public function smove($src, $dst, $value) {}
public function sort($key, array $options = null) {}
public function spop($key) {}
public function srandmember($key, $count = null) {}
public function srem($key, $value) {}
public function sscan($str_key, &$i_iterator, $str_pattern = null, $i_count = null) {}
public function strlen($key) {}
public function subscribe(array $channels, $callback) {}
public function sunion($key, ...$other_keys) {}
public function sunionstore($dst, $key, ...$other_keys) {}
public function time() {}
public function ttl($key) {}
public function type($key) {}
public function unsubscribe($channel, ...$other_channels) {}
public function unlink($key, ...$other_keys) {}
public function unwatch() {}
public function watch($key, ...$other_keys) {}
public function xack($str_key, $str_group, array $arr_ids) {}
public function xadd($str_key, $str_id, array $arr_fields, $i_maxlen = null, $boo_approximate = null) {}
public function xclaim($str_key, $str_group, $str_consumer, $i_min_idle, array $arr_ids, array $arr_opts = null) {}
public function xdel($str_key, array $arr_ids) {}
public function xgroup($str_operation, $str_key = null, $str_arg1 = null, $str_arg2 = null, $str_arg3 = null) {}
public function xinfo($str_cmd, $str_key = null, $str_group = null) {}
public function xlen($key) {}
public function xpending($str_key, $str_group, $str_start = null, $str_end = null, $i_count = null, $str_consumer = null) {}
public function xrange($str_key, $str_start, $str_end, $i_count = null) {}
public function xread(array $arr_streams, $i_count = null, $i_block = null) {}
public function xreadgroup($str_group, $str_consumer, array $arr_streams, $i_count = null, $i_block = null) {}
public function xrevrange($str_key, $str_start, $str_end, $i_count = null) {}
public function xtrim($str_key, $i_maxlen, $boo_approximate = null) {}
public function zadd($key, $score, $value) {}
public function zcard($key) {}
public function zcount($key, $min, $max) {}
public function zincrby($key, $value, $member) {}
public function zinterstore($key, array $keys, ?array $weights = null, $aggregate = null) {}
public function zlexcount($key, $min, $max) {}
public function zpopmax($key) {}
public function zpopmin($key) {}
public function zrange($key, $start, $end, $scores = null) {}
public function zrangebylex($key, $min, $max, $offset = null, $limit = null) {}
public function zrangebyscore($key, $start, $end, array $options = null) {}
public function zrank($key, $member) {}
public function zrem($key, $member, ...$other_members) {}
public function zremrangebylex($key, $min, $max) {}
public function zremrangebyrank($key, $min, $max) {}
public function zremrangebyscore($key, $min, $max) {}
public function zrevrange($key, $start, $end, $scores = null) {}
public function zrevrangebylex($key, $min, $max, $offset = null, $limit = null) {}
public function zrevrangebyscore($key, $start, $end, array $options = null) {}
public function zrevrank($key, $member) {}
public function zscan($str_key, &$i_iterator, $str_pattern = null, $i_count = null) {}
public function zscore($key, $member) {}
public function zunionstore($key, array $keys, ?array $weights = null, $aggregate = null) {}
}
class RedisClusterException extends \Exception {
// properties
protected $message;
protected $code;
protected $file;
protected $line;
}
class RedisException extends \Exception {
// properties
protected $message;
protected $code;
protected $file;
protected $line;
}
}

View File

@ -0,0 +1,211 @@
<?php
// These stubs were generated by the phan stub generator.
// @phan-stub-for-extension sockets@7.0.33-0+deb9u3
namespace {
function socket_accept($socket) {}
function socket_bind($socket, $addr, $port = null) {}
function socket_clear_error($socket = null) {}
function socket_close($socket) {}
function socket_cmsg_space($level, $type) {}
function socket_connect($socket, $addr, $port = null) {}
function socket_create($domain, $type, $protocol) {}
function socket_create_listen($port, $backlog = null) {}
function socket_create_pair($domain, $type, $protocol, &$fd) {}
function socket_export_stream($socket) {}
function socket_get_option($socket, $level, $optname) {}
function socket_getopt($socket, $level, $optname) {}
function socket_getpeername($socket, &$addr, &$port = null) {}
function socket_getsockname($socket, &$addr, &$port = null) {}
function socket_import_stream($stream) {}
function socket_last_error($socket = null) {}
function socket_listen($socket, $backlog = null) {}
function socket_read($socket, $length, $type = null) {}
function socket_recv($socket, &$buf, $len, $flags) {}
function socket_recvfrom($socket, &$buf, $len, $flags, &$name, &$port = null) {}
function socket_recvmsg($socket, &$msghdr, $flags) {}
function socket_select(&$read_fds, &$write_fds, &$except_fds, $tv_sec, $tv_usec = null) {}
function socket_send($socket, $buf, $len, $flags) {}
function socket_sendmsg($socket, $msghdr, $flags) {}
function socket_sendto($socket, $buf, $len, $flags, $addr, $port = null) {}
function socket_set_block($socket) {}
function socket_set_nonblock($socket) {}
function socket_set_option($socket, $level, $optname, $optval) {}
function socket_setopt($socket, $level, $optname, $optval) {}
function socket_shutdown($socket, $how = null) {}
function socket_strerror($errno) {}
function socket_write($socket, $buf, $length = null) {}
const AF_INET = 2;
const AF_INET6 = 10;
const AF_UNIX = 1;
const IPPROTO_IP = 0;
const IPPROTO_IPV6 = 41;
const IPV6_HOPLIMIT = 52;
const IPV6_MULTICAST_HOPS = 18;
const IPV6_MULTICAST_IF = 17;
const IPV6_MULTICAST_LOOP = 19;
const IPV6_PKTINFO = 50;
const IPV6_RECVHOPLIMIT = 51;
const IPV6_RECVPKTINFO = 49;
const IPV6_RECVTCLASS = 66;
const IPV6_TCLASS = 67;
const IPV6_UNICAST_HOPS = 16;
const IPV6_V6ONLY = 26;
const IP_MULTICAST_IF = 32;
const IP_MULTICAST_LOOP = 34;
const IP_MULTICAST_TTL = 33;
const MCAST_BLOCK_SOURCE = 43;
const MCAST_JOIN_GROUP = 42;
const MCAST_JOIN_SOURCE_GROUP = 46;
const MCAST_LEAVE_GROUP = 45;
const MCAST_LEAVE_SOURCE_GROUP = 47;
const MCAST_UNBLOCK_SOURCE = 44;
const MSG_CMSG_CLOEXEC = 1073741824;
const MSG_CONFIRM = 2048;
const MSG_CTRUNC = 8;
const MSG_DONTROUTE = 4;
const MSG_DONTWAIT = 64;
const MSG_EOF = 512;
const MSG_EOR = 128;
const MSG_ERRQUEUE = 8192;
const MSG_MORE = 32768;
const MSG_NOSIGNAL = 16384;
const MSG_OOB = 1;
const MSG_PEEK = 2;
const MSG_TRUNC = 32;
const MSG_WAITALL = 256;
const MSG_WAITFORONE = 65536;
const PHP_BINARY_READ = 2;
const PHP_NORMAL_READ = 1;
const SCM_CREDENTIALS = 2;
const SCM_RIGHTS = 1;
const SOCKET_E2BIG = 7;
const SOCKET_EACCES = 13;
const SOCKET_EADDRINUSE = 98;
const SOCKET_EADDRNOTAVAIL = 99;
const SOCKET_EADV = 68;
const SOCKET_EAFNOSUPPORT = 97;
const SOCKET_EAGAIN = 11;
const SOCKET_EALREADY = 114;
const SOCKET_EBADE = 52;
const SOCKET_EBADF = 9;
const SOCKET_EBADFD = 77;
const SOCKET_EBADMSG = 74;
const SOCKET_EBADR = 53;
const SOCKET_EBADRQC = 56;
const SOCKET_EBADSLT = 57;
const SOCKET_EBUSY = 16;
const SOCKET_ECHRNG = 44;
const SOCKET_ECOMM = 70;
const SOCKET_ECONNABORTED = 103;
const SOCKET_ECONNREFUSED = 111;
const SOCKET_ECONNRESET = 104;
const SOCKET_EDESTADDRREQ = 89;
const SOCKET_EDQUOT = 122;
const SOCKET_EEXIST = 17;
const SOCKET_EFAULT = 14;
const SOCKET_EHOSTDOWN = 112;
const SOCKET_EHOSTUNREACH = 113;
const SOCKET_EIDRM = 43;
const SOCKET_EINPROGRESS = 115;
const SOCKET_EINTR = 4;
const SOCKET_EINVAL = 22;
const SOCKET_EIO = 5;
const SOCKET_EISCONN = 106;
const SOCKET_EISDIR = 21;
const SOCKET_EISNAM = 120;
const SOCKET_EL2HLT = 51;
const SOCKET_EL2NSYNC = 45;
const SOCKET_EL3HLT = 46;
const SOCKET_EL3RST = 47;
const SOCKET_ELNRNG = 48;
const SOCKET_ELOOP = 40;
const SOCKET_EMEDIUMTYPE = 124;
const SOCKET_EMFILE = 24;
const SOCKET_EMLINK = 31;
const SOCKET_EMSGSIZE = 90;
const SOCKET_EMULTIHOP = 72;
const SOCKET_ENAMETOOLONG = 36;
const SOCKET_ENETDOWN = 100;
const SOCKET_ENETRESET = 102;
const SOCKET_ENETUNREACH = 101;
const SOCKET_ENFILE = 23;
const SOCKET_ENOANO = 55;
const SOCKET_ENOBUFS = 105;
const SOCKET_ENOCSI = 50;
const SOCKET_ENODATA = 61;
const SOCKET_ENODEV = 19;
const SOCKET_ENOENT = 2;
const SOCKET_ENOLCK = 37;
const SOCKET_ENOLINK = 67;
const SOCKET_ENOMEDIUM = 123;
const SOCKET_ENOMEM = 12;
const SOCKET_ENOMSG = 42;
const SOCKET_ENONET = 64;
const SOCKET_ENOPROTOOPT = 92;
const SOCKET_ENOSPC = 28;
const SOCKET_ENOSR = 63;
const SOCKET_ENOSTR = 60;
const SOCKET_ENOSYS = 38;
const SOCKET_ENOTBLK = 15;
const SOCKET_ENOTCONN = 107;
const SOCKET_ENOTDIR = 20;
const SOCKET_ENOTEMPTY = 39;
const SOCKET_ENOTSOCK = 88;
const SOCKET_ENOTTY = 25;
const SOCKET_ENOTUNIQ = 76;
const SOCKET_ENXIO = 6;
const SOCKET_EOPNOTSUPP = 95;
const SOCKET_EPERM = 1;
const SOCKET_EPFNOSUPPORT = 96;
const SOCKET_EPIPE = 32;
const SOCKET_EPROTO = 71;
const SOCKET_EPROTONOSUPPORT = 93;
const SOCKET_EPROTOTYPE = 91;
const SOCKET_EREMCHG = 78;
const SOCKET_EREMOTE = 66;
const SOCKET_EREMOTEIO = 121;
const SOCKET_ERESTART = 85;
const SOCKET_EROFS = 30;
const SOCKET_ESHUTDOWN = 108;
const SOCKET_ESOCKTNOSUPPORT = 94;
const SOCKET_ESPIPE = 29;
const SOCKET_ESRMNT = 69;
const SOCKET_ESTRPIPE = 86;
const SOCKET_ETIME = 62;
const SOCKET_ETIMEDOUT = 110;
const SOCKET_ETOOMANYREFS = 109;
const SOCKET_EUNATCH = 49;
const SOCKET_EUSERS = 87;
const SOCKET_EWOULDBLOCK = 11;
const SOCKET_EXDEV = 18;
const SOCKET_EXFULL = 54;
const SOCK_DGRAM = 2;
const SOCK_RAW = 3;
const SOCK_RDM = 4;
const SOCK_SEQPACKET = 5;
const SOCK_STREAM = 1;
const SOL_SOCKET = 1;
const SOL_TCP = 6;
const SOL_UDP = 17;
const SOMAXCONN = 128;
const SO_BINDTODEVICE = 25;
const SO_BROADCAST = 6;
const SO_DEBUG = 1;
const SO_DONTROUTE = 5;
const SO_ERROR = 4;
const SO_KEEPALIVE = 9;
const SO_LINGER = 13;
const SO_OOBINLINE = 10;
const SO_PASSCRED = 16;
const SO_RCVBUF = 8;
const SO_RCVLOWAT = 18;
const SO_RCVTIMEO = 20;
const SO_REUSEADDR = 2;
const SO_REUSEPORT = 15;
const SO_SNDBUF = 7;
const SO_SNDLOWAT = 19;
const SO_SNDTIMEO = 21;
const SO_TYPE = 3;
const TCP_NODELAY = 1;
}

View File

@ -0,0 +1,15 @@
<?php
// These stubs were generated by the phan stub generator.
// @phan-stub-for-extension tideways_xhprof@5.0.4
namespace {
function tideways_xhprof_disable() {}
function tideways_xhprof_enable($options = null) {}
const TIDEWAYS_XHPROF_FLAGS_CPU = 1;
const TIDEWAYS_XHPROF_FLAGS_MEMORY = 6;
const TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC = 16;
const TIDEWAYS_XHPROF_FLAGS_MEMORY_ALLOC_AS_MU = 48;
const TIDEWAYS_XHPROF_FLAGS_MEMORY_MU = 2;
const TIDEWAYS_XHPROF_FLAGS_MEMORY_PMU = 4;
const TIDEWAYS_XHPROF_FLAGS_NO_BUILTINS = 8;
}

View File

@ -0,0 +1,56 @@
<?php
/**
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*/
// phpcs:ignoreFile
/**
* @param string $text1
* @param string $text2
* @param int $numContextLines
* @param int $movedParagraphDetectionCutoff
* @return string
*/
function wikidiff2_do_diff( $text1, $text2, $numContextLines, $movedParagraphDetectionCutoff = 0 ) {
}
/**
* @param string $text1
* @param string $text2
* @param int $numContextLines
* @return string
*/
function wikidiff2_inline_diff( $text1, $text2, $numContextLines ) {
}
/**
* @param string $text1
* @param string $text2
* @param int $numContextLines
* @return string
*/
function wikidiff2_inline_json_diff( $text1, $text2, $numContextLines ) {
}
/**
* @param string $text1
* @param string $text2
* @param array $options
* @return array
*/
function wikidiff2_multi_format_diff( $text1, $text2, $options ) {
}

View File

@ -0,0 +1,6 @@
<?php
// Stub for PHP 8.2's AllowDynamicProperties class
#[Attribute]
class AllowDynamicProperties {
}

View File

@ -0,0 +1,13 @@
<?php
# For the purpose of phan, we're always core's DOM implementation.
class_alias( "DOMAttr", "Wikimedia\\Parsoid\\DOM\\Attr" );
class_alias( "DOMCharacterData", "Wikimedia\\Parsoid\\DOM\\CharacterData" );
class_alias( "DOMComment", "Wikimedia\\Parsoid\\DOM\\Comment" );
class_alias( "DOMDocument", "Wikimedia\\Parsoid\\DOM\\Document" );
class_alias( "DOMDocumentFragment", "Wikimedia\\Parsoid\\DOM\\DocumentFragment" );
class_alias( "DOMDocumentType", "Wikimedia\\Parsoid\\DOM\\DocumentType" );
class_alias( "DOMElement", "Wikimedia\\Parsoid\\DOM\\Element" );
class_alias( "DOMNode", "Wikimedia\\Parsoid\\DOM\\Node" );
class_alias( "DOMProcessingInstruction", "Wikimedia\\Parsoid\\DOM\\ProcessingInstruction" );
class_alias( "DOMText", "Wikimedia\\Parsoid\\DOM\\Text" );

View File

@ -0,0 +1,3 @@
These stubs describe how code that is not available at analysis time should be
used. No implementations are necessary, just define the classes and their
methods and use phpdoc to describe what arguments are allowed.

View File

@ -0,0 +1,5 @@
<?php
// Stub for PHP 8.1's Socket class
final class Socket {
}

143
mediawiki/.phpcs.xml Normal file
View File

@ -0,0 +1,143 @@
<?xml version="1.0"?>
<ruleset name="MediaWiki">
<rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
<exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationPrivate" />
<exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationProtected" />
<exclude name="MediaWiki.Commenting.FunctionComment.MissingDocumentationPublic" />
<exclude name="MediaWiki.Commenting.FunctionComment.WrongStyle" />
<exclude name="MediaWiki.NamingConventions.LowerCamelFunctionsName.FunctionName" />
<exclude name="MediaWiki.WhiteSpace.SpaceBeforeSingleLineComment.NewLineComment" />
</rule>
<rule ref="MediaWiki.NamingConventions.ValidGlobalName">
<properties>
<property name="ignoreList" type="array" value="$IP" />
</properties>
</rule>
<rule ref="MediaWiki.NamingConventions.ValidGlobalName.allowedPrefix">
<exclude-pattern>*/maintenance/doMaintenance\.php</exclude-pattern>
<exclude-pattern>*/maintenance/CommandLineInc\.php</exclude-pattern>
</rule>
<rule ref="Generic.Files.LineLength">
<exclude-pattern>*/languages/messages/*</exclude-pattern>
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="MediaWiki.Files.ClassMatchesFilename.NotMatch">
<!--
Continue to allow existing violations, but enable the sniff to prevent
any new occurrences.
-->
<exclude-pattern>*/maintenance/cleanupTitles\.php</exclude-pattern>
<exclude-pattern>*/maintenance/edit\.php</exclude-pattern>
<exclude-pattern>*/maintenance/findDeprecated\.php</exclude-pattern>
<exclude-pattern>*/maintenance/getText\.php</exclude-pattern>
<exclude-pattern>*/maintenance/importDump\.php</exclude-pattern>
<exclude-pattern>*/maintenance/install\.php</exclude-pattern>
<exclude-pattern>*/maintenance/jsparse\.php</exclude-pattern>
<exclude-pattern>*/maintenance/lag\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/StatOutputs\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/date-formats\.php</exclude-pattern>
<exclude-pattern>*/maintenance/mysql\.php</exclude-pattern>
<exclude-pattern>*/maintenance/parse\.php</exclude-pattern>
<exclude-pattern>*/maintenance/rebuildImages\.php</exclude-pattern>
<exclude-pattern>*/maintenance/renderDump\.php</exclude-pattern>
<exclude-pattern>*/maintenance/shell\.php</exclude-pattern>
<exclude-pattern>*/maintenance/sql\.php</exclude-pattern>
<exclude-pattern>*/maintenance/update\.php</exclude-pattern>
<exclude-pattern>*/maintenance/userOptions\.php</exclude-pattern>
<exclude-pattern>*/maintenance/view\.php</exclude-pattern>
<!-- Skip violations in some tests for now -->
<exclude-pattern>*/tests/parser/*</exclude-pattern>
<exclude-pattern>*/tests/phpunit/maintenance/*</exclude-pattern>
<exclude-pattern>*/tests/phpunit/bootstrap\.php</exclude-pattern>
<exclude-pattern>*/tests/phpunit/phpunit\.php</exclude-pattern>
</rule>
<rule ref="MediaWiki.Files.ClassMatchesFilename.WrongCase">
<!--
Continue to allow existing violations, but enable the sniff to prevent
any new occurrences.
-->
<exclude-pattern>*/maintenance/language/alltrans\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/digit2html\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/langmemusage\.php</exclude-pattern>
<exclude-pattern>*/maintenance/mctest\.php</exclude-pattern>
<exclude-pattern>*/maintenance/mwdocgen\.php</exclude-pattern>
<exclude-pattern>*/maintenance/rebuildall\.php</exclude-pattern>
<exclude-pattern>*/maintenance/rebuildmessages\.php</exclude-pattern>
<exclude-pattern>*/maintenance/rebuildrecentchanges\.php</exclude-pattern>
<exclude-pattern>*/maintenance/rebuildtextindex\.php</exclude-pattern>
<exclude-pattern>*/maintenance/storage/checkStorage\.php</exclude-pattern>
<exclude-pattern>*/maintenance/storage/recompressTracked\.php</exclude-pattern>
<exclude-pattern>*/maintenance/storage/trackBlobs\.php</exclude-pattern>
</rule>
<rule ref="Generic.PHP.NoSilencedErrors.Discouraged">
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="Generic.Files.OneObjectStructurePerFile.MultipleFound">
<!--
Continue to allow existing violations, but enable the sniff to prevent
any new occurrences.
-->
<exclude-pattern>*/maintenance/dumpIterator\.php</exclude-pattern>
<exclude-pattern>*/maintenance/findDeprecated\.php</exclude-pattern>
<exclude-pattern>*/maintenance/storage/recompressTracked\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/StatOutputs\.php</exclude-pattern>
<exclude-pattern>*/maintenance/language/generateCollationData\.php</exclude-pattern>
<!-- We don't care that much about violations in tests -->
<exclude-pattern>*/tests/*</exclude-pattern>
</rule>
<rule ref="MediaWiki.Usage.AssignmentInReturn.AssignmentInReturn">
<exclude-pattern>*/tests/phpunit/*</exclude-pattern>
</rule>
<rule ref="MediaWiki.Usage.ForbiddenFunctions.popen">
<!--
Continue to allow existing violations, but enable the sniff to prevent
any new occurrences.
-->
<exclude-pattern>*/includes/GlobalFunctions\.php</exclude-pattern>
<exclude-pattern>*/includes/libs/filebackend/FSFileBackend\.php</exclude-pattern>
<exclude-pattern>*/maintenance/includes/SevenZipStream\.php</exclude-pattern>
</rule>
<rule ref="MediaWiki.Usage.ForbiddenFunctions.proc_open">
<!--
Continue to allow existing violations, but enable the sniff to prevent
any new occurrences.
-->
<exclude-pattern>*/includes/export/DumpPipeOutput\.php</exclude-pattern>
<exclude-pattern>*/includes/ResourceLoader/Image\.php</exclude-pattern>
<exclude-pattern>*/includes/shell/Command\.php</exclude-pattern>
<exclude-pattern>*/maintenance/includes/TextPassDumper\.php</exclude-pattern>
<exclude-pattern>*/maintenance/mysql\.php</exclude-pattern>
<exclude-pattern>*/maintenance/storage/recompressTracked\.php</exclude-pattern>
<exclude-pattern>*/tests/parser/editTests\.php</exclude-pattern>
</rule>
<rule ref="MediaWiki.Usage.ForbiddenFunctions.shell_exec">
<!--
Continue to allow existing violations, but enable the sniff to prevent
any new occurrences.
-->
<exclude-pattern>*/maintenance/mwdocgen\.php</exclude-pattern>
<exclude-pattern>*/maintenance/updateCredits\.php</exclude-pattern>
</rule>
<rule ref="MediaWiki.Usage.ForbiddenFunctions.system">
<!--
Continue to allow existing violations, but enable the sniff to prevent
any new occurrences.
-->
<exclude-pattern>*/maintenance/mwdocgen\.php</exclude-pattern>
</rule>
<rule ref="Generic.Arrays.DisallowShortArraySyntax">
<!--
T273340: Rule not to be enabled on any other file.
PHPVersionCheck.php requires syntax to be old PHP compatible.
The rest should therefore use [] rather than array() as per the
MediaWiki style guide.
-->
<include-pattern>includes/PHPVersionCheck\.php</include-pattern>
</rule>
<file>.</file>
<arg name="encoding" value="UTF-8"/>
<arg name="extensions" value="php"/>
<exclude-pattern type="relative">^(extensions|skins)/*</exclude-pattern>
<exclude-pattern type="relative">^docs/(coverage|html|js|latex)/*</exclude-pattern>
<exclude-pattern>LocalSettings(-installer)?\.php</exclude-pattern>
</ruleset>

View File

@ -0,0 +1,11 @@
{
"extends": [
"stylelint-config-wikimedia/support-basic",
"stylelint-config-wikimedia/mediawiki"
],
"rules": {
"selector-class-pattern": "^((mw|oo-ui|cdx)-|(wikitable|(toc(|toggle|hidden))|client-(no)?js)$)",
"no-descending-specificity": null,
"selector-max-id": null
}
}

43
mediawiki/.svgo.config.js Normal file
View File

@ -0,0 +1,43 @@
/**
* SVGO Configuration
* Compatible to v3.0.0+
* Recommended options from:
* https://www.mediawiki.org/wiki/Manual:Coding_conventions/SVG#Exemplified_safe_configuration
*/
'use strict';
module.exports = {
plugins: [
{
// Set of built-in plugins enabled by default.
name: 'preset-default',
params: {
overrides: {
cleanupIds: false,
removeDesc: false,
removeTitle: false,
removeViewBox: false,
// If the SVG doesn't start with an XML declaration, then its MIME type will
// be detected as "text/plain" rather than "image/svg+xml" by libmagic and,
// consequently, MediaWiki's CSSMin CSS minifier. libmagic's default database
// currently requires that SVGs contain an XML declaration:
// https://github.com/threatstack/libmagic/blob/master/magic/Magdir/sgml#L5
removeXMLProcInst: false
}
}
},
'removeRasterImages',
'sortAttrs'
],
// Set whitespace according to Wikimedia Coding Conventions.
// @see https://github.com/svg/svgo/blob/main/lib/stringifier.js#L39 for available options.
js2svg: {
eol: 'lf',
finalNewline: true,
// Configure the indent to tabs (default 4 spaces) used by `--pretty` here.
indent: '\t',
pretty: true
},
multipass: true
};

12
mediawiki/.vsls.json Normal file
View File

@ -0,0 +1,12 @@
{
"$schema": "http://json.schemastore.org/vsls",
"gitignore": "hide",
"excludeFiles": [
"!node_modules",
"!vendor"
],
"hideFiles": [
"!node_modules",
"!vendor"
]
}

View File

@ -0,0 +1,4 @@
Code of Conduct
===============
The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct).

378
mediawiki/COPYING Normal file
View File

@ -0,0 +1,378 @@
== License and copyright information ==
=== License ===
MediaWiki is licensed under the terms of the GNU General Public License,
version 2 or later. Derivative works and later versions of the code must be
free software licensed under the same or a compatible license. This includes
"extensions" that use MediaWiki functions or variables; see
https://www.gnu.org/licenses/gpl-faq.html#GPLAndPlugins for details.
For the full text of version 2 of the license, see
https://www.gnu.org/licenses/gpl-2.0.html or '''GNU General Public License'''
below.
=== Copyright owners ===
MediaWiki contributors, including those listed in the CREDITS file, hold the
copyright to this work.
=== Additional license information ===
Some components of MediaWiki imported from other projects may be under other
Free and Open Source, or Free Culture, licenses. Specific details of their
licensing information can be found in those components.
Sections of code written exclusively by Lee Crocker or Erik Moeller are also
released into the public domain, which does not impair the obligations of users
under the GPL for use of the whole code or other sections thereof.
MediaWiki uses the following Creative Commons icons to illustrate links to the
CC licenses:
* resources/assets/licenses/cc-0.png
* resources/assets/licenses/cc-by-nc-sa.png
* resources/assets/licenses/cc-by-sa.png
* resources/assets/licenses/cc-by.png
These icons are trademarked, and used subject to the CC trademark license,
available at https://creativecommons.org/policies#trademark
== GNU GENERAL PUBLIC LICENSE ==
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
<https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
=== Preamble ===
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
== TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ==
'''0.''' This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
'''1.''' You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
'''2.''' You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
'''a)''' You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
'''b)''' You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
'''c)''' If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
'''3.''' You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
'''a)''' Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
'''b)''' Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
'''c)''' Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
'''4.''' You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
'''5.''' You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
'''6.''' Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
'''7.''' If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
'''8.''' If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
'''9.''' The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
'''10.''' If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
=== NO WARRANTY ===
'''11.''' BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
'''12.''' IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
'''END OF TERMS AND CONDITIONS'''
== How to Apply These Terms to Your New Programs ==
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Moe Ghoul>, 1 April 1989
Moe Ghoul, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

1077
mediawiki/CREDITS Normal file

File diff suppressed because it is too large Load Diff

464
mediawiki/DEVELOPERS.md Normal file
View File

@ -0,0 +1,464 @@
# MediaWiki Developers
Welcome to the MediaWiki community! Please see [How to become a MediaWiki
hacker](https://www.mediawiki.org/wiki/How_to_become_a_MediaWiki_hacker) for
general information on contributing to MediaWiki.
## Development environment
MediaWiki provides an extendable local development environment based on Docker Compose. This environment provides PHP,
Apache, Xdebug and a SQLite database.
**Do not use the development environment to serve a public website! Bad things would happen!**
More documentation, examples, and configuration recipes are available at [mediawiki.org/wiki/MediaWiki-Docker][mw-docker].
Support is available on the [Libera IRC network][libera-home] in the [#mediawiki channel][libera-webchat], and on
Phabricator by creating tasks with the [MediaWiki-Docker][mw-docker-phab] tag.
[mw-docker]: https://www.mediawiki.org/wiki/MediaWiki-Docker
[mw-docker-phab]: https://phabricator.wikimedia.org/tag/mediawiki-docker/
[libera-home]: https://libera.chat/
[libera-webchat]: https://web.libera.chat/#mediawiki
## Quickstart
### 1. Requirements
You'll need to have Docker installed:
* [Docker Desktop][docker-install] for macOS or Windows.
* [Docker engine][docker-linux] for Linux.
[docker-install]: https://docs.docker.com/get-docker/
[docker-linux]: https://docs.docker.com/engine/install/
**Linux users**:
* We recommend installing `docker-ce`, `docker-ce-cli`, `containerd.io`, and `docker-compose-plugin` by [downloading the server
releases][dc-release] for your distribution rather than Docker Desktop. You can also install the [binaries][dc-binaries].
* Follow the instructions to ["Manage Docker as a non-root user"][dc-non-root]
[dc-release]: https://docs.docker.com/engine/install/
[dc-binaries]: https://docs.docker.com/engine/install/binaries/
[dc-non-root]: https://docs.docker.com/install/linux/linux-postinstall/#manage-docker-as-a-non-root-user
**Windows users**:
Running Docker from a Windows terminal and using the Windows file system will result in MediaWiki being very slow. For Windows 10 and higher, we recommend configuring Docker and Windows to use the [Windows Subsystem for Linux (WSL)](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux). Turn on WSL in your Windows settings, then run the following commands: `wsl --install -d ubuntu` and `wsl --set-version ubuntu 2`. Then go into Docker -> Settings -> General -> tick "Use the WSL 2 based engine", then go into Docker -> Settings -> Resources -> WSL Integration -> tick "Ubuntu". `git clone` the mediawiki repository into a WSL folder such as `home/yourusername/mediawiki` so that the files are inside WSL. Then you can run most of the commands in this tutorial outside of WSL, by opening PowerShell, navigating to the WSL directory with `cd \\wsl.localhost\Ubuntu\home\yourusername\mediawiki`, and executing shell commands as normal. To access WSL from PowerShell (rare but may be needed sometimes), you can use the command `ubuntu` to turn a PowerShell console into a WSL console. To navigate to WSL folders in [File Explorer](https://en.wikipedia.org/wiki/File_Explorer), show the Navigation Pane, then towards the bottom, look for "Linux" (it will be close to "This PC").
**Mac users**:
If you're using Docker Desktop and have the `Use Rosetta for x86/amd64 emulation on Apple Silicon` setting enabled, you may encounter an issue where loading the wiki results in a blank page or a 503 error. To resolve this, disable the setting and restart Docker Desktop. See [this page](https://phabricator.wikimedia.org/P49617) for more information.
### 2. Download MediaWiki files
Download the latest MediaWiki files to your computer. One way to download the latest alpha version of MediaWiki is to
[install git](https://git-scm.com/), open a shell, navigate to the directory where you want to save the files, then type
`git clone https://gerrit.wikimedia.org/r/mediawiki/core.git mediawiki`.
Optional: If you plan to submit patches to this repository, you will probably want to [create a Gerrit account](https://wikitech.wikimedia.org/wiki/Help:Create_a_Wikimedia_developer_account),
then type `git remote set-url origin ssh://YOUR-GERRIT-USERNAME-HERE@gerrit.wikimedia.org:29418/mediawiki/core`,
replacing YOUR-GERRIT-USERNAME-HERE with your Gerrit username. Please see the official
[MediaWiki Gerrit tutorial](https://www.mediawiki.org/wiki/Gerrit/Tutorial) for more information.
### 3. Prepare `.env` file
Using a text editor, create a `.env` file in the root of the MediaWiki core repository, and copy these contents into
that file:
```sh
MW_SCRIPT_PATH=/w
MW_SERVER=http://localhost:8080
MW_DOCKER_PORT=8080
MEDIAWIKI_USER=Admin
MEDIAWIKI_PASSWORD=dockerpass
XDEBUG_CONFIG=
XDEBUG_ENABLE=true
XHPROF_ENABLE=true
```
Windows users: Run the following command to add a blank user ID and group ID to your `.env` file:
```sh
echo "
MW_DOCKER_UID=
MW_DOCKER_GID=" >> .env
```
Non-Windows users: Run the following command to add your user ID and group ID to your `.env` file:
```sh
echo "MW_DOCKER_UID=$(id -u)
MW_DOCKER_GID=$(id -g)" >> .env
```
Linux users: If you'd like to use Xdebug features inside your IDE, then create a `docker-compose.override.yml` file as
well:
```yaml
services:
mediawiki:
# For Linux: This extra_hosts section enables Xdebug-IDE communication:
extra_hosts:
- "host.docker.internal:host-gateway"
```
### 4. Create the environment
* Start the containers:
```sh
docker compose up -d
```
The "up" command makes sure that the PHP and webserver containers are running (and any others in the
`docker-compose.yml` file). It is safe to run at any time, and will do nothing if the containers are already running.
The first time, it may take a few minutes to download new Docker images.
The `-d` argument stands for "detached" mode, which run the services in the background. If you suspect a problem with
one of the services, you can run it without `-d` to follow the server logs directly from your terminal. You don't have
to stop the services first, if you ran it with `-d` and then without, you'll get connected to the already running
containers including a decent back scroll of server logs.
Note that MediaWiki debug logs go to `/cache/*.log` files (not sent to docker).
* Install PHP dependencies from Composer:
```sh
docker compose exec mediawiki composer update
```
* Install MediaWiki:
```sh
docker compose exec mediawiki /bin/bash /docker/install.sh
```
Windows users: make sure you run the above command in PowerShell as it does not work in Bash.
* Windows users: Make sure to set the SQLite directory to be writable.
```sh
docker compose exec mediawiki chmod -R o+rwx cache/sqlite
```
Done! The wiki should now be available for you at <http://localhost:8080>.
## Usage
### Running commands
You can use `docker compose exec mediawiki bash` to open a Bash shell in the
MediaWiki container. You can then run one or more commands as needed and stay
within the container shell.
You can also run a single command in the container directly from your host
shell, for example: `docker compose exec mediawiki php maintenance/run.php update`.
### PHPUnit
Run a single PHPUnit file or directory:
```sh
docker compose exec mediawiki bash
instance:/w$ cd tests/phpunit
instance:/w/tests/phpunit$ composer phpunit -- path/to/my/test/
```
See [PHPUnit on mediawiki.org][phpunit-testing] for more examples.
[phpunit-testing]: https://www.mediawiki.org/wiki/Manual:PHP_unit_testing/Running_the_tests
### Selenium
You can use [Fresh][fresh] to run [Selenium in a dedicated
container][selenium-dedicated]. Example usage:
```sh
fresh-node -env -net
npm ci
npm run selenium-test
```
[selenium-dedicated]: https://www.mediawiki.org/wiki/Selenium/Getting_Started/Run_tests_using_Fresh
### API Testing
You can use [Fresh][fresh] to run [API tests in a dedicated
container][api-dedicated]. Example usage:
```sh
export MW_SERVER=http://localhost:8080/
export MW_SCRIPT_PATH=/w
export MEDIAWIKI_USER=Admin
export MEDIAWIKI_PASSWORD=dockerpass
fresh-node -env -net
# Create .api-testing.config.json as documented on
# https://www.mediawiki.org/wiki/MediaWiki_API_integration_tests
npm ci
npm run api-testing
```
[fresh]: https://github.com/wikimedia/fresh
[api-dedicated]: https://www.mediawiki.org/wiki/MediaWiki_API_integration_tests
## Modify the development environment
You can override the default services from a `docker-compose.override.yml`
file, and make use of those overrides by changing `LocalSettings.php`.
Example overrides and configurations can be found under
[MediaWiki-Docker on mediawiki.org][mw-docker].
After updating `docker-compose.override.yml`, run `docker compose down`
followed by `docker compose up -d` for changes to take effect.
### Install extra packages
If you need root on the container to install system packages with `apt-get` for
troubleshooting, you can open a shell as root with
`docker compose exec --user root mediawiki bash`.
### Install extensions and skins
To install an extension or skin, follow the instructions of the mediawiki.org
page for the extension or skin in question, and look for any dependencies
or additional steps that may be needed.
For most extensions, only two steps are needed: download the code to the
right directory, and then enable the component from `LocalSettings.php`.
To install the Vector skin:
1. Clone the skin:
```sh
cd skins/
git clone https://gerrit.wikimedia.org/r/mediawiki/skins/Vector
```
2. Enable the skin, by adding the following to `LocalSettings.php`:
```php
wfLoadSkin( 'Vector' );
```
To install the EventLogging extension:
1. Clone the extension repository:
```sh
cd extensions/
git clone https://gerrit.wikimedia.org/r/mediawiki/extensions/EventLogging
```
Alternatively, if you have extension repositories elsewhere on disk, mount each one as an overlapping volume in
`docker-compose.override.yml`. This is comparable to a symlink, but those are not well-supported in Docker.
```yaml
services:
mediawiki:
volumes:
- ~/Code/EventLogging:/var/www/html/w/extensions/EventLogging:cached
```
2. Enable the extension, by adding the following to `LocalSettings.php`:
```php
wfLoadExtension( 'EventLogging' );
```
### Xdebug
By default, you will need to set `XDEBUG_TRIGGER=1` in the GET/POST, or as an
environment variable, to turn on Xdebug for a request.
You can also install a browser extension for controlling whether Xdebug is
active. See the [official Xdebug Step Debugging][step-debug], particularly the
"Activating Step Debugging" section, for more details.
[step-debug]: https://xdebug.org/docs/step_debug
If you wish to run Xdebug on every request, you can set
`start_with_request=yes` in `XDEBUG_CONFIG` in your .env file:
```
XDEBUG_CONFIG=start_with_request=yes
```
You can pass any of Xdebug's configuration values in this variable. For example:
```
XDEBUG_CONFIG=client_host=192.168.42.34 client_port=9000 log=/tmp/xdebug.log
```
This shouldn't be necessary for basic use cases, but see [the Xdebug settings
documentation](https://xdebug.org/docs/all_settings) for available settings.
### Codex
You can use your local version of Codex instead of the one bundled with MediaWiki. This is useful
when testing how changes in Codex affect Codex-based features in MediaWiki.
1. Clone the Codex repository and build Codex, if you haven't done this already:
```sh
cd ../
git clone https://gerrit.wikimedia.org/r/design/codex
cd codex
npm run build-all
```
2. If your clone of the Codex repository is outside of the MediaWiki directory (this is common),
add the following to your `docker-compose.override.yml`. Replace `~/git/codex` with the path to
your Codex clone.
```yaml
services:
mediawiki:
volumes:
- ~/git/codex:/var/www/html/w/codex:cached
```
3. To apply the change to `docker-compose.override.yml`, you have to recreate the environment:
```sh
docker compose down
docker compose up -d
```
4. Enable Codex development mode by adding the following to the bottom of `LocalSettings.php`:
```php
$wgCodexDevelopmentDir = MW_INSTALL_PATH . '/codex';
```
To disable Codex development mode and use the regular version of Codex, delete or comment out
this line.
5. Every time you make a change to your local copy of Codex (or download a Gerrit change), you
have to rerun Codex's build process for these changes to take effect. To do this, run
`npm run build-all` in the Codex directory.
### Stop or recreate environment
Stop the environment, perhaps to reduce the load when working on something
else. This preserves the containers, to be restarted later quickly with
the `docker compose up -d` command.
```sh
docker compose stop
```
Destroy and re-create the environment. This will delete the containers,
including any logs, caches, and other modifications you may have made
via the shell.
```sh
docker compose down
docker compose up -d
```
### Re-install the database
To empty the wiki database and re-install it:
* Remove or rename the `LocalSettings.php` file.
* Delete the `cache/sqlite` directory.
* Re-run the "Install MediaWiki database" command.
You can now restore or copy over any modifications you had in your previous `LocalSettings.php` file.
And if you have any additional extensions installed that required a database table, then also run:
`docker compose exec mediawiki php maintenance/run.php update`.
## Troubleshooting
### Caching
If you suspect a change is not applying due to caching, start by [hard
refreshing](https://en.wikipedia.org/wiki/Wikipedia:Bypass_your_cache) the browser.
If that doesn't work, you can narrow it down by disabling various server-side
caching layers in `LocalSettings.php`, as follows:
```php
$wgMainCacheType = CACHE_NONE;
$wgMessageCacheType = CACHE_NONE;
$wgParserCacheType = CACHE_NONE;
$wgResourceLoaderMaxage = [
'versioned' => 0,
'unversioned' => 0
];
```
The default settings of MediaWiki are such that caching is smart and changes
propagate immediately. Using the above settings may slow down your wiki
significantly. Especially on macOS and Windows, where Docker Desktop uses
a VM internally and thus has longer file access times.
See [Manual:Caching][manual-caching] on mediawiki.org for more information.
[manual-caching]: https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Caching
### Xdebug ports
Older versions of Xdebug used port 9000, which could conflict with php-fpm
running on the host. This document used to recommend a workaround of telling
your IDE to listen on a different port (e.g., 9009) and setting
`XDEBUG_CONFIG=remote_port=9009` in your `.env`.
Xdebug 3.x now uses the `client_port` value, which defaults to 9003. This
should no longer conflict with local php-fpm installations, but you may need
to change the settings in your IDE or debugger.
### Linux desktop, host not found
The image uses `host.docker.internal` as the `client_host` value which
should allow Xdebug work for Docker for Mac/Windows.
On Linux, you need to create a `docker-compose.override.yml` file with the following
contents:
```yaml
services:
mediawiki:
extra_hosts:
- "host.docker.internal:host-gateway"
```
With the latest version of Docker on Linux hosts, this _should_ work
transparently as long as you're using the recommended
`docker-compose.override.yml`. If it doesn't, first check `docker version` to
make sure you're running Docker 20.10.2 or above, and `docker compose version`
to make sure it's 1.27.4 or above.
If Xdebug still doesn't work, try specifying the hostname or IP address of your
host. The IP address works more reliably. You can obtain it by running e.g.
`ip -4 addr show docker0` and copying the IP address into the config in `.env`,
like `XDEBUG_CONFIG=remote_host=172.17.0.1`
### Generating logs
Switching on the remote log for Xdebug comes at a performance cost so only
use it while troubleshooting. You can enable it like so: `XDEBUG_CONFIG=remote_log=/tmp/xdebug.log`
### "(Permission Denied)" errors on running docker compose
See if you're able to run any docker commands to start with. Try running
`docker container ls`, which should also throw a permission error. If not,
go through the following steps to get access to the socket that the docker
client uses to talk to the daemon.
`sudo usermod -aG docker $USER`
And then `relogin` (or `newgrp docker`) to re-login with the new group membership.
### "(Cannot access the database: Unknown error (localhost))"
The environment's working directory has recently changed to `/var/www/html/w`.
Reconfigure this in your `LocalSettings.php` by ensuring that the following
values are set correctly:
```php
$wgScriptPath = '/w';
$wgSQLiteDataDir = "/var/www/html/w/cache/sqlite";
```
### Windows users, "(Cannot access the database: No database connection (unknown))"
The permissions with the `cache/sqlite` directory have to be set manually on Windows:
```sh
docker compose exec mediawiki chmod -R o+rwx cache/sqlite
```
### Linux users, "(Cannot access the database: No database connection (localhost))"
Make sure you have the `MW_DOCKER_UID` and `MW_DOCKER_GID` set in your `.env` file (instructions above).

2
mediawiki/FAQ Normal file
View File

@ -0,0 +1,2 @@
The MediaWiki FAQ can be found at:
https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ

165
mediawiki/Gruntfile.js Normal file
View File

@ -0,0 +1,165 @@
'use strict';
module.exports = function ( grunt ) {
grunt.loadNpmTasks( 'grunt-banana-checker' );
grunt.loadNpmTasks( 'grunt-eslint' );
grunt.loadNpmTasks( 'grunt-karma' );
grunt.loadNpmTasks( 'grunt-stylelint' );
const fs = require( 'fs' );
const wgServer = process.env.MW_SERVER;
const wgScriptPath = process.env.MW_SCRIPT_PATH;
const karmaProxy = {};
let qunitURL = wgServer + wgScriptPath + '/index.php?title=Special:JavaScriptTest/qunit/export';
// "MediaWiki" for core, or extension/skin name (e.g. "GrowthExperiments")
const qunitComponent = grunt.option( 'qunit-component' );
const qunitWatch = grunt.option( 'qunit-watch' ) || false;
const qunitWatchFiles = [];
if ( qunitComponent ) {
qunitURL = qunitURL + '&component=' + qunitComponent;
}
if ( qunitWatch ) {
if ( !qunitComponent || qunitComponent === 'MediaWiki' ) {
// MediaWiki core
qunitWatchFiles.push( 'tests/qunit/**' );
qunitWatchFiles.push( 'resources/**' );
} else {
// one extension or skin
const extPath = __dirname + '/extensions/' + qunitComponent + '/extension.json';
const skinPath = __dirname + '/skins/' + qunitComponent + '/skin.json';
// eslint-disable-next-line security/detect-non-literal-fs-filename
if ( fs.existsSync( extPath ) ) {
qunitWatchFiles.push( 'extensions/' + qunitComponent + '/extension.json' );
qunitWatchFiles.push( 'extensions/' + qunitComponent + '/{modules,resources,tests}/**' );
}
// eslint-disable-next-line security/detect-non-literal-fs-filename
if ( fs.existsSync( skinPath ) ) {
qunitWatchFiles.push( 'skins/' + qunitComponent + '/skin.json' );
qunitWatchFiles.push( 'skins/' + qunitComponent + '/{modules,resources,tests}/**' );
}
}
}
karmaProxy[ wgScriptPath ] = {
target: wgServer + wgScriptPath,
changeOrigin: true
};
grunt.initConfig( {
eslint: {
options: {
extensions: [ '.js', '.json', '.vue' ],
cache: true,
fix: grunt.option( 'fix' )
},
all: '.'
},
banana: {
options: {
requireLowerCase: false,
disallowBlankTranslations: false
},
core: 'languages/i18n/',
codex: 'languages/i18n/codex/',
exif: 'languages/i18n/exif/',
preferences: 'languages/i18n/preferences/',
api: 'includes/api/i18n/',
rest: 'includes/Rest/i18n/',
installer: 'includes/installer/i18n/',
paramvalidator: 'includes/libs/ParamValidator/i18n/'
},
stylelint: {
options: {
reportNeedlessDisables: true,
cache: true
},
resources: 'resources/src/**/*.{css,less,vue}',
config: 'mw-config/**/*.css'
},
watch: {
files: [
'.{stylelintrc,eslintrc}.json',
'**/*',
'!{extensions,node_modules,skins,vendor}/**'
],
tasks: 'test'
},
karma: {
options: {
plugins: [
'@wikimedia/karma-firefox-launcher',
'karma-*'
],
customLaunchers: {
ChromeCustom: {
base: 'ChromeHeadless',
// Chrome requires --no-sandbox in Docker/CI.
// WMF CI images expose CHROMIUM_FLAGS which sets that.
flags: process.env.CHROMIUM_FLAGS ? ( process.env.CHROMIUM_FLAGS || '' ).split( ' ' ) : []
}
},
proxies: karmaProxy,
files: [ {
pattern: qunitURL,
type: 'js',
watched: false,
included: true,
served: false
}, ...qunitWatchFiles.map( ( file ) => ( {
pattern: file,
type: 'js',
watched: true,
included: false,
served: false
} ) ) ],
logLevel: ( process.env.ZUUL_PROJECT ? 'DEBUG' : 'INFO' ),
frameworks: [ 'qunit' ],
// Disable autostart because we load modules asynchronously.
client: {
qunit: {
autostart: false
}
},
reporters: [ 'mocha' ],
singleRun: !qunitWatch,
autoWatch: qunitWatch,
// Some tests in extensions don't yield for more than the default 10s (T89075)
browserNoActivityTimeout: 60 * 1000,
// Karma requires Same-Origin (or CORS) by default since v1.1.1
// for better stacktraces. But we load the first request from wgServer
crossOriginAttribute: false
},
main: {
browsers: [ 'FirefoxHeadless' ]
},
firefox: {
browsers: [ 'FirefoxHeadless' ]
},
chrome: {
browsers: [ 'ChromeCustom' ]
}
}
} );
grunt.registerTask( 'assert-mw-env', () => {
let ok = true;
if ( !process.env.MW_SERVER ) {
grunt.log.error( 'Environment variable MW_SERVER must be set.\n' +
'Set this like $wgServer, e.g. "http://localhost"'
);
ok = false;
}
// MW_SCRIPT_PATH= empty string is valid, e.g. for docroot installs
// This includes "composer serve" (Quickstart)
if ( process.env.MW_SCRIPT_PATH === undefined ) {
grunt.log.error( 'Environment variable MW_SCRIPT_PATH must be set.\n' +
'Set this like $wgScriptPath, e.g. "/w"' );
ok = false;
}
return ok;
} );
grunt.registerTask( 'lint', [ 'eslint', 'banana', 'stylelint' ] );
grunt.registerTask( 'qunit', [ 'assert-mw-env', 'karma:firefox' ] );
};

31225
mediawiki/HISTORY Normal file

File diff suppressed because it is too large Load Diff

103
mediawiki/INSTALL Normal file
View File

@ -0,0 +1,103 @@
---
Installing MediaWiki
---
It is possible to install and configure the wiki "in-place", as long as you have
the necessary prerequisites available.
Required software as of MediaWiki 1.43.0:
* Web server with PHP 8.1.0 or higher, plus the following extensions:
** ctype
** dom
** fileinfo
** iconv
** intl
** json
** libxml
** mbstring
** openssl
** xml
** xmlreader
* A SQL server, the following types are supported
** MariaDB 10.3 or higher
** MySQL 5.7.0 or higher
** PostgreSQL 10 or higher
** SQLite 3.8.0 or higher
In addition, either the bcmath or gmp PHP extension is required on 32-bit
systems.
MediaWiki is developed and tested mainly on Unix/Linux platforms, but should
work on Windows as well.
Support for specialised content requires installing the relevant extension. For
formulæ, see https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:Math
Don't forget to check the RELEASE-NOTES file...
Additional documentation is available online, which may include more detailed
notes on particular operating systems and workarounds for difficult hosting
environments:
https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Installing_MediaWiki
******************* WARNING *******************
REMEMBER: ALWAYS BACK UP YOUR DATABASE BEFORE
ATTEMPTING TO INSTALL OR UPGRADE!!!
******************* WARNING *******************
----
In-place Web install
----
Decompress the MediaWiki installation archive either on your server, or on your
local machine and upload the directory tree. Rename it from "mediawiki-1.x.x" to
something nice, like "wiki", since it will be appearing in your URL,
ie. /wiki/index.php/Article.
+--------------------------------------------------------------------------+
| Note: If you plan to use a fancy URL-rewriting scheme to prettify your |
| URLs, such as http://www.example.com/wiki/Article, you should put the |
| files in a *different* directory from the virtual path where page names |
| will appear. It is common in this case to use w as the folder name and |
| /wiki/ as the virtual article path where your articles pretend to be. |
| |
| See: https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Short_URL |
+--------------------------------------------------------------------------+
Hop into your browser and surf into the wiki directory. It'll direct you into
the config script. Fill out the form... remember you're probably not on an
encrypted connection.
Gaaah! :)
If all goes well, you should soon be told that it's set up your wiki database
and generated a configuration file. There is now a copy of "LocalSettings.php"
available to download from the installer. Download this now, there is not a
way (yet) to get it after you exit the installer. Place it in the main wiki
directory, and the wiki should now be working.
Once the wiki is set up, you should remove the mw-config directory (though it
will refuse to config again if the wiki is set up).
----
Don't forget that this is free software under development! Chances are good
there's a crucial step that hasn't made it into the documentation. You should
probably sign up for the MediaWiki developers' mailing list; you can ask for
help (please provide enough information to work with, and preferably be aware of
what you're doing!) and keep track of major changes to the software, including
performance improvements and security patches.
https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/
(low traffic)
https://lists.wikimedia.org/postorius/lists/mediawiki-l.lists.wikimedia.org/
(site admin support)
https://lists.wikimedia.org/postorius/lists/wikitech-l.lists.wikimedia.org/
(development)

35
mediawiki/README.md Normal file
View File

@ -0,0 +1,35 @@
# MediaWiki
MediaWiki is a free and open-source wiki software package written in PHP. It
serves as the platform for Wikipedia and the other Wikimedia projects, used
by hundreds of millions of people each month. MediaWiki is localised in over
350 languages and its reliability and robust feature set have earned it a large
and vibrant community of third-party users and developers.
MediaWiki is:
* feature-rich and extensible, both on-wiki and with hundreds of extensions;
* scalable and suitable for both small and large sites;
* simple to install, working on most hardware/software combinations; and
* available in your language.
For system requirements, installation, and upgrade details, see the files
RELEASE-NOTES, INSTALL, and UPGRADE.
* Ready to get started?
* https://www.mediawiki.org/wiki/Special:MyLanguage/Download
* Setting up your local development environment?
* https://www.mediawiki.org/wiki/Local_development_quickstart
* Looking for the technical manual?
* https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents
* Seeking help from a person?
* https://www.mediawiki.org/wiki/Special:MyLanguage/Communication
* Looking to file a bug report or a feature request?
* https://bugs.mediawiki.org/
* Interested in helping out?
* https://www.mediawiki.org/wiki/Special:MyLanguage/How_to_contribute
MediaWiki is the result of global collaboration and cooperation. The CREDITS
file lists technical contributors to the project. The COPYING file explains
MediaWiki's copyright and license (GNU General Public License, version 2 or
later). Many thanks to the Wikimedia community for testing and suggestions.

1663
mediawiki/RELEASE-NOTES-1.43 Normal file

File diff suppressed because it is too large Load Diff

3
mediawiki/SECURITY Normal file
View File

@ -0,0 +1,3 @@
MediaWiki takes security very seriously. If you believe you have found a
security issue, see <https://www.mediawiki.org/wiki/Reporting_security_bugs>
for information on how to responsibly report it.

101
mediawiki/UPGRADE Normal file
View File

@ -0,0 +1,101 @@
This file provides an overview of the MediaWiki upgrade process. For help with
specific problems, you should check:
* the docs at https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents ;
* the mediawiki-l mailing list archive at
https://lists.wikimedia.org/hyperkitty/list/mediawiki-l@lists.wikimedia.org/ ;
and
* the bug tracker at https://phabricator.wikimedia.org/
… for information and workarounds to common issues.
== Overview ==
We provide comprehensive documentation on upgrading to the latest version of the
software at https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Upgrading.
Important: Upgrading from releases older than two LTS release is not supported anymore.
If you want to upgrade from an old version, please upgrade to a more recent LTS version first,
then to this version.
Currently any upgrade from any version older than 1.35 will fail.
=== Consult the release notes ===
Before doing anything, stop and consult the release notes supplied with the new
version of the software. These detail bug fixes, new features and functionality,
and any particular points that may need to be noted during the upgrade process.
=== Backup first ===
It is imperative that, prior to attempting an upgrade of the database schema,
you take a complete backup of your wiki database and files and verify it. While
the upgrade scripts are somewhat robust, there is no guarantee that things will
not fail, leaving the database in an inconsistent state.
https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Backing_up_a_wiki is an
overview of the backup process. You should also refer to the documentation for
your database management system for information on backing up a database, and to
your operating system documentation for information on making copies of files.
=== Perform the file upgrade ===
Download the files for the new version of the software. These are available as a
compressed "tar" archive from the Wikimedia Download Service
(https://releases.wikimedia.org/mediawiki/).
You can also obtain the new files directly from our Git source code repository.
Replace the existing MediaWiki files with the new. You should preserve the
LocalSettings.php file and the "extensions" and "images" directories.
Depending upon your configuration, you may also need to preserve additional
directories, including a custom upload directory ($wgUploadDirectory),
deleted file archives, and any custom skins.
=== Perform the database upgrade ===
As of 1.21, it is possible to separate schema changes (i.e. adding, dropping, or
changing tables, fields, or indices) from all other database changes (e.g.
populating fields). If you need this capability, see "From the command line"
below.
==== From the Web ====
If you browse to the Web-based installation script (usually at
./mw-config/index.php) from your wiki installation you can follow the script and
upgrade your database in place.
==== From the command line ====
From the command line, browse to the "maintenance" directory and use
`run.php update` to check and update the schema. This will insert missing
tables, update existing tables, and move data around as needed. In most cases,
this is successful and nothing further needs to be done.
If you need to separate out the schema changes so they can be run by someone
with more privileges, then you can use the --schema option to produce a text
file with the necessary commands. You can use --schema, --noschema,
$wgAllowSchemaUpdates as well as proper database permissions to enforce this
separation.
=== Check configuration settings ===
The names of configuration variables, and their default values and purposes, can
change between release branches, e.g. $wgDisableUploads in 1.4 is replaced with
$wgEnableUploads in later versions. When upgrading, consult the release notes to
check for configuration changes which would alter the expected behavior of
MediaWiki.
=== Check installed extensions ===
Extensions usually need to be upgraded at the same time as the MediaWiki core.
=== Test ===
It makes sense to test your wiki immediately following any kind of maintenance
procedure, and especially after upgrading; check that page views and edits work
normally, that special pages continue to function, etc., and correct any errors
and quirks which reveal themselves.
You should also test any extensions, and upgrade these if necessary.

44
mediawiki/api.php Normal file
View File

@ -0,0 +1,44 @@
<?php
/**
* The web entry point for all %Action API queries, handled by ApiMain
* and ApiBase subclasses.
*
* @see ApiEntryPoint The corresponding entry point class
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* http://www.gnu.org/copyleft/gpl.html
*
* @file
* @ingroup entrypoint
* @ingroup API
*/
use MediaWiki\Api\ApiEntryPoint;
use MediaWiki\Context\RequestContext;
use MediaWiki\EntryPointEnvironment;
use MediaWiki\MediaWikiServices;
// So extensions (and other code) can check whether they're running in API mode
define( 'MW_API', true );
define( 'MW_ENTRY_POINT', 'api' );
require __DIR__ . '/includes/WebStart.php';
// Construct entry point object and call doRun() to handle the request.
( new ApiEntryPoint(
RequestContext::getMain(),
new EntryPointEnvironment(),
MediaWikiServices::getInstance()
) )->run();

3727
mediawiki/autoload.php Normal file

File diff suppressed because it is too large Load Diff

227
mediawiki/composer.json Normal file
View File

@ -0,0 +1,227 @@
{
"name": "mediawiki/core",
"description": "Free software wiki application developed by the Wikimedia Foundation and others",
"type": "mediawiki-core",
"keywords": [
"mediawiki",
"wiki"
],
"homepage": "https://www.mediawiki.org/",
"authors": [
{
"name": "MediaWiki Community",
"homepage": "https://www.mediawiki.org/wiki/Special:Version/Credits"
}
],
"license": "GPL-2.0-or-later",
"support": {
"issues": "https://phabricator.wikimedia.org/",
"irc": "irc://irc.libera.chat/mediawiki",
"wiki": "https://www.mediawiki.org/"
},
"prefer-stable": true,
"require": {
"composer/semver": "3.4.3",
"ext-calendar": "*",
"ext-ctype": "*",
"ext-dom": "*",
"ext-fileinfo": "*",
"ext-iconv": "*",
"ext-intl": "*",
"ext-json": "*",
"ext-libxml": "*",
"ext-mbstring": "*",
"ext-openssl": "*",
"ext-xml": "*",
"ext-xmlreader": "*",
"guzzlehttp/guzzle": "7.10.0",
"justinrainbow/json-schema": "5.3.1",
"liuggio/statsd-php-client": "1.0.18",
"mck89/peast": "1.17.4",
"monolog/monolog": "2.9.3",
"oojs/oojs-ui": "0.51.2",
"pear/mail": "2.0.0",
"pear/mail_mime": "1.10.12",
"pear/net_smtp": "1.12.1",
"php": ">=8.1.0",
"psr/container": "1.1.2",
"psr/http-message": "1.1",
"psr/log": "1.1.4",
"ralouphie/getallheaders": "3.0.3",
"symfony/polyfill-php82": "1.37.0",
"symfony/polyfill-php83": "1.37.0",
"symfony/polyfill-php84": "1.37.0",
"symfony/polyfill-php85": "1.37.0",
"symfony/yaml": "5.4.52",
"wikimedia/assert": "0.5.1",
"wikimedia/at-ease": "3.0.0",
"wikimedia/base-convert": "2.0.2",
"wikimedia/bcp-47-code": "2.0.0",
"wikimedia/cdb": "3.0.0",
"wikimedia/cldr-plural-rule-parser": "3.0.0",
"wikimedia/common-passwords": "0.5.0",
"wikimedia/composer-merge-plugin": "2.1.0",
"wikimedia/css-sanitizer": "6.2.1",
"wikimedia/cssjanus": "2.3.0",
"wikimedia/html-formatter": "4.1.0",
"wikimedia/ip-utils": "5.0.0",
"wikimedia/json-codec": "4.0.0",
"wikimedia/less.php": "5.5.0",
"wikimedia/minify": "2.9.0",
"wikimedia/normalized-exception": "2.0.0",
"wikimedia/object-factory": "5.0.1",
"wikimedia/parsoid": "0.20.8",
"wikimedia/php-session-serializer": "3.0.0",
"wikimedia/purtle": "2.0.0",
"wikimedia/relpath": "4.0.1",
"wikimedia/remex-html": "4.1.2",
"wikimedia/request-timeout": "2.0.0",
"wikimedia/running-stat": "2.1.0",
"wikimedia/scoped-callback": "5.0.0",
"wikimedia/services": "4.0.0",
"wikimedia/shellbox": "4.1.1",
"wikimedia/utfnormal": "4.0.0",
"wikimedia/timestamp": "4.1.1",
"wikimedia/wait-condition-loop": "2.0.2",
"wikimedia/wrappedstring": "4.0.1",
"wikimedia/xmp-reader": "0.10.2",
"zordius/lightncandy": "1.2.6"
},
"require-dev": {
"composer/spdx-licenses": "1.5.8",
"doctrine/dbal": "3.8.4",
"doctrine/sql-formatter": "1.1.3",
"ext-simplexml": "*",
"giorgiosironi/eris": "^0.14.0",
"hamcrest/hamcrest-php": "^2.0",
"johnkary/phpunit-speedtrap": "^4.0",
"mediawiki/mediawiki-codesniffer": "45.0.0",
"mediawiki/mediawiki-phan-config": "0.14.0",
"mediawiki/minus-x": "1.1.3",
"nikic/php-parser": "^5.5.0",
"php-parallel-lint/php-console-highlighter": "1.0.0",
"php-parallel-lint/php-parallel-lint": "1.4.0",
"phpunit/phpunit": "9.6.34",
"psy/psysh": "^0.12.19",
"seld/jsonlint": "1.10.2",
"wikimedia/alea": "1.0.0",
"wikimedia/langconv": "^0.4.2",
"wikimedia/testing-access-wrapper": "^3.0.0 || ^4.0.0",
"wmde/hamcrest-html-matchers": "^1.0.0"
},
"replace": {
"symfony/polyfill-ctype": "1.99",
"symfony/polyfill-intl-grapheme": "1.17.1",
"symfony/polyfill-intl-normalizer": "1.17.1",
"symfony/polyfill-mbstring": "1.99",
"symfony/polyfill-php80": "1.99",
"symfony/polyfill-php81": "1.99"
},
"suggest": {
"ext-apcu": "Faster web responses overall.",
"ext-bcmath": "Increased performance of some operations. Required especially on 32 bit machines. This or ext-gmp are needed for scrambling Temporary Accounts.",
"ext-curl": "Faster HTTP services, e.g. when using InstantCommons, Swift, or Etcd.",
"ext-exif": "Enable processing of EXIF information in file uploads.",
"ext-gd": "Enable thumbnails for file uploads.",
"ext-gmp": "Increased performance of some operations. Required especially on 32 bit machines. This or ext-bcmath are needed for scrambling Temporary Accounts.",
"ext-igbinary": "Enables use of igbinary for serialisation.",
"ext-imagick": "Enables use of imagemagick for image manipulation.",
"ext-memcached": "Enables use of Memcached for caching purposes.",
"ext-mysqli": "Enable the MySQL and MariaDB database type for MediaWiki.",
"ext-pdo": "Enable the SQLite database type for MediaWiki.",
"ext-pgsql": "Enable the PostgreSQL database type for MediaWiki.",
"ext-posix": "Enable CLI concurrent processing, e.g. for runJobs.php.",
"ext-pcntl": "Enable CLI concurrent processing, e.g. for runJobs.php and rebuildLocalisationCache.php.",
"ext-readline": "Enable CLI history and autocomplete, e.g. for eval.php and other REPLs.",
"ext-redis": "Enables use of Redis for caching purposes.",
"ext-sockets": "Enable CLI concurrent processing, e.g. for rebuildLocalisationCache.php.",
"ext-wikidiff2": "Faster text difference engine.",
"ext-zlib": "Enable use of GZIP compression, e.g. for SqlBagOStuff (ParserCache), $wgCompressRevisions, or $wgUseFileCache."
},
"autoload": {
"psr-4": {
"MediaWiki\\Composer\\": "includes/composer"
}
},
"autoload-dev": {
"files": [
"vendor/hamcrest/hamcrest-php/hamcrest/Hamcrest.php",
"vendor/wmde/hamcrest-html-matchers/src/functions.php"
]
},
"scripts": {
"mw-install:sqlite": "@php maintenance/run.php install --server=http://localhost:4000 --dbtype sqlite --with-developmentsettings --dbpath cache/ --scriptpath= --pass adminpassword MediaWiki Admin",
"serve": [
"Composer\\Config::disableProcessTimeout",
"@putenv MW_LOG_DIR=logs",
"@putenv MW_LOG_STDERR=1",
"@putenv PHP_CLI_SERVER_WORKERS=8",
"@php -S 127.0.0.1:4000"
],
"lint": "parallel-lint --exclude node_modules --exclude vendor",
"phan": "phan -d . --long-progress-bar",
"phpcs": "phpcs -p -s --cache",
"fix": [
"minus-x fix .",
"phpcbf"
],
"pre-install-cmd": "MediaWiki\\Composer\\VersionChecker::onEvent",
"pre-update-cmd": "MediaWiki\\Composer\\VersionChecker::onEvent",
"post-install-cmd": "MediaWiki\\Composer\\ComposerVendorHtaccessCreator::onEvent",
"post-update-cmd": "MediaWiki\\Composer\\ComposerVendorHtaccessCreator::onEvent",
"releasenotes": "@phpunit --group ReleaseNotes",
"test": [
"@lint .",
"@phpcs .",
"minus-x check ."
],
"test-some": [
"@lint",
"@phpcs"
],
"phpunit": [
"Composer\\Config::disableProcessTimeout",
"phpunit"
],
"phpunit:unit": "@phpunit --colors=always --testsuite=core:unit,extensions:unit,skins:unit",
"phpunit:integration": "@phpunit --colors=always --testsuite=core:integration,extensions:integration,skins:integration",
"phpunit:coverage": "@phpunit --testsuite=core:unit --exclude-group Dump,Broken",
"phpunit:coverage-edit": "MediaWiki\\Composer\\ComposerPhpunitXmlCoverageEdit::onEvent",
"phpunit:entrypoint": "@phpunit",
"phpunit:prepare-parallel:extensions": [
"MediaWiki\\Composer\\PhpUnitSplitter\\PhpUnitXmlManager::listTestsNotice",
"@phpunit --list-tests-xml=tests-list-extensions.xml --testsuite=extensions",
"MediaWiki\\Composer\\PhpUnitSplitter\\PhpUnitXmlManager::splitTestsListExtensions"
],
"phpunit:prepare-parallel:default": [
"MediaWiki\\Composer\\PhpUnitSplitter\\PhpUnitXmlManager::listTestsNotice",
"@phpunit --list-tests-xml=tests-list-default.xml",
"MediaWiki\\Composer\\PhpUnitSplitter\\PhpUnitXmlManager::splitTestsListDefault"
],
"phpunit:prepare-parallel:split-file": "MediaWiki\\Composer\\PhpUnitSplitter\\PhpUnitXmlManager::splitTestsCustom",
"phpunit:parallel:custom-groups": "MediaWiki\\Composer\\ComposerLaunchParallel::launchTestsCustomGroups",
"phpunit:parallel:database": "MediaWiki\\Composer\\ComposerLaunchParallel::launchTestsDatabase",
"phpunit:parallel:databaseless": "MediaWiki\\Composer\\ComposerLaunchParallel::launchTestsDatabaseless",
"maintenance": "@php maintenance/run.php"
},
"config": {
"optimize-autoloader": true,
"prepend-autoloader": false,
"allow-plugins": {
"wikimedia/composer-merge-plugin": true,
"composer/installers": true,
"dealerdirect/phpcodesniffer-composer-installer": true
},
"audit": {
"block-insecure": false
}
},
"extra": {
"merge-plugin": {
"include": [
"composer.local.json"
],
"merge-dev": false
}
}
}

View File

@ -0,0 +1,10 @@
{
"extra": {
"merge-plugin": {
"include": [
"extensions/*/composer.json",
"skins/*/composer.json"
]
}
}
}

View File

@ -0,0 +1,53 @@
# Please see DEVELOPERS.md for help
#
# Contributions to this file are welcome but please note that this file is
# minimal by design, with the idea to make it easily extensible via
# docker-compose.override.yml. For help with doing that, please see
# DEVELOPERS.md
services:
mediawiki:
image: docker-registry.wikimedia.org/dev/bookworm-php83-fpm:1.0.0-s1
user: "${MW_DOCKER_UID}:${MW_DOCKER_GID}"
volumes:
- ./:/var/www/html/w:cached
env_file:
- '.env'
environment:
COMPOSER_CACHE_DIR: '/var/www/html/w/cache/composer'
MW_SERVER: 'http://localhost:${MW_DOCKER_PORT:-8080}'
MW_DOCKER_PORT: "${MW_DOCKER_PORT:-8080}"
MW_SCRIPT_PATH: '/w'
MW_DBPATH: '/var/www/html/w/cache/sqlite'
MW_DBTYPE: 'sqlite'
MW_LANG: 'en'
MW_USER: '${MEDIAWIKI_USER:-Admin}'
MW_PASS: '${MEDIAWIKI_PASSWORD:-dockerpass}'
MW_SITENAME: 'MediaWiki'
MW_LOG_DIR: /var/www/html/w/cache
XDEBUG_CONFIG: '${XDEBUG_CONFIG}'
XDEBUG_ENABLE: '${XDEBUG_ENABLE:-true}'
XHPROF_ENABLE: '${XHPROF_ENABLE:-true}'
mediawiki-web:
image: docker-registry.wikimedia.org/dev/bookworm-apache2:1.0.1
user: "${MW_DOCKER_UID}:${MW_DOCKER_GID}"
ports:
- "${MW_DOCKER_PORT:-8080}:8080"
volumes:
- ./:/var/www/html/w:cached
env_file:
- '.env'
environment:
MW_LOG_DIR: /var/www/html/w/cache
MW_DOCKER_PORT: "${MW_DOCKER_PORT:-8080}"
mediawiki-jobrunner:
image: docker-registry.wikimedia.org/dev/bookworm-php83-jobrunner:1.0.0
user: "${MW_DOCKER_UID}:${MW_DOCKER_GID}"
volumes:
- ./:/var/www/html/w:cached
env_file:
- '.env'
environment:
MW_LOG_DIR: /var/www/html/w/cache
MW_INSTALL_PATH: /var/www/html/w

274
mediawiki/docs/Hooks.md Normal file
View File

@ -0,0 +1,274 @@
Hooks {#hooks}
=====
## Introduction
Hooks allow MediaWiki Core to call extensions or allow one extension to call
another extension. For more information and a list of hooks, see
https://www.mediawiki.org/wiki/Manual:Hooks
Starting in MediaWiki 1.35, each hook called by MediaWiki Core has an
associated interface with a single method. To call the hook, obtain a "hook
runner" object, which implements the relevant interface, and call the relevant
method. To handle a hook event in an extension, create a handler object which
implements the interface.
The name of the interface is the name of the hook with "Hook" added to the end.
Interfaces are typically placed in the namespace of their primary caller.
The method name for the hook is the name of the hook, prefixed with "on".
Several hooks had colons in their name, which are invalid in an interface or
method name. These hooks have interfaces and method names in which the colons
are replaced with underscores.
For example, if the hook is called `Mash`, we might have the interface:
interface MashHook {
public function onMash( $banana );
}
Hooks can be defined and called by extensions. The extension should define a
hook interface for each hook, as described above.
## HookContainer
HookContainer is a service which is responsible for maintaining a list of hook
handlers and calling those handlers when requested. HookContainer is not aware
of hook interfaces or parameter types.
HookContainer provides hook metadata. For example, `isRegistered()` tells us
whether there are any handlers for a given hook event.
A HookContainer instance can be obtained from the global service locator with
MediaWikiServices::getHookContainer(). When implementing a service that needs
to call a hook, a HookContainer object should be passed to the constructor of
the service.
## Hook runner classes
A hook runner is a class which implements hook interfaces, proxying the calls
to `HookContainer::run()`.
MediaWiki has three hook runner classes: HookRunner, ApiHookRunner and
ResourceLoader\HookRunner.
ApiHookRunner has proxy methods for all hooks which are called by the Action
API. HookRunner has proxy methods for all hooks which are called by other parts
of Core. ResourceLoader\HookRunner contains hooks specific to ResourceLoader.
Some hooks are implemented in two hook runner classes.
Extensions which call hooks should create their own hook runner class, by
analogy with the ones in Core. Hook runner classes are effectively internal
to the module which calls the relevant hooks. Reorganisation of the hook
calling code may lead to methods being removed from hook runner classes. Thus,
it is safer for extensions to define their own hook runner classes even if
they are calling Core hooks.
New code should typically be written in a service which takes a HookContainer
as a constructor parameter. In static functions it can fall back to the global
HookContainer instance.
For example, to call the hook `Mash`, as defined above, in static code:
$hookRunner = new HookRunner( MediaWikiServices::getInstance()->getHookContainer() )
$hookRunner->onMash( $banana );
## How to handle a hook event in an extension
In extension.json, there is a new attribute called `HookHandlers`. This is
an object mapping the handler name to an ObjectFactory specification describing
how to create the handler object. The specification will typically have a
`class` member with the name of the handler class. For example, in an extension
called `FoodProcessor`, we may have:
"HookHandlers": {
"main": {
"class": "MediaWiki\\Extension\\FoodProcessor\\HookHandler"
}
}
Then in the Hooks attribute, instead of a function name, the value will be the
handler name:
"Hooks": {
"Mash": "main"
}
Or more explicitly, by using an object instead of a string for the handler:
"Hooks": {
"Mash": {
"handler": "main"
}
}
Note that while your HookHandler class will implement an interface that ends
with the word "Hook", in `extension.json` you should omit the word "Hook"
from the key in the `Hooks` definition. For example, in the definitions above,
the key must be "Mash", not "MashHook".
Then the extension will define a handler class:
namespace MediaWiki\Extension\FoodProcessor;
class HookHandler implements MashHook {
public function onMash( $banana ) {
// Implementation goes here
}
}
## Service dependencies
The ObjectFactory specification in HookHandlers can contain a list of services
which should be instantiated and provided to the constructor or factory
function for the handler. For example:
"HookHandlers": {
"main": {
"class": "MediaWiki\\Extension\\FoodProcessor\\HookHandler",
"services": [ "ReadOnlyMode" ]
}
}
However, care should be taken with this feature. Some services have expensive
constructors, so requesting them when handling commonly-called hooks may damage
performance. Also, some services may not be safe to construct from within a hook
call.
The safest pattern for service injection is to use a separate handler for each
hook, and to inject only the services needed by that hook.
Calling a hook with the `noServices` option disables service injection. If a
handler for such a hook specifies services, an exception will be thrown when
the hook is called.
## Returning and aborting
If a hook handler returns false, HookContainer will stop iterating through the
list of handlers and will immediately return false.
If a hook handler returns true, or if there is no return value (causing it to
effectively return null), then HookContainer will continue to call any other
remaining handlers. Eventually HookContainer::run() will return true.
If there were no registered handlers, HookContainer::run() will return true.
Some hooks are declared to be "not abortable". If a handler for a non-abortable
hook returns false, an exception will be thrown. A hook is declared to be not
abortable by passing `[ "abortable" => false ]` in the $options parameter to
HookContainer::run().
Aborting is properly used to enforce a convention that only one extension
may handle a given hook call.
Aborting is sometimes used as a generic return value, to indicate that the
caller should stop performing some action.
Most hook callers do not check the return value from HookContainer::run() and
there is no real concept of aborting. The only effect of returning `false` from
a handler of these hooks is to break other extensions.
Theoretically, extensions which are registered first in LocalSettings.php will
be called first, and thus will have the first opportunity to abort a hook call.
This behaviour should not be relied upon. In the new hook system, handlers
registered in the legacy way are called first, before handlers registered in
the new way.
## Parameters passed by reference
The typical way for a handler to return data to the caller is by modifying a
parameter which was passed by reference. This is sometimes called "replacement".
Reference parameters were somewhat overused in early versions of MediaWiki. You
may find that some parameters passed by reference cannot reasonably be modified.
Replacement either has no effect on the caller or would cause unexpected or
inconsistent effects. Handlers should generally only replace a parameter when it
is clear from the documentation that replacement is expected.
## How to define a new hook
* Create a hook interface, typically in a subnamespace called `Hook` relative
to the caller namespace. For example, if the caller is in a namespace called
`MediaWiki\Foo`, the hook interface might be placed in `MediaWiki\Foo\Hook`.
* Add an implementation to the relevant HookRunner class.
## Hook deprecation
Core hooks are deprecated by adding them to an array in the DeprecatedHooks
class. Hooks declared in extensions may be deprecated by listing them in the
`DeprecatedHooks` attribute:
"DeprecatedHooks": {
"Mash": {
"deprecatedVersion": "2.0",
"component": "FoodProcessor"
}
}
If the `component` is not specified, it defaults to the name of the extension.
The hook interface should be marked as deprecated by adding `@deprecated` to the
interface doc comment. The interface doc comment is a better place for
`@deprecated` than the method doc comment, because this causes the interface to
be deprecated for implementation. Deprecating the method only causes calling
to be deprecated, not handling.
Deprecating a hook in this way activates a migration system called
**call filtering**. Extensions opt in to call filtering of deprecated hooks by
**acknowledging** deprecation. An extension acknowledges deprecation with the
`deprecated` parameter in the `Hooks` attribute:
"Hooks": {
"Mash": {
"handler": "main",
"deprecated": true
}
}
If deprecation is acknowledged by the extension:
* If MediaWiki knows that the hook is deprecated, the handler will not be
called. The call to the handler is filtered.
* If MediaWiki does not have the hook in its list of deprecated hooks, the
handler will be called anyway.
Deprecation acknowledgement is a way for the extension to say that it has made
some other arrangement for implementing the relevant functionality and does
not need the handler for the deprecated hook to be called.
### Call filtering example
Suppose the hook `Mash` is deprecated in MediaWiki 2.0, and is replaced by a
new one called `Slice`. In our example extension FoodProcessor 1.0, the
`Mash` hook is handled. In FoodProcessor 2.0, both `Mash` and `Slice` have
handlers, but deprecation of `Mash` is acknowledged. Thus:
* With MediaWiki 2.0 and FoodProcessor 1.0, `onMash` is called but raises a
deprecation warning.
* With MediaWiki 2.0 and FoodProcessor 2.0, `onMash` is filtered, and `onSlice`
is called.
* With MediaWiki 1.0 and FoodProcessor 2.0, `onMash` is called, since it is not
yet deprecated in Core. `onSlice` is not called since it does not yet exist
in Core.
So the call filtering system provides both forwards and backwards compatibility.
### Silent deprecation
Developers sometimes use two stages of deprecation: "soft" deprecation in which
the deprecated entity is merely discouraged in documentation, and "hard"
deprecation in which a warning is raised. When you soft-deprecate a hook, it is
important to register it as deprecated so that call filtering is activated.
Activating call filtering simplifies the task of migrating extensions to the
new hook.
To deprecate a hook without raising deprecation warnings, use the "silent" flag:
"DeprecatedHooks": {
"Mash": {
"deprecatedVersion": "2.0",
"component": "FoodProcessor",
"silent": true
}
}
As with hard deprecation, `@deprecated` should be added to the interface.

372
mediawiki/docs/Injection.md Normal file
View File

@ -0,0 +1,372 @@
Dependency Injection {#dependencyinjection}
=======
This is an overview of how MediaWiki uses of dependency injection.
The design originates from [RFC T384](https://phabricator.wikimedia.org/T384).
The term "dependency injection" (DI) refers to a pattern in object oriented
programming. DI tries to improve modularity by reducing strong coupling
between classes. In practical terms, this means that anything an object needs
to operate should be injected from the outside. The object itself should only
know narrow interfaces, no concrete implementation of the logic it relies on.
The requirement to inject everything typically results in an architecture based
on two main kinds of objects: simple "value" objects with no business logic
(and often immutable), and essentially stateless "service" objects that use
other service objects to operate on value objects.
As of 2022 (MediaWiki 1.39), MediaWiki has adopted dependency injection in much
of its code. However, some operations still require the use of singletons or
otherwise involve global state.
## Overview
The heart of the DI in MediaWiki is the central service locator,
MediaWikiServices, which acts as the top-level factory (or registry) for
services. MediaWikiServices represents the tree (or network) of service objects
that define MediaWiki's application logic. It acts as an entry point to all
dependency injection for MediaWiki core.
When `MediaWikiServices::getInstance()` is first called, it will create an
instance of MediaWikiServices and populate it with the services defined by
MediaWiki core in `includes/ServiceWiring.php`, as well as any additional
bootstrapping files specified in `$wgServiceWiringFiles`. The service
wiring files define the (default) service implementations to use, and
specifies how they depend on each other ("wiring").
Extensions can add their own wiring files to `$wgServiceWiringFiles`, in order
to define their own service. Extensions may also use the `MediaWikiServices`
hook to replace ("redefine") a core service, by calling methods on the
MediaWikiServices instance.
It should be noted that the term "service locator" is often used to refer to a
top-level factory that is accessed directly, throughout the code, to avoid
explicit dependency injection. In contrast, the term "DI container" is often
used to describe a top-level factory that is only accessed only inside service
wiring code when instantiating service classes. We use the term "service locator"
because it is more descriptive than "DI container", even though application
logic is strongly discouraged from accessing MediaWikiServices directly.
`MediaWikiServices::getInstance()` should ideally be accessed only in "static
entry points" such as hook handler functions. See "Migration" below.
## Principles {#di-principles}
Service classes generally only vary on site configuration and are
deterministic and agnostic of global state. It is the responsibility of
callers to a service object to obtain and derive information from a
web request (such as title, user, language, WebRequest, RequestContext),
and pass this to specific methods of a service class as-needed. See
[T218555](https://phabricator.wikimedia.org/T218555) for related discussion.
Consider using the factory pattern if your service would otherwise be
unergonomic or slow, e.g. due to passing many parameters and/or recomputing
the same derived information. This keeps the global state out of the
service class, by having the service be a factory from which the caller
can obtain a (re-usable) object for its specific context.
This design ensures service classes are safe to use in both user-facing
contexts on the web (e.g. index.php page views and special pages), as
well as in an API, job, or maintenance script. It also ensures that
within a web-facing context the same service can be safely used
multiple times to perform different operations, without incorrectly
implying certain commonalities between these calls. Lastly, this
restriction allows services to be instantiated across wikis in the
future.
If a feature is not ready to meet these requirements, keep it outside
the service container. This avoids false confidence in the safety of an
injected service, and its ripple effect on other services.
### Principle exemption
There is a limited exemption to the above principles for "inconsequential
state". That is, global state may be used directly if and only if used
for diagnostics or to optimise performance, so long as they do not
change the observed functional outcome of a called method.
Examples of safe and inconsequential state:
* Use `$_SERVER['REQUEST_TIME_FLOAT']` or `ConvertibleTimestamp::now`
to help compute a time measure that is sent to a metric service.
* Use `wfHostname()`, `PHP_SAPI`, or `WikiMap::getCurrentWikiId()`
to describe where, how, or for which wiki the overall process was
created and send it as message context to a logging service.
* Use `WebRequest::getRequestId()` to automatically inject a
header into HTTP requests to other services. These are for tracking
purposes only.
* Use `function_exists('apcu_fetch')` to automatically enable use
of caching.
Examples of unsafe state in a service class:
* Do not use `WikiMap::getCurrentWikiId()` as the default value
to obtain a database connection.
* Do not use `$_SERVER['SERVER_NAME']` to inject a header into
HTTP requests to other services to control which wiki to operate on.
## Create a new service
To create a new service in MediaWiki core, write a function that will return
the appropriate class instantiation for that service in ServiceWiring.php. This
makes the service available through the generic `getService()` method on the
`MediaWikiServices` class. We then also add a wrapper method to
MediaWikiServices.php with a discoverable method named and strictly typed
return value to reduce mistakes and improve static analysis.
## Service Reset
Services get their configuration injected, and changes to global
configuration variables will not have any effect on services that were already
instantiated. This would typically be the case for low level services like
the ConfigFactory or the ObjectCacheManager, which are used during extension
registration. To address this issue, Setup.php resets the global service
locator instance by calling `MediaWikiServices::resetGlobalInstance()` once
configuration and extension registration is complete.
Note that "unmanaged" legacy services services that manage their own singleton
must not keep references to services managed by MediaWikiServices, to allow a
clean reset. After the global MediaWikiServices instance got reset, any such
references would be stale, and using a stale service will result in an error.
Services should either have all dependencies injected and be themselves managed
by MediaWikiServices, or they should use the Service Locator pattern, accessing
service instances via the global MediaWikiServices instance state when needed.
This ensures that no stale service references remain after a reset.
## Configuration
When the default MediaWikiServices instance is created, a Config object is
provided to the constructor. This Config object represents the "bootstrap"
configuration which will become available as the 'BootstrapConfig' service.
As of MW 1.27, the bootstrap config is a GlobalVarConfig object providing
access to the $wgXxx configuration variables.
The bootstrap config is then used to construct a 'ConfigFactory' service,
which in turn is used to construct the 'MainConfig' service. Application
logic should use the 'MainConfig' service (or a more specific configuration
object). 'BootstrapConfig' should only be used for bootstrapping basic
services that are needed to load the 'MainConfig'.
Note: Several well known services in MediaWiki core act as factories
themselves, e.g. ApiModuleManager, ObjectCache, SpecialPageFactory, etc.
The registries these factories are based on are currently managed as part of
the configuration. This may however change in the future.
## Migration
This section provides some recipes for improving code modularity by reducing
strong coupling. The dependency injection mechanism described above is an
essential tool in this effort.
### Migrate access to global service instances and config variables
Assume `Foo` is a class that uses the `$wgScriptPath` global and calls
`wfGetDB()` to get a database connection, in non-static methods.
* Add `$scriptPath` as a constructor parameter and use `$this->scriptPath`
instead of `$wgScriptPath`.
* Add IConnectionProvider `$dbProvider` as a constructor parameter. Use
`$this->dbProvider->getReplicaDatabase()` instead of `wfGetDB( DB_REPLICA )`,
`$this->dbProvider->->getPrimaryDatabase()` instead of `wfGetDB( DB_PRIMARY )`.
* Any code that calls `Foo`'s constructor would now need to provide the
`$scriptPath` and `$dbProvider`. To avoid this, avoid direct instantiation
of services all together - see below.
### Migrate services with multiple configuration variables
When a service needs multiple configuration globals injected, a ServiceOptions
object is commonly used with the service class defining a public constant
(usually `CONSTRUCTOR_OPTIONS`) with an array of settings that the class needs
access to.
```php
<?php
class DemoService {
public const CONSTRUCTOR_OPTIONS = [
'Foo',
'Bar'
];
private $options;
public function __construct( ServiceOptions $options ) {
// ServiceOptions::assertRequiredOptions ensures that all of the
// settings listed in CONSTRUCTOR_OPTIONS are available
$options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
$this->options = $options;
// $wgFoo is now available with $this->options->get( 'Foo' )
// $wgBar is now available with $this->options->get( 'Bar' )
}
}
```
ServiceOptions objects are constructed within ServiceWiring.php and can also
be created in tests.
```php
'DemoService' => function ( MediaWikiServices $services ) : DemoService {
return new DemoService(
new ServiceOptions(
DemoService::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
)
);
},
```
### Migrate class-level singleton getters
Assume class `Foo` has mostly non-static methods, and provides a static
`getInstance()` method that returns a singleton (or default instance).
* Add an instantiator function for `Foo` into ServiceWiring.php. The
instantiator would do exactly what `Foo::getInstance()` did. However, it
should replace any access to global state with calls to `$services->getXxx()`
to get a service, or `$services->getMainConfig()->get()` to get a
configuration setting.
* Add a `getFoo()` method to MediaWikiServices. Don't forget to add the
appropriate test cases in MediaWikiServicesTest.
* Turn `Foo::getInstance()` into a deprecated alias for
`MediaWikiServices::getInstance()->getFoo()`. Change all calls to
`Foo::getInstance()` to use injection (see above).
### Migrate direct service instantiation
Assume class `Bar` calls `new Foo()`.
* Add an instantiator function for `Foo` into ServiceWiring.php and add a
`getFoo()` method to MediaWikiServices. Don't forget to add the appropriate
test cases in MediaWikiServicesTest.
* In the instantiator, replace any access to global state with calls
to `$services->getXxx()` to get a service, or
`$services->getMainConfig()->get()` to get a configuration setting.
* The code in `Bar` that calls `Foo`'s constructor should be changed to have a
`Foo` instance injected; Eventually, the only code that instantiates `Foo` is
the instantiator in ServiceWiring.php.
* As an intermediate step, `Bar`'s constructor could initialize the `$foo`
member variable by calling `MediaWikiServices::getInstance()->getFoo()`. This
is acceptable as a stepping stone, but should be replaced by proper injection
via a constructor argument. Do not however inject the MediaWikiServices
object!
### Migrate parameterized helper instantiation
Assume class `Bar` creates some helper object by calling `new Foo( $x )`,
and `Foo` uses a global singleton of the `Xyzzy` service.
* Define a `FooFactory` class (or a `FooFactory` interface along with a
`MyFooFactory` implementation). `FooFactory` defines the method
`newFoo( $x )` or `getFoo( $x )`, depending on the desired semantics (`newFoo`
would guarantee a fresh instance). When Foo gets refactored to have `Xyzzy`
injected, `FooFactory` will need a `Xyzzy` instance, so `newFoo()` can pass it
to `new Foo()`.
* Add an instantiator function for FooFactory into ServiceWiring.php and add a
getFooFactory() method to MediaWikiServices. Don't forget to add the
appropriate test cases in MediaWikiServicesTest.
* The code in Bar that calls Foo's constructor should be changed to have a
FooFactory instance injected; Eventually, the only code that instantiates
Foo are implementations of FooFactory, and the only code that instantiates
FooFactory is the instantiator in ServiceWiring.php.
* As an intermediate step, Bar's constructor could initialize the $fooFactory
member variable by calling `MediaWikiServices::getInstance()->getFooFactory()`.
This is acceptable as a stepping stone, but should be replaced by proper
injection via a constructor argument. Do not however inject the
MediaWikiServices object!
### Migrate a handler registry
Assume class `Bar` calls `FooRegistry::getFoo( $x )` to get a specialized `Foo`
instance for handling `$x`.
* Turn `getFoo` into a non-static method.
* Add an instantiator function for `FooRegistry` into ServiceWiring.php and add
a `getFooRegistry()` method to MediaWikiServices. Don't forget to add the
appropriate test cases in MediaWikiServicesTest.
* Change all code that calls `FooRegistry::getFoo()` statically to call this
method on a `FooRegistry` instance. That is, `Bar` would have a `$fooRegistry`
member, initialized from a constructor parameter.
* As an intermediate step, Bar's constructor could initialize the `$fooRegistry`
member variable by calling
`MediaWikiServices::getInstance()->getFooRegistry()`. This is acceptable as a
stepping stone, but should be replaced by proper injection via a constructor
argument. Do not however inject the MediaWikiServices object!
### Migrate deferred service instantiation
Assume class `Bar` calls `new Foo()`, but only when needed, to avoid the cost of
instantiating Foo().
* Define a `FooFactory` interface and a `MyFooFactory` implementation of that
interface. `FooFactory` defines the method `getFoo()` with no parameters.
* Precede as for the "parameterized helper instantiation" case described above.
### Migrate a class with only static methods
Assume `Foo` is a class with only static methods, such as `frob()`, which
interacts with global state or system resources.
* Introduce a `FooService` interface and a `DefaultFoo` implementation of that
interface. `FooService` contains the public methods defined by Foo.
* Add an instantiator function for `FooService` into ServiceWiring.php and
add a `getFooService()` method to MediaWikiServices. Don't forget to
add the appropriate test cases in MediaWikiServicesTest.
* Add a private static `getFooService()` method to `Foo`. That method just
calls `MediaWikiServices::getInstance()->getFooService()`.
* Make all methods in `Foo` delegate to the `FooService` returned by
`getFooService()`. That is, `Foo::frob()` would do
`self::getFooService()->frob()`.
* Deprecate `Foo`. Inject a `FooService` into all code that calls methods
on `Foo`, and change any calls to static methods in foo to the methods
provided by the `FooService` interface.
### Migrate static hook handler functions (to allow unit testing)
Assume `MyExtHooks::onFoo` is a static hook handler function that is called with
the parameter `$x`; Further assume `MyExt::onFoo` needs service `Bar`, which is
already known to MediaWikiServices (if not, see above).
* Create a non-static `doFoo( $x )` method in `MyExtHooks` that has the same
signature as `onFoo( $x )`. Move the code from `onFoo()` into `doFoo()`,
replacing any access to global or static variables with access to instance
member variables.
* Add a constructor to `MyExtHooks` that takes a Bar service as a parameter.
* Add a static method called `newFromGlobalState()` with no parameters. It
should just return
`new MyExtHooks( MediaWikiServices::getInstance()->getBar() )`.
* The original static handler method `onFoo( $x )` is then implemented as
`self::newFromGlobalState()->doFoo( $x )`.
### Migrate a "smart record"
Assume `Thingy` is a "smart record" that "knows" how to load and store itself.
For this purpose, `Thingy` uses wfGetDB().
* Create a "dumb" value class `ThingyRecord` that contains all the information
that `Thingy` represents (e.g. the information from a database row). The value
object should not know about any service.
* Create a DAO-style service for loading and storing `ThingyRecord`s, called
`ThingyStore`. It may be useful to split the interfaces for reading and
writing, with a single class implementing both interfaces, so we in the
end have the `ThingyLookup` and `ThingyStore` interfaces, and a SqlThingyStore
implementation.
* Add instantiator functions for `ThingyLookup` and `ThingyStore` in
ServiceWiring.php. Since we want to use the same instance for both service
interfaces, the instantiator for `ThingyLookup` would return
`$services->getThingyStore()`.
* Add `getThingyLookup()` and `getThingyStore()` methods to MediaWikiServices.
Don't forget to add the appropriate test cases in MediaWikiServicesTest.
* In the old `Thingy` class, replace all member variables that represent the
record's data with a single `ThingyRecord` object.
* In the old Thingy class, replace all calls to static methods or functions,
such as wfGetDB(), with calls to the appropriate services, such as
`IConnectionProvider::getReplicaDatabase()`.
* In Thingy's constructor, pull in any services needed, such as the
IConnectionProvider, by using `MediaWikiServices::getInstance()`. These services
cannot be injected without changing the constructor signature, which
is often impractical for "smart records" that get instantiated directly
in many places in the code base.
* Deprecate the old `Thingy` class. Replace all usages of it with one of the
three new classes: loading needs a `ThingyLookup`, storing needs a
`ThingyStore`, and reading data needs a `ThingyRecord`.
### Migrate lazy loading
Assume `Thingy` is a "smart record" as described above, but requires lazy
loading of some or all the data it represents.
* Instead of a plain object, define `ThingyRecord` to be an interface. Provide a
"simple" and "lazy" implementations, called `SimpleThingyRecord` and
`LazyThingyRecord`. `LazyThingyRecord` knows about some lower level storage
interface, like a LoadBalancer, and uses it to load information on demand.
* Any direct instantiation of a `ThingyRecord` would use the
`SimpleThingyRecord` implementation.
* `SqlThingyStore` however creates instances of `LazyThingyRecord`, and injects
whatever storage layer service `LazyThingyRecord` needs to perform lazy
loading.

View File

@ -0,0 +1,12 @@
Introduction {#mainpage}
===============================
Welcome to the MediaWiki autogenerated documentation system.
If you are looking to use, install or configure your wiki, see the main site: <https://www.mediawiki.org/>.
Helpful resources
-------------------------------
- [General information](https://www.mediawiki.org/)
- [Installation guide](https://www.mediawiki.org/wiki/Manual:Installation_guide)
- [Configuration](https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:System_administration)
- [New Developers](https://www.mediawiki.org/wiki/New_Developers)

View File

@ -0,0 +1,7 @@
Language {#language}
=======
The Language object handles all readable text produced by the software.
See [MediaWiki.org](https://www.mediawiki.org/wiki/Localisation#General_use_.28for_developers.29)
for documentation relating to using localized messages.

View File

@ -0,0 +1,28 @@
LinkCache {#linkcache}
========
The LinkCache class maintains a list of article titles and the information about
whether or not the article exists in the database. This is used to mark up links
when displaying a page. If the same link appears more than once on any page,
then it only has to be looked up once. In most cases, link lookups are done in
batches with the LinkBatch class, or the equivalent in Parser::replaceLinkHolders(),
so the link cache is mostly useful for short snippets of parsed text (such as
the site notice), and for links in the navigation areas of the skin.
The link cache was formerly used to track links used in a document for the
purposes of updating the link tables. This application is now deprecated.
To create a batch, you can use the following code:
```php
$pages = [ 'Main Page', 'Project:Help', /* ... */ ];
$titles = [];
foreach( $pages as $page ){
$titles[] = Title::newFromText( $page );
}
$linkBatchFactory = MediaWikiServices::getInstance()->getLinkBatchFactory();
$batch = $linkBatchFactory->newLinkBatch( $titles );
$batch->execute();
```

70
mediawiki/docs/Logger.md Normal file
View File

@ -0,0 +1,70 @@
Logger {#debuglogger}
=======
MediaWiki.Logger.LoggerFactory implements a [PSR-3] compatible message logging
system.
Named Psr.Log.LoggerInterface instances can be obtained from the
MediaWiki.Logger.LoggerFactory::getInstance() static method.
MediaWiki.Logger.LoggerFactory expects a class implementing the
MediaWiki.Logger.Spi interface to act as a factory for new
Psr.Log.LoggerInterface instances.
The "Spi" in MediaWiki.Logger.Spi stands for "service provider interface". An
SPI is an API intended to be implemented or extended by a third party. This
software design pattern is intended to enable framework extension and
replaceable components. It is specifically used in the
MediaWiki.Logger.LoggerFactory service to allow alternate PSR-3 logging
implementations to be easily integrated with MediaWiki.
The service provider interface allows the backend logging library to be
implemented in multiple ways. The $wgMWLoggerDefaultSpi global provides the
classname of the default MediaWiki.Logger.Spi implementation to be loaded at
runtime. This can either be the name of a class implementing the
MediaWiki.Logger.Spi with a zero argument constructor or a callable that will
return an MediaWiki.Logger.Spi instance. Alternately the
MediaWiki.Logger.LoggerFactory::registerProvider() static method can be called
to inject an MediaWiki.Logger.Spi instance into the LoggerFactory and bypass the
use of the default configuration variable.
The MediaWiki.Logger.LegacySpi class implements a service provider to generate
MediaWiki.Logger.LegacyLogger instances. The MediaWiki.Logger.LegacyLogger class
implements the PSR-3 logger interface and provides output and configuration
equivalent to the historic logging output of wfDebug, wfDebugLog, wfLogDBError
and wfErrorLog. The MediaWiki.Logger.LegacySpi class is the default service
provider defined in MainConfigSchema.php. It's usage should be transparent for
users who are not ready or do not wish to switch to a alternate logging
platform.
The MediaWiki.Logger.MonologSpi class implements a service provider to generate
Psr.Log.LoggerInterface instances that use the [Monolog] logging library. See
the PHP docs (or source) for MediaWiki.Logger.MonologSpi for details on the
configuration of this provider. The default configuration installs a null
handler that will silently discard all logging events. The documentation
provided by the class describes a more feature rich logging configuration.
## Classes
* MediaWiki.Logger.LoggerFactory: Factory for Psr.Log.LoggerInterface loggers
* MediaWiki.Logger.Spi: Service provider interface for
MediaWiki.Logger.LoggerFactory
* MediaWiki.Logger.NullSpi: MediaWiki.Logger.Spi for creating instances that
discard all log events
* MediaWiki.Logger.LegacySpi: Service provider for creating
MediaWiki.Logger.LegacyLogger instances
* MediaWiki.Logger.LegacyLogger: PSR-3 logger that mimics the historical output
and configuration of wfDebug, wfErrorLog and other global logging functions.
* MediaWiki.Logger.MonologSpi: MediaWiki.Logger.Spi for creating instances
backed by the monolog logging library
* MediaWiki.Logger.Monolog.LegacyHandler: Monolog handler that replicates the
udp2log and file logging functionality of wfErrorLog()
* MediaWiki.Logger.Monolog.WikiProcessor: Monolog log processor that adds host:
wfHostname() and wiki: WikiMap::getCurrentWikiId() to all records
## Globals
* $wgMWLoggerDefaultSpi: Configure which service provider LoggerFactory will
use for creating logger objects.
[PSR-3]: https://www.php-fig.org/psr/psr-3/
[Monolog]: https://github.com/Seldaek/monolog

19
mediawiki/docs/README Normal file
View File

@ -0,0 +1,19 @@
/docs Directory README
======================
The 'docs' directory contain various text files that should help you understand
the most important parts of the code of MediaWiki. More in-depth documentation
can be found at:
https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Code
https://www.mediawiki.org/wiki/Special:MyLanguage/Developer_hub
API documentation is automatically generated and updated daily at:
https://doc.wikimedia.org/mediawiki-core/master/php/
You can get a fresh version using 'make doc' or mwdocgen.php in the
../maintenance/ directory.
For end users, most of the documentation is located online at:
https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents
Documentation for MediaWiki site administrators is at:
https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents

83
mediawiki/docs/Skin.md Normal file
View File

@ -0,0 +1,83 @@
Skins {#skin}
=======
## Core Skins
MediaWiki includes four core skins:
* Vector: The default skin. Introduced in the 1.16 release (2010), it has been
set as the default in MediaWiki since the 1.17 release (2011), replacing
Monobook.
* Monobook: Named after the black-and-white photo of a book in the page
background. Introduced in the 2004 release of 1.3, it had been the
default skin since then, before being replaced by Vector.
* Modern: An attractive blue/grey theme with sidebar and top bar. Derived from
Monobook.
* Cologne Blue: A lightweight skin with minimal formatting. The oldest of the
currently bundled skins, largely rewritten in 2012 while keeping its
appearance.
### Legacy core skins
Several legacy skins were removed in the 1.22 release, as the burden of
supporting them became too heavy to bear. Those were:
* Standard (a.k.a. Classic): The old default skin written by Lee Crocker during
the phase 3 rewrite, in 2002.
* Nostalgia: A skin which looks like Wikipedia did in its first year (2001).
This skin is now used for the old Wikipedia snapshot at
https://nostalgia.wikipedia.org/
* Chick: A lightweight Monobook skin with no sidebar. The sidebar links were
given at the bottom of the page instead.
* Simple: A lightweight skin with a simple white-background sidebar and no top
bar.
* MySkin: Essentially Monobook without the CSS. The idea was that it could be
customised using user-specific or site-wide CSS (see below).
## Custom CSS/JS
It is possible to customise the site CSS and JavaScript without editing any
server-side source files. This is done by editing some pages on the wiki:
* `MediaWiki:Common.css` for skin-independent CSS
* `MediaWiki:Common.js` for skin-independent JavaScript
* `MediaWiki:Vector.css`, `MediaWiki:Monobook.css`, etc. for skin-dependent CSS
* `MediaWiki:Vector.js`, `MediaWiki:Monobook.js`, etc. for skin-dependent
JavaScript
These can also be customised on a per-user basis, by editing
`User:<name>/vector.css`, `User:<name>/vector.js`, etc.
## Custom skins
Several custom skins are available as of 2019. List of all skins is available at
[MediaWiki.org](https://www.mediawiki.org/wiki/Special:MyLanguage/Category:All_skins).
Installing a skin requires adding its files in a subdirectory under `skins/` and
adding an appropriate `wfLoadSkin` line to `LocalSettings.php`, similarly to
how extensions are installed.
You can then make that skin the default by adding:
```php
$wgDefaultSkin = '<name>';
```
Or disable it entirely by removing the `wfLoadSkin` line. (User settings will
not be lost if it's reenabled later.)
See https://www.mediawiki.org/wiki/Manual:Skinning for more information on
writing new skins.
### Legacy custom skins
Until MediaWiki 1.25 it used to be possible to just put a `<name>.php` file in
MediaWiki's `skins/` directory, which would be loaded and expected to contain
the `Skin<name>` class. This way has always been discouraged because of its
limitations (inability to add localisation messages, ResourceLoader modules,
etc.) and awkwardness in managing such skins. For information on migrating skins
using this old method, see
https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery.

68
mediawiki/docs/Title.md Normal file
View File

@ -0,0 +1,68 @@
Title {#title}
========
The MediaWiki software's "Title" class represents article titles, which are used
for many purposes: as the human-readable text title of the article, in the URL
used to access the article, the wikitext link to the article, the key into the
article database, and so on. The class in instantiated from one of these forms
and can be queried for the others, and for other attributes of the title. This
is intended to be an immutable "value" class, so there are no mutator functions.
To get a new instance, call Title::newFromText(). Once instantiated, the
non-static accessor methods can be used, such as getText(), getDBkey(),
getNamespace(), etc. Note that Title::newFromText() may return false if the text
is illegal according to the rules below.
The prefix rules: a title consists of an optional interwiki prefix (such as "m:"
for meta or "de:" for German), followed by an optional namespace, followed by
the remainder of the title. Both interwiki prefixes and namespace prefixes have
the same rules: they contain only letters, digits, space, and underscore, must
start with a letter, are case insensitive, and spaces and underscores are
interchangeable. Prefixes end with a ":". A prefix is only recognized if it is
one of those specifically allowed by the software. For example, "de:name" is a
link to the article "name" in the German Wikipedia, because "de" is recognized
as one of the allowable interwikis. The title "talk:name" is a link to the
article "name" in the "talk" namespace of the current wiki, because "talk" is a
recognized namespace. Both may be present, and if so, the interwiki must
come first, for example, "m:talk:name". If a title begins with a colon as its
first character, no prefixes are scanned for, and the colon is just removed.
Note that because of these rules, it is possible to have articles with colons in
their names. "E. Coli 0157:H7" is a valid title, as is "2001: A Space Odyssey",
because "E. Coli 0157" and "2001" are not valid interwikis or namespaces.
It is not possible to have an article whose bare name includes a namespace or
interwiki prefix.
An initial colon in a title listed in wiki text may however suppress special
handling for interlanguage links, image links, and category links. It is also
used to indicate the main namespace in template inclusions.
Once prefixes have been stripped, the rest of the title processed this way:
* Spaces and underscores are treated as equivalent and each is converted to the
other in the appropriate context (underscore in URL and database keys, spaces
in plain text).
* Multiple consecutive spaces are converted to a single space.
* Leading or trailing space is removed.
* If $wgCapitalLinks is enabled (the default), the first letter is capitalised,
using the capitalisation function of the content language object.
* The unicode characters LRM (U+200E) and RLM (U+200F) are silently stripped.
* Invalid UTF-8 sequences or instances of the replacement character (U+FFFD) are
considered illegal.
* A percent sign followed by two hexadecimal characters is illegal
* Anything that looks like an XML/HTML character reference is illegal
* Any character not matched by the $wgLegalTitleChars regex is illegal
* Zero-length titles (after whitespace stripping) are illegal
All titles except special pages must be less than 255 bytes when encoded with
UTF-8, because that is the size of the database field. Special page titles may
be up to 512 bytes.
Note that Unicode Normal Form C (NFC) is enforced by MediaWiki's user interface
input functions, and so titles will typically be in this form.
getArticleID() needs some explanation: for "internal" articles, it should return
the "page_id" field if the article exists, else it returns 0. For all external
articles it returns 0. All of the IDs for all instances of Title created during
a request are cached, so they can be looked up quickly while rendering wiki text
with lots of internal links. See LinkCache.md.

View File

@ -0,0 +1,31 @@
{
"$schema": "https://json-schema.org/schema#",
"description": "MediaWiki abstract database schema schema",
"type": "object",
"additionalProperties": false,
"properties": {
"comment": {
"type": "string",
"description": "Comment describing the schema change"
},
"before": {
"oneOf": [
{
"type": "object",
"description": "Emtpy object signifying table creation",
"maxProperties": 0
},
{
"type": "object",
"description": "Schema before the change",
"$ref": "abstract-schema-table.json"
}
]
},
"after": {
"type": "object",
"description": "Schema after the change",
"$ref": "abstract-schema-table.json"
}
}
}

View File

@ -0,0 +1,228 @@
{
"$schema": "https://json-schema.org/schema#",
"description": "Abstract description of a mediawiki database table",
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Name of the table"
},
"comment": {
"type": "string",
"description": "Comment describing the table"
},
"columns": {
"type": "array",
"additionalItems": false,
"description": "Columns",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Name of the column"
},
"comment": {
"type": "string",
"description": "Comment describing the column"
},
"type": {
"type": "string",
"description": "Data type of the column",
"enum": [
"bigint",
"binary",
"blob",
"boolean",
"datetimetz",
"decimal",
"float",
"integer",
"mwenum",
"mwtimestamp",
"mwtinyint",
"smallint",
"string",
"text"
]
},
"options": {
"type": "object",
"description": "Additional options",
"additionalProperties": false,
"properties": {
"autoincrement": {
"type": "boolean",
"description": "Indicates if the field should use an autoincremented value if no value was provided",
"default": false
},
"default": {
"type": [
"number",
"string",
"null"
],
"description": "The default value of the column if no value was specified",
"default": null
},
"fixed": {
"type": "boolean",
"description": "Indicates if the column should have a fixed length",
"default": false
},
"length": {
"type": "number",
"description": "Length of the field.",
"default": null,
"minimum": 0
},
"notnull": {
"type": "boolean",
"description": "Indicates whether the column is nullable or not",
"default": true
},
"unsigned": {
"type": "boolean",
"description": "If the column should be an unsigned integer",
"default": false
},
"scale": {
"type": "number",
"description": "Exact number of decimal digits to be stored in a decimal type column",
"default": 0
},
"precision": {
"type": "number",
"description": "Precision of a decimal type column that determines the overall maximum number of digits to be stored (including scale)",
"default": 10
},
"PlatformOptions": {
"type": "object",
"additionalProperties": false,
"properties": {
"version": {
"type": "boolean"
}
}
},
"CustomSchemaOptions": {
"type": "object",
"description": "Custom schema options",
"additionalProperties": false,
"properties": {
"allowInfinite": {
"type": "boolean"
},
"doublePrecision": {
"type": "boolean"
},
"enum_values": {
"type": "array",
"description": "Values to use with type 'mwenum'",
"additionalItems": false,
"items": {
"type": "string"
},
"uniqueItems": true
}
}
}
}
}
},
"required": [
"name",
"type",
"options"
]
}
},
"indexes": {
"type": "array",
"additionalItems": false,
"description": "Indexes",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"name": {
"type": "string",
"description": "Index name"
},
"comment": {
"type": "string",
"description": "Comment describing the index"
},
"columns": {
"type": "array",
"additionalItems": false,
"description": "Columns used by the index",
"items": {
"type": "string"
},
"uniqueItems": true
},
"unique": {
"type": "boolean",
"description": "If the index is unique",
"default": false
},
"flags": {
"type": "array",
"items": {
"type": "string",
"enum": [
"fulltext",
"spatial"
]
},
"uniqueItems": true
},
"options": {
"type": "object",
"properties": {
"lengths": {
"type": "array",
"items": {
"type": [
"number",
"null"
]
},
"minItems": 1
}
}
}
},
"required": [
"name",
"columns",
"unique"
]
}
},
"pk": {
"type": "array",
"additionalItems": false,
"description": "Array of column names used in the primary key",
"items": {
"type": "string"
},
"uniqueItems": true
},
"table_options": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"name",
"columns",
"indexes"
]
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://json-schema.org/schema#",
"description": "MediaWiki abstract database schema schema",
"type": "array",
"additionalItems": false,
"items": {
"$ref": "abstract-schema-table.json"
},
"minItems": 1
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
ContentHandler {#contenthandler}
=====
The *ContentHandler* facility adds support for arbitrary content types on wiki pages, instead of relying on wikitext for everything. It was introduced in MediaWiki 1.21.
Each kind of content ("content model") supported by MediaWiki is identified by unique name. The content model determines how a page's content is rendered, compared, stored, edited, and so on.
Built-in content types are:
* wikitext - wikitext, as usual
* javascript - user provided javascript code
* json - simple implementation for use by extensions, etc.
* css - user provided css code
* text - plain text
In PHP, use the corresponding `CONTENT_MODEL_XXX` constant.
A page's content model is available using the `Title::getContentModel()` method. A page's default model is determined by `ContentHandler::getDefaultModelFor($title)` as follows:
* The global setting `$wgNamespaceContentModels` specifies a content model for the given namespace.
* The hook `ContentHandlerDefaultModelFor` may be used to override the page's default model.
* Pages in `NS_MEDIAWIKI` and `NS_USER` default to the CSS or JavaScript model if they end in .css or .js, respectively. Pages in `NS_MEDIAWIKI` default to the wikitext model otherwise.
* Otherwise, the wikitext model is used.
Note that there is no guarantee that revisions of a page will all have the same content model. To find the content model of the slot of a revision, use `SlotRecord::getModel()` -
the content model of the main slot can for now be assumed to be the content model for the overall revision.
## Architecture
Two class hierarchies are used to provide the functionality associated with the different content models:
* Content interface (and `AbstractContent` base class) define functionality that acts on the concrete content of a page, and
* `ContentHandler` base class provides functionality specific to a content model, but not acting on concrete content.
The most important function of ContentHandler is to act as a factory for the appropriate implementation of Content. These `Content` objects are to be used by MediaWiki everywhere, instead of passing page content around as text. All manipulation and analysis of page content must be done via the appropriate methods of the Content object.
For each content model, a subclass of ContentHandler has to be registered with `$wgContentHandlers`. The ContentHandler object for a given content model can be obtained using `ContentHandler::getForModelID( $id )`. Also `Title` and `WikiPage` now have `getContentHandler()` methods for convenience.
`ContentHandler` objects are singletons that provide functionality specific to the content type, but not directly acting on the content of some page. `ContentHandler::makeEmptyContent()` and `ContentHandler::unserializeContent()` can be used to create a Content object of the appropriate type. However, it is recommended to instead use `WikiPage::getContent()` resp. `RevisionRecord::getContent()` to get a page's content as a Content object. These two methods should be the ONLY way in which page content is accessed.
For `WikiPage::getContent()` the content of the main slot is returned, other slots can be retrieved by using `RevisionRecord::getContent()` and specifying the slot.
Another important function of ContentHandler objects is to define custom action handlers for a content model, see `ContentHandler::getActionOverrides()`. This is similar to what `WikiPage::getActionOverrides()` was already doing.
## Serialization
With the ContentHandler facility, page content no longer has to be text based. Objects implementing the Content interface are used to represent and handle the content internally. For storage and data exchange, each content model supports at least one serialization format via `ContentHandler::serializeContent( $content )`. The list of supported formats for a given content model can be accessed using `ContentHandler::getSupportedFormats()`.
Content serialization formats are identified using MIME type like strings. The following formats are built in:
* text/x-wiki - wikitext
* text/javascript - for js pages
* text/css - for css pages
* text/plain - for future use, e.g. with plain text messages.
* text/html - for future use, e.g. with plain html messages.
* application/vnd.php.serialized - for future use with the api and for extensions
* application/json - for future use with the api, and for use by extensions
* application/xml - for future use with the api, and for use by extensions
In PHP, use the corresponding `CONTENT_FORMAT_XXX` constant.
Note that when using the API to access page content, especially `action=edit`, `action=parse` and `action=query&prop=revisions`, the model and format of the content should always be handled explicitly. Without that information, interpretation of the provided content is not reliable. The same applies to XML dumps generated via `maintenance/dumpBackup.php` or `Special:Export`.
Also note that the API will provide encapsulated, serialized content - so if the API was called with `format=json`, and contentformat is also json (or rather, application/json), the page content is represented as a string containing an escaped json structure. Extensions that use JSON to serialize some types of page content may provide specialized API modules that allow access to that content in a more natural form.
## Compatibility
The ContentHandler facility is introduced in a way that should allow all existing code to keep functioning at least for pages that contain wikitext or other text based content. However, a number of functions and hooks have been deprecated in favor of new versions that are aware of the page's content model, and will now generate warnings when used.
Most importantly, the following functions have been deprecated:
* `Revision::getText()` was deprecated in favor of `Revision::getContent()` (though the Revision class was later fully removed as part of the migration to Multi-Content Revisions (MCR), see [documentation of mediawiki.org][mediawiki.org/wiki/Multi-Content_Revisions].
* `WikiPage::getText()` is deprecated in favor of `WikiPage::getContent()`
Also, the old `Article::getContent()` (which returns text) is superceded by `Article::getContentObject()`. However, both methods should be avoided since they do not provide clean access to the page's actual content. For instance, they may return a system message for non-existing pages. Use `WikiPage::getContent()` instead.
Code that relies on a textual representation of the page content should eventually be rewritten. However, `ContentHandler::getContentText()` provides a stop-gap that can be used to get text for a page. It will return the text for text based content, and null for any other content.
For rendering page content, `Content::getParserOutput()` should be used instead of accessing the parser directly. `WikiPage::makeParserOptions()` can be used to construct appropriate options.
Besides some functions, some hooks have also been replaced by new versions (see hooks.txt for details). These hooks will now trigger a warning when used:
* `ArticleAfterFetchContent` was replaced by `ArticleAfterFetchContentObject`, later replaced by `ArticleRevisionViewCustom`
* `ArticleInsertComplete` was replaced by `PageContentInsertComplete`, later replaced by `PageSaveComplete`
* `ArticleSave` was replaced by `PageContentSave`
* `ArticleSaveComplete` was replaced by `PageContentSaveComplete`, later replaced by `PageSaveComplete`
* `ArticleViewCustom` was replaced by `ArticleContentViewCustom`, which was later removed entirely
* `EditFilterMerged` was replaced by `EditFilterMergedContent`
* `EditPageGetDiffText` was replaced by `EditPageGetDiffContent`
* `EditPageGetPreviewText` was replaced by `EditPageGetPreviewContent`
* `ShowRawCssJs` was deprecated in favor of custom rendering implemented in the respective ContentHandler object.
## Database Storage
Page content is stored in the database using the same mechanism as before. Non-text content is serialized first.
Each revision's content model and serialization format is stored in the revision table (resp. in the archive table, if the revision was deleted). The page's (current) content model (that is, the content model of the latest revision) is also stored in the page table.
Note however that the content model and format is only stored if it differs from the page's default, as determined by `ContentHandler::getDefaultModelFor( $title )`. The default values are represented as `NULL` in the database, to preserve space.
## Globals
There are some new globals that can be used to control the behavior of the ContentHandler facility:
* `$wgContentHandlers` associates content model IDs with the names of the appropriate ContentHandler subclasses or callbacks that create an instance of the appropriate ContentHandler subclass.
* `$wgNamespaceContentModels` maps namespace IDs to a content model that should be the default for that namespace.
## Caveats
There are some changes in behavior that might be surprising to users:
* Javascript and CSS pages are no longer parsed as wikitext (though pre-save transform is still applied). Most importantly, this means that links, including categorization links, contained in the code will not work.
* `action=edit` will fail for pages with non-text content, unless the respective ContentHandler implementation has provided a specialized handler for the edit action. This is true for the API as well.
* `action=raw` will fail for all non-text content. This seems better than serving content in other formats to an unsuspecting recipient. This will also cause client-side diffs to fail.
* File pages provide their own action overrides that do not combine gracefully with any custom handlers defined by a ContentHandler. If for example a File page used a content model with a custom revert action, this would be overridden by WikiFilePage's handler for the revert action.

138
mediawiki/docs/database.md Normal file
View File

@ -0,0 +1,138 @@
Database Access {#database}
=====
*Some information about database access in MediaWiki. By Tim Starling, January 2006.*
## Database Layout
For information about the MediaWiki database layout, such as a description of the tables and their contents, please see:
* [The manual](https://www.mediawiki.org/wiki/Manual:Database_layout)
* [Abstract schema of MediaWiki core](https://gerrit.wikimedia.org/g/mediawiki/core/+/refs/heads/master/maintenance/tables.json)
* [MySQL schema (automatically generated)](https://gerrit.wikimedia.org/g/mediawiki/core/+/refs/heads/master/maintenance/tables-generated.sql)
## API
To make a read query, something like this usually suffices:
```php
use MediaWiki\MediaWikiServices;
$dbProvider = MediaWikiServices::getInstance()->getConnectionProvider();
...
$dbr = $dbProvider->getReplicaDatabase();
$res = $dbr->newSelectQueryBuilder()
->select( /* ... see docs... */ )
// ...see docs for other methods...
->fetchResultSet();
foreach( $res as $row ) {
...
}
```
For a write query, use something like:
```php
$dbw = $dbProvider->getPrimaryDatabase();
$dbw->newInsertQueryBuilder()
->insertInto( /* ...see docs... */ )
// ...see docs for other methods...
->execute();
```
We use the convention `$dbr` for read and `$dbw` for write to help you keep track of whether the database object is a replica (read-only) or a primary (read/write). If you write to a replica, the world will explode. Or to be precise, a subsequent write query which succeeded on the primary may fail when propagated to the replica due to a unique key collision. Replication will then stop and it may take hours to repair the database and get it back online. Setting `read_only` in `my.cnf` on the replica will avoid this scenario, but given the dire consequences, we prefer to have as many checks as possible.
We provide a `query()` function for raw SQL, but the query builders like `SelectQueryBuilder` and `InsertQueryBuilder` are usually more convenient. They take care of things like table prefixes and escaping for you. If you really need to make your own SQL, please read the documentation for `tableName()` and `addQuotes()`. You will need both of them.
## Basic query optimisation
MediaWiki developers who need to write DB queries should have some understanding of databases and the performance issues associated with them. Patches containing unacceptably slow features will not be accepted. Unindexed queries are generally not welcome in MediaWiki, except in special pages derived from `QueryPage`. It's a common pitfall for new developers to submit code containing SQL queries which examine huge numbers of rows. Remember that `COUNT(*)` is **O(N)**, counting rows in a table is like counting beans in a bucket.
## Replication
The largest installation of MediaWiki, Wikimedia, uses a large set of replica MySQL servers replicating writes made to a primary MySQL server. It is important to understand the issues associated with this setup if you want to write code destined for Wikipedia.
It's often the case that the best algorithm to use for a given task depends on whether or not replication is in use. Due to our unabashed Wikipedia-centrism, we often just use the replication-friendly version, but if you like, you can use `LoadBalancer::getServerCount() > 1` to check to see if replication is in use.
## Lag
Lag primarily occurs when large write queries are sent to the primary. Writes are executed in parallel on the primary, but they are executed serially when replicated to the replicas. The primary database may not write its query to the binlog for replication, until after the transaction is committed. The replicas poll this binlog, and apply the query locally as soon as it appears there. They can respond to reads while they are applying the replicated writes, but will not read anything more from the binlog and thus will perform no more writes. This means that if the write query runs for a long time, the replicas will lag behind the primary for as long as it takes for the write query to complete.
Lag can be exacerbated by high read load. MediaWiki's LoadBalancer will avoid sending reads to a replica lagged by more than a few seconds.
MediaWiki does its best for multiple queries during a given web request to represent a single consistent snapshot of the database at a given point in time. In addition to this, MediaWiki tries to ensure that a user sees the wiki change in chronological order, such as subsequent web requests see the same or newer data. In particular, it tries to ensure that the user's own actions are immediately reflected in subsequent requests. This is done by saving the primary's binlog position after a database write, and during subsequent connections to a replica it will wait as-needed to catch up to that position before sending any read queries.
If the wait for chronology protection times out, or more generally if a queried replica is lagged by more than 6 seconds (`LoadBalancer::MAX_LAG_DEFAULT`, configurable via `max lag` in `$wgLBFactoryConf`), the MediaWiki request is considered to be in "lagged replica mode" (`ILBFactory::laggedReplicaUsed`). In this mode, MediaWiki automatically shortens the expiry of object caching (via WANObjectCache) and HTTP/CDN caching to ensure that any stale data will soon converge.
## Lag avoidance
To avoid excessive lag, queries which write large numbers of rows should be split up, generally to write one row at a time. Multi-row `INSERT ... SELECT` queries are the worst offenders should be avoided altogether. Instead do the select first and then the insert.
## Working with lag
Despite our best efforts, it's not practical to guarantee a low-lag environment. Lag will usually be less than one second, but may occasionally be up to 30 seconds. For scalability, it's very important to keep load on the primary low, so simply sending all your queries to the primary is not the answer. So when you have a genuine need for up-to-date data, the following approach is advised:
1) Do a quick query to the primary for a sequence number or timestamp
2) Run the full query on the replica and check if it matches the data you got
from the primary
3) If it doesn't, run the full query on the primary
To avoid swamping the primary every time the replicas lag, use of this approach should be kept to a minimum. In most cases you should just read from the replica and let the user deal with the delay.
## Lock contention
Due to the high write rate on Wikipedia (and some other wikis), MediaWiki developers need to be very careful to structure their writes to avoid long-lasting locks. By default, MediaWiki opens a transaction at the first query, and commits it before the output is sent. Locks will be held from the time when the query is done until the commit. So you can reduce lock time by doing as much processing as possible before you do your write queries.
Often this approach is not good enough, and it becomes necessary to enclose small groups of queries in their own transaction. Use the following syntax:
```php
$dbw = $dbProvider->getPrimaryDatabase();
$dbw->begin( __METHOD__ );
/* Do queries */
$dbw->commit( __METHOD__ );
```
Use of locking reads (e.g. the `FOR UPDATE` clause) is not advised. They are poorly implemented in InnoDB and will cause regular deadlock errors. It's also surprisingly easy to cripple the wiki with lock contention.
Instead of locking reads, combine your existence checks into your write queries, by using an appropriate condition in the `WHERE` clause of an `UPDATE`, or by using unique indexes in combination with `INSERT IGNORE`. Then use the affected row count to see if the query succeeded.
## Query groups
MediaWiki supports database query groups, a way to indicate a preferred group of database hosts to use for a given query. Query groups are only supported for connections to child (non-primary) databases, making them only viable for read operations. It should be noted that using query groups does not _guarantee_ a given group of hosts will be used, but rather that the query prefers such group. Making use of query groups can be beneficial in many cases.
One benefit is a reduction of cache misses. Directing reads for a category of queries (e.g. all logging queries) to a given host can result in more deterministic and faster performing queries.
Another benefit is that it allows high-traffic wikis to configure some of their database hosts to handle some types of queries more optimally than others. For example, optimizing with different table indices for faster performance.
Query groups are especially beneficial for queries expected to have a long execution time. Such queries can exhaust a database of its resources (e.g. cache space and I/O time), so targeting a specific group of hosts prevents more urgent queries from suffering a performance decrease.
Additionally, expensive queries can delay database maintenance operations which may increase latency for other queries.
For example, while a database read is executing, if other queries have performed updates to any tables those tables must retain all stale versions of its rows until the read is complete. Now, other potentially unrelated queries must now spend additional time scanning over obsolete rows that are waiting to be purged. Directing these long running queries to dedicated hosts helps prevent other queries in suffering a performance hit.
MediaWiki currently supports the following query groups:
* `api`: For queries specific to api.php requests. This is set via ApiBase::getDB(). Note that most queries from api.php are performed via re-used service classes that are not specific to api.php, and thus don't use this query group.
* `dump`: For dump-related CLI maintenance scripts (also known as "export" or "snapshot"). These scripts set the query group at the process level, and thus affect all queries, including fast/general ones.
* `vslow`: For queries that are expected to have a long execution time (e.g. more than one second when run against the largest wiki). These tend to be unoptimized queries that are unable to use an index, and thus get slower the more rows a table contains instead of staying relatively constant. These should be limited to jobs and maintenance scripts, or guarded by `$wgMiserMode`.
The below is how you specify a query group when obtaining a connection:
```php
$dbProvider = MediaWikiServices::getInstance()->getConnectionProvider();
$dbProvider->getReplicaDatabase( false, 'vslow' );
```
## Supported DBMSs
MediaWiki is written primarily for use with MySQL. Queries are optimized for it and its schema is considered the canonical version. However, MediaWiki does support the following other DBMSs to varying degrees:
* PostgreSQL
* SQLite
More information can be found about each of these databases (known issues, level of support, extra configuration) in the `databases` subdirectory in this folder.
## Use of `GROUP BY`
MySQL supports `GROUP BY` without checking anything in the `SELECT` clause. Other DBMSs (especially Postgres) are stricter and require that all the non-aggregate items in the `SELECT` clause appear in the `GROUP BY`. For this reason, it is highly discouraged to use `SELECT *` with `GROUP BY` queries.

View File

@ -0,0 +1,40 @@
This document describes the state of Postgres support in MediaWiki.
== Overview ==
Support for PostgreSQL has been available since version 1.7
of MediaWiki, and is fairly well maintained. The main code
is very well integrated, while extensions are very hit and miss.
Still, it is probably the most supported database after MySQL.
Much of the work in making MediaWiki database-agnostic came
about through the work of creating Postgres support.
== Required versions ==
The current minimum version of PostgreSQL for MediaWiki is 10.
== Database schema ==
PostgreSQL schema is automatically generated from the abstract schema.
You can see the generated schema in maintenance/postgres/tables-generated.sql
For more information on abstract schema see:
https://www.mediawiki.org/wiki/Manual:Schema_changes
== MySQL differences ==
The major differences between MySQL and Postgres are represented as
methods in the Database class. For example, implicitGroupby() is
true for MySQL and false for Postgres. This means that in those
places where the code does not add all the non-aggregate items
from the SELECT clause to the GROUP BY, we can add them in, but in
a conditional manner with the above method, as simply adding them
all in to the main query may cause performance problems with
MySQL.
== Getting help ==
In addition to the normal venues (MediaWiki mailing lists
and IRC channels), the #postgresql channel on irc.libera.chat
is a friendly and expert resource if you should encounter a
problem with your Postgres-enabled MediaWiki.

View File

@ -0,0 +1,18 @@
SQLite schema is automatically generated from the abstract schema.
You can see the generated schema in maintenance/sqlite/tables-generated.sql
For more information on abstract schema see:
https://www.mediawiki.org/wiki/Manual:Schema_changes
SQLite in MediaWiki also accepts MySQL and there are a set of compatibility
regexes to convert MySQL syntax to SQLite syntax:
* BINARY() and VARBINARY() fields are converted to BLOB
* the UNSIGNED modifier is removed
* "INT" fields are converted to "INTEGER"
* ENUM is converted to BLOB
* the BINARY collation modifier is removed
* AUTO_INCREMENT is converted to AUTOINCREMENT
* Any table options are removed
* Truncated indexes are upgraded to full-width indexes
* FULLTEXT indexes are converted to ordinary indexes

View File

@ -0,0 +1,36 @@
deferred.txt
A few of the database updates required by various functions here can be
deferred until after the result page is displayed to the user. For example,
updating the view counts, updating the linked-to tables after a save, etc. PHP
does not yet have any way to tell the server to actually return and disconnect
while still running these updates (as a Java servlet could), but it might have
such a feature in the future.
We handle these by creating a deferred-update object and putting those objects
on a global list, then executing the whole list after the page is displayed. We
don't do anything smart like collating updates to the same table or such
because the list is almost always going to have just one item on it, if that,
so it's not worth the trouble.
Since 1.6 there is a 'job queue' in the jobs table, which is used to update
link tables of transcluding pages after edits; this may be extended in the
future to more general background tasks.
Job queue items are fetched out of the queue and run either at a random rate
during regular page views (by default) or by a batch process which can be run
via maintenance/runJobs.php.
Currently there are a few different types of jobs:
refreshLinks
Used to refresh the database tables that store the links between pages.
When a page is changed, all pages using that page are also cleared by
inserting a new job for all those pages. Each job refreshes only one page.
htmlCacheUpdate
Clear caches when a template is changed to ensure that changes can be seen.
Each job clears $wgUpdateRowsPerJob pages (300 by default).
enotifNotify
Used to send mail using the job queue.

View File

@ -0,0 +1,207 @@
This document is intended to provide useful advice for parties seeking to
redistribute MediaWiki to end users. It's targeted particularly at maintainers
for Linux distributions, since it's been observed that distribution packages of
MediaWiki often break. We've consistently had to recommend that users seeking
support use official tarballs instead of their distribution's packages, and
this often solves whatever problem the user is having. It would be nice if
this could change.
== Background: why web applications are different ==
MediaWiki is intended to be usable on any web host that provides support for
PHP and a database. Many users of low-end shared hosting have very limited
access to their machine: often only FTP access to some subdirectory of the web
root. Support for these users entails several restrictions, such as:
1) We cannot require installation of any files outside the web root. Few of
our users have access to directories like /usr or /etc.
2) We cannot require the ability to run any utility on the command line.
Many shared hosts have exec() and similar PHP functions disabled.
3) We cannot assume that the software has write access anywhere useful. The
user account that MediaWiki (including its installer) runs under is often
different from the account the user used to upload the files, and we might be
restricted by PHP settings such as safe mode or open_basedir.
4) We cannot assume that the software even has read access anywhere useful.
Many shared hosts run all users' web applications under the same user, so
they can't rely on Unix permissions, and must forbid reads to even standard
directories like /tmp lest users read each others' files.
5) We cannot assume that the user has the ability to install or run any
programs not written as web-accessible PHP scripts.
Since anything that works on cheap shared hosting will work if you have shell
or root access too, MediaWiki's design is based around catering to the lowest
common denominator. Although we support higher-end setups as well (like
Wikipedia!), the way many things work by default is tailored toward shared
hosting. These defaults are unconventional from the point of view of normal
(non-web) applications -- they might conflict with distributors' policies, and
they certainly aren't ideal for someone who's installing MediaWiki as root.
== Directory structure ==
Because of constraint (1) above, MediaWiki does not conform to normal
Unix filesystem layout. Hopefully we'll offer direct support for standard
layouts in the future, but for now *any change to the location of files is
unsupported*. Moving things and leaving symlinks will *probably* not break
anything, but it is *strongly* advised not to try any more intrusive changes to
get MediaWiki to conform more closely to your filesystem hierarchy. Any such
attempt will almost certainly result in unnecessary bugs.
The standard recommended location to install MediaWiki, relative to the web
root, is /w (so, e.g., /var/www/w). Rewrite rules can then be used to enable
"pretty URLs" like /wiki/Article instead of /w/index.php?title=Article. (This
is the convention Wikipedia uses.) In theory, it should be possible to enable
the appropriate rewrite rules by default, if you can reconfigure the web
server, but you'd need to alter LocalSettings.php too. See
<https://www.mediawiki.org/wiki/Manual:Short_URL> for details on short URLs.
If you really must mess around with the directory structure, note that the
following files *must* all be web-accessible for MediaWiki to function
correctly:
* api.php, img_auth.php, index.php, load.php, opensearch_desc.php, thumb.php.
These are the entry points for normal usage. This list may be
incomplete and is subject to change.
* mw-config/index.php: Used for web-based installation (sets up the database,
prompts for the name of the wiki, etc.).
* images/: Used for uploaded files. This could be somewhere else if
$wgUploadDirectory and $wgUploadPath are changed appropriately.
* skins/*/: Subdirectories of skins/ contain CSS and JavaScript files that
must be accessible to web browsers. The PHP files and Skin.sample in skins/
don't need to be accessible. This could be somewhere else if
$wgStyleDirectory and $wgStylePath are changed appropriately.
* extensions/: Many extensions include CSS and JavaScript files in their
extensions directory, and will break if they aren't web-accessible. Some
extensions might theoretically provide additional entry points as well, at
least in principle.
But all files should keep their position relative to the web-visible
installation directory no matter what. If you must move includes/ somewhere in
/usr/share, provide a symlink from /var/www/w. If you don't, you *will* break
something. You have been warned.
== Configuration ==
MediaWiki is configured using LocalSettings.php. This is a PHP file that's
generated when the user visits mw-config/index.php to install the software, and
which the user can edit by hand thereafter. It's just a plain old PHP file,
and can contain any PHP statements. It usually sets global variables that are
used for configuration, and includes files used by any extensions.
Distributors can easily change the default settings by creating
includes/PlatformSettings.php with overrides/additions to the default settings.
The installer will automatically include the platform defaults when generating
the user's LocalSettings.php file.
Furthermore, distributors can change the installer behavior, by placing their
overrides into mw-config/overrides directory. Doing that is highly preferred
to modifying MediaWiki code directly. See mw-config/overrides/README for more
details and examples.
There's a new maintenance/install.php script which could be used for performing
an install through the command line.
Some configuration options that distributors might be in a position to set
intelligently:
* $wgEmergencyContact: An e-mail address that can be used to contact the wiki
administrator. By default, "wikiadmin@ServerName".
* $wgPasswordSender: The e-mail address to use when sending password e-mails.
By default, "MediaWiki Mail <apache@ServerName>".
(with ServerName guessed from the http request)
* $wgSMTP: Can be configured to use SMTP for mail sending instead of PHP
mail().
== Updates ==
The correct way for updating a wiki is to update the files and then run from
command line the maintenance/update.php script (with appropriate parameters if
files were moved). It will perform all the needed steps to update the database
schema and contents to the version from whatever old one it has.
Any package manager which replaces the files but doesn't update the db is leaving
an inconsistent wiki that may produce blank pages (php errors) when new features
using the changed schema would be used.
Since MediaWiki 1.17 it is possible to upgrade using the web installer by providing
an arbitrary secret value stored as $wgUpgradeKey in LocalSettings (older versions
needed to rename LocalSettings.php in order to upgrade using the installer).
== Documentation ==
MediaWiki's official documentation is split between two places: the source
code, and <https://www.mediawiki.org/>. The source code documentation is written
exclusively by developers, and so is likely to be reliable (at worst,
outdated). However, it can be pretty sparse. mediawiki.org documentation is
often much more thorough, but it's maintained by a wiki that's open to
anonymous edits, so its quality is sometimes sketchy -- don't assume that
anything there is officially endorsed!
== Upstream ==
MediaWiki is a project hosted and led by the Wikimedia Foundation, the
not-for-profit charity that operates Wikipedia. Wikimedia employs the lead
developer and several other paid developers, but commit access is given out
liberally and there are multiple very active volunteer developers as well. A
list of developers can be found at <https://www.mediawiki.org/wiki/Developers>.
MediaWiki's bug tracker is at <https://phabricator.wikimedia.org>. However, you
might find that the best place to post if you want to get developers' attention
is the wikitech-l mailing list
<https://lists.wikimedia.org/postorius/lists/wikitech-l.lists.wikimedia.org/>.
Posts to wikitech-l will inevitably be read by multiple experienced MediaWiki
developers. There's also an active IRC chat at <irc://irc.libera.chat/mediawiki>,
where there are usually several developers at reasonably busy times of day.
Our Git repositories are hosted at <https://gerrit.wikimedia.org>, see
<https://www.mediawiki.org/wiki/Gerrit> for more information. Patches should
be submitted there. If you know which developers are best suited to review your
patch, add them to it, otherwise ask on IRC to get better review time.
All redistributors of MediaWiki should be subscribed to mediawiki-announce
<https://lists.wikimedia.org/postorius/lists/mediawiki-announce.lists.wikimedia.org/>.
It's extremely low-traffic, with an average of less than one post per month.
All new releases are announced here, including critical security updates.
== Useful software to install ==
There are several other pieces of software that MediaWiki can make good use of.
Distributors might choose to install these automatically with MediaWiki and
perhaps configure it to use them (see Configuration section of this document):
* APC (Alternative PHP Cache) or similar: Will greatly speed up the
execution of MediaWiki, and all other PHP applications, at some cost in
memory usage. Will be used automatically for the most part.
* clamav: Can be used for virus scanning of uploaded files. Enable with
"$wgAntivirus = 'clamav';".
* DjVuLibre: Allows processing of DjVu files. To enable this, set
"$wgDjvuDump = 'djvudump'; $wgDjvuRenderer = 'ddjvu'; $wgDjvuTxt = 'djvutxt';".
* ImageMagick: For resizing images. "$wgUseImageMagick = true;" will enable
it. PHP's GD can also be used, but ImageMagick is preferable.
* HTTP cache such as Varnish or Squid: can provide a drastic speedup and a
major cut in resource consumption, but enabling it may interfere with other
applications. It might be suitable for a separate package. For setup details, see:
- <https://www.mediawiki.org/wiki/Manual:Varnish_caching>
- <https://www.mediawiki.org/wiki/Manual:Squid_caching>
* rsvg or other SVG rasterizer: ImageMagick can be used for SVG support, but
is not ideal. Wikipedia (as of the time of this writing) uses rsvg. To
enable, set "$wgSVGConverter = 'rsvg';" (or other as appropriate).
MediaWiki uses some standard GNU utilities as well, such as diff and diff3. If
these are present in /usr/bin or some other reasonable location, they will be
configured automatically on install.
MediaWiki also has a "job queue" that handles background processing. Because
shared hosts often don't provide access to cron, the job queue is run on every
page view by default. This means the background tasks aren't really done in
the background. Busy wikis can set $wgJobRunRate to 0 and run
maintenance/runJobs.php periodically out of cron. Distributors probably
shouldn't set this up as a default, however, since the extra cron job is
unnecessary overhead for a little-used wiki.
== Web server configuration ==
MediaWiki includes several .htaccess files to restrict access to some
directories. If the web server is not configured to support these files, and
the relevant directories haven't been moved someplace inaccessible anyway (e.g.
symlinked in /usr/share with the web server configured to not follow symlinks),
then it might be useful to deny web access to those directories in the web
server's configuration.

View File

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.1.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.1/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.1/"
targetNamespace="http://www.mediawiki.org/xml/export-0.1/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision data -->
<element name="revision" type="mw:RevisionType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="string" minOccurs="0"/>
<element name="text" type="string"/>
</sequence>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,294 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
Version 0.7 adds a unique identity constraint for both page and
revision identifiers. See also bug 4220.
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
Version 0.8 adds support for a <model> and a <format> tag for
each revision. See contenthandler.md.
Version 0.9 adds the database name to the site information.
Version 0.10 moved the <model> and <format> tags before the <text> tag.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.10.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.10/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.10/"
targetNamespace="http://www.mediawiki.org/xml/export-0.10/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd" />
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType">
<!-- Page ID contraint, see bug 4220 -->
<unique name="PageIDConstraint">
<selector xpath="mw:page" />
<field xpath="mw:id" />
</unique>
<!-- Revision ID contraint, see bug 4220 -->
<unique name="RevIDConstraint">
<selector xpath="mw:page/mw:revision" />
<field xpath="mw:id" />
</unique>
</element>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1" />
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded" />
<element name="logitem" type="mw:LogItemType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="required" />
<attribute ref="xml:lang" use="required" />
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="dbname" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="RedirectType">
<simpleContent>
<extension base="string">
<attribute name="title" type="string" />
</extension>
</simpleContent>
</complexType>
<simpleType name="ContentModelType">
<restriction base="string">
<pattern value="[a-zA-Z][-+./a-zA-Z0-9]*" />
</restriction>
</simpleType>
<simpleType name="ContentFormatType">
<restriction base="string">
<pattern value="[a-zA-Z][-+.a-zA-Z0-9]*/[a-zA-Z][-+.a-zA-Z0-9]*" />
</restriction>
</simpleType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string" />
<!-- Namespace in canonical form -->
<element name="ns" type="nonNegativeInteger" />
<!-- optional page ID number -->
<element name="id" type="positiveInteger" />
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0" />
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="parentid" type="positiveInteger" minOccurs="0" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="minor" minOccurs="0" maxOccurs="1" />
<element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
<element name="model" type="mw:ContentModelType" />
<element name="format" type="mw:ContentFormatType" />
<element name="text" type="mw:TextType" />
<element name="sha1" type="string" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="mw:CommentType" minOccurs="0" />
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
<element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
<element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" use="optional" type="NMTOKEN" />
<attribute name="bytes" use="optional" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogTextType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogParamsType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="string" minOccurs="0" />
<!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
<element name="filename" type="string" />
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI" />
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,336 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
Version 0.7 adds a unique identity constraint for both page and
revision identifiers. See also bug 4220.
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
Version 0.8 adds support for a <model> and a <format> tag for
each revision. See contenthandler.md.
Version 0.9 adds the database name to the site information.
Version 0.10 moved the <model> and <format> tags before the <text> tag.
Version 0.11 introduced <content> tag.
The canonical URL to the schema document is:
https://www.mediawiki.org/xml/export-0.11.xsd
Use the namespace:
https://www.mediawiki.org/xml/export-0.11/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.11/"
targetNamespace="http://www.mediawiki.org/xml/export-0.11/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd" />
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType">
<!-- Page ID contraint, see bug 4220 -->
<unique name="PageIDConstraint">
<selector xpath="mw:page" />
<field xpath="mw:id" />
</unique>
<!-- Revision ID contraint, see bug 4220 -->
<unique name="RevIDConstraint">
<selector xpath="mw:page/mw:revision" />
<field xpath="mw:id" />
</unique>
</element>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1" />
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded" />
<element name="logitem" type="mw:LogItemType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="required" />
<attribute ref="xml:lang" use="required" />
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="dbname" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="RedirectType">
<simpleContent>
<extension base="string">
<attribute name="title" type="string" />
</extension>
</simpleContent>
</complexType>
<simpleType name="ContentModelType">
<restriction base="string">
<pattern value="[a-zA-Z][-+./a-zA-Z0-9]*" />
</restriction>
</simpleType>
<simpleType name="ContentFormatType">
<restriction base="string">
<pattern value="[a-zA-Z][-+.a-zA-Z0-9]*/[a-zA-Z][-+.a-zA-Z0-9]*" />
</restriction>
</simpleType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string" />
<!-- Namespace in canonical form -->
<element name="ns" type="nonNegativeInteger" />
<!-- optional page ID number -->
<element name="id" type="positiveInteger" />
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0" />
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="parentid" type="positiveInteger" minOccurs="0" maxOccurs="1" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="minor" minOccurs="0" maxOccurs="1" />
<element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
<!-- corresponds to slot origin for the main slot -->
<element name="origin" type="positiveInteger" />
<!-- the main slot's content model -->
<element name="model" type="mw:ContentModelType" />
<!-- the main slot's serialization format -->
<element name="format" type="mw:ContentFormatType" />
<!-- the main slot's serialized content -->
<element name="text" type="mw:TextType"/>
<element name="content" type="mw:ContentType" minOccurs="0" maxOccurs="unbounded"/>
<!-- sha1 of the revision, a combined sha1 of content in all slots -->
<element name="sha1" type="string" />
</sequence>
</complexType>
<complexType name="ContentType">
<sequence>
<!-- corresponds to slot role_name -->
<element name="role" type="mw:SlotRoleType" />
<!-- corresponds to slot origin -->
<element name="origin" type="positiveInteger" />
<element name="model" type="mw:ContentModelType" />
<element name="format" type="mw:ContentFormatType" />
<element name="text" type="mw:ContentTextType" />
</sequence>
</complexType>
<simpleType name="SlotRoleType">
<restriction base="string">
<pattern value="[a-zA-Z][-+./a-zA-Z0-9]*" />
</restriction>
</simpleType>
<complexType name="ContentTextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" type="mw:DeletedFlagType" />
<attribute name="location" type="anyURI" />
<attribute name="sha1" type="string" />
<attribute name="bytes" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="mw:CommentType" minOccurs="0" />
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
<element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
<element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" type="mw:DeletedFlagType" />
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN" />
<attribute name="location" type="anyURI" />
<attribute name="sha1" type="string"/>
<attribute name="bytes" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogTextType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogParamsType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" type="mw:DeletedFlagType" />
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="string" minOccurs="0" />
<!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
<element name="filename" type="string" />
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI" />
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,100 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.2.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.2/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.2/"
targetNamespace="http://www.mediawiki.org/xml/export-0.2/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="string" minOccurs="0"/>
<element name="text" type="string"/>
</sequence>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,154 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.3.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.3/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.3/"
targetNamespace="http://www.mediawiki.org/xml/export-0.3/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have two titles differing only by case. -->
<!-- Not yet implemented as of MediaWiki 1.5 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="string" minOccurs="0"/>
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,216 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.4.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.4/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.4/"
targetNamespace="http://www.mediawiki.org/xml/export-0.4/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted"/>
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- flag if the current revision is a redirect -->
<element name="redirect" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
<element name="logitem" type="mw:LogItemType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN"/>
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,219 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.5.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.5/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.5/"
targetNamespace="http://www.mediawiki.org/xml/export-0.5/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted"/>
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- flag if the current revision is a redirect -->
<element name="redirect" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
<element name="logitem" type="mw:LogItemType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN"/>
<attribute name="bytes" use="optional" type="nonNegativeInteger"/>
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.6.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.6/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.6/"
targetNamespace="http://www.mediawiki.org/xml/export-0.6/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType"/>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1"/>
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded"/>
</sequence>
<attribute name="version" type="string" use="required"/>
<attribute ref="xml:lang" use="required"/>
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted"/>
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string"/>
<!-- Namespace in canonical form -->
<element name="ns" type="positiveInteger"/>
<!-- optional page ID number -->
<element name="id" type="positiveInteger" minOccurs="0"/>
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="string" minOccurs="0"/>
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0"/>
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
<element name="logitem" type="mw:LogItemType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="minor" minOccurs="0" />
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="sha1" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" minOccurs="0"/>
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="mw:CommentType" minOccurs="0"/>
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN"/>
<attribute name="bytes" use="optional" type="nonNegativeInteger"/>
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0"/>
<element name="id" type="positiveInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0"/>
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType"/>
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime"/>
<element name="contributor" type="mw:ContributorType"/>
<element name="comment" type="string" minOccurs="0"/>
<!-- Filename. (Using underscores, not spaces. No 'Image:' namespace marker.) -->
<element name="filename" type="string"/>
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI"/>
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,272 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
Version 0.7 adds a unique identity constraint for both page and
revision identifiers. See also bug 4220.
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.7.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.7/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.7/"
targetNamespace="http://www.mediawiki.org/xml/export-0.7/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd" />
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType">
<!-- Page ID contraint, see bug 4220 -->
<unique name="PageIDConstraint">
<selector xpath="mw:page" />
<field xpath="mw:id" />
</unique>
<!-- Revision ID contraint, see bug 4220 -->
<unique name="RevIDConstraint">
<selector xpath="mw:page/mw:revision" />
<field xpath="mw:id" />
</unique>
</element>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1" />
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded" />
<element name="logitem" type="mw:LogItemType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="required" />
<attribute ref="xml:lang" use="required" />
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="RedirectType">
<simpleContent>
<extension base="string">
<attribute name="title" type="string" />
</extension>
</simpleContent>
</complexType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string" />
<!-- Namespace in canonical form -->
<element name="ns" type="nonNegativeInteger" />
<!-- optional page ID number -->
<element name="id" type="positiveInteger" />
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0" />
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="parentid" type="positiveInteger" minOccurs="0" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="minor" minOccurs="0" maxOccurs="1" />
<element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
<element name="sha1" type="string" />
<element name="text" type="mw:TextType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="mw:CommentType" minOccurs="0" />
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
<element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
<element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN" />
<attribute name="bytes" use="optional" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogTextType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogParamsType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="string" minOccurs="0" />
<!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
<element name="filename" type="string" />
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI" />
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,289 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
Version 0.7 adds a unique identity constraint for both page and
revision identifiers. See also bug 4220.
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
Version 0.8 adds support for a <model> and a <format> tag for
each revision. See contenthandler.md.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.8.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.8/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.8/"
targetNamespace="http://www.mediawiki.org/xml/export-0.8/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd" />
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType">
<!-- Page ID contraint, see bug 4220 -->
<unique name="PageIDConstraint">
<selector xpath="mw:page" />
<field xpath="mw:id" />
</unique>
<!-- Revision ID contraint, see bug 4220 -->
<unique name="RevIDConstraint">
<selector xpath="mw:page/mw:revision" />
<field xpath="mw:id" />
</unique>
</element>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1" />
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded" />
<element name="logitem" type="mw:LogItemType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="required" />
<attribute ref="xml:lang" use="required" />
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="RedirectType">
<simpleContent>
<extension base="string">
<attribute name="title" type="string" />
</extension>
</simpleContent>
</complexType>
<simpleType name="ContentModelType">
<restriction base="string">
<pattern value="[a-zA-Z][-+./a-zA-Z0-9]*" />
</restriction>
</simpleType>
<simpleType name="ContentFormatType">
<restriction base="string">
<pattern value="[a-zA-Z][-+.a-zA-Z0-9]*/[a-zA-Z][-+.a-zA-Z0-9]*" />
</restriction>
</simpleType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string" />
<!-- Namespace in canonical form -->
<element name="ns" type="nonNegativeInteger" />
<!-- optional page ID number -->
<element name="id" type="positiveInteger" />
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0" />
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="parentid" type="positiveInteger" minOccurs="0" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="minor" minOccurs="0" maxOccurs="1" />
<element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
<element name="text" type="mw:TextType" />
<element name="sha1" type="string" />
<element name="model" type="mw:ContentModelType" />
<element name="format" type="mw:ContentFormatType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="mw:CommentType" minOccurs="0" />
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
<element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
<element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN" />
<attribute name="bytes" use="optional" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogTextType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogParamsType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="string" minOccurs="0" />
<!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
<element name="filename" type="string" />
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI" />
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,292 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
output by MediaWiki's Special:Export system.
Version 0.2 adds optional basic file upload info support,
which is used by our OAI export/import submodule.
Version 0.3 adds some site configuration information such
as a list of defined namespaces.
Version 0.4 adds per-revision delete flags, log exports,
discussion threading data, a per-page redirect flag, and
per-namespace capitalization.
Version 0.5 adds byte count per revision.
Version 0.6 adds a separate namespace tag, and resolves the
redirect target and adds a separate sha1 tag for each revision.
Version 0.7 adds a unique identity constraint for both page and
revision identifiers. See also bug 4220.
Fix type for <ns> from "positiveInteger" to "nonNegativeInteger" to allow 0
Moves <logitem> to its right location.
Add parentid to revision.
Fix type for <id> within <contributor> to "nonNegativeInteger"
Version 0.8 adds support for a <model> and a <format> tag for
each revision. See contenthandler.md.
Version 0.9 adds the database name to the site information.
The canonical URL to the schema document is:
http://www.mediawiki.org/xml/export-0.9.xsd
Use the namespace:
http://www.mediawiki.org/xml/export-0.9/
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mw="http://www.mediawiki.org/xml/export-0.9/"
targetNamespace="http://www.mediawiki.org/xml/export-0.9/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's page export format
</documentation>
</annotation>
<!-- Need this to reference xml:lang -->
<import namespace="http://www.w3.org/XML/1998/namespace"
schemaLocation="http://www.w3.org/2001/xml.xsd" />
<!-- Our root element -->
<element name="mediawiki" type="mw:MediaWikiType">
<!-- Page ID contraint, see bug 4220 -->
<unique name="PageIDConstraint">
<selector xpath="mw:page" />
<field xpath="mw:id" />
</unique>
<!-- Revision ID contraint, see bug 4220 -->
<unique name="RevIDConstraint">
<selector xpath="mw:page/mw:revision" />
<field xpath="mw:id" />
</unique>
</element>
<complexType name="MediaWikiType">
<sequence>
<element name="siteinfo" type="mw:SiteInfoType"
minOccurs="0" maxOccurs="1" />
<element name="page" type="mw:PageType"
minOccurs="0" maxOccurs="unbounded" />
<element name="logitem" type="mw:LogItemType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="required" />
<attribute ref="xml:lang" use="required" />
</complexType>
<complexType name="SiteInfoType">
<sequence>
<element name="sitename" type="string" minOccurs="0" />
<element name="dbname" type="string" minOccurs="0" />
<element name="base" type="anyURI" minOccurs="0" />
<element name="generator" type="string" minOccurs="0" />
<element name="case" type="mw:CaseType" minOccurs="0" />
<element name="namespaces" type="mw:NamespacesType" minOccurs="0" />
</sequence>
</complexType>
<simpleType name="CaseType">
<restriction base="NMTOKEN">
<!-- Cannot have two titles differing only by case of first letter. -->
<!-- Default behavior through 1.5, $wgCapitalLinks = true -->
<enumeration value="first-letter" />
<!-- Complete title is case-sensitive -->
<!-- Behavior when $wgCapitalLinks = false -->
<enumeration value="case-sensitive" />
<!-- Cannot have non-case senstitive titles eg [[FOO]] == [[Foo]] -->
<!-- Not yet implemented as of MediaWiki 1.18 -->
<enumeration value="case-insensitive" />
</restriction>
</simpleType>
<simpleType name="DeletedFlagType">
<restriction base="NMTOKEN">
<enumeration value="deleted" />
</restriction>
</simpleType>
<complexType name="NamespacesType">
<sequence>
<element name="namespace" type="mw:NamespaceType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
</complexType>
<complexType name="NamespaceType">
<simpleContent>
<extension base="string">
<attribute name="key" type="integer" />
<attribute name="case" type="mw:CaseType" />
</extension>
</simpleContent>
</complexType>
<complexType name="RedirectType">
<simpleContent>
<extension base="string">
<attribute name="title" type="string" />
</extension>
</simpleContent>
</complexType>
<simpleType name="ContentModelType">
<restriction base="string">
<pattern value="[a-zA-Z][-+./a-zA-Z0-9]*" />
</restriction>
</simpleType>
<simpleType name="ContentFormatType">
<restriction base="string">
<pattern value="[a-zA-Z][-+.a-zA-Z0-9]*/[a-zA-Z][-+.a-zA-Z0-9]*" />
</restriction>
</simpleType>
<complexType name="PageType">
<sequence>
<!-- Title in text form. (Using spaces, not underscores; with namespace ) -->
<element name="title" type="string" />
<!-- Namespace in canonical form -->
<element name="ns" type="nonNegativeInteger" />
<!-- optional page ID number -->
<element name="id" type="positiveInteger" />
<!-- flag if the current revision is a redirect -->
<element name="redirect" type="mw:RedirectType" minOccurs="0" maxOccurs="1" />
<!-- comma-separated list of string tokens, if present -->
<element name="restrictions" type="string" minOccurs="0" />
<!-- Zero or more sets of revision or upload data -->
<choice minOccurs="0" maxOccurs="unbounded">
<element name="revision" type="mw:RevisionType" />
<element name="upload" type="mw:UploadType" />
</choice>
<!-- Zero or One sets of discussion threading data -->
<element name="discussionthreadinginfo" minOccurs="0" maxOccurs="1" type="mw:DiscussionThreadingInfo" />
</sequence>
</complexType>
<complexType name="RevisionType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="parentid" type="positiveInteger" minOccurs="0" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="minor" minOccurs="0" maxOccurs="1" />
<element name="comment" type="mw:CommentType" minOccurs="0" maxOccurs="1" />
<element name="text" type="mw:TextType" />
<element name="sha1" type="string" />
<element name="model" type="mw:ContentModelType" />
<element name="format" type="mw:ContentFormatType" />
</sequence>
</complexType>
<complexType name="LogItemType">
<sequence>
<element name="id" type="positiveInteger" />
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="mw:CommentType" minOccurs="0" />
<element name="type" type="string" />
<element name="action" type="string" />
<element name="text" type="mw:LogTextType" minOccurs="0" maxOccurs="1" />
<element name="logtitle" type="string" minOccurs="0" maxOccurs="1" />
<element name="params" type="mw:LogParamsType" minOccurs="0" maxOccurs="1" />
</sequence>
</complexType>
<complexType name="CommentType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="TextType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
<!-- This isn't a good idea; we should be using "ID" instead of "NMTOKEN" -->
<!-- However, "NMTOKEN" is strictest definition that is both compatible with existing -->
<!-- usage ([0-9]+) and with the "ID" type. -->
<attribute name="id" type="NMTOKEN" />
<attribute name="bytes" use="optional" type="nonNegativeInteger" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogTextType">
<simpleContent>
<extension base="string">
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</extension>
</simpleContent>
</complexType>
<complexType name="LogParamsType">
<simpleContent>
<extension base="string">
<attribute ref="xml:space" use="optional" default="preserve" />
</extension>
</simpleContent>
</complexType>
<complexType name="ContributorType">
<sequence>
<element name="username" type="string" minOccurs="0" />
<element name="id" type="nonNegativeInteger" minOccurs="0" />
<element name="ip" type="string" minOccurs="0" />
</sequence>
<!-- This allows deleted=deleted on non-empty elements, but XSD is not omnipotent -->
<attribute name="deleted" use="optional" type="mw:DeletedFlagType" />
</complexType>
<complexType name="UploadType">
<sequence>
<!-- Revision-style data... -->
<element name="timestamp" type="dateTime" />
<element name="contributor" type="mw:ContributorType" />
<element name="comment" type="string" minOccurs="0" />
<!-- Filename. (Using underscores, not spaces. No 'File:' namespace marker.) -->
<element name="filename" type="string" />
<!-- URI at which this resource can be obtained -->
<element name="src" type="anyURI" />
<element name="size" type="positiveInteger" />
<!-- TODO: add other metadata fields -->
</sequence>
</complexType>
<!-- Discussion threading data for LiquidThreads -->
<complexType name="DiscussionThreadingInfo">
<sequence>
<element name="ThreadSubject" type="string" />
<element name="ThreadParent" type="positiveInteger" />
<element name="ThreadAncestor" type="positiveInteger" />
<element name="ThreadPage" type="string" />
<element name="ThreadID" type="positiveInteger" />
<element name="ThreadAuthor" type="string" />
<element name="ThreadEditStatus" type="string" />
<element name="ThreadType" type="string" />
</sequence>
</complexType>
</schema>

View File

@ -0,0 +1,164 @@
<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.11/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.11/ http://www.mediawiki.org/xml/export-0.11.xsd" version="0.11" xml:lang="en">
<!-- Optional global configuration info -->
<siteinfo>
<!-- Site name, as set in $wgSitename -->
<sitename>DemoWiki</sitename>
<!-- Database name, as set in $wgDBname -->
<dbname>demowiki</dbname>
<!-- Forgot where you got this set? -->
<base>http://example.com/wiki/Main_Page</base>
<!-- Source software version -->
<generator>MediaWiki 1.24</generator>
<!-- Title case sensitivity options of the wiki this data came from -->
<!-- May be 'first-letter', 'case-sensitive', or 'case-insensitive' -->
<case>first-letter</case>
<!-- Defined namespace keys on the source wiki. -->
<namespaces>
<namespace key="-2" case="first-letter">Media</namespace>
<namespace key="-1" case="first-letter">Special</namespace>
<namespace key="0" case="first-letter" />
<namespace key="1" case="first-letter">Talk</namespace>
<namespace key="2" case="first-letter">User</namespace>
<namespace key="3" case="first-letter">User talk</namespace>
<namespace key="4" case="first-letter">DemoWiki</namespace>
<namespace key="5" case="first-letter">DemoWiki talk</namespace>
<namespace key="6" case="first-letter">File</namespace>
<namespace key="7" case="first-letter">File talk</namespace>
<namespace key="8" case="first-letter">MediaWiki</namespace>
<namespace key="9" case="first-letter">MediaWiki talk</namespace>
<namespace key="10" case="first-letter">Template</namespace>
<namespace key="11" case="first-letter">Template talk</namespace>
<namespace key="12" case="first-letter">Help</namespace>
<namespace key="13" case="first-letter">Help talk</namespace>
<namespace key="14" case="first-letter">Category</namespace>
<namespace key="15" case="first-letter">Category talk</namespace>
</namespaces>
</siteinfo>
<!-- The rest of the data will be a series of page records -->
<page>
<!-- Titles are listed here in text form, with namespace prefix -->
<!-- if any, and spaces rather than the underscores used in URLs. -->
<title>Page title</title>
<!-- Namespace in canonical form -->
<ns>0</ns>
<!-- The page's immutable page_id number in the source database. -->
<!-- Page ID numbers are kept across page moves, but may change -->
<!-- if a page is deleted and recreated. -->
<id>1</id>
<!-- Tag whether this article is a redirect and its target -->
<!-- This corresponds to the page_is_redirect in the page table -->
<redirect title="Target" />
<!-- If restricted, the ACL is listed here raw. -->
<restrictions>edit=sysop:move=sysop</restrictions>
<!-- With a series of revision records... -->
<!-- Remember this is XML; if you must use a regex-based extractor -->
<!-- in place of a standard XML parser, be very careful. -->
<!-- * Don't forget to decode character entities! -->
<!-- * If using a 'loose' XML parser, ensure that whitespace is -->
<!-- preserved in the <text> elements. -->
<revision>
<!-- Unique revision ID number (rev_id) in the source database. -->
<!-- This number uniquely identifies the revision on that wiki. -->
<id>100</id>
<!-- revision id of the parent revision -->
<parentid>99</parentid>
<timestamp>2001-01-15T13:15:00Z</timestamp>
<contributor>
<username>Foobar</username>
<id>42</id>
</contributor>
<minor />
<comment>I have just one thing to say!</comment>
<origin>100</origin>
<model>wikitext</model>
<format>text/x-wiki</format>
<text xml:space="preserve" bytes="25">A bunch of [[text]] here.</text>
<sha1>5x0ux8iwjrbmfzgv6pkketxgkcnpr7h</sha1>
</revision>
<revision>
<id>99</id>
<timestamp>2001-01-15T13:10:27Z</timestamp>
<contributor>
<ip>10.0.0.2</ip>
</contributor>
<comment>new!</comment>
<origin>99</origin>
<model>wikitext</model>
<format>text/x-wiki</format>
<text xml:space="preserve" bytes="24">An earlier [[revision]].</text>
<sha1>etaxt3shcge6igz1biwy3d4um2pnle4</sha1>
</revision>
</page>
<page>
<title>Talk:Page title</title>
<ns>1</ns>
<id>2</id>
<revision>
<id>101</id>
<timestamp>2001-01-15T14:03:00Z</timestamp>
<contributor><ip>10.0.0.2</ip></contributor>
<comment>hey</comment>
<origin>101</origin>
<model>wikitext</model>
<format>text/x-wiki</format>
<text xml:space="preserve" bytes="47">WHYD YOU LOCK PAGE??!!! i was editing that jerk</text>
<sha1>ml80vmyjlixdstnywwihx003exfzq9j</sha1>
</revision>
</page>
<page>
<title>File:Some image.jpg</title>
<ns>6</ns>
<id>3</id>
<revision>
<id>102</id>
<timestamp>2001-01-15T20:34:12Z</timestamp>
<contributor><username>Foobar</username><id>42</id></contributor>
<comment>My awesomeest image!</comment>
<origin>102</origin>
<model>wikitext</model>
<format>text/x-wiki</format>
<text xml:space="preserve" bytes="52">This is an awesome little imgae. I lurves it. {{PD}}</text>
<sha1>mehom37npwkpzhaiwu3wyr0egalumki</sha1>
</revision>
<upload>
<timestamp>2001-01-15T20:34:12Z</timestamp>
<contributor><username>Foobar</username><id>42</id></contributor>
<comment>My awesomeest image!</comment>
<filename>Some_image.jpg</filename>
<src>http://upload.wikimedia.org/commons/2/22/Some_image.jpg</src>
<size>12345</size>
</upload>
</page>
<!-- or a series of logitem records, but normaly page and logitem never exist both in one file -->
<logitem>
<id>15</id>
<timestamp>2008-10-23T03:20:32Z</timestamp>
<contributor>
<username>Wikimedian</username>
<id>12345</id>
</contributor>
<comment>content was: 'I think this was a silly edit'</comment>
<type>delete</type>
<action>delete</action>
<logtitle>Silly page name</logtitle>
<params xml:space="preserve" />
</logitem>
</mediawiki>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,61 @@
globals.txt
Globals are evil. The original MediaWiki code relied on globals for processing
context far too often. MediaWiki development since then has been a story of
slowly moving context out of global variables and into objects. Storing
processing context in object member variables allows those objects to be reused
in a much more flexible way. Consider the elegance of:
# Generate the article HTML as if viewed by a web request
$article = new Article( Title::newFromText( $t ) );
$article->view();
versus
# Save current globals
$oldTitle = $wgTitle;
$oldArticle = $wgArticle;
# Generate the HTML
$wgTitle = Title::newFromText( $t );
$wgArticle = new Article;
$wgArticle->view();
# Restore globals
$wgTitle = $oldTitle
$wgArticle = $oldArticle
Some of the current MediaWiki developers have an idle fantasy that some day,
globals will be eliminated from MediaWiki entirely, replaced by a configuration
object which would be passed to constructors. Whether that would be an
efficient, convenient solution remains to be seen, but certainly PHP 5 makes
such object-oriented programming models easier than they were in previous
versions.
For the time being though, MediaWiki programmers will have to work in an
environment with some global context. At the time of writing (2008), 418 globals were
initialised on startup by MediaWiki. 304 of these were configuration settings,
which are defined in MainConfigSchema.php. There is no comprehensive
documentation for the remaining 114 globals, however some of the most important
ones are listed below. They are typically initialised either in index.php or in
Setup.php.
$wgTitle
Title object created from the request URL.
Use IContextSource::getTitle() instead.
$wgOut
OutputPage object for HTTP response.
Use IContextSource::getOutput() instead.
$wgUser
User object for the user associated with the current request.
Use IContextSource::getAuthority() instead.
$wgLang
Language object selected by user preferences.
Use IContextSource::getLanguage() instead.
$wgRequest
WebRequest object, to get request data
Use IContextSource::getRequest() instead.

3
mediawiki/docs/html/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*
!README
!.gitignore

View File

@ -0,0 +1,4 @@
This directory is for the auto-generated doxygen documentation.
Run 'php mwdocgen.php' in the maintenance subdirectory to build the docs.
Get Doxygen from http://www.doxygen.org/

View File

@ -0,0 +1,84 @@
Magic Words {#magicword}
====================================
Magic words are localizable keywords used in wikitext. They are used for many
small fragments of text, including:
* The names of parser functions e.g. `{{urlencode:...}}`
* The names of variables, e.g. `{{CURRENTDAY}}`
* Double-underscore behavior switches, e.g. `__NOTOC__`
* Image link parameter names
Magic words have a synonym list, with the canonical English word always present,
and a case sensitivity flag. The MagicWord class provides facilities for
matching a magic word by converting it to a regex.
A magic word has a unique ID. Often, the ID is the canonical English synonym in
lowercase.
To add a magic word in an extension, add a file to the **ExtensionMessagesFiles**
attribute in extension.json,
and in that file, set a variable called **$magicWords**. This array is associative
with the language code in the first dimension key and an ID in the second key. The
third level array is numerically indexed: the element with key 0 contains the case
sensitivity flag, with 0 for case-insensitive and 1 for case-sensitive. The
subsequent elements of the array are the synonyms in the relevant language.
To add a magic word in core, add it to $magicWords in MessagesEn.php, following the
comment there.
For example, to add a new parser function in an extension: create a file called
**ExtensionName.i18n.magic.php** with the following contents:
```php
<?php
$magicWords = [];
$magicWords['en'] = [
// Case insensitive.
'mag_custom' => [ 0, 'custom' ],
];
$magicWords['es'] = [
'mag_custom' => [ 0, 'aduanero' ],
];
```
Then in extension.json:
```json
{
"ExtensionMessagesFiles": {
"ExtensionNameMagic": "ExtensionName.i18n.magic.php"
},
"Hooks": {
"ParserFirstCallInit": "MyExtensionHooks::onParserFirstCallInit"
}
}
```
It is important that the key "ExtensionNameMagic" is unique. It must not be used
by another extension.
And in the class file:
```php
<?php
class MyExtensionHooks {
public static function onParserFirstCallInit( $parser ) {
$parser->setFunctionHook( 'mag_custom', [ self::class, 'expandCustom' ] );
return true;
}
public static function expandCustom( $parser, $var1, $var2 ) {
return "custom: var1 is $var1, var2 is $var2";
}
}
```
- Online documentation (contains more information):
- Magic words: <https://www.mediawiki.org/wiki/Manual:Magic_words>
- Variables: <https://www.mediawiki.org/wiki/Manual:Variable>
- Parser functions: <https://www.mediawiki.org/wiki/Manual:Parser_functions>

View File

@ -0,0 +1,57 @@
Prior to version 1.16, maintenance scripts were a hodgepodge of code that
had no cohesion or formal method of action. Beginning in 1.16, maintenance
scripts have been cleaned up to use a unified class.
1. Directory structure
2. How to run a script
3. How to write your own
1. DIRECTORY STRUCTURE
The /maintenance directory of a MediaWiki installation contains several
subdirectories, all of which have unique purposes.
2. HOW TO RUN A SCRIPT
Ridiculously simple, just call 'php someScript.php' that's in the top-
level /maintenance directory.
Example:
php clearCacheStats.php
The following parameters are available to all maintenance scripts
--help : Print a help message
--quiet : Quiet non-error output
--dbuser : The database user to use for the script (if needed)
--dbpass : Same as above (if needed)
--conf : Location of LocalSettings.php, if not default
--wiki : For specifying the wiki ID
--batch-size : If the script supports batch operations, do this many per batch
3. HOW TO WRITE YOUR OWN
Make a file in the maintenance directory called myScript.php or something.
In it, write the following:
==BEGIN==
<?php
require_once 'Maintenance.php';
class DemoMaint extends Maintenance {
public function __construct() {
parent::__construct();
}
public function execute() {
}
}
$maintClass = DemoMaint::class;
require_once RUN_MAINTENANCE_IF_MAIN;
==END==
That's it. In the execute() method, you have access to all of the normal
MediaWiki functions, so you can get a DB connection, use the cache, etc.
For full docs on the Maintenance class, see the auto-generated docs at
https://doc.wikimedia.org/mediawiki-core/master/php/classMaintenance.html

253
mediawiki/docs/memcached.md Normal file
View File

@ -0,0 +1,253 @@
Memcached {#memcached}
====================================
MediaWiki has optional support for memcached, a "high-performance,
distributed memory object caching system". For general information
on it, see: <http://www.danga.com/memcached/>
Memcached is likely more trouble than a small site will need, but
for a larger site with heavy load, like Wikipedia, it should help
lighten the load on the database servers by caching data and objects
in memory.
Installation
--------------------------------
Packages are available for Fedora, Debian, Ubuntu and probably other
Linux distributions. If there's no package available for your
distribution, you can compile it from source.
Compilation
--------------------------------
* PHP must be compiled with --enable-sockets
* libevent: <http://www.monkey.org/~provos/libevent/>
(as of 2003-08-11, 0.7a is current)
* optionally, epoll-rt patch for Linux kernel:
<http://www.xmailserver.org/linux-patches/nio-improve.html>
* memcached: <http://www.danga.com/memcached/download.bml>
(as of this writing, 1.1.9 is current)
Memcached and libevent are under BSD-style licenses.
The server should run on Linux and other Unix-like systems... you
can run multiple servers on one machine or on multiple machines on
a network; storage can be distributed across multiple servers, and
multiple web servers can use the same cache cluster.
**W A R N I N G ! ! ! ! !**
Memcached has no security or authentication. Please ensure that your
server is appropriately firewalled, and that the port(s) used for
memcached servers are not publicly accessible. Otherwise, anyone on
the internet can put data into and read data from your cache.
An attacker familiar with MediaWiki internals could use this to steal
passwords and email addresses, or to make themselves a sysop and
install malicious javascript on the site. There may be other types
of vulnerability, no audit has been done -- so be safe and keep it
behind a firewall.
**W A R N I N G ! ! ! ! !**
Setup
--------------------------------
If you installed memcached using a distro, the daemon should be started
automatically using
/etc/init.d/memcached
To start the daemon manually, use something like:
memcached -d -l 127.0.0.1 -p 11211 -m 64
(to run in daemon mode, accessible only via loopback interface,
on port 11211, using up to 64 MiB of memory)
In your LocalSettings.php file, set:
```php
$wgMainCacheType = CACHE_MEMCACHED;
$wgMemCachedServers = [ "127.0.0.1:11211" ];
```
The wiki should then use memcached to cache various data. To use
multiple servers (physically separate boxes or multiple caches
on one machine on a large-memory x86 box), just add more items
to the array. To increase the weight of a server (say, because
it has twice the memory of the others and you want to spread
usage evenly), make its entry a subarray:
```php
$wgMemCachedServers = [
"127.0.0.1:11211", # one gig on this box
[ "192.168.0.1:11211", 2 ] # two gigs on the other box
];
```
PHP client for memcached
--------------------------------
MediaWiki uses a fork of Ryan T. Dean's pure-PHP memcached client.
It also supports the PECL PHP extension for memcached.
MediaWiki uses the ObjectCache class to retrieve instances of
BagOStuff by purpose, controlled by the following variables:
* $wgMainCacheType
* $wgParserCacheType
* $wgMessageCacheType
If you set one of these to CACHE_NONE, MediaWiki still creates a
BagOStuff object, but calls it to it are no-ops. If the cache daemon
can't be contacted, it should also disable itself fairly smoothly.
Keys used
--------------------------------
(incomplete, out of date)
Date Formatter:
key: $wgDBname:dateformatter
ex: wikidb:dateformatter
stores: a single instance of the DateFormatter class
cleared by: nothing
expiry: one hour
Difference Engine:
key: $wgDBname:diff:version:{MW_DIFF_VERSION}:oldid:$old:newid:$new
ex: wikidb:diff:version:1.11a:oldid:1:newid:2
stores: body of a difference
cleared by: nothing
expiry: one week
Interwiki:
key: $wgDBname:interwiki:$prefix
ex: wikidb:interwiki:w
stores: object from the interwiki table of the database
expiry: $wgInterwikiExpiry
cleared by: nothing
Lag time of the databases:
key: $wgDBname:lag_times
ex: wikidb:lag_times
stores: array mapping the database id to its lag time
expiry: 5 secondes
cleared by: nothing
Localisation:
key: $wgDBname:localisation:$lang
ex: wikidb:localisation:de
stores: array of localisation settings
set in: Language::loadLocalisation()
expiry: none
cleared by: Language::loadLocalisation()
Message Cache:
See MessageCache.php.
Newtalk:
key: $wgDBname:newtalk:ip:$ip
ex: wikidb:newtalk:ip:123.45.67.89
stores: integer, 0 or 1
set in: User::loadFromDatabase()
cleared by: User::saveSettings() # ?
expiry: 30 minutes
Parser Cache:
access: ParserCache
backend: $wgParserCacheType
key: $wgDBname:pcache:idhash:$pageid-$renderkey!$hash
$pageid: id of the page
$renderkey: 1 if action=render, 0 otherwise
$hash: hash of user options applied to the page, see ParserOptions::optionsHash()
ex: wikidb:pcache:idhash:1-0!1!0!!en!2
stores: ParserOutput object
modified by: WikiPage::doEditUpdates() or PoolWorkArticleView::doWork()
expiry: $wgParserCacheExpireTime or less if it contains short lived functions
key: $wgDBname:pcache:idoptions:$pageid
stores: CacheTime object with an additional list of used options for the hash,
serves as ParserCache pointer.
modified by: ParserCache::save()
expiry: The same as the ParserCache entry it points to.
Ping limiter:
controlled by: $wgRateLimits
key: $wgDBname:limiter:action:$action:ip:$ip,
$wgDBname:limiter:action:$action:user:$id,
mediawiki:limiter:action:$action:ip:$ip and
mediawiki:limiter:action:$action:subnet:$sub
ex: wikidb:limiter:action:edit:ip:123.45.67.89,
wikidb:limiter:action:edit:user:1012
mediawiki:limiter:action:edit:ip:123.45.67.89 and
mediawiki:limiter:action:$action:subnet:123.45.67
stores: number of action made by user/ip/subnet
cleared by: nothing
expiry: expiry set for the action and group in $wgRateLimits
Proxy Check: (deprecated)
key: $wgDBname:proxy:ip:$ip
ex: wikidb:proxy:ip:123.45.67.89
stores: 1 if the ip is a proxy
cleared by: nothing
expiry: $wgProxyMemcExpiry
Revision text:
key: $wgDBname:revisiontext:textid:$id
ex: wikidb:revisiontext:textid:1012
stores: text of a revision
cleared by: nothing
expiry: $wgRevisionCacheExpiry
Sessions:
controlled by: $wgSessionsInObjectCache
key: $wgBDname:session:$id
ex: wikidb:session:38d7c5b8d3bfc51egf40c69bc40f8be3
stores: $SESSION, useful when using a multi-sever wiki
expiry: one hour
cleared by: session_destroy()
Sidebar:
access: WANObjectCache
controlled by: $wgEnableSidebarCache
key: $wgDBname:sidebar
ex: wikidb:sidebar
stores: the html output of the sidebar
expiry: $wgSidebarCacheExpiry
cleared by: MessageCache::replace()
Special:Allpages:
key: $wgDBname:allpages:ns:$ns
ex: wikidb:allpages:ns:0
stores: array of pages in a namespace
expiry: one hour
cleared by: nothing
Special:Recentchanges (feed):
backend: $wgMessageCacheType
key: $wgDBname:rcfeed:$format:$limit:$hideminor:$target and
rcfeed:$format:timestamp
ex: wikidb:rcfeed:rss:50:: and rcfeed:rss:timestamp
stores: xml output of feed
expiry: one day
clear by: maintenance/rebuildrecentchanges.php script, or
calling Special:Recentchanges?action=purge&feed=rss,
Special:Recentchanges?action=purge&feed=atom,
but note need $wgGroupPermissions[...]['purge'] permission.

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rdf:RDF [
<!ENTITY cc "http://creativecommons.org/ns#">
<!ENTITY xsd "http://www.w3.org/2001/XMLSchema#">
<!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#">
<!ENTITY owl "http://www.w3.org/2002/07/owl#">
<!ENTITY mediawiki "https://www.mediawiki.org/ontology#">
]>
<rdf:RDF
xmlns:xsd="&xsd;"
xmlns:rdf="&rdf;"
xmlns:rdfs="&rdfs;"
xmlns:owl="&owl;"
xmlns:cc="&cc;"
>
<owl:Ontology rdf:about="&mediawiki;">
<rdfs:label>MediaWiki ontology</rdfs:label>
<rdfs:comment>The ontology of MediaWiki</rdfs:comment>
<cc:licence rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
</owl:Ontology>
<!--
///////////////////////////////////////////////////////////////////////////////////////
//
// Classes
//
///////////////////////////////////////////////////////////////////////////////////////
-->
<owl:Class rdf:about="&mediawiki;Dump">
<rdfs:label>Dump</rdfs:label>
<rdfs:comment>A dump of MediaWiki content.</rdfs:comment>
</owl:Class>
<owl:Class rdf:about="&mediawiki;Category">
<rdfs:label>Category</rdfs:label>
<rdfs:comment>MediaWiki category.</rdfs:comment>
</owl:Class>
<owl:Class rdf:about="&mediawiki;HiddenCategory">
<rdfs:label>HiddenCategory</rdfs:label>
<rdfs:comment>MediaWiki hidden category.</rdfs:comment>
</owl:Class>
<!--
///////////////////////////////////////////////////////////////////////////////////////
//
// Properties
//
///////////////////////////////////////////////////////////////////////////////////////
-->
<owl:ObjectProperty rdf:about="&mediawiki;isInCategory">
<rdfs:label>isInCategory</rdfs:label>
<rdfs:comment>One category is the parent of another.</rdfs:comment>
<rdfs:range rdf:resource="&mediawiki;Category"/>
<rdfs:domain rdf:resource="&mediawiki;Category"/>
</owl:ObjectProperty>
<owl:DatatypeProperty rdf:about="&mediawiki;pages">
<rdfs:label>pages</rdfs:label>
<rdfs:comment>Number of articles belonging to this category.</rdfs:comment>
<rdfs:range rdf:resource="&mediawiki;Category"/>
<rdfs:range rdf:resource="&xsd;integer"/>
</owl:DatatypeProperty>
<owl:DatatypeProperty rdf:about="&mediawiki;subcategories">
<rdfs:label>subcategories</rdfs:label>
<rdfs:comment>Number of subcategories belonging to this category.</rdfs:comment>
<rdfs:range rdf:resource="&mediawiki;Category"/>
<rdfs:range rdf:resource="&xsd;integer"/>
</owl:DatatypeProperty>
</rdf:RDF>

View File

@ -0,0 +1,137 @@
PageUpdater {#pageupdater}
===========
This document provides an overview of the usage of PageUpdater and DerivedPageDataUpdater.
## PageUpdater
`PageUpdater` is the canonical way to create page revisions, that is, to perform edits.
`PageUpdater` is a stateful, handle-like object that allows new revisions to be created on a given wiki page using the `saveRevision()` method. `PageUpdater` provides setters for defining the new revision's content as well as meta-data such as change tags. `saveRevision()` stores the new revision's primary content and metadata, and triggers the necessary updates to derived secondary data and cached artifacts e.g. in the `ParserCache` and the CDN layer, using a `DerivedPageDataUpdater`.
`PageUpdater` instances follow the below life cycle, defined by a number of methods:
+----------------------------+
| |
| new |
| |
+------|--------------|------+
| |
grabParentRevision()-| |
or hasEditConflict()-| |
| |
+--------v-------+ |
| | |
| parent known | |
| | |
Enables---------------+--------|-------+ |
safe operations based on | |-saveRevision()
the parent revision, e.g. | |
section replacement or | |
edit conflict resolution. | |
| |
saveRevision()-| |
| |
+------v--------------v------+
| |
| creation committed |
| |
Enables-----------------+----------------------------+
wasSuccess()
isUnchanged()
isNew()
getState()
getNewRevision()
etc.
The stateful nature of `PageUpdater` allows it to be used to safely perform transformations that depend on the new revision's parent revision, such as replacing sections or applying 3-way conflict resolution, while protecting against race conditions using a compare-and-swap (CAS) mechanism: after calling code used the `grabParentRevision()` method to access the edit's logical parent, `PageUpdater` remembers that revision, and ensure that that revision is still the page's current revision when performing the atomic database update for the revision's primary meta-data when `saveRevision()` is called. If another revision was created concurrently, `saveRevision()` will fail, indicating the problem with the "edit-conflict" code in the status object.
Typical usage for programmatic revision creation (with `$page` being a WikiPage as of 1.32, to be replaced by a repository service later):
```php
$updater = $page->newPageUpdater( $user );
$updater->setContent( SlotRecord::MAIN, $content );
$updater->setRcPatrolStatus( RecentChange::PRC_PATROLLED );
$newRev = $updater->saveRevision( $comment );
```
Usage with content depending on the parent revision
```php
$updater = $page->newPageUpdater( $user );
$parent = $updater->grabParentRevision();
$content = $parent->getContent( SlotRecord::MAIN )->replaceSection( $section, $sectionContent );
$updater->setContent( SlotRecord::MAIN, $content );
$newRev = $updater->saveRevision( $comment, EDIT_UPDATE );
```
In both cases, all secondary updates will be triggered automatically.
## DerivedPageDataUpdater
`DerivedPageDataUpdater` is a stateful, handle-like object that caches derived data representing a revision, and can trigger updates of cached copies of that data, e.g. in the links tables, `page_props`, the `ParserCache`, and the CDN layer.
`DerivedPageDataUpdater` is used by `PageUpdater` when creating new revisions, but can also be used independently when performing meta data updates during undeletion, import, or when puring a page. It's a stepping stone on the way to a more complete refactoring of WikiPage.
**NOTE**: Avoid direct usage of `DerivedPageDataUpdater`. In the future, we want to define interfaces for the different use cases of `DerivedPageDataUpdater`, particularly providing access to post-PST content and `ParserOutput` to callbacks during revision creation, which currently use `WikiPage::prepareContentForEdit`, and allowing updates to be triggered on purge, import, and undeletion, which currently use `WikiPage::doEditUpdates()` and `Content::getSecondaryDataUpdates()`.
The primary reason for `DerivedPageDataUpdater` to be stateful is internal caching of state that avoids the re-generation of `ParserOutput` and re-application of pre-save-transformations (PST).
`DerivedPageDataUpdater` instances follow the below life cycle, defined by a number of methods:
+---------------------------------------------------------------------+
| |
| new |
| |
+---------------|------------------|------------------|---------------+
| | |
grabCurrentRevision()-| | |
| | |
+-----------v----------+ | |
| | |-prepareContent() |
| knows current | | |
| | | |
Enables------------------+-----|-----|----------+ | |
pageExisted() | | | |
wasRedirect() | |-prepareContent() | |-prepareUpdate()
| | | |
| | +-------------v------------+ |
| | | | |
| +----> has content | |
| | | |
Enables------------------------|----------+--------------------------+ |
isChange() | | |
isCreation() |-prepareUpdate() | |
getSlots() | prepareUpdate()-| |
getTouchedSlotRoles() | | |
getCanonicalParserOutput() | +-----------v------------v-----------------+
| | |
+------------------> has revision |
| |
Enables-------------------------------------------+------------------------|-----------------+
updateParserCache() |
runSecondaryDataUpdates() |-doUpdates()
|
+-----------v---------+
| |
| updates done |
| |
+---------------------+
- `grabCurrentRevision()` returns the logical parent revision of the target revision. It is guaranteed to always return the same revision for a given `DerivedPageDataUpdater` instance. If called before `prepareUpdate()`, this fixates the logical parent to be the page's current revision. If called for the first time after `prepareUpdate()`, it returns the revision passed as the 'oldrevision' option to `prepareUpdate()`, or, if that wasn't given, the parent of $revision parameter passed to `prepareUpdate()`.
- `prepareContent()` is called before the new revision is created, to apply pre-save-transformation (PST) and allow subsequent access to the canonical `ParserOutput` of the revision. `getSlots()` and `getCanonicalParserOutput()` as well as `getSecondaryDataUpdates()` may be used after `prepareContent()` was called. Calling `prepareContent()` with the same parameters again has no effect. Calling it again with mismatching parameters, or calling it after `prepareUpdate()` was called, triggers a `LogicException`.
- `prepareUpdate()` is called after the new revision has been created. This may happen right after the revision was created, on the same instance on which `prepareContent()` was called, or later (possibly much later), on a fresh instance in a different process, due to deferred or asynchronous updates, or during import, undeletion, purging, etc. `prepareUpdate()` is required before a call to `doUpdates()`, and it also enables calls to `getSlots()` and `getCanonicalParserOutput()` as well as `getSecondaryDataUpdates()`. Calling `prepareUpdate()` with the same parameters again has no effect. Calling it again with mismatching parameters, or calling it with parameters mismatching the ones `prepareContent()` was called with, triggers a `LogicException`.
- `getSecondaryDataUpdates()` returns `DataUpdates` that represent derived data for the revision. These may be used to update such data, e.g. in `ApiPurge`, `RefreshLinksJob`, and the `refreshLinks` script.
- `doUpdates()` triggers the updates defined by `getSecondaryDataUpdates()`, and also causes updates to cached artifacts in the `ParserCache`, the CDN layer, etc. This is primarily used by PageUpdater, but also by `UndeletePage` during undeletion, and when importing revisions from XML. `doUpdates()` can only be called after `prepareUpdate()` was used to initialize the `DerivedPageDataUpdater` instance for a specific revision. Calling it before `prepareUpdate()` is called raises a `LogicException`.
A `DerivedPageDataUpdater` instance is intended to be re-used during different stages of complex update operations that often involve callbacks to extension code via
MediaWiki's hook mechanism, or deferred or even asynchronous execution of Jobs and `DeferredUpdates`. Since these mechanisms typically do not provide a way to pass a
`DerivedPageDataUpdater` directly, `WikiPage::getDerivedDataUpdater()` has to be used to obtain a `DerivedPageDataUpdater` for the update currently in progress - re-using the same `DerivedPageDataUpdater` if possible avoids re-generation of `ParserOutput` objects
and other expensively derived artifacts.
This mechanism for re-using a `DerivedPageDataUpdater` instance without passing it directly requires a way to ensure that a given `DerivedPageDataUpdater` instance can actually be used in the calling code's context. For this purpose, `WikiPage::getDerivedDataUpdater()` calls the `isReusableFor()` method on `DerivedPageDataUpdater`, which ensures that the given instance is applicable to the given parameters. In other words, `isReusableFor()` predicts whether calling `prepareContent()` or `prepareUpdate()` with a given set of parameters will trigger a `LogicException`. In that case, `WikiPage::getDerivedDataUpdater()` creates a fresh `DerivedPageDataUpdater` instance.

View File

@ -0,0 +1,81 @@
{
"$schema": "http://json-schema.org/draft-06/schema#",
"$id": "https://www.mediawiki.org/schema/discovery-1.0",
"title": "MediaWiki REST API discovery document",
"description": "Discovery documents provide all information needed to locate and access REST APIs on a given wiki.",
"type": "object",
"required": [ "info", "servers", "modules" ],
"properties": {
"mw-discovery": {
"description": "The version of the MediaWiki discovery schema used by the discovery document.",
"type": "string"
},
"info": {
"$ref": "#/definitions/Info"
},
"servers": {
"description": "A list of servers that can be used to access the APIs.",
"type": "array",
"minItems": 1,
"item": { "$ref": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Server" }
},
"modules": {
"description": "Information about the API modules available for interacting with the wiki. Depends on the extensions installed on the wiki, and may further vary with the wiki's version and configuration.",
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/definitions/Module" }
}
},
"definitions": {
"Info": {
"description": "Information about the wiki site that offers the API",
"type": "object",
"required": [
"title",
"mediawiki"
],
"properties": {
"title": {
"description": "The name of the wiki",
"type": "string"
},
"license": {
"$ref": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/License"
},
"contact": {
"$ref": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Contact"
},
"mediawiki": {
"description": "The version of the MediaWiki software serving the document",
"type": "string"
}
}
},
"Module": {
"title": "REST API Module",
"description": "Information about a given REST module available of the wiki. Corresponds to the https://www.mediawiki.org/schema/mwapi-1.0 schema.",
"type": "object",
"required": [
"info",
"base",
"spec"
],
"properties": {
"info": {
"$ref": "#/definitions/ModuleInfo"
},
"base": {
"description": "The base path of the module, to be appended to the server URL. This may or may not be a functioning endpoint, as defined by the module's specification.",
"type": "string"
},
"spec": {
"description": "URL of the module's OpenAPI specification. The version of OpenAPI used may vary.",
"type": "string"
}
}
},
"ModuleInfo": {
"$ref": "https://www.mediawiki.org/schema/mwapi-1.0#/definitions/Info"
}
}
}

View File

@ -0,0 +1,127 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"$id": "https://www.mediawiki.org/schema/mwapi-1.0",
"title": "MediaWiki REST API module definition",
"description": "Module definition files provide meta-data about modules and define the available routes. They are similar to OpenAPI specs.",
"type": "object",
"required": [ "mwapi", "info", "moduleId", "paths" ],
"properties": {
"mwapi": {
"description": "The version of the MediaWiki module definition schema used by the document.",
"type": "string"
},
"moduleId": {
"description": "The module name, also used as the path prefix",
"type": "string"
},
"info": {
"$ref": "#/definitions/Info"
},
"paths": {
"description": "Information about the API routes available for interacting with the wiki.",
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/definitions/Path" }
}
},
"definitions": {
"Info": {
"allOf": [
{ "$ref": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/Info" },
{ "$ref": "#/definitions/ExtraInfo" }
]
},
"ExtraInfo": {
"type": "object"
},
"Path": {
"title": "REST API Path",
"description": "The operations available for a given path",
"type": "object",
"minProperties": 1,
"additionalProperties": { "$ref": "#/definitions/Operation" }
},
"Operation": {
"allOf": [
{ "$ref": "#/definitions/OperationInfo" },
{ "$ref": "#/definitions/OperationImpl" }
]
},
"OperationInfo": {
"properties": {
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"summary": {
"type": "string"
},
"description": {
"type": "string"
},
"externalDocs": {
"$ref": "https://spec.openapis.org/oas/3.0/schema/2021-09-28#/definitions/ExternalDocumentation"
}
}
},
"OperationImpl": {
"oneOf": [
{ "$ref": "#/definitions/WithHandler" },
{ "$ref": "#/definitions/WithRedirect" }
]
},
"WithHandler": {
"required": [ "handler" ],
"properties": {
"handler": {
"$ref": "#/definitions/MWObjectSpec"
}
}
},
"MWObjectSpec": {
"type": "object",
"description": "An object-spec for use with the wikimedia/object-factory package.",
"properties": {
"class": {
"type": "string",
"description": "PHP class name of the object to create. If 'factory' is also specified, it will be used to validate the object."
},
"factory": {
"type": [ "string", "array" ],
"description": "Factory method for creating the object (a PHP callable)."
},
"args": {
"type": "array",
"description": "Arguments to pass to the constructor or the factory method."
},
"services": {
"type": "array",
"item": { "type": "string" },
"description": "List of services to pass as arguments. Each name will be looked up in MediaWikiServices."
},
"optional_services": {
"type": "array",
"item": { "type": "string" },
"description": "List of services to pass as arguments. Each name will be looked up in MediaWikiServices. If the service is unknown the parameter is set to 'null' instead of causing an error."
}
},
"additionalProperties": true
},
"WithRedirect": {
"required": [ "redirect" ],
"properties": {
"redirect": {
"type": "object",
"required": [ "path" ],
"properties": {
"path": { "type": "string" },
"code": { "type": "integer" }
},
"additionalProperties": false
}
}
}
}
}

12
mediawiki/docs/schema.md Normal file
View File

@ -0,0 +1,12 @@
Schema {#schema}
======
The most up-to-date schema for the tables in the database
will always be `tables.sql` in the maintenance directory,
which is called from the installation script.
That file has been commented with details of the usage for
each table and field.
Historical information and some other notes are available at
<https://www.mediawiki.org/wiki/Manual:Database_layout>.

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
This is an XML Schema description of the format
used by MediaWiki's exportSites.php and importSites.php
scripts.
-->
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:mwsl="http://www.mediawiki.org/xml/sitelist-1.0/"
targetNamespace="http://www.mediawiki.org/xml/sitelist-1.0/"
elementFormDefault="qualified">
<annotation>
<documentation xml:lang="en">
MediaWiki's export format for site definitions.
</documentation>
</annotation>
<!-- Our root element -->
<element name="sites" type="mwsl:MediaWikiSiteListType">
<unique name="GlobalIDConstraint">
<selector xpath="mwsl:Site" />
<field xpath="mwsl:GlobalID" />
</unique>
</element>
<simpleType name="EmptyTagType">
<restriction base="string">
<length value="0"/>
</restriction>
</simpleType>
<complexType name="TypedIDType">
<simpleContent>
<extension base="NCName">
<attribute name="type" use="required" type="NCName" />
</extension>
</simpleContent>
</complexType>
<complexType name="TypedURIType">
<simpleContent>
<extension base="anyURI">
<attribute name="type" use="required" type="NCName" />
</extension>
</simpleContent>
</complexType>
<complexType name="MediaWikiSiteListType">
<sequence>
<element name="site" type="mwsl:SiteType"
minOccurs="0" maxOccurs="unbounded" />
</sequence>
<attribute name="version" type="string" use="optional" />
</complexType>
<complexType name="SiteType">
<choice maxOccurs="unbounded">
<element name="globalid" type="ID" minOccurs="1" maxOccurs="1" />
<element name="localid" type="mwsl:TypedIDType" minOccurs="0" />
<element name="group" type="NCName" minOccurs="0" maxOccurs="1" />
<element name="source" type="NCName" minOccurs="0" maxOccurs="1" />
<element name="forward" type="mwsl:EmptyTagType" minOccurs="0" maxOccurs="1" />
<element name="path" type="mwsl:TypedURIType" minOccurs="0" />
</choice>
<attribute name="type" use="optional" type="NCName" />
</complexType>
</schema>

View File

@ -0,0 +1,45 @@
Sitelist {#sitelist}
========
This document describes the XML format used to represent information about external sites known to a MediaWiki installation. This information about external sites is used to allow "inter-wiki" links, cross-language navigation, as well as close integration via direct access to the other site's web API or even directly to their database.
Lists of external sites can be imported and exported using the *importSites.php* and *exportSites.php* scripts. In the database, external sites are described by the `sites` and `site_ids` tables.
The formal specification of the format used by *importSites.php* and *exportSites.php* can be found in the *sitelist-1.0.xsd* file. Below is an example and a brief description of what the individual XML elements and attributes mean:
```xml
<sites version="1.0">
<site>
<globalid>acme.com</globalid>
<localid type="interwiki">acme</localid>
<group>Vendor</group>
<path type="link">http://acme.com/</path>
<source>meta.wikimedia.org</source>
</site>
<site type="mediawiki">
<globalid>de.wikidik.example</globalid>
<localid type="equivalent">de</localid>
<group>Dictionary</group>
<forward/>
<path type="page_path">http://acme.com/</path>
</site>
</sites>
```
The XML elements are used as follows:
- `sites`: The root element, containing a set of site tags. May have a `version` attribute with the value `1.0`.
- `site`: A site entry, representing an external website. May have a `type` attribute with one of the following values:
+ `unknown`: (default) any website
+ `mediawiki`: A MediaWiki site
- `globalid`: A unique identifier for the site. For a given site, the same unique global ID must be used across all wikis in a wiki farm (aka wiki family).
- `localid`: An identifier for the site, for use on the local wiki. Multiple local IDs may be assigned to a given site. The same local ID can be used to refer to different sites by different wikis on the same farm/family. The `localid` element may have a type attribute with one of the following values:
+ `interwiki`: Used as an "interwiki" link prefix, for creating cross-wiki links.
+ `equivalent`: Used as a "language" link prefix, for cross-linking equivalent content in different languages.
- `group`: The site group (e.g. wiki family) the site belongs to.
- `path`: A URL template for accessing resources on the site. Several paths may be defined for a given site, for accessing different kinds of resources, identified by the `type` attribute, using one of the following values:
+ `link`: Generic URL template, often the document root.
+ `page_path`: (for `mediawiki` sites) URL template for wiki pages (corresponds to the target wiki's `$wgArticlePath` setting)
+ `file_path`: (for `mediawiki` sites) URL pattern for application entry points and resources (corresponds to the target wiki's `$wgScriptPath` setting).
- `forward`: Whether using a prefix defined by a `localid` tag in the URL will cause the request to be redirected to the corresponding page on the target wiki (currently unused). E.g. whether <http://wiki.acme.com/wiki/foo:Buzz> should be forwarded to <http://wiki.foo.com/read/Buzz>. (CAVEAT: not yet implement, can be specified but has no effect)

3
mediawiki/extensions/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
!README
!.gitignore
!/.vsls.json

View File

@ -0,0 +1,10 @@
{
"$schema": "http://json.schemastore.org/vsls",
"gitignore":"hide",
"excludeFiles":[
"!*"
],
"hideFiles":[
"!*"
]
}

View File

@ -0,0 +1,8 @@
{
"root": true,
"extends": [
"wikimedia/client-es5",
"wikimedia/jquery",
"wikimedia/mediawiki"
]
}

View File

@ -0,0 +1,25 @@
node_modules/
vendor/
composer.lock
# Editors
*.kate-swp
*~
\#*#
.#*
.*.swp
.project
.buildpath
.classpath
.settings
cscope.files
cscope.out
*.orig
## NetBeans
nbproject*
project.index
## Sublime
sublime-*
sftp-config.json
/.eslintcache
/.stylelintcache

Some files were not shown because too many files have changed in this diff Show More