Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserMerger
0.00% covered (danger)
0.00%
0 / 66
0.00% covered (danger)
0.00%
0 / 6
272
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
2
 getAccountFields
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 finalizeMerge
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
20
 purgeTable
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
30
 loadFromTreeRevision
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 loadFromRevision
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3namespace Flow\Data\Utils;
4
5use BatchRowIterator;
6use Flow\Data\ManagerGroup;
7use Flow\DbFactory;
8use Flow\Model\AbstractRevision;
9use Flow\Model\PostRevision;
10use Flow\Model\UUID;
11use Iterator;
12
13class UserMerger {
14    /**
15     * @var DbFactory
16     */
17    protected $dbFactory;
18
19    /**
20     * @var ManagerGroup
21     */
22    protected $storage;
23
24    /**
25     * @var array[][]
26     */
27    protected $config;
28
29    public function __construct( DbFactory $dbFactory, ManagerGroup $storage ) {
30        $this->dbFactory = $dbFactory;
31        $this->storage = $storage;
32        $this->config = [
33            'flow_tree_revision' => [
34                'pk' => [ 'tree_rev_id' ],
35                'userColumns' => [
36                    'tree_orig_user_id' => 'getCreatorTuple',
37                ],
38                'load' => [ $this, 'loadFromTreeRevision' ],
39            ],
40
41            'flow_revision' => [
42                'pk' => [ 'rev_id' ],
43                'userColumns' => [
44                    'rev_user_id' => 'getUserTuple',
45                    'rev_mod_user_id' => 'getModeratedByTuple',
46                    'rev_edit_user_id' => 'getLastContentEditUserTuple',
47                ],
48                'load' => [ $this, 'loadFromRevision' ],
49                'loadColumns' => [ 'rev_type' ],
50            ],
51        ];
52    }
53
54    /**
55     * @return array
56     */
57    public function getAccountFields() {
58        $fields = [];
59        $dbw = $this->dbFactory->getDB( DB_PRIMARY );
60        foreach ( $this->config as $table => $config ) {
61            $row = [
62                'db' => $dbw,
63                $table,
64            ];
65            foreach ( array_keys( $config['userColumns'] ) as $column ) {
66                $row[] = $column;
67            }
68            $fields[] = $row;
69        }
70        return $fields;
71    }
72
73    /**
74     * Called after all databases have been updated. Needs to purge any
75     * cache that contained data about $oldUser
76     *
77     * @param int $oldUserId
78     * @param int $newUserId
79     */
80    public function finalizeMerge( $oldUserId, $newUserId ) {
81        $dbw = $this->dbFactory->getDB( DB_PRIMARY );
82        foreach ( $this->config as $table => $config ) {
83            foreach ( $config['userColumns'] as $column => $userTupleGetter ) {
84                $it = new BatchRowIterator( $dbw, $table, $config['pk'], 500 );
85                // The database is migrated, so look for the new user id
86                $it->addConditions( [ $column => $newUserId ] );
87                $it->setCaller( __METHOD__ );
88                if ( isset( $config['loadColumns'] ) ) {
89                    $it->setFetchColumns( $config['loadColumns'] );
90                }
91                $this->purgeTable( $it, $oldUserId, $config['load'], $userTupleGetter );
92            }
93        }
94    }
95
96    /**
97     * @param Iterator $it
98     * @param int $oldUserId
99     * @param callable $callback Receives a single row, returns domain object or null
100     * @param string $userTupleGetter Method to call on domain object that will return
101     *  a UserTuple instance.
102     */
103    protected function purgeTable( Iterator $it, $oldUserId, $callback, $userTupleGetter ) {
104        foreach ( $it as $batch ) {
105            foreach ( $batch as $pkRow ) {
106                $obj = $callback( $pkRow );
107                if ( !$obj ) {
108                    continue;
109                }
110                // This is funny looking because the loaded objects may have come from
111                // the db with new user ids, or the cache with old user ids.
112                // We need to tweak this object to look like the old user ids and then
113                // purge caches so they get the old user id cache keys.
114                $tuple = $obj->$userTupleGetter();
115                if ( !$tuple ) {
116                    continue;
117                }
118                $tuple->id = $oldUserId;
119                $om = $this->storage->getStorage( get_class( $obj ) );
120                $om->clear();
121                $om->merge( $obj );
122                $om->cachePurge( $obj );
123            }
124            $this->storage->clear();
125        }
126    }
127
128    /**
129     * @param object $row Single row from database
130     * @return PostRevision|null
131     */
132    protected function loadFromTreeRevision( $row ) {
133        return $this->storage->get( PostRevision::class, $row->tree_rev_id );
134    }
135
136    /**
137     * @param object $row Single row from database
138     * @return AbstractRevision|null
139     */
140    protected function loadFromRevision( $row ) {
141        $revTypes = [
142            'header' => \Flow\Model\Header::class,
143            'post-summary' => \Flow\Model\PostSummary::class,
144            'post' => PostRevision::class,
145        ];
146        if ( !isset( $revTypes[$row->rev_type] ) ) {
147            wfDebugLog( 'Flow', __METHOD__ . ': Unknown revision type ' . $row->rev_type . ' did not merge ' .
148                UUID::create( $row->rev_id )->getAlphadecimal() );
149            return null;
150        }
151
152        return $this->storage->get( $revTypes[$row->rev_type], $row->rev_id );
153    }
154}