summaryrefslogtreecommitdiff
path: root/min/lib/Minify/ImportProcessor.php
diff options
context:
space:
mode:
Diffstat (limited to 'min/lib/Minify/ImportProcessor.php')
-rw-r--r--min/lib/Minify/ImportProcessor.php157
1 files changed, 157 insertions, 0 deletions
diff --git a/min/lib/Minify/ImportProcessor.php b/min/lib/Minify/ImportProcessor.php
new file mode 100644
index 0000000..0d6d90a
--- /dev/null
+++ b/min/lib/Minify/ImportProcessor.php
@@ -0,0 +1,157 @@
+<?php
+/**
+ * Class Minify_ImportProcessor
+ * @package Minify
+ */
+
+/**
+ * Linearize a CSS/JS file by including content specified by CSS import
+ * declarations. In CSS files, relative URIs are fixed.
+ *
+ * @imports will be processed regardless of where they appear in the source
+ * files; i.e. @imports commented out or in string content will still be
+ * processed!
+ *
+ * This has a unit test but should be considered "experimental".
+ *
+ * @package Minify
+ * @author Stephen Clay <steve@mrclay.org>
+ */
+class Minify_ImportProcessor {
+
+ public static $filesIncluded = array();
+
+ public static function process($file)
+ {
+ self::$filesIncluded = array();
+ self::$_isCss = (strtolower(substr($file, -4)) === '.css');
+ $obj = new Minify_ImportProcessor(dirname($file));
+ return $obj->_getContent($file);
+ }
+
+ // allows callback funcs to know the current directory
+ private $_currentDir = null;
+
+ // allows _importCB to write the fetched content back to the obj
+ private $_importedContent = '';
+
+ private static $_isCss = null;
+
+ private function __construct($currentDir)
+ {
+ $this->_currentDir = $currentDir;
+ }
+
+ private function _getContent($file)
+ {
+ $file = realpath($file);
+ if (! $file
+ || in_array($file, self::$filesIncluded)
+ || false === ($content = @file_get_contents($file))
+ ) {
+ // file missing, already included, or failed read
+ return '';
+ }
+ self::$filesIncluded[] = realpath($file);
+ $this->_currentDir = dirname($file);
+
+ // remove UTF-8 BOM if present
+ if (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3)) {
+ $content = substr($content, 3);
+ }
+ // ensure uniform EOLs
+ $content = str_replace("\r\n", "\n", $content);
+
+ // process @imports
+ $content = preg_replace_callback(
+ '/
+ @import\\s+
+ (?:url\\(\\s*)? # maybe url(
+ [\'"]? # maybe quote
+ (.*?) # 1 = URI
+ [\'"]? # maybe end quote
+ (?:\\s*\\))? # maybe )
+ ([a-zA-Z,\\s]*)? # 2 = media list
+ ; # end token
+ /x'
+ ,array($this, '_importCB')
+ ,$content
+ );
+
+ if (self::$_isCss) {
+ // rewrite remaining relative URIs
+ $content = preg_replace_callback(
+ '/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
+ ,array($this, '_urlCB')
+ ,$content
+ );
+ }
+
+ return $this->_importedContent . $content;
+ }
+
+ private function _importCB($m)
+ {
+ $url = $m[1];
+ $mediaList = preg_replace('/\\s+/', '', $m[2]);
+
+ if (strpos($url, '://') > 0) {
+ // protocol, leave in place for CSS, comment for JS
+ return self::$_isCss
+ ? $m[0]
+ : "/* Minify_ImportProcessor will not include remote content */";
+ }
+ if ('/' === $url[0]) {
+ // protocol-relative or root path
+ $url = ltrim($url, '/');
+ $file = realpath($_SERVER['DOCUMENT_ROOT']) . DIRECTORY_SEPARATOR
+ . strtr($url, '/', DIRECTORY_SEPARATOR);
+ } else {
+ // relative to current path
+ $file = $this->_currentDir . DIRECTORY_SEPARATOR
+ . strtr($url, '/', DIRECTORY_SEPARATOR);
+ }
+ $obj = new Minify_ImportProcessor(dirname($file));
+ $content = $obj->_getContent($file);
+ if ('' === $content) {
+ // failed. leave in place for CSS, comment for JS
+ return self::$_isCss
+ ? $m[0]
+ : "/* Minify_ImportProcessor could not fetch '{$file}' */";;
+ }
+ return (!self::$_isCss || preg_match('@(?:^$|\\ball\\b)@', $mediaList))
+ ? $content
+ : "@media {$mediaList} {\n{$content}\n}\n";
+ }
+
+ private function _urlCB($m)
+ {
+ // $m[1] is either quoted or not
+ $quote = ($m[1][0] === "'" || $m[1][0] === '"')
+ ? $m[1][0]
+ : '';
+ $url = ($quote === '')
+ ? $m[1]
+ : substr($m[1], 1, strlen($m[1]) - 2);
+ if ('/' !== $url[0]) {
+ if (strpos($url, '//') > 0) {
+ // probably starts with protocol, do not alter
+ } else {
+ // prepend path with current dir separator (OS-independent)
+ $path = $this->_currentDir
+ . DIRECTORY_SEPARATOR . strtr($url, '/', DIRECTORY_SEPARATOR);
+ // strip doc root
+ $path = substr($path, strlen(realpath($_SERVER['DOCUMENT_ROOT'])));
+ // fix to absolute URL
+ $url = strtr($path, '/\\', '//');
+ // remove /./ and /../ where possible
+ $url = str_replace('/./', '/', $url);
+ // inspired by patch from Oleg Cherniy
+ do {
+ $url = preg_replace('@/[^/]+/\\.\\./@', '/', $url, 1, $changed);
+ } while ($changed);
+ }
+ }
+ return "url({$quote}{$url}{$quote})";
+ }
+}