W3 Total Cache - Version 2.1.1

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 Icon 128x128 W3 Total Cache
Version 2.1.1
Comparing to
See all releases

Code changes from version 2.1.0 to 2.1.1

Files changed (66) hide show
  1. BrowserCache_Environment.php +1 -0
  2. FeatureShowcase_Plugin_Admin.php +19 -19
  3. Generic_AdminActions_Test.php +13 -13
  4. Minify_ContentMinifier.php +24 -24
  5. Minify_Environment.php +22 -2
  6. Minify_MinifiedFileRequestHandler.php +11 -11
  7. Minify_Plugin.php +3 -3
  8. Util_Ui.php +1 -0
  9. inc/mime/other.php +1 -0
  10. inc/options/general.php +50 -23
  11. ini/apache_conf/mod_expires.conf +1 -0
  12. ini/apache_conf/mod_mime.conf +1 -0
  13. lib/Aws/aws-autoloader.php +4 -1
  14. lib/Azure/GuzzleHttp/Handler/CurlFactory.php +1 -1
  15. lib/Minify/DooDigestAuth.php +0 -121
  16. lib/Minify/HTTP/ConditionalGet.php +344 -342
  17. lib/Minify/HTTP/Encoder.php +292 -290
  18. lib/Minify/JSMin.php +372 -370
  19. lib/Minify/JSMinPlus.php +14 -13
  20. lib/Minify/Minify.php +676 -674
  21. lib/Minify/Minify/Build.php +69 -67
  22. lib/Minify/Minify/CSS.php +82 -81
  23. lib/Minify/Minify/CSS/Compressor.php +43 -42
  24. lib/Minify/Minify/CSS/UriRewriter.php +434 -433
  25. lib/Minify/Minify/CSSTidy.php +2 -1
  26. lib/Minify/Minify/Cache/File.php +1 -0
  27. lib/Minify/Minify/Cache/W3TCDerived.php +3 -2
  28. lib/Minify/Minify/Cache/ZendPlatform.php +2 -1
  29. lib/Minify/Minify/ClosureCompiler.php +94 -93
  30. lib/Minify/Minify/CombineOnly.php +11 -11
  31. lib/Minify/Minify/CommentPreserver.php +15 -14
  32. lib/Minify/Minify/Controller/Base.php +203 -202
  33. lib/Minify/Minify/Controller/Files.php +11 -11
  34. lib/Minify/Minify/Controller/Groups.php +14 -14
  35. lib/Minify/Minify/Controller/MinApp.php +12 -11
  36. lib/Minify/Minify/Controller/Page.php +54 -54
  37. lib/Minify/Minify/Controller/Version1.php +16 -16
  38. lib/Minify/Minify/DebugDetector.php +1 -0
  39. lib/Minify/Minify/HTML.php +2 -0
  40. lib/Minify/Minify/HTML/Helper.php +4 -3
  41. lib/Minify/Minify/HTMLTidy.php +1 -0
  42. lib/Minify/Minify/IgnoredCommentPreserver.php +1 -0
  43. lib/Minify/Minify/ImportProcessor.php +2 -0
  44. lib/Minify/Minify/Inline.php +1 -0
  45. lib/Minify/Minify/Inline/CSS.php +1 -0
  46. lib/Minify/Minify/Inline/JavaScript.php +1 -0
  47. lib/Minify/Minify/JS/ClosureCompiler.php +141 -140
  48. lib/Minify/Minify/Lines.php +1 -0
  49. lib/Minify/Minify/Loader.php +0 -28
  50. lib/Minify/Minify/Logger.php +7 -6
  51. lib/Minify/Minify/Packer.php +5 -4
  52. lib/Minify/Minify/Source.php +28 -28
  53. lib/Minify/Minify/YUI/CssCompressor.php +150 -149
  54. lib/Minify/Minify/YUICompressor.php +162 -161
  55. lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Colors.php +0 -155
  56. lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Command.php +0 -223
  57. lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Minifier.php +0 -862
  58. lib/Minify/YUI-CSS-compressor-PHP-port-4.1.0/Utils.php +0 -149
  59. lib/YuiCssMin/Colors.php +154 -0
  60. lib/YuiCssMin/Command.php +222 -0
  61. lib/YuiCssMin/Minifier.php +866 -0
  62. lib/YuiCssMin/Utils.php +148 -0
  63. pub/css/options.css +14 -1
  64. readme.txt +11 -2
  65. w3-total-cache-api.php +27 -29
  66. 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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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' ) . '</a>',
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( 'Minify0_JSMin', 'minify' ),
27
- 'yuijs' => array( 'Minify_YUICompressor', 'minifyJs' ),
28
- 'ccjs' => array( 'Minify_ClosureCompiler', 'minify' ),
29
- 'jsminplus' => array( 'Minify0_JSMinPlus', 'minify' ),
30
- 'googleccjs' => array( 'Minify_JS_ClosureCompiler', 'minify' ),
31
 
32
- 'css' => array( 'Minify_CSS', 'minify' ),
33
- 'yuicss' => array( 'Minify_YUICompressor', 'minifyCss' ),
34
- 'cssmin' => array( 'w3tc_tubalmartin\CssMin\Minifier', 'minify' ),
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 = rtrim( network_site_url( '', 'relative' ), '/' ) . '/';
 
 
 
 
 
 
 
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 = rtrim( network_site_url( '', 'relative' ), '/' ) . '/';
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
- \Minify0_Minify::setCache( $cache );
121
 
122
  /**
123
  * Set cache ID
124
  */
125
  $cache_id = $this->get_cache_id( $file );
126
- \Minify0_Minify::setCacheId( $file );
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 = \Minify0_Minify::serve( 'MinApp', $serve_options );
237
  } catch ( \Exception $exception ) {
238
  return $this->finish_with_error( $exception->getMessage(), $quiet );
239
  }
240
 
