Carbon_Fields::is_booted()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
namespace Carbon_Fields;
4
5
use Carbon_Fields\Pimple\Container as PimpleContainer;
6
use Carbon_Fields\Helper\Helper;
7
use Carbon_Fields\Loader\Loader;
8
use Carbon_Fields\Container\Repository as ContainerRepository;
9
use Carbon_Fields\Toolset\Key_Toolset;
10
use Carbon_Fields\Toolset\WP_Toolset;
11
use Carbon_Fields\Service\Meta_Query_Service;
12
use Carbon_Fields\Service\Legacy_Storage_Service_v_1_5;
13
use Carbon_Fields\Service\Revisions_Service;
14
use Carbon_Fields\Service\REST_API_Service;
15
use Carbon_Fields\Libraries\Sidebar_Manager\Sidebar_Manager;
16
17
use Carbon_Fields\REST_API\Router as REST_API_Router;
18
use Carbon_Fields\REST_API\Decorator as REST_API_Decorator;
19
20
use Carbon_Fields\Event\Emitter;
21
use Carbon_Fields\Event\PersistentListener;
22
use Carbon_Fields\Event\SingleEventListener;
23
24
use Carbon_Fields\Exception\Incorrect_Syntax_Exception;
25
26
/**
27
 * Holds a static reference to the ioc container
28
 */
