Made_Cache - Version 1.4.0

Version Notes

N/A

Download this release

Release Info

Developer Jonathan Selander
Extension Made_Cache
Version 1.4.0
Comparing to
See all releases


Code changes from version 1.1.2 to 1.4.0

app/code/community/Made/Cache/Block/Catalog/Product/List.php CHANGED
@@ -18,6 +18,11 @@ class Made_Cache_Block_Catalog_Product_List extends Mage_Catalog_Block_Product_L
18
  */
19
  public function getProductHtml($product)
20
  {
 
 
 
 
 
21
  $name = 'prod_list_prod_' . $product->getId();
22
  $block = $this->getLayout()->createBlock('cache/catalog_product_list_product')
23
  ->setName($name)
@@ -25,6 +30,8 @@ class Made_Cache_Block_Catalog_Product_List extends Mage_Catalog_Block_Product_L
25
  ->setTemplate('catalog/product/list/product.phtml')
26
  ->setProduct($product);
27
 
28
- return $block->toHtml();
 
 
29
  }
30
  }
18
  */
19
  public function getProductHtml($product)
20
  {
21
+ // Prevent crash within catalog_product_view
22
+ if (($viewedProduct = Mage::registry('product')) !== null) {
23
+ Mage::unregister('product');
24
+ }
25
+
26
  $name = 'prod_list_prod_' . $product->getId();
27
  $block = $this->getLayout()->createBlock('cache/catalog_product_list_product')
28
  ->setName($name)
30
  ->setTemplate('catalog/product/list/product.phtml')
31
  ->setProduct($product);
32
 
33
+ $html = $block->toHtml();
34
+ Mage::register('product', $viewedProduct);
35
+ return $html;
36
  }
37
  }
