Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
LocalUserOptionsStore
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 3
210
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
 fetch
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 store
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
110
1<?php
2
3namespace MediaWiki\User\Options;
4
5use MediaWiki\User\UserIdentity;
6use Wikimedia\Rdbms\DBAccessObjectUtils;
7use Wikimedia\Rdbms\IConnectionProvider;
8use Wikimedia\Rdbms\IDBAccessObject;
9
10class LocalUserOptionsStore implements UserOptionsStore {
11    private IConnectionProvider $dbProvider;
12
13    /** @var array[] Cached options for each user, by user ID */
14    private $optionsFromDb;
15
16    public function __construct( IConnectionProvider $dbProvider ) {
17        $this->dbProvider = $dbProvider;
18    }
19
20    public function fetch(
21        UserIdentity $user,
22        int $recency
23    ): array {
24        // In core, only users with local accounts may have preferences
25        if ( !$user->getId() ) {
26            return [];
27        }
28
29        $dbr = DBAccessObjectUtils::getDBFromRecency( $this->dbProvider, $recency );
30        $res = $dbr->newSelectQueryBuilder()
31            ->select( [ 'up_property', 'up_value' ] )
32            ->from( 'user_properties' )
33            ->where( [ 'up_user' => $user->getId() ] )
34            ->recency( $recency )
35            ->caller( __METHOD__ )->fetchResultSet();
36
37        $options = [];
38        foreach ( $res as $row ) {
39            $options[$row->up_property] = (string)$row->up_value;
40        }
41
42        $this->optionsFromDb[$user->getId()] = $options;
43        return $options;
44    }
45
46    public function store( UserIdentity $user, array $updates ) {
47        // In core, only users with local accounts may have preferences
48        if ( !$user->getId() ) {
49            return false;
50        }
51
52        $oldOptions = $this->optionsFromDb[ $user->getId() ]
53            ?? $this->fetch( $user, IDBAccessObject::READ_LATEST );
54        $newOptions = $oldOptions;
55        $keysToDelete = [];
56        $rowsToInsert = [];
57        foreach ( $updates as $key => $value ) {
58            if ( !UserOptionsManager::isValueEqual(
59                $value, $oldOptions[$key] ?? null )
60            ) {
61                // Update by deleting and reinserting
62                if ( array_key_exists( $key, $oldOptions ) ) {
63                    $keysToDelete[] = $key;
64                    unset( $newOptions[$key] );
65                }
66                if ( $value !== null ) {
67                    $truncValue = mb_strcut( $value, 0,
68                        UserOptionsManager::MAX_BYTES_OPTION_VALUE );
69                    $rowsToInsert[] = [
70                        'up_user' => $user->getId(),
71                        'up_property' => $key,
72                        'up_value' => $truncValue,
73                    ];
74                    $newOptions[$key] = $truncValue;
75                }
76            }
77        }
78        if ( !count( $keysToDelete ) && !count( $rowsToInsert ) ) {
79            // Nothing to do
80            return false;
81        }
82
83        // Do the DELETE
84        $dbw = $this->dbProvider->getPrimaryDatabase();
85        if ( $keysToDelete ) {
86            $dbw->newDeleteQueryBuilder()
87                ->deleteFrom( 'user_properties' )
88                ->where( [ 'up_user' => $user->getId() ] )
89                ->andWhere( [ 'up_property' => $keysToDelete ] )
90                ->caller( __METHOD__ )->execute();
91        }
92        if ( $rowsToInsert ) {
93            // Insert the new preference rows
94            $dbw->newInsertQueryBuilder()
95                ->insertInto( 'user_properties' )
96                ->ignore()
97                ->rows( $rowsToInsert )
98                ->caller( __METHOD__ )->execute();
99        }
100
101        // Update cache
102        $this->optionsFromDb[$user->getId()] = $newOptions;
103
104        return true;
105    }
106
107}