Squiz Matrix  4.12.2
 All Data Structures Namespaces Functions Variables Pages
cache_manager.inc
1 <?php
17 require_once SQ_INCLUDE_PATH.'/asset.inc';
18 require_once SQ_FUDGE_PATH.'/general/file_system.inc';
19 require_once SQ_FUDGE_PATH.'/db_extras/db_extras.inc';
20 
31 class Cache_Manager extends Asset
32 {
33 
34 
40  protected $_storage;
41 
47  public $global_cache = FALSE;
48 
49 
56  function __construct($assetid=0)
57  {
58  $this->_ser_attrs = TRUE;
59  parent::__construct($assetid);
60 
61  $storage_type = $this->attr('cache_storage_type');
62  $this->_loadCacheStorage($storage_type);
63 
64  }//end constructor
65 
66 
72  function __wakeup()
73  {
74  $storage_type = $this->attr('cache_storage_type');
75  $this->_loadCacheStorage($storage_type);
76 
77  }//end wakeup
78 
79 
89  function create(&$link)
90  {
91  require_once SQ_CORE_PACKAGE_PATH.'/system/system_asset_fns.inc';
92  if (!system_asset_fns_create_pre_check($this)) {
93  return FALSE;
94  }
95  $GLOBALS['SQ_SYSTEM']->changeDatabaseConnection('db2');
96  $GLOBALS['SQ_SYSTEM']->doTransaction('BEGIN');
97 
98  if ($linkid = parent::create($link)) {
99  if (!system_asset_fns_create_cleanup($this)) {
100  $linkid = FALSE;
101  }
102  }
103 
104  if ($linkid) {
105  $GLOBALS['SQ_SYSTEM']->doTransaction('COMMIT');
106  } else {
107  $GLOBALS['SQ_SYSTEM']->doTransaction('ROLLBACK');
108  }
109 
110  $GLOBALS['SQ_SYSTEM']->restoreDatabaseConnection();
111  return $linkid;
112 
113  }//end create()
114 
115 
124  function _getName($short_name=FALSE)
125  {
126  return $GLOBALS['SQ_SYSTEM']->am->getTypeInfo($this->type(), 'name');
127 
128  }//end _getName()
129 
130 
137  function canDelete()
138  {
139  return FALSE;
140 
141  }//end canDelete()
142 
143 
150  function canClone()
151  {
152  return FALSE;
153 
154  }//end canClone()
155 
156 
179  function loadFromCache($assetid, $asset_type, $cache_key='', $use_url=TRUE)
180  {
181 
182  if (basename($_SERVER['PHP_SELF']) == SQ_CONF_RECACHE_SUFFIX) {
183  return FALSE;
184  }
185 
186  // this will not check specifically set root node options
187  // if any root node specific options are configured, it will return TRUE blindly and the DB will be checked for a cache entry
188  if (!$this->cacheEnabled($asset_type)) return FALSE;
189 
190  $perm_key = $this->_getPermKey($assetid, $cache_key);
191 
192  if (empty($perm_key)) return FALSE;
193 
194  if ($use_url && !$this->global_cache) {
195  $url = current_url();
196  // append the query string (if any)
197  if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
198  $url = $this->_formUniqueUrl($url);
199  }
200  } else {
201  $url = $assetid;
202  }
203  $cache_key = md5($cache_key.$assetid);
204 
205  return $this->_storage->read($cache_key, $perm_key, $url, $assetid);
206 
207  }//end loadFromCache()
208 
209 
227  function saveToCache($assetid, $asset_type, $cache_key, $contents, $use_url=TRUE, $expiry_override='')
228  {
229  if (!$this->cacheEnabled($asset_type, $assetid)) return FALSE;
230  $perm_key = $this->_getPermKey($assetid, $cache_key);
231 
232  if (empty($perm_key)) return FALSE;
233 
234  if ($use_url && !$this->global_cache) {
235  $url = current_url();
236  // append the query string (if any)
237  if (isset($_SERVER['QUERY_STRING']) && !empty($_SERVER['QUERY_STRING'])) {
238  $url = $this->_formUniqueUrl($url);
239  }
240  } else {
241  $url = $assetid;
242  }
243  $cache_key = md5($cache_key.$assetid);
244 
245  $now = time();
246  if (is_int($expiry_override) && ($expiry_override >= 0) && ($expiry_override + $now > $now)) {
247  $expiry = $expiry_override + $now;
248  } else {
249  $expiry = $now + $this->getExpiry($asset_type, $assetid);
250  }
251  return $this->_storage->store($cache_key, $perm_key, $url, $assetid, $contents, $expiry);
252 
253  }//end saveToCache()
254 
255 
268  function _formUniqueUrl($url)
269  {
270  // we don't want this query vars for cache key, e.g. SQ_ACTION=login
271  $query_vars=Array();
272  parse_str($_SERVER['QUERY_STRING'], $query_vars);
273  $mute_list = array_intersect(array_keys($query_vars), Array('SQ_BACKEND_PAGE', 'SQ_ACTION', 'SQ_DESIGN_NAME', 'SQ_CONTEXT_NAME'));
274  foreach ($mute_list as $name) {
275  unset($query_vars[$name]);
276  }
277 
278  if (!empty($query_vars)) {
279  $new_query_str = '';
280  foreach ($query_vars as $key => $val) {
281  if (is_array($val)) {
282  foreach($val as $mkey => $mval) {
283  $new_query_str .= $key.'['.$mkey.']='.$mval.'&';
284  }
285  } else {
286  $new_query_str .= $key.'='.$val.'&';
287  }
288  }
289  $url .= '?'.rtrim($new_query_str, '&');
290  }
291 
292  return $url;
293 
294  }//end _formUniqueUrl()
295 
296 
305  function clearCache($assetids)
306  {
307  if ($this->_storage->clear($assetids) === FALSE) {
308  return FALSE;
309  }
310 
311  foreach ($assetids as $assetid) {
312  $asset = $GLOBALS['SQ_SYSTEM']->am->getAsset($assetid);
313  $GLOBALS['SQ_SYSTEM']->broadcastTriggerEvent('trigger_event_matrix_cache_cleared', $asset, NULL);
314  $GLOBALS['SQ_SYSTEM']->am->forgetAsset($asset);
315  unset($asset);
316  }
317 
318  return TRUE;
319 
320  }//end clearCache()
321 
322 
331  function getFilePaths($assetids)
332  {
333  return $this->_storage->getFilePaths($assetids);
334 
335  }//end getFilePaths()
336 
337 
346  public function canClearByAssetid()
347  {
348  if (is_object($this->_storage)) {
349  return $this->_storage->canClearByAssetid();
350  } else {
351  return NULL;
352  }
353 
354  }//end canClearByAssetid()
355 
356 
363  function clearAllCache()
364  {
365  $this->_storage->clearAll();
366 
367  }//end clearAllCache()
368 
369 
378  function getAllFilePaths($option='')
379  {
380  return $this->_storage->getAllFilePaths($option);
381 
382  }//end getAllFilePaths()
383 
384 
397  function _getPermKey($assetid, $cache_key)
398  {
399  $perm_key = '';
400  $current_user = $GLOBALS['SQ_SYSTEM']->user;
401  if (is_null($current_user) || ($current_user instanceof Public_User)) {
402  // if your not logged in and public level caching is on - you get the public copy
403  if ($this->attr('public_caching')) {
404  if (is_null($current_user)) {
405  $perm_key = 'public';
406  } else {
407  // get the group that this public user is in
408  // for which satisfies the security restrictions
409  $groups = array_values($current_user->getUserGroups());
410  sort($groups);
411  $perm_key = 'public'.implode('|',$groups);
412  }
413  }
414  } else {
415  if (empty($cache_key)) {
416  // if permission level caching is on - check read/write/admin access for this asset
417  // if the key is blank (representing a non list style asset)
418  if ($this->attr('permission_caching')) {
419  $groups = array_keys($current_user->getGroups());
420  $userids = Array((string) $current_user->id) + $groups;
421 
422  // note that we dont use the cache in rollback view mode so we dont have
423  // to run this query through the rollback functions
424  try {
425  $bind_vars = Array(
426  'assetid' => $assetid,
427  'userids' => $userids,
428  );
429  $permission = MatrixDAL::executeOne('cache_manager', 'getHighestPermissionForUsers', $bind_vars);
430  } catch (DALException $e) {
431  throw new Exception('Unable to get maximum permission level of asset ID #'.$assetid.' due to database error: '.$e->getMessage());
432  }
433 
434  switch ($permission) {
435  case SQ_PERMISSION_READ :
436  $perm_key .= ':read';
437  break;
438  case SQ_PERMISSION_WRITE :
439  $perm_key .= ':write';
440  break;
441  case SQ_PERMISSION_ADMIN :
442  $perm_key .= ':admin';
443  break;
444  }
445  }
446  } else {
447  // if group level caching is on - check the groups the user is in if they are logged in
448  // and if user satisfies the group security restrictions
449  if ($this->attr('group_caching')) {
450  $groups = array_values($current_user->getUserGroups());
451  sort($groups);
452  $perm_key = implode('|', $groups);
453  }
454  }
455  }//end else - if logged in
456  $perm_key = (empty($perm_key)) ? '' : md5($perm_key);
457  return $perm_key;
458 
459  }//end _getPermKey()
460 
461 
470  function getAssetHash($assetid=0)
471  {
472  $assetid = trim($assetid);
473 
474  $hash = 0;
475  $bucketsize = $this->attr('num_cache_dirs');
476  $len = strlen($assetid);
477  for ($i = 0; $i < $len; $i++) {
478  if (!is_numeric($assetid{$i})) {
479  $hash += ord($assetid{$i});
480  } else {
481  $hash += (int) $assetid{$i};
482  }
483  }
484  $hash = $hash % $bucketsize;
485 
486  while (strlen($hash) != strlen($bucketsize)) {
487  $hash = '0'.$hash;
488  }
489  return $hash;
490 
491  }//end getAssetHash()
492 
493 
505  function cacheEnabled($type_code='', $assetid='')
506  {
507  // first check some obvious things
508  if (SQ_ROLLBACK_VIEW) return FALSE;
509  if (basename($_SERVER['PHP_SELF']) == SQ_CONF_NOCACHE_SUFFIX) {
510  return FALSE;
511  }
512  if (array_get_index($_GET, 'SQ_ACTION') == 'diff') {
513  return FALSE;
514  }
515  if(defined('SQ_IN_PERFORMANCE_TIMING') && isset($_SESSION['SQ_PERFORMANCE_SETTING']['caching'])) {
516  if($_SESSION['SQ_PERFORMANCE_SETTING']['caching'] === 'Off') return FALSE;
517  }
518 
519  // check if the current URL is excluded
520  $current_url = current_url(FALSE, TRUE);
521  $root_urls = explode("\n", SQ_CONF_SYSTEM_ROOT_URLS);
522  $current_root_url = '';
523  foreach ($root_urls as $url) {
524  if (0 === strpos($current_url, $url)) {
525  if (strlen($url) > strlen($current_root_url)) {
526  $current_root_url = $url;
527  }
528  }
529  }
530  if (in_array($current_root_url, $this->attr('non_cachable_urls'))) {
531  return FALSE;
532  }
533 
534  // check type code and root node specific settings
535  // these take priority over the global setting
536  // in order for a cache entry to be saved and therefore loaded, a valid perm key must be attained
537  // this perm key is only attainable if a cache level is specified
538  $specific = $this->_specificCacheManagementSettings($type_code, $assetid, 'enabled');
539  if ($specific !== FALSE) {
540  return $specific;
541  }
542 
543  return $this->attr('enabled');
544 
545  }//end cacheEnabled()
546 
547 
555  function sendCacheableHeaders($type_code='', $assetid='')
556  {
557  // first check some obvious things
558  if (SQ_ROLLBACK_VIEW) return FALSE;
559  if (basename($_SERVER['PHP_SELF']) == SQ_CONF_NOCACHE_SUFFIX) {
560  return FALSE;
561  }
562  if (array_get_index($_GET, 'SQ_ACTION') == 'diff') {
563  return FALSE;
564  }
565  if(defined('SQ_IN_PERFORMANCE_TIMING')) return FALSE;
566 
567  // check if the current URL is excluded
568  $current_url = current_url(FALSE, TRUE);
569  $root_urls = explode("\n", SQ_CONF_SYSTEM_ROOT_URLS);
570  $current_root_url = '';
571  foreach ($root_urls as $url) {
572  if (0 === strpos($current_url, $url)) {
573  if (strlen($url) > strlen($current_root_url)) {
574  $current_root_url = $url;
575  }
576  }
577  }
578 
579  if (in_array($current_root_url, $this->attr('non_cacheable_header_urls'))) {
580  return FALSE;
581  }
582 
583  // check type code and root node specific settings
584  // these take priority over the global setting
585  $specific = $this->_specificCacheManagementSettings($type_code, $assetid, 'send_cacheable_header');
586  if ($specific !== FALSE) {
587  return $specific;
588  }
589 
590  $is_public = $GLOBALS['SQ_SYSTEM']->user instanceof Public_User;
591  if (!$is_public && in_array($current_root_url, $this->attr('user_cacheable_header_urls'))) {
592  return TRUE;
593  }
594 
595  return $is_public && SQ_CONF_SEND_CACHEABLE_HEADER;
596 
597  }//end sendCacheableHeaders()
598 
599 
612  function getExpiry($type_code='', $assetid='')
613  {
614  $specific = $this->_specificCacheManagementSettings($type_code, $assetid, 'expiry');
615  if ($specific !== FALSE) {
616  return $specific;
617  }
618 
619  return $this->attr('expiry');
620 
621  }//end getExpiry()
622 
623 
636  function getBrowserCacheExpiry($type_code='', $assetid='')
637  {
638  $specific = $this->_specificCacheManagementSettings($type_code, $assetid, 'browser_cache_expiry');
639  if ($specific !== FALSE) {
640  return $specific;
641  }
642 
643  return $this->attr('browser_cache_expiry');
644 
645  }//end getBrowserCacheExpiry()
646 
647 
660  function _specificCacheManagementSettings($type_code, $assetid, $value)
661  {
662  // check for type-code-specific settings
663  $type_codes = $this->attr('type_codes');
664  if (isset($type_codes[$type_code][$value])) {
665  return $type_codes[$type_code][$value];
666  }
667 
668  $root_nodes = $this->attr('root_nodes');
669 
670  // an empty assetid indicates the caller does not want to perform the search for possible tree locations
671  // if there are configured root node specific options return TRUE blindly
672  if (empty($assetid) && !empty($root_nodes)) {
673  return TRUE;
674  } else if (!empty($root_nodes)) {
675  // check for root node specific settings
676  if (isset($root_nodes[$assetid][$value])) {
677  return $root_nodes[$assetid][$value];
678  } else {
679  // there are possible tree locations set so search for them because an exact match was not found
680  // if there are a few root nodes, we should use isUnderRootNodes for performance sake
681  if(count($root_nodes) < 10) {
682  $matched_roots = $GLOBALS['SQ_SYSTEM']->am->isUnderRootNodes($assetid, array_keys($root_nodes), TRUE);
683  }
684  else {
685  $matched_roots = array_keys($GLOBALS['SQ_SYSTEM']->am->getParents($assetid));
686  }
687  foreach ($matched_roots as $rootid) {
688  // check that this entry actually manages child entries
689  if (isset($root_nodes[$rootid]['all_underneath']) && $root_nodes[$rootid]['all_underneath']) {
690  // check the value of the option that was asked for
691  if (isset($root_nodes[$rootid][$value])) {
692  return $root_nodes[$rootid][$value];
693  }
694  }
695  }//end foreach
696  }
697  }
698 
699  return FALSE;
700 
701  }//end _specificCacheManagementSettings()
702 
703 
704  //-- CACHE STORAGE --//
705 
706 
712  public function getCacheStorage()
713  {
714  return $this->_storage;
715 
716  }//end getCacheStorage()
717 
718 
726  protected function _loadCacheStorage($storage_type)
727  {
728  $asset_type = 'cache_storage_'.strtolower($storage_type);
729 
730  if ($GLOBALS['SQ_SYSTEM']->am->installed($asset_type) === FALSE) {
731  trigger_error('Unable to find "'.$storage_type.'" storage type, reverting to default', E_USER_WARNING);
732  $asset_type = 'cache_storage_default';
733  }
734 
735  $GLOBALS['SQ_SYSTEM']->am->includeAsset($asset_type);
736 
737  if (eval('return '.$asset_type.'::isAvailable();') === FALSE) {
738  trigger_error('The cache storage type "'.$storage_type.'" cannot be used under current conditions, reverting to default', E_USER_WARNING);
739  $asset_type = 'cache_storage_default';
740  $GLOBALS['SQ_SYSTEM']->am->includeAsset($asset_type);
741  }
742 
743  $this->_storage = new $asset_type();
744 
745  }//end _loadCacheStorage()
746 
761  public function clearCachedAssetsByUrl($url)
762  {
763  if (empty($url)) {
764  return FALSE;
765  }
766 
767  return $this->_storage->clearCachedAssetsByUrl($url);
768 
769  }//end clearCachedAssetsByUrl()
770 
771 
781  public function setAttrValue($name, $value)
782  {
783  $success = parent::setAttrValue($name, $value);
784 
785  // If the cache type is being set then cache storage needs to be reloaded
786  if ($name === 'cache_storage_type' && $success) {
787  $this->_loadCacheStorage($value);
788  }
789 
790  return $success;
791 
792  }//end setAttrValue()
793 
794 
803  {
804  $protocol_setting = $this->attr('cacheale_header_protocol');
805  if ($protocol_setting != 'both') {
806  return current_protocol() == 'http' ? $protocol_setting == 'http' : $protocol_setting == 'https';
807  }//end if
808 
809  return TRUE;
810 
811  }//end cacheableHeadersEnabledForCurrentProtocol()
812 
813 
821  public function cacheControlLevel()
822  {
823  return current_protocol() == 'http' ? $this->attr('cache_control_http') : $this->attr('cache_control_https');
824 
825  }//end cacheControlLevel()
826 
827 
828 }//end class
829 
830 ?>