Test Failed
Push — master ( 35fc11...b51ba3 )
by Alain
02:50
created

DependencyManager::get_default_handler()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 0
cts 5
cp 0
rs 9.9332
c 0
b 0
f 0
cc 3
nc 3
nop 1
crap 12
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
		if ( ! $this->enqueue_internal_handle( $handle, $context ) ) {
234
			return $this->enqueue_fallback_handle( $handle );
235
		}
236
		return true;
237
	}
238
239
	/**
240
	 * Enqueue a single dependency from the internal dependencies, retrieved by
241
	 * its handle.
242
	 *
243
	 * @since 0.2.4
244
	 *
245
	 * @param string $handle   The dependency handle to enqueue.
246
	 * @param mixed  $context  Optional. The context to pass to the
247
	 *                         dependencies.
248
	 * @return bool Returns whether the handle was found or not.
249
	 */
250
	protected function enqueue_internal_handle( $handle, $context = null ) {
251
		list( $dependency_type, $dependency ) = $this->get_dependency_array( $handle );
252
		$context['dependency_type'] = $dependency_type;
253
254
		if ( ! $dependency ) {
255
			return false;
256
		}
257
258
		$this->enqueue_dependency(
259
			$dependency,
260
			$handle,
261
			$context
262
		);
263
264
		$this->maybe_localize( $dependency, $context );
265
		$this->maybe_add_inline_script( $dependency, $context );
266
267
		return true;
268
	}
269
270
	/**
271
	 * Get the matching dependency for a given handle.
272
	 *
273
	 * @since 0.2.2
274
	 *
275
	 * @param string $handle The dependency handle to search for.
276
	 * @return array Array containing the dependency key as well as the
277
	 *                       dependency array itself.
278
	 */
279
	protected function get_dependency_array( $handle ) {
280
		foreach ( $this->dependencies as $type => $dependencies ) {
281
			if ( array_key_exists( $handle, $dependencies ) ) {
282
				return [ $type, $dependencies[ $handle ] ];
283
			}
284
		}
285
		// Handle not found, return an empty array.
286
		return [ '', null ];
287
	}
288
289
	/**
290
	 * Enqueue a single dependency.
291
	 *
292
	 * @since 0.1.0
293
	 *
294
	 * @param array  $dependency     Configuration data of the dependency.
295
	 * @param string $dependency_key Config key of the dependency.
296
	 * @param mixed  $context        Optional. Context to pass to the
297
	 *                               dependencies. Contains the type of the
298
	 *                               dependency at key
299
	 *                               'dependency_type'.
300
	 */
301
	protected function enqueue_dependency( $dependency, $dependency_key, $context = null ) {
302
		if ( ! $this->is_needed( $dependency, $context ) ) {
303
			return;
304
		}
305
		$handler = $this->handlers[ $context['dependency_type'] ];
306
		$handler->enqueue( $dependency );
307
	}
308
309
	/**
310
	 * Check whether a specific dependency is needed.
311
	 *
312
	 * @since 0.1.0
313
	 *
314
	 * @param array $dependency Configuration of the dependency to check.
315
	 * @param mixed $context    Context to pass to the dependencies.
316
	 *                          Contains the type of the dependency at key
317
	 *                          'dependency_type'.
318
	 * @return bool Whether it is needed or not.
319
	 */
320
	protected function is_needed( $dependency, $context ) {
321
		$is_needed = array_key_exists( 'is_needed', $dependency )
322
			? $dependency['is_needed']
323
			: null;
324
325
		if ( null === $is_needed ) {
326
			return true;
327
		}
328
329
		return is_callable( $is_needed ) && $is_needed( $context );
330
	}
331
332
	/**
333
	 * Localize the script of a given dependency.
334
	 *
335
	 * @since 0.1.0
336
	 *
337
	 * @param array $dependency The dependency to localize the script of.
338
	 * @param mixed $context    Contextual data to pass to the callback.
339
	 *                          Contains the type of the dependency at key
340
	 *                          'dependency_type'.
341
	 */
342
	protected function maybe_localize( $dependency, $context ) {
343
		if ( ! array_key_exists( 'localize', $dependency ) ) {
344
			return;
345
		}
346
347
		$localize = $dependency['localize'];
348
		$data     = $localize['data'];
349
		if ( is_callable( $data ) ) {
350
			$data = $data( $context );
351
		}
352
353
		wp_localize_script( $dependency['handle'], $localize['name'], $data );
354
	}