29
final class Carbon_Fields {
30
31
	/**
32
	 * An event emitter to facilitate events before the WordPress environment is guaranteed to be loaded
33
	 *
34
	 * @var Emitter
35
	 */
36
	protected $emitter;
37
38
	/**
39
	 * Flag if Carbon Fields has been booted
40
	 *
41
	 * @var bool
42
	 */
43
	public $booted = false;
44
45
	/**
46
	 * Inversion of Control container instance
47
	 *
48
	 * @var PimpleContainer
49
	 */
50
	public $ioc = null;
51
52
	/**
53
	 * Singleton implementation
54
	 *
55
	 * @return Carbon_Fields
56
	 */
57
	public static function instance() {
58
		static $instance = null;
59
		if ( $instance === null ) {
60
			$instance = new static();
61
		}
62
		return $instance;
63
	}
64
65
	/**
66
	 * Constructor
67
	 */
68
	private function __construct() {
69
		$this->install( $this->get_default_ioc() );
70
	}
71
72
	/**
73
	 * Resolve a dependency through IoC
74
	 *
75
	 * @param  string      $key
76
	 * @param  string|null $subcontainer Subcontainer to look into
77
	 * @return mixed
78
	 */
79 View Code Duplication
	public static function resolve( $key, $subcontainer = null ) {
80
		$ioc = static::instance()->ioc;
81
		if ( $subcontainer !== null ) {
82
			if ( ! isset( $ioc[ $subcontainer ] ) ) {
83
				return null;
84
			}
85
			$ioc = $ioc[ $subcontainer ];
86
		}
87
		return $ioc[ $key ];
88
	}
89
90
	/**
91
	 * Resolve a dependency through IoC with arguments
92
	 *
93
	 * @param  string $identifier   Key to resolve from the container
94
	 * @param  array  $arguments    Key-value array of arguments
95
	 * @param  string $subcontainer The container to resolve from
96
	 * @return mixed
97
	 */
98
	public static function resolve_with_arguments( $identifier, $arguments, $subcontainer = null ) {
99
		$local_container = $subcontainer ? static::resolve( $subcontainer ) : static::instance()->ioc;
100
		$container = new PimpleContainer();
101
		$container['root_container'] = static::instance()->ioc;
102
		$container['local_container'] = $local_container;
103
		$container['arguments'] = $arguments;
104
		$container['object'] = $local_container->raw( $identifier );
105
		return $container['object'];
106
	}
107
108
	/**
109
	 * Resolve a service through IoC
110
	 *
111
	 * @param string $service_name
112
	 * @return mixed
113
	 */
114
	public static function service( $service_name ) {
115
		return static::resolve( $service_name, 'services' );
116
	}
117
118
	/**
119
	 * Check if a dependency is registered
120
	 *
121
	 * @param  string      $key
122
	 * @param  string|null $subcontainer Subcontainer to look into
123
	 * @return bool
124
	 */
125 View Code Duplication
	public static function has( $key, $subcontainer = null ) {
126
		$ioc = static::instance()->ioc;
127
		if ( $subcontainer !== null ) {
128
			if ( ! isset( $ioc[ $subcontainer ] ) ) {
129
				return false;
130
			}
131
			$ioc = $ioc[ $subcontainer ];
132
		}
133
		return isset( $ioc[ $key ] );
134
	}
135
136
	/**
137
	 * Extend Carbon Fields by adding a new entity (container condition etc.)
138
	 *
139
	 * @param string $class    Extension class name
140
	 * @param string $extender Extending callable
141
	 */
142
	public static function extend( $class, $extender ) {
143
		$type_dictionary = array(
144
			'_Container' => 'containers',
145
			'_Field' => 'fields',
146
			'_Condition' => 'container_conditions',
147
		);
148
149
		$extension_suffix = '';
150
		$extension_subcontainer = '';
151
		foreach ( $type_dictionary as $suffix => $subcontainer ) {
152
			if ( substr( $class, -strlen( $suffix ) ) === $suffix ) {
153
				$extension_suffix = $suffix;
154
				$extension_subcontainer = $subcontainer;
155
			}
156
		}
157
158
		if ( empty( $extension_suffix ) ) {
159
			Incorrect_Syntax_Exception::raise( 'Could not determine "' . $class . '" extension type. Extension classes must have one of the following suffixes: ' . implode( ', ', array_keys( $type_dictionary ) ) );
160
			return;
161
		}
162
163
		$identifier = Helper::class_to_type( $class, $extension_suffix );
164
		$ioc = static::instance()->ioc[ $extension_subcontainer ];
165
		$ioc[ $identifier ] = $ioc->factory( $extender );
166
	}
167
168
	/**
169
	 * Replace the ioc container for Carbon_Fields\Carbon_Fields
170
	 *
171
	 * @param  PimpleContainer $ioc
172
	 */
173
	public function install( PimpleContainer $ioc ) {
174
		$this->ioc = $ioc;
175
	}
176
177
	/**
178
	 * Boot Carbon Fields with default IoC dependencies
179
	 */
180
	public static function boot() {
181
		if ( static::is_booted() ) {
182
			return;
183
		}
184
185
		if ( defined( __NAMESPACE__ . '\VERSION' ) ) {
186
			return; // Possibly attempting to load multiple versions of Carbon Fields; bail in favor of already loaded version
187
		}
188
189
		static::resolve( 'loader' )->boot();
190
		static::instance()->booted = true;
191
		static::instance()->get_emitter()->emit( 'loaded' );
192
		do_action( 'carbon_fields_loaded' );
193
	}
194
195
	/**
196
	 * Check if Carbon Fields has booted
197
	 */
198
	public static function is_booted() {
199
		return static::instance()->booted;
200
	}
201
202
	/**
203
	 * Throw exception if Carbon Fields has not been booted
204
	 */
205
	public static function verify_boot() {
206
		if ( ! static::is_booted() ) {
207
			throw new \Exception( 'You must call Carbon_Fields\Carbon_Fields::boot() in a suitable WordPress hook before using Carbon Fields.' );
208
		}
209
	}
210
211
	/**
212
	 * Throw exception if fields have not been registered yet
213
	 */
214
	public static function verify_fields_registered() {
215
		$register_action = 'carbon_fields_register_fields';
216
		$registered_action = 'carbon_fields_fields_registered';
217
		if ( ! doing_action( $register_action ) && ! doing_action( $registered_action ) && did_action( $registered_action ) === 0 ) {
218
			Incorrect_Syntax_Exception::raise( 'Attempted to access a field before the ' . $register_action . ' and ' . $registered_action . ' actions have fired yet.' );
219
		}
220
	}
221
222
	/**
223
	 * Resolve the public url of a directory inside WordPress
224
	 *
225
	 * @param  string $directory
226
	 * @return string
227
	 */
228
	public static function directory_to_url( $directory ) {
229
		$url = \trailingslashit( $directory );
230
		$count = 0;
231
232
		# Sanitize directory separator on Windows
233
		$url = str_replace( '\\' ,'/', $url );
234
235
		$possible_locations = array(
236
			WP_PLUGIN_DIR => \plugins_url(), # If installed as a plugin
237
			WP_CONTENT_DIR => \content_url(), # If anywhere in wp-content
238
			ABSPATH => \site_url( '/' ), # If anywhere else within the WordPress installation
239
		);
240
241
		foreach ( $possible_locations as $test_dir => $test_url ) {
242
			$test_dir_normalized = str_replace( '\\' ,'/', $test_dir );
243
			$url = str_replace( $test_dir_normalized, $test_url, $url, $count );
244
245
			if ( $count > 0 ) {
246
				return \untrailingslashit( $url );
247
			}
248
		}
249
250
		return ''; // return empty string to avoid exposing half-parsed paths
251
	}
252
253
	/**
254
	 * Get the event emitter
255
	 *
256
	 * @return Emitter
257
	 */
258
	public function get_emitter() {
259
		if ( $this->emitter === null ) {
260
			$this->emitter = static::resolve( 'event_emitter' );
261
		}
262
		return $this->emitter;
263
	}
264
265
	/**
266
	 * Add a listener to an event
267
	 *
268
	 * @param string   $event
269
	 * @param Event\Listener $listener
270
	 * @return Event\Listener $listener
271
	 */
272
	public static function add_listener( $event, $listener ) {
273
		return static::instance()->get_emitter()->add_listener( $event, $listener );
274
	}
275
276
	/**
277
	 * Remove a listener from any event
278
	 *
279
	 * @param Event\Listener $listener
280
	 */
281
	public static function remove_listener( $listener ) {
282
		static::instance()->get_emitter()->remove_listener( $listener );
283
	}
284
285
	/**
286
	 * Add a persistent listener to an event
287
	 *
288
	 * @param  string   $event    The event to listen for
289
	 * @param  string   $callable The callable to call when the event is broadcasted
290
	 * @return Event\Listener
291
	 */
292
	public static function on( $event, $callable ) {
293
		return static::instance()->get_emitter()->on( $event, $callable );
294
	}
295
296
	/**
297
	 * Add a one-time listener to an event
298
	 *
299
	 * @param  string   $event    The event to listen for
300
	 * @param  string   $callable The callable to call when the event is broadcasted
301
	 * @return Event\Listener
302
	 */
303
	public static function once( $event, $callable ) {
304
		return static::instance()->get_emitter()->once( $event, $callable );
305
	}
306
307
	/**
308
	 * Get default IoC container dependencies
309
	 *
310
	 * @return PimpleContainer
311
	 */
312
	protected static function get_default_ioc() {
313
		$ioc = new PimpleContainer();
314
315
		$ioc['loader'] = function( $ioc ) {
316
			return new Loader( $ioc['sidebar_manager'], $ioc['container_repository'] );
317
		};
318
319
		$ioc['container_repository'] = function() {
320
			return new ContainerRepository();
321
		};
322
323
		$ioc['containers'] = function() {
324
			return new PimpleContainer();
325
		};
326
327
		$ioc['fields'] = function() {
328
			return new PimpleContainer();
329
		};
330
331
		$ioc['key_toolset'] = function() {
332
			return new Key_Toolset();
333
		};
334
335
		$ioc['wp_toolset'] = function() {
336
			return new WP_Toolset();
337
		};
338
339
		$ioc['sidebar_manager'] = function() {
340
			return new Sidebar_Manager();
341
		};
342
343
		$ioc['rest_api_router'] = function( $ioc ) {
344
			return new REST_API_Router( $ioc['container_repository'] );
345
		};
346
347
		$ioc['rest_api_decorator'] = function( $ioc ) {
348
			return new REST_API_Decorator( $ioc['container_repository'] );
349
		};
350
351
		/* Services */
352
		$ioc['services'] = function() {
353
			return new PimpleContainer();
354
		};
355
356
		$ioc['services']['revisions'] = function() use ( $ioc ) {
357
			return new Revisions_Service();
358
		};
359
360
		$ioc['services']['meta_query'] = function() use ( $ioc ) {
361
			return new Meta_Query_Service( $ioc['container_repository'], $ioc['key_toolset'] );
362
		};
363
364
		$ioc['services']['legacy_storage'] = function() use ( $ioc ) {
365
			return new Legacy_Storage_Service_v_1_5( $ioc['container_repository'], $ioc['key_toolset'] );
366
		};
367
368
		$ioc['services']['rest_api'] = function() use ( $ioc ) {
369
			return new REST_API_Service( $ioc['rest_api_router'], $ioc['rest_api_decorator'] );
370
		};
371
372
		/* Events */
373
		$ioc['event_emitter'] = function() {
374
			return new Emitter();
375
		};
376
377
		$ioc['event_persistent_listener'] = function() {
378
			return new PersistentListener();
379
		};
380
381
		$ioc['event_single_event_listener'] = function() {
382
			return new SingleEventListener();
383
		};
384
385
		$ioc->register( new \Carbon_Fields\Provider\Container_Condition_Provider() );
386
387
		return $ioc;
388
	}
389
}
390