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
|
|
|
* @var string |
21
|
|
|
*/ |
22
|
|
|
protected $repositoryName; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* @var string |
26
|
|
|
*/ |
27
|
|
|
protected $localPart; |
28
|
|
|
|
29
|
|
|
const PATTERN = '/^:?(\w+:)*[^:]+\z/'; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* @since 6.2 |
33
|
|
|
* |
34
|
|
|
* @param string $serialization |
35
|
|
|
*/ |
36
|
|
|
public function __construct( $serialization ) { |
37
|
|
|
self::assertValidSerialization( $serialization ); |
38
|
|
|
$this->serialization = self::normalizeIdSerialization( $serialization ); |
39
|
|
|
|
40
|
|
|
list ( $this->repositoryName, $this->localPart ) = self::extractRepositoryNameAndLocalPart( $serialization ); |
41
|
|
|
} |
42
|
|
|
|
43
|
|
|
private static function assertValidSerialization( $serialization ) { |
44
|
|
|
if ( !is_string( $serialization ) ) { |
45
|
|
|
throw new InvalidArgumentException( '$serialization must be a string' ); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
if ( $serialization === '' ) { |
49
|
|
|
throw new InvalidArgumentException( '$serialization must not be an empty string' ); |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
if ( !preg_match( self::PATTERN, $serialization ) ) { |
53
|
|
|
throw new InvalidArgumentException( '$serialization must match ' . self::PATTERN ); |
54
|
|
|
} |
55
|
|
|
} |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @return string |
59
|
|
|
*/ |
60
|
|
|
public abstract function getEntityType(); |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @return string |
64
|
|
|
*/ |
65
|
|
|
public function getSerialization() { |
66
|
|
|
return $this->serialization; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Returns an array with 3 elements: the foreign repository name as the first element, the local ID as the last |
71
|
|
|
* element and everything that is in between as the second element. |
72
|
|
|
* |
73
|
|
|
* EntityId::joinSerialization can be used to restore the original serialization from the parts returned. |
74
|
|
|
* |
75
|
|
|
* @since 6.2 |
76
|
|
|
* |
77
|
|
|
* @param string $serialization |
78
|
|
|
* @return string[] Array containing the serialization split into 3 parts. |
79
|
|
|
*/ |
80
|
|
|
public static function splitSerialization( $serialization ) { |
81
|
|
|
self::assertValidSerialization( $serialization ); |
82
|
|
|
|
83
|
|
|
return self::extractSerializationParts( self::normalizeIdSerialization( $serialization ) ); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* Splits the given ID serialization into an array with the following three elements: |
88
|
|
|
* - the repository name as the first element (empty string for local repository) |
89
|
|
|
* - any parts of the ID serialization but the repository name and the local ID (if any, empty string |
90
|
|
|
* if nothing else present) |
91
|
|
|
* - the local ID |
92
|
|
|
* Note: this method does not perform any validation of the given input. Calling code should take |
93
|
|
|
* care of this! |
94
|
|
|
* |
95
|
|
|
* @param $serialization |
96
|
|
|
* @return array |
97
|
|
|
*/ |
98
|
|
|
private static function extractSerializationParts( $serialization ) { |
99
|
|
|
$parts = explode( ':', $serialization ); |
100
|
|
|
$localPart = array_pop( $parts ); |
101
|
|
|
$repoName = array_shift( $parts ); |
102
|
|
|
$prefixRemainder = implode( ':', $parts ); |
103
|
|
|
|
104
|
|
|
return [ |
105
|
|
|
is_string( $repoName ) ? $repoName : '', |
106
|
|
|
$prefixRemainder, |
107
|
|
|
$localPart |
108
|
|
|
]; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Builds an ID serialization from the parts returned by EntityId::splitSerialization. |
113
|
|
|
* |
114
|
|
|
* @since 6.2 |
115
|
|
|
* |
116
|
|
|
* @param string[] $parts |
117
|
|
|
* @return string |
118
|
|
|
* |
119
|
|
|
* @throws InvalidArgumentException |
120
|
|
|
*/ |
121
|
|
|
public static function joinSerialization( array $parts ) { |
122
|
|
|
if ( end( $parts ) === '' ) { |
123
|
|
|
throw new InvalidArgumentException( 'The last element of $parts must not be empty.' ); |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
return implode( |
127
|
|
|
':', |
128
|
|
|
array_filter( $parts, function( $part ) { |
129
|
|
|
return $part !== ''; |
130
|
|
|
} ) |
131
|
|
|
); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Returns '' for local IDs and the foreign repository name for foreign IDs. For chained IDs (e.g. foo:bar:Q42) it |
136
|
|
|
* will return only the first part. |
137
|
|
|
* |
138
|
|
|
* @since 6.2 |
139
|
|
|
* |
140
|
|
|
* @return string |
141
|
|
|
*/ |
142
|
|
|
public function getRepositoryName() { |
143
|
|
|
return $this->repositoryName; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
/** |
147
|
|
|
* Returns the serialization without the first repository prefix. |
148
|
|
|
* |
149
|
|
|
* @since 6.2 |
150
|
|
|
* |
151
|
|
|
* @return string |
152
|
|
|
*/ |
153
|
|
|
public function getLocalPart() { |
154
|
|
|
return $this->localPart; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* Returns true iff EntityId::getRepoName returns a non-empty string. |
159
|
|
|
* |
160
|
|
|
* @since 6.2 |
161
|
|
|
* |
162
|
|
|
* @return bool |
163
|
|
|
*/ |
164
|
|
|
public function isForeign() { |
165
|
|
|
// not actually using EntityId::getRepoName for performance reasons |
166
|
|
|
return strpos( $this->serialization, ':' ) > 0; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
/** |
170
|
|
|
* @param string $id |
171
|
|
|
* @return string |
172
|
|
|
*/ |
173
|
|
|
private static function normalizeIdSerialization( $id ) { |
174
|
|
|
return ltrim( $id, ':' ); |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
/** |
178
|
|
|
* This is a human readable representation of the EntityId. |
179
|
|
|
* This format is allowed to change and should therefore not |
180
|
|
|
* be relied upon to be stable. |
181
|
|
|
* |
182
|
|
|
* @return string |
183
|
|
|
*/ |
184
|
|
|
public function __toString() { |
185
|
|
|
return $this->serialization; |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* @see Comparable::equals |
190
|
|
|
* |
191
|
|
|
* @since 0.5 |
192
|
|
|
* |
193
|
|
|
* @param mixed $target |
194
|
|
|
* |
195
|
|
|
* @return bool |
196
|
|
|
*/ |
197
|
|
|
public function equals( $target ) { |
198
|
|
|
if ( $this === $target ) { |
199
|
|
|
return true; |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
return $target instanceof self |
203
|
|
|
&& $target->serialization === $this->serialization; |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Returns a pair (repository name, local part of ID) from the given ID serialization. |
208
|
|
|
* Note: this does not perform any validation of the given input. Calling code should take |
209
|
|
|
* care of this! |
210
|
|
|
* |
211
|
|
|
* @param string $serialization |
212
|
|
|
* @return string[] Array of form [ string $repositoryName, string $localPart ] |
213
|
|
|
*/ |
214
|
|
|
protected static function extractRepositoryNameAndLocalPart( $serialization ) { |
215
|
|
|
$parts = explode( ':', $serialization, 2 ); |
216
|
|
|
return count( $parts ) > 1 ? [ $parts[0], $parts[1] ] : [ '', $parts[0] ]; |
217
|
|
|
return [ $repoName, $localPart ]; |
|
|
|
|
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
} |
221
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return
,die
orexit
statements that have been added for debug purposes.In the above example, the last
return false
will never be executed, because a return statement has already been met in every possible execution path.