app/code/community/Made/Cache/Block/Messages.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * When varnish is in front we use ESI and a custom messages getter action
4
+ *
5
+ * @author jonathan@madepeople.se
6
+ */
7
+ class Made_Cache_Block_Messages extends Mage_Core_Block_Messages
8
+ {
9
+ protected function _toHtml()
10
+ {
11
+ if (Mage::helper('cache/varnish')->shouldUse()
12
+ && !$this->getBypassVarnish()) {
13
+ return Mage::helper('cache/varnish')
14
+ ->getEsiTag('madecache/varnish/messages');
15
+ }
16
+
17
+ return parent::_toHtml();
18
+ }
19
+ }
app/code/community/Made/Cache/Block/Profiler.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Block that displays a simple block rendering profiler
4
+ *
5
+ * @package Made_Cache
6
+ * @author info@madepeople.se
7
+ * @copyright Copyright (c) 2012 Made People AB. (http://www.madepeople.se/)
8
+ */
9
+ class Made_Cache_Block_Profiler extends Mage_Core_Block_Abstract
10
+ {
11
+ /**
12
+ * Render basic HTML
13
+ *
14
+ * @return string
15
+ */
16
+ protected function _toHtml()
17
+ {
18
+ $profilerData = Made_Cache_Model_Profiler::getProfilerData();
19
+ if (empty($profilerData)) {
20
+ return;
21
+ }
22
+
23
+ $totalTime = 0;
24
+
25
+ $html = '<table>';
26
+ $html .= '<thead><tr><th>Block</th><th>Time (ms)</th></tr></thead>';
27
+ $html .= '<tbody>';
28
+
29
+ foreach ($profilerData as $blockName => $time) {
30
+ $html .= '<tr>'
31
+ . '<td>' . $blockName . '</td>'
32
+ . '<td>' . intval($time*1000) . '</td>'
33
+ . '</tr>';
34
+
35
+ $totalTime += $time;
36
+ }
37
+
38
+ $html .= '<tr><th>Total time spent rendering uncached blocks:</th>'
39
+ . '<th>' . intval($totalTime*1000) . '</th></tr>';
40
+ $html .= '</tbody>';
41
+ $html .= '</table>';
42
+
43
+ return $html;
44
+ }
45
+ }
app/code/community/Made/Cache/Block/Varnish/Footer.php ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Block that echoes a resource whose purpose is to maintain the cookie for the
4
+ * client. This is needed in order to make sure that the cookie the browser
5
+ * sends actually corresponds to a session within Magento.
6
+ *
7
+ * @package Made_Cache
8
+ * @author info@madepeople.se
9
+ * @copyright Copyright (c) 2012 Made People AB. (http://www.madepeople.se/)
10
+ */
11
+ class Made_Cache_Block_Varnish_Footer extends Mage_Core_Block_Abstract
12
+ {
13
+ /**
14
+ * Generate the script resource AJAX request. This could be a good place
15
+ * to fetch placeholders (such as recently viewed) in a general way,
16
+ * by adding product id data from a cookie to a placeholder element ID,
17
+ * for instance
18
+ *
19
+ * @return string
20
+ */
21
+ protected function _toHtml()
22
+ {
23
+ $scriptUrl = Mage::getUrl('madecache/varnish/cookie');
24
+
25
+ $html = <<<EOF
26
+ <script type="text/javascript">
27
+ new Ajax.Request('$scriptUrl');
28
+ </script>
29
+ EOF;
30
+ return $html;
31
+ }
32
+ }
app/code/community/Made/Cache/Helper/Data.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Contains globally used helper functions
4
+ *
5
+ * @package Made_Cache
6
+ * @author info@madepeople.se
7
+ * @copyright Copyright (c) 2012 Made People AB. (http://www.madepeople.se/)
8
+ */
9
+ class Made_Cache_Helper_Data extends Mage_Core_Helper_Abstract
10
+ {
11
+ /**
12
+ * Flattens array
13
+ *
14
+ * @param type $array
15
+ * @return string
16
+ */
17
+ protected function _flattenArray($array)
18
+ {
19
+ $result = array();
20
+ foreach ($array as $key => $value) {
21
+ if (is_array($value)) {
22
+ $result = array_merge($result, $this->_flattenArray($value));
23
+ }
24
+ else {
25
+ $result[$key] = $value;
26
+ }
27
+ }
28
+ return $result;
29
+ }
30
+
31
+ /**
32
+ * Returns string usable as a cache key part, and takes different
33
+ * datatypes into concern
34
+ *
35
+ * @param mixed $value
36
+ * @return string
37
+ */
38
+ public function paramValueToCacheKey($value)
39
+ {
40
+ if (is_array($value)) {
41
+ $value = implode('_', $this->_flattenArray($value));
42
+ } else if (is_object($value)) {
43
+ $newValue = '';
44
+ foreach ($value->getData() as $dataKey => $dataValue) {
45
+ $newValue = $dataKey . $dataValue;
46
+ }
47
+ $value = $newValue;
48
+ }
49
+
50
+ return $value;
51
+ }
52
+
53
+ /**
54
+ * Used to determine if the current response has notification messages,
55
+ * because if it does, neither the block cache or varnish should keep it.
56
+ *
57
+ * @return boolean
58
+ */
59
+ public function responseHasMessages()
60
+ {
61
+ $layout = Mage::app()->getFrontController()->getAction()
62
+ ->getLayout();
63
+
64
+ foreach (array('global_messages', 'messages') as $blockName) {
65
+ if (($messagesBlock = $layout->getBlock($blockName)) !== false) {
66
+ if ($messagesBlock->getMessageCollection()->count()) {
67
+ return true;
68
+ }
69
+ }
70
+ }
71
+
72
+ return (bool)Mage::getModel('core/message_collection')
73
+ ->count();
74
+ }
75
+ }
app/code/community/Made/Cache/Helper/Varnish.php ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Contains functions related to Varnish
4
+ *
5
+ * Methods from https://www.varnish-software.com/static/book/Cache_invalidation.html
6
+ * are used in order to create a dynamic cache invalidation approach
7
+ *
8
+ * @package Made_Cache
9
+ * @author info@madepeople.se
10
+ * @copyright Copyright (c) 2012 Made People AB. (http://www.madepeople.se/)
11
+ */
12
+ class Made_Cache_Helper_Varnish extends Mage_Core_Helper_Abstract
13
+ {
14
+ /**
15
+ * Determine if varnish is in front of Magento
16
+ *
17
+ * @return boolean
18
+ */
19
+ public function isInFront()
20
+ {
21
+ return !!Mage::app()->getFrontController()
22
+ ->getRequest()
23
+ ->getHeader('X-Varnish');
24
+ }
25
+
26
+ /**
27
+ * Determine if Varnish functions should be used
28
+ *
29
+ * @return boolean
30
+ */
31
+ public function shouldUse()
32
+ {
33
+ return Mage::app()->useCache('varnish')
34
+ && $this->isInFront();
35
+ }
36
+
37
+ /**
38
+ * Returns an array of defined Varnish servers
39
+ * @return type
40
+ */
41
+ public function getServers()
42
+ {
43
+ $serversConfig = explode('\n', Mage::getStoreConfig('cache/varnish/servers'));
44
+ $servers = array();
45
+
46
+ foreach ($serversConfig as $server) {
47
+ $server = trim($server);
48
+
49
+ // Skip new lines
50
+ if (empty($server)) {
51
+ continue;
52
+ }
53
+
54
+ $servers[] = $server;
55
+ }
56
+
57
+ return $servers;
58
+ }
59
+
60
+ /**
61
+ * Flush varnish cache by banning all content
62
+ */
63
+ public function flush()
64
+ {
65
+ return $this->_callVarnish('', 'FLUSH');
66
+ }
67
+
68
+ /**
69
+ * Purge specific object in varnish cache
70
+ *
71
+ * @param string|array $urls
72
+ */
73
+ public function purge($urls)
74
+ {
75
+ return $this->_callVarnish($urls, 'PURGE');
76
+ }
77
+
78
+ /**
79
+ * Refresh specific content in varnish, might be more costly than PURGE
80
+ * because backend is called, but also doesn't invalidate cache if the
81
+ * backend is acting up
82
+ *
83
+ * @param string|array $urls
84
+ */
85
+ public function refresh($urls)
86
+ {
87
+ return $this->_callVarnish($urls, 'REFRESH');
88
+ }
89
+
90
+ /**
91
+ * Send a message to all defined Varnish servers
92
+ *
93
+ * Uses code from magneto-varnish.
94
+ *
95
+ * @see https://github.com/madalinoprea/magneto-varnish/blob/master/code/Varnish/Helper/Data.php#L48
96
+ * @param string|array $urls
97
+ * @param string $type
98
+ * @param array $headers
99
+ */
100
+ protected function _callVarnish($urls, $type = 'PURGE', $headers = array())
101
+ {
102
+ $urls = (array)$urls;
103
+ $servers = $this->getServers();
104
+
105
+ // Init curl handler
106
+ $curlHandlers = array(); // keep references for clean up
107
+ $mh = curl_multi_init();
108
+
109
+ foreach ($servers as $varnishServer) {
110
+ foreach ($urls as $url) {
111
+ $varnishUrl = "http://" . $varnishServer . $url;
112
+
113
+ $ch = curl_init();
114
+ curl_setopt($ch, CURLOPT_URL, $varnishUrl);
115
+ curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type);
116
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
117
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
118
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
119
+
120
+ if (!empty($headers)) {
121
+ curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
122
+ }
123
+
124
+ curl_multi_add_handle($mh, $ch);
125
+ $curlHandlers[] = $ch;
126
+ }
127
+ }
128
+
129
+ $active = null;
130
+ do {
131
+ curl_multi_exec($mh, $active);
132
+ } while ($active);
133
+
134
+ // Error handling and clean up
135
+ $errors = array();
136
+ foreach ($curlHandlers as $ch) {
137
+ $info = curl_getinfo($ch);
138
+
139
+ if (curl_errno($ch)) {
140
+ $errors[] = "Cannot purge url {$info['url']} due to error" . curl_error($ch);
141
+ } else if ($info['http_code'] != 200 && $info['http_code'] != 404) {
142
+ $errors[] = "Cannot purge url {$info['url']}, http code: {$info['http_code']}";
143
+ }
144
+
145
+ curl_multi_remove_handle($mh, $ch);
146
+ curl_close($ch);
147
+ }
148
+ curl_multi_close($mh);
149
+
150
+ return $errors;
151
+ }
152
+
153
+ /**
154
+ * Retreive an ESI tag for the specified URL
155
+ *
156
+ * @param string $url
157
+ */
158
+ public function getEsiTag($url)
159
+ {
160
+ $url = preg_replace('#^/#', '', $url);
161
+ return '<esi:include src="' . Mage::getUrl($url) . '"/>';
162
+ }
163
+
164
+ /**
165
+ * Return a hash of the block layout XML in the current configuration,
166
+ * this is used to identify a unique rendering of the block as we cache
167
+ * all ESI requests
168
+ *
169
+ * @param Mage_Core_Block_Abstract $block
170
+ */
171
+ public function getLayoutHash(Mage_Core_Block_Abstract $block)
172
+ {
173
+ $xml = $block->getLayout()->getNode();
174
+ $doc = new DOMDocument;
175
+ $doc->loadXML($xml->asXML());
176
+ $xpath = new DOMXpath($doc);
177
+ $nodeList = $xpath->query("//block[@name='".$block->getNameInLayout()."']");
178
+ return sha1($doc->saveXML($nodeList->item(0)));
179
+ }
180
+
181
+ /**
182
+ * Helper function that purges the user session cache for cached ESI
183
+ * blocks
184
+ */
185
+ public function purgeUserCache()
186
+ {
187
+ $sessionId = Mage::getSingleton('core/session')->getSessionId();
188
+ if (!empty($sessionId)) {
189
+ $this->_callVarnish('/', 'BAN', array('X-Ban-String: req.url ~ madecache/varnish/(esi|messages) && req.http.X-Session-UUID == ' . $sessionId));
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Retrieve the TTL for the current request
195
+ *
196
+ * @param type $request
197
+ */
198
+ public function getRequestTtl($request)
199
+ {
200
+ if ($request->isPost()) {
201
+ // Never cache POST
202
+ return null;
203
+ }
204
+
205
+ $noCacheRoutes = Mage::getStoreConfig('cache/varnish/nocache_routes');
206
+
207
+ if ($this->_matchRoutesAgainstRequest($noCacheRoutes, $request)
208
+ || $this->_matchRoutesAgainstRequest('madecache/varnish/cookie', $request)) {
209
+ return null;
210
+ }
211
+
212
+ // Messages should only be cached if they are empty
213
+ if ($this->_matchRoutesAgainstRequest('madecache/varnish/messages', $request)
214
+ && Mage::helper('cache')->responseHasMessages()) {
215
+ return null;
216
+ }
217
+
218
+ return Mage::getStoreConfig('cache/varnish/ttl');
219
+ }
220
+
221
+ /**
222
+ * Match routes against the current request for cache exclusion
223
+ *
224
+ * @param array|string $routes
225
+ * @param object $request
226
+ * @return boolean
227
+ */
228
+ protected function _matchRoutesAgainstRequest($routes, $request)
229
+ {
230
+ if (!is_array($routes)) {
231
+ $routes = explode("\n", $routes);
232
+ }
233
+
234
+ foreach ($routes as $key => $handle) {
235
+ if (($handle = trim($handle)) === '') {
236
+ continue;
237
+ }
238
+ $routes[$key] = $handle;
239
+ }
240
+
241
+ if (in_array($request->getModuleName(), $routes)
242
+ || in_array($request->getModuleName() . '/' . $request->getControllerName(), $routes)
243
+ || in_array($request->getModuleName() . '/' . $request->getControllerName() . '/' . $request->getActionName(), $routes))
244
+ {
245
+ return true;
246
+ }
247
+
248
+ return false;
249
+ }
250
+
251
+ }
app/code/community/Made/Cache/Model/Layout.php CHANGED
@@ -29,7 +29,7 @@ class Made_Cache_Model_Layout extends Mage_Core_Model_Layout
29
  * @var int
30
  */
31
  const DEFAULT_CACHE_LIFETIME = 9999999999;
32
-
33
  /**
34
  * Take cache/noache/ESI tags into concern for block rendering
35
  *
@@ -39,6 +39,30 @@ class Made_Cache_Model_Layout extends Mage_Core_Model_Layout
39
  {
40
  parent::generateXml();
41
  $xml = $this->getNode();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
  // Find blocks to cache
44
  $cacheList = $xml->xpath("//cache/*");
@@ -72,7 +96,19 @@ class Made_Cache_Model_Layout extends Mage_Core_Model_Layout
72
  $this->_esiBlocks[$blockName] = array();
73
  }
74
  }
75
-
 
 
 
 
 
 
 
 
 
 
 
 
76
  return $this;
77
  }
78
 
@@ -108,16 +144,17 @@ class Made_Cache_Model_Layout extends Mage_Core_Model_Layout
108
  } else {
109
  $paramKeys = array();
110
  foreach (Mage::app()->getRequest()->getParams() as $key => $value) {
111
- if (is_array($value)) {
112
- $value = implode('_', $value);
113
- } elseif (is_object($value)) {
114
- $newValue = '';
115
- foreach ($value->getData() as $dataKey => $dataValue) {
116
- $newValue = $dataKey . $dataValue;
117
- }
118
- $value = $newValue;
119
- }
120
 
 
121
  $paramKeys[] = $key . $value;
122
  }
123
 
29
  * @var int
30
  */
31
  const DEFAULT_CACHE_LIFETIME = 9999999999;
32
+
33
  /**
34
  * Take cache/noache/ESI tags into concern for block rendering
35
  *
39
  {
40
  parent::generateXml();
41
  $xml = $this->getNode();
42
+ $doc = new DOMDocument;
43
+ $doc->loadXML($xml->asXML());
44
+ $xpath = new DOMXpath($doc);
45
+
46
+ // Aggregate references into one layout tree
47
+ $references = $xpath->query("//reference");
48
+ foreach ($references as $node) {
49
+ $blockName = $node->getAttribute('name');
50
+ $nodeList = $xpath->query("//block[@name='".$blockName."']");
51
+ if ($nodeList->length) {
52
+ $blockNode = $nodeList->item(0);
53
+ if ($node->hasChildNodes()) {
54
+ $childNodes = $xpath->query('*', $node);
55
+ foreach ($childNodes as $child) {
56
+ if ($child->nodeType == XML_ELEMENT_NODE) {
57
+ $blockNode->appendChild($child);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ $node->parentNode->removeChild($node);
63
+ }
64
+ $xml = new Mage_Core_Model_Layout_Element($doc->saveXML());
65
+ $this->setXml($xml);
66
 
67
  // Find blocks to cache
68
  $cacheList = $xml->xpath("//cache/*");
96
  $this->_esiBlocks[$blockName] = array();
97
  }
98
  }
99
+
100
+ // Find eventual noesi tags
101
+ $noEsiList = $xml->xpath("//noesi/*");
102
+ if (count($noEsiList)) {
103
+ foreach ($noEsiList as $node) {
104
+ $blockName = trim((string)$node);
105
+ // Names are unique, an array could hold future settings
106
+ if (isset($this->_esiBlocks[$blockName])) {
107
+ unset($this->_esiBlocks[$blockName]);
108
+ }
109
+ }
110
+ }
111
+
112
  return $this;
113
  }
114
 
144
  } else {
145
  $paramKeys = array();
146
  foreach (Mage::app()->getRequest()->getParams() as $key => $value) {
147
+ // if (is_array($value)) {
148
+ // $value = implode('_', $value);
149
+ // } elseif (is_object($value)) {
150
+ // $newValue = '';
151
+ // foreach ($value->getData() as $dataKey => $dataValue) {
152
+ // $newValue = $dataKey . $dataValue;
153
+ // }
154
+ // $value = $newValue;
155
+ // }
156
 
157
+ $value = Mage::helper('cache')->paramValueToCacheKey($value);
158
  $paramKeys[] = $key . $value;
159
  }
160
 
app/code/community/Made/Cache/Model/Observer.php CHANGED
@@ -8,10 +8,12 @@
8
  */
9
  class Made_Cache_Model_Observer
10
  {
 
 
11
  /**
12
  * Observer that injects cache values into specific blocks, we want
13
  * to do it like this instead of block rewrites to prevent other
14
- * third-party modules to work
15
  *
16
  * @param Varien_Event_Observer $observer
17
  */
@@ -22,12 +24,21 @@ class Made_Cache_Model_Observer
22
  // null lifetime means don't use cache
23
  return;
24
  }
 
 
 
 
 
25
 
26
  switch (true) {
27
  case $block instanceof Mage_Catalog_Block_Product_View:
28
  Mage::getSingleton('cache/observer_catalog')
29
  ->applyProductView($block);
30
  break;
 
 
 
 
31
  case $block instanceof Mage_Catalog_Block_Product_List:
32
  Mage::getSingleton('cache/observer_catalog')
33
  ->applyProductList($block);
@@ -46,6 +57,10 @@ class Made_Cache_Model_Observer
46
  ->applyCartSidebar($block);
47
  break;
48
  }
 
 
 
 
49
  }
50
 
51
  /**
@@ -63,42 +78,6 @@ class Made_Cache_Model_Observer
63
  }
64
  }
65
 
66
- /**
67
- * ESI tags for Varnish, needs the block to have the attribute esi === 1
68
- * as well as Varnish configured with for instance Phoenix_VarnishCache
69
- *
70
- * @param Varien_Event_Observer $observer
71
- */
72
- public function addEsiTag(Varien_Event_Observer $observer)
73
- {
74
- $block = $observer->getEvent()->getBlock();
75
-
76
- // Only return an ESI tag if varnish is in front
77
- if (!$block->getRequest()->getHeader('X-Varnish')) {
78
- return;
79
- }
80
-
81
- if ($block->getData('esi')) {
82
- $layoutHandles = $block->getLayout()->getUpdate()
83
- ->getHandles();
84
-
85
- $esiPath = 'madecache/varnish/esi'
86
- . '/block/' . base64_encode($block->getNameInLayout())
87
- . '/layout/' . base64_encode(join(',', $layoutHandles))
88
- ;
89
-
90
- if (($product = Mage::registry('product')) !== null) {
91
- $esiPath .= '/misc/' . base64_encode(serialize(array(
92
- 'product' => $product->getId()
93
- )));
94
- }
95
-
96
- $html = '<esi:include src="' . Mage::getUrl($esiPath) . '"/>';
97
- $transport = $observer->getEvent()->getTransport();
98
- $transport->setHtml($html);
99
- }
100
- }
101
-
102
  /**
103
  * CatalogRule invalidates cache on product save, so this must be cleaned
104
  *
@@ -123,7 +102,7 @@ class Made_Cache_Model_Observer
123
  */
124
  public function clearQuoteCache(Varien_Event_Observer $observer)
125
  {
126
- // Only work when there is an active quote in the session
127
  $object = $observer->getEvent()->getQuote();
128
  Mage::app()->cleanCache(array('SALES_QUOTE_' . $object->getId()));
129
  }
@@ -132,7 +111,7 @@ class Made_Cache_Model_Observer
132
  * Unset the uenc param for redirection of blocks that have cached links.
133
  * If we don't do this, links redirects to where user X came from when
134
  * the block was cached. This also means that cached return links for
135
- * ESI blocks return a visitor to the ESI-URL, and we can't have that.
136
  *
137
  * @param Varien_Event_Observer $observer
138
  */
@@ -144,7 +123,60 @@ class Made_Cache_Model_Observer
144
  // To this day, the only used encoding type is PARAM_NAME_URL_ENCODED
145
  $key = Mage_Core_Controller_Varien_Action::PARAM_NAME_URL_ENCODED;
146
  if (($param = $request->getParam($key)) !== null) {
147
- $request->setParam($key, null);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
  }
149
  }
150
  }
8
  */
9
  class Made_Cache_Model_Observer
10
  {
11
+ protected $_shouldProfile = null;
12
+
13
  /**
14
  * Observer that injects cache values into specific blocks, we want
15
  * to do it like this instead of block rewrites to prevent other
16
+ * third-party modules from breaking
17
  *
18
  * @param Varien_Event_Observer $observer
19
  */
24
  // null lifetime means don't use cache
25
  return;
26
  }
27
+
28
+ // Allow developers to manipulate block cache data
29
+ Mage::dispatchEvent('made_cache_setup_block_before', array(
30
+ 'block' => $block
31
+ ));
32
 
33
  switch (true) {
34
  case $block instanceof Mage_Catalog_Block_Product_View:
35
  Mage::getSingleton('cache/observer_catalog')
36
  ->applyProductView($block);
37
  break;
38
+ case $block instanceof Mage_Catalog_Block_Category_View:
39
+ Mage::getSingleton('cache/observer_catalog')
40
+ ->applyCategoryView($block);
41
+ break;
42
  case $block instanceof Mage_Catalog_Block_Product_List:
43
  Mage::getSingleton('cache/observer_catalog')
44
  ->applyProductList($block);
57
  ->applyCartSidebar($block);
58
  break;
59
  }
60
+
61
+ Mage::dispatchEvent('made_cache_setup_block_after', array(
62
+ 'block' => $block
63
+ ));
64
  }
65
 
66
  /**
78
  }
79
  }
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  /**
82
  * CatalogRule invalidates cache on product save, so this must be cleaned
83
  *
102
  */
103
  public function clearQuoteCache(Varien_Event_Observer $observer)
104
  {
105
+ // Only runs when there is an active quote in the session
106
  $object = $observer->getEvent()->getQuote();
107
  Mage::app()->cleanCache(array('SALES_QUOTE_' . $object->getId()));
108
  }
111
  * Unset the uenc param for redirection of blocks that have cached links.
112
  * If we don't do this, links redirects to where user X came from when
113
  * the block was cached. This also means that cached return links for
114
+ * ESI blocks return a visitor to the ESI URL, and we can't have that.
115
  *
116
  * @param Varien_Event_Observer $observer
117
  */
123
  // To this day, the only used encoding type is PARAM_NAME_URL_ENCODED
124
  $key = Mage_Core_Controller_Varien_Action::PARAM_NAME_URL_ENCODED;
125
  if (($param = $request->getParam($key)) !== null) {
126
+ // Always remove redirections to Varnish actions
127
+ $paramValue = base64_decode($param);
128
+ if (strstr($paramValue, '/madecache/varnish/') ||
129
+ Mage::getStoreConfig('cache/general/remove_redirect_param')) {
130
+ $request->setParam($key, null);
131
+ }
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Private getter to determine if the profiler should be enabled
137
+ *
138
+ * @return bool
139
+ */
140
+ protected function _getShouldProfile()
141
+ {
142
+ if (is_null($this->_shouldProfile)) {
143
+ $this->_shouldProfile = (bool)Mage::getStoreConfig('cache/general/enable_profiler');
144
+ }
145
+
146
+ return $this->_shouldProfile;
147
+ }
148
+
149
+ /**
150
+ * Start counting the time of rendering an uncached block
151
+ *
152
+ * @param Varien_Event_Observer $observer
153
+ */
154
+ public function profilerStart(Varien_Event_Observer $observer)
155
+ {
156
+ $shouldProfile = $this->_getShouldProfile();
157
+ if ($shouldProfile === true) {
158
+ $blockName = $observer->getEvent()
159
+ ->getBlock()
160
+ ->getNameInLayout();
161
+
162
+ Made_Cache_Model_Profiler::start($blockName);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Stop counting the time of rendering an uncached block
168
+ *
169
+ * @param Varien_Event_Observer $observer
170
+ */
171
+ public function profilerEnd(Varien_Event_Observer $observer)
172
+ {
173
+ $shouldProfile = $this->_getShouldProfile();
174
+ if ($shouldProfile === true) {
175
+ $blockName = $observer->getEvent()
176
+ ->getBlock()
177
+ ->getNameInLayout();
178
+
179
+ Made_Cache_Model_Profiler::end($blockName);
180
  }
181
  }
182
  }
app/code/community/Made/Cache/Model/Observer/Abstract.php CHANGED
@@ -33,6 +33,8 @@ abstract class Made_Cache_Model_Observer_Abstract
33
  $keys = array();
34
  }
35
  $keys[] = $block->getLayout()->getUpdate()->getCacheId();
 
 
36
  return $keys;
37
  }
38
  }