355
356
	/**
357
	 * Add an inline script snippet to a given dependency.
358
	 *
359
	 * @since 0.1.0
360
	 *
361
	 * @param array $dependency The dependency to add the inline script to.
362
	 * @param mixed $context    Contextual data to pass to the callback.
363
	 *                          Contains the type of the dependency at key
364
	 *                          'dependency_type'.
365
	 */
366
	protected function maybe_add_inline_script( $dependency, $context ) {
367
		if ( ! array_key_exists( 'add_inline', $dependency ) ) {
368
			return;
369
		}
370
371
		$inline_script = $dependency['add_inline'];
372
373
		if ( is_callable( $inline_script ) ) {
374
			$inline_script = $inline_script( $context );
375
		}
376
377
		wp_add_inline_script( $dependency['handle'], $inline_script );
378
	}
379
380
	/**
381
	 * Enqueue a single dependency from the WP-registered dependencies,
382
	 * retrieved by its handle.
383
	 *
384
	 * @since 0.2.4
385
	 *
386
	 * @param string $handle The dependency handle to enqueue.
387
	 * @return bool Returns whether the handle was found or not.
388
	 */
389
	protected function enqueue_fallback_handle( $handle ) {
390
		$result = false;
391
		foreach ( $this->handlers as $handler ) {
392
			$result = $result || $handler->maybe_enqueue( $handle );
393
		}
394
		return $result;
395
	}
396
397
	/**
398
	 * Enqueue all dependencies of a specific type.
399
	 *
400
	 * @since 0.1.0
401
	 *
402
	 * @param array  $dependencies    The dependencies to enqueue.
403
	 * @param string $dependency_type The type of the dependencies.
404
	 * @param mixed  $context         Optional. The context to pass to the
405
	 *                                dependencies.
406
	 */
407
	protected function enqueue_dependency_type( $dependencies, $dependency_type, $context = null ) {
408
		$context['dependency_type'] = $dependency_type;
409
		array_walk( $dependencies, [ $this, 'enqueue_dependency' ], $context );
410
	}
411
412
	/**
413
	 * Register all dependencies of a specific type.
414
	 *
415
	 * @since 0.1.0
416
	 *
417
	 * @param array  $dependencies    The dependencies to register.
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 register_dependency_type( $dependencies, $dependency_type, $context = null ) {
423
		$context['dependency_type'] = $dependency_type;
424
		array_walk( $dependencies, [ $this, 'register_dependency' ], $context );
425
	}
426
427
	/**
428
	 * Register a single dependency.
429
	 *
430
	 * @since 0.1.0
431
	 *
432
	 * @param array  $dependency     Configuration data of the dependency.
433
	 * @param string $dependency_key Config key of the dependency.
434
	 * @param mixed  $context        Optional. Context to pass to the
435
	 *                               dependencies. Contains the type of the
436
	 *                               dependency at key
437
	 *                               'dependency_type'.
438
	 */
439
	protected function register_dependency( $dependency, $dependency_key, $context = null ) {
440
		$handler = $this->handlers[ $context['dependency_type'] ];
441
		$handler->register( $dependency );
442
443
		if ( $this->enqueue_immediately ) {
444
			$this->register_enqueue_hooks( $dependency, $context );
445
		}
446
	}
447
448
	/**
449
	 * Register the enqueueing to WordPress hooks.
450
	 *
451
	 * @since 0.2.2
452
	 *
453
	 * @param array $dependency Configuration data of the dependency.
454
	 * @param mixed $context    Optional. Context to pass to the dependencies.
455
	 *                          Contains the type of the dependency at key
456
	 *                          'dependency_type'.
457
	 */
458
	protected function register_enqueue_hooks( $dependency, $context = null ) {
459
		$priority = $this->get_priority( $dependency );
460
461
		foreach ( [ 'wp_enqueue_scripts', 'admin_enqueue_scripts' ] as $hook ) {
462
			add_action( $hook, [ $this, 'enqueue' ], $priority, 1 );
463
		}
464
465
		$this->maybe_localize( $dependency, $context );
466
		$this->maybe_add_inline_script( $dependency, $context );
467
	}
468
469
	/**
470
	 * Get the priority of a dependency.
471
	 *
472
	 * @since 0.2.2
473
	 *
474
	 * @param array $dependency Configuration data of the dependency.
475
	 * @return int Priority to use.
476
	 */
477
	protected function get_priority( $dependency ) {
478
		if ( array_key_exists( 'priority', $dependency ) ) {
479
			return intval( $dependency['priority'] );
480
		}
481
		return 10;
482
	}
483
}
484