From a0da4154fa401e35a996501e7c03047a08dffa23 Mon Sep 17 00:00:00 2001 From: Edward Chernenko Date: Mon, 16 Dec 2024 03:21:26 +0300 Subject: [PATCH] (refactor) Turn EventCalendar class into a service --- .phpcs.xml | 2 -- extension.json | 13 ++++++- includes/EventCalendar.php | 69 ++++++++++++++++++++++++++++---------- includes/Hooks.php | 19 ++++++++--- includes/ServiceWiring.php | 46 +++++++++++++++++++++++++ 5 files changed, 125 insertions(+), 24 deletions(-) create mode 100644 includes/ServiceWiring.php diff --git a/.phpcs.xml b/.phpcs.xml index 2f78b2f..30f8f1b 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -5,8 +5,6 @@ - - diff --git a/extension.json b/extension.json index 2d58740..e22706a 100644 --- a/extension.json +++ b/extension.json @@ -53,8 +53,16 @@ "MediaWiki\\JsCalendar\\HtmlSanitizer": "includes/HtmlSanitizer.php", "MediaWiki\\JsCalendar\\Hooks": "includes/Hooks.php" }, + "HookHandlers": { + "main": { + "class": "MediaWiki\\JsCalendar\\Hooks", + "services": [ + "JsCalendar.EventCalendar" + ] + } + }, "Hooks": { - "ParserFirstCallInit": "MediaWiki\\JsCalendar\\Hooks::onParserFirstCallInit" + "ParserFirstCallInit": "main" }, "config": { "ECMaxCacheTime": { @@ -66,5 +74,8 @@ "description": "Version of FullCalendar library to use (this library is responsible for how the calendar is displayed). The only supported values are 2 (default) and 5 (experimental)." } }, + "ServiceWiringFiles": [ + "includes/ServiceWiring.php" + ], "manifest_version": 2 } diff --git a/includes/EventCalendar.php b/includes/EventCalendar.php index 8097eef..cd073ba 100644 --- a/includes/EventCalendar.php +++ b/includes/EventCalendar.php @@ -26,19 +26,56 @@ use DateTime; use FormatJson; use Html; -use MediaWiki\MediaWikiServices; +use Language; +use MediaWiki\Config\ServiceOptions; use MWException; use ObjectCache; use Parser; +use ReadOnlyMode; use Title; +use Wikimedia\Rdbms\ILoadBalancer; class EventCalendar { + /** @var ServiceOptions */ + protected $options; + + /** @var ILoadBalancer */ + protected $loadBalancer; + + /** @var Language */ + protected $contentLanguage; + + /** @var ReadOnlyMode */ + protected $readOnlyMode; + + public const CONSTRUCTOR_OPTIONS = [ + 'ECMaxCacheTime', + 'JsCalendarFullCalendarVersion' + ]; + + /** + * @param ServiceOptions $options + * @param ILoadBalancer $loadBalancer + * @param Language $contentLanguage + * @param ReadOnlyMode $readOnlyMode + */ + public function __construct( + ServiceOptions $options, + ILoadBalancer $loadBalancer, + Language $contentLanguage, + ReadOnlyMode $readOnlyMode + ) { + $this->options = $options; + $this->loadBalancer = $loadBalancer; + $this->contentLanguage = $contentLanguage; + $this->readOnlyMode = $readOnlyMode; + } /** * @var int * For multiple calendars on the same page: they will be named Calendar1, Calendar2, etc. */ - protected static $calendarsCounter = 0; + protected $calendarsCounter = 0; /** * Calculate an array of events in the format expected by JavaScript side of the calendar. @@ -46,7 +83,7 @@ class EventCalendar { * @param Parser $recursiveParser Parser object that is being used to render . * @return array */ - public static function findEvents( array $opt, Parser $recursiveParser ) { + public function findEvents( array $opt, Parser $recursiveParser ) { $namespaceIdx = $opt['namespace'] ?? NS_MAIN; $prefix = $opt['prefix'] ?? ''; $suffix = $opt['suffix'] ?? ''; @@ -103,7 +140,7 @@ public static function findEvents( array $opt, Parser $recursiveParser ) { $res = $query->getResult(); // Obtain connections to DB/cache that may or may not be used for storing snippets in cache. - $dbw = MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_PRIMARY ); + $dbw = $this->loadBalancer->getConnection( DB_PRIMARY ); $dbCache = ObjectCache::getInstance( CACHE_DB ); $eventmap = []; @@ -207,7 +244,7 @@ public static function findEvents( array $opt, Parser $recursiveParser ) { // NOTE: the reason why we don't use $dbCache->set() here is that SqlBagOStuff::set() will do // both compression and serialization, and decoding this is in protected methods. // We don't want all that code duplication here, so we store HTML snippet in raw form. - if ( !MediaWikiServices::getInstance()->getReadOnlyMode()->isReadOnly() ) { + if ( !$this->readOnlyMode->isReadOnly() ) { $dbw->insert( 'objectcache', [ 'keyname' => $dbCache->makeKey( 'jscalendar-snippet-' . $row->latest ), 'value' => $snippet, @@ -307,12 +344,9 @@ public static function findEvents( array $opt, Parser $recursiveParser ) { * @param Parser $parser * @return array|string */ - public static function renderEventCalendar( $input, Parser $parser ) { - // config variables - global $wgECMaxCacheTime, $wgJsCalendarFullCalendarVersion; - + public function renderCalendar( $input, Parser $parser ) { $modules = []; - switch ( $wgJsCalendarFullCalendarVersion ) { + switch ( intval( $this->options->get( 'JsCalendarFullCalendarVersion' ) ) ) { case 2: $modules[] = 'ext.yasec'; break; @@ -322,14 +356,15 @@ public static function renderEventCalendar( $input, Parser $parser ) { break; default: - throw new MWException( 'Unsupported value of $wgJsCalendarFullCalendarVersion (' . - $wgJsCalendarFullCalendarVersion . '): can only be 2 or 5.' ); + throw new MWException( + 'Unsupported value of $wgJsCalendarFullCalendarVersion: can only be 2 or 5.' ); } $parser->getOutput()->addModules( $modules ); - if ( $wgECMaxCacheTime !== false ) { - $parser->getOutput()->updateCacheExpiry( $wgECMaxCacheTime ); + $cacheTime = $this->options->get( 'ECMaxCacheTime' ); + if ( $cacheTime !== false ) { + $parser->getOutput()->updateCacheExpiry( $cacheTime ); } // Parse the contents of the tag ($input string) for parameters. @@ -345,13 +380,13 @@ public static function renderEventCalendar( $input, Parser $parser ) { $val = trim( $keyval[1] ); if ( $key == 'namespace' ) { - $val = MediaWikiServices::getInstance()->getContentLanguage()->getNsIndex( $val ); + $val = $this->contentLanguage->getNsIndex( $val ); } $options[$key] = $val; } - $events = self::findEvents( array_filter( $options ), $parser ); + $events = $this->findEvents( array_filter( $options ), $parser ); // calendar container and data array $scriptHtml = ''; @@ -360,7 +395,7 @@ public static function renderEventCalendar( $input, Parser $parser ) { $attr = [ 'class' => 'eventcalendar', - 'id' => 'eventcalendar-' . ( ++ self::$calendarsCounter ) + 'id' => 'eventcalendar-' . ( ++$this->calendarsCounter ) ]; $height = $options['height'] ?? 0; diff --git a/includes/Hooks.php b/includes/Hooks.php index 8fd268e..3a0be58 100644 --- a/includes/Hooks.php +++ b/includes/Hooks.php @@ -26,14 +26,25 @@ use Parser; class Hooks { + + /** @var EventCalendar */ + protected $eventCalendar; + + /** + * @param EventCalendar $eventCalendar + */ + public function __construct( EventCalendar $eventCalendar ) { + $this->eventCalendar = $eventCalendar; + } + /** * Set up the tag. * * @param Parser $parser * @return true */ - public static function onParserFirstCallInit( Parser $parser ) { - $parser->setHook( 'EventCalendar', '\MediaWiki\JsCalendar\Hooks::parserFunction' ); + public function onParserFirstCallInit( Parser $parser ) { + $parser->setHook( 'EventCalendar', [ $this, 'parserFunction' ] ); return true; } @@ -44,7 +55,7 @@ public static function onParserFirstCallInit( Parser $parser ) { * @param Parser $parser * @return array|string */ - public static function parserFunction( $input, $args, Parser $parser ) { - return EventCalendar::renderEventCalendar( $input, $parser ); + public function parserFunction( $input, $args, Parser $parser ) { + return $this->eventCalendar->renderCalendar( $input, $parser ); } } diff --git a/includes/ServiceWiring.php b/includes/ServiceWiring.php new file mode 100644 index 0000000..8a53339 --- /dev/null +++ b/includes/ServiceWiring.php @@ -0,0 +1,46 @@ + + static function ( MediaWikiServices $services ): EventCalendar { + return new EventCalendar( + new ServiceOptions( + EventCalendar::CONSTRUCTOR_OPTIONS, + $services->getMainConfig() + ), + $services->getDBLoadBalancer(), + $services->getContentLanguage(), + $services->getReadOnlyMode() + ); + } +]; + +// @codeCoverageIgnoreEnd