33
  $keys = array();
34
  }
35
  $keys[] = $block->getLayout()->getUpdate()->getCacheId();
36
+ $keys[] = 'SSL_' . intval(!empty($_SERVER['HTTPS']) &&
37
+ $_SERVER['HTTPS'] !== 'off') . '_';
38
  return $keys;
39
  }
40
  }
app/code/community/Made/Cache/Model/Observer/Catalog.php CHANGED
@@ -17,11 +17,9 @@ class Made_Cache_Model_Observer_Catalog
17
  public function applyProductView(Mage_Catalog_Block_Product_View $block)
18
  {
19
  // The "messages" block is session-dependent, don't cache
20
- if (($messagesBlock = $block->getLayout()->getBlock('messages')) !== null) {
21
- if ($messagesBlock->getMessageCollection()->count()) {
22
- $block->setData('cache_lifetime', null);
23
- return;
24
- }
25
  }
26
 
27
  // Cart stuff is session-dependent
@@ -72,6 +70,22 @@ class Made_Cache_Model_Observer_Catalog
72
  ;
73
  }
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  /**
76
  * Product list cache, needs to clear on displayed products
77
  *
@@ -80,11 +94,9 @@ class Made_Cache_Model_Observer_Catalog
80
  public function applyProductList(Mage_Catalog_Block_Product_List $block)
81
  {
82
  // The "messages" block is session-dependent, don't cache
83
- if (($messagesBlock = $block->getLayout()->getBlock('messages')) !== null) {
84
- if ($messagesBlock->getMessageCollection()->count()) {
85
- $block->setData('cache_lifetime', null);
86
- return;
87
- }
88
  }
89
 
90
  // Set cache tags
@@ -94,7 +106,7 @@ class Made_Cache_Model_Observer_Catalog
94
 
95
  // The toolbar needs to apply sort order etc
96
  $productCollection = $block->getLoadedProductCollection();
97
- $_toolbar = new Mage_Catalog_Block_Product_List_Toolbar();
98
  $_toolbar->setCollection($productCollection);
99
 
100
  foreach ($productCollection as $_product) {
@@ -110,12 +122,10 @@ class Made_Cache_Model_Observer_Catalog
110
  $_categoryId = $this->_getCategoryIdForProductList($block);
111
 
112
  foreach (Mage::app()->getRequest()->getParams() as $key => $value) {
113
- if (is_array($value)) {
114
- $value = implode('_', $value);
115
- }
116
  $keys[] = $key . '_' . $value;
117
  }
118
-
119
  $keys = array_merge($keys, array(
120
  $_categoryId,
121
  $_toolbar->getCurrentOrder(),
17
  public function applyProductView(Mage_Catalog_Block_Product_View $block)
18
  {
19
  // The "messages" block is session-dependent, don't cache
20
+ if (Mage::helper('cache')->responseHasMessages()) {
21
+ $block->setData('cache_lifetime', null);
22
+ return;
 
 
23
  }
24
 
25
  // Cart stuff is session-dependent
70
  ;
71
  }
72
 
73
+ /**
74
+ * Make sure that the category view doesn't cache when there are
75
+ * messages in the session
76
+ *
77
+ * @param Mage_Catalog_Block_Category_View $block
78
+ * @return type
79
+ */
80
+ public function applyCategoryView(Mage_Catalog_Block_Category_View $block)
81
+ {
82
+ // The "messages" block is session-dependent, don't cache
83
+ if (Mage::helper('cache')->responseHasMessages()) {
84
+ $block->setData('cache_lifetime', null);
85
+ return;
86
+ }
87
+ }
88
+
89
  /**
90
  * Product list cache, needs to clear on displayed products
91
  *
94
  public function applyProductList(Mage_Catalog_Block_Product_List $block)
95
  {
96
  // The "messages" block is session-dependent, don't cache
97
+ if (Mage::helper('cache')->responseHasMessages()) {
98
+ $block->setData('cache_lifetime', null);
99
+ return;
 
 
100
  }
101
 
102
  // Set cache tags
106
 
107
  // The toolbar needs to apply sort order etc
108
  $productCollection = $block->getLoadedProductCollection();
109
+ $_toolbar = $block->getToolbarBlock();
110
  $_toolbar->setCollection($productCollection);
111
 
112
  foreach ($productCollection as $_product) {
122
  $_categoryId = $this->_getCategoryIdForProductList($block);
123
 
124
  foreach (Mage::app()->getRequest()->getParams() as $key => $value) {
125
+ $value = Mage::helper('cache')->paramValueToCacheKey($value);
 
 
126
  $keys[] = $key . '_' . $value;
127
  }
128
+
129
  $keys = array_merge($keys, array(
130
  $_categoryId,
131
  $_toolbar->getCurrentOrder(),
app/code/community/Made/Cache/Model/Observer/Checkout.php CHANGED
@@ -39,13 +39,9 @@ class Made_Cache_Model_Observer_Checkout
39
  $block->setData('cache_tags', $tags);
40
 
41
  // Set cache keys
42
- $keys = array(
43
- 'BLOCK_TPL',
44
- Mage::app()->getStore()->getCode(),
45
- $block->getTemplateFile(),
46
- 'template' => $block->getTemplate(),
47
- $this->_getQuoteId($block)
48
- );
49
  $block->setData('cache_key', $this->_getCacheKey($keys));
50
  }
51
  }
39
  $block->setData('cache_tags', $tags);
40
 
41
  // Set cache keys
42
+ $keys = $this->_getBasicKeys($block);
43
+ $keys[] = $this->_getQuoteId($block);
44
+
 
 
 
 
45
  $block->setData('cache_key', $this->_getCacheKey($keys));
46
  }
47
  }
app/code/community/Made/Cache/Model/Observer/Cms.php CHANGED
@@ -17,11 +17,9 @@ class Made_Cache_Model_Observer_Cms
17
  public function applyCmsPage(Mage_Cms_Block_Page $block)
18
  {
19
  // The "messages" block is session-dependent, don't cache
20
- if (($messagesBlock = $block->getLayout()->getBlock('messages')) !== null) {
21
- if ($messagesBlock->getMessageCollection()->count()) {
22
- $block->setData('cache_lifetime', null);
23
- return;
24
- }
25
  }
26
 
27
  // Set cache tags
@@ -46,11 +44,9 @@ class Made_Cache_Model_Observer_Cms
46
  public function applyCmsBlock($block)
47
  {
48
  // The "messages" block is session-dependent, don't cache
49
- if (($messagesBlock = $block->getLayout()->getBlock('messages')) !== null) {
50
- if ($messagesBlock->getMessageCollection()->count()) {
51
- $block->setData('cache_lifetime', null);
52
- return;
53
- }
54
  }
55
 
56
  // Set cache tags
17
  public function applyCmsPage(Mage_Cms_Block_Page $block)
18
  {
19
  // The "messages" block is session-dependent, don't cache
20
+ if (Mage::helper('cache')->responseHasMessages()) {
21
+ $block->setData('cache_lifetime', null);
22
+ return;
 
 
23
  }
24
 
25
  // Set cache tags
44
  public function applyCmsBlock($block)
45
  {
46
  // The "messages" block is session-dependent, don't cache
47
+ if (Mage::helper('cache')->responseHasMessages()) {
48
+ $block->setData('cache_lifetime', null);
49
+ return;
 
 
50
  }
51
 
52
  // Set cache tags
app/code/community/Made/Cache/Model/Profiler.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Model for keeping track of time spent rendering uncached blocks
4
+ *
5
+ * @package Made_Cache
6
+ * @author info@madepeople.se
7
+ * @copyright Copyright (c) 2012 Made People AB. (http://www.madepeople.se/)
8
+ */
9
+ class Made_Cache_Model_Profiler
10
+ {
11
+ /**
12
+ * Holds both calculated and raw profiler data
13
+ *
14
+ * @var array
15
+ */
16
+ private static $_profilerData = array(
17
+ 'raw' => array(),
18
+ 'calculated' => array()
19
+ );
20
+
21
+ /**
22
+ * Starting profiler timestamp
23
+ *
24
+ * @param string $key
25
+ * @return void
26
+ */
27
+ public static function start($key)
28
+ {
29
+ if (empty($key)) {
30
+ return;
31
+ }
32
+
33
+ self::$_profilerData['raw'][$key]['start'] = microtime(true);
34
+ }
35
+
36
+ /**
37
+ * Ending profiler timestamp
38
+ *
39
+ * @param string $key
40
+ * @return void
41
+ */
42
+ public static function end($key)
43
+ {
44
+ if (empty($key)) {
45
+ return;
46
+ }
47
+
48
+ self::$_profilerData['raw'][$key]['end'] = microtime(true);
49
+ self::$_profilerData['calculated'][$key] =
50
+ self::$_profilerData['raw'][$key]['end'] -
51
+ self::$_profilerData['raw'][$key]['start'];
52
+ }
53
+
54
+ /**
55
+ * Return sorted profiler data, longest time first
56
+ *
57
+ * @return array
58
+ */
59
+ public static function getProfilerData()
60
+ {
61
+ $profilerData = self::$_profilerData['calculated'];
62
+ arsort($profilerData);
63
+ return $profilerData;
64
+ }
65
+ }
app/code/community/Made/Cache/Model/VarnishObserver.php ADDED
@@ -0,0 +1,304 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Manage tag-specific cache cleaning
4
+ *
5
+ * @package Made_Cache
6
+ * @author info@madepeople.se
7
+ * @copyright Copyright (c) 2012 Made People AB. (http://www.madepeople.se/)
8
+ */
9
+ class Made_Cache_Model_VarnishObserver
10
+ {
11
+ /**
12
+ * Simple, default is 0 expiry meaning no caching. For every action that
13
+ * requires caching we add it explicitly. Static content is not within
14
+ * this scope.
15
+ *
16
+ * @param Varien_Event_Observer $observer
17
+ */
18
+ public function initializeResponseHeaders(Varien_Event_Observer $observer)
19
+ {
20
+ // Only manipulate headers if Varnish is in front
21
+ if (!Mage::helper('cache/varnish')->shouldUse()) {
22
+ return;
23
+ }
24
+
25
+ $response = $observer->getEvent()
26
+ ->getControllerAction()
27
+ ->getResponse();
28
+
29
+ if (Mage::getStoreConfig('cache/varnish/debug')) {
30
+ $response->setHeader('X-Made-Cache-Debug', 1);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Set TTL for varnish. Since we use ESI we only need to offer the
36
+ * simple possibility to blacklist per request route.
37
+ *
38
+ * @param Varien_Event_Observer $observer
39
+ */
40
+ public function setVarnishCacheHeaders(Varien_Event_Observer $observer)
41
+ {
42
+ // Only manipulate headers if Varnish is in front or if there isn't
43
+ // a messages block in the layout
44
+ if (!Mage::helper('cache/varnish')->shouldUse()) {
45
+ return;
46
+ }
47
+
48
+ $controller = $observer->getEvent()->getControllerAction();
49
+ $ttl = Mage::helper('cache/varnish')->getRequestTtl($controller->getRequest());
50
+ if (empty($ttl)) {
51
+ return;
52
+ }
53
+
54
+ $response = $controller->getResponse();
55
+ $response->setHeader('X-Made-Cache-Ttl', $ttl, true);
56
+ }
57
+
58
+ /**
59
+ * Add 'varnish_enabled' to the list of layout handles, so developers
60
+ * can target layout XML entries for when varnish is in front
61
+ *
62
+ * @param Varien_Event_Observer $observer
63
+ * @return void
64
+ */
65
+ public function addLayoutHandle(Varien_Event_Observer $observer)
66
+ {
67
+ if (!Mage::helper('cache/varnish')->shouldUse()) {
68
+ return;
69
+ }
70
+
71
+ $observer->getEvent()->getControllerAction()
72
+ ->getLayout()
73
+ ->getUpdate()
74
+ ->addHandle('varnish_enabled');
75
+ }
76
+
77
+ /**
78
+ * ESI tags for Varnish, needs the block to have the attribute esi === 1
79
+ * as well as Varnish configured with for instance Phoenix_VarnishCache
80
+ *
81
+ * @param Varien_Event_Observer $observer
82
+ */
83
+ public function addEsiTag(Varien_Event_Observer $observer)
84
+ {
85
+ if (!Mage::helper('cache/varnish')->shouldUse()) {
86
+ return;
87
+ }
88
+
89
+ $block = $observer->getEvent()->getBlock();
90
+
91
+ if ($block->getData('esi')) {
92
+ $layoutHandles = $block->getLayout()->getUpdate()
93
+ ->getHandles();
94
+
95
+ $esiPath = 'madecache/varnish/esi'
96
+ . '/hash/' . Mage::helper('cache/varnish')->getLayoutHash($block)
97
+ . '/block/' . base64_encode($block->getNameInLayout())
98
+ . '/layout/' . base64_encode(join(',', $layoutHandles))
99
+ ;
100
+
101
+ if (($product = Mage::registry('product')) !== null) {
102
+ $esiPath .= '/misc/' . base64_encode(serialize(array(
103
+ 'product' => $product->getId()
104
+ )));
105
+ }
106
+
107
+ $html = Mage::helper('cache/varnish')->getEsiTag($esiPath);
108
+ $transport = $observer->getEvent()->getTransport();
109
+ $transport->setHtml($html);
110
+ }
111
+ }
112
+
113
+ /**
114
+ * Clear user-specific cache when at a non-cachable request because these
115
+ * are what modify the session
116
+ *
117
+ * @param Varien_Event_Observer $observer
118
+ */
119
+ public function purgeUserCache(Varien_Event_Observer $observer)
120
+ {
121
+ // Only manipulate headers if Varnish is in front or if there isn't
122
+ // a messages block in the layout
123
+ if (!Mage::helper('cache/varnish')->shouldUse()) {
124
+ return;
125
+ }
126
+
127
+ $controller = $observer->getEvent()->getControllerAction();
128
+ $request = $controller->getRequest();
129
+ $ttl = Mage::helper('cache/varnish')->getRequestTtl($request);
130
+ if (!empty($ttl)) {
131
+ // Only purge for routes that don't cache
132
+ return;
133
+ }
134
+
135
+ if ($request->getModuleName() === 'madecache'
136
+ && $request->getControllerName() === 'varnish') {
137
+ // It's stupid for ESI requests to clear themselves
138
+ return;
139
+ }
140
+
141
+ Mage::helper('cache/varnish')->purgeUserCache();
142
+ }
143
+
144
+ /**
145
+ * Purge cache in Varnish including entity cache such as products,
146
+ * categories and CMS pages by doing lookups in the rewrite table
147
+ *
148
+ * Uses code from magneto-varnish.
149
+ *
150
+ * @see https://github.com/madalinoprea/magneto-varnish/blob/master/code/Varnish/Model/Observer.php#L65
151
+ * @param Varien_Event_Observer $observer
152
+ */
153
+ public function purge(Varien_Event_Observer $observer)
154
+ {
155
+ if (!Mage::helper('cache/varnish')->shouldUse()) {
156
+ return;
157
+ }
158
+
159
+ $tags = $observer->getTags();
160
+ if (empty($tags)) {
161
+ $errors = Mage::helper('cache/varnish')->flush();
162
+ if (!empty($errors)) {
163
+ Mage::getSingleton('adminhtml/session')->addError("Varnish Purge failed");
164
+ } else {
165
+ Mage::getSingleton('adminhtml/session')->addSuccess("The Varnish cache storage has been flushed.");
166
+ }
167
+ return;
168
+ }
169
+
170
+ $urls = array();
171
+
172
+ // Compute the urls for affected entities
173
+ foreach ((array)$tags as $tag) {
174
+ $tag_fields = explode('_', $tag);
175
+ if (count($tag_fields) === 3) {
176
+ if ($tag_fields[1] == 'product') {
177
+ // Get urls for product
178
+ $product = Mage::getModel('catalog/product')->load($tag_fields[2]);
179
+ $urls = array_merge($urls, $this->_getUrlsForProduct($product));
180
+ } elseif ($tag_fields[1] == 'category') {
181
+ $category = Mage::getModel('catalog/category')->load($tag_fields[2]);
182
+ $category_urls = $this->_getUrlsForCategory($category);
183
+ $urls = array_merge($urls, $category_urls);
184
+ } elseif ($tag_fields[1] == 'page') {
185
+ $urls = $this->_getUrlsForCmsPage($tag_fields[2]);
186
+ }
187
+ }
188
+ }
189
+
190
+ // Transform urls to relative urls
191
+ $relativeUrls = array();
192
+ foreach ($urls as $url) {
193
+ $relativeUrls[] = parse_url($url, PHP_URL_PATH);
194
+ }
195
+
196
+ if (!empty($relativeUrls)) {
197
+ $relativeUrls = array_unique($relativeUrls);
198
+ $errors = Mage::helper('cache/varnish')->purge($relativeUrls);
199
+ if (!empty($errors)) {
200
+ Mage::getSingleton('adminhtml/session')->addError(
201
+ "Some Varnish purges failed: <br/>" . implode("<br/>", $errors));
202
+ } else {
203
+ Mage::getSingleton('adminhtml/session')->addSuccess(
204
+ "The following URLs have been cleared from Varnish: <br/>&nbsp;&nbsp;" . implode(", ", $relativeUrls));
205
+ }
206
+ }
207
+
208
+ return $this;
209
+ }
210
+
211
+ /**
212
+ * Returns all the urls related to product
213
+ *
214
+ * Uses code from magneto-varnish.
215
+ *
216
+ * @see https://github.com/madalinoprea/magneto-varnish/blob/master/code/Varnish/Model/Observer.php#L133
217
+ * @param Mage_Catalog_Model_Product $product
218
+ */
219
+ protected function _getUrlsForProduct($product){
220
+ $urls = array();
221
+
222
+ $store_id = $product->getStoreId();
223
+
224
+ $routePath = 'catalog/product/view';
225
+ $routeParams['id'] = $product->getId();
226
+ $routeParams['s'] = $product->getUrlKey();
227
+ $routeParams['_store'] = (!$store_id ? 1: $store_id);
228
+ $url = Mage::getUrl($routePath, $routeParams);
229
+ $urls[] = $url;
230
+
231
+ // Collect all rewrites
232
+ $rewrites = Mage::getModel('core/url_rewrite')->getCollection();
233
+ if (!Mage::getConfig('catalog/seo/product_use_categories')) {
234
+ $rewrites->getSelect()
235
+ ->where("id_path = 'product/{$product->getId()}'");
236
+ } else {
237
+ // Also show full links with categories
238
+ $rewrites->getSelect()
239
+ ->where("id_path = 'product/{$product->getId()}' OR id_path like 'product/{$product->getId()}/%'");
240
+ }
241
+ foreach ($rewrites as $r) {
242
+ unset($routeParams);
243
+ $routePath = '';
244
+ $routeParams['_direct'] = $r->getRequestPath();
245
+ $routeParams['_store'] = $r->getStoreId();
246
+ $url = Mage::getUrl($routePath, $routeParams);
247
+ $urls[] = $url;
248
+ }
249
+
250
+ return $urls;
251
+ }
252
+
253
+ /**
254
+ * Returns all the urls pointing to the category
255
+ *
256
+ * Uses code from magneto-varnish.
257
+ *
258
+ * @see https://github.com/madalinoprea/magneto-varnish/blob/master/code/Varnish/Model/Observer.php#L171
259
+ */
260
+ protected function _getUrlsForCategory($category) {
261
+ $urls = array();
262
+ $routePath = 'catalog/category/view';
263
+
264
+ $store_id = $category->getStoreId();
265
+ $routeParams['id'] = $category->getId();
266
+ $routeParams['s'] = $category->getUrlKey();
267
+ $routeParams['_store'] = (!$store_id ? 1 : $store_id); # Default store id is 1
268
+ $url = Mage::getUrl($routePath, $routeParams);
269
+ $urls[] = $url;
270
+
271
+ // Collect all rewrites
272
+ $rewrites = Mage::getModel('core/url_rewrite')->getCollection();
273
+ $rewrites->getSelect()->where("id_path = 'category/{$category->getId()}'");
274
+ foreach ($rewrites as $r) {
275
+ unset($routeParams);
276
+ $routePath = '';
277
+ $routeParams['_direct'] = $r->getRequestPath();
278
+ $routeParams['_store'] = $r->getStoreId();
279
+ $routeParams['_nosid'] = True;
280
+ $url = Mage::getUrl($routePath, $routeParams);
281
+ $urls[] = $url;
282
+ }
283
+
284
+ return $urls;
285
+ }
286
+
287
+ /**
288
+ * Returns all urls related to this cms page
289
+ *
290
+ * Uses code from magneto-varnish.
291
+ *
292
+ * @see https://github.com/madalinoprea/magneto-varnish/blob/master/code/Varnish/Model/Observer.php#L201
293
+ */
294
+ protected function _getUrlsForCmsPage($cmsPageId)
295
+ {
296
+ $urls = array();
297
+ $page = Mage::getModel('cms/page')->load($cmsPageId);
298
+ if ($page->getId()) {
299
+ $urls[] = '/' . $page->getIdentifier();
300
+ }
301
+
302
+ return $urls;
303
+ }
304
+ }
app/code/community/Made/Cache/controllers/VarnishController.php CHANGED
@@ -42,4 +42,47 @@ class Made_Cache_VarnishController extends Mage_Core_Controller_Front_Action
42
  $this->getResponse()->setBody($block->toHtml());
43
  }
44
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  }
42
  $this->getResponse()->setBody($block->toHtml());
43
  }
