[Web] Update composer libraries

This commit is contained in:
FreddleSpl0it committed 2026-10-28 11:56:29 +01:00
1 parent 9cc8ac2908
commit bd8c784118
1297 files changed
+71585 -24460

No files matched your search

@@ -1,12 +0,0 @@
# These are supported funding model platforms
github: [matthiasmullie] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
@@ -1,59 +0,0 @@
# How to contribute
## Issues
When [filing bugs](https://github.com/matthiasmullie/minify/issues/new),
try to be as thorough as possible:
* What version did you use?
* What did you try to do? ***Please post the relevant parts of your code.***
* What went wrong? ***Please include error messages, if any.***
* What was the expected result?
## Pull requests
Bug fixes and general improvements to the existing codebase are always welcome.
New features are also welcome, but will be judged on an individual basis. If
you'd rather not risk wasting your time implementing a new feature only to see
it turned down, please start the discussion by
[opening an issue](https://github.com/matthiasmullie/minify/issues/new).
Don't forget to add your changes to the [changelog](CHANGELOG.md).
### Testing
Please include tests for every change or addition to the code.
To run the complete test suite:
```sh
vendor/bin/phpunit
```
When submitting a new pull request, please make sure that that the test suite
passes (Travis CI will run it & report back on your pull request.)
To run the tests on Windows, run `tests/convert_symlinks_to_windows_style.sh`
from the command line in order to convert Linux-style test symlinks to
Windows-style.
### Coding standards
All code must follow [PSR-2](http://www.php-fig.org/psr/psr-2/). Just make sure
to run php-cs-fixer before submitting the code, it'll take care of the
formatting for you:
```sh
vendor/bin/php-cs-fixer fix src
vendor/bin/php-cs-fixer fix tests
```
Document the code thoroughly!
## License
Note that minify is MIT-licensed, which basically allows anyone to do
anything they like with it, without restriction.
@@ -1,13 +0,0 @@
ARG version=cli
FROM php:$version
COPY . /var/www
WORKDIR /var/www
RUN apt-get update
RUN apt-get install -y zip unzip libzip-dev git
RUN docker-php-ext-install zip
RUN docker-php-ext-install pcntl
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
RUN composer install
+10 -5
View File
@@ -3,12 +3,12 @@
"type": "library",
"description": "CSS & JavaScript minifier, in PHP. Removes whitespace, strips comments, combines files (incl. @import statements and small assets in CSS files), and optimizes/shortens a few common programming patterns.",
"keywords": ["minify", "minifier", "css", "js", "javascript"],
"homepage": "http://www.minifier.org",
"homepage": "https://github.com/matthiasmullie/minify",
"license": "MIT",
"authors": [
{
"name": "Matthias Mullie",
"homepage": "http://www.mullie.eu",
"homepage": "https://www.mullie.eu",
"email": "minify@mullie.eu",
"role": "Developer"
}
@@ -19,9 +19,9 @@
"matthiasmullie/path-converter": "~1.1"
},
"require-dev": {
"matthiasmullie/scrapbook": "dev-master",
"phpunit/phpunit": ">=4.8",
"friendsofphp/php-cs-fixer": "~2.0"
"friendsofphp/php-cs-fixer": ">=2.0",
"matthiasmullie/scrapbook": ">=1.3",
"phpunit/phpunit": ">=4.8"
},
"suggest": {
"psr/cache-implementation": "Cache implementation to use with Minify::cache"
@@ -31,6 +31,11 @@
"MatthiasMullie\\Minify\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"MatthiasMullie\\Minify\\Tests\\": "tests/"
}
},
"bin": [
"bin/minifycss",
"bin/minifyjs"
@@ -1,46 +0,0 @@
version: '2.1'
services:
php:
build:
context: .
dockerfile: Dockerfile
volumes:
- ./src:/var/www/src
- ./data:/var/www/data
- ./tests:/var/www/tests
- ./phpunit.xml.dist:/var/www/phpunit.xml.dist
'8.0':
extends: php
build:
args:
version: 8.0-cli
'7.4':
extends: php
build:
args:
version: 7.4-cli
'7.3':
extends: php
build:
args:
version: 7.3-cli
'7.2':
extends: php
build:
args:
version: 7.2-cli
'7.1':
extends: php
build:
args:
version: 7.1-cli
'7.0':
extends: php
build:
args:
version: 7.0-cli
'5.6':
extends: php
build:
args:
version: 5.6-cli
+204 -93
View File
@@ -1,6 +1,7 @@
<?php
/**
* CSS Minifier
* CSS Minifier.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
@@ -12,15 +13,15 @@
namespace MatthiasMullie\Minify;
use MatthiasMullie\Minify\Exceptions\FileImportException;
use MatthiasMullie\PathConverter\ConverterInterface;
use MatthiasMullie\Minify\Exceptions\PatternMatchException;
use MatthiasMullie\PathConverter\Converter;
use MatthiasMullie\PathConverter\ConverterInterface;
/**
* CSS minifier
* CSS minifier.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
* @author Tijs Verkoyen <minify@verkoyen.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
@@ -44,6 +45,10 @@ class CSS extends Minify
'jpeg' => 'data:image/jpeg',
'svg' => 'data:image/svg+xml',
'woff' => 'data:application/x-font-woff',
'woff2' => 'data:application/x-font-woff2',
'avif' => 'data:image/avif',
'apng' => 'data:image/apng',
'webp' => 'data:image/webp',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'xbm' => 'image/x-xbitmap',
@@ -93,7 +98,7 @@ class CSS extends Minify
}
// add to top
$content = implode(';', $matches[2]).';'.trim($content, ';');
$content = implode(';', $matches[2]) . ';' . trim($content, ';');
}
return $content;
@@ -102,11 +107,11 @@ class CSS extends Minify
/**
* Combine CSS from import statements.
*
* @import's will be loaded and their content merged into the original file,
* \@import's will be loaded and their content merged into the original file,
* to save HTTP requests.
*
* @param string $source The file to combine imports for
* @param string $content The CSS content to combine imports for
* @param string $source The file to combine imports for
* @param string $content The CSS content to combine imports for
* @param string[] $parents Parent paths, for circular reference checks
*
* @return string
@@ -200,7 +205,7 @@ class CSS extends Minify
// loop the matches
foreach ($matches as $match) {
// get the path for the file that will be imported
$importPath = dirname($source).'/'.$match['path'];
$importPath = dirname($source) . '/' . $match['path'];
// only replace the import with the content if we can grab the
// content of the file
@@ -211,7 +216,7 @@ class CSS extends Minify
// check if current file was not imported previously in the same
// import chain.
if (in_array($importPath, $parents)) {
throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.');
throw new FileImportException('Failed to import file "' . $importPath . '": circular reference detected.');
}
// grab referenced file & minify it (which may include importing
@@ -223,7 +228,7 @@ class CSS extends Minify
// check if this is only valid for certain media
if (!empty($match['media'])) {
$importContent = '@media '.$match['media'].'{'.$importContent.'}';
$importContent = '@media ' . $match['media'] . '{' . $importContent . '}';
}
// add to replacement array
@@ -241,7 +246,7 @@ class CSS extends Minify
* @url(image.jpg) images will be loaded and their content merged into the
* original file, to save HTTP requests.
*
* @param string $source The file to import files for
* @param string $source The file to import files for
* @param string $content The CSS content to import files for
*
* @return string
@@ -262,7 +267,7 @@ class CSS extends Minify
// get the path for the file that will be imported
$path = $match[2];
$path = dirname($source).'/'.$path;
$path = dirname($source) . '/' . $path;
// only replace the import with the content if we're able to get
// the content of the file, and it's relatively small
@@ -273,7 +278,7 @@ class CSS extends Minify
// build replacement
$search[] = $match[0];
$replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')';
$replace[] = 'url(' . $this->importExtensions[$extension] . ';base64,' . $importContent . ')';
}
}
@@ -289,9 +294,11 @@ class CSS extends Minify
* Perform CSS optimizations.
*
* @param string[optional] $path Path to write the data to
* @param string[] $parents Parent paths, for circular reference checks
* @param string[] $parents Parent paths, for circular reference checks
*
* @return string The minified data
*
* @throws PatternMatchException
*/
public function execute($path = null, $parents = array())
{
@@ -312,7 +319,9 @@ class CSS extends Minify
$css = $this->replace($css);
$css = $this->stripWhitespace($css);
$css = $this->shortenColors($css);
$css = $this->convertLegacyColors($css);
$css = $this->cleanupModernColors($css);
$css = $this->shortenHEXColors($css);
$css = $this->shortenZeroes($css);
$css = $this->shortenFontWeights($css);
$css = $this->stripEmptyTags($css);
@@ -351,7 +360,7 @@ class CSS extends Minify
* (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
*
* @param ConverterInterface $converter Relative path converter
* @param string $content The CSS content to update relative urls for
* @param string $content The CSS content to update relative urls for
*
* @return string
*/
@@ -465,9 +474,9 @@ class CSS extends Minify
// build replacement
$search[] = $match[0];
if ($type === 'url') {
$replace[] = 'url('.$url.')';
$replace[] = 'url(' . $url . ')';
} elseif ($type === 'import') {
$replace[] = '@import "'.$url.'"';
$replace[] = '@import "' . $url . '"';
}
}
@@ -476,64 +485,154 @@ class CSS extends Minify
}
/**
* Shorthand hex color codes.
* #FF0000 -> #F00.
* Shorthand HEX color codes.
* #FF0000FF -> #f00 -> red
* #FF00FF00 -> transparent.
*
* @param string $content The CSS content to shorten the hex color codes for
* @param string $content The CSS content to shorten the HEX color codes for
*
* @return string
*/
protected function shortenColors($content)
protected function shortenHexColors($content)
{
$content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?:([0-9a-z])\\4)?(?=[; }])/i', '#$1$2$3$4', $content);
// shorten repeating patterns within HEX ..
$content = preg_replace('/(?<=[: ])#([0-9a-f])\\1([0-9a-f])\\2([0-9a-f])\\3(?:([0-9a-f])\\4)?(?=[; }])/i', '#$1$2$3$4', $content);
// remove alpha channel if it's pointless...
$content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content);
$content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content);
// remove alpha channel if it's pointless ..
$content = preg_replace('/(?<=[: ])#([0-9a-f]{6})ff(?=[; }])/i', '#$1', $content);
$content = preg_replace('/(?<=[: ])#([0-9a-f]{3})f(?=[; }])/i', '#$1', $content);
// replace `transparent` with shortcut ..
$content = preg_replace('/(?<=[: ])#[0-9a-f]{6}00(?=[; }])/i', '#fff0', $content);
$colors = array(
// make these more readable
'#00f' => 'blue',
'#dc143c' => 'crimson',
'#0ff' => 'cyan',
'#8b0000' => 'darkred',
'#696969' => 'dimgray',
'#ff69b4' => 'hotpink',
'#0f0' => 'lime',
'#fdf5e6' => 'oldlace',
'#87ceeb' => 'skyblue',
'#d8bfd8' => 'thistle',
// we can shorten some even more by replacing them with their color name
'#F0FFFF' => 'azure',
'#F5F5DC' => 'beige',
'#A52A2A' => 'brown',
'#FF7F50' => 'coral',
'#FFD700' => 'gold',
'#f0ffff' => 'azure',
'#f5f5dc' => 'beige',
'#ffe4c4' => 'bisque',
'#a52a2a' => 'brown',
'#ff7f50' => 'coral',
'#ffd700' => 'gold',
'#808080' => 'gray',
'#008000' => 'green',
'#4B0082' => 'indigo',
'#FFFFF0' => 'ivory',
'#F0E68C' => 'khaki',
'#FAF0E6' => 'linen',
'#4b0082' => 'indigo',
'#fffff0' => 'ivory',
'#f0e68c' => 'khaki',
'#faf0e6' => 'linen',
'#800000' => 'maroon',
'#000080' => 'navy',
'#808000' => 'olive',
'#CD853F' => 'peru',
'#FFC0CB' => 'pink',
'#DDA0DD' => 'plum',
'#ffa500' => 'orange',
'#da70d6' => 'orchid',
'#cd853f' => 'peru',
'#ffc0cb' => 'pink',
'#dda0dd' => 'plum',
'#800080' => 'purple',
'#F00' => 'red',
'#FA8072' => 'salmon',
'#A0522D' => 'sienna',
'#C0C0C0' => 'silver',
'#FFFAFA' => 'snow',
'#D2B48C' => 'tan',
'#FF6347' => 'tomato',
'#EE82EE' => 'violet',
'#F5DEB3' => 'wheat',
'#f00' => 'red',
'#fa8072' => 'salmon',
'#a0522d' => 'sienna',
'#c0c0c0' => 'silver',
'#fffafa' => 'snow',
'#d2b48c' => 'tan',
'#008080' => 'teal',
'#ff6347' => 'tomato',
'#ee82ee' => 'violet',
'#f5deb3' => 'wheat',
// or the other way around
'WHITE' => '#fff',
'BLACK' => '#000',
'black' => '#000',
'fuchsia' => '#f0f',
'magenta' => '#f0f',
'white' => '#fff',
'yellow' => '#ff0',
// and also `transparent`
'transparent' => '#fff0',
);
return preg_replace_callback(
'/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i',
'/(?<=[: ])(' . implode('|', array_keys($colors)) . ')(?=[; }])/i',
function ($match) use ($colors) {
return $colors[strtoupper($match[0])];
return $colors[strtolower($match[0])];
},
$content
);
}
/**
* Convert RGB|HSL color codes.
* rgb(255,0,0,.5) -> rgb(255 0 0 / .5).
* rgb(255,0,0) -> #f00.
*
* @param string $content The CSS content to shorten the RGB color codes for
*
* @return string
*/
protected function convertLegacyColors($content)
{
/*
https://drafts.csswg.org/css-color/#color-syntax-legacy
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl
*/
// convert legacy color syntax
$content = preg_replace('/(rgb)a?\(\s*([0-9]{1,3}%?)\s*,\s*([0-9]{1,3}%?)\s*,\s*([0-9]{1,3}%?)\s*,\s*([0,1]?(?:\.[0-9]*)?)\s*\)/i', '$1($2 $3 $4 / $5)', $content);
$content = preg_replace('/(rgb)a?\(\s*([0-9]{1,3}%?)\s*,\s*([0-9]{1,3}%?)\s*,\s*([0-9]{1,3}%?)\s*\)/i', '$1($2 $3 $4)', $content);
$content = preg_replace('/(hsl)a?\(\s*([0-9]+(?:deg|grad|rad|turn)?)\s*,\s*([0-9]{1,3}%)\s*,\s*([0-9]{1,3}%)\s*,\s*([0,1]?(?:\.[0-9]*)?)\s*\)/i', '$1($2 $3 $4 / $5)', $content);
$content = preg_replace('/(hsl)a?\(\s*([0-9]+(?:deg|grad|rad|turn)?)\s*,\s*([0-9]{1,3}%)\s*,\s*([0-9]{1,3}%)\s*\)/i', '$1($2 $3 $4)', $content);
// convert `rgb` to `hex`
$dec = '([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])';
return preg_replace_callback(
"/rgb\($dec $dec $dec\)/i",
function ($match) {
return sprintf('#%02x%02x%02x', $match[1], $match[2], $match[3]);
},
$content
);
}
/**
* Cleanup RGB|HSL|HWB|LCH|LAB
* rgb(255 0 0 / 1) -> rgb(255 0 0).
* rgb(255 0 0 / 0) -> transparent.
*
* @param string $content The CSS content to cleanup HSL|HWB|LCH|LAB
*
* @return string
*/
protected function cleanupModernColors($content)
{
/*
https://drafts.csswg.org/css-color/#color-syntax-modern
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hwb
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lch
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/lab
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklab
*/
$tag = '(rgb|hsl|hwb|(?:(?:ok)?(?:lch|lab)))';
// remove alpha channel if it's pointless ..
$content = preg_replace('/' . $tag . '\(\s*([^\s)]+)\s+([^\s)]+)\s+([^\s)]+)\s+\/\s+1(?:(?:\.\d?)*|00%)?\s*\)/i', '$1($2 $3 $4)', $content);
// replace `transparent` with shortcut ..
$content = preg_replace('/' . $tag . '\(\s*[^\s)]+\s+[^\s)]+\s+[^\s)]+\s+\/\s+0(?:[\.0%]*)?\s*\)/i', '#fff0', $content);
return $content;
}
/**
* Shorten CSS font weights.
*
@@ -549,10 +648,10 @@ class CSS extends Minify
);
$callback = function ($match) use ($weights) {
return $match[1].$weights[$match[2]];
return $match[1] . $weights[$match[2]];
};
return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content);
return preg_replace_callback('/(font-weight\s*:\s*)(' . implode('|', array_keys($weights)) . ')(?=[;}])/', $callback, $content);
}
/**
@@ -584,19 +683,19 @@ class CSS extends Minify
// practice, Webkit (especially Safari) seems to stumble over at least
// 0%, potentially other units as well. Only stripping 'px' for now.
// @see https://github.com/matthiasmullie/minify/issues/60
$content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content);
$content = preg_replace('/' . $before . '(-?0*(\.0+)?)(?<=0)px' . $after . '/', '\\1', $content);
// strip 0-digits (.0 -> 0)
$content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content);
$content = preg_replace('/' . $before . '\.0+' . $units . '?' . $after . '/', '0\\1', $content);
// strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px
$content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content);
$content = preg_replace('/' . $before . '(-?[0-9]+\.[0-9]+)0+' . $units . '?' . $after . '/', '\\1\\2', $content);
// strip trailing 0: 50.00 -> 50, 50.00px -> 50px
$content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content);
$content = preg_replace('/' . $before . '(-?[0-9]+)\.0+' . $units . '?' . $after . '/', '\\1\\2', $content);
// strip leading 0: 0.1 -> .1, 01.1 -> 1.1
$content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content);
$content = preg_replace('/' . $before . '(-?)0+([0-9]*\.[0-9]+)' . $units . '?' . $after . '/', '\\1\\2\\3', $content);
// strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
$content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
$content = preg_replace('/' . $before . '-?0+' . $units . '?' . $after . '/', '0\\1', $content);
// IE doesn't seem to understand a unitless flex-basis value (correct -
// it goes against the spec), so let's add it in again (make it `%`,
@@ -628,18 +727,7 @@ class CSS extends Minify
*/
protected function stripComments()
{
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
$callback = function ($match) use ($minifier) {
$count = count($minifier->extracted);
$placeholder = '/*'.$count.'*/';
$minifier->extracted[$placeholder] = $match[0];
return $placeholder;
};
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
$this->registerPattern('/\/\*.*?\*\//s', '');
$this->stripMultilineComments();
}
/**
@@ -648,29 +736,31 @@ class CSS extends Minify
* @param string $content The CSS content to strip the whitespace for
*
* @return string
*
* @throws PatternMatchException
*/
protected function stripWhitespace($content)
{
// remove leading & trailing whitespace
$content = preg_replace('/^\s*/m', '', $content);
$content = preg_replace('/\s*$/m', '', $content);
$content = $this->pregReplace('/^\s*/m', '', $content);
$content = $this->pregReplace('/\s*$/m', '', $content);
// replace newlines with a single space
$content = preg_replace('/\s+/', ' ', $content);
$content = $this->pregReplace('/\s+/', ' ', $content);
// remove whitespace around meta characters
// inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex
$content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
$content = preg_replace('/([\[(:>\+])\s+/', '$1', $content);
$content = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
$content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
$content = $this->pregReplace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content);
$content = $this->pregReplace('/([\[(:>\+])\s+/', '$1', $content);
$content = $this->pregReplace('/\s+([\]\)>\+])/', '$1', $content);
$content = $this->pregReplace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
// whitespace around + and - can only be stripped inside some pseudo-
// classes, like `:nth-child(3+2n)`
// not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or
// selectors like `div.weird- p`
$pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type');
$content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
$content = $this->pregReplace('/:(' . implode('|', $pseudos) . ')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content);
// remove semicolon/whitespace followed by closing bracket
$content = str_replace(';}', '}', $content);
@@ -678,14 +768,35 @@ class CSS extends Minify
return trim($content);
}
/**
* Perform a preg_replace and check for errors.
*
* @param string $pattern Pattern
* @param string $replacement Replacement
* @param string $subject String to process
*
* @return string
*
* @throws PatternMatchException
*/
protected function pregReplace($pattern, $replacement, $subject)
{
$result = preg_replace($pattern, $replacement, $subject);
if ($result === null) {
throw PatternMatchException::fromLastError("Failed to replace with pattern '$pattern'");
}
return $result;
}
/**
* Replace all occurrences of functions that may contain math, where
* whitespace around operators needs to be preserved (e.g. calc, clamp)
* whitespace around operators needs to be preserved (e.g. calc, clamp).
*/
protected function extractMath()
{
$functions = array('calc', 'clamp', 'min', 'max');
$pattern = '/\b('. implode('|', $functions) .')(\(.+?)(?=$|;|})/m';
$pattern = '/\b(' . implode('|', $functions) . ')(\(.+?)(?=$|;|})/m';
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
@@ -700,11 +811,11 @@ class CSS extends Minify
// instead, it'll match a larger portion of code to where it's certain that
// the calc() musts have ended, and we'll figure out which is the correct
// closing parenthesis here, by counting how many have opened
for ($i = 0; $i < $length; $i++) {
for ($i = 0; $i < $length; ++$i) {
$char = $match[2][$i];
$expr .= $char;
if ($char === '(') {
$opened++;
++$opened;
} elseif ($char === ')' && --$opened === 0) {
break;
}
@@ -712,16 +823,16 @@ class CSS extends Minify
// now that we've figured out where the calc() starts and ends, extract it
$count = count($minifier->extracted);
$placeholder = 'math('.$count.')';
$minifier->extracted[$placeholder] = $function.'('.trim(substr($expr, 1, -1)).')';
$placeholder = 'math(' . $count . ')';
$minifier->extracted[$placeholder] = $function . '(' . trim(substr($expr, 1, -1)) . ')';
// and since we've captured more code than required, we may have some leftover
// calc() in here too - go recursive on the remaining but of code to go figure
// that out and extract what is needed
$rest = str_replace($function.$expr, '', $match[0]);
$rest = $minifier->str_replace_first($function . $expr, '', $match[0]);
$rest = preg_replace_callback($pattern, $callback, $rest);
return $placeholder.$rest;
return $placeholder . $rest;
};
$this->registerPattern($pattern, $callback);
@@ -729,19 +840,19 @@ class CSS extends Minify
/**
* Replace custom properties, whose values may be used in scenarios where
* we wouldn't want them to be minified (e.g. inside calc)
* we wouldn't want them to be minified (e.g. inside calc).
*/
protected function extractCustomProperties()
{
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
$this->registerPattern(
'/(?<=^|[;}])(--[^:;{}"\'\s]+)\s*:([^;{}]+)/m',
'/(?<=^|[;}{])\s*(--[^:;{}"\'\s]+)\s*:([^;{}]+)/m',
function ($match) use ($minifier) {
$placeholder = '--custom-'. count($minifier->extracted) . ':0';
$minifier->extracted[$placeholder] = $match[1] .':'. trim($match[2]);
return $placeholder;
$placeholder = '--custom-' . count($minifier->extracted) . ':0';
$minifier->extracted[$placeholder] = $match[1] . ':' . trim($match[2]);
return $placeholder;
}
);
}
@@ -1,20 +1,20 @@
<?php
/**
* Base Exception
* Base Exception.
*
* @deprecated Use Exceptions\BasicException instead
*
* @author Matthias Mullie <minify@mullie.eu>
*/
namespace MatthiasMullie\Minify;
/**
* Base Exception Class
* Base Exception Class.
*
* @deprecated Use Exceptions\BasicException instead
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
*/
abstract class Exception extends \Exception
{
}
abstract class Exception extends \Exception {}
@@ -1,6 +1,7 @@
<?php
/**
* Basic exception
* Basic exception.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
@@ -8,16 +9,14 @@
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify\Exceptions;
use MatthiasMullie\Minify\Exception;
/**
* Basic Exception Class
* Basic Exception Class.
*
* @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
abstract class BasicException extends Exception
{
}
abstract class BasicException extends Exception {}
@@ -1,6 +1,7 @@
<?php
/**
* File Import Exception
* File Import Exception.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
@@ -8,14 +9,12 @@
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify\Exceptions;
/**
* File Import Exception Class
* File Import Exception Class.
*
* @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
class FileImportException extends BasicException
{
}
class FileImportException extends BasicException {}
@@ -1,6 +1,7 @@
<?php
/**
* IO Exception
* IO Exception.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
@@ -8,14 +9,12 @@
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify\Exceptions;
/**
* IO Exception Class
* IO Exception Class.
*
* @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
class IOException extends BasicException
{
}
class IOException extends BasicException {}
@@ -0,0 +1,36 @@
<?php
/**
* Pattern match exception.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @author Ere Maijala <ere.maijala@helsinki.fi>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify\Exceptions;
/**
* Pattern Match Exception Class.
*
* @author Ere Maijala <ere.maijala@helsinki.fi>
*/
class PatternMatchException extends BasicException
{
/**
* Create an exception from preg_last_error.
*
* @param string $msg Error message
*/
public static function fromLastError($msg)
{
$msg .= ': Error ' . preg_last_error();
if (PHP_MAJOR_VERSION >= 8) {
$msg .= ' - ' . preg_last_error_msg();
}
return new PatternMatchException($msg);
}
}
+57 -51
View File
@@ -1,6 +1,7 @@
<?php
/**
* JavaScript minifier
* JavaScript minifier.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
@@ -8,14 +9,14 @@
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify;
/**
* JavaScript Minifier Class
* JavaScript Minifier Class.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
* @author Tijs Verkoyen <minify@verkoyen.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
@@ -29,6 +30,8 @@ class JS extends Minify
* Note that regular expressions using that bit must have the PCRE_UTF8
* pattern modifier (/u) set.
*
* @internal
*
* @var string
*/
const REGEX_VARIABLE = '\b[$A-Z\_a-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\x{02c1}\x{02c6}-\x{02d1}\x{02e0}-\x{02e4}\x{02ec}\x{02ee}\x{0370}-\x{0374}\x{0376}\x{0377}\x{037a}-\x{037d}\x{0386}\x{0388}-\x{038a}\x{038c}\x{038e}-\x{03a1}\x{03a3}-\x{03f5}\x{03f7}-\x{0481}\x{048a}-\x{0527}\x{0531}-\x{0556}\x{0559}\x{0561}-\x{0587}\x{05d0}-\x{05ea}\x{05f0}-\x{05f2}\x{0620}-\x{064a}\x{066e}\x{066f}\x{0671}-\x{06d3}\x{06d5}\x{06e5}\x{06e6}\x{06ee}\x{06ef}\x{06fa}-\x{06fc}\x{06ff}\x{0710}\x{0712}-\x{072f}\x{074d}-\x{07a5}\x{07b1}\x{07ca}-\x{07ea}\x{07f4}\x{07f5}\x{07fa}\x{0800}-\x{0815}\x{081a}\x{0824}\x{0828}\x{0840}-\x{0858}\x{08a0}\x{08a2}-\x{08ac}\x{0904}-\x{0939}\x{093d}\x{0950}\x{0958}-\x{0961}\x{0971}-\x{0977}\x{0979}-\x{097f}\x{0985}-\x{098c}\x{098f}\x{0990}\x{0993}-\x{09a8}\x{09aa}-\x{09b0}\x{09b2}\x{09b6}-\x{09b9}\x{09bd}\x{09ce}\x{09dc}\x{09dd}\x{09df}-\x{09e1}\x{09f0}\x{09f1}\x{0a05}-\x{0a0a}\x{0a0f}\x{0a10}\x{0a13}-\x{0a28}\x{0a2a}-\x{0a30}\x{0a32}\x{0a33}\x{0a35}\x{0a36}\x{0a38}\x{0a39}\x{0a59}-\x{0a5c}\x{0a5e}\x{0a72}-\x{0a74}\x{0a85}-\x{0a8d}\x{0a8f}-\x{0a91}\x{0a93}-\x{0aa8}\x{0aaa}-\x{0ab0}\x{0ab2}\x{0ab3}\x{0ab5}-\x{0ab9}\x{0abd}\x{0ad0}\x{0ae0}\x{0ae1}\x{0b05}-\x{0b0c}\x{0b0f}\x{0b10}\x{0b13}-\x{0b28}\x{0b2a}-\x{0b30}\x{0b32}\x{0b33}\x{0b35}-\x{0b39}\x{0b3d}\x{0b5c}\x{0b5d}\x{0b5f}-\x{0b61}\x{0b71}\x{0b83}\x{0b85}-\x{0b8a}\x{0b8e}-\x{0b90}\x{0b92}-\x{0b95}\x{0b99}\x{0b9a}\x{0b9c}\x{0b9e}\x{0b9f}\x{0ba3}\x{0ba4}\x{0ba8}-\x{0baa}\x{0bae}-\x{0bb9}\x{0bd0}\x{0c05}-\x{0c0c}\x{0c0e}-\x{0c10}\x{0c12}-\x{0c28}\x{0c2a}-\x{0c33}\x{0c35}-\x{0c39}\x{0c3d}\x{0c58}\x{0c59}\x{0c60}\x{0c61}\x{0c85}-\x{0c8c}\x{0c8e}-\x{0c90}\x{0c92}-\x{0ca8}\x{0caa}-\x{0cb3}\x{0cb5}-\x{0cb9}\x{0cbd}\x{0cde}\x{0ce0}\x{0ce1}\x{0cf1}\x{0cf2}\x{0d05}-\x{0d0c}\x{0d0e}-\x{0d10}\x{0d12}-\x{0d3a}\x{0d3d}\x{0d4e}\x{0d60}\x{0d61}\x{0d7a}-\x{0d7f}\x{0d85}-\x{0d96}\x{0d9a}-\x{0db1}\x{0db3}-\x{0dbb}\x{0dbd}\x{0dc0}-\x{0dc6}\x{0e01}-\x{0e30}\x{0e32}\x{0e33}\x{0e40}-\x{0e46}\x{0e81}\x{0e82}\x{0e84}\x{0e87}\x{0e88}\x{0e8a}\x{0e8d}\x{0e94}-\x{0e97}\x{0e99}-\x{0e9f}\x{0ea1}-\x{0ea3}\x{0ea5}\x{0ea7}\x{0eaa}\x{0eab}\x{0ead}-\x{0eb0}\x{0eb2}\x{0eb3}\x{0ebd}\x{0ec0}-\x{0ec4}\x{0ec6}\x{0edc}-\x{0edf}\x{0f00}\x{0f40}-\x{0f47}\x{0f49}-\x{0f6c}\x{0f88}-\x{0f8c}\x{1000}-\x{102a}\x{103f}\x{1050}-\x{1055}\x{105a}-\x{105d}\x{1061}\x{1065}\x{1066}\x{106e}-\x{1070}\x{1075}-\x{1081}\x{108e}\x{10a0}-\x{10c5}\x{10c7}\x{10cd}\x{10d0}-\x{10fa}\x{10fc}-\x{1248}\x{124a}-\x{124d}\x{1250}-\x{1256}\x{1258}\x{125a}-\x{125d}\x{1260}-\x{1288}\x{128a}-\x{128d}\x{1290}-\x{12b0}\x{12b2}-\x{12b5}\x{12b8}-\x{12be}\x{12c0}\x{12c2}-\x{12c5}\x{12c8}-\x{12d6}\x{12d8}-\x{1310}\x{1312}-\x{1315}\x{1318}-\x{135a}\x{1380}-\x{138f}\x{13a0}-\x{13f4}\x{1401}-\x{166c}\x{166f}-\x{167f}\x{1681}-\x{169a}\x{16a0}-\x{16ea}\x{16ee}-\x{16f0}\x{1700}-\x{170c}\x{170e}-\x{1711}\x{1720}-\x{1731}\x{1740}-\x{1751}\x{1760}-\x{176c}\x{176e}-\x{1770}\x{1780}-\x{17b3}\x{17d7}\x{17dc}\x{1820}-\x{1877}\x{1880}-\x{18a8}\x{18aa}\x{18b0}-\x{18f5}\x{1900}-\x{191c}\x{1950}-\x{196d}\x{1970}-\x{1974}\x{1980}-\x{19ab}\x{19c1}-\x{19c7}\x{1a00}-\x{1a16}\x{1a20}-\x{1a54}\x{1aa7}\x{1b05}-\x{1b33}\x{1b45}-\x{1b4b}\x{1b83}-\x{1ba0}\x{1bae}\x{1baf}\x{1bba}-\x{1be5}\x{1c00}-\x{1c23}\x{1c4d}-\x{1c4f}\x{1c5a}-\x{1c7d}\x{1ce9}-\x{1cec}\x{1cee}-\x{1cf1}\x{1cf5}\x{1cf6}\x{1d00}-\x{1dbf}\x{1e00}-\x{1f15}\x{1f18}-\x{1f1d}\x{1f20}-\x{1f45}\x{1f48}-\x{1f4d}\x{1f50}-\x{1f57}\x{1f59}\x{1f5b}\x{1f5d}\x{1f5f}-\x{1f7d}\x{1f80}-\x{1fb4}\x{1fb6}-\x{1fbc}\x{1fbe}\x{1fc2}-\x{1fc4}\x{1fc6}-\x{1fcc}\x{1fd0}-\x{1fd3}\x{1fd6}-\x{1fdb}\x{1fe0}-\x{1fec}\x{1ff2}-\x{1ff4}\x{1ff6}-\x{1ffc}\x{2071}\x{207f}\x{2090}-\x{209c}\x{2102}\x{2107}\x{210a}-\x{2113}\x{2115}\x{2119}-\x{211d}\x{2124}\x{2126}\x{2128}\x{212a}-\x{212d}\x{212f}-\x{2139}\x{213c}-\x{213f}\x{2145}-\x{2149}\x{214e}\x{2160}-\x{2188}\x{2c00}-\x{2c2e}\x{2c30}-\x{2c5e}\x{2c60}-\x{2ce4}\x{2ceb}-\x{2cee}\x{2cf2}\x{2cf3}\x{2d00}-\x{2d25}\x{2d27}\x{2d2d}\x{2d30}-\x{2d67}\x{2d6f}\x{2d80}-\x{2d96}\x{2da0}-\x{2da6}\x{2da8}-\x{2dae}\x{2db0}-\x{2db6}\x{2db8}-\x{2dbe}\x{2dc0}-\x{2dc6}\x{2dc8}-\x{2dce}\x{2dd0}-\x{2dd6}\x{2dd8}-\x{2dde}\x{2e2f}\x{3005}-\x{3007}\x{3021}-\x{3029}\x{3031}-\x{3035}\x{3038}-\x{303c}\x{3041}-\x{3096}\x{309d}-\x{309f}\x{30a1}-\x{30fa}\x{30fc}-\x{30ff}\x{3105}-\x{312d}\x{3131}-\x{318e}\x{31a0}-\x{31ba}\x{31f0}-\x{31ff}\x{3400}-\x{4db5}\x{4e00}-\x{9fcc}\x{a000}-\x{a48c}\x{a4d0}-\x{a4fd}\x{a500}-\x{a60c}\x{a610}-\x{a61f}\x{a62a}\x{a62b}\x{a640}-\x{a66e}\x{a67f}-\x{a697}\x{a6a0}-\x{a6ef}\x{a717}-\x{a71f}\x{a722}-\x{a788}\x{a78b}-\x{a78e}\x{a790}-\x{a793}\x{a7a0}-\x{a7aa}\x{a7f8}-\x{a801}\x{a803}-\x{a805}\x{a807}-\x{a80a}\x{a80c}-\x{a822}\x{a840}-\x{a873}\x{a882}-\x{a8b3}\x{a8f2}-\x{a8f7}\x{a8fb}\x{a90a}-\x{a925}\x{a930}-\x{a946}\x{a960}-\x{a97c}\x{a984}-\x{a9b2}\x{a9cf}\x{aa00}-\x{aa28}\x{aa40}-\x{aa42}\x{aa44}-\x{aa4b}\x{aa60}-\x{aa76}\x{aa7a}\x{aa80}-\x{aaaf}\x{aab1}\x{aab5}\x{aab6}\x{aab9}-\x{aabd}\x{aac0}\x{aac2}\x{aadb}-\x{aadd}\x{aae0}-\x{aaea}\x{aaf2}-\x{aaf4}\x{ab01}-\x{ab06}Line truncated
@@ -119,21 +122,18 @@ class JS extends Minify
*/
protected $operatorsAfter = array();
/**
* {@inheritdoc}
*/
public function __construct()
{
call_user_func_array(array('parent', '__construct'), func_get_args());
call_user_func_array(array('\\MatthiasMullie\Minify\\Minify', '__construct'), func_get_args());
$dataDir = __DIR__.'/../data/js/';
$dataDir = __DIR__ . '/../data/js/';
$options = FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES;
$this->keywordsReserved = file($dataDir.'keywords_reserved.txt', $options);
$this->keywordsBefore = file($dataDir.'keywords_before.txt', $options);
$this->keywordsAfter = file($dataDir.'keywords_after.txt', $options);
$this->operators = file($dataDir.'operators.txt', $options);
$this->operatorsBefore = file($dataDir.'operators_before.txt', $options);
$this->operatorsAfter = file($dataDir.'operators_after.txt', $options);
$this->keywordsReserved = file($dataDir . 'keywords_reserved.txt', $options);
$this->keywordsBefore = file($dataDir . 'keywords_before.txt', $options);
$this->keywordsAfter = file($dataDir . 'keywords_after.txt', $options);
$this->operators = file($dataDir . 'operators.txt', $options);
$this->operatorsBefore = file($dataDir . 'operators_before.txt', $options);
$this->operatorsAfter = file($dataDir . 'operators_after.txt', $options);
}
/**
@@ -174,7 +174,7 @@ class JS extends Minify
$js = $this->stripWhitespace($js);
// combine js: separating the scripts by a ;
$content .= $js.";";
$content .= $js . ';';
}
// clean up leftover `;`s from the combination of multiple scripts
@@ -195,18 +195,7 @@ class JS extends Minify
*/
protected function stripComments()
{
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
$callback = function ($match) use ($minifier) {
$count = count($minifier->extracted);
$placeholder = '/*'.$count.'*/';
$minifier->extracted[$placeholder] = $match[0];
return $placeholder;
};
// multi-line comments
$this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback);
$this->registerPattern('/\/\*.*?\*\//s', '');
$this->stripMultilineComments();
// single-line comments
$this->registerPattern('/\/\/.*$/m', '');
@@ -235,7 +224,7 @@ class JS extends Minify
$minifier = $this;
$callback = function ($match) use ($minifier) {
$count = count($minifier->extracted);
$placeholder = '"'.$count.'"';
$placeholder = '"' . $count . '"';
$minifier->extracted[$placeholder] = $match[0];
return $placeholder;
@@ -254,7 +243,7 @@ class JS extends Minify
// of the RegExp methods (a `\` followed by a variable or value is
// likely part of a division, not a regex)
$keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
$before = '(^|[=:,;\+\-\*\/\}\(\{\[&\|!]|'.implode('|', $keywords).')\s*';
$before = '(^|[=:,;\+\-\*\?\/\}\(\{\[&\|!]|' . implode('|', $keywords) . ')\s*';
$propertiesAndMethods = array(
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
'constructor',
@@ -274,8 +263,8 @@ class JS extends Minify
);
$delimiters = array_fill(0, count($propertiesAndMethods), '/');
$propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
$after = '(?=\s*([\.,;\)\}&\|+]|\/\/|$|\.('.implode('|', $propertiesAndMethods).')))';
$this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
$after = '(?=\s*([\.,;:\)\}&\|+]|\/\/|$|\.(' . implode('|', $propertiesAndMethods) . ')))';
$this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
// regular expressions following a `)` are rather annoying to detect...
// quite often, `/` after `)` is a division operator & if it happens to
@@ -289,8 +278,8 @@ class JS extends Minify
// if a regex following `)` is not followed by `.<property or method>`,
// it's quite likely not a regex
$before = '\)\s*';
$after = '(?=\s*\.('.implode('|', $propertiesAndMethods).'))';
$this->registerPattern('/'.$before.'\K'.$pattern.$after.'/', $callback);
$after = '(?=\s*\.(' . implode('|', $propertiesAndMethods) . '))';
$this->registerPattern('/' . $before . '\K' . $pattern . $after . '/', $callback);
// 1 more edge case: a regex can be followed by a lot more operators or
// keywords if there's a newline (ASI) in between, where the operator
@@ -298,8 +287,8 @@ class JS extends Minify
// (https://github.com/matthiasmullie/minify/issues/56)
$operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
$operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
$after = '(?=\s*\n\s*('.implode('|', $operators).'))';
$this->registerPattern('/'.$pattern.$after.'/', $callback);
$after = '(?=\s*\n\s*(' . implode('|', $operators) . '))';
$this->registerPattern('/' . $pattern . $after . '/', $callback);
}
/**
@@ -343,8 +332,8 @@ class JS extends Minify
unset($operatorsBefore['+'], $operatorsBefore['-'], $operatorsAfter['+'], $operatorsAfter['-']);
$content = preg_replace(
array(
'/('.implode('|', $operatorsBefore).')\s+/',
'/\s+('.implode('|', $operatorsAfter).')/',
'/(' . implode('|', $operatorsBefore) . ')\s+/',
'/\s+(' . implode('|', $operatorsAfter) . ')/',
),
'\\1',
$content
@@ -361,8 +350,8 @@ class JS extends Minify
);
// collapse whitespace around reserved words into single space
$content = preg_replace('/(^|[;\}\s])\K('.implode('|', $keywordsBefore).')\s+/', '\\2 ', $content);
$content = preg_replace('/\s+('.implode('|', $keywordsAfter).')(?=([;\{\s]|$))/', ' \\1', $content);
$content = preg_replace('/(^|[;\}\s])\K(' . implode('|', $keywordsBefore) . ')\s+/', '\\2 ', $content);
$content = preg_replace('/\s+(' . implode('|', $keywordsAfter) . ')(?=([;\{\s]|$))/', ' \\1', $content);
/*
* We didn't strip whitespace after a couple of operators because they
@@ -372,8 +361,8 @@ class JS extends Minify
*/
$operatorsDiffBefore = array_diff($operators, $operatorsBefore);
$operatorsDiffAfter = array_diff($operators, $operatorsAfter);
$content = preg_replace('/('.implode('|', $operatorsDiffBefore).')[^\S\n]+/', '\\1', $content);
$content = preg_replace('/[^\S\n]+('.implode('|', $operatorsDiffAfter).')/', '\\1', $content);
$content = preg_replace('/(' . implode('|', $operatorsDiffBefore) . ')[^\S\n]+/', '\\1', $content);
$content = preg_replace('/[^\S\n]+(' . implode('|', $operatorsDiffAfter) . ')/', '\\1', $content);
/*
* Whitespace after `return` can be omitted in a few occasions
@@ -405,9 +394,26 @@ class JS extends Minify
* to be the for-loop's body... Same goes for while loops.
* I'm going to double that semicolon (if any) so after the next line,
* which strips semicolons here & there, we're still left with this one.
* Note the special recursive construct in the three inner parts of the for:
* (\{([^\{\}]*(?-2))*[^\{\}]*\})? - it is intended to match inline
* functions bodies, e.g.: i<arr.map(function(e){return e}).length.
* Also note that the construct is applied only once and multiplied
* for each part of the for, otherwise it risks a catastrophic backtracking.
* The limitation is that it will not allow closures in more than one
* of the three parts for a specific for() case.
* REGEX throwing catastrophic backtracking: $content = preg_replace('/(for\([^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*;[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*\));(\}|$)/s', '\\1;;\\8', $content);
*/
$content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
$content = preg_replace('/(for\((?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
$content = preg_replace('/(for\([^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*);[^;\{]*\));(\}|$)/s', '\\1;;\\4', $content);
$content = preg_replace('/(for\([^;\{]*;[^;\{]*;(?:[^;\{]*|[^;\{]*function[^;\{]*(\{([^\{\}]*(?-2))*[^\{\}]*\})?[^;\{]*)\));(\}|$)/s', '\\1;;\\4', $content);
$content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
/*
* Do the same for the if's that don't have a body but are followed by ;}
*/
$content = preg_replace('/(\bif\s*\([^{;]*\));\}/s', '\\1;;}', $content);
/*
* Below will also keep `;` after a `do{}while();` along with `while();`
* While these could be stripped after do-while, detecting this
@@ -444,7 +450,7 @@ class JS extends Minify
* This will prepare the given array by escaping all characters.
*
* @param string[] $operators
* @param string $delimiter
* @param string $delimiter
*
* @return string[]
*/
@@ -465,7 +471,7 @@ class JS extends Minify
// don't confuse = with other assignment shortcuts (e.g. +=)
$chars = preg_quote('+-*\=<>%&|', $delimiter);
$operators['='] = '(?<!['.$chars.'])\=';
$operators['='] = '(?<![' . $chars . '])\=';
return $operators;
}
@@ -475,7 +481,7 @@ class JS extends Minify
* This will prepare the given array by escaping all characters.
*
* @param string[] $keywords
* @param string $delimiter
* @param string $delimiter
*
* @return string[]
*/
@@ -487,7 +493,7 @@ class JS extends Minify
// add word boundaries
array_walk($keywords, function ($value) {
return '\b'.$value.'\b';
return '\b' . $value . '\b';
});
$keywords = array_combine($keywords, $escaped);
@@ -524,11 +530,11 @@ class JS extends Minify
* array['key-here'] can't be replaced by array.key-here since '-'
* is not a valid character there.
*/
if (!preg_match('/^'.$minifier::REGEX_VARIABLE.'$/u', $property)) {
if (!preg_match('/^' . $minifier::REGEX_VARIABLE . '$/u', $property)) {
return $match[0];
}
return '.'.$property;
return '.' . $property;
};
/*
@@ -549,9 +555,9 @@ class JS extends Minify
* separate look-behind assertions, one for each keyword.
*/
$keywords = $this->getKeywordsForRegex($keywords);
$keywords = '(?<!'.implode(')(?<!', $keywords).')';
$keywords = '(?<!' . implode(')(?<!', $keywords) . ')';
return preg_replace_callback('/(?<='.$previousChar.'|\])'.$keywords.'\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
return preg_replace_callback('/(?<=' . $previousChar . '|\])' . $keywords . '\[\s*(([\'"])[0-9]+\\2)\s*\]/u', $callback, $content);
}
/**
@@ -575,7 +581,7 @@ class JS extends Minify
return $match[0];
}
return $match[1].($match[2] === 'true' ? '!0' : '!1');
return $match[1] . ($match[2] === 'true' ? '!0' : '!1');
};
$content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
+126 -56
View File
@@ -1,6 +1,7 @@
<?php
/**
* Abstract minifier class
* Abstract minifier class.
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
@@ -8,9 +9,11 @@
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
namespace MatthiasMullie\Minify;
use MatthiasMullie\Minify\Exceptions\IOException;
use MatthiasMullie\Minify\Exceptions\PatternMatchException;
use Psr\Cache\CacheItemInterface;
/**
@@ -18,7 +21,6 @@ use Psr\Cache\CacheItemInterface;
*
* Please report bugs on https://github.com/matthiasmullie/minify/issues
*
* @package Minify
* @author Matthias Mullie <minify@mullie.eu>
* @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
@@ -44,6 +46,8 @@ abstract class Minify
* been extracted from the JS source code, so we can reliably match "code",
* without having to worry about potential "code-like" characters inside.
*
* @internal
*
* @var string[]
*/
public $extracted = array();
@@ -128,7 +132,7 @@ abstract class Minify
// check if we can read the file
if (!$this->canImportFile($path)) {
throw new IOException('The file "'.$path.'" could not be opened for reading. Check if PHP has enough permissions.');
throw new IOException('The file "' . $path . '" could not be opened for reading. Check if PHP has enough permissions.');
}
$this->add($path);
@@ -227,7 +231,7 @@ abstract class Minify
* Save to file.
*
* @param string $content The minified data
* @param string $path The path to save the minified data to
* @param string $path The path to save the minified data to
*
* @throws IOException
*/
@@ -243,7 +247,10 @@ abstract class Minify
/**
* Register a pattern to execute against the source content.
*
* @param string $pattern PCRE pattern
* If $replacement is a string, it must be plain text. Placeholders like $1 or \2 don't work.
* If you need that functionality, use a callback instead.
*
* @param string $pattern PCRE pattern
* @param string|callable $replacement Replacement value for matched pattern
*/
protected function registerPattern($pattern, $replacement = '')
@@ -254,6 +261,45 @@ abstract class Minify
$this->patterns[] = array($pattern, $replacement);
}
/**
* Both JS and CSS use the same form of multi-line comment, so putting the common code here.
*/
protected function stripMultilineComments()
{
$minifier = $this;
// Pattern for matching comments that we want to preserve
$keepPattern = '/^
# comment content
(?:
# either starts with an !
!
|
# or, after some number of characters which do not end the comment
(?:(?!\*\/).)*?
# there is either a @license or @preserve tag
@(?:license|preserve)
)
/ixs';
$callback = function ($match) use ($minifier, $keepPattern) {
if (preg_match($keepPattern, $match[1])) {
// Preserve the comment
$count = count($minifier->extracted);
$placeholder = '/*' . $count . '*/';
$minifier->extracted[$placeholder] = $match[0];
} else {
// Discard the comment but keep any single line feed
$placeholder = strncmp($match[0], "\n", 1) === 0 || substr($match[0], -1) === "\n"
? "\n"
: '';
}
return $placeholder;
};
$this->registerPattern('/\n?\/\*(.*?)\*\/\n?/s', $callback);
}
/**
* We can't "just" run some regular expressions against JavaScript: it's a
* complex language. E.g. having an occurrence of // xyz would be a comment,
@@ -265,14 +311,18 @@ abstract class Minify
* @param string $content The content to replace patterns in
*
* @return string The (manipulated) content
*
* @throws PatternMatchException
*/
protected function replace($content)
{
$processed = '';
$contentLength = strlen($content);
$output = '';
$processedOffset = 0;
$positions = array_fill(0, count($this->patterns), -1);
$matches = array();
while ($content) {
while ($processedOffset < $contentLength) {
// find first match for all patterns
foreach ($this->patterns as $i => $pattern) {
list($pattern, $replacement) = $pattern;
@@ -285,12 +335,13 @@ abstract class Minify
// no need to re-run matches that are still in the part of the
// content that hasn't been processed
if ($positions[$i] >= 0) {
if ($positions[$i] >= $processedOffset) {
continue;
}
$match = null;
if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
$matchResult = preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE, $processedOffset);
if ($matchResult) {
$matches[$i] = $match;
// we'll store the match position as well; that way, we
@@ -298,6 +349,11 @@ abstract class Minify
// the first (we'll still know where those others are)
$positions[$i] = $match[0][1];
} else {
if ($matchResult === false) {
throw PatternMatchException::fromLastError(
"Failed to match pattern '$pattern' at $processedOffset"
);
}
// if the pattern couldn't be matched, there's no point in
// executing it again in later runs on this same content;
// ignore this one until we reach end of content
@@ -307,61 +363,53 @@ abstract class Minify
// no more matches to find: everything's been processed, break out
if (!$matches) {
$processed .= $content;
// output the remaining content
$output .= substr($content, $processedOffset);
break;
}
// see which of the patterns actually found the first thing (we'll
// only want to execute that one, since we're unsure if what the
// other found was not inside what the first found)
$discardLength = min($positions);
$firstPattern = array_search($discardLength, $positions);
$match = $matches[$firstPattern][0][0];
$matchOffset = min($positions);
$firstPattern = array_search($matchOffset, $positions);
$match = $matches[$firstPattern];
// execute the pattern that matches earliest in the content string
list($pattern, $replacement) = $this->patterns[$firstPattern];
$replacement = $this->replacePattern($pattern, $replacement, $content);
list(, $replacement) = $this->patterns[$firstPattern];
// figure out which part of the string was unmatched; that's the
// part we'll execute the patterns on again next
$content = (string) substr($content, $discardLength);
$unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
// move the replaced part to $processed and prepare $content to
// again match batch of patterns against
$processed .= substr($replacement, 0, strlen($replacement) - strlen($unmatched));
$content = $unmatched;
// first match has been replaced & that content is to be left alone,
// the next matches will start after this replacement, so we should
// fix their offsets
foreach ($positions as $i => $position) {
$positions[$i] -= $discardLength + strlen($match);
}
// add the part of the input between $processedOffset and the first match;
// that content wasn't matched by anything
$output .= substr($content, $processedOffset, $matchOffset - $processedOffset);
// add the replacement for the match
$output .= $this->executeReplacement($replacement, $match);
// advance $processedOffset past the match
$processedOffset = $matchOffset + strlen($match[0][0]);
}
return $processed;
return $output;
}
/**
* This is where a pattern is matched against $content and the matches
* are replaced by their respective value.
* This function will be called plenty of times, where $content will always
* move up 1 character.
* If $replacement is a callback, execute it, passing in the match data.
* If it's a string, just pass it through.
*
* @param string $pattern Pattern to match
* @param string|callable $replacement Replacement value
* @param string $content Content to match pattern against
* @param array $match Match data, in PREG_OFFSET_CAPTURE form
*
* @return string
*/
protected function replacePattern($pattern, $replacement, $content)
protected function executeReplacement($replacement, $match)
{
if (is_callable($replacement)) {
return preg_replace_callback($pattern, $replacement, $content, 1, $count);
} else {
return preg_replace($pattern, $replacement, $content, 1, $count);
if (!is_callable($replacement)) {
return $replacement;
}
// convert $match from the PREG_OFFSET_CAPTURE form to the form the callback expects
foreach ($match as &$matchItem) {
$matchItem = $matchItem[0];
}
return $replacement($match);
}
/**
@@ -395,13 +443,18 @@ abstract class Minify
}
$count = count($minifier->extracted);
$placeholder = $match[1].$placeholderPrefix.$count.$match[1];
$minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
$placeholder = $match[1] . $placeholderPrefix . $count . $match[1];
$minifier->extracted[$placeholder] = $match[1] . $match[2] . $match[1];
return $placeholder;
};
/*
* Quantifier {0,65535} is used instead of *? to avoid exceeding
* backtrack limit with large strings. 65535 is the maximum allowed
* (see https://www.php.net/manual/en/regexp.reference.repetition.php)
* and should be well sufficient for string representations here.
*
* The \\ messiness explained:
* * Don't count ' or " as end-of-string if it's escaped (has backslash
* in front of it)
@@ -413,7 +466,8 @@ abstract class Minify
* considered as escape-char (times 2) and to get it in the regex,
* escaped (times 2)
*/
$this->registerPattern('/(['.$chars.'])(.*?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
$this->registerPattern('/([' . $chars . '])(.{0,65535}?(?<!\\\\)(\\\\\\\\)*+)\\1/s', $callback);
}
/**
@@ -451,14 +505,20 @@ abstract class Minify
$parsed = parse_url($path);
if (
// file is elsewhere
isset($parsed['host']) ||
isset($parsed['host'])
// file responds to queries (may change, or need to bypass cache)
isset($parsed['query'])
|| isset($parsed['query'])
) {
return false;
}
return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
try {
return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
}
// catch openbasedir exceptions which are not caught by @ on is_file()
catch (\Exception $e) {
return false;
}
}
/**
@@ -473,7 +533,7 @@ abstract class Minify
protected function openFileForWriting($path)
{
if ($path === '' || ($handler = @fopen($path, 'w')) === false) {
throw new IOException('The file "'.$path.'" could not be opened for writing. Check if PHP has enough permissions.');
throw new IOException('The file "' . $path . '" could not be opened for writing. Check if PHP has enough permissions.');
}
return $handler;
@@ -483,19 +543,29 @@ abstract class Minify
* Attempts to write $content to the file specified by $handler. $path is used for printing exceptions.
*
* @param resource $handler The resource to write to
* @param string $content The content to write
* @param string $path The path to the file (for exception printing only)
* @param string $content The content to write
* @param string $path The path to the file (for exception printing only)
*
* @throws IOException
*/
protected function writeToFile($handler, $content, $path = '')
{
if (
!is_resource($handler) ||
($result = @fwrite($handler, $content)) === false ||
($result < strlen($content))
!is_resource($handler)
|| ($result = @fwrite($handler, $content)) === false
|| ($result < strlen($content))
) {
throw new IOException('The file "'.$path.'" could not be written to. Check your disk space and file permissions.');
throw new IOException('The file "' . $path . '" could not be written to. Check your disk space and file permissions.');
}
}
protected static function str_replace_first($search, $replace, $subject)
{
$pos = strpos($subject, $search);
if ($pos !== false) {
return substr_replace($subject, $replace, $pos, strlen($search));
}
return $subject;
}
}