diff --git a/.phan/config.php b/.phan/config.php index 0d43c78..e4bd302 100644 --- a/.phan/config.php +++ b/.phan/config.php @@ -17,7 +17,6 @@ $cfg['suppress_issue_types'] = [ 'PhanAccessMethodInternal', 'SecurityCheck-LikelyFalsePositive', - 'UnusedPluginSuppression' ]; return $cfg; diff --git a/.phpcs.xml b/.phpcs.xml index 304518b..ae2956a 100644 --- a/.phpcs.xml +++ b/.phpcs.xml @@ -2,7 +2,24 @@ . - - - + + 5 + + + 5 + + + 5 + + + 5 + + + + + + + + + diff --git a/extension.json b/extension.json index b226dc0..ccc1a38 100644 --- a/extension.json +++ b/extension.json @@ -30,14 +30,13 @@ "Miraheze\\RottenLinks\\": "includes/" }, "JobClasses": { - "RottenLinksJob": "Miraheze\\RottenLinks\\RottenLinksJob" + "RottenLinksJob": "Miraheze\\RottenLinks\\Jobs\\RottenLinksJob" }, "SpecialPages": { "RottenLinks": { - "class": "Miraheze\\RottenLinks\\SpecialRottenLinks", + "class": "Miraheze\\RottenLinks\\Specials\\SpecialRottenLinks", "services": [ - "ConfigFactory", - "DBLoadBalancer" + "ConnectionProvider" ] } }, @@ -62,8 +61,8 @@ "Main": { "class": "Miraheze\\RottenLinks\\HookHandlers\\Main", "services": [ - "JobQueueGroup", - "ConnectionProvider" + "ConnectionProvider", + "JobQueueGroupFactory" ] }, "Scribunto": { @@ -72,36 +71,36 @@ }, "config": { "RottenLinksBadCodes": { - "value": [ "0", "400", "401", "403", "404", "405", "410", "502", "503", "504" ], - "description": "Holds a list of HTTP codes that are considered bad. (array)" + "description": "Array. Holds a list of HTTP codes that are considered bad.", + "value": [ "0", "400", "401", "403", "404", "405", "410", "502", "503", "504" ] }, "RottenLinksCurlTimeout": { - "value": 30, - "description": "Sets the timeout for cURL in seconds. (integer)" - }, - "RottenLinksHTTPProxy": { - "value": "", - "description": "Sets a proxy to use for requests. (string)" + "description": "Integer. Sets the timeout for cURL in seconds.", + "value": 30 }, "RottenLinksExcludeProtocols": { - "value": [ "tel", "mailto" ], - "description": "Holds a list of protocols that should not be checked for validity. (array)" + "description": "Array. Holds a list of protocols that should not be checked for validity.", + "value": [ "tel", "mailto" ] }, "RottenLinksExcludeWebsites": { - "value": false, - "description": "List of websites to exclude checking of response codes for. (array)" + "description": "Array. List of websites to exclude checking of response codes for.", + "value": [] }, "RottenLinksExternalLinkTarget": { - "value": "_self", - "description": "Sets the external link target (_self for the current tab or _blank for a new tab). (string)" + "description": "String. Sets the external link target (_self for the current tab or _blank for a new tab).", + "value": "_self" + }, + "RottenLinksHTTPProxy": { + "description": "String. Sets a proxy to use for requests.", + "value": "" }, "RottenLinksUserAgent": { - "value": "", - "description": "Overrides the user-agent to use for requests. Defaults to 'RottenLinks, MediaWiki extension (https://github.com/miraheze/RottenLinks), running on '. (string)" + "description": "String. Overrides the user-agent to use for requests. Defaults to 'RottenLinks, MediaWiki extension (https://github.com/miraheze/RottenLinks), running on '.", + "value": "" } }, "ConfigRegistry": { - "RottenLinks": "GlobalVarConfig::newInstance" + "RottenLinks": "MediaWiki\\Config\\GlobalVarConfig::newInstance" }, "manifest_version": 2 } diff --git a/includes/HookHandlers/Installer.php b/includes/HookHandlers/Installer.php index f2733fc..1d2bd4f 100644 --- a/includes/HookHandlers/Installer.php +++ b/includes/HookHandlers/Installer.php @@ -2,14 +2,11 @@ namespace Miraheze\RottenLinks\HookHandlers; -use DatabaseUpdater; use MediaWiki\Installer\Hook\LoadExtensionSchemaUpdatesHook; class Installer implements LoadExtensionSchemaUpdatesHook { - /** - * @param DatabaseUpdater $updater - */ + /** @inheritDoc */ public function onLoadExtensionSchemaUpdates( $updater ) { $dir = __DIR__ . '/../../sql'; diff --git a/includes/HookHandlers/Main.php b/includes/HookHandlers/Main.php index 8baa93a..af1fe61 100644 --- a/includes/HookHandlers/Main.php +++ b/includes/HookHandlers/Main.php @@ -2,26 +2,25 @@ namespace Miraheze\RottenLinks\HookHandlers; -use JobQueueGroup; use MediaWiki\Deferred\LinksUpdate\LinksUpdate; use MediaWiki\Hook\LinksUpdateCompleteHook; use MediaWiki\Hook\ParserFirstCallInitHook; +use MediaWiki\JobQueue\JobQueueGroupFactory; use MediaWiki\Parser\Parser; -use Miraheze\RottenLinks\RottenLinksJob; +use Miraheze\RottenLinks\Jobs\RottenLinksJob; use Miraheze\RottenLinks\RottenLinksParserFunctions; use Wikimedia\Rdbms\IConnectionProvider; class Main implements LinksUpdateCompleteHook, ParserFirstCallInitHook { - private JobQueueGroup $jobQueueGroup; + private JobQueueGroupFactory $jobQueueGroupFactory; private RottenLinksParserFunctions $parserFunctions; - /** - * @param JobQueueGroup $jobQueueGroup - * @param IConnectionProvider $connectionProvider - */ - public function __construct( JobQueueGroup $jobQueueGroup, IConnectionProvider $connectionProvider ) { - $this->jobQueueGroup = $jobQueueGroup; + public function __construct( + IConnectionProvider $connectionProvider, + JobQueueGroupFactory $jobQueueGroupFactory + ) { + $this->jobQueueGroupFactory = $jobQueueGroupFactory; $this->parserFunctions = new RottenLinksParserFunctions( $connectionProvider ); } @@ -37,11 +36,12 @@ public function onLinksUpdateComplete( $linksUpdate, $ticket ) { if ( $addedExternalLinks || $removedExternalLinks ) { $params = [ - 'addedExternalLinks' => $addedExternalLinks, - 'removedExternalLinks' => $removedExternalLinks + 'addedExternalLinks' => $addedExternalLinks ?? [], + 'removedExternalLinks' => $removedExternalLinks ?? [], ]; - $this->jobQueueGroup->push( new RottenLinksJob( $params ) ); + $jobQueueGroup = $this->jobQueueGroupFactory->makeJobQueueGroup(); + $jobQueueGroup->push( new RottenLinksJob( $params ) ); } } diff --git a/includes/RottenLinksJob.php b/includes/Jobs/RottenLinksJob.php similarity index 81% rename from includes/RottenLinksJob.php rename to includes/Jobs/RottenLinksJob.php index 5d76584..865ca8d 100644 --- a/includes/RottenLinksJob.php +++ b/includes/Jobs/RottenLinksJob.php @@ -1,43 +1,30 @@ addedExternalLinks = $params['addedExternalLinks'] ?? []; - $this->removedExternalLinks = $params['removedExternalLinks'] ?? []; + $this->addedExternalLinks = $params['addedExternalLinks']; + $this->removedExternalLinks = $params['removedExternalLinks']; } - /** - * Execute the job, updating the 'rottenlinks' table based on added and removed external links. - * - * @return bool True on success. - */ - public function run() { + public function run(): bool { $config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'RottenLinks' ); + $dbw = MediaWikiServices::getInstance()->getConnectionProvider()->getPrimaryDatabase(); if ( $this->addedExternalLinks ) { - $dbw = MediaWikiServices::getInstance() - ->getDBLoadBalancer() - ->getMaintenanceConnectionRef( DB_PRIMARY ); - $excludeProtocols = (array)$config->get( 'RottenLinksExcludeProtocols' ); $excludeWebsites = (array)$config->get( 'RottenLinksExcludeWebsites' ); @@ -86,10 +73,6 @@ public function run() { } if ( $this->removedExternalLinks ) { - $dbw = MediaWikiServices::getInstance() - ->getDBLoadBalancer() - ->getMaintenanceConnectionRef( DB_PRIMARY ); - foreach ( $this->removedExternalLinks as $url ) { $url = $this->decodeDomainName( $url ); @@ -130,7 +113,6 @@ public function run() { * URL-decoding the domain part turns these URLs back into valid syntax. * * @param string $url The URL to decode. - * * @return string The URL with the decoded domain name. */ private function decodeDomainName( string $url ): string { diff --git a/includes/RottenLinks.php b/includes/RottenLinks.php index 9b3cccd..9496d8e 100644 --- a/includes/RottenLinks.php +++ b/includes/RottenLinks.php @@ -2,19 +2,19 @@ namespace Miraheze\RottenLinks; -use Config; +use MediaWiki\Config\Config; use MediaWiki\MediaWikiServices; use WikiMedia\Rdbms\IReadableDatabase; class RottenLinks { + /** * Get the HTTP response status code for a given URL. * * @param string $url The URL to check. - * * @return int The HTTP status code. */ - public static function getResponse( string $url ) { + public static function getResponse( string $url ): int { $services = MediaWikiServices::getInstance(); $config = $services->getConfigFactory()->makeConfig( 'RottenLinks' ); @@ -41,7 +41,6 @@ public static function getResponse( string $url ) { * @param string $method The HTTP method to use ('HEAD' or 'GET'). * @param MediaWikiServices $services MediaWiki service instance. * @param Config $config Configuration instance. - * * @return int The HTTP status code. */ private static function getHttpStatus( @@ -49,7 +48,7 @@ private static function getHttpStatus( string $method, MediaWikiServices $services, Config $config - ) { + ): int { $httpProxy = $config->get( 'RottenLinksHTTPProxy' ); $userAgent = $config->get( 'RottenLinksUserAgent' ) ?: @@ -76,7 +75,6 @@ private static function getHttpStatus( * * @param IReadableDatabase $dbr * @param string $url - * * @return ?int null if the URL is not in the database, 0 if there was no response, or the response code */ public static function getResponseFromDatabase( IReadableDatabase $dbr, string $url ): ?int { diff --git a/includes/RottenLinksLuaLibrary.php b/includes/RottenLinksLuaLibrary.php index b1d03bb..420d437 100644 --- a/includes/RottenLinksLuaLibrary.php +++ b/includes/RottenLinksLuaLibrary.php @@ -1,4 +1,5 @@ showBad = $showBad; $this->config = $config; + $this->showBad = $showBad; } /** diff --git a/includes/RottenLinksParserFunctions.php b/includes/RottenLinksParserFunctions.php index 6b44361..652ba16 100644 --- a/includes/RottenLinksParserFunctions.php +++ b/includes/RottenLinksParserFunctions.php @@ -12,9 +12,6 @@ class RottenLinksParserFunctions { private IConnectionProvider $connectionProvider; - /** - * @param IConnectionProvider $connectionProvider - */ public function __construct( IConnectionProvider $connectionProvider ) { $this->connectionProvider = $connectionProvider; } diff --git a/includes/SpecialRottenLinks.php b/includes/Specials/SpecialRottenLinks.php similarity index 65% rename from includes/SpecialRottenLinks.php rename to includes/Specials/SpecialRottenLinks.php index bdbb1d4..b0ffbed 100644 --- a/includes/SpecialRottenLinks.php +++ b/includes/Specials/SpecialRottenLinks.php @@ -1,40 +1,26 @@ config = $configFactory->makeConfig( 'RottenLinks' ); - $this->dbLoadBalancer = $dbLoadBalancer; + $this->connectionProvider = $connectionProvider; } /** - * @param string $par + * @param ?string $par */ - public function execute( $par ) { + public function execute( $par ): void { $this->setHeaders(); $this->outputHeader(); $this->addHelpLink( 'Extension:RottenLinks' ); @@ -42,7 +28,12 @@ public function execute( $par ) { $showBad = $this->getRequest()->getBool( 'showBad' ); $stats = $this->getRequest()->getBool( 'stats' ); - $pager = new RottenLinksPager( $this->getContext(), $this->config, $showBad ); + $pager = new RottenLinksPager( + $this->getConfig(), + $this->getContext(), + $this->getLinkRenderer(), + $showBad + ); $formDescriptor = [ 'info' => [ @@ -53,21 +44,21 @@ public function execute( $par ) { 'type' => 'check', 'name' => 'showBad', 'label-message' => 'rottenlinks-showbad', - 'default' => $showBad + 'default' => $showBad, ], 'statistics' => [ 'type' => 'check', 'name' => 'stats', 'label-message' => 'rottenlinks-stats', - 'default' => $stats + 'default' => $stats, ], 'limit' => [ 'type' => 'limitselect', 'name' => 'limit', 'label-message' => 'table_pager_limit_label', 'default' => $pager->getLimit(), - 'options' => $pager->getLimitSelectList() - ] + 'options' => $pager->getLimitSelectList(), + ], ]; $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ); @@ -91,13 +82,8 @@ public function execute( $par ) { $this->getOutput()->addParserOutputContent( $pager->getFullOutput() ); } - /** - * Display statistics related to RottenLinks. - * - * @return array Array with statistics information. - */ - private function showStatistics() { - $dbr = $this->dbLoadBalancer->getMaintenanceConnectionRef( DB_REPLICA ); + private function showStatistics(): array { + $dbr = $this->connectionProvider->getReplicaDatabase(); $statusNumbers = $dbr->newSelectQueryBuilder() ->select( 'rl_respcode' ) @@ -122,19 +108,15 @@ private function showStatistics() { 'label' => "HTTP: {$respCode} " . ( $respCode != 0 ? HttpStatus::getMessage( $respCode ) : 'No Response' ), 'default' => $count, - 'section' => 'statistics' + 'section' => 'statistics', ]; } return $statDescriptor; } - /** - * Get the group name for the special page. - * - * @return string Group name. - */ - protected function getGroupName() { + /** @inheritDoc */ + protected function getGroupName(): string { return 'maintenance'; } } diff --git a/maintenance/updateExternalLinks.php b/maintenance/updateExternalLinks.php index b5c64c4..8c19a3c 100644 --- a/maintenance/updateExternalLinks.php +++ b/maintenance/updateExternalLinks.php @@ -2,75 +2,53 @@ namespace Miraheze\RottenLinks\Maintenance; +$IP ??= getenv( 'MW_INSTALL_PATH' ) ?: dirname( __DIR__, 3 ); +require_once "$IP/maintenance/Maintenance.php"; + use Maintenance; use MediaWiki\ExternalLinks\LinkFilter; -use MediaWiki\MediaWikiServices; use Miraheze\RottenLinks\RottenLinks; -$IP = getenv( 'MW_INSTALL_PATH' ); -if ( $IP === false ) { - $IP = __DIR__ . '/../../..'; -} - -require_once "$IP/maintenance/Maintenance.php"; - class UpdateExternalLinks extends Maintenance { + public function __construct() { parent::__construct(); $this->addDescription( 'Updates rottenlinks database table based on externallinks table.' ); - $this->requireExtension( 'RottenLinks' ); } - public function execute() { + public function execute(): void { $time = time(); - $config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'RottenLinks' ); $dbw = $this->getDB( DB_PRIMARY ); $this->output( "Dropping all existing recorded entries\n" ); - $dbw->delete( 'rottenlinks', - '*', - __METHOD__ - ); + $dbw->newDeleteQueryBuilder() + ->deleteFrom( 'rottenlinks' ) + ->where( '*' ) + ->caller( __METHOD__ ) + ->execute(); + + $res = $dbw->newSelectQueryBuilder() + ->select( [ + 'el_from', + 'el_to_domain_index', + 'el_to_path', + ] ) + ->from( 'externallinks' ) + ->caller( __METHOD__ ) + ->fetchResultSet(); $rottenlinksarray = []; - - if ( version_compare( MW_VERSION, '1.41', '>=' ) ) { - $res = $dbw->newSelectQueryBuilder() - ->select( [ - 'el_from', - 'el_to_domain_index', - 'el_to_path', - ] ) - ->from( 'externallinks' ) - ->caller( __METHOD__ ) - ->fetchResultSet(); - - foreach ( $res as $row ) { - // @phan-suppress-next-line PhanUndeclaredStaticMethod - $elUrl = LinkFilter::reverseIndexes( $row->el_to_domain_index ) . $row->el_to_path; - $rottenlinksarray[$elUrl][] = (int)$row->el_from; - } - } else { - $res = $dbw->newSelectQueryBuilder() - ->select( [ - 'el_from', - 'el_to', - ] ) - ->from( 'externallinks' ) - ->caller( __METHOD__ ) - ->fetchResultSet(); - - foreach ( $res as $row ) { - $rottenlinksarray[$row->el_to][] = (int)$row->el_from; - } + foreach ( $res as $row ) { + $elUrl = LinkFilter::reverseIndexes( $row->el_to_domain_index ) . $row->el_to_path; + $rottenlinksarray[$elUrl][] = (int)$row->el_from; } - $excludeProtocols = (array)$config->get( 'RottenLinksExcludeProtocols' ); - $excludeWebsites = (array)$config->get( 'RottenLinksExcludeWebsites' ); + $excludeProtocols = (array)$this->getConfig()->get( 'RottenLinksExcludeProtocols' ); + $excludeWebsites = (array)$this->getConfig()->get( 'RottenLinksExcludeWebsites' ); foreach ( $rottenlinksarray as $url => $pages ) { $url = $this->decodeDomainName( $url ); @@ -108,13 +86,14 @@ public function execute() { $resp = RottenLinks::getResponse( $url ); $pagecount = count( $pages ); - $dbw->insert( 'rottenlinks', - [ + $dbw->newInsertQueryBuilder() + ->insertInto( 'rottenlinks' ) + ->row( [ 'rl_externallink' => $url, - 'rl_respcode' => $resp - ], - __METHOD__ - ); + 'rl_respcode' => $resp, + ] ) + ->caller( __METHOD__ ) + ->execute(); $this->output( "Added externallink ($url) used on $pagecount with code $resp\n" ); } @@ -130,7 +109,6 @@ public function execute() { * URL-decoding the domain part turns these URLs back into valid syntax. * * @param string $url The URL to decode. - * * @return string The URL with the decoded domain name. */ private function decodeDomainName( string $url ): string {