wiki.techinc.nl/includes/ServiceWiring.php

2676 lines
93 KiB
PHP
Raw Normal View History

<?php
/**
* Service implementations for %MediaWiki core.
*
* This file returns the array loaded by the MediaWikiServices class
* for use through `MediaWiki\MediaWikiServices::getInstance()`
*
* @see [Dependency Injection](@ref dependencyinjection) in docs/Injection.md
* for the principles of DI and how to use it MediaWiki core.
*
* Reminder:
*
* - ServiceWiring is NOT a cache for arbitrary singletons.
*
* - Services MUST NOT vary their behaviour on global state, especially not
* WebRequest, RequestContext (T218555), or other details of the current
* request or CLI process (e.g. "current" user or title). Doing so may
* cause a chain reaction and cause serious data corruption.
*
* Refer to [DI Principles](@ref di-principles) in docs/Injection.md for
* how and why we avoid this, as well as for limited exemptions to these
* principles.
*
* -------
*
* 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
*/
use Liuggio\StatsdClient\Factory\StatsdDataFactoryInterface;
use MediaWiki\Actions\ActionFactory;
use MediaWiki\Auth\AuthManager;
use MediaWiki\Auth\Throttler;
use MediaWiki\Block\AutoblockExemptionList;
use MediaWiki\Block\BlockActionInfo;
use MediaWiki\Block\BlockErrorFormatter;
use MediaWiki\Block\BlockManager;
use MediaWiki\Block\BlockPermissionCheckerFactory;
use MediaWiki\Block\BlockRestrictionStore;
use MediaWiki\Block\BlockRestrictionStoreFactory;
use MediaWiki\Block\BlockUserFactory;
use MediaWiki\Block\BlockUtils;
use MediaWiki\Block\BlockUtilsFactory;
use MediaWiki\Block\DatabaseBlock;
use MediaWiki\Block\DatabaseBlockStore;
use MediaWiki\Block\DatabaseBlockStoreFactory;
use MediaWiki\Block\HideUserUtils;
use MediaWiki\Block\UnblockUserFactory;
use MediaWiki\Block\UserBlockCommandFactory;
use MediaWiki\Cache\BacklinkCache;
use MediaWiki\Cache\BacklinkCacheFactory;
use MediaWiki\Cache\GenderCache;
use MediaWiki\Cache\HTMLCacheUpdater;
use MediaWiki\Cache\LinkBatchFactory;
use MediaWiki\Cache\LinkCache;
use MediaWiki\Cache\UserCache;
use MediaWiki\Category\TrackingCategories;
use MediaWiki\ChangeTags\ChangeTagsStore;
use MediaWiki\Collation\CollationFactory;
Introduce CommentFormatter CommentParser: * Move comment formatting backend from Linker to a CommentParser service. Allow link existence and file existence to be batched. * Rename $local to $samePage since I think that is clearer. * Rename $title to $selfLinkTarget since it was unclear what the title was used for. * Rename the "autocomment" concept to "section link" in public interfaces, although the old term remains in CSS classes. * Keep unsafe HTML pass-through in separate "unsafe" methods, for easier static analysis and code review. CommentFormatter: * Add CommentFormatter and RowCommentFormatter services as a usable frontend for comment batches, and to replace the Linker static methods. * Provide fluent and parametric interfaces. Linker: * Remove Linker::makeCommentLink() without deprecation -- nothing calls it and it is obviously an internal helper. * Soft-deprecate Linker methods formatComment(), formatLinksInComment(), commentBlock() and revComment(). Caller migration: * CommentFormatter single: Linker, RollbackAction, ApiComparePages, ApiParse * CommentFormatter parametric batch: ImageHistoryPseudoPager * CommentFormatter fluent batch: ApiQueryFilearchive * RowCommentFormatter sequential: History feed, BlocklistPager, ProtectedPagesPager, ApiQueryProtectedTitles * RowCommentFormatter with index: ChangesFeed, ChangesList, ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges * RevisionCommentBatch: HistoryPager, ContribsPager Bug: T285917 Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
use MediaWiki\CommentFormatter\CommentFormatter;
use MediaWiki\CommentFormatter\CommentParserFactory;
use MediaWiki\CommentFormatter\RowCommentFormatter;
use MediaWiki\CommentStore\CommentStore;
use MediaWiki\Config\Config;
use MediaWiki\Config\ConfigException;
use MediaWiki\Config\ConfigFactory;
use MediaWiki\Config\ConfigRepository;
use MediaWiki\Config\ServiceOptions;
use MediaWiki\Content\ContentHandlerFactory;
use MediaWiki\Content\IContentHandlerFactory;
use MediaWiki\Content\Renderer\ContentRenderer;
use MediaWiki\Content\Transform\ContentTransformer;
use MediaWiki\Context\RequestContext;
use MediaWiki\DAO\WikiAwareEntity;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\Edit\ParsoidOutputStash;
use MediaWiki\Edit\SimpleParsoidOutputStash;
use MediaWiki\EditPage\Constraint\EditConstraintFactory;
use MediaWiki\EditPage\IntroMessageBuilder;
use MediaWiki\EditPage\PreloadedContentBuilder;
use MediaWiki\EditPage\SpamChecker;
use MediaWiki\Export\WikiExporterFactory;
use MediaWiki\FileBackend\FSFile\TempFSFileFactory;
use MediaWiki\FileBackend\LockManager\LockManagerGroupFactory;
use MediaWiki\HookContainer\FauxGlobalHookArray;
use MediaWiki\HookContainer\HookContainer;
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
use MediaWiki\HookContainer\HookRunner;
use MediaWiki\HookContainer\StaticHookRegistry;
use MediaWiki\Http\HttpRequestFactory;
use MediaWiki\Http\Telemetry;
use MediaWiki\Installer\Pingback;
use MediaWiki\Interwiki\ClassicInterwikiLookup;
use MediaWiki\Interwiki\InterwikiLookup;
use MediaWiki\JobQueue\JobFactory;
use MediaWiki\JobQueue\JobQueueGroupFactory;
use MediaWiki\Json\JsonCodec;
use MediaWiki\Language\FormatterFactory;
use MediaWiki\Language\LazyLocalizationContext;
use MediaWiki\Languages\LanguageConverterFactory;
use MediaWiki\Languages\LanguageFactory;
use MediaWiki\Languages\LanguageFallback;
use MediaWiki\Languages\LanguageNameUtils;
use MediaWiki\Linker\LinkRenderer;
Add LinkRenderer (rewrite of Linker::link()) This is a rewrite of Linker::link() to a non-static, LinkTarget-based interface. Users of plain Linker::link() with no options can use the LinkRenderer instance provided by MediaWikiServices. Others that have specific options should create and configure their own instance, which can be used to create as many links as necessary. The main entrypoints for making links are: * ->makeLink( $target, $text, $attribs, $query ); * ->makeKnownLink( $target, $text, $attribs, $query ); * ->makeBrokenLink( $target, $text, $attribs, $query ); The order of the parameters are the same as Linker::link(), except $options are now part of the LinkRenderer instance, and known/broken status requires calling the function explicitly. Additionally, instead of passing in raw $html for the link text, the $text parameter will automatically be escaped unless it is specially marked as safe HTML using the MediaWiki\Linker\HtmlArmor class. The LinkBegin and LinkEnd hooks are now deprecated, but still function for backwards-compatability. Clients should migrate to the nearly- equivalent LinkRendererBegin and LinkRendererEnd hooks. The main differences between the hooks are: * Passing HtmlPageLinkRenderer object instead of deprecated DummyLinker * Using LinkTarget instead of Title * Begin hook can no longer change known/broken status of link. Use the TitleIsAlwaysKnown hook for that. * $options are no longer passed, they can be read (but shouldn't be modified!) from the LinkRenderer object. Bug: T469 Change-Id: I057cc86ae6404a080aa3c8e0e956ecbb10a897d5
2016-04-21 20:13:21 +00:00
use MediaWiki\Linker\LinkRendererFactory;
use MediaWiki\Linker\LinksMigration;
use MediaWiki\Linker\LinkTargetLookup;
use MediaWiki\Linker\LinkTargetStore;
use MediaWiki\Logger\LoggerFactory;
use MediaWiki\Mail\Emailer;
use MediaWiki\Mail\EmailUser;
use MediaWiki\Mail\EmailUserFactory;
use MediaWiki\Mail\IEmailer;
use MediaWiki\MainConfigNames;
use MediaWiki\MediaWikiServices;
use MediaWiki\Message\MessageFormatterFactory;
use MediaWiki\OutputTransform\DefaultOutputPipelineFactory;
use MediaWiki\OutputTransform\OutputTransformPipeline;
use MediaWiki\Page\ContentModelChangeFactory;
use MediaWiki\Page\DeletePageFactory;
use MediaWiki\Page\File\BadFileLookup;
use MediaWiki\Page\MergeHistoryFactory;
use MediaWiki\Page\MovePageFactory;
use MediaWiki\Page\PageCommandFactory;
use MediaWiki\Page\PageProps;
use MediaWiki\Page\PageStore;
use MediaWiki\Page\PageStoreFactory;
use MediaWiki\Page\ParserOutputAccess;
use MediaWiki\Page\RedirectLookup;
use MediaWiki\Page\RedirectStore;
use MediaWiki\Page\RollbackPageFactory;
use MediaWiki\Page\UndeletePageFactory;
use MediaWiki\Page\WikiPageFactory;
use MediaWiki\Parser\MagicWordFactory;
use MediaWiki\Parser\Parser;
use MediaWiki\Parser\ParserCacheFactory;
use MediaWiki\Parser\ParserObserver;
use MediaWiki\Parser\Parsoid\Config\DataAccess as MWDataAccess;
use MediaWiki\Parser\Parsoid\Config\PageConfigFactory as MWPageConfigFactory;
use MediaWiki\Parser\Parsoid\Config\SiteConfig as MWSiteConfig;
use MediaWiki\Parser\Parsoid\HtmlTransformFactory;
use MediaWiki\Parser\Parsoid\ParsoidOutputAccess;
Allow setting a ParserOption to generate Parsoid HTML This is an initial quick-and-dirty implementation. The ParsoidParser class will eventually inherit from \Parser, but this is an initial placeholder to unblock other Parsoid read views work. Currently Parsoid does not fully implement all the ParserOutput metadata set by the legacy parser, but we're working on it. This patch also addresses T300325 by ensuring the the Page HTML APIs use ParserOutput::getRawText(), which will return the entire Parsoid HTML document without post-processing. This is what the Parsoid team refers to as "edit mode" HTML. The ParserOutput::getText() method returns only the <body> contents of the HTML, and applies several transformations, including inserting Table of Contents and style deduplication; this is the "read views" flavor of the Parsoid HTML. We need to be careful of the interaction of the `useParsoid` flag with the ParserCacheMetadata. Effectively `useParsoid` should *always* be marked as "used" or else the ParserCache will assume its value doesn't matter and will serve legacy content for parsoid requests and vice-versa. T330677 is a follow up to address this more thoroughly by splitting the parser cache in ParserOutputAccess; the stop gap in this patch is fragile and, because it doesn't fork the ParserCacheMetadata cache, may corrupt the ParserCacheMetadata in the case when Parsoid and the legacy parser consult different sets of options to render a page. Bug: T300191 Bug: T330677 Bug: T300325 Change-Id: Ica09a4284c00d7917f8b6249e946232b2fb38011
2022-05-27 16:38:32 +00:00
use MediaWiki\Parser\Parsoid\ParsoidParserFactory;
use MediaWiki\Permissions\GrantsInfo;
use MediaWiki\Permissions\GrantsLocalization;
use MediaWiki\Permissions\GroupPermissionsLookup;
use MediaWiki\Permissions\PermissionManager;
use MediaWiki\Permissions\RateLimiter;
use MediaWiki\Permissions\RestrictionStore;
use MediaWiki\PoolCounter\PoolCounterFactory;
use MediaWiki\Preferences\DefaultPreferencesFactory;
use MediaWiki\Preferences\PreferencesFactory;
use MediaWiki\Preferences\SignatureValidator;
use MediaWiki\Preferences\SignatureValidatorFactory;
use MediaWiki\Request\ProxyLookup;
use MediaWiki\Request\WebRequest;
use MediaWiki\ResourceLoader\MessageBlobStore;
use MediaWiki\ResourceLoader\ResourceLoader;
use MediaWiki\Rest\Handler\Helper\PageRestHelperFactory;
use MediaWiki\Revision\ArchivedRevisionLookup;
use MediaWiki\Revision\ContributionsLookup;
use MediaWiki\Revision\MainSlotRoleHandler;
use MediaWiki\Revision\RevisionFactory;
use MediaWiki\Revision\RevisionLookup;
use MediaWiki\Revision\RevisionRenderer;
use MediaWiki\Revision\RevisionStore;
use MediaWiki\Revision\RevisionStoreFactory;
use MediaWiki\Revision\SlotRecord;
use MediaWiki\Revision\SlotRoleRegistry;
Introduce SearchResultThumbnailProvider & move hook + NS_FILE thumbs in What was previously a REST API-only feature (the thumbnails hook allowing for thumbnails for non-file pages via the PageImages extension) is now also being adopted in the main search page. That hook will now be called with NS_FILE result thumbnails pre-filled, which was not the case previously. PageImages essentially duplicated NS_FILE thumbnail logic that was already present in Special:Search, so that can (and will in a follow-up patch) then be removed there. Special:Search will then simply take whatever is produced from the provider (which will include both NS_FILE thumbs - which it handled already - as well as whatever else it receives from the hook), as will the REST API (which already received both) Since thumbnails can now come in for multiple namespaces & having some of those results with & others without a thumbnail can be quite jarring, it was decided that we'd display placeholder images (for certain namespaces). This is now controlled by $wgThumbnailNamespaces. I also split up a few things in FullSearchResultWidget:: generateFileHtml for more clarity. Meanwhile also updated mediawiki.special.search.styles.less to use variables for known colors. Also implemented a 'transform' (required for testing this change properly) and 'getDisplayWidthHeight' (it became needed after implementing transform) callback function for mock Files, and updated some existing tests in response to these changes. And some more Rest test files have been updated to allow passing around a HookContainer instead of only an array of hooks (from which a new HookContainer would then be created) to allow the same container to be used across all relevant objects, who may have it injected as dependency. Bug: T306883 Change-Id: I2a679b51758020d3e822da01a1bde1ae632b0b0a
2022-08-31 14:51:57 +00:00
use MediaWiki\Search\SearchResultThumbnailProvider;
use MediaWiki\Search\TitleMatcher;
use MediaWiki\Settings\Config\ConfigSchema;
use MediaWiki\Settings\SettingsBuilder;
use MediaWiki\Shell\CommandFactory;
use MediaWiki\Shell\ShellboxClientFactory;
use MediaWiki\Site\CachingSiteStore;
use MediaWiki\Site\DBSiteStore;
use MediaWiki\Site\SiteLookup;
use MediaWiki\Site\SiteStore;
use MediaWiki\SpecialPage\SpecialPageFactory;
use MediaWiki\Storage\BlobStore;
use MediaWiki\Storage\BlobStoreFactory;
Add mw-reverted change tag The tag is added to reverted edits as described in T254074. Functionality: * Adding the mw-reverted tag to reverted edits (duh) * Limiting the maximum depth of the update through a config variable (mitigation #2 from T259014). * Only applying the reverted tag after the edit has been somehow approved. Only the patrol subsystem currently implements this, but there's a hook that extensions can use (mitigation #4 from T259014, more explanation in T259103). * When performing the delayed update, it is checked whether the reverted edit was reverted itself. If so, the update is ignored. This is probably the only way to make the feature work due to the lack of an explicit "disapproval" mechanism other than reverting. * The update is also ignored if the revert is marked as deleted. Technical design: * The update code is in RevertedTagUpdate.php, which is a deferrable update, but is not used as such. It's separated to allow for better DI, testing and better code reusability in the future. * The update is queued / ran using the Job subsystem. The relevant job is in RevertedTagUpdateJob.php * PageUpdater determines whether the edit is approved or not and passes that to the DerivedPageDataUpdater. * The BeforeRevertedTagUpdate hook lets extensions decide whether the update should be ran right away or await approval. * DerivedPageDataUpdater checks whether the edit is a revert and if so either enqueues the job (if it's auto-approved) or caches the EditResult for later use (if it needs approval). * RevertedTagUpdateManager allows for easy re-enqueueing of the update for extensions. Thus, it has a very minimal interface. Other notes: * The unit testing setup for RevertedTagUpdate is a bit complicated, but it was the only way I could make this class testable while using the static ChangeTags class. Bug: T254074 Depends-On: I86d0e660f0acd51a7351396c5c82a400d3963b94 Change-Id: I70d5b29fec6b6058613f7ac2fb49f9fad9dc8da4
2020-07-06 11:47:22 +00:00
use MediaWiki\Storage\EditResultCache;
use MediaWiki\Storage\NameTableStore;
use MediaWiki\Storage\NameTableStoreFactory;
use MediaWiki\Storage\PageEditStash;
use MediaWiki\Storage\PageUpdaterFactory;
Add mw-reverted change tag The tag is added to reverted edits as described in T254074. Functionality: * Adding the mw-reverted tag to reverted edits (duh) * Limiting the maximum depth of the update through a config variable (mitigation #2 from T259014). * Only applying the reverted tag after the edit has been somehow approved. Only the patrol subsystem currently implements this, but there's a hook that extensions can use (mitigation #4 from T259014, more explanation in T259103). * When performing the delayed update, it is checked whether the reverted edit was reverted itself. If so, the update is ignored. This is probably the only way to make the feature work due to the lack of an explicit "disapproval" mechanism other than reverting. * The update is also ignored if the revert is marked as deleted. Technical design: * The update code is in RevertedTagUpdate.php, which is a deferrable update, but is not used as such. It's separated to allow for better DI, testing and better code reusability in the future. * The update is queued / ran using the Job subsystem. The relevant job is in RevertedTagUpdateJob.php * PageUpdater determines whether the edit is approved or not and passes that to the DerivedPageDataUpdater. * The BeforeRevertedTagUpdate hook lets extensions decide whether the update should be ran right away or await approval. * DerivedPageDataUpdater checks whether the edit is a revert and if so either enqueues the job (if it's auto-approved) or caches the EditResult for later use (if it needs approval). * RevertedTagUpdateManager allows for easy re-enqueueing of the update for extensions. Thus, it has a very minimal interface. Other notes: * The unit testing setup for RevertedTagUpdate is a bit complicated, but it was the only way I could make this class testable while using the static ChangeTags class. Bug: T254074 Depends-On: I86d0e660f0acd51a7351396c5c82a400d3963b94 Change-Id: I70d5b29fec6b6058613f7ac2fb49f9fad9dc8da4
2020-07-06 11:47:22 +00:00
use MediaWiki\Storage\RevertedTagUpdateManager;
use MediaWiki\Storage\SqlBlobStore;
use MediaWiki\Tidy\RemexDriver;
use MediaWiki\Tidy\TidyDriverBase;
use MediaWiki\Title\MediaWikiTitleCodec;
use MediaWiki\Title\NamespaceInfo;
use MediaWiki\Title\TitleFactory;
use MediaWiki\Title\TitleFormatter;
use MediaWiki\Title\TitleParser;
use MediaWiki\User\ActorMigration;
use MediaWiki\User\ActorNormalization;
use MediaWiki\User\ActorStore;
use MediaWiki\User\ActorStoreFactory;
use MediaWiki\User\BotPasswordStore;
use MediaWiki\User\CentralId\CentralIdLookup;
use MediaWiki\User\CentralId\CentralIdLookupFactory;
use MediaWiki\User\Options\ConditionalDefaultsLookup;
use MediaWiki\User\Options\DefaultOptionsLookup;
use MediaWiki\User\Options\UserOptionsLookup;
use MediaWiki\User\Options\UserOptionsManager;
use MediaWiki\User\PasswordReset;
use MediaWiki\User\Registration\LocalUserRegistrationProvider;
use MediaWiki\User\Registration\UserRegistrationLookup;
use MediaWiki\User\TalkPageNotificationManager;
use MediaWiki\User\TempUser\RealTempUserConfig;
use MediaWiki\User\TempUser\TempUserCreator;
use MediaWiki\User\UserEditTracker;
use MediaWiki\User\UserFactory;
use MediaWiki\User\UserGroupManager;
use MediaWiki\User\UserGroupManagerFactory;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityLookup;
use MediaWiki\User\UserIdentityUtils;
use MediaWiki\User\UserNamePrefixSearch;
use MediaWiki\User\UserNameUtils;
use MediaWiki\Utils\UrlUtils;
use MediaWiki\Watchlist\WatchlistManager;
use MediaWiki\WikiMap\WikiMap;
use Wikimedia\DependencyStore\KeyValueDependencyStore;
use Wikimedia\DependencyStore\SqlModuleDependencyStore;
use Wikimedia\EventRelayer\EventRelayerGroup;
use Wikimedia\Message\IMessageFormatterFactory;
use Wikimedia\ObjectFactory\ObjectFactory;
use Wikimedia\Parsoid\Config\DataAccess;
use Wikimedia\Parsoid\Config\SiteConfig;
use Wikimedia\Parsoid\Parsoid;
use Wikimedia\Rdbms\ChronologyProtector;
use Wikimedia\Rdbms\ConfiguredReadOnlyMode;
use Wikimedia\Rdbms\DatabaseFactory;
use Wikimedia\Rdbms\IConnectionProvider;
use Wikimedia\Rdbms\ReadOnlyMode;
use Wikimedia\RequestTimeout\CriticalSectionProvider;
use Wikimedia\RequestTimeout\RequestTimeout;
use Wikimedia\Stats\StatsCache;
use Wikimedia\Stats\StatsFactory;
use Wikimedia\UUID\GlobalIdGenerator;
use Wikimedia\WRStats\BagOStuffStatsStore;
use Wikimedia\WRStats\WRStatsFactory;
/** @phpcs-require-sorted-array */
return [
'ActionFactory' => static function ( MediaWikiServices $services ): ActionFactory {
return new ActionFactory(
$services->getMainConfig()->get( MainConfigNames::Actions ),
LoggerFactory::getInstance( 'ActionFactory' ),
$services->getObjectFactory(),
$services->getHookContainer()
);
},
'ActorMigration' => static function ( MediaWikiServices $services ): ActorMigration {
return new ActorMigration(
$services->getActorStoreFactory()
);
},
'ActorNormalization' => static function ( MediaWikiServices $services ): ActorNormalization {
return $services->getActorStoreFactory()->getActorNormalization();
},
'ActorStore' => static function ( MediaWikiServices $services ): ActorStore {
return $services->getActorStoreFactory()->getActorStore();
},
'ActorStoreFactory' => static function ( MediaWikiServices $services ): ActorStoreFactory {
return new ActorStoreFactory(
new ServiceOptions( ActorStoreFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getDBLoadBalancerFactory(),
$services->getUserNameUtils(),
$services->getTempUserConfig(),
LoggerFactory::getInstance( 'ActorStore' ),
$services->getHideUserUtils()
);
},
'ArchivedRevisionLookup' => static function ( MediaWikiServices $services ): ArchivedRevisionLookup {
return new ArchivedRevisionLookup(
$services->getConnectionProvider(),
$services->getRevisionStore()
);
},
'AuthManager' => static function ( MediaWikiServices $services ): AuthManager {
$authManager = new AuthManager(
RequestContext::getMain()->getRequest(),
$services->getMainConfig(),
$services->getObjectFactory(),
$services->getHookContainer(),
$services->getReadOnlyMode(),
$services->getUserNameUtils(),
$services->getBlockManager(),
$services->getWatchlistManager(),
$services->getDBLoadBalancer(),
$services->getContentLanguage(),
$services->getLanguageConverterFactory(),
$services->getBotPasswordStore(),
$services->getUserFactory(),
$services->getUserIdentityLookup(),
$services->getUserOptionsManager()
);
$authManager->setLogger( LoggerFactory::getInstance( 'authentication' ) );
return $authManager;
},
'AutoblockExemptionList' => static function ( MediaWikiServices $services ): AutoblockExemptionList {
$messageFormatterFactory = new MessageFormatterFactory( Message::FORMAT_PLAIN );
return new AutoblockExemptionList(
LoggerFactory::getInstance( 'AutoblockExemptionList' ),
$messageFormatterFactory->getTextFormatter(
$services->getContentLanguage()->getCode()
)
);
},
'BacklinkCacheFactory' => static function ( MediaWikiServices $services ): BacklinkCacheFactory {
return new BacklinkCacheFactory(
new ServiceOptions(
BacklinkCache::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getLinksMigration(),
$services->getMainWANObjectCache(),
$services->getHookContainer(),
$services->getConnectionProvider()
);
},
'BadFileLookup' => static function ( MediaWikiServices $services ): BadFileLookup {
return new BadFileLookup(
static function () {
return wfMessage( 'bad_image_list' )->inContentLanguage()->plain();
},
$services->getLocalServerObjectCache(),
$services->getRepoGroup(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getTitleParser(),
$services->getHookContainer()
);
},
'BlobStore' => static function ( MediaWikiServices $services ): BlobStore {
return $services->getService( '_SqlBlobStore' );
},
'BlobStoreFactory' => static function ( MediaWikiServices $services ): BlobStoreFactory {
return new BlobStoreFactory(
$services->getDBLoadBalancerFactory(),
$services->getExternalStoreAccess(),
$services->getMainWANObjectCache(),
new ServiceOptions( BlobStoreFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig() )
);
},
'BlockActionInfo' => static function ( MediaWikiServices $services ): BlockActionInfo {
return new BlockActionInfo( $services->getHookContainer() );
},
'BlockErrorFormatter' => static function ( MediaWikiServices $services ): BlockErrorFormatter {
return $services->getFormatterFactory()->getBlockErrorFormatter(
new LazyLocalizationContext( static function () {
return RequestContext::getMain();
} )
);
},
'BlockManager' => static function ( MediaWikiServices $services ): BlockManager {
return new BlockManager(
new ServiceOptions(
BlockManager::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getUserFactory(),
$services->getUserIdentityUtils(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
LoggerFactory::getInstance( 'BlockManager' ),
$services->getHookContainer(),
$services->getDatabaseBlockStore(),
$services->getProxyLookup()
);
},
'BlockPermissionCheckerFactory' => static function (
MediaWikiServices $services
): BlockPermissionCheckerFactory {
return new BlockPermissionCheckerFactory(
new ServiceOptions(
BlockPermissionCheckerFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getBlockUtils()
);
},
'BlockRestrictionStore' => static function ( MediaWikiServices $services ): BlockRestrictionStore {
return $services->getBlockRestrictionStoreFactory()->getBlockRestrictionStore( WikiAwareEntity::LOCAL );
},
'BlockRestrictionStoreFactory' => static function ( MediaWikiServices $services ): BlockRestrictionStoreFactory {
return new BlockRestrictionStoreFactory(
$services->getDBLoadBalancerFactory(),
$services->getMainConfig()->get( MainConfigNames::BlockTargetMigrationStage )
);
},
'BlockUserFactory' => static function ( MediaWikiServices $services ): BlockUserFactory {
return $services->getService( '_UserBlockCommandFactory' );
},
'BlockUtils' => static function ( MediaWikiServices $services ): BlockUtils {
return $services->getBlockUtilsFactory()->getBlockUtils();
},
'BlockUtilsFactory' => static function ( MediaWikiServices $services ): BlockUtilsFactory {
return new BlockUtilsFactory(
new ServiceOptions(
BlockUtilsFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getActorStoreFactory(),
$services->getUserNameUtils(),
$services->getDBLoadBalancerFactory()
);
},
'BotPasswordStore' => static function ( MediaWikiServices $services ): BotPasswordStore {
return new BotPasswordStore(
new ServiceOptions(
BotPasswordStore::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getCentralIdLookup(),
$services->getDBLoadBalancerFactory()
);
},
'CentralIdLookup' => static function ( MediaWikiServices $services ): CentralIdLookup {
return $services->getCentralIdLookupFactory()->getLookup();
},
'CentralIdLookupFactory' => static function ( MediaWikiServices $services ): CentralIdLookupFactory {
return new CentralIdLookupFactory(
new ServiceOptions( CentralIdLookupFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getObjectFactory(),
$services->getUserIdentityLookup(),
$services->getUserFactory()
);
},
'ChangeTagDefStore' => static function ( MediaWikiServices $services ): NameTableStore {
return $services->getNameTableStoreFactory()->getChangeTagDef();
},
'ChangeTagsStore' => static function ( MediaWikiServices $services ): ChangeTagsStore {
return new ChangeTagsStore(
$services->getConnectionProvider(),
$services->getChangeTagDefStore(),
$services->getMainWANObjectCache(),
$services->getHookContainer(),
LoggerFactory::getInstance( 'ChangeTags' ),
$services->getUserFactory(),
new ServiceOptions(
ChangeTagsStore::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
)
);
},
'ChronologyProtector' => static function ( MediaWikiServices $services ): ChronologyProtector {
$mainConfig = $services->getMainConfig();
$cpStashType = $mainConfig->get( MainConfigNames::ChronologyProtectorStash );
$isMainCacheBad = ObjectCache::isDatabaseId( $mainConfig->get( MainConfigNames::MainCacheType ) );
if ( is_string( $cpStashType ) ) {
$cpStash = ObjectCache::getInstance( $cpStashType );
} elseif ( $isMainCacheBad ) {
$cpStash = new EmptyBagOStuff();
} else {
$cpStash = ObjectCache::getLocalClusterInstance();
}
$chronologyProtector = new ChronologyProtector(
$cpStash,
$mainConfig->get( MainConfigNames::ChronologyProtectorSecret ),
MW_ENTRY_POINT === 'cli',
LoggerFactory::getInstance( 'rdbms' )
);
// Use the global WebRequest singleton. The main reason for using this
// is to call WebRequest::getIP() which is non-trivial to reproduce statically
// because it needs $wgUsePrivateIPs, as well as ProxyLookup and HookRunner services.
// TODO: Create a static version of WebRequest::getIP that accepts these three
// as dependencies, and then call that here. The other uses of $req below can
// trivially use $_COOKIES, $_GET and $_SERVER instead.
$req = RequestContext::getMain()->getRequest();
// Set user IP/agent information for agent session consistency purposes
$reqStart = (int)( $_SERVER['REQUEST_TIME_FLOAT'] ?? time() );
$cpPosInfo = ChronologyProtector::getCPInfoFromCookieValue(
// The cookie has no prefix and is set by MediaWiki::preOutputCommit()
$req->getCookie( 'cpPosIndex', '' ),
// Mitigate broken client-side cookie expiration handling (T190082)
$reqStart - ChronologyProtector::POSITION_COOKIE_TTL
);
$chronologyProtector->setRequestInfo( [
'IPAddress' => $req->getIP(),
'UserAgent' => $req->getHeader( 'User-Agent' ),
'ChronologyPositionIndex' => $req->getInt( 'cpPosIndex', $cpPosInfo['index'] ),
'ChronologyClientId' => $cpPosInfo['clientId'] ?? null,
] );
return $chronologyProtector;
},
'CollationFactory' => static function ( MediaWikiServices $services ): CollationFactory {
return new CollationFactory(
new ServiceOptions(
CollationFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getObjectFactory(),
$services->getHookContainer()
);
},
Introduce CommentFormatter CommentParser: * Move comment formatting backend from Linker to a CommentParser service. Allow link existence and file existence to be batched. * Rename $local to $samePage since I think that is clearer. * Rename $title to $selfLinkTarget since it was unclear what the title was used for. * Rename the "autocomment" concept to "section link" in public interfaces, although the old term remains in CSS classes. * Keep unsafe HTML pass-through in separate "unsafe" methods, for easier static analysis and code review. CommentFormatter: * Add CommentFormatter and RowCommentFormatter services as a usable frontend for comment batches, and to replace the Linker static methods. * Provide fluent and parametric interfaces. Linker: * Remove Linker::makeCommentLink() without deprecation -- nothing calls it and it is obviously an internal helper. * Soft-deprecate Linker methods formatComment(), formatLinksInComment(), commentBlock() and revComment(). Caller migration: * CommentFormatter single: Linker, RollbackAction, ApiComparePages, ApiParse * CommentFormatter parametric batch: ImageHistoryPseudoPager * CommentFormatter fluent batch: ApiQueryFilearchive * RowCommentFormatter sequential: History feed, BlocklistPager, ProtectedPagesPager, ApiQueryProtectedTitles * RowCommentFormatter with index: ChangesFeed, ChangesList, ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges * RevisionCommentBatch: HistoryPager, ContribsPager Bug: T285917 Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
'CommentFormatter' => static function ( MediaWikiServices $services ): CommentFormatter {
return new CommentFormatter(
$services->getCommentParserFactory()
);
},
'CommentParserFactory' => static function ( MediaWikiServices $services ): CommentParserFactory {
return new CommentParserFactory(
$services->getLinkRendererFactory()->create( [ 'renderForComment' => true ] ),
Introduce CommentFormatter CommentParser: * Move comment formatting backend from Linker to a CommentParser service. Allow link existence and file existence to be batched. * Rename $local to $samePage since I think that is clearer. * Rename $title to $selfLinkTarget since it was unclear what the title was used for. * Rename the "autocomment" concept to "section link" in public interfaces, although the old term remains in CSS classes. * Keep unsafe HTML pass-through in separate "unsafe" methods, for easier static analysis and code review. CommentFormatter: * Add CommentFormatter and RowCommentFormatter services as a usable frontend for comment batches, and to replace the Linker static methods. * Provide fluent and parametric interfaces. Linker: * Remove Linker::makeCommentLink() without deprecation -- nothing calls it and it is obviously an internal helper. * Soft-deprecate Linker methods formatComment(), formatLinksInComment(), commentBlock() and revComment(). Caller migration: * CommentFormatter single: Linker, RollbackAction, ApiComparePages, ApiParse * CommentFormatter parametric batch: ImageHistoryPseudoPager * CommentFormatter fluent batch: ApiQueryFilearchive * RowCommentFormatter sequential: History feed, BlocklistPager, ProtectedPagesPager, ApiQueryProtectedTitles * RowCommentFormatter with index: ChangesFeed, ChangesList, ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges * RevisionCommentBatch: HistoryPager, ContribsPager Bug: T285917 Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
$services->getLinkBatchFactory(),
$services->getLinkCache(),
$services->getRepoGroup(),
RequestContext::getMain()->getLanguage(),
$services->getContentLanguage(),
$services->getTitleParser(),
$services->getNamespaceInfo(),
$services->getHookContainer()
);
},
'CommentStore' => static function ( MediaWikiServices $services ): CommentStore {
return new CommentStore( $services->getContentLanguage() );
},
'ConfigFactory' => static function ( MediaWikiServices $services ): ConfigFactory {
// Use the bootstrap config to initialize the ConfigFactory.
$registry = $services->getBootstrapConfig()->get( MainConfigNames::ConfigRegistry );
$factory = new ConfigFactory();
foreach ( $registry as $name => $callback ) {
$factory->register( $name, $callback );
}
return $factory;
},
'ConfigRepository' => static function ( MediaWikiServices $services ): ConfigRepository {
return new ConfigRepository( $services->getConfigFactory() );
},
'ConfigSchema' => static function ( MediaWikiServices $services ): ConfigSchema {
/** @var SettingsBuilder $settings */
$settings = $services->get( '_SettingsBuilder' );
return $settings->getConfigSchema();
},
'ConfiguredReadOnlyMode' => static function ( MediaWikiServices $services ): ConfiguredReadOnlyMode {
$config = $services->getMainConfig();
return new ConfiguredReadOnlyMode(
$config->get( MainConfigNames::ReadOnly ),
$config->get( MainConfigNames::ReadOnlyFile )
);
},
'ConnectionProvider' => static function ( MediaWikiServices $services ): IConnectionProvider {
return $services->getDBLoadBalancerFactory();
},
'ContentHandlerFactory' => static function ( MediaWikiServices $services ): IContentHandlerFactory {
$contentHandlerConfig = $services->getMainConfig()->get( MainConfigNames::ContentHandlers );
return new ContentHandlerFactory(
$contentHandlerConfig,
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getObjectFactory(),
$services->getHookContainer(),
LoggerFactory::getInstance( 'ContentHandler' )
);
},
'ContentLanguage' => static function ( MediaWikiServices $services ): Language {
return $services->getLanguageFactory()->getLanguage(
$services->getMainConfig()->get( MainConfigNames::LanguageCode ) );
},
'ContentModelChangeFactory' => static function ( MediaWikiServices $services ): ContentModelChangeFactory {
return $services->getService( '_PageCommandFactory' );
},
'ContentModelStore' => static function ( MediaWikiServices $services ): NameTableStore {
return $services->getNameTableStoreFactory()->getContentModels();
},
'ContentRenderer' => static function ( MediaWikiServices $services ): ContentRenderer {
Add ParserOutput::{get,set}RenderId() and set render id in ContentRenderer Set the render ID for each parse stored into cache so that we are able to identify a specific parse when there are dependencies (for example in an edit based on that parse). This is recorded as a property added to the ParserOutput, not the parent CacheTime interface. Even though the render ID is /related/ to the CacheTime interface, CacheTime is also used directly as a parser cache key, and the UUID should not be part of the lookup key. In general we are trying to move the location where these cache properties are set as early as possible, so we check at each location to ensure we don't overwrite a previously-set value. Eventually we can convert most of these checks into assertions that the cache properties have already been set (T350538). The primary location for setting cache properties is the ContentRenderer. Moved setting the revision timestamp into ContentRenderer as well, as it was set along the same code paths. An extra parameter was added to ContentRenderer::getParserOutput() to support this. Added merge code to ParserOutput::mergeInternalMetaDataFrom() which should ensure that cache time, revision, timestamp, and render id are all set properly when multiple slots are combined together in MCR. In order to ensure the render ID is set on all codepaths we needed to plumb the GlobalIdGenerator service into ContentRenderer, ParserCache, ParserCacheFactory, and RevisionOutputCache. Eventually (T350538) it should only be necessary in the ContentRenderer. Bug: T350538 Bug: T349868 Followup-To: Ic9b7cc0fcf365e772b7d080d76a065e3fd585f80 Change-Id: I72c5e6f86b7f081ab5ce7a56f5365d2f75067a78
2023-09-14 16:11:20 +00:00
return new ContentRenderer(
$services->getContentHandlerFactory(),
$services->getGlobalIdGenerator()
);
},
'ContentTransformer' => static function ( MediaWikiServices $services ): ContentTransformer {
return new ContentTransformer( $services->getContentHandlerFactory() );
},
'ContributionsLookup' => static function ( MediaWikiServices $services ): ContributionsLookup {
return new ContributionsLookup(
$services->getRevisionStore(),
$services->getLinkRendererFactory(),
$services->getLinkBatchFactory(),
$services->getHookContainer(),
$services->getConnectionProvider(),
Introduce CommentFormatter CommentParser: * Move comment formatting backend from Linker to a CommentParser service. Allow link existence and file existence to be batched. * Rename $local to $samePage since I think that is clearer. * Rename $title to $selfLinkTarget since it was unclear what the title was used for. * Rename the "autocomment" concept to "section link" in public interfaces, although the old term remains in CSS classes. * Keep unsafe HTML pass-through in separate "unsafe" methods, for easier static analysis and code review. CommentFormatter: * Add CommentFormatter and RowCommentFormatter services as a usable frontend for comment batches, and to replace the Linker static methods. * Provide fluent and parametric interfaces. Linker: * Remove Linker::makeCommentLink() without deprecation -- nothing calls it and it is obviously an internal helper. * Soft-deprecate Linker methods formatComment(), formatLinksInComment(), commentBlock() and revComment(). Caller migration: * CommentFormatter single: Linker, RollbackAction, ApiComparePages, ApiParse * CommentFormatter parametric batch: ImageHistoryPseudoPager * CommentFormatter fluent batch: ApiQueryFilearchive * RowCommentFormatter sequential: History feed, BlocklistPager, ProtectedPagesPager, ApiQueryProtectedTitles * RowCommentFormatter with index: ChangesFeed, ChangesList, ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges * RevisionCommentBatch: HistoryPager, ContribsPager Bug: T285917 Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
$services->getNamespaceInfo(),
$services->getCommentFormatter()
);
},
'CriticalSectionProvider' => static function ( MediaWikiServices $services ): CriticalSectionProvider {
$config = $services->getMainConfig();
$limit = MW_ENTRY_POINT === 'cli' ? INF : $config->get( MainConfigNames::CriticalSectionTimeLimit );
return RequestTimeout::singleton()->createCriticalSectionProvider( $limit );
},
'CryptHKDF' => static function ( MediaWikiServices $services ): CryptHKDF {
$config = $services->getMainConfig();
$secret = $config->get( MainConfigNames::HKDFSecret ) ?: $config->get( MainConfigNames::SecretKey );
if ( !$secret ) {
throw new RuntimeException( "Cannot use MWCryptHKDF without a secret." );
}
// In HKDF, the context can be known to the attacker, but this will
// keep simultaneous runs from producing the same output.
$context = [ microtime(), getmypid(), gethostname() ];
// Setup salt cache. Use APC, or fallback to the main cache if it isn't setup
$cache = $services->getLocalServerObjectCache();
if ( $cache instanceof EmptyBagOStuff ) {
$cache = ObjectCache::getLocalClusterInstance();
}
return new CryptHKDF( $secret, $config->get( MainConfigNames::HKDFAlgorithm ), $cache, $context );
},
'DatabaseBlockStore' => static function ( MediaWikiServices $services ): DatabaseBlockStore {
return $services->getDatabaseBlockStoreFactory()->getDatabaseBlockStore( DatabaseBlock::LOCAL );
},
'DatabaseBlockStoreFactory' => static function ( MediaWikiServices $services ): DatabaseBlockStoreFactory {
return new DatabaseBlockStoreFactory(
new ServiceOptions(
DatabaseBlockStoreFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
LoggerFactory::getInstance( 'DatabaseBlockStore' ),
$services->getActorStoreFactory(),
$services->getBlockRestrictionStoreFactory(),
$services->getCommentStore(),
$services->getHookContainer(),
$services->getDBLoadBalancerFactory(),
$services->getReadOnlyMode(),
$services->getUserFactory(),
$services->getTempUserConfig(),
$services->getBlockUtilsFactory(),
$services->getAutoblockExemptionList()
);
},
'DatabaseFactory' => static function ( MediaWikiServices $services ): DatabaseFactory {
return new DatabaseFactory(
[ 'debugSql' => $services->getMainConfig()->get( MainConfigNames::DebugDumpSql ) ]
);
},
'DateFormatterFactory' => static function ( MediaWikiServices $services ): DateFormatterFactory {
return new DateFormatterFactory();
},
'DBLoadBalancer' => static function ( MediaWikiServices $services ): Wikimedia\Rdbms\ILoadBalancer {
// just return the default LB from the DBLoadBalancerFactory service
return $services->getDBLoadBalancerFactory()->getMainLB();
},
'DBLoadBalancerFactory' => static function ( MediaWikiServices $services ): Wikimedia\Rdbms\LBFactory {
$mainConfig = $services->getMainConfig();
$lbFactoryConfigBuilder = $services->getDBLoadBalancerFactoryConfigBuilder();
$lbConf = $lbFactoryConfigBuilder->applyDefaultConfig(
$mainConfig->get( MainConfigNames::LBFactoryConf )
);
$class = $lbFactoryConfigBuilder->getLBFactoryClass( $lbConf );
$instance = new $class( $lbConf );
$lbFactoryConfigBuilder->setDomainAliases( $instance );
// NOTE: This accesses ProxyLookup from the MediaWikiServices singleton
// for non-essential non-nonimal purposes (via WebRequest::getIP).
// This state is fine (and meant) to be consistent for a given PHP process,
// even if applied to the service container for a different wiki.
$lbFactoryConfigBuilder->applyGlobalState(
$instance,
$mainConfig,
$services->getStatsdDataFactory()
);
return $instance;
},
'DBLoadBalancerFactoryConfigBuilder' => static function ( MediaWikiServices $services ): MWLBFactory {
$mainConfig = $services->getMainConfig();
if ( ObjectCache::isDatabaseId( $mainConfig->get( MainConfigNames::MainCacheType ) ) ) {
$wanCache = WANObjectCache::newEmpty();
} else {
$wanCache = $services->getMainWANObjectCache();
}
$srvCache = $services->getLocalServerObjectCache();
if ( $srvCache instanceof EmptyBagOStuff ) {
// Use process cache if no APCU or other local-server cache (e.g. on CLI)
$srvCache = new HashBagOStuff( [ 'maxKeys' => 100 ] );
}
return new MWLBFactory(
new ServiceOptions( MWLBFactory::APPLY_DEFAULT_CONFIG_OPTIONS, $services->getMainConfig() ),
new ConfiguredReadOnlyMode(
$mainConfig->get( MainConfigNames::ReadOnly ),
$mainConfig->get( MainConfigNames::ReadOnlyFile )
),
$services->getChronologyProtector(),
$srvCache,
rdbms: detect corrupt Database instances due to critical section failure This checks that the Database state has not diverged from the driver DB handle nor server-side connection state due to an exception being thrown in an unexpected place within internal Database methods. DB handles with possible state corruption will not accept queries. For example, a PHP extension like Excimer might be used to throw request timeout exceptions. Such exceptions can trigger after any PHP or Zend function returns, e.g. within DatabaseMysqlBase::doSelectDomain() after the "USE" query completes but before $this->currentDomain gets updated. Also: * Make getApproximateLagStatus() catch getLag() errors since begin() expects it to simply use "false" for the lag value on failure. This helps assure that $this->trxAutomatic gets properly set. * Unsuppress exceptions in runOnTransactionPreCommitCallbacks() as the transaction needs to get aborted anyway (as already happens). * Unsuppress exceptions in runOnAtomicSectionCancelCallbacks() since the safest thing to do is just roll back the transaction. * Only suppress DBError exceptions in runOnTransactionIdleCallbacks(). and runTransactionListenerCallbacks(). Return the array of errors rather than throw the first one. Most of the callers had to catch the errors, so it's easier to avoid throwing them to begin with. * Avoid blanket try/catch in sourceStream(), doReplace(), upsert(), doInsertSelectGenericand(). * Clarify various code comments and add missing @internal tags. Bug: T193565 Change-Id: I6b7b02c02b24c2ff01094af3df54c989fe504af7
2020-12-01 05:08:32 +00:00
$wanCache,
$services->getCriticalSectionProvider(),
$services->getStatsdDataFactory(),
ExtensionRegistry::getInstance()->getAttribute( 'DatabaseVirtualDomains' )
);
},
'DefaultOutputPipeline' => static function ( MediaWikiServices $services ): OutputTransformPipeline {
return ( new DefaultOutputPipelineFactory(
$services->getHookContainer(),
$services->getTidy(),
parser: Move lang/dir and mw-content-ltr to ParserOutput::getText == Skin::wrapHTML == Skin::wrapHTML no longer has to perform any guessing of the ParserOutput language. Nor does it have to special wiki pages vs special pages in this regard. Yay, code removal. == ImagePage == On URLs like /wiki/File:Example.jpg, the main output handler is ImagePage::view. This calls the parent Article::view to handle most of its output. Article::view obtains the ParserOptions, and then fetches ParserOutput, and then adds `<div class=mw-parser-output>` and its metadata to OutputPage. Before this change, ImagePage::view was creating a wrapper based on "predicting" what language the ParserOutput will contain. It couldn't call the new OutputPage::getContentLanguage or some equivalent as Article::view wouldn't have populated that yet. This leaky abstraction is fixed by this change as now the `<div>` from ParserOutput no longer comes with a "please wrap it properly" contract that Article subclasses couldn't possibly implement correctly (it coudln't wrap it after the fact because Article::view writes to OutputPage directly). RECENT (T310445): A special case was recently added for file pages about translated SVGs. For those, we decide which language to use for the "fullMedia" thumb atop the page. This was recently changed as part of T310445 from a hardcoded $wgLanguageCode (site content lang) to new problematic Title::getPageViewLanguage, which tries to guestimate the page language of the rendered ParserOutput and then gets the preferred variant for the current user. The motivation for this was to support language variants but used Title::getPageViewLanguage as a kitchen sink to achieve that minor side-effect. The only part of this now-deprecated method that we actually need is LanguageConverter::getPreferredVariant(). Test plan: Covered by ImagePageTest. == Skin mainpage-title == RECENT (T331095, T298715): A special case was added to Skin::getTemplateData that powers the mainpage-title interface message feature. This is empty by default, but when created via MediaWiki:mainpage-title allows interface admins to replace the H1 with a custom and localised page heading. A few months ago, in Ifc9f0a7174, Title::getPageViewLanguage was applied here to support language variants. Replace with the same fix as for ImagePage. Revert back to Message::inContentLanguage() but refactor to inLanguage() via MediaWikiServices::getContentLanguage so that LanguageConverter::getPreferredVariant can be applied. == EditPage == This was doing similar "predicting" of the ParserOutput language to create an empty preview placeholder for use by preview.js. Now that ApiParse (via ParserOutput::getText) returns a usable element without any secret "you magically know the right class, lang, and dir" contract, this placeholder is no longer needed. Test Plan: * EditPage: Default preview 1. index.php?title=Main_Page&action=edit 2. Show preview 3. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr> * EditPage: JS preview 1. Preferences > Editing > Show preview without reload 2. index.php?title=Main_Page&action=edit 3. Show preview 4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr> 5. Type something and 'Show preview' again 6. Assert old element gone, new text is shown, and new element attributes are the same as the above. == McrUndoAction == Same as EditPage basically, but without the JS preview use case. == DifferenceEngine == Test: 1. Open /w/index.php?title=Main_Page&diff=0 (this shows the latest diff, can do manually by viewing /wiki/Main_Page, click "View history", click "Compare selected revisions") 2. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr> 3. Open /w/index.php?title=Main_Page&diff=0&action=render 4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr> == Special:ExpandTemplates == Test: 1. /wiki/Special:ExpandTemplates 2. Write "Hello". 3. "OK" 4. Assert <div class="mw-content-ltr mw-parser-output" lang=en dir=ltr> Bug: T341244 Depends-On: Icd9c079f5896ee83d86b9c2699636dc81d25a14c Depends-On: I4e7484b3b94f1cb6062e7cef9f20626b650bb4b1 Depends-On: I90b88f3b3a3bbeba4f48d118f92f54864997e105 Change-Id: Ib130a055e46764544af0f1a46d2bc2b3a7ee85b7
2023-10-04 04:45:07 +00:00
$services->getLanguageFactory(),
$services->getContentLanguage(),
LoggerFactory::getInstance( 'Parser' ),
$services->getTitleFactory()
) )->buildPipeline();
},
'DeletePageFactory' => static function ( MediaWikiServices $services ): DeletePageFactory {
return $services->getService( '_PageCommandFactory' );
},
'Emailer' => static function ( MediaWikiServices $services ): IEmailer {
return new Emailer();
},
'EmailUserFactory' => static function ( MediaWikiServices $services ): EmailUserFactory {
return new EmailUserFactory(
new ServiceOptions( EmailUser::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getHookContainer(),
$services->getUserOptionsLookup(),
$services->getCentralIdLookup(),
$services->getUserFactory(),
$services->getEmailer(),
$services->getMessageFormatterFactory(),
$services->getMessageFormatterFactory()->getTextFormatter( $services->getContentLanguage()->getCode() )
);
},
'EventRelayerGroup' => static function ( MediaWikiServices $services ): EventRelayerGroup {
return new EventRelayerGroup( $services->getMainConfig()->get( MainConfigNames::EventRelayerConfig ) );
},
'ExtensionRegistry' => static function ( MediaWikiServices $services ): ExtensionRegistry {
return ExtensionRegistry::getInstance();
},
'ExternalStoreAccess' => static function ( MediaWikiServices $services ): ExternalStoreAccess {
return new ExternalStoreAccess(
$services->getExternalStoreFactory(),
LoggerFactory::getInstance( 'ExternalStore' )
);
},
'ExternalStoreFactory' => static function ( MediaWikiServices $services ): ExternalStoreFactory {
$config = $services->getMainConfig();
$writeStores = $config->get( MainConfigNames::DefaultExternalStore );
return new ExternalStoreFactory(
$config->get( MainConfigNames::ExternalStores ),
( $writeStores !== false ) ? (array)$writeStores : [],
$services->getDBLoadBalancer()->getLocalDomainID(),
LoggerFactory::getInstance( 'ExternalStore' )
);
},
'FileBackendGroup' => static function ( MediaWikiServices $services ): FileBackendGroup {
$mainConfig = $services->getMainConfig();
$ld = WikiMap::getCurrentWikiDbDomain();
$fallbackWikiId = WikiMap::getWikiIdFromDbDomain( $ld );
// If the local wiki ID and local domain ID do not match, probably due to a non-default
// schema, issue a warning. A non-default schema indicates that it might be used to
// disambiguate different wikis.
$legacyDomainId = strlen( $ld->getTablePrefix() )
? "{$ld->getDatabase()}-{$ld->getTablePrefix()}"
: $ld->getDatabase();
if ( $ld->getSchema() !== null && $legacyDomainId !== $fallbackWikiId ) {
wfWarn(
"Legacy default 'domainId' is '$legacyDomainId' but wiki ID is '$fallbackWikiId'."
);
}
$cache = $services->getLocalServerObjectCache();
if ( $cache instanceof EmptyBagOStuff ) {
$cache = new HashBagOStuff();
}
return new FileBackendGroup(
new ServiceOptions( FileBackendGroup::CONSTRUCTOR_OPTIONS, $mainConfig,
[ 'fallbackWikiId' => $fallbackWikiId ] ),
$services->getReadOnlyMode(),
$cache,
$services->getMainWANObjectCache(),
$services->getMimeAnalyzer(),
$services->getLockManagerGroupFactory(),
$services->getTempFSFileFactory(),
$services->getObjectFactory()
);
},
'FormatterFactory' => static function ( MediaWikiServices $services ): FormatterFactory {
return new FormatterFactory(
$services->getMessageCache(),
$services->getTitleFormatter(),
$services->getHookContainer(),
$services->getUserIdentityUtils(),
$services->getLanguageFactory()
);
},
'GenderCache' => static function ( MediaWikiServices $services ): GenderCache {
$nsInfo = $services->getNamespaceInfo();
// Database layer may be disabled, so processing without database connection
$dbLoadBalancer = $services->isServiceDisabled( 'DBLoadBalancer' )
? null
: $services->getDBLoadBalancerFactory();
return new GenderCache( $nsInfo, $dbLoadBalancer, $services->get( '_DefaultOptionsLookup' ) );
},
'GlobalIdGenerator' => static function ( MediaWikiServices $services ): GlobalIdGenerator {
$mainConfig = $services->getMainConfig();
return new GlobalIdGenerator(
$mainConfig->get( MainConfigNames::TmpDirectory ),
static function ( $command ) {
return wfShellExec( $command );
}
);
},
'GrantsInfo' => static function ( MediaWikiServices $services ): GrantsInfo {
return new GrantsInfo(
new ServiceOptions(
GrantsInfo::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
)
);
},
'GrantsLocalization' => static function ( MediaWikiServices $services ): GrantsLocalization {
return new GrantsLocalization(
$services->getGrantsInfo(),
$services->getLinkRenderer(),
$services->getLanguageFactory(),
$services->getContentLanguage()
);
},
'GroupPermissionsLookup' => static function ( MediaWikiServices $services ): GroupPermissionsLookup {
return new GroupPermissionsLookup(
new ServiceOptions( GroupPermissionsLookup::CONSTRUCTOR_OPTIONS, $services->getMainConfig() )
);
},
'HideUserUtils' => static function ( MediaWikiServices $services ): HideUserUtils {
return new HideUserUtils(
$services->getMainConfig()->get( MainConfigNames::BlockTargetMigrationStage )
);
},
'HookContainer' => static function ( MediaWikiServices $services ): HookContainer {
// NOTE: This is called while $services is being initialized, in order to call the
// MediaWikiServices hook.
$configHooks = $services->getBootstrapConfig()->get( MainConfigNames::Hooks );
// If we are instantiating this service after $wgHooks was replaced by a fake,
// get the original array out of the object. This should only happen in the installer,
// when it calls resetMediaWikiServices().
if ( $configHooks instanceof FauxGlobalHookArray ) {
$configHooks = $configHooks->getOriginalArray();
}
Hook Container New classes and modificatons to existing classes to support the new Hooks system. All changes are documented in RFC https://phabricator.wikimedia.org/T240307. - HookContainer.php: Class for doing much of what Hooks.php has historically done, but enabling new-style hooks to be processed and registered. Changes include new ways of defining hook handler functions as an object with defined dependencies in extension.json, removing runWithoutAbort() and addit it to an $options parameter to be passed to HookContainer::run(), being able to decipher whether a hook handler is legacy or non-legacy style and run them in the appropriate way, etc. - DeprecatedHooks.php: For marking hooks deprecated and verifying if one is deprecated - DeprecatedHooksTest.php: Unit tests for DeprecatedHooks.php - Hooks.php: register() will now additionally register hooks with handlers in new HooksContainer.php. getHandlers() will be a legacy wrapper for calling the newer HookContainer::getHandlers() - MediaWikiServices.php: Added getHookContainer() for retrieving HookContainer singleton - ExtensionProcessor.php: modified extractHooks() to be able to extract new style handler objects being registered in extension.json - ServiceWiring.php: Added HookContainer to list of services to return - HookContainerTest.php: Unit tests for HookContainer.php - ExtensionProcessorTest.php: Moved file out of /unit folder and now extends MediaWikiTestCase instead of MediaWikiUnitTestCase (as the tests are not truly unit tests). Modified existing tests for ExtensionProcessor::extractHooks() to include a test case for new style handler Bug: T240307 Change-Id: I432861d8995cfd7180e77e115251d8055b7eceec
2020-02-10 14:47:46 +00:00
$extRegistry = ExtensionRegistry::getInstance();
$extHooks = $extRegistry->getAttribute( 'Hooks' );
Hook Container New classes and modificatons to existing classes to support the new Hooks system. All changes are documented in RFC https://phabricator.wikimedia.org/T240307. - HookContainer.php: Class for doing much of what Hooks.php has historically done, but enabling new-style hooks to be processed and registered. Changes include new ways of defining hook handler functions as an object with defined dependencies in extension.json, removing runWithoutAbort() and addit it to an $options parameter to be passed to HookContainer::run(), being able to decipher whether a hook handler is legacy or non-legacy style and run them in the appropriate way, etc. - DeprecatedHooks.php: For marking hooks deprecated and verifying if one is deprecated - DeprecatedHooksTest.php: Unit tests for DeprecatedHooks.php - Hooks.php: register() will now additionally register hooks with handlers in new HooksContainer.php. getHandlers() will be a legacy wrapper for calling the newer HookContainer::getHandlers() - MediaWikiServices.php: Added getHookContainer() for retrieving HookContainer singleton - ExtensionProcessor.php: modified extractHooks() to be able to extract new style handler objects being registered in extension.json - ServiceWiring.php: Added HookContainer to list of services to return - HookContainerTest.php: Unit tests for HookContainer.php - ExtensionProcessorTest.php: Moved file out of /unit folder and now extends MediaWikiTestCase instead of MediaWikiUnitTestCase (as the tests are not truly unit tests). Modified existing tests for ExtensionProcessor::extractHooks() to include a test case for new style handler Bug: T240307 Change-Id: I432861d8995cfd7180e77e115251d8055b7eceec
2020-02-10 14:47:46 +00:00
$extDeprecatedHooks = $extRegistry->getAttribute( 'DeprecatedHooks' );
$hookRegistry = new StaticHookRegistry( $configHooks, $extHooks, $extDeprecatedHooks );
$hookContainer = new HookContainer(
$hookRegistry,
$services->getObjectFactory()
Hook Container New classes and modificatons to existing classes to support the new Hooks system. All changes are documented in RFC https://phabricator.wikimedia.org/T240307. - HookContainer.php: Class for doing much of what Hooks.php has historically done, but enabling new-style hooks to be processed and registered. Changes include new ways of defining hook handler functions as an object with defined dependencies in extension.json, removing runWithoutAbort() and addit it to an $options parameter to be passed to HookContainer::run(), being able to decipher whether a hook handler is legacy or non-legacy style and run them in the appropriate way, etc. - DeprecatedHooks.php: For marking hooks deprecated and verifying if one is deprecated - DeprecatedHooksTest.php: Unit tests for DeprecatedHooks.php - Hooks.php: register() will now additionally register hooks with handlers in new HooksContainer.php. getHandlers() will be a legacy wrapper for calling the newer HookContainer::getHandlers() - MediaWikiServices.php: Added getHookContainer() for retrieving HookContainer singleton - ExtensionProcessor.php: modified extractHooks() to be able to extract new style handler objects being registered in extension.json - ServiceWiring.php: Added HookContainer to list of services to return - HookContainerTest.php: Unit tests for HookContainer.php - ExtensionProcessorTest.php: Moved file out of /unit folder and now extends MediaWikiTestCase instead of MediaWikiUnitTestCase (as the tests are not truly unit tests). Modified existing tests for ExtensionProcessor::extractHooks() to include a test case for new style handler Bug: T240307 Change-Id: I432861d8995cfd7180e77e115251d8055b7eceec
2020-02-10 14:47:46 +00:00
);
return $hookContainer;
Hook Container New classes and modificatons to existing classes to support the new Hooks system. All changes are documented in RFC https://phabricator.wikimedia.org/T240307. - HookContainer.php: Class for doing much of what Hooks.php has historically done, but enabling new-style hooks to be processed and registered. Changes include new ways of defining hook handler functions as an object with defined dependencies in extension.json, removing runWithoutAbort() and addit it to an $options parameter to be passed to HookContainer::run(), being able to decipher whether a hook handler is legacy or non-legacy style and run them in the appropriate way, etc. - DeprecatedHooks.php: For marking hooks deprecated and verifying if one is deprecated - DeprecatedHooksTest.php: Unit tests for DeprecatedHooks.php - Hooks.php: register() will now additionally register hooks with handlers in new HooksContainer.php. getHandlers() will be a legacy wrapper for calling the newer HookContainer::getHandlers() - MediaWikiServices.php: Added getHookContainer() for retrieving HookContainer singleton - ExtensionProcessor.php: modified extractHooks() to be able to extract new style handler objects being registered in extension.json - ServiceWiring.php: Added HookContainer to list of services to return - HookContainerTest.php: Unit tests for HookContainer.php - ExtensionProcessorTest.php: Moved file out of /unit folder and now extends MediaWikiTestCase instead of MediaWikiUnitTestCase (as the tests are not truly unit tests). Modified existing tests for ExtensionProcessor::extractHooks() to include a test case for new style handler Bug: T240307 Change-Id: I432861d8995cfd7180e77e115251d8055b7eceec
2020-02-10 14:47:46 +00:00
},
'HtmlCacheUpdater' => static function ( MediaWikiServices $services ): HTMLCacheUpdater {
$config = $services->getMainConfig();
return new HTMLCacheUpdater(
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getHookContainer(),
$services->getTitleFactory(),
$config->get( MainConfigNames::CdnReboundPurgeDelay ),
$config->get( MainConfigNames::UseFileCache ),
$config->get( MainConfigNames::CdnMaxAge )
);
},
'HtmlTransformFactory' => static function ( MediaWikiServices $services ): HtmlTransformFactory {
return new HtmlTransformFactory(
$services->getService( '_Parsoid' ),
$services->getMainConfig()->get( MainConfigNames::ParsoidSettings ),
$services->getParsoidPageConfigFactory(),
$services->getContentHandlerFactory(),
$services->getParsoidSiteConfig(),
$services->getTitleFactory(),
$services->getLanguageConverterFactory(),
$services->getLanguageFactory()
);
},
'HttpRequestFactory' => static function ( MediaWikiServices $services ): HttpRequestFactory {
return new HttpRequestFactory(
new ServiceOptions(
HttpRequestFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
LoggerFactory::getInstance( 'http' ),
Telemetry::getInstance()
);
},
'InterwikiLookup' => static function ( MediaWikiServices $services ): InterwikiLookup {
return new ClassicInterwikiLookup(
new ServiceOptions(
ClassicInterwikiLookup::CONSTRUCTOR_OPTIONS,
$services->getMainConfig(),
[ 'wikiId' => WikiMap::getCurrentWikiId() ]
),
$services->getContentLanguage(),
$services->getMainWANObjectCache(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getHookContainer(),
$services->getConnectionProvider()
);
},
'IntroMessageBuilder' => static function ( MediaWikiServices $services ): IntroMessageBuilder {
return new IntroMessageBuilder(
$services->getMainConfig(),
$services->getLinkRenderer(),
$services->getPermissionManager(),
$services->getUserNameUtils(),
$services->getTempUserCreator(),
$services->getUserFactory(),
$services->getRestrictionStore(),
$services->getDatabaseBlockStore(),
$services->getReadOnlyMode(),
$services->getSpecialPageFactory(),
$services->getRepoGroup(),
$services->getNamespaceInfo(),
$services->getSkinFactory(),
$services->getConnectionProvider()
);
},
'JobFactory' => static function ( MediaWikiServices $services ): JobFactory {
return new JobFactory(
$services->getObjectFactory(),
$services->getMainConfig()->get( MainConfigNames::JobClasses )
);
},
'JobQueueGroup' => static function ( MediaWikiServices $services ): JobQueueGroup {
return $services->getJobQueueGroupFactory()->makeJobQueueGroup();
},
'JobQueueGroupFactory' => static function ( MediaWikiServices $services ): JobQueueGroupFactory {
return new JobQueueGroupFactory(
new ServiceOptions( JobQueueGroupFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getReadOnlyMode(),
$services->getStatsdDataFactory(),
$services->getMainWANObjectCache(),
$services->getGlobalIdGenerator()
);
},
'JobRunner' => static function ( MediaWikiServices $services ): JobRunner {
return new JobRunner(
new ServiceOptions( JobRunner::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getDBLoadBalancerFactory(),
$services->getJobQueueGroup(),
$services->getReadOnlyMode(),
$services->getLinkCache(),
$services->getStatsdDataFactory(),
LoggerFactory::getInstance( 'runJobs' )
);
},
'JsonCodec' => static function ( MediaWikiServices $services ): JsonCodec {
return new JsonCodec();
},
'LanguageConverterFactory' => static function ( MediaWikiServices $services ): LanguageConverterFactory {
return new LanguageConverterFactory(
new ServiceOptions( LanguageConverterFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getObjectFactory(),
static function () use ( $services ) {
return $services->getContentLanguage();
}
);
},
'LanguageFactory' => static function ( MediaWikiServices $services ): LanguageFactory {
return new LanguageFactory(
new ServiceOptions( LanguageFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getNamespaceInfo(),
$services->getLocalisationCache(),
$services->getLanguageNameUtils(),
$services->getLanguageFallback(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getLanguageConverterFactory(),
$services->getHookContainer(),
$services->getMainConfig()
);
},
'LanguageFallback' => static function ( MediaWikiServices $services ): LanguageFallback {
return new LanguageFallback(
$services->getMainConfig()->get( MainConfigNames::LanguageCode ),
$services->getLocalisationCache(),
$services->getLanguageNameUtils()
);
},
'LanguageNameUtils' => static function ( MediaWikiServices $services ): LanguageNameUtils {
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
return new LanguageNameUtils(
new ServiceOptions(
LanguageNameUtils::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getHookContainer()
);
},
'LinkBatchFactory' => static function ( MediaWikiServices $services ): LinkBatchFactory {
return new LinkBatchFactory(
$services->getLinkCache(),
$services->getTitleFormatter(),
$services->getContentLanguage(),
$services->getGenderCache(),
$services->getConnectionProvider(),
$services->getLinksMigration(),
LoggerFactory::getInstance( 'LinkBatch' )
);
},
'LinkCache' => static function ( MediaWikiServices $services ): LinkCache {
// Database layer may be disabled, so processing without database connection
$dbLoadBalancer = $services->isServiceDisabled( 'DBLoadBalancer' )
? null
: $services->getDBLoadBalancer();
$linkCache = new LinkCache(
$services->getTitleFormatter(),
$services->getMainWANObjectCache(),
$services->getNamespaceInfo(),
$dbLoadBalancer
);
$linkCache->setLogger( LoggerFactory::getInstance( 'LinkCache' ) );
return $linkCache;
},
'LinkRenderer' => static function ( MediaWikiServices $services ): LinkRenderer {
return $services->getLinkRendererFactory()->create();
},
'LinkRendererFactory' => static function ( MediaWikiServices $services ): LinkRendererFactory {
return new LinkRendererFactory(
$services->getTitleFormatter(),
$services->getLinkCache(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getSpecialPageFactory(),
$services->getHookContainer()
);
},
'LinksMigration' => static function ( MediaWikiServices $services ): LinksMigration {
return new LinksMigration(
$services->getMainConfig(),
$services->getLinkTargetLookup()
);
},
'LinkTargetLookup' => static function ( MediaWikiServices $services ): LinkTargetLookup {
return new LinkTargetStore(
$services->getConnectionProvider(),
$services->getLocalServerObjectCache(),
$services->getMainWANObjectCache()
);
},
'LocalisationCache' => static function ( MediaWikiServices $services ): LocalisationCache {
$conf = $services->getMainConfig()->get( MainConfigNames::LocalisationCacheConf );
$logger = LoggerFactory::getInstance( 'localisation' );
$store = LocalisationCache::getStoreFromConf(
$conf, $services->getMainConfig()->get( MainConfigNames::CacheDirectory ) );
$logger->debug( 'LocalisationCache using store ' . get_class( $store ) );
return new $conf['class'](
new ServiceOptions(
LocalisationCache::CONSTRUCTOR_OPTIONS,
// Two of the options are stored in $wgLocalisationCacheConf
$conf,
// In case someone set that config variable and didn't reset all keys, set defaults.
[
'forceRecache' => false,
'manualRecache' => false,
],
// Some other options come from config itself
$services->getMainConfig()
),
$store,
$logger,
[ static function () use ( $services ) {
// NOTE: Make sure we use the same cache object that is assigned in the
// constructor of the MessageBlobStore class used by ResourceLoader.
// T231866: Avoid circular dependency via ResourceLoader.
MessageBlobStore::clearGlobalCacheEntry( $services->getMainWANObjectCache() );
} ],
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getLanguageNameUtils(),
$services->getHookContainer()
);
},
'LocalServerObjectCache' => static function ( MediaWikiServices $services ): BagOStuff {
return $services->getObjectCacheFactory()->getInstance( CACHE_ACCEL );
},
'LockManagerGroupFactory' => static function ( MediaWikiServices $services ): LockManagerGroupFactory {
return new LockManagerGroupFactory(
WikiMap::getCurrentWikiDbDomain()->getId(),
$services->getMainConfig()->get( MainConfigNames::LockManagers )
);
},
'MagicWordFactory' => static function ( MediaWikiServices $services ): MagicWordFactory {
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
return new MagicWordFactory(
$services->getContentLanguage(),
$services->getHookContainer()
);
},
'MainConfig' => static function ( MediaWikiServices $services ): Config {
// Use the 'main' config from the ConfigFactory service.
return $services->getConfigFactory()->makeConfig( 'main' );
},
'MainObjectStash' => static function ( MediaWikiServices $services ): BagOStuff {
$mainConfig = $services->getMainConfig();
$id = $mainConfig->get( MainConfigNames::MainStash );
$store = $services->getObjectCacheFactory()->getInstance( $id );
$store->getLogger()->debug( 'MainObjectStash using store {class}', [
'class' => get_class( $store )
] );
return $store;
},
'MainWANObjectCache' => static function ( MediaWikiServices $services ): WANObjectCache {
$mainConfig = $services->getMainConfig();
objectcache: Remove $wgMainWANCache and $wgWANObjectCaches We always wrap the local cluster cache, and there are no subclasses of WANObjectCache. It was never documented or recommended how these would be used. It is a left-over from the original 2015 Multi-DC plan in which WANObjectCache would work differently. See task for details. Note that this requires no configuration changes, even in the theoretical case of these variables being used, as the only option is to use the main cache, and that's also the default. * Update WAN overrides to override the underlying main cache instead. * Fix EditPageTest which was previously implicitly using a 'hash' as main cache but also relying on wan cache to be 'none'. The part that it actually needs is the 'none'. When WAN cache is enabled, testUpdateNoMinor fails due to an edit conflict because one of the edits it makes is made with a current timestamp whereas it expects to simulate wpEdittime in the year 2012 which, when caching is enabled, is ignored and becomes the current time instead. I don't understand exactly why, but I'm going to conserve that behaviour for now. * Fix TemplateCategoriesTest, which was failing due to an unexpected cache hit: > [objectcache] fetchOrRegenerate(…:page:10:…): volatile hit This could be solved in a more realistic way by splitting the test, or by explicitly resetting services half-way the test to clear WikiPageFactory, PageStore and WANCache process state. For now, keep the prior behaviour of no cache in this test. Bug: T305093 Bug: T329680 Depends-On: If890622eed0d0f8b4bd73d36ba1815a3d760ea05 Depends-On: Ie1def75208822bdf19bb2cfd7e6edf32c2000e6b Depends-On: I35cce61dc3ee90dcee3dd6f0b36f84133be029ed Change-Id: I53781a8c06ebb2583f6ca83dd91bbfe8a5c88b13
2023-02-14 21:43:12 +00:00
$store = $services->get( '_LocalClusterCache' );
$logger = $store->getLogger();
$logger->debug( 'MainWANObjectCache using store {class}', [
'class' => get_class( $store )
] );
objectcache: Remove $wgMainWANCache and $wgWANObjectCaches We always wrap the local cluster cache, and there are no subclasses of WANObjectCache. It was never documented or recommended how these would be used. It is a left-over from the original 2015 Multi-DC plan in which WANObjectCache would work differently. See task for details. Note that this requires no configuration changes, even in the theoretical case of these variables being used, as the only option is to use the main cache, and that's also the default. * Update WAN overrides to override the underlying main cache instead. * Fix EditPageTest which was previously implicitly using a 'hash' as main cache but also relying on wan cache to be 'none'. The part that it actually needs is the 'none'. When WAN cache is enabled, testUpdateNoMinor fails due to an edit conflict because one of the edits it makes is made with a current timestamp whereas it expects to simulate wpEdittime in the year 2012 which, when caching is enabled, is ignored and becomes the current time instead. I don't understand exactly why, but I'm going to conserve that behaviour for now. * Fix TemplateCategoriesTest, which was failing due to an unexpected cache hit: > [objectcache] fetchOrRegenerate(…:page:10:…): volatile hit This could be solved in a more realistic way by splitting the test, or by explicitly resetting services half-way the test to clear WikiPageFactory, PageStore and WANCache process state. For now, keep the prior behaviour of no cache in this test. Bug: T305093 Bug: T329680 Depends-On: If890622eed0d0f8b4bd73d36ba1815a3d760ea05 Depends-On: Ie1def75208822bdf19bb2cfd7e6edf32c2000e6b Depends-On: I35cce61dc3ee90dcee3dd6f0b36f84133be029ed Change-Id: I53781a8c06ebb2583f6ca83dd91bbfe8a5c88b13
2023-02-14 21:43:12 +00:00
$wanParams = $mainConfig->get( MainConfigNames::WANObjectCache ) + [
'cache' => $store,
'logger' => $logger,
'secret' => $mainConfig->get( MainConfigNames::SecretKey ),
];
if ( MW_ENTRY_POINT !== 'cli' ) {
// Send the statsd data post-send on HTTP requests; avoid in CLI mode (T181385)
$wanParams['stats'] = $services->getStatsdDataFactory();
// Let pre-emptive refreshes happen post-send on HTTP requests
$wanParams['asyncHandler'] = [ DeferredUpdates::class, 'addCallableUpdate' ];
}
objectcache: Remove $wgMainWANCache and $wgWANObjectCaches We always wrap the local cluster cache, and there are no subclasses of WANObjectCache. It was never documented or recommended how these would be used. It is a left-over from the original 2015 Multi-DC plan in which WANObjectCache would work differently. See task for details. Note that this requires no configuration changes, even in the theoretical case of these variables being used, as the only option is to use the main cache, and that's also the default. * Update WAN overrides to override the underlying main cache instead. * Fix EditPageTest which was previously implicitly using a 'hash' as main cache but also relying on wan cache to be 'none'. The part that it actually needs is the 'none'. When WAN cache is enabled, testUpdateNoMinor fails due to an edit conflict because one of the edits it makes is made with a current timestamp whereas it expects to simulate wpEdittime in the year 2012 which, when caching is enabled, is ignored and becomes the current time instead. I don't understand exactly why, but I'm going to conserve that behaviour for now. * Fix TemplateCategoriesTest, which was failing due to an unexpected cache hit: > [objectcache] fetchOrRegenerate(…:page:10:…): volatile hit This could be solved in a more realistic way by splitting the test, or by explicitly resetting services half-way the test to clear WikiPageFactory, PageStore and WANCache process state. For now, keep the prior behaviour of no cache in this test. Bug: T305093 Bug: T329680 Depends-On: If890622eed0d0f8b4bd73d36ba1815a3d760ea05 Depends-On: Ie1def75208822bdf19bb2cfd7e6edf32c2000e6b Depends-On: I35cce61dc3ee90dcee3dd6f0b36f84133be029ed Change-Id: I53781a8c06ebb2583f6ca83dd91bbfe8a5c88b13
2023-02-14 21:43:12 +00:00
return new WANObjectCache( $wanParams );
},
'MediaHandlerFactory' => static function ( MediaWikiServices $services ): MediaHandlerFactory {
return new MediaHandlerFactory(
LoggerFactory::getInstance( 'MediaHandlerFactory' ),
$services->getMainConfig()->get( MainConfigNames::MediaHandlers )
);
},
'MergeHistoryFactory' => static function ( MediaWikiServices $services ): MergeHistoryFactory {
return $services->getService( '_PageCommandFactory' );
},
'MessageCache' => static function ( MediaWikiServices $services ): MessageCache {
$mainConfig = $services->getMainConfig();
$clusterCache = ObjectCache::getInstance( $mainConfig->get( MainConfigNames::MessageCacheType ) );
$srvCache = $mainConfig->get( MainConfigNames::UseLocalMessageCache )
? $services->getLocalServerObjectCache()
: new EmptyBagOStuff();
$logger = LoggerFactory::getInstance( 'MessageCache' );
$logger->debug( 'MessageCache using store {class}', [
'class' => get_class( $clusterCache )
] );
$options = new ServiceOptions( MessageCache::CONSTRUCTOR_OPTIONS, $mainConfig );
return new MessageCache(
$services->getMainWANObjectCache(),
$clusterCache,
$srvCache,
$services->getContentLanguage(),
$services->getLanguageConverterFactory(),
$logger,
$options,
$services->getLanguageFactory(),
$services->getLocalisationCache(),
$services->getLanguageNameUtils(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getLanguageFallback(),
$services->getHookContainer(),
$services->getParserFactory()
);
},
'MessageFormatterFactory' => static function ( MediaWikiServices $services ): IMessageFormatterFactory {
return new MessageFormatterFactory();
},
'MicroStash' => static function ( MediaWikiServices $services ): BagOStuff {
$mainConfig = $services->getMainConfig();
$id = $mainConfig->get( MainConfigNames::MicroStashType );
$store = $services->getObjectCacheFactory()->getInstance( $id );
$store->getLogger()->debug( 'MicroStash using store {class}', [
'class' => get_class( $store )
] );
return $store;
},
'MimeAnalyzer' => static function ( MediaWikiServices $services ): MimeAnalyzer {
$logger = LoggerFactory::getInstance( 'Mime' );
$mainConfig = $services->getMainConfig();
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$hookRunner = new HookRunner( $services->getHookContainer() );
$params = [
'typeFile' => $mainConfig->get( MainConfigNames::MimeTypeFile ),
'infoFile' => $mainConfig->get( MainConfigNames::MimeInfoFile ),
'xmlTypes' => $mainConfig->get( MainConfigNames::XMLMimeTypes ),
'guessCallback' => static function (
$mimeAnalyzer, &$head, &$tail, $file, &$mime
) use ( $logger, $hookRunner ) {
// Also test DjVu
$deja = new DjVuImage( $file );
if ( $deja->isValid() ) {
$logger->info( "Detected $file as image/vnd.djvu\n" );
$mime = 'image/vnd.djvu';
return;
}
// Some strings by reference for performance - assuming well-behaved hooks
$hookRunner->onMimeMagicGuessFromContent(
$mimeAnalyzer, $head, $tail, $file, $mime );
},
'extCallback' => static function ( $mimeAnalyzer, $ext, &$mime ) use ( $hookRunner ) {
// Media handling extensions can improve the MIME detected
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$hookRunner->onMimeMagicImproveFromExtension( $mimeAnalyzer, $ext, $mime );
},
'initCallback' => static function ( $mimeAnalyzer ) use ( $hookRunner ) {
// Allow media handling extensions adding MIME-types and MIME-info
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$hookRunner->onMimeMagicInit( $mimeAnalyzer );
},
'logger' => $logger
];
if ( $params['infoFile'] === 'includes/mime.info' ) {
mime: Convert built-in MIME mappings to PHP arrays Currently, MimeAnalyzer builds the internal mappings of MIME types <=> file extensions by concatenating several string buffers in mime.type format into a giant string, and then parsing it. The mapping of MIME types to internal media types is built up in a similar way, except we use a dubious homegrown format with undocumented conventions. It's a mess, and an expensive one -- ~1.5% of api.php CPU time on the WMF cluster is spent building these buffers and parsing them. Converting the mappings to PHP associative arrays makes them much cheaper to load and easier to maintain. Doing this without breaking compatibility with existing behaviors requires some delicate footwork. The current mime.types buffer is made up of the following fragments, in order: 1) MimeAnalyzer::$wellKnownTypes 2) If $wgMimeTypeFile == 'includes/mime.types' (sic!): the contents of includes/libs/mime/mime.types. If $wgMimeTypeFile is another file path (e.g., '/etc/mime.types'): the contents of that file. If !wg$MimeTypeFile, this fragment is blank. 3) MimeAnalyzer::$extraTypes (populated by extensions via hook). The mime.info buffer is built up in the exact same way, except it's MimeAnalyzer::$wellKnownInfo, $wgMimeInfoFile, and MimeAnalyzer::$extraInfo. What this means in effect is that some built-in MediaWiki MIME mappings are "baked in" (anything in MimeAnalyzer::$wellKnown*), and others can be overridden (anything in includes/libs/mime/mime.*). To avoid breaking backward compatibility, we have to preserve the distinction. Thus this change has two MIME mappings, encapsulated in two classes: 'MimeMapMinimal', which contains just the baked-in mappings, and 'MimeMap' which contains both the baked-in and overridable mappings. We also have to keep the code for parsing mime.types and the ad-hoc mime.info format, at least for now. In a FUTURE change (i.e., not here), I think we can: * Deprecate $wgMimeTypeFile in favor of a new config var, $wgExtraMimeTypeFile. $wgMimeTypeFile is evil because if you are using to add support for additional MIME types, you can end up unwittingly dropping support for other types that exist in MediaWiki's mime.types but not your file. The new $wgExtraMimeTypeFile would only be used to add new MIME mappings on top of the standard MimeMappings, which was probably the original intent for $wgMimeTypeFile. * Deprecate $wgMimeInfoFile. I don't think we need to provide a replacement, because extensions can use the hook, and I doubt anyone is using the config var. But if we wanted to provide an alternative, we could have a $wgExtraMimeInfoMap that has an array of extra mappings. * Deprecate MimeAnalyzer::addExtraTypes and MimeAnalyzer::addExtraInfo, and provide alternative interfaces that take structured input instead of string blobs. I tested this by dumping the internal state of MimeAnalyzer before and after this CL using the script in Ib856a69fe, using both default and custom values for $wgMimeInfo(File|Type). Bug: T252228 Change-Id: I9b2979d3c9c0dee96bb19e0290f680724e718891
2020-05-12 19:42:35 +00:00
$params['infoFile'] = MimeAnalyzer::USE_INTERNAL;
}
if ( $params['typeFile'] === 'includes/mime.types' ) {
mime: Convert built-in MIME mappings to PHP arrays Currently, MimeAnalyzer builds the internal mappings of MIME types <=> file extensions by concatenating several string buffers in mime.type format into a giant string, and then parsing it. The mapping of MIME types to internal media types is built up in a similar way, except we use a dubious homegrown format with undocumented conventions. It's a mess, and an expensive one -- ~1.5% of api.php CPU time on the WMF cluster is spent building these buffers and parsing them. Converting the mappings to PHP associative arrays makes them much cheaper to load and easier to maintain. Doing this without breaking compatibility with existing behaviors requires some delicate footwork. The current mime.types buffer is made up of the following fragments, in order: 1) MimeAnalyzer::$wellKnownTypes 2) If $wgMimeTypeFile == 'includes/mime.types' (sic!): the contents of includes/libs/mime/mime.types. If $wgMimeTypeFile is another file path (e.g., '/etc/mime.types'): the contents of that file. If !wg$MimeTypeFile, this fragment is blank. 3) MimeAnalyzer::$extraTypes (populated by extensions via hook). The mime.info buffer is built up in the exact same way, except it's MimeAnalyzer::$wellKnownInfo, $wgMimeInfoFile, and MimeAnalyzer::$extraInfo. What this means in effect is that some built-in MediaWiki MIME mappings are "baked in" (anything in MimeAnalyzer::$wellKnown*), and others can be overridden (anything in includes/libs/mime/mime.*). To avoid breaking backward compatibility, we have to preserve the distinction. Thus this change has two MIME mappings, encapsulated in two classes: 'MimeMapMinimal', which contains just the baked-in mappings, and 'MimeMap' which contains both the baked-in and overridable mappings. We also have to keep the code for parsing mime.types and the ad-hoc mime.info format, at least for now. In a FUTURE change (i.e., not here), I think we can: * Deprecate $wgMimeTypeFile in favor of a new config var, $wgExtraMimeTypeFile. $wgMimeTypeFile is evil because if you are using to add support for additional MIME types, you can end up unwittingly dropping support for other types that exist in MediaWiki's mime.types but not your file. The new $wgExtraMimeTypeFile would only be used to add new MIME mappings on top of the standard MimeMappings, which was probably the original intent for $wgMimeTypeFile. * Deprecate $wgMimeInfoFile. I don't think we need to provide a replacement, because extensions can use the hook, and I doubt anyone is using the config var. But if we wanted to provide an alternative, we could have a $wgExtraMimeInfoMap that has an array of extra mappings. * Deprecate MimeAnalyzer::addExtraTypes and MimeAnalyzer::addExtraInfo, and provide alternative interfaces that take structured input instead of string blobs. I tested this by dumping the internal state of MimeAnalyzer before and after this CL using the script in Ib856a69fe, using both default and custom values for $wgMimeInfo(File|Type). Bug: T252228 Change-Id: I9b2979d3c9c0dee96bb19e0290f680724e718891
2020-05-12 19:42:35 +00:00
$params['typeFile'] = MimeAnalyzer::USE_INTERNAL;
}
$detectorCmd = $mainConfig->get( MainConfigNames::MimeDetectorCommand );
if ( $detectorCmd ) {
$factory = $services->getShellCommandFactory();
$params['detectCallback'] = static function ( $file ) use ( $detectorCmd, $factory ) {
$result = $factory->create()
// $wgMimeDetectorCommand can contain commands with parameters
->unsafeParams( $detectorCmd )
->params( $file )
->execute();
return $result->getStdout();
};
}
return new MimeAnalyzer( $params );
},
'MovePageFactory' => static function ( MediaWikiServices $services ): MovePageFactory {
return $services->getService( '_PageCommandFactory' );
},
'NamespaceInfo' => static function ( MediaWikiServices $services ): NamespaceInfo {
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
return new NamespaceInfo(
new ServiceOptions( NamespaceInfo::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getHookContainer(),
ExtensionRegistry::getInstance()->getAttribute( 'ExtensionNamespaces' ),
ExtensionRegistry::getInstance()->getAttribute( 'ImmovableNamespaces' )
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
);
},
'NameTableStoreFactory' => static function ( MediaWikiServices $services ): NameTableStoreFactory {
return new NameTableStoreFactory(
$services->getDBLoadBalancerFactory(),
$services->getMainWANObjectCache(),
LoggerFactory::getInstance( 'NameTableSqlStore' )
);
},
'ObjectCacheFactory' => static function ( MediaWikiServices $services ): ObjectCacheFactory {
return new ObjectCacheFactory(
new ServiceOptions(
ObjectCacheFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getStatsFactory(),
LoggerFactory::getProvider(),
WikiMap::getCurrentWikiDbDomain()->getId()
);
},
'ObjectFactory' => static function ( MediaWikiServices $services ): ObjectFactory {
return new ObjectFactory( $services );
},
'OldRevisionImporter' => static function ( MediaWikiServices $services ): OldRevisionImporter {
return new ImportableOldRevisionImporter(
true,
LoggerFactory::getInstance( 'OldRevisionImporter' ),
$services->getConnectionProvider(),
$services->getRevisionStoreFactory()->getRevisionStoreForImport(),
$services->getSlotRoleRegistry(),
$services->getWikiPageFactory(),
$services->getPageUpdaterFactory(),
$services->getUserFactory()
);
},
'PageEditStash' => static function ( MediaWikiServices $services ): PageEditStash {
return new PageEditStash(
ObjectCache::getLocalClusterInstance(),
$services->getConnectionProvider(),
LoggerFactory::getInstance( 'StashEdit' ),
$services->getStatsdDataFactory(),
$services->getUserEditTracker(),
$services->getUserFactory(),
$services->getWikiPageFactory(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getHookContainer(),
defined( 'MEDIAWIKI_JOB_RUNNER' ) || MW_ENTRY_POINT === 'cli'
? PageEditStash::INITIATOR_JOB_OR_CLI
: PageEditStash::INITIATOR_USER
);
},
'PageProps' => static function ( MediaWikiServices $services ): PageProps {
return new PageProps(
$services->getLinkBatchFactory(),
$services->getConnectionProvider()
);
},
'PageRestHelperFactory' => static function ( MediaWikiServices $services ): PageRestHelperFactory {
return new PageRestHelperFactory(
new ServiceOptions( PageRestHelperFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getRevisionLookup(),
$services->getTitleFormatter(),
$services->getPageStore(),
$services->getParsoidOutputStash(),
$services->getStatsdDataFactory(),
$services->getParsoidOutputAccess(),
$services->getHtmlTransformFactory(),
$services->getContentHandlerFactory(),
$services->getLanguageFactory(),
$services->getRedirectStore(),
$services->getLanguageConverterFactory()
);
},
'PageStore' => static function ( MediaWikiServices $services ): PageStore {
return $services->getPageStoreFactory()->getPageStore();
},
'PageStoreFactory' => static function ( MediaWikiServices $services ): PageStoreFactory {
$options = new ServiceOptions(
PageStoreFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
);
return new PageStoreFactory(
$options,
$services->getDBLoadBalancerFactory(),
$services->getNamespaceInfo(),
$services->getTitleParser(),
$services->getLinkCache(),
$services->getStatsdDataFactory()
);
},
'PageUpdaterFactory' => static function (
MediaWikiServices $services
): PageUpdaterFactory {
$editResultCache = new EditResultCache(
$services->getMainObjectStash(),
$services->getConnectionProvider(),
new ServiceOptions(
EditResultCache::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
)
);
return new PageUpdaterFactory(
$services->getRevisionStore(),
$services->getRevisionRenderer(),
$services->getSlotRoleRegistry(),
$services->getParserCache(),
$services->getJobQueueGroup(),
$services->getMessageCache(),
$services->getContentLanguage(),
$services->getDBLoadBalancerFactory(),
$services->getContentHandlerFactory(),
$services->getHookContainer(),
$editResultCache,
$services->getUserNameUtils(),
LoggerFactory::getInstance( 'SavePage' ),
new ServiceOptions(
PageUpdaterFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getUserEditTracker(),
$services->getUserGroupManager(),
$services->getTitleFormatter(),
$services->getContentTransformer(),
$services->getPageEditStash(),
$services->getTalkPageNotificationManager(),
$services->getMainWANObjectCache(),
$services->getPermissionManager(),
$services->getWikiPageFactory(),
$services->getChangeTagsStore()->getSoftwareTags()
);
},
'Parser' => static function ( MediaWikiServices $services ): Parser {
// This service exists as convenience function to get the global parser in global code.
// Do not use this service for dependency injection or in service wiring (T343070).
// Use the 'ParserFactory' service instead.
return $services->getParserFactory()->getMainInstance();
},
'ParserCache' => static function ( MediaWikiServices $services ): ParserCache {
return $services->getParserCacheFactory()
->getParserCache( ParserCacheFactory::DEFAULT_NAME );
},
'ParserCacheFactory' => static function ( MediaWikiServices $services ): ParserCacheFactory {
$config = $services->getMainConfig();
$cache = ObjectCache::getInstance( $config->get( MainConfigNames::ParserCacheType ) );
$wanCache = $services->getMainWANObjectCache();
$options = new ServiceOptions( ParserCacheFactory::CONSTRUCTOR_OPTIONS, $config );
return new ParserCacheFactory(
$cache,
$wanCache,
$services->getHookContainer(),
$services->getJsonCodec(),
$services->getStatsFactory(),
LoggerFactory::getInstance( 'ParserCache' ),
$options,
$services->getTitleFactory(),
Add ParserOutput::{get,set}RenderId() and set render id in ContentRenderer Set the render ID for each parse stored into cache so that we are able to identify a specific parse when there are dependencies (for example in an edit based on that parse). This is recorded as a property added to the ParserOutput, not the parent CacheTime interface. Even though the render ID is /related/ to the CacheTime interface, CacheTime is also used directly as a parser cache key, and the UUID should not be part of the lookup key. In general we are trying to move the location where these cache properties are set as early as possible, so we check at each location to ensure we don't overwrite a previously-set value. Eventually we can convert most of these checks into assertions that the cache properties have already been set (T350538). The primary location for setting cache properties is the ContentRenderer. Moved setting the revision timestamp into ContentRenderer as well, as it was set along the same code paths. An extra parameter was added to ContentRenderer::getParserOutput() to support this. Added merge code to ParserOutput::mergeInternalMetaDataFrom() which should ensure that cache time, revision, timestamp, and render id are all set properly when multiple slots are combined together in MCR. In order to ensure the render ID is set on all codepaths we needed to plumb the GlobalIdGenerator service into ContentRenderer, ParserCache, ParserCacheFactory, and RevisionOutputCache. Eventually (T350538) it should only be necessary in the ContentRenderer. Bug: T350538 Bug: T349868 Followup-To: Ic9b7cc0fcf365e772b7d080d76a065e3fd585f80 Change-Id: I72c5e6f86b7f081ab5ce7a56f5365d2f75067a78
2023-09-14 16:11:20 +00:00
$services->getWikiPageFactory(),
$services->getGlobalIdGenerator()
);
},
'ParserFactory' => static function ( MediaWikiServices $services ): ParserFactory {
$options = new ServiceOptions( Parser::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
);
return new ParserFactory(
$options,
$services->getMagicWordFactory(),
$services->getContentLanguage(),
$services->getUrlUtils(),
$services->getSpecialPageFactory(),
$services->getLinkRendererFactory(),
$services->getNamespaceInfo(),
LoggerFactory::getInstance( 'Parser' ),
$services->getBadFileLookup(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getLanguageConverterFactory(),
$services->getHookContainer(),
$services->getTidy(),
$services->getMainWANObjectCache(),
$services->getUserOptionsLookup(),
$services->getUserFactory(),
$services->getTitleFormatter(),
$services->getHttpRequestFactory(),
$services->getTrackingCategories(),
$services->getSignatureValidatorFactory(),
$services->getUserNameUtils()
);
},
'ParserOutputAccess' => static function ( MediaWikiServices $services ): ParserOutputAccess {
return new ParserOutputAccess(
$services->getParserCacheFactory(),
$services->getRevisionLookup(),
$services->getRevisionRenderer(),
$services->getStatsdDataFactory(),
$services->getDBLoadBalancerFactory(),
$services->getChronologyProtector(),
LoggerFactory::getProvider(),
$services->getWikiPageFactory(),
$services->getTitleFormatter()
);
},
'ParsoidDataAccess' => static function ( MediaWikiServices $services ): DataAccess {
$mainConfig = $services->getMainConfig();
return new MWDataAccess(
new ServiceOptions( MWDataAccess::CONSTRUCTOR_OPTIONS, $mainConfig ),
$services->getRepoGroup(),
$services->getBadFileLookup(),
$services->getHookContainer(),
$services->getContentTransformer(),
$services->getReadOnlyMode(),
$services->getParserFactory(), // *legacy* parser factory
$services->getLinkBatchFactory()
);
},
'ParsoidOutputAccess' => static function ( MediaWikiServices $services ): ParsoidOutputAccess {
return new ParsoidOutputAccess(
Make ParsoidOutputAccess a wrapper over ParserOutputAccess * Updated ParserOutput to set Parsoid render ids that REST API functionality expects in ParserOutput objects. * CacheThresholdTime functionality no longer exists since it was implemented in ParsoidOutputAccess and ParserOutputAccess doesn't support it. This is tracked in T346765. * Enforce the constraint that uncacheable parses are only for fake or mutable revisions. Updated tests that violated this constraint to use 'getParseOutput' instead of calling the parse method directly. * Had to make some changes in ParsoidParser around use of preferredVariant passed to Parsoid. I also left some TODO comments for future fixes. T267067 is also relevant here. PARSOID-SPECIFIC OPTIONS: * logLinterData: linter data is always logged by default -- removed support to disable it. Linter extension handles stale lints properly and it is better to let it handle it rather than add special cases to the API. * offsetType: Moved this support to ParsoidHandler as a post-processing of byte-offset output. This eliminates the need to support this Parsoid-specific options in the ContentHandler hierarchies. * body_only / wrapSections: Handled this in HtmlOutputRendererHelper as a post-processing of regular output by removing sections and returning the body content only. This does result in some useless section-wrapping work with Parsoid, but the simplification is probably worth it. If in the future, we support Parsoid-specific options in the ContentHandler hierarchy, we could re-introduce this. But, in any case, this "fragment" flavor options is likely to get moved out of core into the VisualEditor extension code. DEPLOYMENT: * This patch changes the cache key by setting the useParsoid option in ParserOptions. The parent patch handles this to ensure we don't encounter a cold cache on deploy. TESTS: * Updated tests and mocks to reflect new reality. * Do we need any new tests? Bug: T332931 Change-Id: Ic9b7cc0fcf365e772b7d080d76a065e3fd585f80
2023-08-29 20:13:43 +00:00
$services->getParsoidParserFactory(),
$services->getParserOutputAccess(),
$services->getPageStore(),
$services->getRevisionLookup(),
$services->getParsoidSiteConfig(),
$services->getContentHandlerFactory()
);
},
'ParsoidOutputStash' => static function ( MediaWikiServices $services ): ParsoidOutputStash {
// TODO: Determine storage requirements and config options for stashing parsoid
// output for VE edits (T309016).
$config = $services->getMainConfig()->get( MainConfigNames::ParsoidCacheConfig );
$backend = $config['StashType']
? ObjectCache::getInstance( $config['StashType'] )
: $services->getMainObjectStash();
return new SimpleParsoidOutputStash(
$services->getContentHandlerFactory(),
$backend,
$config['StashDuration']
);
},
'ParsoidPageConfigFactory' => static function ( MediaWikiServices $services ): MWPageConfigFactory {
Use Bcp47Code when interfacing with Parsoid It is very easy for developers and maintainers to mix up "internal MediaWiki language codes" and "BCP-47 language codes"; the latter are standards-compliant and used in web protocols like HTTP, HTML, and SVG; but much of WMF production is very dependent on historical codes used by MediaWiki which in some cases predate the IANA standardized name for the language in question. Phan and other static checking tools aren't much help distinguishing BCP-47 from internal codes when both are represented with the PHP string type, so the wikimedia/bcp-47-code package introduced a very lightweight wrapper type in order to uniquely identify BCP-47 codes. Language implements Bcp47Code, and LanguageFactory::getLanguage() is an easy way to convert (or downcast) between Bcp47Code and Language objects. This patch updates the Parsoid integration code and the associated REST handlers to use Bcp47Code in APIs so that the standalone Parsoid library does not need to know anything about MediaWiki-internal codes. The principle has been, first, to try to convert a string to a Bcp47Code as soon as possible and as close to the original input as possible, so it is easy to see *why* a given string is a BCP-47 code (usually, because it is coming from HTTP/HTML/etc) and we're not stuck deep inside some method trying to figure out where a string we're given is coming from and therefore what sort of string code it might be. Second, we've added explicit compatibility code to accept MediaWiki internal codes and convert them to Bcp47Code for backward compatibility with existing clients, using the @internal LanguageCode::normalizeNonstandardCodeAndWarn() method. The intention is to gradually remove these backward compatibility thunks and replace them with HTTP 400 errors or wfDeprecated messages in order to identify and repair callers who are incorrectly using non-standard-compliant language codes in web standards (HTTP/HTML/SVG/etc). Finally, maintaining a code as a Bcp47Code and not immediately converting to Language helps us delay or even avoid full loading of a Language object in some cases, which is another reason to occasionally push Bcp47Code (instead of Language) down the call stack. Bug: T327379 Depends-On: I830867d58f8962d6a57be16ce3735e8384f9ac1c Change-Id: I982e0df706a633b05dcc02b5220b737c19adc401
2022-11-04 17:29:23 +00:00
return new MWPageConfigFactory(
$services->getRevisionStore(),
$services->getSlotRoleRegistry(),
$services->getLanguageFactory()
);
},
Allow setting a ParserOption to generate Parsoid HTML This is an initial quick-and-dirty implementation. The ParsoidParser class will eventually inherit from \Parser, but this is an initial placeholder to unblock other Parsoid read views work. Currently Parsoid does not fully implement all the ParserOutput metadata set by the legacy parser, but we're working on it. This patch also addresses T300325 by ensuring the the Page HTML APIs use ParserOutput::getRawText(), which will return the entire Parsoid HTML document without post-processing. This is what the Parsoid team refers to as "edit mode" HTML. The ParserOutput::getText() method returns only the <body> contents of the HTML, and applies several transformations, including inserting Table of Contents and style deduplication; this is the "read views" flavor of the Parsoid HTML. We need to be careful of the interaction of the `useParsoid` flag with the ParserCacheMetadata. Effectively `useParsoid` should *always* be marked as "used" or else the ParserCache will assume its value doesn't matter and will serve legacy content for parsoid requests and vice-versa. T330677 is a follow up to address this more thoroughly by splitting the parser cache in ParserOutputAccess; the stop gap in this patch is fragile and, because it doesn't fork the ParserCacheMetadata cache, may corrupt the ParserCacheMetadata in the case when Parsoid and the legacy parser consult different sets of options to render a page. Bug: T300191 Bug: T330677 Bug: T300325 Change-Id: Ica09a4284c00d7917f8b6249e946232b2fb38011
2022-05-27 16:38:32 +00:00
'ParsoidParserFactory' => static function ( MediaWikiServices $services ): ParsoidParserFactory {
return new ParsoidParserFactory(
$services->getParsoidSiteConfig(),
$services->getParsoidDataAccess(),
$services->getParsoidPageConfigFactory(),
$services->getLanguageConverterFactory(),
Make ParsoidOutputAccess a wrapper over ParserOutputAccess * Updated ParserOutput to set Parsoid render ids that REST API functionality expects in ParserOutput objects. * CacheThresholdTime functionality no longer exists since it was implemented in ParsoidOutputAccess and ParserOutputAccess doesn't support it. This is tracked in T346765. * Enforce the constraint that uncacheable parses are only for fake or mutable revisions. Updated tests that violated this constraint to use 'getParseOutput' instead of calling the parse method directly. * Had to make some changes in ParsoidParser around use of preferredVariant passed to Parsoid. I also left some TODO comments for future fixes. T267067 is also relevant here. PARSOID-SPECIFIC OPTIONS: * logLinterData: linter data is always logged by default -- removed support to disable it. Linter extension handles stale lints properly and it is better to let it handle it rather than add special cases to the API. * offsetType: Moved this support to ParsoidHandler as a post-processing of byte-offset output. This eliminates the need to support this Parsoid-specific options in the ContentHandler hierarchies. * body_only / wrapSections: Handled this in HtmlOutputRendererHelper as a post-processing of regular output by removing sections and returning the body content only. This does result in some useless section-wrapping work with Parsoid, but the simplification is probably worth it. If in the future, we support Parsoid-specific options in the ContentHandler hierarchy, we could re-introduce this. But, in any case, this "fragment" flavor options is likely to get moved out of core into the VisualEditor extension code. DEPLOYMENT: * This patch changes the cache key by setting the useParsoid option in ParserOptions. The parent patch handles this to ensure we don't encounter a cold cache on deploy. TESTS: * Updated tests and mocks to reflect new reality. * Do we need any new tests? Bug: T332931 Change-Id: Ic9b7cc0fcf365e772b7d080d76a065e3fd585f80
2023-08-29 20:13:43 +00:00
$services->getParserFactory(),
$services->getGlobalIdGenerator()
Allow setting a ParserOption to generate Parsoid HTML This is an initial quick-and-dirty implementation. The ParsoidParser class will eventually inherit from \Parser, but this is an initial placeholder to unblock other Parsoid read views work. Currently Parsoid does not fully implement all the ParserOutput metadata set by the legacy parser, but we're working on it. This patch also addresses T300325 by ensuring the the Page HTML APIs use ParserOutput::getRawText(), which will return the entire Parsoid HTML document without post-processing. This is what the Parsoid team refers to as "edit mode" HTML. The ParserOutput::getText() method returns only the <body> contents of the HTML, and applies several transformations, including inserting Table of Contents and style deduplication; this is the "read views" flavor of the Parsoid HTML. We need to be careful of the interaction of the `useParsoid` flag with the ParserCacheMetadata. Effectively `useParsoid` should *always* be marked as "used" or else the ParserCache will assume its value doesn't matter and will serve legacy content for parsoid requests and vice-versa. T330677 is a follow up to address this more thoroughly by splitting the parser cache in ParserOutputAccess; the stop gap in this patch is fragile and, because it doesn't fork the ParserCacheMetadata cache, may corrupt the ParserCacheMetadata in the case when Parsoid and the legacy parser consult different sets of options to render a page. Bug: T300191 Bug: T330677 Bug: T300325 Change-Id: Ica09a4284c00d7917f8b6249e946232b2fb38011
2022-05-27 16:38:32 +00:00
);
},
'ParsoidSiteConfig' => static function ( MediaWikiServices $services ): SiteConfig {
$mainConfig = $services->getMainConfig();
$parsoidSettings = $mainConfig->get( MainConfigNames::ParsoidSettings );
return new MWSiteConfig(
new ServiceOptions( MWSiteConfig::CONSTRUCTOR_OPTIONS, $mainConfig ),
$parsoidSettings,
$services->getObjectFactory(),
$services->getContentLanguage(),
$services->getStatsdDataFactory(),
$services->getMagicWordFactory(),
$services->getNamespaceInfo(),
$services->getSpecialPageFactory(),
$services->getInterwikiLookup(),
$services->getUserOptionsLookup(),
$services->getLanguageFactory(),
$services->getLanguageConverterFactory(),
$services->getLanguageNameUtils(),
$services->getUrlUtils(),
ExtensionRegistry::getInstance()->getAttribute( 'ParsoidModules' ),
// These arguments are temporary and will be removed once
// better solutions are found.
$services->getParserFactory(), // T268776
$mainConfig, // T268777
ExtensionRegistry::getInstance()->isLoaded( 'TimedMediaHandler' )
);
},
'PasswordFactory' => static function ( MediaWikiServices $services ): PasswordFactory {
$config = $services->getMainConfig();
return new PasswordFactory(
$config->get( MainConfigNames::PasswordConfig ),
$config->get( MainConfigNames::PasswordDefault )
);
},
'PasswordReset' => static function ( MediaWikiServices $services ): PasswordReset {
$options = new ServiceOptions( PasswordReset::CONSTRUCTOR_OPTIONS, $services->getMainConfig() );
return new PasswordReset(
$options,
LoggerFactory::getInstance( 'authentication' ),
$services->getAuthManager(),
$services->getHookContainer(),
$services->getConnectionProvider(),
$services->getUserFactory(),
$services->getUserNameUtils(),
$services->getUserOptionsLookup()
);
},
'PerDbNameStatsdDataFactory' => static function ( MediaWikiServices $services ): StatsdDataFactoryInterface {
$config = $services->getMainConfig();
$wiki = $config->get( MainConfigNames::DBname );
return new PrefixingStatsdDataFactoryProxy(
$services->getStatsdDataFactory(),
$wiki
);
},
'PermissionManager' => static function ( MediaWikiServices $services ): PermissionManager {
return new PermissionManager(
new ServiceOptions(
PermissionManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
),
$services->getSpecialPageFactory(),
$services->getNamespaceInfo(),
$services->getGroupPermissionsLookup(),
$services->getUserGroupManager(),
$services->getBlockManager(),
$services->getFormatterFactory()->getBlockErrorFormatter(
new LazyLocalizationContext( static function () {
return RequestContext::getMain();
} )
),
$services->getHookContainer(),
$services->getUserCache(),
$services->getRedirectLookup(),
$services->getRestrictionStore(),
$services->getTitleFormatter(),
$services->getTempUserConfig(),
$services->getUserFactory(),
$services->getActionFactory()
);
},
'Pingback' => static function ( MediaWikiServices $services ): Pingback {
return new Pingback(
$services->getMainConfig(),
$services->getConnectionProvider(),
ObjectCache::getLocalClusterInstance(),
$services->getHttpRequestFactory(),
LoggerFactory::getInstance( 'Pingback' )
);
},
'PoolCounterFactory' => static function ( MediaWikiServices $services ): PoolCounterFactory {
$mainConfig = $services->getMainConfig();
return new PoolCounterFactory(
$mainConfig->get( MainConfigNames::PoolCounterConf ),
$mainConfig->get( MainConfigNames::PoolCountClientConf ),
LoggerFactory::getInstance( 'poolcounter' )
);
},
'PreferencesFactory' => static function ( MediaWikiServices $services ): PreferencesFactory {
$factory = new DefaultPreferencesFactory(
new ServiceOptions(
DefaultPreferencesFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getContentLanguage(),
$services->getAuthManager(),
$services->getLinkRendererFactory()->create(),
$services->getNamespaceInfo(),
$services->getPermissionManager(),
$services->getLanguageConverterFactory()->getLanguageConverter(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getLanguageNameUtils(),
$services->getHookContainer(),
$services->getUserOptionsManager(),
$services->getLanguageConverterFactory(),
$services->getParserFactory(),
$services->getSkinFactory(),
$services->getUserGroupManager(),
$services->getSignatureValidatorFactory()
);
$factory->setLogger( LoggerFactory::getInstance( 'preferences' ) );
return $factory;
},
'PreloadedContentBuilder' => static function ( MediaWikiServices $services ): PreloadedContentBuilder {
return new PreloadedContentBuilder(
$services->getContentHandlerFactory(),
$services->getWikiPageFactory(),
$services->getRedirectLookup(),
$services->getSpecialPageFactory(),
$services->getContentTransformer(),
$services->getHookContainer(),
);
},
'ProxyLookup' => static function ( MediaWikiServices $services ): ProxyLookup {
$mainConfig = $services->getMainConfig();
return new ProxyLookup(
$mainConfig->get( MainConfigNames::CdnServers ),
$mainConfig->get( MainConfigNames::CdnServersNoPurge ),
$services->getHookContainer()
);
},
'RateLimiter' => static function ( MediaWikiServices $services ): RateLimiter {
$rateLimiter = new RateLimiter(
new ServiceOptions( RateLimiter::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getWRStatsFactory(),
$services->getCentralIdLookupFactory()->getNonLocalLookup(),
$services->getUserFactory(),
$services->getUserGroupManager(),
$services->getHookContainer()
);
$rateLimiter->setStats( $services->getStatsdDataFactory() );
return $rateLimiter;
},
'ReadOnlyMode' => static function ( MediaWikiServices $services ): ReadOnlyMode {
return new ReadOnlyMode(
new ConfiguredReadOnlyMode(
$services->getMainConfig()->get( MainConfigNames::ReadOnly ),
$services->getMainConfig()->get( MainConfigNames::ReadOnlyFile )
),
$services->getDBLoadBalancerFactory()
);
},
'RedirectLookup' => static function ( MediaWikiServices $services ): RedirectLookup {
return $services->getRedirectStore();
},
'RedirectStore' => static function ( MediaWikiServices $services ): RedirectStore {
return new RedirectStore( $services->getWikiPageFactory() );
},
'RepoGroup' => static function ( MediaWikiServices $services ): RepoGroup {
$config = $services->getMainConfig();
return new RepoGroup(
$config->get( MainConfigNames::LocalFileRepo ),
$config->get( MainConfigNames::ForeignFileRepos ),
$services->getMainWANObjectCache(),
$services->getMimeAnalyzer()
);
},
'ResourceLoader' => static function ( MediaWikiServices $services ): ResourceLoader {
$config = $services->getMainConfig();
$maxage = $config->get( MainConfigNames::ResourceLoaderMaxage );
$rl = new ResourceLoader(
$config,
LoggerFactory::getInstance( 'resourceloader' ),
$config->get( MainConfigNames::ResourceLoaderUseObjectCacheForDeps )
? new KeyValueDependencyStore( $services->getMainObjectStash() )
: new SqlModuleDependencyStore( $services->getDBLoadBalancer() ),
[
'loadScript' => $config->get( MainConfigNames::LoadScript ),
'maxageVersioned' => $maxage['versioned'] ?? null,
'maxageUnversioned' => $maxage['unversioned'] ?? null,
]
);
$extRegistry = ExtensionRegistry::getInstance();
// Attribute has precedence over config
$modules = $extRegistry->getAttribute( 'ResourceModules' )
+ $config->get( MainConfigNames::ResourceModules );
$moduleSkinStyles = $extRegistry->getAttribute( 'ResourceModuleSkinStyles' )
+ $config->get( MainConfigNames::ResourceModuleSkinStyles );
$rl->setModuleSkinStyles( $moduleSkinStyles );
$rl->addSource( $config->get( MainConfigNames::ResourceLoaderSources ) );
// Core modules, then extension/skin modules
$rl->register( include MW_INSTALL_PATH . '/resources/Resources.php' );
$rl->register( $modules );
$hookRunner = new \MediaWiki\ResourceLoader\HookRunner( $services->getHookContainer() );
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$hookRunner->onResourceLoaderRegisterModules( $rl );
$msgPosterAttrib = $extRegistry->getAttribute( 'MessagePosterModule' );
$rl->register( 'mediawiki.messagePoster', [
'localBasePath' => MW_INSTALL_PATH,
'debugRaw' => false,
'scripts' => array_merge(
[
"resources/src/mediawiki.messagePoster/factory.js",
"resources/src/mediawiki.messagePoster/MessagePoster.js",
"resources/src/mediawiki.messagePoster/WikitextMessagePoster.js",
],
$msgPosterAttrib['scripts'] ?? []
),
'dependencies' => array_merge(
[
'oojs',
'mediawiki.api',
'mediawiki.ForeignApi',
],
$msgPosterAttrib['dependencies'] ?? []
),
] );
if ( $config->get( MainConfigNames::EnableJavaScriptTest ) === true ) {
$rl->registerTestModules();
}
return $rl;
},
'RestrictionStore' => static function ( MediaWikiServices $services ): RestrictionStore {
return new RestrictionStore(
new ServiceOptions(
RestrictionStore::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
),
$services->getMainWANObjectCache(),
$services->getDBLoadBalancer(),
$services->getLinkCache(),
$services->getLinksMigration(),
$services->getCommentStore(),
$services->getHookContainer(),
$services->getPageStore()
);
},
'RevertedTagUpdateManager' => static function ( MediaWikiServices $services ): RevertedTagUpdateManager {
Add mw-reverted change tag The tag is added to reverted edits as described in T254074. Functionality: * Adding the mw-reverted tag to reverted edits (duh) * Limiting the maximum depth of the update through a config variable (mitigation #2 from T259014). * Only applying the reverted tag after the edit has been somehow approved. Only the patrol subsystem currently implements this, but there's a hook that extensions can use (mitigation #4 from T259014, more explanation in T259103). * When performing the delayed update, it is checked whether the reverted edit was reverted itself. If so, the update is ignored. This is probably the only way to make the feature work due to the lack of an explicit "disapproval" mechanism other than reverting. * The update is also ignored if the revert is marked as deleted. Technical design: * The update code is in RevertedTagUpdate.php, which is a deferrable update, but is not used as such. It's separated to allow for better DI, testing and better code reusability in the future. * The update is queued / ran using the Job subsystem. The relevant job is in RevertedTagUpdateJob.php * PageUpdater determines whether the edit is approved or not and passes that to the DerivedPageDataUpdater. * The BeforeRevertedTagUpdate hook lets extensions decide whether the update should be ran right away or await approval. * DerivedPageDataUpdater checks whether the edit is a revert and if so either enqueues the job (if it's auto-approved) or caches the EditResult for later use (if it needs approval). * RevertedTagUpdateManager allows for easy re-enqueueing of the update for extensions. Thus, it has a very minimal interface. Other notes: * The unit testing setup for RevertedTagUpdate is a bit complicated, but it was the only way I could make this class testable while using the static ChangeTags class. Bug: T254074 Depends-On: I86d0e660f0acd51a7351396c5c82a400d3963b94 Change-Id: I70d5b29fec6b6058613f7ac2fb49f9fad9dc8da4
2020-07-06 11:47:22 +00:00
$editResultCache = new EditResultCache(
$services->getMainObjectStash(),
$services->getConnectionProvider(),
Add mw-reverted change tag The tag is added to reverted edits as described in T254074. Functionality: * Adding the mw-reverted tag to reverted edits (duh) * Limiting the maximum depth of the update through a config variable (mitigation #2 from T259014). * Only applying the reverted tag after the edit has been somehow approved. Only the patrol subsystem currently implements this, but there's a hook that extensions can use (mitigation #4 from T259014, more explanation in T259103). * When performing the delayed update, it is checked whether the reverted edit was reverted itself. If so, the update is ignored. This is probably the only way to make the feature work due to the lack of an explicit "disapproval" mechanism other than reverting. * The update is also ignored if the revert is marked as deleted. Technical design: * The update code is in RevertedTagUpdate.php, which is a deferrable update, but is not used as such. It's separated to allow for better DI, testing and better code reusability in the future. * The update is queued / ran using the Job subsystem. The relevant job is in RevertedTagUpdateJob.php * PageUpdater determines whether the edit is approved or not and passes that to the DerivedPageDataUpdater. * The BeforeRevertedTagUpdate hook lets extensions decide whether the update should be ran right away or await approval. * DerivedPageDataUpdater checks whether the edit is a revert and if so either enqueues the job (if it's auto-approved) or caches the EditResult for later use (if it needs approval). * RevertedTagUpdateManager allows for easy re-enqueueing of the update for extensions. Thus, it has a very minimal interface. Other notes: * The unit testing setup for RevertedTagUpdate is a bit complicated, but it was the only way I could make this class testable while using the static ChangeTags class. Bug: T254074 Depends-On: I86d0e660f0acd51a7351396c5c82a400d3963b94 Change-Id: I70d5b29fec6b6058613f7ac2fb49f9fad9dc8da4
2020-07-06 11:47:22 +00:00
new ServiceOptions(
EditResultCache::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
)
);
return new RevertedTagUpdateManager(
$editResultCache,
$services->getJobQueueGroup()
Add mw-reverted change tag The tag is added to reverted edits as described in T254074. Functionality: * Adding the mw-reverted tag to reverted edits (duh) * Limiting the maximum depth of the update through a config variable (mitigation #2 from T259014). * Only applying the reverted tag after the edit has been somehow approved. Only the patrol subsystem currently implements this, but there's a hook that extensions can use (mitigation #4 from T259014, more explanation in T259103). * When performing the delayed update, it is checked whether the reverted edit was reverted itself. If so, the update is ignored. This is probably the only way to make the feature work due to the lack of an explicit "disapproval" mechanism other than reverting. * The update is also ignored if the revert is marked as deleted. Technical design: * The update code is in RevertedTagUpdate.php, which is a deferrable update, but is not used as such. It's separated to allow for better DI, testing and better code reusability in the future. * The update is queued / ran using the Job subsystem. The relevant job is in RevertedTagUpdateJob.php * PageUpdater determines whether the edit is approved or not and passes that to the DerivedPageDataUpdater. * The BeforeRevertedTagUpdate hook lets extensions decide whether the update should be ran right away or await approval. * DerivedPageDataUpdater checks whether the edit is a revert and if so either enqueues the job (if it's auto-approved) or caches the EditResult for later use (if it needs approval). * RevertedTagUpdateManager allows for easy re-enqueueing of the update for extensions. Thus, it has a very minimal interface. Other notes: * The unit testing setup for RevertedTagUpdate is a bit complicated, but it was the only way I could make this class testable while using the static ChangeTags class. Bug: T254074 Depends-On: I86d0e660f0acd51a7351396c5c82a400d3963b94 Change-Id: I70d5b29fec6b6058613f7ac2fb49f9fad9dc8da4
2020-07-06 11:47:22 +00:00
);
},
'RevisionFactory' => static function ( MediaWikiServices $services ): RevisionFactory {
return $services->getRevisionStore();
},
'RevisionLookup' => static function ( MediaWikiServices $services ): RevisionLookup {
return $services->getRevisionStore();
},
'RevisionRenderer' => static function ( MediaWikiServices $services ): RevisionRenderer {
$renderer = new RevisionRenderer(
$services->getDBLoadBalancer(),
$services->getSlotRoleRegistry(),
$services->getContentRenderer()
);
$renderer->setLogger( LoggerFactory::getInstance( 'SaveParse' ) );
return $renderer;
},
'RevisionStore' => static function ( MediaWikiServices $services ): RevisionStore {
return $services->getRevisionStoreFactory()->getRevisionStore();
},
'RevisionStoreFactory' => static function ( MediaWikiServices $services ): RevisionStoreFactory {
return new RevisionStoreFactory(
$services->getDBLoadBalancerFactory(),
$services->getBlobStoreFactory(),
$services->getNameTableStoreFactory(),
$services->getSlotRoleRegistry(),
$services->getMainWANObjectCache(),
$services->getLocalServerObjectCache(),
$services->getCommentStore(),
$services->getActorStoreFactory(),
LoggerFactory::getInstance( 'RevisionStore' ),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getContentHandlerFactory(),
$services->getPageStoreFactory(),
$services->getTitleFactory(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getHookContainer()
);
},
'RollbackPageFactory' => static function ( MediaWikiServices $services ): RollbackPageFactory {
return $services->get( '_PageCommandFactory' );
},
Introduce CommentFormatter CommentParser: * Move comment formatting backend from Linker to a CommentParser service. Allow link existence and file existence to be batched. * Rename $local to $samePage since I think that is clearer. * Rename $title to $selfLinkTarget since it was unclear what the title was used for. * Rename the "autocomment" concept to "section link" in public interfaces, although the old term remains in CSS classes. * Keep unsafe HTML pass-through in separate "unsafe" methods, for easier static analysis and code review. CommentFormatter: * Add CommentFormatter and RowCommentFormatter services as a usable frontend for comment batches, and to replace the Linker static methods. * Provide fluent and parametric interfaces. Linker: * Remove Linker::makeCommentLink() without deprecation -- nothing calls it and it is obviously an internal helper. * Soft-deprecate Linker methods formatComment(), formatLinksInComment(), commentBlock() and revComment(). Caller migration: * CommentFormatter single: Linker, RollbackAction, ApiComparePages, ApiParse * CommentFormatter parametric batch: ImageHistoryPseudoPager * CommentFormatter fluent batch: ApiQueryFilearchive * RowCommentFormatter sequential: History feed, BlocklistPager, ProtectedPagesPager, ApiQueryProtectedTitles * RowCommentFormatter with index: ChangesFeed, ChangesList, ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges * RevisionCommentBatch: HistoryPager, ContribsPager Bug: T285917 Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
'RowCommentFormatter' => static function ( MediaWikiServices $services ): RowCommentFormatter {
return new RowCommentFormatter(
$services->getCommentParserFactory(),
Introduce CommentFormatter CommentParser: * Move comment formatting backend from Linker to a CommentParser service. Allow link existence and file existence to be batched. * Rename $local to $samePage since I think that is clearer. * Rename $title to $selfLinkTarget since it was unclear what the title was used for. * Rename the "autocomment" concept to "section link" in public interfaces, although the old term remains in CSS classes. * Keep unsafe HTML pass-through in separate "unsafe" methods, for easier static analysis and code review. CommentFormatter: * Add CommentFormatter and RowCommentFormatter services as a usable frontend for comment batches, and to replace the Linker static methods. * Provide fluent and parametric interfaces. Linker: * Remove Linker::makeCommentLink() without deprecation -- nothing calls it and it is obviously an internal helper. * Soft-deprecate Linker methods formatComment(), formatLinksInComment(), commentBlock() and revComment(). Caller migration: * CommentFormatter single: Linker, RollbackAction, ApiComparePages, ApiParse * CommentFormatter parametric batch: ImageHistoryPseudoPager * CommentFormatter fluent batch: ApiQueryFilearchive * RowCommentFormatter sequential: History feed, BlocklistPager, ProtectedPagesPager, ApiQueryProtectedTitles * RowCommentFormatter with index: ChangesFeed, ChangesList, ApiQueryDeletedrevs, ApiQueryLogEvents, ApiQueryRecentChanges * RevisionCommentBatch: HistoryPager, ContribsPager Bug: T285917 Change-Id: Ia3fd50a4a13138ba5003d884962da24746d562d0
2021-07-01 06:55:03 +00:00
$services->getCommentStore()
);
},
'SearchEngineConfig' => static function ( MediaWikiServices $services ): SearchEngineConfig {
// @todo This should not take a Config object, but it's not so easy to remove because it
// exposes it in a getter, which is actually used.
return new SearchEngineConfig(
$services->getMainConfig(),
$services->getContentLanguage(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getHookContainer(),
ExtensionRegistry::getInstance()->getAttribute( 'SearchMappings' ),
$services->getUserOptionsLookup()
);
},
'SearchEngineFactory' => static function ( MediaWikiServices $services ): SearchEngineFactory {
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
return new SearchEngineFactory(
$services->getSearchEngineConfig(),
$services->getHookContainer(),
$services->getConnectionProvider()
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
);
},
Introduce SearchResultThumbnailProvider & move hook + NS_FILE thumbs in What was previously a REST API-only feature (the thumbnails hook allowing for thumbnails for non-file pages via the PageImages extension) is now also being adopted in the main search page. That hook will now be called with NS_FILE result thumbnails pre-filled, which was not the case previously. PageImages essentially duplicated NS_FILE thumbnail logic that was already present in Special:Search, so that can (and will in a follow-up patch) then be removed there. Special:Search will then simply take whatever is produced from the provider (which will include both NS_FILE thumbs - which it handled already - as well as whatever else it receives from the hook), as will the REST API (which already received both) Since thumbnails can now come in for multiple namespaces & having some of those results with & others without a thumbnail can be quite jarring, it was decided that we'd display placeholder images (for certain namespaces). This is now controlled by $wgThumbnailNamespaces. I also split up a few things in FullSearchResultWidget:: generateFileHtml for more clarity. Meanwhile also updated mediawiki.special.search.styles.less to use variables for known colors. Also implemented a 'transform' (required for testing this change properly) and 'getDisplayWidthHeight' (it became needed after implementing transform) callback function for mock Files, and updated some existing tests in response to these changes. And some more Rest test files have been updated to allow passing around a HookContainer instead of only an array of hooks (from which a new HookContainer would then be created) to allow the same container to be used across all relevant objects, who may have it injected as dependency. Bug: T306883 Change-Id: I2a679b51758020d3e822da01a1bde1ae632b0b0a
2022-08-31 14:51:57 +00:00
'SearchResultThumbnailProvider' => static function ( MediaWikiServices $services ): SearchResultThumbnailProvider {
return new SearchResultThumbnailProvider(
$services->getRepoGroup(),
$services->getHookContainer()
);
},
'ShellboxClientFactory' => static function ( MediaWikiServices $services ): ShellboxClientFactory {
$urls = $services->getMainConfig()->get( MainConfigNames::ShellboxUrls );
return new ShellboxClientFactory(
$services->getHttpRequestFactory(),
$urls,
$services->getMainConfig()->get( MainConfigNames::ShellboxSecretKey )
);
},
'ShellCommandFactory' => static function ( MediaWikiServices $services ): CommandFactory {
$config = $services->getMainConfig();
$limits = [
'time' => $config->get( MainConfigNames::MaxShellTime ),
'walltime' => $config->get( MainConfigNames::MaxShellWallClockTime ),
'memory' => $config->get( MainConfigNames::MaxShellMemory ),
'filesize' => $config->get( MainConfigNames::MaxShellFileSize ),
];
$cgroup = $config->get( MainConfigNames::ShellCgroup );
$restrictionMethod = $config->get( MainConfigNames::ShellRestrictionMethod );
$factory = new CommandFactory( $services->getShellboxClientFactory(),
$limits, $cgroup, $restrictionMethod );
$factory->setLogger( LoggerFactory::getInstance( 'exec' ) );
$factory->logStderr();
return $factory;
},
'SignatureValidatorFactory' => static function ( MediaWikiServices $services ): SignatureValidatorFactory {
return new SignatureValidatorFactory(
new ServiceOptions(
SignatureValidator::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
// Use closures for these to avoid a circular dependency on Parser
static function () use ( $services ) {
return $services->getParserFactory();
},
static function () use ( $services ) {
return $services->get( '_Parsoid' );
},
$services->getParsoidPageConfigFactory(),
$services->getSpecialPageFactory(),
$services->getTitleFactory(),
$services->getExtensionRegistry()
);
},
'SiteLookup' => static function ( MediaWikiServices $services ): SiteLookup {
// Use SiteStore as the SiteLookup as well. This was originally separated
// to allow for a cacheable read-only interface, but this was never used.
// SiteStore has caching (see below).
return $services->getSiteStore();
},
'SiteStore' => static function ( MediaWikiServices $services ): SiteStore {
$rawSiteStore = new DBSiteStore( $services->getConnectionProvider() );
$cache = $services->getLocalServerObjectCache();
if ( $cache instanceof EmptyBagOStuff ) {
$cache = ObjectCache::getLocalClusterInstance();
}
return new CachingSiteStore( $rawSiteStore, $cache );
},
/** @suppress PhanTypeInvalidCallableArrayKey */
'SkinFactory' => static function ( MediaWikiServices $services ): SkinFactory {
$factory = new SkinFactory(
$services->getObjectFactory(),
(array)$services->getMainConfig()->get( MainConfigNames::SkipSkins )
);
$names = $services->getMainConfig()->get( MainConfigNames::ValidSkinNames );
foreach ( $names as $name => $skin ) {
if ( is_array( $skin ) ) {
$spec = $skin;
$displayName = $skin['displayname'] ?? $name;
$skippable = $skin['skippable'] ?? null;
} else {
$displayName = $skin;
$skippable = null;
$spec = [
'name' => $name,
'class' => "Skin$skin"
];
}
$factory->register( $name, $displayName, $spec, $skippable );
}
// Register a hidden "fallback" skin
$factory->register( 'fallback', 'Fallback', [
'class' => SkinFallback::class,
'args' => [
[
'name' => 'fallback',
'styles' => [ 'mediawiki.skinning.interface' ],
'templateDirectory' => __DIR__ . '/skins/templates/fallback',
]
]
], true );
// Register a hidden skin for api output
$factory->register( 'apioutput', 'ApiOutput', [
'class' => SkinApi::class,
'args' => [
[
'name' => 'apioutput',
'styles' => [ 'mediawiki.skinning.interface' ],
'templateDirectory' => __DIR__ . '/skins/templates/apioutput',
]
]
], true );
return $factory;
},
'SlotRoleRegistry' => static function ( MediaWikiServices $services ): SlotRoleRegistry {
$registry = new SlotRoleRegistry(
$services->getSlotRoleStore()
);
$config = $services->getMainConfig();
$contentHandlerFactory = $services->getContentHandlerFactory();
$hookContainer = $services->getHookContainer();
$titleFactory = $services->getTitleFactory();
$registry->defineRole(
SlotRecord::MAIN,
static function () use ( $config, $contentHandlerFactory, $hookContainer, $titleFactory ) {
return new MainSlotRoleHandler(
$config->get( MainConfigNames::NamespaceContentModels ),
$contentHandlerFactory,
$hookContainer,
$titleFactory
);
}
);
return $registry;
},
'SlotRoleStore' => static function ( MediaWikiServices $services ): NameTableStore {
return $services->getNameTableStoreFactory()->getSlotRoles();
},
'SpamChecker' => static function ( MediaWikiServices $services ): SpamChecker {
return new SpamChecker(
(array)$services->getMainConfig()->get( MainConfigNames::SpamRegex ),
(array)$services->getMainConfig()->get( MainConfigNames::SummarySpamRegex )
);
},
'SpecialPageFactory' => static function ( MediaWikiServices $services ): SpecialPageFactory {
return new SpecialPageFactory(
new ServiceOptions(
SpecialPageFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getContentLanguage(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getObjectFactory(),
$services->getTitleFactory(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getHookContainer()
);
},
'StatsdDataFactory' => static function ( MediaWikiServices $services ): IBufferingStatsdDataFactory {
return new BufferingStatsdDataFactory(
rtrim( $services->getMainConfig()->get( MainConfigNames::StatsdMetricPrefix ), '.' )
);
},
'StatsFactory' => static function ( MediaWikiServices $services ): StatsFactory {
$config = $services->getMainConfig();
$format = \Wikimedia\Stats\OutputFormats::getFormatFromString(
$config->get( MainConfigNames::StatsFormat ) ?? 'null'
);
$cache = new StatsCache;
$emitter = \Wikimedia\Stats\OutputFormats::getNewEmitter(
$config->get( MainConfigNames::StatsPrefix ) ?? 'MediaWiki',
$cache,
\Wikimedia\Stats\OutputFormats::getNewFormatter( $format ),
$config->get( MainConfigNames::StatsTarget )
);
$factory = new StatsFactory( $cache, $emitter, LoggerFactory::getInstance( 'Stats' ) );
return $factory->withStatsdDataFactory( $services->getStatsdDataFactory() );
},
'TalkPageNotificationManager' => static function (
MediaWikiServices $services
): TalkPageNotificationManager {
return new TalkPageNotificationManager(
new ServiceOptions(
TalkPageNotificationManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
),
$services->getConnectionProvider(),
$services->getReadOnlyMode(),
$services->getRevisionLookup(),
$services->getHookContainer(),
$services->getUserFactory()
);
},
'TempFSFileFactory' => static function ( MediaWikiServices $services ): TempFSFileFactory {
return new TempFSFileFactory( $services->getMainConfig()->get( MainConfigNames::TmpDirectory ) );
},
'TempUserConfig' => static function ( MediaWikiServices $services ): RealTempUserConfig {
return new RealTempUserConfig(
$services->getMainConfig()->get( MainConfigNames::AutoCreateTempUser )
);
},
'TempUserCreator' => static function ( MediaWikiServices $services ): TempUserCreator {
return new TempUserCreator(
$services->getTempUserConfig(),
$services->getObjectFactory(),
$services->getUserFactory(),
$services->getAuthManager(),
Handle collisions from SerialProvider::acquireIndex Why: * When using the TempUserCreator::create or ::acquireAndStashName to get temporary account when the chosen username already existed, that temporary account is treated as if it doesn't exist. This causes confusing "userexists" errors and also causes the user to be logged into an already existing temporary account. * Furthermore, because the user existence check in AuthManager::auto CreateUser only checks the local wiki, if an existing temporary account exists globally but not on the local wiki then the code sign a new user into an existing temporary account. * This is very bad behaviour, though shouldn't happen unless the serialMapping configuration uses a SerialMapping class that could provide duplicates and/or the configuration has been changed to use a different SerialMapping class. * There is a need to change the SerialMapping class in use to a different class, which means that the code will attempt to use temporary account usernames which already exist. * As such, the code that is generating the temporary account usernames based on the SerialMapping and SerialProvider should be aware that it may produce an already existing temporary account username, even if the SerialMapping class being used is asserted to never provide duplicates. * Therefore, the code that generates temporary account usernames should always attempt to verify that a automatically generated temporary account name does not already exist on the wiki. What: * Update TempUserCreator::acquireName to check to see if the username it generates already exists centrally using the CentralIdLookup. If it does, then the method returns null. Otherwise, the username that hasn't been used yet is returned. * Create the private method TempUserCreator::attemptAutoCreate that attempts an autocreate for a temporary account name, and optionally logs the account in. * Update TempUserCreator::create to use ::attemptAutoCreate to first to check if the account can be created and then again once the account is created to actually login to that temporary account. This is done to prevent logins to existing temporary accounts on the local wiki. The second call to actually perform the login is necessary as there is no other way to login to a temporary account. * Update TempUserCreator::acquireAndStashName to respond to the changes to ::acquireName, such that it returns null if ::acquireName returns null and also does not modify the session. * Update EditPage::maybeActivateTempUserCreate to return a Status and return a good status in all cases except when a temporary account name could not be acquired. * Add IEditObject::AS_UNABLE_TO_ACQUIRE_TEMP_ACCOUNT, and use it as the value of the fatal status returned by EditPage ::internalAttemptSave if a temporary account name could not be acquired. This will cause the display of a useful error to the user on edit. * Update ApiEditPage and ApiAcquireTempUserName to die with an error if a temporary account username was unable to be acquired. * Provide tests for the untested ApiAcquireTempUserName.php file including testing the new behaviour. * Add and update tests for TempUserCreator.php Bug: T353390 Change-Id: Id3a316ea0eba544d51d4ffcdfb03e35f4b3c54cc
2023-12-13 22:56:29 +00:00
$services->getCentralIdLookup(),
// This is supposed to match ThrottlePreAuthenticationProvider
new Throttler(
$services->getMainConfig()->get( MainConfigNames::TempAccountCreationThrottle ),
[
'type' => 'tempacctcreate',
'cache' => ObjectCache::getLocalClusterInstance(),
]
)
);
},
'Tidy' => static function ( MediaWikiServices $services ): TidyDriverBase {
return new RemexDriver(
new ServiceOptions(
RemexDriver::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
)
);
},
'TitleFactory' => static function ( MediaWikiServices $services ): TitleFactory {
return new TitleFactory();
},
'TitleFormatter' => static function ( MediaWikiServices $services ): TitleFormatter {
return $services->getService( '_MediaWikiTitleCodec' );
},
'TitleMatcher' => static function ( MediaWikiServices $services ): TitleMatcher {
return new TitleMatcher(
new ServiceOptions(
TitleMatcher::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getContentLanguage(),
$services->getLanguageConverterFactory(),
$services->getHookContainer(),
$services->getWikiPageFactory(),
$services->getUserNameUtils(),
$services->getRepoGroup(),
$services->getTitleFactory()
);
},
'TitleParser' => static function ( MediaWikiServices $services ): TitleParser {
return $services->getService( '_MediaWikiTitleCodec' );
},
'TrackingCategories' => static function ( MediaWikiServices $services ): TrackingCategories {
return new TrackingCategories(
new ServiceOptions(
TrackingCategories::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
$services->getNamespaceInfo(),
$services->getTitleParser(),
LoggerFactory::getInstance( 'TrackingCategories' )
);
},
'UnblockUserFactory' => static function ( MediaWikiServices $services ): UnblockUserFactory {
return $services->getService( '_UserBlockCommandFactory' );
},
'UndeletePageFactory' => static function ( MediaWikiServices $services ): UndeletePageFactory {
return $services->getService( '_PageCommandFactory' );
},
'UploadRevisionImporter' => static function ( MediaWikiServices $services ): UploadRevisionImporter {
return new ImportableUploadRevisionImporter(
$services->getMainConfig()->get( MainConfigNames::EnableUploads ),
LoggerFactory::getInstance( 'UploadRevisionImporter' )
);
},
'UrlUtils' => static function ( MediaWikiServices $services ): UrlUtils {
$config = $services->getMainConfig();
return new UrlUtils( [
UrlUtils::SERVER => $config->get( MainConfigNames::Server ),
UrlUtils::CANONICAL_SERVER => $config->get( MainConfigNames::CanonicalServer ),
UrlUtils::INTERNAL_SERVER => $config->get( MainConfigNames::InternalServer ),
UrlUtils::FALLBACK_PROTOCOL => RequestContext::getMain()->getRequest()->getProtocol(),
UrlUtils::HTTPS_PORT => $config->get( MainConfigNames::HttpsPort ),
UrlUtils::VALID_PROTOCOLS => $config->get( MainConfigNames::UrlProtocols ),
] );
},
'UserCache' => static function ( MediaWikiServices $services ): UserCache {
return new UserCache(
LoggerFactory::getInstance( 'UserCache' ),
$services->getConnectionProvider(),
$services->getLinkBatchFactory()
);
},
'UserEditTracker' => static function ( MediaWikiServices $services ): UserEditTracker {
return new UserEditTracker(
$services->getActorMigration(),
$services->getConnectionProvider(),
$services->getJobQueueGroup()
);
},
'UserFactory' => static function ( MediaWikiServices $services ): UserFactory {
return new UserFactory(
new ServiceOptions(
UserFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
),
$services->getDBLoadBalancerFactory(),
$services->getUserNameUtils()
);
},
'UserGroupManager' => static function ( MediaWikiServices $services ): UserGroupManager {
return $services->getUserGroupManagerFactory()->getUserGroupManager();
},
'UserGroupManagerFactory' => static function ( MediaWikiServices $services ): UserGroupManagerFactory {
return new UserGroupManagerFactory(
new ServiceOptions(
UserGroupManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
),
$services->getReadOnlyMode(),
$services->getDBLoadBalancerFactory(),
$services->getHookContainer(),
$services->getUserEditTracker(),
$services->getGroupPermissionsLookup(),
$services->getJobQueueGroupFactory(),
LoggerFactory::getInstance( 'UserGroupManager' ),
$services->getTempUserConfig(),
[ static function ( UserIdentity $user ) use ( $services ) {
if ( $user->getWikiId() === UserIdentity::LOCAL ) {
$services->getPermissionManager()->invalidateUsersRightsCache( $user );
}
$services->getUserFactory()->invalidateCache( $user );
} ]
);
},
'UserIdentityLookup' => static function ( MediaWikiServices $services ): UserIdentityLookup {
return $services->getActorStoreFactory()->getUserIdentityLookup();
},
'UserIdentityUtils' => static function ( MediaWikiServices $services ): UserIdentityUtils {
return new UserIdentityUtils(
$services->getTempUserConfig()
);
},
'UserNamePrefixSearch' => static function ( MediaWikiServices $services ): UserNamePrefixSearch {
return new UserNamePrefixSearch(
$services->getConnectionProvider(),
$services->getUserNameUtils(),
$services->getHideUserUtils()
);
},
'UserNameUtils' => static function ( MediaWikiServices $services ): UserNameUtils {
$messageFormatterFactory = new MessageFormatterFactory( Message::FORMAT_PLAIN );
return new UserNameUtils(
new ServiceOptions(
UserNameUtils::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
),
$services->getContentLanguage(),
LoggerFactory::getInstance( 'UserNameUtils' ),
$services->getTitleParser(),
$messageFormatterFactory->getTextFormatter(
$services->getContentLanguage()->getCode()
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
),
$services->getHookContainer(),
$services->getTempUserConfig()
);
},
'UserOptionsLookup' => static function ( MediaWikiServices $services ): UserOptionsLookup {
return $services->getUserOptionsManager();
},
'UserOptionsManager' => static function ( MediaWikiServices $services ): UserOptionsManager {
return new UserOptionsManager(
new ServiceOptions( UserOptionsManager::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->get( '_DefaultOptionsLookup' ),
$services->getLanguageConverterFactory(),
$services->getConnectionProvider(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
LoggerFactory::getInstance( 'UserOptionsManager' ),
$services->getHookContainer(),
$services->getUserFactory(),
$services->getUserNameUtils()
);
},
'UserRegistrationLookup' => static function ( MediaWikiServices $services ): UserRegistrationLookup {
$lookup = new UserRegistrationLookup(
new ServiceOptions( UserRegistrationLookup::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getObjectFactory()
);
if ( !$lookup->isRegistered( LocalUserRegistrationProvider::TYPE ) ) {
throw new ConfigException( 'UserRegistrationLookup: Local provider is required' );
}
return $lookup;
},
'WatchedItemQueryService' => static function ( MediaWikiServices $services ): WatchedItemQueryService {
return new WatchedItemQueryService(
$services->getConnectionProvider(),
$services->getCommentStore(),
$services->getWatchedItemStore(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getHookContainer(),
$services->getUserOptionsLookup(),
$services->getTempUserConfig(),
$services->getMainConfig()->get( MainConfigNames::WatchlistExpiry ),
$services->getMainConfig()->get( MainConfigNames::MaxExecutionTimeForExpensiveQueries )
);
},
'WatchedItemStore' => static function ( MediaWikiServices $services ): WatchedItemStore {
$store = new WatchedItemStore(
new ServiceOptions( WatchedItemStore::CONSTRUCTOR_OPTIONS,
$services->getMainConfig() ),
$services->getDBLoadBalancerFactory(),
$services->getJobQueueGroup(),
$services->getMainObjectStash(),
new HashBagOStuff( [ 'maxKeys' => 100 ] ),
$services->getReadOnlyMode(),
$services->getNamespaceInfo(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getRevisionLookup(),
$services->getLinkBatchFactory()
);
$store->setStatsdDataFactory( $services->getStatsdDataFactory() );
if ( $services->getMainConfig()->get( MainConfigNames::ReadOnlyWatchedItemStore ) ) {
$store = new NoWriteWatchedItemStore( $store );
}
return $store;
},
'WatchlistManager' => static function ( MediaWikiServices $services ): WatchlistManager {
return new WatchlistManager(
[
WatchlistManager::OPTION_ENOTIF =>
RecentChange::isEnotifEnabled( $services->getMainConfig() ),
],
$services->getHookContainer(),
$services->getReadOnlyMode(),
$services->getRevisionLookup(),
$services->getTalkPageNotificationManager(),
$services->getWatchedItemStore(),
$services->getUserFactory(),
$services->getNamespaceInfo(),
$services->getWikiPageFactory()
);
},
'WikiExporterFactory' => static function ( MediaWikiServices $services ): WikiExporterFactory {
return new WikiExporterFactory(
$services->getHookContainer(),
$services->getRevisionStore(),
$services->getTitleParser(),
$services->getCommentStore()
);
},
'WikiImporterFactory' => static function ( MediaWikiServices $services ): WikiImporterFactory {
return new WikiImporterFactory(
$services->getMainConfig(),
$services->getHookContainer(),
$services->getContentLanguage(),
$services->getNamespaceInfo(),
$services->getTitleFactory(),
$services->getWikiPageFactory(),
$services->getWikiRevisionUploadImporter(),
$services->getContentHandlerFactory(),
$services->getSlotRoleRegistry()
);
},
'WikiPageFactory' => static function ( MediaWikiServices $services ): WikiPageFactory {
return new WikiPageFactory(
$services->getTitleFactory(),
new HookRunner( $services->getHookContainer() ),
$services->getDBLoadBalancerFactory()
);
},
'WikiRevisionOldRevisionImporterNoUpdates' => static function (
MediaWikiServices $services
): ImportableOldRevisionImporter {
return new ImportableOldRevisionImporter(
false,
LoggerFactory::getInstance( 'OldRevisionImporter' ),
$services->getConnectionProvider(),
$services->getRevisionStoreFactory()->getRevisionStoreForImport(),
$services->getSlotRoleRegistry(),
$services->getWikiPageFactory(),
$services->getPageUpdaterFactory(),
$services->getUserFactory()
);
},
'WRStatsFactory' => static function ( MediaWikiServices $services ): WRStatsFactory {
return new WRStatsFactory(
new BagOStuffStatsStore( $services->getMicroStash() )
);
},
'_ConditionalDefaultsLookup' => static function (
MediaWikiServices $services
): ConditionalDefaultsLookup {
return new ConditionalDefaultsLookup(
new ServiceOptions(
ConditionalDefaultsLookup::CONSTRUCTOR_OPTIONS, $services->getMainConfig()
),
$services->getUserRegistrationLookup()
);
},
'_DefaultOptionsLookup' => static function ( MediaWikiServices $services ): DefaultOptionsLookup {
return new DefaultOptionsLookup(
new ServiceOptions( DefaultOptionsLookup::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getContentLanguage(),
$services->getHookContainer(),
$services->getNamespaceInfo(),
$services->get( '_ConditionalDefaultsLookup' )
);
},
'_EditConstraintFactory' => static function ( MediaWikiServices $services ): EditConstraintFactory {
// This service is internal and currently only exists because a significant number
// of dependencies will be needed by different constraints. It is not part of
// the public interface and has no corresponding method in MediaWikiServices
return new EditConstraintFactory(
// Multiple
new ServiceOptions(
EditConstraintFactory::CONSTRUCTOR_OPTIONS,
$services->getMainConfig()
),
LoggerFactory::getProvider(),
// UserBlockConstraint
$services->getPermissionManager(),
// EditFilterMergedContentHookConstraint
$services->getHookContainer(),
// ReadOnlyConstraint
$services->getReadOnlyMode(),
// SpamRegexConstraint
$services->getSpamChecker(),
// UserRateLimitConstraint
$services->getRateLimiter()
);
},
'_LocalClusterCache' => static function ( MediaWikiServices $services ): BagOStuff {
$mainConfig = $services->getMainConfig();
$id = $mainConfig->get( MainConfigNames::MainCacheType );
return $services->getObjectCacheFactory()->getInstance( $id );
},
'_MediaWikiTitleCodec' => static function ( MediaWikiServices $services ): MediaWikiTitleCodec {
return new MediaWikiTitleCodec(
$services->getContentLanguage(),
$services->getGenderCache(),
$services->getMainConfig()->get( MainConfigNames::LocalInterwikis ),
$services->getInterwikiLookup(),
$services->getNamespaceInfo()
);
},
'_PageCommandFactory' => static function ( MediaWikiServices $services ): PageCommandFactory {
return new PageCommandFactory(
$services->getMainConfig(),
$services->getDBLoadBalancerFactory(),
$services->getNamespaceInfo(),
$services->getWatchedItemStore(),
$services->getRepoGroup(),
$services->getReadOnlyMode(),
$services->getContentHandlerFactory(),
$services->getRevisionStore(),
Hooks::run() call site migration Migrate all callers of Hooks::run() to use the new HookContainer/HookRunner system. General principles: * Use DI if it is already used. We're not changing the way state is managed in this patch. * HookContainer is always injected, not HookRunner. HookContainer is a service, it's a more generic interface, it is the only thing that provides isRegistered() which is needed in some cases, and a HookRunner can be efficiently constructed from it (confirmed by benchmark). Because HookContainer is needed for object construction, it is also needed by all factories. * "Ask your friendly local base class". Big hierarchies like SpecialPage and ApiBase have getHookContainer() and getHookRunner() methods in the base class, and classes that extend that base class are not expected to know or care where the base class gets its HookContainer from. * ProtectedHookAccessorTrait provides protected getHookContainer() and getHookRunner() methods, getting them from the global service container. The point of this is to ease migration to DI by ensuring that call sites ask their local friendly base class rather than getting a HookRunner from the service container directly. * Private $this->hookRunner. In some smaller classes where accessor methods did not seem warranted, there is a private HookRunner property which is accessed directly. Very rarely (two cases), there is a protected property, for consistency with code that conventionally assumes protected=private, but in cases where the class might actually be overridden, a protected accessor is preferred over a protected property. * The last resort: Hooks::runner(). Mostly for static, file-scope and global code. In a few cases it was used for objects with broken construction schemes, out of horror or laziness. Constructors with new required arguments: * AuthManager * BadFileLookup * BlockManager * ClassicInterwikiLookup * ContentHandlerFactory * ContentSecurityPolicy * DefaultOptionsManager * DerivedPageDataUpdater * FullSearchResultWidget * HtmlCacheUpdater * LanguageFactory * LanguageNameUtils * LinkRenderer * LinkRendererFactory * LocalisationCache * MagicWordFactory * MessageCache * NamespaceInfo * PageEditStash * PageHandlerFactory * PageUpdater * ParserFactory * PermissionManager * RevisionStore * RevisionStoreFactory * SearchEngineConfig * SearchEngineFactory * SearchFormWidget * SearchNearMatcher * SessionBackend * SpecialPageFactory * UserNameUtils * UserOptionsManager * WatchedItemQueryService * WatchedItemStore Constructors with new optional arguments: * DefaultPreferencesFactory * Language * LinkHolderArray * MovePage * Parser * ParserCache * PasswordReset * Router setHookContainer() now required after construction: * AuthenticationProvider * ResourceLoaderModule * SearchEngine Change-Id: Id442b0dbe43aba84bd5cf801d86dedc768b082c7
2020-03-19 02:42:09 +00:00
$services->getSpamChecker(),
$services->getTitleFormatter(),
$services->getHookContainer(),
$services->getWikiPageFactory(),
$services->getUserFactory(),
$services->getActorMigration(),
$services->getActorNormalization(),
$services->getTitleFactory(),
$services->getUserEditTracker(),
$services->getCollationFactory(),
$services->getJobQueueGroup(),
$services->getCommentStore(),
$services->getMainObjectStash(),
WikiMap::getCurrentWikiDbDomain()->getId(),
WebRequest::getRequestId(),
$services->getBacklinkCacheFactory(),
LoggerFactory::getInstance( 'UndeletePage' ),
DeletePage: add option to delete the associated talk page Currently this is implemented internally as a second method call. In the future, it might be possible to optimize the implementation, e.g. to reduce the amount of DB queries/jobs etc. without changing anything for callers. Since the implementation of e.g. the result getters is generic, a future commit may add an option to delete subpages with relatively low effort. The revision limit for big deletions is now using the total number of revisions (base page + talk page). This is because both deletions would happen in the same main transaction, and we presumably want to optimize the deletion as a whole, not as two separated steps. This would become even more important if the aforementioned improvements are ever implemented. Note, the whole concept of "big deletion" might have been superseded by batched deletions in the author's opinion, but as long as such a limit exists, it should serve its purpose. The current interpretation of the associated talk option is that the request is validated, and an exception is raised if we cannot delete the associated talk, as opposed to silently ignoring the parameter if the talk cannot be deleted. The only exception to this is that it will not fail hard if the talk page turns out not to exist by the time the deletion is attempted, as that's hard to foresee due to race conditions. Note that the summary for the talk deletion is prefixed with the delete-talk-summary-prefix message. The main reason behind this is that the delete UI can sometimes offer an auto-generated summary like "the only contributor was XXX" or "the content was YYY", and since this may not apply to the talk page as well, the log entry might look confusing. Bug: T263209 Bug: T27471 Change-Id: Ife1f4e8258a69528dd1ce8fef8ae809761aa6f1d
2021-09-01 12:56:43 +00:00
$services->getPageUpdaterFactory(),
$services->getMessageFormatterFactory()->getTextFormatter(
$services->getContentLanguage()->getCode()
),
$services->getArchivedRevisionLookup(),
$services->getRestrictionStore(),
$services->getLinkTargetLookup()
);
},
'_ParserObserver' => static function ( MediaWikiServices $services ): ParserObserver {
return new ParserObserver( LoggerFactory::getInstance( 'DuplicateParse' ) );
},
'_Parsoid' => static function ( MediaWikiServices $services ): Parsoid {
return new Parsoid(
$services->getParsoidSiteConfig(),
$services->getParsoidDataAccess()
);
},
'_SettingsBuilder' => static function ( MediaWikiServices $services ): SettingsBuilder {
return SettingsBuilder::getInstance();
},
'_SqlBlobStore' => static function ( MediaWikiServices $services ): SqlBlobStore {
return $services->getBlobStoreFactory()->newSqlBlobStore();
},
'_UserBlockCommandFactory' => static function ( MediaWikiServices $services ): UserBlockCommandFactory {
return new UserBlockCommandFactory(
new ServiceOptions( UserBlockCommandFactory::CONSTRUCTOR_OPTIONS, $services->getMainConfig() ),
$services->getHookContainer(),
$services->getBlockPermissionCheckerFactory(),
$services->getBlockUtils(),
$services->getDatabaseBlockStore(),
$services->getBlockRestrictionStore(),
$services->getUserFactory(),
$services->getUserEditTracker(),
LoggerFactory::getInstance( 'BlockManager' ),
$services->getTitleFactory(),
$services->getBlockActionInfo()
);
},
///////////////////////////////////////////////////////////////////////////
// NOTE: When adding a service here, don't forget to add a getter function
// in the MediaWikiServices class. The convenience getter should just call
// $this->getService( 'FooBarService' ).
///////////////////////////////////////////////////////////////////////////
];