Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
41.96% covered (danger)
41.96%
47 / 112
37.50% covered (danger)
37.50%
3 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
DBSiteStore
42.34% covered (danger)
42.34%
47 / 111
37.50% covered (danger)
37.50%
3 / 8
124.40
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
 getSites
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 loadSites
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 1
30
 getSite
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 saveSite
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 saveSites
65.31% covered (warning)
65.31%
32 / 49
0.00% covered (danger)
0.00%
0 / 1
14.18
 reset
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 clear
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\Site;
22
23use Wikimedia\Rdbms\IConnectionProvider;
24use Wikimedia\Rdbms\IDatabase;
25
26/**
27 * Holds a list of sites stored in the database.
28 *
29 * @since 1.25
30 * @ingroup Site
31 * @author Jeroen De Dauw < jeroendedauw@gmail.com >
32 * @author Daniel Kinzler
33 */
34class DBSiteStore implements SiteStore {
35    /** @var SiteList|null */
36    protected $sites = null;
37    /** @var IConnectionProvider */
38    private $dbProvider;
39
40    /**
41     * @since 1.27
42     * @param IConnectionProvider $dbProvider
43     */
44    public function __construct( IConnectionProvider $dbProvider ) {
45        $this->dbProvider = $dbProvider;
46    }
47
48    /**
49     * @see SiteStore::getSites
50     *
51     * @since 1.25
52     * @return SiteList
53     */
54    public function getSites() {
55        $this->loadSites();
56
57        return $this->sites;
58    }
59
60    /**
61     * Fetches the site from the database and loads them into the sites field.
62     *
63     * @since 1.25
64     */
65    protected function loadSites() {
66        $this->sites = new SiteList();
67
68        $dbr = $this->dbProvider->getReplicaDatabase();
69
70        $res = $dbr->newSelectQueryBuilder()
71            ->select( [
72                'site_id',
73                'site_global_key',
74                'site_type',
75                'site_group',
76                'site_source',
77                'site_language',
78                'site_protocol',
79                'site_domain',
80                'site_data',
81                'site_forward',
82                'site_config',
83            ] )
84            ->from( 'sites' )
85            ->orderBy( 'site_global_key' )
86            ->caller( __METHOD__ )->fetchResultSet();
87
88        foreach ( $res as $row ) {
89            $site = Site::newForType( $row->site_type );
90            $site->setGlobalId( $row->site_global_key );
91            $site->setInternalId( (int)$row->site_id );
92            $site->setForward( (bool)$row->site_forward );
93            $site->setGroup( $row->site_group );
94            $site->setLanguageCode( $row->site_language === ''
95                ? null
96                : $row->site_language
97            );
98            $site->setSource( $row->site_source );
99            $site->setExtraData( unserialize( $row->site_data ) );
100            $site->setExtraConfig( unserialize( $row->site_config ) );
101            $this->sites[] = $site;
102        }
103
104        // Batch load the local site identifiers.
105        $ids = $dbr->newSelectQueryBuilder()
106            ->select( [ 'si_site', 'si_type', 'si_key', ] )
107            ->from( 'site_identifiers' )
108            ->caller( __METHOD__ )->fetchResultSet();
109
110        foreach ( $ids as $id ) {
111            if ( $this->sites->hasInternalId( $id->si_site ) ) {
112                $site = $this->sites->getSiteByInternalId( $id->si_site );
113                $site->addLocalId( $id->si_type, $id->si_key );
114                $this->sites->setSite( $site );
115            }
116        }
117    }
118
119    /**
120     * @see SiteStore::getSite
121     *
122     * @since 1.25
123     * @param string $globalId
124     * @return Site|null
125     */
126    public function getSite( $globalId ) {
127        if ( $this->sites === null ) {
128            $this->sites = $this->getSites();
129        }
130
131        return $this->sites->hasSite( $globalId ) ? $this->sites->getSite( $globalId ) : null;
132    }
133
134    /**
135     * @see SiteStore::saveSite
136     *
137     * @since 1.25
138     * @param Site $site
139     * @return bool Success indicator
140     */
141    public function saveSite( Site $site ) {
142        return $this->saveSites( [ $site ] );
143    }
144
145    /**
146     * @see SiteStore::saveSites
147     *
148     * @since 1.25
149     * @param Site[] $sites
150     * @return bool Success indicator
151     */
152    public function saveSites( array $sites ) {
153        if ( !$sites ) {
154            return true;
155        }
156
157        $dbw = $this->dbProvider->getPrimaryDatabase();
158
159        $dbw->startAtomic( __METHOD__ );
160
161        $internalIds = [];
162        $localIds = [];
163
164        foreach ( $sites as $site ) {
165            if ( $site->getInternalId() !== null ) {
166                $internalIds[] = $site->getInternalId();
167            }
168
169            $fields = [
170                // Site data
171                'site_global_key' => $site->getGlobalId(), // TODO: check not null
172                'site_type' => $site->getType(),
173                'site_group' => $site->getGroup(),
174                'site_source' => $site->getSource(),
175                'site_language' => $site->getLanguageCode() ?? '',
176                'site_protocol' => $site->getProtocol(),
177                'site_domain' => strrev( $site->getDomain() ?? '' ) . '.',
178                'site_data' => serialize( $site->getExtraData() ),
179
180                // Site config
181                'site_forward' => $site->shouldForward() ? 1 : 0,
182                'site_config' => serialize( $site->getExtraConfig() ),
183            ];
184
185            $rowId = $site->getInternalId();
186            if ( $rowId !== null ) {
187                $dbw->newUpdateQueryBuilder()
188                    ->update( 'sites' )
189                    ->set( $fields )
190                    ->where( [ 'site_id' => $rowId ] )
191                    ->caller( __METHOD__ )->execute();
192            } else {
193                $dbw->newInsertQueryBuilder()
194                    ->insertInto( 'sites' )
195                    ->row( $fields )
196                    ->caller( __METHOD__ )->execute();
197                $rowId = $dbw->insertId();
198            }
199
200            foreach ( $site->getLocalIds() as $idType => $ids ) {
201                foreach ( $ids as $id ) {
202                    $localIds[] = [ $rowId, $idType, $id ];
203                }
204            }
205        }
206
207        if ( $internalIds !== [] ) {
208            $dbw->newDeleteQueryBuilder()
209                ->deleteFrom( 'site_identifiers' )
210                ->where( [ 'si_site' => $internalIds ] )
211                ->caller( __METHOD__ )->execute();
212        }
213
214        foreach ( $localIds as $localId ) {
215            $dbw->newInsertQueryBuilder()
216                ->insertInto( 'site_identifiers' )
217                ->row( [ 'si_site' => $localId[0], 'si_type' => $localId[1], 'si_key' => $localId[2] ] )
218                ->caller( __METHOD__ )->execute();
219        }
220
221        $dbw->endAtomic( __METHOD__ );
222
223        $this->reset();
224
225        return true;
226    }
227
228    /**
229     * Resets the SiteList
230     *
231     * @since 1.25
232     */
233    public function reset() {
234        $this->sites = null;
235    }
236
237    /**
238     * Clears the list of sites stored in the database.
239     *
240     * @see SiteStore::clear()
241     */
242    public function clear() {
243        $dbw = $this->dbProvider->getPrimaryDatabase();
244
245        $dbw->startAtomic( __METHOD__ );
246        $dbw->newDeleteQueryBuilder()
247            ->deleteFrom( 'sites' )
248            ->where( IDatabase::ALL_ROWS )
249            ->caller( __METHOD__ )->execute();
250        $dbw->newDeleteQueryBuilder()
251            ->deleteFrom( 'site_identifiers' )
252            ->where( IDatabase::ALL_ROWS )
253            ->caller( __METHOD__ )->execute();
254        $dbw->endAtomic( __METHOD__ );
255
256        $this->reset();
257    }
258
259}
260
261/** @deprecated class alias since 1.41 */
262class_alias( DBSiteStore::class, 'DBSiteStore' );