Elgg  Version 1.9
CacheHandler.php
Go to the documentation of this file.
1 <?php
2 
11 
12  protected $config;
13 
19  public function __construct($config) {
20  $this->config = $config;
21  }
22 
30  public function handleRequest($get_vars, $server_vars) {
31  if (empty($get_vars['request'])) {
32  $this->send403();
33  }
34  $request = $this->parseRequestVar($get_vars['request']);
35  if (!$request) {
36  $this->send403();
37  }
38  $ts = $request['ts'];
39  $view = $request['view'];
40  $viewtype = $request['viewtype'];
41 
42  $this->sendContentType($view);
43 
44  // this may/may not have to connect to the DB
45  $this->setupSimplecache();
46 
47  if (!$this->config->simplecache_enabled) {
48  $this->loadEngine();
50  $this->send403();
51  } else {
52  echo $this->renderView($view, $viewtype);
53  }
54  exit;
55  }
56 
57  $etag = "\"$ts\"";
58  // If is the same ETag, content didn't change.
59  if (isset($server_vars['HTTP_IF_NONE_MATCH']) && trim($server_vars['HTTP_IF_NONE_MATCH']) === $etag) {
60  header("HTTP/1.1 304 Not Modified");
61  exit;
62  }
63 
64  $filename = $this->config->dataroot . 'views_simplecache/' . md5("$viewtype|$view");
65  if (file_exists($filename)) {
66  $this->sendCacheHeaders($etag);
67  readfile($filename);
68  exit;
69  }
70 
71  $this->loadEngine();
72 
75  $this->send403();
76  }
77 
78  $cache_timestamp = (int)elgg_get_config('lastcache');
79 
80  if ($cache_timestamp == $ts) {
81  $this->sendCacheHeaders($etag);
82 
84 
85  $dir_name = $this->config->dataroot . 'views_simplecache/';
86  if (!is_dir($dir_name)) {
87  mkdir($dir_name, 0700);
88  }
89 
90  file_put_contents($filename, $content);
91  } else {
92  // if wrong timestamp, don't send HTTP cache
94  }
95 
96  echo $content;
97  exit;
98  }
99 
106  public function parseRequestVar($request_var) {
107  // no '..'
108  if (false !== strpos($request_var, '..')) {
109  return array();
110  }
111  // only alphanumeric characters plus /, ., -, and _
112  if (preg_match('#[^a-zA-Z0-9/\.\-_]#', $request_var)) {
113  return array();
114  }
115 
116  // testing showed regex to be marginally faster than array / string functions over 100000 reps
117  // it won't make a difference in real life and regex is easier to read.
118  // <ts>/<viewtype>/<name/of/view.and.dots>.<type>
119  if (!preg_match('#^/?([0-9]+)/([^/]+)/(.+)$#', $request_var, $matches)) {
120  return array();
121  }
122 
123  return array(
124  'ts' => $matches[1],
125  'viewtype' => $matches[2],
126  'view' => $matches[3],
127  );
128  }
129 
135  protected function setupSimplecache() {
136  if (!empty($this->config->dataroot) && isset($this->config->simplecache_enabled)) {
137  return;
138  }
139 
140  $dblink = mysql_connect($this->config->dbhost, $this->config->dbuser, $this->config->dbpass, true);
141  if (!$dblink) {
142  $this->send403('Cache error: unable to connect to database server');
143  }
144 
145  if (!mysql_select_db($this->config->dbname, $dblink)) {
146  $this->send403('Cache error: unable to connect to Elgg database');
147  }
148 
149  $query = "SELECT `name`, `value` FROM {$this->config->dbprefix}datalists
150  WHERE `name` IN ('dataroot', 'simplecache_enabled')";
151 
152  $result = mysql_query($query, $dblink);
153  if ($result) {
154  while ($row = mysql_fetch_object($result)) {
155  $this->config->{$row->name} = $row->value;
156  }
157  mysql_free_result($result);
158  }
159  mysql_close($dblink);
160 
161  if (!$result || !isset($this->config->dataroot)) {
162  $this->send403('Cache error: unable to get the data root');
163  }
164  }
165 
172  protected function sendCacheHeaders($etag) {
173  header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true);
174  header("Pragma: public", true);
175  header("Cache-Control: public", true);
176  header("ETag: $etag");
177  }
178 
185  protected function sendContentType($view) {
186  $segments = explode('/', $view, 2);
187  switch ($segments[0]) {
188  case 'css':
189  header("Content-Type: text/css", true);
190  break;
191  case 'js':
192  header('Content-Type: text/javascript', true);
193  break;
194  }
195  }
196 
205  protected function getProcessedView($view, $viewtype) {
206  $content = $this->renderView($view, $viewtype);
207 
208  $hook_type = _elgg_get_view_filetype($view);
209  $hook_params = array(
210  'view' => $view,
211  'viewtype' => $viewtype,
212  'view_content' => $content,
213  );
214  return elgg_trigger_plugin_hook('simplecache:generate', $hook_type, $hook_params, $content);
215  }
216 
224  protected function renderView($view, $viewtype) {
226 
227  if (!elgg_view_exists($view)) {
228  $this->send403();
229  }
230 
231  // disable error reporting so we don't cache problems
232  elgg_set_config('debug', null);
233 
234  // @todo elgg_view() checks if the page set is done (isset($CONFIG->pagesetupdone)) and
235  // triggers an event if it's not. Calling elgg_view() here breaks submenus
236  // (at least) because the page setup hook is called before any
237  // contexts can be correctly set (since this is called before page_handler()).
238  // To avoid this, lie about $CONFIG->pagehandlerdone to force
239  // the trigger correctly when the first view is actually being output.
240  elgg_set_config('pagesetupdone', true);
241 
242  return elgg_view($view);
243  }
244 
250  protected function loadEngine() {
251  require_once dirname(dirname(dirname(__FILE__))) . "/start.php";
252  }
253 
260  protected function send403($msg = 'Cache error: bad request') {
261  header('HTTP/1.1 403 Forbidden');
262  echo $msg;
263  exit;
264  }
265 }
elgg_get_config($name, $site_guid=0)
Get an Elgg configuration value.
loadEngine()
Load the complete Elgg engine.
$view
Definition: crop.php:68
send403($msg= 'Cache error:bad request')
Send an error message to requestor.
renderView($view, $viewtype)
Render a view for caching.
elgg_view_exists($view, $viewtype= '', $recurse=true)
Returns whether the specified view exists.
Definition: views.php:318
exit
Definition: reorder.php:12
setupSimplecache()
Do a minimal engine load.
_elgg_get_view_filetype($view)
Returns the type of output expected from the view.
Definition: cache.php:200
parseRequestVar($request_var)
Parse a request.
$request
getProcessedView($view, $viewtype)
Get the contents of a view for caching.
elgg_set_viewtype($viewtype="")
Manually set the viewtype.
Definition: views.php:70
sendCacheHeaders($etag)
Send cache headers.
elgg_set_config($name, $value)
Set an Elgg configuration value.
_elgg_is_view_cacheable($view)
Check whether a view is registered as cacheable.
Definition: views.php:253
elgg echo
Translates a string.
Definition: languages.js:43
elgg_trigger_plugin_hook($hook, $type, $params=null, $returnvalue=null)
Trigger a Plugin Hook and run all handler callbacks registered to that hook:type. ...
Definition: elgglib.php:925
elgg_view($view, $vars=array(), $bypass=false, $ignored=false, $viewtype= '')
Return a parsed view.
Definition: views.php:354
handleRequest($get_vars, $server_vars)
Handle a request for a cached view.
$content
Set robots.txt action.
Definition: set_robots.php:6
$filename
Definition: crop.php:23
$row
sendContentType($view)
Send content type.
clearfix elgg elgg elgg elgg page header
Definition: admin.php:127
$viewtype
Definition: start.php:132
__construct($config)
Constructor.