Permalink
Browse files

First working copy, manages .less files correctly

  • Loading branch information...
1 parent d0729b1 commit 30ba097ee0cf92978e12c7f7c6bd7c7381f7e35a @Ocramius committed May 27, 2011
View
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Class LessCss_Minify
+ * @package LessCss
+ */
+
+require_once 'Minify/CSS.php';
+
+/**
+ * Minify CSS
+ *
+ * This class uses Minify_CSS and lessphp to compress LESS sources
+ *
+ * @package LessCss
+ * @author Marco Pivetta <ocramius@gmail.com>
+ * @author http://marco-pivetta.com/
+ */
+class LessCss_Minify extends Minify_CSS {
+
+ /**
+ * Minify a LESS string
+ *
+ * @param string $less
+ *
+ * @param array $options available options:
+ *
+ * @inheritdoc
+ *
+ * @return string
+ */
+ public static function minify($less, $options = array())
+ {
+ require_once 'lessphp/lessc.inc.php';
+ $lessc = new lessc();
+ return parent::minify($lessc->parse($less), $options);
+ }
+}
View
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * A LessCss content source to be minified by Minify.
+ *
+ * This allows per-source minification options and the mixing of files with
+ * content from other sources.
+ *
+ * @package LessCss
+ * @author Stephen Clay <steve@mrclay.org>
+ */
+class LessCss_Source {
+
+ /**
+ * @var int time of last modification
+ */
+ public $lastModified = null;
+
+ /**
+ * @var callback minifier function specifically for this source.
+ */
+ public $minifier = null;
+
+ /**
+ * @var array minification options specific to this source.
+ */
+ public $minifyOptions = null;
+
+ /**
+ * @var string full path of file
+ */
+ public $filepath = null;
+
+ /**
+ * @var string HTTP Content Type (Minify requires one of the constants Minify::TYPE_*)
+ */
+ public $contentType = null;
+
+ /**
+ * Create a Minify_Source
+ *
+ * In the $spec array(), you can either provide a 'filepath' to an existing
+ * file (existence will not be checked!) or give 'id' (unique string for
+ * the content), 'content' (the string content) and 'lastModified'
+ * (unixtime of last update).
+ *
+ * As a shortcut, the controller will replace "//" at the beginning
+ * of a filepath with $_SERVER['DOCUMENT_ROOT'] . '/'.
+ *
+ * @param array $spec options
+ */
+ public function __construct($spec)
+ {
+ if (isset($spec['filepath'])) {
+ if (0 === strpos($spec['filepath'], '//')) {
+ $spec['filepath'] = $_SERVER['DOCUMENT_ROOT'] . substr($spec['filepath'], 1);
+ }
+ $segments = explode('.', $spec['filepath']);
+ $ext = strtolower(array_pop($segments));
+ switch ($ext) {
+ case 'js' : $this->contentType = 'application/x-javascript';
+ break;
+ case 'css' : $this->contentType = 'text/css';
+ break;
+ case 'htm' : // fallthrough
+ case 'html' : $this->contentType = 'text/html';
+ break;
+ }
+ $this->filepath = $spec['filepath'];
+ $this->_id = $spec['filepath'];
+ $this->lastModified = filemtime($spec['filepath'])
+ // offset for Windows uploaders with out of sync clocks
+ + round(Minify::$uploaderHoursBehind * 3600);
+ } elseif (isset($spec['id'])) {
+ $this->_id = 'id::' . $spec['id'];
+ if (isset($spec['content'])) {
+ $this->_content = $spec['content'];
+ } else {
+ $this->_getContentFunc = $spec['getContentFunc'];
+ }
+ $this->lastModified = isset($spec['lastModified'])
+ ? $spec['lastModified']
+ : time();
+ }
+ if (isset($spec['contentType'])) {
+ $this->contentType = $spec['contentType'];
+ }
+ if (isset($spec['minifier'])) {
+ $this->minifier = $spec['minifier'];
+ }
+ if (isset($spec['minifyOptions'])) {
+ $this->minifyOptions = $spec['minifyOptions'];
+ }
+ }
+
+ /**
+ * Get content
+ *
+ * @return string
+ */
+ public function getContent()
+ {
+ $content = (null !== $this->filepath)
+ ? file_get_contents($this->filepath)
+ : ((null !== $this->_content)
+ ? $this->_content
+ : call_user_func($this->_getContentFunc, $this->_id)
+ );
+ // remove UTF-8 BOM if present
+ return (pack("CCC",0xef,0xbb,0xbf) === substr($content, 0, 3))
+ ? substr($content, 3)
+ : $content;
+ }
+
+ /**
+ * Get id
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ return $this->_id;
+ }
+
+ /**
+ * Verifies a single minification call can handle all sources
+ *
+ * @param array $sources Minify_Source instances
+ *
+ * @return bool true iff there no sources with specific minifier preferences.
+ */
+ public static function haveNoMinifyPrefs($sources)
+ {
+ foreach ($sources as $source) {
+ if (null !== $source->minifier
+ || null !== $source->minifyOptions) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get unique string for a set of sources
+ *
+ * @param array $sources Minify_Source instances
+ *
+ * @return string
+ */
+ public static function getDigest($sources)
+ {
+ foreach ($sources as $source) {
+ $info[] = array(
+ $source->_id, $source->minifier, $source->minifyOptions
+ );
+ }
+ return md5(serialize($info));
+ }
+
+ /**
+ * Get content type from a group of sources
+ *
+ * This is called if the user doesn't pass in a 'contentType' options
+ *
+ * @param array $sources Minify_Source instances
+ *
+ * @return string content type. e.g. 'text/css'
+ */
+ public static function getContentType($sources)
+ {
+ foreach ($sources as $source) {
+ if ($source->contentType !== null) {
+ return $source->contentType;
+ }
+ }
+ return 'text/plain';
+ }
+
+ protected $_content = null;
+ protected $_getContentFunc = null;
+ protected $_id = null;
+}
+
View
@@ -35,6 +35,8 @@ class Minify {
// there is some debate over the ideal JS Content-Type, but this is the
// Apache default and what Yahoo! uses..
const TYPE_JS = 'application/x-javascript';
+ // Custom type used to recognize .less files
+ const TYPE_LESS = 'text/less';
/**
* How many hours behind are the file modification times of uploaded files?
@@ -512,7 +514,7 @@ protected static function _handleCssImports($css)
{
if (self::$_options['bubbleCssImports']) {
// bubble CSS imports
- preg_match_all('/@import.*?;/', $css, $imports);
+ preg_match_all('/@import.*?;/', $css, $imports);
$css = implode('', $imports[0]) . preg_replace('/@import.*?;/', '', $css);
} else if ('' !== self::$importWarning) {
// remove comments so we don't mistake { in a comment as a block
@@ -73,6 +73,7 @@ public function getDefaultMinifyOptions() {
public function getDefaultMinifers() {
$ret[Minify::TYPE_JS] = array('JSMin', 'minify');
$ret[Minify::TYPE_CSS] = array('Minify_CSS', 'minify');
+ $ret[Minify::TYPE_LESS] = array('LessCss_Minify', 'minify');
$ret[Minify::TYPE_HTML] = array('Minify_HTML', 'minify');
return $ret;
}
@@ -132,7 +133,7 @@ public static function _fileIsSafe($file, $safeDirs)
return false;
}
list($revExt) = explode('.', strrev($base));
- return in_array(strrev($revExt), array('js', 'css', 'html', 'txt'));
+ return in_array(strrev($revExt), array('js', 'css', 'less', 'html', 'txt'));
}
/**
@@ -72,7 +72,7 @@ public function setupSources($options) {
// respond to. Ideally there should be only one way to reference a file.
if (// verify at least one file, files are single comma separated,
// and are all same extension
- ! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f'])
+ ! preg_match('/^[^,]+\\.(css|js|less)(?:,[^,]+\\.\\1)*$/', $_GET['f'])
// no "//"
|| strpos($_GET['f'], '//') !== false
// no "\"
@@ -66,6 +66,8 @@ public function __construct($spec)
break;
case 'css' : $this->contentType = 'text/css';
break;
+ case 'less' : $this->contentType = 'text/less';
+ break;
case 'htm' : // fallthrough
case 'html' : $this->contentType = 'text/html';
break;

0 comments on commit 30ba097

Please sign in to comment.