Skip to content

Commit

Permalink
Merge remote branch 'vicb/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
everzet committed Jul 5, 2010
2 parents 8b239cb + 9300dbb commit c5b85c1
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 86 deletions.
4 changes: 4 additions & 0 deletions README.markdown
Expand Up @@ -189,6 +189,10 @@ less.js is maintained by Alexis Sellier [http://github.com/cloudhead](http://git

## History ##

### github latest ###

* dependency check improvements

### v1.1.0 released on 2010-07-01 ###

* added an helper to include less stylesheets in layouts / templates
Expand Down
109 changes: 23 additions & 86 deletions lib/sfLESS.class.php
Expand Up @@ -288,9 +288,16 @@ static public function findAndFixContentLinks(sfWebResponse $response, $useJs)

if ($hasLess)
{
$response->addJavascript(
sfConfig::get('app_sf_less_plugin_js_lib', '/sfLESSPlugin/js/less-1.0.30.min.js')
);
if (sfConfig::get('symfony.asset.javascripts_included', false))
{
throw new LogicException("The stylesheets must be included before the javascript in your layout");
}
else
{
$response->addJavascript(
sfConfig::get('app_sf_less_plugin_js_lib', '/sfLESSPlugin/js/less-1.0.30.min.js')
);
}
}
}

Expand Down Expand Up @@ -344,8 +351,19 @@ public function compile($lessFile)
// If we check dates - recompile only really old CSS
if ($this->isCheckDates())
{
$isCompiled = $this->checkDatesAndDependencies($lessFile,
sfConfig::get('app_sf_less_plugin_check_dependencies', false));
try
{
$d = new sfLESSDependency(sfConfig::get('sf_web_dir'),
sfConfig::get('app_sf_less_plugin_check_dependencies', false));
if (!is_file($cssFile) || $d->getMtime($lessFile) > filemtime($cssFile))
{
$isCompiled = $this->callCompiler($lessFile, $cssFile);
}
}
catch (Exception $e)
{
$isCompiled = false;
}
}
else
{
Expand All @@ -363,87 +381,6 @@ public function compile($lessFile)
return $isCompiled;
}

/**
* Compiles the files only if the target file (.css) is older than the source file (.less),
* or optionally older than any of the dependecies.
*
* @param string $lessFile The source file
* @param boolean $checkDeps Wether the dependencies should be taken into account
* @return boolean Wheter the file has been compiled
*/
protected function checkDatesAndDependencies($lessFile, $checkDeps)
{
// The files we depend on
$deps = array();
$mtime = filemtime($lessFile);

// Gets CSS file path
$cssFile = self::getCssPathOfLess($lessFile);

if (!is_file($cssFile))
{
// Always call the compiler when the target does not exist
return $this->callCompiler($lessFile, $cssFile);
}
else
{
if ($checkDeps)
{
// Compute the less file dependencies and the date of the last modified file
$deps = $this->computeDependencies($lessFile, $deps);
foreach ($deps as $file)
{
if (is_file($file))
{
$mtime = max($mtime, filemtime($file));
}
}
}
// Compile the file if the modification date occurs after the target file date
if ($mtime > filemtime($cssFile))
{
return $this->callCompiler($lessFile, $cssFile);
}
else
{
return false;
}
}
}

/**
* Compute the dependencies of the file
*
* @param file $lessFile A less file
* @param array $deps An array of pre-existing dependencies
* @return array The updated array of dependencies
*/
protected function computeDependencies($lessFile, array $deps)
{
$less = file_get_contents($lessFile);

if (preg_match_all("/\s*@import\s+(['\"])(.*?)\\1\s*;/", $less, $files))
{
foreach ($files[2] as $file)
{
// Append the .less extension when omitted
if (strpos('.', $file) === false)
{
$file .= '.less';
}
// Compute the canonical path
$file = realpath(dirname($lessFile) . '/' . $file);
if (is_file($file) && !in_array($file, $deps))
{
$deps[] = $file;
// Recursively add dependencies
$deps = array_merge($deps, $this->computeDependencies($file, $deps));
}
}
}
return $deps;
}

