Completed
Push — add/asset-cdn ( 1281ab...e31931 )
by
unknown
07:52
created

Asset_CDN::flush_concatenated_styles()   D

Complexity

Conditions 10
Paths 21

Size

Total Lines 44
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 26
nc 21
nop 1
dl 0
loc 44
rs 4.8196
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Plugin Name: Asset CDN
4
 * Description: Speed up Javascript and CSS
5
 * Plugin URI: https://github.com/automattic/jetpack
6
 * Author: Automattic
7
 * Author URI: https://automattic.com
8
 * Version: 0.1.0
9
 * Text Domain: asset-cdn
10
 * Domain Path: /languages/
11
 * License: GPLv2 or later
12
 */
13
14
/**
15
 * TODO
0 ignored issues
show
Coding Style introduced by
Comment refers to a TODO task

This check looks TODO comments that have been left in the code.

``TODO``s show that something is left unfinished and should be attended to.

Loading history...
16
 * - versioning (combine ver hashes) and cachebusting
17
 * - concat/minify/serve JS too
18
 * - asset inlining for smaller styles?
19
 * - critical CSS support?
20
 * - non-enqueued assets?
21
 */
22
23
class Asset_CDN {
24
	private static $__instance = null;
25
26
	private $cdn_server;
27
	private $concat_style_groups = array();
28
	private $concat_script_groups = array();
29
	private $inject_critical_css = false;
30
31
	/**
32
	 * Singleton implementation
33
	 *
34
	 * @return object
35
	 */
36
	public static function instance() {
37
		if ( ! is_a( self::$__instance, 'Asset_CDN' ) ) {
38
			self::$__instance = new Asset_CDN();
39
		}
40
41
		return self::$__instance;
42
	}
43
44
	public static function reset() {
45
		self::$__instance = null;
46
	}
47
48
	private function __construct() {
49
		$this->cdn_server = apply_filters( 'jetpack_asset_cdn_url', 'https://cdn.wpvm.io' );
50
		// $this->cdn_server = 'http://localhost:8090';
51
52
		// allow smaller CSS by only minifying assets on the page
53
		add_filter( 'jetpack_implode_frontend_css', '__return_false' );
54
55
		// rewrite CSS tags
56
		add_filter( 'script_loader_tag', array( $this, 'register_concat_scripts' ), -100, 3 );
57
		add_filter( 'style_loader_tag', array( $this, 'register_concat_styles' ), -100, 4 );
58
59
		add_action( 'wp_head', array( $this, 'render_concatenated_styles_head' ), PHP_INT_MAX );
60
		add_action( 'wp_head', array( $this, 'render_concatenated_scripts_head' ), PHP_INT_MAX );
61
		add_action( 'wp_footer', array( $this, 'render_concatenated_styles_footer' ), PHP_INT_MAX );
62
		add_action( 'wp_footer', array( $this, 'render_concatenated_scripts_footer' ), PHP_INT_MAX );
63
	}
64
65
	/**
66
	 * Render functions
67
	 */
68
69
	function render_concatenated_styles_head() {
70
		$this->flush_concatenated_styles(0);
71
	}
72
73
	function render_concatenated_styles_footer() {
74
		$this->flush_concatenated_styles(1);
75
	}
76
77
	private function flush_concatenated_styles( $group ) {
78
		if ( ! isset( $this->concat_style_groups[ $group ] ) ) {
79
			return;
80
		}
81
82
		$style_groups = $this->concat_style_groups[ $group ];
83
84
		if ( empty( $style_groups ) ) {
85
			return;
86
		}
87
88
		// special URL to concatenation service
89
		global $wp_styles;
90
		$site_url = site_url();
91
		foreach( $style_groups as $media => $styles ) {
92
			$urls = array();
93
			$vers = array();
94
95
			foreach( $styles as $style ) {
96
				$urls[] = str_replace( untrailingslashit( $site_url ), '', $style->src );
97
				$vers[] = $style->ver ? $style->ver : $wp_styles->default_version;
98
			}
99
100
			$cdn_url = $this->cdn_server . '/css?b=' .
101
				urlencode( $site_url ) . '&' .
102
				http_build_query( array( 'f' => $urls ) ) . '&' .
103
				http_build_query( array( 'v' => $vers ) );
104
			// if we are injecting critical CSS, load the full CSS async
105
106
			if ( $this->inject_critical_css ) {
107
				echo '<link rel="preload" onload="this.rel=\'stylesheet\'" as="style" type="text/css" media="' . $media . '" href="' . esc_attr( $cdn_url ) . '"/>';
108
			} else {
109
				echo '<link rel="stylesheet" type="text/css" media="' . $media . '" href="' . esc_attr( $cdn_url ) . '"/>';
110
			}
111
112
			foreach( $styles as $style ) {
113
				if ( isset( $style->extra['concat-after'] ) && $style->extra['concat-after'] ) {
114
					printf( "<style id='%s-inline-css' type='text/css'>\n%s\n</style>\n", esc_attr( $style->handle ), implode( "\n", $style->extra['concat-after'] ) );
115
				}
116
			}
117
		}
118
119
		$this->concat_style_groups[ $group ] = array();
120
	}
121
122
	function render_concatenated_scripts_head() {
123
		$this->flush_concatenated_scripts( 0 );
124
	}
125
126
	function render_concatenated_scripts_footer() {
127
		$this->flush_concatenated_scripts( 1 );
128
	}
129
130
	private function flush_concatenated_scripts( $group ) {
131
		if ( ! isset( $this->concat_script_groups[ $group ] ) ) {
132
			return;
133
		}
134
135
		$scripts = $this->concat_script_groups[ $group ];
136
137
		if ( empty( $scripts ) ) {
138
			return;
139
		}
140
141
		// special URL to concatenation service
142
		global $wp_scripts;
143
		$site_url = site_url();
144
		$urls = array();
145
		$vers = array();
146
147
		foreach( $scripts as $script ) {
148
			$urls[] = str_replace( untrailingslashit( $site_url ), '', $script->src );
149
			$vers[] = $script->ver ? $script->ver : $wp_scripts->default_version;
150
			if ( isset( $script->extra['before'] ) && $script->extra['before'] ) {
151
				echo sprintf( "<script type='text/javascript'>\n%s\n</script>\n", $script->extra['before'] );
152
			}
153
		}
154
155
		$cdn_url = $this->cdn_server . '/js?b=' .
156
			urlencode( $site_url ) . '&' .
157
			http_build_query( array( 'f' => $urls ) ) . '&' .
158
			http_build_query( array( 'v' => $vers ) );
159
160
		// TODO: if there is NO inline or external script tags in the body, render async (maybe?)
0 ignored issues
show
Coding Style Best Practice introduced by
Comments for TODO tasks are often forgotten in the code; it might be better to use a dedicated issue tracker.
Loading history...
161
		echo '<script type="text/javascript" src="' . esc_attr( $cdn_url ) . '"></script>';
162
163
		foreach( $scripts as $script ) {
164
			if ( isset( $script->extra['after'] ) && $script->extra['after'] ) {
165
				echo sprintf( "<script type='text/javascript'>\n%s\n</script>\n", $script->extra['after'] );
166
			}
167
		}
168
169
		$this->concat_script_groups[ $group ] = array();
170
	}
171
172
	/**
173
	 * Asset modification functions
174
	 */
175
176
	/**
177
	 * Scripts
178
	 */
179
180
	public function register_concat_scripts( $tag, $handle, $src ) {
181
		global $wp_scripts;
182
183
		// don't do admin for now
184
		if ( is_admin() || ! isset( $wp_scripts->registered[$handle] ) ) {
185
			return $tag;
186
		}
187
188
		$script = $wp_scripts->registered[$handle];
189
190
		if ( $this->should_concat_script( $script ) ) {
191
			$this->buffer_script( $script );
192
			return '';
193
		}
194
195
		// if this is a non-CDN script, and there are existing CDN scripts in this group, print them and reset the
196
		// array
197
		$group = isset( $script->extra['group'] ) ? $script->extra['group'] : 0;
198
		$this->flush_concatenated_scripts( $group );
199
200
		return $tag;
201
	}
202
203 View Code Duplication
	private function should_concat_script( $script ) {
204
		// only concat local scripts
205
		$is_local       = $this->is_local_url( $script->src );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 7 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
206
		// don't concat conditional scripts
207
		$is_conditional = isset( $script->extra['conditional'] );
208
		return apply_filters( 'jetpack_perf_concat_script', $is_local && ! $is_conditional, $script->handle, $script->src );
209
	}
210
211
	private function buffer_script( $script ) {
212
		$group = isset( $script->extra['group'] ) ? $script->extra['group'] : 0;
213
		if ( ! isset( $this->concat_script_groups[$group] ) ) {
214
			$this->concat_script_groups[$group] = array();
215
		}
216
		$this->concat_script_groups[$group][] = $script;
217
	}
218
219
	/**
220
	 * Styles
221
	 */
222
223
	public function register_concat_styles( $tag, $handle, $href, $media ) {
224
		global $wp_styles;
225
226
		// don't do admin for now
227
		if ( is_admin() || ! isset( $wp_styles->registered[$handle] ) ) {
228
			return $tag;
229
		}
230
231
		$style = $wp_styles->registered[$handle];
232
233
		if ( $this->should_concat_style( $style ) ) {
234
			$this->buffer_style( $style );
235
			return '';
236
		}
237
238
		return $tag;
239
	}
240
241
	private function buffer_style( $style ) {
242
		$group = isset( $style->extra['group'] ) ? $style->extra['group'] : 0;
243
		$media = $style->args;
244
245
		// rename the 'after' code so that we can output it separately
246
		if ( isset( $style->extra['after'] ) ) {
247
			$style->extra['concat-after'] = $style->extra['after'];
248
			unset( $style->extra['after'] );
249
		}
250
251
		if ( ! $media ) {
252
			$media = 'all';
253
		}
254
255
		if ( ! isset( $this->concat_style_groups[$group] ) ) {
256
			$this->concat_style_groups[$group] = array();
257
		}
258
259
		if ( ! isset( $this->concat_style_groups[$group][$media] ) ) {
260
			$this->concat_style_groups[$group][$media] = array();
261
		}
262
263
		$this->concat_style_groups[$group][$media][] = $style;
264
	}
265
266 View Code Duplication
	private function should_concat_style( $style ) {
267
		// only concat local styles
268
		$is_local       = $this->is_local_url( $style->src );
0 ignored issues
show
Coding Style introduced by
Equals sign not aligned correctly; expected 1 space but found 7 spaces

This check looks for improperly formatted assignments.

Every assignment must have exactly one space before and one space after the equals operator.

To illustrate:

$a = "a";
$ab = "ab";
$abc = "abc";

will have no issues, while

$a   = "a";
$ab  = "ab";
$abc = "abc";

will report issues in lines 1 and 2.

Loading history...
269
		// don't concat conditional styles
270
		$is_conditional = isset( $style->extra['conditional'] );
271
		return apply_filters( 'jetpack_perf_concat_style', $is_local && ! $is_conditional, $style->handle, $style->src );
272
	}
273
274
	private function is_local_url( $url ) {
275
		$site_url = site_url();
276
		return ( strncmp( $url, '/', 1 ) === 0 && strncmp( $url, '//', 2 ) !== 0 )
277
			|| strpos( $url, $site_url ) === 0;
278
	}
279
}