Passed
Pull Request — 7.x (#767)
by Leszek
02:39
created

EntityId::getRepositoryNameAndLocalPart()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 1
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 ) = $this->getRepositoryNameAndLocalPart( $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::getSerializationParts( self::normalizeIdSerialization( $serialization ) );
84
	}
85
86
	private static function getSerializationParts( $serialization ) {
87
		$parts = explode( ':', $serialization );
88
		$localPart = array_pop( $parts );
89
		$repoName = array_shift( $parts );
90
		$prefixRemainder = implode( ':', $parts );
91
92
		return [
93
			is_string( $repoName ) ? $repoName : '',
94
			$prefixRemainder,
95
			$localPart
96
		];
97
	}
98
99
	/**
100
	 * Builds an ID serialization from the parts returned by EntityId::splitSerialization.
101
	 *
102
	 * @since 6.2
103
	 *
104
	 * @param string[] $parts
105
	 * @return string
106
	 *
107
	 * @throws InvalidArgumentException
108
	 */
109
	public static function joinSerialization( array $parts ) {
110
		if ( end( $parts ) === '' ) {
111
			throw new InvalidArgumentException( 'The last element of $parts must not be empty.' );
112
		}
113
114
		return implode(
115
			':',
116
			array_filter( $parts, function( $part ) {
117
				return $part !== '';
118
			} )
119
		);
120
	}
121
122
	/**
123
	 * Returns '' for local IDs and the foreign repository name for foreign IDs. For chained IDs (e.g. foo:bar:Q42) it
124
	 * will return only the first part.
125
	 *
126
	 * @since 6.2
127
	 *
128
	 * @return string
129
	 */
130
	public function getRepositoryName() {
131
		return $this->repositoryName;
132
	}
133
134
	/**
135
	 * Returns the serialization without the first repository prefix.
136
	 *
137
	 * @since 6.2
138
	 *
139
	 * @return string
140
	 */
141
	public function getLocalPart() {
142
		return $this->localPart;
143
	}
144
145
	/**
146
	 * Returns true iff EntityId::getRepoName returns a non-empty string.
147
	 *
148
	 * @since 6.2
149
	 *
150
	 * @return bool
151
	 */
152
	public function isForeign() {
153
		// not actually using EntityId::getRepoName for performance reasons
154
		return strpos( $this->serialization, ':' ) > 0;
155
	}
156
157
	/**
158
	 * @param string $id
159
	 * @return string
160
	 */
161
	private static function normalizeIdSerialization( $id ) {
162
		return ltrim( $id, ':' );
163
	}
164
165
	/**
166
	 * This is a human readable representation of the EntityId.
167
	 * This format is allowed to change and should therefore not
168
	 * be relied upon to be stable.
169
	 *
170
	 * @return string
171
	 */
172
	public function __toString() {
173
		return $this->serialization;
174
	}
175
176
	/**
177
	 * @see Comparable::equals
178
	 *
179
	 * @since 0.5
180
	 *
181
	 * @param mixed $target
182
	 *
183
	 * @return bool
184
	 */
185
	public function equals( $target ) {
186
		if ( $this === $target ) {
187
			return true;
188
		}
189
190
		return $target instanceof self
191
			&& $target->serialization === $this->serialization;
192
	}
193
194
	protected function getRepositoryNameAndLocalPart( $serialization ) {
195
		list( $repoName, $prefixRemainder, $localId ) = self::getSerializationParts( $serialization );
196
		$localPart = self::joinSerialization( [ '', $prefixRemainder, $localId ] );
197
		return [ $repoName, $localPart ];
198
	}
199
200
}
201