Skip to content

Commit

Permalink
Refactoring Dispatchter::cached() and moving asset related code from …
Browse files Browse the repository at this point in the history
…this method into separate methodes
  • Loading branch information
burzum committed Dec 16, 2009
1 parent 780e85a commit 7aca8df
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 109 deletions.
207 changes: 119 additions & 88 deletions cake/dispatcher.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ function dispatch($url = null, $additionalParams = array()) {
} }
$this->here = $this->base . '/' . $url; $this->here = $this->base . '/' . $url;


if ($this->cached($url)) { if ($this->asset($url) || $this->cached($url)) {
$this->_stop(); $this->_stop();
} }
$controller =& $this->__getController(); $controller =& $this->__getController();
Expand Down Expand Up @@ -582,98 +582,12 @@ function getUrl($uri = null, $base = null) {
} }


/** /**
* Outputs cached dispatch for js, css, img, view cache * Outputs cached dispatch view cache
* *
* @param string $url Requested URL * @param string $url Requested URL
* @access public * @access public
*/ */
function cached($url) { function cached($url) {
if (strpos($url, '..') === false && strpos($url, '.')) {
if (strpos($url, 'ccss/') === 0) {
include WWW_ROOT . DS . Configure::read('Asset.filter.css');
$this->_stop();
} elseif (strpos($url, 'cjs/') === 0) {
include WWW_ROOT . DS . Configure::read('Asset.filter.js');
$this->_stop();
}
App::import('View', 'Media', false);
$controller = null;
$Media = new MediaView($controller);
$ext = array_pop(explode('.', $url));

if (isset($Media->mimeType[$ext])) {
$pos = 0;
$parts = explode('/', $url);

if ($parts[0] === 'theme') {
$pos = strlen($parts[0] . $parts[1]) + 1;
} elseif (count($parts) > 2) {
$pos = strlen($parts[0]);
}
$ob = @ini_get("zlib.output_compression") !== '1' && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);

if ($ob && Configure::read('Asset.compress')) {
ob_start();
ob_start('ob_gzhandler');
}
$assetFile = null;
$paths = array();
$matched = false;

if ($pos > 0) {
$plugin = substr($url, 0, $pos);
$url = preg_replace('/^' . preg_quote($plugin, '/') . '\//i', '', $url);

if (strpos($plugin, '/') !== false) {
list($plugin, $theme) = explode('/', $plugin);
$themePaths = App::path('views');

foreach ($themePaths as $viewPath) {
$path = $viewPath . 'themed' . DS . $theme . DS . 'webroot' . DS;

if ($plugin === 'theme' && (is_file($path . $url) && file_exists($path . $url))) {
$assetFile = $path . $url;
$matched = true;
break;
}
}
}

if ($matched === false) {
$paths[] = App::pluginPath($plugin) . 'webroot' . DS;
}
}

if ($matched === false) {
foreach ($paths as $path) {
if (is_file($path . $url) && file_exists($path . $url)) {
$assetFile = $path . $url;
break;
}
}
}

if ($assetFile !== null) {
$fileModified = filemtime($assetFile);
header("Date: " . date("D, j M Y G:i:s ", $fileModified) . 'GMT');
header('Content-type: ' . $Media->mimeType[$ext]);
header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
header("Cache-Control: cache");
header("Pragma: cache");
if ($ext === 'css' || $ext === 'js') {
include($assetFile);
} else {
readfile($assetFile);
}

if (Configure::read('Asset.compress')) {
ob_end_flush();
}
return true;
}
}
}

if (Configure::read('Cache.check') === true) { if (Configure::read('Cache.check') === true) {
$path = $this->here; $path = $this->here;
if ($this->here == '/') { if ($this->here == '/') {
Expand All @@ -696,7 +610,124 @@ function cached($url) {
return $view->renderCache($filename, getMicrotime()); return $view->renderCache($filename, getMicrotime());
} }
} }

return false; return false;
} }

