PluginProbe
WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript / trunk
WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript vtrunk
trunk 1.0 1.1 1.2 1.3 1.3.1 1.3.2 1.4 1.5 1.5.1 1.6 2.0 2.0.1
wp-super-minify / includes / min / lib / Minify / ImportProcessor.php

ImportProcessor.php in WP Super Minify • Minify, Compress and Cache HTML, CSS & JavaScript trunk, at includes/min/lib/Minify/ImportProcessor.php

218 lines 7.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Minify_ImportProcessor
4 * @package Minify
5 */
6
7 /**
8 * Linearize a CSS/JS file by including content specified by CSS import
9 * declarations. In CSS files, relative URIs are fixed.
10 *
11 * @imports will be processed regardless of where they appear in the source
12 * files; i.e. @imports commented out or in string content will still be
13 * processed!
14 *
15 * This has a unit test but should be considered "experimental".
16 *
17 * @package Minify
18 * @author Stephen Clay <steve@mrclay.org>
19 * @author Simon Schick <simonsimcity@gmail.com>
20 */
21 class Minify_ImportProcessor
22 {
23 public static $filesIncluded = array();
24
25 public static function process($file)
26 {
27 self::$filesIncluded = array();
28 self::$_isCss = (strtolower(substr($file, -4)) === '.css');
29 $obj = new Minify_ImportProcessor(dirname($file));
30
31 return $obj->_getContent($file);
32 }
33
34 // allows callback funcs to know the current directory
35 private $_currentDir;
36
37 // allows callback funcs to know the directory of the file that inherits this one
38 private $_previewsDir;
39
40 // allows _importCB to write the fetched content back to the obj
41 private $_importedContent = '';
42
43 private static $_isCss;
44
45 /**
46 * @param String $currentDir
47 * @param String $previewsDir Is only used internally
48 */
49 private function __construct($currentDir, $previewsDir = "")
50 {
51 $this->_currentDir = $currentDir;
52 $this->_previewsDir = $previewsDir;
53 }
54
55 private function _getContent($file, $is_imported = false)
56 {
57 $file = preg_replace('~\\?.*~', '', $file);
58 $file = realpath($file);
59 if (! $file
60 || in_array($file, self::$filesIncluded)
61 || false === ($content = @file_get_contents($file))) {
62 // file missing, already included, or failed read
63 return '';
64 }
65 self::$filesIncluded[] = realpath($file);
66 $this->_currentDir = dirname($file);
67
68 // remove UTF-8 BOM if present
69 if (pack("CCC", 0xef, 0xbb, 0xbf) === substr($content, 0, 3)) {
70 $content = substr($content, 3);
71 }
72 // ensure uniform EOLs
73 $content = str_replace("\r\n", "\n", $content);
74
75 // process @imports
76 $pattern = '/
77 @import\\s+
78 (?:url\\(\\s*)? # maybe url(
79 [\'"]? # maybe quote
80 (.*?) # 1 = URI
81 [\'"]? # maybe end quote
82 (?:\\s*\\))? # maybe )
83 ([a-zA-Z,\\s]*)? # 2 = media list
84 ; # end token
85 /x';
86 $content = preg_replace_callback($pattern, array($this, '_importCB'), $content);
87
88 // You only need to rework the import-path if the script is imported
89 if (self::$_isCss && $is_imported) {
90 // rewrite remaining relative URIs
91 $pattern = '/url\\(\\s*([^\\)\\s]+)\\s*\\)/';
92 $content = preg_replace_callback($pattern, array($this, '_urlCB'), $content);
93 }
94
95 return $this->_importedContent . $content;
96 }
97
98 private function _importCB($m)
99 {
100 $url = $m[1];
101 $mediaList = preg_replace('/\\s+/', '', $m[2]);
102
103 if (strpos($url, '://') > 0) {
104 // protocol, leave in place for CSS, comment for JS
105 return self::$_isCss
106 ? $m[0]
107 : "/* Minify_ImportProcessor will not include remote content */";
108 }
109 if ('/' === $url[0]) {
110 // protocol-relative or root path
111 $url = ltrim($url, '/');
112 $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR
113 . strtr($url, '/', DIRECTORY_SEPARATOR);
114 } else {
115 // relative to current path
116 $file = $this->_currentDir . DIRECTORY_SEPARATOR
117 . strtr($url, '/', DIRECTORY_SEPARATOR);
118 }
119 $obj = new Minify_ImportProcessor(dirname($file), $this->_currentDir);
120 $content = $obj->_getContent($file, true);
121 if ('' === $content) {
122 // failed. leave in place for CSS, comment for JS
123 return self::$_isCss
124 ? $m[0]
125 : "/* Minify_ImportProcessor could not fetch '{$file}' */";
126 }
127
128 return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList))
129 ? $content
130 : "@media {$mediaList} {\n{$content}\n}\n";
131 }
132
133 private function _urlCB($m)
134 {
135 // $m[1] is either quoted or not
136 $quote = ($m[1][0] === "'" || $m[1][0] === '"') ? $m[1][0] : '';
137
138 $url = ($quote === '') ? $m[1] : substr($m[1], 1, strlen($m[1]) - 2);
139
140 if ('/' !== $url[0]) {
141 if (strpos($url, '//') > 0) {
142 // probably starts with protocol, do not alter
143 } else {
144 // prepend path with current dir separator (OS-independent)
145 $path = $this->_currentDir
146 . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
147 // update the relative path by the directory of the file that imported this one
148 $url = self::getPathDiff(realpath($this->_previewsDir), $path);
149 }
150 }
151
152 return "url({$quote}{$url}{$quote})";
153 }
154
155 /**
156 * @param string $from
157 * @param string $to
158 * @param string $ps
159 * @return string
160 */
161 private function getPathDiff($from, $to, $ps = DIRECTORY_SEPARATOR)
162 {
163 $realFrom = $this->truepath($from);
164 $realTo = $this->truepath($to);
165
166 $arFrom = explode($ps, rtrim($realFrom, $ps));
167 $arTo = explode($ps, rtrim($realTo, $ps));
168 while (count($arFrom) && count($arTo) && ($arFrom[0] == $arTo[0])) {
169 array_shift($arFrom);
170 array_shift($arTo);
171 }
172
173 return str_pad("", count($arFrom) * 3, '..' . $ps) . implode($ps, $arTo);
174 }
175
176 /**
177 * This function is to replace PHP's extremely buggy realpath().
178 * @param string $path The original path, can be relative etc.
179 * @return string The resolved path, it might not exist.
180 * @see http://stackoverflow.com/questions/4049856/replace-phps-realpath
181 */
182 private function truepath($path)
183 {
184 // whether $path is unix or not
185 $unipath = ('' === $path) || ($path[0] !== '/');
186
187 // attempts to detect if path is relative in which case, add cwd
188 if (strpos($path, ':') === false && $unipath) {
189 $path = $this->_currentDir . DIRECTORY_SEPARATOR . $path;
190 }
191
192 // resolve path parts (single dot, double dot and double delimiters)
193 $path = str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $path);
194 $parts = array_filter(explode(DIRECTORY_SEPARATOR, $path), 'strlen');
195 $absolutes = array();
196 foreach ($parts as $part) {
197 if ('.' === $part) {
198 continue;
199 }
200 if ('..' === $part) {
201 array_pop($absolutes);
202 } else {
203 $absolutes[] = $part;
204 }
205 }
206
207 $path = implode(DIRECTORY_SEPARATOR, $absolutes);
208 // resolve any symlinks
209 if (file_exists($path) && linkinfo($path) > 0) {
210 $path = readlink($path);
211 }
212 // put initial separator that could have been lost
213 $path = !$unipath ? '/' . $path : $path;
214
215 return $path;
216 }
217 }
218