Skip to content
This repository
Browse code

Work on Issue 160, Issue 169, Issue 178, Issue 68

  • Loading branch information...
commit a8f8897fbbb44073dbe9f56cfa79c3874c903b51 1 parent f2ebbeb
Steve Clay authored
1  min/groupsConfig.php
@@ -14,5 +14,4 @@
14 14 return array(
15 15 // 'js' => array('//js/file1.js', '//js/file2.js'),
16 16 // 'css' => array('//css/file1.css', '//css/file2.css'),
17   -
18 17 );
82 min/lib/Minify/Cache/File.php
@@ -11,11 +11,11 @@ public function __construct($path = '', $fileLocking = false)
11 11 if (! $path) {
12 12 require_once 'Solar/Dir.php';
13 13 $path = rtrim(Solar_Dir::tmp(), DIRECTORY_SEPARATOR);
14   - }
  14 + }
15 15 $this->_locking = $fileLocking;
16 16 $this->_path = $path;
17 17 }
18   -
  18 +
19 19 /**
20 20 * Write data to cache.
21 21 *
@@ -27,20 +27,23 @@ public function __construct($path = '', $fileLocking = false)
27 27 */
28 28 public function store($id, $data)
29 29 {
30   - $flag = $this->_locking
31   - ? LOCK_EX
32   - : null;
33   - if (is_file($this->_path . '/' . $id)) {
34   - @unlink($this->_path . '/' . $id);
35   - }
36   - if (! @file_put_contents($this->_path . '/' . $id, $data, $flag)) {
37   - return false;
  30 + $flag = $this->_locking
  31 + ? LOCK_EX
  32 + : null;
  33 + $file = $this->_path . '/' . $id;
  34 + if (is_file($file)) {
  35 + @unlink($file);
  36 + }
  37 + if (! @file_put_contents($file, $data, $flag)) {
  38 + $this->_log("Minify_Cache_File: Write failed to '$file'");
  39 + return false;
  40 + }
  41 + // write control
  42 + if ($data !== $this->fetch($id)) {
  43 + @unlink($file);
  44 + $this->_log("Minify_Cache_File: Post-write read failed for '$file'");
  45 + return false;
38 46 }
39   - // write control
40   - if ($data !== $this->fetch($id)) {
41   - @unlink($file);
42   - return false;
43   - }
44 47 return true;
45 48 }
46 49
@@ -78,15 +81,15 @@ public function isValid($id, $srcMtime)
78 81 */
79 82 public function display($id)
80 83 {
81   - if ($this->_locking) {
82   - $fp = fopen($this->_path . '/' . $id, 'rb');
83   - flock($fp, LOCK_SH);
84   - fpassthru($fp);
85   - flock($fp, LOCK_UN);
86   - fclose($fp);
87   - } else {
88   - readfile($this->_path . '/' . $id);
89   - }
  84 + if ($this->_locking) {
  85 + $fp = fopen($this->_path . '/' . $id, 'rb');
  86 + flock($fp, LOCK_SH);
  87 + fpassthru($fp);
  88 + flock($fp, LOCK_UN);
  89 + fclose($fp);
  90 + } else {
  91 + readfile($this->_path . '/' . $id);
  92 + }
90 93 }
91 94
92 95 /**
@@ -98,15 +101,15 @@ public function display($id)
98 101 */
99 102 public function fetch($id)
100 103 {
101   - if ($this->_locking) {
102   - $fp = fopen($this->_path . '/' . $id, 'rb');
103   - flock($fp, LOCK_SH);
104   - $ret = stream_get_contents($fp);
105   - flock($fp, LOCK_UN);
106   - fclose($fp);
107   - return $ret;
108   - } else {
109   - return file_get_contents($this->_path . '/' . $id);
  104 + if ($this->_locking) {
  105 + $fp = fopen($this->_path . '/' . $id, 'rb');
  106 + flock($fp, LOCK_SH);
  107 + $ret = stream_get_contents($fp);
  108 + flock($fp, LOCK_UN);
  109 + fclose($fp);
  110 + return $ret;
  111 + } else {
  112 + return file_get_contents($this->_path . '/' . $id);
110 113 }
111 114 }
112 115
@@ -119,7 +122,18 @@ public function getPath()
119 122 {
120 123 return $this->_path;
121 124 }
  125 +
  126 + /**
  127 + * Send message to the Minify logger
  128 + * @param string $msg
  129 + * @return null
  130 + */
  131 + protected function _log($msg)
  132 + {
  133 + require_once 'Minify/Logger.php';
  134 + Minify_Logger::log($msg);
  135 + }
122 136
123 137 private $_path = null;
124   - private $_locking = null;
  138 + private $_locking = null;
125 139 }
23 min/lib/Minify/Controller/Base.php
@@ -118,6 +118,8 @@ public function loadMinifier($minifierCallback)
118 118 * be in subdirectories of these directories.
119 119 *
120 120 * @return bool file is safe
  121 + *
  122 + * @deprecated use checkAllowDirs, checkNotHidden instead
121 123 */
122 124 public static function _fileIsSafe($file, $safeDirs)
123 125 {
@@ -135,7 +137,28 @@ public static function _fileIsSafe($file, $safeDirs)
135 137 list($revExt) = explode('.', strrev($base));
136 138 return in_array(strrev($revExt), array('js', 'css', 'html', 'txt'));
137 139 }
  140 +
138 141
  142 + public static function checkAllowDirs($file, $allowDirs, $uri)
  143 + {
  144 + foreach ((array)$allowDirs as $allowDir) {
  145 + if (strpos($file, $allowDir) === 0) {
  146 + return true;
  147 + }
  148 + }
  149 + throw new Exception("File '$file' is outside \$allowDirs. If the path is"
  150 + . " resolved via an alias/symlink, look into the \$min_symlinks option."
  151 + . " E.g. \$min_symlinks = array('/" . dirname($uri) . "' => '" . dirname($file) . "');");
  152 + }
  153 +
  154 + public static function checkNotHidden($file)
  155 + {
  156 + $b = basename($file);
  157 + if (0 === strpos($b, '.')) {
  158 + throw new Exception("Filename '$b' starts with period (may be hidden)");
  159 + }
  160 + }
  161 +
139 162 /**
140 163 * @var array instances of Minify_Source, which provide content and
141 164 * any individual minification needs.
138 min/lib/Minify/Controller/MinApp.php
@@ -34,38 +34,56 @@ public function setupSources($options) {
34 34 );
35 35 unset($options['minApp']);
36 36 $sources = array();
  37 + $this->selectionId = '';
  38 + $missingUri = '';
  39 +
37 40 if (isset($_GET['g'])) {
38   - // try groups
39   - if (! isset($cOptions['groups'][$_GET['g']])) {
40   - $this->log("A group configuration for \"{$_GET['g']}\" was not set");
  41 + // add group(s)
  42 + $this->selectionId .= 'g=' . $_GET['g'];
  43 + $keys = explode(',', $_GET['g']);
  44 + if ($keys != array_unique($keys)) {
  45 + $this->log("Duplicate group key found.");
41 46 return $options;
42 47 }
43   -
44   - $this->selectionId = "g=" . $_GET['g'];
45   - $files = $cOptions['groups'][$_GET['g']];
46   - // if $files is a single object, casting will break it
47   - if (is_object($files)) {
48   - $files = array($files);
49   - } elseif (! is_array($files)) {
50   - $files = (array)$files;
51   - }
52   - foreach ($files as $file) {
53   - if ($file instanceof Minify_Source) {
54   - $sources[] = $file;
55   - continue;
  48 + foreach (explode(',', $_GET['g']) as $key) {
  49 + if (! isset($cOptions['groups'][$key])) {
  50 + $this->log("A group configuration for \"{$key}\" was not found");
  51 + return $options;
56 52 }
57   - if (0 === strpos($file, '//')) {
58   - $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1);
  53 + $files = $cOptions['groups'][$key];
  54 + // if $files is a single object, casting will break it
  55 + if (is_object($files)) {
  56 + $files = array($files);
  57 + } elseif (! is_array($files)) {
  58 + $files = (array)$files;
59 59 }
60   - $file = realpath($file);
61   - if (is_file($file)) {
62   - $sources[] = $this->_getFileSource($file, $cOptions);
63   - } else {
64   - $this->log("The path \"{$file}\" could not be found (or was not a file)");
65   - return $options;
  60 + foreach ($files as $file) {
  61 + if ($file instanceof Minify_Source) {
  62 + $sources[] = $file;
  63 + continue;
  64 + }
  65 + if (0 === strpos($file, '//')) {
  66 + $file = $_SERVER['DOCUMENT_ROOT'] . substr($file, 1);
  67 + }
  68 + $file = realpath($file);
  69 + if ($file && is_file($file)) {
  70 + $sources[] = $this->_getFileSource($file, $cOptions);
  71 + } else {
  72 + $this->log("The path \"{$file}\" could not be found (or was not a file)");
  73 + return $options;
  74 + }
  75 + }
  76 + if ($sources) {
  77 + try {
  78 + $this->checkType($sources[0]);
  79 + } catch (Exception $e) {
  80 + $this->log($e->getMessage());
  81 + return $options;
  82 + }
66 83 }
67 84 }
68   - } elseif (! $cOptions['groupsOnly'] && isset($_GET['f'])) {
  85 + }
  86 + if (! $cOptions['groupsOnly'] && isset($_GET['f'])) {
69 87 // try user files
70 88 // The following restrictions are to limit the URLs that minify will
71 89 // respond to. Ideally there should be only one way to reference a file.
@@ -83,12 +101,17 @@ public function setupSources($options) {
83 101 return $options;
84 102 }
85 103 $ext = ".{$m[1]}";
  104 + try {
  105 + $this->checkType($m[1]);
  106 + } catch (Exception $e) {
  107 + $this->log($e->getMessage());
  108 + return $options;
  109 + }
86 110 $files = explode(',', $_GET['f']);
87 111 if ($files != array_unique($files)) {
88 112 $this->log("Duplicate files specified");
89 113 return $options;
90 114 }
91   -
92 115 if (isset($_GET['b'])) {
93 116 // check for validity
94 117 if (preg_match('@^[^/]+(?:/[^/]+)*$@', $_GET['b'])
@@ -109,22 +132,42 @@ public function setupSources($options) {
109 132 }
110 133 $basenames = array(); // just for cache id
111 134 foreach ($files as $file) {
112   - $path = $_SERVER['DOCUMENT_ROOT'] . $base . $file;
  135 + $uri = $base . $file;
  136 + $path = $_SERVER['DOCUMENT_ROOT'] . $uri;
113 137 $file = realpath($path);
114   - if (false === $file) {
115   - $this->log("Path \"{$path}\" failed realpath()");
116   - return $options;
117   - } elseif (! parent::_fileIsSafe($file, $allowDirs)) {
118   - $this->log("Path \"{$path}\" failed Minify_Controller_Base::_fileIsSafe()");
  138 + if (false === $file || ! is_file($file)) {
  139 + if (! $missingUri) {
  140 + $missingUri = $uri;
  141 + continue;
  142 + } else {
  143 + $this->log("At least two files missing: '$missingUri', '$uri'");
  144 + return $options;
  145 + }
  146 + }
  147 + try {
  148 + parent::checkNotHidden($file);
  149 + parent::checkAllowDirs($file, $allowDirs, $uri);
  150 + } catch (Exception $e) {
  151 + $this->log($e->getMessage());
119 152 return $options;
120   - } else {
121   - $sources[] = $this->_getFileSource($file, $cOptions);
122   - $basenames[] = basename($file, $ext);
123 153 }
  154 + $sources[] = $this->_getFileSource($file, $cOptions);
  155 + $basenames[] = basename($file, $ext);
  156 + }
  157 + if ($this->selectionId) {
  158 + $this->selectionId .= '_f=';
124 159 }
125   - $this->selectionId = implode(',', $basenames) . $ext;
  160 + $this->selectionId .= implode(',', $basenames) . $ext;
126 161 }
127 162 if ($sources) {
  163 + if ($missingUri) {
  164 + array_unshift($sources, new Minify_Source(array(
  165 + 'id' => 'missingFile'
  166 + ,'lastModified' => 0
  167 + ,'content' => "/* Minify: missing file '" . ltrim($missingUri, '/') . "' */\n"
  168 + ,'minifier' => ''
  169 + )));
  170 + }
