- MediaWiki should use namespaces in a consistent way with low maintenance overhead.
- Extension registration should be fast.
- The use of namespaces in MediaWiki is inconsistent. Directory names are not necessarily related to namespace names.
- The convention of listing the path of all classes in autoload.php has become unwieldy.
- Extension registration is slow, due to the need to merge enormous class maps. Class names in the top level use generic names which risk conflicting with the PHP core.
I propose moving all MW core classes to the MediaWiki\ namespace, with class_alias() for backwards compatibility. Autoloading would be done using PSR-4, which maps namespaces to directory names.
To work around bugs in HHVM and PHP, class_alias() calls will be inserted in the file scope at the end of the file defining the new class. The autoloader will have an array mapping each legacy class name to the path of the new file. This is how class_alias() is already used in MW core. Whether this legacy autoloader is integrated with the PSR-4 autoloader or is registered separately is an implementation detail.
A notable difference between the current layout and that described by PSR-4 is that the directory names must match the namespace names, including capitalisation. We already have a MediaWiki\Linker namespace, but the files would be moved from includes/linker to MediaWiki/Linker.
Another difference is that there must only be one class per file.
MediaWiki has more directories than namespaces, and this makes sense given how directories are used in editors. Additional namespaces only really become essential when there is a risk of class name conflicts. To use PSR-4, we will have to trade off between our desire to limit the number of namespaces for usability, while still making files easy to find. The plan I detail below generally leans towards introducing additional namespaces.
There is the question of plural names. We (inconsistently) use plurals to indicate that a directory contains multiple things of a certain type, e.g. each member of includes/ is an include, each member of includes/specials is a special. This makes more sense for directories than for namespaces, given the way each is accessed: the class name MediaWiki\Specials\Activeusers would be strange to see in calling code or an error message. The namespace hierarchy is more of an ontology than a collection. I propose to use singular namespace names where the members of the namespace are are singular instance of the parent concept. So: specials -> Special, jobs -> Job, skins -> Skin. But perhaps jobqueue/utils -> JobQueue\Utils since each class also contains utils, not a single util. And perhaps services -> Services, since it has container classes, the members are not themselves services.
There is the question of whether to retain class name prefixes and suffixes. For example, we have 116 special pages, certainly enough to deserve a separate directory. But should we have MediaWiki\Special\SpecialActiveusers or MediaWiki\Special\Activeusers? I think redundant prefixes should be removed, except when the resulting name becomes very ambiguous. We should keep in mind that text editors often only display the base name.
Some directories do not make sense as modules or namespaces, and exist only out of a desire to reduce the number of PHP files in includes/. These should probably be moved into their respective parent directories. Specifically:
But in the other direction, I would propose:
- Sanitizer, MagicWord, MagicWordArray -> Parser\
- ForkController -> Maintenance\
- MessageCache -> Language\
- Message, RawMessage -> Language
- cache/localisation -> Language\LocalisationCache
- A directory for non-class code: WebStart.php, Setup.php, DefaultSettings.php, Defines.php, GlobalFunctions.php, OutputHandler.php, NoLocalSettings.php. Namespacing of global functions and constants might be easier with PHP 7 group use, I'm not proposing that at the moment.
- Also shell scripts, config files?
- A namespace for the web app setup, request routing and response (Request?) MediaWiki, PathRouter, AjaxDispatcher, AjaxResponse, WebRequest, WebRequestUpload, FauxRequest, DerivativeRequest, WebResponse, OutputPage, HeaderCallback, exception/*
- Watchlist: WatchedItem, WatchedItemQueryService, WatchedItemQueryServiceExtension
- StubObject: StubObject, DeprecatedGlobal
- A namespace for revision, page storage (ArticleStore?): Revision, RevisionList, MergeHistory, MovePage, HistoryBlob, LinkBatch, LinkCache,
- A namespace for links storage: BacklinkCache, LinksUpdate, LinksDeletionUpdate
- UserCache -> User\UserCache
- Feed: FeedItem, ChannelFeed, RSSFeed, AtomFeed, FeedUtils
The classes in libs and utils would be better placed under the Wikimedia\ namespace, corresponding to the Composer vendor namespace, reflecting our aspirations for them to be separate from MediaWiki.
Other special cases:
- jobqueue/jobs -> JobQueue\Job
- includes/libs/rdbms/defines.php: migrate to namespaced constants.
- specials -> Special
- specials/helpers -> Special\Helper
- specials/pagers -> Special\Pager
- languages/classes -> MediaWiki\Language
- languages/data/ZhConversion -> MediaWiki\Languages\Data\ZhConversion
- maintenance: we can map MediaWiki\Maintenance to this directory for now
- Some test classes are currently in the namespace they cover, I don't think that works with PSR-4. They should be in MediaWiki\Test\PHPUnit instead.
- Test classes in MediaWiki\Test\PHPUnit, parser test runner in MediaWiki\Test\Parser
- Classes under libs/ will be under the Wikimedia namespace, so associated tests should also be in the Wikimedia namespace
- Map namespace MediaWiki\Test to directory tests\MediaWiki
New directory names will match namespace names, except for maintenance and tests. So for example, the language class files will not stay in languages/.
Existing namespaced extensions mostly use the top level, which I think is fine as long as the name is a distinctive product name. For generic descriptive names, I would prefer MediaWiki\Extension over MediaWiki\Extensions.
The transition should be fully scripted so that it can be used to rebase open changesets in Gerrit.