Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
69.23% covered (warning)
69.23%
36 / 52
22.22% covered (danger)
22.22%
2 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserNameBatch
69.23% covered (warning)
69.23%
36 / 52
22.22% covered (danger)
22.22%
2 / 9
40.78
0.00% covered (danger)
0.00%
0 / 1
 __construct
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 ensureLRU
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 clear
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 add
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 addFromTuple
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 get
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
3.02
 getFromTuple
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 resolve
89.47% covered (warning)
89.47%
17 / 19
0.00% covered (danger)
0.00%
0 / 1
6.04
 resolveUserPages
20.00% covered (danger)
20.00%
2 / 10
0.00% covered (danger)
0.00%
0 / 1
17.80
1<?php
2/**
3 * Provide usernames filtered by per-wiki ipblocks. Batches together
4 * database requests for multiple usernames when possible.
5 */
6namespace Flow\Repository;
7
8use Flow\Model\UserTuple;
9use MapCacheLRU;
10use MediaWiki\MediaWikiServices;
11use MediaWiki\User\User;
12use MediaWiki\WikiMap\WikiMap;
13
14/**
15 * Batch together queries for a bunch of wiki+userid -> username
16 */
17class UserNameBatch {
18    // Maximum number of usernames to cache for each wiki
19    private const USERNAMES_PER_WIKI = 250;
20
21    /**
22     * @var UserName\UserNameQuery
23     */
24    protected $query;
25
26    /**
27     * @var array[] map from wikiid to list of userid's to request
28     */
29    protected $queued = [];
30
31    /**
32     * @var array Map from wiki id to MapCacheLRU.  MapCacheLRU is a map of user ID (as
33     *  string, though to PHP it's the same anyway...) to username.
34     */
35    protected $usernames = [];
36
37    /**
38     * @param UserName\UserNameQuery $query
39     * @param array $queued map from wikiid to list of userid's to request
40     */
41    public function __construct( UserName\UserNameQuery $query, array $queued = [] ) {
42        $this->query = $query;
43        foreach ( $queued as $wiki => $userIds ) {
44            $this->queued[$wiki] = array_map( 'intval', $userIds );
45        }
46    }
47
48    /**
49     * Make sure the LRU for the given wiki is in place.
50     *
51     * @param string $wiki Wiki identifier
52     */
53    protected function ensureLRU( $wiki ) {
54        if ( !isset( $this->usernames[$wiki] ) ) {
55            $this->usernames[$wiki] = new MapCacheLRU( self::USERNAMES_PER_WIKI );
56        }
57    }
58
59    public function clear() {
60        $this->queued = [];
61        $this->usernames = [];
62    }
63
64    /**
65     * @param string $wiki
66     * @param int $userId
67     * @param string|null $userName Non null to set known usernames
68     */
69    public function add( $wiki, $userId, $userName = null ) {
70        $userId = (int)$userId;
71
72        $this->ensureLRU( $wiki );
73        if ( $userName !== null ) {
74            $this->usernames[$wiki]->set( (string)$userId, $userName );
75        } elseif ( !$this->usernames[$wiki]->has( (string)$userId ) ) {
76            $this->queued[$wiki][] = $userId;
77        }
78    }
79
80    public function addFromTuple( UserTuple $tuple ) {
81        $this->add( $tuple->wiki, $tuple->id, $tuple->ip );
82    }
83
84    /**
85     * Get the displayable username
86     *
87     * @param string $wiki
88     * @param int $userId
89     * @param string|bool $userIp
90     * @return string|bool false if username is not found or display is suppressed
91     * @todo Return something better for not found / suppressed, but what? Making
92     *   return type string|Message would suck.
93     */
94    public function get( $wiki, $userId, $userIp = false ) {
95        $userId = (int)$userId;
96        if ( $userId === 0 ) {
97            return $userIp;
98        }
99
100        $this->ensureLRU( $wiki );
101        if ( !$this->usernames[$wiki]->has( (string)$userId ) ) {
102            $this->queued[$wiki][] = $userId;
103            $this->resolve( $wiki );
104        }
105        return $this->usernames[$wiki]->get( (string)$userId );
106    }
107
108    /**
109     * @param UserTuple $tuple
110     * @return string|bool false if username is not found or display is suppressed
111     */
112    public function getFromTuple( UserTuple $tuple ) {
113        return $this->get( $tuple->wiki, $tuple->id, $tuple->ip );
114    }
115
116    /**
117     * Resolve all queued user ids to usernames for the given wiki
118     *
119     * @param string $wiki
120     */
121    public function resolve( $wiki ) {
122        if ( empty( $this->queued[$wiki] ) ) {
123            return;
124        }
125        $queued = array_unique( $this->queued[$wiki] );
126        if ( isset( $this->usernames[$wiki] ) ) {
127            $queued = array_diff( $queued, $this->usernames[$wiki]->getAllKeys() );
128        } else {
129            $this->ensureLRU( $wiki );
130        }
131
132        $res = $this->query->execute( $wiki, $queued );
133        unset( $this->queued[$wiki] );
134        if ( $res ) {
135            $usernames = [];
136            foreach ( $res as $row ) {
137                $id = (int)$row->user_id;
138                $usernames[$id] = $row->user_name;
139                $this->usernames[$wiki]->set( (string)$id, $row->user_name );
140            }
141            $this->resolveUserPages( $wiki, $usernames );
142            $missing = array_diff( $queued, array_keys( $usernames ) );
143        } else {
144            $missing = $queued;
145        }
146        foreach ( $missing as $id ) {
147            $this->usernames[$wiki]->set( (string)$id, false );
148        }
149    }
150
151    /**
152     * Update in-process title existence cache with NS_USER and
153     * NS_USER_TALK pages related to the provided usernames.
154     *
155     * @param string $wiki Wiki the users belong to
156     * @param array $usernames List of user names
157     */
158    protected function resolveUserPages( $wiki, array $usernames ) {
159        // LinkBatch currently only supports the current wiki
160        if ( $wiki !== WikiMap::getCurrentWikiId() || !$usernames ) {
161            return;
162        }
163
164        $lb = MediaWikiServices::getInstance()->getLinkBatchFactory()->newLinkBatch();
165        foreach ( $usernames as $name ) {
166            $user = User::newFromName( $name );
167            if ( $user ) {
168                $lb->addObj( $user->getUserPage() );
169                $lb->addObj( $user->getTalkPage() );
170            }
171        }
172        $lb->setCaller( __METHOD__ );
173        $lb->execute();
174    }
175}