1
0
mirror of https://github.com/mrclay/minify.git synced 2025-09-04 03:25:33 +02:00

366 Commits

Author SHA1 Message Date
Elan Ruusamäe
e1c8b925be Fill changelog for 4.0.1 2025-02-03 10:53:27 +02:00
Elan Ruusamäe
9eb43c9f9f Add reflink to 4.0.0 release in changelog 2025-02-03 10:50:10 +02:00
Elan Ruusamäe
5597771d6e Update .semver to 4.0.1 2025-02-03 10:48:56 +02:00
Elan Ruusamäe
2d5ca3614e Fix ubuntu typo in github actions 2025-02-03 10:47:31 +02:00
Elan Ruusamäe
1a8cdbb6c7 Merge pull request #714 from thirsch/feature/php84
Add support for php 8.4
2025-02-03 10:31:59 +02:00
Thomas A. Hirsch
c222c65211 adding php 8.4 to github actions 2025-01-28 15:50:49 +01:00
Thomas A. Hirsch
60f49fa1e3 explicitly mark parameters as nullable 2025-01-28 15:50:41 +01:00
Elan Ruusamäe
230770a277 Merge pull request #711 from mrclay/deprecation
Recommend against use
2024-06-19 13:18:46 +03:00
Steve Clay
14b1d60448 Recommend against use 2024-06-13 23:57:49 -04:00
Steve Clay
43fb768c13 Fixes #693 2024-01-04 01:29:02 -05:00
Steve Clay
6166d3b785 Skip some tests on CI 2024-01-04 01:19:42 -05:00
Steve Clay
e84d55d4d6 Use Github actions 2024-01-04 01:12:40 -05:00
Steve Clay
45463786f8 Merge pull request #709 from mrclay/docker
Updates
2024-01-04 01:08:48 -05:00
Steve Clay
00e5e20abe Changelog update 2024-01-04 01:08:31 -05:00
Steve Clay
9e182a6255 Use scssphp/scssphp 2024-01-04 01:00:44 -05:00
Steve Clay
bc1ffdc5fc Test fixes 2024-01-04 01:00:44 -05:00
Steve Clay
aa3721156e Require PHP 8.1. Fixes #708 2024-01-04 01:00:35 -05:00
Steve Clay
3047f5c48f Add docker-compose setup 2024-01-03 21:41:47 -05:00
Elan Ruusamäe
104ccd2bfe Update .semver file 2023-05-05 15:03:33 +03:00
Elan Ruusamäe
dc02cdfba7 Fill changelog for 3.0.14 2023-05-05 15:03:06 +03:00
Elan Ruusamäe
23f055d3f5 Merge pull request #705 from rossbearman/monolog-v3 2023-04-13 23:03:05 +03:00
Ross Bearman
4970127e91 Support monolog v3 2023-04-12 16:11:35 +01:00
Elan Ruusamäe
39c37bdd8c Merge pull request #700 from turbulent/feat/allow-invalidation 2022-12-15 12:42:05 +02:00
Adrien Poupa
4a9d0595e3 feat: Allow invalidation from manual invocation 2022-12-14 15:21:52 -05:00
Elan Ruusamäe
ca8fdc6fdd Merge pull request #699 from tamakianimalife/php8.2
Add property declaration
2022-12-14 20:52:27 +02:00
tamakianimalife
8c69866874 Add property declaration 2022-12-14 17:30:53 +09:00
Elan Ruusamäe
ae5b9f0bfb Add changelog for #688 2022-10-03 00:12:43 +03:00
Elan Ruusamäe
e74adc4f6b Add changelog for 3.0.13 release 2022-10-03 00:11:04 +03:00
Elan Ruusamäe
5dee6538be Merge pull request #697 from croensch/apcu 2022-10-03 00:09:24 +03:00
Christoph Rönsch
455084b284 Minify_Cache_APCu replaces APC 2022-10-01 22:23:52 +02:00
Elan Ruusamäe
5112bb50f4 Merge pull request #688 from mrclay/lesserphp 2022-05-14 14:15:55 +03:00
Elan Ruusamäe
1962d3614c Fill changelog for 3.0.12 2022-05-14 14:11:27 +03:00
Elan Ruusamäe
2226a6add1 Merge pull request #696 from 0m3r/patch-1
Fix null argument to preg_split
2022-05-14 14:11:22 +03:00
Oleksandr Krasko
8b91de51c3 Fix null argument to preg_split
Deprecated Functionality: preg_split(): Passing null to parameter #3 ($limit) of type int is deprecated

https://www.php.net/manual/en/function.preg-split.php#refsect1-function.preg-split-parameters

limit:

If specified, then only substrings up to limit are returned with the
rest of the string being placed in the last substring. A limit of -1 or
0 means "no limit".

Refs:
- https://www.drupal.org/project/geshifilter/issues/3262325

Signed-off-by: Elan Ruusamäe <glen@pld-linux.org>
2022-05-14 14:01:14 +03:00
Elan Ruusamäe
1c000f9d05 Merge pull request #692 from venkatraj/dev-master 2022-01-04 10:14:13 +02:00
N. Venkat Raj
cbda49556e updated jquery version to avoid xss attacks 2022-01-04 13:36:07 +05:30
Elan Ruusamäe
b314554a0f Remove php 7.4 block for LessSourceTest 2021-03-11 17:54:46 +02:00
Elan Ruusamäe
9dac0ac873 Require marcusschwarz/lesserphp ^0.5.5
This resolves php 7.4 compatibility
2021-03-11 17:54:29 +02:00
Elan Ruusamäe
c80add80a2 Merge pull request #687 from mrclay/php-8-testing 2021-03-11 17:51:57 +02:00
Elan Ruusamäe
ad732493b8 Remove php 7.4 from allow failures 2021-03-11 14:04:50 +02:00
Elan Ruusamäe
ad0fb95556 Disable less tests on php 7.4+ 2021-03-11 14:04:45 +02:00
Elan Ruusamäe
f1572a580a Prepare for 3.0.11 release 2021-03-11 13:58:14 +02:00
Elan Ruusamäe
40f9dafe5f Merge pull request #685 from mrclay/php-8
Disable less tests on php 7.4+
2021-03-11 13:48:29 +02:00
Elan Ruusamäe
c814ff4bae Merge pull request #682 from angrybrad/master
Add PHP 8 support in composer.json
2021-03-11 13:47:59 +02:00
Elan Ruusamäe
8382fb85d8 Disable less tests on php 7.4+ 2020-12-01 16:16:43 +02:00
Elan Ruusamäe
efec97278f Merge pull request #677 from mrclay/php-7.4 2020-10-24 20:29:56 +03:00
Elan Ruusamäe
e9d4db41ac Add php nightly test
This adds php 8.0 to the test matrix
2020-10-24 18:52:22 +03:00
Elan Ruusamäe
b89bc57164 Fix Array and string offset access syntax with curly braces is deprecated 2020-10-24 18:52:22 +03:00
Brad Bell
69e3eb241f Add PHP 8 support in composer.json 2020-10-23 12:53:19 -07:00
Elan Ruusamäe
b17c00c05c Make code symmetric: Check on ConditionalGet as well
This partly reverts 5705fc9d89
2020-07-12 10:34:01 +03:00
Elan Ruusamäe
e3fe3001c3 Avoid calling get_magic_quotes_gpc() in tests for php >= 5.4 2020-07-12 10:33:58 +03:00
Elan Ruusamäe
8600312ed1 Merge pull request #672 from mrclay/fixer-psr2
Setup php-cs-fixer (@PSR2 rules only); Enable running in CI
2020-04-03 11:59:24 +03:00
Elan Ruusamäe
070c6c0349 Allow failures for php-cs-fixer 2020-04-03 10:48:29 +03:00
Elan Ruusamäe
b31855f6b8 Apply php-cs-fixer fixers
- braces
- function_declaration
- lowercase_keywords
- method_argument_space
- no_spaces_inside_parenthesis
- no_trailing_whitespace
- no_trailing_whitespace_in_comment
- single_blank_line_at_eof
2020-04-03 10:47:27 +03:00
Elan Ruusamäe
031e804d08 Run php-cs-fixer in Travis CI 2020-04-03 10:42:46 +03:00
Elan Ruusamäe
263381a23f Setup php-cs-fixer
Setup only to apply @PSR2 rules
2020-04-03 10:42:46 +03:00
Elan Ruusamäe
a36bfd50b0 Minor changelog fixes 2020-04-03 10:30:36 +03:00
Elan Ruusamäe
8dba84a2d2 Prepare for 3.0.10 release 2020-04-02 22:47:26 +03:00
Andrew Welch
0f607be23a Exclude SSI Comments (#670)
* Exclude SSI comments

Exclude Nginx & Apache Server Side Include (SSI) comments from being stripped via minification, the same way IE directives are not stripped.

Both Nginx & Apache SSI directives begin with:

<!—#

So we can just look for the # and don’t strip the comment in that case.

Nginx SSI: http://nginx.org/en/docs/http/ngx_http_ssi_module.html

Apache SSI: https://httpd.apache.org/docs/2.4/howto/ssi.html

* Add tests for Nginx/Apache SSI directives

Co-authored-by: Andrew Welch <andrew@keluli.local>
2020-04-02 22:45:11 +03:00
Elan Ruusamäe
d2a3e8c1d2 Setup .semver (gem install semver or gem install semver2) 2020-03-24 21:30:43 +02:00
Elan Ruusamäe
e59454bed5 Add 3.0.9 release notes 2020-03-24 21:28:47 +02:00
Elan Ruusamäe
eb5197f9d4 Update phpunit.xml deprecated options, exclude vendor from code-coverage 2020-03-24 21:26:16 +02:00
Elan Ruusamäe
20f336f598 Travis: use 7.4 instead of 7.4snapshot 2020-03-24 21:26:16 +02:00
Elan Ruusamäe
94829874f9 Allow intervention/httpauth 3.x 2020-03-24 21:26:16 +02:00
Elan Ruusamäe
f0aa0aa7d7 Add changelog for 3.0.8 2020-03-19 18:42:53 +02:00
robbykrlos
5705fc9d89 Function get_magic_quotes_gpc() is now deprecated in PHP 7.4 #661
- removed deprecated function that since PHP 5.4.0 returns FALSE always, and since PHP 7.4 is deprecated.
2020-03-06 19:20:17 +02:00
Elan Ruusamäe
3036ee9f55 Add more reflinks to changelog 2020-01-30 13:19:52 +02:00
Elan Ruusamäe
6e3ae8417a Allow marcusschwarz/lesserphp updates again 2020-01-30 13:09:30 +02:00
Elan Ruusamäe
d39fdd9e6b Update changelog for 3.0.7 2020-01-30 13:08:06 +02:00
Elan Ruusamäe
d35e780865 Force lesserphp to v0.5.2 because v0.5.3 changed namespace 2019-12-31 11:59:27 +02:00
Elan Ruusamäe
7378a0efe8 Composer: sort packages, set platform version 2019-12-10 08:31:58 +02:00
Jason Varga
c0dd8f50a1 Upgrade mrclay/props-dic (#658)
* Upgrade mrclay/props-dic
* Also support existing version
2019-12-08 22:38:04 +02:00
Elan Ruusamäe
63a812af8d Update changelog for 3.0.6 2019-10-28 13:39:13 +02:00
Elan Ruusamäe
8836df06d2 Bugfix for option sanitizer (#655) 2019-10-28 13:38:13 +02:00
Matthias Zronek
2f33b69786 Bugfix for option sanitizer 2019-10-28 11:03:44 +01:00
Elan Ruusamäe
8e4b0ceb86 Update changelog to format similar to keepachangelog.com 2019-10-01 13:09:59 +03:00
Elan Ruusamäe
266ddd7d54 Update changelog for 3.0.5 2019-10-01 13:04:39 +03:00
Elan Ruusamäe
07ed67aa77 Fix syntax error in composer.json (#653) 2019-10-01 13:03:36 +03:00
Elan Ruusamäe
2bbcce054d travis: add php 7.3, 7.4, 8.0; remove hhvm 2019-10-01 12:52:50 +03:00
Elan Ruusamäe
9040437901 Fix unit test for bd7d1077 2019-10-01 11:40:04 +03:00
Elan Ruusamäe
f954e1bd58 Fix syntax error in composer.json 2019-10-01 10:35:33 +03:00
Elan Ruusamäe
a8a77e6054 Set 3.0.4 release date 2019-09-24 08:12:01 +03:00
Elan Ruusamäe
25065b1559 Merge pull request #634 from dpauli/#633-Move-lesserphp-to-require
#633 move lesserphp to require
2019-09-19 10:31:19 +03:00
Elan Ruusamäe
807f41903e Merge pull request #642 from csabahete/master
Set "Vary: Accept-Encoding" header only if encoding is set and not empty
2019-09-19 10:30:56 +03:00
Elan Ruusamäe
b81ad2289a Merge pull request #646 from dli7319/master
Do not pass undefined variables into compact() for PHP 7.3
2019-09-19 10:30:25 +03:00
Elan Ruusamäe
fecf395de3 Allow monolog v2 (#650) 2019-09-19 10:29:27 +03:00
Jason Varga
2bd66ae3f9 use phpunit from vendor 2019-09-18 15:02:27 -04:00
Jason Varga
4453f3a4cb Allow monolog v2 2019-09-18 14:18:55 -04:00
David Li
f50353a952 Check if each variable is in the symbol table.
Delete if not before passing into compact.
2019-02-27 14:39:56 -05:00
Csaba Hete
bd7d1077b3 set "Vary: Accept-Encoding" header only if encoding is set and not empty 2018-11-06 19:24:25 +01:00
David Pauli
40f89b528c #633 Remove from suggest 2018-05-16 16:15:07 +02:00
David Pauli
b1bbdccef0 #633 Move marcusschwarz/lesserphp from require-dev to require 2018-05-16 16:13:44 +02:00
Elan Ruusamäe
258e495451 Revert "simplify substr third param"
This reverts commit 5659799c42.

the change actually broken MinifyImportProcessorTest
2018-01-05 19:13:28 +02:00
Elan Ruusamäe
b97a1db01d cleanup unneeded import 2018-01-05 19:09:42 +02:00
Elan Ruusamäe
3081a88dbd Merge pull request #623 from glensc/cs2
Code Quality fixes
2018-01-05 18:32:57 +02:00
Elan Ruusamäe
df7fddfa09 import classes instead of manual qualify 2018-01-05 14:35:57 +02:00
Elan Ruusamäe
c580d24e1b simplify return statement 2018-01-05 14:35:09 +02:00
Elan Ruusamäe
f9c96b6a3c use in_array instead of array_search (clearer intent) 2018-01-05 14:34:43 +02:00
Elan Ruusamäe
d3decb27e1 strict comparison 2018-01-05 14:34:06 +02:00
Elan Ruusamäe
0038cdb5de use empty string comparison instead of strlen 2018-01-05 14:34:06 +02:00
Elan Ruusamäe
5659799c42 simplify substr third param 2018-01-05 14:34:06 +02:00
Elan Ruusamäe
57be61586e use origin function instead of alias 2018-01-05 14:33:52 +02:00
Elan Ruusamäe
2198482600 drop useless return phpdoc annotation for constructor 2018-01-05 14:30:01 +02:00
Elan Ruusamäe
c2f40feb0c split workflows (no else after return) 2018-01-05 14:29:16 +02:00
Elan Ruusamäe
5c300aca97 do not define default value 2018-01-05 14:28:28 +02:00
Elan Ruusamäe
76c1edc6ff drop stale version comment 2018-01-05 14:27:22 +02:00
Elan Ruusamäe
919fc10139 Merge pull request #622 from glensc/travis-php-versions
travis: test php 5.3..7.3
2018-01-05 14:12:04 +02:00
Elan Ruusamäe
bb7fc74330 tests: rename dataproviders not to be picked up as tests 2018-01-05 13:13:20 +02:00
Elan Ruusamäe
eb4c0f6541 leafo/scssphp: allow 0.3/0.6/0.7
tests/ScssSourceTest.php passed with all versions

refs:
- commit ddf3a4e57f
- PR #562
2018-01-05 13:03:30 +02:00
Elan Ruusamäe
b6ef6fa397 travis: test php 5.3..7.3 2017-12-26 02:30:50 +02:00
Elan Ruusamäe
00b9f28630 Merge pull request #621 from glensc/phpunit
tests: move tests to Minify\Test namespace
2017-12-26 02:28:55 +02:00
Elan Ruusamäe
be40d4f9b2 fix namespaced _gzdecode 2017-12-22 18:18:07 +02:00
Elan Ruusamäe
9ee5fb7701 moved tests to minify\test namespace 2017-12-22 18:05:33 +02:00
Elan Ruusamäe
7d60fb9d0f use Minify\Test namespace for tests 2017-12-22 17:58:01 +02:00
Elan Ruusamäe
6236431c8f use forward-compatible phpunit testcase 2017-12-22 17:54:17 +02:00
Elan Ruusamäe
404664509c fill 3.0.3 release notes 2017-11-03 23:00:15 +02:00
Elan Ruusamäe
9ed7f9dc24 Fix closure-compiler's error "redirection limit reached". fixes #618, #619 2017-11-03 22:58:09 +02:00
Emanuele "ToX" Toscano
d9b392c474 Fix closure-compiler's "redirection limit reached"
Since a few days, the js minification was complaining about a redirection limit in the closure-compiler call. This was generating an error each time I have tried to use this tool.

After a bit of investigation I have found out that it was missing some parameters that are now mandatory. Plus, it now works in https only.
2017-11-03 10:24:25 +01:00
Steve Clay
004f6bf730 3.0.2 release notes 2017-09-14 18:13:04 -04:00
Steve Clay
72ece76cdf Fixes syntax error in Groups controller
Fixes #613
2017-09-14 18:03:49 -04:00
Elan Ruusamäe
bde7a41021 Merge pull request #610 from glensc/lesserphp
replace leafo/lessphp with marcusschwarz/lesserphp
2017-08-17 09:38:36 +03:00
Elan Ruusamäe
88aca4019d replace leafo/lessphp with marcusschwarz/lesserphp
This is a better maintained fork of lessphp. It seems to be fully
compatible, so this is more or less a drop in replacement.

Dokuwiki switched to this fork:
https://github.com/splitbrain/dokuwiki/pull/1969
2017-08-17 09:33:05 +03:00
Steve Clay
70952d88f9 Merge pull request #608 from benface/patch-1
Fix issue where minify() would corrupt some characters such as &nbsp; in some environments
2017-08-10 12:22:30 -04:00
Benoît Rouleau
6b274afd0b unicode flag everywhere to fix more issues with &nbsp; and MAMP 2017-08-09 17:22:32 -04:00
Benoît Rouleau
449f009b39 Fix issue where minify() would corrupt Unicode characters (such as &nbsp;) in some environments
Adding the `u` flag there fixes an issue in my environment (MAMP 4.1.1 with PHP 7.1.5) where `minify()` would corrupt the `&nbsp;` character (it would get replaced by the replacement character).
2017-07-20 20:34:49 -04:00
Elan Ruusamäe
6a5b09a00e Merge branch 'travis' 2017-06-08 22:33:53 +03:00
Elan Ruusamäe
93a1edd34b closure: use 20161024 version
this is version the example .min files were created
2017-06-08 22:29:56 +03:00
Elan Ruusamäe
a6531d3481 closure: use specific version
using latest may break our tests as minified result may differ
2017-06-08 22:02:16 +03:00
Elan Ruusamäe
9de63f3acc 3.0.1 release notes 2017-06-08 20:41:20 +03:00
Steve Clay
ed239fbb8b Update CSSmin to v4 2017-06-08 20:38:43 +03:00
Elan Ruusamäe
332f4bc839 cleanup 3.x branch leftover 2017-06-08 20:38:11 +03:00
Steve Clay
79e5a22321 Merge branch '3.x' 2017-05-23 16:24:09 -04:00
Elan Ruusamäe
80c01c6f02 php-cs-fixer fix 2017-04-18 18:24:17 +03:00
Elan Ruusamäe
b6e4b7f152 update cssmin import. resolves #590
refs a1489b2
2017-04-18 18:23:03 +03:00
Elan Ruusamäe
a1489b2d8a bump "tubalmartin/cssmin": "^3.1", fixes #590
https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/issues/39
2017-04-18 18:18:15 +03:00
Steve Clay
d041f763c9 even more relative links 2017-04-03 17:55:56 -04:00
Steve Clay
8056a4a9fb more relative docs links 2017-04-03 17:49:11 -04:00
Steve Clay
e3555570ef relative docs links 2017-04-03 17:47:37 -04:00
Steve Clay
a8304c5fb3 Update README.md 2017-04-03 17:43:17 -04:00
Steve Clay
dfb97a5c85 Checkout branch during install 2017-04-03 17:40:58 -04:00
Steve Clay
4626f6c1e0 HISTORY cleanup 2017-04-03 17:23:31 -04:00
Steve Clay
e904f2e1ae ImportProcessor can handle URIs with query strings
Fixes #479
2017-04-03 16:50:53 -04:00
Steve Clay
62370404c8 Make sure $min_documentRoot is copied into Env.
Fixes #585
2017-04-03 16:36:33 -04:00
Steve Clay
532a010e0e Parse LastModified date without requiring global timezone 2017-04-03 16:01:21 -04:00
srggroup
d95690abc9 Composer compatibility - check if library is inside the vendor dir 2017-03-27 08:44:02 -04:00
Steve Clay
f79794bece Upgrade CSSmin dependency
Fixes PHP 7.1 notices.
2017-03-13 11:24:44 -04:00
Steve Clay
5d334009ee Merge branch '2.x' 2017-03-13 11:20:03 -04:00
Steve Clay
cc1bd24af6 Merge pull request #541 from mrclay/static
Minify now allows static file serving
2017-01-20 09:15:03 -05:00
Steve Clay
5f15af469d Merge branch 'master' into static 2017-01-18 21:09:38 -05:00
Steve Clay
a07991c052 Merge pull request #563 from mrclay/561_empty_url
URI rewriter passes through empty URLs
2017-01-18 21:01:09 -05:00
Steve Clay
b874144e99 Merge pull request #562 from mrclay/less_fix_tests
Require later SCSS PHP for tests
2017-01-18 20:59:28 -05:00
Steve Clay
ddf3a4e57f Require later SCSS PHP for tests
No longer test PHP 5.3
2017-01-18 20:55:49 -05:00
Steve Clay
12b9096b23 URI rewriter passes through empty URLs
Fixes #561
2017-01-18 18:48:24 -05:00
Steve Clay
d1786045b2 Merge pull request #557 from MikeOtown/patch-1
correct typo - CSS -> JS
2016-12-22 10:49:05 -05:00
Michael Wasson
94436b214d correct typo - CSS -> JS 2016-12-22 08:43:05 -05:00
Elan Ruusamäe
1936c946e8 firephp/firephp-core is optional dependency 2016-12-08 16:13:19 +02:00
Elan Ruusamäe
625bc5ac0a fix unzip overwrite. refs 56d55ab 2016-12-06 20:38:17 +02:00
Elan Ruusamäe
1f5641ea2c Merge pull request #556 from glensc/closure-compiler-options
add support for closure compiler options
2016-12-06 20:17:15 +02:00
Elan Ruusamäe
6fc0648a0d phpunit: verbose output of run summary 2016-12-06 17:16:37 +02:00
Elan Ruusamäe
56d55abc01 travis: install closure compiler 2016-12-06 17:16:21 +02:00
Elan Ruusamäe
005dc26e22 tests: skip rather than fail if nailgun.jar is missing 2016-12-06 17:11:23 +02:00
Elan Ruusamäe
ed209f9a84 remove var_dump debug from test output 2016-12-06 17:11:23 +02:00
Elan Ruusamäe
8fe915f486 test $jscomp polyfill output 2016-12-06 17:11:04 +02:00
Elan Ruusamäe
6a330d9091 pass any option to closure compiler 2016-12-06 16:59:36 +02:00
Elan Ruusamäe
8949df33dd add testcase for js minify that produces $jscomp polyfills
with recent closure compiler default input language has changed from ES3 to ES6 produces $jscomp polyfills
2016-12-06 14:46:46 +02:00
Elan Ruusamäe
b8c79ac8d4 Minify: add details to ContentType mismatch errors 2016-11-02 15:53:15 +02:00
Elan Ruusamäe
b31ddbf4c1 add missing .scss to css type map. #549 2016-10-17 15:14:56 +03:00
Elan Ruusamäe
74042c87ad allow .scss via f= param. #549 2016-10-17 14:22:30 +03:00
Steve Clay
8b965059a1 Merge pull request #552 from mrclay/glensc-phpcs-psr2
phpcs: enable psr2 & style fixes
2016-10-16 20:53:10 -04:00
Steve Clay
16c811cd93 Big style cleanup 2016-10-16 15:13:38 -04:00
Elan Ruusamäe
5fb7ea1ed1 phpcs: enable psr2 2016-10-16 18:51:49 +03:00
Elan Ruusamäe
0cc631c5a9 php-cs fixes
1) Minify.php (return)
   2) Minify/Env.php (return)
   3) Minify/JS/JShrink.php (return)
   4) Minify/HTML/Helper.php (return)
   5) Minify/Logger/LegacyHandler.php (unused_use)
   6) Minify/Lines.php (return)
   7) Minify/Controller/Files.php (unused_use)
   8) Minify/ControllerInterface.php (unused_use)
   9) Minify/Cache/File.php (return)
2016-10-16 16:44:58 +03:00
Elan Ruusamäe
2809e3c2eb Merge pull request #550 from glensc/lessphp-cache
lessphp: use updated timestamp from cache
2016-10-16 13:51:17 +03:00
Elan Ruusamäe
a9891a031c Merge pull request #549 from mrclay/scssphp
add scss support via leafo/scssphp
2016-10-16 13:50:58 +03:00
Elan Ruusamäe
8d4a90e83f Merge pull request #548 from mrclay/checkAllowDirs
add checkAllowDirs to sourceFactoryOptions
2016-10-16 13:50:25 +03:00
Elan Ruusamäe
035183b2b8 cacheIsStale is not public 2016-10-13 14:40:21 +03:00
Elan Ruusamäe
72dd4db37a calculate lastmodified to be maximum accurate
the time() may be incorrect if there's slight delay between file modify and cache write. but as file modify is what is important, rely on that.

also needed to use different cache file because the format change.
2016-10-13 12:50:27 +03:00
Elan Ruusamäe
78d5921f91 lessphp: use updated timestamp from cache
no need to calculate the timestamp ourselves each time the calculated value already present in cache object
2016-10-13 12:40:48 +03:00
Elan Ruusamäe
2bd69ca5b1 test scssphp functionality
tested cache dependencies
2016-10-13 12:21:09 +03:00
Elan Ruusamäe
3014900cd4 add base directory of input file to import path
this is needed for @import to be able to resolve included files
2016-10-13 12:20:50 +03:00
Elan Ruusamäe
ae1fdf4a30 use lower scssphp version for php 5.3 compatibility
the functionality we are using is present
2016-10-13 12:18:52 +03:00
Elan Ruusamäe
b5a0ed3e3a ci: bump php version so deps could be installed on travis for php 5.3 2016-10-12 17:01:37 +03:00
Elan Ruusamäe
fdc69a369e add scss support via leafo/scssphp 2016-10-12 16:39:00 +03:00
Elan Ruusamäe
9ca1bc9e74 add checkAllowDirs to sourceFactoryOptions
needed to be able to specify the option via config
2016-10-12 16:34:39 +03:00
Elan Ruusamäe
60b89dcc0d disable server info by default again
mistakenly enabled in 9370e96
2016-09-20 23:36:24 +03:00
Steve Clay
59d4c97ffc Minify now allows static file serving
With slightly altered URLs, Minify can cache files so they're served directly
from the filesystem instead of through PHP. A simple library helps create URLs
and clearing the cache.

See `static/README.md` for details.
2016-06-29 12:43:57 -04:00
Steve Clay
a0e781e278 Merge pull request #505 from glensc/jshrink
add jshrink thin wrapper
2016-06-28 14:58:24 -04:00
Steve Clay
578f99d6d2 Merge branch '2.x' 2016-06-28 14:53:49 -04:00
Steve Clay
02c859b01b Merge pull request #532 from mrclay/urls_in_lines
Improve Minify_Lines algorithm and add tests
2016-06-28 14:52:14 -04:00
Steve Clay
22fb644834 Merge branch '2.x' 2016-06-27 16:13:50 -04:00
Steve Clay
31518971be Merge pull request #501 from glensc/minify-env
do not require SERVER_SOFTWARE being present in $server
2016-06-08 10:14:02 -04:00
Steve Clay
2045731d60 Improve Minify_Lines algorithm and add tests
Fixes #531
Fixes #534
2016-05-19 15:14:27 -04:00
Steve Clay
de39966e9c Makes sure all file/dir paths are normalized to prepare for comparing.
Particularly the system docRoot and currentDir values passed to the URI
rewriter and the allowDirs check.
2016-05-12 13:29:54 -04:00
Steve Clay
7aac6792bc Merge pull request #530 from mrclay/merge2x
Merge 2.x into master
2016-05-12 11:37:14 -04:00
Steve Clay
6f94b78135 Remove stuff accidentally left in merge 2016-05-12 11:14:38 -04:00
Steve Clay
865946c0ed Merge branch '2.x' into merge2x
Conflicts:
	.gitignore
	HISTORY.txt
	README.md
	config.php
	docs/CookBook.wiki.md
	docs/CustomSource.wiki.md
	lib/Minify/CSS/UriRewriter.php
	min/lib/DooDigestAuth.php
	min/lib/FirePHP.php
	min/lib/JSMinPlus.php
	min/lib/Minify/Controller/Base.php
	min/lib/Minify/Loader.php
	min/lib/Minify/Logger.php
2016-05-12 11:03:16 -04:00
Steve Clay
02e7358538 Merge pull request #528 from mrclay/version
Minify::VERSION tracks only the major version
2016-05-12 10:46:17 -04:00
Steve Clay
0db2af8741 Minify::VERSION tracks only the major version 2016-05-12 10:45:58 -04:00
Steve Clay
b7cf3808d7 Improves README and add install doc page
Shows how to install as a composer dependency
2016-04-02 11:46:29 -04:00
Elan Ruusamäe
5da955966b fix branch name. refs ce2b8d6 2016-03-13 14:56:14 +02:00
Elan Ruusamäe
ce2b8d694c composer: add 3.0 branch alias
so could use in deps mrclay/minify: ~3.0
2016-03-13 14:32:24 +02:00
Steve Clay
24b601217e Merge pull request #512 from mrclay/deps
Large restructuring
2016-03-10 18:48:49 -05:00
Steve Clay
4710509c68 Large restructuring
Moves all dependency building into App
bootstrap.php returns an App instance
The app loads config files as necessary
Moves logging to Monolog
Moves HTTP digest auth to packagist component
Rely on sys_get_temp_dir
Env hosts $_POST and allows defaults when reading
HTML helper uses the App and can handle less files
Source factory assumes strings are filenames
Fixes JsClosureCompilerTest::test6 (API now handles ES5 by default)
Exclude JsClosureCompilerTest due to API limitations
config.php can now return a Minify\Config object
Variables set in config.php are now moved to a `Minify\Config` object,
allowing better static analysis.
The `zlib.output_compression` set is moved into `Minify::serve`.
2016-03-10 18:44:23 -05:00
Elan Ruusamäe
7c95ee7540 Update Debugging.wiki.md 2016-03-08 08:50:52 +02:00
Elan Ruusamäe
c29dc274c4 Update CommonProblems.wiki.md 2016-03-08 08:49:11 +02:00
Steve Clay
86bd761a5f Merge pull request #511 from mrclay/fix_499
Minify no longer tries to minify -min.js/.min.js files
2016-03-07 15:52:21 -05:00
Steve Clay
7dbd2c87e4 Minify no longer tries to minify -min.js/.min.js files
2.2 used empty string as a magical value meaning do not minify, but
in the refactoring `Minify::combineMinify` forgot to interpret this
value that way and instead inherited the default compressor for the type.

This eliminates `""` as a magical value, but for BC rewrites it to
`Minify::nullMinifier` in the sources.

Fixes #499
2016-02-26 11:17:55 -05:00
Elan Ruusamäe
237d83d3cb add jshrink thin wrapper 2016-02-03 09:11:52 +02:00
Elan Ruusamäe
0b466c0892 fix debugdetector
current docs say append '&debug' to url, therefore 'debug' parameter has no value.
2016-01-22 09:05:49 +02:00
Elan Ruusamäe
9de4e927c7 truepath can be private 2016-01-22 09:04:14 +02:00
Elan Ruusamäe
72ae74f1c5 fixes to allowDirs processing, #496, #497, #498
Merge branch 'pr/496'
2016-01-22 08:50:54 +02:00
Elan Ruusamäe
14bde12d3b changes as per comments
https://github.com/mrclay/minify/pull/496#discussion_r50247608
2016-01-22 08:50:37 +02:00
Elan Ruusamäe
30961eb2ee Merge pull request #502 from glensc/cs
do some trivial codestyle fixes
2016-01-22 08:46:11 +02:00
Elan Ruusamäe
8fc327bec6 ignore editor backups 2016-01-22 00:57:33 +02:00
Elan Ruusamäe
42875e8388 setup phpcs cache 2016-01-22 00:56:05 +02:00
Elan Ruusamäe
b2ac4a52bb do trim whitespace in editorconfig 2016-01-22 00:41:48 +02:00
Elan Ruusamäe
e6f2df52b3 add php-cs-fixer config
config used to do previous commit (379feab)
2016-01-22 00:38:40 +02:00
Elan Ruusamäe
379feaba99 do some trivial codestyle fixes
unix newlines, trailing spaces
2016-01-22 00:38:12 +02:00
Elan Ruusamäe
90bf31f53b spelling fix 2016-01-20 18:46:41 +02:00
Elan Ruusamäe
22d34533ac do not require SERVER_SOFTWARE being present in $server 2016-01-20 16:00:39 +02:00
Elan Ruusamäe
cd49391833 cosmetic
also comment why the check may be neccessary
2016-01-20 15:41:56 +02:00
Elan Ruusamäe
c387014e27 add test for #500 2016-01-20 13:21:11 +02:00
Elan Ruusamäe
7a73d781f2 closure: mute output for f5c12ad 2015-12-16 22:50:35 +02:00
Elan Ruusamäe
f5c12ad0f4 add test for closure compiler tags validation
https://code.google.com/p/closure-compiler/issues/detail?id=513
2015-12-16 22:43:32 +02:00
Dmitry Demidovsky
01d4835d14 translate legacy setting to option for source factory (AllowDir) 2015-12-05 23:00:11 +03:00
Dmitry Demidovsky
3fcb383f49 Changed allowDirs comparsion logic #497 2015-12-05 22:58:00 +03:00
Dmitry Demidovsky
3f02443c1f Added rtrim, removed preg_match 2015-12-05 17:51:58 +03:00
Dmitry Demidovsky
da70e92cc1 normalize paths before checking allowed dirs 2015-12-04 14:02:02 +03:00
Steve Clay
6998e61654 Merge pull request #476 from mrclay/symlinks_3
Allow specifying files via $min_symlinks paths
2015-12-02 12:14:30 -05:00
Elan Ruusamäe
04d7ab0856 travis: add memcache service
http://docs.travis-ci.com/user/database-setup/#Memcached
2015-11-23 15:41:33 +02:00
Elan Ruusamäe
a89b1b9efc composer.lock is in root 2015-11-23 14:49:53 +02:00
Elan Ruusamäe
3060d2861b Merge pull request #492 from GrahamCampbell/patch-1
Delete composer.lock
2015-11-23 14:48:55 +02:00
Graham Campbell
0ab74bb892 Update .gitignore 2015-11-22 22:31:10 +00:00
Graham Campbell
1ab6bcffc1 Delete composer.lock 2015-11-22 22:30:18 +00:00
Elan Ruusamäe
07dd4f1958 cache also vendor dir 2015-11-22 22:31:10 +02:00
Elan Ruusamäe
628f9bdebb travis: use container infra
http://docs.travis-ci.com/user/migrating-from-legacy/
2015-11-22 22:27:30 +02:00
Elan Ruusamäe
ff5b1249db Update README.md
typical installation does not require dev deps
2015-11-22 22:21:36 +02:00
Elan Ruusamäe
2471fbfc6e travis: always use latest composer.json, run phpunit tests 2015-11-22 22:19:10 +02:00
Elan Ruusamäe
6f923c66ee lower phpunit version to run with php 5.3 on travis 2015-11-22 22:16:37 +02:00
Elan Ruusamäe
492222df18 Update README.md
don't need to escape camelcase in this wiki
2015-11-22 22:08:50 +02:00
Elan Ruusamäe
fe359bbf8a use block comment for better (future) git annotate 2015-11-22 22:06:33 +02:00
Elan Ruusamäe
8d1f4ad765 Merge pull request #491 from mrclay/phpunit_conversion
PHPUnit conversion
2015-11-22 22:02:01 +02:00
Elan Ruusamäe
ac1e254c48 restore *again* composer dev deps and suggests. refs aa825fc 2015-11-22 21:56:59 +02:00
Elan Ruusamäe
2819e133a5 Merge pull request #490 from glensc/nailgun
add Nailgun based ClosureCompiler
2015-11-22 21:45:45 +02:00
Steve Clay
47e2c178a3 Update composer.lock 2015-11-22 14:01:58 -05:00
Steve Clay
316a109032 Port last of min_unit_tests, remove references to it, add composer script 2015-11-22 13:57:53 -05:00
Steve Clay
9370e96fd4 port MinifyTest to phpunit, move test_environment to server-info.php 2015-11-22 13:57:52 -05:00
Steve Clay
2a24e8a31a drop test_Minify_CSS.php 2015-11-22 13:57:52 -05:00
Steve Clay
ae4b49fcee port MinifyLinesTest to phpunit 2015-11-22 13:57:52 -05:00
Steve Clay
8a35a8d9ca Add explicit dependency on PHPUnit 5 2015-11-22 13:57:52 -05:00
Elan Ruusamäe
6c95daf3cf formatting fixes 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
8816e270e6 port HTTPConditionalGetTest 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
3a9687ad4e port MinifyCSSUriRewriterTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
6e7767aa3e port MinifyHTMLTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
16b8296c34 port MinifyHTMLHelperTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
fa9f5db843 port MinifyCacheWinCacheTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
5b82a781dc port MinifyCacheAPCTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
5d46afbc72 port MinifyCacheZendPlatformTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
3cb97e2e47 port MinifyCacheMemcacheTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
f71dd4c79f move common cache test assertion to TestCase 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
554542401f port MinifyCacheFileTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
ed79a279a9 YUI/Closure: mark test skipped if .jar not found 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
e328c0b4af YUI test: ensure jar is in test dir as closure compiler test 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
79bcb7d5fe port HTTPEncoderTest to phpunit 2015-11-22 13:56:59 -05:00
Elan Ruusamäe
ec22663e3e exclude /tests from export 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
d91bcf29fb port MinifyYuiCSSTest to phpunit 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
3f0b99fb52 port MinifyCommentPreserverTest to phpunit 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
316b384b1f closure compiler: handle better when api is limited 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
badb6ce191 fix assertNotContains arguments order 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
f9f3e3ca83 hack to make TestCase class being recognized as tests class 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
31d81df2af fix duplicate class name 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
830640c0e1 closure compiler: make it work with all tests run at same time 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
47c2af74d6 update closure compiler download url 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
4f344b5d21 port MinifyClosureCompilerTest to phpunit 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
a856756705 port MinifyBuildTest to phpunit 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
68f011d73f port MinifyBuildTest to phpunit 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
4bfe976916 add common TestCase class 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
08070aecdb port JsMinTest to phpunit 2015-11-22 13:56:58 -05:00
Elan Ruusamäe
6838349e3a port Minify_JS_ClosureCompiler test to phpunit 2015-11-22 13:56:57 -05:00
Steve Clay
e2bb6b0565 Merge pull request #489 from glensc/travis
add travis config
2015-11-21 16:22:42 -05:00
Elan Ruusamäe
27b5f4b2d0 handle more ng exit codes
comments from #87:

* cbed5408dd
For some reasons Nailgun thinks that it's server
broke the connection and returns 227 instead of 0
We'll just handle this here instead of fixing
the nailgun client itself.

* 7810d4a445
It also sometimes breaks on 229 on the devbox.
To complete this whole madness and made future
'fixes' easier I added this nice little array...
2015-11-20 14:04:14 +02:00
Elan Ruusamäe
5458206700 add Nailgun based ClosureCompiler 2015-11-20 13:54:42 +02:00
Elan Ruusamäe
273a08f2ee do not ignore /test! 2015-11-20 13:52:48 +02:00
Elan Ruusamäe
29f20d95b3 add travis config 2015-11-20 11:57:30 +02:00
Elan Ruusamäe
1f9166a693 Merge pull request #487 from glensc/cc-refactor
refactor closure compiler class to allow extensibility
2015-11-20 11:54:50 +02:00
Elan Ruusamäe
35a9ab7d4a avoid unneccessary static calls 2015-11-19 17:56:05 +02:00
Elan Ruusamäe
0a74b9c4a6 refactor closure compiler class to allow extensibility 2015-11-19 17:52:11 +02:00
Elan Ruusamäe
0873db81b9 typo 2015-11-19 11:41:39 +02:00
Elan Ruusamäe
aa825fc4ed restore require-dev + suggests, lost in 588c859
suggest: a text value to suggest
require-dev: include all suggest deps so can run tests easily
2015-11-19 11:40:06 +02:00
Steve Clay
e271c370a9 Merge pull request #485 from glensc/php-5.3
be compatible with php-5.3
2015-11-17 17:15:14 -05:00
Elan Ruusamäe
8e85853af8 be compatible with php-5.3 2015-11-17 16:46:23 +02:00
Steve Clay
590cf9b57e Update Debugging.wiki.md 2015-10-02 13:40:12 -04:00
Steve Clay
d184597a2f Update UriRewriting.wiki.md 2015-10-02 11:12:02 -04:00
Steve Clay
3a3354aec6 Fix displayed URLs in builder for HTTPS 2015-09-29 16:39:25 -04:00
Steve Clay
5f2c54f636 Builder checks encoding problems with bigger response
Fixes #347
2015-09-29 16:29:21 -04:00
Steve Clay
70c0d4b3bf Builder BM can capture URLs with query strings
Fixes #373
2015-09-29 16:20:30 -04:00
Steve Clay
53a832fd90 Be clearer about missing params
Fixes #423
2015-09-29 15:59:14 -04:00
Steve Clay
5793db9098 Update CommonProblems.wiki.md 2015-09-29 15:57:40 -04:00
Steve Clay
ed06912f74 Update CommonProblems.wiki.md 2015-09-29 15:55:37 -04:00
Steve Clay
6e8a656480 Add HTML5 block level elements to Minify_HTML
Fixes #424
2015-09-29 15:45:24 -04:00
Steve Clay
674460ccd3 Forgot to update HISTORY 2015-09-29 14:41:25 -04:00
Steve Clay
b7b26e3a83 A couple improvements
Show 400 if request is missing spec (instead of redirect)
Allow config.php to change factories for Minify and MinApp objects
These should've been separate commits, :(
2015-09-29 14:35:43 -04:00
Steve Clay
1aaf8f014f Set builder default back to disabled 2015-09-29 14:31:47 -04:00
Steve Clay
12e7a8d0dd Allow sources to have null contentType if serve is given one 2015-09-29 13:18:10 -04:00
Steve Clay
a566536f80 Make Minify::combine and mix_extra stuff operational (and disabled by default) 2015-09-29 13:17:31 -04:00
Steve Clay
f769e35233 Improve source factory constructor docs 2015-09-29 11:31:57 -04:00
Steve Clay
271d8ca5bf Don't assume all allowDir paths start with "//" 2015-09-29 11:31:28 -04:00
Steve Clay
a0d99f5be5 Make sure doc root rtrimmed in Env 2015-09-29 11:29:52 -04:00
Steve Clay
ed1d2d9baf Improve docs for Files controller 2015-09-29 11:29:05 -04:00
Steve Clay
e2efb342a8 Remove JSMin+ and old, unfinished CssCompressor port
JSMin+ was a good effort but is unmaintained and has collected several
reports of impractical memory usage for a project like this.
2015-09-29 10:38:22 -04:00
Steve Clay
fede83cd48 Version constant bump, disable min_extras, docs updates 2015-09-29 10:12:47 -04:00
Steve Clay
7c607ff932 Allow specifying files via $min_symlinks paths
If you have an alias/symlink like "//~name" => "/full/path", then you can
now serve with the more logical URL http://example.com/min/?f=~name/foo.css

Fixes #137
2015-09-28 21:50:32 -04:00
Steve Clay
db8915b2db Update README.md 2015-09-28 21:21:07 -04:00
Steve Clay
6e08631c1d Merge pull request #475 from mrclay/css_docs
Migrate to CSSmin and docs overhaul
2015-09-28 21:18:15 -04:00
Steve Clay
61ba5a47a5 Huge docs overhaul for 3.x 2015-09-28 20:32:56 -04:00
Steve Clay
0bc3769802 Make CSSmin the default CSS compressor 2015-09-28 20:32:56 -04:00
Steve Clay
db1ea29133 Merge pull request #474 from mrclay/min_root
Collapse "min" into project root and get unit tests working.
2015-09-28 20:31:37 -04:00
Steve Clay
e596b35fc4 Collapse "min" into project root and get unit tests working.
Fixes #472
2015-09-28 15:39:25 -04:00
Steve Clay
2720239c8c Provide JSMin via composer 2015-09-27 19:56:33 -04:00
Steve Clay
0cb403096b Merge branch 'master' into 3.0 2015-09-27 18:05:56 -04:00
Steve Clay
de8e8be37e Merge commit '5b6b891cc379be1b9c014a5bb070e5e2e792d64c' into 3.0 2015-09-27 18:02:48 -04:00
Steve Clay
80f50b1915 Merge commit 'd233b65d3dc183933c5be8a16b5b7eaf313ef66c' into 3.0 2015-09-27 17:44:11 -04:00
Steve Clay
2e79efe47c Merge commit 'cabd595a5aebf87e5d229814564aa3b466908be1' into 3.0 2015-09-27 17:10:24 -04:00
Steve Clay
f88109bbf1 Merge commit '3c11ba8232a2155a1a29552aafc528be5fb0a662' into 3.0 2015-09-27 17:09:50 -04:00
Steve Clay
f928043059 Merge commit 'ecbb5ae3768f3cf584b234ad9682a721cc17b1db' into 3.0 2015-09-27 17:09:27 -04:00
Steve Clay
588c859a78 Merge commit 'bd9a450694cda1b54c43a1784adabe36ad522d89' into 3.0 2015-09-27 17:08:58 -04:00
Steve Clay
f941628393 Merge commit 'bceffd5afb0a578f7b4c366cd97a6ec1f3252d10' into 3.0 2015-09-27 17:01:41 -04:00
Steve Clay
6b01037509 Merge commit '153e08b5a0ad16fbeb5d7bd6494225914b1840e9' into 3.0 2015-09-27 17:00:29 -04:00
Steve Clay
a16413a6b1 Merge commit 'ce109925aa11aa31e6b196aa7b931207ff936e5b' into 3.0 2015-09-27 16:52:33 -04:00
Steve Clay
20fe2fd67c Merge commit 'a0c5ecb514a0f20aa072c11748e6d2dd99626c28' into 3.0 2015-09-27 16:50:00 -04:00
Matthias Fax
2847351c10 Add Wincache
Updated comments

Check for Wincache existence in constructor

Normalized markup

Add Wincache test

Tie Wincache test to test index
2015-09-27 16:37:46 -04:00
Steve Clay
24e7b0fbfe Updated wiki URLs 2015-09-27 16:25:33 -04:00
Elan Ruusamäe
7775469b78 Merge pull request #131 from glensc/clean-bundled-libs
Clean bundled libs
2014-12-13 15:59:02 +02:00
Elan Ruusamäe
d3006ddb63 add firephp and cssmin as reqired in composer 2014-12-01 20:41:48 +02:00
Elan Ruusamäe
76e2669180 drop bundled CSSmin, use tubalmartin/cssmin from packagist instead 2014-11-30 17:30:57 +02:00
Elan Ruusamäe
4f0741a935 drop bundled firephp, use firephp/firephp-core from packagist instead 2014-11-30 17:30:37 +02:00
Elan Ruusamäe
44683b2e63 Merge pull request #126 from glensc/autoloader
rework classloader
2014-11-17 23:23:11 +02:00
Elan Ruusamäe
0e9e1237c6 make composer dependency required
NOTE: this changes required minimum PHP version to 5.3.3
2014-10-16 00:04:51 +03:00
Elan Ruusamäe
efe603d454 add firephp to composer 2014-10-12 18:30:31 +03:00
Elan Ruusamäe
bce6facdca remove duplicate $keys. refs bceffd5 2014-10-12 17:34:00 +03:00
Elan Ruusamäe
0cbbcd0a53 get rid of $min_libPath 2014-10-12 17:21:21 +03:00
Elan Ruusamäe
4b8de3d14c use autoloader in tests 2014-10-12 17:15:49 +03:00
Elan Ruusamäe
96da497e9c rework classloader
- make it available in config
- always use classloader, no manual require
- compatible for future composer alternative
2014-10-12 16:28:41 +03:00
Steve Clay
4b495f6ae0 Merge pull request #125 from mrclay/WIP_dynamic_objects
Add dynamic objects and LESS support!
2014-10-01 16:04:29 -04:00
Steve Clay
d9bebb5566 Merge pull request #124 from glensc/WIP_lessphp
WIP lesscss integration. #112
2014-10-01 16:00:19 -04:00
Elan Ruusamäe
16069cc0c0 lesscss integration. #112 2014-10-01 16:37:27 +03:00
Steve Clay
beb750df3e Fix Files and Groups controllers, cleanup unused stuff 2014-09-23 11:47:20 -04:00
Steve Clay
9716fe802c Remove unneeded checkType in MinApp 2014-09-23 11:14:00 -04:00
Steve Clay
6d9fe1531e WIP: Huge overhaul. min app works! 2014-09-23 11:09:09 -04:00
Steve Clay
c75f97f3bc (WIP) Move to dynamic objects, adds env & source factory 2014-09-22 15:04:05 -04:00
Steve Clay
4910238a9f remove unneeded pack() 2014-09-21 13:01:09 -04:00
Steve Clay
7ff9438362 Rename cache interface in pref for namespaces 2014-09-21 13:00:03 -04:00
Elan Ruusamäe
ee47d21be2 rename Minify_Cache_Abstract class to Minify_Cache_Interface interface 2014-09-21 12:50:54 -04:00
Elan Ruusamäe
31a80b43a6 if no Cache is defined, create Minify_Cache_Null 2014-09-21 12:50:54 -04:00
Elan Ruusamäe
5193c76581 add Null cache 2014-09-21 12:50:53 -04:00
Elan Ruusamäe
bd8f3faacc add Minify::getCache() and Minify_Cache_Abstract that all cache classes must implement 2014-09-21 12:50:53 -04:00
acidvertigo
f29e1e6b06 Code clean 2014-09-21 12:50:53 -04:00
Elan Ruusamäe
54cec8b304 remove two DOS EOL 2014-09-21 12:50:53 -04:00
Matthias Fax
bb67f5762b Add Wincache
Updated comments

Check for Wincache existence in constructor

Normalized markup

Add Wincache test

Tie Wincache test to test index
2014-09-21 12:50:53 -04:00
Steve Clay
d1b299d33f Merge pull request #121 from mrclay/source_interface
Adds source interface https://github.com/mrclay/minify/issues/116
2014-09-21 10:06:24 -04:00
Steve Clay
d40d71cdff Adds source interface https://github.com/mrclay/minify/issues/116 2014-09-20 23:37:50 -04:00
Steve Clay
45cf750b3a Merge branch 'glensc-lastmodified' into 3.0 2014-09-18 18:03:47 -04:00
Steve Clay
dca6be63e3 Removes support for arbitrary objects for sources 2014-09-18 18:02:45 -04:00
Elan Ruusamäe
0624820859 tests: use Minify_Source 2014-09-18 21:52:06 +03:00
Elan Ruusamäe
6817584513 last_modified is always integer, no need to check for null 2014-09-18 21:51:39 +03:00
Elan Ruusamäe
4f5cdbb746 Convert Minify_Source::lastModified to ->getModifiedTime() #114 2014-09-18 20:28:44 +03:00
271 changed files with 9152 additions and 12878 deletions

View File

@@ -5,9 +5,6 @@ root = true
charset = utf-8
end_of_line = lf
; temporary
trim_trailing_whitespace = false
[*.php]
indent_style = space
indent_size = 4

2
.gitattributes vendored
View File

@@ -2,4 +2,4 @@
/.gitignore export-ignore
/.gitattributes export-ignore
/min_extras export-ignore
/min_unit_tests export-ignore
/tests export-ignore

54
.github/workflows/php.yml vendored Normal file
View File

@@ -0,0 +1,54 @@
name: PHP Composer
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-versions:
["8.1", "8.2", "8.3", "8.4"]
name: PHP ${{ matrix.php-versions }} Test on ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
coverage: none
tools: composer, wp-cli, phpunit-polyfills:1.0
env:
COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Validate composer.json and composer.lock
run: composer validate --strict
- name: Cache Composer packages
id: composer-cache
uses: actions/cache@v3
with:
path: vendor
key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }}
restore-keys: |
${{ runner.os }}-php-
- name: Install dependencies
run: composer install --prefer-dist --no-progress
# Add a test script to composer.json, for instance: "test": "vendor/bin/phpunit"
# Docs: https://getcomposer.org/doc/articles/scripts.md
- name: Run test suite
run: composer run-script test

20
.gitignore vendored
View File

@@ -1,8 +1,12 @@
# /
/test
/.idea/
/composer.lock
.DS_Store
/vendor
/.php_cs.cache
# ignore IDE/hidden/OS cache files
*~
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
.DS_Store
/.idea/
/composer.lock
/vendor
/.php_cs.cache
/static/[0-9]*
/tests/compiler.jar

23
.php_cs Normal file
View File

@@ -0,0 +1,23 @@
<?php
$rules = array(
'@PSR2' => true,
);
$config = PhpCsFixer\Config::create();
$finder = $config->getFinder();
$finder
->in(array('.', 'builder/', 'lib/', 'tests/', 'min_extras/', 'static/'))
->name('*.php')
->ignoreDotFiles(true)
->ignoreVCS(true);
return $config
->setUsingCache(true)
->setRiskyAllowed(true)
->setRules($rules)
->setIndent(' ')
->setLineEnding("\n");
// vim:ft=php

6
.semver Normal file
View File

@@ -0,0 +1,6 @@
---
:major: 4
:minor: 0
:patch: 1
:special: ''
:metadata: ''

323
CHANGELOG.md Normal file
View File

@@ -0,0 +1,323 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Unreleased
## [4.0.1] - 2025-02-03
- Recommend against use, [#711]
- Fix htmlspecialchars incompatibility with php 8.1, [#693]
- Support PHP 8.4, [#714]
[4.0.1]: https://github.com/mrclay/minify/compare/4.0.0...4.0.1
[#711]: https://github.com/mrclay/minify/pull/711
[#693]: https://github.com/mrclay/minify/issues/693
[#714]: https://github.com/mrclay/minify/pull/714
## [4.0.0] - 2024-01-04
- Support PHP 8.1+
- Update PHPUnit to 8.x
- Replace `leafo/scssphp` with `scssphp/scssphp`
[4.0.0]: https://github.com/mrclay/minify/compare/3.0.14...4.0.0
## [3.0.14] - 2023-05-05
- Support monolog v3, [#705]
- Allow invalidation from manual invocation, [#700]
- Add property declaration, [#699]
[3.0.14]: https://github.com/mrclay/minify/compare/3.0.13...3.0.14
[#705]: https://github.com/mrclay/minify/pull/705
[#700]: https://github.com/mrclay/minify/pull/700
[#699]: https://github.com/mrclay/minify/pull/699
## [3.0.13] - 2022-10-03
- Add `Minify_Cache_APCu` to replace `Minify_Cache_APC`, [#697]
- Require `marcusschwarz/lesserphp:^0.5.5` to fix php 7.4 compatibility, [#688]
[3.0.13]: https://github.com/mrclay/minify/compare/3.0.12...3.0.13
[#697]: https://github.com/mrclay/minify/pull/697
[#688]: https://github.com/mrclay/minify/pull/688
## [3.0.12] - 2022-05-14
- Update jquery to 1.12.4 to avoid xss attacks, [#692]
- Fix null argument to preg_split, [#696], [#695]
[3.0.12]: https://github.com/mrclay/minify/compare/3.0.11...3.0.12
[#692]: https://github.com/mrclay/minify/pull/692
[#696]: https://github.com/mrclay/minify/pull/696
[#695]: https://github.com/mrclay/minify/issues/695
## [3.0.11] - 2021-03-11
- PHP 8.0 support, [#685], [#682], [#677]
[3.0.11]: https://github.com/mrclay/minify/compare/3.0.10...3.0.11
[#685]: https://github.com/mrclay/minify/pull/685
[#682]: https://github.com/mrclay/minify/pull/682
[#677]: https://github.com/mrclay/minify/pull/677
## [3.0.10] - 2020-04-02
- Exclude SSI Comments from HTML minify, [#670], [#671]
[3.0.10]: https://github.com/mrclay/minify/compare/3.0.9...3.0.10
[#671]: https://github.com/mrclay/minify/issues/671
[#670]: https://github.com/mrclay/minify/pull/670
## [3.0.9] - 2020-03-24
- Allow `intervention/httpauth` 3.x, [#667], [#666], [#664]
[3.0.9]: https://github.com/mrclay/minify/compare/3.0.8...3.0.9
[#664]: https://github.com/mrclay/minify/issues/664
[#666]: https://github.com/mrclay/minify/pull/666
[#667]: https://github.com/mrclay/minify/pull/667
## [3.0.8] - 2020-03-19
- Removed deprecated get_magic_quotes_gpc() function that since PHP 5.4.0 returns FALSE always, and since PHP 7.4 is deprecated, [#661]
[3.0.8]: https://github.com/mrclay/minify/compare/3.0.7...3.0.8
[#661]: https://github.com/mrclay/minify/pull/661
## [3.0.7] - 2019-12-10
- Allow mrclay/props-dic ^3.0, [#658]
[3.0.7]: https://github.com/mrclay/minify/compare/3.0.6...3.0.7
[#658]: https://github.com/mrclay/minify/pull/658
## [3.0.6] - 2019-10-28
- Bugfix for option sanitizer, [#654], [#655]
[3.0.6]: https://github.com/mrclay/minify/compare/3.0.5...3.0.6
[#654]: https://github.com/mrclay/minify/issues/654
[#655]: https://github.com/mrclay/minify/pull/655
## [3.0.5] - 2019-10-01
- Fix syntax error in composer.json, [#653]
[3.0.5]: https://github.com/mrclay/minify/compare/3.0.4...3.0.5
[#653]: https://github.com/mrclay/minify/pull/653
## 3.0.4 - 2019-09-24
- Fix PHP 7.3 compatibility issues, [#648]
[3.0.4]: https://github.com/mrclay/minify/compare/3.0.3...3.0.4
[#648]: https://github.com/mrclay/minify/issues/648
## [3.0.3] - 2017-11-03
- Fix closure-compiler's error "redirection limit reached". [#618], [#619]
[3.0.3]: https://github.com/mrclay/minify/compare/3.0.2...3.0.3
[#618]: https://github.com/mrclay/minify/pull/618
[#619]: https://github.com/mrclay/minify/issues/619
## [3.0.2] - 2017-09-14
- Fixes syntax error in Groups controller, [#613]
- Better-maintained lessphp fork, [#610]
- No longer corrupts some chars in some environments, [#608]
[3.0.2]: https://github.com/mrclay/minify/compare/3.0.1...3.0.2
[#608]: https://github.com/mrclay/minify/pull/608
[#610]: https://github.com/mrclay/minify/pull/610
[#613]: https://github.com/mrclay/minify/issues/613
## [3.0.1] - 2017-06-09
- Update CSSmin to v4, [#599], [#590]
[3.0.1]: https://github.com/mrclay/minify/compare/3.0.0...3.0.1
[#590]: https://github.com/mrclay/minify/issues/590
[#599]: https://github.com/mrclay/minify/pull/599
## 3.0.0 - 2017-04-03
* Improved CSS minification via Túbal Martín's CSSMin
* Easier error identification (just see error_log)
* Adds feature to serve static files directly
* Adds config option for simply concatenating files
* Adds config option for altering creation of Minify/MinApp objects
* Missing spec no longer redirects, instead links to docs
* Installation requires use of Composer to install dependencies
* Minify::VERSION is an int that tracks the major version number
* BREAKING: The project root is now what gets deployed as `min`
* BREAKING: Removes JSMin
* BREAKING: Removes JSMin+ (unmaintained, high memory usage)
* BREAKING: Removes DooDigestAuth
* BREAKING: Removes Minify_Loader (uses Composer)
* BREAKING: Removes Minify_Logger (uses Monolog)
* BREAKING: Removes `$min_libPath` option
* BREAKING: The Minify, source, and controller components have changed APIs
## 2.3.0 - 2016-03-11
* Adds `$min_concatOnly` option to just concatenate files
* Deprecates use of Minify_Loader
* Deprecates use of Minify_Logger
* Deprecates use of JSMinPlus
* Deprecates use of FirePHP
* Deprecates use of DooDigestAuth
## 2.2.1 - 2014-10-30
* Builder styled with Bootstrap (thanks to help from acidvertigo)
* Update CSSmin to v.2.4.8
* Added WinCache
* URLs with spaces properly rewritten
## 2.2.0 - 2014-03-12
* Fix handling of RegEx in certain situations in JSMin
* Thanks to Vovan-VE for reporting this
* Update composer.json with support info
* Add ability to set ClosureCompiler URL
* Thanks Elan Ruusamäe for the pull request
* Better report of temp directory errors
* Also thanks to Elan Ruusamäe for anatoher pull request
* Updated CSSmin and added Minify_CSSmin wrapper
* Fix windows issue associated with long cache filenames
* Fix issue with web-based tool
* Fix bug in JSMin exceptions
* Fix "about:blank" bug in CSS_UriRewriter
* Cite is no longer a block element in HTML minification
* Allow for definition of custom config locations outside of the min directory
* Thanks Sam Bauers for the pull request
* Allow option for overriding the maximum byte size POST limit for ClosureCompiler and other additions
* Thanks Joscha Feth for the code
* Fixes to file-relative URL identification in UriRewriter
* Allow far-future expiration and file versioning with the "v" querystirng parameter in addition to existing method
* Lots of general code tidy ups
## 2.1.7 - 2013-07-23
* Fixes arbitrary file inclusion vulnerability on some systems
* Thanks to Matt Mecham for reporting this
## 2.1.6 - 2013-07-19
* JSMin fixes
* Prevents some Closure Compiler API failures
* Uses autoloading for all class loading
* Multiple group support in HTML Helper
* Cache adaptor for XCache
* Allow setting stack-size in YUI Compressor wrapper
* Adds jsCleanComments option to HTML minifier
* Upgrades CSSmin
* CLI script more portable
* Adds composer.json
## 2.1.5 - 2012-03-10
* Removed XSS vulnerability
* Disabled builder by default
* command line tools to minify and rewrite URIs in CSS
* upgrade (optional) JSMin+ library
* more efficient JS minification when using CC/YUIC
* Closure Compiler uses cURL when allow\_url\_fopen is off
* Missing file notices when using groups
## 2.1.4b - 2010-07-10
* Option to minify JS with Closure Compiler API w/ JSMin failover
* Cookie/bookmarklet-based debug mode. No HTML editing!
* Allows 1 file to be missing w/o complete failure
* Combine multiple groups and files in single URI
* More useful HTML helpers for writing versioned URIs
* More detailed error logging, including minifier exceptions
* Builder offers more helpful messages/PHP environment warnings
* Bypass minification based on filename pattern. e.g. foo.min.js / foo-min.css
* JSMin won't choke on common Closure compiler syntaxes (`i+ ++j`)
* Better caching in IE6
* Cache ids are influenced by group/file names
* Debug mode for Javascript doesn't break on common XPath strings (Prototype 1.6)
* Removed annoying maxFiles limit
* mbstring.func\_overload usage is safer
## 2.1.3 - 2009-06-30
* CSS fixes
* A few URI rewriting bugs fixed
* comment/whitespace removal no longer breaks some values
* IE6 [pseudo-element selector bug](http://www.crankygeek.com/ie6pebug/) no longer triggered
* HTTP fixes
* Proper Expires handling in webkit (dropped "must-revalidate", which triggered a [webkit bug](http://mrclay.org/index.php/2009/02/24/safari-4-beta-cache-controlmust-revalidate-bug/))
* ETag generation now valid ([must be unique when gzipped](https://issues.apache.org/bugzilla/show_bug.cgi?id=39727))
* Vary header always sent when Accept-Encoding is sniffed
* Dropped deflate encoding, since browser and proxy support [could be buggy](http://stackoverflow.com/questions/883841/).
* File cache now works w/o setting `$min_cachePath`
* No more 5.3 deprecation warnings: `split()` removed
* API: Can set contentType Minify\_Source objects (fixes an annoying [caveat](http://groups.google.com/group/minify/msg/8446d32ee99a4961))
* [Resolved Issue list](http://code.google.com/p/minify/issues/list?can=1&q=label%3ARelease-2.1.2%20status%3AVerified)
## 2.1.2 - 2009-03-04
* Javascript fixes
* Debug mode no longer confused by `*/*` in strings/RegExps (jQuery)
* quote characters inside RegExp literals no longer cause exception
* files ending in single-line comments no longer cause code loss
* CSS: data: URLs no longer mangled
* Optional error logging to Firefox's FirePHP extension
* Unit tests to check for common DOCUMENT\_ROOT problems
* DOCUMENT\_ROOT no longer overwritten on IIS servers
* Builder app doesn't fail on systems without gzdeflate()
* APC caching class included
## 2.1.1 - 2008-10-19
* Bug fix release
* Detection and workarounds for zlib.output\_compression and non-PHP encoding modules
* Zlib not required (mod\_rewrite, et.al., can still be used for encoding)
* HTML : More IE conditional comments preserved
* Minify\_groupUri() utility fixed
## 2.1.0 - 2008-09-18
* "min" default application for quick deployment
* Minify URI Builder app & bookmarklet for quickly creating minify URIs
* Relative URIs in CSS file are fixed automatically by default
* "debug" mode for revealing original line #s in combined files
* Better IIS support
* Improved minifier classes:
* JS: preserves IE conditional comments
* CSS: smaller output, preserves more hacks and valid CSS syntax, shorter line lengths, other bug fixes
* HTML: smaller output, shorter line lengths, other bug fixes
* Default Cache-Control: max-age of 30 minutes
* Conditional GETs supported even when max-age sent
* Experimental memcache cache class (default is files)
* Minify\_Cache\_File has flock()s (by default)
* Workaround for Windows mtime reporting bug
## 2.0.0 - 2008-05-22
* Complete code overhaul. Minify is now a PEAR-style class and toolkit for building customized minifying file servers.
* Content-Encoding: deflate/gzip/compress, based on request headers
* Expanded CSS and HTML minifiers with test cases
* Easily plug-in 3rd-party minifiers (like Packer)
* Plug-able front end controller allows changing the way files are chosen
* Compression & encoding modules lazy-loaded as needed (304 responses use minimal code)
* Separate utility classes for HTTP encoding and cache control
## 1.0.1 - 2007-05-05
* Fixed various problems resolving pathnames when hosted on an NFS mount.
* Fixed 'undefined constant' notice.
* Replaced old JSMin library with a much faster custom implementation.
## 1.0.0 - 2007-05-02
* First release.

View File

@@ -1,152 +0,0 @@
Minify Release History
Version 2.3.0
* Adds `$min_concatOnly` option to just concatenate files
* Deprecates use of Minify_Loader
* Deprecates use of Minify_Logger
* Deprecates use of JSMinPlus
* Deprecates use of FirePHP
* Deprecates use of DooDigestAuth
Version 2.2.1 (2014-10-30)
* Builder styled with Bootstrap (thanks to help from acidvertigo)
* Update CSSmin to v.2.4.8
* Added WinCache
* URLs with spaces properly rewritten
Version 2.2.0 (2014-03-12)
* Fix handling of RegEx in certain situations in JSMin
* Thanks to Vovan-VE for reporting this
* Update composer.json with support info
* Add ability to set ClosureCompiler URL
* Thanks Elan Ruusamäe for the pull request
* Better report of temp directory errors
* Also thanks to Elan Ruusamäe for anatoher pull request
* Updated CSSmin and added Minify_CSSmin wrapper
* Fix windows issue associated with long cache filenames
* Fix issue with web-based tool
* Fix bug in JSMin exceptions
* Fix "about:blank" bug in CSS_UriRewriter
* Cite is no longer a block element in HTML minification
* Allow for definition of custom config locations outside of the min directory
* Thanks Sam Bauers for the pull request
* Allow option for overriding the maximum byte size POST limit for ClosureCompiler and other additions
* Thanks Joscha Feth for the code
* Fixes to file-relative URL identification in UriRewriter
* Allow far-future expiration and file versioning with the "v" querystirng parameter in addition to existing method
* Lots of general code tidy ups
Version 2.1.7 (2013-07-23)
* Fixes arbitrary file inclusion vulnerability on some systems
* Thanks to Matt Mecham for reporting this
Version 2.1.6 (2013-07-19)
* JSMin fixes
* Prevents some Closure Compiler API failures
* Uses autoloading for all class loading
* Multiple group support in HTML Helper
* Cache adaptor for XCache
* Allow setting stack-size in YUI Compressor wrapper
* Adds jsCleanComments option to HTML minifier
* Upgrades CSSmin
* CLI script more portable
* Adds composer.json
Version 2.1.5 (2012-03-10)
* Removed XSS vulnerability
* Disabled builder bby default
* command line tools to minify and rewrite URIs in CSS
* upgrade (optional) JSMin+ library
* more efficient JS minification when using CC/YUIC
* Closure Compiler uses cURL when allow_url_fopen is off
* Missing file notices when using groups
Version 2.1.4 (2010-07-10)
* Option to minify JS with Closure Compiler API w/ JSMin failover
* Cookie/bookmarklet-based debug mode. No HTML editing!
* Allows 1 file to be missing w/o complete failure
* Combine multiple groups and files in single URI
* More useful HTML helpers for writing versioned URIs
* More detailed error logging, including minifier exceptions
* Builder offers more helpful messages/PHP environment warnings
* Bypass minification based on filename pattern. e.g. foo.min.js / foo-min.css
* JSMin won't choke on common Closure compiler syntaxes (i+ ++j)
* Better caching in IE6
* Cache ids are influenced by group/file names
* Debug mode for Javascript doesn't break on common XPath strings (Prototype 1.6)
* Removed annoying maxFiles limit
* mbstring.func_overload usage is safer
Version 2.1.3 (2009-06-30)
* HTTP fixes
* ETag generation now valid (different when gzipped)
* Vary header always sent when Accept-Encoding is sniffed
* Cache-Control no longer has "must-revalidate" due to webkit bug
See: http://mrclay.org/index.php/2009/02/24/safari-4-beta-cache-controlmust-revalidate-bug/
* Dropped deflate encoding. Browser and proxy support could be buggy.
See: http://stackoverflow.com/questions/883841/
* File cache now works w/o setting $min_cachePath
* Allow setting contentType in Minify_Source objects
* No more 5.3 deprecation warnings: split() removed
Version 2.1.2 (2009-03-04)
* Javascript fixes
* Debug mode no longer confused by "*/*" in strings/RegExps (jQuery)
* quote characters inside RegExp literals no longer cause exception
* files ending in single-line comments no longer cause code loss
* CSS: data: URLs no longer mangled
* Optional error logging to Firefox's FirePHP extension
* Unit tests to check for common DOCUMENT_ROOT problems
* DOCUMENT_ROOT no longer overwritten on IIS servers
* Builder app doesn't fail on systems without gzdeflate()
* APC caching class included
Version 2.1.1 (2008-10-19)
* Bug fix release
* Detection and workarounds for zlib.output_compression and non-PHP encoding modules
* Zlib not required (mod_rewrite, et.al., can still be used for encoding)
* HTML : More IE conditional comments preserved
* Minify_groupUri() utility fixed
Version 2.1.0 (2008-09-18)
* "min" default application for quick deployment
* Minify URI Builder app & bookmarklet for quickly creating minify URIs
* Relative URIs in CSS file are fixed automatically by default
* "debug" mode for revealing original line #s in combined files
* Better IIS support
* Improved minifier classes:
* JS: preserves IE conditional comments
* CSS: smaller output, preserves more hacks and valid CSS syntax,
shorter line lengths, other bug fixes
* HTML: smaller output, shorter line lengths, other bug fixes
* Default Cache-Control: max-age of 30 minutes
* Conditional GETs supported even when max-age sent
* Experimental memcache cache class (default is files)
* Minify_Cache_File has flock()s (by default)
* Workaround for Windows mtime reporting bug
Version 2.0.2 beta (2008-06-24)
* Fast new cache system. Cached files served almost 3x as fast.
* Dropped support of compress encoding (though HTTP_Encoder still supports it)
Version 2.0.1 (2008-05-31)
* E_STRICT compliance (Cache_Lite_File).
Version 2.0.0 (2008-05-22)
* Complete code overhaul. Minify is now a PEAR-style class and toolkit
for building customized minifying file servers.
* Content-Encoding: deflate/gzip/compress, based on request headers
* Expanded CSS and HTML minifiers with test cases
* Easily plug-in 3rd-party minifiers (like Packer)
* Plug-able front end controller allows changing the way files are chosen
* Compression & encoding modules lazy-loaded as needed (304 responses use
use minimal code)
* Separate utility classes for HTTP encoding and cache control
Version 1.0.1 (2007-05-05)
* Fixed various problems resolving pathnames when hosted on an NFS mount.
* Fixed 'undefined constant' notice.
* Replaced old JSMin library with a much faster custom implementation.
Version 1.0.0 (2007-05-02)
* First release.

186
MIN.txt
View File

@@ -1,186 +0,0 @@
The files in the /min/ directory represent the default Minify setup designed to ease
integration with your site. This app will combine and minify your Javascript or
CSS files and serve them with HTTP compression and cache headers.
RECOMMENDED
It's recommended to edit /min/config.php to set $min_cachePath to a writeable
(by PHP) directory on your system. This will improve performance.
GETTING STARTED
The quickest way to get started is to use the Minify URI Builder application
on your website: http://example.com/min/builder/
MINIFYING A SINGLE FILE
Let's say you want to serve this file:
http://example.com/wp-content/themes/default/default.css
Here's the "Minify URL" for this file:
http://example.com/min/?f=wp-content/themes/default/default.css
In other words, the "f" argument is set to the file path from root without the
initial "/". As CSS files may contain relative URIs, Minify will automatically
"fix" these by rewriting them as root relative.
COMBINING MULTIPLE FILES IN ONE DOWNLOAD
Separate the paths given to "f" with commas.
Let's say you have CSS files at these URLs:
http://example.com/scripts/jquery-1.2.6.js
http://example.com/scripts/site.js
You can combine these files through Minify by requesting this URL:
http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js
SIMPLIFYING URLS WITH A BASE PATH
If you're combining files that share the same ancestor directory, you can use
the "b" argument to set the base directory for the "f" argument. Do not include
the leading or trailing "/" characters.
E.g., the following URLs will serve the exact same content:
http://example.com/min/?f=scripts/jquery-1.2.6.js,scripts/site.js,scripts/home.js
http://example.com/min/?b=scripts&f=jquery-1.2.6.js,site.js,home.js
MINIFY URLS IN HTML
In HTML files, don't forget to replace any "&" characters with "&amp;".
SPECIFYING ALLOWED DIRECTORIES
By default, Minify will serve any *.css/*.js files within the DOCUMENT_ROOT. If
you'd prefer to limit Minify's access to certain directories, set the
$min_serveOptions['minApp']['allowDirs'] array in config.php. E.g. to limit
to the /js and /themes/default directories, use:
$min_serveOptions['minApp']['allowDirs'] = array('//js', '//themes/default');
GROUPS: NICER URLS
For nicer URLs, edit groupsConfig.php to pre-specify groups of files
to be combined under preset keys. E.g., here's an example configuration in
groupsConfig.php:
return array(
'js' => array('//js/Class.js', '//js/email.js')
);
This pre-selects the following files to be combined under the key "js":
http://example.com/js/Class.js
http://example.com/js/email.js
You can now serve these files with this simple URL:
http://example.com/min/?g=js
GROUPS: SPECIFYING FILES OUTSIDE THE DOC_ROOT
In the groupsConfig.php array, the "//" in the file paths is a shortcut for
the DOCUMENT_ROOT, but you can also specify paths from the root of the filesystem
or relative to the DOC_ROOT:
return array(
'js' => array(
'//js/file.js' // file within DOC_ROOT
,'//../file.js' // file in parent directory of DOC_ROOT
,'C:/Users/Steve/file.js' // file anywhere on filesystem
)
);
COMBINE MULTIPLE GROUPS AND FILES IN ONE URL
E.g.: http://example.com/min/?g=js&f=more/scripts.js
Separate group keys with commas:
http://example.com/min/?g=baseCss,css1&f=moreStyles.css
FAR-FUTURE EXPIRES HEADERS
Minify can send far-future (one year) Expires headers. To enable this you must
add a number or the parameter "v" to the querystring (e.g. /min/?g=js&1234 or
/min/?g=js&v=1234) and alter it whenever a source file is changed. If you have a
build process you can use a build/source control revision number.
You can alternately use the utility function Minify_getUri() to get a "versioned"
Minify URI for use in your HTML. E.g.:
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php';
$jsUri = Minify_getUri('js'); // a key in groupsConfig.php
echo "<script src='{$jsUri}'></script>";
$cssUri = Minify_getUri(array(
'//css/styles1.css'
,'//css/styles2.css'
)); // a list of files
echo "<link rel=stylesheet href='{$cssUri}'>";
STORING CONFIG FILES OUTSIDE THE MINIFY DIRECTORY
It is possible to store config files (min/config.php, min/config-test.php,
min/groupsConfig.php) in a custom directory outside the Minify directory. This is
useful if you wish to include Minify as an external dependency inside another
project via SVN external or Git submodule inclusion.
For example, let's assume you have a Minify directory "min" in your site root. Then
you could create a new directory called "min-configs" in the site root. Copy any
config files you wish to modify to "min-configs", and modify as desired.
Then create a new file, for example "min.php" in your site root. The contents of
this file could look like this:
<?php
$customConfigDirectory = dirname(__FILE__) . '/min-configs';
$min_customConfigPaths = array(
'base' => $customConfigDirectory . '/config.php',
'test' => $customConfigDirectory . '/config-test.php',
'groups' => $customConfigDirectory . '/groupsConfig.php'
);
include_once 'min/index.php';
You would then reference min.php in your JS and CSS links instead of min/index.php.
This method will affect those using the Minify_getUri() function. You will need
to add options to calls to that function, e.g.:
<?php
require $_SERVER['DOCUMENT_ROOT'] . '/min/utils.php';
$jsUri = Minify_getUri('//js/file.js', array('minAppUri' => '/min.php'));
echo "<script src='{$jsUri}'></script>";
DEBUG MODE
In debug mode, instead of compressing files, Minify sends combined files with
comments prepended to each line to show the line number in the original source
file. To enable this, set $min_allowDebugFlag to true in config.php and append
"&debug=1" to your URIs. E.g. /min/?f=script1.js,script2.js&debug=1
Known issue: files with comment-like strings/regexps can cause problems in this mode.
BYPASSING MINIFICATION
See the $min_concatOnly option in config.php.
QUESTIONS?
http://groups.google.com/group/minify

View File

@@ -1,5 +1,10 @@
Welcome to Minify!
==================
## Minify is no longer regularly maintained
The original authors no longer recommend using Minify, especially previous versions, which were not designed to handle modern JS and CSS syntax. Instead, use an up-to-date performance measurement tool like [Lighthouse](https://developer.chrome.com/docs/lighthouse/overview) and follow its recommendations.
In 2010, Minify offered a fine improvement for some websites, but browsers and HTTP servers are now much better, and Minify may offer only a marginal performance benefit in narrow cases. Also both JS and CSS now change rapidly, and new syntaxes are likely to lead to broken code being served through Minify.
## About
Minify is an HTTP server for JS and CSS assets. It compresses and combines files
and serves it with appropriate headers, allowing conditional GET or long-Expires.
@@ -12,63 +17,43 @@ The stats above are from a [brief walkthrough](http://mrclay.org/index.php/2008/
Relative URLs in CSS files are rewritten to compensate for being served from a different directory.
News
----
## Static file serving
Version [2.3.0](https://github.com/mrclay/minify/releases/tag/2.3.0) was released, mainly to deprecate some classes that will be removed in 3.0.
Version 3 allows [serving files directly from the filesystem](static/README.md) for much better performance. We encourage you to try this feature.
Installation
------------
## Support
Place the `/min/` directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: `/home/example/www/min`
Post to the [Google Group](http://groups.google.com/group/minify).
You can see verify that it is working by visiting these two URLs:
- http://example.org/min/?f=min/quick-test.js
- http://example.org/min/?f=min/quick-test.css
## Installation
If your server supports mod_rewrite, this URL should also work:
- http://example.org/min/f=min/quick-test.js
See the [install guide](docs/Install.wiki.md).
Configuration & Usage
---------------------
## Configuration & Usage
See the MIN.txt file and the [user guide](https://github.com/mrclay/minify/blob/master/docs/UserGuide.wiki.md)
(Using 2.x? [Here are the 2.x docs](https://github.com/mrclay/minify/tree/2.x/docs).)
Minify also comes with a [URI Builder application](https://github.com/mrclay/minify/blob/master/docs/BuilderApp.wiki.md) that can help you write URLs
See the [user guide](docs/UserGuide.wiki.md).
Minify also comes with a [URI Builder application](docs/BuilderApp.wiki.md) that can help you write URLs
for use with Minify or configure groups of files.
See the [cookbook](https://github.com/mrclay/minify/blob/master/docs/CookBook.wiki.md) for more advanced options for minification.
See the [cookbook](docs/CookBook.wiki.md) for more advanced options for minification.
More [docs are available](https://github.com/mrclay/minify/tree/master/docs).
More [docs are available](docs).
Support
-------
## Unit Testing
[Google Group](http://groups.google.com/group/minify)
1. Install dev deps via Composer: `composer install`
1. `composer test` or `phpunit`
Unit Testing
------------
## Warnings
1. Place the /min_unit_tests/ directory as a child of your DOCUMENT_ROOT
directory: i.e. you will have: /home/example/www/min_unit_tests
* Minify is designed for efficiency, but, for very high traffic sites, it will probably serve files slower than your HTTPd due to the CGI overhead of PHP. See the [FAQ](docs/FAQ.wiki.md#how-fast-is-it) and [CookBook](docs/CookBook.wiki.md) for more info.
* If you combine a lot of CSS, watch out for [IE's 4096 selectors-per-file limit](http://stackoverflow.com/a/9906889/3779), affects IE 6 through 9.
* Minify *should* work fine with files encoded in UTF-8 or other 8-bit encodings like ISO 8859/Windows-1252. By default Minify appends ";charset=utf-8" to the Content-Type headers it sends.
2. To run unit tests, access: http://example.org/min_unit_tests/test_all.php
(If you wish, the other test_*.php files can be run to test individual
components with more verbose output.)
3. Remove /min_unit_tests/ from your DOCUMENT_ROOT when you are done.
Warnings
--------
* Minify is designed for efficiency, but, for very high traffic sites, it will probably serve files slower than your HTTPd due to the CGI overhead of PHP. See the [FAQ](https://github.com/mrclay/minify/blob/master/docs/FAQ.wiki.md#how-fast-is-it) and [CookBook](https://github.com/mrclay/minify/blob/master/docs/CookBook.wiki.md) for more info.
* If you combine a lot of CSS, watch out for [IE's 4096 selectors-per-file limit](http://stackoverflow.com/a/9906889/3779), affects IE 6 through 9.
* Minify *should* work fine with files encoded in UTF-8 or other 8-bit encodings like ISO 8859/Windows-1252. By default Minify appends ";charset=utf-8" to the Content-Type headers it sends.
Acknowledgments
---------------
## Acknowledgments
Minify was inspired by [jscsscomp](http://code.google.com/p/jscsscomp/) by Maxim Martynyuk and by the article [Supercharged JavaScript](http://www.hunlock.com/blogs/Supercharged_Javascript) by Patrick Hunlock.

View File

@@ -7,7 +7,7 @@ UPGRADING FROM 2.1.*
/min/config.php --> /min/old_config.php
/min/groupsConfig.php --> /min/old_groupsConfig.php
2. Overwrite all files in /min (and /min_unit_tests) with those from this zip.
2. Overwrite all files in /min with those from this zip.
3. Delete /min/groupsConfig.php

25
bootstrap.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
/**
* Sets up autoloading and returns the Minify\App
*/
call_user_func(function () {
if (is_dir(__DIR__ . '/../../../vendor')) {
// Used as a composer library
$vendorDir = __DIR__ . '/../../../vendor';
} else {
$vendorDir = __DIR__ . '/vendor';
}
$file = $vendorDir . '/autoload.php';
if (!is_file($file)) {
echo 'You must set up the project dependencies, run the following commands:'.PHP_EOL.
'curl -sS https://getcomposer.org/installer | php'.PHP_EOL.
'php composer.phar install'.PHP_EOL;
exit(1);
}
require $file;
});
return new \Minify\App(__DIR__);

View File

@@ -25,7 +25,7 @@ var MUB = {
* Get markup for new source LI element
*/
newLi : function () {
return '<li id="li' + MUB._uid + '">http://' + location.host + '/<input type=text size=20>' +
return '<li id="li' + MUB._uid + '">' + location.protocol + '//' + location.host + '/<input type=text size=20>' +
' <button class="btn btn-danger btn-sm" title="Remove">x</button> <button class="btn btn-default btn-sm" title="Include Earlier">&uarr;</button>' +
' <button class="btn btn-default btn-sm" title="Include Later">&darr;</button> <span></span></li>';
},

View File

@@ -5,9 +5,11 @@ javascript:(function() {
,o
,home = (location + '').split('/').splice(0, 3).join('/') + '/';
function add(uri) {
return (0 === uri.indexOf(home))
&& (!/[\?&]/.test(uri))
&& uris.push(escape(uri.substr(home.length)));
if (0 !== uri.indexOf(home)) {
return;
}
uri = uri.replace(/\?.*/, '');
uris.push(escape(uri.substr(home.length)));
};
function sheet(ss) {
// we must check the domain with add() before accessing ss.cssRules

View File

@@ -1,47 +1,37 @@
<?php
<?php
if (phpversion() < 5) {
exit('Minify requires PHP5 or greater.');
}
$app = (require __DIR__ . '/../bootstrap.php');
/* @var \Minify\App $app */
// check for auto-encoding
$encodeOutput = (function_exists('gzdeflate')
&& !ini_get('zlib.output_compression'));
$config = $app->config;
// recommend $min_symlinks setting for Apache UserDir
$symlinkOption = '';
if (0 === strpos($_SERVER["SERVER_SOFTWARE"], 'Apache/')
&& preg_match('@^/\\~(\\w+)/@', $_SERVER['REQUEST_URI'], $m)
if (0 === strpos($app->env->server("SERVER_SOFTWARE"), 'Apache/')
&& preg_match('@^/\\~(\\w+)/@', $app->env->server('REQUEST_URI'), $m)
) {
$userDir = DIRECTORY_SEPARATOR . $m[1] . DIRECTORY_SEPARATOR;
if (false !== strpos(__FILE__, $userDir)) {
$sm = array();
$sm["//~{$m[1]}"] = dirname(dirname(__FILE__));
$sm["//~{$m[1]}"] = dirname(__DIR__);
$array = str_replace('array (', 'array(', var_export($sm, 1));
$symlinkOption = "\$min_symlinks = $array;";
}
}
require dirname(__FILE__) . '/../config.php';
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
if (! $min_enableBuilder) {
if (!$config->enableBuilder) {
header('Content-Type: text/plain');
die('This application is not enabled. See http://code.google.com/p/minify/wiki/BuilderApp');
die('This application is not enabled. See https://github.com/mrclay/minify/blob/master/docs/BuilderApp.wiki.md');
}
if (isset($min_builderPassword)
&& is_string($min_builderPassword)
&& $min_builderPassword !== '') {
DooDigestAuth::http_auth('Minify Builder', array('admin' => $min_builderPassword));
}
$cachePathCode = '';
if (! isset($min_cachePath) && ! function_exists('sys_get_temp_dir')) {
$detectedTmp = Minify_Cache_File::tmp();
$cachePathCode = "\$min_cachePath = " . var_export($detectedTmp, 1) . ';';
if ($config->builderPassword && $config->builderPassword !== '') {
$auth = new Intervention\Httpauth\Httpauth(array(
'username' => 'admin',
'password' => $config->builderPassword,
'type' => 'digest',
'realm' => 'Minify Builder',
));
$auth->secure();
}
ob_start();
@@ -80,18 +70,11 @@ b {color:#c00}
<p class=topWarning id=jsDidntLoad><strong>Uh Oh.</strong> Minify was unable to
serve Javascript for this app. To troubleshoot this,
<a href="http://code.google.com/p/minify/wiki/Debugging">enable FirePHP debugging</a>
<a href="https://github.com/mrclay/minify/blob/master/docs/Debugging.wiki.md">enable FirePHP debugging</a>
and request the <a id=builderScriptSrc href=#>Minify URL</a> directly. Hopefully the
FirePHP console will report the cause of the error.
</p>
<?php if ($cachePathCode): ?>
<p class=topNote><strong>Note:</strong> <code><?php echo
htmlspecialchars($detectedTmp); ?></code> was discovered as a usable temp directory.<br>To
slightly improve performance you can hardcode this in /min/config.php:
<code><?php echo htmlspecialchars($cachePathCode); ?></code></p>
<?php endIf; ?>
<p id=minRewriteFailed class="hide"><strong>Note:</strong> Your webserver does not seem to
support mod_rewrite (used in /min/.htaccess). Your Minify URIs will contain "?", which
<a href="http://www.stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring/"
@@ -112,7 +95,7 @@ and click [Update].</p>
<div id=bmUris></div>
<p><button class="btn btn-primary" id=update class=hide>Update</button></p>
<p><button class="btn btn-primary hide" id=update>Update</button></p>
<div id=results class=hide>
@@ -165,13 +148,13 @@ by Minify. E.g. <code>@import "<span class=minRoot>/min/?</span>g=css2";</code><
</div><!-- #app -->
<hr>
<p>Need help? Check the <a href="http://code.google.com/p/minify/w/list?can=3">wiki</a>,
<p>Need help? Check the <a href="https://github.com/mrclay/minify/tree/master/docs">wiki</a>,
or post to the <a class=ext href="http://groups.google.com/group/minify">discussion
list</a>.</p>
<p><small>Powered by Minify <?php echo Minify::VERSION; ?></small></p>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="jquery-1.6.3.min.js"><\/script>')</script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="jquery-1.12.4.min.js"><\/script>')</script>
<script>
(function () {
// workaround required to test when /min isn't child of web root
@@ -193,7 +176,11 @@ by Minify. E.g. <code>@import "<span class=minRoot>/min/?</span>g=css2";</code><
var url = 'ocCheck.php?' + (new Date()).getTime();
$.get(url, function (ocStatus) {
$.get(url + '&hello=1', function (ocHello) {
if (ocHello != 'World!') {
var expected = [];
for (var i = 0; i < 500; i++) {
expected.push('0123456789');
}
if (ocHello != expected.join('')) {
msg += 'It appears output is being automatically compressed, interfering '
+ ' with Minify\'s own compression. ';
if (ocStatus == '1')
@@ -220,22 +207,15 @@ by Minify. E.g. <code>@import "<span class=minRoot>/min/?</span>g=css2";</code><
<?php
$content = ob_get_clean();
// setup Minify
Minify::setCache(
isset($min_cachePath) ? $min_cachePath : ''
,$min_cacheFileLocking
);
Minify::$uploaderHoursBehind = $min_uploaderHoursBehind;
Minify::serve('Page', array(
'content' => $content
,'id' => __FILE__
,'lastModifiedTime' => max(
$controller = new Minify_Controller_Page($app->env, $app->sourceFactory);
$minify = $app->minify->serve($controller, array(
'content' => $content,
'id' => __FILE__,
'lastModifiedTime' => max(
// regenerate cache if any of these change
filemtime(__FILE__)
,filemtime(dirname(__FILE__) . '/../config.php')
,filemtime(dirname(__FILE__) . '/../lib/Minify.php')
)
,'minifyAll' => true
,'encodeOutput' => $encodeOutput
filemtime(__FILE__),
filemtime(__DIR__ . '/../config.php'),
filemtime(__DIR__ . '/../lib/Minify.php')
),
'minifyAll' => true,
));

5
builder/jquery-1.12.4.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,34 +1,34 @@
<?php
/**
* AJAX checks for zlib.output_compression
*
*
* @package Minify
*/
$app = (require __DIR__ . '/../bootstrap.php');
/* @var \Minify\App $app */
$_oc = ini_get('zlib.output_compression');
// allow access only if builder is enabled
require dirname(__FILE__) . '/../config.php';
if (! $min_enableBuilder) {
if (!$app->config->enableBuilder) {
header('Location: /');
exit;
}
if (isset($_GET['hello'])) {
if ($app->env->get('hello')) {
// echo 'World!'
// try to prevent double encoding (may not have an effect)
ini_set('zlib.output_compression', '0');
require $min_libPath . '/HTTP/Encoder.php';
HTTP_Encoder::$encodeToIe6 = true; // just in case
$he = new HTTP_Encoder(array(
'content' => 'World!'
,'method' => 'deflate'
'content' => str_repeat('0123456789', 500),
'method' => 'deflate',
));
$he->encode();
$he->sendAll();
} else {
// echo status "0" or "1"
header('Content-Type: text/plain');

View File

@@ -6,7 +6,7 @@ exit;
$_oc = ini_get('zlib.output_compression');
// allow access only if builder is enabled
require dirname(__FILE__) . '/../config.php';
require __DIR__ . '/../config.php';
if (! $min_enableBuilder) {
exit;
}
@@ -37,7 +37,7 @@ if (isset($_GET['oc'])) {
echo "<p class=topNote><strong>\$_SERVER['SUBDOMAIN_DOCUMENT_ROOT'] is set.</strong> "
. "You may need to set \$min_documentRoot to this in config.php</p>";
}
}
//*/
//*/

View File

@@ -1,8 +1,8 @@
{
"name": "mrclay/minify",
"type": "library",
"description": "Minify is a PHP5 app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers",
"homepage": "http://code.google.com/p/minify/",
"description": "Minify is a PHP app that helps you follow several rules for client-side performance. It combines multiple CSS or Javascript files, removes unnecessary whitespace and comments, and serves them with gzip encoding and optimal client-side cache headers",
"homepage": "https://github.com/mrclay/minify",
"license": "BSD-3-Clause",
"authors": [
{
@@ -13,20 +13,52 @@
],
"support": {
"email": "minify@googlegroups.com",
"issues": "http://code.google.com/p/minify/issues/list",
"wiki": "http://code.google.com/p/minify/w/list"
"issues": "https://github.com/mrclay/minify/issues",
"wiki": "https://github.com/mrclay/minify/blob/master/docs"
},
"autoload": {
"classmap": ["min/lib/"]
"classmap": [
"lib/"
]
},
"autoload-dev": {
"psr-4": {
"Minify\\Test\\": "tests/"
}
},
"config": {
"platform": {
"php": "8.1.0"
},
"sort-packages": true
},
"require": {
"php": ">=5.2.1",
"ext-pcre": "*"
"php": "^8.1",
"ext-pcre": "*",
"intervention/httpauth": "^2.0|^3.0",
"marcusschwarz/lesserphp": "^0.5.5",
"monolog/monolog": "~1.1|~2.0|~3.0",
"mrclay/jsmin-php": "~2",
"mrclay/props-dic": "^4",
"tubalmartin/cssmin": "~4"
},
"require-dev": {
"tubalmartin/cssmin": "~2.4.8"
"firephp/firephp-core": "~0.4.0",
"meenie/javascript-packer": "~1.1",
"phpunit/phpunit": "^8",
"scssphp/scssphp": "^1.12",
"tedivm/jshrink": "~1.1.0"
},
"suggest": {
"tubalmartin/cssmin": "Support minify with CSSMin (YUI PHP port)"
"firephp/firephp-core": "Use FirePHP for Log messages",
"meenie/javascript-packer": "Keep track of the Packer PHP port using Composer"
},
"scripts": {
"test": "phpunit"
},
"extra": {
"branch-alias": {
"dev-master": "3.0.x-dev"
}
}
}

View File

@@ -4,7 +4,6 @@
*
* To test config options, place them in this file and add "&test" to your Minify URL.
* Note that if this is on a public server, anyone can execute your test.
*
*
* @package Minify
*/

View File

@@ -2,11 +2,17 @@
/**
* Configuration for "min", the default application built with the Minify
* library
*
*
* @package Minify
*/
/**
* Enable the static serving feature
*/
$min_enableStatic = false;
/**
* Allow use of the Minify URI Builder app. Only set this to true while you need it.
*/
@@ -27,12 +33,8 @@ $min_builderPassword = 'admin';
/**
* Set to true to log messages to FirePHP (Firefox Firebug addon).
* Set to true to log messages to FirePHP (Firefox Firebug addon) and PHP's error_log
* Set to false for no error logging (Minify may be slightly faster).
* @link http://www.firephp.org/
*
* If you want to use a custom error logger, set this to your logger
* instance. Your object should have a method log(string $message).
*/
$min_errorLogger = false;
@@ -45,7 +47,7 @@ $min_errorLogger = false;
* /min/f=file1.js send the cookie minDebug=file1.js
* You can manually enable debugging by appending "&debug" to a URI.
* E.g. /min/?f=script1.js,script2.js&debug
*
*
* In 'debug' mode, Minify combines files with no minification and adds comments
* to indicate line #s of the original files.
*/
@@ -61,38 +63,31 @@ $min_allowDebugFlag = false;
//$min_cachePath = preg_replace('/^\\d+;/', '', session_save_path());
/**
* Path to Minify's lib folder. If you happen to move it, change
* this accordingly.
*/
$min_libPath = dirname(__FILE__) . '/lib';
/**
* To use APC/Memcache/ZendPlatform for cache storage, require the class and
* set $min_cachePath to an instance. Example below:
*/
//require "$min_libPath/Minify/Cache/APC.php";
//$min_cachePath = new Minify_Cache_APC();
/**
* Leave an empty string to use PHP's $_SERVER['DOCUMENT_ROOT'].
*
* On some servers, this value may be misconfigured or missing. If so, set this
* On some servers, this value may be misconfigured or missing. If so, set this
* to your full document root path with no trailing slash.
* E.g. '/home/accountname/public_html' or 'c:\\xampp\\htdocs'
*
* If /min/ is directly inside your document root, just uncomment the
* If /min/ is directly inside your document root, just uncomment the
* second line. The third line might work on some Apache servers.
*/
$min_documentRoot = '';
//$min_documentRoot = dirname(dirname(__DIR__));
//$min_documentRoot = substr(__FILE__, 0, -15);
//$min_documentRoot = $_SERVER['SUBDOMAIN_DOCUMENT_ROOT'];
/**
* Cache file locking. Set to false if filesystem is NFS. On at least one
* Cache file locking. Set to false if filesystem is NFS. On at least one
* NFS system flock-ing attempts stalled PHP for 30 seconds!
*/
$min_cacheFileLocking = true;
@@ -101,9 +96,9 @@ $min_cacheFileLocking = true;
/**
* Combining multiple CSS files can place @import declarations after rules, which
* is invalid. Minify will attempt to detect when this happens and place a
* warning comment at the top of the CSS output. To resolve this you can either
* move the @imports within your CSS files, or enable this option, which will
* move all @imports to the top of the output. Note that moving @imports could
* warning comment at the top of the CSS output. To resolve this you can either
* move the @imports within your CSS files, or enable this option, which will
* move all @imports to the top of the output. Note that moving @imports could
* affect CSS values (which is why this option is disabled by default).
*/
$min_serveOptions['bubbleCssImports'] = false;
@@ -122,9 +117,9 @@ $min_serveOptions['maxAge'] = 1800;
/**
* To use CSSmin (Túbal Martín's port of the YUI CSS compressor), uncomment the following line:
* To use the CSS compressor that shipped with 2.x, uncomment the following line:
*/
//$min_serveOptions['minifiers']['text/css'] = array('Minify_CSSmin', 'minify');
//$min_serveOptions['minifiers'][Minify::TYPE_CSS] = array('Minify_CSS', 'minify');
/**
@@ -139,8 +134,8 @@ $min_serveOptions['maxAge'] = 1800;
* particular directories below DOCUMENT_ROOT, set this here.
* You will still need to include the directory in the
* f or b GET parameters.
*
* // = shortcut for DOCUMENT_ROOT
*
* // = shortcut for DOCUMENT_ROOT
*/
//$min_serveOptions['minApp']['allowDirs'] = array('//js', '//css');
@@ -154,7 +149,7 @@ $min_serveOptions['minApp']['groupsOnly'] = false;
/**
* By default, Minify will not minify files with names containing .min or -min
* before the extension. E.g. myFile.min.js will not be processed by JSMin
*
*
* To minify all files, set this option to null. You could also specify your
* own pattern that is matched against the filename.
*/
@@ -165,8 +160,8 @@ $min_serveOptions['minApp']['groupsOnly'] = false;
* If you minify CSS files stored in symlink-ed directories, the URI rewriting
* algorithm can fail. To prevent this, provide an array of link paths to
* target paths, where the link paths are within the document root.
*
* Because paths need to be normalized for this to work, use "//" to substitute
*
* Because paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
@@ -178,22 +173,28 @@ $min_symlinks = array();
/**
* If you upload files from Windows to a non-Windows server, Windows may report
* incorrect mtimes for the files. This may cause Minify to keep serving stale
* incorrect mtimes for the files. This may cause Minify to keep serving stale
* cache files when source file changes are made too frequently (e.g. more than
* once an hour).
*
* Immediately after modifying and uploading a file, use the touch command to
*
* Immediately after modifying and uploading a file, use the touch command to
* update the mtime on the server. If the mtime jumps ahead by a number of hours,
* set this variable to that number. If the mtime moves back, this should not be
* set this variable to that number. If the mtime moves back, this should not be
* needed.
*
* In the Windows SFTP client WinSCP, there's an option that may fix this
* issue without changing the variable below. Under login > environment,
* In the Windows SFTP client WinSCP, there's an option that may fix this
* issue without changing the variable below. Under login > environment,
* select the option "Adjust remote timestamp with DST".
* @link http://winscp.net/eng/docs/ui_login_environment#daylight_saving_time
*/
$min_uploaderHoursBehind = 0;
// try to disable output_compression (may not have an effect)
ini_set('zlib.output_compression', '0');
/**
* Advanced: you can replace some of the PHP classes Minify uses to serve requests.
* To do this, assign a callable to one of the elements of the $min_factories array.
*
* You can see the default implementations (and what gets passed in) in index.php.
*/
//$min_factories['minify'] = ... a callable accepting a Minify\App object
//$min_factories['controller'] = ... a callable accepting a Minify\App object

8
docker-compose.yml Normal file
View File

@@ -0,0 +1,8 @@
version: "3.7"
services:
apache:
image: php:8.3-apache
ports:
- 8080:80
volumes:
- .:/var/www/html/min

View File

@@ -1,12 +1,13 @@
If you test sites in a subdirectory (e.g. `http://localhost/testSite/`) rather than a virtualhost, then you'll need to adjust the way you use Minify to rewrite CSS correctly.
1. Place the following in `min/config.php`:
```
1. Place the following in `config.php`:
```php
// Set the document root to be the path of the "site root"
$min_documentRoot = substr(__FILE__, 0, -15);
$min_documentRoot = substr(__FILE__, 0, -11);
// Set $sitePrefix to the path of the site from the webserver's real docroot
list($sitePrefix) = explode('/min/index.php', $_SERVER['SCRIPT_NAME'], 2);
list($sitePrefix) = explode('/index.php', $_SERVER['SCRIPT_NAME'], 2);
// Prepend $sitePrefix to the rewritten URIs in CSS files
$min_symlinks['//' . ltrim($sitePrefix, '/')] = $min_documentRoot;
@@ -25,5 +26,5 @@ Now the `min` application should operate correctly from a subdirectory and will
| **file served** | `/home/mysite_com/www/js/file1.js` | `/var/www/testSite/js/file1.js` |
Caveats:
* This configuration may break the Builder application (located at `/min/builder/`) used to create Minify URIs, but you can still [create them by hand](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/README.txt#18).
* This configuration may break the Builder application (located at `/min/builder/`) used to create Minify URIs, but you can still create them by hand.
* Make sure you don't reset `$min_symlinks` to a different value lower in your config file.

View File

@@ -1,9 +1,9 @@
Minify ships with "Builder", a simple Javascript app for constructing URIs to use with Minify. ([screenshots of the 2.1.0 version](http://www.mrclay.org/index.php/2008/09/19/minify-21-on-mrclayorg/))
It also does some run-time checks of your PHP and Minify configuration to look for problematic settings like [auto-encoding](http://code.google.com/p/minify/wiki/CommonProblems#Output_is_distorted/random_chars).
It also does some run-time checks of your PHP and Minify configuration to look for problematic settings like [auto-encoding](CommonProblems.wiki.md#output-is-distortedrandom-chars).
After installation, this is found at **`http://example.com/min/builder/`**
You must enable it by editing `min/config.php` and setting `$min_enableBuilder = true;`
You must enable it by editing `config.php` and setting `$min_enableBuilder = true;`
After use, you should disable it by resetting `$min_enableBuilder = false;`

View File

@@ -2,20 +2,15 @@ If this page doesn't help, please post a question on our [Google group](http://g
## URIs are re-written incorrectly in CSS output
See UriRewriting.
## Files aren't cached in IE6
**Use Minify 2.1.4+**.
For Minify 2.1.3 and below:
1. Open `/min/lib/HTTP/Encoder.php`
1. On line [62](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/HTTP/Encoder.php#62), change `false` to `true`.
See [UriRewriting](UriRewriting.wiki.md).
## Builder Fails / 400 Errors
**Use Minify 2.1.4+**, and you can see the cause of 400 responses using FirePHP (See [Debugging](Debugging.md)).
This is usually due to an unusual server setup. You can see the cause of 400 responses using FirePHP (See [Debugging](Debugging.wiki.md)).
## Long URL parameters are ignored
Some server setups will refuse to populate very long `$_GET` params. Use [groups](UserGuide.wiki.md#using-groups-for-nicer-urls) to shorten the URLs.
## PHP/Apache crashes
@@ -23,15 +18,15 @@ For Minify 2.1.3 and below:
* Raise Apache's [ThreadStackSize](http://stackoverflow.com/a/7597506/3779)
* In [php.ini](http://php.net/manual/en/pcre.configuration.php) raise `pcre.backtrack_limit` and `pcre.recursion_limit` to 1000000. These will allow processing longer strings, but also require a larger stack size.
* Try [this CSSmin configuration](http://code.google.com/p/minify/wiki/CookBook#CSSmin_PHP_port)
* Use YUICompressor instead of PHP-based CSS compressors
## Dealing with Javascript errors
Short answer: **use Minify 2.1.4+, use a pre-compressed version of your file, and rename it `*.min.js` or `*-min.js`**. By default Minify won't try to minify these files (but will still gzip them). The [Compressor Rater](http://compressorrater.thruhere.net/) is handy for compressing files individually.
If the error is in your code, enable [debug mode](Debugging.md) while debugging your code in Firebug or your favorite browser's Javascript debugger. This will insert comments to allow you to keep track of the individual source locations in the combined file.
If the error is in your code, enable [debug mode](Debugging.wiki.md) while debugging your code in Firebug or your favorite browser's Javascript debugger. This will insert comments to allow you to keep track of the individual source locations in the combined file.
If you have Java on your web host, you can use the [wrapper for YUI Compressor](http://code.google.com/p/minify/source/browse/min/lib/Minify/YUICompressor.php) instead of JSMin. [This thread](http://groups.google.com/group/minify/browse_thread/thread/f12f25f27e1256fe) shows how a user has done this.
If you have Java on your web host, you can use the [wrapper for YUI Compressor](../lib/Minify/YUICompressor.php) instead of JSMin. [This thread](http://groups.google.com/group/minify/browse_thread/thread/f12f25f27e1256fe) shows how a user has done this.
## Javascript isn't being minified
@@ -53,7 +48,7 @@ Scriptaculous 1.8.2 (and probably all 1.x) has an [autoloader script](http://git
If you upload files using [Coda or Transmit](http://groups.google.com/group/coda-users/browse_thread/thread/572d2dc315ec02e7/) or from a Windows PC to a non-Windows server, your new files may end up with the wrong `mtime` (timestamp) on the server, confusing the cache system.
Setting the [$min\_uploaderHoursBehind option](https://github.com/mrclay/minify/blob/master/min/config.php#L171) in `config.php` can compensate for this.
Setting the [$min\_uploaderHoursBehind option](../config.php#L171) in `config.php` can compensate for this.
WinSCP has a [Daylight Saving Time option](http://winscp.net/eng/docs/ui_login_environment#daylight_saving_time) that can prevent this issue.
@@ -69,8 +64,8 @@ If a change is not seen, verify that the server cache file is being updated.
## Disable Caching
If you'd like to temporarily disable the cache without using [debug mode](Debugging.md), place these settings at the end of `config.php`:
```
If you'd like to temporarily disable the cache without using [debug mode](Debugging.wiki.md), place these settings at the end of `config.php`:
```php
// disable server caching
$min_cachePath = null;
// prevent client caching
@@ -103,8 +98,8 @@ Debug mode adds line numbers in comments. Unfortunately, in versions <= 2.1.1, i
This issue was resolved in version 2.1.2.
In rare instances the [JSMin](http://code.google.com/p/minify/source/browse/tags/release_2.1.1/min/lib/JSMin.php#14) algorithm in versions <= 2.1.1 could be confused by regexes in certain contexts and throw an exception. The workaround was to simply wrap the expression in parenthesis. E.g.
```
In rare instances the [JSMin](https://github.com/mrclay/jsmin-php/blob/master/src/JSMin/JSMin.php) algorithm in versions <= 2.1.1 could be confused by regexes in certain contexts and throw an exception. The workaround was to simply wrap the expression in parenthesis. E.g.
```js
// in 2.1.1 and previous
return /'/; // JSMin throws error
return (/'/); // no error
@@ -124,6 +119,14 @@ Use Minify 2.1.4+. Before there was a setting to adjust the maximum allowed.
This may also appear as "Virtual Directory does not allow contents to be listed". Minify requires that the URI `/min/` (a request for a directory listing) result in the execution of `/min/index.php`. On Apache, you would make sure `index.php` is listed in the [DirectoryIndex directive](http://httpd.apache.org/docs/2.0/mod/mod_dir.html#directoryindex). IIS calls this the [Default Document](http://www.iis.net/ConfigReference/system.webServer/defaultDocument).
## "WARN: environment : Local HTTP request failed. Testing cannot continue."
The `test_environment.php` unit test makes a few local HTTP requests to sniff for `zlib.output_compression` and other auto-encoding behavior, which may break Minify's output. This warning will appear if `allow_url_fopen` is disabled in php.ini, but **does not** necessarily mean there is a problem.
If Minify seems to work fine, ignore the warning. If Minify produces garbled output, enable `allow_url_fopen` in php.ini and re-run the tests. The tests may be able to tell you if PHP or your server is automatically encoding output.
Unless you need it in other scripts, disable `allow_url_fopen` once the issue is resolved. Minify does not need it.
## See Also
* [Debugging](Debugging.md)
* [Debugging](Debugging.wiki.md)

View File

@@ -1,5 +1,7 @@
# PHP5 Component Classes
(This information is not updated for version 3)
| **Class** | **Functionality** |
|:----------|:------------------|
| [Minify](http://code.google.com/p/minify/source/browse/min/lib/Minify.php) | Combine, process, and serve pieces of content (usually CSS/JS files). Almost all behavior is configurable including request handling (via [controllers](http://code.google.com/p/minify/source/browse/min/lib/Minify/Controller/)), HTTP headers & encoding, caching ([file/APC/memcached](http://code.google.com/p/minify/source/browse/min/lib/Minify/Cache/)), and compression functions (by default, the classes below) |

View File

@@ -1,21 +1,19 @@
Unless mentioned, all the following snippets go in `min/config.php`.
Unless mentioned, all the following snippets go in `config.php`.
## Faster Cache Performance
By default, Minify uses [Minify\_Cache\_File](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Cache/File.php). It uses `readfile`/`fpassthru` to improve performance over most file-based systems, but it's still file IO. I haven't done comparative benchmarks on all three, but APC/Memcache _should_ be faster. In all cases, Minify cache ids begin with `"minify_"`.
By default, Minify uses `Minify_Cache_File`. It uses `readfile`/`fpassthru` to improve performance over most file-based systems, but it's still file IO, so the following caching options should be faster. In all cases, Minify cache ids begin with `"minify_"`.
### APC
```
require "$min_libPath/Minify/Cache/APC.php";
```php
$min_cachePath = new Minify_Cache_APC();
```
### Memcache
You must create and connect your Memcache object then pass it to `Minify_Cache_Memcache`'s constructor.
```
require "$min_libPath/Minify/Cache/Memcache.php";
```php
$memcache = new Memcache;
$memcache->connect('localhost', 11211);
$min_cachePath = new Minify_Cache_Memcache($memcache);
@@ -23,30 +21,44 @@ $min_cachePath = new Minify_Cache_Memcache($memcache);
### Zend Platform
Patrick van Dissel has contributed a [cache adapter for Zend Platform](http://code.google.com/p/minify/issues/detail?id=167).
```php
$min_cachePath = new Minify_Cache_ZendPlatform();
```
### XCache
```php
$min_cachePath = new Minify_Cache_XCache();
```
### WinCache
```php
$min_cachePath = new Minify_Cache_WinCache();
```
## Closure Compiler API Wrapper
An [experimental wrapper for Google's closure compiler API](https://github.com/mrclay/minify/blob/master/min/lib/Minify/JS/ClosureCompiler.php) is available for compressing Javascript. If the API fails for any reason, JSMin is used as the default backup minifier.
```
$min_serveOptions['minifiers']['application/x-javascript'] = array('Minify_JS_ClosureCompiler', 'minify');
An [experimental wrapper for Google's closure compiler API](../lib/Minify/JS/ClosureCompiler.php) is available for compressing Javascript. If the API fails for any reason, JSMin is used as the default backup minifier.
```php
$min_serveOptions['minifiers'][Minify::TYPE_JS] = array('Minify_JS_ClosureCompiler', 'minify');
```
## YUICompressor
If your host can execute Java, you can use Minify's YUI Compressor wrapper. You'll need the latest [yuicompressor-x.x.x.jar](http://yuilibrary.com/downloads/#yuicompressor) and a temp directory. Place the .jar in `min/lib`, then:
```
If your host can execute Java, you can use Minify's YUI Compressor wrapper. You'll need the latest [yuicompressor-x.x.x.jar](https://github.com/yui/yuicompressor/releases) and a temp directory. Place the .jar in `min/lib`, then:
```php
function yuiJs($js) {
Minify_YUICompressor::$jarFile = __DIR__ . '/lib/yuicompressor-x.x.x.jar';
Minify_YUICompressor::$tempDir = '/tmp';
return Minify_YUICompressor::minifyJs($js);
}
$min_serveOptions['minifiers']['application/x-javascript'] = 'yuiJs';
$min_serveOptions['minifiers'][Minify::TYPE_JS] = 'yuiJs';
```
To use YUIC for CSS with fixed URIs:
```
```php
function yuiCss($css, $options) {
Minify_YUICompressor::$jarFile = __DIR__ . '/lib/yuicompressor-x.x.x.jar';
Minify_YUICompressor::$tempDir = '/tmp';
@@ -60,48 +72,21 @@ function yuiCss($css, $options) {
);
return $css;
}
$min_serveOptions['minifiers']['text/css'] = 'yuiCss';
$min_serveOptions['minifiers'][Minify::TYPE_CSS] = 'yuiCss';
```
### CSSmin PHP port
## Legacy CSS compressor
Minify has added Túbal Martín's [PHP port](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port/blob/master/cssmin.php) of the YUI Compressor's CSSmin. While it is not completely integrated yet, you may try it out:
In 3.x, Minify uses [CSSmin](https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port), a PHP port of the YUI CSS compressor. To use the compressor that came with Minify 2.x (not recommended), uncomment this line in your `config.php` file:
```
function yuiCssPort($css, $options) {
$compressor = new CSSmin();
$css = $compressor->run($css, 9999999);
$css = Minify_CSS_UriRewriter::rewrite(
$css,
$options['currentDir'],
isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'],
isset($options['symlinks']) ? $options['symlinks'] : array()
);
return $css;
}
$min_serveOptions['minifiers']['text/css'] = 'yuiCssPort';
```
As of commit [218f37](https://github.com/mrclay/minify/commit/218f37fb44f9be2ea138cf9efb8b7f6dc84bad7f), this is easier:
```
$min_serveOptions['minifiers']['text/css'] = array('Minify_CSSmin', 'minify');
```
## JSMin+
Tino Zijdel's [JSMin+](http://crisp.tweakblogs.net/blog/6861/jsmin%2B-version-14.html) has resulted in memory usage problems for many users and will be removed from the Minify codebase in 3.0. If you wish to use it, you should download it outside the Minify directory and link to it:
```
require '/path/to/jsminplus.php';
$min_serveOptions['minifiers']['application/x-javascript'] = array('JSMinPlus', 'minify');
```php
//$min_serveOptions['minifiers'][Minify::TYPE_CSS] = array('Minify_CSS', 'minify');
```
## Server-specific Options
You may need to have different options depending on what server you're on. You can do this just how you'd expect:
```
```php
if ($_SERVER['SERVER_NAME'] == 'myTestingWorkstation') {
// testing
$min_allowDebugFlag = true;
@@ -126,7 +111,7 @@ If you test/develop sites in a subdirectory (e.g. `http://localhost/siteA/`), se
## Group-specific Options
In "group" requests, `$_GET['g']` holds the group key, so you can code based on it:
```
```php
if (isset($_GET['g'])) {
switch ($_GET['g']) {
case 'js' : $min_serveOptions['maxAge'] = 86400 * 7;
@@ -144,7 +129,7 @@ See CustomSource.
## Processing Output After Minification
If `$min_serveOptions['postprocessor']` is set to a callback, Minify will pass the minified content to this function with type as the second argument. This allows you to apply changes to your minified content without making your own custom minifier. E.g.:
```
```php
function postProcess($content, $type) {
if ($type === Minify::TYPE_CSS) {
require_once 'CssColorReplacer.php';

View File

@@ -1,153 +1,108 @@
Please see the UserGuide if you're just getting started with Minify. This guide is for more advanced users who wish to implement an HTTP server in PHP using the [Minify](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify.php) class.
Please see the UserGuide if you're just getting started with Minify. This guide is for more advanced users who wish to implement an HTTP server in PHP using the `Minify` class.
The basic steps are:
1. Set up the include\_path
1. Set up caching
1. Choose a Minify controller
1. Set up service and controller options
1. Handle the request
## Set up the include\_path
## Pull in Minify via Composer
In composer.json:
```
set_include_path('/path/to/min/lib' . PATH_SEPARATOR . get_include_path());
"require": {
"mrclay/minify": "~3"
},
```
## Set up caching
Minify ships with [cache classes](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Cache/) for files, APC, and memcache. Choose one, instantiate it, and pass it to Minify:
```bash
composer install
```
require 'Minify.php';
require 'Minify/Cache/File.php';
Minify::setCache(new Minify_Cache_File()); // files in directory chosen by Solar_Dir
## Set up autoloading, caching, and the Minify instance
Minify ships with several [cache classes](https://github.com/mrclay/minify/tree/master/lib/Minify/Cache) for files, APC, memcache, etc.:
```php
require __DIR__ . '/vendor/autoload.php';
$cache = new Minify_Cache_APC();
$minify = new Minify($cache);
```
## Create the environment and the factory for source objects
```php
$env = new Minify_Env();
$sourceFactory = new Minify_Source_Factory($env, [], $cache);
```
## Choose a Minify controller
Minify uses controller classes to analyze HTTP requests and determine which sources will make up the response. (The content isn't always returned; if the browser's cache is valid, Minify can return a 304 header instead).
Minify uses controller classes to analyze the environment (HTTP requests) and determine which sources will make up the response. (The content isn't always returned; if the browser's cache is valid, Minify can return a 304 header instead).
The [Files](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Controller/Files.php#9) controller doesn't care about the HTTP request. It just specifies a given array of sources (file paths or [source objects](CustomSource.md)).
The `Files` controller doesn't care about the HTTP request. It just specifies a given array of sources (file paths or [source objects](CustomSource.md)).
The [Groups](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Controller/Groups.php) controller uses `$_SERVER['PATH_INFO']` to choose from an array of source lists. There's an example at the end of this page.
The `Groups` controller uses `$_SERVER['PATH_INFO']` to choose from an array of source lists. There's an example at the end of this page.
The Files controller is simple, so we'll use it here.
```php
$controller = new Minify_Controller_Files($env, $sourceFactory);
```
## Set up service and controller options
A single array is used for configuration, and is passed to the `Minify::serve` method. `serve` passes it to the controller, which picks off the keys it needs and returns it to `serve` for Minify's own built-in [options](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify.php#88). This architecture allows the controller to set defaults for `serve`.
A single array is used for configuring both the behavior of `Minify::serve` (see the [default options](../lib/Minify.php#L73)) and the controller, which has its own option keys.
The Files controller only needs one key: `files`: the array of sources to be combined and served.
The only `serve` option we'll set is `maxAge` (the default is a measly 1800 seconds).
The only `serve` option we'll set is `maxAge` (the default is only 1800 seconds).
```
$options = array(
```php
$options = [
// options for the controller
'files' => array('//js/file1.js', '//js/file2.js', $src),
'files' => ['//js/file1.js', '//js/file2.js'],
// options for Minify::serve
'maxAge' => 86400
);
'maxAge' => 86400,
'minifierOptions' => [
'text/css' => [
'docRoot' => $env->getDocRoot(), // allows URL rewriting
],
],
];
```
(In the above $src is a [Minify\_Source object](CustomSource.md), which allows you to serve non-file content, and/or apply settings to individual sources.)
Note: `files` can also accept `Minify_Source` objects, which allow serving more than static files.
## Handle the request
```
Minify::serve('Files', $options);
```php
$minify->serve($controller, $options);
```
That's it...
Minify's default application (sometimes referred to as "min") is implemented this way, in the file [min/index.php](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/index.php). Most of its request handling is encapsulated in its own [MinApp](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Controller/MinApp.php#15) controller.
Minify's default application (`index.php`) is implemented similarly, but uses the `MinApp` controller. If you need URL rewriting in CSS files, you'll need to configure
# The Request Cycle
In handling a request, `Minify::serve` does a number of operations:
1. creates a controller object, unless you pass it a readymade object (let's call this `$ctrl`).
1. calls `$ctrl->setupSources($options)`, which analyzes the request and sets `$ctrl->sources` accordingly to an array of Minify\_Source objects.
1. calls `$ctrl->analyzeSources($options)`, which set `$options` keys for `contentType` and `lastModifiedTime`, based on the sources.
1. calls `$ctrl->mixInDefaultOptions($options)`, to mix the controller's default options with the user's.
1. Minify merges your given options with its default options
1. calls the controller's `createConfiguration()`, which analyzes the request and returns a `Minify_ServeConfiguration` object, encapsulating the source objects found.
1. uses `analyzeSources()` to determine the Content-Type and last modified time.
1. determines if the browser accepts gzip
1. validates the browser cache (optionally responding with a 304)
1. validates the server cache. If it needs refreshing, `Minify::_combineMinify` is called, which...
* calls `$source->getContent` on each source
* the content is combined before or after minification (depending on individual source options)
1. validates the server cache. If it needs refreshing, `combineMinify()` fetchs and combines the content of each source.
1. sets up headers to be sent
1. either returns the headers and content in an array (if `quiet` is set), or sends it to the browser.
# Using the Groups controller
The Groups controller uses `$_SERVER['PATH_INFO']` to select an array of sources from the given options:
```
$options = array(
'groups' => array(
'js' => array('//js/file1.js', '//js/file2.js'),
'css' => array('//css/file1.css', '//css/file2.css'),
),
);
Minify::serve('Groups', $options);
$options = [
'groups' => [
'js' => ['//js/file1.js', '//js/file2.js'],
'css' => ['//css/file1.css', '//css/file2.css'],
],
];
```
With the above, if you request `http://example.org/myServer.php/css`, Apache will set `$_SERVER['PATH_INFO'] = '/css'` and the sources in `$options['groups']['css']` will be served.
<a href='Hidden comment: Move somewhere else
== Sending far future Expires headers ==
By default Minify enables [http://fishbowl.pastiche.org/2002/10/21/http_conditional_get_for_rss_hackers conditional GETs] for client-side caching, but in this model the browser has to continuously check back with the server to revalidate its cache. A better method is to send [http://developer.yahoo.com/performance/rules.html#expires far future Expires headers] and change the URL when the file changes. As long as you generate your HTML with PHP, Minify makes it easy to do this.
1. Move the "groups" configuration array into a separate file "_groupsConfig.php":
```
<?php
// configures both Minify::serve() and Minify_Build
return array(
"js1" => array("//js/yourFile1.js", "//js/yourFile2.js")
,"js2" => array("//js/yourFile1.js", "//js/yourFile3.js")
,"jQuery" => array("//js/jquery-1.2.6.js")
,"css" => array("//css/layout.css", "//css/fonts.css")
);
```
2. Adjust "min.php" to use this file:
```
Minify::serve("Groups", array(
"groups" => (require "_groupsConfig.php")
));
```
3. In your HTML-generating script, create Minify_Build objects for each minify URI you"re going to use, and, to each, apply the uri() method:
```
require "Minify/Build.php";
$_gc = (require "_groupsConfig.php");
$js1Build = new Minify_Build($_gc["js1"]);
$cssBuild = new Minify_Build($_gc["css"]);
echo "<link rel="stylesheet" type="text/css" href="" . $cssBuild->uri("/min.php/css") . "" />";
/* ... */
echo "<script type="text/javascript" src="" . $js1Build->uri("/min.php/js1") . ""></script>";
```
5. Open the (X)HTML page in your browser. The Javascript and CSS should "work".
6. View source. The minify URIs should look like: "/min.php/js1?##########" (a unix timestamp)
7. Now that our URI"s are synched with source file changes, we can safely send Expires headers. In "min.php":
```
Minify::serve("Groups", array(
"groups" => (require "_groupsConfig.php")
,"maxAge" => 31536000 // 1 yr
// in 2.0 was "setExpires" => $_SERVER["REQUEST_TIME"] + 31536000
));
```
Now "min.php" will serve files with Expires headers, causing the browser to always retrieve them from cache (until the expiration date).
When you make a change to any of your source Javascript/CSS files, your HTML file will have a different querystring appended to the minify URIs, causing the browser to download it as a new URL, and Minify will automatically rebuild its cache files on the server.
'></a>

View File

@@ -1,11 +1,12 @@
In `groupsConfig.php`, usually you specify source file paths using strings like `/path/to/file.js` or `//js/file1.js` (Minify expands this to `"{$_SERVER['DOCUMENT_ROOT']}/js/file1.js"` ).
Instead of a string, you may substitute a "source object" (an instance of class [Minify\_Source](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/lib/Minify/Source.php)). This allows you to customize how minification is applied, and/or pull content from a non-file location (e.g. a URL).
Instead of a string, you may substitute an instance of class `Minify_Source`. This allows you to customize how minification is applied, and/or pull content from a non-file location (e.g. a URL).
### Example: filepath
In the `$spec` array, set the key `filepath` to produce a source based on a file path:
```
```php
$src1 = new Minify_Source(array(
'filepath' => '//js/file1.js',
));
@@ -13,15 +14,16 @@ $src2 = new Minify_Source(array(
'filepath' => '//js/file2.js',
));
return array(
'js' => array($src1, $src2)
);
return [
'js' => [$src1, $src2]
];
```
Note the above is functionally identical to:
```
return array(
'js' => array('//js/file1.js', '//js/file2.js')
);
```php
return [
'js' => ['//js/file1.js', '//js/file2.js'],
];
```
### Example: Specify a different minifier or none at all
@@ -30,26 +32,19 @@ To change minifier, set `minifier` to a [callback](http://php.net/manual/en/lang
**`*`Prepare for `groupsConfig.php` to be executed more than once.** (This is likely if you're using the functions in `/min/utils.php`.) In practice this just means making sure functions are conditionally defined if they don't already exist, etc.
```
if (! function_exists('myMin')) {
function myMin($js) {
require_once '/path/to/jsminplus.php';
return JSMinPlus::minify($js);
}
}
```php
$src1 = new Minify_Source(array(
'filepath' => '//js/file1.js',
'minifier' => 'myMin',
'minifier' => 'myJsMinifier',
));
$src2 = new Minify_Source(array(
'filepath' => '//js/file2.js',
'minifier' => '', // don't compress
'minifier' => 'Minify::nullMinifier', // don't compress
));
```
In the above, `myMin()` is only called when rebuilding the cache. This way `JSMinPlus.php` is only loaded when needed.
In the above, `JmyJsMinifier()` is only called when the contents of `$src1` is needed.
**`*`Do _not_ use [create\_function](http://php.net/manual/en/function.create-function.php) or anonymous functions for the minifier.** The internal names of these function tend to vary, causing Minify to create lots of [duplicate cache files](http://code.google.com/p/minify/issues/detail?id=138) (and perform poorly).
**`*`Do _not_ use `create_function()` or anonymous functions for the minifier.** The internal names of these function tend to vary, causing endless cache misses, killing performance and filling cache storage up.
## Non-File Sources
@@ -63,24 +58,23 @@ You're not limited to flat js/css files, but without `filepath`, the `$spec` arr
### Example: Content from a URL
Here we want to fetch javascript from a URL. We don't know when it will change, so we use a stepping expression to re-fetch it every midnight:
```
```php
if (! function_exists('src1_fetch')) {
function src1_fetch() {
return file_get_contents('http://example.org/javascript.php');
}
}
$src1 = new Minify_Source(array(
$src1 = new Minify_Source([
'id' => 'source1',
'getContentFunc' => 'src1_fetch',
'contentType' => Minify::TYPE_JS,
'lastModified' => ($_SERVER['REQUEST_TIME'] - $_SERVER['REQUEST_TIME'] % 86400),
));
]);
```
If you know that the URL content only depends on a few local files, you can use the maximum of their `mtime`s as the `lastModified` key:
```
...
$src1 = new Minify_Source(array(
```php
$src1 = new Minify_Source([
'id' => 'source1',
'getContentFunc' => 'src1_fetch',
'contentType' => Minify::TYPE_JS,
@@ -88,51 +82,55 @@ $src1 = new Minify_Source(array(
filemtime('/path/to/javascript.php')
,filemtime('/path/to/javascript_input.css')
),
));
]);
```
## Performance Considerations
Be aware that all the code you put in groupsConfig.php will be evaluated upon every request like `/min/g=...`, so make it as light as possible.
Be aware that all the code you put in `groupsConfig.php` will be evaluated upon every request like `/min/g=...`, so make it as light as possible.
If you wish to keep groupsConfig.php "clean", you can alternately create a separate PHP script that manually sets up sources, caching, options, and calls Minify::serve().
```
<?php // myServer.php
If you wish to keep `groupsConfig.php` "clean", you can alternately create a separate PHP script that manually sets up sources, caching, options, and calls Minify::serve().
```php
// myServer.php
/**
* This script implements a Minify server for a single set of sources.
* If you don't want '.php' in the URL, use mod_rewrite...
*/
require __DIR__ . '/vendor/autoload.php';
// setup Minify
set_include_path('path/to/min/lib' . PATH_SEPARATOR . get_include_path());
require 'Minify.php';
require 'Minify/Cache/File.php';
Minify::setCache(new Minify_Cache_File()); // guesses a temp directory
$cache = new Minify_Cache_File();
$minify = new Minify($cache);
$env = new Minify_Env();
$sourceFactory = new Minify_Source_Factory($env, [], $cache);
$controller = new Minify_Controller_Files($env, $sourceFactory);
function src1_fetch() {
return file_get_contents('http://example.org/javascript.php');
}
// setup sources
$sources = array();
$sources[] = new Minify_Source(array(
'id' => 'source1'
,'getContentFunc' => 'src1_fetch'
,'contentType' => Minify::TYPE_JS
,'lastModified' => max(
filemtime('/path/to/javascript.php')
,filemtime('/path/to/javascript_input.js')
)
));
$sources = [];
$sources[] = new Minify_Source([
'id' => 'source1',
'getContentFunc' => 'src1_fetch',
'contentType' => Minify::TYPE_JS,
'lastModified' => max(
filemtime('/path/to/javascript.php'),
filemtime('/path/to/javascript_input.js')
),
]);
$sources[] = '//file2.js';
$sources[] = '//file3.js';
// setup serve options
$options = array(
// setup serve and controller options
$options = [
'files' => $sources,
'maxAge' => 86400,
);
];
// handle request
Minify::serve('Files', $options);
```
$minify->serve($controller, $options);
```

View File

@@ -7,9 +7,9 @@
You can find details by enabling FirePHP logging:
1. Install/enable [FirePHP](https://addons.mozilla.org/en-US/firefox/addon/6149).
1. Open Firebug's console
1. Set `$min_errorLogger = true;` in min/config.php
1. Install/enable FirePHP for [Firefox](https://addons.mozilla.org/en-US/firefox/addon/6149) or [Chrome](https://chrome.google.com/webstore/detail/firephp4chrome/gpgbmonepdpnacijbbdijfbecmgoojma?hl=en-US).
1. Open the Chrome DevTools/Firebug console
1. Set `$min_errorLogger = true;` in config.php
1. Reload the Minify URL
Hopefully you'll see the error appear:
@@ -22,7 +22,7 @@ Minify: Something bad happened!
When Javascript errors occur, or URIs in CSS files are incorrectly rewritten, enable "debug mode" to ease debugging combined files:
1. Set `$min_allowDebugFlag = 'true'` in min/config.php
1. Set `$min_allowDebugFlag = true;` in config.php
1. Append `&debug` to the Minify URI. E.g. `/min/?f=script1.js,script2.js&debug` (or use the bookmarklet provided by /min/builder/)
In "debug mode":
@@ -34,7 +34,7 @@ In "debug mode":
Example: a combination of two Javascript files in debug mode
```
```js
/* firstFile.js */
/* 1 */ (function () {
@@ -70,11 +70,11 @@ traversals removed : /images/bg.jpg
### Tips for handling Javascript errors
* Use the latest version (2.1.4 beta as of Dec 2010)
* Try [debug mode](#Javascript/CSS_Problems.md) to make the combined file more readable (and error locations findable)
* Try [debug mode](#javascriptcss-problems) to make the combined file more readable (and error locations findable)
* Find out if other browsers have the same error
* For pre-minified files, make the filenames end in `.min.js` or `-min.js`, which will prevent Minify from altering them
* Test your scripts in [JSLint](http://www.jslint.com/).
## See Also
* CommonProblems
* [CommonProblems](CommonProblems.wiki.md)

View File

@@ -1,79 +1,51 @@
## Why do the CSS & HTML minifiers add so many line breaks?
## Minify (JSMin) doesn't compress as much as product XYZ. Why not?
TL;DR: Ignore them. They don't add to the output size and if you absolutely want all content on one line you will have to use another tool.
It's [rumored](https://github.com/yui/yuicompressor/blob/master/doc/README#L43) that some source control tools and old browsers don't like very long lines. Compressed files with shorter lines are also easier to diff.
Since both Minify classes are regex-based, it would be very difficult/error-prone to count characters then try to re-establish context to add line breaks. Instead, both classes trade 1 space for 1 line break (`\n`) wherever possible, adding breaks but without adding bytes.
If you can think of another safe & efficient way to limit lines in these two tools without adding bytes, please submit a patch, but this is not something anyone should be worrying about.
## Minify doesn't compress as much as product XYZ. Why not?
Out of the box, Minify uses algorithms available in PHP, which frankly aren't as solid as competing products, but I consider them _good enough_. At this point I don't have time to work on further tweaking, so if you must have _perfect_ minification you can explore the CookBook to plug in other minifiers. If you'd like to propose specific tweaks in the algorithm (and don't have a patch), please propose these in the Google Group, not the issue tracker.
The simple JSMin algorithm is the most reliable in PHP, but check the [CookBook](CookBook.wiki.md) to plug in other minifiers.
## How fast is it?
With Minify you will ideally serve _fewer_ requests, but Minify can be slower than your HTTPd serving flat files. If you have a high-traffic site with hundreds of simultaneous requests from new users, you should probably:
If you [serve static files](../static/README.md), it's as fast as your web server, and you should do this for high-traffic sites.
* Use the [APC/Memcache adapters](CookBook.md).
* Revision your Minify URIs (so far-off Expires headers will be sent). One way to do this is using [predefined groups](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/README.txt#69) and the [Minify\_groupUri()](http://code.google.com/p/minify/source/browse/tags/release_2.1.3/min/utils.php#13) utility function.
* Place your HTTPd behind a [reverse proxy](http://www.squid-cache.org/Intro/why.dyn) to cache the Minify URLs.
* Benchmark Minify on your development server before rolling out to production.
### Will it get faster?
Ideally, but a couple [other goals](ProjectGoals.md) come first. For Apache users we're designing a feature to enable [minified and pre-encoded files to be served directly from the HTTPd](http://mrclay.org/index.php/2008/05/25/apache-http-encoding-negotiation-notes/). Requests will not execute PHP at all and be blazingly fast (for varying definitions of "blazingly").
## How does it compare with other services?
Yahoo's [Combo Handler](http://yuiblog.com/blog/2008/07/16/combohandler/) and Google's [AJAX Libraries API](http://code.google.com/apis/ajaxlibs/) both serve content from their heavy-duty [CDN](http://en.wikipedia.org/wiki/Content_Delivery_Network)s and _potentially_ increase the chance that your visitor will already have a file in her browser cache. Neither service serves custom content that you provide. You may wish to use these services to serve popular libraries and Minify to serve your code.
## Is this where I get support for...
If you get a link to this page in response to a request for help, please make sure that you're using the software downloaded from [this project](http://code.google.com/p/minify/) (or [on github](https://github.com/mrclay/minify)), and have followed the [directions](UserGuide.md).
There are many projects with "minify" in the title/description but don't have anything to do with this project, or which many only use a few [components](ComponentClasses.md) from this project.
Although you may be able to get support for usage of the components, the [Google Group](http://groups.google.com/group/minify) members/project owners may not be able to offer any helpful advice with unrelated projects.
The PHP-based server is not as fast, but still performs well thanks to an internal cache. Tips:
* **Use a reverse proxy** to cache the Minify URLs. This is by far the most important tip.
* Revision your Minify URIs (so far-off Expires headers will be sent). One way to do this is using [groups](UserGuide.wiki.md#using-groups-for-nicer-urls) and the [Minify_groupUri()](UserGuide.wiki.md#far-future-expires-headers) utility function. Without this, clients will re-request Minify URLs every 30 minutes to check for updates.
* Use the [APC/Memcache adapters](CookBook.wiki.md).
## Does it support gzip compression?
Yes. Based on the browser's Accept-Encoding header, Minify will serve content encoded with deflate or gzip.
Yes. Based on the browser's Accept-Encoding header.
## Does it work with PHP opcode caches like APC and eAccelerator?
## Does it work with PHP opcode caches?
Of course, and you can also use [APC for content caching](CookBook.md).
Yes, and you can also use [APC for content caching](CookBook.wiki.md).
## Can it minify remote files/the output of dynamic scripts?
[Yes](http://code.google.com/p/minify/wiki/CustomSource#Non-File_Sources), but it's not a straightforward setup, and probably best avoided.
[Yes](CustomSource.wiki.md#non-file-sources), but it's not a straightforward setup, and probably best avoided.
## Is there a minifier for HTML?
Yes, but only in the form of a PHP class: [Minify\_HTML](http://code.google.com/p/minify/source/browse/min/lib/Minify/HTML.php).
It also can accept callbacks to minify embedded STYLE and SCRIPT elements.
Since Minify\_HTML is not fast, there's no _easy way_ to integrate it into dynamic pages, and you'll have to search the archives for ideas of how to use it. One opportunity would be when storing HTML (assuming writes are infrequent); e.g., in a DB keep one copy for editing and one minified for serving.
Minify is not suited for _serving_ HTML pages on a site, though it can be done for small numbers of static pages. Look the the [Page controller](http://code.google.com/p/minify/source/browse/min/lib/Minify/Controller/Page.php).
The class `Minify_HTML` can do this (and minify embedded STYLE and SCRIPT elements), but it's too slow to use directly. You'd want to integrate it into a system that caches the output. E.g., in a CMS, keep one copy for editing and one minified for serving.
## How does it ensure that the client can't request files it shouldn't have access to?
In 2.1, by default, Minify allows files to be specified using the URI, or using pre-configured sets of files. With URI-specified files, Minify is [very careful](Security.md) to serve only JS/CSS files that are already public on your server, but if you hide public directories--with .htaccess, e.g.--Minify can't know that. Obvious Tip: don't put sensitive info in JS/CSS files inside DOC\_ROOT :)
Minify allows files to be specified using the URI, or using pre-configured sets of files. With URI-specified files, Minify is very careful to serve only JS/CSS files that are already public on your server, but if you hide public directories--with .htaccess, e.g.--Minify can't know that. Obvious Tip: don't put sensitive info in JS/CSS files inside DOC_ROOT :)
An included option can disable URI-specified files so Minify will serve only the pre-configured file sets.
## Is it used in production by any large-scale websites?
I'd love to know. 2.1.1 had 54K downloads and I know the library is powering several [plugins](http://mrclay.org/index.php/2009/01/10/minify-getting-out-there/) these days, at least 3 for WordPress.
The libraries are used in many CMS's and frameworks, but the use of `index.php` to serve URLs like http://example.com/min/f=hello.js probably is rare. Minify is made to drop in place to boost small to medium sites not already built for performance.
Version 2.1.1 had 54K downloads.
## Can I use it with my commercial website or product?
Yes. Minify is distributed under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php), which means that you're free to use, modify, and redistribute Minify or derivative works thereof, even for commercial purposes, as long as you comply with a few simple requirements. See the [LICENSE.txt](http://code.google.com/p/minify/source/browse/LICENSE.txt) file for details.
Yes. Minify is distributed under the [New BSD License](http://www.opensource.org/licenses/bsd-license.php).
## How can I tell if my server cache is working?
The easiest way is to place a Minify URL directly in your browser's address bar and refresh (F5), which should override the client-side caching that Minify specifies and force Minify to send you a complete response. With cache working, this response should take 100ms or so. Without cache, multiple seconds. (You can get accurate response times using an HTTP inspector like Firebug.)
The easiest way is to place a Minify URL directly in your browser's address bar and refresh (F5), which should override the client-side caching that Minify specifies and force Minify to send you a complete response. With cache working, this response should take 100ms or so. Without cache, it could be multiple seconds.
If you have file access to the server you can check your cache path directly for filenames beginning with "minify
If you have file access to the server you can check your cache path directly for filenames beginning with `minify_`.

61
docs/Install.wiki.md Normal file
View File

@@ -0,0 +1,61 @@
# Installation
Minify requires PHP 5.3+, `git`, and `composer`.
## Typical Installation
Clone the project into the `min/` directory inside your document root and install its dependencies:
```bash
cd /path/to/public_html
git clone https://github.com/mrclay/minify.git min
cd min
composer install --no-dev
```
**Note:** If you do this on localhost, make sure the `min/vendor/` directory gets deployed to production.
## Installing as a composer dependency
Add `"mrclay/minify": "~3.0.0"` to your site's composer.json, and `composer install`.
The following assumes your `vendor` directory is in your document root. Adjust the `MINIFY` path as needed:
```bash
cd /path/to/public_html
mkdir min
MIN=min/
MINIFY=vendor/mrclay/minify/
cp ${MINIFY}example.index.php ${MIN}index.php
cp ${MINIFY}.htaccess ${MIN}
cp ${MINIFY}config.php ${MIN}
cp ${MINIFY}groupsConfig.php ${MIN}
cp ${MINIFY}quick-test.js ${MIN}
cp ${MINIFY}quick-test.css ${MIN}
```
Edit `min/index.php` to remove the ``die()`` statement and adjust the `vendor` path as needed.
**Note:** This does not install the [URL builder](BuilderApp.wiki.md), but it's not necessary for operation.
## Verifing it works
You can verify it works via these two URLs:
* http://example.org/min/?f=min/quick-test.js
* http://example.org/min/?f=min/quick-test.css
If your server supports mod_rewrite, the `?` are not necessary:
* http://example.org/min/f=min/quick-test.js
* http://example.org/min/f=min/quick-test.css
## Having trouble?
Write the [Google Group](http://groups.google.com/group/minify) for help.
## More links
* [Usage instructions](UserGuide.wiki.md)
* [Cookbook](CookBook.wiki.md) for more advanced options
* [All docs](docs)

View File

@@ -1,14 +1,14 @@
"Min" is the application included in Minify that handles requests to `http://example.com/min/` and responds with compressed/combined content.
"Min" is the front end application `index.php` that handles requests to `http://example.com/min/` and responds with compressed/combined content.
When the documentation refers to "Minify" it usually means this application, but sometimes refers to the ComponentClasses.
When the documentation refers to "Minify" it usually means this application, but sometimes refers to the [ComponentClasses](ComponentClasses.wiki.md).
User-configurable files:
* `/min/config.php`: general configuration
* `/min/groupsConfig.php`: configuration of pre-defined groups of files
* `/config.php`: general configuration
* `/groupsConfig.php`: configuration of pre-defined groups of files
Other files of interest:
* `/min/.htaccess`: rewrites URLs for the front controller
* `/min/index.php`: front controller
* `/min/lib/Minify/Controller/MinApp.php`: determines which files are combined based on `$_GET`, sets some default options
* `/.htaccess`: rewrites URLs for the front controller
* `/index.php`: front controller
* `/lib/Minify/Controller/MinApp.php`: determines which files are combined based on `$_GET`, sets some default options

View File

@@ -1,19 +0,0 @@
# Unit Testing
0. If you haven't already, install Minify using the UserGuide.
1. Copy the "min\_unit\_tests" directory directly into your DOCUMENT\_ROOT.
2. Browse to http://example.com/min_unit_tests/test_all.php
You should see a list of "PASS"es. You can run the individual test PHP files in http://example.com/min_unit_tests/ for more verbose output.
## Common Problems
### !WARN: environment : Local HTTP request failed. Testing cannot continue.
[test\_environment.php](http://code.google.com/p/minify/source/browse/min_unit_tests/test_environment.php) makes a few local HTTP requests to sniff for `zlib.output_compression` and other auto-encoding behavior, which may break Minify's output. This warning will appear if `allow_url_fopen` is disabled in php.ini, but **does not** necessarily mean there is a problem.
If Minify seems to work fine, ignore the warning. If Minify produces garbled output, enable `allow_url_fopen` in php.ini and re-run the tests. The tests may be able to tell you if PHP or your server is automatically encoding output.
Unless you need it in other scripts, disable `allow_url_fopen` once the issue is resolved. Minify does not need it.

View File

@@ -12,24 +12,24 @@ When Minify serves this content (from `http://example.org/min/f=theme/fashion/st
body{background:url(/theme/fashion/bg.jpg)}
```
You can see the steps used to rewrite your URIs by enabling [debug mode](Debugging.md).
You can see the steps used to rewrite your URIs by enabling [debug mode](Debugging.wiki.md).
## Disable Rewriting
You can disable the automatic rewriting by setting this in min/config.php:
```
You can disable the automatic rewriting by setting this in config.php:
```php
$min_serveOptions['rewriteCssUris'] = false;
```
## Manual Rewriting
You can manually rewrite relative URIs in CSS in a couple ways. The simplest is to prepend a string to each relative URI:
```
```php
$min_serveOptions['rewriteCssUris'] = false;
$min_serveOptions['minifierOptions']['text/css']['prependRelativePath'] = '/css/';
$min_serveOptions['minifierOptions'][Minify::TYPE_CSS]['prependRelativePath'] = '/css/';
```
Or you can run the minified output through a custom [post-processor](CookBook#Processing_Output_After_Minification.md) function.
Or you can run the minified output through a custom [post-processor](CookBook.wiki.md#Processing_Output_After_Minification.md) function.
## Document Root Confusion
@@ -44,8 +44,8 @@ Whether you use [aliases](http://httpd.apache.org/docs/2.2/mod/mod_alias.html),
| Apache mod\_alias | `Alias /static /etc/static_content` |
| ...or symlink | `ln -s /etc/static_content /var/www/static` |
In `/min/config.php` you'll need the following:
```
In `/config.php` you'll need the following:
```php
// map URL path to file path
$min_symlinks = array(
'//static' => '/etc/static_content'
@@ -54,7 +54,7 @@ $min_symlinks = array(
This lets Minify know during the rewriting process that an internal file path starting with `/etc/static_content` should be rewritten as a public URI beginning with `/static`.
If your alias target directory is outside of DOC\_ROOT, you'll also need to explicitly allow Minify to serve files from it:
```
```php
$min_serveOptions['minApp']['allowDirs'] = array(
'//', // allow from the normal DOC_ROOT
'/etc/static_content' // allow from our alias target
@@ -67,7 +67,7 @@ You can enable the script `min/server-info.php` and open http://example.org/min/
## It's still not working
1. Make sure you have the [latest version](http://code.google.com/p/minify/downloads/list).
1. Enable [debug mode](Debugging.md), which will show you the URI transformation process.
1. Check that `$_SERVER['DOCUMENT_ROOT']` is a real directory path. If not, URI rewriting will fail. If you cannot fix this in httpd.conf, etc., use the [configuration option](http://code.google.com/p/minify/source/browse/min/config.php?r=292#47).
1. Paste your [debug mode](Debugging.md) comment block into a new post on the [minify mailing list](http://groups.google.com/group/minify).
1. Make sure you have the latest version.
1. Enable [debug mode](Debugging.wiki.md), which will show you the URI transformation process.
1. Check that `$_SERVER['DOCUMENT_ROOT']` is the correct directory path. If not, URI rewriting will fail. If you cannot fix this in httpd.conf, etc., set `$min_documentRoot` in config.php.
1. Paste your [debug mode](Debugging.wiki.md) comment block into a new post on the [minify mailing list](http://groups.google.com/group/minify).

View File

@@ -1,48 +1,129 @@
If this page doesn't help, please post a question on our [Google group](http://groups.google.com/group/minify).
# Installing Minify for the first time
# Creating Minify URLs
1. Clone the [minify git repository](https://github.com/mrclay/minify).
1. The distribution contains a folder "min". Copy this into your DOCUMENT\_ROOT so it is a **direct child** of DOCUMENT\_ROOT (e.g. `http://example.com/min/`). Document roots are usually named `htdocs`, `public_html`, or `www`.
1. Test it can serve content: http://example.com/min/?f=min/quick-test.js and http://example.com/min/?f=min/quick-test.css
1. If you want to use the BuilderApp, you must enable it in `min/config.php`.
Let's say you want to serve the file http://example.com/css/foo/bar.css
Note: The BuilderApp will not function properly in subdirectories, but it's not necessary for Minify's functionality.
You would use the URL http://example.com/min/?f=css/foo/bar.css
Done!
In other words, the "f" argument is set to the file path from root without the initial `/`. As CSS files may contain relative URIs, Minify will automatically "fix" these by rewriting them as root relative.
(Optional) See TestingMinify if you'd like to run unit tests.
To combine multiple files, separate the paths given to "f" with commas.
Let's say you have JS files at these URLs:
* http://example.com/scripts/library-1.5.js
* http://example.com/scripts/site.js
You'd use the URL http://example.com/min/?f=scripts/library-1.5.js,scripts/site.js
## Shortening URLs with common directories
If you're combining files that share the same ancestor directory, you can use the "b" argument to set the base directory for the "f" argument. Do not include the leading or trailing `/` characters.
E.g., the following URLs will serve the exact same content:
* http://example.com/min/?f=path/to/scripts/library-1.5.js,path/to/scripts/foo/site.js
* http://example.com/min/?b=path/to/scripts&f=library-1.5.js,site.js,home.js
# Limiting access to directories
By default, Minify will serve any CSS/JS files within the DOCUMENT_ROOT. If you'd prefer to limit Minify's access to certain directories, set the `$min_serveOptions['minApp']['allowDirs']` array in config.php. E.g. to limit to the `/js` and `/themes/default` directories, use:
```php
$min_serveOptions['minApp']['allowDirs'] = ['//js', '//themes/default'];
```
# Using groups for nicer URLs
For nicer URLs, edit groupsConfig.php to pre-specify groups of files to be combined under preset keys. E.g., here's an example configuration in groupsConfig.php:
```php
return [
'js' => ['//js/Class.js', '//js/email.js'],
];
```
This pre-selects the following files to be combined under the key "js":
* http://example.com/js/Class.js
* http://example.com/js/email.js
You can now serve these files with http://example.com/min/?g=js
## Specifying files outside the document root
In the groupsConfig.php array, the `//` in the file paths is a shortcut for the DOCUMENT_ROOT, but you can also specify paths from the root of the filesystem or relative to the DOC_ROOT:
```php
return [
'js' => [
'//js/file.js', // file within DOC_ROOT
'//../file.js', // file in parent directory of DOC_ROOT
'C:/Users/Steve/file.js', // file anywhere on filesystem
],
];
```
## Multiple groups and files in one URL
E.g.: http://example.com/min/?g=js&f=more/scripts.js
Separate group keys with commas: http://example.com/min/?g=baseCss,css1&f=moreStyles.css
## Creating URLs with the Builder
Enable the [BuilderApp](BuilderApp.wiki.md) via config.php. The default password is "admin", but even if no password is used there's very little server information disclosed.
Browse to http://example.com/min/builder/
The Minify URI Builder will help you create URIs you can use to minify existing files on your site. You can see screenshots and get a feel for this process from this [walkthrough on mrclay.org](http://mrclay.org/index.php/2008/09/19/minify-21-on-mrclayorg/)
You may want to disable the [BuilderApp](BuilderApp.wiki.md) when not in use.
# Far-future Expires headers
Minify can send far-future (one year) Expires headers. To enable this you must add a number or the parameter "v" to the querystring (e.g. `/min/?g=js&1234` or `/min/?g=js&v=1234`) and alter it whenever a source file is changed. If you have a build process you can use a build/source control revision number.
You can alternately use the utility function `Minify_getUri()` to get a "versioned" Minify URI for use in your HTML (it sniffs the `mtime` of the files). E.g.:
```php
require 'path/to/min/utils.php';
$jsUri = Minify_getUri('js'); // a key in groupsConfig.php
echo "<script src='{$jsUri}'></script>";
$cssUri = Minify_getUri([ // a list of files
'//css/styles1.css',
'//css/styles2.css',
]);
echo "<link rel=stylesheet href='{$cssUri}'>";
```
# Debug mode
In debug mode, instead of compressing files, Minify sends combined files with comments prepended to each line to show the line number in the original source file. To enable this, set `$min_allowDebugFlag` to `true` in config.php and append `&debug=1` to your URIs. E.g. `/min/?f=script1.js,script2.js&debug=1`
Known issue: files with comment-like strings/regexps can cause problems in this mode.
# Configuration
See [config.php](../config.php) for general config options.
[groupsConfig.php](../groupsConfig.php) holds preset groups of files to minify. (The builder application can help with this).
[CookBook](CookBook.wiki.md) shows how to customize settings between production/development environments, and between groups.
[CustomSource](CustomSource.wiki.md) shows how to set some file/source-specific options, or serve content from a PHP script or URL.
### Hosting on Lighttpd
Minify comes with Apache mod\_rewrite rules, but this does the same for Lighttpd:
Minify comes with Apache mod_rewrite rules, but this does the same for Lighttpd:
```
url.rewrite-once = ( "^/min/([a-z]=.*)" => "/min/index.php?$1" )
```
# Usage
Enable the BuilderApp via your [config file](https://github.com/mrclay/minify/blob/master/min/config.php#L10). The default password is "admin", but even if no password is used there's very little server information disclosed.
Browse to http://example.com/min/
The Minify URI Builder will help you create URIs you can use to minify existing files on your site. You can see screenshots and get a feel for this process from this [walkthrough on mrclay.org](http://mrclay.org/index.php/2008/09/19/minify-21-on-mrclayorg/)
More info here: https://github.com/mrclay/minify/blob/master/MIN.txt
You may want to disable the BuilderApp when not in use.
# Configuration
[min/config.php](https://github.com/mrclay/minify/blob/master/min/config.php) holds general config options.
[min/groupsConfig.php](https://github.com/mrclay/minify/blob/master/min/groupsConfig.php) holds preset groups of files to minify. (The builder application can help with this).
CookBook shows how to customize settings between production/development environments, and between groups.
CustomSource shows how to set some file/source-specific options, or serve content from a PHP script or URL.
# Problems?
See [CommonProblems](https://github.com/mrclay/minify/blob/master/docs/CommonProblems.wiki.md) and [Debugging](https://github.com/mrclay/minify/blob/master/docs/Debugging.wiki.md). You might also try [TestingMinify](https://github.com/mrclay/minify/blob/master/docs/TestingMinify.wiki.md) (running `test_environment.php` in particular).
See [CommonProblems](CommonProblems.wiki.md) and [Debugging](Debugging.wiki.md). You might also try running `server-info.php` in particular.

View File

@@ -1,92 +0,0 @@
## Version 2.1.5 (2012-03-10)
* Removed XSS vulnerability
* Disabled builder by default
* command line tools to minify and rewrite URIs in CSS
* upgrade (optional) JSMin+ library
* more efficient JS minification when using CC/YUIC
* Closure Compiler uses cURL when allow\_url\_fopen is off
* Missing file notices when using groups
## Version 2.1.4b (2010-07-10)
* Option to minify JS with Closure Compiler API w/ JSMin failover
* Cookie/bookmarklet-based debug mode. No HTML editing!
* Allows 1 file to be missing w/o complete failure
* Combine multiple groups and files in single URI
* More useful HTML helpers for writing versioned URIs
* More detailed error logging, including minifier exceptions
* Builder offers more helpful messages/PHP environment warnings
* Bypass minification based on filename pattern. e.g. foo.min.js / foo-min.css
* JSMin won't choke on common Closure compiler syntaxes (`i+ ++j`)
* Better caching in IE6
* Cache ids are influenced by group/file names
* Debug mode for Javascript doesn't break on common XPath strings (Prototype 1.6)
* Removed annoying maxFiles limit
* mbstring.func\_overload usage is safer
## Version 2.1.3 (2009-06-30)
* CSS fixes
* A few URI rewriting bugs fixed
* comment/whitespace removal no longer breaks some values
* IE6 [pseudo-element selector bug](http://www.crankygeek.com/ie6pebug/) no longer triggered
* HTTP fixes
* Proper Expires handling in webkit (dropped "must-revalidate", which triggered a [webkit bug](http://mrclay.org/index.php/2009/02/24/safari-4-beta-cache-controlmust-revalidate-bug/))
* ETag generation now valid ([must be unique when gzipped](https://issues.apache.org/bugzilla/show_bug.cgi?id=39727))
* Vary header always sent when Accept-Encoding is sniffed
* Dropped deflate encoding, since browser and proxy support [could be buggy](http://stackoverflow.com/questions/883841/).
* File cache now works w/o setting `$min_cachePath`
* No more 5.3 deprecation warnings: `split()` removed
* API: Can set contentType Minify\_Source objects (fixes an annoying [caveat](http://groups.google.com/group/minify/msg/8446d32ee99a4961))
* [Resolved Issue list](http://code.google.com/p/minify/issues/list?can=1&q=label%3ARelease-2.1.2%20status%3AVerified)
## Version 2.1.2 (2009-03-04)
* Javascript fixes
* Debug mode no longer confused by `*/*` in strings/RegExps (jQuery)
* quote characters inside RegExp literals no longer cause exception
* files ending in single-line comments no longer cause code loss
* CSS: data: URLs no longer mangled
* Optional error logging to Firefox's FirePHP extension
* Unit tests to check for common DOCUMENT\_ROOT problems
* DOCUMENT\_ROOT no longer overwritten on IIS servers
* Builder app doesn't fail on systems without gzdeflate()
* APC caching class included
## Version 2.1.1 (2008-10-19)
* Bug fix release
* Detection and workarounds for zlib.output\_compression and non-PHP encoding modules
* Zlib not required (mod\_rewrite, et.al., can still be used for encoding)
* HTML : More IE conditional comments preserved
* Minify\_groupUri() utility fixed
## Version 2.1.0 (2008-09-18)
* "min" default application for quick deployment
* Minify URI Builder app & bookmarklet for quickly creating minify URIs
* Relative URIs in CSS file are fixed automatically by default
* "debug" mode for revealing original line #s in combined files
* Better IIS support
* Improved minifier classes:
* JS: preserves IE conditional comments
* CSS: smaller output, preserves more hacks and valid CSS syntax, shorter line lengths, other bug fixes
* HTML: smaller output, shorter line lengths, other bug fixes
* Default Cache-Control: max-age of 30 minutes
* Conditional GETs supported even when max-age sent
* Experimental memcache cache class (default is files)
* Minify\_Cache\_File has flock()s (by default)
* Workaround for Windows mtime reporting bug
## Version 2.0.0 (2008-05-22)
* Complete code overhaul. Minify is now a PEAR-style class and toolkit for building customized minifying file servers.
* Content-Encoding: deflate/gzip/compress, based on request headers
* Expanded CSS and HTML minifiers with test cases
* Easily plug-in 3rd-party minifiers (like Packer)
* Plug-able front end controller allows changing the way files are chosen
* Compression & encoding modules lazy-loaded as needed (304 responses use minimal code)
* Separate utility classes for HTTP encoding and cache control
## Version 1.0.1 (2007-05-05)
* Fixed various problems resolving pathnames when hosted on an NFS mount.
* Fixed 'undefined constant' notice.
* Replaced old JSMin library with a much faster custom implementation.
## Version 1.0.0 (2007-05-02)
* First release.

11
example.index.php Normal file
View File

@@ -0,0 +1,11 @@
<?php
// template file for creating your own Minify endpoint
// remove this
die('disabled');
// adjust this path as necessary
require __DIR__ . '/../vendor/autoload.php';
$app = new \Minify\App(__DIR__);
$app->runServer();

19
groupsConfig.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
/**
* Groups configuration for default Minify implementation
* @package Minify
*/
/**
* You may wish to use the Minify URI Builder app to suggest
* changes. http://yourdomain/min/builder/
*
* See https://github.com/mrclay/minify/blob/master/docs/CustomServer.wiki.md for other ideas
**/
return array(
// 'testJs' => array('//minify/quick-test.js'),
// 'testCss' => array('//minify/quick-test.css'),
// 'js' => array('//js/file1.js', '//js/file2.js'),
// 'css' => array('//css/file1.css', '//css/file2.css'),
);

13
index.php Normal file
View File

@@ -0,0 +1,13 @@
<?php
/**
* Sets up MinApp controller and serves files
*
* DO NOT EDIT! Configure this utility via config.php and groupsConfig.php
*
* @package Minify
*/
$app = (require __DIR__ . '/bootstrap.php');
/* @var \Minify\App $app */
$app->runServer();

View File

@@ -1,6 +1,6 @@
<?php
/**
* Class HTTP_ConditionalGet
* Class HTTP_ConditionalGet
* @package Minify
* @subpackage HTTP
*/
@@ -21,7 +21,7 @@
* }
* echo $content;
* </code>
*
*
* E.g. Shortcut for the above
* <code>
* HTTP_ConditionalGet::check($updateTime, true); // exits if client has cache
@@ -40,7 +40,7 @@
* }
* echo $content;
* </code>
*
*
* E.g. Static content with some static includes:
* <code>
* // before content
@@ -60,11 +60,12 @@
* @subpackage HTTP
* @author Stephen Clay <steve@mrclay.org>
*/
class HTTP_ConditionalGet {
class HTTP_ConditionalGet
{
/**
* Does the client have a valid copy of the requested resource?
*
*
* You'll want to check this after instantiating the object. If true, do
* not send content, just call sendHeaders() if you haven't already.
*
@@ -74,36 +75,36 @@ class HTTP_ConditionalGet {
/**
* @param array $spec options
*
*
* 'isPublic': (bool) if false, the Cache-Control header will contain
* "private", allowing only browser caching. (default false)
*
*
* 'lastModifiedTime': (int) if given, both ETag AND Last-Modified headers
* will be sent with content. This is recommended.
*
* 'encoding': (string) if set, the header "Vary: Accept-Encoding" will
* always be sent and a truncated version of the encoding will be appended
* to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient
* to the ETag. E.g. "pub123456;gz". This will also trigger a more lenient
* checking of the client's If-None-Match header, as the encoding portion of
* the ETag will be stripped before comparison.
*
*
* 'contentHash': (string) if given, only the ETag header can be sent with
* content (only HTTP1.1 clients can conditionally GET). The given string
* should be short with no quote characters and always change when the
* resource changes (recommend md5()). This is not needed/used if
* content (only HTTP1.1 clients can conditionally GET). The given string
* should be short with no quote characters and always change when the
* resource changes (recommend md5()). This is not needed/used if
* lastModifiedTime is given.
*
*
* 'eTag': (string) if given, this will be used as the ETag header rather
* than values based on lastModifiedTime or contentHash. Also the encoding
* string will not be appended to the given value as described above.
*
*
* 'invalidate': (bool) if true, the client cache will be considered invalid
* without testing. Effectively this disables conditional GET.
* without testing. Effectively this disables conditional GET.
* (default false)
*
* 'maxAge': (int) if given, this will set the Cache-Control max-age in
* seconds, and also set the Expires header to the equivalent GMT date.
* After the max-age period has passed, the browser will again send a
*
* 'maxAge': (int) if given, this will set the Cache-Control max-age in
* seconds, and also set the Expires header to the equivalent GMT date.
* After the max-age period has passed, the browser will again send a
* conditional GET to revalidate its cache.
*/
public function __construct($spec)
@@ -113,7 +114,7 @@ class HTTP_ConditionalGet {
: 'private';
$maxAge = 0;
// backwards compatibility (can be removed later)
if (isset($spec['setExpires'])
if (isset($spec['setExpires'])
&& is_numeric($spec['setExpires'])
&& ! isset($spec['maxAge'])) {
$spec['maxAge'] = $spec['setExpires'] - $_SERVER['REQUEST_TIME'];
@@ -121,14 +122,14 @@ class HTTP_ConditionalGet {
if (isset($spec['maxAge'])) {
$maxAge = $spec['maxAge'];
$this->_headers['Expires'] = self::gmtDate(
$_SERVER['REQUEST_TIME'] + $spec['maxAge']
$_SERVER['REQUEST_TIME'] + $spec['maxAge']
);
}
$etagAppend = '';
if (isset($spec['encoding'])) {
$this->_stripEtag = true;
$this->_headers['Vary'] = 'Accept-Encoding';
if ('' !== $spec['encoding']) {
$this->_headers['Vary'] = 'Accept-Encoding';
if (0 === strpos($spec['encoding'], 'x-')) {
$spec['encoding'] = substr($spec['encoding'], 2);
}
@@ -156,14 +157,14 @@ class HTTP_ConditionalGet {
? false
: $this->_isCacheValid();
}
/**
* Get array of output headers to be sent
*
*
* In the case of 304 responses, this array will only contain the response
* code header: array('_responseCode' => 'HTTP/1.0 304 Not Modified')
*
* Otherwise something like:
*
* Otherwise something like:
* <code>
* array(
* 'Cache-Control' => 'max-age=0, public'
@@ -171,7 +172,7 @@ class HTTP_ConditionalGet {
* )
* </code>
*
* @return array
* @return array
*/
public function getHeaders()
{
@@ -180,13 +181,13 @@ class HTTP_ConditionalGet {
/**
* Set the Content-Length header in bytes
*
*
* With most PHP configs, as long as you don't flush() output, this method
* is not needed and PHP will buffer all output and set Content-Length for
* is not needed and PHP will buffer all output and set Content-Length for
* you. Otherwise you'll want to call this to let the client know up front.
*
*
* @param int $bytes
*
*
* @return int copy of input $bytes
*/
public function setContentLength($bytes)
@@ -196,9 +197,9 @@ class HTTP_ConditionalGet {
/**
* Send headers
*
*
* @see getHeaders()
*
*
* Note this doesn't "clear" the headers. Calling sendHeaders() will
* call header() again (but probably have not effect) and getHeaders() will
* still return the headers.
@@ -218,7 +219,7 @@ class HTTP_ConditionalGet {
header($name . ': ' . $val);
}
}
/**
* Exit if the client's cache is valid for this resource
*
@@ -227,8 +228,8 @@ class HTTP_ConditionalGet {
* @param int $lastModifiedTime if given, both ETag AND Last-Modified headers
* will be sent with content. This is recommended.
*
* @param bool $isPublic (default false) if true, the Cache-Control header
* will contain "public", allowing proxies to cache the content. Otherwise
* @param bool $isPublic (default false) if true, the Cache-Control header
* will contain "public", allowing proxies to cache the content. Otherwise
* "private" will be sent, allowing only browser caching.
*
* @param array $options (default empty) additional options for constructor
@@ -245,24 +246,23 @@ class HTTP_ConditionalGet {
exit();
}
}
/**
* Get a GMT formatted date for use in HTTP headers
*
*
* <code>
* header('Expires: ' . HTTP_ConditionalGet::gmtdate($time));
* </code>
* </code>
*
* @param int $time unix timestamp
*
*
* @return string
*/
public static function gmtDate($time)
{
return gmdate('D, d M Y H:i:s \G\M\T', $time);
}
protected $_headers = array();
protected $_lmTime = null;
protected $_etag = null;
@@ -297,7 +297,7 @@ class HTTP_ConditionalGet {
{
if (null === $this->_etag) {
// lmTime is copied to ETag, so this condition implies that the
// server sent neither ETag nor Last-Modified, so the client can't
// server sent neither ETag nor Last-Modified, so the client can't
// possibly has a valid cache.
return false;
}
@@ -305,6 +305,7 @@ class HTTP_ConditionalGet {
if ($isValid) {
$this->_headers['_responseCode'] = 'HTTP/1.0 304 Not Modified';
}
return $isValid;
}
@@ -316,20 +317,22 @@ class HTTP_ConditionalGet {
if (!isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
return false;
}
$clientEtagList = get_magic_quotes_gpc()
$clientEtagList = PHP_VERSION_ID < 50400 && get_magic_quotes_gpc()
? stripslashes($_SERVER['HTTP_IF_NONE_MATCH'])
: $_SERVER['HTTP_IF_NONE_MATCH'];
$clientEtags = explode(',', $clientEtagList);
$compareTo = $this->normalizeEtag($this->_etag);
foreach ($clientEtags as $clientEtag) {
if ($this->normalizeEtag($clientEtag) === $compareTo) {
// respond with the client's matched ETag, even if it's not what
// we would've sent by default
$this->_headers['ETag'] = trim($clientEtag);
return true;
}
}
return false;
}
@@ -338,8 +341,10 @@ class HTTP_ConditionalGet {
*
* @return string
*/
protected function normalizeEtag($etag) {
protected function normalizeEtag($etag)
{
$etag = trim($etag);
return $this->_stripEtag
? preg_replace('/;\\w\\w"$/', '"', $etag)
: $etag;
@@ -355,12 +360,17 @@ class HTTP_ConditionalGet {
}
// strip off IE's extra data (semicolon)
list($ifModifiedSince) = explode(';', $_SERVER['HTTP_IF_MODIFIED_SINCE'], 2);
if (strtotime($ifModifiedSince) >= $this->_lmTime) {
// Apache 2.2's behavior. If there was no ETag match, send the
$date = new DateTime($ifModifiedSince, new DateTimeZone('UTC'));
if ($date->getTimestamp() >= $this->_lmTime) {
// Apache 2.2's behavior. If there was no ETag match, send the
// non-encoded version of the ETag value.
$this->_headers['ETag'] = $this->normalizeEtag($this->_etag);
return true;
}
return false;
}
}

View File

@@ -1,14 +1,14 @@
<?php
/**
* Class HTTP_Encoder
* Class HTTP_Encoder
* @package Minify
* @subpackage HTTP
*/
/**
* Encode and send gzipped/deflated content
*
* The "Vary: Accept-Encoding" header is sent. If the client allows encoding,
* The "Vary: Accept-Encoding" header is sent. If the client allows encoding,
* Content-Encoding and Content-Length are added.
*
* <code>
@@ -26,7 +26,7 @@
* header('Content-Type: text/css'); // needed if not HTML
* HTTP_Encoder::output($css);
* </code>
*
*
* <code>
* // Just sniff for the accepted encoding
* $encoding = HTTP_Encoder::getAcceptedEncoding();
@@ -34,59 +34,58 @@
*
* For more control over headers, use getHeaders() and getData() and send your
* own output.
*
* Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
*
* Note: If you don't need header mgmt, use PHP's native gzencode, gzdeflate,
* and gzcompress functions for gzip, deflate, and compress-encoding
* respectively.
*
*
* @package Minify
* @subpackage HTTP
* @author Stephen Clay <steve@mrclay.org>
*/
class HTTP_Encoder {
class HTTP_Encoder
{
/**
* Should the encoder allow HTTP encoding to IE6?
*
* If you have many IE6 users and the bandwidth savings is worth troubling
* Should the encoder allow HTTP encoding to IE6?
*
* If you have many IE6 users and the bandwidth savings is worth troubling
* some of them, set this to true.
*
*
* By default, encoding is only offered to IE7+. When this is true,
* getAcceptedEncoding() will return an encoding for IE6 if its user agent
* string contains "SV1". This has been documented in many places as "safe",
* but there seem to be remaining, intermittent encoding bugs in patched
* but there seem to be remaining, intermittent encoding bugs in patched
* IE6 on the wild web.
*
*
* @var bool
*/
public static $encodeToIe6 = true;
/**
* Default compression level for zlib operations
*
*
* This level is used if encode() is not given a $compressionLevel
*
*
* @var int
*/
public static $compressionLevel = 6;
/**
* Get an HTTP Encoder object
*
*
* @param array $spec options
*
*
* 'content': (string required) content to be encoded
*
*
* 'type': (string) if set, the Content-Type header will have this value.
*
*
* 'method: (string) only set this if you are forcing a particular encoding
* method. If not set, the best method will be chosen by getAcceptedEncoding()
* The available methods are 'gzip', 'deflate', 'compress', and '' (no
* encoding)
*/
public function __construct($spec)
public function __construct($spec)
{
$this->_useMbStrlen = (function_exists('mb_strlen')
&& (ini_get('mbstring.func_overload') !== '')
@@ -99,8 +98,7 @@ class HTTP_Encoder {
$this->_headers['Content-Type'] = $spec['type'];
}
if (isset($spec['method'])
&& in_array($spec['method'], array('gzip', 'deflate', 'compress', '')))
{
&& in_array($spec['method'], array('gzip', 'deflate', 'compress', ''))) {
$this->_encodeMethod = array($spec['method'], $spec['method']);
} else {
$this->_encodeMethod = self::getAcceptedEncoding();
@@ -109,19 +107,19 @@ class HTTP_Encoder {
/**
* Get content in current form
*
*
* Call after encode() for encoded content.
*
*
* @return string
*/
public function getContent()
public function getContent()
{
return $this->_content;
}
/**
* Get array of output headers to be sent
*
*
* E.g.
* <code>
* array(
@@ -131,7 +129,7 @@ class HTTP_Encoder {
* )
* </code>
*
* @return array
* @return array
*/
public function getHeaders()
{
@@ -140,11 +138,11 @@ class HTTP_Encoder {
/**
* Send output headers
*
*
* You must call this before headers are sent and it probably cannot be
* used in conjunction with zlib output buffering / mod_gzip. Errors are
* not handled purposefully.
*
*
* @see getHeaders()
*/
public function sendHeaders()
@@ -153,10 +151,10 @@ class HTTP_Encoder {
header($name . ': ' . $val);
}
}
/**
* Send output headers and content
*
*
* A shortcut for sendHeaders() and echo getContent()
*
* You must call this before headers are sent and it probably cannot be
@@ -170,21 +168,21 @@ class HTTP_Encoder {
}
/**
* Determine the client's best encoding method from the HTTP Accept-Encoding
* Determine the client's best encoding method from the HTTP Accept-Encoding
* header.
*
*
* If no Accept-Encoding header is set, or the browser is IE before v6 SP2,
* this will return ('', ''), the "identity" encoding.
*
*
* A syntax-aware scan is done of the Accept-Encoding, so the method must
* be non 0. The methods are favored in order of gzip, deflate, then
* compress. Deflate is always smallest and generally faster, but is
* be non 0. The methods are favored in order of gzip, deflate, then
* compress. Deflate is always smallest and generally faster, but is
* rarely sent by servers, so client support could be buggier.
*
*
* @param bool $allowCompress allow the older compress encoding
*
*
* @param bool $allowDeflate allow the more recent deflate encoding
*
*
* @return array two values, 1st is the actual encoding method, 2nd is the
* alias of that method to use in the Content-Encoding header (some browsers
* call gzip "x-gzip" etc.)
@@ -192,10 +190,9 @@ class HTTP_Encoder {
public static function getAcceptedEncoding($allowCompress = true, $allowDeflate = true)
{
// @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
if (! isset($_SERVER['HTTP_ACCEPT_ENCODING'])
|| self::isBuggyIe())
{
|| self::isBuggyIe()) {
return array('', '');
}
$ae = $_SERVER['HTTP_ACCEPT_ENCODING'];
@@ -207,47 +204,52 @@ class HTTP_Encoder {
}
// gzip checks (slow)
if (preg_match(
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
,$ae
,$m)) {
'@(?:^|,)\\s*((?:x-)?gzip)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@',
$ae,
$m
)) {
return array('gzip', $m[1]);
}
if ($allowDeflate) {
// deflate checks
// deflate checks
$aeRev = strrev($ae);
if (0 === strpos($aeRev, 'etalfed ,') // ie, webkit
|| 0 === strpos($aeRev, 'etalfed,') // gecko
|| 0 === strpos($ae, 'deflate,') // opera
// slow parsing
|| preg_match(
'@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@', $ae)) {
'@(?:^|,)\\s*deflate\\s*(?:$|,|;\\s*q=(?:0\\.|1))@',
$ae
)) {
return array('deflate', 'deflate');
}
}
if ($allowCompress && preg_match(
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@'
,$ae
,$m)) {
'@(?:^|,)\\s*((?:x-)?compress)\\s*(?:$|,|;\\s*q=(?:0\\.|1))@',
$ae,
$m
)) {
return array('compress', $m[1]);
}
return array('', '');
}
/**
* Encode (compress) the content
*
*
* If the encode method is '' (none) or compression level is 0, or the 'zlib'
* extension isn't loaded, we return false.
*
*
* Then the appropriate gz_* function is called to compress the content. If
* this fails, false is returned.
*
* The header "Vary: Accept-Encoding" is added. If encoding is successful,
*
* The header "Vary: Accept-Encoding" is added. If encoding is successful,
* the Content-Length header is updated, and Content-Encoding is also added.
*
*
* @param int $compressionLevel given to zlib functions. If not given, the
* class default will be used.
*
*
* @return bool success true if the content was actually compressed
*/
public function encode($compressionLevel = null)
@@ -260,8 +262,7 @@ class HTTP_Encoder {
}
if ('' === $this->_encodeMethod[0]
|| ($compressionLevel == 0)
|| !extension_loaded('zlib'))
{
|| !extension_loaded('zlib')) {
return false;
}
if ($this->_encodeMethod[0] === 'deflate') {
@@ -279,19 +280,20 @@ class HTTP_Encoder {
: (string)strlen($encoded);
$this->_headers['Content-Encoding'] = $this->_encodeMethod[1];
$this->_content = $encoded;
return true;
}
/**
* Encode and send appropriate headers and content
*
* This is a convenience method for common use of the class
*
*
* @param string $content
*
*
* @param int $compressionLevel given to zlib functions. If not given, the
* class default will be used.
*
*
* @return bool success true if the content was actually compressed
*/
public static function output($content, $compressionLevel = null)
@@ -302,6 +304,7 @@ class HTTP_Encoder {
$he = new HTTP_Encoder(array('content' => $content));
$ret = $he->encode($compressionLevel);
$he->sendAll();
return $ret;
}
@@ -323,11 +326,12 @@ class HTTP_Encoder {
}
// no regex = faaast
$version = (float)substr($ua, 30);
return self::$encodeToIe6
? ($version < 6 || ($version == 6 && false === strpos($ua, 'SV1')))
: ($version < 7);
}
protected $_content = '';
protected $_headers = array();
protected $_encodeMethod = array('', '');

761
lib/Minify.php Normal file
View File

@@ -0,0 +1,761 @@
<?php
/**
* Class Minify
* @package Minify
*/
use Psr\Log\LoggerInterface;
/**
* Minify - Combines, minifies, and caches JavaScript and CSS files on demand.
*
* See README for usage instructions (for now).
*
* This library was inspired by {@link mailto:flashkot@mail.ru jscsscomp by Maxim Martynyuk}
* and by the article {@link http://www.hunlock.com/blogs/Supercharged_Javascript "Supercharged JavaScript" by Patrick Hunlock}.
*
* @package Minify
* @author Ryan Grove <ryan@wonko.com>
* @author Stephen Clay <steve@mrclay.org>
* @copyright 2008 Ryan Grove, Stephen Clay. All rights reserved.
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @link https://github.com/mrclay/minify
*/
class Minify
{
/**
* API version
*
* This is only bumped when API breaks are done and should follow the major version of the library
*
* @var int
*/
const VERSION = 3;
const TYPE_CSS = 'text/css';
const TYPE_HTML = 'text/html';
// there is some debate over the ideal JS Content-Type, but this is the
// Apache default and what Yahoo! uses..
const TYPE_JS = 'application/x-javascript';
const URL_DEBUG = 'https://github.com/mrclay/minify/blob/master/docs/Debugging.wiki.md';
/**
* Any Minify_Cache_* object or null (i.e. no server cache is used)
*
* @var Minify_CacheInterface
*/
private $cache;
/**
* Active controller for current request
*
* @var Minify_Controller_Base
*/
protected $controller;
/**
* @var Minify_Env
*/
protected $env;
/**
* @var Minify_SourceInterface[]
*/
protected $sources;
/**
* @var string
*/
protected $selectionId;
/**
* Options for current request
*
* @var array
*/
protected $options;
/**
* @var LoggerInterface|null
*/
protected $logger;
/**
* @param Minify_CacheInterface $cache
* @param LoggerInterface $logger
*/
public function __construct(Minify_CacheInterface $cache, ?LoggerInterface $logger = null)
{
$this->cache = $cache;
$this->logger = $logger;
}
/**
* Get default Minify options.
*
* @return array options for Minify
*/
public function getDefaultOptions()
{
return array(
'isPublic' => true,
'encodeOutput' => function_exists('gzdeflate'),
'encodeMethod' => null, // determine later
'encodeLevel' => 9,
'minifiers' => array(
Minify::TYPE_JS => array('JSMin\\JSMin', 'minify'),
Minify::TYPE_CSS => array('Minify_CSSmin', 'minify'),
Minify::TYPE_HTML => array('Minify_HTML', 'minify'),
),
'minifierOptions' => array(), // no minifier options
'contentTypeCharset' => 'utf-8',
'maxAge' => 1800, // 30 minutes
'rewriteCssUris' => true,
'bubbleCssImports' => false,
'quiet' => false, // serve() will send headers and output
'debug' => false,
'concatOnly' => false,
'invalidate' => false,
// if you override these, the response codes MUST be directly after
// the first space.
'badRequestHeader' => 'HTTP/1.0 400 Bad Request',
'errorHeader' => 'HTTP/1.0 500 Internal Server Error',
// callback function to see/modify content of all sources
'postprocessor' => null,
// file to require to load preprocessor
'postprocessorRequire' => null,
/**
* If this string is not empty AND the serve() option 'bubbleCssImports' is
* NOT set, then serve() will check CSS files for @import declarations that
* appear too late in the combined stylesheet. If found, serve() will prepend
* the output with this warning.
*/
'importWarning' => "/* See https://github.com/mrclay/minify/blob/master/docs/CommonProblems.wiki.md#imports-can-appear-in-invalid-locations-in-combined-css-files */\n"
);
}
/**
* Serve a request for a minified file.
*
* Here are the available options and defaults:
*
* 'isPublic' : send "public" instead of "private" in Cache-Control
* headers, allowing shared caches to cache the output. (default true)
*
* 'quiet' : set to true to have serve() return an array rather than sending
* any headers/output (default false)
*
* 'encodeOutput' : set to false to disable content encoding, and not send
* the Vary header (default true)
*
* 'encodeMethod' : generally you should let this be determined by
* HTTP_Encoder (leave null), but you can force a particular encoding
* to be returned, by setting this to 'gzip' or '' (no encoding)
*
* 'encodeLevel' : level of encoding compression (0 to 9, default 9)
*
* 'contentTypeCharset' : appended to the Content-Type header sent. Set to a falsey
* value to remove. (default 'utf-8')
*
* 'maxAge' : set this to the number of seconds the client should use its cache
* before revalidating with the server. This sets Cache-Control: max-age and the
* Expires header. Unlike the old 'setExpires' setting, this setting will NOT
* prevent conditional GETs. Note this has nothing to do with server-side caching.
*
* 'rewriteCssUris' : If true, serve() will automatically set the 'currentDir'
* minifier option to enable URI rewriting in CSS files (default true)
*
* 'bubbleCssImports' : If true, all @import declarations in combined CSS
* files will be move to the top. Note this may alter effective CSS values
* due to a change in order. (default false)
*
* 'debug' : set to true to minify all sources with the 'Lines' controller, which
* eases the debugging of combined files. This also prevents 304 responses.
* @see Minify_Lines::minify()
*
* 'concatOnly' : set to true to disable minification and simply concatenate the files.
* For JS, no minifier will be used. For CSS, only URI rewriting is still performed.
*
* 'minifiers' : to override Minify's default choice of minifier function for
* a particular content-type, specify your callback under the key of the
* content-type:
* <code>
* // call customCssMinifier($css) for all CSS minification
* $options['minifiers'][Minify::TYPE_CSS] = 'customCssMinifier';
*
* // don't minify Javascript at all
* $options['minifiers'][Minify::TYPE_JS] = 'Minify::nullMinifier';
* </code>
*
* 'minifierOptions' : to send options to the minifier function, specify your options
* under the key of the content-type. E.g. To send the CSS minifier an option:
* <code>
* // give CSS minifier array('optionName' => 'optionValue') as 2nd argument
* $options['minifierOptions'][Minify::TYPE_CSS]['optionName'] = 'optionValue';
* </code>
*
* 'contentType' : (optional) this is only needed if your file extension is not
* js/css/html. The given content-type will be sent regardless of source file
* extension, so this should not be used in a Groups config with other
* Javascript/CSS files.
*
* 'importWarning' : serve() will check CSS files for @import declarations that
* appear too late in the combined stylesheet. If found, serve() will prepend
* the output with this warning. To disable this, set this option to empty string.
*
* Any controller options are documented in that controller's createConfiguration() method.
*
* @param Minify_ControllerInterface $controller instance of subclass of Minify_Controller_Base
*
* @param array $options controller/serve options
*
* @return null|array if the 'quiet' option is set to true, an array
* with keys "success" (bool), "statusCode" (int), "content" (string), and
* "headers" (array).
*
* @throws Exception
*/
public function serve(Minify_ControllerInterface $controller, $options = array())
{
$this->env = $controller->getEnv();
$options = array_merge($this->getDefaultOptions(), $options);
$config = $controller->createConfiguration($options);
$this->sources = $config->getSources();
$this->selectionId = $config->getSelectionId();
$this->options = $this->analyzeSources($config->getOptions());
if (!$this->options['quiet'] && !headers_sent()) {
ini_set('zlib.output_compression', '0');
}
// check request validity
if (!$this->sources) {
// invalid request!
if (! $this->options['quiet']) {
$this->errorExit($this->options['badRequestHeader'], self::URL_DEBUG);
} else {
list(, $statusCode) = explode(' ', $this->options['badRequestHeader']);
return array(
'success' => false,
'statusCode' => (int)$statusCode,
'content' => '',
'headers' => array(),
);
}
}
$this->controller = $controller;
if ($this->options['debug']) {
$this->setupDebug();
$this->options['maxAge'] = 0;
}
// determine encoding
if ($this->options['encodeOutput']) {
$sendVary = true;
if ($this->options['encodeMethod'] !== null) {
// controller specifically requested this
$contentEncoding = $this->options['encodeMethod'];
} else {
// sniff request header
// depending on what the client accepts, $contentEncoding may be
// 'x-gzip' while our internal encodeMethod is 'gzip'. Calling
// getAcceptedEncoding(false, false) leaves out compress and deflate as options.
$list = HTTP_Encoder::getAcceptedEncoding(false, false);
list($this->options['encodeMethod'], $contentEncoding) = $list;
$sendVary = ! HTTP_Encoder::isBuggyIe();
}
} else {
$this->options['encodeMethod'] = ''; // identity (no encoding)
}
// check client cache
$cgOptions = array(
'lastModifiedTime' => $this->options['lastModifiedTime'],
'isPublic' => $this->options['isPublic'],
'encoding' => $this->options['encodeMethod'],
'invalidate' => $this->options['invalidate'],
);
if ($this->options['maxAge'] > 0) {
$cgOptions['maxAge'] = $this->options['maxAge'];
} elseif ($this->options['debug']) {
$cgOptions['invalidate'] = true;
}
$cg = new HTTP_ConditionalGet($cgOptions);
if ($cg->cacheIsValid) {
// client's cache is valid
if (! $this->options['quiet']) {
$cg->sendHeaders();
return;
}
return array(
'success' => true,
'statusCode' => 304,
'content' => '',
'headers' => $cg->getHeaders(),
);
}
// client will need output
$headers = $cg->getHeaders();
unset($cg);
if ($this->options['contentType'] === self::TYPE_CSS && $this->options['rewriteCssUris']) {
$this->setupUriRewrites();
}
if ($this->options['concatOnly']) {
$this->options['minifiers'][self::TYPE_JS] = false;
foreach ($this->sources as $key => $source) {
if ($this->options['contentType'] === self::TYPE_JS) {
$source->setMinifier('Minify::nullMinifier');
} elseif ($this->options['contentType'] === self::TYPE_CSS) {
$source->setMinifier(array('Minify_CSSmin', 'minify'));
$sourceOpts = $source->getMinifierOptions();
$sourceOpts['compress'] = false;
$source->setMinifierOptions($sourceOpts);
}
}
}
// check server cache
if (! $this->options['debug']) {
// using cache
// the goal is to use only the cache methods to sniff the length and
// output the content, as they do not require ever loading the file into
// memory.
$cacheId = $this->_getCacheId();
$fullCacheId = ($this->options['encodeMethod']) ? $cacheId . '.gz' : $cacheId;
// check cache for valid entry
$cacheIsReady = $this->cache->isValid($fullCacheId, $this->options['lastModifiedTime']);
if ($cacheIsReady) {
$cacheContentLength = $this->cache->getSize($fullCacheId);
} else {
// generate & cache content
try {
$content = $this->combineMinify();
} catch (Exception $e) {
$this->logger && $this->logger->critical($e->getMessage());
if (! $this->options['quiet']) {
$this->errorExit($this->options['errorHeader'], self::URL_DEBUG);
}
throw $e;
}
$this->cache->store($cacheId, $content);
if (function_exists('gzencode') && $this->options['encodeMethod']) {
$this->cache->store($cacheId . '.gz', gzencode($content, $this->options['encodeLevel']));
}
}
} else {
// no cache
$cacheIsReady = false;
try {
$content = $this->combineMinify();
} catch (Exception $e) {
$this->logger && $this->logger->critical($e->getMessage());
if (! $this->options['quiet']) {
$this->errorExit($this->options['errorHeader'], self::URL_DEBUG);
}
throw $e;
}
}
if (! $cacheIsReady && $this->options['encodeMethod']) {
// still need to encode
$content = gzencode($content, $this->options['encodeLevel']);
}
// add headers
if ($cacheIsReady) {
$headers['Content-Length'] = $cacheContentLength;
} else {
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
$headers['Content-Length'] = mb_strlen($content, '8bit');
} else {
$headers['Content-Length'] = strlen($content);
}
}
$headers['Content-Type'] = $this->options['contentType'];
if ($this->options['contentTypeCharset']) {
$headers['Content-Type'] .= '; charset=' . $this->options['contentTypeCharset'];
}
if ($this->options['encodeMethod'] !== '') {
$headers['Content-Encoding'] = $contentEncoding;
}
if ($this->options['encodeOutput'] && $sendVary) {
$headers['Vary'] = 'Accept-Encoding';
}
if (! $this->options['quiet']) {
// output headers & content
foreach ($headers as $name => $val) {
header($name . ': ' . $val);
}
if ($cacheIsReady) {
$this->cache->display($fullCacheId);
} else {
echo $content;
}
} else {
return array(
'success' => true,
'statusCode' => 200,
'content' => $cacheIsReady ? $this->cache->fetch($fullCacheId) : $content,
'headers' => $headers,
);
}
}
/**
* Return combined minified content for a set of sources
*
* No internal caching will be used and the content will not be HTTP encoded.
*
* @param array $sources array of filepaths and/or Minify_Source objects
*
* @param array $options (optional) array of options for serve.
*
* @return string
*/
public function combine($sources, $options = array())
{
$tmpCache = $this->cache;
$this->cache = new Minify_Cache_Null();
$env = new Minify_Env();
$sourceFactory = new Minify_Source_Factory($env, array(
'checkAllowDirs' => false,
), $this->cache);
$controller = new Minify_Controller_Files($env, $sourceFactory, $this->logger);
$options = array_merge($options, array(
'files' => (array)$sources,
'quiet' => true,
'encodeMethod' => '',
'lastModifiedTime' => 0,
));
$out = $this->serve($controller, $options);
$this->cache = $tmpCache;
return $out['content'];
}
/**
* Show an error page
*
* @param string $header Full header. E.g. 'HTTP/1.0 500 Internal Server Error'
* @param string $url URL to direct the user to
* @param string $msgHtml HTML message for the client
*
* @return void
* @internal This is not part of the public API and is subject to change
* @access private
*/
public function errorExit($header, $url = '', $msgHtml = '')
{
$url = htmlspecialchars($url);
list(, $h1) = explode(' ', $header, 2);
$h1 = htmlspecialchars($h1);
// FastCGI environments require 3rd arg to header() to be set
list(, $code) = explode(' ', $header, 3);
header($header, true, $code);
header('Content-Type: text/html; charset=utf-8');
echo "<h1>$h1</h1>";
if ($msgHtml) {
echo $msgHtml;
}
if ($url) {
echo "<p>Please see <a href='$url'>$url</a>.</p>";
}
exit;
}
/**
* Default minifier for .min or -min JS files.
*
* @param string $content
* @return string
*/
public static function nullMinifier($content)
{
if (isset($content[0]) && $content[0] === "\xef") {
$content = substr($content, 3);
}
$content = str_replace("\r\n", "\n", $content);
return trim($content);
}
/**
* Setup CSS sources for URI rewriting
*/
protected function setupUriRewrites()
{
foreach ($this->sources as $key => $source) {
$file = $this->env->normalizePath($source->getFilePath());
$minifyOptions = $source->getMinifierOptions();
if ($file
&& !isset($minifyOptions['currentDir'])
&& !isset($minifyOptions['prependRelativePath'])) {
$minifyOptions['currentDir'] = dirname($file);
$source->setMinifierOptions($minifyOptions);
}
}
}
/**
* Set up sources to use Minify_Lines
*/
protected function setupDebug()
{
foreach ($this->sources as $source) {
$source->setMinifier(array('Minify_Lines', 'minify'));
$id = $source->getId();
$source->setMinifierOptions(array(
'id' => (is_file($id) ? basename($id) : $id),
));
}
}
/**
* Combines sources and minifies the result.
*
* @return string
*
* @throws Exception
*/
protected function combineMinify()
{
$type = $this->options['contentType']; // ease readability
// when combining scripts, make sure all statements separated and
// trailing single line comment is terminated
$implodeSeparator = ($type === self::TYPE_JS) ? "\n;" : '';
// allow the user to pass a particular array of options to each
// minifier (designated by type). source objects may still override
// these
if (isset($this->options['minifierOptions'][$type])) {
$defaultOptions = $this->options['minifierOptions'][$type];
} else {
$defaultOptions = array();
}
// if minifier not set, default is no minification. source objects
// may still override this
if (isset($this->options['minifiers'][$type])) {
$defaultMinifier = $this->options['minifiers'][$type];
} else {
$defaultMinifier = false;
}
// process groups of sources with identical minifiers/options
$content = array();
$i = 0;
$l = count($this->sources);
$groupToProcessTogether = array();
$lastMinifier = null;
$lastOptions = null;
do {
// get next source
$source = null;
if ($i < $l) {
$source = $this->sources[$i];
$sourceContent = $source->getContent();
// allow the source to override our minifier and options
$minifier = $source->getMinifier();
if (!$minifier) {
$minifier = $defaultMinifier;
}
$options = array_merge($defaultOptions, $source->getMinifierOptions());
}
// do we need to process our group right now?
if ($i > 0 // yes, we have at least the first group populated
&& (
! $source // yes, we ran out of sources
|| $type === self::TYPE_CSS // yes, to process CSS individually (avoiding PCRE bugs/limits)
|| $minifier !== $lastMinifier // yes, minifier changed
|| $options !== $lastOptions // yes, options changed
)) {
// minify previous sources with last settings
$imploded = implode($implodeSeparator, $groupToProcessTogether);
$groupToProcessTogether = array();
if ($lastMinifier) {
try {
$content[] = call_user_func($lastMinifier, $imploded, $lastOptions);
} catch (Exception $e) {
throw new Exception("Exception in minifier: " . $e->getMessage());
}
} else {
$content[] = $imploded;
}
}
// add content to the group
if ($source) {
$groupToProcessTogether[] = $sourceContent;
$lastMinifier = $minifier;
$lastOptions = $options;
}
$i++;
} while ($source);
$content = implode($implodeSeparator, $content);
if ($type === self::TYPE_CSS && false !== strpos($content, '@import')) {
$content = $this->handleCssImports($content);
}
// do any post-processing (esp. for editing build URIs)
if ($this->options['postprocessorRequire']) {
require_once $this->options['postprocessorRequire'];
}
if ($this->options['postprocessor']) {
$content = call_user_func($this->options['postprocessor'], $content, $type);
}
return $content;
}
/**
* Make a unique cache id for for this request.
*
* Any settings that could affect output are taken into consideration
*
* @param string $prefix
*
* @return string
*/
protected function _getCacheId($prefix = 'minify')
{
$name = preg_replace('/[^a-zA-Z0-9\\.=_,]/', '', $this->selectionId);
$name = preg_replace('/\\.+/', '.', $name);
$name = substr($name, 0, 100 - 34 - strlen($prefix));
$md5 = md5(serialize(array(
Minify_SourceSet::getDigest($this->sources),
$this->options['minifiers'],
$this->options['minifierOptions'],
$this->options['postprocessor'],
$this->options['bubbleCssImports'],
Minify::VERSION,
)));
return "{$prefix}_{$name}_{$md5}";
}
/**
* Bubble CSS @imports to the top or prepend a warning if an import is detected not at the top.
*
* @param string $css
*
* @return string
*/
protected function handleCssImports($css)
{
if ($this->options['bubbleCssImports']) {
// bubble CSS imports
preg_match_all('/@import.*?;/', $css, $imports);
$css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css);
return $css;
}
if ('' === $this->options['importWarning']) {
return $css;
}
// remove comments so we don't mistake { in a comment as a block
$noCommentCss = preg_replace('@/\\*[\\s\\S]*?\\*/@', '', $css);
$lastImportPos = strrpos($noCommentCss, '@import');
$firstBlockPos = strpos($noCommentCss, '{');
if (false !== $lastImportPos
&& false !== $firstBlockPos
&& $firstBlockPos < $lastImportPos
) {
// { appears before @import : prepend warning
$css = $this->options['importWarning'] . $css;
}
return $css;
}
/**
* Analyze sources (if there are any) and set $options 'contentType'
* and 'lastModifiedTime' if they already aren't.
*
* @param array $options options for Minify
*
* @return array options for Minify
*/
protected function analyzeSources($options = array())
{
if (!$this->sources) {
return $options;
}
$type = null;
foreach ($this->sources as $source) {
$sourceType = $source->getContentType();
if (!empty($options['contentType'])) {
// just verify sources have null content type or match the options
if ($sourceType !== null && $sourceType !== $options['contentType']) {
$this->logger && $this->logger->warning("ContentType mismatch: '{$sourceType}' != '{$options['contentType']}'");
$this->sources = array();
return $options;
}
continue;
}
if ($type === null) {
$type = $sourceType;
} elseif ($sourceType !== $type) {
$this->logger && $this->logger->warning("ContentType mismatch: '{$sourceType}' != '{$type}'");
$this->sources = array();
return $options;
}
}
if (empty($options['contentType'])) {
if (null === $type) {
$type = 'text/plain';
}
$options['contentType'] = $type;
}
// last modified is needed for caching, even if setExpires is set
if (!isset($options['lastModifiedTime'])) {
$max = 0;
foreach ($this->sources as $source) {
$max = max($source->getLastModified(), $max);
}
$options['lastModifiedTime'] = $max;
}
return $options;
}
}

299
lib/Minify/App.php Normal file
View File

@@ -0,0 +1,299 @@
<?php
namespace Minify;
use Minify_Cache_File;
use Minify_CacheInterface;
use Minify_Controller_MinApp;
use Minify_ControllerInterface;
use Minify_DebugDetector;
use Minify_Env;
use Minify_Source_Factory;
use Props\Container;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Monolog;
use Minify;
/**
* @property Minify_CacheInterface $cache
* @property Config $config
* @property string $configPath
* @property Minify_ControllerInterface $controller
* @property string $dir
* @property string $docRoot
* @property Minify_Env $env
* @property Monolog\Handler\ErrorLogHandler $errorLogHandler
* @property array $groupsConfig
* @property string $groupsConfigPath
* @property LoggerInterface $logger
* @property \Minify $minify
* @property array $serveOptions
* @property Minify_Source_Factory $sourceFactory
* @property array $sourceFactoryOptions
*/
class App extends Container
{
/**
* Constructor
*
* @param string $dir Directory containing config files
*/
public function __construct($dir)
{
$that = $this;
$this->dir = rtrim($dir, '/\\');
$this->cache = function (App $app) use ($that) {
$config = $app->config;
if ($config->cachePath instanceof Minify_CacheInterface) {
return $config->cachePath;
}
if (!$config->cachePath || is_string($config->cachePath)) {
return new Minify_Cache_File($config->cachePath, $config->cacheFileLocking, $app->logger);
}
$type = $that->typeOf($config->cachePath);
throw new RuntimeException('$min_cachePath must be a path or implement Minify_CacheInterface.'
. " Given $type");
};
$this->config = function (App $app) {
$config = (require $app->configPath);
if ($config instanceof Minify\Config) {
return $config;
}
// copy from vars into properties
$config = new Minify\Config();
$propNames = array_keys(get_object_vars($config));
$prefixer = function ($name) {
return "min_$name";
};
$varNames = array_map($prefixer, $propNames);
$varDefined = get_defined_vars();
$varNames = array_filter($varNames, function ($name) use ($varDefined) {
return array_key_exists($name, $varDefined);
});
$vars = compact($varNames);
foreach ($varNames as $varName) {
if (isset($vars[$varName])) {
$config->{substr($varName, 4)} = $vars[$varName];
}
}
if ($config->documentRoot) {
// copy into env
if (empty($config->envArgs['server'])) {
$config->envArgs['server'] = $_SERVER;
}
$config->envArgs['server']['DOCUMENT_ROOT'] = $config->documentRoot;
}
return $config;
};
$this->configPath = "{$this->dir}/config.php";
$this->controller = function (App $app) use ($that) {
$config = $app->config;
if (empty($config->factories['controller'])) {
$ctrl = new Minify_Controller_MinApp($app->env, $app->sourceFactory, $app->logger);
} else {
$ctrl = call_user_func($config->factories['controller'], $app);
}
if ($ctrl instanceof Minify_ControllerInterface) {
return $ctrl;
}
$type = $that->typeOf($ctrl);
throw new RuntimeException('$min_factories["controller"] callable must return an implementation'
. " of Minify_CacheInterface. Returned $type");
};
$this->docRoot = function (App $app) {
$config = $app->config;
if (empty($config->documentRoot)) {
return $app->env->getDocRoot();
}
return $app->env->normalizePath($config->documentRoot);
};
$this->env = function (App $app) {
return new Minify_Env($app->config->envArgs);
};
$this->errorLogHandler = function (App $app) {
$format = "%channel%.%level_name%: %message% %context% %extra%";
$handler = new Monolog\Handler\ErrorLogHandler();
$handler->setFormatter(new Monolog\Formatter\LineFormatter($format));
return $handler;
};
$this->groupsConfig = function (App $app) {
return (require $app->groupsConfigPath);
};
$this->groupsConfigPath = "{$this->dir}/groupsConfig.php";
$this->logger = function (App $app) use ($that) {
$value = $app->config->errorLogger;
if ($value instanceof LoggerInterface) {
return $value;
}
$logger = new Monolog\Logger('minify');
if (!$value) {
return $logger;
}
if ($value === true || $value instanceof \FirePHP) {
$logger->pushHandler($app->errorLogHandler);
$logger->pushHandler(new Monolog\Handler\FirePHPHandler());
return $logger;
}
if ($value instanceof Monolog\Handler\HandlerInterface) {
$logger->pushHandler($value);
return $logger;
}
// BC
if (is_object($value) && is_callable(array($value, 'log'))) {
$handler = new Minify\Logger\LegacyHandler($value);
$logger->pushHandler($handler);
return $logger;
}
$type = $that->typeOf($value);
throw new RuntimeException('If set, $min_errorLogger must be a PSR-3 logger or a Monolog handler.'
. " Given $type");
};
$this->minify = function (App $app) use ($that) {
$config = $app->config;
if (empty($config->factories['minify'])) {
return new \Minify($app->cache, $app->logger);
}
$minify = call_user_func($config->factories['minify'], $app);
if ($minify instanceof \Minify) {
return $minify;
}
$type = $that->typeOf($minify);
throw new RuntimeException('$min_factories["minify"] callable must return a Minify object.'
. " Returned $type");
};
$this->serveOptions = function (App $app) {
$config = $app->config;
$env = $app->env;
$ret = $config->serveOptions;
$ret['minifierOptions']['text/css']['docRoot'] = $app->docRoot;
$ret['minifierOptions']['text/css']['symlinks'] = $config->symlinks;
$ret['minApp']['symlinks'] = $config->symlinks;
// auto-add targets to allowDirs
foreach ($config->symlinks as $uri => $target) {
$ret['minApp']['allowDirs'][] = $target;
}
if ($config->allowDebugFlag) {
$ret['debug'] = Minify_DebugDetector::shouldDebugRequest($env);
}
if ($config->concatOnly) {
$ret['concatOnly'] = true;
}
// check for URI versioning
if ($env->get('v') !== null || preg_match('/&\\d/', $app->env->server('QUERY_STRING') ?? '')) {
$ret['maxAge'] = 31536000;
}
// need groups config?
if ($env->get('g') !== null) {
$ret['minApp']['groups'] = $app->groupsConfig;
}
return $ret;
};
$this->sourceFactory = function (App $app) {
return new Minify_Source_Factory($app->env, $app->sourceFactoryOptions, $app->cache);
};
$this->sourceFactoryOptions = function (App $app) {
$serveOptions = $app->serveOptions;
$ret = array();
// translate legacy setting to option for source factory
if (isset($serveOptions['minApp']['noMinPattern'])) {
$ret['noMinPattern'] = $serveOptions['minApp']['noMinPattern'];
}
if (isset($serveOptions['minApp']['allowDirs'])) {
$ret['allowDirs'] = $serveOptions['minApp']['allowDirs'];
}
if (isset($serveOptions['checkAllowDirs'])) {
$ret['checkAllowDirs'] = $serveOptions['checkAllowDirs'];
}
if (is_numeric($app->config->uploaderHoursBehind)) {
$ret['uploaderHoursBehind'] = $app->config->uploaderHoursBehind;
}
return $ret;
};
}
public function runServer()
{
if (!$this->env->get('f') && $this->env->get('g') === null) {
// no spec given
$msg = '<p>No "f" or "g" parameters were detected.</p>';
$url = 'https://github.com/mrclay/minify/blob/master/docs/CommonProblems.wiki.md#long-url-parameters-are-ignored';
$defaults = $this->minify->getDefaultOptions();
$this->minify->errorExit($defaults['badRequestHeader'], $url, $msg);
}
$this->minify->serve($this->controller, $this->serveOptions);
}
/**
* @param mixed $var
* @return string
*/
private function typeOf($var)
{
$type = gettype($var);
return $type === 'object' ? get_class($var) : $type;
}
}

View File

@@ -1,48 +1,49 @@
<?php
/**
* Class Minify_Build
* Class Minify_Build
* @package Minify
*/
/**
* Maintain a single last modification time for a group of Minify sources to
* allow use of far off Expires headers in Minify.
*
*
* <code>
* // in config file
* $groupSources = array(
* 'js' => array('file1.js', 'file2.js')
* ,'css' => array('file1.css', 'file2.css', 'file3.css')
* )
*
*
* // during HTML generation
* $jsBuild = new Minify_Build($groupSources['js']);
* $cssBuild = new Minify_Build($groupSources['css']);
*
*
* $script = "<script type='text/javascript' src='"
* . $jsBuild->uri('/min.php/js') . "'></script>";
* $link = "<link rel='stylesheet' type='text/css' href='"
* . $cssBuild->uri('/min.php/css') . "'>";
*
*
* // in min.php
* Minify::serve('Groups', array(
* 'groups' => $groupSources
* ,'setExpires' => (time() + 86400 * 365)
* ));
* </code>
*
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_Build {
class Minify_Build
{
/**
* Last modification time of all files in the build
*
* @var int
*
* @var int
*/
public $lastModified = 0;
/**
* String to use as ampersand in uri(). Set this to '&' if
* you are not HTML-escaping URIs.
@@ -50,43 +51,42 @@ class Minify_Build {
* @var string
*/
public static $ampersand = '&amp;';
/**
* Get a time-stamped URI
*
*
* <code>
* echo $b->uri('/site.js');
* // outputs "/site.js?1678242"
*
*
* echo $b->uri('/scriptaculous.js?load=effects');
* // outputs "/scriptaculous.js?load=effects&amp1678242"
* </code>
*
* @param string $uri
* @param boolean $forceAmpersand (default = false) Force the use of ampersand to
* @param boolean $forceAmpersand (default = false) Force the use of ampersand to
* append the timestamp to the URI.
* @return string
*/
public function uri($uri, $forceAmpersand = false) {
$sep = ($forceAmpersand || strpos($uri, '?') !== false)
? self::$ampersand
: '?';
public function uri($uri, $forceAmpersand = false)
{
$sep = ($forceAmpersand || strpos($uri, '?') !== false) ? self::$ampersand : '?';
return "{$uri}{$sep}{$this->lastModified}";
}
/**
/**
* Create a build object
*
*
* @param array $sources array of Minify_Source objects and/or file paths
*
* @return null
*
*/
public function __construct($sources)
public function __construct($sources)
{
$max = 0;
foreach ((array)$sources as $source) {
if ($source instanceof Minify_Source) {
$max = max($max, $source->lastModified);
$max = max($max, $source->getLastModified());
} elseif (is_string($source)) {
if (0 === strpos($source, '//')) {
$source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);

View File

@@ -1,99 +1,98 @@
<?php
/**
* Class Minify_CSS
* @package Minify
*/
/**
* Minify CSS
*
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
* minify CSS and rewrite relative URIs.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
*/
class Minify_CSS {
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options available options:
*
* 'preserveComments': (default true) multi-line comments that begin
* with "/*!" will be preserved with newlines before and after to
* enhance readability.
*
* 'removeCharsets': (default true) remove all @charset at-rules
*
* 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files. For this to work, the files *must* exist and be
* visible by the PHP process.
*
* 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
*
* @return string
*/
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'preserveComments' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
if ($options['compress']) {
if (! $options['preserveComments']) {
$css = Minify_CSS_Compressor::process($css, $options);
} else {
$css = Minify_CommentPreserver::process(
$css
,array('Minify_CSS_Compressor', 'process')
,array($options)
);
}
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite(
$css
,$options['currentDir']
,$options['docRoot']
,$options['symlinks']
);
} else {
return Minify_CSS_UriRewriter::prepend(
$css
,$options['prependRelativePath']
);
}
}
}
<?php
/**
* Class Minify_CSS
* @package Minify
*/
/**
* Minify CSS
*
* This class uses Minify_CSS_Compressor and Minify_CSS_UriRewriter to
* minify CSS and rewrite relative URIs.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
*
* @deprecated Use Minify_CSSmin
*/
class Minify_CSS
{
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options available options:
*
* 'preserveComments': (default true) multi-line comments that begin
* with "/*!" will be preserved with newlines before and after to
* enhance readability.
*
* 'removeCharsets': (default true) remove all @charset at-rules
*
* 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files. For this to work, the files *must* exist and be
* visible by the PHP process.
*
* 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
*
* @return string
*/
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'preserveComments' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
if ($options['compress']) {
if (! $options['preserveComments']) {
$css = Minify_CSS_Compressor::process($css, $options);
} else {
$processor = array('Minify_CSS_Compressor', 'process');
$css = Minify_CommentPreserver::process($css, $processor, array($options));
}
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
if ($options['currentDir']) {
$currentDir = $options['currentDir'];
$docRoot = $options['docRoot'];
$symlinks = $options['symlinks'];
return Minify_CSS_UriRewriter::rewrite($css, $currentDir, $docRoot, $symlinks);
}
return Minify_CSS_UriRewriter::prepend($css, $options['prependRelativePath']);
}
}

View File

@@ -1,249 +1,275 @@
<?php
/**
* Class Minify_CSS_Compressor
* @package Minify
*/
/**
* Compress CSS
*
* This is a heavy regex-based removal of whitespace, unnecessary
* comments and tokens, and some CSS value minimization, where practical.
* Many steps have been taken to avoid breaking comment-based hacks,
* including the ie5/mac filter (and its inversion), but expect tricky
* hacks involving comment tokens in 'content' value strings to break
* minimization badly. A test suite is available.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
*/
class Minify_CSS_Compressor {
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options (currently ignored)
*
* @return string
*/
public static function process($css, $options = array())
{
$obj = new Minify_CSS_Compressor($options);
return $obj->_process($css);
}
/**
* @var array
*/
protected $_options = null;
/**
* Are we "in" a hack? I.e. are some browsers targetted until the next comment?
*
* @var bool
*/
protected $_inHack = false;
/**
* Constructor
*
* @param array $options (currently ignored)
*/
private function __construct($options) {
$this->_options = $options;
}
/**
* Minify a CSS string
*
* @param string $css
*
* @return string
*/
protected function _process($css)
{
$css = str_replace("\r\n", "\n", $css);
// preserve empty comment after '>'
// http://www.webdevout.net/css-hacks#in_css-selectors
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
// preserve empty comment between property and value
// http://css-discuss.incutio.com/?page=BoxModelHack
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
// apply callback to all valid comments (and strip out surrounding ws
$css = preg_replace_callback('@\\s*/\\*([\\s\\S]*?)\\*/\\s*@'
,array($this, '_commentCB'), $css);
// remove ws around { } and last semicolon in declaration block
$css = preg_replace('/\\s*{\\s*/', '{', $css);
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
// remove ws surrounding semicolons
$css = preg_replace('/\\s*;\\s*/', ';', $css);
// remove ws around urls
$css = preg_replace('/
url\\( # url(
\\s*
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
\\s*
\\) # )
/x', 'url($1)', $css);
// remove ws between rules and colons
$css = preg_replace('/
\\s*
([{;]) # 1 = beginning of block or rule separator
\\s*
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
\\s*
:
\\s*
(\\b|[#\'"-]) # 3 = first character of a value
/x', '$1$2:$3', $css);
// remove ws in selectors
$css = preg_replace_callback('/
(?: # non-capture
\\s*
[^~>+,\\s]+ # selector part
\\s*
[,>+~] # combinators
)+
\\s*
[^~>+,\\s]+ # selector part
{ # open declaration block
/x'
,array($this, '_selectorsCB'), $css);
// minimize hex colors
$css = preg_replace('/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i'
, '$1#$2$3$4$5', $css);
// remove spaces between font families
$css = preg_replace_callback('/font-family:([^;}]+)([;}])/'
,array($this, '_fontFamilyCB'), $css);
$css = preg_replace('/@import\\s+url/', '@import url', $css);
// replace any ws involving newlines with a single newline
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
// separate common descendent selectors w/ newlines (to limit line lengths)
$css = preg_replace('/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/', "$1\n$2{", $css);
// Use newline after 1st numeric value (to limit line lengths).
$css = preg_replace('/
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
\\s+
/x'
,"$1\n", $css);
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
return trim($css);
}
/**
* Replace what looks like a set of selectors
*
* @param array $m regex matches
*
* @return string
*/
protected function _selectorsCB($m)
{
// remove ws around the combinators
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
}
/**
* Process a comment and return a replacement
*
* @param array $m regex matches
*
* @return string
*/
protected function _commentCB($m)
{
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
$m = $m[1];
// $m is the comment content w/o the surrounding tokens,
// but the return value will replace the entire comment.
if ($m === 'keep') {
return '/**/';
}
if ($m === '" "') {
// component of http://tantek.com/CSS/Examples/midpass.html
return '/*" "*/';
}
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
// component of http://tantek.com/CSS/Examples/midpass.html
return '/*";}}/* */';
}
if ($this->_inHack) {
// inversion: feeding only to one browser
if (preg_match('@
^/ # comment started like /*/
\\s*
(\\S[\\s\\S]+?) # has at least some non-ws content
\\s*
/\\* # ends like /*/ or /**/
@x', $m, $n)) {
// end hack mode after this comment, but preserve the hack and comment content
$this->_inHack = false;
return "/*/{$n[1]}/**/";
}
}
if (substr($m, -1) === '\\') { // comment ends like \*/
// begin hack mode and preserve hack
$this->_inHack = true;
return '/*\\*/';
}
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
// begin hack mode and preserve hack
$this->_inHack = true;
return '/*/*/';
}
if ($this->_inHack) {
// a regular comment ends hack mode but should be preserved
$this->_inHack = false;
return '/**/';
}
// Issue 107: if there's any surrounding whitespace, it may be important, so
// replace the comment with a single space
return $hasSurroundingWs // remove all other comments
? ' '
: '';
}
/**
* Process a font-family listing and return a replacement
*
* @param array $m regex matches
*
* @return string
*/
protected function _fontFamilyCB($m)
{
// Issue 210: must not eliminate WS between words in unquoted families
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
$out = 'font-family:';
while (null !== ($piece = array_shift($pieces))) {
if ($piece[0] !== '"' && $piece[0] !== "'") {
$piece = preg_replace('/\\s+/', ' ', $piece);
$piece = preg_replace('/\\s?,\\s?/', ',', $piece);
}
$out .= $piece;
}
return $out . $m[2];
}
}
<?php
/**
* Class Minify_CSS_Compressor
* @package Minify
*/
/**
* Compress CSS
*
* This is a heavy regex-based removal of whitespace, unnecessary
* comments and tokens, and some CSS value minimization, where practical.
* Many steps have been taken to avoid breaking comment-based hacks,
* including the ie5/mac filter (and its inversion), but expect tricky
* hacks involving comment tokens in 'content' value strings to break
* minimization badly. A test suite is available.
*
* Note: This replaces a lot of spaces with line breaks. It's rumored
* (https://github.com/yui/yuicompressor/blob/master/README.md#global-options)
* that some source control tools and old browsers don't like very long lines.
* Compressed files with shorter lines are also easier to diff. If this is
* unacceptable please use CSSmin instead.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author http://code.google.com/u/1stvamp/ (Issue 64 patch)
*
* @deprecated Use CSSmin (tubalmartin/cssmin)
*/
class Minify_CSS_Compressor
{
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options (currently ignored)
*
* @return string
*/
public static function process($css, $options = array())
{
$obj = new Minify_CSS_Compressor($options);
return $obj->_process($css);
}
/**
* @var array
*/
protected $_options = null;
/**
* Are we "in" a hack? I.e. are some browsers targetted until the next comment?
*
* @var bool
*/
protected $_inHack = false;
/**
* Constructor
*
* @param array $options (currently ignored)
*/
private function __construct($options)
{
$this->_options = $options;
}
/**
* Minify a CSS string
*
* @param string $css
*
* @return string
*/
protected function _process($css)
{
$css = str_replace("\r\n", "\n", $css);
// preserve empty comment after '>'
// http://www.webdevout.net/css-hacks#in_css-selectors
$css = preg_replace('@>/\\*\\s*\\*/@', '>/*keep*/', $css);
// preserve empty comment between property and value
// http://css-discuss.incutio.com/?page=BoxModelHack
$css = preg_replace('@/\\*\\s*\\*/\\s*:@', '/*keep*/:', $css);
$css = preg_replace('@:\\s*/\\*\\s*\\*/@', ':/*keep*/', $css);
// apply callback to all valid comments (and strip out surrounding ws
$pattern = '@\\s*/\\*([\\s\\S]*?)\\*/\\s*@';
$css = preg_replace_callback($pattern, array($this, '_commentCB'), $css);
// remove ws around { } and last semicolon in declaration block
$css = preg_replace('/\\s*{\\s*/', '{', $css);
$css = preg_replace('/;?\\s*}\\s*/', '}', $css);
// remove ws surrounding semicolons
$css = preg_replace('/\\s*;\\s*/', ';', $css);
// remove ws around urls
$pattern = '/
url\\( # url(
\\s*
([^\\)]+?) # 1 = the URL (really just a bunch of non right parenthesis)
\\s*
\\) # )
/x';
$css = preg_replace($pattern, 'url($1)', $css);
// remove ws between rules and colons
$pattern = '/
\\s*
([{;]) # 1 = beginning of block or rule separator
\\s*
([\\*_]?[\\w\\-]+) # 2 = property (and maybe IE filter)
\\s*
:
\\s*
(\\b|[#\'"-]) # 3 = first character of a value
/x';
$css = preg_replace($pattern, '$1$2:$3', $css);
// remove ws in selectors
$pattern = '/
(?: # non-capture
\\s*
[^~>+,\\s]+ # selector part
\\s*
[,>+~] # combinators
)+
\\s*
[^~>+,\\s]+ # selector part
{ # open declaration block
/x';
$css = preg_replace_callback($pattern, array($this, '_selectorsCB'), $css);
// minimize hex colors
$pattern = '/([^=])#([a-f\\d])\\2([a-f\\d])\\3([a-f\\d])\\4([\\s;\\}])/i';
$css = preg_replace($pattern, '$1#$2$3$4$5', $css);
// remove spaces between font families
$pattern = '/font-family:([^;}]+)([;}])/';
$css = preg_replace_callback($pattern, array($this, '_fontFamilyCB'), $css);
$css = preg_replace('/@import\\s+url/', '@import url', $css);
// replace any ws involving newlines with a single newline
$css = preg_replace('/[ \\t]*\\n+\\s*/', "\n", $css);
// separate common descendent selectors w/ newlines (to limit line lengths)
$pattern = '/([\\w#\\.\\*]+)\\s+([\\w#\\.\\*]+){/';
$css = preg_replace($pattern, "$1\n$2{", $css);
// Use newline after 1st numeric value (to limit line lengths).
$pattern = '/
((?:padding|margin|border|outline):\\d+(?:px|em)?) # 1 = prop : 1st numeric value
\\s+
/x';
$css = preg_replace($pattern, "$1\n", $css);
// prevent triggering IE6 bug: http://www.crankygeek.com/ie6pebug/
$css = preg_replace('/:first-l(etter|ine)\\{/', ':first-l$1 {', $css);
return trim($css);
}
/**
* Replace what looks like a set of selectors
*
* @param array $m regex matches
*
* @return string
*/
protected function _selectorsCB($m)
{
// remove ws around the combinators
return preg_replace('/\\s*([,>+~])\\s*/', '$1', $m[0]);
}
/**
* Process a comment and return a replacement
*
* @param array $m regex matches
*
* @return string
*/
protected function _commentCB($m)
{
$hasSurroundingWs = (trim($m[0]) !== $m[1]);
$m = $m[1];
// $m is the comment content w/o the surrounding tokens,
// but the return value will replace the entire comment.
if ($m === 'keep') {
return '/**/';
}
if ($m === '" "') {
// component of http://tantek.com/CSS/Examples/midpass.html
return '/*" "*/';
}
if (preg_match('@";\\}\\s*\\}/\\*\\s+@', $m)) {
// component of http://tantek.com/CSS/Examples/midpass.html
return '/*";}}/* */';
}
if ($this->_inHack) {
// inversion: feeding only to one browser
$pattern = '@
^/ # comment started like /*/
\\s*
(\\S[\\s\\S]+?) # has at least some non-ws content
\\s*
/\\* # ends like /*/ or /**/
@x';
if (preg_match($pattern, $m, $n)) {
// end hack mode after this comment, but preserve the hack and comment content
$this->_inHack = false;
return "/*/{$n[1]}/**/";
}
}
if (substr($m, -1) === '\\') { // comment ends like \*/
// begin hack mode and preserve hack
$this->_inHack = true;
return '/*\\*/';
}
if ($m !== '' && $m[0] === '/') { // comment looks like /*/ foo */
// begin hack mode and preserve hack
$this->_inHack = true;
return '/*/*/';
}
if ($this->_inHack) {
// a regular comment ends hack mode but should be preserved
$this->_inHack = false;
return '/**/';
}
// Issue 107: if there's any surrounding whitespace, it may be important, so
// replace the comment with a single space
return $hasSurroundingWs ? ' ' : ''; // remove all other comments
}
/**
* Process a font-family listing and return a replacement
*
* @param array $m regex matches
*
* @return string
*/
protected function _fontFamilyCB($m)
{
// Issue 210: must not eliminate WS between words in unquoted families
$flags = PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY;
$pieces = preg_split('/(\'[^\']+\'|"[^"]+")/', $m[1], -1, $flags);
$out = 'font-family:';
while (null !== ($piece = array_shift($pieces))) {
if ($piece[0] !== '"' && $piece[0] !== "'") {
$piece = preg_replace('/\\s+/', ' ', $piece);
$piece = preg_replace('/\\s?,\\s?/', ',', $piece);
}
$out .= $piece;
}
return $out . $m[2];
}
}

View File

@@ -1,6 +1,6 @@
<?php
/**
* Class Minify_CSS_UriRewriter
* Class Minify_CSS_UriRewriter
* @package Minify
*/
@@ -10,70 +10,71 @@
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_CSS_UriRewriter {
class Minify_CSS_UriRewriter
{
/**
* rewrite() and rewriteRelative() append debugging information here
*
* @var string
*/
public static $debugText = '';
/**
* In CSS content, rewrite file relative URIs as root relative
*
*
* @param string $css
*
*
* @param string $currentDir The directory of the current CSS file.
*
* @param string $docRoot The document root of the web site in which
*
* @param string $docRoot The document root of the web site in which
* the CSS file resides (default = $_SERVER['DOCUMENT_ROOT']).
*
* @param array $symlinks (default = array()) If the CSS file is stored in
*
* @param array $symlinks (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
*
* @return string
*/
public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
public static function rewrite($css, $currentDir, $docRoot = null, $symlinks = array())
{
self::$_docRoot = self::_realpath(
$docRoot ? $docRoot : $_SERVER['DOCUMENT_ROOT']
);
self::$_currentDir = self::_realpath($currentDir);
self::$_symlinks = array();
// normalize symlinks
// normalize symlinks in order to map to link
foreach ($symlinks as $link => $target) {
$link = ($link === '//')
? self::$_docRoot
: str_replace('//', self::$_docRoot . '/', $link);
$link = ($link === '//') ? self::$_docRoot : str_replace('//', self::$_docRoot . '/', $link);
$link = strtr($link, '/', DIRECTORY_SEPARATOR);
self::$_symlinks[$link] = self::_realpath($target);
}
self::$debugText .= "docRoot : " . self::$_docRoot . "\n"
. "currentDir : " . self::$_currentDir . "\n";
if (self::$_symlinks) {
self::$debugText .= "symlinks : " . var_export(self::$_symlinks, 1) . "\n";
}
self::$debugText .= "\n";
$css = self::_trimUrls($css);
$css = self::_owlifySvgPaths($css);
// rewrite
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css);
$pattern = '/@import\\s+([\'"])(.*?)[\'"]/';
$css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$pattern = '/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/';
$css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$css = self::_unOwlify($css);
@@ -82,33 +83,35 @@ class Minify_CSS_UriRewriter {
/**
* In CSS content, prepend a path to relative URIs
*
*
* @param string $css
*
*
* @param string $path The path to prepend.
*
*
* @return string
*/
public static function prepend($css, $path)
{
self::$_prependPath = $path;
$css = self::_trimUrls($css);
$css = self::_owlifySvgPaths($css);
// append
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
,array(self::$className, '_processUriCB'), $css);
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
,array(self::$className, '_processUriCB'), $css);
$pattern = '/@import\\s+([\'"])(.*?)[\'"]/';
$css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$pattern = '/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/';
$css = preg_replace_callback($pattern, array(self::$className, '_processUriCB'), $css);
$css = self::_unOwlify($css);
self::$_prependPath = null;
return $css;
}
/**
* Get a root relative URI from a file relative URI
*
@@ -119,7 +122,7 @@ class Minify_CSS_UriRewriter {
* , '/home/user/www' // doc root
* );
* // returns '/img/hello.gif'
*
*
* // example where static files are stored in a symlinked directory
* Minify_CSS_UriRewriter::rewriteRelative(
* 'hello.gif'
@@ -129,55 +132,55 @@ class Minify_CSS_UriRewriter {
* );
* // returns '/static/theme/hello.gif'
* </code>
*
*
* @param string $uri file relative URI
*
*
* @param string $realCurrentDir realpath of the current file's directory.
*
*
* @param string $realDocRoot realpath of the site document root.
*
* @param array $symlinks (default = array()) If the file is stored in
*
* @param array $symlinks (default = array()) If the file is stored in
* a symlink-ed directory, provide an array of link paths to
* real target paths, where the link paths "appear" to be within the document
* real target paths, where the link paths "appear" to be within the document
* root. E.g.:
* <code>
* array('/home/foo/www/not/real/path' => '/real/target/path') // unix
* array('C:\\htdocs\\not\\real' => 'D:\\real\\target\\path') // Windows
* </code>
*
*
* @return string
*/
public static function rewriteRelative($uri, $realCurrentDir, $realDocRoot, $symlinks = array())
{
// prepend path with current dir separator (OS-independent)
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR)
. DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
$path = strtr($realCurrentDir, '/', DIRECTORY_SEPARATOR);
$path .= DIRECTORY_SEPARATOR . strtr($uri, '/', DIRECTORY_SEPARATOR);
self::$debugText .= "file-relative URI : {$uri}\n"
. "path prepended : {$path}\n";
// "unresolve" a symlink back to doc root
foreach ($symlinks as $link => $target) {
if (0 === strpos($path, $target)) {
// replace $target with $link
$path = $link . substr($path, strlen($target));
self::$debugText .= "symlink unresolved : {$path}\n";
break;
}
}
// strip doc root
$path = substr($path, strlen($realDocRoot));
self::$debugText .= "docroot stripped : {$path}\n";
// fix to root-relative URI
$uri = strtr($path, '/\\', '//');
$uri = self::removeDots($uri);
self::$debugText .= "traversals removed : {$uri}\n\n";
return $uri;
}
@@ -195,9 +198,10 @@ class Minify_CSS_UriRewriter {
do {
$uri = preg_replace('@/[^/]+/\\.\\./@', '/', $uri, 1, $changed);
} while ($changed);
return $uri;
}
/**
* Defines which class to call as part of callbacks, change this
* if you extend Minify_CSS_UriRewriter
@@ -209,9 +213,9 @@ class Minify_CSS_UriRewriter {
/**
* Get realpath with any trailing slash removed. If realpath() fails,
* just remove the trailing slash.
*
*
* @param string $path
*
*
* @return mixed path with no trailing slash
*/
protected static function _realpath($path)
@@ -220,6 +224,7 @@ class Minify_CSS_UriRewriter {
if ($realPath !== false) {
$path = $realPath;
}
return rtrim($path, '/\\');
}
@@ -259,13 +264,15 @@ class Minify_CSS_UriRewriter {
*/
private static function _trimUrls($css)
{
return preg_replace('/
$pattern = '/
url\\( # url(
\\s*
([^\\)]+?) # 1 = URI (assuming does not contain ")")
\\s*
\\) # )
/x', 'url($1)', $css);
/x';
return preg_replace($pattern, 'url($1)', $css);
}
/**
@@ -283,12 +290,9 @@ class Minify_CSS_UriRewriter {
$uri = $m[2];
} else {
// $m[1] is either quoted or not
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"')
? $m[1][0]
: '';
$uri = ($quoteChar === '')
? $m[1]
: substr($m[1], 1, strlen($m[1]) - 2);
$quoteChar = ($m[1][0] === "'" || $m[1][0] === '"') ? $m[1][0] : '';
$uri = ($quoteChar === '') ? $m[1] : substr($m[1], 1, strlen($m[1]) - 2);
}
if ($uri === '') {
@@ -313,9 +317,12 @@ class Minify_CSS_UriRewriter {
}
}
}
return $isImport
? "@import {$quoteChar}{$uri}{$quoteChar}"
: "url({$quoteChar}{$uri}{$quoteChar})";
if ($isImport) {
return "@import {$quoteChar}{$uri}{$quoteChar}";
} else {
return "url({$quoteChar}{$uri}{$quoteChar})";
}
}
/**
@@ -327,9 +334,11 @@ class Minify_CSS_UriRewriter {
* @param string $css
* @return string
*/
private static function _owlifySvgPaths($css) {
return preg_replace('~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)url(\(\s*#\w+\s*\))~', '$1owl$2', $css);
private static function _owlifySvgPaths($css)
{
$pattern = '~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)url(\(\s*#\w+\s*\))~';
return preg_replace($pattern, '$1owl$2', $css);
}
/**
@@ -340,7 +349,10 @@ class Minify_CSS_UriRewriter {
* @param string $css
* @return string
*/
private static function _unOwlify($css) {
return preg_replace('~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)owl~', '$1url', $css);
private static function _unOwlify($css)
{
$pattern = '~\b((?:clip-path|mask|-webkit-mask)\s*\:\s*)owl~';
return preg_replace($pattern, '$1url', $css);
}
}

View File

@@ -1,85 +1,88 @@
<?php
/**
* Class Minify_CSSmin
* @package Minify
*/
/**
* Wrapper for CSSmin
*
* This class uses CSSmin and Minify_CSS_UriRewriter to minify CSS and rewrite relative URIs.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_CSSmin {
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options available options:
*
* 'removeCharsets': (default true) remove all @charset at-rules
*
* 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files. For this to work, the files *must* exist and be
* visible by the PHP process.
*
* 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
*
* @return string
*/
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
if ($options['compress']) {
$obj = new CSSmin();
$css = $obj->run($css);
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite(
$css
,$options['currentDir']
,$options['docRoot']
,$options['symlinks']
);
} else {
return Minify_CSS_UriRewriter::prepend(
$css
,$options['prependRelativePath']
);
}
}
}
<?php
/**
* Class Minify_CSSmin
* @package Minify
*/
use tubalmartin\CssMin\Minifier as CSSmin;
/**
* Wrapper for CSSmin
*
* This class uses CSSmin and Minify_CSS_UriRewriter to minify CSS and rewrite relative URIs.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_CSSmin
{
/**
* Minify a CSS string
*
* @param string $css
*
* @param array $options available options:
*
* 'removeCharsets': (default true) remove all @charset at-rules
*
* 'prependRelativePath': (default null) if given, this string will be
* prepended to all relative URIs in import/url declarations
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files. For this to work, the files *must* exist and be
* visible by the PHP process.
*
* 'symlinks': (default = array()) If the CSS file is stored in
* a symlink-ed directory, provide an array of link paths to
* target paths, where the link paths are within the document root. Because
* paths need to be normalized for this to work, use "//" to substitute
* the doc root in the link paths (the array keys). E.g.:
* <code>
* array('//symlink' => '/real/target/path') // unix
* array('//static' => 'D:\\staticStorage') // Windows
* </code>
*
* 'docRoot': (default = $_SERVER['DOCUMENT_ROOT'])
* see Minify_CSS_UriRewriter::rewrite
*
* @return string
*/
public static function minify($css, $options = array())
{
$options = array_merge(array(
'compress' => true,
'removeCharsets' => true,
'currentDir' => null,
'docRoot' => $_SERVER['DOCUMENT_ROOT'],
'prependRelativePath' => null,
'symlinks' => array(),
), $options);
if ($options['removeCharsets']) {
$css = preg_replace('/@charset[^;]+;\\s*/', '', $css);
}
if ($options['compress']) {
$obj = new CSSmin();
$css = $obj->run($css);
}
if (! $options['currentDir'] && ! $options['prependRelativePath']) {
return $css;
}
if ($options['currentDir']) {
return Minify_CSS_UriRewriter::rewrite(
$css,
$options['currentDir'],
$options['docRoot'],
$options['symlinks']
);
}
return Minify_CSS_UriRewriter::prepend(
$css,
$options['prependRelativePath']
);
}
}

View File

@@ -6,15 +6,18 @@
/**
* APC-based cache class for Minify
*
*
* <code>
* Minify::setCache(new Minify_Cache_APC());
* </code>
*
*
* @package Minify
* @author Chris Edwards
*
* @deprecated Use Minify_Cache_APCu
**/
class Minify_Cache_APC {
class Minify_Cache_APC implements Minify_CacheInterface
{
/**
* Create a Minify_Cache_APC object, to be passed to
@@ -57,9 +60,12 @@ class Minify_Cache_APC {
if (! $this->_fetch($id)) {
return false;
}
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($this->_data, '8bit')
: strlen($this->_data);
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
return mb_strlen($this->_data, '8bit');
} else {
return strlen($this->_data);
}
}
/**
@@ -83,9 +89,7 @@ class Minify_Cache_APC {
*/
public function display($id)
{
echo $this->_fetch($id)
? $this->_data
: '';
echo $this->_fetch($id) ? $this->_data : '';
}
/**
@@ -97,9 +101,7 @@ class Minify_Cache_APC {
*/
public function fetch($id)
{
return $this->_fetch($id)
? $this->_data
: '';
return $this->_fetch($id) ? $this->_data : '';
}
private $_exp = null;
@@ -124,10 +126,13 @@ class Minify_Cache_APC {
$ret = apc_fetch($id);
if (false === $ret) {
$this->_id = null;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}

136
lib/Minify/Cache/APCu.php Normal file
View File

@@ -0,0 +1,136 @@
<?php
/**
* Class Minify_Cache_APCu
* @package Minify
*/
/**
* APCu-based cache class for Minify
*
* <code>
* Minify::setCache(new Minify_Cache_APCu());
* </code>
*
* @package Minify
* @author Chris Edwards
**/
class Minify_Cache_APCu implements Minify_CacheInterface
{
/**
* Create a Minify_Cache_APCu object, to be passed to
* Minify::setCache().
*
*
* @param int $expire seconds until expiration (default = 0
* meaning the item will not get an expiration date)
*
* @return null
*/
public function __construct($expire = 0)
{
$this->_exp = $expire;
}
/**
* Write data to cache.
*
* @param string $id cache id
*
* @param string $data
*
* @return bool success
*/
public function store($id, $data)
{
return apcu_store($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
}
/**
* Get the size of a cache entry
*
* @param string $id cache id
*
* @return int size in bytes
*/
public function getSize($id)
{
if (! $this->_fetch($id)) {
return false;
}
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
return mb_strlen($this->_data, '8bit');
} else {
return strlen($this->_data);
}
}
/**
* Does a valid cache entry exist?
*
* @param string $id cache id
*
* @param int $srcMtime mtime of the original source file(s)
*
* @return bool exists
*/
public function isValid($id, $srcMtime)
{
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
}
/**
* Send the cached content to output
*
* @param string $id cache id
*/
public function display($id)
{
echo $this->_fetch($id) ? $this->_data : '';
}
/**
* Fetch the cached content
*
* @param string $id cache id
*
* @return string
*/
public function fetch($id)
{
return $this->_fetch($id) ? $this->_data : '';
}
private $_exp = null;
// cache of most recently fetched id
private $_lm = null;
private $_data = null;
private $_id = null;
/**
* Fetch data and timestamp from apcu, store in instance
*
* @param string $id
*
* @return bool success
*/
private function _fetch($id)
{
if ($this->_id === $id) {
return true;
}
$ret = apcu_fetch($id);
if (false === $ret) {
$this->_id = null;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}

183
lib/Minify/Cache/File.php Normal file
View File

@@ -0,0 +1,183 @@
<?php
/**
* Class Minify_Cache_File
* @package Minify
*/
use Psr\Log\LoggerInterface;
class Minify_Cache_File implements Minify_CacheInterface
{
/**
* @var string
*/
private $path;
/**
* @var bool
*/
private $locking;
/**
* @var LoggerInterface
*/
private $logger;
/**
* @param string $path
* @param bool $fileLocking
* @param LoggerInterface $logger
*/
public function __construct($path = '', $fileLocking = false, ?LoggerInterface $logger = null)
{
if (! $path) {
$path = sys_get_temp_dir();
}
$this->locking = $fileLocking;
$this->path = $path;
if (!$logger) {
$logger = new \Monolog\Logger('minify');
}
$this->logger = $logger;
}
/**
* Write data to cache.
*
* @param string $id cache id (e.g. a filename)
*
* @param string $data
*
* @return bool success
*/
public function store($id, $data)
{
$flag = $this->locking ? LOCK_EX : null;
$file = $this->path . '/' . $id;
if (! @file_put_contents($file, $data, $flag)) {
$this->logger->warning("Minify_Cache_File: Write failed to '$file'");
}
// write control
if ($data !== $this->fetch($id)) {
@unlink($file);
$this->logger->warning("Minify_Cache_File: Post-write read failed for '$file'");
return false;
}
return true;
}
/**
* Get the size of a cache entry
*
* @param string $id cache id (e.g. a filename)
*
* @return int size in bytes
*/
public function getSize($id)
{
return filesize($this->path . '/' . $id);
}
/**
* Does a valid cache entry exist?
*
* @param string $id cache id (e.g. a filename)
*
* @param int $srcMtime mtime of the original source file(s)
*
* @return bool exists
*/
public function isValid($id, $srcMtime)
{
$file = $this->path . '/' . $id;
return (is_file($file) && (filemtime($file) >= $srcMtime));
}
/**
* Send the cached content to output
*
* @param string $id cache id (e.g. a filename)
*/
public function display($id)
{
if (!$this->locking) {
readfile($this->path . '/' . $id);
return;
}
$fp = fopen($this->path . '/' . $id, 'rb');
flock($fp, LOCK_SH);
fpassthru($fp);
flock($fp, LOCK_UN);
fclose($fp);
}
/**
* Fetch the cached content
*
* @param string $id cache id (e.g. a filename)
*
* @return string
*/
public function fetch($id)
{
if (!$this->locking) {
return file_get_contents($this->path . '/' . $id);
}
$fp = fopen($this->path . '/' . $id, 'rb');
if (!$fp) {
return false;
}
flock($fp, LOCK_SH);
$ret = stream_get_contents($fp);
flock($fp, LOCK_UN);
fclose($fp);
return $ret;
}
/**
* Fetch the cache path used
*
* @return string
*/
public function getPath()
{
return $this->path;
}
/**
* Get a usable temp directory
*
* @return string
* @deprecated
*/
public static function tmp()
{
trigger_error(__METHOD__ . ' is deprecated in Minfy 3.0', E_USER_DEPRECATED);
return sys_get_temp_dir();
}
/**
* Send message to the Minify logger
* @param string $msg
* @return null
* @deprecated Use $this->logger
*/
protected function _log($msg)
{
trigger_error(__METHOD__ . ' is deprecated in Minify 3.0.', E_USER_DEPRECATED);
$this->logger->warning($msg);
}
}

View File

@@ -6,7 +6,7 @@
/**
* Memcache-based cache class for Minify
*
*
* <code>
* // fall back to disk caching if memcache can't connect
* $memcache = new Memcache;
@@ -17,45 +17,43 @@
* }
* </code>
**/
class Minify_Cache_Memcache {
class Minify_Cache_Memcache implements Minify_CacheInterface
{
/**
* Create a Minify_Cache_Memcache object, to be passed to
* Create a Minify_Cache_Memcache object, to be passed to
* Minify::setCache().
*
* @param Memcache $memcache already-connected instance
*
*
* @param int $expire seconds until expiration (default = 0
* meaning the item will not get an expiration date)
*
* @return null
*/
public function __construct($memcache, $expire = 0)
{
$this->_mc = $memcache;
$this->_exp = $expire;
}
/**
* Write data to cache.
*
* @param string $id cache id
*
*
* @param string $data
*
*
* @return bool success
*/
public function store($id, $data)
{
return $this->_mc->set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", 0, $this->_exp);
}
/**
* Get the size of a cache entry
*
* @param string $id cache id
*
*
* @return int size in bytes
*/
public function getSize($id)
@@ -63,25 +61,28 @@ class Minify_Cache_Memcache {
if (! $this->_fetch($id)) {
return false;
}
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($this->_data, '8bit')
: strlen($this->_data);
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
return mb_strlen($this->_data, '8bit');
} else {
return strlen($this->_data);
}
}
/**
* Does a valid cache entry exist?
*
* @param string $id cache id
*
*
* @param int $srcMtime mtime of the original source file(s)
*
*
* @return bool exists
*/
public function isValid($id, $srcMtime)
{
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
}
/**
* Send the cached content to output
*
@@ -89,38 +90,34 @@ class Minify_Cache_Memcache {
*/
public function display($id)
{
echo $this->_fetch($id)
? $this->_data
: '';
echo $this->_fetch($id) ? $this->_data : '';
}
/**
/**
* Fetch the cached content
*
* @param string $id cache id
*
*
* @return string
*/
public function fetch($id)
{
return $this->_fetch($id)
? $this->_data
: '';
return $this->_fetch($id) ? $this->_data : '';
}
private $_mc = null;
private $_exp = null;
// cache of most recently fetched id
private $_lm = null;
private $_data = null;
private $_id = null;
/**
/**
* Fetch data and timestamp from memcache, store in instance
*
*
* @param string $id
*
*
* @return bool success
*/
private function _fetch($id)
@@ -128,13 +125,17 @@ class Minify_Cache_Memcache {
if ($this->_id === $id) {
return true;
}
$ret = $this->_mc->get($id);
if (false === $ret) {
$this->_id = null;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}

67
lib/Minify/Cache/Null.php Normal file
View File

@@ -0,0 +1,67 @@
<?php
/**
* Class Minify_Cache_Null
*
* If this is used, Minify will not use a cache and, for each 200 response, will
* need to recombine files, minify and encode the output.
*
* @package Minify
*/
class Minify_Cache_Null implements Minify_CacheInterface
{
/**
* Write data to cache.
*
* @param string $id cache id (e.g. a filename)
* @param string $data
*
* @return bool success
*/
public function store($id, $data)
{
}
/**
* Get the size of a cache entry
*
* @param string $id cache id (e.g. a filename)
*
* @return int size in bytes
*/
public function getSize($id)
{
}
/**
* Does a valid cache entry exist?
*
* @param string $id cache id (e.g. a filename)
* @param int $srcMtime mtime of the original source file(s)
*
* @return bool exists
*/
public function isValid($id, $srcMtime)
{
}
/**
* Send the cached content to output
*
* @param string $id cache id (e.g. a filename)
*/
public function display($id)
{
}
/**
* Fetch the cached content
*
* @param string $id cache id (e.g. a filename)
*
* @return string
*/
public function fetch($id)
{
}
}

View File

@@ -6,24 +6,24 @@
/**
* WinCache-based cache class for Minify
*
*
* <code>
* Minify::setCache(new Minify_Cache_WinCache());
* </code>
*
*
* @package Minify
* @author Matthias Fax
**/
class Minify_Cache_WinCache
class Minify_Cache_WinCache implements Minify_CacheInterface
{
/**
* Create a Minify_Cache_Wincache object, to be passed to
* Minify::setCache().
*
*
* @param int $expire seconds until expiration (default = 0
* meaning the item will not get an expiration date)
*
* @throws Exception
*/
public function __construct($expire = 0)
{
@@ -32,7 +32,7 @@ class Minify_Cache_WinCache
}
$this->_exp = $expire;
}
/**
* Write data to cache.
*
@@ -46,7 +46,7 @@ class Minify_Cache_WinCache
{
return wincache_ucache_set($id, "{$_SERVER['REQUEST_TIME']}|{$data}", $this->_exp);
}
/**
* Get the size of a cache entry
*
@@ -59,9 +59,14 @@ class Minify_Cache_WinCache
if (!$this->_fetch($id)) {
return false;
}
return (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) ? mb_strlen($this->_data, '8bit') : strlen($this->_data);
if (function_exists('mb_strlen') && ((int) ini_get('mbstring.func_overload') & 2)) {
return mb_strlen($this->_data, '8bit');
} else {
return strlen($this->_data);
}
}
/**
* Does a valid cache entry exist?
*
@@ -75,7 +80,7 @@ class Minify_Cache_WinCache
{
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
}
/**
* Send the cached content to output
*
@@ -85,7 +90,7 @@ class Minify_Cache_WinCache
{
echo $this->_fetch($id) ? $this->_data : '';
}
/**
* Fetch the cached content
*
@@ -97,14 +102,14 @@ class Minify_Cache_WinCache
{
return $this->_fetch($id) ? $this->_data : '';
}
private $_exp = NULL;
private $_exp = null;
// cache of most recently fetched id
private $_lm = NULL;
private $_data = NULL;
private $_id = NULL;
private $_lm = null;
private $_data = null;
private $_id = null;
/**
* Fetch data and timestamp from WinCache, store in instance
*
@@ -117,14 +122,18 @@ class Minify_Cache_WinCache
if ($this->_id === $id) {
return true;
}
$suc = false;
$ret = wincache_ucache_get($id, $suc);
if (!$suc) {
$this->_id = NULL;
$this->_id = null;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}
}

View File

@@ -17,7 +17,8 @@
* @package Minify
* @author Elan Ruusamäe <glen@delfi.ee>
**/
class Minify_Cache_XCache {
class Minify_Cache_XCache implements Minify_CacheInterface
{
/**
* Create a Minify_Cache_XCache object, to be passed to
@@ -54,9 +55,12 @@ class Minify_Cache_XCache {
if (! $this->_fetch($id)) {
return false;
}
return (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2))
? mb_strlen($this->_data, '8bit')
: strlen($this->_data);
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
return mb_strlen($this->_data, '8bit');
} else {
return strlen($this->_data);
}
}
/**
@@ -78,9 +82,7 @@ class Minify_Cache_XCache {
*/
public function display($id)
{
echo $this->_fetch($id)
? $this->_data
: '';
echo $this->_fetch($id) ? $this->_data : '';
}
/**
@@ -91,9 +93,7 @@ class Minify_Cache_XCache {
*/
public function fetch($id)
{
return $this->_fetch($id)
? $this->_data
: '';
return $this->_fetch($id) ? $this->_data : '';
}
private $_exp = null;
@@ -114,13 +114,17 @@ class Minify_Cache_XCache {
if ($this->_id === $id) {
return true;
}
$ret = xcache_get($id);
if (false === $ret) {
$this->_id = null;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}

View File

@@ -4,12 +4,11 @@
* @package Minify
*/
/**
* ZendPlatform-based cache class for Minify
*
* Based on Minify_Cache_APC, uses output_cache_get/put (currently deprecated)
*
*
* <code>
* Minify::setCache(new Minify_Cache_ZendPlatform());
* </code>
@@ -17,8 +16,8 @@
* @package Minify
* @author Patrick van Dissel
*/
class Minify_Cache_ZendPlatform {
class Minify_Cache_ZendPlatform implements Minify_CacheInterface
{
/**
* Create a Minify_Cache_ZendPlatform object, to be passed to
@@ -27,14 +26,12 @@ class Minify_Cache_ZendPlatform {
* @param int $expire seconds until expiration (default = 0
* meaning the item will not get an expiration date)
*
* @return null
*/
public function __construct($expire = 0)
{
$this->_exp = $expire;
}
/**
* Write data to cache.
*
@@ -49,7 +46,6 @@ class Minify_Cache_ZendPlatform {
return output_cache_put($id, "{$_SERVER['REQUEST_TIME']}|{$data}");
}
/**
* Get the size of a cache entry
*
@@ -59,12 +55,9 @@ class Minify_Cache_ZendPlatform {
*/
public function getSize($id)
{
return $this->_fetch($id)
? strlen($this->_data)
: false;
return $this->_fetch($id) ? strlen($this->_data) : false;
}
/**
* Does a valid cache entry exist?
*
@@ -76,11 +69,9 @@ class Minify_Cache_ZendPlatform {
*/
public function isValid($id, $srcMtime)
{
$ret = ($this->_fetch($id) && ($this->_lm >= $srcMtime));
return $ret;
return ($this->_fetch($id) && ($this->_lm >= $srcMtime));
}
/**
* Send the cached content to output
*
@@ -88,12 +79,9 @@ class Minify_Cache_ZendPlatform {
*/
public function display($id)
{
echo $this->_fetch($id)
? $this->_data
: '';
echo $this->_fetch($id) ? $this->_data : '';
}
/**
* Fetch the cached content
*
@@ -103,21 +91,16 @@ class Minify_Cache_ZendPlatform {
*/
public function fetch($id)
{
return $this->_fetch($id)
? $this->_data
: '';
return $this->_fetch($id) ? $this->_data : '';
}
private $_exp = null;
// cache of most recently fetched id
private $_lm = null;
private $_data = null;
private $_id = null;
/**
* Fetch data and timestamp from ZendPlatform, store in instance
*
@@ -130,13 +113,17 @@ class Minify_Cache_ZendPlatform {
if ($this->_id === $id) {
return true;
}
$ret = output_cache_get($id, $this->_exp);
if (false === $ret) {
$this->_id = null;
return false;
}
list($this->_lm, $this->_data) = explode('|', $ret, 2);
$this->_id = $id;
return true;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
* Interface Minify_CacheInterface
* @package Minify
*/
/**
* Interface for Minify cache adapters
*
* @package Minify
*/
interface Minify_CacheInterface
{
/**
* Write data to cache.
*
* @param string $id cache id (e.g. a filename)
* @param string $data
*
* @return bool success
*/
public function store($id, $data);
/**
* Get the size of a cache entry
*
* @param string $id cache id (e.g. a filename)
*
* @return int size in bytes
*/
public function getSize($id);
/**
* Does a valid cache entry exist?
*
* @param string $id cache id (e.g. a filename)
* @param int $srcMtime mtime of the original source file(s)
*
* @return bool exists
*/
public function isValid($id, $srcMtime);
/**
* Send the cached content to output
*
* @param string $id cache id (e.g. a filename)
*/
public function display($id);
/**
* Fetch the cached content
*
* @param string $id cache id (e.g. a filename)
*
* @return string
*/
public function fetch($id);
}

View File

@@ -0,0 +1,240 @@
<?php
/**
* Class Minify_ClosureCompiler
* @package Minify
*/
/**
* Compress Javascript using the Closure Compiler
*
* You must set $jarFile and $tempDir before calling the minify functions.
* Also, depending on your shell's environment, you may need to specify
* the full path to java in $javaExecutable or use putenv() to setup the
* Java environment.
*
* <code>
* Minify_ClosureCompiler::$jarFile = '/path/to/closure-compiler-20120123.jar';
* Minify_ClosureCompiler::$tempDir = '/tmp';
* $code = Minify_ClosureCompiler::minify(
* $code,
* array('compilation_level' => 'SIMPLE_OPTIMIZATIONS')
* );
*
* --compilation_level WHITESPACE_ONLY, SIMPLE_OPTIMIZATIONS, ADVANCED_OPTIMIZATIONS
*
* </code>
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author Elan Ruusamäe <glen@delfi.ee>
*/
class Minify_ClosureCompiler
{
public static $isDebug = false;
/**
* Filepath of the Closure Compiler jar file. This must be set before
* calling minifyJs().
*
* @var string
*/
public static $jarFile;
/**
* Writable temp directory. This must be set before calling minifyJs().
*
* @var string
*/
public static $tempDir;
/**
* Filepath of "java" executable (may be needed if not in shell's PATH)
*
* @var string
*/
public static $javaExecutable = 'java';
/**
* Default command line options passed to closure-compiler
*
* @var array
*/
public static $defaultOptions = array(
'charset' => 'utf-8',
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
'warning_level' => 'QUIET',
);
/**
* Minify a Javascript string
*
* @param string $js
* @param array $options (verbose is ignored)
* @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
* @return string
* @throws Minify_ClosureCompiler_Exception
*/
public static function minify($js, $options = array())
{
$min = new static();
return $min->process($js, $options);
}
/**
* Process $js using $options.
*
* @param string $js
* @param array $options
* @return string
* @throws Exception
* @throws Minify_ClosureCompiler_Exception
*/
public function process($js, $options)
{
$tmpFile = $this->dumpFile(self::$tempDir, $js);
try {
$result = $this->compile($tmpFile, $options);
} catch (Exception $e) {
unlink($tmpFile);
throw $e;
}
unlink($tmpFile);
return $result;
}
/**
* @param string $tmpFile
* @param array $options
* @return string
* @throws Minify_ClosureCompiler_Exception
*/
protected function compile($tmpFile, $options)
{
$command = $this->getCommand($options, $tmpFile);
return implode("\n", $this->shell($command));
}
/**
* @param array $userOptions
* @param string $tmpFile
* @return string
*/
protected function getCommand($userOptions, $tmpFile)
{
$args = array_merge(
$this->getCompilerCommandLine(),
$this->getOptionsCommandLine($userOptions)
);
return implode(' ', $args) . ' ' . escapeshellarg($tmpFile);
}
/**
* @return array
* @throws Minify_ClosureCompiler_Exception
*/
protected function getCompilerCommandLine()
{
$this->checkJar(self::$jarFile);
$server = array(
self::$javaExecutable,
'-jar',
escapeshellarg(self::$jarFile)
);
return $server;
}
/**
* @param array $userOptions
* @return array
*/
protected function getOptionsCommandLine($userOptions)
{
$args = array();
$options = array_merge(
static::$defaultOptions,
$userOptions
);
foreach ($options as $key => $value) {
$args[] = "--{$key} " . escapeshellarg($value);
}
return $args;
}
/**
* @param string $jarFile
* @throws Minify_ClosureCompiler_Exception
*/
protected function checkJar($jarFile)
{
if (!is_file($jarFile)) {
throw new Minify_ClosureCompiler_Exception('$jarFile(' . $jarFile . ') is not a valid file.');
}
if (!is_readable($jarFile)) {
throw new Minify_ClosureCompiler_Exception('$jarFile(' . $jarFile . ') is not readable.');
}
}
/**
* @param string $tempDir
* @throws Minify_ClosureCompiler_Exception
*/
protected function checkTempdir($tempDir)
{
if ($tempDir === null || !is_dir($tempDir)) {
throw new Minify_ClosureCompiler_Exception('$tempDir(' . $tempDir . ') is not a valid direcotry.');
}
if (!is_writable($tempDir)) {
throw new Minify_ClosureCompiler_Exception('$tempDir(' . $tempDir . ') is not writable.');
}
}
/**
* Write $content to a temporary file residing in $dir.
*
* @param string $dir
* @param string $content
* @return string
* @throws Minify_ClosureCompiler_Exception
*/
protected function dumpFile($dir, $content)
{
$this->checkTempdir($dir);
$tmpFile = tempnam($dir, 'cc_');
if (!$tmpFile) {
throw new Minify_ClosureCompiler_Exception('Could not create temp file in "' . $dir . '".');
}
file_put_contents($tmpFile, $content);
return $tmpFile;
}
/**
* Execute command, throw if exit code is not in $expectedCodes array
*
* @param string $command
* @param array $expectedCodes
* @return mixed
* @throws Minify_ClosureCompiler_Exception
*/
protected function shell($command, $expectedCodes = array(0))
{
exec($command, $output, $result_code);
if (!in_array($result_code, $expectedCodes)) {
throw new Minify_ClosureCompiler_Exception("Unpexpected return code: $result_code");
}
return $output;
}
}
class Minify_ClosureCompiler_Exception extends Exception
{
}

View File

@@ -1,41 +1,42 @@
<?php
/**
* Class Minify_CommentPreserver
* Class Minify_CommentPreserver
* @package Minify
*/
/**
* Process a string in pieces preserving C-style comments that begin with "/*!"
*
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_CommentPreserver {
class Minify_CommentPreserver
{
/**
* String to be prepended to each preserved comment
*
* @var string
*/
public static $prepend = "\n";
/**
* String to be appended to each preserved comment
*
* @var string
*/
public static $append = "\n";
/**
* Process a string outside of C-style comments that begin with "/*!"
*
* On each non-empty string outside these comments, the given processor
* function will be called. The comments will be surrounded by
* On each non-empty string outside these comments, the given processor
* function will be called. The comments will be surrounded by
* Minify_CommentPreserver::$preprend and Minify_CommentPreserver::$append.
*
*
* @param string $content
* @param callback $processor function
* @param array $args array of extra arguments to pass to the processor
* @param array $args array of extra arguments to pass to the processor
* function (default = array())
* @return string
*/
@@ -47,7 +48,7 @@ class Minify_CommentPreserver {
if ('' !== $beforeComment) {
$callArgs = $args;
array_unshift($callArgs, $beforeComment);
$ret .= call_user_func_array($processor, $callArgs);
$ret .= call_user_func_array($processor, $callArgs);
}
if (false === $comment) {
break;
@@ -55,35 +56,32 @@ class Minify_CommentPreserver {
$ret .= $comment;
$content = $afterComment;
}
return $ret;
}
/**
* Extract comments that YUI Compressor preserves.
*
*
* @param string $in input
*
*
* @return array 3 elements are returned. If a YUI comment is found, the
* 2nd element is the comment and the 1st and 3rd are the surrounding
* strings. If no comment is found, the entire string is returned as the
* strings. If no comment is found, the entire string is returned as the
* 1st element and the other two are false.
*/
private static function _nextComment($in)
{
if (
false === ($start = strpos($in, '/*!'))
|| false === ($end = strpos($in, '*/', $start + 3))
) {
if (false === ($start = strpos($in, '/*!')) || false === ($end = strpos($in, '*/', $start + 3))) {
return array($in, false, false);
}
$ret = array(
substr($in, 0, $start)
,self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append
);
$beforeComment = substr($in, 0, $start);
$comment = self::$prepend . '/*!' . substr($in, $start + 3, $end - $start - 1) . self::$append;
$endChars = (strlen($in) - $end - 2);
$ret[] = (0 === $endChars)
? ''
: substr($in, -$endChars);
return $ret;
$afterComment = (0 === $endChars) ? '' : substr($in, -$endChars);
return array($beforeComment, $comment, $afterComment);
}
}

78
lib/Minify/Config.php Normal file
View File

@@ -0,0 +1,78 @@
<?php
namespace Minify;
use Minify_CacheInterface;
class Config
{
/**
* @var bool
*/
public $enableBuilder = false;
/**
* @var bool
*/
public $enableStatic = false;
/**
* @var bool
*/
public $concatOnly = false;
/**
* @var string
*/
public $builderPassword = 'admin';
/**
* @var bool|object
*/
public $errorLogger = false;
/**
* @var bool
*/
public $allowDebugFlag = false;
/**
* @var string|Minify_CacheInterface
*/
public $cachePath = '';
/**
* @var string
*/
public $documentRoot = '';
/**
* @var bool
*/
public $cacheFileLocking = true;
/**
* @var array
*/
public $serveOptions = array();
/**
* @var array
*/
public $symlinks = array();
/**
* @var int
*/
public $uploaderHoursBehind = 0;
/**
* @var array
*/
public $envArgs = array();
/**
* @var callable[]
*/
public $factories = array();
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* Class Minify_Controller_Base
* @package Minify
*/
use Psr\Log\LoggerInterface;
use Monolog\Logger;
/**
* Base class for Minify controller
*
* The controller class validates a request and uses it to create a configuration for Minify::serve().
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
abstract class Minify_Controller_Base implements Minify_ControllerInterface
{
/**
* @var Minify_Env
*/
protected $env;
/**
* @var Minify_Source_Factory
*/
protected $sourceFactory;
/**
* @var LoggerInterface
*/
protected $logger;
/**
* @param Minify_Env $env
* @param Minify_Source_Factory $sourceFactory
* @param LoggerInterface $logger
*/
public function __construct(Minify_Env $env, Minify_Source_Factory $sourceFactory, ?LoggerInterface $logger = null)
{
$this->env = $env;
$this->sourceFactory = $sourceFactory;
if (!$logger) {
$logger = new Logger('minify');
}
$this->logger = $logger;
}
/**
* Create controller sources and options for Minify::serve()
*
* @param array $options controller and Minify options
*
* @return Minify_ServeConfiguration
*/
abstract public function createConfiguration(array $options);
/**
* Send message to the Minify logger
*
* @param string $msg
*
* @return null
* @deprecated use $this->logger
*/
public function log($msg)
{
trigger_error(__METHOD__ . ' is deprecated in Minify 3.0.', E_USER_DEPRECATED);
$this->logger->info($msg);
}
/**
* {@inheritdoc}
*/
public function getEnv()
{
return $this->env;
}
}

View File

@@ -0,0 +1,70 @@
<?php
/**
* Class Minify_Controller_Files
* @package Minify
*/
use Monolog\Logger;
/**
* Controller class for minifying a set of files
*
* E.g. the following would serve the minified Javascript for a site
* <code>
* $options = [
* 'checkAllowDirs' => false, // allow files to be anywhere
* ];
* $sourceFactory = new Minify_Source_Factory($env, $options, $cache);
* $controller = new Minify_Controller_Files($env, $sourceFactory);
* $minify->serve($controller, [
* 'files' => [
* '//js/jquery.js',
* '//js/plugins.js',
* '/home/username/file.js',
* ],
* ]);
* </code>
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_Controller_Files extends Minify_Controller_Base
{
/**
* Set up file sources
*
* @param array $options controller and Minify options
* @return Minify_ServeConfiguration
*
* Controller options:
*
* 'files': (required) array of complete file paths, or a single path
*/
public function createConfiguration(array $options)
{
// strip controller options
$files = $options['files'];
// if $files is a single object, casting will break it
if (is_object($files)) {
$files = array($files);
} elseif (! is_array($files)) {
$files = (array)$files;
}
unset($options['files']);
$sources = array();
foreach ($files as $file) {
try {
$sources[] = $this->sourceFactory->makeSource($file);
} catch (Minify_Source_FactoryException $e) {
$this->logger->error($e->getMessage());
return new Minify_ServeConfiguration($options);
}
}
return new Minify_ServeConfiguration($options, $sources);
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* Class Minify_Controller_Groups
* @package Minify
*/
/**
* Controller class for serving predetermined groups of minimized sets, selected
* by PATH_INFO
*
* <code>
* Minify::serve('Groups', array(
* 'groups' => array(
* 'css' => array('//css/type.css', '//css/layout.css')
* ,'js' => array('//js/jquery.js', '//js/site.js')
* )
* ));
* </code>
*
* If the above code were placed in /serve.php, it would enable the URLs
* /serve.php/js and /serve.php/css
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_Controller_Groups extends Minify_Controller_Files
{
/**
* Set up groups of files as sources
*
* @param array $options controller and Minify options
*
* 'groups': (required) array mapping PATH_INFO strings to arrays
* of complete file paths. @see Minify_Controller_Groups
*
* @return array Minify options
*/
public function createConfiguration(array $options)
{
// strip controller options
$groups = $options['groups'];
unset($options['groups']);
$server = $this->env->server();
// mod_fcgid places PATH_INFO in ORIG_PATH_INFO
if (isset($server['ORIG_PATH_INFO'])) {
$pathInfo = substr($server['ORIG_PATH_INFO'], 1);
} elseif (isset($server['PATH_INFO'])) {
$pathInfo = substr($server['PATH_INFO'], 1);
} else {
$pathInfo = false;
}
if (false === $pathInfo || ! isset($groups[$pathInfo])) {
// no PATH_INFO or not a valid group
$this->logger->info("Missing PATH_INFO or no group set for \"$pathInfo\"");
return new Minify_ServeConfiguration($options);
}
$files = $groups[$pathInfo];
// if $files is a single object, casting will break it
if (is_object($files)) {
$files = array($files);
} elseif (! is_array($files)) {
$files = (array)$files;
}
$options['files'] = $files;
return parent::createConfiguration($options);
}
}

View File

@@ -0,0 +1,196 @@
<?php
/**
* Class Minify_Controller_MinApp
* @package Minify
*/
/**
* Controller class for requests to /min/index.php
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_Controller_MinApp extends Minify_Controller_Base
{
/**
* Set up groups of files as sources
*
* @param array $options controller and Minify options
*
* @return array Minify options
*/
public function createConfiguration(array $options)
{
// PHP insecure by default: realpath() and other FS functions can't handle null bytes.
$get = $this->env->get();
foreach (array('g', 'b', 'f') as $key) {
if (isset($get[$key])) {
$get[$key] = str_replace("\x00", '', (string)$get[$key]);
}
}
// filter controller options
$defaults = array(
'groupsOnly' => false,
'groups' => array(),
'symlinks' => array(),
);
$minApp = isset($options['minApp']) ? $options['minApp'] : array();
$localOptions = array_merge($defaults, $minApp);
unset($options['minApp']);
// normalize $symlinks in order to map to target
$symlinks = array();
foreach ($localOptions['symlinks'] as $link => $target) {
if (0 === strpos($link, '//')) {
$link = rtrim(substr($link, 1), '/') . '/';
$target = rtrim($target, '/\\');
$symlinks[$link] = $target;
}
}
$sources = array();
$selectionId = '';
$firstMissing = null;
if (isset($get['g'])) {
// add group(s)
$selectionId .= 'g=' . $get['g'];
$keys = explode(',', $get['g']);
if ($keys != array_unique($keys)) {
$this->logger->info("Duplicate group key found.");
return new Minify_ServeConfiguration($options);
}
foreach ($keys as $key) {
if (! isset($localOptions['groups'][$key])) {
$this->logger->info("A group configuration for \"{$key}\" was not found");
return new Minify_ServeConfiguration($options);
}
$files = $localOptions['groups'][$key];
// if $files is a single object, casting will break it
if (is_object($files)) {
$files = array($files);
} elseif (! is_array($files)) {
$files = (array)$files;
}
foreach ($files as $file) {
try {
$source = $this->sourceFactory->makeSource($file);
$sources[] = $source;
} catch (Minify_Source_FactoryException $e) {
$this->logger->error($e->getMessage());
if (null === $firstMissing) {
$firstMissing = basename($file);
continue;
} else {
$secondMissing = basename($file);
$this->logger->info("More than one file was missing: '$firstMissing', '$secondMissing'");
return new Minify_ServeConfiguration($options);
}
}
}
}
}
if (! $localOptions['groupsOnly'] && isset($get['f'])) {
// try user files
// The following restrictions are to limit the URLs that minify will
// respond to.
// verify at least one file, files are single comma separated, and are all same extension
$validPattern = preg_match('/^[^,]+\\.(css|less|scss|js)(?:,[^,]+\\.\\1)*$/', $get['f'], $m);
$hasComment = strpos($get['f'], '//') !== false;
$hasEscape = strpos($get['f'], '\\') !== false;
if (!$validPattern || $hasComment || $hasEscape) {
$this->logger->info("GET param 'f' was invalid");
return new Minify_ServeConfiguration($options);
}
$ext = ".{$m[1]}";
$files = explode(',', $get['f']);
if ($files != array_unique($files)) {
$this->logger->info("Duplicate files were specified");
return new Minify_ServeConfiguration($options);
}
if (isset($get['b'])) {
// check for validity
$isValidBase = preg_match('@^[^/]+(?:/[^/]+)*$@', $get['b']);
$hasDots = false !== strpos($get['b'], '..');
$isDot = $get['b'] === '.';
if ($isValidBase && !$hasDots && !$isDot) {
// valid base
$base = "/{$get['b']}/";
} else {
$this->logger->info("GET param 'b' was invalid");
return new Minify_ServeConfiguration($options);
}
} else {
$base = '/';
}
$basenames = array(); // just for cache id
foreach ($files as $file) {
$uri = $base . $file;
$path = $this->env->getDocRoot() . $uri;
// try to rewrite path
foreach ($symlinks as $link => $target) {
if (0 === strpos($uri, $link)) {
$path = $target . DIRECTORY_SEPARATOR . substr($uri, strlen($link));
break;
}
}
try {
$source = $this->sourceFactory->makeSource($path);
$sources[] = $source;
$basenames[] = basename($path, $ext);
} catch (Minify_Source_FactoryException $e) {
$this->logger->error($e->getMessage());
if (null === $firstMissing) {
$firstMissing = $uri;
continue;
} else {
$secondMissing = $uri;
$this->logger->info("More than one file was missing: '$firstMissing', '$secondMissing`'");
return new Minify_ServeConfiguration($options);
}
}
}
if ($selectionId) {
$selectionId .= '_f=';
}
$selectionId .= implode(',', $basenames) . $ext;
}
if (!$sources) {
$this->logger->info("No sources to serve");
return new Minify_ServeConfiguration($options);
}
if (null !== $firstMissing) {
array_unshift($sources, new Minify_Source(array(
'id' => 'missingFile',
// should not cause cache invalidation
'lastModified' => 0,
// due to caching, filename is unreliable.
'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n",
'minifier' => 'Minify::nullMinifier',
)));
}
return new Minify_ServeConfiguration($options, $sources, $selectionId);
}
}

View File

@@ -1,39 +1,39 @@
<?php
/**
* Class Minify_Controller_Page
* Class Minify_Controller_Page
* @package Minify
*/
/**
* Controller class for serving a single HTML page
*
*
* @link http://code.google.com/p/minify/source/browse/trunk/web/examples/1/index.php#59
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_Controller_Page extends Minify_Controller_Base {
class Minify_Controller_Page extends Minify_Controller_Base
{
/**
* Set up source of HTML content
*
*
* @param array $options controller and Minify options
* @return array Minify options
*
*
* Controller options:
*
*
* 'content': (required) HTML markup
*
*
* 'id': (required) id of page (string for use in server-side caching)
*
*
* 'lastModifiedTime': timestamp of when this content changed. This
* is recommended to allow both server and client-side caching.
*
* 'minifyAll': should all CSS and Javascript blocks be individually
* minified? (default false)
*
* @todo Add 'file' option to read HTML file.
* 'minifyAll': should all CSS and Javascript blocks be individually
* minified? (default false)
*/
public function setupSources($options) {
public function createConfiguration(array $options)
{
if (isset($options['file'])) {
$sourceSpec = array(
'filepath' => $options['file']
@@ -42,27 +42,27 @@ class Minify_Controller_Page extends Minify_Controller_Base {
} else {
// strip controller options
$sourceSpec = array(
'content' => $options['content']
,'id' => $options['id']
'content' => $options['content'],
'id' => $options['id'],
);
$f = $options['id'];
unset($options['content'], $options['id']);
}
// something like "builder,index.php" or "directory,file.html"
$this->selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,');
$selectionId = strtr(substr($f, 1 + strlen(dirname(dirname($f)))), '/\\', ',,');
if (isset($options['minifyAll'])) {
// this will be the 2nd argument passed to Minify_HTML::minify()
$sourceSpec['minifyOptions'] = array(
'cssMinifier' => array('Minify_CSS', 'minify')
,'jsMinifier' => array('JSMin', 'minify')
'cssMinifier' => array('Minify_CSSmin', 'minify'),
'jsMinifier' => array('JSMin\\JSMin', 'minify'),
);
unset($options['minifyAll']);
}
$this->sources[] = new Minify_Source($sourceSpec);
$options['contentType'] = Minify::TYPE_HTML;
return $options;
$sourceSpec['contentType'] = Minify::TYPE_HTML;
$sources[] = new Minify_Source($sourceSpec);
return new Minify_ServeConfiguration($options, $sources, $selectionId);
}
}

View File

@@ -0,0 +1,22 @@
<?php
interface Minify_ControllerInterface
{
/**
* Create controller sources and options for Minify::serve()
*
* @param array $options controller and Minify options
*
* @return Minify_ServeConfiguration
*/
public function createConfiguration(array $options);
/**
* Get the Env component
*
* @return Minify_Env
*/
public function getEnv();
}

View File

@@ -1,26 +1,30 @@
<?php
/**
* Detect whether request should be debugged
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_DebugDetector {
public static function shouldDebugRequest($cookie, $get, $requestUri)
{
if (isset($get['debug'])) {
return true;
}
if (! empty($cookie['minifyDebug'])) {
foreach (preg_split('/\\s+/', $cookie['minifyDebug']) as $debugUri) {
$pattern = '@' . preg_quote($debugUri, '@') . '@i';
$pattern = str_replace(array('\\*', '\\?'), array('.*', '.'), $pattern);
if (preg_match($pattern, $requestUri)) {
return true;
}
}
}
return false;
}
}
<?php
/**
* Detect whether request should be debugged
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_DebugDetector
{
public static function shouldDebugRequest(Minify_Env $env)
{
if ($env->get('debug') !== null) {
return true;
}
$cookieValue = $env->cookie('minifyDebug');
if ($cookieValue) {
foreach (preg_split('/\\s+/', $cookieValue) as $debugUri) {
$pattern = '@' . preg_quote($debugUri, '@') . '@i';
$pattern = str_replace(array('\\*', '\\?'), array('.*', '.'), $pattern);
if (preg_match($pattern, $env->getRequestUri())) {
return true;
}
}
}
return false;
}
}

127
lib/Minify/Env.php Normal file
View File

@@ -0,0 +1,127 @@
<?php
class Minify_Env
{
/**
* @return string
*/
public function getDocRoot()
{
return $this->server['DOCUMENT_ROOT'];
}
/**
* @return string
*/
public function getRequestUri()
{
return $this->server['REQUEST_URI'];
}
public function __construct($options = array())
{
$options = array_merge(array(
'server' => $_SERVER,
'get' => $_GET,
'post' => $_POST,
'cookie' => $_COOKIE,
), $options);
$this->server = $options['server'];
if (empty($this->server['DOCUMENT_ROOT'])) {
$this->server['DOCUMENT_ROOT'] = $this->computeDocRoot($options['server']);
} else {
$this->server['DOCUMENT_ROOT'] = rtrim($this->server['DOCUMENT_ROOT'], '/\\');
}
$this->server['DOCUMENT_ROOT'] = $this->normalizePath($this->server['DOCUMENT_ROOT']);
$this->get = $options['get'];
$this->post = $options['post'];
$this->cookie = $options['cookie'];
}
public function server($key = null)
{
if (null === $key) {
return $this->server;
}
return isset($this->server[$key]) ? $this->server[$key] : null;
}
public function cookie($key = null, $default = null)
{
if (null === $key) {
return $this->cookie;
}
return isset($this->cookie[$key]) ? $this->cookie[$key] : $default;
}
public function get($key = null, $default = null)
{
if (null === $key) {
return $this->get;
}
return isset($this->get[$key]) ? $this->get[$key] : $default;
}
public function post($key = null, $default = null)
{
if (null === $key) {
return $this->post;
}
return isset($this->post[$key]) ? $this->post[$key] : $default;
}
/**
* turn windows-style slashes into unix-style,
* remove trailing slash
* and lowercase drive letter
*
* @param string $path absolute path
*
* @return string
*/
public function normalizePath($path)
{
$realpath = realpath($path);
if ($realpath) {
$path = $realpath;
}
$path = str_replace('\\', '/', $path);
$path = rtrim($path, '/');
if (substr($path, 1, 1) === ':') {
$path = lcfirst($path);
}
return $path;
}
protected $server;
protected $get;
protected $post;
protected $cookie;
/**
* Compute $_SERVER['DOCUMENT_ROOT'] for IIS using SCRIPT_FILENAME and SCRIPT_NAME.
*
* @param array $server
* @return string
*/
protected function computeDocRoot(array $server)
{
if (isset($server['SERVER_SOFTWARE']) && 0 !== strpos($server['SERVER_SOFTWARE'], 'Microsoft-IIS/')) {
throw new InvalidArgumentException('DOCUMENT_ROOT is not provided and could not be computed');
}
$substrLength = strlen($server['SCRIPT_FILENAME']) - strlen($server['SCRIPT_NAME']);
$docRoot = substr($server['SCRIPT_FILENAME'], 0, $substrLength);
return rtrim($docRoot, '\\');
}
}

View File

@@ -1,255 +1,268 @@
<?php
/**
* Class Minify_HTML
* @package Minify
*/
/**
* Compress HTML
*
* This is a heavy regex-based removal of whitespace, unnecessary comments and
* tokens. IE conditional comments are preserved. There are also options to have
* STYLE and SCRIPT blocks compressed by callback functions.
*
* A test suite is available.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_HTML {
/**
* @var boolean
*/
protected $_jsCleanComments = true;
/**
* "Minify" an HTML page
*
* @param string $html
*
* @param array $options
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*
* @return string
*/
public static function minify($html, $options = array()) {
$min = new self($html, $options);
return $min->process();
}
/**
* Create a minifier object
*
* @param string $html
*
* @param array $options
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
* 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*/
public function __construct($html, $options = array())
{
$this->_html = str_replace("\r\n", "\n", trim($html));
if (isset($options['xhtml'])) {
$this->_isXhtml = (bool)$options['xhtml'];
}
if (isset($options['cssMinifier'])) {
$this->_cssMinifier = $options['cssMinifier'];
}
if (isset($options['jsMinifier'])) {
$this->_jsMinifier = $options['jsMinifier'];
}
if (isset($options['jsCleanComments'])) {
$this->_jsCleanComments = (bool)$options['jsCleanComments'];
}
}
/**
* Minify the markeup given in the constructor
*
* @return string
*/
public function process()
{
if ($this->_isXhtml === null) {
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
}
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
$this->_placeholders = array();
// replace SCRIPTs (and minify) with placeholders
$this->_html = preg_replace_callback(
'/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/i'
,array($this, '_removeScriptCB')
,$this->_html);
// replace STYLEs (and minify) with placeholders
$this->_html = preg_replace_callback(
'/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/i'
,array($this, '_removeStyleCB')
,$this->_html);
// remove HTML comments (not containing IE conditional comments).
$this->_html = preg_replace_callback(
'/<!--([\\s\\S]*?)-->/'
,array($this, '_commentCB')
,$this->_html);
// replace PREs with placeholders
$this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/i'
,array($this, '_removePreCB')
,$this->_html);
// replace TEXTAREAs with placeholders
$this->_html = preg_replace_callback(
'/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/i'
,array($this, '_removeTextareaCB')
,$this->_html);
// trim each line.
// @todo take into account attribute values that span multiple lines.
$this->_html = preg_replace('/^\\s+|\\s+$/m', '', $this->_html);
// remove ws around block/undisplayed elements
$this->_html = preg_replace('/\\s+(<\\/?(?:area|base(?:font)?|blockquote|body'
.'|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|form'
.'|frame(?:set)?|h[1-6]|head|hr|html|legend|li|link|map|menu|meta'
.'|ol|opt(?:group|ion)|p|param|t(?:able|body|head|d|h||r|foot|itle)'
.'|ul)\\b[^>]*>)/i', '$1', $this->_html);
// remove ws outside of all elements
$this->_html = preg_replace(
'/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</'
,'>$1$2$3<'
,$this->_html);
// use newlines before 1st attribute in open tags (to limit line lengths)
$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/i', "$1\n$2", $this->_html);
// fill placeholders
$this->_html = str_replace(
array_keys($this->_placeholders)
,array_values($this->_placeholders)
,$this->_html
);
// issue 229: multi-pass to catch scripts that didn't get replaced in textareas
$this->_html = str_replace(
array_keys($this->_placeholders)
,array_values($this->_placeholders)
,$this->_html
);
return $this->_html;
}
protected function _commentCB($m)
{
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<!['))
? $m[0]
: '';
}
protected function _reservePlace($content)
{
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
$this->_placeholders[$placeholder] = $content;
return $placeholder;
}
protected $_isXhtml = null;
protected $_replacementHash = null;
protected $_placeholders = array();
protected $_cssMinifier = null;
protected $_jsMinifier = null;
protected function _removePreCB($m)
{
return $this->_reservePlace("<pre{$m[1]}");
}
protected function _removeTextareaCB($m)
{
return $this->_reservePlace("<textarea{$m[1]}");
}
protected function _removeStyleCB($m)
{
$openStyle = "<style{$m[1]}";
$css = $m[2];
// remove HTML comments
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/', '', $css);
// remove CDATA section markers
$css = $this->_removeCdata($css);
// minify
$minifier = $this->_cssMinifier
? $this->_cssMinifier
: 'trim';
$css = call_user_func($minifier, $css);
return $this->_reservePlace($this->_needsCdata($css)
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
: "{$openStyle}{$css}</style>"
);
}
protected function _removeScriptCB($m)
{
$openScript = "<script{$m[2]}";
$js = $m[3];
// whitespace surrounding? preserve at least one space
$ws1 = ($m[1] === '') ? '' : ' ';
$ws2 = ($m[4] === '') ? '' : ' ';
// remove HTML comments (and ending "//" if present)
if ($this->_jsCleanComments) {
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/', '', $js);
}
// remove CDATA section markers
$js = $this->_removeCdata($js);
// minify
$minifier = $this->_jsMinifier
? $this->_jsMinifier
: 'trim';
$js = call_user_func($minifier, $js);
return $this->_reservePlace($this->_needsCdata($js)
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
);
}
protected function _removeCdata($str)
{
return (false !== strpos($str, '<![CDATA['))
? str_replace(array('<![CDATA[', ']]>'), '', $str)
: $str;
}
protected function _needsCdata($str)
{
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/', $str));
}
}
<?php
/**
* Class Minify_HTML
* @package Minify
*/
/**
* Compress HTML
*
* This is a heavy regex-based removal of whitespace, unnecessary comments and
* tokens. IE conditional comments are preserved. There are also options to have
* STYLE and SCRIPT blocks compressed by callback functions.
*
* A test suite is available.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_HTML
{
/**
* @var boolean
*/
protected $_jsCleanComments = true;
/**
* @var string
*/
protected $_html;
/**
* "Minify" an HTML page
*
* @param string $html
*
* @param array $options
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*
* @return string
*/
public static function minify($html, $options = array())
{
$min = new self($html, $options);
return $min->process();
}
/**
* Create a minifier object
*
* @param string $html
*
* @param array $options
*
* 'cssMinifier' : (optional) callback function to process content of STYLE
* elements.
*
* 'jsMinifier' : (optional) callback function to process content of SCRIPT
* elements. Note: the type attribute is ignored.
*
* 'jsCleanComments' : (optional) whether to remove HTML comments beginning and end of script block
*
* 'xhtml' : (optional boolean) should content be treated as XHTML1.0? If
* unset, minify will sniff for an XHTML doctype.
*/
public function __construct($html, $options = array())
{
$this->_html = str_replace("\r\n", "\n", trim($html));
if (isset($options['xhtml'])) {
$this->_isXhtml = (bool)$options['xhtml'];
}
if (isset($options['cssMinifier'])) {
$this->_cssMinifier = $options['cssMinifier'];
}
if (isset($options['jsMinifier'])) {
$this->_jsMinifier = $options['jsMinifier'];
}
if (isset($options['jsCleanComments'])) {
$this->_jsCleanComments = (bool)$options['jsCleanComments'];
}
}
/**
* Minify the markeup given in the constructor
*
* @return string
*/
public function process()
{
if ($this->_isXhtml === null) {
$this->_isXhtml = (false !== strpos($this->_html, '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML'));
}
$this->_replacementHash = 'MINIFYHTML' . md5($_SERVER['REQUEST_TIME']);
$this->_placeholders = array();
// replace SCRIPTs (and minify) with placeholders
$this->_html = preg_replace_callback(
'/(\\s*)<script(\\b[^>]*?>)([\\s\\S]*?)<\\/script>(\\s*)/iu',
array($this, '_removeScriptCB'),
$this->_html
);
// replace STYLEs (and minify) with placeholders
$this->_html = preg_replace_callback(
'/\\s*<style(\\b[^>]*>)([\\s\\S]*?)<\\/style>\\s*/iu',
array($this, '_removeStyleCB'),
$this->_html
);
// remove HTML comments (not containing IE conditional comments).
$this->_html = preg_replace_callback(
'/<!--([\\s\\S]*?)-->/u',
array($this, '_commentCB'),
$this->_html
);
// replace PREs with placeholders
$this->_html = preg_replace_callback('/\\s*<pre(\\b[^>]*?>[\\s\\S]*?<\\/pre>)\\s*/iu', array($this, '_removePreCB'), $this->_html);
// replace TEXTAREAs with placeholders
$this->_html = preg_replace_callback(
'/\\s*<textarea(\\b[^>]*?>[\\s\\S]*?<\\/textarea>)\\s*/iu',
array($this, '_removeTextareaCB'),
$this->_html
);
// trim each line.
// @todo take into account attribute values that span multiple lines.
$this->_html = preg_replace('/^\\s+|\\s+$/mu', '', $this->_html);
// remove ws around block/undisplayed elements
$this->_html = preg_replace('/\\s+(<\\/?(?:area|article|aside|base(?:font)?|blockquote|body'
.'|canvas|caption|center|col(?:group)?|dd|dir|div|dl|dt|fieldset|figcaption|figure|footer|form'
.'|frame(?:set)?|h[1-6]|head|header|hgroup|hr|html|legend|li|link|main|map|menu|meta|nav'
.'|ol|opt(?:group|ion)|output|p|param|section|t(?:able|body|head|d|h||r|foot|itle)'
.'|ul|video)\\b[^>]*>)/iu', '$1', $this->_html);
// remove ws outside of all elements
$this->_html = preg_replace(
'/>(\\s(?:\\s*))?([^<]+)(\\s(?:\s*))?</u',
'>$1$2$3<',
$this->_html
);
// use newlines before 1st attribute in open tags (to limit line lengths)
$this->_html = preg_replace('/(<[a-z\\-]+)\\s+([^>]+>)/iu', "$1\n$2", $this->_html);
// fill placeholders
$this->_html = str_replace(
array_keys($this->_placeholders),
array_values($this->_placeholders),
$this->_html
);
// issue 229: multi-pass to catch scripts that didn't get replaced in textareas
$this->_html = str_replace(
array_keys($this->_placeholders),
array_values($this->_placeholders),
$this->_html
);
return $this->_html;
}
protected function _commentCB($m)
{
return (0 === strpos($m[1], '[') || false !== strpos($m[1], '<![') || 0 === strpos($m[1], '#'))
? $m[0]
: '';
}
protected function _reservePlace($content)
{
$placeholder = '%' . $this->_replacementHash . count($this->_placeholders) . '%';
$this->_placeholders[$placeholder] = $content;
return $placeholder;
}
protected $_isXhtml;
protected $_replacementHash;
protected $_placeholders = array();
protected $_cssMinifier;
protected $_jsMinifier;
protected function _removePreCB($m)
{
return $this->_reservePlace("<pre{$m[1]}");
}
protected function _removeTextareaCB($m)
{
return $this->_reservePlace("<textarea{$m[1]}");
}
protected function _removeStyleCB($m)
{
$openStyle = "<style{$m[1]}";
$css = $m[2];
// remove HTML comments
$css = preg_replace('/(?:^\\s*<!--|-->\\s*$)/u', '', $css);
// remove CDATA section markers
$css = $this->_removeCdata($css);
// minify
$minifier = $this->_cssMinifier
? $this->_cssMinifier
: 'trim';
$css = call_user_func($minifier, $css);
return $this->_reservePlace(
$this->_needsCdata($css)
? "{$openStyle}/*<![CDATA[*/{$css}/*]]>*/</style>"
: "{$openStyle}{$css}</style>"
);
}
protected function _removeScriptCB($m)
{
$openScript = "<script{$m[2]}";
$js = $m[3];
// whitespace surrounding? preserve at least one space
$ws1 = ($m[1] === '') ? '' : ' ';
$ws2 = ($m[4] === '') ? '' : ' ';
// remove HTML comments (and ending "//" if present)
if ($this->_jsCleanComments) {
$js = preg_replace('/(?:^\\s*<!--\\s*|\\s*(?:\\/\\/)?\\s*-->\\s*$)/u', '', $js);
}
// remove CDATA section markers
$js = $this->_removeCdata($js);
// minify
$minifier = $this->_jsMinifier
? $this->_jsMinifier
: 'trim';
$js = call_user_func($minifier, $js);
return $this->_reservePlace(
$this->_needsCdata($js)
? "{$ws1}{$openScript}/*<![CDATA[*/{$js}/*]]>*/</script>{$ws2}"
: "{$ws1}{$openScript}{$js}</script>{$ws2}"
);
}
protected function _removeCdata($str)
{
return (false !== strpos($str, '<![CDATA['))
? str_replace(array('<![CDATA[', ']]>'), '', $str)
: $str;
}
protected function _needsCdata($str)
{
return ($this->_isXhtml && preg_match('/(?:[<&]|\\-\\-|\\]\\]>)/u', $str));
}
}

View File

@@ -5,19 +5,20 @@
*/
/**
* Helpers for writing Minfy URIs into HTML
* Helpers for writing Minify URIs into HTML
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_HTML_Helper {
class Minify_HTML_Helper
{
public $rewriteWorks = true;
public $minAppUri = '/min';
public $groupsConfigFile = '';
/**
* Get an HTML-escaped Minify URI for a group or set of files
*
*
* @param string|array $keyOrFiles a group key or array of filepaths/URIs
* @param array $opts options:
* 'farExpires' : (default true) append a modified timestamp for cache revving
@@ -31,24 +32,27 @@ class Minify_HTML_Helper {
public static function getUri($keyOrFiles, $opts = array())
{
$opts = array_merge(array( // default options
'farExpires' => true
,'debug' => false
,'charset' => 'UTF-8'
,'minAppUri' => '/min'
,'rewriteWorks' => true
,'groupsConfigFile' => ''
'farExpires' => true,
'debug' => false,
'charset' => 'UTF-8',
'minAppUri' => '/min',
'rewriteWorks' => true,
'groupsConfigFile' => self::app()->groupsConfigPath,
), $opts);
$h = new self;
$h->minAppUri = $opts['minAppUri'];
$h->rewriteWorks = $opts['rewriteWorks'];
$h->groupsConfigFile = $opts['groupsConfigFile'];
if (is_array($keyOrFiles)) {
$h->setFiles($keyOrFiles, $opts['farExpires']);
} else {
$h->setGroup($keyOrFiles, $opts['farExpires']);
}
$uri = $h->getRawUri($opts['farExpires'], $opts['debug']);
return htmlspecialchars($uri, ENT_QUOTES, $opts['charset']);
return htmlspecialchars($uri, ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML401, $opts['charset']);
}
/**
@@ -75,6 +79,7 @@ class Minify_HTML_Helper {
} elseif ($farExpires && $this->_lastModified) {
$path .= "&" . $this->_lastModified;
}
return $path;
}
@@ -94,9 +99,8 @@ class Minify_HTML_Helper {
foreach ($files as $k => $file) {
if (0 === strpos($file, '//')) {
$file = substr($file, 2);
} elseif (0 === strpos($file, '/')
|| 1 === strpos($file, ':\\')) {
$file = substr($file, strlen($_SERVER['DOCUMENT_ROOT']) + 1);
} elseif (0 === strpos($file, '/') || 1 === strpos($file, ':\\')) {
$file = substr($file, strlen(self::app()->env->getDocRoot()) + 1);
}
$file = strtr($file, '\\', '/');
$files[$k] = $file;
@@ -115,15 +119,19 @@ class Minify_HTML_Helper {
$this->_groupKey = $key;
if ($checkLastModified) {
if (! $this->groupsConfigFile) {
$this->groupsConfigFile = dirname(dirname(dirname(dirname(__FILE__)))) . '/groupsConfig.php';
$this->groupsConfigFile = self::app()->groupsConfigPath;
}
if (is_file($this->groupsConfigFile)) {
$gc = (require $this->groupsConfigFile);
$keys = explode(',', $key);
foreach ($keys as $key) {
if (isset($gc[$key])) {
$this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified);
if (!isset($gc[$key])) {
// this can happen if value is null
// which could be solved with array_filter
continue;
}
$this->_lastModified = self::getLastModified($gc[$key], $this->_lastModified);
}
}
}
@@ -139,26 +147,41 @@ class Minify_HTML_Helper {
public static function getLastModified($sources, $lastModified = 0)
{
$max = $lastModified;
$factory = self::app()->sourceFactory;
/** @var Minify_Source $source */
foreach ((array)$sources as $source) {
if (is_object($source) && isset($source->lastModified)) {
$max = max($max, $source->lastModified);
} elseif (is_string($source)) {
if (0 === strpos($source, '//')) {
$source = $_SERVER['DOCUMENT_ROOT'] . substr($source, 1);
}
if (is_file($source)) {
$max = max($max, filemtime($source));
}
}
$source = $factory->makeSource($source);
$max = max($max, $source->getLastModified());
}
return $max;
}
/**
* @param \Minify\App $app
* @return \Minify\App
* @internal
*/
public static function app(?\Minify\App $app = null)
{
static $cached;
if ($app) {
$cached = $app;
return $app;
}
if ($cached === null) {
$cached = (require __DIR__ . '/../../../bootstrap.php');
}
return $cached;
}
protected $_groupKey = null; // if present, URI will be like g=...
protected $_filePaths = array();
protected $_lastModified = null;
/**
* In a given array of strings, find the character they all have at
* a particular index
@@ -167,7 +190,8 @@ class Minify_HTML_Helper {
* @param int $pos index to check
* @return mixed a common char or '' if any do not match
*/
protected static function _getCommonCharAtPos($arr, $pos) {
protected static function _getCommonCharAtPos($arr, $pos)
{
if (!isset($arr[0][$pos])) {
return '';
}
@@ -181,6 +205,7 @@ class Minify_HTML_Helper {
return '';
}
}
return $c;
}
@@ -191,7 +216,8 @@ class Minify_HTML_Helper {
* @param string $minRoot root-relative URI of the "min" application
* @return string
*/
protected static function _getShortestUri($paths, $minRoot = '/min/') {
protected static function _getShortestUri($paths, $minRoot = '/min/')
{
$pos = 0;
$base = '';
while (true) {
@@ -205,7 +231,7 @@ class Minify_HTML_Helper {
}
$base = preg_replace('@[^/]+$@', '', $base);
$uri = $minRoot . 'f=' . implode(',', $paths);
if (substr($base, -1) === '/') {
// we have a base dir!
$basedPaths = $paths;
@@ -216,10 +242,9 @@ class Minify_HTML_Helper {
$base = substr($base, 0, strlen($base) - 1);
$bUri = $minRoot . 'b=' . $base . '&f=' . implode(',', $basedPaths);
$uri = strlen($uri) < strlen($bUri)
? $uri
: $bUri;
$uri = strlen($uri) < strlen($bUri) ? $uri : $bUri;
}
return $uri;
}
}

View File

@@ -1,216 +1,217 @@
<?php
/**
* Class Minify_ImportProcessor
* @package Minify
*/
/**
* Linearize a CSS/JS file by including content specified by CSS import
* declarations. In CSS files, relative URIs are fixed.
*
* @imports will be processed regardless of where they appear in the source
* files; i.e. @imports commented out or in string content will still be
* processed!
*
* This has a unit test but should be considered "experimental".
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author Simon Schick <simonsimcity@gmail.com>
*/
class Minify_ImportProcessor {
public static $filesIncluded = array();
public static function process($file)
{
self::$filesIncluded = array();
self::$_isCss = (strtolower(substr($file, -4)) === '.css');
$obj = new Minify_ImportProcessor(dirname($file));
return $obj->_getContent($file);
}
// allows callback funcs to know the current directory
private $_currentDir = null;
// allows callback funcs to know the directory of the file that inherits this one
private $_previewsDir = null;
// allows _importCB to write the fetched content back to the obj
private $_importedContent = '';
private static $_isCss = null;
/**
* @param String $currentDir
* @param String $previewsDir Is only used internally
*/
private function __construct($currentDir, $previewsDir = "")
{
$this->_currentDir = $currentDir;
$this->_previewsDir = $previewsDir;
}
private function _getContent($file, $is_imported = false)
{
$file = realpath($file);
if (! $file
|| in_array($file, self::$filesIncluded)
|| false === ($content = @file_get_contents($file))
) {
// file missing, already included, or failed read
return '';
}
self::$filesIncluded[] = realpath($file);
$this->_currentDir = dirname($file);
// remove UTF-8 BOM if present
if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) {
$content = substr($content, 3);
}
// ensure uniform EOLs
$content = str_replace("\r\n", "\n", $content);
// process @imports
$content = preg_replace_callback(
'/
@import\\s+
(?:url\\(\\s*)? # maybe url(
[\'"]? # maybe quote
(.*?) # 1 = URI
[\'"]? # maybe end quote
(?:\\s*\\))? # maybe )
([a-zA-Z,\\s]*)? # 2 = media list
; # end token
/x'
,array($this, '_importCB')
,$content
);
// You only need to rework the import-path if the script is imported
if (self::$_isCss && $is_imported) {
// rewrite remaining relative URIs
$content = preg_replace_callback(
'/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
,array($this, '_urlCB')
,$content
);
}
return $this->_importedContent . $content;
}
private function _importCB($m)
{
$url = $m[1];
$mediaList = preg_replace('/\\s+/', '', $m[2]);
if (strpos($url, '://') > 0) {
// protocol, leave in place for CSS, comment for JS
return self::$_isCss
? $m[0]
: "/* Minify_ImportProcessor will not include remote content */";
}
if ('/' === $url[0]) {
// protocol-relative or root path
$url = ltrim($url, '/');
$file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR
. strtr($url, '/', DIRECTORY_SEPARATOR);
} else {
// relative to current path
$file = $this->_currentDir . DIRECTORY_SEPARATOR
. strtr($url, '/', DIRECTORY_SEPARATOR);
}
$obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir);
$content = $obj->_getContent($file, true);
if ('' === $content) {
// failed. leave in place for CSS, comment for JS
return self::$_isCss
? $m[0]
: "/* Minify_ImportProcessor could not fetch '{$file}' */";
}
return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList))
? $content
: "@media {$mediaList} {\n{$content}\n}\n";
}
private function _urlCB($m)
{
// $m[1] is either quoted or not
$quote = ($m[1][0] === "'" || $m[1][0] === '"')
? $m[1][0]
: '';
$url = ($quote === '')
? $m[1]
: substr($m[1], 1, strlen($m[1]) - 2);
if ('/' !== $url[0]) {
if (strpos($url, '//') > 0) {
// probably starts with protocol, do not alter
} else {
// prepend path with current dir separator (OS-independent)
$path = $this->_currentDir
. DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
// update the relative path by the directory of the file that imported this one
$url = self::getPathDiff(realpath($this->_previewsDir), $path);
}
}
return "url({$quote}{$url}{$quote})";
}
/**
* @param string $from
* @param string $to
* @param string $ps
* @return string
*/
private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR)
{
$realFrom = $this->truepath($from);
$realTo = $this->truepath($to);
$arFrom = explode($ps, rtrim($realFrom, $ps));
$arTo = explode($ps, rtrim($realTo, $ps));
while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0]))
{
array_shift($arFrom);
array_shift($arTo);
}
return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo);
}
/**
* This function is to replace PHP's extremely buggy realpath().
* @param string $path The original path, can be relative etc.
* @return string The resolved path, it might not exist.
* @see http://stackoverflow.com/questions/4049856/replace-phps-realpath
*/
function truepath($path)
{
// whether $path is unix or not
$unipath = strlen($path) == 0 || $path{0} != '/';
// attempts to detect if path is relative in which case, add cwd
if (strpos($path, ':') === false && $unipath)
$path = $this->_currentDir . DIRECTORY_SEPARATOR . $path;
// resolve path parts (single dot, double dot and double delimiters)
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = array();
foreach ($parts as $part) {
if ('.' == $part)
continue;
if ('..' == $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
$path = implode(DIRECTORY_SEPARATOR, $absolutes);
// resolve any symlinks
if (file_exists($path) && linkinfo($path) > 0)
$path = readlink($path);
// put initial separator that could have been lost
$path = !$unipath ? '/' . $path : $path;
return $path;
}
}
<?php
/**
* Class Minify_ImportProcessor
* @package Minify
*/
/**
* Linearize a CSS/JS file by including content specified by CSS import
* declarations. In CSS files, relative URIs are fixed.
*
* @imports will be processed regardless of where they appear in the source
* files; i.e. @imports commented out or in string content will still be
* processed!
*
* This has a unit test but should be considered "experimental".
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author Simon Schick <simonsimcity@gmail.com>
*/
class Minify_ImportProcessor
{
public static $filesIncluded = array();
public static function process($file)
{
self::$filesIncluded = array();
self::$_isCss = (strtolower(substr($file, -4)) === '.css');
$obj = new Minify_ImportProcessor(dirname($file));
return $obj->_getContent($file);
}
// allows callback funcs to know the current directory
private $_currentDir;
// allows callback funcs to know the directory of the file that inherits this one
private $_previewsDir;
// allows _importCB to write the fetched content back to the obj
private $_importedContent = '';
private static $_isCss;
/**
* @param String $currentDir
* @param String $previewsDir Is only used internally
*/
private function __construct($currentDir, $previewsDir = "")
{
$this->_currentDir = $currentDir;
$this->_previewsDir = $previewsDir;
}
private function _getContent($file, $is_imported = false)
{
$file = preg_replace('~\\?.*~', '', $file);
$file = realpath($file);
if (! $file
|| in_array($file, self::$filesIncluded)
|| false === ($content = @file_get_contents($file))) {
// file missing, already included, or failed read
return '';
}
self::$filesIncluded[] = realpath($file);
$this->_currentDir = dirname($file);
// remove UTF-8 BOM if present
if (pack("CCC", 0xef, 0xbb, 0xbf) === substr($content, 0, 3)) {
$content = substr($content, 3);
}
// ensure uniform EOLs
$content = str_replace("\r\n", "\n", $content);
// process @imports
$pattern = '/
@import\\s+
(?:url\\(\\s*)? # maybe url(
[\'"]? # maybe quote
(.*?) # 1 = URI
[\'"]? # maybe end quote
(?:\\s*\\))? # maybe )
([a-zA-Z,\\s]*)? # 2 = media list
; # end token
/x';
$content = preg_replace_callback($pattern, array($this, '_importCB'), $content);
// You only need to rework the import-path if the script is imported
if (self::$_isCss && $is_imported) {
// rewrite remaining relative URIs
$pattern = '/url\\(\\s*([^\\)\\s]+)\\s*\\)/';
$content = preg_replace_callback($pattern, array($this, '_urlCB'), $content);
}
return $this->_importedContent . $content;
}
private function _importCB($m)
{
$url = $m[1];
$mediaList = preg_replace('/\\s+/', '', $m[2]);
if (strpos($url, '://') > 0) {
// protocol, leave in place for CSS, comment for JS
return self::$_isCss
? $m[0]
: "/* Minify_ImportProcessor will not include remote content */";
}
if ('/' === $url[0]) {
// protocol-relative or root path
$url = ltrim($url, '/');
$file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR
. strtr($url, '/', DIRECTORY_SEPARATOR);
} else {
// relative to current path
$file = $this->_currentDir . DIRECTORY_SEPARATOR
. strtr($url, '/', DIRECTORY_SEPARATOR);
}
$obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir);
$content = $obj->_getContent($file, true);
if ('' === $content) {
// failed. leave in place for CSS, comment for JS
return self::$_isCss
? $m[0]
: "/* Minify_ImportProcessor could not fetch '{$file}' */";
}
return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList))
? $content
: "@media {$mediaList} {\n{$content}\n}\n";
}
private function _urlCB($m)
{
// $m[1] is either quoted or not
$quote = ($m[1][0] === "'" || $m[1][0] === '"') ? $m[1][0] : '';
$url = ($quote === '') ? $m[1] : substr($m[1], 1, strlen($m[1]) - 2);
if ('/' !== $url[0]) {
if (strpos($url, '//') > 0) {
// probably starts with protocol, do not alter
} else {
// prepend path with current dir separator (OS-independent)
$path = $this->_currentDir
. DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
// update the relative path by the directory of the file that imported this one
$url = self::getPathDiff(realpath($this->_previewsDir), $path);
}
}
return "url({$quote}{$url}{$quote})";
}
/**
* @param string $from
* @param string $to
* @param string $ps
* @return string
*/
private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR)
{
$realFrom = $this->truepath($from);
$realTo = $this->truepath($to);
$arFrom = explode($ps, rtrim($realFrom, $ps));
$arTo = explode($ps, rtrim($realTo, $ps));
while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) {
array_shift($arFrom);
array_shift($arTo);
}
return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo);
}
/**
* This function is to replace PHP's extremely buggy realpath().
* @param string $path The original path, can be relative etc.
* @return string The resolved path, it might not exist.
* @see http://stackoverflow.com/questions/4049856/replace-phps-realpath
*/
private function truepath($path)
{
// whether $path is unix or not
$unipath = ('' === $path) || ($path[0] !== '/');
// attempts to detect if path is relative in which case, add cwd
if (strpos($path, ':') === false && $unipath) {
$path = $this->_currentDir . DIRECTORY_SEPARATOR . $path;
}
// resolve path parts (single dot, double dot and double delimiters)
$path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
$parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
$absolutes = array();
foreach ($parts as $part) {
if ('.' === $part) {
continue;
}
if ('..' === $part) {
array_pop($absolutes);
} else {
$absolutes[] = $part;
}
}
$path = implode(DIRECTORY_SEPARATOR, $absolutes);
// resolve any symlinks
if (file_exists($path) && linkinfo($path) > 0) {
$path = readlink($path);
}
// put initial separator that could have been lost
$path = !$unipath ? '/' . $path : $path;
return $path;
}
}

View File

@@ -13,7 +13,8 @@
*
* @todo can use a stream wrapper to unit test this?
*/
class Minify_JS_ClosureCompiler {
class Minify_JS_ClosureCompiler
{
/**
* @var string The option key for the maximum POST byte size
@@ -53,7 +54,7 @@ class Minify_JS_ClosureCompiler {
/**
* @var string $url URL of compiler server. defaults to Google's
*/
protected $serviceUrl = 'http://closure-compiler.appspot.com/compile';
protected $serviceUrl = 'https://closure-compiler.appspot.com/compile';
/**
* @var int $maxBytes The maximum JS size that can be sent to the compiler server in bytes
@@ -68,7 +69,7 @@ class Minify_JS_ClosureCompiler {
/**
* @var callable Function to minify JS if service fails. Default is JSMin
*/
protected $fallbackMinifier = array('JSMin', 'minify');
protected $fallbackMinifier = array('JSMin\\JSMin', 'minify');
/**
* Minify JavaScript code via HTTP request to a Closure Compiler API
@@ -81,6 +82,7 @@ class Minify_JS_ClosureCompiler {
public static function minify($js, array $options = array())
{
$obj = new self($options);
return $obj->min($js);
}
@@ -172,6 +174,9 @@ class Minify_JS_ClosureCompiler {
$contents = file_get_contents($this->serviceUrl, false, stream_context_create(array(
'http' => array(
'method' => 'POST',
'compilation_level' => 'SIMPLE',
'output_format' => 'text',
'output_info' => 'compiled_code',
'header' => "Content-type: application/x-www-form-urlencoded\r\nConnection: close\r\n",
'content' => $postBody,
'max_redirects' => 0,
@@ -190,13 +195,13 @@ class Minify_JS_ClosureCompiler {
curl_close($ch);
} else {
throw new Minify_JS_ClosureCompiler_Exception(
"Could not make HTTP request: allow_url_open is false and cURL not available"
"Could not make HTTP request: allow_url_open is false and cURL not available"
);
}
if (false === $contents) {
throw new Minify_JS_ClosureCompiler_Exception(
"No HTTP response from server"
"No HTTP response from server"
);
}
@@ -227,4 +232,6 @@ class Minify_JS_ClosureCompiler {
}
}
class Minify_JS_ClosureCompiler_Exception extends Exception {}
class Minify_JS_ClosureCompiler_Exception extends Exception
{
}

48
lib/Minify/JS/JShrink.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
/**
* Class Minify\JS\JShrink
*
* @package Minify
*/
namespace Minify\JS;
/**
* Wrapper to Javascript Minifier built in PHP http://www.tedivm.com
*
* @package Minify
* @author Elan Ruusamäe <glen@pld-linux.org>
* @link https://github.com/tedious/JShrink
*
*/
class JShrink
{
/**
* Contains the default options for minification. This array is merged with
* the one passed in by the user to create the request specific set of
* options (stored in the $options attribute).
*
* @var string[]
*/
protected static $defaultOptions = array('flaggedComments' => true);
/**
* Takes a string containing javascript and removes unneeded characters in
* order to shrink the code without altering it's functionality.
*
* @param string $js The raw javascript to be minified
* @param array $options Various runtime options in an associative array
*
* @see JShrink\Minifier::minify()
* @return string
*/
public static function minify($js, array $options = array())
{
$options = array_merge(
self::$defaultOptions,
$options
);
return \JShrink\Minifier::minify($js, $options);
}
}

View File

@@ -0,0 +1,128 @@
<?php
class Minify_LessCssSource extends Minify_Source
{
/**
* @var Minify_CacheInterface
*/
private $cache;
/**
* Parsed lessphp cache object
*
* @var array
*/
private $parsed;
/**
* @inheritdoc
*/
public function __construct(array $spec, Minify_CacheInterface $cache)
{
parent::__construct($spec);
$this->cache = $cache;
}
/**
* Get last modified of all parsed files
*
* @return int
*/
public function getLastModified()
{
$cache = $this->getCache();
return $cache['lastModified'];
}
/**
* Get content
*
* @return string
*/
public function getContent()
{
$cache = $this->getCache();
return $cache['compiled'];
}
/**
* Get lessphp cache object
*
* @return array
*/
private function getCache()
{
// cache for single run
// so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache)
if (isset($this->parsed)) {
return $this->parsed;
}
// check from cache first
$cache = null;
$cacheId = $this->getCacheId();
if ($this->cache->isValid($cacheId, 0)) {
if ($cache = $this->cache->fetch($cacheId)) {
$cache = unserialize($cache);
}
}
$less = $this->getCompiler();
$input = $cache ? $cache : $this->filepath;
$cache = $less->cachedCompile($input);
if (!is_array($input) || $cache['updated'] > $input['updated']) {
$cache['lastModified'] = $this->getMaxLastModified($cache);
$this->cache->store($cacheId, serialize($cache));
}
return $this->parsed = $cache;
}
/**
* Calculate maximum last modified of all files,
* as the 'updated' timestamp in cache is not the same as file last modified timestamp:
* @link https://github.com/leafo/lessphp/blob/v0.4.0/lessc.inc.php#L1904
* @return int
*/
private function getMaxLastModified($cache)
{
$lastModified = 0;
foreach ($cache['files'] as $mtime) {
$lastModified = max($lastModified, $mtime);
}
return $lastModified;
}
/**
* Make a unique cache id for for this source.
*
* @param string $prefix
*
* @return string
*/
private function getCacheId($prefix = 'minify')
{
$md5 = md5($this->filepath);
return "{$prefix}_less2_{$md5}";
}
/**
* Get instance of less compiler
*
* @return lessc
*/
private function getCompiler()
{
$less = new lessc();
// do not spend CPU time letting less doing minify
$less->setPreserveComments(true);
return $less;
}
}

209
lib/Minify/Lines.php Normal file
View File

@@ -0,0 +1,209 @@
<?php
/**
* Class Minify_Lines
* @package Minify
*/
/**
* Add line numbers in C-style comments for easier debugging of combined content
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
* @author Adam Pedersen (Issue 55 fix)
*/
class Minify_Lines
{
/**
* Add line numbers in C-style comments
*
* This uses a very basic parser easily fooled by comment tokens inside
* strings or regexes, but, otherwise, generally clean code will not be
* mangled. URI rewriting can also be performed.
*
* @param string $content
*
* @param array $options available options:
*
* 'id': (optional) string to identify file. E.g. file name/path
*
* 'currentDir': (default null) if given, this is assumed to be the
* directory of the current CSS file. Using this, minify will rewrite
* all relative URIs in import/url declarations to correctly point to
* the desired files, and prepend a comment with debugging information about
* this process.
*
* @return string
*/
public static function minify($content, $options = array())
{
$id = (isset($options['id']) && $options['id']) ? $options['id'] : '';
$content = str_replace("\r\n", "\n", $content);
$lines = explode("\n", $content);
$numLines = count($lines);
// determine left padding
$padTo = strlen((string) $numLines); // e.g. 103 lines = 3 digits
$inComment = false;
$i = 0;
$newLines = array();
while (null !== ($line = array_shift($lines))) {
if (('' !== $id) && (0 === $i % 50)) {
if ($inComment) {
array_push($newLines, '', "/* {$id} *|", '');
} else {
array_push($newLines, '', "/* {$id} */", '');
}
}
++$i;
$newLines[] = self::_addNote($line, $i, $inComment, $padTo);
$inComment = self::_eolInComment($line, $inComment);
}
$content = implode("\n", $newLines) . "\n";
// check for desired URI rewriting
if (isset($options['currentDir'])) {
Minify_CSS_UriRewriter::$debugText = '';
$docRoot = isset($options['docRoot']) ? $options['docRoot'] : $_SERVER['DOCUMENT_ROOT'];
$symlinks = isset($options['symlinks']) ? $options['symlinks'] : array();
$content = Minify_CSS_UriRewriter::rewrite($content, $options['currentDir'], $docRoot, $symlinks);
$content = "/* Minify_CSS_UriRewriter::\$debugText\n\n"
. Minify_CSS_UriRewriter::$debugText . "*/\n"
. $content;
}
return $content;
}
/**
* Is the parser within a C-style comment at the end of this line?
*
* @param string $line current line of code
*
* @param bool $inComment was the parser in a C-style comment at the
* beginning of the previous line?
*
* @return bool
*/
private static function _eolInComment($line, $inComment)
{
while (strlen($line)) {
if ($inComment) {
// only "*/" can end the comment
$index = self::_find($line, '*/');
if ($index === false) {
return true;
}
// stop comment and keep walking line
$inComment = false;
@$line = (string)substr($line, $index + 2);
continue;
}
// look for "//" and "/*"
$single = self::_find($line, '//');
$multi = self::_find($line, '/*');
if ($multi === false) {
return false;
}
if ($single === false || $multi < $single) {
// start comment and keep walking line
$inComment = true;
@$line = (string)substr($line, $multi + 2);
continue;
}
// a single-line comment preceeded it
return false;
}
return $inComment;
}
/**
* Prepend a comment (or note) to the given line
*
* @param string $line current line of code
*
* @param string $note content of note/comment
*
* @param bool $inComment was the parser in a comment at the
* beginning of the line?
*
* @param int $padTo minimum width of comment
*
* @return string
*/
private static function _addNote($line, $note, $inComment, $padTo)
{
if ($inComment) {
$line = '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' *| ' . $line;
} else {
$line = '/* ' . str_pad($note, $padTo, ' ', STR_PAD_RIGHT) . ' */ ' . $line;
}
return rtrim($line);
}
/**
* Find a token trying to avoid false positives
*
* @param string $str String containing the token
* @param string $token Token being checked
* @return bool
*/
private static function _find($str, $token)
{
switch ($token) {
case '//':
$fakes = array(
'://' => 1,
'"//' => 1,
'\'//' => 1,
'".//' => 2,
'\'.//' => 2,
);
break;
case '/*':
$fakes = array(
'"/*' => 1,
'\'/*' => 1,
'"//*' => 2,
'\'//*' => 2,
'".//*' => 3,
'\'.//*' => 3,
'*/*' => 1,
'\\/*' => 1,
);
break;
default:
$fakes = array();
}
$index = strpos($str, $token);
$offset = 0;
while ($index !== false) {
foreach ($fakes as $fake => $skip) {
$check = substr($str, $index - $skip, strlen($fake));
if ($check === $fake) {
// move offset and scan again
$offset += $index + strlen($token);
$index = strpos($str, $token, $offset);
break;
}
}
// legitimate find
return $index;
}
return $index;
}
}

View File

@@ -0,0 +1,24 @@
<?php
namespace Minify\Logger;
use Monolog\Handler\AbstractProcessingHandler;
class LegacyHandler extends AbstractProcessingHandler
{
private $obj;
public function __construct($obj)
{
if (!is_callable(array($obj, 'log'))) {
throw new \InvalidArgumentException('$obj must have a public log() method');
}
$this->obj = $obj;
parent::__construct();
}
protected function write(array $record)
{
$this->obj->log((string)$record['formatted']);
}
}

View File

@@ -0,0 +1,113 @@
<?php
/**
* Class Minify_ClosureCompiler
* @package Minify
*/
/**
* Run Closure Compiler via NailGun
*
* @package Minify
* @author Elan Ruusamäe <glen@delfi.ee>
* @link https://github.com/martylamb/nailgun
*/
class Minify_NailgunClosureCompiler extends Minify_ClosureCompiler
{
const NG_SERVER = 'com.martiansoftware.nailgun.NGServer';
const CC_MAIN = 'com.google.javascript.jscomp.CommandLineRunner';
/**
* For some reasons Nailgun thinks that it's server
* broke the connection and returns 227 instead of 0
* We'll just handle this here instead of fixing
* the nailgun client itself.
*
* It also sometimes breaks on 229 on the devbox.
* To complete this whole madness and made future
* 'fixes' easier I added this nice little array...
* @var array
*/
private static $NG_EXIT_CODES = array(0, 227, 229);
/**
* Filepath of "ng" executable (from Nailgun package)
*
* @var string
*/
public static $ngExecutable = 'ng';
/**
* Filepath of the Nailgun jar file.
*
* @var string
*/
public static $ngJarFile;
/**
* Get command to launch NailGun server.
*
* @return array
*/
protected function getServerCommandLine()
{
$this->checkJar(self::$ngJarFile);
$this->checkJar(self::$jarFile);
$classPath = array(
self::$ngJarFile,
self::$jarFile,
);
// The command for the server that should show up in the process list
$server = array(
self::$javaExecutable,
'-server',
'-cp', implode(':', $classPath),
self::NG_SERVER,
);
return $server;
}
/**
* @return array
* @throws Minify_ClosureCompiler_Exception
*/
protected function getCompilerCommandLine()
{
$server = array(
self::$ngExecutable,
escapeshellarg(self::CC_MAIN)
);
return $server;
}
/**
* @param string $tmpFile
* @param array $options
* @return string
* @throws Minify_ClosureCompiler_Exception
*/
protected function compile($tmpFile, $options)
{
$this->startServer();
$command = $this->getCommand($options, $tmpFile);
return implode("\n", $this->shell($command, self::$NG_EXIT_CODES));
}
private function startServer()
{
$serverCommand = implode(' ', $this->getServerCommandLine());
$psCommand = $this->shell("ps -o cmd= -C " . self::$javaExecutable);
if (in_array($serverCommand, $psCommand, true)) {
// already started!
return;
}
$this->shell("$serverCommand </dev/null >/dev/null 2>/dev/null & sleep 10");
}
}

View File

@@ -4,34 +4,28 @@
*
* To use this class you must first download the PHP port of Packer
* and place the file "class.JavaScriptPacker.php" in /lib (or your
* include_path).
* include_path).
* @link http://joliclic.free.fr/php/javascript-packer/en/
*
* Be aware that, as long as HTTP encoding is used, scripts minified with JSMin
* will provide better client-side performance, as they need not be unpacked in
* client-side code.
*
* @package Minify
*
* @package Minify
*/
if (false === (@include 'class.JavaScriptPacker.php')) {
trigger_error(
'The script "class.JavaScriptPacker.php" is required. Please see: http:'
.'//code.google.com/p/minify/source/browse/trunk/min/lib/Minify/Packer.php'
,E_USER_ERROR
);
}
/**
* Minify Javascript using Dean Edward's Packer
*
*
* @package Minify
*/
class Minify_Packer {
class Minify_Packer
{
public static function minify($code, $options = array())
{
// @todo: set encoding options based on $options :)
$packer = new JavascriptPacker($code, 'Normal', true, false);
return trim($packer->pack());
}
}

View File

@@ -0,0 +1,175 @@
<?php
use ScssPhp\ScssPhp\Compiler;
use ScssPhp\ScssPhp\Version;
/**
* Class for using SCSS files
*
* @link https://github.com/leafo/scssphp/
*/
class Minify_ScssCssSource extends Minify_Source
{
/**
* @var Minify_CacheInterface
*/
private $cache;
/**
* Parsed SCSS cache object
*
* @var array
*/
private $parsed;
/**
* @inheritdoc
*/
public function __construct(array $spec, Minify_CacheInterface $cache)
{
parent::__construct($spec);
$this->cache = $cache;
}
/**
* Get last modified of all parsed files
*
* @return int
*/
public function getLastModified()
{
$cache = $this->getCache();
return $cache['updated'];
}
/**
* Get content
*
* @return string
*/
public function getContent()
{
$cache = $this->getCache();
return $cache['content'];
}
/**
* Make a unique cache id for for this source.
*
* @param string $prefix
*
* @return string
*/
private function getCacheId($prefix = 'minify')
{
$md5 = md5($this->filepath);
return "{$prefix}_scss_{$md5}";
}
/**
* Get SCSS cache object
*
* Runs the compilation if needed
*
* Implements Leafo\ScssPhp\Server logic because we need to get parsed files without parsing actual content
*
* @return array
*/
private function getCache()
{
// cache for single run
// so that getLastModified and getContent in single request do not add additional cache roundtrips (i.e memcache)
if (isset($this->parsed)) {
return $this->parsed;
}
// check from cache first
$cache = null;
$cacheId = $this->getCacheId();
if ($this->cache->isValid($cacheId, 0)) {
if ($cache = $this->cache->fetch($cacheId)) {
$cache = unserialize($cache);
}
}
$input = $cache ? $cache : $this->filepath;
if ($this->cacheIsStale($cache)) {
$cache = $this->compile($this->filepath);
}
if (!is_array($input) || $cache['updated'] > $input['updated']) {
$this->cache->store($cacheId, serialize($cache));
}
return $this->parsed = $cache;
}
/**
* Determine whether .scss file needs to be re-compiled.
*
* @param array $cache Cache object
*
* @return boolean True if compile required.
*/
private function cacheIsStale($cache)
{
if (!$cache) {
return true;
}
$updated = $cache['updated'];
foreach ($cache['files'] as $import => $mtime) {
$filemtime = filemtime($import);
if ($filemtime !== $mtime || $filemtime > $updated) {
return true;
}
}
return false;
}
/**
* Compile .scss file
*
* @param string $filename Input path (.scss)
*
* @see Server::compile()
* @return array meta data result of the compile
*/
private function compile($filename)
{
$start = microtime(true);
$scss = new Compiler();
// set import path directory the input filename resides
// otherwise @import statements will not find the files
// and will treat the @import line as css import
$scss->setImportPaths(dirname($filename));
$css = $scss->compileString(file_get_contents($filename), $filename)->getCss();
$elapsed = round((microtime(true) - $start), 4);
$v = Version::VERSION;
$ts = date('r', (int) $start);
$css = "/* compiled by scssphp $v on $ts ({$elapsed}s) */\n\n" . $css;
$imports = $scss->getParsedFiles();
$updated = 0;
foreach ($imports as $mtime) {
$updated = max($updated, $mtime);
}
return array(
'elapsed' => $elapsed, // statistic, can be dropped
'updated' => $updated,
'content' => $css,
'files' => $imports,
);
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* Class Minify_ServeConfiguration
* @package Minify
*/
/**
* A configuration for Minify::serve() determined by a controller
*
* @package Minify
*/
class Minify_ServeConfiguration
{
/**
* @var Minify_SourceInterface[]
*/
protected $sources;
/**
* @var array
*/
protected $options;
/**
* @var string
*/
protected $selectionId = '';
/**
* @param array $options
* @param Minify_SourceInterface[] $sources
* @param string $selectionId
*/
public function __construct(array $options, array $sources = array(), $selectionId = '')
{
$this->options = $options;
$this->sources = $sources;
$this->selectionId = $selectionId;
}
/**
* @return array
*/
public function getOptions()
{
return $this->options;
}
/**
* @return Minify_SourceInterface[]
*/
public function getSources()
{
return $this->sources;
}
/**
* Short name to place inside cache id
*
* The setupSources() method may choose to set this, making it easier to
* recognize a particular set of sources/settings in the cache folder. It
* will be filtered and truncated to make the final cache id <= 250 bytes.
*
* @return string
*/
public function getSelectionId()
{
return $this->selectionId;
}
}

219
lib/Minify/Source.php Normal file
View File

@@ -0,0 +1,219 @@
<?php
/**
* Class Minify_Source
* @package Minify
*/
/**
* A content source to be minified by Minify.
*
* This allows per-source minification options and the mixing of files with
* content from other sources.
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_Source implements Minify_SourceInterface
{
/**
* @var int time of last modification
*/
protected $lastModified;
/**
* @var callback minifier function specifically for this source.
*/
protected $minifier;
/**
* @var array minification options specific to this source.
*/
protected $minifyOptions = array();
/**
* @var string full path of file
*/
protected $filepath;
/**
* @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*)
*/
protected $contentType;
/**
* @var string
*/
protected $content;
/**
* @var callable
*/
protected $getContentFunc;
/**
* @var string
*/
protected $id;
/**
* Create a Minify_Source
*
* In the $spec array(), you can either provide a 'filepath' to an existing
* file (existence will not be checked!) or give 'id' (unique string for
* the content), 'content' (the string content) and 'lastModified'
* (unixtime of last update).
*
* @param array $spec options
*/
public function __construct($spec)
{
if (isset($spec['filepath'])) {
$ext = pathinfo($spec['filepath'], PATHINFO_EXTENSION);
switch ($ext) {
case 'js': $this->contentType = Minify::TYPE_JS;
break;
case 'less': // fallthrough
case 'scss': // fallthrough
case 'css': $this->contentType = Minify::TYPE_CSS;
break;
case 'htm': // fallthrough
case 'html': $this->contentType = Minify::TYPE_HTML;
break;
}
$this->filepath = $spec['filepath'];
$this->id = $spec['filepath'];
// TODO ideally not touch disk in constructor
$this->lastModified = filemtime($spec['filepath']);
if (!empty($spec['uploaderHoursBehind'])) {
// offset for Windows uploaders with out of sync clocks
$this->lastModified += round($spec['uploaderHoursBehind'] * 3600);
}
} elseif (isset($spec['id'])) {
$this->id = 'id::' . $spec['id'];
if (isset($spec['content'])) {
$this->content = $spec['content'];
} else {
$this->getContentFunc = $spec['getContentFunc'];
}
$this->lastModified = isset($spec['lastModified']) ? $spec['lastModified'] : time();
}
if (isset($spec['contentType'])) {
$this->contentType = $spec['contentType'];
}
if (isset($spec['minifier'])) {
$this->setMinifier($spec['minifier']);
}
if (isset($spec['minifyOptions'])) {
$this->minifyOptions = $spec['minifyOptions'];
}
}
/**
* {@inheritdoc}
*/
public function getLastModified()
{
return $this->lastModified;
}
/**
* {@inheritdoc}
*/
public function getMinifier()
{
return $this->minifier;
}
/**
* {@inheritdoc}
*/
public function setMinifier($minifier = null)
{
if ($minifier === '') {
error_log(__METHOD__ . " cannot accept empty string. Use 'Minify::nullMinifier' or 'trim'.");
$minifier = 'Minify::nullMinifier';
}
if ($minifier !== null && !is_callable($minifier, true)) {
throw new InvalidArgumentException('minifier must be null or a valid callable');
}
$this->minifier = $minifier;
}
/**
* {@inheritdoc}
*/
public function getMinifierOptions()
{
return $this->minifyOptions;
}
/**
* {@inheritdoc}
*/
public function setMinifierOptions(array $options)
{
$this->minifyOptions = $options;
}
/**
* {@inheritdoc}
*/
public function getContentType()
{
return $this->contentType;
}
/**
* {@inheritdoc}
*/
public function getContent()
{
if (null === $this->filepath) {
if (null === $this->content) {
$content = call_user_func($this->getContentFunc, $this->id);
} else {
$content = $this->content;
}
} else {
$content = file_get_contents($this->filepath);
}
// remove UTF-8 BOM if present
if (strpos($content, "\xEF\xBB\xBF") === 0) {
return substr($content, 3);
}
return $content;
}
/**
* {@inheritdoc}
*/
public function getId()
{
return $this->id;
}
/**
* {@inheritdoc}
*/
public function getFilePath()
{
return $this->filepath;
}
/**
* {@inheritdoc}
*/
public function setupUriRewrites()
{
if ($this->filepath
&& !isset($this->minifyOptions['currentDir'])
&& !isset($this->minifyOptions['prependRelativePath'])) {
$this->minifyOptions['currentDir'] = dirname($this->filepath);
}
}
}

View File

@@ -0,0 +1,197 @@
<?php
class Minify_Source_Factory
{
/**
* @var array
*/
protected $options;
/**
* @var callable[]
*/
protected $handlers = array();
/**
* @var Minify_Env
*/
protected $env;
/**
* @param Minify_Env $env
* @param array $options
*
* noMinPattern : Pattern matched against basename of the filepath (if present). If the pattern
* matches, Minify will try to avoid re-compressing the resource.
*
* fileChecker : Callable responsible for verifying the existence of the file.
*
* resolveDocRoot : If true, a leading "//" will be replaced with the document root.
*
* checkAllowDirs : If true, the filepath will be verified to be within one of the directories
* specified by allowDirs.
*
* allowDirs : Directory paths in which sources can be served.
*
* uploaderHoursBehind : How many hours behind are the file modification times of uploaded files?
* If you upload files from Windows to a non-Windows server, Windows may report
* incorrect mtimes for the files. Immediately after modifying and uploading a
* file, use the touch command to update the mtime on the server. If the mtime
* jumps ahead by a number of hours, set this variable to that number. If the mtime
* moves back, this should not be needed.
*
* @param Minify_CacheInterface $cache Optional cache for handling .less files.
*
*/
public function __construct(Minify_Env $env, array $options = array(), ?Minify_CacheInterface $cache = null)
{
$this->env = $env;
$this->options = array_merge(array(
'noMinPattern' => '@[-\\.]min\\.(?:[a-zA-Z]+)$@i', // matched against basename
'fileChecker' => array($this, 'checkIsFile'),
'resolveDocRoot' => true,
'checkAllowDirs' => true,
'allowDirs' => array('//'),
'uploaderHoursBehind' => 0,
), $options);
// resolve // in allowDirs
$docRoot = $env->getDocRoot();
foreach ($this->options['allowDirs'] as $i => $dir) {
if (0 === strpos($dir, '//')) {
$this->options['allowDirs'][$i] = $docRoot . substr($dir, 1);
}
}
if ($this->options['fileChecker'] && !is_callable($this->options['fileChecker'])) {
throw new InvalidArgumentException("fileChecker option is not callable");
}
$this->setHandler('~\.less$~i', function ($spec) use ($cache) {
return new Minify_LessCssSource($spec, $cache);
});
$this->setHandler('~\.scss~i', function ($spec) use ($cache) {
return new Minify_ScssCssSource($spec, $cache);
});
$this->setHandler('~\.(js|css)$~i', function ($spec) {
return new Minify_Source($spec);
});
}
/**
* @param string $basenamePattern A pattern tested against basename. E.g. "~\.css$~"
* @param callable $handler Function that recieves a $spec array and returns a Minify_SourceInterface
*/
public function setHandler($basenamePattern, $handler)
{
$this->handlers[$basenamePattern] = $handler;
}
/**
* @param string $file
* @return string
*
* @throws Minify_Source_FactoryException
*/
public function checkIsFile($file)
{
$realpath = realpath($file);
if (!$realpath) {
throw new Minify_Source_FactoryException("File failed realpath(): $file");
}
$basename = basename($file);
if (0 === strpos($basename, '.')) {
throw new Minify_Source_FactoryException("Filename starts with period (may be hidden): $basename");
}
if (!is_file($realpath) || !is_readable($realpath)) {
throw new Minify_Source_FactoryException("Not a file or isn't readable: $file");
}
return $realpath;
}
/**
* @param mixed $spec
*
* @return Minify_SourceInterface
*
* @throws Minify_Source_FactoryException
*/
public function makeSource($spec)
{
if (is_string($spec)) {
$spec = array(
'filepath' => $spec,
);
} elseif ($spec instanceof Minify_SourceInterface) {
return $spec;
}
$source = null;
if (empty($spec['filepath'])) {
// not much we can check
return new Minify_Source($spec);
}
if ($this->options['resolveDocRoot'] && 0 === strpos($spec['filepath'], '//')) {
$spec['filepath'] = $this->env->getDocRoot() . substr($spec['filepath'], 1);
}
if (!empty($this->options['fileChecker'])) {
$spec['filepath'] = call_user_func($this->options['fileChecker'], $spec['filepath']);
}
if ($this->options['checkAllowDirs']) {
$allowDirs = (array)$this->options['allowDirs'];
$inAllowedDir = false;
$filePath = $this->env->normalizePath($spec['filepath']);
foreach ($allowDirs as $allowDir) {
if (strpos($filePath, $this->env->normalizePath($allowDir)) === 0) {
$inAllowedDir = true;
}
}
if (!$inAllowedDir) {
$allowDirsStr = implode(';', $allowDirs);
throw new Minify_Source_FactoryException("File '{$spec['filepath']}' is outside \$allowDirs "
. "($allowDirsStr). If the path is resolved via an alias/symlink, look into the "
. "\$min_symlinks option.");
}
}
$basename = basename($spec['filepath']);
if ($this->options['noMinPattern'] && preg_match($this->options['noMinPattern'], $basename)) {
if (preg_match('~\.(css|less)$~i', $basename)) {
$spec['minifyOptions']['compress'] = false;
// we still want URI rewriting to work for CSS
} else {
$spec['minifier'] = 'Minify::nullMinifier';
}
}
$hoursBehind = $this->options['uploaderHoursBehind'];
if ($hoursBehind != 0) {
$spec['uploaderHoursBehind'] = $hoursBehind;
}
foreach ($this->handlers as $basenamePattern => $handler) {
if (preg_match($basenamePattern, $basename)) {
$source = call_user_func($handler, $spec);
break;
}
}
if (!$source) {
throw new Minify_Source_FactoryException("Handler not found for file: $basename");
}
return $source;
}
}

View File

@@ -0,0 +1,5 @@
<?php
class Minify_Source_FactoryException extends Exception
{
}

View File

@@ -0,0 +1,82 @@
<?php
/**
* Interface Minify_SourceInterface
* @package Minify
*/
/**
* A content source to be minified by Minify.
*
* This allows per-source minification options and the mixing of files with
* content from other sources.
*
* @package Minify
*/
interface Minify_SourceInterface
{
/**
* Get the minifier
*
* @return callable|null
*/
public function getMinifier();
/**
* Set the minifier
*
* @param callable $minifier
* @return void
*/
public function setMinifier($minifier = null);
/**
* Get options for the minifier
*
* @return array
*/
public function getMinifierOptions();
/**
* Set options for the minifier
*
* @param array $options
* @return void
*/
public function setMinifierOptions(array $options);
/**
* Get the content type
*
* @return string|null
*/
public function getContentType();
/**
* Get content
*
* @return string
*/
public function getContent();
/**
* Get last modified timestamp
*
* @return int
*/
public function getLastModified();
/**
* Get id
*
* @return string
*/
public function getId();
/**
* Get the path of the file that this source is based on (may be null)
*
* @return string|null
*/
public function getFilePath();
}

31
lib/Minify/SourceSet.php Normal file
View File

@@ -0,0 +1,31 @@
<?php
/**
* Class Minify_SourceSet
* @package Minify
*/
/**
* @package Minify
*/
class Minify_SourceSet
{
/**
* Get unique string for a set of sources
*
* @param Minify_SourceInterface[] $sources Minify_Source instances
*
* @return string
*/
public static function getDigest($sources)
{
$info = array();
foreach ($sources as $source) {
$info[] = array(
$source->getId(), $source->getMinifier(), $source->getMinifierOptions()
);
}
return md5(serialize($info));
}
}

View File

@@ -1,17 +1,17 @@
<?php
/**
* Class Minify_YUICompressor
* Class Minify_YUICompressor
* @package Minify
*/
/**
* Compress Javascript/CSS using the YUI Compressor
*
*
* You must set $jarFile and $tempDir before calling the minify functions.
* Also, depending on your shell's environment, you may need to specify
* the full path to java in $javaExecutable or use putenv() to setup the
* Java environment.
*
*
* <code>
* Minify_YUICompressor::$jarFile = '/path/to/yuicompressor-2.4.6.jar';
* Minify_YUICompressor::$tempDir = '/tmp';
@@ -25,11 +25,12 @@
* array('stack-size' => '2048k')
*
* @todo unit tests, $options docs
*
*
* @package Minify
* @author Stephen Clay <steve@mrclay.org>
*/
class Minify_YUICompressor {
class Minify_YUICompressor
{
/**
* Filepath of the YUI Compressor jar file. This must be set before
@@ -37,106 +38,106 @@ class Minify_YUICompressor {
*
* @var string
*/
public static $jarFile = null;
public static $jarFile;
/**
* Writable temp directory. This must be set before calling minifyJs()
* or minifyCss().
*
* @var string
*/
public static $tempDir = null;
public static $tempDir;
/**
* Filepath of "java" executable (may be needed if not in shell's PATH)
*
* @var string
*/
public static $javaExecutable = 'java';
/**
* Minify a Javascript string
*
*
* @param string $js
*
*
* @param array $options (verbose is ignored)
*
*
* @see http://www.julienlecomte.net/yuicompressor/README
*
* @return string
*
* @return string
*/
public static function minifyJs($js, $options = array())
{
return self::_minify('js', $js, $options);
}
/**
* Minify a CSS string
*
*
* @param string $css
*
*
* @param array $options (verbose is ignored)
*
*
* @see http://www.julienlecomte.net/yuicompressor/README
*
* @return string
*
* @return string
*/
public static function minifyCss($css, $options = array())
{
return self::_minify('css', $css, $options);
}
private static function _minify($type, $content, $options)
{
self::_prepare();
if (! ($tmpFile = tempnam(self::$tempDir, 'yuic_'))) {
throw new Exception('Minify_YUICompressor : could not create temp file in "'.self::$tempDir.'".');
}
file_put_contents($tmpFile, $content);
exec(self::_getCmd($options, $type, $tmpFile), $output, $result_code);
unlink($tmpFile);
if ($result_code != 0) {
throw new Exception('Minify_YUICompressor : YUI compressor execution failed.');
}
return implode("\n", $output);
}
private static function _getCmd($userOptions, $type, $tmpFile)
{
$o = array_merge(
array(
'charset' => ''
,'line-break' => 5000
,'type' => $type
,'nomunge' => false
,'preserve-semi' => false
,'disable-optimizations' => false
,'stack-size' => ''
)
,$userOptions
$defaults = array(
'charset' => '',
'line-break' => 5000,
'type' => $type,
'nomunge' => false,
'preserve-semi' => false,
'disable-optimizations' => false,
'stack-size' => '',
);
$o = array_merge($defaults, $userOptions);
$cmd = self::$javaExecutable
. (!empty($o['stack-size'])
? ' -Xss' . $o['stack-size']
: '')
. ' -jar ' . escapeshellarg(self::$jarFile)
. (!empty($o['stack-size']) ? ' -Xss' . $o['stack-size'] : '')
. ' -jar ' . escapeshellarg(self::$jarFile)
. " --type {$type}"
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
? " --charset {$o['charset']}"
? " --charset {$o['charset']}"
: '')
. (is_numeric($o['line-break']) && $o['line-break'] >= 0
? ' --line-break ' . (int)$o['line-break']
: '');
if ($type === 'js') {
foreach (array('nomunge', 'preserve-semi', 'disable-optimizations') as $opt) {
$cmd .= $o[$opt]
$cmd .= $o[$opt]
? " --{$opt}"
: '';
}
}
return $cmd . ' ' . escapeshellarg($tmpFile);
}
private static function _prepare()
{
if (! is_file(self::$jarFile)) {
@@ -153,4 +154,3 @@ class Minify_YUICompressor {
}
}
}

View File

@@ -1,4 +1,4 @@
<?php
<?php
namespace MrClay;
@@ -18,16 +18,17 @@ use InvalidArgumentException;
* @author Steve Clay <steve@mrclay.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
class Cli {
class Cli
{
/**
* @var array validation errors
*/
public $errors = array();
/**
* @var array option values available after validation.
*
*
* E.g. array(
* 'a' => false // option was missing
* ,'b' => true // option was present
@@ -67,7 +68,7 @@ class Cli {
* @var resource
*/
protected $_stdout = null;
/**
* @param bool $exitIfNoStdin (default true) Exit() if STDIN is not defined
*/
@@ -107,7 +108,7 @@ class Cli {
* @return Arg
* @throws InvalidArgumentException
*/
public function addArgument($letter, $required, Arg $arg = null)
public function addArgument($letter, $required, ?Arg $arg = null)
{
if (! preg_match('/^[a-zA-Z]$/', $letter)) {
throw new InvalidArgumentException('$letter must be in [a-zA-Z]');
@@ -116,6 +117,7 @@ class Cli {
$arg = new Arg($required);
}
$this->_args[$letter] = $arg;
return $arg;
}
@@ -130,7 +132,7 @@ class Cli {
/*
* Read and validate options
*
*
* @return bool true if all options are valid
*/
public function validate()
@@ -139,17 +141,17 @@ class Cli {
$this->errors = array();
$this->values = array();
$this->_stdin = null;
if ($this->isHelpRequest) {
return false;
}
$lettersUsed = '';
foreach ($this->_args as $letter => $arg) {
/* @var Arg $arg */
$options .= $letter;
$lettersUsed .= $letter;
if ($arg->mayHaveValue || $arg->mustHaveValue) {
$options .= ($arg->mustHaveValue ? ':' : '::');
}
@@ -201,14 +203,15 @@ class Cli {
array_splice($argvCopy, $k, 2);
}
}
// check that value isn't really another option
if (strlen($lettersUsed) > 1) {
$pattern = "/^-[" . str_replace($letter, '', $lettersUsed) . "]/i";
if (preg_match($pattern, $v)) {
$this->addError($letter, "Value was read as another option: %s", $v);
return false;
}
}
}
if ($arg->assertFile || $arg->assertDir) {
if ($v[0] !== '/' && $v[0] !== '~') {
@@ -249,6 +252,7 @@ class Cli {
}
$this->moreArgs = $argvCopy;
reset($this->moreArgs);
return empty($this->errors);
}
@@ -270,12 +274,13 @@ class Cli {
$r[$k] = $v;
}
}
return $r;
}
/**
* Get a short list of errors with options
*
*
* @return string
*/
public function getErrorReport()
@@ -288,6 +293,7 @@ class Cli {
$r .= " $letter : " . implode(', ', $arr) . "\n";
}
$r .= "\n";
return $r;
}
@@ -318,9 +324,10 @@ class Cli {
$desc = wordwrap($desc, 70);
$r .= $flag . str_replace("\n", "\n ", $desc) . "\n\n";
}
return $r;
}
/**
* Get resource of open input stream. May be STDIN or a file pointer
* to the file specified by an option with 'STDIN'.
@@ -333,17 +340,18 @@ class Cli {
return STDIN;
} else {
$this->_stdin = fopen($this->_stdin, 'rb');
return $this->_stdin;
}
}
public function closeInput()
{
if (null !== $this->_stdin) {
fclose($this->_stdin);
}
}
/**
* Get resource of open output stream. May be STDOUT or a file pointer
* to the file specified by an option with 'STDOUT'. The file will be
@@ -357,10 +365,11 @@ class Cli {
return STDOUT;
} else {
$this->_stdout = fopen($this->_stdout, 'wb');
return $this->_stdout;
}
}
public function closeOutput()
{
if (null !== $this->_stdout) {
@@ -381,4 +390,3 @@ class Cli {
$this->errors[$letter][] = sprintf($msg, $value);
}
}

View File

@@ -43,7 +43,8 @@ use BadMethodCallException;
* @author Steve Clay <steve@mrclay.org>
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
class Arg {
class Arg
{
/**
* @return array
*/
@@ -97,6 +98,7 @@ class Arg {
public function useAsOutfile()
{
$this->spec['useAsOutfile'] = true;
return $this->assertFile()->assertWritable();
}
@@ -109,6 +111,7 @@ class Arg {
public function useAsInfile()
{
$this->spec['useAsInfile'] = true;
return $this->assertFile()->assertReadable();
}
@@ -127,6 +130,7 @@ class Arg {
public function setDescription($desc)
{
$this->description = $desc;
return $this;
}
@@ -164,6 +168,7 @@ class Arg {
} else {
throw new BadMethodCallException('Method does not exist');
}
return $this;
}
@@ -178,6 +183,7 @@ class Arg {
if (array_key_exists($name, $this->spec)) {
return $this->spec[$name];
}
return null;
}
}

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +0,0 @@
<?php
/**
* Groups configuration for default Minify implementation
* @package Minify
*/
/**
* You may wish to use the Minify URI Builder app to suggest
* changes. http://yourdomain/min/builder/
*
* See http://code.google.com/p/minify/wiki/CustomSource for other ideas
**/
return array(
// 'js' => array('//js/file1.js', '//js/file2.js'),
// 'css' => array('//css/file1.css', '//css/file2.css'),
);

View File

@@ -1,92 +0,0 @@
<?php
/**
* Front controller for default Minify implementation
*
* DO NOT EDIT! Configure this utility via config.php and groupsConfig.php
*
* @package Minify
*/
define('MINIFY_MIN_DIR', dirname(__FILE__));
// set config path defaults
$min_configPaths = array(
'base' => MINIFY_MIN_DIR . '/config.php',
'test' => MINIFY_MIN_DIR . '/config-test.php',
'groups' => MINIFY_MIN_DIR . '/groupsConfig.php'
);
// check for custom config paths
if (!empty($min_customConfigPaths) && is_array($min_customConfigPaths)) {
$min_configPaths = array_merge($min_configPaths, $min_customConfigPaths);
}
// load config
require $min_configPaths['base'];
if (isset($_GET['test'])) {
include $min_configPaths['test'];
}
require "$min_libPath/Minify/Loader.php";
Minify_Loader::register();
Minify::$uploaderHoursBehind = $min_uploaderHoursBehind;
Minify::setCache(
isset($min_cachePath) ? $min_cachePath : ''
,$min_cacheFileLocking
);
if ($min_documentRoot) {
$_SERVER['DOCUMENT_ROOT'] = $min_documentRoot;
Minify::$isDocRootSet = true;
}
$min_serveOptions['minifierOptions']['text/css']['symlinks'] = $min_symlinks;
// auto-add targets to allowDirs
foreach ($min_symlinks as $uri => $target) {
$min_serveOptions['minApp']['allowDirs'][] = $target;
}
if ($min_allowDebugFlag) {
$min_serveOptions['debug'] = Minify_DebugDetector::shouldDebugRequest($_COOKIE, $_GET, $_SERVER['REQUEST_URI']);
}
if (!empty($min_concatOnly)) {
$min_serveOptions['concatOnly'] = true;
}
if ($min_errorLogger) {
if (true === $min_errorLogger) {
$min_errorLogger = FirePHP::getInstance(true);
}
Minify_Logger::setLogger($min_errorLogger);
}
// check for URI versioning
if (preg_match('/&\\d/', $_SERVER['QUERY_STRING']) || isset($_GET['v'])) {
$min_serveOptions['maxAge'] = 31536000;
}
// need groups config?
if (isset($_GET['g'])) {
// well need groups config
$min_serveOptions['minApp']['groups'] = (require $min_configPaths['groups']);
}
// serve or redirect
if (isset($_GET['f']) || isset($_GET['g'])) {
if (! isset($min_serveController)) {
$min_serveController = new Minify_Controller_MinApp();
}
Minify::serve($min_serveController, $min_serveOptions);
} elseif ($min_enableBuilder) {
header('Location: builder/');
exit;
} else {
header('Location: /');
exit;
}

View File

@@ -1,777 +0,0 @@
<?php
/*!
* cssmin.php v2.4.8-4
* Author: Tubal Martin - http://tubalmartin.me/
* Repo: https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port
*
* This is a PHP port of the CSS minification tool distributed with YUICompressor,
* itself a port of the cssmin utility by Isaac Schlueter - http://foohack.com/
* Permission is hereby granted to use the PHP version under the same
* conditions as the YUICompressor.
*/
/*!
* YUI Compressor
* http://developer.yahoo.com/yui/compressor/
* Author: Julien Lecomte - http://www.julienlecomte.net/
* Copyright (c) 2013 Yahoo! Inc. All rights reserved.
* The copyrights embodied in the content of this file are licensed
* by Yahoo! Inc. under the BSD (revised) open source license.
*/
class CSSmin
{
const NL = '___YUICSSMIN_PRESERVED_NL___';
const TOKEN = '___YUICSSMIN_PRESERVED_TOKEN_';
const COMMENT = '___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_';
const CLASSCOLON = '___YUICSSMIN_PSEUDOCLASSCOLON___';
const QUERY_FRACTION = '___YUICSSMIN_QUERY_FRACTION___';
private $comments;
private $preserved_tokens;
private $memory_limit;
private $max_execution_time;
private $pcre_backtrack_limit;
private $pcre_recursion_limit;
private $raise_php_limits;
/**
* @param bool|int $raise_php_limits
* If true, PHP settings will be raised if needed
*/
public function __construct($raise_php_limits = TRUE)
{
// Set suggested PHP limits
$this->memory_limit = 128 * 1048576; // 128MB in bytes
$this->max_execution_time = 60; // 1 min
$this->pcre_backtrack_limit = 1000 * 1000;
$this->pcre_recursion_limit = 500 * 1000;
$this->raise_php_limits = (bool) $raise_php_limits;
}
/**
* Minify a string of CSS
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
public function run($css = '', $linebreak_pos = FALSE)
{
if (empty($css)) {
return '';
}
if ($this->raise_php_limits) {
$this->do_raise_php_limits();
}
$this->comments = array();
$this->preserved_tokens = array();
$start_index = 0;
$length = strlen($css);
$css = $this->extract_data_urls($css);
// collect all comment blocks...
while (($start_index = $this->index_of($css, '/*', $start_index)) >= 0) {
$end_index = $this->index_of($css, '*/', $start_index + 2);
if ($end_index < 0) {
$end_index = $length;
}
$comment_found = $this->str_slice($css, $start_index + 2, $end_index);
$this->comments[] = $comment_found;
$comment_preserve_string = self::COMMENT . (count($this->comments) - 1) . '___';
$css = $this->str_slice($css, 0, $start_index + 2) . $comment_preserve_string . $this->str_slice($css, $end_index);
// Set correct start_index: Fixes issue #2528130
$start_index = $end_index + 2 + strlen($comment_preserve_string) - strlen($comment_found);
}
// preserve strings so their content doesn't get accidentally minified
$css = preg_replace_callback('/(?:"(?:[^\\\\"]|\\\\.|\\\\)*")|'."(?:'(?:[^\\\\']|\\\\.|\\\\)*')/S", array($this, 'replace_string'), $css);
// Let's divide css code in chunks of 5.000 chars aprox.
// Reason: PHP's PCRE functions like preg_replace have a "backtrack limit"
// of 100.000 chars by default (php < 5.3.7) so if we're dealing with really
// long strings and a (sub)pattern matches a number of chars greater than
// the backtrack limit number (i.e. /(.*)/s) PCRE functions may fail silently
// returning NULL and $css would be empty.
$charset = '';
$charset_regexp = '/(@charset)( [^;]+;)/i';
$css_chunks = array();
$css_chunk_length = 5000; // aprox size, not exact
$start_index = 0;
$i = $css_chunk_length; // save initial iterations
$l = strlen($css);
// if the number of characters is 5000 or less, do not chunk
if ($l <= $css_chunk_length) {
$css_chunks[] = $css;
} else {
// chunk css code securely
while ($i < $l) {
$i += 50; // save iterations
if ($l - $start_index <= $css_chunk_length || $i >= $l) {
$css_chunks[] = $this->str_slice($css, $start_index);
break;
}
if ($css[$i - 1] === '}' && $i - $start_index > $css_chunk_length) {
// If there are two ending curly braces }} separated or not by spaces,
// join them in the same chunk (i.e. @media blocks)
$next_chunk = substr($css, $i);
if (preg_match('/^\s*\}/', $next_chunk)) {
$i = $i + $this->index_of($next_chunk, '}') + 1;
}
$css_chunks[] = $this->str_slice($css, $start_index, $i);
$start_index = $i;
}
}
}
// Minify each chunk
for ($i = 0, $n = count($css_chunks); $i < $n; $i++) {
$css_chunks[$i] = $this->minify($css_chunks[$i], $linebreak_pos);
// Keep the first @charset at-rule found
if (empty($charset) && preg_match($charset_regexp, $css_chunks[$i], $matches)) {
$charset = strtolower($matches[1]) . $matches[2];
}
// Delete all @charset at-rules
$css_chunks[$i] = preg_replace($charset_regexp, '', $css_chunks[$i]);
}
// Update the first chunk and push the charset to the top of the file.
$css_chunks[0] = $charset . $css_chunks[0];
return implode('', $css_chunks);
}
/**
* Sets the memory limit for this script
* @param int|string $limit
*/
public function set_memory_limit($limit)
{
$this->memory_limit = $this->normalize_int($limit);
}
/**
* Sets the maximum execution time for this script
* @param int|string $seconds
*/
public function set_max_execution_time($seconds)
{
$this->max_execution_time = (int) $seconds;
}
/**
* Sets the PCRE backtrack limit for this script
* @param int $limit
*/
public function set_pcre_backtrack_limit($limit)
{
$this->pcre_backtrack_limit = (int) $limit;
}
/**
* Sets the PCRE recursion limit for this script
* @param int $limit
*/
public function set_pcre_recursion_limit($limit)
{
$this->pcre_recursion_limit = (int) $limit;
}
/**
* Try to configure PHP to use at least the suggested minimum settings
*/
private function do_raise_php_limits()
{
$php_limits = array(
'memory_limit' => $this->memory_limit,
'max_execution_time' => $this->max_execution_time,
'pcre.backtrack_limit' => $this->pcre_backtrack_limit,
'pcre.recursion_limit' => $this->pcre_recursion_limit
);
// If current settings are higher respect them.
foreach ($php_limits as $name => $suggested) {
$current = $this->normalize_int(ini_get($name));
// memory_limit exception: allow -1 for "no memory limit".
if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
ini_set($name, $suggested);
}
}
}
/**
* Does bulk of the minification
* @param string $css
* @param int|bool $linebreak_pos
* @return string
*/
private function minify($css, $linebreak_pos)
{
// strings are safe, now wrestle the comments
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
$token = $this->comments[$i];
$placeholder = '/' . self::COMMENT . $i . '___/';
// ! in the first position of the comment means preserve
// so push to the preserved tokens keeping the !
if (substr($token, 0, 1) === '!') {
$this->preserved_tokens[] = $token;
$token_tring = self::TOKEN . (count($this->preserved_tokens) - 1) . '___';
$css = preg_replace($placeholder, $token_tring, $css, 1);
// Preserve new lines for /*! important comments
$css = preg_replace('/\s*[\n\r\f]+\s*(\/\*'. $token_tring .')/S', self::NL.'$1', $css);
$css = preg_replace('/('. $token_tring .'\*\/)\s*[\n\r\f]+\s*/', '$1'.self::NL, $css);
continue;
}
// \ in the last position looks like hack for Mac/IE5
// shorten that to /*\*/ and the next one to /**/
if (substr($token, (strlen($token) - 1), 1) === '\\') {
$this->preserved_tokens[] = '\\';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
$i = $i + 1; // attn: advancing the loop
$this->preserved_tokens[] = '';
$css = preg_replace('/' . self::COMMENT . $i . '___/', self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
continue;
}
// keep empty comments after child selectors (IE7 hack)
// e.g. html >/**/ body
if (strlen($token) === 0) {
$start_index = $this->index_of($css, $this->str_slice($placeholder, 1, -1));
if ($start_index > 2) {
if (substr($css, $start_index - 3, 1) === '>') {
$this->preserved_tokens[] = '';
$css = preg_replace($placeholder, self::TOKEN . (count($this->preserved_tokens) - 1) . '___', $css, 1);
}
}
}
// in all other cases kill the comment
$css = preg_replace('/\/\*' . $this->str_slice($placeholder, 1, -1) . '\*\//', '', $css, 1);
}
// Normalize all whitespace strings to single spaces. Easier to work with that way.
$css = preg_replace('/\s+/', ' ', $css);
// Fix IE7 issue on matrix filters which browser accept whitespaces between Matrix parameters
$css = preg_replace_callback('/\s*filter\:\s*progid:DXImageTransform\.Microsoft\.Matrix\(([^\)]+)\)/', array($this, 'preserve_old_IE_specific_matrix_definition'), $css);
// Shorten & preserve calculations calc(...) since spaces are important
$css = preg_replace_callback('/calc(\(((?:[^\(\)]+|(?1))*)\))/i', array($this, 'replace_calc'), $css);
// Replace positive sign from numbers preceded by : or a white-space before the leading space is removed
// +1.2em to 1.2em, +.8px to .8px, +2% to 2%
$css = preg_replace('/((?<!\\\\)\:|\s)\+(\.?\d+)/S', '$1$2', $css);
// Remove leading zeros from integer and float numbers preceded by : or a white-space
// 000.6 to .6, -0.8 to -.8, 0050 to 50, -01.05 to -1.05
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)0+(\.?\d+)/S', '$1$2$3', $css);
// Remove trailing zeros from float numbers preceded by : or a white-space
// -6.0100em to -6.01em, .0100 to .01, 1.200px to 1.2px
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?)(\d?\.\d+?)0+([^\d])/S', '$1$2$3$4', $css);
// Remove trailing .0 -> -9.0 to -9
$css = preg_replace('/((?<!\\\\)\:|\s)(\-?\d+)\.0([^\d])/S', '$1$2$3', $css);
// Replace 0 length numbers with 0
$css = preg_replace('/((?<!\\\\)\:|\s)\-?\.?0+([^\d])/S', '${1}0$2', $css);
// Remove the spaces before the things that should not have spaces before them.
// But, be careful not to turn "p :link {...}" into "p:link{...}"
// Swap out any pseudo-class colons with the token, and then swap back.
$css = preg_replace_callback('/(?:^|\})[^\{]*\s+\:/', array($this, 'replace_colon'), $css);
// Remove spaces before the things that should not have spaces before them.
$css = preg_replace('/\s+([\!\{\}\;\:\>\+\(\)\]\~\=,])/', '$1', $css);
// Restore spaces for !important
$css = preg_replace('/\!important/i', ' !important', $css);
// bring back the colon
$css = preg_replace('/' . self::CLASSCOLON . '/', ':', $css);
// retain space for special IE6 cases
$css = preg_replace_callback('/\:first\-(line|letter)(\{|,)/i', array($this, 'lowercase_pseudo_first'), $css);
// no space after the end of a preserved comment
$css = preg_replace('/\*\/ /', '*/', $css);
// lowercase some popular @directives
$css = preg_replace_callback('/@(font-face|import|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?keyframe|media|page|namespace)/i', array($this, 'lowercase_directives'), $css);
// lowercase some more common pseudo-elements
$css = preg_replace_callback('/:(active|after|before|checked|disabled|empty|enabled|first-(?:child|of-type)|focus|hover|last-(?:child|of-type)|link|only-(?:child|of-type)|root|:selection|target|visited)/i', array($this, 'lowercase_pseudo_elements'), $css);
// lowercase some more common functions
$css = preg_replace_callback('/:(lang|not|nth-child|nth-last-child|nth-last-of-type|nth-of-type|(?:-(?:moz|webkit)-)?any)\(/i', array($this, 'lowercase_common_functions'), $css);
// lower case some common function that can be values
// NOTE: rgb() isn't useful as we replace with #hex later, as well as and() is already done for us
$css = preg_replace_callback('/([:,\( ]\s*)(attr|color-stop|from|rgba|to|url|(?:-(?:atsc|khtml|moz|ms|o|wap|webkit)-)?(?:calc|max|min|(?:repeating-)?(?:linear|radial)-gradient)|-webkit-gradient)/iS', array($this, 'lowercase_common_functions_values'), $css);
// Put the space back in some cases, to support stuff like
// @media screen and (-webkit-min-device-pixel-ratio:0){
$css = preg_replace('/\band\(/i', 'and (', $css);
// Remove the spaces after the things that should not have spaces after them.
$css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
// remove unnecessary semicolons
$css = preg_replace('/;+\}/', '}', $css);
// Fix for issue: #2528146
// Restore semicolon if the last property is prefixed with a `*` (lte IE7 hack)
// to avoid issues on Symbian S60 3.x browsers.
$css = preg_replace('/(\*[a-z0-9\-]+\s*\:[^;\}]+)(\})/', '$1;$2', $css);
// Replace 0 <length> and 0 <percentage> values with 0.
// <length> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/length
// <percentage> data type: https://developer.mozilla.org/en-US/docs/Web/CSS/percentage
$css = preg_replace('/([^\\\\]\:|\s)0(?:em|ex|ch|rem|vw|vh|vm|vmin|cm|mm|in|px|pt|pc|%)/iS', '${1}0', $css);
// 0% step in a keyframe? restore the % unit
$css = preg_replace_callback('/(@[a-z\-]*?keyframes[^\{]+\{)(.*?)(\}\})/iS', array($this, 'replace_keyframe_zero'), $css);
// Replace 0 0; or 0 0 0; or 0 0 0 0; with 0.
$css = preg_replace('/\:0(?: 0){1,3}(;|\}| \!)/', ':0$1', $css);
// Fix for issue: #2528142
// Replace text-shadow:0; with text-shadow:0 0 0;
$css = preg_replace('/(text-shadow\:0)(;|\}| \!)/i', '$1 0 0$2', $css);
// Replace background-position:0; with background-position:0 0;
// same for transform-origin
// Changing -webkit-mask-position: 0 0 to just a single 0 will result in the second parameter defaulting to 50% (center)
$css = preg_replace('/(background\-position|webkit-mask-position|(?:webkit|moz|o|ms|)\-?transform\-origin)\:0(;|\}| \!)/iS', '$1:0 0$2', $css);
// Shorten colors from rgb(51,102,153) to #336699, rgb(100%,0%,0%) to #ff0000 (sRGB color space)
// Shorten colors from hsl(0, 100%, 50%) to #ff0000 (sRGB color space)
// This makes it more likely that it'll get further compressed in the next step.
$css = preg_replace_callback('/rgb\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'rgb_to_hex'), $css);
$css = preg_replace_callback('/hsl\s*\(\s*([0-9,\s\-\.\%]+)\s*\)(.{1})/i', array($this, 'hsl_to_hex'), $css);
// Shorten colors from #AABBCC to #ABC or short color name.
$css = $this->compress_hex_colors($css);
// border: none to border:0, outline: none to outline:0
$css = preg_replace('/(border\-?(?:top|right|bottom|left|)|outline)\:none(;|\}| \!)/iS', '$1:0$2', $css);
// shorter opacity IE filter
$css = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $css);
// Find a fraction that is used for Opera's -o-device-pixel-ratio query
// Add token to add the "\" back in later
$css = preg_replace('/\(([a-z\-]+):([0-9]+)\/([0-9]+)\)/i', '($1:$2'. self::QUERY_FRACTION .'$3)', $css);
// Remove empty rules.
$css = preg_replace('/[^\};\{\/]+\{\}/S', '', $css);
// Add "/" back to fix Opera -o-device-pixel-ratio query
$css = preg_replace('/'. self::QUERY_FRACTION .'/', '/', $css);
// Replace multiple semi-colons in a row by a single one
// See SF bug #1980989
$css = preg_replace('/;;+/', ';', $css);
// Restore new lines for /*! important comments
$css = preg_replace('/'. self::NL .'/', "\n", $css);
// Lowercase all uppercase properties
$css = preg_replace_callback('/(\{|\;)([A-Z\-]+)(\:)/', array($this, 'lowercase_properties'), $css);
// Some source control tools don't like it when files containing lines longer
// than, say 8000 characters, are checked in. The linebreak option is used in
// that case to split long lines after a specific column.
if ($linebreak_pos !== FALSE && (int) $linebreak_pos >= 0) {
$linebreak_pos = (int) $linebreak_pos;
$start_index = $i = 0;
while ($i < strlen($css)) {
$i++;
if ($css[$i - 1] === '}' && $i - $start_index > $linebreak_pos) {
$css = $this->str_slice($css, 0, $i) . "\n" . $this->str_slice($css, $i);
$start_index = $i;
}
}
}
// restore preserved comments and strings in reverse order
for ($i = count($this->preserved_tokens) - 1; $i >= 0; $i--) {
$css = preg_replace('/' . self::TOKEN . $i . '___/', $this->preserved_tokens[$i], $css, 1);
}
// Trim the final string (for any leading or trailing white spaces)
return trim($css);
}
/**
* Utility method to replace all data urls with tokens before we start
* compressing, to avoid performance issues running some of the subsequent
* regexes against large strings chunks.
*
* @param string $css
* @return string
*/
private function extract_data_urls($css)
{
// Leave data urls alone to increase parse performance.
$max_index = strlen($css) - 1;
$append_index = $index = $last_index = $offset = 0;
$sb = array();
$pattern = '/url\(\s*(["\']?)data\:/i';
// Since we need to account for non-base64 data urls, we need to handle
// ' and ) being part of the data string. Hence switching to indexOf,
// to determine whether or not we have matching string terminators and
// handling sb appends directly, instead of using matcher.append* methods.
while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$start_index = $index + 4; // "url(".length()
$end_index = $last_index - 1;
$terminator = $m[1]; // ', " or empty (not quoted)
$found_terminator = FALSE;
if (strlen($terminator) === 0) {
$terminator = ')';
}
while ($found_terminator === FALSE && $end_index+1 <= $max_index) {
$end_index = $this->index_of($css, $terminator, $end_index + 1);
// endIndex == 0 doesn't really apply here
if ($end_index > 0 && substr($css, $end_index - 1, 1) !== '\\') {
$found_terminator = TRUE;
if (')' != $terminator) {
$end_index = $this->index_of($css, ')', $end_index);
}
}
}
// Enough searching, start moving stuff over to the buffer
$sb[] = $this->str_slice($css, $append_index, $index);
if ($found_terminator) {
$token = $this->str_slice($css, $start_index, $end_index);
$token = preg_replace('/\s+/', '', $token);
$this->preserved_tokens[] = $token;
$preserver = 'url(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___)';
$sb[] = $preserver;
$append_index = $end_index + 1;
} else {
// No end terminator found, re-add the whole match. Should we throw/warn here?
$sb[] = $this->str_slice($css, $index, $last_index);
$append_index = $last_index;
}
$offset = $last_index;
}
$sb[] = $this->str_slice($css, $append_index);
return implode('', $sb);
}
/**
* Utility method to compress hex color values of the form #AABBCC to #ABC or short color name.
*
* DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
* e.g. #AddressForm { ... }
*
* DOES NOT compress IE filters, which have hex color values (which would break things).
* e.g. filter: chroma(color="#FFFFFF");
*
* DOES NOT compress invalid hex values.
* e.g. background-color: #aabbccdd
*
* @param string $css
* @return string
*/
private function compress_hex_colors($css)
{
// Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
$pattern = '/(\=\s*?["\']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/iS';
$_index = $index = $last_index = $offset = 0;
$sb = array();
// See: http://ajaxmin.codeplex.com/wikipage?title=CSS%20Colors
$short_safe = array(
'#808080' => 'gray',
'#008000' => 'green',
'#800000' => 'maroon',
'#000080' => 'navy',
'#808000' => 'olive',
'#ffa500' => 'orange',
'#800080' => 'purple',
'#c0c0c0' => 'silver',
'#008080' => 'teal',
'#f00' => 'red'
);
while (preg_match($pattern, $css, $m, 0, $offset)) {
$index = $this->index_of($css, $m[0], $offset);
$last_index = $index + strlen($m[0]);
$is_filter = $m[1] !== null && $m[1] !== '';
$sb[] = $this->str_slice($css, $_index, $index);
if ($is_filter) {
// Restore, maintain case, otherwise filter will break
$sb[] = $m[1] . '#' . $m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7];
} else {
if (strtolower($m[2]) == strtolower($m[3]) &&
strtolower($m[4]) == strtolower($m[5]) &&
strtolower($m[6]) == strtolower($m[7])) {
// Compress.
$hex = '#' . strtolower($m[3] . $m[5] . $m[7]);
} else {
// Non compressible color, restore but lower case.
$hex = '#' . strtolower($m[2] . $m[3] . $m[4] . $m[5] . $m[6] . $m[7]);
}
// replace Hex colors to short safe color names
$sb[] = array_key_exists($hex, $short_safe) ? $short_safe[$hex] : $hex;
}
$_index = $offset = $last_index - strlen($m[8]);
}
$sb[] = $this->str_slice($css, $_index);
return implode('', $sb);
}
/* CALLBACKS
* ---------------------------------------------------------------------------------------------
*/
private function replace_string($matches)
{
$match = $matches[0];
$quote = substr($match, 0, 1);
// Must use addcslashes in PHP to avoid parsing of backslashes
$match = addcslashes($this->str_slice($match, 1, -1), '\\');
// maybe the string contains a comment-like substring?
// one, maybe more? put'em back then
if (($pos = $this->index_of($match, self::COMMENT)) >= 0) {
for ($i = 0, $max = count($this->comments); $i < $max; $i++) {
$match = preg_replace('/' . self::COMMENT . $i . '___/', $this->comments[$i], $match, 1);
}
}
// minify alpha opacity in filter strings
$match = preg_replace('/progid\:DXImageTransform\.Microsoft\.Alpha\(Opacity\=/i', 'alpha(opacity=', $match);
$this->preserved_tokens[] = $match;
return $quote . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . $quote;
}
private function replace_colon($matches)
{
return preg_replace('/\:/', self::CLASSCOLON, $matches[0]);
}
private function replace_calc($matches)
{
$this->preserved_tokens[] = trim(preg_replace('/\s*([\*\/\(\),])\s*/', '$1', $matches[2]));
return 'calc('. self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
}
private function preserve_old_IE_specific_matrix_definition($matches)
{
$this->preserved_tokens[] = $matches[1];
return 'filter:progid:DXImageTransform.Microsoft.Matrix(' . self::TOKEN . (count($this->preserved_tokens) - 1) . '___' . ')';
}
private function replace_keyframe_zero($matches)
{
return $matches[1] . preg_replace('/0(\{|,[^\)\{]+\{)/', '0%$1', $matches[2]) . $matches[3];
}
private function rgb_to_hex($matches)
{
// Support for percentage values rgb(100%, 0%, 45%);
if ($this->index_of($matches[1], '%') >= 0){
$rgbcolors = explode(',', str_replace('%', '', $matches[1]));
for ($i = 0; $i < count($rgbcolors); $i++) {
$rgbcolors[$i] = $this->round_number(floatval($rgbcolors[$i]) * 2.55);
}
} else {
$rgbcolors = explode(',', $matches[1]);
}
// Values outside the sRGB color space should be clipped (0-255)
for ($i = 0; $i < count($rgbcolors); $i++) {
$rgbcolors[$i] = $this->clamp_number(intval($rgbcolors[$i], 10), 0, 255);
$rgbcolors[$i] = sprintf("%02x", $rgbcolors[$i]);
}
// Fix for issue #2528093
if (!preg_match('/[\s\,\);\}]/', $matches[2])){
$matches[2] = ' ' . $matches[2];
}
return '#' . implode('', $rgbcolors) . $matches[2];
}
private function hsl_to_hex($matches)
{
$values = explode(',', str_replace('%', '', $matches[1]));
$h = floatval($values[0]);
$s = floatval($values[1]);
$l = floatval($values[2]);
// Wrap and clamp, then fraction!
$h = ((($h % 360) + 360) % 360) / 360;
$s = $this->clamp_number($s, 0, 100) / 100;
$l = $this->clamp_number($l, 0, 100) / 100;
if ($s == 0) {
$r = $g = $b = $this->round_number(255 * $l);
} else {
$v2 = $l < 0.5 ? $l * (1 + $s) : ($l + $s) - ($s * $l);
$v1 = (2 * $l) - $v2;
$r = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h + (1/3)));
$g = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h));
$b = $this->round_number(255 * $this->hue_to_rgb($v1, $v2, $h - (1/3)));
}
return $this->rgb_to_hex(array('', $r.','.$g.','.$b, $matches[2]));
}
private function lowercase_pseudo_first($matches)
{
return ':first-'. strtolower($matches[1]) .' '. $matches[2];
}
private function lowercase_directives($matches)
{
return '@'. strtolower($matches[1]);
}
private function lowercase_pseudo_elements($matches)
{
return ':'. strtolower($matches[1]);
}
private function lowercase_common_functions($matches)
{
return ':'. strtolower($matches[1]) .'(';
}
private function lowercase_common_functions_values($matches)
{
return $matches[1] . strtolower($matches[2]);
}
private function lowercase_properties($matches)
{
return $matches[1].strtolower($matches[2]).$matches[3];
}
/* HELPERS
* ---------------------------------------------------------------------------------------------
*/
private function hue_to_rgb($v1, $v2, $vh)
{
$vh = $vh < 0 ? $vh + 1 : ($vh > 1 ? $vh - 1 : $vh);
if ($vh * 6 < 1) return $v1 + ($v2 - $v1) * 6 * $vh;
if ($vh * 2 < 1) return $v2;
if ($vh * 3 < 2) return $v1 + ($v2 - $v1) * ((2/3) - $vh) * 6;
return $v1;
}
private function round_number($n)
{
return intval(floor(floatval($n) + 0.5), 10);
}
private function clamp_number($n, $min, $max)
{
return min(max($n, $min), $max);
}
/**
* PHP port of Javascript's "indexOf" function for strings only
* Author: Tubal Martin http://blog.margenn.com
*
* @param string $haystack
* @param string $needle
* @param int $offset index (optional)
* @return int
*/
private function index_of($haystack, $needle, $offset = 0)
{
$index = strpos($haystack, $needle, $offset);
return ($index !== FALSE) ? $index : -1;
}
/**
* PHP port of Javascript's "slice" function for strings only
* Author: Tubal Martin http://blog.margenn.com
* Tests: http://margenn.com/tubal/str_slice/
*
* @param string $str
* @param int $start index
* @param int|bool $end index (optional)
* @return string
*/
private function str_slice($str, $start = 0, $end = FALSE)
{
if ($end !== FALSE && ($start < 0 || $end <= 0)) {
$max = strlen($str);
if ($start < 0) {
if (($start = $max + $start) < 0) {
return '';
}
}
if ($end < 0) {
if (($end = $max + $end) < 0) {
return '';
}
}
if ($end <= $start) {
return '';
}
}
$slice = ($end === FALSE) ? substr($str, $start) : substr($str, $start, $end - $start);
return ($slice === FALSE) ? '' : $slice;
}
/**
* Convert strings like "64M" or "30" to int values
* @param mixed $size
* @return int
*/
private function normalize_int($size)
{
if (is_string($size)) {
switch (substr($size, -1)) {
case 'M': case 'm': return $size * 1048576;
case 'K': case 'k': return $size * 1024;
case 'G': case 'g': return $size * 1073741824;
}
}
return (int) $size;
}
}

View File

@@ -1,123 +0,0 @@
<?php
/**
* DooDigestAuth class file.
*
* @author Leng Sheng Hong <darkredz@gmail.com>
* @link http://www.doophp.com/
* @copyright Copyright &copy; 2009 Leng Sheng Hong
* @license http://www.doophp.com/license
*/
/**
* Handles HTTP digest authentication
*
* <p>HTTP digest authentication can be used with the URI router.
* HTTP digest is much more recommended over the use of HTTP Basic auth which doesn't provide any encryption.
* If you are running PHP on Apache in CGI/FastCGI mode, you would need to
* add the following line to your .htaccess for digest auth to work correctly.</p>
* <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code>
*
* <p>This class is tested under Apache 2.2 and Cherokee web server. It should work in both mod_php and cgi mode.</p>
*
* @author Leng Sheng Hong <darkredz@gmail.com>
* @version $Id: DooDigestAuth.php 1000 2009-07-7 18:27:22
* @package doo.auth
* @since 1.0
*
* @deprecated 2.3 This will be removed in Minify 3.0
*/
class DooDigestAuth{
/**
* Authenticate against a list of username and passwords.
*
* <p>HTTP Digest Authentication doesn't work with PHP in CGI mode,
* you have to add this into your .htaccess <code>RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]</code></p>
*
* @param string $realm Name of the authentication session
* @param array $users An assoc array of username and password: array('uname1'=>'pwd1', 'uname2'=>'pwd2')
* @param string $fail_msg Message to be displayed if the User cancel the login
* @param string $fail_url URL to be redirect if the User cancel the login
* @return string The username if login success.
*/
public static function http_auth($realm, $users, $fail_msg=NULL, $fail_url=NULL){
$realm = "Restricted area - $realm";
//user => password
//$users = array('admin' => '1234', 'guest' => 'guest');
if(!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']) && strpos($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 'Digest')===0){
$_SERVER['PHP_AUTH_DIGEST'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
}
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
header('HTTP/1.1 401 Unauthorized');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// analyze the PHP_AUTH_DIGEST variable
if (!($data = self::http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) || !isset($users[$data['username']])){
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
header('HTTP/1.1 401 Unauthorized');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);
if ($data['response'] != $valid_response){
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
if($fail_msg!=NULL)
die($fail_msg);
if($fail_url!=NULL)
die("<script>window.location.href = '$fail_url'</script>");
exit;
}
// ok, valid username & password
return $data['username'];
}
/**
* Method to parse the http auth header, works with IE.
*
* Internet Explorer returns a qop="xxxxxxxxxxx" in the header instead of qop=xxxxxxxxxxx as most browsers do.
*
* @param string $txt header string to parse
* @return array An assoc array of the digest auth session
*/
private static function http_digest_parse($txt)
{
$res = preg_match("/username=\"([^\"]+)\"/i", $txt, $match);
$data['username'] = (isset($match[1]))?$match[1]:null;
$res = preg_match('/nonce=\"([^\"]+)\"/i', $txt, $match);
$data['nonce'] = $match[1];
$res = preg_match('/nc=([0-9]+)/i', $txt, $match);
$data['nc'] = $match[1];
$res = preg_match('/cnonce=\"([^\"]+)\"/i', $txt, $match);
$data['cnonce'] = $match[1];
$res = preg_match('/qop=([^,]+)/i', $txt, $match);
$data['qop'] = str_replace('"','',$match[1]);
$res = preg_match('/uri=\"([^\"]+)\"/i', $txt, $match);
$data['uri'] = $match[1];
$res = preg_match('/response=\"([^\"]+)\"/i', $txt, $match);
$data['response'] = $match[1];
return $data;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,456 +0,0 @@
<?php
/**
* JSMin.php - modified PHP implementation of Douglas Crockford's JSMin.
*
* <code>
* $minifiedJs = JSMin::minify($js);
* </code>
*
* This is a modified port of jsmin.c. Improvements:
*
* Does not choke on some regexp literals containing quote characters. E.g. /'/
*
* Spaces are preserved after some add/sub operators, so they are not mistakenly
* converted to post-inc/dec. E.g. a + ++b -> a+ ++b
*
* Preserves multi-line comments that begin with /*!
*
* PHP 5 or higher is required.
*
* Permission is hereby granted to use this version of the library under the
* same terms as jsmin.c, which has the following license:
*
* --
* Copyright (c) 2002 Douglas Crockford (www.crockford.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* The Software shall be used for Good, not Evil.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
* --
*
* @package JSMin
* @author Ryan Grove <ryan@wonko.com> (PHP port)
* @author Steve Clay <steve@mrclay.org> (modifications + cleanup)
* @author Andrea Giammarchi <http://www.3site.eu> (spaceBeforeRegExp)
* @copyright 2002 Douglas Crockford <douglas@crockford.com> (jsmin.c)
* @copyright 2008 Ryan Grove <ryan@wonko.com> (PHP port)
* @license http://opensource.org/licenses/mit-license.php MIT License
* @link http://code.google.com/p/jsmin-php/
*/
class JSMin {
const ORD_LF = 10;
const ORD_SPACE = 32;
const ACTION_KEEP_A = 1;
const ACTION_DELETE_A = 2;
const ACTION_DELETE_A_B = 3;
protected $a = "\n";
protected $b = '';
protected $input = '';
protected $inputIndex = 0;
protected $inputLength = 0;
protected $lookAhead = null;
protected $output = '';
protected $lastByteOut = '';
protected $keptComment = '';
/**
* Minify Javascript.
*
* @param string $js Javascript to be minified
*
* @return string
*/
public static function minify($js)
{
$jsmin = new JSMin($js);
return $jsmin->min();
}
/**
* @param string $input
*/
public function __construct($input)
{
$this->input = $input;
}
/**
* Perform minification, return result
*
* @return string
*/
public function min()
{
if ($this->output !== '') { // min already run
return $this->output;
}
$mbIntEnc = null;
if (function_exists('mb_strlen') && ((int)ini_get('mbstring.func_overload') & 2)) {
$mbIntEnc = mb_internal_encoding();
mb_internal_encoding('8bit');
}
if (isset($this->input[0]) && $this->input[0] === "\xef") {
$this->input = substr($this->input, 3);
}
$this->input = str_replace("\r\n", "\n", $this->input);
$this->inputLength = strlen($this->input);
$this->action(self::ACTION_DELETE_A_B);
while ($this->a !== null) {
// determine next command
$command = self::ACTION_KEEP_A; // default
if ($this->a === ' ') {
if (($this->lastByteOut === '+' || $this->lastByteOut === '-')
&& ($this->b === $this->lastByteOut)) {
// Don't delete this space. If we do, the addition/subtraction
// could be parsed as a post-increment
} elseif (! $this->isAlphaNum($this->b)) {
$command = self::ACTION_DELETE_A;
}
} elseif ($this->a === "\n") {
if ($this->b === ' ') {
$command = self::ACTION_DELETE_A_B;
// in case of mbstring.func_overload & 2, must check for null b,
// otherwise mb_strpos will give WARNING
} elseif ($this->b === null
|| (false === strpos('{[(+-!~', $this->b)
&& ! $this->isAlphaNum($this->b))) {
$command = self::ACTION_DELETE_A;
}
} elseif (! $this->isAlphaNum($this->a)) {
if ($this->b === ' '
|| ($this->b === "\n"
&& (false === strpos('}])+-"\'', $this->a)))) {
$command = self::ACTION_DELETE_A_B;
}
}
$this->action($command);
}
$this->output = trim($this->output);
if ($mbIntEnc !== null) {
mb_internal_encoding($mbIntEnc);
}
return $this->output;
}
/**
* ACTION_KEEP_A = Output A. Copy B to A. Get the next B.
* ACTION_DELETE_A = Copy B to A. Get the next B.
* ACTION_DELETE_A_B = Get the next B.
*
* @param int $command
* @throws JSMin_UnterminatedRegExpException|JSMin_UnterminatedStringException
*/
protected function action($command)
{
// make sure we don't compress "a + ++b" to "a+++b", etc.
if ($command === self::ACTION_DELETE_A_B
&& $this->b === ' '
&& ($this->a === '+' || $this->a === '-')) {
// Note: we're at an addition/substraction operator; the inputIndex
// will certainly be a valid index
if ($this->input[$this->inputIndex] === $this->a) {
// This is "+ +" or "- -". Don't delete the space.
$command = self::ACTION_KEEP_A;
}
}
switch ($command) {
case self::ACTION_KEEP_A: // 1
$this->output .= $this->a;
if ($this->keptComment) {
$this->output = rtrim($this->output, "\n");
$this->output .= $this->keptComment;
$this->keptComment = '';
}
$this->lastByteOut = $this->a;
// fallthrough intentional
case self::ACTION_DELETE_A: // 2
$this->a = $this->b;
if ($this->a === "'" || $this->a === '"') { // string literal
$str = $this->a; // in case needed for exception
for(;;) {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
if ($this->a === $this->b) { // end quote
break;
}
if ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedStringException(
"JSMin: Unterminated String at byte {$byte}: {$str}");
}
$str .= $this->a;
if ($this->a === '\\') {
$this->output .= $this->a;
$this->lastByteOut = $this->a;
$this->a = $this->get();
$str .= $this->a;
}
}
}
// fallthrough intentional
case self::ACTION_DELETE_A_B: // 3
$this->b = $this->next();
if ($this->b === '/' && $this->isRegexpLiteral()) {
$this->output .= $this->a . $this->b;
$pattern = '/'; // keep entire pattern in case we need to report it in the exception
for(;;) {
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === '[') {
for(;;) {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
if ($this->a === ']') {
break;
}
if ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
}
if ($this->isEOF($this->a)) {
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated set in RegExp at byte "
. $this->inputIndex .": {$pattern}");
}
}
}
if ($this->a === '/') { // end pattern
break; // while (true)
} elseif ($this->a === '\\') {
$this->output .= $this->a;
$this->a = $this->get();
$pattern .= $this->a;
} elseif ($this->isEOF($this->a)) {
$byte = $this->inputIndex - 1;
throw new JSMin_UnterminatedRegExpException(
"JSMin: Unterminated RegExp at byte {$byte}: {$pattern}");
}
$this->output .= $this->a;
$this->lastByteOut = $this->a;
}
$this->b = $this->next();
}
// end case ACTION_DELETE_A_B
}
}
/**
* @return bool
*/
protected function isRegexpLiteral()
{
if (false !== strpos("(,=:[!&|?+-~*{;", $this->a)) {
// we can't divide after these tokens
return true;
}
// check if first non-ws token is "/" (see starts-regex.js)
$length = strlen($this->output);
if ($this->a === ' ' || $this->a === "\n") {
if ($length < 2) { // weird edge case
return true;
}
}
// if the "/" follows a keyword, it must be a regexp, otherwise it's best to assume division
$subject = $this->output . trim($this->a);
if (!preg_match('/(?:case|else|in|return|typeof)$/', $subject, $m)) {
// not a keyword
return false;
}
// can't be sure it's a keyword yet (see not-regexp.js)
$charBeforeKeyword = substr($subject, 0 - strlen($m[0]) - 1, 1);
if ($this->isAlphaNum($charBeforeKeyword)) {
// this is really an identifier ending in a keyword, e.g. "xreturn"
return false;
}
// it's a regexp. Remove unneeded whitespace after keyword
if ($this->a === ' ' || $this->a === "\n") {
$this->a = '';
}
return true;
}
/**
* Return the next character from stdin. Watch out for lookahead. If the character is a control character,
* translate it to a space or linefeed.
*
* @return string
*/
protected function get()
{
$c = $this->lookAhead;
$this->lookAhead = null;
if ($c === null) {
// getc(stdin)
if ($this->inputIndex < $this->inputLength) {
$c = $this->input[$this->inputIndex];
$this->inputIndex += 1;
} else {
$c = null;
}
}
if (ord($c) >= self::ORD_SPACE || $c === "\n" || $c === null) {
return $c;
}
if ($c === "\r") {
return "\n";
}
return ' ';
}
/**
* Does $a indicate end of input?
*
* @param string $a
* @return bool
*/
protected function isEOF($a)
{
return ord($a) <= self::ORD_LF;
}
/**
* Get next char (without getting it). If is ctrl character, translate to a space or newline.
*
* @return string
*/
protected function peek()
{
$this->lookAhead = $this->get();
return $this->lookAhead;
}
/**
* Return true if the character is a letter, digit, underscore, dollar sign, or non-ASCII character.
*
* @param string $c
*
* @return bool
*/
protected function isAlphaNum($c)
{
return (preg_match('/^[a-z0-9A-Z_\\$\\\\]$/', $c) || ord($c) > 126);
}
/**
* Consume a single line comment from input (possibly retaining it)
*/
protected function consumeSingleLineComment()
{
$comment = '';
while (true) {
$get = $this->get();
$comment .= $get;
if (ord($get) <= self::ORD_LF) { // end of line reached
// if IE conditional comment
if (preg_match('/^\\/@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
$this->keptComment .= "/{$comment}";
}
return;
}
}
}
/**
* Consume a multiple line comment from input (possibly retaining it)
*
* @throws JSMin_UnterminatedCommentException
*/
protected function consumeMultipleLineComment()
{
$this->get();
$comment = '';
for(;;) {
$get = $this->get();
if ($get === '*') {
if ($this->peek() === '/') { // end of comment reached
$this->get();
if (0 === strpos($comment, '!')) {
// preserved by YUI Compressor
if (!$this->keptComment) {
// don't prepend a newline if two comments right after one another
$this->keptComment = "\n";
}
$this->keptComment .= "/*!" . substr($comment, 1) . "*/\n";
} else if (preg_match('/^@(?:cc_on|if|elif|else|end)\\b/', $comment)) {
// IE conditional
$this->keptComment .= "/*{$comment}*/";
}
return;
}
} elseif ($get === null) {
throw new JSMin_UnterminatedCommentException(
"JSMin: Unterminated comment at byte {$this->inputIndex}: /*{$comment}");
}
$comment .= $get;
}
}
/**
* Get the next character, skipping over comments. Some comments may be preserved.
*
* @return string
*/
protected function next()
{
$get = $this->get();
if ($get === '/') {
switch ($this->peek()) {
case '/':
$this->consumeSingleLineComment();
$get = "\n";
break;
case '*':
$this->consumeMultipleLineComment();
$get = ' ';
break;
}
}
return $get;
}
}
class JSMin_UnterminatedStringException extends Exception {}
class JSMin_UnterminatedCommentException extends Exception {}
class JSMin_UnterminatedRegExpException extends Exception {}

Some files were not shown because too many files have changed in this diff Show More