Passed
Push — master ( cf6756...3ddcea )
by Glynn
02:03 queued 12s
created

Enqueue::for_block()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * A chainable helper class for enqueuing scripts and styles.
7
 *
8
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
 *
20
 * @author Glynn Quelch <[email protected]>
21
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
22
 * @package PinkCrab\Enqueue
23
 */
24
25
namespace PinkCrab\Enqueue;
26
27
/**
28
 * WordPress Script and Style enqueuing class.
29
 *
30
 * @version 1.1.0
31
 * @author Glynn Quelch <[email protected]>
32
 */
33
class Enqueue {
34
35
	/**
36
	 * The handle to enqueue the script or style with.
37
	 * Also used for any locaized variables.
38
	 *
39
	 * @var string
40
	 */
41
	protected $handle;
42
43
	/**
44
	 * The type of file to enqueue.
45
	 *
46
	 * @var string
47
	 */
48
	protected $type;
49
50
	/**
51
	 * The file loaction (URI)
52
	 *
53
	 * @var string
54
	 */
55
	protected $src;
56
57
	/**
58
	 * Dependencies which must be loaded prior.
59
	 *
60
	 * @var array<int, string>
61
	 */
62
	protected $deps = array();
63
64
	/**
65
	 * Version tag for file enqueued
66
	 *
67
	 * @var mixed
68
	 */
69
	protected $ver = false;
70
71
	/**
72
	 * Defines if script should be loaded in footer (true) or header (false)
73
	 *
74
	 * @var boolean
75
	 */
76
	protected $footer = true;
77
78
	/**
79
	 * Values to be localized when script enqueued.
80
	 *
81
	 * @var array<string, mixed>|null
82
	 */
83
	protected $localize = null;
84
85
	/**
86
	 * Defines if script should be parsed inline or enqueued.
87
	 * Please note this should only be used for simple and small JS files.
88
	 *
89
	 * @var boolean
90
	 */
91
	protected $inline = false;
92
93
	/**
94
	 * Style sheet which has been defined.
95
	 * Accepts media types like wp_enqueue_styles.
96
	 *
97
	 * @var string
98
	 */
99
	protected $media = 'all';
100
101
	/**
102
	 * All custom flags and attributes to add to the script and style tags
103
	 *
104
	 * @var array<string, string|null>
105
	 */
106
	protected $attributes = array();
107
108
	/**
109
	 * Denotes if being enqueued for a block.
110
	 *
111
	 * @var bool
112
	 */
113
	protected $for_block = false;
114
115
	/**
116
	 * Creates an Enqueue instance.
117
	 *
118
	 * @param string $handle
119
	 * @param string $type
120
	 */
121
	public function __construct( string $handle, string $type ) {
122
		$this->handle = $handle;
123
		$this->type   = $type;
124
	}
125
126
	/**
127
	 * Creates a static instace of the Enqueue class for a script.
128
	 *
129
	 * @param string $handle
130
	 * @return self
131
	 */
132
	public static function script( string $handle ): self {
133
		return new self( $handle, 'script' );
134
	}
135
136
	/**
137
	 * Creates a static instace of the Enqueue class for a style.
138
	 *
139
	 * @param string $handle
140
	 * @return self
141
	 */
142
	public static function style( string $handle ): self {
143
		return new self( $handle, 'style' );
144
	}
145
146
	/**
147
	 * Defined the SRC of the file.
148
	 *
149
	 * @param string $src
150
	 * @return self
151
	 */
152
	public function src( string $src ): self {
153
		$this->src = $src;
154
		return $this;
155
	}
156
157
	/**
158
	 * Defined the Dependencies of the enqueue.
159
	 *
160
	 * @param string ...$deps
161
	 * @return self
162
	 */
163
	public function deps( string ...$deps ): self {
164
		$this->deps = $deps;
165
		return $this;
166
	}
167
168
	/**
169
	 * Defined the version of the enqueue
170
	 *
171
	 * @param string $ver
172
	 * @return self
173
	 */
174
	public function ver( string $ver ): self {
175
		$this->ver = $ver;
176
		return $this;
177
	}
178
179
	/**
180
	 * Define the media type.
181
	 *
182
	 * @param string $media
183
	 * @return self
184
	 */
185
	public function media( string $media ): self {
186
		$this->media = $media;
187
		return $this;
188
	}
189
190
	/**
191
	 * Sets the version as last modified file time.
192
	 * Doesnt set the version if the fileheader can be read.
193
	 *
194
	 * @return self
195
	 */
196
	public function lastest_version(): self {
197
		if ( $this->does_file_exist( $this->src ) ) {
198
199
			$headers = get_headers( $this->src, 1 );
200
201
			if ( is_array( $headers )
202
			&& array_key_exists( 'Last-Modified', $headers )
203
			) {
204
				$this->ver = strtotime( $headers['Last-Modified'] );
205
			}
206
		}
207
		return $this;
208
	}
209
210
	/**
211
	 * Checks to see if a file exist using URL (not path).
212
	 *
213
	 * @param string $url The URL of the file being checked.
214
	 * @return boolean true if it does, false if it doesnt.
215
	 */
216
	private function does_file_exist( string $url ): bool {
217
		$ch = curl_init( $url );
218
		if ( ! $ch ) {
219
			return false;
220
		}
221
		curl_setopt( $ch, CURLOPT_NOBODY, true );
222
		curl_setopt( $ch, CURLOPT_TIMEOUT_MS, 50 );
223
		curl_exec( $ch );
224
		$http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
225
		curl_close( $ch );
226
		return $http_code === 200;
227
	}
228
229
	/**
230
	 * Should the script be called in the footer.
231
	 *
232
	 * @param boolean $footer
233
	 * @return self
234
	 */
235
	public function footer( bool $footer = true ): self {
236
		$this->footer = $footer;
237
		return $this;
238
	}
239
240
	/**
241
	 * Alias for footerfalse
242
	 *
243
	 * @return self
244
	 */
245
	public function header(): self {
246
		$this->footer = false;
247
		return $this;
248
	}
249
250
	/**
251
	 * Should the script be called in the inline.
252
	 *
253
	 * @param boolean $inline
254
	 * @return self
255
	 */
256
	public function inline( bool $inline = true ):self {
257
		$this->inline = $inline;
258
		return $this;
259
	}
260
261
	/**
262
	 * Pass any key => value pairs to be localised with the enqueue.
263
	 *
264
	 * @param array<string, mixed> $args
265
	 * @return self
266
	 */
267
	public function localize( array $args ): self {
268
		$this->localize = $args;
269
		return $this;
270
	}
271
272
	/**
273
	 * Adds a Flag (attribute with no value) to a script/style tag
274
	 *
275
	 * @param string $flag
276
	 * @return self
277
	 */
278
	public function flag( string $flag ): self {
279
		$this->attributes[ $flag ] = null;
280
		return $this;
281
	}
282
283
	/**
284
	 * Adds an attribute tto a script/style tag
285
	 *
286
	 * @param string $key
287
	 * @param string $value
288
	 * @return self
289
	 */
290
	public function attribute( string $key, string $value ): self {
291
		$this->attributes[ $key ] = $value;
292
		return $this;
293
	}
294
295
	/**
296
	 * Marks the script or style as deferred loaded.
297
	 *
298
	 * @return self
299
	 */
300
	public function defer(): self {
301
		// Remove ASYNC if set.
302
		if ( \array_key_exists( 'async', $this->attributes ) ) {
303
			unset( $this->attributes['async'] );
304
		}
305
306
		$this->attributes['defer'] = '';
307
		return $this;
308
	}
309
310
	/**
311
	 * Marks the script or style as async loaded.
312
	 *
313
	 * @return self
314
	 */
315
	public function async(): self {
316
		// Remove DEFER if set.
317
		if ( \array_key_exists( 'defer', $this->attributes ) ) {
318
			unset( $this->attributes['defer'] );
319
		}
320
321
		$this->attributes['async'] = '';
322
		return $this;
323
	}
324
325
	/**
326
	 * Set if being enqueued for a block.
327
	 *
328
	 * @param bool $for_block Denotes if being enqueued for a block.
329
	 * @return self
330
	 */
331
	public function for_block( bool $for_block = true ) : self {
332
		$this->for_block = $for_block;
333
		return $this;
334
	}
335
336
	/**
337
	 * Registers the file as either enqueued or inline parsed.
338
	 *
339
	 * @return void
340
	 */
341
	public function register(): void {
342
		if ( $this->type === 'script' ) {
343
			$this->register_script();
344
		}
345
346
		if ( $this->type === 'style' ) {
347
			$this->register_style();
348
		}
349
	}
350
351
	/**
352
	 * Regsiters the style.
353
	 *
354
	 * @return void
355
	 */
356
	private function register_style() {
357
358
		\wp_register_style(
359
			$this->handle,
360
			$this->src,
361
			$this->deps,
362
			$this->ver,
363
			$this->media
364
		);
365
		if ( false === $this->for_block ) {
366
			wp_enqueue_style( $this->handle );
367
		}
368
369
		$this->add_style_attributes();
370
371
	}
372
373
	/**
374
	 * Registers and enqueues or inlines the script, with any passed localised data.
375
	 *
376
	 * @return void
377
	 */
378
	private function register_script() {
379
380
		\wp_register_script(
381
			$this->handle,
382
			$this->inline === true ? '' : $this->src,
383
			$this->deps,
384
			$this->ver,
385
			$this->footer
386
		);
387
388
		// Maybe add as an inline script.
389
		if ( $this->inline && $this->does_file_exist( $this->src ) ) {
390
			\wp_add_inline_script( $this->handle, file_get_contents( $this->src ) ?: '' );
391
		}
392
393
		// Localize all values if defined.
394
		if ( ! empty( $this->localize ) ) {
395
			\wp_localize_script( $this->handle, $this->handle, $this->localize );
396
		}
397
398
		// Enqueue file if not used for a block.
399
		if ( false === $this->for_block ) {
400
			\wp_enqueue_script( $this->handle );
401
		}
402
403
		$this->add_script_attributes();
404
	}
405
406
	/**
407
	 * Adds any additional attributes to a script.
408
	 *
409
	 * @return void
410
	 */
411
	private function add_script_attributes(): void {
412
413
		$attributes = $this->get_attributes();
414
415
		// Bail if we have no attributes.
416
		if ( 0 === count( $attributes ) ) {
417
			return;
418
		}
419
420
		// Add to any scripts.
421
		add_filter(
422
			'script_loader_tag',
423
			function( string $tag, string $handle, string $source ) use ( $attributes ): string {
424
				// Bail if not our script.
425
				if ( $this->handle !== $handle ) {
426
					return $tag;
427
				}
428
				return sprintf( '<script type="text/javascript" src="%s" %s></script>', $source, join( ' ', $attributes ) ); //phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
429
			},
430
			1,
431
			3
432
		);
433
	}
434
435
	/**
436
	 * Adds any additional attributes to a style.
437
	 *
438
	 * @return void
439
	 */
440
	private function add_style_attributes(): void {
441
442
		$attributes = $this->get_attributes();
443
444
		// Bail if we have no attributes.
445
		if ( 0 === count( $attributes ) ) {
446
			return;
447
		}
448
449
		// Add to any relevant styles.
450
		add_filter(
451
			'style_loader_tag',
452
			function( string $tag, string $handle, string $href, string $media ) use ( $attributes ): string {
453
				// Bail if not our script.
454
				if ( $this->handle !== $handle ) {
455
					return $tag;
456
				}
457
				return sprintf(
458
					'<link rel="stylesheet" id="%s-css" href="%s" type="text/css" media="%s" %s>', //phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet
459
					$handle,
460
					$href,
461
					$media,
462
					join( ' ', $attributes )
463
				);
464
			},
465
			1,
466
			4
467
		);
468
	}
469
470
	/**
471
	 * Gets all attributes mapped as HTML attributes.
472
	 *
473
	 * @return string[]
474
	 */
475
	private function get_attributes():array {
476
		return array_map(
477
			function( string $key, ?string $value ): string {
478
				return null === $value
479
					? "{$key}"
480
					: "{$key}='{$value}'";
481
			},
482
			array_keys( $this->attributes ),
483
			$this->attributes
484
		);
485
	}
486
487
488
}
489