【问题标题】:Adding a prefix to all selectors in a excerpt of CSS with PHP?使用 PHP 在 CSS 的摘录中为所有选择器添加前缀?
【发布时间】:2018-12-12 19:04:51
【问题描述】:

好吧,假设我有一个包含一些 CSS 代码的变量 $css。我想要做的是在每个选择器前面添加一个特定的文本。比如下面的代码:

#hello, .class{width:1px;height:1px;background-color:#AAA;}
div{font-size:1px}
input, a, span{padding:4px}

将被转换为:

#someId #hello, #someId .class{ ... }
#someId div{ ... }
#someId input, #someId a, #someId span{ ... }

我一直在尝试使用几个explode()s。但是,我一直未能有效地完成任务。有什么想法吗?

谢谢! JCOC611

【问题讨论】:

  • 每条 CSS 规则都在一行吗?

标签: php css parsing


【解决方案1】:

根据 Jon 的建议,我编写了以下代码:

<?php

$prefix = '#someId';
$css = '#hello, .class{width:1px;height:1px;background-color:#AAA;}
div{font-size:1px}
input, a, span{padding:4px}';

$parts = explode('}', $css);
foreach ($parts as &$part) {
    if (empty($part)) {
        continue;
    }

    $subParts = explode(',', $part);
    foreach ($subParts as &$subPart) {
        $subPart = $prefix . ' ' . trim($subPart);
    }

    $part = implode(', ', $subParts);
}

$prefixedCss = implode("}\n", $parts);

echo $prefixedCss;

要查看它是否有效,请参阅http://codepad.org/bqRd83gu

【讨论】:

  • 如果使用@media@font-face 查询会怎样?
  • @Kolink 好点,在这些情况下,此代码将不起作用。但正如 Jon 所指出的,这种类型的代码是解决问题的快速而肮脏的解决方案。幸运的是,它满足了 JCOC611 的需求。
  • @Jan-Henk 如果我需要确保它只在前缀不存在的情况下添加,它会起作用吗?
【解决方案2】:

我写了改进版,因为我还需要媒体查询。这是我基于 Jan 的回答的函数:

function getPrefixedCss($css,$prefix){
  $parts = explode('}', $css);  
  $mediaQueryStarted = false;
  foreach ($parts as &$part) {
      if (empty($part)) {
          continue;
      }

      $partDetails = explode('{',$part);
      if(substr_count($part,"{")==2){
        $mediaQuery = $partDetails[0]."{";
        $partDetails[0] = $partDetails[1];
        $mediaQueryStarted = true;
      }

      $subParts = explode(',', $partDetails[0]);
      foreach ($subParts as &$subPart) {
          if(trim($subPart)=="@font-face") continue;    
          $subPart = $prefix . ' ' . trim($subPart);
      }       

      if(substr_count($part,"{")==2){
        $part = $mediaQuery."\n".implode(', ', $subParts)."{".$partDetails[2];
      }elseif(empty($part[0]) && $mediaQueryStarted){
        $mediaQueryStarted = false;
        $part = implode(', ', $subParts)."{".$partDetails[2]."}\n"; //finish media query
      }else{
        $part = implode(', ', $subParts)."{".$partDetails[1];
      }        
  }

  $prefixedCss = implode("}\n", $parts);

  return $prefixedCss;   
} 

它适用于媒体查询和@font-face。