/**
* Checks if a requested asset exists and sends it to the browser
*
* @param $url string $url Requested URL
* @return boolean True on success if the asset file was found and sent
* @access public
*/
function asset($url) {
if (strpos($url, '..') !== false || strpos($url, '.') === false) {
return false;
}

if (strpos($url, 'ccss/') === 0) {
include WWW_ROOT . DS . Configure::read('Asset.filter.css');
$this->_stop();
} elseif (strpos($url, 'cjs/') === 0) {
include WWW_ROOT . DS . Configure::read('Asset.filter.js');
$this->_stop();
}
$controller = null;
$ext = array_pop(explode('.', $url));
$pos = 0;
$parts = explode('/', $url);

if ($parts[0] === 'theme') {
$pos = strlen($parts[0] . $parts[1]) + 1;
} elseif (count($parts) > 2) {
$pos = strlen($parts[0]);
}
$assetFile = null;
$paths = array();
$matched = false;

if ($pos > 0) {
$plugin = substr($url, 0, $pos);
$url = preg_replace('/^' . preg_quote($plugin, '/') . '\//i', '', $url);

if (strpos($plugin, '/') !== false) {
list($plugin, $theme) = explode('/', $plugin);
$themePaths = App::path('views');

foreach ($themePaths as $viewPath) {
$path = $viewPath . 'themed' . DS . $theme . DS . 'webroot' . DS;
if ($plugin === 'theme' && (is_file($path . $url) && file_exists($path . $url))) {
$assetFile = $path . $url;
break;
}
}
}

if ($matched === false) {
$paths[] = App::pluginPath($plugin) . 'webroot' . DS;
}
}

if ($matched === false) {
foreach ($paths as $path) {
if (is_file($path . $url) && file_exists($path . $url)) {
$assetFile = $path . $url;
break;
}
}
}

if ($assetFile !== null) {
$this->_deliverAsset($assetFile, $ext);
return true;
}
return false;
}

/**
* Sends an asset file to the client
*
* @param string $assetFile Path to the asset file in the file system
* @param string $ext The extension of the file to determine its mime type
* @return void
* @access protected
*/
function _deliverAsset($assetFile, $ext) {
$ob = @ini_get("zlib.output_compression") !== '1' && extension_loaded("zlib") && (strpos(env('HTTP_ACCEPT_ENCODING'), 'gzip') !== false);
if ($ob && Configure::read('Asset.compress')) {
ob_start();
ob_start('ob_gzhandler');
}

App::import('View', 'Media', false);
$Media = new MediaView($controller);
if (isset($Media->mimeType[$ext])) {
$contentType = $Media->mimeType[$ext];
} else {
$contentType = 'application/octet-stream';
$agent = env('HTTP_USER_AGENT');
if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
$contentType = 'application/octetstream';
}
}

header("Date: " . date("D, j M Y G:i:s ", filemtime($assetFile)) . 'GMT');
header('Content-type: ' . $contentType);
header("Expires: " . gmdate("D, j M Y H:i:s", time() + DAY) . " GMT");
header("Cache-Control: cache");
header("Pragma: cache");

if ($ext === 'css' || $ext === 'js') {
include($assetFile);
} else {
readfile($assetFile);
}

if (Configure::read('Asset.compress')) {
ob_end_flush();
}
}

} }
?> ?>
63 changes: 42 additions & 21 deletions cake/tests/cases/dispatcher.test.php
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -1771,7 +1771,7 @@ function testChangingParamsFromBeforeFilter() {
* @return void * @return void
* @access public * @access public
*/ */
function testStaticAssets() { function testAssets() {
Router::reload(); Router::reload();
$Configure = Configure::getInstance(); $Configure = Configure::getInstance();
$Configure->__objects = null; $Configure->__objects = null;
Expand All @@ -1781,106 +1781,127 @@ function testStaticAssets() {
'vendors' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors'. DS), 'vendors' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'vendors'. DS),
'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS) 'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
)); ));

$Dispatcher =& new TestDispatcher(); $Dispatcher =& new TestDispatcher();
$debug = Configure::read('debug'); $debug = Configure::read('debug');
Configure::write('debug', 0); Configure::write('debug', 0);

ob_start(); ob_start();
$Dispatcher->dispatch('theme/test_theme/../webroot/css/test_asset.css'); $Dispatcher->dispatch('theme/test_theme/../webroot/css/test_asset.css');
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEqual(null, $result); $this->assertFalse($result);


