Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
HideUserUtils
0.00% covered (danger)
0.00%
0 / 43
0.00% covered (danger)
0.00%
0 / 3
56
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getExpression
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
20
 addFieldToBuilder
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace MediaWiki\Block;
4
5use Wikimedia\Rdbms\IReadableDatabase;
6use Wikimedia\Rdbms\SelectQueryBuilder;
7
8/**
9 * Helpers for building queries that determine whether a user is hidden
10 * @since 1.42
11 */
12class HideUserUtils {
13    /**
14     * Select users that are not hidden
15     */
16    public const SHOWN_USERS = 1;
17
18    /**
19     * Select users that are hidden
20     */
21    public const HIDDEN_USERS = 2;
22
23    /** @var int */
24    private $readStage;
25
26    public function __construct( $blockTargetMigrationStage ) {
27        $this->readStage = $blockTargetMigrationStage & SCHEMA_COMPAT_READ_MASK;
28    }
29
30    /**
31     * Get an SQL expression suitable for use in WHERE clause which will be
32     * true for either hidden or non-hidden users as specified.
33     *
34     * The expression will contain a subquery.
35     *
36     * @param IReadableDatabase $dbr
37     * @param string $userIdField The field to be used as the user_id when
38     *   joining on block/ipblocks. Defaults to "user_id".
39     * @param int $status Either self::SHOWN_USERS or self::HIDDEN_USERS
40     *   depending on what sort of user you want to match.
41     * @return string
42     */
43    public function getExpression(
44        IReadableDatabase $dbr,
45        string $userIdField = 'user_id',
46        $status = self::SHOWN_USERS
47    ) {
48        if ( $this->readStage === SCHEMA_COMPAT_READ_OLD ) {
49            $cond = $status === self::HIDDEN_USERS ? '' : 'NOT ';
50            $cond .= 'EXISTS (' .
51                $dbr->newSelectQueryBuilder()
52                    ->select( '1' )
53                    ->from( 'ipblocks', 'hu_ipblocks' )
54                    ->where( [ "hu_ipblocks.ipb_user=$userIdField", 'hu_ipblocks.ipb_deleted' => 1 ] )
55                    ->caller( __METHOD__ )
56                    ->getSQL() .
57                ')';
58        } else {
59            // Use a scalar subquery, not IN/EXISTS, to avoid materialization (T360160)
60            $cond = '(' .
61                $dbr->newSelectQueryBuilder()
62                    ->select( '1' )
63                    // $userIdField may be e.g. block_target.bt_user so an inner table
64                    // alias is necessary to ensure that that refers to the outer copy
65                    // of the block_target table.
66                    ->from( 'block_target', 'hu_block_target' )
67                    ->join( 'block', 'hu_block', 'hu_block.bl_target=hu_block_target.bt_id' )
68                    ->where( [ "hu_block_target.bt_user=$userIdField", 'hu_block.bl_deleted' => 1 ] )
69                    ->caller( __METHOD__ )
70                    ->getSQL() .
71                ') ' .
72                ( $status === self::HIDDEN_USERS ? 'IS NOT NULL' : 'IS NULL' );
73        }
74        return $cond;
75    }
76
77    /**
78     * Add a field and related joins to the query builder. The field in the
79     * query result will be true if the user is hidden or false otherwise.
80     *
81     * Note that a GROUP BY option will be set, to avoid duplicating the result
82     * row if the user is hidden by more than one block.
83     *
84     * @param SelectQueryBuilder $qb The query builder to be modified
85     * @param string $userIdField The name of the user_id field to use in the join
86     * @param string $deletedFieldAlias The field alias which will contain the
87     *   true if the user is deleted.
88     */
89    public function addFieldToBuilder(
90        SelectQueryBuilder $qb,
91        $userIdField = 'user_id',
92        $deletedFieldAlias = 'hu_deleted'
93    ) {
94        if ( $this->readStage === SCHEMA_COMPAT_READ_OLD ) {
95            $qb
96                ->select( [ $deletedFieldAlias => 'ipb_deleted IS NOT NULL' ] )
97                ->leftJoin(
98                    'ipblocks', 'hide_user_ipblocks',
99                    [ "ipb_user=$userIdField", 'ipb_deleted' => 1 ]
100                );
101        } else {
102            $group = $qb->newJoinGroup()
103                ->table( 'block_target' )
104                ->join( 'block', 'hide_user_block', 'bl_target=bt_id' );
105            $qb
106                ->select( [ $deletedFieldAlias => 'bl_deleted IS NOT NULL' ] )
107                ->leftJoin(
108                    $group,
109                    'hide_user_block_group',
110                    [ "bt_user=$userIdField", 'bl_deleted' => 1 ]
111                )
112                ->groupBy( $userIdField );
113        }
114    }
115}