【讨论】:

    【解决方案3】:

    这是 JohnyFree 的更高级变体,对 dirty 的修改很少。

    这个变体将整个 CSS 打包成一行, 也删除所有 css cmets,并检查 $partDetails[1] 是否真的在最后设置,然后内爆 *(last else)

    给 JohnyFree 更多的声望,这是他应得的。

    ps:我已经在我进行修改的地方添加了# shell style comments

    function getPrefixedCss($css,$prefix)
    {
        # Wipe all block comments
        $css = preg_replace('!/\*.*?\*/!s', '', $css);
    
        $parts = explode('}', $css);
        $mediaQueryStarted = false;
    
        foreach($parts as &$part)
        {
            $part = trim($part); # Wht not trim immediately .. ?
            if(empty($part)) continue;
            else # This else is also required
            {
                $partDetails = explode('{', $part);
                if(substr_count($part, "{")==2)
                {
                    $mediaQuery = $partDetails[0]."{";
                    $partDetails[0] = $partDetails[1];
                    $mediaQueryStarted = true;
                }
    
                $subParts = explode(',', $partDetails[0]);
                foreach($subParts as &$subPart)
                {
                    if(trim($subPart)==="@font-face") continue;
                    else $subPart = $prefix . ' ' . trim($subPart);
                }
    
                if(substr_count($part,"{")==2)
                {
                    $part = $mediaQuery."\n".implode(', ', $subParts)."{".$partDetails[2];
                }
                elseif(empty($part[0]) && $mediaQueryStarted)
                {
                    $mediaQueryStarted = false;
                    $part = implode(', ', $subParts)."{".$partDetails[2]."}\n"; //finish media query
                }
                else
                {
                    if(isset($partDetails[1]))
                    {   # Sometimes, without this check,
                        # there is an error-notice, we don't need that..
                        $part = implode(', ', $subParts)."{".$partDetails[1];
                    }
                }
    
                unset($partDetails, $mediaQuery, $subParts); # Kill those three ..
            }   unset($part); # Kill this one as well
        }
    
        # Finish with the whole new prefixed string/file in one line
        return(preg_replace('/\s+/',' ',implode("} ", $parts)));
    }
    

    【讨论】:

      【解决方案4】:

      看起来您需要一个合适的 CSS 解析器才能知道每个选择器的开始位置。

      但是,考虑到} 表示每个选择器的结束,并在除最后一个之外的每个右大括号出现后插入必要的文本,您可能能够摆脱快速而肮脏的解决方案。

      【讨论】:

      • 您的解决方案的缺陷在于规则中有多个用逗号分隔的选择器。我要搜索一些解析器;万一,你知道 QueryPath 是否可以解析它?
      • @JCOC611:确实如此。抱歉,我无法回答 QueryPath。
      【解决方案5】:

      此方法将为所有选择器添加前缀。

      /**
       * Prefix CSS code.
       * 
       * @param  $prefix prefix to add to all selectors.
       * @param  $css    CSS code to prefix.
       * @return CSS     the prefixed CSS
       */
      function prefixCSS($prefix, $css) {
          $parts = explode('}', $css);
          foreach ($parts as &$part) {
              if (empty($part)) {
                  continue;
              }
      
              $firstPart = substr($part, 0, strpos($part, '{') + 1);
              $lastPart = substr($part, strpos($part, '{') + 2);
              $subParts = explode(',', $firstPart);
              foreach ($subParts as &$subPart) {
                  $subPart = str_replace("\n", '', $subPart);
                  $subPart = $prefix . ' ' . trim($subPart);
              }
      
              $part = implode(', ', $subParts) . $lastPart;
          }
      
          $prefixedCSS = implode("}\n", $parts);
      
          return $prefixedCSS;
      }
      

      【讨论】:

        【解决方案6】:

        感谢您的解决方案。 对我来说唯一缺少的是关键帧支持。 这是我的补充:

        function getPrefixedCss($css,$prefix)
        {
            # Wipe all block comments
            $css = preg_replace('!/\*.*?\*/!s', '', $css);
        
            $parts = explode('}', $css);
            $keyframeStarted = false;
            $mediaQueryStarted = false;
        
            foreach($parts as &$part)
            {
                $part = trim($part); # Wht not trim immediately .. ?
                if(empty($part)) {
                    $keyframeStarted = false;
                    continue;
                }
                else # This else is also required
                {
                    $partDetails = explode('{', $part);
        
                    if (strpos($part, 'keyframes') !== false) {
                        $keyframeStarted = true;
                        continue;
                    }
        
                    if($keyframeStarted) {
                        continue;
                    }
        
                    if(substr_count($part, "{")==2)
                    {
                        $mediaQuery = $partDetails[0]."{";
                        $partDetails[0] = $partDetails[1];
                        $mediaQueryStarted = true;
                    }
        
                    $subParts = explode(',', $partDetails[0]);
                    foreach($subParts as &$subPart)
                    {
                        if(trim($subPart)==="@font-face") continue;
                        else $subPart = $prefix . ' ' . trim($subPart);
                    }
        
                    if(substr_count($part,"{")==2)
                    {
                        $part = $mediaQuery."\n".implode(', ', $subParts)."{".$partDetails[2];
                    }
                    elseif(empty($part[0]) && $mediaQueryStarted)
                    {
                        $mediaQueryStarted = false;
                        $part = implode(', ', $subParts)."{".$partDetails[2]."}\n"; //finish media query
                    }
                    else
                    {
                        if(isset($partDetails[1]))
                        {   # Sometimes, without this check,
                            # there is an error-notice, we don't need that..
                            $part = implode(', ', $subParts)."{".$partDetails[1];
                        }
                    }
        
                    unset($partDetails, $mediaQuery, $subParts); # Kill those three ..
                }   unset($part); # Kill this one as well
            }
        
            # Finish with the whole new prefixed string/file in one line
            return(preg_replace('/\s+/',' ',implode("} ", $parts)));
        }
        

        【讨论】:

          猜你喜欢
          • 2015-01-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-29
          • 2019-03-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多