--- /dev/null
+Copyright (c) 2012 Matthias Mullie
+
+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 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.
<?php
+/**
+ * CSS Minifier
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
namespace MatthiasMullie\Minify;
use MatthiasMullie\PathConverter\Converter;
/**
- * 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
class CSS extends Minify
{
/**
- * @var int
+ * @var int maximum inport size in kB
*/
protected $maxImportSize = 5;
/**
- * @var string[]
+ * @var string[] valid import extensions
*/
protected $importExtensions = array(
'gif' => 'data:image/gif',
*/
protected function moveImportsToTop($content)
{
- if (preg_match_all('/@import[^;]+;/', $content, $matches)) {
+ if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) {
// remove from content
foreach ($matches[0] as $import) {
$content = str_replace($import, '', $content);
}
// add to top
- $content = implode('', $matches[0]).$content;
+ $content = implode(';', $matches[2]).';'.trim($content, ';');
}
return $content;
// grab referenced file & minify it (which may include importing
// yet other @import statements recursively)
$minifier = new static($importPath);
+ $minifier->setMaxImportSize($this->maxImportSize);
+ $minifier->setImportExtensions($this->importExtensions);
$importContent = $minifier->execute($source, $parents);
// check if this is only valid for certain media
*/
$this->extractStrings();
$this->stripComments();
+ $this->extractCalcs();
$css = $this->replace($css);
$css = $this->stripWhitespace($css);
- $css = $this->shortenHex($css);
+ $css = $this->shortenColors($css);
$css = $this->shortenZeroes($css);
$css = $this->shortenFontWeights($css);
$css = $this->stripEmptyTags($css);
*
* @return string
*/
- protected function shortenHex($content)
+ protected function shortenColors($content)
{
- $content = preg_replace('/(?<=[: ])#([0-9a-z])\\1([0-9a-z])\\2([0-9a-z])\\3(?=[; }])/i', '#$1$2$3', $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);
+
+ // 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);
- // we can shorten some even more by replacing them with their color name
$colors = array(
+ // we can shorten some even more by replacing them with their color name
'#F0FFFF' => 'azure',
'#F5F5DC' => 'beige',
'#A52A2A' => 'brown',
'#FF6347' => 'tomato',
'#EE82EE' => 'violet',
'#F5DEB3' => 'wheat',
+ // or the other way around
+ 'WHITE' => '#fff',
+ 'BLACK' => '#000',
);
return preg_replace_callback(
*/
protected function shortenZeroes($content)
{
+ // we don't want to strip units in `calc()` expressions:
+ // `5px - 0px` is valid, but `5px - 0` is not
+ // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
+ // `10 * 0` is invalid
+ // we've extracted calcs earlier, so we don't need to worry about this
+
// reusable bits of code throughout these regexes:
// before & after are used to make sure we don't match lose unintended
// 0-like values (e.g. in #000, or in http://url/1.0)
// strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
$content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
- // remove zeroes where they make no sense in calc: e.g. calc(100px - 0)
- // the 0 doesn't have any effect, and this isn't even valid without unit
- // strip all `+ 0` or `- 0` occurrences: calc(10% + 0) -> calc(10%)
- // looped because there may be multiple 0s inside 1 group of parentheses
- do {
- $previous = $content;
- $content = preg_replace('/\(([^\(\)]+) [\+\-] 0( [^\(\)]+)?\)/', '(\\1\\2)', $content);
- } while ($content !== $previous);
- // strip all `0 +` occurrences: calc(0 + 10%) -> calc(10%)
- $content = preg_replace('/\(0 \+ ([^\(\)]+)\)/', '(\\1)', $content);
- // strip all `0 -` occurrences: calc(0 - 10%) -> calc(-10%)
- $content = preg_replace('/\(0 \- ([^\(\)]+)\)/', '(-\\1)', $content);
- // I'm not going to attempt to optimize away `x * 0` instances:
- // it's dumb enough code already that it likely won't occur, and it's
- // too complex to do right (order of operations would have to be
- // respected etc)
- // what I cared about most here was fixing incorrectly truncated units
-
- // IE doesn't seem to understand a unitless flex-basis value, so let's
- // add it in again (make it `%`, which is only 1 char: 0%, 0px, 0
- // anything, it's all just the same)
- $content = preg_replace('/flex:([^ ]+ [^ ]+ )0([;\}])/', 'flex:${1}0%${2}', $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 `%`,
+ // which is only 1 char: 0%, 0px, 0 anything, it's all just the same)
+ // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex
+ $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:${1}0%${2}', $content);
$content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
return $content;
*/
protected function stripEmptyTags($content)
{
- return preg_replace('/(^|\}|;)[^\{\};]+\{\s*\}/', '\\1', $content);
+ $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content);
+ $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content);
+
+ return $content;
}
/**
*/
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', '');
}
// 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 = preg_replace('/\s+([\]\)>\+])/', '$1', $content);
$content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
// whitespace around + and - can only be stripped inside some pseudo-
return trim($content);
}
+ /**
+ * Replace all `calc()` occurrences.
+ */
+ protected function extractCalcs()
+ {
+ // PHP only supports $this inside anonymous functions since 5.4
+ $minifier = $this;
+ $callback = function ($match) use ($minifier) {
+ $length = strlen($match[1]);
+ $expr = '';
+ $opened = 0;
+
+ for ($i = 0; $i < $length; $i++) {
+ $char = $match[1][$i];
+ $expr .= $char;
+ if ($char === '(') {
+ $opened++;
+ } elseif ($char === ')' && --$opened === 0) {
+ break;
+ }
+ }
+ $rest = str_replace($expr, '', $match[1]);
+ $expr = trim(substr($expr, 1, -1));
+
+ $count = count($minifier->extracted);
+ $placeholder = 'calc('.$count.')';
+ $minifier->extracted[$placeholder] = 'calc('.$expr.')';
+
+ return $placeholder.$rest;
+ };
+
+ $this->registerPattern('/calc(\(.+?)(?=$|;|calc\()/', $callback);
+ }
+
/**
* Check if file is small enough to be imported.
*
<?php
-
+/**
+ * Base Exception
+ *
+ * @deprecated Use Exceptions\BasicException instead
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ */
namespace MatthiasMullie\Minify;
/**
+ * Base Exception Class
* @deprecated Use Exceptions\BasicException instead
*
+ * @package Minify
* @author Matthias Mullie <minify@mullie.eu>
*/
abstract class Exception extends \Exception
<?php
-
+/**
+ * Basic exception
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
namespace MatthiasMullie\Minify\Exceptions;
use MatthiasMullie\Minify\Exception;
/**
+ * Basic Exception Class
+ *
+ * @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
abstract class BasicException extends Exception
<?php
-
+/**
+ * File Import Exception
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
namespace MatthiasMullie\Minify\Exceptions;
/**
+ * File Import Exception Class
+ *
+ * @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
class FileImportException extends BasicException
<?php
-
+/**
+ * IO Exception
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
namespace MatthiasMullie\Minify\Exceptions;
/**
+ * IO Exception Class
+ *
+ * @package Minify\Exception
* @author Matthias Mullie <minify@mullie.eu>
*/
class IOException extends BasicException
<?php
-
+/**
+ * JavaScript minifier
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
namespace MatthiasMullie\Minify;
/**
- * JavaScript minifier.
+ * 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
*/
protected $operatorsAfter = array();
- /**
- * @var array
- */
- protected $nestedExtracted = array();
-
/**
* {@inheritdoc}
*/
{
$content = '';
- // loop files
- foreach ($this->data as $source => $js) {
- /*
- * Combine js: separating the scripts by a ;
- * I'm also adding a newline: it will be eaten when whitespace is
- * stripped, but we need to make sure we're not just appending
- * a new script right after a previous script that ended with a
- * singe-line comment on the last line (in which case it would also
- * be seen as part of that comment)
- */
- $content .= $js."\n;";
- }
-
/*
* Let's first take out strings, comments and regular expressions.
* All of these can contain JS code-like characters, and we should make
$this->extractStrings('\'"`');
$this->stripComments();
$this->extractRegex();
- $content = $this->replace($content);
- $content = $this->propertyNotation($content);
- $content = $this->shortenBools($content);
- $content = $this->stripWhitespace($content);
+ // loop files
+ foreach ($this->data as $source => $js) {
+ // take out strings, comments & regex (for which we've registered
+ // the regexes just a few lines earlier)
+ $js = $this->replace($js);
+
+ $js = $this->propertyNotation($js);
+ $js = $this->shortenBools($js);
+ $js = $this->stripWhitespace($js);
+
+ // combine js: separating the scripts by a ;
+ $content .= $js.";";
+ }
+
+ // clean up leftover `;`s from the combination of multiple scripts
+ $content = ltrim($content, ';');
+ $content = (string) substr($content, 0, -1);
/*
* Earlier, we extracted strings & regular expressions and replaced them
*/
protected function stripComments()
{
- // single-line comments
- $this->registerPattern('/\/\/.*$/m', '');
+ // 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', '');
+
+ // single-line comments
+ $this->registerPattern('/\/\/.*$/m', '');
}
/**
$callback = function ($match) use ($minifier) {
$count = count($minifier->extracted);
$placeholder = '"'.$count.'"';
- $minifier->extracted[$placeholder] = $match['regex'];
-
- // because we're also trying to find regular expressions that follow
- // if/when/for statements, we should also make sure that the content
- // within these statements is also minified...
- // e.g. `if("some string"/* or comment */)` should become
- // `if("some string")`
- if (isset($match['before'])) {
- $other = new static();
- $other->extractStrings('\'"`', "$count-");
- $other->stripComments();
- $match['before'] = $other->replace($match['before']);
- $this->nestedExtracted += $other->extracted;
- }
+ $minifier->extracted[$placeholder] = $match[0];
- return (isset($match['before']) ? $match['before'] : '').
- $placeholder.
- (isset($match['after']) ? $match['after'] : '');
+ return $placeholder;
};
- $pattern = '(?P<regex>\/.+?((?<!\\\\)\\\\\\\\)*\/[gimy]*)(?![0-9a-zA-Z\/])';
+ // match all chars except `/` and `\`
+ // `\` is allowed though, along with whatever char follows (which is the
+ // one being escaped)
+ // this should allow all chars, except for an unescaped `/` (= the one
+ // closing the regex)
+ // then also ignore bare `/` inside `[]`, where they don't need to be
+ // escaped: anything inside `[]` can be ignored safely
+ $pattern = '\\/(?!\*)(?:[^\\[\\/\\\\\n\r]++|(?:\\\\.)++|(?:\\[(?:[^\\]\\\\\n\r]++|(?:\\\\.)++)++\\])++)++\\/[gimuy]*';
// a regular expression can only be followed by a few operators or some
// 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 = '(?P<before>[=:,;\}\(\{&\|!]|^|'.implode('|', $keywords).')';
+ $before = '([=:,;\+\-\*\/\}\(\{\[&\|!]|^|'.implode('|', $keywords).')\s*';
$propertiesAndMethods = array(
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties
- 'prototype',
- 'length',
- 'lastIndex',
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Properties_2
'constructor',
'flags',
'compile(',
'exec(',
'test(',
- 'match',
- 'replace(',
- 'search(',
- 'split(',
'toSource(',
'toString(',
);
$delimiters = array_fill(0, count($propertiesAndMethods), '/');
$propertiesAndMethods = array_map('preg_quote', $propertiesAndMethods, $delimiters);
- $after = '(?P<after>[\.,;\)\}&\|+]|$|\.('.implode('|', $propertiesAndMethods).'))';
- $this->registerPattern('/'.$before.'\s*'.$pattern.'\s*'.$after.'/', $callback);
-
- // we didn't check for regular expressions after `)`, because that is
- // more often than not not a character where a regex can follow (e.g.
- // (1+2)/3/4 -> /3/ could be considered a regex, but it's not)
- // however, after single-line if/while/for, there could very well be a
- // regex after `)` (e.g. if(true)/regex/)
- // there is one problem, though: it's (near) impossible to check for
- // when the if/while/for statement is closed (same amount of closing
- // brackets as there were opened), so I'll ignore single-line statements
- // with nested brackets followed by a regex for now...
- $before = '(?P<before>\b(if|while|for)\s*\((?P<code>[^\(]+?)\))';
- $this->registerPattern('/'.$before.'\s*'.$pattern.'\s*'.$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
+ // be followed by another one (or a comment), it is likely to be
+ // confused for a regular expression
+ // however, it's perfectly possible for a regex to follow a `)`: after
+ // a single-line `if()`, `while()`, ... statement, for example
+ // since, when they occur like that, they're always the start of a
+ // statement, there's only a limited amount of ways they can be useful:
+ // by calling the regex methods directly
+ // 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);
// 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
// (https://github.com/matthiasmullie/minify/issues/56)
$operators = $this->getOperatorsForRegex($this->operatorsBefore, '/');
$operators += $this->getOperatorsForRegex($this->keywordsReserved, '/');
- $after = '(?P<after>\n\s*('.implode('|', $operators).'))';
- $this->registerPattern('/'.$pattern.'\s*'.$after.'/', $callback);
- }
-
- /**
- * In addition to the regular restore routine, we also need to restore a few
- * more things that have been extracted as part of the regex extraction...
- *
- * {@inheritdoc}
- */
- protected function restoreExtractedData($content)
- {
- // restore regular extracted stuff
- $content = parent::restoreExtractedData($content);
-
- // restore nested stuff from within regex extraction
- $content = strtr($content, $this->nestedExtracted);
-
- return $content;
+ $after = '(?=\s*\n\s*('.implode('|', $operators).'))';
+ $this->registerPattern('/'.$pattern.$after.'/', $callback);
}
/**
array(
'/('.implode('|', $operatorsBefore).')\s+/',
'/\s+('.implode('|', $operatorsAfter).')/',
- ), '\\1', $content
+ ),
+ '\\1',
+ $content
);
// make sure + and - can't be mistaken for, or joined into ++ and --
array(
'/(?<![\+\-])\s*([\+\-])(?![\+\-])/',
'/(?<![\+\-])([\+\-])\s*(?![\+\-])/',
- ), '\\1', $content
+ ),
+ '\\1',
+ $content
);
// collapse whitespace around reserved words into single space
$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
+ * (such as when followed by a string or regex)
+ * Same for whitespace in between `)` and `{`, or between `{` and some
+ * keywords.
+ */
+ $content = preg_replace('/\breturn\s+(["\'\/\+\-])/', 'return$1', $content);
+ $content = preg_replace('/\)\s+\{/', '){', $content);
+ $content = preg_replace('/}\n(else|catch|finally)\b/', '}$1', $content);
+
/*
* Get rid of double semicolons, except where they can be used like:
* "for(v=1,_=b;;)", "for(v=1;;v++)" or "for(;;ja||(ja=true))".
* semicolon), like: `for(i=1;i<3;i++);`, of `for(i in list);`
* Here, nothing happens during the loop; it's just used to keep
* increasing `i`. With that ; omitted, the next line would be expected
- * to be the for-loop's body...
+ * 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.
*/
$content = preg_replace('/(for\([^;\{]*;[^;\{]*;[^;\{]*\));(\}|$)/s', '\\1;;\\2', $content);
$content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
+ /*
+ * Below will also keep `;` after a `do{}while();` along with `while();`
+ * While these could be stripped after do-while, detecting this
+ * distinction is cumbersome, so I'll play it safe and make sure `;`
+ * after any kind of `while` is kept.
+ */
+ $content = preg_replace('/(while\([^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
/*
* We also can't strip empty else-statements. Even though they're
<?php
-
+/**
+ * Abstract minifier class
+ *
+ * Please report bugs on https://github.com/matthiasmullie/minify/issues
+ *
+ * @author Matthias Mullie <minify@mullie.eu>
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
namespace MatthiasMullie\Minify;
use MatthiasMullie\Minify\Exceptions\IOException;
*
* 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
foreach ($this->patterns as $i => $pattern) {
list($pattern, $replacement) = $pattern;
+ // we can safely ignore patterns for positions we've unset earlier,
+ // because we know these won't show up anymore
+ if (array_key_exists($i, $positions) == false) {
+ continue;
+ }
+
// no need to re-run matches that are still in the part of the
// content that hasn't been processed
if ($positions[$i] >= 0) {
}
$match = null;
- if (preg_match($pattern, $content, $match)) {
+ if (preg_match($pattern, $content, $match, PREG_OFFSET_CAPTURE)) {
$matches[$i] = $match;
// we'll store the match position as well; that way, we
// don't have to redo all preg_matches after changing only
// the first (we'll still know where those others are)
- $positions[$i] = strpos($content, $match[0]);
+ $positions[$i] = $match[0][1];
} else {
// 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
- unset($matches[$i]);
- $positions[$i] = strlen($content);
+ unset($matches[$i], $positions[$i]);
}
}
// other found was not inside what the first found)
$discardLength = min($positions);
$firstPattern = array_search($discardLength, $positions);
- $match = $matches[$firstPattern][0];
+ $match = $matches[$firstPattern][0][0];
// execute the pattern that matches earliest in the content string
list($pattern, $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 = substr($content, $discardLength);
+ $content = (string) substr($content, $discardLength);
$unmatched = (string) substr($content, strpos($content, $match) + strlen($match));
// move the replaced part to $processed and prepare $content to
*/
protected function canImportFile($path)
{
+ $parsed = parse_url($path);
+ if (
+ // file is elsewhere
+ isset($parsed['host']) ||
+ // file responds to queries (may change, or need to bypass cache)
+ isset($parsed['query'])
+ ) {
+ return false;
+ }
+
return strlen($path) < PHP_MAXPATHLEN && @is_file($path) && is_readable($path);
}
<location>minify/matthiasmullie-minify</location>
<name>MatthiasMullie\Minify</name>
<license>MIT</license>
- <version>1.3.51</version>
+ <version>1.3.61</version>
<licenseversion></licenseversion>
</library>
<library>