示例#1
0
文件: csslib.php 项目: numbas/moodle
 /**
  * Processes incoming CSS optimising it and then returning it.
  *
  * @param string $css The raw CSS to optimise
  * @return string The optimised CSS
  */
 public function process($css)
 {
     global $CFG;
     $this->reset_stats();
     $this->timestart = microtime(true);
     $this->rawstrlen = strlen($css);
     // First up we need to remove all line breaks - this allows us to instantly
     // reduce our processing requirements and as we will process everything
     // into a new structure there's really nothing lost.
     $css = preg_replace('#\\r?\\n#', ' ', $css);
     // Next remove the comments... no need to them in an optimised world and
     // knowing they're all gone allows us to REALLY make our processing simpler
     $css = preg_replace('#/\\*(.*?)\\*/#m', '', $css, -1, $this->commentsincss);
     $medias = array('all' => new css_media());
     $imports = array();
     $charset = false;
     $currentprocess = self::PROCESSING_START;
     $currentrule = css_rule::init();
     $currentselector = css_selector::init();
     $inquotes = false;
     // ' or "
     $inbraces = false;
     // {
     $inbrackets = false;
     // [
     $inparenthesis = false;
     // (
     $currentmedia = $medias['all'];
     $currentatrule = null;
     $suspectatrule = false;
     $buffer = '';
     $char = null;
     // Next we are going to iterate over every single character in $css.
     // This is why we removed line breaks and comments!
     for ($i = 0; $i < $this->rawstrlen; $i++) {
         $lastchar = $char;
         $char = substr($css, $i, 1);
         if ($char == '@' && $buffer == '') {
             $suspectatrule = true;
         }
         switch ($currentprocess) {
             // Start processing an at rule e.g. @media, @page
             case self::PROCESSING_ATRULE:
                 switch ($char) {
                     case ';':
                         if (!$inbraces) {
                             $buffer .= $char;
                             if ($currentatrule == 'import') {
                                 $imports[] = $buffer;
                                 $currentprocess = self::PROCESSING_SELECTORS;
                             } else {
                                 if ($currentatrule == 'charset') {
                                     $charset = $buffer;
                                     $currentprocess = self::PROCESSING_SELECTORS;
                                 }
                             }
                         }
                         $buffer = '';
                         $currentatrule = false;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '{':
                         if ($currentatrule == 'media' && preg_match('#\\s*@media\\s*([a-zA-Z0-9]+(\\s*,\\s*[a-zA-Z0-9]+)*)#', $buffer, $matches)) {
                             $mediatypes = str_replace(' ', '', $matches[1]);
                             if (!array_key_exists($mediatypes, $medias)) {
                                 $medias[$mediatypes] = new css_media($mediatypes);
                             }
                             $currentmedia = $medias[$mediatypes];
                             $currentprocess = self::PROCESSING_SELECTORS;
                             $buffer = '';
                         }
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                 }
                 break;
                 // Start processing selectors
                 // Start processing selectors
             // Start processing selectors
             // Start processing selectors
             case self::PROCESSING_START:
             case self::PROCESSING_SELECTORS:
                 switch ($char) {
                     case '[':
                         $inbrackets++;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ']':
                         $inbrackets--;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ' ':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         if (!empty($buffer)) {
                             if ($suspectatrule && preg_match('#@(media|import|charset)\\s*#', $buffer, $matches)) {
                                 $currentatrule = $matches[1];
                                 $currentprocess = self::PROCESSING_ATRULE;
                                 $buffer .= $char;
                             } else {
                                 $currentselector->add($buffer);
                                 $buffer = '';
                             }
                         }
                         $suspectatrule = false;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '{':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         $currentselector->add($buffer);
                         $currentrule->add_selector($currentselector);
                         $currentselector = css_selector::init();
                         $currentprocess = self::PROCESSING_STYLES;
                         $buffer = '';
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '}':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         if ($currentatrule == 'media') {
                             $currentmedia = $medias['all'];
                             $currentatrule = false;
                             $buffer = '';
                         }
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ',':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         $currentselector->add($buffer);
                         $currentrule->add_selector($currentselector);
                         $currentselector = css_selector::init();
                         $buffer = '';
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                 }
                 break;
                 // Start processing styles
                 // Start processing styles
             // Start processing styles
             // Start processing styles
             case self::PROCESSING_STYLES:
                 if ($char == '"' || $char == "'") {
                     if ($inquotes === false) {
                         $inquotes = $char;
                     }
                     if ($inquotes === $char && $lastchar !== '\\') {
                         $inquotes = false;
                     }
                 }
                 if ($inquotes) {
                     $buffer .= $char;
                     continue 2;
                 }
                 switch ($char) {
                     case ';':
                         if ($inparenthesis) {
                             $buffer .= $char;
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         $currentrule->add_style($buffer);
                         $buffer = '';
                         $inquotes = false;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '}':
                         $currentrule->add_style($buffer);
                         $this->rawselectors += $currentrule->get_selector_count();
                         $currentmedia->add_rule($currentrule);
                         $currentrule = css_rule::init();
                         $currentprocess = self::PROCESSING_SELECTORS;
                         $this->rawrules++;
                         $buffer = '';
                         $inquotes = false;
                         $inparenthesis = false;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '(':
                         $inparenthesis = true;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ')':
                         $inparenthesis = false;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                 }
                 break;
         }
         $buffer .= $char;
     }
     $css = '';
     if (!empty($charset)) {
         $imports[] = $charset;
     }
     if (!empty($imports)) {
         $css .= implode("\n", $imports);
         $css .= "\n\n";
     }
     foreach ($medias as $media) {
         $media->organise_rules_by_selectors();
         $this->optimisedrules += $media->count_rules();
         $this->optimisedselectors += $media->count_selectors();
         if ($media->has_errors()) {
             $this->errors[] = $media->get_errors();
         }
         $css .= $media->out();
     }
     $this->optimisedstrlen = strlen($css);
     $this->timecomplete = microtime(true);
     return trim($css);
 }
示例#2
0
 /**
  * Processes incoming CSS optimising it and then returning it.
  *
  * @param string $css The raw CSS to optimise
  * @return string The optimised CSS
  */
 public function process($css)
 {
     global $CFG;
     // Easiest win there is
     $css = trim($css);
     $this->reset_stats();
     $this->timestart = microtime(true);
     $this->rawstrlen = strlen($css);
     // Don't try to process files with no content... it just doesn't make sense.
     // But we should produce an error for them, an empty CSS file will lead to a
     // useless request for those running theme designer mode.
     if ($this->rawstrlen === 0) {
         $this->errors[] = 'Skipping file as it has no content.';
         return '';
     }
     // First up we need to remove all line breaks - this allows us to instantly
     // reduce our processing requirements and as we will process everything
     // into a new structure there's really nothing lost.
     $css = preg_replace('#\\r?\\n#', ' ', $css);
     // Next remove the comments... no need to them in an optimised world and
     // knowing they're all gone allows us to REALLY make our processing simpler
     $css = preg_replace('#/\\*(.*?)\\*/#m', '', $css, -1, $this->commentsincss);
     $medias = array('all' => new css_media());
     $imports = array();
     $charset = false;
     // Keyframes are used for CSS animation they will be processed right at the very end.
     $keyframes = array();
     $currentprocess = self::PROCESSING_START;
     $currentrule = css_rule::init();
     $currentselector = css_selector::init();
     $inquotes = false;
     // ' or "
     $inbraces = false;
     // {
     $inbrackets = false;
     // [
     $inparenthesis = false;
     // (
     $currentmedia = $medias['all'];
     $currentatrule = null;
     $suspectatrule = false;
     $buffer = '';
     $char = null;
     // Next we are going to iterate over every single character in $css.
     // This is why we removed line breaks and comments!
     for ($i = 0; $i < $this->rawstrlen; $i++) {
         $lastchar = $char;
         $char = substr($css, $i, 1);
         if ($char == '@' && $buffer == '') {
             $suspectatrule = true;
         }
         switch ($currentprocess) {
             // Start processing an @ rule e.g. @media, @page, @keyframes
             case self::PROCESSING_ATRULE:
                 switch ($char) {
                     case ';':
                         if (!$inbraces) {
                             $buffer .= $char;
                             if ($currentatrule == 'import') {
                                 $imports[] = $buffer;
                                 $currentprocess = self::PROCESSING_SELECTORS;
                             } else {
                                 if ($currentatrule == 'charset') {
                                     $charset = $buffer;
                                     $currentprocess = self::PROCESSING_SELECTORS;
                                 }
                             }
                         }
                         if ($currentatrule !== 'media') {
                             $buffer = '';
                             $currentatrule = false;
                         }
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '{':
                         if ($currentatrule == 'media' && preg_match('#\\s*@media\\s*([a-zA-Z0-9]+(\\s*,\\s*[a-zA-Z0-9]+)*)\\s*{#', $buffer, $matches)) {
                             // Basic media declaration
                             $mediatypes = str_replace(' ', '', $matches[1]);
                             if (!array_key_exists($mediatypes, $medias)) {
                                 $medias[$mediatypes] = new css_media($mediatypes);
                             }
                             $currentmedia = $medias[$mediatypes];
                             $currentprocess = self::PROCESSING_SELECTORS;
                             $buffer = '';
                         } else {
                             if ($currentatrule == 'media' && preg_match('#\\s*@media\\s*([^{]+)#', $buffer, $matches)) {
                                 // Advanced media query declaration http://www.w3.org/TR/css3-mediaqueries/
                                 $mediatypes = $matches[1];
                                 $hash = md5($mediatypes);
                                 $medias[$hash] = new css_media($mediatypes);
                                 $currentmedia = $medias[$hash];
                                 $currentprocess = self::PROCESSING_SELECTORS;
                                 $buffer = '';
                             } else {
                                 if ($currentatrule == 'keyframes' && preg_match('#@((\\-moz\\-|\\-webkit\\-)?keyframes)\\s*([^\\s]+)#', $buffer, $matches)) {
                                     // Keyframes declaration, we treat it exactly like a @media declaration except we don't allow
                                     // them to be overridden to ensure we don't mess anything up. (means we keep everything in order)
                                     $keyframefor = $matches[1];
                                     $keyframename = $matches[3];
                                     $keyframe = new css_keyframe($keyframefor, $keyframename);
                                     $keyframes[] = $keyframe;
                                     $currentmedia = $keyframe;
                                     $currentprocess = self::PROCESSING_SELECTORS;
                                     $buffer = '';
                                 }
                             }
                         }
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                 }
                 break;
                 // Start processing selectors
             // Start processing selectors
             case self::PROCESSING_START:
             case self::PROCESSING_SELECTORS:
                 switch ($char) {
                     case '[':
                         $inbrackets++;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ']':
                         $inbrackets--;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ' ':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         if (!empty($buffer)) {
                             // Check for known @ rules
                             if ($suspectatrule && preg_match('#@(media|import|charset|(\\-moz\\-|\\-webkit\\-)?(keyframes))\\s*#', $buffer, $matches)) {
                                 $currentatrule = !empty($matches[3]) ? $matches[3] : $matches[1];
                                 $currentprocess = self::PROCESSING_ATRULE;
                                 $buffer .= $char;
                             } else {
                                 $currentselector->add($buffer);
                                 $buffer = '';
                             }
                         }
                         $suspectatrule = false;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '{':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         if ($buffer !== '') {
                             $currentselector->add($buffer);
                         }
                         $currentrule->add_selector($currentselector);
                         $currentselector = css_selector::init();
                         $currentprocess = self::PROCESSING_STYLES;
                         $buffer = '';
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '}':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         if ($currentatrule == 'media') {
                             $currentmedia = $medias['all'];
                             $currentatrule = false;
                             $buffer = '';
                         } else {
                             if (strpos($currentatrule, 'keyframes') !== false) {
                                 $currentmedia = $medias['all'];
                                 $currentatrule = false;
                                 $buffer = '';
                             }
                         }
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ',':
                         if ($inbrackets) {
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         $currentselector->add($buffer);
                         $currentrule->add_selector($currentselector);
                         $currentselector = css_selector::init();
                         $buffer = '';
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                 }
                 break;
                 // Start processing styles
             // Start processing styles
             case self::PROCESSING_STYLES:
                 if ($char == '"' || $char == "'") {
                     if ($inquotes === false) {
                         $inquotes = $char;
                     }
                     if ($inquotes === $char && $lastchar !== '\\') {
                         $inquotes = false;
                     }
                 }
                 if ($inquotes) {
                     $buffer .= $char;
                     continue 2;
                 }
                 switch ($char) {
                     case ';':
                         if ($inparenthesis) {
                             $buffer .= $char;
                             // continue 1: The switch processing chars
                             // continue 2: The switch processing the state
                             // continue 3: The for loop
                             continue 3;
                         }
                         $currentrule->add_style($buffer);
                         $buffer = '';
                         $inquotes = false;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '}':
                         $currentrule->add_style($buffer);
                         $this->rawselectors += $currentrule->get_selector_count();
                         $currentmedia->add_rule($currentrule);
                         $currentrule = css_rule::init();
                         $currentprocess = self::PROCESSING_SELECTORS;
                         $this->rawrules++;
                         $buffer = '';
                         $inquotes = false;
                         $inparenthesis = false;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case '(':
                         $inparenthesis = true;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                     case ')':
                         $inparenthesis = false;
                         $buffer .= $char;
                         // continue 1: The switch processing chars
                         // continue 2: The switch processing the state
                         // continue 3: The for loop
                         continue 3;
                 }
                 break;
         }
         $buffer .= $char;
     }
     foreach ($medias as $media) {
         $this->optimise($media);
     }
     $css = $this->produce_css($charset, $imports, $medias, $keyframes);
     $this->timecomplete = microtime(true);
     return trim($css);
 }