Completed
Push — master ( 85cf11...eb6656 )
by no
04:55 queued 02:19
created

EntityId::extractSerializationParts()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 9
nc 2
nop 1
crap 6
1
<?php
2
3
namespace Wikibase\DataModel\Entity;
4
5
use Comparable;
6
use InvalidArgumentException;
7
use Serializable;
8
9
/**
10
 * @since 0.5
11
 * Abstract since 2.0
12
 *
13
 * @license GPL-2.0+
14
 */
15
abstract class EntityId implements Comparable, Serializable {
16
17
	protected $serialization;
18
19
	/**
20
	 * @since 7.3
21
	 *
22
	 * @var string
23
	 */
24
	protected $repositoryName;
25
26
	/**
27
	 * @since 7.3
28 31
	 *
29 31
	 * @var string
30
	 */
31
	protected $localPart;
32
33
	const PATTERN = '/^:?(\w+:)*[^:]+\z/';
34
35
	/**
36
	 * @since 6.2
37
	 *
38
	 * @param string $serialization
39 5
	 *
40 5
	 * @throws InvalidArgumentException
41
	 */
42
	public function __construct( $serialization ) {
43
		self::assertValidSerialization( $serialization );
44
		$this->serialization = self::normalizeIdSerialization( $serialization );
45
46
		list ( $this->repositoryName, $this->localPart ) = self::extractRepositoryNameAndLocalPart( $serialization );
47
	}
48
49
	private static function assertValidSerialization( $serialization ) {
50
		if ( !is_string( $serialization ) ) {
51
			throw new InvalidArgumentException( '$serialization must be a string' );
52 5
		}
53 5
54 5
		if ( $serialization === '' ) {
55
			throw new InvalidArgumentException( '$serialization must not be an empty string' );
56
		}
57
58 5
		if ( !preg_match( self::PATTERN, $serialization ) ) {
59
			throw new InvalidArgumentException( '$serialization must match ' . self::PATTERN );
60
		}
61
	}
62
63
	/**
64
	 * @return string
65
	 */
66
	public abstract function getEntityType();
67
68
	/**
69
	 * @return string
70
	 */
71
	public function getSerialization() {
72
		return $this->serialization;
73
	}
74
75
	/**
76
	 * Returns an array with 3 elements: the foreign repository name as the first element, the local ID as the last
77
	 * element and everything that is in between as the second element.
78
	 *
79
	 * EntityId::joinSerialization can be used to restore the original serialization from the parts returned.
80
	 *
81
	 * @since 6.2
82
	 *
83
	 * @param string $serialization
84
	 *
85
	 * @throws InvalidArgumentException
86
	 * @return string[] Array containing the serialization split into 3 parts.
87
	 */
88
	public static function splitSerialization( $serialization ) {
89
		self::assertValidSerialization( $serialization );
90
91
		return self::extractSerializationParts( self::normalizeIdSerialization( $serialization ) );
92
	}
93
94
	/**
95
	 * Splits the given ID serialization into an array with the following three elements:
96
	 *  - the repository name as the first element (empty string for local repository)
97
	 *  - any parts of the ID serialization but the repository name and the local ID (if any, empty string
98
	 *    if nothing else present)
99
	 *  - the local ID
100
	 * Note: this method does not perform any validation of the given input. Calling code should take
101
	 * care of this!
102
	 *
103
	 * @param string $serialization
104
	 *
105
	 * @return string[]
106
	 */
107
	private static function extractSerializationParts( $serialization ) {
108
		$parts = explode( ':', $serialization );
109
		$localPart = array_pop( $parts );
110
		$repoName = array_shift( $parts );
111
		$prefixRemainder = implode( ':', $parts );
112
113
		return [
114
			is_string( $repoName ) ? $repoName : '',
115
			$prefixRemainder,
116
			$localPart
117
		];
118
	}
119
120
	/**
121
	 * Builds an ID serialization from the parts returned by EntityId::splitSerialization.
122
	 *
123
	 * @since 6.2
124
	 *
125
	 * @param string[] $parts
126
	 *
127
	 * @throws InvalidArgumentException
128
	 * @return string
129
	 */
130
	public static function joinSerialization( array $parts ) {
131
		if ( end( $parts ) === '' ) {
132
			throw new InvalidArgumentException( 'The last element of $parts must not be empty.' );
133
		}
134
135
		return implode(
136
			':',
137
			array_filter( $parts, function( $part ) {
138
				return $part !== '';
139
			} )
140
		);
141
	}
142
143
	/**
144
	 * Returns '' for local IDs and the foreign repository name for foreign IDs. For chained IDs (e.g. foo:bar:Q42) it
145
	 * will return only the first part.
146
	 *
147
	 * @since 6.2
148
	 *
149
	 * @return string
150
	 */
151
	public function getRepositoryName() {
152
		return $this->repositoryName;
153
	}
154
155
	/**
156
	 * Returns the serialization without the first repository prefix.
157
	 *
158
	 * @since 6.2
159
	 *
160
	 * @return string
161
	 */
162
	public function getLocalPart() {
163
		return $this->localPart;
164
	}
165
166
	/**
167
	 * Returns true iff EntityId::getRepoName returns a non-empty string.
168
	 *
169
	 * @since 6.2
170
	 *
171
	 * @return bool
172
	 */
173
	public function isForeign() {
174
		// not actually using EntityId::getRepoName for performance reasons
175
		return strpos( $this->serialization, ':' ) > 0;
176
	}
177
178
	/**
179
	 * @param string $id
180
	 *
181
	 * @return string
182
	 */
183
	private static function normalizeIdSerialization( $id ) {
184
		return ltrim( $id, ':' );
185
	}
186
187
	/**
188
	 * This is a human readable representation of the EntityId.
189
	 * This format is allowed to change and should therefore not
190
	 * be relied upon to be stable.
191
	 *
192
	 * @return string
193
	 */
194
	public function __toString() {
195
		return $this->serialization;
196
	}
197
198
	/**
199
	 * @see Comparable::equals
200
	 *
201
	 * @since 0.5
202
	 *
203
	 * @param mixed $target
204
	 *
205
	 * @return bool
206
	 */
207
	public function equals( $target ) {
208
		if ( $this === $target ) {
209
			return true;
210
		}
211
212
		return $target instanceof self
213
			&& $target->serialization === $this->serialization;
214
	}
215
216
	/**
217
	 * Returns a pair (repository name, local part of ID) from the given ID serialization.
218
	 * Note: this does not perform any validation of the given input. Calling code should take
219
	 * care of this!
220
	 *
221
	 * @since 7.3
222
	 *
223
	 * @param string $serialization
224
	 *
225
	 * @return string[] Array of form [ string $repositoryName, string $localPart ]
226
	 */
227
	protected static function extractRepositoryNameAndLocalPart( $serialization ) {
228
		$parts = explode( ':', $serialization, 2 );
229
		return isset( $parts[1] ) ? $parts : [ '', $parts[0] ];
230
	}
231
232
}
233