Test Failed
Push — master ( 2c95af...0de91d )
by Alain
04:14
created

DependencyManager   C

Complexity

Total Complexity 55

Size/Duplication

Total Lines 470
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
dl 0
loc 470
c 0
b 0
f 0
wmc 55
lcom 1
cbo 2
ccs 0
cts 121
cp 0
rs 6

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A init_handlers() 0 8 3
A add_handler() 0 10 4
A get_default_handler() 0 10 3
A init_dependencies() 0 8 2
A init_dependency_type() 0 10 3
A register() 0 5 1
A validate_context() 0 6 2
A enqueue() 0 6 1
A enqueue_handle() 0 11 3
A enqueue_internal_handle() 0 19 2
A get_dependency_array() 0 9 3
A enqueue_dependency() 0 14 5
A is_needed() 0 11 4
A maybe_localize() 0 16 4
A maybe_add_inline_script() 0 13 3
A enqueue_fallback_handle() 0 7 3
A enqueue_dependency_type() 0 4 1
A register_dependency_type() 0 4 1
A register_dependency() 0 8 2
A register_enqueue_hooks() 0 10 2
A get_priority() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like DependencyManager often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DependencyManager, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Bright Nucleus Dependency Component.
4
 *
5
 * @package   BrightNucleus\Dependency
6
 * @author    Alain Schlesser <[email protected]>
7
 * @license   MIT
8
 * @link      http://www.brightnucleus.com/
9
 * @copyright 2015-2016 Alain Schlesser, Bright Nucleus
10
 */
11
12
namespace BrightNucleus\Dependency;
13
14
use BrightNucleus\Config\ConfigInterface;
15
use BrightNucleus\Config\ConfigTrait;
16
use BrightNucleus\Exception\InvalidArgumentException;
17
use BrightNucleus\Exception\RuntimeException;
18
19
/**
20
 * Class DependencyManager.
21
 *
22
 * Register and enqueue dependencies that are listed in the config file.
23
 *
24
 * @since   0.1.0
25
 *
26
 * @package BrightNucleus\Dependency
27
 * @author  Alain Schlesser <[email protected]>
28
 */