44
  }
45
+
46
+ /**
47
+ * Render all messages from currently used session namespaces
48
+ *
49
+ * @return void
50
+ */
51
+ public function messagesAction()
52
+ {
53
+ if (empty($_SESSION)) {
54
+ return;
55
+ }
56
+
57
+ $messagesBlock = $this->getLayout()
58
+ ->createBlock('core/messages', 'messages')
59
+ ->setBypassVarnish(true);
60
+
61
+ foreach ($_SESSION as $contents) {
62
+ if (isset($contents['messages']) &&
63
+ $contents['messages'] instanceof Mage_Core_Model_Message_Collection) {
64
+ if (!$contents['messages']->count()) {
65
+ continue;
66
+ }
67
+ $messagesBlock->addMessages($contents['messages']);
68
+ $contents['messages']->clear();
69
+ }
70
+ }
71
+
72
+ if (!$messagesBlock->getMessageCollection()->count()) {
73
+ return;
74
+ }
75
+
76
+ $this->getResponse()
77
+ ->setBody($messagesBlock->toHtml());
78
+ }
79
+
80
+ /**
81
+ * Simply make sure that the session is valid
82
+ *
83
+ * @see Made_Cache_Block_Footer
84
+ */
85
+ public function cookieAction()
86
+ {
87
+ }
88
  }
