将CSS背景缩写转换为分开的属性值

13

我正在尝试编写一个函数,将CSS背景的简写声明转换为长格式。我已经编写了以下函数,但它有一些问题。首先它没有考虑到background-color可能是颜色值,例如blackyellow。另外,如果某些属性包含inheritnone怎么办?这是一个例子:

url('http://2.bp.blogspot.com/-h28qvOsfm1c/TaGyO_qAFcI/AAAAAAAAA9w/I7zPQLy0zVM/s640/funny-image.jpg') inherit inherit 0 0 #FFFFFF;

将上述内容转换为CSS长手写法。这是我的函数,能否改进以涵盖其他情况?

function rewrite_background($b){

    $long_hand = "";

    $count = count($b); 

    for($i=0; $i < $count; $i++){

        if(stripos($b[$i], '#') !== false){

            $long_hand .= 'background-color: '.$b[$i].'; ';

            unset($b[$i]);

        }else if(stripos($b[$i], 'url') !== false){

            $long_hand .= 'background-image: '.$b[$i].'; ';

            unset($b[$i]);

        }else if((stripos($b[$i], 'repeat') !== false) || (stripos($b[$i], 'no-repeat') !== false) || (stripos($b[$i], 'repeat-x') !== false) || (stripos($b[$i], 'repeat-y') !== false)){

            $long_hand .= 'background-repeat: '.$b[$i].'; ';

            unset($b[$i]);

        }else if((stripos($b[$i], 'scroll') !== false) || (stripos($b[$i], 'fixed') !== false)){

            $long_hand .= 'background-attachment: '.$b[$i].'; ';

            unset($b[$i]);

        }else{

            // not recognized

        }

    }

    $b = array_values($b);

    if(isset($b[0])) $long_hand .= 'background-position: '.$b[0].' '.$b[1].';';  

    return $long_hand;

} 

5个回答

5

解析CSS背景快捷方式的类

这个类可以解析几乎任何顺序的背景快捷属性,包括那些根据规范无效的属性。例如,background: top top被视为background-position: center top