128 171 $this->sources = $sources;
129 172 } else {
130 173 $this->log("No sources to serve");
@@ -141,4 +184,27 @@ protected function _getFileSource($file, $cOptions)
141 184 }
142 185 return new Minify_Source($spec);
143 186 }
  187 +
  188 + protected $_type = null;
  189 +
  190 + /*
  191 + * Make sure that only source files of a single type are registered
  192 + */
  193 + public function checkType($sourceOrExt)
  194 + {
  195 + if ($sourceOrExt === 'js') {
  196 + $type = Minify::TYPE_JS;
  197 + } elseif ($sourceOrExt === 'css') {
  198 + $type = Minify::TYPE_CSS;
  199 + } elseif ($sourceOrExt->contentType !== null) {
  200 + $type = $sourceOrExt->contentType;
  201 + } else {
  202 + return;
  203 + }
  204 + if ($this->_type === null) {
  205 + $this->_type = $type;
  206 + } elseif ($this->_type !== $type) {
  207 + throw new Exception('Content-Type mismatch');
  208 + }
  209 + }
144 210 }
2  min/lib/Minify/Logger.php
@@ -9,6 +9,8 @@
9 9 *
10 10 * @package Minify
11 11 * @author Stephen Clay <steve@mrclay.org>
  12 + *
  13 + * @todo lose this singleton! pass log object in Minify::serve and distribute to others
12 14 */
13 15 class Minify_Logger {
14 16

0 comments on commit a8f8897

Please sign in to comment.
Something went wrong with that request. Please try again.