app/code/community/Made/Cache/etc/config.xml CHANGED
@@ -9,10 +9,24 @@
9
  <config>
10
  <modules>
11
  <Made_Cache>
12
- <version>1.1.2</version>
13
  </Made_Cache>
14
  </modules>
15
  <global>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  <models>
17
  <cache>
18
  <class>Made_Cache_Model</class>
@@ -27,8 +41,31 @@
27
  <cache>
28
  <class>Made_Cache_Block</class>
29
  </cache>
 
 
 
 
 
30
  </blocks>
31
  <events>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  <controller_action_predispatch>
33
  <observers>
34
  <remove_uenc_param>
@@ -47,15 +84,6 @@
47
  </cache_setup_block>
48
  </observers>
49
  </core_block_abstract_to_html_before>
50
- <core_block_abstract_to_html_after>
51
- <observers>
52
- <cache_add_esi_tags>
53
- <type>singleton</type>
54
- <class>cache/observer</class>
55
- <method>addEsiTag</method>
56
- </cache_add_esi_tags>
57
- </observers>
58
- </core_block_abstract_to_html_after>
59
  <sales_quote_save_after>
60
  <observers>
61
  <quote_save_after>
@@ -112,5 +140,106 @@
112
  </Made_Cache>
113
  </modules>
114
  </translate>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
  </frontend>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  </config>
9
  <config>
10
  <modules>
11
  <Made_Cache>
12
+ <version>1.4.0</version>
13
  </Made_Cache>
14
  </modules>
15
  <global>
