Completed
Push — add/changelog-tooling ( 7f5585...86359e )
by
unknown
58:45 queued 48:46
created

Config::getPlugin()   B

Complexity

Conditions 7
Paths 16

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
nc 16
nop 2
dl 0
loc 25
rs 8.5866
c 0
b 0
f 0
1
<?php // phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase
2
/**
3
 * Configuration loader for the changelogger tool.
4
 *
5
 * @package automattic/jetpack-changelogger
6
 */
7
8
// phpcs:disable WordPress.NamingConventions.ValidFunctionName, WordPress.NamingConventions.ValidVariableName, WordPress.WP.AlternativeFunctions
9
10
namespace Automattic\Jetpack\Changelogger;
11
12
use Symfony\Component\Console\Output\OutputInterface;
13
14
/**
15
 * Configuration loader for the changelogger tool.
16
 */
17
class Config {
18
19
	/**
20
	 * Default config settings.
21
	 *
22
	 * @var array
23
	 */
24
	private static $defaultConfig = array(
25
		'changelog'   => 'CHANGELOG.md',
26
		'changes-dir' => 'changelog',
27
		'formatter'   => 'keepachangelog',
28
		'types'       => array(
29
			'security'   => 'Security',
30
			'added'      => 'Added',
31
			'changed'    => 'Changed',
32
			'deprecated' => 'Deprecated',
33
			'removed'    => 'Removed',
34
			'fixed'      => 'Fixed',
35
		),
36
		'versioning'  => 'semver',
37
	);
38
39
	/**
40
	 * Active config settings.
41
	 *
42
	 * @var array
43
	 */
44
	private static $config = array();
45
46
	/**
47
	 * Cached config settings.
48
	 *
49
	 * @var array
50
	 */
51
	private static $cache = array();
52
53
	/**
54
	 * Whether `load()` was called already.
55
	 *
56
	 * @var bool
57
	 */
58
	private static $loaded = false;
59
60
	/**
61
	 * OutputInterface.
62
	 *
63
	 * @var OutputInterface|null
64
	 */
65
	private static $out;
66
67
	/**
68
	 * Set the OutputInterface.
69
	 *
70
	 * @param OutputInterface $out OutputInterface.
71
	 */
72
	public static function setOutput( OutputInterface $out ) {
73
		self::$out = $out;
74
	}
75
76
	/**
77
	 * Load the configuration.
78
	 *
79
	 * @throws \LogicException If called before `setOutput()`.
80
	 * @throws \DomainException If the path to composer.json exists but can't be `realpath`-ed.
81
	 */
82
	private static function load() {
83
		if ( ! self::$out ) {
84
			throw new \LogicException( 'Must call Config::setOutput() before Config::load()' );
85
		}
86
		if ( self::$loaded ) {
87
			return;
88
		}
89
		self::$loaded = true;
90
91
		self::$config         = self::$defaultConfig;
92
		self::$config['base'] = getcwd();
93
94
		$composer = getenv( 'COMPOSER' );
95
		if ( $composer ) {
96
			$from = ' (as specified by the COMPOSER environment variable)'; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
97
		} else {
98
			$composer = 'composer.json';
99
			$from     = ''; // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
100
		}
101
		if ( ! file_exists( $composer ) ) {
102
			self::$out->writeln( "<error>File {$composer}{$from} is not found.</>" );
103
			return;
104
		}
105
		$data = json_decode( file_get_contents( $composer ), true );
106
		if ( ! is_array( $data ) ) {
107
			self::$out->writeln( "<error>File {$composer}{$from} could not be parsed.</>" );
108
			return;
109
		}
110
111
		$dir = realpath( $composer );
112
		if ( false === $dir ) {
113
			throw new \DomainException( "Path $composer is not valid" ); // @codeCoverageIgnore
114
		}
115
		self::$config['base'] = dirname( $dir );
116
		if ( isset( $data['extra']['changelogger'] ) ) {
117
			self::$config = array_merge( self::$config, $data['extra']['changelogger'] );
118
		}
119
	}
120
121
	/**
122
	 * Get the base directory.
123
	 *
124
	 * @return string
125
	 */
126
	public static function base() {
127
		self::load();
128
		return self::$config['base'];
129
	}
130
131
	/**
132
	 * Add the base directory to a path, if necessary.
133
	 *
134
	 * @param string $path Path.
135
	 * @return string
136
	 */
137
	private static function addBase( $path ) {
138
		// Stupid Windows requires a regex.
139
		if ( ! preg_match( '#^(?:/|' . preg_quote( DIRECTORY_SEPARATOR, '#' ) . '|[a-zA-Z]:\\\\)#', $path ) ) {
140
			$path = self::base() . DIRECTORY_SEPARATOR . $path;
141
		}
142
		return $path;
143
	}
144
145
	/**
146
	 * Get the changelog filename.
147
	 *
148
	 * @return string
149
	 */
150 View Code Duplication
	public static function changelogFile() {
151
		self::load();
152
		if ( ! isset( self::$cache['changelog'] ) ) {
153
			self::$cache['changelog'] = self::addBase( self::$config['changelog'] );
154
		}
155
		return self::$cache['changelog'];
156
	}
157
158
	/**
159
	 * Get the changes directory.
160
	 *
161
	 * @return string
162
	 */
163 View Code Duplication
	public static function changesDir() {
164
		self::load();
165
		if ( ! isset( self::$cache['changes-dir'] ) ) {
166
			self::$cache['changes-dir'] = self::addBase( self::$config['changes-dir'] );
167
		}
168
		return self::$cache['changes-dir'];
169
	}
170
171
	/**
172
	 * Get change types.
173
	 *
174
	 * @return array
175
	 */
176
	public static function types() {
177
		self::load();
178
		if ( ! isset( self::$cache['types'] ) ) {
179
			self::$cache['types'] = array();
180
			foreach ( self::$config['types'] as $k => $v ) {
181
				self::$cache['types'][ strtolower( $k ) ] = $v;
182
			}
183
		}
184
		return self::$cache['types'];
185
	}
186
187
	/**
188
	 * Get a plugin.
189
	 *
190
	 * @param string|array $config Plugin name or configuration array.
191
	 * @param string       $suffix Plugin class suffix.
192
	 * @return object|null Object, or null if the plugin was not found.
193
	 */
194
	private static function getPlugin( $config, $suffix ) {
195
		if ( is_string( $config ) ) {
196
			$config = array( 'name' => $config );
197
		}
198
199
		if ( isset( $config['name'] ) ) {
200
			$class = __NAMESPACE__ . '\\Plugins\\' . ucfirst( $config['name'] ) . $suffix;
201
		} elseif ( isset( $config['class'] ) ) {
202
			$class = $config['class'];
203
		} elseif ( isset( $config['filename'] ) ) {
204
			$classes = get_declared_classes();
205
			require $config['filename'];
206
			$classes = array_diff( get_declared_classes(), $classes );
207
			if ( count( $classes ) !== 1 ) {
208
				return null;
209
			}
210
			$class = array_pop( $classes );
211
		} else {
212
			return null;
213
		}
214
		if ( ! class_exists( $class ) ) {
215
			return null;
216
		}
217
		return $class::instantiate( $config );
218
	}
219
220
	/**
221
	 * Get formatting plugin.
222
	 *
223
	 * @return Formatter
224
	 * @throws \RuntimeException If the configured formatter is unknown.
225
	 */
226 View Code Duplication
	public static function formatterPlugin() {
227
		self::load();
228
		if ( ! isset( self::$cache['formatter'] ) ) {
229
			$obj = self::getPlugin( self::$config['formatter'], 'Formatter' );
230
			if ( ! $obj instanceof FormatterPlugin ) {
231
				$info = json_encode( self::$config['formatter'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
232
				throw new \RuntimeException( "Unknown formatter plugin $info" );
233
			}
234
			self::$cache['formatter'] = $obj;
235
		}
236
		return self::$cache['formatter'];
237
	}
238
239
	/**
240
	 * Get verisoning plugin.
241
	 *
242
	 * @return Versioning
243
	 * @throws \RuntimeException If the configured versioning plugin is unknown.
244
	 */
245 View Code Duplication
	public static function versioningPlugin() {
246
		self::load();
247
		if ( ! isset( self::$cache['versioning'] ) ) {
248
			$obj = self::getPlugin( self::$config['versioning'], 'Versioning' );
249
			if ( ! $obj instanceof VersioningPlugin ) {
250
				$info = json_encode( self::$config['versioning'], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE );
251
				throw new \RuntimeException( "Unknown versioning plugin $info" );
252
			}
253
			self::$cache['versioning'] = $obj;
254
		}
255
		return self::$cache['versioning'];
256
	}
257
258
}
259