Completed
Push — add/implement_pre_connection_j... ( 32e6e5...ab77f2 )
by
unknown
27:17 queued 20:18
created

Config::is_configured()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * The base Jetpack configuration class file.
4
 *
5
 * @package automattic/jetpack-config
6
 */
7
8
namespace Automattic\Jetpack;
9
10
use Automattic\Jetpack\Connection\Manager;
11
use Automattic\Jetpack\JITMS\JITM;
12
use Automattic\Jetpack\Connection\Plugin;
13
use Automattic\Jetpack\Plugin\Tracking as Plugin_Tracking;
14
use Automattic\Jetpack\Sync\Main as Sync_Main;
15
16
/**
17
 * The configuration class.
18
 */
19
class Config {
20
21
	const FEATURE_ENSURED         = 1;
22
	const FEATURE_NOT_AVAILABLE   = 0;
23
	const FEATURE_ALREADY_ENSURED = -1;
24
25
	/**
26
	 * The initial setting values.
27
	 *
28
	 * @var Array
29
	 */
30
	protected $config = array(
31
		'jitm'       => false,
32
		'connection' => false,
33
		'sync'       => false,
34
		'tracking'   => false,
35
		'tos'        => false,
36
	);
37
38
	/**
39
	 * Initialization options stored here.
40
	 *
41
	 * @var array
42
	 */
43
	protected $feature_options = array();
44
45
	/**
46
	 * Indicates if the configuration is needed.
47
	 * If at least one `Config` object was created, the property is set to `true`.
48
	 * If no objects were created, the value is `false`.
49
	 *
50
	 * @var bool
51
	 */
52
	private static $is_configuration_needed = false;
53
54
	/**
55
	 * Indicates whether the configuration process has completed.
56
	 *
57
	 * @var bool
58
	 */
59
	private static $is_configured = false;
60
61
	/**
62
	 * Creates the configuration class instance.
63
	 */
64
	public function __construct() {
65
		/**
66
		 * Adding the config handler to run on priority 2 because the class itself is
67
		 * being constructed on priority 1.
68
		 */
69
		add_action( 'plugins_loaded', array( $this, 'on_plugins_loaded' ), 2 );
70
71
		if ( false === self::$is_configuration_needed ) {
72
			// This is the first instance of the class, add the `on_configured` action (we need it to run only once).
73
			add_action( 'plugins_loaded', array( $this, 'on_configured' ), 3 );
74
			self::$is_configuration_needed = true;
75
		}
76
	}
77
78
	/**
79
	 * Require a feature to be initialized. It's up to the package consumer to actually add
80
	 * the package to their composer project. Declaring a requirement using this method
81
	 * instructs the class to initialize it.
82
	 *
83
	 * @param String $feature the feature slug.
84
	 * @param array  $options Additional options, optional.
85
	 */
86
	public function ensure( $feature, array $options = array() ) {
87
		$this->config[ $feature ] = true;
88
89
		$this->set_feature_options( $feature, $options );
90
	}
91
92
	/**
93
	 * Runs on plugins_loaded hook priority with priority 2.
94
	 *
95
	 * @action plugins_loaded
96
	 */
97
	public function on_plugins_loaded() {
98
		if ( $this->config['connection'] ) {
99
			$this->ensure_class( 'Automattic\Jetpack\Connection\Manager' )
100
				&& $this->ensure_feature( 'connection' );
101
		}
102
103
		if ( $this->config['tracking'] ) {
104
			$this->ensure_class( 'Automattic\Jetpack\Terms_Of_Service' )
105
				&& $this->ensure_class( 'Automattic\Jetpack\Tracking' )
106
				&& $this->ensure_feature( 'tracking' );
107
		}
108
109
		if ( $this->config['sync'] ) {
110
			$this->ensure_class( 'Automattic\Jetpack\Sync\Main' )
111
				&& $this->ensure_feature( 'sync' );
112
		}
113
114
		if ( $this->config['jitm'] ) {
115
			$this->ensure_class( 'Automattic\Jetpack\JITMS\JITM' )
116
				&& $this->ensure_feature( 'jitm' );
117
		}
118
	}
119
120
	/**
121
	 * The method is called when configuration is completed.
122
	 * `Config` is done, so we set the flag..
123
	 */
124
	public static function on_configured() {
125
		self::$is_configured = true;
126
	}
127
128
	/**
129
	 * The method can be used to check if the `Config` needs to be run.
130
	 *
131
	 * @return bool Returns `true` if configuration has completed, or not needed (no instances were created); `false` if configuration is pending.
132
	 */
133
	public static function is_configured() {
134
		return ! self::$is_configuration_needed || self::$is_configured;
135
	}
136
137
	/**
138
	 * Returns true if the required class is available and alerts the user if it's not available
139
	 * in case the site is in debug mode.
140
	 *
141
	 * @param String $classname a fully qualified class name.
142
	 * @return Boolean whether the class is available.
143
	 */
144
	protected function ensure_class( $classname ) {
145
		$available = class_exists( $classname );
146
147
		if ( ! $available && defined( 'WP_DEBUG' ) && WP_DEBUG ) {
148
			trigger_error( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error
149
				sprintf(
150
					/* translators: %1$s is a PHP class name. */
151
					esc_html__(
152
						'Unable to load class %1$s. Please add the package that contains it using composer and make sure you are requiring the Jetpack autoloader',
153
						'jetpack'
154
					),
155
					esc_html( $classname )
156
				),
157
				E_USER_NOTICE
158
			);
159
		}
160
161
		return $available;
162
	}
163
164
	/**
165
	 * Ensures a feature is enabled, sets it up if it hasn't already been set up.
166
	 * Run the options method (if exists) every time the method is called.
167
	 *
168
	 * @param String $feature slug of the feature.
169
	 * @return Integer either FEATURE_ENSURED, FEATURE_ALREADY_ENSURED or FEATURE_NOT_AVAILABLE constants.
170
	 */
171
	protected function ensure_feature( $feature ) {
172
		$method = 'enable_' . $feature;
173
		if ( ! method_exists( $this, $method ) ) {
174
			return self::FEATURE_NOT_AVAILABLE;
175
		}
176
177
		$method_options = 'ensure_options_' . $feature;
178
		if ( method_exists( $this, $method_options ) ) {
179
			$this->{ $method_options }();
180
		}
181
182
		if ( did_action( 'jetpack_feature_' . $feature . '_enabled' ) ) {
183
			return self::FEATURE_ALREADY_ENSURED;
184
		}
185
186
		$this->{ $method }();
187
188
		/**
189
		 * Fires when a specific Jetpack package feature is initalized using the Config package.
190
		 *
191
		 * @since 8.2.0
192
		 */
193
		do_action( 'jetpack_feature_' . $feature . '_enabled' );
194
195
		return self::FEATURE_ENSURED;
196
	}
197
198
	/**
199
	 * Dummy method to enable Terms of Service.
200
	 */
201
	protected function enable_tos() {
202
		return true;
203
	}
204
205
	/**
206
	 * Enables the tracking feature. Depends on the Terms of Service package, so enables it too.
207
	 */
208
	protected function enable_tracking() {
209
210
		// Enabling dependencies.
211
		$this->ensure_feature( 'tos' );
212
213
		$terms_of_service = new Terms_Of_Service();
214
		$tracking         = new Plugin_Tracking();
215
		if ( $terms_of_service->has_agreed() ) {
216
			add_action( 'init', array( $tracking, 'init' ) );
217
		} else {
218
			/**
219
			 * Initialize tracking right after the user agrees to the terms of service.
220
			 */
221
			add_action( 'jetpack_agreed_to_terms_of_service', array( $tracking, 'init' ) );
222
		}
223
224
		return true;
225
	}
226
227
	/**
228
	 * Enables the JITM feature.
229
	 */
230
	protected function enable_jitm() {
231
		JITM::configure();
232
233
		return true;
234
	}
235
236
	/**
237
	 * Enables the Sync feature.
238
	 */
239
	protected function enable_sync() {
240
		Sync_Main::configure();
241
242
		return true;
243
	}
244
245
	/**
246
	 * Enables the Connection feature.
247
	 */
248
	protected function enable_connection() {
249
		Manager::configure();
250
251
		return true;
252
	}
253
254
	/**
255
	 * Setup the Connection options.
256
	 */
257
	protected function ensure_options_connection() {
258
		$options = $this->get_feature_options( 'connection' );
259
260
		if ( ! empty( $options['slug'] ) ) {
261
			// The `slug` and `name` are removed from the options because they need to be passed as arguments.
262
			$slug = $options['slug'];
263
			unset( $options['slug'] );
264
265
			$name = $slug;
266
			if ( ! empty( $options['name'] ) ) {
267
				$name = $options['name'];
268
				unset( $options['name'] );
269
			}
270
271
			( new Plugin( $slug ) )->add( $name, $options );
272
		}
273
274
		return true;
275
	}
276
277
	/**
278
	 * Temporary save initialization options for a feature.
279
	 *
280
	 * @param string $feature The feature slug.
281
	 * @param array  $options The options.
282
	 *
283
	 * @return bool
284
	 */
285
	protected function set_feature_options( $feature, array $options ) {
286
		if ( $options ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $options of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
287
			$this->feature_options[ $feature ] = $options;
288
		}
289
290
		return true;
291
	}
292
293
	/**
294
	 * Get initialization options for a feature from the temporary storage.
295
	 *
296
	 * @param string $feature The feature slug.
297
	 *
298
	 * @return array
299
	 */
300
	protected function get_feature_options( $feature ) {
301
		return empty( $this->feature_options[ $feature ] ) ? array() : $this->feature_options[ $feature ];
302
	}
303
304
}
305