Skip to content

Commit

Permalink
Implemented asset compressor as a dispatcher filter. Simplified helper
Browse files Browse the repository at this point in the history
to reflect this. Temporarly removing baseUrl feature
  • Loading branch information
lorenzo committed Aug 25, 2012
1 parent b651dce commit fe0beed
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 103 deletions.
55 changes: 47 additions & 8 deletions Lib/AssetCompiler.php
Expand Up @@ -12,6 +12,8 @@ class AssetCompiler {

protected $_Config;

protected $filesList = array();

public function __construct(AssetConfig $config) {
$this->_Config = $config;
}
Expand All @@ -24,6 +26,48 @@ public function __construct(AssetConfig $config) {
* @throws RuntimeException
*/
public function generate($build) {
$output = '';
foreach ($this->_getFilesList($build) as $file) {
$content = $this->_readFile($file);
$content = $this->filters->input($file, $content);
$output .= $content;
}
if (Configure::read('debug') < 2 || php_sapi_name() == 'cli') {
$output = $this->filters->output($build, $output);
}
return trim($output);
}

/**
* Gets the latest modified time for the files set on the build
*
* @param string $target The name of the build target to generate.
* @return int last modified time in UNIX seconds
*/
public function getLastModified($build) {
$time = 0;
foreach ($this->_getFilesList($build) as $file) {
if ($this->_Scanner->isRemote($file)) {
return time();
}
$mtime = filemtime($file);
$time = ($mtime > $time) ? $mtime : $time;
}
return $time;
}

/**
* Returns the list of files required to generate a named build
*
* @param string $target The name of the build target to generate.
* @return array The list of files to be processed
* @throws RuntimeException
*/
protected function _getFilesList($build) {
if (!empty($this->_fileList[$build])) {
return $this->_fileList[$build];
}

$ext = $this->_Config->getExt($build);
$this->_Scanner = $this->_makeScanner($this->_Config->paths($ext), $this->_Config->theme());
$this->filters = $this->_makeFilters($ext, $build);
Expand All @@ -33,16 +77,11 @@ public function generate($build) {
if (empty($files)) {
throw new RuntimeException(sprintf('No files found for build file "%s"', $build));
}
foreach ($files as $file) {

foreach ($files as &$file) {
$file = $this->_findFile($file);
$content = $this->_readFile($file);
$content = $this->filters->input($file, $content);
$output .= $content;
}
if (Configure::read('debug') < 2 || php_sapi_name() == 'cli') {
$output = $this->filters->output($build, $output);
}
return trim($output);
return $this->_fileList[$build] = $files;
}

/**
Expand Down
109 changes: 109 additions & 0 deletions Routing/Filter/AssetCompressor.php
@@ -0,0 +1,109 @@
<?php

App::uses('DispatcherFilter', 'Routing');
App::uses('AssetConfig', 'AssetCompress.Lib');
App::uses('AssetCompiler', 'AssetCompress.Lib');
App::uses('AssetCache', 'AssetCompress.Lib');

class AssetCompressor extends DispatcherFilter {

/**
* Filter priority, we need it to run before router
*
* @var int
**/
public $priority = 9;

/**
* Object containing configuration settings for asset compressor
*
* @var AssetConfig
**/
protected $_Config;

/**
* Checks if request is for a compiled asset, otherwise skip any operation
*
* @param CakeEvent $event containing the request and response object
* @return CakeResponse if the client is requesting a recognized asset, null otherwise
*/
public function beforeDispatch($event) {
$url = $event->data['request']->url;
$Config = $this->_getConfig();
$production = Configure::read('debug') === 0;
if ($production && !$Config->general('alwaysEnableController')) {
return;
}

$build = $this->_getBuild($url);
if ($build === false) {
return;
}

if (isset($event->data['request']->query['theme'])) {
$Config->theme($event->data['request']->query['theme']);
}

// Dynamically defined build file. Disabled in production for
// hopefully obvious reasons.
if ($Config->files($build) === array()) {
$files = array();
if (isset($event->data['request']->query['file'])) {
$files = $event->data['request']->query['file'];
}
$Config->files($build, $files);
}

try {
$Compiler = new AssetCompiler($Config);
$mtime = $Compiler->getLastModified($build);
$event->data['response']->modified($mtime);
if ($event->data['response']->checkNotModified($event->data['request'])) {
return $event->data['response'];
}
$contents = $Compiler->generate($build);
} catch (Exception $e) {
throw new NotFoundException($e->getMessage());
}

$event->data['response']->type($Config->getExt($build));
$event->data['response']->body($contents);
$event->stopPropagation();
return $event->data['response'];
}

/**
* Returns the build name for a requested asset
*
* @return boolean|string false if no build can be parsed from URL
* with url path otherwise
**/
protected function _getBuild($url) {
$parts = explode('.', $url, 2);
if (count($parts) < 2) {
return false;
}

$path = $this->_Config->cachePath($parts[1]);
if (empty($path)) {
return false;
}

$path = str_replace(WWW_ROOT, '', $path);
if (strpos($url, $path) !== 0) {
return false;
}
return str_replace($path, '', $url);
}

/**
* Config setter, used for testing the controller.
*/
protected function _getConfig() {
if (empty($this->_Config)) {
$this->_Config = AssetConfig::buildFromIniFile();
}
return $this->_Config;
}

}
110 changes: 15 additions & 95 deletions View/Helper/AssetCompressHelper.php
Expand Up @@ -30,12 +30,7 @@ class AssetCompressHelper extends AppHelper {
* @var array
*/
public $options = array(
'autoIncludePath' => 'views',
'buildUrl' => array(
'plugin' => 'asset_compress',
'controller' => 'assets',
'action' => 'get'
),
'autoIncludePath' => 'views'
);

/**
Expand Down Expand Up @@ -289,14 +284,8 @@ public function css($file, $options = array()) {
return $output;
}

$baseUrl = $this->_Config->get('css.baseUrl');
if ($baseUrl && !Configure::read('debug')) {
$route = $baseUrl . $this->_getBuildName($file);
} elseif ($this->useDynamicBuild($file)) {
$route = $this->_getRoute($file);
} else {
$route = $this->_locateBuild($file);
}
$baseUrl = str_replace(WWW_ROOT, '/', $this->_Config->get('css.cachePath'));
$route = $this->_getRoute($file, $baseUrl);

if (DS == '\\') {
$route = str_replace(DS, '/', $route);
Expand Down Expand Up @@ -337,14 +326,9 @@ public function script($file, $options = array()) {
}
return $output;
}
$baseUrl = $this->_Config->get('js.baseUrl');
if ($baseUrl && !Configure::read('debug')) {
$route = $baseUrl . $this->_getBuildName($file);
} elseif ($this->useDynamicBuild($file)) {
$route = $this->_getRoute($file);
} else {
$route = $this->_locateBuild($file);
}

$baseUrl = str_replace(WWW_ROOT, '/', $this->_Config->get('js.cachePath'));
$route = $this->_getRoute($file, $baseUrl);

if (DS == '\\') {
$route = str_replace(DS, '/', $route);
Expand All @@ -354,89 +338,25 @@ public function script($file, $options = array()) {
}

/**
* Check if caching is on. If caching is off, then dynamic builds
* (pointing at the controller) will be generated.
*
* If caching is on for this extension, the helper will try to locate build
* files using the cachePath. If no cache file exists a dynamic build will be done.
* Get the dynamic build path for an asset.
*/
public function useDynamicBuild($file) {
protected function _getRoute($file, $base) {
$ext = $this->_Config->getExt($file);
if (!$this->_Config->cachePath($ext)) {
return true;
}
if ($this->_locateBuild($file)) {
return false;
}
return true;
}

/**
* Get the build file name.
*
* @param string $build The build being resolved.
* @return string The resolved build name.
*/
protected function _getBuildName($build) {
$ext = $this->_Config->getExt($build);
$hash = $this->_getHashName($build, $ext);
if ($hash) {
$build = $hash;
}
$this->_Config->theme($this->theme);
return $this->_AssetCache->buildFileName($build);
}
$query = array();

/**
* Locates a build file and returns the url path to it.
*
* @param string $build Filename of the build to locate.
* @return string The url path to the built asset.
*/
protected function _locateBuild($build) {
$ext = $this->_Config->getExt($build);
$path = $this->_Config->cachePath($ext);
if (!$path) {
return false;
}
$build = $this->_getBuildName($build);
if (file_exists($path . $build)) {
return str_replace(WWW_ROOT, '/', $path . $build);
}
}

/**
* Get the dynamic build path for an asset.
*/
protected function _getRoute($file) {
$url = $this->options['buildUrl'];

//escape out of prefixes.
$prefixes = Router::prefixes();
foreach ($prefixes as $prefix) {
if (!array_key_exists($prefix, $url)) {
$url[$prefix] = false;
}
if ($this->_Config->isThemed($file)) {
$query['theme'] = $this->theme;
}
$params = array(
$file,
'base' => false
);
$ext = $this->_Config->getExt($file);
if (isset($this->_runtime[$ext][$file])) {
$hash = $this->_getHashName($file, $ext);
$components = $this->_Config->files($file);
if ($hash) {
$params[0] = $hash;
$file = $hash;
}
$params['?'] = array('file' => $components);
}
if ($this->_Config->isThemed($file)) {
$params['?']['theme'] = $this->theme;
$query['file'] = $components;
}

$url = Router::url(array_merge($url, $params));
return $url;
$query = empty($query) ? '' : '?' . http_build_query($query);
return $base . $file . $query;
}

/**
Expand Down

0 comments on commit fe0beed

Please sign in to comment.