16
+ <cache>
17
+ <types>
18
+ <varnish translate="label,description" module="cache">
19
+ <label>Varnish</label>
20
+ <description><![CDATA[Full page cache. Requires a configured Varnish daemon, see https://www.varnish-cache.org/]]></description>
21
+ <tags/>
22
+ </varnish>
23
+ </types>
24
+ </cache>
25
+ <helpers>
26
+ <cache>
27
+ <class>Made_Cache_Helper</class>
28
+ </cache>
29
+ </helpers>
30
  <models>
31
  <cache>
32
  <class>Made_Cache_Model</class>
41
  <cache>
42
  <class>Made_Cache_Block</class>
43
  </cache>
44
+ <core>
45
+ <rewrite>
46
+ <messages>Made_Cache_Block_Messages</messages>
47
+ </rewrite>
48
+ </core>
49
  </blocks>
50
  <events>
51
+ <adminhtml_cache_flush_all>
52
+ <observers>
53
+ <varnish_purge>
54
+ <type>singleton</type>
55
+ <class>cache/varnishObserver</class>
56
+ <method>purge</method>
57
+ </varnish_purge>
58
+ </observers>
59
+ </adminhtml_cache_flush_all>
60
+ <application_clean_cache>
61
+ <observers>
62
+ <varnish_purge>
63
+ <type>singleton</type>
64
+ <class>cache/varnishObserver</class>
65
+ <method>purge</method>
66
+ </varnish_purge>
67
+ </observers>
68
+ </application_clean_cache>
69
  <controller_action_predispatch>
70
  <observers>
71
  <remove_uenc_param>
84
  </cache_setup_block>
85
  </observers>
86
  </core_block_abstract_to_html_before>
 
 
 
 
 
 
 
 
 
87
  <sales_quote_save_after>
88
  <observers>
89
  <quote_save_after>
140
  </Made_Cache>
141
  </modules>
142
  </translate>
143
+ <events>
144
+ <controller_action_predispatch>
145
+ <observers>
146
+ <add_layout_handle>
147
+ <type>singleton</type>
148
+ <class>cache/varnishObserver</class>
149
+ <method>addLayoutHandle</method>
150
+ </add_layout_handle>
151
+ </observers>
152
+ </controller_action_predispatch>
153
+ <controller_action_predispatch>
154
+ <observers>
155
+ <clear_cache_headers>
156
+ <type>singleton</type>
157
+ <class>cache/varnishObserver</class>
158
+ <method>initializeResponseHeaders</method>
159
+ </clear_cache_headers>
160
+ </observers>
161
+ </controller_action_predispatch>
162
+ <controller_action_postdispatch>
163
+ <observers>
164
+ <set_varnish_headers>
165
+ <type>singleton</type>
166
+ <class>cache/varnishObserver</class>
167
+ <method>setVarnishCacheHeaders</method>
168
+ </set_varnish_headers>
169
+ <purge_user_cache>
170
+ <type>singleton</type>
171
+ <class>cache/varnishObserver</class>
172
+ <method>purgeUserCache</method>
173
+ </purge_user_cache>
174
+ </observers>
175
+ </controller_action_postdispatch>
176
+ <core_block_abstract_to_html_after>
177
+ <observers>
178
+ <cache_add_esi_tags>
179
+ <type>singleton</type>
180
+ <class>cache/varnishObserver</class>
181
+ <method>addEsiTag</method>
182
+ </cache_add_esi_tags>
183
+ <cache_profiler_end>
184
+ <type>singleton</type>
185
+ <class>cache/observer</class>
186
+ <method>profilerEnd</method>
187
+ </cache_profiler_end>
188
+ </observers>
189
+ </core_block_abstract_to_html_after>
190
+ <core_block_abstract_to_html_before>
191
+ <observers>
192
+ <cache_profiler_start>
193
+ <type>singleton</type>
194
+ <class>cache/observer</class>
195
+ <method>profilerStart</method>
196
+ </cache_profiler_start>
197
+ </observers>
198
+ </core_block_abstract_to_html_before>
199
+ </events>
200
  </frontend>
201
+ <adminhtml>
202
+ <acl>
203
+ <resources>
204
+ <admin>
205
+ <children>
206
+ <system>
207
+ <children>
208
+ <config>
209
+ <children>
210
+ <cache>
211
+ <title>Made Cache Section</title>
212
+ </cache>
213
+ </children>
214
+ </config>
215
+ </children>
216
+ </system>
217
+ </children>
218
+ </admin>
219
+ </resources>
220
+ </acl>
221
+ </adminhtml>
222
+ <default>
223
+ <cache>
224
+ <general>
225
+ <enable_profiler>0</enable_profiler>
226
+ <remove_redirect_param>0</remove_redirect_param>
227
+ </general>
228
+ <varnish>
229
+ <ttl>1w</ttl>
230
+ <nocache_routes><![CDATA[customer
231
+ checkout
232
+ streamcheckout
233
+ onestepcheckout
234
+ ajaxcart
235
+ catalog/product_compare
236
+ wishlist
237
+ moneybookers
238
+ paypal]]>
239
+ </nocache_routes>
240
+ <servers>127.0.0.1</servers>
241
+ <debug>0</debug>
242
+ </varnish>
243
+ </cache>
244
+ </default>
245
  </config>
app/code/community/Made/Cache/etc/magento.vcl CHANGED
@@ -1,98 +1,88 @@
1
- # This is a basic VCL configuration file for PageCache powered by Varnish for Magento module.
2
- #
3
- # Modifications for use with ESI tags and Made_Cache
4
- #
5
 
6
- # default backend definition. Set this to point to your content server.
7
  backend default {
8
- .host = "127.0.0.1";
9
- .port = "9000";
10
  }
11
 
12
- # admin backend with longer timeout values. Set this to the same IP & port as your default server.
13
  backend admin {
14
- .host = "127.0.0.1";
15
- .port = "9000";
16
- .first_byte_timeout = 18000s;
17
- .between_bytes_timeout = 18000s;
18
  }
19
 
20
- # add your Magento server IP to allow purges from the backend
21
  acl purge {
22
- "localhost";
23
- "127.0.0.1";
24
  }
25
 
26
  sub vcl_recv {
27
- if (req.restarts == 0) {
28
- if (req.http.x-forwarded-for) {
29
- set req.http.X-Forwarded-For =
30
- req.http.X-Forwarded-For + ", " + client.ip;
31
- } else {
32
- set req.http.X-Forwarded-For = client.ip;
33
  }
 
34
  }
35
-
36
- if (req.request != "GET" &&
37
- req.request != "HEAD" &&
38
- req.request != "PUT" &&
39
- req.request != "POST" &&
40
- req.request != "TRACE" &&
41
- req.request != "OPTIONS" &&
42
- req.request != "DELETE" &&
43
- req.request != "PURGE") {
44
- /* Non-RFC2616 or CONNECT which is weird. */
45
- return (pipe);
46
- }
47
-
48
- # purge request
49
- if (req.request == "PURGE") {
50
  if (!client.ip ~ purge) {
51
  error 405 "Not allowed.";
52
  }
53
- ban("obj.http.X-Purge-Host ~ " + req.http.X-Purge-Host + " && obj.http.X-Purge-URL ~ " + req.http.X-Purge-Regex + " && obj.http.Content-Type ~ " + req.http.X-Purge-Content-Type);
54
- error 200 "Purged.";
 
 
 
 
 
 
 
55
  }
56
 
57
- # switch to admin backend configuration
58
- if (req.http.cookie ~ "adminhtml=") {
59
- set req.backend = admin;
 
 
 
 
 
 
60
  }
61
 
62
- # we only deal with GET and HEAD by default
63
- if (req.request != "GET" && req.request != "HEAD") {
64
- return (pass);
 
 
 
 
65
  }
66
-
67
- # normalize url in case of leading HTTP scheme and domain
68
- set req.url = regsub(req.url, "^http[s]?://[^/]+", "");
69
-
70
- # static files are always cacheable. remove SSL flag and cookie
71
- if (req.url ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico)$") {
72
- unset req.http.Https;
73
- unset req.http.Cookie;
74
  }
75
 
76
- # not cacheable by default
77
- if (req.http.Authorization || req.http.Https) {
78
- return (pass);
 
79
  }
80
 
81
- # do not cache any page from
82
- # - index files
83
- # - ...
84
- if (req.url ~ "^/(index)") {
85
  return (pass);
86
  }
87
 
88
- # as soon as we have a NO_CACHE cookie pass request
89
- # Modification for Made_Cache, ideally we should only disable cache per
90
- # specific ruote
91
- #if (req.http.cookie ~ "NO_CACHE=") {
92
- # return (pass);
93
- #}
94
-
95
- # normalize Aceept-Encoding header
96
  # http://varnish.projects.linpro.no/wiki/FAQ/Compression
97
  if (req.http.Accept-Encoding) {
98
  if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
@@ -107,149 +97,93 @@ sub vcl_recv {
107
  remove req.http.Accept-Encoding;
108
  }
109
  }
110
-
111
- # remove Google gclid parameters
112
- set req.url = regsuball(req.url,"\?gclid=[^&]+$",""); # strips when QS = "?gclid=AAA"
113
- set req.url = regsuball(req.url,"\?gclid=[^&]+&","?"); # strips when QS = "?gclid=AAA&foo=bar"
114
- set req.url = regsuball(req.url,"&gclid=[^&]+",""); # strips when QS = "?foo=bar&gclid=AAA" or QS = "?foo=bar&gclid=AAA&bar=baz"
115
-
116
  return (lookup);
117
  }
118
 
119
- # sub vcl_pipe {
120
- # # Note that only the first request to the backend will have
121
- # # X-Forwarded-For set. If you use X-Forwarded-For and want to
122
- # # have it set for all requests, make sure to have:
123
- # # set bereq.http.connection = "close";
124
- # # here. It is not set by default as it might break some broken web
125
- # # applications, like IIS with NTLM authentication.
126
- # return (pipe);
127
- # }
128
- #
129
- # sub vcl_pass {
130
- # return (pass);
131
- # }
132
- #
133
  sub vcl_hash {
134
- hash_data(req.url);
 
 
 
 
 
 
 
 
 
 
 
 
135
  if (req.http.host) {
136
  hash_data(req.http.host);
137
  } else {
138
  hash_data(server.ip);
139
  }
140
- if (!(req.url ~ "^/(media|js|skin)/.*\.(png|jpg|jpeg|gif|css|js|swf|ico)$")) {
141
- call design_exception;
142
- }
143
  return (hash);
144
  }
145
- #
146
- # sub vcl_hit {
147
- # return (deliver);
148
- # }
149
- #
150
- # sub vcl_miss {
151
- # return (fetch);
152
- # }
153
 
154
- sub vcl_fetch {
155
- if (req.url ~ "/madecache/varnish/") {
156
- return (hit_for_pass);
 
157
  }
 
 
 
 
 
 
 
 
158
 
159
- if (beresp.status == 500) {
160
- set beresp.saintmode = 10s;
161
- return (restart);
 
 
162
  }
163
- set beresp.grace = 5m;
164
 
165
- # add ban-lurker tags to object
166
- set beresp.http.X-Purge-URL = req.url;
167
- set beresp.http.X-Purge-Host = req.http.host;
168
-
169
  if (beresp.status == 200 || beresp.status == 301 || beresp.status == 404) {
170
  if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/xml") {
171
  set beresp.do_esi = true;
172
- if ((beresp.http.Set-Cookie ~ "NO_CACHE=") || (beresp.ttl < 1s)) {
173
- set beresp.ttl = 0s;
174
- return (hit_for_pass);
175
- }
176
-
177
- # marker for vcl_deliver to reset Age:
178
- set beresp.http.magicmarker = "1";
179
-
180
- # Don't cache cookies
181
- unset beresp.http.set-cookie;
182
  } else {
183
- # set default TTL value for static content
184
- set beresp.ttl = 4h;
185
  }
 
 
 
 
 
 
 
 
 
186
  return (deliver);
187
  }
188
-
 
189
  return (hit_for_pass);
190
  }
191
 
