Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
2 / 2
CRAP
100.00% covered (success)
100.00%
1 / 1
UserNamePrefixSearch
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
2 / 2
7
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 search
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\User;
8
9use InvalidArgumentException;
10use MediaWiki\Block\HideUserUtils;
11use MediaWiki\Permissions\Authority;
12use Wikimedia\Rdbms\IConnectionProvider;
13use Wikimedia\Rdbms\IExpression;
14use Wikimedia\Rdbms\LikeValue;
15
16/**
17 * Handles searching prefixes of user names
18 *
19 * @since 1.36
20 * @ingroup User
21 * @author DannyS712
22 */
23class UserNamePrefixSearch {
24
25    public const AUDIENCE_PUBLIC = 'public';
26
27    public function __construct(
28        private readonly IConnectionProvider $dbProvider,
29        private readonly UserNameUtils $userNameUtils,
30        private readonly HideUserUtils $hideUserUtils
31    ) {
32    }
33
34    /**
35     * Do a prefix search of usernames and return a list of matching usernames.
36     *
37     * @param string|Authority $audience Either AUDIENCE_PUBLIC or a user to
38     *    show the search for
39     * @param string $search
40     * @param int $limit
41     * @param int $offset How many results to offset from the beginning
42     * @return string[]
43     * @throws InvalidArgumentException if $audience is invalid
44     */
45    public function search( $audience, string $search, int $limit, int $offset = 0 ): array {
46        if ( $audience !== self::AUDIENCE_PUBLIC &&
47            !( $audience instanceof Authority )
48        ) {
49            throw new InvalidArgumentException(
50                '$audience must be AUDIENCE_PUBLIC or an Authority object'
51            );
52        }
53
54        // Invalid usernames are treated as empty strings
55        $prefix = $this->userNameUtils->getCanonical( $search ) ?: '';
56
57        $dbr = $this->dbProvider->getReplicaDatabase();
58        $queryBuilder = $dbr->newSelectQueryBuilder()
59            ->select( 'user_name' )
60            ->from( 'user' )
61            ->where( $dbr->expr( 'user_name', IExpression::LIKE, new LikeValue( $prefix, $dbr->anyString() ) ) )
62            ->orderBy( 'user_name' )
63            ->limit( $limit )
64            ->offset( $offset );
65
66        // Filter out hidden usernames
67        if ( $audience === self::AUDIENCE_PUBLIC || !$audience->isAllowed( 'hideuser' ) ) {
68            $queryBuilder->andWhere( $this->hideUserUtils->getExpression( $dbr ) );
69        }
70
71        return $queryBuilder->caller( __METHOD__ )->fetchFieldValues();
72    }
73}