AssetFilter.php 4.7 KB

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