AssetFilter.php 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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. */
  27. class AssetFilter extends DispatcherFilter {
  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. * Checks if a requested asset exists and sends it to the browser
  37. *
  38. * @param \Cake\Event\Event $event containing the request and response object
  39. * @return \Cake\Network\Response if the client is requesting a recognized asset, null otherwise
  40. * @throws NotFoundException When asset not found
  41. */
  42. public function beforeDispatch(Event $event) {
  43. $request = $event->data['request'];
  44. $url = urldecode($request->url);
  45. if (strpos($url, '..') !== false || strpos($url, '.') === false) {
  46. return;
  47. }
  48. $assetFile = $this->_getAssetFile($url);
  49. if ($assetFile === null || !file_exists($assetFile)) {
  50. return null;
  51. }
  52. $response = $event->data['response'];
  53. $event->stopPropagation();
  54. $response->modified(filemtime($assetFile));
  55. if ($response->checkNotModified($request)) {
  56. return $response;
  57. }
  58. $pathSegments = explode('.', $url);
  59. $ext = array_pop($pathSegments);
  60. $this->_deliverAsset($request, $response, $assetFile, $ext);
  61. return $response;
  62. }
  63. /**
  64. * Builds asset file path based off url
  65. *
  66. * @param string $url Asset URL
  67. * @return string Absolute path for asset file
  68. */
  69. protected function _getAssetFile($url) {
  70. $parts = explode('/', $url);
  71. $pluginPart = [];
  72. for ($i = 0; $i < 2; $i++) {
  73. if (!isset($parts[$i])) {
  74. break;
  75. }
  76. $pluginPart[] = Inflector::camelize($parts[$i]);
  77. $plugin = implode('/', $pluginPart);
  78. if ($plugin && Plugin::loaded($plugin)) {
  79. $parts = array_slice($parts, $i + 1);
  80. $fileFragment = implode(DS, $parts);
  81. $pluginWebroot = Plugin::path($plugin) . 'webroot' . DS;
  82. return $pluginWebroot . $fileFragment;
  83. }
  84. }
  85. }
  86. /**
  87. * Sends an asset file to the client
  88. *
  89. * @param \Cake\Network\Request $request The request object to use.
  90. * @param \Cake\Network\Response $response The response object to use.
  91. * @param string $assetFile Path to the asset file in the file system
  92. * @param string $ext The extension of the file to determine its mime type
  93. * @return void
  94. */
  95. protected function _deliverAsset(Request $request, Response $response, $assetFile, $ext) {
  96. $compressionEnabled = $response->compress();
  97. if ($response->type($ext) === $ext) {
  98. $contentType = 'application/octet-stream';
  99. $agent = $request->env('HTTP_USER_AGENT');
  100. if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
  101. $contentType = 'application/octetstream';
  102. }
  103. $response->type($contentType);
  104. }
  105. if (!$compressionEnabled) {
  106. $response->header('Content-Length', filesize($assetFile));
  107. }
  108. $response->cache(filemtime($assetFile));
  109. $response->sendHeaders();
  110. readfile($assetFile);
  111. if ($compressionEnabled) {
  112. ob_end_flush();
  113. }
  114. }
  115. }