Version Description
- Fix: Move Minify library to a namespace to avoid conflicts with other plugins
- Fix: Check for AWS before loading functions
- Fix: Update Minify ClosureCompiler base URL; use HTTPS
- Fix: Corrected getting the network siteurl
- Fix: Prevent PHP warning in CurlFactory
- Update: Added information links to general minify options
- Update: Added video/ogg support for browser caching
Download this release
Release Info
Developer | joemoto |
Plugin | W3 Total Cache |
Version | 2.1.1 |
Comparing to | |
See all releases |
Code changes from version 2.1.0 to 2.1.1
- BrowserCache_Environment.php +1 -0
- FeatureShowcase_Plugin_Admin.php +19 -19
- Generic_AdminActions_Test.php +13 -13
- Minify_ContentMinifier.php +24 -24
- Minify_Environment.php +22 -2
- Minify_MinifiedFileRequestHandler.php +11 -11
- Minify_Plugin.php +3 -3
- Util_Ui.php +1 -0
- inc/mime/other.php +1 -0
- inc/options/general.php +50 -23
- ini/apache_conf/mod_expires.conf +1 -0
- ini/apache_conf/mod_mime.conf +1 -0
- lib/Aws/aws-autoloader.php +4 -1
- lib/Azure/GuzzleHttp/Handler/CurlFactory.php +1 -1
- lib/Minify/DooDigestAuth.php +0 -121
- lib/Minify/HTTP/ConditionalGet.php +344 -342
- lib/Minify/HTTP/Encoder.php +292 -290
- lib/Minify/JSMin.php +372 -370
- lib/Minify/JSMinPlus.php +14 -13
- lib/Minify/Minify.php +676 -674
- lib/Minify/Minify/Build.php +69 -67
- lib/Minify/Minify/CSS.php +82 -81
- lib/Minify/Minify/CSS/Compressor.php +43 -42
- lib/Minify/Minify/CSS/UriRewriter.php +434 -433
- lib/Minify/Minify/CSSTidy.php +2 -1
- lib/Minify/Minify/Cache/File.php +1 -0
- lib/Minify/Minify/Cache/W3TCDerived.php +3 -2
- lib/Minify/Minify/Cache/ZendPlatform.php +2 -1
- lib/Minify/Minify/ClosureCompiler.php +94 -93
- lib/Minify/Minify/CombineOnly.php +11 -11
- lib/Minify/Minify/CommentPreserver.php +15 -14
- lib/Minify/Minify/Controller/Base.php +203 -202
- lib/Minify/Minify/Controller/Files.php +11 -11
- lib/Minify/Minify/Controller/Groups.php +14 -14
- lib/Minify/Minify/Controller/MinApp.php +12 -11
- lib/Minify/Minify/Controller/Page.php +54 -54
- lib/Minify/Minify/Controller/Version1.php +16 -16
- lib/Minify/Minify/DebugDetector.php +1 -0
- lib/Minify/Minify/HTML.php +2 -0
- lib/Minify/Minify/HTML/Helper.php +4 -3
- lib/Minify/Minify/HTMLTidy.php +1 -0
- lib/Minify/Minify/IgnoredCommentPreserver.php +1 -0
- lib/Minify/Minify/ImportProcessor.php +2 -0
- lib/Minify/Minify/Inline.php +1 -0
- lib/Minify/Minify/Inline/CSS.php +1 -0
- lib/Minify/Minify/Inline/JavaScript.php +1 -0
- lib/Minify/Minify/JS/ClosureCompiler.php +141 -140
- lib/Minify/Minify/Lines.php +1 -0
- lib/Minify/Minify/Loader.php +0 -28
- lib/Minify/Minify/Logger.php +7 -6
- lib/Minify/Minify/Packer.php +5 -4
- lib/Minify/Minify/Source.php +28 -28
- lib/Minify/Minify/YUI/CssCompressor.php +150 -149
- lib/Minify/Minify/YUICompressor.php +162 -161
- lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Colors.php +0 -155
- lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Command.php +0 -223
- lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Minifier.php +0 -862
- lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Utils.php +0 -149
- lib/YuiCssMin/Colors.php +154 -0
- lib/YuiCssMin/Command.php +222 -0
- lib/YuiCssMin/Minifier.php +866 -0
- lib/YuiCssMin/Utils.php +148 -0
- pub/css/options.css +14 -1
- readme.txt +11 -2
- w3-total-cache-api.php +27 -29
- w3-total-cache.php +1 -1
BrowserCache_Environment.php
CHANGED
@@ -106,6 +106,7 @@ class BrowserCache_Environment {
|
|
106 |
unset( $other_compression['mov|qt'] );
|
107 |
unset( $other_compression['mp3|m4a'] );
|
108 |
unset( $other_compression['mp4|m4v'] );
|
|
|
109 |
unset( $other_compression['mpeg|mpg|mpe'] );
|
110 |
unset( $other_compression['png'] );
|
111 |
unset( $other_compression['ra|ram'] );
|
106 |
unset( $other_compression['mov|qt'] );
|
107 |
unset( $other_compression['mp3|m4a'] );
|
108 |
unset( $other_compression['mp4|m4v'] );
|
109 |
+
unset( $other_compression['ogv'] );
|
110 |
unset( $other_compression['mpeg|mpg|mpe'] );
|
111 |
unset( $other_compression['png'] );
|
112 |
unset( $other_compression['ra|ram'] );
|
FeatureShowcase_Plugin_Admin.php
CHANGED
@@ -173,7 +173,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
173 |
esc_url( admin_url( 'admin.php?page=w3tc_setup_guide' ) ) . '\'">' .
|
174 |
__( 'Launch', 'w3-total-cache' ) . '</button>',
|
175 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/setup-guide-wizard/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=setup_guide' ) .
|
176 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
177 |
'is_premium' => false,
|
178 |
'is_new' => true,
|
179 |
),
|
@@ -185,7 +185,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
185 |
esc_url( admin_url( 'admin.php?page=w3tc_userexperience' ) ) . '\'">' .
|
186 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
187 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/lazy-load-google-maps/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_lazyload_googlemaps' ) .
|
188 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
189 |
'is_premium' => true,
|
190 |
'is_new' => true,
|
191 |
),
|
@@ -197,7 +197,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
197 |
esc_url( admin_url( 'admin.php?page=w3tc_general#cdn' ) ) . '\'">' .
|
198 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
199 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/cdn-full-site-delivery/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_cdn_fsd' ) .
|
200 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
201 |
'is_premium' => true,
|
202 |
'is_new' => false,
|
203 |
),
|
@@ -209,7 +209,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
209 |
esc_url( admin_url( 'admin.php?page=w3tc_minify#css' ) ) . '\'">' .
|
210 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
211 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/how-to-use-manual-minify-for-css-and-js/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_minify_CSS' ) .
|
212 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
213 |
'is_premium' => true,
|
214 |
'is_new' => false,
|
215 |
),
|
@@ -221,7 +221,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
221 |
esc_url( admin_url( 'admin.php?page=w3tc_extensions' ) ) . '\'">' .
|
222 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
223 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/extension-framework-pro/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_extensions' ) .
|
224 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
225 |
'is_premium' => true,
|
226 |
'is_new' => false,
|
227 |
),
|
@@ -233,7 +233,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
233 |
esc_url( admin_url( 'admin.php?page=w3tc_general#fragmentcache' ) ) . '\'">' .
|
234 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
235 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-a-fragment-caching-method-for-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_fragment_cache' ) .
|
236 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
237 |
'is_premium' => true,
|
238 |
'is_new' => false,
|
239 |
),
|
@@ -245,7 +245,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
245 |
esc_url( admin_url( 'admin.php?page=w3tc_pgcache#rest' ) ) . '\'">' .
|
246 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
247 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/achieve-ultimate-wordpress-performance-with-w3-total-cache-pro/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_rest_api_caching' ) .
|
248 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
249 |
'is_premium' => true,
|
250 |
'is_new' => false,
|
251 |
),
|
@@ -257,7 +257,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
257 |
esc_url( admin_url( 'admin.php?page=w3tc_stats' ) ) . '\'">' .
|
258 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
259 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-w3-total-cache-statistics-to-give-detailed-information-about-your-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_stats' ) .
|
260 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
261 |
'is_premium' => true,
|
262 |
'is_new' => false,
|
263 |
),
|
@@ -269,7 +269,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
269 |
esc_url( admin_url( 'admin.php?page=w3tc_general#debug' ) ) . '\'">' .
|
270 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
271 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/purge-cache-log/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_purge_logs' ) .
|
272 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
273 |
'is_premium' => true,
|
274 |
'is_new' => false,
|
275 |
),
|
@@ -281,7 +281,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
281 |
esc_url( admin_url( 'admin.php?page=w3tc_general#page_cache' ) ) . '\'">' .
|
282 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
283 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-page-caching-in-w3-total-cache-for-shared-hosting/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=page_cache' ) .
|
284 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
285 |
'is_premium' => false,
|
286 |
'is_new' => false,
|
287 |
),
|
@@ -293,7 +293,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
293 |
esc_url( admin_url( 'admin.php?page=w3tc_general#minify' ) ) . '\'">' .
|
294 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
295 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-a-minification-method-for-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=minify' ) .
|
296 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
297 |
'is_premium' => false,
|
298 |
'is_new' => false,
|
299 |
),
|
@@ -305,7 +305,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
305 |
esc_url( admin_url( 'admin.php?page=w3tc_general#userexperience' ) ) . '\'">' .
|
306 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
307 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-lazy-loading-for-your-wordpress-website-with-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=lazyload' ) .
|
308 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
309 |
'is_premium' => false,
|
310 |
'is_new' => false,
|
311 |
),
|
@@ -317,7 +317,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
317 |
esc_url( admin_url( 'admin.php?page=w3tc_general#cdn' ) ) . '\'">' .
|
318 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
319 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-w3-total-cache-with-stackpath-for-cdn-objects/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=cdn' ) .
|
320 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
321 |
'is_premium' => false,
|
322 |
'is_new' => false,
|
323 |
),
|
@@ -329,7 +329,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
329 |
esc_url( admin_url( 'admin.php?page=w3tc_general#system_opcache' ) ) . '\'">' .
|
330 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
331 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-an-opcode-caching-method-with-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=opcode_cache' ) .
|
332 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
333 |
'is_premium' => false,
|
334 |
'is_new' => false,
|
335 |
),
|
@@ -341,7 +341,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
341 |
esc_url( admin_url( 'admin.php?page=w3tc_general#database_cache' ) ) . '\'">' .
|
342 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
343 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-a-database-caching-method-in-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=database_cache' ) .
|
344 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
345 |
'is_premium' => false,
|
346 |
'is_new' => false,
|
347 |
),
|
@@ -353,7 +353,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
353 |
esc_url( admin_url( 'admin.php?page=w3tc_general#object_cache' ) ) . '\'">' .
|
354 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
355 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-object-caching-methods-in-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=object_cache' ) .
|
356 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
357 |
'is_premium' => false,
|
358 |
'is_new' => false,
|
359 |
),
|
@@ -365,7 +365,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
365 |
esc_url( admin_url( 'admin.php?page=w3tc_general#browser_cache' ) ) . '\'">' .
|
366 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
367 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-browser-caching-in-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=browser_cache' ) .
|
368 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
369 |
'is_premium' => false,
|
370 |
'is_new' => false,
|
371 |
),
|
@@ -377,7 +377,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
377 |
esc_url( admin_url( 'admin.php?page=w3tc_extensions' ) ) . '\'">' .
|
378 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
379 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/extension-framework/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=extensions' ) .
|
380 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
381 |
'is_premium' => false,
|
382 |
'is_new' => false,
|
383 |
),
|
@@ -389,7 +389,7 @@ class FeatureShowcase_Plugin_Admin {
|
|
389 |
esc_url( admin_url( 'admin.php?page=w3tc_cachegroups' ) ) . '\'">' .
|
390 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
391 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/cache-groups/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=cache_groups' ) .
|
392 |
-
'">' . __( 'More info', 'w3-total-cache' ) . '
|
393 |
'is_premium' => false,
|
394 |
'is_new' => false,
|
395 |
),
|
173 |
esc_url( admin_url( 'admin.php?page=w3tc_setup_guide' ) ) . '\'">' .
|
174 |
__( 'Launch', 'w3-total-cache' ) . '</button>',
|
175 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/setup-guide-wizard/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=setup_guide' ) .
|
176 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
177 |
'is_premium' => false,
|
178 |
'is_new' => true,
|
179 |
),
|
185 |
esc_url( admin_url( 'admin.php?page=w3tc_userexperience' ) ) . '\'">' .
|
186 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
187 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/lazy-load-google-maps/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_lazyload_googlemaps' ) .
|
188 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
189 |
'is_premium' => true,
|
190 |
'is_new' => true,
|
191 |
),
|
197 |
esc_url( admin_url( 'admin.php?page=w3tc_general#cdn' ) ) . '\'">' .
|
198 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
199 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/cdn-full-site-delivery/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_cdn_fsd' ) .
|
200 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
201 |
'is_premium' => true,
|
202 |
'is_new' => false,
|
203 |
),
|
209 |
esc_url( admin_url( 'admin.php?page=w3tc_minify#css' ) ) . '\'">' .
|
210 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
211 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/how-to-use-manual-minify-for-css-and-js/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_minify_CSS' ) .
|
212 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
213 |
'is_premium' => true,
|
214 |
'is_new' => false,
|
215 |
),
|
221 |
esc_url( admin_url( 'admin.php?page=w3tc_extensions' ) ) . '\'">' .
|
222 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
223 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/extension-framework-pro/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_extensions' ) .
|
224 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
225 |
'is_premium' => true,
|
226 |
'is_new' => false,
|
227 |
),
|
233 |
esc_url( admin_url( 'admin.php?page=w3tc_general#fragmentcache' ) ) . '\'">' .
|
234 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
235 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-a-fragment-caching-method-for-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_fragment_cache' ) .
|
236 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
237 |
'is_premium' => true,
|
238 |
'is_new' => false,
|
239 |
),
|
245 |
esc_url( admin_url( 'admin.php?page=w3tc_pgcache#rest' ) ) . '\'">' .
|
246 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
247 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/achieve-ultimate-wordpress-performance-with-w3-total-cache-pro/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_rest_api_caching' ) .
|
248 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
249 |
'is_premium' => true,
|
250 |
'is_new' => false,
|
251 |
),
|
257 |
esc_url( admin_url( 'admin.php?page=w3tc_stats' ) ) . '\'">' .
|
258 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
259 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-w3-total-cache-statistics-to-give-detailed-information-about-your-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_stats' ) .
|
260 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
261 |
'is_premium' => true,
|
262 |
'is_new' => false,
|
263 |
),
|
269 |
esc_url( admin_url( 'admin.php?page=w3tc_general#debug' ) ) . '\'">' .
|
270 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
271 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/purge-cache-log/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=pro_purge_logs' ) .
|
272 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
273 |
'is_premium' => true,
|
274 |
'is_new' => false,
|
275 |
),
|
281 |
esc_url( admin_url( 'admin.php?page=w3tc_general#page_cache' ) ) . '\'">' .
|
282 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
283 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-page-caching-in-w3-total-cache-for-shared-hosting/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=page_cache' ) .
|
284 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
285 |
'is_premium' => false,
|
286 |
'is_new' => false,
|
287 |
),
|
293 |
esc_url( admin_url( 'admin.php?page=w3tc_general#minify' ) ) . '\'">' .
|
294 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
295 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-a-minification-method-for-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=minify' ) .
|
296 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
297 |
'is_premium' => false,
|
298 |
'is_new' => false,
|
299 |
),
|
305 |
esc_url( admin_url( 'admin.php?page=w3tc_general#userexperience' ) ) . '\'">' .
|
306 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
307 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-lazy-loading-for-your-wordpress-website-with-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=lazyload' ) .
|
308 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
309 |
'is_premium' => false,
|
310 |
'is_new' => false,
|
311 |
),
|
317 |
esc_url( admin_url( 'admin.php?page=w3tc_general#cdn' ) ) . '\'">' .
|
318 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
319 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-w3-total-cache-with-stackpath-for-cdn-objects/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=cdn' ) .
|
320 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
321 |
'is_premium' => false,
|
322 |
'is_new' => false,
|
323 |
),
|
329 |
esc_url( admin_url( 'admin.php?page=w3tc_general#system_opcache' ) ) . '\'">' .
|
330 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
331 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-an-opcode-caching-method-with-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=opcode_cache' ) .
|
332 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
333 |
'is_premium' => false,
|
334 |
'is_new' => false,
|
335 |
),
|
341 |
esc_url( admin_url( 'admin.php?page=w3tc_general#database_cache' ) ) . '\'">' .
|
342 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
343 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/choosing-a-database-caching-method-in-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=database_cache' ) .
|
344 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
345 |
'is_premium' => false,
|
346 |
'is_new' => false,
|
347 |
),
|
353 |
esc_url( admin_url( 'admin.php?page=w3tc_general#object_cache' ) ) . '\'">' .
|
354 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
355 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-object-caching-methods-in-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=object_cache' ) .
|
356 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
357 |
'is_premium' => false,
|
358 |
'is_new' => false,
|
359 |
),
|
365 |
esc_url( admin_url( 'admin.php?page=w3tc_general#browser_cache' ) ) . '\'">' .
|
366 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
367 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/configuring-browser-caching-in-w3-total-cache/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=browser_cache' ) .
|
368 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
369 |
'is_premium' => false,
|
370 |
'is_new' => false,
|
371 |
),
|
377 |
esc_url( admin_url( 'admin.php?page=w3tc_extensions' ) ) . '\'">' .
|
378 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
379 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/extension-framework/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=extensions' ) .
|
380 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
381 |
'is_premium' => false,
|
382 |
'is_new' => false,
|
383 |
),
|
389 |
esc_url( admin_url( 'admin.php?page=w3tc_cachegroups' ) ) . '\'">' .
|
390 |
__( 'Settings', 'w3-total-cache' ) . '</button>',
|
391 |
'link' => '<a target="_blank" href="' . esc_url( 'https://www.boldgrid.com/support/w3-total-cache/cache-groups/?utm_source=w3tc&utm_medium=feature_showcase&utm_campaign=cache_groups' ) .
|
392 |
+
'">' . __( 'More info', 'w3-total-cache' ) . '<span class="dashicons dashicons-external"></span></a>',
|
393 |
'is_premium' => false,
|
394 |
'is_new' => false,
|
395 |
),
|
Generic_AdminActions_Test.php
CHANGED
@@ -109,32 +109,32 @@ class Generic_AdminActions_Test {
|
|
109 |
if ( empty( $error ) ) {
|
110 |
switch ( $engine ) {
|
111 |
case 'yuijs':
|
112 |
-
\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
113 |
-
\Minify_YUICompressor::$javaExecutable = $path_java;
|
114 |
-
\Minify_YUICompressor::$jarFile = $path_jar;
|
115 |
|
116 |
-
$result = \Minify_YUICompressor::testJs( $error );
|
117 |
break;
|
118 |
|
119 |
case 'yuicss':
|
120 |
-
\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
121 |
-
\Minify_YUICompressor::$javaExecutable = $path_java;
|
122 |
-
\Minify_YUICompressor::$jarFile = $path_jar;
|
123 |
|
124 |
-
$result = \Minify_YUICompressor::testCss( $error );
|
125 |
break;
|
126 |
|
127 |
case 'ccjs':
|
128 |
-
\Minify_ClosureCompiler::$tempDir = Util_File::create_tmp_dir();
|
129 |
-
\Minify_ClosureCompiler::$javaExecutable = $path_java;
|
130 |
-
\Minify_ClosureCompiler::$jarFile = $path_jar;
|
131 |
|
132 |
-
$result = \Minify_ClosureCompiler::test( $error );
|
133 |
break;
|
134 |
|
135 |
case 'googleccjs':
|
136 |
|
137 |
-
$result = \Minify_JS_ClosureCompiler::test( $error );
|
138 |
break;
|
139 |
|
140 |
default:
|
109 |
if ( empty( $error ) ) {
|
110 |
switch ( $engine ) {
|
111 |
case 'yuijs':
|
112 |
+
\W3TCL\Minify\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
113 |
+
\W3TCL\Minify\Minify_YUICompressor::$javaExecutable = $path_java;
|
114 |
+
\W3TCL\Minify\Minify_YUICompressor::$jarFile = $path_jar;
|
115 |
|
116 |
+
$result = \W3TCL\Minify\Minify_YUICompressor::testJs( $error );
|
117 |
break;
|
118 |
|
119 |
case 'yuicss':
|
120 |
+
\W3TCL\Minify\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
121 |
+
\W3TCL\Minify\Minify_YUICompressor::$javaExecutable = $path_java;
|
122 |
+
\W3TCL\Minify\Minify_YUICompressor::$jarFile = $path_jar;
|
123 |
|
124 |
+
$result = \W3TCL\Minify\Minify_YUICompressor::testCss( $error );
|
125 |
break;
|
126 |
|
127 |
case 'ccjs':
|
128 |
+
\W3TCL\Minify\Minify_ClosureCompiler::$tempDir = Util_File::create_tmp_dir();
|
129 |
+
\W3TCL\Minify\Minify_ClosureCompiler::$javaExecutable = $path_java;
|
130 |
+
\W3TCL\Minify\Minify_ClosureCompiler::$jarFile = $path_jar;
|
131 |
|
132 |
+
$result = \W3TCL\Minify\Minify_ClosureCompiler::test( $error );
|
133 |
break;
|
134 |
|
135 |
case 'googleccjs':
|
136 |
|
137 |
+
$result = \W3TCL\Minify\Minify_JS_ClosureCompiler::test( $error );
|
138 |
break;
|
139 |
|
140 |
default:
|
Minify_ContentMinifier.php
CHANGED
@@ -20,25 +20,25 @@ class Minify_ContentMinifier {
|
|
20 |
* @var array
|
21 |
*/
|
22 |
var $_minifiers = array(
|
23 |
-
'combinejs' => array( 'Minify_CombineOnly', 'minify' ),
|
24 |
-
'combinecss' => array( 'Minify_CombineOnly', 'minify' ),
|
25 |
|
26 |
-
'js' => array( '
|
27 |
-
'yuijs' => array( 'Minify_YUICompressor', 'minifyJs' ),
|
28 |
-
'ccjs' => array( 'Minify_ClosureCompiler', 'minify' ),
|
29 |
-
'jsminplus' => array( '
|
30 |
-
'googleccjs' => array( 'Minify_JS_ClosureCompiler', 'minify' ),
|
31 |
|
32 |
-
'css' => array( 'Minify_CSS', 'minify' ),
|
33 |
-
'yuicss' => array( 'Minify_YUICompressor', 'minifyCss' ),
|
34 |
-
'cssmin' => array( '
|
35 |
-
'csstidy' => array( 'Minify_CSSTidy', 'minify' ),
|
36 |
|
37 |
-
'html' => array( 'Minify_HTML', 'minify' ),
|
38 |
-
'htmlxml' => array( 'Minify_HTML', 'minify' ),
|
39 |
|
40 |
-
'htmltidy' => array( 'Minify_HTMLTidy', 'minifyXhtml' ),
|
41 |
-
'htmltidyxml' => array( 'Minify_HTMLTidy', 'minifyXml' )
|
42 |
);
|
43 |
|
44 |
/**
|
@@ -115,21 +115,21 @@ class Minify_ContentMinifier {
|
|
115 |
function init( $engine ) {
|
116 |
switch ( $engine ) {
|
117 |
case 'yuijs':
|
118 |
-
\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
119 |
-
\Minify_YUICompressor::$javaExecutable = $this->_config->get_string( 'minify.yuijs.path.java' );
|
120 |
-
\Minify_YUICompressor::$jarFile = $this->_config->get_string( 'minify.yuijs.path.jar' );
|
121 |
break;
|
122 |
|
123 |
case 'yuicss':
|
124 |
-
\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
125 |
-
\Minify_YUICompressor::$javaExecutable = $this->_config->get_string( 'minify.yuicss.path.java' );
|
126 |
-
\Minify_YUICompressor::$jarFile = $this->_config->get_string( 'minify.yuicss.path.jar' );
|
127 |
break;
|
128 |
|
129 |
case 'ccjs':
|
130 |
-
\Minify_ClosureCompiler::$tempDir = Util_File::create_tmp_dir();
|
131 |
-
\Minify_ClosureCompiler::$javaExecutable = $this->_config->get_string( 'minify.ccjs.path.java' );
|
132 |
-
\Minify_ClosureCompiler::$jarFile = $this->_config->get_string( 'minify.ccjs.path.jar' );
|
133 |
break;
|
134 |
}
|
135 |
}
|
20 |
* @var array
|
21 |
*/
|
22 |
var $_minifiers = array(
|
23 |
+
'combinejs' => array( '\W3TCL\Minify\Minify_CombineOnly', 'minify' ),
|
24 |
+
'combinecss' => array( '\W3TCL\Minify\Minify_CombineOnly', 'minify' ),
|
25 |
|
26 |
+
'js' => array( '\W3TCL\Minify\JSMin', 'minify' ),
|
27 |
+
'yuijs' => array( '\W3TCL\Minify\Minify_YUICompressor', 'minifyJs' ),
|
28 |
+
'ccjs' => array( '\W3TCL\Minify\Minify_ClosureCompiler', 'minify' ),
|
29 |
+
'jsminplus' => array( '\W3TCL\Minify\JSMinPlus', 'minify' ),
|
30 |
+
'googleccjs' => array( '\W3TCL\Minify\Minify_JS_ClosureCompiler', 'minify' ),
|
31 |
|
32 |
+
'css' => array( '\W3TCL\Minify\Minify_CSS', 'minify' ),
|
33 |
+
'yuicss' => array( '\W3TCL\Minify\Minify_YUICompressor', 'minifyCss' ),
|
34 |
+
'cssmin' => array( '\W3TCL\YuiCssMin\Minifier', 'minify_static' ),
|
35 |
+
'csstidy' => array( '\W3TCL\Minify\Minify_CSSTidy', 'minify' ),
|
36 |
|
37 |
+
'html' => array( '\W3TCL\Minify\Minify_HTML', 'minify' ),
|
38 |
+
'htmlxml' => array( '\W3TCL\Minify\Minify_HTML', 'minify' ),
|
39 |
|
40 |
+
'htmltidy' => array( '\W3TCL\Minify\Minify_HTMLTidy', 'minifyXhtml' ),
|
41 |
+
'htmltidyxml' => array( '\W3TCL\Minify\Minify_HTMLTidy', 'minifyXml' )
|
42 |
);
|
43 |
|
44 |
/**
|
115 |
function init( $engine ) {
|
116 |
switch ( $engine ) {
|
117 |
case 'yuijs':
|
118 |
+
\W3TCL\Minify\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
119 |
+
\W3TCL\Minify\Minify_YUICompressor::$javaExecutable = $this->_config->get_string( 'minify.yuijs.path.java' );
|
120 |
+
\W3TCL\Minify\Minify_YUICompressor::$jarFile = $this->_config->get_string( 'minify.yuijs.path.jar' );
|
121 |
break;
|
122 |
|
123 |
case 'yuicss':
|
124 |
+
\W3TCL\Minify\Minify_YUICompressor::$tempDir = Util_File::create_tmp_dir();
|
125 |
+
\W3TCL\Minify\Minify_YUICompressor::$javaExecutable = $this->_config->get_string( 'minify.yuicss.path.java' );
|
126 |
+
\W3TCL\Minify\Minify_YUICompressor::$jarFile = $this->_config->get_string( 'minify.yuicss.path.jar' );
|
127 |
break;
|
128 |
|
129 |
case 'ccjs':
|
130 |
+
\W3TCL\Minify\Minify_ClosureCompiler::$tempDir = Util_File::create_tmp_dir();
|
131 |
+
\W3TCL\Minify\Minify_ClosureCompiler::$javaExecutable = $this->_config->get_string( 'minify.ccjs.path.java' );
|
132 |
+
\W3TCL\Minify\Minify_ClosureCompiler::$jarFile = $this->_config->get_string( 'minify.ccjs.path.jar' );
|
133 |
break;
|
134 |
}
|
135 |
}
|
Minify_Environment.php
CHANGED
@@ -364,6 +364,19 @@ class Minify_Environment {
|
|
364 |
return '';
|
365 |
}
|
366 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
367 |
/**
|
368 |
* Generates rules
|
369 |
*
|
@@ -373,7 +386,14 @@ class Minify_Environment {
|
|
373 |
function rules_core_generate_apache( $config ) {
|
374 |
$cache_uri = Util_Environment::url_to_uri(
|
375 |
Util_Environment::filename_to_url( W3TC_CACHE_MINIFY_DIR ) ) . '/';
|
376 |
-
$site_uri =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
377 |
|
378 |
$engine = $config->get_string( 'minify.engine' );
|
379 |
$browsercache = $config->get_boolean( 'browsercache.enabled' );
|
@@ -439,7 +459,7 @@ class Minify_Environment {
|
|
439 |
$first_regex_var = '$2';
|
440 |
}
|
441 |
|
442 |
-
$minify_uri =
|
443 |
|
444 |
$engine = $config->get_string( 'minify.engine' );
|
445 |
$browsercache = $config->get_boolean( 'browsercache.enabled' );
|
364 |
return '';
|
365 |
}
|
366 |
|
367 |
+
private function site_uri() {
|
368 |
+
$site_uri = rtrim( network_site_url( '', 'relative' ), '/' ) . '/';
|
369 |
+
|
370 |
+
/* There is a bug in WP where network_home_url can return
|
371 |
+
* a non-relative URI even though scheme is set to relative.
|
372 |
+
*/
|
373 |
+
if ( Util_Environment::is_url( $site_uri ) ) {
|
374 |
+
$site_uri = parse_url( $site_uri, PHP_URL_PATH );
|
375 |
+
}
|
376 |
+
|
377 |
+
return $site_uri;
|
378 |
+
}
|
379 |
+
|
380 |
/**
|
381 |
* Generates rules
|
382 |
*
|
386 |
function rules_core_generate_apache( $config ) {
|
387 |
$cache_uri = Util_Environment::url_to_uri(
|
388 |
Util_Environment::filename_to_url( W3TC_CACHE_MINIFY_DIR ) ) . '/';
|
389 |
+
$site_uri = $this->site_uri();
|
390 |
+
|
391 |
+
/* There is a bug in WP where network_home_url can return
|
392 |
+
* a non-relative URI even though scheme is set to relative.
|
393 |
+
*/
|
394 |
+
if ( Util_Environment::is_url( $site_uri ) ) {
|
395 |
+
$site_uri = parse_url( $site_uri, PHP_URL_PATH );
|
396 |
+
}
|
397 |
|
398 |
$engine = $config->get_string( 'minify.engine' );
|
399 |
$browsercache = $config->get_boolean( 'browsercache.enabled' );
|
459 |
$first_regex_var = '$2';
|
460 |
}
|
461 |
|
462 |
+
$minify_uri = $this->site_uri();
|
463 |
|
464 |
$engine = $config->get_string( 'minify.engine' );
|
465 |
$browsercache = $config->get_boolean( 'browsercache.enabled' );
|
Minify_MinifiedFileRequestHandler.php
CHANGED
@@ -117,18 +117,18 @@ class Minify_MinifiedFileRequestHandler {
|
|
117 |
* Set cache engine
|
118 |
*/
|
119 |
$cache = $this->_get_cache();
|
120 |
-
\
|
121 |
|
122 |
/**
|
123 |
* Set cache ID
|
124 |
*/
|
125 |
$cache_id = $this->get_cache_id( $file );
|
126 |
-
\
|
127 |
|
128 |
/**
|
129 |
* Set logger
|
130 |
*/
|
131 |
-
\Minify_Logger::setLogger( array(
|
132 |
$this,
|
133 |
'debug_error' ) );
|
134 |
|
@@ -233,13 +233,13 @@ class Minify_MinifiedFileRequestHandler {
|
|
233 |
|
234 |
$return = array();
|
235 |
try {
|
236 |
-
$return = \
|
237 |
} catch ( \Exception $exception ) {
|
238 |
return $this->finish_with_error( $exception->getMessage(), $quiet );
|
239 |
}
|
240 |
|
241 |
-
if ( !is_null( \
|
242 |
-
$this->_handle_error( \
|
243 |
|
244 |
$state = Dispatcher::config_state_master();
|
245 |
if ( !$this->_error_occurred && $state->get_boolean( 'minify.show_note_minify_error' ) ) {
|
@@ -256,7 +256,7 @@ class Minify_MinifiedFileRequestHandler {
|
|
256 |
|
257 |
|
258 |
public function w3tc_usage_statistics_of_request( $storage ) {
|
259 |
-
$stats = \
|
260 |
if ( count( $stats ) > 0 ) {
|
261 |
$storage->counter_add( 'minify_requests_total', 1 );
|
262 |
if ( $stats['content_type'] == 'text/css' ) {
|
@@ -434,7 +434,7 @@ class Minify_MinifiedFileRequestHandler {
|
|
434 |
$document_root = Util_Environment::document_root();
|
435 |
|
436 |
foreach ( $files as $file ) {
|
437 |
-
if ( is_a( $file, '\Minify_Source' ) ) {
|
438 |
$path = $file->filepath;
|
439 |
} else {
|
440 |
$path = rtrim( $document_root, '/' ) . '/' . ltrim( $file, '/' );
|
@@ -624,7 +624,7 @@ class Minify_MinifiedFileRequestHandler {
|
|
624 |
* @return Minify_Source
|
625 |
*/
|
626 |
function _get_minify_source( $file_path, $url ) {
|
627 |
-
return new \Minify_Source( array(
|
628 |
'filepath' => $file_path,
|
629 |
'minifyOptions' => array(
|
630 |
'prependRelativePath' => $url
|
@@ -726,11 +726,11 @@ class Minify_MinifiedFileRequestHandler {
|
|
726 |
}
|
727 |
|
728 |
if ( !is_null( $inner_cache ) ) {
|
729 |
-
$cache = new \Minify_Cache_W3TCDerived( $inner_cache );
|
730 |
} else {
|
731 |
// case 'file' or fallback
|
732 |
|
733 |
-
$cache = new \Minify_Cache_File(
|
734 |
Util_Environment::cache_blog_minify_dir(),
|
735 |
array(
|
736 |
'.htaccess',
|
117 |
* Set cache engine
|
118 |
*/
|
119 |
$cache = $this->_get_cache();
|
120 |
+
\W3TCL\Minify\Minify::setCache( $cache );
|
121 |
|
122 |
/**
|
123 |
* Set cache ID
|
124 |
*/
|
125 |
$cache_id = $this->get_cache_id( $file );
|
126 |
+
\W3TCL\Minify\Minify::setCacheId( $file );
|
127 |
|
128 |
/**
|
129 |
* Set logger
|
130 |
*/
|
131 |
+
\W3TCL\Minify\Minify_Logger::setLogger( array(
|
132 |
$this,
|
133 |
'debug_error' ) );
|
134 |
|
233 |
|
234 |
$return = array();
|
235 |
try {
|
236 |
+
$return = \W3TCL\Minify\Minify::serve( 'MinApp', $serve_options );
|
237 |
} catch ( \Exception $exception ) {
|
238 |
return $this->finish_with_error( $exception->getMessage(), $quiet );
|
239 |
}
|
240 |
|
241 |
+
if ( !is_null( \W3TCL\Minify\Minify::$recoverableError ) )
|
242 |
+
$this->_handle_error( \W3TCL\Minify\Minify::$recoverableError );
|
243 |
|
244 |
$state = Dispatcher::config_state_master();
|
245 |
if ( !$this->_error_occurred && $state->get_boolean( 'minify.show_note_minify_error' ) ) {
|
256 |
|
257 |
|
258 |
public function w3tc_usage_statistics_of_request( $storage ) {
|
259 |
+
$stats = \W3TCL\Minify\Minify::getUsageStatistics();
|
260 |
if ( count( $stats ) > 0 ) {
|
261 |
$storage->counter_add( 'minify_requests_total', 1 );
|
262 |
if ( $stats['content_type'] == 'text/css' ) {
|
434 |
$document_root = Util_Environment::document_root();
|
435 |
|
436 |
foreach ( $files as $file ) {
|
437 |
+
if ( is_a( $file, '\W3TCL\Minify\Minify_Source' ) ) {
|
438 |
$path = $file->filepath;
|
439 |
} else {
|
440 |
$path = rtrim( $document_root, '/' ) . '/' . ltrim( $file, '/' );
|
624 |
* @return Minify_Source
|
625 |
*/
|
626 |
function _get_minify_source( $file_path, $url ) {
|
627 |
+
return new \W3TCL\Minify\Minify_Source( array(
|
628 |
'filepath' => $file_path,
|
629 |
'minifyOptions' => array(
|
630 |
'prependRelativePath' => $url
|
726 |
}
|
727 |
|
728 |
if ( !is_null( $inner_cache ) ) {
|
729 |
+
$cache = new \W3TCL\Minify\Minify_Cache_W3TCDerived( $inner_cache );
|
730 |
} else {
|
731 |
// case 'file' or fallback
|
732 |
|
733 |
+
$cache = new \W3TCL\Minify\Minify_Cache_File(
|
734 |
Util_Environment::cache_blog_minify_dir(),
|
735 |
array(
|
736 |
'.htaccess',
|
Minify_Plugin.php
CHANGED
@@ -513,7 +513,7 @@ class Minify_Plugin {
|
|
513 |
$ignored_comments = $this->_config->get_array( 'minify.html.comments.ignore' );
|
514 |
|
515 |
if ( count( $ignored_comments ) ) {
|
516 |
-
$ignored_comments_preserver = new \Minify_IgnoredCommentPreserver();
|
517 |
$ignored_comments_preserver->setIgnoredComments( $ignored_comments );
|
518 |
|
519 |
$html = $ignored_comments_preserver->search( $html );
|
@@ -531,7 +531,7 @@ class Minify_Plugin {
|
|
531 |
|
532 |
$w3_minifier->init( $js_engine );
|
533 |
|
534 |
-
$html = \Minify_Inline_JavaScript::minify( $html, $js_minifier, $js_options );
|
535 |
}
|
536 |
|
537 |
if ( $this->_config->get_boolean( 'minify.html.inline.css' ) ) {
|
@@ -546,7 +546,7 @@ class Minify_Plugin {
|
|
546 |
|
547 |
$w3_minifier->init( $css_engine );
|
548 |
|
549 |
-
$html = \Minify_Inline_CSS::minify( $html, $css_minifier, $css_options );
|
550 |
}
|
551 |
|
552 |
$engine = $this->_config->get_string( 'minify.html.engine' );
|
513 |
$ignored_comments = $this->_config->get_array( 'minify.html.comments.ignore' );
|
514 |
|
515 |
if ( count( $ignored_comments ) ) {
|
516 |
+
$ignored_comments_preserver = new \W3TCL\Minify\Minify_IgnoredCommentPreserver();
|
517 |
$ignored_comments_preserver->setIgnoredComments( $ignored_comments );
|
518 |
|
519 |
$html = $ignored_comments_preserver->search( $html );
|
531 |
|
532 |
$w3_minifier->init( $js_engine );
|
533 |
|
534 |
+
$html = \W3TCL\Minify\Minify_Inline_JavaScript::minify( $html, $js_minifier, $js_options );
|
535 |
}
|
536 |
|
537 |
if ( $this->_config->get_boolean( 'minify.html.inline.css' ) ) {
|
546 |
|
547 |
$w3_minifier->init( $css_engine );
|
548 |
|
549 |
+
$html = \W3TCL\Minify\Minify_Inline_CSS::minify( $html, $css_minifier, $css_options );
|
550 |
}
|
551 |
|
552 |
$engine = $this->_config->get_string( 'minify.html.engine' );
|
Util_Ui.php
CHANGED
@@ -932,6 +932,7 @@ class Util_Ui {
|
|
932 |
__( 'Dedicated / Virtual Server:', 'w3-total-cache' ),
|
933 |
__( 'Multiple Servers:', 'w3-total-cache' )
|
934 |
),
|
|
|
935 |
) );
|
936 |
}
|
937 |
|
932 |
__( 'Dedicated / Virtual Server:', 'w3-total-cache' ),
|
933 |
__( 'Multiple Servers:', 'w3-total-cache' )
|
934 |
),
|
935 |
+
'control_after' => isset( $a['control_after'] ) ? $a['control_after'] : null,
|
936 |
) );
|
937 |
}
|
938 |
|
inc/mime/other.php
CHANGED
@@ -36,6 +36,7 @@ return array(
|
|
36 |
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
37 |
'odt' => 'application/vnd.oasis.opendocument.text',
|
38 |
'ogg' => 'audio/ogg',
|
|
|
39 |
'pdf' => 'application/pdf',
|
40 |
'png' => 'image/png',
|
41 |
'pot|pps|ppt|pptx' => 'application/vnd.ms-powerpoint',
|
36 |
'ods' => 'application/vnd.oasis.opendocument.spreadsheet',
|
37 |
'odt' => 'application/vnd.oasis.opendocument.text',
|
38 |
'ogg' => 'audio/ogg',
|
39 |
+
'ogv' => 'video/ogg',
|
40 |
'pdf' => 'application/pdf',
|
41 |
'png' => 'image/png',
|
42 |
'pot|pps|ppt|pptx' => 'application/vnd.ms-powerpoint',
|
inc/options/general.php
CHANGED
@@ -130,36 +130,63 @@ Util_Ui::config_overloading_button( array(
|
|
130 |
|
131 |
<table class="form-table">
|
132 |
<?php
|
133 |
-
Util_Ui::config_item(
|
134 |
-
|
135 |
-
'
|
|
|
136 |
'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
|
137 |
-
'description'
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
'radiogroup_values' => array(
|
144 |
'1' => __( 'Auto', 'w3-total-cache' ),
|
145 |
-
'0' => __( 'Manual', 'w3-total-cache' )
|
146 |
),
|
147 |
-
'description'
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
155 |
'selectbox_values' => array(
|
156 |
-
'html'
|
157 |
'htmltidy' => array(
|
158 |
'disabled' => !Util_Installed::tidy(),
|
159 |
-
'label'
|
160 |
-
)
|
161 |
-
)
|
162 |
-
|
|
|
|
|
|
|
|
|
|
|
163 |
Util_Ui::config_item( array(
|
164 |
'key' => 'minify.js.engine',
|
165 |
'control' => 'selectbox',
|
130 |
|
131 |
<table class="form-table">
|
132 |
<?php
|
133 |
+
Util_Ui::config_item(
|
134 |
+
array(
|
135 |
+
'key' => 'minify.enabled',
|
136 |
+
'control' => 'checkbox',
|
137 |
'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
|
138 |
+
'description' => __( 'Minification can decrease file size of <acronym title="Hypertext Markup Language">HTML</acronym>, <acronym title="Cascading Style Sheet">CSS</acronym>, <acronym title="JavaScript">JS</acronym> and feeds respectively by ~10% on average.', 'w3-total-cache' ),
|
139 |
+
'control_after' => ' <a class="w3tc-control-after" target="_blank" href="https://www.boldgrid.com/support/w3-total-cache/w3-total-cache-minify-faq/?utm_source=w3tc&utm_medium=learn_more_links&utm_campaign=minify_faq" title="' .
|
140 |
+
__('Minify frequently asked questions', 'w3-total-cache' ) . '">' . __( 'Learn more', 'w3-total-cache' ) .
|
141 |
+
'<span class="dashicons dashicons-external"></span></a>',
|
142 |
+
)
|
143 |
+
);
|
144 |
+
|
145 |
+
Util_Ui::config_item(
|
146 |
+
array(
|
147 |
+
'key' => 'minify.auto',
|
148 |
+
'value' => ( $this->_config->get_boolean( 'minify.auto' ) ? 1 : 0 ),
|
149 |
+
'control' => 'radiogroup',
|
150 |
'radiogroup_values' => array(
|
151 |
'1' => __( 'Auto', 'w3-total-cache' ),
|
152 |
+
'0' => __( 'Manual', 'w3-total-cache' ),
|
153 |
),
|
154 |
+
'description' => __(
|
155 |
+
'Select manual mode to use fields on the minify settings tab to specify files to be minified, otherwise files will be minified automatically.',
|
156 |
+
'w3-total-cache'
|
157 |
+
),
|
158 |
+
'control_after' => ' <a class="w3tc-control-after" target="_blank" href="https://www.boldgrid.com/support/w3-total-cache/how-to-use-manual-minify-for-css-and-js/?utm_source=w3tc&utm_medium=learn_more_links&utm_campaign=manual_minify#difference-between-auto-and-manual-minify" title="'
|
159 |
+
. __( 'How to use manual minify', 'w3-total-cache' ) . '">' . __( 'Learn more', 'w3-total-cache' ) .
|
160 |
+
'<span class="dashicons dashicons-external"></span></a>',
|
161 |
+
)
|
162 |
+
);
|
163 |
+
|
164 |
+
Util_Ui::config_item_engine(
|
165 |
+
array(
|
166 |
+
'key' => 'minify.engine',
|
167 |
+
'control_after' => ' <a class="w3tc-control-after" target="_blank" href="https://www.boldgrid.com/support/w3-total-cache/choosing-a-minification-method-for-w3-total-cache/?utm_source=w3tc&utm_medium=learn_more_links&utm_campaign=minify_engine" title="' .
|
168 |
+
__('Choosing a minification method', 'w3-total-cache' ) . '">' . __( 'Learn more', 'w3-total-cache' ) .
|
169 |
+
'<span class="dashicons dashicons-external"></span></a>',
|
170 |
+
)
|
171 |
+
);
|
172 |
+
|
173 |
+
Util_Ui::config_item(
|
174 |
+
array(
|
175 |
+
'key' => 'minify.html.engine',
|
176 |
+
'control' => 'selectbox',
|
177 |
'selectbox_values' => array(
|
178 |
+
'html' => __( 'Minify (default)', 'w3-total-cache' ),
|
179 |
'htmltidy' => array(
|
180 |
'disabled' => !Util_Installed::tidy(),
|
181 |
+
'label' => __( 'HTML Tidy', 'w3-total-cache' ),
|
182 |
+
),
|
183 |
+
),
|
184 |
+
'control_after' => ' <a class="w3tc-control-after" target="_blank" href="https://www.boldgrid.com/support/w3-total-cache/minify/html-minify-or-tidy/?utm_source=w3tc&utm_medium=learn_more_links&utm_campaign=minify_html#minify-default" title="' .
|
185 |
+
__('How to use minify HTML', 'w3-total-cache' ) . '">' . __( 'Learn more', 'w3-total-cache' ) .
|
186 |
+
'<span class="dashicons dashicons-external"></span></a>',
|
187 |
+
)
|
188 |
+
);
|
189 |
+
|
190 |
Util_Ui::config_item( array(
|
191 |
'key' => 'minify.js.engine',
|
192 |
'control' => 'selectbox',
|
ini/apache_conf/mod_expires.conf
CHANGED
@@ -36,6 +36,7 @@ ExpiresByType audio/midi A31536000
|
|
36 |
ExpiresByType video/quicktime A31536000
|
37 |
ExpiresByType audio/mpeg A31536000
|
38 |
ExpiresByType video/mp4 A31536000
|
|
|
39 |
ExpiresByType video/mpeg A31536000
|
40 |
ExpiresByType application/vnd.ms-project A31536000
|
41 |
ExpiresByType application/x-font-otf A31536000
|
36 |
ExpiresByType video/quicktime A31536000
|
37 |
ExpiresByType audio/mpeg A31536000
|
38 |
ExpiresByType video/mp4 A31536000
|
39 |
+
ExpiresByType video/ogg A31536000
|
40 |
ExpiresByType video/mpeg A31536000
|
41 |
ExpiresByType application/vnd.ms-project A31536000
|
42 |
ExpiresByType application/x-font-otf A31536000
|
ini/apache_conf/mod_mime.conf
CHANGED
@@ -32,6 +32,7 @@
|
|
32 |
AddType video/quicktime .mov .qt
|
33 |
AddType audio/mpeg .mp3 .m4a
|
34 |
AddType video/mp4 .mp4 .m4v
|
|
|
35 |
AddType video/mpeg .mpeg .mpg .mpe
|
36 |
AddType application/vnd.ms-project .mpp
|
37 |
AddType application/x-font-otf .otf
|
32 |
AddType video/quicktime .mov .qt
|
33 |
AddType audio/mpeg .mp3 .m4a
|
34 |
AddType video/mp4 .mp4 .m4v
|
35 |
+
AddType video/ogg .ogv
|
36 |
AddType video/mpeg .mpeg .mpg .mpe
|
37 |
AddType application/vnd.ms-project .mpp
|
38 |
AddType application/x-font-otf .otf
|
lib/Aws/aws-autoloader.php
CHANGED
@@ -1159,7 +1159,10 @@ spl_autoload_register(function ($class) use ($mapping) {
|
|
1159 |
}
|
1160 |
}, true);
|
1161 |
|
1162 |
-
|
|
|
|
|
|
|
1163 |
require __DIR__ . '/GuzzleHttp/functions_include.php';
|
1164 |
require __DIR__ . '/GuzzleHttp/Psr7/functions_include.php';
|
1165 |
require __DIR__ . '/GuzzleHttp/Promise/functions_include.php';
|
1159 |
}
|
1160 |
}, true);
|
1161 |
|
1162 |
+
if ( ! function_exists( 'Aws\parse_ini_file' ) ) {
|
1163 |
+
require __DIR__ . '/Aws/functions.php';
|
1164 |
+
}
|
1165 |
+
|
1166 |
require __DIR__ . '/GuzzleHttp/functions_include.php';
|
1167 |
require __DIR__ . '/GuzzleHttp/Psr7/functions_include.php';
|
1168 |
require __DIR__ . '/GuzzleHttp/Promise/functions_include.php';
|
lib/Azure/GuzzleHttp/Handler/CurlFactory.php
CHANGED
@@ -64,7 +64,7 @@ class CurlFactory implements CurlFactoryInterface
|
|
64 |
$resource = $easy->handle;
|
65 |
unset($easy->handle);
|
66 |
|
67 |
-
if (count($this->handles) >= $this->maxHandles) {
|
68 |
curl_close($resource);
|
69 |
} else {
|
70 |
// Remove all callback functions as they can hold onto references
|
64 |
$resource = $easy->handle;
|
65 |
unset($easy->handle);
|
66 |
|
67 |
+
if ( $this->handles != null && count($this->handles) >= $this->maxHandles ) {
|
68 |
curl_close($resource);
|
69 |
} else {
|
70 |
// Remove all callback functions as they can hold onto references
|
lib/Minify/DooDigestAuth.php
DELETED
@@ -1,121 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* DooDigestAuth class file.
|
4 |
-
*
|
5 |
-
* @author Leng Sheng Hong <darkredz@gmail.com>
|
6 |
-
* @link http://www.doophp.com/
|
7 |
-
* @copyright Copyright © 2009 Leng Sheng Hong
|
8 |
-
* @license http://www.doophp.com/license
|
9 |
-
*/
|
10 |
-
|
11 |
-
/**
|
12 |
-
* Handles HTTP digest authentication
|
13 |
-
*
|
14 |
-
* <p>HTTP digest authentication can be used with the URI router.
|
15 |
-
* HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption.
|
16 |
-
* If you are running PHP on Apache in CGI/FastCGI mode, you would need to
|
17 |
-
* add the following line to your .htaccess for digest auth to work correctly.</p>
|
18 |
-
* <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code>
|
19 |
-
*
|
20 |
-
* <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p>
|
21 |
-
*
|
22 |
-
* @author Leng Sheng Hong <darkredz@gmail.com>
|
23 |
-
* @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22
|
24 |
-
* @package doo.auth
|
25 |
-
* @since 1.0
|
26 |
-
*/
|
27 |
-
class DooDigestAuth{
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Authenticate against a list of username and passwords.
|
31 |
-
*
|
32 |
-
* <p>HTTP Digest Authentication doesn't work with PHP in CGI mode,
|
33 |
-
* you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p>
|
34 |
-
*
|
35 |
-
* @param string $realm Name of the authentication session
|
36 |
-
* @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2')
|
37 |
-
* @param string $fail_msg Message to be displayed if the User cancel the login
|
38 |
-
* @param string $fail_url URL to be redirect if the User cancel the login
|
39 |
-
* @return string The username if login success.
|
40 |
-
*/
|
41 |
-
public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){
|
42 |
-
$realm = "Restricted area - $realm";
|
43 |
-
|
44 |
-
//user => password
|
45 |
-
//$users = array('admin' => '1234', 'guest' => 'guest');
|
46 |
-
if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){
|
47 |
-
$_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
48 |
-
}
|
49 |
-
|
50 |
-
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
|
51 |
-
header('WWW-Authenticate: Digest realm="'.$realm.
|
52 |
-
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
53 |
-
header('HTTP/1.1 401 Unauthorized');
|
54 |
-
if($fail_msg!=NULL)
|
55 |
-
die($fail_msg);
|
56 |
-
if($fail_url!=NULL)
|
57 |
-
die("<script>window.location.href = '$fail_url'</script>");
|
58 |
-
exit;
|
59 |
-
}
|
60 |
-
|
61 |
-
// analyze the PHP_AUTH_DIGEST variable
|
62 |
-
if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){
|
63 |
-
header('WWW-Authenticate: Digest realm="'.$realm.
|
64 |
-
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
65 |
-
header('HTTP/1.1 401 Unauthorized');
|
66 |
-
if($fail_msg!=NULL)
|
67 |
-
die($fail_msg);
|
68 |
-
if($fail_url!=NULL)
|
69 |
-
die("<script>window.location.href = '$fail_url'</script>");
|
70 |
-
exit;
|
71 |
-
}
|
72 |
-
|
73 |
-
// generate the valid response
|
74 |
-
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
|
75 |
-
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
|
76 |
-
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
|
77 |
-
|
78 |
-
if ($data['response'] != $valid_response){
|
79 |
-
header('HTTP/1.1 401 Unauthorized');
|
80 |
-
header('WWW-Authenticate: Digest realm="'.$realm.
|
81 |
-
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
|
82 |
-
if($fail_msg!=NULL)
|
83 |
-
die($fail_msg);
|
84 |
-
if($fail_url!=NULL)
|
85 |
-
die("<script>window.location.href = '$fail_url'</script>");
|
86 |
-
exit;
|
87 |
-
}
|
88 |
-
|
89 |
-
// ok, valid username & password
|
90 |
-
return $data['username'];
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Method to parse the http auth header, works with IE.
|
95 |
-
*
|
96 |
-
* Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do.
|
97 |
-
*
|
98 |
-
* @param string $txt header string to parse
|
99 |
-
* @return array An assoc array of the digest auth session
|
100 |
-
*/
|
101 |
-
private static function http_digest_parse($txt)
|
102 |
-
{
|
103 |
-
$res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match);
|
104 |
-
$data['username'] = (isset($match[1]))?$match[1]:null;
|
105 |
-
$res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match);
|
106 |
-
$data['nonce'] = $match[1];
|
107 |
-
$res = preg_match('/nc=([0-9]+)/i', $txt, $match);
|
108 |
-
$data['nc'] = $match[1];
|
109 |
-
$res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match);
|
110 |
-
$data['cnonce'] = $match[1];
|
111 |
-
$res = preg_match('/qop=([^,]+)/i', $txt, $match);
|
112 |
-
$data['qop'] = str_replace('"','',$match[1]);
|
113 |
-
$res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match);
|
114 |
-
$data['uri'] = $match[1];
|
115 |
-
$res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match);
|
116 |
-
$data['response'] = $match[1];
|
117 |
-
return $data;
|
118 |
-
}
|
119 |
-
|
120 |
-
|
121 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/Minify/HTTP/ConditionalGet.php
CHANGED
@@ -1,6 +1,8 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
-
* Class HTTP_ConditionalGet
|
4 |
* @package Minify
|
5 |
* @subpackage HTTP
|
6 |
*/
|
@@ -21,7 +23,7 @@
|
|
21 |
* }
|
22 |
* echo $content;
|
23 |
* </code>
|
24 |
-
*
|
25 |
* E.g. Shortcut for the above
|
26 |
* <code>
|
27 |
* HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache
|
@@ -40,7 +42,7 @@
|
|
40 |
* }
|
41 |
* echo $content;
|
42 |
* </code>
|
43 |
-
*
|
44 |
* E.g. Static content with some static includes:
|
45 |
* <code>
|
46 |
* // before content
|
@@ -62,353 +64,353 @@
|
|
62 |
*/
|
63 |
class HTTP_ConditionalGet {
|
64 |
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
* 'contentHash': (string) if given, only the ETag header can be sent with
|
91 |
-
* content (only HTTP1.1 clients can conditionally GET). The given string
|
92 |
-
* should be short with no quote characters and always change when the
|
93 |
-
* resource changes (recommend md5()). This is not needed/used if
|
94 |
-
* lastModifiedTime is given.
|
95 |
-
*
|
96 |
-
* 'eTag': (string) if given, this will be used as the ETag header rather
|
97 |
-
* than values based on lastModifiedTime or contentHash. Also the encoding
|
98 |
-
* string will not be appended to the given value as described above.
|
99 |
-
*
|
100 |
-
* 'invalidate': (bool) if true, the client cache will be considered invalid
|
101 |
-
* without testing. Effectively this disables conditional GET.
|
102 |
-
* (default false)
|
103 |
-
*
|
104 |
-
* 'maxAge': (int) if given, this will set the Cache-Control max-age in
|
105 |
-
* seconds, and also set the Expires header to the equivalent GMT date.
|
106 |
-
* After the max-age period has passed, the browser will again send a
|
107 |
-
* conditional GET to revalidate its cache.
|
108 |
-
*/
|
109 |
-
public function __construct($spec)
|
110 |
-
{
|
111 |
-
if (isset($spec['cacheHeaders']) && is_array($spec['cacheHeaders'])) {
|
112 |
-
$this->_cacheHeaders = $spec['cacheHeaders'];
|
113 |
-
}
|
114 |
-
|
115 |
-
$scope = ($this->_cacheHeaders['cacheheaders_enabled'] && $this->_cacheHeaders['cacheheaders'] != 'no_cache') ? 'public' : 'private';
|
116 |
-
$maxAge = 0;
|
117 |
-
|
118 |
-
$this->_headers['Pragma'] = $scope;
|
119 |
-
|
120 |
-
// For backwards compatibility (will be removed in the future)
|
121 |
-
if (isset($spec['setExpires'])
|
122 |
-
&& is_numeric($spec['setExpires'])
|
123 |
-
&& ! isset($spec['maxAge'])) {
|
124 |
-
$spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME'];
|
125 |
-
}
|
126 |
-
if (isset($spec['maxAge']) && $this->_cacheHeaders['expires_enabled'] && $spec['maxAge']) {
|
127 |
-
$maxAge = $spec['maxAge'];
|
128 |
-
$this->_headers['Expires'] = self::gmtDate(
|
129 |
-
$_SERVER['REQUEST_TIME'] + $spec['maxAge']
|
130 |
-
);
|
131 |
-
}
|
132 |
-
$etagAppend = '';
|
133 |
-
if (isset($spec['encoding'])) {
|
134 |
-
$this->_stripEtag = true;
|
135 |
-
$this->_headers['Vary'] = 'Accept-Encoding';
|
136 |
-
if ('' !== $spec['encoding']) {
|
137 |
-
if (0 === strpos($spec['encoding'], 'x-')) {
|
138 |
-
$spec['encoding'] = substr($spec['encoding'], 2);
|
139 |
-
}
|
140 |
-
$etagAppend = ';' . substr($spec['encoding'], 0, 2);
|
141 |
-
}
|
142 |
-
}
|
143 |
-
if (isset($spec['lastModifiedTime'])) {
|
144 |
-
$this->_setLastModified($spec['lastModifiedTime']);
|
145 |
-
if (isset($spec['eTag'])) { // Use it
|
146 |
-
$this->_setEtag($spec['eTag'], $scope);
|
147 |
-
} else { // base both headers on time
|
148 |
-
$this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope);
|
149 |
-
}
|
150 |
-
} elseif (isset($spec['eTag'])) { // Use it
|
151 |
-
$this->_setEtag($spec['eTag'], $scope);
|
152 |
-
} elseif (isset($spec['contentHash'])) { // Use the hash as the ETag
|
153 |
-
$this->_setEtag($spec['contentHash'] . $etagAppend, $scope);
|
154 |
-
}
|
155 |
-
|
156 |
-
if ($this->_cacheHeaders['cacheheaders_enabled']) {
|
157 |
-
switch ($this->_cacheHeaders['cacheheaders']) {
|
158 |
-
case 'cache':
|
159 |
-
$this->_headers['Cache-Control'] = 'public';
|
160 |
-
break;
|
161 |
|
162 |
-
|
163 |
-
|
164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
case 'cache_maxage':
|
175 |
-
$this->_headers['Cache-Control'] = "max-age={$maxAge}, {$scope}, must-revalidate, proxy-revalidate";
|
176 |
-
break;
|
177 |
-
|
178 |
-
case 'no_cache':
|
179 |
-
$this->_headers['Cache-Control'] = 'max-age=0, private, no-store, no-cache, must-revalidate';
|
180 |
-
break;
|
181 |
-
}
|
182 |
-
}
|
183 |
-
|
184 |
-
/**
|
185 |
-
* Disable caching for preview mode
|
186 |
-
*/
|
187 |
-
if (\W3TC\Util_Environment::is_preview_mode()) {
|
188 |
-
$this->_headers = array_merge($this->_headers, array(
|
189 |
-
'Pragma' => 'private',
|
190 |
-
'Cache-Control' => 'private'
|
191 |
-
));
|
192 |
-
}
|
193 |
-
|
194 |
-
// invalidate cache if disabled, otherwise check
|
195 |
-
$this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate'])
|
196 |
-
? false
|
197 |
-
: $this->_isCacheValid();
|
198 |
-
}
|
199 |
-
|
200 |
-
/**
|
201 |
-
* Get array of output headers to be sent
|
202 |
-
*
|
203 |
-
* In the case of 304 responses, this array will only contain the response
|
204 |
-
* code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified')
|
205 |
-
*
|
206 |
-
* Otherwise something like:
|
207 |
-
* <code>
|
208 |
-
* array(
|
209 |
-
* 'Cache-Control' => 'max-age=0, public'
|
210 |
-
* ,'ETag' => '"foobar"'
|
211 |
-
* )
|
212 |
-
* </code>
|
213 |
-
*
|
214 |
-
* @return array
|
215 |
-
*/
|
216 |
-
public function getHeaders()
|
217 |
-
{
|
218 |
-
return $this->_headers;
|
219 |
-
}
|
220 |
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
* With most PHP configs, as long as you don't flush() output, this method
|
225 |
-
* is not needed and PHP will buffer all output and set Content-Length for
|
226 |
-
* you. Otherwise you'll want to call this to let the client know up front.
|
227 |
-
*
|
228 |
-
* @param int $bytes
|
229 |
-
*
|
230 |
-
* @return int copy of input $bytes
|
231 |
-
*/
|
232 |
-
public function setContentLength($bytes)
|
233 |
-
{
|
234 |
-
return $this->_headers['Content-Length'] = $bytes;
|
235 |
-
}
|
236 |
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
*
|
246 |
-
* @return null
|
247 |
-
*/
|
248 |
-
public function sendHeaders()
|
249 |
-
{
|
250 |
-
$headers = $this->_headers;
|
251 |
-
if (array_key_exists('_responseCode', $headers)) {
|
252 |
-
// FastCGI environments require 3rd arg to header() to be set
|
253 |
-
list(, $code) = explode(' ', $headers['_responseCode'], 3);
|
254 |
-
header($headers['_responseCode'], true, $code);
|
255 |
-
unset($headers['_responseCode']);
|
256 |
-
}
|
257 |
-
foreach ($headers as $name => $val) {
|
258 |
-
header($name . ': ' . $val);
|
259 |
-
}
|
260 |
-
}
|
261 |
-
|
262 |
-
/**
|
263 |
-
* Exit if the client's cache is valid for this resource
|
264 |
-
*
|
265 |
-
* This is a convenience method for common use of the class
|
266 |
-
*
|
267 |
-
* @param int $lastModifiedTime if given, both ETag AND Last-Modified headers
|
268 |
-
* will be sent with content. This is recommended.
|
269 |
-
*
|
270 |
-
* @param bool $isPublic (default false) if true, the Cache-Control header
|
271 |
-
* will contain "public", allowing proxies to cache the content. Otherwise
|
272 |
-
* "private" will be sent, allowing only browser caching.
|
273 |
-
*
|
274 |
-
* @param array $options (default empty) additional options for constructor
|
275 |
-
*/
|
276 |
-
public static function check($lastModifiedTime = null, $isPublic = false, $options = array())
|
277 |
-
{
|
278 |
-
if (null !== $lastModifiedTime) {
|
279 |
-
$options['lastModifiedTime'] = (int)$lastModifiedTime;
|
280 |
-
}
|
281 |
-
$options['isPublic'] = (bool)$isPublic;
|
282 |
-
$cg = new HTTP_ConditionalGet($options);
|
283 |
-
$cg->sendHeaders();
|
284 |
-
if ($cg->cacheIsValid) {
|
285 |
-
exit();
|
286 |
-
}
|
287 |
-
}
|
288 |
-
|
289 |
-
|
290 |
-
/**
|
291 |
-
* Get a GMT formatted date for use in HTTP headers
|
292 |
-
*
|
293 |
-
* <code>
|
294 |
-
* header('Expires: ' . HTTP_ConditionalGet::gmtdate($time));
|
295 |
-
* </code>
|
296 |
-
*
|
297 |
-
* @param int $time unix timestamp
|
298 |
-
*
|
299 |
-
* @return string
|
300 |
-
*/
|
301 |
-
public static function gmtDate($time)
|
302 |
-
{
|
303 |
-
return gmdate('D, d M Y H:i:s \G\M\T', $time);
|
304 |
-
}
|
305 |
-
|
306 |
-
protected $_headers = array();
|
307 |
-
protected $_lmTime = null;
|
308 |
-
protected $_etag = null;
|
309 |
-
protected $_stripEtag = false;
|
310 |
-
protected $_cacheHeaders = array(
|
311 |
-
'use_etag' => true,
|
312 |
-
'expires_enabled' => true,
|
313 |
-
'cacheheaders_enabled' => true,
|
314 |
-
'cacheheaders' => 'cache_validation'
|
315 |
-
);
|
316 |
-
|
317 |
-
/**
|
318 |
-
* @param string $hash
|
319 |
-
*
|
320 |
-
* @param string $scope
|
321 |
-
*/
|
322 |
-
protected function _setEtag($hash, $scope)
|
323 |
-
{
|
324 |
-
$this->_etag = '"' . substr($scope, 0, 3) . $hash . '"';
|
325 |
-
|
326 |
-
if ($this->_cacheHeaders['use_etag'])
|
327 |
-
$this->_headers['ETag'] = $this->_etag;
|
328 |
-
}
|
329 |
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
338 |
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
// possibly has a valid cache.
|
350 |
-
return false;
|
351 |
-
}
|
352 |
-
$isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified());
|
353 |
-
if ($isValid) {
|
354 |
-
$this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified';
|
355 |
-
}
|
356 |
-
return $isValid;
|
357 |
-
}
|
358 |
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
foreach ($clientEtags as $clientEtag) {
|
372 |
-
if ($this->normalizeEtag($clientEtag) === $compareTo) {
|
373 |
-
// respond with the client's matched ETag, even if it's not what
|
374 |
-
// we would've sent by default
|
375 |
-
if ($this->_cacheHeaders['use_etag'])
|
376 |
-
$this->_headers['ETag'] = trim($clientEtag);
|
377 |
-
return true;
|
378 |
-
}
|
379 |
-
}
|
380 |
-
return false;
|
381 |
-
}
|
382 |
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
+
|
4 |
/**
|
5 |
+
* Class HTTP_ConditionalGet
|
6 |
* @package Minify
|
7 |
* @subpackage HTTP
|
8 |
*/
|
23 |
* }
|
24 |
* echo $content;
|
25 |
* </code>
|
26 |
+
*
|
27 |
* E.g. Shortcut for the above
|
28 |
* <code>
|
29 |
* HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache
|
42 |
* }
|
43 |
* echo $content;
|
44 |
* </code>
|
45 |
+
*
|
46 |
* E.g. Static content with some static includes:
|
47 |
* <code>
|
48 |
* // before content
|
64 |
*/
|
65 |
class HTTP_ConditionalGet {
|
66 |
|
67 |
+
/**
|
68 |
+
* Does the client have a valid copy of the requested resource?
|
69 |
+
*
|
70 |
+
* You'll want to check this after instantiating the object. If true, do
|
71 |
+
* not send content, just call sendHeaders() if you haven't already.
|
72 |
+
*
|
73 |
+
* @var bool
|
74 |
+
*/
|
75 |
+
public $cacheIsValid = null;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* @param array $spec options
|
79 |
+
*
|
80 |
+
* 'isPublic': (bool) if false, the Cache-Control header will contain
|
81 |
+
* "private", allowing only browser caching. (default false)
|
82 |
+
*
|
83 |
+
* 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers
|
84 |
+
* will be sent with content. This is recommended.
|
85 |
+
*
|
86 |
+
* 'encoding': (string) if set, the header "Vary: Accept-Encoding" will
|
87 |
+
* always be sent and a truncated version of the encoding will be appended
|
88 |
+
* to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient
|
89 |
+
* checking of the client's If-None-Match header, as the encoding portion of
|
90 |
+
* the ETag will be stripped before comparison.
|
91 |
+
*
|
92 |
+
* 'contentHash': (string) if given, only the ETag header can be sent with
|
93 |
+
* content (only HTTP1.1 clients can conditionally GET). The given string
|
94 |
+
* should be short with no quote characters and always change when the
|
95 |
+
* resource changes (recommend md5()). This is not needed/used if
|
96 |
+
* lastModifiedTime is given.
|
97 |
+
*
|
98 |
+
* 'eTag': (string) if given, this will be used as the ETag header rather
|
99 |
+
* than values based on lastModifiedTime or contentHash. Also the encoding
|
100 |
+
* string will not be appended to the given value as described above.
|
101 |
+
*
|
102 |
+
* 'invalidate': (bool) if true, the client cache will be considered invalid
|
103 |
+
* without testing. Effectively this disables conditional GET.
|
104 |
+
* (default false)
|
105 |
+
*
|
106 |
+
* 'maxAge': (int) if given, this will set the Cache-Control max-age in
|
107 |
+
* seconds, and also set the Expires header to the equivalent GMT date.
|
108 |
+
* After the max-age period has passed, the browser will again send a
|
109 |
+
* conditional GET to revalidate its cache.
|
110 |
+
*/
|
111 |
+
public function __construct($spec)
|
112 |
+
{
|
113 |
+
if (isset($spec['cacheHeaders']) && is_array($spec['cacheHeaders'])) {
|
114 |
+
$this->_cacheHeaders = $spec['cacheHeaders'];
|
115 |
+
}
|
116 |
+
|
117 |
+
$scope = ($this->_cacheHeaders['cacheheaders_enabled'] && $this->_cacheHeaders['cacheheaders'] != 'no_cache') ? 'public' : 'private';
|
118 |
+
$maxAge = 0;
|
119 |
+
|
120 |
+
$this->_headers['Pragma'] = $scope;
|
121 |
+
|
122 |
+
// For backwards compatibility (will be removed in the future)
|
123 |
+
if (isset($spec['setExpires'])
|
124 |
+
&& is_numeric($spec['setExpires'])
|
125 |
+
&& ! isset($spec['maxAge'])) {
|
126 |
+
$spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME'];
|
127 |
+
}
|
128 |
+
if (isset($spec['maxAge']) && $this->_cacheHeaders['expires_enabled'] && $spec['maxAge']) {
|
129 |
+
$maxAge = $spec['maxAge'];
|
130 |
+
$this->_headers['Expires'] = self::gmtDate(
|
131 |
+
$_SERVER['REQUEST_TIME'] + $spec['maxAge']
|
132 |
+
);
|
133 |
+
}
|
134 |
+
$etagAppend = '';
|
135 |
+
if (isset($spec['encoding'])) {
|
136 |
+
$this->_stripEtag = true;
|
137 |
+
$this->_headers['Vary'] = 'Accept-Encoding';
|
138 |
+
if ('' !== $spec['encoding']) {
|
139 |
+
if (0 === strpos($spec['encoding'], 'x-')) {
|
140 |
+
$spec['encoding'] = substr($spec['encoding'], 2);
|
141 |
+
}
|
142 |
+
$etagAppend = ';' . substr($spec['encoding'], 0, 2);
|
143 |
+
}
|
144 |
+
}
|
145 |
+
if (isset($spec['lastModifiedTime'])) {
|
146 |
+
$this->_setLastModified($spec['lastModifiedTime']);
|
147 |
+
if (isset($spec['eTag'])) { // Use it
|
148 |
+
$this->_setEtag($spec['eTag'], $scope);
|
149 |
+
} else { // base both headers on time
|
150 |
+
$this->_setEtag($spec['lastModifiedTime'] . $etagAppend, $scope);
|
151 |
+
}
|
152 |
+
} elseif (isset($spec['eTag'])) { // Use it
|
153 |
+
$this->_setEtag($spec['eTag'], $scope);
|
154 |
+
} elseif (isset($spec['contentHash'])) { // Use the hash as the ETag
|
155 |
+
$this->_setEtag($spec['contentHash'] . $etagAppend, $scope);
|
156 |
+
}
|
157 |
+
|
158 |
+
if ($this->_cacheHeaders['cacheheaders_enabled']) {
|
159 |
+
switch ($this->_cacheHeaders['cacheheaders']) {
|
160 |
+
case 'cache':
|
161 |
+
$this->_headers['Cache-Control'] = 'public';
|
162 |
+
break;
|
163 |
+
|
164 |
+
case 'cache_public_maxage':
|
165 |
+
$this->_headers['Cache-Control'] = "max-age={$maxAge}, public";
|
166 |
+
break;
|
167 |
+
|
168 |
+
case 'cache_validation':
|
169 |
+
$this->_headers['Cache-Control'] = 'public, must-revalidate, proxy-revalidate';
|
170 |
+
break;
|
171 |
+
|
172 |
+
case 'cache_noproxy':
|
173 |
+
$this->_headers['Cache-Control'] = 'private, must-revalidate';
|
174 |
+
break;
|
175 |
+
|
176 |
+
case 'cache_maxage':
|
177 |
+
$this->_headers['Cache-Control'] = "max-age={$maxAge}, {$scope}, must-revalidate, proxy-revalidate";
|
178 |
+
break;
|
179 |
+
|
180 |
+
case 'no_cache':
|
181 |
+
$this->_headers['Cache-Control'] = 'max-age=0, private, no-store, no-cache, must-revalidate';
|
182 |
+
break;
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
/**
|
187 |
+
* Disable caching for preview mode
|
188 |
+
*/
|
189 |
+
if (\W3TC\Util_Environment::is_preview_mode()) {
|
190 |
+
$this->_headers = array_merge($this->_headers, array(
|
191 |
+
'Pragma' => 'private',
|
192 |
+
'Cache-Control' => 'private'
|
193 |
+
));
|
194 |
+
}
|
195 |
+
|
196 |
+
// invalidate cache if disabled, otherwise check
|
197 |
+
$this->cacheIsValid = (isset($spec['invalidate']) && $spec['invalidate'])
|
198 |
+
? false
|
199 |
+
: $this->_isCacheValid();
|
200 |
+
}
|
201 |
+
|
202 |
+
/**
|
203 |
+
* Get array of output headers to be sent
|
204 |
+
*
|
205 |
+
* In the case of 304 responses, this array will only contain the response
|
206 |
+
* code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified')
|
207 |
+
*
|
208 |
+
* Otherwise something like:
|
209 |
+
* <code>
|
210 |
+
* array(
|
211 |
+
* 'Cache-Control' => 'max-age=0, public'
|
212 |
+
* ,'ETag' => '"foobar"'
|
213 |
+
* )
|
214 |
+
* </code>
|
215 |
+
*
|
216 |
+
* @return array
|
217 |
+
*/
|
218 |
+
public function getHeaders()
|
219 |
+
{
|
220 |
+
return $this->_headers;
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Set the Content-Length header in bytes
|
225 |
+
*
|
226 |
+
* With most PHP configs, as long as you don't flush() output, this method
|
227 |
+
* is not needed and PHP will buffer all output and set Content-Length for
|
228 |
+
* you. Otherwise you'll want to call this to let the client know up front.
|
229 |
+
*
|
230 |
+
* @param int $bytes
|
231 |
+
*
|
232 |
+
* @return int copy of input $bytes
|
233 |
+
*/
|
234 |
+
public function setContentLength($bytes)
|
235 |
+
{
|
236 |
+
return $this->_headers['Content-Length'] = $bytes;
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Send headers
|
241 |
+
*
|
242 |
+
* @see getHeaders()
|
243 |
+
*
|
244 |
+
* Note this doesn't "clear" the headers. Calling sendHeaders() will
|
245 |
+
* call header() again (but probably have not effect) and getHeaders() will
|
246 |
+
* still return the headers.
|
247 |
+
*
|
248 |
+
* @return null
|
249 |
+
*/
|
250 |
+
public function sendHeaders()
|
251 |
+
{
|
252 |
+
$headers = $this->_headers;
|
253 |
+
if (array_key_exists('_responseCode', $headers)) {
|
254 |
+
// FastCGI environments require 3rd arg to header() to be set
|
255 |
+
list(, $code) = explode(' ', $headers['_responseCode'], 3);
|
256 |
+
header($headers['_responseCode'], true, $code);
|
257 |
+
unset($headers['_responseCode']);
|
258 |
+
}
|
259 |
+
foreach ($headers as $name => $val) {
|
260 |
+
header($name . ': ' . $val);
|
261 |
+
}
|
262 |
+
}
|
263 |
+
|
264 |
+
/**
|
265 |
+
* Exit if the client's cache is valid for this resource
|
266 |
+
*
|
267 |
+
* This is a convenience method for common use of the class
|
268 |
+
*
|
269 |
+
* @param int $lastModifiedTime if given, both ETag AND Last-Modified headers
|
270 |
+
* will be sent with content. This is recommended.
|
271 |
+
*
|
272 |
+
* @param bool $isPublic (default false) if true, the Cache-Control header
|
273 |
+
* will contain "public", allowing proxies to cache the content. Otherwise
|
274 |
+
* "private" will be sent, allowing only browser caching.
|
275 |
+
*
|
276 |
+
* @param array $options (default empty) additional options for constructor
|
277 |
+
*/
|
278 |
+
public static function check($lastModifiedTime = null, $isPublic = false, $options = array())
|
279 |
+
{
|
280 |
+
if (null !== $lastModifiedTime) {
|
281 |
+
$options['lastModifiedTime'] = (int)$lastModifiedTime;
|
282 |
+
}
|
283 |
+
$options['isPublic'] = (bool)$isPublic;
|
284 |
+
$cg = new HTTP_ConditionalGet($options);
|
285 |
+
$cg->sendHeaders();
|
286 |
+
if ($cg->cacheIsValid) {
|
287 |
+
exit();
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
|
292 |
+
/**
|
293 |
+
* Get a GMT formatted date for use in HTTP headers
|
294 |
+
*
|
295 |
+
* <code>
|
296 |
+
* header('Expires: ' . HTTP_ConditionalGet::gmtdate($time));
|
297 |
+
* </code>
|
298 |
+
*
|
299 |
+
* @param int $time unix timestamp
|
300 |
+
*
|
301 |
+
* @return string
|
302 |
+
*/
|
303 |
+
public static function gmtDate($time)
|
304 |
+
{
|
305 |
+
return gmdate('D, d M Y H:i:s \G\M\T', $time);
|
306 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
307 |
|
308 |
+
protected $_headers = array();
|
309 |
+
protected $_lmTime = null;
|
310 |
+
protected $_etag = null;
|
311 |
+
protected $_stripEtag = false;
|
312 |
+
protected $_cacheHeaders = array(
|
313 |
+
'use_etag' => true,
|
314 |
+
'expires_enabled' => true,
|
315 |
+
'cacheheaders_enabled' => true,
|
316 |
+
'cacheheaders' => 'cache_validation'
|
317 |
+
);
|
318 |
|
319 |
+
/**
|
320 |
+
* @param string $hash
|
321 |
+
*
|
322 |
+
* @param string $scope
|
323 |
+
*/
|
324 |
+
protected function _setEtag($hash, $scope)
|
325 |
+
{
|
326 |
+
$this->_etag = '"' . substr($scope, 0, 3) . $hash . '"';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
|
328 |
+
if ($this->_cacheHeaders['use_etag'])
|
329 |
+
$this->_headers['ETag'] = $this->_etag;
|
330 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
|
332 |
+
/**
|
333 |
+
* @param int $time
|
334 |
+
*/
|
335 |
+
protected function _setLastModified($time)
|
336 |
+
{
|
337 |
+
$this->_lmTime = (int)$time;
|
338 |
+
$this->_headers['Last-Modified'] = self::gmtDate($time);
|
339 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
|
341 |
+
/**
|
342 |
+
* Determine validity of client cache and queue 304 header if valid
|
343 |
+
*
|
344 |
+
* @return bool
|
345 |
+
*/
|
346 |
+
protected function _isCacheValid()
|
347 |
+
{
|
348 |
+
if (null === $this->_etag) {
|
349 |
+
// lmTime is copied to ETag, so this condition implies that the
|
350 |
+
// server sent neither ETag nor Last-Modified, so the client can't
|
351 |
+
// possibly has a valid cache.
|
352 |
+
return false;
|
353 |
+
}
|
354 |
+
$isValid = ($this->resourceMatchedEtag() || $this->resourceNotModified());
|
355 |
+
if ($isValid) {
|
356 |
+
$this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified';
|
357 |
+
}
|
358 |
+
return $isValid;
|
359 |
+
}
|
360 |
|
361 |
+
/**
|
362 |
+
* @return bool
|
363 |
+
*/
|
364 |
+
protected function resourceMatchedEtag()
|
365 |
+
{
|
366 |
+
if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
|
367 |
+
return false;
|
368 |
+
}
|
369 |
+
$clientEtagList = $_SERVER['HTTP_IF_NONE_MATCH'];
|
370 |
+
$clientEtags = explode(',', $clientEtagList);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
371 |
|
372 |
+
$compareTo = $this->normalizeEtag($this->_etag);
|
373 |
+
foreach ($clientEtags as $clientEtag) {
|
374 |
+
if ($this->normalizeEtag($clientEtag) === $compareTo) {
|
375 |
+
// respond with the client's matched ETag, even if it's not what
|
376 |
+
// we would've sent by default
|
377 |
+
if ($this->_cacheHeaders['use_etag'])
|
378 |
+
$this->_headers['ETag'] = trim($clientEtag);
|
379 |
+
return true;
|
380 |
+
}
|
381 |
+
}
|
382 |
+
return false;
|
383 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
384 |
|
385 |
+
/**
|
386 |
+
* @param string $etag
|
387 |
+
*
|
388 |
+
* @return string
|
389 |
+
*/
|
390 |
+
protected function normalizeEtag($etag) {
|
391 |
+
$etag = trim($etag);
|
392 |
+
return $this->_stripEtag
|
393 |
+
? preg_replace('/;\\w\\w"$/', '"', $etag)
|
394 |
+
: $etag;
|
395 |
+
}
|
396 |
|
397 |
+
/**
|
398 |
+
* @return bool
|
399 |
+
*/
|
400 |
+
protected function resourceNotModified()
|
401 |
+
{
|
402 |
+
if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
403 |
+
return false;
|
404 |
+
}
|
405 |
+
// strip off IE's extra data (semicolon)
|
406 |
+
list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2);
|
407 |
+
if (strtotime($ifModifiedSince) >= $this->_lmTime) {
|
408 |
+
// Apache 2.2's behavior. If there was no ETag match, send the
|
409 |
+
// non-encoded version of the ETag value.
|
410 |
+
if ($this->_cacheHeaders['use_etag'])
|
411 |
+
$this->_headers['ETag'] = $this->normalizeEtag($this->_etag);
|
412 |
+
return true;
|
413 |
+
}
|
414 |
+
return false;
|
415 |
+
}
|
416 |
}
|
lib/Minify/HTTP/Encoder.php
CHANGED
@@ -1,14 +1,16 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
-
* Class HTTP_Encoder
|
4 |
* @package Minify
|
5 |
* @subpackage HTTP
|
6 |
*/
|
7 |
-
|
8 |
/**
|
9 |
* Encode and send gzipped/deflated content
|
10 |
*
|
11 |
-
* The "Vary: Accept-Encoding" header is sent. If the client allows encoding,
|
12 |
* Content-Encoding and Content-Length are added.
|
13 |
*
|
14 |
* <code>
|
@@ -26,7 +28,7 @@
|
|
26 |
* header('Content-Type: text/css'); // needed if not HTML
|
27 |
* HTTP_Encoder::output($css);
|
28 |
* </code>
|
29 |
-
*
|
30 |
* <code>
|
31 |
* // Just sniff for the accepted encoding
|
32 |
* $encoding = HTTP_Encoder::getAcceptedEncoding();
|
@@ -34,305 +36,305 @@
|
|
34 |
*
|
35 |
* For more control over headers, use getHeaders() and getData() and send your
|
36 |
* own output.
|
37 |
-
*
|
38 |
-
* Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
|
39 |
* and gzcompress functions for gzip, deflate, and compress-encoding
|
40 |
* respectively.
|
41 |
-
*
|
42 |
* @package Minify
|
43 |
* @subpackage HTTP
|
44 |
* @author Stephen Clay <steve@mrclay.org>
|
45 |
*/
|
46 |
class HTTP_Encoder {
|
47 |
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
if (isset($spec['type'])) {
|
99 |
-
$this->_headers['Content-Type'] = $spec['type'];
|
100 |
-
}
|
101 |
-
if (isset($spec['method'])
|
102 |
-
&& in_array($spec['method'], array('gzip', 'deflate', '')))
|
103 |
-
{
|
104 |
-
$this->_encodeMethod = array($spec['method'], $spec['method']);
|
105 |
-
} else {
|
106 |
-
$this->_encodeMethod = self::getAcceptedEncoding();
|
107 |
-
}
|
108 |
-
}
|
109 |
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
|
141 |
-
|
142 |
-
|
143 |
-
*
|
144 |
-
* You must call this before headers are sent and it probably cannot be
|
145 |
-
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
146 |
-
* not handled purposefully.
|
147 |
-
*
|
148 |
-
* @see getHeaders()
|
149 |
-
*/
|
150 |
-
public function sendHeaders()
|
151 |
-
{
|
152 |
-
foreach ($this->_headers as $name => $val) {
|
153 |
-
header($name . ': ' . $val);
|
154 |
-
}
|
155 |
-
}
|
156 |
-
|
157 |
-
/**
|
158 |
-
* Send output headers and content
|
159 |
-
*
|
160 |
-
* A shortcut for sendHeaders() and echo getContent()
|
161 |
-
*
|
162 |
-
* You must call this before headers are sent and it probably cannot be
|
163 |
-
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
164 |
-
* not handled purposefully.
|
165 |
-
*/
|
166 |
-
public function sendAll()
|
167 |
-
{
|
168 |
-
$this->sendHeaders();
|
169 |
-
echo $this->_content;
|
170 |
-
}
|
171 |
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
) {
|
223 |
-
if (function_exists('gzencode'))
|
224 |
-
return array('gzip', 'gzip');
|
225 |
-
}
|
226 |
-
// gzip checks (slow)
|
227 |
-
if (preg_match(
|
228 |
-
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
229 |
-
,$ae
|
230 |
-
,$m)) {
|
231 |
-
return array('gzip', $m[1]);
|
232 |
-
}
|
233 |
|
234 |
-
|
235 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
236 |
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
if (null === $compressionLevel) {
|
260 |
-
$compressionLevel = self::$compressionLevel;
|
261 |
-
}
|
262 |
-
if ('' === $this->_encodeMethod[0]
|
263 |
-
|| ($compressionLevel == 0)
|
264 |
-
|| !extension_loaded('zlib'))
|
265 |
-
{
|
266 |
-
return false;
|
267 |
-
}
|
268 |
-
if ($this->_encodeMethod[0] === 'br') {
|
269 |
-
$encoded = brotli_compress($this->_content);
|
270 |
-
} elseif ($this->_encodeMethod[0] === 'deflate') {
|
271 |
-
$encoded = gzdeflate($this->_content, $compressionLevel);
|
272 |
-
} elseif ($this->_encodeMethod[0] === 'gzip') {
|
273 |
-
$encoded = gzencode($this->_content, $compressionLevel);
|
274 |
-
} else {
|
275 |
-
$encoded = gzcompress($this->_content, $compressionLevel);
|
276 |
-
}
|
277 |
-
if (false === $encoded) {
|
278 |
-
return false;
|
279 |
-
}
|
280 |
-
$this->_headers['Content-Length'] = $this->_useMbStrlen
|
281 |
-
? (string)mb_strlen($encoded, '8bit')
|
282 |
-
: (string)strlen($encoded);
|
283 |
-
$this->_headers['Content-Encoding'] = $this->_encodeMethod[1];
|
284 |
-
$this->_content = $encoded;
|
285 |
-
return true;
|
286 |
-
}
|
287 |
-
|
288 |
-
/**
|
289 |
-
* Encode and send appropriate headers and content
|
290 |
-
*
|
291 |
-
* This is a convenience method for common use of the class
|
292 |
-
*
|
293 |
-
* @param string $content
|
294 |
-
*
|
295 |
-
* @param int $compressionLevel given to zlib functions. If not given, the
|
296 |
-
* class default will be used.
|
297 |
-
*
|
298 |
-
* @return bool success true if the content was actually compressed
|
299 |
-
*/
|
300 |
-
public static function output($content, $compressionLevel = null)
|
301 |
-
{
|
302 |
-
if (null === $compressionLevel) {
|
303 |
-
$compressionLevel = self::$compressionLevel;
|
304 |
-
}
|
305 |
-
$he = new HTTP_Encoder(array('content' => $content));
|
306 |
-
$ret = $he->encode($compressionLevel);
|
307 |
-
$he->sendAll();
|
308 |
-
return $ret;
|
309 |
-
}
|
310 |
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
*/
|
316 |
-
public static function isBuggyIe()
|
317 |
-
{
|
318 |
-
if (empty($_SERVER['HTTP_USER_AGENT'])) {
|
319 |
-
return false;
|
320 |
-
}
|
321 |
-
$ua = $_SERVER['HTTP_USER_AGENT'];
|
322 |
-
// quick escape for non-IEs
|
323 |
-
if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ')
|
324 |
-
|| false !== strpos($ua, 'Opera')) {
|
325 |
-
return false;
|
326 |
-
}
|
327 |
-
// no regex = faaast
|
328 |
-
$version = (float)substr($ua, 30);
|
329 |
-
return self::$encodeToIe6
|
330 |
-
? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1')))
|
331 |
-
: ($version < 7);
|
332 |
-
}
|
333 |
-
|
334 |
-
protected $_content = '';
|
335 |
-
protected $_headers = array();
|
336 |
-
protected $_encodeMethod = array('', '');
|
337 |
-
protected $_useMbStrlen = false;
|
338 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
+
|
4 |
/**
|
5 |
+
* Class HTTP_Encoder
|
6 |
* @package Minify
|
7 |
* @subpackage HTTP
|
8 |
*/
|
9 |
+
|
10 |
/**
|
11 |
* Encode and send gzipped/deflated content
|
12 |
*
|
13 |
+
* The "Vary: Accept-Encoding" header is sent. If the client allows encoding,
|
14 |
* Content-Encoding and Content-Length are added.
|
15 |
*
|
16 |
* <code>
|
28 |
* header('Content-Type: text/css'); // needed if not HTML
|
29 |
* HTTP_Encoder::output($css);
|
30 |
* </code>
|
31 |
+
*
|
32 |
* <code>
|
33 |
* // Just sniff for the accepted encoding
|
34 |
* $encoding = HTTP_Encoder::getAcceptedEncoding();
|
36 |
*
|
37 |
* For more control over headers, use getHeaders() and getData() and send your
|
38 |
* own output.
|
39 |
+
*
|
40 |
+
* Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
|
41 |
* and gzcompress functions for gzip, deflate, and compress-encoding
|
42 |
* respectively.
|
43 |
+
*
|
44 |
* @package Minify
|
45 |
* @subpackage HTTP
|
46 |
* @author Stephen Clay <steve@mrclay.org>
|
47 |
*/
|
48 |
class HTTP_Encoder {
|
49 |
|
50 |
+
/**
|
51 |
+
* Should the encoder allow HTTP encoding to IE6?
|
52 |
+
*
|
53 |
+
* If you have many IE6 users and the bandwidth savings is worth troubling
|
54 |
+
* some of them, set this to true.
|
55 |
+
*
|
56 |
+
* By default, encoding is only offered to IE7+. When this is true,
|
57 |
+
* getAcceptedEncoding() will return an encoding for IE6 if its user agent
|
58 |
+
* string contains "SV1". This has been documented in many places as "safe",
|
59 |
+
* but there seem to be remaining, intermittent encoding bugs in patched
|
60 |
+
* IE6 on the wild web.
|
61 |
+
*
|
62 |
+
* @var bool
|
63 |
+
*/
|
64 |
+
public static $encodeToIe6 = true;
|
65 |
+
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Default compression level for zlib operations
|
69 |
+
*
|
70 |
+
* This level is used if encode() is not given a $compressionLevel
|
71 |
+
*
|
72 |
+
* @var int
|
73 |
+
*/
|
74 |
+
public static $compressionLevel = 6;
|
75 |
+
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Get an HTTP Encoder object
|
79 |
+
*
|
80 |
+
* @param array $spec options
|
81 |
+
*
|
82 |
+
* 'content': (string required) content to be encoded
|
83 |
+
*
|
84 |
+
* 'type': (string) if set, the Content-Type header will have this value.
|
85 |
+
*
|
86 |
+
* 'method: (string) only set this if you are forcing a particular encoding
|
87 |
+
* method. If not set, the best method will be chosen by getAcceptedEncoding()
|
88 |
+
* The available methods are 'gzip', 'deflate', 'compress', and '' (no
|
89 |
+
* encoding)
|
90 |
+
*/
|
91 |
+
public function __construct($spec)
|
92 |
+
{
|
93 |
+
$this->_useMbStrlen = (function_exists('mb_strlen')
|
94 |
+
&& (ini_get('mbstring.func_overload') !== '')
|
95 |
+
&& ((int)ini_get('mbstring.func_overload') & 2));
|
96 |
+
$this->_content = $spec['content'];
|
97 |
+
$this->_headers['Content-Length'] = $this->_useMbStrlen
|
98 |
+
? (string)mb_strlen($this->_content, '8bit')
|
99 |
+
: (string)strlen($this->_content);
|
100 |
+
if (isset($spec['type'])) {
|
101 |
+
$this->_headers['Content-Type'] = $spec['type'];
|
102 |
+
}
|
103 |
+
if (isset($spec['method'])
|
104 |
+
&& in_array($spec['method'], array('gzip', 'deflate', '')))
|
105 |
+
{
|
106 |
+
$this->_encodeMethod = array($spec['method'], $spec['method']);
|
107 |
+
} else {
|
108 |
+
$this->_encodeMethod = self::getAcceptedEncoding();
|
109 |
+
}
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Get content in current form
|
114 |
+
*
|
115 |
+
* Call after encode() for encoded content.
|
116 |
+
*
|
117 |
+
* @return string
|
118 |
+
*/
|
119 |
+
public function getContent()
|
120 |
+
{
|
121 |
+
return $this->_content;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Get array of output headers to be sent
|
126 |
+
*
|
127 |
+
* E.g.
|
128 |
+
* <code>
|
129 |
+
* array(
|
130 |
+
* 'Content-Length' => '615'
|
131 |
+
* ,'Content-Encoding' => 'x-gzip'
|
132 |
+
* ,'Vary' => 'Accept-Encoding'
|
133 |
+
* )
|
134 |
+
* </code>
|
135 |
+
*
|
136 |
+
* @return array
|
137 |
+
*/
|
138 |
+
public function getHeaders()
|
139 |
+
{
|
140 |
+
return $this->_headers;
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Send output headers
|
145 |
+
*
|
146 |
+
* You must call this before headers are sent and it probably cannot be
|
147 |
+
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
148 |
+
* not handled purposefully.
|
149 |
+
*
|
150 |
+
* @see getHeaders()
|
151 |
+
*/
|
152 |
+
public function sendHeaders()
|
153 |
+
{
|
154 |
+
foreach ($this->_headers as $name => $val) {
|
155 |
+
header($name . ': ' . $val);
|
156 |
+
}
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* Send output headers and content
|
161 |
+
*
|
162 |
+
* A shortcut for sendHeaders() and echo getContent()
|
163 |
+
*
|
164 |
+
* You must call this before headers are sent and it probably cannot be
|
165 |
+
* used in conjunction with zlib output buffering / mod_gzip. Errors are
|
166 |
+
* not handled purposefully.
|
167 |
+
*/
|
168 |
+
public function sendAll()
|
169 |
+
{
|
170 |
+
$this->sendHeaders();
|
171 |
+
echo $this->_content;
|
172 |
+
}
|
173 |
|
174 |
+
/**
|
175 |
+
* Determine the client's best encoding method from the HTTP Accept-Encoding
|
176 |
+
* header.
|
177 |
+
*
|
178 |
+
* If no Accept-Encoding header is set, or the browser is IE before v6 SP2,
|
179 |
+
* this will return ('', ''), the "identity" encoding.
|
180 |
+
*
|
181 |
+
* A syntax-aware scan is done of the Accept-Encoding, so the method must
|
182 |
+
* be non 0. The methods are favored in order of gzip, deflate, then
|
183 |
+
* compress. Deflate is always smallest and generally faster, but is
|
184 |
+
* rarely sent by servers, so client support could be buggier.
|
185 |
+
*
|
186 |
+
* @param bool $allowCompress allow the older compress encoding
|
187 |
+
*
|
188 |
+
* @param bool $allowDeflate allow the more recent deflate encoding
|
189 |
+
*
|
190 |
+
* @return array two values, 1st is the actual encoding method, 2nd is the
|
191 |
+
* alias of that method to use in the Content-Encoding header (some browsers
|
192 |
+
* call gzip "x-gzip" etc.)
|
193 |
+
*/
|
194 |
+
public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true)
|
195 |
+
{
|
196 |
+
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
|
198 |
+
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|
199 |
+
|| \W3TC\Util_Environment::is_zlib_enabled()
|
200 |
+
|| headers_sent()
|
201 |
+
|| self::isBuggyIe())
|
202 |
+
{
|
203 |
+
return array('', '');
|
204 |
+
}
|
205 |
+
$ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
|
206 |
+
// brotli checks (quick)
|
207 |
+
if (0 === strpos($ae, 'br,')
|
208 |
+
|| strpos($ae, ', br') === strlen($ae) - 4
|
209 |
+
) {
|
210 |
+
if (function_exists('brotli_compress'))
|
211 |
+
return array('br', 'br');
|
212 |
+
}
|
213 |
+
// brotli checks (slow)
|
214 |
+
if (preg_match(
|
215 |
+
'@(?:^|,)\\s*((?:x-)?br)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
216 |
+
,$ae
|
217 |
+
,$m)) {
|
218 |
+
if (function_exists('brotli_compress'))
|
219 |
+
return array('br', $m[1]);
|
220 |
+
}
|
221 |
+
// gzip checks (quick)
|
222 |
+
if (0 === strpos($ae, 'gzip,') // most browsers
|
223 |
+
|| 0 === strpos($ae, 'deflate, gzip,') // opera
|
224 |
+
) {
|
225 |
+
if (function_exists('gzencode'))
|
226 |
+
return array('gzip', 'gzip');
|
227 |
+
}
|
228 |
+
// gzip checks (slow)
|
229 |
+
if (preg_match(
|
230 |
+
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
|
231 |
+
,$ae
|
232 |
+
,$m)) {
|
233 |
+
return array('gzip', $m[1]);
|
234 |
+
}
|
235 |
|
236 |
+
return array('', '');
|
237 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
|
239 |
+
/**
|
240 |
+
* Encode (compress) the content
|
241 |
+
*
|
242 |
+
* If the encode method is '' (none) or compression level is 0, or the 'zlib'
|
243 |
+
* extension isn't loaded, we return false.
|
244 |
+
*
|
245 |
+
* Then the appropriate gz_* function is called to compress the content. If
|
246 |
+
* this fails, false is returned.
|
247 |
+
*
|
248 |
+
* The header "Vary: Accept-Encoding" is added. If encoding is successful,
|
249 |
+
* the Content-Length header is updated, and Content-Encoding is also added.
|
250 |
+
*
|
251 |
+
* @param int $compressionLevel given to zlib functions. If not given, the
|
252 |
+
* class default will be used.
|
253 |
+
*
|
254 |
+
* @return bool success true if the content was actually compressed
|
255 |
+
*/
|
256 |
+
public function encode($compressionLevel = null)
|
257 |
+
{
|
258 |
+
if (! self::isBuggyIe()) {
|
259 |
+
$this->_headers['Vary'] = 'Accept-Encoding';
|
260 |
+
}
|
261 |
+
if (null === $compressionLevel) {
|
262 |
+
$compressionLevel = self::$compressionLevel;
|
263 |
+
}
|
264 |
+
if ('' === $this->_encodeMethod[0]
|
265 |
+
|| ($compressionLevel == 0)
|
266 |
+
|| !extension_loaded('zlib'))
|
267 |
+
{
|
268 |
+
return false;
|
269 |
+
}
|
270 |
+
if ($this->_encodeMethod[0] === 'br') {
|
271 |
+
$encoded = brotli_compress($this->_content);
|
272 |
+
} elseif ($this->_encodeMethod[0] === 'deflate') {
|
273 |
+
$encoded = gzdeflate($this->_content, $compressionLevel);
|
274 |
+
} elseif ($this->_encodeMethod[0] === 'gzip') {
|
275 |
+
$encoded = gzencode($this->_content, $compressionLevel);
|
276 |
+
} else {
|
277 |
+
$encoded = gzcompress($this->_content, $compressionLevel);
|
278 |
+
}
|
279 |
+
if (false === $encoded) {
|
280 |
+
return false;
|
281 |
+
}
|
282 |
+
$this->_headers['Content-Length'] = $this->_useMbStrlen
|
283 |
+
? (string)mb_strlen($encoded, '8bit')
|
284 |
+
: (string)strlen($encoded);
|
285 |
+
$this->_headers['Content-Encoding'] = $this->_encodeMethod[1];
|
286 |
+
$this->_content = $encoded;
|
287 |
+
return true;
|
288 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
|
290 |
+
/**
|
291 |
+
* Encode and send appropriate headers and content
|
292 |
+
*
|
293 |
+
* This is a convenience method for common use of the class
|
294 |
+
*
|
295 |
+
* @param string $content
|
296 |
+
*
|
297 |
+
* @param int $compressionLevel given to zlib functions. If not given, the
|
298 |
+
* class default will be used.
|
299 |
+
*
|
300 |
+
* @return bool success true if the content was actually compressed
|
301 |
+
*/
|
302 |
+
public static function output($content, $compressionLevel = null)
|
303 |
+
{
|
304 |
+
if (null === $compressionLevel) {
|
305 |
+
$compressionLevel = self::$compressionLevel;
|
306 |
+
}
|
307 |
+
$he = new HTTP_Encoder(array('content' => $content));
|
308 |
+
$ret = $he->encode($compressionLevel);
|
309 |
+
$he->sendAll();
|
310 |
+
return $ret;
|
311 |
+
}
|
312 |
|
313 |
+
/**
|
314 |
+
* Is the browser an IE version earlier than 6 SP2?
|
315 |
+
*
|
316 |
+
* @return bool
|
317 |
+
*/
|
318 |
+
public static function isBuggyIe()
|
319 |
+
{
|
320 |
+
if (empty($_SERVER['HTTP_USER_AGENT'])) {
|
321 |
+
return false;
|
322 |
+
}
|
323 |
+
$ua = $_SERVER['HTTP_USER_AGENT'];
|
324 |
+
// quick escape for non-IEs
|
325 |
+
if (0 !== strpos($ua, 'Mozilla/4.0 (compatible; MSIE ')
|
326 |
+
|| false !== strpos($ua, 'Opera')) {
|
327 |
+
return false;
|
328 |
+
}
|
329 |
+
// no regex = faaast
|
330 |
+
$version = (float)substr($ua, 30);
|
331 |
+
return self::$encodeToIe6
|
332 |
+
? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1')))
|
333 |
+
: ($version < 7);
|
334 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
335 |
|
336 |
+
protected $_content = '';
|
337 |
+
protected $_headers = array();
|
338 |
+
protected $_encodeMethod = array('', '');
|
339 |
+
protected $_useMbStrlen = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
}
|
lib/Minify/JSMin.php
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
|
4 |
*
|
@@ -54,402 +56,402 @@
|
|
54 |
* @link http://code.google.com/p/jsmin-php/
|
55 |
*/
|
56 |
|
57 |
-
class
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
74 |
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
{
|
84 |
-
// look out for syntax like "++ +" and "- ++"
|
85 |
-
$p = '\\+';
|
86 |
-
$m = '\\-';
|
87 |
-
if (preg_match("/([$p$m])(?:\\1 [$p$m]| (?:$p$p|$m$m))/", $js)) {
|
88 |
-
// likely pre-minified and would be broken by JSMin
|
89 |
-
return $js;
|
90 |
-
}
|
91 |
-
$jsmin = new Minify0_JSMin($js, $options);
|
92 |
-
return $jsmin->min();
|
93 |
-
}
|
94 |
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
|
|
|
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
if ($this->output !== '') { // min already run
|
112 |
-
return $this->output;
|
113 |
-
}
|
114 |
|
115 |
-
|
116 |
-
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
|
117 |
-
$mbIntEnc = mb_internal_encoding();
|
118 |
-
mb_internal_encoding('8bit');
|
119 |
-
}
|
120 |
-
$this->input = str_replace("\r\n", "\n", $this->input);
|
121 |
-
$this->inputLength = strlen($this->input);
|
122 |
|
123 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
124 |
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
|
|
|
|
|
|
139 |
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|| (false === strpos('{[(+-!~', $this->b)
|
144 |
-
&& ! $this->isAlphaNum($this->b))) {
|
145 |
-
$command = self::ACTION_DELETE_A;
|
146 |
-
}
|
147 |
-
} elseif (! $this->isAlphaNum($this->a)) {
|
148 |
-
if ($this->b === ' '
|
149 |
-
|| ($this->b === "\n"
|
150 |
-
&& (false === strpos('}])+-"\'', $this->a)))) {
|
151 |
-
$command = self::ACTION_DELETE_A_B;
|
152 |
-
}
|
153 |
-
}
|
154 |
-
$this->action($command);
|
155 |
-
}
|
156 |
-
$this->output = trim($this->output);
|
157 |
|
158 |
-
|
159 |
-
|
160 |
-
|
|
|
|
|
161 |
|
162 |
-
|
163 |
-
|
164 |
-
} else {
|
165 |
-
$this->output = preg_replace("~[\r\n]+~", "\n", $this->output);
|
166 |
-
}
|
167 |
-
|
168 |
-
return $this->output;
|
169 |
-
}
|
170 |
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
|
203 |
-
|
204 |
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
}
|
452 |
|
453 |
-
class JSMin_UnterminatedStringException extends Exception {}
|
454 |
-
class JSMin_UnterminatedCommentException extends Exception {}
|
455 |
-
class JSMin_UnterminatedRegExpException extends Exception {}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
+
|
4 |
/**
|
5 |
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
|
6 |
*
|
56 |
* @link http://code.google.com/p/jsmin-php/
|
57 |
*/
|
58 |
|
59 |
+
class JSMin {
|
60 |
+
const ORD_LF = 10;
|
61 |
+
const ORD_SPACE = 32;
|
62 |
+
const ACTION_KEEP_A = 1;
|
63 |
+
const ACTION_DELETE_A = 2;
|
64 |
+
const ACTION_DELETE_A_B = 3;
|
65 |
+
|
66 |
+
protected $a = "\n";
|
67 |
+
protected $b = '';
|
68 |
+
protected $input = '';
|
69 |
+
protected $inputIndex = 0;
|
70 |
+
protected $inputLength = 0;
|
71 |
+
protected $lookAhead = null;
|
72 |
+
protected $output = '';
|
73 |
+
protected $lastByteOut = '';
|
74 |
+
protected $keptComment = '';
|
75 |
+
protected $options = array();
|
76 |
|
77 |
+
/**
|
78 |
+
* Minify Javascript.
|
79 |
+
*
|
80 |
+
* @param string $js Javascript to be minified
|
81 |
+
*
|
82 |
+
* @return string
|
83 |
+
*/
|
84 |
+
public static function minify($js, $options = array())
|
85 |
+
{
|
86 |
+
// look out for syntax like "++ +" and "- ++"
|
87 |
+
$p = '\\+';
|
88 |
+
$m = '\\-';
|
89 |
+
if (preg_match("/([$p$m])(?:\\1 [$p$m]| (?:$p$p|$m$m))/", $js)) {
|
90 |
+
// likely pre-minified and would be broken by JSMin
|
91 |
+
return $js;
|
92 |
+
}
|
93 |
+
$jsmin = new JSMin($js, $options);
|
94 |
+
return $jsmin->min();
|
95 |
+
}
|
96 |
|
97 |
+
/**
|
98 |
+
* @param string $input
|
99 |
+
*/
|
100 |
+
public function __construct($input, $options = array())
|
101 |
+
{
|
102 |
+
$this->input = $input;
|
103 |
+
$this->options = $options;
|
104 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
|
106 |
+
/**
|
107 |
+
* Perform minification, return result
|
108 |
+
*
|
109 |
+
* @return string
|
110 |
+
*/
|
111 |
+
public function min()
|
112 |
+
{
|
113 |
+
if ($this->output !== '') { // min already run
|
114 |
+
return $this->output;
|
115 |
+
}
|
116 |
|
117 |
+
$mbIntEnc = null;
|
118 |
+
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
|
119 |
+
$mbIntEnc = mb_internal_encoding();
|
120 |
+
mb_internal_encoding('8bit');
|
121 |
+
}
|
122 |
+
$this->input = str_replace("\r\n", "\n", $this->input);
|
123 |
+
$this->inputLength = strlen($this->input);
|
|
|
|
|
|
|
124 |
|
125 |
+
$this->action(self::ACTION_DELETE_A_B);
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
+
while ($this->a !== null) {
|
128 |
+
// determine next command
|
129 |
+
$command = self::ACTION_KEEP_A; // default
|
130 |
+
if ($this->a === ' ') {
|
131 |
+
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
|
132 |
+
&& ($this->b === $this->lastByteOut)) {
|
133 |
+
// Don't delete this space. If we do, the addition/subtraction
|
134 |
+
// could be parsed as a post-increment
|
135 |
+
} elseif (! $this->isAlphaNum($this->b)) {
|
136 |
+
$command = self::ACTION_DELETE_A;
|
137 |
+
}
|
138 |
+
} elseif ($this->a === "\n") {
|
139 |
+
if ($this->b === ' ') {
|
140 |
+
$command = self::ACTION_DELETE_A_B;
|
141 |
|
142 |
+
// in case of mbstring.func_overload & 2, must check for null b,
|
143 |
+
// otherwise mb_strpos will give WARNING
|
144 |
+
} elseif ($this->b === null
|
145 |
+
|| (false === strpos('{[(+-!~', $this->b)
|
146 |
+
&& ! $this->isAlphaNum($this->b))) {
|
147 |
+
$command = self::ACTION_DELETE_A;
|
148 |
+
}
|
149 |
+
} elseif (! $this->isAlphaNum($this->a)) {
|
150 |
+
if ($this->b === ' '
|
151 |
+
|| ($this->b === "\n"
|
152 |
+
&& (false === strpos('}])+-"\'', $this->a)))) {
|
153 |
+
$command = self::ACTION_DELETE_A_B;
|
154 |
+
}
|
155 |
+
}
|
156 |
+
$this->action($command);
|
157 |
+
}
|
158 |
+
$this->output = trim($this->output);
|
159 |
|
160 |
+
if ($mbIntEnc !== null) {
|
161 |
+
mb_internal_encoding($mbIntEnc);
|
162 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
|
164 |
+
if (isset($this->options['stripCrlf']) && $this->options['stripCrlf']) {
|
165 |
+
$this->output = preg_replace("~[\r\n]+~", '', $this->output);
|
166 |
+
} else {
|
167 |
+
$this->output = preg_replace("~[\r\n]+~", "\n", $this->output);
|
168 |
+
}
|
169 |
|
170 |
+
return $this->output;
|
171 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
|
173 |
+
/**
|
174 |
+
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
|
175 |
+
* ACTION_DELETE_A = Copy B to A. Get the next B.
|
176 |
+
* ACTION_DELETE_A_B = Get the next B.
|
177 |
+
*
|
178 |
+
* @param int $command
|
179 |
+
* @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
|
180 |
+
*/
|
181 |
+
protected function action($command)
|
182 |
+
{
|
183 |
+
// make sure we don't compress "a + ++b" to "a+++b", etc.
|
184 |
+
if ($command === self::ACTION_DELETE_A_B
|
185 |
+
&& $this->b === ' '
|
186 |
+
&& ($this->a === '+' || $this->a === '-')) {
|
187 |
+
// Note: we're at an addition/substraction operator; the inputIndex
|
188 |
+
// will certainly be a valid index
|
189 |
+
if ($this->input[$this->inputIndex] === $this->a) {
|
190 |
+
// This is "+ +" or "- -". Don't delete the space.
|
191 |
+
$command = self::ACTION_KEEP_A;
|
192 |
+
}
|
193 |
+
}
|
194 |
|
195 |
+
switch ($command) {
|
196 |
+
case self::ACTION_KEEP_A: // 1
|
197 |
+
$this->output .= $this->a;
|
198 |
|
199 |
+
if ($this->keptComment) {
|
200 |
+
$this->output = rtrim($this->output, "\n");
|
201 |
+
$this->output .= $this->keptComment;
|
202 |
+
$this->keptComment = '';
|
203 |
+
}
|
204 |
|
205 |
+
$this->lastByteOut = $this->a;
|
206 |
|
207 |
+
// fallthrough intentional
|
208 |
+
case self::ACTION_DELETE_A: // 2
|
209 |
+
$this->a = $this->b;
|
210 |
+
if ($this->a === "'" || $this->a === '"') { // string literal
|
211 |
+
$str = $this->a; // in case needed for exception
|
212 |
+
for(;;) {
|
213 |
+
$this->output .= $this->a;
|
214 |
+
$this->lastByteOut = $this->a;
|
215 |
|
216 |
+
$this->a = $this->get();
|
217 |
+
if ($this->a === $this->b) { // end quote
|
218 |
+
break;
|
219 |
+
}
|
220 |
+
if ($this->isEOF($this->a)) {
|
221 |
+
throw new JSMin_UnterminatedStringException(
|
222 |
+
"JSMin: Unterminated String at byte {$this->inputIndex}: {$str}");
|
223 |
+
}
|
224 |
+
$str .= $this->a;
|
225 |
+
if ($this->a === '\\') {
|
226 |
+
$this->output .= $this->a;
|
227 |
+
$this->lastByteOut = $this->a;
|
228 |
|
229 |
+
$this->a = $this->get();
|
230 |
+
$str .= $this->a;
|
231 |
+
}
|
232 |
+
}
|
233 |
+
}
|
234 |
|
235 |
+
// fallthrough intentional
|
236 |
+
case self::ACTION_DELETE_A_B: // 3
|
237 |
+
$this->b = $this->next();
|
238 |
+
if ($this->b === '/' && $this->isRegexpLiteral()) {
|
239 |
+
$this->output .= $this->a . $this->b;
|
240 |
+
$pattern = '/'; // keep entire pattern in case we need to report it in the exception
|
241 |
+
for(;;) {
|
242 |
+
$this->a = $this->get();
|
243 |
+
$pattern .= $this->a;
|
244 |
+
if ($this->a === '[') {
|
245 |
+
for(;;) {
|
246 |
+
$this->output .= $this->a;
|
247 |
+
$this->a = $this->get();
|
248 |
+
$pattern .= $this->a;
|
249 |
+
if ($this->a === ']') {
|
250 |
+
break;
|
251 |
+
}
|
252 |
+
if ($this->a === '\\') {
|
253 |
+
$this->output .= $this->a;
|
254 |
+
$this->a = $this->get();
|
255 |
+
$pattern .= $this->a;
|
256 |
+
}
|
257 |
+
if ($this->isEOF($this->a)) {
|
258 |
+
throw new JSMin_UnterminatedRegExpException(
|
259 |
+
"JSMin: Unterminated set in RegExp at byte "
|
260 |
+
. $this->inputIndex .": {$pattern}");
|
261 |
+
}
|
262 |
+
}
|
263 |
+
}
|
264 |
|
265 |
+
if ($this->a === '/') { // end pattern
|
266 |
+
break; // while (true)
|
267 |
+
} elseif ($this->a === '\\') {
|
268 |
+
$this->output .= $this->a;
|
269 |
+
$this->a = $this->get();
|
270 |
+
$pattern .= $this->a;
|
271 |
+
} elseif ($this->isEOF($this->a)) {
|
272 |
+
throw new JSMin_UnterminatedRegExpException(
|
273 |
+
"JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}");
|
274 |
+
}
|
275 |
+
$this->output .= $this->a;
|
276 |
+
$this->lastByteOut = $this->a;
|
277 |
+
}
|
278 |
+
$this->b = $this->next();
|
279 |
+
}
|
280 |
+
// end case ACTION_DELETE_A_B
|
281 |
+
}
|
282 |
+
}
|
283 |
|
284 |
+
/**
|
285 |
+
* @return bool
|
286 |
+
*/
|
287 |
+
protected function isRegexpLiteral()
|
288 |
+
{
|
289 |
+
if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
|
290 |
+
// we obviously aren't dividing
|
291 |
+
return true;
|
292 |
+
}
|
293 |
+
if ($this->a === ' ' || $this->a === "\n") {
|
294 |
+
$length = strlen($this->output);
|
295 |
+
if ($length < 2) { // weird edge case
|
296 |
+
return true;
|
297 |
+
}
|
298 |
+
// you can't divide a keyword
|
299 |
+
if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
|
300 |
+
if ($this->output === $m[0]) { // odd but could happen
|
301 |
+
return true;
|
302 |
+
}
|
303 |
+
// make sure it's a keyword, not end of an identifier
|
304 |
+
$charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
|
305 |
+
if (! $this->isAlphaNum($charBeforeKeyword)) {
|
306 |
+
return true;
|
307 |
+
}
|
308 |
+
}
|
309 |
+
}
|
310 |
+
return false;
|
311 |
+
}
|
312 |
|
313 |
+
/**
|
314 |
+
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
|
315 |
+
* translate it to a space or linefeed.
|
316 |
+
*
|
317 |
+
* @return string
|
318 |
+
*/
|
319 |
+
protected function get()
|
320 |
+
{
|
321 |
+
$c = $this->lookAhead;
|
322 |
+
$this->lookAhead = null;
|
323 |
+
if ($c === null) {
|
324 |
+
// getc(stdin)
|
325 |
+
if ($this->inputIndex < $this->inputLength) {
|
326 |
+
$c = $this->input[$this->inputIndex];
|
327 |
+
$this->inputIndex += 1;
|
328 |
+
} else {
|
329 |
+
$c = null;
|
330 |
+
}
|
331 |
+
}
|
332 |
+
if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
|
333 |
+
return $c;
|
334 |
+
}
|
335 |
+
if ($c === "\r") {
|
336 |
+
return "\n";
|
337 |
+
}
|
338 |
+
return ' ';
|
339 |
+
}
|
340 |
|
341 |
+
/**
|
342 |
+
* Does $a indicate end of input?
|
343 |
+
*
|
344 |
+
* @param string $a
|
345 |
+
* @return bool
|
346 |
+
*/
|
347 |
+
protected function isEOF($a)
|
348 |
+
{
|
349 |
+
return ord($a) <= self::ORD_LF;
|
350 |
+
}
|
351 |
|
352 |
+
/**
|
353 |
+
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
|
354 |
+
*
|
355 |
+
* @return string
|
356 |
+
*/
|
357 |
+
protected function peek()
|
358 |
+
{
|
359 |
+
$this->lookAhead = $this->get();
|
360 |
+
return $this->lookAhead;
|
361 |
+
}
|
362 |
|
363 |
+
/**
|
364 |
+
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
|
365 |
+
*
|
366 |
+
* @param string $c
|
367 |
+
*
|
368 |
+
* @return bool
|
369 |
+
*/
|
370 |
+
protected function isAlphaNum($c)
|
371 |
+
{
|
372 |
+
return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
|
373 |
+
}
|
374 |
|
375 |
+
/**
|
376 |
+
* Consume a single line comment from input (possibly retaining it)
|
377 |
+
*/
|
378 |
+
protected function consumeSingleLineComment()
|
379 |
+
{
|
380 |
+
$comment = '';
|
381 |
+
while (true) {
|
382 |
+
$get = $this->get();
|
383 |
+
$comment .= $get;
|
384 |
+
if (ord($get) <= self::ORD_LF) { // end of line reached
|
385 |
+
// if IE conditional comment
|
386 |
+
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
387 |
+
$this->keptComment .= "/{$comment}";
|
388 |
+
}
|
389 |
+
return;
|
390 |
+
}
|
391 |
+
}
|
392 |
+
}
|
393 |
|
394 |
+
/**
|
395 |
+
* Consume a multiple line comment from input (possibly retaining it)
|
396 |
+
*
|
397 |
+
* @throws JSMin_UnterminatedCommentException
|
398 |
+
*/
|
399 |
+
protected function consumeMultipleLineComment()
|
400 |
+
{
|
401 |
+
$this->get();
|
402 |
+
$comment = '';
|
403 |
+
for(;;) {
|
404 |
+
$get = $this->get();
|
405 |
+
if ($get === '*') {
|
406 |
+
if ($this->peek() === '/') { // end of comment reached
|
407 |
+
$this->get();
|
408 |
+
if (isset($this->options['preserveComments']) &&
|
409 |
+
$this->options['preserveComments'] &&
|
410 |
+
0 === strpos($comment, '!')) {
|
411 |
+
// preserved by YUI Compressor
|
412 |
+
if (!$this->keptComment) {
|
413 |
+
// don't prepend a newline if two comments right after one another
|
414 |
+
$this->keptComment = "\n";
|
415 |
+
}
|
416 |
+
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
|
417 |
+
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
|
418 |
+
// IE conditional
|
419 |
+
$this->keptComment .= "/*{$comment}*/";
|
420 |
+
}
|
421 |
+
return;
|
422 |
+
}
|
423 |
+
} elseif ($get === null) {
|
424 |
+
throw new JSMin_UnterminatedCommentException(
|
425 |
+
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
|
426 |
+
}
|
427 |
+
$comment .= $get;
|
428 |
+
}
|
429 |
+
}
|
430 |
|
431 |
+
/**
|
432 |
+
* Get the next character, skipping over comments. Some comments may be preserved.
|
433 |
+
*
|
434 |
+
* @return string
|
435 |
+
*/
|
436 |
+
protected function next()
|
437 |
+
{
|
438 |
+
$get = $this->get();
|
439 |
+
if ($get === '/') {
|
440 |
+
switch ($this->peek()) {
|
441 |
+
case '/':
|
442 |
+
$this->consumeSingleLineComment();
|
443 |
+
$get = "\n";
|
444 |
+
break;
|
445 |
+
case '*':
|
446 |
+
$this->consumeMultipleLineComment();
|
447 |
+
$get = ' ';
|
448 |
+
break;
|
449 |
+
}
|
450 |
+
}
|
451 |
+
return $get;
|
452 |
+
}
|
453 |
}
|
454 |
|
455 |
+
class JSMin_UnterminatedStringException extends \Exception {}
|
456 |
+
class JSMin_UnterminatedCommentException extends \Exception {}
|
457 |
+
class JSMin_UnterminatedRegExpException extends \Exception {}
|
lib/Minify/JSMinPlus.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
/**
|
4 |
* JSMinPlus version 1.4
|
@@ -172,7 +173,7 @@ define('KEYWORD_WHILE', 'while');
|
|
172 |
define('KEYWORD_WITH', 'with');
|
173 |
|
174 |
|
175 |
-
class
|
176 |
{
|
177 |
private $parser;
|
178 |
private $reserved = array(
|
@@ -202,7 +203,7 @@ class Minify0_JSMinPlus
|
|
202 |
|
203 |
// this is a singleton
|
204 |
if(!$instance)
|
205 |
-
$instance = new
|
206 |
|
207 |
return $instance->min($js, $filename);
|
208 |
}
|
@@ -214,7 +215,7 @@ class Minify0_JSMinPlus
|
|
214 |
$n = $this->parser->parse($js, $filename, 1);
|
215 |
return $this->parseTree($n);
|
216 |
}
|
217 |
-
catch(Exception $e)
|
218 |
{
|
219 |
echo $e->getMessage() . "\n";
|
220 |
}
|
@@ -462,7 +463,7 @@ class Minify0_JSMinPlus
|
|
462 |
break;
|
463 |
|
464 |
case KEYWORD_DEBUGGER:
|
465 |
-
throw new Exception('NOT IMPLEMENTED: DEBUGGER');
|
466 |
break;
|
467 |
|
468 |
case TOKEN_CONDCOMMENT_START:
|
@@ -654,7 +655,7 @@ class Minify0_JSMinPlus
|
|
654 |
break;
|
655 |
|
656 |
default:
|
657 |
-
throw new Exception('UNKNOWN TOKEN TYPE: ' . $n->type);
|
658 |
}
|
659 |
|
660 |
return $s;
|
@@ -921,10 +922,10 @@ class JSParser
|
|
921 |
return $n;
|
922 |
|
923 |
case KEYWORD_WHILE:
|
924 |
-
|
925 |
-
|
926 |
-
|
927 |
-
|
928 |
return $n;
|
929 |
|
930 |
case KEYWORD_DO:
|
@@ -1044,7 +1045,7 @@ class JSParser
|
|
1044 |
|
1045 |
case KEYWORD_VAR:
|
1046 |
case KEYWORD_CONST:
|
1047 |
-
|
1048 |
break;
|
1049 |
|
1050 |
case TOKEN_CONDCOMMENT_START:
|
@@ -1463,7 +1464,7 @@ class JSParser
|
|
1463 |
|
1464 |
case OP_RIGHT_CURLY:
|
1465 |
if (!$this->t->scanOperand && $x->curlyLevel != $cl)
|
1466 |
-
throw new Exception('PANIC: right curly botch');
|
1467 |
break 2;
|
1468 |
|
1469 |
case OP_LEFT_PAREN:
|
@@ -1785,7 +1786,7 @@ class JSTokenizer
|
|
1785 |
|
1786 |
public function mustMatch($tt)
|
1787 |
{
|
1788 |
-
|
1789 |
throw $this->newSyntaxError('Unexpected token; token ' . $tt . ' expected');
|
1790 |
|
1791 |
return $this->currentToken();
|
@@ -2072,7 +2073,7 @@ class JSTokenizer
|
|
2072 |
|
2073 |
public function newSyntaxError($m)
|
2074 |
{
|
2075 |
-
return new Exception('Parse error: ' . $m . ' in file \'' . $this->filename . '\' on line ' . $this->lineno);
|
2076 |
}
|
2077 |
}
|
2078 |
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
/**
|
5 |
* JSMinPlus version 1.4
|
173 |
define('KEYWORD_WITH', 'with');
|
174 |
|
175 |
|
176 |
+
class JSMinPlus
|
177 |
{
|
178 |
private $parser;
|
179 |
private $reserved = array(
|
203 |
|
204 |
// this is a singleton
|
205 |
if(!$instance)
|
206 |
+
$instance = new JSMinPlus();
|
207 |
|
208 |
return $instance->min($js, $filename);
|
209 |
}
|
215 |
$n = $this->parser->parse($js, $filename, 1);
|
216 |
return $this->parseTree($n);
|
217 |
}
|
218 |
+
catch(\Exception $e)
|
219 |
{
|
220 |
echo $e->getMessage() . "\n";
|
221 |
}
|
463 |
break;
|
464 |
|
465 |
case KEYWORD_DEBUGGER:
|
466 |
+
throw new \Exception('NOT IMPLEMENTED: DEBUGGER');
|
467 |
break;
|
468 |
|
469 |
case TOKEN_CONDCOMMENT_START:
|
655 |
break;
|
656 |
|
657 |
default:
|
658 |
+
throw new \Exception('UNKNOWN TOKEN TYPE: ' . $n->type);
|
659 |
}
|
660 |
|
661 |
return $s;
|
922 |
return $n;
|
923 |
|
924 |
case KEYWORD_WHILE:
|
925 |
+
$n = new JSNode($this->t);
|
926 |
+
$n->isLoop = true;
|
927 |
+
$n->condition = $this->ParenExpression($x);
|
928 |
+
$n->body = $this->nest($x, $n);
|
929 |
return $n;
|
930 |
|
931 |
case KEYWORD_DO:
|
1045 |
|
1046 |
case KEYWORD_VAR:
|
1047 |
case KEYWORD_CONST:
|
1048 |
+
$n = $this->Variables($x);
|
1049 |
break;
|
1050 |
|
1051 |
case TOKEN_CONDCOMMENT_START:
|
1464 |
|
1465 |
case OP_RIGHT_CURLY:
|
1466 |
if (!$this->t->scanOperand && $x->curlyLevel != $cl)
|
1467 |
+
throw new \Exception('PANIC: right curly botch');
|
1468 |
break 2;
|
1469 |
|
1470 |
case OP_LEFT_PAREN:
|
1786 |
|
1787 |
public function mustMatch($tt)
|
1788 |
{
|
1789 |
+
if (!$this->match($tt))
|
1790 |
throw $this->newSyntaxError('Unexpected token; token ' . $tt . ' expected');
|
1791 |
|
1792 |
return $this->currentToken();
|
2073 |
|
2074 |
public function newSyntaxError($m)
|
2075 |
{
|
2076 |
+
return new \Exception('Parse error: ' . $m . ' in file \'' . $this->filename . '\' on line ' . $this->lineno);
|
2077 |
}
|
2078 |
}
|
2079 |
|
lib/Minify/Minify.php
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
* Class Minify
|
4 |
* @package Minify
|
@@ -22,681 +24,681 @@
|
|
22 |
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
23 |
* @link http://code.google.com/p/minify/
|
24 |
*/
|
25 |
-
class
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
|
475 |
-
|
476 |
-
|
477 |
-
|
478 |
-
|
479 |
-
|
480 |
-
|
481 |
-
|
482 |
-
|
483 |
-
|
484 |
-
|
485 |
-
|
486 |
-
|
487 |
-
|
488 |
-
|
489 |
-
|
490 |
-
|
491 |
-
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
|
519 |
-
|
520 |
-
|
521 |
-
|
522 |
-
|
523 |
-
|
524 |
-
|
525 |
-
|
526 |
-
|
527 |
-
|
528 |
-
|
529 |
-
|
530 |
-
|
531 |
-
|
532 |
-
|
533 |
-
|
534 |
-
|
535 |
-
|
536 |
-
|
537 |
-
|
538 |
-
|
539 |
-
|
540 |
-
|
541 |
-
|
542 |
-
|
543 |
-
|
544 |
-
|
545 |
-
|
546 |
-
|
547 |
-
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
|
552 |
-
|
553 |
-
|
554 |
-
|
555 |
-
|
556 |
-
|
557 |
-
|
558 |
-
|
559 |
-
|
560 |
-
|
561 |
-
|
562 |
-
|
563 |
-
|
564 |
-
|
565 |
-
|
566 |
-
|
567 |
-
|
568 |
-
|
569 |
-
|
570 |
-
|
571 |
-
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
587 |
-
|
588 |
-
|
589 |
-
|
590 |
-
|
591 |
-
|
592 |
-
|
593 |
-
|
594 |
-
|
595 |
-
|
596 |
-
|
597 |
-
|
598 |
-
|
599 |
-
|
600 |
-
|
601 |
-
|
602 |
-
|
603 |
-
|
604 |
-
|
605 |
-
|
606 |
-
|
607 |
-
|
608 |
-
|
609 |
-
|
610 |
-
|
611 |
-
|
612 |
-
|
613 |
-
|
614 |
-
|
615 |
-
|
616 |
-
|
617 |
-
|
618 |
-
|
619 |
if ( $type === self::TYPE_JS ) {
|
620 |
-
|
621 |
}
|
622 |
|
623 |
-
|
624 |
-
|
625 |
-
|
626 |
-
|
627 |
-
|
628 |
-
|
629 |
-
|
630 |
-
|
631 |
-
|
632 |
-
|
633 |
-
|
634 |
-
|
635 |
-
|
636 |
-
|
637 |
-
|
638 |
-
|
639 |
-
|
640 |
-
|
641 |
-
|
642 |
-
|
643 |
-
|
644 |
-
|
645 |
-
|
646 |
-
|
647 |
-
|
648 |
-
|
649 |
-
|
650 |
-
|
651 |
-
|
652 |
-
|
653 |
-
|
654 |
-
|
655 |
-
|
656 |
-
|
657 |
-
|
658 |
-
|
659 |
-
|
660 |
-
|
661 |
-
|
662 |
-
|
663 |
-
|
664 |
-
|
665 |
-
|
666 |
-
|
667 |
-
|
668 |
-
|
669 |
-
|
670 |
-
|
671 |
-
|
672 |
-
|
673 |
-
|
674 |
-
|
675 |
-
|
676 |
-
|
677 |
-
|
678 |
-
|
679 |
-
|
680 |
-
|
681 |
-
|
682 |
-
|
683 |
-
|
684 |
-
|
685 |
-
|
686 |
-
|
687 |
-
|
688 |
-
|
689 |
-
|
690 |
-
|
691 |
-
|
692 |
-
|
693 |
-
|
694 |
-
|
695 |
-
|
696 |
-
|
697 |
-
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
-
|
702 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
+
|
4 |
/**
|
5 |
* Class Minify
|
6 |
* @package Minify
|
24 |
* @license http://opensource.org/licenses/bsd-license.php New BSD License
|
25 |
* @link http://code.google.com/p/minify/
|
26 |
*/
|
27 |
+
class Minify {
|
28 |
+
|
29 |
+
const VERSION = '2.1.7';
|
30 |
+
const TYPE_CSS = 'text/css';
|
31 |
+
const TYPE_HTML = 'text/html';
|
32 |
+
// there is some debate over the ideal JS Content-Type, but this is the
|
33 |
+
// Apache default and what Yahoo! uses..
|
34 |
+
const TYPE_JS = 'application/x-javascript';
|
35 |
+
const URL_DEBUG = 'http://code.google.com/p/minify/wiki/Debugging';
|
36 |
+
|
37 |
+
/**
|
38 |
+
* How many hours behind are the file modification times of uploaded files?
|
39 |
+
*
|
40 |
+
* If you upload files from Windows to a non-Windows server, Windows may report
|
41 |
+
* incorrect mtimes for the files. Immediately after modifying and uploading a
|
42 |
+
* file, use the touch command to update the mtime on the server. If the mtime
|
43 |
+
* jumps ahead by a number of hours, set this variable to that number. If the mtime
|
44 |
+
* moves back, this should not be needed.
|
45 |
+
*
|
46 |
+
* @var int $uploaderHoursBehind
|
47 |
+
*/
|
48 |
+
public static $uploaderHoursBehind = 0;
|
49 |
+
|
50 |
+
/**
|
51 |
+
* If this string is not empty AND the serve() option 'bubbleCssImports' is
|
52 |
+
* NOT set, then serve() will check CSS files for @import declarations that
|
53 |
+
* appear too late in the combined stylesheet. If found, serve() will prepend
|
54 |
+
* the output with this warning.
|
55 |
+
*
|
56 |
+
* @var string $importWarning
|
57 |
+
*/
|
58 |
+
public static $importWarning = "/* See http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files */\n";
|
59 |
+
|
60 |
+
/**
|
61 |
+
* Has the DOCUMENT_ROOT been set in user code?
|
62 |
+
*
|
63 |
+
* @var bool
|
64 |
+
*/
|
65 |
+
public static $isDocRootSet = false;
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Cache ID
|
69 |
+
* @var string
|
70 |
+
*/
|
71 |
+
protected static $_cacheId = null;
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Specify a cache object (with identical interface as Minify_Cache_File) or
|
75 |
+
* a path to use with Minify_Cache_File.
|
76 |
+
*
|
77 |
+
* If not called, Minify will not use a cache and, for each 200 response, will
|
78 |
+
* need to recombine files, minify and encode the output.
|
79 |
+
*
|
80 |
+
* @param mixed $cache object with identical interface as Minify_Cache_File or
|
81 |
+
* a directory path, or null to disable caching. (default = '')
|
82 |
+
*
|
83 |
+
* @param bool $fileLocking (default = true) This only applies if the first
|
84 |
+
* parameter is a string.
|
85 |
+
*
|
86 |
+
* @return null
|
87 |
+
*/
|
88 |
+
public static function setCache($cache = '', $fileLocking = true)
|
89 |
+
{
|
90 |
+
if (is_string($cache)) {
|
91 |
+
self::$_cache = new Minify_Cache_File($cache, $fileLocking);
|
92 |
+
} else {
|
93 |
+
self::$_cache = $cache;
|
94 |
+
}
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Serve a request for a minified file.
|
99 |
+
*
|
100 |
+
* Here are the available options and defaults in the base controller:
|
101 |
+
*
|
102 |
+
* 'isPublic' : send "public" instead of "private" in Cache-Control
|
103 |
+
* headers, allowing shared caches to cache the output. (default true)
|
104 |
+
*
|
105 |
+
* 'quiet' : set to true to have serve() return an array rather than sending
|
106 |
+
* any headers/output (default false)
|
107 |
+
*
|
108 |
+
* 'encodeOutput' : set to false to disable content encoding, and not send
|
109 |
+
* the Vary header (default true)
|
110 |
+
*
|
111 |
+
* 'encodeMethod' : generally you should let this be determined by
|
112 |
+
* HTTP_Encoder (leave null), but you can force a particular encoding
|
113 |
+
* to be returned, by setting this to 'gzip' or '' (no encoding)
|
114 |
+
*
|
115 |
+
* 'encodeLevel' : level of encoding compression (0 to 9, default 9)
|
116 |
+
*
|
117 |
+
* 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
|
118 |
+
* value to remove. (default 'utf-8')
|
119 |
+
*
|
120 |
+
* 'maxAge' : set this to the number of seconds the client should use its cache
|
121 |
+
* before revalidating with the server. This sets Cache-Control: max-age and the
|
122 |
+
* Expires header. Unlike the old 'setExpires' setting, this setting will NOT
|
123 |
+
* prevent conditional GETs. Note this has nothing to do with server-side caching.
|
124 |
+
*
|
125 |
+
* 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
|
126 |
+
* minifier option to enable URI rewriting in CSS files (default true)
|
127 |
+
*
|
128 |
+
* 'bubbleCssImports' : If true, all @import declarations in combined CSS
|
129 |
+
* files will be move to the top. Note this may alter effective CSS values
|
130 |
+
* due to a change in order. (default false)
|
131 |
+
*
|
132 |
+
* 'debug' : set to true to minify all sources with the 'Lines' controller, which
|
133 |
+
* eases the debugging of combined files. This also prevents 304 responses.
|
134 |
+
* @see Minify_Lines::minify()
|
135 |
+
*
|
136 |
+
* 'minifiers' : to override Minify's default choice of minifier function for
|
137 |
+
* a particular content-type, specify your callback under the key of the
|
138 |
+
* content-type:
|
139 |
+
* <code>
|
140 |
+
* // call customCssMinifier($css) for all CSS minification
|
141 |
+
* $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
|
142 |
+
*
|
143 |
+
* // don't minify Javascript at all
|
144 |
+
* $options['minifiers'][Minify::TYPE_JS] = '';
|
145 |
+
* </code>
|
146 |
+
*
|
147 |
+
* 'minifierOptions' : to send options to the minifier function, specify your options
|
148 |
+
* under the key of the content-type. E.g. To send the CSS minifier an option:
|
149 |
+
* <code>
|
150 |
+
* // give CSS minifier array('optionName' => 'optionValue') as 2nd argument
|
151 |
+
* $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
|
152 |
+
* </code>
|
153 |
+
*
|
154 |
+
* 'contentType' : (optional) this is only needed if your file extension is not
|
155 |
+
* js/css/html. The given content-type will be sent regardless of source file
|
156 |
+
* extension, so this should not be used in a Groups config with other
|
157 |
+
* Javascript/CSS files.
|
158 |
+
*
|
159 |
+
* Any controller options are documented in that controller's setupSources() method.
|
160 |
+
*
|
161 |
+
* @param mixed $controller instance of subclass of Minify_Controller_Base or string
|
162 |
+
* name of controller. E.g. 'Files'
|
163 |
+
*
|
164 |
+
* @param array $options controller/serve options
|
165 |
+
*
|
166 |
+
* @return null|array if the 'quiet' option is set to true, an array
|
167 |
+
* with keys "success" (bool), "statusCode" (int), "content" (string), and
|
168 |
+
* "headers" (array).
|
169 |
+
*
|
170 |
+
* @throws Exception
|
171 |
+
*/
|
172 |
+
public static function serve($controller, $options = array())
|
173 |
+
{
|
174 |
+
if (! self::$isDocRootSet && 0 === stripos(PHP_OS, 'win')) {
|
175 |
+
self::setDocRoot();
|
176 |
+
}
|
177 |
+
|
178 |
+
if (is_string($controller)) {
|
179 |
+
// make $controller into object
|
180 |
+
$class = '\W3TCL\Minify\Minify_Controller_' . $controller;
|
181 |
+
$controller = new $class();
|
182 |
+
/* @var Minify_Controller_Base $controller */
|
183 |
+
}
|
184 |
+
|
185 |
+
// set up controller sources and mix remaining options with
|
186 |
+
// controller defaults
|
187 |
+
$options = $controller->setupSources($options);
|
188 |
+
$options = $controller->analyzeSources($options);
|
189 |
+
self::$_options = $controller->mixInDefaultOptions($options);
|
190 |
+
|
191 |
+
// check request validity
|
192 |
+
if (! $controller->sources) {
|
193 |
+
// invalid request!
|
194 |
+
if (! self::$_options['quiet']) {
|
195 |
+
self::_errorExit(self::$_options['badRequestHeader'], self::URL_DEBUG);
|
196 |
+
} else {
|
197 |
+
list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']);
|
198 |
+
return array(
|
199 |
+
'success' => false
|
200 |
+
,'statusCode' => (int)$statusCode
|
201 |
+
,'content' => ''
|
202 |
+
,'headers' => array()
|
203 |
+
);
|
204 |
+
}
|
205 |
+
}
|
206 |
+
|
207 |
+
self::$_controller = $controller;
|
208 |
+
|
209 |
+
if (self::$_options['debug']) {
|
210 |
+
self::_setupDebug($controller->sources);
|
211 |
+
self::$_options['maxAge'] = 0;
|
212 |
+
}
|
213 |
+
|
214 |
+
// determine encoding
|
215 |
+
if (self::$_options['encodeOutput']) {
|
216 |
+
$sendVary = true;
|
217 |
+
if (self::$_options['encodeMethod'] !== null) {
|
218 |
+
// controller specifically requested this
|
219 |
+
$contentEncoding = self::$_options['encodeMethod'];
|
220 |
+
} else {
|
221 |
+
// sniff request header
|
222 |
+
// depending on what the client accepts, $contentEncoding may be
|
223 |
+
// 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
|
224 |
+
// getAcceptedEncoding(false, false) leaves out compress and deflate as options.
|
225 |
+
list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
|
226 |
+
$sendVary = ! HTTP_Encoder::isBuggyIe();
|
227 |
+
}
|
228 |
+
} else {
|
229 |
+
self::$_options['encodeMethod'] = ''; // identity (no encoding)
|
230 |
+
}
|
231 |
+
|
232 |
+
// check client cache
|
233 |
+
$cgOptions = array(
|
234 |
+
'lastModifiedTime' => self::$_options['lastModifiedTime']
|
235 |
+
,'cacheHeaders' => self::$_options['cacheHeaders']
|
236 |
+
,'isPublic' => self::$_options['isPublic']
|
237 |
+
,'encoding' => self::$_options['encodeMethod']
|
238 |
+
);
|
239 |
+
if (self::$_options['maxAge'] > 0) {
|
240 |
+
$cgOptions['maxAge'] = self::$_options['maxAge'];
|
241 |
+
} elseif (self::$_options['debug']) {
|
242 |
+
$cgOptions['invalidate'] = true;
|
243 |
+
}
|
244 |
+
|
245 |
+
$cg = new HTTP_ConditionalGet($cgOptions);
|
246 |
+
if ( $cg->cacheIsValid && !self::$_options['disable_304'] &&
|
247 |
+
!defined( 'W3TC_MINIFY_CONDITIONAL_OFF' ) ) {
|
248 |
+
// client's cache is valid
|
249 |
+
if (! self::$_options['quiet']) {
|
250 |
+
self::$lastServed = array(
|
251 |
+
'fullCacheId' =>self::_getCacheId() .
|
252 |
+
(self::$_options['encodeMethod']
|
253 |
+
? '.' . self::$_options['encodeMethod']
|
254 |
+
: '')
|
255 |
+
);
|
256 |
+
$cg->sendHeaders();
|
257 |
+
return;
|
258 |
+
} else {
|
259 |
+
return array(
|
260 |
+
'success' => true
|
261 |
+
,'statusCode' => 304
|
262 |
+
,'content' => ''
|
263 |
+
,'headers' => $cg->getHeaders()
|
264 |
+
);
|
265 |
+
}
|
266 |
+
} else {
|
267 |
+
// client will need output
|
268 |
+
$headers = $cg->getHeaders();
|
269 |
+
unset($cg);
|
270 |
+
}
|
271 |
+
|
272 |
+
if (self::$_options['contentType'] === self::TYPE_CSS
|
273 |
+
&& self::$_options['rewriteCssUris']) {
|
274 |
+
foreach($controller->sources as $key => $source) {
|
275 |
+
if (self::$_options['rewriteCssUris']
|
276 |
+
&& $source->filepath
|
277 |
+
&& !isset($source->minifyOptions['currentDir'])
|
278 |
+
&& !isset($source->minifyOptions['prependRelativePath'])
|
279 |
+
) {
|
280 |
+
$source->minifyOptions['currentDir'] = dirname($source->filepath);
|
281 |
+
}
|
282 |
+
|
283 |
+
$source->minifyOptions['processCssImports'] = self::$_options['processCssImports'];
|
284 |
+
}
|
285 |
+
}
|
286 |
+
|
287 |
+
// check server cache
|
288 |
+
$content = array();
|
289 |
+
if (null !== self::$_cache && ! self::$_options['debug']) {
|
290 |
+
// using cache
|
291 |
+
// the goal is to use only the cache methods to sniff the length and
|
292 |
+
// output the content, as they do not require ever loading the file into
|
293 |
+
// memory.
|
294 |
+
$cacheId = self::_getCacheId();
|
295 |
+
$fullCacheId = (self::$_options['encodeMethod'])
|
296 |
+
? $cacheId . '.' . self::$_options['encodeMethod']
|
297 |
+
: $cacheId;
|
298 |
+
// check cache for valid entry
|
299 |
+
$cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']);
|
300 |
+
if ($cacheIsReady) {
|
301 |
+
$cacheContentLength = self::$_cache->getSize($fullCacheId);
|
302 |
+
} else {
|
303 |
+
// generate & cache content
|
304 |
+
try {
|
305 |
+
$content = self::_combineMinify();
|
306 |
+
} catch (\Exception $e) {
|
307 |
+
self::$_controller->log($e->getMessage());
|
308 |
+
if (! self::$_options['quiet']) {
|
309 |
+
self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
|
310 |
+
}
|
311 |
+
throw $e;
|
312 |
+
}
|
313 |
+
self::$_cache->store($cacheId, $content);
|
314 |
+
if (function_exists('brotli_compress') && self::$_options['encodeMethod'] === 'br' && self::$_options['encodeOutput']) {
|
315 |
+
$compressed = $content;
|
316 |
+
$compressed['content'] = brotli_compress($content['content']);
|
317 |
+
|
318 |
+
self::$_cache->store($cacheId . '_' . self::$_options['encodeMethod'],
|
319 |
+
$compressed);
|
320 |
+
}
|
321 |
+
if (function_exists('gzencode') && self::$_options['encodeMethod'] && self::$_options['encodeMethod'] !== 'br' && self::$_options['encodeOutput']) {
|
322 |
+
$compressed = $content;
|
323 |
+
$compressed['content'] = gzencode($content['content'],
|
324 |
+
self::$_options['encodeLevel']);
|
325 |
+
|
326 |
+
self::$_cache->store($cacheId . '_' . self::$_options['encodeMethod'],
|
327 |
+
$compressed);
|
328 |
+
}
|
329 |
+
}
|
330 |
+
} else {
|
331 |
+
// no cache
|
332 |
+
$cacheIsReady = false;
|
333 |
+
try {
|
334 |
+
$content = self::_combineMinify();
|
335 |
+
} catch (\Exception $e) {
|
336 |
+
self::$_controller->log($e->getMessage());
|
337 |
+
if (! self::$_options['quiet']) {
|
338 |
+
self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
|
339 |
+
}
|
340 |
+
throw $e;
|
341 |
+
}
|
342 |
+
}
|
343 |
+
if (! $cacheIsReady && self::$_options['encodeMethod']) {
|
344 |
+
switch (self::$_options['encodeMethod']) {
|
345 |
+
case 'gzip':
|
346 |
+
$content['content'] = gzencode($content['content'], self::$_options['encodeLevel']);
|
347 |
+
break;
|
348 |
+
|
349 |
+
case 'deflate':
|
350 |
+
$content['content'] = gzdeflate($content['content'], self::$_options['encodeLevel']);
|
351 |
+
break;
|
352 |
+
|
353 |
+
case 'br':
|
354 |
+
$content['content'] = brotli_compress($content['content']);
|
355 |
+
break;
|
356 |
+
}
|
357 |
+
// still need to encode
|
358 |
+
}
|
359 |
+
|
360 |
+
// add headers
|
361 |
+
if ( !defined( 'W3TC_MINIFY_CONTENT_LENGTH_OFF' ) ) {
|
362 |
+
$headers['Content-Length'] = $cacheIsReady
|
363 |
+
? $cacheContentLength
|
364 |
+
: ((function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
365 |
+
? mb_strlen($content['content'], '8bit')
|
366 |
+
: strlen($content['content'])
|
367 |
+
);
|
368 |
+
}
|
369 |
+
|
370 |
+
$headers['Content-Type'] = self::$_options['contentTypeCharset']
|
371 |
+
? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset']
|
372 |
+
: self::$_options['contentType'];
|
373 |
+
if (self::$_options['encodeMethod'] !== '') {
|
374 |
+
$headers['Content-Encoding'] = $contentEncoding;
|
375 |
+
}
|
376 |
+
if (self::$_options['encodeOutput'] && $sendVary) {
|
377 |
+
$headers['Vary'] = 'Accept-Encoding';
|
378 |
+
}
|
379 |
+
|
380 |
+
self::$lastServed = $content;
|
381 |
+
|
382 |
+
if (! self::$_options['quiet']) {
|
383 |
+
// output headers & content
|
384 |
+
foreach ($headers as $name => $val) {
|
385 |
+
header($name . ': ' . $val);
|
386 |
+
}
|
387 |
+
if ($cacheIsReady) {
|
388 |
+
self::$lastServed['fullCacheId'] = $fullCacheId;
|
389 |
+
self::$_cache->display($fullCacheId);
|
390 |
+
} else {
|
391 |
+
echo $content['content'];
|
392 |
+
}
|
393 |
+
} else {
|
394 |
+
if ($cacheIsReady)
|
395 |
+
$content = self::$_cache->fetch($fullCacheId);
|
396 |
+
|
397 |
+
return array(
|
398 |
+
'success' => true
|
399 |
+
,'statusCode' => 200
|
400 |
+
,'content' => $content['content']
|
401 |
+
,'headers' => $headers
|
402 |
+
);
|
403 |
+
}
|
404 |
+
}
|
405 |
+
|
406 |
+
private static $lastServed = array();
|
407 |
+
|
408 |
+
public static function getUsageStatistics() {
|
409 |
+
if (isset(self::$lastServed['fullCacheId']))
|
410 |
+
self::$lastServed = self::$_cache->fetch(self::$lastServed['fullCacheId']);
|
411 |
+
|
412 |
+
if (isset(self::$lastServed['content']) &&
|
413 |
+
isset(self::$lastServed['originalLength'])) {
|
414 |
+
return array(
|
415 |
+
'content_type' => self::$_options['contentType'],
|
416 |
+
'content_original_length' => self::$lastServed['originalLength'],
|
417 |
+
'content_output_length' => strlen(self::$lastServed['content'])
|
418 |
+
);
|
419 |
+
}
|
420 |
+
|
421 |
+
return array();
|
422 |
+
}
|
423 |
+
|
424 |
+
/**
|
425 |
+
* Return combined minified content for a set of sources
|
426 |
+
*
|
427 |
+
* No internal caching will be used and the content will not be HTTP encoded.
|
428 |
+
*
|
429 |
+
* @param array $sources array of filepaths and/or Minify_Source objects
|
430 |
+
*
|
431 |
+
* @param array $options (optional) array of options for serve. By default
|
432 |
+
* these are already set: quiet = true, encodeMethod = '', lastModifiedTime = 0.
|
433 |
+
*
|
434 |
+
* @return string
|
435 |
+
*/
|
436 |
+
public static function combine($sources, $options = array())
|
437 |
+
{
|
438 |
+
$cache = self::$_cache;
|
439 |
+
self::$_cache = null;
|
440 |
+
$options = array_merge(array(
|
441 |
+
'files' => (array)$sources
|
442 |
+
,'quiet' => true
|
443 |
+
,'encodeMethod' => ''
|
444 |
+
,'lastModifiedTime' => 0
|
445 |
+
), $options);
|
446 |
+
$out = self::serve('Files', $options);
|
447 |
+
self::$_cache = $cache;
|
448 |
+
return $out['content'];
|
449 |
+
}
|
450 |
+
|
451 |
+
/**
|
452 |
+
* Set $_SERVER['DOCUMENT_ROOT']. On IIS, the value is created from SCRIPT_FILENAME and SCRIPT_NAME.
|
453 |
+
*
|
454 |
+
* @param string $docRoot value to use for DOCUMENT_ROOT
|
455 |
+
*/
|
456 |
+
public static function setDocRoot($docRoot = '')
|
457 |
+
{
|
458 |
+
self::$isDocRootSet = true;
|
459 |
+
if ($docRoot) {
|
460 |
+
$_SERVER['DOCUMENT_ROOT'] = $docRoot;
|
461 |
+
} elseif (isset($_SERVER['SERVER_SOFTWARE'])
|
462 |
+
&& 0 === strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/')) {
|
463 |
+
$_SERVER['DOCUMENT_ROOT'] = substr(
|
464 |
+
$_SERVER['SCRIPT_FILENAME']
|
465 |
+
,0
|
466 |
+
,strlen($_SERVER['SCRIPT_FILENAME']) - strlen($_SERVER['SCRIPT_NAME']));
|
467 |
+
$_SERVER['DOCUMENT_ROOT'] = rtrim($_SERVER['DOCUMENT_ROOT'], '\\');
|
468 |
+
}
|
469 |
+
}
|
470 |
+
|
471 |
+
public static $recoverableError = null;
|
472 |
+
|
473 |
+
/**
|
474 |
+
* Any Minify_Cache_* object or null (i.e. no server cache is used)
|
475 |
+
*
|
476 |
+
* @var Minify_Cache_File
|
477 |
+
*/
|
478 |
+
private static $_cache = null;
|
479 |
+
|
480 |
+
/**
|
481 |
+
* Active controller for current request
|
482 |
+
*
|
483 |
+
* @var Minify_Controller_Base
|
484 |
+
*/
|
485 |
+
protected static $_controller = null;
|
486 |
+
|
487 |
+
/**
|
488 |
+
* Options for current request
|
489 |
+
*
|
490 |
+
* @var array
|
491 |
+
*/
|
492 |
+
protected static $_options = null;
|
493 |
+
|
494 |
+
/**
|
495 |
+
* @param string $header
|
496 |
+
*
|
497 |
+
* @param string $url
|
498 |
+
*/
|
499 |
+
protected static function _errorExit($header, $url)
|
500 |
+
{
|
501 |
+
$url = htmlspecialchars($url, ENT_QUOTES);
|
502 |
+
list(,$h1) = explode(' ', $header, 2);
|
503 |
+
$h1 = htmlspecialchars($h1);
|
504 |
+
// FastCGI environments require 3rd arg to header() to be set
|
505 |
+
list(, $code) = explode(' ', $header, 3);
|
506 |
+
header($header, true, $code);
|
507 |
+
header('Content-Type: text/html; charset=utf-8');
|
508 |
+
echo "<h1>$h1</h1>";
|
509 |
+
echo "<p>Please see <a href='$url'>$url</a>.</p>";
|
510 |
+
exit();
|
511 |
+
}
|
512 |
+
|
513 |
+
/**
|
514 |
+
* Set up sources to use Minify_Lines
|
515 |
+
*
|
516 |
+
* @param Minify_Source[] $sources Minify_Source instances
|
517 |
+
*/
|
518 |
+
protected static function _setupDebug($sources)
|
519 |
+
{
|
520 |
+
foreach ($sources as $source) {
|
521 |
+
$source->minifier = array('\W3TCL\Minify\Minify_Lines', 'minify');
|
522 |
+
$id = $source->getId();
|
523 |
+
$source->minifyOptions = array(
|
524 |
+
'id' => (is_file($id) ? basename($id) : $id)
|
525 |
+
);
|
526 |
+
}
|
527 |
+
}
|
528 |
+
|
529 |
+
/**
|
530 |
+
* Combines sources and minifies the result.
|
531 |
+
*
|
532 |
+
* @return string
|
533 |
+
*
|
534 |
+
* @throws Exception
|
535 |
+
*/
|
536 |
+
protected static function _combineMinify()
|
537 |
+
{
|
538 |
+
$type = self::$_options['contentType']; // ease readability
|
539 |
+
|
540 |
+
// when combining scripts, make sure all statements separated and
|
541 |
+
// trailing single line comment is terminated
|
542 |
+
$implodeSeparator = ($type === self::TYPE_JS)
|
543 |
+
? "\n;"
|
544 |
+
: '';
|
545 |
+
// allow the user to pass a particular array of options to each
|
546 |
+
// minifier (designated by type). source objects may still override
|
547 |
+
// these
|
548 |
+
$defaultOptions = isset(self::$_options['minifierOptions'][$type])
|
549 |
+
? self::$_options['minifierOptions'][$type]
|
550 |
+
: array();
|
551 |
+
// if minifier not set, default is no minification. source objects
|
552 |
+
// may still override this
|
553 |
+
$defaultMinifier = isset(self::$_options['minifiers'][$type])
|
554 |
+
? self::$_options['minifiers'][$type]
|
555 |
+
: false;
|
556 |
+
|
557 |
+
// process groups of sources with identical minifiers/options
|
558 |
+
$content = array();
|
559 |
+
$originalLength = 0;
|
560 |
+
$i = 0;
|
561 |
+
$l = count(self::$_controller->sources);
|
562 |
+
$groupToProcessTogether = array();
|
563 |
+
$lastMinifier = null;
|
564 |
+
$lastOptions = null;
|
565 |
+
do {
|
566 |
+
// get next source
|
567 |
+
$source = null;
|
568 |
+
if ($i < $l) {
|
569 |
+
$source = self::$_controller->sources[$i];
|
570 |
+
/* @var Minify_Source $source */
|
571 |
+
$sourceContent = $source->getContent();
|
572 |
+
$originalLength += strlen($sourceContent);
|
573 |
+
|
574 |
+
// allow the source to override our minifier and options
|
575 |
+
$minifier = (null !== $source->minifier)
|
576 |
+
? $source->minifier
|
577 |
+
: $defaultMinifier;
|
578 |
+
$options = (null !== $source->minifyOptions)
|
579 |
+
? array_merge($defaultOptions, $source->minifyOptions)
|
580 |
+
: $defaultOptions;
|
581 |
+
}
|
582 |
+
// do we need to process our group right now?
|
583 |
+
if ($i > 0 // yes, we have at least the first group populated
|
584 |
+
&& (
|
585 |
+
! $source // yes, we ran out of sources
|
586 |
+
|| $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits)
|
587 |
+
|| $minifier !== $lastMinifier // yes, minifier changed
|
588 |
+
|| $options !== $lastOptions) // yes, options changed
|
589 |
+
)
|
590 |
+
{
|
591 |
+
// minify previous sources with last settings
|
592 |
+
$imploded = implode($implodeSeparator, $groupToProcessTogether);
|
593 |
+
$groupToProcessTogether = array();
|
594 |
+
if ($lastMinifier) {
|
595 |
+
try {
|
596 |
+
$content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
|
597 |
+
} catch (\Exception $e) {
|
598 |
+
throw new \Exception("Exception in minifier: " . $e->getMessage());
|
599 |
+
}
|
600 |
+
} else {
|
601 |
+
$content[] = $imploded;
|
602 |
+
}
|
603 |
+
}
|
604 |
+
// add content to the group
|
605 |
+
if ($source) {
|
606 |
+
$groupToProcessTogether[] = $sourceContent;
|
607 |
+
$lastMinifier = $minifier;
|
608 |
+
$lastOptions = $options;
|
609 |
+
}
|
610 |
+
$i++;
|
611 |
+
} while ($source);
|
612 |
+
|
613 |
+
$content = implode($implodeSeparator, $content);
|
614 |
+
|
615 |
+
if ($type === self::TYPE_CSS && false !== strpos($content, '@import')) {
|
616 |
+
$content = self::_handleCssImports($content);
|
617 |
+
}
|
618 |
+
if ( $type === self::TYPE_CSS ) {
|
619 |
+
$content = apply_filters( 'w3tc_minify_css_content', $content, null, null );
|
620 |
+
}
|
621 |
if ( $type === self::TYPE_JS ) {
|
622 |
+
$content = apply_filters( 'w3tc_minify_js_content', $content, null, null );
|
623 |
}
|
624 |
|
625 |
+
// do any post-processing (esp. for editing build URIs)
|
626 |
+
if (self::$_options['postprocessorRequire']) {
|
627 |
+
require_once self::$_options['postprocessorRequire'];
|
628 |
+
}
|
629 |
+
if (self::$_options['postprocessor']) {
|
630 |
+
$content = call_user_func(self::$_options['postprocessor'], $content, $type);
|
631 |
+
}
|
632 |
+
return array(
|
633 |
+
'originalLength' => $originalLength,
|
634 |
+
'content' => $content
|
635 |
+
);
|
636 |
+
}
|
637 |
+
|
638 |
+
/**
|
639 |
+
* Sets cache ID
|
640 |
+
* @param string $cacheId
|
641 |
+
*/
|
642 |
+
public static function setCacheId($cacheId = null)
|
643 |
+
{
|
644 |
+
self::$_cacheId = $cacheId;
|
645 |
+
}
|
646 |
+
|
647 |
+
/**
|
648 |
+
* Returns cache ID
|
649 |
+
*/
|
650 |
+
public static function getCacheId()
|
651 |
+
{
|
652 |
+
return self::$_cacheId;
|
653 |
+
}
|
654 |
+
|
655 |
+
/**
|
656 |
+
* Make a unique cache id for for this request.
|
657 |
+
*
|
658 |
+
* Any settings that could affect output are taken into consideration
|
659 |
+
*
|
660 |
+
* @param string $prefix
|
661 |
+
*
|
662 |
+
* @return string
|
663 |
+
*/
|
664 |
+
protected static function _getCacheId($prefix = 'minify')
|
665 |
+
{
|
666 |
+
return (self::$_cacheId ? self::$_cacheId : md5(serialize(array(
|
667 |
+
Minify_Source::getDigest(self::$_controller->sources)
|
668 |
+
,self::$_options['minifiers']
|
669 |
+
,self::$_options['minifierOptions']
|
670 |
+
,self::$_options['postprocessor']
|
671 |
+
,self::$_options['bubbleCssImports']
|
672 |
+
,self::$_options['processCssImports']
|
673 |
+
))));
|
674 |
+
}
|
675 |
+
|
676 |
+
/**
|
677 |
+
* Bubble CSS @imports to the top or prepend a warning if an import is detected not at the top.
|
678 |
+
*
|
679 |
+
* @param string $css
|
680 |
+
*
|
681 |
+
* @return string
|
682 |
+
*/
|
683 |
+
protected static function _handleCssImports($css)
|
684 |
+
{
|
685 |
+
if (self::$_options['bubbleCssImports']) {
|
686 |
+
// bubble CSS imports
|
687 |
+
preg_match_all('/@import.*?;/', $css, $imports);
|
688 |
+
$css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css);
|
689 |
+
} else if ('' !== self::$importWarning) {
|
690 |
+
// remove comments so we don't mistake { in a comment as a block
|
691 |
+
$noCommentCss = preg_replace('@/\\*[\\s\\S]*?\\*/@', '', $css);
|
692 |
+
$lastImportPos = strrpos($noCommentCss, '@import');
|
693 |
+
$firstBlockPos = strpos($noCommentCss, '{');
|
694 |
+
if (false !== $lastImportPos
|
695 |
+
&& false !== $firstBlockPos
|
696 |
+
&& $firstBlockPos < $lastImportPos
|
697 |
+
) {
|
698 |
+
// { appears before @import : prepend warning
|
699 |
+
$css = self::$importWarning . $css;
|
700 |
+
}
|
701 |
+
}
|
702 |
+
return $css;
|
703 |
+
}
|
704 |
}
|
lib/Minify/Minify/Build.php
CHANGED
@@ -1,101 +1,103 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Build
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Maintain a single last modification time for a group of Minify sources to
|
9 |
* allow use of far off Expires headers in Minify.
|
10 |
-
*
|
11 |
* <code>
|
12 |
* // in config file
|
13 |
* $groupSources = array(
|
14 |
* 'js' => array('file1.js', 'file2.js')
|
15 |
* ,'css' => array('file1.css', 'file2.css', 'file3.css')
|
16 |
* )
|
17 |
-
*
|
18 |
* // during HTML generation
|
19 |
* $jsBuild = new Minify_Build($groupSources['js']);
|
20 |
* $cssBuild = new Minify_Build($groupSources['css']);
|
21 |
-
*
|
22 |
* $script = "<script type='text/javascript' src='"
|
23 |
* . $jsBuild->uri('/min.php/js') . "'></script>";
|
24 |
* $link = "<link rel='stylesheet' type='text/css' href='"
|
25 |
* . $cssBuild->uri('/min.php/css') . "'>";
|
26 |
-
*
|
27 |
* // in min.php
|
28 |
* Minify::serve('Groups', array(
|
29 |
* 'groups' => $groupSources
|
30 |
* ,'setExpires' => (time() + 86400 * 365)
|
31 |
* ));
|
32 |
* </code>
|
33 |
-
*
|
34 |
* @package Minify
|
35 |
* @author Stephen Clay <steve@mrclay.org>
|
36 |
*/
|
37 |
class Minify_Build {
|
38 |
-
|
39 |
-
/**
|
40 |
-
* Last modification time of all files in the build
|
41 |
-
*
|
42 |
-
* @var int
|
43 |
-
*/
|
44 |
-
public $lastModified = 0;
|
45 |
-
|
46 |
-
/**
|
47 |
-
* String to use as ampersand in uri(). Set this to '&' if
|
48 |
-
* you are not HTML-escaping URIs.
|
49 |
-
*
|
50 |
-
* @var string
|
51 |
-
*/
|
52 |
-
public static $ampersand = '&';
|
53 |
-
|
54 |
-
/**
|
55 |
-
* Get a time-stamped URI
|
56 |
-
*
|
57 |
-
* <code>
|
58 |
-
* echo $b->uri('/site.js');
|
59 |
-
* // outputs "/site.js?1678242"
|
60 |
-
*
|
61 |
-
* echo $b->uri('/scriptaculous.js?load=effects');
|
62 |
-
* // outputs "/scriptaculous.js?load=effects&1678242"
|
63 |
-
* </code>
|
64 |
-
*
|
65 |
-
* @param string $uri
|
66 |
-
* @param boolean $forceAmpersand (default = false) Force the use of ampersand to
|
67 |
-
* append the timestamp to the URI.
|
68 |
-
* @return string
|
69 |
-
*/
|
70 |
-
public function uri($uri, $forceAmpersand = false) {
|
71 |
-
$sep = ($forceAmpersand || strpos($uri, '?') !== false)
|
72 |
-
? self::$ampersand
|
73 |
-
: '?';
|
74 |
-
return "{$uri}{$sep}{$this->lastModified}";
|
75 |
-
}
|
76 |
|
77 |
/**
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
101 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
+
|
4 |
/**
|
5 |
+
* Class Minify_Build
|
6 |
* @package Minify
|
7 |
*/
|
8 |
|
9 |
/**
|
10 |
* Maintain a single last modification time for a group of Minify sources to
|
11 |
* allow use of far off Expires headers in Minify.
|
12 |
+
*
|
13 |
* <code>
|
14 |
* // in config file
|
15 |
* $groupSources = array(
|
16 |
* 'js' => array('file1.js', 'file2.js')
|
17 |
* ,'css' => array('file1.css', 'file2.css', 'file3.css')
|
18 |
* )
|
19 |
+
*
|
20 |
* // during HTML generation
|
21 |
* $jsBuild = new Minify_Build($groupSources['js']);
|
22 |
* $cssBuild = new Minify_Build($groupSources['css']);
|
23 |
+
*
|
24 |
* $script = "<script type='text/javascript' src='"
|
25 |
* . $jsBuild->uri('/min.php/js') . "'></script>";
|
26 |
* $link = "<link rel='stylesheet' type='text/css' href='"
|
27 |
* . $cssBuild->uri('/min.php/css') . "'>";
|
28 |
+
*
|
29 |
* // in min.php
|
30 |
* Minify::serve('Groups', array(
|
31 |
* 'groups' => $groupSources
|
32 |
* ,'setExpires' => (time() + 86400 * 365)
|
33 |
* ));
|
34 |
* </code>
|
35 |
+
*
|
36 |
* @package Minify
|
37 |
* @author Stephen Clay <steve@mrclay.org>
|
38 |
*/
|
39 |
class Minify_Build {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
40 |
|
41 |
/**
|
42 |
+
* Last modification time of all files in the build
|
43 |
+
*
|
44 |
+
* @var int
|
45 |
+
*/
|
46 |
+
public $lastModified = 0;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* String to use as ampersand in uri(). Set this to '&' if
|
50 |
+
* you are not HTML-escaping URIs.
|
51 |
+
*
|
52 |
+
* @var string
|
53 |
+
*/
|
54 |
+
public static $ampersand = '&';
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Get a time-stamped URI
|
58 |
+
*
|
59 |
+
* <code>
|
60 |
+
* echo $b->uri('/site.js');
|
61 |
+
* // outputs "/site.js?1678242"
|
62 |
+
*
|
63 |
+
* echo $b->uri('/scriptaculous.js?load=effects');
|
64 |
+
* // outputs "/scriptaculous.js?load=effects&1678242"
|
65 |
+
* </code>
|
66 |
+
*
|
67 |
+
* @param string $uri
|
68 |
+
* @param boolean $forceAmpersand (default = false) Force the use of ampersand to
|
69 |
+
* append the timestamp to the URI.
|
70 |
+
* @return string
|
71 |
+
*/
|
72 |
+
public function uri($uri, $forceAmpersand = false) {
|
73 |
+
$sep = ($forceAmpersand || strpos($uri, '?') !== false)
|
74 |
+
? self::$ampersand
|
75 |
+
: '?';
|
76 |
+
return "{$uri}{$sep}{$this->lastModified}";
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Create a build object
|
81 |
+
*
|
82 |
+
* @param array $sources array of Minify_Source objects and/or file paths
|
83 |
+
*
|
84 |
+
* @return null
|
85 |
+
*/
|
86 |
+
public function __construct($sources)
|
87 |
+
{
|
88 |
+
$max = 0;
|
89 |
+
foreach ((array)$sources as $source) {
|
90 |
+
if ($source instanceof Minify_Source) {
|
91 |
+
$max = max($max, $source->lastModified);
|
92 |
+
} elseif (is_string($source)) {
|
93 |
+
if (0 === strpos($source, '//')) {
|
94 |
+
$source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);
|
95 |
+
}
|
96 |
+
if (is_file($source)) {
|
97 |
+
$max = max($max, filemtime($source));
|
98 |
+
}
|
99 |
+
}
|
100 |
+
}
|
101 |
+
$this->lastModified = $max;
|
102 |
+
}
|
103 |
}
|
lib/Minify/Minify/CSS.php
CHANGED
@@ -1,96 +1,97 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_CSS
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Minify CSS
|
9 |
*
|
10 |
-
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
|
11 |
* minify CSS and rewrite relative URIs.
|
12 |
-
*
|
13 |
* @package Minify
|
14 |
* @author Stephen Clay <steve@mrclay.org>
|
15 |
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
16 |
*/
|
17 |
class Minify_CSS {
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_CSS
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Minify CSS
|
10 |
*
|
11 |
+
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
|
12 |
* minify CSS and rewrite relative URIs.
|
13 |
+
*
|
14 |
* @package Minify
|
15 |
* @author Stephen Clay <steve@mrclay.org>
|
16 |
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
17 |
*/
|
18 |
class Minify_CSS {
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Minify a CSS string
|
22 |
+
*
|
23 |
+
* @param string $css
|
24 |
+
*
|
25 |
+
* @param array $options available options:
|
26 |
+
*
|
27 |
+
* 'preserveComments': (default true) multi-line comments that begin
|
28 |
+
* with "/*!" will be preserved with newlines before and after to
|
29 |
+
* enhance readability.
|
30 |
+
*
|
31 |
+
* 'removeCharsets': (default true) remove all @charset at-rules
|
32 |
+
*
|
33 |
+
* 'prependRelativePath': (default null) if given, this string will be
|
34 |
+
* prepended to all relative URIs in import/url declarations
|
35 |
+
*
|
36 |
+
* 'currentDir': (default null) if given, this is assumed to be the
|
37 |
+
* directory of the current CSS file. Using this, minify will rewrite
|
38 |
+
* all relative URIs in import/url declarations to correctly point to
|
39 |
+
* the desired files. For this to work, the files *must* exist and be
|
40 |
+
* visible by the PHP process.
|
41 |
+
*
|
42 |
+
* 'symlinks': (default = array()) If the CSS file is stored in
|
43 |
+
* a symlink-ed directory, provide an array of link paths to
|
44 |
+
* target paths, where the link paths are within the document root. Because
|
45 |
+
* paths need to be normalized for this to work, use "//" to substitute
|
46 |
+
* the doc root in the link paths (the array keys). E.g.:
|
47 |
+
* <code>
|
48 |
+
* array('//symlink' => '/real/target/path') // unix
|
49 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
50 |
+
* </code>
|
51 |
+
*
|
52 |
+
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
|
53 |
+
* see Minify_CSS_UriRewriter::rewrite
|
54 |
+
*
|
55 |
+
* @return string
|
56 |
+
*/
|
57 |
+
public static function minify($css, $options = array())
|
58 |
+
{
|
59 |
+
$options = array_merge(array(
|
60 |
+
'compress' => true,
|
61 |
+
'removeCharsets' => true,
|
62 |
+
'preserveComments' => true,
|
63 |
+
'currentDir' => null,
|
64 |
+
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
|
65 |
+
'prependRelativePath' => null,
|
66 |
+
'symlinks' => array(),
|
67 |
+
), $options);
|
68 |
+
|
69 |
+
if ($options['removeCharsets']) {
|
70 |
+
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
|
71 |
+
}
|
72 |
+
if ($options['compress']) {
|
73 |
+
if (! $options['preserveComments']) {
|
74 |
+
$css = Minify_CSS_Compressor::process($css, $options);
|
75 |
+
} else {
|
76 |
+
$css = Minify_CommentPreserver::process(
|
77 |
+
$css
|
78 |
+
,array('\W3TCL\Minify\Minify_CSS_Compressor', 'process')
|
79 |
+
,array($options)
|
80 |
+
);
|
81 |
+
}
|
82 |
+
}
|
83 |
+
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
|
84 |
+
return $css;
|
85 |
+
}
|
86 |
+
if ($options['currentDir']) {
|
87 |
+
return Minify_CSS_UriRewriter::rewrite(
|
88 |
+
$css
|
89 |
+
,$options);
|
90 |
+
} else {
|
91 |
+
return Minify_CSS_UriRewriter::prepend(
|
92 |
+
$css
|
93 |
+
,$options['prependRelativePath']
|
94 |
+
);
|
95 |
+
}
|
96 |
+
}
|
97 |
}
|
lib/Minify/Minify/CSS/Compressor.php
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_CSS_Compressor
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
@@ -9,11 +10,11 @@
|
|
9 |
*
|
10 |
* This is a heavy regex-based removal of whitespace, unnecessary
|
11 |
* comments and tokens, and some CSS value minimization, where practical.
|
12 |
-
* Many steps have been taken to avoid breaking comment-based hacks,
|
13 |
* including the ie5/mac filter (and its inversion), but expect tricky
|
14 |
* hacks involving comment tokens in 'content' value strings to break
|
15 |
* minimization badly. A test suite is available.
|
16 |
-
*
|
17 |
* @package Minify
|
18 |
* @author Stephen Clay <steve@mrclay.org>
|
19 |
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
@@ -22,11 +23,11 @@ class Minify_CSS_Compressor {
|
|
22 |
|
23 |
/**
|
24 |
* Minify a CSS string
|
25 |
-
*
|
26 |
* @param string $css
|
27 |
-
*
|
28 |
* @param array $options (currently ignored)
|
29 |
-
*
|
30 |
* @return string
|
31 |
*/
|
32 |
public static function process($css, $options = array())
|
@@ -34,34 +35,34 @@ class Minify_CSS_Compressor {
|
|
34 |
$obj = new Minify_CSS_Compressor($options);
|
35 |
return $obj->_process($css);
|
36 |
}
|
37 |
-
|
38 |
/**
|
39 |
* @var array
|
40 |
*/
|
41 |
protected $_options = null;
|
42 |
-
|
43 |
/**
|
44 |
* Are we "in" a hack? I.e. are some browsers targetted until the next comment?
|
45 |
*
|
46 |
* @var bool
|
47 |
*/
|
48 |
protected $_inHack = false;
|
49 |
-
|
50 |
-
|
51 |
/**
|
52 |
* Constructor
|
53 |
-
*
|
54 |
* @param array $options (currently ignored)
|
55 |
*/
|
56 |
private function __construct($options) {
|
57 |
$this->_options = $options;
|
58 |
}
|
59 |
-
|
60 |
/**
|
61 |
* Minify a CSS string
|
62 |
-
*
|
63 |
* @param string $css
|
64 |
-
*
|
65 |
* @return string
|
66 |
*/
|
67 |
protected function _process($css)
|
@@ -72,16 +73,16 @@ class Minify_CSS_Compressor {
|
|
72 |
$css = preg_replace_callback('~(".*"|\'.*\')~U', array($this, '_removeQuotesCB'), $css);
|
73 |
|
74 |
$css = str_replace("\r\n", "\n", $css);
|
75 |
-
|
76 |
// preserve empty comment after '>'
|
77 |
// http://www.webdevout.net/css-hacks#in_css-selectors
|
78 |
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
|
79 |
-
|
80 |
// preserve empty comment between property and value
|
81 |
// http://css-discuss.incutio.com/?page=BoxModelHack
|
82 |
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
|
83 |
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
|
84 |
-
|
85 |
// apply callback to all valid comments (and strip out surrounding ws
|
86 |
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
|
87 |
,array($this, '_commentCB'), $css);
|
@@ -89,10 +90,10 @@ class Minify_CSS_Compressor {
|
|
89 |
// remove ws around { } and last semicolon in declaration block
|
90 |
$css = preg_replace('/\\s*{\\s*/', '{', $css);
|
91 |
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
|
92 |
-
|
93 |
// remove ws surrounding semicolons
|
94 |
$css = preg_replace('/\\s*;\\s*/', ';', $css);
|
95 |
-
|
96 |
// remove ws around urls
|
97 |
$css = preg_replace('/
|
98 |
url\\( # url(
|
@@ -101,11 +102,11 @@ class Minify_CSS_Compressor {
|
|
101 |
\\s*
|
102 |
\\) # )
|
103 |
/x', 'url($1)', $css);
|
104 |
-
|
105 |
// remove ws between rules and colons
|
106 |
$css = preg_replace('/
|
107 |
\\s*
|
108 |
-
([{;]) # 1 = beginning of block or rule separator
|
109 |
\\s*
|
110 |
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
|
111 |
\\s*
|
@@ -113,7 +114,7 @@ class Minify_CSS_Compressor {
|
|
113 |
\\s*
|
114 |
(\\b|[#\'"-]) # 3 = first character of a value
|
115 |
/x', '$1$2:$3', $css);
|
116 |
-
|
117 |
// remove ws in selectors
|
118 |
$css = preg_replace_callback('/
|
119 |
(?: # non-capture
|
@@ -127,30 +128,30 @@ class Minify_CSS_Compressor {
|
|
127 |
{ # open declaration block
|
128 |
/x'
|
129 |
,array($this, '_selectorsCB'), $css);
|
130 |
-
|
131 |
// minimize hex colors
|
132 |
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
|
133 |
, '$1#$2$3$4$5', $css);
|
134 |
-
|
135 |
// remove spaces between font families
|
136 |
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
|
137 |
,array($this, '_fontFamilyCB'), $css);
|
138 |
-
|
139 |
$css = preg_replace('/@import\\s+url/', '@import url', $css);
|
140 |
-
|
141 |
// replace any ws involving newlines with a single newline
|
142 |
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
|
143 |
-
|
144 |
// separate common descendent selectors w/ newlines (to limit line lengths)
|
145 |
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
|
146 |
-
|
147 |
// Use newline after 1st numeric value (to limit line lengths).
|
148 |
$css = preg_replace('/
|
149 |
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
|
150 |
\\s+
|
151 |
/x'
|
152 |
,"$1\n", $css);
|
153 |
-
|
154 |
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
155 |
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
156 |
|
@@ -169,12 +170,12 @@ class Minify_CSS_Compressor {
|
|
169 |
|
170 |
return trim($css);
|
171 |
}
|
172 |
-
|
173 |
/**
|
174 |
-
* Replace what looks like a set of selectors
|
175 |
*
|
176 |
* @param array $m regex matches
|
177 |
-
*
|
178 |
* @return string
|
179 |
*/
|
180 |
protected function _selectorsCB($m)
|
@@ -182,19 +183,19 @@ class Minify_CSS_Compressor {
|
|
182 |
// remove ws around the combinators
|
183 |
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
|
184 |
}
|
185 |
-
|
186 |
/**
|
187 |
* Process a comment and return a replacement
|
188 |
-
*
|
189 |
* @param array $m regex matches
|
190 |
-
*
|
191 |
* @return string
|
192 |
*/
|
193 |
protected function _commentCB($m)
|
194 |
{
|
195 |
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
|
196 |
-
$m = $m[1];
|
197 |
-
// $m is the comment content w/o the surrounding tokens,
|
198 |
// but the return value will replace the entire comment.
|
199 |
if ($m === 'keep') {
|
200 |
return '/**/';
|
@@ -236,19 +237,19 @@ class Minify_CSS_Compressor {
|
|
236 |
$this->_inHack = false;
|
237 |
return '/**/';
|
238 |
}
|
239 |
-
// Issue 107: if there's any surrounding whitespace, it may be important, so
|
240 |
// replace the comment with a single space
|
241 |
return $hasSurroundingWs // remove all other comments
|
242 |
? ' '
|
243 |
: '';
|
244 |
}
|
245 |
-
|
246 |
/**
|
247 |
* Process a font-family listing and return a replacement
|
248 |
-
*
|
249 |
* @param array $m regex matches
|
250 |
-
*
|
251 |
-
* @return string
|
252 |
*/
|
253 |
protected function _fontFamilyCB($m)
|
254 |
{
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_CSS_Compressor
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
10 |
*
|
11 |
* This is a heavy regex-based removal of whitespace, unnecessary
|
12 |
* comments and tokens, and some CSS value minimization, where practical.
|
13 |
+
* Many steps have been taken to avoid breaking comment-based hacks,
|
14 |
* including the ie5/mac filter (and its inversion), but expect tricky
|
15 |
* hacks involving comment tokens in 'content' value strings to break
|
16 |
* minimization badly. A test suite is available.
|
17 |
+
*
|
18 |
* @package Minify
|
19 |
* @author Stephen Clay <steve@mrclay.org>
|
20 |
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
|
23 |
|
24 |
/**
|
25 |
* Minify a CSS string
|
26 |
+
*
|
27 |
* @param string $css
|
28 |
+
*
|
29 |
* @param array $options (currently ignored)
|
30 |
+
*
|
31 |
* @return string
|
32 |
*/
|
33 |
public static function process($css, $options = array())
|
35 |
$obj = new Minify_CSS_Compressor($options);
|
36 |
return $obj->_process($css);
|
37 |
}
|
38 |
+
|
39 |
/**
|
40 |
* @var array
|
41 |
*/
|
42 |
protected $_options = null;
|
43 |
+
|
44 |
/**
|
45 |
* Are we "in" a hack? I.e. are some browsers targetted until the next comment?
|
46 |
*
|
47 |
* @var bool
|
48 |
*/
|
49 |
protected $_inHack = false;
|
50 |
+
|
51 |
+
|
52 |
/**
|
53 |
* Constructor
|
54 |
+
*
|
55 |
* @param array $options (currently ignored)
|
56 |
*/
|
57 |
private function __construct($options) {
|
58 |
$this->_options = $options;
|
59 |
}
|
60 |
+
|
61 |
/**
|
62 |
* Minify a CSS string
|
63 |
+
*
|
64 |
* @param string $css
|
65 |
+
*
|
66 |
* @return string
|
67 |
*/
|
68 |
protected function _process($css)
|
73 |
$css = preg_replace_callback('~(".*"|\'.*\')~U', array($this, '_removeQuotesCB'), $css);
|
74 |
|
75 |
$css = str_replace("\r\n", "\n", $css);
|
76 |
+
|
77 |
// preserve empty comment after '>'
|
78 |
// http://www.webdevout.net/css-hacks#in_css-selectors
|
79 |
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
|
80 |
+
|
81 |
// preserve empty comment between property and value
|
82 |
// http://css-discuss.incutio.com/?page=BoxModelHack
|
83 |
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
|
84 |
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
|
85 |
+
|
86 |
// apply callback to all valid comments (and strip out surrounding ws
|
87 |
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
|
88 |
,array($this, '_commentCB'), $css);
|
90 |
// remove ws around { } and last semicolon in declaration block
|
91 |
$css = preg_replace('/\\s*{\\s*/', '{', $css);
|
92 |
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
|
93 |
+
|
94 |
// remove ws surrounding semicolons
|
95 |
$css = preg_replace('/\\s*;\\s*/', ';', $css);
|
96 |
+
|
97 |
// remove ws around urls
|
98 |
$css = preg_replace('/
|
99 |
url\\( # url(
|
102 |
\\s*
|
103 |
\\) # )
|
104 |
/x', 'url($1)', $css);
|
105 |
+
|
106 |
// remove ws between rules and colons
|
107 |
$css = preg_replace('/
|
108 |
\\s*
|
109 |
+
([{;]) # 1 = beginning of block or rule separator
|
110 |
\\s*
|
111 |
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
|
112 |
\\s*
|
114 |
\\s*
|
115 |
(\\b|[#\'"-]) # 3 = first character of a value
|
116 |
/x', '$1$2:$3', $css);
|
117 |
+
|
118 |
// remove ws in selectors
|
119 |
$css = preg_replace_callback('/
|
120 |
(?: # non-capture
|
128 |
{ # open declaration block
|
129 |
/x'
|
130 |
,array($this, '_selectorsCB'), $css);
|
131 |
+
|
132 |
// minimize hex colors
|
133 |
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
|
134 |
, '$1#$2$3$4$5', $css);
|
135 |
+
|
136 |
// remove spaces between font families
|
137 |
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
|
138 |
,array($this, '_fontFamilyCB'), $css);
|
139 |
+
|
140 |
$css = preg_replace('/@import\\s+url/', '@import url', $css);
|
141 |
+
|
142 |
// replace any ws involving newlines with a single newline
|
143 |
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
|
144 |
+
|
145 |
// separate common descendent selectors w/ newlines (to limit line lengths)
|
146 |
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
|
147 |
+
|
148 |
// Use newline after 1st numeric value (to limit line lengths).
|
149 |
$css = preg_replace('/
|
150 |
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
|
151 |
\\s+
|
152 |
/x'
|
153 |
,"$1\n", $css);
|
154 |
+
|
155 |
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
156 |
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
157 |
|
170 |
|
171 |
return trim($css);
|
172 |
}
|
173 |
+
|
174 |
/**
|
175 |
+
* Replace what looks like a set of selectors
|
176 |
*
|
177 |
* @param array $m regex matches
|
178 |
+
*
|
179 |
* @return string
|
180 |
*/
|
181 |
protected function _selectorsCB($m)
|
183 |
// remove ws around the combinators
|
184 |
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
|
185 |
}
|
186 |
+
|
187 |
/**
|
188 |
* Process a comment and return a replacement
|
189 |
+
*
|
190 |
* @param array $m regex matches
|
191 |
+
*
|
192 |
* @return string
|
193 |
*/
|
194 |
protected function _commentCB($m)
|
195 |
{
|
196 |
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
|
197 |
+
$m = $m[1];
|
198 |
+
// $m is the comment content w/o the surrounding tokens,
|
199 |
// but the return value will replace the entire comment.
|
200 |
if ($m === 'keep') {
|
201 |
return '/**/';
|
237 |
$this->_inHack = false;
|
238 |
return '/**/';
|
239 |
}
|
240 |
+
// Issue 107: if there's any surrounding whitespace, it may be important, so
|
241 |
// replace the comment with a single space
|
242 |
return $hasSurroundingWs // remove all other comments
|
243 |
? ' '
|
244 |
: '';
|
245 |
}
|
246 |
+
|
247 |
/**
|
248 |
* Process a font-family listing and return a replacement
|
249 |
+
*
|
250 |
* @param array $m regex matches
|
251 |
+
*
|
252 |
+
* @return string
|
253 |
*/
|
254 |
protected function _fontFamilyCB($m)
|
255 |
{
|
lib/Minify/Minify/CSS/UriRewriter.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_CSS_UriRewriter
|
4 |
* @package Minify
|
@@ -12,439 +13,439 @@
|
|
12 |
*/
|
13 |
class Minify_CSS_UriRewriter {
|
14 |
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
|
224 |
-
|
225 |
-
|
226 |
-
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
|
272 |
-
|
273 |
-
|
274 |
-
|
275 |
-
|
276 |
-
|
277 |
-
|
278 |
-
|
279 |
-
|
280 |
-
|
281 |
-
|
282 |
-
|
283 |
-
|
284 |
-
|
285 |
-
|
286 |
-
|
287 |
-
|
288 |
-
|
289 |
-
|
290 |
-
|
291 |
-
|
292 |
-
|
293 |
-
|
294 |
-
|
295 |
-
|
296 |
-
|
297 |
-
|
298 |
-
|
299 |
-
|
300 |
-
|
301 |
-
|
302 |
-
|
303 |
-
|
304 |
-
|
305 |
-
|
306 |
-
|
307 |
-
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
315 |
-
|
316 |
-
|
317 |
-
|
318 |
-
|
319 |
-
|
320 |
-
|
321 |
-
|
322 |
-
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
331 |
-
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
|
366 |
-
|
367 |
-
|
368 |
-
|
369 |
-
|
370 |
-
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
378 |
-
|
379 |
-
|
380 |
-
|
381 |
-
|
382 |
-
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
$uri = self::$_prependPath . $uri;
|
403 |
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
|
429 |
-
|
430 |
-
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
437 |
-
|
438 |
-
|
439 |
-
|
440 |
-
|
441 |
-
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_CSS_UriRewriter
|
5 |
* @package Minify
|
13 |
*/
|
14 |
class Minify_CSS_UriRewriter {
|
15 |
|
16 |
+
/**
|
17 |
+
* rewrite() and rewriteRelative() append debugging information here
|
18 |
+
*
|
19 |
+
* @var string
|
20 |
+
*/
|
21 |
+
public static $debugText = '';
|
22 |
+
|
23 |
+
/**
|
24 |
+
* In CSS content, rewrite file relative URIs as root relative
|
25 |
+
*
|
26 |
+
* @param string $css
|
27 |
+
*
|
28 |
+
* @param string $currentDir The directory of the current CSS file.
|
29 |
+
*
|
30 |
+
* @param string $docRoot The document root of the web site in which
|
31 |
+
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
|
32 |
+
*
|
33 |
+
* @param array $symlinks (default = array()) If the CSS file is stored in
|
34 |
+
* a symlink-ed directory, provide an array of link paths to
|
35 |
+
* target paths, where the link paths are within the document root. Because
|
36 |
+
* paths need to be normalized for this to work, use "//" to substitute
|
37 |
+
* the doc root in the link paths (the array keys). E.g.:
|
38 |
+
* <code>
|
39 |
+
* array('//symlink' => '/real/target/path') // unix
|
40 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
41 |
+
* </code>
|
42 |
+
*
|
43 |
+
* @return string
|
44 |
+
*/
|
45 |
+
public static function rewrite($css, $options) {
|
46 |
+
self::$_prependPath = null;
|
47 |
+
|
48 |
+
if (!isset($options['prependRelativePath']) && !isset($options['currentDir']))
|
49 |
+
return $css;
|
50 |
+
|
51 |
+
self::$_browserCacheId = (isset($options['browserCacheId']) ?
|
52 |
+
$options['browserCacheId'] : 0);
|
53 |
+
self::$_browserCacheExtensions =
|
54 |
+
(isset($options['browserCacheExtensions']) ?
|
55 |
+
$options['browserCacheExtensions'] : array());
|
56 |
+
|
57 |
+
if (isset($options['currentDir'])) {
|
58 |
+
$document_root = (isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT']);
|
59 |
+
$symlinks = (isset($options['symlinks']) ? $options['symlinks'] : array());
|
60 |
+
$prependAbsolutePath = (isset($options['prependAbsolutePath']) ? $options['prependAbsolutePath'] : '');
|
61 |
+
$prependAbsolutePathCallback = (isset($options['prependAbsolutePathCallback']) ? $options['prependAbsolutePathCallback'] : '');
|
62 |
+
|
63 |
+
$css = self::_rewrite(
|
64 |
+
$css,
|
65 |
+
$options['currentDir'],
|
66 |
+
$prependAbsolutePath,
|
67 |
+
$prependAbsolutePathCallback,
|
68 |
+
$document_root,
|
69 |
+
$symlinks
|
70 |
+
);
|
71 |
+
} elseif (isset($options['prependRelativePath'])) {
|
72 |
+
$css = self::prepend(
|
73 |
+
$css,
|
74 |
+
$options['prependRelativePath']
|
75 |
+
);
|
76 |
+
}
|
77 |
+
|
78 |
+
return $css;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Rewrite file relative URIs as root relative in CSS files
|
83 |
+
*
|
84 |
+
* @param string $css
|
85 |
+
*
|
86 |
+
* @param string $currentDir The directory of the current CSS file.
|
87 |
+
*
|
88 |
+
* @param string $prependAbsolutePath
|
89 |
+
*
|
90 |
+
* @param string $prependAbsolutePathCallback
|
91 |
+
*
|
92 |
+
* @param string $docRoot The document root of the web site in which
|
93 |
+
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
|
94 |
+
*
|
95 |
+
* @param array $symlinks (default = array()) If the CSS file is stored in
|
96 |
+
* a symlink-ed directory, provide an array of link paths to
|
97 |
+
* target paths, where the link paths are within the document root. Because
|
98 |
+
* paths need to be normalized for this to work, use "//" to substitute
|
99 |
+
* the doc root in the link paths (the array keys). E.g.:
|
100 |
+
* <code>
|
101 |
+
* array('//symlink' => '/real/target/path') // unix
|
102 |
+
* array('//static' => 'D:\\staticStorage') // Windows
|
103 |
+
* </code>
|
104 |
+
*
|
105 |
+
* @param int $browserCacheId
|
106 |
+
*
|
107 |
+
* @param array $browserCacheExtensions
|
108 |
+
*
|
109 |
+
* @return string
|
110 |
+
*/
|
111 |
+
private static function _rewrite($css, $currentDir,
|
112 |
+
$prependAbsolutePath = null, $prependAbsolutePathCallback = null,
|
113 |
+
$docRoot = null, $symlinks = array()) {
|
114 |
+
self::$_docRoot = self::_realpath(
|
115 |
+
$docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
|
116 |
+
);
|
117 |
+
self::$_currentDir = self::_realpath($currentDir);
|
118 |
+
self::$_prependAbsolutePath = $prependAbsolutePath;
|
119 |
+
self::$_prependAbsolutePathCallback = $prependAbsolutePathCallback;
|
120 |
+
self::$_symlinks = array();
|
121 |
+
|
122 |
+
// normalize symlinks
|
123 |
+
foreach ($symlinks as $link => $target) {
|
124 |
+
$link = ($link === '//')
|
125 |
+
? self::$_docRoot
|
126 |
+
: str_replace('//', self::$_docRoot . '/', $link);
|
127 |
+
$link = strtr($link, '/', DIRECTORY_SEPARATOR);
|
128 |
+
self::$_symlinks[$link] = self::_realpath($target);
|
129 |
+
}
|
130 |
+
|
131 |
+
self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
|
132 |
+
. "currentDir : " . self::$_currentDir . "\n";
|
133 |
+
if (self::$_symlinks) {
|
134 |
+
self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
|
135 |
+
}
|
136 |
+
self::$debugText .= "\n";
|
137 |
+
|
138 |
+
$css = self::_trimUrls($css);
|
139 |
+
|
140 |
+
// rewrite
|
141 |
+
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
142 |
+
,array(self::$className, '_processUriCB'), $css);
|
143 |
+
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
144 |
+
,array(self::$className, '_processUriCB'), $css);
|
145 |
+
|
146 |
+
return $css;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* In CSS content, prepend a path to relative URIs
|
151 |
+
*
|
152 |
+
* @param string $css
|
153 |
+
*
|
154 |
+
* @param string $path The path to prepend.
|
155 |
+
*
|
156 |
+
* @return string
|
157 |
+
*/
|
158 |
+
public static function prepend($css, $path)
|
159 |
+
{
|
160 |
+
self::$_prependPath = $path;
|
161 |
+
|
162 |
+
$css = self::_trimUrls($css);
|
163 |
+
|
164 |
+
// append
|
165 |
+
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
166 |
+
,array(self::$className, '_processUriCB'), $css);
|
167 |
+
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
168 |
+
,array(self::$className, '_processUriCB'), $css);
|
169 |
+
|
170 |
+
return $css;
|
171 |
+
}
|
172 |
+
|
173 |
+
/**
|
174 |
+
* Get a root relative URI from a file relative URI
|
175 |
+
*
|
176 |
+
* <code>
|
177 |
+
* Minify_CSS_UriRewriter::rewriteRelative(
|
178 |
+
* '../img/hello.gif'
|
179 |
+
* , '/home/user/www/css' // path of CSS file
|
180 |
+
* , '/home/user/www' // doc root
|
181 |
+
* );
|
182 |
+
* // returns '/img/hello.gif'
|
183 |
+
*
|
184 |
+
* // example where static files are stored in a symlinked directory
|
185 |
+
* Minify_CSS_UriRewriter::rewriteRelative(
|
186 |
+
* 'hello.gif'
|
187 |
+
* , '/var/staticFiles/theme'
|
188 |
+
* , '/home/user/www'
|
189 |
+
* , array('/home/user/www/static' => '/var/staticFiles')
|
190 |
+
* );
|
191 |
+
* // returns '/static/theme/hello.gif'
|
192 |
+
* </code>
|
193 |
+
*
|
194 |
+
* @param string $uri file relative URI
|
195 |
+
*
|
196 |
+
* @param string $realCurrentDir realpath of the current file's directory.
|
197 |
+
*
|
198 |
+
* @param string $realDocRoot realpath of the site document root.
|
199 |
+
*
|
200 |
+
* @param array $symlinks (default = array()) If the file is stored in
|
201 |
+
* a symlink-ed directory, provide an array of link paths to
|
202 |
+
* real target paths, where the link paths "appear" to be within the document
|
203 |
+
* root. E.g.:
|
204 |
+
* <code>
|
205 |
+
* array('/home/foo/www/not/real/path' => '/real/target/path') // unix
|
206 |
+
* array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
|
207 |
+
* </code>
|
208 |
+
*
|
209 |
+
* @return string
|
210 |
+
*/
|
211 |
+
private static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
|
212 |
+
{
|
213 |
+
if ('/' === substr($uri, 0, 1)) { // root-relative
|
214 |
+
return $uri;
|
215 |
+
}
|
216 |
+
|
217 |
+
// prepend path with current dir separator (OS-independent)
|
218 |
+
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
|
219 |
+
. DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
|
220 |
+
|
221 |
+
self::$debugText .= "file-relative URI : {$uri}\n"
|
222 |
+
. "path prepended : {$path}\n";
|
223 |
+
|
224 |
+
// "unresolve" a symlink back to doc root
|
225 |
+
foreach ($symlinks as $link => $target) {
|
226 |
+
if (0 === strpos($path, $target)) {
|
227 |
+
// replace $target with $link
|
228 |
+
$path = $link . substr($path, strlen($target));
|
229 |
+
|
230 |
+
self::$debugText .= "symlink unresolved : {$path}\n";
|
231 |
+
|
232 |
+
break;
|
233 |
+
}
|
234 |
+
}
|
235 |
+
// strip doc root
|
236 |
+
$path = substr($path, strlen($realDocRoot));
|
237 |
+
|
238 |
+
self::$debugText .= "docroot stripped : {$path}\n";
|
239 |
+
|
240 |
+
// fix to root-relative URI
|
241 |
+
$uri = strtr($path, '/\\', '//');
|
242 |
+
$uri = self::removeDots($uri);
|
243 |
+
|
244 |
+
self::$debugText .= "traversals removed : {$uri}\n\n";
|
245 |
+
|
246 |
+
return $uri;
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Remove instances of "./" and "../" where possible from a root-relative URI
|
251 |
+
*
|
252 |
+
* @param string $uri
|
253 |
+
*
|
254 |
+
* @return string
|
255 |
+
*/
|
256 |
+
public static function removeDots($uri)
|
257 |
+
{
|
258 |
+
$uri = str_replace('/./', '/', $uri);
|
259 |
+
// inspired by patch from Oleg Cherniy
|
260 |
+
do {
|
261 |
+
$uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
|
262 |
+
} while ($changed);
|
263 |
+
return $uri;
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Defines which class to call as part of callbacks, change this
|
268 |
+
* if you extend Minify_CSS_UriRewriter
|
269 |
+
*
|
270 |
+
* @var string
|
271 |
+
*/
|
272 |
+
protected static $className = '\W3TCL\Minify\Minify_CSS_UriRewriter';
|
273 |
+
|
274 |
+
/**
|
275 |
+
* Get realpath with any trailing slash removed. If realpath() fails,
|
276 |
+
* just remove the trailing slash.
|
277 |
+
*
|
278 |
+
* @param string $path
|
279 |
+
*
|
280 |
+
* @return mixed path with no trailing slash
|
281 |
+
*/
|
282 |
+
protected static function _realpath($path)
|
283 |
+
{
|
284 |
+
$realPath = realpath($path);
|
285 |
+
if ($realPath !== false) {
|
286 |
+
$path = $realPath;
|
287 |
+
}
|
288 |
+
return rtrim($path, '/\\');
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Directory of this stylesheet
|
293 |
+
*
|
294 |
+
* @var string
|
295 |
+
*/
|
296 |
+
private static $_currentDir = '';
|
297 |
+
|
298 |
+
/**
|
299 |
+
* DOC_ROOT
|
300 |
+
*
|
301 |
+
* @var string
|
302 |
+
*/
|
303 |
+
private static $_docRoot = '';
|
304 |
+
|
305 |
+
/**
|
306 |
+
* directory replacements to map symlink targets back to their
|
307 |
+
* source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
|
308 |
+
*
|
309 |
+
* @var array
|
310 |
+
*/
|
311 |
+
private static $_symlinks = array();
|
312 |
+
|
313 |
+
/**
|
314 |
+
* Path to prepend
|
315 |
+
*
|
316 |
+
* @var string
|
317 |
+
*/
|
318 |
+
private static $_prependPath = null;
|
319 |
+
|
320 |
+
/**
|
321 |
+
* @var string
|
322 |
+
*/
|
323 |
+
private static $_prependAbsolutePath = null;
|
324 |
+
|
325 |
+
/**
|
326 |
+
* @var string
|
327 |
+
*/
|
328 |
+
private static $_prependAbsolutePathCallback = null;
|
329 |
+
|
330 |
+
/**
|
331 |
+
* @var int
|
332 |
+
*/
|
333 |
+
private static $_browserCacheId = 0;
|
334 |
+
|
335 |
+
/**
|
336 |
+
* @var array
|
337 |
+
*/
|
338 |
+
private static $_browserCacheExtensions = array();
|
339 |
+
|
340 |
+
|
341 |
+
/**
|
342 |
+
* @param string $css
|
343 |
+
*
|
344 |
+
* @return string
|
345 |
+
*/
|
346 |
+
private static function _trimUrls($css)
|
347 |
+
{
|
348 |
+
return preg_replace('/
|
349 |
+
url\\( # url(
|
350 |
+
\\s*
|
351 |
+
([^\\)]+?) # 1 = URI (assuming does not contain ")")
|
352 |
+
\\s*
|
353 |
+
\\) # )
|
354 |
+
/x', 'url($1)', $css);
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* @param array $m
|
359 |
+
*
|
360 |
+
* @return string
|
361 |
+
*/
|
362 |
+
private static function _processUriCB($m)
|
363 |
+
{
|
364 |
+
// $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
365 |
+
$isImport = ($m[0][0] === '@');
|
366 |
+
// determine URI and the quote character (if any)
|
367 |
+
if ($isImport) {
|
368 |
+
$quoteChar = $m[1];
|
369 |
+
$uri = $m[2];
|
370 |
+
} else {
|
371 |
+
// $m[1] is either quoted or not
|
372 |
+
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
|
373 |
+
? $m[1][0]
|
374 |
+
: '';
|
375 |
+
$uri = ($quoteChar === '')
|
376 |
+
? $m[1]
|
377 |
+
: substr($m[1], 1, strlen($m[1]) - 2);
|
378 |
+
}
|
379 |
+
// analyze URI
|
380 |
+
if ( !empty($uri)
|
381 |
+
&& '/' !== substr($uri, 0, 1) // Root-relative (/).
|
382 |
+
&& false === strpos( $uri, '//' ) // Protocol/non-data (//).
|
383 |
+
&& 'data:' !== substr( $uri, 0, 5 ) // Data protocol.
|
384 |
+
&& '%' !== substr( $uri, 0, 1 ) // URL-encoded entity.
|
385 |
+
&& '#' !== substr( $uri, 0, 1 ) // URL fragment.
|
386 |
+
) {
|
387 |
+
// URI is file-relative: rewrite depending on options
|
388 |
+
if (self::$_prependPath === null) {
|
389 |
+
$uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
|
390 |
+
if (self::$_prependAbsolutePath) {
|
391 |
+
$prependAbsolutePath = self::$_prependAbsolutePath;
|
392 |
+
} elseif (self::$_prependAbsolutePathCallback) {
|
393 |
+
$prependAbsolutePath = call_user_func(self::$_prependAbsolutePathCallback, $uri);
|
394 |
+
} else {
|
395 |
+
$prependAbsolutePath = '';
|
396 |
+
}
|
397 |
+
|
398 |
+
if ($prependAbsolutePath) {
|
399 |
+
$uri = rtrim($prependAbsolutePath, '/') . $uri;
|
400 |
+
}
|
401 |
+
} else {
|
402 |
+
if (!\W3TC\Util_Environment::is_url(self::$_prependPath)) {
|
403 |
$uri = self::$_prependPath . $uri;
|
404 |
|
405 |
+
if (substr($uri, 0, 1) === '/') {
|
406 |
+
$root = '';
|
407 |
+
$rootRelative = $uri;
|
408 |
+
$uri = $root . self::removeDots($rootRelative);
|
409 |
+
} elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
|
410 |
+
$root = $m[1];
|
411 |
+
$rootRelative = substr($uri, strlen($root));
|
412 |
+
$uri = $root . self::removeDots($rootRelative);
|
413 |
+
}
|
414 |
+
} else {
|
415 |
+
$parse_url = @parse_url(self::$_prependPath);
|
416 |
+
|
417 |
+
if ($parse_url && isset($parse_url['host'])) {
|
418 |
+
$scheme = array_key_exists('scheme', $parse_url) ? $parse_url['scheme'] : '';
|
419 |
+
$host = $parse_url['host'];
|
420 |
+
$port = (isset($parse_url['port']) && $parse_url['port'] != 80 ? ':' . (int) $parse_url['port'] : '');
|
421 |
+
$path = (!empty($parse_url['path']) ? $parse_url['path'] : '/');
|
422 |
+
$dir_css = preg_replace('~[^/]+$~', '', $path);
|
423 |
+
$dir_obj = preg_replace('~[^/]+$~', '', $uri);
|
424 |
+
$dir = (ltrim((strpos($dir_obj, '/') === 0 ? \W3TC\Util_Environment::realpath($dir_obj) : \W3TC\Util_Environment::realpath($dir_css . $dir_obj)), '/'));
|
425 |
+
$file = basename($uri);
|
426 |
+
|
427 |
+
$scheme_dot = ( empty( $scheme ) ? '' : $scheme . ':' );
|
428 |
+
$uri = sprintf('%s//%s%s/%s/%s', $scheme_dot, $host,
|
429 |
+
$port, $dir, $file);
|
430 |
+
}
|
431 |
+
}
|
432 |
+
}
|
433 |
+
|
434 |
+
if (self::$_browserCacheId && count(self::$_browserCacheExtensions)) {
|
435 |
+
$matches = null;
|
436 |
+
|
437 |
+
if (preg_match('~\.([a-z-_]+)(\?.*)?$~', $uri, $matches)) {
|
438 |
+
$extension = $matches[1];
|
439 |
+
|
440 |
+
if ($extension && in_array($extension, self::$_browserCacheExtensions)) {
|
441 |
+
$uri = \W3TC\Util_Environment::remove_query($uri);
|
442 |
+
$uri .= ( strpos( $uri, '?' ) !== false ? '&' : '?' ) . self::$_browserCacheId;
|
443 |
+
}
|
444 |
+
}
|
445 |
+
}
|
446 |
+
}
|
447 |
+
return $isImport
|
448 |
+
? "@import {$quoteChar}{$uri}{$quoteChar}"
|
449 |
+
: "url({$quoteChar}{$uri}{$quoteChar})";
|
450 |
+
}
|
451 |
}
|
lib/Minify/Minify/CSSTidy.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
class Minify_CSSTidy {
|
4 |
public static function minify($css, $options = array()) {
|
@@ -26,7 +27,7 @@ class Minify_CSSTidy {
|
|
26 |
|
27 |
require_once 'class.csstidy.php';
|
28 |
|
29 |
-
$csstidy = new csstidy();
|
30 |
|
31 |
foreach ($options as $option => $value) {
|
32 |
$csstidy->set_cfg($option, $value);
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
class Minify_CSSTidy {
|
5 |
public static function minify($css, $options = array()) {
|
27 |
|
28 |
require_once 'class.csstidy.php';
|
29 |
|
30 |
+
$csstidy = new \csstidy();
|
31 |
|
32 |
foreach ($options as $option => $value) {
|
33 |
$csstidy->set_cfg($option, $value);
|
lib/Minify/Minify/Cache/File.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
/**
|
4 |
* Class Minify_Cache_File
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
/**
|
5 |
* Class Minify_Cache_File
|
lib/Minify/Minify/Cache/W3TCDerived.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_Cache_APC
|
4 |
* @package Minify
|
@@ -6,11 +7,11 @@
|
|
6 |
|
7 |
/**
|
8 |
* APC-based cache class for Minify
|
9 |
-
*
|
10 |
* <code>
|
11 |
* Minify::setCache(new Minify_Cache_APC());
|
12 |
* </code>
|
13 |
-
*
|
14 |
* @package Minify
|
15 |
* @author Chris Edwards
|
16 |
**/
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_Cache_APC
|
5 |
* @package Minify
|
7 |
|
8 |
/**
|
9 |
* APC-based cache class for Minify
|
10 |
+
*
|
11 |
* <code>
|
12 |
* Minify::setCache(new Minify_Cache_APC());
|
13 |
* </code>
|
14 |
+
*
|
15 |
* @package Minify
|
16 |
* @author Chris Edwards
|
17 |
**/
|
lib/Minify/Minify/Cache/ZendPlatform.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_Cache_ZendPlatform
|
4 |
* @package Minify
|
@@ -9,7 +10,7 @@
|
|
9 |
* ZendPlatform-based cache class for Minify
|
10 |
*
|
11 |
* Based on Minify_Cache_APC, uses output_cache_get/put (currently deprecated)
|
12 |
-
*
|
13 |
* <code>
|
14 |
* Minify::setCache(new Minify_Cache_ZendPlatform());
|
15 |
* </code>
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_Cache_ZendPlatform
|
5 |
* @package Minify
|
10 |
* ZendPlatform-based cache class for Minify
|
11 |
*
|
12 |
* Based on Minify_Cache_APC, uses output_cache_get/put (currently deprecated)
|
13 |
+
*
|
14 |
* <code>
|
15 |
* Minify::setCache(new Minify_Cache_ZendPlatform());
|
16 |
* </code>
|
lib/Minify/Minify/ClosureCompiler.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_ClosureCompiler
|
4 |
* @package Minify
|
@@ -33,111 +34,111 @@
|
|
33 |
*/
|
34 |
class Minify_ClosureCompiler {
|
35 |
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
|
94 |
-
|
95 |
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
}
|
142 |
|
143 |
/* vim:ts=4:sw=4:et */
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_ClosureCompiler
|
5 |
* @package Minify
|
34 |
*/
|
35 |
class Minify_ClosureCompiler {
|
36 |
|
37 |
+
/**
|
38 |
+
* Filepath of the Closure Compiler jar file. This must be set before
|
39 |
+
* calling minifyJs().
|
40 |
+
*
|
41 |
+
* @var string
|
42 |
+
*/
|
43 |
+
public static $jarFile = null;
|
44 |
|
45 |
+
/**
|
46 |
+
* Writable temp directory. This must be set before calling minifyJs().
|
47 |
+
*
|
48 |
+
* @var string
|
49 |
+
*/
|
50 |
+
public static $tempDir = null;
|
51 |
|
52 |
+
/**
|
53 |
+
* Filepath of "java" executable (may be needed if not in shell's PATH)
|
54 |
+
*
|
55 |
+
* @var string
|
56 |
+
*/
|
57 |
+
public static $javaExecutable = 'java';
|
58 |
|
59 |
+
/**
|
60 |
+
* Minify a Javascript string
|
61 |
+
*
|
62 |
+
* @param string $js
|
63 |
+
*
|
64 |
+
* @param array $options (verbose is ignored)
|
65 |
+
*
|
66 |
+
* @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
|
67 |
+
*
|
68 |
+
* @return string
|
69 |
+
*/
|
70 |
+
public static function minify($js, $options = array())
|
71 |
+
{
|
72 |
+
self::_prepare();
|
73 |
+
if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) {
|
74 |
+
throw new \Exception('Minify_ClosureCompiler : could not create temp file.');
|
75 |
+
}
|
76 |
+
file_put_contents($tmpFile, $js);
|
77 |
+
exec(self::_getCmd($options, $tmpFile), $output, $result_code);
|
78 |
+
unlink($tmpFile);
|
79 |
+
if ($result_code != 0) {
|
80 |
+
throw new \Exception('Minify_ClosureCompiler : Closure Compiler execution failed.');
|
81 |
+
}
|
82 |
+
return implode("\n", $output);
|
83 |
+
}
|
84 |
|
85 |
+
private static function _getCmd($userOptions, $tmpFile)
|
86 |
+
{
|
87 |
+
$o = array_merge(
|
88 |
+
array(
|
89 |
+
'charset' => 'utf-8',
|
90 |
+
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
|
91 |
+
),
|
92 |
+
$userOptions
|
93 |
+
);
|
94 |
|
95 |
+
$javaExecutable = self::$javaExecutable;
|
96 |
|
97 |
+
if ( false !== strpos(trim($javaExecutable), ' ') ) {
|
98 |
+
$javaExecutable = '"'.$javaExecutable.'"';
|
99 |
+
}
|
100 |
|
101 |
+
$cmd = $javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
|
102 |
+
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
|
103 |
+
? " --charset {$o['charset']}"
|
104 |
+
: '');
|
105 |
|
106 |
+
foreach (array('compilation_level') as $opt) {
|
107 |
+
if ($o[$opt]) {
|
108 |
+
$cmd .= " --{$opt} ". escapeshellarg($o[$opt]);
|
109 |
+
}
|
110 |
+
}
|
111 |
+
return $cmd . ' ' . escapeshellarg($tmpFile);
|
112 |
+
}
|
113 |
|
114 |
+
private static function _prepare()
|
115 |
+
{
|
116 |
+
if (! is_file(self::$jarFile)) {
|
117 |
+
throw new \Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
|
118 |
+
}
|
119 |
+
if (! is_readable(self::$jarFile)) {
|
120 |
+
throw new \Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
|
121 |
+
}
|
122 |
+
if (! is_dir(self::$tempDir)) {
|
123 |
+
throw new \Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
124 |
+
}
|
125 |
+
if (! is_writable(self::$tempDir)) {
|
126 |
+
throw new \Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
|
127 |
+
}
|
128 |
+
}
|
129 |
|
130 |
+
public static function test(&$error) {
|
131 |
+
try {
|
132 |
+
self::minify('alert("ok");');
|
133 |
+
$error = 'OK';
|
134 |
|
135 |
+
return true;
|
136 |
+
} catch (\Exception $exception) {
|
137 |
+
$error = $exception->getMessage();
|
138 |
|
139 |
+
return false;
|
140 |
+
}
|
141 |
+
}
|
142 |
}
|
143 |
|
144 |
/* vim:ts=4:sw=4:et */
|
lib/Minify/Minify/CombineOnly.php
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
<?php
|
2 |
-
|
3 |
/**
|
4 |
* Combine only minifier
|
5 |
*/
|
6 |
class Minify_CombineOnly {
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
|
16 |
-
|
17 |
-
|
18 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Combine only minifier
|
5 |
*/
|
6 |
class Minify_CombineOnly {
|
7 |
+
/**
|
8 |
+
* Minifies content
|
9 |
+
* @param string $content
|
10 |
+
* @param array $options
|
11 |
+
* @return string
|
12 |
+
*/
|
13 |
+
public static function minify($content, $options = array()) {
|
14 |
+
$content = Minify_CSS_UriRewriter::rewrite($content, $options);
|
15 |
|
16 |
+
return $content;
|
17 |
+
}
|
18 |
}
|
lib/Minify/Minify/CommentPreserver.php
CHANGED
@@ -1,41 +1,42 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_CommentPreserver
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Process a string in pieces preserving C-style comments that begin with "/*!"
|
9 |
-
*
|
10 |
* @package Minify
|
11 |
* @author Stephen Clay <steve@mrclay.org>
|
12 |
*/
|
13 |
class Minify_CommentPreserver {
|
14 |
-
|
15 |
/**
|
16 |
* String to be prepended to each preserved comment
|
17 |
*
|
18 |
* @var string
|
19 |
*/
|
20 |
public static $prepend = "\n";
|
21 |
-
|
22 |
/**
|
23 |
* String to be appended to each preserved comment
|
24 |
*
|
25 |
* @var string
|
26 |
*/
|
27 |
public static $append = "\n";
|
28 |
-
|
29 |
/**
|
30 |
* Process a string outside of C-style comments that begin with "/*!"
|
31 |
*
|
32 |
-
* On each non-empty string outside these comments, the given processor
|
33 |
-
* function will be called. The comments will be surrounded by
|
34 |
* Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append.
|
35 |
-
*
|
36 |
* @param string $content
|
37 |
* @param callback $processor function
|
38 |
-
* @param array $args array of extra arguments to pass to the processor
|
39 |
* function (default = array())
|
40 |
* @return string
|
41 |
*/
|
@@ -47,7 +48,7 @@ class Minify_CommentPreserver {
|
|
47 |
if ('' !== $beforeComment) {
|
48 |
$callArgs = $args;
|
49 |
array_unshift($callArgs, $beforeComment);
|
50 |
-
$ret .= call_user_func_array($processor, $callArgs);
|
51 |
}
|
52 |
if (false === $comment) {
|
53 |
break;
|
@@ -57,15 +58,15 @@ class Minify_CommentPreserver {
|
|
57 |
}
|
58 |
return $ret;
|
59 |
}
|
60 |
-
|
61 |
/**
|
62 |
* Extract comments that YUI Compressor preserves.
|
63 |
-
*
|
64 |
* @param string $in input
|
65 |
-
*
|
66 |
* @return array 3 elements are returned. If a YUI comment is found, the
|
67 |
* 2nd element is the comment and the 1st and 3rd are the surrounding
|
68 |
-
* strings. If no comment is found, the entire string is returned as the
|
69 |
* 1st element and the other two are false.
|
70 |
*/
|
71 |
private static function _nextComment($in)
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_CommentPreserver
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Process a string in pieces preserving C-style comments that begin with "/*!"
|
10 |
+
*
|
11 |
* @package Minify
|
12 |
* @author Stephen Clay <steve@mrclay.org>
|
13 |
*/
|
14 |
class Minify_CommentPreserver {
|
15 |
+
|
16 |
/**
|
17 |
* String to be prepended to each preserved comment
|
18 |
*
|
19 |
* @var string
|
20 |
*/
|
21 |
public static $prepend = "\n";
|
22 |
+
|
23 |
/**
|
24 |
* String to be appended to each preserved comment
|
25 |
*
|
26 |
* @var string
|
27 |
*/
|
28 |
public static $append = "\n";
|
29 |
+
|
30 |
/**
|
31 |
* Process a string outside of C-style comments that begin with "/*!"
|
32 |
*
|
33 |
+
* On each non-empty string outside these comments, the given processor
|
34 |
+
* function will be called. The comments will be surrounded by
|
35 |
* Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append.
|
36 |
+
*
|
37 |
* @param string $content
|
38 |
* @param callback $processor function
|
39 |
+
* @param array $args array of extra arguments to pass to the processor
|
40 |
* function (default = array())
|
41 |
* @return string
|
42 |
*/
|
48 |
if ('' !== $beforeComment) {
|
49 |
$callArgs = $args;
|
50 |
array_unshift($callArgs, $beforeComment);
|
51 |
+
$ret .= call_user_func_array($processor, $callArgs);
|
52 |
}
|
53 |
if (false === $comment) {
|
54 |
break;
|
58 |
}
|
59 |
return $ret;
|
60 |
}
|
61 |
+
|
62 |
/**
|
63 |
* Extract comments that YUI Compressor preserves.
|
64 |
+
*
|
65 |
* @param string $in input
|
66 |
+
*
|
67 |
* @return array 3 elements are returned. If a YUI comment is found, the
|
68 |
* 2nd element is the comment and the 1st and 3rd are the surrounding
|
69 |
+
* strings. If no comment is found, the entire string is returned as the
|
70 |
* 1st element and the other two are false.
|
71 |
*/
|
72 |
private static function _nextComment($in)
|
lib/Minify/Minify/Controller/Base.php
CHANGED
@@ -1,223 +1,224 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Controller_Base
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Base class for Minify controller
|
9 |
-
*
|
10 |
* The controller class validates a request and uses it to create sources
|
11 |
* for minification and set options like contentType. It's also responsible
|
12 |
* for loading minifier code upon request.
|
13 |
-
*
|
14 |
* @package Minify
|
15 |
* @author Stephen Clay <steve@mrclay.org>
|
16 |
*/
|
17 |
abstract class Minify_Controller_Base {
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Setup controller sources and set an needed options for Minify::source
|
21 |
-
*
|
22 |
-
* You must override this method in your subclass controller to set
|
23 |
-
* $this->sources. If the request is NOT valid, make sure $this->sources
|
24 |
-
* is left an empty array. Then strip any controller-specific options from
|
25 |
-
* $options and return it. To serve files, $this->sources must be an array of
|
26 |
-
* Minify_Source objects.
|
27 |
-
*
|
28 |
-
* @param array $options controller and Minify options
|
29 |
-
*
|
30 |
-
* @return array $options Minify::serve options
|
31 |
-
*/
|
32 |
-
abstract public function setupSources($options);
|
33 |
-
|
34 |
-
/**
|
35 |
-
* Get default Minify options for this controller.
|
36 |
-
*
|
37 |
-
* Override in subclass to change defaults
|
38 |
-
*
|
39 |
-
* @return array options for Minify
|
40 |
-
*/
|
41 |
-
public function getDefaultMinifyOptions() {
|
42 |
-
return array(
|
43 |
-
'isPublic' => true
|
44 |
-
,'encodeOutput' => function_exists('gzdeflate')
|
45 |
-
,'encodeMethod' => null // determine later
|
46 |
-
,'encodeLevel' => 9
|
47 |
-
,'minifierOptions' => array() // no minifier options
|
48 |
-
,'contentTypeCharset' => 'utf-8'
|
49 |
-
,'maxAge' => 1800 // 30 minutes
|
50 |
-
,'rewriteCssUris' => true
|
51 |
-
,'bubbleCssImports' => false
|
52 |
-
,'processCssImports' => false
|
53 |
-
,'quiet' => false // serve() will send headers and output
|
54 |
-
,'debug' => false
|
55 |
-
|
56 |
-
// if you override these, the response codes MUST be directly after
|
57 |
-
// the first space.
|
58 |
-
,'badRequestHeader' => 'HTTP/1.0 400 Bad Request'
|
59 |
-
,'errorHeader' => 'HTTP/1.0 500 Internal Server Error'
|
60 |
-
|
61 |
-
// callback function to see/modify content of all sources
|
62 |
-
,'postprocessor' => null
|
63 |
-
// file to require to load preprocessor
|
64 |
-
,'postprocessorRequire' => null
|
65 |
-
);
|
66 |
-
}
|
67 |
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
/**
|
83 |
-
* Is a user-given file within an allowable directory, existing,
|
84 |
-
* and having an extension js/css/html/txt ?
|
85 |
-
*
|
86 |
-
* This is a convenience function for controllers that have to accept
|
87 |
-
* user-given paths
|
88 |
-
*
|
89 |
-
* @param string $file full file path (already processed by realpath())
|
90 |
-
*
|
91 |
-
* @param array $safeDirs directories where files are safe to serve. Files can also
|
92 |
-
* be in subdirectories of these directories.
|
93 |
-
*
|
94 |
-
* @return bool file is safe
|
95 |
-
*
|
96 |
-
* @deprecated use checkAllowDirs, checkNotHidden instead
|
97 |
-
*/
|
98 |
-
public static function _fileIsSafe($file, $safeDirs)
|
99 |
-
{
|
100 |
-
$pathOk = false;
|
101 |
-
foreach ((array)$safeDirs as $safeDir) {
|
102 |
-
if (strpos($file, $safeDir) === 0) {
|
103 |
-
$pathOk = true;
|
104 |
-
break;
|
105 |
-
}
|
106 |
-
}
|
107 |
-
$base = basename($file);
|
108 |
-
if (! $pathOk || ! is_file($file) || $base[0] === '.') {
|
109 |
-
return false;
|
110 |
-
}
|
111 |
-
list($revExt) = explode('.', strrev($base));
|
112 |
-
return in_array(strrev($revExt), array('js', 'css', 'html', 'txt'));
|
113 |
-
}
|
114 |
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
|
|
|
|
|
|
133 |
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
public static function checkNotHidden($file)
|
139 |
-
{
|
140 |
-
$b = basename($file);
|
141 |
-
if (0 === strpos($b, '.')) {
|
142 |
-
throw new Exception("Filename '$b' starts with period (may be hidden)");
|
143 |
-
}
|
144 |
-
}
|
145 |
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
*/
|
153 |
-
public $sources = array();
|
154 |
-
|
155 |
-
/**
|
156 |
-
* Short name to place inside cache id
|
157 |
-
*
|
158 |
-
* The setupSources() method may choose to set this, making it easier to
|
159 |
-
* recognize a particular set of sources/settings in the cache folder. It
|
160 |
-
* will be filtered and truncated to make the final cache id <= 250 bytes.
|
161 |
-
*
|
162 |
-
* @var string
|
163 |
-
*/
|
164 |
-
public $selectionId = '';
|
165 |
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
$options['minifiers'] = array();
|
180 |
-
}
|
181 |
-
$ret['minifiers'] = array_merge(
|
182 |
-
$this->getDefaultMinifers(), $options['minifiers']
|
183 |
-
);
|
184 |
-
return $ret;
|
185 |
-
}
|
186 |
-
|
187 |
-
/**
|
188 |
-
* Analyze sources (if there are any) and set $options 'contentType'
|
189 |
-
* and 'lastModifiedTime' if they already aren't.
|
190 |
-
*
|
191 |
-
* @param array $options options for Minify
|
192 |
-
*
|
193 |
-
* @return array options for Minify
|
194 |
-
*/
|
195 |
-
public final function analyzeSources($options = array())
|
196 |
-
{
|
197 |
-
if ($this->sources) {
|
198 |
-
if (! isset($options['contentType'])) {
|
199 |
-
$options['contentType'] = Minify_Source::getContentType($this->sources);
|
200 |
-
}
|
201 |
-
// last modified is needed for caching, even if setExpires is set
|
202 |
-
if (! isset($options['lastModifiedTime'])) {
|
203 |
-
$max = 0;
|
204 |
-
foreach ($this->sources as $source) {
|
205 |
-
$max = max($source->lastModified, $max);
|
206 |
-
}
|
207 |
-
$options['lastModifiedTime'] = $max;
|
208 |
-
}
|
209 |
-
}
|
210 |
-
return $options;
|
211 |
-
}
|
212 |
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
223 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Controller_Base
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Base class for Minify controller
|
10 |
+
*
|
11 |
* The controller class validates a request and uses it to create sources
|
12 |
* for minification and set options like contentType. It's also responsible
|
13 |
* for loading minifier code upon request.
|
14 |
+
*
|
15 |
* @package Minify
|
16 |
* @author Stephen Clay <steve@mrclay.org>
|
17 |
*/
|
18 |
abstract class Minify_Controller_Base {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
/**
|
21 |
+
* Setup controller sources and set an needed options for Minify::source
|
22 |
+
*
|
23 |
+
* You must override this method in your subclass controller to set
|
24 |
+
* $this->sources. If the request is NOT valid, make sure $this->sources
|
25 |
+
* is left an empty array. Then strip any controller-specific options from
|
26 |
+
* $options and return it. To serve files, $this->sources must be an array of
|
27 |
+
* Minify_Source objects.
|
28 |
+
*
|
29 |
+
* @param array $options controller and Minify options
|
30 |
+
*
|
31 |
+
* @return array $options Minify::serve options
|
32 |
+
*/
|
33 |
+
abstract public function setupSources($options);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
+
/**
|
36 |
+
* Get default Minify options for this controller.
|
37 |
+
*
|
38 |
+
* Override in subclass to change defaults
|
39 |
+
*
|
40 |
+
* @return array options for Minify
|
41 |
+
*/
|
42 |
+
public function getDefaultMinifyOptions() {
|
43 |
+
return array(
|
44 |
+
'isPublic' => true
|
45 |
+
,'encodeOutput' => function_exists('gzdeflate')
|
46 |
+
,'encodeMethod' => null // determine later
|
47 |
+
,'encodeLevel' => 9
|
48 |
+
,'minifierOptions' => array() // no minifier options
|
49 |
+
,'contentTypeCharset' => 'utf-8'
|
50 |
+
,'maxAge' => 1800 // 30 minutes
|
51 |
+
,'rewriteCssUris' => true
|
52 |
+
,'bubbleCssImports' => false
|
53 |
+
,'processCssImports' => false
|
54 |
+
,'quiet' => false // serve() will send headers and output
|
55 |
+
,'debug' => false
|
56 |
|
57 |
+
// if you override these, the response codes MUST be directly after
|
58 |
+
// the first space.
|
59 |
+
,'badRequestHeader' => 'HTTP/1.0 400 Bad Request'
|
60 |
+
,'errorHeader' => 'HTTP/1.0 500 Internal Server Error'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
+
// callback function to see/modify content of all sources
|
63 |
+
,'postprocessor' => null
|
64 |
+
// file to require to load preprocessor
|
65 |
+
,'postprocessorRequire' => null
|
66 |
+
);
|
67 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
+
/**
|
70 |
+
* Get default minifiers for this controller.
|
71 |
+
*
|
72 |
+
* Override in subclass to change defaults
|
73 |
+
*
|
74 |
+
* @return array minifier callbacks for common types
|
75 |
+
*/
|
76 |
+
public function getDefaultMinifers() {
|
77 |
+
$ret[Minify::TYPE_JS] = array('\W3TCL\Minify\JSMin', 'minify');
|
78 |
+
$ret[Minify::TYPE_CSS] = array('\W3TCL\Minify\Minify_CSS', 'minify');
|
79 |
+
$ret[Minify::TYPE_HTML] = array('\W3TCL\Minify\Minify_HTML', 'minify');
|
80 |
+
return $ret;
|
81 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
|
83 |
+
/**
|
84 |
+
* Is a user-given file within an allowable directory, existing,
|
85 |
+
* and having an extension js/css/html/txt ?
|
86 |
+
*
|
87 |
+
* This is a convenience function for controllers that have to accept
|
88 |
+
* user-given paths
|
89 |
+
*
|
90 |
+
* @param string $file full file path (already processed by realpath())
|
91 |
+
*
|
92 |
+
* @param array $safeDirs directories where files are safe to serve. Files can also
|
93 |
+
* be in subdirectories of these directories.
|
94 |
+
*
|
95 |
+
* @return bool file is safe
|
96 |
+
*
|
97 |
+
* @deprecated use checkAllowDirs, checkNotHidden instead
|
98 |
+
*/
|
99 |
+
public static function _fileIsSafe($file, $safeDirs)
|
100 |
+
{
|
101 |
+
$pathOk = false;
|
102 |
+
foreach ((array)$safeDirs as $safeDir) {
|
103 |
+
if (strpos($file, $safeDir) === 0) {
|
104 |
+
$pathOk = true;
|
105 |
+
break;
|
106 |
+
}
|
107 |
+
}
|
108 |
+
$base = basename($file);
|
109 |
+
if (! $pathOk || ! is_file($file) || $base[0] === '.') {
|
110 |
+
return false;
|
111 |
+
}
|
112 |
+
list($revExt) = explode('.', strrev($base));
|
113 |
+
return in_array(strrev($revExt), array('js', 'css', 'html', 'txt'));
|
114 |
+
}
|
115 |
+
|
116 |
+
/**
|
117 |
+
* @param string $file
|
118 |
+
* @param array $allowDirs
|
119 |
+
* @param string $uri
|
120 |
+
* @return bool
|
121 |
+
* @throws Exception
|
122 |
+
*/
|
123 |
+
public static function checkAllowDirs($file, $allowDirs, $uri)
|
124 |
+
{
|
125 |
+
foreach ((array)$allowDirs as $allowDir) {
|
126 |
+
if (strpos($file, $allowDir) === 0) {
|
127 |
+
return true;
|
128 |
+
}
|
129 |
+
}
|
130 |
+
throw new \Exception("File '$file' is outside \$allowDirs. If the path is"
|
131 |
+
. " resolved via an alias/symlink, look into the \$min_symlinks option."
|
132 |
+
. " E.g. \$min_symlinks['/" . dirname($uri) . "'] = '" . dirname($file) . "';");
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* @param string $file
|
137 |
+
* @throws Exception
|
138 |
+
*/
|
139 |
+
public static function checkNotHidden($file)
|
140 |
+
{
|
141 |
+
$b = basename($file);
|
142 |
+
if (0 === strpos($b, '.')) {
|
143 |
+
throw new \Exception("Filename '$b' starts with period (may be hidden)");
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* instances of Minify_Source, which provide content and any individual minification needs.
|
149 |
+
*
|
150 |
+
* @var array
|
151 |
+
*
|
152 |
+
* @see Minify_Source
|
153 |
+
*/
|
154 |
+
public $sources = array();
|
155 |
+
|
156 |
+
/**
|
157 |
+
* Short name to place inside cache id
|
158 |
+
*
|
159 |
+
* The setupSources() method may choose to set this, making it easier to
|
160 |
+
* recognize a particular set of sources/settings in the cache folder. It
|
161 |
+
* will be filtered and truncated to make the final cache id <= 250 bytes.
|
162 |
+
*
|
163 |
+
* @var string
|
164 |
+
*/
|
165 |
+
public $selectionId = '';
|
166 |
+
|
167 |
+
/**
|
168 |
+
* Mix in default controller options with user-given options
|
169 |
+
*
|
170 |
+
* @param array $options user options
|
171 |
+
*
|
172 |
+
* @return array mixed options
|
173 |
+
*/
|
174 |
+
public final function mixInDefaultOptions($options)
|
175 |
+
{
|
176 |
+
$ret = array_merge(
|
177 |
+
$this->getDefaultMinifyOptions(), $options
|
178 |
+
);
|
179 |
+
if (! isset($options['minifiers'])) {
|
180 |
+
$options['minifiers'] = array();
|
181 |
+
}
|
182 |
+
$ret['minifiers'] = array_merge(
|
183 |
+
$this->getDefaultMinifers(), $options['minifiers']
|
184 |
+
);
|
185 |
+
return $ret;
|
186 |
+
}
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Analyze sources (if there are any) and set $options 'contentType'
|
190 |
+
* and 'lastModifiedTime' if they already aren't.
|
191 |
+
*
|
192 |
+
* @param array $options options for Minify
|
193 |
+
*
|
194 |
+
* @return array options for Minify
|
195 |
+
*/
|
196 |
+
public final function analyzeSources($options = array())
|
197 |
+
{
|
198 |
+
if ($this->sources) {
|
199 |
+
if (! isset($options['contentType'])) {
|
200 |
+
$options['contentType'] = Minify_Source::getContentType($this->sources);
|
201 |
+
}
|
202 |
+
// last modified is needed for caching, even if setExpires is set
|
203 |
+
if (! isset($options['lastModifiedTime'])) {
|
204 |
+
$max = 0;
|
205 |
+
foreach ($this->sources as $source) {
|
206 |
+
$max = max($source->lastModified, $max);
|
207 |
+
}
|
208 |
+
$options['lastModifiedTime'] = $max;
|
209 |
+
}
|
210 |
+
}
|
211 |
+
return $options;
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Send message to the Minify logger
|
216 |
+
*
|
217 |
+
* @param string $msg
|
218 |
+
*
|
219 |
+
* @return null
|
220 |
+
*/
|
221 |
+
public function log($msg) {
|
222 |
+
Minify_Logger::log($msg);
|
223 |
+
}
|
224 |
}
|
lib/Minify/Minify/Controller/Files.php
CHANGED
@@ -1,12 +1,13 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Controller_Files
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Controller class for minifying a set of files
|
9 |
-
*
|
10 |
* E.g. the following would serve the minified Javascript for a site
|
11 |
* <code>
|
12 |
* Minify::serve('Files', array(
|
@@ -17,7 +18,7 @@
|
|
17 |
* )
|
18 |
* ));
|
19 |
* </code>
|
20 |
-
*
|
21 |
* As a shortcut, the controller will replace "//" at the beginning
|
22 |
* of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
23 |
*
|
@@ -25,20 +26,20 @@
|
|
25 |
* @author Stephen Clay <steve@mrclay.org>
|
26 |
*/
|
27 |
class Minify_Controller_Files extends Minify_Controller_Base {
|
28 |
-
|
29 |
/**
|
30 |
* Set up file sources
|
31 |
-
*
|
32 |
* @param array $options controller and Minify options
|
33 |
* @return array Minify options
|
34 |
-
*
|
35 |
* Controller options:
|
36 |
-
*
|
37 |
* 'files': (required) array of complete file paths, or a single path
|
38 |
*/
|
39 |
public function setupSources($options) {
|
40 |
// strip controller options
|
41 |
-
|
42 |
$files = $options['files'];
|
43 |
// if $files is a single object, casting will break it
|
44 |
if (is_object($files)) {
|
@@ -47,7 +48,7 @@ class Minify_Controller_Files extends Minify_Controller_Base {
|
|
47 |
$files = (array)$files;
|
48 |
}
|
49 |
unset($options['files']);
|
50 |
-
|
51 |
$sources = array();
|
52 |
foreach ($files as $file) {
|
53 |
if ($file instanceof Minify_Source) {
|
@@ -65,7 +66,7 @@ class Minify_Controller_Files extends Minify_Controller_Base {
|
|
65 |
if (is_file($realPath)) {
|
66 |
$sources[] = new Minify_Source(array(
|
67 |
'filepath' => $realPath
|
68 |
-
));
|
69 |
} else {
|
70 |
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
71 |
return $options;
|
@@ -77,4 +78,3 @@ class Minify_Controller_Files extends Minify_Controller_Base {
|
|
77 |
return $options;
|
78 |
}
|
79 |
}
|
80 |
-
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Controller_Files
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Controller class for minifying a set of files
|
10 |
+
*
|
11 |
* E.g. the following would serve the minified Javascript for a site
|
12 |
* <code>
|
13 |
* Minify::serve('Files', array(
|
18 |
* )
|
19 |
* ));
|
20 |
* </code>
|
21 |
+
*
|
22 |
* As a shortcut, the controller will replace "//" at the beginning
|
23 |
* of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
24 |
*
|
26 |
* @author Stephen Clay <steve@mrclay.org>
|
27 |
*/
|
28 |
class Minify_Controller_Files extends Minify_Controller_Base {
|
29 |
+
|
30 |
/**
|
31 |
* Set up file sources
|
32 |
+
*
|
33 |
* @param array $options controller and Minify options
|
34 |
* @return array Minify options
|
35 |
+
*
|
36 |
* Controller options:
|
37 |
+
*
|
38 |
* 'files': (required) array of complete file paths, or a single path
|
39 |
*/
|
40 |
public function setupSources($options) {
|
41 |
// strip controller options
|
42 |
+
|
43 |
$files = $options['files'];
|
44 |
// if $files is a single object, casting will break it
|
45 |
if (is_object($files)) {
|
48 |
$files = (array)$files;
|
49 |
}
|
50 |
unset($options['files']);
|
51 |
+
|
52 |
$sources = array();
|
53 |
foreach ($files as $file) {
|
54 |
if ($file instanceof Minify_Source) {
|
66 |
if (is_file($realPath)) {
|
67 |
$sources[] = new Minify_Source(array(
|
68 |
'filepath' => $realPath
|
69 |
+
));
|
70 |
} else {
|
71 |
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
72 |
return $options;
|
78 |
return $options;
|
79 |
}
|
80 |
}
|
|
lib/Minify/Minify/Controller/Groups.php
CHANGED
@@ -1,36 +1,37 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Controller_Groups
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Controller class for serving predetermined groups of minimized sets, selected
|
9 |
* by PATH_INFO
|
10 |
-
*
|
11 |
* <code>
|
12 |
-
* Minify::serve('Groups', array(
|
13 |
* 'groups' => array(
|
14 |
* 'css' => array('//css/type.css', '//css/layout.css')
|
15 |
* ,'js' => array('//js/jquery.js', '//js/site.js')
|
16 |
* )
|
17 |
* ));
|
18 |
* </code>
|
19 |
-
*
|
20 |
* If the above code were placed in /serve.php, it would enable the URLs
|
21 |
* /serve.php/js and /serve.php/css
|
22 |
-
*
|
23 |
* As a shortcut, the controller will replace "//" at the beginning
|
24 |
* of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
25 |
-
*
|
26 |
* @package Minify
|
27 |
* @author Stephen Clay <steve@mrclay.org>
|
28 |
*/
|
29 |
class Minify_Controller_Groups extends Minify_Controller_Base {
|
30 |
-
|
31 |
/**
|
32 |
* Set up groups of files as sources
|
33 |
-
*
|
34 |
* @param array $options controller and Minify options
|
35 |
*
|
36 |
* 'groups': (required) array mapping PATH_INFO strings to arrays
|
@@ -42,12 +43,12 @@ class Minify_Controller_Groups extends Minify_Controller_Base {
|
|
42 |
// strip controller options
|
43 |
$groups = $options['groups'];
|
44 |
unset($options['groups']);
|
45 |
-
|
46 |
// mod_fcgid places PATH_INFO in ORIG_PATH_INFO
|
47 |
$pi = isset($_SERVER['ORIG_PATH_INFO'])
|
48 |
-
? substr($_SERVER['ORIG_PATH_INFO'], 1)
|
49 |
: (isset($_SERVER['PATH_INFO'])
|
50 |
-
? substr($_SERVER['PATH_INFO'], 1)
|
51 |
: false
|
52 |
);
|
53 |
if (false === $pi || ! isset($groups[$pi])) {
|
@@ -56,7 +57,7 @@ class Minify_Controller_Groups extends Minify_Controller_Base {
|
|
56 |
return $options;
|
57 |
}
|
58 |
$sources = array();
|
59 |
-
|
60 |
$files = $groups[$pi];
|
61 |
// if $files is a single object, casting will break it
|
62 |
if (is_object($files)) {
|
@@ -76,7 +77,7 @@ class Minify_Controller_Groups extends Minify_Controller_Base {
|
|
76 |
if (is_file($realPath)) {
|
77 |
$sources[] = new Minify_Source(array(
|
78 |
'filepath' => $realPath
|
79 |
-
));
|
80 |
} else {
|
81 |
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
82 |
return $options;
|
@@ -88,4 +89,3 @@ class Minify_Controller_Groups extends Minify_Controller_Base {
|
|
88 |
return $options;
|
89 |
}
|
90 |
}
|
91 |
-
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Controller_Groups
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Controller class for serving predetermined groups of minimized sets, selected
|
10 |
* by PATH_INFO
|
11 |
+
*
|
12 |
* <code>
|
13 |
+
* Minify::serve('Groups', array(
|
14 |
* 'groups' => array(
|
15 |
* 'css' => array('//css/type.css', '//css/layout.css')
|
16 |
* ,'js' => array('//js/jquery.js', '//js/site.js')
|
17 |
* )
|
18 |
* ));
|
19 |
* </code>
|
20 |
+
*
|
21 |
* If the above code were placed in /serve.php, it would enable the URLs
|
22 |
* /serve.php/js and /serve.php/css
|
23 |
+
*
|
24 |
* As a shortcut, the controller will replace "//" at the beginning
|
25 |
* of a filename with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
26 |
+
*
|
27 |
* @package Minify
|
28 |
* @author Stephen Clay <steve@mrclay.org>
|
29 |
*/
|
30 |
class Minify_Controller_Groups extends Minify_Controller_Base {
|
31 |
+
|
32 |
/**
|
33 |
* Set up groups of files as sources
|
34 |
+
*
|
35 |
* @param array $options controller and Minify options
|
36 |
*
|
37 |
* 'groups': (required) array mapping PATH_INFO strings to arrays
|
43 |
// strip controller options
|
44 |
$groups = $options['groups'];
|
45 |
unset($options['groups']);
|
46 |
+
|
47 |
// mod_fcgid places PATH_INFO in ORIG_PATH_INFO
|
48 |
$pi = isset($_SERVER['ORIG_PATH_INFO'])
|
49 |
+
? substr($_SERVER['ORIG_PATH_INFO'], 1)
|
50 |
: (isset($_SERVER['PATH_INFO'])
|
51 |
+
? substr($_SERVER['PATH_INFO'], 1)
|
52 |
: false
|
53 |
);
|
54 |
if (false === $pi || ! isset($groups[$pi])) {
|
57 |
return $options;
|
58 |
}
|
59 |
$sources = array();
|
60 |
+
|
61 |
$files = $groups[$pi];
|
62 |
// if $files is a single object, casting will break it
|
63 |
if (is_object($files)) {
|
77 |
if (is_file($realPath)) {
|
78 |
$sources[] = new Minify_Source(array(
|
79 |
'filepath' => $realPath
|
80 |
+
));
|
81 |
} else {
|
82 |
$this->log("The path \"{$file}\" could not be found (or was not a file)");
|
83 |
return $options;
|
89 |
return $options;
|
90 |
}
|
91 |
}
|
|
lib/Minify/Minify/Controller/MinApp.php
CHANGED
@@ -1,20 +1,21 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Controller_MinApp
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Controller class for requests to /min/index.php
|
9 |
-
*
|
10 |
* @package Minify
|
11 |
* @author Stephen Clay <steve@mrclay.org>
|
12 |
*/
|
13 |
class Minify_Controller_MinApp extends Minify_Controller_Base {
|
14 |
-
|
15 |
/**
|
16 |
* Set up groups of files as sources
|
17 |
-
*
|
18 |
* @param array $options controller and Minify options
|
19 |
*
|
20 |
* @return array Minify options
|
@@ -89,7 +90,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
|
89 |
if ($sources) {
|
90 |
try {
|
91 |
$this->checkType($sources[0]);
|
92 |
-
} catch (Exception $e) {
|
93 |
$this->log($e->getMessage());
|
94 |
return $options;
|
95 |
}
|
@@ -107,7 +108,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
|
107 |
&& false === strpos($_GET['b'], '..')
|
108 |
&& $_GET['b'] !== '.') {
|
109 |
// valid base
|
110 |
-
$base = "/{$_GET['b']}/";
|
111 |
} else {
|
112 |
$this->log("GET param 'b' invalid (see MinApp.php line 84)");
|
113 |
return $options;
|
@@ -143,7 +144,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
|
143 |
try {
|
144 |
parent::checkNotHidden($realpath);
|
145 |
parent::checkAllowDirs($realpath, $allowDirs, $uri);
|
146 |
-
} catch (Exception $e) {
|
147 |
$this->log($e->getMessage());
|
148 |
return $options;
|
149 |
}
|
@@ -162,7 +163,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
|
162 |
// should not cause cache invalidation
|
163 |
,'lastModified' => 0
|
164 |
// due to caching, filename is unreliable.
|
165 |
-
,'content' => "/* Minify: at least one missing file. See " .
|
166 |
,'minifier' => ''
|
167 |
)));
|
168 |
}
|
@@ -205,9 +206,9 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
|
205 |
public function checkType($sourceOrExt)
|
206 |
{
|
207 |
if ($sourceOrExt === 'js') {
|
208 |
-
$type =
|
209 |
} elseif ($sourceOrExt === 'css') {
|
210 |
-
$type =
|
211 |
} elseif ($sourceOrExt->contentType !== null) {
|
212 |
$type = $sourceOrExt->contentType;
|
213 |
} else {
|
@@ -216,7 +217,7 @@ class Minify_Controller_MinApp extends Minify_Controller_Base {
|
|
216 |
if ($this->_type === null) {
|
217 |
$this->_type = $type;
|
218 |
} elseif ($this->_type !== $type) {
|
219 |
-
throw new Exception('Content-Type mismatch');
|
220 |
}
|
221 |
}
|
222 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Controller_MinApp
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Controller class for requests to /min/index.php
|
10 |
+
*
|
11 |
* @package Minify
|
12 |
* @author Stephen Clay <steve@mrclay.org>
|
13 |
*/
|
14 |
class Minify_Controller_MinApp extends Minify_Controller_Base {
|
15 |
+
|
16 |
/**
|
17 |
* Set up groups of files as sources
|
18 |
+
*
|
19 |
* @param array $options controller and Minify options
|
20 |
*
|
21 |
* @return array Minify options
|
90 |
if ($sources) {
|
91 |
try {
|
92 |
$this->checkType($sources[0]);
|
93 |
+
} catch (\Exception $e) {
|
94 |
$this->log($e->getMessage());
|
95 |
return $options;
|
96 |
}
|
108 |
&& false === strpos($_GET['b'], '..')
|
109 |
&& $_GET['b'] !== '.') {
|
110 |
// valid base
|
111 |
+
$base = "/{$_GET['b']}/";
|
112 |
} else {
|
113 |
$this->log("GET param 'b' invalid (see MinApp.php line 84)");
|
114 |
return $options;
|
144 |
try {
|
145 |
parent::checkNotHidden($realpath);
|
146 |
parent::checkAllowDirs($realpath, $allowDirs, $uri);
|
147 |
+
} catch (\Exception $e) {
|
148 |
$this->log($e->getMessage());
|
149 |
return $options;
|
150 |
}
|
163 |
// should not cause cache invalidation
|
164 |
,'lastModified' => 0
|
165 |
// due to caching, filename is unreliable.
|
166 |
+
,'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n"
|
167 |
,'minifier' => ''
|
168 |
)));
|
169 |
}
|
206 |
public function checkType($sourceOrExt)
|
207 |
{
|
208 |
if ($sourceOrExt === 'js') {
|
209 |
+
$type = Minify::TYPE_JS;
|
210 |
} elseif ($sourceOrExt === 'css') {
|
211 |
+
$type = Minify::TYPE_CSS;
|
212 |
} elseif ($sourceOrExt->contentType !== null) {
|
213 |
$type = $sourceOrExt->contentType;
|
214 |
} else {
|
217 |
if ($this->_type === null) {
|
218 |
$this->_type = $type;
|
219 |
} elseif ($this->_type !== $type) {
|
220 |
+
throw new \Exception('Content-Type mismatch');
|
221 |
}
|
222 |
}
|
223 |
}
|
lib/Minify/Minify/Controller/Page.php
CHANGED
@@ -1,68 +1,68 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Controller_Page
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Controller class for serving a single HTML page
|
9 |
-
*
|
10 |
* @link http://code.google.com/p/minify/source/browse/trunk/web/examples/1/index.php#59
|
11 |
* @package Minify
|
12 |
* @author Stephen Clay <steve@mrclay.org>
|
13 |
*/
|
14 |
class Minify_Controller_Page extends Minify_Controller_Base {
|
15 |
-
|
16 |
-
/**
|
17 |
-
* Set up source of HTML content
|
18 |
-
*
|
19 |
-
* @param array $options controller and Minify options
|
20 |
-
* @return array Minify options
|
21 |
-
*
|
22 |
-
* Controller options:
|
23 |
-
*
|
24 |
-
* 'content': (required) HTML markup
|
25 |
-
*
|
26 |
-
* 'id': (required) id of page (string for use in server-side caching)
|
27 |
-
*
|
28 |
-
* 'lastModifiedTime': timestamp of when this content changed. This
|
29 |
-
* is recommended to allow both server and client-side caching.
|
30 |
-
*
|
31 |
-
* 'minifyAll': should all CSS and Javascript blocks be individually
|
32 |
-
* minified? (default false)
|
33 |
-
*
|
34 |
-
* @todo Add 'file' option to read HTML file.
|
35 |
-
*/
|
36 |
-
public function setupSources($options) {
|
37 |
-
if (isset($options['file'])) {
|
38 |
-
$sourceSpec = array(
|
39 |
-
'filepath' => $options['file']
|
40 |
-
);
|
41 |
-
$f = $options['file'];
|
42 |
-
} else {
|
43 |
-
// strip controller options
|
44 |
-
$sourceSpec = array(
|
45 |
-
'content' => $options['content']
|
46 |
-
,'id' => $options['id']
|
47 |
-
);
|
48 |
-
$f = $options['id'];
|
49 |
-
unset($options['content'], $options['id']);
|
50 |
-
}
|
51 |
-
// something like "builder,index.php" or "directory,file.html"
|
52 |
-
$this->selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,');
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
|
|
|
|
|
|
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Controller_Page
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Controller class for serving a single HTML page
|
10 |
+
*
|
11 |
* @link http://code.google.com/p/minify/source/browse/trunk/web/examples/1/index.php#59
|
12 |
* @package Minify
|
13 |
* @author Stephen Clay <steve@mrclay.org>
|
14 |
*/
|
15 |
class Minify_Controller_Page extends Minify_Controller_Base {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
|
17 |
+
/**
|
18 |
+
* Set up source of HTML content
|
19 |
+
*
|
20 |
+
* @param array $options controller and Minify options
|
21 |
+
* @return array Minify options
|
22 |
+
*
|
23 |
+
* Controller options:
|
24 |
+
*
|
25 |
+
* 'content': (required) HTML markup
|
26 |
+
*
|
27 |
+
* 'id': (required) id of page (string for use in server-side caching)
|
28 |
+
*
|
29 |
+
* 'lastModifiedTime': timestamp of when this content changed. This
|
30 |
+
* is recommended to allow both server and client-side caching.
|
31 |
+
*
|
32 |
+
* 'minifyAll': should all CSS and Javascript blocks be individually
|
33 |
+
* minified? (default false)
|
34 |
+
*
|
35 |
+
* @todo Add 'file' option to read HTML file.
|
36 |
+
*/
|
37 |
+
public function setupSources($options) {
|
38 |
+
if (isset($options['file'])) {
|
39 |
+
$sourceSpec = array(
|
40 |
+
'filepath' => $options['file']
|
41 |
+
);
|
42 |
+
$f = $options['file'];
|
43 |
+
} else {
|
44 |
+
// strip controller options
|
45 |
+
$sourceSpec = array(
|
46 |
+
'content' => $options['content']
|
47 |
+
,'id' => $options['id']
|
48 |
+
);
|
49 |
+
$f = $options['id'];
|
50 |
+
unset($options['content'], $options['id']);
|
51 |
+
}
|
52 |
+
// something like "builder,index.php" or "directory,file.html"
|
53 |
+
$this->selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,');
|
54 |
+
|
55 |
+
if (isset($options['minifyAll'])) {
|
56 |
+
// this will be the 2nd argument passed to Minify_HTML::minify()
|
57 |
+
$sourceSpec['minifyOptions'] = array(
|
58 |
+
'cssMinifier' => array('\W3TCL\Minify\Minify_CSS', 'minify')
|
59 |
+
,'jsMinifier' => array('\W3TCL\Minify\JSMin', 'minify')
|
60 |
+
);
|
61 |
+
unset($options['minifyAll']);
|
62 |
+
}
|
63 |
+
$this->sources[] = new Minify_Source($sourceSpec);
|
64 |
|
65 |
+
$options['contentType'] = Minify::TYPE_HTML;
|
66 |
+
return $options;
|
67 |
+
}
|
68 |
+
}
|
lib/Minify/Minify/Controller/Version1.php
CHANGED
@@ -1,27 +1,28 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Controller_Version1
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
/**
|
8 |
* Controller class for emulating version 1 of minify.php (mostly a proof-of-concept)
|
9 |
-
*
|
10 |
* <code>
|
11 |
* Minify::serve('Version1');
|
12 |
* </code>
|
13 |
-
*
|
14 |
* @package Minify
|
15 |
* @author Stephen Clay <steve@mrclay.org>
|
16 |
*/
|
17 |
class Minify_Controller_Version1 extends Minify_Controller_Base {
|
18 |
-
|
19 |
/**
|
20 |
* Set up groups of files as sources
|
21 |
-
*
|
22 |
* @param array $options controller and Minify options
|
23 |
* @return array Minify options
|
24 |
-
*
|
25 |
*/
|
26 |
public function setupSources($options) {
|
27 |
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
|
@@ -34,7 +35,7 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
|
|
34 |
$cacheDir = defined('MINIFY_CACHE_DIR')
|
35 |
? MINIFY_CACHE_DIR
|
36 |
: '';
|
37 |
-
|
38 |
}
|
39 |
$options['badRequestHeader'] = 'HTTP/1.0 404 Not Found';
|
40 |
$options['contentTypeCharset'] = MINIFY_ENCODING;
|
@@ -42,7 +43,7 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
|
|
42 |
// The following restrictions are to limit the URLs that minify will
|
43 |
// respond to. Ideally there should be only one way to reference a file.
|
44 |
if (! isset($_GET['files'])
|
45 |
-
// verify at least one file, files are single comma separated,
|
46 |
// and are all same extension
|
47 |
|| ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m)
|
48 |
// no "//" (makes URL rewriting easier)
|
@@ -59,27 +60,27 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
|
|
59 |
if (count($files) > MINIFY_MAX_FILES) {
|
60 |
return $options;
|
61 |
}
|
62 |
-
|
63 |
// strings for prepending to relative/absolute paths
|
64 |
$prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME'])
|
65 |
. DIRECTORY_SEPARATOR;
|
66 |
$prependAbsPaths = $_SERVER['DOCUMENT_ROOT'];
|
67 |
-
|
68 |
$goodFiles = array();
|
69 |
$hasBadSource = false;
|
70 |
-
|
71 |
$allowDirs = isset($options['allowDirs'])
|
72 |
? $options['allowDirs']
|
73 |
: MINIFY_BASE_DIR;
|
74 |
-
|
75 |
foreach ($files as $file) {
|
76 |
// prepend appropriate string for abs/rel paths
|
77 |
$file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file;
|
78 |
// make sure a real file!
|
79 |
$file = realpath($file);
|
80 |
// don't allow unsafe or duplicate files
|
81 |
-
if (parent::_fileIsSafe($file, $allowDirs)
|
82 |
-
&& !in_array($file, $goodFiles))
|
83 |
{
|
84 |
$goodFiles[] = $file;
|
85 |
$srcOptions = array(
|
@@ -99,7 +100,7 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
|
|
99 |
}
|
100 |
return $options;
|
101 |
}
|
102 |
-
|
103 |
private static function _setupDefines()
|
104 |
{
|
105 |
$defaults = array(
|
@@ -116,4 +117,3 @@ class Minify_Controller_Version1 extends Minify_Controller_Base {
|
|
116 |
}
|
117 |
}
|
118 |
}
|
119 |
-
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Controller_Version1
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
/**
|
9 |
* Controller class for emulating version 1 of minify.php (mostly a proof-of-concept)
|
10 |
+
*
|
11 |
* <code>
|
12 |
* Minify::serve('Version1');
|
13 |
* </code>
|
14 |
+
*
|
15 |
* @package Minify
|
16 |
* @author Stephen Clay <steve@mrclay.org>
|
17 |
*/
|
18 |
class Minify_Controller_Version1 extends Minify_Controller_Base {
|
19 |
+
|
20 |
/**
|
21 |
* Set up groups of files as sources
|
22 |
+
*
|
23 |
* @param array $options controller and Minify options
|
24 |
* @return array Minify options
|
25 |
+
*
|
26 |
*/
|
27 |
public function setupSources($options) {
|
28 |
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
|
35 |
$cacheDir = defined('MINIFY_CACHE_DIR')
|
36 |
? MINIFY_CACHE_DIR
|
37 |
: '';
|
38 |
+
Minify::setCache($cacheDir);
|
39 |
}
|
40 |
$options['badRequestHeader'] = 'HTTP/1.0 404 Not Found';
|
41 |
$options['contentTypeCharset'] = MINIFY_ENCODING;
|
43 |
// The following restrictions are to limit the URLs that minify will
|
44 |
// respond to. Ideally there should be only one way to reference a file.
|
45 |
if (! isset($_GET['files'])
|
46 |
+
// verify at least one file, files are single comma separated,
|
47 |
// and are all same extension
|
48 |
|| ! preg_match('/^[^,]+\\.(css|js)(,[^,]+\\.\\1)*$/', $_GET['files'], $m)
|
49 |
// no "//" (makes URL rewriting easier)
|
60 |
if (count($files) > MINIFY_MAX_FILES) {
|
61 |
return $options;
|
62 |
}
|
63 |
+
|
64 |
// strings for prepending to relative/absolute paths
|
65 |
$prependRelPaths = dirname($_SERVER['SCRIPT_FILENAME'])
|
66 |
. DIRECTORY_SEPARATOR;
|
67 |
$prependAbsPaths = $_SERVER['DOCUMENT_ROOT'];
|
68 |
+
|
69 |
$goodFiles = array();
|
70 |
$hasBadSource = false;
|
71 |
+
|
72 |
$allowDirs = isset($options['allowDirs'])
|
73 |
? $options['allowDirs']
|
74 |
: MINIFY_BASE_DIR;
|
75 |
+
|
76 |
foreach ($files as $file) {
|
77 |
// prepend appropriate string for abs/rel paths
|
78 |
$file = ($file[0] === '/' ? $prependAbsPaths : $prependRelPaths) . $file;
|
79 |
// make sure a real file!
|
80 |
$file = realpath($file);
|
81 |
// don't allow unsafe or duplicate files
|
82 |
+
if (parent::_fileIsSafe($file, $allowDirs)
|
83 |
+
&& !in_array($file, $goodFiles))
|
84 |
{
|
85 |
$goodFiles[] = $file;
|
86 |
$srcOptions = array(
|
100 |
}
|
101 |
return $options;
|
102 |
}
|
103 |
+
|
104 |
private static function _setupDefines()
|
105 |
{
|
106 |
$defaults = array(
|
117 |
}
|
118 |
}
|
119 |
}
|
|
lib/Minify/Minify/DebugDetector.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
/**
|
4 |
* Detect whether request should be debugged
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
/**
|
5 |
* Detect whether request should be debugged
|
lib/Minify/Minify/HTML.php
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
* Class Minify_HTML
|
4 |
* @package Minify
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
+
|
4 |
/**
|
5 |
* Class Minify_HTML
|
6 |
* @package Minify
|
lib/Minify/Minify/HTML/Helper.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_HTML_Helper
|
4 |
* @package Minify
|
@@ -17,7 +18,7 @@ class Minify_HTML_Helper {
|
|
17 |
|
18 |
/**
|
19 |
* Get an HTML-escaped Minify URI for a group or set of files
|
20 |
-
*
|
21 |
* @param string|array $keyOrFiles a group key or array of filepaths/URIs
|
22 |
* @param array $opts options:
|
23 |
* 'farExpires' : (default true) append a modified timestamp for cache revving
|
@@ -158,7 +159,7 @@ class Minify_HTML_Helper {
|
|
158 |
protected $_filePaths = array();
|
159 |
protected $_lastModified = null;
|
160 |
|
161 |
-
|
162 |
/**
|
163 |
* In a given array of strings, find the character they all have at
|
164 |
* a particular index
|
@@ -205,7 +206,7 @@ class Minify_HTML_Helper {
|
|
205 |
}
|
206 |
$base = preg_replace('@[^/]+$@', '', $base);
|
207 |
$uri = $minRoot . 'f=' . implode(',', $paths);
|
208 |
-
|
209 |
if (substr($base, -1) === '/') {
|
210 |
// we have a base dir!
|
211 |
$basedPaths = $paths;
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_HTML_Helper
|
5 |
* @package Minify
|
18 |
|
19 |
/**
|
20 |
* Get an HTML-escaped Minify URI for a group or set of files
|
21 |
+
*
|
22 |
* @param string|array $keyOrFiles a group key or array of filepaths/URIs
|
23 |
* @param array $opts options:
|
24 |
* 'farExpires' : (default true) append a modified timestamp for cache revving
|
159 |
protected $_filePaths = array();
|
160 |
protected $_lastModified = null;
|
161 |
|
162 |
+
|
163 |
/**
|
164 |
* In a given array of strings, find the character they all have at
|
165 |
* a particular index
|
206 |
}
|
207 |
$base = preg_replace('@[^/]+$@', '', $base);
|
208 |
$uri = $minRoot . 'f=' . implode(',', $paths);
|
209 |
+
|
210 |
if (substr($base, -1) === '/') {
|
211 |
// we have a base dir!
|
212 |
$basedPaths = $paths;
|
lib/Minify/Minify/HTMLTidy.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
class Minify_HTMLTidy {
|
4 |
public static function minify($content, $options = array()) {
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
class Minify_HTMLTidy {
|
5 |
public static function minify($content, $options = array()) {
|
lib/Minify/Minify/IgnoredCommentPreserver.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
class Minify_IgnoredCommentPreserver {
|
4 |
protected $_replacementHash = '';
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
class Minify_IgnoredCommentPreserver {
|
5 |
protected $_replacementHash = '';
|
lib/Minify/Minify/ImportProcessor.php
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
<?php
|
|
|
|
|
2 |
/**
|
3 |
* Class Minify_ImportProcessor
|
4 |
* @package Minify
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
+
|
4 |
/**
|
5 |
* Class Minify_ImportProcessor
|
6 |
* @package Minify
|
lib/Minify/Minify/Inline.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
abstract class Minify_Inline {
|
4 |
protected $_tag = '';
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
abstract class Minify_Inline {
|
5 |
protected $_tag = '';
|
lib/Minify/Minify/Inline/CSS.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
if (!defined('W3TC')) {
|
4 |
die();
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
if (!defined('W3TC')) {
|
5 |
die();
|
lib/Minify/Minify/Inline/JavaScript.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
|
3 |
if (!defined('W3TC')) {
|
4 |
die();
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
|
4 |
if (!defined('W3TC')) {
|
5 |
die();
|
lib/Minify/Minify/JS/ClosureCompiler.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_JS_ClosureCompiler
|
4 |
* @package Minify
|
@@ -14,145 +15,145 @@
|
|
14 |
* @todo can use a stream wrapper to unit test this?
|
15 |
*/
|
16 |
class Minify_JS_ClosureCompiler {
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
}
|
157 |
|
158 |
-
class Minify_JS_ClosureCompiler_Exception extends Exception {}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_JS_ClosureCompiler
|
5 |
* @package Minify
|
15 |
* @todo can use a stream wrapper to unit test this?
|
16 |
*/
|
17 |
class Minify_JS_ClosureCompiler {
|
18 |
+
const URL = 'https://closure-compiler.appspot.com/compile';
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Minify Javascript code via HTTP request to the Closure Compiler API
|
22 |
+
*
|
23 |
+
* @param string $js input code
|
24 |
+
* @param array $options unused at this point
|
25 |
+
* @return string
|
26 |
+
*/
|
27 |
+
public static function minify($js, array $options = array())
|
28 |
+
{
|
29 |
+
$obj = new self($options);
|
30 |
+
return $obj->min($js);
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
*
|
35 |
+
* @param array $options
|
36 |
+
*
|
37 |
+
* fallbackFunc : default array($this, 'fallback');
|
38 |
+
*/
|
39 |
+
public function __construct(array $options = array())
|
40 |
+
{
|
41 |
+
$this->options = $options;
|
42 |
+
$this->_fallbackFunc = isset($options['fallbackMinifier'])
|
43 |
+
? $options['fallbackMinifier']
|
44 |
+
: array($this, '_fallback');
|
45 |
+
}
|
46 |
+
|
47 |
+
public function min($js)
|
48 |
+
{
|
49 |
+
if (trim($js) === '')
|
50 |
+
return $js;
|
51 |
+
|
52 |
+
$postBody = $this->_buildPostBody($js);
|
53 |
+
$bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
|
54 |
+
? mb_strlen($postBody, '8bit')
|
55 |
+
: strlen($postBody);
|
56 |
+
if ($bytes > 200000)
|
57 |
+
return $this->fail($js,
|
58 |
+
'File size is larger than Closure Compiler API limit (200000 bytes)');
|
59 |
+
|
60 |
+
$response = $this->_getResponse($postBody);
|
61 |
+
if (preg_match('/^Error\(\d\d?\):/', $response))
|
62 |
+
return $this->fail($response,
|
63 |
+
"Received errors from Closure Compiler API:\n$response");
|
64 |
+
|
65 |
+
return $response;
|
66 |
+
}
|
67 |
+
|
68 |
+
private function fail($js, $errorMessage) {
|
69 |
+
Minify::$recoverableError = $errorMessage;
|
70 |
+
$response = "/* " . $errorMessage . "\n(Using fallback minifier)\n*/\n";
|
71 |
+
if (is_callable($this->_fallbackFunc))
|
72 |
+
$response .= call_user_func($this->_fallbackFunc, $js);
|
73 |
+
else
|
74 |
+
$response .= $js;
|
75 |
+
|
76 |
+
return $response;
|
77 |
+
}
|
78 |
+
|
79 |
+
protected $_fallbackFunc = null;
|
80 |
+
protected $_options = array();
|
81 |
+
|
82 |
+
protected function _getResponse($postBody)
|
83 |
+
{
|
84 |
+
$allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
|
85 |
+
if ($allowUrlFopen) {
|
86 |
+
$contents = file_get_contents(self::URL, false, stream_context_create(array(
|
87 |
+
'http' => array(
|
88 |
+
'method' => 'POST',
|
89 |
+
'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
|
90 |
+
'content' => $postBody,
|
91 |
+
'max_redirects' => 0,
|
92 |
+
'timeout' => 15,
|
93 |
+
)
|
94 |
+
)));
|
95 |
+
} elseif (defined('CURLOPT_POST')) {
|
96 |
+
$ch = curl_init(self::URL);
|
97 |
+
curl_setopt($ch, CURLOPT_POST, true);
|
98 |
+
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
99 |
+
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
|
100 |
+
curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
|
101 |
+
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
|
102 |
+
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
|
103 |
+
$contents = curl_exec($ch);
|
104 |
+
curl_close($ch);
|
105 |
+
} else {
|
106 |
+
throw new Minify_JS_ClosureCompiler_Exception(
|
107 |
+
"Could not make HTTP request: allow_url_open is false and cURL not available"
|
108 |
+
);
|
109 |
+
}
|
110 |
+
if (false === $contents) {
|
111 |
+
throw new Minify_JS_ClosureCompiler_Exception(
|
112 |
+
"No HTTP response from server"
|
113 |
+
);
|
114 |
+
}
|
115 |
+
return trim($contents);
|
116 |
+
}
|
117 |
+
|
118 |
+
protected function _buildPostBody($js, $returnErrors = false)
|
119 |
+
{
|
120 |
+
$a = array(
|
121 |
+
'js_code' => $js,
|
122 |
+
'output_info' => ($returnErrors ? 'errors' : 'compiled_code'),
|
123 |
+
'output_format' => 'text',
|
124 |
+
'compilation_level' =>
|
125 |
+
(isset($this->options['compilation_level']) ?
|
126 |
+
$this->options['compilation_level'] :
|
127 |
+
'SIMPLE_OPTIMIZATIONS')
|
128 |
+
);
|
129 |
+
if (isset($this->options['formatting']) && !empty($this->options['formatting']))
|
130 |
+
$a['formatting'] = $this->options['formatting'];
|
131 |
+
|
132 |
+
return http_build_query($a, null, '&');
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Default fallback function if CC API fails
|
137 |
+
* @param string $js
|
138 |
+
* @return string
|
139 |
+
*/
|
140 |
+
protected function _fallback($js)
|
141 |
+
{
|
142 |
+
return JSMin::minify($js);
|
143 |
+
}
|
144 |
+
|
145 |
+
public static function test(&$error) {
|
146 |
+
try {
|
147 |
+
self::minify('alert("ok");');
|
148 |
+
$error = 'OK';
|
149 |
+
|
150 |
+
return true;
|
151 |
+
} catch (\Exception $exception) {
|
152 |
+
$error = $exception->getMessage();
|
153 |
+
|
154 |
+
return false;
|
155 |
+
}
|
156 |
+
}
|
157 |
}
|
158 |
|
159 |
+
class Minify_JS_ClosureCompiler_Exception extends \Exception {}
|
lib/Minify/Minify/Lines.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_Lines
|
4 |
* @package Minify
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_Lines
|
5 |
* @package Minify
|
lib/Minify/Minify/Loader.php
DELETED
@@ -1,28 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
/**
|
3 |
-
* Class Minify_Loader
|
4 |
-
* @package Minify
|
5 |
-
*/
|
6 |
-
|
7 |
-
/**
|
8 |
-
* Class autoloader
|
9 |
-
*
|
10 |
-
* @package Minify
|
11 |
-
* @author Stephen Clay <steve@mrclay.org>
|
12 |
-
*/
|
13 |
-
class Minify_Loader {
|
14 |
-
public function loadClass($class)
|
15 |
-
{
|
16 |
-
$file = dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR;
|
17 |
-
$file .= strtr($class, "\\_", DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR) . '.php';
|
18 |
-
if (is_readable($file)) {
|
19 |
-
require $file;
|
20 |
-
}
|
21 |
-
}
|
22 |
-
|
23 |
-
static public function register()
|
24 |
-
{
|
25 |
-
$inst = new self();
|
26 |
-
spl_autoload_register(array($inst, 'loadClass'));
|
27 |
-
}
|
28 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/Minify/Minify/Logger.php
CHANGED
@@ -1,12 +1,13 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Logger
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
-
/**
|
8 |
* Message logging class
|
9 |
-
*
|
10 |
* @package Minify
|
11 |
* @author Stephen Clay <steve@mrclay.org>
|
12 |
*
|
@@ -15,7 +16,7 @@
|
|
15 |
class Minify_Logger {
|
16 |
|
17 |
/**
|
18 |
-
* Set logger object.
|
19 |
*
|
20 |
* The object should have a method "log" that accepts a value as 1st argument and
|
21 |
* an optional string label as the 2nd.
|
@@ -26,7 +27,7 @@ class Minify_Logger {
|
|
26 |
public static function setLogger($obj = null) {
|
27 |
self::$_logger = $obj;
|
28 |
}
|
29 |
-
|
30 |
/**
|
31 |
* Pass a message to the logger (if set)
|
32 |
*
|
@@ -38,7 +39,7 @@ class Minify_Logger {
|
|
38 |
call_user_func(self::$_logger, $msg);
|
39 |
}
|
40 |
}
|
41 |
-
|
42 |
/**
|
43 |
* @var mixed logger object (like FirePHP) or null (i.e. no logger available)
|
44 |
*/
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Logger
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
+
/**
|
9 |
* Message logging class
|
10 |
+
*
|
11 |
* @package Minify
|
12 |
* @author Stephen Clay <steve@mrclay.org>
|
13 |
*
|
16 |
class Minify_Logger {
|
17 |
|
18 |
/**
|
19 |
+
* Set logger object.
|
20 |
*
|
21 |
* The object should have a method "log" that accepts a value as 1st argument and
|
22 |
* an optional string label as the 2nd.
|
27 |
public static function setLogger($obj = null) {
|
28 |
self::$_logger = $obj;
|
29 |
}
|
30 |
+
|
31 |
/**
|
32 |
* Pass a message to the logger (if set)
|
33 |
*
|
39 |
call_user_func(self::$_logger, $msg);
|
40 |
}
|
41 |
}
|
42 |
+
|
43 |
/**
|
44 |
* @var mixed logger object (like FirePHP) or null (i.e. no logger available)
|
45 |
*/
|
lib/Minify/Minify/Packer.php
CHANGED
@@ -1,17 +1,18 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_Packer
|
4 |
*
|
5 |
* To use this class you must first download the PHP port of Packer
|
6 |
* and place the file "class.JavaScriptPacker.php" in /lib (or your
|
7 |
-
* include_path).
|
8 |
* @link http://joliclic.free.fr/php/javascript-packer/en/
|
9 |
*
|
10 |
* Be aware that, as long as HTTP encoding is used, scripts minified with JSMin
|
11 |
* will provide better client-side performance, as they need not be unpacked in
|
12 |
* client-side code.
|
13 |
-
*
|
14 |
-
* @package Minify
|
15 |
*/
|
16 |
|
17 |
if (false === (@include 'class.JavaScriptPacker.php')) {
|
@@ -24,7 +25,7 @@ if (false === (@include 'class.JavaScriptPacker.php')) {
|
|
24 |
|
25 |
/**
|
26 |
* Minify Javascript using Dean Edward's Packer
|
27 |
-
*
|
28 |
* @package Minify
|
29 |
*/
|
30 |
class Minify_Packer {
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_Packer
|
5 |
*
|
6 |
* To use this class you must first download the PHP port of Packer
|
7 |
* and place the file "class.JavaScriptPacker.php" in /lib (or your
|
8 |
+
* include_path).
|
9 |
* @link http://joliclic.free.fr/php/javascript-packer/en/
|
10 |
*
|
11 |
* Be aware that, as long as HTTP encoding is used, scripts minified with JSMin
|
12 |
* will provide better client-side performance, as they need not be unpacked in
|
13 |
* client-side code.
|
14 |
+
*
|
15 |
+
* @package Minify
|
16 |
*/
|
17 |
|
18 |
if (false === (@include 'class.JavaScriptPacker.php')) {
|
25 |
|
26 |
/**
|
27 |
* Minify Javascript using Dean Edward's Packer
|
28 |
+
*
|
29 |
* @package Minify
|
30 |
*/
|
31 |
class Minify_Packer {
|
lib/Minify/Minify/Source.php
CHANGED
@@ -1,15 +1,16 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_Source
|
4 |
* @package Minify
|
5 |
*/
|
6 |
|
7 |
-
/**
|
8 |
-
* A content source to be minified by Minify.
|
9 |
-
*
|
10 |
* This allows per-source minification options and the mixing of files with
|
11 |
* content from other sources.
|
12 |
-
*
|
13 |
* @package Minify
|
14 |
* @author Stephen Clay <steve@mrclay.org>
|
15 |
*/
|
@@ -19,12 +20,12 @@ class Minify_Source {
|
|
19 |
* @var int time of last modification
|
20 |
*/
|
21 |
public $lastModified = null;
|
22 |
-
|
23 |
/**
|
24 |
* @var callback minifier function specifically for this source.
|
25 |
*/
|
26 |
public $minifier = null;
|
27 |
-
|
28 |
/**
|
29 |
* @var array minification options specific to this source.
|
30 |
*/
|
@@ -34,20 +35,20 @@ class Minify_Source {
|
|
34 |
* @var string full path of file
|
35 |
*/
|
36 |
public $filepath = null;
|
37 |
-
|
38 |
/**
|
39 |
* @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*)
|
40 |
*/
|
41 |
public $contentType = null;
|
42 |
-
|
43 |
/**
|
44 |
* Create a Minify_Source
|
45 |
-
*
|
46 |
* In the $spec array(), you can either provide a 'filepath' to an existing
|
47 |
-
* file (existence will not be checked!) or give 'id' (unique string for
|
48 |
-
* the content), 'content' (the string content) and 'lastModified'
|
49 |
* (unixtime of last update).
|
50 |
-
*
|
51 |
* As a shortcut, the controller will replace "//" at the beginning
|
52 |
* of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
53 |
*
|
@@ -74,7 +75,7 @@ class Minify_Source {
|
|
74 |
$this->_id = $spec['filepath'];
|
75 |
$this->lastModified = filemtime($spec['filepath'])
|
76 |
// offset for Windows uploaders with out of sync clocks
|
77 |
-
+ round(
|
78 |
} elseif (isset($spec['id'])) {
|
79 |
$this->_id = 'id::' . $spec['id'];
|
80 |
if (isset($spec['content'])) {
|
@@ -96,7 +97,7 @@ class Minify_Source {
|
|
96 |
$this->minifyOptions = $spec['minifyOptions'];
|
97 |
}
|
98 |
}
|
99 |
-
|
100 |
/**
|
101 |
* Get content
|
102 |
*
|
@@ -114,13 +115,13 @@ class Minify_Source {
|
|
114 |
: call_user_func($this->_getContentFunc, $this->_id)
|
115 |
);
|
116 |
}
|
117 |
-
|
118 |
// remove UTF-8 BOM if present
|
119 |
return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3))
|
120 |
? substr($content, 3)
|
121 |
: $content;
|
122 |
}
|
123 |
-
|
124 |
/**
|
125 |
* Get id
|
126 |
*
|
@@ -130,12 +131,12 @@ class Minify_Source {
|
|
130 |
{
|
131 |
return $this->_id;
|
132 |
}
|
133 |
-
|
134 |
/**
|
135 |
* Verifies a single minification call can handle all sources
|
136 |
*
|
137 |
* @param array $sources Minify_Source instances
|
138 |
-
*
|
139 |
* @return bool true iff there no sources with specific minifier preferences.
|
140 |
*/
|
141 |
public static function haveNoMinifyPrefs($sources)
|
@@ -148,12 +149,12 @@ class Minify_Source {
|
|
148 |
}
|
149 |
return true;
|
150 |
}
|
151 |
-
|
152 |
/**
|
153 |
* Get unique string for a set of sources
|
154 |
*
|
155 |
* @param array $sources Minify_Source instances
|
156 |
-
*
|
157 |
* @return string
|
158 |
*/
|
159 |
public static function getDigest($sources)
|
@@ -165,14 +166,14 @@ class Minify_Source {
|
|
165 |
}
|
166 |
return md5(serialize($info));
|
167 |
}
|
168 |
-
|
169 |
/**
|
170 |
* Get content type from a group of sources
|
171 |
-
*
|
172 |
-
* This is called if the user doesn't pass in a 'contentType' options
|
173 |
-
*
|
174 |
* @param array $sources Minify_Source instances
|
175 |
-
*
|
176 |
* @return string content type. e.g. 'text/css'
|
177 |
*/
|
178 |
public static function getContentType($sources)
|
@@ -184,9 +185,8 @@ class Minify_Source {
|
|
184 |
}
|
185 |
return 'text/plain';
|
186 |
}
|
187 |
-
|
188 |
protected $_content = null;
|
189 |
protected $_getContentFunc = null;
|
190 |
protected $_id = null;
|
191 |
}
|
192 |
-
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_Source
|
5 |
* @package Minify
|
6 |
*/
|
7 |
|
8 |
+
/**
|
9 |
+
* A content source to be minified by Minify.
|
10 |
+
*
|
11 |
* This allows per-source minification options and the mixing of files with
|
12 |
* content from other sources.
|
13 |
+
*
|
14 |
* @package Minify
|
15 |
* @author Stephen Clay <steve@mrclay.org>
|
16 |
*/
|
20 |
* @var int time of last modification
|
21 |
*/
|
22 |
public $lastModified = null;
|
23 |
+
|
24 |
/**
|
25 |
* @var callback minifier function specifically for this source.
|
26 |
*/
|
27 |
public $minifier = null;
|
28 |
+
|
29 |
/**
|
30 |
* @var array minification options specific to this source.
|
31 |
*/
|
35 |
* @var string full path of file
|
36 |
*/
|
37 |
public $filepath = null;
|
38 |
+
|
39 |
/**
|
40 |
* @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*)
|
41 |
*/
|
42 |
public $contentType = null;
|
43 |
+
|
44 |
/**
|
45 |
* Create a Minify_Source
|
46 |
+
*
|
47 |
* In the $spec array(), you can either provide a 'filepath' to an existing
|
48 |
+
* file (existence will not be checked!) or give 'id' (unique string for
|
49 |
+
* the content), 'content' (the string content) and 'lastModified'
|
50 |
* (unixtime of last update).
|
51 |
+
*
|
52 |
* As a shortcut, the controller will replace "//" at the beginning
|
53 |
* of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'.
|
54 |
*
|
75 |
$this->_id = $spec['filepath'];
|
76 |
$this->lastModified = filemtime($spec['filepath'])
|
77 |
// offset for Windows uploaders with out of sync clocks
|
78 |
+
+ round(Minify::$uploaderHoursBehind * 3600);
|
79 |
} elseif (isset($spec['id'])) {
|
80 |
$this->_id = 'id::' . $spec['id'];
|
81 |
if (isset($spec['content'])) {
|
97 |
$this->minifyOptions = $spec['minifyOptions'];
|
98 |
}
|
99 |
}
|
100 |
+
|
101 |
/**
|
102 |
* Get content
|
103 |
*
|
115 |
: call_user_func($this->_getContentFunc, $this->_id)
|
116 |
);
|
117 |
}
|
118 |
+
|
119 |
// remove UTF-8 BOM if present
|
120 |
return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3))
|
121 |
? substr($content, 3)
|
122 |
: $content;
|
123 |
}
|
124 |
+
|
125 |
/**
|
126 |
* Get id
|
127 |
*
|
131 |
{
|
132 |
return $this->_id;
|
133 |
}
|
134 |
+
|
135 |
/**
|
136 |
* Verifies a single minification call can handle all sources
|
137 |
*
|
138 |
* @param array $sources Minify_Source instances
|
139 |
+
*
|
140 |
* @return bool true iff there no sources with specific minifier preferences.
|
141 |
*/
|
142 |
public static function haveNoMinifyPrefs($sources)
|
149 |
}
|
150 |
return true;
|
151 |
}
|
152 |
+
|
153 |
/**
|
154 |
* Get unique string for a set of sources
|
155 |
*
|
156 |
* @param array $sources Minify_Source instances
|
157 |
+
*
|
158 |
* @return string
|
159 |
*/
|
160 |
public static function getDigest($sources)
|
166 |
}
|
167 |
return md5(serialize($info));
|
168 |
}
|
169 |
+
|
170 |
/**
|
171 |
* Get content type from a group of sources
|
172 |
+
*
|
173 |
+
* This is called if the user doesn't pass in a 'contentType' options
|
174 |
+
*
|
175 |
* @param array $sources Minify_Source instances
|
176 |
+
*
|
177 |
* @return string content type. e.g. 'text/css'
|
178 |
*/
|
179 |
public static function getContentType($sources)
|
185 |
}
|
186 |
return 'text/plain';
|
187 |
}
|
188 |
+
|
189 |
protected $_content = null;
|
190 |
protected $_getContentFunc = null;
|
191 |
protected $_id = null;
|
192 |
}
|
|
lib/Minify/Minify/YUI/CssCompressor.php
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
-
* Class Minify_YUI_CssCompressor
|
4 |
* @package Minify
|
5 |
*
|
6 |
* YUI Compressor
|
@@ -15,157 +16,157 @@
|
|
15 |
|
16 |
/**
|
17 |
* Compress CSS (incomplete DO NOT USE)
|
18 |
-
*
|
19 |
* @see https://github.com/yui/yuicompressor/blob/master/src/com/yahoo/platform/yui/compressor/CssCompressor.java
|
20 |
*
|
21 |
* @package Minify
|
22 |
*/
|
23 |
class Minify_YUI_CssCompressor {
|
24 |
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
+
* Class Minify_YUI_CssCompressor
|
5 |
* @package Minify
|
6 |
*
|
7 |
* YUI Compressor
|
16 |
|
17 |
/**
|
18 |
* Compress CSS (incomplete DO NOT USE)
|
19 |
+
*
|
20 |
* @see https://github.com/yui/yuicompressor/blob/master/src/com/yahoo/platform/yui/compressor/CssCompressor.java
|
21 |
*
|
22 |
* @package Minify
|
23 |
*/
|
24 |
class Minify_YUI_CssCompressor {
|
25 |
|
26 |
+
/**
|
27 |
+
* Minify a CSS string
|
28 |
+
*
|
29 |
+
* @param string $css
|
30 |
+
*
|
31 |
+
* @return string
|
32 |
+
*/
|
33 |
+
public function compress($css, $linebreakpos = 0)
|
34 |
+
{
|
35 |
+
$css = str_replace("\r\n", "\n", $css);
|
36 |
+
|
37 |
+
/**
|
38 |
+
* @todo comment removal
|
39 |
+
* @todo re-port from newer Java version
|
40 |
+
*/
|
41 |
+
|
42 |
+
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
43 |
+
$css = preg_replace('@\s+@', ' ', $css);
|
44 |
+
|
45 |
+
// Make a pseudo class for the Box Model Hack
|
46 |
+
$css = preg_replace("@\"\\\\\"}\\\\\"\"@", "___PSEUDOCLASSBMH___", $css);
|
47 |
+
|
48 |
+
// Remove the spaces before the things that should not have spaces before them.
|
49 |
+
// But, be careful not to turn "p :link {...}" into "p:link{...}"
|
50 |
+
// Swap out any pseudo-class colons with the token, and then swap back.
|
51 |
+
$css = preg_replace_callback("@(^|\\})(([^\\{:])+:)+([^\\{]*\\{)@", array($this, '_removeSpacesCB'), $css);
|
52 |
+
|
53 |
+
$css = preg_replace("@\\s+([!{};:>+\\(\\)\\],])@", "$1", $css);
|
54 |
+
$css = str_replace("___PSEUDOCLASSCOLON___", ":", $css);
|
55 |
+
|
56 |
+
// Remove the spaces after the things that should not have spaces after them.
|
57 |
+
$css = preg_replace("@([!{}:;>+\\(\\[,])\\s+@", "$1", $css);
|
58 |
+
|
59 |
+
// Add the semicolon where it's missing.
|
60 |
+
$css = preg_replace("@([^;\\}])}@", "$1;}", $css);
|
61 |
+
|
62 |
+
// Replace 0(px,em,%) with 0.
|
63 |
+
$css = preg_replace("@([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)@", "$1$2", $css);
|
64 |
+
|
65 |
+
// Replace 0 0 0 0; with 0.
|
66 |
+
$css = str_replace(":0 0 0 0;", ":0;", $css);
|
67 |
+
$css = str_replace(":0 0 0;", ":0;", $css);
|
68 |
+
$css = str_replace(":0 0;", ":0;", $css);
|
69 |
+
|
70 |
+
// Replace background-position:0; with background-position:0 0;
|
71 |
+
$css = str_replace("background-position:0;", "background-position:0 0;", $css);
|
72 |
+
|
73 |
+
// Replace 0.6 to .6, but only when preceded by : or a white-space
|
74 |
+
$css = preg_replace("@(:|\\s)0+\\.(\\d+)@", "$1.$2", $css);
|
75 |
+
|
76 |
+
// Shorten colors from rgb(51,102,153) to #336699
|
77 |
+
// This makes it more likely that it'll get further compressed in the next step.
|
78 |
+
$css = preg_replace_callback("@rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)@", array($this, '_shortenRgbCB'), $css);
|
79 |
+
|
80 |
+
// Shorten colors from #AABBCC to #ABC. Note that we want to make sure
|
81 |
+
// the color is not preceded by either ", " or =. Indeed, the property
|
82 |
+
// filter: chroma(color="#FFFFFF");
|
83 |
+
// would become
|
84 |
+
// filter: chroma(color="#FFF");
|
85 |
+
// which makes the filter break in IE.
|
86 |
+
$css = preg_replace_callback("@([^\"'=\\s])(\\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])@", array($this, '_shortenHexCB'), $css);
|
87 |
+
|
88 |
+
// Remove empty rules.
|
89 |
+
$css = preg_replace("@[^\\}]+\\{;\\}@", "", $css);
|
90 |
+
|
91 |
+
$linebreakpos = isset($this->_options['linebreakpos'])
|
92 |
+
? $this->_options['linebreakpos']
|
93 |
+
: 0;
|
94 |
+
|
95 |
+
if ($linebreakpos > 0) {
|
96 |
+
// Some source control tools don't like it when files containing lines longer
|
97 |
+
// than, say 8000 characters, are checked in. The linebreak option is used in
|
98 |
+
// that case to split long lines after a specific column.
|
99 |
+
$i = 0;
|
100 |
+
$linestartpos = 0;
|
101 |
+
$sb = $css;
|
102 |
+
|
103 |
+
// make sure strlen returns byte count
|
104 |
+
$mbIntEnc = null;
|
105 |
+
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
|
106 |
+
$mbIntEnc = mb_internal_encoding();
|
107 |
+
mb_internal_encoding('8bit');
|
108 |
+
}
|
109 |
+
$sbLength = strlen($css);
|
110 |
+
while ($i < $sbLength) {
|
111 |
+
$c = $sb[$i++];
|
112 |
+
if ($c === '}' && $i - $linestartpos > $linebreakpos) {
|
113 |
+
$sb = substr_replace($sb, "\n", $i, 0);
|
114 |
+
$sbLength++;
|
115 |
+
$linestartpos = $i;
|
116 |
+
}
|
117 |
+
}
|
118 |
+
$css = $sb;
|
119 |
+
|
120 |
+
// undo potential mb_encoding change
|
121 |
+
if ($mbIntEnc !== null) {
|
122 |
+
mb_internal_encoding($mbIntEnc);
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
// Replace the pseudo class for the Box Model Hack
|
127 |
+
$css = str_replace("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"", $css);
|
128 |
+
|
129 |
+
// Replace multiple semi-colons in a row by a single one
|
130 |
+
// See SF bug #1980989
|
131 |
+
$css = preg_replace("@;;+@", ";", $css);
|
132 |
+
|
133 |
+
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
|
134 |
+
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
|
135 |
+
|
136 |
+
// Trim the final string (for any leading or trailing white spaces)
|
137 |
+
$css = trim($css);
|
138 |
+
|
139 |
+
return $css;
|
140 |
+
}
|
141 |
+
|
142 |
+
protected function _removeSpacesCB($m)
|
143 |
+
{
|
144 |
+
return str_replace(':', '___PSEUDOCLASSCOLON___', $m[0]);
|
145 |
+
}
|
146 |
+
|
147 |
+
protected function _shortenRgbCB($m)
|
148 |
+
{
|
149 |
+
$rgbcolors = explode(',', $m[1]);
|
150 |
+
$hexcolor = '#';
|
151 |
+
for ($i = 0; $i < count($rgbcolors); $i++) {
|
152 |
+
$val = round($rgbcolors[$i]);
|
153 |
+
if ($val < 16) {
|
154 |
+
$hexcolor .= '0';
|
155 |
+
}
|
156 |
+
$hexcolor .= dechex($val);
|
157 |
+
}
|
158 |
+
return $hexcolor;
|
159 |
+
}
|
160 |
+
|
161 |
+
protected function _shortenHexCB($m)
|
162 |
+
{
|
163 |
+
// Test for AABBCC pattern
|
164 |
+
if ((strtolower($m[3])===strtolower($m[4])) &&
|
165 |
+
(strtolower($m[5])===strtolower($m[6])) &&
|
166 |
+
(strtolower($m[7])===strtolower($m[8]))) {
|
167 |
+
return $m[1] . $m[2] . "#" . $m[3] . $m[5] . $m[7];
|
168 |
+
} else {
|
169 |
+
return $m[0];
|
170 |
+
}
|
171 |
+
}
|
172 |
+
}
|
lib/Minify/Minify/YUICompressor.php
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Class Minify_YUICompressor
|
4 |
* @package Minify
|
@@ -31,167 +32,167 @@
|
|
31 |
*/
|
32 |
class Minify_YUICompressor {
|
33 |
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
|
129 |
$javaExecutable = self::$javaExecutable;
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
}
|
1 |
<?php
|
2 |
+
namespace W3TCL\Minify;
|
3 |
/**
|
4 |
* Class Minify_YUICompressor
|
5 |
* @package Minify
|
32 |
*/
|
33 |
class Minify_YUICompressor {
|
34 |
|
35 |
+
/**
|
36 |
+
* Filepath of the YUI Compressor jar file. This must be set before
|
37 |
+
* calling minifyJs() or minifyCss().
|
38 |
+
*
|
39 |
+
* @var string
|
40 |
+
*/
|
41 |
+
public static $jarFile = null;
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Writable temp directory. This must be set before calling minifyJs()
|
45 |
+
* or minifyCss().
|
46 |
+
*
|
47 |
+
* @var string
|
48 |
+
*/
|
49 |
+
public static $tempDir = null;
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Filepath of "java" executable (may be needed if not in shell's PATH)
|
53 |
+
*
|
54 |
+
* @var string
|
55 |
+
*/
|
56 |
+
public static $javaExecutable = 'java';
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Minify a Javascript string
|
60 |
+
*
|
61 |
+
* @param string $js
|
62 |
+
*
|
63 |
+
* @param array $options (verbose is ignored)
|
64 |
+
*
|
65 |
+
* @see http://www.julienlecomte.net/yuicompressor/README
|
66 |
+
*
|
67 |
+
* @return string
|
68 |
+
*/
|
69 |
+
public static function minifyJs($js, $options = array())
|
70 |
+
{
|
71 |
+
return self::_minify('js', $js, $options);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* Minify a CSS string
|
76 |
+
*
|
77 |
+
* @param string $css
|
78 |
+
*
|
79 |
+
* @param array $options (verbose is ignored)
|
80 |
+
*
|
81 |
+
* @see http://www.julienlecomte.net/yuicompressor/README
|
82 |
+
*
|
83 |
+
* @return string
|
84 |
+
*/
|
85 |
+
public static function minifyCss($css, $options = array())
|
86 |
+
{
|
87 |
+
$css = self::_minify('css', $css, $options);
|
88 |
+
$css = Minify_CSS_UriRewriter::rewrite($css, $options);
|
89 |
+
|
90 |
+
return $css;
|
91 |
+
}
|
92 |
+
|
93 |
+
private static function _minify($type, $content, $options)
|
94 |
+
{
|
95 |
+
self::_prepare();
|
96 |
+
if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
|
97 |
+
throw new \Exception('Minify_YUICompressor : could not create temp file.');
|
98 |
+
}
|
99 |
+
file_put_contents($tmpFile, $content);
|
100 |
+
exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code);
|
101 |
+
unlink($tmpFile);
|
102 |
+
if ($result_code != 0) {
|
103 |
+
throw new \Exception('Minify_YUICompressor : YUI compressor execution failed.');
|
104 |
+
}
|
105 |
+
return implode("\n", $output);
|
106 |
+
}
|
107 |
+
|
108 |
+
private static function _getCmd($userOptions, $type, $tmpFile)
|
109 |
+
{
|
110 |
+
if (!is_file(self::$javaExecutable)) {
|
111 |
+
throw new \Exception(sprintf('JAVA executable (%s) is not a valid file.', self::$javaExecutable));
|
112 |
+
}
|
113 |
+
|
114 |
+
if (!is_file(self::$jarFile)) {
|
115 |
+
throw new \Exception(sprintf('JAR file (%s) is not a valid file.', self::$jarFile));
|
116 |
+
}
|
117 |
+
$o = array_merge(
|
118 |
+
array(
|
119 |
+
'charset' => ''
|
120 |
+
,'line-break' => 5000
|
121 |
+
,'type' => $type
|
122 |
+
,'nomunge' => false
|
123 |
+
,'preserve-semi' => false
|
124 |
+
,'disable-optimizations' => false
|
125 |
+
,'stack-size' => ''
|
126 |
+
)
|
127 |
+
,$userOptions
|
128 |
+
);
|
129 |
|
130 |
$javaExecutable = self::$javaExecutable;
|
131 |
+
if ( false !== strpos(trim($javaExecutable), ' ') ) {
|
132 |
+
$javaExecutable = '"'. $javaExecutable . '"';
|
133 |
+
}
|
134 |
+
|
135 |
+
$cmd = $javaExecutable
|
136 |
+
. (!empty($o['stack-size'])
|
137 |
+
? ' -Xss' . $o['stack-size']
|
138 |
+
: '')
|
139 |
+
. ' -jar ' . escapeshellarg(self::$jarFile)
|
140 |
+
. " --type {$type}"
|
141 |
+
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
|
142 |
+
? " --charset {$o['charset']}"
|
143 |
+
: '')
|
144 |
+
. (is_numeric($o['line-break']) && $o['line-break'] >= 0
|
145 |
+
? ' --line-break ' . (int)$o['line-break']
|
146 |
+
: '');
|
147 |
+
if ($type === 'js') {
|
148 |
+
foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) {
|
149 |
+
$cmd .= $o[$opt]
|
150 |
+
? " --{$opt}"
|
151 |
+
: '';
|
152 |
+
}
|
153 |
+
}
|
154 |
+
return $cmd . ' ' . escapeshellarg($tmpFile);
|
155 |
+
}
|
156 |
+
|
157 |
+
private static function _prepare()
|
158 |
+
{
|
159 |
+
if (! is_file(self::$jarFile)) {
|
160 |
+
throw new \Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
|
161 |
+
}
|
162 |
+
if (! is_readable(self::$jarFile)) {
|
163 |
+
throw new \Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.');
|
164 |
+
}
|
165 |
+
if (! is_dir(self::$tempDir)) {
|
166 |
+
throw new \Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
167 |
+
}
|
168 |
+
if (! is_writable(self::$tempDir)) {
|
169 |
+
throw new \Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.');
|
170 |
+
}
|
171 |
+
}
|
172 |
+
|
173 |
+
public static function testJs(&$error) {
|
174 |
+
try {
|
175 |
+
Minify_YUICompressor::minifyJs('alert("ok");');
|
176 |
+
$error = 'OK';
|
177 |
+
|
178 |
+
return true;
|
179 |
+
} catch (\Exception $exception) {
|
180 |
+
$error = $exception->getMessage();
|
181 |
+
|
182 |
+
return false;
|
183 |
+
}
|
184 |
+
}
|
185 |
+
|
186 |
+
public static function testCss(&$error) {
|
187 |
+
try {
|
188 |
+
Minify_YUICompressor::minifyCss('p{color:red}');
|
189 |
+
$error = 'OK';
|
190 |
+
|
191 |
+
return true;
|
192 |
+
} catch (\Exception $exception) {
|
193 |
+
$error = $exception->getMessage();
|
194 |
+
|
195 |
+
return false;
|
196 |
+
}
|
197 |
+
}
|
198 |
}
|
lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Colors.php
DELETED
@@ -1,155 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace w3tc_tubalmartin\CssMin;
|
4 |
-
|
5 |
-
class Colors
|
6 |
-
{
|
7 |
-
public static function getHexToNamedMap()
|
8 |
-
{
|
9 |
-
// Hex colors longer than named counterpart
|
10 |
-
return array(
|
11 |
-
'#f0ffff' => 'azure',
|
12 |
-
'#f5f5dc' => 'beige',
|
13 |
-
'#ffe4c4' => 'bisque',
|
14 |
-
'#a52a2a' => 'brown',
|
15 |
-
'#ff7f50' => 'coral',
|
16 |
-
'#ffd700' => 'gold',
|
17 |
-
'#808080' => 'gray',
|
18 |
-
'#008000' => 'green',
|
19 |
-
'#4b0082' => 'indigo',
|
20 |
-
'#fffff0' => 'ivory',
|
21 |
-
'#f0e68c' => 'khaki',
|
22 |
-
'#faf0e6' => 'linen',
|
23 |
-
'#800000' => 'maroon',
|
24 |
-
'#000080' => 'navy',
|
25 |
-
'#fdf5e6' => 'oldlace',
|
26 |
-
'#808000' => 'olive',
|
27 |
-
'#ffa500' => 'orange',
|
28 |
-
'#da70d6' => 'orchid',
|
29 |
-
'#cd853f' => 'peru',
|
30 |
-
'#ffc0cb' => 'pink',
|
31 |
-
'#dda0dd' => 'plum',
|
32 |
-
'#800080' => 'purple',
|
33 |
-
'#f00' => 'red',
|
34 |
-
'#fa8072' => 'salmon',
|
35 |
-
'#a0522d' => 'sienna',
|
36 |
-
'#c0c0c0' => 'silver',
|
37 |
-
'#fffafa' => 'snow',
|
38 |
-
'#d2b48c' => 'tan',
|
39 |
-
'#008080' => 'teal',
|
40 |
-
'#ff6347' => 'tomato',
|
41 |
-
'#ee82ee' => 'violet',
|
42 |
-
'#f5deb3' => 'wheat'
|
43 |
-
);
|
44 |
-
}
|
45 |
-
|
46 |
-
public static function getNamedToHexMap()
|
47 |
-
{
|
48 |
-
// Named colors longer than hex counterpart
|
49 |
-
return array(
|
50 |
-
'aliceblue' => '#f0f8ff',
|
51 |
-
'antiquewhite' => '#faebd7',
|
52 |
-
'aquamarine' => '#7fffd4',
|
53 |
-
'black' => '#000',
|
54 |
-
'blanchedalmond' => '#ffebcd',
|
55 |
-
'blueviolet' => '#8a2be2',
|
56 |
-
'burlywood' => '#deb887',
|
57 |
-
'cadetblue' => '#5f9ea0',
|
58 |
-
'chartreuse' => '#7fff00',
|
59 |
-
'chocolate' => '#d2691e',
|
60 |
-
'cornflowerblue' => '#6495ed',
|
61 |
-
'cornsilk' => '#fff8dc',
|
62 |
-
'darkblue' => '#00008b',
|
63 |
-
'darkcyan' => '#008b8b',
|
64 |
-
'darkgoldenrod' => '#b8860b',
|
65 |
-
'darkgray' => '#a9a9a9',
|
66 |
-
'darkgreen' => '#006400',
|
67 |
-
'darkgrey' => '#a9a9a9',
|
68 |
-
'darkkhaki' => '#bdb76b',
|
69 |
-
'darkmagenta' => '#8b008b',
|
70 |
-
'darkolivegreen' => '#556b2f',
|
71 |
-
'darkorange' => '#ff8c00',
|
72 |
-
'darkorchid' => '#9932cc',
|
73 |
-
'darksalmon' => '#e9967a',
|
74 |
-
'darkseagreen' => '#8fbc8f',
|
75 |
-
'darkslateblue' => '#483d8b',
|
76 |
-
'darkslategray' => '#2f4f4f',
|
77 |
-
'darkslategrey' => '#2f4f4f',
|
78 |
-
'darkturquoise' => '#00ced1',
|
79 |
-
'darkviolet' => '#9400d3',
|
80 |
-
'deeppink' => '#ff1493',
|
81 |
-
'deepskyblue' => '#00bfff',
|
82 |
-
'dodgerblue' => '#1e90ff',
|
83 |
-
'firebrick' => '#b22222',
|
84 |
-
'floralwhite' => '#fffaf0',
|
85 |
-
'forestgreen' => '#228b22',
|
86 |
-
'fuchsia' => '#f0f',
|
87 |
-
'gainsboro' => '#dcdcdc',
|
88 |
-
'ghostwhite' => '#f8f8ff',
|
89 |
-
'goldenrod' => '#daa520',
|
90 |
-
'greenyellow' => '#adff2f',
|
91 |
-
'honeydew' => '#f0fff0',
|
92 |
-
'indianred' => '#cd5c5c',
|
93 |
-
'lavender' => '#e6e6fa',
|
94 |
-
'lavenderblush' => '#fff0f5',
|
95 |
-
'lawngreen' => '#7cfc00',
|
96 |
-
'lemonchiffon' => '#fffacd',
|
97 |
-
'lightblue' => '#add8e6',
|
98 |
-
'lightcoral' => '#f08080',
|
99 |
-
'lightcyan' => '#e0ffff',
|
100 |
-
'lightgoldenrodyellow' => '#fafad2',
|
101 |
-
'lightgray' => '#d3d3d3',
|
102 |
-
'lightgreen' => '#90ee90',
|
103 |
-
'lightgrey' => '#d3d3d3',
|
104 |
-
'lightpink' => '#ffb6c1',
|
105 |
-
'lightsalmon' => '#ffa07a',
|
106 |
-
'lightseagreen' => '#20b2aa',
|
107 |
-
'lightskyblue' => '#87cefa',
|
108 |
-
'lightslategray' => '#778899',
|
109 |
-
'lightslategrey' => '#778899',
|
110 |
-
'lightsteelblue' => '#b0c4de',
|
111 |
-
'lightyellow' => '#ffffe0',
|
112 |
-
'limegreen' => '#32cd32',
|
113 |
-
'mediumaquamarine' => '#66cdaa',
|
114 |
-
'mediumblue' => '#0000cd',
|
115 |
-
'mediumorchid' => '#ba55d3',
|
116 |
-
'mediumpurple' => '#9370db',
|
117 |
-
'mediumseagreen' => '#3cb371',
|
118 |
-
'mediumslateblue' => '#7b68ee',
|
119 |
-
'mediumspringgreen' => '#00fa9a',
|
120 |
-
'mediumturquoise' => '#48d1cc',
|
121 |
-
'mediumvioletred' => '#c71585',
|
122 |
-
'midnightblue' => '#191970',
|
123 |
-
'mintcream' => '#f5fffa',
|
124 |
-
'mistyrose' => '#ffe4e1',
|
125 |
-
'moccasin' => '#ffe4b5',
|
126 |
-
'navajowhite' => '#ffdead',
|
127 |
-
'olivedrab' => '#6b8e23',
|
128 |
-
'orangered' => '#ff4500',
|
129 |
-
'palegoldenrod' => '#eee8aa',
|
130 |
-
'palegreen' => '#98fb98',
|
131 |
-
'paleturquoise' => '#afeeee',
|
132 |
-
'palevioletred' => '#db7093',
|
133 |
-
'papayawhip' => '#ffefd5',
|
134 |
-
'peachpuff' => '#ffdab9',
|
135 |
-
'powderblue' => '#b0e0e6',
|
136 |
-
'rebeccapurple' => '#663399',
|
137 |
-
'rosybrown' => '#bc8f8f',
|
138 |
-
'royalblue' => '#4169e1',
|
139 |
-
'saddlebrown' => '#8b4513',
|
140 |
-
'sandybrown' => '#f4a460',
|
141 |
-
'seagreen' => '#2e8b57',
|
142 |
-
'seashell' => '#fff5ee',
|
143 |
-
'slateblue' => '#6a5acd',
|
144 |
-
'slategray' => '#708090',
|
145 |
-
'slategrey' => '#708090',
|
146 |
-
'springgreen' => '#00ff7f',
|
147 |
-
'steelblue' => '#4682b4',
|
148 |
-
'turquoise' => '#40e0d0',
|
149 |
-
'white' => '#fff',
|
150 |
-
'whitesmoke' => '#f5f5f5',
|
151 |
-
'yellow' => '#ff0',
|
152 |
-
'yellowgreen' => '#9acd32'
|
153 |
-
);
|
154 |
-
}
|
155 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Command.php
DELETED
@@ -1,223 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace w3tc_tubalmartin\CssMin;
|
4 |
-
|
5 |
-
class Command
|
6 |
-
{
|
7 |
-
const SUCCESS_EXIT = 0;
|
8 |
-
const FAILURE_EXIT = 1;
|
9 |
-
|
10 |
-
protected $stats = array();
|
11 |
-
|
12 |
-
public static function main()
|
13 |
-
{
|
14 |
-
$command = new self;
|
15 |
-
$command->run();
|
16 |
-
}
|
17 |
-
|
18 |
-
public function run()
|
19 |
-
{
|
20 |
-
$opts = getopt(
|
21 |
-
'hi:o:',
|
22 |
-
array(
|
23 |
-
'help',
|
24 |
-
'input:',
|
25 |
-
'output:',
|
26 |
-
'dry-run',
|
27 |
-
'keep-sourcemap',
|
28 |
-
'keep-sourcemap-comment',
|
29 |
-
'linebreak-position:',
|
30 |
-
'memory-limit:',
|
31 |
-
'pcre-backtrack-limit:',
|
32 |
-
'pcre-recursion-limit:',
|
33 |
-
'remove-important-comments'
|
34 |
-
)
|
35 |
-
);
|
36 |
-
|
37 |
-
$help = $this->getOpt(array('h', 'help'), $opts);
|
38 |
-
$input = $this->getOpt(array('i', 'input'), $opts);
|
39 |
-
$output = $this->getOpt(array('o', 'output'), $opts);
|
40 |
-
$dryrun = $this->getOpt('dry-run', $opts);
|
41 |
-
$keepSourceMapComment = $this->getOpt(array('keep-sourcemap', 'keep-sourcemap-comment'), $opts);
|
42 |
-
$linebreakPosition = $this->getOpt('linebreak-position', $opts);
|
43 |
-
$memoryLimit = $this->getOpt('memory-limit', $opts);
|
44 |
-
$backtrackLimit = $this->getOpt('pcre-backtrack-limit', $opts);
|
45 |
-
$recursionLimit = $this->getOpt('pcre-recursion-limit', $opts);
|
46 |
-
$removeImportantComments = $this->getOpt('remove-important-comments', $opts);
|
47 |
-
|
48 |
-
if (!is_null($help)) {
|
49 |
-
$this->showHelp();
|
50 |
-
die(self::SUCCESS_EXIT);
|
51 |
-
}
|
52 |
-
|
53 |
-
if (is_null($input)) {
|
54 |
-
fwrite(STDERR, '-i <file> argument is missing' . PHP_EOL);
|
55 |
-
$this->showHelp();
|
56 |
-
die(self::FAILURE_EXIT);
|
57 |
-
}
|
58 |
-
|
59 |
-
if (!is_readable($input)) {
|
60 |
-
fwrite(STDERR, 'Input file is not readable' . PHP_EOL);
|
61 |
-
die(self::FAILURE_EXIT);
|
62 |
-
}
|
63 |
-
|
64 |
-
$css = file_get_contents($input);
|
65 |
-
|
66 |
-
if ($css === false) {
|
67 |
-
fwrite(STDERR, 'Input CSS code could not be retrieved from input file' . PHP_EOL);
|
68 |
-
die(self::FAILURE_EXIT);
|
69 |
-
}
|
70 |
-
|
71 |
-
$this->setStat('original-size', strlen($css));
|
72 |
-
|
73 |
-
$cssmin = new Minifier;
|
74 |
-
|
75 |
-
if (!is_null($keepSourceMapComment)) {
|
76 |
-
$cssmin->keepSourceMapComment();
|
77 |
-
}
|
78 |
-
|
79 |
-
if (!is_null($removeImportantComments)) {
|
80 |
-
$cssmin->removeImportantComments();
|
81 |
-
}
|
82 |
-
|
83 |
-
if (!is_null($linebreakPosition)) {
|
84 |
-
$cssmin->setLineBreakPosition($linebreakPosition);
|
85 |
-
}
|
86 |
-
|
87 |
-
if (!is_null($memoryLimit)) {
|
88 |
-
$cssmin->setMemoryLimit($memoryLimit);
|
89 |
-
}
|
90 |
-
|
91 |
-
if (!is_null($backtrackLimit)) {
|
92 |
-
$cssmin->setPcreBacktrackLimit($backtrackLimit);
|
93 |
-
}
|
94 |
-
|
95 |
-
if (!is_null($recursionLimit)) {
|
96 |
-
$cssmin->setPcreRecursionLimit($recursionLimit);
|
97 |
-
}
|
98 |
-
|
99 |
-
$this->setStat('compression-time-start', microtime(true));
|
100 |
-
|
101 |
-
$css = $cssmin->run($css);
|
102 |
-
|
103 |
-
$this->setStat('compression-time-end', microtime(true));
|
104 |
-
$this->setStat('peak-memory-usage', memory_get_peak_usage(true));
|
105 |
-
$this->setStat('compressed-size', strlen($css));
|
106 |
-
|
107 |
-
if (!is_null($dryrun)) {
|
108 |
-
$this->showStats();
|
109 |
-
die(self::SUCCESS_EXIT);
|
110 |
-
}
|
111 |
-
|
112 |
-
if (is_null($output)) {
|
113 |
-
fwrite(STDOUT, $css . PHP_EOL);
|
114 |
-
$this->showStats();
|
115 |
-
die(self::SUCCESS_EXIT);
|
116 |
-
}
|
117 |
-
|
118 |
-
if (!is_writable(dirname($output))) {
|
119 |
-
fwrite(STDERR, 'Output file is not writable' . PHP_EOL);
|
120 |
-
die(self::FAILURE_EXIT);
|
121 |
-
}
|
122 |
-
|
123 |
-
if (file_put_contents($output, $css) === false) {
|
124 |
-
fwrite(STDERR, 'Compressed CSS code could not be saved to output file' . PHP_EOL);
|
125 |
-
die(self::FAILURE_EXIT);
|
126 |
-
}
|
127 |
-
|
128 |
-
$this->showStats();
|
129 |
-
|
130 |
-
die(self::SUCCESS_EXIT);
|
131 |
-
}
|
132 |
-
|
133 |
-
protected function getOpt($opts, $options)
|
134 |
-
{
|
135 |
-
$value = null;
|
136 |
-
|
137 |
-
if (is_string($opts)) {
|
138 |
-
$opts = array($opts);
|
139 |
-
}
|
140 |
-
|
141 |
-
foreach ($opts as $opt) {
|
142 |
-
if (array_key_exists($opt, $options)) {
|
143 |
-
$value = $options[$opt];
|
144 |
-
break;
|
145 |
-
}
|
146 |
-
}
|
147 |
-
|
148 |
-
return $value;
|
149 |
-
}
|
150 |
-
|
151 |
-
protected function setStat($statName, $statValue)
|
152 |
-
{
|
153 |
-
$this->stats[$statName] = $statValue;
|
154 |
-
}
|
155 |
-
|
156 |
-
protected function formatBytes($size, $precision = 2)
|
157 |
-
{
|
158 |
-
$base = log($size, 1024);
|
159 |
-
$suffixes = array('B', 'K', 'M', 'G', 'T');
|
160 |
-
return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)];
|
161 |
-
}
|
162 |
-
|
163 |
-
protected function formatMicroSeconds($microSecs, $precision = 2)
|
164 |
-
{
|
165 |
-
// ms
|
166 |
-
$time = round($microSecs * 1000, $precision);
|
167 |
-
|
168 |
-
if ($time >= 60 * 1000) {
|
169 |
-
$time = round($time / 60 * 1000, $precision) .' m'; // m
|
170 |
-
} elseif ($time >= 1000) {
|
171 |
-
$time = round($time / 1000, $precision) .' s'; // s
|
172 |
-
} else {
|
173 |
-
$time .= ' ms';
|
174 |
-
}
|
175 |
-
|
176 |
-
return $time;
|
177 |
-
}
|
178 |
-
|
179 |
-
protected function showStats()
|
180 |
-
{
|
181 |
-
$spaceSavings = round((1 - ($this->stats['compressed-size'] / $this->stats['original-size'])) * 100, 2);
|
182 |
-
$compressionRatio = round($this->stats['original-size'] / $this->stats['compressed-size'], 2);
|
183 |
-
$compressionTime = $this->formatMicroSeconds(
|
184 |
-
$this->stats['compression-time-end'] - $this->stats['compression-time-start']
|
185 |
-
);
|
186 |
-
$peakMemoryUsage = $this->formatBytes($this->stats['peak-memory-usage']);
|
187 |
-
|
188 |
-
print <<<EOT
|
189 |
-
|
190 |
-
------------------------------
|
191 |
-
CSSMIN STATS
|
192 |
-
------------------------------
|
193 |
-
Space savings: {$spaceSavings} %
|
194 |
-
Compression ratio: {$compressionRatio}:1
|
195 |
-
Compression time: $compressionTime
|
196 |
-
Peak memory usage: $peakMemoryUsage
|
197 |
-
|
198 |
-
|
199 |
-
EOT;
|
200 |
-
}
|
201 |
-
|
202 |
-
protected function showHelp()
|
203 |
-
{
|
204 |
-
print <<<'EOT'
|
205 |
-
Usage: cssmin [options] -i <file> [-o <file>]
|
206 |
-
|
207 |
-
-i|--input <file> File containing uncompressed CSS code.
|
208 |
-
-o|--output <file> File to use to save compressed CSS code.
|
209 |
-
|
210 |
-
Options:
|
211 |
-
|
212 |
-
-h|--help Prints this usage information.
|
213 |
-
--dry-run Performs a dry run displaying statistics.
|
214 |
-
--keep-sourcemap[-comment] Keeps the sourcemap special comment in the output.
|
215 |
-
--linebreak-position <pos> Splits long lines after a specific column in the output.
|
216 |
-
--memory-limit <limit> Sets the memory limit for this script.
|
217 |
-
--pcre-backtrack-limit <limit> Sets the PCRE backtrack limit for this script.
|
218 |
-
--pcre-recursion-limit <limit> Sets the PCRE recursion limit for this script.
|
219 |
-
--remove-important-comments Removes !important comments from output.
|
220 |
-
|
221 |
-
EOT;
|
222 |
-
}
|
223 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Minifier.php
DELETED
@@ -1,862 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/*!
|
4 |
-
* CssMin
|
5 |
-
* Author: Tubal Martin - http://tubalmartin.me/
|
6 |
-
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
|
7 |
-
*
|
8 |
-
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
|
9 |
-
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
|
10 |
-
* Permission is hereby granted to use the PHP version under the same
|
11 |
-
* conditions as the YUICompressor.
|
12 |
-
*/
|
13 |
-
|
14 |
-
/*!
|
15 |
-
* YUI Compressor
|
16 |
-
* http://developer.yahoo.com/yui/compressor/
|
17 |
-
* Author: Julien Lecomte - http://www.julienlecomte.net/
|
18 |
-
* Copyright (c) 2013 Yahoo! Inc. All rights reserved.
|
19 |
-
* The copyrights embodied in the content of this file are licensed
|
20 |
-
* by Yahoo! Inc. under the BSD (revised) open source license.
|
21 |
-
*/
|
22 |
-
|
23 |
-
namespace w3tc_tubalmartin\CssMin;
|
24 |
-
|
25 |
-
class Minifier
|
26 |
-
{
|
27 |
-
const QUERY_FRACTION = '_CSSMIN_QF_';
|
28 |
-
const COMMENT_TOKEN = '_CSSMIN_CMT_%d_';
|
29 |
-
const COMMENT_TOKEN_START = '_CSSMIN_CMT_';
|
30 |
-
const RULE_BODY_TOKEN = '_CSSMIN_RBT_%d_';
|
31 |
-
const PRESERVED_TOKEN = '_CSSMIN_PTK_%d_';
|
32 |
-
|
33 |
-
// Token lists
|
34 |
-
private $comments = array();
|
35 |
-
private $ruleBodies = array();
|
36 |
-
private $preservedTokens = array();
|
37 |
-
|
38 |
-
// Output options
|
39 |
-
private $keepImportantComments = true;
|
40 |
-
private $keepSourceMapComment = false;
|
41 |
-
private $linebreakPosition = 0;
|
42 |
-
|
43 |
-
// PHP ini limits
|
44 |
-
private $raisePhpLimits;
|
45 |
-
private $memoryLimit;
|
46 |
-
private $maxExecutionTime = 60; // 1 min
|
47 |
-
private $pcreBacktrackLimit;
|
48 |
-
private $pcreRecursionLimit;
|
49 |
-
|
50 |
-
// Color maps
|
51 |
-
private $hexToNamedColorsMap;
|
52 |
-
private $namedToHexColorsMap;
|
53 |
-
|
54 |
-
// Regexes
|
55 |
-
private $numRegex;
|
56 |
-
private $charsetRegex = '/@charset [^;]+;/Si';
|
57 |
-
private $importRegex = '/@import [^;]+;/Si';
|
58 |
-
private $namespaceRegex = '/@namespace [^;]+;/Si';
|
59 |
-
private $namedToHexColorsRegex;
|
60 |
-
private $shortenOneZeroesRegex;
|
61 |
-
private $shortenTwoZeroesRegex;
|
62 |
-
private $shortenThreeZeroesRegex;
|
63 |
-
private $shortenFourZeroesRegex;
|
64 |
-
private $unitsGroupRegex = '(?:ch|cm|em|ex|gd|in|mm|px|pt|pc|q|rem|vh|vmax|vmin|vw|%)';
|
65 |
-
|
66 |
-
/**
|
67 |
-
* @param bool|int $raisePhpLimits If true, PHP settings will be raised if needed
|
68 |
-
*/
|
69 |
-
public function __construct($raisePhpLimits = true)
|
70 |
-
{
|
71 |
-
$this->raisePhpLimits = (bool) $raisePhpLimits;
|
72 |
-
$this->memoryLimit = 128 * 1048576; // 128MB in bytes
|
73 |
-
$this->pcreBacktrackLimit = 1000 * 1000;
|
74 |
-
$this->pcreRecursionLimit = 500 * 1000;
|
75 |
-
$this->hexToNamedColorsMap = Colors::getHexToNamedMap();
|
76 |
-
$this->namedToHexColorsMap = Colors::getNamedToHexMap();
|
77 |
-
$this->namedToHexColorsRegex = sprintf(
|
78 |
-
'/([:,( ])(%s)( |,|\)|;|$)/Si',
|
79 |
-
implode('|', array_keys($this->namedToHexColorsMap))
|
80 |
-
);
|
81 |
-
$this->numRegex = sprintf('-?\d*\.?\d+%s?', $this->unitsGroupRegex);
|
82 |
-
$this->setShortenZeroValuesRegexes();
|
83 |
-
}
|
84 |
-
|
85 |
-
/**
|
86 |
-
* Parses & minifies the given input CSS string
|
87 |
-
* @param string $css
|
88 |
-
* @return string
|
89 |
-
*/
|
90 |
-
public function run($css = '')
|
91 |
-
{
|
92 |
-
if (empty($css) || !is_string($css)) {
|
93 |
-
return '';
|
94 |
-
}
|
95 |
-
|
96 |
-
$this->resetRunProperties();
|
97 |
-
|
98 |
-
if ($this->raisePhpLimits) {
|
99 |
-
$this->doRaisePhpLimits();
|
100 |
-
}
|
101 |
-
|
102 |
-
return $this->minify($css);
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* Sets whether to keep or remove sourcemap special comment.
|
107 |
-
* Sourcemap comments are removed by default.
|
108 |
-
* @param bool $keepSourceMapComment
|
109 |
-
*/
|
110 |
-
public function keepSourceMapComment($keepSourceMapComment = true)
|
111 |
-
{
|
112 |
-
$this->keepSourceMapComment = (bool) $keepSourceMapComment;
|
113 |
-
}
|
114 |
-
|
115 |
-
/**
|
116 |
-
* Sets whether to keep or remove important comments.
|
117 |
-
* Important comments outside of a declaration block are kept by default.
|
118 |
-
* @param bool $removeImportantComments
|
119 |
-
*/
|
120 |
-
public function removeImportantComments($removeImportantComments = true)
|
121 |
-
{
|
122 |
-
$this->keepImportantComments = !(bool) $removeImportantComments;
|
123 |
-
}
|
124 |
-
|
125 |
-
/**
|
126 |
-
* Sets the approximate column after which long lines will be splitted in the output
|
127 |
-
* with a linebreak.
|
128 |
-
* @param int $position
|
129 |
-
*/
|
130 |
-
public function setLineBreakPosition($position)
|
131 |
-
{
|
132 |
-
$this->linebreakPosition = (int) $position;
|
133 |
-
}
|
134 |
-
|
135 |
-
/**
|
136 |
-
* Sets the memory limit for this script
|
137 |
-
* @param int|string $limit
|
138 |
-
*/
|
139 |
-
public function setMemoryLimit($limit)
|
140 |
-
{
|
141 |
-
$this->memoryLimit = Utils::normalizeInt($limit);
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Sets the maximum execution time for this script
|
146 |
-
* @param int|string $seconds
|
147 |
-
*/
|
148 |
-
public function setMaxExecutionTime($seconds)
|
149 |
-
{
|
150 |
-
$this->maxExecutionTime = (int) $seconds;
|
151 |
-
}
|
152 |
-
|
153 |
-
/**
|
154 |
-
* Sets the PCRE backtrack limit for this script
|
155 |
-
* @param int $limit
|
156 |
-
*/
|
157 |
-
public function setPcreBacktrackLimit($limit)
|
158 |
-
{
|
159 |
-
$this->pcreBacktrackLimit = (int) $limit;
|
160 |
-
}
|
161 |
-
|
162 |
-
/**
|
163 |
-
* Sets the PCRE recursion limit for this script
|
164 |
-
* @param int $limit
|
165 |
-
*/
|
166 |
-
public function setPcreRecursionLimit($limit)
|
167 |
-
{
|
168 |
-
$this->pcreRecursionLimit = (int) $limit;
|
169 |
-
}
|
170 |
-
|
171 |
-
/**
|
172 |
-
* Builds regular expressions needed for shortening zero values
|
173 |
-
*/
|
174 |
-
private function setShortenZeroValuesRegexes()
|
175 |
-
{
|
176 |
-
$zeroRegex = '0'. $this->unitsGroupRegex;
|
177 |
-
$numOrPosRegex = '('. $this->numRegex .'|top|left|bottom|right|center) ';
|
178 |
-
$oneZeroSafeProperties = array(
|
179 |
-
'(?:line-)?height',
|
180 |
-
'(?:(?:min|max)-)?width',
|
181 |
-
'top',
|
182 |
-
'left',
|
183 |
-
'background-position',
|
184 |
-
'bottom',
|
185 |
-
'right',
|
186 |
-
'border(?:-(?:top|left|bottom|right))?(?:-width)?',
|
187 |
-
'border-(?:(?:top|bottom)-(?:left|right)-)?radius',
|
188 |
-
'column-(?:gap|width)',
|
189 |
-
'margin(?:-(?:top|left|bottom|right))?',
|
190 |
-
'outline-width',
|
191 |
-
'padding(?:-(?:top|left|bottom|right))?'
|
192 |
-
);
|
193 |
-
|
194 |
-
// First zero regex
|
195 |
-
$regex = '/(^|;)('. implode('|', $oneZeroSafeProperties) .'):%s/Si';
|
196 |
-
$this->shortenOneZeroesRegex = sprintf($regex, $zeroRegex);
|
197 |
-
|
198 |
-
// Multiple zeroes regexes
|
199 |
-
$regex = '/(^|;)(margin|padding|border-(?:width|radius)|background-position):%s/Si';
|
200 |
-
$this->shortenTwoZeroesRegex = sprintf($regex, $numOrPosRegex . $zeroRegex);
|
201 |
-
$this->shortenThreeZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $zeroRegex);
|
202 |
-
$this->shortenFourZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $numOrPosRegex . $zeroRegex);
|
203 |
-
}
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Resets properties whose value may change between runs
|
207 |
-
*/
|
208 |
-
private function resetRunProperties()
|
209 |
-
{
|
210 |
-
$this->comments = array();
|
211 |
-
$this->ruleBodies = array();
|
212 |
-
$this->preservedTokens = array();
|
213 |
-
}
|
214 |
-
|
215 |
-
/**
|
216 |
-
* Tries to configure PHP to use at least the suggested minimum settings
|
217 |
-
* @return void
|
218 |
-
*/
|
219 |
-
private function doRaisePhpLimits()
|
220 |
-
{
|
221 |
-
$phpLimits = array(
|
222 |
-
'memory_limit' => $this->memoryLimit,
|
223 |
-
'max_execution_time' => $this->maxExecutionTime,
|
224 |
-
'pcre.backtrack_limit' => $this->pcreBacktrackLimit,
|
225 |
-
'pcre.recursion_limit' => $this->pcreRecursionLimit
|
226 |
-
);
|
227 |
-
|
228 |
-
// If current settings are higher respect them.
|
229 |
-
foreach ($phpLimits as $name => $suggested) {
|
230 |
-
$current = Utils::normalizeInt(ini_get($name));
|
231 |
-
|
232 |
-
if ($current >= $suggested) {
|
233 |
-
continue;
|
234 |
-
}
|
235 |
-
|
236 |
-
// memoryLimit exception: allow -1 for "no memory limit".
|
237 |
-
if ($name === 'memory_limit' && $current === -1) {
|
238 |
-
continue;
|
239 |
-
}
|
240 |
-
|
241 |
-
// maxExecutionTime exception: allow 0 for "no memory limit".
|
242 |
-
if ($name === 'max_execution_time' && $current === 0) {
|
243 |
-
continue;
|
244 |
-
}
|
245 |
-
|
246 |
-
ini_set($name, $suggested);
|
247 |
-
}
|
248 |
-
}
|
249 |
-
|
250 |
-
/**
|
251 |
-
* Registers a preserved token
|
252 |
-
* @param string $token
|
253 |
-
* @return string The token ID string
|
254 |
-
*/
|
255 |
-
private function registerPreservedToken($token)
|
256 |
-
{
|
257 |
-
$tokenId = sprintf(self::PRESERVED_TOKEN, count($this->preservedTokens));
|
258 |
-
$this->preservedTokens[$tokenId] = $token;
|
259 |
-
return $tokenId;
|
260 |
-
}
|
261 |
-
|
262 |
-
/**
|
263 |
-
* Registers a candidate comment token
|
264 |
-
* @param string $comment
|
265 |
-
* @return string The comment token ID string
|
266 |
-
*/
|
267 |
-
private function registerCommentToken($comment)
|
268 |
-
{
|
269 |
-
$tokenId = sprintf(self::COMMENT_TOKEN, count($this->comments));
|
270 |
-
$this->comments[$tokenId] = $comment;
|
271 |
-
return $tokenId;
|
272 |
-
}
|
273 |
-
|
274 |
-
/**
|
275 |
-
* Registers a rule body token
|
276 |
-
* @param string $body the minified rule body
|
277 |
-
* @return string The rule body token ID string
|
278 |
-
*/
|
279 |
-
private function registerRuleBodyToken($body)
|
280 |
-
{
|
281 |
-
if (empty($body)) {
|
282 |
-
return '';
|
283 |
-
}
|
284 |
-
|
285 |
-
$tokenId = sprintf(self::RULE_BODY_TOKEN, count($this->ruleBodies));
|
286 |
-
$this->ruleBodies[$tokenId] = $body;
|
287 |
-
return $tokenId;
|
288 |
-
}
|
289 |
-
|
290 |
-
/**
|
291 |
-
* Parses & minifies the given input CSS string
|
292 |
-
* @param string $css
|
293 |
-
* @return string
|
294 |
-
*/
|
295 |
-
private function minify($css)
|
296 |
-
{
|
297 |
-
// Process data urls
|
298 |
-
$css = $this->processDataUrls($css);
|
299 |
-
|
300 |
-
// Process comments
|
301 |
-
$css = preg_replace_callback(
|
302 |
-
'/(?<!\\\\)\/\*(.*?)\*(?<!\\\\)\//Ss',
|
303 |
-
array($this, 'processCommentsCallback'),
|
304 |
-
$css
|
305 |
-
);
|
306 |
-
|
307 |
-
// IE7: Process Microsoft matrix filters (whitespaces between Matrix parameters). Can contain strings inside.
|
308 |
-
$css = preg_replace_callback(
|
309 |
-
'/filter:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^)]+)\)/Ss',
|
310 |
-
array($this, 'processOldIeSpecificMatrixDefinitionCallback'),
|
311 |
-
$css
|
312 |
-
);
|
313 |
-
|
314 |
-
// Process quoted unquotable attribute selectors to unquote them. Covers most common cases.
|
315 |
-
// Likelyhood of a quoted attribute selector being a substring in a string: Very very low.
|
316 |
-
$css = preg_replace(
|
317 |
-
'/\[\s*([a-z][a-z-]+)\s*([\*\|\^\$~]?=)\s*[\'"](-?[a-z_][a-z0-9-_]+)[\'"]\s*\]/Ssi',
|
318 |
-
'[$1$2$3]',
|
319 |
-
$css
|
320 |
-
);
|
321 |
-
|
322 |
-
// Process strings so their content doesn't get accidentally minified
|
323 |
-
$css = preg_replace_callback(
|
324 |
-
'/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S",
|
325 |
-
array($this, 'processStringsCallback'),
|
326 |
-
$css
|
327 |
-
);
|
328 |
-
|
329 |
-
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
330 |
-
$css = preg_replace('/\s+/S', ' ', $css);
|
331 |
-
|
332 |
-
// Process comments
|
333 |
-
$css = $this->processComments($css);
|
334 |
-
|
335 |
-
// Process rule bodies
|
336 |
-
$css = $this->processRuleBodies($css);
|
337 |
-
|
338 |
-
// Process at-rules and selectors
|
339 |
-
$css = $this->processAtRulesAndSelectors($css);
|
340 |
-
|
341 |
-
// Restore preserved rule bodies before splitting
|
342 |
-
$css = strtr($css, $this->ruleBodies);
|
343 |
-
|
344 |
-
// Some source control tools don't like it when files containing lines longer
|
345 |
-
// than, say 8000 characters, are checked in. The linebreak option is used in
|
346 |
-
// that case to split long lines after a specific column.
|
347 |
-
if ($this->linebreakPosition > 0) {
|
348 |
-
$l = strlen($css);
|
349 |
-
$offset = $this->linebreakPosition;
|
350 |
-
while (preg_match('/(?<!\\\\)\}(?!\n)/S', $css, $matches, PREG_OFFSET_CAPTURE, $offset)) {
|
351 |
-
$matchIndex = $matches[0][1];
|
352 |
-
$css = substr_replace($css, "\n", $matchIndex + 1, 0);
|
353 |
-
$offset = $matchIndex + 2 + $this->linebreakPosition;
|
354 |
-
$l += 1;
|
355 |
-
if ($offset > $l) {
|
356 |
-
break;
|
357 |
-
}
|
358 |
-
}
|
359 |
-
}
|
360 |
-
|
361 |
-
// Restore preserved comments and strings
|
362 |
-
$css = strtr($css, $this->preservedTokens);
|
363 |
-
|
364 |
-
return trim($css);
|
365 |
-
}
|
366 |
-
|
367 |
-
/**
|
368 |
-
* Searches & replaces all data urls with tokens before we start compressing,
|
369 |
-
* to avoid performance issues running some of the subsequent regexes against large string chunks.
|
370 |
-
* @param string $css
|
371 |
-
* @return string
|
372 |
-
*/
|
373 |
-
private function processDataUrls($css)
|
374 |
-
{
|
375 |
-
$ret = '';
|
376 |
-
$searchOffset = $substrOffset = 0;
|
377 |
-
|
378 |
-
// Since we need to account for non-base64 data urls, we need to handle
|
379 |
-
// ' and ) being part of the data string.
|
380 |
-
while (preg_match('/url\(\s*(["\']?)data:/Si', $css, $m, PREG_OFFSET_CAPTURE, $searchOffset)) {
|
381 |
-
$matchStartIndex = $m[0][1];
|
382 |
-
$dataStartIndex = $matchStartIndex + 4; // url( length
|
383 |
-
$searchOffset = $matchStartIndex + strlen($m[0][0]);
|
384 |
-
$terminator = $m[1][0]; // ', " or empty (not quoted)
|
385 |
-
$terminatorRegex = '/(?<!\\\\)'. (strlen($terminator) === 0 ? '' : $terminator.'\s*') .'(\))/S';
|
386 |
-
|
387 |
-
$ret .= substr($css, $substrOffset, $matchStartIndex - $substrOffset);
|
388 |
-
|
389 |
-
// Terminator found
|
390 |
-
if (preg_match($terminatorRegex, $css, $matches, PREG_OFFSET_CAPTURE, $searchOffset)) {
|
391 |
-
$matchEndIndex = $matches[1][1];
|
392 |
-
$searchOffset = $matchEndIndex + 1;
|
393 |
-
$token = substr($css, $dataStartIndex, $matchEndIndex - $dataStartIndex);
|
394 |
-
|
395 |
-
// Remove all spaces only for base64 encoded URLs.
|
396 |
-
if (stripos($token, 'base64,') !== false) {
|
397 |
-
$token = preg_replace('/\s+/S', '', $token);
|
398 |
-
}
|
399 |
-
|
400 |
-
$ret .= 'url('. $this->registerPreservedToken(trim($token)) .')';
|
401 |
-
// No end terminator found, re-add the whole match. Should we throw/warn here?
|
402 |
-
} else {
|
403 |
-
$ret .= substr($css, $matchStartIndex, $searchOffset - $matchStartIndex);
|
404 |
-
}
|
405 |
-
|
406 |
-
$substrOffset = $searchOffset;
|
407 |
-
}
|
408 |
-
|
409 |
-
$ret .= substr($css, $substrOffset);
|
410 |
-
|
411 |
-
return $ret;
|
412 |
-
}
|
413 |
-
|
414 |
-
/**
|
415 |
-
* Registers all comments found as candidates to be preserved.
|
416 |
-
* @param array $matches
|
417 |
-
* @return string
|
418 |
-
*/
|
419 |
-
private function processCommentsCallback($matches)
|
420 |
-
{
|
421 |
-
return '/*'. $this->registerCommentToken($matches[1]) .'*/';
|
422 |
-
}
|
423 |
-
|
424 |
-
/**
|
425 |
-
* Preserves old IE Matrix string definition
|
426 |
-
* @param array $matches
|
427 |
-
* @return string
|
428 |
-
*/
|
429 |
-
private function processOldIeSpecificMatrixDefinitionCallback($matches)
|
430 |
-
{
|
431 |
-
return 'filter:progid:DXImageTransform.Microsoft.Matrix('. $this->registerPreservedToken($matches[1]) .')';
|
432 |
-
}
|
433 |
-
|
434 |
-
/**
|
435 |
-
* Preserves strings found
|
436 |
-
* @param array $matches
|
437 |
-
* @return string
|
438 |
-
*/
|
439 |
-
private function processStringsCallback($matches)
|
440 |
-
{
|
441 |
-
$match = $matches[0];
|
442 |
-
$quote = substr($match, 0, 1);
|
443 |
-
$match = substr($match, 1, -1);
|
444 |
-
|
445 |
-
// maybe the string contains a comment-like substring?
|
446 |
-
// one, maybe more? put'em back then
|
447 |
-
if (strpos($match, self::COMMENT_TOKEN_START) !== false) {
|
448 |
-
$match = strtr($match, $this->comments);
|
449 |
-
}
|
450 |
-
|
451 |
-
// minify alpha opacity in filter strings
|
452 |
-
$match = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $match);
|
453 |
-
|
454 |
-
return $quote . $this->registerPreservedToken($match) . $quote;
|
455 |
-
}
|
456 |
-
|
457 |
-
/**
|
458 |
-
* Preserves or removes comments found.
|
459 |
-
* @param string $css
|
460 |
-
* @return string
|
461 |
-
*/
|
462 |
-
private function processComments($css)
|
463 |
-
{
|
464 |
-
foreach ($this->comments as $commentId => $comment) {
|
465 |
-
$commentIdString = '/*'. $commentId .'*/';
|
466 |
-
|
467 |
-
// ! in the first position of the comment means preserve
|
468 |
-
// so push to the preserved tokens keeping the !
|
469 |
-
if ($this->keepImportantComments && strpos($comment, '!') === 0) {
|
470 |
-
$preservedTokenId = $this->registerPreservedToken($comment);
|
471 |
-
// Put new lines before and after /*! important comments
|
472 |
-
$css = str_replace($commentIdString, "\n/*$preservedTokenId*/\n", $css);
|
473 |
-
continue;
|
474 |
-
}
|
475 |
-
|
476 |
-
// # sourceMappingURL= in the first position of the comment means sourcemap
|
477 |
-
// so push to the preserved tokens if {$this->keepSourceMapComment} is truthy.
|
478 |
-
if ($this->keepSourceMapComment && strpos($comment, '# sourceMappingURL=') === 0) {
|
479 |
-
$preservedTokenId = $this->registerPreservedToken($comment);
|
480 |
-
// Add new line before the sourcemap comment
|
481 |
-
$css = str_replace($commentIdString, "\n/*$preservedTokenId*/", $css);
|
482 |
-
continue;
|
483 |
-
}
|
484 |
-
|
485 |
-
// Keep empty comments after child selectors (IE7 hack)
|
486 |
-
// e.g. html >/**/ body
|
487 |
-
if (strlen($comment) === 0 && strpos($css, '>/*'.$commentId) !== false) {
|
488 |
-
$css = str_replace($commentId, $this->registerPreservedToken(''), $css);
|
489 |
-
continue;
|
490 |
-
}
|
491 |
-
|
492 |
-
// in all other cases kill the comment
|
493 |
-
$css = str_replace($commentIdString, '', $css);
|
494 |
-
}
|
495 |
-
|
496 |
-
// Normalize whitespace again
|
497 |
-
$css = preg_replace('/ +/S', ' ', $css);
|
498 |
-
|
499 |
-
return $css;
|
500 |
-
}
|
501 |
-
|
502 |
-
/**
|
503 |
-
* Finds, minifies & preserves all rule bodies.
|
504 |
-
* @param string $css the whole stylesheet.
|
505 |
-
* @return string
|
506 |
-
*/
|
507 |
-
private function processRuleBodies($css)
|
508 |
-
{
|
509 |
-
$ret = '';
|
510 |
-
$searchOffset = $substrOffset = 0;
|
511 |
-
|
512 |
-
while (($blockStartPos = strpos($css, '{', $searchOffset)) !== false) {
|
513 |
-
$blockEndPos = strpos($css, '}', $blockStartPos);
|
514 |
-
$nextBlockStartPos = strpos($css, '{', $blockStartPos + 1);
|
515 |
-
$ret .= substr($css, $substrOffset, $blockStartPos - $substrOffset);
|
516 |
-
|
517 |
-
if ($nextBlockStartPos !== false && $nextBlockStartPos < $blockEndPos) {
|
518 |
-
$ret .= substr($css, $blockStartPos, $nextBlockStartPos - $blockStartPos);
|
519 |
-
$searchOffset = $nextBlockStartPos;
|
520 |
-
} else {
|
521 |
-
$ruleBody = substr($css, $blockStartPos + 1, $blockEndPos - $blockStartPos - 1);
|
522 |
-
$ruleBodyToken = $this->registerRuleBodyToken($this->processRuleBody($ruleBody));
|
523 |
-
$ret .= '{'. $ruleBodyToken .'}';
|
524 |
-
$searchOffset = $blockEndPos + 1;
|
525 |
-
}
|
526 |
-
|
527 |
-
$substrOffset = $searchOffset;
|
528 |
-
}
|
529 |
-
|
530 |
-
$ret .= substr($css, $substrOffset);
|
531 |
-
|
532 |
-
return $ret;
|
533 |
-
}
|
534 |
-
|
535 |
-
/**
|
536 |
-
* Compresses non-group rule bodies.
|
537 |
-
* @param string $body The rule body without curly braces
|
538 |
-
* @return string
|
539 |
-
*/
|
540 |
-
private function processRuleBody($body)
|
541 |
-
{
|
542 |
-
$body = trim($body);
|
543 |
-
|
544 |
-
// Remove spaces before the things that should not have spaces before them.
|
545 |
-
$body = preg_replace('/ ([:=,)*\/;\n])/S', '$1', $body);
|
546 |
-
|
547 |
-
// Remove the spaces after the things that should not have spaces after them.
|
548 |
-
$body = preg_replace('/([:=,(*\/!;\n]) /S', '$1', $body);
|
549 |
-
|
550 |
-
// Replace multiple semi-colons in a row by a single one
|
551 |
-
$body = preg_replace('/;;+/S', ';', $body);
|
552 |
-
|
553 |
-
// Remove semicolon before closing brace except when:
|
554 |
-
// - The last property is prefixed with a `*` (lte IE7 hack) to avoid issues on Symbian S60 3.x browsers.
|
555 |
-
if (!preg_match('/\*[a-z0-9-]+:[^;]+;$/Si', $body)) {
|
556 |
-
$body = rtrim($body, ';');
|
557 |
-
}
|
558 |
-
|
559 |
-
// Remove important comments inside a rule body (because they make no sense here).
|
560 |
-
if (strpos($body, '/*') !== false) {
|
561 |
-
$body = preg_replace('/\n?\/\*[A-Z0-9_]+\*\/\n?/S', '', $body);
|
562 |
-
}
|
563 |
-
|
564 |
-
// Empty rule body? Exit :)
|
565 |
-
if (empty($body)) {
|
566 |
-
return '';
|
567 |
-
}
|
568 |
-
|
569 |
-
// Shorten font-weight values
|
570 |
-
$body = preg_replace(
|
571 |
-
array('/(font-weight:)bold\b/Si', '/(font-weight:)normal\b/Si'),
|
572 |
-
array('${1}700', '${1}400'),
|
573 |
-
$body
|
574 |
-
);
|
575 |
-
|
576 |
-
// Shorten background property
|
577 |
-
$body = preg_replace('/(background:)(?:none|transparent)( !|;|$)/Si', '${1}0 0$2', $body);
|
578 |
-
|
579 |
-
// Shorten opacity IE filter
|
580 |
-
$body = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $body);
|
581 |
-
|
582 |
-
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
|
583 |
-
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
|
584 |
-
// This makes it more likely that it'll get further compressed in the next step.
|
585 |
-
$body = preg_replace_callback(
|
586 |
-
'/(rgb|hsl)\(([0-9,.% -]+)\)(.|$)/Si',
|
587 |
-
array($this, 'shortenHslAndRgbToHexCallback'),
|
588 |
-
$body
|
589 |
-
);
|
590 |
-
|
591 |
-
// Shorten colors from #AABBCC to #ABC or shorter color name:
|
592 |
-
// - Look for hex colors which don't have a "=" in front of them (to avoid MSIE filters)
|
593 |
-
$body = preg_replace_callback(
|
594 |
-
'/(?<!=)#([0-9a-f]{3,6})( |,|\)|;|$)/Si',
|
595 |
-
array($this, 'shortenHexColorsCallback'),
|
596 |
-
$body
|
597 |
-
);
|
598 |
-
|
599 |
-
// Shorten long named colors with a shorter HEX counterpart: white -> #fff.
|
600 |
-
// Run at least 2 times to cover most cases
|
601 |
-
$body = preg_replace_callback(
|
602 |
-
array($this->namedToHexColorsRegex, $this->namedToHexColorsRegex),
|
603 |
-
array($this, 'shortenNamedColorsCallback'),
|
604 |
-
$body
|
605 |
-
);
|
606 |
-
|
607 |
-
// Replace positive sign from numbers before the leading space is removed.
|
608 |
-
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
|
609 |
-
$body = preg_replace('/([ :,(])\+(\.?\d+)/S', '$1$2', $body);
|
610 |
-
|
611 |
-
// shorten ms to s
|
612 |
-
$body = preg_replace_callback('/([ :,(])(-?)(\d{3,})ms/Si', function ($matches) {
|
613 |
-
return $matches[1] . $matches[2] . ((int) $matches[3] / 1000) .'s';
|
614 |
-
}, $body);
|
615 |
-
|
616 |
-
// Remove leading zeros from integer and float numbers.
|
617 |
-
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
|
618 |
-
$body = preg_replace('/([ :,(])(-?)0+([1-9]?\.?\d+)/S', '$1$2$3', $body);
|
619 |
-
|
620 |
-
// Remove trailing zeros from float numbers.
|
621 |
-
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
|
622 |
-
$body = preg_replace('/([ :,(])(-?\d?\.\d+?)0+([^\d])/S', '$1$2$3', $body);
|
623 |
-
|
624 |
-
// Remove trailing .0 -> -9.0 to -9
|
625 |
-
$body = preg_replace('/([ :,(])(-?\d+)\.0([^\d])/S', '$1$2$3', $body);
|
626 |
-
|
627 |
-
// Replace 0 length numbers with 0
|
628 |
-
$body = preg_replace('/([ :,(])-?\.?0+([^\d])/S', '${1}0$2', $body);
|
629 |
-
|
630 |
-
// Shorten zero values for safe properties only
|
631 |
-
$body = preg_replace(
|
632 |
-
array(
|
633 |
-
$this->shortenOneZeroesRegex,
|
634 |
-
$this->shortenTwoZeroesRegex,
|
635 |
-
$this->shortenThreeZeroesRegex,
|
636 |
-
$this->shortenFourZeroesRegex
|
637 |
-
),
|
638 |
-
array(
|
639 |
-
'$1$2:0',
|
640 |
-
'$1$2:$3 0',
|
641 |
-
'$1$2:$3 $4 0',
|
642 |
-
'$1$2:$3 $4 $5 0'
|
643 |
-
),
|
644 |
-
$body
|
645 |
-
);
|
646 |
-
|
647 |
-
// Replace 0 0 0; or 0 0 0 0; with 0 0 for background-position property.
|
648 |
-
$body = preg_replace('/(background-position):0(?: 0){2,3}( !|;|$)/Si', '$1:0 0$2', $body);
|
649 |
-
|
650 |
-
// Shorten suitable shorthand properties with repeated values
|
651 |
-
$body = preg_replace(
|
652 |
-
array(
|
653 |
-
'/(margin|padding|border-(?:width|radius)):('.$this->numRegex.')(?: \2)+( !|;|$)/Si',
|
654 |
-
'/(border-(?:style|color)):([#a-z0-9]+)(?: \2)+( !|;|$)/Si'
|
655 |
-
),
|
656 |
-
'$1:$2$3',
|
657 |
-
$body
|
658 |
-
);
|
659 |
-
$body = preg_replace(
|
660 |
-
array(
|
661 |
-
'/(margin|padding|border-(?:width|radius)):'.
|
662 |
-
'('.$this->numRegex.') ('.$this->numRegex.') \2 \3( !|;|$)/Si',
|
663 |
-
'/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) \2 \3( !|;|$)/Si'
|
664 |
-
),
|
665 |
-
'$1:$2 $3$4',
|
666 |
-
$body
|
667 |
-
);
|
668 |
-
$body = preg_replace(
|
669 |
-
array(
|
670 |
-
'/(margin|padding|border-(?:width|radius)):'.
|
671 |
-
'('.$this->numRegex.') ('.$this->numRegex.') ('.$this->numRegex.') \3( !|;|$)/Si',
|
672 |
-
'/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) ([#a-z0-9]+) \3( !|;|$)/Si'
|
673 |
-
),
|
674 |
-
'$1:$2 $3 $4$5',
|
675 |
-
$body
|
676 |
-
);
|
677 |
-
|
678 |
-
// Lowercase some common functions that can be values
|
679 |
-
$body = preg_replace_callback(
|
680 |
-
'/(?:attr|blur|brightness|circle|contrast|cubic-bezier|drop-shadow|ellipse|from|grayscale|'.
|
681 |
-
'hsla?|hue-rotate|inset|invert|local|minmax|opacity|perspective|polygon|rgba?|rect|repeat|saturate|sepia|'.
|
682 |
-
'steps|to|url|var|-webkit-gradient|'.
|
683 |
-
'(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|(?:repeating-)?(?:linear|radial)-gradient))\(/Si',
|
684 |
-
array($this, 'strtolowerCallback'),
|
685 |
-
$body
|
686 |
-
);
|
687 |
-
|
688 |
-
// Lowercase all uppercase properties
|
689 |
-
$body = preg_replace_callback('/(?:^|;)[A-Z-]+:/S', array($this, 'strtolowerCallback'), $body);
|
690 |
-
|
691 |
-
return $body;
|
692 |
-
}
|
693 |
-
|
694 |
-
/**
|
695 |
-
* Compresses At-rules and selectors.
|
696 |
-
* @param string $css the whole stylesheet with rule bodies tokenized.
|
697 |
-
* @return string
|
698 |
-
*/
|
699 |
-
private function processAtRulesAndSelectors($css)
|
700 |
-
{
|
701 |
-
$charset = '';
|
702 |
-
$imports = '';
|
703 |
-
$namespaces = '';
|
704 |
-
|
705 |
-
// Remove spaces before the things that should not have spaces before them.
|
706 |
-
$css = preg_replace('/ ([@{};>+)\]~=,\/\n])/S', '$1', $css);
|
707 |
-
|
708 |
-
// Remove the spaces after the things that should not have spaces after them.
|
709 |
-
$css = preg_replace('/([{}:;>+(\[~=,\/\n]) /S', '$1', $css);
|
710 |
-
|
711 |
-
// Shorten shortable double colon (CSS3) pseudo-elements to single colon (CSS2)
|
712 |
-
$css = preg_replace('/::(before|after|first-(?:line|letter))(\{|,)/Si', ':$1$2', $css);
|
713 |
-
|
714 |
-
// Retain space for special IE6 cases
|
715 |
-
$css = preg_replace_callback('/:first-(line|letter)(\{|,)/Si', function ($matches) {
|
716 |
-
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
|
717 |
-
}, $css);
|
718 |
-
|
719 |
-
// Find a fraction that may used in some @media queries such as: (min-aspect-ratio: 1/1)
|
720 |
-
// Add token to add the "/" back in later
|
721 |
-
$css = preg_replace('/\(([a-z-]+):([0-9]+)\/([0-9]+)\)/Si', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
|
722 |
-
|
723 |
-
// Remove empty rule blocks up to 2 levels deep.
|
724 |
-
$css = preg_replace(array_fill(0, 2, '/(\{)[^{};\/\n]+\{\}/S'), '$1', $css);
|
725 |
-
$css = preg_replace('/[^{};\/\n]+\{\}/S', '', $css);
|
726 |
-
|
727 |
-
// Two important comments next to each other? Remove extra newline.
|
728 |
-
if ($this->keepImportantComments) {
|
729 |
-
$css = str_replace("\n\n", "\n", $css);
|
730 |
-
}
|
731 |
-
|
732 |
-
// Restore fraction
|
733 |
-
$css = str_replace(self::QUERY_FRACTION, '/', $css);
|
734 |
-
|
735 |
-
// Lowercase some popular @directives
|
736 |
-
$css = preg_replace_callback(
|
737 |
-
'/(?<!\\\\)@(?:charset|document|font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframes|media|'.
|
738 |
-
'namespace|page|supports|viewport)/Si',
|
739 |
-
array($this, 'strtolowerCallback'),
|
740 |
-
$css
|
741 |
-
);
|
742 |
-
|
743 |
-
// Lowercase some popular media types
|
744 |
-
$css = preg_replace_callback(
|
745 |
-
'/[ ,](?:all|aural|braille|handheld|print|projection|screen|tty|tv|embossed|speech)[ ,;{]/Si',
|
746 |
-
array($this, 'strtolowerCallback'),
|
747 |
-
$css
|
748 |
-
);
|
749 |
-
|
750 |
-
// Lowercase some common pseudo-classes & pseudo-elements
|
751 |
-
$css = preg_replace_callback(
|
752 |
-
'/(?<!\\\\):(?:active|after|before|checked|default|disabled|empty|enabled|first-(?:child|of-type)|'.
|
753 |
-
'focus(?:-within)?|hover|indeterminate|in-range|invalid|lang\(|last-(?:child|of-type)|left|link|not\(|'.
|
754 |
-
'nth-(?:child|of-type)\(|nth-last-(?:child|of-type)\(|only-(?:child|of-type)|optional|out-of-range|'.
|
755 |
-
'read-(?:only|write)|required|right|root|:selection|target|valid|visited)/Si',
|
756 |
-
array($this, 'strtolowerCallback'),
|
757 |
-
$css
|
758 |
-
);
|
759 |
-
|
760 |
-
// @charset handling
|
761 |
-
if (preg_match($this->charsetRegex, $css, $matches)) {
|
762 |
-
// Keep the first @charset at-rule found
|
763 |
-
$charset = $matches[0];
|
764 |
-
// Delete all @charset at-rules
|
765 |
-
$css = preg_replace($this->charsetRegex, '', $css);
|
766 |
-
}
|
767 |
-
|
768 |
-
// @import handling
|
769 |
-
$css = preg_replace_callback($this->importRegex, function ($matches) use (&$imports) {
|
770 |
-
// Keep all @import at-rules found for later
|
771 |
-
$imports .= $matches[0];
|
772 |
-
// Delete all @import at-rules
|
773 |
-
return '';
|
774 |
-
}, $css);
|
775 |
-
|
776 |
-
// @namespace handling
|
777 |
-
$css = preg_replace_callback($this->namespaceRegex, function ($matches) use (&$namespaces) {
|
778 |
-
// Keep all @namespace at-rules found for later
|
779 |
-
$namespaces .= $matches[0];
|
780 |
-
// Delete all @namespace at-rules
|
781 |
-
return '';
|
782 |
-
}, $css);
|
783 |
-
|
784 |
-
// Order critical at-rules:
|
785 |
-
// 1. @charset first
|
786 |
-
// 2. @imports below @charset
|
787 |
-
// 3. @namespaces below @imports
|
788 |
-
$css = $charset . $imports . $namespaces . $css;
|
789 |
-
|
790 |
-
return $css;
|
791 |
-
}
|
792 |
-
|
793 |
-
/**
|
794 |
-
* Converts hsl() & rgb() colors to HEX format.
|
795 |
-
* @param $matches
|
796 |
-
* @return string
|
797 |
-
*/
|
798 |
-
private function shortenHslAndRgbToHexCallback($matches)
|
799 |
-
{
|
800 |
-
$type = $matches[1];
|
801 |
-
$values = explode(',', $matches[2]);
|
802 |
-
$terminator = $matches[3];
|
803 |
-
|
804 |
-
if ($type === 'hsl') {
|
805 |
-
$values = Utils::hslToRgb($values);
|
806 |
-
}
|
807 |
-
|
808 |
-
$hexColors = Utils::rgbToHex($values);
|
809 |
-
|
810 |
-
// Restore space after rgb() or hsl() function in some cases such as:
|
811 |
-
// background-image: linear-gradient(to bottom, rgb(210,180,140) 10%, rgb(255,0,0) 90%);
|
812 |
-
if (!empty($terminator) && !preg_match('/[ ,);]/S', $terminator)) {
|
813 |
-
$terminator = ' '. $terminator;
|
814 |
-
}
|
815 |
-
|
816 |
-
return '#'. implode('', $hexColors) . $terminator;
|
817 |
-
}
|
818 |
-
|
819 |
-
/**
|
820 |
-
* Compresses HEX color values of the form #AABBCC to #ABC or short color name.
|
821 |
-
* @param $matches
|
822 |
-
* @return string
|
823 |
-
*/
|
824 |
-
private function shortenHexColorsCallback($matches)
|
825 |
-
{
|
826 |
-
$hex = $matches[1];
|
827 |
-
|
828 |
-
// Shorten suitable 6 chars HEX colors
|
829 |
-
if (strlen($hex) === 6 && preg_match('/^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/Si', $hex, $m)) {
|
830 |
-
$hex = $m[1] . $m[2] . $m[3];
|
831 |
-
}
|
832 |
-
|
833 |
-
// Lowercase
|
834 |
-
$hex = '#'. strtolower($hex);
|
835 |
-
|
836 |
-
// Replace Hex colors with shorter color names
|
837 |
-
$color = array_key_exists($hex, $this->hexToNamedColorsMap) ? $this->hexToNamedColorsMap[$hex] : $hex;
|
838 |
-
|
839 |
-
return $color . $matches[2];
|
840 |
-
}
|
841 |
-
|
842 |
-
/**
|
843 |
-
* Shortens all named colors with a shorter HEX counterpart for a set of safe properties
|
844 |
-
* e.g. white -> #fff
|
845 |
-
* @param array $matches
|
846 |
-
* @return string
|
847 |
-
*/
|
848 |
-
private function shortenNamedColorsCallback($matches)
|
849 |
-
{
|
850 |
-
return $matches[1] . $this->namedToHexColorsMap[strtolower($matches[2])] . $matches[3];
|
851 |
-
}
|
852 |
-
|
853 |
-
/**
|
854 |
-
* Makes a string lowercase
|
855 |
-
* @param array $matches
|
856 |
-
* @return string
|
857 |
-
*/
|
858 |
-
private function strtolowerCallback($matches)
|
859 |
-
{
|
860 |
-
return strtolower($matches[0]);
|
861 |
-
}
|
862 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Utils.php
DELETED
@@ -1,149 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
namespace w3tc_tubalmartin\CssMin;
|
4 |
-
|
5 |
-
class Utils
|
6 |
-
{
|
7 |
-
/**
|
8 |
-
* Clamps a number between a minimum and a maximum value.
|
9 |
-
* @param int|float $n the number to clamp
|
10 |
-
* @param int|float $min the lower end number allowed
|
11 |
-
* @param int|float $max the higher end number allowed
|
12 |
-
* @return int|float
|
13 |
-
*/
|
14 |
-
public static function clampNumber($n, $min, $max)
|
15 |
-
{
|
16 |
-
return min(max($n, $min), $max);
|
17 |
-
}
|
18 |
-
|
19 |
-
/**
|
20 |
-
* Clamps a RGB color number outside the sRGB color space
|
21 |
-
* @param int|float $n the number to clamp
|
22 |
-
* @return int|float
|
23 |
-
*/
|
24 |
-
public static function clampNumberSrgb($n)
|
25 |
-
{
|
26 |
-
return self::clampNumber($n, 0, 255);
|
27 |
-
}
|
28 |
-
|
29 |
-
/**
|
30 |
-
* Converts a HSL color into a RGB color
|
31 |
-
* @param array $hslValues
|
32 |
-
* @return array
|
33 |
-
*/
|
34 |
-
public static function hslToRgb($hslValues)
|
35 |
-
{
|
36 |
-
$h = floatval($hslValues[0]);
|
37 |
-
$s = floatval(str_replace('%', '', $hslValues[1]));
|
38 |
-
$l = floatval(str_replace('%', '', $hslValues[2]));
|
39 |
-
|
40 |
-
// Wrap and clamp, then fraction!
|
41 |
-
$h = ((($h % 360) + 360) % 360) / 360;
|
42 |
-
$s = self::clampNumber($s, 0, 100) / 100;
|
43 |
-
$l = self::clampNumber($l, 0, 100) / 100;
|
44 |
-
|
45 |
-
if ($s == 0) {
|
46 |
-
$r = $g = $b = self::roundNumber(255 * $l);
|
47 |
-
} else {
|
48 |
-
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
|
49 |
-
$v1 = (2 * $l) - $v2;
|
50 |
-
$r = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h + (1/3)));
|
51 |
-
$g = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h));
|
52 |
-
$b = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h - (1/3)));
|
53 |
-
}
|
54 |
-
|
55 |
-
return array($r, $g, $b);
|
56 |
-
}
|
57 |
-
|
58 |
-
/**
|
59 |
-
* Tests and selects the correct formula for each RGB color channel
|
60 |
-
* @param $v1
|
61 |
-
* @param $v2
|
62 |
-
* @param $vh
|
63 |
-
* @return mixed
|
64 |
-
*/
|
65 |
-
public static function hueToRgb($v1, $v2, $vh)
|
66 |
-
{
|
67 |
-
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
|
68 |
-
|
69 |
-
if ($vh * 6 < 1) {
|
70 |
-
return $v1 + ($v2 - $v1) * 6 * $vh;
|
71 |
-
}
|
72 |
-
|
73 |
-
if ($vh * 2 < 1) {
|
74 |
-
return $v2;
|
75 |
-
}
|
76 |
-
|
77 |
-
if ($vh * 3 < 2) {
|
78 |
-
return $v1 + ($v2 - $v1) * ((2 / 3) - $vh) * 6;
|
79 |
-
}
|
80 |
-
|
81 |
-
return $v1;
|
82 |
-
}
|
83 |
-
|
84 |
-
/**
|
85 |
-
* Convert strings like "64M" or "30" to int values
|
86 |
-
* @param mixed $size
|
87 |
-
* @return int
|
88 |
-
*/
|
89 |
-
public static function normalizeInt($size)
|
90 |
-
{
|
91 |
-
if (is_string($size)) {
|
92 |
-
$letter = substr($size, -1);
|
93 |
-
$size = intval($size);
|
94 |
-
switch ($letter) {
|
95 |
-
case 'M':
|
96 |
-
case 'm':
|
97 |
-
return (int) $size * 1048576;
|
98 |
-
case 'K':
|
99 |
-
case 'k':
|
100 |
-
return (int) $size * 1024;
|
101 |
-
case 'G':
|
102 |
-
case 'g':
|
103 |
-
return (int) $size * 1073741824;
|
104 |
-
}
|
105 |
-
}
|
106 |
-
return (int) $size;
|
107 |
-
}
|
108 |
-
|
109 |
-
/**
|
110 |
-
* Converts a string containing and RGB percentage value into a RGB integer value i.e. '90%' -> 229.5
|
111 |
-
* @param $rgbPercentage
|
112 |
-
* @return int
|
113 |
-
*/
|
114 |
-
public static function rgbPercentageToRgbInteger($rgbPercentage)
|
115 |
-
{
|
116 |
-
if (strpos($rgbPercentage, '%') !== false) {
|
117 |
-
$rgbPercentage = self::roundNumber(floatval(str_replace('%', '', $rgbPercentage)) * 2.55);
|
118 |
-
}
|
119 |
-
|
120 |
-
return intval($rgbPercentage, 10);
|
121 |
-
}
|
122 |
-
|
123 |
-
/**
|
124 |
-
* Converts a RGB color into a HEX color
|
125 |
-
* @param array $rgbColors
|
126 |
-
* @return array
|
127 |
-
*/
|
128 |
-
public static function rgbToHex($rgbColors)
|
129 |
-
{
|
130 |
-
$hexColors = array();
|
131 |
-
|
132 |
-
// Values outside the sRGB color space should be clipped (0-255)
|
133 |
-
for ($i = 0, $l = count($rgbColors); $i < $l; $i++) {
|
134 |
-
$hexColors[$i] = sprintf("%02x", self::clampNumberSrgb(self::rgbPercentageToRgbInteger($rgbColors[$i])));
|
135 |
-
}
|
136 |
-
|
137 |
-
return $hexColors;
|
138 |
-
}
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Rounds a number to its closest integer
|
142 |
-
* @param $n
|
143 |
-
* @return int
|
144 |
-
*/
|
145 |
-
public static function roundNumber($n)
|
146 |
-
{
|
147 |
-
return intval(round(floatval($n)), 10);
|
148 |
-
}
|
149 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lib/YuiCssMin/Colors.php
ADDED
@@ -0,0 +1,154 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace W3TCL\YuiCssMin;
|
3 |
+
|
4 |
+
class Colors
|
5 |
+
{
|
6 |
+
public static function getHexToNamedMap()
|
7 |
+
{
|
8 |
+
// Hex colors longer than named counterpart
|
9 |
+
return array(
|
10 |
+
'#f0ffff' => 'azure',
|
11 |
+
'#f5f5dc' => 'beige',
|
12 |
+
'#ffe4c4' => 'bisque',
|
13 |
+
'#a52a2a' => 'brown',
|
14 |
+
'#ff7f50' => 'coral',
|
15 |
+
'#ffd700' => 'gold',
|
16 |
+
'#808080' => 'gray',
|
17 |
+
'#008000' => 'green',
|
18 |
+
'#4b0082' => 'indigo',
|
19 |
+
'#fffff0' => 'ivory',
|
20 |
+
'#f0e68c' => 'khaki',
|
21 |
+
'#faf0e6' => 'linen',
|
22 |
+
'#800000' => 'maroon',
|
23 |
+
'#000080' => 'navy',
|
24 |
+
'#fdf5e6' => 'oldlace',
|
25 |
+
'#808000' => 'olive',
|
26 |
+
'#ffa500' => 'orange',
|
27 |
+
'#da70d6' => 'orchid',
|
28 |
+
'#cd853f' => 'peru',
|
29 |
+
'#ffc0cb' => 'pink',
|
30 |
+
'#dda0dd' => 'plum',
|
31 |
+
'#800080' => 'purple',
|
32 |
+
'#f00' => 'red',
|
33 |
+
'#fa8072' => 'salmon',
|
34 |
+
'#a0522d' => 'sienna',
|
35 |
+
'#c0c0c0' => 'silver',
|
36 |
+
'#fffafa' => 'snow',
|
37 |
+
'#d2b48c' => 'tan',
|
38 |
+
'#008080' => 'teal',
|
39 |
+
'#ff6347' => 'tomato',
|
40 |
+
'#ee82ee' => 'violet',
|
41 |
+
'#f5deb3' => 'wheat'
|
42 |
+
);
|
43 |
+
}
|
44 |
+
|
45 |
+
public static function getNamedToHexMap()
|
46 |
+
{
|
47 |
+
// Named colors longer than hex counterpart
|
48 |
+
return array(
|
49 |
+
'aliceblue' => '#f0f8ff',
|
50 |
+
'antiquewhite' => '#faebd7',
|
51 |
+
'aquamarine' => '#7fffd4',
|
52 |
+
'black' => '#000',
|
53 |
+
'blanchedalmond' => '#ffebcd',
|
54 |
+
'blueviolet' => '#8a2be2',
|
55 |
+
'burlywood' => '#deb887',
|
56 |
+
'cadetblue' => '#5f9ea0',
|
57 |
+
'chartreuse' => '#7fff00',
|
58 |
+
'chocolate' => '#d2691e',
|
59 |
+
'cornflowerblue' => '#6495ed',
|
60 |
+
'cornsilk' => '#fff8dc',
|
61 |
+
'darkblue' => '#00008b',
|
62 |
+
'darkcyan' => '#008b8b',
|
63 |
+
'darkgoldenrod' => '#b8860b',
|
64 |
+
'darkgray' => '#a9a9a9',
|
65 |
+
'darkgreen' => '#006400',
|
66 |
+
'darkgrey' => '#a9a9a9',
|
67 |
+
'darkkhaki' => '#bdb76b',
|
68 |
+
'darkmagenta' => '#8b008b',
|
69 |
+
'darkolivegreen' => '#556b2f',
|
70 |
+
'darkorange' => '#ff8c00',
|
71 |
+
'darkorchid' => '#9932cc',
|
72 |
+
'darksalmon' => '#e9967a',
|
73 |
+
'darkseagreen' => '#8fbc8f',
|
74 |
+
'darkslateblue' => '#483d8b',
|
75 |
+
'darkslategray' => '#2f4f4f',
|
76 |
+
'darkslategrey' => '#2f4f4f',
|
77 |
+
'darkturquoise' => '#00ced1',
|
78 |
+
'darkviolet' => '#9400d3',
|
79 |
+
'deeppink' => '#ff1493',
|
80 |
+
'deepskyblue' => '#00bfff',
|
81 |
+
'dodgerblue' => '#1e90ff',
|
82 |
+
'firebrick' => '#b22222',
|
83 |
+
'floralwhite' => '#fffaf0',
|
84 |
+
'forestgreen' => '#228b22',
|
85 |
+
'fuchsia' => '#f0f',
|
86 |
+
'gainsboro' => '#dcdcdc',
|
87 |
+
'ghostwhite' => '#f8f8ff',
|
88 |
+
'goldenrod' => '#daa520',
|
89 |
+
'greenyellow' => '#adff2f',
|
90 |
+
'honeydew' => '#f0fff0',
|
91 |
+
'indianred' => '#cd5c5c',
|
92 |
+
'lavender' => '#e6e6fa',
|
93 |
+
'lavenderblush' => '#fff0f5',
|
94 |
+
'lawngreen' => '#7cfc00',
|
95 |
+
'lemonchiffon' => '#fffacd',
|
96 |
+
'lightblue' => '#add8e6',
|
97 |
+
'lightcoral' => '#f08080',
|
98 |
+
'lightcyan' => '#e0ffff',
|
99 |
+
'lightgoldenrodyellow' => '#fafad2',
|
100 |
+
'lightgray' => '#d3d3d3',
|
101 |
+
'lightgreen' => '#90ee90',
|
102 |
+
'lightgrey' => '#d3d3d3',
|
103 |
+
'lightpink' => '#ffb6c1',
|
104 |
+
'lightsalmon' => '#ffa07a',
|
105 |
+
'lightseagreen' => '#20b2aa',
|
106 |
+
'lightskyblue' => '#87cefa',
|
107 |
+
'lightslategray' => '#778899',
|
108 |
+
'lightslategrey' => '#778899',
|
109 |
+
'lightsteelblue' => '#b0c4de',
|
110 |
+
'lightyellow' => '#ffffe0',
|
111 |
+
'limegreen' => '#32cd32',
|
112 |
+
'mediumaquamarine' => '#66cdaa',
|
113 |
+
'mediumblue' => '#0000cd',
|
114 |
+
'mediumorchid' => '#ba55d3',
|
115 |
+
'mediumpurple' => '#9370db',
|
116 |
+
'mediumseagreen' => '#3cb371',
|
117 |
+
'mediumslateblue' => '#7b68ee',
|
118 |
+
'mediumspringgreen' => '#00fa9a',
|
119 |
+
'mediumturquoise' => '#48d1cc',
|
120 |
+
'mediumvioletred' => '#c71585',
|
121 |
+
'midnightblue' => '#191970',
|
122 |
+
'mintcream' => '#f5fffa',
|
123 |
+
'mistyrose' => '#ffe4e1',
|
124 |
+
'moccasin' => '#ffe4b5',
|
125 |
+
'navajowhite' => '#ffdead',
|
126 |
+
'olivedrab' => '#6b8e23',
|
127 |
+
'orangered' => '#ff4500',
|
128 |
+
'palegoldenrod' => '#eee8aa',
|
129 |
+
'palegreen' => '#98fb98',
|
130 |
+
'paleturquoise' => '#afeeee',
|
131 |
+
'palevioletred' => '#db7093',
|
132 |
+
'papayawhip' => '#ffefd5',
|
133 |
+
'peachpuff' => '#ffdab9',
|
134 |
+
'powderblue' => '#b0e0e6',
|
135 |
+
'rebeccapurple' => '#663399',
|
136 |
+
'rosybrown' => '#bc8f8f',
|
137 |
+
'royalblue' => '#4169e1',
|
138 |
+
'saddlebrown' => '#8b4513',
|
139 |
+
'sandybrown' => '#f4a460',
|
140 |
+
'seagreen' => '#2e8b57',
|
141 |
+
'seashell' => '#fff5ee',
|
142 |
+
'slateblue' => '#6a5acd',
|
143 |
+
'slategray' => '#708090',
|
144 |
+
'slategrey' => '#708090',
|
145 |
+
'springgreen' => '#00ff7f',
|
146 |
+
'steelblue' => '#4682b4',
|
147 |
+
'turquoise' => '#40e0d0',
|
148 |
+
'white' => '#fff',
|
149 |
+
'whitesmoke' => '#f5f5f5',
|
150 |
+
'yellow' => '#ff0',
|
151 |
+
'yellowgreen' => '#9acd32'
|
152 |
+
);
|
153 |
+
}
|
154 |
+
}
|
lib/YuiCssMin/Command.php
ADDED
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace W3TCL\YuiCssMin;
|
3 |
+
|
4 |
+
class Command
|
5 |
+
{
|
6 |
+
const SUCCESS_EXIT = 0;
|
7 |
+
const FAILURE_EXIT = 1;
|
8 |
+
|
9 |
+
protected $stats = array();
|
10 |
+
|
11 |
+
public static function main()
|
12 |
+
{
|
13 |
+
$command = new self;
|
14 |
+
$command->run();
|
15 |
+
}
|
16 |
+
|
17 |
+
public function run()
|
18 |
+
{
|
19 |
+
$opts = getopt(
|
20 |
+
'hi:o:',
|
21 |
+
array(
|
22 |
+
'help',
|
23 |
+
'input:',
|
24 |
+
'output:',
|
25 |
+
'dry-run',
|
26 |
+
'keep-sourcemap',
|
27 |
+
'keep-sourcemap-comment',
|
28 |
+
'linebreak-position:',
|
29 |
+
'memory-limit:',
|
30 |
+
'pcre-backtrack-limit:',
|
31 |
+
'pcre-recursion-limit:',
|
32 |
+
'remove-important-comments'
|
33 |
+
)
|
34 |
+
);
|
35 |
+
|
36 |
+
$help = $this->getOpt(array('h', 'help'), $opts);
|
37 |
+
$input = $this->getOpt(array('i', 'input'), $opts);
|
38 |
+
$output = $this->getOpt(array('o', 'output'), $opts);
|
39 |
+
$dryrun = $this->getOpt('dry-run', $opts);
|
40 |
+
$keepSourceMapComment = $this->getOpt(array('keep-sourcemap', 'keep-sourcemap-comment'), $opts);
|
41 |
+
$linebreakPosition = $this->getOpt('linebreak-position', $opts);
|
42 |
+
$memoryLimit = $this->getOpt('memory-limit', $opts);
|
43 |
+
$backtrackLimit = $this->getOpt('pcre-backtrack-limit', $opts);
|
44 |
+
$recursionLimit = $this->getOpt('pcre-recursion-limit', $opts);
|
45 |
+
$removeImportantComments = $this->getOpt('remove-important-comments', $opts);
|
46 |
+
|
47 |
+
if (!is_null($help)) {
|
48 |
+
$this->showHelp();
|
49 |
+
die(self::SUCCESS_EXIT);
|
50 |
+
}
|
51 |
+
|
52 |
+
if (is_null($input)) {
|
53 |
+
fwrite(STDERR, '-i <file> argument is missing' . PHP_EOL);
|
54 |
+
$this->showHelp();
|
55 |
+
die(self::FAILURE_EXIT);
|
56 |
+
}
|
57 |
+
|
58 |
+
if (!is_readable($input)) {
|
59 |
+
fwrite(STDERR, 'Input file is not readable' . PHP_EOL);
|
60 |
+
die(self::FAILURE_EXIT);
|
61 |
+
}
|
62 |
+
|
63 |
+
$css = file_get_contents($input);
|
64 |
+
|
65 |
+
if ($css === false) {
|
66 |
+
fwrite(STDERR, 'Input CSS code could not be retrieved from input file' . PHP_EOL);
|
67 |
+
die(self::FAILURE_EXIT);
|
68 |
+
}
|
69 |
+
|
70 |
+
$this->setStat('original-size', strlen($css));
|
71 |
+
|
72 |
+
$cssmin = new Minifier;
|
73 |
+
|
74 |
+
if (!is_null($keepSourceMapComment)) {
|
75 |
+
$cssmin->keepSourceMapComment();
|
76 |
+
}
|
77 |
+
|
78 |
+
if (!is_null($removeImportantComments)) {
|
79 |
+
$cssmin->removeImportantComments();
|
80 |
+
}
|
81 |
+
|
82 |
+
if (!is_null($linebreakPosition)) {
|
83 |
+
$cssmin->setLineBreakPosition($linebreakPosition);
|
84 |
+
}
|
85 |
+
|
86 |
+
if (!is_null($memoryLimit)) {
|
87 |
+
$cssmin->setMemoryLimit($memoryLimit);
|
88 |
+
}
|
89 |
+
|
90 |
+
if (!is_null($backtrackLimit)) {
|
91 |
+
$cssmin->setPcreBacktrackLimit($backtrackLimit);
|
92 |
+
}
|
93 |
+
|
94 |
+
if (!is_null($recursionLimit)) {
|
95 |
+
$cssmin->setPcreRecursionLimit($recursionLimit);
|
96 |
+
}
|
97 |
+
|
98 |
+
$this->setStat('compression-time-start', microtime(true));
|
99 |
+
|
100 |
+
$css = $cssmin->run($css);
|
101 |
+
|
102 |
+
$this->setStat('compression-time-end', microtime(true));
|
103 |
+
$this->setStat('peak-memory-usage', memory_get_peak_usage(true));
|
104 |
+
$this->setStat('compressed-size', strlen($css));
|
105 |
+
|
106 |
+
if (!is_null($dryrun)) {
|
107 |
+
$this->showStats();
|
108 |
+
die(self::SUCCESS_EXIT);
|
109 |
+
}
|
110 |
+
|
111 |
+
if (is_null($output)) {
|
112 |
+
fwrite(STDOUT, $css . PHP_EOL);
|
113 |
+
$this->showStats();
|
114 |
+
die(self::SUCCESS_EXIT);
|
115 |
+
}
|
116 |
+
|
117 |
+
if (!is_writable(dirname($output))) {
|
118 |
+
fwrite(STDERR, 'Output file is not writable' . PHP_EOL);
|
119 |
+
die(self::FAILURE_EXIT);
|
120 |
+
}
|
121 |
+
|
122 |
+
if (file_put_contents($output, $css) === false) {
|
123 |
+
fwrite(STDERR, 'Compressed CSS code could not be saved to output file' . PHP_EOL);
|
124 |
+
die(self::FAILURE_EXIT);
|
125 |
+
}
|
126 |
+
|
127 |
+
$this->showStats();
|
128 |
+
|
129 |
+
die(self::SUCCESS_EXIT);
|
130 |
+
}
|
131 |
+
|
132 |
+
protected function getOpt($opts, $options)
|
133 |
+
{
|
134 |
+
$value = null;
|
135 |
+
|
136 |
+
if (is_string($opts)) {
|
137 |
+
$opts = array($opts);
|
138 |
+
}
|
139 |
+
|
140 |
+
foreach ($opts as $opt) {
|
141 |
+
if (array_key_exists($opt, $options)) {
|
142 |
+
$value = $options[$opt];
|
143 |
+
break;
|
144 |
+
}
|
145 |
+
}
|
146 |
+
|
147 |
+
return $value;
|
148 |
+
}
|
149 |
+
|
150 |
+
protected function setStat($statName, $statValue)
|
151 |
+
{
|
152 |
+
$this->stats[$statName] = $statValue;
|
153 |
+
}
|
154 |
+
|
155 |
+
protected function formatBytes($size, $precision = 2)
|
156 |
+
{
|
157 |
+
$base = log($size, 1024);
|
158 |
+
$suffixes = array('B', 'K', 'M', 'G', 'T');
|
159 |
+
return round(pow(1024, $base - floor($base)), $precision) .' '. $suffixes[floor($base)];
|
160 |
+
}
|
161 |
+
|
162 |
+
protected function formatMicroSeconds($microSecs, $precision = 2)
|
163 |
+
{
|
164 |
+
// ms
|
165 |
+
$time = round($microSecs * 1000, $precision);
|
166 |
+
|
167 |
+
if ($time >= 60 * 1000) {
|
168 |
+
$time = round($time / 60 * 1000, $precision) .' m'; // m
|
169 |
+
} elseif ($time >= 1000) {
|
170 |
+
$time = round($time / 1000, $precision) .' s'; // s
|
171 |
+
} else {
|
172 |
+
$time .= ' ms';
|
173 |
+
}
|
174 |
+
|
175 |
+
return $time;
|
176 |
+
}
|
177 |
+
|
178 |
+
protected function showStats()
|
179 |
+
{
|
180 |
+
$spaceSavings = round((1 - ($this->stats['compressed-size'] / $this->stats['original-size'])) * 100, 2);
|
181 |
+
$compressionRatio = round($this->stats['original-size'] / $this->stats['compressed-size'], 2);
|
182 |
+
$compressionTime = $this->formatMicroSeconds(
|
183 |
+
$this->stats['compression-time-end'] - $this->stats['compression-time-start']
|
184 |
+
);
|
185 |
+
$peakMemoryUsage = $this->formatBytes($this->stats['peak-memory-usage']);
|
186 |
+
|
187 |
+
print <<<EOT
|
188 |
+
|
189 |
+
------------------------------
|
190 |
+
CSSMIN STATS
|
191 |
+
------------------------------
|
192 |
+
Space savings: {$spaceSavings} %
|
193 |
+
Compression ratio: {$compressionRatio}:1
|
194 |
+
Compression time: $compressionTime
|
195 |
+
Peak memory usage: $peakMemoryUsage
|
196 |
+
|
197 |
+
|
198 |
+
EOT;
|
199 |
+
}
|
200 |
+
|
201 |
+
protected function showHelp()
|
202 |
+
{
|
203 |
+
print <<<'EOT'
|
204 |
+
Usage: cssmin [options] -i <file> [-o <file>]
|
205 |
+
|
206 |
+
-i|--input <file> File containing uncompressed CSS code.
|
207 |
+
-o|--output <file> File to use to save compressed CSS code.
|
208 |
+
|
209 |
+
Options:
|
210 |
+
|
211 |
+
-h|--help Prints this usage information.
|
212 |
+
--dry-run Performs a dry run displaying statistics.
|
213 |
+
--keep-sourcemap[-comment] Keeps the sourcemap special comment in the output.
|
214 |
+
--linebreak-position <pos> Splits long lines after a specific column in the output.
|
215 |
+
--memory-limit <limit> Sets the memory limit for this script.
|
216 |
+
--pcre-backtrack-limit <limit> Sets the PCRE backtrack limit for this script.
|
217 |
+
--pcre-recursion-limit <limit> Sets the PCRE recursion limit for this script.
|
218 |
+
--remove-important-comments Removes !important comments from output.
|
219 |
+
|
220 |
+
EOT;
|
221 |
+
}
|
222 |
+
}
|
lib/YuiCssMin/Minifier.php
ADDED
@@ -0,0 +1,866 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace W3TCL\YuiCssMin;
|
3 |
+
|
4 |
+
/*!
|
5 |
+
* CssMin
|
6 |
+
* Author: Tubal Martin - http://tubalmartin.me/
|
7 |
+
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
|
8 |
+
*
|
9 |
+
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
|
10 |
+
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
|
11 |
+
* Permission is hereby granted to use the PHP version under the same
|
12 |
+
* conditions as the YUICompressor.
|
13 |
+
*/
|
14 |
+
|
15 |
+
/*!
|
16 |
+
* YUI Compressor
|
17 |
+
* http://developer.yahoo.com/yui/compressor/
|
18 |
+
* Author: Julien Lecomte - http://www.julienlecomte.net/
|
19 |
+
* Copyright (c) 2013 Yahoo! Inc. All rights reserved.
|
20 |
+
* The copyrights embodied in the content of this file are licensed
|
21 |
+
* by Yahoo! Inc. under the BSD (revised) open source license.
|
22 |
+
*/
|
23 |
+
|
24 |
+
class Minifier
|
25 |
+
{
|
26 |
+
const QUERY_FRACTION = '_CSSMIN_QF_';
|
27 |
+
const COMMENT_TOKEN = '_CSSMIN_CMT_%d_';
|
28 |
+
const COMMENT_TOKEN_START = '_CSSMIN_CMT_';
|
29 |
+
const RULE_BODY_TOKEN = '_CSSMIN_RBT_%d_';
|
30 |
+
const PRESERVED_TOKEN = '_CSSMIN_PTK_%d_';
|
31 |
+
|
32 |
+
// Token lists
|
33 |
+
private $comments = array();
|
34 |
+
private $ruleBodies = array();
|
35 |
+
private $preservedTokens = array();
|
36 |
+
|
37 |
+
// Output options
|
38 |
+
private $keepImportantComments = true;
|
39 |
+
private $keepSourceMapComment = false;
|
40 |
+
private $linebreakPosition = 0;
|
41 |
+
|
42 |
+
// PHP ini limits
|
43 |
+
private $raisePhpLimits;
|
44 |
+
private $memoryLimit;
|
45 |
+
private $maxExecutionTime = 60; // 1 min
|
46 |
+
private $pcreBacktrackLimit;
|
47 |
+
private $pcreRecursionLimit;
|
48 |
+
|
49 |
+
// Color maps
|
50 |
+
private $hexToNamedColorsMap;
|
51 |
+
private $namedToHexColorsMap;
|
52 |
+
|
53 |
+
// Regexes
|
54 |
+
private $numRegex;
|
55 |
+
private $charsetRegex = '/@charset [^;]+;/Si';
|
56 |
+
private $importRegex = '/@import [^;]+;/Si';
|
57 |
+
private $namespaceRegex = '/@namespace [^;]+;/Si';
|
58 |
+
private $namedToHexColorsRegex;
|
59 |
+
private $shortenOneZeroesRegex;
|
60 |
+
private $shortenTwoZeroesRegex;
|
61 |
+
private $shortenThreeZeroesRegex;
|
62 |
+
private $shortenFourZeroesRegex;
|
63 |
+
private $unitsGroupRegex = '(?:ch|cm|em|ex|gd|in|mm|px|pt|pc|q|rem|vh|vmax|vmin|vw|%)';
|
64 |
+
|
65 |
+
public static function minify_static($css, $options = array()) {
|
66 |
+
$m = new Minifier();
|
67 |
+
return $m->run($css);
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* @param bool|int $raisePhpLimits If true, PHP settings will be raised if needed
|
72 |
+
*/
|
73 |
+
public function __construct($raisePhpLimits = true)
|
74 |
+
{
|
75 |
+
$this->raisePhpLimits = (bool) $raisePhpLimits;
|
76 |
+
$this->memoryLimit = 128 * 1048576; // 128MB in bytes
|
77 |
+
$this->pcreBacktrackLimit = 1000 * 1000;
|
78 |
+
$this->pcreRecursionLimit = 500 * 1000;
|
79 |
+
$this->hexToNamedColorsMap = Colors::getHexToNamedMap();
|
80 |
+
$this->namedToHexColorsMap = Colors::getNamedToHexMap();
|
81 |
+
$this->namedToHexColorsRegex = sprintf(
|
82 |
+
'/([:,( ])(%s)( |,|\)|;|$)/Si',
|
83 |
+
implode('|', array_keys($this->namedToHexColorsMap))
|
84 |
+
);
|
85 |
+
$this->numRegex = sprintf('-?\d*\.?\d+%s?', $this->unitsGroupRegex);
|
86 |
+
$this->setShortenZeroValuesRegexes();
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Parses & minifies the given input CSS string
|
91 |
+
* @param string $css
|
92 |
+
* @return string
|
93 |
+
*/
|
94 |
+
public function run($css = '')
|
95 |
+
{
|
96 |
+
if (empty($css) || !is_string($css)) {
|
97 |
+
return '';
|
98 |
+
}
|
99 |
+
|
100 |
+
$this->resetRunProperties();
|
101 |
+
|
102 |
+
if ($this->raisePhpLimits) {
|
103 |
+
$this->doRaisePhpLimits();
|
104 |
+
}
|
105 |
+
|
106 |
+
return $this->minify($css);
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Sets whether to keep or remove sourcemap special comment.
|
111 |
+
* Sourcemap comments are removed by default.
|
112 |
+
* @param bool $keepSourceMapComment
|
113 |
+
*/
|
114 |
+
public function keepSourceMapComment($keepSourceMapComment = true)
|
115 |
+
{
|
116 |
+
$this->keepSourceMapComment = (bool) $keepSourceMapComment;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Sets whether to keep or remove important comments.
|
121 |
+
* Important comments outside of a declaration block are kept by default.
|
122 |
+
* @param bool $removeImportantComments
|
123 |
+
*/
|
124 |
+
public function removeImportantComments($removeImportantComments = true)
|
125 |
+
{
|
126 |
+
$this->keepImportantComments = !(bool) $removeImportantComments;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Sets the approximate column after which long lines will be splitted in the output
|
131 |
+
* with a linebreak.
|
132 |
+
* @param int $position
|
133 |
+
*/
|
134 |
+
public function setLineBreakPosition($position)
|
135 |
+
{
|
136 |
+
$this->linebreakPosition = (int) $position;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Sets the memory limit for this script
|
141 |
+
* @param int|string $limit
|
142 |
+
*/
|
143 |
+
public function setMemoryLimit($limit)
|
144 |
+
{
|
145 |
+
$this->memoryLimit = Utils::normalizeInt($limit);
|
146 |
+
}
|
147 |
+
|
148 |
+
/**
|
149 |
+
* Sets the maximum execution time for this script
|
150 |
+
* @param int|string $seconds
|
151 |
+
*/
|
152 |
+
public function setMaxExecutionTime($seconds)
|
153 |
+
{
|
154 |
+
$this->maxExecutionTime = (int) $seconds;
|
155 |
+
}
|
156 |
+
|
157 |
+
/**
|
158 |
+
* Sets the PCRE backtrack limit for this script
|
159 |
+
* @param int $limit
|
160 |
+
*/
|
161 |
+
public function setPcreBacktrackLimit($limit)
|
162 |
+
{
|
163 |
+
$this->pcreBacktrackLimit = (int) $limit;
|
164 |
+
}
|
165 |
+
|
166 |
+
/**
|
167 |
+
* Sets the PCRE recursion limit for this script
|
168 |
+
* @param int $limit
|
169 |
+
*/
|
170 |
+
public function setPcreRecursionLimit($limit)
|
171 |
+
{
|
172 |
+
$this->pcreRecursionLimit = (int) $limit;
|
173 |
+
}
|
174 |
+
|
175 |
+
/**
|
176 |
+
* Builds regular expressions needed for shortening zero values
|
177 |
+
*/
|
178 |
+
private function setShortenZeroValuesRegexes()
|
179 |
+
{
|
180 |
+
$zeroRegex = '0'. $this->unitsGroupRegex;
|
181 |
+
$numOrPosRegex = '('. $this->numRegex .'|top|left|bottom|right|center) ';
|
182 |
+
$oneZeroSafeProperties = array(
|
183 |
+
'(?:line-)?height',
|
184 |
+
'(?:(?:min|max)-)?width',
|
185 |
+
'top',
|
186 |
+
'left',
|
187 |
+
'background-position',
|
188 |
+
'bottom',
|
189 |
+
'right',
|
190 |
+
'border(?:-(?:top|left|bottom|right))?(?:-width)?',
|
191 |
+
'border-(?:(?:top|bottom)-(?:left|right)-)?radius',
|
192 |
+
'column-(?:gap|width)',
|
193 |
+
'margin(?:-(?:top|left|bottom|right))?',
|
194 |
+
'outline-width',
|
195 |
+
'padding(?:-(?:top|left|bottom|right))?'
|
196 |
+
);
|
197 |
+
|
198 |
+
// First zero regex
|
199 |
+
$regex = '/(^|;)('. implode('|', $oneZeroSafeProperties) .'):%s/Si';
|
200 |
+
$this->shortenOneZeroesRegex = sprintf($regex, $zeroRegex);
|
201 |
+
|
202 |
+
// Multiple zeroes regexes
|
203 |
+
$regex = '/(^|;)(margin|padding|border-(?:width|radius)|background-position):%s/Si';
|
204 |
+
$this->shortenTwoZeroesRegex = sprintf($regex, $numOrPosRegex . $zeroRegex);
|
205 |
+
$this->shortenThreeZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $zeroRegex);
|
206 |
+
$this->shortenFourZeroesRegex = sprintf($regex, $numOrPosRegex . $numOrPosRegex . $numOrPosRegex . $zeroRegex);
|
207 |
+
}
|
208 |
+
|
209 |
+
/**
|
210 |
+
* Resets properties whose value may change between runs
|
211 |
+
*/
|
212 |
+
private function resetRunProperties()
|
213 |
+
{
|
214 |
+
$this->comments = array();
|
215 |
+
$this->ruleBodies = array();
|
216 |
+
$this->preservedTokens = array();
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Tries to configure PHP to use at least the suggested minimum settings
|
221 |
+
* @return void
|
222 |
+
*/
|
223 |
+
private function doRaisePhpLimits()
|
224 |
+
{
|
225 |
+
$phpLimits = array(
|
226 |
+
'memory_limit' => $this->memoryLimit,
|
227 |
+
'max_execution_time' => $this->maxExecutionTime,
|
228 |
+
'pcre.backtrack_limit' => $this->pcreBacktrackLimit,
|
229 |
+
'pcre.recursion_limit' => $this->pcreRecursionLimit
|
230 |
+
);
|
231 |
+
|
232 |
+
// If current settings are higher respect them.
|
233 |
+
foreach ($phpLimits as $name => $suggested) {
|
234 |
+
$current = Utils::normalizeInt(ini_get($name));
|
235 |
+
|
236 |
+
if ($current >= $suggested) {
|
237 |
+
continue;
|
238 |
+
}
|
239 |
+
|
240 |
+
// memoryLimit exception: allow -1 for "no memory limit".
|
241 |
+
if ($name === 'memory_limit' && $current === -1) {
|
242 |
+
continue;
|
243 |
+
}
|
244 |
+
|
245 |
+
// maxExecutionTime exception: allow 0 for "no memory limit".
|
246 |
+
if ($name === 'max_execution_time' && $current === 0) {
|
247 |
+
continue;
|
248 |
+
}
|
249 |
+
|
250 |
+
ini_set($name, $suggested);
|
251 |
+
}
|
252 |
+
}
|
253 |
+
|
254 |
+
/**
|
255 |
+
* Registers a preserved token
|
256 |
+
* @param string $token
|
257 |
+
* @return string The token ID string
|
258 |
+
*/
|
259 |
+
private function registerPreservedToken($token)
|
260 |
+
{
|
261 |
+
$tokenId = sprintf(self::PRESERVED_TOKEN, count($this->preservedTokens));
|
262 |
+
$this->preservedTokens[$tokenId] = $token;
|
263 |
+
return $tokenId;
|
264 |
+
}
|
265 |
+
|
266 |
+
/**
|
267 |
+
* Registers a candidate comment token
|
268 |
+
* @param string $comment
|
269 |
+
* @return string The comment token ID string
|
270 |
+
*/
|
271 |
+
private function registerCommentToken($comment)
|
272 |
+
{
|
273 |
+
$tokenId = sprintf(self::COMMENT_TOKEN, count($this->comments));
|
274 |
+
$this->comments[$tokenId] = $comment;
|
275 |
+
return $tokenId;
|
276 |
+
}
|
277 |
+
|
278 |
+
/**
|
279 |
+
* Registers a rule body token
|
280 |
+
* @param string $body the minified rule body
|
281 |
+
* @return string The rule body token ID string
|
282 |
+
*/
|
283 |
+
private function registerRuleBodyToken($body)
|
284 |
+
{
|
285 |
+
if (empty($body)) {
|
286 |
+
return '';
|
287 |
+
}
|
288 |
+
|
289 |
+
$tokenId = sprintf(self::RULE_BODY_TOKEN, count($this->ruleBodies));
|
290 |
+
$this->ruleBodies[$tokenId] = $body;
|
291 |
+
return $tokenId;
|
292 |
+
}
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Parses & minifies the given input CSS string
|
296 |
+
* @param string $css
|
297 |
+
* @return string
|
298 |
+
*/
|
299 |
+
private function minify($css)
|
300 |
+
{
|
301 |
+
// Process data urls
|
302 |
+
$css = $this->processDataUrls($css);
|
303 |
+
|
304 |
+
// Process comments
|
305 |
+
$css = preg_replace_callback(
|
306 |
+
'/(?<!\\\\)\/\*(.*?)\*(?<!\\\\)\//Ss',
|
307 |
+
array($this, 'processCommentsCallback'),
|
308 |
+
$css
|
309 |
+
);
|
310 |
+
|
311 |
+
// IE7: Process Microsoft matrix filters (whitespaces between Matrix parameters). Can contain strings inside.
|
312 |
+
$css = preg_replace_callback(
|
313 |
+
'/filter:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^)]+)\)/Ss',
|
314 |
+
array($this, 'processOldIeSpecificMatrixDefinitionCallback'),
|
315 |
+
$css
|
316 |
+
);
|
317 |
+
|
318 |
+
// Process quoted unquotable attribute selectors to unquote them. Covers most common cases.
|
319 |
+
// Likelyhood of a quoted attribute selector being a substring in a string: Very very low.
|
320 |
+
$css = preg_replace(
|
321 |
+
'/\[\s*([a-z][a-z-]+)\s*([\*\|\^\$~]?=)\s*[\'"](-?[a-z_][a-z0-9-_]+)[\'"]\s*\]/Ssi',
|
322 |
+
'[$1$2$3]',
|
323 |
+
$css
|
324 |
+
);
|
325 |
+
|
326 |
+
// Process strings so their content doesn't get accidentally minified
|
327 |
+
$css = preg_replace_callback(
|
328 |
+
'/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S",
|
329 |
+
array($this, 'processStringsCallback'),
|
330 |
+
$css
|
331 |
+
);
|
332 |
+
|
333 |
+
// Normalize all whitespace strings to single spaces. Easier to work with that way.
|
334 |
+
$css = preg_replace('/\s+/S', ' ', $css);
|
335 |
+
|
336 |
+
// Process comments
|
337 |
+
$css = $this->processComments($css);
|
338 |
+
|
339 |
+
// Process rule bodies
|
340 |
+
$css = $this->processRuleBodies($css);
|
341 |
+
|
342 |
+
// Process at-rules and selectors
|
343 |
+
$css = $this->processAtRulesAndSelectors($css);
|
344 |
+
|
345 |
+
// Restore preserved rule bodies before splitting
|
346 |
+
$css = strtr($css, $this->ruleBodies);
|
347 |
+
|
348 |
+
// Some source control tools don't like it when files containing lines longer
|
349 |
+
// than, say 8000 characters, are checked in. The linebreak option is used in
|
350 |
+
// that case to split long lines after a specific column.
|
351 |
+
if ($this->linebreakPosition > 0) {
|
352 |
+
$l = strlen($css);
|
353 |
+
$offset = $this->linebreakPosition;
|
354 |
+
while (preg_match('/(?<!\\\\)\}(?!\n)/S', $css, $matches, PREG_OFFSET_CAPTURE, $offset)) {
|
355 |
+
$matchIndex = $matches[0][1];
|
356 |
+
$css = substr_replace($css, "\n", $matchIndex + 1, 0);
|
357 |
+
$offset = $matchIndex + 2 + $this->linebreakPosition;
|
358 |
+
$l += 1;
|
359 |
+
if ($offset > $l) {
|
360 |
+
break;
|
361 |
+
}
|
362 |
+
}
|
363 |
+
}
|
364 |
+
|
365 |
+
// Restore preserved comments and strings
|
366 |
+
$css = strtr($css, $this->preservedTokens);
|
367 |
+
|
368 |
+
return trim($css);
|
369 |
+
}
|
370 |
+
|
371 |
+
/**
|
372 |
+
* Searches & replaces all data urls with tokens before we start compressing,
|
373 |
+
* to avoid performance issues running some of the subsequent regexes against large string chunks.
|
374 |
+
* @param string $css
|
375 |
+
* @return string
|
376 |
+
*/
|
377 |
+
private function processDataUrls($css)
|
378 |
+
{
|
379 |
+
$ret = '';
|
380 |
+
$searchOffset = $substrOffset = 0;
|
381 |
+
|
382 |
+
// Since we need to account for non-base64 data urls, we need to handle
|
383 |
+
// ' and ) being part of the data string.
|
384 |
+
while (preg_match('/url\(\s*(["\']?)data:/Si', $css, $m, PREG_OFFSET_CAPTURE, $searchOffset)) {
|
385 |
+
$matchStartIndex = $m[0][1];
|
386 |
+
$dataStartIndex = $matchStartIndex + 4; // url( length
|
387 |
+
$searchOffset = $matchStartIndex + strlen($m[0][0]);
|
388 |
+
$terminator = $m[1][0]; // ', " or empty (not quoted)
|
389 |
+
$terminatorRegex = '/(?<!\\\\)'. (strlen($terminator) === 0 ? '' : $terminator.'\s*') .'(\))/S';
|
390 |
+
|
391 |
+
$ret .= substr($css, $substrOffset, $matchStartIndex - $substrOffset);
|
392 |
+
|
393 |
+
// Terminator found
|
394 |
+
if (preg_match($terminatorRegex, $css, $matches, PREG_OFFSET_CAPTURE, $searchOffset)) {
|
395 |
+
$matchEndIndex = $matches[1][1];
|
396 |
+
$searchOffset = $matchEndIndex + 1;
|
397 |
+
$token = substr($css, $dataStartIndex, $matchEndIndex - $dataStartIndex);
|
398 |
+
|
399 |
+
// Remove all spaces only for base64 encoded URLs.
|
400 |
+
if (stripos($token, 'base64,') !== false) {
|
401 |
+
$token = preg_replace('/\s+/S', '', $token);
|
402 |
+
}
|
403 |
+
|
404 |
+
$ret .= 'url('. $this->registerPreservedToken(trim($token)) .')';
|
405 |
+
// No end terminator found, re-add the whole match. Should we throw/warn here?
|
406 |
+
} else {
|
407 |
+
$ret .= substr($css, $matchStartIndex, $searchOffset - $matchStartIndex);
|
408 |
+
}
|
409 |
+
|
410 |
+
$substrOffset = $searchOffset;
|
411 |
+
}
|
412 |
+
|
413 |
+
$ret .= substr($css, $substrOffset);
|
414 |
+
|
415 |
+
return $ret;
|
416 |
+
}
|
417 |
+
|
418 |
+
/**
|
419 |
+
* Registers all comments found as candidates to be preserved.
|
420 |
+
* @param array $matches
|
421 |
+
* @return string
|
422 |
+
*/
|
423 |
+
private function processCommentsCallback($matches)
|
424 |
+
{
|
425 |
+
return '/*'. $this->registerCommentToken($matches[1]) .'*/';
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Preserves old IE Matrix string definition
|
430 |
+
* @param array $matches
|
431 |
+
* @return string
|
432 |
+
*/
|
433 |
+
private function processOldIeSpecificMatrixDefinitionCallback($matches)
|
434 |
+
{
|
435 |
+
return 'filter:progid:DXImageTransform.Microsoft.Matrix('. $this->registerPreservedToken($matches[1]) .')';
|
436 |
+
}
|
437 |
+
|
438 |
+
/**
|
439 |
+
* Preserves strings found
|
440 |
+
* @param array $matches
|
441 |
+
* @return string
|
442 |
+
*/
|
443 |
+
private function processStringsCallback($matches)
|
444 |
+
{
|
445 |
+
$match = $matches[0];
|
446 |
+
$quote = substr($match, 0, 1);
|
447 |
+
$match = substr($match, 1, -1);
|
448 |
+
|
449 |
+
// maybe the string contains a comment-like substring?
|
450 |
+
// one, maybe more? put'em back then
|
451 |
+
if (strpos($match, self::COMMENT_TOKEN_START) !== false) {
|
452 |
+
$match = strtr($match, $this->comments);
|
453 |
+
}
|
454 |
+
|
455 |
+
// minify alpha opacity in filter strings
|
456 |
+
$match = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $match);
|
457 |
+
|
458 |
+
return $quote . $this->registerPreservedToken($match) . $quote;
|
459 |
+
}
|
460 |
+
|
461 |
+
/**
|
462 |
+
* Preserves or removes comments found.
|
463 |
+
* @param string $css
|
464 |
+
* @return string
|
465 |
+
*/
|
466 |
+
private function processComments($css)
|
467 |
+
{
|
468 |
+
foreach ($this->comments as $commentId => $comment) {
|
469 |
+
$commentIdString = '/*'. $commentId .'*/';
|
470 |
+
|
471 |
+
// ! in the first position of the comment means preserve
|
472 |
+
// so push to the preserved tokens keeping the !
|
473 |
+
if ($this->keepImportantComments && strpos($comment, '!') === 0) {
|
474 |
+
$preservedTokenId = $this->registerPreservedToken($comment);
|
475 |
+
// Put new lines before and after /*! important comments
|
476 |
+
$css = str_replace($commentIdString, "\n/*$preservedTokenId*/\n", $css);
|
477 |
+
continue;
|
478 |
+
}
|
479 |
+
|
480 |
+
// # sourceMappingURL= in the first position of the comment means sourcemap
|
481 |
+
// so push to the preserved tokens if {$this->keepSourceMapComment} is truthy.
|
482 |
+
if ($this->keepSourceMapComment && strpos($comment, '# sourceMappingURL=') === 0) {
|
483 |
+
$preservedTokenId = $this->registerPreservedToken($comment);
|
484 |
+
// Add new line before the sourcemap comment
|
485 |
+
$css = str_replace($commentIdString, "\n/*$preservedTokenId*/", $css);
|
486 |
+
continue;
|
487 |
+
}
|
488 |
+
|
489 |
+
// Keep empty comments after child selectors (IE7 hack)
|
490 |
+
// e.g. html >/**/ body
|
491 |
+
if (strlen($comment) === 0 && strpos($css, '>/*'.$commentId) !== false) {
|
492 |
+
$css = str_replace($commentId, $this->registerPreservedToken(''), $css);
|
493 |
+
continue;
|
494 |
+
}
|
495 |
+
|
496 |
+
// in all other cases kill the comment
|
497 |
+
$css = str_replace($commentIdString, '', $css);
|
498 |
+
}
|
499 |
+
|
500 |
+
// Normalize whitespace again
|
501 |
+
$css = preg_replace('/ +/S', ' ', $css);
|
502 |
+
|
503 |
+
return $css;
|
504 |
+
}
|
505 |
+
|
506 |
+
/**
|
507 |
+
* Finds, minifies & preserves all rule bodies.
|
508 |
+
* @param string $css the whole stylesheet.
|
509 |
+
* @return string
|
510 |
+
*/
|
511 |
+
private function processRuleBodies($css)
|
512 |
+
{
|
513 |
+
$ret = '';
|
514 |
+
$searchOffset = $substrOffset = 0;
|
515 |
+
|
516 |
+
while (($blockStartPos = strpos($css, '{', $searchOffset)) !== false) {
|
517 |
+
$blockEndPos = strpos($css, '}', $blockStartPos);
|
518 |
+
$nextBlockStartPos = strpos($css, '{', $blockStartPos + 1);
|
519 |
+
$ret .= substr($css, $substrOffset, $blockStartPos - $substrOffset);
|
520 |
+
|
521 |
+
if ($nextBlockStartPos !== false && $nextBlockStartPos < $blockEndPos) {
|
522 |
+
$ret .= substr($css, $blockStartPos, $nextBlockStartPos - $blockStartPos);
|
523 |
+
$searchOffset = $nextBlockStartPos;
|
524 |
+
} else {
|
525 |
+
$ruleBody = substr($css, $blockStartPos + 1, $blockEndPos - $blockStartPos - 1);
|
526 |
+
$ruleBodyToken = $this->registerRuleBodyToken($this->processRuleBody($ruleBody));
|
527 |
+
$ret .= '{'. $ruleBodyToken .'}';
|
528 |
+
$searchOffset = $blockEndPos + 1;
|
529 |
+
}
|
530 |
+
|
531 |
+
$substrOffset = $searchOffset;
|
532 |
+
}
|
533 |
+
|
534 |
+
$ret .= substr($css, $substrOffset);
|
535 |
+
|
536 |
+
return $ret;
|
537 |
+
}
|
538 |
+
|
539 |
+
/**
|
540 |
+
* Compresses non-group rule bodies.
|
541 |
+
* @param string $body The rule body without curly braces
|
542 |
+
* @return string
|
543 |
+
*/
|
544 |
+
private function processRuleBody($body)
|
545 |
+
{
|
546 |
+
$body = trim($body);
|
547 |
+
|
548 |
+
// Remove spaces before the things that should not have spaces before them.
|
549 |
+
$body = preg_replace('/ ([:=,)*\/;\n])/S', '$1', $body);
|
550 |
+
|
551 |
+
// Remove the spaces after the things that should not have spaces after them.
|
552 |
+
$body = preg_replace('/([:=,(*\/!;\n]) /S', '$1', $body);
|
553 |
+
|
554 |
+
// Replace multiple semi-colons in a row by a single one
|
555 |
+
$body = preg_replace('/;;+/S', ';', $body);
|
556 |
+
|
557 |
+
// Remove semicolon before closing brace except when:
|
558 |
+
// - The last property is prefixed with a `*` (lte IE7 hack) to avoid issues on Symbian S60 3.x browsers.
|
559 |
+
if (!preg_match('/\*[a-z0-9-]+:[^;]+;$/Si', $body)) {
|
560 |
+
$body = rtrim($body, ';');
|
561 |
+
}
|
562 |
+
|
563 |
+
// Remove important comments inside a rule body (because they make no sense here).
|
564 |
+
if (strpos($body, '/*') !== false) {
|
565 |
+
$body = preg_replace('/\n?\/\*[A-Z0-9_]+\*\/\n?/S', '', $body);
|
566 |
+
}
|
567 |
+
|
568 |
+
// Empty rule body? Exit :)
|
569 |
+
if (empty($body)) {
|
570 |
+
return '';
|
571 |
+
}
|
572 |
+
|
573 |
+
// Shorten font-weight values
|
574 |
+
$body = preg_replace(
|
575 |
+
array('/(font-weight:)bold\b/Si', '/(font-weight:)normal\b/Si'),
|
576 |
+
array('${1}700', '${1}400'),
|
577 |
+
$body
|
578 |
+
);
|
579 |
+
|
580 |
+
// Shorten background property
|
581 |
+
$body = preg_replace('/(background:)(?:none|transparent)( !|;|$)/Si', '${1}0 0$2', $body);
|
582 |
+
|
583 |
+
// Shorten opacity IE filter
|
584 |
+
$body = str_ireplace('progid:DXImageTransform.Microsoft.Alpha(Opacity=', 'alpha(opacity=', $body);
|
585 |
+
|
586 |
+
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
|
587 |
+
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
|
588 |
+
// This makes it more likely that it'll get further compressed in the next step.
|
589 |
+
$body = preg_replace_callback(
|
590 |
+
'/(rgb|hsl)\(([0-9,.% -]+)\)(.|$)/Si',
|
591 |
+
array($this, 'shortenHslAndRgbToHexCallback'),
|
592 |
+
$body
|
593 |
+
);
|
594 |
+
|
595 |
+
// Shorten colors from #AABBCC to #ABC or shorter color name:
|
596 |
+
// - Look for hex colors which don't have a "=" in front of them (to avoid MSIE filters)
|
597 |
+
$body = preg_replace_callback(
|
598 |
+
'/(?<!=)#([0-9a-f]{3,6})( |,|\)|;|$)/Si',
|
599 |
+
array($this, 'shortenHexColorsCallback'),
|
600 |
+
$body
|
601 |
+
);
|
602 |
+
|
603 |
+
// Shorten long named colors with a shorter HEX counterpart: white -> #fff.
|
604 |
+
// Run at least 2 times to cover most cases
|
605 |
+
$body = preg_replace_callback(
|
606 |
+
array($this->namedToHexColorsRegex, $this->namedToHexColorsRegex),
|
607 |
+
array($this, 'shortenNamedColorsCallback'),
|
608 |
+
$body
|
609 |
+
);
|
610 |
+
|
611 |
+
// Replace positive sign from numbers before the leading space is removed.
|
612 |
+
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
|
613 |
+
$body = preg_replace('/([ :,(])\+(\.?\d+)/S', '$1$2', $body);
|
614 |
+
|
615 |
+
// shorten ms to s
|
616 |
+
$body = preg_replace_callback('/([ :,(])(-?)(\d{3,})ms/Si', function ($matches) {
|
617 |
+
return $matches[1] . $matches[2] . ((int) $matches[3] / 1000) .'s';
|
618 |
+
}, $body);
|
619 |
+
|
620 |
+
// Remove leading zeros from integer and float numbers.
|
621 |
+
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
|
622 |
+
$body = preg_replace('/([ :,(])(-?)0+([1-9]?\.?\d+)/S', '$1$2$3', $body);
|
623 |
+
|
624 |
+
// Remove trailing zeros from float numbers.
|
625 |
+
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
|
626 |
+
$body = preg_replace('/([ :,(])(-?\d?\.\d+?)0+([^\d])/S', '$1$2$3', $body);
|
627 |
+
|
628 |
+
// Remove trailing .0 -> -9.0 to -9
|
629 |
+
$body = preg_replace('/([ :,(])(-?\d+)\.0([^\d])/S', '$1$2$3', $body);
|
630 |
+
|
631 |
+
// Replace 0 length numbers with 0
|
632 |
+
$body = preg_replace('/([ :,(])-?\.?0+([^\d])/S', '${1}0$2', $body);
|
633 |
+
|
634 |
+
// Shorten zero values for safe properties only
|
635 |
+
$body = preg_replace(
|
636 |
+
array(
|
637 |
+
$this->shortenOneZeroesRegex,
|
638 |
+
$this->shortenTwoZeroesRegex,
|
639 |
+
$this->shortenThreeZeroesRegex,
|
640 |
+
$this->shortenFourZeroesRegex
|
641 |
+
),
|
642 |
+
array(
|
643 |
+
'$1$2:0',
|
644 |
+
'$1$2:$3 0',
|
645 |
+
'$1$2:$3 $4 0',
|
646 |
+
'$1$2:$3 $4 $5 0'
|
647 |
+
),
|
648 |
+
$body
|
649 |
+
);
|
650 |
+
|
651 |
+
// Replace 0 0 0; or 0 0 0 0; with 0 0 for background-position property.
|
652 |
+
$body = preg_replace('/(background-position):0(?: 0){2,3}( !|;|$)/Si', '$1:0 0$2', $body);
|
653 |
+
|
654 |
+
// Shorten suitable shorthand properties with repeated values
|
655 |
+
$body = preg_replace(
|
656 |
+
array(
|
657 |
+
'/(margin|padding|border-(?:width|radius)):('.$this->numRegex.')(?: \2)+( !|;|$)/Si',
|
658 |
+
'/(border-(?:style|color)):([#a-z0-9]+)(?: \2)+( !|;|$)/Si'
|
659 |
+
),
|
660 |
+
'$1:$2$3',
|
661 |
+
$body
|
662 |
+
);
|
663 |
+
$body = preg_replace(
|
664 |
+
array(
|
665 |
+
'/(margin|padding|border-(?:width|radius)):'.
|
666 |
+
'('.$this->numRegex.') ('.$this->numRegex.') \2 \3( !|;|$)/Si',
|
667 |
+
'/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) \2 \3( !|;|$)/Si'
|
668 |
+
),
|
669 |
+
'$1:$2 $3$4',
|
670 |
+
$body
|
671 |
+
);
|
672 |
+
$body = preg_replace(
|
673 |
+
array(
|
674 |
+
'/(margin|padding|border-(?:width|radius)):'.
|
675 |
+
'('.$this->numRegex.') ('.$this->numRegex.') ('.$this->numRegex.') \3( !|;|$)/Si',
|
676 |
+
'/(border-(?:style|color)):([#a-z0-9]+) ([#a-z0-9]+) ([#a-z0-9]+) \3( !|;|$)/Si'
|
677 |
+
),
|
678 |
+
'$1:$2 $3 $4$5',
|
679 |
+
$body
|
680 |
+
);
|
681 |
+
|
682 |
+
// Lowercase some common functions that can be values
|
683 |
+
$body = preg_replace_callback(
|
684 |
+
'/(?:attr|blur|brightness|circle|contrast|cubic-bezier|drop-shadow|ellipse|from|grayscale|'.
|
685 |
+
'hsla?|hue-rotate|inset|invert|local|minmax|opacity|perspective|polygon|rgba?|rect|repeat|saturate|sepia|'.
|
686 |
+
'steps|to|url|var|-webkit-gradient|'.
|
687 |
+
'(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|(?:repeating-)?(?:linear|radial)-gradient))\(/Si',
|
688 |
+
array($this, 'strtolowerCallback'),
|
689 |
+
$body
|
690 |
+
);
|
691 |
+
|
692 |
+
// Lowercase all uppercase properties
|
693 |
+
$body = preg_replace_callback('/(?:^|;)[A-Z-]+:/S', array($this, 'strtolowerCallback'), $body);
|
694 |
+
|
695 |
+
return $body;
|
696 |
+
}
|
697 |
+
|
698 |
+
/**
|
699 |
+
* Compresses At-rules and selectors.
|
700 |
+
* @param string $css the whole stylesheet with rule bodies tokenized.
|
701 |
+
* @return string
|
702 |
+
*/
|
703 |
+
private function processAtRulesAndSelectors($css)
|
704 |
+
{
|
705 |
+
$charset = '';
|
706 |
+
$imports = '';
|
707 |
+
$namespaces = '';
|
708 |
+
|
709 |
+
// Remove spaces before the things that should not have spaces before them.
|
710 |
+
$css = preg_replace('/ ([@{};>+)\]~=,\/\n])/S', '$1', $css);
|
711 |
+
|
712 |
+
// Remove the spaces after the things that should not have spaces after them.
|
713 |
+
$css = preg_replace('/([{}:;>+(\[~=,\/\n]) /S', '$1', $css);
|
714 |
+
|
715 |
+
// Shorten shortable double colon (CSS3) pseudo-elements to single colon (CSS2)
|
716 |
+
$css = preg_replace('/::(before|after|first-(?:line|letter))(\{|,)/Si', ':$1$2', $css);
|
717 |
+
|
718 |
+
// Retain space for special IE6 cases
|
719 |
+
$css = preg_replace_callback('/:first-(line|letter)(\{|,)/Si', function ($matches) {
|
720 |
+
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
|
721 |
+
}, $css);
|
722 |
+
|
723 |
+
// Find a fraction that may used in some @media queries such as: (min-aspect-ratio: 1/1)
|
724 |
+
// Add token to add the "/" back in later
|
725 |
+
$css = preg_replace('/\(([a-z-]+):([0-9]+)\/([0-9]+)\)/Si', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
|
726 |
+
|
727 |
+
// Remove empty rule blocks up to 2 levels deep.
|
728 |
+
$css = preg_replace(array_fill(0, 2, '/(\{)[^{};\/\n]+\{\}/S'), '$1', $css);
|
729 |
+
$css = preg_replace('/[^{};\/\n]+\{\}/S', '', $css);
|
730 |
+
|
731 |
+
// Two important comments next to each other? Remove extra newline.
|
732 |
+
if ($this->keepImportantComments) {
|
733 |
+
$css = str_replace("\n\n", "\n", $css);
|
734 |
+
}
|
735 |
+
|
736 |
+
// Restore fraction
|
737 |
+
$css = str_replace(self::QUERY_FRACTION, '/', $css);
|
738 |
+
|
739 |
+
// Lowercase some popular @directives
|
740 |
+
$css = preg_replace_callback(
|
741 |
+
'/(?<!\\\\)@(?:charset|document|font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframes|media|'.
|
742 |
+
'namespace|page|supports|viewport)/Si',
|
743 |
+
array($this, 'strtolowerCallback'),
|
744 |
+
$css
|
745 |
+
);
|
746 |
+
|
747 |
+
// Lowercase some popular media types
|
748 |
+
$css = preg_replace_callback(
|
749 |
+
'/[ ,](?:all|aural|braille|handheld|print|projection|screen|tty|tv|embossed|speech)[ ,;{]/Si',
|
750 |
+
array($this, 'strtolowerCallback'),
|
751 |
+
$css
|
752 |
+
);
|
753 |
+
|
754 |
+
// Lowercase some common pseudo-classes & pseudo-elements
|
755 |
+
$css = preg_replace_callback(
|
756 |
+
'/(?<!\\\\):(?:active|after|before|checked|default|disabled|empty|enabled|first-(?:child|of-type)|'.
|
757 |
+
'focus(?:-within)?|hover|indeterminate|in-range|invalid|lang\(|last-(?:child|of-type)|left|link|not\(|'.
|
758 |
+
'nth-(?:child|of-type)\(|nth-last-(?:child|of-type)\(|only-(?:child|of-type)|optional|out-of-range|'.
|
759 |
+
'read-(?:only|write)|required|right|root|:selection|target|valid|visited)/Si',
|
760 |
+
array($this, 'strtolowerCallback'),
|
761 |
+
$css
|
762 |
+
);
|
763 |
+
|
764 |
+
// @charset handling
|
765 |
+
if (preg_match($this->charsetRegex, $css, $matches)) {
|
766 |
+
// Keep the first @charset at-rule found
|
767 |
+
$charset = $matches[0];
|
768 |
+
// Delete all @charset at-rules
|
769 |
+
$css = preg_replace($this->charsetRegex, '', $css);
|
770 |
+
}
|
771 |
+
|
772 |
+
// @import handling
|
773 |
+
$css = preg_replace_callback($this->importRegex, function ($matches) use (&$imports) {
|
774 |
+
// Keep all @import at-rules found for later
|
775 |
+
$imports .= $matches[0];
|
776 |
+
// Delete all @import at-rules
|
777 |
+
return '';
|
778 |
+
}, $css);
|
779 |
+
|
780 |
+
// @namespace handling
|
781 |
+
$css = preg_replace_callback($this->namespaceRegex, function ($matches) use (&$namespaces) {
|
782 |
+
// Keep all @namespace at-rules found for later
|
783 |
+
$namespaces .= $matches[0];
|
784 |
+
// Delete all @namespace at-rules
|
785 |
+
return '';
|
786 |
+
}, $css);
|
787 |
+
|
788 |
+
// Order critical at-rules:
|
789 |
+
// 1. @charset first
|
790 |
+
// 2. @imports below @charset
|
791 |
+
// 3. @namespaces below @imports
|
792 |
+
$css = $charset . $imports . $namespaces . $css;
|
793 |
+
|
794 |
+
return $css;
|
795 |
+
}
|
796 |
+
|
797 |
+
/**
|
798 |
+
* Converts hsl() & rgb() colors to HEX format.
|
799 |
+
* @param $matches
|
800 |
+
* @return string
|
801 |
+
*/
|
802 |
+
private function shortenHslAndRgbToHexCallback($matches)
|
803 |
+
{
|
804 |
+
$type = $matches[1];
|
805 |
+
$values = explode(',', $matches[2]);
|
806 |
+
$terminator = $matches[3];
|
807 |
+
|
808 |
+
if ($type === 'hsl') {
|
809 |
+
$values = Utils::hslToRgb($values);
|
810 |
+
}
|
811 |
+
|
812 |
+
$hexColors = Utils::rgbToHex($values);
|
813 |
+
|
814 |
+
// Restore space after rgb() or hsl() function in some cases such as:
|
815 |
+
// background-image: linear-gradient(to bottom, rgb(210,180,140) 10%, rgb(255,0,0) 90%);
|
816 |
+
if (!empty($terminator) && !preg_match('/[ ,);]/S', $terminator)) {
|
817 |
+
$terminator = ' '. $terminator;
|
818 |
+
}
|
819 |
+
|
820 |
+
return '#'. implode('', $hexColors) . $terminator;
|
821 |
+
}
|
822 |
+
|
823 |
+
/**
|
824 |
+
* Compresses HEX color values of the form #AABBCC to #ABC or short color name.
|
825 |
+
* @param $matches
|
826 |
+
* @return string
|
827 |
+
*/
|
828 |
+
private function shortenHexColorsCallback($matches)
|
829 |
+
{
|
830 |
+
$hex = $matches[1];
|
831 |
+
|
832 |
+
// Shorten suitable 6 chars HEX colors
|
833 |
+
if (strlen($hex) === 6 && preg_match('/^([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3$/Si', $hex, $m)) {
|
834 |
+
$hex = $m[1] . $m[2] . $m[3];
|
835 |
+
}
|
836 |
+
|
837 |
+
// Lowercase
|
838 |
+
$hex = '#'. strtolower($hex);
|
839 |
+
|
840 |
+
// Replace Hex colors with shorter color names
|
841 |
+
$color = array_key_exists($hex, $this->hexToNamedColorsMap) ? $this->hexToNamedColorsMap[$hex] : $hex;
|
842 |
+
|
843 |
+
return $color . $matches[2];
|
844 |
+
}
|
845 |
+
|
846 |
+
/**
|
847 |
+
* Shortens all named colors with a shorter HEX counterpart for a set of safe properties
|
848 |
+
* e.g. white -> #fff
|
849 |
+
* @param array $matches
|
850 |
+
* @return string
|
851 |
+
*/
|
852 |
+
private function shortenNamedColorsCallback($matches)
|
853 |
+
{
|
854 |
+
return $matches[1] . $this->namedToHexColorsMap[strtolower($matches[2])] . $matches[3];
|
855 |
+
}
|
856 |
+
|
857 |
+
/**
|
858 |
+
* Makes a string lowercase
|
859 |
+
* @param array $matches
|
860 |
+
* @return string
|
861 |
+
*/
|
862 |
+
private function strtolowerCallback($matches)
|
863 |
+
{
|
864 |
+
return strtolower($matches[0]);
|
865 |
+
}
|
866 |
+
}
|
lib/YuiCssMin/Utils.php
ADDED
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace W3TCL\YuiCssMin;
|
3 |
+
|
4 |
+
class Utils
|
5 |
+
{
|
6 |
+
/**
|
7 |
+
* Clamps a number between a minimum and a maximum value.
|
8 |
+
* @param int|float $n the number to clamp
|
9 |
+
* @param int|float $min the lower end number allowed
|
10 |
+
* @param int|float $max the higher end number allowed
|
11 |
+
* @return int|float
|
12 |
+
*/
|
13 |
+
public static function clampNumber($n, $min, $max)
|
14 |
+
{
|
15 |
+
return min(max($n, $min), $max);
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Clamps a RGB color number outside the sRGB color space
|
20 |
+
* @param int|float $n the number to clamp
|
21 |
+
* @return int|float
|
22 |
+
*/
|
23 |
+
public static function clampNumberSrgb($n)
|
24 |
+
{
|
25 |
+
return self::clampNumber($n, 0, 255);
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Converts a HSL color into a RGB color
|
30 |
+
* @param array $hslValues
|
31 |
+
* @return array
|
32 |
+
*/
|
33 |
+
public static function hslToRgb($hslValues)
|
34 |
+
{
|
35 |
+
$h = floatval($hslValues[0]);
|
36 |
+
$s = floatval(str_replace('%', '', $hslValues[1]));
|
37 |
+
$l = floatval(str_replace('%', '', $hslValues[2]));
|
38 |
+
|
39 |
+
// Wrap and clamp, then fraction!
|
40 |
+
$h = ((($h % 360) + 360) % 360) / 360;
|
41 |
+
$s = self::clampNumber($s, 0, 100) / 100;
|
42 |
+
$l = self::clampNumber($l, 0, 100) / 100;
|
43 |
+
|
44 |
+
if ($s == 0) {
|
45 |
+
$r = $g = $b = self::roundNumber(255 * $l);
|
46 |
+
} else {
|
47 |
+
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
|
48 |
+
$v1 = (2 * $l) - $v2;
|
49 |
+
$r = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h + (1/3)));
|
50 |
+
$g = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h));
|
51 |
+
$b = self::roundNumber(255 * self::hueToRgb($v1, $v2, $h - (1/3)));
|
52 |
+
}
|
53 |
+
|
54 |
+
return array($r, $g, $b);
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Tests and selects the correct formula for each RGB color channel
|
59 |
+
* @param $v1
|
60 |
+
* @param $v2
|
61 |
+
* @param $vh
|
62 |
+
* @return mixed
|
63 |
+
*/
|
64 |
+
public static function hueToRgb($v1, $v2, $vh)
|
65 |
+
{
|
66 |
+
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
|
67 |
+
|
68 |
+
if ($vh * 6 < 1) {
|
69 |
+
return $v1 + ($v2 - $v1) * 6 * $vh;
|
70 |
+
}
|
71 |
+
|
72 |
+
if ($vh * 2 < 1) {
|
73 |
+
return $v2;
|
74 |
+
}
|
75 |
+
|
76 |
+
if ($vh * 3 < 2) {
|
77 |
+
return $v1 + ($v2 - $v1) * ((2 / 3) - $vh) * 6;
|
78 |
+
}
|
79 |
+
|
80 |
+
return $v1;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Convert strings like "64M" or "30" to int values
|
85 |
+
* @param mixed $size
|
86 |
+
* @return int
|
87 |
+
*/
|
88 |
+
public static function normalizeInt($size)
|
89 |
+
{
|
90 |
+
if (is_string($size)) {
|
91 |
+
$letter = substr($size, -1);
|
92 |
+
$size = intval($size);
|
93 |
+
switch ($letter) {
|
94 |
+
case 'M':
|
95 |
+
case 'm':
|
96 |
+
return (int) $size * 1048576;
|
97 |
+
case 'K':
|
98 |
+
case 'k':
|
99 |
+
return (int) $size * 1024;
|
100 |
+
case 'G':
|
101 |
+
case 'g':
|
102 |
+
return (int) $size * 1073741824;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
return (int) $size;
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Converts a string containing and RGB percentage value into a RGB integer value i.e. '90%' -> 229.5
|
110 |
+
* @param $rgbPercentage
|
111 |
+
* @return int
|
112 |
+
*/
|
113 |
+
public static function rgbPercentageToRgbInteger($rgbPercentage)
|
114 |
+
{
|
115 |
+
if (strpos($rgbPercentage, '%') !== false) {
|
116 |
+
$rgbPercentage = self::roundNumber(floatval(str_replace('%', '', $rgbPercentage)) * 2.55);
|
117 |
+
}
|
118 |
+
|
119 |
+
return intval($rgbPercentage, 10);
|
120 |
+
}
|
121 |
+
|
122 |
+
/**
|
123 |
+
* Converts a RGB color into a HEX color
|
124 |
+
* @param array $rgbColors
|
125 |
+
* @return array
|
126 |
+
*/
|
127 |
+
public static function rgbToHex($rgbColors)
|
128 |
+
{
|
129 |
+
$hexColors = array();
|
130 |
+
|
131 |
+
// Values outside the sRGB color space should be clipped (0-255)
|
132 |
+
for ($i = 0, $l = count($rgbColors); $i < $l; $i++) {
|
133 |
+
$hexColors[$i] = sprintf("%02x", self::clampNumberSrgb(self::rgbPercentageToRgbInteger($rgbColors[$i])));
|
134 |
+
}
|
135 |
+
|
136 |
+
return $hexColors;
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Rounds a number to its closest integer
|
141 |
+
* @param $n
|
142 |
+
* @return int
|
143 |
+
*/
|
144 |
+
public static function roundNumber($n)
|
145 |
+
{
|
146 |
+
return intval(round(floatval($n)), 10);
|
147 |
+
}
|
148 |
+
}
|
pub/css/options.css
CHANGED
@@ -374,7 +374,7 @@ input.w3tc-error, textarea.w3tc-error {
|
|
374 |
display:none;
|
375 |
}
|
376 |
|
377 |
-
#cdn_netdna_authorization_key, #cdn_maxcdn_authorization_key
|
378 |
margin-bottom: 3px;
|
379 |
}
|
380 |
|
@@ -693,3 +693,16 @@ table:not(.w3tc-pro-feature) .w3tc-gopro:after {
|
|
693 |
/* Same color as "update plugin count" in WP Dashboard left nav. */
|
694 |
background: #ca4a1f;;
|
695 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
374 |
display:none;
|
375 |
}
|
376 |
|
377 |
+
#cdn_netdna_authorization_key, #cdn_maxcdn_authorization_key {
|
378 |
margin-bottom: 3px;
|
379 |
}
|
380 |
|
693 |
/* Same color as "update plugin count" in WP Dashboard left nav. */
|
694 |
background: #ca4a1f;;
|
695 |
}
|
696 |
+
|
697 |
+
/* Learn more links */
|
698 |
+
a > span.dashicons-external {
|
699 |
+
text-decoration: none;
|
700 |
+
}
|
701 |
+
|
702 |
+
.w3tc-control-after {
|
703 |
+
vertical-align: middle;
|
704 |
+
}
|
705 |
+
|
706 |
+
td .w3tc-control-after span {
|
707 |
+
vertical-align: text-top;
|
708 |
+
}
|
readme.txt
CHANGED
@@ -2,8 +2,8 @@
|
|
2 |
Contributors: boldgrid, fredericktownes, maxicusc, gidomanders, bwmarkle, harryjackson1221, joemoto
|
3 |
Tags: seo, cache, optimize, pagespeed, performance, caching, compression, maxcdn, nginx, varnish, redis, new relic, aws, amazon web services, s3, cloudfront, rackspace, cloudflare, azure, apache
|
4 |
Requires at least: 3.8
|
5 |
-
Tested up to: 5.
|
6 |
-
Stable tag: 2.1.
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
@@ -275,6 +275,15 @@ Please reach out to all of these people and support their projects if you're so
|
|
275 |
|
276 |
== Changelog ==
|
277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
= 2.1.0 =
|
279 |
* Feature: Added a Feature Showcase to highlight new and existing features
|
280 |
* Update: Consolidated cache groups settings pages
|
2 |
Contributors: boldgrid, fredericktownes, maxicusc, gidomanders, bwmarkle, harryjackson1221, joemoto
|
3 |
Tags: seo, cache, optimize, pagespeed, performance, caching, compression, maxcdn, nginx, varnish, redis, new relic, aws, amazon web services, s3, cloudfront, rackspace, cloudflare, azure, apache
|
4 |
Requires at least: 3.8
|
5 |
+
Tested up to: 5.7
|
6 |
+
Stable tag: 2.1.1
|
7 |
License: GPLv2 or later
|
8 |
License URI: http://www.gnu.org/licenses/gpl-2.0.html
|
9 |
|
275 |
|
276 |
== Changelog ==
|
277 |
|
278 |
+
= 2.1.1 =
|
279 |
+
* Fix: Move Minify library to a namespace to avoid conflicts with other plugins
|
280 |
+
* Fix: Check for AWS before loading functions
|
281 |
+
* Fix: Update Minify ClosureCompiler base URL; use HTTPS
|
282 |
+
* Fix: Corrected getting the network siteurl
|
283 |
+
* Fix: Prevent PHP warning in CurlFactory
|
284 |
+
* Update: Added information links to general minify options
|
285 |
+
* Update: Added video/ogg support for browser caching
|
286 |
+
|
287 |
= 2.1.0 =
|
288 |
* Feature: Added a Feature Showcase to highlight new and existing features
|
289 |
* Update: Consolidated cache groups settings pages
|
w3-total-cache-api.php
CHANGED
@@ -5,7 +5,7 @@ if ( !defined( 'ABSPATH' ) ) {
|
|
5 |
}
|
6 |
|
7 |
define( 'W3TC', true );
|
8 |
-
define( 'W3TC_VERSION', '2.1.
|
9 |
define( 'W3TC_POWERED_BY', 'W3 Total Cache' );
|
10 |
define( 'W3TC_EMAIL', 'w3tc@w3-edge.com' );
|
11 |
define( 'W3TC_TEXT_DOMAIN', 'w3-total-cache' );
|
@@ -144,18 +144,29 @@ $w3_late_init = false;
|
|
144 |
* @param string $class Classname
|
145 |
*/
|
146 |
function w3tc_class_autoload( $class ) {
|
147 |
-
$base = null;
|
148 |
-
|
149 |
// some php pass classes with slash
|
150 |
-
if ( substr( $class, 0, 1 ) == "\\" )
|
151 |
$class = substr( $class, 1 );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
152 |
|
153 |
-
if ( substr( $class, 0,
|
154 |
-
$base = W3TC_LIB_DIR . DIRECTORY_SEPARATOR . 'Minify' . DIRECTORY_SEPARATOR;
|
155 |
-
} elseif ( substr( $class, 0, 8 ) == 'Minify0_' ) {
|
156 |
-
$base = W3TC_LIB_DIR . DIRECTORY_SEPARATOR . 'Minify' . DIRECTORY_SEPARATOR;
|
157 |
-
$class = substr( $class, 8 );
|
158 |
-
} elseif ( substr( $class, 0, 13 ) == 'W3TCG_Google_' &&
|
159 |
( !defined( 'W3TC_GOOGLE_LIBRARY' ) || W3TC_GOOGLE_LIBRARY ) ) {
|
160 |
// Google library
|
161 |
$classPath = explode( '_', substr( $class, 6 ) );
|
@@ -170,31 +181,18 @@ function w3tc_class_autoload( $class ) {
|
|
170 |
if ( file_exists( $filePath ) )
|
171 |
require $filePath;
|
172 |
return;
|
173 |
-
} elseif ( substr( $class, 0, 24 ) == 'w3tc_tubalmartin\\CssMin\\' ) {
|
174 |
-
$base = W3TC_LIB_DIR . DIRECTORY_SEPARATOR . 'Minify' . DIRECTORY_SEPARATOR .
|
175 |
-
'YUI-CSS-compressor-PHP-port-4.1.0' . DIRECTORY_SEPARATOR;
|
176 |
-
$class = substr( $class, 24 );
|
177 |
}
|
178 |
|
179 |
-
if (
|
|
|
|
|
|
|
|
|
180 |
$file = $base . strtr( $class, "\\_",
|
181 |
DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ) . '.php';
|
182 |
if ( file_exists( $file ) )
|
183 |
require_once $file;
|
184 |
-
}
|
185 |
-
$filename = W3TC_DIR . DIRECTORY_SEPARATOR . substr( $class, 5 ) . '.php';
|
186 |
-
|
187 |
-
if ( file_exists( $filename ) ) {
|
188 |
-
require $filename;
|
189 |
-
} else {
|
190 |
-
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
191 |
-
echo 'Attempt to create object of class ' .
|
192 |
-
$class . ' has been made, but file ' .
|
193 |
-
$filename . ' doesnt exists';
|
194 |
-
debug_print_backtrace();
|
195 |
-
}
|
196 |
-
}
|
197 |
-
}
|
198 |
}
|
199 |
|
200 |
spl_autoload_register( 'w3tc_class_autoload' );
|
5 |
}
|
6 |
|
7 |
define( 'W3TC', true );
|
8 |
+
define( 'W3TC_VERSION', '2.1.1' );
|
9 |
define( 'W3TC_POWERED_BY', 'W3 Total Cache' );
|
10 |
define( 'W3TC_EMAIL', 'w3tc@w3-edge.com' );
|
11 |
define( 'W3TC_TEXT_DOMAIN', 'w3-total-cache' );
|
144 |
* @param string $class Classname
|
145 |
*/
|
146 |
function w3tc_class_autoload( $class ) {
|
|
|
|
|
147 |
// some php pass classes with slash
|
148 |
+
if ( substr( $class, 0, 1 ) == "\\" ) {
|
149 |
$class = substr( $class, 1 );
|
150 |
+
}
|
151 |
+
|
152 |
+
// try core w3tc classes first
|
153 |
+
if ( substr( $class, 0, 5 ) == 'W3TC\\' ) {
|
154 |
+
$filename = W3TC_DIR . DIRECTORY_SEPARATOR . substr( $class, 5 ) . '.php';
|
155 |
+
|
156 |
+
if ( file_exists( $filename ) ) {
|
157 |
+
require $filename;
|
158 |
+
return;
|
159 |
+
} else {
|
160 |
+
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
|
161 |
+
echo 'Attempt to create object of class ' .
|
162 |
+
$class . ' has been made, but file ' .
|
163 |
+
$filename . ' doesnt exists';
|
164 |
+
debug_print_backtrace();
|
165 |
+
}
|
166 |
+
}
|
167 |
+
}
|
168 |
|
169 |
+
if ( substr( $class, 0, 13 ) == 'W3TCG_Google_' &&
|
|
|
|
|
|
|
|
|
|
|
170 |
( !defined( 'W3TC_GOOGLE_LIBRARY' ) || W3TC_GOOGLE_LIBRARY ) ) {
|
171 |
// Google library
|
172 |
$classPath = explode( '_', substr( $class, 6 ) );
|
181 |
if ( file_exists( $filePath ) )
|
182 |
require $filePath;
|
183 |
return;
|
|
|
|
|
|
|
|
|
184 |
}
|
185 |
|
186 |
+
if ( substr( $class, 0, 6 ) == 'W3TCL\\' ) {
|
187 |
+
$base = W3TC_LIB_DIR . DIRECTORY_SEPARATOR;
|
188 |
+
$class = substr( $class, 6 );
|
189 |
+
|
190 |
+
// psr loader
|
191 |
$file = $base . strtr( $class, "\\_",
|
192 |
DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ) . '.php';
|
193 |
if ( file_exists( $file ) )
|
194 |
require_once $file;
|
195 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
}
|
197 |
|
198 |
spl_autoload_register( 'w3tc_class_autoload' );
|
w3-total-cache.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
* Plugin Name: W3 Total Cache
|
4 |
* Plugin URI: https://www.boldgrid.com/totalcache/
|
5 |
* Description: The highest rated and most complete WordPress performance plugin. Dramatically improve the speed and user experience of your site. Add browser, page, object and database caching as well as minify and content delivery network (CDN) to WordPress.
|
6 |
-
* Version: 2.1.
|
7 |
* Requires at least: 3.8
|
8 |
* Requires PHP: 5.3
|
9 |
* Author: BoldGrid
|
3 |
* Plugin Name: W3 Total Cache
|
4 |
* Plugin URI: https://www.boldgrid.com/totalcache/
|
5 |
* Description: The highest rated and most complete WordPress performance plugin. Dramatically improve the speed and user experience of your site. Add browser, page, object and database caching as well as minify and content delivery network (CDN) to WordPress.
|
6 |
+
* Version: 2.1.1
|
7 |
* Requires at least: 3.8
|
8 |
* Requires PHP: 5.3
|
9 |
* Author: BoldGrid
|