1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
15: namespace Cake\Routing\Filter;
16:
17: use Cake\Core\Plugin;
18: use Cake\Event\Event;
19: use Cake\Http\Response;
20: use Cake\Http\ServerRequest;
21: use Cake\Routing\DispatcherFilter;
22: use Cake\Utility\Inflector;
23:
24: 25: 26: 27:
28: class AssetFilter extends DispatcherFilter
29: {
30: 31: 32: 33: 34: 35:
36: protected $_priority = 9;
37:
38: 39: 40: 41: 42:
43: protected $_cacheTime = '+1 day';
44:
45: 46: 47: 48: 49: 50:
51: public function __construct($config = [])
52: {
53: if (!empty($config['cacheTime'])) {
54: $this->_cacheTime = $config['cacheTime'];
55: }
56: parent::__construct($config);
57: }
58:
59: 60: 61: 62: 63: 64: 65:
66: public function beforeDispatch(Event $event)
67: {
68:
69: $request = $event->getData('request');
70:
71: $url = urldecode($request->getUri()->getPath());
72: if (strpos($url, '..') !== false || strpos($url, '.') === false) {
73: return null;
74: }
75:
76: $assetFile = $this->_getAssetFile($url);
77: if ($assetFile === null || !file_exists($assetFile)) {
78: return null;
79: }
80:
81: $response = $event->getData('response');
82: $event->stopPropagation();
83:
84: $response = $response->withModified(filemtime($assetFile));
85: if ($response->checkNotModified($request)) {
86: return $response;
87: }
88:
89: $pathSegments = explode('.', $url);
90: $ext = array_pop($pathSegments);
91:
92: return $this->_deliverAsset($request, $response, $assetFile, $ext);
93: }
94:
95: 96: 97: 98: 99: 100:
101: protected function _getAssetFile($url)
102: {
103: $parts = explode('/', ltrim($url, '/'));
104: $pluginPart = [];
105: for ($i = 0; $i < 2; $i++) {
106: if (!isset($parts[$i])) {
107: break;
108: }
109: $pluginPart[] = Inflector::camelize($parts[$i]);
110: $plugin = implode('/', $pluginPart);
111: if ($plugin && Plugin::isLoaded($plugin)) {
112: $parts = array_slice($parts, $i + 1);
113: $fileFragment = implode(DIRECTORY_SEPARATOR, $parts);
114: $pluginWebroot = Plugin::path($plugin) . 'webroot' . DIRECTORY_SEPARATOR;
115:
116: return $pluginWebroot . $fileFragment;
117: }
118: }
119: }
120:
121: 122: 123: 124: 125: 126: 127: 128: 129:
130: protected function _deliverAsset(ServerRequest $request, Response $response, $assetFile, $ext)
131: {
132: $compressionEnabled = $response->compress();
133: if ($response->getType() === $ext) {
134: $contentType = 'application/octet-stream';
135: $agent = $request->getEnv('HTTP_USER_AGENT');
136: if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
137: $contentType = 'application/octetstream';
138: }
139: $response = $response->withType($contentType);
140: }
141: if (!$compressionEnabled) {
142: $response = $response->withHeader('Content-Length', filesize($assetFile));
143: }
144: $response = $response->withCache(filemtime($assetFile), $this->_cacheTime)
145: ->withFile($assetFile);
146:
147: return $response;
148: }
149: }
150: