1 <?php
2 /**
3 * @package Joomla.Platform
4 * @subpackage Cache
5 *
6 * @copyright Copyright (C) 2005 - 2017 Open Source Matters, Inc. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 */
9
10 defined('JPATH_PLATFORM') or die;
11
12 /**
13 * Joomla! Cache page type object
14 *
15 * @since 11.1
16 */
17 class JCacheControllerPage extends JCacheController
18 {
19 /**
20 * ID property for the cache page object.
21 *
22 * @var integer
23 * @since 11.1
24 */
25 protected $_id;
26
27 /**
28 * Cache group
29 *
30 * @var string
31 * @since 11.1
32 */
33 protected $_group;
34
35 /**
36 * Cache lock test
37 *
38 * @var stdClass
39 * @since 11.1
40 */
41 protected $_locktest = null;
42
43 /**
44 * Get the cached page data
45 *
46 * @param boolean $id The cache data ID
47 * @param string $group The cache data group
48 *
49 * @return mixed Boolean false on no result, cached object otherwise
50 *
51 * @since 11.1
52 */
53 public function get($id = false, $group = 'page')
54 {
55 // If an id is not given, generate it from the request
56 if (!$id)
57 {
58 $id = $this->_makeId();
59 }
60
61 // If the etag matches the page id ... set a no change header and exit : utilize browser cache
62 if (!headers_sent() && isset($_SERVER['HTTP_IF_NONE_MATCH']))
63 {
64 $etag = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
65
66 if ($etag == $id)
67 {
68 $browserCache = isset($this->options['browsercache']) ? $this->options['browsercache'] : false;
69
70 if ($browserCache)
71 {
72 $this->_noChange();
73 }
74 }
75 }
76
77 // We got a cache hit... set the etag header and echo the page data
78 $data = $this->cache->get($id, $group);
79
80 $this->_locktest = (object) array('locked' => null, 'locklooped' => null);
81
82 if ($data === false)
83 {
84 $this->_locktest = $this->cache->lock($id, $group);
85
86 // If locklooped is true try to get the cached data again; it could exist now.
87 if ($this->_locktest->locked === true && $this->_locktest->locklooped === true)
88 {
89 $data = $this->cache->get($id, $group);
90 }
91 }
92
93 if ($data !== false)
94 {
95 if ($this->_locktest->locked === true)
96 {
97 $this->cache->unlock($id, $group);
98 }
99
100 $data = unserialize(trim($data));
101 $data = JCache::getWorkarounds($data);
102
103 $this->_setEtag($id);
104
105 return $data;
106 }
107
108 // Set ID and group placeholders
109 $this->_id = $id;
110 $this->_group = $group;
111
112 return false;
113 }
114
115 /**
116 * Stop the cache buffer and store the cached data
117 *
118 * @param mixed $data The data to store
119 * @param string $id The cache data ID
120 * @param string $group The cache data group
121 * @param boolean $wrkarounds True to use wrkarounds
122 *
123 * @return boolean
124 *
125 * @since 11.1
126 */
127 public function store($data, $id, $group = null, $wrkarounds = true)
128 {
129 if ($this->_locktest->locked === false && $this->_locktest->locklooped === true)
130 {
131 // We can not store data because another process is in the middle of saving
132 return false;
133 }
134
135 // Get page data from the application object
136 if (!$data)
137 {
138 $data = JFactory::getApplication()->getBody();
139
140 // Only attempt to store if page data exists.
141 if (!$data)
142 {
143 return false;
144 }
145 }
146
147 // Get id and group and reset the placeholders
148 if (!$id)
149 {
150 $id = $this->_id;
151 }
152
153 if (!$group)
154 {
155 $group = $this->_group;
156 }
157
158 if ($wrkarounds)
159 {
160 $data = JCache::setWorkarounds(
161 $data,
162 array(
163 'nopathway' => 1,
164 'nohead' => 1,
165 'nomodules' => 1,
166 'headers' => true,
167 )
168 );
169 }
170
171 $result = $this->cache->store(serialize($data), $id, $group);
172
173 if ($this->_locktest->locked === true)
174 {
175 $this->cache->unlock($id, $group);
176 }
177
178 return $result;
179 }
180
181 /**
182 * Generate a page cache id
183 *
184 * @return string MD5 Hash
185 *
186 * @since 11.1
187 * @todo Discuss whether this should be coupled to a data hash or a request hash ... perhaps hashed with a serialized request
188 */
189 protected function _makeId()
190 {
191 return JCache::makeId();
192 }
193
194 /**
195 * There is no change in page data so send an unmodified header and die gracefully
196 *
197 * @return void
198 *
199 * @since 11.1
200 */
201 protected function _noChange()
202 {
203 $app = JFactory::getApplication();
204
205 // Send not modified header and exit gracefully
206 header('HTTP/1.x 304 Not Modified', true);
207 $app->close();
208 }
209
210 /**
211 * Set the ETag header in the response
212 *
213 * @param string $etag The entity tag (etag) to set
214 *
215 * @return void
216 *
217 * @since 11.1
218 */
219 protected function _setEtag($etag)
220 {
221 JFactory::getApplication()->setHeader('ETag', $etag, true);
222 }
223 }
224