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\Filter;
13
14use Assetic\Asset\AssetInterface;
15
16/**
17 * Fixes relative CSS urls.
18 *
19 * @author Kris Wallsmith <kris.wallsmith@gmail.com>
20 */
21class CssRewriteFilter extends BaseCssFilter
22{
23    public function filterLoad(AssetInterface $asset)
24    {
25    }
26
27    public function filterDump(AssetInterface $asset)
28    {
29        $sourceBase = $asset->getSourceRoot();
30        $sourcePath = $asset->getSourcePath();
31        $targetPath = $asset->getTargetPath();
32
33        if (null === $sourcePath || null === $targetPath || $sourcePath == $targetPath) {
34            return;
35        }
36
37        // learn how to get from the target back to the source
38        if (false !== strpos($sourceBase, '://')) {
39            list($scheme, $url) = explode('://', $sourceBase.'/'.$sourcePath, 2);
40            list($host, $path) = explode('/', $url, 2);
41
42            $host = $scheme.'://'.$host.'/';
43            $path = false === strpos($path, '/') ? '' : dirname($path);
44            $path .= '/';
45        } else {
46            // assume source and target are on the same host
47            $host = '';
48
49            // pop entries off the target until it fits in the source
50            if ('.' == dirname($sourcePath)) {
51                $path = str_repeat('../', substr_count($targetPath, '/'));
52            } elseif ('.' == $targetDir = dirname($targetPath)) {
53                $path = dirname($sourcePath).'/';
54            } else {
55                $path = '';
56                while (0 !== strpos($sourcePath, $targetDir)) {
57                    if (false !== $pos = strrpos($targetDir, '/')) {
58                        $targetDir = substr($targetDir, 0, $pos);
59                        $path .= '../';
60                    } else {
61                        $targetDir = '';
62                        $path .= '../';
63                        break;
64                    }
65                }
66                $path .= ltrim(substr(dirname($sourcePath).'/', strlen($targetDir)), '/');
67            }
68        }
69
70        $content = $this->filterReferences($asset->getContent(), function ($matches) use ($host, $path) {
71            if (false !== strpos($matches['url'], '://') || 0 === strpos($matches['url'], '//') || 0 === strpos($matches['url'], 'data:')) {
72                // absolute or protocol-relative or data uri
73                return $matches[0];
74            }
75
76            if (isset($matches['url'][0]) && '/' == $matches['url'][0]) {
77                // root relative
78                return str_replace($matches['url'], $host.$matches['url'], $matches[0]);
79            }
80
81            // document relative
82            $url = $matches['url'];
83            while (0 === strpos($url, '../') && 2 <= substr_count($path, '/')) {
84                $path = substr($path, 0, strrpos(rtrim($path, '/'), '/') + 1);
85                $url = substr($url, 3);
86            }
87
88            $parts = array();
89            foreach (explode('/', $host.$path.$url) as $part) {
90                if ('..' === $part && count($parts) && '..' !== end($parts)) {
91                    array_pop($parts);
92                } else {
93                    $parts[] = $part;
94                }
95            }
96
97            return str_replace($matches['url'], implode('/', $parts), $matches[0]);
98        });
99
100        $asset->setContent($content);
101    }
102}
103