所有颜色值都得到了充分支持,包括:rgb、rgba、hls、hlsa、大小写不敏感的短形式十六进制(例如 #fff)、大小写不敏感的长形式十六进制(例如 #123Abc)和大小写不敏感的颜色名称。

!important现在也得到了支持。

inherit似乎是最具挑战性的问题,但结果证明它是最简单的。对于这个属性,我参考了http://reference.sitepoint.com/css/inheritvalue,其中指出:

When you’re using shorthand notation such as background, you can’t mix inherit with other values. For example, the following background declaration is wrong:

p {
  background: #fff inherit left top;
}

... inherit must be the only value in the declaration, because there’s simply no way of identifying the subproperty to which the value inherit refers—after all, it’s not unique within the sequence. In the example above, inherit becomes ambiguous.

为了应对不确定性,这个类简单地忽略了所有其他东西(除了 !important),并将 inherit 应用于所有属性,就像你使用了 background: inherit 一样。

代码

<?php
class CSSBackground
{
    private $color_names = array(
        'AliceBlue', 'AntiqueWhite', 'Aqua', 'Aquamarine', 'Azure',
        'Beige', 'Bisque', 'Black', 'BlanchedAlmond', 'Blue',
        'BlueViolet', 'Brown', 'BurlyWood', 'CadetBlue', 'Chartreuse',
        'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson',
        'Cyan', 'DarkBlue', 'DarkCyan', 'DarkGoldenRod', 'DarkGray',
        'DarkGrey', 'DarkGreen', 'DarkKhaki', 'DarkMagenta',
        'DarkOliveGreen', 'Darkorange', 'DarkOrchid', 'DarkRed',
        'DarkSalmon', 'DarkSeaGreen', 'DarkSlateBlue', 'DarkSlateGray',
        'DarkSlateGrey', 'DarkTurquoise', 'DarkViolet', 'DeepPink',
        'DeepSkyBlue', 'DimGray', 'DimGrey', 'DodgerBlue', 'FireBrick',
        'FloralWhite', 'ForestGreen', 'Fuchsia', 'Gainsboro',
        'GhostWhite', 'Gold', 'GoldenRod', 'Gray', 'Grey', 'Green',
        'GreenYellow', 'HoneyDew', 'HotPink', 'IndianRed', 'Indigo',
        'Ivory', 'Khaki', 'Lavender', 'LavenderBlush', 'LawnGreen',
        'LemonChiffon', 'LightBlue', 'LightCoral', 'LightCyan',
        'LightGoldenRodYellow', 'LightGray', 'LightGrey', 'LightGreen',
        'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue',
        'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow',
        'Lime', 'LimeGreen', 'Linen', 'Magenta', 'Maroon',
        'MediumAquaMarine', 'MediumBlue', 'MediumOrchid', 'MediumPurple',
        'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen',
        'MediumTurquoise', 'MediumVioletRed', 'MidnightBlue', 'MintCream',
        'MistyRose', 'Moccasin', 'NavajoWhite', 'Navy', 'OldLace', 'Olive',
        'OliveDrab', 'Orange', 'OrangeRed', 'Orchid', 'PaleGoldenRod',
        'PaleGreen', 'PaleTurquoise', 'PaleVioletRed', 'PapayaWhip',
        'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple', 'Red',
        'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Salmon', 'SandyBrown',
        'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',
        'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'Tan',
        'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White',
        'WhiteSmoke', 'Yellow', 'YellowGreen'
    );

    private $m_bgcolor = 'transparent';
    private $m_bgimage = 'none';
    private $m_bgrepeat = 'repeat';
    private $m_bgattachment = 'scroll';
    private $m_bgposition = '0% 0%';
    private $m_bgimportant = false;
    private $m_bg;

    public function __construct($bg)
    {
        // reformat array names for efficient pattern matching
        $this->color_names = '/\b('.implode('|',$this->color_names).')\b/i';

        $this->m_bg = $bg;  // save original

        $bg = $this->parse_important($bg);
        $bg = $this->parse_inherit($bg);
        $bg = $this->parse_color($bg);
        $bg = $this->parse_image($bg);
        $bg = $this->parse_repeat($bg);
        $bg = $this->parse_attachment($bg);
        $bg = $this->parse_position($bg);
    }

    public function original()
    {
        return $this->m_bg;
    }

    public function color()
    {
        return $this->m_bgcolor;
    }

    public function image()
    {
        return $this->m_bgimage;
    }

    public function repeat()
    {
        return $this->m_bgrepeat;
    }

    public function attachment()
    {
        return $this->m_bgattachment;
    }

    public function position()
    {
        return $this->m_bgposition;
    }

    public function important()
    {
        return $this->m_bgimportant;
    }

    private function parse_important($c)
    {
        // check for !important
        if (preg_match('/!important/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgimportant = true ;
        }

        return $c;
    }

    private function parse_inherit($c)
    {
        // check for !important
        if (preg_match('/inherit/i', $c, $m))
        {
            $this->m_bgcolor = $this->apply_important('inherit');
            $this->m_bgimage = $this->apply_important('inherit');
            $this->m_bgrepeat = $this->apply_important('inherit');
            $this->m_bgattachment = $this->apply_important('inherit');
            $this->m_bgposition = $this->apply_important('inherit');
            $c = '';
        }

        return $c;
    }

    private function parse_color($c)
    {
        // check for hexit color value
        if (preg_match('/#([[:xdigit:]]{3}){1,2}/', $c, $m))
        {
            $c = str_replace($m[0], '', $c);

            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for rgb color value
        elseif (preg_match('/rgb\(\d{0,3}\,\d{0,3},\d{0,3}\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for rgba color value
        elseif (preg_match('/rgba\(\d{0,3}%?\,\d{0,3}%?,\d{0,3}%?\,\d(\.\d)?\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for hls color value
        elseif (preg_match('/hls\(\d{0,3}\,\d{0,3}%,\d{0,3}%\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for hlsa color value
        elseif (preg_match('/hlsa\(\d{0,3}\,\d{0,3}%,\d{0,3}%\,\d(\.\d)?\)/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        // check for transparent
        elseif (preg_match('/transparent/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important('transparent');
        }

        // check for color names
        elseif (preg_match($this->color_names, $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgcolor = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_image($c)
    {
        // check for double word positions
        if (preg_match('/url\((.*?)\)|none/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            if (isset($m[1]))
            {
                $m[0] = str_replace($m[1], urlencode($m[1]), $m[0]);
            }
            $this->m_bgimage = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_repeat($c)
    {
        // check for repeat values
        if (preg_match('/\b(repeat-x|repeat-y|no-repeat|repeat)\b/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgrepeat = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_attachment($c)
    {
        // check for repeat values
        if (preg_match('/scroll|fixed/i', $c, $m))
        {
            $c = str_replace($m[0], '', $c);
            $this->m_bgattachment = $this->apply_important($m[0]);
        }

        return $c;
    }

    private function parse_position($c)
    {
        // check for position values
        if (preg_match_all('/left|right|center|top|bottom|-?\d+([a-zA-Z]{2}|%?)/i', $c, $m))
        {
            $horz = '0%';
            $vert = '0%';

            if (!isset($m[0][1]))
            {
                $x = strtolower($m[0][0]);
                switch ($x)
                {
                    case 'top':
                    case 'bottom':
                        $horz = 'center';
                        $vert = $x;
                        break;
                    case 'left':
                    case 'right':
                    case 'center':
                        $horz = $x;
                        $vert = 'center';
                        break;
                    default:
                        $horz = is_numeric($x) ? "{$x}px" : $x;
                        $vert = 'center';
                }
            }

            else
            {
                $horz = strtolower($m[0][0]);
                $vert = strtolower($m[0][1]);

                if (($horz === $vert) && in_array($horz, array('left','right')))
                {
                    $vert = 'center';
                }

                if (($horz === $vert) && in_array($horz, array('top','bottom')))
                {
                    $horz = 'center';
                }

                if ($horz === 'top' || $horz === 'bottom')
                {
                    list($horz,$vert) = array($vert,$horz);
                }

                if ($vert === 'left' || $vert === 'right')
                {
                    list($horz,$vert) = array($vert,$horz);
                }
            }

            $this->m_bgposition = $this->apply_important("$horz $vert");
        }

        return $c;
    }

    private function apply_important($prop)
    {
        return $prop . ($this->m_bgimportant ? ' !important' : '');
    }
}

?>

使用示例

<?php
header('Content-type: text/plain');

$bg = 'url("chess.png") gray 50% repeat fixed';

$cssbg = new CSSBackground($bg);

echo "background: ", $cssbg->original(), "\n\n";
echo "background-color: ", $cssbg->color(), "\n";
echo "background-image: ", $cssbg->image(), "\n";
echo "background-repeat: ", $cssbg->repeat(), "\n";
echo "background-attachment: ", $cssbg->attachment(), "\n";
echo "background-position: ", $cssbg->position(), "\n\n";
echo "!important applied: ", $cssbg->important() ? 'true' : 'false', "\n";
?>

这个类是通过对CSS background属性的w3c规范进行广泛分析而开发的。其他CSS属性也需要进行同样的分析处理。


1
@Herbert - 这是一个很棒的课程。然而,有一个重大问题,就是用户不会遵循顺序,我已经看到许多这样的情况发生了! - Abs
1
@Abs - 感谢您的积极反馈。您提出了一个有效的观点。正如我所提到的,这只是一个让人们入门的hack工作。太多的答案都说“不可能”,这对我来说是一个四个字母的词。我会看看能否改进它。 - Herbert
1
完全重写。我现在对CSS背景属性的了解超出了我的预期。 - Herbert
1
@Herbert - 真是太棒了!细节的水平令人印象深刻。非常值得的赏金!非常感谢你! :) - Abs
1
@Abs - 感谢您的慷慨奖励。我为此付出了很多努力...有些人认为PHP无法做任何有用的事情。 :P - Herbert

1

不要使用stripos搜索$b并根据结果不是FALSE来分配CSS长手属性,为什么不按顺序进行呢?每次在将$b属性制定到函数之前,您可以使用固定模式:

$b[0]=background-attachment;
$b[1]=background-color;
$b[2]=background-image;
$b[3]=background-position;
$b[4]=background-repeat;

这意味着您需要将简写重新排列为类似于以下内容的某些内容:
$b=array(inherit, #fff, url('example.jpg'), 0 0, inherit);

你因此可以处理任何属性值(甚至包括'inherit'和'none') - 你只需确保在整个过程中保持正确的顺序,不要漏掉任何属性,否则数组键就会出错。 你可以在另一个PHP函数中轻松将CSS颜色名称转换为十六进制值(使用所有CSS名称的列表),或者你可以使用这个JavaScript函数

1

我认为在这里最好的方法是构建一个近似值,因为你的程序必须完全理解CSS才能进行任何转换,而正则表达式并不能完全胜任此任务。相反,你需要一个真正的解析库。但是为了开始,这里有一个非常好的教程,介绍如何使用Coco/R 解析器来处理CSS:http://www.codeproject.com/KB/recipes/CSSParser.aspx 它会给你更好的了解这个问题的范围。

还有一个PHP类用于CSS解析,可以在这里找到 http://www.phpclasses.org/package/1289-PHP-CSS-parser-class.html#download 但我对它的期望不高。


0

我建议你找一个有很多正则表达式经验的人为你编写一个强大的匹配字符串。

按照你目前的方式,你将会遇到真正的问题,尽管你可以假设:

url(.*)总是一个背景图片

#(.{3})(.{3})?总是一种颜色(但你可能有其他不匹配任何其他匹配项的单词也是颜色)

([1-9][0-9]%|[0-9][0-9]+(px|em)|0|inherit|top|bottom|center|left|right)是背景位置(可能有一个或两个)

(repeat|repeat-x|repeat-y|no-repeat|inherit)总是背景重复

记住它们可能以多种顺序出现。


-1

在 PHP 中完美地实现你想要的功能几乎是不可能的。最好的情况是你会得到一个非常接近的结果,但其中仍然存在一些缺陷,例如,你的代码将不支持 rgb、rgba、hsl、hsla 颜色格式,而这些格式是有效的。


你应该做的是找一个好的 php css解析器 并与之合作。

我从未说过这是不可能的。好好读清楚。我说的是它几乎不可能,事实就是如此。 - Madara's Ghost
我在不到24小时内完成了它。虽然有些困难,但绝对不是完全不可能的。此外,“php css解析器”是用PHP编写的!我想这不是讨论这种话题的地方,但这样的“答案”和类似的回复纯粹是懒惰,它们正在拖累SO的质量。回答问题或不回答,但不要告诉OP某事是不可能的,只因为你自己做不到。 - Herbert

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接