More specific type hints and type declarations

Most notably:
* Use the much more narrow UserIdentity interface where possible.
* Make array type hints in PHPDocs as specific as possible.

Change-Id: Id189da4028b7874909277881dcf6539169dd13b6
This commit is contained in:
thiemowmde 2024-03-15 09:27:36 +01:00
parent 40d6588415
commit 7e3d73c11b
21 changed files with 71 additions and 87 deletions

View file

@ -18,10 +18,8 @@ use Wikimedia\ParamValidator\ParamValidator;
* - Optionally, override getForeignQueryParams() to customize what is sent to the foreign wikis
*/
trait ApiCrossWiki {
/**
* @var ForeignNotifications
*/
protected $foreignNotifications;
protected ?ForeignNotifications $foreignNotifications = null;
/**
* This will take the current API call (with all of its params) and execute
@ -73,7 +71,7 @@ trait ApiCrossWiki {
*
* @return string[]
*/
protected function getRequestedWikis() {
protected function getRequestedWikis(): array {
$params = $this->extractRequestParams();
// if wiki is omitted from params, that's because crosswiki is/was not
@ -99,31 +97,26 @@ trait ApiCrossWiki {
/**
* @return string[] Wiki names
*/
protected function getRequestedForeignWikis() {
protected function getRequestedForeignWikis(): array {
return array_diff( $this->getRequestedWikis(), [ WikiMap::getCurrentWikiId() ] );
}
/**
* @return ForeignNotifications
*/
protected function getForeignNotifications() {
if ( $this->foreignNotifications === null ) {
$this->foreignNotifications = new ForeignNotifications( $this->getUser() );
}
protected function getForeignNotifications(): ForeignNotifications {
$this->foreignNotifications ??= new ForeignNotifications( $this->getUser() );
return $this->foreignNotifications;
}
/**
* @return string[] Wiki names
*/
protected function getForeignWikisWithUnreadNotifications() {
protected function getForeignWikisWithUnreadNotifications(): array {
return $this->getForeignNotifications()->getWikis();
}
/**
* @return array[]
*/
public function getCrossWikiParams() {
public function getCrossWikiParams(): array {
global $wgConf;
$params = [];

View file

@ -18,6 +18,7 @@ use MediaWiki\Extension\Notifications\SeenTime;
use MediaWiki\Extension\Notifications\Services;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
use MediaWiki\WikiMap\WikiMap;
use Wikimedia\ParamValidator\ParamValidator;
use Wikimedia\ParamValidator\TypeDef\IntegerDef;
@ -343,12 +344,12 @@ class ApiEchoNotifications extends ApiQueryBase {
/**
* Internal helper method for getting property 'count' data
* @param User $user
* @param UserIdentity $user
* @param string[] $sections
* @param bool $groupBySection
* @return array
*/
protected function getPropCount( User $user, array $sections, $groupBySection ) {
protected function getPropCount( UserIdentity $user, array $sections, $groupBySection ) {
$result = [];
$notifUser = NotifUser::newFromUser( $user );
$global = $this->crossWikiSummary ? 'preference' : false;
@ -370,12 +371,12 @@ class ApiEchoNotifications extends ApiQueryBase {
/**
* Internal helper method for getting property 'seenTime' data
* @param User $user
* @param UserIdentity $user
* @param string[] $sections
* @param bool $groupBySection
* @return array
*/
protected function getPropSeenTime( User $user, array $sections, $groupBySection ) {
protected function getPropSeenTime( UserIdentity $user, array $sections, $groupBySection ) {
$result = [];
$seenTimeHelper = SeenTime::newFromUser( $user );
@ -649,10 +650,7 @@ class ApiEchoNotifications extends ApiQueryBase {
return $params;
}
/**
* @see ApiBase::getExamplesMessages()
* @return array
*/
/** @inheritDoc */
protected function getExamplesMessages() {
return [
'action=query&meta=notifications'

View file

@ -282,9 +282,9 @@ class AttributeManager {
* Gets an associative array mapping categories to the notification types in
* the category
*
* @return array[] Associative array with category as key
* @return array<string,string[]> Associative array with category as key
*/
public function getEventsByCategory() {
public function getEventsByCategory(): array {
$eventsByCategory = [];
foreach ( $this->categories as $category => $categoryDetails ) {

View file

@ -481,7 +481,7 @@ class NotificationController {
* Retrieves an array of User objects to be notified for an Event.
*
* @param Event $event
* @return Iterator values are User objects
* @return Iterator<User>
*/
public static function getUsersToNotifyForEvent( Event $event ) {
$notify = new FilteredSequentialIterator;

View file

@ -11,6 +11,7 @@ use MediaWiki\Extension\Notifications\Model\Event;
use MediaWiki\Extension\Notifications\Model\Notification;
use MediaWiki\Revision\RevisionRecord;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
use MediaWiki\Utils\MWTimestamp;
use MediaWiki\WikiMap\WikiMap;
use RequestContext;
@ -22,7 +23,7 @@ use RequestContext;
class DataOutputFormatter {
/**
* @var string[] type => class
* @var array<string,class-string<EchoEventFormatter>> type => class
*/
protected static $formatters = [
'flyout' => EchoFlyoutFormatter::class,
@ -223,13 +224,13 @@ class DataOutputFormatter {
/**
* Helper function for converting UTC timezone to a user's timezone
*
* @param User $user
* @param UserIdentity $user
* @param string|int $ts
* @param int $format output format
*
* @return string
*/
public static function getUserLocalTime( User $user, $ts, $format = TS_MW ) {
public static function getUserLocalTime( UserIdentity $user, $ts, $format = TS_MW ) {
$timestamp = new MWTimestamp( $ts );
$timestamp->offsetForUser( $user );

View file

@ -114,7 +114,7 @@ class ForeignNotifications {
* @param string $section Name of section
* @return string[]
*/
public function getWikis( $section = AttributeManager::ALL ) {
public function getWikis( $section = AttributeManager::ALL ): array {
$this->populate();
if ( $section === AttributeManager::ALL ) {
@ -202,7 +202,7 @@ class ForeignNotifications {
* @param string[] $wikis
* @return array[] [(string) wiki => (array) data]
*/
public static function getApiEndpoints( array $wikis ) {
public static function getApiEndpoints( array $wikis ): array {
global $wgConf;
$wgConf->loadFullData();

View file

@ -22,11 +22,10 @@ class ForeignWikiRequest {
/** @var User */
protected $user;
/** @var array */
protected $params;
protected array $params;
/** @var array */
protected $wikis;
/** @var string[] */
protected array $wikis;
/** @var string|null */
protected $wikiParam;
@ -43,7 +42,7 @@ class ForeignWikiRequest {
/**
* @param User $user
* @param array $params Request parameters
* @param array $wikis Wikis to send the request to
* @param string[] $wikis Wikis to send the request to
* @param string|null $wikiParam Parameter name to set to the name of the wiki
* @param string|null $postToken If set, use POST requests and inject a token of this type;
* if null, use GET requests.

View file

@ -12,6 +12,7 @@ use MediaWiki\Revision\RevisionRecord;
use MediaWiki\SpecialPage\SpecialPage;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
use MediaWiki\WikiMap\WikiMap;
use Message;
use MessageLocalizer;
@ -127,6 +128,7 @@ abstract class EchoEventPresentationModel implements JsonSerializable, MessageLo
global $wgEchoNotifications;
// @todo don't depend upon globals
/** @var class-string<EchoEventPresentationModel> $class */
$class = $wgEchoNotifications[$event->getType()]['presentation-model'];
return new $class( $event, $language, $user, $distributionType );
}
@ -562,10 +564,10 @@ abstract class EchoEventPresentationModel implements JsonSerializable, MessageLo
}
/**
* @param User $user
* @param UserIdentity $user
* @return string
*/
protected function getTruncatedUsername( User $user ) {
protected function getTruncatedUsername( UserIdentity $user ) {
return $this->language->embedBidi( $this->language->truncateForVisual(
$user->getName(), self::USERNAME_RECOMMENDED_LENGTH, '...', false ) );
}

View file

@ -43,7 +43,11 @@ class EchoForeignPresentationModel extends EchoEventPresentationModel {
return $msg;
}
protected function getWikiNames( array $wikis ) {
/**
* @param string[] $wikis
* @return string[]
*/
protected function getWikiNames( array $wikis ): array {
$data = ForeignNotifications::getApiEndpoints( $wikis );
$names = [];
foreach ( $wikis as $wiki ) {

View file

@ -152,9 +152,9 @@ EOF;
/**
* @param EchoEventPresentationModel[] $models
* @return array [ 'category name' => EchoEventPresentationModel[] ]
* @return array<string,EchoEventPresentationModel[]> [ 'category name' => EchoEventPresentationModel[] ]
*/
private function groupByCategory( array $models ) {
private function groupByCategory( array $models ): array {
$eventsByCategory = [];
foreach ( $models as $model ) {
$eventsByCategory[$model->getCategory()][] = $model;

View file

@ -11,7 +11,7 @@ class EchoModelFormatter extends EchoEventFormatter {
* @param EchoEventPresentationModel $model
* @return array
*/
protected function formatModel( EchoEventPresentationModel $model ) {
protected function formatModel( EchoEventPresentationModel $model ): array {
$data = $model->jsonSerialize();
$data['iconUrl'] = EchoIcon::getUrl( $model->getIconType(), $this->language->getDir() );

View file

@ -4,7 +4,7 @@ namespace MediaWiki\Extension\Notifications\Mapper;
use InvalidArgumentException;
use MediaWiki\Extension\Notifications\Model\Event;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
/**
* Database mapper for Event model, which is an immutable class, there should
@ -154,11 +154,11 @@ class EventMapper extends AbstractMapper {
/**
* Fetch events unread by a user and associated with a page
*
* @param User $user
* @param UserIdentity $user
* @param int $pageId
* @return Event[]
*/
public function fetchUnreadByUserAndPage( User $user, $pageId ) {
public function fetchUnreadByUserAndPage( UserIdentity $user, $pageId ) {
$dbr = $this->dbFactory->getEchoDb( DB_REPLICA );
$fields = array_merge( Event::selectFields(), [ 'notification_timestamp' ] );

View file

@ -5,17 +5,17 @@ namespace MediaWiki\Extension\Notifications\Push;
use MediaWiki\Extension\Notifications\Model\Event;
use MediaWiki\Extension\Notifications\Services;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
class PushNotifier {
/**
* Submits a notification derived from an Echo event to each push notifications service
* subscription found for a user, via a configured service handler implementation
* @param User $user
* @param UserIdentity $user
* @param Event $event
*/
public static function notifyWithPush( User $user, Event $event ): void {
public static function notifyWithPush( UserIdentity $user, Event $event ): void {
$attributeManager = Services::getInstance()->getAttributeManager();
$userEnabledEvents = $attributeManager->getUserEnabledEvents( $user, 'push' );
if ( in_array( $event->getType(), $userEnabledEvents ) ) {
@ -24,11 +24,11 @@ class PushNotifier {
}
/**
* @param User $user
* @param UserIdentity $user
* @param Event|null $event
* @return NotificationRequestJob
*/
private static function createJob( User $user, Event $event = null ): NotificationRequestJob {
private static function createJob( UserIdentity $user, Event $event = null ): NotificationRequestJob {
$centralId = Utils::getPushUserId( $user );
$params = [ 'centralId' => $centralId ];
// below params are only needed for debug logging (T255068)

View file

@ -89,9 +89,9 @@ class SubscriptionManager extends AbstractMapper {
/**
* Get full data for all registered subscriptions for a user (by central ID).
* @param int $centralId
* @return array array of Subscription objects
* @return Subscription[]
*/
public function getSubscriptionsForUser( int $centralId ) {
public function getSubscriptionsForUser( int $centralId ): array {
$res = $this->dbr->newSelectQueryBuilder()
->select( '*' )
->from( 'echo_push_subscription' )

View file

@ -7,7 +7,7 @@ use CachedBagOStuff;
use MediaWiki\Deferred\DeferredUpdates;
use MediaWiki\MediaWikiServices;
use MediaWiki\User\CentralId\CentralIdLookup;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
use UnexpectedValueException;
/**
@ -22,23 +22,16 @@ class SeenTime {
*/
private static $allowedTypes = [ 'alert', 'message' ];
/**
* @var User
*/
private $user;
private UserIdentity $user;
/**
* @param User $user A logged in user
* @param UserIdentity $user A logged in user
*/
private function __construct( User $user ) {
private function __construct( UserIdentity $user ) {
$this->user = $user;
}
/**
* @param User $user
* @return SeenTime
*/
public static function newFromUser( User $user ) {
public static function newFromUser( UserIdentity $user ): self {
return new self( $user );
}

View file

@ -4,23 +4,19 @@ namespace MediaWiki\Extension\Notifications;
use MediaWiki\Title\Title;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
class SummaryParser {
/** @var callable */
private $userLookup;
/**
* @param callable|null $userLookup Function that receives User object and returns its id
* or 0 if the user doesn't exist. Passing null to this parameter will result in default
* implementation being used.
* @param callable|null $userLookup An alternative filter function that receives a User object
* (see the caller in {@see parse}) and returns the user id or 0 if the user doesn't exist.
* Only tests should modify this.
*/
public function __construct( callable $userLookup = null ) {
$this->userLookup = $userLookup;
if ( !$this->userLookup ) {
$this->userLookup = static function ( User $user ) {
return $user->getId();
};
}
$this->userLookup = $userLookup ?? static fn ( UserIdentity $user ) => $user->getId();
}
/**
@ -48,8 +44,7 @@ class SummaryParser {
&& $title->getNamespace() === NS_USER
) {
$user = User::newFromName( $title->getText() );
$lookup = $this->userLookup;
if ( $user && $lookup( $user ) > 0 ) {
if ( $user && ( $this->userLookup )( $user ) > 0 ) {
$users[$user->getName()] = $user;
}
}

View file

@ -57,7 +57,7 @@ class SuppressionRowUpdateGenerator implements RowUpdateGenerator {
* @param stdClass $row A row from the database
* @return array All updates required for this row
*/
protected function updatePageIdFromTitle( $row ) {
protected function updatePageIdFromTitle( $row ): array {
$update = [];
$title = $this->newTitleFromNsAndText( $row->event_page_namespace, $row->event_page_title );
if ( $title !== null ) {
@ -88,7 +88,7 @@ class SuppressionRowUpdateGenerator implements RowUpdateGenerator {
*
* @return array All updates required for this row
*/
protected function updatePageLinkedExtraData( $row, array $update ) {
protected function updatePageLinkedExtraData( $row, array $update ): array {
$extra = $this->extra( $row, $update );
if ( isset( $extra['link-from-title'] ) && isset( $extra['link-from-namespace'] ) ) {
@ -115,7 +115,7 @@ class SuppressionRowUpdateGenerator implements RowUpdateGenerator {
* @param array $update Updates that need to be applied to the database row
* @return array The event extra data
*/
protected function extra( $row, array $update = [] ) {
protected function extra( $row, array $update = [] ): array {
if ( isset( $update['event_extra'] ) ) {
return unserialize( $update['event_extra'] );
}

View file

@ -168,7 +168,7 @@ class UserLocator {
*
* @param Event $event
* @param string[] $keys one or more keys to check for user ids
* @return User[]
* @return array<int,User>
*/
public static function locateFromEventExtra( Event $event, array $keys ) {
$users = [];

View file

@ -66,9 +66,8 @@ class NotificationTest extends MediaWikiIntegrationTestCase {
/**
* Mock a notification row from database
* @return array
*/
protected function mockNotificationRow() {
protected function mockNotificationRow(): array {
return [
'notification_user' => 1,
'notification_event' => 1,
@ -80,9 +79,8 @@ class NotificationTest extends MediaWikiIntegrationTestCase {
/**
* Mock an event row from database
* @return array
*/
protected function mockEventRow() {
protected function mockEventRow(): array {
return [
'event_id' => 1,
'event_type' => 'test_event',
@ -97,9 +95,8 @@ class NotificationTest extends MediaWikiIntegrationTestCase {
/**
* Mock a target page row
* @return array
*/
protected function mockTargetPageRow() {
protected function mockTargetPageRow(): array {
return [
'etp_page' => 2,
'etp_event' => 1

View file

@ -3,6 +3,7 @@
use MediaWiki\Extension\Notifications\Mapper\NotificationMapper;
use MediaWiki\Extension\Notifications\Model\Event;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
/**
* Tests for the built in notification types
@ -21,7 +22,7 @@ class NotificationsTest extends MediaWikiIntegrationTestCase {
/**
* Helper function to get a user's latest notification
* @param User $user
* @param UserIdentity $user
* @return Event
*/
public static function getLatestNotification( $user ) {

View file

@ -2,6 +2,7 @@
use MediaWiki\Extension\Notifications\SummaryParser;
use MediaWiki\User\User;
use MediaWiki\User\UserIdentity;
/**
* @group Echo
@ -22,7 +23,7 @@ class SummaryParserTest extends MediaWikiIntegrationTestCase {
* @param string[] $expectedUsers
*/
public function testParse( $summary, array $expectedUsers ) {
$parser = new SummaryParser( function ( User $user ) {
$parser = new SummaryParser( function ( UserIdentity $user ) {
if ( in_array( $user->getName(), $this->existingUsers ) ) {
return crc32( $user->getName() );
}