241
- if ( !is_null( \Minify0_Minify::$recoverableError ) )
242
- $this->_handle_error( \Minify0_Minify::$recoverableError );
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 = \Minify0_Minify::getUsageStatistics();
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( array(
134
- 'key' => 'minify.enabled',
135
- 'control' => 'checkbox',
 
136
  'checkbox_label' => __( 'Enable', 'w3-total-cache' ),
137
- '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' )
138
- ) );
139
- Util_Ui::config_item( array(
140
- 'key' => 'minify.auto',
141
- 'value' => ( $this->_config->get_boolean( 'minify.auto' ) ? 1 : 0 ),
142
- 'control' => 'radiogroup',
 
 
 
 
 
 
143
  'radiogroup_values' => array(
144
  '1' => __( 'Auto', 'w3-total-cache' ),
145
- '0' => __( 'Manual', 'w3-total-cache' )
146
  ),
147
- 'description' => __( 'Select manual mode to use fields on the minify settings tab to specify files to be minified, otherwise files will be minified automatically.', 'w3-total-cache' )
148
- ) );
149
- Util_Ui::config_item_engine( array(
150
- 'key' => 'minify.engine'
151
- ) );
152
- Util_Ui::config_item( array(
153
- 'key' => 'minify.html.engine',
154
- 'control' => 'selectbox',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
  'selectbox_values' => array(
156
- 'html' => __( 'Minify (default)', 'w3-total-cache' ),
157
  'htmltidy' => array(
158
  'disabled' => !Util_Installed::tidy(),
159
- 'label' => __( 'HTML Tidy', 'w3-total-cache' )
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
- require __DIR__ . '/Aws/functions.php';
 
 
 
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 &copy; 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
- * Does the client have a valid copy of the requested resource?
67
- *
68
- * You'll want to check this after instantiating the object. If true, do
69
- * not send content, just call sendHeaders() if you haven't already.
70
- *
71
- * @var bool
72
- */
73
- public $cacheIsValid = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- /**
76
- * @param array $spec options
77
- *
78
- * 'isPublic': (bool) if false, the Cache-Control header will contain
79
- * "private", allowing only browser caching. (default false)
80
- *
81
- * 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers
82
- * will be sent with content. This is recommended.
83
- *
84
- * 'encoding': (string) if set, the header "Vary: Accept-Encoding" will
85
- * always be sent and a truncated version of the encoding will be appended
86
- * to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient
87
- * checking of the client's If-None-Match header, as the encoding portion of
88
- * the ETag will be stripped before comparison.
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
- case 'cache_public_maxage':
163
- $this->_headers['Cache-Control'] = "max-age={$maxAge}, public";
164
- break;
 
 
 
 
 
 
 
165
 
166
- case 'cache_validation':
167
- $this->_headers['Cache-Control'] = 'public, must-revalidate, proxy-revalidate';
168
- break;
169
-
170
- case 'cache_noproxy':
171
- $this->_headers['Cache-Control'] = 'private, must-revalidate';
172
- break;
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
- * Set the Content-Length header in bytes
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
- * Send headers
239
- *
240
- * @see getHeaders()
241
- *
242
- * Note this doesn't "clear" the headers. Calling sendHeaders() will
243
- * call header() again (but probably have not effect) and getHeaders() will
244
- * still return the headers.
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
- * @param int $time
332
- */
333
- protected function _setLastModified($time)
334
- {
335
- $this->_lmTime = (int)$time;
336
- $this->_headers['Last-Modified'] = self::gmtDate($time);
337
- }
 
 
 
 
 
 
 
 
 
 
 
338
 
339
- /**
340
- * Determine validity of client cache and queue 304 header if valid
341
- *
342
- * @return bool
343
- */
344
- protected function _isCacheValid()
345
- {
346
- if (null === $this->_etag) {
347
- // lmTime is copied to ETag, so this condition implies that the
348
- // server sent neither ETag nor Last-Modified, so the client can't
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
- * @return bool
361
- */
362
- protected function resourceMatchedEtag()
363
- {
364
- if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
365
- return false;
366
- }
367
- $clientEtagList = $_SERVER['HTTP_IF_NONE_MATCH'];
368
- $clientEtags = explode(',', $clientEtagList);
369
-
370
- $compareTo = $this->normalizeEtag($this->_etag);
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
- * @param string $etag
385
- *
386
- * @return string
387
- */
388
- protected function normalizeEtag($etag) {
389
- $etag = trim($etag);
390
- return $this->_stripEtag
391
- ? preg_replace('/;\\w\\w"$/', '"', $etag)
392
- : $etag;
393
- }
394
 
395
- /**
396
- * @return bool
397
- */
398
- protected function resourceNotModified()
399
- {
400
- if (!isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
401
- return false;
402
- }
403
- // strip off IE's extra data (semicolon)
404
- list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2);
405
- if (strtotime($ifModifiedSince) >= $this->_lmTime) {
406
- // Apache 2.2's behavior. If there was no ETag match, send the
407
- // non-encoded version of the ETag value.
408
- if ($this->_cacheHeaders['use_etag'])
409
- $this->_headers['ETag'] = $this->normalizeEtag($this->_etag);
410
- return true;
411
- }
412
- return false;
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
- * Should the encoder allow HTTP encoding to IE6?
50
- *
51
- * If you have many IE6 users and the bandwidth savings is worth troubling
52
- * some of them, set this to true.
53
- *
54
- * By default, encoding is only offered to IE7+. When this is true,
55
- * getAcceptedEncoding() will return an encoding for IE6 if its user agent
56
- * string contains "SV1". This has been documented in many places as "safe",
57
- * but there seem to be remaining, intermittent encoding bugs in patched
58
- * IE6 on the wild web.
59
- *
60
- * @var bool
61
- */
62
- public static $encodeToIe6 = true;
63
-
64
-
65
- /**
66
- * Default compression level for zlib operations
67
- *
68
- * This level is used if encode() is not given a $compressionLevel
69
- *
70
- * @var int
71
- */
72
- public static $compressionLevel = 6;
73
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- /**
76
- * Get an HTTP Encoder object
77
- *
78
- * @param array $spec options
79
- *
80
- * 'content': (string required) content to be encoded
81
- *
82
- * 'type': (string) if set, the Content-Type header will have this value.
83
- *
84
- * 'method: (string) only set this if you are forcing a particular encoding
85
- * method. If not set, the best method will be chosen by getAcceptedEncoding()
86
- * The available methods are 'gzip', 'deflate', 'compress', and '' (no
87
- * encoding)
88
- */
89
- public function __construct($spec)
90
- {
91
- $this->_useMbStrlen = (function_exists('mb_strlen')
92
- && (ini_get('mbstring.func_overload') !== '')
93
- && ((int)ini_get('mbstring.func_overload') & 2));
94
- $this->_content = $spec['content'];
95
- $this->_headers['Content-Length'] = $this->_useMbStrlen
96
- ? (string)mb_strlen($this->_content, '8bit')
97
- : (string)strlen($this->_content);
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
- * Get content in current form
112
- *
113
- * Call after encode() for encoded content.
114
- *
115
- * @return string
116
- */
117
- public function getContent()
118
- {
119
- return $this->_content;
120
- }
121
-
122
- /**
123
- * Get array of output headers to be sent
124
- *
125
- * E.g.
126
- * <code>
127
- * array(
128
- * 'Content-Length' => '615'
129
- * ,'Content-Encoding' => 'x-gzip'
130
- * ,'Vary' => 'Accept-Encoding'
131
- * )
132
- * </code>
133
- *
134
- * @return array
135
- */
136
- public function getHeaders()
137
- {
138
- return $this->_headers;
139
- }
 
 
 
 
 
 
 
140
 
141
- /**
142
- * Send output headers
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
- * Determine the client's best encoding method from the HTTP Accept-Encoding
174
- * header.
175
- *
176
- * If no Accept-Encoding header is set, or the browser is IE before v6 SP2,
177
- * this will return ('', ''), the "identity" encoding.
178
- *
179
- * A syntax-aware scan is done of the Accept-Encoding, so the method must
180
- * be non 0. The methods are favored in order of gzip, deflate, then
181
- * compress. Deflate is always smallest and generally faster, but is
182
- * rarely sent by servers, so client support could be buggier.
183
- *
184
- * @param bool $allowCompress allow the older compress encoding
185
- *
186
- * @param bool $allowDeflate allow the more recent deflate encoding
187
- *
188
- * @return array two values, 1st is the actual encoding method, 2nd is the
189
- * alias of that method to use in the Content-Encoding header (some browsers
190
- * call gzip "x-gzip" etc.)
191
- */
192
- public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true)
193
- {
194
- // @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
195
-
196
- if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
197
- || \W3TC\Util_Environment::is_zlib_enabled()
198
- || headers_sent()
199
- || self::isBuggyIe())
200
- {
201
- return array('', '');
202
- }
203
- $ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
204
- // brotli checks (quick)
205
- if (0 === strpos($ae, 'br,')
206
- || strpos($ae, ', br') === strlen($ae) - 4
207
- ) {
208
- if (function_exists('brotli_compress'))
209
- return array('br', 'br');
210
- }
211
- // brotli checks (slow)
212
- if (preg_match(
213
- '@(?:^|,)\\s*((?:x-)?br)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
214
- ,$ae
215
- ,$m)) {
216
- if (function_exists('brotli_compress'))
217
- return array('br', $m[1]);
218
- }
219
- // gzip checks (quick)
220
- if (0 === strpos($ae, 'gzip,') // most browsers
221
- || 0 === strpos($ae, 'deflate, gzip,') // opera
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
- return array('', '');
235
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
- /**
238
- * Encode (compress) the content
239
- *
240
- * If the encode method is '' (none) or compression level is 0, or the 'zlib'
241
- * extension isn't loaded, we return false.
242
- *
243
- * Then the appropriate gz_* function is called to compress the content. If
244
- * this fails, false is returned.
245
- *
246
- * The header "Vary: Accept-Encoding" is added. If encoding is successful,
247
- * the Content-Length header is updated, and Content-Encoding is also added.
248
- *
249
- * @param int $compressionLevel given to zlib functions. If not given, the
250
- * class default will be used.
251
- *
252
- * @return bool success true if the content was actually compressed
253
- */
254
- public function encode($compressionLevel = null)
255
- {
256
- if (! self::isBuggyIe()) {
257
- $this->_headers['Vary'] = 'Accept-Encoding';
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
- * Is the browser an IE version earlier than 6 SP2?
313
- *
314
- * @return bool
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 Minify0_JSMin {
58
- const ORD_LF = 10;
59
- const ORD_SPACE = 32;
60
- const ACTION_KEEP_A = 1;
61
- const ACTION_DELETE_A = 2;
62
- const ACTION_DELETE_A_B = 3;
 
 
 
 
 
 
 
 
 
 
 
63
 
64
- protected $a = "\n";
65
- protected $b = '';
66
- protected $input = '';
67
- protected $inputIndex = 0;
68
- protected $inputLength = 0;
69
- protected $lookAhead = null;
70
- protected $output = '';
71
- protected $lastByteOut = '';
72
- protected $keptComment = '';
73
- protected $options = array();
 
 
 
 
 
 
 
 
 
74
 
75
- /**
76
- * Minify Javascript.
77
- *
78
- * @param string $js Javascript to be minified
79
- *
80
- * @return string
81
- */
82
- public static function minify($js, $options = array())
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
- * @param string $input
97
- */
98
- public function __construct($input, $options = array())
99
- {
100
- $this->input = $input;
101
- $this->options = $options;
102
- }
 
 
103
 
104
- /**
105
- * Perform minification, return result
106
- *
107
- * @return string
108
- */
109
- public function min()
110
- {
111
- if ($this->output !== '') { // min already run
112
- return $this->output;
113
- }
114
 
115
- $mbIntEnc = null;
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
- $this->action(self::ACTION_DELETE_A_B);
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
- while ($this->a !== null) {
126
- // determine next command
127
- $command = self::ACTION_KEEP_A; // default
128
- if ($this->a === ' ') {
129
- if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
130
- && ($this->b === $this->lastByteOut)) {
131
- // Don't delete this space. If we do, the addition/subtraction
132
- // could be parsed as a post-increment
133
- } elseif (! $this->isAlphaNum($this->b)) {
134
- $command = self::ACTION_DELETE_A;
135
- }
136
- } elseif ($this->a === "\n") {
137
- if ($this->b === ' ') {
138
- $command = self::ACTION_DELETE_A_B;
 
 
 
139
 
140
- // in case of mbstring.func_overload & 2, must check for null b,
141
- // otherwise mb_strpos will give WARNING
142
- } elseif ($this->b === null
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
- if ($mbIntEnc !== null) {
159
- mb_internal_encoding($mbIntEnc);
160
- }
 
 
161
 
162
- if (isset($this->options['stripCrlf']) && $this->options['stripCrlf']) {
163
- $this->output = preg_replace("~[\r\n]+~", '', $this->output);
164
- } else {
165
- $this->output = preg_replace("~[\r\n]+~", "\n", $this->output);
166
- }
167
-
168
- return $this->output;
169
- }
170
 
171
- /**
172
- * ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
173
- * ACTION_DELETE_A = Copy B to A. Get the next B.
174
- * ACTION_DELETE_A_B = Get the next B.
175
- *
176
- * @param int $command
177
- * @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
178
- */
179
- protected function action($command)
180
- {
181
- // make sure we don't compress "a + ++b" to "a+++b", etc.
182
- if ($command === self::ACTION_DELETE_A_B
183
- && $this->b === ' '
184
- && ($this->a === '+' || $this->a === '-')) {
185
- // Note: we're at an addition/substraction operator; the inputIndex
186
- // will certainly be a valid index
187
- if ($this->input[$this->inputIndex] === $this->a) {
188
- // This is "+ +" or "- -". Don't delete the space.
189
- $command = self::ACTION_KEEP_A;
190
- }
191
- }
192
 
193
- switch ($command) {
194
- case self::ACTION_KEEP_A: // 1
195
- $this->output .= $this->a;
196
 
197
- if ($this->keptComment) {
198
- $this->output = rtrim($this->output, "\n");
199
- $this->output .= $this->keptComment;
200
- $this->keptComment = '';
201
- }
202
 
203
- $this->lastByteOut = $this->a;
204
 
205
- // fallthrough intentional
206
- case self::ACTION_DELETE_A: // 2
207
- $this->a = $this->b;
208
- if ($this->a === "'" || $this->a === '"') { // string literal
209
- $str = $this->a; // in case needed for exception
210
- for(;;) {
211
- $this->output .= $this->a;
212
- $this->lastByteOut = $this->a;
213
 
214
- $this->a = $this->get();
215
- if ($this->a === $this->b) { // end quote
216
- break;
217
- }
218
- if ($this->isEOF($this->a)) {
219
- throw new JSMin_UnterminatedStringException(
220
- "JSMin: Unterminated String at byte {$this->inputIndex}: {$str}");
221
- }
222
- $str .= $this->a;
223
- if ($this->a === '\\') {
224
- $this->output .= $this->a;
225
- $this->lastByteOut = $this->a;
226
 
227
- $this->a = $this->get();
228
- $str .= $this->a;
229
- }
230
- }
231
- }
232
 
233
- // fallthrough intentional
234
- case self::ACTION_DELETE_A_B: // 3
235
- $this->b = $this->next();
236
- if ($this->b === '/' && $this->isRegexpLiteral()) {
237
- $this->output .= $this->a . $this->b;
238
- $pattern = '/'; // keep entire pattern in case we need to report it in the exception
239
- for(;;) {
240
- $this->a = $this->get();
241
- $pattern .= $this->a;
242
- if ($this->a === '[') {
243
- for(;;) {
244
- $this->output .= $this->a;
245
- $this->a = $this->get();
246
- $pattern .= $this->a;
247
- if ($this->a === ']') {
248
- break;
249
- }
250
- if ($this->a === '\\') {
251
- $this->output .= $this->a;
252
- $this->a = $this->get();
253
- $pattern .= $this->a;
254
- }
255
- if ($this->isEOF($this->a)) {
256
- throw new JSMin_UnterminatedRegExpException(
257
- "JSMin: Unterminated set in RegExp at byte "
258
- . $this->inputIndex .": {$pattern}");
259
- }
260
- }
261
- }
262
 
263
- if ($this->a === '/') { // end pattern
264
- break; // while (true)
265
- } elseif ($this->a === '\\') {
266
- $this->output .= $this->a;
267
- $this->a = $this->get();
268
- $pattern .= $this->a;
269
- } elseif ($this->isEOF($this->a)) {
270
- throw new JSMin_UnterminatedRegExpException(
271
- "JSMin: Unterminated RegExp at byte {$this->inputIndex}: {$pattern}");
272
- }
273
- $this->output .= $this->a;
274
- $this->lastByteOut = $this->a;
275
- }
276
- $this->b = $this->next();
277
- }
278
- // end case ACTION_DELETE_A_B
279
- }
280
- }
281
 
282
- /**
283
- * @return bool
284
- */
285
- protected function isRegexpLiteral()
286
- {
287
- if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
288
- // we obviously aren't dividing
289
- return true;
290
- }
291
- if ($this->a === ' ' || $this->a === "\n") {
292
- $length = strlen($this->output);
293
- if ($length < 2) { // weird edge case
294
- return true;
295
- }
296
- // you can't divide a keyword
297
- if (preg_match('/(?:case|else|in|return|typeof)$/', $this->output, $m)) {
298
- if ($this->output === $m[0]) { // odd but could happen
299
- return true;
300
- }
301
- // make sure it's a keyword, not end of an identifier
302
- $charBeforeKeyword = substr($this->output, $length - strlen($m[0]) - 1, 1);
303
- if (! $this->isAlphaNum($charBeforeKeyword)) {
304
- return true;
305
- }
306
- }
307
- }
308
- return false;
309
- }
310
 
311
- /**
312
- * Return the next character from stdin. Watch out for lookahead. If the character is a control character,
313
- * translate it to a space or linefeed.
314
- *
315
- * @return string
316
- */
317
- protected function get()
318
- {
319
- $c = $this->lookAhead;
320
- $this->lookAhead = null;
321
- if ($c === null) {
322
- // getc(stdin)
323
- if ($this->inputIndex < $this->inputLength) {
324
- $c = $this->input[$this->inputIndex];
325
- $this->inputIndex += 1;
326
- } else {
327
- $c = null;
328
- }
329
- }
330
- if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
331
- return $c;
332
- }
333
- if ($c === "\r") {
334
- return "\n";
335
- }
336
- return ' ';
337
- }
338
 
339
- /**
340
- * Does $a indicate end of input?
341
- *
342
- * @param string $a
343
- * @return bool
344
- */
345
- protected function isEOF($a)
346
- {
347
- return ord($a) <= self::ORD_LF;
348
- }
349
 
350
- /**
351
- * Get next char (without getting it). If is ctrl character, translate to a space or newline.
352
- *
353
- * @return string
354
- */
355
- protected function peek()
356
- {
357
- $this->lookAhead = $this->get();
358
- return $this->lookAhead;
359
- }
360
 
361
- /**
362
- * Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
363
- *
364
- * @param string $c
365
- *
366
- * @return bool
367
- */
368
- protected function isAlphaNum($c)
369
- {
370
- return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
371
- }
372
 
373
- /**
374
- * Consume a single line comment from input (possibly retaining it)
375
- */
376
- protected function consumeSingleLineComment()
377
- {
378
- $comment = '';
379
- while (true) {
380
- $get = $this->get();
381
- $comment .= $get;
382
- if (ord($get) <= self::ORD_LF) { // end of line reached
383
- // if IE conditional comment
384
- if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
385
- $this->keptComment .= "/{$comment}";
386
- }
387
- return;
388
- }
389
- }
390
- }
391
 
392
- /**
393
- * Consume a multiple line comment from input (possibly retaining it)
394
- *
395
- * @throws JSMin_UnterminatedCommentException
396
- */
397
- protected function consumeMultipleLineComment()
398
- {
399
- $this->get();
400
- $comment = '';
401
- for(;;) {
402
- $get = $this->get();
403
- if ($get === '*') {
404
- if ($this->peek() === '/') { // end of comment reached
405
- $this->get();
406
- if (isset($this->options['preserveComments']) &&
407
- $this->options['preserveComments'] &&
408
- 0 === strpos($comment, '!')) {
409
- // preserved by YUI Compressor
410
- if (!$this->keptComment) {
411
- // don't prepend a newline if two comments right after one another
412
- $this->keptComment = "\n";
413
- }
414
- $this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
415
- } else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
416
- // IE conditional
417
- $this->keptComment .= "/*{$comment}*/";
418
- }
419
- return;
420
- }
421
- } elseif ($get === null) {
422
- throw new JSMin_UnterminatedCommentException(
423
- "JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
424
- }
425
- $comment .= $get;
426
- }
427
- }
428
 
429
- /**
430
- * Get the next character, skipping over comments. Some comments may be preserved.
431
- *
432
- * @return string
433
- */
434
- protected function next()
435
- {
436
- $get = $this->get();
437
- if ($get === '/') {
438
- switch ($this->peek()) {
439
- case '/':
440
- $this->consumeSingleLineComment();
441
- $get = "\n";
442
- break;
443
- case '*':
444
- $this->consumeMultipleLineComment();
445
- $get = ' ';
446
- break;
447
- }
448
- }
449
- return $get;
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 Minify0_JSMinPlus
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 Minify0_JSMinPlus();
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
- $n = new JSNode($this->t);
925
- $n->isLoop = true;
926
- $n->condition = $this->ParenExpression($x);
927
- $n->body = $this->nest($x, $n);
928
  return $n;
929
 
930
  case KEYWORD_DO:
@@ -1044,7 +1045,7 @@ class JSParser
1044
 
1045
  case KEYWORD_VAR:
1046
  case KEYWORD_CONST:
1047
- $n = $this->Variables($x);
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
- if (!$this->match($tt))
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 Minify0_Minify {
26
-
27
- const VERSION = '2.1.7';
28
- const TYPE_CSS = 'text/css';
29
- const TYPE_HTML = 'text/html';
30
- // there is some debate over the ideal JS Content-Type, but this is the
31
- // Apache default and what Yahoo! uses..
32
- const TYPE_JS = 'application/x-javascript';
33
- const URL_DEBUG = 'http://code.google.com/p/minify/wiki/Debugging';
34
-
35
- /**
36
- * How many hours behind are the file modification times of uploaded files?
37
- *
38
- * If you upload files from Windows to a non-Windows server, Windows may report
39
- * incorrect mtimes for the files. Immediately after modifying and uploading a
40
- * file, use the touch command to update the mtime on the server. If the mtime
41
- * jumps ahead by a number of hours, set this variable to that number. If the mtime
42
- * moves back, this should not be needed.
43
- *
44
- * @var int $uploaderHoursBehind
45
- */
46
- public static $uploaderHoursBehind = 0;
47
-
48
- /**
49
- * If this string is not empty AND the serve() option 'bubbleCssImports' is
50
- * NOT set, then serve() will check CSS files for @import declarations that
51
- * appear too late in the combined stylesheet. If found, serve() will prepend
52
- * the output with this warning.
53
- *
54
- * @var string $importWarning
55
- */
56
- public static $importWarning = "/* See http://code.google.com/p/minify/wiki/CommonProblems#@imports_can_appear_in_invalid_locations_in_combined_CSS_files */\n";
57
-
58
- /**
59
- * Has the DOCUMENT_ROOT been set in user code?
60
- *
61
- * @var bool
62
- */
63
- public static $isDocRootSet = false;
64
-
65
- /**
66
- * Cache ID
67
- * @var string
68
- */
69
- protected static $_cacheId = null;
70
-
71
- /**
72
- * Specify a cache object (with identical interface as Minify_Cache_File) or
73
- * a path to use with Minify_Cache_File.
74
- *
75
- * If not called, Minify will not use a cache and, for each 200 response, will
76
- * need to recombine files, minify and encode the output.
77
- *
78
- * @param mixed $cache object with identical interface as Minify_Cache_File or
79
- * a directory path, or null to disable caching. (default = '')
80
- *
81
- * @param bool $fileLocking (default = true) This only applies if the first
82
- * parameter is a string.
83
- *
84
- * @return null
85
- */
86
- public static function setCache($cache = '', $fileLocking = true)
87
- {
88
- if (is_string($cache)) {
89
- self::$_cache = new Minify_Cache_File($cache, $fileLocking);
90
- } else {
91
- self::$_cache = $cache;
92
- }
93
- }
94
-
95
- /**
96
- * Serve a request for a minified file.
97
- *
98
- * Here are the available options and defaults in the base controller:
99
- *
100
- * 'isPublic' : send "public" instead of "private" in Cache-Control
101
- * headers, allowing shared caches to cache the output. (default true)
102
- *
103
- * 'quiet' : set to true to have serve() return an array rather than sending
104
- * any headers/output (default false)
105
- *
106
- * 'encodeOutput' : set to false to disable content encoding, and not send
107
- * the Vary header (default true)
108
- *
109
- * 'encodeMethod' : generally you should let this be determined by
110
- * HTTP_Encoder (leave null), but you can force a particular encoding
111
- * to be returned, by setting this to 'gzip' or '' (no encoding)
112
- *
113
- * 'encodeLevel' : level of encoding compression (0 to 9, default 9)
114
- *
115
- * 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
116
- * value to remove. (default 'utf-8')
117
- *
118
- * 'maxAge' : set this to the number of seconds the client should use its cache
119
- * before revalidating with the server. This sets Cache-Control: max-age and the
120
- * Expires header. Unlike the old 'setExpires' setting, this setting will NOT
121
- * prevent conditional GETs. Note this has nothing to do with server-side caching.
122
- *
123
- * 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
124
- * minifier option to enable URI rewriting in CSS files (default true)
125
- *
126
- * 'bubbleCssImports' : If true, all @import declarations in combined CSS
127
- * files will be move to the top. Note this may alter effective CSS values
128
- * due to a change in order. (default false)
129
- *
130
- * 'debug' : set to true to minify all sources with the 'Lines' controller, which
131
- * eases the debugging of combined files. This also prevents 304 responses.
132
- * @see Minify_Lines::minify()
133
- *
134
- * 'minifiers' : to override Minify's default choice of minifier function for
135
- * a particular content-type, specify your callback under the key of the
136
- * content-type:
137
- * <code>
138
- * // call customCssMinifier($css) for all CSS minification
139
- * $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
140
- *
141
- * // don't minify Javascript at all
142
- * $options['minifiers'][Minify::TYPE_JS] = '';
143
- * </code>
144
- *
145
- * 'minifierOptions' : to send options to the minifier function, specify your options
146
- * under the key of the content-type. E.g. To send the CSS minifier an option:
147
- * <code>
148
- * // give CSS minifier array('optionName' => 'optionValue') as 2nd argument
149
- * $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
150
- * </code>
151
- *
152
- * 'contentType' : (optional) this is only needed if your file extension is not
153
- * js/css/html. The given content-type will be sent regardless of source file
154
- * extension, so this should not be used in a Groups config with other
155
- * Javascript/CSS files.
156
- *
157
- * Any controller options are documented in that controller's setupSources() method.
158
- *
159
- * @param mixed $controller instance of subclass of Minify_Controller_Base or string
160
- * name of controller. E.g. 'Files'
161
- *
162
- * @param array $options controller/serve options
163
- *
164
- * @return null|array if the 'quiet' option is set to true, an array
165
- * with keys "success" (bool), "statusCode" (int), "content" (string), and
166
- * "headers" (array).
167
- *
168
- * @throws Exception
169
- */
170
- public static function serve($controller, $options = array())
171
- {
172
- if (! self::$isDocRootSet && 0 === stripos(PHP_OS, 'win')) {
173
- self::setDocRoot();
174
- }
175
-
176
- if (is_string($controller)) {
177
- // make $controller into object
178
- $class = 'Minify_Controller_' . $controller;
179
- $controller = new $class();
180
- /* @var Minify_Controller_Base $controller */
181
- }
182
-
183
- // set up controller sources and mix remaining options with
184
- // controller defaults
185
- $options = $controller->setupSources($options);
186
- $options = $controller->analyzeSources($options);
187
- self::$_options = $controller->mixInDefaultOptions($options);
188
-
189
- // check request validity
190
- if (! $controller->sources) {
191
- // invalid request!
192
- if (! self::$_options['quiet']) {
193
- self::_errorExit(self::$_options['badRequestHeader'], self::URL_DEBUG);
194
- } else {
195
- list(,$statusCode) = explode(' ', self::$_options['badRequestHeader']);
196
- return array(
197
- 'success' => false
198
- ,'statusCode' => (int)$statusCode
199
- ,'content' => ''
200
- ,'headers' => array()
201
- );
202
- }
203
- }
204
-
205
- self::$_controller = $controller;
206
-
207
- if (self::$_options['debug']) {
208
- self::_setupDebug($controller->sources);
209
- self::$_options['maxAge'] = 0;
210
- }
211
-
212
- // determine encoding
213
- if (self::$_options['encodeOutput']) {
214
- $sendVary = true;
215
- if (self::$_options['encodeMethod'] !== null) {
216
- // controller specifically requested this
217
- $contentEncoding = self::$_options['encodeMethod'];
218
- } else {
219
- // sniff request header
220
- // depending on what the client accepts, $contentEncoding may be
221
- // 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
222
- // getAcceptedEncoding(false, false) leaves out compress and deflate as options.
223
- list(self::$_options['encodeMethod'], $contentEncoding) = HTTP_Encoder::getAcceptedEncoding(false, false);
224
- $sendVary = ! HTTP_Encoder::isBuggyIe();
225
- }
226
- } else {
227
- self::$_options['encodeMethod'] = ''; // identity (no encoding)
228
- }
229
-
230
- // check client cache
231
- $cgOptions = array(
232
- 'lastModifiedTime' => self::$_options['lastModifiedTime']
233
- ,'cacheHeaders' => self::$_options['cacheHeaders']
234
- ,'isPublic' => self::$_options['isPublic']
235
- ,'encoding' => self::$_options['encodeMethod']
236
- );
237
- if (self::$_options['maxAge'] > 0) {
238
- $cgOptions['maxAge'] = self::$_options['maxAge'];
239
- } elseif (self::$_options['debug']) {
240
- $cgOptions['invalidate'] = true;
241
- }
242
-
243
- $cg = new HTTP_ConditionalGet($cgOptions);
244
- if ( $cg->cacheIsValid && !self::$_options['disable_304'] &&
245
- !defined( 'W3TC_MINIFY_CONDITIONAL_OFF' ) ) {
246
- // client's cache is valid
247
- if (! self::$_options['quiet']) {
248
- self::$lastServed = array(
249
- 'fullCacheId' =>self::_getCacheId() .
250
- (self::$_options['encodeMethod']
251
- ? '.' . self::$_options['encodeMethod']
252
- : '')
253
- );
254
- $cg->sendHeaders();
255
- return;
256
- } else {
257
- return array(
258
- 'success' => true
259
- ,'statusCode' => 304
260
- ,'content' => ''
261
- ,'headers' => $cg->getHeaders()
262
- );
263
- }
264
- } else {
265
- // client will need output
266
- $headers = $cg->getHeaders();
267
- unset($cg);
268
- }
269
-
270
- if (self::$_options['contentType'] === self::TYPE_CSS
271
- && self::$_options['rewriteCssUris']) {
272
- foreach($controller->sources as $key => $source) {
273
- if (self::$_options['rewriteCssUris']
274
- && $source->filepath
275
- && !isset($source->minifyOptions['currentDir'])
276
- && !isset($source->minifyOptions['prependRelativePath'])
277
- ) {
278
- $source->minifyOptions['currentDir'] = dirname($source->filepath);
279
- }
280
-
281
- $source->minifyOptions['processCssImports'] = self::$_options['processCssImports'];
282
- }
283
- }
284
-
285
- // check server cache
286
- $content = array();
287
- if (null !== self::$_cache && ! self::$_options['debug']) {
288
- // using cache
289
- // the goal is to use only the cache methods to sniff the length and
290
- // output the content, as they do not require ever loading the file into
291
- // memory.
292
- $cacheId = self::_getCacheId();
293
- $fullCacheId = (self::$_options['encodeMethod'])
294
- ? $cacheId . '.' . self::$_options['encodeMethod']
295
- : $cacheId;
296
- // check cache for valid entry
297
- $cacheIsReady = self::$_cache->isValid($fullCacheId, self::$_options['lastModifiedTime']);
298
- if ($cacheIsReady) {
299
- $cacheContentLength = self::$_cache->getSize($fullCacheId);
300
- } else {
301
- // generate & cache content
302
- try {
303
- $content = self::_combineMinify();
304
- } catch (Exception $e) {
305
- self::$_controller->log($e->getMessage());
306
- if (! self::$_options['quiet']) {
307
- self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
308
- }
309
- throw $e;
310
- }
311
- self::$_cache->store($cacheId, $content);
312
- if (function_exists('brotli_compress') && self::$_options['encodeMethod'] === 'br' && self::$_options['encodeOutput']) {
313
- $compressed = $content;
314
- $compressed['content'] = brotli_compress($content['content']);
315
-
316
- self::$_cache->store($cacheId . '_' . self::$_options['encodeMethod'],
317
- $compressed);
318
- }
319
- if (function_exists('gzencode') && self::$_options['encodeMethod'] && self::$_options['encodeMethod'] !== 'br' && self::$_options['encodeOutput']) {
320
- $compressed = $content;
321
- $compressed['content'] = gzencode($content['content'],
322
- self::$_options['encodeLevel']);
323
-
324
- self::$_cache->store($cacheId . '_' . self::$_options['encodeMethod'],
325
- $compressed);
326
- }
327
- }
328
- } else {
329
- // no cache
330
- $cacheIsReady = false;
331
- try {
332
- $content = self::_combineMinify();
333
- } catch (Exception $e) {
334
- self::$_controller->log($e->getMessage());
335
- if (! self::$_options['quiet']) {
336
- self::_errorExit(self::$_options['errorHeader'], self::URL_DEBUG);
337
- }
338
- throw $e;
339
- }
340
- }
341
- if (! $cacheIsReady && self::$_options['encodeMethod']) {
342
- switch (self::$_options['encodeMethod']) {
343
- case 'gzip':
344
- $content['content'] = gzencode($content['content'], self::$_options['encodeLevel']);
345
- break;
346
-
347
- case 'deflate':
348
- $content['content'] = gzdeflate($content['content'], self::$_options['encodeLevel']);
349
- break;
350
-
351
- case 'br':
352
- $content['content'] = brotli_compress($content['content']);
353
- break;
354
- }
355
- // still need to encode
356
- }
357
-
358
- // add headers
359
- if ( !defined( 'W3TC_MINIFY_CONTENT_LENGTH_OFF' ) ) {
360
- $headers['Content-Length'] = $cacheIsReady
361
- ? $cacheContentLength
362
- : ((function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
363
- ? mb_strlen($content['content'], '8bit')
364
- : strlen($content['content'])
365
- );
366
- }
367
-
368
- $headers['Content-Type'] = self::$_options['contentTypeCharset']
369
- ? self::$_options['contentType'] . '; charset=' . self::$_options['contentTypeCharset']
370
- : self::$_options['contentType'];
371
- if (self::$_options['encodeMethod'] !== '') {
372
- $headers['Content-Encoding'] = $contentEncoding;
373
- }
374
- if (self::$_options['encodeOutput'] && $sendVary) {
375
- $headers['Vary'] = 'Accept-Encoding';
376
- }
377
-
378
- self::$lastServed = $content;
379
-
380
- if (! self::$_options['quiet']) {
381
- // output headers & content
382
- foreach ($headers as $name => $val) {
383
- header($name . ': ' . $val);
384
- }
385
- if ($cacheIsReady) {
386
- self::$lastServed['fullCacheId'] = $fullCacheId;
387
- self::$_cache->display($fullCacheId);
388
- } else {
389
- echo $content['content'];
390
- }
391
- } else {
392
- if ($cacheIsReady)
393
- $content = self::$_cache->fetch($fullCacheId);
394
-
395
- return array(
396
- 'success' => true
397
- ,'statusCode' => 200
398
- ,'content' => $content['content']
399
- ,'headers' => $headers
400
- );
401
- }
402
- }
403
-
404
- private static $lastServed = array();
405
-
406
- public static function getUsageStatistics() {
407
- if (isset(self::$lastServed['fullCacheId']))
408
- self::$lastServed = self::$_cache->fetch(self::$lastServed['fullCacheId']);
409
-
410
- if (isset(self::$lastServed['content']) &&
411
- isset(self::$lastServed['originalLength'])) {
412
- return array(
413
- 'content_type' => self::$_options['contentType'],
414
- 'content_original_length' => self::$lastServed['originalLength'],
415
- 'content_output_length' => strlen(self::$lastServed['content'])
416
- );
417
- }
418
-
419
- return array();
420
- }
421
-
422
- /**
423
- * Return combined minified content for a set of sources
424
- *
425
- * No internal caching will be used and the content will not be HTTP encoded.
426
- *
427
- * @param array $sources array of filepaths and/or Minify_Source objects
428
- *
429
- * @param array $options (optional) array of options for serve. By default
430
- * these are already set: quiet = true, encodeMethod = '', lastModifiedTime = 0.
431
- *
432
- * @return string
433
- */
434
- public static function combine($sources, $options = array())
435
- {
436
- $cache = self::$_cache;
437
- self::$_cache = null;
438
- $options = array_merge(array(
439
- 'files' => (array)$sources
440
- ,'quiet' => true
441
- ,'encodeMethod' => ''
442
- ,'lastModifiedTime' => 0
443
- ), $options);
444
- $out = self::serve('Files', $options);
445
- self::$_cache = $cache;
446
- return $out['content'];
447
- }
448
-
449
- /**
450
- * Set $_SERVER['DOCUMENT_ROOT']. On IIS, the value is created from SCRIPT_FILENAME and SCRIPT_NAME.
451
- *
452
- * @param string $docRoot value to use for DOCUMENT_ROOT
453
- */
454
- public static function setDocRoot($docRoot = '')
455
- {
456
- self::$isDocRootSet = true;
457
- if ($docRoot) {
458
- $_SERVER['DOCUMENT_ROOT'] = $docRoot;
459
- } elseif (isset($_SERVER['SERVER_SOFTWARE'])
460
- && 0 === strpos($_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS/')) {
461
- $_SERVER['DOCUMENT_ROOT'] = substr(
462
- $_SERVER['SCRIPT_FILENAME']
463
- ,0
464
- ,strlen($_SERVER['SCRIPT_FILENAME']) - strlen($_SERVER['SCRIPT_NAME']));
465
- $_SERVER['DOCUMENT_ROOT'] = rtrim($_SERVER['DOCUMENT_ROOT'], '\\');
466
- }
467
- }
468
-
469
- public static $recoverableError = null;
470
-
471
- /**
472
- * Any Minify_Cache_* object or null (i.e. no server cache is used)
473
- *
474
- * @var Minify_Cache_File
475
- */
476
- private static $_cache = null;
477
-
478
- /**
479
- * Active controller for current request
480
- *
481
- * @var Minify_Controller_Base
482
- */
483
- protected static $_controller = null;
484
-
485
- /**
486
- * Options for current request
487
- *
488
- * @var array
489
- */
490
- protected static $_options = null;
491
-
492
- /**
493
- * @param string $header
494
- *
495
- * @param string $url
496
- */
497
- protected static function _errorExit($header, $url)
498
- {
499
- $url = htmlspecialchars($url, ENT_QUOTES);
500
- list(,$h1) = explode(' ', $header, 2);
501
- $h1 = htmlspecialchars($h1);
502
- // FastCGI environments require 3rd arg to header() to be set
503
- list(, $code) = explode(' ', $header, 3);
504
- header($header, true, $code);
505
- header('Content-Type: text/html; charset=utf-8');
506
- echo "<h1>$h1</h1>";
507
- echo "<p>Please see <a href='$url'>$url</a>.</p>";
508
- exit();
509
- }
510
-
511
- /**
512
- * Set up sources to use Minify_Lines
513
- *
514
- * @param Minify_Source[] $sources Minify_Source instances
515
- */
516
- protected static function _setupDebug($sources)
517
- {
518
- foreach ($sources as $source) {
519
- $source->minifier = array('Minify_Lines', 'minify');
520
- $id = $source->getId();
521
- $source->minifyOptions = array(
522
- 'id' => (is_file($id) ? basename($id) : $id)
523
- );
524
- }
525
- }
526
-
527
- /**
528
- * Combines sources and minifies the result.
529
- *
530
- * @return string
531
- *
532
- * @throws Exception
533
- */
534
- protected static function _combineMinify()
535
- {
536
- $type = self::$_options['contentType']; // ease readability
537
-
538
- // when combining scripts, make sure all statements separated and
539
- // trailing single line comment is terminated
540
- $implodeSeparator = ($type === self::TYPE_JS)
541
- ? "\n;"
542
- : '';
543
- // allow the user to pass a particular array of options to each
544
- // minifier (designated by type). source objects may still override
545
- // these
546
- $defaultOptions = isset(self::$_options['minifierOptions'][$type])
547
- ? self::$_options['minifierOptions'][$type]
548
- : array();
549
- // if minifier not set, default is no minification. source objects
550
- // may still override this
551
- $defaultMinifier = isset(self::$_options['minifiers'][$type])
552
- ? self::$_options['minifiers'][$type]
553
- : false;
554
-
555
- // process groups of sources with identical minifiers/options
556
- $content = array();
557
- $originalLength = 0;
558
- $i = 0;
559
- $l = count(self::$_controller->sources);
560
- $groupToProcessTogether = array();
561
- $lastMinifier = null;
562
- $lastOptions = null;
563
- do {
564
- // get next source
565
- $source = null;
566
- if ($i < $l) {
567
- $source = self::$_controller->sources[$i];
568
- /* @var Minify_Source $source */
569
- $sourceContent = $source->getContent();
570
- $originalLength += strlen($sourceContent);
571
-
572
- // allow the source to override our minifier and options
573
- $minifier = (null !== $source->minifier)
574
- ? $source->minifier
575
- : $defaultMinifier;
576
- $options = (null !== $source->minifyOptions)
577
- ? array_merge($defaultOptions, $source->minifyOptions)
578
- : $defaultOptions;
579
- }
580
- // do we need to process our group right now?
581
- if ($i > 0 // yes, we have at least the first group populated
582
- && (
583
- ! $source // yes, we ran out of sources
584
- || $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits)
585
- || $minifier !== $lastMinifier // yes, minifier changed
586
- || $options !== $lastOptions) // yes, options changed
587
- )
588
- {
589
- // minify previous sources with last settings
590
- $imploded = implode($implodeSeparator, $groupToProcessTogether);
591
- $groupToProcessTogether = array();
592
- if ($lastMinifier) {
593
- try {
594
- $content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
595
- } catch (Exception $e) {
596
- throw new Exception("Exception in minifier: " . $e->getMessage());
597
- }
598
- } else {
599
- $content[] = $imploded;
600
- }
601
- }
602
- // add content to the group
603
- if ($source) {
604
- $groupToProcessTogether[] = $sourceContent;
605
- $lastMinifier = $minifier;
606
- $lastOptions = $options;
607
- }
608
- $i++;
609
- } while ($source);
610
-
611
- $content = implode($implodeSeparator, $content);
612
-
613
- if ($type === self::TYPE_CSS && false !== strpos($content, '@import')) {
614
- $content = self::_handleCssImports($content);
615
- }
616
- if ( $type === self::TYPE_CSS ) {
617
- $content = apply_filters( 'w3tc_minify_css_content', $content, null, null );
618
- }
619
  if ( $type === self::TYPE_JS ) {
620
- $content = apply_filters( 'w3tc_minify_js_content', $content, null, null );
621
  }
622
 
623
- // do any post-processing (esp. for editing build URIs)
624
- if (self::$_options['postprocessorRequire']) {
625
- require_once self::$_options['postprocessorRequire'];
626
- }
627
- if (self::$_options['postprocessor']) {
628
- $content = call_user_func(self::$_options['postprocessor'], $content, $type);
629
- }
630
- return array(
631
- 'originalLength' => $originalLength,
632
- 'content' => $content
633
- );
634
- }
635
-
636
- /**
637
- * Sets cache ID
638
- * @param string $cacheId
639
- */
640
- public static function setCacheId($cacheId = null)
641
- {
642
- self::$_cacheId = $cacheId;
643
- }
644
-
645
- /**
646
- * Returns cache ID
647
- */
648
- public static function getCacheId()
649
- {
650
- return self::$_cacheId;
651
- }
652
-
653
- /**
654
- * Make a unique cache id for for this request.
655
- *
656
- * Any settings that could affect output are taken into consideration
657
- *
658
- * @param string $prefix
659
- *
660
- * @return string
661
- */
662
- protected static function _getCacheId($prefix = 'minify')
663
- {
664
- return (self::$_cacheId ? self::$_cacheId : md5(serialize(array(
665
- Minify_Source::getDigest(self::$_controller->sources)
666
- ,self::$_options['minifiers']
667
- ,self::$_options['minifierOptions']
668
- ,self::$_options['postprocessor']
669
- ,self::$_options['bubbleCssImports']
670
- ,self::$_options['processCssImports']
671
- ))));
672
- }
673
-
674
- /**
675
- * Bubble CSS @imports to the top or prepend a warning if an import is detected not at the top.
676
- *
677
- * @param string $css
678
- *
679
- * @return string
680
- */
681
- protected static function _handleCssImports($css)
682
- {
683
- if (self::$_options['bubbleCssImports']) {
684
- // bubble CSS imports
685
- preg_match_all('/@import.*?;/', $css, $imports);
686
- $css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css);
687
- } else if ('' !== self::$importWarning) {
688
- // remove comments so we don't mistake { in a comment as a block
689
- $noCommentCss = preg_replace('@/\\*[\\s\\S]*?\\*/@', '', $css);
690
- $lastImportPos = strrpos($noCommentCss, '@import');
691
- $firstBlockPos = strpos($noCommentCss, '{');
692
- if (false !== $lastImportPos
693
- && false !== $firstBlockPos
694
- && $firstBlockPos < $lastImportPos
695
- ) {
696
- // { appears before @import : prepend warning
697
- $css = self::$importWarning . $css;
698
- }
699
- }
700
- return $css;
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 = '&amp;';
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&amp1678242"
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
- * Create a build object
79
- *
80
- * @param array $sources array of Minify_Source objects and/or file paths
81
- *
82
- * @return null
83
- */
84
- public function __construct($sources)
85
- {
86
- $max = 0;
87
- foreach ((array)$sources as $source) {
88
- if ($source instanceof Minify_Source) {
89
- $max = max($max, $source->lastModified);
90
- } elseif (is_string($source)) {
91
- if (0 === strpos($source, '//')) {
92
- $source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);
93
- }
94
- if (is_file($source)) {
95
- $max = max($max, filemtime($source));
96
- }
97
- }
98
- }
99
- $this->lastModified = $max;
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 = '&amp;';
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&amp1678242"
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
- * Minify a CSS string
21
- *
22
- * @param string $css
23
- *
24
- * @param array $options available options:
25
- *
26
- * 'preserveComments': (default true) multi-line comments that begin
27
- * with "/*!" will be preserved with newlines before and after to
28
- * enhance readability.
29
- *
30
- * 'removeCharsets': (default true) remove all @charset at-rules
31
- *
32
- * 'prependRelativePath': (default null) if given, this string will be
33
- * prepended to all relative URIs in import/url declarations
34
- *
35
- * 'currentDir': (default null) if given, this is assumed to be the
36
- * directory of the current CSS file. Using this, minify will rewrite
37
- * all relative URIs in import/url declarations to correctly point to
38
- * the desired files. For this to work, the files *must* exist and be
39
- * visible by the PHP process.
40
- *
41
- * 'symlinks': (default = array()) If the CSS file is stored in
42
- * a symlink-ed directory, provide an array of link paths to
43
- * target paths, where the link paths are within the document root. Because
44
- * paths need to be normalized for this to work, use "//" to substitute
45
- * the doc root in the link paths (the array keys). E.g.:
46
- * <code>
47
- * array('//symlink' => '/real/target/path') // unix
48
- * array('//static' => 'D:\\staticStorage') // Windows
49
- * </code>
50
- *
51
- * 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
52
- * see Minify_CSS_UriRewriter::rewrite
53
- *
54
- * @return string
55
- */
56
- public static function minify($css, $options = array())
57
- {
58
- $options = array_merge(array(
59
- 'compress' => true,
60
- 'removeCharsets' => true,
61
- 'preserveComments' => true,
62
- 'currentDir' => null,
63
- 'docRoot' => $_SERVER['DOCUMENT_ROOT'],
64
- 'prependRelativePath' => null,
65
- 'symlinks' => array(),
66
- ), $options);
67
-
68
- if ($options['removeCharsets']) {
69
- $css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
70
- }
71
- if ($options['compress']) {
72
- if (! $options['preserveComments']) {
73
- $css = Minify_CSS_Compressor::process($css, $options);
74
- } else {
75
- $css = Minify_CommentPreserver::process(
76
- $css
77
- ,array('Minify_CSS_Compressor', 'process')
78
- ,array($options)
79
- );
80
- }
81
- }
82
- if (! $options['currentDir'] && ! $options['prependRelativePath']) {
83
- return $css;
84
- }
85
- if ($options['currentDir']) {
86
- return Minify_CSS_UriRewriter::rewrite(
87
- $css
88
- ,$options);
89
- } else {
90
- return Minify_CSS_UriRewriter::prepend(
91
- $css
92
- ,$options['prependRelativePath']
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
- * rewrite() and rewriteRelative() append debugging information here
17
- *
18
- * @var string
19
- */
20
- public static $debugText = '';
21
-
22
- /**
23
- * In CSS content, rewrite file relative URIs as root relative
24
- *
25
- * @param string $css
26
- *
27
- * @param string $currentDir The directory of the current CSS file.
28
- *
29
- * @param string $docRoot The document root of the web site in which
30
- * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
31
- *
32
- * @param array $symlinks (default = array()) If the CSS file is stored in
33
- * a symlink-ed directory, provide an array of link paths to
34
- * target paths, where the link paths are within the document root. Because
35
- * paths need to be normalized for this to work, use "//" to substitute
36
- * the doc root in the link paths (the array keys). E.g.:
37
- * <code>
38
- * array('//symlink' => '/real/target/path') // unix
39
- * array('//static' => 'D:\\staticStorage') // Windows
40
- * </code>
41
- *
42
- * @return string
43
- */
44
- public static function rewrite($css, $options) {
45
- self::$_prependPath = null;
46
-
47
- if (!isset($options['prependRelativePath']) && !isset($options['currentDir']))
48
- return $css;
49
-
50
- self::$_browserCacheId = (isset($options['browserCacheId']) ?
51
- $options['browserCacheId'] : 0);
52
- self::$_browserCacheExtensions =
53
- (isset($options['browserCacheExtensions']) ?
54
- $options['browserCacheExtensions'] : array());
55
-
56
- if (isset($options['currentDir'])) {
57
- $document_root = (isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT']);
58
- $symlinks = (isset($options['symlinks']) ? $options['symlinks'] : array());
59
- $prependAbsolutePath = (isset($options['prependAbsolutePath']) ? $options['prependAbsolutePath'] : '');
60
- $prependAbsolutePathCallback = (isset($options['prependAbsolutePathCallback']) ? $options['prependAbsolutePathCallback'] : '');
61
-
62
- $css = self::_rewrite(
63
- $css,
64
- $options['currentDir'],
65
- $prependAbsolutePath,
66
- $prependAbsolutePathCallback,
67
- $document_root,
68
- $symlinks
69
- );
70
- } elseif (isset($options['prependRelativePath'])) {
71
- $css = self::prepend(
72
- $css,
73
- $options['prependRelativePath']
74
- );
75
- }
76
-
77
- return $css;
78
- }
79
-
80
- /**
81
- * Rewrite file relative URIs as root relative in CSS files
82
- *
83
- * @param string $css
84
- *
85
- * @param string $currentDir The directory of the current CSS file.
86
- *
87
- * @param string $prependAbsolutePath
88
- *
89
- * @param string $prependAbsolutePathCallback
90
- *
91
- * @param string $docRoot The document root of the web site in which
92
- * the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
93
- *
94
- * @param array $symlinks (default = array()) If the CSS file is stored in
95
- * a symlink-ed directory, provide an array of link paths to
96
- * target paths, where the link paths are within the document root. Because
97
- * paths need to be normalized for this to work, use "//" to substitute
98
- * the doc root in the link paths (the array keys). E.g.:
99
- * <code>
100
- * array('//symlink' => '/real/target/path') // unix
101
- * array('//static' => 'D:\\staticStorage') // Windows
102
- * </code>
103
- *
104
- * @param int $browserCacheId
105
- *
106
- * @param array $browserCacheExtensions
107
- *
108
- * @return string
109
- */
110
- private static function _rewrite($css, $currentDir,
111
- $prependAbsolutePath = null, $prependAbsolutePathCallback = null,
112
- $docRoot = null, $symlinks = array()) {
113
- self::$_docRoot = self::_realpath(
114
- $docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
115
- );
116
- self::$_currentDir = self::_realpath($currentDir);
117
- self::$_prependAbsolutePath = $prependAbsolutePath;
118
- self::$_prependAbsolutePathCallback = $prependAbsolutePathCallback;
119
- self::$_symlinks = array();
120
-
121
- // normalize symlinks
122
- foreach ($symlinks as $link => $target) {
123
- $link = ($link === '//')
124
- ? self::$_docRoot
125
- : str_replace('//', self::$_docRoot . '/', $link);
126
- $link = strtr($link, '/', DIRECTORY_SEPARATOR);
127
- self::$_symlinks[$link] = self::_realpath($target);
128
- }
129
-
130
- self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
131
- . "currentDir : " . self::$_currentDir . "\n";
132
- if (self::$_symlinks) {
133
- self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
134
- }
135
- self::$debugText .= "\n";
136
-
137
- $css = self::_trimUrls($css);
138
-
139
- // rewrite
140
- $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
141
- ,array(self::$className, '_processUriCB'), $css);
142
- $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
143
- ,array(self::$className, '_processUriCB'), $css);
144
-
145
- return $css;
146
- }
147
-
148
- /**
149
- * In CSS content, prepend a path to relative URIs
150
- *
151
- * @param string $css
152
- *
153
- * @param string $path The path to prepend.
154
- *
155
- * @return string
156
- */
157
- public static function prepend($css, $path)
158
- {
159
- self::$_prependPath = $path;
160
-
161
- $css = self::_trimUrls($css);
162
-
163
- // append
164
- $css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
165
- ,array(self::$className, '_processUriCB'), $css);
166
- $css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
167
- ,array(self::$className, '_processUriCB'), $css);
168
-
169
- return $css;
170
- }
171
-
172
- /**
173
- * Get a root relative URI from a file relative URI
174
- *
175
- * <code>
176
- * Minify_CSS_UriRewriter::rewriteRelative(
177
- * '../img/hello.gif'
178
- * , '/home/user/www/css' // path of CSS file
179
- * , '/home/user/www' // doc root
180
- * );
181
- * // returns '/img/hello.gif'
182
- *
183
- * // example where static files are stored in a symlinked directory
184
- * Minify_CSS_UriRewriter::rewriteRelative(
185
- * 'hello.gif'
186
- * , '/var/staticFiles/theme'
187
- * , '/home/user/www'
188
- * , array('/home/user/www/static' => '/var/staticFiles')
189
- * );
190
- * // returns '/static/theme/hello.gif'
191
- * </code>
192
- *
193
- * @param string $uri file relative URI
194
- *
195
- * @param string $realCurrentDir realpath of the current file's directory.
196
- *
197
- * @param string $realDocRoot realpath of the site document root.
198
- *
199
- * @param array $symlinks (default = array()) If the file is stored in
200
- * a symlink-ed directory, provide an array of link paths to
201
- * real target paths, where the link paths "appear" to be within the document
202
- * root. E.g.:
203
- * <code>
204
- * array('/home/foo/www/not/real/path' => '/real/target/path') // unix
205
- * array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
206
- * </code>
207
- *
208
- * @return string
209
- */
210
- private static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
211
- {
212
- if ('/' === substr($uri, 0, 1)) { // root-relative
213
- return $uri;
214
- }
215
-
216
- // prepend path with current dir separator (OS-independent)
217
- $path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
218
- . DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
219
-
220
- self::$debugText .= "file-relative URI : {$uri}\n"
221
- . "path prepended : {$path}\n";
222
-
223
- // "unresolve" a symlink back to doc root
224
- foreach ($symlinks as $link => $target) {
225
- if (0 === strpos($path, $target)) {
226
- // replace $target with $link
227
- $path = $link . substr($path, strlen($target));
228
-
229
- self::$debugText .= "symlink unresolved : {$path}\n";
230
-
231
- break;
232
- }
233
- }
234
- // strip doc root
235
- $path = substr($path, strlen($realDocRoot));
236
-
237
- self::$debugText .= "docroot stripped : {$path}\n";
238
-
239
- // fix to root-relative URI
240
- $uri = strtr($path, '/\\', '//');
241
- $uri = self::removeDots($uri);
242
-
243
- self::$debugText .= "traversals removed : {$uri}\n\n";
244
-
245
- return $uri;
246
- }
247
-
248
- /**
249
- * Remove instances of "./" and "../" where possible from a root-relative URI
250
- *
251
- * @param string $uri
252
- *
253
- * @return string
254
- */
255
- public static function removeDots($uri)
256
- {
257
- $uri = str_replace('/./', '/', $uri);
258
- // inspired by patch from Oleg Cherniy
259
- do {
260
- $uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
261
- } while ($changed);
262
- return $uri;
263
- }
264
-
265
- /**
266
- * Defines which class to call as part of callbacks, change this
267
- * if you extend Minify_CSS_UriRewriter
268
- *
269
- * @var string
270
- */
271
- protected static $className = 'Minify_CSS_UriRewriter';
272
-
273
- /**
274
- * Get realpath with any trailing slash removed. If realpath() fails,
275
- * just remove the trailing slash.
276
- *
277
- * @param string $path
278
- *
279
- * @return mixed path with no trailing slash
280
- */
281
- protected static function _realpath($path)
282
- {
283
- $realPath = realpath($path);
284
- if ($realPath !== false) {
285
- $path = $realPath;
286
- }
287
- return rtrim($path, '/\\');
288
- }
289
-
290
- /**
291
- * Directory of this stylesheet
292
- *
293
- * @var string
294
- */
295
- private static $_currentDir = '';
296
-
297
- /**
298
- * DOC_ROOT
299
- *
300
- * @var string
301
- */
302
- private static $_docRoot = '';
303
-
304
- /**
305
- * directory replacements to map symlink targets back to their
306
- * source (within the document root) E.g. '/var/www/symlink' => '/var/realpath'
307
- *
308
- * @var array
309
- */
310
- private static $_symlinks = array();
311
-
312
- /**
313
- * Path to prepend
314
- *
315
- * @var string
316
- */
317
- private static $_prependPath = null;
318
-
319
- /**
320
- * @var string
321
- */
322
- private static $_prependAbsolutePath = null;
323
-
324
- /**
325
- * @var string
326
- */
327
- private static $_prependAbsolutePathCallback = null;
328
-
329
- /**
330
- * @var int
331
- */
332
- private static $_browserCacheId = 0;
333
-
334
- /**
335
- * @var array
336
- */
337
- private static $_browserCacheExtensions = array();
338
-
339
-
340
- /**
341
- * @param string $css
342
- *
343
- * @return string
344
- */
345
- private static function _trimUrls($css)
346
- {
347
- return preg_replace('/
348
- url\\( # url(
349
- \\s*
350
- ([^\\)]+?) # 1 = URI (assuming does not contain ")")
351
- \\s*
352
- \\) # )
353
- /x', 'url($1)', $css);
354
- }
355
-
356
- /**
357
- * @param array $m
358
- *
359
- * @return string
360
- */
361
- private static function _processUriCB($m)
362
- {
363
- // $m matched either '/@import\\s+([\'"])(.*?)[\'"]/' or '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
364
- $isImport = ($m[0][0] === '@');
365
- // determine URI and the quote character (if any)
366
- if ($isImport) {
367
- $quoteChar = $m[1];
368
- $uri = $m[2];
369
- } else {
370
- // $m[1] is either quoted or not
371
- $quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
372
- ? $m[1][0]
373
- : '';
374
- $uri = ($quoteChar === '')
375
- ? $m[1]
376
- : substr($m[1], 1, strlen($m[1]) - 2);
377
- }
378
- // analyze URI
379
- if ( !empty($uri)
380
- && '/' !== substr($uri, 0, 1) // Root-relative (/).
381
- && false === strpos( $uri, '//' ) // Protocol/non-data (//).
382
- && 'data:' !== substr( $uri, 0, 5 ) // Data protocol.
383
- && '%' !== substr( $uri, 0, 1 ) // URL-encoded entity.
384
- && '#' !== substr( $uri, 0, 1 ) // URL fragment.
385
- ) {
386
- // URI is file-relative: rewrite depending on options
387
- if (self::$_prependPath === null) {
388
- $uri = self::rewriteRelative($uri, self::$_currentDir, self::$_docRoot, self::$_symlinks);
389
- if (self::$_prependAbsolutePath) {
390
- $prependAbsolutePath = self::$_prependAbsolutePath;
391
- } elseif (self::$_prependAbsolutePathCallback) {
392
- $prependAbsolutePath = call_user_func(self::$_prependAbsolutePathCallback, $uri);
393
- } else {
394
- $prependAbsolutePath = '';
395
- }
396
-
397
- if ($prependAbsolutePath) {
398
- $uri = rtrim($prependAbsolutePath, '/') . $uri;
399
- }
400
- } else {
401
- if (!\W3TC\Util_Environment::is_url(self::$_prependPath)) {
402
  $uri = self::$_prependPath . $uri;
403
 
404
- if (substr($uri, 0, 1) === '/') {
405
- $root = '';
406
- $rootRelative = $uri;
407
- $uri = $root . self::removeDots($rootRelative);
408
- } elseif (preg_match('@^((https?\:)?//([^/]+))/@', $uri, $m) && (false !== strpos($m[3], '.'))) {
409
- $root = $m[1];
410
- $rootRelative = substr($uri, strlen($root));
411
- $uri = $root . self::removeDots($rootRelative);
412
- }
413
- } else {
414
- $parse_url = @parse_url(self::$_prependPath);
415
-
416
- if ($parse_url && isset($parse_url['host'])) {
417
- $scheme = array_key_exists('scheme', $parse_url) ? $parse_url['scheme'] : '';
418
- $host = $parse_url['host'];
419
- $port = (isset($parse_url['port']) && $parse_url['port'] != 80 ? ':' . (int) $parse_url['port'] : '');
420
- $path = (!empty($parse_url['path']) ? $parse_url['path'] : '/');
421
- $dir_css = preg_replace('~[^/]+$~', '', $path);
422
- $dir_obj = preg_replace('~[^/]+$~', '', $uri);
423
- $dir = (ltrim((strpos($dir_obj, '/') === 0 ? \W3TC\Util_Environment::realpath($dir_obj) : \W3TC\Util_Environment::realpath($dir_css . $dir_obj)), '/'));
424
- $file = basename($uri);
425
-
426
- $scheme_dot = ( empty( $scheme ) ? '' : $scheme . ':' );
427
- $uri = sprintf('%s//%s%s/%s/%s', $scheme_dot, $host,
428
- $port, $dir, $file);
429
- }
430
- }
431
- }
432
-
433
- if (self::$_browserCacheId && count(self::$_browserCacheExtensions)) {
434
- $matches = null;
435
-
436
- if (preg_match('~\.([a-z-_]+)(\?.*)?$~', $uri, $matches)) {
437
- $extension = $matches[1];
438
-
439
- if ($extension && in_array($extension, self::$_browserCacheExtensions)) {
440
- $uri = \W3TC\Util_Environment::remove_query($uri);
441
- $uri .= ( strpos( $uri, '?' ) !== false ? '&' : '?' ) . self::$_browserCacheId;
442
- }
443
- }
444
- }
445
- }
446
- return $isImport
447
- ? "@import {$quoteChar}{$uri}{$quoteChar}"
448
- : "url({$quoteChar}{$uri}{$quoteChar})";
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
- * Filepath of the Closure Compiler jar file. This must be set before
38
- * calling minifyJs().
39
- *
40
- * @var string
41
- */
42
- public static $jarFile = null;
43
 
44
- /**
45
- * Writable temp directory. This must be set before calling minifyJs().
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 https://code.google.com/p/closure-compiler/source/browse/trunk/README
66
- *
67
- * @return string
68
- */
69
- public static function minify($js, $options = array())
70
- {
71
- self::_prepare();
72
- if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) {
73
- throw new Exception('Minify_ClosureCompiler : could not create temp file.');
74
- }
75
- file_put_contents($tmpFile, $js);
76
- exec(self::_getCmd($options, $tmpFile), $output, $result_code);
77
- unlink($tmpFile);
78
- if ($result_code != 0) {
79
- throw new Exception('Minify_ClosureCompiler : Closure Compiler execution failed.');
80
- }
81
- return implode("\n", $output);
82
- }
83
 
84
- private static function _getCmd($userOptions, $tmpFile)
85
- {
86
- $o = array_merge(
87
- array(
88
- 'charset' => 'utf-8',
89
- 'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
90
- ),
91
- $userOptions
92
- );
93
 
94
- $javaExecutable = self::$javaExecutable;
95
 
96
- if ( false !== strpos(trim($javaExecutable), ' ') ) {
97
- $javaExecutable = '"'.$javaExecutable.'"';
98
- }
99
 
100
- $cmd = $javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
101
- . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
102
- ? " --charset {$o['charset']}"
103
- : '');
104
 
105
- foreach (array('compilation_level') as $opt) {
106
- if ($o[$opt]) {
107
- $cmd .= " --{$opt} ". escapeshellarg($o[$opt]);
108
- }
109
- }
110
- return $cmd . ' ' . escapeshellarg($tmpFile);
111
- }
112
 
113
- private static function _prepare()
114
- {
115
- if (! is_file(self::$jarFile)) {
116
- throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
117
- }
118
- if (! is_readable(self::$jarFile)) {
119
- throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
120
- }
121
- if (! is_dir(self::$tempDir)) {
122
- throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
123
- }
124
- if (! is_writable(self::$tempDir)) {
125
- throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
126
- }
127
- }
128
 
129
- public static function test(&$error) {
130
- try {
131
- self::minify('alert("ok");');
132
- $error = 'OK';
133
 
134
- return true;
135
- } catch (Exception $exception) {
136
- $error = $exception->getMessage();
137
 
138
- return false;
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
- * 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
  }
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
- * Get default minifiers for this controller.
70
- *
71
- * Override in subclass to change defaults
72
- *
73
- * @return array minifier callbacks for common types
74
- */
75
- public function getDefaultMinifers() {
76
- $ret[Minify0_Minify::TYPE_JS] = array('Minify0_JSMin', 'minify');
77
- $ret[Minify0_Minify::TYPE_CSS] = array('Minify_CSS', 'minify');
78
- $ret[Minify0_Minify::TYPE_HTML] = array('Minify_HTML', 'minify');
79
- return $ret;
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
- * @param string $file
117
- * @param array $allowDirs
118
- * @param string $uri
119
- * @return bool
120
- * @throws Exception
121
- */
122
- public static function checkAllowDirs($file, $allowDirs, $uri)
123
- {
124
- foreach ((array)$allowDirs as $allowDir) {
125
- if (strpos($file, $allowDir) === 0) {
126
- return true;
127
- }
128
- }
129
- throw new Exception("File '$file' is outside \$allowDirs. If the path is"
130
- . " resolved via an alias/symlink, look into the \$min_symlinks option."
131
- . " E.g. \$min_symlinks['/" . dirname($uri) . "'] = '" . dirname($file) . "';");
132
- }
 
 
 
133
 
134
- /**
135
- * @param string $file
136
- * @throws Exception
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
- * instances of Minify_Source, which provide content and any individual minification needs.
148
- *
149
- * @var array
150
- *
151
- * @see Minify_Source
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
- * Mix in default controller options with user-given options
168
- *
169
- * @param array $options user options
170
- *
171
- * @return array mixed options
172
- */
173
- public final function mixInDefaultOptions($options)
174
- {
175
- $ret = array_merge(
176
- $this->getDefaultMinifyOptions(), $options
177
- );
178
- if (! isset($options['minifiers'])) {
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
- * Send message to the Minify logger
215
- *
216
- * @param string $msg
217
- *
218
- * @return null
219
- */
220
- public function log($msg) {
221
- Minify_Logger::log($msg);
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 " . Minify0_Minify::URL_DEBUG . " */\n"
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 = Minify0_Minify::TYPE_JS;
209
  } elseif ($sourceOrExt === 'css') {
210
- $type = Minify0_Minify::TYPE_CSS;
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
- if (isset($options['minifyAll'])) {
55
- // this will be the 2nd argument passed to Minify_HTML::minify()
56
- $sourceSpec['minifyOptions'] = array(
57
- 'cssMinifier' => array('Minify_CSS', 'minify')
58
- ,'jsMinifier' => array('Minify0_JSMin', 'minify')
59
- );
60
- unset($options['minifyAll']);
61
- }
62
- $this->sources[] = new Minify_Source($sourceSpec);
63
-
64
- $options['contentType'] = Minify0_Minify::TYPE_HTML;
65
- return $options;
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
- Minify0_Minify::setCache($cacheDir);
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
- const URL = 'http://closure-compiler.appspot.com/compile';
18
-
19
- /**
20
- * Minify Javascript code via HTTP request to the Closure Compiler API
21
- *
22
- * @param string $js input code
23
- * @param array $options unused at this point
24
- * @return string
25
- */
26
- public static function minify($js, array $options = array())
27
- {
28
- $obj = new self($options);
29
- return $obj->min($js);
30
- }
31
-
32
- /**
33
- *
34
- * @param array $options
35
- *
36
- * fallbackFunc : default array($this, 'fallback');
37
- */
38
- public function __construct(array $options = array())
39
- {
40
- $this->options = $options;
41
- $this->_fallbackFunc = isset($options['fallbackMinifier'])
42
- ? $options['fallbackMinifier']
43
- : array($this, '_fallback');
44
- }
45
-
46
- public function min($js)
47
- {
48
- if (trim($js) === '')
49
- return $js;
50
-
51
- $postBody = $this->_buildPostBody($js);
52
- $bytes = (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
53
- ? mb_strlen($postBody, '8bit')
54
- : strlen($postBody);
55
- if ($bytes > 200000)
56
- return $this->fail($js,
57
- 'File size is larger than Closure Compiler API limit (200000 bytes)');
58
-
59
- $response = $this->_getResponse($postBody);
60
- if (preg_match('/^Error\(\d\d?\):/', $response))
61
- return $this->fail($response,
62
- "Received errors from Closure Compiler API:\n$response");
63
-
64
- return $response;
65
- }
66
-
67
- private function fail($js, $errorMessage) {
68
- Minify0_Minify::$recoverableError = $errorMessage;
69
- $response = "/* " . $errorMessage . "\n(Using fallback minifier)\n*/\n";
70
- if (is_callable($this->_fallbackFunc))
71
- $response .= call_user_func($this->_fallbackFunc, $js);
72
- else
73
- $response .= $js;
74
-
75
- return $response;
76
- }
77
-
78
- protected $_fallbackFunc = null;
79
- protected $_options = array();
80
-
81
- protected function _getResponse($postBody)
82
- {
83
- $allowUrlFopen = preg_match('/1|yes|on|true/i', ini_get('allow_url_fopen'));
84
- if ($allowUrlFopen) {
85
- $contents = file_get_contents(self::URL, false, stream_context_create(array(
86
- 'http' => array(
87
- 'method' => 'POST',
88
- 'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
89
- 'content' => $postBody,
90
- 'max_redirects' => 0,
91
- 'timeout' => 15,
92
- )
93
- )));
94
- } elseif (defined('CURLOPT_POST')) {
95
- $ch = curl_init(self::URL);
96
- curl_setopt($ch, CURLOPT_POST, true);
97
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
98
- curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/x-www-form-urlencoded'));
99
- curl_setopt($ch, CURLOPT_POSTFIELDS, $postBody);
100
- curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
101
- curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 15);
102
- $contents = curl_exec($ch);
103
- curl_close($ch);
104
- } else {
105
- throw new Minify_JS_ClosureCompiler_Exception(
106
- "Could not make HTTP request: allow_url_open is false and cURL not available"
107
- );
108
- }
109
- if (false === $contents) {
110
- throw new Minify_JS_ClosureCompiler_Exception(
111
- "No HTTP response from server"
112
- );
113
- }
114
- return trim($contents);
115
- }
116
-
117
- protected function _buildPostBody($js, $returnErrors = false)
118
- {
119
- $a = array(
120
- 'js_code' => $js,
121
- 'output_info' => ($returnErrors ? 'errors' : 'compiled_code'),
122
- 'output_format' => 'text',
123
- 'compilation_level' =>
124
- (isset($this->options['compilation_level']) ?
125
- $this->options['compilation_level'] :
126
- 'SIMPLE_OPTIMIZATIONS')
127
- );
128
- if (isset($this->options['formatting']) && !empty($this->options['formatting']))
129
- $a['formatting'] = $this->options['formatting'];
130
-
131
- return http_build_query($a, null, '&');
132
- }
133
-
134
- /**
135
- * Default fallback function if CC API fails
136
- * @param string $js
137
- * @return string
138
- */
139
- protected function _fallback($js)
140
- {
141
- return Minify0_JSMin::minify($js);
142
- }
143
-
144
- public static function test(&$error) {
145
- try {
146
- self::minify('alert("ok");');
147
- $error = 'OK';
148
-
149
- return true;
150
- } catch (Exception $exception) {
151
- $error = $exception->getMessage();
152
-
153
- return false;
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(Minify0_Minify::$uploaderHoursBehind * 3600);
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
- * Minify a CSS string
27
- *
28
- * @param string $css
29
- *
30
- * @return string
31
- */
32
- public function compress($css, $linebreakpos = 0)
33
- {
34
- $css = str_replace("\r\n", "\n", $css);
35
-
36
- /**
37
- * @todo comment removal
38
- * @todo re-port from newer Java version
39
- */
40
-
41
- // Normalize all whitespace strings to single spaces. Easier to work with that way.
42
- $css = preg_replace('@\s+@', ' ', $css);
43
-
44
- // Make a pseudo class for the Box Model Hack
45
- $css = preg_replace("@\"\\\\\"}\\\\\"\"@", "___PSEUDOCLASSBMH___", $css);
46
-
47
- // Remove the spaces before the things that should not have spaces before them.
48
- // But, be careful not to turn "p :link {...}" into "p:link{...}"
49
- // Swap out any pseudo-class colons with the token, and then swap back.
50
- $css = preg_replace_callback("@(^|\\})(([^\\{:])+:)+([^\\{]*\\{)@", array($this, '_removeSpacesCB'), $css);
51
-
52
- $css = preg_replace("@\\s+([!{};:>+\\(\\)\\],])@", "$1", $css);
53
- $css = str_replace("___PSEUDOCLASSCOLON___", ":", $css);
54
-
55
- // Remove the spaces after the things that should not have spaces after them.
56
- $css = preg_replace("@([!{}:;>+\\(\\[,])\\s+@", "$1", $css);
57
-
58
- // Add the semicolon where it's missing.
59
- $css = preg_replace("@([^;\\}])}@", "$1;}", $css);
60
-
61
- // Replace 0(px,em,%) with 0.
62
- $css = preg_replace("@([\\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)@", "$1$2", $css);
63
-
64
- // Replace 0 0 0 0; with 0.
65
- $css = str_replace(":0 0 0 0;", ":0;", $css);
66
- $css = str_replace(":0 0 0;", ":0;", $css);
67
- $css = str_replace(":0 0;", ":0;", $css);
68
-
69
- // Replace background-position:0; with background-position:0 0;
70
- $css = str_replace("background-position:0;", "background-position:0 0;", $css);
71
-
72
- // Replace 0.6 to .6, but only when preceded by : or a white-space
73
- $css = preg_replace("@(:|\\s)0+\\.(\\d+)@", "$1.$2", $css);
74
-
75
- // Shorten colors from rgb(51,102,153) to #336699
76
- // This makes it more likely that it'll get further compressed in the next step.
77
- $css = preg_replace_callback("@rgb\\s*\\(\\s*([0-9,\\s]+)\\s*\\)@", array($this, '_shortenRgbCB'), $css);
78
-
79
- // Shorten colors from #AABBCC to #ABC. Note that we want to make sure
80
- // the color is not preceded by either ", " or =. Indeed, the property
81
- // filter: chroma(color="#FFFFFF");
82
- // would become
83
- // filter: chroma(color="#FFF");
84
- // which makes the filter break in IE.
85
- $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);
86
-
87
- // Remove empty rules.
88
- $css = preg_replace("@[^\\}]+\\{;\\}@", "", $css);
89
-
90
- $linebreakpos = isset($this->_options['linebreakpos'])
91
- ? $this->_options['linebreakpos']
92
- : 0;
93
-
94
- if ($linebreakpos > 0) {
95
- // Some source control tools don't like it when files containing lines longer
96
- // than, say 8000 characters, are checked in. The linebreak option is used in
97
- // that case to split long lines after a specific column.
98
- $i = 0;
99
- $linestartpos = 0;
100
- $sb = $css;
101
-
102
- // make sure strlen returns byte count
103
- $mbIntEnc = null;
104
- if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
105
- $mbIntEnc = mb_internal_encoding();
106
- mb_internal_encoding('8bit');
107
- }
108
- $sbLength = strlen($css);
109
- while ($i < $sbLength) {
110
- $c = $sb[$i++];
111
- if ($c === '}' && $i - $linestartpos > $linebreakpos) {
112
- $sb = substr_replace($sb, "\n", $i, 0);
113
- $sbLength++;
114
- $linestartpos = $i;
115
- }
116
- }
117
- $css = $sb;
118
-
119
- // undo potential mb_encoding change
120
- if ($mbIntEnc !== null) {
121
- mb_internal_encoding($mbIntEnc);
122
- }
123
- }
124
-
125
- // Replace the pseudo class for the Box Model Hack
126
- $css = str_replace("___PSEUDOCLASSBMH___", "\"\\\\\"}\\\\\"\"", $css);
127
-
128
- // Replace multiple semi-colons in a row by a single one
129
- // See SF bug #1980989
130
- $css = preg_replace("@;;+@", ";", $css);
131
-
132
- // prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
133
- $css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
134
-
135
- // Trim the final string (for any leading or trailing white spaces)
136
- $css = trim($css);
137
-
138
- return $css;
139
- }
140
-
141
- protected function _removeSpacesCB($m)
142
- {
143
- return str_replace(':', '___PSEUDOCLASSCOLON___', $m[0]);
144
- }
145
-
146
- protected function _shortenRgbCB($m)
147
- {
148
- $rgbcolors = explode(',', $m[1]);
149
- $hexcolor = '#';
150
- for ($i = 0; $i < count($rgbcolors); $i++) {
151
- $val = round($rgbcolors[$i]);
152
- if ($val < 16) {
153
- $hexcolor .= '0';
154
- }
155
- $hexcolor .= dechex($val);
156
- }
157
- return $hexcolor;
158
- }
159
-
160
- protected function _shortenHexCB($m)
161
- {
162
- // Test for AABBCC pattern
163
- if ((strtolower($m[3])===strtolower($m[4])) &&
164
- (strtolower($m[5])===strtolower($m[6])) &&
165
- (strtolower($m[7])===strtolower($m[8]))) {
166
- return $m[1] . $m[2] . "#" . $m[3] . $m[5] . $m[7];
167
- } else {
168
- return $m[0];
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
- * Filepath of the YUI Compressor jar file. This must be set before
36
- * calling minifyJs() or minifyCss().
37
- *
38
- * @var string
39
- */
40
- public static $jarFile = null;
41
-
42
- /**
43
- * Writable temp directory. This must be set before calling minifyJs()
44
- * or minifyCss().
45
- *
46
- * @var string
47
- */
48
- public static $tempDir = null;
49
-
50
- /**
51
- * Filepath of "java" executable (may be needed if not in shell's PATH)
52
- *
53
- * @var string
54
- */
55
- public static $javaExecutable = 'java';
56
-
57
- /**
58
- * Minify a Javascript string
59
- *
60
- * @param string $js
61
- *
62
- * @param array $options (verbose is ignored)
63
- *
64
- * @see http://www.julienlecomte.net/yuicompressor/README
65
- *
66
- * @return string
67
- */
68
- public static function minifyJs($js, $options = array())
69
- {
70
- return self::_minify('js', $js, $options);
71
- }
72
-
73
- /**
74
- * Minify a CSS string
75
- *
76
- * @param string $css
77
- *
78
- * @param array $options (verbose is ignored)
79
- *
80
- * @see http://www.julienlecomte.net/yuicompressor/README
81
- *
82
- * @return string
83
- */
84
- public static function minifyCss($css, $options = array())
85
- {
86
- $css = self::_minify('css', $css, $options);
87
- $css = Minify_CSS_UriRewriter::rewrite($css, $options);
88
-
89
- return $css;
90
- }
91
-
92
- private static function _minify($type, $content, $options)
93
- {
94
- self::_prepare();
95
- if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
96
- throw new Exception('Minify_YUICompressor : could not create temp file.');
97
- }
98
- file_put_contents($tmpFile, $content);
99
- exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code);
100
- unlink($tmpFile);
101
- if ($result_code != 0) {
102
- throw new Exception('Minify_YUICompressor : YUI compressor execution failed.');
103
- }
104
- return implode("\n", $output);
105
- }
106
-
107
- private static function _getCmd($userOptions, $type, $tmpFile)
108
- {
109
- if (!is_file(self::$javaExecutable)) {
110
- throw new Exception(sprintf('JAVA executable (%s) is not a valid file.', self::$javaExecutable));
111
- }
112
-
113
- if (!is_file(self::$jarFile)) {
114
- throw new Exception(sprintf('JAR file (%s) is not a valid file.', self::$jarFile));
115
- }
116
- $o = array_merge(
117
- array(
118
- 'charset' => ''
119
- ,'line-break' => 5000
120
- ,'type' => $type
121
- ,'nomunge' => false
122
- ,'preserve-semi' => false
123
- ,'disable-optimizations' => false
124
- ,'stack-size' => ''
125
- )
126
- ,$userOptions
127
- );
128
 
129
  $javaExecutable = self::$javaExecutable;
130
- if ( false !== strpos(trim($javaExecutable), ' ') ) {
131
- $javaExecutable = '"'. $javaExecutable . '"';
132
- }
133
-
134
- $cmd = $javaExecutable
135
- . (!empty($o['stack-size'])
136
- ? ' -Xss' . $o['stack-size']
137
- : '')
138
- . ' -jar ' . escapeshellarg(self::$jarFile)
139
- . " --type {$type}"
140
- . (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
141
- ? " --charset {$o['charset']}"
142
- : '')
143
- . (is_numeric($o['line-break']) && $o['line-break'] >= 0
144
- ? ' --line-break ' . (int)$o['line-break']
145
- : '');
146
- if ($type === 'js') {
147
- foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) {
148
- $cmd .= $o[$opt]
149
- ? " --{$opt}"
150
- : '';
151
- }
152
- }
153
- return $cmd . ' ' . escapeshellarg($tmpFile);
154
- }
155
-
156
- private static function _prepare()
157
- {
158
- if (! is_file(self::$jarFile)) {
159
- throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not a valid file.');
160
- }
161
- if (! is_readable(self::$jarFile)) {
162
- throw new Exception('Minify_YUICompressor : $jarFile('.self::$jarFile.') is not readable.');
163
- }
164
- if (! is_dir(self::$tempDir)) {
165
- throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not a valid direcotry.');
166
- }
167
- if (! is_writable(self::$tempDir)) {
168
- throw new Exception('Minify_YUICompressor : $tempDir('.self::$tempDir.') is not writable.');
169
- }
170
- }
171
-
172
- public static function testJs(&$error) {
173
- try {
174
- Minify_YUICompressor::minifyJs('alert("ok");');
175
- $error = 'OK';
176
-
177
- return true;
178
- } catch (Exception $exception) {
179
- $error = $exception->getMessage();
180
-
181
- return false;
182
- }
183
- }
184
-
185
- public static function testCss(&$error) {
186
- try {
187
- Minify_YUICompressor::minifyCss('p{color:red}');
188
- $error = 'OK';
189
-
190
- return true;
191
- } catch (Exception $exception) {
192
- $error = $exception->getMessage();
193
-
194
- return false;
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
6
- Stable tag: 2.1.0
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.0' );
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, 5 ) == 'HTTP_' || substr( $class, 0, 7 ) == 'Minify_' ) {
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 ( !is_null( $base ) ) {
 
 
 
 
180
  $file = $base . strtr( $class, "\\_",
181
  DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR ) . '.php';
182
  if ( file_exists( $file ) )
183
  require_once $file;
184
- } else if ( substr( $class, 0, 5 ) == 'W3TC\\' ) {
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.0
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