PluginProbe
Getwid – Gutenberg Blocks / 2.0.7
Getwid – Gutenberg Blocks v2.0.7
3.0.1 3.0.0 2.1.0 2.1.1 2.1.2 2.1.3 2.2.0 trunk 1.8.7 1.9.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9
getwid / includes / assets-optimization.php

assets-optimization.php in Getwid – Gutenberg Blocks 2.0.7, at includes/assets-optimization.php

391 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Getwid;
4
5 class AssetsOptimization {
6
7 /**
8 * @var AssetsOptimization
9 */
10 private static $instance = null;
11
12 /**
13 * Flag to indicate if CSS is already minified
14 *
15 * @var bool
16 */
17 private $alreadyoptimized = false;
18
19 /**
20 * @return AssetsOptimization
21 */
22 public static function getInstance() {
23 if ( is_null( self::$instance ) ) {
24 self::$instance = new self();
25 }
26
27 return self::$instance;
28 }
29
30 /**
31 * AssetsOptimization constructor.
32 */
33 public function __construct() {
34
35 getwid_maybe_add_option( 'getwid_load_assets_on_demand', false, true );
36 getwid_maybe_add_option( 'getwid_move_css_to_head', false, true );
37
38 if ( FALSE == $this->load_assets_on_demand() ||
39 FALSE == $this->move_css_to_head() ) {
40 return;
41 }
42
43 add_action( 'template_redirect', array( $this, 'buffer_start' ), 1 );
44
45 add_filter( 'getwid/html/output', array( $this, 'filter_css' ) );
46 add_filter( 'getwid/html/output', array( $this, 'filter_test' ) );
47
48 // Filter Autoptimize plugin output
49 add_filter( 'autoptimize_filter_html_before_minify', array( $this, 'filter_css' ) );
50
51 }
52
53 /**
54 * Setup output buffering if needed.
55 *
56 * @return void
57 */
58 public function buffer_start() {
59
60 if ( $this->should_buffer() ) {
61 ob_start( array( $this, 'buffer_end' ) );
62 }
63 }
64
65 /**
66 * Processes/optimizes the output-buffered content and returns it.
67 * If the content is not processable, it is returned unmodified.
68 *
69 * @param string $html Buffered content.
70 *
71 * @return string
72 */
73 public function buffer_end( $html ) {
74
75 // Bail early without modifying anything if we can't handle the content.
76 if ( ! $this->is_valid_buffer( $html ) ) {
77 return $html;
78 }
79
80 // Apply any filters to the final output
81 $html = apply_filters( 'getwid/html/output', $html );
82
83 return $html;
84 }
85
86 /*
87 * Test replacement
88 *
89 * @param string $html Buffered content.
90 *
91 * @return string
92 */
93 public function filter_test( $html ) {
94
95 if ( $this->alreadyoptimized ) {
96 $html .= PHP_EOL . '<!-- getwid optimized -->';
97 }
98
99 return $html;
100 }
101
102 /*
103 * Find stylesheets of our blocks in original HTML and move them to Header
104 *
105 * @param string $html Buffered content.
106 *
107 * @return string
108 *
109 * Source \plugins\autoptimize\classes\autoptimizeStyles.php
110 */
111 public function filter_css( $html ) {
112
113 if ( $this->alreadyoptimized ) {
114 return $html;
115 }
116
117 $css = array();
118
119 $blocks = getwid()->blocksManager()->getBlocks();
120 //var_dump($blocks);exit;
121
122 $optimize_more = apply_filters( 'getwid/optimize/assets', [] );
123
124 /*
125 * Blocks array example:
126 * ["getwid/anchor"] => object(Getwid\Blocks\Anchor) {}
127 *
128 * Style tag example:
129 * <link rel='stylesheet' id='getwid/banner-css' href='https://.../wp-content/plugins/getwid/assets/blocks/banner/style.css' type='text/css' media='all' />
130 */
131
132 // Find stylesheets in original html
133 if ( preg_match_all( '#(<link[^>]*stylesheet[^>]*>)#Usmi', $html, $tag_matches ) ) {
134
135 foreach ( $tag_matches[0] as $tag ) {
136
137 if ( preg_match( '#<link.*id=("|\')(.*)-css("|\')#Usmi', $tag, $link_matches ) ) {
138
139 $link_id = $link_matches[2];
140 //var_dump($link_id);
141
142 // Check if this is a stylesheet of our block
143 if (
144 array_key_exists( $link_id, $blocks ) ||
145 in_array( $link_id, $optimize_more )
146 ) {
147 //var_dump($link_id);
148
149 // Save tag for next steps
150 $css[ $link_id ] = $tag;
151
152 // Clear tag in original html
153 $html = str_replace( $tag, '<!-- #' . $link_id .' -->', $html );
154 }
155 }
156 }
157 }
158
159 // Process stylesheets if we found ones
160 if ( ! empty( $css ) ) {
161
162 // move stylesheet to a new positon
163 $replace_tag = array( '</title>', 'after' );
164
165 // Debug
166 $css_scope = PHP_EOL . '<!-- getwid styles -->' . PHP_EOL . "\t";
167
168 // Print stylesheets
169 $css_scope .= implode( PHP_EOL . "\t", $css );
170
171 // Debug
172 $css_scope .= PHP_EOL . '<!-- /getwid styles -->' . PHP_EOL;
173
174 //var_dump($css_scope);exit;
175 $html = $this->inject_in_html( $css_scope, $replace_tag, $html );
176
177 // Mark as optimized
178 $this->alreadyoptimized = true;
179 }
180
181 return $html;
182 }
183
184 /**
185 * Injects/replaces the given payload markup into `$html`
186 * at the specified location.
187 * If the specified tag cannot be found, the payload is appended into
188 * $html along with a warning wrapped inside <!--noptimize--> tags.
189 *
190 * @param string $payload Markup to inject.
191 * @param array $where Array specifying the tag name and method of injection.
192 * Index 0 is the tag name (i.e., `</body>`).
193 * Index 1 specifies ?'before', 'after' or 'replace'. Defaults to 'before'.
194 *
195 * @return void
196 */
197 protected function inject_in_html( $payload, $where, $html )
198 {
199 $warned = false;
200 $position = $this->strpos( $html, $where[0] );
201 if ( false !== $position ) {
202 // Found the tag, setup content/injection as specified.
203 if ( 'after' === $where[1] ) {
204 $content = $where[0] . $payload;
205 } elseif ( 'replace' === $where[1] ) {
206 $content = $payload;
207 } else {
208 $content = $payload . $where[0];
209 }
210 // Place where specified.
211 $html = $this->substr_replace(
212 $html,
213 $content,
214 $position,
215 // Using plain strlen() should be safe here for now, since
216 // we're not searching for multibyte chars here still...
217 strlen( $where[0] )
218 );
219 } else {
220 // Couldn't find what was specified, just append and add a warning.
221 $html .= $payload;
222 if ( ! $warned ) {
223 $tag_display = str_replace( array( '<', '>' ), '', $where[0] );
224 $html .= '<!--noptimize--><!-- We found a problem with the HTML in your Theme, tag `' . $tag_display . '` missing --><!--/noptimize-->';
225 $warned = true;
226 }
227 }
228
229 return $html;
230 }
231
232 /**
233 * Returns true if all the conditions to start output buffering are satisfied.
234 *
235 * @return bool
236 */
237 public function should_buffer() {
238
239 $do_buffering = false;
240
241 // Check for site being previewed in the Customizer (available since WP 4.0).
242 $is_customize_preview = false;
243 if ( function_exists( 'is_customize_preview' ) && is_customize_preview() ) {
244 $is_customize_preview = is_customize_preview();
245 }
246
247 /**
248 * We only buffer the frontend requests (and then only if not a feed
249 * and not turned off explicitly and not when being previewed in Customizer)!
250 */
251 $do_buffering = ( ! is_admin() && ! is_feed() && ! is_embed() && ! $is_customize_preview );
252
253 return $do_buffering;
254 }
255
256 /**
257 * Returns true if given markup is considered valid/processable/optimizable.
258 *
259 * @param string $content Markup.
260 *
261 * @return bool
262 */
263 public function is_valid_buffer( $content )
264 {
265 // Defaults to true.
266 $valid = true;
267
268 return $valid;
269 }
270
271 /**
272 * Multibyte-capable strpos() if support is available on the server.
273 * If not, it falls back to using \strpos().
274 *
275 * @param string $haystack Haystack.
276 * @param string $needle Needle.
277 * @param int $offset Offset.
278 * @param string|null $encoding Encoding. Default null.
279 *
280 * @return int|false
281 */
282 public function strpos( $haystack, $needle, $offset = 0, $encoding = null )
283 {
284 if ( $this->mbstring_available() ) {
285 return ( null === $encoding ) ? \mb_strpos( $haystack, $needle, $offset ) : \mb_strpos( $haystack, $needle, $offset, $encoding );
286 } else {
287 return \strpos( $haystack, $needle, $offset );
288 }
289 }
290
291 /**
292 * Our wrapper around implementations of \substr_replace()
293 * that attempts to not break things horribly if at all possible.
294 * Uses mbstring if available, before falling back to regular
295 * substr_replace() (which works just fine in the majority of cases).
296 *
297 * @param string $string String.
298 * @param string $replacement Replacement.
299 * @param int $start Start offset.
300 * @param int|null $length Length.
301 * @param string|null $encoding Encoding.
302 *
303 * @return string
304 */
305 public function substr_replace( $string, $replacement, $start, $length = null, $encoding = null )
306 {
307 if ( $this->mbstring_available() ) {
308 $strlen = $this->strlen( $string, $encoding );
309
310 if ( $start < 0 ) {
311 if ( -$start < $strlen ) {
312 $start = $strlen + $start;
313 } else {
314 $start = 0;
315 }
316 } elseif ( $start > $strlen ) {
317 $start = $strlen;
318 }
319
320 if ( null === $length || '' === $length ) {
321 $start2 = $strlen;
322 } elseif ( $length < 0 ) {
323 $start2 = $strlen + $length;
324 if ( $start2 < $start ) {
325 $start2 = $start;
326 }
327 } else {
328 $start2 = $start + $length;
329 }
330
331 if ( null === $encoding ) {
332 $leader = $start ? \mb_substr( $string, 0, $start ) : '';
333 $trailer = ( $start2 < $strlen ) ? \mb_substr( $string, $start2, null ) : '';
334 } else {
335 $leader = $start ? \mb_substr( $string, 0, $start, $encoding ) : '';
336 $trailer = ( $start2 < $strlen ) ? \mb_substr( $string, $start2, null, $encoding ) : '';
337 }
338
339 return "{$leader}{$replacement}{$trailer}";
340 }
341
342 return ( null === $length ) ? \substr_replace( $string, $replacement, $start ) : \substr_replace( $string, $replacement, $start, $length );
343 }
344
345 /**
346 * Attempts to return the number of characters in the given $string if
347 * mbstring is available. Returns the number of bytes
348 * (instead of characters) as fallback.
349 *
350 * @param string $string String.
351 * @param string|null $encoding Encoding.
352 *
353 * @return int Number of characters or bytes in given $string
354 * (characters if/when supported, bytes otherwise).
355 */
356 public function strlen( $string, $encoding = null )
357 {
358 if ( $this->mbstring_available() ) {
359 return ( null === $encoding ) ? \mb_strlen( $string ) : \mb_strlen( $string, $encoding );
360 } else {
361 return \strlen( $string );
362 }
363 }
364
365 /**
366 * Returns true when mbstring is available.
367 *
368 * @param bool|null $override Allows overriding the decision.
369 *
370 * @return bool
371 */
372 public function mbstring_available()
373 {
374
375 $available = \extension_loaded( 'mbstring' );
376
377 return $available;
378 }
379
380 public function load_assets_on_demand()
381 {
382 return get_option( 'getwid_load_assets_on_demand', false );
383 }
384
385 public function move_css_to_head()
386 {
387 return get_option( 'getwid_move_css_to_head', false );
388 }
389
390 }
391