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