Completed
Push — development ( 5ed9d2...646735 )
by
unknown
04:14
created

Carbon_Fields::resolve()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 10
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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