PHP:解析CSS文件,找到引用的font-family值,复制不带引号字体的规则/选择器,将类添加到具有该字体名称的所有选择器

时间:2015-07-02 01:51:04

标签: php css asynchronous fonts

这是一个简单的例子,展示了我想用CSS做什么。

示例输入:

html {
  font-family: "PT Sans", Helvetica, Arial, sans-serif;
  color: #222222;
}

期望的输出:

html {
  font-family: Helvetica, Arial, sans-serif;
  color: #222222;
}
.pt-sans html {
  font-family: "PT Sans", Helvetica, Arial, sans-serif;
  color: #222222;
}

我一直在使用https://github.com/sabberworm/PHP-CSS-Parser,但这些例子对我来说还不够详细。

这是我到目前为止所做的:

<?php
$css_string = '
    html {
      font-family: "PT Sans", Helvetica, Arial, sans-serif;
      color: #222222;
    }';

// Create parser.
$oSettings = Sabberworm\CSS\Settings::create()->withMultibyteSupport(false);
$oCssParser = new Sabberworm\CSS\Parser($css_string, $oSettings);
$oCssDocument = $oCssParser->parse();

// Get font-family rules.
foreach($oCssDocument->getAllRuleSets() as $key0 => $oRuleSet) {
  $rules = $oRuleSet->getRules('font-family');
  if (!empty($rules)) {
    foreach ($rules as $key1 => $values) {
      var_dump(array($key0, $key1));
      var_dump($values->getValue());
    }
  }
}

哪个输出

array (size=2)
  0 => int 0
  1 => int 0

object(Sabberworm\CSS\Value\RuleValueList)[91]
  protected 'aComponents' => 
    array (size=4)
      0 => 
        object(Sabberworm\CSS\Value\String)[85]
          private 'sString' => string 'PT Sans' (length=7)
      1 => string 'Helvetica' (length=9)
      2 => string 'Arial' (length=5)
      3 => string 'sans-serif' (length=10)
  protected 'sSeparator' => string ',' (length=1)

我想要这样做的原因是异步字体加载https://www.filamentgroup.com/lab/font-events.html我会在这里运行一些js代码。

<script src="//cdn.rawgit.com/bramstein/fontfaceobserver/master/fontfaceobserver.js"></script>
<script>
var observer = new window.FontFaceObserver("PT Sans", {});
observer.check(null, 5000).then(function () {
  window.document.documentElement.className += " pt-sans";
});
</script>

编辑: 我现在有了这个

$oSettings = Sabberworm\CSS\Settings::create()->withMultibyteSupport(false);
$oParser = new Sabberworm\CSS\Parser($css_string, $oSettings);
$oDoc = $oParser->parse();

$external_fonts_used = array();
foreach($oDoc->getAllRuleSets() as $key0 => $oRuleSet) {
  $rules = $oRuleSet->getRules('font-family');
  if (!empty($rules)) {
    foreach ($rules as $key1 => $values) {
      $get_value = $values->getValue();
      if ($get_value instanceof Sabberworm\CSS\Value\RuleValueList) {
        foreach ($get_value->getListComponents() as $key2 => $value) {
          if ($value instanceof Sabberworm\CSS\Value\String) {
            $external_fonts_used[$key0] = $value->getString();
          }
        }
      }
      if ($get_value instanceof Sabberworm\CSS\Value\String) {
        $external_fonts_used[$key0] = $get_value->getString();
      }
    }
  }
}
var_dump($external_fonts_used);

哪个给了我

array (size=1)
  0 => string 'PT Sans' (length=7)

这意味着我可以定位正确的CSS规则并阅读信息;现在我需要修改和复制它们。

1 个答案:

答案 0 :(得分:0)

结束创建我自己的简单css解析器,因为它的速度要快得多。

$css_string = '
html {
  font-family: "PT Sans", Helvetica, Arial, sans-serif;
  color: #222222;
}';

// Get the CSS that contains a font-family rule.
$length = strlen($css_string);
$porperty = 'font-family';
$replacements = array();

while (($last_position = strpos($css_string, $porperty, $last_position)) !== FALSE) {
  // Get closing bracket.
  $end = strpos($css_string, '}', $last_position);
  if ($end === FALSE) {
    $end = $length;
  }
  $end++;

  // Get position of the last closing bracket (start of this section).
  $start = strrpos($css_string, '}', - ($length - $last_position));
  if ($start === FALSE) {
    $start = 0;
  }
  else {
    $start++;
  }

  // Get closing ; in order to get the end of the declaration.
  $declaration_end = strpos($css_string, ';', $last_position);

  // Get values.
  $start_of_values = strpos($css_string, ':', $last_position);
  $values_string = substr($css_string, $start_of_values + 1, $declaration_end - ($start_of_values + 1));
  // Parse values string into an array of values.
  $values_array = explode(',', $values_string);

  // Values array has more than 1 value and first element is a quoted string.
  if (count($values_array) > 1 && (strpos($values_array[0], '"') || strpos($values_array[0], "'"))) {
    // Remove first value and render css rule.
    $removed_value = strtolower(trim(str_replace(array('"', "'"), '', $values_array[0])));
    $removed_value = str_replace(' ', '-', $removed_value);
    unset($values_array[0]);
    $new_values_array = implode(',', $values_array);

    // Get all selectors.
    $end_of_selectors = strpos($css_string, '{', $start);
    $selectors = substr($css_string, $start, $end_of_selectors - $start);

    // From advagg_load_stylesheet_content().
    // Perform some safe CSS optimizations.
    // Regexp to match comment blocks.
    $comment     = '/\*[^*]*\*+(?:[^/*][^*]*\*+)*/';
    // Regexp to match double quoted strings.
    $double_quot = '"[^"\\\\]*(?:\\\\.[^"\\\\]*)*"';
    // Regexp to match single quoted strings.
    $single_quot = "'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'";
    // Strip all comment blocks, but keep double/single quoted strings.
    $selectors_stripped = preg_replace(
      "<($double_quot|$single_quot)|$comment>Ss",
      "$1",
      $selectors
    );

    // Add css class to all the selectors.
    $selectors_array = explode(',', $selectors_stripped);
    foreach ($selectors_array as &$selector) {
      $selector = ' .' . $removed_value . ' ' . $selector;
    }
    $new_selectors = implode(',', $selectors_array);

    // Get full rule set.
    $full_rule_set = substr($css_string, $start, $end - $start);
    // Replace values.
    $new_full_rule_set = str_replace($values_string, $new_values_array, $full_rule_set);
    // Replace selectors.
    $new_full_rule_set = str_replace($selectors, $new_selectors, $new_full_rule_set);

    // Record info.
    $replacements[] = array($full_rule_set, $new_full_rule_set, $removed_value);
  }

  // Advance position.
  $last_position = $end;
}

foreach ($replacements as $replace) {
  $css_string = str_replace($replace[0], $replace[0] . $replace[1], $css_string);
}

输出:

html {
  font-family: "PT Sans", Helvetica, Arial, sans-serif;
  color: #222222;
} .pt-sans
html {
  font-family: Helvetica, Arial, sans-serif;
  color: #222222;
}