ob_start();
$Dispatcher->dispatch('theme/test_theme/pdfs');
$result = ob_get_clean();
$this->assertFalse($result);

ob_start(); ob_start();
$Dispatcher->dispatch('theme/test_theme/flash/theme_test.swf'); $Dispatcher->dispatch('theme/test_theme/flash/theme_test.swf');
$result = ob_get_clean(); $result = ob_get_clean();
$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'flash' . DS . 'theme_test.swf'); $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'flash' . DS . 'theme_test.swf');
$this->assertEqual($file, $result); $this->assertEqual($file, $result);
$this->assertEqual('this is just a test to load swf file from the theme.', $result); $this->assertEqual('this is just a test to load swf file from the theme.', $result);

ob_start(); ob_start();
$Dispatcher->dispatch('theme/test_theme/pdfs/theme_test.pdf'); $Dispatcher->dispatch('theme/test_theme/pdfs/theme_test.pdf');
$result = ob_get_clean(); $result = ob_get_clean();
$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'pdfs' . DS . 'theme_test.pdf'); $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'pdfs' . DS . 'theme_test.pdf');
$this->assertEqual($file, $result); $this->assertEqual($file, $result);
$this->assertEqual('this is just a test to load pdf file from the theme.', $result); $this->assertEqual('this is just a test to load pdf file from the theme.', $result);

ob_start(); ob_start();
$Dispatcher->dispatch('theme/test_theme/img/test.jpg'); $Dispatcher->dispatch('theme/test_theme/img/test.jpg');
$result = ob_get_clean(); $result = ob_get_clean();
$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'img' . DS . 'test.jpg'); $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS . 'webroot' . DS . 'img' . DS . 'test.jpg');
$this->assertEqual($file, $result); $this->assertEqual($file, $result);

$Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/css/test_asset.css'); $Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/css/test_asset.css');
ob_start(); ob_start();
$Dispatcher->cached('theme/test_theme/css/test_asset.css'); $Dispatcher->asset('theme/test_theme/css/test_asset.css');
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEqual('this is the test asset css file', $result); $this->assertEqual('this is the test asset css file', $result);

$Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/js/theme.js'); $Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/js/theme.js');
ob_start(); ob_start();
$Dispatcher->cached('theme/test_theme/js/theme.js'); $Dispatcher->asset('theme/test_theme/js/theme.js');
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEqual('root theme js file', $result); $this->assertEqual('root theme js file', $result);

$Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/js/one/theme_one.js'); $Dispatcher->params = $Dispatcher->parseParams('theme/test_theme/js/one/theme_one.js');
ob_start(); ob_start();
$Dispatcher->cached('theme/test_theme/js/one/theme_one.js'); $Dispatcher->asset('theme/test_theme/js/one/theme_one.js');
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEqual('nested theme js file', $result); $this->assertEqual('nested theme js file', $result);

ob_start(); ob_start();
$Dispatcher->dispatch('test_plugin/flash/plugin_test.swf'); $Dispatcher->dispatch('test_plugin/flash/plugin_test.swf');
$result = ob_get_clean(); $result = ob_get_clean();
$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'webroot' . DS . 'flash' . DS . 'plugin_test.swf'); $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'webroot' . DS . 'flash' . DS . 'plugin_test.swf');
$this->assertEqual($file, $result); $this->assertEqual($file, $result);
$this->assertEqual('this is just a test to load swf file from the plugin.', $result); $this->assertEqual('this is just a test to load swf file from the plugin.', $result);

ob_start(); ob_start();
$Dispatcher->dispatch('test_plugin/pdfs/plugin_test.pdf'); $Dispatcher->dispatch('test_plugin/pdfs/plugin_test.pdf');
$result = ob_get_clean(); $result = ob_get_clean();
$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'webroot' . DS . 'pdfs' . DS . 'plugin_test.pdf'); $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' . DS . 'webroot' . DS . 'pdfs' . DS . 'plugin_test.pdf');
$this->assertEqual($file, $result); $this->assertEqual($file, $result);
$this->assertEqual('this is just a test to load pdf file from the plugin.', $result); $this->assertEqual('this is just a test to load pdf file from the plugin.', $result);