192
  sub vcl_deliver {
193
- # debug info
194
- if (resp.http.X-Cache-Debug) {
195
- if (obj.hits > 0) {
196
- set resp.http.X-Cache = "HIT";
197
- set resp.http.X-Cache-Hits = obj.hits;
198
- } else {
199
- set resp.http.X-Cache = "MISS";
200
- }
201
- set resp.http.X-Cache-Expires = resp.http.Expires;
202
- } else {
203
- # remove Varnish/proxy header
204
- remove resp.http.X-Varnish;
205
- remove resp.http.Via;
206
- remove resp.http.Age;
207
- remove resp.http.X-Purge-URL;
208
- remove resp.http.X-Purge-Host;
209
- }
210
-
211
- if (resp.http.magicmarker) {
212
- # Remove the magic marker
213
- unset resp.http.magicmarker;
214
-
215
- set resp.http.Cache-Control = "no-store, no-cache, must-revalidate, post-check=0, pre-check=0";
216
- set resp.http.Pragma = "no-cache";
217
- set resp.http.Expires = "Mon, 31 Mar 2008 10:00:00 GMT";
218
- set resp.http.Age = "0";
219
  }
220
- }
221
 
222
- # sub vcl_error {
223
- # set obj.http.Content-Type = "text/html; charset=utf-8";
224
- # set obj.http.Retry-After = "5";
225
- # synthetic {"
226
- # <?xml version="1.0" encoding="utf-8"?>
227
- # <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
228
- # "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
229
- # <html>
230
- # <head>
231
- # <title>"} + obj.status + " " + obj.response + {"</title>
232
- # </head>
233
- # <body>
234
- # <h1>Error "} + obj.status + " " + obj.response + {"</h1>
235
- # <p>"} + obj.response + {"</p>
236
- # <h3>Guru Meditation:</h3>
237
- # <p>XID: "} + req.xid + {"</p>
238
- # <hr>
239
- # <p>Varnish cache server</p>
240
- # </body>
241
- # </html>
242
- # "};
243
- # return (deliver);
244
- # }
245
- #
246
- # sub vcl_init {
247
- # return (ok);
248
- # }
249
- #
250
- # sub vcl_fini {
251
- # return (ok);
252
- # }
253
-
254
- sub design_exception {
255
- }
1
+ import std;
 
 
 
2
 
 
3
  backend default {
4
+ .host = "127.0.0.1";
5
+ .port = "9000";
6
  }
7
 
8
+ # The admin backend needs longer timeout values
9
  backend admin {
10
+ .host = "127.0.0.1";
11
+ .port = "9000";
12
+ .first_byte_timeout = 18000s;
13
+ .between_bytes_timeout = 18000s;
14
  }
15
 
16
+ # Add additional (ie webserver) IPs here that should be able to purge cache
17
  acl purge {
18
+ "localhost";
19
+ "127.0.0.1";
20
  }
21
 
22
  sub vcl_recv {
23
+ # Ban specific object in the cache
24
+ if (req.request == "PURGE") {
25
+ if (!client.ip ~ purge) {
26
+ error 405 "Not allowed.";
 
 
27
  }
28
+ return (lookup);
29
  }
30
+
31
+ # Ban something
32
+ if (req.request == "BAN") {
33
+ # Same ACL check as above:
 
 
 
 
 
 
 
 
 
 
 
34
  if (!client.ip ~ purge) {
35
  error 405 "Not allowed.";
36
  }
37
+ if (req.http.X-Ban-String) {
38
+ ban(req.http.X-Ban-String);
39
+
40
+ # Throw a synthetic page so the
41
+ # request won't go to the backend.
42
+ error 200 "Ban added";
43
+ }
44
+
45
+ error 400 "Bad request.";
46
  }
47
 
48
+ # Flush the whole cache
49
+ if (req.request == "FLUSH") {
50
+ if (!client.ip ~ purge) {
51
+ error 405 "Not allowed.";
52
+ }
53
+ # If there are multiple vhosts we only want to clear all cache for
54
+ # the one issuing the request
55
+ ban("req.url ~ .*");
56
+ error 200 "Flushed";
57
  }
58
 
59
+ # Refresh specific object
60
+ if (req.request == "REFRESH") {
61
+ if (!client.ip ~ purge) {
62
+ error 405 "Not allowed.";
63
+ }
64
+ set req.request = "GET";
65
+ set req.hash_always_miss = true;
66
  }
67
+
68
+ # Switch to the admin backend
69
+ if (req.http.Cookie ~ "adminhtml=") {
70
+ set req.backend = admin;
 
 
 
 
71
  }
72
 
73
+ # Keep track of logged in users
74
+ if (req.http.Cookie ~ "frontend=") {
75
+ set req.http.X-Session-UUID =
76
+ regsub(req.http.Cookie, "^.*?frontend=([^;]*);*.*$", "\1");
77
  }
78
 
79
+ # Pass anything other than GET and HEAD directly.
80
+ if (req.request != "GET" && req.request != "HEAD") {
81
+ /* We only deal with GET and HEAD by default */
 
82
  return (pass);
83
  }
84
 
85
+ # Normalize Aceept-Encoding header to reduce vary
 
 
 
 
 
 
 
86
  # http://varnish.projects.linpro.no/wiki/FAQ/Compression
87
  if (req.http.Accept-Encoding) {
88
  if (req.url ~ "\.(jpg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|swf|flv)$") {
97
  remove req.http.Accept-Encoding;
98
  }
99
  }
100
+
 
 
 
 
 
101
  return (lookup);
102
  }
103
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  sub vcl_hash {
105
+ # ESI request
106
+ if (req.url ~ "/madecache/varnish/(esi|messages)") {
107
+ hash_data(regsub(req.url, "(/hash/[^\/]+/).*", "\1"));
108
+
109
+ # Logged in user, cache on UUID level
110
+ if (req.http.X-Session-UUID) {
111
+ hash_data(req.http.X-Session-UUID);
112
+ }
113
+ } else {
114
+ hash_data(req.url);
115
+ }
116
+
117
+ # Also consider the host name for caching (multi-site with different themes etc)
118
  if (req.http.host) {
119
  hash_data(req.http.host);
120
  } else {
121
  hash_data(server.ip);
122
  }
123
+
 
 
124
  return (hash);
125
  }
 
 
 
 
 
 
 
 
126
 
127
+ sub vcl_hit {
128
+ if (req.request == "PURGE") {
129
+ purge;
130
+ error 200 "Purged";
131
  }
132
+ }
133
+
134
+ sub vcl_miss {
135
+ if (req.request == "PURGE") {
136
+ purge;
137
+ error 404 "Not in cache";
138
+ }
139
+ }
140
 
141
+ # Called when an object is fetched from the backend
142
+ sub vcl_fetch {
143
+ # Pass the cookie requests directly to the backend, without caching
144
+ if (req.url ~ "/madecache/varnish/cookie") {
145
+ return (hit_for_pass);
146
  }
 
147
 
148
+ # If the X-Made-Cache-Ttl header is set, use it, otherwise default to
149
+ # not caching the contents (0s)
 
 
150
  if (beresp.status == 200 || beresp.status == 301 || beresp.status == 404) {
151
  if (beresp.http.Content-Type ~ "text/html" || beresp.http.Content-Type ~ "text/xml") {
152
  set beresp.do_esi = true;
153
+ set beresp.ttl = std.duration(beresp.http.X-Made-Cache-Ttl, 0s);
 
 
 
 
 
 
 
 
 
154
  } else {
155
+ # TTL for static content
156
+ set beresp.ttl = 1w;
157
  }
158
+
159
+ # Don't cache expire headers, we maintain those differently
160
+ unset beresp.http.expires;
161
+
162
+ # Caching the cookie header would make multiple clients share session
163
+ set req.http.tempCookie = beresp.http.Set-Cookie;
164
+ unset beresp.http.Set-Cookie;
165
+
166
+ # Cache (if positive TTL)
167
  return (deliver);
168
  }
169
+
170
+ # Don't cache
171
  return (hit_for_pass);
172
  }
173
 