29
class DependencyManager implements DependencyManagerInterface {
30
31
	use ConfigTrait;
32
33
	/*
34
	 * Default dependency handler implementations.
35
	 */
36
	const DEFAULT_SCRIPT_HANDLER = '\BrightNucleus\Dependency\ScriptHandler';
37
	const DEFAULT_STYLE_HANDLER  = '\BrightNucleus\Dependency\StyleHandler';
38
39
	/*
40
	 * Names of the configuration keys.
41
	 */
42
	const KEY_HANDLERS = 'handlers';
43
	const KEY_SCRIPTS  = 'scripts';
44
	const KEY_STYLES   = 'styles';
45
46
	/**
47
	 * Hold the dependencies, grouped by type.
48
	 *
49
	 * @since 0.1.0
50
	 *
51
	 * @var array;
52
	 */
53
	protected $dependencies = [ ];
54
55
	/**
56
	 * Hold the handlers.
57
	 *
58
	 * @since 0.1.0
59
	 *
60
	 * @var DependencyHandlerInterface[]
61
	 */
62
	protected $handlers = [ ];
63
64
	/**
65
	 * Whether to enqueue immediately upon registration.
66
	 *
67
	 * @since 0.2.2
68
	 *
69
	 * @var bool
70
	 */
71
	protected $enqueue_immediately;
72
73
	/**
74
	 * Instantiate DependencyManager object.
75
	 *
76
	 * @since 0.1.0
77
	 *
78
	 * @param ConfigInterface $config   ConfigInterface object that contains
79
	 *                                  dependency settings.
80
	 * @param bool            $enqueue  Optional. Whether to enqueue
81
	 *                                  immediately. Defaults to true.
82
	 * @throws RuntimeException If the config could not be processed.
83
	 * @throws InvalidArgumentException If no dependency handlers were
84
	 *                                  specified.
85
	 */
86
	public function __construct( ConfigInterface $config, $enqueue = true ) {
87
		$this->processConfig( $config );
88
		$this->enqueue_immediately = $enqueue;
89
		$this->init_handlers();
90
		$this->init_dependencies();
91
	}
92
93
	/**
94
	 * Initialize the dependency handlers.
95
	 *
96
	 * @since 0.1.0
97
	 */
98
	protected function init_handlers() {
99
		$keys = [ self::KEY_SCRIPTS, self::KEY_STYLES ];
100
		foreach ( $keys as $key ) {
101
			if ( $this->hasConfigKey( $key ) ) {
102
				$this->add_handler( $key );
103
			}
104
		}
105
	}
106
107
	/**
108
	 * Add a single dependency handler.
109
	 *
110
	 * @since 0.1.0
111
	 *
112
	 * @param string $dependency The dependency type for which to add a handler.
113
	 */
114
	protected function add_handler( $dependency ) {
115
		if ( $this->hasConfigKey( $dependency ) ) {
116
			$handler = $this->hasConfigKey( self::KEY_HANDLERS, $dependency )
117
				? $this->getConfigKey( self::KEY_HANDLERS, $dependency )
118
				: $this->get_default_handler( $dependency );
119
			if ( $handler ) {
120
				$this->handlers[ $dependency ] = new $handler;
121
			}
122
		}
123
	}
124
125
	/**
126
	 * Get the default handler class for a given type of dependency.
127
	 *
128
	 * @since 0.1.0
129
	 *
130
	 * @param string $dependency The dependency that needs a handler.
131
	 * @return string|null Class name of the handler. Null if none.
132
	 */
133
	protected function get_default_handler( $dependency ) {
134
		switch ( $dependency ) {
135
			case self::KEY_STYLES:
136
				return self::DEFAULT_STYLE_HANDLER;
137
			case self::KEY_SCRIPTS:
138
				return self::DEFAULT_SCRIPT_HANDLER;
139
			default:
140
				return null;
141
		}
142
	}
143
144
	/**
145
	 * Initialize the actual dependencies.
146
	 *
147
	 * @since 0.1.0
148
	 */
149
	protected function init_dependencies() {
150
		array_walk( $this->handlers,
151
			function ( $handler, $dependency_type ) {
152
				if ( $this->hasConfigKey( $dependency_type ) ) {
153
					$this->dependencies[ $dependency_type ] = $this->init_dependency_type( $dependency_type );
154
				}
155
			} );
156
	}
157
158
	/**
159
	 * Initialize the dependencies of a given type.
160
	 *
161
	 * @since 0.2.2
162
	 *
163
	 * @param string $type The type of dependency to initialize.
164
	 * @return array Array of dependency configurations.
165
	 */
166
	protected function init_dependency_type( $type ) {
167
		$array = [ ];
168
		$data  = $this->getConfigKey( $type );
169
		foreach ( $data as $dependency ) {
170
			$handle           = array_key_exists( 'handle',
171
				$dependency ) ? $dependency['handle'] : '';
172
			$array[ $handle ] = $dependency;
173
		}
174
		return $array;
175
	}
176
177
	/**
178
	 * Register all dependencies.
179
	 *
180
	 * @since 0.1.0
181
	 *
182
	 * @param mixed $context Optional. The context to pass to the dependencies.
183
	 */
184
	public function register( $context = null ) {
185
		$context = $this->validate_context( $context );
186
		array_walk( $this->dependencies,
187
			[ $this, 'register_dependency_type' ], $context );
188
	}
189
190
	/**
191
	 * Validate the context to make sure it is an array.
192
	 *
193
	 * @since 0.2.1
194
	 *
195
	 * @param mixed $context The context as passed in by WordPress.
196
	 * @return array Validated context.
197
	 */
198
	protected function validate_context( $context ) {
199
		if ( is_string( $context ) ) {
200
			return [ 'wp_context' => $context ];
201
		}
202
		return (array) $context;
203
	}
204
205
	/**
206
	 * Enqueue all dependencies.
207
	 *
208
	 * @since 0.1.0
209
	 *
210
	 * @param mixed $context  Optional. The context to pass to the
211
	 *                        dependencies.
212
	 */
213
	public function enqueue( $context = null ) {
214
		$context = $this->validate_context( $context );
215
216
		array_walk( $this->dependencies,
217
			[ $this, 'enqueue_dependency_type' ], $context );
218
	}
219
220
	/**
221
	 * Enqueue a single dependency retrieved by its handle.
222
	 *
223
	 * @since 0.2.2
224
	 *
225
	 * @param string $handle   The dependency handle to enqueue.
226
	 * @param mixed  $context  Optional. The context to pass to the
227
	 *                         dependencies.
228
	 * @param bool   $fallback Whether to fall back to dependencies registered
229
	 *                         outside of DependencyManager. Defaults to false.
230
	 * @return bool Returns whether the handle was found or not.
231
	 */
232
	public function enqueue_handle( $handle, $context = null, $fallback = false ) {
233
		$handler = $this->handlers[ $context['dependency_type'] ];
234
		if ( $handler->is_enqueued( $handle ) ) {
235
			return true;
236
		}
237
238
		if ( ! $this->enqueue_internal_handle( $handle, $context ) ) {
239
			return $this->enqueue_fallback_handle( $handle );
240
		}
241
		return true;
242
	}
243
244
	/**
245
	 * Enqueue a single dependency from the internal dependencies, retrieved by
246
	 * its handle.
247
	 *
248
	 * @since 0.2.4
249
	 *
250
	 * @param string $handle   The dependency handle to enqueue.
251
	 * @param mixed  $context  Optional. The context to pass to the
252
	 *                         dependencies.
253
	 * @return bool Returns whether the handle was found or not.
254
	 */
255
	protected function enqueue_internal_handle( $handle, $context = null ) {
256
		list( $dependency_type, $dependency ) = $this->get_dependency_array( $handle );
257
		$context['dependency_type'] = $dependency_type;
258
259
		if ( ! $dependency ) {
260
			return false;
261
		}
262
263
		$this->enqueue_dependency(
264
			$dependency,
265
			$handle,
266
			$context
267
		);
268
269
		$this->maybe_localize( $dependency, $context );
270
		$this->maybe_add_inline_script( $dependency, $context );
271
272
		return true;
273
	}
274
275
	/**
276
	 * Get the matching dependency for a given handle.
277
	 *
278
	 * @since 0.2.2
279
	 *
280
	 * @param string $handle The dependency handle to search for.
281
	 * @return array Array containing the dependency key as well as the
282
	 *                       dependency array itself.
283
	 */
284
	protected function get_dependency_array( $handle ) {
285
		foreach ( $this->dependencies as $type => $dependencies ) {
286
			if ( array_key_exists( $handle, $dependencies ) ) {
287
				return [ $type, $dependencies[ $handle ] ];
288
			}
289
		}
290
		// Handle not found, return an empty array.
291
		return [ '', null ];
292
	}
293
294
	/**
295
	 * Enqueue a single dependency.
296
	 *
297
	 * @since 0.1.0
298
	 *
299
	 * @param array  $dependency     Configuration data of the dependency.
300
	 * @param string $dependency_key Config key of the dependency.
301
	 * @param mixed  $context        Optional. Context to pass to the
302
	 *                               dependencies. Contains the type of the
303
	 *                               dependency at key
304
	 *                               'dependency_type'.
305
	 */
306
	protected function enqueue_dependency( $dependency, $dependency_key, $context = null ) {
307
		$handler = $this->handlers[ $context['dependency_type'] ];
308
		$handle  = array_key_exists( 'handle', $dependency ) ? $dependency['handle'] : '';
309
310
		if ( $handle && $handler->is_enqueued( $handle ) ) {
311
			return;
312
		}
313
314
		if ( ! $this->is_needed( $dependency, $context ) ) {
315
			return;
316
		}
317
318
		$handler->enqueue( $dependency );
319
	}
320
321
	/**
322
	 * Check whether a specific dependency is needed.
323
	 *
324
	 * @since 0.1.0
325
	 *
326
	 * @param array $dependency Configuration of the dependency to check.
327
	 * @param mixed $context    Context to pass to the dependencies.
328
	 *                          Contains the type of the dependency at key
329
	 *                          'dependency_type'.
330
	 * @return bool Whether it is needed or not.
331
	 */
332
	protected function is_needed( $dependency, $context ) {
333
		$is_needed = array_key_exists( 'is_needed', $dependency )
334
			? $dependency['is_needed']
335
			: null;
336
337
		if ( null === $is_needed ) {
338
			return true;
339
		}
340
341
		return is_callable( $is_needed ) && $is_needed( $context );
342
	}
343
344
	/**
345
	 * Localize the script of a given dependency.
346
	 *
347
	 * @since 0.1.0
348
	 *
349
	 * @param array $dependency The dependency to localize the script of.
350
	 * @param mixed $context    Contextual data to pass to the callback.
351
	 *                          Contains the type of the dependency at key
352
	 *                          'dependency_type'.
353
	 */
354
	protected function maybe_localize( $dependency, $context ) {
355
		static $already_localized = [];
356
		if ( ! array_key_exists( 'localize', $dependency )
357
		     || array_key_exists( $dependency['handle'], $already_localized ) ) {
358
			return;
359
		}
360
361
		$localize = $dependency['localize'];
362
		$data     = $localize['data'];
363
		if ( is_callable( $data ) ) {
364
			$data = $data( $context );
365
		}
366
367
		wp_localize_script( $dependency['handle'], $localize['name'], $data );
368
		$already_localized[ $dependency['handle'] ] = true;
369
	}
370
371
	/**
372
	 * Add an inline script snippet to a given dependency.
373
	 *
374
	 * @since 0.1.0
375
	 *
376
	 * @param array $dependency The dependency to add the inline script to.
377
	 * @param mixed $context    Contextual data to pass to the callback.
378
	 *                          Contains the type of the dependency at key
379
	 *                          'dependency_type'.
380
	 */
381
	protected function maybe_add_inline_script( $dependency, $context ) {
382
		if ( ! array_key_exists( 'add_inline', $dependency ) ) {
383
			return;
384
		}
385
386
		$inline_script = $dependency['add_inline'];
387
388
		if ( is_callable( $inline_script ) ) {
389
			$inline_script = $inline_script( $context );
390
		}
391
392
		wp_add_inline_script( $dependency['handle'], $inline_script );
393
	}
394
395
	/**
396
	 * Enqueue a single dependency from the WP-registered dependencies,
397
	 * retrieved by its handle.
398
	 *
399
	 * @since 0.2.4
400
	 *
401
	 * @param string $handle The dependency handle to enqueue.
402
	 * @return bool Returns whether the handle was found or not.
403
	 */
404
	protected function enqueue_fallback_handle( $handle ) {
405
		$result = false;
406
		foreach ( $this->handlers as $handler ) {
407
			$result = $result || $handler->maybe_enqueue( $handle );
408
		}
409
		return $result;
410
	}
411
412
	/**
413
	 * Enqueue all dependencies of a specific type.
414
	 *
415
	 * @since 0.1.0
416
	 *
417
	 * @param array  $dependencies    The dependencies to enqueue.
418
	 * @param string $dependency_type The type of the dependencies.
419
	 * @param mixed  $context         Optional. The context to pass to the
420
	 *                                dependencies.
421
	 */
422
	protected function enqueue_dependency_type( $dependencies, $dependency_type, $context = null ) {
423
		$context['dependency_type'] = $dependency_type;
424
		array_walk( $dependencies, [ $this, 'enqueue_dependency' ], $context );
425
	}
426
427
	/**
428
	 * Register all dependencies of a specific type.
429
	 *
430
	 * @since 0.1.0
431
	 *
432
	 * @param array  $dependencies    The dependencies to register.
433
	 * @param string $dependency_type The type of the dependencies.
434
	 * @param mixed  $context         Optional. The context to pass to the
435
	 *                                dependencies.
436
	 */
437
	protected function register_dependency_type( $dependencies, $dependency_type, $context = null ) {
438
		$context['dependency_type'] = $dependency_type;
439
		array_walk( $dependencies, [ $this, 'register_dependency' ], $context );
440
	}
441
442
	/**
443
	 * Register a single dependency.
444
	 *
445
	 * @since 0.1.0
446
	 *
447
	 * @param array  $dependency     Configuration data of the dependency.
448
	 * @param string $dependency_key Config key of the dependency.
449
	 * @param mixed  $context        Optional. Context to pass to the
450
	 *                               dependencies. Contains the type of the
451
	 *                               dependency at key
452
	 *                               'dependency_type'.
453
	 */
454
	protected function register_dependency( $dependency, $dependency_key, $context = null ) {
455
		$handler = $this->handlers[ $context['dependency_type'] ];
456
		$handler->register( $dependency );
457
458
		if ( $this->enqueue_immediately ) {
459
			$this->register_enqueue_hooks( $dependency, $context );
460
		}
461
	}
462
463
	/**
464
	 * Register the enqueueing to WordPress hooks.
465
	 *
466
	 * @since 0.2.2
467
	 *
468
	 * @param array $dependency Configuration data of the dependency.
469
	 * @param mixed $context    Optional. Context to pass to the dependencies.
470
	 *                          Contains the type of the dependency at key
471
	 *                          'dependency_type'.
472
	 */
473
	protected function register_enqueue_hooks( $dependency, $context = null ) {
474
		$priority = $this->get_priority( $dependency );
475
476
		foreach ( [ 'wp_enqueue_scripts', 'admin_enqueue_scripts' ] as $hook ) {
477
			add_action( $hook, [ $this, 'enqueue' ], $priority, 1 );
478
		}
479
480
		$this->maybe_localize( $dependency, $context );
481
		$this->maybe_add_inline_script( $dependency, $context );
482
	}
483
484
	/**
485
	 * Get the priority of a dependency.
486
	 *
487
	 * @since 0.2.2
488
	 *
489
	 * @param array $dependency Configuration data of the dependency.
490
	 * @return int Priority to use.
491
	 */
492
	protected function get_priority( $dependency ) {
493
		if ( array_key_exists( 'priority', $dependency ) ) {
494
			return intval( $dependency['priority'] );
495
		}
496
		return 10;
497
	}
498
}
499