]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/Minify/extlib/minify/min/lib/Minify/ImportProcessor.php
Merge branch '0.9.x' into testing
[quix0rs-gnu-social.git] / plugins / Minify / extlib / minify / min / lib / Minify / ImportProcessor.php
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  */
20 class Minify_ImportProcessor {
21     
22     public static $filesIncluded = array();
23     
24     public static function process($file)
25     {
26         self::$filesIncluded = array();
27         self::$_isCss = (strtolower(substr($file, -4)) === '.css');
28         $obj = new Minify_ImportProcessor(dirname($file));
29         return $obj->_getContent($file);
30     }
31     
32     // allows callback funcs to know the current directory
33     private $_currentDir = null;
34     
35     // allows _importCB to write the fetched content back to the obj
36     private $_importedContent = '';
37     
38     private static $_isCss = null;
39     
40     private function __construct($currentDir)
41     {
42         $this->_currentDir = $currentDir;
43     }
44     
45     private function _getContent($file)
46     {
47         $file = realpath($file);
48         if (! $file
49             || in_array($file, self::$filesIncluded)
50             || false === ($content = @file_get_contents($file))
51         ) {
52             // file missing, already included, or failed read
53             return '';
54         }
55         self::$filesIncluded[] = realpath($file);
56         $this->_currentDir = dirname($file);
57         
58         // remove UTF-8 BOM if present
59         if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) {
60             $content = substr($content, 3);
61         }
62         // ensure uniform EOLs
63         $content = str_replace("\r\n", "\n", $content);
64         
65         // process @imports
66         $content = preg_replace_callback(
67             '/
68                 @import\\s+
69                 (?:url\\(\\s*)?      # maybe url(
70                 [\'"]?               # maybe quote
71                 (.*?)                # 1 = URI
72                 [\'"]?               # maybe end quote
73                 (?:\\s*\\))?         # maybe )
74                 ([a-zA-Z,\\s]*)?     # 2 = media list
75                 ;                    # end token
76             /x'
77             ,array($this, '_importCB')
78             ,$content
79         );
80         
81         if (self::$_isCss) {
82             // rewrite remaining relative URIs
83             $content = preg_replace_callback(
84                 '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
85                 ,array($this, '_urlCB')
86                 ,$content
87             );
88         }
89         
90         return $this->_importedContent . $content;
91     }
92     
93     private function _importCB($m)
94     {
95         $url = $m[1];
96         $mediaList = preg_replace('/\\s+/', '', $m[2]);
97         
98         if (strpos($url, '://') > 0) {
99             // protocol, leave in place for CSS, comment for JS
100             return self::$_isCss
101                 ? $m[0]
102                 : "/* Minify_ImportProcessor will not include remote content */";
103         }
104         if ('/' === $url[0]) {
105             // protocol-relative or root path
106             $url = ltrim($url, '/');
107             $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR
108                 . strtr($url, '/', DIRECTORY_SEPARATOR);
109         } else {
110             // relative to current path
111             $file = $this->_currentDir . DIRECTORY_SEPARATOR 
112                 . strtr($url, '/', DIRECTORY_SEPARATOR);
113         }
114         $obj = new Minify_ImportProcessor(dirname($file));
115         $content = $obj->_getContent($file);
116         if ('' === $content) {
117             // failed. leave in place for CSS, comment for JS
118             return self::$_isCss
119                 ? $m[0]
120                 : "/* Minify_ImportProcessor could not fetch '{$file}' */";;
121         }
122         return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList))
123             ? $content
124             : "@media {$mediaList} {\n{$content}\n}\n";
125     }
126     
127     private function _urlCB($m)
128     {
129         // $m[1] is either quoted or not
130         $quote = ($m[1][0] === "'" || $m[1][0] === '"')
131             ? $m[1][0]
132             : '';
133         $url = ($quote === '')
134             ? $m[1]
135             : substr($m[1], 1, strlen($m[1]) - 2);
136         if ('/' !== $url[0]) {
137             if (strpos($url, '//') > 0) {
138                 // probably starts with protocol, do not alter
139             } else {
140                 // prepend path with current dir separator (OS-independent)
141                 $path = $this->_currentDir 
142                     . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
143                 // strip doc root
144                 $path = substr($path, strlen(realpath($_SERVER['DOCUMENT_ROOT'])));
145                 // fix to absolute URL
146                 $url = strtr($path, '/\\', '//');
147                 // remove /./ and /../ where possible
148                 $url = str_replace('/./', '/', $url);
149                 // inspired by patch from Oleg Cherniy
150                 do {
151                     $url = preg_replace('@/[^/]+/\\.\\./@', '/', $url, 1, $changed);
152                 } while ($changed);
153             }
154         }
155         return "url({$quote}{$url}{$quote})";
156     }
157 }