ob_start(); ob_start();
$Dispatcher->cached('test_plugin/js/test_plugin/test.js'); $Dispatcher->asset('test_plugin/js/test_plugin/test.js');
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEqual('alert("Test App");', $result); $this->assertEqual('alert("Test App");', $result);


$Dispatcher->params = $Dispatcher->parseParams('test_plugin/js/test_plugin/test.js'); $Dispatcher->params = $Dispatcher->parseParams('test_plugin/js/test_plugin/test.js');
ob_start(); ob_start();
$Dispatcher->cached('test_plugin/js/test_plugin/test.js'); $Dispatcher->asset('test_plugin/js/test_plugin/test.js');
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEqual('alert("Test App");', $result); $this->assertEqual('alert("Test App");', $result);


$Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/test_plugin_asset.css'); $Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/test_plugin_asset.css');
ob_start(); ob_start();
$Dispatcher->cached('test_plugin/css/test_plugin_asset.css'); $Dispatcher->asset('test_plugin/css/test_plugin_asset.css');
$result = ob_get_clean(); $result = ob_get_clean();
$this->assertEqual('this is the test plugin asset css file', $result); $this->assertEqual('this is the test plugin asset css file', $result);


$Dispatcher->params = $Dispatcher->parseParams('test_plugin/img/cake.icon.gif'); $Dispatcher->params = $Dispatcher->parseParams('test_plugin/img/cake.icon.gif');
ob_start(); ob_start();
$Dispatcher->cached('test_plugin/img/cake.icon.gif'); $Dispatcher->asset('test_plugin/img/cake.icon.gif');
$result = ob_get_clean(); $result = ob_get_clean();
$file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' .DS . 'webroot' . DS . 'img' . DS . 'cake.icon.gif'); $file = file_get_contents(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS . 'test_plugin' .DS . 'webroot' . DS . 'img' . DS . 'cake.icon.gif');
$this->assertEqual($file, $result); $this->assertEqual($file, $result);


$Dispatcher->params = $Dispatcher->parseParams('plugin_js/js/plugin_js.js'); $Dispatcher->params = $Dispatcher->parseParams('plugin_js/js/plugin_js.js');
ob_start(); ob_start();
$Dispatcher->cached('plugin_js/js/plugin_js.js'); $Dispatcher->asset('plugin_js/js/plugin_js.js');
$result = ob_get_clean(); $result = ob_get_clean();
$expected = "alert('win sauce');"; $expected = "alert('win sauce');";
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);

$Dispatcher->params = $Dispatcher->parseParams('plugin_js/js/one/plugin_one.js'); $Dispatcher->params = $Dispatcher->parseParams('plugin_js/js/one/plugin_one.js');
ob_start(); ob_start();
$Dispatcher->cached('plugin_js/js/one/plugin_one.js'); $Dispatcher->asset('plugin_js/js/one/plugin_one.js');
$result = ob_get_clean(); $result = ob_get_clean();
$expected = "alert('plugin one nested js file');"; $expected = "alert('plugin one nested js file');";
$this->assertEqual($result, $expected); $this->assertEqual($result, $expected);
Configure::write('debug', $debug); Configure::write('debug', $debug);
//reset the header content-type without page can render as plain text. //reset the header content-type without page can render as plain text.
header('Content-type: text/html'); header('Content-type: text/html');

$Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/theme_one.htc');
ob_start();
$Dispatcher->asset('test_plugin/css/unknown.extension');
$result = ob_get_clean();
$this->assertEqual('Testing a file with unknown extension to mime mapping.', $result);
header('Content-type: text/html');

$Dispatcher->params = $Dispatcher->parseParams('test_plugin/css/theme_one.htc');
ob_start();
$Dispatcher->asset('test_plugin/css/theme_one.htc');
$result = ob_get_clean();
$this->assertEqual('htc file', $result);
header('Content-type: text/html');
} }


/** /**
Expand Down
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1 @@
htc file
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1 @@
Testing a file with unknown extension to mime mapping.

0 comments on commit 7aca8df

Please sign in to comment.