/
AssetDispatcher.php
133 lines (120 loc) · 3.85 KB
/
AssetDispatcher.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php
/**
* AssetDispatcher
*
* CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
* @link http://cakephp.org CakePHP(tm) Project
* @since CakePHP(tm) v 2.2
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace Cake\Routing\Filter;
use Cake\Core\App;
use Cake\Core\Configure;
use Cake\Core\Plugin;
use Cake\Event\Event;
use Cake\Network\Request;
use Cake\Network\Response;
use Cake\Routing\DispatcherFilter;
use Cake\Utility\Inflector;
/**
* Filters a request and tests whether it is a file in the webroot folder or not and
* serves the file to the client if appropriate.
*
*/
class AssetDispatcher extends DispatcherFilter {
/**
* Default priority for all methods in this filter
* This filter should run before the request gets parsed by router
*
* @var integer
*/
public $priority = 9;
/**
* Checks if a requested asset exists and sends it to the browser
*
* @param Cake\Event\Event $event containing the request and response object
* @return Cake\Network\Response if the client is requesting a recognized asset, null otherwise
*/
public function beforeDispatch(Event $event) {
$request = $event->data['request'];
$url = urldecode($request->url);
if (strpos($url, '..') !== false || strpos($url, '.') === false) {
return;
}
$assetFile = $this->_getAssetFile($url);
if ($assetFile === null || !file_exists($assetFile)) {
return null;
}
$response = $event->data['response'];
$event->stopPropagation();
$response->modified(filemtime($assetFile));
if ($response->checkNotModified($request)) {
return $response;
}
$pathSegments = explode('.', $url);
$ext = array_pop($pathSegments);
$this->_deliverAsset($request, $response, $assetFile, $ext);
return $response;
}
/**
* Builds asset file path based off url
*
* @param string $url
* @return string Absolute path for asset file
*/
protected function _getAssetFile($url) {
$parts = explode('/', $url);
if ($parts[0] === 'theme') {
$themeName = $parts[1];
unset($parts[0], $parts[1]);
$fileFragment = implode(DS, $parts);
$path = App::themePath($themeName) . 'webroot' . DS;
return $path . $fileFragment;
}
$plugin = Inflector::camelize($parts[0]);
if ($plugin && Plugin::loaded($plugin)) {
unset($parts[0]);
$fileFragment = implode(DS, $parts);
$pluginWebroot = Plugin::path($plugin) . 'webroot' . DS;
return $pluginWebroot . $fileFragment;
}
}
/**
* Sends an asset file to the client
*
* @param Cake\Network\Request $request The request object to use.
* @param Cake\Network\Response $response The response object to use.
* @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
*/
protected function _deliverAsset(Request $request, Response $response, $assetFile, $ext) {
ob_start();
$compressionEnabled = Configure::read('Asset.compress') && $response->compress();
if ($response->type($ext) == $ext) {
$contentType = 'application/octet-stream';
$agent = $request->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';
}
$response->type($contentType);
}
if (!$compressionEnabled) {
$response->header('Content-Length', filesize($assetFile));
}
$response->cache(filemtime($assetFile));
$response->send();
ob_clean();
readfile($assetFile);
if ($compressionEnabled) {
ob_end_flush();
}
}
}