/**
* Compress CSS by removing whitespaces, tabs, newlines, etc.
*
Expand Down
122 changes: 122 additions & 0 deletions lib/sfLESSDependency.class.php
@@ -0,0 +1,122 @@
<?php
/*
* This file is part of the sfLESSPlugin.
* (c) 2010 Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* sfLESSDependency checks for less dependencies
*
* @package sfLESSPlugin
* @subpackage lib
* @author Victor Berchet <victor@suumit.com>
* @version 1.0.0
*/
class sfLESSDependency
{
/**
* @var string $path The base path
*/
protected $path;

/**
* @var boolean $check Wether to check for dependency
*/
protected $check = false;

/**
* @param string $path Base path (web root folder)
* @param boolean $check Whether to check for dependency
*/
public function __construct($path, $check)
{
if (!sfLESSUtils::isPathAbsolute($path) || !is_dir($path))
{
throw new InvalidArgumentException("An existing absolute folder must be provided");
}
else
{
$this->path = preg_replace('/\/$/', '', $path);
}
$this->check = $check;
}

/**
* Return the modification time of the file (optionally including its dependency)
*
* @param string $file Filename
* @return integer|false The time the files was last modified (unix timestamp)
*/
public function getMtime($lessFile)
{
$mtime = filemtime($lessFile);

if ($mtime !== false && $this->check)
{
$deps = $this->computeDependencies($lessFile, array());
foreach ($deps as $file)
{
if (is_file($file))
{
$mtime = max($mtime, filemtime($file));
}
}
}

return $mtime;
}

/**
* Compute the dependencies of the file
*
* @param file $lessFile A less file
* @param array $deps An array of pre-existing dependencies
* @return array The updated array of dependencies
*/
protected function computeDependencies($lessFile, array $deps)
{
if (!sfLESSUtils::isPathAbsolute($lessFile))
{
$lessFile = realpath($this->path . '/' . $lessFile);
}

if (is_file($lessFile))
{
$less = sfLESSUtils::stripLessComments(file_get_contents($lessFile));
if (preg_match_all("/@import\s+(?:url\s*\(\s*)?(['\"])(.*?)\\1\s*(?:\))?\s*;/", $less, $files))
{
foreach ($files[2] as $file)
{
// Append the .less when the extension is omitted
if (!preg_match('/\.(le?|c)ss$/', $file))
{
$file .= '.less';
}
// Compute the canonical path
if (sfLESSUtils::isPathAbsolute($file))
{
$file = realpath($this->path . $file);
}
else
{
$file = realpath(dirname($lessFile) . '/' . $file);
}
if ($file !== false && !in_array($file, $deps) && !preg_match('/\.css$/', $file))
{
$deps[] = $file;
// Recursively add dependencies
$deps += $this->computeDependencies($file, $deps);
}
}
}
return $deps;
}
else
{
return array();
}
}
}
56 changes: 56 additions & 0 deletions lib/sfLESSUtils.class.php
@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the sfLESSPlugin.
* (c) 2010 Konstantin Kudryashov <ever.zet@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

/**
* Various utility functions
*
* @package sfLESSPlugin
* @subpackage lib
* @author Victor Berchet <victor@suumit.com>
* @version 1.0.0
*/

class sfLESSUtils
{
/**
* Determine if a filesystem path is absolute.
*
* @param path $path A filesystem path.
*
* @return bool true, if the path is absolute, otherwise false.
*/
public static function isPathAbsolute($path)
{
if ($path[0] == '/' || $path[0] == '\\' ||
(strlen($path) > 3 && ctype_alpha($path[0]) &&
$path[1] == ':' &&
($path[2] == '\\' || $path[2] == '/')
)
)
{
return true;
}
return false;
}

/**
* Strip comments from less content
*
* @param string $less LESS code
* @return string LESS code without comments
*/
public static function stripLessComments($less)
{
// strip /* */ style comments
$less = preg_replace('#/\*.*?\*/#ms', '', $less);
// stip // style comments
$less = preg_replace('#//.*$#m', '', $less);
return $less;
}
}
2 changes: 2 additions & 0 deletions lib/test/fixtures/a/a.less
@@ -0,0 +1,2 @@
// relative path, no extension, extra spaces, single quotes
@import '../b/b' ;
3 changes: 3 additions & 0 deletions lib/test/fixtures/b/b.less
@@ -0,0 +1,3 @@
// basename with a '.'
@import "../c/c.c";
@import "../a/a";
5 changes: 5 additions & 0 deletions lib/test/fixtures/c/c.c.less
@@ -0,0 +1,5 @@
// comments
/* comment */@import /* comment */ url("../d/d"); // comment
// @import "../not/not.less";
/* @import "../not/not.less"; */
@import "../a/a";
4 changes: 4 additions & 0 deletions lib/test/fixtures/d/d.less
@@ -0,0 +1,4 @@
// circular reference
@import "../a/a";
@import "../e.css";
@import "../a/a";
2 changes: 2 additions & 0 deletions lib/test/fixtures/e/e.css
@@ -0,0 +1,2 @@
// css files should not be processed
@import "../not/not";
Empty file added lib/test/fixtures/not/not.less
Empty file.
2 changes: 2 additions & 0 deletions lib/test/fixtures/root.less
@@ -0,0 +1,2 @@
// absolute path, less extension, double quotes
@import "/a/a.less";

0 comments on commit c5b85c1

Please sign in to comment.