Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400] [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]
1 <?php 2 /** 3 * CSS Minifier 4 * 5 * Please report bugs on https://github.com/matthiasmullie/minify/issues 6 * 7 * @author Matthias Mullie <minify@mullie.eu> 8 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved 9 * @license MIT License 10 */ 11 12 namespace MatthiasMullie\Minify; 13 14 use MatthiasMullie\Minify\Exceptions\FileImportException; 15 use MatthiasMullie\PathConverter\ConverterInterface; 16 use MatthiasMullie\PathConverter\Converter; 17 18 /** 19 * CSS minifier 20 * 21 * Please report bugs on https://github.com/matthiasmullie/minify/issues 22 * 23 * @package Minify 24 * @author Matthias Mullie <minify@mullie.eu> 25 * @author Tijs Verkoyen <minify@verkoyen.eu> 26 * @copyright Copyright (c) 2012, Matthias Mullie. All rights reserved 27 * @license MIT License 28 */ 29 class CSS extends Minify 30 { 31 /** 32 * @var int maximum inport size in kB 33 */ 34 protected $maxImportSize = 5; 35 36 /** 37 * @var string[] valid import extensions 38 */ 39 protected $importExtensions = array( 40 'gif' => 'data:image/gif', 41 'png' => 'data:image/png', 42 'jpe' => 'data:image/jpeg', 43 'jpg' => 'data:image/jpeg', 44 'jpeg' => 'data:image/jpeg', 45 'svg' => 'data:image/svg+xml', 46 'woff' => 'data:application/x-font-woff', 47 'tif' => 'image/tiff', 48 'tiff' => 'image/tiff', 49 'xbm' => 'image/x-xbitmap', 50 ); 51 52 /** 53 * Set the maximum size if files to be imported. 54 * 55 * Files larger than this size (in kB) will not be imported into the CSS. 56 * Importing files into the CSS as data-uri will save you some connections, 57 * but we should only import relatively small decorative images so that our 58 * CSS file doesn't get too bulky. 59 * 60 * @param int $size Size in kB 61 */ 62 public function setMaxImportSize($size) 63 { 64 $this->maxImportSize = $size; 65 } 66 67 /** 68 * Set the type of extensions to be imported into the CSS (to save network 69 * connections). 70 * Keys of the array should be the file extensions & respective values 71 * should be the data type. 72 * 73 * @param string[] $extensions Array of file extensions 74 */ 75 public function setImportExtensions(array $extensions) 76 { 77 $this->importExtensions = $extensions; 78 } 79 80 /** 81 * Move any import statements to the top. 82 * 83 * @param string $content Nearly finished CSS content 84 * 85 * @return string 86 */ 87 protected function moveImportsToTop($content) 88 { 89 if (preg_match_all('/(;?)(@import (?<url>url\()?(?P<quotes>["\']?).+?(?P=quotes)(?(url)\)));?/', $content, $matches)) { 90 // remove from content 91 foreach ($matches[0] as $import) { 92 $content = str_replace($import, '', $content); 93 } 94 95 // add to top 96 $content = implode(';', $matches[2]).';'.trim($content, ';'); 97 } 98 99 return $content; 100 } 101 102 /** 103 * Combine CSS from import statements. 104 * 105 * @import's will be loaded and their content merged into the original file, 106 * to save HTTP requests. 107 * 108 * @param string $source The file to combine imports for 109 * @param string $content The CSS content to combine imports for 110 * @param string[] $parents Parent paths, for circular reference checks 111 * 112 * @return string 113 * 114 * @throws FileImportException 115 */ 116 protected function combineImports($source, $content, $parents) 117 { 118 $importRegexes = array( 119 // @import url(xxx) 120 '/ 121 # import statement 122 @import 123 124 # whitespace 125 \s+ 126 127 # open url() 128 url\( 129 130 # (optional) open path enclosure 131 (?P<quotes>["\']?) 132 133 # fetch path 134 (?P<path>.+?) 135 136 # (optional) close path enclosure 137 (?P=quotes) 138 139 # close url() 140 \) 141 142 # (optional) trailing whitespace 143 \s* 144 145 # (optional) media statement(s) 146 (?P<media>[^;]*) 147 148 # (optional) trailing whitespace 149 \s* 150 151 # (optional) closing semi-colon 152 ;? 153 154 /ix', 155 156 // @import 'xxx' 157 '/ 158 159 # import statement 160 @import 161 162 # whitespace 163 \s+ 164 165 # open path enclosure 166 (?P<quotes>["\']) 167 168 # fetch path 169 (?P<path>.+?) 170 171 # close path enclosure 172 (?P=quotes) 173 174 # (optional) trailing whitespace 175 \s* 176 177 # (optional) media statement(s) 178 (?P<media>[^;]*) 179 180 # (optional) trailing whitespace 181 \s* 182 183 # (optional) closing semi-colon 184 ;? 185 186 /ix', 187 ); 188 189 // find all relative imports in css 190 $matches = array(); 191 foreach ($importRegexes as $importRegex) { 192 if (preg_match_all($importRegex, $content, $regexMatches, PREG_SET_ORDER)) { 193 $matches = array_merge($matches, $regexMatches); 194 } 195 } 196 197 $search = array(); 198 $replace = array(); 199 200 // loop the matches 201 foreach ($matches as $match) { 202 // get the path for the file that will be imported 203 $importPath = dirname($source).'/'.$match['path']; 204 205 // only replace the import with the content if we can grab the 206 // content of the file 207 if (!$this->canImportByPath($match['path']) || !$this->canImportFile($importPath)) { 208 continue; 209 } 210 211 // check if current file was not imported previously in the same 212 // import chain. 213 if (in_array($importPath, $parents)) { 214 throw new FileImportException('Failed to import file "'.$importPath.'": circular reference detected.'); 215 } 216 217 // grab referenced file & minify it (which may include importing 218 // yet other @import statements recursively) 219 $minifier = new self($importPath); 220 $minifier->setMaxImportSize($this->maxImportSize); 221 $minifier->setImportExtensions($this->importExtensions); 222 $importContent = $minifier->execute($source, $parents); 223 224 // check if this is only valid for certain media 225 if (!empty($match['media'])) { 226 $importContent = '@media '.$match['media'].'{'.$importContent.'}'; 227 } 228 229 // add to replacement array 230 $search[] = $match[0]; 231 $replace[] = $importContent; 232 } 233 234 // replace the import statements 235 return str_replace($search, $replace, $content); 236 } 237 238 /** 239 * Import files into the CSS, base64-ized. 240 * 241 * @url(image.jpg) images will be loaded and their content merged into the 242 * original file, to save HTTP requests. 243 * 244 * @param string $source The file to import files for 245 * @param string $content The CSS content to import files for 246 * 247 * @return string 248 */ 249 protected function importFiles($source, $content) 250 { 251 $regex = '/url\((["\']?)(.+?)\\1\)/i'; 252 if ($this->importExtensions && preg_match_all($regex, $content, $matches, PREG_SET_ORDER)) { 253 $search = array(); 254 $replace = array(); 255 256 // loop the matches 257 foreach ($matches as $match) { 258 $extension = substr(strrchr($match[2], '.'), 1); 259 if ($extension && !array_key_exists($extension, $this->importExtensions)) { 260 continue; 261 } 262 263 // get the path for the file that will be imported 264 $path = $match[2]; 265 $path = dirname($source).'/'.$path; 266 267 // only replace the import with the content if we're able to get 268 // the content of the file, and it's relatively small 269 if ($this->canImportFile($path) && $this->canImportBySize($path)) { 270 // grab content && base64-ize 271 $importContent = $this->load($path); 272 $importContent = base64_encode($importContent); 273 274 // build replacement 275 $search[] = $match[0]; 276 $replace[] = 'url('.$this->importExtensions[$extension].';base64,'.$importContent.')'; 277 } 278 } 279 280 // replace the import statements 281 $content = str_replace($search, $replace, $content); 282 } 283 284 return $content; 285 } 286 287 /** 288 * Minify the data. 289 * Perform CSS optimizations. 290 * 291 * @param string[optional] $path Path to write the data to 292 * @param string[] $parents Parent paths, for circular reference checks 293 * 294 * @return string The minified data 295 */ 296 public function execute($path = null, $parents = array()) 297 { 298 $content = ''; 299 300 // loop CSS data (raw data and files) 301 foreach ($this->data as $source => $css) { 302 /* 303 * Let's first take out strings & comments, since we can't just 304 * remove whitespace anywhere. If whitespace occurs inside a string, 305 * we should leave it alone. E.g.: 306 * p { content: "a test" } 307 */ 308 $this->extractStrings(); 309 $this->stripComments(); 310 $this->extractMath(); 311 $this->extractCustomProperties(); 312 $css = $this->replace($css); 313 314 $css = $this->stripWhitespace($css); 315 $css = $this->shortenColors($css); 316 $css = $this->shortenZeroes($css); 317 $css = $this->shortenFontWeights($css); 318 $css = $this->stripEmptyTags($css); 319 320 // restore the string we've extracted earlier 321 $css = $this->restoreExtractedData($css); 322 323 $source = is_int($source) ? '' : $source; 324 $parents = $source ? array_merge($parents, array($source)) : $parents; 325 $css = $this->combineImports($source, $css, $parents); 326 $css = $this->importFiles($source, $css); 327 328 /* 329 * If we'll save to a new path, we'll have to fix the relative paths 330 * to be relative no longer to the source file, but to the new path. 331 * If we don't write to a file, fall back to same path so no 332 * conversion happens (because we still want it to go through most 333 * of the move code, which also addresses url() & @import syntax...) 334 */ 335 $converter = $this->getPathConverter($source, $path ?: $source); 336 $css = $this->move($converter, $css); 337 338 // combine css 339 $content .= $css; 340 } 341 342 $content = $this->moveImportsToTop($content); 343 344 return $content; 345 } 346 347 /** 348 * Moving a css file should update all relative urls. 349 * Relative references (e.g. ../images/image.gif) in a certain css file, 350 * will have to be updated when a file is being saved at another location 351 * (e.g. ../../images/image.gif, if the new CSS file is 1 folder deeper). 352 * 353 * @param ConverterInterface $converter Relative path converter 354 * @param string $content The CSS content to update relative urls for 355 * 356 * @return string 357 */ 358 protected function move(ConverterInterface $converter, $content) 359 { 360 /* 361 * Relative path references will usually be enclosed by url(). @import 362 * is an exception, where url() is not necessary around the path (but is 363 * allowed). 364 * This *could* be 1 regular expression, where both regular expressions 365 * in this array are on different sides of a |. But we're using named 366 * patterns in both regexes, the same name on both regexes. This is only 367 * possible with a (?J) modifier, but that only works after a fairly 368 * recent PCRE version. That's why I'm doing 2 separate regular 369 * expressions & combining the matches after executing of both. 370 */ 371 $relativeRegexes = array( 372 // url(xxx) 373 '/ 374 # open url() 375 url\( 376 377 \s* 378 379 # open path enclosure 380 (?P<quotes>["\'])? 381 382 # fetch path 383 (?P<path>.+?) 384 385 # close path enclosure 386 (?(quotes)(?P=quotes)) 387 388 \s* 389 390 # close url() 391 \) 392 393 /ix', 394 395 // @import "xxx" 396 '/ 397 # import statement 398 @import 399 400 # whitespace 401 \s+ 402 403 # we don\'t have to check for @import url(), because the 404 # condition above will already catch these 405 406 # open path enclosure 407 (?P<quotes>["\']) 408 409 # fetch path 410 (?P<path>.+?) 411 412 # close path enclosure 413 (?P=quotes) 414 415 /ix', 416 ); 417 418 // find all relative urls in css 419 $matches = array(); 420 foreach ($relativeRegexes as $relativeRegex) { 421 if (preg_match_all($relativeRegex, $content, $regexMatches, PREG_SET_ORDER)) { 422 $matches = array_merge($matches, $regexMatches); 423 } 424 } 425 426 $search = array(); 427 $replace = array(); 428 429 // loop all urls 430 foreach ($matches as $match) { 431 // determine if it's a url() or an @import match 432 $type = (strpos($match[0], '@import') === 0 ? 'import' : 'url'); 433 434 $url = $match['path']; 435 if ($this->canImportByPath($url)) { 436 // attempting to interpret GET-params makes no sense, so let's discard them for awhile 437 $params = strrchr($url, '?'); 438 $url = $params ? substr($url, 0, -strlen($params)) : $url; 439 440 // fix relative url 441 $url = $converter->convert($url); 442 443 // now that the path has been converted, re-apply GET-params 444 $url .= $params; 445 } 446 447 /* 448 * Urls with control characters above 0x7e should be quoted. 449 * According to Mozilla's parser, whitespace is only allowed at the 450 * end of unquoted urls. 451 * Urls with `)` (as could happen with data: uris) should also be 452 * quoted to avoid being confused for the url() closing parentheses. 453 * And urls with a # have also been reported to cause issues. 454 * Urls with quotes inside should also remain escaped. 455 * 456 * @see https://developer.mozilla.org/nl/docs/Web/CSS/url#The_url()_functional_notation 457 * @see https://hg.mozilla.org/mozilla-central/rev/14abca4e7378 458 * @see https://github.com/matthiasmullie/minify/issues/193 459 */ 460 $url = trim($url); 461 if (preg_match('/[\s\)\'"#\x{7f}-\x{9f}]/u', $url)) { 462 $url = $match['quotes'] . $url . $match['quotes']; 463 } 464 465 // build replacement 466 $search[] = $match[0]; 467 if ($type === 'url') { 468 $replace[] = 'url('.$url.')'; 469 } elseif ($type === 'import') { 470 $replace[] = '@import "'.$url.'"'; 471 } 472 } 473 474 // replace urls 475 return str_replace($search, $replace, $content); 476 } 477 478 /** 479 * Shorthand hex color codes. 480 * #FF0000 -> #F00. 481 * 482 * @param string $content The CSS content to shorten the hex color codes for 483 * 484 * @return string 485 */ 486 protected function shortenColors($content) 487 { 488 $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); 489 490 // remove alpha channel if it's pointless... 491 $content = preg_replace('/(?<=[: ])#([0-9a-z]{6})ff?(?=[; }])/i', '#$1', $content); 492 $content = preg_replace('/(?<=[: ])#([0-9a-z]{3})f?(?=[; }])/i', '#$1', $content); 493 494 $colors = array( 495 // we can shorten some even more by replacing them with their color name 496 '#F0FFFF' => 'azure', 497 '#F5F5DC' => 'beige', 498 '#A52A2A' => 'brown', 499 '#FF7F50' => 'coral', 500 '#FFD700' => 'gold', 501 '#808080' => 'gray', 502 '#008000' => 'green', 503 '#4B0082' => 'indigo', 504 '#FFFFF0' => 'ivory', 505 '#F0E68C' => 'khaki', 506 '#FAF0E6' => 'linen', 507 '#800000' => 'maroon', 508 '#000080' => 'navy', 509 '#808000' => 'olive', 510 '#CD853F' => 'peru', 511 '#FFC0CB' => 'pink', 512 '#DDA0DD' => 'plum', 513 '#800080' => 'purple', 514 '#F00' => 'red', 515 '#FA8072' => 'salmon', 516 '#A0522D' => 'sienna', 517 '#C0C0C0' => 'silver', 518 '#FFFAFA' => 'snow', 519 '#D2B48C' => 'tan', 520 '#FF6347' => 'tomato', 521 '#EE82EE' => 'violet', 522 '#F5DEB3' => 'wheat', 523 // or the other way around 524 'WHITE' => '#fff', 525 'BLACK' => '#000', 526 ); 527 528 return preg_replace_callback( 529 '/(?<=[: ])('.implode('|', array_keys($colors)).')(?=[; }])/i', 530 function ($match) use ($colors) { 531 return $colors[strtoupper($match[0])]; 532 }, 533 $content 534 ); 535 } 536 537 /** 538 * Shorten CSS font weights. 539 * 540 * @param string $content The CSS content to shorten the font weights for 541 * 542 * @return string 543 */ 544 protected function shortenFontWeights($content) 545 { 546 $weights = array( 547 'normal' => 400, 548 'bold' => 700, 549 ); 550 551 $callback = function ($match) use ($weights) { 552 return $match[1].$weights[$match[2]]; 553 }; 554 555 return preg_replace_callback('/(font-weight\s*:\s*)('.implode('|', array_keys($weights)).')(?=[;}])/', $callback, $content); 556 } 557 558 /** 559 * Shorthand 0 values to plain 0, instead of e.g. -0em. 560 * 561 * @param string $content The CSS content to shorten the zero values for 562 * 563 * @return string 564 */ 565 protected function shortenZeroes($content) 566 { 567 // we don't want to strip units in `calc()` expressions: 568 // `5px - 0px` is valid, but `5px - 0` is not 569 // `10px * 0` is valid (equates to 0), and so is `10 * 0px`, but 570 // `10 * 0` is invalid 571 // we've extracted calcs earlier, so we don't need to worry about this 572 573 // reusable bits of code throughout these regexes: 574 // before & after are used to make sure we don't match lose unintended 575 // 0-like values (e.g. in #000, or in http://url/1.0) 576 // units can be stripped from 0 values, or used to recognize non 0 577 // values (where wa may be able to strip a .0 suffix) 578 $before = '(?<=[:(, ])'; 579 $after = '(?=[ ,);}])'; 580 $units = '(em|ex|%|px|cm|mm|in|pt|pc|ch|rem|vh|vw|vmin|vmax|vm)'; 581 582 // strip units after zeroes (0px -> 0) 583 // NOTE: it should be safe to remove all units for a 0 value, but in 584 // practice, Webkit (especially Safari) seems to stumble over at least 585 // 0%, potentially other units as well. Only stripping 'px' for now. 586 // @see https://github.com/matthiasmullie/minify/issues/60 587 $content = preg_replace('/'.$before.'(-?0*(\.0+)?)(?<=0)px'.$after.'/', '\\1', $content); 588 589 // strip 0-digits (.0 -> 0) 590 $content = preg_replace('/'.$before.'\.0+'.$units.'?'.$after.'/', '0\\1', $content); 591 // strip trailing 0: 50.10 -> 50.1, 50.10px -> 50.1px 592 $content = preg_replace('/'.$before.'(-?[0-9]+\.[0-9]+)0+'.$units.'?'.$after.'/', '\\1\\2', $content); 593 // strip trailing 0: 50.00 -> 50, 50.00px -> 50px 594 $content = preg_replace('/'.$before.'(-?[0-9]+)\.0+'.$units.'?'.$after.'/', '\\1\\2', $content); 595 // strip leading 0: 0.1 -> .1, 01.1 -> 1.1 596 $content = preg_replace('/'.$before.'(-?)0+([0-9]*\.[0-9]+)'.$units.'?'.$after.'/', '\\1\\2\\3', $content); 597 598 // strip negative zeroes (-0 -> 0) & truncate zeroes (00 -> 0) 599 $content = preg_replace('/'.$before.'-?0+'.$units.'?'.$after.'/', '0\\1', $content); 600 601 // IE doesn't seem to understand a unitless flex-basis value (correct - 602 // it goes against the spec), so let's add it in again (make it `%`, 603 // which is only 1 char: 0%, 0px, 0 anything, it's all just the same) 604 // @see https://developer.mozilla.org/nl/docs/Web/CSS/flex 605 $content = preg_replace('/flex:([0-9]+\s[0-9]+\s)0([;\}])/', 'flex:$1}0%$2}', $content); 606 $content = preg_replace('/flex-basis:0([;\}])/', 'flex-basis:0%$1}', $content); 607 608 return $content; 609 } 610 611 /** 612 * Strip empty tags from source code. 613 * 614 * @param string $content 615 * 616 * @return string 617 */ 618 protected function stripEmptyTags($content) 619 { 620 $content = preg_replace('/(?<=^)[^\{\};]+\{\s*\}/', '', $content); 621 $content = preg_replace('/(?<=(\}|;))[^\{\};]+\{\s*\}/', '', $content); 622 623 return $content; 624 } 625 626 /** 627 * Strip comments from source code. 628 */ 629 protected function stripComments() 630 { 631 // PHP only supports $this inside anonymous functions since 5.4 632 $minifier = $this; 633 $callback = function ($match) use ($minifier) { 634 $count = count($minifier->extracted); 635 $placeholder = '/*'.$count.'*/'; 636 $minifier->extracted[$placeholder] = $match[0]; 637 638 return $placeholder; 639 }; 640 // Moodle-specific change MDL-68191 starts. 641 /* This was the old code: 642 $this->registerPattern('/\n?\/\*(!|.*?@license|.*?@preserve).*?\*\/\n?/s', $callback); 643 */ 644 // This is the new, more accurate and faster regex. 645 $this->registerPattern('/ 646 # optional newline 647 \n? 648 649 # start comment 650 \/\* 651 652 # comment content 653 (?: 654 # either starts with an ! 655 ! 656 | 657 # or, after some number of characters which do not end the comment 658 (?:(?!\*\/).)*? 659 660 # there is either a @license or @preserve tag 661 @(?:license|preserve) 662 ) 663 664 # then match to the end of the comment 665 .*?\*\/\n? 666 667 /ixs', $callback); 668 // Moodle-specific change MDL-68191. 669 670 $this->registerPattern('/\/\*.*?\*\//s', ''); 671 } 672 673 /** 674 * Strip whitespace. 675 * 676 * @param string $content The CSS content to strip the whitespace for 677 * 678 * @return string 679 */ 680 protected function stripWhitespace($content) 681 { 682 // remove leading & trailing whitespace 683 $content = preg_replace('/^\s*/m', '', $content); 684 $content = preg_replace('/\s*$/m', '', $content); 685 686 // replace newlines with a single space 687 $content = preg_replace('/\s+/', ' ', $content); 688 689 // remove whitespace around meta characters 690 // inspired by stackoverflow.com/questions/15195750/minify-compress-css-with-regex 691 $content = preg_replace('/\s*([\*$~^|]?+=|[{};,>~]|!important\b)\s*/', '$1', $content); 692 $content = preg_replace('/([\[(:>\+])\s+/', '$1', $content); 693 $content = preg_replace('/\s+([\]\)>\+])/', '$1', $content); 694 $content = preg_replace('/\s+(:)(?![^\}]*\{)/', '$1', $content); 695 696 // whitespace around + and - can only be stripped inside some pseudo- 697 // classes, like `:nth-child(3+2n)` 698 // not in things like `calc(3px + 2px)`, shorthands like `3px -2px`, or 699 // selectors like `div.weird- p` 700 $pseudos = array('nth-child', 'nth-last-child', 'nth-last-of-type', 'nth-of-type'); 701 $content = preg_replace('/:('.implode('|', $pseudos).')\(\s*([+-]?)\s*(.+?)\s*([+-]?)\s*(.*?)\s*\)/', ':$1($2$3$4$5)', $content); 702 703 // remove semicolon/whitespace followed by closing bracket 704 $content = str_replace(';}', '}', $content); 705 706 return trim($content); 707 } 708 709 /** 710 * Replace all occurrences of functions that may contain math, where 711 * whitespace around operators needs to be preserved (e.g. calc, clamp) 712 */ 713 protected function extractMath() 714 { 715 $functions = array('calc', 'clamp', 'min', 'max'); 716 $pattern = '/\b('. implode('|', $functions) .')(\(.+?)(?=$|;|})/m'; 717 718 // PHP only supports $this inside anonymous functions since 5.4 719 $minifier = $this; 720 $callback = function ($match) use ($minifier, $pattern, &$callback) { 721 $function = $match[1]; 722 $length = strlen($match[2]); 723 $expr = ''; 724 $opened = 0; 725 726 // the regular expression for extracting math has 1 significant problem: 727 // it can't determine the correct closing parenthesis... 728 // instead, it'll match a larger portion of code to where it's certain that 729 // the calc() musts have ended, and we'll figure out which is the correct 730 // closing parenthesis here, by counting how many have opened 731 for ($i = 0; $i < $length; $i++) { 732 $char = $match[2][$i]; 733 $expr .= $char; 734 if ($char === '(') { 735 $opened++; 736 } elseif ($char === ')' && --$opened === 0) { 737 break; 738 } 739 } 740 741 // now that we've figured out where the calc() starts and ends, extract it 742 $count = count($minifier->extracted); 743 $placeholder = 'math('.$count.')'; 744 $minifier->extracted[$placeholder] = $function.'('.trim(substr($expr, 1, -1)).')'; 745 746 // and since we've captured more code than required, we may have some leftover 747 // calc() in here too - go recursive on the remaining but of code to go figure 748 // that out and extract what is needed 749 $rest = str_replace($function.$expr, '', $match[0]); 750 $rest = preg_replace_callback($pattern, $callback, $rest); 751 752 return $placeholder.$rest; 753 }; 754 755 $this->registerPattern($pattern, $callback); 756 } 757 758 /** 759 * Replace custom properties, whose values may be used in scenarios where 760 * we wouldn't want them to be minified (e.g. inside calc) 761 */ 762 protected function extractCustomProperties() 763 { 764 // PHP only supports $this inside anonymous functions since 5.4 765 $minifier = $this; 766 $this->registerPattern( 767 '/(?<=^|[;}])(--[^:;{}"\'\s]+)\s*:([^;{}]+)/m', 768 function ($match) use ($minifier) { 769 $placeholder = '--custom-'. count($minifier->extracted) . ':0'; 770 $minifier->extracted[$placeholder] = $match[1] .':'. trim($match[2]); 771 return $placeholder; 772 773 } 774 ); 775 } 776 777 /** 778 * Check if file is small enough to be imported. 779 * 780 * @param string $path The path to the file 781 * 782 * @return bool 783 */ 784 protected function canImportBySize($path) 785 { 786 return ($size = @filesize($path)) && $size <= $this->maxImportSize * 1024; 787 } 788 789 /** 790 * Check if file a file can be imported, going by the path. 791 * 792 * @param string $path 793 * 794 * @return bool 795 */ 796 protected function canImportByPath($path) 797 { 798 return preg_match('/^(data:|https?:|\\/)/', $path) === 0; 799 } 800 801 /** 802 * Return a converter to update relative paths to be relative to the new 803 * destination. 804 * 805 * @param string $source 806 * @param string $target 807 * 808 * @return ConverterInterface 809 */ 810 protected function getPathConverter($source, $target) 811 { 812 return new Converter($source, $target); 813 } 814 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body