extends
package
private
-continue
function
protected
implements
namespace MatthiasMullie\Minify;
use MatthiasMullie\Minify\Exceptions\FileImportException;
+use MatthiasMullie\PathConverter\ConverterInterface;
use MatthiasMullie\PathConverter\Converter;
/**
*
* @author Matthias Mullie <minify@mullie.eu>
* @author Tijs Verkoyen <minify@verkoyen.eu>
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved.
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
class CSS extends Minify
// add to top
$content = implode('', $matches[0]).$content;
- };
+ }
return $content;
}
* @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[] $parents Parent paths, for circular reference checks.
+ * @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
*
(?P<quotes>["\']?)
# fetch path
- (?P<path>
-
- # do not fetch data uris or external sources
- (?!(
- ["\']?
- (data|https?):
- ))
-
- .+?
- )
+ (?P<path>.+?)
# (optional) close path enclosure
(?P=quotes)
(?P<quotes>["\'])
# fetch path
- (?P<path>
-
- # do not fetch data uris or external sources
- (?!(
- ["\']?
- (data|https?):
- ))
-
- .+?
- )
+ (?P<path>.+?)
# close path enclosure
(?P=quotes)
// only replace the import with the content if we can grab the
// content of the file
- if ($this->canImportFile($importPath)) {
- // 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.');
- }
+ if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) {
+ continue;
+ }
- // grab referenced file & minify it (which may include importing
- // yet other @import statements recursively)
- $minifier = new static($importPath);
- $importContent = $minifier->execute($source, $parents);
+ // 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.');
+ }
- // check if this is only valid for certain media
- if (!empty($match['media'])) {
- $importContent = '@media '.$match['media'].'{'.$importContent.'}';
- }
+ // grab referenced file & minify it (which may include importing
+ // yet other @import statements recursively)
+ $minifier = new static($importPath);
+ $importContent = $minifier->execute($source, $parents);
- // add to replacement array
- $search[] = $match[0];
- $replace[] = $importContent;
+ // check if this is only valid for certain media
+ if (!empty($match['media'])) {
+ $importContent = '@media '.$match['media'].'{'.$importContent.'}';
}
+
+ // add to replacement array
+ $search[] = $match[0];
+ $replace[] = $importContent;
}
// replace the import statements
- $content = str_replace($search, $replace, $content);
-
- return $content;
+ return str_replace($search, $replace, $content);
}
/**
* @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 $content The CSS content 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
*/
protected function importFiles($source, $content)
{
- $extensions = array_keys($this->importExtensions);
- $regex = '/url\((["\']?)((?!["\']?data:).*?\.('.implode('|', $extensions).'))\\1\)/i';
- if ($extensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
+ $regex = '/url\((["\']?)(.+?)\\1\)/i';
+ if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) {
$search = array();
$replace = array();
// loop the matches
foreach ($matches as $match) {
+ $extension = substr(strrchr($match[2], '.'), 1);
+ if ($extension && !array_key_exists($extension, $this->importExtensions)) {
+ continue;
+ }
+
// get the path for the file that will be imported
$path = $match[2];
$path = dirname($source).'/'.$path;
- $extension = $match[3];
// only replace the import with the content if we're able to get
// the content of the file, and it's relatively small
* Minify the data.
* Perform CSS optimizations.
*
- * @param string[optional] $path Path to write the data to.
- * @param string[] $parents Parent paths, for circular reference checks.
+ * @param string[optional] $path Path to write the data to
+ * @param string[] $parents Parent paths, for circular reference checks
*
- * @return string The minified data.
+ * @return string The minified data
*/
public function execute($path = null, $parents = array())
{
$content = '';
- // loop css data (raw data and files)
+ // loop CSS data (raw data and files)
foreach ($this->data as $source => $css) {
/*
- * Let's first take out strings & comments, since we can't just remove
- * whitespace anywhere. If whitespace occurs inside a string, we should
- * leave it alone. E.g.:
+ * Let's first take out strings & comments, since we can't just
+ * remove whitespace anywhere. If whitespace occurs inside a string,
+ * we should leave it alone. E.g.:
* p { content: "a test" }
*/
$this->extractStrings();
* to be relative no longer to the source file, but to the new path.
* If we don't write to a file, fall back to same path so no
* conversion happens (because we still want it to go through most
- * of the move code...)
+ * of the move code, which also addresses url() & @import syntax...)
*/
- $converter = new Converter($source, $path ?: $source);
+ $converter = $this->getPathConverter($source, $path ?: $source);
$css = $this->move($converter, $css);
// combine css
* will have to be updated when a file is being saved at another location
* (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper).
*
- * @param Converter $converter Relative path converter
- * @param string $content The CSS content to update relative urls for.
+ * @param ConverterInterface $converter Relative path converter
+ * @param string $content The CSS content to update relative urls for
*
* @return string
*/
- protected function move(Converter $converter, $content)
+ protected function move(ConverterInterface $converter, $content)
{
/*
* Relative path references will usually be enclosed by url(). @import
(?P<quotes>["\'])?
# fetch path
- (?P<path>
-
- # do not fetch data uris or external sources
- (?!(
- \s?
- ["\']?
- (data|https?):
- ))
-
- .+?
- )
+ (?P<path>.+?)
# close path enclosure
(?(quotes)(?P=quotes))
(?P<quotes>["\'])
# fetch path
- (?P<path>
-
- # do not fetch data uris or external sources
- (?!(
- ["\']?
- (data|https?):
- ))
-
- .+?
- )
+ (?P<path>.+?)
# close path enclosure
(?P=quotes)
// determine if it's a url() or an @import match
$type = (strpos($match[0], '@import') === 0 ? 'import' : 'url');
- // attempting to interpret GET-params makes no sense, so let's discard them for awhile
- $params = strrchr($match['path'], '?');
- $url = $params ? substr($match['path'], 0, -strlen($params)) : $match['path'];
+ $url = $match['path'];
+ if ($this->canImportByPath($url)) {
+ // attempting to interpret GET-params makes no sense, so let's discard them for awhile
+ $params = strrchr($url, '?');
+ $url = $params ? substr($url, 0, -strlen($params)) : $url;
- // fix relative url
- $url = $converter->convert($url);
+ // fix relative url
+ $url = $converter->convert($url);
- // now that the path has been converted, re-apply GET-params
- $url .= $params;
+ // now that the path has been converted, re-apply GET-params
+ $url .= $params;
+ }
+
+ /*
+ * Urls with control characters above 0x7e should be quoted.
+ * According to Mozilla's parser, whitespace is only allowed at the
+ * end of unquoted urls.
+ * Urls with `)` (as could happen with data: uris) should also be
+ * quoted to avoid being confused for the url() closing parentheses.
+ * And urls with a # have also been reported to cause issues.
+ * Urls with quotes inside should also remain escaped.
+ *
+ * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation
+ * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378
+ * @see https://github.com/matthiasmullie/minify/issues/193
+ */
+ $url = trim($url);
+ if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) {
+ $url = $match['quotes'] . $url . $match['quotes'];
+ }
// build replacement
$search[] = $match[0];
- if ($type == 'url') {
+ if ($type === 'url') {
$replace[] = 'url('.$url.')';
- } elseif ($type == 'import') {
+ } elseif ($type === 'import') {
$replace[] = '@import "'.$url.'"';
}
}
// replace urls
- $content = str_replace($search, $replace, $content);
-
- return $content;
+ return str_replace($search, $replace, $content);
}
/**
* Shorthand hex color codes.
* #FF0000 -> #F00.
*
- * @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 shortenHex($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(?=[; }])/i', '#$1$2$3', $content);
// we can shorten some even more by replacing them with their color name
$colors = array(
'#F5DEB3' => 'wheat',
);
- return str_ireplace(array_keys($colors), $colors, $content);
+ return preg_replace_callback(
+ '/(?<=[: ])('.implode(array_keys($colors), '|').')(?=[; }])/i',
+ function ($match) use ($colors) {
+ return $colors[strtoupper($match[0])];
+ },
+ $content
+ );
}
/**
* Shorten CSS font weights.
*
- * @param string $content The CSS content to shorten the font weights for.
+ * @param string $content The CSS content to shorten the font weights for
*
* @return string
*/
);
$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);
/**
* Shorthand 0 values to plain 0, instead of e.g. -0em.
*
- * @param string $content The CSS content to shorten the zero values for.
+ * @param string $content The CSS content to shorten the zero values for
*
* @return string
*/
// looped because there may be multiple 0s inside 1 group of parentheses
do {
$previous = $content;
- $content = preg_replace('/\(([^\(\)]+)\s+[\+\-]\s+0(\s+[^\(\)]+)?\)/', '(\\1\\2)', $content);
- } while ( $content !== $previous );
+ $content = preg_replace('/\(([^\(\)]+) [\+\-] 0( [^\(\)]+)?\)/', '(\\1\\2)', $content);
+ } while ($content !== $previous);
// strip all `0 +` occurrences: calc(0 + 10%) -> calc(10%)
- $content = preg_replace('/\(\s*0\s+\+\s+([^\(\)]+)\)/', '(\\1)', $content);
+ $content = preg_replace('/\(0 \+ ([^\(\)]+)\)/', '(\\1)', $content);
// strip all `0 -` occurrences: calc(0 - 10%) -> calc(-10%)
- $content = preg_replace('/\(\s*0\s+\-\s+([^\(\)]+)\)/', '(-\\1)', $content);
+ $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);
+ $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
+
return $content;
}
/**
- * Strip comments from source code.
+ * Strip empty tags from source code.
*
* @param string $content
*
*/
protected function stripEmptyTags($content)
{
- return preg_replace('/(^|\})[^\{\}]+\{\s*\}/', '\\1', $content);
+ return preg_replace('/(^|\}|;)[^\{\};]+\{\s*\}/', '\\1', $content);
}
/**
/**
* Strip whitespace.
*
- * @param string $content The CSS content to strip the whitespace for.
+ * @param string $content The CSS content to strip the whitespace for
*
* @return string
*/
$content = preg_replace('/\s+([\]\)])/', '$1', $content);
$content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content);
- // whitespace around + and - can only be stripped in selectors, like
- // :nth-child(3+2n), not in things like calc(3px + 2px) or shorthands
- // like 3px -2px
- $content = preg_replace('/\s*([+-])\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);
// remove semicolon/whitespace followed by closing bracket
$content = str_replace(';}', '}', $content);
/**
* Check if file is small enough to be imported.
*
- * @param string $path The path to the file.
+ * @param string $path The path to the file
*
* @return bool
*/
{
return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024;
}
+
+ /**
+ * Check if file a file can be imported, going by the path.
+ *
+ * @param string $path
+ *
+ * @return bool
+ */
+ protected function canImportByPath($path)
+ {
+ return preg_match('/^(data:|https?:|\\/)/', $path) === 0;
+ }
+
+ /**
+ * Return a converter to update relative paths to be relative to the new
+ * destination.
+ *
+ * @param string $source
+ * @param string $target
+ *
+ * @return ConverterInterface
+ */
+ protected function getPathConverter($source, $target)
+ {
+ return new Converter($source, $target);
+ }
}
*
* @author Matthias Mullie <minify@mullie.eu>
* @author Tijs Verkoyen <minify@verkoyen.eu>
- * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved.
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
class JS extends Minify
* them. Some end of lines are not the end of a statement, like with these
* operators.
*
- * Note: Most operators are fine, we've only removed !, ++ and --.
- * There can't be a newline separating ! and whatever it is negating.
+ * Note: Most operators are fine, we've only removed ++ and --.
* ++ & -- have to be joined with the value they're in-/decrementing.
*
* Will be loaded from /data/js/operators_before.txt
* them. Some end of lines are not the end of a statement, like when
* continued by one of these operators on the newline.
*
- * Note: Most operators are fine, we've only removed ), ], ++ and --.
+ * Note: Most operators are fine, we've only removed ), ], ++, --, ! and ~.
+ * There can't be a newline separating ! or ~ and whatever it is negating.
* ++ & -- have to be joined with the value they're in-/decrementing.
* ) & ] are "special" in that they have lots or usecases. () for example
* is used for function calls, for grouping, in if () and for (), ...
*/
protected $operatorsAfter = array();
+ /**
+ * @var array
+ */
+ protected $nestedExtracted = array();
+
/**
* {@inheritdoc}
*/
* Minify the data.
* Perform JS optimizations.
*
- * @param string[optional] $path Path to write the data to.
+ * @param string[optional] $path Path to write the data to
*
- * @return string The minified data.
+ * @return string The minified data
*/
public function execute($path = null)
{
$minifier = $this;
$callback = function ($match) use ($minifier) {
$count = count($minifier->extracted);
- $placeholder = '/'.$count.'/';
- $minifier->extracted[$placeholder] = $match[0];
+ $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;
+ }
- return $placeholder;
+ return (isset($match['before']) ? $match['before'] : '').
+ $placeholder.
+ (isset($match['after']) ? $match['after'] : '');
};
- $pattern = '\/.*?(?<!\\\\)(\\\\\\\\)*+\/[gimy]*(?![0-9a-zA-Z\/])';
+ $pattern = '(?P<regex>\/.+?((?<!\\\\)\\\\\\\\)*\/[gimy]*)(?![0-9a-zA-Z\/])';
// 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)
- $after = '[,;\)\}]';
- $methods = '\.(exec|test|match|search|replace|split)\(';
- $this->registerPattern('/'.$pattern.'(?=\s*('.$after.'|'.$methods.'))/', $callback);
+ $keywords = array('do', 'in', 'new', 'else', 'throw', 'yield', 'delete', 'return', 'typeof');
+ $before = '(?P<before>[=:,;\}\(\{&\|!]|^|'.implode('|', $keywords).')';
+ $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',
+ 'global',
+ 'ignoreCase',
+ 'multiline',
+ 'source',
+ 'sticky',
+ 'unicode',
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#Methods_2
+ '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);
// 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, '/');
- $this->registerPattern('/'.$pattern.'\s*\n?(?=\s*('.implode('|', $operators).'))/', $callback);
+ $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;
}
/**
* Because it's sometimes hard to tell if a newline is part of a statement
* that should be terminated or not, we'll just leave some of them alone.
*
- * @param string $content The content to strip the whitespace for.
+ * @param string $content The content to strip the whitespace for
*
* @return string
*/
/*
* Next, we'll be removing all semicolons where ASI kicks in.
* for-loops however, can have an empty body (ending in only a
- * semicolon), like: `for(i=1;i<3;i++);`
+ * 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...
* 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', '\\1;;\\2', $content);
+ $content = preg_replace('/(for\([^;\{]+\s+in\s+[^;\{]+\));(\}|$)/s', '\\1;;\\2', $content);
/*
* We also can't strip empty else-statements. Even though they're
protected function getOperatorsForRegex(array $operators, $delimiter = '/')
{
// escape operators for use in regex
- $delimiter = array_fill(0, count($operators), $delimiter);
- $escaped = array_map('preg_quote', $operators, $delimiter);
+ $delimiters = array_fill(0, count($operators), $delimiter);
+ $escaped = array_map('preg_quote', $operators, $delimiters);
$operators = array_combine($operators, $escaped);
$operators['.'] = '(?<![0-9]\s)\.';
// don't confuse = with other assignment shortcuts (e.g. +=)
- $chars = preg_quote('+-*\=<>%&|');
+ $chars = preg_quote('+-*\=<>%&|', $delimiter);
$operators['='] = '(?<!['.$chars.'])\=';
return $operators;
*/
protected function shortenBools($content)
{
- $content = preg_replace('/\btrue\b(?!:)/', '!0', $content);
- $content = preg_replace('/\bfalse\b(?!:)/', '!1', $content);
+ /*
+ * 'true' or 'false' could be used as property names (which may be
+ * followed by whitespace) - we must not replace those!
+ * Since PHP doesn't allow variable-length (to account for the
+ * whitespace) lookbehind assertions, I need to capture the leading
+ * character and check if it's a `.`
+ */
+ $callback = function ($match) {
+ if (trim($match[1]) === '.') {
+ return $match[0];
+ }
+
+ return $match[1].($match[2] === 'true' ? '!0' : '!1');
+ };
+ $content = preg_replace_callback('/(^|.\s*)\b(true|false)\b(?!:)/', $callback, $content);
- // for(;;) is exactly the same as while(true)
+ // for(;;) is exactly the same as while(true), but shorter :)
$content = preg_replace('/\bwhile\(!0\){/', 'for(;;){', $content);
// now make sure we didn't turn any do ... while(true) into do ... for(;;)
* 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.
+ * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved
* @license MIT License
*/
abstract class Minify
/**
* Add a file or straight-up code to be minified.
*
- * @param string $data
+ * @param string|string[] $data
+ *
+ * @return static
*/
public function add($data /* $data = null, ... */)
{
// this method can be overloaded
foreach ($args as $data) {
+ if (is_array($data)) {
+ call_user_func_array(array($this, 'add'), $data);
+ continue;
+ }
+
// redefine var
$data = (string) $data;
$value = $this->load($data);
$key = ($data != $value) ? $data : count($this->data);
+ // replace CR linefeeds etc.
+ // @see https://github.com/matthiasmullie/minify/pull/139
+ $value = str_replace(array("\r\n", "\r"), "\n", $value);
+
// store data
$this->data[$key] = $value;
}
+
+ return $this;
}
/**
* Minify the data & (optionally) saves it to a file.
*
- * @param string[optional] $path Path to write the data to.
+ * @param string[optional] $path Path to write the data to
*
- * @return string The minified data.
+ * @return string The minified data
*/
public function minify($path = null)
{
/**
* Minify & gzip the data & (optionally) saves it to a file.
*
- * @param string[optional] $path Path to write the data to.
- * @param int[optional] $level Compression level, from 0 to 9.
+ * @param string[optional] $path Path to write the data to
+ * @param int[optional] $level Compression level, from 0 to 9
*
- * @return string The minified & gzipped data.
+ * @return string The minified & gzipped data
*/
public function gzip($path = null, $level = 9)
{
/**
* Minify the data & write it to a CacheItemInterface object.
*
- * @param CacheItemInterface $item Cache item to write the data to.
+ * @param CacheItemInterface $item Cache item to write the data to
*
- * @return CacheItemInterface Cache item with the minifier data.
+ * @return CacheItemInterface Cache item with the minifier data
*/
public function cache(CacheItemInterface $item)
{
/**
* Minify the data.
*
- * @param string[optional] $path Path to write the data to.
+ * @param string[optional] $path Path to write the data to
*
- * @return string The minified data.
+ * @return string The minified data
*/
abstract public function execute($path = null);
/**
* Load data.
*
- * @param string $data Either a path to a file or the content itself.
+ * @param string $data Either a path to a file or the content itself
*
* @return string
*/
/**
* Save to file.
*
- * @param string $content The minified data.
- * @param string $path The path to save the minified data to.
+ * @param string $content The minified data
+ * @param string $path The path to save the minified data to
*
* @throws IOException
*/
/**
* Register a pattern to execute against the source content.
*
- * @param string $pattern PCRE pattern.
- * @param string|callable $replacement Replacement value for matched pattern.
+ * @param string $pattern PCRE pattern
+ * @param string|callable $replacement Replacement value for matched pattern
*/
protected function registerPattern($pattern, $replacement = '')
{
* The only way to accurately replace these pieces is to traverse the JS one
* character at a time and try to find whatever starts first.
*
- * @param string $content The content to replace patterns in.
+ * @param string $content The content to replace patterns in
*
- * @return string The (manipulated) content.
+ * @return string The (manipulated) content
*/
protected function replace($content)
{
* This function will be called plenty of times, where $content will always
* move up 1 character.
*
- * @param string $pattern Pattern to match.
- * @param string|callable $replacement Replacement value.
- * @param string $content Content to match pattern against.
+ * @param string $pattern Pattern to match
+ * @param string|callable $replacement Replacement value
+ * @param string $content Content to match pattern against
*
* @return string
*/
* via restoreStrings().
*
* @param string[optional] $chars
+ * @param string[optional] $placeholderPrefix
*/
- protected function extractStrings($chars = '\'"')
+ protected function extractStrings($chars = '\'"', $placeholderPrefix = '')
{
// PHP only supports $this inside anonymous functions since 5.4
$minifier = $this;
- $callback = function ($match) use ($minifier) {
+ $callback = function ($match) use ($minifier, $placeholderPrefix) {
// check the second index here, because the first always contains a quote
if ($match[2] === '') {
/*
}
$count = count($minifier->extracted);
- $placeholder = $match[1].$count.$match[1];
+ $placeholder = $match[1].$placeholderPrefix.$count.$match[1];
$minifier->extracted[$placeholder] = $match[1].$match[2].$match[1];
return $placeholder;
/**
* Attempts to open file specified by $path for writing.
*
- * @param string $path The path to the file.
+ * @param string $path The path to the file
*
- * @return resource Specifier for the target file.
+ * @return resource Specifier for the target file
*
* @throws IOException
*/
/**
* 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 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)
*
* @throws IOException
*/
* Please report bugs on https://github.com/matthiasmullie/path-converter/issues
*
* @author Matthias Mullie <pathconverter@mullie.eu>
- * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved.
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
* @license MIT License
*/
-class Converter
+class Converter implements ConverterInterface
{
/**
* @var string
* ../../core/layout/images/img.gif relative to
* /home/forkcms/frontend/cache/minified_css
*
- * @param string $path The relative path that needs to be converted.
+ * @param string $path The relative path that needs to be converted
*
- * @return string The new relative path.
+ * @return string The new relative path
*/
public function convert($path)
{
*
* @return string
*/
- public function dirname($path)
+ protected function dirname($path)
{
if (is_file($path)) {
return dirname($path);
--- /dev/null
+<?php
+
+namespace MatthiasMullie\PathConverter;
+
+/**
+ * Convert file paths.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
+ *
+ * @author Matthias Mullie <pathconverter@mullie.eu>
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+interface ConverterInterface
+{
+ /**
+ * Convert file paths.
+ *
+ * @param string $path The path to be converted
+ *
+ * @return string The new path
+ */
+ public function convert($path);
+}
--- /dev/null
+<?php
+
+namespace MatthiasMullie\PathConverter;
+
+/**
+ * Don't convert paths.
+ *
+ * Please report bugs on https://github.com/matthiasmullie/path-converter/issues
+ *
+ * @author Matthias Mullie <pathconverter@mullie.eu>
+ * @copyright Copyright (c) 2015, Matthias Mullie. All rights reserved
+ * @license MIT License
+ */
+class NoConverter implements ConverterInterface
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function convert($path)
+ {
+ return $path;
+ }
+}
Description of MatthiasMullie\Minify import into Moodle
-1) Download https://github.com/matthiasmullie/minify/archive/1.3.37.zip
+1) Download https://github.com/matthiasmullie/minify/archive/1.3.51.zip and unzip
-mv minify-1.3.37/src /path/to/moodle/lib/minify/matthiasmullie-minify/
-mv minify-1.3.37/data /path/to/moodle/lib/lib/minify/matthiasmullie-minify/
+mv minify-1.3.51/src /path/to/moodle/lib/minify/matthiasmullie-minify/
+mv minify-1.3.51/data /path/to/moodle/lib/minify/matthiasmullie-minify/
-2) Download https://github.com/matthiasmullie/path-converter/archive/1.0.8.zip
+2) Download https://github.com/matthiasmullie/path-converter/archive/1.1.0.zip and unzip
-mv path-converter-1.0.8/src/ /path/to/moodle/lib/minify/matthiasmullie-pathconverter/
+mv path-converter-1.1.0/src/ /path/to/moodle/lib/minify/matthiasmullie-pathconverter/
<location>minify/matthiasmullie-minify</location>
<name>MatthiasMullie\Minify</name>
<license>MIT</license>
- <version>1.3.37</version>
+ <version>1.3.51</version>
<licenseversion></licenseversion>
</library>
<library>
<location>minify/matthiasmullie-pathconverter</location>
<name>MatthiasMullie\PathConverter</name>
<license>MIT</license>
- <version>1.0.8</version>
+ <version>1.1.0</version>
<licenseversion></licenseversion>
</library>
<library>