Passed
Push — master ( 078687...6fac54 )
by Alain
02:20
created

DependencyManager::enqueue_internal_handle()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

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