1<?php
2
3/*
4 * This file is part of the Assetic package, an OpenSky project.
5 *
6 * (c) 2010-2014 OpenSky Project Inc
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Assetic\Util;
13
14/**
15 * CSS Utils.
16 *
17 * @author Kris Wallsmith <kris.wallsmith@gmail.com>
18 */
19abstract class CssUtils
20{
21    const REGEX_URLS            = '/url\((["\']?)(?P<url>.*?)(\\1)\)/';
22    const REGEX_IMPORTS         = '/@import (?:url\()?(\'|"|)(?P<url>[^\'"\)\n\r]*)\1\)?;?/';
23    const REGEX_IMPORTS_NO_URLS = '/@import (?!url\()(\'|"|)(?P<url>[^\'"\)\n\r]*)\1;?/';
24    const REGEX_IE_FILTERS      = '/src=(["\']?)(?P<url>.*?)\\1/';
25    const REGEX_COMMENTS        = '/(\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)/';
26
27    /**
28     * Filters all references -- url() and "@import" -- through a callable.
29     *
30     * @param string   $content  The CSS
31     * @param callable $callback A PHP callable
32     *
33     * @return string The filtered CSS
34     */
35    public static function filterReferences($content, $callback)
36    {
37        $content = static::filterUrls($content, $callback);
38        $content = static::filterImports($content, $callback, false);
39        $content = static::filterIEFilters($content, $callback);
40
41        return $content;
42    }
43
44    /**
45     * Filters all CSS url()'s through a callable.
46     *
47     * @param string   $content  The CSS
48     * @param callable $callback A PHP callable
49     *
50     * @return string The filtered CSS
51     */
52    public static function filterUrls($content, $callback)
53    {
54        $pattern = static::REGEX_URLS;
55
56        return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
57            return preg_replace_callback($pattern, $callback, $part);
58        });
59    }
60
61    /**
62     * Filters all CSS imports through a callable.
63     *
64     * @param string   $content    The CSS
65     * @param callable $callback   A PHP callable
66     * @param Boolean  $includeUrl Whether to include url() in the pattern
67     *
68     * @return string The filtered CSS
69     */
70    public static function filterImports($content, $callback, $includeUrl = true)
71    {
72        $pattern = $includeUrl ? static::REGEX_IMPORTS : static::REGEX_IMPORTS_NO_URLS;
73
74        return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
75            return preg_replace_callback($pattern, $callback, $part);
76        });
77    }
78
79    /**
80     * Filters all IE filters (AlphaImageLoader filter) through a callable.
81     *
82     * @param string   $content  The CSS
83     * @param callable $callback A PHP callable
84     *
85     * @return string The filtered CSS
86     */
87    public static function filterIEFilters($content, $callback)
88    {
89        $pattern = static::REGEX_IE_FILTERS;
90
91        return static::filterCommentless($content, function ($part) use (& $callback, $pattern) {
92            return preg_replace_callback($pattern, $callback, $part);
93        });
94    }
95
96    /**
97     * Filters each non-comment part through a callable.
98     *
99     * @param string   $content  The CSS
100     * @param callable $callback A PHP callable
101     *
102     * @return string The filtered CSS
103     */
104    public static function filterCommentless($content, $callback)
105    {
106        $result = '';
107        foreach (preg_split(static::REGEX_COMMENTS, $content, -1, PREG_SPLIT_DELIM_CAPTURE) as $part) {
108            if (!preg_match(static::REGEX_COMMENTS, $part, $match) || $part != $match[0]) {
109                $part = call_user_func($callback, $part);
110            }
111
112            $result .= $part;
113        }
114
115        return $result;
116    }
117
118    /**
119     * Extracts all references from the supplied CSS content.
120     *
121     * @param string $content The CSS content
122     *
123     * @return array An array of unique URLs
124     */
125    public static function extractImports($content)
126    {
127        $imports = array();
128        static::filterImports($content, function ($matches) use (&$imports) {
129            $imports[] = $matches['url'];
130        });
131
132        return array_unique(array_filter($imports));
133    }
134
135    final private function __construct()
136    {
137    }
138}
139