PageRenderTime 59ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/minify/lib/Minify/Controller/MinApp.php

https://bitbucket.org/synergylearning/campusconnect
PHP | 238 lines | 189 code | 6 blank | 43 comment | 35 complexity | 2ab3f9c88d9340b684a88ca16132d804 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, LGPL-2.1, Apache-2.0, BSD-3-Clause, AGPL-3.0
  1. <?php
  2. /**
  3. * Class Minify_Controller_MinApp
  4. * @package Minify
  5. */
  6. /**
  7. * Controller class for requests to /min/index.php
  8. *
  9. * @package Minify
  10. * @author Stephen Clay <steve@mrclay.org>
  11. */
  12. class Minify_Controller_MinApp extends Minify_Controller_Base {
  13. /**
  14. * Set up groups of files as sources
  15. *
  16. * @param array $options controller and Minify options
  17. *
  18. * @return array Minify options
  19. */
  20. public function setupSources($options) {
  21. // PHP insecure by default: realpath() and other FS functions can't handle null bytes.
  22. foreach (array('g', 'b', 'f') as $key) {
  23. if (isset($_GET[$key])) {
  24. $_GET[$key] = str_replace("\x00", '', (string)$_GET[$key]);
  25. }
  26. }
  27. // filter controller options
  28. $cOptions = array_merge(
  29. array(
  30. 'allowDirs' => '//'
  31. ,'groupsOnly' => false
  32. ,'groups' => array()
  33. ,'noMinPattern' => '@[-\\.]min\\.(?:js|css)$@i' // matched against basename
  34. )
  35. ,(isset($options['minApp']) ? $options['minApp'] : array())
  36. );
  37. unset($options['minApp']);
  38. $sources = array();
  39. $this->selectionId = '';
  40. $firstMissingResource = null;
  41. if (isset($_GET['g'])) {
  42. // add group(s)
  43. $this->selectionId .= 'g=' . $_GET['g'];
  44. $keys = explode(',', $_GET['g']);
  45. if ($keys != array_unique($keys)) {
  46. $this->log("Duplicate group key found.");
  47. return $options;
  48. }
  49. $keys = explode(',', $_GET['g']);
  50. foreach ($keys as $key) {
  51. if (! isset($cOptions['groups'][$key])) {
  52. $this->log("A group configuration for \"{$key}\" was not found");
  53. return $options;
  54. }
  55. $files = $cOptions['groups'][$key];
  56. // if $files is a single object, casting will break it
  57. if (is_object($files)) {
  58. $files = array($files);
  59. } elseif (! is_array($files)) {
  60. $files = (array)$files;
  61. }
  62. foreach ($files as $file) {
  63. if ($file instanceof Minify_Source) {
  64. $sources[] = $file;
  65. continue;
  66. }
  67. if (0 === strpos($file, '//')) {
  68. $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1);
  69. }
  70. $realpath = realpath($file);
  71. if ($realpath && is_file($realpath)) {
  72. $sources[] = $this->_getFileSource($realpath, $cOptions);
  73. } else {
  74. $this->log("The path \"{$file}\" (realpath \"{$realpath}\") could not be found (or was not a file)");
  75. if (null === $firstMissingResource) {
  76. $firstMissingResource = basename($file);
  77. continue;
  78. } else {
  79. $secondMissingResource = basename($file);
  80. $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource'");
  81. return $options;
  82. }
  83. }
  84. }
  85. if ($sources) {
  86. try {
  87. $this->checkType($sources[0]);
  88. } catch (Exception $e) {
  89. $this->log($e->getMessage());
  90. return $options;
  91. }
  92. }
  93. }
  94. }
  95. if (! $cOptions['groupsOnly'] && isset($_GET['f'])) {
  96. // try user files
  97. // The following restrictions are to limit the URLs that minify will
  98. // respond to.
  99. if (// verify at least one file, files are single comma separated,
  100. // and are all same extension
  101. ! preg_match('/^[^,]+\\.(css|js)(?:,[^,]+\\.\\1)*$/', $_GET['f'], $m)
  102. // no "//"
  103. || strpos($_GET['f'], '//') !== false
  104. // no "\"
  105. || strpos($_GET['f'], '\\') !== false
  106. ) {
  107. $this->log("GET param 'f' was invalid");
  108. return $options;
  109. }
  110. $ext = ".{$m[1]}";
  111. try {
  112. $this->checkType($m[1]);
  113. } catch (Exception $e) {
  114. $this->log($e->getMessage());
  115. return $options;
  116. }
  117. $files = explode(',', $_GET['f']);
  118. if ($files != array_unique($files)) {
  119. $this->log("Duplicate files were specified");
  120. return $options;
  121. }
  122. if (isset($_GET['b'])) {
  123. // check for validity
  124. if (preg_match('@^[^/]+(?:/[^/]+)*$@', $_GET['b'])
  125. && false === strpos($_GET['b'], '..')
  126. && $_GET['b'] !== '.') {
  127. // valid base
  128. $base = "/{$_GET['b']}/";
  129. } else {
  130. $this->log("GET param 'b' was invalid");
  131. return $options;
  132. }
  133. } else {
  134. $base = '/';
  135. }
  136. $allowDirs = array();
  137. foreach ((array)$cOptions['allowDirs'] as $allowDir) {
  138. $allowDirs[] = realpath(str_replace('//', $_SERVER['DOCUMENT_ROOT'] . '/', $allowDir));
  139. }
  140. $basenames = array(); // just for cache id
  141. foreach ($files as $file) {
  142. $uri = $base . $file;
  143. $path = $_SERVER['DOCUMENT_ROOT'] . $uri;
  144. $realpath = realpath($path);
  145. if (false === $realpath || ! is_file($realpath)) {
  146. $this->log("The path \"{$path}\" (realpath \"{$realpath}\") could not be found (or was not a file)");
  147. if (null === $firstMissingResource) {
  148. $firstMissingResource = $uri;
  149. continue;
  150. } else {
  151. $secondMissingResource = $uri;
  152. $this->log("More than one file was missing: '$firstMissingResource', '$secondMissingResource`'");
  153. return $options;
  154. }
  155. }
  156. try {
  157. parent::checkNotHidden($realpath);
  158. parent::checkAllowDirs($realpath, $allowDirs, $uri);
  159. } catch (Exception $e) {
  160. $this->log($e->getMessage());
  161. return $options;
  162. }
  163. $sources[] = $this->_getFileSource($realpath, $cOptions);
  164. $basenames[] = basename($realpath, $ext);
  165. }
  166. if ($this->selectionId) {
  167. $this->selectionId .= '_f=';
  168. }
  169. $this->selectionId .= implode(',', $basenames) . $ext;
  170. }
  171. if ($sources) {
  172. if (null !== $firstMissingResource) {
  173. array_unshift($sources, new Minify_Source(array(
  174. 'id' => 'missingFile'
  175. // should not cause cache invalidation
  176. ,'lastModified' => 0
  177. // due to caching, filename is unreliable.
  178. ,'content' => "/* Minify: at least one missing file. See " . Minify::URL_DEBUG . " */\n"
  179. ,'minifier' => ''
  180. )));
  181. }
  182. $this->sources = $sources;
  183. } else {
  184. $this->log("No sources to serve");
  185. }
  186. return $options;
  187. }
  188. /**
  189. * @param string $file
  190. *
  191. * @param array $cOptions
  192. *
  193. * @return Minify_Source
  194. */
  195. protected function _getFileSource($file, $cOptions)
  196. {
  197. $spec['filepath'] = $file;
  198. if ($cOptions['noMinPattern'] && preg_match($cOptions['noMinPattern'], basename($file))) {
  199. if (preg_match('~\.css$~i', $file)) {
  200. $spec['minifyOptions']['compress'] = false;
  201. } else {
  202. $spec['minifier'] = '';
  203. }
  204. }
  205. return new Minify_Source($spec);
  206. }
  207. protected $_type = null;
  208. /**
  209. * Make sure that only source files of a single type are registered
  210. *
  211. * @param string $sourceOrExt
  212. *
  213. * @throws Exception
  214. */
  215. public function checkType($sourceOrExt)
  216. {
  217. if ($sourceOrExt === 'js') {
  218. $type = Minify::TYPE_JS;
  219. } elseif ($sourceOrExt === 'css') {
  220. $type = Minify::TYPE_CSS;
  221. } elseif ($sourceOrExt->contentType !== null) {
  222. $type = $sourceOrExt->contentType;
  223. } else {
  224. return;
  225. }
  226. if ($this->_type === null) {
  227. $this->_type = $type;
  228. } elseif ($this->_type !== $type) {
  229. throw new Exception('Content-Type mismatch');
  230. }
  231. }
  232. }