Completed
Push — update/use-identity-crisis-pac... ( d14b6d...6e141b )
by
unknown
128:58 queued 119:18
created

ChangelogEntry::__construct()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11

Duplication

Lines 11
Ratio 100 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 11
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php // phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase
2
/**
3
 * Class representing a changelog entry.
4
 *
5
 * @package automattic/jetpack-changelogger
6
 */
7
8
// phpcs:disable WordPress.NamingConventions.ValidFunctionName.MethodNameInvalid
9
10
namespace Automattic\Jetpack\Changelog;
11
12
use DateTime;
13
use InvalidArgumentException;
14
use JsonSerializable;
15
16
/**
17
 * Class representing a changelog entry.
18
 */
19
class ChangelogEntry implements JsonSerializable {
20
21
	/**
22
	 * Entry version.
23
	 *
24
	 * @var string
25
	 */
26
	protected $version;
27
28
	/**
29
	 * Entry link.
30
	 *
31
	 * @var string|null
32
	 */
33
	protected $link = null;
34
35
	/**
36
	 * Entry timestamp.
37
	 *
38
	 * @var DateTime|null
39
	 */
40
	protected $timestamp;
41
42
	/**
43
	 * Content before the changes themselves.
44
	 *
45
	 * @var string
46
	 */
47
	protected $prologue = '';
48
49
	/**
50
	 * Content after the changes themselves.
51
	 *
52
	 * @var string
53
	 */
54
	protected $epilogue = '';
55
56
	/**
57
	 * Changes.
58
	 *
59
	 * @var ChangeEntry[]
60
	 */
61
	protected $changes = array();
62
63
	/**
64
	 * Constructor.
65
	 *
66
	 * @param string $version Version for the new entry.
67
	 * @param array  $data Data for other entry fields. Keys correspond to the setters, e.g. key 'link' calls `setLink()`.
68
	 * @throws InvalidArgumentException If an argument is invalid.
69
	 */
70 View Code Duplication
	public function __construct( $version, array $data = array() ) {
71
		$data = array( 'version' => $version ) + $data + array( 'timestamp' => 'now' );
72
		foreach ( $data as $k => $v ) {
73
			$func = array( $this, 'set' . ucfirst( $k ) );
74
			if ( is_callable( $func ) ) {
75
				$func( $v );
76
			} else {
77
				throw new InvalidArgumentException( __METHOD__ . ": Unrecognized data item \"$k\"." );
78
			}
79
		}
80
	}
81
82
	/**
83
	 * Get the version.
84
	 *
85
	 * @return string
86
	 */
87
	public function getVersion() {
88
		return $this->version;
89
	}
90
91
	/**
92
	 * Set the version.
93
	 *
94
	 * @param string $version Version to set.
95
	 * @returns $this
96
	 * @throws InvalidArgumentException If an argument is invalid.
97
	 */
98
	public function setVersion( $version ) {
99
		$version = (string) $version;
100
		if ( '' === $version ) {
101
			throw new InvalidArgumentException( __METHOD__ . ': Version may not be empty' );
102
		}
103
		$this->version = (string) $version;
104
		return $this;
105
	}
106
107
	/**
108
	 * Get the link.
109
	 *
110
	 * @return string|null
111
	 */
112
	public function getLink() {
113
		return $this->link;
114
	}
115
116
	/**
117
	 * Set the link.
118
	 *
119
	 * @param string|null $link Link to set.
120
	 * @returns $this
121
	 * @throws InvalidArgumentException If an argument is invalid.
122
	 */
123
	public function setLink( $link ) {
124
		if ( '' === $link ) {
125
			$link = null;
126
		} elseif ( null !== $link ) {
127
			$link = filter_var( $link, FILTER_VALIDATE_URL );
128
			if ( ! is_string( $link ) ) {
129
				throw new InvalidArgumentException( __METHOD__ . ': Invalid URL' );
130
			}
131
		}
132
		$this->link = $link;
133
		return $this;
134
	}
135
136
	/**
137
	 * Get the timestamp.
138
	 *
139
	 * The timestamp may be null, which should be interpreted as "unreleased".
140
	 *
141
	 * @return DateTime|null
142
	 */
143
	public function getTimestamp() {
144
		return $this->timestamp;
145
	}
146
147
	/**
148
	 * Set the timestamp.
149
	 *
150
	 * The timestamp may be null, which should be interpreted as "unreleased".
151
	 *
152
	 * @param DateTime|string|null $timestamp Timestamp to set.
153
	 * @returns $this
154
	 * @throws InvalidArgumentException If an argument is invalid.
155
	 */
156 View Code Duplication
	public function setTimestamp( $timestamp ) {
157
		if ( null !== $timestamp && ! $timestamp instanceof DateTime ) {
158
			try {
159
				$timestamp = new DateTime( $timestamp );
160
			} catch ( \Exception $ex ) {
161
				throw new InvalidArgumentException( __METHOD__ . ': Invalid timestamp', 0, $ex );
162
			}
163
		}
164
		$this->timestamp = $timestamp;
165
		return $this;
166
	}
167
168
	/**
169
	 * Get the prologue content.
170
	 *
171
	 * @return string
172
	 */
173
	public function getPrologue() {
174
		return $this->prologue;
175
	}
176
177
	/**
178
	 * Set the prologue content.
179
	 *
180
	 * @param string $prologue Prologue content to set.
181
	 * @return $this
182
	 */
183
	public function setPrologue( $prologue ) {
184
		$this->prologue = (string) $prologue;
185
		return $this;
186
	}
187
188
	/**
189
	 * Get the epilogue content.
190
	 *
191
	 * @return string
192
	 */
193
	public function getEpilogue() {
194
		return $this->epilogue;
195
	}
196
197
	/**
198
	 * Set the epilogue content.
199
	 *
200
	 * @param string $epilogue Epilogue content to set.
201
	 * @return $this
202
	 */
203
	public function setEpilogue( $epilogue ) {
204
		$this->epilogue = (string) $epilogue;
205
		return $this;
206
	}
207
208
	/**
209
	 * Get the list of changes.
210
	 *
211
	 * @return ChangeEntry[]
212
	 */
213
	public function getChanges() {
214
		return $this->changes;
215
	}
216
217
	/**
218
	 * Set the list of changes.
219
	 *
220
	 * This replaces all existing changes. The caller is responsible
221
	 * for making sure the changes are sorted properly.
222
	 *
223
	 * @param ChangeEntry[] $changes Changes.
224
	 * @return $this
225
	 * @throws InvalidArgumentException If an argument is invalid.
226
	 */
227 View Code Duplication
	public function setChanges( array $changes ) {
228
		foreach ( $changes as $i => $change ) {
229
			if ( ! $change instanceof ChangeEntry ) {
230
				$what = is_object( $change ) ? get_class( $change ) : gettype( $change );
231
				throw new InvalidArgumentException( __METHOD__ . ": Expected a ChangeEntry, got $what at index $i" );
232
			}
233
		}
234
		$this->changes = array_values( $changes );
235
		return $this;
236
	}
237
238
	/**
239
	 * Add a new change.
240
	 *
241
	 * The new change is inserted before the first existing change where
242
	 * `ChangeEntry::compare()` says the existing change should come after.
243
	 *
244
	 * @param ChangeEntry $change New change.
245
	 * @param array       $compareconfig Comparison config, see `ChangeEntry::compare()`.
246
	 * @return $this
247
	 */
248
	public function insertChange( ChangeEntry $change, $compareconfig = array() ) {
249
		foreach ( $this->changes as $i => $e ) {
250
			if ( ChangeEntry::compare( $change, $e, $compareconfig ) < 0 ) {
251
				array_splice( $this->changes, $i, 0, array( $change ) );
252
				return $this;
253
			}
254
		}
255
		$this->changes[] = $change;
256
		return $this;
257
	}
258
259
	/**
260
	 * Append a new change.
261
	 *
262
	 * @param ChangeEntry $change New change.
263
	 * @return $this
264
	 */
265
	public function appendChange( ChangeEntry $change ) {
266
		$this->changes[] = $change;
267
		return $this;
268
	}
269
270
	/**
271
	 * Get the changes grouped by subheading.
272
	 *
273
	 * @param string|null $subheading Subheading to retrieve.
274
	 * @return ChangeEntry[]|ChangeEntry[][] An array of changes with the
275
	 *   heading if `$subheading` was passed, or an array keyed by subheading of
276
	 *   arrays of changes if it was null.
277
	 */
278
	public function getChangesBySubheading( $subheading = null ) {
279
		$ret = array();
280
		foreach ( $this->changes as $entry ) {
281
			$ret[ $entry->getSubheading() ][] = $entry;
282
		}
283
284
		return null === $subheading
285
			? $ret
286
			: ( isset( $ret[ $subheading ] ) ? $ret[ $subheading ] : array() );
287
	}
288
289
	/**
290
	 * Return data for serializing to JSON.
291
	 *
292
	 * @return array
293
	 */
294
	public function jsonSerialize() {
295
		return array(
296
			'__class__' => static::class,
297
			'version'   => $this->version,
298
			'link'      => $this->link,
299
			'timestamp' => null === $this->timestamp ? null : $this->timestamp->format( DateTime::ISO8601 ),
300
			'prologue'  => $this->prologue,
301
			'epilogue'  => $this->epilogue,
302
			'changes'   => $this->changes,
303
		);
304
	}
305
306
	/**
307
	 * Unserialize from JSON.
308
	 *
309
	 * @param array $data JSON data as returned by self::jsonSerialize().
310
	 * @return static
311
	 * @throws InvalidArgumentException If the data is invalid.
312
	 */
313
	public static function jsonUnserialize( $data ) {
314
		$data = (array) $data;
315
		if ( ! isset( $data['__class__'] ) || ! isset( $data['version'] ) ) {
316
			throw new InvalidArgumentException( 'Invalid data' );
317
		}
318
		$class   = $data['__class__'];
319
		$version = $data['version'];
320
		unset( $data['__class__'], $data['version'] );
321 View Code Duplication
		if ( ! class_exists( $class ) || ! is_a( $class, static::class, true ) ) {
322
			throw new InvalidArgumentException( "Cannot instantiate $class via " . static::class . '::' . __FUNCTION__ );
323
		}
324
		if ( isset( $data['changes'] ) ) {
325
			$data['changes'] = array_map( array( ChangeEntry::class, 'jsonUnserialize' ), $data['changes'] );
326
		}
327
		return new $class( $version, $data );
328
	}
329
330
}
331