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