174
  sub vcl_deliver {
175
+ # To debug if it's a hit or a miss
176
+ if (req.http.X-Made-Cache-Debug) {
177
+ set resp.http.X-Cache-Hits = obj.hits;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  }
 
179
 
180
+ if (req.http.tempCookie) {
181
+ # We saved the cookie to give the user that cached the page a session
182
+ set resp.http.Set-Cookie = req.http.tempCookie;
183
+
184
+ # Version of https://www.varnish-cache.org/trac/wiki/VCLExampleLongerCaching
185
+ set resp.http.age = "0";
186
+ }
187
+
188
+ return (deliver);
189
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app/code/community/Made/Cache/etc/system.xml ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0"?>
2
+ <config>
3
+ <tabs>
4
+ <made translate="label" module="core">
5
+ <label>Made People</label>
6
+ <sort_order>101</sort_order>
7
+ </made>
8
+ </tabs>
9
+ <sections>
10
+ <cache>
11
+ <label>Cache</label>
12
+ <tab>made</tab>
13
+ <frontend_type>text</frontend_type>
14
+ <sort_order>1000</sort_order>
15
+ <show_in_default>1</show_in_default>
16
+ <show_in_website>1</show_in_website>
17
+ <show_in_store>1</show_in_store>
18
+ <groups>
19
+ <general translate="label">
20
+ <label>General Settings</label>
21
+ <frontend_type>text</frontend_type>
22
+ <sort_order>0</sort_order>
23
+ <show_in_default>1</show_in_default>
24
+ <show_in_website>1</show_in_website>
25
+ <show_in_store>1</show_in_store>
26
+ <fields>
27
+ <enable_profiler>
28
+ <label>Enable Block Profiler</label>
29
+ <comment>Displays hierarchical data of uncached block rendering times</comment>
30
+ <frontend_type>select</frontend_type>
31
+ <source_model>adminhtml/system_config_source_yesno</source_model>
32
+ <sort_order>1</sort_order>
33
+ <show_in_default>1</show_in_default>
34
+ <show_in_website>1</show_in_website>
35
+ <show_in_store>1</show_in_store>
36
+ </enable_profiler>
37
+ <remove_redirect_param>
38
+ <label>Remove Redirect Parameter</label>
39
+ <comment>Required if you display blocks with cached redirect links on more than one page, making redirects incorrect. This requires manual implementation which means you probably know what you're doing. Otherwise, disable.</comment>
40
+ <frontend_type>select</frontend_type>
41
+ <source_model>adminhtml/system_config_source_yesno</source_model>
42
+ <sort_order>2</sort_order>
43
+ <show_in_default>1</show_in_default>
44
+ <show_in_website>1</show_in_website>
45
+ <show_in_store>1</show_in_store>
46
+ </remove_redirect_param>
47
+ </fields>
48
+ </general>
49
+ <varnish translate="label">
50
+ <label>Varnish Settings (Enabled in Cache Management)</label>
51
+ <frontend_type>text</frontend_type>
52
+ <sort_order>1</sort_order>
53
+ <show_in_default>1</show_in_default>
54
+ <show_in_website>1</show_in_website>
55
+ <show_in_store>1</show_in_store>
56
+ <fields>
57
+ <ttl>
58
+ <label>Default Varnish TTL</label>
59
+ <comment><![CDATA[<a href="https://www.varnish-software.com/static/book/VCL_Basics.html#the-initial-value-of-beresp-ttl" target="_blank">Click here for an explanation of Varnish TTL values</a><br/>Default 1w]]></comment>
60
+ <frontend_type>text</frontend_type>
61
+ <sort_order>1</sort_order>
62
+ <show_in_default>1</show_in_default>
63
+ <show_in_website>1</show_in_website>
64
+ <show_in_store>1</show_in_store>
65
+ </ttl>
66
+ <nocache_routes>
67
+ <label>Routes to exclude from cache</label>
68
+ <comment>One per line: module, module/controller, or module/controller/action</comment>
69
+ <frontend_type>textarea</frontend_type>
70
+ <sort_order>2</sort_order>
71
+ <show_in_default>1</show_in_default>
72
+ <show_in_website>1</show_in_website>
73
+ <show_in_store>1</show_in_store>
74
+ </nocache_routes>
75
+ <servers>
76
+ <label>IPs to Varnish servers</label>
77
+ <comment>One per line</comment>
78
+ <frontend_type>textarea</frontend_type>
79
+ <sort_order>3</sort_order>
80
+ <show_in_default>1</show_in_default>
81
+ <show_in_website>1</show_in_website>
82
+ <show_in_store>1</show_in_store>
83
+ </servers>
84
+ <debug>
85
+ <label>Debug Mode</label>
86
+ <comment>Displays debug information as response headers</comment>
87
+ <frontend_type>select</frontend_type>
88
+ <source_model>adminhtml/system_config_source_yesno</source_model>
89
+ <sort_order>3</sort_order>
90
+ <show_in_default>1</show_in_default>
91
+ <show_in_website>1</show_in_website>
92
+ <show_in_store>1</show_in_store>
93
+ </debug>
94
+ </fields>
95
+ </varnish>
96
+ </groups>
97
+ </cache>
98
+ </sections>
99
+ </config>
app/design/frontend/base/default/layout/madecache.xml CHANGED
@@ -20,23 +20,62 @@
20
  <name>before_body_end</name>
21
  <name>product.info</name>
22
  <name>product_list</name>
23
- <name>category.products</name>
24
  <name lifetime="7200">cart_sidebar</name>
 
 
 
 
 
 
25
  </cache>
26
-
 
 
 
 
 
 
 
 
27
  <!--
28
  Example of blocks that typically should render using ESI, this
29
- is only used if Varnish is actually in front of Magento
 
 
 
 
 
 
30
  -->
31
  <esi>
 
32
  <name>cart_sidebar</name>
33
  <name>catalog.compare.sidebar</name>
34
- <name>right.reports.product.viewed</name>
35
  <name>right.reports.product.compared</name>
36
- <name>left.reports.product.viewed</name>
37
- <name>google_analytics</name>
38
  </esi>
39
- </default>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
  <checkout_onepage_index>
42
  <nocache>
@@ -52,4 +91,4 @@
52
  <name>content</name>
53
  </nocache>
54
  </streamcheckout_index_index>
55
- </layout>
20
  <name>before_body_end</name>
21
  <name>product.info</name>
22
  <name>product_list</name>
 
23
  <name lifetime="7200">cart_sidebar</name>
24
+
25
+ <!--
26
+ People want to cache this one, but it works badly if you use
27
+ the toolbar block to switch listing types
28
+ -->
29
+ <!--<name>category.products</name>-->
30
  </cache>
31
+
32
+ <!-- Inject profiler block into the bottom of the layout -->
33
+ <reference name="root">
34
+ <!-- This block can't be used with varnish cache enabled -->
35
+ <block type="cache/profiler" name="cache_profiler" output="toHtml" ifconfig="cache/general/enable_profiler"/>
36
+ </reference>
37
+ </default>
38
+
39
+ <varnish_enabled>
40
  <!--
41
  Example of blocks that typically should render using ESI, this
42
+ is only used if Varnish is actually in front of Magento.
43
+
44
+ It's best to keep these blocks at a minimum, and if possible,
45
+ bundled together as single big blocks. At the moment there is
46
+ no good way to serve the product view cached while recording
47
+ "recently viewed products", as this requires a costly backend
48
+ process for each product view.
49
  -->
50
  <esi>
51
+ <name>top.links</name>
52
  <name>cart_sidebar</name>
53
  <name>catalog.compare.sidebar</name>
 
54
  <name>right.reports.product.compared</name>
55
+ <name>right.poll</name>
 
56
  </esi>
57
+
58
+ <!--
59
+ Messages are fetched using ESI outside of the normal layout.
60
+
61
+ Stupid? Unsure. We must take care of all messages at every stage,
62
+ keeping the standard distributed way of rendering messages fails
63
+ here. The block is replaced under the hood with a special ESI tag.
64
+
65
+ Keep global_messages as a combined renderer
66
+
67
+ @see Made_Cache_Block_Messages
68
+ -->
69
+ <remove name="messages"/>
70
+
71
+ <!-- There is no good way to manage the recently viewed block -->
72
+ <remove name="right.reports.product.viewed"/>
73
+ <remove name="left.reports.product.viewed"/>
74
+
75
+ <reference name="root">
76
+ <block type="cache/varnish_footer" name="varnish_cache_footer" output="toHtml"/>
77
+ </reference>
78
+ </varnish_enabled>
79
 
80
  <checkout_onepage_index>
81
  <nocache>
91
  <name>content</name>
92
  </nocache>
93
  </streamcheckout_index_index>
94
+ </layout>
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Made_Cache</name>
4
- <version>1.1.2</version>
5
  <stability>stable</stability>
6
  <license>OSL 3.0</license>
7
  <channel>community</channel>
@@ -18,9 +18,9 @@ ESI is supported in conjunction with Phoenix_VarnishCache, and allows for super-
18
  A good block cache is vital for scaling a site, be sure to implement it before residing to full page cache.</description>
19
  <notes>N/A</notes>
20
  <authors><author><name>Jonathan Selander</name><user>jonathan_made</user><email>info@madepeople.se</email></author></authors>
21
- <date>2012-06-03</date>
22
- <time>19:11:24</time>
23
- <contents><target name="magecommunity"><dir name="Made"><dir name="Cache"><dir name="Block"><dir name="Catalog"><dir name="Product"><dir name="List"><file name="Product.php" hash="6356fde9c5acf5f0a96851e780a691b1"/></dir><file name="List.php" hash="06ab53286ff19e5cf057b6f3da90088f"/></dir></dir></dir><dir name="Model"><file name="Layout.php" hash="a9de02e5a6bf7b7f91434998f5d53c40"/><dir name="Observer"><file name="Abstract.php" hash="e8e501179fdbca2596b4d8b5291ec95c"/><file name="Catalog.php" hash="400213a265ce42ce439848ab28f5479a"/><file name="Checkout.php" hash="4c1361159d91da2452c1d50e1fc52505"/><file name="Cms.php" hash="2136c1b1cdfeef96a1ec4c486160ddb4"/></dir><file name="Observer.php" hash="ae71a2dd08b385bae7549ed235b2a528"/></dir><dir name="controllers"><file name="VarnishController.php" hash="6ae7446eef83e106c56b0f05711c3ffa"/></dir><dir name="etc"><file name="config.xml" hash="a536b882ea4ea864c0a38975641c6108"/><file name="magento.vcl" hash="dd4f36661f4da1563fb383aa23e872d1"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Made_Cache.xml" hash="4cf53cc9b4e525eb560f7fe1278d96bd"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="madecache.xml" hash="88f2ec1fcdbfc56b99fa6f8987ac8545"/></dir></dir></dir></dir></target></contents>
24
  <compatible/>
25
  <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
26
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Made_Cache</name>
4
+ <version>1.4.0</version>
5
  <stability>stable</stability>
6
  <license>OSL 3.0</license>
7
  <channel>community</channel>
18
  A good block cache is vital for scaling a site, be sure to implement it before residing to full page cache.</description>
19
  <notes>N/A</notes>
20
  <authors><author><name>Jonathan Selander</name><user>jonathan_made</user><email>info@madepeople.se</email></author></authors>
21
+ <date>2012-11-15</date>
22
+ <time>21:05:37</time>
23
+ <contents><target name="magecommunity"><dir name="Made"><dir name="Cache"><dir name="Block"><dir name="Catalog"><dir name="Product"><dir name="List"><file name="Product.php" hash="6356fde9c5acf5f0a96851e780a691b1"/></dir><file name="List.php" hash="bb1e30501c95e83901de75bcb780d072"/></dir></dir><file name="Messages.php" hash="6d7eaed6a93c0deb6037ea36e88ec62f"/><file name="Profiler.php" hash="7d1fd856f08a1900b15a72ae46eb143d"/><dir name="Varnish"><file name="Footer.php" hash="4da9584f4cf4b70149b5c48f9657f668"/></dir></dir><dir name="Helper"><file name="Data.php" hash="6ea193010f6bf79c0c92f06d15e8d73b"/><file name="Varnish.php" hash="e94eaec8c45a829aba298ca4a0e4fe08"/></dir><dir name="Model"><file name="Layout.php" hash="0bfb72b6b9b5ccdeb2e9f8f2a5883367"/><dir name="Observer"><file name="Abstract.php" hash="e9f870d8766e1b2e5c6c091052565731"/><file name="Catalog.php" hash="39e069dd45ef75d2ab5b00c708181567"/><file name="Checkout.php" hash="4f6cc6aa7b2466976cad6c25f6a466e5"/><file name="Cms.php" hash="7bac2c6d57d3ff85d397c7cc1867c323"/></dir><file name="Observer.php" hash="8e641fbc376853a6237e0271288b0e4a"/><file name="Profiler.php" hash="888d3e38e212d872a4420a3556ca4b08"/><file name="VarnishObserver.php" hash="f1b479228036c3bd37144be0666cbaf0"/></dir><dir name="controllers"><file name="VarnishController.php" hash="507120591d2e0ad6b33e7816f2c654cd"/></dir><dir name="etc"><file name="config.xml" hash="02e4540714fd10428c614471a7c153f0"/><file name="magento.vcl" hash="a247cd54d96f824bacacda6704ad5285"/><file name="system.xml" hash="4ffc1ded6a50a61aeaf932c5a3556a28"/></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Made_Cache.xml" hash="4cf53cc9b4e525eb560f7fe1278d96bd"/></dir></target><target name="magedesign"><dir name="frontend"><dir name="base"><dir name="default"><dir name="layout"><file name="madecache.xml" hash="2dc3f6f914a30e1a57a7576585e58209"/></dir></dir></dir></dir></target></contents>
24
  <compatible/>
25
  <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
26
  </package>