Пример #1
0
 /**
  * Parses CSS in a string. The output is saved as an array in $this->css.
  *
  * @since 1.0.0
  *
  * @param string $string The CSS code.
  * @return bool [return value]
  */
 public function parse($string)
 {
     // Temporarily set locale to en_US in order to handle floats properly.
     $old = @setlocale(LC_ALL, 0);
     @setlocale(LC_ALL, 'C');
     $all_properties =& $this->data['csstidy']['all_properties'];
     $at_rules =& $this->data['csstidy']['at_rules'];
     $quoted_string_properties =& $this->data['csstidy']['quoted_string_properties'];
     $this->css = array();
     $this->print->input_css = $string;
     $string = str_replace("\r\n", "\n", $string) . ' ';
     $cur_comment = '';
     for ($i = 0, $size = strlen($string); $i < $size; $i++) {
         if ("\n" === $string[$i] || "\r" === $string[$i]) {
             ++$this->line;
         }
         switch ($this->status) {
             /* Case in at-block */
             case 'at':
                 if ($this->is_token($string, $i)) {
                     if ('/' === $string[$i] && '*' === @$string[$i + 1]) {
                         $this->status = 'ic';
                         ++$i;
                         $this->from[] = 'at';
                     } elseif ('{' === $string[$i]) {
                         $this->status = 'is';
                         $this->at = $this->css_new_media_section($this->at);
                         $this->_add_token(AT_START, $this->at);
                     } elseif (',' === $string[$i]) {
                         $this->at = trim($this->at) . ',';
                     } elseif ('\\' === $string[$i]) {
                         $this->at .= $this->_unicode($string, $i);
                     } elseif (in_array($string[$i], array('(', ')', ':', '.', '/'), true)) {
                         $this->at .= $string[$i];
                     }
                 } else {
                     $lastpos = strlen($this->at) - 1;
                     if (!((ctype_space($this->at[$lastpos]) || $this->is_token($this->at, $lastpos) && ',' === $this->at[$lastpos]) && ctype_space($string[$i]))) {
                         $this->at .= $string[$i];
                     }
                 }
                 break;
                 /* Case in-selector */
             /* Case in-selector */
             case 'is':
                 if ($this->is_token($string, $i)) {
                     if ('/' === $string[$i] && '*' === @$string[$i + 1] && '' === trim($this->selector)) {
                         $this->status = 'ic';
                         ++$i;
                         $this->from[] = 'is';
                     } elseif ('@' === $string[$i] && '' === trim($this->selector)) {
                         // Check for at-rule.
                         $this->invalid_at = true;
                         foreach ($at_rules as $name => $type) {
                             if (!strcasecmp(substr($string, $i + 1, strlen($name)), $name)) {
                                 if ('at' === $type) {
                                     $this->at = '@' . $name;
                                 } else {
                                     $this->selector = '@' . $name;
                                 }
                                 if ('atis' === $type) {
                                     $this->next_selector_at = $this->next_selector_at ? $this->next_selector_at : ($this->at ? $this->at : DEFAULT_AT);
                                     $this->at = $this->css_new_media_section(' ');
                                     $type = 'is';
                                 }
                                 $this->status = $type;
                                 $i += strlen($name);
                                 $this->invalid_at = false;
                             }
                         }
                         if ($this->invalid_at) {
                             $this->selector = '@';
                             $invalid_at_name = '';
                             for ($j = $i + 1; $j < $size; ++$j) {
                                 if (!ctype_alpha($string[$j])) {
                                     break;
                                 }
                                 $invalid_at_name .= $string[$j];
                             }
                             $this->log('Invalid @-rule: ' . $invalid_at_name . ' (removed)', 'Warning');
                         }
                     } elseif ('"' === $string[$i] || "'" === $string[$i]) {
                         $this->cur_string[] = $string[$i];
                         $this->status = 'instr';
                         $this->str_char[] = $string[$i];
                         $this->from[] = 'is';
                         /* Fixing CSS3 attribute selectors, i.e. a[href$=".mp3" */
                         $this->quoted_string[] = '=' === $string[$i - 1];
                     } elseif ($this->invalid_at && ';' === $string[$i]) {
                         $this->invalid_at = false;
                         $this->status = 'is';
                         if ($this->next_selector_at) {
                             $this->at = $this->css_new_media_section($this->next_selector_at);
                             $this->next_selector_at = '';
                         }
                     } elseif ('{' === $string[$i]) {
                         $this->status = 'ip';
                         if ('' === $this->at) {
                             $this->at = $this->css_new_media_section(DEFAULT_AT);
                         }
                         $this->selector = $this->css_new_selector($this->at, $this->selector);
                         $this->_add_token(SEL_START, $this->selector);
                         $this->added = false;
                     } elseif ('}' === $string[$i]) {
                         $this->_add_token(AT_END, $this->at);
                         $this->at = '';
                         $this->selector = '';
                         $this->sel_separate = array();
                     } elseif (',' === $string[$i]) {
                         $this->selector = trim($this->selector) . ',';
                         $this->sel_separate[] = strlen($this->selector);
                     } elseif ('\\' === $string[$i]) {
                         $this->selector .= $this->_unicode($string, $i);
                     } elseif ('*' === $string[$i] && @in_array($string[$i + 1], array('.', '#', '[', ':'), true) && (0 === $i || '/' !== $string[$i - 1])) {
                         // Remove unnecessary universal selector, FS#147, but not comment in selector.
                     } else {
                         $this->selector .= $string[$i];
                     }
                 } else {
                     $lastpos = strlen($this->selector) - 1;
                     if (-1 === $lastpos || !((ctype_space($this->selector[$lastpos]) || $this->is_token($this->selector, $lastpos) && ',' === $this->selector[$lastpos]) && ctype_space($string[$i]))) {
                         $this->selector .= $string[$i];
                     }
                 }
                 break;
                 /* Case in-property */
             /* Case in-property */
             case 'ip':
                 if ($this->is_token($string, $i)) {
                     if ((':' === $string[$i] || '=' === $string[$i]) && '' !== $this->property) {
                         $this->status = 'iv';
                         if (!$this->get_cfg('discard_invalid_properties') || $this->property_is_valid($this->property)) {
                             $this->property = $this->css_new_property($this->at, $this->selector, $this->property);
                             $this->property = strtolower($this->property);
                             $this->_add_token(PROPERTY, $this->property);
                         }
                     } elseif ('/' === $string[$i] && '*' === @$string[$i + 1] && '' === $this->property) {
                         $this->status = 'ic';
                         ++$i;
                         $this->from[] = 'ip';
                     } elseif ('}' === $string[$i]) {
                         $this->explode_selectors();
                         $this->status = 'is';
                         $this->invalid_at = false;
                         $this->_add_token(SEL_END, $this->selector);
                         $this->selector = '';
                         $this->property = '';
                         if ($this->next_selector_at) {
                             $this->at = $this->css_new_media_section($this->next_selector_at);
                             $this->next_selector_at = '';
                         }
                     } elseif (';' === $string[$i]) {
                         $this->property = '';
                     } elseif ('\\' === $string[$i]) {
                         $this->property .= $this->_unicode($string, $i);
                     } elseif ('' === $this->property && !ctype_space($string[$i]) || ('/' === $this->property || '/' === $string[$i])) {
                         $this->property .= $string[$i];
                     }
                 } elseif (!ctype_space($string[$i])) {
                     $this->property .= $string[$i];
                 }
                 break;
                 /* Case in-value */
             /* Case in-value */
             case 'iv':
                 $pn = ("\n" === $string[$i] || "\r" === $string[$i]) && $this->property_is_next($string, $i + 1) || $i === strlen($string) - 1;
                 if (($this->is_token($string, $i) || $pn) && !(',' === $string[$i] && !ctype_space($string[$i + 1]))) {
                     if ('/' === $string[$i] && '*' === @$string[$i + 1]) {
                         $this->status = 'ic';
                         ++$i;
                         $this->from[] = 'iv';
                     } elseif ('"' === $string[$i] || "'" === $string[$i] || '(' === $string[$i]) {
                         $this->cur_string[] = $string[$i];
                         $this->str_char[] = '(' === $string[$i] ? ')' : $string[$i];
                         $this->status = 'instr';
                         $this->from[] = 'iv';
                         $this->quoted_string[] = in_array(strtolower($this->property), $quoted_string_properties, true);
                     } elseif (',' === $string[$i]) {
                         $this->sub_value = trim($this->sub_value) . ',';
                     } elseif ('\\' === $string[$i]) {
                         $this->sub_value .= $this->_unicode($string, $i);
                     } elseif (';' === $string[$i] || $pn) {
                         if ('@' === $this->selector[0] && isset($at_rules[substr($this->selector, 1)]) && 'iv' === $at_rules[substr($this->selector, 1)]) {
                             $this->status = 'is';
                             switch ($this->selector) {
                                 case '@charset':
                                     // Add quotes to charset.
                                     $this->sub_value_arr[] = '"' . trim($this->sub_value) . '"';
                                     $this->charset = $this->sub_value_arr[0];
                                     break;
                                 case '@namespace':
                                     // Add quotes to namespace.
                                     $this->sub_value_arr[] = '"' . trim($this->sub_value) . '"';
                                     $this->namespace = implode(' ', $this->sub_value_arr);
                                     break;
                                 case '@import':
                                     $this->sub_value = trim($this->sub_value);
                                     if (empty($this->sub_value_arr)) {
                                         // Quote URLs in imports only if they're not already inside url() and not already quoted.
                                         if ('url(' !== substr($this->sub_value, 0, 4)) {
                                             if (!($this->sub_value[0] === substr($this->sub_value, -1) && in_array($this->sub_value[0], array("'", '"'), true))) {
                                                 $this->sub_value = '"' . $this->sub_value . '"';
                                             }
                                         }
                                     }
                                     $this->sub_value_arr[] = $this->sub_value;
                                     $this->import[] = implode(' ', $this->sub_value_arr);
                                     break;
                             }
                             $this->sub_value_arr = array();
                             $this->sub_value = '';
                             $this->selector = '';
                             $this->sel_separate = array();
                         } else {
                             $this->status = 'ip';
                         }
                     } elseif ('}' !== $string[$i]) {
                         $this->sub_value .= $string[$i];
                     }
                     if (('}' === $string[$i] || ';' === $string[$i] || $pn) && !empty($this->selector)) {
                         if ('' === $this->at) {
                             $this->at = $this->css_new_media_section(DEFAULT_AT);
                         }
                         // Case settings.
                         if ($this->get_cfg('lowercase_s')) {
                             $this->selector = strtolower($this->selector);
                         }
                         $this->property = strtolower($this->property);
                         $this->optimise->subvalue();
                         if ('' !== $this->sub_value) {
                             $this->sub_value_arr[] = $this->sub_value;
                             $this->sub_value = '';
                         }
                         $this->value = '';
                         while (count($this->sub_value_arr)) {
                             $sub = array_shift($this->sub_value_arr);
                             if (strstr($this->selector, 'font-face')) {
                                 $sub = $this->quote_font_format($sub);
                             }
                             if ('' !== $sub) {
                                 if (strlen($this->value) && (',' !== substr($this->value, -1, 1) || $this->get_cfg('preserve_css'))) {
                                     $this->value .= ' ';
                                 }
                                 $this->value .= $sub;
                             }
                         }
                         $this->optimise->value();
                         $valid = $this->property_is_valid($this->property);
                         if ((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) {
                             $this->css_add_property($this->at, $this->selector, $this->property, $this->value);
                             $this->_add_token(VALUE, $this->value);
                             $this->optimise->shorthands();
                         }
                         if (!$valid) {
                             if ($this->get_cfg('discard_invalid_properties')) {
                                 $this->log('Removed invalid property: ' . $this->property, 'Warning');
                             } else {
                                 $this->log('Invalid property in ' . strtoupper($this->get_cfg('css_level')) . ': ' . $this->property, 'Warning');
                             }
                         }
                         $this->property = '';
                         $this->sub_value_arr = array();
                         $this->value = '';
                     }
                     if ('}' === $string[$i]) {
                         $this->explode_selectors();
                         $this->_add_token(SEL_END, $this->selector);
                         $this->status = 'is';
                         $this->invalid_at = false;
                         $this->selector = '';
                         if ($this->next_selector_at) {
                             $this->at = $this->css_new_media_section($this->next_selector_at);
                             $this->next_selector_at = '';
                         }
                     }
                 } elseif (!$pn) {
                     $this->sub_value .= $string[$i];
                     if (ctype_space($string[$i]) || ',' === $string[$i]) {
                         $this->optimise->subvalue();
                         if ('' !== $this->sub_value) {
                             $this->sub_value_arr[] = $this->sub_value;
                             $this->sub_value = '';
                         }
                     }
                 }
                 break;
                 /* Case in string */
             /* Case in string */
             case 'instr':
                 $_str_char = $this->str_char[count($this->str_char) - 1];
                 $_cur_string = $this->cur_string[count($this->cur_string) - 1];
                 $_quoted_string = $this->quoted_string[count($this->quoted_string) - 1];
                 $temp_add = $string[$i];
                 // Add another string to the stack. Strings can't be nested inside of quotes, only parentheses,
                 // but parentheticals can be nested more than once.
                 if (')' === $_str_char && ('(' === $string[$i] || '"' === $string[$i] || '\\' === $string[$i]) && !$this->escaped($string, $i)) {
                     $this->cur_string[] = $string[$i];
                     $this->str_char[] = '(' === $string[$i] ? ')' : $string[$i];
                     $this->from[] = 'instr';
                     $this->quoted_string[] = ')' === $_str_char && '(' !== $string[$i] && '(' === trim($_cur_string) ? $_quoted_string : '(' !== $string[$i];
                     continue;
                 }
                 if (')' !== $_str_char && ("\n" === $string[$i] || "\r" === $string[$i]) && !('\\' === $string[$i - 1] && !$this->escaped($string, $i - 1))) {
                     $temp_add = '\\A';
                     $this->log('Fixed incorrect newline in string', 'Warning');
                 }
                 $_cur_string .= $temp_add;
                 if ($string[$i] === $_str_char && !$this->escaped($string, $i)) {
                     $this->status = array_pop($this->from);
                     if (!preg_match('|[' . implode('', $this->data['csstidy']['whitespace']) . ']|uis', $_cur_string) && 'content' !== $this->property) {
                         if (!$_quoted_string) {
                             if (')' !== $_str_char) {
                                 // Convert properties like
                                 // font-family: 'Arial';
                                 // to
                                 // font-family: Arial;
                                 // or
                                 // url("abc")
                                 // to
                                 // url(abc)
                                 $_cur_string = substr($_cur_string, 1, -1);
                             }
                         } else {
                             $_quoted_string = false;
                         }
                     }
                     array_pop($this->cur_string);
                     array_pop($this->quoted_string);
                     array_pop($this->str_char);
                     if (')' === $_str_char) {
                         $_cur_string = "(" . trim(substr($_cur_string, 1, -1)) . ")";
                     }
                     if ('iv' === $this->status) {
                         if (!$_quoted_string) {
                             if (false !== strpos($_cur_string, ',')) {
                                 // We can on only remove space next to ','
                                 $_cur_string = implode(',', array_map('trim', explode(',', $_cur_string)));
                             }
                             // and multiple spaces (too expensive).
                             if (false !== strpos($_cur_string, '  ')) {
                                 $_cur_string = preg_replace(',\\s+,', ' ', $_cur_string);
                             }
                         }
                         $this->sub_value .= $_cur_string;
                     } elseif ('is' === $this->status) {
                         $this->selector .= $_cur_string;
                     } elseif ('instr' === $this->status) {
                         $this->cur_string[count($this->cur_string) - 1] .= $_cur_string;
                     }
                 } else {
                     $this->cur_string[count($this->cur_string) - 1] = $_cur_string;
                 }
                 break;
                 /* Case in-comment */
             /* Case in-comment */
             case 'ic':
                 if ('*' === $string[$i] && '/' === $string[$i + 1]) {
                     $this->status = array_pop($this->from);
                     $i++;
                     $this->_add_token(COMMENT, $cur_comment);
                     $cur_comment = '';
                 } else {
                     $cur_comment .= $string[$i];
                 }
                 break;
         }
     }
     $this->optimise->postparse();
     $this->print->_reset();
     // Set locale back to original setting.
     @setlocale(LC_ALL, $old);
     return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace));
 }
 /**
  * [postparse description]
  *
  * @since 1.0.0
  *
  * @return [type] [description]
  */
 public function postparse()
 {
     if (!empty($this->parser->import)) {
         $this->parser->import = array();
     }
     if (!empty($this->parser->charset)) {
         $this->parser->charset = array();
     }
     return parent::postparse();
 }