Completed
Branch master (f93894)
by
unknown
27:35
created

DBSiteStore::setLanguageCodeMapping()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 * Represents the site configuration of a wiki.
5
 * Holds a list of sites (ie SiteList), stored in the database.
6
 *
7
 * This program is free software; you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation; either version 2 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License along
18
 * with this program; if not, write to the Free Software Foundation, Inc.,
19
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
 * http://www.gnu.org/copyleft/gpl.html
21
 *
22
 * @since 1.25
23
 *
24
 * @file
25
 * @ingroup Site
26
 *
27
 * @license GNU GPL v2+
28
 * @author Jeroen De Dauw < [email protected] >
29
 * @author Daniel Kinzler
30
 */
31
class DBSiteStore implements SiteStore {
32
33
	/**
34
	 * @var SiteList|null
35
	 */
36
	protected $sites = null;
37
38
	/**
39
	 * @var LoadBalancer
40
	 */
41
	private $dbLoadBalancer;
42
43
	/**
44
	 * @since 1.27
45
	 *
46
	 * @todo: inject some kind of connection manager that is aware of the target wiki,
47
	 * instead of injecting a LoadBalancer.
48
	 *
49
	 * @param LoadBalancer $dbLoadBalancer
50
	 */
51
	public function __construct( LoadBalancer $dbLoadBalancer ) {
52
		$this->dbLoadBalancer = $dbLoadBalancer;
53
	}
54
55
	/**
56
	 * @see SiteStore::getSites
57
	 *
58
	 * @since 1.25
59
	 *
60
	 * @return SiteList
61
	 */
62
	public function getSites() {
63
		$this->loadSites();
64
65
		return $this->sites;
66
	}
67
68
	/**
69
	 * Fetches the site from the database and loads them into the sites field.
70
	 *
71
	 * @since 1.25
72
	 */
73
	protected function loadSites() {
74
		$this->sites = new SiteList();
75
76
		$dbr = $this->dbLoadBalancer->getConnection( DB_SLAVE );
77
78
		$res = $dbr->select(
79
			'sites',
80
			[
81
				'site_id',
82
				'site_global_key',
83
				'site_type',
84
				'site_group',
85
				'site_source',
86
				'site_language',
87
				'site_protocol',
88
				'site_domain',
89
				'site_data',
90
				'site_forward',
91
				'site_config',
92
			],
93
			'',
94
			__METHOD__,
95
			[ 'ORDER BY' => 'site_global_key' ]
96
		);
97
98
		foreach ( $res as $row ) {
0 ignored issues
show
Bug introduced by
The expression $res of type object<ResultWrapper>|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
99
			$site = Site::newForType( $row->site_type );
100
			$site->setGlobalId( $row->site_global_key );
101
			$site->setInternalId( (int)$row->site_id );
102
			$site->setForward( (bool)$row->site_forward );
103
			$site->setGroup( $row->site_group );
104
			$site->setLanguageCode( $row->site_language === ''
105
				? null
106
				: $row->site_language
107
			);
108
			$site->setSource( $row->site_source );
109
			$site->setExtraData( unserialize( $row->site_data ) );
110
			$site->setExtraConfig( unserialize( $row->site_config ) );
111
			$this->sites[] = $site;
112
		}
113
114
		// Batch load the local site identifiers.
115
		$ids = $dbr->select(
116
			'site_identifiers',
117
			[
118
				'si_site',
119
				'si_type',
120
				'si_key',
121
			],
122
			[],
123
			__METHOD__
124
		);
125
126
		foreach ( $ids as $id ) {
0 ignored issues
show
Bug introduced by
The expression $ids of type object<ResultWrapper>|boolean is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
127
			if ( $this->sites->hasInternalId( $id->si_site ) ) {
128
				$site = $this->sites->getSiteByInternalId( $id->si_site );
129
				$site->addLocalId( $id->si_type, $id->si_key );
130
				$this->sites->setSite( $site );
0 ignored issues
show
Unused Code introduced by
The call to the method SiteList::setSite() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
131
			}
132
		}
133
134
		$this->dbLoadBalancer->reuseConnection( $dbr );
135
	}
136
137
	/**
138
	 * @see SiteStore::getSite
139
	 *
140
	 * @since 1.25
141
	 *
142
	 * @param string $globalId
143
	 *
144
	 * @return Site|null
145
	 */
146
	public function getSite( $globalId ) {
147
		if ( $this->sites === null ) {
148
			$this->sites = $this->getSites();
149
		}
150
151
		return $this->sites->hasSite( $globalId ) ? $this->sites->getSite( $globalId ) : null;
152
	}
153
154
	/**
155
	 * @see SiteStore::saveSite
156
	 *
157
	 * @since 1.25
158
	 *
159
	 * @param Site $site
160
	 *
161
	 * @return bool Success indicator
162
	 */
163
	public function saveSite( Site $site ) {
164
		return $this->saveSites( [ $site ] );
165
	}
166
167
	/**
168
	 * @see SiteStore::saveSites
169
	 *
170
	 * @since 1.25
171
	 *
172
	 * @param Site[] $sites
173
	 *
174
	 * @return bool Success indicator
175
	 */
176
	public function saveSites( array $sites ) {
177
		if ( empty( $sites ) ) {
178
			return true;
179
		}
180
181
		$dbw = $this->dbLoadBalancer->getConnection( DB_MASTER );
182
183
		$dbw->startAtomic( __METHOD__ );
184
185
		$success = true;
186
187
		$internalIds = [];
188
		$localIds = [];
189
190
		foreach ( $sites as $site ) {
191
			if ( $site->getInternalId() !== null ) {
192
				$internalIds[] = $site->getInternalId();
193
			}
194
195
			$fields = [
196
				// Site data
197
				'site_global_key' => $site->getGlobalId(), // TODO: check not null
198
				'site_type' => $site->getType(),
199
				'site_group' => $site->getGroup(),
200
				'site_source' => $site->getSource(),
201
				'site_language' => $site->getLanguageCode() === null ? '' : $site->getLanguageCode(),
202
				'site_protocol' => $site->getProtocol(),
203
				'site_domain' => strrev( $site->getDomain() ) . '.',
204
				'site_data' => serialize( $site->getExtraData() ),
205
206
				// Site config
207
				'site_forward' => $site->shouldForward() ? 1 : 0,
208
				'site_config' => serialize( $site->getExtraConfig() ),
209
			];
210
211
			$rowId = $site->getInternalId();
212
			if ( $rowId !== null ) {
213
				$success = $dbw->update(
214
					'sites', $fields, [ 'site_id' => $rowId ], __METHOD__
215
				) && $success;
216
			} else {
217
				$rowId = $dbw->nextSequenceValue( 'sites_site_id_seq' );
218
				$fields['site_id'] = $rowId;
219
				$success = $dbw->insert( 'sites', $fields, __METHOD__ ) && $success;
220
				$rowId = $dbw->insertId();
221
			}
222
223 View Code Duplication
			foreach ( $site->getLocalIds() as $idType => $ids ) {
224
				foreach ( $ids as $id ) {
225
					$localIds[] = [ $rowId, $idType, $id ];
226
				}
227
			}
228
		}
229
230
		if ( $internalIds !== [] ) {
231
			$dbw->delete(
232
				'site_identifiers',
233
				[ 'si_site' => $internalIds ],
234
				__METHOD__
235
			);
236
		}
237
238
		foreach ( $localIds as $localId ) {
239
			$dbw->insert(
240
				'site_identifiers',
241
				[
242
					'si_site' => $localId[0],
243
					'si_type' => $localId[1],
244
					'si_key' => $localId[2],
245
				],
246
				__METHOD__
247
			);
248
		}
249
250
		$dbw->endAtomic( __METHOD__ );
251
252
		$this->dbLoadBalancer->reuseConnection( $dbw );
253
254
		$this->reset();
255
256
		return $success;
257
	}
258
259
	/**
260
	 * Resets the SiteList
261
	 *
262
	 * @since 1.25
263
	 */
264
	public function reset() {
265
		$this->sites = null;
266
	}
267
268
	/**
269
	 * Clears the list of sites stored in the database.
270
	 *
271
	 * @see SiteStore::clear()
272
	 *
273
	 * @return bool Success
274
	 */
275
	public function clear() {
276
		$dbw = $this->dbLoadBalancer->getConnection( DB_MASTER );
277
278
		$dbw->startAtomic( __METHOD__ );
279
		$ok = $dbw->delete( 'sites', '*', __METHOD__ );
280
		$ok = $dbw->delete( 'site_identifiers', '*', __METHOD__ ) && $ok;
281
		$dbw->endAtomic( __METHOD__ );
282
283
		$this->dbLoadBalancer->reuseConnection( $dbw );
284
285
		$this->reset();
286
287
		return $ok;
288
	}
289
290
}
291