Skip to content

Commit 86e4a4e

Browse files
Leave calc expressions alone when stripping units off of 0
Fixed #217
1 parent 2550f2d commit 86e4a4e

File tree

2 files changed

+67
-25
lines changed

2 files changed

+67
-25
lines changed

src/CSS.php

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,16 @@ protected function shortenFontWeights($content)
553553
*/
554554
protected function shortenZeroes($content)
555555
{
556+
// we don't want to strip units in `calc()` expressions:
557+
// `5px - 0px` is valid, but `5px - 0` is not
558+
// `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but
559+
// `10 * 0` is invalid
560+
// best to just leave `calc()`s alone, even if they could be optimized
561+
// (which is a whole other undertaking, where units & order of
562+
// operations all need to be considered...)
563+
$calcs = $this->findCalcs($content);
564+
$content = str_replace($calcs, array_keys($calcs), $content);
565+
556566
// reusable bits of code throughout these regexes:
557567
// before & after are used to make sure we don't match lose unintended
558568
// 0-like values (e.g. in #000, or in http://url/1.0)
@@ -581,30 +591,15 @@ protected function shortenZeroes($content)
581591
// strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0)
582592
$content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content);
583593

584-
// remove zeroes where they make no sense in calc: e.g. calc(100px - 0)
585-
// the 0 doesn't have any effect, and this isn't even valid without unit
586-
// strip all `+ 0` or `- 0` occurrences: calc(10% + 0) -> calc(10%)
587-
// looped because there may be multiple 0s inside 1 group of parentheses
588-
do {
589-
$previous = $content;
590-
$content = preg_replace('/\(([^\(\)]+) [\+\-] 0( [^\(\)]+)?\)/', '(\\1\\2)', $content);
591-
} while ($content !== $previous);
592-
// strip all `0 +` occurrences: calc(0 + 10%) -> calc(10%)
593-
$content = preg_replace('/\(0 \+ ([^\(\)]+)\)/', '(\\1)', $content);
594-
// strip all `0 -` occurrences: calc(0 - 10%) -> calc(-10%)
595-
$content = preg_replace('/\(0 \- ([^\(\)]+)\)/', '(-\\1)', $content);
596-
// I'm not going to attempt to optimize away `x * 0` instances:
597-
// it's dumb enough code already that it likely won't occur, and it's
598-
// too complex to do right (order of operations would have to be
599-
// respected etc)
600-
// what I cared about most here was fixing incorrectly truncated units
601-
602594
// IE doesn't seem to understand a unitless flex-basis value, so let's
603595
// add it in again (make it `%`, which is only 1 char: 0%, 0px, 0
604596
// anything, it's all just the same)
605597
$content = preg_replace('/flex:([^ ]+ [^ ]+ )0([;\}])/', 'flex:${1}0%${2}', $content);
606598
$content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%${1}', $content);
607599

600+
// restore `calc()` expressions
601+
$content = str_replace(array_keys($calcs), $calcs, $content);
602+
608603
return $content;
609604
}
610605

@@ -667,6 +662,39 @@ protected function stripWhitespace($content)
667662
return trim($content);
668663
}
669664

665+
/**
666+
* Find all `calc()` occurrences.
667+
*
668+
* @param string $content The CSS content to find `calc()`s in.
669+
*
670+
* @return string[]
671+
*/
672+
protected function findCalcs($content)
673+
{
674+
$results = array();
675+
preg_match_all('/calc(\(.+?)(?=$|;|calc\()/', $content, $matches, PREG_SET_ORDER);
676+
677+
foreach ($matches as $match) {
678+
$length = strlen($match[1]);
679+
$expr = '';
680+
$opened = 0;
681+
682+
for ($i = 0; $i < $length; $i++) {
683+
$char = $match[1][$i];
684+
$expr .= $char;
685+
if ($char === '(') {
686+
$opened++;
687+
} elseif ($char === ')' && --$opened === 0) {
688+
break;
689+
}
690+
}
691+
692+
$results['calc('.count($results).')'] = 'calc'.$expr;
693+
}
694+
695+
return $results;
696+
}
697+
670698
/**
671699
* Check if file is small enough to be imported.
672700
*

tests/css/CSSTest.php

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -451,31 +451,31 @@ public function dataProvider()
451451
// https://github.com/matthiasmullie/minify/issues/137
452452
$tests[] = array(
453453
'p{width: calc(35% - 0px);}',
454-
'p{width:calc(35%)}',
454+
'p{width:calc(35% - 0px)}',
455455
);
456456
$tests[] = array(
457457
'p{width: calc(0px + 35%);}',
458-
'p{width:calc(35%)}',
458+
'p{width:calc(0px + 35%)}',
459459
);
460460
$tests[] = array(
461461
'p{width: calc(0px - 35%);}',
462-
'p{width:calc(-35%)}',
462+
'p{width:calc(0px - 35%)}',
463463
);
464464
$tests[] = array(
465465
'p{width: calc(0px + 35% - 0px);}',
466-
'p{width:calc(35%)}',
466+
'p{width:calc(0px + 35% - 0px)}',
467467
);
468468
$tests[] = array(
469469
'p{width: calc(5% + 0px + 35% - 0px + 5%);}',
470-
'p{width:calc(5% + 35% + 5%)}',
470+
'p{width:calc(5% + 0px + 35% - 0px + 5%)}',
471471
);
472472
$tests[] = array(
473473
'p{width:calc(35% + (10% + 0px))}',
474-
'p{width:calc(35% + (10%))}',
474+
'p{width:calc(35% + (10% + 0px))}',
475475
);
476476
$tests[] = array(
477477
'p{width:calc(35% + (10% + 0px + 10%))}',
478-
'p{width:calc(35% + (10% + 10%))}',
478+
'p{width:calc(35% + (10% + 0px + 10%))}',
479479
);
480480

481481
// https://github.com/matthiasmullie/minify/issues/139
@@ -689,6 +689,20 @@ public function dataProvider()
689689
'.link-wrapper a:before{content:"Click for Download"}',
690690
);
691691

692+
// https://github.com/matthiasmullie/minify/issues/217
693+
$tests[] = array(
694+
'div { background:url("sprite.svg") calc(-192px * 1) calc(0px * 1) no-repeat; }',
695+
'div{background:url(sprite.svg) calc(-192px * 1) calc(0px * 1) no-repeat}',
696+
);
697+
$tests[] = array(
698+
'div { background:url("sprite.svg") calc((-192px - 60px) * 0) calc(0px * 1) no-repeat; }',
699+
'div{background:url(sprite.svg) calc((-192px - 60px) * 0) calc(0px * 1) no-repeat}',
700+
);
701+
$tests[] = array(
702+
'div{border:calc((-192px - 60px) * 0) calc(0px * 1) calc(5px + (2px + (0px * 1) + 5px) * 3) calc(0px - 0px) solid red}',
703+
'div{border:calc((-192px - 60px) * 0) calc(0px * 1) calc(5px + (2px + (0px * 1) + 5px) * 3) calc(0px - 0px) solid red}',
704+
);
705+
692706
return $tests;
693707
}
694708

0 commit comments

Comments
 (0)